@@ -13,7 +13,7 @@ use ibc::apps::transfer::types::{Memo, PrefixedCoin, PrefixedDenom};
1313use ibc:: core:: host:: types:: error:: HostError ;
1414use ibc:: core:: host:: types:: identifiers:: { ChannelId , PortId } ;
1515use ibc:: core:: primitives:: Signer ;
16- use namada_core:: address:: { Address , InternalAddress , MASP } ;
16+ use namada_core:: address:: { Address , InternalAddress , MASP , PGF } ;
1717use namada_core:: arith:: { CheckedAdd , checked} ;
1818use namada_core:: masp:: { AssetData , CompactNote , PaymentAddress } ;
1919use namada_core:: token:: { Amount , MaspDigitPos } ;
5858 }
5959
6060 /// Insert a verifier address whose VP will verify the tx.
61- pub ( crate ) fn insert_verifier ( & mut self , addr : & Address ) {
61+ pub ( crate ) fn insert_verifier ( & self , addr : & Address ) {
6262 self . verifiers . borrow_mut ( ) . insert ( addr. clone ( ) ) ;
6363 }
6464
@@ -194,40 +194,138 @@ where
194194 )
195195 }
196196
197- fn validate_masp_withdraw (
198- & self ,
199- from_account : & IbcAccountId ,
200- ) -> Result < ( ) , HostError > {
201- if from_account. is_shielded ( ) && !self . has_masp_tx {
202- return Err ( HostError :: Other {
203- description : format ! (
204- "Set refund address {from_account} without including an \
205- IBC unshielding MASP transaction"
206- ) ,
207- } ) ;
208- }
209- Ok ( ( ) )
210- }
211-
212197 #[ inline]
213- fn maybe_store_masp_note_commitments (
198+ fn maybe_handle_masp_memoless_shielding < F > (
214199 & self ,
215200 to_account : & IbcAccountId ,
216201 token : & Address ,
217202 amount : & Amount ,
218- ) -> Result < ( ) , HostError >
203+ transfer : F ,
204+ ) -> Result < Amount , HostError >
219205 where
206+ F : FnOnce ( Amount ) -> Result < ( ) , HostError > ,
220207 Params :
221208 namada_systems:: parameters:: Read < <C as IbcStorageContext >:: Storage > ,
222209 Token : namada_systems:: trans_token:: Read < <C as IbcStorageContext >:: Storage > ,
223210 ShieldedToken : namada_systems:: shielded_token:: Write <
224211 <C as IbcStorageContext >:: Storage ,
225212 > ,
226213 {
214+ let mut amount = * amount;
215+
227216 if let IbcAccountId :: Shielded ( owner_pa) = to_account {
228- self . store_masp_note_commitments ( owner_pa, token, amount) ?;
217+ if let Some ( fee) = self . get_masp_shielding_fee ( token, & amount) ? {
218+ amount = amount. checked_sub ( fee) . ok_or_else ( || {
219+ HostError :: Other {
220+ description : "Shielding fee greater than deposited \
221+ amount"
222+ . to_string ( ) ,
223+ }
224+ } ) ?;
225+
226+ transfer ( fee) ?;
227+ }
228+
229+ self . store_masp_note_commitments ( owner_pa, token, & amount) ?;
229230 }
230- Ok ( ( ) )
231+
232+ Ok ( amount)
233+ }
234+
235+ #[ inline]
236+ fn maybe_handle_masp_unshielding < F > (
237+ & self ,
238+ from_account : & IbcAccountId ,
239+ token : & Address ,
240+ amount : & Amount ,
241+ transfer : F ,
242+ ) -> Result < Amount , HostError >
243+ where
244+ F : FnOnce ( Amount ) -> Result < ( ) , HostError > ,
245+ Params :
246+ namada_systems:: parameters:: Read < <C as IbcStorageContext >:: Storage > ,
247+ {
248+ let mut amount = * amount;
249+
250+ if !self . has_masp_tx {
251+ return if from_account. is_transparent ( ) {
252+ Ok ( amount)
253+ } else {
254+ Err ( HostError :: Other {
255+ description : format ! (
256+ "Set refund address {from_account} without including \
257+ an IBC unshielding MASP transaction"
258+ ) ,
259+ } )
260+ } ;
261+ }
262+
263+ if let Some ( fee) = self . get_masp_unshielding_fee ( token, & amount) ? {
264+ amount =
265+ amount. checked_sub ( fee) . ok_or_else ( || HostError :: Other {
266+ description : "Unshielding fee greater than withdrawn \
267+ amount"
268+ . to_string ( ) ,
269+ } ) ?;
270+
271+ transfer ( fee) ?;
272+ }
273+
274+ Ok ( amount)
275+ }
276+
277+ fn get_masp_shielding_fee (
278+ & self ,
279+ token : & Address ,
280+ amount : & Amount ,
281+ ) -> Result < Option < Amount > , HostError >
282+ where
283+ Params :
284+ namada_systems:: parameters:: Read < <C as IbcStorageContext >:: Storage > ,
285+ {
286+ if self . is_refund {
287+ return Ok ( None ) ;
288+ }
289+
290+ let Some ( fee_percentage) = Params :: ibc_shielding_fee_percentage (
291+ self . inner . borrow ( ) . storage ( ) ,
292+ token,
293+ ) ?
294+ else {
295+ return Ok ( None ) ;
296+ } ;
297+
298+ Ok ( Some ( amount. checked_mul_dec ( fee_percentage) . ok_or_else (
299+ || HostError :: Other {
300+ description :
301+ "Overflow in MASP shielding fee computation" . to_string ( ) ,
302+ } ,
303+ ) ?) )
304+ }
305+
306+ fn get_masp_unshielding_fee (
307+ & self ,
308+ token : & Address ,
309+ amount : & Amount ,
310+ ) -> Result < Option < Amount > , HostError >
311+ where
312+ Params :
313+ namada_systems:: parameters:: Read < <C as IbcStorageContext >:: Storage > ,
314+ {
315+ let Some ( fee_percentage) = Params :: ibc_unshielding_fee_percentage (
316+ self . inner . borrow ( ) . storage ( ) ,
317+ token,
318+ ) ?
319+ else {
320+ return Ok ( None ) ;
321+ } ;
322+
323+ Ok ( Some ( amount. checked_mul_dec ( fee_percentage) . ok_or_else (
324+ || HostError :: Other {
325+ description :
326+ "Overflow in MASP unshielding fee computation" . to_string ( ) ,
327+ } ,
328+ ) ?) )
231329 }
232330
233331 fn store_masp_note_commitments (
@@ -522,7 +620,24 @@ where
522620 ) -> Result < ( ) , HostError > {
523621 let ( ibc_token, amount) = self . get_token_amount ( coin) ?;
524622
525- self . validate_masp_withdraw ( from_account) ?;
623+ let from_trans_account = if self . has_masp_tx {
624+ Cow :: Owned ( MASP )
625+ } else {
626+ from_account. to_address ( )
627+ } ;
628+ let amount = self . maybe_handle_masp_unshielding (
629+ from_account,
630+ & ibc_token,
631+ & amount,
632+ |fee| {
633+ self . insert_verifier ( & PGF ) ;
634+ self . inner
635+ . borrow_mut ( )
636+ . transfer_token ( & from_trans_account, & PGF , & ibc_token, fee)
637+ . map_err ( HostError :: from)
638+ } ,
639+ ) ?;
640+
526641 self . increment_per_epoch_withdraw_limits ( & ibc_token, amount) ?;
527642
528643 // A transfer of NUT tokens must be verified by their VP
@@ -532,16 +647,10 @@ where
532647 self . insert_verifier ( & ibc_token) ;
533648 }
534649
535- let from_account = if self . has_masp_tx {
536- Cow :: Owned ( MASP )
537- } else {
538- from_account. to_address ( )
539- } ;
540-
541650 self . inner
542651 . borrow_mut ( )
543652 . transfer_token (
544- & from_account ,
653+ & from_trans_account ,
545654 & IBC_ESCROW_ADDRESS ,
546655 & ibc_token,
547656 amount,
@@ -558,11 +667,21 @@ where
558667 ) -> Result < ( ) , HostError > {
559668 let ( ibc_token, amount) = self . get_token_amount ( coin) ?;
560669
561- self . increment_per_epoch_deposit_limits ( & ibc_token, amount) ?;
562- self . maybe_store_masp_note_commitments (
563- to_account, & ibc_token, & amount,
670+ let amount = self . maybe_handle_masp_memoless_shielding (
671+ to_account,
672+ & ibc_token,
673+ & amount,
674+ |fee| {
675+ self . insert_verifier ( & PGF ) ;
676+ self . inner
677+ . borrow_mut ( )
678+ . transfer_token ( & IBC_ESCROW_ADDRESS , & PGF , & ibc_token, fee)
679+ . map_err ( HostError :: from)
680+ } ,
564681 ) ?;
565682
683+ self . increment_per_epoch_deposit_limits ( & ibc_token, amount) ?;
684+
566685 self . inner
567686 . borrow_mut ( )
568687 . transfer_token (
@@ -582,9 +701,21 @@ where
582701 // The trace path of the denom is already updated if receiving the token
583702 let ( ibc_token, amount) = self . get_token_amount ( coin) ?;
584703
704+ let amount = self . maybe_handle_masp_memoless_shielding (
705+ account,
706+ & ibc_token,
707+ & amount,
708+ |fee| {
709+ self . insert_verifier ( & PGF ) ;
710+ self . inner
711+ . borrow_mut ( )
712+ . mint_token ( & PGF , & ibc_token, fee)
713+ . map_err ( HostError :: from)
714+ } ,
715+ ) ?;
716+
585717 self . update_mint_amount ( & ibc_token, amount, true ) ?;
586718 self . increment_per_epoch_deposit_limits ( & ibc_token, amount) ?;
587- self . maybe_store_masp_note_commitments ( account, & ibc_token, & amount) ?;
588719
589720 // A transfer of NUT tokens must be verified by their VP
590721 if ibc_token. is_internal ( )
@@ -613,7 +744,24 @@ where
613744 ) -> Result < ( ) , HostError > {
614745 let ( ibc_token, amount) = self . get_token_amount ( coin) ?;
615746
616- self . validate_masp_withdraw ( account) ?;
747+ let trans_account = if self . has_masp_tx {
748+ Cow :: Owned ( MASP )
749+ } else {
750+ account. to_address ( )
751+ } ;
752+ let amount = self . maybe_handle_masp_unshielding (
753+ account,
754+ & ibc_token,
755+ & amount,
756+ |fee| {
757+ self . insert_verifier ( & PGF ) ;
758+ self . inner
759+ . borrow_mut ( )
760+ . transfer_token ( & trans_account, & PGF , & ibc_token, fee)
761+ . map_err ( HostError :: from)
762+ } ,
763+ ) ?;
764+
617765 self . update_mint_amount ( & ibc_token, amount, false ) ?;
618766 self . increment_per_epoch_withdraw_limits ( & ibc_token, amount) ?;
619767
@@ -624,16 +772,10 @@ where
624772 self . insert_verifier ( & ibc_token) ;
625773 }
626774
627- let account = if self . has_masp_tx {
628- Cow :: Owned ( MASP )
629- } else {
630- account. to_address ( )
631- } ;
632-
633775 // The burn is "unminting" from the minted balance
634776 self . inner
635777 . borrow_mut ( )
636- . burn_token ( & account , & ibc_token, amount)
778+ . burn_token ( & trans_account , & ibc_token, amount)
637779 . map_err ( HostError :: from)
638780 }
639781}
0 commit comments