diff --git a/.gitignore b/.gitignore index 632f2b292..f98fce63e 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ dist node_modules npm-debug.log package -yarn.lock \ No newline at end of file +yarn.lock +__tests__/scripts diff --git a/__tests__/accountInstance.test.ts b/__tests__/accountInstance.test.ts new file mode 100644 index 000000000..86d43ea9d --- /dev/null +++ b/__tests__/accountInstance.test.ts @@ -0,0 +1,25 @@ +import { Account, AccountInstance, LibraryError, RpcProvider } from '../src'; + +describe('Account instantiated from provider instance', () => { + const rpc = new RpcProvider(); + const getNonceMock = jest.fn().mockResolvedValue('mock'); + rpc.getNonceForAddress = getNonceMock; + + test('Account to throw on extended custom methods for using re-instantiation on provider', () => { + const acc = new Account(rpc, '0x0', '0x1'); + + return expect(acc.getNonceForAddress('0x0')).rejects.toThrow(LibraryError); + }); + + test('AccountInstance to pass on extending', async () => { + const acc = new AccountInstance(rpc, '0x0', '0x1'); + expect(await acc.getNonceForAddress('0x1')).toBe('mock'); + expect(getNonceMock).toHaveBeenLastCalledWith('0x1'); + }); + + test('Account to pass on extended custom methods for using instance on provider', async () => { + const acc = new Account(rpc, '0x0', '0x1', undefined, undefined, true); + expect(await acc.getNonceForAddress('0x2')).toBe('mock'); + expect(getNonceMock).toHaveBeenLastCalledWith('0x2'); + }); +}); diff --git a/__tests__/config/jestGlobalSetup.ts b/__tests__/config/jestGlobalSetup.ts index 41c6fdbb9..62093c55a 100644 --- a/__tests__/config/jestGlobalSetup.ts +++ b/__tests__/config/jestGlobalSetup.ts @@ -6,6 +6,7 @@ */ import { BaseUrl } from '../../src/constants'; +import { getDefaultNodeUrl } from '../../src/utils/provider'; import localDevnetDetector, { type DevnetStrategy } from './helpers/localDevnetDetector'; import { GS_DEFAULT_TEST_PROVIDER_URL } from './constants'; import { setIfNullish } from './helpers/env'; @@ -98,11 +99,10 @@ const verifySetup = (final?: boolean) => { if (final) throw new Error('TEST_ACCOUNT_PRIVATE_KEY env is not provided'); else warnings.push('TEST_ACCOUNT_PRIVATE_KEY env is not provided!'); } - // TODO: revise after Sequencer removal - // if (!process.env.TEST_RPC_URL) { - // process.env.TEST_RPC_URL = getDefaultNodeUrl(); - // console.warn('TEST_RPC_URL env is not provided'); - // } + + if (!process.env.TEST_RPC_URL && final) { + process.env.TEST_RPC_URL = getDefaultNodeUrl(); + } if (warnings.length > 0) { console.log('\x1b[33m', warnings.join('\n'), '\x1b[0m'); diff --git a/__tests__/rpcProvider.test.ts b/__tests__/rpcProvider.test.ts index cb8024c51..4c1899d03 100644 --- a/__tests__/rpcProvider.test.ts +++ b/__tests__/rpcProvider.test.ts @@ -5,11 +5,12 @@ import { Block, CallData, Contract, + ETransactionExecutionStatus, + ETransactionStatus, FeeEstimate, RPC, RPC06, RpcProvider, - TransactionExecutionStatus, stark, waitForTransactionOptions, } from '../src'; @@ -142,8 +143,8 @@ describeIfRpc('RPCProvider', () => { const generateOptions = (o: waitForTransactionOptions) => ({ retryInterval: 10, ...o }); const generateTransactionStatus = ( - finality_status: RPC.SPEC.TXN_STATUS, - execution_status?: RPC.SPEC.TXN_EXECUTION_STATUS + finality_status: `${ETransactionStatus}`, + execution_status?: `${ETransactionExecutionStatus}` ): RPC.TransactionStatus => ({ finality_status, execution_status, @@ -183,7 +184,7 @@ describeIfRpc('RPCProvider', () => { test('reverted - as error state', async () => { transactionStatusSpy.mockResolvedValueOnce(response.reverted); - const options = generateOptions({ errorStates: [TransactionExecutionStatus.REVERTED] }); + const options = generateOptions({ errorStates: [ETransactionExecutionStatus.REVERTED] }); await expect(rpcProvider.waitForTransaction(0, options)).rejects.toThrow( `${RPC.ETransactionExecutionStatus.REVERTED}: ${RPC.ETransactionStatus.ACCEPTED_ON_L2}` ); diff --git a/src/account/default.ts b/src/account/default.ts index 4101c1a10..5c0668fae 100644 --- a/src/account/default.ts +++ b/src/account/default.ts @@ -1,4 +1,4 @@ -import { UDC, ZERO } from '../constants'; +import { DEFAULT_TRANSACTION_VERSION, UDC, ZERO } from '../constants'; import { Provider, ProviderInterface } from '../provider'; import { Signer, SignerInterface } from '../signer'; import { @@ -72,9 +72,12 @@ export class Account extends Provider implements AccountInterface { address: string, pkOrSigner: Uint8Array | string | SignerInterface, cairoVersion?: CairoVersion, - transactionVersion: ETransactionVersion.V2 | ETransactionVersion.V3 = ETransactionVersion.V2 // TODO: Discuss this, set to v2 for backward compatibility + transactionVersion: + | ETransactionVersion.V2 + | ETransactionVersion.V3 = DEFAULT_TRANSACTION_VERSION, + instance?: boolean ) { - super(providerOrOptions); + super(providerOrOptions, instance); this.address = address.toLowerCase(); this.signer = isString(pkOrSigner) || pkOrSigner instanceof Uint8Array diff --git a/src/account/instance.ts b/src/account/instance.ts new file mode 100644 index 000000000..6dd1ebe67 --- /dev/null +++ b/src/account/instance.ts @@ -0,0 +1,19 @@ +import { DEFAULT_TRANSACTION_VERSION } from '../constants'; +import { ProviderInterface } from '../provider/interface'; +import { SignerInterface } from '../signer/interface'; +import { CairoVersion, ETransactionVersion, ProviderOptions } from '../types'; +import { Account } from '.'; + +export class AccountInstance extends Account { + constructor( + providerOrOptions: ProviderOptions | ProviderInterface, + address: string, + pkOrSigner: Uint8Array | string | SignerInterface, + cairoVersion?: CairoVersion, + transactionVersion: + | ETransactionVersion.V2 + | ETransactionVersion.V3 = DEFAULT_TRANSACTION_VERSION + ) { + super(providerOrOptions, address, pkOrSigner, cairoVersion, transactionVersion, true); + } +} diff --git a/src/channel/rpc_0_6.ts b/src/channel/rpc_0_6.ts index c7754fbb2..df25c819a 100644 --- a/src/channel/rpc_0_6.ts +++ b/src/channel/rpc_0_6.ts @@ -5,19 +5,19 @@ import { AccountInvocations, BigNumberish, BlockIdentifier, - BlockTag, Call, DeclareContractTransaction, DeployAccountContractTransaction, + EBlockTag, Invocation, InvocationsDetailsWithNonce, - RpcProviderOptions, + RpcChannelOptions, TransactionType, getEstimateFeeBulkOptions, getSimulateTransactionOptions, waitForTransactionOptions, } from '../types'; -import { JRPC, RPCSPEC06 as RPC } from '../types/api'; +import { ERPCVersion, JRPC, RPCSPEC06 as RPC } from '../types/api'; import { CallData } from '../utils/calldata'; import { isSierra } from '../utils/contract'; import fetch from '../utils/fetchPonyfill'; @@ -30,7 +30,7 @@ import { getVersionsByType } from '../utils/transaction'; const defaultOptions = { headers: { 'Content-Type': 'application/json' }, - blockIdentifier: BlockTag.pending, + blockIdentifier: EBlockTag.PENDING, retries: 200, }; @@ -51,23 +51,31 @@ export class RpcChannel { readonly waitMode: Boolean; // behave like web2 rpc and return when tx is processed - constructor(optionsOrProvider?: RpcProviderOptions) { + constructor(optionsOrProvider?: RpcChannelOptions) { const { nodeUrl, retries, headers, blockIdentifier, chainId, specVersion, waitMode } = optionsOrProvider || {}; if (Object.values(NetworkName).includes(nodeUrl as NetworkName)) { - this.nodeUrl = getDefaultNodeUrl(nodeUrl as NetworkName, optionsOrProvider?.default); + this.nodeUrl = getDefaultNodeUrl( + nodeUrl as NetworkName, + optionsOrProvider?.rpcVersion, + optionsOrProvider?.default + ); } else if (nodeUrl) { this.nodeUrl = nodeUrl; } else { - this.nodeUrl = getDefaultNodeUrl(undefined, optionsOrProvider?.default); + this.nodeUrl = getDefaultNodeUrl( + undefined, + optionsOrProvider?.rpcVersion, + optionsOrProvider?.default + ); } this.retries = retries || defaultOptions.retries; this.headers = { ...defaultOptions.headers, ...headers }; this.blockIdentifier = blockIdentifier || defaultOptions.blockIdentifier; this.chainId = chainId; - this.specVersion = specVersion; this.waitMode = waitMode || false; this.requestId = 0; + this.specVersion = specVersion; } public setChainId(chainId: StarknetChainId) { @@ -378,7 +386,7 @@ export class RpcChannel { ) { const block_id = new Block(blockIdentifier).identifier; let flags = {}; - if (!isVersion('0.5', await this.getSpecVersion())) { + if (!isVersion(ERPCVersion.V0_5, await this.getSpecVersion())) { flags = { simulation_flags: skipValidate ? [RPC.ESimulationFlag.SKIP_VALIDATE] : [], }; diff --git a/src/channel/rpc_0_7.ts b/src/channel/rpc_0_7.ts index 7e74e0fa5..b9712a819 100644 --- a/src/channel/rpc_0_7.ts +++ b/src/channel/rpc_0_7.ts @@ -5,13 +5,13 @@ import { AccountInvocations, BigNumberish, BlockIdentifier, - BlockTag, Call, DeclareContractTransaction, DeployAccountContractTransaction, + EBlockTag, Invocation, InvocationsDetailsWithNonce, - RpcProviderOptions, + RpcChannelOptions, TransactionType, getEstimateFeeBulkOptions, getSimulateTransactionOptions, @@ -30,7 +30,7 @@ import { getVersionsByType } from '../utils/transaction'; const defaultOptions = { headers: { 'Content-Type': 'application/json' }, - blockIdentifier: BlockTag.pending, + blockIdentifier: EBlockTag.PENDING, retries: 200, }; @@ -47,19 +47,27 @@ export class RpcChannel { private chainId?: StarknetChainId; - private speckVersion?: string; + private specVersion?: string; readonly waitMode: Boolean; // behave like web2 rpc and return when tx is processed - constructor(optionsOrProvider?: RpcProviderOptions) { - const { nodeUrl, retries, headers, blockIdentifier, chainId, waitMode } = + constructor(optionsOrProvider?: RpcChannelOptions) { + const { nodeUrl, retries, headers, blockIdentifier, chainId, specVersion, waitMode } = optionsOrProvider || {}; if (Object.values(NetworkName).includes(nodeUrl as NetworkName)) { - this.nodeUrl = getDefaultNodeUrl(nodeUrl as NetworkName, optionsOrProvider?.default); + this.nodeUrl = getDefaultNodeUrl( + nodeUrl as NetworkName, + optionsOrProvider?.rpcVersion, + optionsOrProvider?.default + ); } else if (nodeUrl) { this.nodeUrl = nodeUrl; } else { - this.nodeUrl = getDefaultNodeUrl(undefined, optionsOrProvider?.default); + this.nodeUrl = getDefaultNodeUrl( + undefined, + optionsOrProvider?.rpcVersion, + optionsOrProvider?.default + ); } this.retries = retries || defaultOptions.retries; this.headers = { ...defaultOptions.headers, ...headers }; @@ -67,6 +75,7 @@ export class RpcChannel { this.chainId = chainId; this.waitMode = waitMode || false; this.requestId = 0; + this.specVersion = specVersion; } public setChainId(chainId: StarknetChainId) { @@ -124,8 +133,8 @@ export class RpcChannel { } public async getSpecVersion() { - this.speckVersion ??= (await this.fetchEndpoint('starknet_specVersion')) as StarknetChainId; - return this.speckVersion; + this.specVersion ??= (await this.fetchEndpoint('starknet_specVersion')) as StarknetChainId; + return this.specVersion; } public getNonceForAddress( diff --git a/src/constants.ts b/src/constants.ts index fc4c3bfdd..fcf30894f 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -59,19 +59,28 @@ export const UDC = { ENTRYPOINT: 'deployContract', }; +export const DEFAULT_TRANSACTION_VERSION = ETransactionVersion.V2; // TODO: Discuss this, set to v2 for backward compatibility export const RPC_DEFAULT_VERSION = 'v0_7'; +export const DEFAULT_NETWORK_NAME = NetworkName.SN_GOERLI; // TODO: when goerli deprecated switch default to sepolia +export const RPC_DEFAULT_VERSION_TAG = 'v0_6'; export const RPC_NODES = { SN_GOERLI: [ - `https://starknet-testnet.public.blastapi.io/rpc/${RPC_DEFAULT_VERSION}`, - `https://free-rpc.nethermind.io/goerli-juno/${RPC_DEFAULT_VERSION}`, + `https://starknet-testnet.public.blastapi.io/rpc/${RPC_DEFAULT_VERSION_TAG}`, + `https://free-rpc.nethermind.io/goerli-juno/${RPC_DEFAULT_VERSION_TAG}`, ], SN_MAIN: [ - `https://starknet-mainnet.public.blastapi.io/rpc/${RPC_DEFAULT_VERSION}`, - `https://free-rpc.nethermind.io/mainnet-juno/${RPC_DEFAULT_VERSION}`, + `https://starknet-mainnet.public.blastapi.io/rpc/${RPC_DEFAULT_VERSION_TAG}`, + `https://free-rpc.nethermind.io/mainnet-juno/${RPC_DEFAULT_VERSION_TAG}`, ], SN_SEPOLIA: [ - `https://starknet-sepolia.public.blastapi.io/rpc/${RPC_DEFAULT_VERSION}`, - `https://free-rpc.nethermind.io/sepolia-juno/${RPC_DEFAULT_VERSION}`, + `https://starknet-sepolia.public.blastapi.io/rpc/${RPC_DEFAULT_VERSION_TAG}`, + `https://free-rpc.nethermind.io/sepolia-juno/${RPC_DEFAULT_VERSION_TAG}`, ], + getNode(network: NetworkName, index: number, rpcVersion?: string) { + const defRpcUrl = this[network][index]; + return rpcVersion + ? `${defRpcUrl.substring(0, defRpcUrl.lastIndexOf('/') + 1)}v${rpcVersion.replace('.', '_')}` + : defRpcUrl; + }, }; diff --git a/src/index.ts b/src/index.ts index 3a2c4576e..375e98177 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,6 +7,7 @@ export * from './contract'; export * from './provider'; export * from './signer'; export * from './channel'; +export * from './account/instance'; // TODO: decide on final export style export * from './types'; diff --git a/src/provider/rpc.ts b/src/provider/rpc.ts index 0e3fa6970..e294d7792 100644 --- a/src/provider/rpc.ts +++ b/src/provider/rpc.ts @@ -1,16 +1,15 @@ -import { ProviderInterface } from './interface'; -import { LibraryError } from './errors'; -import { RpcChannel, RPC06, RPC07 } from '../channel'; +import { RPC06, RPC07, RpcChannel } from '../channel'; import { AccountInvocations, BigNumberish, Block, + BlockHashAndNumber, BlockIdentifier, - BlockTag, Call, ContractVersion, DeclareContractTransaction, DeployAccountContractTransaction, + EBlockTag, GetBlockResponse, Invocation, InvocationsDetailsWithNonce, @@ -29,19 +28,35 @@ import { import { getAbiContractVersion } from '../utils/calldata/cairo'; import { isSierra } from '../utils/contract'; import { RPCResponseParser } from '../utils/responseParser/rpc'; +import { LibraryError } from './errors'; +import { ProviderInterface } from './interface'; export class RpcProvider implements ProviderInterface { - private responseParser: RPCResponseParser; + private responseParser = new RPCResponseParser(); public channel: RPC07.RpcChannel | RPC06.RpcChannel; - constructor(optionsOrProvider?: RpcProviderOptions | ProviderInterface | RpcProvider) { - if (optionsOrProvider && 'channel' in optionsOrProvider) { + constructor( + optionsOrProvider?: RpcProviderOptions | ProviderInterface | RpcProvider, + instance?: boolean + ) { + if (optionsOrProvider && instance) { + // stop ts complains + this.channel = null as any; + // use provided instance + Object.setPrototypeOf(this, Object.getPrototypeOf(optionsOrProvider)); + Object.assign(this, optionsOrProvider); + } else if ( + optionsOrProvider && + 'channel' in optionsOrProvider && + optionsOrProvider.channel !== undefined + ) { this.channel = optionsOrProvider.channel; this.responseParser = (optionsOrProvider as any).responseParser; } else { this.channel = new RpcChannel({ ...optionsOrProvider, waitMode: false }); - this.responseParser = new RPCResponseParser(optionsOrProvider?.feeMarginPercentage); + // TODO: check this hotfix (optionsOrProvider as any)? why required ? + this.responseParser = new RPCResponseParser((optionsOrProvider as any)?.feeMarginPercentage); } } @@ -77,7 +92,7 @@ export class RpcProvider implements ProviderInterface { /** * Get the most recent accepted block hash and number */ - public async getBlockLatestAccepted() { + public async getBlockLatestAccepted(): Promise { return this.channel.getBlockLatestAccepted(); } @@ -129,7 +144,7 @@ export class RpcProvider implements ProviderInterface { * Utility method, same result can be achieved using getBlockWithTxHashes(BlockTag.pending); */ public async getPendingTransactions() { - const { transactions } = await this.getBlockWithTxHashes(BlockTag.pending).then( + const { transactions } = await this.getBlockWithTxHashes(EBlockTag.PENDING).then( this.responseParser.parseGetBlockResponse ); return Promise.all(transactions.map((it: any) => this.getTransactionByHash(it))); diff --git a/src/types/account.ts b/src/types/account.ts index 9572cfeb8..4da859a92 100644 --- a/src/types/account.ts +++ b/src/types/account.ts @@ -76,11 +76,6 @@ export type SimulateTransactionDetails = { skipExecute?: boolean; } & Partial; -export enum SIMULATION_FLAG { - SKIP_VALIDATE = 'SKIP_VALIDATE', - SKIP_EXECUTE = 'SKIP_EXECUTE', -} - export type EstimateFeeAction = | { type: TransactionType.INVOKE; diff --git a/src/types/api/forward.ts b/src/types/api/forward.ts new file mode 100644 index 000000000..ea8eed4f1 --- /dev/null +++ b/src/types/api/forward.ts @@ -0,0 +1,30 @@ +/* eslint-disable prefer-destructuring */ +/* + * Forward important RPC types into the top namespace + */ + +import { + EBlockTag as _EBlockTag, + EDAMode as _EDAMode, + EDataAvailabilityMode as _EDataAvailabilityMode, + ESimulationFlag as _ESimulationFlag, + ETransactionExecutionStatus as _ETransactionExecutionStatus, + ETransactionFinalityStatus as _ETransactionFinalityStatus, + ETransactionStatus as _ETransactionStatus, + ETransactionType as _ETransactionType, + ETransactionVersion as _ETransactionVersion, + ETransactionVersion2 as _ETransactionVersion2, + ETransactionVersion3 as _ETransactionVersion3, +} from '.'; + +export { _ETransactionStatus as ETransactionStatus }; +export { _ESimulationFlag as ESimulationFlag }; +export { _ETransactionType as ETransactionType }; +export { _ETransactionFinalityStatus as ETransactionFinalityStatus }; +export { _ETransactionExecutionStatus as ETransactionExecutionStatus }; +export { _EBlockTag as EBlockTag }; +export { _EDataAvailabilityMode as EDataAvailabilityMode }; +export { _EDAMode as EDAMode }; +export { _ETransactionVersion as ETransactionVersion }; +export { _ETransactionVersion2 as ETransactionVersion2 }; +export { _ETransactionVersion3 as ETransactionVersion3 }; diff --git a/src/types/api/index.ts b/src/types/api/index.ts index 56fb63876..93669ac95 100644 --- a/src/types/api/index.ts +++ b/src/types/api/index.ts @@ -3,3 +3,9 @@ export * as JRPC from './jsonrpc'; export * as RPCSPEC06 from './rpcspec_0_6'; export * as RPCSPEC07 from './rpcspec_0_7'; export * from './rpcspec_0_7'; + +export enum ERPCVersion { + V0_5 = '0.5', + V0_6 = '0.6', + V0_7 = '0.7', +} diff --git a/src/types/api/rpcspec_0_6/nonspec.ts b/src/types/api/rpcspec_0_6/nonspec.ts index f3453b43b..ec25ba158 100644 --- a/src/types/api/rpcspec_0_6/nonspec.ts +++ b/src/types/api/rpcspec_0_6/nonspec.ts @@ -120,6 +120,7 @@ export enum ESimulationFlag { SKIP_FEE_CHARGE = 'SKIP_FEE_CHARGE', } +// TXN_STATUS export enum ETransactionStatus { RECEIVED = 'RECEIVED', REJECTED = 'REJECTED', @@ -131,6 +132,8 @@ export enum ETransactionFinalityStatus { ACCEPTED_ON_L2 = 'ACCEPTED_ON_L2', ACCEPTED_ON_L1 = 'ACCEPTED_ON_L1', } + +// TXN_EXECUTION_STATUS export enum ETransactionExecutionStatus { SUCCEEDED = 'SUCCEEDED', REVERTED = 'REVERTED', diff --git a/src/types/channel/index.ts b/src/types/channel/index.ts new file mode 100644 index 000000000..07b2b3a25 --- /dev/null +++ b/src/types/channel/index.ts @@ -0,0 +1,15 @@ +import { NetworkName, StarknetChainId } from '../../constants'; +import { ERPCVersion } from '../api'; +import { BlockIdentifier } from '../lib'; + +export type RpcChannelOptions = { + nodeUrl?: string | NetworkName; + retries?: number; + headers?: object; + blockIdentifier?: BlockIdentifier; + chainId?: StarknetChainId; + default?: boolean; + waitMode?: boolean; + rpcVersion?: ERPCVersion; + specVersion?: string; +}; diff --git a/src/types/index.ts b/src/types/index.ts index a096ffccc..50e9d0fda 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -3,8 +3,10 @@ export * from './calldata'; export * from './contract'; export * from './lib'; export * from './provider'; +export * from './channel'; export * from './signer'; export * from './typedData'; export * from './cairoEnum'; +export * from './api/forward'; export * as RPC from './api'; diff --git a/src/types/lib/index.ts b/src/types/lib/index.ts index db718e7ad..eb6a45931 100644 --- a/src/types/lib/index.ts +++ b/src/types/lib/index.ts @@ -1,6 +1,12 @@ import { StarknetChainId } from '../../constants'; import { weierstrass } from '../../utils/ec'; -import { EDataAvailabilityMode, ResourceBounds } from '../api'; +import { + EBlockTag, + EDataAvailabilityMode, + ETransactionExecutionStatus, + ETransactionFinalityStatus, + ResourceBounds, +} from '../api'; import { CairoEnum } from '../cairoEnum'; import { CompiledContract, CompiledSierraCasm, ContractClass } from './contract'; @@ -155,52 +161,14 @@ export enum TransactionType { INVOKE = 'INVOKE_FUNCTION', } -/** - * new statuses are defined by props: finality_status and execution_status - * to be #deprecated - */ -export enum TransactionStatus { - NOT_RECEIVED = 'NOT_RECEIVED', - RECEIVED = 'RECEIVED', - ACCEPTED_ON_L2 = 'ACCEPTED_ON_L2', - ACCEPTED_ON_L1 = 'ACCEPTED_ON_L1', - REJECTED = 'REJECTED', - REVERTED = 'REVERTED', -} - -export enum TransactionFinalityStatus { - NOT_RECEIVED = 'NOT_RECEIVED', - RECEIVED = 'RECEIVED', - ACCEPTED_ON_L2 = 'ACCEPTED_ON_L2', - ACCEPTED_ON_L1 = 'ACCEPTED_ON_L1', -} - -export enum TransactionExecutionStatus { - REJECTED = 'REJECTED', - REVERTED = 'REVERTED', - SUCCEEDED = 'SUCCEEDED', -} - -export enum BlockStatus { - PENDING = 'PENDING', - ACCEPTED_ON_L1 = 'ACCEPTED_ON_L1', - ACCEPTED_ON_L2 = 'ACCEPTED_ON_L2', - REJECTED = 'REJECTED', -} - -export enum BlockTag { - pending = 'pending', - latest = 'latest', -} - -export type BlockNumber = BlockTag | null | number; +export type BlockNumber = number; /** * hex string and BN are detected as block hashes * decimal string and number are detected as block numbers * null appends nothing to the request url */ -export type BlockIdentifier = BlockNumber | BigNumberish; +export type BlockIdentifier = BlockNumber | BigNumberish | EBlockTag | null; /** * items used by AccountInvocations @@ -240,8 +208,8 @@ export type ParsedStruct = { export type waitForTransactionOptions = { retryInterval?: number; - successStates?: Array; - errorStates?: Array; + successStates?: Array; + errorStates?: Array; }; export type getSimulateTransactionOptions = { diff --git a/src/types/provider/configuration.ts b/src/types/provider/configuration.ts index 71eaf534f..b6eeb29a6 100644 --- a/src/types/provider/configuration.ts +++ b/src/types/provider/configuration.ts @@ -1,4 +1,5 @@ import { NetworkName, StarknetChainId } from '../../constants'; +import { ERPCVersion } from '../api'; import { BlockIdentifier } from '../lib'; export interface ProviderOptions extends RpcProviderOptions {} @@ -9,9 +10,10 @@ export type RpcProviderOptions = { headers?: object; blockIdentifier?: BlockIdentifier; chainId?: StarknetChainId; - specVersion?: string; default?: boolean; waitMode?: boolean; + rpcVersion?: ERPCVersion; + specVersion?: string; feeMarginPercentage?: { l1BoundMaxAmount: number; l1BoundMaxPricePerUnit: number; diff --git a/src/types/provider/response.ts b/src/types/provider/response.ts index 6be225614..33f70117c 100644 --- a/src/types/provider/response.ts +++ b/src/types/provider/response.ts @@ -1,6 +1,7 @@ /** - * Common interface response - * Intersection (sequencer response ∩ (∪ rpc responses)) + * Provider interface responses + * DEV NOTE: For stability ensure this types do not change with minor versions + * aka. when rpc spec change update parser so this types can stay the same */ import { CompiledSierra, LegacyContractClass } from '../lib'; @@ -9,8 +10,10 @@ import { BLOCK_NUMBER, DECLARE_TXN_RECEIPT, DEPLOY_ACCOUNT_TXN_RECEIPT, + DeclaredTransaction, FELT, INVOKE_TXN_RECEIPT, + InvokedTransaction, L1_HANDLER_TXN_RECEIPT, PENDING_DECLARE_TXN_RECEIPT, PENDING_DEPLOY_ACCOUNT_TXN_RECEIPT, @@ -18,16 +21,14 @@ import { PENDING_L1_HANDLER_TXN_RECEIPT, PENDING_STATE_UPDATE, PRICE_UNIT, - RESOURCE_PRICE, - SIMULATION_FLAG, - STATE_UPDATE, - TXN_HASH, - DeclaredTransaction, - InvokedTransaction, PendingReceipt, + RESOURCE_PRICE, Receipt, ResourceBounds, + SIMULATION_FLAG, + STATE_UPDATE, SimulateTransaction, + TXN_HASH, TransactionWithHash, } from './spec'; @@ -115,3 +116,10 @@ export type PendingStateUpdate = PENDING_STATE_UPDATE; export type ContractClassResponse = | LegacyContractClass | Omit; + +/* RE-Export from RPC */ + +export type BlockHashAndNumber = { + block_hash: string; + block_number: number; +}; diff --git a/src/utils/provider.ts b/src/utils/provider.ts index 59983e23d..96a07c9a4 100644 --- a/src/utils/provider.ts +++ b/src/utils/provider.ts @@ -1,11 +1,12 @@ -import { NetworkName, RPC_NODES } from '../constants'; +/* eslint-disable no-console */ +import { DEFAULT_NETWORK_NAME, NetworkName, RPC_NODES } from '../constants'; import { BigNumberish, BlockIdentifier, - BlockTag, CompiledContract, CompiledSierra, ContractClass, + EBlockTag, GetBlockResponse, GetTransactionReceiptResponse, InvocationsDetailsWithNonce, @@ -17,13 +18,13 @@ import { StateUpdateResponse, V3TransactionDetails, } from '../types'; -import { ETransactionVersion } from '../types/api'; +import { ERPCVersion, ETransactionVersion } from '../types/api'; import { isSierra } from './contract'; import { formatSpaces } from './hash'; import { parse, stringify } from './json'; import { isBigInt, isHex, isNumber, toHex } from './num'; -import { compressProgram } from './stark'; import { isString } from './shortString'; +import { compressProgram } from './stark'; /** * Helper - Async Sleep for 'delay' time @@ -72,13 +73,16 @@ export function parseContract(contract: CompiledContract | string): ContractClas * @param mute mute public node warning * @returns default node url */ -export const getDefaultNodeUrl = (networkName?: NetworkName, mute: boolean = false): string => { +export const getDefaultNodeUrl = ( + networkName?: NetworkName, + rpcVersion?: ERPCVersion, + mute: boolean = false +): string => { if (!mute) - // eslint-disable-next-line no-console console.warn('Using default public node url, please provide nodeUrl in provider options!'); - const nodes = RPC_NODES[networkName ?? NetworkName.SN_GOERLI]; // TODO: when goerli deprecated switch default to sepolia - const randIdx = Math.floor(Math.random() * nodes.length); - return nodes[randIdx]; + const network = networkName ?? DEFAULT_NETWORK_NAME; + const randIdx = Math.floor(Math.random() * RPC_NODES[network].length); + return RPC_NODES.getNode(network, randIdx, rpcVersion); }; /** @@ -101,7 +105,7 @@ export function txIdentifier(txHash?: BigNumberish, txId?: BigNumberish): string return `transactionHash=${hashString}`; } -export const validBlockTags = Object.values(BlockTag); +export const validBlockTags = Object.values(EBlockTag); export class Block { hash: BlockIdentifier = null; @@ -114,7 +118,7 @@ export class Block { if (isString(__identifier)) { if (isHex(__identifier)) { this.hash = __identifier; - } else if (validBlockTags.includes(__identifier as BlockTag)) { + } else if (validBlockTags.includes(__identifier as EBlockTag)) { this.tag = __identifier; } } else if (isBigInt(__identifier)) { @@ -122,7 +126,7 @@ export class Block { } else if (isNumber(__identifier)) { this.number = __identifier; } else { - this.tag = BlockTag.pending; + this.tag = EBlockTag.PENDING; } } @@ -176,7 +180,7 @@ export function isV3Tx(details: InvocationsDetailsWithNonce): details is V3Trans return version === ETransactionVersion.V3 || version === ETransactionVersion.F3; } -export function isVersion(version: '0.5' | '0.6' | '0.7', response: string) { +export function isVersion(version: string, response: string) { const [majorS, minorS] = version.split('.'); const [majorR, minorR] = response.split('.'); diff --git a/src/utils/responseParser/rpc.ts b/src/utils/responseParser/rpc.ts index 9ae3ce45d..c50830156 100644 --- a/src/utils/responseParser/rpc.ts +++ b/src/utils/responseParser/rpc.ts @@ -6,20 +6,20 @@ import { BlockWithTxHashes, ContractClassPayload, ContractClassResponse, - TransactionReceipt, EstimateFeeResponse, EstimateFeeResponseBulk, + FeeEstimate, GetBlockResponse, GetTransactionReceiptResponse, - FeeEstimate, + RpcProviderOptions, SimulateTransactionResponse, SimulatedTransaction, - RpcProviderOptions, + TransactionReceipt, } from '../../types/provider'; import { toBigInt } from '../num'; +import { isString } from '../shortString'; import { estimateFeeToBounds, estimatedFeeToMaxFee } from '../stark'; import { ResponseParser } from '.'; -import { isString } from '../shortString'; export class RPCResponseParser implements