Skip to content

Commit

Permalink
feat: add Predicate.getTransferTxId helper (#1467)
Browse files Browse the repository at this point in the history
* first draft

* feat: add `Predicate.getTransferTxId` helper

* add changeset

* refactor
  • Loading branch information
Dhaiwat10 committed Nov 30, 2023
1 parent 2216b6e commit 70233c1
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 12 deletions.
6 changes: 6 additions & 0 deletions .changeset/kind-glasses-brake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@fuel-ts/predicate": patch
"@fuel-ts/wallet": patch
---

New helper method `Predicate.getTransferTxId`, which lets you calculate the transaction ID for a Predicate.transfer transaction, before actually sending it.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import {
BaseAssetId,
} from 'fuels';

import { DocSnippetProjectsEnum, getDocsSnippetsForcProject } from '../../../test/fixtures/forc-projects';
import {
DocSnippetProjectsEnum,
getDocsSnippetsForcProject,
} from '../../../test/fixtures/forc-projects';
import { getTestWallet } from '../../utils';

describe(__filename, () => {
Expand Down Expand Up @@ -50,11 +53,22 @@ describe(__filename, () => {
predicate.setData(inputAddress);
// #endregion send-and-spend-funds-from-predicates-4

// #region send-and-spend-funds-from-predicates-5
const receiverWallet = WalletUnlocked.generate({
provider,
});

// #region send-and-spend-funds-from-predicates-8
const txId = await predicate.getTransferTxId(
receiverWallet.address,
amountToPredicate - 150_000,
BaseAssetId,
{
gasPrice,
}
);
// #endregion send-and-spend-funds-from-predicates-8

// #region send-and-spend-funds-from-predicates-5
const tx2 = await predicate.transfer(
receiverWallet.address,
amountToPredicate - 150_000,
Expand All @@ -66,6 +80,9 @@ describe(__filename, () => {

await tx2.waitForResult();
// #endregion send-and-spend-funds-from-predicates-5
const txIdFromExecutedTx = tx2.id;

expect(txId).toEqual(txIdFromExecutedTx);
});

it('should fail when trying to spend predicates entire amount', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ Note the method transfer has two parameters: the recipient's address and the int

Once the predicate resolves with a return value `true` based on its predefined condition, our predicate successfully spends its funds by means of a transfer to a desired wallet.

---

You can also use the `getTransferTxId` helper to obtain the transaction ID of the transfer beforehand, without actually executing it.

<<< @/../../docs-snippets/src/guide/predicates/send-and-spend-funds-from-predicates.test.ts#send-and-spend-funds-from-predicates-8{ts:line-numbers}

## Spending Entire Predicate Held Amount

Trying to forward the entire amount held by the predicate results in an error because no funds are left to cover the transaction fees. Attempting this will result in an error message like:
Expand Down
1 change: 1 addition & 0 deletions packages/predicate/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@fuel-ts/abi-coder": "workspace:*",
"@fuel-ts/address": "workspace:*",
"@fuel-ts/interfaces": "workspace:*",
"@fuel-ts/math": "workspace:*",
"@fuel-ts/merkle": "workspace:*",
"@fuel-ts/providers": "workspace:*",
"@fuel-ts/transactions": "workspace:*",
Expand Down
30 changes: 29 additions & 1 deletion packages/predicate/src/predicate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ import {
calculateVmTxMemory,
} from '@fuel-ts/abi-coder';
import { Address } from '@fuel-ts/address';
import { BaseAssetId } from '@fuel-ts/address/configs';
import { ErrorCode, FuelError } from '@fuel-ts/errors';
import type { AbstractPredicate } from '@fuel-ts/interfaces';
import { hashTransaction } from '@fuel-ts/hasher';
import type { AbstractAddress, AbstractPredicate } from '@fuel-ts/interfaces';
import type { BigNumberish } from '@fuel-ts/math';
import type {
CallResult,
Provider,
Expand All @@ -17,6 +20,7 @@ import type {
} from '@fuel-ts/providers';
import { transactionRequestify } from '@fuel-ts/providers';
import { ByteArrayCoder, InputType } from '@fuel-ts/transactions';
import type { TxParamsType } from '@fuel-ts/wallet';
import { Account } from '@fuel-ts/wallet';
import type { BytesLike } from 'ethers';
import { getBytesCopy, hexlify } from 'ethers';
Expand Down Expand Up @@ -92,6 +96,30 @@ export class Predicate<ARGS extends InputValue[]> extends Account implements Abs
return super.sendTransaction(transactionRequest);
}

/**
* Returns the transaction ID for a transfer transaction, without sending it.
*
* @param destination - The address of the destination.
* @param amount - The amount of coins to transfer.
* @param assetId - The asset ID of the coins to transfer.
* @param txParams - The transaction parameters (gasLimit, gasPrice, maturity).
* @returns A promise that resolves to the transaction ID.
*/
async getTransferTxId(
/** Address of the destination */
destination: AbstractAddress,
/** Amount of coins */
amount: BigNumberish,
/** Asset ID of coins */
assetId: BytesLike = BaseAssetId,
/** Tx Params */
txParams: TxParamsType = {}
): Promise<string> {
const request = await super.prepareTransferTxRequest(destination, amount, assetId, txParams);
const populatedRequest = this.populateTransactionPredicateData(request);
return hashTransaction(populatedRequest, this.provider.getChainId());
}

/**
* Simulates a transaction with the populated predicate data.
*
Expand Down
30 changes: 25 additions & 5 deletions packages/wallet/src/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import {
formatScriptDataForTransferringToContract,
} from './utils';

type TxParamsType = Pick<TransactionRequestLike, 'gasLimit' | 'gasPrice' | 'maturity'>;
export type TxParamsType = Pick<TransactionRequestLike, 'gasLimit' | 'gasPrice' | 'maturity'>;

/**
* `Account` provides an abstraction for interacting with accounts or wallets on the network.
Expand Down Expand Up @@ -239,16 +239,36 @@ export class Account extends AbstractAccount {
/** Tx Params */
txParams: TxParamsType = {}
): Promise<TransactionResponse> {
const request = await this.prepareTransferTxRequest(destination, amount, assetId, txParams);
return this.sendTransaction(request);
}

/**
* A helper that prepares a transaction request for calculating the transaction ID.
*
* @param destination - The address of the destination.
* @param amount - The amount of coins to transfer.
* @param assetId - The asset ID of the coins to transfer.
* @param txParams - The transaction parameters (gasLimit, gasPrice, maturity).
* @returns A promise that resolves to the prepared transaction request.
*/
protected async prepareTransferTxRequest(
/** Address of the destination */
destination: AbstractAddress,
/** Amount of coins */
amount: BigNumberish,
/** Asset ID of coins */
assetId: BytesLike = BaseAssetId,
/** Tx Params */
txParams: TxParamsType = {}
): Promise<TransactionRequest> {
const { maxGasPerTx } = this.provider.getGasConfig();
const params: TxParamsType = { gasLimit: maxGasPerTx, ...txParams };
const request = new ScriptTransactionRequest(params);
request.addCoinOutput(destination, amount, assetId);

const { maxFee, requiredQuantities } = await this.provider.getTransactionCost(request);

await this.fund(request, requiredQuantities, maxFee);

return this.sendTransaction(request);
return request;
}

/**
Expand Down
7 changes: 3 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 70233c1

Please sign in to comment.