Skip to content

Commit

Permalink
feat: implement sendAndAwaitStatus subscription (#3541)
Browse files Browse the repository at this point in the history
* chore: rebuild

* chore: rebuild

* feat: implement `sendTransactionAndAwaitStatus`

* chore: alter snippet region

* chore: changeset

* chore: add spellcheck work

* chore: add spellcheck work

* chore: changeset

* Update apps/docs/src/guide/transactions/snippets/transaction-subscriptions/contract-call.ts

* Update apps/docs/src/guide/transactions/snippets/transaction-subscriptions/transaction-request.ts

* Update apps/docs/src/guide/transactions/transaction-subscriptions.md

* Update apps/docs/src/guide/transactions/transaction-subscriptions.md

* Update packages/account/src/providers/provider.ts

* docs: improvement

Co-authored-by: Nedim Salkić <[email protected]>

* test: remove node args

* feat: use waitForResult

* chore: remove test on;y

* docs: update docs and tests

* chore: update spellcheck

* chore: lint

* chore: rebuild

* chore: remove redundant method

* chore: code style

Co-authored-by: Nedim Salkić <[email protected]>

* chore: update docs

* Revert "chore: code style"

This reverts commit fc5ce61.

* chore: cleaner doc

Co-authored-by: Nedim Salkić <[email protected]>

* chor: update test

* chore: update and fix tests

* chore: lint

---------

Co-authored-by: Nedim Salkić <[email protected]>
Co-authored-by: Anderson Arboleya <[email protected]>
Co-authored-by: Peter Smith <[email protected]>
  • Loading branch information
4 people authored Jan 7, 2025
1 parent 75fae34 commit 40d9960
Show file tree
Hide file tree
Showing 8 changed files with 170 additions and 6 deletions.
5 changes: 5 additions & 0 deletions .changeset/orange-cherries-clean.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@fuel-ts/account": patch
---

feat: implement `sendAndAwaitStatus` subscription
12 changes: 8 additions & 4 deletions apps/docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -336,10 +336,6 @@ export default defineConfig({
text: 'Transaction Request',
link: '/guide/transactions/transaction-request',
},
{
text: 'Transaction Response',
link: '/guide/transactions/transaction-response',
},
{
text: 'Transaction Parameters',
link: '/guide/transactions/transaction-parameters',
Expand All @@ -348,6 +344,14 @@ export default defineConfig({
text: 'Transaction Policies',
link: '/guide/transactions/transaction-policies',
},
{
text: 'Transaction Response',
link: '/guide/transactions/transaction-response',
},
{
text: 'Transaction Subscriptions',
link: '/guide/transactions/transaction-subscriptions',
},
],
},
{
Expand Down
5 changes: 4 additions & 1 deletion apps/docs/spell-check-custom-words.txt
Original file line number Diff line number Diff line change
Expand Up @@ -344,4 +344,7 @@ Workspaces
WSL
XOR
XORs
YAML
YAML
TransactionRequest
TransactionResponse
frictionless
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Provider, Wallet } from 'fuels';

import { LOCAL_NETWORK_URL, WALLET_PVT_KEY } from '../../../../env';
import { CounterFactory } from '../../../../typegend';

const provider = new Provider(LOCAL_NETWORK_URL);
const wallet = Wallet.fromPrivateKey(WALLET_PVT_KEY, provider);

const deploy = await CounterFactory.deploy(wallet);
const { contract } = await deploy.waitForResult();

// #region main
// Create a new transaction request from a contract call
const txRequest = await contract.functions
.increment_count(1)
.getTransactionRequest();

// Fund the transaction
await txRequest.autoCost(wallet);

// Sign the transaction
const txSignature = await wallet.signTransaction(txRequest);
txRequest.updateWitnessByOwner(wallet.address, txSignature);

// Send the transaction and await it's result via the opened subscription
const result = await provider.sendTransactionAndAwaitStatus(txRequest);
// #endregion main

console.log('transactionId', result.id);
console.log('status', result.status);
console.log('receipts', result.receipts);
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Provider, ScriptTransactionRequest, Wallet } from 'fuels';

import { LOCAL_NETWORK_URL, WALLET_PVT_KEY } from '../../../../env';

const provider = new Provider(LOCAL_NETWORK_URL);
const wallet = Wallet.fromPrivateKey(WALLET_PVT_KEY, provider);
const recipient = Wallet.generate();

// #region main
// Create a new transaction request
const txRequest = new ScriptTransactionRequest();
txRequest.addCoinOutput(
recipient.address,
1_000,
await provider.getBaseAssetId()
);

// Fund the transaction
await txRequest.autoCost(wallet);

// Sign the transaction
const txSignature = await wallet.signTransaction(txRequest);
txRequest.updateWitnessByOwner(wallet.address, txSignature);

// Send the transaction and await it's result via the opened subscription
const result = await provider.sendTransactionAndAwaitStatus(txRequest);
// #endregion main

console.log('transactionId', result.id);
console.log('status', result.status);
console.log('receipts', result.receipts);
16 changes: 16 additions & 0 deletions apps/docs/src/guide/transactions/transaction-subscriptions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Transaction Subscriptions

When submitting transactions via the SDK, usually this is done by calling a method in a [script](../scripts/running-scripts.md) or [contract](../contracts/methods.md#call), or sending a transaction via a [predicate](../predicates/methods.md#sendtransaction) or [wallet](../wallets/index.md). These methods submit the transaction and then return a [TransactionResponse](./transaction-response.md) that allows you to view the result of a transaction at your convenience, leaving the rest of your app processing unblocked.

However, if you want to send a transaction and wait until it's processed, for convenience the SDK also exposes the `sendTransactionAndAwaitStatus` available on a [Provider](../provider/index.md) which behaves the same as `sendTransaction` but waits until the transaction is processed by the node and then returns the transaction result.

This functionality can be used like so:

<<< @./snippets/transaction-subscriptions/transaction-request.ts#main{ts:line-numbers}

Or used with a contract call like so:

<<< @./snippets/transaction-subscriptions/contract-call.ts#main{ts:line-numbers}

> [!NOTE] Note
> This is a blocking call, which means the rest of your app logic could be blocked for several seconds until the transaction is processed. If this is a problem for your app then we recommend using the previous submission methods mentioned in this guide.
58 changes: 58 additions & 0 deletions packages/account/src/providers/provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2262,4 +2262,62 @@ Supported fuel-core version: ${mock.supportedVersion}.`

expect(fetchChainAndNodeInfo).toHaveBeenCalledTimes(2);
});

it('submits transaction and awaits status [success]', async () => {
using launched = await setupTestProviderAndWallets();
const {
provider,
wallets: [wallet],
} = launched;

const transactionRequest = await wallet.createTransfer(wallet.address, 100_000);
const signedTransaction = await wallet.signTransaction(transactionRequest);
transactionRequest.updateWitnessByOwner(wallet.address, signedTransaction);
const transactionId = transactionRequest.getTransactionId(await provider.getChainId());
const response = await provider.sendTransactionAndAwaitStatus(transactionRequest, {
estimateTxDependencies: false,
});
expect(response.status).toBe('success');
expect(response.receipts.length).not.toBe(0);
expect(response.id).toBe(transactionId);
});

it('submits transaction and awaits status [success with estimation]', async () => {
using launched = await setupTestProviderAndWallets();
const {
provider,
wallets: [wallet],
} = launched;

const transactionRequest = await wallet.createTransfer(wallet.address, 100_000);
const signedTransaction = await wallet.signTransaction(transactionRequest);
transactionRequest.updateWitnessByOwner(wallet.address, signedTransaction);
const transactionId = transactionRequest.getTransactionId(await provider.getChainId());
const response = await provider.sendTransactionAndAwaitStatus(transactionRequest);
expect(response.status).toBe('success');
expect(response.receipts.length).not.toBe(0);
expect(response.id).toBe(transactionId);
});

it('submits transaction and awaits status [failure]', async () => {
using launched = await setupTestProviderAndWallets();
const {
provider,
wallets: [wallet],
} = launched;

const transactionRequest = await wallet.createTransfer(wallet.address, 100_000);
transactionRequest.gasLimit = bn(0); // force fail
const signedTransaction = await wallet.signTransaction(transactionRequest);
transactionRequest.updateWitnessByOwner(wallet.address, signedTransaction);
await expectToThrowFuelError(
() =>
provider.sendTransactionAndAwaitStatus(transactionRequest, {
estimateTxDependencies: false,
}),
{
code: ErrorCode.SCRIPT_REVERTED,
}
);
});
});
18 changes: 17 additions & 1 deletion packages/account/src/providers/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ import {
isTransactionTypeScript,
transactionRequestify,
} from './transaction-request';
import type { TransactionResultReceipt } from './transaction-response';
import type { TransactionResult, TransactionResultReceipt } from './transaction-response';
import { TransactionResponse, getDecodedLogs } from './transaction-response';
import { processGqlReceipt } from './transaction-summary/receipt';
import {
Expand Down Expand Up @@ -880,6 +880,22 @@ Supported fuel-core version: ${supportedVersion}.`
return new TransactionResponse(transactionRequest, this, chainId, abis, subscription);
}

/**
* Submits a transaction to the chain and awaits its status response.
*
* @param transactionRequestLike - the request to submit.
* @param sendTransactionParams - The provider send transaction parameters (optional).
* @returns A promise that resolves to a settled transaction.
*/
async sendTransactionAndAwaitStatus(
transactionRequestLike: TransactionRequestLike,
providerSendTxParams: ProviderSendTxParams = {}
): Promise<TransactionResult<void>> {
const response = await this.sendTransaction(transactionRequestLike, providerSendTxParams);
const result = await response.waitForResult();
return result;
}

/**
* Executes a transaction without actually submitting it to the chain.
*
Expand Down

0 comments on commit 40d9960

Please sign in to comment.