@@ -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 } ;
5656 }
5757
5858 /// Insert a verifier address whose VP will verify the tx.
59- pub ( crate ) fn insert_verifier ( & mut self , addr : & Address ) {
59+ pub ( crate ) fn insert_verifier ( & self , addr : & Address ) {
6060 self . verifiers . borrow_mut ( ) . insert ( addr. clone ( ) ) ;
6161 }
6262
@@ -184,40 +184,134 @@ where
184184 )
185185 }
186186
187- fn validate_masp_withdraw (
188- & self ,
189- from_account : & IbcAccountId ,
190- ) -> Result < ( ) , HostError > {
191- if from_account. is_shielded ( ) && !self . has_masp_tx {
192- return Err ( HostError :: Other {
193- description : format ! (
194- "Set refund address {from_account} without including an \
195- IBC unshielding MASP transaction"
196- ) ,
197- } ) ;
198- }
199- Ok ( ( ) )
200- }
201-
202187 #[ inline]
203- fn maybe_store_masp_note_commitments (
188+ fn maybe_handle_masp_memoless_shielding < F > (
204189 & self ,
205190 to_account : & IbcAccountId ,
206191 token : & Address ,
207192 amount : & Amount ,
208- ) -> Result < ( ) , HostError >
193+ transfer : F ,
194+ ) -> Result < Amount , HostError >
209195 where
196+ F : FnOnce ( Amount ) -> Result < ( ) , HostError > ,
210197 Params :
211198 namada_systems:: parameters:: Read < <C as IbcStorageContext >:: Storage > ,
212199 Token : namada_systems:: trans_token:: Read < <C as IbcStorageContext >:: Storage > ,
213200 ShieldedToken : namada_systems:: shielded_token:: Write <
214201 <C as IbcStorageContext >:: Storage ,
215202 > ,
216203 {
204+ let mut amount = * amount;
205+
217206 if let IbcAccountId :: Shielded ( owner_pa) = to_account {
218- self . store_masp_note_commitments ( owner_pa, token, amount) ?;
207+ if let Some ( fee) = self . get_masp_shielding_fee ( token, & amount) ? {
208+ amount = amount. checked_sub ( fee) . ok_or_else ( || {
209+ HostError :: Other {
210+ description : "Shielding fee greater than deposited \
211+ amount"
212+ . to_string ( ) ,
213+ }
214+ } ) ?;
215+
216+ transfer ( fee) ?;
217+ }
218+
219+ self . store_masp_note_commitments ( owner_pa, token, & amount) ?;
219220 }
220- Ok ( ( ) )
221+
222+ Ok ( amount)
223+ }
224+
225+ #[ inline]
226+ fn maybe_handle_masp_unshielding < F > (
227+ & self ,
228+ from_account : & IbcAccountId ,
229+ token : & Address ,
230+ amount : & Amount ,
231+ transfer : F ,
232+ ) -> Result < Amount , HostError >
233+ where
234+ F : FnOnce ( Amount ) -> Result < ( ) , HostError > ,
235+ Params :
236+ namada_systems:: parameters:: Read < <C as IbcStorageContext >:: Storage > ,
237+ {
238+ let mut amount = * amount;
239+
240+ if !self . has_masp_tx {
241+ return if from_account. is_transparent ( ) {
242+ Ok ( amount)
243+ } else {
244+ Err ( HostError :: Other {
245+ description : format ! (
246+ "Set refund address {from_account} without including \
247+ an IBC unshielding MASP transaction"
248+ ) ,
249+ } )
250+ } ;
251+ }
252+
253+ if let Some ( fee) = self . get_masp_unshielding_fee ( token, & amount) ? {
254+ amount =
255+ amount. checked_sub ( fee) . ok_or_else ( || HostError :: Other {
256+ description : "Unshielding fee greater than withdrawn \
257+ amount"
258+ . to_string ( ) ,
259+ } ) ?;
260+
261+ transfer ( fee) ?;
262+ }
263+
264+ Ok ( amount)
265+ }
266+
267+ fn get_masp_shielding_fee (
268+ & self ,
269+ token : & Address ,
270+ amount : & Amount ,
271+ ) -> Result < Option < Amount > , HostError >
272+ where
273+ Params :
274+ namada_systems:: parameters:: Read < <C as IbcStorageContext >:: Storage > ,
275+ {
276+ let Some ( fee_percentage) = Params :: ibc_shielding_fee_percentage (
277+ self . inner . borrow ( ) . storage ( ) ,
278+ token,
279+ ) ?
280+ else {
281+ return Ok ( None ) ;
282+ } ;
283+
284+ Ok ( Some ( amount. checked_mul_dec ( fee_percentage) . ok_or_else (
285+ || HostError :: Other {
286+ description :
287+ "Overflow in MASP shielding fee computation" . to_string ( ) ,
288+ } ,
289+ ) ?) )
290+ }
291+
292+ fn get_masp_unshielding_fee (
293+ & self ,
294+ token : & Address ,
295+ amount : & Amount ,
296+ ) -> Result < Option < Amount > , HostError >
297+ where
298+ Params :
299+ namada_systems:: parameters:: Read < <C as IbcStorageContext >:: Storage > ,
300+ {
301+ let Some ( fee_percentage) = Params :: ibc_unshielding_fee_percentage (
302+ self . inner . borrow ( ) . storage ( ) ,
303+ token,
304+ ) ?
305+ else {
306+ return Ok ( None ) ;
307+ } ;
308+
309+ Ok ( Some ( amount. checked_mul_dec ( fee_percentage) . ok_or_else (
310+ || HostError :: Other {
311+ description :
312+ "Overflow in MASP unshielding fee computation" . to_string ( ) ,
313+ } ,
314+ ) ?) )
221315 }
222316
223317 fn store_masp_note_commitments (
@@ -512,7 +606,24 @@ where
512606 ) -> Result < ( ) , HostError > {
513607 let ( ibc_token, amount) = self . get_token_amount ( coin) ?;
514608
515- self . validate_masp_withdraw ( from_account) ?;
609+ let from_trans_account = if self . has_masp_tx {
610+ Cow :: Owned ( MASP )
611+ } else {
612+ from_account. to_address ( )
613+ } ;
614+ let amount = self . maybe_handle_masp_unshielding (
615+ from_account,
616+ & ibc_token,
617+ & amount,
618+ |fee| {
619+ self . insert_verifier ( & PGF ) ;
620+ self . inner
621+ . borrow_mut ( )
622+ . transfer_token ( & from_trans_account, & PGF , & ibc_token, fee)
623+ . map_err ( HostError :: from)
624+ } ,
625+ ) ?;
626+
516627 self . increment_per_epoch_withdraw_limits ( & ibc_token, amount) ?;
517628
518629 // A transfer of NUT tokens must be verified by their VP
@@ -522,16 +633,10 @@ where
522633 self . insert_verifier ( & ibc_token) ;
523634 }
524635
525- let from_account = if self . has_masp_tx {
526- Cow :: Owned ( MASP )
527- } else {
528- from_account. to_address ( )
529- } ;
530-
531636 self . inner
532637 . borrow_mut ( )
533638 . transfer_token (
534- & from_account ,
639+ & from_trans_account ,
535640 & IBC_ESCROW_ADDRESS ,
536641 & ibc_token,
537642 amount,
@@ -548,11 +653,21 @@ where
548653 ) -> Result < ( ) , HostError > {
549654 let ( ibc_token, amount) = self . get_token_amount ( coin) ?;
550655
551- self . increment_per_epoch_deposit_limits ( & ibc_token, amount) ?;
552- self . maybe_store_masp_note_commitments (
553- to_account, & ibc_token, & amount,
656+ let amount = self . maybe_handle_masp_memoless_shielding (
657+ to_account,
658+ & ibc_token,
659+ & amount,
660+ |fee| {
661+ self . insert_verifier ( & PGF ) ;
662+ self . inner
663+ . borrow_mut ( )
664+ . transfer_token ( & IBC_ESCROW_ADDRESS , & PGF , & ibc_token, fee)
665+ . map_err ( HostError :: from)
666+ } ,
554667 ) ?;
555668
669+ self . increment_per_epoch_deposit_limits ( & ibc_token, amount) ?;
670+
556671 self . inner
557672 . borrow_mut ( )
558673 . transfer_token (
@@ -572,9 +687,21 @@ where
572687 // The trace path of the denom is already updated if receiving the token
573688 let ( ibc_token, amount) = self . get_token_amount ( coin) ?;
574689
690+ let amount = self . maybe_handle_masp_memoless_shielding (
691+ account,
692+ & ibc_token,
693+ & amount,
694+ |fee| {
695+ self . insert_verifier ( & PGF ) ;
696+ self . inner
697+ . borrow_mut ( )
698+ . mint_token ( & PGF , & ibc_token, fee)
699+ . map_err ( HostError :: from)
700+ } ,
701+ ) ?;
702+
575703 self . update_mint_amount ( & ibc_token, amount, true ) ?;
576704 self . increment_per_epoch_deposit_limits ( & ibc_token, amount) ?;
577- self . maybe_store_masp_note_commitments ( account, & ibc_token, & amount) ?;
578705
579706 // A transfer of NUT tokens must be verified by their VP
580707 if ibc_token. is_internal ( )
@@ -603,7 +730,24 @@ where
603730 ) -> Result < ( ) , HostError > {
604731 let ( ibc_token, amount) = self . get_token_amount ( coin) ?;
605732
606- self . validate_masp_withdraw ( account) ?;
733+ let trans_account = if self . has_masp_tx {
734+ Cow :: Owned ( MASP )
735+ } else {
736+ account. to_address ( )
737+ } ;
738+ let amount = self . maybe_handle_masp_unshielding (
739+ account,
740+ & ibc_token,
741+ & amount,
742+ |fee| {
743+ self . insert_verifier ( & PGF ) ;
744+ self . inner
745+ . borrow_mut ( )
746+ . transfer_token ( & trans_account, & PGF , & ibc_token, fee)
747+ . map_err ( HostError :: from)
748+ } ,
749+ ) ?;
750+
607751 self . update_mint_amount ( & ibc_token, amount, false ) ?;
608752 self . increment_per_epoch_withdraw_limits ( & ibc_token, amount) ?;
609753
@@ -614,16 +758,10 @@ where
614758 self . insert_verifier ( & ibc_token) ;
615759 }
616760
617- let account = if self . has_masp_tx {
618- Cow :: Owned ( MASP )
619- } else {
620- account. to_address ( )
621- } ;
622-
623761 // The burn is "unminting" from the minted balance
624762 self . inner
625763 . borrow_mut ( )
626- . burn_token ( & account , & ibc_token, amount)
764+ . burn_token ( & trans_account , & ibc_token, amount)
627765 . map_err ( HostError :: from)
628766 }
629767}
0 commit comments