use crate::types::{DepositMessage, MultiTokenReceiver}; use crate::*; use common_data::asset::Asset; use near_contract_standards::fungible_token::core::FungibleTokenCore; use near_contract_standards::fungible_token::receiver::FungibleTokenReceiver; use near_contract_standards::fungible_token::FungibleTokenResolver; use near_contract_standards::storage_management::{StorageBalance, StorageManagement}; use near_sdk::{ env, json_types::U128, near, near_bindgen, require, serde_json, AccountId, NearToken, PromiseOrValue, }; #[near] impl FungibleTokenCore for Contract { #[payable] fn ft_transfer(&mut self, receiver_id: AccountId, amount: U128, memo: Option) { let sender_id = env::predecessor_account_id(); // Validate access control permissions for transfers self.internal_validate_transfer_permissions(&sender_id, &receiver_id); // Perform the transfer self.token .ft_transfer(receiver_id.clone(), amount, memo.clone()); } #[payable] fn ft_transfer_call( &mut self, receiver_id: AccountId, amount: U128, memo: Option, msg: String, ) -> PromiseOrValue { let sender_id = env::predecessor_account_id(); // Validate access control permissions for transfers self.internal_validate_transfer_permissions(&sender_id, &receiver_id); // Perform the transfer call let result = self .token .ft_transfer_call(receiver_id.clone(), amount, memo.clone(), msg); result } fn ft_total_supply(&self) -> U128 { self.token.ft_total_supply() } fn ft_balance_of(&self, account_id: AccountId) -> U128 { self.token.ft_balance_of(account_id) } } #[near] impl FungibleTokenResolver for Contract { fn ft_resolve_transfer( &mut self, sender_id: AccountId, receiver_id: AccountId, amount: U128, ) -> U128 { self.token .internal_ft_resolve_transfer(&sender_id, receiver_id, amount) .0 .into() } } // Implement metadata methods for NEP-141 #[near] impl Contract { /// Get fungible token metadata pub fn ft_metadata(&self) -> FungibleTokenMetadata { self.metadata.clone() } } // Implement storage management for NEP-141 #[near] impl StorageManagement for Contract { #[payable] fn storage_deposit( &mut self, account_id: Option, registration_only: Option, ) -> StorageBalance { self.token.storage_deposit(account_id, registration_only) } #[payable] fn storage_withdraw(&mut self, amount: Option) -> StorageBalance { self.token.storage_withdraw(amount) } #[payable] fn storage_unregister(&mut self, force: Option) -> bool { self.token.storage_unregister(force) } fn storage_balance_bounds( &self, ) -> near_contract_standards::storage_management::StorageBalanceBounds { self.token.storage_balance_bounds() } fn storage_balance_of(&self, account_id: AccountId) -> Option { self.token.storage_balance_of(account_id) } } #[near_bindgen] impl FungibleTokenReceiver for Contract { fn ft_on_transfer( &mut self, sender_id: AccountId, amount: U128, msg: String, ) -> PromiseOrValue { // Create asset from the calling contract (FT contract) let asset = Asset::FungibleToken { contract_id: env::predecessor_account_id(), }; // Parse the message to get deposit parameters let deposit_msg: DepositMessage = serde_json::from_str(&msg).unwrap_or_else(|_| { // Return all tokens if message parsing fails env::panic_str("Failed to parse deposit message"); }); // Handle process_redeems if specified if let Some(operation_ids) = deposit_msg.process_redeems { self.internal_validate_asset(&asset); require!(amount.0 > 0, "Amount must be positive"); // Process redeems with transferred funds // Leftover automatically stays in available_amount self.internal_process_redeems_with_funds(&asset, amount, operation_ids); return PromiseOrValue::Value(U128(0)); } // skip emergency pause checks for strategist transfers if deposit_msg.is_strategist_transfer.unwrap_or(false) { self.internal_validate_asset(&asset); require!(amount.0 > 0, "Amount must be positive"); self.internal_update_asset_balance(&asset, amount, true); log!( "Dew Kernel increased available {} assets by {}", asset, amount.0 ); return PromiseOrValue::Value(U128(0)); } self.internal_validate_not_emergency_paused(); self.internal_validate_share_price_freshness(); self.internal_validate_asset_for_deposit(&asset); // Validate access control permissions for deposits let receiver_id = deposit_msg.receiver_id.as_ref().unwrap_or(&sender_id); self.internal_validate_deposit_permissions(&sender_id, None, receiver_id); // Perform the deposit - panics on error, no partial consumption self.internal_process_deposit(sender_id, asset, amount, deposit_msg); // All tokens consumed successfully PromiseOrValue::Value(U128(0)) } } // Transfer callback implementations for receiving deposits #[near_bindgen] impl MultiTokenReceiver for Contract { /// NEP-245 multi-token transfer callback /// Called when external MT contracts transfer tokens to this vault via mt_transfer_call fn mt_on_transfer( &mut self, sender_id: AccountId, previous_owner_ids: Vec, token_ids: Vec, amounts: Vec, msg: String, ) -> PromiseOrValue> { require!( token_ids.len() == 1 && amounts.len() == 1 && previous_owner_ids.len() == 1, "Only single token transfers are allowed" ); let predecessor = env::predecessor_account_id(); let token_id = token_ids[0].clone(); let amount = amounts[0]; // Create asset from the calling contract and token ID let asset = Asset::MultiToken { contract_id: predecessor, token_id, }; // Parse the message to get deposit parameters let deposit_msg: DepositMessage = serde_json::from_str(&msg).unwrap_or_else(|_| { // Return all tokens if message parsing fails env::panic_str("Failed to parse deposit message"); }); // Handle process_redeems if specified if let Some(operation_ids) = deposit_msg.process_redeems { self.internal_validate_asset(&asset); require!(amount.0 > 0, "Amount must be positive"); // Process redeems with transferred funds // Leftover automatically stays in available_amount self.internal_process_redeems_with_funds(&asset, amount, operation_ids); return PromiseOrValue::Value(vec![U128(0)]); } // skip emergency pause checks for strategist transfers if deposit_msg.is_strategist_transfer.unwrap_or(false) { self.internal_validate_asset(&asset); require!(amount.0 > 0, "Amount must be positive"); self.internal_update_asset_balance(&asset, amount, true); log!( "Dew Kernel increased available {} assets by {}", asset, amount.0 ); return PromiseOrValue::Value(vec![U128(0)]); } self.internal_validate_not_emergency_paused(); self.internal_validate_share_price_freshness(); self.internal_validate_asset_for_deposit(&asset); // Validate access control permissions for deposits // Check sender, previous_owner (original token owner), and receiver let receiver_id = deposit_msg.receiver_id.as_ref().unwrap_or(&sender_id); let previous_owner = previous_owner_ids.first(); self.internal_validate_deposit_permissions(&sender_id, previous_owner, receiver_id); // Perform the deposit - panics on error, no partial consumption self.internal_process_deposit(sender_id, asset, amount, deposit_msg); // All tokens consumed successfully PromiseOrValue::Value(vec![U128(0)]) } }