diff --git a/pkg/abi/abidecode.go b/pkg/abi/abidecode.go index afa076d..12db5fb 100644 --- a/pkg/abi/abidecode.go +++ b/pkg/abi/abidecode.go @@ -88,14 +88,25 @@ func decodeABIElement(ctx context.Context, breadcrumbs string, block []byte, hea } return 32, cv, err case TupleComponent: - headOffset, err := decodeABILength(ctx, breadcrumbs, block, headPosition) + dynamic, err := isDynamicType(ctx, component) if err != nil { return -1, nil, err } - headStart += headOffset - headPosition = headStart - _, cv, err := walkDynamicChildArrayABIBytes(ctx, "tup", breadcrumbs, block, headStart, headPosition, component, component.tupleChildren) - return 32, cv, err + if dynamic { + headOffset, err := decodeABILength(ctx, breadcrumbs, block, headPosition) + if err != nil { + return -1, nil, err + } + headStart += headOffset + headPosition = headStart + } + + headBytesRead, cv, err := walkDynamicChildArrayABIBytes(ctx, "tup", breadcrumbs, block, headStart, headPosition, component, component.tupleChildren) + if dynamic { + // In the case where it's dynamic we only read one block + headBytesRead = 32 + } + return headBytesRead, cv, err default: return -1, nil, i18n.NewError(ctx, signermsgs.MsgBadABITypeComponent, component.cType) } diff --git a/pkg/abi/abidecode_test.go b/pkg/abi/abidecode_test.go index befd8c2..1f41941 100644 --- a/pkg/abi/abidecode_test.go +++ b/pkg/abi/abidecode_test.go @@ -346,6 +346,35 @@ func TestExampleABIDecodeTuple(t *testing.T) { assert.Equal(t, big.NewInt(2414), cv.Children[0].Children[0].Children[3].Value) } +// TestExampleABIDecodeNonDynamicTuple is responsible for testing a duple that does +// not have any dynamic fields +func TestExampleABIDecodeNonDynamicTuple(t *testing.T) { + + f := &Entry{ + Name: "coupon", + Outputs: ParameterArray{ + { + Type: "tuple", + Name: "mystruct", + Components: ParameterArray{ + {Type: "address", Name: "account"}, + {Type: "uint256", Name: "value"}, + }, + }, + }, + } + + d, _ := hex.DecodeString("" + + "000000000000000000000000d2d8a61d774f552301d71aa99a78aff2e4765ca7" + + "000000000000000000000000000000000000000000000000000000000000007b") + + cv, err := f.Outputs.DecodeABIData(d, 0) + assert.NoError(t, err) + assert.Equal(t, big.NewInt(123), cv.Children[0].Children[1].Value) + address, _ := cv.Children[0].Children[0].JSON() + assert.Equal(t, "\"d2d8a61d774f552301d71aa99a78aff2e4765ca7\"", string(address)) +} + func TestExampleABIDecodeDoubleNestedTuple(t *testing.T) { f := &Entry{ @@ -410,6 +439,112 @@ func TestExampleABIDecodeDoubleNestedTuple(t *testing.T) { assert.Equal(t, big.NewInt(66666), cv.Children[0].Children[0].Children[3].Value) } +func TestExampleABIDecodeStaticNestedTupleInDynamicTuple(t *testing.T) { + + f := &Entry{ + Name: "f", + Outputs: ParameterArray{ + { + Type: "tuple[]", + Name: "nested", + Components: ParameterArray{ + {Type: "uint256", Name: "a"}, + {Type: "string", Name: "b"}, + {Type: "tuple", Name: "c", Components: ParameterArray{ + {Type: "uint256", Name: "c1"}, + {Type: "uint256", Name: "c2"}, + }}, + {Type: "uint256", Name: "d"}, + }, + }, + }, + } + + b, err := f.Outputs.EncodeABIDataJSON([]byte(`{ + "nested": [{ + "a": 11111, + "b": "test22222", + "c": { + "c1": 33333, + "c2": 44444 + }, + "d": 55555 + }] + }`)) + assert.NoError(t, err) + + assert.Equal(t, + "0000000000000000000000000000000000000000000000000000000000000020"+ // 0 - 32 - offset for the start of the dynamic array + "0000000000000000000000000000000000000000000000000000000000000001"+ // 32 - 1 - number of tuples in the dynamic array + "0000000000000000000000000000000000000000000000000000000000000020"+ // 64 - 32 - offset of the data for the tuple at position 0 in the array + "0000000000000000000000000000000000000000000000000000000000002b67"+ // 96 - 11111 - value "a" + "00000000000000000000000000000000000000000000000000000000000000a0"+ // 128 - 160 - offset of the string data for "b", relative to 96 = 224 + "0000000000000000000000000000000000000000000000000000000000008235"+ // 160 - 33333 - value of "c1" + "000000000000000000000000000000000000000000000000000000000000ad9c"+ // 192 - 44444 - value of "c2" + "000000000000000000000000000000000000000000000000000000000000d903"+ // 224 - 55555 - value of "d" + "0000000000000000000000000000000000000000000000000000000000000009"+ // 256 - 9 - length of the string data for "b" + "7465737432323232320000000000000000000000000000000000000000000000", // 288 - "test22222" + hex.EncodeToString(b)) + + cv, err := f.Outputs.DecodeABIData(b, 0) + + assert.Equal(t, big.NewInt(11111), cv.Children[0].Children[0].Children[0].Value) + assert.Equal(t, "test22222", cv.Children[0].Children[0].Children[1].Value) + assert.Equal(t, big.NewInt(33333), cv.Children[0].Children[0].Children[2].Children[0].Value) + assert.Equal(t, big.NewInt(44444), cv.Children[0].Children[0].Children[2].Children[1].Value) + assert.Equal(t, big.NewInt(55555), cv.Children[0].Children[0].Children[3].Value) +} + +func TestExampleABIDecodeDoubleStaticNestedTuple(t *testing.T) { + + f := &Entry{ + Name: "f", + Outputs: ParameterArray{ + { + Type: "tuple[]", + Name: "nested", + Components: ParameterArray{ + {Type: "uint256", Name: "a"}, + {Type: "tuple", Name: "c", Components: ParameterArray{ + {Type: "uint256", Name: "c1"}, + {Type: "uint256", Name: "c2"}, + }}, + {Type: "uint256", Name: "d"}, + }, + }, + }, + } + + b, err := f.Outputs.EncodeABIDataJSON([]byte(`{ + "nested": [{ + "a": 11111, + "c": { + "c1": 22222, + "c2": 33333 + }, + "d": 44444 + }] + }`)) + assert.NoError(t, err) + + // The encoding looks right to me + assert.Equal(t, + "0000000000000000000000000000000000000000000000000000000000000020"+ // 0 - 32 - offset for the start of the dynamic array + "0000000000000000000000000000000000000000000000000000000000000001"+ // 32 - 1 - number of tuples in the dynamic array + "0000000000000000000000000000000000000000000000000000000000002b67"+ // 64 - 11111 - value of "a" + "00000000000000000000000000000000000000000000000000000000000056ce"+ // 96 - 22222 - value of "c1" + "0000000000000000000000000000000000000000000000000000000000008235"+ // 128 - 33333 - value of "c2" + "000000000000000000000000000000000000000000000000000000000000ad9c", // 160 - 44444 - value of "d" + hex.EncodeToString(b)) + + cv, err := f.Outputs.DecodeABIData(b, 0) + + assert.Equal(t, big.NewInt(11111), cv.Children[0].Children[0].Children[0].Value) + assert.Equal(t, big.NewInt(22222), cv.Children[0].Children[0].Children[1].Children[0].Value) + assert.Equal(t, big.NewInt(33333), cv.Children[0].Children[0].Children[1].Children[1].Value) + assert.Equal(t, big.NewInt(44444), cv.Children[0].Children[0].Children[2].Value) +} + func TestExampleABIDecodeTupleFixed(t *testing.T) { f := &Entry{ @@ -834,7 +969,6 @@ func TestIsDynamicTypeBadNestedTupleType(t *testing.T) { }) assert.Regexp(t, "FF22041", err) } - func TestDecodeABIElementBadDynamicTypeFixedArray(t *testing.T) { block, err := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000020") @@ -848,6 +982,20 @@ func TestDecodeABIElementBadDynamicTypeFixedArray(t *testing.T) { assert.Regexp(t, "FF22041", err) } +func TestDecodeABIElementBadDynamicTypeTuple(t *testing.T) { + + block, err := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000020") + assert.NoError(t, err) + + _, _, err = decodeABIElement(context.Background(), "", block, 0, 0, &typeComponent{ + cType: TupleComponent, + tupleChildren: []*typeComponent{ + {cType: 99}, + }, + }) + assert.Regexp(t, "FF22041", err) +} + func TestDecodeABIElementInsufficientDataFixedArrayDynamicType(t *testing.T) { p := &ParameterArray{ diff --git a/pkg/abi/abiencode.go b/pkg/abi/abiencode.go index 720e5b0..f16f7da 100644 --- a/pkg/abi/abiencode.go +++ b/pkg/abi/abiencode.go @@ -48,7 +48,7 @@ func (cv *ComponentValue) encodeABIData(ctx context.Context, desc string) ([]byt case DynamicArrayComponent: return cv.encodeABIChildren(ctx, desc, true /* always dynamic */, true /* need length */) case TupleComponent: - return cv.encodeABIChildren(ctx, desc, true /* always dynamic */, false /* no length */) + return cv.encodeABIChildren(ctx, desc, false /* only dynamic if the children are dynamic */, false /* no length */) default: return nil, false, i18n.NewError(ctx, signermsgs.MsgBadABITypeComponent, tc.cType) }