From aa7e6563aa88d59dbeaaca8e4aa54cac686363a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Wed, 10 Jul 2024 12:48:03 -0300 Subject: [PATCH] feat!: implement pagination for `Account` methods (#2408) --- .changeset/three-shoes-drum.md | 5 + .../introduction/getting-started.test.ts | 2 +- .../src/guide/provider/pagination.test.ts | 106 +++++++ .../src/guide/provider/provider.test.ts | 2 +- .../guide/provider/querying-the-chain.test.ts | 130 ++++---- .../testing/launching-a-test-node.test.ts | 10 +- .../checking-balances-and-coins.test.ts | 4 +- .../src/guide/wallets/test-wallets.test.ts | 8 +- apps/docs/.vitepress/config.ts | 4 + apps/docs/spell-check-custom-words.txt | 1 + apps/docs/src/guide/provider/pagination.md | 65 ++++ .../src/guide/provider/querying-the-chain.md | 40 ++- packages/account/src/account.test.ts | 167 +++++----- packages/account/src/account.ts | 105 ++----- .../account/src/providers/operations.graphql | 12 + .../account/src/providers/provider.test.ts | 295 +++++++++++++++++- packages/account/src/providers/provider.ts | 190 ++++++++--- .../setup-test-provider-and-wallets.test.ts | 27 +- .../fuel-gauge/src/coverage-contract.test.ts | 9 +- packages/fuel-gauge/src/doc-examples.test.ts | 18 +- .../src/funding-transaction.test.ts | 2 +- .../src/token-test-contract.test.ts | 20 +- 22 files changed, 863 insertions(+), 359 deletions(-) create mode 100644 .changeset/three-shoes-drum.md create mode 100644 apps/docs-snippets/src/guide/provider/pagination.test.ts create mode 100644 apps/docs/src/guide/provider/pagination.md diff --git a/.changeset/three-shoes-drum.md b/.changeset/three-shoes-drum.md new file mode 100644 index 0000000000..cb51038f60 --- /dev/null +++ b/.changeset/three-shoes-drum.md @@ -0,0 +1,5 @@ +--- +"@fuel-ts/account": minor +--- + +feat!: implement pagination for `Account` methods diff --git a/apps/docs-snippets/src/guide/introduction/getting-started.test.ts b/apps/docs-snippets/src/guide/introduction/getting-started.test.ts index 533b2be3f4..6b0ce5f2c3 100644 --- a/apps/docs-snippets/src/guide/introduction/getting-started.test.ts +++ b/apps/docs-snippets/src/guide/introduction/getting-started.test.ts @@ -42,7 +42,7 @@ describe('Getting started', () => { const wallet = Wallet.fromPrivateKey(PRIVATE_KEY, provider); // Perform a balance check. - const balances = await wallet.getBalances(); + const { balances } = await wallet.getBalances(); // [{ assetId: '0x..', amount: bn(..) }, ..] // #endregion connecting-to-the-testnet diff --git a/apps/docs-snippets/src/guide/provider/pagination.test.ts b/apps/docs-snippets/src/guide/provider/pagination.test.ts new file mode 100644 index 0000000000..45d2dc271f --- /dev/null +++ b/apps/docs-snippets/src/guide/provider/pagination.test.ts @@ -0,0 +1,106 @@ +import type { GqlPageInfo } from '@fuel-ts/account/dist/providers/__generated__/operations'; +import type { CursorPaginationArgs } from 'fuels'; +import { FUEL_NETWORK_URL, Provider, Wallet } from 'fuels'; + +/** + * @group node + */ +describe('querying the chain', () => { + it('pagination snippet test 1', () => { + // #region pagination-1 + const paginationArgs: CursorPaginationArgs = { + after: 'cursor', + first: 10, + before: 'cursor', + last: 10, + }; + // #endregion pagination-1 + + // #region pagination-2 + const pageInfo: GqlPageInfo = { + endCursor: 'cursor', + hasNextPage: true, + startCursor: 'cursor', + hasPreviousPage: true, + }; + // #endregion pagination-2 + + expect(paginationArgs).toBeDefined(); + expect(pageInfo).toBeDefined(); + }); + + it('pagination snippet test 2', async () => { + // #region pagination-3 + // #import { Provider, CursorPaginationArgs, FUEL_NETWORK_URL, Wallet }; + + const provider = await Provider.create(FUEL_NETWORK_URL); + const baseAssetId = provider.getBaseAssetId(); + const myWallet = Wallet.generate({ provider }); + + let paginationArgs: CursorPaginationArgs = { + first: 10, // It will return only the first 10 coins + }; + + const { coins, pageInfo } = await provider.getCoins( + myWallet.address, + baseAssetId, + paginationArgs + ); + + if (pageInfo.hasNextPage) { + paginationArgs = { + after: pageInfo.endCursor, + first: 10, + }; + // The coins array will include the next 10 coins after the last one in the previous array + await provider.getCoins(myWallet.address, baseAssetId, paginationArgs); + } + // #endregion pagination-3 + + // #region pagination-4 + if (pageInfo.hasPreviousPage) { + paginationArgs = { + before: pageInfo.startCursor, + last: 10, + }; + + // It will includes the previous 10 coins before the first one in the previous array + await provider.getCoins(myWallet.address, baseAssetId, paginationArgs); + } + // #endregion pagination-4 + + expect(paginationArgs).toBeDefined(); + expect(coins).toBeDefined(); + expect(pageInfo).toBeDefined(); + }); + + it('pagination snippet test 3', () => { + // #region pagination-5 + const paginationArgs = { after: 'cursor', first: 10 }; + // #endregion pagination-5 + + expect(paginationArgs).toBeDefined(); + }); + it('pagination snippet test 4', () => { + // #region pagination-6 + const paginationArgs = { before: 'cursor', last: 10 }; + // #endregion pagination-6 + + expect(paginationArgs).toBeDefined(); + }); + + it('pagination snippet test 5', async () => { + // #region pagination-7 + // #import { Provider, FUEL_NETWORK_URL, Wallet }; + + const provider = await Provider.create(FUEL_NETWORK_URL); + const myWallet = Wallet.generate({ provider }); + + // It will return the first 100 coins of the base asset + const { coins, pageInfo } = await provider.getCoins(myWallet.address); + // #endregion pagination-7 + + expect(coins).toBeDefined(); + expect(pageInfo).toBeDefined(); + }); +}); diff --git a/apps/docs-snippets/src/guide/provider/provider.test.ts b/apps/docs-snippets/src/guide/provider/provider.test.ts index 55e4a00bef..0750663e3a 100644 --- a/apps/docs-snippets/src/guide/provider/provider.test.ts +++ b/apps/docs-snippets/src/guide/provider/provider.test.ts @@ -34,7 +34,7 @@ describe('Provider', () => { const wallet = WalletUnlocked.generate({ provider }); // Get the balances of the wallet (this will be empty until we have assets) - const balances = await wallet.getBalances(); + const { balances } = await wallet.getBalances(); // [] // #endregion provider-definition diff --git a/apps/docs-snippets/src/guide/provider/querying-the-chain.test.ts b/apps/docs-snippets/src/guide/provider/querying-the-chain.test.ts index a8e09b2f2b..6fdb1977e9 100644 --- a/apps/docs-snippets/src/guide/provider/querying-the-chain.test.ts +++ b/apps/docs-snippets/src/guide/provider/querying-the-chain.test.ts @@ -1,4 +1,4 @@ -import { FUEL_NETWORK_URL, Provider, ScriptTransactionRequest, bn } from 'fuels'; +import { FUEL_NETWORK_URL, Provider, ScriptTransactionRequest } from 'fuels'; import { generateTestWallet } from 'fuels/test-utils'; /** @@ -6,8 +6,9 @@ import { generateTestWallet } from 'fuels/test-utils'; */ describe('querying the chain', () => { it('query coins', async () => { - // #region wallet-query + // #region get-coins-1 // #import { Provider, FUEL_NETWORK_URL, generateTestWallet }; + const provider = await Provider.create(FUEL_NETWORK_URL); const assetIdA = '0x0101010101010101010101010101010101010101010101010101010101010101'; const baseAssetId = provider.getBaseAssetId(); @@ -17,40 +18,33 @@ describe('querying the chain', () => { [100, assetIdA], ]); - // get single coin - const coin = await wallet.getCoins(baseAssetId); - // [{ amount: bn(42), assetId: baseAssetId }] + // fetches up to 100 coins from baseAssetId + const { coins, pageInfo } = await provider.getCoins(wallet.address, baseAssetId); + // [ + // { amount: bn(42), assetId: baseAssetId }, + // ... + // ] - // get all coins - const coins = await wallet.getCoins(); + // fetches up to 100 coins from all assets + await provider.getCoins(wallet.address); // [ // { amount: bn(42), assetId: baseAssetId } // { amount: bn(100), assetId: assetIdA } + // ... // ] - // #endregion wallet-query - - expect(coin.length).toEqual(1); - expect(coin).toEqual([ - expect.objectContaining({ - assetId: baseAssetId, - amount: bn(42), - }), - ]); - expect(coins).toEqual([ - expect.objectContaining({ - assetId: baseAssetId, - amount: bn(42), - }), - expect.objectContaining({ - assetId: assetIdA, - amount: bn(100), - }), - ]); + // #endregion get-coins-1 + + // #region get-coins-2 + await wallet.getCoins(baseAssetId); + // #endregion get-coins-2 + + expect(coins).toBeDefined(); + expect(pageInfo).toBeDefined(); }); - it('get balances', async () => { - // #region wallet-get-balances - // #import { Provider, FUEL_NETWORK_URL, generateTestWallet }; + it('get spendable resources', async () => { + // #region get-spendable-resources-1 + // #import { Provider, FUEL_NETWORK_URL, generateTestWallet, ScriptTransactionRequest }; const provider = await Provider.create(FUEL_NETWORK_URL); const assetIdA = '0x0101010101010101010101010101010101010101010101010101010101010101'; @@ -61,21 +55,38 @@ describe('querying the chain', () => { [100, assetIdA], ]); - const walletBalances = await wallet.getBalances(); - // [ - // { amount: bn(42), assetId: baseAssetId } - // { amount: bn(100), assetId: assetIdA } - // ] - // #endregion wallet-get-balances + const quantities: CoinQuantityLike[] = [ + { amount: 32, assetId: baseAssetId, max: 42 }, + { amount: 50, assetId: assetIdA }, + ]; - expect(walletBalances).toEqual([ - { assetId: assetIdA, amount: bn(100) }, - { assetId: baseAssetId, amount: bn(42) }, - ]); + const utxoId = '0x00000000000000000000000000000000000000000000000000000000000000010001'; + const messageNonce = '0x381de90750098776c71544527fd253412908dec3d07ce9a7367bd1ba975908a0'; + const excludedIds: ExcludeResourcesOption = { + utxos: [utxoId], + messages: [messageNonce], + }; + + const spendableResources = await provider.getResourcesToSpend( + wallet.address, + quantities, + excludedIds + ); + + const tx = new ScriptTransactionRequest(); + tx.addResources(spendableResources); + // #endregion get-spendable-resources-1 + + // #region get-spendable-resources-2 + await wallet.getResourcesToSpend(spendableResources, excludedIds); + // #endregion get-spendable-resources-2 + + expect(spendableResources).toBeDefined(); }); - it('get spendable resources', async () => { - // #region wallet-get-spendable-resources - // #import { Provider, FUEL_NETWORK_URL, generateTestWallet, ScriptTransactionRequest }; + + it('get balances', async () => { + // #region get-balances-1 + // #import { Provider, FUEL_NETWORK_URL, generateTestWallet }; const provider = await Provider.create(FUEL_NETWORK_URL); const assetIdA = '0x0101010101010101010101010101010101010101010101010101010101010101'; @@ -86,43 +97,48 @@ describe('querying the chain', () => { [100, assetIdA], ]); - const spendableResources = await wallet.getResourcesToSpend([ - { amount: 32, assetId: baseAssetId, max: 42 }, - { amount: 50, assetId: assetIdA }, - ]); + const { balances } = await provider.getBalances(wallet.address); + // [ + // { amount: bn(42), assetId: baseAssetId } // total amount of baseAssetId + // { amount: bn(100), assetId: assetIdA } // total amount of assetIdA + // ] + // #endregion get-balances-1 - const tx = new ScriptTransactionRequest(); - tx.addResources(spendableResources); - // #endregion wallet-get-spendable-resources + // #region get-balances-2 + await wallet.getBalances(); + // #endregion get-balances-2 - expect(spendableResources[0].amount).toEqual(bn(42)); - expect(spendableResources[1].amount).toEqual(bn(100)); + expect(balances).toBeDefined(); }); it('can getBlocks', async () => { // #region Provider-get-blocks // #import { Provider, FUEL_NETWORK_URL }; + const provider = await Provider.create(FUEL_NETWORK_URL); + const blockToProduce = 3; + // Force-producing some blocks to make sure that 10 blocks exist - await provider.produceBlocks(10); - const blocks = await provider.getBlocks({ - last: 10, + await provider.produceBlocks(blockToProduce); + + const { blocks } = await provider.getBlocks({ + last: blockToProduce, }); // #endregion Provider-get-blocks - expect(blocks.length).toBe(10); + expect(blocks.length).toBe(blockToProduce); }); it('can getMessageByNonce', async () => { - // #region getMessageByNonce + // #region get-message-by-nonce-1 // #import { FUEL_NETWORK_URL, Provider }; const provider = await Provider.create(FUEL_NETWORK_URL); const nonce = '0x381de90750098776c71544527fd253412908dec3d07ce9a7367bd1ba975908a0'; const message = await provider.getMessageByNonce(nonce); + // #endregion get-message-by-nonce-1 expect(message).toBeDefined(); expect(message?.nonce).toEqual(nonce); - // #endregion getMessageByNonce }); }); diff --git a/apps/docs-snippets/src/guide/testing/launching-a-test-node.test.ts b/apps/docs-snippets/src/guide/testing/launching-a-test-node.test.ts index e7614b0a44..c5a1c7fe71 100644 --- a/apps/docs-snippets/src/guide/testing/launching-a-test-node.test.ts +++ b/apps/docs-snippets/src/guide/testing/launching-a-test-node.test.ts @@ -190,7 +190,7 @@ describe('launching a test node', () => { wallets: [wallet], } = launched; - const coins = await wallet.getCoins(assets[0].value); + const { coins } = await wallet.getCoins(assets[0].value); // #endregion asset-ids expect(coins[0].assetId).toEqual(assets[0].value); }); @@ -211,7 +211,9 @@ describe('launching a test node', () => { wallets: [wallet], } = launched; - const [message] = await wallet.getMessages(); + const { + messages: [message], + } = await wallet.getMessages(); // message.nonce === testMessage.nonce // #endregion test-messages @@ -242,7 +244,9 @@ describe('launching a test node', () => { recipient.provider = provider; - const [message] = await recipient.getMessages(); + const { + messages: [message], + } = await recipient.getMessages(); // message.nonce === testMessage.nonce // #endregion test-messages-chain diff --git a/apps/docs-snippets/src/guide/wallets/checking-balances-and-coins.test.ts b/apps/docs-snippets/src/guide/wallets/checking-balances-and-coins.test.ts index b8ce8b87ff..8d3b208c61 100644 --- a/apps/docs-snippets/src/guide/wallets/checking-balances-and-coins.test.ts +++ b/apps/docs-snippets/src/guide/wallets/checking-balances-and-coins.test.ts @@ -1,4 +1,4 @@ -import type { BigNumberish, CoinQuantity, WalletUnlocked } from 'fuels'; +import type { BigNumberish, WalletUnlocked } from 'fuels'; import { Provider, Wallet, FUEL_NETWORK_URL } from 'fuels'; /** @@ -30,7 +30,7 @@ describe(__filename, () => { // #region wallet-check-balances // #context import { CoinQuantity } from 'fuels'; - const balances: CoinQuantity[] = await myWallet.getBalances(); + const { balances } = await myWallet.getBalances(); // #endregion wallet-check-balances expect(balances).toEqual([]); diff --git a/apps/docs-snippets/src/guide/wallets/test-wallets.test.ts b/apps/docs-snippets/src/guide/wallets/test-wallets.test.ts index ee0b7c042e..40d68fc23c 100644 --- a/apps/docs-snippets/src/guide/wallets/test-wallets.test.ts +++ b/apps/docs-snippets/src/guide/wallets/test-wallets.test.ts @@ -1,4 +1,4 @@ -import type { CoinQuantity, WalletUnlocked } from 'fuels'; +import type { WalletUnlocked } from 'fuels'; import { FUEL_NETWORK_URL, Provider, bn } from 'fuels'; import { generateTestWallet } from 'fuels/test-utils'; @@ -31,9 +31,9 @@ describe(__filename, () => { const walletC = await generateTestWallet(provider); // retrieve balances of wallets - const walletABalances: CoinQuantity[] = await walletA.getBalances(); - const walletBBalances = await walletB.getBalances(); - const walletCBalances = await walletC.getBalances(); + const { balances: walletABalances } = await walletA.getBalances(); + const { balances: walletBBalances } = await walletB.getBalances(); + const { balances: walletCBalances } = await walletC.getBalances(); expect(walletABalances).toEqual([{ assetId: baseAssetId, amount: bn(42) }]); expect(walletBBalances).toEqual([ diff --git a/apps/docs/.vitepress/config.ts b/apps/docs/.vitepress/config.ts index ee23c150c5..d433a72ec9 100644 --- a/apps/docs/.vitepress/config.ts +++ b/apps/docs/.vitepress/config.ts @@ -131,6 +131,10 @@ export default defineConfig({ text: 'Provider Options', link: '/guide/provider/provider-options', }, + { + text: 'Pagination', + link: '/guide/provider/pagination', + }, { text: 'Querying the Chain', link: '/guide/provider/querying-the-chain', diff --git a/apps/docs/spell-check-custom-words.txt b/apps/docs/spell-check-custom-words.txt index 78086c1521..975173064e 100644 --- a/apps/docs/spell-check-custom-words.txt +++ b/apps/docs/spell-check-custom-words.txt @@ -91,6 +91,7 @@ mempool merkle monorepo monorepos +nonces natively npm nvm diff --git a/apps/docs/src/guide/provider/pagination.md b/apps/docs/src/guide/provider/pagination.md new file mode 100644 index 0000000000..4e073419e6 --- /dev/null +++ b/apps/docs/src/guide/provider/pagination.md @@ -0,0 +1,65 @@ +# Pagination + +Pagination is highly efficient when dealing with large sets of data. Because of this some methods from the `Provider` class support [GraphQL cursor pagination](https://graphql.org/learn/pagination/), allowing you to efficiently navigate through data chunks. + +## Pagination Arguments + +The pagination arguments object is used to specify the range of data you want to retrieve. It includes the following properties: + +- `after`: A cursor pointing to a position after which you want to retrieve items. +- `first`: The number of items to retrieve after the specified cursor. This is used in conjunction with the `after` argument. +- `before`: A cursor pointing to a position before which you want to retrieve items. +- `last`: The number of items to retrieve before the specified cursor. This is used in conjunction with the `before` argument. + +<<< @/../../docs-snippets/src/guide/provider/pagination.test.ts#pagination-1{ts:line-numbers} + +## Page Info + +The `pageInfo` object is included in the GraphQL response for requests that support cursor pagination. It provides crucial metadata about the current page of results, allowing you to understand the pagination state and determine if there are more items to fetch before or after the current set. + +- `endCursor`: A cursor representing the last item in the current set of results. It should be used as the `after` argument in subsequent queries to fetch the next set of items. +- `hasNextPage`: A boolean indicating whether there are more items available after the current set. +- `startCursor`: A cursor representing the first item in the current set of results. It should be used as the `before` argument in subsequent queries to fetch the previous set of items. +- `hasPreviousPage`: A boolean indicating whether there are more items available before the current set. + +<<< @/../../docs-snippets/src/guide/provider/pagination.test.ts#pagination-2{ts:line-numbers} + +## Using Pagination + +One of the methods that supports pagination is the `getCoins` method. This method receives three parameters: + +- `address`: The owner's account address +- `assetId`: The asset ID of the coins (optional) +- `paginationArgs`: The pagination arguments (optional) + +### Basic Pagination + +Here is how you can use the `getCoins` method with pagination: + +<<< @/../../docs-snippets/src/guide/provider/pagination.test.ts#pagination-3{ts:line-numbers} + +### Navigating to the Previous Page + +You can also use the `paginationArgs` to navigate to the previous page of results: + +<<< @/../../docs-snippets/src/guide/provider/pagination.test.ts#pagination-4{ts:line-numbers} + +## Valid Combinations + +- Forward Pagination: + + Use `after` with `first` to retrieve items following a cursor. + +<<< @/../../docs-snippets/src/guide/provider/pagination.test.ts#pagination-5{ts} + +- Backward Pagination: + + Use `before` with `last` to retrieve items preceding a cursor. + +<<< @/../../docs-snippets/src/guide/provider/pagination.test.ts#pagination-6{ts} + +## Default Behavior + +If neither `assetId` nor `paginationArgs` are provided, the `getCoins` method will default to the base asset ID and return the first 100 items: + +<<< @/../../docs-snippets/src/guide/provider/pagination.test.ts#pagination-7{ts:line-numbers} diff --git a/apps/docs/src/guide/provider/querying-the-chain.md b/apps/docs/src/guide/provider/querying-the-chain.md index c4b9ba69a1..f38e0cd464 100644 --- a/apps/docs/src/guide/provider/querying-the-chain.md +++ b/apps/docs/src/guide/provider/querying-the-chain.md @@ -9,41 +9,53 @@ We can connect to either a _*local*_ or an _*external*_ node: Let's look at a few examples below. -## Get the Base Asset ID +## `getBaseAssetId` The base asset is the underlying asset used to perform any transaction on a chain. This should be fetched from a provider to then be used in transactions. <<< @/../../docs-snippets/src/guide/provider/provider.test.ts#provider-getBaseAssetId{ts:line-numbers} -## Get all coins from an address +## `getCoins` -This method returns all coins (of an optional given asset ID) from a wallet, including spent ones. +Returns UTXOs coins from an account address, optionally filtered by asset ID. This method supports [pagination](./pagination.md). -<<< @/../../docs-snippets/src/guide/provider/querying-the-chain.test.ts#wallet-query{ts:line-numbers} +<<< @/../../docs-snippets/src/guide/provider/querying-the-chain.test.ts#get-coins-1{ts:line-numbers} -## Get spendable resources from an address +This method is also implemented on the `Account` class and can be used without providing the `address`: -The last argument says how much you want to spend. This method returns only spendable, i.e., unspent coins (of a given asset ID). If you ask for more spendable than the amount of unspent coins you have, it returns an error. +<<< @/../../docs-snippets/src/guide/provider/querying-the-chain.test.ts#get-coins-2{ts:line-numbers} -<<< @/../../docs-snippets/src/guide/provider/querying-the-chain.test.ts#wallet-get-spendable-resources{ts:line-numbers} +## `getResourcesToSpend` -## Get balances from an address +Returns spendable resources (coins or messages) for a transaction request. It accepts an optional third parameter, `excludedIds`, to exclude specific UTXO IDs or coin message nonces: -Get all the spendable balances of all assets for an address. This is different from getting the coins because we only return the numbers (the sum of UTXOs coins amount for each asset id) and not the UTXOs coins themselves. +<<< @/../../docs-snippets/src/guide/provider/querying-the-chain.test.ts#get-spendable-resources-1{ts:line-numbers} -<<< @/../../docs-snippets/src/guide/provider/querying-the-chain.test.ts#wallet-get-balances{ts:line-numbers} +This method is also available in the `Account` class and can be used without providing the `address`: -## Get blocks +<<< @/../../docs-snippets/src/guide/provider/querying-the-chain.test.ts#get-spendable-resources-2{ts:line-numbers} -This method returns all the blocks from the blockchain that match the given query. The below code snippet shows how to get the last 10 blocks. +## `getBalances` + +Returns the sum of all UTXOs coins and unspent message coins amounts for all assets. Unlike `getCoins`, it only returns the total amounts, not the individual coins: + +<<< @/../../docs-snippets/src/guide/provider/querying-the-chain.test.ts#get-balances-1{ts:line-numbers} + +This method is also available in the `Account` class and can be used without providing the `address` parameter: + +<<< @/../../docs-snippets/src/guide/provider/querying-the-chain.test.ts#get-balances-2{ts:line-numbers} + +## `getBlocks` + +The `getBlocks` method returns blocks from the blockchain matching the given `paginationArgs` parameter, supporting [pagination](./pagination.md). The below code snippet shows how to get the last 10 blocks. <<< @/../../docs-snippets/src/guide/provider/querying-the-chain.test.ts#Provider-get-blocks{ts:line-numbers} -## Get a message by its nonce +## `getMessageByNonce` You can use the `getMessageByNonce` method to retrieve a message by its nonce. -<<< @/../../docs-snippets/src/guide/provider/querying-the-chain.test.ts#getMessageByNonce{ts:line-numbers} +<<< @/../../docs-snippets/src/guide/provider/querying-the-chain.test.ts#get-message-by-nonce-1{ts:line-numbers}