可见性和可变性

可见性

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()
        }
    }
}

访问 Voyager 上的合约,或在 Remix 中尝试它。

Last change: 2023-10-19, commit: 3e4c697