Skip to content

Commit

Permalink
ethcoder: improve eip712 salt encoding (#150)
Browse files Browse the repository at this point in the history
  • Loading branch information
pkieltyka authored Dec 16, 2024
1 parent 404855b commit a630de7
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 6 deletions.
2 changes: 1 addition & 1 deletion ethcoder/typed_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ type TypedDataDomain struct {
Version string `json:"version,omitempty"`
ChainID *big.Int `json:"chainId,omitempty"`
VerifyingContract *common.Address `json:"verifyingContract,omitempty"`
Salt *[32]byte `json:"salt,omitempty"`
Salt *common.Hash `json:"salt,omitempty"`
}

func (t TypedDataDomain) Map() map[string]interface{} {
Expand Down
133 changes: 128 additions & 5 deletions ethcoder/typed_data_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ func TestTypedDataCase1(t *testing.T) {

func TestTypedDataCase2(t *testing.T) {
verifyingContract := common.HexToAddress("0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC")
salt := common.HexToHash("0x1122112211221122112211221122112211221122112211221122112211221122")

typedData := &ethcoder.TypedData{
Types: ethcoder.TypedDataTypes{
Expand All @@ -109,6 +110,7 @@ func TestTypedDataCase2(t *testing.T) {
{Name: "version", Type: "string"},
{Name: "chainId", Type: "uint256"},
{Name: "verifyingContract", Type: "address"},
{Name: "salt", Type: "bytes32"},
},
"Person": {
{Name: "name", Type: "string"},
Expand All @@ -122,6 +124,7 @@ func TestTypedDataCase2(t *testing.T) {
Version: "1",
ChainID: big.NewInt(1),
VerifyingContract: &verifyingContract,
Salt: &salt,
},
Message: map[string]interface{}{
"name": "Bob",
Expand All @@ -131,12 +134,12 @@ func TestTypedDataCase2(t *testing.T) {
}

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

digest, _, err := typedData.Encode()
assert.NoError(t, err)
assert.Equal(t, "0x2218fda59750be7bb9e5dfb2b49e4ec000dc2542862c5826f1fe980d6d727e95", ethcoder.HexEncode(digest))
_, encodedTypeData, err := typedData.Encode()
require.NoError(t, err)
require.Equal(t, "0x19017f94cc8efb643110a5cd5c94f08051bb7d6203f3f457a43d7f3138a219a1ea6ff5117e79519388f3d62844df1325ebe783523d9db9762c50fa78a60400a20b5b", ethcoder.HexEncode(encodedTypeData))

// fmt.Println("===> digest", HexEncode(digest))
}
Expand Down Expand Up @@ -291,3 +294,123 @@ func TestTypedDataFromJSONPart2(t *testing.T) {
require.NoError(t, err)
require.True(t, valid)
}

func TestTypedDataFromJSONPart3(t *testing.T) {
typedDataJson := `{
"types": {
"EIP712Domain": [
{"name": "name", "type": "string"},
{"name": "version", "type": "string"},
{"name": "chainId", "type": "uint256"},
{"name": "verifyingContract", "type": "address"}
],
"Person": [
{"name": "name", "type": "string"},
{"name": "wallet", "type": "address"},
{"name": "count", "type": "uint8"},
{"name": "data", "type": "bytes"},
{"name": "hash", "type": "bytes32"}
]
},
"primaryType": "Person",
"domain": {
"name": "Ether Mail",
"version": "1",
"chainId": 1,
"verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
},
"message": {
"name": "Bob",
"wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB",
"count": 4,
"data": "0x1234567890abcdef",
"hash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
}
}`

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))

// 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)

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

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

func TestTypedDataFromJSONPart4(t *testing.T) {
typedDataJson := `{
"types": {
"EIP712Domain": [
{ "name": "name", "type": "string" },
{ "name": "version", "type": "string" },
{ "name": "chainId", "type": "uint256" },
{ "name": "verifyingContract", "type": "address" },
{ "name": "salt", "type": "bytes32" }
],
"ExampleMessage": [
{ "name": "message", "type": "string" },
{ "name": "value", "type": "uint256" },
{ "name": "from", "type": "address" },
{ "name": "to", "type": "address" }
]
},
"domain": {
"name": "EIP712Example",
"version": "1",
"chainId": 5,
"verifyingContract": "0xc0ffee254729296a45a3885639AC7E10F9d54979",
"salt": "0x70736575646f2d72616e646f6d2076616c756500000000000000000000000000"
},
"primaryType": "ExampleMessage",
"message": {
"message": "Test message",
"value": 10000,
"from": "0xc0ffee254729296a45a3885639AC7E10F9d54979",
"to": "0xc0ffee254729296a45a3885639AC7E10F9d54979"
}
}`

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

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

// 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)

// NOTE: this signature and above method has been compared against ethers v6 test
require.Equal(t,
"0xdf10e9cd68dc1464a8ddceeb2875be9e12c8706ce2467f5dec987f36731dac2a34130d46c5a96ca182ead8530bc0b16920487f5c2d378aaafd93c6e41617f3ea1b",
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 a630de7

Please sign in to comment.