质押池核心合约 - ext_contract 宏和回调

Staking-pool core contract - ext_contract macro and callbacks

  1. internal_restake函数中,为什么我们必须访问on_stake_action函数,就好像它是一个外部交叉合约调用,而它的函数是调用的一部分合同?我们不能只做 .then(self.on_stake_action()) 吗?我假设它与面部有关,它是 stake() promise 调用的回调。

  2. 在什么情况下你会像ext_self一样为合约本身制作一个接口?

  3. 简而言之,#[ext_contract()] 宏的作用是什么?


    -------- 下面的质押池合约代码-------------------- lib.rs lines 155~162

/// Interface for the contract itself.
#[ext_contract(ext_self)]
pub trait SelfContract {
    /// A callback to check the result of the staking action.
    fn on_stake_action(&mut self);
}

lib.rs lines 399~421

   pub fn on_stake_action(&mut self) {
        assert_eq!(
            env::current_account_id(),
            env::predecessor_account_id(),
            "Can be called only as a callback"
        );

        assert_eq!(
            env::promise_results_count(),
            1,
            "Contract expected a result on the callback"
        );
        let stake_action_succeeded = match env::promise_result(0) {
            PromiseResult::Successful(_) => true,
            _ => false,
        };

        // If the stake action failed and the current locked amount is positive, then the contract
        // has to unstake.
        if !stake_action_succeeded && env::account_locked_balance() > 0 {
            Promise::new(env::current_account_id()).stake(0, self.stake_public_key.clone());
        }
    }

internal.rs lines 8~22

    /// Restakes the current `total_staked_balance` again.
    pub(crate) fn internal_restake(&mut self) {
        if self.paused {
            return;
        }
        // Stakes with the staking public key. If the public key is invalid the entire function
        // call will be rolled back.
        Promise::new(env::current_account_id())
            .stake(self.total_staked_balance, self.stake_public_key.clone())
            .then(ext_self::on_stake_action(
                &env::current_account_id(),
                NO_DEPOSIT,
                ON_STAKE_ACTION_GAS,
            ));
    }

合同的“外部”接口的原因是对方法的调用是外部的,因为它是作为承诺操作的一部分“调用”的。

/// Interface for the contract itself.
#[ext_contract(ext_self)]
pub trait SelfContract {
    /// A callback to check the result of the staking action.
    fn on_stake_action(&mut self);
}

首先,在 Rust 过程宏中,输入和输出中的标记流(pubtraitSelfContract、...)。在这种情况下,输出不是一个特征,而是一个名为 ext_self 的模块](https://doc.rust-lang.org/reference/items/modules.html)。然后将函数 on_stake_action 添加到模块并进行修改,删除 self 参数并添加三个新参数和 returns a Promise.

        Promise::new(env::current_account_id())
            .stake(self.total_staked_balance, self.stake_public_key.clone())
            .then(ext_self::on_stake_action(
                &env::current_account_id(),
                NO_DEPOSIT,
                ON_STAKE_ACTION_GAS,
            ));

注意ext_self是模块::是访问的路径分隔符on_stake_action&env::current_account_id()是接收者,NO_DEPOSIT是附加存款, ON_STAKE_ACTION_GAS 是 promise 调用的 gas。此外,还生成了实现该功能的代码;它对函数的参数进行编码(在这种情况下没有任何参数)并创建一个调用方法 on_stake_action.

的承诺

这个初始声明是一个特征的原因是它不需要实现,IDE 具有良好的 Rust 支持已经扩展了这个宏,允许你使用 ext_self作为一个模块,即使你看不到它是什么。

尽管 ext_contract 宏将对同一合同的调用与其他合同的调用视为相同,但您提出了一个很好的观点。因此,也许一个新的有用功能是创建一个已经使用 env::current_account_id().

的新宏