Here is a very sample crude implemetation:
Receiver interface declaration:
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>;
}
Then checking if the interface is implemented:
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);
And then taking action based on the call result:
/**
* 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);
}
}
Full code: https://github.com/miohtama/advanced-fungible/blob/master/contract/token/src/receiver.rs
Optimally, NEAR should offer a branching primitive in in promise chains, so that the conditional chains could be build right off the start and you would have one less round trip back to your original caller contract after the interface check.