Commit a422f56b authored by Adrian Sutton's avatar Adrian Sutton Committed by GitHub

op-challenger: Register games that support bond claims through the game type registry. (#9294)

Preserves the ability to use different contract types for different game types.
parent 9a062d54
...@@ -6,20 +6,12 @@ import ( ...@@ -6,20 +6,12 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts"
"github.com/ethereum-optimism/optimism/op-challenger/game/types" "github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-service/sources/batching"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
var _ BondClaimer = (*claimer)(nil)
type BondClaimer interface {
ClaimBonds(ctx context.Context, games []types.GameMetadata) error
}
type BondClaimMetrics interface { type BondClaimMetrics interface {
RecordBondClaimed(amount uint64) RecordBondClaimed(amount uint64)
} }
...@@ -29,45 +21,47 @@ type BondContract interface { ...@@ -29,45 +21,47 @@ type BondContract interface {
ClaimCredit(receipient common.Address) (txmgr.TxCandidate, error) ClaimCredit(receipient common.Address) (txmgr.TxCandidate, error)
} }
type claimer struct { type BondContractCreator func(game types.GameMetadata) (BondContract, error)
logger log.Logger
metrics BondClaimMetrics
caller *batching.MultiCaller type Claimer struct {
txSender types.TxSender logger log.Logger
metrics BondClaimMetrics
contractCreator BondContractCreator
txSender types.TxSender
} }
func NewBondClaimer(l log.Logger, m BondClaimMetrics, c *batching.MultiCaller, txSender types.TxSender) *claimer { var _ BondClaimer = (*Claimer)(nil)
return &claimer{
logger: l, func NewBondClaimer(l log.Logger, m BondClaimMetrics, contractCreator BondContractCreator, txSender types.TxSender) *Claimer {
metrics: m, return &Claimer{
caller: c, logger: l,
txSender: txSender, metrics: m,
contractCreator: contractCreator,
txSender: txSender,
} }
} }
func (c *claimer) ClaimBonds(ctx context.Context, games []types.GameMetadata) (err error) { func (c *Claimer) ClaimBonds(ctx context.Context, games []types.GameMetadata) (err error) {
for _, game := range games { for _, game := range games {
err = errors.Join(err, c.claimBond(ctx, game.Proxy)) err = errors.Join(err, c.claimBond(ctx, game))
} }
return err return err
} }
func (c *claimer) claimBond(ctx context.Context, gameAddr common.Address) error { func (c *Claimer) claimBond(ctx context.Context, game types.GameMetadata) error {
c.logger.Debug("attempting to claim bonds for", "game", gameAddr) c.logger.Debug("Attempting to claim bonds for", "game", game.Proxy)
contract, err := contracts.NewFaultDisputeGameContract(gameAddr, c.caller) contract, err := c.contractCreator(game)
if err != nil { if err != nil {
return fmt.Errorf("failed to create contract: %w", err) return fmt.Errorf("failed to create bond contract bindings: %w", err)
} }
credit, err := contract.GetCredit(ctx, c.txSender.From()) credit, err := contract.GetCredit(ctx, c.txSender.From())
if err != nil { if err != nil {
return fmt.Errorf("failed to get credit: %w", err) return fmt.Errorf("failed to get credit: %w", err)
} }
if credit.Cmp(big.NewInt(0)) == 0 { if credit.Cmp(big.NewInt(0)) == 0 {
c.logger.Debug("no credit to claim", "game", gameAddr) c.logger.Debug("No credit to claim", "game", game.Proxy)
return nil return nil
} }
......
...@@ -6,10 +6,7 @@ import ( ...@@ -6,10 +6,7 @@ import (
"math/big" "math/big"
"testing" "testing"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-challenger/game/types" "github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-service/sources/batching"
batchingTest "github.com/ethereum-optimism/optimism/op-service/sources/batching/test"
"github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -19,15 +16,14 @@ import ( ...@@ -19,15 +16,14 @@ import (
) )
var ( var (
methodCredit = "credit"
mockTxMgrSendError = errors.New("mock tx mgr send error") mockTxMgrSendError = errors.New("mock tx mgr send error")
) )
func TestClaimer_ClaimBonds(t *testing.T) { func TestClaimer_ClaimBonds(t *testing.T) {
t.Run("MultipleBondClaimsSucceed", func(t *testing.T) { t.Run("MultipleBondClaimsSucceed", func(t *testing.T) {
gameAddr := common.HexToAddress("0x1234") gameAddr := common.HexToAddress("0x1234")
c, m, rpc, txSender := newTestClaimer(t, gameAddr) c, m, contract, txSender := newTestClaimer(t, gameAddr)
rpc.SetResponse(gameAddr, methodCredit, batching.BlockLatest, []interface{}{txSender.From()}, []interface{}{big.NewInt(1)}) contract.credit = 1
err := c.ClaimBonds(context.Background(), []types.GameMetadata{{Proxy: gameAddr}, {Proxy: gameAddr}, {Proxy: gameAddr}}) err := c.ClaimBonds(context.Background(), []types.GameMetadata{{Proxy: gameAddr}, {Proxy: gameAddr}, {Proxy: gameAddr}})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 3, txSender.sends) require.Equal(t, 3, txSender.sends)
...@@ -36,8 +32,8 @@ func TestClaimer_ClaimBonds(t *testing.T) { ...@@ -36,8 +32,8 @@ func TestClaimer_ClaimBonds(t *testing.T) {
t.Run("BondClaimSucceeds", func(t *testing.T) { t.Run("BondClaimSucceeds", func(t *testing.T) {
gameAddr := common.HexToAddress("0x1234") gameAddr := common.HexToAddress("0x1234")
c, m, rpc, txSender := newTestClaimer(t, gameAddr) c, m, contract, txSender := newTestClaimer(t, gameAddr)
rpc.SetResponse(gameAddr, methodCredit, batching.BlockLatest, []interface{}{txSender.From()}, []interface{}{big.NewInt(1)}) contract.credit = 1
err := c.ClaimBonds(context.Background(), []types.GameMetadata{{Proxy: gameAddr}}) err := c.ClaimBonds(context.Background(), []types.GameMetadata{{Proxy: gameAddr}})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, txSender.sends) require.Equal(t, 1, txSender.sends)
...@@ -46,9 +42,9 @@ func TestClaimer_ClaimBonds(t *testing.T) { ...@@ -46,9 +42,9 @@ func TestClaimer_ClaimBonds(t *testing.T) {
t.Run("BondClaimFails", func(t *testing.T) { t.Run("BondClaimFails", func(t *testing.T) {
gameAddr := common.HexToAddress("0x1234") gameAddr := common.HexToAddress("0x1234")
c, m, rpc, txSender := newTestClaimer(t, gameAddr) c, m, contract, txSender := newTestClaimer(t, gameAddr)
txSender.sendFails = true txSender.sendFails = true
rpc.SetResponse(gameAddr, methodCredit, batching.BlockLatest, []interface{}{txSender.From()}, []interface{}{big.NewInt(1)}) contract.credit = 1
err := c.ClaimBonds(context.Background(), []types.GameMetadata{{Proxy: gameAddr}}) err := c.ClaimBonds(context.Background(), []types.GameMetadata{{Proxy: gameAddr}})
require.ErrorIs(t, err, mockTxMgrSendError) require.ErrorIs(t, err, mockTxMgrSendError)
require.Equal(t, 1, txSender.sends) require.Equal(t, 1, txSender.sends)
...@@ -57,8 +53,8 @@ func TestClaimer_ClaimBonds(t *testing.T) { ...@@ -57,8 +53,8 @@ func TestClaimer_ClaimBonds(t *testing.T) {
t.Run("ZeroCreditReturnsNil", func(t *testing.T) { t.Run("ZeroCreditReturnsNil", func(t *testing.T) {
gameAddr := common.HexToAddress("0x1234") gameAddr := common.HexToAddress("0x1234")
c, m, rpc, txSender := newTestClaimer(t, gameAddr) c, m, contract, txSender := newTestClaimer(t, gameAddr)
rpc.SetResponse(gameAddr, methodCredit, batching.BlockLatest, []interface{}{txSender.From()}, []interface{}{big.NewInt(0)}) contract.credit = 0
err := c.ClaimBonds(context.Background(), []types.GameMetadata{{Proxy: gameAddr}}) err := c.ClaimBonds(context.Background(), []types.GameMetadata{{Proxy: gameAddr}})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 0, txSender.sends) require.Equal(t, 0, txSender.sends)
...@@ -67,8 +63,8 @@ func TestClaimer_ClaimBonds(t *testing.T) { ...@@ -67,8 +63,8 @@ func TestClaimer_ClaimBonds(t *testing.T) {
t.Run("MultipleBondClaimFails", func(t *testing.T) { t.Run("MultipleBondClaimFails", func(t *testing.T) {
gameAddr := common.HexToAddress("0x1234") gameAddr := common.HexToAddress("0x1234")
c, m, rpc, txSender := newTestClaimer(t, gameAddr) c, m, contract, txSender := newTestClaimer(t, gameAddr)
rpc.SetResponse(gameAddr, methodCredit, batching.BlockLatest, []interface{}{txSender.From()}, []interface{}{big.NewInt(1)}) contract.credit = 1
txSender.sendFails = true txSender.sendFails = true
err := c.ClaimBonds(context.Background(), []types.GameMetadata{{Proxy: gameAddr}, {Proxy: gameAddr}, {Proxy: gameAddr}}) err := c.ClaimBonds(context.Background(), []types.GameMetadata{{Proxy: gameAddr}, {Proxy: gameAddr}, {Proxy: gameAddr}})
require.ErrorIs(t, err, mockTxMgrSendError) require.ErrorIs(t, err, mockTxMgrSendError)
...@@ -77,16 +73,16 @@ func TestClaimer_ClaimBonds(t *testing.T) { ...@@ -77,16 +73,16 @@ func TestClaimer_ClaimBonds(t *testing.T) {
}) })
} }
func newTestClaimer(t *testing.T, gameAddr common.Address) (*claimer, *mockClaimMetrics, *batchingTest.AbiBasedRpc, *mockTxSender) { func newTestClaimer(t *testing.T, gameAddr common.Address) (*Claimer, *mockClaimMetrics, *stubBondContract, *mockTxSender) {
logger := testlog.Logger(t, log.LvlDebug) logger := testlog.Logger(t, log.LvlDebug)
m := &mockClaimMetrics{} m := &mockClaimMetrics{}
txSender := &mockTxSender{} txSender := &mockTxSender{}
fdgAbi, err := bindings.FaultDisputeGameMetaData.GetAbi() bondContract := &stubBondContract{}
require.NoError(t, err) contractCreator := func(game types.GameMetadata) (BondContract, error) {
stubRpc := batchingTest.NewAbiBasedRpc(t, gameAddr, fdgAbi) return bondContract, nil
caller := batching.NewMultiCaller(stubRpc, 100) }
c := NewBondClaimer(logger, m, caller, txSender) c := NewBondClaimer(logger, m, contractCreator, txSender)
return c, m, stubRpc, txSender return c, m, bondContract, txSender
} }
type mockClaimMetrics struct { type mockClaimMetrics struct {
...@@ -117,3 +113,15 @@ func (s *mockTxSender) SendAndWait(_ string, _ ...txmgr.TxCandidate) ([]*ethtype ...@@ -117,3 +113,15 @@ func (s *mockTxSender) SendAndWait(_ string, _ ...txmgr.TxCandidate) ([]*ethtype
} }
return []*ethtypes.Receipt{{Status: ethtypes.ReceiptStatusSuccessful}}, nil return []*ethtypes.Receipt{{Status: ethtypes.ReceiptStatusSuccessful}}, nil
} }
type stubBondContract struct {
credit int64
}
func (s *stubBondContract) GetCredit(_ context.Context, _ common.Address) (*big.Int, error) {
return big.NewInt(s.credit), nil
}
func (s *stubBondContract) ClaimCredit(_ common.Address) (txmgr.TxCandidate, error) {
return txmgr.TxCandidate{}, nil
}
...@@ -8,6 +8,10 @@ import ( ...@@ -8,6 +8,10 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
type BondClaimer interface {
ClaimBonds(ctx context.Context, games []types.GameMetadata) error
}
type BondClaimScheduler struct { type BondClaimScheduler struct {
log log.Logger log log.Logger
metrics BondClaimSchedulerMetrics metrics BondClaimSchedulerMetrics
......
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"github.com/ethereum-optimism/optimism/op-challenger/config" "github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/claims"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/cannon" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/cannon"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/outputs" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/outputs"
...@@ -28,6 +29,7 @@ type CloseFunc func() ...@@ -28,6 +29,7 @@ type CloseFunc func()
type Registry interface { type Registry interface {
RegisterGameType(gameType uint32, creator scheduler.PlayerCreator, oracle keccakTypes.LargePreimageOracle) RegisterGameType(gameType uint32, creator scheduler.PlayerCreator, oracle keccakTypes.LargePreimageOracle)
RegisterBondContract(gameType uint32, creator claims.BondContractCreator)
} }
func RegisterGameTypes( func RegisterGameTypes(
...@@ -106,6 +108,11 @@ func registerAlphabet( ...@@ -106,6 +108,11 @@ func registerAlphabet(
return err return err
} }
registry.RegisterGameType(alphabetGameType, playerCreator, oracle) registry.RegisterGameType(alphabetGameType, playerCreator, oracle)
contractCreator := func(game types.GameMetadata) (claims.BondContract, error) {
return contracts.NewFaultDisputeGameContract(game.Proxy, caller)
}
registry.RegisterBondContract(alphabetGameType, contractCreator)
return nil return nil
} }
...@@ -168,5 +175,10 @@ func registerCannon( ...@@ -168,5 +175,10 @@ func registerCannon(
return err return err
} }
registry.RegisterGameType(cannonGameType, playerCreator, oracle) registry.RegisterGameType(cannonGameType, playerCreator, oracle)
contractCreator := func(game types.GameMetadata) (claims.BondContract, error) {
return contracts.NewFaultDisputeGameContract(game.Proxy, caller)
}
registry.RegisterBondContract(cannonGameType, contractCreator)
return nil return nil
} }
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/claims"
keccakTypes "github.com/ethereum-optimism/optimism/op-challenger/game/keccak/types" keccakTypes "github.com/ethereum-optimism/optimism/op-challenger/game/keccak/types"
"github.com/ethereum-optimism/optimism/op-challenger/game/scheduler" "github.com/ethereum-optimism/optimism/op-challenger/game/scheduler"
"github.com/ethereum-optimism/optimism/op-challenger/game/types" "github.com/ethereum-optimism/optimism/op-challenger/game/types"
...@@ -14,14 +15,16 @@ import ( ...@@ -14,14 +15,16 @@ import (
var ErrUnsupportedGameType = errors.New("unsupported game type") var ErrUnsupportedGameType = errors.New("unsupported game type")
type GameTypeRegistry struct { type GameTypeRegistry struct {
types map[uint32]scheduler.PlayerCreator types map[uint32]scheduler.PlayerCreator
oracles map[common.Address]keccakTypes.LargePreimageOracle bondCreators map[uint32]claims.BondContractCreator
oracles map[common.Address]keccakTypes.LargePreimageOracle
} }
func NewGameTypeRegistry() *GameTypeRegistry { func NewGameTypeRegistry() *GameTypeRegistry {
return &GameTypeRegistry{ return &GameTypeRegistry{
types: make(map[uint32]scheduler.PlayerCreator), types: make(map[uint32]scheduler.PlayerCreator),
oracles: make(map[common.Address]keccakTypes.LargePreimageOracle), bondCreators: make(map[uint32]claims.BondContractCreator),
oracles: make(map[common.Address]keccakTypes.LargePreimageOracle),
} }
} }
...@@ -39,6 +42,13 @@ func (r *GameTypeRegistry) RegisterGameType(gameType uint32, creator scheduler.P ...@@ -39,6 +42,13 @@ func (r *GameTypeRegistry) RegisterGameType(gameType uint32, creator scheduler.P
} }
} }
func (r *GameTypeRegistry) RegisterBondContract(gameType uint32, creator claims.BondContractCreator) {
if _, ok := r.bondCreators[gameType]; ok {
panic(fmt.Errorf("duplicate bond contract registered for game type: %v", gameType))
}
r.bondCreators[gameType] = creator
}
// CreatePlayer creates a new game player for the given game, using the specified directory for persisting data. // CreatePlayer creates a new game player for the given game, using the specified directory for persisting data.
func (r *GameTypeRegistry) CreatePlayer(game types.GameMetadata, dir string) (scheduler.GamePlayer, error) { func (r *GameTypeRegistry) CreatePlayer(game types.GameMetadata, dir string) (scheduler.GamePlayer, error) {
creator, ok := r.types[game.GameType] creator, ok := r.types[game.GameType]
...@@ -48,6 +58,14 @@ func (r *GameTypeRegistry) CreatePlayer(game types.GameMetadata, dir string) (sc ...@@ -48,6 +58,14 @@ func (r *GameTypeRegistry) CreatePlayer(game types.GameMetadata, dir string) (sc
return creator(game, dir) return creator(game, dir)
} }
func (r *GameTypeRegistry) CreateBondContract(game types.GameMetadata) (claims.BondContract, error) {
creator, ok := r.bondCreators[game.GameType]
if !ok {
return nil, fmt.Errorf("%w: %v", ErrUnsupportedGameType, game.GameType)
}
return creator(game)
}
func (r *GameTypeRegistry) Oracles() []keccakTypes.LargePreimageOracle { func (r *GameTypeRegistry) Oracles() []keccakTypes.LargePreimageOracle {
return maps.Values(r.oracles) return maps.Values(r.oracles)
} }
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"math/big" "math/big"
"testing" "testing"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/claims"
keccakTypes "github.com/ethereum-optimism/optimism/op-challenger/game/keccak/types" keccakTypes "github.com/ethereum-optimism/optimism/op-challenger/game/keccak/types"
"github.com/ethereum-optimism/optimism/op-challenger/game/scheduler" "github.com/ethereum-optimism/optimism/op-challenger/game/scheduler"
"github.com/ethereum-optimism/optimism/op-challenger/game/scheduler/test" "github.com/ethereum-optimism/optimism/op-challenger/game/scheduler/test"
...@@ -61,6 +62,35 @@ func TestDeduplicateOracles(t *testing.T) { ...@@ -61,6 +62,35 @@ func TestDeduplicateOracles(t *testing.T) {
require.Contains(t, oracles, oracleB) require.Contains(t, oracles, oracleB)
} }
func TestBondContracts(t *testing.T) {
t.Run("UnknownGameType", func(t *testing.T) {
registry := NewGameTypeRegistry()
contract, err := registry.CreateBondContract(types.GameMetadata{GameType: 0})
require.ErrorIs(t, err, ErrUnsupportedGameType)
require.Nil(t, contract)
})
t.Run("KnownGameType", func(t *testing.T) {
registry := NewGameTypeRegistry()
expected := &stubBondContract{}
registry.RegisterBondContract(0, func(game types.GameMetadata) (claims.BondContract, error) {
return expected, nil
})
creator, err := registry.CreateBondContract(types.GameMetadata{GameType: 0})
require.NoError(t, err)
require.Same(t, expected, creator)
})
t.Run("PanicsOnDuplicate", func(t *testing.T) {
registry := NewGameTypeRegistry()
creator := func(game types.GameMetadata) (claims.BondContract, error) {
return nil, nil
}
registry.RegisterBondContract(0, creator)
require.Panics(t, func() {
registry.RegisterBondContract(0, creator)
})
})
}
type stubPreimageOracle common.Address type stubPreimageOracle common.Address
func (s stubPreimageOracle) ChallengeTx(_ keccakTypes.LargePreimageIdent, _ keccakTypes.Challenge) (txmgr.TxCandidate, error) { func (s stubPreimageOracle) ChallengeTx(_ keccakTypes.LargePreimageIdent, _ keccakTypes.Challenge) (txmgr.TxCandidate, error) {
...@@ -82,3 +112,13 @@ func (s stubPreimageOracle) Addr() common.Address { ...@@ -82,3 +112,13 @@ func (s stubPreimageOracle) Addr() common.Address {
func (s stubPreimageOracle) GetActivePreimages(_ context.Context, _ common.Hash) ([]keccakTypes.LargePreimageMetaData, error) { func (s stubPreimageOracle) GetActivePreimages(_ context.Context, _ common.Hash) ([]keccakTypes.LargePreimageMetaData, error) {
return nil, nil return nil, nil
} }
type stubBondContract struct{}
func (s *stubBondContract) GetCredit(ctx context.Context, receipient common.Address) (*big.Int, error) {
panic("not supported")
}
func (s *stubBondContract) ClaimCredit(receipient common.Address) (txmgr.TxCandidate, error) {
panic("not supported")
}
...@@ -108,12 +108,12 @@ func (s *Service) initFromConfig(ctx context.Context, cfg *config.Config) error ...@@ -108,12 +108,12 @@ func (s *Service) initFromConfig(ctx context.Context, cfg *config.Config) error
if err := s.initGameLoader(); err != nil { if err := s.initGameLoader(); err != nil {
return fmt.Errorf("failed to init game loader: %w", err) return fmt.Errorf("failed to init game loader: %w", err)
} }
if err := s.initBondClaims(cfg); err != nil {
return fmt.Errorf("failed to init bond claiming: %w", err)
}
if err := s.registerGameTypes(ctx, cfg); err != nil { if err := s.registerGameTypes(ctx, cfg); err != nil {
return fmt.Errorf("failed to register game types: %w", err) return fmt.Errorf("failed to register game types: %w", err)
} }
if err := s.initBondClaims(); err != nil {
return fmt.Errorf("failed to init bond claiming: %w", err)
}
if err := s.initScheduler(cfg); err != nil { if err := s.initScheduler(cfg); err != nil {
return fmt.Errorf("failed to init scheduler: %w", err) return fmt.Errorf("failed to init scheduler: %w", err)
} }
...@@ -207,9 +207,8 @@ func (s *Service) initGameLoader() error { ...@@ -207,9 +207,8 @@ func (s *Service) initGameLoader() error {
return nil return nil
} }
func (s *Service) initBondClaims(cfg *config.Config) error { func (s *Service) initBondClaims() error {
caller := batching.NewMultiCaller(s.l1Client.Client(), batching.DefaultBatchSize) claimer := claims.NewBondClaimer(s.logger, s.metrics, s.registry.CreateBondContract, s.txSender)
claimer := claims.NewBondClaimer(s.logger, s.metrics, caller, s.txSender)
s.claimer = claims.NewBondClaimScheduler(s.logger, s.metrics, claimer) s.claimer = claims.NewBondClaimScheduler(s.logger, s.metrics, claimer)
return nil return nil
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment