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.
- package/CHANGELOG.md +26 -0
- package/__mocks__/ArgentAccount.json +68548 -51944
- package/__mocks__/TestDapp.json +12962 -0
- package/__mocks__/contract.json +33191 -0
- package/__mocks__/multicall.json +8139 -0
- package/__tests__/account.test.ts +63 -49
- package/__tests__/accountContract.test.ts +51 -70
- package/__tests__/contract.test.ts +182 -35
- package/__tests__/fixtures.ts +13 -0
- package/__tests__/provider.test.ts +4 -14
- package/__tests__/utils/__snapshots__/utils.browser.test.ts.snap +2 -2
- package/__tests__/utils/__snapshots__/utils.test.ts.snap +2 -2
- package/__tests__/utils/ellipticalCurve.test.ts +20 -13
- package/__tests__/utils/utils.test.ts +3 -3
- package/account/default.d.ts +4 -4
- package/account/default.js +43 -92
- package/account/interface.d.ts +2 -2
- package/contract.d.ts +68 -9
- package/contract.js +229 -77
- package/dist/account/default.d.ts +3 -3
- package/dist/account/default.js +31 -57
- package/dist/account/interface.d.ts +2 -2
- package/dist/contract.d.ts +68 -6
- package/dist/contract.js +207 -55
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/provider/default.d.ts +15 -2
- package/dist/provider/default.js +61 -17
- package/dist/provider/interface.d.ts +5 -1
- package/dist/signer/default.d.ts +1 -1
- package/dist/signer/default.js +6 -18
- package/dist/signer/interface.d.ts +3 -2
- package/dist/types/api.d.ts +12 -0
- package/dist/types/lib.d.ts +3 -3
- package/dist/utils/ellipticCurve.js +1 -1
- package/dist/utils/hash.d.ts +12 -2
- package/dist/utils/hash.js +37 -9
- package/dist/utils/number.d.ts +1 -0
- package/dist/utils/number.js +28 -2
- package/dist/utils/stark.d.ts +2 -9
- package/dist/utils/stark.js +44 -14
- package/dist/utils/transaction.d.ts +19 -0
- package/dist/utils/transaction.js +75 -0
- package/dist/utils/typedData/index.d.ts +1 -1
- package/dist/utils/typedData/index.js +2 -3
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/package.json +2 -2
- package/provider/default.d.ts +20 -1
- package/provider/default.js +83 -19
- package/provider/interface.d.ts +5 -1
- package/signer/default.d.ts +1 -1
- package/signer/default.js +10 -44
- package/signer/interface.d.ts +3 -2
- package/src/account/default.ts +21 -43
- package/src/account/interface.ts +2 -2
- package/src/contract.ts +232 -62
- package/src/index.ts +1 -0
- package/src/provider/default.ts +58 -22
- package/src/provider/interface.ts +6 -1
- package/src/signer/default.ts +10 -26
- package/src/signer/interface.ts +3 -2
- package/src/types/api.ts +11 -0
- package/src/types/lib.ts +3 -4
- package/src/utils/ellipticCurve.ts +1 -1
- package/src/utils/hash.ts +39 -12
- package/src/utils/number.ts +8 -1
- package/src/utils/stark.ts +14 -15
- package/src/utils/transaction.ts +50 -0
- package/src/utils/typedData/index.ts +2 -3
- package/types/api.d.ts +15 -0
- package/types/lib.d.ts +3 -3
- package/utils/ellipticCurve.js +1 -1
- package/utils/hash.d.ts +15 -6
- package/utils/hash.js +42 -10
- package/utils/number.d.ts +1 -0
- package/utils/number.js +46 -1
- package/utils/stark.d.ts +2 -9
- package/utils/stark.js +64 -15
- package/utils/transaction.d.ts +19 -0
- package/utils/transaction.js +99 -0
- package/utils/typedData/index.d.ts +1 -1
- 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,
|
|
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]:
|
|
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(
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
|
105
|
-
(
|
|
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
|
-
|
|
108
|
-
`arg ${input.name}
|
|
118
|
+
arg.length === tupleLength,
|
|
119
|
+
`arg ${input.name} should have ${tupleLength} elements in tuple`
|
|
109
120
|
);
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
|
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
|
|
307
|
+
const { outputs } = this.abi.find((abi) => abi.name === method) as FunctionAbi;
|
|
144
308
|
const responseIterator = response.flat()[Symbol.iterator]();
|
|
145
|
-
return
|
|
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
package/src/provider/default.ts
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
|
319
|
-
* @param
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
}
|
package/src/signer/default.ts
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { Abi, Invocation, InvocationsSignerDetails, KeyPair, Signature } from '../types';
|
|
2
2
|
import { getStarkKey, sign } from '../utils/ellipticCurve';
|
|
3
|
-
import {
|
|
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
|
|
21
|
+
abis?: Abi[]
|
|
25
22
|
): Promise<Signature> {
|
|
26
|
-
if (
|
|
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
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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,
|
|
55
|
-
const msgHash = getMessageHash(typedData,
|
|
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
|
}
|
package/src/signer/interface.ts
CHANGED
|
@@ -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
|
|
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,
|
|
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 =
|
|
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
|
|
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[][] {
|