starknet 3.8.0 → 3.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +35 -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 +10 -6
- package/account/default.js +32 -39
- package/account/interface.d.ts +2 -0
- package/constants.d.ts +9 -0
- package/constants.js +13 -0
- package/contract/contractFactory.d.ts +5 -5
- package/contract/default.js +14 -2
- package/dist/account/default.d.ts +6 -6
- package/dist/account/default.js +32 -23
- package/dist/account/interface.d.ts +2 -0
- package/dist/constants.d.ts +9 -0
- package/dist/constants.js +12 -1
- package/dist/contract/contractFactory.d.ts +5 -5
- package/dist/contract/default.js +14 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -1
- package/dist/provider/default.d.ts +4 -1
- package/dist/provider/default.js +20 -1
- package/dist/provider/interface.d.ts +2 -0
- package/dist/signer/default.js +4 -2
- package/dist/signer/ledger.js +4 -2
- package/dist/types/api.d.ts +1 -0
- 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/index.d.ts +1 -0
- package/index.js +2 -0
- package/package.json +1 -1
- package/provider/default.d.ts +4 -1
- package/provider/default.js +21 -1
- package/provider/interface.d.ts +2 -0
- package/signer/default.js +12 -5
- package/signer/ledger.js +12 -5
- package/src/account/default.ts +38 -17
- package/src/account/interface.ts +3 -0
- package/src/constants.ts +10 -0
- package/src/contract/contractFactory.ts +5 -5
- package/src/contract/default.ts +13 -2
- package/src/index.ts +1 -0
- package/src/provider/default.ts +23 -2
- package/src/provider/interface.ts +3 -0
- package/src/signer/default.ts +10 -5
- package/src/signer/ledger.ts +10 -5
- package/src/types/api.ts +1 -0
- 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 +1 -0
- 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/signer/ledger.js
CHANGED
|
@@ -141,6 +141,7 @@ var hw_app_eth_1 = __importDefault(require('@ledgerhq/hw-app-eth'));
|
|
|
141
141
|
var hw_transport_webhid_1 = __importDefault(require('@ledgerhq/hw-transport-webhid'));
|
|
142
142
|
var encode_1 = require('../utils/encode');
|
|
143
143
|
var hash_1 = require('../utils/hash');
|
|
144
|
+
var transaction_1 = require('../utils/transaction');
|
|
144
145
|
var typedData_1 = require('../utils/typedData');
|
|
145
146
|
function hexZeroPad(hash, length) {
|
|
146
147
|
var value = hash;
|
|
@@ -200,13 +201,19 @@ var LedgerBlindSigner = /** @class */ (function () {
|
|
|
200
201
|
};
|
|
201
202
|
LedgerBlindSigner.prototype.signTransaction = function (transactions, transactionsDetail) {
|
|
202
203
|
return __awaiter(this, void 0, void 0, function () {
|
|
203
|
-
var msgHash;
|
|
204
|
+
var calldata, msgHash;
|
|
204
205
|
return __generator(this, function (_a) {
|
|
205
|
-
|
|
206
|
-
transactionsDetail.walletAddress,
|
|
206
|
+
calldata = (0, transaction_1.fromCallsToExecuteCalldataWithNonce)(
|
|
207
207
|
transactions,
|
|
208
|
-
transactionsDetail.nonce
|
|
209
|
-
|
|
208
|
+
transactionsDetail.nonce
|
|
209
|
+
);
|
|
210
|
+
msgHash = (0, hash_1.calculcateTransactionHash)(
|
|
211
|
+
transactionsDetail.walletAddress,
|
|
212
|
+
transactionsDetail.version,
|
|
213
|
+
(0, hash_1.getSelectorFromName)('__execute__'),
|
|
214
|
+
calldata,
|
|
215
|
+
transactionsDetail.maxFee,
|
|
216
|
+
transactionsDetail.chainId
|
|
210
217
|
);
|
|
211
218
|
return [2 /*return*/, this.sign(msgHash)];
|
|
212
219
|
});
|
package/src/account/default.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import assert from 'minimalistic-assert';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { ZERO } from '../constants';
|
|
4
|
+
import { Provider, ProviderInterface } from '../provider';
|
|
4
5
|
import { BlockIdentifier } from '../provider/utils';
|
|
5
6
|
import { Signer, SignerInterface } from '../signer';
|
|
6
7
|
import {
|
|
@@ -9,6 +10,7 @@ import {
|
|
|
9
10
|
Call,
|
|
10
11
|
EstimateFeeResponse,
|
|
11
12
|
InvocationsDetails,
|
|
13
|
+
InvocationsSignerDetails,
|
|
12
14
|
InvokeFunctionTransaction,
|
|
13
15
|
KeyPair,
|
|
14
16
|
Signature,
|
|
@@ -17,22 +19,27 @@ import {
|
|
|
17
19
|
import { sign } from '../utils/ellipticCurve';
|
|
18
20
|
import {
|
|
19
21
|
computeHashOnElements,
|
|
22
|
+
feeTransactionVersion,
|
|
20
23
|
getSelectorFromName,
|
|
21
|
-
transactionPrefix,
|
|
22
24
|
transactionVersion,
|
|
23
25
|
} from '../utils/hash';
|
|
24
26
|
import { BigNumberish, bigNumberishArrayToDecimalStringArray, toBN, toHex } from '../utils/number';
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
+
import { encodeShortString } from '../utils/shortString';
|
|
28
|
+
import { compileCalldata, estimatedFeeToMaxFee } from '../utils/stark';
|
|
29
|
+
import { fromCallsToExecuteCalldataWithNonce } from '../utils/transaction';
|
|
27
30
|
import { TypedData, getMessageHash } from '../utils/typedData';
|
|
28
31
|
import { AccountInterface } from './interface';
|
|
29
32
|
|
|
30
33
|
export class Account extends Provider implements AccountInterface {
|
|
31
34
|
public address: string;
|
|
32
35
|
|
|
33
|
-
|
|
36
|
+
public signer: SignerInterface;
|
|
34
37
|
|
|
35
|
-
constructor(
|
|
38
|
+
constructor(
|
|
39
|
+
provider: ProviderInterface,
|
|
40
|
+
address: string,
|
|
41
|
+
keyPairOrSigner: KeyPair | SignerInterface
|
|
42
|
+
) {
|
|
36
43
|
super(provider);
|
|
37
44
|
this.signer =
|
|
38
45
|
'getPubKey' in keyPairOrSigner ? keyPairOrSigner : new Signer(keyPairOrSigner as KeyPair);
|
|
@@ -56,14 +63,19 @@ export class Account extends Provider implements AccountInterface {
|
|
|
56
63
|
): Promise<EstimateFeeResponse> {
|
|
57
64
|
const transactions = Array.isArray(calls) ? calls : [calls];
|
|
58
65
|
const nonce = providedNonce ?? (await this.getNonce());
|
|
59
|
-
const
|
|
66
|
+
const version = toBN(feeTransactionVersion);
|
|
67
|
+
|
|
68
|
+
const signerDetails: InvocationsSignerDetails = {
|
|
60
69
|
walletAddress: this.address,
|
|
61
70
|
nonce: toBN(nonce),
|
|
62
|
-
maxFee:
|
|
71
|
+
maxFee: ZERO,
|
|
72
|
+
version,
|
|
73
|
+
chainId: this.chainId,
|
|
63
74
|
};
|
|
75
|
+
|
|
64
76
|
const signature = await this.signer.signTransaction(transactions, signerDetails);
|
|
65
77
|
|
|
66
|
-
const calldata =
|
|
78
|
+
const calldata = fromCallsToExecuteCalldataWithNonce(transactions, nonce);
|
|
67
79
|
return this.fetchEndpoint(
|
|
68
80
|
'estimate_fee',
|
|
69
81
|
{ blockIdentifier },
|
|
@@ -71,6 +83,7 @@ export class Account extends Provider implements AccountInterface {
|
|
|
71
83
|
contract_address: this.address,
|
|
72
84
|
entry_point_selector: getSelectorFromName('__execute__'),
|
|
73
85
|
calldata,
|
|
86
|
+
version: toHex(version),
|
|
74
87
|
signature: bigNumberishArrayToDecimalStringArray(signature),
|
|
75
88
|
}
|
|
76
89
|
);
|
|
@@ -91,17 +104,25 @@ export class Account extends Provider implements AccountInterface {
|
|
|
91
104
|
): Promise<AddTransactionResponse> {
|
|
92
105
|
const transactions = Array.isArray(calls) ? calls : [calls];
|
|
93
106
|
const nonce = toBN(transactionsDetail.nonce ?? (await this.getNonce()));
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
107
|
+
let maxFee: BigNumberish = '0';
|
|
108
|
+
if (transactionsDetail.maxFee || transactionsDetail.maxFee === 0) {
|
|
109
|
+
maxFee = transactionsDetail.maxFee;
|
|
110
|
+
} else {
|
|
111
|
+
const estimatedFee = (await this.estimateFee(transactions, { nonce })).amount;
|
|
112
|
+
maxFee = estimatedFeeToMaxFee(estimatedFee).toString();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const signerDetails: InvocationsSignerDetails = {
|
|
97
116
|
walletAddress: this.address,
|
|
98
117
|
nonce,
|
|
99
118
|
maxFee,
|
|
119
|
+
version: toBN(transactionVersion),
|
|
120
|
+
chainId: this.chainId,
|
|
100
121
|
};
|
|
101
122
|
|
|
102
123
|
const signature = await this.signer.signTransaction(transactions, signerDetails, abis);
|
|
103
124
|
|
|
104
|
-
const calldata =
|
|
125
|
+
const calldata = fromCallsToExecuteCalldataWithNonce(transactions, nonce);
|
|
105
126
|
return this.fetchEndpoint('add_transaction', undefined, {
|
|
106
127
|
type: 'INVOKE_FUNCTION',
|
|
107
128
|
contract_address: this.address,
|
|
@@ -151,7 +172,7 @@ export class Account extends Provider implements AccountInterface {
|
|
|
151
172
|
.map(computeHashOnElements);
|
|
152
173
|
|
|
153
174
|
return computeHashOnElements([
|
|
154
|
-
|
|
175
|
+
encodeShortString('StarkNet Transaction'),
|
|
155
176
|
account,
|
|
156
177
|
computeHashOnElements(hashArray),
|
|
157
178
|
nonce,
|
|
@@ -233,7 +254,8 @@ export class Account extends Provider implements AccountInterface {
|
|
|
233
254
|
}
|
|
234
255
|
|
|
235
256
|
/**
|
|
236
|
-
* Verify a signature of a
|
|
257
|
+
* Verify a signature of a given hash
|
|
258
|
+
* @warning This method is not recommended, use verifyMessage instead
|
|
237
259
|
*
|
|
238
260
|
* @param hash - JSON object to be verified
|
|
239
261
|
* @param signature - signature of the JSON object
|
|
@@ -257,8 +279,7 @@ export class Account extends Provider implements AccountInterface {
|
|
|
257
279
|
}
|
|
258
280
|
|
|
259
281
|
/**
|
|
260
|
-
* Verify a signature of a
|
|
261
|
-
* @warning This method is not recommended, use verifyMessage instead
|
|
282
|
+
* Verify a signature of a JSON object
|
|
262
283
|
*
|
|
263
284
|
* @param hash - hash to be verified
|
|
264
285
|
* @param signature - signature of the hash
|
package/src/account/interface.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ProviderInterface } from '../provider';
|
|
2
|
+
import { SignerInterface } from '../signer';
|
|
2
3
|
import {
|
|
3
4
|
Abi,
|
|
4
5
|
AddTransactionResponse,
|
|
@@ -15,6 +16,8 @@ import { TypedData } from '../utils/typedData/types';
|
|
|
15
16
|
export abstract class AccountInterface extends ProviderInterface {
|
|
16
17
|
public abstract address: string;
|
|
17
18
|
|
|
19
|
+
public abstract signer: SignerInterface;
|
|
20
|
+
|
|
18
21
|
/**
|
|
19
22
|
* Deploys a given compiled contract (json) to starknet
|
|
20
23
|
*
|
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.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from 'minimalistic-assert';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { AccountInterface } from '../account';
|
|
4
|
+
import { ProviderInterface, defaultProvider } from '../provider';
|
|
5
5
|
import { Abi, CompiledContract, RawCalldata } from '../types';
|
|
6
6
|
import { BigNumberish } from '../utils/number';
|
|
7
7
|
import { Contract } from './default';
|
|
@@ -11,11 +11,11 @@ export class ContractFactory {
|
|
|
11
11
|
|
|
12
12
|
compiledContract: CompiledContract;
|
|
13
13
|
|
|
14
|
-
providerOrAccount:
|
|
14
|
+
providerOrAccount: ProviderInterface | AccountInterface;
|
|
15
15
|
|
|
16
16
|
constructor(
|
|
17
17
|
compiledContract: CompiledContract,
|
|
18
|
-
providerOrAccount:
|
|
18
|
+
providerOrAccount: ProviderInterface | AccountInterface = defaultProvider,
|
|
19
19
|
abi: Abi = compiledContract.abi // abi can be different from the deployed contract ie for proxy contracts
|
|
20
20
|
) {
|
|
21
21
|
this.abi = abi;
|
|
@@ -59,7 +59,7 @@ export class ContractFactory {
|
|
|
59
59
|
*
|
|
60
60
|
* @param providerOrAccount - new Provider or Account to attach to
|
|
61
61
|
*/
|
|
62
|
-
connect(providerOrAccount:
|
|
62
|
+
connect(providerOrAccount: ProviderInterface | AccountInterface): ContractFactory {
|
|
63
63
|
this.providerOrAccount = providerOrAccount;
|
|
64
64
|
return this;
|
|
65
65
|
}
|
package/src/contract/default.ts
CHANGED
|
@@ -46,7 +46,18 @@ function buildCall(contract: Contract, functionAbi: FunctionAbi): AsyncContractF
|
|
|
46
46
|
*/
|
|
47
47
|
function buildInvoke(contract: Contract, functionAbi: FunctionAbi): AsyncContractFunction {
|
|
48
48
|
return async function (...args: Array<any>): Promise<any> {
|
|
49
|
-
|
|
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);
|
|
50
61
|
};
|
|
51
62
|
}
|
|
52
63
|
|
|
@@ -338,7 +349,7 @@ export class Contract implements ContractInterface {
|
|
|
338
349
|
if (member.type === 'felt') {
|
|
339
350
|
return acc + 1;
|
|
340
351
|
}
|
|
341
|
-
return acc + this.
|
|
352
|
+
return acc + this.calculateStructMembers(member.type);
|
|
342
353
|
}, 0);
|
|
343
354
|
}
|
|
344
355
|
|
package/src/index.ts
CHANGED
|
@@ -15,6 +15,7 @@ export * as encode from './utils/encode';
|
|
|
15
15
|
export * as hash from './utils/hash';
|
|
16
16
|
export * as json from './utils/json';
|
|
17
17
|
export * as number from './utils/number';
|
|
18
|
+
export * as transaction from './utils/transaction';
|
|
18
19
|
export * as stark from './utils/stark';
|
|
19
20
|
export * as ec from './utils/ellipticCurve';
|
|
20
21
|
export * as uint256 from './utils/uint256';
|
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,24 @@ export class Provider implements ProviderInterface {
|
|
|
49
50
|
|
|
50
51
|
public gatewayUrl: string;
|
|
51
52
|
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
public chainId: StarknetChainId;
|
|
54
|
+
|
|
55
|
+
constructor(
|
|
56
|
+
optionsOrProvider: ProviderOptions | ProviderInterface = { network: 'goerli-alpha' }
|
|
57
|
+
) {
|
|
58
|
+
if (optionsOrProvider instanceof ProviderInterface) {
|
|
54
59
|
this.baseUrl = optionsOrProvider.baseUrl;
|
|
55
60
|
this.feederGatewayUrl = optionsOrProvider.feederGatewayUrl;
|
|
56
61
|
this.gatewayUrl = optionsOrProvider.gatewayUrl;
|
|
62
|
+
this.chainId =
|
|
63
|
+
optionsOrProvider.chainId ?? Provider.getChainIdFromBaseUrl(optionsOrProvider.baseUrl);
|
|
57
64
|
} else {
|
|
58
65
|
const baseUrl =
|
|
59
66
|
'baseUrl' in optionsOrProvider
|
|
60
67
|
? optionsOrProvider.baseUrl
|
|
61
68
|
: Provider.getNetworkFromName(optionsOrProvider.network);
|
|
62
69
|
this.baseUrl = baseUrl;
|
|
70
|
+
this.chainId = Provider.getChainIdFromBaseUrl(baseUrl);
|
|
63
71
|
this.feederGatewayUrl = urljoin(baseUrl, 'feeder_gateway');
|
|
64
72
|
this.gatewayUrl = urljoin(baseUrl, 'gateway');
|
|
65
73
|
}
|
|
@@ -75,6 +83,19 @@ export class Provider implements ProviderInterface {
|
|
|
75
83
|
}
|
|
76
84
|
}
|
|
77
85
|
|
|
86
|
+
protected static getChainIdFromBaseUrl(baseUrl: string): StarknetChainId {
|
|
87
|
+
try {
|
|
88
|
+
const url = new URL(baseUrl);
|
|
89
|
+
if (url.host.includes('mainnet.starknet.io')) {
|
|
90
|
+
return StarknetChainId.MAINNET;
|
|
91
|
+
}
|
|
92
|
+
} catch {
|
|
93
|
+
// eslint-disable-next-line no-console
|
|
94
|
+
console.error(`Could not parse baseUrl: ${baseUrl}`);
|
|
95
|
+
}
|
|
96
|
+
return StarknetChainId.TESTNET;
|
|
97
|
+
}
|
|
98
|
+
|
|
78
99
|
private getFetchUrl(endpoint: keyof Endpoints) {
|
|
79
100
|
const gatewayUrlEndpoints = ['add_transaction'];
|
|
80
101
|
|
|
@@ -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/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
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
|
}
|
package/src/utils/stark.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import BN from 'bn.js';
|
|
1
2
|
import { gzip } from 'pako';
|
|
2
3
|
|
|
3
4
|
import { Calldata, CompressedProgram, Program, RawArgs, Signature } from '../types';
|
|
4
5
|
import { genKeyPair, getStarkKey } from './ellipticCurve';
|
|
5
6
|
import { addHexPrefix, btoaUniversal } from './encode';
|
|
6
7
|
import { stringify } from './json';
|
|
7
|
-
import { toBN } from './number';
|
|
8
|
+
import { BigNumberish, toBN } from './number';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Function to compress compiled cairo program
|
|
@@ -48,3 +49,9 @@ export function compileCalldata(args: RawArgs): Calldata {
|
|
|
48
49
|
return toBN(value).toString();
|
|
49
50
|
});
|
|
50
51
|
}
|
|
52
|
+
|
|
53
|
+
export function estimatedFeeToMaxFee(estimatedFee: BigNumberish, overhead: number = 0.15): BN {
|
|
54
|
+
// BN can only handle Integers, so we need to do all calulations with integers
|
|
55
|
+
const overHeadPercent = Math.round((1 + overhead) * 100);
|
|
56
|
+
return toBN(estimatedFee).mul(toBN(overHeadPercent)).div(toBN(100));
|
|
57
|
+
}
|
package/src/utils/transaction.ts
CHANGED
|
@@ -47,3 +47,10 @@ export const fromCallsToExecuteCalldata = (calls: Call[]): string[] => {
|
|
|
47
47
|
...calldata,
|
|
48
48
|
];
|
|
49
49
|
};
|
|
50
|
+
|
|
51
|
+
export const fromCallsToExecuteCalldataWithNonce = (
|
|
52
|
+
calls: Call[],
|
|
53
|
+
nonce: BigNumberish
|
|
54
|
+
): string[] => {
|
|
55
|
+
return [...fromCallsToExecuteCalldata(calls), toBN(nonce).toString()];
|
|
56
|
+
};
|
package/types/api.d.ts
CHANGED
package/types/lib.d.ts
CHANGED
package/types/signer.d.ts
CHANGED
package/utils/hash.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import BN from 'bn.js';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { StarknetChainId, TransactionHashPrefix } from '../constants';
|
|
4
4
|
import { BigNumberish } from './number';
|
|
5
|
-
export declare const transactionPrefix: string;
|
|
6
5
|
export declare const transactionVersion = 0;
|
|
6
|
+
export declare const feeTransactionVersion: BN;
|
|
7
7
|
/**
|
|
8
8
|
* Function to get the starknet keccak hash from a string
|
|
9
9
|
*
|
|
@@ -22,9 +22,27 @@ export declare function starknetKeccak(value: string): BN;
|
|
|
22
22
|
export declare function getSelectorFromName(funcName: string): string;
|
|
23
23
|
export declare function pedersen(input: [BigNumberish, BigNumberish]): string;
|
|
24
24
|
export declare function computeHashOnElements(data: BigNumberish[]): string;
|
|
25
|
-
export declare function
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
export declare function calculateTransactionHashCommon(
|
|
26
|
+
txHashPrefix: TransactionHashPrefix,
|
|
27
|
+
version: BigNumberish,
|
|
28
|
+
contractAddress: BigNumberish,
|
|
29
|
+
entryPointSelector: BigNumberish,
|
|
30
|
+
calldata: BigNumberish[],
|
|
31
|
+
maxFee: BigNumberish,
|
|
32
|
+
chainId: StarknetChainId,
|
|
33
|
+
additionalData?: BigNumberish[]
|
|
34
|
+
): string;
|
|
35
|
+
export declare function calculateDeployTransactionHash(
|
|
36
|
+
contractAddress: BigNumberish,
|
|
37
|
+
constructorCalldata: BigNumberish[],
|
|
38
|
+
version: BigNumberish,
|
|
39
|
+
chainId: StarknetChainId
|
|
40
|
+
): string;
|
|
41
|
+
export declare function calculcateTransactionHash(
|
|
42
|
+
contractAddress: BigNumberish,
|
|
43
|
+
version: BigNumberish,
|
|
44
|
+
entryPointSelector: BigNumberish,
|
|
45
|
+
calldata: BigNumberish[],
|
|
46
|
+
maxFee: BigNumberish,
|
|
47
|
+
chainId: StarknetChainId
|
|
30
48
|
): string;
|