Predicates
Predicates, in Sway, are programs that return a Boolean value and do not have any side effects (they are pure). A predicate address can own assets. The predicate address is generated from the compiled byte code and is the same as the P2SH
address used in Bitcoin. Users can seamlessly send assets to the predicate address as they do for any other address. To spend the predicate funds, the user has to provide the original byte code
of the predicate together with the predicate data
. The predicate data
will be used when executing the byte code
, and the funds can be transferred if the predicate is validated successfully.
Instantiating predicates
Let's consider the following predicate example:
predicate;
fn main(a: u32, b: u64) -> bool {
a == b
}
We will look at a complete example of using the SDK to send and receive funds from a predicate.
First, we set up the wallets, node, and a predicate encoder instance. The call to the abigen!
macro will generate all the types specified in the predicate plus a custom encoder with an encode_data
function that will conveniently encode all the arguments of the main function for us.
let asset_id = AssetId::default();
let wallets_config = WalletsConfig::new_multiple_assets(
2,
vec![AssetConfig {
id: asset_id,
num_coins: 1,
coin_amount: 1_000,
}],
);
let wallets = &launch_custom_provider_and_get_wallets(wallets_config, None, None).await;
let first_wallet = &wallets[0];
let second_wallet = &wallets[1];
abigen!(Predicate(name="MyPredicateEncoder", abi="packages/fuels/tests/predicates/basic_predicate/out/debug/basic_predicate-abi.json"));
Once we've compiled our predicate with forc build
, we can create a Predicate
instance via Predicate::load_from
. The resulting data from encode_data
can then be set on the loaded predicate.
let predicate_data = MyPredicateEncoder::encode_data(4096, 4096);
let code_path =
"../../packages/fuels/tests/predicates/basic_predicate/out/debug/basic_predicate.bin";
let predicate: Predicate = Predicate::load_from(code_path)?
.with_data(predicate_data)
.with_provider(first_wallet.try_provider()?.clone());
Next, we lock some assets in this predicate using the first wallet:
// First wallet transfers amount to predicate.
first_wallet
.transfer(predicate.address(), 500, asset_id, TxParameters::default())
.await?;
// Check predicate balance.
let balance = predicate.get_asset_balance(&AssetId::default()).await?;
assert_eq!(balance, 500);
Then we can transfer assets owned by the predicate via the Account trait:
let amount_to_unlock = 500;
predicate
.transfer(
second_wallet.address(),
amount_to_unlock,
asset_id,
TxParameters::default(),
)
.await?;
// Predicate balance is zero.
let balance = predicate.get_asset_balance(&AssetId::default()).await?;
assert_eq!(balance, 0);
// Second wallet balance is updated.
let balance = second_wallet.get_asset_balance(&AssetId::default()).await?;
assert_eq!(balance, 1500);