Skip to content

simplify extends for the account class #1021

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
# [6.5.0](https://github.com/starknet-io/starknet.js/compare/v6.4.2...v6.5.0) (2024-03-14)

### Bug Fixes

- adjust max amount bound calculation for RPC v0.7.0 ([dd34cdb](https://github.com/starknet-io/starknet.js/commit/dd34cdb8b9817a55a16a97d960b1544d75c0059a))

### Features

- make fee margins configurable ([cedd984](https://github.com/starknet-io/starknet.js/commit/cedd984e1106db5b73d17630e282eb956d344a97))

## [6.4.2](https://github.com/starknet-io/starknet.js/compare/v6.4.1...v6.4.2) (2024-03-14)

### Bug Fixes
Expand Down
13 changes: 12 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ Or run tests in watch mode:
npm test --watch
```

By default the tests are executed in your local Devnet. If you want to use a specific rpc node, you have to set some global variables before executing the tests :
By default the tests are executed in your local Devnet. If you want to use a specific
RPC node, you have to set some global variables before executing the tests:

```bash
export TEST_RPC_URL=http://192.168.1.44:9545/rpc/v0.5 # example of a Pathfinder node located in your local network
Expand All @@ -43,6 +44,16 @@ export TEST_ACCOUNT_ADDRESS=0x065A822f0000000000000000000000000c26641
export TEST_ACCOUNT_PRIVATE_KEY=0x02a80000000000000000000000001754438a
```

The global variables above will only be valid for some of the tests.
The recommended and more straightforward approach is to go with the docker.
You just need to do the following steps:

- Install [Docker](https://docs.docker.com/engine/install/) (it can also be installed via a package manager, e.g. `brew` for Mac)
- Run `Docker` on your machine (open the application).
- Go to the [starknet-devnet-rs](https://hub.docker.com/r/shardlabs/starknet-devnet-rs/tags) and copy the `docker pull` command from the latest tag
- Run `docker pull shardlabs/starknet-devnet-rs:latest` in your terminal
- Run tests locally with `npm run test`

**Don’t forget to add tests and [update documentation](./www/README.md) for your changes.**
Documentation can be archived by using JSDoc.

Expand Down
2 changes: 2 additions & 0 deletions __tests__/config/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/* Default test config based on run `starknet-devnet --seed 0` */
export const GS_DEFAULT_TEST_PROVIDER_URL = 'http://127.0.0.1:5050/';
4 changes: 4 additions & 0 deletions __tests__/config/helpers/env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const setIfNullish = (envName: string, value?: boolean) => {
const stringifiedBooleanValue = value ? 'true' : 'false';
process.env[envName] ??= stringifiedBooleanValue;
};
73 changes: 73 additions & 0 deletions __tests__/config/helpers/localDevnetDetector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/* eslint-disable no-console */
import { GS_DEFAULT_TEST_PROVIDER_URL } from '../constants';
import { setIfNullish } from './env';

export type DevnetStrategy = Record<'isDevnet' | 'isRS', boolean>;

const LOCAL_DEVNET_NOT_RUNNING_MESSAGE = `
Local devnet is not running. In order to properly run it you need to do the following: \n
- Go to the: https://hub.docker.com/r/shardlabs/starknet-devnet-rs/tags
- Find the latest tag and copy the "docker pull" command
- Run Docker on your machine
- Run the command: "docker pull shardlabs/starknet-devnet-rs:latest"
`;

class LocalDevnetDetector {
private strategy: DevnetStrategy = { isDevnet: false, isRS: false };

get isDevnet() {
return this.strategy.isDevnet;
}

get isRS() {
return this.strategy.isRS;
}

private setup() {
setIfNullish('IS_LOCALHOST_DEVNET', this.isDevnet);
setIfNullish('IS_RPC_DEVNET', this.isDevnet && (this.isRS || !!process.env.TEST_RPC_URL));
setIfNullish('IS_SEQUENCER_DEVNET', this.isDevnet && process.env.IS_RPC_DEVNET === 'false');
return this.strategy;
}

private async isLocalDevnet(): Promise<boolean> {
// if is_alive work it is local devnet
const devnetResult = await fetch(`${GS_DEFAULT_TEST_PROVIDER_URL}is_alive`)
.then((res) => res.text())
.catch(() => null);

return devnetResult === 'Alive!!!';
}

private async isRsDevnet(): Promise<boolean> {
const response = await fetch(GS_DEFAULT_TEST_PROVIDER_URL, {
method: 'POST',
headers: { Accept: 'application/json', 'Content-Type': 'application/json' },
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'starknet_syncing' }),
});
const { jsonrpc } = await response.json();
return jsonrpc === '2.0';
}

async execute() {
this.strategy.isDevnet = await this.isLocalDevnet();

if (!this.strategy.isDevnet) {
console.log('\x1b[36m%s\x1b[0m', LOCAL_DEVNET_NOT_RUNNING_MESSAGE);
this.setup();
throw new Error('Local devnet is not Running. Please follow the devnet setup instructions.');
}

// if on base url RPC endpoint work it is devnet-rs else it devnet-py
try {
this.strategy.isRS = await this.isRsDevnet();
if (this.isRS) console.log('Detected Devnet-RS');
} catch (error) {
return this.setup();
}

return this.setup();
}
}

export default new LocalDevnetDetector();
90 changes: 12 additions & 78 deletions __tests__/config/jestGlobalSetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,82 +6,25 @@
*/

import { BaseUrl } from '../../src/constants';
import localDevnetDetector, { type DevnetStrategy } from './helpers/localDevnetDetector';
import { GS_DEFAULT_TEST_PROVIDER_URL } from './constants';
import { setIfNullish } from './helpers/env';

type DevnetStrategy = {
isDevnet: boolean;
isRS: boolean;
};
type ProviderType = {
sequencer: boolean;
rpc: boolean;
};
type ProviderType = Record<'sequencer' | 'rpc', boolean>;

/**
* Global Setup Fixtures
*/

/* Default test config based on run `starknet-devnet --seed 0` */
const GS_DEFAULT_TEST_PROVIDER_URL = 'http://127.0.0.1:5050/';

const setIfNullish = (envName: string, setValue?: string | boolean) => {
process.env[envName] ??= setValue?.toString();
};

const localDevnetDetectionStrategy = async () => {
const setup = (strategy: DevnetStrategy) => {
setIfNullish('IS_LOCALHOST_DEVNET', strategy.isDevnet ? 'true' : 'false');
setIfNullish(
'IS_RPC_DEVNET',
strategy.isDevnet && (strategy.isRS || process.env.TEST_RPC_URL) ? 'true' : 'false'
);
setIfNullish(
'IS_SEQUENCER_DEVNET',
strategy.isDevnet && process.env.IS_RPC_DEVNET === 'false' ? 'true' : 'false'
);
return strategy;
};

const strategy: DevnetStrategy = {
isDevnet: false,
isRS: false,
};

// if is_alive work it is local devnet
const devnetResult = await fetch(`${GS_DEFAULT_TEST_PROVIDER_URL}is_alive`)
.then((res) => res.text())
.catch(() => '');
if (devnetResult !== 'Alive!!!') {
return setup(strategy);
}
strategy.isDevnet = true;

// if on base url RPC endpoint work it is devnet-rs else it devnet-py
try {
const response = await fetch(`${GS_DEFAULT_TEST_PROVIDER_URL}`, {
method: 'POST',
headers: { Accept: 'application/json', 'Content-Type': 'application/json' },
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'starknet_syncing' }),
});
const json = await response.json();
strategy.isRS = json.jsonrpc === '2.0';
} catch (error) {
return setup(strategy);
}

return setup(strategy);
};

const sequencerOrRpc = async (devnetStrategy?: DevnetStrategy) => {
const setup = (providerType: ProviderType) => {
setIfNullish('IS_SEQUENCER', providerType.sequencer ? 'true' : 'false');
setIfNullish('IS_RPC', providerType.rpc ? 'true' : 'false');
setIfNullish('IS_SEQUENCER', providerType.sequencer);
setIfNullish('IS_RPC', providerType.rpc);
setIfNullish(
'IS_SEQUENCER_GOERLI',
(process.env.TEST_PROVIDER_BASE_URL || process.env.TEST_RPC_URL || '').includes(
BaseUrl.SN_GOERLI
)
? 'true'
: 'false'
);
return providerType;
};
Expand Down Expand Up @@ -169,18 +112,16 @@ const verifySetup = (final?: boolean) => {
}

if (!final) {
setIfNullish('IS_LOCALHOST_DEVNET', 'false');
setIfNullish('IS_RPC_DEVNET', 'false');
setIfNullish('IS_SEQUENCER_DEVNET', 'false');
setIfNullish('IS_RPC', process.env.TEST_RPC_URL ? 'true' : 'false');
setIfNullish('IS_SEQUENCER', process.env.TEST_PROVIDER_BASE_URL ? 'true' : 'false');
setIfNullish('IS_LOCALHOST_DEVNET', false);
setIfNullish('IS_RPC_DEVNET', false);
setIfNullish('IS_SEQUENCER_DEVNET', false);
setIfNullish('IS_RPC', !!process.env.TEST_RPC_URL);
setIfNullish('IS_SEQUENCER', !!process.env.TEST_PROVIDER_BASE_URL);
setIfNullish(
'IS_SEQUENCER_GOERLI',
(process.env.TEST_PROVIDER_BASE_URL || process.env.TEST_RPC_URL || '').includes(
BaseUrl.SN_GOERLI
)
? 'true'
: 'false'
);
}

Expand Down Expand Up @@ -216,14 +157,7 @@ const executeStrategy = async () => {

// 2. Try to detect devnet setup
console.log('Basic test parameters are missing, Auto Setup Started');
const devnetStrategy = await localDevnetDetectionStrategy();
if (devnetStrategy.isDevnet) {
if (devnetStrategy.isRS) {
console.log('Detected Devnet-RS');
} else {
console.log('Detected Devnet-PY');
}
}
const devnetStrategy = await localDevnetDetector.execute();

const providerType = await sequencerOrRpc(devnetStrategy);
if (providerType.sequencer) {
Expand Down
28 changes: 28 additions & 0 deletions __tests__/rpcProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
Block,
CallData,
Contract,
FeeEstimate,
RPC,
RPC06,
RpcProvider,
Expand Down Expand Up @@ -79,6 +80,33 @@ describeIfRpc('RPCProvider', () => {
expect(typeof spec).toBe('string');
});

test('configurable margin', async () => {
const p = new RpcProvider({
nodeUrl: provider.channel.nodeUrl,
feeMarginPercentage: {
l1BoundMaxAmount: 0,
l1BoundMaxPricePerUnit: 0,
maxFee: 0,
},
});
const estimateSpy = jest.spyOn(p.channel as any, 'getEstimateFee');
const mockFeeEstimate: FeeEstimate = {
gas_consumed: '0x2',
gas_price: '0x1',
data_gas_consumed: '0x2',
data_gas_price: '0x1',
overall_fee: '0x4',
unit: 'WEI',
};
estimateSpy.mockResolvedValue([mockFeeEstimate]);
const result = (await p.getEstimateFeeBulk([{} as any], {}))[0];
expect(estimateSpy).toHaveBeenCalledTimes(1);
expect(result.suggestedMaxFee).toBe(4n);
expect(result.resourceBounds.l1_gas.max_amount).toBe('0x4');
expect(result.resourceBounds.l1_gas.max_price_per_unit).toBe('0x1');
estimateSpy.mockRestore();
});

describe('Test Estimate message fee', () => {
const L1_ADDRESS = '0x8359E4B0152ed5A731162D3c7B0D8D56edB165A0';
let l1l2ContractAddress: string;
Expand Down
12 changes: 11 additions & 1 deletion __tests__/utils/stark.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,19 @@ describe('stark', () => {
overall_fee: '1000',
unit: 'FRI',
};
const estimateFeeResponse07: FeeEstimate = {
...estimateFeeResponse,
data_gas_consumed: '100',
data_gas_price: '10',
overall_fee: '2000',
};
expect(stark.estimateFeeToBounds(estimateFeeResponse)).toStrictEqual({
l2_gas: { max_amount: '0x0', max_price_per_unit: '0x0' },
l1_gas: { max_amount: '0x6e', max_price_per_unit: '0xf' },
l1_gas: { max_amount: '0x96', max_price_per_unit: '0xf' },
});
expect(stark.estimateFeeToBounds(estimateFeeResponse07)).toStrictEqual({
l2_gas: { max_amount: '0x0', max_price_per_unit: '0x0' },
l1_gas: { max_amount: '0x12c', max_price_per_unit: '0xf' },
});
});

Expand Down
4 changes: 2 additions & 2 deletions __tests__/utils/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,11 @@ describe('computeHashOnElements()', () => {
});
describe('estimatedFeeToMaxFee()', () => {
test('should return maxFee for 0', () => {
const res = stark.estimatedFeeToMaxFee(0, 0.15);
const res = stark.estimatedFeeToMaxFee(0, 15);
expect(res).toBe(0n);
});
test('should return maxFee for 10_000', () => {
const res = stark.estimatedFeeToMaxFee(10_000, 0.15);
const res = stark.estimatedFeeToMaxFee(10_000, 15);
expect(res).toBe(11500n);
});
});
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "starknet",
"version": "6.4.2",
"version": "6.5.0",
"description": "JavaScript library for Starknet",
"main": "dist/index.js",
"module": "dist/index.mjs",
Expand Down
6 changes: 3 additions & 3 deletions src/account/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export class Account extends Provider implements AccountInterface {
}

// provided version or contract based preferred transactionVersion
private getPreferredVersion(type12: ETransactionVersion, type3: ETransactionVersion) {
protected getPreferredVersion(type12: ETransactionVersion, type3: ETransactionVersion) {
if (this.transactionVersion === ETransactionVersion.V3) return type3;
if (this.transactionVersion === ETransactionVersion.V2) return type12;

Expand All @@ -100,7 +100,7 @@ export class Account extends Provider implements AccountInterface {
return super.getNonceForAddress(this.address, blockIdentifier);
}

private async getNonceSafe(nonce?: BigNumberish) {
protected async getNonceSafe(nonce?: BigNumberish) {
// Patch DEPLOY_ACCOUNT: RPC getNonce for non-existing address will result in error, on Sequencer it is '0x0'
try {
return toBigInt(nonce ?? (await this.getNonce()));
Expand Down Expand Up @@ -600,7 +600,7 @@ export class Account extends Provider implements AccountInterface {
* Support methods
*/

private async getUniversalSuggestedFee(
protected async getUniversalSuggestedFee(
version: ETransactionVersion,
{ type, payload }: EstimateFeeAction,
details: UniversalDetails
Expand Down
8 changes: 7 additions & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,18 @@ export enum TransactionHashPrefix {
L1_HANDLER = '0x6c315f68616e646c6572', // encodeShortString('l1_handler'),
}

export const enum feeMarginPercentage {
L1_BOUND_MAX_AMOUNT = 50,
L1_BOUND_MAX_PRICE_PER_UNIT = 50,
MAX_FEE = 50,
}

export const UDC = {
ADDRESS: '0x041a78e741e5af2fec34b695679bc6891742439f7afb8484ecd7766661ad02bf',
ENTRYPOINT: 'deployContract',
};

export const RPC_DEFAULT_VERSION = 'v0_6';
export const RPC_DEFAULT_VERSION = 'v0_7';

export const RPC_NODES = {
SN_GOERLI: [
Expand Down
Loading