use crate::events::VaultWithdraw; use crate::*; use near_contract_standards::fungible_token::events::FtMint; use near_sdk::{json_types::U128, log, near, AccountId, PromiseError}; #[near] impl Contract { #[private] pub fn asset_transfer_callback( &mut self, #[callback_result] call_result: Result<(), PromiseError>, _operation_id: Option, asset: Asset, amount: U128, receiver_id: AccountId, memo: Option, ) { match call_result { Ok(_) => { log!( "Asset transfer successful: {} {} assets to {} with memo: {:?}", amount.0, asset, receiver_id, memo, ); } Err(err) => { log!( "Asset transfer failed: {:?}. Rolling back asset balance for {}", err, asset ); // Rollback: Restore the decreased asset balance self.internal_update_asset_balance(&asset, amount, true); log!("Rolled back {} {} assets to vault balance", amount.0, asset); } } } #[private] pub fn redeem_transfer_callback( &mut self, #[callback_result] call_result: Result<(), PromiseError>, _operation_id: Option, owner_id: AccountId, receiver_id: AccountId, asset: Asset, shares: U128, net_redeem_amount: U128, vault_fee: U128, protocol_fee: U128, _memo: Option, ) { match call_result { Ok(_) => { // Accumulate fees in vault self.internal_accumulate_fees(&asset, vault_fee, protocol_fee); log!( "Redeem transfer successful: {} shares redeemed for {} {} assets to {}", shares.0, net_redeem_amount.0, asset, receiver_id ); } Err(err) => { log!( "Redeem transfer failed: {:?}. Rolling back shares for {}", err, owner_id ); // Rollback: Re-mint the burned shares to restore user balance self.token.internal_deposit(&owner_id, shares.0); // Emit FT mint event to track the rollback FtMint { owner_id: &owner_id, amount: shares, memo: Some("Rollback from failed redeem transfer"), } .emit(); // Restore asset balance since transfer failed self.internal_update_asset_balance(&asset, net_redeem_amount, true); // Rollback withdrawal flow tracker if let Some(_cap) = self.vault_config.get_effective_withdrawal_cap() { self.state.withdrawal_flow_tracker.accumulated_amount.0 = self .state .withdrawal_flow_tracker .accumulated_amount .0 .saturating_sub(shares.0); } log!( "Rolled back {} shares to {} and restored {} {} assets", shares.0, owner_id, net_redeem_amount.0, asset ); } } } #[private] pub fn async_redeem_transfer_callback( &mut self, #[callback_result] call_result: Result<(), PromiseError>, operation_id: u64, owner_id: AccountId, receiver_id: AccountId, asset: Asset, shares: U128, net_redeem_amount: U128, vault_fee: U128, protocol_fee: U128, min_asset_amount: U128, memo: Option, activation_timestamp_nanosec: U128, confirmed_share_price: U128, ) { match call_result { Ok(_) => { // Accumulate fees in vault self.internal_accumulate_fees(&asset, vault_fee, protocol_fee); // Transfer successful - emit event and log success VaultWithdraw { owner_id: &owner_id, receiver_id: &receiver_id, asset: &asset, shares, withdraw_amount: net_redeem_amount, memo: memo.as_deref(), } .emit(); log!( "Async redeem transfer successful: operation {} - {} shares redeemed for {} {} assets to {}", operation_id, shares.0, net_redeem_amount.0, asset, receiver_id ); } Err(err) => { log!( "Async redeem transfer failed for operation {}: {:?}. Rolling back state", operation_id, err ); // Rollback: Restore asset balance since transfer failed self.internal_update_asset_balance(&asset, net_redeem_amount, true); // Rollback: Restore the withdrawal request to pending state let mut withdraw = Withdraw::new( asset.clone(), shares, min_asset_amount, owner_id.clone(), receiver_id.clone(), memo, activation_timestamp_nanosec, ); withdraw.confirmed = true; withdraw.confirmed_share_price = Some(confirmed_share_price); let restored = self .state .teller_state .restore_withdraw(operation_id, withdraw); require!( restored, format!("Failed to restore withdrawal {} - operation_id already exists. State corruption detected.", operation_id) ); log!( "Restored withdrawal request {} to pending state after failed transfer", operation_id ); log!( "Rolled back async redeem: restored {} {} assets to vault balance", net_redeem_amount.0, asset ); } } } #[private] pub fn deposit_rejection_transfer_callback( &mut self, #[callback_result] call_result: Result<(), PromiseError>, operation_id: u64, owner_id: AccountId, asset: Asset, rejected_amount: U128, min_shares: U128, receiver_id: AccountId, memo: Option, reason: String, ) { match call_result { Ok(_) => { log!( "Deposit rejection transfer successful: operation {} - returned {} {} assets to {} (reason: {})", operation_id, rejected_amount.0, asset, owner_id, reason ); } Err(err) => { log!( "Deposit rejection transfer failed for operation {}: {:?}. Restoring deposit request", operation_id, err ); // Rollback: Restore vault balance (pending_deposit) since assets couldn't be returned let mut balance = self.get_asset_balance(&asset); balance.pending_deposit.0 = balance .pending_deposit .0 .checked_add(rejected_amount.0) .expect("Pending deposit balance overflow during rollback"); self.state.asset_balances.insert(asset.clone(), balance); log!( "Restored {} {} to pending_deposit balance after failed rejection transfer", rejected_amount.0, asset ); // Rollback: Restore the deposit request to pending state since transfer failed let deposit = Deposit::new( asset, rejected_amount, min_shares, owner_id.clone(), receiver_id, memo, ); let restored = self .state .teller_state .restore_deposit(operation_id, deposit); require!( restored, format!("Failed to restore deposit {} - operation_id already exists. State corruption detected.", operation_id) ); log!( "Restored deposit request {} to pending state after failed rejection transfer", operation_id ); } } } #[private] pub fn claim_assets_callback( &mut self, #[callback_result] call_result: Result<(), PromiseError>, claimer: AccountId, asset: Asset, amount: U128, ) { match call_result { Ok(_) => { log!( "Assets claim successful: {} claimed {} {} assets", claimer, amount.0, asset ); } Err(err) => { log!( "Assets claim failed for {}: {:?}. Restoring claimable balance", claimer, err ); // Rollback: Restore claimable balance since transfer failed self.internal_add_claimable_asset_amount(&claimer, &asset, amount); // Rollback: Restore available assets in vault self.internal_update_asset_balance(&asset, amount, true); log!( "Restored {} {} assets to claimable balance for {} after failed transfer", amount.0, asset, claimer ); } } } }