Skip to content

Commit 4bee775

Browse files
authored
chore: avoid reassigning inputs and outputs at BaseInvocationScope (#1805)
1 parent 96a735a commit 4bee775

File tree

6 files changed

+64
-28
lines changed

6 files changed

+64
-28
lines changed

.changeset/heavy-trainers-serve.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
"@fuel-ts/account": minor
3+
"@fuel-ts/program": minor
4+
---
5+
6+
- Add `outputVariables` and `missingContractIds` to the return of `estimateTxDependencies`
7+
- Removed `estimatedOutputs` from return of `getTransactionCost`
8+
- Add `outputVariables` and `missingContractIds` to the return of `getTransactionCost`
9+
- Avoid reassigning `inputs` and `outputs` from the estimated TX at `BaseInvocationScope`

packages/account/src/account.test.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,9 @@ describe('Account', () => {
303303

304304
const estimateTxDependencies = vi
305305
.spyOn(providersMod.Provider.prototype, 'estimateTxDependencies')
306-
.mockImplementation(() => Promise.resolve({ receipts: [] }));
306+
.mockImplementation(() =>
307+
Promise.resolve({ receipts: [], missingContractIds: [], outputVariables: 0 })
308+
);
307309

308310
const sendTransaction = vi
309311
.spyOn(providersMod.Provider.prototype, 'sendTransaction')
@@ -341,7 +343,9 @@ describe('Account', () => {
341343

342344
const estimateTxDependencies = vi
343345
.spyOn(providersMod.Provider.prototype, 'estimateTxDependencies')
344-
.mockImplementation(() => Promise.resolve({ receipts: [] }));
346+
.mockImplementation(() =>
347+
Promise.resolve({ receipts: [], missingContractIds: [], outputVariables: 0 })
348+
);
345349

346350
const simulate = vi
347351
.spyOn(providersMod.Provider.prototype, 'simulate')

packages/account/src/providers/provider.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ export type CallResult = {
5959
receipts: TransactionResultReceipt[];
6060
};
6161

62+
export type EstimateTxDependenciesReturns = CallResult & {
63+
outputVariables: number;
64+
missingContractIds: string[];
65+
};
66+
6267
/**
6368
* A Fuel block
6469
*/
@@ -693,16 +698,22 @@ export default class Provider {
693698
* @param transactionRequest - The transaction request object.
694699
* @returns A promise.
695700
*/
696-
async estimateTxDependencies(transactionRequest: TransactionRequest): Promise<CallResult> {
701+
async estimateTxDependencies(
702+
transactionRequest: TransactionRequest
703+
): Promise<EstimateTxDependenciesReturns> {
697704
if (transactionRequest.type === TransactionType.Create) {
698705
return {
699706
receipts: [],
707+
outputVariables: 0,
708+
missingContractIds: [],
700709
};
701710
}
702711

703712
await this.estimatePredicates(transactionRequest);
704713

705714
let receipts: TransactionResultReceipt[] = [];
715+
const missingContractIds: string[] = [];
716+
let outputVariables = 0;
706717

707718
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
708719
const { dryRun: gqlReceipts } = await this.operations.dryRun({
@@ -717,9 +728,11 @@ export default class Provider {
717728
missingOutputVariables.length !== 0 || missingOutputContractIds.length !== 0;
718729

719730
if (hasMissingOutputs) {
731+
outputVariables += missingOutputVariables.length;
720732
transactionRequest.addVariableOutputs(missingOutputVariables.length);
721733
missingOutputContractIds.forEach(({ contractId }) => {
722734
transactionRequest.addContractInputAndOutput(Address.fromString(contractId));
735+
missingContractIds.push(contractId);
723736
});
724737
} else {
725738
break;
@@ -728,6 +741,8 @@ export default class Provider {
728741

729742
return {
730743
receipts,
744+
outputVariables,
745+
missingContractIds,
731746
};
732747
}
733748

@@ -786,7 +801,8 @@ export default class Provider {
786801
): Promise<
787802
TransactionCost & {
788803
estimatedInputs: TransactionRequest['inputs'];
789-
estimatedOutputs: TransactionRequest['outputs'];
804+
outputVariables: number;
805+
missingContractIds: string[];
790806
}
791807
> {
792808
const txRequestClone = clone(transactionRequestify(transactionRequestLike));
@@ -835,6 +851,8 @@ export default class Provider {
835851
*/
836852

837853
let receipts: TransactionResultReceipt[] = [];
854+
let missingContractIds: string[] = [];
855+
let outputVariables = 0;
838856
// Transactions of type Create does not consume any gas so we can the dryRun
839857
if (isScriptTransaction && estimateTxDependencies) {
840858
/**
@@ -852,6 +870,8 @@ export default class Provider {
852870
const result = await this.estimateTxDependencies(txRequestClone);
853871

854872
receipts = result.receipts;
873+
outputVariables = result.outputVariables;
874+
missingContractIds = result.missingContractIds;
855875
}
856876

857877
// For CreateTransaction the gasUsed is going to be the minGas
@@ -877,7 +897,8 @@ export default class Provider {
877897
minFee,
878898
maxFee,
879899
estimatedInputs: txRequestClone.inputs,
880-
estimatedOutputs: txRequestClone.outputs,
900+
outputVariables,
901+
missingContractIds,
881902
};
882903
}
883904

packages/account/src/providers/transaction-request/transaction-request.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -642,8 +642,6 @@ export abstract class BaseTransactionRequest implements BaseTransactionRequestLi
642642
this.inputs.forEach((i) => {
643643
let correspondingInput: TransactionRequestInput | undefined;
644644
switch (i.type) {
645-
case InputType.Contract:
646-
return;
647645
case InputType.Coin:
648646
correspondingInput = inputs.find((x) => x.type === InputType.Coin && x.owner === i.owner);
649647
break;
@@ -653,7 +651,7 @@ export abstract class BaseTransactionRequest implements BaseTransactionRequestLi
653651
);
654652
break;
655653
default:
656-
break;
654+
return;
657655
}
658656
if (
659657
correspondingInput &&

packages/account/src/wallet/wallet-unlocked.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,9 @@ describe('WalletUnlocked', () => {
226226

227227
const estimateTxDependencies = vi
228228
.spyOn(providersMod.Provider.prototype, 'estimateTxDependencies')
229-
.mockImplementation(() => Promise.resolve({ receipts: [] }));
229+
.mockImplementation(() =>
230+
Promise.resolve({ receipts: [], missingContractIds: [], outputVariables: 0 })
231+
);
230232

231233
const call = vi
232234
.spyOn(providersMod.Provider.prototype, 'call')

packages/program/src/functions/base-invocation-scope.ts

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
import type { InputValue } from '@fuel-ts/abi-coder';
44
import type { BaseWalletUnlocked, Provider, CoinQuantity } from '@fuel-ts/account';
55
import { ScriptTransactionRequest } from '@fuel-ts/account';
6+
import { Address } from '@fuel-ts/address';
67
import { ErrorCode, FuelError } from '@fuel-ts/errors';
78
import type { AbstractAccount, AbstractContract, AbstractProgram } from '@fuel-ts/interfaces';
89
import type { BN } from '@fuel-ts/math';
910
import { bn, toNumber } from '@fuel-ts/math';
10-
import { InputType, OutputType } from '@fuel-ts/transactions';
11+
import { InputType } from '@fuel-ts/transactions';
1112
import * as asm from '@fuels/vm-asm';
1213

1314
import { getContractCallScript } from '../contract-call-script';
@@ -230,33 +231,34 @@ export class BaseInvocationScope<TReturn = any> {
230231
async fundWithRequiredCoins() {
231232
const transactionRequest = await this.getTransactionRequest();
232233

233-
const { maxFee, gasUsed, minGasPrice, estimatedInputs, estimatedOutputs } =
234-
await this.getTransactionCost();
235-
234+
const {
235+
maxFee,
236+
gasUsed,
237+
minGasPrice,
238+
estimatedInputs,
239+
outputVariables,
240+
missingContractIds,
241+
requiredQuantities,
242+
} = await this.getTransactionCost();
236243
this.setDefaultTxParams(transactionRequest, minGasPrice, gasUsed);
237244

238-
transactionRequest.outputs = estimatedOutputs;
239-
240245
// Clean coin inputs before add new coins to the request
241-
this.transactionRequest.inputs = estimatedInputs.filter((i) => i.type !== InputType.Coin);
246+
this.transactionRequest.inputs = this.transactionRequest.inputs.filter(
247+
(i) => i.type !== InputType.Coin
248+
);
242249

243-
await this.program.account?.fund(this.transactionRequest, this.requiredCoins, maxFee);
250+
await this.program.account?.fund(this.transactionRequest, requiredQuantities, maxFee);
244251

245-
// update predicate inputs with estimated predicate-related info because the funding removes it
246252
this.transactionRequest.updatePredicateInputs(estimatedInputs);
247253

248-
// Update output coin indexes after funding because the funding reordered the inputs
249-
this.transactionRequest.outputs = this.transactionRequest.outputs.filter(
250-
(x) => x.type !== OutputType.Contract
251-
);
252-
253-
this.transactionRequest.inputs.forEach((input, inputIndex) => {
254-
if (input.type !== InputType.Contract) {
255-
return;
256-
}
257-
this.transactionRequest.outputs.push({ type: OutputType.Contract, inputIndex });
254+
// Adding missing contract ids
255+
missingContractIds.forEach((contractId) => {
256+
this.transactionRequest.addContractInputAndOutput(Address.fromString(contractId));
258257
});
259258

259+
// Adding required number of OutputVariables
260+
this.transactionRequest.addVariableOutputs(outputVariables);
261+
260262
return this;
261263
}
262264

0 commit comments

Comments
 (0)