Skip to content

Commit

Permalink
ethcoder: eip712 additional test cases (#154)
Browse files Browse the repository at this point in the history
  • Loading branch information
pkieltyka authored Dec 19, 2024
1 parent 0d8f8a3 commit 7f8d429
Show file tree
Hide file tree
Showing 4 changed files with 234 additions and 9 deletions.
8 changes: 7 additions & 1 deletion ethcoder/abi_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -414,8 +414,14 @@ func ABIUnmarshalStringValues(argTypes []string, stringValues []string) ([]any,
return nil, fmt.Errorf("ethcoder: value at position %d is invalid. invalid number type '%s'", i, typ)
}

base := 10
if strings.HasPrefix(s, "0x") {
base = 16
s = s[2:]
}

num := big.NewInt(0)
num, ok := num.SetString(s, 10)
num, ok := num.SetString(s, base)
if !ok {
return nil, fmt.Errorf("ethcoder: value at position %d is invalid. expecting number. unable to set value of '%s'", i, s)
}
Expand Down
28 changes: 28 additions & 0 deletions ethcoder/abi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,20 @@ func TestABIUnmarshalStringValuesAny(t *testing.T) {
assert.Equal(t, int64(2), v2.Int64())
}

{
values, err := ABIUnmarshalStringValuesAny([]string{"address", "uint256"}, []any{"0x6615e4e985bf0d137196897dfa182dbd7127f54f", "0x123456"})
assert.NoError(t, err)
assert.Len(t, values, 2)

v1, ok := values[0].(common.Address)
assert.True(t, ok)
assert.Equal(t, "0x6615e4e985BF0D137196897Dfa182dBD7127f54f", v1.String())

v2, ok := values[1].(*big.Int)
assert.True(t, ok)
assert.Equal(t, int64(1193046), v2.Int64())
}

{
values, err := ABIUnmarshalStringValuesAny([]string{"address", "bytes8"}, []any{"0x6615e4e985bf0d137196897dfa182dbd7127f54f", "0xaabbccddaabbccdd"})
assert.NoError(t, err)
Expand Down Expand Up @@ -360,6 +374,20 @@ func TestABIUnmarshalStringValues(t *testing.T) {
assert.Equal(t, int64(2), v2.Int64())
}

{
values, err := ABIUnmarshalStringValues([]string{"address", "uint256"}, []string{"0x6615e4e985bf0d137196897dfa182dbd7127f54f", "0x123456"})
assert.NoError(t, err)
assert.Len(t, values, 2)

v1, ok := values[0].(common.Address)
assert.True(t, ok)
assert.Equal(t, "0x6615e4e985BF0D137196897Dfa182dBD7127f54f", v1.String())

v2, ok := values[1].(*big.Int)
assert.True(t, ok)
assert.Equal(t, int64(1193046), v2.Int64())
}

{
values, err := ABIUnmarshalStringValues([]string{"address", "bytes8"}, []string{"0x6615e4e985bf0d137196897dfa182dbd7127f54f", "0xaabbccddaabbccdd"})
assert.NoError(t, err)
Expand Down
43 changes: 37 additions & 6 deletions ethcoder/typed_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,10 +282,16 @@ func TypedDataFromJSON(typedDataJSON string) (*TypedData, error) {
func (t *TypedData) UnmarshalJSON(data []byte) error {
// Intermediary structure to decode message field
type TypedDataRaw struct {
Types TypedDataTypes `json:"types"`
PrimaryType string `json:"primaryType"`
Domain TypedDataDomain `json:"domain"`
Message map[string]interface{} `json:"message"`
Types TypedDataTypes `json:"types"`
PrimaryType string `json:"primaryType"`
Domain struct {
Name string `json:"name,omitempty"`
Version string `json:"version,omitempty"`
ChainID interface{} `json:"chainId,omitempty"`
VerifyingContract *common.Address `json:"verifyingContract,omitempty"`
Salt *common.Hash `json:"salt,omitempty"`
} `json:"domain"`
Message map[string]interface{} `json:"message"`
}

// Json decoder with json.Number support, so that we can decode big.Int values
Expand Down Expand Up @@ -333,6 +339,31 @@ func (t *TypedData) UnmarshalJSON(data []byte) error {
return fmt.Errorf("primary type '%s' is not defined", raw.PrimaryType)
}

// Decode the domain, which is mostly decooded except the chainId is an interface{} type
// because the value may be a number of a hex encoded number. We want it in a big.Int.
domain := TypedDataDomain{
Name: raw.Domain.Name,
Version: raw.Domain.Version,
ChainID: nil,
VerifyingContract: raw.Domain.VerifyingContract,
Salt: raw.Domain.Salt,
}
if raw.Domain.ChainID != nil {
chainID := big.NewInt(0)
if val, ok := raw.Domain.ChainID.(float64); ok {
chainID.SetInt64(int64(val))
} else if val, ok := raw.Domain.ChainID.(json.Number); ok {
chainID.SetString(val.String(), 10)
} else if val, ok := raw.Domain.ChainID.(string); ok {
if strings.HasPrefix(val, "0x") {
chainID.SetString(val[2:], 16)
} else {
chainID.SetString(val, 10)
}
}
domain.ChainID = chainID
}

// Decode the raw message into Go runtime types
message, err := typedDataDecodeRawMessageMap(raw.Types.Map(), raw.PrimaryType, raw.Message)
if err != nil {
Expand All @@ -341,7 +372,7 @@ func (t *TypedData) UnmarshalJSON(data []byte) error {

t.Types = raw.Types
t.PrimaryType = raw.PrimaryType
t.Domain = raw.Domain
t.Domain = domain

m, ok := message.(map[string]interface{})
if !ok {
Expand Down Expand Up @@ -465,7 +496,7 @@ func typedDataDecodePrimitiveValue(typ string, value interface{}) (interface{},
val := fmt.Sprintf("%v", value)
out, err := ABIUnmarshalStringValuesAny([]string{typ}, []any{val})
if err != nil {
return nil, err
return nil, fmt.Errorf("typedDataDecodePrimitiveValue: %w", err)
}
return out[0], nil
}
164 changes: 162 additions & 2 deletions ethcoder/typed_data_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ func TestTypedDataFromJSONPart4(t *testing.T) {
require.True(t, valid)
}

func TypedDataFromJSONPart5(t *testing.T) {
func TestTypedDataFromJSONPart5(t *testing.T) {
typedDataJson := `{
"types": {
"EIP712Domain": [
Expand All @@ -443,7 +443,7 @@ func TypedDataFromJSONPart5(t *testing.T) {
},
"message": {
"message": "Test message",
"value": "0x634abebe1d4da48b00000000000000000cde63753dad4f0f42f79ebef71ee924,
"value": "0x634abebe1d4da48b00000000000000000cde63753dad4f0f42f79ebef71ee924",
"from": "0xc0ffee254729296a45a3885639AC7E10F9d54979",
"to": "0xc0ffee254729296a45a3885639AC7E10F9d54979"
}
Expand All @@ -454,3 +454,163 @@ func TypedDataFromJSONPart5(t *testing.T) {

require.Equal(t, typedData.Domain.ChainID.Int64(), int64(15))
}

func TestTypedDataFromJSONPart6(t *testing.T) {
typedDataJson := `{
"domain": {
"name": "Seaport",
"version": "1.5",
"chainId": 80002,
"verifyingContract": "0x00000000000000adc04c56bf30ac9d3c0aaf14dc"
},
"message": {
"conduitKey": "0xf3d63166f0ca56c3c1a3508fce03ff0cf3fb691e000000000000000000000000",
"consideration": [
{
"endAmount": "1",
"identifierOrCriteria": "1",
"itemType": 3,
"recipient": "0x033ccc543501e462a2d50b579845709ff21f2eb6",
"startAmount": "1",
"token": "0xb7d432df27ab7b2a1be636bd945e6cb63bc84feb"
}
],
"counter": "0",
"endTime": 1735219168,
"offer": [
{
"endAmount": "1",
"identifierOrCriteria": "0",
"itemType": 1,
"startAmount": "1",
"token": "0x41e94eb019c0762f9bfcf9fb1e58725bfb0e7582"
}
],
"offerer": "0x033ccc543501e462a2d50b579845709ff21f2eb6",
"orderType": 1,
"salt": "0x634abebe1d4da48b0000000000000000f6dad44ce6d8c81dcbf213906d353f0c",
"startTime": 1734614365,
"zone": "0x0000000000000000000000000000000000000000",
"zoneHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
},
"primaryType": "OrderComponents",
"types": {
"EIP712Domain": [
{
"name": "name",
"type": "string"
},
{
"name": "version",
"type": "string"
},
{
"name": "chainId",
"type": "uint256"
},
{
"name": "verifyingContract",
"type": "address"
}
],
"ConsiderationItem": [
{
"name": "itemType",
"type": "uint8"
},
{
"name": "token",
"type": "address"
},
{
"name": "identifierOrCriteria",
"type": "uint256"
},
{
"name": "startAmount",
"type": "uint256"
},
{
"name": "endAmount",
"type": "uint256"
},
{
"name": "recipient",
"type": "address"
}
],
"OfferItem": [
{
"name": "itemType",
"type": "uint8"
},
{
"name": "token",
"type": "address"
},
{
"name": "identifierOrCriteria",
"type": "uint256"
},
{
"name": "startAmount",
"type": "uint256"
},
{
"name": "endAmount",
"type": "uint256"
}
],
"OrderComponents": [
{
"name": "offerer",
"type": "address"
},
{
"name": "zone",
"type": "address"
},
{
"name": "offer",
"type": "OfferItem[]"
},
{
"name": "consideration",
"type": "ConsiderationItem[]"
},
{
"name": "orderType",
"type": "uint8"
},
{
"name": "startTime",
"type": "uint256"
},
{
"name": "endTime",
"type": "uint256"
},
{
"name": "zoneHash",
"type": "bytes32"
},
{
"name": "salt",
"type": "uint256"
},
{
"name": "conduitKey",
"type": "bytes32"
},
{
"name": "counter",
"type": "uint256"
}
]
}
}`

typedData, err := ethcoder.TypedDataFromJSON(typedDataJson)
require.NoError(t, err)
require.NotNil(t, typedData)
}

0 comments on commit 7f8d429

Please sign in to comment.