From ac8c76383bb3f566164bc4f38c477e3e3dc45f31 Mon Sep 17 00:00:00 2001 From: Toni Tabak Date: Fri, 24 Nov 2023 14:05:26 +0100 Subject: [PATCH] feat: dual specification with 0.6 implementation, not yet integrated --- src/channel/{rpc.ts => rpc_0_5.ts} | 2 + src/channel/rpc_0_6.ts | 561 +++++++++++++++ src/provider/interface.ts | 2 +- src/provider/rpc.ts | 2 +- src/types/api/rpc.ts | 2 +- .../{rpcspec => rpcspec_0_5}/components.ts | 0 .../api/{rpcspec => rpcspec_0_5}/contract.ts | 0 .../api/{rpcspec => rpcspec_0_5}/errors.ts | 0 .../api/{rpcspec => rpcspec_0_5}/index.ts | 0 .../api/{rpcspec => rpcspec_0_5}/methods.ts | 0 .../api/{rpcspec => rpcspec_0_5}/nonspec.ts | 0 src/types/api/rpcspec_0_6/components.ts | 669 ++++++++++++++++++ src/types/api/rpcspec_0_6/contract.ts | 101 +++ src/types/api/rpcspec_0_6/errors.ts | 151 ++++ src/types/api/rpcspec_0_6/index.ts | 9 + src/types/api/rpcspec_0_6/methods.ts | 329 +++++++++ src/types/api/rpcspec_0_6/nonspec.ts | 134 ++++ src/utils/responseParser/rpc.ts | 2 +- 18 files changed, 1960 insertions(+), 4 deletions(-) rename src/channel/{rpc.ts => rpc_0_5.ts} (99%) create mode 100644 src/channel/rpc_0_6.ts rename src/types/api/{rpcspec => rpcspec_0_5}/components.ts (100%) rename src/types/api/{rpcspec => rpcspec_0_5}/contract.ts (100%) rename src/types/api/{rpcspec => rpcspec_0_5}/errors.ts (100%) rename src/types/api/{rpcspec => rpcspec_0_5}/index.ts (100%) rename src/types/api/{rpcspec => rpcspec_0_5}/methods.ts (100%) rename src/types/api/{rpcspec => rpcspec_0_5}/nonspec.ts (100%) create mode 100644 src/types/api/rpcspec_0_6/components.ts create mode 100644 src/types/api/rpcspec_0_6/contract.ts create mode 100644 src/types/api/rpcspec_0_6/errors.ts create mode 100644 src/types/api/rpcspec_0_6/index.ts create mode 100644 src/types/api/rpcspec_0_6/methods.ts create mode 100644 src/types/api/rpcspec_0_6/nonspec.ts diff --git a/src/channel/rpc.ts b/src/channel/rpc_0_5.ts similarity index 99% rename from src/channel/rpc.ts rename to src/channel/rpc_0_5.ts index 768132953..a1c9ad427 100644 --- a/src/channel/rpc.ts +++ b/src/channel/rpc_0_5.ts @@ -51,6 +51,8 @@ export class RpcChannel { readonly waitMode: Boolean; // behave like web2 rpc and return when tx is processed + readonly rpcSpec = '0_5'; + constructor(optionsOrProvider?: RpcProviderOptions) { const { nodeUrl, retries, headers, blockIdentifier, chainId, waitMode } = optionsOrProvider || {}; diff --git a/src/channel/rpc_0_6.ts b/src/channel/rpc_0_6.ts new file mode 100644 index 000000000..6163f65b4 --- /dev/null +++ b/src/channel/rpc_0_6.ts @@ -0,0 +1,561 @@ +import { + HEX_STR_TRANSACTION_VERSION_1, + HEX_STR_TRANSACTION_VERSION_2, + NetworkName, + StarknetChainId, +} from '../constants'; +import { LibraryError } from '../provider/errors'; +import { + AccountInvocationItem, + AccountInvocations, + BigNumberish, + BlockIdentifier, + BlockTag, + Call, + DeclareContractTransaction, + DeployAccountContractTransaction, + Invocation, + InvocationsDetailsWithNonce, + RPC, + RpcProviderOptions, + TransactionType, + getEstimateFeeBulkOptions, + getSimulateTransactionOptions, + waitForTransactionOptions, +} from '../types'; +import { CallData } from '../utils/calldata'; +import { isSierra } from '../utils/contract'; +import fetch from '../utils/fetchPonyfill'; +import { getSelector, getSelectorFromName, getVersionsByType } from '../utils/hash'; +import { stringify } from '../utils/json'; +import { getHexStringArray, toHex, toStorageKey } from '../utils/num'; +import { Block, getDefaultNodeUrl, wait } from '../utils/provider'; +import { decompressProgram, signatureToHexArray } from '../utils/stark'; + +const defaultOptions = { + headers: { 'Content-Type': 'application/json' }, + blockIdentifier: BlockTag.pending, + retries: 200, +}; + +export class RpcChannel { + public nodeUrl: string; + + public headers: object; + + readonly retries: number; + + readonly blockIdentifier: BlockIdentifier; + + private chainId?: StarknetChainId; + + readonly waitMode: Boolean; // behave like web2 rpc and return when tx is processed + + readonly rpcSpec = '0_6'; + + constructor(optionsOrProvider?: RpcProviderOptions) { + const { nodeUrl, retries, headers, blockIdentifier, chainId, waitMode } = + optionsOrProvider || {}; + if (Object.values(NetworkName).includes(nodeUrl as NetworkName)) { + this.nodeUrl = getDefaultNodeUrl(nodeUrl as NetworkName, optionsOrProvider?.default); + } else if (nodeUrl) { + this.nodeUrl = nodeUrl; + } else { + this.nodeUrl = getDefaultNodeUrl(undefined, optionsOrProvider?.default); + } + this.retries = retries || defaultOptions.retries; + this.headers = { ...defaultOptions.headers, ...headers }; + this.blockIdentifier = blockIdentifier || defaultOptions.blockIdentifier; + this.chainId = chainId; + this.waitMode = waitMode || false; + } + + public fetch(method: string, params?: object, id: string | number = 0) { + const rpcRequestBody: RPC.JRPC.RequestBody = { + id, + jsonrpc: '2.0', + method, + ...(params && { params }), + }; + return fetch(this.nodeUrl, { + method: 'POST', + body: stringify(rpcRequestBody), + headers: this.headers as Record, + }); + } + + protected errorHandler(method: string, params: any, rpcError?: RPC.JRPC.Error, otherError?: any) { + if (rpcError) { + const { code, message, data } = rpcError; + throw new LibraryError( + `RPC: ${method} with params ${stringify(params)}\n ${code}: ${message}: ${stringify(data)}` + ); + } + if (otherError instanceof LibraryError) { + throw otherError; + } + if (otherError) { + throw Error(otherError.message); + } + } + + protected async fetchEndpoint( + method: T, + params?: RPC.Methods[T]['params'] + ): Promise { + try { + const rawResult = await this.fetch(method, params); + const { error, result } = await rawResult.json(); + this.errorHandler(method, params, error); + return result as RPC.Methods[T]['result']; + } catch (error: any) { + this.errorHandler(method, params, error?.response?.data, error); + throw error; + } + } + + public async getChainId() { + this.chainId ??= (await this.fetchEndpoint('starknet_chainId')) as StarknetChainId; + return this.chainId; + } + + public getSpecVersion() { + return this.fetchEndpoint('starknet_specVersion'); + } + + public getNonceForAddress( + contractAddress: BigNumberish, + blockIdentifier: BlockIdentifier = this.blockIdentifier + ) { + const contract_address = toHex(contractAddress); + const block_id = new Block(blockIdentifier).identifier; + return this.fetchEndpoint('starknet_getNonce', { + contract_address, + block_id, + }); + } + + /** + * Get the most recent accepted block hash and number + */ + public getBlockLatestAccepted() { + return this.fetchEndpoint('starknet_blockHashAndNumber'); + } + + /** + * Get the most recent accepted block number + * redundant use getBlockLatestAccepted(); + * @returns Number of the latest block + */ + public getBlockNumber() { + return this.fetchEndpoint('starknet_blockNumber'); + } + + public getBlockWithTxHashes(blockIdentifier: BlockIdentifier = this.blockIdentifier) { + const block_id = new Block(blockIdentifier).identifier; + return this.fetchEndpoint('starknet_getBlockWithTxHashes', { block_id }); + } + + public getBlockWithTxs(blockIdentifier: BlockIdentifier = this.blockIdentifier) { + const block_id = new Block(blockIdentifier).identifier; + return this.fetchEndpoint('starknet_getBlockWithTxs', { block_id }); + } + + public getBlockStateUpdate(blockIdentifier: BlockIdentifier = this.blockIdentifier) { + const block_id = new Block(blockIdentifier).identifier; + return this.fetchEndpoint('starknet_getStateUpdate', { block_id }); + } + + public getBlockTransactionsTraces(blockIdentifier: BlockIdentifier = this.blockIdentifier) { + const block_id = new Block(blockIdentifier).identifier; + return this.fetchEndpoint('starknet_traceBlockTransactions', { block_id }); + } + + public getBlockTransactionCount(blockIdentifier: BlockIdentifier = this.blockIdentifier) { + const block_id = new Block(blockIdentifier).identifier; + return this.fetchEndpoint('starknet_getBlockTransactionCount', { block_id }); + } + + public getTransactionByHash(txHash: BigNumberish) { + const transaction_hash = toHex(txHash); + return this.fetchEndpoint('starknet_getTransactionByHash', { + transaction_hash, + }); + } + + public getTransactionByBlockIdAndIndex(blockIdentifier: BlockIdentifier, index: number) { + const block_id = new Block(blockIdentifier).identifier; + return this.fetchEndpoint('starknet_getTransactionByBlockIdAndIndex', { block_id, index }); + } + + public getTransactionReceipt(txHash: BigNumberish) { + const transaction_hash = toHex(txHash); + return this.fetchEndpoint('starknet_getTransactionReceipt', { transaction_hash }); + } + + public getTransactionTrace(txHash: BigNumberish) { + const transaction_hash = toHex(txHash); + return this.fetchEndpoint('starknet_traceTransaction', { transaction_hash }); + } + + /** + * Get the status of a transaction + */ + public getTransactionStatus(transactionHash: BigNumberish) { + const transaction_hash = toHex(transactionHash); + return this.fetchEndpoint('starknet_getTransactionStatus', { transaction_hash }); + } + + /** + * @param invocations AccountInvocations + * @param simulateTransactionOptions blockIdentifier and flags to skip validation and fee charge
+ * - blockIdentifier
+ * - skipValidate (default false)
+ * - skipFeeCharge (default true)
+ */ + public simulateTransaction( + invocations: AccountInvocations, + { + blockIdentifier = this.blockIdentifier, + skipValidate = false, + skipFeeCharge = true, + }: getSimulateTransactionOptions = {} + ) { + const block_id = new Block(blockIdentifier).identifier; + const simulationFlags = []; + if (skipValidate) simulationFlags.push(RPC.ESimulationFlag.SKIP_VALIDATE); + if (skipFeeCharge) simulationFlags.push(RPC.ESimulationFlag.SKIP_FEE_CHARGE); + + return this.fetchEndpoint('starknet_simulateTransactions', { + block_id, + transactions: invocations.map((it) => this.buildTransaction(it)), + simulation_flags: simulationFlags, + }); + } + + public async waitForTransaction(txHash: BigNumberish, options?: waitForTransactionOptions) { + const transactionHash = toHex(txHash); + let { retries } = this; + let onchain = false; + let isErrorState = false; + const retryInterval = options?.retryInterval ?? 5000; + const errorStates: any = options?.errorStates ?? [ + RPC.ETransactionStatus.REJECTED, + RPC.ETransactionExecutionStatus.REVERTED, + ]; + const successStates: any = options?.successStates ?? [ + RPC.ETransactionExecutionStatus.SUCCEEDED, + RPC.ETransactionStatus.ACCEPTED_ON_L2, + RPC.ETransactionStatus.ACCEPTED_ON_L1, + ]; + + let txStatus: RPC.TransactionStatus; + while (!onchain) { + // eslint-disable-next-line no-await-in-loop + await wait(retryInterval); + try { + // eslint-disable-next-line no-await-in-loop + txStatus = await this.getTransactionStatus(transactionHash); + + const executionStatus = txStatus.execution_status; + const finalityStatus = txStatus.finality_status; + + if (!finalityStatus) { + // Transaction is potentially NOT_RECEIVED or RPC not Synced yet + // so we will retry '{ retries }' times + const error = new Error('waiting for transaction status'); + throw error; + } + + if (successStates.includes(executionStatus) || successStates.includes(finalityStatus)) { + onchain = true; + } else if (errorStates.includes(executionStatus) || errorStates.includes(finalityStatus)) { + const message = `${executionStatus}: ${finalityStatus}`; + const error = new Error(message) as Error & { response: RPC.TransactionStatus }; + error.response = txStatus; + isErrorState = true; + throw error; + } + } catch (error) { + if (error instanceof Error && isErrorState) { + throw error; + } + + if (retries <= 0) { + throw new Error(`waitForTransaction timed-out with retries ${this.retries}`); + } + } + + retries -= 1; + } + + /** + * For some nodes even though the transaction has executionStatus SUCCEEDED finalityStatus ACCEPTED_ON_L2, getTransactionReceipt returns "Transaction hash not found" + * Retry until rpc is actually ready to work with txHash + */ + let txReceipt = null; + while (txReceipt === null) { + try { + // eslint-disable-next-line no-await-in-loop + txReceipt = await this.getTransactionReceipt(transactionHash); + } catch (error) { + if (retries <= 0) { + throw new Error(`waitForTransaction timed-out with retries ${this.retries}`); + } + } + retries -= 1; + // eslint-disable-next-line no-await-in-loop + await wait(retryInterval); + } + return txReceipt as RPC.TransactionReceipt; + } + + public getStorageAt( + contractAddress: BigNumberish, + key: BigNumberish, + blockIdentifier: BlockIdentifier = this.blockIdentifier + ) { + const contract_address = toHex(contractAddress); + const parsedKey = toStorageKey(key); + const block_id = new Block(blockIdentifier).identifier; + return this.fetchEndpoint('starknet_getStorageAt', { + contract_address, + key: parsedKey, + block_id, + }); + } + + public getClassHashAt( + contractAddress: BigNumberish, + blockIdentifier: BlockIdentifier = this.blockIdentifier + ) { + const contract_address = toHex(contractAddress); + const block_id = new Block(blockIdentifier).identifier; + return this.fetchEndpoint('starknet_getClassHashAt', { + block_id, + contract_address, + }); + } + + public getClass( + classHash: BigNumberish, + blockIdentifier: BlockIdentifier = this.blockIdentifier + ) { + const class_hash = toHex(classHash); + const block_id = new Block(blockIdentifier).identifier; + return this.fetchEndpoint('starknet_getClass', { + class_hash, + block_id, + }); + } + + public getClassAt( + contractAddress: BigNumberish, + blockIdentifier: BlockIdentifier = this.blockIdentifier + ) { + const contract_address = toHex(contractAddress); + const block_id = new Block(blockIdentifier).identifier; + return this.fetchEndpoint('starknet_getClassAt', { + block_id, + contract_address, + }); + } + + public getEstimateFee( + invocations: AccountInvocations, + { blockIdentifier = this.blockIdentifier }: getEstimateFeeBulkOptions + ) { + const block_id = new Block(blockIdentifier).identifier; + return this.fetchEndpoint('starknet_estimateFee', { + request: invocations.map((it) => this.buildTransaction(it, 'fee')), + block_id, + }); + } + + public async invoke(functionInvocation: Invocation, details: InvocationsDetailsWithNonce) { + const promise = this.fetchEndpoint('starknet_addInvokeTransaction', { + invoke_transaction: { + sender_address: functionInvocation.contractAddress, + calldata: CallData.toHex(functionInvocation.calldata), + type: RPC.ETransactionType.INVOKE, + max_fee: toHex(details.maxFee || 0), + version: '0x1', + signature: signatureToHexArray(functionInvocation.signature), + nonce: toHex(details.nonce), + }, + }); + + return this.waitMode ? this.waitForTransaction((await promise).transaction_hash) : promise; + } + + public async declare( + { contract, signature, senderAddress, compiledClassHash }: DeclareContractTransaction, + details: InvocationsDetailsWithNonce + ) { + let promise; + if (!isSierra(contract)) { + promise = this.fetchEndpoint('starknet_addDeclareTransaction', { + declare_transaction: { + type: RPC.ETransactionType.DECLARE, + contract_class: { + program: contract.program, + entry_points_by_type: contract.entry_points_by_type, + abi: contract.abi, + }, + version: HEX_STR_TRANSACTION_VERSION_1, + max_fee: toHex(details.maxFee || 0), + signature: signatureToHexArray(signature), + sender_address: senderAddress, + nonce: toHex(details.nonce), + }, + }); + } else { + promise = this.fetchEndpoint('starknet_addDeclareTransaction', { + declare_transaction: { + type: RPC.ETransactionType.DECLARE, + contract_class: { + sierra_program: decompressProgram(contract.sierra_program), + contract_class_version: contract.contract_class_version, + entry_points_by_type: contract.entry_points_by_type, + abi: contract.abi, + }, + compiled_class_hash: compiledClassHash || '', + version: HEX_STR_TRANSACTION_VERSION_2, + max_fee: toHex(details.maxFee || 0), + signature: signatureToHexArray(signature), + sender_address: senderAddress, + nonce: toHex(details.nonce), + }, + }); + } + + return this.waitMode ? this.waitForTransaction((await promise).transaction_hash) : promise; + } + + public async deployAccount( + { classHash, constructorCalldata, addressSalt, signature }: DeployAccountContractTransaction, + details: InvocationsDetailsWithNonce + ) { + const promise = this.fetchEndpoint('starknet_addDeployAccountTransaction', { + deploy_account_transaction: { + constructor_calldata: CallData.toHex(constructorCalldata || []), + class_hash: toHex(classHash), + contract_address_salt: toHex(addressSalt || 0), + type: RPC.ETransactionType.DEPLOY_ACCOUNT, + max_fee: toHex(details.maxFee || 0), + version: toHex(details.version || 0), + signature: signatureToHexArray(signature), + nonce: toHex(details.nonce), + }, + }); + + return this.waitMode ? this.waitForTransaction((await promise).transaction_hash) : promise; + } + + public callContract(call: Call, blockIdentifier: BlockIdentifier = this.blockIdentifier) { + const block_id = new Block(blockIdentifier).identifier; + return this.fetchEndpoint('starknet_call', { + request: { + contract_address: call.contractAddress, + entry_point_selector: getSelectorFromName(call.entrypoint), + calldata: CallData.toHex(call.calldata), + }, + block_id, + }); + } + + /** + * NEW: Estimate the fee for a message from L1 + * @param message Message From L1 + */ + public estimateMessageFee( + message: RPC.L1Message, + blockIdentifier: BlockIdentifier = this.blockIdentifier + ) { + const { from_address, to_address, entry_point_selector, payload } = message; + const formattedMessage = { + from_address: toHex(from_address), + to_address: toHex(to_address), + entry_point_selector: getSelector(entry_point_selector), + payload: getHexStringArray(payload), + }; + + const block_id = new Block(blockIdentifier).identifier; + return this.fetchEndpoint('starknet_estimateMessageFee', { + message: formattedMessage, + block_id, + }); + } + + /** + * Returns an object about the sync status, or false if the node is not synching + * @returns Object with the stats data + */ + public getSyncingStats() { + return this.fetchEndpoint('starknet_syncing'); + } + + /** + * Returns all events matching the given filter + * @returns events and the pagination of the events + */ + public getEvents(eventFilter: RPC.EventFilter) { + return this.fetchEndpoint('starknet_getEvents', { filter: eventFilter }); + } + + public buildTransaction( + invocation: AccountInvocationItem, + versionType?: 'fee' | 'transaction' + ): RPC.BaseTransaction { + const defaultVersions = getVersionsByType(versionType); + const details = { + signature: signatureToHexArray(invocation.signature), + nonce: toHex(invocation.nonce), + max_fee: toHex(invocation.maxFee || 0), + }; + + if (invocation.type === TransactionType.INVOKE) { + return { + type: RPC.ETransactionType.INVOKE, // Diff between sequencer and rpc invoke type + sender_address: invocation.contractAddress, + calldata: CallData.toHex(invocation.calldata), + version: toHex(invocation.version || defaultVersions.v1), + ...details, + }; + } + if (invocation.type === TransactionType.DECLARE) { + if (!isSierra(invocation.contract)) { + return { + type: invocation.type, + contract_class: invocation.contract, + sender_address: invocation.senderAddress, + version: toHex(invocation.version || defaultVersions.v1), + ...details, + }; + } + return { + // compiled_class_hash + type: invocation.type, + contract_class: { + ...invocation.contract, + sierra_program: decompressProgram(invocation.contract.sierra_program), + }, + compiled_class_hash: invocation.compiledClassHash || '', + sender_address: invocation.senderAddress, + version: toHex(invocation.version || defaultVersions.v2), + ...details, + }; + } + if (invocation.type === TransactionType.DEPLOY_ACCOUNT) { + return { + type: invocation.type, + constructor_calldata: CallData.toHex(invocation.constructorCalldata || []), + class_hash: toHex(invocation.classHash), + contract_address_salt: toHex(invocation.addressSalt || 0), + version: toHex(invocation.version || defaultVersions.v1), + ...details, + }; + } + throw Error('RPC buildTransaction received unknown TransactionType'); + } +} diff --git a/src/provider/interface.ts b/src/provider/interface.ts index bca3614c0..0333b9a70 100644 --- a/src/provider/interface.ts +++ b/src/provider/interface.ts @@ -1,4 +1,4 @@ -import { RpcChannel } from '../channel/rpc'; +import { RpcChannel } from '../channel/rpc_0_5'; import { StarknetChainId } from '../constants'; import type { AccountInvocations, diff --git a/src/provider/rpc.ts b/src/provider/rpc.ts index eba74620c..53ac3eafc 100644 --- a/src/provider/rpc.ts +++ b/src/provider/rpc.ts @@ -1,4 +1,4 @@ -import { RpcChannel } from '../channel/rpc'; +import { RpcChannel } from '../channel/rpc_0_5'; import { AccountInvocations, BigNumberish, diff --git a/src/types/api/rpc.ts b/src/types/api/rpc.ts index ee34d1727..49f4cdf89 100644 --- a/src/types/api/rpc.ts +++ b/src/types/api/rpc.ts @@ -1,2 +1,2 @@ export * as JRPC from './jsonrpc'; -export * from './rpcspec'; +export * from './rpcspec_0_5'; diff --git a/src/types/api/rpcspec/components.ts b/src/types/api/rpcspec_0_5/components.ts similarity index 100% rename from src/types/api/rpcspec/components.ts rename to src/types/api/rpcspec_0_5/components.ts diff --git a/src/types/api/rpcspec/contract.ts b/src/types/api/rpcspec_0_5/contract.ts similarity index 100% rename from src/types/api/rpcspec/contract.ts rename to src/types/api/rpcspec_0_5/contract.ts diff --git a/src/types/api/rpcspec/errors.ts b/src/types/api/rpcspec_0_5/errors.ts similarity index 100% rename from src/types/api/rpcspec/errors.ts rename to src/types/api/rpcspec_0_5/errors.ts diff --git a/src/types/api/rpcspec/index.ts b/src/types/api/rpcspec_0_5/index.ts similarity index 100% rename from src/types/api/rpcspec/index.ts rename to src/types/api/rpcspec_0_5/index.ts diff --git a/src/types/api/rpcspec/methods.ts b/src/types/api/rpcspec_0_5/methods.ts similarity index 100% rename from src/types/api/rpcspec/methods.ts rename to src/types/api/rpcspec_0_5/methods.ts diff --git a/src/types/api/rpcspec/nonspec.ts b/src/types/api/rpcspec_0_5/nonspec.ts similarity index 100% rename from src/types/api/rpcspec/nonspec.ts rename to src/types/api/rpcspec_0_5/nonspec.ts diff --git a/src/types/api/rpcspec_0_6/components.ts b/src/types/api/rpcspec_0_6/components.ts new file mode 100644 index 000000000..3d08a538c --- /dev/null +++ b/src/types/api/rpcspec_0_6/components.ts @@ -0,0 +1,669 @@ +/** + * PRIMITIVES + */ + +/** + * A field element. represented by at most 63 hex digits + * @pattern ^0x(0|[a-fA-F1-9]{1}[a-fA-F0-9]{0,62})$ + */ +export type FELT = string; +/** + * an ethereum address represented as 40 hex digits + * @pattern ^0x[a-fA-F0-9]{40}$ + */ +export type ETH_ADDRESS = string; +/** + * A storage key. Represented as up to 62 hex digits, 3 bits, and 5 leading zeroes. + * @pattern ^0x0[0-7]{1}[a-fA-F0-9]{0,62}$ + */ +export type STORAGE_KEY = string; +export type ADDRESS = FELT; +export type NUM_AS_HEX = string; +/** + * 64 bit integers, represented by hex string of length at most 16 + * "pattern": "^0x(0|[a-fA-F1-9]{1}[a-fA-F0-9]{0,15})$" + */ +export type u64 = string; +/** + * 64 bit integers, represented by hex string of length at most 32 + * "pattern": "^0x(0|[a-fA-F1-9]{1}[a-fA-F0-9]{0,31})$" + */ +export type u128 = string; +export type SIGNATURE = Array; +export type BLOCK_NUMBER = number; +export type BLOCK_HASH = FELT; +export type TXN_HASH = FELT; +export type CHAIN_ID = NUM_AS_HEX; +export type STRUCT_ABI_TYPE = 'struct'; +export type EVENT_ABI_TYPE = 'event'; +export type FUNCTION_ABI_TYPE = 'function' | 'l1_handler' | 'constructor'; +// Represents the type of an entry point. +export type ENTRY_POINT_TYPE = 'EXTERNAL' | 'L1_HANDLER' | 'CONSTRUCTOR'; +// Represents the type of a function call. +export type CALL_TYPE = 'DELEGATE' | 'LIBRARY_CALL' | 'CALL'; +// Represents the status of the transaction +export type TXN_STATUS = 'RECEIVED' | 'REJECTED' | 'ACCEPTED_ON_L2' | 'ACCEPTED_ON_L1'; +// Flags that indicate how to simulate a given transaction. By default, the sequencer behavior is replicated locally (enough funds are expected to be in the account, and the fee will be deducted from the balance before the simulation of the next transaction). To skip the fee charge, use the SKIP_FEE_CHARGE flag. +export type SIMULATION_FLAG = 'SKIP_VALIDATE' | 'SKIP_FEE_CHARGE'; +// Data availability mode +export type DA_MODE = 'L1' | 'L2'; +export type TXN_TYPE = 'DECLARE' | 'DEPLOY' | 'DEPLOY_ACCOUNT' | 'INVOKE' | 'L1_HANDLER'; +export type TXN_FINALITY_STATUS = 'ACCEPTED_ON_L2' | 'ACCEPTED_ON_L1'; +export type TXN_EXECUTION_STATUS = 'SUCCEEDED' | 'REVERTED'; +export type BLOCK_STATUS = 'PENDING' | 'ACCEPTED_ON_L2' | 'ACCEPTED_ON_L1' | 'REJECTED'; +export type BLOCK_TAG = 'latest' | 'pending'; + +/** + * READ API + */ + +export type EVENTS_CHUNK = { + // Returns matching events + events: EMITTED_EVENT[]; + // Use this token in a subsequent query to obtain the next page. Should not appear if there are no more pages. + continuation_token?: string; +}; + +export type RESULT_PAGE_REQUEST = { + // The token returned from the previous query. If no token is provided the first page is returned. + continuation_token?: string; + // Chunk size + chunk_size: number; +}; + +export type EMITTED_EVENT = EVENT & { + transaction_hash: TXN_HASH; +}; + +export type EVENT = { + from_address: ADDRESS; +} & EVENT_CONTENT; + +export type EVENT_CONTENT = { + keys: FELT[]; + data: FELT[]; +}; + +export type EVENT_FILTER = { + from_block?: BLOCK_ID; + to_block?: BLOCK_ID; + address?: ADDRESS; + keys?: FELT[][]; +}; + +export type BLOCK_ID = + | { + block_hash?: BLOCK_HASH; + block_number?: BLOCK_NUMBER; + } + | BLOCK_TAG; + +export type SYNC_STATUS = { + starting_block_hash: BLOCK_HASH; + starting_block_num: BLOCK_NUMBER; + current_block_hash: BLOCK_HASH; + current_block_num: BLOCK_NUMBER; + highest_block_hash: BLOCK_HASH; + highest_block_num: BLOCK_NUMBER; +}; + +export type NEW_CLASSES = { + class_hash: FELT; + compiled_class_hash: FELT; +}; + +export type REPLACED_CLASS = { + class_hash: FELT; + contract_address: FELT; +}; + +export type NONCE_UPDATE = { + contract_address: ADDRESS; + nonce: FELT; +}; + +export type STATE_DIFF = { + storage_diffs: CONTRACT_STORAGE_DIFF_ITEM[]; + deprecated_declared_classes: FELT[]; + declared_classes: NEW_CLASSES[]; + deployed_contracts: DEPLOYED_CONTRACT_ITEM[]; + replaced_classes: REPLACED_CLASS[]; + nonces: NONCE_UPDATE[]; +}; + +export type PENDING_STATE_UPDATE = { + old_root: FELT; + state_diff: STATE_DIFF; +}; + +export type STATE_UPDATE = { + block_hash: BLOCK_HASH; + old_root: FELT; + new_root: FELT; + state_diff: STATE_DIFF; +}; + +export type BLOCK_BODY_WITH_TX_HASHES = { + transactions: TXN_HASH[]; +}; + +export type BLOCK_BODY_WITH_TXS = { + transactions: { + transaction: TXN; + transaction_hash: TXN_HASH; + }[]; +}; + +export type BLOCK_HEADER = { + block_hash: BLOCK_HASH; + parent_hash: BLOCK_HASH; + block_number: BLOCK_NUMBER; + new_root: FELT; + timestamp: number; + sequencer_address: FELT; + l1_gas_price: RESOURCE_PRICE; + starknet_version: string; +}; + +export type PENDING_BLOCK_HEADER = { + parent_hash: BLOCK_HASH; + timestamp: number; + sequencer_address: FELT; + l1_gas_price: RESOURCE_PRICE; + starknet_version: string; +}; + +export type BLOCK_WITH_TX_HASHES = { status: BLOCK_STATUS } & BLOCK_HEADER & + BLOCK_BODY_WITH_TX_HASHES; + +export type BLOCK_WITH_TXS = { status: BLOCK_STATUS } & BLOCK_HEADER & BLOCK_BODY_WITH_TXS; + +export type PENDING_BLOCK_WITH_TX_HASHES = BLOCK_BODY_WITH_TX_HASHES & PENDING_BLOCK_HEADER; + +export type PENDING_BLOCK_WITH_TXS = BLOCK_BODY_WITH_TXS & PENDING_BLOCK_HEADER; + +export type DEPLOYED_CONTRACT_ITEM = { + address: FELT; + class_hash: FELT; +}; + +export type CONTRACT_STORAGE_DIFF_ITEM = { + // The contract address for which the storage changed (in FELT format) + address: string; + // The changes in the storage of the contract + storage_entries: StorageDiffItem[]; +}; + +export type StorageDiffItem = { + // The key of the changed value (in FELT format) + key: string; + // The new value applied to the given address (in FELT format) + value: string; +}; + +export type TXN = INVOKE_TXN | L1_HANDLER_TXN | DECLARE_TXN | DEPLOY_TXN | DEPLOY_ACCOUNT_TXN; + +export type DECLARE_TXN = DECLARE_TXN_V0 | DECLARE_TXN_V1 | DECLARE_TXN_V2 | DECLARE_TXN_V3; + +export type DECLARE_TXN_V0 = { + type: 'DECLARE'; + sender_address: ADDRESS; + max_fee: FELT; + version: '0x0' | '0x100000000000000000000000000000000'; + signature: SIGNATURE; + class_hash: FELT; +}; + +export type DECLARE_TXN_V1 = { + type: 'DECLARE'; + sender_address: ADDRESS; + max_fee: FELT; + version: '0x1' | '0x100000000000000000000000000000001'; + signature: SIGNATURE; + nonce: FELT; + class_hash: FELT; +}; + +export type DECLARE_TXN_V2 = { + type: 'DECLARE'; + sender_address: ADDRESS; + compiled_class_hash: FELT; + max_fee: FELT; + version: '0x2' | '0x100000000000000000000000000000002'; + signature: SIGNATURE; + nonce: FELT; + class_hash: FELT; +}; + +export type DECLARE_TXN_V3 = { + type: 'DECLARE'; + sender_address: ADDRESS; + compiled_class_hash: FELT; + version: '0x3' | '0x100000000000000000000000000000003'; + signature: SIGNATURE; + nonce: FELT; + class_hash: FELT; + // new... + resource_bounds: RESOURCE_BOUNDS_MAPPING; + tip: u64; + paymaster_data: FELT[]; + account_deployment_data: FELT[]; + nonce_data_availability_mode: DA_MODE; + fee_data_availability_mode: DA_MODE; +}; + +export type BROADCASTED_TXN = + | BROADCASTED_INVOKE_TXN + | BROADCASTED_DECLARE_TXN + | BROADCASTED_DEPLOY_ACCOUNT_TXN; + +export type BROADCASTED_INVOKE_TXN = INVOKE_TXN; + +export type BROADCASTED_DEPLOY_ACCOUNT_TXN = DEPLOY_ACCOUNT_TXN; + +export type BROADCASTED_DECLARE_TXN = + | BROADCASTED_DECLARE_TXN_V1 + | BROADCASTED_DECLARE_TXN_V2 + | BROADCASTED_DECLARE_TXN_V3; + +export type BROADCASTED_DECLARE_TXN_V1 = { + type: 'DECLARE'; + sender_address: ADDRESS; + max_fee: FELT; + // todo: check if working, prev i fixed it with NUM_AS_HEX + version: '0x1' | '0x100000000000000000000000000000001'; + signature: SIGNATURE; + nonce: FELT; + contract_class: DEPRECATED_CONTRACT_CLASS; +}; + +export type BROADCASTED_DECLARE_TXN_V2 = { + type: 'DECLARE'; + sender_address: ADDRESS; + compiled_class_hash: FELT; + max_fee: FELT; + version: '0x2' | '0x100000000000000000000000000000002'; + signature: SIGNATURE; + nonce: FELT; + contract_class: CONTRACT_CLASS; +}; + +export type BROADCASTED_DECLARE_TXN_V3 = { + type: 'DECLARE'; + sender_address: ADDRESS; + compiled_class_hash: FELT; + version: '0x3' | '0x100000000000000000000000000000003'; + signature: SIGNATURE; + nonce: FELT; + contract_class: CONTRACT_CLASS; + // new... + resource_bounds: RESOURCE_BOUNDS_MAPPING; + tip: u64; + paymaster_data: FELT[]; + account_deployment_data: FELT[]; + nonce_data_availability_mode: DA_MODE; + fee_data_availability_mode: DA_MODE; +}; + +export type DEPLOY_ACCOUNT_TXN = DEPLOY_ACCOUNT_TXN_V1 | DEPLOY_ACCOUNT_TXN_V3; + +export type DEPLOY_ACCOUNT_TXN_V1 = { + type: 'DEPLOY_ACCOUNT'; + max_fee: FELT; + version: '0x1' | '0x100000000000000000000000000000001'; + signature: SIGNATURE; + nonce: FELT; + contract_address_salt: FELT; + constructor_calldata: FELT[]; + class_hash: FELT; +}; + +export type DEPLOY_ACCOUNT_TXN_V3 = { + type: 'DEPLOY_ACCOUNT'; + max_fee: FELT; + version: '0x3' | '0x100000000000000000000000000000003'; + signature: SIGNATURE; + nonce: FELT; + contract_address_salt: FELT; + constructor_calldata: FELT[]; + class_hash: FELT; + resource_bounds: RESOURCE_BOUNDS_MAPPING; + tip: u64; + paymaster_data: FELT[]; + account_deployment_data: FELT[]; + nonce_data_availability_mode: DA_MODE; + fee_data_availability_mode: DA_MODE; +}; + +export type DEPLOY_TXN = { + type: 'DEPLOY'; + version: FELT; + contract_address_salt: FELT; + constructor_calldata: FELT[]; + class_hash: FELT; +}; + +export type INVOKE_TXN = INVOKE_TXN_V0 | INVOKE_TXN_V1 | INVOKE_TXN_V3; + +export type INVOKE_TXN_V0 = { + type: 'INVOKE'; + max_fee: FELT; + version: '0x0' | '0x100000000000000000000000000000000'; + signature: SIGNATURE; + contract_address: ADDRESS; + entry_point_selector: FELT; + calldata: FELT[]; +}; + +export type INVOKE_TXN_V1 = { + type: 'INVOKE'; + sender_address: ADDRESS; + calldata: FELT[]; + max_fee: FELT; + version: '0x1' | '0x100000000000000000000000000000001'; + signature: SIGNATURE; + nonce: FELT; +}; + +export type INVOKE_TXN_V3 = { + type: 'INVOKE'; + sender_address: ADDRESS; + calldata: FELT[]; + version: '0x3' | '0x100000000000000000000000000000003'; + signature: SIGNATURE; + nonce: FELT; + resource_bounds: RESOURCE_BOUNDS_MAPPING; + tip: u64; + paymaster_data: FELT[]; + account_deployment_data: FELT[]; + nonce_data_availability_mode: DA_MODE; + fee_data_availability_mode: DA_MODE; +}; + +export type L1_HANDLER_TXN = { + version: FELT; + type: 'L1_HANDLER'; + nonce: NUM_AS_HEX; +} & FUNCTION_CALL; + +export type COMMON_RECEIPT_PROPERTIES = { + transaction_hash: TXN_HASH; + actual_fee: FEE_PAYMENT; + execution_status: TXN_EXECUTION_STATUS; + finality_status: TXN_FINALITY_STATUS; + block_hash: BLOCK_HASH; + block_number: BLOCK_NUMBER; + messages_sent: MSG_TO_L1[]; + revert_reason?: string; + events: EVENT[]; + execution_resources: EXECUTION_RESOURCES; +}; + +export type PENDING_COMMON_RECEIPT_PROPERTIES = { + transaction_hash: TXN_HASH; + actual_fee: FEE_PAYMENT; + messages_sent: MSG_TO_L1[]; + events: EVENT[]; + revert_reason?: string; + finality_status: 'ACCEPTED_ON_L2'; + execution_status: TXN_EXECUTION_STATUS; + execution_resources: EXECUTION_RESOURCES; +}; + +export type INVOKE_TXN_RECEIPT = { + type: 'INVOKE'; +} & COMMON_RECEIPT_PROPERTIES; + +export type PENDING_INVOKE_TXN_RECEIPT = { + type: 'INVOKE'; +} & PENDING_COMMON_RECEIPT_PROPERTIES; + +export type DECLARE_TXN_RECEIPT = { + type: 'DECLARE'; +} & COMMON_RECEIPT_PROPERTIES; + +export type PENDING_DECLARE_TXN_RECEIPT = { + type: 'DECLARE'; +} & PENDING_COMMON_RECEIPT_PROPERTIES; + +export type DEPLOY_ACCOUNT_TXN_RECEIPT = { + type: 'DEPLOY_ACCOUNT'; + contract_address: FELT; +} & COMMON_RECEIPT_PROPERTIES; + +export type PENDING_DEPLOY_ACCOUNT_TXN_RECEIPT = { + type: 'DEPLOY_ACCOUNT'; + contract_address: FELT; +} & PENDING_COMMON_RECEIPT_PROPERTIES; + +export type DEPLOY_TXN_RECEIPT = { + type: 'DEPLOY'; + contract_address: FELT; +} & COMMON_RECEIPT_PROPERTIES; + +export type L1_HANDLER_TXN_RECEIPT = { + type: 'L1_HANDLER'; + message_hash: NUM_AS_HEX; +} & COMMON_RECEIPT_PROPERTIES; + +export type PENDING_L1_HANDLER_TXN_RECEIPT = { + type: 'L1_HANDLER'; + message_hash: NUM_AS_HEX; +} & PENDING_COMMON_RECEIPT_PROPERTIES; + +export type TXN_RECEIPT = + | INVOKE_TXN_RECEIPT + | L1_HANDLER_TXN_RECEIPT + | DECLARE_TXN_RECEIPT + | DEPLOY_TXN_RECEIPT + | DEPLOY_ACCOUNT_TXN_RECEIPT; + +export type PENDING_TXN_RECEIPT = + | PENDING_INVOKE_TXN_RECEIPT + | PENDING_L1_HANDLER_TXN_RECEIPT + | PENDING_DECLARE_TXN_RECEIPT + | PENDING_DEPLOY_ACCOUNT_TXN_RECEIPT; + +export type MSG_TO_L1 = { + from_address: FELT; + to_address: FELT; + payload: FELT[]; +}; + +export type MSG_FROM_L1 = { + from_address: ETH_ADDRESS; + to_address: ADDRESS; + entry_point_selector: FELT; + payload: FELT[]; +}; + +export type FUNCTION_CALL = { + contract_address: ADDRESS; + entry_point_selector: FELT; + calldata: FELT[]; +}; + +export type CONTRACT_CLASS = { + sierra_program: FELT[]; + contract_class_version: string; + entry_points_by_type: { + CONSTRUCTOR: SIERRA_ENTRY_POINT[]; + EXTERNAL: SIERRA_ENTRY_POINT[]; + L1_HANDLER: SIERRA_ENTRY_POINT[]; + }; + abi: string; +}; + +export type DEPRECATED_CONTRACT_CLASS = { + program: string; + entry_points_by_type: { + CONSTRUCTOR: DEPRECATED_CAIRO_ENTRY_POINT[]; + EXTERNAL: DEPRECATED_CAIRO_ENTRY_POINT[]; + L1_HANDLER: DEPRECATED_CAIRO_ENTRY_POINT[]; + }; + abi: CONTRACT_ABI; +}; + +export type DEPRECATED_CAIRO_ENTRY_POINT = { + offset: NUM_AS_HEX | number; + selector: FELT; +}; + +export type SIERRA_ENTRY_POINT = { + selector: FELT; + function_idx: number; +}; + +export type CONTRACT_ABI = CONTRACT_ABI_ENTRY[]; + +export type CONTRACT_ABI_ENTRY = { + selector: FELT; + input: string; + output: string; +}; + +export type STRUCT_ABI_ENTRY = { + type: STRUCT_ABI_TYPE; + name: string; + size: number; + members: STRUCT_MEMBER[]; +}; + +export type STRUCT_MEMBER = TYPED_PARAMETER & { + offset: number; +}; + +export type EVENT_ABI_ENTRY = { + type: EVENT_ABI_TYPE; + name: string; + keys: TYPED_PARAMETER[]; + data: TYPED_PARAMETER[]; +}; + +export type FUNCTION_STATE_MUTABILITY = 'view'; + +export type FUNCTION_ABI_ENTRY = { + type: FUNCTION_ABI_TYPE; + name: string; + inputs: TYPED_PARAMETER[]; + outputs: TYPED_PARAMETER[]; + stateMutability: FUNCTION_STATE_MUTABILITY; +}; + +export type TYPED_PARAMETER = { + name: string; + type: string; +}; + +export type FEE_ESTIMATE = { + gas_consumed: FELT; + gas_price: FELT; + overall_fee: FELT; +}; + +export type FEE_PAYMENT = { + amount: FELT; + unit: 'WEI' | 'STRK'; +}; + +export type RESOURCE_BOUNDS_MAPPING = { + l1_gas: RESOURCE_BOUNDS; + l2_gas: RESOURCE_BOUNDS; +}; + +export type RESOURCE_BOUNDS = { + max_amount: u64; + max_price_per_unit: u128; +}; + +export type RESOURCE_PRICE = { + price_in_strk?: FELT; + price_in_wei: FELT; +}; + +export type EXECUTION_RESOURCES = { + steps: number; + memory_holes?: number; + range_check_builtin_applications?: number; + pedersen_builtin_applications?: number; + poseidon_builtin_applications?: number; + ec_op_builtin_applications?: number; + ecdsa_builtin_applications?: number; + bitwise_builtin_applications?: number; + keccak_builtin_applications?: number; + segment_arena_builtin?: number; +}; + +/** + * TRACE API + */ + +// Represents a transaction trace including the execution details. +export type TRANSACTION_TRACE = { + invoke_tx_trace?: INVOKE_TXN_TRACE; + declare_tx_trace?: DECLARE_TXN_TRACE; + deploy_account_tx_trace?: DEPLOY_ACCOUNT_TXN_TRACE; + l1_handler_tx_trace?: L1_HANDLER_TXN_TRACE; +}; + +// Represents a transaction trace for an invoke transaction. +export type INVOKE_TXN_TRACE = { + type: 'INVOKE'; + execute_invocation: FUNCTION_INVOCATION | { revert_reason: string }; + validate_invocation?: FUNCTION_INVOCATION; + fee_transfer_invocation?: FUNCTION_INVOCATION; + state_diff?: STATE_DIFF; +}; + +// Represents a transaction trace for a declare transaction. +export type DECLARE_TXN_TRACE = { + type: 'DECLARE'; + validate_invocation?: FUNCTION_INVOCATION; + fee_transfer_invocation?: FUNCTION_INVOCATION; + state_diff?: STATE_DIFF; +}; + +// Represents a transaction trace for a deploy account transaction. +export type DEPLOY_ACCOUNT_TXN_TRACE = { + type: 'DEPLOY_ACCOUNT'; + constructor_invocation: FUNCTION_INVOCATION; + validate_invocation?: FUNCTION_INVOCATION; + fee_transfer_invocation?: FUNCTION_INVOCATION; + state_diff?: STATE_DIFF; +}; + +// Represents a transaction trace for an L1 handler transaction. +export type L1_HANDLER_TXN_TRACE = { + type: 'L1_HANDLER'; + function_invocation: FUNCTION_INVOCATION; + state_diff?: STATE_DIFF; +}; + +// Represents a nested function call. +export type NESTED_CALL = FUNCTION_INVOCATION; + +// Represents a function invocation along with its execution details. +export type FUNCTION_INVOCATION = { + function_call: FUNCTION_CALL; + caller_address: string; + class_hash: string; + entry_point_type: ENTRY_POINT_TYPE; + call_type: CALL_TYPE; + result: string[]; + calls: NESTED_CALL[]; + events: ORDERED_EVENT[]; + messages: ORDERED_MESSAGE[]; + execution_resources: EXECUTION_RESOURCES; +}; + +// Represents an ordered event alongside its order within the transaction. +export type ORDERED_EVENT = { + order: number; + event: EVENT; +}; + +// Represents an ordered message alongside its order within the transaction. +export type ORDERED_MESSAGE = { + order: number; + message: MSG_TO_L1; +}; diff --git a/src/types/api/rpcspec_0_6/contract.ts b/src/types/api/rpcspec_0_6/contract.ts new file mode 100644 index 000000000..5cb22cad4 --- /dev/null +++ b/src/types/api/rpcspec_0_6/contract.ts @@ -0,0 +1,101 @@ +/** + * TypeScript Representation of Cairo1 v2+ Starknet Contract ABI + * + * starknet_metadata.json - tags/v0.5.0 + * + * 'starknet-specs' (OpenRpc protocol types) + * https://github.com/starkware-libs/starknet-specs + */ + +export type ABI = Array< + FUNCTION | CONSTRUCTOR | L1_HANDLER | EVENT | STRUCT | ENUM | INTERFACE | IMPL +>; + +type FUNCTION = { + type: 'function'; + name: string; + inputs: Array<{ + name: string; + type: string; + }>; + outputs?: Array<{ + type: string; + }>; + state_mutability: 'view' | 'external'; +}; + +type CONSTRUCTOR = { + type: 'constructor'; + name: 'constructor'; + inputs: Array<{ + name: string; + type: string; + }>; +}; + +type L1_HANDLER = { + type: 'l1_handler'; + name: string; + inputs: Array<{ + name: string; + type: string; + }>; + outputs?: Array<{ + type: string; + }>; + state_mutability: 'view' | 'external'; +}; + +type EVENT = { + type: 'event'; + name: string; +} & (ENUM_EVENT | STRUCT_EVENT); + +type STRUCT_EVENT = { + kind: 'struct'; + members: Array; +}; + +type ENUM_EVENT = { + kind: 'enum'; + variants: Array; +}; + +type STRUCT = { + type: 'struct'; + name: string; + members: Array<{ + name: string; + type: string; + }>; +}; + +type ENUM = { + type: 'enum'; + name: string; + variants: Array<{ + name: string; + type: string; + }>; +}; + +type INTERFACE = { + type: 'interface'; + name: string; + items: Array; +}; + +type IMPL = { + type: 'impl'; + name: string; + interface_name: string; +}; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +type EVENT_KIND = 'struct' | 'enum'; + +type EVENT_FIELD = { + name: string; + type: string; + kind: 'key' | 'data' | 'nested'; +}; diff --git a/src/types/api/rpcspec_0_6/errors.ts b/src/types/api/rpcspec_0_6/errors.ts new file mode 100644 index 000000000..b845230b9 --- /dev/null +++ b/src/types/api/rpcspec_0_6/errors.ts @@ -0,0 +1,151 @@ +export interface FAILED_TO_RECEIVE_TXN { + code: 1; + message: 'Failed to write transaction'; +} + +export interface NO_TRACE_AVAILABLE { + code: 10; + message: 'No trace available for transaction'; + data: { + status: 'RECEIVED' | 'REJECTED'; + }; +} + +export interface CONTRACT_NOT_FOUND { + code: 20; + message: 'Contract not found'; +} + +export interface INVALID_MESSAGE_SELECTOR { + code: 21; + message: 'Invalid message selector'; +} + +export interface INVALID_CALL_DATA { + code: 22; + message: 'Invalid call data'; +} + +export interface BLOCK_NOT_FOUND { + code: 24; + message: 'Block not found'; +} + +export interface INVALID_TXN_HASH { + code: 25; + message: 'Invalid transaction hash'; +} + +export interface INVALID_BLOCK_HASH { + code: 26; + message: 'Invalid block hash'; +} + +export interface INVALID_TXN_INDEX { + code: 27; + message: 'Invalid transaction index in a block'; +} + +export interface CLASS_HASH_NOT_FOUND { + code: 28; + message: 'Class hash not found'; +} + +export interface TXN_HASH_NOT_FOUND { + code: 29; + message: 'Transaction hash not found'; +} + +export interface PAGE_SIZE_TOO_BIG { + code: 31; + message: 'Requested page size is too big'; +} + +export interface NO_BLOCKS { + code: 32; + message: 'There are no blocks'; +} + +export interface INVALID_CONTINUATION_TOKEN { + code: 33; + message: 'The supplied continuation token is invalid or unknown'; +} + +export interface TOO_MANY_KEYS_IN_FILTER { + code: 34; + message: 'Too many keys provided in a filter'; +} + +export interface CONTRACT_ERROR { + code: 40; + message: 'Contract error'; + data: { + revert_error: string; + }; +} + +export interface CLASS_ALREADY_DECLARED { + code: 51; + message: 'Class already declared'; +} + +export interface INVALID_TRANSACTION_NONCE { + code: 52; + message: 'Invalid transaction nonce'; +} + +export interface INSUFFICIENT_MAX_FEE { + code: 53; + message: 'Max fee is smaller than the minimal transaction cost (validation plus fee transfer)'; +} + +export interface INSUFFICIENT_ACCOUNT_BALANCE { + code: 54; + message: "Account balance is smaller than the transaction's max_fee"; +} + +export interface VALIDATION_FAILURE { + code: 55; + message: 'Account validation failed'; +} + +export interface COMPILATION_FAILED { + code: 56; + message: 'Compilation failed'; +} + +export interface CONTRACT_CLASS_SIZE_IS_TOO_LARGE { + code: 57; + message: 'Contract class size it too large'; +} + +export interface NON_ACCOUNT { + code: 58; + message: 'Sender address in not an account contract'; +} + +export interface DUPLICATE_TX { + code: 59; + message: 'A transaction with the same hash already exists in the mempool'; +} + +export interface COMPILED_CLASS_HASH_MISMATCH { + code: 60; + message: 'the compiled class hash did not match the one supplied in the transaction'; +} + +export interface UNSUPPORTED_TX_VERSION { + code: 61; + message: 'the transaction version is not supported'; +} + +export interface UNSUPPORTED_CONTRACT_CLASS_VERSION { + code: 62; + message: 'the contract class version is not supported'; +} + +export interface UNEXPECTED_ERROR { + code: 63; + message: 'An unexpected error occurred'; + data: string; +} diff --git a/src/types/api/rpcspec_0_6/index.ts b/src/types/api/rpcspec_0_6/index.ts new file mode 100644 index 000000000..89087f0e2 --- /dev/null +++ b/src/types/api/rpcspec_0_6/index.ts @@ -0,0 +1,9 @@ +/** + * version 0.6.0-rc2 + */ + +export { Methods } from './methods'; +export { ABI } from './contract'; +export * as Errors from './errors'; +export * as SPEC from './components'; +export * from './nonspec'; diff --git a/src/types/api/rpcspec_0_6/methods.ts b/src/types/api/rpcspec_0_6/methods.ts new file mode 100644 index 000000000..577b56673 --- /dev/null +++ b/src/types/api/rpcspec_0_6/methods.ts @@ -0,0 +1,329 @@ +import { + ADDRESS, + BLOCK_ID, + BLOCK_NUMBER, + BROADCASTED_DECLARE_TXN, + BROADCASTED_DEPLOY_ACCOUNT_TXN, + BROADCASTED_INVOKE_TXN, + BROADCASTED_TXN, + CHAIN_ID, + EVENT_FILTER, + FELT, + FUNCTION_CALL, + MSG_FROM_L1, + PENDING_STATE_UPDATE, + RESULT_PAGE_REQUEST, + SIMULATION_FLAG, + STATE_UPDATE, + STORAGE_KEY, + TXN_HASH, +} from './components'; +import * as Errors from './errors'; +import { + BlockHashAndNumber, + BlockTransactionsTraces, + BlockWithTxHashes, + BlockWithTxs, + ContractClass, + DeclaredTransaction, + DeployedAccountTransaction, + Events, + FeeEstimate, + InvokedTransaction, + Nonce, + SimulateTransactionResponse, + Syncing, + TransactionReceipt, + TransactionStatus, + TransactionTrace, + TransactionWithHash, +} from './nonspec'; + +export type Methods = ReadMethods & WriteMethods & TraceMethods; + +type ReadMethods = { + // Returns the version of the Starknet JSON-RPC specification being used + starknet_specVersion: { + params: []; + result: string; + }; + + // Get block information with transaction hashes given the block id + starknet_getBlockWithTxHashes: { + params: { + block_id: BLOCK_ID; + }; + result: BlockWithTxHashes; + errors: Errors.BLOCK_NOT_FOUND; + }; + + // Get block information with full transactions given the block id + starknet_getBlockWithTxs: { + params: { + block_id: BLOCK_ID; + }; + result: BlockWithTxs; + errors: Errors.BLOCK_NOT_FOUND; + }; + + // Get the information about the result of executing the requested block + starknet_getStateUpdate: { + params: { + block_id: BLOCK_ID; + }; + result: STATE_UPDATE | PENDING_STATE_UPDATE; + errors: Errors.BLOCK_NOT_FOUND; + }; + + // Get the value of the storage at the given address and key + starknet_getStorageAt: { + params: { + contract_address: ADDRESS; + key: STORAGE_KEY; + block_id: BLOCK_ID; + }; + result: FELT; + errors: Errors.CONTRACT_NOT_FOUND | Errors.BLOCK_NOT_FOUND; + }; + + // Gets the transaction status (possibly reflecting that the tx is still in the mempool, or dropped from it) + starknet_getTransactionStatus: { + params: { + transaction_hash: TXN_HASH; + }; + result: TransactionStatus; + errors: Errors.TXN_HASH_NOT_FOUND; + }; + + // Get the details and status of a submitted transaction + starknet_getTransactionByHash: { + params: { + transaction_hash: TXN_HASH; + }; + result: TransactionWithHash; + errors: Errors.TXN_HASH_NOT_FOUND; + }; + + // Get the details of a transaction by a given block id and index + starknet_getTransactionByBlockIdAndIndex: { + params: { + block_id: BLOCK_ID; + index: number; + }; + result: TransactionWithHash; + errors: Errors.BLOCK_NOT_FOUND | Errors.INVALID_TXN_INDEX; + }; + + // Get the transaction receipt by the transaction hash + starknet_getTransactionReceipt: { + params: { + transaction_hash: TXN_HASH; + }; + result: TransactionReceipt; + errors: Errors.TXN_HASH_NOT_FOUND; + }; + + // Get the contract class definition in the given block associated with the given hash + starknet_getClass: { + params: { + block_id: BLOCK_ID; + class_hash: FELT; + }; + result: ContractClass; + errors: Errors.BLOCK_NOT_FOUND | Errors.CLASS_HASH_NOT_FOUND; + }; + + // Get the contract class hash in the given block for the contract deployed at the given address + starknet_getClassHashAt: { + params: { + block_id: BLOCK_ID; + contract_address: ADDRESS; + }; + result: FELT; + errors: Errors.BLOCK_NOT_FOUND | Errors.CONTRACT_NOT_FOUND; + }; + + // Get the contract class definition in the given block at the given address + starknet_getClassAt: { + params: { + block_id: BLOCK_ID; + contract_address: ADDRESS; + }; + result: ContractClass; + errors: Errors.BLOCK_NOT_FOUND | Errors.CONTRACT_NOT_FOUND; + }; + + // Get the number of transactions in a block given a block id + starknet_getBlockTransactionCount: { + params: { + block_id: BLOCK_ID; + }; + result: number; + errors: Errors.BLOCK_NOT_FOUND; + }; + + // Call a StarkNet function without creating a StarkNet transaction + starknet_call: { + params: { + request: FUNCTION_CALL; + block_id: BLOCK_ID; + }; + result: FELT[]; + errors: Errors.CONTRACT_NOT_FOUND | Errors.CONTRACT_ERROR | Errors.BLOCK_NOT_FOUND; + }; + + // Estimate the fee for StarkNet transactions + starknet_estimateFee: { + params: { + request: BROADCASTED_TXN[]; + block_id: BLOCK_ID; + }; + result: FeeEstimate[]; + errors: Errors.CONTRACT_NOT_FOUND | Errors.CONTRACT_ERROR | Errors.BLOCK_NOT_FOUND; + }; + + // Estimate the L2 fee of a message sent on L1 + starknet_estimateMessageFee: { + params: { + message: MSG_FROM_L1; + block_id: BLOCK_ID; + }; + result: FeeEstimate; + errors: Errors.CONTRACT_NOT_FOUND | Errors.CONTRACT_ERROR | Errors.BLOCK_NOT_FOUND; + }; + + // Get the most recent accepted block number + starknet_blockNumber: { + params: []; + result: BLOCK_NUMBER; + errors: Errors.NO_BLOCKS; + }; + + // Get the most recent accepted block hash and number + starknet_blockHashAndNumber: { + params: []; + result: BlockHashAndNumber; + errors: Errors.NO_BLOCKS; + }; + + // Return the currently configured StarkNet chain id + starknet_chainId: { + params: []; + result: CHAIN_ID; + }; + + // Returns an object about the sync status, or false if the node is not syncing + starknet_syncing: { + params: []; + result: Syncing; + }; + + // Returns all events matching the given filter + starknet_getEvents: { + params: { + filter: EVENT_FILTER & RESULT_PAGE_REQUEST; + }; + result: Events; + errors: + | Errors.PAGE_SIZE_TOO_BIG + | Errors.INVALID_CONTINUATION_TOKEN + | Errors.BLOCK_NOT_FOUND + | Errors.TOO_MANY_KEYS_IN_FILTER; + }; + + // Get the nonce associated with the given address in the given block + starknet_getNonce: { + params: { + block_id: BLOCK_ID; + contract_address: ADDRESS; + }; + result: Nonce; + errors: Errors.BLOCK_NOT_FOUND | Errors.CONTRACT_NOT_FOUND; + }; +}; + +type WriteMethods = { + // Submit a new transaction to be added to the chain + starknet_addInvokeTransaction: { + params: { + invoke_transaction: BROADCASTED_INVOKE_TXN; + }; + result: InvokedTransaction; + errors: + | Errors.INSUFFICIENT_ACCOUNT_BALANCE + | Errors.INSUFFICIENT_MAX_FEE + | Errors.INVALID_TRANSACTION_NONCE + | Errors.VALIDATION_FAILURE + | Errors.NON_ACCOUNT + | Errors.DUPLICATE_TX + | Errors.UNSUPPORTED_TX_VERSION + | Errors.UNEXPECTED_ERROR; + }; + + // Submit a new class declaration transaction + starknet_addDeclareTransaction: { + params: { + declare_transaction: BROADCASTED_DECLARE_TXN; + }; + result: DeclaredTransaction; + errors: + | Errors.CLASS_ALREADY_DECLARED + | Errors.COMPILATION_FAILED + | Errors.COMPILED_CLASS_HASH_MISMATCH + | Errors.INSUFFICIENT_ACCOUNT_BALANCE + | Errors.INSUFFICIENT_MAX_FEE + | Errors.INVALID_TRANSACTION_NONCE + | Errors.VALIDATION_FAILURE + | Errors.NON_ACCOUNT + | Errors.DUPLICATE_TX + | Errors.CONTRACT_CLASS_SIZE_IS_TOO_LARGE + | Errors.UNSUPPORTED_TX_VERSION + | Errors.UNSUPPORTED_CONTRACT_CLASS_VERSION + | Errors.UNEXPECTED_ERROR; + }; + + // Submit a new deploy account transaction + starknet_addDeployAccountTransaction: { + params: { + deploy_account_transaction: BROADCASTED_DEPLOY_ACCOUNT_TXN; + }; + result: DeployedAccountTransaction; + errors: + | Errors.INSUFFICIENT_ACCOUNT_BALANCE + | Errors.INSUFFICIENT_MAX_FEE + | Errors.INVALID_TRANSACTION_NONCE + | Errors.VALIDATION_FAILURE + | Errors.NON_ACCOUNT + | Errors.CLASS_HASH_NOT_FOUND + | Errors.DUPLICATE_TX + | Errors.UNSUPPORTED_TX_VERSION + | Errors.UNEXPECTED_ERROR; + }; +}; + +type TraceMethods = { + // For a given executed transaction, return the trace of its execution, including internal calls + starknet_traceTransaction: { + params: { transaction_hash: TXN_HASH }; + result: TransactionTrace; + errors: Errors.INVALID_TXN_HASH | Errors.NO_TRACE_AVAILABLE; + }; + + // Returns the execution traces of all transactions included in the given block + starknet_traceBlockTransactions: { + params: { block_id: BLOCK_ID }; + result: BlockTransactionsTraces; + errors: Errors.BLOCK_NOT_FOUND; + }; + + // Simulate a given sequence of transactions on the requested state, and generate the execution traces. If one of the transactions is reverted, raises CONTRACT_ERROR + starknet_simulateTransactions: { + params: { + block_id: BLOCK_ID; + transactions: Array; + simulation_flags: Array; + }; + result: SimulateTransactionResponse; + errors: Errors.CONTRACT_NOT_FOUND | Errors.CONTRACT_ERROR | Errors.BLOCK_NOT_FOUND; + }; +}; diff --git a/src/types/api/rpcspec_0_6/nonspec.ts b/src/types/api/rpcspec_0_6/nonspec.ts new file mode 100644 index 000000000..3e6b5ace4 --- /dev/null +++ b/src/types/api/rpcspec_0_6/nonspec.ts @@ -0,0 +1,134 @@ +/** + * Types that are not in spec but required for UX + */ +import { + ADDRESS, + BLOCK_HASH, + BLOCK_NUMBER, + BLOCK_WITH_TXS, + BLOCK_WITH_TX_HASHES, + BROADCASTED_TXN, + CHAIN_ID, + CONTRACT_CLASS, + CONTRACT_STORAGE_DIFF_ITEM, + DEPRECATED_CONTRACT_CLASS, + EVENTS_CHUNK, + EVENT_FILTER, + FEE_ESTIMATE, + FELT, + MSG_FROM_L1, + NONCE_UPDATE, + PENDING_BLOCK_WITH_TXS, + PENDING_BLOCK_WITH_TX_HASHES, + PENDING_STATE_UPDATE, + PENDING_TXN_RECEIPT, + REPLACED_CLASS, + RESULT_PAGE_REQUEST, + SIMULATION_FLAG, + STATE_UPDATE, + SYNC_STATUS, + TRANSACTION_TRACE, + TXN, + TXN_EXECUTION_STATUS, + TXN_HASH, + TXN_RECEIPT, + TXN_STATUS, +} from './components'; + +// METHOD RESPONSES +// response starknet_getClass +export type ContractClass = CONTRACT_CLASS | DEPRECATED_CONTRACT_CLASS; +// response starknet_simulateTransactions +export type SimulateTransactionResponse = { + transaction_trace: TRANSACTION_TRACE; + fee_estimation: FEE_ESTIMATE; +}[]; +// response starknet_estimateFee +export type FeeEstimate = FEE_ESTIMATE; +// response starknet_getTransactionByHash, starknet_getTransactionByBlockIdAndIndex +export type TransactionWithHash = TXN & { transaction_hash: TXN_HASH }; +// response starknet_blockHashAndNumber +export type BlockHashAndNumber = { block_hash: BLOCK_HASH; block_number: BLOCK_NUMBER }; +// response starknet_getBlockWithTxs +export type BlockWithTxs = BLOCK_WITH_TXS | PENDING_BLOCK_WITH_TXS; +// response starknet_getBlockWithTxHashes +export type BlockWithTxHashes = BLOCK_WITH_TX_HASHES | PENDING_BLOCK_WITH_TX_HASHES; +// response starknet_getStateUpdate +export type StateUpdate = STATE_UPDATE | PENDING_STATE_UPDATE; +// response starknet_traceBlockTransactions +export type BlockTransactionsTraces = { transaction_hash: FELT; trace_root: TRANSACTION_TRACE }[]; +// response starknet_syncing +export type Syncing = false | SYNC_STATUS; +// response starknet_getEvents +export type Events = EVENTS_CHUNK; +// response starknet_addInvokeTransaction +export type InvokedTransaction = { transaction_hash: TXN_HASH }; +// response starknet_addDeclareTransaction +export type DeclaredTransaction = { transaction_hash: TXN_HASH; class_hash: FELT }; +// response starknet_addDeployAccountTransaction +export type DeployedAccountTransaction = { transaction_hash: TXN_HASH; contract_address: FELT }; + +// Nice Components names +export type ContractAddress = ADDRESS; +export type Felt = FELT; +export type Nonce = FELT; +export type TransactionHash = TXN_HASH; +export type TransactionTrace = TRANSACTION_TRACE; +export type BlockHash = BLOCK_HASH; +export type TransactionReceipt = TXN_RECEIPT | PENDING_TXN_RECEIPT; +export type EventFilter = EVENT_FILTER & RESULT_PAGE_REQUEST; +export type SimulationFlags = Array; +export type L1Message = MSG_FROM_L1; +export type BaseTransaction = BROADCASTED_TXN; +export type ChainId = CHAIN_ID; +export type Transaction = TXN; +export type TransactionStatus = { + finality_status: TXN_STATUS; + execution_status?: TXN_EXECUTION_STATUS; +}; + +// Diff Than Seq +export type StorageDiffs = Array; +export type DeprecatedDeclaredClasses = Array; +export type NonceUpdates = NONCE_UPDATE[]; +export type ReplacedClasses = REPLACED_CLASS[]; + +// Enums Derived From Spec Types (require manual check for changes) +export enum ETransactionType { + DECLARE = 'DECLARE', + DEPLOY = 'DEPLOY', + DEPLOY_ACCOUNT = 'DEPLOY_ACCOUNT', + INVOKE = 'INVOKE', + L1_HANDLER = 'L1_HANDLER', +} + +export enum ESimulationFlag { + SKIP_VALIDATE = 'SKIP_VALIDATE', + SKIP_FEE_CHARGE = 'SKIP_FEE_CHARGE', +} + +export enum ETransactionStatus { + RECEIVED = 'RECEIVED', + REJECTED = 'REJECTED', + ACCEPTED_ON_L2 = 'ACCEPTED_ON_L2', + ACCEPTED_ON_L1 = 'ACCEPTED_ON_L1', +} + +export enum ETransactionFinalityStatus { + ACCEPTED_ON_L2 = 'ACCEPTED_ON_L2', + ACCEPTED_ON_L1 = 'ACCEPTED_ON_L1', +} +export enum ETransactionExecutionStatus { + SUCCEEDED = 'SUCCEEDED', + REVERTED = 'REVERTED', +} + +export enum EBlockTag { + LATEST = 'latest', + PENDING = 'pending', +} + +export enum EDataAvailabilityMode { + L1 = 'L1', + L2 = 'L2', +} diff --git a/src/utils/responseParser/rpc.ts b/src/utils/responseParser/rpc.ts index cf88211fa..3e5b942b5 100644 --- a/src/utils/responseParser/rpc.ts +++ b/src/utils/responseParser/rpc.ts @@ -18,7 +18,7 @@ import { FeeEstimate, SimulateTransactionResponse as RPCSimulateTransactionResponse, TransactionWithHash, -} from '../../types/api/rpcspec'; +} from '../../types/api/rpcspec_0_5'; import { toBigInt } from '../num'; import { estimatedFeeToMaxFee } from '../stark'; import { ResponseParser } from '.';