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.
Files changed (80) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +18 -53
  3. package/__mocks__/ArgentAccount.json +32022 -38726
  4. package/__tests__/accountContract.test.ts +42 -32
  5. package/__tests__/contract.test.ts +20 -6
  6. package/__tests__/utils/__snapshots__/utils.browser.test.ts.snap +2 -2
  7. package/__tests__/utils/__snapshots__/utils.test.ts.snap +2 -2
  8. package/__tests__/utils/ellipticalCurve.test.ts +26 -8
  9. package/__tests__/utils/transactionHash.test.ts +17 -0
  10. package/__tests__/utils/utils.test.ts +10 -0
  11. package/account/default.d.ts +10 -6
  12. package/account/default.js +32 -39
  13. package/account/interface.d.ts +2 -0
  14. package/constants.d.ts +9 -0
  15. package/constants.js +13 -0
  16. package/contract/contractFactory.d.ts +5 -5
  17. package/contract/default.js +14 -2
  18. package/dist/account/default.d.ts +6 -6
  19. package/dist/account/default.js +32 -23
  20. package/dist/account/interface.d.ts +2 -0
  21. package/dist/constants.d.ts +9 -0
  22. package/dist/constants.js +12 -1
  23. package/dist/contract/contractFactory.d.ts +5 -5
  24. package/dist/contract/default.js +14 -2
  25. package/dist/index.d.ts +1 -0
  26. package/dist/index.js +2 -1
  27. package/dist/provider/default.d.ts +4 -1
  28. package/dist/provider/default.js +20 -1
  29. package/dist/provider/interface.d.ts +2 -0
  30. package/dist/signer/default.js +4 -2
  31. package/dist/signer/ledger.js +4 -2
  32. package/dist/types/api.d.ts +1 -0
  33. package/dist/types/lib.d.ts +1 -0
  34. package/dist/types/signer.d.ts +2 -0
  35. package/dist/utils/hash.d.ts +5 -3
  36. package/dist/utils/hash.js +25 -23
  37. package/dist/utils/stark.d.ts +3 -0
  38. package/dist/utils/stark.js +8 -1
  39. package/dist/utils/transaction.d.ts +2 -0
  40. package/dist/utils/transaction.js +5 -1
  41. package/dist/utils/typedData/index.d.ts +2 -2
  42. package/dist/utils/typedData/types.d.ts +3 -3
  43. package/dist/utils/typedData/utils.d.ts +1 -1
  44. package/index.d.ts +1 -0
  45. package/index.js +2 -0
  46. package/package.json +1 -1
  47. package/provider/default.d.ts +4 -1
  48. package/provider/default.js +21 -1
  49. package/provider/interface.d.ts +2 -0
  50. package/signer/default.js +12 -5
  51. package/signer/ledger.js +12 -5
  52. package/src/account/default.ts +38 -17
  53. package/src/account/interface.ts +3 -0
  54. package/src/constants.ts +10 -0
  55. package/src/contract/contractFactory.ts +5 -5
  56. package/src/contract/default.ts +13 -2
  57. package/src/index.ts +1 -0
  58. package/src/provider/default.ts +23 -2
  59. package/src/provider/interface.ts +3 -0
  60. package/src/signer/default.ts +10 -5
  61. package/src/signer/ledger.ts +10 -5
  62. package/src/types/api.ts +1 -0
  63. package/src/types/lib.ts +1 -0
  64. package/src/types/signer.ts +2 -0
  65. package/src/utils/hash.ts +70 -26
  66. package/src/utils/stark.ts +8 -1
  67. package/src/utils/transaction.ts +7 -0
  68. package/types/api.d.ts +1 -0
  69. package/types/lib.d.ts +1 -0
  70. package/types/signer.d.ts +2 -0
  71. package/utils/hash.d.ts +25 -7
  72. package/utils/hash.js +60 -26
  73. package/utils/stark.d.ts +4 -0
  74. package/utils/stark.js +13 -1
  75. package/utils/transaction.d.ts +5 -0
  76. package/utils/transaction.js +12 -1
  77. package/utils/typedData/index.d.ts +2 -2
  78. package/utils/typedData/types.d.ts +3 -3
  79. package/utils/typedData/utils.d.ts +1 -1
  80. 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
- msgHash = (0, hash_1.hashMulticall)(
206
- transactionsDetail.walletAddress,
206
+ calldata = (0, transaction_1.fromCallsToExecuteCalldataWithNonce)(
207
207
  transactions,
208
- transactionsDetail.nonce.toString(),
209
- transactionsDetail.maxFee.toString()
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
  });
@@ -1,6 +1,7 @@
1
1
  import assert from 'minimalistic-assert';
2
2
 
3
- import { Provider } from '../provider';
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 { compileCalldata } from '../utils/stark';
26
- import { fromCallsToExecuteCalldata } from '../utils/transaction';
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
- private signer: SignerInterface;
36
+ public signer: SignerInterface;
34
37
 
35
- constructor(provider: Provider, address: string, keyPairOrSigner: KeyPair | SignerInterface) {
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 signerDetails = {
66
+ const version = toBN(feeTransactionVersion);
67
+
68
+ const signerDetails: InvocationsSignerDetails = {
60
69
  walletAddress: this.address,
61
70
  nonce: toBN(nonce),
62
- maxFee: toBN('0'),
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 = [...fromCallsToExecuteCalldata(transactions), signerDetails.nonce.toString()];
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
- const maxFee =
95
- transactionsDetail.maxFee ?? (await this.estimateFee(transactions, { nonce })).amount;
96
- const signerDetails = {
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 = [...fromCallsToExecuteCalldata(transactions), signerDetails.nonce.toString()];
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
- transactionPrefix,
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 JSON object
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 given hash
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
@@ -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 { Account } from '../account';
4
- import { Provider, defaultProvider } from '../provider';
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: Provider | Account;
14
+ providerOrAccount: ProviderInterface | AccountInterface;
15
15
 
16
16
  constructor(
17
17
  compiledContract: CompiledContract,
18
- providerOrAccount: Provider | Account = defaultProvider,
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: Provider | Account): ContractFactory {
62
+ connect(providerOrAccount: ProviderInterface | AccountInterface): ContractFactory {
63
63
  this.providerOrAccount = providerOrAccount;
64
64
  return this;
65
65
  }
@@ -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
- return contract.invoke(functionAbi.name, args);
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.structMemberNum(member.type);
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';
@@ -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
- constructor(optionsOrProvider: ProviderOptions | Provider = { network: 'goerli-alpha' }) {
53
- if (optionsOrProvider instanceof Provider) {
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
  *
@@ -1,6 +1,7 @@
1
1
  import { Abi, Invocation, InvocationsSignerDetails, KeyPair, Signature } from '../types';
2
2
  import { getStarkKey, sign } from '../utils/ellipticCurve';
3
- import { hashMulticall } from '../utils/hash';
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 msgHash = hashMulticall(
29
+ const calldata = fromCallsToExecuteCalldataWithNonce(transactions, transactionsDetail.nonce);
30
+
31
+ const msgHash = calculcateTransactionHash(
29
32
  transactionsDetail.walletAddress,
30
- transactions,
31
- transactionsDetail.nonce.toString(),
32
- transactionsDetail.maxFee.toString()
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);
@@ -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 { hashMulticall } from '../utils/hash';
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 msgHash = hashMulticall(
50
+ const calldata = fromCallsToExecuteCalldataWithNonce(transactions, transactionsDetail.nonce);
51
+
52
+ const msgHash = calculcateTransactionHash(
50
53
  transactionsDetail.walletAddress,
51
- transactions,
52
- transactionsDetail.nonce.toString(),
53
- transactionsDetail.maxFee.toString()
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
@@ -105,6 +105,7 @@ export type InvokeFunctionTransaction = {
105
105
  calldata?: RawCalldata;
106
106
  nonce?: BigNumberish;
107
107
  max_fee?: BigNumberish;
108
+ version?: BigNumberish;
108
109
  };
109
110
 
110
111
  export type InvokeFunctionTrace = {
package/src/types/lib.ts CHANGED
@@ -24,6 +24,7 @@ export type Call = Omit<Invocation, 'signature'>;
24
24
  export type InvocationsDetails = {
25
25
  nonce?: BigNumberish;
26
26
  maxFee?: BigNumberish;
27
+ version?: BigNumberish;
27
28
  };
28
29
 
29
30
  export type Status =
@@ -1,5 +1,7 @@
1
+ import { StarknetChainId } from '../constants';
1
2
  import { InvocationsDetails } from './lib';
2
3
 
3
4
  export interface InvocationsSignerDetails extends Required<InvocationsDetails> {
4
5
  walletAddress: string;
6
+ chainId: StarknetChainId;
5
7
  }
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 { CONSTANT_POINTS, FIELD_PRIME, MASK_250, ONE, ZERO } from '../constants';
6
- import { Call } from '../types';
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, bigNumberishArrayToDecimalStringArray, toBN, toHex } from './number';
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
- export function hashMulticall(
68
- account: string,
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
- return computeHashOnElements([
83
- transactionPrefix,
84
- account,
85
- computeHashOnElements(hashArray),
86
- nonce,
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
- transactionVersion,
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
  }
@@ -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
+ }
@@ -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
@@ -101,6 +101,7 @@ export declare type InvokeFunctionTransaction = {
101
101
  calldata?: RawCalldata;
102
102
  nonce?: BigNumberish;
103
103
  max_fee?: BigNumberish;
104
+ version?: BigNumberish;
104
105
  };
105
106
  export declare type InvokeFunctionTrace = {
106
107
  caller_address: string;
package/types/lib.d.ts CHANGED
@@ -19,6 +19,7 @@ export declare type Call = Omit<Invocation, 'signature'>;
19
19
  export declare type InvocationsDetails = {
20
20
  nonce?: BigNumberish;
21
21
  maxFee?: BigNumberish;
22
+ version?: BigNumberish;
22
23
  };
23
24
  export declare type Status =
24
25
  | 'NOT_RECEIVED'
package/types/signer.d.ts CHANGED
@@ -1,4 +1,6 @@
1
+ import { StarknetChainId } from '../constants';
1
2
  import { InvocationsDetails } from './lib';
2
3
  export interface InvocationsSignerDetails extends Required<InvocationsDetails> {
3
4
  walletAddress: string;
5
+ chainId: StarknetChainId;
4
6
  }
package/utils/hash.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import BN from 'bn.js';
2
2
 
3
- import { Call } from '../types';
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 hashMulticall(
26
- account: string,
27
- transactions: Call[],
28
- nonce: string,
29
- maxFee: string
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;