@ledgerhq/hw-app-eth 6.28.2 → 6.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +5 -2
- package/CHANGELOG.md +13 -0
- package/README.md +58 -16
- package/jest.config.ts +6 -0
- package/lib/Eth.d.ts +37 -0
- package/lib/Eth.d.ts.map +1 -1
- package/lib/Eth.js +66 -72
- package/lib/Eth.js.map +1 -1
- package/lib/modules/EIP712/EIP712.types.d.ts +44 -0
- package/lib/modules/EIP712/EIP712.types.d.ts.map +1 -0
- package/lib/modules/EIP712/EIP712.types.js +3 -0
- package/lib/modules/EIP712/EIP712.types.js.map +1 -0
- package/lib/modules/EIP712/EIP712.utils.d.ts +65 -0
- package/lib/modules/EIP712/EIP712.utils.d.ts.map +1 -0
- package/lib/modules/EIP712/EIP712.utils.js +217 -0
- package/lib/modules/EIP712/EIP712.utils.js.map +1 -0
- package/lib/modules/EIP712/index.d.ts +60 -0
- package/lib/modules/EIP712/index.d.ts.map +1 -0
- package/lib/modules/EIP712/index.js +554 -0
- package/lib/modules/EIP712/index.js.map +1 -0
- package/lib/utils.d.ts +15 -1
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +43 -3
- package/lib/utils.js.map +1 -1
- package/lib-es/Eth.d.ts +37 -0
- package/lib-es/Eth.d.ts.map +1 -1
- package/lib-es/Eth.js +42 -48
- package/lib-es/Eth.js.map +1 -1
- package/lib-es/modules/EIP712/EIP712.types.d.ts +44 -0
- package/lib-es/modules/EIP712/EIP712.types.d.ts.map +1 -0
- package/lib-es/modules/EIP712/EIP712.types.js +2 -0
- package/lib-es/modules/EIP712/EIP712.types.js.map +1 -0
- package/lib-es/modules/EIP712/EIP712.utils.d.ts +65 -0
- package/lib-es/modules/EIP712/EIP712.utils.d.ts.map +1 -0
- package/lib-es/modules/EIP712/EIP712.utils.js +211 -0
- package/lib-es/modules/EIP712/EIP712.utils.js.map +1 -0
- package/lib-es/modules/EIP712/index.d.ts +60 -0
- package/lib-es/modules/EIP712/index.d.ts.map +1 -0
- package/lib-es/modules/EIP712/index.js +549 -0
- package/lib-es/modules/EIP712/index.js.map +1 -0
- package/lib-es/services/ledger/contracts.d.ts +0 -0
- package/lib-es/services/ledger/contracts.d.ts.map +0 -0
- package/lib-es/services/ledger/contracts.js +0 -0
- package/lib-es/services/ledger/contracts.js.map +0 -0
- package/lib-es/services/ledger/erc20.d.ts +0 -0
- package/lib-es/services/ledger/erc20.d.ts.map +0 -0
- package/lib-es/services/ledger/erc20.js +0 -0
- package/lib-es/services/ledger/erc20.js.map +0 -0
- package/lib-es/services/ledger/index.d.ts +0 -0
- package/lib-es/services/ledger/index.d.ts.map +0 -0
- package/lib-es/services/ledger/index.js +0 -0
- package/lib-es/services/ledger/index.js.map +0 -0
- package/lib-es/services/ledger/loadConfig.d.ts +0 -0
- package/lib-es/services/ledger/loadConfig.d.ts.map +0 -0
- package/lib-es/services/ledger/loadConfig.js +0 -0
- package/lib-es/services/ledger/loadConfig.js.map +0 -0
- package/lib-es/services/ledger/nfts.d.ts +0 -0
- package/lib-es/services/ledger/nfts.d.ts.map +0 -0
- package/lib-es/services/ledger/nfts.js +0 -0
- package/lib-es/services/ledger/nfts.js.map +0 -0
- package/lib-es/services/types.d.ts +0 -0
- package/lib-es/services/types.d.ts.map +0 -0
- package/lib-es/services/types.js +0 -0
- package/lib-es/services/types.js.map +0 -0
- package/lib-es/utils.d.ts +15 -1
- package/lib-es/utils.d.ts.map +1 -1
- package/lib-es/utils.js +38 -2
- package/lib-es/utils.js.map +1 -1
- package/package.json +13 -8
- package/src/Eth.ts +59 -56
- package/src/modules/EIP712/EIP712.types.ts +54 -0
- package/src/modules/EIP712/EIP712.utils.ts +251 -0
- package/src/modules/EIP712/index.ts +409 -0
- package/src/utils.ts +42 -2
- package/tests/EIP712.unit.test.ts +760 -0
- package/tests/sample-messages/0.apdus +58 -0
- package/tests/sample-messages/0.json +44 -0
- package/tests/sample-messages/1.apdus +66 -0
- package/tests/sample-messages/1.json +50 -0
- package/tests/sample-messages/10.apdus +30 -0
- package/tests/sample-messages/10.json +23 -0
- package/tests/sample-messages/2.apdus +126 -0
- package/tests/sample-messages/2.json +153 -0
- package/tests/sample-messages/3.apdus +42 -0
- package/tests/sample-messages/3.json +31 -0
- package/tests/sample-messages/4.apdus +84 -0
- package/tests/sample-messages/4.json +110 -0
- package/tests/sample-messages/5.apdus +112 -0
- package/tests/sample-messages/5.json +92 -0
- package/tests/sample-messages/6.apdus +94 -0
- package/tests/sample-messages/6.json +78 -0
- package/tests/sample-messages/7.apdus +70 -0
- package/tests/sample-messages/7.json +55 -0
- package/tests/sample-messages/8.apdus +68 -0
- package/tests/sample-messages/8.json +50 -0
- package/tests/sample-messages/9.apdus +68 -0
- package/tests/sample-messages/9.json +50 -0
- package/LICENSE +0 -202
- package/lib-es/contracts.d.ts +0 -17
- package/lib-es/contracts.d.ts.map +0 -1
- package/lib-es/contracts.js +0 -103
- package/lib-es/contracts.js.map +0 -1
- package/lib-es/erc20.d.ts +0 -22
- package/lib-es/erc20.d.ts.map +0 -1
- package/lib-es/erc20.js +0 -64
- package/lib-es/erc20.js.map +0 -1
package/lib-es/utils.js
CHANGED
|
@@ -1,6 +1,29 @@
|
|
|
1
1
|
import { encode, decode } from "@ethersproject/rlp";
|
|
2
2
|
import { BigNumber } from "bignumber.js";
|
|
3
|
-
export function
|
|
3
|
+
export function splitPath(path) {
|
|
4
|
+
var result = [];
|
|
5
|
+
var components = path.split("/");
|
|
6
|
+
components.forEach(function (element) {
|
|
7
|
+
var number = parseInt(element, 10);
|
|
8
|
+
if (isNaN(number)) {
|
|
9
|
+
return; // FIXME shouldn't it throws instead?
|
|
10
|
+
}
|
|
11
|
+
if (element.length > 1 && element[element.length - 1] === "'") {
|
|
12
|
+
number += 0x80000000;
|
|
13
|
+
}
|
|
14
|
+
result.push(number);
|
|
15
|
+
});
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
18
|
+
export function hexBuffer(str) {
|
|
19
|
+
return Buffer.from(str.startsWith("0x") ? str.slice(2) : str, "hex");
|
|
20
|
+
}
|
|
21
|
+
export function maybeHexBuffer(str) {
|
|
22
|
+
if (!str)
|
|
23
|
+
return null;
|
|
24
|
+
return hexBuffer(str);
|
|
25
|
+
}
|
|
26
|
+
export var decodeTxInfo = function (rawTx) {
|
|
4
27
|
var VALID_TYPES = [1, 2];
|
|
5
28
|
var txType = VALID_TYPES.includes(rawTx[0]) ? rawTx[0] : null;
|
|
6
29
|
var rlpData = txType === null ? rawTx : rawTx.slice(1);
|
|
@@ -68,5 +91,18 @@ export function decodeTxInfo(rawTx) {
|
|
|
68
91
|
chainIdTruncated: chainIdTruncated,
|
|
69
92
|
vrsOffset: vrsOffset
|
|
70
93
|
};
|
|
71
|
-
}
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* @ignore for the README
|
|
97
|
+
*
|
|
98
|
+
* Helper to convert an integer as a hexadecimal string with the right amount of digits
|
|
99
|
+
* to respect the number of bytes given as parameter
|
|
100
|
+
*
|
|
101
|
+
* @param int Integer
|
|
102
|
+
* @param bytes Number of bytes it should be represented as (1 byte = 2 caraters)
|
|
103
|
+
* @returns The given integer as an hexa string padded with the right number of 0
|
|
104
|
+
*/
|
|
105
|
+
export var intAsHexBytes = function (int, bytes) {
|
|
106
|
+
return int.toString(16).padStart(2 * bytes, "0");
|
|
107
|
+
};
|
|
72
108
|
//# sourceMappingURL=utils.js.map
|
package/lib-es/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ledgerhq/hw-app-eth",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.29.0",
|
|
4
4
|
"description": "Ledger Hardware Wallet Ethereum Application API",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Ledger",
|
|
@@ -32,15 +32,20 @@
|
|
|
32
32
|
"@ledgerhq/cryptoassets": "^6.28.2",
|
|
33
33
|
"@ledgerhq/errors": "^6.10.0",
|
|
34
34
|
"@ledgerhq/hw-transport": "^6.27.1",
|
|
35
|
+
"@ledgerhq/hw-transport-mocker": "^6.27.1",
|
|
35
36
|
"@ledgerhq/logs": "^6.10.0",
|
|
36
37
|
"axios": "^0.26.1",
|
|
37
38
|
"bignumber.js": "^9.0.2"
|
|
38
39
|
},
|
|
40
|
+
"gitHead": "dd0dea64b58e5a9125c8a422dcffd29e5ef6abec",
|
|
39
41
|
"scripts": {
|
|
40
|
-
"clean": "
|
|
41
|
-
"build": "
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
"clean": "rimraf lib lib-es",
|
|
43
|
+
"build": "tsc && tsc -m ES6 --outDir lib-es",
|
|
44
|
+
"prewatch": "pnpm build",
|
|
45
|
+
"watch": "tsc --watch",
|
|
46
|
+
"doc": "documentation readme src/** --section=API --pe ts --re ts --re d.ts",
|
|
47
|
+
"lint": "eslint ./src --no-error-on-unmatched-pattern --ext .ts,.tsx",
|
|
48
|
+
"lint:fix": "pnpm lint --fix",
|
|
49
|
+
"test": "jest"
|
|
50
|
+
}
|
|
51
|
+
}
|
package/src/Eth.ts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
// FIXME drop:
|
|
18
18
|
import type Transport from "@ledgerhq/hw-transport";
|
|
19
19
|
import { BigNumber } from "bignumber.js";
|
|
20
|
-
import { decodeTxInfo } from "./utils";
|
|
20
|
+
import { decodeTxInfo, hexBuffer, maybeHexBuffer, splitPath } from "./utils";
|
|
21
21
|
// NB: these are temporary import for the deprecated fallback mechanism
|
|
22
22
|
import { LedgerEthTransactionResolution, LoadConfig } from "./services/types";
|
|
23
23
|
import ledgerService from "./services/ledger";
|
|
@@ -25,6 +25,11 @@ import {
|
|
|
25
25
|
EthAppNftNotSupported,
|
|
26
26
|
EthAppPleaseEnableContractData,
|
|
27
27
|
} from "./errors";
|
|
28
|
+
import {
|
|
29
|
+
signEIP712HashedMessage,
|
|
30
|
+
signEIP712Message,
|
|
31
|
+
EIP712Message,
|
|
32
|
+
} from "./modules/EIP712";
|
|
28
33
|
|
|
29
34
|
export type StarkQuantizationType =
|
|
30
35
|
| "eth"
|
|
@@ -40,33 +45,6 @@ const starkQuantizationTypeMap = {
|
|
|
40
45
|
erc721mintable: 5,
|
|
41
46
|
};
|
|
42
47
|
|
|
43
|
-
function splitPath(path: string): number[] {
|
|
44
|
-
const result: number[] = [];
|
|
45
|
-
const components = path.split("/");
|
|
46
|
-
components.forEach((element) => {
|
|
47
|
-
let number = parseInt(element, 10);
|
|
48
|
-
if (isNaN(number)) {
|
|
49
|
-
return; // FIXME shouldn't it throws instead?
|
|
50
|
-
}
|
|
51
|
-
if (element.length > 1 && element[element.length - 1] === "'") {
|
|
52
|
-
number += 0x80000000;
|
|
53
|
-
}
|
|
54
|
-
result.push(number);
|
|
55
|
-
});
|
|
56
|
-
return result;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function hexBuffer(str: string): Buffer {
|
|
60
|
-
return Buffer.from(str.startsWith("0x") ? str.slice(2) : str, "hex");
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function maybeHexBuffer(
|
|
64
|
-
str: string | null | undefined
|
|
65
|
-
): Buffer | null | undefined {
|
|
66
|
-
if (!str) return null;
|
|
67
|
-
return hexBuffer(str);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
48
|
const remapTransactionRelatedErrors = (e) => {
|
|
71
49
|
if (e && e.statusCode === 0x6a80) {
|
|
72
50
|
return new EthAppPleaseEnableContractData(
|
|
@@ -76,6 +54,7 @@ const remapTransactionRelatedErrors = (e) => {
|
|
|
76
54
|
|
|
77
55
|
return e;
|
|
78
56
|
};
|
|
57
|
+
|
|
79
58
|
/**
|
|
80
59
|
* Ethereum API
|
|
81
60
|
*
|
|
@@ -107,6 +86,7 @@ export default class Eth {
|
|
|
107
86
|
"signTransaction",
|
|
108
87
|
"signPersonalMessage",
|
|
109
88
|
"getAppConfiguration",
|
|
89
|
+
"signEIP712Message",
|
|
110
90
|
"signEIP712HashedMessage",
|
|
111
91
|
"starkGetPublicKey",
|
|
112
92
|
"starkSignOrder",
|
|
@@ -251,9 +231,8 @@ export default class Eth {
|
|
|
251
231
|
}
|
|
252
232
|
|
|
253
233
|
const rawTx = Buffer.from(rawTxHex, "hex");
|
|
254
|
-
const { vrsOffset, txType, chainId, chainIdTruncated } =
|
|
255
|
-
rawTx
|
|
256
|
-
);
|
|
234
|
+
const { vrsOffset, txType, chainId, chainIdTruncated } =
|
|
235
|
+
decodeTxInfo(rawTx);
|
|
257
236
|
|
|
258
237
|
const paths = splitPath(path);
|
|
259
238
|
let response;
|
|
@@ -432,31 +411,55 @@ export default class Eth {
|
|
|
432
411
|
s: string;
|
|
433
412
|
r: string;
|
|
434
413
|
}> {
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
414
|
+
return signEIP712HashedMessage(
|
|
415
|
+
this.transport,
|
|
416
|
+
path,
|
|
417
|
+
domainSeparatorHex,
|
|
418
|
+
hashStructMessageHex
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Sign an EIP-721 formatted message following the specification here:
|
|
424
|
+
* https://github.com/LedgerHQ/app-ethereum/blob/develop/doc/ethapp.asc#sign-eth-eip-712
|
|
425
|
+
@example
|
|
426
|
+
eth.signEIP721Message("44'/60'/0'/0/0", {
|
|
427
|
+
domain: {
|
|
428
|
+
chainId: 69,
|
|
429
|
+
name: "Da Domain",
|
|
430
|
+
verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
|
|
431
|
+
version: "1"
|
|
432
|
+
},
|
|
433
|
+
types: {
|
|
434
|
+
"EIP712Domain": [
|
|
435
|
+
{ name: "name", type: "string" },
|
|
436
|
+
{ name: "version", type: "string" },
|
|
437
|
+
{ name: "chainId", type: "uint256" },
|
|
438
|
+
{ name: "verifyingContract", type: "address" }
|
|
439
|
+
],
|
|
440
|
+
"Test": [
|
|
441
|
+
{ name: "contents", type: "string" }
|
|
442
|
+
]
|
|
443
|
+
},
|
|
444
|
+
primaryType: "Test",
|
|
445
|
+
message: {contents: "Hello, Bob!"},
|
|
446
|
+
})
|
|
447
|
+
*
|
|
448
|
+
* @param {String} path derivationPath
|
|
449
|
+
* @param {Object} jsonMessage message to sign
|
|
450
|
+
* @param {Boolean} fullImplem use the legacy implementation
|
|
451
|
+
* @returns {Promise}
|
|
452
|
+
*/
|
|
453
|
+
async signEIP712Message(
|
|
454
|
+
path: string,
|
|
455
|
+
jsonMessage: EIP712Message,
|
|
456
|
+
fullImplem = false
|
|
457
|
+
): Promise<{
|
|
458
|
+
v: number;
|
|
459
|
+
s: string;
|
|
460
|
+
r: string;
|
|
461
|
+
}> {
|
|
462
|
+
return signEIP712Message(this.transport, path, jsonMessage, fullImplem);
|
|
460
463
|
}
|
|
461
464
|
|
|
462
465
|
/**
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export type EIP712Message = {
|
|
2
|
+
domain: EIP712MessageDomain;
|
|
3
|
+
types: EIP712MessageTypes;
|
|
4
|
+
primaryType: string;
|
|
5
|
+
message: Record<string, unknown>;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export type EIP712MessageDomain = Partial<{
|
|
9
|
+
name: string;
|
|
10
|
+
chainId: number;
|
|
11
|
+
version: string;
|
|
12
|
+
verifyingContract: string;
|
|
13
|
+
salt: string;
|
|
14
|
+
}>;
|
|
15
|
+
|
|
16
|
+
export type EIP712MessageTypesEntry = {
|
|
17
|
+
name: string;
|
|
18
|
+
type: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type EIP712MessageTypes = {
|
|
22
|
+
EIP712Domain: EIP712MessageTypesEntry[];
|
|
23
|
+
[key: string]: EIP712MessageTypesEntry[];
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type StructDefData = Required<
|
|
27
|
+
| {
|
|
28
|
+
structType: "name";
|
|
29
|
+
value: string;
|
|
30
|
+
}
|
|
31
|
+
| {
|
|
32
|
+
structType: "field";
|
|
33
|
+
value: Buffer;
|
|
34
|
+
}
|
|
35
|
+
>;
|
|
36
|
+
|
|
37
|
+
export type StructImplemData = Required<
|
|
38
|
+
| {
|
|
39
|
+
structType: "root";
|
|
40
|
+
value: string;
|
|
41
|
+
}
|
|
42
|
+
| {
|
|
43
|
+
structType: "array";
|
|
44
|
+
value: number;
|
|
45
|
+
}
|
|
46
|
+
| {
|
|
47
|
+
structType: "field";
|
|
48
|
+
value: Required<{
|
|
49
|
+
data: unknown;
|
|
50
|
+
type: string;
|
|
51
|
+
sizeInBits: number | undefined;
|
|
52
|
+
}>;
|
|
53
|
+
}
|
|
54
|
+
>;
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { hexBuffer, intAsHexBytes } from "../../utils";
|
|
2
|
+
import { EIP712MessageTypesEntry } from "./EIP712.types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @ignore for the README
|
|
6
|
+
*
|
|
7
|
+
* A Map of helpers to get the wanted binary value for
|
|
8
|
+
* each type of array possible in a type definition
|
|
9
|
+
*/
|
|
10
|
+
enum EIP712_ARRAY_TYPE_VALUE {
|
|
11
|
+
DYNAMIC = 0,
|
|
12
|
+
FIXED = 1,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @ignore for the README
|
|
17
|
+
*
|
|
18
|
+
* A Map of helpers to get the id and size to return for each
|
|
19
|
+
* type that can be used in EIP712
|
|
20
|
+
*/
|
|
21
|
+
export const EIP712_TYPE_PROPERTIES: Record<
|
|
22
|
+
string,
|
|
23
|
+
{
|
|
24
|
+
key: (size?: number) => number;
|
|
25
|
+
sizeInBits: (size?: number) => number | null;
|
|
26
|
+
}
|
|
27
|
+
> = {
|
|
28
|
+
CUSTOM: {
|
|
29
|
+
key: () => 0,
|
|
30
|
+
sizeInBits: () => null,
|
|
31
|
+
},
|
|
32
|
+
INT: {
|
|
33
|
+
key: () => 1,
|
|
34
|
+
sizeInBits: (size) => Number(size) / 8,
|
|
35
|
+
},
|
|
36
|
+
UINT: {
|
|
37
|
+
key: () => 2,
|
|
38
|
+
sizeInBits: (size) => Number(size) / 8,
|
|
39
|
+
},
|
|
40
|
+
ADDRESS: {
|
|
41
|
+
key: () => 3,
|
|
42
|
+
sizeInBits: () => null,
|
|
43
|
+
},
|
|
44
|
+
BOOL: {
|
|
45
|
+
key: () => 4,
|
|
46
|
+
sizeInBits: () => null,
|
|
47
|
+
},
|
|
48
|
+
STRING: {
|
|
49
|
+
key: () => 5,
|
|
50
|
+
sizeInBits: () => null,
|
|
51
|
+
},
|
|
52
|
+
BYTES: {
|
|
53
|
+
key: (size) => (typeof size !== "undefined" ? 6 : 7),
|
|
54
|
+
sizeInBits: (size) => (typeof size !== "undefined" ? Number(size) : null),
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @ignore for the README
|
|
60
|
+
*
|
|
61
|
+
* A Map of encoders to transform a value to formatted buffer
|
|
62
|
+
*/
|
|
63
|
+
export const EIP712_TYPE_ENCODERS = {
|
|
64
|
+
INT(value: number | string | null): Buffer {
|
|
65
|
+
const failSafeValue = value ?? 0;
|
|
66
|
+
|
|
67
|
+
if (typeof failSafeValue === "string" && failSafeValue?.startsWith("0x")) {
|
|
68
|
+
return hexBuffer(failSafeValue);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const valueAsInt =
|
|
72
|
+
typeof failSafeValue === "string"
|
|
73
|
+
? parseInt(failSafeValue, 10)
|
|
74
|
+
: failSafeValue;
|
|
75
|
+
|
|
76
|
+
const valueAsHexString = valueAsInt.toString(16);
|
|
77
|
+
const paddedHexString =
|
|
78
|
+
valueAsHexString.length % 2 ? "0" + valueAsHexString : valueAsHexString;
|
|
79
|
+
|
|
80
|
+
return Buffer.from(paddedHexString, "hex");
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
UINT(value: number | string): Buffer {
|
|
84
|
+
return this.INT(value);
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
BOOL(value: number | string | boolean | null): Buffer {
|
|
88
|
+
return this.INT(typeof value === "boolean" ? Number(value) : value);
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
ADDRESS(value: string | null): Buffer {
|
|
92
|
+
// Only sending the first 10 bytes (why ?)
|
|
93
|
+
return hexBuffer(value ?? "").slice(0, 20);
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
STRING(value: string | null): Buffer {
|
|
97
|
+
return Buffer.from(value ?? "", "utf-8");
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
BYTES(value: string | null, sizeInBits?: number): Buffer {
|
|
101
|
+
const failSafeValue = value ?? "";
|
|
102
|
+
// Why slice again ?
|
|
103
|
+
return hexBuffer(failSafeValue).slice(
|
|
104
|
+
0,
|
|
105
|
+
sizeInBits ?? (failSafeValue?.length - 2) / 2
|
|
106
|
+
);
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* @ignore for the README
|
|
112
|
+
*
|
|
113
|
+
* Helper parsing an EIP712 Type name to return its type and size(s)
|
|
114
|
+
* if it's an array or nested arrays
|
|
115
|
+
*
|
|
116
|
+
* @see EIP712MessageTypes
|
|
117
|
+
*
|
|
118
|
+
* @example "uint8[2][][4]" => [{name: "uint", bits: 8}, [2, null, 4]]
|
|
119
|
+
* @example "bool" => [{name: "bool", bits: null}, []]
|
|
120
|
+
*
|
|
121
|
+
* @param {String} typeName
|
|
122
|
+
* @returns {[{ name: string; bits: Number | null }, Array<Number | null | undefined>]}
|
|
123
|
+
*/
|
|
124
|
+
export const destructTypeFromString = (
|
|
125
|
+
typeName?: string
|
|
126
|
+
): [
|
|
127
|
+
{ name: string; bits: number | undefined } | null,
|
|
128
|
+
Array<number | null>
|
|
129
|
+
] => {
|
|
130
|
+
// Will split "any[][1][10]" in "any", "[][1][10]"
|
|
131
|
+
const splitNameAndArraysRegex = new RegExp(/^([^[\]]*)(\[.*\])*/g);
|
|
132
|
+
// Will match all numbers (or null) inside each array. [0][10][] => [0,10,null]
|
|
133
|
+
const splitArraysRegex = new RegExp(/\[(\d*)\]/g);
|
|
134
|
+
// Will separate the the name from the potential bits allocation. uint8 => [uint,8]
|
|
135
|
+
const splitNameAndNumberRegex = new RegExp(/(\D*)(\d*)/);
|
|
136
|
+
|
|
137
|
+
const [, type, maybeArrays] =
|
|
138
|
+
splitNameAndArraysRegex.exec(typeName || "") || [];
|
|
139
|
+
const [, name, bits] = splitNameAndNumberRegex.exec(type || "") || [];
|
|
140
|
+
const typeDescription = name
|
|
141
|
+
? { name, bits: bits ? Number(bits) : undefined }
|
|
142
|
+
: null;
|
|
143
|
+
|
|
144
|
+
const arrays = maybeArrays ? [...maybeArrays.matchAll(splitArraysRegex)] : [];
|
|
145
|
+
// Parse each size to either a Number or null
|
|
146
|
+
const arraySizes = arrays.map(([, size]) => (size ? Number(size) : null));
|
|
147
|
+
|
|
148
|
+
return [typeDescription, arraySizes];
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* @ignore for the README
|
|
153
|
+
*
|
|
154
|
+
* Helper to construct the hexadecimal ByteString for the description
|
|
155
|
+
* of a field in an EIP712 Message
|
|
156
|
+
*
|
|
157
|
+
* @param isArray
|
|
158
|
+
* @param typeSize
|
|
159
|
+
* @param typeValue
|
|
160
|
+
* @returns {String} HexByteString
|
|
161
|
+
*/
|
|
162
|
+
export const constructTypeDescByteString = (
|
|
163
|
+
isArray: boolean,
|
|
164
|
+
typeSize: number | null | undefined,
|
|
165
|
+
typeValue: number
|
|
166
|
+
): string => {
|
|
167
|
+
if (typeValue >= 16) {
|
|
168
|
+
throw new Error(
|
|
169
|
+
"Eth utils - constructTypeDescByteString - Cannot accept a typeValue >= 16 because the typeValue can only be 4 bits in binary" +
|
|
170
|
+
{ isArray, typeSize, typeValue }
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
// 1 is array, 0 is not array
|
|
174
|
+
const isArrayBit = isArray ? "1" : "0";
|
|
175
|
+
// 1 has type size, 0 has no type size
|
|
176
|
+
const hasTypeSize = typeof typeSize === "number" ? "1" : "0";
|
|
177
|
+
// 2 unused bits
|
|
178
|
+
const unusedBits = "00";
|
|
179
|
+
// type key as 4 bits
|
|
180
|
+
const typeValueBits = typeValue.toString(2).padStart(4, "0");
|
|
181
|
+
|
|
182
|
+
return intAsHexBytes(
|
|
183
|
+
parseInt(isArrayBit + hasTypeSize + unusedBits + typeValueBits, 2),
|
|
184
|
+
1
|
|
185
|
+
);
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* @ignore for the README
|
|
190
|
+
*
|
|
191
|
+
* Helper to create the buffer to describe an EIP712 types' entry structure
|
|
192
|
+
*
|
|
193
|
+
* @param {EIP712MessageTypesEntry} entry
|
|
194
|
+
* @returns {Buffer}
|
|
195
|
+
*/
|
|
196
|
+
export const makeTypeEntryStructBuffer = ({
|
|
197
|
+
name,
|
|
198
|
+
type,
|
|
199
|
+
}: EIP712MessageTypesEntry): Buffer => {
|
|
200
|
+
const [typeDescription, arrSizes] = destructTypeFromString(type as string);
|
|
201
|
+
const isTypeAnArray = Boolean(arrSizes.length);
|
|
202
|
+
const typeProperties =
|
|
203
|
+
EIP712_TYPE_PROPERTIES[typeDescription?.name?.toUpperCase() || ""] ||
|
|
204
|
+
EIP712_TYPE_PROPERTIES.CUSTOM;
|
|
205
|
+
|
|
206
|
+
const typeKey = typeProperties.key(typeDescription?.bits);
|
|
207
|
+
const typeSizeInBits = typeProperties.sizeInBits(typeDescription?.bits);
|
|
208
|
+
|
|
209
|
+
const typeDescData = constructTypeDescByteString(
|
|
210
|
+
isTypeAnArray,
|
|
211
|
+
typeSizeInBits,
|
|
212
|
+
typeKey
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
const bufferArray: Buffer[] = [Buffer.from(typeDescData, "hex")];
|
|
216
|
+
|
|
217
|
+
if (typeProperties === EIP712_TYPE_PROPERTIES.CUSTOM) {
|
|
218
|
+
bufferArray.push(
|
|
219
|
+
Buffer.from(intAsHexBytes(typeDescription?.name?.length ?? 0, 1), "hex")
|
|
220
|
+
);
|
|
221
|
+
bufferArray.push(Buffer.from(typeDescription?.name ?? "", "utf-8"));
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (typeof typeSizeInBits === "number") {
|
|
225
|
+
bufferArray.push(Buffer.from(intAsHexBytes(typeSizeInBits, 1), "hex"));
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (isTypeAnArray) {
|
|
229
|
+
bufferArray.push(Buffer.from(intAsHexBytes(arrSizes.length, 1), "hex"));
|
|
230
|
+
|
|
231
|
+
arrSizes.forEach((size) => {
|
|
232
|
+
if (typeof size === "number") {
|
|
233
|
+
bufferArray.push(
|
|
234
|
+
Buffer.from(intAsHexBytes(EIP712_ARRAY_TYPE_VALUE.FIXED, 1), "hex"),
|
|
235
|
+
Buffer.from(intAsHexBytes(size, 1), "hex")
|
|
236
|
+
);
|
|
237
|
+
} else {
|
|
238
|
+
bufferArray.push(
|
|
239
|
+
Buffer.from(intAsHexBytes(EIP712_ARRAY_TYPE_VALUE.DYNAMIC, 1), "hex")
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
bufferArray.push(
|
|
246
|
+
Buffer.from(intAsHexBytes(name.length, 1), "hex"),
|
|
247
|
+
Buffer.from(name, "utf-8")
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
return Buffer.concat(bufferArray);
|
|
251
|
+
};
|