如何检查NEAR帐户是否已部署智能合约并实现了所需的接口? 解决方法注意:

问题描述

是否有一种方法可以检查(在Rust中)智能合约内部,是否另一个帐户具有与其关联的智能合约并实现了某些接口?

尤其是在此功能中,我想检查收件人是否是智能合约,以及它是否具有必需的方法

trait FTReceiver {
    fn on_nft_receive(&self,sender: AddressID,token: AddressID) -> bool;
}
pub fn transfer(&mut self,recipient: AccountID,reference: String) {
    // note: env::* functions below don't exist!
    if env::has_smart_contract(recipient) && env::ctr_implements(recipient,FTReceiver) {
        Promise::new(token.clone()).function_call(
            "on_ft_receive".into(),&serde_json::to_vec(&json!({
                "sender": env::predecessor_account_id() /*...*/
            })),);
    }
}

解决方法

检查的唯一方法是尝试失败方法:

  1. 打个电话
  2. 设计足够的回调/保证处理失败情况。

不幸的是,这很复杂,我们无法处理极端情况。我们无法以可靠的方式检查Promise失败的原因。该承诺可能由于以下原因而失败:A)一个账户没有智能合约,B)智能合约没有被调用的函数C)函数失败(例如断言失败)。 在Simulation Tests存储库Error messages early in promise execution中描述了此限制。仿真测试使用与区块链完全相同的运行时代码。

解决方法

@ vlad-frolow注意到,我们的智能合约可以使用view方法来报告其界面。在撰写本文时,此视图方法尚无标准。

一个主意:

pub fn implements_nep(&self,nep String) -> bool {
   nep == "21" || nep == ...

}

注意:

正在进行一项工作,以检查NEAR运行时中的外部帐户。

,

这是一个非常简单的示例实现:

接收器接口声明:

use near_sdk::{ AccountId,Balance,ext_contract };
use near_sdk::json_types::U128;

/* The smart contract interface for handing incoming token transfers of Advanced Fungible.
 *
 */
#[ext_contract(ext_token_receiver)]
pub trait Receiver {

    /// Interface check promise to check if the receiver contract is able to handle Advanced Fungible
    /// Always return true
    fn is_receiver(self) -> PromiseOrValue<bool>;

    /// Notified after the balance transfer is complete. Must return true to finalise the transaction.
    /// TODO: More advanced error code / return value needed
    fn on_token_received(&mut self,sender_id: AccountId,amount_received: U128,amount_total: U128,message: Vec<u8>) -> PromiseOrValue<bool>;
}

然后检查接口是否已实现:

        let promise0 = env::promise_create(
            new_owner_id.clone(),b"is_receiver",&[],SINGLE_CALL_GAS/3,);

        let promise1 = env::promise_then(
            promise0,env::current_account_id(),b"handle_receiver",json!({
                "old_owner_id": owner_id,"new_owner_id": new_owner_id,"amount_received": amount.to_string(),"amount_total": new_target_balance.to_string(),"message": message,}).to_string().as_bytes(),);

        env::promise_return(promise1);

然后根据通话结果采取措施:

    /**
     * After trying to call receiving smart contract if it reports it can receive tokens.
     *
     * We gpt the interface test promise back. If the account was not smart contract,finalise the transaction.
     * Otherwise trigger the smart contract notifier.
     */
    pub fn handle_receiver(&mut self,old_owner_id: AccountId,new_owner_id: AccountId,message: Vec<u8>) {
        // Only callable by self
        assert_eq!(env::current_account_id(),env::predecessor_account_id());
        env::log(b"handle_receiver reached");

        let uint_amount_received: u128 = amount_received.into();
        let uint_amount_total: u128 = amount_total.into();

        if is_promise_success() {
            // The send() was destined to a compatible receiver smart contract.
            // Build another promise that notifies the smart contract
            // that is has received new tokens.
        } else {
            // Non-code account or incompatible smart contract
            // Finalise transaction now.
            self.ledger.finalise(new_owner_id,uint_amount_received);
        }
    }


完整代码:https://github.com/miohtama/advanced-fungible/blob/master/contract/token/src/receiver.rs

最理想的是,NEAR应该在promise链中提供一个分支原语,以便可以从一开始就建立条件链,并且在进行接口检查之后,可以减少往返原始呼叫者合同的时间。