From 4cbcac52e33d51b4cae67c6bf56954111f640b1d Mon Sep 17 00:00:00 2001 From: PhilippeR26 Date: Wed, 30 Jul 2025 10:37:45 +0200 Subject: [PATCH 1/3] feat: fastExecute --- src/account/default.ts | 37 +++++++++++++++++- src/account/types/index.type.ts | 6 +++ src/channel/rpc_0_9_0.ts | 67 +++++++++++++++++++++++++++++++++ src/provider/rpc.ts | 25 ++++++++++++ src/types/lib/index.ts | 5 +++ 5 files changed, 139 insertions(+), 1 deletion(-) diff --git a/src/account/default.ts b/src/account/default.ts index 98a4ea001..2794968a2 100644 --- a/src/account/default.ts +++ b/src/account/default.ts @@ -8,7 +8,7 @@ import { } from '../global/constants'; import { logger } from '../global/logger'; import { LibraryError, Provider } from '../provider'; -import { ETransactionVersion, ETransactionVersion3 } from '../provider/types/spec.type'; +import { BlockTag, ETransactionVersion, ETransactionVersion3 } from '../provider/types/spec.type'; import { Signer, type SignerInterface } from '../signer'; import { // Runtime values @@ -58,6 +58,8 @@ import type { UniversalDetails, UserTransaction, waitForTransactionOptions, + fastWaitForTransactionOptions, + fastExecuteResponse, } from '../types'; import { ETransactionType } from '../types/api'; import { CallData } from '../utils/calldata'; @@ -331,6 +333,39 @@ export class Account extends Provider implements AccountInterface { ); } + /** + * Execute one or multiple calls through the account contract, + * responding as soon a new transaction is possible with the same account. + * Useful for gaming usage. + * @param transactions + * @param transactionsDetail + * @param waitDetail + * @returns + */ + public async fastExecute( + transactions: AllowArray, + transactionsDetail: UniversalDetails = {}, + waitDetail: fastWaitForTransactionOptions = {} + ): Promise { + assert( + this.channel.blockIdentifier === BlockTag.PRE_CONFIRMED, + 'Provider needs to be initialized with `pre_confirmed` blockIdentifier option.' + ); + const initNonce = BigInt( + transactionsDetail.nonce ?? + (await this.getNonceForAddress(this.address, BlockTag.PRE_CONFIRMED)) + ); + const details = { ...transactionsDetail, nonce: initNonce }; + const resultTx: InvokeFunctionResponse = await this.execute(transactions, details); + const resultWait = await this.fastWaitForTransaction( + resultTx.transaction_hash, + this.address, + initNonce, + waitDetail + ); + return { txResult: resultTx, isReady: resultWait } as fastExecuteResponse; + } + /** * First check if contract is already declared, if not declare it * If contract already declared returned transaction_hash is ''. diff --git a/src/account/types/index.type.ts b/src/account/types/index.type.ts index 4726db59e..f7e039207 100644 --- a/src/account/types/index.type.ts +++ b/src/account/types/index.type.ts @@ -8,6 +8,7 @@ import type { import type { DeclareTransactionReceiptResponse, EstimateFeeResponseOverhead, + InvokeFunctionResponse, ProviderOptions, } from '../../provider/types/index.type'; import type { ResourceBoundsBN } from '../../provider/types/spec.type'; @@ -110,3 +111,8 @@ export type StarkProfile = { github?: string; proofOfPersonhood?: boolean; }; + +export type fastExecuteResponse = { + txResult: InvokeFunctionResponse; + isReady: boolean; +}; diff --git a/src/channel/rpc_0_9_0.ts b/src/channel/rpc_0_9_0.ts index 9046d4453..52ae68ddc 100644 --- a/src/channel/rpc_0_9_0.ts +++ b/src/channel/rpc_0_9_0.ts @@ -21,6 +21,7 @@ import { RPC_ERROR, RpcProviderOptions, waitForTransactionOptions, + type fastWaitForTransactionOptions, } from '../types'; import assert from '../utils/assert'; import { ETransactionType, JRPC, RPCSPEC09 as RPC } from '../types/api'; @@ -477,6 +478,72 @@ export class RpcChannel { return txReceipt as RPC.TXN_RECEIPT; } + public async fastWaitForTransaction( + txHash: BigNumberish, + address: string, + initNonce: bigint, + options?: fastWaitForTransactionOptions + ): Promise { + let retries = options?.retries ?? this.retries; + const retryInterval = options?.retryInterval ?? 500; // 0.5s + let isErrorState = false; + const errorStates: string[] = [RPC.ETransactionExecutionStatus.REVERTED]; + const successStates: string[] = [ + RPC.ETransactionFinalityStatus.ACCEPTED_ON_L2, + RPC.ETransactionFinalityStatus.ACCEPTED_ON_L1, + RPC.ETransactionFinalityStatus.PRE_CONFIRMED, + ]; + let txStatus: RPC.TransactionStatus; + const start = new Date().getTime(); + while (retries > 0) { + // eslint-disable-next-line no-await-in-loop + await wait(retryInterval); + try { + // eslint-disable-next-line no-await-in-loop + txStatus = await this.getTransactionStatus(txHash); + console.log( + retries, + txStatus, + // eslint-disable-next-line no-await-in-loop + BigInt(await this.getNonceForAddress(address, BlockTag.PRE_CONFIRMED)), + (new Date().getTime() - start) / 1000 + ); + const executionStatus = txStatus.execution_status ?? ''; + const finalityStatus = txStatus.finality_status; + if (errorStates.includes(executionStatus)) { + const message = `${executionStatus}: ${finalityStatus}`; + const error = new Error(message) as Error & { response: RPC.TransactionStatus }; + error.response = txStatus; + isErrorState = true; + throw error; + } else if (successStates.includes(finalityStatus)) { + let currentNonce = initNonce; + while (currentNonce === initNonce && retries > 0) { + // eslint-disable-next-line no-await-in-loop + currentNonce = BigInt(await this.getNonceForAddress(address, BlockTag.PRE_CONFIRMED)); + console.log( + retries, + 'waiting new nonce', + initNonce, + (new Date().getTime() - start) / 1000 + ); + if (currentNonce !== initNonce) return true; + // eslint-disable-next-line no-await-in-loop + await wait(retryInterval); + retries -= 1; + } + return false; + } + } catch (error) { + if (error instanceof Error && isErrorState) { + throw error; + } + } + retries -= 1; + } + return false; + } + public getStorageAt( contractAddress: BigNumberish, key: BigNumberish, diff --git a/src/provider/rpc.ts b/src/provider/rpc.ts index 23ca86ab1..02ff11282 100644 --- a/src/provider/rpc.ts +++ b/src/provider/rpc.ts @@ -14,6 +14,7 @@ import { ContractVersion, DeclareContractTransaction, DeployAccountContractTransaction, + type fastWaitForTransactionOptions, type GasPrices, GetBlockResponse, getContractVersionOptions, @@ -323,6 +324,30 @@ export class RpcProvider implements ProviderInterface { return new ReceiptTx(receiptWoHelper) as GetTransactionReceiptResponse; } + /** + * + * @param txHash + * @param options + * @returns + */ + public async fastWaitForTransaction( + txHash: BigNumberish, + address: string, + initNonce: bigint, + options?: fastWaitForTransactionOptions + ): Promise { + if (this.channel instanceof RPC09.RpcChannel) { + const isSuccess = await this.channel.fastWaitForTransaction( + txHash, + address, + initNonce, + options + ); + return isSuccess; + } + throw new Error('Unsupported channel type'); + } + public async getStorageAt( contractAddress: BigNumberish, key: BigNumberish, diff --git a/src/types/lib/index.ts b/src/types/lib/index.ts index 9303dfe45..6d47a4ba7 100644 --- a/src/types/lib/index.ts +++ b/src/types/lib/index.ts @@ -307,6 +307,11 @@ export type waitForTransactionOptions = { errorStates?: Array; }; +export type fastWaitForTransactionOptions = { + retries?: number; + retryInterval?: number; +}; + export type getSimulateTransactionOptions = { blockIdentifier?: BlockIdentifier; skipValidate?: boolean; From 488e991f3f5945ecef45ae69e16e7cf2f6021cb8 Mon Sep 17 00:00:00 2001 From: PhilippeR26 Date: Wed, 30 Jul 2025 16:21:22 +0200 Subject: [PATCH 2/3] docs: jscode & guide --- src/account/default.ts | 28 ++++++++++++--- src/channel/rpc_0_9_0.ts | 2 +- src/provider/rpc.ts | 13 ++++--- www/docs/guides/contracts/interact.md | 51 ++++++++++++++++++++++++++- 4 files changed, 83 insertions(+), 11 deletions(-) diff --git a/src/account/default.ts b/src/account/default.ts index 2794968a2..3a16615ef 100644 --- a/src/account/default.ts +++ b/src/account/default.ts @@ -90,6 +90,7 @@ import { assertPaymasterTransactionSafety } from '../utils/paymaster'; import assert from '../utils/assert'; import { defaultDeployer, Deployer } from '../deployer'; import type { TipType } from '../provider/modules/tip'; +import { RPC09 } from '../channel'; export class Account extends Provider implements AccountInterface { public signer: SignerInterface; @@ -335,18 +336,35 @@ export class Account extends Provider implements AccountInterface { /** * Execute one or multiple calls through the account contract, - * responding as soon a new transaction is possible with the same account. + * responding as soon as a new transaction is possible with the same account. * Useful for gaming usage. - * @param transactions - * @param transactionsDetail - * @param waitDetail - * @returns + * - This method requires the provider to be initialized with `pre_confirmed` blockIdentifier option. + * - Rpc 0.9 minimum. + * - In a normal myAccount.execute() call, followed by myProvider.waitForTransaction(), you have an immediate access to the events and to the transaction report. Here, we are processing consecutive transactions faster, but events & transaction reports are not available immediately. + * - As a consequence of the previous point, do not use contract/account deployment with this method. + * @param {AllowArray} transactions - Single call or array of calls to execute + * @param {UniversalDetails} [transactionsDetail] - Transaction execution options + * @param {fastWaitForTransactionOptions} [waitDetail={retries: 50, retryInterval: 500}] - options to scan the network for the next possible transaction. `retries` is the number of times to retry, `retryInterval` is the time in ms between retries. + * @returns {Promise} Response containing the transaction result and status for the next transaction. If `isReady` is true, you can execute the next transaction. If false, timeout has been reached before the next transaction was possible. + * @example + * ```typescript + * const myProvider = new RpcProvider({ nodeUrl: url, blockIdentifier: BlockTag.PRE_CONFIRMED }); + * const myAccount = new Account({ provider: myProvider, address: accountAddress0, signer: privateKey0 }); + * const resp = await myAccount.fastExecute( + * call, { tip: recommendedTip}, + * { retries: 30, retryInterval: 500 }); + * // if resp.isReady is true, you can launch immediately a new tx. + * ``` */ public async fastExecute( transactions: AllowArray, transactionsDetail: UniversalDetails = {}, waitDetail: fastWaitForTransactionOptions = {} ): Promise { + assert( + this.channel instanceof RPC09.RpcChannel, + 'Wrong Rpc version in Provider. At least Rpc v0.9 required.' + ); assert( this.channel.blockIdentifier === BlockTag.PRE_CONFIRMED, 'Provider needs to be initialized with `pre_confirmed` blockIdentifier option.' diff --git a/src/channel/rpc_0_9_0.ts b/src/channel/rpc_0_9_0.ts index 52ae68ddc..40debaeea 100644 --- a/src/channel/rpc_0_9_0.ts +++ b/src/channel/rpc_0_9_0.ts @@ -484,7 +484,7 @@ export class RpcChannel { initNonce: bigint, options?: fastWaitForTransactionOptions ): Promise { - let retries = options?.retries ?? this.retries; + let retries = options?.retries ?? 50; const retryInterval = options?.retryInterval ?? 500; // 0.5s let isErrorState = false; const errorStates: string[] = [RPC.ETransactionExecutionStatus.REVERTED]; diff --git a/src/provider/rpc.ts b/src/provider/rpc.ts index 02ff11282..7210d9eb9 100644 --- a/src/provider/rpc.ts +++ b/src/provider/rpc.ts @@ -325,10 +325,15 @@ export class RpcProvider implements ProviderInterface { } /** - * - * @param txHash - * @param options - * @returns + * Wait up until a new transaction is possible with same the account. + * This method is fast, but Events and transaction report are not yet + * available. Useful for gaming activity. + * - only rpc 0.9 and onwards. + * @param {BigNumberish} txHash - transaction hash + * @param {string} address - address of the account + * @param {bigint} initNonce - initial nonce of the account (before the transaction). + * @param {fastWaitForTransactionOptions} [options={retries: 50, retryInterval: 500}] - options to scan the network for the next possible transaction. `retries` is the number of times to retry. + * @returns {Promise} Returns true if the next transaction is possible, false if the timeout has been reached. */ public async fastWaitForTransaction( txHash: BigNumberish, diff --git a/www/docs/guides/contracts/interact.md b/www/docs/guides/contracts/interact.md index efd167a2b..1aac71b85 100644 --- a/www/docs/guides/contracts/interact.md +++ b/www/docs/guides/contracts/interact.md @@ -10,7 +10,9 @@ Once your contract is connected (see [Contract Instance guide](./connect_contrac - **Write operations**: Paid - modify contract state with STRK fees :::info + Ensure your account has sufficient STRK for transaction fees (20 STRK is a good start). + ::: ![](./pictures/contract-interaction.svg) @@ -46,7 +48,8 @@ console.log('User balance:', userBalance); - Cairo 1 contracts return values directly as `bigint` - Cairo 0 contracts return objects with named properties (e.g., `result.res`) - ::: + +::: ## ✍️ Writing to Contract State @@ -68,13 +71,17 @@ await myProvider.waitForTransaction(tx2.transaction_hash); ``` :::tip + Use `Contract.populate()` to prepare call data for complex parameters or multicalls. + ::: :::info + **v8 Note**: Only V3 transactions with STRK fees are supported in Starknet.js v8. ETH fee transactions (V1/V2) have been removed with Starknet 0.14. All transactions now use V3 transactions with STRK fees by default. + ::: ## ✍️ Send a transaction, paying fees with ETH or any supported Token @@ -116,6 +123,48 @@ await myProvider.waitForTransaction(result.transaction_hash); For detailed multicall examples, see the [Multicall guide](./multiCall.md). +## Fast consecutive transactions + +In some cases, it's important to be able to process as fast as possible consecutive transactions. Gaming is fond of this feature. +A normal transaction (with `myProvider.waitForTransaction(txH)`) needs more than 10 seconds. To be able to process a transaction each 2-3 seconds, use: + +```ts +const myProvider = new RpcProvider({ + nodeUrl: url, + specVersion: '0.9.0', + blockIdentifier: BlockTag.PRE_CONFIRMED, +}); +const myAccount = new Account({ + provider: myProvider, + address: accountAddress0, + signer: privateKey0, +}); +const call1 = gameContract.populate('decrease_qty_weapons', [5]); +const tipStats = await myProvider.getEstimateTip(); +const resp = await myAccount.fastExecute( + call1, + { tip: recommendedTip }, + { retries: 30, retryInterval: 500 } +); +if (resp.isReady) { + const call2 = gameContract.populate('increase_qty_weapons', [10]); + const resp = await myAccount.fastExecute( + call2, + { tip: tipStats.recommendedTip }, + { retries: 30, retryInterval: 500 } + ); +} +``` + +:::warning Warning + +- This method requires the provider to be initialized with `pre_confirmed` blockIdentifier option. +- Rpc 0.9 minimum. +- In a normal `myAccount.execute()` call, followed by `myProvider.waitForTransaction()`, you have an immediate access to the events and to the transaction report. Here, we are processing consecutive transactions faster ; then events & transaction reports are not available immediately. +- As a consequence of the previous point, do not use contract/account deployment with this method. Use the normal way. + +::: + ## Other existing methods Some other useful methods to interact with Starknet: From 2d962d3ad04b15140bf126922b4d6993d69e9de2 Mon Sep 17 00:00:00 2001 From: PhilippeR26 Date: Fri, 1 Aug 2025 14:28:44 +0200 Subject: [PATCH 3/3] test: add tests --- __tests__/account.test.ts | 60 ++++++++++++++++++++++ __tests__/rpcProvider.test.ts | 71 +++++++++++++++++++++++++++ src/channel/rpc_0_9_0.ts | 68 +++++++++++-------------- src/provider/rpc.ts | 8 +-- www/docs/guides/contracts/interact.md | 5 +- 5 files changed, 167 insertions(+), 45 deletions(-) diff --git a/__tests__/account.test.ts b/__tests__/account.test.ts index 33c531d3d..ebd5286d3 100644 --- a/__tests__/account.test.ts +++ b/__tests__/account.test.ts @@ -16,6 +16,9 @@ import { type InvokeTransactionReceiptResponse, Deployer, RPC, + RpcProvider, + BlockTag, + type Call, } from '../src'; import { C1v2ClassHash, @@ -23,6 +26,7 @@ import { describeIfDevnet, describeIfNotDevnet, erc20ClassHash, + getTestProvider, } from './config/fixtures'; import { createTestProvider, @@ -30,6 +34,7 @@ import { devnetFeeTokenAddress, adaptAccountIfDevnet, TEST_TX_VERSION, + STRKtokenAddress, } from './config/fixturesInit'; import { initializeMatcher } from './config/schema'; @@ -385,6 +390,61 @@ describe('deploy and test Account', () => { expect(after - before).toStrictEqual(57n); }); + describe('fastExecute()', () => { + test('Only Rpc0.9', async () => { + const provider08 = new RpcProvider({ + nodeUrl: 'dummy', + blockIdentifier: BlockTag.PRE_CONFIRMED, + specVersion: '0.8.1', + }); + const testAccount = new Account({ + provider: provider08, + address: '0x123', + signer: '0x456', + }); + const myCall: Call = { contractAddress: '0x036', entrypoint: 'withdraw', calldata: [] }; + await expect(testAccount.fastExecute(myCall)).rejects.toThrow( + 'Wrong Rpc version in Provider. At least Rpc v0.9 required.' + ); + }); + + test('Only provider with PRE_CONFIRMED blockIdentifier', async () => { + const providerLatest = new RpcProvider({ + nodeUrl: 'dummy', + blockIdentifier: BlockTag.LATEST, + specVersion: '0.9.0', + }); + const testAccount = new Account({ + provider: providerLatest, + address: '0x123', + signer: '0x456', + }); + const myCall: Call = { contractAddress: '0x036', entrypoint: 'withdraw', calldata: [] }; + await expect(testAccount.fastExecute(myCall)).rejects.toThrow( + 'Provider needs to be initialized with `pre_confirmed` blockIdentifier option.' + ); + }); + + test('fast consecutive txs', async () => { + const testProvider = getTestProvider(false, { + blockIdentifier: BlockTag.PRE_CONFIRMED, + }); + const testAccount = getTestAccount(testProvider); + const myCall: Call = { + contractAddress: STRKtokenAddress, + entrypoint: 'transfer', + calldata: [testAccount.address, cairo.uint256(100)], + }; + const tx1 = await testAccount.fastExecute(myCall); + expect(tx1.isReady).toBe(true); + expect(tx1.txResult.transaction_hash).toMatch(/^0x/); + const tx2 = await testAccount.fastExecute(myCall); + await provider.waitForTransaction(tx2.txResult.transaction_hash); // to be sure to have the right nonce in `provider`, that is set with BlockTag.LATEST (otherwise next tests will fail) + expect(tx2.isReady).toBe(true); + expect(tx2.txResult.transaction_hash).toMatch(/^0x/); + }); + }); + describe('EIP712 verification', () => { // currently only in Starknet-Devnet, because can fail in Sepolia. test('sign and verify EIP712 message fail', async () => { diff --git a/__tests__/rpcProvider.test.ts b/__tests__/rpcProvider.test.ts index f8f3b67d7..bea6007b3 100644 --- a/__tests__/rpcProvider.test.ts +++ b/__tests__/rpcProvider.test.ts @@ -324,6 +324,77 @@ describeIfRpc('RPCProvider', () => { }); }); + describe('fastWaitForTransaction()', () => { + test('timeout due to low tip', async () => { + const spyProvider = jest + .spyOn(rpcProvider.channel, 'getTransactionStatus') + .mockImplementation(async () => { + return { finality_status: 'RECEIVED' }; + }); + const resp = await rpcProvider.fastWaitForTransaction('0x123', '0x456', 10, { + retries: 2, + retryInterval: 100, + }); + spyProvider.mockRestore(); + expect(resp).toBe(false); + }); + + test('timeout due to missing new nonce', async () => { + const spyProvider = jest + .spyOn(rpcProvider.channel, 'getTransactionStatus') + .mockImplementation(async () => { + return { finality_status: 'PRE_CONFIRMED', execution_status: 'SUCCEEDED' }; + }); + const spyChannel = jest + .spyOn(rpcProvider.channel, 'getNonceForAddress') + .mockImplementation(async () => { + return '0x8'; + }); + const resp = await rpcProvider.fastWaitForTransaction('0x123', '0x456', 8, { + retries: 2, + retryInterval: 100, + }); + spyProvider.mockRestore(); + spyChannel.mockRestore(); + expect(resp).toBe(false); + }); + + test('transaction reverted', async () => { + const spyProvider = jest + .spyOn(rpcProvider.channel, 'getTransactionStatus') + .mockImplementation(async () => { + return { finality_status: 'PRE_CONFIRMED', execution_status: 'REVERTED' }; + }); + await expect( + rpcProvider.fastWaitForTransaction('0x123', '0x456', 10, { + retries: 2, + retryInterval: 100, + }) + ).rejects.toThrow('REVERTED: PRE_CONFIRMED'); + spyProvider.mockRestore(); + }); + + test('Normal behavior', async () => { + const spyProvider = jest + .spyOn(rpcProvider.channel, 'getTransactionStatus') + .mockImplementation(async () => { + return { finality_status: 'ACCEPTED_ON_L2', execution_status: 'SUCCEEDED' }; + }); + const spyChannel = jest + .spyOn(rpcProvider.channel, 'getNonceForAddress') + .mockImplementation(async () => { + return '0x9'; + }); + const resp = await rpcProvider.fastWaitForTransaction('0x123', '0x456', 8, { + retries: 2, + retryInterval: 100, + }); + spyProvider.mockRestore(); + spyChannel.mockRestore(); + expect(resp).toBe(true); + }); + }); + describe('RPC methods', () => { let latestBlock: Block; diff --git a/src/channel/rpc_0_9_0.ts b/src/channel/rpc_0_9_0.ts index 40debaeea..94e896260 100644 --- a/src/channel/rpc_0_9_0.ts +++ b/src/channel/rpc_0_9_0.ts @@ -481,12 +481,12 @@ export class RpcChannel { public async fastWaitForTransaction( txHash: BigNumberish, address: string, - initNonce: bigint, + initNonceBN: BigNumberish, options?: fastWaitForTransactionOptions ): Promise { + const initNonce = BigInt(initNonceBN); let retries = options?.retries ?? 50; const retryInterval = options?.retryInterval ?? 500; // 0.5s - let isErrorState = false; const errorStates: string[] = [RPC.ETransactionExecutionStatus.REVERTED]; const successStates: string[] = [ RPC.ETransactionFinalityStatus.ACCEPTED_ON_L2, @@ -498,47 +498,35 @@ export class RpcChannel { while (retries > 0) { // eslint-disable-next-line no-await-in-loop await wait(retryInterval); - try { - // eslint-disable-next-line no-await-in-loop - txStatus = await this.getTransactionStatus(txHash); - console.log( - retries, - txStatus, + + // eslint-disable-next-line no-await-in-loop + txStatus = await this.getTransactionStatus(txHash); + logger.info( + `${retries} ${JSON.stringify(txStatus)} ${(new Date().getTime() - start) / 1000}s.` + ); + const executionStatus = txStatus.execution_status ?? ''; + const finalityStatus = txStatus.finality_status; + if (errorStates.includes(executionStatus)) { + const message = `${executionStatus}: ${finalityStatus}`; + const error = new Error(message) as Error & { response: RPC.TransactionStatus }; + error.response = txStatus; + throw error; + } else if (successStates.includes(finalityStatus)) { + let currentNonce = initNonce; + while (currentNonce === initNonce && retries > 0) { // eslint-disable-next-line no-await-in-loop - BigInt(await this.getNonceForAddress(address, BlockTag.PRE_CONFIRMED)), - (new Date().getTime() - start) / 1000 - ); - const executionStatus = txStatus.execution_status ?? ''; - const finalityStatus = txStatus.finality_status; - if (errorStates.includes(executionStatus)) { - const message = `${executionStatus}: ${finalityStatus}`; - const error = new Error(message) as Error & { response: RPC.TransactionStatus }; - error.response = txStatus; - isErrorState = true; - throw error; - } else if (successStates.includes(finalityStatus)) { - let currentNonce = initNonce; - while (currentNonce === initNonce && retries > 0) { - // eslint-disable-next-line no-await-in-loop - currentNonce = BigInt(await this.getNonceForAddress(address, BlockTag.PRE_CONFIRMED)); - console.log( - retries, - 'waiting new nonce', - initNonce, - (new Date().getTime() - start) / 1000 - ); - if (currentNonce !== initNonce) return true; - // eslint-disable-next-line no-await-in-loop - await wait(retryInterval); - retries -= 1; - } - return false; - } - } catch (error) { - if (error instanceof Error && isErrorState) { - throw error; + currentNonce = BigInt(await this.getNonceForAddress(address, BlockTag.PRE_CONFIRMED)); + logger.info( + `${retries} Checking new nonce ${currentNonce} ${(new Date().getTime() - start) / 1000}s.` + ); + if (currentNonce !== initNonce) return true; + // eslint-disable-next-line no-await-in-loop + await wait(retryInterval); + retries -= 1; } + return false; } + retries -= 1; } return false; diff --git a/src/provider/rpc.ts b/src/provider/rpc.ts index 7210d9eb9..f7c585413 100644 --- a/src/provider/rpc.ts +++ b/src/provider/rpc.ts @@ -331,14 +331,16 @@ export class RpcProvider implements ProviderInterface { * - only rpc 0.9 and onwards. * @param {BigNumberish} txHash - transaction hash * @param {string} address - address of the account - * @param {bigint} initNonce - initial nonce of the account (before the transaction). + * @param {BigNumberish} initNonce - initial nonce of the account (before the transaction). * @param {fastWaitForTransactionOptions} [options={retries: 50, retryInterval: 500}] - options to scan the network for the next possible transaction. `retries` is the number of times to retry. - * @returns {Promise} Returns true if the next transaction is possible, false if the timeout has been reached. + * @returns {Promise} Returns true if the next transaction is possible, + * false if the timeout has been reached, + * throw an error in case of provider communication. */ public async fastWaitForTransaction( txHash: BigNumberish, address: string, - initNonce: bigint, + initNonce: BigNumberish, options?: fastWaitForTransactionOptions ): Promise { if (this.channel instanceof RPC09.RpcChannel) { diff --git a/www/docs/guides/contracts/interact.md b/www/docs/guides/contracts/interact.md index 1aac71b85..ce0242ccb 100644 --- a/www/docs/guides/contracts/interact.md +++ b/www/docs/guides/contracts/interact.md @@ -139,7 +139,7 @@ const myAccount = new Account({ address: accountAddress0, signer: privateKey0, }); -const call1 = gameContract.populate('decrease_qty_weapons', [5]); +const call1 = gameContract.populate('decrease_qty_weapons', { qty: 5 }); const tipStats = await myProvider.getEstimateTip(); const resp = await myAccount.fastExecute( call1, @@ -147,7 +147,7 @@ const resp = await myAccount.fastExecute( { retries: 30, retryInterval: 500 } ); if (resp.isReady) { - const call2 = gameContract.populate('increase_qty_weapons', [10]); + const call2 = gameContract.populate('increase_qty_weapons', { qty: 10 }); const resp = await myAccount.fastExecute( call2, { tip: tipStats.recommendedTip }, @@ -162,6 +162,7 @@ if (resp.isReady) { - Rpc 0.9 minimum. - In a normal `myAccount.execute()` call, followed by `myProvider.waitForTransaction()`, you have an immediate access to the events and to the transaction report. Here, we are processing consecutive transactions faster ; then events & transaction reports are not available immediately. - As a consequence of the previous point, do not use contract/account deployment with this method. Use the normal way. +- `fastExecute()` is generating a significant amount of communication with the node. To use sparingly, especially with a public node. :::