starknet 4.3.0 → 4.4.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 +40 -1
- package/__mocks__/typedDataSessionExample.json +42 -0
- package/__tests__/defaultProvider.test.ts +11 -24
- package/__tests__/rpcProvider.test.ts +32 -5
- package/__tests__/sequencerProvider.test.ts +9 -1
- package/__tests__/utils/__snapshots__/ellipticalCurve.test.ts.snap +2 -0
- package/__tests__/utils/ellipticalCurve.test.ts +5 -0
- package/__tests__/utils/merkle.test.ts +146 -0
- package/__tests__/utils/typedData.test.ts +107 -9
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -1
- package/dist/provider/default.d.ts +2 -2
- package/dist/provider/default.js +3 -3
- package/dist/provider/interface.d.ts +6 -3
- package/dist/provider/rpc.d.ts +9 -4
- package/dist/provider/rpc.js +70 -28
- package/dist/provider/sequencer.d.ts +2 -2
- package/dist/provider/sequencer.js +4 -4
- package/dist/provider/utils.d.ts +12 -0
- package/dist/provider/utils.js +17 -1
- package/dist/signer/default.d.ts +1 -1
- package/dist/signer/default.js +1 -0
- package/dist/types/api/openrpc.d.ts +151 -0
- package/dist/types/api/openrpc.js +9 -0
- package/dist/types/api/rpc.d.ts +22 -43
- package/dist/types/provider.d.ts +5 -5
- package/dist/utils/ellipticCurve.d.ts +13 -0
- package/dist/utils/ellipticCurve.js +20 -16
- package/dist/utils/hash.js +8 -6
- package/dist/utils/merkle.d.ts +10 -0
- package/dist/utils/merkle.js +90 -0
- package/dist/utils/number.d.ts +1 -0
- package/dist/utils/number.js +6 -2
- package/dist/utils/responseParser/rpc.d.ts +13 -3
- package/dist/utils/responseParser/rpc.js +2 -10
- package/dist/utils/responseParser/sequencer.d.ts +4 -1
- package/dist/utils/responseParser/sequencer.js +1 -7
- package/dist/utils/typedData/index.d.ts +23 -8
- package/dist/utils/typedData/index.js +70 -31
- package/dist/utils/typedData/types.d.ts +8 -3
- package/index.d.ts +1 -0
- package/index.js +2 -1
- package/package.json +1 -1
- package/provider/default.d.ts +2 -2
- package/provider/default.js +3 -3
- package/provider/interface.d.ts +6 -3
- package/provider/rpc.d.ts +9 -4
- package/provider/rpc.js +70 -28
- package/provider/sequencer.d.ts +2 -2
- package/provider/sequencer.js +4 -4
- package/provider/utils.d.ts +12 -0
- package/provider/utils.js +17 -1
- package/signer/default.d.ts +1 -1
- package/signer/default.js +1 -0
- package/src/index.ts +1 -0
- package/src/provider/default.ts +2 -3
- package/src/provider/interface.ts +5 -3
- package/src/provider/rpc.ts +59 -32
- package/src/provider/sequencer.ts +3 -6
- package/src/provider/utils.ts +22 -1
- package/src/signer/default.ts +2 -2
- package/src/types/api/openrpc.ts +168 -0
- package/src/types/api/rpc.ts +22 -45
- package/src/types/provider.ts +5 -5
- package/src/utils/ellipticCurve.ts +20 -16
- package/src/utils/hash.ts +8 -6
- package/src/utils/merkle.ts +70 -0
- package/src/utils/number.ts +5 -1
- package/src/utils/responseParser/rpc.ts +16 -13
- package/src/utils/responseParser/sequencer.ts +5 -8
- package/src/utils/typedData/index.ts +88 -34
- package/src/utils/typedData/types.ts +12 -4
- package/types/api/openrpc.d.ts +151 -0
- package/types/api/openrpc.js +9 -0
- package/types/api/rpc.d.ts +22 -43
- package/types/provider.d.ts +5 -5
- package/utils/ellipticCurve.d.ts +13 -0
- package/utils/ellipticCurve.js +20 -16
- package/utils/hash.js +8 -6
- package/utils/merkle.d.ts +10 -0
- package/utils/merkle.js +90 -0
- package/utils/number.d.ts +1 -0
- package/utils/number.js +6 -2
- package/utils/responseParser/rpc.d.ts +13 -3
- package/utils/responseParser/rpc.js +2 -10
- package/utils/responseParser/sequencer.d.ts +4 -1
- package/utils/responseParser/sequencer.js +1 -7
- package/utils/typedData/index.d.ts +23 -8
- package/utils/typedData/index.js +70 -31
- package/utils/typedData/types.d.ts +8 -3
- package/www/docs/API/account.md +20 -18
- package/www/docs/API/contract.md +10 -10
- package/www/docs/API/contractFactory.md +14 -11
- package/www/docs/API/provider.md +29 -63
- package/www/docs/API/signer.md +8 -10
- package/www/docs/API/utils.md +157 -74
- package/www/guides/account.md +12 -12
- package/www/guides/erc20.md +19 -4
- package/www/guides/intro.md +3 -1
|
@@ -21,11 +21,13 @@ export const ec = new EC(
|
|
|
21
21
|
})
|
|
22
22
|
);
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
The function _truncateToN in lib/elliptic/ec/index.js does a shift-right of 4 bits
|
|
26
|
-
in some cases. This function does the opposite operation so that
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
/**
|
|
25
|
+
* The function _truncateToN in lib/elliptic/ec/index.js does a shift-right of 4 bits
|
|
26
|
+
* in some cases. This function does the opposite operation so that
|
|
27
|
+
* _truncateToN(fixMessage(msg)) == msg.
|
|
28
|
+
*
|
|
29
|
+
* @param msg
|
|
30
|
+
*/
|
|
29
31
|
function fixMessage(msg: string) {
|
|
30
32
|
const pureHex = msg.replace(/^0x0*/, '');
|
|
31
33
|
|
|
@@ -64,11 +66,12 @@ export function getKeyPairFromPublicKey(publicKey: BigNumberish): KeyPair {
|
|
|
64
66
|
return ec.keyFromPublic(removeHexPrefix(toHex(publicKeyBn)), 'hex');
|
|
65
67
|
}
|
|
66
68
|
|
|
67
|
-
|
|
68
|
-
Signs a message using the provided key.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Signs a message using the provided key.
|
|
71
|
+
*
|
|
72
|
+
* @param keyPair should be an KeyPair with a valid private key.
|
|
73
|
+
* @returns an Signature.
|
|
74
|
+
*/
|
|
72
75
|
export function sign(keyPair: KeyPair, msgHash: string): Signature {
|
|
73
76
|
const msgHashBN = toBN(addHexPrefix(msgHash));
|
|
74
77
|
// Verify message hash has valid length.
|
|
@@ -89,12 +92,13 @@ function chunkArray(arr: any[], n: number): any[][] {
|
|
|
89
92
|
.map((_, i) => arr.slice(i * n, i * n + n));
|
|
90
93
|
}
|
|
91
94
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
/**
|
|
96
|
+
* Verifies a message using the provided key.
|
|
97
|
+
*
|
|
98
|
+
* @param keyPair should be an KeyPair with a valid public key.
|
|
99
|
+
* @param sig should be an Signature.
|
|
100
|
+
* @returns true if the verification succeeds.
|
|
101
|
+
*/
|
|
98
102
|
export function verify(keyPair: KeyPair | KeyPair[], msgHash: string, sig: Signature): boolean {
|
|
99
103
|
const keyPairArray = Array.isArray(keyPair) ? keyPair : [keyPair];
|
|
100
104
|
const msgHashBN = toBN(addHexPrefix(msgHash));
|
package/src/utils/hash.ts
CHANGED
|
@@ -62,13 +62,15 @@ export function pedersen(input: [BigNumberish, BigNumberish]) {
|
|
|
62
62
|
for (let i = 0; i < input.length; i += 1) {
|
|
63
63
|
let x = toBN(input[i]);
|
|
64
64
|
assert(x.gte(ZERO) && x.lt(toBN(addHexPrefix(FIELD_PRIME))), `Invalid input: ${input[i]}`);
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
65
|
+
if (!x.isZero()) {
|
|
66
|
+
for (let j = 0; j < 252; j += 1) {
|
|
67
|
+
const pt = constantPoints[2 + i * 252 + j];
|
|
68
|
+
assert(!point.getX().eq(pt.getX()));
|
|
69
|
+
if (x.and(ONE).toNumber() !== 0) {
|
|
70
|
+
point = point.add(pt);
|
|
71
|
+
}
|
|
72
|
+
x = x.shrn(1);
|
|
70
73
|
}
|
|
71
|
-
x = x.shrn(1);
|
|
72
74
|
}
|
|
73
75
|
}
|
|
74
76
|
return addHexPrefix(point.getX().toString(16));
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { pedersen } from './hash';
|
|
2
|
+
|
|
3
|
+
export class MerkleTree {
|
|
4
|
+
public leaves: string[];
|
|
5
|
+
|
|
6
|
+
public branches: string[][] = [];
|
|
7
|
+
|
|
8
|
+
public root: string;
|
|
9
|
+
|
|
10
|
+
constructor(leafHashes: string[]) {
|
|
11
|
+
this.leaves = leafHashes;
|
|
12
|
+
this.root = this.build(leafHashes);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
private build(leaves: string[]): string {
|
|
16
|
+
if (leaves.length === 1) {
|
|
17
|
+
return leaves[0];
|
|
18
|
+
}
|
|
19
|
+
if (leaves.length !== this.leaves.length) {
|
|
20
|
+
this.branches.push(leaves);
|
|
21
|
+
}
|
|
22
|
+
const newLeaves = [];
|
|
23
|
+
for (let i = 0; i < leaves.length; i += 2) {
|
|
24
|
+
if (i + 1 === leaves.length) {
|
|
25
|
+
newLeaves.push(leaves[i]);
|
|
26
|
+
} else {
|
|
27
|
+
newLeaves.push(MerkleTree.hash(leaves[i], leaves[i + 1]));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return this.build(newLeaves);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static hash(a: string, b: string) {
|
|
34
|
+
const [aSorted, bSorted] = [a, b].sort();
|
|
35
|
+
return pedersen([aSorted, bSorted]);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public getProof(leaf: string, branch = this.leaves, hashPath: string[] = []): string[] {
|
|
39
|
+
const index = branch.indexOf(leaf);
|
|
40
|
+
if (index === -1) {
|
|
41
|
+
throw new Error('leaf not found');
|
|
42
|
+
}
|
|
43
|
+
if (branch.length === 1) {
|
|
44
|
+
return hashPath;
|
|
45
|
+
}
|
|
46
|
+
const isLeft = index % 2 === 0;
|
|
47
|
+
const neededBranch = (isLeft ? branch[index + 1] : branch[index - 1]) ?? '0x0';
|
|
48
|
+
const newHashPath = [...hashPath, neededBranch];
|
|
49
|
+
const currentBranchLevelIndex =
|
|
50
|
+
this.leaves.length === branch.length
|
|
51
|
+
? -1
|
|
52
|
+
: this.branches.findIndex((b) => b.length === branch.length);
|
|
53
|
+
const nextBranch = this.branches[currentBranchLevelIndex + 1] ?? [this.root];
|
|
54
|
+
return this.getProof(
|
|
55
|
+
neededBranch === '0x0'
|
|
56
|
+
? leaf
|
|
57
|
+
: MerkleTree.hash(isLeft ? leaf : neededBranch, isLeft ? neededBranch : leaf),
|
|
58
|
+
nextBranch,
|
|
59
|
+
newHashPath
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function proofMerklePath(root: string, leaf: string, path: string[]): boolean {
|
|
65
|
+
if (path.length === 0) {
|
|
66
|
+
return root === leaf;
|
|
67
|
+
}
|
|
68
|
+
const [next, ...rest] = path;
|
|
69
|
+
return proofMerklePath(root, MerkleTree.hash(leaf, next), rest);
|
|
70
|
+
}
|
package/src/utils/number.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { addHexPrefix, removeHexPrefix } from './encode';
|
|
|
6
6
|
export type BigNumberish = string | number | BN;
|
|
7
7
|
|
|
8
8
|
export function isHex(hex: string): boolean {
|
|
9
|
-
return
|
|
9
|
+
return /^0x[0-9a-f]*$/i.test(hex);
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export function toBN(number: BigNumberish, base?: number | 'hex') {
|
|
@@ -57,3 +57,7 @@ export function assertInRange(
|
|
|
57
57
|
export function bigNumberishArrayToDecimalStringArray(rawCalldata: BigNumberish[]): string[] {
|
|
58
58
|
return rawCalldata.map((x) => toBN(x).toString(10));
|
|
59
59
|
}
|
|
60
|
+
|
|
61
|
+
export function bigNumberishArrayToHexadecimalStringArray(rawCalldata: BigNumberish[]): string[] {
|
|
62
|
+
return rawCalldata.map((x) => toHex(toBN(x)));
|
|
63
|
+
}
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Map RPC Response to common interface response
|
|
3
|
+
* Intersection (sequencer response ∩ (∪ rpc responses))
|
|
4
|
+
*/
|
|
1
5
|
import {
|
|
2
6
|
CallContractResponse,
|
|
3
7
|
DeclareContractResponse,
|
|
@@ -12,33 +16,36 @@ import { RPC } from '../../types/api';
|
|
|
12
16
|
import { toBN } from '../number';
|
|
13
17
|
import { ResponseParser } from '.';
|
|
14
18
|
|
|
19
|
+
type RpcGetBlockResponse = RPC.GetBlockWithTxHashesResponse & {
|
|
20
|
+
[key: string]: any;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type GetTransactionByHashResponse = RPC.GetTransactionByHashResponse & {
|
|
24
|
+
[key: string]: any;
|
|
25
|
+
};
|
|
26
|
+
|
|
15
27
|
export class RPCResponseParser extends ResponseParser {
|
|
16
|
-
public parseGetBlockResponse(res:
|
|
28
|
+
public parseGetBlockResponse(res: RpcGetBlockResponse): GetBlockResponse {
|
|
17
29
|
return {
|
|
18
|
-
|
|
30
|
+
timestamp: res.timestamp,
|
|
19
31
|
block_hash: res.block_hash,
|
|
20
32
|
block_number: res.block_number,
|
|
21
|
-
gas_price: res.gas_price,
|
|
22
33
|
new_root: res.new_root,
|
|
23
|
-
old_root: res.old_root,
|
|
24
34
|
parent_hash: res.parent_hash,
|
|
25
|
-
sequencer: res.sequencer,
|
|
26
35
|
status: res.status,
|
|
27
36
|
transactions: res.transactions,
|
|
28
37
|
};
|
|
29
38
|
}
|
|
30
39
|
|
|
31
|
-
public parseGetTransactionResponse(res:
|
|
40
|
+
public parseGetTransactionResponse(res: GetTransactionByHashResponse): GetTransactionResponse {
|
|
32
41
|
return {
|
|
33
42
|
calldata: res.calldata || [],
|
|
34
43
|
contract_address: res.contract_address,
|
|
35
|
-
contract_class: res.contract_class,
|
|
36
44
|
entry_point_selector: res.entry_point_selector,
|
|
37
45
|
max_fee: res.max_fee,
|
|
38
46
|
nonce: res.nonce,
|
|
39
|
-
sender_address: res.sender_address,
|
|
40
47
|
signature: res.signature || [],
|
|
41
|
-
transaction_hash: res.
|
|
48
|
+
transaction_hash: res.transaction_hash,
|
|
42
49
|
version: res.version,
|
|
43
50
|
};
|
|
44
51
|
}
|
|
@@ -57,10 +64,6 @@ export class RPCResponseParser extends ResponseParser {
|
|
|
57
64
|
};
|
|
58
65
|
}
|
|
59
66
|
|
|
60
|
-
public parseGetCodeResponse(res: RPC.GetCodeResponse): RPC.GetCodeResponse {
|
|
61
|
-
return res;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
67
|
public parseFeeEstimateResponse(res: RPC.EstimateFeeResponse): EstimateFeeResponse {
|
|
65
68
|
return {
|
|
66
69
|
overall_fee: toBN(res.overall_fee),
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Map Sequencer Response to common interface response
|
|
3
|
+
* Intersection (sequencer response ∩ (∪ rpc responses))
|
|
4
|
+
*/
|
|
1
5
|
import {
|
|
2
6
|
CallContractResponse,
|
|
3
7
|
DeclareContractResponse,
|
|
@@ -15,14 +19,11 @@ import { ResponseParser } from '.';
|
|
|
15
19
|
export class SequencerAPIResponseParser extends ResponseParser {
|
|
16
20
|
public parseGetBlockResponse(res: Sequencer.GetBlockResponse): GetBlockResponse {
|
|
17
21
|
return {
|
|
18
|
-
|
|
22
|
+
timestamp: res.timestamp,
|
|
19
23
|
block_hash: res.block_hash,
|
|
20
24
|
block_number: res.block_number,
|
|
21
|
-
gas_price: res.gas_price,
|
|
22
25
|
new_root: res.state_root,
|
|
23
|
-
old_root: undefined,
|
|
24
26
|
parent_hash: res.parent_block_hash,
|
|
25
|
-
sequencer: res.sequencer_address,
|
|
26
27
|
status: res.status,
|
|
27
28
|
transactions: Object.values(res.transactions)
|
|
28
29
|
.map((value) => 'transaction_hash' in value && value.transaction_hash)
|
|
@@ -70,10 +71,6 @@ export class SequencerAPIResponseParser extends ResponseParser {
|
|
|
70
71
|
};
|
|
71
72
|
}
|
|
72
73
|
|
|
73
|
-
public parseGetCodeResponse(res: Sequencer.GetCodeResponse): Sequencer.GetCodeResponse {
|
|
74
|
-
return res;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
74
|
public parseFeeEstimateResponse(res: Sequencer.EstimateFeeResponse): EstimateFeeResponse {
|
|
78
75
|
if ('overall_fee' in res) {
|
|
79
76
|
let gasInfo = {};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { computeHashOnElements, getSelectorFromName } from '../hash';
|
|
2
|
-
import {
|
|
2
|
+
import { MerkleTree } from '../merkle';
|
|
3
|
+
import { BigNumberish, isHex, toBN, toHex } from '../number';
|
|
3
4
|
import { encodeShortString } from '../shortString';
|
|
4
|
-
import { TypedData } from './types';
|
|
5
|
+
import { StarkNetMerkleType, StarkNetType, TypedData } from './types';
|
|
5
6
|
import { validateTypedData } from './utils';
|
|
6
7
|
|
|
7
8
|
export * from './types';
|
|
@@ -17,6 +18,19 @@ function getHex(value: BigNumberish): string {
|
|
|
17
18
|
}
|
|
18
19
|
}
|
|
19
20
|
|
|
21
|
+
export function prepareSelector(selector: string): string {
|
|
22
|
+
return isHex(selector) ? selector : getSelectorFromName(selector);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function isMerkleTreeType(type: StarkNetType): type is StarkNetMerkleType {
|
|
26
|
+
return type.type === 'merkletree';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface Context {
|
|
30
|
+
parent?: string;
|
|
31
|
+
key?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
20
34
|
/**
|
|
21
35
|
* Get the dependencies of a struct type. If a struct has the same dependency multiple times, it's only included once
|
|
22
36
|
* in the resulting array.
|
|
@@ -27,15 +41,10 @@ function getHex(value: BigNumberish): string {
|
|
|
27
41
|
* @return {string[]}
|
|
28
42
|
*/
|
|
29
43
|
export const getDependencies = (
|
|
30
|
-
|
|
44
|
+
types: TypedData['types'],
|
|
31
45
|
type: string,
|
|
32
46
|
dependencies: string[] = []
|
|
33
47
|
): string[] => {
|
|
34
|
-
// `getDependencies` is called by most other functions, so we validate the JSON schema here
|
|
35
|
-
if (!validateTypedData(typedData)) {
|
|
36
|
-
throw new Error('Typed data does not match JSON schema');
|
|
37
|
-
}
|
|
38
|
-
|
|
39
48
|
// Include pointers (struct arrays)
|
|
40
49
|
if (type[type.length - 1] === '*') {
|
|
41
50
|
// eslint-disable-next-line no-param-reassign
|
|
@@ -46,16 +55,16 @@ export const getDependencies = (
|
|
|
46
55
|
return dependencies;
|
|
47
56
|
}
|
|
48
57
|
|
|
49
|
-
if (!
|
|
58
|
+
if (!types[type]) {
|
|
50
59
|
return dependencies;
|
|
51
60
|
}
|
|
52
61
|
|
|
53
62
|
return [
|
|
54
63
|
type,
|
|
55
|
-
...
|
|
64
|
+
...types[type].reduce<string[]>(
|
|
56
65
|
(previous, t) => [
|
|
57
66
|
...previous,
|
|
58
|
-
...getDependencies(
|
|
67
|
+
...getDependencies(types, t.type, previous).filter(
|
|
59
68
|
(dependency) => !previous.includes(dependency)
|
|
60
69
|
),
|
|
61
70
|
],
|
|
@@ -64,6 +73,22 @@ export const getDependencies = (
|
|
|
64
73
|
];
|
|
65
74
|
};
|
|
66
75
|
|
|
76
|
+
function getMerkleTreeType(types: TypedData['types'], ctx: Context) {
|
|
77
|
+
if (ctx.parent && ctx.key) {
|
|
78
|
+
const parentType = types[ctx.parent];
|
|
79
|
+
const merkleType = parentType.find((t) => t.name === ctx.key)!;
|
|
80
|
+
const isMerkleTree = isMerkleTreeType(merkleType);
|
|
81
|
+
if (!isMerkleTree) {
|
|
82
|
+
throw new Error(`${ctx.key} is not a merkle tree`);
|
|
83
|
+
}
|
|
84
|
+
if (merkleType.contains.endsWith('*')) {
|
|
85
|
+
throw new Error(`Merkle tree contain property must not be an array but was given ${ctx.key}`);
|
|
86
|
+
}
|
|
87
|
+
return merkleType.contains;
|
|
88
|
+
}
|
|
89
|
+
return 'raw';
|
|
90
|
+
}
|
|
91
|
+
|
|
67
92
|
/**
|
|
68
93
|
* Encode a type to a string. All dependant types are alphabetically sorted.
|
|
69
94
|
*
|
|
@@ -71,13 +96,13 @@ export const getDependencies = (
|
|
|
71
96
|
* @param {string} type
|
|
72
97
|
* @return {string}
|
|
73
98
|
*/
|
|
74
|
-
export const encodeType = (
|
|
75
|
-
const [primary, ...dependencies] = getDependencies(
|
|
76
|
-
const
|
|
99
|
+
export const encodeType = (types: TypedData['types'], type: string): string => {
|
|
100
|
+
const [primary, ...dependencies] = getDependencies(types, type);
|
|
101
|
+
const newTypes = !primary ? [] : [primary, ...dependencies.sort()];
|
|
77
102
|
|
|
78
|
-
return
|
|
103
|
+
return newTypes
|
|
79
104
|
.map((dependency) => {
|
|
80
|
-
return `${dependency}(${
|
|
105
|
+
return `${dependency}(${types[dependency].map((t) => `${t.name}:${t.type}`)})`;
|
|
81
106
|
})
|
|
82
107
|
.join('');
|
|
83
108
|
};
|
|
@@ -89,8 +114,8 @@ export const encodeType = (typedData: TypedData, type: string): string => {
|
|
|
89
114
|
* @param {string} type
|
|
90
115
|
* @return {string}
|
|
91
116
|
*/
|
|
92
|
-
export const getTypeHash = (
|
|
93
|
-
return getSelectorFromName(encodeType(
|
|
117
|
+
export const getTypeHash = (types: TypedData['types'], type: string): string => {
|
|
118
|
+
return getSelectorFromName(encodeType(types, type));
|
|
94
119
|
};
|
|
95
120
|
|
|
96
121
|
/**
|
|
@@ -102,28 +127,47 @@ export const getTypeHash = (typedData: TypedData, type: string): string => {
|
|
|
102
127
|
* @param {any} data
|
|
103
128
|
* @returns {[string, string]}
|
|
104
129
|
*/
|
|
105
|
-
const encodeValue = (
|
|
106
|
-
|
|
130
|
+
export const encodeValue = (
|
|
131
|
+
types: TypedData['types'],
|
|
132
|
+
type: string,
|
|
133
|
+
data: unknown,
|
|
134
|
+
ctx: Context = {}
|
|
135
|
+
): [string, string] => {
|
|
136
|
+
if (types[type]) {
|
|
107
137
|
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
108
|
-
return [type, getStructHash(
|
|
138
|
+
return [type, getStructHash(types, type, data as Record<string, unknown>)];
|
|
109
139
|
}
|
|
110
140
|
|
|
111
141
|
if (
|
|
112
|
-
Object.keys(
|
|
142
|
+
Object.keys(types)
|
|
113
143
|
.map((x) => `${x}*`)
|
|
114
144
|
.includes(type)
|
|
115
145
|
) {
|
|
116
146
|
const structHashes: string[] = (data as unknown[]).map((struct) => {
|
|
117
147
|
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
118
|
-
return getStructHash(
|
|
148
|
+
return getStructHash(types, type.slice(0, -1), struct as Record<string, unknown>);
|
|
119
149
|
});
|
|
120
150
|
return [type, computeHashOnElements(structHashes)];
|
|
121
151
|
}
|
|
122
152
|
|
|
153
|
+
if (type === 'merkletree') {
|
|
154
|
+
const merkleTreeType = getMerkleTreeType(types, ctx);
|
|
155
|
+
const structHashes: string[] = (data as unknown[]).map((struct) => {
|
|
156
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
157
|
+
return encodeValue(types, merkleTreeType, struct as Record<string, unknown>)[1];
|
|
158
|
+
});
|
|
159
|
+
const { root } = new MerkleTree(structHashes as string[]);
|
|
160
|
+
return ['felt', root];
|
|
161
|
+
}
|
|
162
|
+
|
|
123
163
|
if (type === 'felt*') {
|
|
124
164
|
return ['felt*', computeHashOnElements(data as string[])];
|
|
125
165
|
}
|
|
126
166
|
|
|
167
|
+
if (type === 'selector') {
|
|
168
|
+
return ['felt', prepareSelector(data as string)];
|
|
169
|
+
}
|
|
170
|
+
|
|
127
171
|
return [type, getHex(data as string)];
|
|
128
172
|
};
|
|
129
173
|
|
|
@@ -135,25 +179,32 @@ const encodeValue = (typedData: TypedData, type: string, data: unknown): [string
|
|
|
135
179
|
* @param {string} type
|
|
136
180
|
* @param {Record<string, any>} data
|
|
137
181
|
*/
|
|
138
|
-
export const encodeData = <T extends TypedData>(
|
|
139
|
-
|
|
182
|
+
export const encodeData = <T extends TypedData>(
|
|
183
|
+
types: T['types'],
|
|
184
|
+
type: string,
|
|
185
|
+
data: T['message']
|
|
186
|
+
) => {
|
|
187
|
+
const [returnTypes, values] = types[type].reduce<[string[], string[]]>(
|
|
140
188
|
([ts, vs], field) => {
|
|
141
189
|
if (data[field.name] === undefined || data[field.name] === null) {
|
|
142
190
|
throw new Error(`Cannot encode data: missing data for '${field.name}'`);
|
|
143
191
|
}
|
|
144
192
|
|
|
145
193
|
const value = data[field.name];
|
|
146
|
-
const [t, encodedValue] = encodeValue(
|
|
194
|
+
const [t, encodedValue] = encodeValue(types, field.type, value, {
|
|
195
|
+
parent: type,
|
|
196
|
+
key: field.name,
|
|
197
|
+
});
|
|
147
198
|
|
|
148
199
|
return [
|
|
149
200
|
[...ts, t],
|
|
150
201
|
[...vs, encodedValue],
|
|
151
202
|
];
|
|
152
203
|
},
|
|
153
|
-
[['felt'], [getTypeHash(
|
|
204
|
+
[['felt'], [getTypeHash(types, type)]]
|
|
154
205
|
);
|
|
155
206
|
|
|
156
|
-
return [
|
|
207
|
+
return [returnTypes, values];
|
|
157
208
|
};
|
|
158
209
|
|
|
159
210
|
/**
|
|
@@ -166,27 +217,30 @@ export const encodeData = <T extends TypedData>(typedData: T, type: string, data
|
|
|
166
217
|
* @return {Buffer}
|
|
167
218
|
*/
|
|
168
219
|
export const getStructHash = <T extends TypedData>(
|
|
169
|
-
|
|
220
|
+
types: T['types'],
|
|
170
221
|
type: string,
|
|
171
222
|
data: T['message']
|
|
172
223
|
) => {
|
|
173
|
-
return computeHashOnElements(encodeData(
|
|
224
|
+
return computeHashOnElements(encodeData(types, type, data)[1]);
|
|
174
225
|
};
|
|
175
226
|
|
|
176
227
|
/**
|
|
177
|
-
* Get the EIP-191 encoded message to sign, from the typedData object.
|
|
178
|
-
* with Keccak256.
|
|
228
|
+
* Get the EIP-191 encoded message to sign, from the typedData object.
|
|
179
229
|
*
|
|
180
230
|
* @param {TypedData} typedData
|
|
181
231
|
* @param {BigNumberish} account
|
|
182
232
|
* @return {string}
|
|
183
233
|
*/
|
|
184
234
|
export const getMessageHash = (typedData: TypedData, account: BigNumberish): string => {
|
|
235
|
+
if (!validateTypedData(typedData)) {
|
|
236
|
+
throw new Error('Typed data does not match JSON schema');
|
|
237
|
+
}
|
|
238
|
+
|
|
185
239
|
const message = [
|
|
186
240
|
encodeShortString('StarkNet Message'),
|
|
187
|
-
getStructHash(typedData, 'StarkNetDomain', typedData.domain),
|
|
241
|
+
getStructHash(typedData.types, 'StarkNetDomain', typedData.domain),
|
|
188
242
|
account,
|
|
189
|
-
getStructHash(typedData, typedData.primaryType, typedData.message),
|
|
243
|
+
getStructHash(typedData.types, typedData.primaryType, typedData.message),
|
|
190
244
|
];
|
|
191
245
|
|
|
192
246
|
return computeHashOnElements(message);
|
|
@@ -1,13 +1,21 @@
|
|
|
1
|
+
export type StarkNetMerkleType = {
|
|
2
|
+
name: string;
|
|
3
|
+
type: 'merkletree';
|
|
4
|
+
contains: string;
|
|
5
|
+
};
|
|
6
|
+
|
|
1
7
|
/**
|
|
2
8
|
* A single type, as part of a struct. The `type` field can be any of the EIP-712 supported types.
|
|
3
9
|
*
|
|
4
10
|
* Note that the `uint` and `int` aliases like in Solidity, and fixed point numbers are not supported by the EIP-712
|
|
5
11
|
* standard.
|
|
6
12
|
*/
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
13
|
+
export type StarkNetType =
|
|
14
|
+
| {
|
|
15
|
+
name: string;
|
|
16
|
+
type: string;
|
|
17
|
+
}
|
|
18
|
+
| StarkNetMerkleType;
|
|
11
19
|
|
|
12
20
|
/**
|
|
13
21
|
* The EIP712 domain struct. Any of these fields are optional, but it must contain at least one field.
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Starknet RPC version 0.1.0
|
|
3
|
+
* starknet_api_openrpc version 0.31.0
|
|
4
|
+
*
|
|
5
|
+
* TypeScript Representation of OpenRpc protocol types | results
|
|
6
|
+
* errors are not implemented here only results
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* "type": "string",
|
|
10
|
+
* "title": "Field element",
|
|
11
|
+
* "$comment": "A field element, represented as a string of hex digits",
|
|
12
|
+
* "description": "A field element. Represented as up to 63 hex digits and leading 4 bits zeroed.",
|
|
13
|
+
* "pattern": "^0x0[a-fA-F0-9]{1,63}$"
|
|
14
|
+
*/
|
|
15
|
+
declare type FELT = string;
|
|
16
|
+
declare type BLOCK_NUMBER = number;
|
|
17
|
+
declare type BLOCK_HASH = FELT;
|
|
18
|
+
declare type TXN_HASH = FELT;
|
|
19
|
+
declare type TXN_STATUS = 'PENDING' | 'ACCEPTED_ON_L2' | 'ACCEPTED_ON_L1' | 'REJECTED';
|
|
20
|
+
declare type TXN_TYPE = 'DECLARE' | 'DEPLOY' | 'INVOKE' | 'L1_HANDLER';
|
|
21
|
+
declare type MSG_TO_L1 = {
|
|
22
|
+
to_address: FELT;
|
|
23
|
+
payload: Array<FELT>;
|
|
24
|
+
};
|
|
25
|
+
declare type EVENT = {
|
|
26
|
+
from_address: FELT;
|
|
27
|
+
keys: Array<FELT>;
|
|
28
|
+
data: Array<FELT>;
|
|
29
|
+
};
|
|
30
|
+
declare type COMMON_RECEIPT_PROPERTIES = {
|
|
31
|
+
transaction_hash: TXN_HASH;
|
|
32
|
+
actual_fee: FELT;
|
|
33
|
+
status: TXN_STATUS;
|
|
34
|
+
block_hash: BLOCK_HASH;
|
|
35
|
+
block_number: BLOCK_NUMBER;
|
|
36
|
+
type?: TXN_TYPE;
|
|
37
|
+
};
|
|
38
|
+
declare type INVOKE_TXN_RECEIPT_PROPERTIES = {
|
|
39
|
+
messages_sent: MSG_TO_L1;
|
|
40
|
+
events: EVENT;
|
|
41
|
+
};
|
|
42
|
+
declare type PENDING_COMMON_RECEIPT_PROPERTIES = {
|
|
43
|
+
transaction_hash: TXN_HASH;
|
|
44
|
+
actual_fee: FELT;
|
|
45
|
+
type?: TXN_TYPE;
|
|
46
|
+
};
|
|
47
|
+
declare type INVOKE_TXN_RECEIPT = COMMON_RECEIPT_PROPERTIES & INVOKE_TXN_RECEIPT_PROPERTIES;
|
|
48
|
+
declare type L1_HANDLER_TXN_RECEIPT = COMMON_RECEIPT_PROPERTIES;
|
|
49
|
+
declare type DECLARE_TXN_RECEIPT = COMMON_RECEIPT_PROPERTIES;
|
|
50
|
+
declare type DEPLOY_TXN_RECEIPT = COMMON_RECEIPT_PROPERTIES;
|
|
51
|
+
declare type PENDING_INVOKE_TXN_RECEIPT = PENDING_COMMON_RECEIPT_PROPERTIES & INVOKE_TXN_RECEIPT_PROPERTIES;
|
|
52
|
+
declare type PENDING_TXN_RECEIPT = PENDING_INVOKE_TXN_RECEIPT | PENDING_COMMON_RECEIPT_PROPERTIES;
|
|
53
|
+
declare type TXN_RECEIPT = INVOKE_TXN_RECEIPT | L1_HANDLER_TXN_RECEIPT | DECLARE_TXN_RECEIPT | DEPLOY_TXN_RECEIPT | PENDING_TXN_RECEIPT;
|
|
54
|
+
export declare namespace RPC_1 {
|
|
55
|
+
type GetTransactionReceiptResponse = TXN_RECEIPT;
|
|
56
|
+
type Methods = {
|
|
57
|
+
starknet_getTransactionReceipt: {
|
|
58
|
+
QUERY: never;
|
|
59
|
+
REQUEST: any[];
|
|
60
|
+
RESPONSE: GetTransactionReceiptResponse;
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
declare type BLOCK_HEADER = {
|
|
65
|
+
block_hash: BLOCK_HASH;
|
|
66
|
+
parent_hash: BLOCK_HASH;
|
|
67
|
+
block_number: BLOCK_NUMBER;
|
|
68
|
+
new_root: FELT;
|
|
69
|
+
timestamp: number;
|
|
70
|
+
sequencer_address: FELT;
|
|
71
|
+
};
|
|
72
|
+
declare type BLOCK_BODY_WITH_TX_HASHES = {
|
|
73
|
+
transactions: Array<TXN_HASH>;
|
|
74
|
+
};
|
|
75
|
+
declare type BLOCK_WITH_TX_HASHES = {
|
|
76
|
+
status: TXN_STATUS;
|
|
77
|
+
} & BLOCK_HEADER & BLOCK_BODY_WITH_TX_HASHES;
|
|
78
|
+
declare type PENDING_BLOCK_WITH_TX_HASHES = BLOCK_BODY_WITH_TX_HASHES & {
|
|
79
|
+
timestamp: number;
|
|
80
|
+
sequencer_address: FELT;
|
|
81
|
+
parent_hash: BLOCK_HASH;
|
|
82
|
+
};
|
|
83
|
+
declare type BLOCK_STATUS = 'PENDING' | 'ACCEPTED_ON_L2' | 'ACCEPTED_ON_L1' | 'REJECTED';
|
|
84
|
+
/**
|
|
85
|
+
* "title": "An integer number in hex format (0x...)",
|
|
86
|
+
* "pattern": "^0x[a-fA-F0-9]+$"
|
|
87
|
+
*/
|
|
88
|
+
declare type NUM_AS_HEX = string;
|
|
89
|
+
declare type SIGNATURE = Array<FELT>;
|
|
90
|
+
declare type COMMON_TXN_PROPERTIES = {
|
|
91
|
+
transaction_hash: TXN_HASH;
|
|
92
|
+
max_fee: FELT;
|
|
93
|
+
version: NUM_AS_HEX;
|
|
94
|
+
signature: SIGNATURE;
|
|
95
|
+
nonce: FELT;
|
|
96
|
+
type: TXN_TYPE;
|
|
97
|
+
};
|
|
98
|
+
declare type ADDRESS = FELT;
|
|
99
|
+
declare type FUNCTION_CALL = {
|
|
100
|
+
contract_address: ADDRESS;
|
|
101
|
+
entry_point_selector: FELT;
|
|
102
|
+
calldata: Array<FELT>;
|
|
103
|
+
};
|
|
104
|
+
declare type INVOKE_TXN = COMMON_TXN_PROPERTIES & FUNCTION_CALL;
|
|
105
|
+
declare type DECLARE_TXN = COMMON_TXN_PROPERTIES & {
|
|
106
|
+
class_hash: FELT;
|
|
107
|
+
sender_address: ADDRESS;
|
|
108
|
+
};
|
|
109
|
+
declare type DEPLOY_TXN = {
|
|
110
|
+
transaction_hash: TXN_HASH;
|
|
111
|
+
class_hash: FELT;
|
|
112
|
+
version: NUM_AS_HEX;
|
|
113
|
+
type: TXN_TYPE;
|
|
114
|
+
contract_address: FELT;
|
|
115
|
+
contract_address_salt: FELT;
|
|
116
|
+
constructor_calldata: Array<FELT>;
|
|
117
|
+
};
|
|
118
|
+
declare type TXN = INVOKE_TXN | DECLARE_TXN | DEPLOY_TXN;
|
|
119
|
+
declare type BLOCK_BODY_WITH_TXS = {
|
|
120
|
+
transactions: Array<TXN>;
|
|
121
|
+
};
|
|
122
|
+
declare type BLOCK_WITH_TXS = {
|
|
123
|
+
status: BLOCK_STATUS;
|
|
124
|
+
} & BLOCK_HEADER & BLOCK_BODY_WITH_TXS;
|
|
125
|
+
declare type PENDING_BLOCK_WITH_TXS = BLOCK_BODY_WITH_TXS & {
|
|
126
|
+
timestamp: number;
|
|
127
|
+
sequencer_address: FELT;
|
|
128
|
+
parent_hash: BLOCK_HASH;
|
|
129
|
+
};
|
|
130
|
+
declare type CONTRACT_CLASS = {
|
|
131
|
+
program: string;
|
|
132
|
+
entry_points_by_type: {
|
|
133
|
+
CONSTRUCTOR: CONTRACT_ENTRY_POINT_LIST;
|
|
134
|
+
EXTERNAL: CONTRACT_ENTRY_POINT_LIST;
|
|
135
|
+
L1_HANDLER: CONTRACT_ENTRY_POINT_LIST;
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
declare type CONTRACT_ENTRY_POINT_LIST = Array<CONTRACT_ENTRY_POINT>;
|
|
139
|
+
declare type CONTRACT_ENTRY_POINT = {
|
|
140
|
+
offset: NUM_AS_HEX;
|
|
141
|
+
selector: FELT;
|
|
142
|
+
};
|
|
143
|
+
export declare namespace OPENRPC {
|
|
144
|
+
type GetBlockWithTxHashesResponse = BLOCK_WITH_TX_HASHES | PENDING_BLOCK_WITH_TX_HASHES;
|
|
145
|
+
type GetBlockWithTxs = BLOCK_WITH_TXS | PENDING_BLOCK_WITH_TXS;
|
|
146
|
+
type GetStorageAtResponse = FELT;
|
|
147
|
+
type GetTransactionByHashResponse = TXN;
|
|
148
|
+
type GetTransactionByBlockIdAndIndex = TXN;
|
|
149
|
+
type GetClassResponse = CONTRACT_CLASS;
|
|
150
|
+
}
|
|
151
|
+
export {};
|