Skip to content

Commit

Permalink
add hook to parse addresses as string
Browse files Browse the repository at this point in the history
  • Loading branch information
Farber98 committed Sep 20, 2024
1 parent 1e39e3c commit 0ed1ac0
Show file tree
Hide file tree
Showing 6 changed files with 279 additions and 56 deletions.
5 changes: 5 additions & 0 deletions contracts/src/v0.8/shared/test/helpers/ChainReaderTester.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ contract ChainReaderTester {
MidLevelTestStruct nestedStruct
);

event TriggeredWithAddress(address indexed field1, address field2);

event TriggeredEventWithDynamicTopic(string indexed fieldHash, string field);

// First topic is event hash
Expand Down Expand Up @@ -133,4 +135,7 @@ contract ChainReaderTester {
function triggerWithFourTopicsWithHashed(string memory field1, uint8[32] memory field2, bytes32 field3) public {
emit TriggeredWithFourTopicsWithHashed(field1, field2, field3);
}
function triggerWithAddress(address field1, address field2) public {
emit TriggeredWithAddress(field1, field2);
}
}
158 changes: 156 additions & 2 deletions core/gethwrappers/generated/chain_reader_tester/chain_reader_tester.go

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ batch_vrf_coordinator_v2: ../../contracts/solc/v0.8.6/BatchVRFCoordinatorV2/Batc
batch_vrf_coordinator_v2plus: ../../contracts/solc/v0.8.19/BatchVRFCoordinatorV2Plus/BatchVRFCoordinatorV2Plus.abi ../../contracts/solc/v0.8.19/BatchVRFCoordinatorV2Plus/BatchVRFCoordinatorV2Plus.bin f13715b38b5b9084b08bffa571fb1c8ef686001535902e1255052f074b31ad4e
blockhash_store: ../../contracts/solc/v0.8.19/BlockhashStore/BlockhashStore.abi ../../contracts/solc/v0.8.19/BlockhashStore/BlockhashStore.bin 31b118f9577240c8834c35f8b5a1440e82a6ca8aea702970de2601824b6ab0e1
chain_module_base: ../../contracts/solc/v0.8.19/ChainModuleBase/ChainModuleBase.abi ../../contracts/solc/v0.8.19/ChainModuleBase/ChainModuleBase.bin 7a82cc28014761090185c2650239ad01a0901181f1b2b899b42ca293bcda3741
chain_reader_tester: ../../contracts/solc/v0.8.19/ChainReaderTester/ChainReaderTester.abi ../../contracts/solc/v0.8.19/ChainReaderTester/ChainReaderTester.bin 84c4223c4dbd51aafd77a6787f4b84ce80f661ce86a907c1431c5b82d633f2ad
chain_reader_tester: ../../contracts/solc/v0.8.19/ChainReaderTester/ChainReaderTester.abi ../../contracts/solc/v0.8.19/ChainReaderTester/ChainReaderTester.bin 50e455ed1a81b0f8a7e75bc199a78671c6d2c460ce10e523da3d6da408bb26ce
chain_specific_util_helper: ../../contracts/solc/v0.8.6/ChainSpecificUtilHelper/ChainSpecificUtilHelper.abi ../../contracts/solc/v0.8.6/ChainSpecificUtilHelper/ChainSpecificUtilHelper.bin 66eb30b0717fefe05672df5ec863c0b9a5a654623c4757307a2726d8f31e26b1
counter: ../../contracts/solc/v0.8.6/Counter/Counter.abi ../../contracts/solc/v0.8.6/Counter/Counter.bin 6ca06e000e8423573ffa0bdfda749d88236ab3da2a4cbb4a868c706da90488c9
cron_upkeep_factory_wrapper: ../../contracts/solc/v0.8.6/CronUpkeepFactory/CronUpkeepFactory.abi - dacb0f8cdf54ae9d2781c5e720fc314b32ed5e58eddccff512c75d6067292cd7
Expand Down
20 changes: 20 additions & 0 deletions core/services/relay/evm/codec/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ var DecoderHooks = []mapstructure.DecodeHookFunc{
commoncodec.SliceToArrayVerifySizeHook,
sizeVerifyBigIntHook,
commoncodec.NumberHook,
addressStringDecodeHook,
}

// NewCodec creates a new [commontypes.RemoteCodec] for EVM.
Expand Down Expand Up @@ -159,3 +160,22 @@ func decodeAddress(data any) (any, error) {

return common.Address(decoded), nil
}

// addressStringDecodeHook converts between string and common.Address, and vice versa.
// It handles both converting a string (hex format) to a common.Address and converting a common.Address to a string.
func addressStringDecodeHook(from reflect.Type, to reflect.Type, value interface{}) (interface{}, error) {
// Check if we're converting from string to common.Address
if from == reflect.TypeOf("") && to == reflect.TypeOf(common.Address{}) {
// Decode the string (hex format) to common.Address
return decodeAddress(value)
}

// Check if we're converting from common.Address to string
if from == reflect.TypeOf(common.Address{}) && to == reflect.TypeOf("") {
// Convert the common.Address to its string (hex) representation
return value.(common.Address).Hex(), nil
}

// If no valid conversion, return the original value unchanged
return value, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const (
triggerWithDynamicTopic = "TriggeredEventWithDynamicTopic"
triggerWithAllTopics = "TriggeredWithFourTopics"
triggerWithAllTopicsWithHashed = "TriggeredWithFourTopicsWithHashed"
triggerWithAddress = "TriggeredWithAddress"
finalityDepth = 4
)

Expand Down Expand Up @@ -119,7 +120,7 @@ func (it *EVMChainComponentsInterfaceTester[T]) Setup(t T) {
AnyContractName: {
ContractABI: chain_reader_tester.ChainReaderTesterMetaData.ABI,
ContractPollingFilter: types.ContractPollingFilter{
GenericEventNames: []string{EventName, EventWithFilterName, triggerWithAllTopicsWithHashed},
GenericEventNames: []string{EventName, EventWithFilterName, triggerWithAllTopicsWithHashed, triggerWithAddress},
},
Configs: map[string]*types.ChainReaderDefinition{
MethodTakingLatestParamsReturningTestStruct: &methodTakingLatestParamsReturningTestStructConfig,
Expand Down Expand Up @@ -190,6 +191,11 @@ func (it *EVMChainComponentsInterfaceTester[T]) Setup(t T) {
&codec.RenameModifierConfig{Fields: map[string]string{"NestedStruct.Inner.IntVal": "I"}},
},
},
triggerWithAddress: {
ChainSpecificName: triggerWithAddress,
ReadType: types.Event,
EventDefinitions: &types.EventDefinitions{},
},
},
},
AnySecondContractName: {
Expand Down Expand Up @@ -253,6 +259,12 @@ func (it *EVMChainComponentsInterfaceTester[T]) Setup(t T) {
GasLimit: 2_000_000,
Checker: "simulate",
},
"triggerWithAddress": {
ChainSpecificName: "triggerWithAddress",
FromAddress: it.Helper.Accounts(t)[1].From,
GasLimit: 2_000_000,
Checker: "simulate",
},
},
},
AnySecondContractName: {
Expand Down
136 changes: 84 additions & 52 deletions core/services/relay/evm/evmtesting/run_tests.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,27 +41,24 @@ func RunContractReaderEvmTests[T TestingT[T]](t T, it *EVMChainComponentsInterfa

cr := it.GetContractReader(t)
bindings := it.GetBindings(t)
bound := BindingsByName(bindings, AnyContractName)[0]
require.NoError(t, cr.Bind(ctx, bindings))

type DynamicEvent struct {
Field string
}
SubmitTransactionToCW(t, it, "triggerEventWithDynamicTopic", DynamicEvent{Field: anyString}, bindings[0], types.Unconfirmed)

triggerEvent(t, it, "triggerEventWithDynamicTopic", DynamicEvent{Field: anyString}, bound, types.Unconfirmed)

input := struct{ Field string }{Field: anyString}
tp := cr.(clcommontypes.ContractTypeProvider)

readName := types.BoundContract{
Address: bindings[0].Address,
Name: AnyContractName,
}.ReadIdentifier(triggerWithDynamicTopic)

output, err := tp.CreateContractType(readName, false)
output, err := tp.CreateContractType(bound.ReadIdentifier(triggerWithDynamicTopic), false)
require.NoError(t, err)
rOutput := reflect.Indirect(reflect.ValueOf(output))

require.Eventually(t, func() bool {
return cr.GetLatestValue(ctx, readName, primitives.Unconfirmed, input, output) == nil
return cr.GetLatestValue(ctx, bound.ReadIdentifier(triggerWithDynamicTopic), primitives.Unconfirmed, input, output) == nil
}, it.MaxWaitTimeForEvents(), 100*time.Millisecond)

assert.Equal(t, &anyString, rOutput.FieldByName("Field").Interface())
Expand All @@ -75,21 +72,20 @@ func RunContractReaderEvmTests[T TestingT[T]](t T, it *EVMChainComponentsInterfa
ctx := it.Helper.Context(t)
cr := it.GetContractReader(t)
bindings := it.GetBindings(t)

bound := BindingsByName(bindings, AnyContractName)[0]
require.NoError(t, cr.Bind(ctx, bindings))

triggerFourTopics(t, it, int32(1), int32(2), int32(3))
triggerFourTopics(t, it, int32(2), int32(2), int32(3))
triggerFourTopics(t, it, int32(1), int32(3), int32(3))
triggerFourTopics(t, it, int32(1), int32(2), int32(4))

var bound types.BoundContract
for idx := range bindings {
if bindings[idx].Name == AnyContractName {
bound = bindings[idx]
}
type DynamicEvent struct {
Field1 int32
Field2 int32
Field3 int32
}

triggerEvent(t, it, "triggerWithFourTopics", DynamicEvent{Field1: int32(1), Field2: int32(2), Field3: int32(3)}, bound, types.Unconfirmed)
triggerEvent(t, it, "triggerWithFourTopics", DynamicEvent{Field1: int32(2), Field2: int32(2), Field3: int32(3)}, bound, types.Unconfirmed)
triggerEvent(t, it, "triggerWithFourTopics", DynamicEvent{Field1: int32(1), Field2: int32(3), Field3: int32(3)}, bound, types.Unconfirmed)
triggerEvent(t, it, "triggerWithFourTopics", DynamicEvent{Field1: int32(1), Field2: int32(2), Field3: int32(4)}, bound, types.Unconfirmed)

var latest struct{ Field1, Field2, Field3 int32 }
params := struct{ Field1, Field2, Field3 int32 }{Field1: 1, Field2: 2, Field3: 3}

Expand All @@ -108,19 +104,20 @@ func RunContractReaderEvmTests[T TestingT[T]](t T, it *EVMChainComponentsInterfa
ctx := it.Helper.Context(t)
bindings := it.GetBindings(t)

bound := BindingsByName(bindings, AnyContractName)[0]
require.NoError(t, cr.Bind(ctx, bindings))

triggerFourTopicsWithHashed(t, it, "1", [32]uint8{2}, [32]byte{5})
triggerFourTopicsWithHashed(t, it, "2", [32]uint8{2}, [32]byte{3})
triggerFourTopicsWithHashed(t, it, "1", [32]uint8{3}, [32]byte{3})

var bound types.BoundContract
for idx := range bindings {
if bindings[idx].Name == AnyContractName {
bound = bindings[idx]
}
// Define the struct for the dynamic event with string and hashed fields
type DynamicEvent struct {
Field1 string
Field2 [32]uint8
Field3 [32]byte
}

triggerEvent(t, it, "triggerWithFourTopicsWithHashed", DynamicEvent{Field1: "1", Field2: [32]uint8{2}, Field3: [32]byte{5}}, bound, types.Unconfirmed)
triggerEvent(t, it, "triggerWithFourTopicsWithHashed", DynamicEvent{Field1: "2", Field2: [32]uint8{2}, Field3: [32]byte{3}}, bound, types.Unconfirmed)
triggerEvent(t, it, "triggerWithFourTopicsWithHashed", DynamicEvent{Field1: "1", Field2: [32]uint8{3}, Field3: [32]byte{3}}, bound, types.Unconfirmed)

var latest struct {
Field3 [32]byte
}
Expand All @@ -147,6 +144,55 @@ func RunContractReaderEvmTests[T TestingT[T]](t T, it *EVMChainComponentsInterfa

require.ErrorIs(t, err, read.NoContractExistsError{Address: addr})
})

t.Run("Filtering can be done on address types and parsed as string", func(t T) {
it.Setup(t)
cr := it.GetContractReader(t)
ctx := it.Helper.Context(t)
bindings := it.GetBindings(t)
bound := BindingsByName(bindings, AnyContractName)[0]
require.NoError(t, cr.Bind(ctx, bindings))

// Define the struct for the dynamic event with address fields
type DynamicEvent struct {
Field1 common.Address
Field2 common.Address
}

// Trigger 3 events with diff addresses.
addr1ToMatch := common.HexToAddress("0xabcdefabcdefabcdefabcdefabcdefabcdefabcd")
addr2ToMatch := common.HexToAddress("0x0987654321abcdef0987654321abcdef09876543")
triggerEvent(t, it, "triggerWithAddress", DynamicEvent{
Field1: common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678"),
Field2: common.HexToAddress("0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"),
}, bound, types.Unconfirmed)
triggerEvent(t, it, "triggerWithAddress", DynamicEvent{
Field1: addr1ToMatch,
Field2: addr2ToMatch,
}, bound, types.Unconfirmed)
triggerEvent(t, it, "triggerWithAddress", DynamicEvent{
Field1: common.HexToAddress("0x9876543210abcdef9876543210abcdef98765432"),
Field2: common.HexToAddress("0x0123456789abcdef0123456789abcdef01234567"),
}, bound, types.Unconfirmed)

// returned event data
var latest struct {
Field1 string
Field2 string
}

// params to query the event with
params := struct {
Field1 common.Address
Field2 common.Address
}{Field1: addr1ToMatch, Field2: addr2ToMatch}

// Retrieve the latest values for the event and verify the addresses were parsed correctly
time.Sleep(it.MaxWaitTimeForEvents())
require.NoError(t, cr.GetLatestValue(ctx, bound.ReadIdentifier(triggerWithAddress), primitives.Unconfirmed, params, &latest))
assert.Equal(t, addr1ToMatch.Hex(), latest.Field1)
assert.Equal(t, addr2ToMatch.Hex(), latest.Field2)
})
}

func RunContractReaderInLoopTests[T TestingT[T]](t T, it ChainComponentsInterfaceTester[T]) {
Expand All @@ -162,12 +208,12 @@ func RunContractReaderInLoopTests[T TestingT[T]](t T, it ChainComponentsInterfac
boundContract := BindingsByName(bindings, AnyContractName)[0]
require.NoError(t, cr.Bind(ctx, bindings))

ts1 := CreateTestStruct[T](0, it)
_ = SubmitTransactionToCW(t, it, MethodTriggeringEvent, ts1, boundContract, types.Unconfirmed)
ts2 := CreateTestStruct[T](15, it)
_ = SubmitTransactionToCW(t, it, MethodTriggeringEvent, ts2, boundContract, types.Unconfirmed)
ts3 := CreateTestStruct[T](35, it)
_ = SubmitTransactionToCW(t, it, MethodTriggeringEvent, ts3, boundContract, types.Unconfirmed)
ts1 := CreateTestStruct(0, it)
triggerEvent(t, it, MethodTriggeringEvent, ts1, boundContract, types.Unconfirmed)
ts2 := CreateTestStruct(15, it)
triggerEvent(t, it, MethodTriggeringEvent, ts2, boundContract, types.Unconfirmed)
ts3 := CreateTestStruct(35, it)
triggerEvent(t, it, MethodTriggeringEvent, ts3, boundContract, types.Unconfirmed)

ts := &TestStruct{}
assert.Eventually(t, func() bool {
Expand All @@ -184,22 +230,8 @@ func RunContractReaderInLoopTests[T TestingT[T]](t T, it ChainComponentsInterfac
})
}

func triggerFourTopics[T TestingT[T]](t T, it *EVMChainComponentsInterfaceTester[T], i1, i2, i3 int32) {
type DynamicEvent struct {
Field1 int32
Field2 int32
Field3 int32
}
contracts := it.GetBindings(t)
SubmitTransactionToCW(t, it, "triggerWithFourTopics", DynamicEvent{Field1: i1, Field2: i2, Field3: i3}, contracts[0], types.Unconfirmed)
}

func triggerFourTopicsWithHashed[T TestingT[T]](t T, it *EVMChainComponentsInterfaceTester[T], i1 string, i2 [32]uint8, i3 [32]byte) {
type DynamicEvent struct {
Field1 string
Field2 [32]uint8
Field3 [32]byte
}
contracts := it.GetBindings(t)
SubmitTransactionToCW(t, it, "triggerWithFourTopicsWithHashed", DynamicEvent{Field1: i1, Field2: i2, Field3: i3}, contracts[0], types.Unconfirmed)
func triggerEvent[T TestingT[T]](t T, it ChainComponentsInterfaceTester[T], methodName string,
dynamicEvent any, boundContract clcommontypes.BoundContract, confirmationType types.TransactionStatus,
) {
SubmitTransactionToCW(t, it, methodName, dynamicEvent, boundContract, confirmationType)
}

0 comments on commit 0ed1ac0

Please sign in to comment.