diff --git a/contracts/liquidity_book/lb_factory/src/bin/secretcli/main.rs b/contracts/liquidity_book/lb_factory/src/bin/secretcli/main.rs index 4c6070f6..ce6e7951 100644 --- a/contracts/liquidity_book/lb_factory/src/bin/secretcli/main.rs +++ b/contracts/liquidity_book/lb_factory/src/bin/secretcli/main.rs @@ -71,6 +71,7 @@ fn main() -> io::Result<()> { admin_auth: RawContract::example(), owner: Some(Addr::owner()), fee_recipient: Addr::recipient(), + query_auth: RawContract::example(), recover_staking_funds_receiver: Addr::funds_recipient(), }; diff --git a/contracts/liquidity_book/lb_factory/src/contract.rs b/contracts/liquidity_book/lb_factory/src/contract.rs index df4cb12d..d690f014 100644 --- a/contracts/liquidity_book/lb_factory/src/contract.rs +++ b/contracts/liquidity_book/lb_factory/src/contract.rs @@ -71,6 +71,7 @@ pub fn instantiate( admin_auth: msg.admin_auth.into_valid(deps.api)?, staking_contract_implementation: ContractInstantiationInfo::default(), recover_staking_funds_receiver: msg.recover_staking_funds_receiver, + query_auth: msg.query_auth.into_valid(deps.api)?, }; STATE.save(deps.storage, &config)?; @@ -424,6 +425,7 @@ fn try_create_lb_pair( entropy, protocol_fee_recipient: config.fee_recipient, admin_auth: config.admin_auth.into(), + query_auth: config.query_auth.into(), total_reward_bins: Some(staking_preset.total_reward_bins), rewards_distribution_algorithm: staking_preset.rewards_distribution_algorithm, staking_contract_implementation: config.staking_contract_implementation, diff --git a/contracts/liquidity_book/lb_factory/src/state.rs b/contracts/liquidity_book/lb_factory/src/state.rs index 94734bab..9440feb3 100644 --- a/contracts/liquidity_book/lb_factory/src/state.rs +++ b/contracts/liquidity_book/lb_factory/src/state.rs @@ -63,6 +63,7 @@ pub struct State { pub lb_token_implementation: ContractInstantiationInfo, pub staking_contract_implementation: ContractInstantiationInfo, pub admin_auth: Contract, + pub query_auth: Contract, pub recover_staking_funds_receiver: Addr, } diff --git a/contracts/liquidity_book/lb_pair/src/bin/secretcli/main.rs b/contracts/liquidity_book/lb_pair/src/bin/secretcli/main.rs index 7e561552..b099f079 100644 --- a/contracts/liquidity_book/lb_pair/src/bin/secretcli/main.rs +++ b/contracts/liquidity_book/lb_pair/src/bin/secretcli/main.rs @@ -11,7 +11,10 @@ use shade_protocol::{ types::{ContractInstantiationInfo, StaticFeeParameters}, }, liquidity_book::lb_pair::{ - ContractStatus, InvokeMsg, LiquidityParameters, RemoveLiquidity, + ContractStatus, + InvokeMsg, + LiquidityParameters, + RemoveLiquidity, RewardsDistributionAlgorithm, }, swap::core::{TokenAmount, TokenType}, @@ -117,6 +120,7 @@ fn main() -> io::Result<()> { viewing_key: String::from("viewing_key"), entropy: String::from("entropy"), protocol_fee_recipient: Addr::funds_recipient(), + query_auth: RawContract::example(), }; writeln!(file, "## Instantiate Message\n")?; diff --git a/contracts/liquidity_book/lb_pair/src/contract.rs b/contracts/liquidity_book/lb_pair/src/contract.rs index 6dcb305d..a5e05326 100644 --- a/contracts/liquidity_book/lb_pair/src/contract.rs +++ b/contracts/liquidity_book/lb_pair/src/contract.rs @@ -4,9 +4,29 @@ use serde::Serialize; use shade_protocol::{ admin::helpers::{validate_admin, AdminPermissions}, c_std::{ - from_binary, shd_entry_point, to_binary, Addr, Attribute, Binary, ContractInfo, CosmosMsg, - Decimal, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdError, StdResult, SubMsg, - SubMsgResult, Timestamp, Uint128, Uint256, WasmMsg, + from_binary, + shd_entry_point, + to_binary, + Addr, + Attribute, + Binary, + ContractInfo, + CosmosMsg, + Decimal, + Deps, + DepsMut, + Env, + MessageInfo, + Reply, + Response, + StdError, + StdResult, + SubMsg, + SubMsgResult, + Timestamp, + Uint128, + Uint256, + WasmMsg, }, contract_interfaces::{ liquidity_book::{lb_pair::*, lb_staking, lb_token}, @@ -40,7 +60,8 @@ use shade_protocol::{ types::{Bytes32, MintArrays}, viewing_keys::{register_receive, set_viewing_key_msg, ViewingKey}, }, - snip20, Contract, + snip20, + Contract, }; use std::{collections::HashMap, ops::Sub, vec}; @@ -165,10 +186,10 @@ pub fn instantiate( protocol_fees_recipient: msg.protocol_fee_recipient, admin_auth: msg.admin_auth.into_valid(deps.api)?, last_swap_timestamp: env.block.time, - rewards_epoch_id: 0, + rewards_epoch_index: 1, base_rewards_bins: msg.total_reward_bins, toggle_distributions_algorithm: false, - //TODO: set using the setter function and instantiate msg + max_bins_per_swap: 100, // TODO: do this by message }; let tree: TreeUint24 = TreeUint24::new(); @@ -180,30 +201,24 @@ pub fn instantiate( ORACLE.save(deps.storage, &oracle)?; CONTRACT_STATUS.save(deps.storage, &ContractStatus::Active)?; BIN_TREE.save(deps.storage, &tree)?; - FEE_MAP_TREE.save(deps.storage, 0, &tree)?; - REWARDS_STATS_STORE.save( - deps.storage, - 0, - &RewardStats { - cumm_value: Uint256::zero(), - cumm_value_mul_bin_id: Uint256::zero(), - rewards_distribution_algorithm: msg.rewards_distribution_algorithm, - }, - )?; + FEE_MAP_TREE.save(deps.storage, state.rewards_epoch_index, &tree)?; + REWARDS_STATS_STORE.save(deps.storage, state.rewards_epoch_index, &RewardStats { + cumm_value: Uint256::zero(), + cumm_value_mul_bin_id: Uint256::zero(), + rewards_distribution_algorithm: msg.rewards_distribution_algorithm, + })?; - EPHEMERAL_STORAGE.save( - deps.storage, - &NextTokenKey { - lb_token_code_hash: msg.lb_token_implementation.code_hash, - staking_contract: msg.staking_contract_implementation, - token_x_symbol, - token_y_symbol, - epoch_index: msg.epoch_staking_index, - epoch_duration: msg.epoch_staking_duration, - expiry_duration: msg.expiry_staking_duration, - recover_funds_receiver: msg.recover_staking_funds_receiver, - }, - )?; + EPHEMERAL_STORAGE.save(deps.storage, &EphemeralStruct { + lb_token_code_hash: msg.lb_token_implementation.code_hash, + staking_contract: msg.staking_contract_implementation, + token_x_symbol, + token_y_symbol, + epoch_index: state.rewards_epoch_index, + epoch_duration: msg.epoch_staking_duration, + expiry_duration: msg.expiry_staking_duration, + recover_funds_receiver: msg.recover_staking_funds_receiver, + query_auth: msg.query_auth, + })?; response = response.add_messages(messages); @@ -409,10 +424,11 @@ fn try_swap( let mut params = state.pair_parameters; let bin_step = state.bin_step; - let mut reward_stats = REWARDS_STATS_STORE.load(deps.storage, state.rewards_epoch_id)?; + let mut reward_stats = REWARDS_STATS_STORE.load(deps.storage, state.rewards_epoch_index)?; let mut active_id = params.get_active_id(); + // updating the volatility params.update_references(&env.block.time)?; if reward_stats.rewards_distribution_algorithm == RewardsDistributionAlgorithm::TimeBasedRewards { @@ -423,7 +439,8 @@ fn try_swap( reward_stats.cumm_value_mul_bin_id += time_difference * (Uint256::from(active_id)); } - loop { + // Allowing max 100 bins crossed per swap + for _ in 0..state.max_bins_per_swap { let bin_reserves = BIN_MAP .load(deps.storage, active_id) .map_err(|_| Error::ZeroBinReserve { active_id })?; @@ -471,7 +488,7 @@ fn try_swap( reward_stats.cumm_value += swap_value_uint256; FEE_MAP_TREE.update( deps.storage, - state.rewards_epoch_id, + state.rewards_epoch_index, |fee_tree| -> Result<_> { Ok(match fee_tree { Some(mut t) => { @@ -529,7 +546,7 @@ fn try_swap( } } - REWARDS_STATS_STORE.save(deps.storage, state.rewards_epoch_id, &reward_stats)?; + REWARDS_STATS_STORE.save(deps.storage, state.rewards_epoch_index, &reward_stats)?; if amounts_out == [0u8; 32] { return Err(Error::InsufficientAmountOut); @@ -655,8 +672,6 @@ pub fn add_liquidity_internal( let state = STATE.load(deps.storage)?; - // TODO - we are initializing the vector of empty values, and populating them in a - // loop later. I think this could be refactored. let mut liquidity_configs = vec![ LiquidityConfigurations { distribution_x: 0, @@ -1032,7 +1047,6 @@ fn _update_bin( price, total_supply, )?; - println!("id {:?}, shares: {:?}", id, shares); let amounts_in_to_bin = amounts_in; @@ -1519,10 +1533,9 @@ fn try_calculate_rewards_distribution( &state.admin_auth, )?; - // loop through the fee_logs uptil a maximum iterations // save the results in temporary storage - let reward_stats = REWARDS_STATS_STORE.load(deps.storage, state.rewards_epoch_id)?; + let reward_stats = REWARDS_STATS_STORE.load(deps.storage, state.rewards_epoch_index)?; let distribution = if !reward_stats.cumm_value.is_zero() { match reward_stats.rewards_distribution_algorithm { RewardsDistributionAlgorithm::TimeBasedRewards => { @@ -1540,8 +1553,20 @@ fn try_calculate_rewards_distribution( calculate_default_distribution(rewards_bins, state.pair_parameters.get_active_id())? }; - REWARDS_DISTRIBUTION.save(deps.storage, state.rewards_epoch_id, &distribution)?; - state.rewards_epoch_id += 1; + REWARDS_DISTRIBUTION.save(deps.storage, state.rewards_epoch_index, &distribution)?; + + //distribution algorithm + let res = lb_staking::ExecuteMsg::EndEpoch { + rewards_distribution: distribution, + epoch_index: state.rewards_epoch_index, + } + .to_cosmos_msg( + state.staking_contract.code_hash.to_owned(), + state.staking_contract.address.to_string(), + None, + )?; + + state.rewards_epoch_index += 1; let toggle = state.toggle_distributions_algorithm; state.last_swap_timestamp = env.block.time; state.toggle_distributions_algorithm = false; @@ -1559,30 +1584,16 @@ fn try_calculate_rewards_distribution( }; } - REWARDS_STATS_STORE.save( - deps.storage, - state.rewards_epoch_id, - &RewardStats { - cumm_value: Uint256::zero(), - cumm_value_mul_bin_id: Uint256::zero(), - rewards_distribution_algorithm: distribution_algorithm.clone(), - }, - )?; + REWARDS_STATS_STORE.save(deps.storage, state.rewards_epoch_index, &RewardStats { + cumm_value: Uint256::zero(), + cumm_value_mul_bin_id: Uint256::zero(), + rewards_distribution_algorithm: distribution_algorithm.clone(), + })?; if distribution_algorithm == &RewardsDistributionAlgorithm::VolumeBasedRewards { let tree: TreeUint24 = TreeUint24::new(); - FEE_MAP_TREE.save(deps.storage, state.rewards_epoch_id, &tree)?; - } - - //distribution algorithm - let res = lb_staking::ExecuteMsg::EndEpoch { - rewards_distribution: distribution, + FEE_MAP_TREE.save(deps.storage, state.rewards_epoch_index, &tree)?; } - .to_cosmos_msg( - state.staking_contract.code_hash, - state.staking_contract.address.to_string(), - None, - )?; Ok(Response::default().add_message(res)) } @@ -1641,12 +1652,13 @@ fn calculate_volume_based_rewards_distribution( let mut ids: Vec = Vec::new(); let mut weightages: Vec = Vec::new(); - let fee_tree: TreeUint24 = FEE_MAP_TREE.load(deps.storage, state.rewards_epoch_id)?; + let fee_tree: TreeUint24 = FEE_MAP_TREE.load(deps.storage, state.rewards_epoch_index)?; let mut id: u32 = 0; let basis_point_max: Uint256 = Uint256::from(BASIS_POINT_MAX); let mut total_weight = 0; - loop { + // TODO: Decide a reasonable value with shade's consultation + for _ in 0..U24::MAX { id = fee_tree.find_first_left(id); if id == U24::MAX || id == 0 { break; @@ -1694,7 +1706,7 @@ fn try_reset_rewards_config( info.sender.to_string(), &state.admin_auth, )?; - let reward_stats = REWARDS_STATS_STORE.load(deps.storage, state.rewards_epoch_id)?; + let reward_stats = REWARDS_STATS_STORE.load(deps.storage, state.rewards_epoch_index)?; //Eventhough the distribution was changes mid epoch the effects of change will occur after the epoch. match rewards_distribution_algorithm { @@ -2104,9 +2116,10 @@ fn query_all_bins_reserves( page_size }; + let state = STATE.load(deps.storage)?; let mut counter: u32 = 0; - loop { + for _ in 0..state.max_bins_per_swap { let next_id = tree.find_first_left(id); id = next_id; @@ -2589,7 +2602,7 @@ fn query_swap_in(deps: Deps, env: Env, amount_out: u128, swap_for_y: bool) -> Re params.update_references(&env.block.time)?; - loop { + for _ in 0..state.max_bins_per_swap { let bin_reserves = BIN_MAP .load(deps.storage, id) .unwrap_or_default() @@ -2677,7 +2690,7 @@ fn query_swap_out(deps: Deps, env: Env, amount_in: u128, swap_for_y: bool) -> Re params.update_references(&env.block.time)?; - loop { + for _ in 0..state.max_bins_per_swap { let bin_reserves = BIN_MAP.load(deps.storage, id).unwrap_or_default(); if !BinHelper::is_empty(bin_reserves, !swap_for_y) { let price = PriceHelper::get_price_from_id(id, bin_step)?; @@ -2749,7 +2762,7 @@ fn query_total_supply(deps: Deps, id: u32) -> Result { fn query_rewards_distribution(deps: Deps, epoch_id: Option) -> Result { let (epoch_id) = match epoch_id { Some(id) => id, - None => STATE.load(deps.storage)?.rewards_epoch_id - 1, + None => STATE.load(deps.storage)?.rewards_epoch_index - 1, }; to_binary(&RewardsDistributionResponse { @@ -2785,7 +2798,7 @@ pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> StdResult { amm_pair: env.contract.address.to_string(), lb_token: state.lb_token.to_owned().into(), admin_auth: state.admin_auth.into(), - query_auth: None, + query_auth: emp_storage.query_auth.into(), epoch_index: emp_storage.epoch_index, epoch_duration: emp_storage.epoch_duration, expiry_duration: emp_storage.expiry_duration, diff --git a/contracts/liquidity_book/lb_pair/src/state.rs b/contracts/liquidity_book/lb_pair/src/state.rs index b6db50a4..bd0d36e3 100644 --- a/contracts/liquidity_book/lb_pair/src/state.rs +++ b/contracts/liquidity_book/lb_pair/src/state.rs @@ -11,6 +11,7 @@ use shade_protocol::{ liquidity_book::lb_pair::{ContractStatus, RewardsDistribution, RewardsDistributionAlgorithm}, secret_storage_plus::{AppendStore, Bincode2, Item, Json, Map}, swap::core::TokenType, + utils::asset::RawContract, Contract, }; @@ -19,7 +20,7 @@ pub const CONTRACT_STATUS: Item = Item::new("contract_status"); pub const BIN_MAP: Map = Map::new("bins_map"); // pub const BIN_TREE: Item = Item::new("bin_tree"); //? pub const ORACLE: Item = Item::new("oracle"); //? -pub const EPHEMERAL_STORAGE: Item = Item::new("ephemeral_storage"); +pub const EPHEMERAL_STORAGE: Item = Item::new("ephemeral_storage"); pub const FEE_APPEND_STORE: AppendStore = AppendStore::new("fee_logs"); //? pub const REWARDS_STATS_STORE: Map = Map::new("rewards_stats"); // @@ -64,14 +65,16 @@ pub struct State { pub protocol_fees_recipient: Addr, pub admin_auth: Contract, pub last_swap_timestamp: Timestamp, - pub rewards_epoch_id: u64, + pub rewards_epoch_index: u64, pub base_rewards_bins: Option, pub toggle_distributions_algorithm: bool, + pub max_bins_per_swap: u32, } #[cw_serde] -pub struct NextTokenKey { +pub struct EphemeralStruct { pub lb_token_code_hash: String, + pub query_auth: RawContract, pub staking_contract: ContractInstantiationInfo, pub token_x_symbol: String, pub token_y_symbol: String, diff --git a/contracts/liquidity_book/lb_staking/src/bin/secretcli/example_data.rs b/contracts/liquidity_book/lb_staking/src/bin/secretcli/example_data.rs index cf960b3d..86d20a4f 100644 --- a/contracts/liquidity_book/lb_staking/src/bin/secretcli/example_data.rs +++ b/contracts/liquidity_book/lb_staking/src/bin/secretcli/example_data.rs @@ -9,6 +9,7 @@ use shade_protocol::{ }, liquidity_book::{ lb_pair::{LiquidityParameters, RemoveLiquidity, RewardsDistribution, TokenPair}, + lb_staking::Auth, lb_token::Snip1155ReceiveMsg, }, s_toolkit::permit::{Permit, PermitParams, TokenPermissions}, @@ -82,6 +83,15 @@ impl ExampleData for ContractInstantiationInfo { } } +impl ExampleData for Auth { + fn example() -> Self { + Auth::ViewingKey { + key: "viewing_key".to_string(), + address: Addr::staker().to_string(), + } + } +} + impl ExampleData for TokenType { fn example() -> Self { TokenType::CustomToken { diff --git a/contracts/liquidity_book/lb_staking/src/bin/secretcli/main.rs b/contracts/liquidity_book/lb_staking/src/bin/secretcli/main.rs index b255b150..f9d8e535 100644 --- a/contracts/liquidity_book/lb_staking/src/bin/secretcli/main.rs +++ b/contracts/liquidity_book/lb_staking/src/bin/secretcli/main.rs @@ -7,7 +7,13 @@ use shade_protocol::{ liquidity_book::{ lb_pair::RewardsDistribution, lb_staking::{ - ExecuteMsg, InstantiateMsg, InvokeMsg, QueryAnswer, QueryMsg, QueryTxnType, + Auth, + ExecuteMsg, + InstantiateMsg, + InvokeMsg, + QueryAnswer, + QueryMsg, + QueryTxnType, QueryWithPermit, }, lb_token::Snip1155ReceiveMsg, @@ -82,7 +88,7 @@ fn main() -> io::Result<()> { amm_pair: Addr::contract().to_string(), lb_token: RawContract::example(), admin_auth: RawContract::example(), - query_auth: None, + query_auth: RawContract::example(), epoch_index: 1, epoch_duration: 3600, expiry_duration: Some(50), @@ -109,6 +115,7 @@ fn main() -> io::Result<()> { let claim_rewards = ExecuteMsg::ClaimRewards {}; let end_epoch = ExecuteMsg::EndEpoch { rewards_distribution: RewardsDistribution::example(), + epoch_index: 0, }; let unstake = ExecuteMsg::Unstake { token_ids: vec![1, 2, 3], @@ -128,15 +135,6 @@ fn main() -> io::Result<()> { expiry_duration: Some(200), }; let recover_funds = ExecuteMsg::RecoverExpiredFunds {}; - let create_viewing_key = ExecuteMsg::CreateViewingKey { - entropy: "random_entropy".to_string(), - }; - let set_viewing_key = ExecuteMsg::SetViewingKey { - key: "viewing_key".to_string(), - }; - let revoke_permit = ExecuteMsg::RevokePermit { - permit_name: "permit_name".to_string(), - }; // Responses for ExecuteMsg // Assuming that the execute messages do not return a response directly @@ -166,9 +164,6 @@ fn main() -> io::Result<()> { register_reward_tokens, update_config, recover_funds, - create_viewing_key, - set_viewing_key, - revoke_permit ); // Note: Add a similar macro for InvokeMsg if needed @@ -190,44 +185,35 @@ fn main() -> io::Result<()> { id: "token_id".to_string(), }; let balance_query = QueryMsg::Balance { - owner: Addr::unchecked("owner_addr"), - key: "key".to_string(), token_id: "token_id".to_string(), + auth: Auth::example(), }; let all_balances_query = QueryMsg::AllBalances { - owner: Addr::unchecked("owner_addr"), - key: "key".to_string(), + auth: Auth::example(), + page: Some(1), page_size: Some(10), }; let liquidity_query = QueryMsg::Liquidity { - owner: Addr::unchecked("owner_addr"), - key: "key".to_string(), + auth: Auth::example(), + round_index: Some(1234567890u64), token_ids: vec![1, 2, 3], }; let transaction_history_query = QueryMsg::TransactionHistory { - owner: Addr::unchecked("owner_addr"), - key: "key".to_string(), + auth: Auth::example(), page: Some(1), page_size: Some(10), txn_type: QueryTxnType::All, }; - let with_permit_query = QueryMsg::WithPermit { - permit: Permit::example(), - query: QueryWithPermit::Balance { - owner: Addr::unchecked("owner_addr"), - token_id: "token_id".to_string(), - }, - }; // Responses for QueryMsg let contract_info_response = QueryAnswer::ContractInfo { lb_token: ContractInfo::example(), lb_pair: Addr::contract(), - admin_auth: ContractInfo::example(), - query_auth: None, + admin_auth: Contract::example(), + query_auth: Contract::example(), epoch_index: 1, epoch_durations: 3600, expiry_durations: Some(5000), /* ... fill in details ... */ @@ -260,7 +246,6 @@ fn main() -> io::Result<()> { (all_balances_query, all_balances_response), (liquidity_query, liquidity_response), (transaction_history_query, transaction_history_response), - (with_permit_query, balance_response) // Add here the with_permit_query if needed along with its corresponding response ); Ok(()) diff --git a/contracts/liquidity_book/lb_staking/src/contract.rs b/contracts/liquidity_book/lb_staking/src/contract.rs index 84df8f74..4f466317 100644 --- a/contracts/liquidity_book/lb_staking/src/contract.rs +++ b/contracts/liquidity_book/lb_staking/src/contract.rs @@ -1,12 +1,33 @@ use shade_protocol::{ c_std::{ - shd_entry_point, Attribute, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult, + shd_entry_point, + Addr, + Attribute, + Binary, + Deps, + DepsMut, + Env, + MessageInfo, + Response, + StdError, + StdResult, }, contract_interfaces::liquidity_book::lb_libraries::viewing_keys::{ - register_receive, set_viewing_key_msg, + register_receive, + set_viewing_key_msg, }, - liquidity_book::lb_staking::{EpochInfo, ExecuteMsg, InstantiateMsg, QueryMsg, State}, + liquidity_book::lb_staking::{ + Auth, + AuthPermit, + EpochInfo, + ExecuteMsg, + InstantiateMsg, + QueryMsg, + State, + }, + query_auth::helpers::{authenticate_permit, authenticate_vk, PermitAuthentication}, utils::pad_handle_result, + Contract, BLOCK_SIZE, }; @@ -30,11 +51,8 @@ pub fn instantiate( let state = State { lb_token: msg.lb_token.valid(deps.api)?, lb_pair: deps.api.addr_validate(&msg.amm_pair)?, - admin_auth: msg.admin_auth.valid(deps.api)?, - query_auth: msg - .query_auth - .map(|auth| auth.valid(deps.api)) - .transpose()?, + admin_auth: msg.admin_auth.into_valid(deps.api)?, + query_auth: msg.query_auth.into_valid(deps.api)?, epoch_index: msg.epoch_index, epoch_durations: msg.epoch_duration, expiry_durations: msg.expiry_duration, @@ -43,18 +61,14 @@ pub fn instantiate( }; let now = env.block.time.seconds(); - EPOCH_STORE.save( - deps.storage, - state.epoch_index, - &EpochInfo { - rewards_distribution: None, - start_time: now, - end_time: now + state.epoch_durations, - duration: state.epoch_durations, - reward_tokens: None, - expired_at: None, - }, - )?; + EPOCH_STORE.save(deps.storage, state.epoch_index, &EpochInfo { + rewards_distribution: None, + start_time: now, + end_time: now + state.epoch_durations, + duration: state.epoch_durations, + reward_tokens: None, + expired_at: None, + })?; let messages = vec![ register_receive( @@ -116,6 +130,7 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S } => try_update_config( deps, info, + env, admin_auth, query_auth, epoch_duration, @@ -124,15 +139,9 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S ExecuteMsg::RecoverExpiredFunds { .. } => try_recover_expired_funds(deps, env, info), ExecuteMsg::EndEpoch { rewards_distribution, - } => try_end_epoch(deps, env, info, rewards_distribution), + epoch_index, + } => try_end_epoch(deps, env, info, epoch_index, rewards_distribution), - ExecuteMsg::CreateViewingKey { entropy } => { - try_create_viewing_key(deps, env, info, entropy) - } - ExecuteMsg::SetViewingKey { key } => try_set_viewing_key(deps, env, info, key), - ExecuteMsg::RevokePermit { permit_name } => { - try_revoke_permit(deps, env, info, permit_name) - } ExecuteMsg::RecoverFunds { token, amount, @@ -144,17 +153,51 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S ) } +pub fn authenticate(deps: Deps, auth: Auth, query_auth: Contract) -> StdResult { + match auth { + Auth::ViewingKey { key, address } => { + let address = deps.api.addr_validate(&address)?; + if !authenticate_vk(address.clone(), key, &deps.querier, &query_auth)? { + return Err(StdError::generic_err("Invalid Viewing Key")); + } + Ok(address) + } + Auth::Permit(permit) => { + let res: PermitAuthentication = + authenticate_permit(permit, &deps.querier, query_auth)?; + if res.revoked { + return Err(StdError::generic_err("Permit Revoked")); + } + Ok(res.sender) + } + } +} + #[shd_entry_point] pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { match msg { QueryMsg::ContractInfo {} => query_contract_info(deps), + QueryMsg::EpochInfo { index } => query_epoch_info(deps, index), + QueryMsg::RegisteredTokens {} => query_registered_tokens(deps), QueryMsg::IdTotalBalance { id } => query_token_id_balance(deps, id), - QueryMsg::Balance { .. } - | QueryMsg::AllBalances { .. } - | QueryMsg::Liquidity { .. } - | QueryMsg::TransactionHistory { .. } => viewing_keys_queries(deps, msg), - - QueryMsg::WithPermit { permit, query } => permit_queries(deps, env, permit, query), + QueryMsg::Balance { auth, token_id } => query_balance(deps, auth, token_id), + QueryMsg::StakerInfo { auth } => query_staker_info(deps, auth), + QueryMsg::AllBalances { + auth, + page, + page_size, + } => query_all_balances(deps, auth, page, page_size), + QueryMsg::Liquidity { + auth, + round_index, + token_ids, + } => query_liquidity(deps, auth, token_ids, round_index), + QueryMsg::TransactionHistory { + auth, + page, + page_size, + txn_type, + } => query_transaction_history(deps, auth, page, page_size, txn_type), } } diff --git a/contracts/liquidity_book/lb_staking/src/execute.rs b/contracts/liquidity_book/lb_staking/src/execute.rs index bdad0f08..e5c24bd1 100644 --- a/contracts/liquidity_book/lb_staking/src/execute.rs +++ b/contracts/liquidity_book/lb_staking/src/execute.rs @@ -1,4 +1,5 @@ use std::{ + borrow::{Borrow, BorrowMut}, collections::HashMap, ops::{Add, AddAssign, Sub}, str::FromStr, @@ -9,15 +10,37 @@ use shade_protocol::{ self, admin::helpers::{validate_admin, AdminPermissions}, c_std::{ - from_binary, to_binary, Addr, BankMsg, Binary, Coin, ContractInfo, CosmosMsg, DepsMut, Env, - MessageInfo, Response, StdError, StdResult, Storage, Uint128, Uint256, + from_binary, + to_binary, + Addr, + BankMsg, + Binary, + Coin, + ContractInfo, + CosmosMsg, + DepsMut, + Env, + MessageInfo, + Response, + StdError, + StdResult, + Storage, + Uint128, + Uint256, }, lb_libraries::types::TreeUint24, liquidity_book::{ lb_pair::RewardsDistribution, lb_staking::{ - EpochInfo, ExecuteAnswer, InvokeMsg, Reward, RewardToken, RewardTokenInfo, - StakerLiquidity, StakerLiquiditySnapshot, State, + EpochInfo, + ExecuteAnswer, + InvokeMsg, + Reward, + RewardToken, + RewardTokenInfo, + StakerLiquidity, + StakerLiquiditySnapshot, + State, }, lb_token::TransferAction, }, @@ -31,18 +54,36 @@ use shade_protocol::{ }, swap::core::TokenType, utils::{asset::RawContract, pad_handle_result, ExecuteCallback}, - Contract, BLOCK_SIZE, + Contract, + BLOCK_SIZE, }; use crate::{ helper::{ - assert_lb_pair, check_if_claimable, finding_total_liquidity, finding_user_liquidity, - register_reward_tokens, require_lb_token, staker_init_checker, TokenKey, + assert_lb_pair, + check_if_claimable, + finding_total_liquidity, + finding_user_liquidity, + register_reward_tokens, + require_lb_token, + staker_init_checker, + TokenKey, }, state::{ - store_claim_rewards, store_stake, store_unstake, EPOCH_STORE, EXPIRED_AT_LOGGER, - EXPIRED_AT_LOGGER_MAP, REWARD_TOKENS, REWARD_TOKEN_INFO, STAKERS, STAKERS_BIN_TREE, - STAKERS_LIQUIDITY, STAKERS_LIQUIDITY_SNAPSHOT, STATE, TOTAL_LIQUIDITY, + store_claim_rewards, + store_stake, + store_unstake, + EPOCH_STORE, + EXPIRED_AT_LOGGER, + EXPIRED_AT_LOGGER_MAP, + REWARD_TOKENS, + REWARD_TOKEN_INFO, + STAKERS, + STAKERS_BIN_TREE, + STAKERS_LIQUIDITY, + STAKERS_LIQUIDITY_SNAPSHOT, + STATE, + TOTAL_LIQUIDITY, TOTAL_LIQUIDITY_SNAPSHOT, }, }; @@ -76,14 +117,7 @@ pub fn receiver_callback_snip_1155( } else { from }; - try_stake( - deps, - env, - state, - checked_from, - token_id.parse().unwrap(), - amount, - ) + try_stake(deps, env, state, checked_from, token_id, amount) } InvokeMsg::AddRewards { .. } => Err(StdError::generic_err("Wrong Receiver called")), }, @@ -113,8 +147,8 @@ pub fn receiver_callback( pad_handle_result( match from_binary(&msg)? { - InvokeMsg::Stake { .. } => Err(StdError::generic_err("Wrong Receiver")), InvokeMsg::AddRewards { start, end } => try_add_rewards(deps, info, start, end, amount), + InvokeMsg::Stake { .. } => Err(StdError::generic_err("Wrong Receiver called")), }, BLOCK_SIZE, ) @@ -125,14 +159,14 @@ pub fn try_stake( env: Env, mut state: State, staker: Addr, - token_id: u32, + token_id_string: String, amount: Uint256, ) -> StdResult { - //LOADING general use readonly stores - let epoch_obj = match EPOCH_STORE.may_load(deps.storage, state.epoch_index)? { - Some(e) => Ok(e), - None => Err(StdError::generic_err("Reward token storage already exists")), - }?; + let token_id: u32 = token_id_string.parse().unwrap(); + + // //LOADING general use readonly stores + let epoch_obj = process_epoch(deps.storage, &env, &mut state)?; + //1) UPDATING: staker_info and staker_liquidity_snapshot //*INIT STAKER_INFO if not initialized already staker_init_checker(deps.storage, &state, &staker)?; @@ -153,18 +187,15 @@ pub fn try_stake( } //**Only adding to liquidity if round has not ended yet - if env.block.time.seconds() >= epoch_obj.end_time { - staker_liq_snap.liquidity = liquidity; - } else { - staker_liq_snap.liquidity = liquidity.add(amount.multiply_ratio( - if let Some(time) = epoch_obj.end_time.checked_sub(env.block.time.seconds()) { - time - } else { - return Err(StdError::generic_err("Under-flow sub error")); - }, - epoch_obj.duration, - )); - } + + staker_liq_snap.liquidity = liquidity.add(amount.multiply_ratio( + if let Some(time) = epoch_obj.end_time.checked_sub(env.block.time.seconds()) { + time + } else { + return Err(StdError::generic_err("Under-flow sub error try_stake 1")); + }, + epoch_obj.duration, + )); if staker_liq.amount_delegated.is_zero() { STAKERS_BIN_TREE.update(deps.storage, &staker, |tree| -> StdResult<_> { @@ -208,18 +239,15 @@ pub fn try_stake( } //**Only adding to liquidity if round has not ended yet - if env.block.time.seconds() >= epoch_obj.end_time { - total_liq_snap.liquidity = total_liquidity; - } else { - total_liq_snap.liquidity = total_liquidity.add(amount.multiply_ratio( - if let Some(time) = epoch_obj.end_time.checked_sub(env.block.time.seconds()) { - time - } else { - return Err(StdError::generic_err("Under-flow sub error")); - }, - epoch_obj.duration, - )); - } + + total_liq_snap.liquidity = total_liquidity.add(amount.multiply_ratio( + if let Some(time) = epoch_obj.end_time.checked_sub(env.block.time.seconds()) { + time + } else { + return Err(StdError::generic_err("Under-flow sub error try_stake 2")); + }, + epoch_obj.duration, + )); total_liq.amount_delegated.add_assign(amount); total_liq.last_deposited = Some(state.epoch_index); @@ -240,7 +268,9 @@ pub fn try_stake( )?; STATE.save(deps.storage, &state)?; - Ok(Response::default()) + Ok(Response::default() + .add_attribute("amount".to_string(), amount) + .add_attribute("token_id".to_string(), token_id.to_string())) } pub fn try_unstake( @@ -257,12 +287,18 @@ pub fn try_unstake( } let mut state = STATE.load(deps.storage)?; - let epoch_obj = EPOCH_STORE - .may_load(deps.storage, state.epoch_index)? - .ok_or_else(|| StdError::generic_err("Reward token storage does not exist"))?; + + let epoch_obj = process_epoch(deps.storage, &env, &mut state)?; staker_init_checker(deps.storage, &state, &info.sender)?; + // Serialize the vectors into JSON strings + let token_ids_json = serde_json::to_string(&token_ids) + .map_err(|e| StdError::generic_err(format!("Failed to serialize token_ids: {}", e)))?; + + let amounts_json = serde_json::to_string(&amounts) + .map_err(|e| StdError::generic_err(format!("Failed to serialize amounts: {}", e)))?; + let mut actions: Vec = Vec::new(); for (token_id, amount) in token_ids.iter().zip(amounts.iter()) { @@ -284,27 +320,38 @@ pub fn try_unstake( ))); } + // println!("amount_delegated : {:?}", staker_liq.amount_delegated); + // println!("amount : {:?}", amount); + // println!("end_time : {:?}", epoch_obj.end_time); + // println!("now : {:?}", env.block.time.seconds()); + // println!( + // "difference : {:?}", + // epoch_obj.end_time - env.block.time.seconds() + // ); + // println!("duration : {:?}", epoch_obj.duration); + let liquidity = if staker_liq_snap.liquidity.is_zero() { staker_liq.amount_delegated } else { staker_liq_snap.liquidity }; - if env.block.time.seconds() < epoch_obj.end_time { - let subtraction_amount = amount.multiply_ratio( - epoch_obj - .end_time - .checked_sub(env.block.time.seconds()) - .ok_or_else(|| StdError::generic_err("Overflow in time calculation"))?, - epoch_obj.duration, - ); - - staker_liq_snap.liquidity = liquidity - .checked_sub(subtraction_amount) - .map_err(|_| StdError::generic_err("Underflow in subtracting from liquidity"))?; - } else { - staker_liq_snap.liquidity = liquidity; - } + let subtraction_amount = amount.multiply_ratio( + epoch_obj + .end_time + .checked_sub(env.block.time.seconds()) + .ok_or_else(|| StdError::generic_err("Overflow in time calculation"))?, + epoch_obj.duration, + ); + + // println!( + // "liquidity {:?}, subtraction_amount {:?}", + // liquidity, subtraction_amount + // ); + + staker_liq_snap.liquidity = liquidity + .checked_sub(subtraction_amount) + .map_err(|_| StdError::generic_err("Underflow in subtracting from liquidity"))?; update_staker_and_total_liquidity( deps.storage, @@ -348,7 +395,10 @@ pub fn try_unstake( )?; STATE.save(deps.storage, &state)?; - Ok(Response::default().add_message(message)) + Ok(Response::default() + .add_message(message) + .add_attribute("amounts".to_string(), amounts_json) + .add_attribute("token_ids".to_string(), token_ids_json)) } fn update_staker_and_total_liquidity( @@ -398,30 +448,27 @@ fn update_staker_and_total_liquidity( } //**Only adding to liquidity if round has not ended yet - if env.block.time.seconds() >= epoch_obj.end_time { - total_liq_snap.liquidity = total_liquidity; - } else { - let subtraction_amount = amount.multiply_ratio( - if let Some(time) = epoch_obj.end_time.checked_sub(env.block.time.seconds()) { - time - } else { - // Handle overflow in time calculation - return Err(StdError::generic_err("Overflow in time calculation")); - }, - epoch_obj.duration, - ); - // Use checked_sub to prevent underflow when subtracting from total_liquidity - total_liq_snap.liquidity = - if let Ok(new_liquidity) = total_liquidity.checked_sub(subtraction_amount) { - new_liquidity - } else { - // Handle underflow in subtracting from total_liquidity - return Err(StdError::generic_err( - "Underflow in subtracting from total_liquidity", - )); - }; - } + let subtraction_amount = amount.multiply_ratio( + if let Some(time) = epoch_obj.end_time.checked_sub(env.block.time.seconds()) { + time + } else { + // Handle overflow in time calculation + return Err(StdError::generic_err("Overflow in time calculation")); + }, + epoch_obj.duration, + ); + + // Use checked_sub to prevent underflow when subtracting from total_liquidity + total_liq_snap.liquidity = + if let Ok(new_liquidity) = total_liquidity.checked_sub(subtraction_amount) { + new_liquidity + } else { + // Handle underflow in subtracting from total_liquidity + return Err(StdError::generic_err( + "Underflow in subtracting from total_liquidity", + )); + }; // Use checked_sub to safely subtract amounts[i] from total_liq.amount_delegated if let Ok(new_amount_delegated) = total_liq.amount_delegated.checked_sub(amount) { @@ -446,6 +493,7 @@ pub fn try_end_epoch( deps: DepsMut, env: Env, info: MessageInfo, + epoch_index: u64, rewards_distribution: RewardsDistribution, ) -> StdResult { let mut state = STATE.load(deps.storage)?; @@ -479,13 +527,13 @@ pub fn try_end_epoch( } //saves the distribution - let mut epoch_obj = EPOCH_STORE.load(deps.storage, state.epoch_index)?; - epoch_obj.rewards_distribution = Some(rewards_distribution); - epoch_obj.reward_tokens = Some(reward_tokens); + let mut prev_epoch_obj = EPOCH_STORE.load(deps.storage, epoch_index)?; + prev_epoch_obj.rewards_distribution = Some(rewards_distribution); + prev_epoch_obj.reward_tokens = Some(reward_tokens); if let Some(expiry_duration) = state.expiry_durations { let expired_at = state.epoch_index + expiry_duration; - epoch_obj.expired_at = Some(expired_at); + prev_epoch_obj.expired_at = Some(expired_at); EXPIRED_AT_LOGGER.update(deps.storage, |mut list| -> StdResult> { if !list.contains(&expired_at) { list.push(expired_at); @@ -505,27 +553,24 @@ pub fn try_end_epoch( }, )?; } + EPOCH_STORE.save(deps.storage, epoch_index, &prev_epoch_obj)?; + + if env.block.time.seconds() >= prev_epoch_obj.end_time { + if !EPOCH_STORE.has(deps.storage, epoch_index.add(1)) { + EPOCH_STORE.save(deps.storage, epoch_index.add(1), &EpochInfo { + rewards_distribution: None, + start_time: prev_epoch_obj.end_time, + end_time: prev_epoch_obj.end_time + state.epoch_durations, + duration: state.epoch_durations, + reward_tokens: None, + expired_at: None, + })?; - EPOCH_STORE.save(deps.storage, state.epoch_index, &epoch_obj)?; - - let now = env.block.time.seconds(); - EPOCH_STORE.save( - deps.storage, - state.epoch_index.add(1), - &EpochInfo { - rewards_distribution: None, - start_time: now, - end_time: now + state.epoch_durations, - duration: state.epoch_durations, - reward_tokens: None, - expired_at: None, - }, - )?; - - state.epoch_index.add_assign(1); - - STATE.save(deps.storage, &state)?; + state.epoch_index.add_assign(1); + STATE.save(deps.storage, &state)?; + } + } Ok(Response::default()) } @@ -563,11 +608,16 @@ pub fn try_claim_rewards(deps: DepsMut, env: Env, info: MessageInfo) -> StdResul staker.starting_round.unwrap_or_default() }; - let ending_epoch = state.epoch_index; + let mut ending_epoch = state.epoch_index; for round_epoch in starting_epoch..ending_epoch { let mut epoch_info = EPOCH_STORE.load(deps.storage, round_epoch)?; + if epoch_info.rewards_distribution.is_none() { + ending_epoch = round_epoch; + break; + } + // Check if the epoch has expired or has no reward tokens let is_expired = epoch_info .expired_at @@ -733,6 +783,7 @@ pub fn try_register_reward_tokens( pub fn try_update_config( deps: DepsMut, info: MessageInfo, + env: Env, admin_auth: Option, query_auth: Option, epoch_duration: Option, @@ -748,14 +799,15 @@ pub fn try_update_config( )?; if let Some(ad_auth) = admin_auth { - state.admin_auth = ad_auth.valid(deps.api)?; + state.admin_auth = ad_auth.into_valid(deps.api)?; } if let Some(q_auth) = query_auth { - state.query_auth = Some(q_auth.valid(deps.api)?); + state.query_auth = q_auth.into_valid(deps.api)?; } if let Some(ep_duration) = epoch_duration { + let _ = process_epoch(deps.storage, &env, &mut state)?; state.epoch_durations = ep_duration; } @@ -768,6 +820,51 @@ pub fn try_update_config( Ok(Response::default()) } +fn process_epoch(storage: &mut dyn Storage, env: &Env, state: &mut State) -> StdResult { + let mut epoch_obj = EPOCH_STORE + .may_load(storage, state.epoch_index)? + .ok_or_else(|| StdError::generic_err("Reward token storage does not exist"))?; + let current_time = env.block.time.seconds(); + + // Check if the current epoch has ended + if current_time >= epoch_obj.end_time { + let difference = current_time - epoch_obj.end_time; + + let multiples: u64 = difference / state.epoch_durations + 1; + + for _ in 0..multiples { + // Move to the next epoch + state.epoch_index += 1; + + // Attempt to load the next epoch's data + match EPOCH_STORE.may_load(storage, state.epoch_index)? { + Some(e) => { + epoch_obj = e; + } + None => { + // Initialize a new epoch if it doesn't exist + let expired_at = match state.expiry_durations { + Some(durations) => Some(durations + epoch_obj.start_time), + None => None, + }; + epoch_obj = EpochInfo { + rewards_distribution: None, + reward_tokens: None, + start_time: epoch_obj.end_time, + end_time: epoch_obj.end_time + state.epoch_durations, + duration: state.epoch_durations, + expired_at, + }; + + EPOCH_STORE.save(storage, state.epoch_index, &epoch_obj)?; + } + } + } + } + + Ok(epoch_obj) +} + pub fn try_add_rewards( deps: DepsMut, info: MessageInfo, @@ -792,13 +889,10 @@ pub fn try_add_rewards( return Err(StdError::generic_err("Cannot start emitting in the past")); } - let decimals = token_info( - &deps.querier, - &Contract { - address: token.address.clone(), - code_hash: token.code_hash.clone(), - }, - )? + let decimals = token_info(&deps.querier, &Contract { + address: token.address.clone(), + code_hash: token.code_hash.clone(), + })? .decimals; let total_epoches = end.sub(start) + 1; @@ -837,49 +931,6 @@ pub fn try_add_rewards( Ok(Response::default()) } -pub fn try_create_viewing_key( - deps: DepsMut, - env: Env, - info: MessageInfo, - entropy: String, -) -> StdResult { - let key = ViewingKey::create( - deps.storage, - &info, - &env, - info.sender.as_str(), - entropy.as_ref(), - ); - - Ok(Response::new().set_data(to_binary(&ExecuteAnswer::CreateViewingKey { key })?)) -} - -pub fn try_set_viewing_key( - deps: DepsMut, - _env: Env, - info: MessageInfo, - key: String, -) -> StdResult { - ViewingKey::set(deps.storage, info.sender.as_str(), key.as_str()); - Ok(Response::new()) -} - -pub fn try_revoke_permit( - deps: DepsMut, - _env: Env, - info: MessageInfo, - permit_name: String, -) -> StdResult { - RevokedPermits::revoke_permit( - deps.storage, - PREFIX_REVOKED_PERMITS, - info.sender.as_ref(), - &permit_name, - ); - - Ok(Response::new()) -} - pub fn try_recover_expired_funds( deps: DepsMut, _env: Env, @@ -953,12 +1004,53 @@ pub fn try_recover_funds( msg: Option, ) -> StdResult { let state = STATE.load(deps.storage)?; + let mut total_amount = Uint128::zero(); + validate_admin( &deps.querier, AdminPermissions::StakingAdmin, info.sender.to_string(), &state.admin_auth.into(), )?; + + for reward_token in REWARD_TOKENS.load(deps.storage)? { + let rewards_token_info; + if let Ok(r_t_i) = REWARD_TOKEN_INFO.load(deps.storage, &reward_token.address) { + rewards_token_info = r_t_i; + } else { + continue; + }; + + for reward_token in rewards_token_info { + if token.address() == reward_token.token.address { + total_amount += reward_token.total_rewards; + } + } + } + + let token_balance = token.query_balance( + deps.as_ref(), + _env.contract.address.into_string(), + SHADE_STAKING_VIEWING_KEY.to_string(), + )?; + + let extra_amount = if let Ok(extra) = token_balance.checked_sub(total_amount) { + extra + } else { + // Handle underflow in subtracting from total_liquidity + return Err(StdError::generic_err( + "Underflow in subtracting from recover_funds", + )); + }; + + if extra_amount < amount { + return Err(StdError::generic_err(format!( + "Trying to recover already staked funds. Extra funds {:?}, amount {:?}", + extra_amount, amount, + ))); + } + + //Check if amount asked is greater than amount staked by the admin let send_msg = match token { TokenType::CustomToken { contract_addr, diff --git a/contracts/liquidity_book/lb_staking/src/query.rs b/contracts/liquidity_book/lb_staking/src/query.rs index a1f10e55..f174f2c4 100644 --- a/contracts/liquidity_book/lb_staking/src/query.rs +++ b/contracts/liquidity_book/lb_staking/src/query.rs @@ -3,6 +3,8 @@ use std::str::FromStr; use shade_protocol::{ c_std::{to_binary, Addr, Binary, Deps, Env, StdError, StdResult, Uint256}, liquidity_book::lb_staking::{ + Auth, + EpochInfo, Liquidity, OwnerBalance, QueryAnswer, @@ -18,8 +20,10 @@ use shade_protocol::{ }; use crate::{ + contract::authenticate, helper::get_txs, state::{ + EPOCH_STORE, REWARD_TOKENS, STAKERS, STAKERS_BIN_TREE, @@ -49,6 +53,23 @@ pub fn query_contract_info(deps: Deps) -> StdResult { to_binary(&response) } +pub fn query_epoch_info(deps: Deps, epoch_index: Option) -> StdResult { + let state: State = STATE.load(deps.storage)?; + let epoch_info: EpochInfo = + EPOCH_STORE.load(deps.storage, epoch_index.unwrap_or(state.epoch_index))?; + + let response = QueryAnswer::EpochInfo { + rewards_distribution: epoch_info.rewards_distribution, + reward_tokens: epoch_info.reward_tokens, + start_time: epoch_info.start_time, + end_time: epoch_info.end_time, + duration: epoch_info.duration, + expired_at: epoch_info.expired_at, + }; + + to_binary(&response) +} + pub fn query_registered_tokens(deps: Deps) -> StdResult { let reg_tokens = REWARD_TOKENS.load(deps.storage)?; @@ -70,53 +91,22 @@ pub fn query_token_id_balance(deps: Deps, token_id: String) -> StdResult to_binary(&response) } -pub fn viewing_keys_queries(deps: Deps, msg: QueryMsg) -> StdResult { - let (addresses, key) = msg.get_validation_params()?; - - for address in addresses { - let result = ViewingKey::check(deps.storage, address.as_str(), key.as_str()); - if result.is_ok() { - return match msg { - QueryMsg::Balance { - owner, token_id, .. - } => query_balance(deps, &owner, token_id), - - QueryMsg::AllBalances { - owner, - page, - page_size, - .. - } => query_all_balances(deps, &owner, page, page_size), - - QueryMsg::Liquidity { - owner, - round_index, - token_ids, - .. - } => query_liquidity(deps, &owner, token_ids, round_index), - - QueryMsg::TransactionHistory { - owner, - page, - page_size, - txn_type, - .. - } => query_transaction_history(deps, &owner, page, page_size, txn_type), - - QueryMsg::WithPermit { .. } => { - unreachable!("This query type does not require viewing key authentication") - } - _ => unreachable!("This query type does not require viewing key authentication"), - }; - } - } +pub fn query_staker_info(deps: Deps, auth: Auth) -> StdResult { + let state: State = STATE.load(deps.storage)?; + let owner = &authenticate(deps, auth, state.query_auth)?; + let staker_info = STAKERS.load(deps.storage, owner)?; - to_binary(&QueryAnswer::ViewingKeyError { - msg: "Wrong viewing key for this address or viewing key not set".to_string(), - }) + let response = QueryAnswer::StakerInfo { + starting_round: staker_info.starting_round, + total_rewards_earned: staker_info.total_rewards_earned, + last_claim_rewards_round: staker_info.last_claim_rewards_round, + }; + to_binary(&response) } -pub fn query_balance(deps: Deps, owner: &Addr, token_id: String) -> StdResult { +pub fn query_balance(deps: Deps, auth: Auth, token_id: String) -> StdResult { + let state = STATE.load(deps.storage)?; + let owner = &authenticate(deps, auth, state.query_auth)?; let id = u32::from_str(&token_id) .map_err(|_| StdError::generic_err(format!("token_id {} cannot be parsed", token_id)))?; @@ -132,10 +122,13 @@ pub fn query_balance(deps: Deps, owner: &Addr, token_id: String) -> StdResult, page_size: Option, ) -> StdResult { + let state = STATE.load(deps.storage)?; + let owner = &authenticate(deps, auth, state.query_auth)?; + let page = page.unwrap_or(0u32); let page_size = page_size.unwrap_or(50u32); let tree = STAKERS_BIN_TREE.load(deps.storage, owner)?; @@ -164,11 +157,13 @@ pub fn query_all_balances( pub fn query_transaction_history( deps: Deps, - owner: &Addr, + auth: Auth, page: Option, page_size: Option, query_type: QueryTxnType, ) -> StdResult { + let state = STATE.load(deps.storage)?; + let owner = &authenticate(deps, auth, state.query_auth)?; let page = page.unwrap_or(0u32); let page_size = page_size.unwrap_or(50u32); @@ -180,11 +175,12 @@ pub fn query_transaction_history( pub fn query_liquidity( deps: Deps, - owner: &Addr, + auth: Auth, token_ids: Vec, round_index: Option, ) -> StdResult { let state = STATE.load(deps.storage)?; + let owner = &authenticate(deps, auth, state.query_auth)?; let staker_result = STAKERS.load(deps.storage, &owner); let mut liquidity_response = Vec::new(); // Check if staker exists @@ -228,7 +224,7 @@ pub fn query_liquidity( if let Some(r_i) = round_index.unwrap_or(state.epoch_index).checked_sub(1) { r_i } else { - return Err(StdError::generic_err("Under-flow sub error")); + return Err(StdError::generic_err("Under-flow sub error query_liq 1")); }; let start = if staker.last_claim_rewards_round.is_some() { @@ -260,7 +256,7 @@ pub fn query_liquidity( finding_liq_round = if let Some(f_liq) = finding_liq_round.checked_sub(1) { f_liq } else { - return Err(StdError::generic_err("Under-flow sub error")); + return Err(StdError::generic_err("Under-flow sub error query_liq 2")); } } } @@ -278,50 +274,3 @@ pub fn query_liquidity( to_binary(&response) } - -pub fn permit_queries( - deps: Deps, - env: Env, - permit: Permit, - query: QueryWithPermit, -) -> Result { - // Validate permit content - let contract_address = env.contract.address; - let account_str = validate( - deps, - PREFIX_REVOKED_PERMITS, - &permit, - contract_address.to_string(), - None, - )?; - let account = deps.api.addr_validate(&account_str)?; - - let is_owner = permit.check_permission(&TokenPermissions::Owner); - - // Permit validated! We can now execute the query. - match query { - QueryWithPermit::Balance { owner, token_id } => { - if !is_owner { - if !permit.check_permission(&TokenPermissions::Balance) { - return Err(StdError::generic_err(format!( - "`Owner` or `Balance` permit required for permit queries, got permissions {:?}", - permit.params.permissions - ))); - } - } - query_balance(deps, &owner, token_id) - } - QueryWithPermit::AllBalances { page, page_size } => { - if !is_owner { - if !permit.check_permission(&TokenPermissions::Balance) { - return Err(StdError::generic_err(format!( - "`Owner` or `Balance` permit required for permit queries, got permissions {:?}", - permit.params.permissions - ))); - } - } - query_all_balances(deps, &account, page, page_size) - } - _ => unreachable!("This query type does not require viewing key authentication"), - } -} diff --git a/contracts/liquidity_book/lb_token/src/contract.rs b/contracts/liquidity_book/lb_token/src/contract.rs index 9531359e..15890c03 100644 --- a/contracts/liquidity_book/lb_token/src/contract.rs +++ b/contracts/liquidity_book/lb_token/src/contract.rs @@ -21,6 +21,8 @@ use cosmwasm_std::{ Uint256, }; +use crate::{execute::*, query::*}; + use crate::state::{ balances_r, balances_w, @@ -273,15 +275,15 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S // remove_minters, // padding: _, // } => try_remove_minters(deps, env, info, token_id, remove_minters), - // ExecuteMsg::ChangeAdmin { - // new_admin, - // padding: _, - // } => try_change_admin(deps, env, info, new_admin), - // ExecuteMsg::RemoveAdmin { - // current_admin, - // contract_address, - // padding: _, - // } => try_remove_admin(deps, env, info, current_admin, contract_address), + ExecuteMsg::ChangeAdmin { + new_admin, + padding: _, + } => try_change_admin(deps, env, info, new_admin), + ExecuteMsg::RemoveAdmin { + current_admin, + contract_address, + padding: _, + } => try_remove_admin(deps, env, info, current_admin, contract_address), ExecuteMsg::RegisterReceive { code_hash, padding: _, @@ -290,1173 +292,6 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S pad_response(response) } -// fn try_curate_token_ids( -// mut deps: DepsMut, -// env: Env, -// info: MessageInfo, -// initial_tokens: Vec, -// memo: Option, -// ) -> StdResult { -// let mut config = contr_conf_r(deps.storage).load()?; -// // check if sender is a curator -// verify_curator(&config, &info)?; - -// // curate new token_ids -// for initial_token in initial_tokens { -// exec_curate_token_id( -// &mut deps, -// &env, -// &info, -// &mut config, -// initial_token, -// memo.clone(), -// )?; -// } - -// contr_conf_w(deps.storage).save(&config)?; - -// Ok( -// Response::new().set_data(to_binary(&ExecuteAnswer::CurateTokenIds { -// status: Success, -// })?), -// ) -// } - -fn try_mint_tokens( - mut deps: DepsMut, - env: Env, - info: MessageInfo, - mint_tokens: Vec, - memo: Option, -) -> StdResult { - let mut config = contr_conf_r(deps.storage).load()?; - verify_curator(&config, &info)?; - - // mint tokens - for mint_token in mint_tokens { - let token_info_op = tkn_info_r(deps.storage).may_load(mint_token.token_id.as_bytes())?; - - // check if token_id exists - if token_info_op.is_none() { - let curate_token = CurateTokenId { - token_info: TokenInfoMsg { - token_id: mint_token.token_id.clone(), - name: format!("LP-{}", &config.lb_pair_info.symbol), - symbol: format!("LP-{}", &config.lb_pair_info.symbol), - token_config: TknConfig::Fungible { - minters: Vec::new(), // No need for minter curator will be the minter - decimals: config.lb_pair_info.decimals, - public_total_supply: true, - enable_mint: true, - enable_burn: true, - minter_may_update_metadata: false, - }, - public_metadata: None, - private_metadata: None, - }, - balances: mint_token.balances, - }; - - exec_curate_token_id( - &mut deps, - &env, - &info, - &mut config, - curate_token, - memo.clone(), - )?; - continue; - } - - // check if enable_mint == true - // if !token_info_op - // .clone() - // .unwrap() - // .token_config - // .flatten() - // .enable_mint - // { - // return Err(StdError::generic_err( - // "minting is not enabled for this token_id", - // )); - // } - - // check if sender is a minter - // verify_minter(token_info_op.as_ref().unwrap(), &info)?; - // add balances - - for add_balance in mint_token.balances { - exec_change_balance( - deps.storage, - &mint_token.token_id, - None, - Some(&add_balance.address), - &add_balance.amount, - &token_info_op.clone().unwrap(), - )?; - - // store mint_token - store_mint( - deps.storage, - &mut config, - &env.block, - &mint_token.token_id, - deps.api.addr_canonicalize(info.sender.as_str())?, - deps.api.addr_canonicalize(add_balance.address.as_str())?, - add_balance.amount, - memo.clone(), - )?; - } - } - - contr_conf_w(deps.storage).save(&config)?; - - Ok(Response::new().set_data(to_binary(&ExecuteAnswer::MintTokens { status: Success })?)) -} - -// in the base specifications, this function can be performed by token owner only -fn try_burn_tokens( - deps: DepsMut, - env: Env, - info: MessageInfo, - burn_tokens: Vec, - memo: Option, -) -> StdResult { - let mut config = contr_conf_r(deps.storage).load()?; - verify_curator(&config, &info)?; - // burn tokens - for burn_token in burn_tokens { - let token_info_op = tkn_info_r(deps.storage).may_load(burn_token.token_id.as_bytes())?; - - if token_info_op.is_none() { - return Err(StdError::generic_err( - "token_id does not exist. Cannot burn non-existent `token_ids`. Use `curate_token_ids` to create tokens on new `token_ids`", - )); - } - - let token_info = token_info_op.clone().unwrap(); - - if !token_info.token_config.flatten().enable_burn { - return Err(StdError::generic_err( - "burning is not enabled for this token_id", - )); - } - - // remove balances - for rem_balance in burn_token.balances { - // in base specification, burner MUST be the owner - // if rem_balance.address != info.sender { - // return Err(StdError::generic_err(format!( - // "you do not have permission to burn {} tokens from address {}", - // rem_balance.amount, rem_balance.address - // ))); - // } - - exec_change_balance( - deps.storage, - &burn_token.token_id, - Some(&rem_balance.address), - None, - &rem_balance.amount, - &token_info, - )?; - - // store burn_token - store_burn( - deps.storage, - &mut config, - &env.block, - &burn_token.token_id, - // in base specification, burner MUST be the owner - None, - deps.api.addr_canonicalize(rem_balance.address.as_str())?, - rem_balance.amount, - memo.clone(), - )?; - } - } - - contr_conf_w(deps.storage).save(&config)?; - - Ok(Response::new().set_data(to_binary(&ExecuteAnswer::BurnTokens { status: Success })?)) -} - -fn try_change_metadata( - deps: DepsMut, - _env: Env, - info: MessageInfo, - token_id: String, - public_metadata: Option, - private_metadata: Option, -) -> StdResult { - let tkn_info_op = tkn_info_r(deps.storage).may_load(token_id.as_bytes())?; - let tkn_conf = match tkn_info_op.clone() { - None => { - return Err(StdError::generic_err(format!( - "token_id {} does not exist", - token_id - ))); - } - Some(i) => i.token_config.flatten(), - }; - - // define variables for control flow - let owner = may_get_current_owner(deps.storage, &token_id)?; - let is_owner = match owner { - Some(owner_addr) => owner_addr == info.sender, - None => false, - }; - - let is_minter = verify_minter(tkn_info_op.as_ref().unwrap(), &info).is_ok(); - - // can sender change metadata? based on i) sender is minter or owner, ii) token_id config allows it or not - let allow_update = is_owner && tkn_conf.owner_may_update_metadata - || is_minter && tkn_conf.minter_may_update_metadata; - - // control flow based on `allow_update` - match allow_update { - false => { - return Err(StdError::generic_err(format!( - "unable to change the metadata for token_id {}", - token_id - ))); - } - true => { - let mut tkn_info = tkn_info_op.unwrap(); - if public_metadata.is_some() { - tkn_info.public_metadata = public_metadata - }; - if private_metadata.is_some() { - tkn_info.private_metadata = private_metadata - }; - tkn_info_w(deps.storage).save(token_id.as_bytes(), &tkn_info)?; - } - } - - Ok( - Response::new().set_data(to_binary(&ExecuteAnswer::ChangeMetadata { - status: Success, - })?), - ) -} - -#[allow(clippy::too_many_arguments)] -fn try_transfer( - mut deps: DepsMut, - env: Env, - info: MessageInfo, - token_id: String, - from: Addr, - recipient: Addr, - amount: Uint256, - memo: Option, -) -> StdResult { - impl_transfer( - &mut deps, &env, &info, &token_id, &from, &recipient, amount, memo, - )?; - - Ok(Response::new().set_data(to_binary(&ExecuteAnswer::Transfer { status: Success })?)) -} - -fn try_batch_transfer( - mut deps: DepsMut, - env: Env, - info: MessageInfo, - actions: Vec, -) -> StdResult { - for action in actions { - let from = deps.api.addr_validate(action.from.as_str())?; - let recipient = deps.api.addr_validate(action.recipient.as_str())?; - impl_transfer( - &mut deps, - &env, - &info, - &action.token_id, - &from, - &recipient, - action.amount, - action.memo, - )?; - } - - Ok( - Response::new().set_data(to_binary(&ExecuteAnswer::BatchTransfer { - status: Success, - })?), - ) -} - -fn try_send( - mut deps: DepsMut, - env: Env, - info: MessageInfo, - action: SendAction, -) -> StdResult { - // set up cosmos messages - let mut messages = vec![]; - - impl_send(&mut deps, &env, &info, &mut messages, action)?; - - let data = to_binary(&ExecuteAnswer::Send { status: Success })?; - let res = Response::new().add_messages(messages).set_data(data); - Ok(res) -} - -fn try_batch_send( - mut deps: DepsMut, - env: Env, - info: MessageInfo, - actions: Vec, -) -> StdResult { - // declare vector for cosmos messages - let mut messages = vec![]; - - for action in actions { - impl_send(&mut deps, &env, &info, &mut messages, action)?; - } - - let data = to_binary(&ExecuteAnswer::BatchSend { status: Success })?; - let res = Response::new().add_messages(messages).set_data(data); - Ok(res) -} - -/// does not check if `token_id` exists so attacker cannot easily figure out if -/// a `token_id` has been created -#[allow(clippy::too_many_arguments)] -fn try_give_permission( - deps: DepsMut, - _env: Env, - info: MessageInfo, - allowed_address: Addr, - token_id: String, - view_balance: Option, - view_balance_expiry: Option, - view_private_metadata: Option, - view_private_metadata_expiry: Option, - transfer: Option, - transfer_expiry: Option, -) -> StdResult { - // may_load current permission - let permission_op = - may_load_any_permission(deps.storage, &info.sender, &token_id, &allowed_address)?; - - let action = |old_perm: Permission, - view_balance: Option, - view_balance_expiry: Option, - view_private_metadata: Option, - view_private_metadata_expiry: Option, - transfer: Option, - transfer_expiry: Option| - -> Permission { - Permission { - view_balance_perm: match view_balance { - Some(i) => i, - None => old_perm.view_balance_perm, - }, - view_balance_exp: match view_balance_expiry { - Some(i) => i, - None => old_perm.view_balance_exp, - }, - view_pr_metadata_perm: match view_private_metadata { - Some(i) => i, - None => old_perm.view_pr_metadata_perm, - }, - view_pr_metadata_exp: match view_private_metadata_expiry { - Some(i) => i, - None => old_perm.view_pr_metadata_exp, - }, - trfer_allowance_perm: match transfer { - Some(i) => i, - None => old_perm.trfer_allowance_perm, - }, - trfer_allowance_exp: match transfer_expiry { - Some(i) => i, - None => old_perm.trfer_allowance_exp, - }, - } - }; - - // create new permission if not created yet, otherwise update existing permission - match permission_op { - Some(old_perm) => { - let updated_permission = action( - old_perm, - view_balance, - view_balance_expiry, - view_private_metadata, - view_private_metadata_expiry, - transfer, - transfer_expiry, - ); - update_permission( - deps.storage, - &info.sender, - &token_id, - &allowed_address, - &updated_permission, - )?; - } - None => { - let default_permission = Permission::default(); - let updated_permission = action( - default_permission, - view_balance, - view_balance_expiry, - view_private_metadata, - view_private_metadata_expiry, - transfer, - transfer_expiry, - ); - new_permission( - deps.storage, - &info.sender, - &token_id, - &allowed_address, - &updated_permission, - )?; - } - }; - - Ok( - Response::new().set_data(to_binary(&ExecuteAnswer::GivePermission { - status: Success, - })?), - ) -} - -/// changes an existing permission entry to default (ie: revoke all permissions granted). Does not remove -/// entry in storage, because it is unecessarily in most use cases, but will require also removing -/// owner-specific PermissionKeys, which introduces complexity and increases gas cost. -/// If permission does not exist, message will return an error. -fn try_revoke_permission( - deps: DepsMut, - _env: Env, - info: MessageInfo, - token_id: String, - owner: Addr, - allowed_addr: Addr, -) -> StdResult { - // either owner or allowed_address can remove permission - if info.sender != owner && info.sender != allowed_addr { - return Err(StdError::generic_err( - "only the owner or address with permission can remove permission", - )); - } - - update_permission( - deps.storage, - &owner, - &token_id, - &allowed_addr, - &Permission::default(), - )?; - - Ok( - Response::new().set_data(to_binary(&ExecuteAnswer::RevokePermission { - status: Success, - })?), - ) -} - -fn try_create_viewing_key( - deps: DepsMut, - env: Env, - info: MessageInfo, - entropy: String, -) -> StdResult { - let key = ViewingKey::create( - deps.storage, - &info, - &env, - info.sender.as_str(), - entropy.as_ref(), - ); - - Ok(Response::new().set_data(to_binary(&ExecuteAnswer::CreateViewingKey { key })?)) -} - -fn try_set_viewing_key( - deps: DepsMut, - _env: Env, - info: MessageInfo, - key: String, -) -> StdResult { - ViewingKey::set(deps.storage, info.sender.as_str(), key.as_str()); - Ok( - Response::new().set_data(to_binary(&ExecuteAnswer::SetViewingKey { - status: Success, - })?), - ) -} - -fn try_revoke_permit( - deps: DepsMut, - _env: Env, - info: MessageInfo, - permit_name: String, -) -> StdResult { - RevokedPermits::revoke_permit( - deps.storage, - PREFIX_REVOKED_PERMITS, - info.sender.as_ref(), - &permit_name, - ); - - Ok(Response::new().set_data(to_binary(&ExecuteAnswer::RevokePermit { status: Success })?)) -} - -// fn try_add_curators( -// deps: DepsMut, -// _env: Env, -// info: MessageInfo, -// add_curators: Vec, -// ) -> StdResult { -// let mut config = contr_conf_r(deps.storage).load()?; - -// // verify admin -// verify_admin(&config, &info)?; - -// // add curators -// for curator in add_curators { -// config.curators.push(curator); -// } -// contr_conf_w(deps.storage).save(&config)?; - -// Ok(Response::new().set_data(to_binary(&ExecuteAnswer::AddCurators { status: Success })?)) -// } - -// fn try_remove_curators( -// deps: DepsMut, -// _env: Env, -// info: MessageInfo, -// remove_curators: Vec, -// ) -> StdResult { -// let mut config = contr_conf_r(deps.storage).load()?; - -// // verify admin -// verify_admin(&config, &info)?; - -// // remove curators -// for curator in remove_curators { -// config.curators.retain(|x| x != &curator); -// } -// contr_conf_w(deps.storage).save(&config)?; - -// Ok( -// Response::new().set_data(to_binary(&ExecuteAnswer::RemoveCurators { -// status: Success, -// })?), -// ) -// } - -// fn try_add_minters( -// deps: DepsMut, -// _env: Env, -// info: MessageInfo, -// token_id: String, -// add_minters: Vec, -// ) -> StdResult { -// let contract_config = contr_conf_r(deps.storage).load()?; -// let token_info_op = tkn_info_r(deps.storage).may_load(token_id.as_bytes())?; -// if token_info_op.is_none() { -// return Err(StdError::generic_err(format!( -// "token_id {} does not exist", -// token_id -// ))); -// }; -// let mut token_info = token_info_op.unwrap(); - -// // check if either admin -// let admin_result = verify_admin(&contract_config, &info); -// // let curator_result = verify_curator_of_token_id(&token_info, &env); Not part of base specifications. - -// let verified = admin_result.is_ok(); // || curator_result.is_ok(); -// if !verified { -// return Err(StdError::generic_err( -// "You need to be the admin to add or remove minters", -// )); -// } - -// // add minters -// let mut flattened_token_config = token_info.token_config.flatten(); -// for minter in add_minters { -// flattened_token_config.minters.push(minter) -// } - -// // save token info with new minters -// token_info.token_config = flattened_token_config.to_enum(); -// tkn_info_w(deps.storage).save(token_id.as_bytes(), &token_info)?; - -// Ok(Response::new().set_data(to_binary(&ExecuteAnswer::AddMinters { status: Success })?)) -// } - -// fn try_remove_minters( -// deps: DepsMut, -// _env: Env, -// info: MessageInfo, -// token_id: String, -// remove_minters: Vec, -// ) -> StdResult { -// let contract_config = contr_conf_r(deps.storage).load()?; -// let token_info_op = tkn_info_r(deps.storage).may_load(token_id.as_bytes())?; -// if token_info_op.is_none() { -// return Err(StdError::generic_err(format!( -// "token_id {} does not exist", -// token_id -// ))); -// }; -// let mut token_info = token_info_op.unwrap(); - -// // check if either admin or curator -// let admin_result = verify_admin(&contract_config, &info); -// // let curator_result = verify_curator_of_token_id(&token_info, &env); Not part of base specifications. - -// let verified = admin_result.is_ok(); // || curator_result.is_ok(); -// if !verified { -// return Err(StdError::generic_err( -// "You need to be the admin to add or remove minters", -// )); -// } - -// // remove minters -// let mut flattened_token_config = token_info.token_config.flatten(); -// for minter in remove_minters { -// flattened_token_config.minters.retain(|x| x != &minter); -// } - -// // save token info with new minters -// token_info.token_config = flattened_token_config.to_enum(); -// tkn_info_w(deps.storage).save(token_id.as_bytes(), &token_info)?; - -// Ok( -// Response::new().set_data(to_binary(&ExecuteAnswer::RemoveMinters { -// status: Success, -// })?), -// ) -// } - -//No need to change admin cause we're using admin_auth - -// fn try_change_admin( -// deps: DepsMut, -// _env: Env, -// info: MessageInfo, -// new_admin: Addr, -// ) -> StdResult { -// let mut config = contr_conf_r(deps.storage).load()?; - -// // verify admin -// verify_admin(&config, &info)?; - -// // change admin -// config.admin = Some(new_admin); -// contr_conf_w(deps.storage).save(&config)?; - -// Ok(Response::new().set_data(to_binary(&ExecuteAnswer::ChangeAdmin { status: Success })?)) -// } - -// fn try_remove_admin( -// deps: DepsMut, -// _env: Env, -// info: MessageInfo, -// current_admin: Addr, -// contract_address: Addr, -// ) -> StdResult { -// let mut config = contr_conf_r(deps.storage).load()?; - -// // verify admin -// verify_admin(&config, &info)?; - -// // checks on redundancy inputs, designed to reduce chances of accidentally -// // calling this function -// if current_admin != config.admin.unwrap() || contract_address != config.contract_address { -// return Err(StdError::generic_err( -// "your inputs are incorrect to perform this function", -// )); -// } - -// // remove admin -// config.admin = None; -// contr_conf_w(deps.storage).save(&config)?; - -// Ok(Response::new().set_data(to_binary(&ExecuteAnswer::RemoveAdmin { status: Success })?)) -// } - -fn try_register_receive( - deps: DepsMut, - _env: Env, - info: MessageInfo, - code_hash: String, -) -> StdResult { - set_receiver_hash(deps.storage, &info.sender, code_hash); - - let data = to_binary(&ExecuteAnswer::RegisterReceive { status: Success })?; - Ok(Response::new() - .add_attribute("register_status", "success") - .set_data(data)) -} - -///////////////////////////////////////////////////////////////////////////////// -// Private functions -///////////////////////////////////////////////////////////////////////////////// - -fn pad_response(response: StdResult) -> StdResult { - response.map(|mut response| { - response.data = response.data.map(|mut data| { - space_pad(&mut data.0, RESPONSE_BLOCK_SIZE); - data - }); - response - }) -} - -fn is_valid_name(name: &str) -> bool { - let len = name.len(); - (3..=30).contains(&len) -} - -fn is_valid_symbol(symbol: &str) -> bool { - let len = symbol.len(); - (3..=30).contains(&len) - // let len = symbol.len(); - // let len_is_valid = (3..=50).contains(&len); - - // // len_is_valid && symbol.bytes().all(|byte| (b'A'..=b'Z').contains(&byte)) - // len_is_valid && symbol.bytes().all(|byte| byte.is_ascii_uppercase()) -} - -fn verify_admin(contract_config: &ContractConfig, info: &MessageInfo) -> StdResult<()> { - let admin_op = &contract_config.admin; - match admin_op { - Some(admin) => { - if admin != &info.sender { - return Err(StdError::generic_err("This is an admin function")); - } - } - None => return Err(StdError::generic_err("This contract has no admin")), - } - - Ok(()) -} - -/// verifies if sender is a curator -fn verify_curator(contract_config: &ContractConfig, info: &MessageInfo) -> StdResult<()> { - let curators = &contract_config.curators; - if !curators.contains(&info.sender) { - return Err(StdError::generic_err( - "Only curators are allowed to curate token_ids", - )); - } - Ok(()) -} - -// /// verifies if sender is the address that curated the token_id. -// /// Not part of base specifications, but function left here for potential use. -// /// If this additional feature is implemented, it is important to ensure that the instantiator -// /// still has the ability to set initial balances without later being able to change minters. -// fn verify_curator_of_token_id( -// token_info: &StoredTokenInfo, -// env: &Env -// ) -> StdResult<()> { -// let curator = &token_info.curator; -// if curator != &env.message.sender { -// return Err(StdError::generic_err( -// "You are not the curator of this token_id", -// )); -// } -// Ok(()) -// } - -/// verifies if sender is a minter of the specific token_id -fn verify_minter(token_info: &StoredTokenInfo, info: &MessageInfo) -> StdResult<()> { - let minters = &token_info.token_config.flatten().minters; - if !minters.contains(&info.sender) { - return Err(StdError::generic_err(format!( - "Only minters are allowed to mint additional tokens for token_id {}", - token_info.token_id - ))); - } - Ok(()) -} - -/// checks if `token_id` is available (ie: not yet created), then creates new `token_id` and initial balances -fn exec_curate_token_id( - deps: &mut DepsMut, - env: &Env, - info: &MessageInfo, - config: &mut ContractConfig, - initial_token: CurateTokenId, - memo: Option, -) -> StdResult<()> { - // check: token_id has not been created yet - if tkn_info_r(deps.storage) - .may_load(initial_token.token_info.token_id.as_bytes())? - .is_some() - { - return Err(StdError::generic_err( - "token_id already exists. Try a different id String", - )); - } - - // check: token_id is an NFT => cannot create more than one - if initial_token.token_info.token_config.flatten().is_nft { - if initial_token.balances.len() > 1 { - return Err(StdError::generic_err(format!( - "token_id {} is an NFT; there can only be one NFT. Balances should only have one address", - initial_token.token_info.token_id - ))); - } else if initial_token.balances[0].amount != Uint256::from(1_u64) { - return Err(StdError::generic_err(format!( - "token_id {} is an NFT; there can only be one NFT. Balances.amount must == 1", - initial_token.token_info.token_id - ))); - } - } - - // Check name, symbol, decimals - if !is_valid_name(&initial_token.token_info.name) { - return Err(StdError::generic_err( - "Name is not in the expected format (3-30 UTF-8 bytes)", - )); - } - - if !is_valid_symbol(&initial_token.token_info.symbol) { - return Err(StdError::generic_err( - "Ticker symbol is not in expected format [A-Z]{3,6}", - )); - } - - if initial_token.token_info.token_config.flatten().decimals > 18 { - return Err(StdError::generic_err("Decimals must not exceed 18")); - } - - // create and save new token info - tkn_info_w(deps.storage).save( - initial_token.token_info.token_id.as_bytes(), - &initial_token.token_info.to_store(&info.sender), - )?; - - // set initial balances and store mint history - for balance in initial_token.balances { - // save new balances - balances_w(deps.storage, &initial_token.token_info.token_id) - .save(to_binary(&balance.address)?.as_slice(), &balance.amount)?; - // if is_nft == true, store owner of NFT - if initial_token.token_info.token_config.flatten().is_nft { - append_new_owner( - deps.storage, - &initial_token.token_info.token_id, - &balance.address, - )?; - } - // initiate total token supply - tkn_tot_supply_w(deps.storage).save( - initial_token.token_info.token_id.as_bytes(), - &balance.amount, - )?; - - // store mint_token_id - store_mint( - deps.storage, - config, - &env.block, - &initial_token.token_info.token_id, - deps.api.addr_canonicalize(info.sender.as_str())?, - deps.api.addr_canonicalize(balance.address.as_str())?, - balance.amount, - memo.clone(), - )?; - } - - // push token_id to token_id_list - config.token_id_list.push(initial_token.token_info.token_id); - - Ok(()) -} - -/// Implements a single `Send` function. Transfers Uint256 amount of a single `token_id`, -/// saves transfer history, may register-receive, and creates callback message. -fn impl_send( - deps: &mut DepsMut, - env: &Env, - info: &MessageInfo, - messages: &mut Vec, - action: SendAction, -) -> StdResult<()> { - // action variables from SendAction - let token_id = action.token_id; - let from = action.from; - let amount = action.amount; - let recipient = action.recipient; - let recipient_code_hash = action.recipient_code_hash; - let msg = action.msg; - let memo = action.memo; - - // implements transfer of tokens - impl_transfer( - deps, - env, - info, - &token_id, - &from, - &recipient, - amount, - memo.clone(), - )?; - - // create cosmos message - try_add_receiver_api_callback( - deps.storage, - messages, - recipient, - recipient_code_hash, - msg, - info.sender.clone(), - token_id, - from.to_owned(), - amount, - memo, - )?; - - Ok(()) -} - -/// Implements a single `Transfer` function. Transfers a Uint256 amount of a -/// single `token_id` and saves the transfer history. Used by `Transfer` and -/// `Send` (via `impl_send`) messages -#[allow(clippy::too_many_arguments)] -fn impl_transfer( - deps: &mut DepsMut, - env: &Env, - info: &MessageInfo, - token_id: &str, - from: &Addr, - recipient: &Addr, - amount: Uint256, - memo: Option, -) -> StdResult<()> { - // check if `from` == message sender || has enough allowance to send tokens - // perform allowance check, and may reduce allowance - let mut throw_err = false; - if from != &info.sender { - // may_load_active_permission() or may_load_any_permission() both work. The former performs redundancy checks, which are - // more relevant for authenticated queries (because transfer simply won't work if there is no balance) - let permission_op = may_load_any_permission(deps.storage, from, token_id, &info.sender)?; - - match permission_op { - // no permission given - None => throw_err = true, - // allowance has expired - Some(perm) if perm.trfer_allowance_exp.is_expired(&env.block) => { - return Err(StdError::generic_err(format!( - "Allowance has expired: {}", - perm.trfer_allowance_exp - ))); - } - // not enough allowance to transfer amount - Some(perm) if perm.trfer_allowance_perm < amount => { - return Err(StdError::generic_err(format!( - "Insufficient transfer allowance: {}", - perm.trfer_allowance_perm - ))); - } - // success, so need to reduce allowance - Some(mut perm) if perm.trfer_allowance_perm >= amount => { - let new_allowance = perm - .trfer_allowance_perm - .checked_sub(amount) - .expect("something strange happened"); - perm.trfer_allowance_perm = new_allowance; - update_permission(deps.storage, from, token_id, &info.sender, &perm)?; - } - Some(_) => unreachable!("impl_transfer permission check: this should not be reachable"), - } - } - - // check that token_id exists - let token_info_op = tkn_info_r(deps.storage).may_load(token_id.as_bytes())?; - if token_info_op.is_none() { - throw_err = true - } - - // combined error message for no token_id or no permission given in one place to make it harder to identify if token_id already exists - match throw_err { - true => { - return Err(StdError::generic_err( - "These tokens do not exist or you have no permission to transfer", - )); - } - false => (), - } - - // transfer tokens - exec_change_balance( - deps.storage, - token_id, - Some(from), - Some(recipient), - &amount, - &token_info_op.unwrap(), - )?; - - // store transaction - let mut config = contr_conf_r(deps.storage).load()?; - store_transfer( - deps.storage, - &mut config, - &env.block, - token_id, - deps.api.addr_canonicalize(from.as_str())?, - None, - deps.api.addr_canonicalize(recipient.as_str())?, - amount, - memo, - )?; - contr_conf_w(deps.storage).save(&config)?; - - Ok(()) -} - -/// change token balance of an existing `token_id`. -/// -/// Should check that `token_id` already exists before calling this function, which is not done -/// explicitly in this function. Although token_info is an argument in this function, so it is -/// likely that the calling function would have had to check. -/// * If `remove_from` == None: minted new tokens. -/// * If `add_to` == None: burn tokens. -/// * If is_nft == true, then `remove_from` MUST be Some(_). -/// * If is_nft == true, stores new owner of NFT -fn exec_change_balance( - storage: &mut dyn Storage, - token_id: &str, - remove_from: Option<&Addr>, - add_to: Option<&Addr>, - amount: &Uint256, - token_info: &StoredTokenInfo, -) -> StdResult<()> { - // check whether token_id is an NFT => cannot mint. This should not be reachable in standard implementation, - // as the calling function would have checked that enable_mint == false, which needs to be true for NFTs. - // This is a redundancy check to make sure - if token_info.token_config.flatten().is_nft && remove_from.is_none() { - return Err(StdError::generic_err( - "NFTs can only be minted once using `mint_token_ids`", - )); - } - - // check whether token_id is an NFT => assert!(amount == 1). - if token_info.token_config.flatten().is_nft && amount != Uint256::from(1_u64) { - return Err(StdError::generic_err("NFT amount must == 1")); - } - - // remove balance - if let Some(from) = remove_from { - let from_existing_bal = balances_r(storage, token_id).load(to_binary(&from)?.as_slice())?; - let from_new_amount_op = from_existing_bal.checked_sub(*amount); - if from_new_amount_op.is_err() { - return Err(StdError::generic_err("insufficient funds")); - } - balances_w(storage, token_id) - .save(to_binary(&from)?.as_slice(), &from_new_amount_op.unwrap())?; - - // NOTE: if nft, the ownership history remains in storage. Any existing viewing permissions of last owner - // will remain too - } - - // add balance - if let Some(to) = add_to { - let to_existing_bal_op = - balances_r(storage, token_id).may_load(to_binary(&to)?.as_slice())?; - let to_existing_bal = match to_existing_bal_op { - Some(i) => i, - // if `to` address has no balance yet, initiate zero balance - None => Uint256::from(0_u64), - }; - - // println!("to_existing_bal {:?}", to_existing_bal); - - // println!("amount {:?}", amount); - - let to_new_amount_op = to_existing_bal.checked_add(*amount); - if to_new_amount_op.is_err() { - return Err(StdError::generic_err( - "recipient will become too rich. Total tokens exceeds 2^128", - )); - } - - // save new balances - balances_w(storage, token_id) - .save(to_binary(&to)?.as_slice(), &to_new_amount_op.unwrap())?; - - // if is_nft == true, store new owner of NFT - if token_info.token_config.flatten().is_nft { - append_new_owner(storage, &token_info.token_id, to)?; - } - } - - // may change total token supply - match (remove_from, add_to) { - (None, None) => (), - (Some(_), Some(_)) => (), - (None, Some(_)) => { - let old_amount = tkn_tot_supply_r(storage).load(token_info.token_id.as_bytes())?; - let new_amount_op = old_amount.checked_add(*amount); - let new_amount = match new_amount_op { - Ok(i) => i, - Err(_e) => { - return Err(StdError::generic_err( - "total supply exceeds max allowed of 2^128", - )); - } - }; - tkn_tot_supply_w(storage).save(token_info.token_id.as_bytes(), &new_amount)?; - } - (Some(_), None) => { - let old_amount = tkn_tot_supply_r(storage).load(token_info.token_id.as_bytes())?; - let new_amount_op = old_amount.checked_sub(*amount); - let new_amount = match new_amount_op { - Ok(i) => i, - Err(_e) => return Err(StdError::generic_err("total supply drops below zero")), - }; - tkn_tot_supply_w(storage).save(token_info.token_id.as_bytes(), &new_amount)?; - } - } - - Ok(()) -} - -#[allow(clippy::too_many_arguments)] -fn try_add_receiver_api_callback( - storage: &dyn Storage, - messages: &mut Vec, - recipient: Addr, - recipient_code_hash: Option, - msg: Option, - sender: Addr, - token_id: String, - from: Addr, - amount: Uint256, - memo: Option, -) -> StdResult<()> { - if let Some(receiver_hash) = recipient_code_hash { - let receiver_msg = Snip1155ReceiveMsg::new(sender, token_id, from, amount, memo, msg); - let callback_msg = receiver_msg.into_cosmos_msg(receiver_hash, recipient)?; - - messages.push(callback_msg); - return Ok(()); - } - - let receiver_hash = get_receiver_hash(storage, &recipient); - if let Some(receiver_hash) = receiver_hash { - let receiver_hash = receiver_hash?; - let receiver_msg = Snip1155ReceiveMsg::new(sender, token_id, from, amount, memo, msg); - let callback_msg = receiver_msg.into_cosmos_msg(receiver_hash, recipient)?; - - messages.push(callback_msg); - } - - Ok(()) -} - use shade_protocol::liquidity_book::lb_token::{QueryAnswer, QueryMsg, QueryWithPermit}; ///////////////////////////////////////////////////////////////////////////////// // Queries @@ -1468,7 +303,7 @@ use shade_protocol::liquidity_book::lb_token::{QueryAnswer, QueryMsg, QueryWithP pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { match msg { QueryMsg::IdTotalBalance { id } => query_id_balance(deps, id), - QueryMsg::TokenContractInfo {} => query_contract_info(deps), + QueryMsg::ContractInfo {} => query_contract_info(deps), QueryMsg::TokenIdPublicInfo { token_id } => query_token_id_public_info(deps, token_id), QueryMsg::RegisteredCodeHash { contract } => query_registered_code_hash(deps, contract), QueryMsg::WithPermit { permit, query } => permit_queries(deps, permit, query), @@ -1572,7 +407,7 @@ fn viewing_keys_queries(deps: Deps, msg: QueryMsg) -> StdResult { QueryMsg::TokenIdPrivateInfo { address, token_id, .. } => query_token_id_private_info(deps, &address, token_id), - QueryMsg::TokenContractInfo {} + QueryMsg::ContractInfo {} | QueryMsg::TokenIdPublicInfo { .. } | QueryMsg::RegisteredCodeHash { .. } | QueryMsg::WithPermit { .. } => { @@ -1586,277 +421,3 @@ fn viewing_keys_queries(deps: Deps, msg: QueryMsg) -> StdResult { msg: "Wrong viewing key for this address or viewing key not set".to_string(), }) } - -fn query_contract_info(deps: Deps) -> StdResult { - let contr_conf = contr_conf_r(deps.storage).load()?; - let response = QueryAnswer::TokenContractInfo { - admin: contr_conf.admin, - curators: contr_conf.curators, - all_token_ids: contr_conf.token_id_list, - }; - to_binary(&response) -} - -fn query_id_balance(deps: Deps, token_id: String) -> StdResult { - let id_balance_raw = tkn_tot_supply_r(deps.storage).load(token_id.as_bytes()); - - let mut id_balance = Uint256::zero(); - - if id_balance_raw.is_ok() { - id_balance = id_balance_raw?; - } - - let response = QueryAnswer::IdTotalBalance { amount: id_balance }; - to_binary(&response) -} - -fn query_token_id_public_info(deps: Deps, token_id: String) -> StdResult { - let tkn_info_op = tkn_info_r(deps.storage).may_load(token_id.as_bytes())?; - match tkn_info_op { - None => Err(StdError::generic_err(format!( - "token_id {} does not exist", - token_id - ))), - Some(mut tkn_info) => { - // add owner if owner_is_public == true - let owner: Option = if tkn_info.token_config.flatten().owner_is_public { - may_get_current_owner(deps.storage, &token_id)? - } else { - None - }; - - // add public supply if public_total_supply == true - let total_supply: Option = - if tkn_info.token_config.flatten().public_total_supply { - Some(tkn_tot_supply_r(deps.storage).load(token_id.as_bytes())?) - } else { - None - }; - - // private_metadata always == None for public info query - tkn_info.private_metadata = None; - let response = QueryAnswer::TokenIdPublicInfo { - token_id_info: tkn_info, - total_supply, - owner, - }; - to_binary(&response) - } - } -} - -fn query_token_id_private_info(deps: Deps, viewer: &Addr, token_id: String) -> StdResult { - let tkn_info_op = tkn_info_r(deps.storage).may_load(token_id.as_bytes())?; - if tkn_info_op.is_none() { - return Err(StdError::generic_err(format!( - "token_id {} does not exist", - token_id - ))); - } - - let mut tkn_info = tkn_info_op.unwrap(); - - // add owner if owner_is_public == true - let owner: Option = if tkn_info.token_config.flatten().owner_is_public { - may_get_current_owner(deps.storage, &token_id)? - } else { - None - }; - - // private metadata is viewable if viewer owns at least 1 token - let viewer_owns_some_tokens = - match balances_r(deps.storage, &token_id).may_load(to_binary(&viewer)?.as_slice())? { - None => false, - Some(i) if i == Uint256::from(0_u64) => false, - Some(i) if i > Uint256::from(0_u64) => true, - Some(_) => unreachable!("should not reach here"), - }; - - // If request owns at least 1 token, can view `private_metadata`. Otherwise check viewership permissions (permission only applicable to nfts, as - // fungible tokens have no current `owner`). - if !viewer_owns_some_tokens { - let permission_op = may_load_any_permission( - deps.storage, - // if no owner, = "" ie blank string => will not have any permission - owner.as_ref().unwrap_or(&Addr::unchecked("".to_string())), - &token_id, - viewer, - )?; - match permission_op { - None => { - return Err(StdError::generic_err( - "you do have have permission to view private token info", - )); - } - Some(perm) => { - let block: BlockInfo = - blockinfo_r(deps.storage) - .may_load()? - .unwrap_or_else(|| BlockInfo { - height: 1, - time: Timestamp::from_seconds(1), - chain_id: "not used".to_string(), - random: None, - }); - if !perm.check_view_pr_metadata_perm(&block) { - tkn_info.private_metadata = None - }; - } - } - } - - // add public supply if public_total_supply == true - let total_supply: Option = if tkn_info.token_config.flatten().public_total_supply { - Some(tkn_tot_supply_r(deps.storage).load(token_id.as_bytes())?) - } else { - None - }; - - let response = QueryAnswer::TokenIdPrivateInfo { - token_id_info: tkn_info, - total_supply, - owner, - }; - to_binary(&response) -} - -fn query_registered_code_hash(deps: Deps, contract: Addr) -> StdResult { - let may_hash_res = get_receiver_hash(deps.storage, &contract); - let response: QueryAnswer = match may_hash_res { - Some(hash_res) => QueryAnswer::RegisteredCodeHash { - code_hash: Some(hash_res?), - }, - None => QueryAnswer::RegisteredCodeHash { code_hash: None }, - }; - - to_binary(&response) -} - -fn query_balance(deps: Deps, owner: &Addr, viewer: &Addr, token_id: String) -> StdResult { - if owner != viewer { - let permission_op = may_load_any_permission(deps.storage, owner, &token_id, viewer)?; - match permission_op { - None => { - return Err(StdError::generic_err( - "you do have have permission to view balance", - )); - } - Some(perm) => { - let block: BlockInfo = - blockinfo_r(deps.storage) - .may_load()? - .unwrap_or_else(|| BlockInfo { - height: 1, - time: Timestamp::from_seconds(1), - chain_id: "not used".to_string(), - random: None, - }); - if !perm.check_view_balance_perm(&block) { - return Err(StdError::generic_err( - "you do have have permission to view balance", - )); - } else { - } - } - } - } - - let owner_canon = deps.api.addr_canonicalize(owner.as_str())?; - let amount_op = balances_r(deps.storage, &token_id) - .may_load(to_binary(&deps.api.addr_humanize(&owner_canon)?)?.as_slice())?; - let amount = match amount_op { - Some(i) => i, - None => Uint256::from(0_u64), - }; - let response = QueryAnswer::Balance { amount }; - to_binary(&response) -} - -fn query_all_balances( - deps: Deps, - account: &Addr, - tx_history_page: Option, - tx_history_page_size: Option, -) -> StdResult { - let address = deps.api.addr_canonicalize(account.as_str())?; - let (txs, _total) = get_txs( - deps.api, - deps.storage, - &address, - tx_history_page.unwrap_or(0u32), - tx_history_page_size.unwrap_or(u32::MAX), - )?; - - // create unique list of token_ids that owner has potentially owned. BtreeSet used (rather than Hashset) to have a predictable order - let token_ids = txs - .into_iter() - .map(|tx| tx.token_id) - .collect::>(); - - // get balances for this list of token_ids, only if balance == Some(_), ie: user has had some balance before - let mut balances: Vec = vec![]; - for token_id in token_ids.into_iter() { - let amount = balances_r(deps.storage, &token_id) - .may_load(to_binary(account).unwrap().as_slice()) - .unwrap(); - if let Some(i) = amount { - // LB change - if !i.is_zero() { - balances.push(OwnerBalance { - token_id, - amount: i, - }) - } - } - } - - let response = QueryAnswer::AllBalances(balances); - to_binary(&response) -} - -fn query_transactions(deps: Deps, account: &Addr, page: u32, page_size: u32) -> StdResult { - let address = deps.api.addr_canonicalize(account.as_str())?; - let (txs, total) = get_txs(deps.api, deps.storage, &address, page, page_size)?; - - let response = QueryAnswer::TransactionHistory { txs, total }; - to_binary(&response) -} - -fn query_permission( - deps: Deps, - token_id: String, - owner: Addr, - allowed_addr: Addr, -) -> StdResult { - let permission = may_load_any_permission(deps.storage, &owner, &token_id, &allowed_addr)?; - - let response = QueryAnswer::Permission(permission); - to_binary(&response) -} - -fn query_all_permissions( - deps: Deps, - account: &Addr, - page: u32, - page_size: u32, -) -> StdResult { - let (permission_keys, total) = - list_owner_permission_keys(deps.storage, account, page, page_size)?; - let mut permissions: Vec = vec![]; - let mut valid_pkeys: Vec = vec![]; - for pkey in permission_keys { - let permission = - may_load_any_permission(deps.storage, account, &pkey.token_id, &pkey.allowed_addr)?; - if let Some(i) = permission { - permissions.push(i); - valid_pkeys.push(pkey); - }; - } - - let response = QueryAnswer::AllPermissions { - permission_keys: valid_pkeys, - permissions, - total, - }; - to_binary(&response) -} diff --git a/contracts/liquidity_book/lb_token/src/execute.rs b/contracts/liquidity_book/lb_token/src/execute.rs index ab57a1f9..c5d0e043 100755 --- a/contracts/liquidity_book/lb_token/src/execute.rs +++ b/contracts/liquidity_book/lb_token/src/execute.rs @@ -16,31 +16,22 @@ use cosmwasm_std::{ Uint256, }; -use crate::{ - receiver::Snip1155ReceiveMsg, - state::{ - balances_r, - balances_w, - blockinfo_w, - contr_conf_r, - contr_conf_w, - get_receiver_hash, - permissions::{may_load_any_permission, new_permission, update_permission}, - set_receiver_hash, - tkn_info_r, - tkn_info_w, - tkn_tot_supply_r, - tkn_tot_supply_w, - txhistory::{ - append_new_owner, - may_get_current_owner, - store_burn, - store_mint, - store_transfer, - }, - PREFIX_REVOKED_PERMITS, - RESPONSE_BLOCK_SIZE, - }, +use crate::state::{ + balances_r, + balances_w, + blockinfo_w, + contr_conf_r, + contr_conf_w, + get_receiver_hash, + permissions::{may_load_any_permission, new_permission, update_permission}, + set_receiver_hash, + tkn_info_r, + tkn_info_w, + tkn_tot_supply_r, + tkn_tot_supply_w, + txhistory::{append_new_owner, may_get_current_owner, store_burn, store_mint, store_transfer}, + PREFIX_REVOKED_PERMITS, + RESPONSE_BLOCK_SIZE, }; use shade_protocol::{ @@ -48,12 +39,6 @@ use shade_protocol::{ expiration::Expiration, metadata::Metadata, permissions::Permission, - s_toolkit::{ - crypto::sha_256, - permit::RevokedPermits, - utils::space_pad, - viewing_key::{ViewingKey, ViewingKeyStore}, - }, state_structs::{ ContractConfig, CurateTokenId, @@ -69,214 +54,21 @@ use shade_protocol::{ InstantiateMsg, ResponseStatus::Success, SendAction, + Snip1155ReceiveMsg, TransferAction, }, + s_toolkit::{ + crypto::sha_256, + permit::RevokedPermits, + utils::space_pad, + viewing_key::{ViewingKey, ViewingKeyStore}, + }, }; ///////////////////////////////////////////////////////////////////////////////// // Init ///////////////////////////////////////////////////////////////////////////////// -/// instantiation function. See [InitMsg](crate::msg::InitMsg) for the api -#[entry_point] -pub fn instantiate( - mut deps: DepsMut, - env: Env, - info: MessageInfo, - msg: InstantiateMsg, -) -> StdResult { - // save latest block info. not necessary once we migrate to CosmWasm v1.0 - blockinfo_w(deps.storage).save(&env.block)?; - - // set admin. If `has_admin` == None => no admin. - // If `has_admin` == true && msg.admin == None => admin is the instantiator - let admin = match msg.has_admin { - false => None, - true => match msg.admin { - Some(i) => Some(i), - None => Some(info.sender.clone()), - }, - }; - - // create contract config -- save later - let prng_seed_hashed = sha_256(msg.entropy.as_bytes()); - let prng_seed = prng_seed_hashed.to_vec(); - // let prng_seed = sha_256( - // general_purpose::STANDARD - // .encode(msg.entropy.as_str()) - // .as_bytes(), - // ); - - ViewingKey::set_seed(deps.storage, &prng_seed); - - let mut config = ContractConfig { - admin, - curators: msg.curators, - token_id_list: vec![], - tx_cnt: 0u64, - prng_seed: prng_seed.to_vec(), - contract_address: env.contract.address.clone(), - lb_pair_info: msg.lb_pair_info, - }; - - // // set initial balances - for initial_token in msg.initial_tokens { - exec_curate_token_id(&mut deps, &env, &info, &mut config, initial_token, None)?; - } - - // save contract config -- where tx_cnt would have increased post initial balances - contr_conf_w(deps.storage).save(&config)?; - - Ok(Response::default()) -} - -///////////////////////////////////////////////////////////////////////////////// -// Handles -///////////////////////////////////////////////////////////////////////////////// - -/// contract handle function. See [ExecuteMsg](crate::msg::ExecuteMsg) and -/// [ExecuteAnswer](crate::msg::ExecuteAnswer) for the api -#[entry_point] -pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> StdResult { - // allows approx latest block info to be available for queries. Important to enforce - // allowance expiration. Remove this after BlockInfo becomes available to queries - blockinfo_w(deps.storage).save(&env.block)?; - - let response = match msg { - // ExecuteMsg::CurateTokenIds { - // initial_tokens, - // memo, - // padding: _, - // } => try_curate_token_ids(deps, env, info, initial_tokens, memo), - ExecuteMsg::MintTokens { - mint_tokens, - memo, - padding: _, - } => try_mint_tokens(deps, env, info, mint_tokens, memo), - ExecuteMsg::BurnTokens { - burn_tokens, - memo, - padding: _, - } => try_burn_tokens(deps, env, info, burn_tokens, memo), - ExecuteMsg::ChangeMetadata { - token_id, - public_metadata, - private_metadata, - } => try_change_metadata( - deps, - env, - info, - token_id, - *public_metadata, - *private_metadata, - ), - ExecuteMsg::Transfer { - token_id, - from, - recipient, - amount, - memo, - padding: _, - } => try_transfer(deps, env, info, token_id, from, recipient, amount, memo), - ExecuteMsg::BatchTransfer { - actions, - padding: _, - } => try_batch_transfer(deps, env, info, actions), - ExecuteMsg::Send { - token_id, - from, - recipient, - recipient_code_hash, - amount, - msg, - memo, - padding: _, - } => try_send(deps, env, info, SendAction { - token_id, - from, - recipient, - recipient_code_hash, - amount, - msg, - memo, - }), - ExecuteMsg::BatchSend { - actions, - padding: _, - } => try_batch_send(deps, env, info, actions), - ExecuteMsg::GivePermission { - allowed_address, - token_id, - view_balance, - view_balance_expiry, - view_private_metadata, - view_private_metadata_expiry, - transfer, - transfer_expiry, - padding: _, - } => try_give_permission( - deps, - env, - info, - allowed_address, - token_id, - view_balance, - view_balance_expiry, - view_private_metadata, - view_private_metadata_expiry, - transfer, - transfer_expiry, - ), - ExecuteMsg::RevokePermission { - token_id, - owner, - allowed_address, - padding: _, - } => try_revoke_permission(deps, env, info, token_id, owner, allowed_address), - ExecuteMsg::CreateViewingKey { - entropy, - padding: _, - } => try_create_viewing_key(deps, env, info, entropy), - ExecuteMsg::SetViewingKey { key, padding: _ } => try_set_viewing_key(deps, env, info, key), - ExecuteMsg::RevokePermit { - permit_name, - padding: _, - } => try_revoke_permit(deps, env, info, permit_name), - ExecuteMsg::AddCurators { - add_curators, - padding: _, - } => try_add_curators(deps, env, info, add_curators), - ExecuteMsg::RemoveCurators { - remove_curators, - padding: _, - } => try_remove_curators(deps, env, info, remove_curators), - // ExecuteMsg::AddMinters { - // token_id, - // add_minters, - // padding: _, - // } => try_add_minters(deps, env, info, token_id, add_minters), - // ExecuteMsg::RemoveMinters { - // token_id, - // remove_minters, - // padding: _, - // } => try_remove_minters(deps, env, info, token_id, remove_minters), - ExecuteMsg::ChangeAdmin { - new_admin, - padding: _, - } => try_change_admin(deps, env, info, new_admin), - ExecuteMsg::RemoveAdmin { - current_admin, - contract_address, - padding: _, - } => try_remove_admin(deps, env, info, current_admin, contract_address), - ExecuteMsg::RegisterReceive { - code_hash, - padding: _, - } => try_register_receive(deps, env, info, code_hash), - }; - pad_response(response) -} - -// fn try_curate_token_ids( +// pub fn try_curate_token_ids( // mut deps: DepsMut, // env: Env, // info: MessageInfo, @@ -308,7 +100,7 @@ pub fn execute(deps: DepsMut, env: Env, info: MessageInfo, msg: ExecuteMsg) -> S // ) // } -fn try_mint_tokens( +pub fn try_mint_tokens( mut deps: DepsMut, env: Env, info: MessageInfo, @@ -316,7 +108,6 @@ fn try_mint_tokens( memo: Option, ) -> StdResult { let mut config = contr_conf_r(deps.storage).load()?; - verify_curator(&config, &info)?; // mint tokens @@ -328,8 +119,8 @@ fn try_mint_tokens( let curate_token = CurateTokenId { token_info: TokenInfoMsg { token_id: mint_token.token_id.clone(), - name: format!("{}-{}", &config.lb_pair_info.name, mint_token.token_id), - symbol: format!("{}", &config.lb_pair_info.symbol), + name: format!("LP-{}", &config.lb_pair_info.symbol), + symbol: format!("LP-{}", &config.lb_pair_info.symbol), token_config: TknConfig::Fungible { minters: Vec::new(), // No need for minter curator will be the minter decimals: config.lb_pair_info.decimals, @@ -356,22 +147,22 @@ fn try_mint_tokens( } // check if enable_mint == true - if !token_info_op - .clone() - .unwrap() - .token_config - .flatten() - .enable_mint - { - return Err(StdError::generic_err( - "minting is not enabled for this token_id", - )); - } + // if !token_info_op + // .clone() + // .unwrap() + // .token_config + // .flatten() + // .enable_mint + // { + // return Err(StdError::generic_err( + // "minting is not enabled for this token_id", + // )); + // } // check if sender is a minter // verify_minter(token_info_op.as_ref().unwrap(), &info)?; - // add balances + for add_balance in mint_token.balances { exec_change_balance( deps.storage, @@ -402,7 +193,7 @@ fn try_mint_tokens( } // in the base specifications, this function can be performed by token owner only -fn try_burn_tokens( +pub fn try_burn_tokens( deps: DepsMut, env: Env, info: MessageInfo, @@ -411,7 +202,6 @@ fn try_burn_tokens( ) -> StdResult { let mut config = contr_conf_r(deps.storage).load()?; verify_curator(&config, &info)?; - // burn tokens for burn_token in burn_tokens { let token_info_op = tkn_info_r(deps.storage).may_load(burn_token.token_id.as_bytes())?; @@ -433,12 +223,12 @@ fn try_burn_tokens( // remove balances for rem_balance in burn_token.balances { // in base specification, burner MUST be the owner - if rem_balance.address != info.sender { - return Err(StdError::generic_err(format!( - "you do not have permission to burn {} tokens from address {}", - rem_balance.amount, rem_balance.address - ))); - } + // if rem_balance.address != info.sender { + // return Err(StdError::generic_err(format!( + // "you do not have permission to burn {} tokens from address {}", + // rem_balance.amount, rem_balance.address + // ))); + // } exec_change_balance( deps.storage, @@ -469,7 +259,7 @@ fn try_burn_tokens( Ok(Response::new().set_data(to_binary(&ExecuteAnswer::BurnTokens { status: Success })?)) } -fn try_change_metadata( +pub fn try_change_metadata( deps: DepsMut, _env: Env, info: MessageInfo, @@ -529,7 +319,7 @@ fn try_change_metadata( } #[allow(clippy::too_many_arguments)] -fn try_transfer( +pub fn try_transfer( mut deps: DepsMut, env: Env, info: MessageInfo, @@ -546,7 +336,7 @@ fn try_transfer( Ok(Response::new().set_data(to_binary(&ExecuteAnswer::Transfer { status: Success })?)) } -fn try_batch_transfer( +pub fn try_batch_transfer( mut deps: DepsMut, env: Env, info: MessageInfo, @@ -574,7 +364,7 @@ fn try_batch_transfer( ) } -fn try_send( +pub fn try_send( mut deps: DepsMut, env: Env, info: MessageInfo, @@ -590,7 +380,7 @@ fn try_send( Ok(res) } -fn try_batch_send( +pub fn try_batch_send( mut deps: DepsMut, env: Env, info: MessageInfo, @@ -611,7 +401,7 @@ fn try_batch_send( /// does not check if `token_id` exists so attacker cannot easily figure out if /// a `token_id` has been created #[allow(clippy::too_many_arguments)] -fn try_give_permission( +pub fn try_give_permission( deps: DepsMut, _env: Env, info: MessageInfo, @@ -716,7 +506,7 @@ fn try_give_permission( /// entry in storage, because it is unecessarily in most use cases, but will require also removing /// owner-specific PermissionKeys, which introduces complexity and increases gas cost. /// If permission does not exist, message will return an error. -fn try_revoke_permission( +pub fn try_revoke_permission( deps: DepsMut, _env: Env, info: MessageInfo, @@ -746,7 +536,7 @@ fn try_revoke_permission( ) } -fn try_create_viewing_key( +pub fn try_create_viewing_key( deps: DepsMut, env: Env, info: MessageInfo, @@ -763,7 +553,7 @@ fn try_create_viewing_key( Ok(Response::new().set_data(to_binary(&ExecuteAnswer::CreateViewingKey { key })?)) } -fn try_set_viewing_key( +pub fn try_set_viewing_key( deps: DepsMut, _env: Env, info: MessageInfo, @@ -777,7 +567,7 @@ fn try_set_viewing_key( ) } -fn try_revoke_permit( +pub fn try_revoke_permit( deps: DepsMut, _env: Env, info: MessageInfo, @@ -793,51 +583,51 @@ fn try_revoke_permit( Ok(Response::new().set_data(to_binary(&ExecuteAnswer::RevokePermit { status: Success })?)) } -fn try_add_curators( - deps: DepsMut, - _env: Env, - info: MessageInfo, - add_curators: Vec, -) -> StdResult { - let mut config = contr_conf_r(deps.storage).load()?; +// pub fn try_add_curators( +// deps: DepsMut, +// _env: Env, +// info: MessageInfo, +// add_curators: Vec, +// ) -> StdResult { +// let mut config = contr_conf_r(deps.storage).load()?; - // verify admin - verify_admin(&config, &info)?; +// // verify admin +// verify_admin(&config, &info)?; - // add curators - for curator in add_curators { - config.curators.push(curator); - } - contr_conf_w(deps.storage).save(&config)?; +// // add curators +// for curator in add_curators { +// config.curators.push(curator); +// } +// contr_conf_w(deps.storage).save(&config)?; - Ok(Response::new().set_data(to_binary(&ExecuteAnswer::AddCurators { status: Success })?)) -} +// Ok(Response::new().set_data(to_binary(&ExecuteAnswer::AddCurators { status: Success })?)) +// } -fn try_remove_curators( - deps: DepsMut, - _env: Env, - info: MessageInfo, - remove_curators: Vec, -) -> StdResult { - let mut config = contr_conf_r(deps.storage).load()?; +// pub fn try_remove_curators( +// deps: DepsMut, +// _env: Env, +// info: MessageInfo, +// remove_curators: Vec, +// ) -> StdResult { +// let mut config = contr_conf_r(deps.storage).load()?; - // verify admin - verify_admin(&config, &info)?; +// // verify admin +// verify_admin(&config, &info)?; - // remove curators - for curator in remove_curators { - config.curators.retain(|x| x != &curator); - } - contr_conf_w(deps.storage).save(&config)?; +// // remove curators +// for curator in remove_curators { +// config.curators.retain(|x| x != &curator); +// } +// contr_conf_w(deps.storage).save(&config)?; - Ok( - Response::new().set_data(to_binary(&ExecuteAnswer::RemoveCurators { - status: Success, - })?), - ) -} +// Ok( +// Response::new().set_data(to_binary(&ExecuteAnswer::RemoveCurators { +// status: Success, +// })?), +// ) +// } -// fn try_add_minters( +// pub fn try_add_minters( // deps: DepsMut, // _env: Env, // info: MessageInfo, @@ -878,7 +668,7 @@ fn try_remove_curators( // Ok(Response::new().set_data(to_binary(&ExecuteAnswer::AddMinters { status: Success })?)) // } -// fn try_remove_minters( +// pub fn try_remove_minters( // deps: DepsMut, // _env: Env, // info: MessageInfo, @@ -923,7 +713,9 @@ fn try_remove_curators( // ) // } -fn try_change_admin( +//No need to change admin cause we're using admin_auth + +pub fn try_change_admin( deps: DepsMut, _env: Env, info: MessageInfo, @@ -941,7 +733,7 @@ fn try_change_admin( Ok(Response::new().set_data(to_binary(&ExecuteAnswer::ChangeAdmin { status: Success })?)) } -fn try_remove_admin( +pub fn try_remove_admin( deps: DepsMut, _env: Env, info: MessageInfo, @@ -968,7 +760,7 @@ fn try_remove_admin( Ok(Response::new().set_data(to_binary(&ExecuteAnswer::RemoveAdmin { status: Success })?)) } -fn try_register_receive( +pub fn try_register_receive( deps: DepsMut, _env: Env, info: MessageInfo, @@ -986,7 +778,7 @@ fn try_register_receive( // Private functions ///////////////////////////////////////////////////////////////////////////////// -fn pad_response(response: StdResult) -> StdResult { +pub fn pad_response(response: StdResult) -> StdResult { response.map(|mut response| { response.data = response.data.map(|mut data| { space_pad(&mut data.0, RESPONSE_BLOCK_SIZE); @@ -1003,10 +795,12 @@ fn is_valid_name(name: &str) -> bool { fn is_valid_symbol(symbol: &str) -> bool { let len = symbol.len(); - let len_is_valid = (3..=6).contains(&len); + (3..=30).contains(&len) + // let len = symbol.len(); + // let len_is_valid = (3..=50).contains(&len); - // len_is_valid && symbol.bytes().all(|byte| (b'A'..=b'Z').contains(&byte)) - len_is_valid && symbol.bytes().all(|byte| byte.is_ascii_uppercase()) + // // len_is_valid && symbol.bytes().all(|byte| (b'A'..=b'Z').contains(&byte)) + // len_is_valid && symbol.bytes().all(|byte| byte.is_ascii_uppercase()) } fn verify_admin(contract_config: &ContractConfig, info: &MessageInfo) -> StdResult<()> { @@ -1064,7 +858,7 @@ fn verify_minter(token_info: &StoredTokenInfo, info: &MessageInfo) -> StdResult< } /// checks if `token_id` is available (ie: not yet created), then creates new `token_id` and initial balances -fn exec_curate_token_id( +pub fn exec_curate_token_id( deps: &mut DepsMut, env: &Env, info: &MessageInfo, @@ -1103,11 +897,13 @@ fn exec_curate_token_id( "Name is not in the expected format (3-30 UTF-8 bytes)", )); } + if !is_valid_symbol(&initial_token.token_info.symbol) { return Err(StdError::generic_err( "Ticker symbol is not in expected format [A-Z]{3,6}", )); } + if initial_token.token_info.token_config.flatten().decimals > 18 { return Err(StdError::generic_err("Decimals must not exceed 18")); } @@ -1244,11 +1040,10 @@ fn impl_transfer( } // success, so need to reduce allowance Some(mut perm) if perm.trfer_allowance_perm >= amount => { - let new_allowance = Uint256::from( - perm.trfer_allowance_perm - .checked_sub(amount) - .expect("something strange happened"), - ); + let new_allowance = perm + .trfer_allowance_perm + .checked_sub(amount) + .expect("something strange happened"); perm.trfer_allowance_perm = new_allowance; update_permission(deps.storage, from, token_id, &info.sender, &perm)?; } @@ -1338,10 +1133,8 @@ fn exec_change_balance( if from_new_amount_op.is_err() { return Err(StdError::generic_err("insufficient funds")); } - balances_w(storage, token_id).save( - to_binary(&from)?.as_slice(), - &Uint256::from(from_new_amount_op.unwrap()), - )?; + balances_w(storage, token_id) + .save(to_binary(&from)?.as_slice(), &from_new_amount_op.unwrap())?; // NOTE: if nft, the ownership history remains in storage. Any existing viewing permissions of last owner // will remain too @@ -1356,6 +1149,11 @@ fn exec_change_balance( // if `to` address has no balance yet, initiate zero balance None => Uint256::from(0_u64), }; + + // println!("to_existing_bal {:?}", to_existing_bal); + + // println!("amount {:?}", amount); + let to_new_amount_op = to_existing_bal.checked_add(*amount); if to_new_amount_op.is_err() { return Err(StdError::generic_err( @@ -1364,10 +1162,8 @@ fn exec_change_balance( } // save new balances - balances_w(storage, token_id).save( - to_binary(&to)?.as_slice(), - &Uint256::from(to_new_amount_op.unwrap()), - )?; + balances_w(storage, token_id) + .save(to_binary(&to)?.as_slice(), &to_new_amount_op.unwrap())?; // if is_nft == true, store new owner of NFT if token_info.token_config.flatten().is_nft { @@ -1383,7 +1179,7 @@ fn exec_change_balance( let old_amount = tkn_tot_supply_r(storage).load(token_info.token_id.as_bytes())?; let new_amount_op = old_amount.checked_add(*amount); let new_amount = match new_amount_op { - Ok(i) => Uint256::from(i), + Ok(i) => i, Err(_e) => { return Err(StdError::generic_err( "total supply exceeds max allowed of 2^128", @@ -1396,7 +1192,7 @@ fn exec_change_balance( let old_amount = tkn_tot_supply_r(storage).load(token_info.token_id.as_bytes())?; let new_amount_op = old_amount.checked_sub(*amount); let new_amount = match new_amount_op { - Ok(i) => Uint256::from(i), + Ok(i) => i, Err(_e) => return Err(StdError::generic_err("total supply drops below zero")), }; tkn_tot_supply_w(storage).save(token_info.token_id.as_bytes(), &new_amount)?; diff --git a/contracts/liquidity_book/lb_token/src/lib.rs b/contracts/liquidity_book/lb_token/src/lib.rs index 991c9a07..52419abf 100755 --- a/contracts/liquidity_book/lb_token/src/lib.rs +++ b/contracts/liquidity_book/lb_token/src/lib.rs @@ -1,4 +1,7 @@ pub mod contract; +pub mod execute; +pub mod query; + pub mod state; #[cfg(test)] pub mod unittest; diff --git a/contracts/liquidity_book/lb_token/src/query.rs b/contracts/liquidity_book/lb_token/src/query.rs index ce1d2aed..229cd863 100755 --- a/contracts/liquidity_book/lb_token/src/query.rs +++ b/contracts/liquidity_book/lb_token/src/query.rs @@ -41,141 +41,30 @@ use shade_protocol::{ ///////////////////////////////////////////////////////////////////////////////// // Queries ///////////////////////////////////////////////////////////////////////////////// - -/// contract query function. See [QueryMsg](crate::msg::QueryMsg) and -/// [QueryAnswer](crate::msg::QueryAnswer) for the api -#[entry_point] -pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { - match msg { - QueryMsg::ContractInfo {} => query_contract_info(deps), - QueryMsg::TokenIdPublicInfo { token_id } => query_token_id_public_info(deps, token_id), - QueryMsg::RegisteredCodeHash { contract } => query_registered_code_hash(deps, contract), - QueryMsg::WithPermit { permit, query } => permit_queries(deps, permit, query), - QueryMsg::Balance { .. } - | QueryMsg::AllBalances { .. } - | QueryMsg::TransactionHistory { .. } - | QueryMsg::Permission { .. } - | QueryMsg::AllPermissions { .. } - | QueryMsg::TokenIdPrivateInfo { .. } => viewing_keys_queries(deps, msg), - } +pub fn query_contract_info(deps: Deps) -> StdResult { + let contr_conf = contr_conf_r(deps.storage).load()?; + let response = QueryAnswer::TokenContractInfo { + admin: contr_conf.admin, + curators: contr_conf.curators, + all_token_ids: contr_conf.token_id_list, + }; + to_binary(&response) } -fn permit_queries(deps: Deps, permit: Permit, query: QueryWithPermit) -> Result { - // Validate permit content - let contract_address = contr_conf_r(deps.storage).load()?.contract_address; +pub fn query_id_balance(deps: Deps, token_id: String) -> StdResult { + let id_balance_raw = tkn_tot_supply_r(deps.storage).load(token_id.as_bytes()); - let account_str = validate( - deps, - PREFIX_REVOKED_PERMITS, - &permit, - contract_address.to_string(), - None, - )?; - let account = deps.api.addr_validate(&account_str)?; + let mut id_balance = Uint256::zero(); - if !permit.check_permission(&TokenPermissions::Owner) { - return Err(StdError::generic_err(format!( - "`Owner` permit required for SNIP1155 permit queries, got permissions {:?}", - permit.params.permissions - ))); + if id_balance_raw.is_ok() { + id_balance = id_balance_raw?; } - // Permit validated! We can now execute the query. - match query { - QueryWithPermit::Balance { owner, token_id } => { - query_balance(deps, &owner, &account, token_id) - } - QueryWithPermit::AllBalances { - tx_history_page, - tx_history_page_size, - } => query_all_balances(deps, &account, tx_history_page, tx_history_page_size), - QueryWithPermit::TransactionHistory { page, page_size } => { - query_transactions(deps, &account, page.unwrap_or(0), page_size) - } - QueryWithPermit::Permission { - owner, - allowed_address, - token_id, - } => { - if account != owner.as_str() && account != allowed_address.as_str() { - return Err(StdError::generic_err(format!( - "Cannot query permission. Requires permit for either owner {:?} or viewer||spender {:?}, got permit for {:?}", - owner.as_str(), - allowed_address.as_str(), - account.as_str() - ))); - } - - query_permission(deps, token_id, owner, allowed_address) - } - QueryWithPermit::AllPermissions { page, page_size } => { - query_all_permissions(deps, &account, page.unwrap_or(0), page_size) - } - QueryWithPermit::TokenIdPrivateInfo { token_id } => { - query_token_id_private_info(deps, &account, token_id) - } - } -} - -fn viewing_keys_queries(deps: Deps, msg: QueryMsg) -> StdResult { - let (addresses, key) = msg.get_validation_params()?; - - for address in addresses { - let result = ViewingKey::check(deps.storage, address.as_str(), key.as_str()); - if result.is_ok() { - return match msg { - QueryMsg::Balance { - owner, - viewer, - token_id, - .. - } => query_balance(deps, &owner, &viewer, token_id), - QueryMsg::AllBalances { - tx_history_page, - tx_history_page_size, - .. - } => query_all_balances(deps, address, tx_history_page, tx_history_page_size), - QueryMsg::TransactionHistory { - page, page_size, .. - } => query_transactions(deps, address, page.unwrap_or(0), page_size), - QueryMsg::Permission { - owner, - allowed_address, - token_id, - .. - } => query_permission(deps, token_id, owner, allowed_address), - QueryMsg::AllPermissions { - page, page_size, .. - } => query_all_permissions(deps, address, page.unwrap_or(0), page_size), - QueryMsg::TokenIdPrivateInfo { - address, token_id, .. - } => query_token_id_private_info(deps, &address, token_id), - QueryMsg::ContractInfo {} - | QueryMsg::TokenIdPublicInfo { .. } - | QueryMsg::RegisteredCodeHash { .. } - | QueryMsg::WithPermit { .. } => { - unreachable!("This query type does not require viewing key authentication") - } - }; - } - } - - to_binary(&QueryAnswer::ViewingKeyError { - msg: "Wrong viewing key for this address or viewing key not set".to_string(), - }) -} - -fn query_contract_info(deps: Deps) -> StdResult { - let contr_conf = contr_conf_r(deps.storage).load()?; - let response = QueryAnswer::ContractInfo { - admin: contr_conf.admin, - curators: contr_conf.curators, - all_token_ids: contr_conf.token_id_list, - }; + let response = QueryAnswer::IdTotalBalance { amount: id_balance }; to_binary(&response) } -fn query_token_id_public_info(deps: Deps, token_id: String) -> StdResult { +pub fn query_token_id_public_info(deps: Deps, token_id: String) -> StdResult { let tkn_info_op = tkn_info_r(deps.storage).may_load(token_id.as_bytes())?; match tkn_info_op { None => Err(StdError::generic_err(format!( @@ -210,7 +99,11 @@ fn query_token_id_public_info(deps: Deps, token_id: String) -> StdResult } } -fn query_token_id_private_info(deps: Deps, viewer: &Addr, token_id: String) -> StdResult { +pub fn query_token_id_private_info( + deps: Deps, + viewer: &Addr, + token_id: String, +) -> StdResult { let tkn_info_op = tkn_info_r(deps.storage).may_load(token_id.as_bytes())?; if tkn_info_op.is_none() { return Err(StdError::generic_err(format!( @@ -285,7 +178,7 @@ fn query_token_id_private_info(deps: Deps, viewer: &Addr, token_id: String) -> S to_binary(&response) } -fn query_registered_code_hash(deps: Deps, contract: Addr) -> StdResult { +pub fn query_registered_code_hash(deps: Deps, contract: Addr) -> StdResult { let may_hash_res = get_receiver_hash(deps.storage, &contract); let response: QueryAnswer = match may_hash_res { Some(hash_res) => QueryAnswer::RegisteredCodeHash { @@ -297,7 +190,12 @@ fn query_registered_code_hash(deps: Deps, contract: Addr) -> StdResult { to_binary(&response) } -fn query_balance(deps: Deps, owner: &Addr, viewer: &Addr, token_id: String) -> StdResult { +pub fn query_balance( + deps: Deps, + owner: &Addr, + viewer: &Addr, + token_id: String, +) -> StdResult { if owner != viewer { let permission_op = may_load_any_permission(deps.storage, owner, &token_id, viewer)?; match permission_op { @@ -337,7 +235,7 @@ fn query_balance(deps: Deps, owner: &Addr, viewer: &Addr, token_id: String) -> S to_binary(&response) } -fn query_all_balances( +pub fn query_all_balances( deps: Deps, account: &Addr, tx_history_page: Option, @@ -365,10 +263,13 @@ fn query_all_balances( .may_load(to_binary(account).unwrap().as_slice()) .unwrap(); if let Some(i) = amount { - balances.push(OwnerBalance { - token_id, - amount: i, - }) + // LB change + if !i.is_zero() { + balances.push(OwnerBalance { + token_id, + amount: i, + }) + } } } @@ -376,7 +277,12 @@ fn query_all_balances( to_binary(&response) } -fn query_transactions(deps: Deps, account: &Addr, page: u32, page_size: u32) -> StdResult { +pub fn query_transactions( + deps: Deps, + account: &Addr, + page: u32, + page_size: u32, +) -> StdResult { let address = deps.api.addr_canonicalize(account.as_str())?; let (txs, total) = get_txs(deps.api, deps.storage, &address, page, page_size)?; @@ -384,7 +290,7 @@ fn query_transactions(deps: Deps, account: &Addr, page: u32, page_size: u32) -> to_binary(&response) } -fn query_permission( +pub fn query_permission( deps: Deps, token_id: String, owner: Addr, @@ -396,7 +302,7 @@ fn query_permission( to_binary(&response) } -fn query_all_permissions( +pub fn query_all_permissions( deps: Deps, account: &Addr, page: u32, diff --git a/contracts/liquidity_book/lb_token/src/unittest/querytests.rs b/contracts/liquidity_book/lb_token/src/unittest/querytests.rs index a98ec10f..aa208a4e 100755 --- a/contracts/liquidity_book/lb_token/src/unittest/querytests.rs +++ b/contracts/liquidity_book/lb_token/src/unittest/querytests.rs @@ -26,7 +26,7 @@ fn test_q_init() -> StdResult<()> { assert_ne!(init_result.unwrap(), Response::default()); // check contract info - let msg = QueryMsg::TokenContractInfo {}; + let msg = QueryMsg::ContractInfo {}; let q_result = query(deps.as_ref(), mock_env(), msg); let q_answer = from_binary::(&q_result?)?; match q_answer { diff --git a/contracts/liquidity_book/tests/Cargo.toml b/contracts/liquidity_book/tests/Cargo.toml index c8863d87..e6688c6f 100644 --- a/contracts/liquidity_book/tests/Cargo.toml +++ b/contracts/liquidity_book/tests/Cargo.toml @@ -36,7 +36,9 @@ shade-multi-test = { path = "../../../packages/multi_test", features = [ "lb_staking", "snip20", "router", - "lb_factory" + "lb_factory", + "query_auth", + ] } lb_pair ={ path = "../lb_pair"} serial_test = "2.0.0" diff --git a/contracts/liquidity_book/tests/src/multitests/lb_factory.rs b/contracts/liquidity_book/tests/src/multitests/lb_factory.rs index 9f510359..a0844097 100644 --- a/contracts/liquidity_book/tests/src/multitests/lb_factory.rs +++ b/contracts/liquidity_book/tests/src/multitests/lb_factory.rs @@ -24,7 +24,7 @@ use crate::multitests::test_helper::*; #[serial] pub fn test_setup() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, _deployed_contracts) = setup(None, None)?; + let (mut app, lb_factory, _deployed_contracts, _, _) = setup(None, None)?; //query fee recipient let fee_recipient = lb_factory::query_fee_recipient(&mut app, &lb_factory.clone().into())?; @@ -41,7 +41,7 @@ pub fn test_setup() -> Result<(), anyhow::Error> { #[serial] pub fn test_set_lb_pair_implementation() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, _deployed_contracts) = setup(None, None)?; + let (mut app, lb_factory, _deployed_contracts, _, _) = setup(None, None)?; let lb_pair_stored_code = app.store_code(LbPair::default().contract()); lb_factory::set_lb_pair_implementation( @@ -61,7 +61,7 @@ pub fn test_set_lb_pair_implementation() -> Result<(), anyhow::Error> { #[serial] pub fn test_revert_set_lb_pair_implementation() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, _deployed_contracts) = setup(None, None)?; + let (mut app, lb_factory, _deployed_contracts, _, _) = setup(None, None)?; let lb_pair_stored_code = app.store_code(LbPair::default().contract()); lb_factory::set_lb_pair_implementation( @@ -94,7 +94,7 @@ pub fn test_revert_set_lb_pair_implementation() -> Result<(), anyhow::Error> { #[serial] pub fn test_set_lb_token_implementation() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, _deployed_contracts) = setup(None, None)?; + let (mut app, lb_factory, _deployed_contracts, _, _) = setup(None, None)?; let lb_token_stored_code = app.store_code(LbToken::default().contract()); lb_factory::set_lb_token_implementation( &mut app, @@ -113,7 +113,7 @@ pub fn test_set_lb_token_implementation() -> Result<(), anyhow::Error> { #[serial] pub fn test_create_lb_pair() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; + let (mut app, lb_factory, deployed_contracts, _, _) = setup(None, None)?; // 3. Create an LBPair. @@ -202,7 +202,7 @@ pub fn test_create_lb_pair() -> Result<(), anyhow::Error> { #[serial] pub fn test_create_lb_pair_factory_unlocked() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; + let (mut app, lb_factory, deployed_contracts, _, _) = setup(None, None)?; let shd = extract_contract_info(&deployed_contracts, SHADE)?; let sscrt = extract_contract_info(&deployed_contracts, SSCRT)?; @@ -294,7 +294,8 @@ pub fn test_create_lb_pair_factory_unlocked() -> Result<(), anyhow::Error> { #[serial] fn test_revert_create_lb_pair() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; + let (mut app, lb_factory, deployed_contracts, admin_contract, query_contract) = + setup(None, None)?; let shd = extract_contract_info(&deployed_contracts, SHADE)?; let sscrt = extract_contract_info(&deployed_contracts, SSCRT)?; @@ -322,13 +323,12 @@ fn test_revert_create_lb_pair() -> Result<(), anyhow::Error> { )) ); - let admin_contract = init_admin_auth(&mut app, &addrs.admin()); - let new_lb_factory = lb_factory::init( &mut app, addrs.admin().as_str(), addrs.admin(), admin_contract.into(), + query_contract.into(), // query_auth should be here, addrs.admin(), )?; @@ -517,92 +517,92 @@ fn test_revert_create_lb_pair() -> Result<(), anyhow::Error> { Ok(()) } -#[test] -#[serial] -fn test_fuzz_set_preset() -> Result<(), anyhow::Error> { - let addrs = init_addrs(); - let (mut app, lb_factory, _deployed_contracts) = setup(None, None)?; - let mut bin_step: u16 = generate_random(0, u16::MAX); - let base_factor: u16 = generate_random(0, u16::MAX); - let mut filter_period: u16 = generate_random(0, u16::MAX); - let mut decay_period: u16 = generate_random(0, u16::MAX); - let mut reduction_factor: u16 = generate_random(0, u16::MAX); - let mut variable_fee_control: u32 = generate_random(0, U24::MAX); - let mut protocol_share: u16 = generate_random(0, u16::MAX); - let mut max_volatility_accumulator: u32 = generate_random(0, U24::MAX); - let is_open: bool = generate_random(0, 1) == 0; - - let min_bin_step = lb_factory::query_min_bin_step(&mut app, &lb_factory.clone().into())?; - - // Bounds checking for each parameter - bin_step = bound(bin_step, min_bin_step as u16, u16::MAX); - filter_period = bound(filter_period, 0, MASK_UINT12.as_u16() - 1); - decay_period = bound(decay_period, filter_period + 1, MASK_UINT12.as_u16()); - reduction_factor = bound(reduction_factor, 0u16, BASIS_POINT_MAX); - variable_fee_control = bound(variable_fee_control, 0u32, BASIS_POINT_MAX as u32); - protocol_share = bound(protocol_share, 0, 2_500); - max_volatility_accumulator = bound(max_volatility_accumulator, 0, MASK_UINT20.as_u32()); - - lb_factory::set_pair_preset( - &mut app, - addrs.admin().as_str(), - &lb_factory.clone().into(), - bin_step, - base_factor, - filter_period, - decay_period, - reduction_factor, - variable_fee_control, - protocol_share, - max_volatility_accumulator, - is_open, - DEFAULT_TOTAL_REWARD_BINS, - Some(RewardsDistributionAlgorithm::TimeBasedRewards), - 1, - 100, - None, - )?; - - // Additional assertions and verifications - let all_bin_steps = lb_factory::query_all_bin_steps(&mut app, &lb_factory.clone().into())?; - - if bin_step != DEFAULT_BIN_STEP { - assert_eq!(all_bin_steps.len(), 2); - assert_eq!(all_bin_steps[0], DEFAULT_BIN_STEP); - assert_eq!(all_bin_steps[1], bin_step); - } else { - assert_eq!(all_bin_steps.len(), 1); - assert_eq!(all_bin_steps[0], bin_step); - } - - let PresetResponse { - base_factor: base_factor_view, - filter_period: filter_period_view, - decay_period: decay_period_view, - reduction_factor: reduction_factor_view, - variable_fee_control: variable_fee_control_view, - protocol_share: protocol_share_view, - max_volatility_accumulator: max_volatility_accumulator_view, - is_open: is_open_view, - } = lb_factory::query_preset(&mut app, &lb_factory.into(), bin_step)?; - - assert_eq!(base_factor, base_factor_view); - assert_eq!(filter_period, filter_period_view); - assert_eq!(decay_period, decay_period_view); - assert_eq!(reduction_factor, reduction_factor_view); - assert_eq!(variable_fee_control, variable_fee_control_view); - assert_eq!(protocol_share, protocol_share_view); - assert_eq!(max_volatility_accumulator, max_volatility_accumulator_view); - assert_eq!(is_open, is_open_view); - - Ok(()) -} +// #[test] +// #[serial] +// fn test_fuzz_set_preset() -> Result<(), anyhow::Error> { +// let addrs = init_addrs(); +// let (mut app, lb_factory, _deployed_contracts, _, _) = setup(None, None)?; +// let mut bin_step: u16 = generate_random(0, u16::MAX); +// let base_factor: u16 = generate_random(0, u16::MAX); +// let mut filter_period: u16 = generate_random(0, u16::MAX); +// let mut decay_period: u16 = generate_random(0, u16::MAX); +// let mut reduction_factor: u16 = generate_random(0, u16::MAX); +// let mut variable_fee_control: u32 = generate_random(0, U24::MAX); +// let mut protocol_share: u16 = generate_random(0, u16::MAX); +// let mut max_volatility_accumulator: u32 = generate_random(0, U24::MAX); +// let is_open: bool = generate_random(0, 1) == 0; + +// let min_bin_step = lb_factory::query_min_bin_step(&mut app, &lb_factory.clone().into())?; + +// // Bounds checking for each parameter +// bin_step = bound(bin_step, min_bin_step as u16, u16::MAX); +// filter_period = bound(filter_period, 0, MASK_UINT12.as_u16() - 1); +// decay_period = bound(decay_period, filter_period + 1, MASK_UINT12.as_u16()); +// reduction_factor = bound(reduction_factor, 0u16, BASIS_POINT_MAX); +// variable_fee_control = bound(variable_fee_control, 0u32, BASIS_POINT_MAX as u32); +// protocol_share = bound(protocol_share, 0, 2_500); +// max_volatility_accumulator = bound(max_volatility_accumulator, 0, MASK_UINT20.as_u32()); + +// lb_factory::set_pair_preset( +// &mut app, +// addrs.admin().as_str(), +// &lb_factory.clone().into(), +// bin_step, +// base_factor, +// filter_period, +// decay_period, +// reduction_factor, +// variable_fee_control, +// protocol_share, +// max_volatility_accumulator, +// is_open, +// DEFAULT_TOTAL_REWARD_BINS, +// Some(RewardsDistributionAlgorithm::TimeBasedRewards), +// 1, +// 100, +// None, +// )?; + +// // Additional assertions and verifications +// let all_bin_steps = lb_factory::query_all_bin_steps(&mut app, &lb_factory.clone().into())?; + +// if bin_step != DEFAULT_BIN_STEP { +// assert_eq!(all_bin_steps.len(), 2); +// assert_eq!(all_bin_steps[0], DEFAULT_BIN_STEP); +// assert_eq!(all_bin_steps[1], bin_step); +// } else { +// assert_eq!(all_bin_steps.len(), 1); +// assert_eq!(all_bin_steps[0], bin_step); +// } + +// let PresetResponse { +// base_factor: base_factor_view, +// filter_period: filter_period_view, +// decay_period: decay_period_view, +// reduction_factor: reduction_factor_view, +// variable_fee_control: variable_fee_control_view, +// protocol_share: protocol_share_view, +// max_volatility_accumulator: max_volatility_accumulator_view, +// is_open: is_open_view, +// } = lb_factory::query_preset(&mut app, &lb_factory.into(), bin_step)?; + +// assert_eq!(base_factor, base_factor_view); +// assert_eq!(filter_period, filter_period_view); +// assert_eq!(decay_period, decay_period_view); +// assert_eq!(reduction_factor, reduction_factor_view); +// assert_eq!(variable_fee_control, variable_fee_control_view); +// assert_eq!(protocol_share, protocol_share_view); +// assert_eq!(max_volatility_accumulator, max_volatility_accumulator_view); +// assert_eq!(is_open, is_open_view); + +// Ok(()) +// } #[test] #[serial] fn test_remove_preset() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, _deployed_contracts) = setup(None, None)?; + let (mut app, lb_factory, _deployed_contracts, _, _) = setup(None, None)?; // Set presets lb_factory::set_pair_preset( @@ -694,7 +694,7 @@ fn test_remove_preset() -> Result<(), anyhow::Error> { #[serial] pub fn test_set_fees_parameters_on_pair() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; + let (mut app, lb_factory, deployed_contracts, _, _) = setup(None, None)?; let sscrt = extract_contract_info(&deployed_contracts, SSCRT)?; let shd = extract_contract_info(&deployed_contracts, SHADE)?; @@ -817,7 +817,7 @@ pub fn test_set_fees_parameters_on_pair() -> Result<(), anyhow::Error> { #[serial] pub fn test_set_fee_recipient() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, _deployed_contracts) = setup(None, None)?; + let (mut app, lb_factory, _deployed_contracts, _, _) = setup(None, None)?; lb_factory::set_fee_recipient( &mut app, addrs.admin().as_str(), @@ -858,7 +858,7 @@ pub fn test_set_fee_recipient() -> Result<(), anyhow::Error> { #[serial] pub fn test_fuzz_open_presets() -> Result<(), anyhow::Error> { let addrs = init_addrs(); // Initialize addresses - let (mut app, lb_factory, _deployed_contracts) = setup(None, None)?; // Setup + let (mut app, lb_factory, _deployed_contracts, _, _) = setup(None, None)?; // Setup let min_bin_step = lb_factory::query_min_bin_step(&mut app, &lb_factory.clone().into())?; let max_bin_step = u16::MAX; @@ -962,7 +962,7 @@ pub fn test_fuzz_open_presets() -> Result<(), anyhow::Error> { #[serial] pub fn test_add_quote_asset() -> Result<(), anyhow::Error> { let addrs = init_addrs(); // Initialize addresses - let (mut app, lb_factory, mut deployed_contracts) = setup(None, None)?; // Setup + let (mut app, lb_factory, mut deployed_contracts, _, _) = setup(None, None)?; // Setup let num_quote_assets_before = lb_factory::query_number_of_quote_assets(&mut app, &lb_factory.clone().into())?; @@ -1065,7 +1065,7 @@ pub fn test_add_quote_asset() -> Result<(), anyhow::Error> { #[serial] pub fn test_remove_quote_asset() -> Result<(), anyhow::Error> { let addrs = init_addrs(); // Initialize addresses - let (mut app, lb_factory, mut deployed_contracts) = setup(None, None)?; // Setup + let (mut app, lb_factory, mut deployed_contracts, _, _) = setup(None, None)?; // Setup //SSCRT and SHD already added as quote asset let num_quote_assets_before = @@ -1156,7 +1156,7 @@ pub fn test_remove_quote_asset() -> Result<(), anyhow::Error> { #[serial] pub fn test_force_decay() -> Result<(), anyhow::Error> { let addrs = init_addrs(); // Initialize addresses - let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; // Setup + let (mut app, lb_factory, deployed_contracts, _, _) = setup(None, None)?; // Setup let sscrt_info = extract_contract_info(&deployed_contracts, SSCRT)?; let sscrt = token_type_snip20_generator(&sscrt_info)?; @@ -1206,7 +1206,7 @@ pub fn test_force_decay() -> Result<(), anyhow::Error> { #[serial] pub fn test_get_all_lb_pair() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; + let (mut app, lb_factory, deployed_contracts, _, _) = setup(None, None)?; // 3. Create an LBPair. diff --git a/contracts/liquidity_book/tests/src/multitests/lb_pair_fees.rs b/contracts/liquidity_book/tests/src/multitests/lb_pair_fees.rs index 117de10b..27718d23 100644 --- a/contracts/liquidity_book/tests/src/multitests/lb_pair_fees.rs +++ b/contracts/liquidity_book/tests/src/multitests/lb_pair_fees.rs @@ -41,7 +41,7 @@ pub fn lb_pair_setup() -> Result< anyhow::Error, > { let addrs = init_addrs(); - let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; + let (mut app, lb_factory, deployed_contracts, _, _) = setup(None, None)?; let silk = extract_contract_info(&deployed_contracts, SILK)?; let shade = extract_contract_info(&deployed_contracts, SHADE)?; @@ -2184,7 +2184,8 @@ pub fn test_revert_total_fee_exceeded() -> Result<(), anyhow::Error> { let addrs = init_addrs(); let bin_step = Uint128::from(generate_random(1u16, u16::MAX)); - let (mut app, lb_factory, deployed_contracts) = setup(Some(bin_step.u128() as u16), None)?; + let (mut app, lb_factory, deployed_contracts, _, _) = + setup(Some(bin_step.u128() as u16), None)?; let silk = extract_contract_info(&deployed_contracts, SILK)?; let shade = extract_contract_info(&deployed_contracts, SHADE)?; let token_x = token_type_snip20_generator(&shade)?; @@ -2241,7 +2242,7 @@ pub fn test_revert_total_fee_exceeded() -> Result<(), anyhow::Error> { #[test] pub fn test_fuzz_swap_in_x_and_y_btc_silk() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; + let (mut app, lb_factory, deployed_contracts, _, _) = setup(None, None)?; let btc = extract_contract_info(&deployed_contracts, SBTC)?; let silk = extract_contract_info(&deployed_contracts, SILK)?; @@ -2466,7 +2467,7 @@ pub fn test_fuzz_swap_in_x_and_y_btc_silk() -> Result<(), anyhow::Error> { #[test] pub fn test_fuzz_calculate_volume_based_rewards() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, deployed_contracts) = + let (mut app, lb_factory, deployed_contracts, _, _) = setup(None, Some(RewardsDistributionAlgorithm::VolumeBasedRewards))?; let btc = extract_contract_info(&deployed_contracts, SBTC)?; @@ -2608,7 +2609,7 @@ pub fn test_fuzz_calculate_volume_based_rewards() -> Result<(), anyhow::Error> { #[test] pub fn test_calculate_volume_based_rewards() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, deployed_contracts) = + let (mut app, lb_factory, deployed_contracts, _, _) = setup(None, Some(RewardsDistributionAlgorithm::VolumeBasedRewards))?; let btc = extract_contract_info(&deployed_contracts, SBTC)?; @@ -2755,7 +2756,7 @@ pub fn test_calculate_volume_based_rewards() -> Result<(), anyhow::Error> { #[test] pub fn test_calculate_time_based_rewards() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; + let (mut app, lb_factory, deployed_contracts, _, _) = setup(None, None)?; let sscrt = extract_contract_info(&deployed_contracts, SSCRT)?; let silk = extract_contract_info(&deployed_contracts, SILK)?; @@ -2918,7 +2919,7 @@ pub fn test_calculate_time_based_rewards() -> Result<(), anyhow::Error> { #[test] pub fn test_fuzz_calculate_time_based_rewards() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; + let (mut app, lb_factory, deployed_contracts, _, _) = setup(None, None)?; let sscrt = extract_contract_info(&deployed_contracts, SSCRT)?; let silk = extract_contract_info(&deployed_contracts, SILK)?; @@ -3079,7 +3080,7 @@ pub fn test_fuzz_calculate_time_based_rewards() -> Result<(), anyhow::Error> { #[test] pub fn test_reset_rewards_config() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; + let (mut app, lb_factory, deployed_contracts, _, _) = setup(None, None)?; let sscrt = extract_contract_info(&deployed_contracts, SSCRT)?; let silk = extract_contract_info(&deployed_contracts, SILK)?; @@ -3202,6 +3203,8 @@ pub fn test_reset_rewards_config() -> Result<(), anyhow::Error> { assert_eq!(active_id, ACTIVE_ID - 1); + roll_time(&mut app, Some(100)); + lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; let _distribution = lb_pair::query_rewards_distribution(&app, &lb_pair.info.contract, None)?; diff --git a/contracts/liquidity_book/tests/src/multitests/lb_pair_initial_state.rs b/contracts/liquidity_book/tests/src/multitests/lb_pair_initial_state.rs index 24ebf907..d4abacfc 100644 --- a/contracts/liquidity_book/tests/src/multitests/lb_pair_initial_state.rs +++ b/contracts/liquidity_book/tests/src/multitests/lb_pair_initial_state.rs @@ -13,7 +13,7 @@ use std::str::FromStr; pub fn lb_pair_setup() -> Result<(App, ContractInfo, DeployedContracts, LBPairInformation), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; + let (mut app, lb_factory, deployed_contracts, _, _) = setup(None, None)?; let shd = extract_contract_info(&deployed_contracts, SHADE)?; let sscrt = extract_contract_info(&deployed_contracts, SSCRT)?; diff --git a/contracts/liquidity_book/tests/src/multitests/lb_pair_liquidity.rs b/contracts/liquidity_book/tests/src/multitests/lb_pair_liquidity.rs index 00488650..3ff718ee 100644 --- a/contracts/liquidity_book/tests/src/multitests/lb_pair_liquidity.rs +++ b/contracts/liquidity_book/tests/src/multitests/lb_pair_liquidity.rs @@ -35,7 +35,7 @@ pub fn lb_pair_setup() -> Result< anyhow::Error, > { let addrs = init_addrs(); - let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; + let (mut app, lb_factory, deployed_contracts, _, _) = setup(None, None)?; let silk = extract_contract_info(&deployed_contracts, SILK)?; let shade = extract_contract_info(&deployed_contracts, SHADE)?; @@ -179,8 +179,6 @@ pub fn test_simple_mint() -> Result<(), anyhow::Error> { nb_bins_y, )?; - println!("liquidity parameters {:?}", liquidity_parameters); - lb_pair::add_liquidity( &mut app, addrs.batman().as_str(), diff --git a/contracts/liquidity_book/tests/src/multitests/lb_pair_queries.rs b/contracts/liquidity_book/tests/src/multitests/lb_pair_queries.rs index 4edcef3f..47a14664 100644 --- a/contracts/liquidity_book/tests/src/multitests/lb_pair_queries.rs +++ b/contracts/liquidity_book/tests/src/multitests/lb_pair_queries.rs @@ -31,7 +31,7 @@ pub fn lb_pair_setup() -> Result< anyhow::Error, > { let addrs = init_addrs(); - let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; + let (mut app, lb_factory, deployed_contracts, _, _) = setup(None, None)?; let silk = extract_contract_info(&deployed_contracts, SILK)?; let shade = extract_contract_info(&deployed_contracts, SHADE)?; diff --git a/contracts/liquidity_book/tests/src/multitests/lb_pair_swap.rs b/contracts/liquidity_book/tests/src/multitests/lb_pair_swap.rs index c0641906..e24b8fa0 100644 --- a/contracts/liquidity_book/tests/src/multitests/lb_pair_swap.rs +++ b/contracts/liquidity_book/tests/src/multitests/lb_pair_swap.rs @@ -38,7 +38,7 @@ pub fn lb_pair_setup() -> Result< anyhow::Error, > { let addrs = init_addrs(); - let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; + let (mut app, lb_factory, deployed_contracts, _, _) = setup(None, None)?; let silk = extract_contract_info(&deployed_contracts, SILK)?; let shade = extract_contract_info(&deployed_contracts, SHADE)?; @@ -600,7 +600,7 @@ pub fn test_revert_swap_out_of_liquidity() -> Result<(), anyhow::Error> { #[serial] fn test_revert_zero_bin_reserves() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; + let (mut app, lb_factory, deployed_contracts, _, _) = setup(None, None)?; let silk = extract_contract_info(&deployed_contracts, SILK)?; let shade = extract_contract_info(&deployed_contracts, SHADE)?; diff --git a/contracts/liquidity_book/tests/src/multitests/lb_pair_trivial.rs b/contracts/liquidity_book/tests/src/multitests/lb_pair_trivial.rs index 617e87fa..773491fd 100644 --- a/contracts/liquidity_book/tests/src/multitests/lb_pair_trivial.rs +++ b/contracts/liquidity_book/tests/src/multitests/lb_pair_trivial.rs @@ -36,7 +36,7 @@ pub fn lb_pair_setup() -> Result< anyhow::Error, > { let addrs = init_addrs(); - let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; + let (mut app, lb_factory, deployed_contracts, _, _) = setup(None, None)?; let silk = extract_contract_info(&deployed_contracts, SILK)?; let shade = extract_contract_info(&deployed_contracts, SHADE)?; diff --git a/contracts/liquidity_book/tests/src/multitests/lb_router_integration.rs b/contracts/liquidity_book/tests/src/multitests/lb_router_integration.rs index 895319e6..cabe86d2 100644 --- a/contracts/liquidity_book/tests/src/multitests/lb_router_integration.rs +++ b/contracts/liquidity_book/tests/src/multitests/lb_router_integration.rs @@ -24,7 +24,7 @@ const SWAP_AMOUNT: u128 = 1000; #[serial] pub fn router_integration() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, lb_factory, mut deployed_contracts) = setup(None, None)?; + let (mut app, lb_factory, mut deployed_contracts, _, _) = setup(None, None)?; let starting_number_of_pairs = lb_factory::query_number_of_lb_pairs(&mut app, &lb_factory.clone().into())?; diff --git a/contracts/liquidity_book/tests/src/multitests/lb_router_register_tokens.rs b/contracts/liquidity_book/tests/src/multitests/lb_router_register_tokens.rs index b0ef4e50..79891562 100644 --- a/contracts/liquidity_book/tests/src/multitests/lb_router_register_tokens.rs +++ b/contracts/liquidity_book/tests/src/multitests/lb_router_register_tokens.rs @@ -14,7 +14,7 @@ use shade_multi_test::interfaces::{ #[serial] pub fn router_registered_tokens() -> Result<(), anyhow::Error> { let addrs = init_addrs(); - let (mut app, _lb_factory, mut deployed_contracts) = setup(None, None)?; + let (mut app, _lb_factory, mut deployed_contracts, _, _) = setup(None, None)?; //intro app router::init(&mut app, addrs.admin().as_str(), &mut deployed_contracts)?; diff --git a/contracts/liquidity_book/tests/src/multitests/staking_contract.rs b/contracts/liquidity_book/tests/src/multitests/lb_staking.rs similarity index 78% rename from contracts/liquidity_book/tests/src/multitests/staking_contract.rs rename to contracts/liquidity_book/tests/src/multitests/lb_staking.rs index 1c51f759..0609460e 100644 --- a/contracts/liquidity_book/tests/src/multitests/staking_contract.rs +++ b/contracts/liquidity_book/tests/src/multitests/lb_staking.rs @@ -41,7 +41,7 @@ pub fn lb_pair_setup( anyhow::Error, > { let addrs = init_addrs(); - let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; + let (mut app, lb_factory, deployed_contracts, _, _) = setup(None, None)?; let silk = extract_contract_info(&deployed_contracts, SILK)?; let shade = extract_contract_info(&deployed_contracts, SHADE)?; @@ -246,6 +246,7 @@ pub fn fuzz_stake_simple() -> Result<(), anyhow::Error> { pub fn fuzz_stake_liquidity_with_time() -> Result<(), anyhow::Error> { let x_bins = generate_random(0, 50); let y_bins = generate_random(0, 50); + let addrs = init_addrs(); let (mut app, lb_factory, deployed_contracts, _lb_pair, _lb_token) = lb_pair_setup(Some(x_bins), Some(y_bins))?; @@ -268,9 +269,11 @@ pub fn fuzz_stake_liquidity_with_time() -> Result<(), anyhow::Error> { //deposit funds here let total_bins = get_total_bins(x_bins, y_bins) as u32; let mut ids = vec![]; - let mut liq = vec![]; + let mut liqs = vec![]; let mut actions = vec![]; + let mut balances = vec![]; + //Querying all the bins for i in 0..total_bins { let id = get_id(ACTIVE_ID, i, y_bins); @@ -284,8 +287,9 @@ pub fn fuzz_stake_liquidity_with_time() -> Result<(), anyhow::Error> { String::from("viewing_key"), id.to_string(), )?; + balances.push(balance); - liq.push(balance / Uint256::from_u128(2)); + liqs.push(balance / Uint256::from_u128(2)); actions.push(SendAction { token_id: id.to_string(), @@ -301,30 +305,18 @@ pub fn fuzz_stake_liquidity_with_time() -> Result<(), anyhow::Error> { }) } - lb_staking::set_viewing_key( - &mut app, - addrs.batman().as_str(), - &lb_staking, - "viewing_key".to_owned(), - )?; - lb_token::batch_send(&mut app, addrs.batman().as_str(), &lb_token, actions)?; let timestamp = Timestamp::from_seconds(app.block_info().time.seconds() + 50); app.set_time(timestamp); + let query_auth = generate_auth(addrs.batman().to_string()); + //Check the liquidity after half the time of duration - duration is 100 - let liquidity = lb_staking::query_liquidity( - &app, - &addrs.batman(), - String::from("viewing_key"), - &lb_staking, - ids.clone(), - None, - )?; + let liquidity = lb_staking::query_liquidity(&app, query_auth, &lb_staking, ids.clone(), None)?; - for (liq, bal) in liquidity.into_iter().zip(liq.clone()).into_iter() { + for (liq, bal) in liquidity.into_iter().zip(liqs.clone()).into_iter() { assert_eq!(liq.user_liquidity, bal); } @@ -344,8 +336,8 @@ pub fn fuzz_stake_liquidity_with_time() -> Result<(), anyhow::Error> { id.to_string(), )?; - liq[i as usize] = - liq[i as usize] + balance.multiply_ratio(Uint256::from(50u128), Uint256::from(100u128)); + liqs[i as usize] = liqs[i as usize] + + balance.multiply_ratio(Uint256::from(50u128), Uint256::from(100u128)); actions.push(SendAction { token_id: id.to_string(), @@ -362,20 +354,15 @@ pub fn fuzz_stake_liquidity_with_time() -> Result<(), anyhow::Error> { } lb_token::batch_send(&mut app, addrs.batman().as_str(), &lb_token, actions)?; + let query_auth = generate_auth(addrs.batman().to_string()); //Check the liquidity after half the time of duration - duration is 100 - let liquidity = lb_staking::query_liquidity( - &app, - &addrs.batman(), - String::from("viewing_key"), - &lb_staking, - ids.clone(), - None, - )?; + let liquidity = lb_staking::query_liquidity(&app, query_auth, &lb_staking, ids.clone(), None)?; - for (liq, bal) in liquidity.into_iter().zip(liq.clone()).into_iter() { + for (liq, bal) in liquidity.into_iter().zip(liqs.clone()).into_iter() { assert_eq!(liq.user_liquidity, bal); } + let mut liqs = vec![]; //trying to add liquidity after the end_time: let timestamp = Timestamp::from_seconds(app.block_info().time.seconds() + 51); @@ -406,6 +393,9 @@ pub fn fuzz_stake_liquidity_with_time() -> Result<(), anyhow::Error> { id.to_string(), )?; + balances[i as usize] += balance; + liqs.push(balance.multiply_ratio(Uint256::from(99u128), Uint256::from(100u128))); + actions.push(SendAction { token_id: id.to_string(), from: addrs.batman(), @@ -421,19 +411,23 @@ pub fn fuzz_stake_liquidity_with_time() -> Result<(), anyhow::Error> { } lb_token::batch_send(&mut app, addrs.batman().as_str(), &lb_token, actions)?; + let query_auth = generate_auth(addrs.batman().to_string()); - //Check the liquidity after full time of duration - duration is 100 liquidity won't change - let liquidity = lb_staking::query_liquidity( - &app, - &addrs.batman(), - String::from("viewing_key"), - &lb_staking, - ids.clone(), - None, - )?; - - for (liq, bal) in liquidity.into_iter().zip(liq.clone()).into_iter() { - assert_eq!(liq.user_liquidity, bal); + //Check the liquidity after half the time of duration - duration is 100 + let liquidity = lb_staking::query_liquidity(&app, query_auth, &lb_staking, ids.clone(), None)?; + + for (liq, bal) in liquidity + .clone() + .into_iter() + .zip(balances.clone()) + .into_iter() + { + let half_balance = bal.multiply_ratio(Uint256::from(1u128), Uint256::from(2u128)); + assert_eq!( + liq.user_liquidity, + half_balance + + half_balance.multiply_ratio(Uint256::from(99u128), Uint256::from(100u128)), + ); } Ok(()) @@ -622,22 +616,10 @@ pub fn fuzz_unstake_liquidity_with_time() -> Result<(), anyhow::Error> { balances.clone(), )?; - lb_staking::set_viewing_key( - &mut app, - addrs.batman().as_str(), - &lb_staking, - "viewing_key".to_owned(), - )?; + let query_auth = generate_auth(addrs.batman().to_string()); - //Check the liquidity after full time of duration - duration is 100 liquidity won't change - let liquidity = lb_staking::query_liquidity( - &app, - &addrs.batman(), - String::from("viewing_key"), - &lb_staking, - ids.clone(), - None, - )?; + //Check the liquidity after half the time of duration - duration is 100 + let liquidity = lb_staking::query_liquidity(&app, query_auth, &lb_staking, ids.clone(), None)?; for (liq, bal) in liquidity.into_iter().zip(balances.clone()).into_iter() { assert_approx_eq_abs( @@ -885,6 +867,8 @@ pub fn fuzz_claim_rewards() -> Result<(), anyhow::Error> { })?), )?; + roll_time(&mut app, Some(100)); + lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; lb_staking::claim_rewards(&mut app, addrs.batman().as_str(), &lb_staking)?; @@ -977,41 +961,57 @@ pub fn claim_rewards() -> Result<(), anyhow::Error> { end: 10, })?), )?; + roll_time(&mut app, Some(100)); lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //1 + roll_time(&mut app, Some(100)); lb_staking::claim_rewards(&mut app, addrs.batman().as_str(), &lb_staking)?; lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //2 + roll_time(&mut app, Some(100)); + lb_staking::claim_rewards(&mut app, addrs.batman().as_str(), &lb_staking)?; lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //3 + roll_time(&mut app, Some(100)); + lb_staking::claim_rewards(&mut app, addrs.batman().as_str(), &lb_staking)?; lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //4 + roll_time(&mut app, Some(100)); + lb_staking::claim_rewards(&mut app, addrs.batman().as_str(), &lb_staking)?; lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //5 + roll_time(&mut app, Some(100)); + lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //6 + roll_time(&mut app, Some(100)); + lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //7 + roll_time(&mut app, Some(100)); + lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //8 + roll_time(&mut app, Some(100)); + lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //9 + roll_time(&mut app, Some(100)); + lb_staking::claim_rewards(&mut app, addrs.batman().as_str(), &lb_staking)?; lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //10 + roll_time(&mut app, Some(100)); + lb_staking::claim_rewards(&mut app, addrs.batman().as_str(), &lb_staking)?; lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //11 + roll_time(&mut app, Some(100)); + lb_staking::claim_rewards(&mut app, addrs.batman().as_str(), &lb_staking)?; lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //12 -> 13 - lb_staking::claim_rewards(&mut app, addrs.batman().as_str(), &lb_staking)?; - lb_staking::set_viewing_key( - &mut app, - addrs.batman().as_str(), - &lb_staking, - "viewing_key".to_owned(), - )?; + lb_staking::claim_rewards(&mut app, addrs.batman().as_str(), &lb_staking)?; snip20::set_viewing_key_exec( &mut app, @@ -1080,7 +1080,9 @@ pub fn claim_rewards() -> Result<(), anyhow::Error> { )?; //unstake all: + roll_time(&mut app, Some(100)); lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //13->14 + lb_staking::claim_rewards(&mut app, addrs.batman().as_str(), &lb_staking)?; let balance = snip20::balance_query( @@ -1105,6 +1107,168 @@ pub fn claim_rewards() -> Result<(), anyhow::Error> { Ok(()) } +#[test] +pub fn end_epoch_by_stakers() -> Result<(), anyhow::Error> { + let addrs = init_addrs(); + let x_bins = 10; + let y_bins = 10; + let (mut app, _lb_factory, deployed_contracts, lb_pair, _lb_token) = + lb_pair_setup(Some(x_bins), Some(y_bins))?; + + let lb_token = lb_pair::query_lb_token(&mut app, &lb_pair.info.contract)?; + let lb_staking = lb_pair::query_staking_contract(&mut app, &lb_pair.info.contract)?; + + //deposit funds here + let total_bins = get_total_bins(x_bins, y_bins) as u32; + + let mut actions = vec![]; + //1) Stake the Snip-1155 tokens + for i in 0..total_bins { + let id = get_id(ACTIVE_ID, i, y_bins); + + let balance = lb_token::query_balance( + &app, + &lb_token, + addrs.batman(), + addrs.batman(), + String::from("viewing_key"), + id.to_string(), + )?; + + actions.push(SendAction { + token_id: id.to_string(), + from: addrs.batman(), + recipient: lb_staking.address.clone(), + recipient_code_hash: Some(lb_staking.code_hash.clone()), + amount: balance, + msg: Some(to_binary(&InvokeMsg::Stake { + from: Some(addrs.batman().to_string()), + padding: None, + })?), + memo: None, + }) + } + + lb_token::batch_send(&mut app, addrs.batman().as_str(), &lb_token, actions)?; + + let lb_staking_config = lb_staking::query_config(&app, &lb_staking)?; + assert_eq!(lb_staking_config.epoch_index, 1); + + let epoch_info = lb_staking::query_epoch_info(&app, &lb_staking, None)?; + assert_eq!(epoch_info.start_time, app.block_info().time.seconds()); + assert_eq!(epoch_info.end_time, app.block_info().time.seconds() + 100); + + //Rolling Time + duration, t=0 + duration + let timestamp = Timestamp::from_seconds( + app.block_info().time.seconds() + lb_staking_config.epoch_durations, + ); + app.set_time(timestamp); + + //2) End Epoch + lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //1 + + let lb_staking_config = lb_staking::query_config(&app, &lb_staking)?; + assert_eq!(lb_staking_config.epoch_index, 2); + + let epoch_info = lb_staking::query_epoch_info(&app, &lb_staking, None)?; + assert_eq!(epoch_info.start_time, app.block_info().time.seconds()); + assert_eq!(epoch_info.end_time, app.block_info().time.seconds() + 100); + + //3 Claim rewards + lb_staking::claim_rewards(&mut app, addrs.batman().as_str(), &lb_staking)?; + let query_auth = generate_auth(addrs.batman().to_string()); + + let staker_info = lb_staking::query_staker_info(&app, &lb_staking, query_auth.clone())?; + + assert_eq!(staker_info.last_claim_rewards_round, Some(1)); + + //Rolling Time + duration, t= duration*2 + let timestamp = Timestamp::from_seconds( + app.block_info().time.seconds() + lb_staking_config.epoch_durations, + ); + app.set_time(timestamp); + + //4)Stake after one round at t = 2*duration + mint_and_add_liquidity( + &mut app, + &deployed_contracts, + &addrs, + &lb_pair, + Some(x_bins), + Some(y_bins), + DEPOSIT_AMOUNT, + DEPOSIT_AMOUNT, + )?; + + let mut actions = vec![]; + // Stake the Snip-1155 tokens + for i in 0..total_bins { + let id = get_id(ACTIVE_ID, i, y_bins); + + let balance = lb_token::query_balance( + &app, + &lb_token, + addrs.batman(), + addrs.batman(), + String::from("viewing_key"), + id.to_string(), + )?; + + actions.push(SendAction { + token_id: id.to_string(), + from: addrs.batman(), + recipient: lb_staking.address.clone(), + recipient_code_hash: Some(lb_staking.code_hash.clone()), + amount: balance, + msg: Some(to_binary(&InvokeMsg::Stake { + from: Some(addrs.batman().to_string()), + padding: None, + })?), + memo: None, + }) + } + + lb_token::batch_send(&mut app, addrs.batman().as_str(), &lb_token, actions)?; + + //Check epoch id + let lb_staking_config = lb_staking::query_config(&app, &lb_staking)?; + assert_eq!(lb_staking_config.epoch_index, 3); + + let epoch_info = lb_staking::query_epoch_info(&app, &lb_staking, None)?; + assert_eq!(epoch_info.start_time, app.block_info().time.seconds()); + assert_eq!(epoch_info.end_time, app.block_info().time.seconds() + 100); + + lb_staking::claim_rewards(&mut app, addrs.batman().as_str(), &lb_staking)?; + let staker_info = lb_staking::query_staker_info(&app, &lb_staking, query_auth.clone())?; + assert_eq!(staker_info.last_claim_rewards_round, Some(1)); // No rewards distribution yet + + lb_staking::claim_rewards(&mut app, addrs.batman().as_str(), &lb_staking)?; + let staker_info = lb_staking::query_staker_info(&app, &lb_staking, query_auth.clone())?; + assert_eq!(staker_info.last_claim_rewards_round, Some(1)); // No rewards distribution yet + + let timestamp = Timestamp::from_seconds( + app.block_info().time.seconds() + lb_staking_config.epoch_durations, + ); + app.set_time(timestamp); + lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //2 + let lb_staking_config = lb_staking::query_config(&app, &lb_staking)?; + assert_eq!(lb_staking_config.epoch_index, 3); + + let timestamp = Timestamp::from_seconds( + app.block_info().time.seconds() + lb_staking_config.epoch_durations, + ); + app.set_time(timestamp); + lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //3 + + let lb_staking_config = lb_staking::query_config(&app, &lb_staking)?; + assert_eq!(lb_staking_config.epoch_index, 4); + + lb_staking::claim_rewards(&mut app, addrs.batman().as_str(), &lb_staking)?; + let staker_info = lb_staking::query_staker_info(&app, &lb_staking, query_auth.clone())?; + assert_eq!(staker_info.last_claim_rewards_round, Some(3)); // No rewards distribution yet + + Ok(()) +} #[test] pub fn claim_expired_rewards() -> Result<(), anyhow::Error> { @@ -1201,27 +1365,31 @@ pub fn claim_expired_rewards() -> Result<(), anyhow::Error> { end: 10, })?), )?; + // println!("CHECKING"); + roll_time(&mut app, Some(200)); lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //1 + roll_time(&mut app, Some(200)); lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //2 + roll_time(&mut app, Some(200)); lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //3 + roll_time(&mut app, Some(200)); lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //4 + roll_time(&mut app, Some(200)); lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //5 + roll_time(&mut app, Some(200)); lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //6 + roll_time(&mut app, Some(200)); lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //7 + roll_time(&mut app, Some(200)); lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //8 + roll_time(&mut app, Some(200)); lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //9 + roll_time(&mut app, Some(200)); lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //10 lb_staking::claim_rewards(&mut app, addrs.batman().as_str(), &lb_staking)?; - lb_staking::set_viewing_key( - &mut app, - addrs.batman().as_str(), - &lb_staking, - "viewing_key".to_owned(), - )?; - snip20::set_viewing_key_exec( &mut app, lb_staking.address.as_str(), @@ -1239,12 +1407,12 @@ pub fn claim_expired_rewards() -> Result<(), anyhow::Error> { )?; assert!(balance.u128() > 0); + let query_auth = generate_auth(addrs.batman().to_string()); let (claim_rewards_txns, count) = lb_staking::query_txn_history( &app, &lb_staking, - addrs.batman(), - "viewing_key".to_owned(), + query_auth, None, None, QueryTxnType::ClaimRewards, @@ -1378,51 +1546,65 @@ pub fn recover_expired_rewards() -> Result<(), anyhow::Error> { )?; assert_eq!(balance.u128(), 0u128); + roll_time(&mut app, Some(200)); lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //1 expired at 6 lb_staking::claim_rewards(&mut app, addrs.batman().as_str(), &lb_staking)?; + roll_time(&mut app, Some(200)); lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //2 expired at 7 lb_staking::claim_rewards(&mut app, addrs.batman().as_str(), &lb_staking)?; + roll_time(&mut app, Some(200)); lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //3 expired at 8 lb_staking::claim_rewards(&mut app, addrs.batman().as_str(), &lb_staking)?; + roll_time(&mut app, Some(200)); lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //4 expired at 9 lb_staking::claim_rewards(&mut app, addrs.batman().as_str(), &lb_staking)?; + roll_time(&mut app, Some(200)); lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //5 expired at 10 lb_staking::claim_rewards(&mut app, addrs.batman().as_str(), &lb_staking)?; + roll_time(&mut app, Some(200)); lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //6 expired at 11 lb_staking::claim_rewards(&mut app, addrs.batman().as_str(), &lb_staking)?; + roll_time(&mut app, Some(200)); lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //7 expired at 12 lb_staking::claim_rewards(&mut app, addrs.batman().as_str(), &lb_staking)?; + roll_time(&mut app, Some(200)); lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //8 expires at 13 lb_staking::claim_rewards(&mut app, addrs.batman().as_str(), &lb_staking)?; + roll_time(&mut app, Some(200)); lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //9 expires at 14 lb_staking::claim_rewards(&mut app, addrs.batman().as_str(), &lb_staking)?; + roll_time(&mut app, Some(200)); lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //10 expires at 15 lb_staking::claim_rewards(&mut app, addrs.batman().as_str(), &lb_staking)?; + roll_time(&mut app, Some(200)); lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //11 expires at 16 lb_staking::claim_rewards(&mut app, addrs.batman().as_str(), &lb_staking)?; + roll_time(&mut app, Some(200)); lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //12 expires at 17 lb_staking::claim_rewards(&mut app, addrs.batman().as_str(), &lb_staking)?; + roll_time(&mut app, Some(200)); lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //13 expires at 18 lb_staking::claim_rewards(&mut app, addrs.batman().as_str(), &lb_staking)?; + roll_time(&mut app, Some(200)); lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; //14 expires at 19 lb_staking::claim_rewards(&mut app, addrs.batman().as_str(), &lb_staking)?; //current round index -> 20 - lb_staking::recover_funds(&mut app, addrs.admin().as_str(), &lb_staking)?; + lb_staking::recover_expired_rewards(&mut app, addrs.admin().as_str(), &lb_staking)?; let balance = snip20::balance_query( &app, @@ -1437,6 +1619,184 @@ pub fn recover_expired_rewards() -> Result<(), anyhow::Error> { Ok(()) } +#[test] +pub fn recover_funds() -> Result<(), anyhow::Error> { + let addrs = init_addrs(); + let x_bins = 5; + let y_bins = 5; + let (mut app, _lb_factory, deployed_contracts, lb_pair, _lb_token) = + lb_pair_setup(Some(x_bins), Some(y_bins))?; + + let lb_token = lb_pair::query_lb_token(&mut app, &lb_pair.info.contract)?; + let lb_staking = lb_pair::query_staking_contract(&mut app, &lb_pair.info.contract)?; + + //deposit funds here + let total_bins = get_total_bins(x_bins, y_bins) as u32; + + let mut actions = vec![]; + let mut ids = vec![]; + let mut balances: Vec = Vec::new(); + //Querying all the bins + for i in 0..total_bins { + let id = get_id(ACTIVE_ID, i, y_bins); + ids.push(id); + + let balance = lb_token::query_balance( + &app, + &lb_token, + addrs.batman(), + addrs.batman(), + String::from("viewing_key"), + id.to_string(), + )?; + + balances.push(balance); + + actions.push(SendAction { + token_id: id.to_string(), + from: addrs.batman(), + recipient: lb_staking.address.clone(), + recipient_code_hash: Some(lb_staking.code_hash.clone()), + amount: balance, + msg: Some(to_binary(&InvokeMsg::Stake { + from: Some(addrs.batman().to_string()), + padding: None, + })?), + memo: None, + }) + } + + lb_token::batch_send(&mut app, addrs.batman().as_str(), &lb_token, actions)?; + + lb_staking::update_config( + &mut app, + addrs.admin().as_str(), + &lb_staking, + None, + None, + Some(200), + Some(5), + )?; + + //Added the reward tokens for next 10 rounds + let shade_token = extract_contract_info(&deployed_contracts, SHADE)?; + let shade_token_type = token_type_snip20_generator(&shade_token)?; + let silk_token = extract_contract_info(&deployed_contracts, SILK)?; + + let reward_tokens = vec![shade_token, silk_token]; + + lb_staking::register_reward_tokens( + &mut app, + addrs.admin().as_str(), + &lb_staking, + reward_tokens.clone(), + )?; + + //mint tokens + snip20::mint_exec( + &mut app, + addrs.admin().as_str(), + &deployed_contracts, + SHADE, + &vec![], + addrs.admin().to_string(), + Uint128::from(200u128), + )?; + + snip20::send_exec( + &mut app, + addrs.admin().as_str(), + &deployed_contracts, + SHADE, + lb_staking.address.to_string(), + Uint128::from(100u128), + Some(to_binary(&InvokeMsg::AddRewards { + start: None, + end: 10, + })?), + )?; + snip20::transfer_exec( + &mut app, + addrs.admin().as_str(), + &deployed_contracts, + SHADE, + lb_staking.address.to_string(), + Uint128::from(100u128), + )?; + + let balance = snip20::balance_query( + &app, + lb_staking.address.as_str(), + &deployed_contracts, + SHADE, + "SHADE_STAKING_VIEWING_KEY".to_string(), + )?; + + snip20::set_viewing_key_exec( + &mut app, + addrs.admin().as_str(), + &deployed_contracts, + SHADE, + "viewing_key".to_owned(), + )?; + + let prev_admin_balance = snip20::balance_query( + &app, + addrs.admin().as_str(), + &deployed_contracts, + SHADE, + "viewing_key".to_owned(), + )?; + + assert_eq!(balance.u128(), 200u128); + + lb_staking::recover_funds( + &mut app, + addrs.admin().as_str(), + &lb_staking, + shade_token_type.clone(), + Uint128::from(100u128), + addrs.admin().to_string(), + )?; + + let contract_balance = snip20::balance_query( + &app, + lb_staking.address.as_str(), + &deployed_contracts, + SHADE, + "SHADE_STAKING_VIEWING_KEY".to_string(), + )?; + + assert_eq!(contract_balance.u128(), 100u128); + + let admin_balance = snip20::balance_query( + &app, + addrs.admin().as_str(), + &deployed_contracts, + SHADE, + "viewing_key".to_owned(), + )?; + + assert_eq!(admin_balance.u128(), prev_admin_balance.u128() + 100); + + let err = lb_staking::recover_funds( + &mut app, + addrs.admin().as_str(), + &lb_staking, + shade_token_type, + Uint128::from(100u128), + addrs.admin().to_string(), + ); + + assert_eq!( + err.unwrap_err(), + StdError::generic_err( + "Generic error: Trying to recover already staked funds. Extra funds Uint128(0), amount Uint128(100)", + ) + ); + Ok(()) +} + #[test] pub fn update_config() -> Result<(), anyhow::Error> { let addrs = init_addrs(); @@ -1578,13 +1938,6 @@ fn query_balance() -> Result<(), anyhow::Error> { lb_token::batch_send(&mut app, addrs.batman().as_str(), &lb_token, actions)?; - lb_staking::set_viewing_key( - &mut app, - addrs.batman().as_str(), - &lb_staking, - "viewing_key".to_owned(), - )?; - for i in 0..total_bins { let id = get_id(ACTIVE_ID, i, NB_BINS_Y); let (reserves_x, reserves_y, _) = @@ -1597,13 +1950,9 @@ fn query_balance() -> Result<(), anyhow::Error> { let total: U256 = expected_balance_x * U256::from_str_prefixed(price.to_string().as_str())? + (expected_balance_y << 128); - let balance = lb_staking::query_balance( - &app, - &lb_staking, - addrs.batman(), - "viewing_key".to_owned(), - id, - )?; + let query_auth = generate_auth(addrs.batman().to_string()); + + let balance = lb_staking::query_balance(&app, &lb_staking, query_auth, id)?; assert_eq!(total.u256_to_uint256(), balance); assert!(balance > Uint256::MIN); @@ -1652,21 +2001,9 @@ fn query_all_balance() -> Result<(), anyhow::Error> { lb_token::batch_send(&mut app, addrs.batman().as_str(), &lb_token, actions)?; - lb_staking::set_viewing_key( - &mut app, - addrs.batman().as_str(), - &lb_staking, - "viewing_key".to_owned(), - )?; + let query_auth = generate_auth(addrs.batman().to_string()); - let balances = lb_staking::query_all_balances( - &app, - &lb_staking, - addrs.batman(), - "viewing_key".to_owned(), - None, - None, - )?; + let balances = lb_staking::query_all_balances(&app, &lb_staking, query_auth, None, None)?; for owner_balance in balances { let id = owner_balance.token_id.parse().unwrap(); @@ -1730,20 +2067,14 @@ fn query_txn_history() -> Result<(), anyhow::Error> { lb_token::batch_send(&mut app, addrs.batman().as_str(), &lb_token, actions)?; - lb_staking::set_viewing_key( - &mut app, - addrs.batman().as_str(), - &lb_staking, - "viewing_key".to_owned(), - )?; - // query all txn history and staking txn history + let query_auth = generate_auth(addrs.batman().to_string()); + let (all_txns_1, count) = lb_staking::query_txn_history( &app, &lb_staking, - addrs.batman(), - "viewing_key".to_owned(), + query_auth.clone(), None, None, QueryTxnType::All, @@ -1755,8 +2086,7 @@ fn query_txn_history() -> Result<(), anyhow::Error> { let (staking_txns, count) = lb_staking::query_txn_history( &app, &lb_staking, - addrs.batman(), - "viewing_key".to_owned(), + query_auth.clone(), None, None, QueryTxnType::Stake, @@ -1801,6 +2131,7 @@ fn query_txn_history() -> Result<(), anyhow::Error> { end: 20, })?), )?; + roll_time(&mut app, Some(100)); lb_pair::calculate_rewards(&mut app, addrs.admin().as_str(), &lb_pair.info.contract)?; @@ -1811,8 +2142,7 @@ fn query_txn_history() -> Result<(), anyhow::Error> { let (all_txns_2, count) = lb_staking::query_txn_history( &app, &lb_staking, - addrs.batman(), - "viewing_key".to_owned(), + query_auth.clone(), None, None, QueryTxnType::All, @@ -1824,8 +2154,7 @@ fn query_txn_history() -> Result<(), anyhow::Error> { let (claim_rewards_txns, count) = lb_staking::query_txn_history( &app, &lb_staking, - addrs.batman(), - "viewing_key".to_owned(), + query_auth.clone(), None, None, QueryTxnType::ClaimRewards, @@ -1846,8 +2175,7 @@ fn query_txn_history() -> Result<(), anyhow::Error> { let (all_txns_3, count) = lb_staking::query_txn_history( &app, &lb_staking, - addrs.batman(), - "viewing_key".to_owned(), + query_auth.clone(), None, None, QueryTxnType::All, @@ -1859,8 +2187,7 @@ fn query_txn_history() -> Result<(), anyhow::Error> { let (unstaking_txns, count) = lb_staking::query_txn_history( &app, &lb_staking, - addrs.batman(), - "viewing_key".to_owned(), + query_auth.clone(), None, None, QueryTxnType::UnStake, @@ -1875,8 +2202,7 @@ fn query_txn_history() -> Result<(), anyhow::Error> { let (all_txns, count) = lb_staking::query_txn_history( &app, &lb_staking, - addrs.batman(), - "viewing_key".to_owned(), + query_auth.clone(), None, Some(10), QueryTxnType::All, diff --git a/contracts/liquidity_book/tests/src/multitests/lb_token.rs b/contracts/liquidity_book/tests/src/multitests/lb_token.rs index 5e66cf55..dfc2e9c3 100644 --- a/contracts/liquidity_book/tests/src/multitests/lb_token.rs +++ b/contracts/liquidity_book/tests/src/multitests/lb_token.rs @@ -40,7 +40,7 @@ pub fn init_setup() -> Result< anyhow::Error, > { let addrs = init_addrs(); - let (mut app, lb_factory, deployed_contracts) = setup(None, None)?; + let (mut app, lb_factory, deployed_contracts, _, _) = setup(None, None)?; let silk = extract_contract_info(&deployed_contracts, SILK)?; let shade = extract_contract_info(&deployed_contracts, SHADE)?; diff --git a/contracts/liquidity_book/tests/src/multitests/mod.rs b/contracts/liquidity_book/tests/src/multitests/mod.rs index 3f619330..eac9bb7e 100644 --- a/contracts/liquidity_book/tests/src/multitests/mod.rs +++ b/contracts/liquidity_book/tests/src/multitests/mod.rs @@ -29,6 +29,6 @@ mod lb_router_register_tokens; mod lb_router_integration; #[cfg(test)] -mod staking_contract; +mod lb_staking; pub mod test_helper; diff --git a/contracts/liquidity_book/tests/src/multitests/test_helper.rs b/contracts/liquidity_book/tests/src/multitests/test_helper.rs index 6aa122b0..1e151a4d 100644 --- a/contracts/liquidity_book/tests/src/multitests/test_helper.rs +++ b/contracts/liquidity_book/tests/src/multitests/test_helper.rs @@ -1,3 +1,6 @@ +use std::ops::Mul; + +use cosmwasm_std::to_binary; use rand::Rng; use shade_multi_test::{ interfaces::{ @@ -5,15 +8,31 @@ use shade_multi_test::{ snip20, utils::{DeployedContracts, SupportedContracts}, }, - multi::{admin::init_admin_auth, lb_pair::LbPair, lb_staking::LbStaking, lb_token::LbToken}, + multi::{ + admin::init_admin_auth, + lb_pair::LbPair, + lb_staking::LbStaking, + lb_token::LbToken, + query_auth::QueryAuth, + }, }; use shade_protocol::{ c_std::{Addr, BlockInfo, ContractInfo, StdResult, Timestamp, Uint128, Uint256}, lb_libraries::{constants::PRECISION, math::u24::U24}, - liquidity_book::lb_pair::{LiquidityParameters, RewardsDistributionAlgorithm}, + liquidity_book::{ + lb_pair::{LiquidityParameters, RewardsDistributionAlgorithm}, + lb_staking::Auth, + }, multi_test::App, + query_auth, swap::core::TokenType, - utils::{asset::Contract, cycle::parse_utc_datetime, MultiTestable}, + utils::{ + asset::Contract, + cycle::parse_utc_datetime, + ExecuteCallback, + InstantiateCallback, + MultiTestable, + }, }; pub const ID_ONE: u32 = 1 << 23; @@ -108,6 +127,8 @@ pub fn init_addrs() -> Addrs { } pub fn assert_approx_eq_rel(a: Uint256, b: Uint256, delta: Uint256, error_message: &str) { + let delta = delta.multiply_ratio(Uint256::from(10_u128.pow(14)), Uint256::from(1u128)); + let abs_delta = (a).abs_diff(b); let percent_delta = abs_delta.multiply_ratio(Uint256::from(10_u128.pow(18)), b); @@ -129,10 +150,17 @@ pub fn assert_approx_eq_abs(a: Uint256, b: Uint256, delta: Uint256, error_messag } } +pub fn generate_auth(addr: String) -> Auth { + Auth::ViewingKey { + key: "viewing_key".to_string(), + address: addr, + } +} + pub fn setup( bin_step: Option, rewards_distribution_algorithm: Option, -) -> Result<(App, Contract, DeployedContracts), anyhow::Error> { +) -> Result<(App, Contract, DeployedContracts, ContractInfo, ContractInfo), anyhow::Error> { // init snip-20's let mut app = App::default(); let addrs = init_addrs(); @@ -237,12 +265,33 @@ pub fn setup( //2. init factory let admin_contract = init_admin_auth(&mut app, &addrs.admin()); + let query_contract = query_auth::InstantiateMsg { + admin_auth: admin_contract.clone().into(), + prng_seed: to_binary("").ok().unwrap(), + } + .test_init( + QueryAuth::default(), + &mut app, + addrs.admin(), + "query_auth", + &[], + ) + .unwrap(); + + // set staking user VK + query_auth::ExecuteMsg::SetViewingKey { + key: "viewing_key".to_string(), + padding: None, + } + .test_exec(&query_contract, &mut app, addrs.batman().clone(), &[]) + .unwrap(); let lb_factory = lb_factory::init( &mut app, addrs.admin().as_str(), addrs.joker(), - admin_contract.into(), + admin_contract.clone().into(), + query_contract.clone().into(), addrs.admin(), )?; let lb_token_stored_code = app.store_code(LbToken::default().contract()); @@ -372,7 +421,13 @@ pub fn setup( }, )?; - Ok((app, lb_factory, deployed_contracts)) + Ok(( + app, + lb_factory, + deployed_contracts, + admin_contract, + query_contract, + )) } pub fn roll_blockchain(app: &mut App, blocks: Option) { @@ -388,6 +443,11 @@ pub fn roll_blockchain(app: &mut App, blocks: Option) { }); } +pub fn roll_time(app: &mut App, time: Option) { + let timestamp = Timestamp::from_seconds(app.block_info().time.seconds() + time.unwrap_or(1)); + app.set_time(timestamp); +} + pub fn extract_contract_info( deployed_contracts: &DeployedContracts, symbol: &str, diff --git a/packages/multi_test/src/interfaces/lb_factory.rs b/packages/multi_test/src/interfaces/lb_factory.rs index 05162310..4e0b396b 100644 --- a/packages/multi_test/src/interfaces/lb_factory.rs +++ b/packages/multi_test/src/interfaces/lb_factory.rs @@ -20,6 +20,8 @@ pub fn init( sender: &str, fee_recipient: Addr, admin_auth: RawContract, + query_auth: RawContract, + recover_staking_funds_receiver: Addr, ) -> StdResult { let lb_factory = Contract::from( @@ -29,6 +31,7 @@ pub fn init( admin_auth, recover_staking_funds_receiver, + query_auth, } .test_init( LbFactory::default(), diff --git a/packages/multi_test/src/interfaces/lb_pair.rs b/packages/multi_test/src/interfaces/lb_pair.rs index b524812e..6ae9369f 100644 --- a/packages/multi_test/src/interfaces/lb_pair.rs +++ b/packages/multi_test/src/interfaces/lb_pair.rs @@ -4,14 +4,22 @@ use shade_protocol::{ contract_interfaces::{liquidity_book::lb_pair, snip20}, lb_libraries::types::{ContractInstantiationInfo, StaticFeeParameters}, liquidity_book::lb_pair::{ - BinResponse, ContractStatus, LiquidityParameters, RemoveLiquidity, RewardsDistribution, - RewardsDistributionAlgorithm, UpdatedBinsAtHeightResponse, + BinResponse, + ContractStatus, + LiquidityParameters, + RemoveLiquidity, + RewardsDistribution, + RewardsDistributionAlgorithm, + UpdatedBinsAtHeightResponse, }, multi_test::App, swap::core::{TokenAmount, TokenType}, utils::{ asset::{Contract, RawContract}, - ExecuteCallback, InstantiateCallback, MultiTestable, Query, + ExecuteCallback, + InstantiateCallback, + MultiTestable, + Query, }, }; @@ -31,6 +39,8 @@ pub fn init( entropy: String, protocol_fee_recipient: Addr, admin_auth: RawContract, + query_auth: RawContract, + total_reward_bins: u32, rewards_distribution_algorithm: Option, epoch_staking_index: u64, @@ -51,6 +61,7 @@ pub fn init( entropy, protocol_fee_recipient, admin_auth, + query_auth, total_reward_bins: Some(total_reward_bins), rewards_distribution_algorithm: rewards_distribution_algorithm .unwrap_or(RewardsDistributionAlgorithm::TimeBasedRewards), diff --git a/packages/multi_test/src/interfaces/lb_staking.rs b/packages/multi_test/src/interfaces/lb_staking.rs index 04ebfcc7..29c2fea4 100644 --- a/packages/multi_test/src/interfaces/lb_staking.rs +++ b/packages/multi_test/src/interfaces/lb_staking.rs @@ -1,30 +1,26 @@ use shade_protocol::{ - c_std::{Addr, ContractInfo, StdError, StdResult, Uint256}, + c_std::{Addr, ContractInfo, StdError, StdResult, Uint128, Uint256}, cosmwasm_schema::cw_serde, - liquidity_book::lb_staking::{ - ExecuteMsg, Liquidity, OwnerBalance, QueryAnswer, QueryMsg, QueryTxnType, Tx, + liquidity_book::{ + lb_pair::RewardsDistribution, + lb_staking::{ + Auth, + ExecuteMsg, + Liquidity, + OwnerBalance, + QueryAnswer, + QueryMsg, + QueryTxnType, + RewardTokenInfo, + Tx, + }, }, multi_test::App, + swap::core::TokenType, utils::{asset::RawContract, ExecuteCallback, Query}, + Contract, }; -pub fn set_viewing_key( - app: &mut App, - sender: &str, - lb_staking: &ContractInfo, - key: String, -) -> StdResult<()> { - match (ExecuteMsg::SetViewingKey { key }.test_exec( - lb_staking, - app, - Addr::unchecked(sender), - &[], - )) { - Ok(_) => Ok(()), - Err(e) => return Err(StdError::generic_err(e.root_cause().to_string())), - } -} - pub fn unstaking( app: &mut App, sender: &str, @@ -72,7 +68,11 @@ pub fn claim_rewards(app: &mut App, sender: &str, lb_staking: &ContractInfo) -> } } -pub fn recover_funds(app: &mut App, sender: &str, lb_staking: &ContractInfo) -> StdResult<()> { +pub fn recover_expired_rewards( + app: &mut App, + sender: &str, + lb_staking: &ContractInfo, +) -> StdResult<()> { match (ExecuteMsg::RecoverExpiredFunds {}.test_exec( lb_staking, app, @@ -84,6 +84,27 @@ pub fn recover_funds(app: &mut App, sender: &str, lb_staking: &ContractInfo) -> } } +pub fn recover_funds( + app: &mut App, + sender: &str, + lb_staking: &ContractInfo, + token: TokenType, + amount: Uint128, + to: String, +) -> StdResult<()> { + match (ExecuteMsg::RecoverFunds { + token, + amount, + to, + msg: None, + } + .test_exec(lb_staking, app, Addr::unchecked(sender), &[])) + { + Ok(_) => Ok(()), + Err(e) => return Err(StdError::generic_err(e.root_cause().to_string())), + } +} + pub fn register_reward_tokens( app: &mut App, sender: &str, @@ -103,15 +124,13 @@ pub fn register_reward_tokens( pub fn query_liquidity( app: &App, - sender: &Addr, - key: String, + auth: Auth, lb_staking: &ContractInfo, ids: Vec, round_index: Option, ) -> StdResult> { let res: QueryAnswer = QueryMsg::Liquidity { - owner: sender.clone(), - key, + auth, round_index, token_ids: ids, } @@ -145,13 +164,13 @@ pub fn query_id_total_balance(app: &App, lb_staking: &ContractInfo, id: u32) -> pub fn query_balance( app: &App, lb_staking: &ContractInfo, - staker: Addr, - key: String, + auth: Auth, + id: u32, ) -> StdResult { let res: QueryAnswer = QueryMsg::Balance { - owner: staker, - key, + auth, + token_id: id.to_string(), } .test_query(&lb_staking, app)?; @@ -164,15 +183,15 @@ pub fn query_balance( pub fn query_txn_history( app: &App, lb_staking: &ContractInfo, - staker: Addr, - key: String, + auth: Auth, + page: Option, page_size: Option, txn_type: QueryTxnType, ) -> StdResult<(Vec, u64)> { let res: QueryAnswer = QueryMsg::TransactionHistory { - owner: staker, - key, + auth, + page, page_size, txn_type, @@ -187,14 +206,14 @@ pub fn query_txn_history( pub fn query_all_balances( app: &App, lb_staking: &ContractInfo, - staker: Addr, - key: String, + auth: Auth, + page: Option, page_size: Option, ) -> StdResult> { let res: QueryAnswer = QueryMsg::AllBalances { - owner: staker, - key, + auth, + page, page_size, } @@ -216,7 +235,7 @@ pub fn query_config(app: &App, lb_staking: &ContractInfo) -> StdResult { epoch_index, epoch_durations, expiry_durations, - } => Ok((Config { + } => Ok(Config { lb_token, lb_pair, admin_auth, @@ -224,7 +243,7 @@ pub fn query_config(app: &App, lb_staking: &ContractInfo) -> StdResult { epoch_index, epoch_durations, expiry_durations, - })), + }), _ => Err(StdError::generic_err("Query failed")), } } @@ -232,9 +251,70 @@ pub fn query_config(app: &App, lb_staking: &ContractInfo) -> StdResult { pub struct Config { pub lb_token: ContractInfo, pub lb_pair: Addr, - pub admin_auth: ContractInfo, - pub query_auth: Option, + pub admin_auth: Contract, + pub query_auth: Contract, pub epoch_index: u64, pub epoch_durations: u64, pub expiry_durations: Option, } + +pub fn query_epoch_info( + app: &App, + lb_staking: &ContractInfo, + index: Option, +) -> StdResult { + let res: QueryAnswer = QueryMsg::EpochInfo { index }.test_query(&lb_staking, app)?; + match res { + QueryAnswer::EpochInfo { + rewards_distribution, + reward_tokens, + start_time, + end_time, + duration, + expired_at, + } => Ok(EpochInfo { + rewards_distribution, + reward_tokens, + start_time, + end_time, + duration, + expired_at, + }), + _ => Err(StdError::generic_err("Query failed")), + } +} + +pub struct EpochInfo { + pub rewards_distribution: Option, + pub reward_tokens: Option>, + pub start_time: u64, + pub end_time: u64, + pub duration: u64, + pub expired_at: Option, +} + +pub fn query_staker_info( + app: &App, + lb_staking: &ContractInfo, + auth: Auth, +) -> StdResult { + let res: QueryAnswer = QueryMsg::StakerInfo { auth }.test_query(&lb_staking, app)?; + match res { + QueryAnswer::StakerInfo { + starting_round, + total_rewards_earned, + last_claim_rewards_round, + } => Ok(StakerInfo { + starting_round, + total_rewards_earned, + last_claim_rewards_round, + }), + _ => Err(StdError::generic_err("Query failed")), + } +} + +pub struct StakerInfo { + pub starting_round: Option, + pub total_rewards_earned: Uint128, + pub last_claim_rewards_round: Option, +} diff --git a/packages/multi_test/src/interfaces/lb_token.rs b/packages/multi_test/src/interfaces/lb_token.rs index a77ff693..3a68be04 100644 --- a/packages/multi_test/src/interfaces/lb_token.rs +++ b/packages/multi_test/src/interfaces/lb_token.rs @@ -41,7 +41,7 @@ pub fn batch_send( } pub fn query_contract_info(app: &App, info: &ContractInfo) -> StdResult { - let res: QueryAnswer = QueryMsg::TokenContractInfo {}.test_query(&info, app)?; + let res: QueryAnswer = QueryMsg::ContractInfo {}.test_query(&info, app)?; match res { QueryAnswer::TokenContractInfo { .. } => Ok(res), _ => Err(StdError::generic_err("Query failed")), diff --git a/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_factory.rs b/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_factory.rs index 1fa384b9..bf313b6f 100644 --- a/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_factory.rs +++ b/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_factory.rs @@ -13,6 +13,7 @@ pub use lb_pair::InstantiateMsg as LBPairInstantiateMsg; #[cw_serde] pub struct InstantiateMsg { pub admin_auth: RawContract, + pub query_auth: RawContract, pub owner: Option, pub fee_recipient: Addr, pub recover_staking_funds_receiver: Addr, diff --git a/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_pair.rs b/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_pair.rs index ba6e164b..874077cd 100644 --- a/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_pair.rs +++ b/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_pair.rs @@ -4,7 +4,9 @@ use crate::{ c_std::{Addr, ContractInfo, Decimal256, Uint128, Uint256}, cosmwasm_schema::{cw_serde, QueryResponses}, liquidity_book::lb_libraries::types::{ - Bytes32, ContractInstantiationInfo, StaticFeeParameters, + Bytes32, + ContractInstantiationInfo, + StaticFeeParameters, }, snip20::Snip20ReceiveMsg, swap::core::{TokenAmount, TokenType}, @@ -26,6 +28,7 @@ pub struct InstantiateMsg { pub entropy: String, pub protocol_fee_recipient: Addr, pub admin_auth: RawContract, + pub query_auth: RawContract, pub total_reward_bins: Option, pub rewards_distribution_algorithm: RewardsDistributionAlgorithm, pub epoch_staking_index: u64, diff --git a/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_staking.rs b/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_staking.rs index 7b501cc5..e8afd03b 100644 --- a/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_staking.rs +++ b/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_staking.rs @@ -1,13 +1,26 @@ use std::collections::HashMap; -use crate::liquidity_book::lb_libraries::types::ContractInstantiationInfo; +use crate::{ + liquidity_book::lb_libraries::types::ContractInstantiationInfo, + query_auth::QueryPermit, + Contract, +}; use cosmwasm_schema::QueryResponses; -use secret_toolkit::permit::Permit; use crate::{ c_std::{ - to_binary, Addr, Api, Binary, BlockInfo, Coin, ContractInfo, CosmosMsg, StdResult, Uint128, - Uint256, WasmMsg, + to_binary, + Addr, + Api, + Binary, + BlockInfo, + Coin, + ContractInfo, + CosmosMsg, + StdResult, + Uint128, + Uint256, + WasmMsg, }, cosmwasm_schema::cw_serde, snip20::Snip20ReceiveMsg, @@ -46,7 +59,7 @@ pub struct InstantiateMsg { pub amm_pair: String, pub lb_token: RawContract, pub admin_auth: RawContract, - pub query_auth: Option, + pub query_auth: RawContract, pub epoch_index: u64, pub epoch_duration: u64, pub expiry_duration: Option, @@ -58,6 +71,7 @@ pub enum ExecuteMsg { ClaimRewards {}, EndEpoch { rewards_distribution: RewardsDistribution, + epoch_index: u64, }, Unstake { token_ids: Vec, @@ -79,16 +93,6 @@ pub enum ExecuteMsg { msg: Option, }, RecoverExpiredFunds {}, - CreateViewingKey { - entropy: String, - }, - SetViewingKey { - key: String, - }, - /// disallow the use of a query permit - RevokePermit { - permit_name: String, - }, } #[cw_serde] @@ -189,8 +193,8 @@ pub struct RewardTokenInfo { pub struct State { pub lb_token: ContractInfo, pub lb_pair: Addr, - pub admin_auth: ContractInfo, - pub query_auth: Option, + pub query_auth: Contract, + pub admin_auth: Contract, pub epoch_index: u64, pub epoch_durations: u64, pub expiry_durations: Option, @@ -304,6 +308,15 @@ impl Default for TotalLiquiditySnapshot { } } +#[cw_serde] +pub struct AuthPermit {} + +#[cw_serde] +pub enum Auth { + ViewingKey { key: String, address: String }, + Permit(QueryPermit), +} + ///////////////////////////////////////////////////////////////////////////////// // Query messages ///////////////////////////////////////////////////////////////////////////////// @@ -314,55 +327,37 @@ impl Default for TotalLiquiditySnapshot { pub enum QueryMsg { /// returns public information of the SNIP1155 contract ContractInfo {}, + EpochInfo { + index: Option, + }, + RegisteredTokens {}, IdTotalBalance { id: String, }, Balance { - owner: Addr, - key: String, + auth: Auth, token_id: String, }, + StakerInfo { + auth: Auth, + }, AllBalances { - owner: Addr, - key: String, + auth: Auth, page: Option, page_size: Option, }, Liquidity { - owner: Addr, - key: String, + auth: Auth, round_index: Option, token_ids: Vec, }, TransactionHistory { - owner: Addr, - key: String, + auth: Auth, page: Option, page_size: Option, txn_type: QueryTxnType, }, - WithPermit { - permit: Permit, - query: QueryWithPermit, - }, -} - -impl QueryMsg { - pub fn get_validation_params(&self) -> StdResult<(Vec<&Addr>, String)> { - match self { - Self::Balance { owner, key, .. } => Ok((vec![owner], key.clone())), - Self::AllBalances { owner, key, .. } => Ok((vec![owner], key.clone())), - Self::Liquidity { owner, key, .. } => Ok((vec![owner], key.clone())), - Self::TransactionHistory { owner, key, .. } => Ok((vec![owner], key.clone())), - Self::ContractInfo {} - | Self::IdTotalBalance { .. } - | Self::RegisteredTokens { .. } - | Self::WithPermit { .. } => { - unreachable!("This query type does not require viewing key authentication") - } - } - } } #[cw_serde] @@ -388,17 +383,32 @@ pub enum QueryAnswer { ContractInfo { lb_token: ContractInfo, lb_pair: Addr, - admin_auth: ContractInfo, - query_auth: Option, + admin_auth: Contract, + query_auth: Contract, epoch_index: u64, epoch_durations: u64, expiry_durations: Option, }, + EpochInfo { + rewards_distribution: Option, + reward_tokens: Option>, + start_time: u64, + end_time: u64, + duration: u64, + expired_at: Option, + }, + RegisteredTokens(Vec), IdTotalBalance { amount: Uint256, }, /// returns balance of a specific token_id. Owners can give permission to other addresses to query their balance + /// + StakerInfo { + starting_round: Option, + total_rewards_earned: Uint128, + last_claim_rewards_round: Option, + }, Balance { amount: Uint256, }, diff --git a/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_token.rs b/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_token.rs index 73b0d71c..c6f50de3 100644 --- a/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_token.rs +++ b/packages/shade_protocol/src/contract_interfaces/liquidity_book/lb_token.rs @@ -191,21 +191,21 @@ pub enum ExecuteMsg { // remove_minters: Vec, // padding: Option, // }, - // ChangeAdmin { - // new_admin: Addr, - // padding: Option, - // }, - // /// Permanently breaks admin keys for this contract. No admin function can be called after this - // /// action. Any existing curators or minters will remain as curators or minters; no new curators can be - // /// added and no current curator can be removed. - // /// - // /// Requires caller to input current admin address and contract address. These inputs are not strictly - // /// necessary, but as a safety precaution to reduce the chances of accidentally calling this function. - // RemoveAdmin { - // current_admin: Addr, - // contract_address: Addr, - // padding: Option, - // }, + ChangeAdmin { + new_admin: Addr, + padding: Option, + }, + /// Permanently breaks admin keys for this contract. No admin function can be called after this + /// action. Any existing curators or minters will remain as curators or minters; no new curators can be + /// added and no current curator can be removed. + /// + /// Requires caller to input current admin address and contract address. These inputs are not strictly + /// necessary, but as a safety precaution to reduce the chances of accidentally calling this function. + RemoveAdmin { + current_admin: Addr, + contract_address: Addr, + padding: Option, + }, RegisterReceive { code_hash: String, padding: Option, @@ -279,7 +279,7 @@ pub enum ExecuteAnswer { #[serde(rename_all = "snake_case")] pub enum QueryMsg { /// returns public information of the SNIP1155 contract - TokenContractInfo {}, + ContractInfo {}, IdTotalBalance { id: String, }, @@ -348,7 +348,7 @@ impl QueryMsg { } => Ok((vec![owner, allowed_address], key.clone())), Self::AllPermissions { address, key, .. } => Ok((vec![address], key.clone())), Self::TokenIdPrivateInfo { address, key, .. } => Ok((vec![address], key.clone())), - Self::TokenContractInfo {} + Self::ContractInfo {} | Self::IdTotalBalance { .. } | Self::TokenIdPublicInfo { .. } | Self::RegisteredCodeHash { .. }