defuse/contract/tokens/nep245/
resolver.rs1use std::borrow::Cow;
2
3use defuse_near_utils::{Lock, REFUND_MEMO, promise_result_checked_json_with_len};
4use defuse_nep245::{
5 ClearedApproval, MtEvent, MtTransferEvent, TokenId, resolver::MultiTokenResolver,
6};
7use near_sdk::{AccountId, json_types::U128, near, require};
8
9use crate::contract::{Contract, ContractExt};
10
11#[near]
12impl MultiTokenResolver for Contract {
13 #[private]
14 fn mt_resolve_transfer(
15 &mut self,
16 previous_owner_ids: Vec<AccountId>,
17 receiver_id: AccountId,
18 token_ids: Vec<TokenId>,
19 #[allow(unused_mut)] mut amounts: Vec<U128>,
20 approvals: Option<Vec<Option<Vec<ClearedApproval>>>>,
21 ) -> Vec<U128> {
22 require!(approvals.is_none(), "approvals are not supported");
23 require!(
24 !token_ids.is_empty()
25 && previous_owner_ids.len() == token_ids.len()
26 && amounts.len() == token_ids.len(),
27 "invalid args"
28 );
29
30 let mut refunds = promise_result_checked_json_with_len::<Vec<U128>>(0, amounts.len())
31 .ok()
32 .and_then(Result::ok)
33 .filter(|refund| refund.len() == amounts.len())
34 .unwrap_or_else(|| amounts.clone());
35
36 let sender_id = previous_owner_ids.first().cloned().unwrap();
37
38 for ((token_id, previous_owner_id), (amount, refund)) in token_ids
39 .iter()
40 .map(|token_id| token_id.parse().unwrap_or_else(|e| panic!("{e}")))
41 .zip(previous_owner_ids)
42 .zip(amounts.iter_mut().zip(&mut refunds))
43 {
44 require!(
45 sender_id == previous_owner_id,
46 "approvals are not supported"
47 );
48
49 refund.0 = refund.0.min(amount.0);
50 let Some(receiver) = self
51 .accounts
52 .get_mut(&receiver_id)
53 .map(Lock::as_inner_unchecked_mut)
66 else {
67 return amounts;
69 };
70 let receiver_balance = receiver.token_balances.amount_for(&token_id);
71 refund.0 = refund.0.min(receiver_balance);
73 if refund.0 == 0 {
74 continue;
76 }
77
78 receiver
80 .token_balances
81 .sub(token_id.clone(), refund.0)
82 .unwrap();
83 self.accounts
85 .get_or_create(previous_owner_id)
86 .as_inner_unchecked_mut()
88 .token_balances
89 .add(token_id, refund.0)
90 .unwrap();
91
92 amount.0 -= refund.0;
94 }
95
96 let (refunded_token_ids, refunded_amounts): (Vec<_>, Vec<_>) = token_ids
97 .into_iter()
98 .zip(refunds)
99 .filter(|(_token_id, refund)| refund.0 > 0)
100 .unzip();
101
102 if !refunded_amounts.is_empty() {
103 MtEvent::MtTransfer(Cow::Borrowed(
104 [MtTransferEvent {
105 authorized_id: None,
106 old_owner_id: Cow::Borrowed(&receiver_id),
107 new_owner_id: Cow::Borrowed(&sender_id),
108 token_ids: refunded_token_ids.into(),
109 amounts: refunded_amounts.into(),
110 memo: Some(REFUND_MEMO.into()),
111 }]
112 .as_slice(),
113 ))
114 .emit();
117 }
118
119 amounts
120 }
121}