首页 > 解决方案 > Staking-pool 核心合约 - ext_contract 宏和回调

问题描述

  1. 在函数中,当它是调用合约的一部分时internal_restake,为什么我们必须像外部交叉合约调用一样访问该函数?on_stake_action难道我们就不能这样做.then(self.on_stake_action())吗?我假设这与它是来自stake()承诺调用的回调有关。

  2. 在什么情况下你会为合约本身做一个接口ext_self

  3. 简而言之,#[ext_contract()] 宏做了什么?


    -------- Staking Pool Contract Code 下面 -------- lib.rs 第 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 第 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 第 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,
            ));
    }

标签: rustnearprotocol

解决方案


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

/// 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 程序宏中,输入和输出中的标记流 ( pub, trait, SelfContract, ...)。在这种情况下,输出不是一个特征,而是一个模块](https://doc.rust-lang.org/reference/items/modules.html)命名ext_self。然后将一个函数on_stake_action添加到模块中,并使用删除 self 参数和添加三个新参数进行修改,并返回一个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是承诺调用的气体。此外,生成了用于实现该功能的代码;它对函数的参数进行编码(在这种情况下没有任何参数)并创建一个调用方法的承诺on_stake_action

最初声明这是一个特征的原因是它不需要实现,并且具有良好 rust 支持的 IDE 已经扩展了这个宏,允许您将其ext_self用作模块,即使您看不到它是什么.

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


推荐阅读