std-lib
包含一个名为 run_external
的函数,允许 Sway 合约执行任意外部 Sway 代码。
此功能启用了可升级合约和代理等功能。
可升级合约旨在允许智能合约的逻辑在部署后进行更新。
考虑以下代理合约示例:
#[namespace(my_storage_namespace)]
storage {
target_contract: Option<ContractId> = None,
}
impl Proxy for Contract {
#[storage(write)]
fn set_target_contract(id: ContractId) {
storage.target_contract.write(Some(id));
}
#[storage(read)]
fn double_input(_value: u64) -> u64 {
let target = storage.target_contract.read().unwrap();
run_external(target)
}
}
该合约有两个函数:
set_target_contract
使用外部合约的 ContractId
更新存储中的 target_contract
变量。 double_input
从存储中读取 target_contract
并使用它运行外部代码。如果 target_contract
中有一个同名的函数(double_input
),则将运行外部 double_input
函数中的代码。在这种情况下,该函数将返回一个 u64
。 注意在上面的 Proxy
示例中,存储块具有 namespace
属性。使用此属性被认为是所有 Sway 代理合约的最佳实践,因为它可以防止与实现合约的存储冲突,因为实现合约可以访问两个存储上下文。
下面是一个实现合约可能的示例:
storage {
value: u64 = 0,
// to stay compatible, this has to stay the same in the next version
}
impl Implementation for Contract {
#[storage(write)]
fn double_input(value: u64) -> u64 {
let new_value = value * 2;
storage.value.write(new_value);
new_value
}
}
该合约有一个名为 double_input
的函数,该函数计算输入值的两倍,更新存储中的 value
变量,并返回新值。
直接调用合约和使用 run_external
方法之间有几个主要区别。
首先,使用 run_external
时,不需要外部合约的 ABI。代理合约除了其 ContractId
外,对外部合约一无所知。
其次,代理合约的存储上下文保留给加载的代码。这意味着在上面的示例中,value
变量在 代理 合约的存储中被更新。
例如,如果直接调用实现合约读取 value
变量,您将获得与通过代理合约读取不同的结果。代理合约加载代码并在其自己的上下文中执行。
如果目标合约中不存在该函数名称,但存在 fallback
函数,则将触发 fallback
函数。
如果没有
fallback
函数,交易将回滚。
您可以使用 std-lib
中的 call_frames
模块访问 fallback
函数的参数。例如,要在下面的代理函数中访问 _foo
输入参数,可以在 fallback
函数中使用 called_args
方法:
fn does_not_exist_in_the_target(_foo: u64) -> u64 {
run_external(TARGET)
}
#[fallback]
fn fallback() -> u64 {
use std::call_frames::*;
__log(3);
__log(called_method());
__log("double_value");
__log(called_method() == "double_value");
let foo = called_args::<u64>();
foo * 3
}
在这种情况下,does_not_exist_in_the_target
函数将返回 _foo * 3
。
run_external
函数的一些限制是:
run_external
之前,不能在另一个调用帧中使用调用栈。只能在包含 run_external
的调用帧中使用调用栈。