Skip to content

Commit 371a4c3

Browse files
committed
update
1 parent 76c1637 commit 371a4c3

2 files changed

Lines changed: 103 additions & 44 deletions

File tree

ethcoder/typed_data.go

Lines changed: 24 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -238,19 +238,18 @@ func TypedDataFromJSON(typedDataJSON string) (*TypedData, error) {
238238
}
239239

240240
func (t *TypedData) UnmarshalJSON(data []byte) error {
241-
// Create an intermediate structure using json.Number
241+
// Intermediary structure to decode message field
242242
type TypedDataRaw struct {
243243
Types TypedDataTypes `json:"types"`
244244
PrimaryType string `json:"primaryType"`
245245
Domain TypedDataDomain `json:"domain"`
246246
Message map[string]interface{} `json:"message"`
247247
}
248248

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

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

281-
// ..
280+
// Check primary type is defined
281+
if raw.PrimaryType == "" {
282+
return fmt.Errorf("primary type is required")
283+
}
282284
primaryDomainType, ok := raw.Types[raw.PrimaryType]
283285
if !ok {
284-
return fmt.Errorf("primary type %s is not defined", raw.PrimaryType)
286+
return fmt.Errorf("primary type '%s' is not defined", raw.PrimaryType)
285287
}
286288
primaryDomainTypeMap := typedDataTypeMap(primaryDomainType)
287289
fmt.Println("===> primaryDomainType", primaryDomainTypeMap)
288290

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

300-
// TODO: its possible that the type is a struct, and we need to do another call to get the typedData map, etc
301-
302-
switch val := v.(type) {
303-
case json.Number:
304-
// TODO: we will check the domain, etc.........
305-
306-
if typ == "uint8" {
307-
num, err := val.Int64()
308-
if err != nil {
309-
return fmt.Errorf("failed to parse uint8 value %s, because %w", val, err)
310-
}
311-
// TODO: is this okay ... int64 to uint8 ..???...
312-
processedMessage[k] = uint8(num)
313-
} else {
314-
// Try parsing as big.Int first
315-
if n, ok := new(big.Int).SetString(string(val), 10); ok {
316-
processedMessage[k] = n
317-
} else {
318-
// If it's not a valid integer, keep the original value
319-
processedMessage[k] = v
320-
}
321-
}
322-
323-
case string:
324-
if typ == "address" {
325-
addr := common.HexToAddress(val)
326-
processedMessage[k] = addr
327-
} else if len(val) > 2 && (val[:2] == "0x" || val[:2] == "0X") {
328-
// Convert hex strings to *big.Int
329-
n := new(big.Int)
330-
n.SetString(val[2:], 16)
331-
processedMessage[k] = n
332-
} else {
333-
processedMessage[k] = val
302+
// ...
303+
customType, ok := raw.Types[typ]
304+
if ok {
305+
val := fmt.Sprintf("%v", v)
306+
fmt.Println("===> customType", customType, val)
307+
// processedMessage[k] = val
308+
309+
// ............
310+
// ..
311+
312+
} else {
313+
val := fmt.Sprintf("%v", v)
314+
out, err := ABIUnmarshalStringValuesAny([]string{typ}, []any{val})
315+
if err != nil {
316+
return fmt.Errorf("failed to unmarshal string value for type %s with argument name %s, because %w", typ, k, err)
334317
}
335-
336-
default:
337-
// TODO: prob needs to be recursive.. cuz might be some array or object ..
338-
return fmt.Errorf("unsupported type %T for value %v", v, v)
318+
processedMessage[k] = out[0]
339319
}
340320
}
341321

ethcoder/typed_data_test.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,3 +208,82 @@ func TestTypedDataFromJSON(t *testing.T) {
208208
require.NoError(t, err)
209209
require.True(t, valid)
210210
}
211+
212+
func TestTypedDataFromJSONPart2(t *testing.T) {
213+
// NOTE: we omit the EIP712Domain type definition because it will
214+
// automatically be added by the library if its not specified
215+
typedDataJson := `{
216+
"types": {
217+
"Person": [
218+
{ "name": "name", "type": "string" },
219+
{ "name": "wallets", "type": "address[]" }
220+
],
221+
"Mail": [
222+
{ "name": "from", "type": "Person" },
223+
{ "name": "to", "type": "Person[]" },
224+
{ "name": "contents", "type": "string" }
225+
],
226+
"Group": [
227+
{ "name": "name", "type": "string" },
228+
{ "name": "members", "type": "Person[]" }
229+
]
230+
},
231+
"domain": {
232+
"name": "Ether Mail",
233+
"version": "1",
234+
"chainId": 1,
235+
"verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
236+
},
237+
"primaryType": "Mail",
238+
"message": {
239+
"from": {
240+
"name": "Cow",
241+
"wallets": [
242+
"0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826",
243+
"0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF"
244+
]
245+
},
246+
"to": [{
247+
"name": "Bob",
248+
"wallets": [
249+
"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB",
250+
"0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57",
251+
"0xB0B0b0b0b0b0B000000000000000000000000000"
252+
]
253+
}],
254+
"contents": "Hello, Bob!"
255+
}
256+
}`
257+
258+
typedData, err := ethcoder.TypedDataFromJSON(typedDataJson)
259+
require.NoError(t, err)
260+
261+
domainHash, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map())
262+
require.NoError(t, err)
263+
require.Equal(t, "0xf2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f", ethcoder.HexEncode(domainHash))
264+
265+
digest, typedDataEncoded, err := typedData.Encode()
266+
require.NoError(t, err)
267+
require.Equal(t, "0x2218fda59750be7bb9e5dfb2b49e4ec000dc2542862c5826f1fe980d6d727e95", ethcoder.HexEncode(digest))
268+
require.Equal(t, "0x1901f2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090ff5117e79519388f3d62844df1325ebe783523d9db9762c50fa78a60400a20b5b", ethcoder.HexEncode(typedDataEncoded))
269+
270+
// Sign and validate
271+
wallet, err := ethwallet.NewWalletFromMnemonic("dose weasel clever culture letter volume endorse used harvest ripple circle install")
272+
require.NoError(t, err)
273+
274+
ethSigedTypedData, typedDataEncodedOut, err := wallet.SignTypedData(typedData)
275+
ethSigedTypedDataHex := ethcoder.HexEncode(ethSigedTypedData)
276+
require.NoError(t, err)
277+
require.Equal(t, typedDataEncoded, typedDataEncodedOut)
278+
279+
// NOTE: this signature and above method has been compared against ethers v6 test
280+
require.Equal(t,
281+
"0x296c98bed8f3fd7ea96f55ca8148b4d092cbada953c8d9205b2fff759461ab4e6d6db0b78833b954684900530caeee9aaef8e42dfd8439a3fa107e910b57e2cc1b",
282+
ethSigedTypedDataHex,
283+
)
284+
285+
// recover / validate signature
286+
valid, err := ethwallet.ValidateEthereumSignature(wallet.Address().Hex(), typedDataEncodedOut, ethSigedTypedDataHex)
287+
require.NoError(t, err)
288+
require.True(t, valid)
289+
}

0 commit comments

Comments
 (0)