Icon LinkPredicate With Configurable Constants

Predicates, much like contracts and scripts, also supports configurable constants. This enables Predicates to suit specific use cases and enhance their functionality.

Icon LinkExample: Asset Transfer Validation

Let's consider an example where a predicate is used to validate an asset transfer. In this case, the transfer will only be executed if the recipient's address is on a pre-approved whitelist.

The following snippet illustrates how this could be implemented:

predicate;
 
configurable {
WHITELISTED: b256 = 0xa703b26833939dabc41d3fcaefa00e62cee8e1ac46db37e0fa5d4c9fe30b4132,
}
 
fn main(address: b256) -> bool {
WHITELISTED == address
}

In this example, you'll notice the use of a configurable constant named WHITELISTED. This constant has a default value that represents the default approved address.

Icon LinkModifying The Whitelist

If there is a need to whitelist another address, the WHITELISTED constant can be easily updated. The following snippet demonstrates how to set a new value for the WHITELISTED constant and to make the predicate execute the transfer:

const newWhitelistedAddress = getRandomB256();
 
const configurable = { WHITELISTED: newWhitelistedAddress };
const chainId = await wallet.provider.getChainId();
// instantiate predicate with configurable constants
const predicate = new Predicate(bin, chainId, abi, wallet.provider, configurable);
 
// set predicate data to be the same as the configurable constant
predicate.setData(configurable.WHITELISTED);
 
// transfering funds to the predicate
const tx1 = await wallet.transfer(predicate.address, 500);
 
await tx1.waitForResult();
 
const destinationWallet = WalletUnlocked.generate();
 
const amountToTransfer = 100;
 
// transfering funds from the predicate to destination if predicate returns true
const tx2 = await predicate.transfer(destinationWallet.address, amountToTransfer);
 
await tx2.waitForResult();

By ensuring that the updated WHITELISTED address matches the intended recipient's address, the predicate will validate the transfer successfully.

Icon LinkDefault Whitelist Address

In scenarios where the default whitelisted address is already the intended recipient, there's no need to update the WHITELISTED constant. The predicate will validate the transfer based on the default value. Here's how this scenario might look:

const chainId = await wallet.provider.getChainId();
const predicate = new Predicate(bin, chainId, abi, wallet.provider);
 
// set predicate data to be the same as the configurable constant
predicate.setData('0xa703b26833939dabc41d3fcaefa00e62cee8e1ac46db37e0fa5d4c9fe30b4132');
 
// transfering funds to the predicate
const tx1 = await wallet.transfer(predicate.address, 500);
 
await tx1.waitForResult();
 
const destinationWallet = WalletUnlocked.generate();
 
const amountToTransfer = 100;
 
// transfering funds from the predicate to destination if predicate returns true
const tx2 = await predicate.transfer(destinationWallet.address, amountToTransfer);
 
await tx2.waitForResult();

This ability to configure constants within predicates provides a flexible mechanism for customizing their behavior, thereby enhancing the robustness and versatility of our asset transfer process.

It's important to note that these customizations do not directly modify the original predicate. The address of a predicate is a hash of its bytecode. Any change to the bytecode, including altering a constant value, would generate a different bytecode, and thus a different hash. This leads to the creation of a new predicate with a new address.

This doesn't mean that we're changing the behavior of the original predicate. Instead, we're creating a new predicate with a different configuration.

Therefore, while configurable constants do indeed enhance the flexibility and robustness of predicates, it is achieved by creating new predicates with different configurations, rather than altering the behavior of existing ones.