starknet 3.0.0 → 3.3.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.
Files changed (83) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/__mocks__/ArgentAccount.json +68548 -51944
  3. package/__mocks__/TestDapp.json +12962 -0
  4. package/__mocks__/contract.json +33191 -0
  5. package/__mocks__/multicall.json +8139 -0
  6. package/__tests__/account.test.ts +63 -49
  7. package/__tests__/accountContract.test.ts +51 -70
  8. package/__tests__/contract.test.ts +182 -35
  9. package/__tests__/fixtures.ts +13 -0
  10. package/__tests__/provider.test.ts +4 -14
  11. package/__tests__/utils/__snapshots__/utils.browser.test.ts.snap +2 -2
  12. package/__tests__/utils/__snapshots__/utils.test.ts.snap +2 -2
  13. package/__tests__/utils/ellipticalCurve.test.ts +20 -13
  14. package/__tests__/utils/utils.test.ts +3 -3
  15. package/account/default.d.ts +4 -4
  16. package/account/default.js +43 -92
  17. package/account/interface.d.ts +2 -2
  18. package/contract.d.ts +68 -9
  19. package/contract.js +229 -77
  20. package/dist/account/default.d.ts +3 -3
  21. package/dist/account/default.js +31 -57
  22. package/dist/account/interface.d.ts +2 -2
  23. package/dist/contract.d.ts +68 -6
  24. package/dist/contract.js +207 -55
  25. package/dist/index.d.ts +1 -0
  26. package/dist/index.js +1 -0
  27. package/dist/provider/default.d.ts +15 -2
  28. package/dist/provider/default.js +61 -17
  29. package/dist/provider/interface.d.ts +5 -1
  30. package/dist/signer/default.d.ts +1 -1
  31. package/dist/signer/default.js +6 -18
  32. package/dist/signer/interface.d.ts +3 -2
  33. package/dist/types/api.d.ts +12 -0
  34. package/dist/types/lib.d.ts +3 -3
  35. package/dist/utils/ellipticCurve.js +1 -1
  36. package/dist/utils/hash.d.ts +12 -2
  37. package/dist/utils/hash.js +37 -9
  38. package/dist/utils/number.d.ts +1 -0
  39. package/dist/utils/number.js +28 -2
  40. package/dist/utils/stark.d.ts +2 -9
  41. package/dist/utils/stark.js +44 -14
  42. package/dist/utils/transaction.d.ts +19 -0
  43. package/dist/utils/transaction.js +75 -0
  44. package/dist/utils/typedData/index.d.ts +1 -1
  45. package/dist/utils/typedData/index.js +2 -3
  46. package/index.d.ts +1 -0
  47. package/index.js +1 -0
  48. package/package.json +2 -2
  49. package/provider/default.d.ts +20 -1
  50. package/provider/default.js +83 -19
  51. package/provider/interface.d.ts +5 -1
  52. package/signer/default.d.ts +1 -1
  53. package/signer/default.js +10 -44
  54. package/signer/interface.d.ts +3 -2
  55. package/src/account/default.ts +21 -43
  56. package/src/account/interface.ts +2 -2
  57. package/src/contract.ts +232 -62
  58. package/src/index.ts +1 -0
  59. package/src/provider/default.ts +58 -22
  60. package/src/provider/interface.ts +6 -1
  61. package/src/signer/default.ts +10 -26
  62. package/src/signer/interface.ts +3 -2
  63. package/src/types/api.ts +11 -0
  64. package/src/types/lib.ts +3 -4
  65. package/src/utils/ellipticCurve.ts +1 -1
  66. package/src/utils/hash.ts +39 -12
  67. package/src/utils/number.ts +8 -1
  68. package/src/utils/stark.ts +14 -15
  69. package/src/utils/transaction.ts +50 -0
  70. package/src/utils/typedData/index.ts +2 -3
  71. package/types/api.d.ts +15 -0
  72. package/types/lib.d.ts +3 -3
  73. package/utils/ellipticCurve.js +1 -1
  74. package/utils/hash.d.ts +15 -6
  75. package/utils/hash.js +42 -10
  76. package/utils/number.d.ts +1 -0
  77. package/utils/number.js +46 -1
  78. package/utils/stark.d.ts +2 -9
  79. package/utils/stark.js +64 -15
  80. package/utils/transaction.d.ts +19 -0
  81. package/utils/transaction.js +99 -0
  82. package/utils/typedData/index.d.ts +1 -1
  83. package/utils/typedData/index.js +2 -3
package/src/contract.ts CHANGED
@@ -3,11 +3,18 @@ import assert from 'minimalistic-assert';
3
3
 
4
4
  import { Provider, defaultProvider } from './provider';
5
5
  import { BlockIdentifier } from './provider/utils';
6
- import { Abi, AbiEntry, FunctionAbi, RawCalldata, Signature, StructAbi } from './types';
7
- import { BigNumberish, toBN } from './utils/number';
6
+ import { Abi, AbiEntry, Calldata, FunctionAbi, Signature, StructAbi } from './types';
7
+ import { BigNumberish, toBN, toFelt } from './utils/number';
8
8
 
9
+ export type Struct = {
10
+ type: 'struct';
11
+ [k: string]: BigNumberish;
12
+ };
13
+ export type ParsedStruct = {
14
+ [key: string]: BigNumberish | ParsedStruct;
15
+ };
9
16
  export type Args = {
10
- [inputName: string]: string | string[] | { type: 'struct'; [k: string]: BigNumberish };
17
+ [inputName: string]: BigNumberish | BigNumberish[] | ParsedStruct | ParsedStruct[];
11
18
  };
12
19
 
13
20
  function parseFelt(candidate: string): BN {
@@ -18,27 +25,6 @@ function parseFelt(candidate: string): BN {
18
25
  }
19
26
  }
20
27
 
21
- function isFelt(candidate: string): boolean {
22
- try {
23
- parseFelt(candidate);
24
- return true;
25
- } catch (e) {
26
- return false;
27
- }
28
- }
29
-
30
- export function compileCalldata(args: Args): RawCalldata {
31
- return Object.values(args).flatMap((value) => {
32
- if (Array.isArray(value))
33
- return [toBN(value.length).toString(), ...value.map((x) => toBN(x).toString())];
34
- if (typeof value === 'object' && 'type' in value)
35
- return Object.entries(value)
36
- .filter(([k]) => k !== 'type')
37
- .map(([, v]) => toBN(v).toString());
38
- return toBN(value).toString();
39
- });
40
- }
41
-
42
28
  export class Contract {
43
29
  connectedTo: string | null = null;
44
30
 
@@ -69,11 +55,24 @@ export class Contract {
69
55
  );
70
56
  }
71
57
 
58
+ /**
59
+ * Saves the address of the contract deployed on network that will be used for interaction
60
+ *
61
+ * @param address - address of the contract
62
+ * @returns Contract
63
+ */
72
64
  public connect(address: string): Contract {
73
65
  this.connectedTo = address;
74
66
  return this;
75
67
  }
76
68
 
69
+ /**
70
+ * Validates if all arguments that are passed to the method are corresponding to the ones in the abi
71
+ *
72
+ * @param type - type of the method
73
+ * @param method - name of the method
74
+ * @param args - arguments that are passed to the method
75
+ */
77
76
  private validateMethodAndArgs(type: 'INVOKE' | 'CALL', method: string, args: Args = {}) {
78
77
  // ensure provided method exists
79
78
  const invokeableFunctionNames = this.abi
@@ -96,64 +95,235 @@ export class Contract {
96
95
  const arg = args[input.name];
97
96
  if (arg !== undefined) {
98
97
  if (input.type === 'felt') {
99
- assert(typeof arg === 'string', `arg ${input.name} should be a felt (string)`);
100
- assert(isFelt(arg as string), `arg ${input.name} should be decimal or hexadecimal`);
101
- } else if (typeof arg === 'object' && 'type' in arg) {
102
- assert(arg.type === 'struct', `arg ${input.name} should be a struct`);
98
+ assert(
99
+ typeof arg === 'string' || typeof arg === 'number' || arg instanceof BN,
100
+ `arg ${input.name} should be a felt (string, number, BigNumber)`
101
+ );
102
+ } else if (typeof arg === 'object' && input.type in this.structs) {
103
+ this.structs[input.type].members.forEach(({ name }) => {
104
+ assert(Object.keys(arg).includes(name), `arg should have a property ${name}`);
105
+ });
103
106
  } else {
104
- assert(Array.isArray(arg), `arg ${input.name} should be a felt* (string[])`);
105
- (arg as string[]).forEach((felt, i) => {
107
+ assert(Array.isArray(arg), `arg ${input.name} should be an Array`);
108
+ if (input.type === 'felt*') {
109
+ arg.forEach((felt) => {
110
+ assert(
111
+ typeof felt === 'string' || typeof felt === 'number' || felt instanceof BN,
112
+ `arg ${input.name} should be an array of string, number or BigNumber`
113
+ );
114
+ });
115
+ } else if (/\(felt/.test(input.type)) {
116
+ const tupleLength = input.type.split(',').length;
106
117
  assert(
107
- typeof felt === 'string',
108
- `arg ${input.name}[${i}] should be a felt (string) as part of a felt* (string[])`
118
+ arg.length === tupleLength,
119
+ `arg ${input.name} should have ${tupleLength} elements in tuple`
109
120
  );
110
- assert(
111
- isFelt(felt),
112
- `arg ${input.name}[${i}] should be decimal or hexadecimal as part of a felt* (string[])`
113
- );
114
- });
121
+ arg.forEach((felt) => {
122
+ assert(
123
+ typeof felt === 'string' || typeof felt === 'number' || felt instanceof BN,
124
+ `arg ${input.name} should be an array of string, number or BigNumber`
125
+ );
126
+ });
127
+ } else {
128
+ const arrayType = input.type.replace('*', '');
129
+ arg.forEach((struct) => {
130
+ this.structs[arrayType].members.forEach(({ name }) => {
131
+ assert(
132
+ Object.keys(struct).includes(name),
133
+ `arg ${input.name} should be an array of ${arrayType}`
134
+ );
135
+ });
136
+ });
137
+ }
115
138
  }
116
139
  }
117
140
  });
118
141
  }
119
142
 
120
- private parseResponseField(
121
- element: AbiEntry | FunctionAbi,
122
- responseIterator: Iterator<string>
123
- ): Args {
124
- let entries: AbiEntry[] = [];
125
- if (['felt', 'felt*'].includes(element.type)) {
126
- return responseIterator.next().value;
143
+ /**
144
+ * Deep parse of the object that has been passed to the method
145
+ *
146
+ * @param element - element that needs to be parsed
147
+ * @param type - name of the method
148
+ * @return {string | string[]} - parsed arguments in format that contract is expecting
149
+ */
150
+ private parseCalldataObject(
151
+ element: ParsedStruct | BigNumberish,
152
+ type: string
153
+ ): string | string[] {
154
+ if (element === undefined) {
155
+ throw Error('Missing element in calldata object');
156
+ }
157
+ // checking if the passed element is struct or element in struct
158
+ if (this.structs[type] && this.structs[type].members.length) {
159
+ // going through all the members of the struct and parsing the value
160
+ return this.structs[type].members.reduce((acc, member: AbiEntry) => {
161
+ // if the member of the struct is another struct this will return array of the felts if not it will be single felt
162
+ // TODO: refactor types so member name can be used as keyof ParsedStruct
163
+ /* @ts-ignore */
164
+ const parsedData = this.parseCalldataObject(element[member.name], member.type);
165
+ if (typeof parsedData === 'string') {
166
+ acc.push(parsedData);
167
+ } else {
168
+ acc.push(...parsedData);
169
+ }
170
+ return acc;
171
+ }, [] as string[]);
127
172
  }
128
- if (element.type in this.structs) {
129
- entries = this.structs[element.type].members;
130
- } else if ('outputs' in element) {
131
- entries = element.outputs;
173
+ return toFelt(element as BigNumberish);
174
+ }
175
+
176
+ /**
177
+ * Parse of the response elements that are converted to Object (Struct) by using the abi
178
+ *
179
+ * @param responseIterator - iterator of the response
180
+ * @param type - type of the struct
181
+ * @return {BigNumberish | ParsedStruct} - parsed arguments in format that contract is expecting
182
+ */
183
+ private parseResponseStruct(
184
+ responseIterator: Iterator<string>,
185
+ type: string
186
+ ): BigNumberish | ParsedStruct {
187
+ // check the type of current element
188
+ if (type in this.structs && this.structs[type]) {
189
+ return this.structs[type].members.reduce((acc, el) => {
190
+ // parse each member of the struct (member can felt or nested struct)
191
+ acc[el.name] = this.parseResponseStruct(responseIterator, el.type);
192
+ return acc;
193
+ }, {} as any);
132
194
  }
133
- return entries.reduce(
134
- (acc, member) => ({
135
- ...acc,
136
- [member.name]: this.parseResponseField(member, responseIterator),
137
- }),
138
- {}
139
- );
195
+ return parseFelt(responseIterator.next().value);
140
196
  }
141
197
 
198
+ /**
199
+ * Parse one field of the calldata by using input field from the abi for that method
200
+ *
201
+ * @param args - value of the field
202
+ * @param input - input(field) information from the abi that will be used to parse the data
203
+ * @return {string | string[]} - parsed arguments in format that contract is expecting
204
+ */
205
+ private parsCalldataField(args: Args, input: AbiEntry): string | string[] {
206
+ const { name, type } = input;
207
+ const value = args[name];
208
+ const propName = name.replace(/_len$/, '');
209
+ switch (true) {
210
+ case /_len$/.test(name):
211
+ if (Array.isArray(args[propName])) {
212
+ const arr = args[propName] as (BigNumberish | ParsedStruct)[];
213
+ return toFelt(arr.length);
214
+ }
215
+ throw Error(`Expected ${propName} to be array`);
216
+ case /\*/.test(type):
217
+ if (Array.isArray(value)) {
218
+ return (value as (BigNumberish | ParsedStruct)[]).reduce((acc, el) => {
219
+ if (/felt/.test(type)) {
220
+ acc.push(toFelt(el as BigNumberish));
221
+ } else {
222
+ acc.push(...this.parseCalldataObject(el, type.replace('*', '')));
223
+ }
224
+ return acc;
225
+ }, [] as string[]);
226
+ }
227
+ throw Error(`Expected ${name} to be array`);
228
+ case type in this.structs:
229
+ return this.parseCalldataObject(value as ParsedStruct, type);
230
+ case /\(felt/.test(type):
231
+ if (Array.isArray(value)) {
232
+ return value.map((el) => toFelt(el as BigNumberish));
233
+ }
234
+ throw Error(`Expected ${name} to be array`);
235
+ default:
236
+ return toFelt(value as BigNumberish);
237
+ }
238
+ }
239
+
240
+ /**
241
+ * Parse the calldata by using input fields from the abi for that method
242
+ *
243
+ * @param args - arguments passed the the method
244
+ * @param inputs - list of inputs(fields) that are in the abi
245
+ * @return {Calldata} - parsed arguments in format that contract is expecting
246
+ */
247
+ private compileCalldata(args: Args, inputs: AbiEntry[]): Calldata {
248
+ return inputs.reduce((acc, input) => {
249
+ const parsedData = this.parsCalldataField(args, input);
250
+ if (Array.isArray(parsedData)) {
251
+ acc.push(...parsedData);
252
+ } else {
253
+ acc.push(parsedData);
254
+ }
255
+ return acc;
256
+ }, [] as Calldata);
257
+ }
258
+
259
+ /**
260
+ * Parse elements of the response and structuring them into one field by using output property from the abi for that method
261
+ *
262
+ * @param responseIterator - iterator of the response
263
+ * @param output - output(field) information from the abi that will be used to parse the data
264
+ * @return - parsed response corresponding to the abi structure of the field
265
+ */
266
+ private parseResponseField(
267
+ responseIterator: Iterator<string>,
268
+ output: AbiEntry,
269
+ parsedResult?: Args
270
+ ): any {
271
+ const { name, type } = output;
272
+ let arrLen: number;
273
+ const parsedDataArr: (BigNumberish | ParsedStruct)[] = [];
274
+ switch (true) {
275
+ case /_len$/.test(name):
276
+ return parseFelt(responseIterator.next().value).toNumber();
277
+ case /\(felt/.test(type):
278
+ return type.split(',').reduce((acc) => {
279
+ acc.push(parseFelt(responseIterator.next().value));
280
+ return acc;
281
+ }, [] as BigNumberish[]);
282
+ case /\*/.test(type):
283
+ if (parsedResult && parsedResult[`${name}_len`]) {
284
+ arrLen = parsedResult[`${name}_len`] as number;
285
+ while (parsedDataArr.length < arrLen) {
286
+ parsedDataArr.push(
287
+ this.parseResponseStruct(responseIterator, output.type.replace('*', ''))
288
+ );
289
+ }
290
+ }
291
+ return parsedDataArr;
292
+ case type in this.structs:
293
+ return this.parseResponseStruct(responseIterator, type);
294
+ default:
295
+ return parseFelt(responseIterator.next().value);
296
+ }
297
+ }
298
+
299
+ /**
300
+ * Parse elements of the response array and structuring them into response object
301
+ *
302
+ * @param method - method name
303
+ * @param response - response from the method
304
+ * @return - parsed response corresponding to the abi
305
+ */
142
306
  private parseResponse(method: string, response: string[]): Args {
143
- const methodAbi = this.abi.find((abi) => abi.name === method) as FunctionAbi;
307
+ const { outputs } = this.abi.find((abi) => abi.name === method) as FunctionAbi;
144
308
  const responseIterator = response.flat()[Symbol.iterator]();
145
- return this.parseResponseField(methodAbi, responseIterator);
309
+ return outputs.flat().reduce((acc, output) => {
310
+ acc[output.name] = this.parseResponseField(responseIterator, output, acc);
311
+ if (acc[output.name] && acc[`${output.name}_len`]) {
312
+ delete acc[`${output.name}_len`];
313
+ }
314
+ return acc;
315
+ }, {} as Args);
146
316
  }
147
317
 
148
318
  public invoke(method: string, args: Args = {}, signature?: Signature) {
149
319
  // ensure contract is connected
150
320
  assert(this.connectedTo !== null, 'contract isnt connected to an address');
151
-
152
321
  // validate method and args
153
322
  this.validateMethodAndArgs('INVOKE', method, args);
323
+ const { inputs } = this.abi.find((abi) => abi.name === method) as FunctionAbi;
154
324
 
155
325
  // compile calldata
156
- const calldata = compileCalldata(args);
326
+ const calldata = this.compileCalldata(args, inputs);
157
327
 
158
328
  return this.provider.invokeFunction({
159
329
  contractAddress: this.connectedTo,
@@ -169,16 +339,16 @@ export class Contract {
169
339
 
170
340
  // validate method and args
171
341
  this.validateMethodAndArgs('CALL', method, args);
342
+ const { inputs } = this.abi.find((abi) => abi.name === method) as FunctionAbi;
172
343
 
173
344
  // compile calldata
174
- const calldata = compileCalldata(args);
175
-
345
+ const calldata = this.compileCalldata(args, inputs);
176
346
  return this.provider
177
347
  .callContract(
178
348
  {
179
349
  contractAddress: this.connectedTo,
180
- entrypoint: method,
181
350
  calldata,
351
+ entrypoint: method,
182
352
  },
183
353
  blockIdentifier
184
354
  )
package/src/index.ts CHANGED
@@ -5,6 +5,7 @@ export * from './types';
5
5
  export * from './contract';
6
6
  export * from './provider';
7
7
  export * from './account';
8
+ export * from './signer';
8
9
 
9
10
  /**
10
11
  * Utils
@@ -15,23 +15,19 @@ import {
15
15
  GetTransactionResponse,
16
16
  GetTransactionStatusResponse,
17
17
  Invocation,
18
+ Signature,
18
19
  TransactionReceipt,
19
20
  } from '../types';
21
+ import { getSelectorFromName } from '../utils/hash';
20
22
  import { parse, stringify } from '../utils/json';
21
23
  import { BigNumberish, bigNumberishArrayToDecimalStringArray, toBN, toHex } from '../utils/number';
22
- import { compressProgram, getSelectorFromName, randomAddress } from '../utils/stark';
24
+ import { compressProgram, randomAddress } from '../utils/stark';
23
25
  import { ProviderInterface } from './interface';
24
26
  import { BlockIdentifier, getFormattedBlockIdentifier, txIdentifier } from './utils';
25
27
 
26
28
  type NetworkName = 'mainnet-alpha' | 'goerli-alpha';
27
29
 
28
- type ProviderOptions =
29
- | {
30
- network: NetworkName;
31
- }
32
- | {
33
- baseUrl: string;
34
- };
30
+ type ProviderOptions = { network: NetworkName } | { baseUrl: string };
35
31
 
36
32
  function wait(delay: number) {
37
33
  return new Promise((res) => setTimeout(res, delay));
@@ -133,14 +129,21 @@ export class Provider implements ProviderInterface {
133
129
  const queryString = this.getQueryString(query);
134
130
  const headers = this.getHeaders(method);
135
131
 
136
- const { data } = await axios.request<Endpoints[T]['RESPONSE']>({
137
- method,
138
- url: urljoin(baseUrl, endpoint, queryString),
139
- data: stringify(request),
140
- headers,
141
- });
142
-
143
- return data;
132
+ try {
133
+ const { data } = await axios.request<Endpoints[T]['RESPONSE']>({
134
+ method,
135
+ url: urljoin(baseUrl, endpoint, queryString),
136
+ data: stringify(request),
137
+ headers,
138
+ });
139
+ return data;
140
+ } catch (error: any) {
141
+ const data = error?.response?.data;
142
+ if (data?.message) {
143
+ throw new Error(`${data.code}: ${data.message}`);
144
+ }
145
+ throw error;
146
+ }
144
147
  }
145
148
 
146
149
  /**
@@ -315,10 +318,8 @@ export class Provider implements ProviderInterface {
315
318
  * Invokes a function on starknet
316
319
  * @deprecated This method wont be supported as soon as fees are mandatory
317
320
  *
318
- * @param contractAddress - target contract address for invoke
319
- * @param entrypointSelector - target entrypoint selector for
320
- * @param calldata - (optional, default []) calldata
321
- * @param signature - (optional) signature to send along
321
+ * @param invocation
322
+ * @param _abi - (optional) signature to send along
322
323
  * @returns response from addTransaction
323
324
  */
324
325
  public invokeFunction(invocation: Invocation, _abi?: Abi): Promise<AddTransactionResponse> {
@@ -331,7 +332,32 @@ export class Provider implements ProviderInterface {
331
332
  });
332
333
  }
333
334
 
334
- public async waitForTx(txHash: BigNumberish, retryInterval: number = 8000) {
335
+ /**
336
+ * Invokes a function on starknet
337
+ * @deprecated This method wont be supported as soon as fees are mandatory
338
+ *
339
+ * @param contractAddress - target contract address for invoke
340
+ * @param entrypointSelector - target entrypoint selector for
341
+ * @param calldata - (optional, default []) calldata
342
+ * @param signature - (optional) signature to send along
343
+ * @returns response from addTransaction
344
+ */
345
+ public LEGACY_invokeFunction(
346
+ contractAddress: string,
347
+ entrypointSelector: string,
348
+ calldata?: string[],
349
+ signature?: Signature
350
+ ): Promise<AddTransactionResponse> {
351
+ return this.fetchEndpoint('add_transaction', undefined, {
352
+ type: 'INVOKE_FUNCTION',
353
+ contract_address: contractAddress,
354
+ entry_point_selector: entrypointSelector,
355
+ calldata: bigNumberishArrayToDecimalStringArray(calldata ?? []),
356
+ signature: bigNumberishArrayToDecimalStringArray(signature ?? []),
357
+ });
358
+ }
359
+
360
+ public async waitForTransaction(txHash: BigNumberish, retryInterval: number = 8000) {
335
361
  let onchain = false;
336
362
  await wait(retryInterval);
337
363
 
@@ -344,10 +370,20 @@ export class Provider implements ProviderInterface {
344
370
  if (res.tx_status === 'ACCEPTED_ON_L1' || res.tx_status === 'ACCEPTED_ON_L2') {
345
371
  onchain = true;
346
372
  } else if (res.tx_status === 'REJECTED' || res.tx_status === 'NOT_RECEIVED') {
347
- const error = Error(res.tx_status) as Error & { response: GetTransactionStatusResponse };
373
+ const message = res.tx_failure_reason
374
+ ? `${res.tx_status}: ${res.tx_failure_reason.code}\n${res.tx_failure_reason.error_message}`
375
+ : res.tx_status;
376
+ const error = new Error(message) as Error & { response: GetTransactionStatusResponse };
348
377
  error.response = res;
349
378
  throw error;
350
379
  }
351
380
  }
352
381
  }
382
+
383
+ /**
384
+ * @deprecated use `waitForTransaction` instead
385
+ */
386
+ public async waitForTx(txHash: BigNumberish, retryInterval: number = 8000) {
387
+ return this.waitForTransaction(txHash, retryInterval);
388
+ }
353
389
  }
@@ -137,5 +137,10 @@ export abstract class ProviderInterface {
137
137
  */
138
138
  public abstract invokeFunction(invocation: Invocation): Promise<AddTransactionResponse>;
139
139
 
140
- public abstract waitForTx(txHash: BigNumberish, retryInterval?: number): Promise<void>;
140
+ public abstract waitForTransaction(txHash: BigNumberish, retryInterval?: number): Promise<void>;
141
+
142
+ /**
143
+ * @deprecated use `waitForTransaction` instead
144
+ */
145
+ public abstract waitForTransaction(txHash: BigNumberish, retryInterval?: number): Promise<void>;
141
146
  }
@@ -1,9 +1,6 @@
1
1
  import { Abi, Invocation, InvocationsSignerDetails, KeyPair, Signature } from '../types';
2
2
  import { getStarkKey, sign } from '../utils/ellipticCurve';
3
- import { addHexPrefix } from '../utils/encode';
4
- import { hashMessage } from '../utils/hash';
5
- import { bigNumberishArrayToDecimalStringArray, toBN } from '../utils/number';
6
- import { getSelectorFromName } from '../utils/stark';
3
+ import { hashMulticall } from '../utils/hash';
7
4
  import { TypedData, getMessageHash } from '../utils/typedData';
8
5
  import { SignerInterface } from './interface';
9
6
 
@@ -21,38 +18,25 @@ export class Signer implements SignerInterface {
21
18
  public async signTransaction(
22
19
  transactions: Invocation[],
23
20
  transactionsDetail: InvocationsSignerDetails,
24
- abis: Abi[] = []
21
+ abis?: Abi[]
25
22
  ): Promise<Signature> {
26
- if (transactions.length !== 1) {
27
- throw new Error('Only one transaction at a time is currently supported by this signer');
28
- }
29
- if (abis?.length !== 0 && abis.length !== transactions.length) {
23
+ if (abis && abis.length !== transactions.length) {
30
24
  throw new Error('ABI must be provided for each transaction or no transaction');
31
25
  }
32
26
  // now use abi to display decoded data somewhere, but as this signer is headless, we can't do that
33
27
 
34
- const { contractAddress, entrypoint, calldata = [] } = transactions[0];
35
- const { nonce, walletAddress } = transactionsDetail;
36
-
37
- const nonceBn = toBN(nonce);
38
- const entrypointSelector = getSelectorFromName(entrypoint);
39
- const calldataDecimal = bigNumberishArrayToDecimalStringArray(calldata);
40
-
41
- const msgHash = addHexPrefix(
42
- hashMessage(
43
- walletAddress,
44
- contractAddress,
45
- entrypointSelector,
46
- calldataDecimal,
47
- nonceBn.toString()
48
- )
28
+ const msgHash = hashMulticall(
29
+ transactionsDetail.walletAddress,
30
+ transactions,
31
+ transactionsDetail.nonce.toString(),
32
+ transactionsDetail.maxFee.toString()
49
33
  );
50
34
 
51
35
  return sign(this.keyPair, msgHash);
52
36
  }
53
37
 
54
- public async signMessage(typedData: TypedData, walletAddress: string): Promise<Signature> {
55
- const msgHash = getMessageHash(typedData, walletAddress);
38
+ public async signMessage(typedData: TypedData, accountAddress: string): Promise<Signature> {
39
+ const msgHash = getMessageHash(typedData, accountAddress);
56
40
  return sign(this.keyPair, msgHash);
57
41
  }
58
42
  }
@@ -13,11 +13,12 @@ export abstract class SignerInterface {
13
13
  * Sign an JSON object for off-chain usage with the starknet private key and return the signature
14
14
  * This adds a message prefix so it cant be interchanged with transactions
15
15
  *
16
- * @param json - JSON object to be signed
16
+ * @param typedData - JSON object to be signed
17
+ * @param accountAddress - account
17
18
  * @returns the signature of the JSON object
18
19
  * @throws {Error} if the JSON object is not a valid JSON
19
20
  */
20
- public abstract signMessage(typedData: TypedData, walletAddress: string): Promise<Signature>;
21
+ public abstract signMessage(typedData: TypedData, accountAddress: string): Promise<Signature>;
21
22
 
22
23
  /**
23
24
  * Signs a transaction with the starknet private key and returns the signature
package/src/types/api.ts CHANGED
@@ -137,6 +137,11 @@ export type GetCodeResponse = {
137
137
  export type GetTransactionStatusResponse = {
138
138
  tx_status: Status;
139
139
  block_hash: string;
140
+ tx_failure_reason?: {
141
+ tx_id: number;
142
+ code: string;
143
+ error_message: string;
144
+ };
140
145
  };
141
146
 
142
147
  export type GetTransactionResponse = {
@@ -163,3 +168,9 @@ export type TransactionReceipt = {
163
168
  l2_to_l1_messages: string[];
164
169
  events: string[];
165
170
  };
171
+
172
+ export type RawArgs = {
173
+ [inputName: string]: string | string[] | { type: 'struct'; [k: string]: BigNumberish };
174
+ };
175
+
176
+ export type Calldata = string[];
package/src/types/lib.ts CHANGED
@@ -3,7 +3,7 @@ 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 = BigNumberish[];
6
+ export type Signature = string[];
7
7
  export type RawCalldata = BigNumberish[];
8
8
 
9
9
  export type DeployContractPayload = {
@@ -19,14 +19,13 @@ export type Invocation = {
19
19
  signature?: Signature;
20
20
  };
21
21
 
22
- export type ExecuteInvocation = Omit<Invocation, 'signature'>;
22
+ export type Call = Omit<Invocation, 'signature'>;
23
23
 
24
24
  export type InvocationsDetails = {
25
25
  nonce?: BigNumberish;
26
+ maxFee?: BigNumberish;
26
27
  };
27
28
 
28
- export type Call = Omit<Invocation, 'signature' | 'nonce'>;
29
-
30
29
  export type Status =
31
30
  | 'NOT_RECEIVED'
32
31
  | 'RECEIVED'
@@ -80,7 +80,7 @@ export function sign(keyPair: KeyPair, msgHash: string): Signature {
80
80
  assertInRange(r, ONE, toBN(addHexPrefix(MAX_ECDSA_VAL)), 'r');
81
81
  assertInRange(s, ONE, toBN(addHexPrefix(EC_ORDER)), 's');
82
82
  assertInRange(w, ONE, toBN(addHexPrefix(MAX_ECDSA_VAL)), 'w');
83
- return [r, s];
83
+ return [r.toString(), s.toString()];
84
84
  }
85
85
 
86
86
  function chunkArray(arr: any[], n: number): any[][] {