Skip to content

Commit

Permalink
Merge branch 'master' into db/chore/exclude-modules-templates
Browse files Browse the repository at this point in the history
  • Loading branch information
Torres-ssf authored Oct 21, 2024
2 parents 29a3b50 + 9dba357 commit 94e145d
Show file tree
Hide file tree
Showing 18 changed files with 226 additions and 54 deletions.
4 changes: 4 additions & 0 deletions .changeset/eleven-baboons-help.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
---

build(deps): bump eslint-plugin-react from 7.35.0 to 7.37.1
8 changes: 8 additions & 0 deletions .changeset/lucky-avocados-add.md
Original file line number Diff line number Diff line change
@@ -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`
6 changes: 6 additions & 0 deletions .changeset/tasty-hounds-know.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@fuel-ts/versions": patch
"@internal/forc": patch
---

chore: upgrading `forc` to `0.66.2`
57 changes: 57 additions & 0 deletions apps/docs-snippets2/src/cookbook/combining-utxos.ts
Original file line number Diff line number Diff line change
@@ -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
4 changes: 4 additions & 0 deletions apps/docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
},
],
},
{
Expand Down
11 changes: 11 additions & 0 deletions apps/docs/src/guide/cookbook/combining-utxos.md
Original file line number Diff line number Diff line change
@@ -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}
6 changes: 6 additions & 0 deletions apps/docs/src/guide/errors/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
2 changes: 1 addition & 1 deletion internal/forc/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.65.2
0.66.2
2 changes: 1 addition & 1 deletion internal/fuel-core/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.39.0
0.40.0
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@
"eslint-plugin-jsdoc": "^46.8.2",
"eslint-plugin-jsx-a11y": "^6.9.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-react": "^7.35.0",
"eslint-plugin-react": "^7.37.1",
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-tsdoc": "^0.3.0",
"glob": "^10.4.5",
Expand Down
6 changes: 4 additions & 2 deletions packages/account/src/account.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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.',
});
});

Expand Down
8 changes: 7 additions & 1 deletion packages/account/src/providers/provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}`
)
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand All @@ -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);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/errors/src/error-codes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',

Expand Down
50 changes: 50 additions & 0 deletions packages/fuel-gauge/src/mapped-error-messages.test.ts
Original file line number Diff line number Diff line change
@@ -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.',
});
});
});
22 changes: 0 additions & 22 deletions packages/fuel-gauge/src/not-enough-coins.test.ts

This file was deleted.

4 changes: 2 additions & 2 deletions packages/versions/src/lib/getBuiltinVersions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import type { Versions } from './types';

export function getBuiltinVersions(): Versions {
return {
FORC: '0.65.2',
FUEL_CORE: '0.39.0',
FORC: '0.66.2',
FUEL_CORE: '0.40.0',
FUELS: '0.96.1',
};
}
Loading

0 comments on commit 94e145d

Please sign in to comment.