⌘K

Predicates

Icon Link断言

在 Sway 中,断言是返回布尔值且没有任何副作用(它们是纯粹的)的程序。断言地址可以拥有资产。断言地址是从编译后的字节码生成的,与比特币中使用的 P2SH 地址相同。用户可以像发送给任何其他地址一样无缝地将资产发送到断言地址。要花费断言资金,用户必须提供断言的原始 byte code断言数据。在执行 byte code 时将使用 断言数据,如果断言成功验证,则可以转移资金。

Icon Link实例化断言

让我们考虑以下断言示例:

predicate;
 
fn main(a: u32, b: u64) -> bool {
    b == a.as_u64()
}
 

我们将看到一个完整的示例,使用 SDK 从断言中发送和接收资金。

首先,我们设置钱包和一个节点实例。调用 abigen! 宏将为我们生成断言中指定的所有类型,以及两个自定义结构体:

  • 一个编码器,带有一个 encode_data 函数,可以方便地为我们编码主函数的所有参数。
  • 一个可配置结构,其中包含设置断言中提到的所有可配置项的方法
Icon InfoCircle

注意:abigen! 宏将向断言的 name 字段附加 EncoderConfigurables。例如,name="MyPredicate" 将导致两个名为 MyPredicateEncoderMyPredicateConfigurables 的结构体。

let asset_id = AssetId::zeroed();
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 = "MyPredicate",
    abi = "e2e/sway/predicates/basic_predicate/out/release/basic_predicate-abi.json"
));

一旦我们使用 forc build 编译了我们的断言,我们就可以通过 Predicate::load_from 创建一个 Predicate 实例。然后可以将 encode_data 的结果设置到加载的断言上。

let predicate_data = MyPredicateEncoder::default().encode_data(4096, 4096)?;
let code_path = "../../e2e/sway/predicates/basic_predicate/out/release/basic_predicate.bin";
 
let predicate: Predicate = Predicate::load_from(code_path)?
    .with_provider(first_wallet.try_provider()?.clone())
    .with_data(predicate_data);

接下来,使用第一个钱包在此断言中锁定一些资产:

// First wallet transfers amount to predicate.
first_wallet
    .transfer(predicate.address(), 500, asset_id, TxPolicies::default())
    .await?;
 
// Check predicate balance.
let balance = predicate.get_asset_balance(&AssetId::zeroed()).await?;
 
assert_eq!(balance, 500);

然后,我们可以通过 Account 特性转移由断言拥有的资产:

let amount_to_unlock = 500;
 
predicate
    .transfer(
        second_wallet.address(),
        amount_to_unlock,
        asset_id,
        TxPolicies::default(),
    )
    .await?;
 
// Predicate balance is zero.
let balance = predicate.get_asset_balance(&AssetId::zeroed()).await?;
 
assert_eq!(balance, 0);
 
// Second wallet balance is updated.
let balance = second_wallet.get_asset_balance(&AssetId::zeroed()).await?;
assert_eq!(balance, 1500);

Icon Link可配置常量

与合约和脚本一样,您可以在 predicates 中定义可配置常量,在断言执行期间可以更改。以下是定义常量的示例。

#[allow(dead_code)]
enum EnumWithGeneric<D> {
    VariantOne: D,
    VariantTwo: (),
}
 
struct StructWithGeneric<D> {
    field_1: D,
    field_2: u64,
}
 
configurable {
    BOOL: bool = true,
    U8: u8 = 8,
    STRUCT: StructWithGeneric<u8> = StructWithGeneric {
        field_1: 8,
        field_2: 16,
    },
    ENUM: EnumWithGeneric<bool> = EnumWithGeneric::VariantOne(true),
}
 
fn main(
    switch: bool,
    u_8: u8,
    some_struct: StructWithGeneric<u8>,
    some_enum: EnumWithGeneric<bool>,
) -> bool {
    switch == BOOL && u_8 == U8 && some_struct == STRUCT && some_enum == ENUM
}

每个可配置常量在 SDK 中都会获得一个专用的 with 方法。例如,常量 U8 将获得名为 with_U8 的方法,该方法接受 sway 中定义的相同类型。下面是一个示例,其中我们链接了几个 with 方法,并使用新的常量更新了断言。

abigen!(Predicate(
    name = "MyPredicate",
    abi = "e2e/sway/predicates/predicate_configurables/out/release/predicate_configurables-abi.json"
));
 
let new_struct = StructWithGeneric {
    field_1: 32u8,
    field_2: 64,
};
let new_enum = EnumWithGeneric::VariantTwo;
 
let configurables = MyPredicateConfigurables::default()
    .with_U8(8)?
    .with_STRUCT(new_struct.clone())?
    .with_ENUM(new_enum.clone())?;
 
let predicate_data =
    MyPredicateEncoder::default().encode_data(true, 8u8, new_struct, new_enum)?;
 
let mut predicate: Predicate = Predicate::load_from(
    "sway/predicates/predicate_configurables/out/release/predicate_configurables.bin",
)?
.with_data(predicate_data)
.with_configurables(configurables);