starknet 3.7.0 → 3.10.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 +38 -0
- package/README.md +18 -53
- package/__mocks__/ArgentAccount.json +32022 -38726
- package/__tests__/accountContract.test.ts +42 -32
- package/__tests__/contract.test.ts +20 -6
- 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 +26 -8
- package/__tests__/utils/transactionHash.test.ts +17 -0
- package/__tests__/utils/utils.test.ts +10 -0
- package/account/default.d.ts +11 -1
- package/account/default.js +58 -50
- package/constants.d.ts +9 -0
- package/constants.js +13 -0
- package/contract/default.d.ts +12 -2
- package/contract/default.js +27 -20
- package/contract/interface.d.ts +21 -3
- package/dist/account/default.d.ts +5 -1
- package/dist/account/default.js +46 -30
- package/dist/constants.d.ts +9 -0
- package/dist/constants.js +12 -1
- package/dist/contract/default.d.ts +6 -3
- package/dist/contract/default.js +23 -20
- package/dist/contract/interface.d.ts +9 -4
- package/dist/provider/default.d.ts +5 -2
- package/dist/provider/default.js +36 -19
- package/dist/provider/interface.d.ts +2 -0
- package/dist/provider/utils.d.ts +1 -2
- package/dist/provider/utils.js +7 -8
- package/dist/signer/default.js +4 -2
- package/dist/signer/ledger.js +4 -2
- package/dist/types/api.d.ts +4 -1
- package/dist/types/lib.d.ts +1 -0
- package/dist/types/signer.d.ts +2 -0
- package/dist/utils/hash.d.ts +5 -3
- package/dist/utils/hash.js +25 -23
- package/dist/utils/stark.d.ts +3 -0
- package/dist/utils/stark.js +8 -1
- package/dist/utils/transaction.d.ts +2 -0
- package/dist/utils/transaction.js +5 -1
- package/dist/utils/typedData/index.d.ts +2 -2
- package/dist/utils/typedData/types.d.ts +3 -3
- package/dist/utils/typedData/utils.d.ts +1 -1
- package/package.json +1 -1
- package/provider/default.d.ts +7 -2
- package/provider/default.js +49 -27
- package/provider/interface.d.ts +2 -0
- package/provider/utils.d.ts +1 -2
- package/provider/utils.js +7 -8
- package/signer/default.js +12 -5
- package/signer/ledger.js +12 -5
- package/src/account/default.ts +47 -18
- package/src/constants.ts +10 -0
- package/src/contract/default.ts +32 -19
- package/src/contract/interface.ts +21 -3
- package/src/provider/default.ts +37 -12
- package/src/provider/interface.ts +3 -0
- package/src/provider/utils.ts +7 -8
- package/src/signer/default.ts +10 -5
- package/src/signer/ledger.ts +10 -5
- package/src/types/api.ts +4 -1
- package/src/types/lib.ts +1 -0
- package/src/types/signer.ts +2 -0
- package/src/utils/hash.ts +70 -26
- package/src/utils/stark.ts +8 -1
- package/src/utils/transaction.ts +7 -0
- package/types/api.d.ts +4 -1
- package/types/lib.d.ts +1 -0
- package/types/signer.d.ts +2 -0
- package/utils/hash.d.ts +25 -7
- package/utils/hash.js +60 -26
- package/utils/stark.d.ts +4 -0
- package/utils/stark.js +13 -1
- package/utils/transaction.d.ts +5 -0
- package/utils/transaction.js +12 -1
- package/utils/typedData/index.d.ts +2 -2
- package/utils/typedData/types.d.ts +3 -3
- package/utils/typedData/utils.d.ts +1 -1
- package/__tests__/constancts.ts +0 -2
package/src/account/default.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import assert from 'minimalistic-assert';
|
|
2
2
|
|
|
3
|
+
import { ZERO } from '../constants';
|
|
3
4
|
import { Provider } from '../provider';
|
|
5
|
+
import { BlockIdentifier } from '../provider/utils';
|
|
4
6
|
import { Signer, SignerInterface } from '../signer';
|
|
5
7
|
import {
|
|
6
8
|
Abi,
|
|
@@ -8,6 +10,7 @@ import {
|
|
|
8
10
|
Call,
|
|
9
11
|
EstimateFeeResponse,
|
|
10
12
|
InvocationsDetails,
|
|
13
|
+
InvocationsSignerDetails,
|
|
11
14
|
InvokeFunctionTransaction,
|
|
12
15
|
KeyPair,
|
|
13
16
|
Signature,
|
|
@@ -16,13 +19,14 @@ import {
|
|
|
16
19
|
import { sign } from '../utils/ellipticCurve';
|
|
17
20
|
import {
|
|
18
21
|
computeHashOnElements,
|
|
22
|
+
feeTransactionVersion,
|
|
19
23
|
getSelectorFromName,
|
|
20
|
-
transactionPrefix,
|
|
21
24
|
transactionVersion,
|
|
22
25
|
} from '../utils/hash';
|
|
23
26
|
import { BigNumberish, bigNumberishArrayToDecimalStringArray, toBN, toHex } from '../utils/number';
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
27
|
+
import { encodeShortString } from '../utils/shortString';
|
|
28
|
+
import { compileCalldata, estimatedFeeToMaxFee } from '../utils/stark';
|
|
29
|
+
import { fromCallsToExecuteCalldataWithNonce } from '../utils/transaction';
|
|
26
30
|
import { TypedData, getMessageHash } from '../utils/typedData';
|
|
27
31
|
import { AccountInterface } from './interface';
|
|
28
32
|
|
|
@@ -46,23 +50,39 @@ export class Account extends Provider implements AccountInterface {
|
|
|
46
50
|
return toHex(toBN(result[0]));
|
|
47
51
|
}
|
|
48
52
|
|
|
49
|
-
public async estimateFee(
|
|
53
|
+
public async estimateFee(
|
|
54
|
+
calls: Call | Call[],
|
|
55
|
+
{
|
|
56
|
+
nonce: providedNonce,
|
|
57
|
+
blockIdentifier = 'pending',
|
|
58
|
+
}: { nonce?: BigNumberish; blockIdentifier?: BlockIdentifier } = {}
|
|
59
|
+
): Promise<EstimateFeeResponse> {
|
|
50
60
|
const transactions = Array.isArray(calls) ? calls : [calls];
|
|
51
|
-
const nonce = await this.getNonce();
|
|
52
|
-
const
|
|
61
|
+
const nonce = providedNonce ?? (await this.getNonce());
|
|
62
|
+
const version = toBN(feeTransactionVersion);
|
|
63
|
+
|
|
64
|
+
const signerDetails: InvocationsSignerDetails = {
|
|
53
65
|
walletAddress: this.address,
|
|
54
66
|
nonce: toBN(nonce),
|
|
55
|
-
maxFee:
|
|
67
|
+
maxFee: ZERO,
|
|
68
|
+
version,
|
|
69
|
+
chainId: this.chainId,
|
|
56
70
|
};
|
|
71
|
+
|
|
57
72
|
const signature = await this.signer.signTransaction(transactions, signerDetails);
|
|
58
73
|
|
|
59
|
-
const calldata =
|
|
60
|
-
return this.fetchEndpoint(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
74
|
+
const calldata = fromCallsToExecuteCalldataWithNonce(transactions, nonce);
|
|
75
|
+
return this.fetchEndpoint(
|
|
76
|
+
'estimate_fee',
|
|
77
|
+
{ blockIdentifier },
|
|
78
|
+
{
|
|
79
|
+
contract_address: this.address,
|
|
80
|
+
entry_point_selector: getSelectorFromName('__execute__'),
|
|
81
|
+
calldata,
|
|
82
|
+
version: toHex(version),
|
|
83
|
+
signature: bigNumberishArrayToDecimalStringArray(signature),
|
|
84
|
+
}
|
|
85
|
+
);
|
|
66
86
|
}
|
|
67
87
|
|
|
68
88
|
/**
|
|
@@ -80,16 +100,25 @@ export class Account extends Provider implements AccountInterface {
|
|
|
80
100
|
): Promise<AddTransactionResponse> {
|
|
81
101
|
const transactions = Array.isArray(calls) ? calls : [calls];
|
|
82
102
|
const nonce = toBN(transactionsDetail.nonce ?? (await this.getNonce()));
|
|
83
|
-
|
|
84
|
-
|
|
103
|
+
let maxFee: BigNumberish = '0';
|
|
104
|
+
if (transactionsDetail.maxFee || transactionsDetail.maxFee === 0) {
|
|
105
|
+
maxFee = transactionsDetail.maxFee;
|
|
106
|
+
} else {
|
|
107
|
+
const estimatedFee = (await this.estimateFee(transactions, { nonce })).amount;
|
|
108
|
+
maxFee = estimatedFeeToMaxFee(estimatedFee).toString();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const signerDetails: InvocationsSignerDetails = {
|
|
85
112
|
walletAddress: this.address,
|
|
86
113
|
nonce,
|
|
87
114
|
maxFee,
|
|
115
|
+
version: toBN(transactionVersion),
|
|
116
|
+
chainId: this.chainId,
|
|
88
117
|
};
|
|
89
118
|
|
|
90
119
|
const signature = await this.signer.signTransaction(transactions, signerDetails, abis);
|
|
91
120
|
|
|
92
|
-
const calldata =
|
|
121
|
+
const calldata = fromCallsToExecuteCalldataWithNonce(transactions, nonce);
|
|
93
122
|
return this.fetchEndpoint('add_transaction', undefined, {
|
|
94
123
|
type: 'INVOKE_FUNCTION',
|
|
95
124
|
contract_address: this.address,
|
|
@@ -139,7 +168,7 @@ export class Account extends Provider implements AccountInterface {
|
|
|
139
168
|
.map(computeHashOnElements);
|
|
140
169
|
|
|
141
170
|
return computeHashOnElements([
|
|
142
|
-
|
|
171
|
+
encodeShortString('StarkNet Transaction'),
|
|
143
172
|
account,
|
|
144
173
|
computeHashOnElements(hashArray),
|
|
145
174
|
nonce,
|
package/src/constants.ts
CHANGED
|
@@ -8,6 +8,16 @@ export const TWO = toBN(2);
|
|
|
8
8
|
export const MASK_250 = TWO.pow(toBN(250)).sub(ONE); // 2 ** 250 - 1
|
|
9
9
|
export const MASK_251 = TWO.pow(toBN(251));
|
|
10
10
|
|
|
11
|
+
export enum StarknetChainId {
|
|
12
|
+
MAINNET = '0x534e5f4d41494e', // encodeShortString('SN_MAIN'),
|
|
13
|
+
TESTNET = '0x534e5f474f45524c49', // encodeShortString('SN_GOERLI'),
|
|
14
|
+
}
|
|
15
|
+
export enum TransactionHashPrefix {
|
|
16
|
+
DEPLOY = '0x6465706c6f79', // encodeShortString('deploy'),
|
|
17
|
+
INVOKE = '0x696e766f6b65', // encodeShortString('invoke'),
|
|
18
|
+
L1_HANDLER = '0x6c315f68616e646c6572', // encodeShortString('l1_handler'),
|
|
19
|
+
}
|
|
20
|
+
|
|
11
21
|
/**
|
|
12
22
|
* The following is taken from https://github.com/starkware-libs/starkex-resources/blob/master/crypto/starkware/crypto/signature/pedersen_params.json but converted to hex, because JS is very bad handling big integers by default
|
|
13
23
|
* Please do not edit until the JSON changes.
|
package/src/contract/default.ts
CHANGED
|
@@ -3,6 +3,7 @@ import assert from 'minimalistic-assert';
|
|
|
3
3
|
|
|
4
4
|
import { AccountInterface } from '../account';
|
|
5
5
|
import { ProviderInterface, defaultProvider } from '../provider';
|
|
6
|
+
import { BlockIdentifier } from '../provider/utils';
|
|
6
7
|
import {
|
|
7
8
|
Abi,
|
|
8
9
|
AbiEntry,
|
|
@@ -45,7 +46,18 @@ function buildCall(contract: Contract, functionAbi: FunctionAbi): AsyncContractF
|
|
|
45
46
|
*/
|
|
46
47
|
function buildInvoke(contract: Contract, functionAbi: FunctionAbi): AsyncContractFunction {
|
|
47
48
|
return async function (...args: Array<any>): Promise<any> {
|
|
48
|
-
|
|
49
|
+
const { inputs } = functionAbi;
|
|
50
|
+
const inputsLength = inputs.reduce((acc, input) => {
|
|
51
|
+
if (!/_len$/.test(input.name)) {
|
|
52
|
+
return acc + 1;
|
|
53
|
+
}
|
|
54
|
+
return acc;
|
|
55
|
+
}, 0);
|
|
56
|
+
const options = {};
|
|
57
|
+
if (inputsLength + 1 === args.length && typeof args[args.length - 1] === 'object') {
|
|
58
|
+
Object.assign(options, args.pop());
|
|
59
|
+
}
|
|
60
|
+
return contract.invoke(functionAbi.name, args, options);
|
|
49
61
|
};
|
|
50
62
|
}
|
|
51
63
|
|
|
@@ -528,7 +540,11 @@ export class Contract implements ContractInterface {
|
|
|
528
540
|
}, [] as Result);
|
|
529
541
|
}
|
|
530
542
|
|
|
531
|
-
public invoke(
|
|
543
|
+
public invoke(
|
|
544
|
+
method: string,
|
|
545
|
+
args: Array<any> = [],
|
|
546
|
+
options: Overrides = {}
|
|
547
|
+
): Promise<AddTransactionResponse> {
|
|
532
548
|
// ensure contract is connected
|
|
533
549
|
assert(this.address !== null, 'contract isnt connected to an address');
|
|
534
550
|
// validate method and args
|
|
@@ -542,11 +558,6 @@ export class Contract implements ContractInterface {
|
|
|
542
558
|
return acc;
|
|
543
559
|
}, 0);
|
|
544
560
|
|
|
545
|
-
const overrides: Overrides = {};
|
|
546
|
-
if (args.length === inputsLength + 1 && Array.isArray(args[args.length - 1])) {
|
|
547
|
-
Object.assign(overrides, args.pop());
|
|
548
|
-
}
|
|
549
|
-
|
|
550
561
|
if (args.length !== inputsLength) {
|
|
551
562
|
throw Error(
|
|
552
563
|
`Invalid number of arguments, expected ${inputsLength} arguments, but got ${args.length}`
|
|
@@ -562,31 +573,33 @@ export class Contract implements ContractInterface {
|
|
|
562
573
|
};
|
|
563
574
|
if ('execute' in this.providerOrAccount) {
|
|
564
575
|
return this.providerOrAccount.execute(invocation, undefined, {
|
|
565
|
-
maxFee:
|
|
566
|
-
nonce:
|
|
576
|
+
maxFee: options.maxFee,
|
|
577
|
+
nonce: options.nonce,
|
|
567
578
|
});
|
|
568
579
|
}
|
|
569
580
|
|
|
570
581
|
return this.providerOrAccount.invokeFunction({
|
|
571
582
|
...invocation,
|
|
572
|
-
signature:
|
|
583
|
+
signature: options.signature || [],
|
|
573
584
|
});
|
|
574
585
|
}
|
|
575
586
|
|
|
576
|
-
public async call(
|
|
587
|
+
public async call(
|
|
588
|
+
method: string,
|
|
589
|
+
args: Array<any> = [],
|
|
590
|
+
{
|
|
591
|
+
blockIdentifier = 'pending',
|
|
592
|
+
}: {
|
|
593
|
+
blockIdentifier?: BlockIdentifier;
|
|
594
|
+
} = {}
|
|
595
|
+
): Promise<Result> {
|
|
577
596
|
// ensure contract is connected
|
|
578
597
|
assert(this.address !== null, 'contract isnt connected to an address');
|
|
579
598
|
|
|
580
599
|
// validate method and args
|
|
581
600
|
this.validateMethodAndArgs('CALL', method, args);
|
|
582
601
|
const { inputs } = this.abi.find((abi) => abi.name === method) as FunctionAbi;
|
|
583
|
-
|
|
584
|
-
const options = {
|
|
585
|
-
blockIdentifier: null,
|
|
586
|
-
};
|
|
587
|
-
if (args.length === inputsLength + 1 && typeof args[args.length - 1] === 'object') {
|
|
588
|
-
Object.assign(options, args.pop());
|
|
589
|
-
}
|
|
602
|
+
|
|
590
603
|
// compile calldata
|
|
591
604
|
const calldata = this.compileCalldata(args, inputs);
|
|
592
605
|
return this.providerOrAccount
|
|
@@ -596,7 +609,7 @@ export class Contract implements ContractInterface {
|
|
|
596
609
|
calldata,
|
|
597
610
|
entrypoint: method,
|
|
598
611
|
},
|
|
599
|
-
|
|
612
|
+
{ blockIdentifier }
|
|
600
613
|
)
|
|
601
614
|
.then((x) => this.parseResponse(method, x.result));
|
|
602
615
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { AccountInterface } from '../account';
|
|
2
2
|
import { ProviderInterface } from '../provider';
|
|
3
|
+
import { BlockIdentifier } from '../provider/utils';
|
|
3
4
|
import {
|
|
4
5
|
Abi,
|
|
5
6
|
AddTransactionResponse,
|
|
6
7
|
AsyncContractFunction,
|
|
7
8
|
ContractFunction,
|
|
8
9
|
Invocation,
|
|
10
|
+
Overrides,
|
|
9
11
|
Result,
|
|
10
12
|
} from '../types';
|
|
11
13
|
|
|
@@ -57,7 +59,13 @@ export abstract class ContractInterface {
|
|
|
57
59
|
* @param args Array of the arguments for the call
|
|
58
60
|
* @returns Result of the call as an array with key value pars
|
|
59
61
|
*/
|
|
60
|
-
public abstract call(
|
|
62
|
+
public abstract call(
|
|
63
|
+
method: string,
|
|
64
|
+
args?: Array<any>,
|
|
65
|
+
options?: {
|
|
66
|
+
blockIdentifier?: BlockIdentifier;
|
|
67
|
+
}
|
|
68
|
+
): Promise<Result>;
|
|
61
69
|
|
|
62
70
|
/**
|
|
63
71
|
* Invokes a method on a contract
|
|
@@ -66,7 +74,11 @@ export abstract class ContractInterface {
|
|
|
66
74
|
* @param args Array of the arguments for the invoke
|
|
67
75
|
* @returns Add Transaction Response
|
|
68
76
|
*/
|
|
69
|
-
public abstract invoke(
|
|
77
|
+
public abstract invoke(
|
|
78
|
+
method: string,
|
|
79
|
+
args?: Array<any>,
|
|
80
|
+
options?: Overrides
|
|
81
|
+
): Promise<AddTransactionResponse>;
|
|
70
82
|
|
|
71
83
|
/**
|
|
72
84
|
* Calls a method on a contract
|
|
@@ -74,7 +86,13 @@ export abstract class ContractInterface {
|
|
|
74
86
|
* @param method name of the method
|
|
75
87
|
* @param args Array of the arguments for the call
|
|
76
88
|
*/
|
|
77
|
-
public abstract estimate(
|
|
89
|
+
public abstract estimate(
|
|
90
|
+
method: string,
|
|
91
|
+
args?: Array<any>,
|
|
92
|
+
options?: {
|
|
93
|
+
blockIdentifier?: BlockIdentifier;
|
|
94
|
+
}
|
|
95
|
+
): Promise<any>;
|
|
78
96
|
|
|
79
97
|
/**
|
|
80
98
|
* Calls a method on a contract
|
package/src/provider/default.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import axios, { AxiosRequestHeaders } from 'axios';
|
|
2
2
|
import urljoin from 'url-join';
|
|
3
3
|
|
|
4
|
+
import { StarknetChainId } from '../constants';
|
|
4
5
|
import {
|
|
5
6
|
Abi,
|
|
6
7
|
AddTransactionResponse,
|
|
@@ -49,17 +50,22 @@ export class Provider implements ProviderInterface {
|
|
|
49
50
|
|
|
50
51
|
public gatewayUrl: string;
|
|
51
52
|
|
|
53
|
+
public chainId: StarknetChainId;
|
|
54
|
+
|
|
52
55
|
constructor(optionsOrProvider: ProviderOptions | Provider = { network: 'goerli-alpha' }) {
|
|
53
56
|
if (optionsOrProvider instanceof Provider) {
|
|
54
57
|
this.baseUrl = optionsOrProvider.baseUrl;
|
|
55
58
|
this.feederGatewayUrl = optionsOrProvider.feederGatewayUrl;
|
|
56
59
|
this.gatewayUrl = optionsOrProvider.gatewayUrl;
|
|
60
|
+
this.chainId =
|
|
61
|
+
optionsOrProvider.chainId ?? Provider.getChainIdFromBaseUrl(optionsOrProvider.baseUrl);
|
|
57
62
|
} else {
|
|
58
63
|
const baseUrl =
|
|
59
64
|
'baseUrl' in optionsOrProvider
|
|
60
65
|
? optionsOrProvider.baseUrl
|
|
61
66
|
: Provider.getNetworkFromName(optionsOrProvider.network);
|
|
62
67
|
this.baseUrl = baseUrl;
|
|
68
|
+
this.chainId = Provider.getChainIdFromBaseUrl(baseUrl);
|
|
63
69
|
this.feederGatewayUrl = urljoin(baseUrl, 'feeder_gateway');
|
|
64
70
|
this.gatewayUrl = urljoin(baseUrl, 'gateway');
|
|
65
71
|
}
|
|
@@ -75,6 +81,19 @@ export class Provider implements ProviderInterface {
|
|
|
75
81
|
}
|
|
76
82
|
}
|
|
77
83
|
|
|
84
|
+
protected static getChainIdFromBaseUrl(baseUrl: string): StarknetChainId {
|
|
85
|
+
try {
|
|
86
|
+
const url = new URL(baseUrl);
|
|
87
|
+
if (url.host.includes('mainnet.starknet.io')) {
|
|
88
|
+
return StarknetChainId.MAINNET;
|
|
89
|
+
}
|
|
90
|
+
} catch {
|
|
91
|
+
// eslint-disable-next-line no-console
|
|
92
|
+
console.error(`Could not parse baseUrl: ${baseUrl}`);
|
|
93
|
+
}
|
|
94
|
+
return StarknetChainId.TESTNET;
|
|
95
|
+
}
|
|
96
|
+
|
|
78
97
|
private getFetchUrl(endpoint: keyof Endpoints) {
|
|
79
98
|
const gatewayUrlEndpoints = ['add_transaction'];
|
|
80
99
|
|
|
@@ -168,14 +187,18 @@ export class Provider implements ProviderInterface {
|
|
|
168
187
|
*/
|
|
169
188
|
public async callContract(
|
|
170
189
|
{ contractAddress, entrypoint, calldata = [] }: Call,
|
|
171
|
-
|
|
190
|
+
{ blockIdentifier = 'pending' }: { blockIdentifier?: BlockIdentifier } = {}
|
|
172
191
|
): Promise<CallContractResponse> {
|
|
173
|
-
return this.fetchEndpoint(
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
192
|
+
return this.fetchEndpoint(
|
|
193
|
+
'call_contract',
|
|
194
|
+
{ blockIdentifier },
|
|
195
|
+
{
|
|
196
|
+
signature: [],
|
|
197
|
+
contract_address: contractAddress,
|
|
198
|
+
entry_point_selector: getSelectorFromName(entrypoint),
|
|
199
|
+
calldata,
|
|
200
|
+
}
|
|
201
|
+
);
|
|
179
202
|
}
|
|
180
203
|
|
|
181
204
|
/**
|
|
@@ -203,7 +226,7 @@ export class Provider implements ProviderInterface {
|
|
|
203
226
|
*/
|
|
204
227
|
public async getCode(
|
|
205
228
|
contractAddress: string,
|
|
206
|
-
blockIdentifier: BlockIdentifier =
|
|
229
|
+
blockIdentifier: BlockIdentifier = 'pending'
|
|
207
230
|
): Promise<GetCodeResponse> {
|
|
208
231
|
return this.fetchEndpoint('get_code', { blockIdentifier, contractAddress });
|
|
209
232
|
}
|
|
@@ -223,7 +246,7 @@ export class Provider implements ProviderInterface {
|
|
|
223
246
|
public async getStorageAt(
|
|
224
247
|
contractAddress: string,
|
|
225
248
|
key: number,
|
|
226
|
-
blockIdentifier: BlockIdentifier =
|
|
249
|
+
blockIdentifier: BlockIdentifier = 'pending'
|
|
227
250
|
): Promise<object> {
|
|
228
251
|
return this.fetchEndpoint('get_storage_at', { blockIdentifier, contractAddress, key });
|
|
229
252
|
}
|
|
@@ -340,7 +363,6 @@ export class Provider implements ProviderInterface {
|
|
|
340
363
|
|
|
341
364
|
public async waitForTransaction(txHash: BigNumberish, retryInterval: number = 8000) {
|
|
342
365
|
let onchain = false;
|
|
343
|
-
await wait(retryInterval);
|
|
344
366
|
|
|
345
367
|
while (!onchain) {
|
|
346
368
|
// eslint-disable-next-line no-await-in-loop
|
|
@@ -348,9 +370,12 @@ export class Provider implements ProviderInterface {
|
|
|
348
370
|
// eslint-disable-next-line no-await-in-loop
|
|
349
371
|
const res = await this.getTransactionStatus(txHash);
|
|
350
372
|
|
|
351
|
-
|
|
373
|
+
const successStates = ['ACCEPTED_ON_L1', 'ACCEPTED_ON_L2', 'PENDING'];
|
|
374
|
+
const errorStates = ['REJECTED', 'NOT_RECEIVED'];
|
|
375
|
+
|
|
376
|
+
if (successStates.includes(res.tx_status)) {
|
|
352
377
|
onchain = true;
|
|
353
|
-
} else if (
|
|
378
|
+
} else if (errorStates.includes(res.tx_status)) {
|
|
354
379
|
const message = res.tx_failure_reason
|
|
355
380
|
? `${res.tx_status}: ${res.tx_failure_reason.code}\n${res.tx_failure_reason.error_message}`
|
|
356
381
|
: res.tx_status;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { StarknetChainId } from '../constants';
|
|
1
2
|
import type {
|
|
2
3
|
AddTransactionResponse,
|
|
3
4
|
Call,
|
|
@@ -21,6 +22,8 @@ export abstract class ProviderInterface {
|
|
|
21
22
|
|
|
22
23
|
public abstract gatewayUrl: string;
|
|
23
24
|
|
|
25
|
+
public abstract chainId: StarknetChainId;
|
|
26
|
+
|
|
24
27
|
/**
|
|
25
28
|
* Gets the smart contract address on the goerli testnet.
|
|
26
29
|
*
|
package/src/provider/utils.ts
CHANGED
|
@@ -43,6 +43,12 @@ type BlockIdentifierObject =
|
|
|
43
43
|
* @returns block identifier object
|
|
44
44
|
*/
|
|
45
45
|
export function getBlockIdentifier(blockIdentifier: BlockIdentifier): BlockIdentifierObject {
|
|
46
|
+
if (blockIdentifier === null) {
|
|
47
|
+
return { type: 'BLOCK_NUMBER', data: null };
|
|
48
|
+
}
|
|
49
|
+
if (blockIdentifier === 'pending') {
|
|
50
|
+
return { type: 'BLOCK_NUMBER', data: 'pending' };
|
|
51
|
+
}
|
|
46
52
|
if (typeof blockIdentifier === 'number') {
|
|
47
53
|
return { type: 'BLOCK_NUMBER', data: blockIdentifier };
|
|
48
54
|
}
|
|
@@ -52,12 +58,6 @@ export function getBlockIdentifier(blockIdentifier: BlockIdentifier): BlockIdent
|
|
|
52
58
|
if (typeof blockIdentifier === 'string' && !Number.isNaN(parseInt(blockIdentifier, 10))) {
|
|
53
59
|
return { type: 'BLOCK_NUMBER', data: parseInt(blockIdentifier, 10) };
|
|
54
60
|
}
|
|
55
|
-
if (blockIdentifier === null) {
|
|
56
|
-
return { type: 'BLOCK_NUMBER', data: null };
|
|
57
|
-
}
|
|
58
|
-
if (blockIdentifier === 'pending') {
|
|
59
|
-
return { type: 'BLOCK_NUMBER', data: 'pending' };
|
|
60
|
-
}
|
|
61
61
|
if (typeof blockIdentifier === 'string') {
|
|
62
62
|
throw new Error(`Invalid block identifier: ${blockIdentifier}`);
|
|
63
63
|
}
|
|
@@ -69,8 +69,7 @@ export function getBlockIdentifier(blockIdentifier: BlockIdentifier): BlockIdent
|
|
|
69
69
|
*
|
|
70
70
|
* [Reference](https://github.com/starkware-libs/cairo-lang/blob/fc97bdd8322a7df043c87c371634b26c15ed6cee/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L164-L173)
|
|
71
71
|
*
|
|
72
|
-
* @param
|
|
73
|
-
* @param blockHash
|
|
72
|
+
* @param blockIdentifier
|
|
74
73
|
* @returns block identifier for API request
|
|
75
74
|
*/
|
|
76
75
|
export function getFormattedBlockIdentifier(blockIdentifier: BlockIdentifier = null): string {
|
package/src/signer/default.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Abi, Invocation, InvocationsSignerDetails, KeyPair, Signature } from '../types';
|
|
2
2
|
import { getStarkKey, sign } from '../utils/ellipticCurve';
|
|
3
|
-
import {
|
|
3
|
+
import { calculcateTransactionHash, getSelectorFromName } from '../utils/hash';
|
|
4
|
+
import { fromCallsToExecuteCalldataWithNonce } from '../utils/transaction';
|
|
4
5
|
import { TypedData, getMessageHash } from '../utils/typedData';
|
|
5
6
|
import { SignerInterface } from './interface';
|
|
6
7
|
|
|
@@ -25,11 +26,15 @@ export class Signer implements SignerInterface {
|
|
|
25
26
|
}
|
|
26
27
|
// now use abi to display decoded data somewhere, but as this signer is headless, we can't do that
|
|
27
28
|
|
|
28
|
-
const
|
|
29
|
+
const calldata = fromCallsToExecuteCalldataWithNonce(transactions, transactionsDetail.nonce);
|
|
30
|
+
|
|
31
|
+
const msgHash = calculcateTransactionHash(
|
|
29
32
|
transactionsDetail.walletAddress,
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
transactionsDetail.version,
|
|
34
|
+
getSelectorFromName('__execute__'),
|
|
35
|
+
calldata,
|
|
36
|
+
transactionsDetail.maxFee,
|
|
37
|
+
transactionsDetail.chainId
|
|
33
38
|
);
|
|
34
39
|
|
|
35
40
|
return sign(this.keyPair, msgHash);
|
package/src/signer/ledger.ts
CHANGED
|
@@ -4,7 +4,8 @@ import TransportWebHID from '@ledgerhq/hw-transport-webhid';
|
|
|
4
4
|
|
|
5
5
|
import { Invocation, InvocationsSignerDetails, Signature } from '../types';
|
|
6
6
|
import { addHexPrefix } from '../utils/encode';
|
|
7
|
-
import {
|
|
7
|
+
import { calculcateTransactionHash, getSelectorFromName } from '../utils/hash';
|
|
8
|
+
import { fromCallsToExecuteCalldataWithNonce } from '../utils/transaction';
|
|
8
9
|
import { TypedData, getMessageHash } from '../utils/typedData';
|
|
9
10
|
import { SignerInterface } from './interface';
|
|
10
11
|
|
|
@@ -46,11 +47,15 @@ export class LedgerBlindSigner implements SignerInterface {
|
|
|
46
47
|
transactions: Invocation[],
|
|
47
48
|
transactionsDetail: InvocationsSignerDetails
|
|
48
49
|
): Promise<Signature> {
|
|
49
|
-
const
|
|
50
|
+
const calldata = fromCallsToExecuteCalldataWithNonce(transactions, transactionsDetail.nonce);
|
|
51
|
+
|
|
52
|
+
const msgHash = calculcateTransactionHash(
|
|
50
53
|
transactionsDetail.walletAddress,
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
transactionsDetail.version,
|
|
55
|
+
getSelectorFromName('__execute__'),
|
|
56
|
+
calldata,
|
|
57
|
+
transactionsDetail.maxFee,
|
|
58
|
+
transactionsDetail.chainId
|
|
54
59
|
);
|
|
55
60
|
|
|
56
61
|
return this.sign(msgHash);
|
package/src/types/api.ts
CHANGED
|
@@ -75,7 +75,9 @@ export type Endpoints = {
|
|
|
75
75
|
RESPONSE: CallContractResponse;
|
|
76
76
|
};
|
|
77
77
|
estimate_fee: {
|
|
78
|
-
QUERY:
|
|
78
|
+
QUERY: {
|
|
79
|
+
blockIdentifier: BlockIdentifier;
|
|
80
|
+
};
|
|
79
81
|
REQUEST: CallContractTransaction;
|
|
80
82
|
RESPONSE: EstimateFeeResponse;
|
|
81
83
|
};
|
|
@@ -103,6 +105,7 @@ export type InvokeFunctionTransaction = {
|
|
|
103
105
|
calldata?: RawCalldata;
|
|
104
106
|
nonce?: BigNumberish;
|
|
105
107
|
max_fee?: BigNumberish;
|
|
108
|
+
version?: BigNumberish;
|
|
106
109
|
};
|
|
107
110
|
|
|
108
111
|
export type InvokeFunctionTrace = {
|
package/src/types/lib.ts
CHANGED
package/src/types/signer.ts
CHANGED
package/src/utils/hash.ts
CHANGED
|
@@ -2,15 +2,21 @@ import BN from 'bn.js';
|
|
|
2
2
|
import { keccak256 } from 'ethereum-cryptography/keccak';
|
|
3
3
|
import assert from 'minimalistic-assert';
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
|
|
5
|
+
import {
|
|
6
|
+
CONSTANT_POINTS,
|
|
7
|
+
FIELD_PRIME,
|
|
8
|
+
MASK_250,
|
|
9
|
+
ONE,
|
|
10
|
+
StarknetChainId,
|
|
11
|
+
TransactionHashPrefix,
|
|
12
|
+
ZERO,
|
|
13
|
+
} from '../constants';
|
|
7
14
|
import { ec } from './ellipticCurve';
|
|
8
15
|
import { addHexPrefix, buf2hex, utf8ToArray } from './encode';
|
|
9
|
-
import { BigNumberish,
|
|
10
|
-
import { encodeShortString } from './shortString';
|
|
16
|
+
import { BigNumberish, toBN, toHex } from './number';
|
|
11
17
|
|
|
12
|
-
export const transactionPrefix = encodeShortString('StarkNet Transaction');
|
|
13
18
|
export const transactionVersion = 0;
|
|
19
|
+
export const feeTransactionVersion = toBN(2).pow(toBN(128)).add(toBN(transactionVersion));
|
|
14
20
|
|
|
15
21
|
function keccakHex(value: string): string {
|
|
16
22
|
return addHexPrefix(buf2hex(keccak256(utf8ToArray(value))));
|
|
@@ -64,27 +70,65 @@ export function computeHashOnElements(data: BigNumberish[]) {
|
|
|
64
70
|
return [...data, data.length].reduce((x, y) => pedersen([x, y]), 0).toString();
|
|
65
71
|
}
|
|
66
72
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
transactions: Call[],
|
|
70
|
-
nonce: string,
|
|
71
|
-
maxFee: string
|
|
72
|
-
) {
|
|
73
|
-
const hashArray = transactions
|
|
74
|
-
.map(({ contractAddress, entrypoint, calldata }) => [
|
|
75
|
-
contractAddress,
|
|
76
|
-
getSelectorFromName(entrypoint),
|
|
77
|
-
computeHashOnElements(calldata || []),
|
|
78
|
-
])
|
|
79
|
-
.map(bigNumberishArrayToDecimalStringArray)
|
|
80
|
-
.map(computeHashOnElements);
|
|
73
|
+
// following implementation is based on this python implementation:
|
|
74
|
+
// https://github.com/starkware-libs/cairo-lang/blob/b614d1867c64f3fb2cf4a4879348cfcf87c3a5a7/src/starkware/starknet/core/os/transaction_hash/transaction_hash.py
|
|
81
75
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
76
|
+
export function calculateTransactionHashCommon(
|
|
77
|
+
txHashPrefix: TransactionHashPrefix,
|
|
78
|
+
version: BigNumberish,
|
|
79
|
+
contractAddress: BigNumberish,
|
|
80
|
+
entryPointSelector: BigNumberish,
|
|
81
|
+
calldata: BigNumberish[],
|
|
82
|
+
maxFee: BigNumberish,
|
|
83
|
+
chainId: StarknetChainId,
|
|
84
|
+
additionalData: BigNumberish[] = []
|
|
85
|
+
): string {
|
|
86
|
+
const calldataHash = computeHashOnElements(calldata);
|
|
87
|
+
const dataToHash = [
|
|
88
|
+
txHashPrefix,
|
|
89
|
+
version,
|
|
90
|
+
contractAddress,
|
|
91
|
+
entryPointSelector,
|
|
92
|
+
calldataHash,
|
|
87
93
|
maxFee,
|
|
88
|
-
|
|
89
|
-
|
|
94
|
+
chainId,
|
|
95
|
+
...additionalData,
|
|
96
|
+
];
|
|
97
|
+
return computeHashOnElements(dataToHash);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function calculateDeployTransactionHash(
|
|
101
|
+
contractAddress: BigNumberish,
|
|
102
|
+
constructorCalldata: BigNumberish[],
|
|
103
|
+
version: BigNumberish,
|
|
104
|
+
chainId: StarknetChainId
|
|
105
|
+
): string {
|
|
106
|
+
return calculateTransactionHashCommon(
|
|
107
|
+
TransactionHashPrefix.DEPLOY,
|
|
108
|
+
version,
|
|
109
|
+
contractAddress,
|
|
110
|
+
getSelectorFromName('constructor'),
|
|
111
|
+
constructorCalldata,
|
|
112
|
+
ZERO,
|
|
113
|
+
chainId
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function calculcateTransactionHash(
|
|
118
|
+
contractAddress: BigNumberish,
|
|
119
|
+
version: BigNumberish,
|
|
120
|
+
entryPointSelector: BigNumberish,
|
|
121
|
+
calldata: BigNumberish[],
|
|
122
|
+
maxFee: BigNumberish,
|
|
123
|
+
chainId: StarknetChainId
|
|
124
|
+
): string {
|
|
125
|
+
return calculateTransactionHashCommon(
|
|
126
|
+
TransactionHashPrefix.INVOKE,
|
|
127
|
+
version,
|
|
128
|
+
contractAddress,
|
|
129
|
+
entryPointSelector,
|
|
130
|
+
calldata,
|
|
131
|
+
maxFee,
|
|
132
|
+
chainId
|
|
133
|
+
);
|
|
90
134
|
}
|