starknet 2.7.0 → 2.9.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 (48) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/CONTRIBUTING.md +1 -1
  3. package/README.md +3 -3
  4. package/__mocks__/typedDataExample.json +35 -0
  5. package/__tests__/provider.test.ts +5 -3
  6. package/__tests__/signer.test.ts +6 -0
  7. package/__tests__/utils/address.test.ts +16 -0
  8. package/__tests__/utils/typedData.test.ts +1 -36
  9. package/constants.d.ts +2 -0
  10. package/constants.js +4 -0
  11. package/dist/constants.d.ts +2 -0
  12. package/dist/constants.js +3 -1
  13. package/dist/index.d.ts +1 -0
  14. package/dist/index.js +1 -0
  15. package/dist/provider/default.d.ts +6 -5
  16. package/dist/provider/default.js +12 -12
  17. package/dist/provider/interface.d.ts +11 -14
  18. package/dist/provider/utils.d.ts +17 -1
  19. package/dist/provider/utils.js +39 -6
  20. package/dist/signer/default.d.ts +20 -0
  21. package/dist/signer/default.js +57 -0
  22. package/dist/signer/interface.d.ts +20 -0
  23. package/dist/types.d.ts +1 -1
  24. package/dist/utils/address.d.ts +2 -0
  25. package/dist/utils/address.js +22 -0
  26. package/index.d.ts +1 -0
  27. package/index.js +1 -0
  28. package/package.json +1 -1
  29. package/provider/default.d.ts +5 -11
  30. package/provider/default.js +16 -28
  31. package/provider/interface.d.ts +10 -17
  32. package/provider/utils.d.ts +19 -4
  33. package/provider/utils.js +44 -7
  34. package/signer/default.d.ts +20 -0
  35. package/signer/default.js +64 -0
  36. package/signer/interface.d.ts +20 -0
  37. package/src/constants.ts +2 -0
  38. package/src/index.ts +1 -0
  39. package/src/provider/default.ts +9 -16
  40. package/src/provider/interface.ts +10 -20
  41. package/src/provider/utils.ts +46 -9
  42. package/src/signer/default.ts +40 -1
  43. package/src/signer/interface.ts +22 -0
  44. package/src/types.ts +1 -1
  45. package/src/utils/address.ts +23 -0
  46. package/types.d.ts +1 -1
  47. package/utils/address.d.ts +2 -0
  48. package/utils/address.js +22 -0
@@ -3,7 +3,6 @@ import urljoin from 'url-join';
3
3
 
4
4
  import {
5
5
  AddTransactionResponse,
6
- BlockNumber,
7
6
  CallContractResponse,
8
7
  CallContractTransaction,
9
8
  CompiledContract,
@@ -19,7 +18,7 @@ import { parse, stringify } from '../utils/json';
19
18
  import { BigNumberish, toBN, toHex } from '../utils/number';
20
19
  import { compressProgram, formatSignature, randomAddress } from '../utils/stark';
21
20
  import { ProviderInterface } from './interface';
22
- import { getFormattedBlockIdentifier } from './utils';
21
+ import { BlockIdentifier, getFormattedBlockIdentifier } from './utils';
23
22
 
24
23
  type NetworkName = 'mainnet-alpha' | 'goerli-alpha';
25
24
 
@@ -93,10 +92,9 @@ export class Provider implements ProviderInterface {
93
92
  */
94
93
  public async callContract(
95
94
  invokeTransaction: CallContractTransaction,
96
- blockHash?: BigNumberish,
97
- blockNumber: BlockNumber = null
95
+ blockIdentifier: BlockIdentifier = null
98
96
  ): Promise<CallContractResponse> {
99
- const formattedBlockIdentifier = getFormattedBlockIdentifier(blockHash, blockNumber);
97
+ const formattedBlockIdentifier = getFormattedBlockIdentifier(blockIdentifier);
100
98
 
101
99
  const { data } = await axios.post<CallContractResponse>(
102
100
  urljoin(this.feederGatewayUrl, 'call_contract', formattedBlockIdentifier),
@@ -118,11 +116,8 @@ export class Provider implements ProviderInterface {
118
116
  * @param blockNumber
119
117
  * @returns the block object { block_number, previous_block_number, state_root, status, timestamp, transaction_receipts, transactions }
120
118
  */
121
- public async getBlock(
122
- blockHash?: BigNumberish,
123
- blockNumber: BlockNumber = null
124
- ): Promise<GetBlockResponse> {
125
- const formattedBlockIdentifier = getFormattedBlockIdentifier(blockHash, blockNumber);
119
+ public async getBlock(blockIdentifier: BlockIdentifier = null): Promise<GetBlockResponse> {
120
+ const formattedBlockIdentifier = getFormattedBlockIdentifier(blockIdentifier);
126
121
 
127
122
  const { data } = await axios.get<GetBlockResponse>(
128
123
  urljoin(this.feederGatewayUrl, 'get_block', formattedBlockIdentifier)
@@ -142,10 +137,9 @@ export class Provider implements ProviderInterface {
142
137
  */
143
138
  public async getCode(
144
139
  contractAddress: string,
145
- blockHash?: BigNumberish,
146
- blockNumber: BlockNumber = null
140
+ blockIdentifier: BlockIdentifier = null
147
141
  ): Promise<GetCodeResponse> {
148
- const formattedBlockIdentifier = getFormattedBlockIdentifier(blockHash, blockNumber);
142
+ const formattedBlockIdentifier = getFormattedBlockIdentifier(blockIdentifier);
149
143
 
150
144
  const { data } = await axios.get<GetCodeResponse>(
151
145
  urljoin(
@@ -172,10 +166,9 @@ export class Provider implements ProviderInterface {
172
166
  public async getStorageAt(
173
167
  contractAddress: string,
174
168
  key: number,
175
- blockHash?: BigNumberish,
176
- blockNumber: BlockNumber = null
169
+ blockIdentifier: BlockIdentifier = null
177
170
  ): Promise<object> {
178
- const formattedBlockIdentifier = getFormattedBlockIdentifier(blockHash, blockNumber);
171
+ const formattedBlockIdentifier = getFormattedBlockIdentifier(blockIdentifier);
179
172
 
180
173
  const { data } = await axios.get<object>(
181
174
  urljoin(
@@ -1,6 +1,5 @@
1
1
  import type {
2
2
  AddTransactionResponse,
3
- BlockNumber,
4
3
  CallContractResponse,
5
4
  CallContractTransaction,
6
5
  CompiledContract,
@@ -13,6 +12,7 @@ import type {
13
12
  Transaction,
14
13
  } from '../types';
15
14
  import type { BigNumberish } from '../utils/number';
15
+ import { BlockIdentifier } from './utils';
16
16
 
17
17
  export abstract class ProviderInterface {
18
18
  public abstract baseUrl: string;
@@ -35,14 +35,12 @@ export abstract class ProviderInterface {
35
35
  * [Reference](https://github.com/starkware-libs/cairo-lang/blob/fc97bdd8322a7df043c87c371634b26c15ed6cee/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L25-L39)
36
36
  *
37
37
  * @param invokeTransaction - transaction to be invoked
38
- * @param blockHash
39
- * @param blockNumber
38
+ * @param blockIdentifier - block identifier
40
39
  * @returns the result of the function on the smart contract.
41
40
  */
42
41
  public abstract callContract(
43
42
  invokeTransaction: CallContractTransaction,
44
- blockHash?: BigNumberish,
45
- blockNumber?: BlockNumber
43
+ blockIdentifier?: BlockIdentifier
46
44
  ): Promise<CallContractResponse>;
47
45
 
48
46
  /**
@@ -50,29 +48,23 @@ export abstract class ProviderInterface {
50
48
  *
51
49
  * [Reference](https://github.com/starkware-libs/cairo-lang/blob/fc97bdd8322a7df043c87c371634b26c15ed6cee/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L41-L53)
52
50
  *
53
- * @param blockHash
54
- * @param blockNumber
51
+ * @param blockIdentifier - block identifier
55
52
  * @returns the block object { block_number, previous_block_number, state_root, status, timestamp, transaction_receipts, transactions }
56
53
  */
57
- public abstract getBlock(
58
- blockHash?: BigNumberish,
59
- blockNumber?: BlockNumber
60
- ): Promise<GetBlockResponse>;
54
+ public abstract getBlock(blockIdentifier?: BlockIdentifier): Promise<GetBlockResponse>;
61
55
 
62
56
  /**
63
57
  * Gets the code of the deployed contract.
64
58
  *
65
59
  * [Reference](https://github.com/starkware-libs/cairo-lang/blob/fc97bdd8322a7df043c87c371634b26c15ed6cee/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L55-L68)
66
60
  *
67
- * @param contractAddress
68
- * @param blockHash
69
- * @param blockNumber
61
+ * @param contractAddress - contract address
62
+ * @param blockIdentifier - block identifier
70
63
  * @returns Bytecode and ABI of compiled contract
71
64
  */
72
65
  public abstract getCode(
73
66
  contractAddress: string,
74
- blockHash?: BigNumberish,
75
- blockNumber?: BlockNumber
67
+ blockIdentifier?: BlockIdentifier
76
68
  ): Promise<GetCodeResponse>;
77
69
 
78
70
  // TODO: add proper type
@@ -83,15 +75,13 @@ export abstract class ProviderInterface {
83
75
  *
84
76
  * @param contractAddress
85
77
  * @param key - from getStorageVarAddress('<STORAGE_VARIABLE_NAME>') (WIP)
86
- * @param blockHash
87
- * @param blockNumber
78
+ * @param blockIdentifier - block identifier
88
79
  * @returns the value of the storage variable
89
80
  */
90
81
  public abstract getStorageAt(
91
82
  contractAddress: string,
92
83
  key: number,
93
- blockHash?: BigNumberish,
94
- blockNumber?: BlockNumber
84
+ blockIdentifier?: BlockIdentifier
95
85
  ): Promise<object>;
96
86
 
97
87
  /**
@@ -1,5 +1,5 @@
1
1
  import type { BlockNumber } from '../types';
2
- import { BigNumberish } from '../utils/number';
2
+ import { BigNumberish, toBN, toHex } from '../utils/number';
3
3
 
4
4
  /**
5
5
  * TODO
@@ -18,6 +18,42 @@ export function formatHash() {}
18
18
  */
19
19
  export function txIdentifier() {}
20
20
 
21
+ // hex string and BN are detected as block hashes
22
+ // decimal string and number are detected as block numbers
23
+ // null appends nothing to the request url
24
+ export type BlockIdentifier = BlockNumber | BigNumberish;
25
+ type BlockIdentifierObject =
26
+ | { type: 'BLOCK_NUMBER'; data: BlockNumber }
27
+ | { type: 'BLOCK_HASH'; data: BigNumberish };
28
+
29
+ /**
30
+ * Identifies the block to be queried.
31
+ *
32
+ * @param blockIdentifier - block identifier
33
+ * @returns block identifier object
34
+ */
35
+ export function getBlockIdentifier(blockIdentifier: BlockIdentifier): BlockIdentifierObject {
36
+ if (typeof blockIdentifier === 'number') {
37
+ return { type: 'BLOCK_NUMBER', data: blockIdentifier };
38
+ }
39
+ if (typeof blockIdentifier === 'string' && blockIdentifier.startsWith('0x')) {
40
+ return { type: 'BLOCK_HASH', data: blockIdentifier };
41
+ }
42
+ if (typeof blockIdentifier === 'string' && !Number.isNaN(parseInt(blockIdentifier, 10))) {
43
+ return { type: 'BLOCK_NUMBER', data: parseInt(blockIdentifier, 10) };
44
+ }
45
+ if (blockIdentifier === null) {
46
+ return { type: 'BLOCK_NUMBER', data: null };
47
+ }
48
+ if (blockIdentifier === 'pending') {
49
+ return { type: 'BLOCK_NUMBER', data: 'pending' };
50
+ }
51
+ if (typeof blockIdentifier === 'string') {
52
+ throw new Error(`Invalid block identifier: ${blockIdentifier}`);
53
+ }
54
+ return { type: 'BLOCK_HASH', data: blockIdentifier };
55
+ }
56
+
21
57
  /**
22
58
  * Gets the block identifier for API request
23
59
  *
@@ -27,12 +63,13 @@ export function txIdentifier() {}
27
63
  * @param blockHash
28
64
  * @returns block identifier for API request
29
65
  */
30
- export function getFormattedBlockIdentifier(
31
- blockHash?: BigNumberish,
32
- blockNumber: BlockNumber = null
33
- ): string {
34
- if (blockHash) {
35
- return `?blockHash=${blockHash}`;
36
- }
37
- return `?blockNumber=${blockNumber}`;
66
+ export function getFormattedBlockIdentifier(blockIdentifier: BlockIdentifier = null): string {
67
+ const blockIdentifierObject = getBlockIdentifier(blockIdentifier);
68
+ if (blockIdentifierObject.type === 'BLOCK_NUMBER' && blockIdentifierObject.data === null) {
69
+ return '';
70
+ }
71
+ if (blockIdentifierObject.type === 'BLOCK_NUMBER') {
72
+ return `?blockNumber=${blockIdentifierObject.data}`;
73
+ }
74
+ return `?blockHash=${toHex(toBN(blockIdentifierObject.data))}`;
38
75
  }
@@ -1,11 +1,12 @@
1
1
  import assert from 'minimalistic-assert';
2
2
 
3
+ import { compileCalldata } from '../contract';
3
4
  import { Provider } from '../provider';
4
5
  import { AddTransactionResponse, KeyPair, Signature, Transaction } from '../types';
5
6
  import { sign } from '../utils/ellipticCurve';
6
7
  import { addHexPrefix } from '../utils/encode';
7
8
  import { hashMessage } from '../utils/hash';
8
- import { toBN } from '../utils/number';
9
+ import { BigNumberish, toBN } from '../utils/number';
9
10
  import { getSelectorFromName } from '../utils/stark';
10
11
  import { TypedData, getMessageHash } from '../utils/typedData';
11
12
  import { SignerInterface } from './interface';
@@ -98,4 +99,42 @@ export class Signer extends Provider implements SignerInterface {
98
99
  public async hashMessage(typedData: TypedData): Promise<string> {
99
100
  return getMessageHash(typedData, this.address);
100
101
  }
102
+
103
+ /**
104
+ * Verify a signature of a JSON object
105
+ *
106
+ * @param json - JSON object to be verified
107
+ * @param signature - signature of the JSON object
108
+ * @returns true if the signature is valid, false otherwise
109
+ * @throws {Error} if the JSON object is not a valid JSON or the signature is not a valid signature
110
+ */
111
+ public async verifyMessageHash(hash: BigNumberish, signature: Signature): Promise<boolean> {
112
+ try {
113
+ await this.callContract({
114
+ contract_address: this.address,
115
+ entry_point_selector: getSelectorFromName('is_valid_signature'),
116
+ calldata: compileCalldata({
117
+ hash: toBN(hash).toString(),
118
+ signature: signature.map((x) => toBN(x).toString()),
119
+ }),
120
+ });
121
+ return true;
122
+ } catch {
123
+ return false;
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Verify a signature of a given hash
129
+ * @warning This method is not recommended, use verifyMessage instead
130
+ *
131
+ * @param hash - hash to be verified
132
+ * @param signature - signature of the hash
133
+ * @returns true if the signature is valid, false otherwise
134
+ * @throws {Error} if the signature is not a valid signature
135
+ */
136
+ public async verifyMessage(typedData: TypedData, signature: Signature): Promise<boolean> {
137
+ const hash = await this.hashMessage(typedData);
138
+ return this.verifyMessageHash(hash, signature);
139
+ }
101
140
  }
@@ -1,5 +1,6 @@
1
1
  import { Provider } from '../provider';
2
2
  import { AddTransactionResponse, Signature, Transaction } from '../types';
3
+ import { BigNumberish } from '../utils/number';
3
4
  import { TypedData } from '../utils/typedData/types';
4
5
 
5
6
  export abstract class SignerInterface extends Provider {
@@ -35,4 +36,25 @@ export abstract class SignerInterface extends Provider {
35
36
  * @throws {Error} if the JSON object is not a valid JSON
36
37
  */
37
38
  public abstract hashMessage(typedData: TypedData): Promise<string>;
39
+
40
+ /**
41
+ * Verify a signature of a JSON object
42
+ *
43
+ * @param json - JSON object to be verified
44
+ * @param signature - signature of the JSON object
45
+ * @returns true if the signature is valid, false otherwise
46
+ * @throws {Error} if the JSON object is not a valid JSON or the signature is not a valid signature
47
+ */
48
+ public abstract verifyMessage(typedData: TypedData, signature: Signature): Promise<boolean>;
49
+
50
+ /**
51
+ * Verify a signature of a given hash
52
+ * @warning This method is not recommended, use verifyMessage instead
53
+ *
54
+ * @param hash - hash to be verified
55
+ * @param signature - signature of the hash
56
+ * @returns true if the signature is valid, false otherwise
57
+ * @throws {Error} if the signature is not a valid signature
58
+ */
59
+ public abstract verifyMessageHash(hash: BigNumberish, signature: Signature): Promise<boolean>;
38
60
  }
package/src/types.ts CHANGED
@@ -80,7 +80,7 @@ export type CallContractResponse = {
80
80
  };
81
81
 
82
82
  export type GetBlockResponse = {
83
- sequence_number: number;
83
+ block_number: number;
84
84
  state_root: string;
85
85
  block_hash: string;
86
86
  transactions: {
@@ -0,0 +1,23 @@
1
+ import { MASK_251, ZERO } from '../constants';
2
+ import { addHexPrefix, removeHexPrefix } from './encode';
3
+ import { assertInRange } from './number';
4
+
5
+ export function addAddressPadding(address: string): string {
6
+ return addHexPrefix(removeHexPrefix(address).padStart(64, '0'));
7
+ }
8
+
9
+ export function validateAndParseAddress(address: string): string {
10
+ if (typeof address !== 'string') {
11
+ throw new Error('Invalid Address Type');
12
+ }
13
+
14
+ assertInRange(address, ZERO, MASK_251, 'Starknet Address');
15
+
16
+ const result = addAddressPadding(address);
17
+
18
+ if (!result.match(/^(0x)?[0-9a-fA-F]{64}$/)) {
19
+ throw new Error('Invalid Address Format');
20
+ }
21
+
22
+ return result;
23
+ }
package/types.d.ts CHANGED
@@ -69,7 +69,7 @@ export declare type CallContractResponse = {
69
69
  result: string[];
70
70
  };
71
71
  export declare type GetBlockResponse = {
72
- sequence_number: number;
72
+ block_number: number;
73
73
  state_root: string;
74
74
  block_hash: string;
75
75
  transactions: {
@@ -0,0 +1,2 @@
1
+ export declare function addAddressPadding(address: string): string;
2
+ export declare function validateAndParseAddress(address: string): string;
@@ -0,0 +1,22 @@
1
+ 'use strict';
2
+ Object.defineProperty(exports, '__esModule', { value: true });
3
+ exports.validateAndParseAddress = exports.addAddressPadding = void 0;
4
+ var constants_1 = require('../constants');
5
+ var encode_1 = require('./encode');
6
+ var number_1 = require('./number');
7
+ function addAddressPadding(address) {
8
+ return (0, encode_1.addHexPrefix)((0, encode_1.removeHexPrefix)(address).padStart(64, '0'));
9
+ }
10
+ exports.addAddressPadding = addAddressPadding;
11
+ function validateAndParseAddress(address) {
12
+ if (typeof address !== 'string') {
13
+ throw new Error('Invalid Address Type');
14
+ }
15
+ (0, number_1.assertInRange)(address, constants_1.ZERO, constants_1.MASK_251, 'Starknet Address');
16
+ var result = addAddressPadding(address);
17
+ if (!result.match(/^(0x)?[0-9a-fA-F]{64}$/)) {
18
+ throw new Error('Invalid Address Format');
19
+ }
20
+ return result;
21
+ }
22
+ exports.validateAndParseAddress = validateAndParseAddress;