Predicates can be used to validate transactions. This implies that a predicate can safeguard assets, only allowing their transfer if the predicate conditions are met.
This guide will demonstrate how to send and spend funds using a predicate.
Consider the following predicate:
predicate;
fn main(input_address: b256) -> bool {
let valid_address = 0xfc05c23a8f7f66222377170ddcbfea9c543dff0dd2d2ba4d0478a4521423a9d4;
input_address == valid_address
}
This predicate accepts an address of type b256
and compares it with a hardcoded address of the same type. If both addresses are equal, the predicate returns true, otherwise it will return false.
Let's use the above predicate to validate our transaction.
Once you've compiled the predicate (forc build
), you'll obtain two important artifacts: the JSON ABI and the predicate's binary code. These are needed to instantiate a new predicate.
const provider = new Provider(FUEL_NETWORK_URL);
const chainId = await provider.getChainId();
const predicate = new Predicate(bin, chainId, abi, provider);
With the predicate instantiated, we can transfer funds to its address. This requires us to have a wallet with sufficient funds. If you're unsure about using wallets with the SDK, we recommend checking out our wallet guide.
const amountToPredicate = 1_000;
const tx = await walletWithFunds.transfer(predicate.address, amountToPredicate);
await tx.waitForResult();
Now that our predicate holds funds, we can use it to validate a transaction.
First, we need to set its data. Note that the main
function in our predicate example requires a parameter called input_address
of type b256
. We achieve this using the Predicate
class method setData
.
const inputAddress = '0xfc05c23a8f7f66222377170ddcbfea9c543dff0dd2d2ba4d0478a4521423a9d4';
predicate.setData(inputAddress);
We are now ready to use our predicate to execute our transfer. We can achieve that by doing the following:
const receiverWallet = WalletUnlocked.generate();
const tx2 = await predicate.transfer(receiverWallet.address, amountToPredicate - 100);
await tx2.waitForResult();
Note the method transfer has two parameters: the recipient's address and the intended transfer amount.
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.
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:
const errorMsg = 'not enough coins to fit the target';
What happens when a predicate fails to validate? Recall our predicate only validates if the input_address
matches the hardcoded valid_address
. Hence, if we set a different data from the valid_address
, the predicate will fail to validate.
When a predicate fails to validate, the SDK throws an error that starts like this:
const errorMsg =
'Invalid transaction: The transaction contains a predicate which failed to validate';