diff --git a/core/services/relay/evm/chain_reader_test.go b/core/services/relay/evm/chain_reader_test.go index e7ec4351d95..888b662c160 100644 --- a/core/services/relay/evm/chain_reader_test.go +++ b/core/services/relay/evm/chain_reader_test.go @@ -12,6 +12,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/crypto" "github.com/jmoiron/sqlx" @@ -145,6 +146,18 @@ func TestChainReader(t *testing.T) { it := &EVMChainReaderInterfaceTester[*testing.T]{Helper: &helper{}} RunChainReaderEvmTests(t, it) RunChainReaderInterfaceTests[*testing.T](t, commontestutils.WrapChainReaderTesterForLoop(it)) + + t.Run("Bind returns error on missing contract at address", func(t *testing.T) { + t.Parallel() + it.Setup(t) + + addr := common.BigToAddress(big.NewInt(42)) + reader := it.GetChainReader(t) + + err := reader.Bind(context.Background(), []clcommontypes.BoundContract{{Name: AnyContractName, Address: addr.Hex(), Pending: true}}) + + require.ErrorIs(t, err, evm.NoContractExistsError{Address: addr}) + }) } type helper struct { diff --git a/core/services/relay/evm/method_binding.go b/core/services/relay/evm/method_binding.go index 3a212bfea4b..4f642bdab94 100644 --- a/core/services/relay/evm/method_binding.go +++ b/core/services/relay/evm/method_binding.go @@ -13,6 +13,14 @@ import ( evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" ) +type NoContractExistsError struct { + Address common.Address +} + +func (e NoContractExistsError) Error() string { + return fmt.Sprintf("contract does not exist at address: %s", e.Address) +} + type methodBinding struct { address common.Address contractName string @@ -28,9 +36,22 @@ func (m *methodBinding) SetCodec(codec commontypes.RemoteCodec) { m.codec = codec } -func (m *methodBinding) Bind(_ context.Context, binding commontypes.BoundContract) error { - m.address = common.HexToAddress(binding.Address) +func (m *methodBinding) Bind(ctx context.Context, binding commontypes.BoundContract) error { + addr := common.HexToAddress(binding.Address) + + // check for contract byte code at the latest block and provided address + byteCode, err := m.client.CodeAt(ctx, addr, nil) + if err != nil { + return err + } + + if len(byteCode) == 0 { + return NoContractExistsError{Address: addr} + } + + m.address = addr m.bound = true + return nil } diff --git a/core/services/relay/evm/write_target_test.go b/core/services/relay/evm/write_target_test.go index 95d0617db4f..694f7e1910c 100644 --- a/core/services/relay/evm/write_target_test.go +++ b/core/services/relay/evm/write_target_test.go @@ -44,6 +44,7 @@ func TestEvmWrite(t *testing.T) { mockCall = append(mockCall, byte(0)) } evmClient.On("CallContract", mock.Anything, mock.Anything, mock.Anything).Return(mockCall, nil).Maybe() + evmClient.On("CodeAt", mock.Anything, mock.Anything, mock.Anything).Return([]byte("test"), nil) chain.On("ID").Return(big.NewInt(11155111)) chain.On("TxManager").Return(txManager)