Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:

services:
devnet:
image: janek2601/starknet-devnet-patched
image: shardlabs/starknet-devnet:0.3.1-seed0
ports:
- 5050:5050
env:
Expand Down
7 changes: 2 additions & 5 deletions __tests__/account.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,8 @@ describe('deploy and test Wallet', () => {
});

test('execute with custom nonce', async () => {
const { result } = await account.callContract({
contractAddress: account.address,
entrypoint: 'get_nonce',
});
const nonce = toBN(result[0]).toNumber();
const result = await account.getNonce();
const nonce = toBN(result).toNumber();
const { transaction_hash } = await account.execute(
{
contractAddress: erc20Address,
Expand Down
1 change: 0 additions & 1 deletion __tests__/contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ describe('class Contract {}', () => {
expect(res).toHaveProperty('contractAddress');
expect(res).toHaveProperty('entrypoint');
expect(res).toHaveProperty('calldata');
expect(res).toHaveProperty('signature');
});

test('estimate gas fee for `mint` should fail when connected to the provider', async () => {
Expand Down
5 changes: 5 additions & 0 deletions __tests__/defaultProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ describe('defaultProvider', () => {
return expect(block).toHaveProperty('block_number');
});

test('getNonce()', async () => {
const nonce = await testProvider.getNonce(exampleContractAddress);
return expect(nonce).toEqual('0x0');
});

describe('getStorageAt', () => {
test('with "key" type of number', () => {
return expect(testProvider.getStorageAt(exampleContractAddress, 0)).resolves.not.toThrow();
Expand Down
18 changes: 10 additions & 8 deletions __tests__/sequencerProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ import {

// Run only if Devnet Sequencer
describeIfSequencer('SequencerProvider', () => {
let provider: SequencerProvider;
let sequencerProvider: SequencerProvider;
let customSequencerProvider: Provider;
let exampleContractAddress: string;

beforeAll(async () => {
provider = getTestProvider() as SequencerProvider;
sequencerProvider = getTestProvider() as SequencerProvider;
customSequencerProvider = new Provider({
sequencer: {
baseUrl: 'https://alpha4.starknet.io',
Expand All @@ -28,32 +28,34 @@ describeIfSequencer('SequencerProvider', () => {
let exampleTransactionHash: string;

beforeAll(async () => {
const { transaction_hash, contract_address } = await provider.deployContract({
const { transaction_hash, contract_address } = await sequencerProvider.deployContract({
contract: compiledErc20,
});
await provider.waitForTransaction(transaction_hash);
await sequencerProvider.waitForTransaction(transaction_hash);
exampleTransactionHash = transaction_hash;
exampleContractAddress = contract_address;
});

test('getTransactionStatus()', async () => {
return expect(provider.getTransactionStatus(exampleTransactionHash)).resolves.not.toThrow();
return expect(
sequencerProvider.getTransactionStatus(exampleTransactionHash)
).resolves.not.toThrow();
});

test('transaction trace', async () => {
const transactionTrace = await provider.getTransactionTrace(exampleTransactionHash);
const transactionTrace = await sequencerProvider.getTransactionTrace(exampleTransactionHash);
expect(transactionTrace).toHaveProperty('function_invocation');
expect(transactionTrace).toHaveProperty('signature');
});

test('getCode() -> { bytecode }', async () => {
const code = await provider.getCode(exampleContractAddress);
const code = await sequencerProvider.getCode(exampleContractAddress);
return expect(Array.isArray(code.bytecode)).toBe(true);
});

describeIfNotDevnet('which are not available on devnet', () => {
test('getContractAddresses()', async () => {
const { GpsStatementVerifier, Starknet } = await provider.getContractAddresses();
const { GpsStatementVerifier, Starknet } = await sequencerProvider.getContractAddresses();
expect(typeof GpsStatementVerifier).toBe('string');
expect(typeof Starknet).toBe('string');
});
Expand Down
15 changes: 7 additions & 8 deletions __tests__/utils/ellipticalCurve.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ import { StarknetChainId } from '../../src/constants';
import { ec, getKeyPair, getStarkKey, sign, verify } from '../../src/utils/ellipticCurve';
import { removeHexPrefix } from '../../src/utils/encode';
import {
calculcateTransactionHash,
calculateTransactionHash,
computeHashOnElements,
getSelectorFromName,
pedersen,
transactionVersion,
} from '../../src/utils/hash';
Expand Down Expand Up @@ -57,25 +56,25 @@ test('hashMessage()', () => {
const maxFee = '0';
const calldata = fromCallsToExecuteCalldataWithNonce(transactions, nonce);

const hashMsg = calculcateTransactionHash(
const hashMsg = calculateTransactionHash(
account,
transactionVersion,
getSelectorFromName('__execute__'),
calldata,
maxFee,
StarknetChainId.TESTNET
StarknetChainId.TESTNET,
nonce
);

expect(hashMsg).toMatchInlineSnapshot(
`"0x4c337c6bf32b2cf2b8ae54064e4b982c214660e8d0423b431a3fde10b9b9c02"`
`"0x6d1706bd3d1ba7c517be2a2a335996f63d4738e2f182144d078a1dd9997062e"`
);
const keyPair = getKeyPair(privateKey);
const [r, s] = sign(keyPair, removeHexPrefix(hashMsg));
expect(r.toString()).toMatchInlineSnapshot(
`"1944132633844378384908742523072599391732300777648030785844673145513474741467"`
`"1427981024487605678086498726488552139932400435436186597196374630267616399345"`
);
expect(s.toString()).toMatchInlineSnapshot(
`"1067771353159635307522498807851959257107695451405842425488451092336556917559"`
`"1853664302719670721837677288395394946745467311923401353018029119631574115563"`
);
});

Expand Down
36 changes: 17 additions & 19 deletions src/account/default.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ZERO } from '../constants';
import { ProviderInterface, ProviderOptions } from '../provider';
import { Provider } from '../provider/default';
import { BlockIdentifier } from '../provider/utils';
import { Signer, SignerInterface } from '../signer';
import {
Abi,
Expand All @@ -12,10 +13,10 @@ import {
Signature,
} from '../types';
import { EstimateFee, EstimateFeeDetails } from '../types/account';
import { feeTransactionVersion, transactionVersion } from '../utils/hash';
import { BigNumberish, toBN, toHex } from '../utils/number';
import { transactionVersion } from '../utils/hash';
import { BigNumberish, toBN } from '../utils/number';
import { compileCalldata, estimatedFeeToMaxFee } from '../utils/stark';
import { fromCallsToExecuteCalldataWithNonce } from '../utils/transaction';
import { fromCallsToExecuteCalldata } from '../utils/transaction';
import { TypedData, getMessageHash } from '../utils/typedData';
import { AccountInterface } from './interface';

Expand All @@ -32,38 +33,34 @@ export class Account extends Provider implements AccountInterface {
'getPubKey' in keyPairOrSigner ? keyPairOrSigner : new Signer(keyPairOrSigner as KeyPair);
}

public async getNonce(): Promise<string> {
const { result } = await this.callContract({
contractAddress: this.address,
entrypoint: 'get_nonce',
});
return toHex(toBN(result[0]));
public async getNonce(blockIdentifier?: BlockIdentifier): Promise<BigNumberish> {
return super.getNonce(this.address, blockIdentifier);
}

public async estimateFee(
calls: Call | Call[],
{ nonce: providedNonce, blockIdentifier }: EstimateFeeDetails = {}
): Promise<EstimateFee> {
const transactions = Array.isArray(calls) ? calls : [calls];
const nonce = providedNonce ?? (await this.getNonce());
const version = toBN(feeTransactionVersion);
const nonce = toBN(providedNonce ?? (await this.getNonce()));
const version = toBN(transactionVersion);
const chainId = await this.getChainId();

const signerDetails: InvocationsSignerDetails = {
walletAddress: this.address,
nonce: toBN(nonce),
nonce,
maxFee: ZERO,
version,
chainId,
};

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

const calldata = fromCallsToExecuteCalldataWithNonce(transactions, nonce);
const calldata = fromCallsToExecuteCalldata(transactions);
const response = await super.getEstimateFee(
{ contractAddress: this.address, entrypoint: '__execute__', calldata, signature },
blockIdentifier,
{ version }
{ contractAddress: this.address, calldata, signature },
{ version, nonce },
blockIdentifier
);

const suggestedMaxFee = estimatedFeeToMaxFee(response.overall_fee);
Expand Down Expand Up @@ -112,11 +109,12 @@ export class Account extends Provider implements AccountInterface {

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

const calldata = fromCallsToExecuteCalldataWithNonce(transactions, nonce);
const calldata = fromCallsToExecuteCalldata(transactions);

return this.invokeFunction(
{ contractAddress: this.address, entrypoint: '__execute__', calldata, signature },
{ contractAddress: this.address, calldata, signature },
{
nonce,
maxFee,
version,
}
Expand Down Expand Up @@ -158,7 +156,7 @@ export class Account extends Provider implements AccountInterface {
try {
await this.callContract({
contractAddress: this.address,
entrypoint: 'is_valid_signature',
entrypoint: 'isValidSignature',
calldata: compileCalldata({
hash: toBN(hash).toString(),
signature: signature.map((x) => toBN(x).toString()),
Expand Down
3 changes: 2 additions & 1 deletion src/account/interface.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ProviderInterface } from '../provider';
import { BlockIdentifier } from '../provider/utils';
import { SignerInterface } from '../signer';
import {
Abi,
Expand Down Expand Up @@ -92,5 +93,5 @@ export abstract class AccountInterface extends ProviderInterface {
*/
public abstract verifyMessageHash(hash: BigNumberish, signature: Signature): Promise<boolean>;

public abstract getNonce(): Promise<string>;
public abstract getNonce(blockIdentifier?: BlockIdentifier): Promise<BigNumberish>;
}
22 changes: 15 additions & 7 deletions src/contract/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import {
AbiEntry,
Args,
AsyncContractFunction,
Call,
Calldata,
ContractFunction,
FunctionAbi,
Invocation,
InvokeFunctionResponse,
Overrides,
ParsedStruct,
Expand Down Expand Up @@ -578,13 +578,22 @@ export class Contract implements ContractInterface {
});
}

if (!options.nonce) {
throw new Error(`Nonce is required when invoking a function without an account`);
}

// eslint-disable-next-line no-console
console.warn(`Invoking ${method} without an account. This will not work on a public node.`);

return this.providerOrAccount.invokeFunction({
...invocation,
signature: options.signature || [],
});
return this.providerOrAccount.invokeFunction(
{
...invocation,
signature: options.signature || [],
},
{
nonce: options.nonce,
}
);
}

public async call(
Expand Down Expand Up @@ -630,13 +639,12 @@ export class Contract implements ContractInterface {
throw Error('Contract must be connected to the account contract to estimate');
}

public populate(method: string, args: Array<any> = []): Invocation {
public populate(method: string, args: Array<any> = []): Call {
const { inputs } = this.abi.find((abi) => abi.name === method) as FunctionAbi;
return {
contractAddress: this.address,
entrypoint: method,
calldata: this.compileCalldata(args, inputs),
signature: [],
};
}
}
17 changes: 12 additions & 5 deletions src/provider/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
GetTransactionReceiptResponse,
GetTransactionResponse,
Invocation,
InvocationsDetails,
InvocationsDetailsWithNonce,
InvokeFunctionResponse,
} from '../types';
import { BigNumberish } from '../utils/number';
Expand Down Expand Up @@ -63,10 +63,17 @@ export class Provider implements ProviderInterface {

public async getEstimateFee(
invocation: Invocation,
blockIdentifier: BlockIdentifier = 'pending',
invocationDetails: InvocationsDetails = {}
invocationDetails: InvocationsDetailsWithNonce,
blockIdentifier: BlockIdentifier = 'pending'
): Promise<EstimateFeeResponse> {
return this.provider.getEstimateFee(invocation, blockIdentifier, invocationDetails);
return this.provider.getEstimateFee(invocation, invocationDetails, blockIdentifier);
}

public async getNonce(
contractAddress: string,
blockIdentifier?: BlockIdentifier
): Promise<BigNumberish> {
return this.provider.getNonce(contractAddress, blockIdentifier);
}

public async getStorageAt(
Expand Down Expand Up @@ -94,7 +101,7 @@ export class Provider implements ProviderInterface {

public async invokeFunction(
functionInvocation: Invocation,
details: InvocationsDetails
details: InvocationsDetailsWithNonce
): Promise<InvokeFunctionResponse> {
return this.provider.invokeFunction(functionInvocation, details);
}
Expand Down
19 changes: 15 additions & 4 deletions src/provider/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import type {
GetTransactionReceiptResponse,
GetTransactionResponse,
Invocation,
InvocationsDetails,
InvocationsDetailsWithNonce,
InvokeFunctionResponse,
} from '../types';
import type { BigNumberish } from '../utils/number';
Expand Down Expand Up @@ -69,6 +69,17 @@ export abstract class ProviderInterface {
blockIdentifier?: BlockIdentifier
): Promise<ContractClass>;

/**
* Gets the nonce of a contract with respect to a specific block
*
* @param contractAddress - contract address
* @returns the hex nonce
*/
public abstract getNonce(
contractAddress: string,
blockIdentifier?: BlockIdentifier
): Promise<BigNumberish>;

/**
* Gets the contract's storage variable at a specific key.
*
Expand Down Expand Up @@ -141,7 +152,7 @@ export abstract class ProviderInterface {
*/
public abstract invokeFunction(
invocation: Invocation,
details?: InvocationsDetails
details: InvocationsDetailsWithNonce
): Promise<InvokeFunctionResponse>;

/**
Expand All @@ -160,8 +171,8 @@ export abstract class ProviderInterface {
*/
public abstract getEstimateFee(
invocation: Invocation,
blockIdentifier: BlockIdentifier,
details?: InvocationsDetails
details: InvocationsDetailsWithNonce,
blockIdentifier: BlockIdentifier
): Promise<EstimateFeeResponse>;

/**
Expand Down
Loading