From 584f0f26710eee62cba96c5b53093351ce8f23ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Wed, 8 May 2024 08:28:04 -0300 Subject: [PATCH 01/17] add ramda to program package --- packages/program/package.json | 4 ++++ packages/program/src/functions/base-invocation-scope.ts | 1 + pnpm-lock.yaml | 7 +++++++ 3 files changed, 12 insertions(+) diff --git a/packages/program/package.json b/packages/program/package.json index a73981eea59..8b2bde12acf 100644 --- a/packages/program/package.json +++ b/packages/program/package.json @@ -25,6 +25,7 @@ }, "license": "Apache-2.0", "dependencies": { + "ramda": "^0.29.0", "@fuel-ts/abi-coder": "workspace:*", "@fuel-ts/account": "workspace:*", "@fuel-ts/address": "workspace:*", @@ -34,5 +35,8 @@ "@fuel-ts/transactions": "workspace:*", "@fuel-ts/utils": "workspace:*", "@fuels/vm-asm": "0.49.0" + }, + "devDependencies": { + "@types/ramda": "^0.29.3" } } diff --git a/packages/program/src/functions/base-invocation-scope.ts b/packages/program/src/functions/base-invocation-scope.ts index 22e714079d1..0f7bf52444a 100644 --- a/packages/program/src/functions/base-invocation-scope.ts +++ b/packages/program/src/functions/base-invocation-scope.ts @@ -16,6 +16,7 @@ import { bn } from '@fuel-ts/math'; import { InputType, TransactionType } from '@fuel-ts/transactions'; import { isDefined } from '@fuel-ts/utils'; import * as asm from '@fuels/vm-asm'; +import { clone } from 'ramda'; import { getContractCallScript } from '../contract-call-script'; import { POINTER_DATA_OFFSET } from '../script-request'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7db304181df..57f4b9737c4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1033,6 +1033,13 @@ importers: '@fuels/vm-asm': specifier: 0.49.0 version: 0.49.0 + ramda: + specifier: ^0.29.0 + version: 0.29.0 + devDependencies: + '@types/ramda': + specifier: ^0.29.3 + version: 0.29.3 packages/script: dependencies: From 262ab41fe7b8c21feb2bb921e6d48528ec17add3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Wed, 8 May 2024 08:28:40 -0300 Subject: [PATCH 02/17] refact setDefaultTxParams --- .../src/functions/base-invocation-scope.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/program/src/functions/base-invocation-scope.ts b/packages/program/src/functions/base-invocation-scope.ts index 0f7bf52444a..9eb4053952e 100644 --- a/packages/program/src/functions/base-invocation-scope.ts +++ b/packages/program/src/functions/base-invocation-scope.ts @@ -472,23 +472,24 @@ export class BaseInvocationScope { const gasLimitSpecified = isDefined(this.txParameters?.gasLimit) || this.hasCallParamsGasLimit; const maxFeeSpecified = isDefined(this.txParameters?.maxFee); - const { gasLimit, maxFee: setMaxFee } = transactionRequest; + const { gasLimit: setGasLimit, maxFee: setMaxFee } = transactionRequest; - if (gasLimitSpecified && gasLimit.lt(gasUsed)) { + if (!gasLimitSpecified) { + transactionRequest.gasLimit = gasUsed; + } else if (setGasLimit.lt(gasUsed)) { throw new FuelError( ErrorCode.GAS_LIMIT_TOO_LOW, - `Gas limit '${gasLimit}' is lower than the required: '${gasUsed}'.` + `Gas limit '${setGasLimit}' is lower than the required: '${gasUsed}'.` ); } - if (maxFeeSpecified && maxFee.gt(setMaxFee)) { + if (!maxFeeSpecified) { + transactionRequest.maxFee = maxFee; + } else if (maxFee.gt(setMaxFee)) { throw new FuelError( ErrorCode.MAX_FEE_TOO_LOW, `Max fee '${setMaxFee}' is lower than the required: '${maxFee}'.` ); } - - transactionRequest.gasLimit = gasUsed; - transactionRequest.maxFee = maxFee; } } From 6c9b8fc8bdb26d84ebdad847d48e0d63cffc2cfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Wed, 8 May 2024 08:31:00 -0300 Subject: [PATCH 03/17] add setMaxFee to getTransactionCost return --- packages/account/src/providers/provider.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/account/src/providers/provider.ts b/packages/account/src/providers/provider.ts index 24685e79555..3aaa383f3c2 100644 --- a/packages/account/src/providers/provider.ts +++ b/packages/account/src/providers/provider.ts @@ -164,6 +164,7 @@ export type TransactionCost = { requiredQuantities: CoinQuantity[]; addedSignatures: number; dryRunStatus?: DryRunStatus; + setMaxFee?: boolean; }; // #endregion cost-estimation-1 @@ -1059,7 +1060,7 @@ Supported fuel-core version: ${supportedVersion}.` const txRequestClone = clone(transactionRequestify(transactionRequestLike)); const isScriptTransaction = txRequestClone.type === TransactionType.Script; const baseAssetId = this.getBaseAssetId(); - + const setMaxFee = !txRequestClone.maxFee.eq(0); // Fund with fake UTXOs to avoid not enough funds error // Getting coin quantities from amounts being transferred const coinOutputsQuantities = txRequestClone.getCoinOutputsQuantities(); @@ -1148,6 +1149,7 @@ Supported fuel-core version: ${supportedVersion}.` addedSignatures, estimatedPredicates: txRequestClone.inputs, dryRunStatus, + setMaxFee, }; } From 0afb18825e59dc09507ed383ee99f685bffa3025 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Wed, 8 May 2024 08:31:52 -0300 Subject: [PATCH 04/17] ajust fund method --- packages/account/src/account.ts | 70 ++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/packages/account/src/account.ts b/packages/account/src/account.ts index a7e8e0bc630..f53214d9be5 100644 --- a/packages/account/src/account.ts +++ b/packages/account/src/account.ts @@ -46,7 +46,7 @@ export type TxParamsType = Pick< export type EstimatedTxParams = Pick< TransactionCost, - 'maxFee' | 'estimatedPredicates' | 'addedSignatures' | 'requiredQuantities' + 'estimatedPredicates' | 'addedSignatures' | 'requiredQuantities' | 'setMaxFee' >; const MAX_FUNDING_ATTEMPTS = 2; @@ -252,13 +252,13 @@ export class Account extends AbstractAccount { * @returns A promise that resolves when the resources are added to the transaction. */ async fund(request: T, params: EstimatedTxParams): Promise { - const { addedSignatures, estimatedPredicates, maxFee: fee, requiredQuantities } = params; + const { addedSignatures, estimatedPredicates, requiredQuantities, setMaxFee } = params; + const fee = request.maxFee; const baseAssetId = this.provider.getBaseAssetId(); const requiredInBaseAsset = requiredQuantities.find((quantity) => quantity.assetId === baseAssetId)?.amount || bn(0); - const txRequest = request as T; const requiredQuantitiesWithFee = addAmountToCoinQuantities({ amount: bn(fee), assetId: baseAssetId, @@ -301,57 +301,65 @@ export class Account extends AbstractAccount { ); request.addResources(resources); + request.shiftPredicateData(); + request.updatePredicateGasUsed(estimatedPredicates); - txRequest.shiftPredicateData(); - txRequest.updatePredicateGasUsed(estimatedPredicates); - - const requestToReestimate = clone(txRequest); + const requestToReestimate = clone(request); if (addedSignatures) { Array.from({ length: addedSignatures }).forEach(() => requestToReestimate.addEmptyWitness() ); } - const { maxFee: newFee } = await this.provider.estimateTxGasAndFee({ - transactionRequest: requestToReestimate, - }); - - const totalBaseAssetOnInputs = getAssetAmountInRequestInputs( - request.inputs, - baseAssetId, - baseAssetId - ); - - const totalBaseAssetRequiredWithFee = requiredInBaseAsset.add(newFee); - - if (totalBaseAssetOnInputs.gt(totalBaseAssetRequiredWithFee)) { + if (setMaxFee) { needsToBeFunded = false; } else { - missingQuantities = [ - { - amount: totalBaseAssetRequiredWithFee.sub(totalBaseAssetOnInputs), - assetId: baseAssetId, - }, - ]; + const { maxFee: newFee } = await this.provider.estimateTxGasAndFee({ + transactionRequest: requestToReestimate, + }); + + const totalBaseAssetOnInputs = getAssetAmountInRequestInputs( + request.inputs, + baseAssetId, + baseAssetId + ); + + const totalBaseAssetRequiredWithFee = requiredInBaseAsset.add(newFee); + + if (totalBaseAssetOnInputs.gt(totalBaseAssetRequiredWithFee)) { + needsToBeFunded = false; + } else { + missingQuantities = [ + { + amount: totalBaseAssetRequiredWithFee.sub(totalBaseAssetOnInputs), + assetId: baseAssetId, + }, + ]; + } } fundingAttempts += 1; } - txRequest.shiftPredicateData(); - txRequest.updatePredicateGasUsed(estimatedPredicates); + request.shiftPredicateData(); + request.updatePredicateGasUsed(estimatedPredicates); - const requestToReestimate = clone(txRequest); + const requestToReestimate = clone(request); if (addedSignatures) { Array.from({ length: addedSignatures }).forEach(() => requestToReestimate.addEmptyWitness()); } + + if (setMaxFee) { + return request; + } + const { maxFee } = await this.provider.estimateTxGasAndFee({ transactionRequest: requestToReestimate, }); - txRequest.maxFee = maxFee; + request.maxFee = maxFee; - return txRequest; + return request; } /** From d6192350d06b8309fa41a12751824d0493fb0150 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Wed, 8 May 2024 08:32:42 -0300 Subject: [PATCH 05/17] refact method validateGasLimitAndMaxFee --- packages/account/src/account.ts | 40 ++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/packages/account/src/account.ts b/packages/account/src/account.ts index f53214d9be5..5a921a77082 100644 --- a/packages/account/src/account.ts +++ b/packages/account/src/account.ts @@ -381,7 +381,7 @@ export class Account extends AbstractAccount { /** Tx Params */ txParams: TxParamsType = {} ): Promise { - const request = new ScriptTransactionRequest(txParams); + let request = new ScriptTransactionRequest(txParams); const assetIdToTransfer = assetId ?? this.provider.getBaseAssetId(); request.addCoinOutput(Address.fromAddressOrString(destination), amount, assetIdToTransfer); const txCost = await this.provider.getTransactionCost(request, { @@ -389,15 +389,13 @@ export class Account extends AbstractAccount { resourcesOwner: this, }); - this.validateGasLimitAndMaxFee({ + request = this.validateGasLimitAndMaxFee({ + transactionRequest: request, gasUsed: txCost.gasUsed, maxFee: txCost.maxFee, txParams, }); - request.gasLimit = txCost.gasUsed; - request.maxFee = txCost.maxFee; - await this.fund(request, txCost); return request; @@ -467,7 +465,7 @@ export class Account extends AbstractAccount { assetId: assetIdToTransfer, }); - const request = new ScriptTransactionRequest({ + let request = new ScriptTransactionRequest({ ...txParams, script, scriptData, @@ -480,15 +478,13 @@ export class Account extends AbstractAccount { quantitiesToContract: [{ amount: bn(amount), assetId: String(assetIdToTransfer) }], }); - this.validateGasLimitAndMaxFee({ + request = this.validateGasLimitAndMaxFee({ + transactionRequest: request, gasUsed: txCost.gasUsed, maxFee: txCost.maxFee, txParams, }); - request.gasLimit = txCost.gasUsed; - request.maxFee = txCost.maxFee; - await this.fund(request, txCost); return this.sendTransaction(request); @@ -527,20 +523,18 @@ export class Account extends AbstractAccount { const params: ScriptTransactionRequestLike = { script, ...txParams }; const baseAssetId = this.provider.getBaseAssetId(); - const request = new ScriptTransactionRequest(params); + let request = new ScriptTransactionRequest(params); const quantitiesToContract = [{ amount: bn(amount), assetId: baseAssetId }]; const txCost = await this.provider.getTransactionCost(request, { quantitiesToContract }); - this.validateGasLimitAndMaxFee({ + request = this.validateGasLimitAndMaxFee({ + transactionRequest: request, gasUsed: txCost.gasUsed, maxFee: txCost.maxFee, txParams, }); - request.maxFee = txCost.maxFee; - request.gasLimit = txCost.gasUsed; - await this.fund(request, txCost); return this.sendTransaction(request); @@ -612,26 +606,36 @@ export class Account extends AbstractAccount { } private validateGasLimitAndMaxFee({ - txParams: { gasLimit: setGasLimit, maxFee: setMaxFee }, gasUsed, maxFee, + transactionRequest, + txParams: { gasLimit: setGasLimit, maxFee: setMaxFee }, }: { gasUsed: BN; maxFee: BN; + transactionRequest: ScriptTransactionRequest; txParams: Pick; }) { - if (isDefined(setGasLimit) && gasUsed.gt(setGasLimit)) { + const request = transactionRequestify(transactionRequest) as ScriptTransactionRequest; + + if (!isDefined(setGasLimit)) { + request.gasLimit = gasUsed; + } else if (gasUsed.gt(setGasLimit)) { throw new FuelError( ErrorCode.GAS_LIMIT_TOO_LOW, `Gas limit '${setGasLimit}' is lower than the required: '${gasUsed}'.` ); } - if (isDefined(setMaxFee) && maxFee.gt(setMaxFee)) { + if (!isDefined(setMaxFee)) { + request.maxFee = maxFee; + } else if (maxFee.gt(setMaxFee)) { throw new FuelError( ErrorCode.MAX_FEE_TOO_LOW, `Max fee '${setMaxFee}' is lower than the required: '${maxFee}'.` ); } + + return request; } } From 392f79ba51e0f0ae957962705b516f210c81892b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Wed, 8 May 2024 08:33:53 -0300 Subject: [PATCH 06/17] remove unused flag from BaseInvocationScope TxParams --- .../program/src/functions/base-invocation-scope.ts | 10 ---------- packages/program/src/types.ts | 1 - 2 files changed, 11 deletions(-) diff --git a/packages/program/src/functions/base-invocation-scope.ts b/packages/program/src/functions/base-invocation-scope.ts index 9eb4053952e..ded4c9dc2f5 100644 --- a/packages/program/src/functions/base-invocation-scope.ts +++ b/packages/program/src/functions/base-invocation-scope.ts @@ -275,21 +275,11 @@ export class BaseInvocationScope { // Adding required number of OutputVariables transactionRequest.addVariableOutputs(outputVariables); - const optimizeGas = this.txParameters?.optimizeGas ?? true; - if (this.txParameters?.gasLimit && !optimizeGas) { - transactionRequest.gasLimit = bn(this.txParameters.gasLimit); - const { maxFee: maxFeeForGasLimit } = await this.getProvider().estimateTxGasAndFee({ - transactionRequest, - }); - transactionRequest.maxFee = maxFeeForGasLimit; - } - await this.program.account?.fund(transactionRequest, txCost); if (this.addSignersCallback) { await this.addSignersCallback(transactionRequest); } - return transactionRequest; } diff --git a/packages/program/src/types.ts b/packages/program/src/types.ts index 9662cb4dca9..17d6a32338d 100644 --- a/packages/program/src/types.ts +++ b/packages/program/src/types.ts @@ -41,7 +41,6 @@ export type TxParams = Partial<{ maxFee?: BigNumberish; witnessLimit?: BigNumberish; variableOutputs: number; - optimizeGas?: boolean; }>; /** From f108faf75c1b64408159a239108e79b2d1132c40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Wed, 8 May 2024 08:34:34 -0300 Subject: [PATCH 07/17] avoid mutationg transaction request on fundWithRequiredCoins --- packages/program/src/functions/base-invocation-scope.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/program/src/functions/base-invocation-scope.ts b/packages/program/src/functions/base-invocation-scope.ts index ded4c9dc2f5..8b94e63077e 100644 --- a/packages/program/src/functions/base-invocation-scope.ts +++ b/packages/program/src/functions/base-invocation-scope.ts @@ -258,12 +258,12 @@ export class BaseInvocationScope { * @returns The current instance of the class. */ async fundWithRequiredCoins() { - const transactionRequest = await this.getTransactionRequest(); + let transactionRequest = await this.getTransactionRequest(); + transactionRequest = clone(transactionRequest); const txCost = await this.getTransactionCost(); const { gasUsed, missingContractIds, outputVariables, maxFee } = txCost; this.setDefaultTxParams(transactionRequest, gasUsed, maxFee); - // Clean coin inputs before add new coins to the request transactionRequest.inputs = transactionRequest.inputs.filter((i) => i.type !== InputType.Coin); From 302d29c348f33554b8464c109c61f4b5f3782304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Wed, 8 May 2024 08:34:50 -0300 Subject: [PATCH 08/17] ajusting some tests --- packages/account/src/account.test.ts | 3 ++- packages/fuel-gauge/src/contract.test.ts | 6 +----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/account/src/account.test.ts b/packages/account/src/account.test.ts index fd93147ea3d..94cc53d47da 100644 --- a/packages/account/src/account.test.ts +++ b/packages/account/src/account.test.ts @@ -251,6 +251,8 @@ describe('Account', () => { const request = new ScriptTransactionRequest(); + request.maxFee = fee; + const resourcesToSpend: Resource[] = []; const getResourcesToSpendSpy = vi .spyOn(Account.prototype, 'getResourcesToSpend') @@ -267,7 +269,6 @@ describe('Account', () => { await account.fund(request, { requiredQuantities: quantities, - maxFee: fee, estimatedPredicates: [], addedSignatures: 0, }); diff --git a/packages/fuel-gauge/src/contract.test.ts b/packages/fuel-gauge/src/contract.test.ts index 30ab12e0dc5..580cfa4293c 100644 --- a/packages/fuel-gauge/src/contract.test.ts +++ b/packages/fuel-gauge/src/contract.test.ts @@ -478,7 +478,6 @@ describe('Contract', () => { ]) .txParams({ gasLimit: 4_000_000, - optimizeGas: false, }) .call<[BN, BN]>(); @@ -510,7 +509,6 @@ describe('Contract', () => { const { value } = await invocationScope .txParams({ gasLimit: transactionCost.gasUsed, - optimizeGas: false, }) .call<[string, string]>(); @@ -646,9 +644,7 @@ describe('Contract', () => { const struct = { a: true, b: 1337 }; const invocationScopes = [contract.functions.foo(num), contract.functions.boo(struct)]; const multiCallScope = contract.multiCall(invocationScopes); - await multiCallScope.fundWithRequiredCoins(); - - const transactionRequest = await multiCallScope.getTransactionRequest(); + const transactionRequest = await multiCallScope.fundWithRequiredCoins(); const txRequest = JSON.stringify(transactionRequest); const txRequestParsed = JSON.parse(txRequest); From 7f4419ec9ccc1110b56016509ad8857501871b3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Wed, 8 May 2024 08:41:12 -0300 Subject: [PATCH 09/17] refact code --- packages/account/src/providers/provider.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/account/src/providers/provider.ts b/packages/account/src/providers/provider.ts index 3aaa383f3c2..e7ede26bf42 100644 --- a/packages/account/src/providers/provider.ts +++ b/packages/account/src/providers/provider.ts @@ -1073,7 +1073,6 @@ Supported fuel-core version: ${supportedVersion}.` * Estimate predicates gasUsed */ // Remove gasLimit to avoid gasLimit when estimating predicates - txRequestClone.maxFee = bn(0); if (isScriptTransaction) { txRequestClone.gasLimit = bn(0); } @@ -1098,6 +1097,7 @@ Supported fuel-core version: ${supportedVersion}.` } await this.estimatePredicates(signedRequest); + txRequestClone.updatePredicateGasUsed(signedRequest.inputs); /** * Calculate minGas and maxGas based on the real transaction @@ -1113,8 +1113,6 @@ Supported fuel-core version: ${supportedVersion}.` let outputVariables = 0; let gasUsed = bn(0); - txRequestClone.updatePredicateGasUsed(signedRequest.inputs); - txRequestClone.maxFee = maxFee; if (isScriptTransaction) { txRequestClone.gasLimit = gasLimit; From 6d02711841e5f3f4c7a0e291675ef981b1b894e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Wed, 8 May 2024 08:44:14 -0300 Subject: [PATCH 10/17] add changeset --- .changeset/hot-vans-battle.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/hot-vans-battle.md diff --git a/.changeset/hot-vans-battle.md b/.changeset/hot-vans-battle.md new file mode 100644 index 00000000000..ff1cb006ad3 --- /dev/null +++ b/.changeset/hot-vans-battle.md @@ -0,0 +1,6 @@ +--- +"@fuel-ts/account": patch +"@fuel-ts/program": patch +--- + +fix: avoid overriding user `gasLimit` and `maxFee` inputs From 0452091dd23ff2608de1954f787f8734565b4d65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Wed, 8 May 2024 09:43:35 -0300 Subject: [PATCH 11/17] rename setMaxFee to updateMaxFee --- packages/account/src/account.ts | 61 +++++++++++----------- packages/account/src/providers/provider.ts | 10 ++-- 2 files changed, 35 insertions(+), 36 deletions(-) diff --git a/packages/account/src/account.ts b/packages/account/src/account.ts index 5a921a77082..9cf6741f7d5 100644 --- a/packages/account/src/account.ts +++ b/packages/account/src/account.ts @@ -46,7 +46,7 @@ export type TxParamsType = Pick< export type EstimatedTxParams = Pick< TransactionCost, - 'estimatedPredicates' | 'addedSignatures' | 'requiredQuantities' | 'setMaxFee' + 'estimatedPredicates' | 'addedSignatures' | 'requiredQuantities' | 'updateMaxFee' >; const MAX_FUNDING_ATTEMPTS = 2; @@ -244,15 +244,15 @@ export class Account extends AbstractAccount { } /** - * Adds resources to the transaction enough to fund it. + * Funds a transaction request by adding the necessary resources. * - * @param request - The transaction request. - * @param requiredQuantities - The coin quantities required to execute the transaction. - * @param fee - The estimated transaction fee. - * @returns A promise that resolves when the resources are added to the transaction. + * @template T - The type of the TransactionRequest. + * @param T - request - The transaction request to fund. + * @param EstimatedTxParams - The estimated transaction parameters. + * @returns The funded transaction request. */ async fund(request: T, params: EstimatedTxParams): Promise { - const { addedSignatures, estimatedPredicates, requiredQuantities, setMaxFee } = params; + const { addedSignatures, estimatedPredicates, requiredQuantities, updateMaxFee } = params; const fee = request.maxFee; const baseAssetId = this.provider.getBaseAssetId(); @@ -311,31 +311,30 @@ export class Account extends AbstractAccount { ); } - if (setMaxFee) { - needsToBeFunded = false; - } else { - const { maxFee: newFee } = await this.provider.estimateTxGasAndFee({ - transactionRequest: requestToReestimate, - }); + if (!updateMaxFee) { + break; + } + const { maxFee: newFee } = await this.provider.estimateTxGasAndFee({ + transactionRequest: requestToReestimate, + }); - const totalBaseAssetOnInputs = getAssetAmountInRequestInputs( - request.inputs, - baseAssetId, - baseAssetId - ); + const totalBaseAssetOnInputs = getAssetAmountInRequestInputs( + request.inputs, + baseAssetId, + baseAssetId + ); + + const totalBaseAssetRequiredWithFee = requiredInBaseAsset.add(newFee); - const totalBaseAssetRequiredWithFee = requiredInBaseAsset.add(newFee); - - if (totalBaseAssetOnInputs.gt(totalBaseAssetRequiredWithFee)) { - needsToBeFunded = false; - } else { - missingQuantities = [ - { - amount: totalBaseAssetRequiredWithFee.sub(totalBaseAssetOnInputs), - assetId: baseAssetId, - }, - ]; - } + if (totalBaseAssetOnInputs.gt(totalBaseAssetRequiredWithFee)) { + needsToBeFunded = false; + } else { + missingQuantities = [ + { + amount: totalBaseAssetRequiredWithFee.sub(totalBaseAssetOnInputs), + assetId: baseAssetId, + }, + ]; } fundingAttempts += 1; @@ -349,7 +348,7 @@ export class Account extends AbstractAccount { Array.from({ length: addedSignatures }).forEach(() => requestToReestimate.addEmptyWitness()); } - if (setMaxFee) { + if (!updateMaxFee) { return request; } diff --git a/packages/account/src/providers/provider.ts b/packages/account/src/providers/provider.ts index e7ede26bf42..51a1c3dcf48 100644 --- a/packages/account/src/providers/provider.ts +++ b/packages/account/src/providers/provider.ts @@ -164,7 +164,7 @@ export type TransactionCost = { requiredQuantities: CoinQuantity[]; addedSignatures: number; dryRunStatus?: DryRunStatus; - setMaxFee?: boolean; + updateMaxFee?: boolean; }; // #endregion cost-estimation-1 @@ -484,8 +484,8 @@ export default class Provider { if (!isMajorSupported || !isMinorSupported) { // eslint-disable-next-line no-console console.warn( - `The Fuel Node that you are trying to connect to is using fuel-core version ${nodeInfo.nodeVersion}, -which is not supported by the version of the TS SDK that you are using. + `The Fuel Node that you are trying to connect to is using fuel-core version ${nodeInfo.nodeVersion}, +which is not supported by the version of the TS SDK that you are using. Things may not work as expected. Supported fuel-core version: ${supportedVersion}.` ); @@ -1060,7 +1060,7 @@ Supported fuel-core version: ${supportedVersion}.` const txRequestClone = clone(transactionRequestify(transactionRequestLike)); const isScriptTransaction = txRequestClone.type === TransactionType.Script; const baseAssetId = this.getBaseAssetId(); - const setMaxFee = !txRequestClone.maxFee.eq(0); + const updateMaxFee = txRequestClone.maxFee.eq(0); // Fund with fake UTXOs to avoid not enough funds error // Getting coin quantities from amounts being transferred const coinOutputsQuantities = txRequestClone.getCoinOutputsQuantities(); @@ -1147,7 +1147,7 @@ Supported fuel-core version: ${supportedVersion}.` addedSignatures, estimatedPredicates: txRequestClone.inputs, dryRunStatus, - setMaxFee, + updateMaxFee, }; } From c9478cb6be4350603b9b0c4351558856ada84306 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Wed, 8 May 2024 10:17:37 -0300 Subject: [PATCH 12/17] release PR --- .github/workflows/pr-release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-release.yaml b/.github/workflows/pr-release.yaml index 9b48fc2352a..34ad25146b1 100644 --- a/.github/workflows/pr-release.yaml +++ b/.github/workflows/pr-release.yaml @@ -8,7 +8,7 @@ jobs: name: "Release PR to npm" runs-on: ubuntu-latest # comment out if:false to enable release PR to npm - if: false + # if: false permissions: write-all steps: - name: Checkout From 02147bef00cadef79372820ddf61660eeee4cb3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Wed, 8 May 2024 11:17:25 -0300 Subject: [PATCH 13/17] formating annoying messages --- packages/account/src/providers/provider.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/account/src/providers/provider.test.ts b/packages/account/src/providers/provider.test.ts index 71a696462d6..f49cb9781aa 100644 --- a/packages/account/src/providers/provider.test.ts +++ b/packages/account/src/providers/provider.test.ts @@ -882,8 +882,8 @@ describe('Provider', () => { expect(consoleWarnSpy).toHaveBeenCalledOnce(); expect(consoleWarnSpy).toHaveBeenCalledWith( - `The Fuel Node that you are trying to connect to is using fuel-core version ${FUEL_CORE}, -which is not supported by the version of the TS SDK that you are using. + `The Fuel Node that you are trying to connect to is using fuel-core version ${FUEL_CORE}, +which is not supported by the version of the TS SDK that you are using. Things may not work as expected. Supported fuel-core version: ${mock.supportedVersion}.` ); @@ -914,8 +914,8 @@ Supported fuel-core version: ${mock.supportedVersion}.` expect(consoleWarnSpy).toHaveBeenCalledOnce(); expect(consoleWarnSpy).toHaveBeenCalledWith( - `The Fuel Node that you are trying to connect to is using fuel-core version ${FUEL_CORE}, -which is not supported by the version of the TS SDK that you are using. + `The Fuel Node that you are trying to connect to is using fuel-core version ${FUEL_CORE}, +which is not supported by the version of the TS SDK that you are using. Things may not work as expected. Supported fuel-core version: ${mock.supportedVersion}.` ); From 76a9375a3849e8adb438f0274c036fe941f6cd50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Wed, 8 May 2024 11:20:38 -0300 Subject: [PATCH 14/17] fix TS DOC --- packages/account/src/account.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/account/src/account.ts b/packages/account/src/account.ts index 9cf6741f7d5..d8a7254123b 100644 --- a/packages/account/src/account.ts +++ b/packages/account/src/account.ts @@ -246,9 +246,9 @@ export class Account extends AbstractAccount { /** * Funds a transaction request by adding the necessary resources. * - * @template T - The type of the TransactionRequest. - * @param T - request - The transaction request to fund. - * @param EstimatedTxParams - The estimated transaction parameters. + * @typeParam T - The type of the TransactionRequest. + * @param request - The transaction request to fund. + * @param params - The estimated transaction parameters. * @returns The funded transaction request. */ async fund(request: T, params: EstimatedTxParams): Promise { From 63758fe3e05acdb6ba90d20eede9c4aeb59a304c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Wed, 8 May 2024 14:30:33 -0300 Subject: [PATCH 15/17] unrelease PR --- .github/workflows/pr-release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-release.yaml b/.github/workflows/pr-release.yaml index 34ad25146b1..9b48fc2352a 100644 --- a/.github/workflows/pr-release.yaml +++ b/.github/workflows/pr-release.yaml @@ -8,7 +8,7 @@ jobs: name: "Release PR to npm" runs-on: ubuntu-latest # comment out if:false to enable release PR to npm - # if: false + if: false permissions: write-all steps: - name: Checkout From f8bed39b505aa134e28379fae408bc04af3725e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Thu, 9 May 2024 09:51:34 -0300 Subject: [PATCH 16/17] adding tests --- packages/account/src/account.test.ts | 46 +++++++++++++ packages/fuel-gauge/src/contract.test.ts | 85 +++++++++++++++++++++++- 2 files changed, 130 insertions(+), 1 deletion(-) diff --git a/packages/account/src/account.test.ts b/packages/account/src/account.test.ts index 94cc53d47da..54b3a2995ab 100644 --- a/packages/account/src/account.test.ts +++ b/packages/account/src/account.test.ts @@ -3,6 +3,7 @@ import { ZeroBytes32 } from '@fuel-ts/address/configs'; import { ErrorCode, FuelError } from '@fuel-ts/errors'; import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; import { bn } from '@fuel-ts/math'; +import { PolicyType } from '@fuel-ts/transactions'; import { ASSET_A, ASSET_B } from '@fuel-ts/utils/test-utils'; import { Account } from './account'; @@ -409,6 +410,28 @@ describe('Account', () => { expect(receiverBalances).toEqual([{ assetId: baseAssetId, amount: bn(1) }]); }); + it('can set "gasLimit" and "maxFee" when transferring amounts', async () => { + const sender = await generateTestWallet(provider, [[500_000, baseAssetId]]); + const receiver = Address.fromRandom(); + + const gasLimit = 30_000; + const maxFee = 15_000; + + const request = await sender.createTransfer(receiver, 1, baseAssetId, { + gasLimit, + maxFee, + }); + + const response = await sender.sendTransaction(request); + const { transaction } = await response.wait(); + + const { scriptGasLimit, policies } = transaction; + const maxFeePolicy = policies?.find((policy) => policy.type === PolicyType.MaxFee); + + expect(scriptGasLimit?.toNumber()).toBe(gasLimit); + expect(bn(maxFeePolicy?.data).toNumber()).toBe(maxFee); + }); + it('can transfer with custom TX Params', async () => { const sender = await generateTestWallet(provider, [[50_000, baseAssetId]]); const receiver = Wallet.generate({ provider }); @@ -591,6 +614,29 @@ describe('Account', () => { expect(senderBalances).toEqual([{ assetId: baseAssetId, amount: bn(expectedRemaining) }]); }); + it('can set "gasLimit" and "maxFee" when withdrawing to base layer', async () => { + const sender = Wallet.generate({ + provider, + }); + + await seedTestWallet(sender, [[500_000, baseAssetId]]); + + const recipient = Address.fromRandom(); + const amount = 110; + + const gasLimit = 100_000; + const maxFee = 50_000; + + const tx = await sender.withdrawToBaseLayer(recipient, amount, { gasLimit, maxFee }); + const { transaction } = await tx.wait(); + + const { scriptGasLimit, policies } = transaction; + const maxFeePolicy = policies?.find((policy) => policy.type === PolicyType.MaxFee); + + expect(scriptGasLimit?.toNumber()).toBe(gasLimit); + expect(bn(maxFeePolicy?.data).toNumber()).toBe(maxFee); + }); + it('should ensure gas price and gas limit are validated when transfering amounts', async () => { const sender = await generateTestWallet(provider, [[1000, baseAssetId]]); const receiver = Wallet.generate({ provider }); diff --git a/packages/fuel-gauge/src/contract.test.ts b/packages/fuel-gauge/src/contract.test.ts index 580cfa4293c..681b0f53fa6 100644 --- a/packages/fuel-gauge/src/contract.test.ts +++ b/packages/fuel-gauge/src/contract.test.ts @@ -25,6 +25,7 @@ import { ZeroBytes32, FUEL_NETWORK_URL, Predicate, + PolicyType, } from 'fuels'; import { FuelGaugeProjectsEnum, getFuelGaugeForcProject } from '../test/fixtures'; @@ -817,7 +818,7 @@ describe('Contract', () => { * to move them to another test suite when addressing https://github.com/FuelLabs/fuels-ts/issues/1043. */ it('should tranfer asset to a deployed contract just fine (NATIVE ASSET)', async () => { - const wallet = await generateTestWallet(provider, [[10_000_000_000, baseAssetId]]); + const wallet = await generateTestWallet(provider, [[10_000_000, baseAssetId]]); const contract = await setupContract(); @@ -835,6 +836,28 @@ describe('Contract', () => { expect(finalBalance).toBe(initialBalance + amountToContract.toNumber()); }); + it('should set "gasLimit" and "maxFee" when transferring amounts to contract just fine', async () => { + const wallet = await generateTestWallet(provider, [[10_000_000, baseAssetId]]); + const contract = await setupContract(); + const amountToContract = 5_000; + + const gasLimit = 80_000; + const maxFee = 40_000; + + const tx = await wallet.transferToContract(contract.id, amountToContract, baseAssetId, { + gasLimit, + maxFee, + }); + + const { transaction } = await tx.waitForResult(); + + const { scriptGasLimit, policies } = transaction; + const maxFeePolicy = policies?.find((policy) => policy.type === PolicyType.MaxFee); + + expect(scriptGasLimit?.toNumber()).toBe(gasLimit); + expect(bn(maxFeePolicy?.data).toNumber()).toBe(maxFee); + }); + it('should ensure gas price and gas limit are validated when transfering to contract', async () => { const wallet = await generateTestWallet(provider, [[1000, baseAssetId]]); @@ -1144,4 +1167,64 @@ describe('Contract', () => { expect(value.toNumber()).toBe(initialCounterValue); }); + + it('should ensure "maxFee" and "gasLimit" can be set for a contract call', async () => { + const { abiContents, binHexlified } = getFuelGaugeForcProject( + FuelGaugeProjectsEnum.STORAGE_TEST_CONTRACT + ); + + const wallet = await generateTestWallet(provider, [[150_000, baseAssetId]]); + const factory = new ContractFactory(binHexlified, abiContents, wallet); + + const storageContract = await factory.deployContract(); + + const gasLimit = 200_000; + const maxFee = 100_000; + + const { + transactionResult: { transaction }, + } = await storageContract.functions + .counter() + .txParams({ + gasLimit, + maxFee, + }) + .call(); + + const maxFeePolicy = transaction.policies?.find((policy) => policy.type === PolicyType.MaxFee); + const scriptGasLimit = transaction.scriptGasLimit; + + expect(scriptGasLimit?.toNumber()).toBe(gasLimit); + expect(bn(maxFeePolicy?.data).toNumber()).toBe(maxFee); + }); + + it('should ensure "maxFee" and "gasLimit" can be set on a multicall', async () => { + const contract = await setupContract(); + + const gasLimit = 500_000; + const maxFee = 250_000; + + const { + transactionResult: { transaction }, + } = await contract + .multiCall([ + contract.functions.foo(1336), + contract.functions.foo(1336), + contract.functions.foo(1336), + contract.functions.foo(1336), + contract.functions.foo(1336), + contract.functions.foo(1336), + contract.functions.foo(1336), + contract.functions.foo(1336), + ]) + .txParams({ gasLimit, maxFee }) + .call(); + + const { scriptGasLimit, policies } = transaction; + + const maxFeePolicy = policies?.find((policy) => policy.type === PolicyType.MaxFee); + + expect(scriptGasLimit?.toNumber()).toBe(gasLimit); + expect(bn(maxFeePolicy?.data).toNumber()).toBe(maxFee); + }); }); From b2768ecf70877615a2ce1e9fa7b86724b56784fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Torres?= <30977845+Torres-ssf@users.noreply.github.com> Date: Thu, 9 May 2024 10:03:50 -0300 Subject: [PATCH 17/17] fix test --- packages/fuel-gauge/src/contract.test.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/fuel-gauge/src/contract.test.ts b/packages/fuel-gauge/src/contract.test.ts index 681b0f53fa6..2dbb929bb1f 100644 --- a/packages/fuel-gauge/src/contract.test.ts +++ b/packages/fuel-gauge/src/contract.test.ts @@ -824,7 +824,7 @@ describe('Contract', () => { const initialBalance = new BN(await contract.getBalance(baseAssetId)).toNumber(); - const u64Amount = bn(5_000_000_000); + const u64Amount = bn(10_000); const amountToContract = u64Amount; const tx = await wallet.transferToContract(contract.id, amountToContract, baseAssetId); @@ -1199,7 +1199,9 @@ describe('Contract', () => { }); it('should ensure "maxFee" and "gasLimit" can be set on a multicall', async () => { - const contract = await setupContract(); + const contract = await setupContract({ + cache: false, + }); const gasLimit = 500_000; const maxFee = 250_000;