如何检查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() /*...*/
})),
);
}
}

唯一的检查方法是try&失败方法:

  1. 打一个乐观的电话
  2. 设计足够的回调/承诺来处理失败的情况

不幸的是,这很复杂,我们无法处理边缘情况。我们无法可靠地检查Promise失败的原因。承诺可能会失败,因为:A(帐户没有智能合约,B(智能合约没有被调用的函数C(函数失败(例如断言失败(。这个限制在Simulation Tests存储库中有描述:promise执行早期的错误消息。模拟测试使用与区块链完全相同的运行时代码。

变通办法

正如@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",
&[],
0,
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(),
0,
SINGLE_CALL_GAS/3,
);
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, amount_received: U128, amount_total: U128, 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应该在承诺链中提供一个分支原语,这样条件链就可以从一开始就构建起来,并且在接口检查后,您可以少往返一次原始调用方契约。

最新更新