The Predicate
class extends the Account
class, inheriting all its methods. Therefore, there are multiple ways to interact with predicates, but broadly speaking, we can think about three:
Checking Balances
Transactions
Transfers
getBalances
This will return the balances of all assets owned by the predicate.
See also: Checking Wallet Balances
getResourcesToSpend
This will return the resources owned by a predicate so that they can be added to a transaction request.
This method is called under the hood when using transfer
or createTransfer
.
You may want to use this method when using a predicate in an existing transaction request.
import { bn, Provider, ScriptTransactionRequest, Wallet } from 'fuels';
import { LOCAL_NETWORK_URL, WALLET_PVT_KEY } from '../../../../env';
import { ReturnTruePredicate } from '../../../../typegend';
const provider = new Provider(LOCAL_NETWORK_URL);
const baseAssetId = await provider.getBaseAssetId();
const funder = Wallet.fromPrivateKey(WALLET_PVT_KEY, provider);
const predicate = new ReturnTruePredicate({
provider,
});
// Fund the predicate
const fundPredicate = await funder.transfer(
predicate.address,
100_000_000,
baseAssetId
);
await fundPredicate.waitForResult();
// Instantiate the transaction request.
const transactionRequest = new ScriptTransactionRequest({
gasLimit: 2000,
maxFee: bn(0),
});
// Get the resources available to send from the predicate.
const predicateCoins = await predicate.getResourcesToSpend([
{ amount: 2000, assetId: baseAssetId },
]);
// Add the predicate input and resources.
transactionRequest.addResources(predicateCoins);
setData
The setData
method can be used to update the predicate data (i.e., predicate arguments) after the predicate has already been instantiated. Since the predicate data is initially set during instantiation, setData
provides a way to modify it afterward if needed.
const predicate = new ConfigurablePin({
provider,
data: [1000], // This is the initial set data
});
predicate.setData([1337]); // This is the new set data
Note: Using
setData
only updates the predicate data inside the predicate instance itself. It does not affect predicate data already embedded in a transaction request that includes predicate UTXOs. This is because each predicate UTXO carries its own copy of the predicate data.
const predicate = new ConfigurablePin({
provider,
data: [1000],
});
// Fund the predicate
const fundPredicate = await funder.transfer(predicate.address, 100_000_000);
await fundPredicate.waitForResult();
const transactionRequest = await predicate.createTransfer(
receiver.address,
100_000,
baseAssetId
);
// The data will not be modified within the transaction request
predicate.setData([1337]);
If you need to modify the predicate data within a transaction request, use the populateTransactionPredicateData
method after setting the new data.
const predicate = new ConfigurablePin({
provider,
data: [1000],
});
// Fund the predicate
const fundPredicate = await funder.transfer(predicate.address, 100_000_000);
await fundPredicate.waitForResult();
const transactionRequest = await predicate.createTransfer(
receiver.address,
100_000,
baseAssetId
);
predicate.setData([1337]);
predicate.populateTransactionPredicateData(transactionRequest);
sendTransaction
This is used to send a transaction to the node.
import { Provider, ScriptTransactionRequest, Wallet } from 'fuels';
import { LOCAL_NETWORK_URL, WALLET_PVT_KEY } from '../../../../env';
import { ReturnTruePredicate } from '../../../../typegend';
const provider = new Provider(LOCAL_NETWORK_URL);
const baseAssetId = await provider.getBaseAssetId();
const funder = Wallet.fromPrivateKey(WALLET_PVT_KEY, provider);
const predicate = new ReturnTruePredicate({
provider,
});
// Fund the predicate
const fundPredicate = await funder.transfer(
predicate.address,
100_000_000,
baseAssetId
);
await fundPredicate.waitForResult();
// Instantiate the transaction request.
const request = new ScriptTransactionRequest();
// Estimate and fund the transaction
const { assembledRequest } = await provider.assembleTx({
request,
feePayerAccount: predicate,
accountCoinQuantities: [
{
amount: '0',
assetId: baseAssetId,
account: predicate,
changeOutputAccount: predicate,
},
],
});
// Send the transaction using the predicate
const result = await predicate.sendTransaction(assembledRequest);
await result.waitForResult();
simulateTransaction
You can use the simulateTransaction
method to dry-run a predicate call without consuming resources. A typical use case of a dry-run call is to validate that sufficient funds are available to cover the transaction fees.
import {
bn,
Provider,
ReceiptType,
ScriptTransactionRequest,
Wallet,
} from 'fuels';
import { LOCAL_NETWORK_URL, WALLET_PVT_KEY } from '../../../../env';
import { ReturnTruePredicate } from '../../../../typegend';
const provider = new Provider(LOCAL_NETWORK_URL);
const baseAssetId = await provider.getBaseAssetId();
const funder = Wallet.fromPrivateKey(WALLET_PVT_KEY, provider);
const receiver = Wallet.generate({ provider });
const predicate = new ReturnTruePredicate({
provider,
});
const fundPredicate = await funder.transfer(
predicate.address,
100_000_000,
baseAssetId
);
await fundPredicate.waitForResult();
// Instantiate the transaction request.
const request = new ScriptTransactionRequest();
const amount = bn(1_000_000);
request.addCoinOutput(receiver.address, amount, baseAssetId);
// Estimate and fund the transaction
const { assembledRequest } = await provider.assembleTx({
request,
feePayerAccount: predicate,
accountCoinQuantities: [
{
amount,
assetId: baseAssetId,
account: predicate,
changeOutputAccount: predicate,
},
],
});
const result = await predicate.simulateTransaction(assembledRequest);
createTransfer
The createTransfer
method creates a transaction request with all the necessary transfer details. It automatically estimates the transaction costs via a dry-run call and funds the request with the required predicate resources. After this, one can submit the returned transaction request with greater certainty that it will succeed.
However, please remember that you can still modify the transfer request details and use its properties before submitting it to the node.
import { Provider, Wallet } from 'fuels';
import { LOCAL_NETWORK_URL, WALLET_PVT_KEY } from '../../../../env';
import { ReturnTruePredicate } from '../../../../typegend';
const provider = new Provider(LOCAL_NETWORK_URL);
const baseAssetId = await provider.getBaseAssetId();
const funder = Wallet.fromPrivateKey(WALLET_PVT_KEY, provider);
const predicate = new ReturnTruePredicate({
provider,
});
// Fund the predicate
const fundPredicate = await funder.transfer(
predicate.address,
100_000_000,
baseAssetId
);
await fundPredicate.waitForResult();
const receiver = Wallet.generate({ provider });
const amountToReceiver = 1000;
const transactionRequest = await predicate.createTransfer(
receiver.address,
amountToReceiver,
baseAssetId,
{
gasLimit: 1000,
}
);
const sendFromPredicate = await predicate.sendTransaction(transactionRequest);
await sendFromPredicate.waitForResult();
transfer
You can send funds to another address using the transfer
method.
import { Provider, Wallet } from 'fuels';
import { LOCAL_NETWORK_URL, WALLET_PVT_KEY } from '../../../../env';
import { ReturnTruePredicate } from '../../../../typegend';
const provider = new Provider(LOCAL_NETWORK_URL);
const baseAssetId = await provider.getBaseAssetId();
const funder = Wallet.fromPrivateKey(WALLET_PVT_KEY, provider);
const predicate = new ReturnTruePredicate({
provider,
});
const fundPredicate = await funder.transfer(
predicate.address,
100_000_000,
baseAssetId
);
await fundPredicate.waitForResult();
const receiver = Wallet.generate({ provider });
const amountToReceiver = 1000;
const transferPredicateCoins = await predicate.transfer(
receiver.address,
amountToReceiver,
baseAssetId,
{
gasLimit: 1000,
}
);
await transferPredicateCoins.waitForResult();