starknet 2.3.0 → 2.6.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 +42 -0
- package/__tests__/account.test.ts +3 -3
- package/__tests__/provider.test.ts +3 -3
- package/__tests__/signer.test.ts +17 -0
- package/__tests__/utils/ellipticalCurve.test.ts +1 -1
- package/__tests__/utils/typedData.test.ts +72 -0
- package/contract.d.ts +9 -3
- package/contract.js +23 -5
- package/dist/contract.d.ts +6 -3
- package/dist/contract.js +19 -5
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -1
- package/dist/provider/default.d.ts +17 -17
- package/dist/provider/default.js +37 -32
- package/dist/provider/interface.d.ts +16 -16
- package/dist/signer/default.d.ts +20 -3
- package/dist/signer/default.js +61 -20
- package/dist/signer/interface.d.ts +22 -3
- package/dist/types.d.ts +10 -7
- package/dist/utils/ellipticCurve.d.ts +8 -1
- package/dist/utils/ellipticCurve.js +48 -9
- package/dist/utils/stark.d.ts +2 -6
- package/dist/utils/stark.js +1 -5
- package/dist/utils/typedData/index.d.ts +91 -0
- package/dist/utils/typedData/index.js +183 -0
- package/dist/utils/typedData/types.d.ts +82 -0
- package/dist/utils/typedData/types.js +47 -0
- package/dist/utils/typedData/utils.d.ts +24 -0
- package/dist/utils/typedData/utils.js +15 -0
- package/index.d.ts +1 -0
- package/index.js +3 -1
- package/package.json +3 -1
- package/provider/default.d.ts +21 -16
- package/provider/default.js +50 -39
- package/provider/interface.d.ts +22 -16
- package/signer/default.d.ts +20 -3
- package/signer/default.js +60 -17
- package/signer/interface.d.ts +22 -3
- package/src/contract.ts +17 -14
- package/src/index.ts +1 -0
- package/src/provider/default.ts +37 -31
- package/src/provider/interface.ts +21 -16
- package/src/signer/default.ts +49 -17
- package/src/signer/interface.ts +26 -3
- package/src/types.ts +16 -7
- package/src/utils/ellipticCurve.ts +31 -9
- package/src/utils/stark.ts +4 -8
- package/src/utils/typedData/index.ts +176 -0
- package/src/utils/typedData/types.ts +82 -0
- package/src/utils/typedData/utils.ts +13 -0
- package/types.d.ts +12 -8
- package/utils/ellipticCurve.d.ts +12 -1
- package/utils/ellipticCurve.js +72 -23
- package/utils/stark.d.ts +2 -8
- package/utils/stark.js +1 -6
- package/utils/typedData/index.d.ts +113 -0
- package/utils/typedData/index.js +247 -0
- package/utils/typedData/types.d.ts +103 -0
- package/utils/typedData/types.js +57 -0
- package/utils/typedData/utils.d.ts +27 -0
- package/utils/typedData/utils.js +15 -0
package/src/provider/default.ts
CHANGED
|
@@ -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,6 +12,7 @@ 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';
|
|
@@ -18,7 +20,7 @@ import { BigNumberish, toBN, toHex } from '../utils/number';
|
|
|
18
20
|
import { compressProgram, formatSignature, randomAddress } from '../utils/stark';
|
|
19
21
|
import { ProviderInterface } from './interface';
|
|
20
22
|
|
|
21
|
-
type NetworkName = 'mainnet-alpha' | '
|
|
23
|
+
type NetworkName = 'mainnet-alpha' | 'goerli-alpha';
|
|
22
24
|
|
|
23
25
|
type ProviderOptions =
|
|
24
26
|
| {
|
|
@@ -39,7 +41,7 @@ export class Provider implements ProviderInterface {
|
|
|
39
41
|
|
|
40
42
|
public gatewayUrl: string;
|
|
41
43
|
|
|
42
|
-
constructor(optionsOrProvider: ProviderOptions | Provider = { network: '
|
|
44
|
+
constructor(optionsOrProvider: ProviderOptions | Provider = { network: 'goerli-alpha' }) {
|
|
43
45
|
if (optionsOrProvider instanceof Provider) {
|
|
44
46
|
this.baseUrl = optionsOrProvider.baseUrl;
|
|
45
47
|
this.feederGatewayUrl = optionsOrProvider.feederGatewayUrl;
|
|
@@ -59,7 +61,7 @@ export class Provider implements ProviderInterface {
|
|
|
59
61
|
switch (name) {
|
|
60
62
|
case 'mainnet-alpha':
|
|
61
63
|
return 'https://alpha-mainnet.starknet.io';
|
|
62
|
-
case '
|
|
64
|
+
case 'goerli-alpha':
|
|
63
65
|
default:
|
|
64
66
|
return 'https://alpha4.starknet.io';
|
|
65
67
|
}
|
|
@@ -83,20 +85,20 @@ export class Provider implements ProviderInterface {
|
|
|
83
85
|
*
|
|
84
86
|
* [Reference](https://github.com/starkware-libs/cairo-lang/blob/f464ec4797361b6be8989e36e02ec690e74ef285/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L17-L25)
|
|
85
87
|
*
|
|
86
|
-
* @param
|
|
87
|
-
* @param
|
|
88
|
+
* @param invokeTransaction - transaction to be invoked
|
|
89
|
+
* @param blockNumber
|
|
88
90
|
* @returns the result of the function on the smart contract.
|
|
89
91
|
*/
|
|
90
92
|
public async callContract(
|
|
91
|
-
|
|
92
|
-
|
|
93
|
+
invokeTransaction: CallContractTransaction,
|
|
94
|
+
blockNumber: BlockNumber = null
|
|
93
95
|
): Promise<CallContractResponse> {
|
|
94
96
|
const { data } = await axios.post<CallContractResponse>(
|
|
95
|
-
urljoin(this.feederGatewayUrl, 'call_contract', `?
|
|
97
|
+
urljoin(this.feederGatewayUrl, 'call_contract', `?blockNumber=${blockNumber}`),
|
|
96
98
|
{
|
|
97
99
|
signature: [],
|
|
98
100
|
calldata: [],
|
|
99
|
-
...
|
|
101
|
+
...invokeTransaction,
|
|
100
102
|
}
|
|
101
103
|
);
|
|
102
104
|
return data;
|
|
@@ -107,12 +109,12 @@ export class Provider implements ProviderInterface {
|
|
|
107
109
|
*
|
|
108
110
|
* [Reference](https://github.com/starkware-libs/cairo-lang/blob/f464ec4797361b6be8989e36e02ec690e74ef285/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L27-L31)
|
|
109
111
|
*
|
|
110
|
-
* @param
|
|
111
|
-
* @returns the block object {
|
|
112
|
+
* @param blockNumber
|
|
113
|
+
* @returns the block object { block_number, previous_block_number, state_root, status, timestamp, transaction_receipts, transactions }
|
|
112
114
|
*/
|
|
113
|
-
public async getBlock(
|
|
115
|
+
public async getBlock(blockNumber: BlockNumber = null): Promise<GetBlockResponse> {
|
|
114
116
|
const { data } = await axios.get<GetBlockResponse>(
|
|
115
|
-
urljoin(this.feederGatewayUrl, 'get_block', `?
|
|
117
|
+
urljoin(this.feederGatewayUrl, 'get_block', `?blockNumber=${blockNumber}`)
|
|
116
118
|
);
|
|
117
119
|
return data;
|
|
118
120
|
}
|
|
@@ -123,15 +125,18 @@ export class Provider implements ProviderInterface {
|
|
|
123
125
|
* [Reference](https://github.com/starkware-libs/cairo-lang/blob/f464ec4797361b6be8989e36e02ec690e74ef285/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L33-L36)
|
|
124
126
|
*
|
|
125
127
|
* @param contractAddress
|
|
126
|
-
* @param
|
|
128
|
+
* @param blockNumber
|
|
127
129
|
* @returns Bytecode and ABI of compiled contract
|
|
128
130
|
*/
|
|
129
|
-
public async getCode(
|
|
131
|
+
public async getCode(
|
|
132
|
+
contractAddress: string,
|
|
133
|
+
blockNumber: BlockNumber = null
|
|
134
|
+
): Promise<GetCodeResponse> {
|
|
130
135
|
const { data } = await axios.get<GetCodeResponse>(
|
|
131
136
|
urljoin(
|
|
132
137
|
this.feederGatewayUrl,
|
|
133
138
|
'get_code',
|
|
134
|
-
`?contractAddress=${contractAddress}&
|
|
139
|
+
`?contractAddress=${contractAddress}&blockNumber=${blockNumber}`
|
|
135
140
|
)
|
|
136
141
|
);
|
|
137
142
|
return data;
|
|
@@ -145,19 +150,19 @@ export class Provider implements ProviderInterface {
|
|
|
145
150
|
*
|
|
146
151
|
* @param contractAddress
|
|
147
152
|
* @param key - from getStorageVarAddress('<STORAGE_VARIABLE_NAME>') (WIP)
|
|
148
|
-
* @param
|
|
153
|
+
* @param blockNumber
|
|
149
154
|
* @returns the value of the storage variable
|
|
150
155
|
*/
|
|
151
156
|
public async getStorageAt(
|
|
152
157
|
contractAddress: string,
|
|
153
158
|
key: number,
|
|
154
|
-
|
|
159
|
+
blockNumber: BlockNumber = null
|
|
155
160
|
): Promise<object> {
|
|
156
161
|
const { data } = await axios.get<object>(
|
|
157
162
|
urljoin(
|
|
158
163
|
this.feederGatewayUrl,
|
|
159
164
|
'get_storage_at',
|
|
160
|
-
`?contractAddress=${contractAddress}&key=${key}&
|
|
165
|
+
`?contractAddress=${contractAddress}&key=${key}&blockNumber=${blockNumber}`
|
|
161
166
|
)
|
|
162
167
|
);
|
|
163
168
|
return data;
|
|
@@ -169,7 +174,7 @@ export class Provider implements ProviderInterface {
|
|
|
169
174
|
* [Reference](https://github.com/starkware-libs/cairo-lang/blob/f464ec4797361b6be8989e36e02ec690e74ef285/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L48-L52)
|
|
170
175
|
*
|
|
171
176
|
* @param txHash
|
|
172
|
-
* @returns the transaction status object {
|
|
177
|
+
* @returns the transaction status object { block_number, tx_status: NOT_RECEIVED | RECEIVED | PENDING | REJECTED | ACCEPTED_ONCHAIN }
|
|
173
178
|
*/
|
|
174
179
|
public async getTransactionStatus(txHash: BigNumberish): Promise<GetTransactionStatusResponse> {
|
|
175
180
|
const txHashBn = toBN(txHash);
|
|
@@ -189,7 +194,7 @@ export class Provider implements ProviderInterface {
|
|
|
189
194
|
* [Reference](https://github.com/starkware-libs/cairo-lang/blob/f464ec4797361b6be8989e36e02ec690e74ef285/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L54-L58)
|
|
190
195
|
*
|
|
191
196
|
* @param txHash
|
|
192
|
-
* @returns the transacton object { transaction_id, status, transaction,
|
|
197
|
+
* @returns the transacton object { transaction_id, status, transaction, block_number?, block_number?, transaction_index?, transaction_failure_reason? }
|
|
193
198
|
*/
|
|
194
199
|
public async getTransaction(txHash: BigNumberish): Promise<GetTransactionResponse> {
|
|
195
200
|
const txHashBn = toBN(txHash);
|
|
@@ -204,17 +209,19 @@ export class Provider implements ProviderInterface {
|
|
|
204
209
|
*
|
|
205
210
|
* [Reference](https://github.com/starkware-libs/cairo-lang/blob/f464ec4797361b6be8989e36e02ec690e74ef285/src/starkware/starknet/services/api/gateway/gateway_client.py#L13-L17)
|
|
206
211
|
*
|
|
207
|
-
* @param
|
|
212
|
+
* @param transaction - transaction to be invoked
|
|
208
213
|
* @returns a confirmation of invoking a function on the starknet contract
|
|
209
214
|
*/
|
|
210
|
-
public async addTransaction(
|
|
211
|
-
const signature =
|
|
212
|
-
|
|
215
|
+
public async addTransaction(transaction: Transaction): Promise<AddTransactionResponse> {
|
|
216
|
+
const signature =
|
|
217
|
+
transaction.type === 'INVOKE_FUNCTION' && formatSignature(transaction.signature);
|
|
218
|
+
const contract_address_salt =
|
|
219
|
+
transaction.type === 'DEPLOY' && toHex(toBN(transaction.contract_address_salt));
|
|
213
220
|
|
|
214
221
|
const { data } = await axios.post<AddTransactionResponse>(
|
|
215
222
|
urljoin(this.gatewayUrl, 'add_transaction'),
|
|
216
223
|
stringify({
|
|
217
|
-
...
|
|
224
|
+
...transaction, // the tx can contain BigInts, so we use our own `stringify`
|
|
218
225
|
...(Array.isArray(signature) && { signature }), // not needed on deploy tx
|
|
219
226
|
...(contract_address_salt && { contract_address_salt }), // not needed on invoke tx
|
|
220
227
|
}),
|
|
@@ -263,7 +270,7 @@ export class Provider implements ProviderInterface {
|
|
|
263
270
|
contractAddress: string,
|
|
264
271
|
entrypointSelector: string,
|
|
265
272
|
calldata?: string[],
|
|
266
|
-
signature?:
|
|
273
|
+
signature?: Signature
|
|
267
274
|
): Promise<AddTransactionResponse> {
|
|
268
275
|
return this.addTransaction({
|
|
269
276
|
type: 'INVOKE_FUNCTION',
|
|
@@ -276,16 +283,15 @@ export class Provider implements ProviderInterface {
|
|
|
276
283
|
|
|
277
284
|
public async waitForTx(txHash: BigNumberish, retryInterval: number = 8000) {
|
|
278
285
|
let onchain = false;
|
|
286
|
+
await wait(retryInterval);
|
|
287
|
+
|
|
279
288
|
while (!onchain) {
|
|
280
289
|
// eslint-disable-next-line no-await-in-loop
|
|
281
290
|
await wait(retryInterval);
|
|
282
291
|
// eslint-disable-next-line no-await-in-loop
|
|
283
292
|
const res = await this.getTransactionStatus(txHash);
|
|
284
293
|
|
|
285
|
-
if (
|
|
286
|
-
res.tx_status === 'ACCEPTED_ONCHAIN' ||
|
|
287
|
-
(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.
|
|
288
|
-
) {
|
|
294
|
+
if (res.tx_status === 'ACCEPTED_ON_L1' || res.tx_status === 'ACCEPTED_ON_L2') {
|
|
289
295
|
onchain = true;
|
|
290
296
|
} else if (res.tx_status === 'REJECTED') {
|
|
291
297
|
throw Error('REJECTED');
|
|
@@ -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';
|
|
@@ -32,13 +34,13 @@ export abstract class ProviderInterface {
|
|
|
32
34
|
*
|
|
33
35
|
* [Reference](https://github.com/starkware-libs/cairo-lang/blob/f464ec4797361b6be8989e36e02ec690e74ef285/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L17-L25)
|
|
34
36
|
*
|
|
35
|
-
* @param
|
|
36
|
-
* @param
|
|
37
|
+
* @param invokeTransaction - transaction to be invoked
|
|
38
|
+
* @param blockNumber
|
|
37
39
|
* @returns the result of the function on the smart contract.
|
|
38
40
|
*/
|
|
39
41
|
public abstract callContract(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
invokeTransaction: CallContractTransaction,
|
|
43
|
+
blockNumber?: BlockNumber
|
|
42
44
|
): Promise<CallContractResponse>;
|
|
43
45
|
|
|
44
46
|
/**
|
|
@@ -46,10 +48,10 @@ export abstract class ProviderInterface {
|
|
|
46
48
|
*
|
|
47
49
|
* [Reference](https://github.com/starkware-libs/cairo-lang/blob/f464ec4797361b6be8989e36e02ec690e74ef285/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L27-L31)
|
|
48
50
|
*
|
|
49
|
-
* @param
|
|
50
|
-
* @returns the block object {
|
|
51
|
+
* @param blockNumber
|
|
52
|
+
* @returns the block object { block_number, previous_block_number, state_root, status, timestamp, transaction_receipts, transactions }
|
|
51
53
|
*/
|
|
52
|
-
public abstract getBlock(
|
|
54
|
+
public abstract getBlock(blockNumber?: BlockNumber): Promise<GetBlockResponse>;
|
|
53
55
|
|
|
54
56
|
/**
|
|
55
57
|
* Gets the code of the deployed contract.
|
|
@@ -57,10 +59,13 @@ export abstract class ProviderInterface {
|
|
|
57
59
|
* [Reference](https://github.com/starkware-libs/cairo-lang/blob/f464ec4797361b6be8989e36e02ec690e74ef285/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L33-L36)
|
|
58
60
|
*
|
|
59
61
|
* @param contractAddress
|
|
60
|
-
* @param
|
|
62
|
+
* @param blockNumber
|
|
61
63
|
* @returns Bytecode and ABI of compiled contract
|
|
62
64
|
*/
|
|
63
|
-
public abstract getCode(
|
|
65
|
+
public abstract getCode(
|
|
66
|
+
contractAddress: string,
|
|
67
|
+
blockNumber?: BlockNumber
|
|
68
|
+
): Promise<GetCodeResponse>;
|
|
64
69
|
|
|
65
70
|
// TODO: add proper type
|
|
66
71
|
/**
|
|
@@ -70,13 +75,13 @@ export abstract class ProviderInterface {
|
|
|
70
75
|
*
|
|
71
76
|
* @param contractAddress
|
|
72
77
|
* @param key - from getStorageVarAddress('<STORAGE_VARIABLE_NAME>') (WIP)
|
|
73
|
-
* @param
|
|
78
|
+
* @param blockNumber
|
|
74
79
|
* @returns the value of the storage variable
|
|
75
80
|
*/
|
|
76
81
|
public abstract getStorageAt(
|
|
77
82
|
contractAddress: string,
|
|
78
83
|
key: number,
|
|
79
|
-
|
|
84
|
+
blockNumber?: BlockNumber
|
|
80
85
|
): Promise<object>;
|
|
81
86
|
|
|
82
87
|
/**
|
|
@@ -85,7 +90,7 @@ export abstract class ProviderInterface {
|
|
|
85
90
|
* [Reference](https://github.com/starkware-libs/cairo-lang/blob/f464ec4797361b6be8989e36e02ec690e74ef285/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L48-L52)
|
|
86
91
|
*
|
|
87
92
|
* @param txHash
|
|
88
|
-
* @returns the transaction status object {
|
|
93
|
+
* @returns the transaction status object { block_number, tx_status: NOT_RECEIVED | RECEIVED | PENDING | REJECTED | ACCEPTED_ONCHAIN }
|
|
89
94
|
*/
|
|
90
95
|
public abstract getTransactionStatus(txHash: BigNumberish): Promise<GetTransactionStatusResponse>;
|
|
91
96
|
|
|
@@ -95,7 +100,7 @@ export abstract class ProviderInterface {
|
|
|
95
100
|
* [Reference](https://github.com/starkware-libs/cairo-lang/blob/f464ec4797361b6be8989e36e02ec690e74ef285/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L54-L58)
|
|
96
101
|
*
|
|
97
102
|
* @param txHash
|
|
98
|
-
* @returns the transacton object { transaction_id, status, transaction,
|
|
103
|
+
* @returns the transacton object { transaction_id, status, transaction, block_number?, block_number?, transaction_index?, transaction_failure_reason? }
|
|
99
104
|
*/
|
|
100
105
|
public abstract getTransaction(txHash: BigNumberish): Promise<GetTransactionResponse>;
|
|
101
106
|
|
|
@@ -104,10 +109,10 @@ export abstract class ProviderInterface {
|
|
|
104
109
|
*
|
|
105
110
|
* [Reference](https://github.com/starkware-libs/cairo-lang/blob/f464ec4797361b6be8989e36e02ec690e74ef285/src/starkware/starknet/services/api/gateway/gateway_client.py#L13-L17)
|
|
106
111
|
*
|
|
107
|
-
* @param
|
|
112
|
+
* @param transaction - transaction to be invoked
|
|
108
113
|
* @returns a confirmation of invoking a function on the starknet contract
|
|
109
114
|
*/
|
|
110
|
-
public abstract addTransaction(
|
|
115
|
+
public abstract addTransaction(transaction: Transaction): Promise<AddTransactionResponse>;
|
|
111
116
|
|
|
112
117
|
/**
|
|
113
118
|
* Deploys a given compiled contract (json) to starknet
|
|
@@ -135,7 +140,7 @@ export abstract class ProviderInterface {
|
|
|
135
140
|
contractAddress: string,
|
|
136
141
|
entrypointSelector: string,
|
|
137
142
|
calldata?: string[],
|
|
138
|
-
signature?:
|
|
143
|
+
signature?: Signature
|
|
139
144
|
): Promise<AddTransactionResponse>;
|
|
140
145
|
|
|
141
146
|
public abstract waitForTx(txHash: BigNumberish, retryInterval?: number): Promise<void>;
|
package/src/signer/default.ts
CHANGED
|
@@ -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 {
|
|
@@ -25,45 +26,76 @@ export class Signer extends Provider implements SignerInterface {
|
|
|
25
26
|
*
|
|
26
27
|
* [Reference](https://github.com/starkware-libs/cairo-lang/blob/f464ec4797361b6be8989e36e02ec690e74ef285/src/starkware/starknet/services/api/gateway/gateway_client.py#L13-L17)
|
|
27
28
|
*
|
|
28
|
-
* @param
|
|
29
|
+
* @param transaction - transaction to be invoked
|
|
29
30
|
* @returns a confirmation of invoking a function on the starknet contract
|
|
30
31
|
*/
|
|
31
|
-
public override async addTransaction(
|
|
32
|
-
if (
|
|
32
|
+
public override async addTransaction(transaction: Transaction): Promise<AddTransactionResponse> {
|
|
33
|
+
if (transaction.type === 'DEPLOY') return super.addTransaction(transaction);
|
|
33
34
|
|
|
34
|
-
assert(
|
|
35
|
+
assert(
|
|
36
|
+
!transaction.signature,
|
|
37
|
+
"Adding signatures to a signer transaction currently isn't supported"
|
|
38
|
+
);
|
|
35
39
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
let nonceBn;
|
|
41
|
+
if (transaction.nonce) {
|
|
42
|
+
nonceBn = toBN(transaction.nonce);
|
|
43
|
+
} else {
|
|
44
|
+
const { result } = await this.callContract({
|
|
45
|
+
contract_address: this.address,
|
|
46
|
+
entry_point_selector: getSelectorFromName('get_nonce'),
|
|
47
|
+
});
|
|
48
|
+
nonceBn = toBN(result[0]);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const calldataDecimal = (transaction.calldata || []).map((x) => toBN(x).toString());
|
|
42
52
|
|
|
43
53
|
const msgHash = addHexPrefix(
|
|
44
54
|
hashMessage(
|
|
45
55
|
this.address,
|
|
46
|
-
|
|
47
|
-
|
|
56
|
+
transaction.contract_address,
|
|
57
|
+
transaction.entry_point_selector,
|
|
48
58
|
calldataDecimal,
|
|
49
59
|
nonceBn.toString()
|
|
50
60
|
)
|
|
51
61
|
);
|
|
52
62
|
|
|
53
|
-
const
|
|
63
|
+
const signature = sign(this.keyPair, msgHash);
|
|
54
64
|
|
|
55
65
|
return super.addTransaction({
|
|
56
66
|
type: 'INVOKE_FUNCTION',
|
|
57
67
|
entry_point_selector: getSelectorFromName('execute'),
|
|
58
68
|
calldata: [
|
|
59
|
-
|
|
60
|
-
|
|
69
|
+
transaction.contract_address,
|
|
70
|
+
transaction.entry_point_selector,
|
|
61
71
|
calldataDecimal.length.toString(),
|
|
62
72
|
...calldataDecimal,
|
|
63
73
|
nonceBn.toString(),
|
|
64
74
|
].map((x) => toBN(x).toString()),
|
|
65
75
|
contract_address: this.address,
|
|
66
|
-
signature
|
|
76
|
+
signature,
|
|
67
77
|
});
|
|
68
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
|
+
}
|
|
69
101
|
}
|
package/src/signer/interface.ts
CHANGED
|
@@ -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;
|
|
@@ -8,8 +9,30 @@ export abstract class SignerInterface extends Provider {
|
|
|
8
9
|
*
|
|
9
10
|
* [Reference](https://github.com/starkware-libs/cairo-lang/blob/f464ec4797361b6be8989e36e02ec690e74ef285/src/starkware/starknet/services/api/gateway/gateway_client.py#L13-L17)
|
|
10
11
|
*
|
|
11
|
-
* @param
|
|
12
|
+
* @param transaction - transaction to be invoked
|
|
12
13
|
* @returns a confirmation of invoking a function on the starknet contract
|
|
13
14
|
*/
|
|
14
|
-
public abstract override addTransaction(
|
|
15
|
+
public abstract override addTransaction(
|
|
16
|
+
transaction: Transaction
|
|
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>;
|
|
15
38
|
}
|
package/src/types.ts
CHANGED
|
@@ -3,15 +3,21 @@ 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 = BigNumberish[];
|
|
7
7
|
|
|
8
8
|
export type GetContractAddressesResponse = {
|
|
9
9
|
Starknet: string;
|
|
10
10
|
GpsStatementVerifier: string;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
-
export type Status =
|
|
14
|
-
|
|
13
|
+
export type Status =
|
|
14
|
+
| 'NOT_RECEIVED'
|
|
15
|
+
| 'RECEIVED'
|
|
16
|
+
| 'PENDING'
|
|
17
|
+
| 'ACCEPTED_ON_L2'
|
|
18
|
+
| 'ACCEPTED_ON_L1'
|
|
19
|
+
| 'REJECTED';
|
|
20
|
+
export type TransactionStatus = 'TRANSACTION_RECEIVED';
|
|
15
21
|
export type Type = 'DEPLOY' | 'INVOKE_FUNCTION';
|
|
16
22
|
export type EntryPointType = 'EXTERNAL';
|
|
17
23
|
export type CompressedProgram = string;
|
|
@@ -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[];
|
|
@@ -51,15 +58,17 @@ export type DeployTransaction = {
|
|
|
51
58
|
contract_definition: CompressedCompiledContract;
|
|
52
59
|
contract_address_salt: BigNumberish;
|
|
53
60
|
constructor_calldata: string[];
|
|
61
|
+
nonce?: BigNumberish;
|
|
54
62
|
};
|
|
55
63
|
|
|
56
64
|
export type InvokeFunctionTransaction = {
|
|
57
65
|
type: 'INVOKE_FUNCTION';
|
|
58
66
|
contract_address: string;
|
|
59
|
-
signature?:
|
|
67
|
+
signature?: Signature;
|
|
60
68
|
entry_point_type?: EntryPointType;
|
|
61
69
|
entry_point_selector: string;
|
|
62
70
|
calldata?: string[];
|
|
71
|
+
nonce?: BigNumberish;
|
|
63
72
|
};
|
|
64
73
|
|
|
65
74
|
export type CallContractTransaction = Omit<InvokeFunctionTransaction, 'type'>;
|
|
@@ -87,7 +96,7 @@ export type GetBlockResponse = {
|
|
|
87
96
|
payload: string[];
|
|
88
97
|
from_address: string;
|
|
89
98
|
}[];
|
|
90
|
-
block_number:
|
|
99
|
+
block_number: BlockNumber;
|
|
91
100
|
status: Status;
|
|
92
101
|
transaction_index: number;
|
|
93
102
|
};
|
|
@@ -110,13 +119,13 @@ export type GetTransactionResponse = {
|
|
|
110
119
|
status: Status;
|
|
111
120
|
transaction: Transaction;
|
|
112
121
|
block_hash: string;
|
|
113
|
-
block_number:
|
|
122
|
+
block_number: BlockNumber;
|
|
114
123
|
transaction_index: number;
|
|
115
124
|
transaction_hash: string;
|
|
116
125
|
};
|
|
117
126
|
|
|
118
127
|
export type AddTransactionResponse = {
|
|
119
|
-
code:
|
|
128
|
+
code: TransactionStatus;
|
|
120
129
|
transaction_hash: string;
|
|
121
130
|
address?: string;
|
|
122
131
|
};
|
|
@@ -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
|
|
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
|
-
|
|
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
|
|
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
|
}
|
package/src/utils/stark.ts
CHANGED
|
@@ -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 {
|
|
8
|
+
import { toBN, toHex } from './number';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Function to compress compiled cairo program
|
|
@@ -41,15 +41,11 @@ export function makeAddress(input: string): string {
|
|
|
41
41
|
return addHexPrefix(input).toLowerCase();
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
export function formatSignature(sig?:
|
|
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())
|
|
47
|
+
return sig.map((x) => toBN(x)).map((x) => x.toString());
|
|
48
48
|
} catch (e) {
|
|
49
49
|
return [];
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
-
|
|
53
|
-
export function compileStructToCalldata<S extends { [k: string]: string }>(struct: S): string[] {
|
|
54
|
-
return Object.values(struct);
|
|
55
|
-
}
|