starknet 2.4.0 → 2.7.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 (61) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/__tests__/account.test.ts +3 -3
  3. package/__tests__/provider.test.ts +28 -13
  4. package/__tests__/utils/ellipticalCurve.test.ts +1 -1
  5. package/__tests__/utils/typedData.test.ts +72 -0
  6. package/contract.d.ts +2 -2
  7. package/dist/contract.d.ts +2 -2
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.js +2 -1
  10. package/dist/provider/default.d.ts +23 -19
  11. package/dist/provider/default.js +58 -41
  12. package/dist/provider/interface.d.ts +22 -18
  13. package/dist/provider/utils.d.ts +27 -0
  14. package/dist/provider/utils.js +37 -0
  15. package/dist/signer/default.d.ts +18 -1
  16. package/dist/signer/default.js +43 -7
  17. package/dist/signer/interface.d.ts +20 -1
  18. package/dist/types.d.ts +7 -6
  19. package/dist/utils/ellipticCurve.d.ts +8 -1
  20. package/dist/utils/ellipticCurve.js +48 -9
  21. package/dist/utils/stark.d.ts +2 -3
  22. package/dist/utils/typedData/index.d.ts +91 -0
  23. package/dist/utils/typedData/index.js +183 -0
  24. package/dist/utils/typedData/types.d.ts +82 -0
  25. package/dist/utils/typedData/types.js +47 -0
  26. package/dist/utils/typedData/utils.d.ts +24 -0
  27. package/dist/utils/typedData/utils.js +15 -0
  28. package/index.d.ts +1 -0
  29. package/index.js +3 -1
  30. package/package.json +2 -1
  31. package/provider/default.d.ts +34 -18
  32. package/provider/default.js +75 -45
  33. package/provider/interface.d.ts +33 -17
  34. package/provider/utils.d.ts +30 -0
  35. package/provider/utils.js +39 -0
  36. package/signer/default.d.ts +18 -1
  37. package/signer/default.js +44 -7
  38. package/signer/interface.d.ts +20 -1
  39. package/src/contract.ts +2 -2
  40. package/src/index.ts +1 -0
  41. package/src/provider/default.ts +55 -32
  42. package/src/provider/interface.ts +32 -17
  43. package/src/provider/utils.ts +38 -0
  44. package/src/signer/default.ts +26 -3
  45. package/src/signer/interface.ts +22 -1
  46. package/src/types.ts +13 -6
  47. package/src/utils/ellipticCurve.ts +31 -9
  48. package/src/utils/stark.ts +4 -4
  49. package/src/utils/typedData/index.ts +176 -0
  50. package/src/utils/typedData/types.ts +82 -0
  51. package/src/utils/typedData/utils.ts +13 -0
  52. package/types.d.ts +9 -7
  53. package/utils/ellipticCurve.d.ts +12 -1
  54. package/utils/ellipticCurve.js +72 -23
  55. package/utils/stark.d.ts +2 -3
  56. package/utils/typedData/index.d.ts +113 -0
  57. package/utils/typedData/index.js +247 -0
  58. package/utils/typedData/types.d.ts +103 -0
  59. package/utils/typedData/types.js +57 -0
  60. package/utils/typedData/utils.d.ts +27 -0
  61. package/utils/typedData/utils.js +15 -0
@@ -3,6 +3,7 @@ import urljoin from 'url-join';
3
3
 
4
4
  import {
5
5
  AddTransactionResponse,
6
+ BlockNumber,
6
7
  CallContractResponse,
7
8
  CallContractTransaction,
8
9
  CompiledContract,
@@ -11,14 +12,16 @@ import {
11
12
  GetContractAddressesResponse,
12
13
  GetTransactionResponse,
13
14
  GetTransactionStatusResponse,
15
+ Signature,
14
16
  Transaction,
15
17
  } from '../types';
16
18
  import { parse, stringify } from '../utils/json';
17
19
  import { BigNumberish, toBN, toHex } from '../utils/number';
18
20
  import { compressProgram, formatSignature, randomAddress } from '../utils/stark';
19
21
  import { ProviderInterface } from './interface';
22
+ import { getFormattedBlockIdentifier } from './utils';
20
23
 
21
- type NetworkName = 'mainnet-alpha' | 'georli-alpha';
24
+ type NetworkName = 'mainnet-alpha' | 'goerli-alpha';
22
25
 
23
26
  type ProviderOptions =
24
27
  | {
@@ -39,7 +42,7 @@ export class Provider implements ProviderInterface {
39
42
 
40
43
  public gatewayUrl: string;
41
44
 
42
- constructor(optionsOrProvider: ProviderOptions | Provider = { network: 'georli-alpha' }) {
45
+ constructor(optionsOrProvider: ProviderOptions | Provider = { network: 'goerli-alpha' }) {
43
46
  if (optionsOrProvider instanceof Provider) {
44
47
  this.baseUrl = optionsOrProvider.baseUrl;
45
48
  this.feederGatewayUrl = optionsOrProvider.feederGatewayUrl;
@@ -59,7 +62,7 @@ export class Provider implements ProviderInterface {
59
62
  switch (name) {
60
63
  case 'mainnet-alpha':
61
64
  return 'https://alpha-mainnet.starknet.io';
62
- case 'georli-alpha':
65
+ case 'goerli-alpha':
63
66
  default:
64
67
  return 'https://alpha4.starknet.io';
65
68
  }
@@ -81,18 +84,22 @@ export class Provider implements ProviderInterface {
81
84
  /**
82
85
  * Calls a function on the StarkNet contract.
83
86
  *
84
- * [Reference](https://github.com/starkware-libs/cairo-lang/blob/f464ec4797361b6be8989e36e02ec690e74ef285/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L17-L25)
87
+ * [Reference](https://github.com/starkware-libs/cairo-lang/blob/fc97bdd8322a7df043c87c371634b26c15ed6cee/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L25-L39)
85
88
  *
86
89
  * @param invokeTransaction - transaction to be invoked
87
- * @param blockId
90
+ * @param blockHash
91
+ * @param blockNumber
88
92
  * @returns the result of the function on the smart contract.
89
93
  */
90
94
  public async callContract(
91
95
  invokeTransaction: CallContractTransaction,
92
- blockId?: number
96
+ blockHash?: BigNumberish,
97
+ blockNumber: BlockNumber = null
93
98
  ): Promise<CallContractResponse> {
99
+ const formattedBlockIdentifier = getFormattedBlockIdentifier(blockHash, blockNumber);
100
+
94
101
  const { data } = await axios.post<CallContractResponse>(
95
- urljoin(this.feederGatewayUrl, 'call_contract', `?blockId=${blockId ?? 'null'}`),
102
+ urljoin(this.feederGatewayUrl, 'call_contract', formattedBlockIdentifier),
96
103
  {
97
104
  signature: [],
98
105
  calldata: [],
@@ -103,16 +110,22 @@ export class Provider implements ProviderInterface {
103
110
  }
104
111
 
105
112
  /**
106
- * Gets the block information from a block ID.
113
+ * Gets the block information
107
114
  *
108
- * [Reference](https://github.com/starkware-libs/cairo-lang/blob/f464ec4797361b6be8989e36e02ec690e74ef285/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L27-L31)
115
+ * [Reference](https://github.com/starkware-libs/cairo-lang/blob/fc97bdd8322a7df043c87c371634b26c15ed6cee/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L41-L53)
109
116
  *
110
- * @param blockId
111
- * @returns the block object { block_id, previous_block_id, state_root, status, timestamp, transaction_receipts, transactions }
117
+ * @param blockHash
118
+ * @param blockNumber
119
+ * @returns the block object { block_number, previous_block_number, state_root, status, timestamp, transaction_receipts, transactions }
112
120
  */
113
- public async getBlock(blockId?: number): Promise<GetBlockResponse> {
121
+ public async getBlock(
122
+ blockHash?: BigNumberish,
123
+ blockNumber: BlockNumber = null
124
+ ): Promise<GetBlockResponse> {
125
+ const formattedBlockIdentifier = getFormattedBlockIdentifier(blockHash, blockNumber);
126
+
114
127
  const { data } = await axios.get<GetBlockResponse>(
115
- urljoin(this.feederGatewayUrl, 'get_block', `?blockId=${blockId ?? 'null'}`)
128
+ urljoin(this.feederGatewayUrl, 'get_block', formattedBlockIdentifier)
116
129
  );
117
130
  return data;
118
131
  }
@@ -120,18 +133,25 @@ export class Provider implements ProviderInterface {
120
133
  /**
121
134
  * Gets the code of the deployed contract.
122
135
  *
123
- * [Reference](https://github.com/starkware-libs/cairo-lang/blob/f464ec4797361b6be8989e36e02ec690e74ef285/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L33-L36)
136
+ * [Reference](https://github.com/starkware-libs/cairo-lang/blob/fc97bdd8322a7df043c87c371634b26c15ed6cee/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L55-L68)
124
137
  *
125
138
  * @param contractAddress
126
- * @param blockId
139
+ * @param blockHash
140
+ * @param blockNumber
127
141
  * @returns Bytecode and ABI of compiled contract
128
142
  */
129
- public async getCode(contractAddress: string, blockId?: number): Promise<GetCodeResponse> {
143
+ public async getCode(
144
+ contractAddress: string,
145
+ blockHash?: BigNumberish,
146
+ blockNumber: BlockNumber = null
147
+ ): Promise<GetCodeResponse> {
148
+ const formattedBlockIdentifier = getFormattedBlockIdentifier(blockHash, blockNumber);
149
+
130
150
  const { data } = await axios.get<GetCodeResponse>(
131
151
  urljoin(
132
152
  this.feederGatewayUrl,
133
153
  'get_code',
134
- `?contractAddress=${contractAddress}&blockId=${blockId ?? 'null'}`
154
+ `?contractAddress=${contractAddress}&${formattedBlockIdentifier}`
135
155
  )
136
156
  );
137
157
  return data;
@@ -141,23 +161,27 @@ export class Provider implements ProviderInterface {
141
161
  /**
142
162
  * Gets the contract's storage variable at a specific key.
143
163
  *
144
- * [Reference](https://github.com/starkware-libs/cairo-lang/blob/f464ec4797361b6be8989e36e02ec690e74ef285/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L38-L46)
164
+ * [Reference](https://github.com/starkware-libs/cairo-lang/blob/fc97bdd8322a7df043c87c371634b26c15ed6cee/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L70-L85)
145
165
  *
146
166
  * @param contractAddress
147
167
  * @param key - from getStorageVarAddress('<STORAGE_VARIABLE_NAME>') (WIP)
148
- * @param blockId
168
+ * @param blockHash
169
+ * @param blockNumber
149
170
  * @returns the value of the storage variable
150
171
  */
151
172
  public async getStorageAt(
152
173
  contractAddress: string,
153
174
  key: number,
154
- blockId?: number
175
+ blockHash?: BigNumberish,
176
+ blockNumber: BlockNumber = null
155
177
  ): Promise<object> {
178
+ const formattedBlockIdentifier = getFormattedBlockIdentifier(blockHash, blockNumber);
179
+
156
180
  const { data } = await axios.get<object>(
157
181
  urljoin(
158
182
  this.feederGatewayUrl,
159
183
  'get_storage_at',
160
- `?contractAddress=${contractAddress}&key=${key}&blockId=${blockId ?? 'null'}`
184
+ `?contractAddress=${contractAddress}&key=${key}&${formattedBlockIdentifier}`
161
185
  )
162
186
  );
163
187
  return data;
@@ -169,7 +193,7 @@ export class Provider implements ProviderInterface {
169
193
  * [Reference](https://github.com/starkware-libs/cairo-lang/blob/f464ec4797361b6be8989e36e02ec690e74ef285/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L48-L52)
170
194
  *
171
195
  * @param txHash
172
- * @returns the transaction status object { block_id, tx_status: NOT_RECEIVED | RECEIVED | PENDING | REJECTED | ACCEPTED_ONCHAIN }
196
+ * @returns the transaction status object { block_number, tx_status: NOT_RECEIVED | RECEIVED | PENDING | REJECTED | ACCEPTED_ONCHAIN }
173
197
  */
174
198
  public async getTransactionStatus(txHash: BigNumberish): Promise<GetTransactionStatusResponse> {
175
199
  const txHashBn = toBN(txHash);
@@ -189,7 +213,7 @@ export class Provider implements ProviderInterface {
189
213
  * [Reference](https://github.com/starkware-libs/cairo-lang/blob/f464ec4797361b6be8989e36e02ec690e74ef285/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L54-L58)
190
214
  *
191
215
  * @param txHash
192
- * @returns the transacton object { transaction_id, status, transaction, block_id?, block_number?, transaction_index?, transaction_failure_reason? }
216
+ * @returns the transacton object { transaction_id, status, transaction, block_number?, block_number?, transaction_index?, transaction_failure_reason? }
193
217
  */
194
218
  public async getTransaction(txHash: BigNumberish): Promise<GetTransactionResponse> {
195
219
  const txHashBn = toBN(txHash);
@@ -265,7 +289,7 @@ export class Provider implements ProviderInterface {
265
289
  contractAddress: string,
266
290
  entrypointSelector: string,
267
291
  calldata?: string[],
268
- signature?: [BigNumberish, BigNumberish]
292
+ signature?: Signature
269
293
  ): Promise<AddTransactionResponse> {
270
294
  return this.addTransaction({
271
295
  type: 'INVOKE_FUNCTION',
@@ -278,21 +302,20 @@ export class Provider implements ProviderInterface {
278
302
 
279
303
  public async waitForTx(txHash: BigNumberish, retryInterval: number = 8000) {
280
304
  let onchain = false;
305
+ await wait(retryInterval);
306
+
281
307
  while (!onchain) {
282
308
  // eslint-disable-next-line no-await-in-loop
283
309
  await wait(retryInterval);
284
310
  // eslint-disable-next-line no-await-in-loop
285
311
  const res = await this.getTransactionStatus(txHash);
286
312
 
287
- if (
288
- res.tx_status === 'ACCEPTED_ONCHAIN' ||
289
- (res.tx_status === 'PENDING' && res.block_hash !== 'pending') // This is needed as of today. In the future there will be a different status for pending transactions.
290
- ) {
313
+ if (res.tx_status === 'ACCEPTED_ON_L1' || res.tx_status === 'ACCEPTED_ON_L2') {
291
314
  onchain = true;
292
- } else if (res.tx_status === 'REJECTED') {
293
- throw Error('REJECTED');
294
- } else if (res.tx_status === 'NOT_RECEIVED') {
295
- throw Error('NOT_RECEIVED');
315
+ } else if (res.tx_status === 'REJECTED' || res.tx_status === 'NOT_RECEIVED') {
316
+ const error = Error(res.tx_status) as Error & { response: GetTransactionStatusResponse };
317
+ error.response = res;
318
+ throw error;
296
319
  }
297
320
  }
298
321
  }
@@ -1,5 +1,6 @@
1
1
  import type {
2
2
  AddTransactionResponse,
3
+ BlockNumber,
3
4
  CallContractResponse,
4
5
  CallContractTransaction,
5
6
  CompiledContract,
@@ -8,6 +9,7 @@ import type {
8
9
  GetContractAddressesResponse,
9
10
  GetTransactionResponse,
10
11
  GetTransactionStatusResponse,
12
+ Signature,
11
13
  Transaction,
12
14
  } from '../types';
13
15
  import type { BigNumberish } from '../utils/number';
@@ -30,53 +32,66 @@ export abstract class ProviderInterface {
30
32
  /**
31
33
  * Calls a function on the StarkNet contract.
32
34
  *
33
- * [Reference](https://github.com/starkware-libs/cairo-lang/blob/f464ec4797361b6be8989e36e02ec690e74ef285/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L17-L25)
35
+ * [Reference](https://github.com/starkware-libs/cairo-lang/blob/fc97bdd8322a7df043c87c371634b26c15ed6cee/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L25-L39)
34
36
  *
35
37
  * @param invokeTransaction - transaction to be invoked
36
- * @param blockId
38
+ * @param blockHash
39
+ * @param blockNumber
37
40
  * @returns the result of the function on the smart contract.
38
41
  */
39
42
  public abstract callContract(
40
43
  invokeTransaction: CallContractTransaction,
41
- blockId?: number
44
+ blockHash?: BigNumberish,
45
+ blockNumber?: BlockNumber
42
46
  ): Promise<CallContractResponse>;
43
47
 
44
48
  /**
45
- * Gets the block information from a block ID.
49
+ * Gets the block information
46
50
  *
47
- * [Reference](https://github.com/starkware-libs/cairo-lang/blob/f464ec4797361b6be8989e36e02ec690e74ef285/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L27-L31)
51
+ * [Reference](https://github.com/starkware-libs/cairo-lang/blob/fc97bdd8322a7df043c87c371634b26c15ed6cee/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L41-L53)
48
52
  *
49
- * @param blockId
50
- * @returns the block object { block_id, previous_block_id, state_root, status, timestamp, transaction_receipts, transactions }
53
+ * @param blockHash
54
+ * @param blockNumber
55
+ * @returns the block object { block_number, previous_block_number, state_root, status, timestamp, transaction_receipts, transactions }
51
56
  */
52
- public abstract getBlock(blockId?: number): Promise<GetBlockResponse>;
57
+ public abstract getBlock(
58
+ blockHash?: BigNumberish,
59
+ blockNumber?: BlockNumber
60
+ ): Promise<GetBlockResponse>;
53
61
 
54
62
  /**
55
63
  * Gets the code of the deployed contract.
56
64
  *
57
- * [Reference](https://github.com/starkware-libs/cairo-lang/blob/f464ec4797361b6be8989e36e02ec690e74ef285/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L33-L36)
65
+ * [Reference](https://github.com/starkware-libs/cairo-lang/blob/fc97bdd8322a7df043c87c371634b26c15ed6cee/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L55-L68)
58
66
  *
59
67
  * @param contractAddress
60
- * @param blockId
68
+ * @param blockHash
69
+ * @param blockNumber
61
70
  * @returns Bytecode and ABI of compiled contract
62
71
  */
63
- public abstract getCode(contractAddress: string, blockId?: number): Promise<GetCodeResponse>;
72
+ public abstract getCode(
73
+ contractAddress: string,
74
+ blockHash?: BigNumberish,
75
+ blockNumber?: BlockNumber
76
+ ): Promise<GetCodeResponse>;
64
77
 
65
78
  // TODO: add proper type
66
79
  /**
67
80
  * Gets the contract's storage variable at a specific key.
68
81
  *
69
- * [Reference](https://github.com/starkware-libs/cairo-lang/blob/f464ec4797361b6be8989e36e02ec690e74ef285/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L38-L46)
82
+ * [Reference](https://github.com/starkware-libs/cairo-lang/blob/fc97bdd8322a7df043c87c371634b26c15ed6cee/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L70-L85)
70
83
  *
71
84
  * @param contractAddress
72
85
  * @param key - from getStorageVarAddress('<STORAGE_VARIABLE_NAME>') (WIP)
73
- * @param blockId
86
+ * @param blockHash
87
+ * @param blockNumber
74
88
  * @returns the value of the storage variable
75
89
  */
76
90
  public abstract getStorageAt(
77
91
  contractAddress: string,
78
92
  key: number,
79
- blockId?: number
93
+ blockHash?: BigNumberish,
94
+ blockNumber?: BlockNumber
80
95
  ): Promise<object>;
81
96
 
82
97
  /**
@@ -85,7 +100,7 @@ export abstract class ProviderInterface {
85
100
  * [Reference](https://github.com/starkware-libs/cairo-lang/blob/f464ec4797361b6be8989e36e02ec690e74ef285/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L48-L52)
86
101
  *
87
102
  * @param txHash
88
- * @returns the transaction status object { block_id, tx_status: NOT_RECEIVED | RECEIVED | PENDING | REJECTED | ACCEPTED_ONCHAIN }
103
+ * @returns the transaction status object { block_number, tx_status: NOT_RECEIVED | RECEIVED | PENDING | REJECTED | ACCEPTED_ONCHAIN }
89
104
  */
90
105
  public abstract getTransactionStatus(txHash: BigNumberish): Promise<GetTransactionStatusResponse>;
91
106
 
@@ -95,7 +110,7 @@ export abstract class ProviderInterface {
95
110
  * [Reference](https://github.com/starkware-libs/cairo-lang/blob/f464ec4797361b6be8989e36e02ec690e74ef285/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L54-L58)
96
111
  *
97
112
  * @param txHash
98
- * @returns the transacton object { transaction_id, status, transaction, block_id?, block_number?, transaction_index?, transaction_failure_reason? }
113
+ * @returns the transacton object { transaction_id, status, transaction, block_number?, block_number?, transaction_index?, transaction_failure_reason? }
99
114
  */
100
115
  public abstract getTransaction(txHash: BigNumberish): Promise<GetTransactionResponse>;
101
116
 
@@ -135,7 +150,7 @@ export abstract class ProviderInterface {
135
150
  contractAddress: string,
136
151
  entrypointSelector: string,
137
152
  calldata?: string[],
138
- signature?: [BigNumberish, BigNumberish]
153
+ signature?: Signature
139
154
  ): Promise<AddTransactionResponse>;
140
155
 
141
156
  public abstract waitForTx(txHash: BigNumberish, retryInterval?: number): Promise<void>;
@@ -0,0 +1,38 @@
1
+ import type { BlockNumber } from '../types';
2
+ import { BigNumberish } from '../utils/number';
3
+
4
+ /**
5
+ * TODO
6
+ * [Reference](https://github.com/starkware-libs/cairo-lang/blob/fc97bdd8322a7df043c87c371634b26c15ed6cee/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L148-L153)
7
+ *
8
+ * @param hashValue
9
+ * @param hashField
10
+ */
11
+ export function formatHash() {}
12
+
13
+ /**
14
+ * TODO
15
+ * [Reference](https://github.com/starkware-libs/cairo-lang/blob/fc97bdd8322a7df043c87c371634b26c15ed6cee/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L156-L161)
16
+ * @param txHash
17
+ * @param txId
18
+ */
19
+ export function txIdentifier() {}
20
+
21
+ /**
22
+ * Gets the block identifier for API request
23
+ *
24
+ * [Reference](https://github.com/starkware-libs/cairo-lang/blob/fc97bdd8322a7df043c87c371634b26c15ed6cee/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L164-L173)
25
+ *
26
+ * @param blockNumber
27
+ * @param blockHash
28
+ * @returns block identifier for API request
29
+ */
30
+ export function getFormattedBlockIdentifier(
31
+ blockHash?: BigNumberish,
32
+ blockNumber: BlockNumber = null
33
+ ): string {
34
+ if (blockHash) {
35
+ return `?blockHash=${blockHash}`;
36
+ }
37
+ return `?blockNumber=${blockNumber}`;
38
+ }
@@ -1,12 +1,13 @@
1
1
  import assert from 'minimalistic-assert';
2
2
 
3
3
  import { Provider } from '../provider';
4
- import { AddTransactionResponse, KeyPair, Transaction } from '../types';
4
+ import { AddTransactionResponse, KeyPair, Signature, Transaction } from '../types';
5
5
  import { sign } from '../utils/ellipticCurve';
6
6
  import { addHexPrefix } from '../utils/encode';
7
7
  import { hashMessage } from '../utils/hash';
8
8
  import { toBN } from '../utils/number';
9
9
  import { getSelectorFromName } from '../utils/stark';
10
+ import { TypedData, getMessageHash } from '../utils/typedData';
10
11
  import { SignerInterface } from './interface';
11
12
 
12
13
  export class Signer extends Provider implements SignerInterface {
@@ -59,7 +60,7 @@ export class Signer extends Provider implements SignerInterface {
59
60
  )
60
61
  );
61
62
 
62
- const { r, s } = sign(this.keyPair, msgHash);
63
+ const signature = sign(this.keyPair, msgHash);
63
64
 
64
65
  return super.addTransaction({
65
66
  type: 'INVOKE_FUNCTION',
@@ -72,7 +73,29 @@ export class Signer extends Provider implements SignerInterface {
72
73
  nonceBn.toString(),
73
74
  ].map((x) => toBN(x).toString()),
74
75
  contract_address: this.address,
75
- signature: [r, s],
76
+ signature,
76
77
  });
77
78
  }
79
+
80
+ /**
81
+ * Sign an JSON object with the starknet private key and return the signature
82
+ *
83
+ * @param json - JSON object to be signed
84
+ * @returns the signature of the JSON object
85
+ * @throws {Error} if the JSON object is not a valid JSON
86
+ */
87
+ public async signMessage(typedData: TypedData): Promise<Signature> {
88
+ return sign(this.keyPair, await this.hashMessage(typedData));
89
+ }
90
+
91
+ /**
92
+ * Hash a JSON object with pederson hash and return the hash
93
+ *
94
+ * @param json - JSON object to be hashed
95
+ * @returns the hash of the JSON object
96
+ * @throws {Error} if the JSON object is not a valid JSON
97
+ */
98
+ public async hashMessage(typedData: TypedData): Promise<string> {
99
+ return getMessageHash(typedData, this.address);
100
+ }
78
101
  }
@@ -1,5 +1,6 @@
1
1
  import { Provider } from '../provider';
2
- import { AddTransactionResponse, Transaction } from '../types';
2
+ import { AddTransactionResponse, Signature, Transaction } from '../types';
3
+ import { TypedData } from '../utils/typedData/types';
3
4
 
4
5
  export abstract class SignerInterface extends Provider {
5
6
  public abstract address: string;
@@ -14,4 +15,24 @@ export abstract class SignerInterface extends Provider {
14
15
  public abstract override addTransaction(
15
16
  transaction: Transaction
16
17
  ): Promise<AddTransactionResponse>;
18
+
19
+ /**
20
+ * Sign an JSON object for off-chain usage with the starknet private key and return the signature
21
+ * This adds a message prefix so it cant be interchanged with transactions
22
+ *
23
+ * @param json - JSON object to be signed
24
+ * @returns the signature of the JSON object
25
+ * @throws {Error} if the JSON object is not a valid JSON
26
+ */
27
+ public abstract signMessage(typedData: TypedData): Promise<Signature>;
28
+
29
+ /**
30
+ * Hash a JSON object with pederson hash and return the hash
31
+ * This adds a message prefix so it cant be interchanged with transactions
32
+ *
33
+ * @param json - JSON object to be hashed
34
+ * @returns the hash of the JSON object
35
+ * @throws {Error} if the JSON object is not a valid JSON
36
+ */
37
+ public abstract hashMessage(typedData: TypedData): Promise<string>;
17
38
  }
package/src/types.ts CHANGED
@@ -3,14 +3,20 @@ import type { ec as EC } from 'elliptic';
3
3
  import type { BigNumberish } from './utils/number';
4
4
 
5
5
  export type KeyPair = EC.KeyPair;
6
- export type Signature = EC.Signature;
6
+ export type Signature = BigNumberish[];
7
7
 
8
8
  export type GetContractAddressesResponse = {
9
9
  Starknet: string;
10
10
  GpsStatementVerifier: string;
11
11
  };
12
12
 
13
- export type Status = 'NOT_RECEIVED' | 'RECEIVED' | 'PENDING' | 'REJECTED' | 'ACCEPTED_ONCHAIN';
13
+ export type Status =
14
+ | 'NOT_RECEIVED'
15
+ | 'RECEIVED'
16
+ | 'PENDING'
17
+ | 'ACCEPTED_ON_L2'
18
+ | 'ACCEPTED_ON_L1'
19
+ | 'REJECTED';
14
20
  export type TransactionStatus = 'TRANSACTION_RECEIVED';
15
21
  export type Type = 'DEPLOY' | 'INVOKE_FUNCTION';
16
22
  export type EntryPointType = 'EXTERNAL';
@@ -37,6 +43,7 @@ export type Abi = FunctionAbi | StructAbi;
37
43
 
38
44
  export type EntryPointsByType = object;
39
45
  export type Program = object;
46
+ export type BlockNumber = 'pending' | null | number;
40
47
 
41
48
  export type CompiledContract = {
42
49
  abi: Abi[];
@@ -57,7 +64,7 @@ export type DeployTransaction = {
57
64
  export type InvokeFunctionTransaction = {
58
65
  type: 'INVOKE_FUNCTION';
59
66
  contract_address: string;
60
- signature?: [BigNumberish, BigNumberish];
67
+ signature?: Signature;
61
68
  entry_point_type?: EntryPointType;
62
69
  entry_point_selector: string;
63
70
  calldata?: string[];
@@ -73,7 +80,7 @@ export type CallContractResponse = {
73
80
  };
74
81
 
75
82
  export type GetBlockResponse = {
76
- sequence_number: number;
83
+ block_number: number;
77
84
  state_root: string;
78
85
  block_hash: string;
79
86
  transactions: {
@@ -89,7 +96,7 @@ export type GetBlockResponse = {
89
96
  payload: string[];
90
97
  from_address: string;
91
98
  }[];
92
- block_number: number;
99
+ block_number: BlockNumber;
93
100
  status: Status;
94
101
  transaction_index: number;
95
102
  };
@@ -112,7 +119,7 @@ export type GetTransactionResponse = {
112
119
  status: Status;
113
120
  transaction: Transaction;
114
121
  block_hash: string;
115
- block_number: number;
122
+ block_number: BlockNumber;
116
123
  transaction_index: number;
117
124
  transaction_hash: string;
118
125
  };
@@ -53,6 +53,17 @@ export function getStarkKey(keyPair: KeyPair): string {
53
53
  return addHexPrefix(sanitizeBytes((keyPair as any).pub.getX().toString(16), 2));
54
54
  }
55
55
 
56
+ /**
57
+ * Takes a public key and casts it into `elliptic` KeyPair format.
58
+ *
59
+ * @param publicKey - public key which should get casted to a KeyPair
60
+ * @returns keyPair with public key only, which can be used to verify signatures, but cant sign anything
61
+ */
62
+ export function getKeyPairFromPublicKey(publicKey: BigNumberish): KeyPair {
63
+ const publicKeyBn = toBN(publicKey);
64
+ return ec.keyFromPublic(removeHexPrefix(toHex(publicKeyBn)), 'hex');
65
+ }
66
+
56
67
  /*
57
68
  Signs a message using the provided key.
58
69
  key should be an KeyPair with a valid private key.
@@ -69,7 +80,13 @@ export function sign(keyPair: KeyPair, msgHash: string): Signature {
69
80
  assertInRange(r, ONE, toBN(addHexPrefix(MAX_ECDSA_VAL)), 'r');
70
81
  assertInRange(s, ONE, toBN(addHexPrefix(EC_ORDER)), 's');
71
82
  assertInRange(w, ONE, toBN(addHexPrefix(MAX_ECDSA_VAL)), 'w');
72
- return msgSignature;
83
+ return [r, s];
84
+ }
85
+
86
+ function chunkArray(arr: any[], n: number): any[][] {
87
+ return Array(Math.ceil(arr.length / n))
88
+ .fill('')
89
+ .map((_, i) => arr.slice(i * n, i * n + n));
73
90
  }
74
91
 
75
92
  /*
@@ -78,15 +95,20 @@ export function sign(keyPair: KeyPair, msgHash: string): Signature {
78
95
  msgSignature should be an Signature.
79
96
  Returns a boolean true if the verification succeeds.
80
97
  */
81
- export function verify(keyPair: KeyPair, msgHash: string, sig: Signature): boolean {
98
+ export function verify(keyPair: KeyPair | KeyPair[], msgHash: string, sig: Signature): boolean {
99
+ const keyPairArray = Array.isArray(keyPair) ? keyPair : [keyPair];
82
100
  const msgHashBN = toBN(addHexPrefix(msgHash));
101
+ assert(sig.length % 2 === 0, 'Signature must be an array of length dividable by 2');
83
102
  assertInRange(msgHashBN, ZERO, toBN(addHexPrefix(MAX_ECDSA_VAL)), 'msgHash');
84
- const { r, s } = sig;
85
- const w = s.invm(ec.n!);
86
- // Verify signature has valid length.
87
- assertInRange(r, ONE, toBN(addHexPrefix(MAX_ECDSA_VAL)), 'r');
88
- assertInRange(s, ONE, toBN(addHexPrefix(EC_ORDER)), 's');
89
- assertInRange(w, ONE, toBN(addHexPrefix(MAX_ECDSA_VAL)), 'w');
103
+ assert(keyPairArray.length === sig.length / 2, 'Signature and keyPair length must be equal');
90
104
 
91
- return keyPair.verify(fixMessage(msgHash), sig);
105
+ return chunkArray(sig, 2).every(([r, s], i) => {
106
+ const rBN = toBN(r);
107
+ const sBN = toBN(s);
108
+ const w = sBN.invm((ec as any).n);
109
+ assertInRange(rBN, ONE, toBN(addHexPrefix(MAX_ECDSA_VAL)), 'r');
110
+ assertInRange(sBN, ONE, toBN(addHexPrefix(EC_ORDER)), 's');
111
+ assertInRange(w, ONE, toBN(addHexPrefix(MAX_ECDSA_VAL)), 'w');
112
+ return ec.verify(fixMessage(msgHash), { r: rBN, s: sBN }, keyPairArray[i]) ?? false;
113
+ });
92
114
  }
@@ -1,11 +1,11 @@
1
1
  import { gzip } from 'pako';
2
2
 
3
- import { CompressedProgram, Program } from '../types';
3
+ import { CompressedProgram, Program, Signature } from '../types';
4
4
  import { genKeyPair, getStarkKey } from './ellipticCurve';
5
5
  import { addHexPrefix, btoaUniversal } from './encode';
6
6
  import { starknetKeccak } from './hash';
7
7
  import { stringify } from './json';
8
- import { BigNumberish, toBN, toHex } from './number';
8
+ import { toBN, toHex } from './number';
9
9
 
10
10
  /**
11
11
  * Function to compress compiled cairo program
@@ -41,10 +41,10 @@ export function makeAddress(input: string): string {
41
41
  return addHexPrefix(input).toLowerCase();
42
42
  }
43
43
 
44
- export function formatSignature(sig?: [BigNumberish, BigNumberish]): [string, string] | [] {
44
+ export function formatSignature(sig?: Signature): string[] {
45
45
  if (!sig) return [];
46
46
  try {
47
- return sig.map((x) => toBN(x)).map((x) => x.toString()) as [string, string];
47
+ return sig.map((x) => toBN(x)).map((x) => x.toString());
48
48
  } catch (e) {
49
49
  return [];
50
50
  }