Skip to content

Commit bae49e8

Browse files
authored
Merge pull request #166 from 0xs34n/feature/new-hash-formular-for-accounts
2 parents f9eeee3 + 49b04e2 commit bae49e8

File tree

18 files changed

+32282
-38830
lines changed

18 files changed

+32282
-38830
lines changed

__mocks__/ArgentAccount.json

Lines changed: 32022 additions & 38726 deletions
Large diffs are not rendered by default.

__tests__/accountContract.test.ts

Lines changed: 42 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
import { Contract, defaultProvider, ec, hash, number, stark } from '../src';
2+
import { StarknetChainId } from '../src/constants';
3+
import {
4+
calculcateTransactionHash,
5+
getSelectorFromName,
6+
transactionVersion,
7+
} from '../src/utils/hash';
8+
import { fromCallsToExecuteCalldataWithNonce } from '../src/utils/transaction';
29
import { compiledArgentAccount, compiledErc20 } from './fixtures';
310

411
describe('getStarkAccountFromPrivateKey()', () => {
@@ -20,6 +27,41 @@ describe('getStarkAccountFromPrivateKey()', () => {
2027
});
2128
});
2229

30+
test('build tx', async () => {
31+
const privateKey = '0x1B69B4BE052FAB1';
32+
const keyPair = ec.getKeyPair(privateKey);
33+
const address = ec.getStarkKey(keyPair);
34+
35+
expect(address).toBe('0x04024999b9574cb7623679ce049a609db62a95098982c5b28ac61abdebd1c82b');
36+
37+
const selector = hash.getSelectorFromName('transfer');
38+
39+
expect(selector).toMatchInlineSnapshot();
40+
41+
const calls = [{ contractAddress: '1', entrypoint: 'transfer', calldata: ['6', '7'] }];
42+
const calldata = fromCallsToExecuteCalldataWithNonce(calls, 0);
43+
44+
const msgHash = calculcateTransactionHash(
45+
address,
46+
transactionVersion,
47+
getSelectorFromName('__execute__'),
48+
calldata,
49+
0,
50+
StarknetChainId.TESTNET
51+
);
52+
expect(number.toBN(msgHash).toString()).toMatchInlineSnapshot(
53+
`"235855380881994314533025886817815774848495061484535023348790852315407085619"`
54+
);
55+
56+
const [r, s] = ec.sign(keyPair, msgHash);
57+
expect(r.toString()).toMatchInlineSnapshot(
58+
`"181489288548431284937202760565682158657883789985879744111612429574110648095"`
59+
);
60+
expect(s.toString()).toMatchInlineSnapshot(
61+
`"2055384802167699202203509702082340762385659879831017273872106910763470114538"`
62+
);
63+
});
64+
2365
describe('deploy and test Wallet', () => {
2466
const privateKey = stark.randomAddress();
2567

@@ -64,35 +106,3 @@ describe('deploy and test Wallet', () => {
64106
expect(res).toStrictEqual(number.toBN(1000));
65107
});
66108
});
67-
68-
test('build tx', async () => {
69-
const privateKey = '0x1B69B4BE052FAB1';
70-
const keyPair = ec.getKeyPair(privateKey);
71-
const address = ec.getStarkKey(keyPair);
72-
73-
expect(address).toBe('0x04024999b9574cb7623679ce049a609db62a95098982c5b28ac61abdebd1c82b');
74-
75-
const selector = hash.getSelectorFromName('transfer');
76-
77-
expect(selector).toBe(
78-
number.toHex(
79-
number.toBN('232670485425082704932579856502088130646006032362877466777181098476241604910')
80-
)
81-
);
82-
83-
const calls = [{ contractAddress: '1', entrypoint: 'transfer', calldata: ['6', '7'] }];
84-
const msgHash = hash.hashMulticall(address, calls, '0', '0');
85-
expect(number.toBN(msgHash).toString()).toStrictEqual(
86-
number
87-
.toBN('533725737276146993132325070982049323585915612981489962412873515411469143806')
88-
.toString()
89-
);
90-
91-
const [r, s] = ec.sign(keyPair, msgHash);
92-
expect(r.toString()).toBe(
93-
'3081830197073374427897814075820860503521735760640862828374253887454357679197'
94-
);
95-
expect(s.toString()).toBe(
96-
'384293936273611705317490990661155378189310283917528660618713929845936492551'
97-
);
98-
});

__tests__/constancts.ts

Lines changed: 0 additions & 2 deletions
This file was deleted.

__tests__/contract.test.ts

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@ import { Account, Contract, ContractFactory, Provider, defaultProvider, ec, star
44
import { getSelectorFromName } from '../src/utils/hash';
55
import { BigNumberish, toBN } from '../src/utils/number';
66
import { compileCalldata } from '../src/utils/stark';
7-
import { ACCOUNT_ADDRESS, PRIVATE_KEY } from './constancts';
8-
import { compiledErc20, compiledMulticall, compiledTypeTransformation } from './fixtures';
7+
import {
8+
compiledArgentAccount,
9+
compiledErc20,
10+
compiledMulticall,
11+
compiledTypeTransformation,
12+
} from './fixtures';
913

1014
describe('class Contract {}', () => {
1115
const wallet = stark.randomAddress();
@@ -204,21 +208,29 @@ describe('class Contract {}', () => {
204208
});
205209

206210
describe('Contract interaction with Account', () => {
207-
const starkKeyPair = ec.getKeyPair(PRIVATE_KEY);
208211
let account: Account;
209212
let erc20: Contract;
210213
let erc20Address: string;
211214

212215
beforeAll(async () => {
213-
account = new Account(defaultProvider, ACCOUNT_ADDRESS, starkKeyPair);
216+
const starkKeyPair = ec.genKeyPair();
217+
const starkKeyPub = ec.getStarkKey(starkKeyPair);
218+
const { address } = await defaultProvider.deployContract({
219+
contract: compiledArgentAccount,
220+
addressSalt: starkKeyPub,
221+
});
222+
expect(address).toBeDefined();
223+
account = new Account(defaultProvider, address, starkKeyPair);
224+
const accountContract = new Contract(compiledArgentAccount.abi, address);
225+
await accountContract.initialize(starkKeyPub, '0');
214226

215227
const erc20Response = await defaultProvider.deployContract({
216228
contract: compiledErc20,
217229
});
218230
erc20Address = erc20Response.address;
219231
erc20 = new Contract(compiledErc20.abi, erc20Address, defaultProvider);
220-
await defaultProvider.waitForTransaction(erc20Response.transaction_hash);
221232
expect(erc20Response.code).toBe('TRANSACTION_RECEIVED');
233+
await defaultProvider.waitForTransaction(erc20Response.transaction_hash);
222234

223235
const mintResponse = await erc20.mint(account.address, '1000');
224236

@@ -251,7 +263,9 @@ describe('class Contract {}', () => {
251263
});
252264

253265
test('invoke contract by wallet owner', async () => {
254-
const { transaction_hash, code } = await erc20.transfer(toBN(erc20Address).toString(), 10);
266+
const { transaction_hash, code } = await erc20.transfer(erc20Address, 10, {
267+
maxFee: 0,
268+
});
255269
expect(code).toBe('TRANSACTION_RECEIVED');
256270
await defaultProvider.waitForTransaction(transaction_hash);
257271
const { res } = await erc20.balance_of(account.address);

__tests__/utils/__snapshots__/utils.browser.test.ts.snap

Lines changed: 2 additions & 2 deletions
Large diffs are not rendered by default.

__tests__/utils/__snapshots__/utils.test.ts.snap

Lines changed: 2 additions & 2 deletions
Large diffs are not rendered by default.

__tests__/utils/ellipticalCurve.test.ts

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
1+
import { StarknetChainId } from '../../src/constants';
12
import { ec, getKeyPair, getStarkKey, sign, verify } from '../../src/utils/ellipticCurve';
23
import { removeHexPrefix } from '../../src/utils/encode';
3-
import { computeHashOnElements, hashMulticall, pedersen } from '../../src/utils/hash';
4+
import {
5+
calculcateTransactionHash,
6+
computeHashOnElements,
7+
getSelectorFromName,
8+
pedersen,
9+
transactionVersion,
10+
} from '../../src/utils/hash';
411
import { toBN, toHex } from '../../src/utils/number';
12+
import { fromCallsToExecuteCalldataWithNonce } from '../../src/utils/transaction';
513

614
test('getKeyPair()', () => {
715
const privateKey = '0x019800ea6a9a73f94aee6a3d2edf018fc770443e90c7ba121e8303ec6b349279';
@@ -42,17 +50,27 @@ test('hashMessage()', () => {
4250
];
4351
const nonce = '3';
4452
const maxFee = '0';
45-
const hashMsg = hashMulticall(account, transactions, nonce, maxFee);
46-
expect(hashMsg).toBe(
47-
toHex(toBN('1608351043472325350463069815257733118091727529101532499046754244230898025592'))
53+
const calldata = fromCallsToExecuteCalldataWithNonce(transactions, nonce);
54+
55+
const hashMsg = calculcateTransactionHash(
56+
account,
57+
transactionVersion,
58+
getSelectorFromName('__execute__'),
59+
calldata,
60+
maxFee,
61+
StarknetChainId.TESTNET
62+
);
63+
64+
expect(hashMsg).toMatchInlineSnapshot(
65+
`"0x4c337c6bf32b2cf2b8ae54064e4b982c214660e8d0423b431a3fde10b9b9c02"`
4866
);
4967
const keyPair = getKeyPair(privateKey);
5068
const [r, s] = sign(keyPair, removeHexPrefix(hashMsg));
51-
expect(r.toString()).toStrictEqual(
52-
toBN('1079537730825246752292590270213864261175133133352510235773017189606850691611').toString()
69+
expect(r.toString()).toMatchInlineSnapshot(
70+
`"1944132633844378384908742523072599391732300777648030785844673145513474741467"`
5371
);
54-
expect(s.toString()).toStrictEqual(
55-
toBN('2904560423220491364719171767721067837294296476624248675613584621502231297000').toString()
72+
expect(s.toString()).toMatchInlineSnapshot(
73+
`"1067771353159635307522498807851959257107695451405842425488451092336556917559"`
5674
);
5775
});
5876

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { StarknetChainId, TransactionHashPrefix } from '../../src/constants';
2+
import { calculateTransactionHashCommon } from '../../src/utils/hash';
3+
4+
describe('calculateTransactionHashCommon()', () => {
5+
test('should match most simple python output', () => {
6+
const result = calculateTransactionHashCommon(
7+
TransactionHashPrefix.INVOKE,
8+
'0x0',
9+
'0x2a',
10+
'0x64',
11+
[],
12+
'0x0',
13+
StarknetChainId.TESTNET
14+
);
15+
expect(result).toBe('0x7d260744de9d8c55e7675a34512d1951a7b262c79e685d26599edd2948de959');
16+
});
17+
});

package-lock.json

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/account/default.ts

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import assert from 'minimalistic-assert';
22

3+
import { ZERO } from '../constants';
34
import { Provider } from '../provider';
45
import { BlockIdentifier } from '../provider/utils';
56
import { Signer, SignerInterface } from '../signer';
@@ -9,6 +10,7 @@ import {
910
Call,
1011
EstimateFeeResponse,
1112
InvocationsDetails,
13+
InvocationsSignerDetails,
1214
InvokeFunctionTransaction,
1315
KeyPair,
1416
Signature,
@@ -19,12 +21,12 @@ import {
1921
computeHashOnElements,
2022
feeTransactionVersion,
2123
getSelectorFromName,
22-
transactionPrefix,
2324
transactionVersion,
2425
} from '../utils/hash';
2526
import { BigNumberish, bigNumberishArrayToDecimalStringArray, toBN, toHex } from '../utils/number';
27+
import { encodeShortString } from '../utils/shortString';
2628
import { compileCalldata, estimatedFeeToMaxFee } from '../utils/stark';
27-
import { fromCallsToExecuteCalldata } from '../utils/transaction';
29+
import { fromCallsToExecuteCalldataWithNonce } from '../utils/transaction';
2830
import { TypedData, getMessageHash } from '../utils/typedData';
2931
import { AccountInterface } from './interface';
3032

@@ -58,15 +60,18 @@ export class Account extends Provider implements AccountInterface {
5860
const transactions = Array.isArray(calls) ? calls : [calls];
5961
const nonce = providedNonce ?? (await this.getNonce());
6062
const version = toBN(feeTransactionVersion);
61-
const signerDetails = {
63+
64+
const signerDetails: InvocationsSignerDetails = {
6265
walletAddress: this.address,
6366
nonce: toBN(nonce),
64-
maxFee: toBN('0'),
67+
maxFee: ZERO,
6568
version,
69+
chainId: this.chainId,
6670
};
71+
6772
const signature = await this.signer.signTransaction(transactions, signerDetails);
6873

69-
const calldata = [...fromCallsToExecuteCalldata(transactions), signerDetails.nonce.toString()];
74+
const calldata = fromCallsToExecuteCalldataWithNonce(transactions, nonce);
7075
return this.fetchEndpoint(
7176
'estimate_fee',
7277
{ blockIdentifier },
@@ -96,22 +101,24 @@ export class Account extends Provider implements AccountInterface {
96101
const transactions = Array.isArray(calls) ? calls : [calls];
97102
const nonce = toBN(transactionsDetail.nonce ?? (await this.getNonce()));
98103
let maxFee: BigNumberish = '0';
99-
if (transactionsDetail.maxFee) {
104+
if (transactionsDetail.maxFee || transactionsDetail.maxFee === 0) {
100105
maxFee = transactionsDetail.maxFee;
101106
} else {
102107
const estimatedFee = (await this.estimateFee(transactions, { nonce })).amount;
103108
maxFee = estimatedFeeToMaxFee(estimatedFee).toString();
104109
}
105-
const signerDetails = {
110+
111+
const signerDetails: InvocationsSignerDetails = {
106112
walletAddress: this.address,
107113
nonce,
108114
maxFee,
109115
version: toBN(transactionVersion),
116+
chainId: this.chainId,
110117
};
111118

112119
const signature = await this.signer.signTransaction(transactions, signerDetails, abis);
113120

114-
const calldata = [...fromCallsToExecuteCalldata(transactions), signerDetails.nonce.toString()];
121+
const calldata = fromCallsToExecuteCalldataWithNonce(transactions, nonce);
115122
return this.fetchEndpoint('add_transaction', undefined, {
116123
type: 'INVOKE_FUNCTION',
117124
contract_address: this.address,
@@ -161,7 +168,7 @@ export class Account extends Provider implements AccountInterface {
161168
.map(computeHashOnElements);
162169

163170
return computeHashOnElements([
164-
transactionPrefix,
171+
encodeShortString('StarkNet Transaction'),
165172
account,
166173
computeHashOnElements(hashArray),
167174
nonce,

0 commit comments

Comments
 (0)