@@ -2,9 +2,14 @@ use super::{
22 u256_from_big_decimal, u256_to_big_decimal, ChainSpec , ChainTaggedAddress , EthCoinType , EthDerivationMethod ,
33 EthPrivKeyPolicy , Public , WithdrawError , WithdrawRequest , WithdrawResult , ERC20_CONTRACT , H256 ,
44} ;
5+ use crate :: eth:: tron:: gasfree:: withdraw:: {
6+ is_standard_tron_withdraw_unavailable, maybe_build_tron_gasless_withdraw, validate_non_tron_gasless_request,
7+ TronGaslessMode ,
8+ } ;
9+ use crate :: eth:: tron:: gasfree:: GaslessWithdrawError ;
510use crate :: eth:: tron:: sign:: sign_tron_transaction;
611use crate :: eth:: tron:: withdraw:: {
7- build_tron_trc20_withdraw , build_tron_trx_withdraw , validate_tron_fee_policy , TronWithdrawContext ,
12+ build_standard_tron_withdraw , validate_tron_fee_policy , StandardTronWithdrawParams , TronWithdrawContext ,
813} ;
914use crate :: eth:: tron:: TronAddress ;
1015use crate :: eth:: wallet_connect:: WcEthTxParams ;
@@ -28,13 +33,15 @@ use crypto::hw_rpc_task::HwRpcTaskAwaitingStatus;
2833use crypto:: trezor:: trezor_rpc_task:: { TrezorRequestStatuses , TrezorRpcTaskProcessor } ;
2934use crypto:: { CryptoCtx , HwRpcError } ;
3035use ethabi:: Token ;
36+ use ethereum_types:: U256 ;
3137use futures:: compat:: Future01CompatExt ;
3238use kdf_walletconnect:: { WalletConnectCtx , WalletConnectOps } ;
3339use mm2_core:: mm_ctx:: MmArc ;
3440use mm2_err_handle:: map_mm_error:: MapMmError ;
3541use mm2_err_handle:: mm_error:: MmResult ;
3642use mm2_err_handle:: prelude:: { MapToMmResult , MmError , MmResultExt , OrMmError } ;
3743use prost:: Message ;
44+ use std:: convert:: TryFrom ;
3845use std:: ops:: Deref ;
3946use std:: sync:: Arc ;
4047#[ cfg( target_arch = "wasm32" ) ]
6774 #[ allow( clippy:: result_large_err) ]
6875 fn on_finishing ( & self ) -> Result < ( ) , MmError < WithdrawError > > ;
6976
77+ #[ allow( clippy:: result_large_err) ]
78+ fn on_fetching_gasless_quote ( & self ) -> Result < ( ) , MmError < WithdrawError > > ;
79+
80+ #[ allow( clippy:: result_large_err) ]
81+ fn on_signing_gasless_authorization ( & self ) -> Result < ( ) , MmError < WithdrawError > > ;
82+
7083 /// Signs the transaction with a Trezor hardware wallet.
7184 async fn sign_tx_with_trezor (
7285 & self ,
8497 from_tagged : & ChainTaggedAddress ,
8598 to_tagged : & ChainTaggedAddress ,
8699 tx : TransactionData ,
87- amount : ethabi :: ethereum_types :: U256 ,
100+ amount : U256 ,
88101 total_fee : & BigDecimal ,
89102 fee_details : TxFeeDetails ,
90103 ) -> WithdrawResult {
@@ -286,6 +299,7 @@ where
286299 "Memo is not yet supported for TRON withdraw (TRON charges 1 TRX burn fee for memo)" . to_owned ( ) ,
287300 ) ) ;
288301 }
302+ let gasless_mode = TronGaslessMode :: try_from ( req) ?;
289303
290304 // 2. Validate key policy (only Iguana/HDWallet supported for TRON MVP)
291305 match coin. priv_key_policy {
@@ -314,26 +328,39 @@ where
314328 . tron_rpc ( )
315329 . ok_or_else ( || WithdrawError :: InternalError ( "TRON RPC client is not initialized" . to_owned ( ) ) ) ?;
316330
317- let my_balance = coin. address_balance ( from_tagged) . compat ( ) . await . map_mm_err ( ) ?;
318- let my_balance_dec = u256_to_big_decimal ( my_balance, coin. decimals ) . map_mm_err ( ) ?;
331+ let trc20_contract = match & coin. coin_type {
332+ EthCoinType :: Eth => None ,
333+ EthCoinType :: Erc20 { token_addr, .. } => Some ( TronAddress :: from ( * token_addr) ) ,
334+ EthCoinType :: Nft { .. } => {
335+ return MmError :: err ( WithdrawError :: ProtocolNotSupported (
336+ "NFT withdraw is not supported for TRON" . to_owned ( ) ,
337+ ) )
338+ } ,
339+ } ;
319340
320- if req. max && my_balance. is_zero ( ) {
321- return MmError :: err ( WithdrawError :: ZeroBalanceToWithdrawMax ) ;
341+ if gasless_mode == TronGaslessMode :: Gasless {
342+ let Some ( contract_tron) = trc20_contract else {
343+ return MmError :: err ( WithdrawError :: Gasless ( GaslessWithdrawError :: Unavailable ) ) ;
344+ } ;
345+
346+ if let Some ( details) =
347+ maybe_build_tron_gasless_withdraw ( self , tron, & from_tagged, & to_tagged, contract_tron, gasless_mode)
348+ . await ?
349+ {
350+ return Ok ( details) ;
351+ }
322352 }
323353
354+ let my_balance = coin. address_balance ( from_tagged) . compat ( ) . await . map_mm_err ( ) ?;
355+ let my_balance_dec = u256_to_big_decimal ( my_balance, coin. decimals ) . map_mm_err ( ) ?;
356+
324357 let amount_base_units = if req. max {
358+ if my_balance. is_zero ( ) {
359+ return MmError :: err ( WithdrawError :: ZeroBalanceToWithdrawMax ) ;
360+ }
325361 my_balance
326362 } else {
327- let amount = u256_from_big_decimal ( & req. amount , coin. decimals ) . map_mm_err ( ) ?;
328- if amount > my_balance {
329- let required_dec = u256_to_big_decimal ( amount, coin. decimals ) . map_mm_err ( ) ?;
330- return MmError :: err ( WithdrawError :: NotSufficientBalance {
331- coin : ticker. to_owned ( ) ,
332- available : my_balance_dec,
333- required : required_dec,
334- } ) ;
335- }
336- amount
363+ u256_from_big_decimal ( & req. amount , coin. decimals ) . map_mm_err ( ) ?
337364 } ;
338365
339366 // 4. Convert addresses to TRON format
@@ -347,7 +374,7 @@ where
347374 let resources = tron. get_account_resource ( & from_tron) . await . map_mm_err ( ) ?;
348375 let prices = tron. get_chain_prices ( ) . await . map_mm_err ( ) ?;
349376
350- // 7. Build tx, estimate fees — branching on coin type
377+ // 7. Build tx and estimate fees.
351378 let withdraw_ctx = TronWithdrawContext {
352379 from : & from_tron,
353380 to : & to_tron,
@@ -357,24 +384,42 @@ where
357384 fee_coin : ticker,
358385 expiration_seconds : req. expiration_seconds ,
359386 } ;
360- let ( raw, tron_fee_details, final_amount) = match & coin. coin_type {
361- EthCoinType :: Eth => {
362- build_tron_trx_withdraw ( & withdraw_ctx, amount_base_units, my_balance, & my_balance_dec, req. max ) ?
363- } ,
364- EthCoinType :: Erc20 {
365- token_addr, platform, ..
366- } => {
367- let contract_tron = TronAddress :: from ( * token_addr) ;
368- let trc20_ctx = TronWithdrawContext {
369- fee_coin : platform. as_str ( ) ,
370- ..withdraw_ctx
387+ let standard_tron_withdraw_params = StandardTronWithdrawParams {
388+ coin_type : & coin. coin_type ,
389+ ticker,
390+ decimals : coin. decimals ,
391+ balance : my_balance,
392+ balance_dec : & my_balance_dec,
393+ amount_base_units,
394+ is_max : req. max ,
395+ ctx : & withdraw_ctx,
396+ tron,
397+ } ;
398+
399+ let ( raw, tron_fee_details, final_amount) = match build_standard_tron_withdraw ( standard_tron_withdraw_params)
400+ . await
401+ {
402+ Ok ( prepared) => prepared,
403+ Err ( standard_fee_route_err) => {
404+ if gasless_mode != TronGaslessMode :: Auto
405+ || req. max
406+ || !is_standard_tron_withdraw_unavailable ( & standard_fee_route_err)
407+ {
408+ return Err ( standard_fee_route_err) ;
409+ }
410+
411+ let Some ( contract_tron) = trc20_contract else {
412+ return Err ( standard_fee_route_err) ;
371413 } ;
372- build_tron_trc20_withdraw ( & trc20_ctx, tron, & contract_tron, amount_base_units) . await ?
373- } ,
374- EthCoinType :: Nft { .. } => {
375- return MmError :: err ( WithdrawError :: ProtocolNotSupported (
376- "NFT withdraw is not supported for TRON" . to_owned ( ) ,
377- ) )
414+
415+ if let Some ( details) =
416+ maybe_build_tron_gasless_withdraw ( self , tron, & from_tagged, & to_tagged, contract_tron, gasless_mode)
417+ . await ?
418+ {
419+ return Ok ( details) ;
420+ }
421+
422+ return Err ( standard_fee_route_err) ;
378423 } ,
379424 } ;
380425
@@ -417,6 +462,8 @@ where
417462 return self . build_tron_withdraw ( from_tagged, to_tagged) . await ;
418463 }
419464
465+ validate_non_tron_gasless_request ( & req) ?;
466+
420467 // ── EVM withdraw: existing path (unchanged) ──
421468 let my_balance = coin. address_balance ( from_tagged) . compat ( ) . await . map_mm_err ( ) ?;
422469 let my_balance_dec = u256_to_big_decimal ( my_balance, coin. decimals ) . map_mm_err ( ) ?;
@@ -603,6 +650,18 @@ impl EthWithdraw for InitEthWithdraw {
603650 . map_mm_err ( )
604651 }
605652
653+ fn on_fetching_gasless_quote ( & self ) -> Result < ( ) , MmError < WithdrawError > > {
654+ self . task_handle
655+ . update_in_progress_status ( WithdrawInProgressStatus :: FetchingGaslessQuote )
656+ . map_mm_err ( )
657+ }
658+
659+ fn on_signing_gasless_authorization ( & self ) -> Result < ( ) , MmError < WithdrawError > > {
660+ self . task_handle
661+ . update_in_progress_status ( WithdrawInProgressStatus :: SigningGaslessAuthorization )
662+ . map_mm_err ( )
663+ }
664+
606665 async fn sign_tx_with_trezor (
607666 & self ,
608667 derivation_path : & DerivationPath ,
@@ -677,6 +736,14 @@ impl EthWithdraw for StandardEthWithdraw {
677736 Ok ( ( ) )
678737 }
679738
739+ fn on_fetching_gasless_quote ( & self ) -> Result < ( ) , MmError < WithdrawError > > {
740+ Ok ( ( ) )
741+ }
742+
743+ fn on_signing_gasless_authorization ( & self ) -> Result < ( ) , MmError < WithdrawError > > {
744+ Ok ( ( ) )
745+ }
746+
680747 async fn sign_tx_with_trezor (
681748 & self ,
682749 _derivation_path : & DerivationPath ,
0 commit comments