starknet 3.9.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/__mocks__/ArgentAccount.json +32022 -38726
  3. package/__tests__/accountContract.test.ts +42 -32
  4. package/__tests__/contract.test.ts +20 -6
  5. package/__tests__/utils/__snapshots__/utils.browser.test.ts.snap +2 -2
  6. package/__tests__/utils/__snapshots__/utils.test.ts.snap +2 -2
  7. package/__tests__/utils/ellipticalCurve.test.ts +26 -8
  8. package/__tests__/utils/transactionHash.test.ts +17 -0
  9. package/account/default.js +10 -21
  10. package/constants.d.ts +9 -0
  11. package/constants.js +13 -0
  12. package/dist/account/default.js +9 -5
  13. package/dist/constants.d.ts +9 -0
  14. package/dist/constants.js +12 -1
  15. package/dist/provider/default.d.ts +3 -0
  16. package/dist/provider/default.js +18 -0
  17. package/dist/provider/interface.d.ts +2 -0
  18. package/dist/signer/default.js +4 -2
  19. package/dist/signer/ledger.js +4 -2
  20. package/dist/types/signer.d.ts +2 -0
  21. package/dist/utils/hash.d.ts +4 -3
  22. package/dist/utils/hash.js +24 -24
  23. package/dist/utils/transaction.d.ts +2 -0
  24. package/dist/utils/transaction.js +5 -1
  25. package/dist/utils/typedData/index.d.ts +2 -2
  26. package/dist/utils/typedData/types.d.ts +3 -3
  27. package/dist/utils/typedData/utils.d.ts +1 -1
  28. package/package.json +1 -1
  29. package/provider/default.d.ts +3 -0
  30. package/provider/default.js +19 -0
  31. package/provider/interface.d.ts +2 -0
  32. package/signer/default.js +12 -6
  33. package/signer/ledger.js +12 -5
  34. package/src/account/default.ts +16 -9
  35. package/src/constants.ts +10 -0
  36. package/src/provider/default.ts +19 -0
  37. package/src/provider/interface.ts +3 -0
  38. package/src/signer/default.ts +10 -6
  39. package/src/signer/ledger.ts +10 -5
  40. package/src/types/signer.ts +2 -0
  41. package/src/utils/hash.ts +68 -26
  42. package/src/utils/transaction.ts +7 -0
  43. package/types/signer.d.ts +2 -0
  44. package/utils/hash.d.ts +24 -8
  45. package/utils/hash.js +55 -28
  46. package/utils/transaction.d.ts +5 -0
  47. package/utils/transaction.js +12 -1
  48. package/utils/typedData/index.d.ts +2 -2
  49. package/utils/typedData/types.d.ts +3 -3
  50. package/utils/typedData/utils.d.ts +1 -1
  51. package/__tests__/constancts.ts +0 -2
@@ -175,6 +175,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
175
175
  exports.Provider = void 0;
176
176
  var axios_1 = __importDefault(require('axios'));
177
177
  var url_join_1 = __importDefault(require('url-join'));
178
+ var constants_1 = require('../constants');
178
179
  var hash_1 = require('../utils/hash');
179
180
  var json_1 = require('../utils/json');
180
181
  var number_1 = require('../utils/number');
@@ -203,16 +204,22 @@ var Provider = /** @class */ (function () {
203
204
  if (optionsOrProvider === void 0) {
204
205
  optionsOrProvider = { network: 'goerli-alpha' };
205
206
  }
207
+ var _a;
206
208
  if (optionsOrProvider instanceof Provider) {
207
209
  this.baseUrl = optionsOrProvider.baseUrl;
208
210
  this.feederGatewayUrl = optionsOrProvider.feederGatewayUrl;
209
211
  this.gatewayUrl = optionsOrProvider.gatewayUrl;
212
+ this.chainId =
213
+ (_a = optionsOrProvider.chainId) !== null && _a !== void 0
214
+ ? _a
215
+ : Provider.getChainIdFromBaseUrl(optionsOrProvider.baseUrl);
210
216
  } else {
211
217
  var baseUrl =
212
218
  'baseUrl' in optionsOrProvider
213
219
  ? optionsOrProvider.baseUrl
214
220
  : Provider.getNetworkFromName(optionsOrProvider.network);
215
221
  this.baseUrl = baseUrl;
222
+ this.chainId = Provider.getChainIdFromBaseUrl(baseUrl);
216
223
  this.feederGatewayUrl = (0, url_join_1.default)(baseUrl, 'feeder_gateway');
217
224
  this.gatewayUrl = (0, url_join_1.default)(baseUrl, 'gateway');
218
225
  }
@@ -226,6 +233,18 @@ var Provider = /** @class */ (function () {
226
233
  return 'https://alpha4.starknet.io';
227
234
  }
228
235
  };
236
+ Provider.getChainIdFromBaseUrl = function (baseUrl) {
237
+ try {
238
+ var url = new URL(baseUrl);
239
+ if (url.host.includes('mainnet.starknet.io')) {
240
+ return constants_1.StarknetChainId.MAINNET;
241
+ }
242
+ } catch (_a) {
243
+ // eslint-disable-next-line no-console
244
+ console.error('Could not parse baseUrl: ' + baseUrl);
245
+ }
246
+ return constants_1.StarknetChainId.TESTNET;
247
+ };
229
248
  Provider.prototype.getFetchUrl = function (endpoint) {
230
249
  var gatewayUrlEndpoints = ['add_transaction'];
231
250
  return gatewayUrlEndpoints.includes(endpoint) ? this.gatewayUrl : this.feederGatewayUrl;
@@ -1,3 +1,4 @@
1
+ import { StarknetChainId } from '../constants';
1
2
  import type {
2
3
  AddTransactionResponse,
3
4
  Call,
@@ -17,6 +18,7 @@ export declare abstract class ProviderInterface {
17
18
  abstract baseUrl: string;
18
19
  abstract feederGatewayUrl: string;
19
20
  abstract gatewayUrl: string;
21
+ abstract chainId: StarknetChainId;
20
22
  /**
21
23
  * Gets the smart contract address on the goerli testnet.
22
24
  *
package/signer/default.js CHANGED
@@ -134,6 +134,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
134
134
  exports.Signer = void 0;
135
135
  var ellipticCurve_1 = require('../utils/ellipticCurve');
136
136
  var hash_1 = require('../utils/hash');
137
+ var transaction_1 = require('../utils/transaction');
137
138
  var typedData_1 = require('../utils/typedData');
138
139
  var Signer = /** @class */ (function () {
139
140
  function Signer(keyPair) {
@@ -148,17 +149,22 @@ var Signer = /** @class */ (function () {
148
149
  };
149
150
  Signer.prototype.signTransaction = function (transactions, transactionsDetail, abis) {
150
151
  return __awaiter(this, void 0, void 0, function () {
151
- var msgHash;
152
+ var calldata, msgHash;
152
153
  return __generator(this, function (_a) {
153
154
  if (abis && abis.length !== transactions.length) {
154
155
  throw new Error('ABI must be provided for each transaction or no transaction');
155
156
  }
156
- msgHash = (0, hash_1.hashMulticall)(
157
- transactionsDetail.walletAddress,
157
+ calldata = (0, transaction_1.fromCallsToExecuteCalldataWithNonce)(
158
158
  transactions,
159
- transactionsDetail.nonce.toString(),
160
- transactionsDetail.maxFee.toString(),
161
- transactionsDetail.version.toString()
159
+ transactionsDetail.nonce
160
+ );
161
+ msgHash = (0, hash_1.calculcateTransactionHash)(
162
+ transactionsDetail.walletAddress,
163
+ transactionsDetail.version,
164
+ (0, hash_1.getSelectorFromName)('__execute__'),
165
+ calldata,
166
+ transactionsDetail.maxFee,
167
+ transactionsDetail.chainId
162
168
  );
163
169
  return [2 /*return*/, (0, ellipticCurve_1.sign)(this.keyPair, msgHash)];
164
170
  });
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,5 +1,6 @@
1
1
  import assert from 'minimalistic-assert';
2
2
 
3
+ import { ZERO } from '../constants';
3
4
  import { Provider } from '../provider';
4
5
  import { BlockIdentifier } from '../provider/utils';
5
6
  import { Signer, SignerInterface } from '../signer';
@@ -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,
@@ -19,12 +21,12 @@ import {
19
21
  computeHashOnElements,
20
22
  feeTransactionVersion,
21
23
  getSelectorFromName,
22
- transactionPrefix,
23
24
  transactionVersion,
24
25
  } from '../utils/hash';
25
26
  import { BigNumberish, bigNumberishArrayToDecimalStringArray, toBN, toHex } from '../utils/number';
27
+ import { encodeShortString } from '../utils/shortString';
26
28
  import { compileCalldata, estimatedFeeToMaxFee } from '../utils/stark';
27
- import { fromCallsToExecuteCalldata } from '../utils/transaction';
29
+ import { fromCallsToExecuteCalldataWithNonce } from '../utils/transaction';
28
30
  import { TypedData, getMessageHash } from '../utils/typedData';
29
31
  import { AccountInterface } from './interface';
30
32
 
@@ -58,15 +60,18 @@ export class Account extends Provider implements AccountInterface {
58
60
  const transactions = Array.isArray(calls) ? calls : [calls];
59
61
  const nonce = providedNonce ?? (await this.getNonce());
60
62
  const version = toBN(feeTransactionVersion);
61
- const signerDetails = {
63
+
64
+ const signerDetails: InvocationsSignerDetails = {
62
65
  walletAddress: this.address,
63
66
  nonce: toBN(nonce),
64
- maxFee: toBN('0'),
67
+ maxFee: ZERO,
65
68
  version,
69
+ chainId: this.chainId,
66
70
  };
71
+
67
72
  const signature = await this.signer.signTransaction(transactions, signerDetails);
68
73
 
69
- const calldata = [...fromCallsToExecuteCalldata(transactions), signerDetails.nonce.toString()];
74
+ const calldata = fromCallsToExecuteCalldataWithNonce(transactions, nonce);
70
75
  return this.fetchEndpoint(
71
76
  'estimate_fee',
72
77
  { blockIdentifier },
@@ -96,22 +101,24 @@ export class Account extends Provider implements AccountInterface {
96
101
  const transactions = Array.isArray(calls) ? calls : [calls];
97
102
  const nonce = toBN(transactionsDetail.nonce ?? (await this.getNonce()));
98
103
  let maxFee: BigNumberish = '0';
99
- if (transactionsDetail.maxFee) {
104
+ if (transactionsDetail.maxFee || transactionsDetail.maxFee === 0) {
100
105
  maxFee = transactionsDetail.maxFee;
101
106
  } else {
102
107
  const estimatedFee = (await this.estimateFee(transactions, { nonce })).amount;
103
108
  maxFee = estimatedFeeToMaxFee(estimatedFee).toString();
104
109
  }
105
- const signerDetails = {
110
+
111
+ const signerDetails: InvocationsSignerDetails = {
106
112
  walletAddress: this.address,
107
113
  nonce,
108
114
  maxFee,
109
115
  version: toBN(transactionVersion),
116
+ chainId: this.chainId,
110
117
  };
111
118
 
112
119
  const signature = await this.signer.signTransaction(transactions, signerDetails, abis);
113
120
 
114
- const calldata = [...fromCallsToExecuteCalldata(transactions), signerDetails.nonce.toString()];
121
+ const calldata = fromCallsToExecuteCalldataWithNonce(transactions, nonce);
115
122
  return this.fetchEndpoint('add_transaction', undefined, {
116
123
  type: 'INVOKE_FUNCTION',
117
124
  contract_address: this.address,
@@ -161,7 +168,7 @@ export class Account extends Provider implements AccountInterface {
161
168
  .map(computeHashOnElements);
162
169
 
163
170
  return computeHashOnElements([
164
- transactionPrefix,
171
+ encodeShortString('StarkNet Transaction'),
165
172
  account,
166
173
  computeHashOnElements(hashArray),
167
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.
@@ -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
 
@@ -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,12 +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.toString()
33
+ transactionsDetail.version,
34
+ getSelectorFromName('__execute__'),
35
+ calldata,
36
+ transactionsDetail.maxFee,
37
+ transactionsDetail.chainId
34
38
  );
35
39
 
36
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);
@@ -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,14 +2,19 @@ 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;
14
19
  export const feeTransactionVersion = toBN(2).pow(toBN(128)).add(toBN(transactionVersion));
15
20
 
@@ -65,28 +70,65 @@ export function computeHashOnElements(data: BigNumberish[]) {
65
70
  return [...data, data.length].reduce((x, y) => pedersen([x, y]), 0).toString();
66
71
  }
67
72
 
68
- export function hashMulticall(
69
- account: string,
70
- transactions: Call[],
71
- nonce: string,
72
- maxFee: string,
73
- version: string | number = transactionVersion
74
- ) {
75
- const hashArray = transactions
76
- .map(({ contractAddress, entrypoint, calldata }) => [
77
- contractAddress,
78
- getSelectorFromName(entrypoint),
79
- computeHashOnElements(calldata || []),
80
- ])
81
- .map(bigNumberishArrayToDecimalStringArray)
82
- .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
83
75
 
84
- return computeHashOnElements([
85
- transactionPrefix,
86
- account,
87
- computeHashOnElements(hashArray),
88
- 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,
89
93
  maxFee,
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,
90
108
  version,
91
- ]);
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
+ );
92
134
  }
@@ -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/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,8 +1,7 @@
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;
7
6
  export declare const feeTransactionVersion: BN;
8
7
  /**
@@ -23,10 +22,27 @@ export declare function starknetKeccak(value: string): BN;
23
22
  export declare function getSelectorFromName(funcName: string): string;
24
23
  export declare function pedersen(input: [BigNumberish, BigNumberish]): string;
25
24
  export declare function computeHashOnElements(data: BigNumberish[]): string;
26
- export declare function hashMulticall(
27
- account: string,
28
- transactions: Call[],
29
- nonce: string,
30
- maxFee: string,
31
- version?: string | number
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
32
48
  ): string;
package/utils/hash.js CHANGED
@@ -39,14 +39,15 @@ var __importDefault =
39
39
  return mod && mod.__esModule ? mod : { default: mod };
40
40
  };
41
41
  Object.defineProperty(exports, '__esModule', { value: true });
42
- exports.hashMulticall =
42
+ exports.calculcateTransactionHash =
43
+ exports.calculateDeployTransactionHash =
44
+ exports.calculateTransactionHashCommon =
43
45
  exports.computeHashOnElements =
44
46
  exports.pedersen =
45
47
  exports.getSelectorFromName =
46
48
  exports.starknetKeccak =
47
49
  exports.feeTransactionVersion =
48
50
  exports.transactionVersion =
49
- exports.transactionPrefix =
50
51
  void 0;
51
52
  var keccak_1 = require('ethereum-cryptography/keccak');
52
53
  var minimalistic_assert_1 = __importDefault(require('minimalistic-assert'));
@@ -54,8 +55,6 @@ var constants_1 = require('../constants');
54
55
  var ellipticCurve_1 = require('./ellipticCurve');
55
56
  var encode_1 = require('./encode');
56
57
  var number_1 = require('./number');
57
- var shortString_1 = require('./shortString');
58
- exports.transactionPrefix = (0, shortString_1.encodeShortString)('StarkNet Transaction');
59
58
  exports.transactionVersion = 0;
60
59
  exports.feeTransactionVersion = (0, number_1.toBN)(2)
61
60
  .pow((0, number_1.toBN)(128))
@@ -120,30 +119,58 @@ function computeHashOnElements(data) {
120
119
  .toString();
121
120
  }
122
121
  exports.computeHashOnElements = computeHashOnElements;
123
- function hashMulticall(account, transactions, nonce, maxFee, version) {
124
- if (version === void 0) {
125
- version = exports.transactionVersion;
122
+ // following implementation is based on this python implementation:
123
+ // https://github.com/starkware-libs/cairo-lang/blob/b614d1867c64f3fb2cf4a4879348cfcf87c3a5a7/src/starkware/starknet/core/os/transaction_hash/transaction_hash.py
124
+ function calculateTransactionHashCommon(
125
+ txHashPrefix,
126
+ version,
127
+ contractAddress,
128
+ entryPointSelector,
129
+ calldata,
130
+ maxFee,
131
+ chainId,
132
+ additionalData
133
+ ) {
134
+ if (additionalData === void 0) {
135
+ additionalData = [];
126
136
  }
127
- var hashArray = transactions
128
- .map(function (_a) {
129
- var contractAddress = _a.contractAddress,
130
- entrypoint = _a.entrypoint,
131
- calldata = _a.calldata;
132
- return [
133
- contractAddress,
134
- getSelectorFromName(entrypoint),
135
- computeHashOnElements(calldata || []),
136
- ];
137
- })
138
- .map(number_1.bigNumberishArrayToDecimalStringArray)
139
- .map(computeHashOnElements);
140
- return computeHashOnElements([
141
- exports.transactionPrefix,
142
- account,
143
- computeHashOnElements(hashArray),
144
- nonce,
145
- maxFee,
137
+ var calldataHash = computeHashOnElements(calldata);
138
+ var dataToHash = __spreadArray(
139
+ [txHashPrefix, version, contractAddress, entryPointSelector, calldataHash, maxFee, chainId],
140
+ __read(additionalData),
141
+ false
142
+ );
143
+ return computeHashOnElements(dataToHash);
144
+ }
145
+ exports.calculateTransactionHashCommon = calculateTransactionHashCommon;
146
+ function calculateDeployTransactionHash(contractAddress, constructorCalldata, version, chainId) {
147
+ return calculateTransactionHashCommon(
148
+ constants_1.TransactionHashPrefix.DEPLOY,
149
+ version,
150
+ contractAddress,
151
+ getSelectorFromName('constructor'),
152
+ constructorCalldata,
153
+ constants_1.ZERO,
154
+ chainId
155
+ );
156
+ }
157
+ exports.calculateDeployTransactionHash = calculateDeployTransactionHash;
158
+ function calculcateTransactionHash(
159
+ contractAddress,
160
+ version,
161
+ entryPointSelector,
162
+ calldata,
163
+ maxFee,
164
+ chainId
165
+ ) {
166
+ return calculateTransactionHashCommon(
167
+ constants_1.TransactionHashPrefix.INVOKE,
146
168
  version,
147
- ]);
169
+ contractAddress,
170
+ entryPointSelector,
171
+ calldata,
172
+ maxFee,
173
+ chainId
174
+ );
148
175
  }
149
- exports.hashMulticall = hashMulticall;
176
+ exports.calculcateTransactionHash = calculcateTransactionHash;