starknet 4.3.0 → 4.4.1

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.
Files changed (99) hide show
  1. package/CHANGELOG.md +40 -1
  2. package/__mocks__/typedDataSessionExample.json +42 -0
  3. package/__tests__/defaultProvider.test.ts +11 -24
  4. package/__tests__/rpcProvider.test.ts +32 -5
  5. package/__tests__/sequencerProvider.test.ts +9 -1
  6. package/__tests__/utils/__snapshots__/ellipticalCurve.test.ts.snap +2 -0
  7. package/__tests__/utils/ellipticalCurve.test.ts +5 -0
  8. package/__tests__/utils/merkle.test.ts +146 -0
  9. package/__tests__/utils/typedData.test.ts +107 -9
  10. package/dist/index.d.ts +1 -0
  11. package/dist/index.js +2 -1
  12. package/dist/provider/default.d.ts +2 -2
  13. package/dist/provider/default.js +3 -3
  14. package/dist/provider/interface.d.ts +6 -3
  15. package/dist/provider/rpc.d.ts +9 -4
  16. package/dist/provider/rpc.js +70 -28
  17. package/dist/provider/sequencer.d.ts +2 -2
  18. package/dist/provider/sequencer.js +4 -4
  19. package/dist/provider/utils.d.ts +12 -0
  20. package/dist/provider/utils.js +17 -1
  21. package/dist/signer/default.d.ts +1 -1
  22. package/dist/signer/default.js +1 -0
  23. package/dist/types/api/openrpc.d.ts +151 -0
  24. package/dist/types/api/openrpc.js +9 -0
  25. package/dist/types/api/rpc.d.ts +22 -43
  26. package/dist/types/provider.d.ts +5 -5
  27. package/dist/utils/ellipticCurve.d.ts +13 -0
  28. package/dist/utils/ellipticCurve.js +20 -16
  29. package/dist/utils/hash.js +8 -6
  30. package/dist/utils/merkle.d.ts +10 -0
  31. package/dist/utils/merkle.js +90 -0
  32. package/dist/utils/number.d.ts +1 -0
  33. package/dist/utils/number.js +6 -2
  34. package/dist/utils/responseParser/rpc.d.ts +13 -3
  35. package/dist/utils/responseParser/rpc.js +2 -10
  36. package/dist/utils/responseParser/sequencer.d.ts +4 -1
  37. package/dist/utils/responseParser/sequencer.js +1 -7
  38. package/dist/utils/typedData/index.d.ts +23 -8
  39. package/dist/utils/typedData/index.js +70 -31
  40. package/dist/utils/typedData/types.d.ts +8 -3
  41. package/index.d.ts +1 -0
  42. package/index.js +2 -1
  43. package/package.json +1 -1
  44. package/provider/default.d.ts +2 -2
  45. package/provider/default.js +3 -3
  46. package/provider/interface.d.ts +6 -3
  47. package/provider/rpc.d.ts +9 -4
  48. package/provider/rpc.js +70 -28
  49. package/provider/sequencer.d.ts +2 -2
  50. package/provider/sequencer.js +4 -4
  51. package/provider/utils.d.ts +12 -0
  52. package/provider/utils.js +17 -1
  53. package/signer/default.d.ts +1 -1
  54. package/signer/default.js +1 -0
  55. package/src/index.ts +1 -0
  56. package/src/provider/default.ts +2 -3
  57. package/src/provider/interface.ts +5 -3
  58. package/src/provider/rpc.ts +59 -32
  59. package/src/provider/sequencer.ts +3 -6
  60. package/src/provider/utils.ts +22 -1
  61. package/src/signer/default.ts +2 -2
  62. package/src/types/api/openrpc.ts +168 -0
  63. package/src/types/api/rpc.ts +22 -45
  64. package/src/types/provider.ts +5 -5
  65. package/src/utils/ellipticCurve.ts +20 -16
  66. package/src/utils/hash.ts +8 -6
  67. package/src/utils/merkle.ts +70 -0
  68. package/src/utils/number.ts +5 -1
  69. package/src/utils/responseParser/rpc.ts +16 -13
  70. package/src/utils/responseParser/sequencer.ts +5 -8
  71. package/src/utils/typedData/index.ts +88 -34
  72. package/src/utils/typedData/types.ts +12 -4
  73. package/types/api/openrpc.d.ts +151 -0
  74. package/types/api/openrpc.js +9 -0
  75. package/types/api/rpc.d.ts +22 -43
  76. package/types/provider.d.ts +5 -5
  77. package/utils/ellipticCurve.d.ts +13 -0
  78. package/utils/ellipticCurve.js +20 -16
  79. package/utils/hash.js +8 -6
  80. package/utils/merkle.d.ts +10 -0
  81. package/utils/merkle.js +90 -0
  82. package/utils/number.d.ts +1 -0
  83. package/utils/number.js +6 -2
  84. package/utils/responseParser/rpc.d.ts +13 -3
  85. package/utils/responseParser/rpc.js +2 -10
  86. package/utils/responseParser/sequencer.d.ts +4 -1
  87. package/utils/responseParser/sequencer.js +1 -7
  88. package/utils/typedData/index.d.ts +23 -8
  89. package/utils/typedData/index.js +70 -31
  90. package/utils/typedData/types.d.ts +8 -3
  91. package/www/docs/API/account.md +20 -18
  92. package/www/docs/API/contract.md +10 -10
  93. package/www/docs/API/contractFactory.md +14 -11
  94. package/www/docs/API/provider.md +29 -63
  95. package/www/docs/API/signer.md +8 -10
  96. package/www/docs/API/utils.md +157 -74
  97. package/www/guides/account.md +12 -12
  98. package/www/guides/erc20.md +19 -4
  99. package/www/guides/intro.md +3 -1
@@ -21,11 +21,13 @@ export const ec = new EC(
21
21
  })
22
22
  );
23
23
 
24
- /*
25
- The function _truncateToN in lib/elliptic/ec/index.js does a shift-right of 4 bits
26
- in some cases. This function does the opposite operation so that
27
- _truncateToN(fixMessage(msg)) == msg.
28
- */
24
+ /**
25
+ * The function _truncateToN in lib/elliptic/ec/index.js does a shift-right of 4 bits
26
+ * in some cases. This function does the opposite operation so that
27
+ * _truncateToN(fixMessage(msg)) == msg.
28
+ *
29
+ * @param msg
30
+ */
29
31
  function fixMessage(msg: string) {
30
32
  const pureHex = msg.replace(/^0x0*/, '');
31
33
 
@@ -64,11 +66,12 @@ export function getKeyPairFromPublicKey(publicKey: BigNumberish): KeyPair {
64
66
  return ec.keyFromPublic(removeHexPrefix(toHex(publicKeyBn)), 'hex');
65
67
  }
66
68
 
67
- /*
68
- Signs a message using the provided key.
69
- key should be an KeyPair with a valid private key.
70
- Returns an Signature.
71
- */
69
+ /**
70
+ * Signs a message using the provided key.
71
+ *
72
+ * @param keyPair should be an KeyPair with a valid private key.
73
+ * @returns an Signature.
74
+ */
72
75
  export function sign(keyPair: KeyPair, msgHash: string): Signature {
73
76
  const msgHashBN = toBN(addHexPrefix(msgHash));
74
77
  // Verify message hash has valid length.
@@ -89,12 +92,13 @@ function chunkArray(arr: any[], n: number): any[][] {
89
92
  .map((_, i) => arr.slice(i * n, i * n + n));
90
93
  }
91
94
 
92
- /*
93
- Verifies a message using the provided key.
94
- key should be an KeyPair with a valid public key.
95
- msgSignature should be an Signature.
96
- Returns a boolean true if the verification succeeds.
97
- */
95
+ /**
96
+ * Verifies a message using the provided key.
97
+ *
98
+ * @param keyPair should be an KeyPair with a valid public key.
99
+ * @param sig should be an Signature.
100
+ * @returns true if the verification succeeds.
101
+ */
98
102
  export function verify(keyPair: KeyPair | KeyPair[], msgHash: string, sig: Signature): boolean {
99
103
  const keyPairArray = Array.isArray(keyPair) ? keyPair : [keyPair];
100
104
  const msgHashBN = toBN(addHexPrefix(msgHash));
package/src/utils/hash.ts CHANGED
@@ -62,13 +62,15 @@ export function pedersen(input: [BigNumberish, BigNumberish]) {
62
62
  for (let i = 0; i < input.length; i += 1) {
63
63
  let x = toBN(input[i]);
64
64
  assert(x.gte(ZERO) && x.lt(toBN(addHexPrefix(FIELD_PRIME))), `Invalid input: ${input[i]}`);
65
- for (let j = 0; j < 252; j += 1) {
66
- const pt = constantPoints[2 + i * 252 + j];
67
- assert(!point.getX().eq(pt.getX()));
68
- if (x.and(ONE).toNumber() !== 0) {
69
- point = point.add(pt);
65
+ if (!x.isZero()) {
66
+ for (let j = 0; j < 252; j += 1) {
67
+ const pt = constantPoints[2 + i * 252 + j];
68
+ assert(!point.getX().eq(pt.getX()));
69
+ if (x.and(ONE).toNumber() !== 0) {
70
+ point = point.add(pt);
71
+ }
72
+ x = x.shrn(1);
70
73
  }
71
- x = x.shrn(1);
72
74
  }
73
75
  }
74
76
  return addHexPrefix(point.getX().toString(16));
@@ -0,0 +1,70 @@
1
+ import { pedersen } from './hash';
2
+
3
+ export class MerkleTree {
4
+ public leaves: string[];
5
+
6
+ public branches: string[][] = [];
7
+
8
+ public root: string;
9
+
10
+ constructor(leafHashes: string[]) {
11
+ this.leaves = leafHashes;
12
+ this.root = this.build(leafHashes);
13
+ }
14
+
15
+ private build(leaves: string[]): string {
16
+ if (leaves.length === 1) {
17
+ return leaves[0];
18
+ }
19
+ if (leaves.length !== this.leaves.length) {
20
+ this.branches.push(leaves);
21
+ }
22
+ const newLeaves = [];
23
+ for (let i = 0; i < leaves.length; i += 2) {
24
+ if (i + 1 === leaves.length) {
25
+ newLeaves.push(leaves[i]);
26
+ } else {
27
+ newLeaves.push(MerkleTree.hash(leaves[i], leaves[i + 1]));
28
+ }
29
+ }
30
+ return this.build(newLeaves);
31
+ }
32
+
33
+ static hash(a: string, b: string) {
34
+ const [aSorted, bSorted] = [a, b].sort();
35
+ return pedersen([aSorted, bSorted]);
36
+ }
37
+
38
+ public getProof(leaf: string, branch = this.leaves, hashPath: string[] = []): string[] {
39
+ const index = branch.indexOf(leaf);
40
+ if (index === -1) {
41
+ throw new Error('leaf not found');
42
+ }
43
+ if (branch.length === 1) {
44
+ return hashPath;
45
+ }
46
+ const isLeft = index % 2 === 0;
47
+ const neededBranch = (isLeft ? branch[index + 1] : branch[index - 1]) ?? '0x0';
48
+ const newHashPath = [...hashPath, neededBranch];
49
+ const currentBranchLevelIndex =
50
+ this.leaves.length === branch.length
51
+ ? -1
52
+ : this.branches.findIndex((b) => b.length === branch.length);
53
+ const nextBranch = this.branches[currentBranchLevelIndex + 1] ?? [this.root];
54
+ return this.getProof(
55
+ neededBranch === '0x0'
56
+ ? leaf
57
+ : MerkleTree.hash(isLeft ? leaf : neededBranch, isLeft ? neededBranch : leaf),
58
+ nextBranch,
59
+ newHashPath
60
+ );
61
+ }
62
+ }
63
+
64
+ export function proofMerklePath(root: string, leaf: string, path: string[]): boolean {
65
+ if (path.length === 0) {
66
+ return root === leaf;
67
+ }
68
+ const [next, ...rest] = path;
69
+ return proofMerklePath(root, MerkleTree.hash(leaf, next), rest);
70
+ }
@@ -6,7 +6,7 @@ import { addHexPrefix, removeHexPrefix } from './encode';
6
6
  export type BigNumberish = string | number | BN;
7
7
 
8
8
  export function isHex(hex: string): boolean {
9
- return hex.startsWith('0x');
9
+ return /^0x[0-9a-f]*$/i.test(hex);
10
10
  }
11
11
 
12
12
  export function toBN(number: BigNumberish, base?: number | 'hex') {
@@ -57,3 +57,7 @@ export function assertInRange(
57
57
  export function bigNumberishArrayToDecimalStringArray(rawCalldata: BigNumberish[]): string[] {
58
58
  return rawCalldata.map((x) => toBN(x).toString(10));
59
59
  }
60
+
61
+ export function bigNumberishArrayToHexadecimalStringArray(rawCalldata: BigNumberish[]): string[] {
62
+ return rawCalldata.map((x) => toHex(toBN(x)));
63
+ }
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Map RPC Response to common interface response
3
+ * Intersection (sequencer response ∩ (∪ rpc responses))
4
+ */
1
5
  import {
2
6
  CallContractResponse,
3
7
  DeclareContractResponse,
@@ -12,33 +16,36 @@ import { RPC } from '../../types/api';
12
16
  import { toBN } from '../number';
13
17
  import { ResponseParser } from '.';
14
18
 
19
+ type RpcGetBlockResponse = RPC.GetBlockWithTxHashesResponse & {
20
+ [key: string]: any;
21
+ };
22
+
23
+ type GetTransactionByHashResponse = RPC.GetTransactionByHashResponse & {
24
+ [key: string]: any;
25
+ };
26
+
15
27
  export class RPCResponseParser extends ResponseParser {
16
- public parseGetBlockResponse(res: RPC.GetBlockResponse): GetBlockResponse {
28
+ public parseGetBlockResponse(res: RpcGetBlockResponse): GetBlockResponse {
17
29
  return {
18
- accepted_time: res.accepted_time,
30
+ timestamp: res.timestamp,
19
31
  block_hash: res.block_hash,
20
32
  block_number: res.block_number,
21
- gas_price: res.gas_price,
22
33
  new_root: res.new_root,
23
- old_root: res.old_root,
24
34
  parent_hash: res.parent_hash,
25
- sequencer: res.sequencer,
26
35
  status: res.status,
27
36
  transactions: res.transactions,
28
37
  };
29
38
  }
30
39
 
31
- public parseGetTransactionResponse(res: RPC.GetTransactionResponse): GetTransactionResponse {
40
+ public parseGetTransactionResponse(res: GetTransactionByHashResponse): GetTransactionResponse {
32
41
  return {
33
42
  calldata: res.calldata || [],
34
43
  contract_address: res.contract_address,
35
- contract_class: res.contract_class,
36
44
  entry_point_selector: res.entry_point_selector,
37
45
  max_fee: res.max_fee,
38
46
  nonce: res.nonce,
39
- sender_address: res.sender_address,
40
47
  signature: res.signature || [],
41
- transaction_hash: res.txn_hash,
48
+ transaction_hash: res.transaction_hash,
42
49
  version: res.version,
43
50
  };
44
51
  }
@@ -57,10 +64,6 @@ export class RPCResponseParser extends ResponseParser {
57
64
  };
58
65
  }
59
66
 
60
- public parseGetCodeResponse(res: RPC.GetCodeResponse): RPC.GetCodeResponse {
61
- return res;
62
- }
63
-
64
67
  public parseFeeEstimateResponse(res: RPC.EstimateFeeResponse): EstimateFeeResponse {
65
68
  return {
66
69
  overall_fee: toBN(res.overall_fee),
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Map Sequencer Response to common interface response
3
+ * Intersection (sequencer response ∩ (∪ rpc responses))
4
+ */
1
5
  import {
2
6
  CallContractResponse,
3
7
  DeclareContractResponse,
@@ -15,14 +19,11 @@ import { ResponseParser } from '.';
15
19
  export class SequencerAPIResponseParser extends ResponseParser {
16
20
  public parseGetBlockResponse(res: Sequencer.GetBlockResponse): GetBlockResponse {
17
21
  return {
18
- accepted_time: res.timestamp,
22
+ timestamp: res.timestamp,
19
23
  block_hash: res.block_hash,
20
24
  block_number: res.block_number,
21
- gas_price: res.gas_price,
22
25
  new_root: res.state_root,
23
- old_root: undefined,
24
26
  parent_hash: res.parent_block_hash,
25
- sequencer: res.sequencer_address,
26
27
  status: res.status,
27
28
  transactions: Object.values(res.transactions)
28
29
  .map((value) => 'transaction_hash' in value && value.transaction_hash)
@@ -70,10 +71,6 @@ export class SequencerAPIResponseParser extends ResponseParser {
70
71
  };
71
72
  }
72
73
 
73
- public parseGetCodeResponse(res: Sequencer.GetCodeResponse): Sequencer.GetCodeResponse {
74
- return res;
75
- }
76
-
77
74
  public parseFeeEstimateResponse(res: Sequencer.EstimateFeeResponse): EstimateFeeResponse {
78
75
  if ('overall_fee' in res) {
79
76
  let gasInfo = {};
@@ -1,7 +1,8 @@
1
1
  import { computeHashOnElements, getSelectorFromName } from '../hash';
2
- import { BigNumberish, toBN, toHex } from '../number';
2
+ import { MerkleTree } from '../merkle';
3
+ import { BigNumberish, isHex, toBN, toHex } from '../number';
3
4
  import { encodeShortString } from '../shortString';
4
- import { TypedData } from './types';
5
+ import { StarkNetMerkleType, StarkNetType, TypedData } from './types';
5
6
  import { validateTypedData } from './utils';
6
7
 
7
8
  export * from './types';
@@ -17,6 +18,19 @@ function getHex(value: BigNumberish): string {
17
18
  }
18
19
  }
19
20
 
21
+ export function prepareSelector(selector: string): string {
22
+ return isHex(selector) ? selector : getSelectorFromName(selector);
23
+ }
24
+
25
+ export function isMerkleTreeType(type: StarkNetType): type is StarkNetMerkleType {
26
+ return type.type === 'merkletree';
27
+ }
28
+
29
+ interface Context {
30
+ parent?: string;
31
+ key?: string;
32
+ }
33
+
20
34
  /**
21
35
  * Get the dependencies of a struct type. If a struct has the same dependency multiple times, it's only included once
22
36
  * in the resulting array.
@@ -27,15 +41,10 @@ function getHex(value: BigNumberish): string {
27
41
  * @return {string[]}
28
42
  */
29
43
  export const getDependencies = (
30
- typedData: TypedData,
44
+ types: TypedData['types'],
31
45
  type: string,
32
46
  dependencies: string[] = []
33
47
  ): string[] => {
34
- // `getDependencies` is called by most other functions, so we validate the JSON schema here
35
- if (!validateTypedData(typedData)) {
36
- throw new Error('Typed data does not match JSON schema');
37
- }
38
-
39
48
  // Include pointers (struct arrays)
40
49
  if (type[type.length - 1] === '*') {
41
50
  // eslint-disable-next-line no-param-reassign
@@ -46,16 +55,16 @@ export const getDependencies = (
46
55
  return dependencies;
47
56
  }
48
57
 
49
- if (!typedData.types[type]) {
58
+ if (!types[type]) {
50
59
  return dependencies;
51
60
  }
52
61
 
53
62
  return [
54
63
  type,
55
- ...typedData.types[type].reduce<string[]>(
64
+ ...types[type].reduce<string[]>(
56
65
  (previous, t) => [
57
66
  ...previous,
58
- ...getDependencies(typedData, t.type, previous).filter(
67
+ ...getDependencies(types, t.type, previous).filter(
59
68
  (dependency) => !previous.includes(dependency)
60
69
  ),
61
70
  ],
@@ -64,6 +73,22 @@ export const getDependencies = (
64
73
  ];
65
74
  };
66
75
 
76
+ function getMerkleTreeType(types: TypedData['types'], ctx: Context) {
77
+ if (ctx.parent && ctx.key) {
78
+ const parentType = types[ctx.parent];
79
+ const merkleType = parentType.find((t) => t.name === ctx.key)!;
80
+ const isMerkleTree = isMerkleTreeType(merkleType);
81
+ if (!isMerkleTree) {
82
+ throw new Error(`${ctx.key} is not a merkle tree`);
83
+ }
84
+ if (merkleType.contains.endsWith('*')) {
85
+ throw new Error(`Merkle tree contain property must not be an array but was given ${ctx.key}`);
86
+ }
87
+ return merkleType.contains;
88
+ }
89
+ return 'raw';
90
+ }
91
+
67
92
  /**
68
93
  * Encode a type to a string. All dependant types are alphabetically sorted.
69
94
  *
@@ -71,13 +96,13 @@ export const getDependencies = (
71
96
  * @param {string} type
72
97
  * @return {string}
73
98
  */
74
- export const encodeType = (typedData: TypedData, type: string): string => {
75
- const [primary, ...dependencies] = getDependencies(typedData, type);
76
- const types = [primary, ...dependencies.sort()];
99
+ export const encodeType = (types: TypedData['types'], type: string): string => {
100
+ const [primary, ...dependencies] = getDependencies(types, type);
101
+ const newTypes = !primary ? [] : [primary, ...dependencies.sort()];
77
102
 
78
- return types
103
+ return newTypes
79
104
  .map((dependency) => {
80
- return `${dependency}(${typedData.types[dependency].map((t) => `${t.name}:${t.type}`)})`;
105
+ return `${dependency}(${types[dependency].map((t) => `${t.name}:${t.type}`)})`;
81
106
  })
82
107
  .join('');
83
108
  };
@@ -89,8 +114,8 @@ export const encodeType = (typedData: TypedData, type: string): string => {
89
114
  * @param {string} type
90
115
  * @return {string}
91
116
  */
92
- export const getTypeHash = (typedData: TypedData, type: string): string => {
93
- return getSelectorFromName(encodeType(typedData, type));
117
+ export const getTypeHash = (types: TypedData['types'], type: string): string => {
118
+ return getSelectorFromName(encodeType(types, type));
94
119
  };
95
120
 
96
121
  /**
@@ -102,28 +127,47 @@ export const getTypeHash = (typedData: TypedData, type: string): string => {
102
127
  * @param {any} data
103
128
  * @returns {[string, string]}
104
129
  */
105
- const encodeValue = (typedData: TypedData, type: string, data: unknown): [string, string] => {
106
- if (typedData.types[type]) {
130
+ export const encodeValue = (
131
+ types: TypedData['types'],
132
+ type: string,
133
+ data: unknown,
134
+ ctx: Context = {}
135
+ ): [string, string] => {
136
+ if (types[type]) {
107
137
  // eslint-disable-next-line @typescript-eslint/no-use-before-define
108
- return [type, getStructHash(typedData, type, data as Record<string, unknown>)];
138
+ return [type, getStructHash(types, type, data as Record<string, unknown>)];
109
139
  }
110
140
 
111
141
  if (
112
- Object.keys(typedData.types)
142
+ Object.keys(types)
113
143
  .map((x) => `${x}*`)
114
144
  .includes(type)
115
145
  ) {
116
146
  const structHashes: string[] = (data as unknown[]).map((struct) => {
117
147
  // eslint-disable-next-line @typescript-eslint/no-use-before-define
118
- return getStructHash(typedData, type.slice(0, -1), struct as Record<string, unknown>);
148
+ return getStructHash(types, type.slice(0, -1), struct as Record<string, unknown>);
119
149
  });
120
150
  return [type, computeHashOnElements(structHashes)];
121
151
  }
122
152
 
153
+ if (type === 'merkletree') {
154
+ const merkleTreeType = getMerkleTreeType(types, ctx);
155
+ const structHashes: string[] = (data as unknown[]).map((struct) => {
156
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
157
+ return encodeValue(types, merkleTreeType, struct as Record<string, unknown>)[1];
158
+ });
159
+ const { root } = new MerkleTree(structHashes as string[]);
160
+ return ['felt', root];
161
+ }
162
+
123
163
  if (type === 'felt*') {
124
164
  return ['felt*', computeHashOnElements(data as string[])];
125
165
  }
126
166
 
167
+ if (type === 'selector') {
168
+ return ['felt', prepareSelector(data as string)];
169
+ }
170
+
127
171
  return [type, getHex(data as string)];
128
172
  };
129
173
 
@@ -135,25 +179,32 @@ const encodeValue = (typedData: TypedData, type: string, data: unknown): [string
135
179
  * @param {string} type
136
180
  * @param {Record<string, any>} data
137
181
  */
138
- export const encodeData = <T extends TypedData>(typedData: T, type: string, data: T['message']) => {
139
- const [types, values] = typedData.types[type].reduce<[string[], string[]]>(
182
+ export const encodeData = <T extends TypedData>(
183
+ types: T['types'],
184
+ type: string,
185
+ data: T['message']
186
+ ) => {
187
+ const [returnTypes, values] = types[type].reduce<[string[], string[]]>(
140
188
  ([ts, vs], field) => {
141
189
  if (data[field.name] === undefined || data[field.name] === null) {
142
190
  throw new Error(`Cannot encode data: missing data for '${field.name}'`);
143
191
  }
144
192
 
145
193
  const value = data[field.name];
146
- const [t, encodedValue] = encodeValue(typedData, field.type, value);
194
+ const [t, encodedValue] = encodeValue(types, field.type, value, {
195
+ parent: type,
196
+ key: field.name,
197
+ });
147
198
 
148
199
  return [
149
200
  [...ts, t],
150
201
  [...vs, encodedValue],
151
202
  ];
152
203
  },
153
- [['felt'], [getTypeHash(typedData, type)]]
204
+ [['felt'], [getTypeHash(types, type)]]
154
205
  );
155
206
 
156
- return [types, values];
207
+ return [returnTypes, values];
157
208
  };
158
209
 
159
210
  /**
@@ -166,27 +217,30 @@ export const encodeData = <T extends TypedData>(typedData: T, type: string, data
166
217
  * @return {Buffer}
167
218
  */
168
219
  export const getStructHash = <T extends TypedData>(
169
- typedData: T,
220
+ types: T['types'],
170
221
  type: string,
171
222
  data: T['message']
172
223
  ) => {
173
- return computeHashOnElements(encodeData(typedData, type, data)[1]);
224
+ return computeHashOnElements(encodeData(types, type, data)[1]);
174
225
  };
175
226
 
176
227
  /**
177
- * Get the EIP-191 encoded message to sign, from the typedData object. If `hash` is enabled, the message will be hashed
178
- * with Keccak256.
228
+ * Get the EIP-191 encoded message to sign, from the typedData object.
179
229
  *
180
230
  * @param {TypedData} typedData
181
231
  * @param {BigNumberish} account
182
232
  * @return {string}
183
233
  */
184
234
  export const getMessageHash = (typedData: TypedData, account: BigNumberish): string => {
235
+ if (!validateTypedData(typedData)) {
236
+ throw new Error('Typed data does not match JSON schema');
237
+ }
238
+
185
239
  const message = [
186
240
  encodeShortString('StarkNet Message'),
187
- getStructHash(typedData, 'StarkNetDomain', typedData.domain),
241
+ getStructHash(typedData.types, 'StarkNetDomain', typedData.domain),
188
242
  account,
189
- getStructHash(typedData, typedData.primaryType, typedData.message),
243
+ getStructHash(typedData.types, typedData.primaryType, typedData.message),
190
244
  ];
191
245
 
192
246
  return computeHashOnElements(message);
@@ -1,13 +1,21 @@
1
+ export type StarkNetMerkleType = {
2
+ name: string;
3
+ type: 'merkletree';
4
+ contains: string;
5
+ };
6
+
1
7
  /**
2
8
  * A single type, as part of a struct. The `type` field can be any of the EIP-712 supported types.
3
9
  *
4
10
  * Note that the `uint` and `int` aliases like in Solidity, and fixed point numbers are not supported by the EIP-712
5
11
  * standard.
6
12
  */
7
- export interface StarkNetType {
8
- name: string;
9
- type: 'felt' | 'felt*' | string;
10
- }
13
+ export type StarkNetType =
14
+ | {
15
+ name: string;
16
+ type: string;
17
+ }
18
+ | StarkNetMerkleType;
11
19
 
12
20
  /**
13
21
  * The EIP712 domain struct. Any of these fields are optional, but it must contain at least one field.
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Starknet RPC version 0.1.0
3
+ * starknet_api_openrpc version 0.31.0
4
+ *
5
+ * TypeScript Representation of OpenRpc protocol types | results
6
+ * errors are not implemented here only results
7
+ */
8
+ /**
9
+ * "type": "string",
10
+ * "title": "Field element",
11
+ * "$comment": "A field element, represented as a string of hex digits",
12
+ * "description": "A field element. Represented as up to 63 hex digits and leading 4 bits zeroed.",
13
+ * "pattern": "^0x0[a-fA-F0-9]{1,63}$"
14
+ */
15
+ declare type FELT = string;
16
+ declare type BLOCK_NUMBER = number;
17
+ declare type BLOCK_HASH = FELT;
18
+ declare type TXN_HASH = FELT;
19
+ declare type TXN_STATUS = 'PENDING' | 'ACCEPTED_ON_L2' | 'ACCEPTED_ON_L1' | 'REJECTED';
20
+ declare type TXN_TYPE = 'DECLARE' | 'DEPLOY' | 'INVOKE' | 'L1_HANDLER';
21
+ declare type MSG_TO_L1 = {
22
+ to_address: FELT;
23
+ payload: Array<FELT>;
24
+ };
25
+ declare type EVENT = {
26
+ from_address: FELT;
27
+ keys: Array<FELT>;
28
+ data: Array<FELT>;
29
+ };
30
+ declare type COMMON_RECEIPT_PROPERTIES = {
31
+ transaction_hash: TXN_HASH;
32
+ actual_fee: FELT;
33
+ status: TXN_STATUS;
34
+ block_hash: BLOCK_HASH;
35
+ block_number: BLOCK_NUMBER;
36
+ type?: TXN_TYPE;
37
+ };
38
+ declare type INVOKE_TXN_RECEIPT_PROPERTIES = {
39
+ messages_sent: MSG_TO_L1;
40
+ events: EVENT;
41
+ };
42
+ declare type PENDING_COMMON_RECEIPT_PROPERTIES = {
43
+ transaction_hash: TXN_HASH;
44
+ actual_fee: FELT;
45
+ type?: TXN_TYPE;
46
+ };
47
+ declare type INVOKE_TXN_RECEIPT = COMMON_RECEIPT_PROPERTIES & INVOKE_TXN_RECEIPT_PROPERTIES;
48
+ declare type L1_HANDLER_TXN_RECEIPT = COMMON_RECEIPT_PROPERTIES;
49
+ declare type DECLARE_TXN_RECEIPT = COMMON_RECEIPT_PROPERTIES;
50
+ declare type DEPLOY_TXN_RECEIPT = COMMON_RECEIPT_PROPERTIES;
51
+ declare type PENDING_INVOKE_TXN_RECEIPT = PENDING_COMMON_RECEIPT_PROPERTIES & INVOKE_TXN_RECEIPT_PROPERTIES;
52
+ declare type PENDING_TXN_RECEIPT = PENDING_INVOKE_TXN_RECEIPT | PENDING_COMMON_RECEIPT_PROPERTIES;
53
+ declare type TXN_RECEIPT = INVOKE_TXN_RECEIPT | L1_HANDLER_TXN_RECEIPT | DECLARE_TXN_RECEIPT | DEPLOY_TXN_RECEIPT | PENDING_TXN_RECEIPT;
54
+ export declare namespace RPC_1 {
55
+ type GetTransactionReceiptResponse = TXN_RECEIPT;
56
+ type Methods = {
57
+ starknet_getTransactionReceipt: {
58
+ QUERY: never;
59
+ REQUEST: any[];
60
+ RESPONSE: GetTransactionReceiptResponse;
61
+ };
62
+ };
63
+ }
64
+ declare type BLOCK_HEADER = {
65
+ block_hash: BLOCK_HASH;
66
+ parent_hash: BLOCK_HASH;
67
+ block_number: BLOCK_NUMBER;
68
+ new_root: FELT;
69
+ timestamp: number;
70
+ sequencer_address: FELT;
71
+ };
72
+ declare type BLOCK_BODY_WITH_TX_HASHES = {
73
+ transactions: Array<TXN_HASH>;
74
+ };
75
+ declare type BLOCK_WITH_TX_HASHES = {
76
+ status: TXN_STATUS;
77
+ } & BLOCK_HEADER & BLOCK_BODY_WITH_TX_HASHES;
78
+ declare type PENDING_BLOCK_WITH_TX_HASHES = BLOCK_BODY_WITH_TX_HASHES & {
79
+ timestamp: number;
80
+ sequencer_address: FELT;
81
+ parent_hash: BLOCK_HASH;
82
+ };
83
+ declare type BLOCK_STATUS = 'PENDING' | 'ACCEPTED_ON_L2' | 'ACCEPTED_ON_L1' | 'REJECTED';
84
+ /**
85
+ * "title": "An integer number in hex format (0x...)",
86
+ * "pattern": "^0x[a-fA-F0-9]+$"
87
+ */
88
+ declare type NUM_AS_HEX = string;
89
+ declare type SIGNATURE = Array<FELT>;
90
+ declare type COMMON_TXN_PROPERTIES = {
91
+ transaction_hash: TXN_HASH;
92
+ max_fee: FELT;
93
+ version: NUM_AS_HEX;
94
+ signature: SIGNATURE;
95
+ nonce: FELT;
96
+ type: TXN_TYPE;
97
+ };
98
+ declare type ADDRESS = FELT;
99
+ declare type FUNCTION_CALL = {
100
+ contract_address: ADDRESS;
101
+ entry_point_selector: FELT;
102
+ calldata: Array<FELT>;
103
+ };
104
+ declare type INVOKE_TXN = COMMON_TXN_PROPERTIES & FUNCTION_CALL;
105
+ declare type DECLARE_TXN = COMMON_TXN_PROPERTIES & {
106
+ class_hash: FELT;
107
+ sender_address: ADDRESS;
108
+ };
109
+ declare type DEPLOY_TXN = {
110
+ transaction_hash: TXN_HASH;
111
+ class_hash: FELT;
112
+ version: NUM_AS_HEX;
113
+ type: TXN_TYPE;
114
+ contract_address: FELT;
115
+ contract_address_salt: FELT;
116
+ constructor_calldata: Array<FELT>;
117
+ };
118
+ declare type TXN = INVOKE_TXN | DECLARE_TXN | DEPLOY_TXN;
119
+ declare type BLOCK_BODY_WITH_TXS = {
120
+ transactions: Array<TXN>;
121
+ };
122
+ declare type BLOCK_WITH_TXS = {
123
+ status: BLOCK_STATUS;
124
+ } & BLOCK_HEADER & BLOCK_BODY_WITH_TXS;
125
+ declare type PENDING_BLOCK_WITH_TXS = BLOCK_BODY_WITH_TXS & {
126
+ timestamp: number;
127
+ sequencer_address: FELT;
128
+ parent_hash: BLOCK_HASH;
129
+ };
130
+ declare type CONTRACT_CLASS = {
131
+ program: string;
132
+ entry_points_by_type: {
133
+ CONSTRUCTOR: CONTRACT_ENTRY_POINT_LIST;
134
+ EXTERNAL: CONTRACT_ENTRY_POINT_LIST;
135
+ L1_HANDLER: CONTRACT_ENTRY_POINT_LIST;
136
+ };
137
+ };
138
+ declare type CONTRACT_ENTRY_POINT_LIST = Array<CONTRACT_ENTRY_POINT>;
139
+ declare type CONTRACT_ENTRY_POINT = {
140
+ offset: NUM_AS_HEX;
141
+ selector: FELT;
142
+ };
143
+ export declare namespace OPENRPC {
144
+ type GetBlockWithTxHashesResponse = BLOCK_WITH_TX_HASHES | PENDING_BLOCK_WITH_TX_HASHES;
145
+ type GetBlockWithTxs = BLOCK_WITH_TXS | PENDING_BLOCK_WITH_TXS;
146
+ type GetStorageAtResponse = FELT;
147
+ type GetTransactionByHashResponse = TXN;
148
+ type GetTransactionByBlockIdAndIndex = TXN;
149
+ type GetClassResponse = CONTRACT_CLASS;
150
+ }
151
+ export {};