Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
pkieltyka committed Dec 12, 2024
1 parent 76c1637 commit 371a4c3
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 44 deletions.
68 changes: 24 additions & 44 deletions ethcoder/typed_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,19 +238,18 @@ func TypedDataFromJSON(typedDataJSON string) (*TypedData, error) {
}

func (t *TypedData) UnmarshalJSON(data []byte) error {
// Create an intermediate structure using json.Number
// 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"`
}

// Create a decoder that will preserve number strings
// Json decoder with json.Number support
dec := json.NewDecoder(bytes.NewReader(data))
dec.UseNumber()

// First unmarshal into the intermediate structure
var raw TypedDataRaw
if err := dec.Decode(&raw); err != nil {
return err
Expand Down Expand Up @@ -278,15 +277,18 @@ func (t *TypedData) UnmarshalJSON(data []byte) error {
}
}

// ..
// Check primary type is defined
if raw.PrimaryType == "" {
return fmt.Errorf("primary type is required")
}
primaryDomainType, ok := raw.Types[raw.PrimaryType]
if !ok {
return fmt.Errorf("primary type %s is not defined", raw.PrimaryType)
return fmt.Errorf("primary type '%s' is not defined", raw.PrimaryType)
}
primaryDomainTypeMap := typedDataTypeMap(primaryDomainType)
fmt.Println("===> primaryDomainType", primaryDomainTypeMap)

// Process the Message map to convert values to desired types
// Decode the message map into the typedData struct
processedMessage := make(map[string]interface{})
for k, v := range raw.Message {
fmt.Println("===> k", k, "v", v)
Expand All @@ -297,45 +299,23 @@ func (t *TypedData) UnmarshalJSON(data []byte) error {
}
fmt.Println("===> typ", k, typ)

// TODO: its possible that the type is a struct, and we need to do another call to get the typedData map, etc

switch val := v.(type) {
case json.Number:
// TODO: we will check the domain, etc.........

if typ == "uint8" {
num, err := val.Int64()
if err != nil {
return fmt.Errorf("failed to parse uint8 value %s, because %w", val, err)
}
// TODO: is this okay ... int64 to uint8 ..???...
processedMessage[k] = uint8(num)
} else {
// Try parsing as big.Int first
if n, ok := new(big.Int).SetString(string(val), 10); ok {
processedMessage[k] = n
} else {
// If it's not a valid integer, keep the original value
processedMessage[k] = v
}
}

case string:
if typ == "address" {
addr := common.HexToAddress(val)
processedMessage[k] = addr
} else if len(val) > 2 && (val[:2] == "0x" || val[:2] == "0X") {
// Convert hex strings to *big.Int
n := new(big.Int)
n.SetString(val[2:], 16)
processedMessage[k] = n
} else {
processedMessage[k] = val
// ...
customType, ok := raw.Types[typ]
if ok {
val := fmt.Sprintf("%v", v)
fmt.Println("===> customType", customType, val)
// processedMessage[k] = val

// ............
// ..

} else {
val := fmt.Sprintf("%v", v)
out, err := ABIUnmarshalStringValuesAny([]string{typ}, []any{val})
if err != nil {
return fmt.Errorf("failed to unmarshal string value for type %s with argument name %s, because %w", typ, k, err)
}

default:
// TODO: prob needs to be recursive.. cuz might be some array or object ..
return fmt.Errorf("unsupported type %T for value %v", v, v)
processedMessage[k] = out[0]
}
}

Expand Down
79 changes: 79 additions & 0 deletions ethcoder/typed_data_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,82 @@ func TestTypedDataFromJSON(t *testing.T) {
require.NoError(t, err)
require.True(t, valid)
}

func TestTypedDataFromJSONPart2(t *testing.T) {
// NOTE: we omit the EIP712Domain type definition because it will
// automatically be added by the library if its not specified
typedDataJson := `{
"types": {
"Person": [
{ "name": "name", "type": "string" },
{ "name": "wallets", "type": "address[]" }
],
"Mail": [
{ "name": "from", "type": "Person" },
{ "name": "to", "type": "Person[]" },
{ "name": "contents", "type": "string" }
],
"Group": [
{ "name": "name", "type": "string" },
{ "name": "members", "type": "Person[]" }
]
},
"domain": {
"name": "Ether Mail",
"version": "1",
"chainId": 1,
"verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
},
"primaryType": "Mail",
"message": {
"from": {
"name": "Cow",
"wallets": [
"0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826",
"0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF"
]
},
"to": [{
"name": "Bob",
"wallets": [
"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB",
"0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57",
"0xB0B0b0b0b0b0B000000000000000000000000000"
]
}],
"contents": "Hello, Bob!"
}
}`

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

domainHash, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map())
require.NoError(t, err)
require.Equal(t, "0xf2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f", ethcoder.HexEncode(domainHash))

digest, typedDataEncoded, err := typedData.Encode()
require.NoError(t, err)
require.Equal(t, "0x2218fda59750be7bb9e5dfb2b49e4ec000dc2542862c5826f1fe980d6d727e95", ethcoder.HexEncode(digest))
require.Equal(t, "0x1901f2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090ff5117e79519388f3d62844df1325ebe783523d9db9762c50fa78a60400a20b5b", ethcoder.HexEncode(typedDataEncoded))

// Sign and validate
wallet, err := ethwallet.NewWalletFromMnemonic("dose weasel clever culture letter volume endorse used harvest ripple circle install")
require.NoError(t, err)

ethSigedTypedData, typedDataEncodedOut, err := wallet.SignTypedData(typedData)
ethSigedTypedDataHex := ethcoder.HexEncode(ethSigedTypedData)
require.NoError(t, err)
require.Equal(t, typedDataEncoded, typedDataEncodedOut)

// NOTE: this signature and above method has been compared against ethers v6 test
require.Equal(t,
"0x296c98bed8f3fd7ea96f55ca8148b4d092cbada953c8d9205b2fff759461ab4e6d6db0b78833b954684900530caeee9aaef8e42dfd8439a3fa107e910b57e2cc1b",
ethSigedTypedDataHex,
)

// recover / validate signature
valid, err := ethwallet.ValidateEthereumSignature(wallet.Address().Hex(), typedDataEncodedOut, ethSigedTypedDataHex)
require.NoError(t, err)
require.True(t, valid)
}

0 comments on commit 371a4c3

Please sign in to comment.