可见性和可变性
可见性
Starknet合约有两种功能:
- 外部可访问、任何人都可调用的函数。
- 只能在内部访问的函数,只能被合约中的其他函数调用。
这些函数通常也分为两个不同的实现块。第一个impl
块用于外部访问的函数,明确标注了 #[abi(embed_v0)]
属性。这表明该代码块中的所有函数都可以作为交易或视图函数调用。第二个用于内部可访问函数的 impl
块没有注释任何属性,这意味着该块中的所有函数默认都是私有的。
状态可变性
无论函数是内部函数还是外部函数,它都可以修改或不修改合约的状态。当我们在智能合约中声明与存储变量交互的函数时,
我们需要将 ContractState
添加为函数的第一个参数,明确说明我们正在访问 合约的状态。这有两种不同的方法:
- 如果我们希望我们的函数能够更改合约的状态,我们可以像这样通过引用来传递它:ref self:ContractState`。
- 如果我们希望我们的函数是只读的,并且不更改合约的状态,我们可以通过快照传递它,如下所示:
self:@ContractState
.
只读函数(也称为视图函数)可以直接调用,无需进行事务处理。你可以直接通过 RPC 节点与它们交互,读取合约的状态,而且可以自由调用! 而修改合约状态的外部函数则只能通过交易来调用。
内部函数不能被外部调用,同样的原则也适用于状态可变性。
让我们通过一个简单的合约示例来了解这些功能:
#[starknet::interface]
trait IExampleContract<TContractState> {
fn set(ref self: TContractState, value: u32);
fn get(self: @TContractState) -> u32;
}
#[starknet::contract]
mod ExampleContract {
#[storage]
struct Storage {
value: u32
}
// The `abi(embed_v0)` attribute indicates that all the functions in this implementation can be called externally.
// Omitting this attribute would make all the functions in this implementation internal.
#[abi(embed_v0)]
impl ExampleContract of super::IExampleContract<ContractState> {
// The `set` function can be called externally because it is written inside an implementation marked as `#[external]`.
// It can modify the contract's state as it is passed as a reference.
fn set(ref self: ContractState, value: u32) {
self.value.write(value);
}
// The `get` function can be called externally because it is written inside an implementation marked as `#[external]`.
// However, it can't modify the contract's state is passed as a snapshot: it is only a "view" function.
fn get(self: @ContractState) -> u32 {
// We can call an internal function from any functions within the contract
PrivateFunctionsTrait::_read_value(self)
}
}
// The lack of the `external` attribute indicates that all the functions in this implementation can only be called internally.
// We name the trait `PrivateFunctionsTrait` to indicate that it is an internal trait allowing us to call internal functions.
#[generate_trait]
impl PrivateFunctions of PrivateFunctionsTrait {
// The `_read_value` function is outside the implementation that is marked as `#[abi(embed_v0)]`, so it's an _internal_ function
// and can only be called from within the contract.
// However, it can't modify the contract's state is passed as a snapshot: it is only a "view" function.
fn _read_value(self: @ContractState) -> u32 {
self.value.read()
}
}
}