From 9dba357cb757abcf1dcaa346eccdedf656b33390 Mon Sep 17 00:00:00 2001 From: Anderson Arboleya Date: Mon, 21 Oct 2024 11:04:23 -0300 Subject: [PATCH 1/9] chore: upgrading `fuel-core` to `0.40.0` (#3332) --- .changeset/lucky-avocados-add.md | 8 +++ .../src/cookbook/combining-utxos.ts | 57 +++++++++++++++++++ apps/docs/.vitepress/config.ts | 4 ++ .../src/guide/cookbook/combining-utxos.md | 11 ++++ apps/docs/src/guide/errors/index.md | 6 ++ internal/fuel-core/VERSION | 2 +- packages/account/src/account.test.ts | 6 +- .../account/src/providers/provider.test.ts | 8 ++- .../utils/handle-gql-error-message.ts | 8 +++ packages/errors/src/error-codes.ts | 2 +- .../src/mapped-error-messages.test.ts | 50 ++++++++++++++++ .../fuel-gauge/src/not-enough-coins.test.ts | 22 ------- .../versions/src/lib/getBuiltinVersions.ts | 2 +- 13 files changed, 158 insertions(+), 28 deletions(-) create mode 100644 .changeset/lucky-avocados-add.md create mode 100644 apps/docs-snippets2/src/cookbook/combining-utxos.ts create mode 100644 apps/docs/src/guide/cookbook/combining-utxos.md create mode 100644 packages/fuel-gauge/src/mapped-error-messages.test.ts delete mode 100644 packages/fuel-gauge/src/not-enough-coins.test.ts diff --git a/.changeset/lucky-avocados-add.md b/.changeset/lucky-avocados-add.md new file mode 100644 index 00000000000..25355bc1eeb --- /dev/null +++ b/.changeset/lucky-avocados-add.md @@ -0,0 +1,8 @@ +--- +"@internal/fuel-core": patch +"@fuel-ts/versions": patch +"@fuel-ts/account": patch +"@fuel-ts/errors": patch +--- + +chore: upgrading `fuel-core` to `0.40.0` diff --git a/apps/docs-snippets2/src/cookbook/combining-utxos.ts b/apps/docs-snippets2/src/cookbook/combining-utxos.ts new file mode 100644 index 00000000000..773a563a0b2 --- /dev/null +++ b/apps/docs-snippets2/src/cookbook/combining-utxos.ts @@ -0,0 +1,57 @@ +// #region combining-utxos +import { Provider, Wallet } from 'fuels'; + +import { LOCAL_NETWORK_URL, WALLET_PVT_KEY } from '../env'; + +const provider = await Provider.create(LOCAL_NETWORK_URL); +const fundingWallet = Wallet.fromPrivateKey(WALLET_PVT_KEY, provider); + +const wallet = Wallet.generate({ provider }); + +// First, lets fund a wallet with 10_000 of the base asset. But as we are doing this across 10 transactions, +// we will end up with 10 UTXOs. +for (let i = 0; i < 10; i++) { + const initTx = await fundingWallet.transfer( + wallet.address, + 1000, + provider.getBaseAssetId() + ); + await initTx.waitForResult(); +} + +// We can fetch the coins to see how many UTXOs we have, and confirm it is 10. +const { coins: initialCoins } = await wallet.getCoins( + provider.getBaseAssetId() +); +console.log('Initial Coins Length', initialCoins.length); +// 10 + +// But we can also confirm the total balance of the base asset for this account is 10_000. +const initialBalance = await wallet.getBalance(provider.getBaseAssetId()); +console.log('Initial Balance', initialBalance.toNumber()); +// 10_000 + +// Now we can combine the UTXOs into a single UTXO by performing a single transfer for the +// majority of the balance. Of course, we will still need some funds for the transaction fees. +const combineTx = await wallet.transfer( + wallet.address, + 9500, + provider.getBaseAssetId() +); +await combineTx.wait(); + +// Now we can perform the same validations and see we have less UTXOs. +// We have 2 in this instance, as we have performed the transfer in the base asset: +// a UTXO for our transfer, and another for what is left after paying the fees. +const { coins: combinedCoins } = await wallet.getCoins( + provider.getBaseAssetId() +); +console.log('Combined Coins Length', combinedCoins.length); +// 2 + +// And we can also confirm the final balance of the base asset for this account is 9_998, +// so the cost of combining is also minimal. +const combinedBalance = await wallet.getBalance(provider.getBaseAssetId()); +console.log('Combined Balance', combinedBalance.toNumber()); +// 9_998 +// #endregion combining-utxos diff --git a/apps/docs/.vitepress/config.ts b/apps/docs/.vitepress/config.ts index f62eb604519..4efd373d2b1 100644 --- a/apps/docs/.vitepress/config.ts +++ b/apps/docs/.vitepress/config.ts @@ -422,6 +422,10 @@ export default defineConfig({ text: 'Resubmitting Failed Transactions', link: '/guide/cookbook/resubmitting-failed-transactions', }, + { + text: 'Combining UTXOs', + link: '/guide/cookbook/combining-utxos', + }, ], }, { diff --git a/apps/docs/src/guide/cookbook/combining-utxos.md b/apps/docs/src/guide/cookbook/combining-utxos.md new file mode 100644 index 00000000000..3d076ba6858 --- /dev/null +++ b/apps/docs/src/guide/cookbook/combining-utxos.md @@ -0,0 +1,11 @@ +# Combining UTXOs + +When performing a funding operation or calling `getResourcesToSpend`, you may encounter the `MAX_COINS_REACHED` error if the number of coins fetched per asset exceeds the maximum limit allowed by the chain. + +You may also want to do this if you want to reduce the number of inputs in your transaction, which can be useful if you are trying to reduce the size of your transaction or you are receiving the `MAX_INPUTS_EXCEEDED` error. + +One way to avoid these errors is to combine your UTXOs. This can be done by performing a transfer that combines multiple UTXOs into a single UTXO, where the transaction has multiple inputs for the asset, but a smaller number of outputs. + +> **Note:** You will not be able to have a single UTXO for the base asset after combining, as one output will be for the transfer, and you will have another for the fees. + +<<< @/../../docs-snippets2/src/cookbook/combining-utxos.ts#combining-utxos{ts:line-numbers} \ No newline at end of file diff --git a/apps/docs/src/guide/errors/index.md b/apps/docs/src/guide/errors/index.md index ad57e214d8c..324e1c8b12f 100644 --- a/apps/docs/src/guide/errors/index.md +++ b/apps/docs/src/guide/errors/index.md @@ -325,3 +325,9 @@ When the number of transaction inputs exceeds the maximum limit allowed by the b ### `MAX_OUTPUTS_EXCEEDED` When the number of transaction outputs exceeds the maximum limit allowed by the blockchain. + +### `MAX_COINS_REACHED` + +When performing a funding operation, or calling `getResourcesToSpend`, this error can be thrown if the number of coins fetched per asset exceeds the maximum limit allowed by the blockchain. + +This can be avoided by paginating the results of the `getCoins` method to fund your transaction, or by reducing the number of UTXOs for your account. This can be done by performing a transfer that amalgamates your UTXOs, as demonstrated in [this cookbook](../cookbook/combining-utxos.md). diff --git a/internal/fuel-core/VERSION b/internal/fuel-core/VERSION index 4ef2eb086f5..9b0025a7850 100644 --- a/internal/fuel-core/VERSION +++ b/internal/fuel-core/VERSION @@ -1 +1 @@ -0.39.0 +0.40.0 diff --git a/packages/account/src/account.test.ts b/packages/account/src/account.test.ts index 7e49d958516..e6d31144455 100644 --- a/packages/account/src/account.test.ts +++ b/packages/account/src/account.test.ts @@ -899,7 +899,7 @@ describe('Account', () => { vi.restoreAllMocks(); }); - it('should validate max number of inputs when funding the TX', async () => { + it('throws when funding with more than 255 coins for an input', async () => { using launched = await setupTestProviderAndWallets({ walletsConfig: { amountPerCoin: 100, @@ -920,7 +920,9 @@ describe('Account', () => { request.maxFee = txCost.maxFee; await expectToThrowFuelError(() => wallet.fund(request, txCost), { - code: ErrorCode.MAX_INPUTS_EXCEEDED, + code: ErrorCode.MAX_COINS_REACHED, + message: + 'The account retrieving coins has exceeded the maximum number of coins per asset. Please consider combining your coins into a single UTXO.', }); }); diff --git a/packages/account/src/providers/provider.test.ts b/packages/account/src/providers/provider.test.ts index eea8adcd4e1..f0042b90ad0 100644 --- a/packages/account/src/providers/provider.test.ts +++ b/packages/account/src/providers/provider.test.ts @@ -867,11 +867,17 @@ describe('Provider', () => { request.addCoinOutput(receiver.address, 500, provider.getBaseAssetId()); request.addResources(resources); + // We need to add more resources manually here as a single `getResourcesToSpend` call + // will always truncate to `maxInputs`. So we need to add more resources manually + // to test our validation logic. + const moreResources = await sender.getResourcesToSpend(quantities); + request.addResources(moreResources); + await expectToThrowFuelError( () => sender.sendTransaction(request), new FuelError( ErrorCode.MAX_INPUTS_EXCEEDED, - `The transaction exceeds the maximum allowed number of inputs. Tx inputs: ${quantities.length}, max inputs: ${maxInputs}` + `The transaction exceeds the maximum allowed number of inputs. Tx inputs: 4, max inputs: ${maxInputs}` ) ); }); diff --git a/packages/account/src/providers/utils/handle-gql-error-message.ts b/packages/account/src/providers/utils/handle-gql-error-message.ts index 9f19a2f53b9..3a2d3d82fb2 100644 --- a/packages/account/src/providers/utils/handle-gql-error-message.ts +++ b/packages/account/src/providers/utils/handle-gql-error-message.ts @@ -3,6 +3,7 @@ import type { GraphQLError } from 'graphql'; export enum GqlErrorMessage { NOT_ENOUGH_COINS = 'not enough coins to fit the target', + MAX_COINS_REACHED = 'max number of coins is reached while trying to fit the target', } export const handleGqlErrorMessage = (errorMessage: string, rawError: GraphQLError) => { @@ -14,6 +15,13 @@ export const handleGqlErrorMessage = (errorMessage: string, rawError: GraphQLErr {}, rawError ); + case GqlErrorMessage.MAX_COINS_REACHED: + throw new FuelError( + ErrorCode.MAX_COINS_REACHED, + 'The account retrieving coins has exceeded the maximum number of coins per asset. Please consider combining your coins into a single UTXO.', + {}, + rawError + ); default: throw new FuelError(ErrorCode.INVALID_REQUEST, errorMessage); } diff --git a/packages/errors/src/error-codes.ts b/packages/errors/src/error-codes.ts index 151235770bd..76f6e8aae80 100644 --- a/packages/errors/src/error-codes.ts +++ b/packages/errors/src/error-codes.ts @@ -85,7 +85,7 @@ export enum ErrorCode { MAX_INPUTS_EXCEEDED = 'max-inputs-exceeded', FUNDS_TOO_LOW = 'funds-too-low', MAX_OUTPUTS_EXCEEDED = 'max-outputs-exceeded', - + MAX_COINS_REACHED = 'max-coins-reached', // receipt INVALID_RECEIPT_TYPE = 'invalid-receipt-type', diff --git a/packages/fuel-gauge/src/mapped-error-messages.test.ts b/packages/fuel-gauge/src/mapped-error-messages.test.ts new file mode 100644 index 00000000000..d2a1404a8e7 --- /dev/null +++ b/packages/fuel-gauge/src/mapped-error-messages.test.ts @@ -0,0 +1,50 @@ +import { Contract, ErrorCode, ScriptTransactionRequest, Wallet } from 'fuels'; +import { expectToThrowFuelError, launchTestNode } from 'fuels/test-utils'; + +import { CallTestContractFactory } from '../test/typegen/contracts'; + +import { launchTestContract } from './utils'; + +/** + * @group node + * @group browser + */ +describe('mapped error messages', () => { + test('not enough coins error', async () => { + using contract = await launchTestContract({ factory: CallTestContractFactory }); + + const emptyWallet = Wallet.generate({ provider: contract.provider }); + + const emptyWalletContract = new Contract(contract.id, contract.interface.jsonAbi, emptyWallet); + + await expectToThrowFuelError(() => emptyWalletContract.functions.return_void().call(), { + code: ErrorCode.NOT_ENOUGH_FUNDS, + message: `The account(s) sending the transaction don't have enough funds to cover the transaction.`, + }); + }); + + test('max coins reached error', async () => { + using launched = await launchTestNode({ + walletsConfig: { + amountPerCoin: 1, + coinsPerAsset: 256, + }, + }); + const { + wallets: [wallet], + } = launched; + + const request = new ScriptTransactionRequest(); + request.addCoinOutput(wallet.address, 256, wallet.provider.getBaseAssetId()); + const txCost = await wallet.getTransactionCost(request); + + request.gasLimit = txCost.gasUsed; + request.maxFee = txCost.maxFee; + + await expectToThrowFuelError(() => wallet.fund(request, txCost), { + code: ErrorCode.MAX_COINS_REACHED, + message: + 'The account retrieving coins has exceeded the maximum number of coins per asset. Please consider combining your coins into a single UTXO.', + }); + }); +}); diff --git a/packages/fuel-gauge/src/not-enough-coins.test.ts b/packages/fuel-gauge/src/not-enough-coins.test.ts deleted file mode 100644 index 273d7263426..00000000000 --- a/packages/fuel-gauge/src/not-enough-coins.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Contract, ErrorCode, Wallet } from 'fuels'; -import { expectToThrowFuelError } from 'fuels/test-utils'; - -import { CallTestContractFactory } from '../test/typegen/contracts'; - -import { launchTestContract } from './utils'; - -/** - * @group node - */ -test('not enough coins error', async () => { - using contract = await launchTestContract({ factory: CallTestContractFactory }); - - const emptyWallet = Wallet.generate({ provider: contract.provider }); - - const emptyWalletContract = new Contract(contract.id, contract.interface.jsonAbi, emptyWallet); - - await expectToThrowFuelError(() => emptyWalletContract.functions.return_void().call(), { - code: ErrorCode.NOT_ENOUGH_FUNDS, - message: `The account(s) sending the transaction don't have enough funds to cover the transaction.`, - }); -}); diff --git a/packages/versions/src/lib/getBuiltinVersions.ts b/packages/versions/src/lib/getBuiltinVersions.ts index 49c8e400f5f..9541c6a98bd 100644 --- a/packages/versions/src/lib/getBuiltinVersions.ts +++ b/packages/versions/src/lib/getBuiltinVersions.ts @@ -3,7 +3,7 @@ import type { Versions } from './types'; export function getBuiltinVersions(): Versions { return { FORC: '0.66.2', - FUEL_CORE: '0.39.0', + FUEL_CORE: '0.40.0', FUELS: '0.96.1', }; } From 3a56354d6bd1db87584014aad07602ea95e282c4 Mon Sep 17 00:00:00 2001 From: Daniel Bate Date: Mon, 21 Oct 2024 21:40:45 +0700 Subject: [PATCH 2/9] chore: exclude `node_modules` in template tests (#3342) --- .changeset/proud-donuts-stare.md | 5 +++++ templates/nextjs/vitest.config.mts | 2 +- templates/vite/vitest.config.mts | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .changeset/proud-donuts-stare.md diff --git a/.changeset/proud-donuts-stare.md b/.changeset/proud-donuts-stare.md new file mode 100644 index 00000000000..01d12040425 --- /dev/null +++ b/.changeset/proud-donuts-stare.md @@ -0,0 +1,5 @@ +--- +"create-fuels": patch +--- + +chore: exclude `node_modules` in template tests diff --git a/templates/nextjs/vitest.config.mts b/templates/nextjs/vitest.config.mts index 92dda13d6fc..c9a86a08ebd 100644 --- a/templates/nextjs/vitest.config.mts +++ b/templates/nextjs/vitest.config.mts @@ -3,6 +3,6 @@ import { defineConfig } from "vitest/config"; export default defineConfig({ esbuild: { target: "es2022" }, test: { - exclude: ["**/test/ui/**"], + exclude: ["**/test/ui/**", "**/node_modules/**"], }, }); diff --git a/templates/vite/vitest.config.mts b/templates/vite/vitest.config.mts index 92dda13d6fc..c9a86a08ebd 100644 --- a/templates/vite/vitest.config.mts +++ b/templates/vite/vitest.config.mts @@ -3,6 +3,6 @@ import { defineConfig } from "vitest/config"; export default defineConfig({ esbuild: { target: "es2022" }, test: { - exclude: ["**/test/ui/**"], + exclude: ["**/test/ui/**", "**/node_modules/**"], }, }); From 2d21767604213ab1680ba5d22788a8ab944ba316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nedim=20Salki=C4=87?= Date: Mon, 21 Oct 2024 17:04:37 +0200 Subject: [PATCH 3/9] fix: proxy contract versions not being updated correctly (#3339) --- .changeset/thick-geckos-cheer.md | 2 ++ scripts/changeset/changeset-version-with-docs.ts | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 .changeset/thick-geckos-cheer.md diff --git a/.changeset/thick-geckos-cheer.md b/.changeset/thick-geckos-cheer.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/thick-geckos-cheer.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/scripts/changeset/changeset-version-with-docs.ts b/scripts/changeset/changeset-version-with-docs.ts index 71fbd2d8aef..bbded5b557a 100644 --- a/scripts/changeset/changeset-version-with-docs.ts +++ b/scripts/changeset/changeset-version-with-docs.ts @@ -16,8 +16,12 @@ import { error } from 'console'; */ execSync(`changeset version`); - // Invoke versions' prebuild script (will rewrite version files if needed) - execSync(`pnpm -C packages/versions prebuild`); + /** + * Invoke versions' build script (will rewrite version files if needed) + * and build the versions package, so that fuels-typegen picks up the + * new fuels version when generating the proxy contract below. + */ + execSync(`pnpm -C packages/versions build`); // Invoke fuels' build:proxy script (will rewrite header versions in generated files) execSync(`pnpm -C packages/fuels build:proxy`); From 9bb2002a50de83701e0029070779c6cf2c87ebd6 Mon Sep 17 00:00:00 2001 From: Chad Nehemiah Date: Mon, 21 Oct 2024 10:57:13 -0500 Subject: [PATCH 4/9] ci: add optimized graphql query e2e tests (#3311) --- .changeset/selfish-weeks-agree.md | 3 ++ packages/fuel-gauge/src/e2e-script.test.ts | 49 +++++++++++++++++++++- 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 .changeset/selfish-weeks-agree.md diff --git a/.changeset/selfish-weeks-agree.md b/.changeset/selfish-weeks-agree.md new file mode 100644 index 00000000000..3577aed90e7 --- /dev/null +++ b/.changeset/selfish-weeks-agree.md @@ -0,0 +1,3 @@ +--- +--- +ci: add optimized graphql query e2e tests \ No newline at end of file diff --git a/packages/fuel-gauge/src/e2e-script.test.ts b/packages/fuel-gauge/src/e2e-script.test.ts index cc078eec72a..674fed133c8 100644 --- a/packages/fuel-gauge/src/e2e-script.test.ts +++ b/packages/fuel-gauge/src/e2e-script.test.ts @@ -1,6 +1,6 @@ /* eslint-disable no-console */ import { DEVNET_NETWORK_URL, TESTNET_NETWORK_URL } from '@internal/utils'; -import { WalletUnlocked, Provider, TransactionType, CHAIN_IDS, rawAssets, assets } from 'fuels'; +import { WalletUnlocked, Provider, TransactionType, CHAIN_IDS, rawAssets, assets, bn } from 'fuels'; import { ScriptMainArgBool } from '../test/typegen'; @@ -120,6 +120,53 @@ describe.each(selectedNetworks)('Live Script Test', (selectedNetwork) => { 15_000 ); + describe('optimized graphql queries', () => { + it('should get the balance of the wallet', { timeout: 15_000 }, async () => { + if (shouldSkip) { + return; + } + + const balance = await provider.getBalance(wallet.address, provider.getBaseAssetId()); + expect(bn(balance).gt(0)); + }); + + it('should get the chain and node info', { timeout: 15_000 }, async () => { + if (shouldSkip) { + return; + } + + const chainInfo = await provider.fetchChainAndNodeInfo(); + expect(chainInfo).toBeDefined(); + }); + + it('should get latest block height', { timeout: 15_000 }, async () => { + if (shouldSkip) { + return; + } + + const blockNumber = await provider.getBlockNumber(); + expect(bn(blockNumber).gt(0)); + }); + + it('should get the latest block', { timeout: 15_000 }, async () => { + if (shouldSkip) { + return; + } + + const block = await provider.getBlock('latest'); + expect(block).toBeDefined(); + }); + + it('should get block with transactions', { timeout: 15_000 }, async () => { + if (shouldSkip) { + return; + } + + const block = await provider.getBlockWithTransactions('latest'); + expect(block).toBeDefined(); + }); + }); + it(`should have correct assets`, () => { if (shouldSkip) { return; From 3c6ffb70590a484dcaabb6c8732acc73714aa218 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Oct 2024 16:41:00 +0000 Subject: [PATCH 5/9] build(deps): bump memfs from 4.11.1 to 4.14.0 (#3348) --- .changeset/loud-hotels-knock.md | 4 +++ package.json | 2 +- pnpm-lock.yaml | 46 +++++++++++++++++---------------- 3 files changed, 29 insertions(+), 23 deletions(-) create mode 100644 .changeset/loud-hotels-knock.md diff --git a/.changeset/loud-hotels-knock.md b/.changeset/loud-hotels-knock.md new file mode 100644 index 00000000000..611cfd0f87f --- /dev/null +++ b/.changeset/loud-hotels-knock.md @@ -0,0 +1,4 @@ +--- +--- + +build(deps): bump memfs from 4.11.1 to 4.14.0 diff --git a/package.json b/package.json index 753666fcd18..52235571fbc 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,7 @@ "eslint-plugin-tsdoc": "^0.3.0", "glob": "^10.4.5", "knip": "^5.30.6", - "memfs": "^4.11.1", + "memfs": "^4.14.0", "nodemon": "^3.1.7", "npm-run-all": "^4.1.5", "nyc": "^17.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d59caa39192..bc704afc787 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -129,8 +129,8 @@ importers: specifier: ^5.30.6 version: 5.30.6(@types/node@22.5.5)(typescript@5.6.2) memfs: - specifier: ^4.11.1 - version: 4.11.1 + specifier: ^4.14.0 + version: 4.14.0 nodemon: specifier: ^3.1.7 version: 3.1.7 @@ -6481,6 +6481,7 @@ packages: '@web3modal/wagmi@5.0.0': resolution: {integrity: sha512-AegPzmmArOpELk9N44/BzNHKE50Fp19nfDJ/eVq8fM/yqDSlq7Gj2D1sEeZuEeXQGxgoAKNOWOlKP6IoQ/+s6g==} + deprecated: Web3Modal is now Reown AppKit. Please follow the upgrade guide at https://docs.reown.com/appkit/upgrade/from-w3m-to-reown peerDependencies: '@wagmi/connectors': '>=4' '@wagmi/core': '>=2.0.0' @@ -8593,6 +8594,7 @@ packages: eciesjs@0.3.20: resolution: {integrity: sha512-Rz5AB8v9+xmMdS/R7RzWPe/R8DP5QfyrkA6ce4umJopoB5su2H2aDy/GcgIfwhmCwxnBkqGf/PbGzmKcGtIgGA==} + deprecated: Please upgrade to v0.4+ edge-paths@3.0.5: resolution: {integrity: sha512-sB7vSrDnFa4ezWQk9nZ/n0FdpdUuC6R1EOrlU3DL+bovcNFK28rqu2emmAUjujYEJTWIgQGqgVVWUZXMnc8iWg==} @@ -11511,8 +11513,8 @@ packages: resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} engines: {node: '>= 4.0.0'} - memfs@4.11.1: - resolution: {integrity: sha512-LZcMTBAgqUUKNXZagcZxvXXfgF1bHX7Y7nQ0QyEiNbRJgE29GhgPd8Yna1VQcLlPiHt/5RFJMWYN9Uv/VPNvjQ==} + memfs@4.14.0: + resolution: {integrity: sha512-JUeY0F/fQZgIod31Ja1eJgiSxLn7BfQlCnqhwXFBzFHEw63OdLK7VJUJ7bnzNsWgCyoUP5tEp1VRY8rDaYzqOA==} engines: {node: '>= 4.0.0'} memoize-one@5.2.1: @@ -20655,21 +20657,21 @@ snapshots: '@jridgewell/sourcemap-codec': 1.5.0 optional: true - '@jsonjoy.com/base64@1.1.2(tslib@2.6.3)': + '@jsonjoy.com/base64@1.1.2(tslib@2.7.0)': dependencies: - tslib: 2.6.3 + tslib: 2.7.0 - '@jsonjoy.com/json-pack@1.0.4(tslib@2.6.3)': + '@jsonjoy.com/json-pack@1.0.4(tslib@2.7.0)': dependencies: - '@jsonjoy.com/base64': 1.1.2(tslib@2.6.3) - '@jsonjoy.com/util': 1.3.0(tslib@2.6.3) + '@jsonjoy.com/base64': 1.1.2(tslib@2.7.0) + '@jsonjoy.com/util': 1.3.0(tslib@2.7.0) hyperdyperid: 1.2.0 - thingies: 1.21.0(tslib@2.6.3) - tslib: 2.6.3 + thingies: 1.21.0(tslib@2.7.0) + tslib: 2.7.0 - '@jsonjoy.com/util@1.3.0(tslib@2.6.3)': + '@jsonjoy.com/util@1.3.0(tslib@2.7.0)': dependencies: - tslib: 2.6.3 + tslib: 2.7.0 '@kamilkisiela/fast-url-parser@1.1.4': {} @@ -31369,12 +31371,12 @@ snapshots: dependencies: fs-monkey: 1.0.4 - memfs@4.11.1: + memfs@4.14.0: dependencies: - '@jsonjoy.com/json-pack': 1.0.4(tslib@2.6.3) - '@jsonjoy.com/util': 1.3.0(tslib@2.6.3) - tree-dump: 1.0.2(tslib@2.6.3) - tslib: 2.6.3 + '@jsonjoy.com/json-pack': 1.0.4(tslib@2.7.0) + '@jsonjoy.com/util': 1.3.0(tslib@2.7.0) + tree-dump: 1.0.2(tslib@2.7.0) + tslib: 2.7.0 memoize-one@5.2.1: {} @@ -35469,9 +35471,9 @@ snapshots: dependencies: any-promise: 1.3.0 - thingies@1.21.0(tslib@2.6.3): + thingies@1.21.0(tslib@2.7.0): dependencies: - tslib: 2.6.3 + tslib: 2.7.0 thread-stream@0.15.2: dependencies: @@ -35594,9 +35596,9 @@ snapshots: merge-source-map: 1.0.4 nanobench: 2.1.1 - tree-dump@1.0.2(tslib@2.6.3): + tree-dump@1.0.2(tslib@2.7.0): dependencies: - tslib: 2.6.3 + tslib: 2.7.0 tree-kill@1.2.2: {} From 955d8cc736da5658f3aee22d0c8d2f2eaec97b6a Mon Sep 17 00:00:00 2001 From: Daniel Bate Date: Tue, 22 Oct 2024 00:03:31 +0700 Subject: [PATCH 6/9] chore: increase benchmark iterations (#3303) --- .changeset/rare-drinks-bake.md | 4 ++++ internal/benchmarks/src/config.ts | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 .changeset/rare-drinks-bake.md diff --git a/.changeset/rare-drinks-bake.md b/.changeset/rare-drinks-bake.md new file mode 100644 index 00000000000..03798f5399b --- /dev/null +++ b/.changeset/rare-drinks-bake.md @@ -0,0 +1,4 @@ +--- +--- + +chore: increase benchmark iterations diff --git a/internal/benchmarks/src/config.ts b/internal/benchmarks/src/config.ts index 512c66d155c..27eb67f07ae 100644 --- a/internal/benchmarks/src/config.ts +++ b/internal/benchmarks/src/config.ts @@ -4,7 +4,7 @@ import { bench } from 'vitest'; export const isDevnet = process.env.DEVNET_WALLET_PVT_KEY !== undefined; -const iterations = isDevnet ? 1 : 10; +const iterations = isDevnet ? 1 : 20; export const runBenchmark = (name: string, benchmarkFn: () => Promise) => { bench( From eede61c1a120410e3655649713b3979f82f560ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Mon, 21 Oct 2024 14:26:00 -0300 Subject: [PATCH 7/9] feat: implement batch transfer to contracts (#3335) Co-authored-by: Daniel Bate Co-authored-by: Chad Nehemiah --- .changeset/popular-seals-move.md | 5 + .../cookbook/transferring-assets.test.ts | 68 ++++++++-- .../src/guide/wallets/wallet-transferring.md | 18 ++- packages/account/src/account.ts | 72 ++++++----- ...formatTransferToContractScriptData.test.ts | 50 ++++---- .../formatTransferToContractScriptData.ts | 91 +++++++------- packages/fuel-gauge/src/contract.test.ts | 117 +++++++++++++++++- 7 files changed, 307 insertions(+), 114 deletions(-) create mode 100644 .changeset/popular-seals-move.md diff --git a/.changeset/popular-seals-move.md b/.changeset/popular-seals-move.md new file mode 100644 index 00000000000..8e008745ab2 --- /dev/null +++ b/.changeset/popular-seals-move.md @@ -0,0 +1,5 @@ +--- +"@fuel-ts/account": patch +--- + +feat: implement batch transfer to contracts diff --git a/apps/docs-snippets/src/guide/cookbook/transferring-assets.test.ts b/apps/docs-snippets/src/guide/cookbook/transferring-assets.test.ts index b4e789a28ef..a173df234cc 100644 --- a/apps/docs-snippets/src/guide/cookbook/transferring-assets.test.ts +++ b/apps/docs-snippets/src/guide/cookbook/transferring-assets.test.ts @@ -1,7 +1,8 @@ -import { Address, BN, Wallet } from 'fuels'; -import { launchTestNode } from 'fuels/test-utils'; +import type { ContractTransferParams, ReceiptTransfer } from 'fuels'; +import { BN, ReceiptType, Wallet } from 'fuels'; +import { launchTestNode, TestAssetId } from 'fuels/test-utils'; -import { CounterFactory } from '../../../test/typegen'; +import { CounterFactory, TokenFactory } from '../../../test/typegen'; /** * @group node @@ -111,7 +112,7 @@ describe('Transferring Assets', () => { // #endregion transferring-assets-3 }); - it('should successfully prepare transfer transaction request', async () => { + it('should successfully transfer to contract', async () => { using launched = await launchTestNode({ contractsConfigs: [ { @@ -124,25 +125,72 @@ describe('Transferring Assets', () => { wallets: [sender], contracts: [deployedContract], } = launched; - const contractId = Address.fromAddressOrString(deployedContract.id); // #region transferring-assets-4 // #import { Wallet, BN }; - // #context const senderWallet = Wallet.fromPrivateKey('...'); + // #context const sender = Wallet.fromPrivateKey('...'); const amountToTransfer = 400; const assetId = provider.getBaseAssetId(); - // #context const contractId = Address.fromAddressOrString('0x123...'); + const contractId = deployedContract.id; const contractBalance = await deployedContract.getBalance(assetId); const tx = await sender.transferToContract(contractId, amountToTransfer, assetId); - await tx.waitForResult(); - expect(new BN(contractBalance).toNumber()).toBe(0); - await tx.waitForResult(); + expect(new BN(contractBalance).toNumber()).toBe(0); expect(new BN(await deployedContract.getBalance(assetId)).toNumber()).toBe(amountToTransfer); // #endregion transferring-assets-4 }); + + it('should successfully batch transfer to contracts', async () => { + using launched = await launchTestNode({ + contractsConfigs: [ + { + factory: CounterFactory, + }, + { + factory: TokenFactory, + }, + ], + }); + const { + provider, + wallets: [sender], + contracts: [contract1, contract2], + } = launched; + + // #region transferring-assets-5 + const baseAssetId = provider.getBaseAssetId(); + const assetA = TestAssetId.A.value; + + const contractTransferParams: ContractTransferParams[] = [ + { + contractId: contract1.id, + amount: 999, + assetId: baseAssetId, + }, + { + contractId: contract1.id, + amount: 550, + assetId: assetA, + }, + { + contractId: contract2.id, + amount: 200, + assetId: assetA, + }, + ]; + + const submit = await sender.batchTransferToContracts(contractTransferParams); + const txResult = await submit.waitForResult(); + // #endregion transferring-assets-5 + + const transferReceipts = txResult.receipts.filter( + (receipt) => receipt.type === ReceiptType.Transfer + ) as ReceiptTransfer[]; + + expect(transferReceipts.length).toBe(contractTransferParams.length); + }); }); diff --git a/apps/docs/src/guide/wallets/wallet-transferring.md b/apps/docs/src/guide/wallets/wallet-transferring.md index 9025df598c9..a3bdb8d55d5 100644 --- a/apps/docs/src/guide/wallets/wallet-transferring.md +++ b/apps/docs/src/guide/wallets/wallet-transferring.md @@ -30,6 +30,12 @@ This method also creates a `ScriptTransactionRequest` and populates it with the <<< @/../../docs-snippets/src/guide/cookbook/transferring-assets.test.ts#transferring-assets-3{ts:line-numbers} +## Transferring Assets To Multiple Wallets + +To transfer assets to multiple wallets, use the `Account.batchTransfer` method: + +<<< @/../../docs-snippets/src/guide/wallets/wallet-transferring.test.ts#wallet-transferring-6{ts:line-numbers} + ## Transferring Assets To Contracts When transferring assets to a deployed contract, we use the `transferToContract` method, which shares a similar parameter structure with the `transfer` method. @@ -42,15 +48,17 @@ Here's an example demonstrating how to use `transferToContract`: <<< @/../../docs-snippets/src/guide/cookbook/transferring-assets.test.ts#transferring-assets-4{ts:line-numbers} -Always remember to call the `waitForResult()` function on the transaction response. That ensures the transaction has been mined successfully before proceeding. +_Note: Use `transferToContract` exclusively for transfers to a contract. For transfers to an account address, use `transfer` instead._ -## Transferring Assets To Multiple Wallets +## Transferring Assets To Multiple Contracts -To transfer assets to multiple wallets, use the `Account.batchTransfer` method: +Similar to the `Account.batchTransfer` method, you can transfer multiple assets to multiple contracts using the `Account.batchTransferToContracts` method. Here's how it works: -<<< @/../../docs-snippets/src/guide/wallets/wallet-transferring.test.ts#wallet-transferring-6{ts:line-numbers} +<<< @/../../docs-snippets/src/guide/cookbook/transferring-assets.test.ts#transferring-assets-5{ts:line-numbers} + +Always remember to call the `waitForResult()` function on the transaction response. That ensures the transaction has been mined successfully before proceeding. -This section demonstrates additional examples of transferring assets between wallets and to contracts. +_Note: Use `batchTransferToContracts` solely for transferring assets to contracts. Do not use account addresses with this method. For multiple account transfers, use `batchTransfer` instead._ ## Checking Balances diff --git a/packages/account/src/account.ts b/packages/account/src/account.ts index 1ee9006d0c5..112aaaaa318 100644 --- a/packages/account/src/account.ts +++ b/packages/account/src/account.ts @@ -59,6 +59,12 @@ export type TransferParams = { assetId?: BytesLike; }; +export type ContractTransferParams = { + contractId: string | AbstractAddress; + amount: BigNumberish; + assetId?: BytesLike; +}; + export type EstimatedTxParams = Pick< TransactionCost, 'estimatedPredicates' | 'addedSignatures' | 'requiredQuantities' | 'updateMaxFee' | 'gasPrice' @@ -433,41 +439,50 @@ export class Account extends AbstractAccount { assetId?: BytesLike, txParams: TxParamsType = {} ): Promise { - if (bn(amount).lte(0)) { - throw new FuelError( - ErrorCode.INVALID_TRANSFER_AMOUNT, - 'Transfer amount must be a positive number.' - ); - } - - const contractAddress = Address.fromAddressOrString(contractId); - const assetIdToTransfer = assetId ?? this.provider.getBaseAssetId(); - const { script, scriptData } = await assembleTransferToContractScript({ - hexlifiedContractId: contractAddress.toB256(), - amountToTransfer: bn(amount), - assetId: assetIdToTransfer, - }); + return this.batchTransferToContracts([{ amount, assetId, contractId }], txParams); + } + async batchTransferToContracts( + contractTransferParams: ContractTransferParams[], + txParams: TxParamsType = {} + ): Promise { let request = new ScriptTransactionRequest({ ...txParams, - script, - scriptData, }); - request.addContractInputAndOutput(contractAddress); + const quantities: CoinQuantity[] = []; - const txCost = await this.getTransactionCost(request, { - quantities: [{ amount: bn(amount), assetId: String(assetIdToTransfer) }], - }); + const transferParams = contractTransferParams.map((transferParam) => { + const amount = bn(transferParam.amount); + const contractAddress = Address.fromAddressOrString(transferParam.contractId); - request = this.validateGasLimitAndMaxFee({ - transactionRequest: request, - gasUsed: txCost.gasUsed, - maxFee: txCost.maxFee, - txParams, + const assetId = transferParam.assetId + ? hexlify(transferParam.assetId) + : this.provider.getBaseAssetId(); + + if (amount.lte(0)) { + throw new FuelError( + ErrorCode.INVALID_TRANSFER_AMOUNT, + 'Transfer amount must be a positive number.' + ); + } + + request.addContractInputAndOutput(contractAddress); + quantities.push({ amount, assetId }); + + return { + amount, + contractId: contractAddress.toB256(), + assetId, + }; }); - await this.fund(request, txCost); + const { script, scriptData } = await assembleTransferToContractScript(transferParams); + + request.script = script; + request.scriptData = scriptData; + + request = await this.estimateAndFundTransaction(request, txParams, { quantities }); return this.sendTransaction(request); } @@ -693,10 +708,11 @@ export class Account extends AbstractAccount { /** @hidden * */ private async estimateAndFundTransaction( transactionRequest: ScriptTransactionRequest, - txParams: TxParamsType + txParams: TxParamsType, + costParams?: TransactionCostParams ) { let request = transactionRequest; - const txCost = await this.getTransactionCost(request); + const txCost = await this.getTransactionCost(request, costParams); request = this.validateGasLimitAndMaxFee({ transactionRequest: request, gasUsed: txCost.gasUsed, diff --git a/packages/account/src/utils/formatTransferToContractScriptData.test.ts b/packages/account/src/utils/formatTransferToContractScriptData.test.ts index 51604297e4b..956677fe332 100644 --- a/packages/account/src/utils/formatTransferToContractScriptData.test.ts +++ b/packages/account/src/utils/formatTransferToContractScriptData.test.ts @@ -1,7 +1,7 @@ import { BigNumberCoder } from '@fuel-ts/abi-coder'; -import { ZeroBytes32 } from '@fuel-ts/address/configs'; +import { getRandomB256 } from '@fuel-ts/address'; import type { BytesLike } from '@fuel-ts/interfaces'; -import type { BigNumberish } from '@fuel-ts/math'; +import { bn, type BigNumberish } from '@fuel-ts/math'; import * as arrayifyMod from '@fuel-ts/utils'; import { @@ -18,26 +18,30 @@ describe('util', () => { }); it('should ensure "composeScriptForTransferringToContract" returns script just fine', async () => { - const hexlifiedContractId = '0x1234567890123456789012345678901234567890'; - const amountToTransfer: BigNumberish = 0; - const assetId: BytesLike = ZeroBytes32; + const contractId = '0xf3eb53ed00347d305fc6f6e3a57e91ea6c3182a9efc253488db29494f63c9610'; + const assetId: BytesLike = '0x0f622143ec845f9095bdf02d80273ac48556efcf9f95c1fdadb9351fd8ffcd24'; + const amount: BigNumberish = bn(0); - const { script, scriptData } = await assembleTransferToContractScript({ - hexlifiedContractId, - amountToTransfer, - assetId, - }); + const { script, scriptData } = await assembleTransferToContractScript([ + { + contractId, + amount, + assetId, + }, + ]); expect(script).toStrictEqual( new Uint8Array([ - 97, 64, 0, 10, 80, 69, 0, 32, 93, 73, 16, 0, 80, 77, 16, 8, 60, 65, 36, 192, 36, 4, 0, 0, + 97, 64, 0, 10, 80, 69, 0, 0, 80, 73, 16, 32, 93, 77, 32, 0, 80, 81, 32, 8, 60, 69, 53, 0, + 36, 4, 0, 0, ]) ); expect(scriptData).toStrictEqual( new Uint8Array([ - 18, 52, 86, 120, 144, 18, 52, 86, 120, 144, 18, 52, 86, 120, 144, 18, 52, 86, 120, 144, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, + 243, 235, 83, 237, 0, 52, 125, 48, 95, 198, 246, 227, 165, 126, 145, 234, 108, 49, 130, 169, + 239, 194, 83, 72, 141, 178, 148, 148, 246, 60, 150, 16, 0, 0, 0, 0, 0, 0, 0, 0, 15, 98, 33, + 67, 236, 132, 95, 144, 149, 189, 240, 45, 128, 39, 58, 196, 133, 86, 239, 207, 159, 149, + 193, 253, 173, 185, 53, 31, 216, 255, 205, 36, ]) ); }); @@ -51,15 +55,17 @@ describe('util', () => { const arrayify = vi.spyOn(arrayifyMod, 'arrayify').mockReturnValue(Uint8Array.from(byte)); - const hexlifiedContractId = '0x1234567890123456789012345678901234567890'; - const amountToTransfer: BigNumberish = 0; - const assetId: BytesLike = ZeroBytes32; + const contractId = getRandomB256(); + const amount: BigNumberish = bn(0); + const assetId: BytesLike = getRandomB256(); - const scriptData = formatTransferToContractScriptData({ - hexlifiedContractId, - amountToTransfer, - assetId, - }); + const scriptData = formatTransferToContractScriptData([ + { + contractId, + amount, + assetId, + }, + ]); expect(scriptData).toStrictEqual(Uint8Array.from([].concat(...Array(3).fill(byte)))); diff --git a/packages/account/src/utils/formatTransferToContractScriptData.ts b/packages/account/src/utils/formatTransferToContractScriptData.ts index eafd0d70ee8..d290ccbd949 100644 --- a/packages/account/src/utils/formatTransferToContractScriptData.ts +++ b/packages/account/src/utils/formatTransferToContractScriptData.ts @@ -1,67 +1,64 @@ -import { BigNumberCoder } from '@fuel-ts/abi-coder'; -import type { B256Address, BytesLike } from '@fuel-ts/interfaces'; -import { BN, type BigNumberish } from '@fuel-ts/math'; -import { arrayify } from '@fuel-ts/utils'; +import { ASSET_ID_LEN, BigNumberCoder, CONTRACT_ID_LEN, WORD_SIZE } from '@fuel-ts/abi-coder'; +import { Address } from '@fuel-ts/address'; +import type { BytesLike } from '@fuel-ts/interfaces'; +import { BN } from '@fuel-ts/math'; +import { arrayify, concat } from '@fuel-ts/utils'; import * as asm from '@fuels/vm-asm'; -interface IAssembleTransferToContractScriptParams { - hexlifiedContractId: B256Address; - amountToTransfer: BigNumberish; +interface AssembleTransferToContractParams { + contractId: string; assetId: BytesLike; + amount: BN; } export const formatTransferToContractScriptData = ( - params: IAssembleTransferToContractScriptParams + transferParams: Array ) => { - const { assetId, amountToTransfer, hexlifiedContractId } = params; - const numberCoder = new BigNumberCoder('u64'); - - const encoded = numberCoder.encode(new BN(amountToTransfer).toNumber()); - - const scriptData = Uint8Array.from([ - ...arrayify(hexlifiedContractId), - ...encoded, - ...arrayify(assetId), - ]); - - return scriptData; + return transferParams.reduce((acc, transferParam) => { + const { assetId, amount, contractId } = transferParam; + const encoded = numberCoder.encode(new BN(amount).toNumber()); + const scriptData = concat([ + Address.fromAddressOrString(contractId).toBytes(), + encoded, + arrayify(assetId), + ]); + return concat([acc, scriptData]); + }, new Uint8Array()); }; export const assembleTransferToContractScript = async ( - params: IAssembleTransferToContractScriptParams + transferParams: Array ) => { - const scriptData = formatTransferToContractScriptData(params); + const scriptData = formatTransferToContractScriptData(transferParams); - // implementation extracted from Rust SDK at: - // https://github.com/FuelLabs/fuels-rs/blob/master/packages/fuels-core/src/types/transaction_builders.rs#L240-L272 - // This script loads: - // - a pointer to the contract id, - // - the actual amount - // - a pointer to the asset id - // into the registers 0x10, 0x12, 0x13 - // and calls the TR instruction - - // TODO: Remove ts-expect-error // @ts-expect-error method reference missing in DTS await asm.initWasm(); - // const gtf = fuelAsm.gtf(0x10, 0x00, 0xc); - const gtf = asm.gtf(0x10, 0x00, asm.GTFArgs.ScriptData); - const addi = asm.addi(0x11, 0x10, 0x20); - const lw = asm.lw(0x12, 0x11, 0x0); - const addi2 = asm.addi(0x13, 0x11, 0x8); - const tr = asm.tr(0x10, 0x12, 0x13); - const ret = asm.ret(0x1); + let script = new Uint8Array(); + + transferParams.forEach((_, i) => { + const offset = (CONTRACT_ID_LEN + WORD_SIZE + ASSET_ID_LEN) * i; + + script = concat([ + script, + // Load ScriptData into register 0x10. + asm.gtf(0x10, 0x0, asm.GTFArgs.ScriptData).to_bytes(), + // Add the offset to 0x10 so it will point to the current contract ID, store in 0x11. + asm.addi(0x11, 0x10, offset).to_bytes(), + // Add CONTRACT_ID_LEN to 0x11 to point to the amount in the ScriptData, store in 0x12. + asm.addi(0x12, 0x11, CONTRACT_ID_LEN).to_bytes(), + // Load word to the amount at 0x12 into register 0x13. + asm.lw(0x13, 0x12, 0x0).to_bytes(), + // Add WORD_SIZE to 0x12 to point to the asset ID in the ScriptData, store in 0x14. + asm.addi(0x14, 0x12, WORD_SIZE).to_bytes(), + // Perform the transfer using contract ID in 0x11, amount in 0x13, and asset ID in 0x14. + asm.tr(0x11, 0x13, 0x14).to_bytes(), + ]); + }); - const script = Uint8Array.from([ - ...gtf.to_bytes(), - ...addi.to_bytes(), - ...lw.to_bytes(), - ...addi2.to_bytes(), - ...tr.to_bytes(), - ...ret.to_bytes(), - ]); + // Add return instruction at the end of the script + script = concat([script, asm.ret(0x1).to_bytes()]); return { script, scriptData }; }; diff --git a/packages/fuel-gauge/src/contract.test.ts b/packages/fuel-gauge/src/contract.test.ts index 23948c13eb4..fc962e6a78e 100644 --- a/packages/fuel-gauge/src/contract.test.ts +++ b/packages/fuel-gauge/src/contract.test.ts @@ -15,8 +15,20 @@ import { buildFunctionResult, ReceiptType, } from 'fuels'; -import type { ReceiptMessageOut, ScriptTransactionRequest, TransferParams } from 'fuels'; -import { expectToThrowFuelError, ASSET_A, ASSET_B, launchTestNode } from 'fuels/test-utils'; +import type { + ContractTransferParams, + ReceiptMessageOut, + ReceiptTransfer, + ScriptTransactionRequest, + TransferParams, +} from 'fuels'; +import { + expectToThrowFuelError, + ASSET_A, + ASSET_B, + launchTestNode, + TestAssetId, +} from 'fuels/test-utils'; import type { DeployContractConfig } from 'fuels/test-utils'; import { @@ -25,6 +37,7 @@ import { SmoContractFactory, StorageTestContract, StorageTestContractFactory, + VoidFactory, } from '../test/typegen/contracts'; import { PredicateTrue } from '../test/typegen/predicates/PredicateTrue'; @@ -667,6 +680,106 @@ describe('Contract', () => { expect(finalBalance).toBe(initialBalance + amountToContract.toNumber()); }); + it('should transfer asset to a deployed contract just fine (NON-NATIVE ASSET)', async () => { + using launched = await launchTestNode({ + contractsConfigs, + }); + const { + provider, + wallets: [wallet], + contracts: [contract], + } = launched; + + const initialBalance = new BN(await contract.getBalance(provider.getBaseAssetId())).toNumber(); + const amountToContract = bn(10_000); + const assetId = TestAssetId.A.value; + + const tx = await wallet.transferToContract(contract.id, amountToContract, assetId); + + await tx.waitForResult(); + + const finalBalance = new BN(await contract.getBalance(assetId)).toNumber(); + expect(finalBalance).toBe(initialBalance + amountToContract.toNumber()); + }); + + it('should transfer assets to deployed contracts just fine', async () => { + using launched = await launchTestNode({ + contractsConfigs: [ + { factory: StorageTestContractFactory }, + { factory: VoidFactory }, + { factory: SmoContractFactory }, + ], + }); + const { + provider, + wallets: [wallet], + contracts: [storageContract, voidContract, smoContract], + } = launched; + + const baseAssetId = provider.getBaseAssetId(); + const assetA = TestAssetId.A.value; + const assetB = TestAssetId.B.value; + + const storageId = storageContract.id.toB256(); + const voidId = voidContract.id.toB256(); + const smoId = smoContract.id.toB256(); + + const contractTransferParams: ContractTransferParams[] = [ + { + contractId: storageId, + amount: 999, + }, + { + contractId: storageId, + amount: 550, + assetId: assetA, + }, + { + contractId: voidId, + amount: 200, + assetId: baseAssetId, + }, + { + contractId: voidId, + amount: 133, + assetId: assetB, + }, + { + contractId: smoId, + amount: 800, + assetId: assetA, + }, + { + contractId: voidId, + amount: 166, + assetId: assetB, + }, + { + contractId: storageId, + amount: 2278, + assetId: assetB, + }, + ]; + + const submit = await wallet.batchTransferToContracts(contractTransferParams); + + const { receipts } = await submit.waitForResult(); + + const transferReceipts = receipts.filter( + ({ type }) => type === ReceiptType.Transfer + ) as ReceiptTransfer[]; + + expect(transferReceipts.length).toBe(contractTransferParams.length); + + contractTransferParams.forEach(({ amount, contractId, assetId = baseAssetId }) => { + const foundReceipt = transferReceipts.find( + (r) => r.amount.eq(amount) && r.to === contractId && r.assetId === assetId + ); + + expect(foundReceipt).toBeDefined(); + }); + }); + it('should set "gasLimit" and "maxFee" when transferring amounts to contract just fine', async () => { using launched = await launchTestNode({ contractsConfigs, From 5a004dc0cb47d4630a95df28b5ba59d02c787bd0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Oct 2024 18:02:28 +0000 Subject: [PATCH 8/9] ci(deps): bump thollander/actions-comment-pull-request from 2 to 3 (#3326) --- .changeset/happy-kiwis-sell.md | 4 ++++ .github/workflows/test.yaml | 10 +++++----- 2 files changed, 9 insertions(+), 5 deletions(-) create mode 100644 .changeset/happy-kiwis-sell.md diff --git a/.changeset/happy-kiwis-sell.md b/.changeset/happy-kiwis-sell.md new file mode 100644 index 00000000000..0eff399e32e --- /dev/null +++ b/.changeset/happy-kiwis-sell.md @@ -0,0 +1,4 @@ +--- +--- + +ci(deps): bump thollander/actions-comment-pull-request from 2 to 3 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 33eb5238653..30ac1836d03 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -164,11 +164,11 @@ jobs: run: pnpm test:coverage-diff - name: Report Coverage - uses: thollander/actions-comment-pull-request@v2 + uses: thollander/actions-comment-pull-request@v3 if: ${{ steps.findPr.outputs.number }} with: - filePath: coverage/report/coverage-diff.txt - pr_number: ${{ (steps.findPr.outputs.number) }} - comment_tag: diff + file-path: coverage/report/coverage-diff.txt + pr-number: ${{ (steps.findPr.outputs.number) }} + comment-tag: diff mode: recreate - create_if_not_exists: true + create-if-not-exists: true From bf47c3d9197114b9ccbb5db534f793122e5f0f3f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Oct 2024 18:45:27 +0000 Subject: [PATCH 9/9] build(deps): bump typescript-eslint from 8.5.0 to 8.8.0 (#3260) Co-authored-by: Chad Nehemiah Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Dhaiwat Co-authored-by: Anderson Arboleya --- .changeset/honest-jobs-grow.md | 5 + apps/create-fuels-counter-guide/package.json | 2 +- pnpm-lock.yaml | 108 +++++++++---------- templates/vite/package.json | 2 +- 4 files changed, 61 insertions(+), 56 deletions(-) create mode 100644 .changeset/honest-jobs-grow.md diff --git a/.changeset/honest-jobs-grow.md b/.changeset/honest-jobs-grow.md new file mode 100644 index 00000000000..d0d165d7609 --- /dev/null +++ b/.changeset/honest-jobs-grow.md @@ -0,0 +1,5 @@ +--- +"create-fuels": patch +--- + +build(deps): bump typescript-eslint from 8.5.0 to 8.8.0 diff --git a/apps/create-fuels-counter-guide/package.json b/apps/create-fuels-counter-guide/package.json index cf7f9884ad9..49e2836eb41 100644 --- a/apps/create-fuels-counter-guide/package.json +++ b/apps/create-fuels-counter-guide/package.json @@ -41,7 +41,7 @@ "postcss": "^8.4.47", "tailwindcss": "^3.4.12", "typescript": "~5.6.2", - "typescript-eslint": "^8.5.0", + "typescript-eslint": "^8.8.0", "vite": "^5.4.8", "vitest": "~2.0.5" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bc704afc787..d4b6ec21f7f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -271,8 +271,8 @@ importers: specifier: ~5.6.2 version: 5.6.2 typescript-eslint: - specifier: ^8.5.0 - version: 8.5.0(eslint@8.57.0)(typescript@5.6.2) + specifier: ^8.8.0 + version: 8.8.0(eslint@8.57.0)(typescript@5.6.2) vite: specifier: ^5.4.8 version: 5.4.8(@types/node@22.5.5)(terser@5.34.1) @@ -1406,8 +1406,8 @@ importers: specifier: ~5.6.2 version: 5.6.2 typescript-eslint: - specifier: ^8.5.0 - version: 8.5.0(eslint@8.57.0)(typescript@5.6.2) + specifier: ^8.8.0 + version: 8.8.0(eslint@8.57.0)(typescript@5.6.2) vite: specifier: ^5.4.8 version: 5.4.8(@types/node@22.7.4)(terser@5.34.1) @@ -5840,8 +5840,8 @@ packages: typescript: optional: true - '@typescript-eslint/eslint-plugin@8.5.0': - resolution: {integrity: sha512-lHS5hvz33iUFQKuPFGheAB84LwcJ60G8vKnEhnfcK1l8kGVLro2SFYW6K0/tj8FUhRJ0VHyg1oAfg50QGbPPHw==} + '@typescript-eslint/eslint-plugin@8.8.0': + resolution: {integrity: sha512-wORFWjU30B2WJ/aXBfOm1LX9v9nyt9D3jsSOxC3cCaTQGCW5k4jNpmjFv3U7p/7s4yvdjHzwtv2Sd2dOyhjS0A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 @@ -5877,8 +5877,8 @@ packages: typescript: optional: true - '@typescript-eslint/parser@8.5.0': - resolution: {integrity: sha512-gF77eNv0Xz2UJg/NbpWJ0kqAm35UMsvZf1GHj8D9MRFTj/V3tAciIWXfmPLsAAF/vUlpWPvUDyH1jjsr0cMVWw==} + '@typescript-eslint/parser@8.8.0': + resolution: {integrity: sha512-uEFUsgR+tl8GmzmLjRqz+VrDv4eoaMqMXW7ruXfgThaAShO9JTciKpEsB+TvnfFfbg5IpujgMXVV36gOJRLtZg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -5903,8 +5903,8 @@ packages: resolution: {integrity: sha512-38IxvKB6NAne3g/+MyXMs2Cda/Sz+CEpmm+KLGEM8hx/CvnSRuw51i8ukfwB/B/sESdeTGet1NH1Wj7I0YXswg==} engines: {node: ^16.0.0 || >=18.0.0} - '@typescript-eslint/scope-manager@8.5.0': - resolution: {integrity: sha512-06JOQ9Qgj33yvBEx6tpC8ecP9o860rsR22hWMEd12WcTRrfaFgHr2RB/CA/B+7BMhHkXT4chg2MyboGdFGawYg==} + '@typescript-eslint/scope-manager@8.8.0': + resolution: {integrity: sha512-EL8eaGC6gx3jDd8GwEFEV091210U97J0jeEHrAYvIYosmEGet4wJ+g0SYmLu+oRiAwbSA5AVrt6DxLHfdd+bUg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/type-utils@5.59.0': @@ -5927,8 +5927,8 @@ packages: typescript: optional: true - '@typescript-eslint/type-utils@8.5.0': - resolution: {integrity: sha512-N1K8Ix+lUM+cIDhL2uekVn/ZD7TZW+9/rwz8DclQpcQ9rk4sIL5CAlBC0CugWKREmDjBzI/kQqU4wkg46jWLYA==} + '@typescript-eslint/type-utils@8.8.0': + resolution: {integrity: sha512-IKwJSS7bCqyCeG4NVGxnOP6lLT9Okc3Zj8hLO96bpMkJab+10HIfJbMouLrlpyOr3yrQ1cA413YPFiGd1mW9/Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '*' @@ -5952,8 +5952,8 @@ packages: resolution: {integrity: sha512-BUGslGOb14zUHOUmDB2FfT6SI1CcZEJYfF3qFwBeUrU6srJfzANonwRYHDpLBuzbq3HaoF2XL2hcr01c8f8OaQ==} engines: {node: ^16.0.0 || >=18.0.0} - '@typescript-eslint/types@8.5.0': - resolution: {integrity: sha512-qjkormnQS5wF9pjSi6q60bKUHH44j2APxfh9TQRXK8wbYVeDYYdYJGIROL87LGZZ2gz3Rbmjc736qyL8deVtdw==} + '@typescript-eslint/types@8.8.0': + resolution: {integrity: sha512-QJwc50hRCgBd/k12sTykOJbESe1RrzmX6COk8Y525C9l7oweZ+1lw9JiU56im7Amm8swlz00DRIlxMYLizr2Vw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/typescript-estree@5.59.0': @@ -5992,8 +5992,8 @@ packages: typescript: optional: true - '@typescript-eslint/typescript-estree@8.5.0': - resolution: {integrity: sha512-vEG2Sf9P8BPQ+d0pxdfndw3xIXaoSjliG0/Ejk7UggByZPKXmJmw3GW5jV2gHNQNawBUyfahoSiCFVov0Ruf7Q==} + '@typescript-eslint/typescript-estree@8.8.0': + resolution: {integrity: sha512-ZaMJwc/0ckLz5DaAZ+pNLmHv8AMVGtfWxZe/x2JVEkD5LnmhWiQMMcYT7IY7gkdJuzJ9P14fRy28lUrlDSWYdw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '*' @@ -6019,8 +6019,8 @@ packages: peerDependencies: eslint: ^7.0.0 || ^8.0.0 - '@typescript-eslint/utils@8.5.0': - resolution: {integrity: sha512-6yyGYVL0e+VzGYp60wvkBHiqDWOpT63pdMV2CVG4LVDd5uR6q1qQN/7LafBZtAtNIn/mqXjsSeS5ggv/P0iECw==} + '@typescript-eslint/utils@8.8.0': + resolution: {integrity: sha512-QE2MgfOTem00qrlPgyByaCHay9yb1+9BjnMFnSFkUKQfu7adBXDTnCAivURnuPPAG/qiB+kzKkZKmKfaMT0zVg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -6041,8 +6041,8 @@ packages: resolution: {integrity: sha512-MUaPUe/QRLEffARsmNfmpghuQkW436DvESW+h+M52w0coICHRfD6Np9/K6PdACwnrq1HmuLl+cSPZaJmeVPkSw==} engines: {node: ^16.0.0 || >=18.0.0} - '@typescript-eslint/visitor-keys@8.5.0': - resolution: {integrity: sha512-yTPqMnbAZJNy2Xq2XU8AdtOW9tJIr+UQb64aXB9f3B1498Zx9JorVgFJcZpEc9UBuCCrdzKID2RGAMkYcDtZOw==} + '@typescript-eslint/visitor-keys@8.8.0': + resolution: {integrity: sha512-8mq51Lx6Hpmd7HnA2fcHQo3YgfX1qbccxQOgZcb4tvasu//zXRaA1j5ZRFeCw/VRAdFi4mRM9DnZw0Nu0Q2d1g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@ungap/structured-clone@1.2.0': @@ -15201,8 +15201,8 @@ packages: types-ramda@0.30.1: resolution: {integrity: sha512-1HTsf5/QVRmLzcGfldPFvkVsAdi1db1BBKzi7iW3KBUlOICg/nKnFS+jGqDJS3YD8VsWbAh7JiHeBvbsw8RPxA==} - typescript-eslint@8.5.0: - resolution: {integrity: sha512-uD+XxEoSIvqtm4KE97etm32Tn5MfaZWgWfMMREStLxR6JzvHkc2Tkj7zhTEK5XmtpTmKHNnG8Sot6qDfhHtR1Q==} + typescript-eslint@8.8.0: + resolution: {integrity: sha512-BjIT/VwJ8+0rVO01ZQ2ZVnjE1svFBiRczcpr1t1Yxt7sT25VSbPfrJtDsQ8uQTy2pilX5nI9gwxhUyLULNentw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '*' @@ -23052,14 +23052,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@8.5.0(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2)': + '@typescript-eslint/eslint-plugin@8.8.0(@typescript-eslint/parser@8.8.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2)': dependencies: - '@eslint-community/regexpp': 4.11.0 - '@typescript-eslint/parser': 8.5.0(eslint@8.57.0)(typescript@5.6.2) - '@typescript-eslint/scope-manager': 8.5.0 - '@typescript-eslint/type-utils': 8.5.0(eslint@8.57.0)(typescript@5.6.2) - '@typescript-eslint/utils': 8.5.0(eslint@8.57.0)(typescript@5.6.2) - '@typescript-eslint/visitor-keys': 8.5.0 + '@eslint-community/regexpp': 4.11.1 + '@typescript-eslint/parser': 8.8.0(eslint@8.57.0)(typescript@5.6.2) + '@typescript-eslint/scope-manager': 8.8.0 + '@typescript-eslint/type-utils': 8.8.0(eslint@8.57.0)(typescript@5.6.2) + '@typescript-eslint/utils': 8.8.0(eslint@8.57.0)(typescript@5.6.2) + '@typescript-eslint/visitor-keys': 8.8.0 eslint: 8.57.0 graphemer: 1.4.0 ignore: 5.3.2 @@ -23103,12 +23103,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2)': + '@typescript-eslint/parser@8.8.0(eslint@8.57.0)(typescript@5.6.2)': dependencies: - '@typescript-eslint/scope-manager': 8.5.0 - '@typescript-eslint/types': 8.5.0 - '@typescript-eslint/typescript-estree': 8.5.0(typescript@5.6.2) - '@typescript-eslint/visitor-keys': 8.5.0 + '@typescript-eslint/scope-manager': 8.8.0 + '@typescript-eslint/types': 8.8.0 + '@typescript-eslint/typescript-estree': 8.8.0(typescript@5.6.2) + '@typescript-eslint/visitor-keys': 8.8.0 debug: 4.3.7(supports-color@5.5.0) eslint: 8.57.0 optionalDependencies: @@ -23136,10 +23136,10 @@ snapshots: '@typescript-eslint/types': 6.9.1 '@typescript-eslint/visitor-keys': 6.9.1 - '@typescript-eslint/scope-manager@8.5.0': + '@typescript-eslint/scope-manager@8.8.0': dependencies: - '@typescript-eslint/types': 8.5.0 - '@typescript-eslint/visitor-keys': 8.5.0 + '@typescript-eslint/types': 8.8.0 + '@typescript-eslint/visitor-keys': 8.8.0 '@typescript-eslint/type-utils@5.59.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.6.2)': dependencies: @@ -23165,10 +23165,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@8.5.0(eslint@8.57.0)(typescript@5.6.2)': + '@typescript-eslint/type-utils@8.8.0(eslint@8.57.0)(typescript@5.6.2)': dependencies: - '@typescript-eslint/typescript-estree': 8.5.0(typescript@5.6.2) - '@typescript-eslint/utils': 8.5.0(eslint@8.57.0)(typescript@5.6.2) + '@typescript-eslint/typescript-estree': 8.8.0(typescript@5.6.2) + '@typescript-eslint/utils': 8.8.0(eslint@8.57.0)(typescript@5.6.2) debug: 4.3.7(supports-color@5.5.0) ts-api-utils: 1.3.0(typescript@5.6.2) optionalDependencies: @@ -23185,7 +23185,7 @@ snapshots: '@typescript-eslint/types@6.9.1': {} - '@typescript-eslint/types@8.5.0': {} + '@typescript-eslint/types@8.8.0': {} '@typescript-eslint/typescript-estree@5.59.0(typescript@5.6.2)': dependencies: @@ -23244,10 +23244,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.5.0(typescript@5.6.2)': + '@typescript-eslint/typescript-estree@8.8.0(typescript@5.6.2)': dependencies: - '@typescript-eslint/types': 8.5.0 - '@typescript-eslint/visitor-keys': 8.5.0 + '@typescript-eslint/types': 8.8.0 + '@typescript-eslint/visitor-keys': 8.8.0 debug: 4.3.7(supports-color@5.5.0) fast-glob: 3.3.2 is-glob: 4.0.3 @@ -23303,12 +23303,12 @@ snapshots: - supports-color - typescript - '@typescript-eslint/utils@8.5.0(eslint@8.57.0)(typescript@5.6.2)': + '@typescript-eslint/utils@8.8.0(eslint@8.57.0)(typescript@5.6.2)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@typescript-eslint/scope-manager': 8.5.0 - '@typescript-eslint/types': 8.5.0 - '@typescript-eslint/typescript-estree': 8.5.0(typescript@5.6.2) + '@typescript-eslint/scope-manager': 8.8.0 + '@typescript-eslint/types': 8.8.0 + '@typescript-eslint/typescript-estree': 8.8.0(typescript@5.6.2) eslint: 8.57.0 transitivePeerDependencies: - supports-color @@ -23334,9 +23334,9 @@ snapshots: '@typescript-eslint/types': 6.9.1 eslint-visitor-keys: 3.4.3 - '@typescript-eslint/visitor-keys@8.5.0': + '@typescript-eslint/visitor-keys@8.8.0': dependencies: - '@typescript-eslint/types': 8.5.0 + '@typescript-eslint/types': 8.8.0 eslint-visitor-keys: 3.4.3 '@ungap/structured-clone@1.2.0': {} @@ -35885,11 +35885,11 @@ snapshots: dependencies: ts-toolbelt: 9.6.0 - typescript-eslint@8.5.0(eslint@8.57.0)(typescript@5.6.2): + typescript-eslint@8.8.0(eslint@8.57.0)(typescript@5.6.2): dependencies: - '@typescript-eslint/eslint-plugin': 8.5.0(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2) - '@typescript-eslint/parser': 8.5.0(eslint@8.57.0)(typescript@5.6.2) - '@typescript-eslint/utils': 8.5.0(eslint@8.57.0)(typescript@5.6.2) + '@typescript-eslint/eslint-plugin': 8.8.0(@typescript-eslint/parser@8.8.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2) + '@typescript-eslint/parser': 8.8.0(eslint@8.57.0)(typescript@5.6.2) + '@typescript-eslint/utils': 8.8.0(eslint@8.57.0)(typescript@5.6.2) optionalDependencies: typescript: 5.6.2 transitivePeerDependencies: diff --git a/templates/vite/package.json b/templates/vite/package.json index 9ba09b19748..5a7f3be0280 100644 --- a/templates/vite/package.json +++ b/templates/vite/package.json @@ -40,7 +40,7 @@ "postcss": "^8.4.47", "tailwindcss": "^3.4.12", "typescript": "~5.6.2", - "typescript-eslint": "^8.5.0", + "typescript-eslint": "^8.8.0", "vite": "^5.4.8", "vitest": "~2.0.5" }