diff --git a/ref-farming/Cargo.toml b/ref-farming/Cargo.toml index fdfb428..c40a88c 100644 --- a/ref-farming/Cargo.toml +++ b/ref-farming/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ref_farming" -version = "1.0.1" +version = "1.1.0" authors = ["Marco Sun "] edition = "2018" diff --git a/ref-farming/release_notes.md b/ref-farming/release_notes.md new file mode 100644 index 0000000..114e55e --- /dev/null +++ b/ref-farming/release_notes.md @@ -0,0 +1,11 @@ +# Release Notes + +### Version 1.1.0 +1. add claim_and_withdraw interfaces; + +### Version 1.0.2 +1. 80T gas for seed withdraw resolve, 10T gas for reward withdraw resolve; +2. add get_user_storage_state interface; + +### Version 1.0.1 +1. Increase estimate gas for resolve transfer to 20T; \ No newline at end of file diff --git a/ref-farming/src/actions_of_reward.rs b/ref-farming/src/actions_of_reward.rs index 2887c3d..9a3fe65 100644 --- a/ref-farming/src/actions_of_reward.rs +++ b/ref-farming/src/actions_of_reward.rs @@ -1,7 +1,6 @@ - -use std::convert::TryInto; use near_sdk::json_types::{ValidAccountId, U128}; -use near_sdk::{assert_one_yocto, env, near_bindgen, AccountId, Balance, PromiseResult}; +use near_sdk::{assert_one_yocto, env, near_bindgen, + AccountId, Balance, Promise, PromiseResult}; use crate::utils::{ext_fungible_token, ext_self, GAS_FOR_FT_TRANSFER, GAS_FOR_RESOLVE_TRANSFER, parse_farm_id}; use crate::errors::*; @@ -44,9 +43,30 @@ impl Contract { self.assert_storage_usage(&sender_id); } + /// if withdraw_all_tokens is true, withdraw all tokens, + /// or just withdraw the reward token of this farm. + pub fn claim_and_withdraw_by_farm(&mut self, farm_id: FarmId, withdraw_all_tokens: bool) { + let sender_id = env::predecessor_account_id(); + self.claim_reward_by_farm(farm_id.clone()); + if withdraw_all_tokens { + self.internal_withdraw_all_rewards(&sender_id, None); + } else { + if let Some(farm) = self.data().farms.get(&farm_id) { + let token_id = farm.get_reward_token(); + self.internal_withdraw_all_rewards(&sender_id, Some(&token_id)); + } + } + } + + pub fn claim_and_withdraw_by_seed(&mut self, seed_id: SeedId) { + let sender_id = env::predecessor_account_id(); + self.claim_reward_by_seed(seed_id.clone()); + self.internal_withdraw_all_rewards(&sender_id, None); + } + /// Withdraws given reward token of given user. #[payable] - pub fn withdraw_reward(&mut self, token_id: ValidAccountId, amount: Option) { + pub fn withdraw_reward(&mut self, token_id: ValidAccountId, amount: Option) -> Promise { assert_one_yocto(); let token_id: AccountId = token_id.into(); @@ -59,22 +79,27 @@ impl Contract { // Note: subtraction, will be reverted if the promise fails. let amount = farmer.get_ref_mut().sub_reward(&token_id, amount); self.data_mut().farmers.insert(&sender_id, &farmer); - ext_fungible_token::ft_transfer( - sender_id.clone().try_into().unwrap(), - amount.into(), - None, - &token_id, - 1, - GAS_FOR_FT_TRANSFER, - ) - .then(ext_self::callback_post_withdraw_reward( - token_id, - sender_id, - amount.into(), - &env::current_account_id(), - 0, - GAS_FOR_RESOLVE_TRANSFER, - )); + + self.internal_send_tokens(&sender_id, &token_id, amount) + } + + /// Withdraws given reward tokens of given user. + #[payable] + pub fn withdraw_rewards(&mut self, token_ids: Vec) { + assert_one_yocto(); + + let tokens = token_ids.iter().map(|x| x.clone().into()).collect::>(); + + let sender_id = env::predecessor_account_id(); + let mut farmer = self.get_farmer(&sender_id); + let withdraw_amounts: Vec<_> = tokens.iter().map(|token_id| farmer.get_ref_mut().sub_reward(&token_id, 0)).collect(); + self.data_mut().farmers.insert(&sender_id, &farmer); + + for (token_id, amount) in tokens.into_iter().zip(withdraw_amounts) { + if amount > 0 { + self.internal_send_tokens(&sender_id, &token_id, amount); + } + } } #[private] @@ -83,7 +108,7 @@ impl Contract { token_id: AccountId, sender_id: AccountId, amount: U128, - ) { + ) -> U128 { assert_eq!( env::promise_results_count(), 1, @@ -100,6 +125,7 @@ impl Contract { ) .as_bytes(), ); + amount.into() } PromiseResult::Failed => { env::log( @@ -113,8 +139,9 @@ impl Contract { let mut farmer = self.get_farmer(&sender_id); farmer.get_ref_mut().add_reward(&token_id, amount.0); self.data_mut().farmers.insert(&sender_id, &farmer); + 0.into() } - }; + } } } @@ -153,6 +180,55 @@ fn claim_user_reward_from_farm( } impl Contract { + + /// if token was given, then just withdraw that token, or withdraw all tokens + pub(crate) fn internal_withdraw_all_rewards(&mut self, farmer_id: &AccountId, token: Option<&AccountId>) { + + let view_farmer = self.get_farmer(&farmer_id); + let tokens = { + if let Some(token) = token { + vec![token.clone()] + } else { + view_farmer.get_ref().rewards.keys().map(|x| x.clone()).collect::>() + } + }; + + let mut farmer = view_farmer; + let withdraw_amounts: Vec<_> = tokens.iter().map(|token_id| farmer.get_ref_mut().sub_reward(&token_id, 0)).collect(); + self.data_mut().farmers.insert(farmer_id, &farmer); + + for (token_id, amount) in tokens.into_iter().zip(withdraw_amounts) { + if amount > 0 { + self.internal_send_tokens(farmer_id, &token_id, amount); + } + } + } + + /// Sends given amount to given user and if it fails, returns it back to user's balance. + /// Tokens must already be subtracted from internal balance. + pub(crate) fn internal_send_tokens( + &self, + sender_id: &AccountId, + token_id: &AccountId, + amount: Balance, + ) -> Promise { + ext_fungible_token::ft_transfer( + sender_id.clone(), + U128(amount), + None, + token_id, + 1, + GAS_FOR_FT_TRANSFER, + ) + .then(ext_self::callback_post_withdraw_reward( + token_id.clone(), + sender_id.clone(), + U128(amount), + &env::current_account_id(), + 0, + GAS_FOR_RESOLVE_TRANSFER, + )) + } pub(crate) fn internal_claim_user_reward_by_seed_id( &mut self, diff --git a/ref-farming/src/actions_of_seed.rs b/ref-farming/src/actions_of_seed.rs index 5e88ee0..1f104da 100644 --- a/ref-farming/src/actions_of_seed.rs +++ b/ref-farming/src/actions_of_seed.rs @@ -5,7 +5,7 @@ use near_sdk::{AccountId, Balance, PromiseResult}; use crate::utils::{ assert_one_yocto, ext_multi_fungible_token, ext_fungible_token, - ext_self, wrap_mft_token_id, parse_seed_id, GAS_FOR_FT_TRANSFER, GAS_FOR_RESOLVE_TRANSFER + ext_self, wrap_mft_token_id, parse_seed_id, GAS_FOR_FT_TRANSFER, GAS_FOR_RESOLVE_WITHDRAW_SEED }; use crate::errors::*; use crate::farm_seed::SeedType; @@ -41,7 +41,7 @@ impl Contract { amount.into(), &env::current_account_id(), 0, - GAS_FOR_RESOLVE_TRANSFER, + GAS_FOR_RESOLVE_WITHDRAW_SEED, )); } SeedType::MFT => { @@ -61,7 +61,7 @@ impl Contract { amount.into(), &env::current_account_id(), 0, - GAS_FOR_RESOLVE_TRANSFER, + GAS_FOR_RESOLVE_WITHDRAW_SEED, )); } } @@ -74,7 +74,7 @@ impl Contract { seed_id: SeedId, sender_id: AccountId, amount: U128, - ) { + ) -> U128 { assert_eq!( env::promise_results_count(), 1, @@ -102,6 +102,7 @@ impl Contract { farmer.get_ref_mut().add_seed(&seed_id, amount); self.data_mut().seeds.insert(&seed_id, &farm_seed); self.data_mut().farmers.insert(&sender_id, &farmer); + 0.into() }, PromiseResult::Successful(_) => { env::log( @@ -111,8 +112,9 @@ impl Contract { ) .as_bytes(), ); + amount.into() } - }; + } } #[private] @@ -121,7 +123,7 @@ impl Contract { seed_id: SeedId, sender_id: AccountId, amount: U128, - ) { + ) -> U128 { assert_eq!( env::promise_results_count(), 1, @@ -149,6 +151,7 @@ impl Contract { farmer.get_ref_mut().add_seed(&seed_id, amount); self.data_mut().seeds.insert(&seed_id, &farm_seed); self.data_mut().farmers.insert(&sender_id, &farmer); + 0.into() }, PromiseResult::Successful(_) => { env::log( @@ -158,8 +161,9 @@ impl Contract { ) .as_bytes(), ); + amount.into() } - }; + } } } diff --git a/ref-farming/src/farmer.rs b/ref-farming/src/farmer.rs index 8b34a39..1924994 100644 --- a/ref-farming/src/farmer.rs +++ b/ref-farming/src/farmer.rs @@ -52,8 +52,8 @@ impl Farmer { pub(crate) fn sub_reward(&mut self, token: &AccountId, amount: Balance) -> Balance { let value = *self.rewards.get(token).expect(ERR21_TOKEN_NOT_REG); assert!(value >= amount, "{}", ERR22_NOT_ENOUGH_TOKENS); - if amount == 0 { - self.rewards.remove(&token.clone()); + if amount == 0 || amount == value { + self.rewards.remove(token); value } else { self.rewards.insert(token.clone(), value - amount); diff --git a/ref-farming/src/utils.rs b/ref-farming/src/utils.rs index 252fdf6..2a20adc 100644 --- a/ref-farming/src/utils.rs +++ b/ref-farming/src/utils.rs @@ -11,8 +11,10 @@ pub const MIN_SEED_DEPOSIT: u128 = 1_000_000_000_000_000_000; pub const MAX_ACCOUNT_LENGTH: u128 = 64; /// Amount of gas for fungible token transfers. pub const GAS_FOR_FT_TRANSFER: Gas = 10_000_000_000_000; -/// hotfix_insuffient_gas_for_mft_resolve_transfer, increase from 5T to 20T -pub const GAS_FOR_RESOLVE_TRANSFER: Gas = 20_000_000_000_000; +/// Amount of gas for reward token transfers resolve. +pub const GAS_FOR_RESOLVE_TRANSFER: Gas = 10_000_000_000_000; +/// Amount of gas for seed token transfers resolve. +pub const GAS_FOR_RESOLVE_WITHDRAW_SEED: Gas = 80_000_000_000_000; pub const MFT_TAG: &str = "@"; diff --git a/ref-farming/src/view.rs b/ref-farming/src/view.rs index 2f8ae63..6421578 100644 --- a/ref-farming/src/view.rs +++ b/ref-farming/src/view.rs @@ -29,6 +29,14 @@ pub struct Metadata { pub reward_count: U64, } +#[derive(Serialize, Deserialize, PartialEq)] +#[serde(crate = "near_sdk::serde")] +#[cfg_attr(not(target_arch = "wasm32"), derive(Debug))] +pub struct StorageState { + pub deposit: U128, + pub usage: U128, +} + #[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] #[serde(crate = "near_sdk::serde")] pub struct FarmInfo { @@ -287,4 +295,17 @@ impl Contract { String::from("0") } } + + /// Get farmer's storage deposit and needed in the account of current version + pub fn get_user_storage_state(&self, account_id: ValidAccountId) -> Option { + let (locked, deposited) = self.internal_farmer_storage(account_id.as_ref()); + if locked > 0 { + Some(StorageState { + deposit: U128(deposited), + usage: U128(locked), + }) + } else { + None + } + } } diff --git a/ref-farming/tests/common/views.rs b/ref-farming/tests/common/views.rs index c8812bc..20d666b 100644 --- a/ref-farming/tests/common/views.rs +++ b/ref-farming/tests/common/views.rs @@ -4,6 +4,7 @@ use near_sdk_sim::{view, ContractAccount}; use super::utils::to_va; use ref_farming::{ContractContract as Farming, FarmInfo}; +use test_token::ContractContract as TestToken; use std::collections::HashMap; #[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] @@ -184,6 +185,12 @@ pub(crate) fn show_storage_balance(farming: &ContractAccount, farmer: S ret } +pub(crate) fn balance_of(token: &ContractAccount, account_id: String) -> u128 { + view!(token.ft_balance_of(to_va(account_id.clone()))) + .unwrap_json::() + .0 +} + // ============= Assertions =============== #[allow(dead_code)] pub(crate) fn assert_farming( diff --git a/ref-farming/tests/test_claim_and_withdraw.rs b/ref-farming/tests/test_claim_and_withdraw.rs new file mode 100644 index 0000000..f89e401 --- /dev/null +++ b/ref-farming/tests/test_claim_and_withdraw.rs @@ -0,0 +1,347 @@ +use near_sdk_sim::{call, init_simulator, to_yocto, view}; +use near_sdk::json_types::{U128}; +use near_sdk::serde_json::Value; + +use ref_farming::{HRSimpleFarmTerms}; + +use crate::common::utils::*; +use crate::common::init::deploy_farming; +use crate::common::views::*; +use crate::common::actions::*; + +mod common; + +#[test] +fn claim_and_withdraw_0() { + let root = init_simulator(None); + + let owner = root.create_user("owner".to_string(), to_yocto("100")); + let farmer1 = root.create_user("farmer1".to_string(), to_yocto("100")); + let farmer2 = root.create_user("farmer2".to_string(), to_yocto("100")); + + let (pool, token1, token2) = prepair_pool_and_liquidity( + &root, &owner, farming_id(), vec![&farmer1, &farmer2]); + + let farming = deploy_farming(&root, farming_id(), owner.account_id()); + call!(farmer1, farming.storage_deposit(None, None), deposit = to_yocto("1")).assert_success(); + call!(farmer2, farming.storage_deposit(None, None), deposit = to_yocto("1")).assert_success(); + + let farm1_id = "swap@0#0".to_string(); + let farm2_id = "swap@0#1".to_string(); + let out_come = call!( + owner, + farming.create_simple_farm(HRSimpleFarmTerms{ + seed_id: format!("{}@0", pool.account_id()), + reward_token: token1.valid_account_id(), + start_at: 0, + reward_per_session: to_yocto("1").into(), + session_interval: 60, + }, None), + deposit = to_yocto("1") + ); + out_come.assert_success(); + + let out_come = call!( + owner, + farming.create_simple_farm(HRSimpleFarmTerms{ + seed_id: format!("{}@0", pool.account_id()), + reward_token: token2.valid_account_id(), + start_at: 0, + reward_per_session: to_yocto("1").into(), + session_interval: 60, + }, None), + deposit = to_yocto("1") + ); + out_come.assert_success(); + + call!( + root, + token1.storage_deposit(Some(to_va(farming_id())), None), + deposit = to_yocto("1") + ) + .assert_success(); + mint_token(&token1, &root, to_yocto("10")); + call!( + root, + token1.ft_transfer_call(to_va(farming_id()), U128(to_yocto("10")), None, farm1_id.clone()), + deposit = 1 + ) + .assert_success(); + + call!( + root, + token2.storage_deposit(Some(to_va(farming_id())), None), + deposit = to_yocto("1") + ) + .assert_success(); + mint_token(&token2, &root, to_yocto("10")); + call!( + root, + token2.ft_transfer_call(to_va(farming_id()), U128(to_yocto("10")), None, farm2_id.clone()), + deposit = 1 + ) + .assert_success(); + + let out_come = call!( + farmer1, + pool.mft_transfer_call(":0".to_string(), to_va(farming_id()), to_yocto("1").into(), None, "".to_string()), + deposit = 1 + ); + out_come.assert_success(); + assert_eq!(out_come.promise_errors().len(), 0); + let out_come = call!( + farmer2, + pool.mft_transfer_call(":0".to_string(), to_va(farming_id()), to_yocto("1").into(), None, "".to_string()), + deposit = 1 + ); + out_come.assert_success(); + assert_eq!(out_come.promise_errors().len(), 0); + + assert!(root.borrow_runtime_mut().produce_blocks(60).is_ok()); + let farm_info = show_farminfo(&farming, farm1_id.clone(), false); + assert_farming(&farm_info, "Running".to_string(), to_yocto("10"), 1, 0, 0, to_yocto("1"), 0); + let farm_info = show_farminfo(&farming, farm2_id.clone(), false); + assert_farming(&farm_info, "Running".to_string(), to_yocto("10"), 1, 0, 0, to_yocto("1"), 0); + let unclaim = show_unclaim(&farming, farmer1.account_id(), farm1_id.clone(), false); + assert_eq!(unclaim.0, to_yocto("0.5")); + let unclaim = show_unclaim(&farming, farmer1.account_id(), farm2_id.clone(), false); + assert_eq!(unclaim.0, to_yocto("0.5")); + let unclaim = show_unclaim(&farming, farmer2.account_id(), farm1_id.clone(), false); + assert_eq!(unclaim.0, to_yocto("0.5")); + let unclaim = show_unclaim(&farming, farmer2.account_id(), farm2_id.clone(), false); + assert_eq!(unclaim.0, to_yocto("0.5")); + + println!("Case0301 claim_and_withdraw_by_farm"); + let out_come = call!( + farmer1, + farming.claim_and_withdraw_by_farm(farm1_id.clone(), false), + deposit = 0 + ); + out_come.assert_success(); + assert_eq!(out_come.promise_errors().len(), 0); + let farm_info = show_farminfo(&farming, farm1_id.clone(), false); + assert_farming(&farm_info, "Running".to_string(), to_yocto("10"), 1, 1, to_yocto("0.5"), to_yocto("0.5"), 0); + let unclaim = show_unclaim(&farming, farmer1.account_id(), farm1_id.clone(), false); + assert_eq!(unclaim.0, 0_u128); + let unclaim = show_unclaim(&farming, farmer1.account_id(), farm2_id.clone(), false); + assert_eq!(unclaim.0, to_yocto("0.5")); + let reward = show_reward(&farming, farmer1.account_id(), token1.account_id(), false); + assert_eq!(reward.0, to_yocto("0")); + let balance = balance_of(&token1, farmer1.account_id()); + assert_eq!(balance, to_yocto("5.5")); + + println!("Case0302 claim_and_withdraw_by_seed."); + assert!(root.borrow_runtime_mut().produce_blocks(60).is_ok()); + let out_come = call!( + farmer2, + farming.claim_and_withdraw_by_seed(farm_info.seed_id.clone()), + deposit = 0 + ); + out_come.assert_success(); + assert_eq!(out_come.promise_errors().len(), 0); + let farm_info = show_farminfo(&farming, farm1_id.clone(), false); + assert_farming(&farm_info, "Running".to_string(), to_yocto("10"), 2, 2, to_yocto("1.5"), to_yocto("0.5"), 0); + let farm_info = show_farminfo(&farming, farm2_id.clone(), false); + assert_farming(&farm_info, "Running".to_string(), to_yocto("10"), 2, 2, to_yocto("1"), to_yocto("1"), 0); + let reward = show_reward(&farming, farmer2.account_id(), token1.account_id(), false); + assert_eq!(reward.0, to_yocto("0")); + let reward = show_reward(&farming, farmer2.account_id(), token2.account_id(), false); + assert_eq!(reward.0, to_yocto("0")); + let balance = balance_of(&token1, farmer2.account_id()); + assert_eq!(balance, to_yocto("6")); + let balance = balance_of(&token2, farmer2.account_id()); + assert_eq!(balance, to_yocto("6")); + + assert!(root.borrow_runtime_mut().produce_blocks(60).is_ok()); + let out_come = call!( + farmer1, + farming.withdraw_seed(farm_info.seed_id.clone(), to_yocto("0.5").into()), + deposit = 1 + ); + out_come.assert_success(); + assert_eq!(out_come.promise_errors().len(), 0); + let out_come = call!( + farmer2, + farming.withdraw_seed(farm_info.seed_id.clone(), to_yocto("0.5").into()), + deposit = 1 + ); + out_come.assert_success(); + assert_eq!(out_come.promise_errors().len(), 0); + let reward = show_reward(&farming, farmer1.account_id(), token1.account_id(), false); + assert_eq!(reward.0, to_yocto("1")); + let reward = show_reward(&farming, farmer1.account_id(), token2.account_id(), false); + assert_eq!(reward.0, to_yocto("1.5")); + + println!("Case0303 claim_and_withdraw_by_seed after seed change."); + let out_come = call!( + farmer1, + farming.claim_and_withdraw_by_seed(farm_info.seed_id.clone()), + deposit = 0 + ); + out_come.assert_success(); + assert_eq!(out_come.promise_errors().len(), 0); + let reward = show_reward(&farming, farmer1.account_id(), token1.account_id(), false); + assert_eq!(reward.0, to_yocto("0")); + let reward = show_reward(&farming, farmer1.account_id(), token2.account_id(), false); + assert_eq!(reward.0, to_yocto("0")); + let balance = balance_of(&token1, farmer1.account_id()); + assert_eq!(balance, to_yocto("6.5")); + let balance = balance_of(&token2, farmer1.account_id()); + assert_eq!(balance, to_yocto("6.5")); + + println!("Case0304 claim_and_withdraw_by_farm after seed change."); + assert!(root.borrow_runtime_mut().produce_blocks(60).is_ok()); + let out_come = call!( + farmer2, + farming.claim_and_withdraw_by_farm(farm1_id.clone(), true), + deposit = 0 + ); + out_come.assert_success(); + assert_eq!(out_come.promise_errors().len(), 0); + let unclaim = show_unclaim(&farming, farmer2.account_id(), farm2_id.clone(), false); + assert_eq!(unclaim.0, to_yocto("0.5")); + let reward = show_reward(&farming, farmer2.account_id(), token1.account_id(), false); + assert_eq!(reward.0, to_yocto("0")); + let reward = show_reward(&farming, farmer2.account_id(), token2.account_id(), false); + assert_eq!(reward.0, to_yocto("0")); + let balance = balance_of(&token1, farmer2.account_id()); + assert_eq!(balance, to_yocto("7")); + let balance = balance_of(&token2, farmer2.account_id()); + assert_eq!(balance, to_yocto("6.5")); + + // send token failure + println!("Case0305 claim_and_withdraw_by_seed with tokens unregstered."); + assert!(root.borrow_runtime_mut().produce_blocks(80).is_ok()); + let farm_info = show_farminfo(&farming, farm1_id.clone(), false); + assert_farming(&farm_info, "Running".to_string(), to_yocto("10"), 6, 4, to_yocto("3.5"), to_yocto("2.5"), 0); + let farm_info = show_farminfo(&farming, farm2_id.clone(), false); + assert_farming(&farm_info, "Running".to_string(), to_yocto("10"), 6, 3, to_yocto("3"), to_yocto("3"), 0); + let reward = show_reward(&farming, farmer1.account_id(), token1.account_id(), false); + assert_eq!(reward.0, to_yocto("0")); + let reward = show_reward(&farming, farmer1.account_id(), token2.account_id(), false); + assert_eq!(reward.0, to_yocto("0")); + let unclaim = show_unclaim(&farming, farmer1.account_id(), farm1_id.clone(), false); + assert_eq!(unclaim.0, to_yocto("1.5")); + let unclaim = show_unclaim(&farming, farmer1.account_id(), farm2_id.clone(), false); + assert_eq!(unclaim.0, to_yocto("1.5")); + call!(farmer1, token1.storage_unregister(Some(true)), deposit = 1).assert_success(); + call!(farmer1, token2.storage_unregister(Some(true)), deposit = 1).assert_success(); + let out_come = call!( + farmer1, + farming.claim_and_withdraw_by_seed(farm_info.seed_id.clone()), + deposit = 0 + ); + out_come.assert_success(); + assert_eq!(out_come.promise_errors().len(), 2); + let reward = show_reward(&farming, farmer1.account_id(), token1.account_id(), false); + assert_eq!(reward.0, to_yocto("1.5")); + let reward = show_reward(&farming, farmer1.account_id(), token2.account_id(), false); + assert_eq!(reward.0, to_yocto("1.5")); + + // token1 registered + call!(farmer1, token1.storage_deposit(None, None), deposit = to_yocto("1")).assert_success(); + let out_come = call!( + farmer1, + farming.claim_and_withdraw_by_seed(farm_info.seed_id.clone()), + deposit = 0 + ); + out_come.assert_success(); + assert_eq!(out_come.promise_errors().len(), 1); + let reward = show_reward(&farming, farmer1.account_id(), token1.account_id(), false); + assert_eq!(reward.0, to_yocto("0")); + let reward = show_reward(&farming, farmer1.account_id(), token2.account_id(), false); + assert_eq!(reward.0, to_yocto("1.5")); + let balance = balance_of(&token1, farmer1.account_id()); + assert_eq!(balance, to_yocto("1.5")); + + // token2 registered + call!(farmer1, token2.storage_deposit(None, None), deposit = to_yocto("1")).assert_success(); + let out_come = call!( + farmer1, + farming.claim_and_withdraw_by_seed(farm_info.seed_id.clone()), + deposit = 0 + ); + out_come.assert_success(); + assert_eq!(out_come.promise_errors().len(), 0); + let reward = show_reward(&farming, farmer1.account_id(), token1.account_id(), false); + assert_eq!(reward.0, to_yocto("0")); + let reward = show_reward(&farming, farmer1.account_id(), token2.account_id(), false); + assert_eq!(reward.0, to_yocto("0")); + let balance = balance_of(&token1, farmer1.account_id()); + assert_eq!(balance, to_yocto("1.5")); + let balance = balance_of(&token2, farmer1.account_id()); + assert_eq!(balance, to_yocto("1.5")); + + // normal withdraw + println!("Case0306 batch withdraw and normal withdraw."); + assert!(root.borrow_runtime_mut().produce_blocks(60).is_ok()); + let farm_info = show_farminfo(&farming, farm1_id.clone(), false); + assert_farming(&farm_info, "Running".to_string(), to_yocto("10"), 7, 6, to_yocto("5"), to_yocto("2"), 0); + let farm_info = show_farminfo(&farming, farm2_id.clone(), false); + assert_farming(&farm_info, "Running".to_string(), to_yocto("10"), 7, 6, to_yocto("4.5"), to_yocto("2.5"), 0); + let out_come = call!( + farmer1, + pool.mft_transfer_call(":0".to_string(), to_va(farming_id()), to_yocto("0.5").into(), None, "".to_string()), + deposit = 1 + ); + out_come.assert_success(); + let out_come = call!( + farmer2, + pool.mft_transfer_call(":0".to_string(), to_va(farming_id()), to_yocto("0.5").into(), None, "".to_string()), + deposit = 1 + ); + out_come.assert_success(); + let reward = show_reward(&farming, farmer1.account_id(), token1.account_id(), false); + assert_eq!(reward.0, to_yocto("0.5")); + let reward = show_reward(&farming, farmer1.account_id(), token2.account_id(), false); + assert_eq!(reward.0, to_yocto("0.5")); + let reward = show_reward(&farming, farmer2.account_id(), token1.account_id(), false); + assert_eq!(reward.0, to_yocto("1.5")); + let reward = show_reward(&farming, farmer2.account_id(), token2.account_id(), false); + assert_eq!(reward.0, to_yocto("2")); + + let balance = balance_of(&token1, farmer1.account_id()); + assert_eq!(balance, to_yocto("1.5")); + let balance = balance_of(&token2, farmer1.account_id()); + assert_eq!(balance, to_yocto("1.5")); + let out_come = call!( + farmer1, + farming.withdraw_rewards(vec![token1.valid_account_id(), token2.valid_account_id()]), + deposit = 1 + ); + out_come.assert_success(); + assert_eq!(out_come.promise_errors().len(), 0); + let reward = show_reward(&farming, farmer1.account_id(), token1.account_id(), false); + assert_eq!(reward.0, to_yocto("0")); + let reward = show_reward(&farming, farmer1.account_id(), token2.account_id(), false); + assert_eq!(reward.0, to_yocto("0")); + let balance = balance_of(&token1, farmer1.account_id()); + assert_eq!(balance, to_yocto("2")); + let balance = balance_of(&token2, farmer1.account_id()); + assert_eq!(balance, to_yocto("2")); + + + let out_come = call!( + farmer2, + farming.withdraw_reward(token1.valid_account_id(), None), + deposit = 1 + ); + out_come.assert_success(); + assert_eq!(out_come.promise_errors().len(), 0); + let reward = show_reward(&farming, farmer2.account_id(), token1.account_id(), false); + assert_eq!(reward.0, to_yocto("0")); + let balance = balance_of(&token1, farmer2.account_id()); + assert_eq!(balance, to_yocto("8.5")); + let out_come = call!( + farmer2, + farming.withdraw_reward(token2.valid_account_id(), Some(U128(to_yocto("1.5")))), + deposit = 1 + ); + out_come.assert_success(); + assert_eq!(out_come.promise_errors().len(), 0); + let reward = show_reward(&farming, farmer2.account_id(), token2.account_id(), false); + assert_eq!(reward.0, to_yocto("0.5")); + let balance = balance_of(&token2, farmer2.account_id()); + assert_eq!(balance, to_yocto("8")); + +} \ No newline at end of file diff --git a/res/ref_farming_release.wasm b/res/ref_farming_release.wasm index 00710e7..c955512 100755 Binary files a/res/ref_farming_release.wasm and b/res/ref_farming_release.wasm differ