From b416e882cb04a3004860b9da48b54be11dad5a13 Mon Sep 17 00:00:00 2001 From: CryptoKage2306 <26vivek06@gmail.com> Date: Tue, 13 Aug 2024 17:51:43 +0530 Subject: [PATCH 1/7] feat:add ah contract --- bindings/src/account_history/mod.rs | 2 + bindings/src/account_history/msg/execute.rs | 18 + .../src/account_history/msg/instantiate.rs | 9 + bindings/src/account_history/msg/migration.rs | 7 + bindings/src/account_history/msg/mod.rs | 60 ++ bindings/src/account_history/msg/query.rs | 169 ++++ .../earn/get_eden_boost_earn_details_resp.rs | 9 + .../earn/get_eden_earn_details_resp.rs | 9 + .../earn/get_elys_earn_details_resp.rs | 8 + .../earn/get_usdc_earn_details_resp.rs | 8 + .../estaking/get_estaking_rewards_response.rs | 16 + .../msg/query_resp/get_all_resp.rs | 9 + .../msg/query_resp/get_rewards_resp.rs | 9 + .../msg/query_resp/get_storage_size.rs | 8 + .../msg/query_resp/liquid_asset.rs | 14 + .../get_masterchef_pending_rewards.rs | 12 + .../get_masterchef_pool_apr_response.rs | 8 + .../get_masterchef_stable_stake_response.rs | 7 + .../query_resp/membership_tier_response.rs | 51 ++ .../msg/query_resp/params_resp.rs | 14 + .../msg/query_resp/staked_assets_response.rs | 34 + .../query_resp/total_value_per_asset_resp.rs | 10 + .../msg/query_resp/user_value_response.rs | 7 + bindings/src/account_history/msg/sudo.rs | 6 + .../account_history/types/account_snapshot.rs | 44 + .../src/account_history/types/coin_value.rs | 120 +++ bindings/src/account_history/types/denom.rs | 33 + .../types/earn_detail/earn_detail.rs | 117 +++ .../types/earn_program/eden_boost_earn.rs | 35 + .../types/earn_program/eden_earn.rs | 64 ++ .../types/earn_program/elys_earn.rs | 46 + .../types/earn_program/usdc_earn.rs | 41 + .../src/account_history/types/liquid_asset.rs | 35 + .../src/account_history/types/metadata.rs | 144 +++ bindings/src/account_history/types/mod.rs | 47 + .../account_history/types/perpetual_assets.rs | 118 +++ .../account_history/types/pool_balances.rs | 13 + .../src/account_history/types/portfolio.rs | 1 + .../types/portfolio_balance_snapshot.rs | 24 + bindings/src/account_history/types/reward.rs | 24 + .../src/account_history/types/staked_asset.rs | 13 + .../account_history/types/staked_assets.rs | 25 + .../account_history/types/total_balance.rs | 27 + bindings/src/lib.rs | 2 + .../account-history-contract/.cargo/config | 4 + contracts/account-history-contract/Cargo.toml | 38 + .../execute/add_user_address_to_queue.rs | 9 + .../src/action/execute/clean_up_storage.rs | 20 + .../src/action/mod.rs | 74 ++ .../src/action/query/all.rs | 29 + .../src/action/query/exit_pool_estimation.rs | 39 + .../get_eden_boost_earn_program_details.rs | 55 ++ .../query/get_eden_earn_program_details.rs | 71 ++ .../query/get_elys_earn_program_details.rs | 72 ++ .../src/action/query/get_estaking_rewards.rs | 28 + .../src/action/query/get_liquid_assets.rs | 53 ++ .../query/get_masterchef_pending_rewards.rs | 19 + .../action/query/get_masterchef_pool_apr.rs | 17 + .../query/get_masterchef_stable_stake_apr.rs | 13 + .../src/action/query/get_membership_tier.rs | 23 + .../src/action/query/get_perpetual_asset.rs | 14 + .../src/action/query/get_pool_balances.rs | 16 + .../src/action/query/get_pools.rs | 16 + .../src/action/query/get_pools_apr.rs | 11 + .../src/action/query/get_portfolio.rs | 11 + .../src/action/query/get_rewards.rs | 22 + .../src/action/query/get_staked_assets.rs | 24 + .../query/get_usdc_earn_program_details.rs | 66 ++ .../src/action/query/join_pool_estimation.rs | 12 + .../src/action/query/last_snapshot.rs | 20 + .../src/action/query/params.rs | 29 + .../src/action/query/pool_asset_estimation.rs | 63 ++ .../src/action/query/user_snapshots.rs | 46 + .../src/action/query/user_value.rs | 23 + .../src/action/sudo/update_metadata_prices.rs | 14 + .../bin/account-history-contract-schema.rs | 11 + .../src/entry_point/execute.rs | 76 ++ .../src/entry_point/instantiate.rs | 63 ++ .../src/entry_point/migrate.rs | 74 ++ .../src/entry_point/mod.rs | 11 + .../src/entry_point/query.rs | 276 ++++++ .../src/entry_point/sudo.rs | 20 + .../account-history-contract/src/error.rs | 19 + contracts/account-history-contract/src/lib.rs | 15 + .../src/states/admin_address.rs | 3 + .../src/states/enable_update_account.rs | 3 + .../src/states/expiration.rs | 4 + .../src/states/history.rs | 14 + .../src/states/metadata.rs | 4 + .../src/states/mod.rs | 17 + .../src/states/processed_account_per_block.rs | 3 + .../src/states/trade_shield_address.rs | 3 + .../src/states/user_address_queue.rs | 3 + .../src/tests/get_all_pools.rs | 359 ++++++++ .../get_eden_boost_earn_program_details.rs | 295 ++++++ .../tests/get_eden_earn_program_details.rs | 308 +++++++ .../src/tests/get_elys_earn_program_detail.rs | 304 +++++++ .../src/tests/get_liquid_assets.rs | 541 +++++++++++ .../src/tests/get_rewards.rs | 287 ++++++ .../src/tests/get_staked_assets.rs | 850 ++++++++++++++++++ .../tests/get_usdc_earn_program_details.rs | 305 +++++++ .../account-history-contract/src/tests/mod.rs | 8 + .../src/types/account_snapshot_generator.rs | 778 ++++++++++++++++ .../account-history-contract/src/types/mod.rs | 3 + .../account-history-contract/src/utils.rs | 11 + 105 files changed, 7096 insertions(+) create mode 100644 bindings/src/account_history/mod.rs create mode 100644 bindings/src/account_history/msg/execute.rs create mode 100644 bindings/src/account_history/msg/instantiate.rs create mode 100644 bindings/src/account_history/msg/migration.rs create mode 100644 bindings/src/account_history/msg/mod.rs create mode 100644 bindings/src/account_history/msg/query.rs create mode 100644 bindings/src/account_history/msg/query_resp/earn/get_eden_boost_earn_details_resp.rs create mode 100644 bindings/src/account_history/msg/query_resp/earn/get_eden_earn_details_resp.rs create mode 100644 bindings/src/account_history/msg/query_resp/earn/get_elys_earn_details_resp.rs create mode 100644 bindings/src/account_history/msg/query_resp/earn/get_usdc_earn_details_resp.rs create mode 100644 bindings/src/account_history/msg/query_resp/estaking/get_estaking_rewards_response.rs create mode 100644 bindings/src/account_history/msg/query_resp/get_all_resp.rs create mode 100644 bindings/src/account_history/msg/query_resp/get_rewards_resp.rs create mode 100644 bindings/src/account_history/msg/query_resp/get_storage_size.rs create mode 100644 bindings/src/account_history/msg/query_resp/liquid_asset.rs create mode 100644 bindings/src/account_history/msg/query_resp/masterchef/get_masterchef_pending_rewards.rs create mode 100644 bindings/src/account_history/msg/query_resp/masterchef/get_masterchef_pool_apr_response.rs create mode 100644 bindings/src/account_history/msg/query_resp/masterchef/get_masterchef_stable_stake_response.rs create mode 100644 bindings/src/account_history/msg/query_resp/membership_tier_response.rs create mode 100644 bindings/src/account_history/msg/query_resp/params_resp.rs create mode 100644 bindings/src/account_history/msg/query_resp/staked_assets_response.rs create mode 100644 bindings/src/account_history/msg/query_resp/total_value_per_asset_resp.rs create mode 100644 bindings/src/account_history/msg/query_resp/user_value_response.rs create mode 100644 bindings/src/account_history/msg/sudo.rs create mode 100644 bindings/src/account_history/types/account_snapshot.rs create mode 100644 bindings/src/account_history/types/coin_value.rs create mode 100644 bindings/src/account_history/types/denom.rs create mode 100644 bindings/src/account_history/types/earn_detail/earn_detail.rs create mode 100644 bindings/src/account_history/types/earn_program/eden_boost_earn.rs create mode 100644 bindings/src/account_history/types/earn_program/eden_earn.rs create mode 100644 bindings/src/account_history/types/earn_program/elys_earn.rs create mode 100644 bindings/src/account_history/types/earn_program/usdc_earn.rs create mode 100644 bindings/src/account_history/types/liquid_asset.rs create mode 100644 bindings/src/account_history/types/metadata.rs create mode 100644 bindings/src/account_history/types/mod.rs create mode 100644 bindings/src/account_history/types/perpetual_assets.rs create mode 100644 bindings/src/account_history/types/pool_balances.rs create mode 100644 bindings/src/account_history/types/portfolio.rs create mode 100644 bindings/src/account_history/types/portfolio_balance_snapshot.rs create mode 100644 bindings/src/account_history/types/reward.rs create mode 100644 bindings/src/account_history/types/staked_asset.rs create mode 100644 bindings/src/account_history/types/staked_assets.rs create mode 100644 bindings/src/account_history/types/total_balance.rs create mode 100644 contracts/account-history-contract/.cargo/config create mode 100644 contracts/account-history-contract/Cargo.toml create mode 100644 contracts/account-history-contract/src/action/execute/add_user_address_to_queue.rs create mode 100644 contracts/account-history-contract/src/action/execute/clean_up_storage.rs create mode 100644 contracts/account-history-contract/src/action/mod.rs create mode 100644 contracts/account-history-contract/src/action/query/all.rs create mode 100644 contracts/account-history-contract/src/action/query/exit_pool_estimation.rs create mode 100644 contracts/account-history-contract/src/action/query/get_eden_boost_earn_program_details.rs create mode 100644 contracts/account-history-contract/src/action/query/get_eden_earn_program_details.rs create mode 100644 contracts/account-history-contract/src/action/query/get_elys_earn_program_details.rs create mode 100644 contracts/account-history-contract/src/action/query/get_estaking_rewards.rs create mode 100644 contracts/account-history-contract/src/action/query/get_liquid_assets.rs create mode 100644 contracts/account-history-contract/src/action/query/get_masterchef_pending_rewards.rs create mode 100644 contracts/account-history-contract/src/action/query/get_masterchef_pool_apr.rs create mode 100644 contracts/account-history-contract/src/action/query/get_masterchef_stable_stake_apr.rs create mode 100644 contracts/account-history-contract/src/action/query/get_membership_tier.rs create mode 100644 contracts/account-history-contract/src/action/query/get_perpetual_asset.rs create mode 100644 contracts/account-history-contract/src/action/query/get_pool_balances.rs create mode 100644 contracts/account-history-contract/src/action/query/get_pools.rs create mode 100644 contracts/account-history-contract/src/action/query/get_pools_apr.rs create mode 100644 contracts/account-history-contract/src/action/query/get_portfolio.rs create mode 100644 contracts/account-history-contract/src/action/query/get_rewards.rs create mode 100644 contracts/account-history-contract/src/action/query/get_staked_assets.rs create mode 100644 contracts/account-history-contract/src/action/query/get_usdc_earn_program_details.rs create mode 100644 contracts/account-history-contract/src/action/query/join_pool_estimation.rs create mode 100644 contracts/account-history-contract/src/action/query/last_snapshot.rs create mode 100644 contracts/account-history-contract/src/action/query/params.rs create mode 100644 contracts/account-history-contract/src/action/query/pool_asset_estimation.rs create mode 100644 contracts/account-history-contract/src/action/query/user_snapshots.rs create mode 100644 contracts/account-history-contract/src/action/query/user_value.rs create mode 100644 contracts/account-history-contract/src/action/sudo/update_metadata_prices.rs create mode 100644 contracts/account-history-contract/src/bin/account-history-contract-schema.rs create mode 100644 contracts/account-history-contract/src/entry_point/execute.rs create mode 100644 contracts/account-history-contract/src/entry_point/instantiate.rs create mode 100644 contracts/account-history-contract/src/entry_point/migrate.rs create mode 100644 contracts/account-history-contract/src/entry_point/mod.rs create mode 100644 contracts/account-history-contract/src/entry_point/query.rs create mode 100644 contracts/account-history-contract/src/entry_point/sudo.rs create mode 100644 contracts/account-history-contract/src/error.rs create mode 100644 contracts/account-history-contract/src/lib.rs create mode 100644 contracts/account-history-contract/src/states/admin_address.rs create mode 100644 contracts/account-history-contract/src/states/enable_update_account.rs create mode 100644 contracts/account-history-contract/src/states/expiration.rs create mode 100644 contracts/account-history-contract/src/states/history.rs create mode 100644 contracts/account-history-contract/src/states/metadata.rs create mode 100644 contracts/account-history-contract/src/states/mod.rs create mode 100644 contracts/account-history-contract/src/states/processed_account_per_block.rs create mode 100644 contracts/account-history-contract/src/states/trade_shield_address.rs create mode 100644 contracts/account-history-contract/src/states/user_address_queue.rs create mode 100644 contracts/account-history-contract/src/tests/get_all_pools.rs create mode 100644 contracts/account-history-contract/src/tests/get_eden_boost_earn_program_details.rs create mode 100644 contracts/account-history-contract/src/tests/get_eden_earn_program_details.rs create mode 100644 contracts/account-history-contract/src/tests/get_elys_earn_program_detail.rs create mode 100644 contracts/account-history-contract/src/tests/get_liquid_assets.rs create mode 100644 contracts/account-history-contract/src/tests/get_rewards.rs create mode 100644 contracts/account-history-contract/src/tests/get_staked_assets.rs create mode 100644 contracts/account-history-contract/src/tests/get_usdc_earn_program_details.rs create mode 100644 contracts/account-history-contract/src/tests/mod.rs create mode 100644 contracts/account-history-contract/src/types/account_snapshot_generator.rs create mode 100644 contracts/account-history-contract/src/types/mod.rs create mode 100644 contracts/account-history-contract/src/utils.rs diff --git a/bindings/src/account_history/mod.rs b/bindings/src/account_history/mod.rs new file mode 100644 index 00000000..c413ff51 --- /dev/null +++ b/bindings/src/account_history/mod.rs @@ -0,0 +1,2 @@ +pub mod msg; +pub mod types; diff --git a/bindings/src/account_history/msg/execute.rs b/bindings/src/account_history/msg/execute.rs new file mode 100644 index 00000000..4af4ee62 --- /dev/null +++ b/bindings/src/account_history/msg/execute.rs @@ -0,0 +1,18 @@ +use cosmwasm_schema::cw_serde; + +#[cw_serde] +pub enum ExecuteMsg { + AddUserAddressToQueue { + user_address: String, + }, + ChangeParams { + update_account_enabled: Option, + processed_account_per_block: Option, + delete_old_data_enabled: Option, + delete_epoch: Option, + }, + CleanStorage { + limit: u64, + }, + CleanStorageBulk {}, +} diff --git a/bindings/src/account_history/msg/instantiate.rs b/bindings/src/account_history/msg/instantiate.rs new file mode 100644 index 00000000..a7e7a15f --- /dev/null +++ b/bindings/src/account_history/msg/instantiate.rs @@ -0,0 +1,9 @@ +use cosmwasm_schema::cw_serde; +use cw_utils::Expiration; + +#[cw_serde] +pub struct InstantiateMsg { + pub limit: Option, + pub expiration: Option, + pub trade_shield_address: Option, +} diff --git a/bindings/src/account_history/msg/migration.rs b/bindings/src/account_history/msg/migration.rs new file mode 100644 index 00000000..9718fcf8 --- /dev/null +++ b/bindings/src/account_history/msg/migration.rs @@ -0,0 +1,7 @@ +use cosmwasm_schema::cw_serde; + +#[cw_serde] +pub struct MigrationMsg { + pub limit: Option, + pub trade_shield_address: Option, +} diff --git a/bindings/src/account_history/msg/mod.rs b/bindings/src/account_history/msg/mod.rs new file mode 100644 index 00000000..6e6323c0 --- /dev/null +++ b/bindings/src/account_history/msg/mod.rs @@ -0,0 +1,60 @@ +mod execute; +mod instantiate; +mod migration; +mod query; +mod sudo; + +pub use execute::ExecuteMsg; +pub use instantiate::InstantiateMsg; +pub use migration::MigrationMsg; +pub use query::QueryMsg; +pub use sudo::SudoMsg; + +pub mod query_resp { + mod get_all_resp; + mod get_rewards_resp; + mod get_storage_size; + mod liquid_asset; + mod membership_tier_response; + mod params_resp; + mod total_value_per_asset_resp; + mod user_value_response; + + pub use get_all_resp::GetAllResp; + pub use get_rewards_resp::GetRewardsResp; + pub use get_storage_size::StorageSizeResp; + pub use liquid_asset::LiquidAsset; + pub use membership_tier_response::MembershipTierResponse; + pub use params_resp::ParamsResp; + pub use total_value_per_asset_resp::GetLiquidAssetsResp; + pub use user_value_response::UserValueResponse; + + mod staked_assets_response; + pub use staked_assets_response::StakeAssetBalanceBreakdown; + pub use staked_assets_response::StakedAssetsResponse; + + pub mod earn { + mod get_eden_boost_earn_details_resp; + pub use get_eden_boost_earn_details_resp::GetEdenBoostEarnProgramResp; + mod get_eden_earn_details_resp; + pub use get_eden_earn_details_resp::GetEdenEarnProgramResp; + mod get_elys_earn_details_resp; + pub use get_elys_earn_details_resp::GetElysEarnProgramResp; + mod get_usdc_earn_details_resp; + pub use get_usdc_earn_details_resp::GetUsdcEarnProgramResp; + } + + pub mod estaking { + mod get_estaking_rewards_response; + pub use get_estaking_rewards_response::GetEstakingRewardsResponse; + } + + pub mod masterchef { + mod get_masterchef_pending_rewards; + mod get_masterchef_pool_apr_response; + mod get_masterchef_stable_stake_response; + pub use get_masterchef_pending_rewards::GetMasterchefUserPendingRewardResponse; + pub use get_masterchef_pool_apr_response::MasterChefPoolAprResponse; + pub use get_masterchef_stable_stake_response::StableStakeAprResponse; + } +} diff --git a/bindings/src/account_history/msg/query.rs b/bindings/src/account_history/msg/query.rs new file mode 100644 index 00000000..7c4d7d38 --- /dev/null +++ b/bindings/src/account_history/msg/query.rs @@ -0,0 +1,169 @@ +#[allow(unused_imports)] +use super::super::types::{PerpetualAssets, PortfolioBalanceSnapshot}; +#[allow(unused_imports)] +use super::query_resp::earn::*; +#[allow(unused_imports)] +use super::query_resp::estaking::*; +#[allow(unused_imports)] +use super::query_resp::masterchef::*; + +#[allow(unused_imports)] +use super::query_resp::*; +#[allow(unused_imports)] +use crate::query_resp::QueryStableStakeAprResponse; +#[allow(unused_imports)] +use crate::query_resp::{ + AuthAddressesResponse, BalanceBorrowed, MasterchefParamsResponse, MasterchefPoolInfoResponse, + PoolFilterType, QueryAprsResponse, QueryEarnPoolResponse, QueryExitPoolEstimationResponse, + QueryJoinPoolEstimationResponse, QueryPoolAssetEstimationResponse, QueryStakedPositionResponse, + QueryUnstakedPositionResponse, QueryUserPoolResponse, QueryVestingInfoResponse, + StableStakeParamsData, StakedAvailable, +}; +#[allow(unused_imports)] +use crate::types::{BalanceAvailable, PageRequest}; +use cosmwasm_schema::{cw_serde, QueryResponses}; +#[allow(unused_imports)] +use cosmwasm_std::Uint128; +#[cfg(feature = "debug")] +use cosmwasm_std::{Coin, DecCoin, Decimal}; +use cw2::ContractVersion; + +#[cw_serde] +#[derive(QueryResponses)] +pub enum QueryMsg { + #[returns(AuthAddressesResponse)] + Accounts { pagination: Option }, + #[returns(GetLiquidAssetsResp)] + GetLiquidAssets { user_address: String }, + #[returns(StakedAssetsResponse)] + GetStakedAssets { user_address: Option }, + #[returns(QueryUserPoolResponse)] + GetPoolBalances { user_address: String }, + #[returns(GetRewardsResp)] + GetRewards { user_address: String }, + #[returns(MembershipTierResponse)] + GetMembershipTier { user_address: String }, + #[returns(PerpetualAssets)] + GetPerpetualAssets { user_address: String }, + #[returns(Decimal)] + GetAssetPrice { asset: String }, + #[returns(Decimal)] + GetAssetPriceFromDenomInToDenomOut { denom_in: String, denom_out: String }, + + #[returns(QueryEarnPoolResponse)] + GetLiquidityPools { + pool_ids: Option>, + filter_type: PoolFilterType, + pagination: Option, + }, + + #[returns(QueryJoinPoolEstimationResponse)] + JoinPoolEstimation { pool_id: u64, amounts_in: Vec }, + + #[returns(QueryExitPoolEstimationResponse)] + ExitPoolEstimation { + pool_id: u64, + exit_fiat_amount: Decimal, + }, + + #[returns(QueryPoolAssetEstimationResponse)] + PoolAssetEstimation { pool_id: u64, amount: DecCoin }, + + #[returns(GetEstakingRewardsResponse)] + GetEstakingRewards { address: String }, + + #[returns(MasterchefParamsResponse)] + GetMasterchefParams {}, + + #[returns(MasterchefPoolInfoResponse)] + GetMasterchefPoolInfo { pool_id: u64 }, + + #[returns(GetMasterchefUserPendingRewardResponse)] + GetMasterchefPendingRewards { address: String }, + + #[returns(QueryStableStakeAprResponse)] + GetMasterchefStableStakeApr { denom: String }, + + #[returns(MasterChefPoolAprResponse)] + GetMasterChefPoolApr { pool_ids: Vec }, + // debug only + #[cfg(feature = "debug")] + #[returns(ParamsResp)] + Params {}, + + #[cfg(feature = "debug")] + #[returns(PortfolioBalanceSnapshot)] + LastSnapshot { user_address: String }, + + #[cfg(feature = "debug")] + #[returns(UserValueResponse)] + UserValue { user_address: String }, + + #[cfg(feature = "debug")] + #[returns(Vec<(String, Vec)>)] + All { pagination: Option }, + + #[cfg(feature = "debug")] + #[returns(Vec)] + UserSnapshots { user_address: String }, + + #[cfg(feature = "debug")] + #[returns(QueryStakedPositionResponse)] + CommitmentStakedPositions { delegator_address: String }, + + #[cfg(feature = "debug")] + #[returns(QueryUnstakedPositionResponse)] + CommitmentUnStakedPositions { delegator_address: String }, + + #[cfg(feature = "debug")] + #[returns(StakedAvailable)] + CommitmentStakedBalanceOfDenom { address: String, denom: String }, + + #[cfg(feature = "debug")] + #[returns(BalanceBorrowed)] + StableStakeBalanceOfBorrow {}, + + #[cfg(feature = "debug")] + #[returns(StableStakeParamsData)] + StableStakeParams {}, + + #[cfg(feature = "debug")] + #[returns(QueryVestingInfoResponse)] + CommitmentVestingInfo { address: String }, + + #[cfg(feature = "debug")] + #[returns(BalanceAvailable)] + Balance { address: String, denom: String }, + + #[cfg(feature = "debug")] + #[returns(Decimal)] + AmmPriceByDenom { token_in: Coin, discount: Decimal }, + + #[cfg(feature = "debug")] + #[returns(GetEdenEarnProgramResp)] + GetEdenEarnProgramDetails { address: String }, + + #[cfg(feature = "debug")] + #[returns(GetEdenBoostEarnProgramResp)] + GetEdenBoostEarnProgramDetails { address: String }, + + #[cfg(feature = "debug")] + #[returns(GetElysEarnProgramResp)] + GetElysEarnProgramDetails { address: String }, + + #[cfg(feature = "debug")] + #[returns(GetUsdcEarnProgramResp)] + GetUsdcEarnProgramDetails { address: String }, + + #[cfg(feature = "debug")] + #[returns(QueryAprsResponse)] + IncentiveAprs {}, + + #[cfg(feature = "debug")] + #[returns(StorageSizeResp)] + StorageSize {}, + + #[cfg(feature = "debug")] + #[returns(ContractVersion)] + Version {}, +} diff --git a/bindings/src/account_history/msg/query_resp/earn/get_eden_boost_earn_details_resp.rs b/bindings/src/account_history/msg/query_resp/earn/get_eden_boost_earn_details_resp.rs new file mode 100644 index 00000000..1e0206b0 --- /dev/null +++ b/bindings/src/account_history/msg/query_resp/earn/get_eden_boost_earn_details_resp.rs @@ -0,0 +1,9 @@ +use crate::account_history::types::earn_program::eden_boost_earn::EdenBoostEarnProgram; + +use cosmwasm_schema::cw_serde; + +#[cw_serde] +#[derive(Default)] +pub struct GetEdenBoostEarnProgramResp { + pub data: EdenBoostEarnProgram, +} diff --git a/bindings/src/account_history/msg/query_resp/earn/get_eden_earn_details_resp.rs b/bindings/src/account_history/msg/query_resp/earn/get_eden_earn_details_resp.rs new file mode 100644 index 00000000..9d69ef09 --- /dev/null +++ b/bindings/src/account_history/msg/query_resp/earn/get_eden_earn_details_resp.rs @@ -0,0 +1,9 @@ +use cosmwasm_schema::cw_serde; + +use crate::account_history::types::earn_program::eden_earn::EdenEarnProgram; + +#[cw_serde] +#[derive(Default)] +pub struct GetEdenEarnProgramResp { + pub data: EdenEarnProgram, +} diff --git a/bindings/src/account_history/msg/query_resp/earn/get_elys_earn_details_resp.rs b/bindings/src/account_history/msg/query_resp/earn/get_elys_earn_details_resp.rs new file mode 100644 index 00000000..b2bead57 --- /dev/null +++ b/bindings/src/account_history/msg/query_resp/earn/get_elys_earn_details_resp.rs @@ -0,0 +1,8 @@ +use crate::account_history::types::earn_program::elys_earn::ElysEarnProgram; +use cosmwasm_schema::cw_serde; + +#[cw_serde] +#[derive(Default)] +pub struct GetElysEarnProgramResp { + pub data: ElysEarnProgram, +} diff --git a/bindings/src/account_history/msg/query_resp/earn/get_usdc_earn_details_resp.rs b/bindings/src/account_history/msg/query_resp/earn/get_usdc_earn_details_resp.rs new file mode 100644 index 00000000..ca156b16 --- /dev/null +++ b/bindings/src/account_history/msg/query_resp/earn/get_usdc_earn_details_resp.rs @@ -0,0 +1,8 @@ +use crate::account_history::types::earn_program::usdc_earn::UsdcEarnProgram; +use cosmwasm_schema::cw_serde; + +#[cw_serde] +#[derive(Default)] +pub struct GetUsdcEarnProgramResp { + pub data: UsdcEarnProgram, +} diff --git a/bindings/src/account_history/msg/query_resp/estaking/get_estaking_rewards_response.rs b/bindings/src/account_history/msg/query_resp/estaking/get_estaking_rewards_response.rs new file mode 100644 index 00000000..b1d2a699 --- /dev/null +++ b/bindings/src/account_history/msg/query_resp/estaking/get_estaking_rewards_response.rs @@ -0,0 +1,16 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::Coin; + +use crate::account_history::types::CoinValue; + +#[cw_serde] +pub struct GetEstakingRewardsResponse { + pub rewards: Vec, + pub total: Vec, +} + +#[cw_serde] +pub struct DelegationDelegatorReward { + pub validator_address: String, + pub reward: Vec, +} diff --git a/bindings/src/account_history/msg/query_resp/get_all_resp.rs b/bindings/src/account_history/msg/query_resp/get_all_resp.rs new file mode 100644 index 00000000..6f18ecc2 --- /dev/null +++ b/bindings/src/account_history/msg/query_resp/get_all_resp.rs @@ -0,0 +1,9 @@ +use crate::account_history::types::PortfolioBalanceSnapshot; +use crate::types::PageResponse; +use cosmwasm_schema::cw_serde; + +#[cw_serde] +pub struct GetAllResp { + pub snapshot_list: Vec<(String, PortfolioBalanceSnapshot)>, + pub pagination: Option, +} diff --git a/bindings/src/account_history/msg/query_resp/get_rewards_resp.rs b/bindings/src/account_history/msg/query_resp/get_rewards_resp.rs new file mode 100644 index 00000000..6afdc875 --- /dev/null +++ b/bindings/src/account_history/msg/query_resp/get_rewards_resp.rs @@ -0,0 +1,9 @@ +use crate::account_history::types::{CoinValue, Reward}; + +use cosmwasm_schema::cw_serde; + +#[cw_serde] +pub struct GetRewardsResp { + pub rewards_map: Reward, + pub rewards: Vec, +} diff --git a/bindings/src/account_history/msg/query_resp/get_storage_size.rs b/bindings/src/account_history/msg/query_resp/get_storage_size.rs new file mode 100644 index 00000000..dff0900c --- /dev/null +++ b/bindings/src/account_history/msg/query_resp/get_storage_size.rs @@ -0,0 +1,8 @@ +use cosmwasm_schema::cw_serde; + +#[cw_serde] +pub struct StorageSizeResp { + pub user_address_queue_data_size: u128, + pub history_data_size: u128, + pub old_history_2_data_size: u128, +} diff --git a/bindings/src/account_history/msg/query_resp/liquid_asset.rs b/bindings/src/account_history/msg/query_resp/liquid_asset.rs new file mode 100644 index 00000000..1e264d33 --- /dev/null +++ b/bindings/src/account_history/msg/query_resp/liquid_asset.rs @@ -0,0 +1,14 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::Decimal; + +#[cw_serde] +pub struct LiquidAsset { + pub denom: String, + pub price: Decimal, + pub available_amount: Decimal, + pub available_value: Decimal, + pub in_order_amount: Decimal, + pub in_order_value: Decimal, + pub total_amount: Decimal, + pub total_value: Decimal, +} diff --git a/bindings/src/account_history/msg/query_resp/masterchef/get_masterchef_pending_rewards.rs b/bindings/src/account_history/msg/query_resp/masterchef/get_masterchef_pending_rewards.rs new file mode 100644 index 00000000..b3d30886 --- /dev/null +++ b/bindings/src/account_history/msg/query_resp/masterchef/get_masterchef_pending_rewards.rs @@ -0,0 +1,12 @@ +use std::collections::HashMap; + +use cosmwasm_schema::cw_serde; + +use crate::account_history::types::CoinValue; + +#[cw_serde] +#[derive(Default)] +pub struct GetMasterchefUserPendingRewardResponse { + pub rewards: HashMap>, + pub total_rewards: Vec, +} diff --git a/bindings/src/account_history/msg/query_resp/masterchef/get_masterchef_pool_apr_response.rs b/bindings/src/account_history/msg/query_resp/masterchef/get_masterchef_pool_apr_response.rs new file mode 100644 index 00000000..e523edfc --- /dev/null +++ b/bindings/src/account_history/msg/query_resp/masterchef/get_masterchef_pool_apr_response.rs @@ -0,0 +1,8 @@ +use cosmwasm_schema::cw_serde; + +use crate::query_resp::PoolApr; + +#[cw_serde] +pub struct MasterChefPoolAprResponse { + pub data: Vec, +} diff --git a/bindings/src/account_history/msg/query_resp/masterchef/get_masterchef_stable_stake_response.rs b/bindings/src/account_history/msg/query_resp/masterchef/get_masterchef_stable_stake_response.rs new file mode 100644 index 00000000..21fd032d --- /dev/null +++ b/bindings/src/account_history/msg/query_resp/masterchef/get_masterchef_stable_stake_response.rs @@ -0,0 +1,7 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::Int128; + +#[cw_serde] +pub struct StableStakeAprResponse { + pub apr: Int128, +} diff --git a/bindings/src/account_history/msg/query_resp/membership_tier_response.rs b/bindings/src/account_history/msg/query_resp/membership_tier_response.rs new file mode 100644 index 00000000..dee6a005 --- /dev/null +++ b/bindings/src/account_history/msg/query_resp/membership_tier_response.rs @@ -0,0 +1,51 @@ +use std::str::FromStr; + +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Decimal, Decimal256}; + +// Tier fee discount is +// Bronze tier: standard ( no discount) +// Silver tier: > $50k balance ( 10% discount) +// Gold tier: > $250K balance ( 20% discount) +// Platinum tier: > $500K balance ( 30% discount) + +#[cw_serde] +pub struct MembershipTierResponse { + pub identifier: String, + pub name: String, + pub discount: Decimal, +} + +impl MembershipTierResponse { + pub fn zero() -> Self { + Self::calc(Decimal256::zero()) + } + + pub fn calc(balance: Decimal256) -> Self { + if balance > Decimal256::from_str("500000").unwrap() { + Self { + identifier: "platinum".to_string(), + name: "Platinum".to_string(), + discount: Decimal::from_str("0.3").unwrap(), + } + } else if balance > Decimal256::from_str("250000").unwrap() { + Self { + identifier: "gold".to_string(), + name: "Gold".to_string(), + discount: Decimal::from_str("0.2").unwrap(), + } + } else if balance > Decimal256::from_str("50000").unwrap() { + Self { + identifier: "silver".to_string(), + name: "Silver".to_string(), + discount: Decimal::from_str("0.1").unwrap(), + } + } else { + Self { + identifier: "bronze".to_string(), + name: "Bronze".to_string(), + discount: Decimal::zero(), + } + } + } +} diff --git a/bindings/src/account_history/msg/query_resp/params_resp.rs b/bindings/src/account_history/msg/query_resp/params_resp.rs new file mode 100644 index 00000000..71390d85 --- /dev/null +++ b/bindings/src/account_history/msg/query_resp/params_resp.rs @@ -0,0 +1,14 @@ +use crate::account_history::types::Metadata; +use cosmwasm_schema::cw_serde; +use cw_utils::Expiration; + +#[cw_serde] +pub struct ParamsResp { + pub expiration: Expiration, + pub processed_account_per_block: u64, + pub trade_shield_address: Option, + pub update_account_enabled: bool, + pub metadata: Metadata, + pub delete_old_data_enabled: bool, + pub delete_epoch: u64, +} diff --git a/bindings/src/account_history/msg/query_resp/staked_assets_response.rs b/bindings/src/account_history/msg/query_resp/staked_assets_response.rs new file mode 100644 index 00000000..4c03f2f4 --- /dev/null +++ b/bindings/src/account_history/msg/query_resp/staked_assets_response.rs @@ -0,0 +1,34 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{DecCoin, Decimal}; + +use crate::account_history::types::StakedAssets; + +#[cw_serde] +#[derive(Default)] +pub struct StakeAssetBalanceBreakdown { + pub staked: Decimal, + pub unstaking: Decimal, + pub vesting: Decimal, +} + +#[cw_serde] +pub struct StakedAssetsResponse { + pub total_staked_balance: DecCoin, + pub staked_assets: StakedAssets, + pub total_balance: Decimal, + pub balance_break_down: StakeAssetBalanceBreakdown, +} + +impl StakeAssetBalanceBreakdown { + pub fn total(&self) -> Decimal { + let total = self + .staked + .checked_add(self.unstaking) + .and_then(|sum| sum.checked_add(self.vesting)); + + match total { + Ok(result) => result, + Err(_) => Decimal::zero(), + } + } +} diff --git a/bindings/src/account_history/msg/query_resp/total_value_per_asset_resp.rs b/bindings/src/account_history/msg/query_resp/total_value_per_asset_resp.rs new file mode 100644 index 00000000..ad2182cd --- /dev/null +++ b/bindings/src/account_history/msg/query_resp/total_value_per_asset_resp.rs @@ -0,0 +1,10 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::DecCoin; + +use super::LiquidAsset; + +#[cw_serde] +pub struct GetLiquidAssetsResp { + pub liquid_assets: Vec, + pub total_liquid_asset_balance: DecCoin, +} diff --git a/bindings/src/account_history/msg/query_resp/user_value_response.rs b/bindings/src/account_history/msg/query_resp/user_value_response.rs new file mode 100644 index 00000000..1300dad6 --- /dev/null +++ b/bindings/src/account_history/msg/query_resp/user_value_response.rs @@ -0,0 +1,7 @@ +use crate::account_history::types::PortfolioBalanceSnapshot; +use cosmwasm_schema::cw_serde; + +#[cw_serde] +pub struct UserValueResponse { + pub value: PortfolioBalanceSnapshot, +} diff --git a/bindings/src/account_history/msg/sudo.rs b/bindings/src/account_history/msg/sudo.rs new file mode 100644 index 00000000..ea31f9fd --- /dev/null +++ b/bindings/src/account_history/msg/sudo.rs @@ -0,0 +1,6 @@ +use cosmwasm_schema::cw_serde; + +#[cw_serde] +pub enum SudoMsg { + ClockEndBlock {}, +} diff --git a/bindings/src/account_history/types/account_snapshot.rs b/bindings/src/account_history/types/account_snapshot.rs new file mode 100644 index 00000000..5a86c6ad --- /dev/null +++ b/bindings/src/account_history/types/account_snapshot.rs @@ -0,0 +1,44 @@ +use cosmwasm_schema::cw_serde; +use cw_utils::Expiration; + +use super::{LiquidAsset, PerpetualAssets, PoolBalances, Reward, StakedAssets, TotalBalance}; + +#[cw_serde] +pub struct AccountSnapshot { + pub date: Expiration, + pub total_balance: TotalBalance, + pub reward: Reward, + pub pool_balances: PoolBalances, + pub liquid_asset: LiquidAsset, + pub staked_assets: StakedAssets, + pub perpetual_assets: PerpetualAssets, +} + +impl AccountSnapshot { + pub fn zero(value_denom: &String) -> Self { + Self { + date: Expiration::Never {}, + total_balance: TotalBalance::zero(value_denom), + reward: Reward::default(), + pool_balances: PoolBalances::default(), + liquid_asset: LiquidAsset::zero(value_denom), + staked_assets: StakedAssets::default(), + perpetual_assets: PerpetualAssets::default(), + } + } +} + +// implement default +impl Default for AccountSnapshot { + fn default() -> Self { + Self { + date: Expiration::Never {}, + total_balance: TotalBalance::default(), + reward: Reward::default(), + pool_balances: PoolBalances::default(), + liquid_asset: LiquidAsset::default(), + staked_assets: StakedAssets::default(), + perpetual_assets: PerpetualAssets::default(), + } + } +} diff --git a/bindings/src/account_history/types/coin_value.rs b/bindings/src/account_history/types/coin_value.rs new file mode 100644 index 00000000..f45ae0ca --- /dev/null +++ b/bindings/src/account_history/types/coin_value.rs @@ -0,0 +1,120 @@ +use crate::{query_resp::OracleAssetInfoResponse, types::OracleAssetInfo, ElysQuerier}; +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Coin, Decimal, StdError, StdResult}; + +use super::ElysDenom; + +#[cw_serde] +#[derive(Default)] +pub struct CoinValue { + pub denom: String, + pub amount_token: Decimal, + pub price: Decimal, + pub amount_usd: Decimal, +} + +impl CoinValue { + pub fn new(denom: String, amount_token: Decimal, price: Decimal, amount_usd: Decimal) -> Self { + Self { + denom, + amount_token, + price, + amount_usd, + } + } + + pub fn from_price_and_coin( + balance: &Coin, + price_and_decimal_point: (Decimal, u64), + ) -> StdResult { + let amount_token = Decimal::from_atomics(balance.amount, price_and_decimal_point.1 as u32) + .map_err(|e| { + StdError::generic_err(format!("failed to convert amount to Decimal: {}", e)) + })?; + + let amount_usd = price_and_decimal_point + .0 + .clone() + .checked_mul( + Decimal::from_atomics(balance.amount, price_and_decimal_point.1 as u32).map_err( + |e| { + StdError::generic_err(format!( + "failed to convert amount_usd_base to Decimal: {}", + e + )) + }, + )?, + ) + .map_err(|e| { + StdError::generic_err(format!( + "failed to convert amount_usd_base to Decimal: {}", + e + )) + })?; + + Ok(Self { + denom: balance.denom.clone(), + amount_token, + price: price_and_decimal_point.0, + amount_usd, + }) + } + + pub fn from_coin(balance: &Coin, querier: &ElysQuerier<'_>) -> StdResult { + let OracleAssetInfoResponse { asset_info } = querier + .asset_info(balance.denom.clone()) + .unwrap_or(OracleAssetInfoResponse { + asset_info: OracleAssetInfo { + denom: balance.denom.clone(), + display: balance.denom.clone(), + band_ticker: balance.denom.clone(), + elys_ticker: balance.denom.clone(), + decimal: 6, + }, + }); + let decimal_point_usd = asset_info.decimal; + + let amount_token = Decimal::from_atomics(balance.amount, decimal_point_usd as u32) + .map_err(|e| { + StdError::generic_err(format!("failed to convert amount to Decimal: {}", e)) + })?; + + // Eden boost does not have Fiat valuation. + if balance.denom == ElysDenom::EdenBoost.as_str() { + return Ok(Self { + amount_token, + denom: balance.denom.clone(), + amount_usd: Decimal::zero(), + price: Decimal::zero(), + }); + } + + let price = querier + .get_asset_price(balance.denom.clone()) + .map_err(|e| StdError::generic_err(format!("failed to get_asset_price: {}", e)))?; + + let amount_usd = price + .clone() + .checked_mul( + Decimal::from_atomics(balance.amount, decimal_point_usd as u32).map_err(|e| { + StdError::generic_err(format!( + "failed to convert amount_usd_base to Decimal: {}", + e + )) + })?, + ) + .map_err(|e| { + StdError::generic_err(format!( + "failed to convert amount_usd_base to Decimal: {}", + e + )) + })?; + + Ok(Self { + denom: balance.denom.clone(), + amount_token, + price, + amount_usd, + }) + } +} diff --git a/bindings/src/account_history/types/denom.rs b/bindings/src/account_history/types/denom.rs new file mode 100644 index 00000000..f7e00013 --- /dev/null +++ b/bindings/src/account_history/types/denom.rs @@ -0,0 +1,33 @@ +use cosmwasm_schema::cw_serde; + +#[cw_serde] +pub enum ElysDenom { + // Elys + Elys, + // Eden + Eden, + // Eden Boost + EdenBoost, + // Usdc + Usdc, + // USDC + USDC, + // ElysSource + ElysSource, + // AnySource + AnySource, +} + +impl ElysDenom { + pub fn as_str(&self) -> &'static str { + match self { + ElysDenom::Elys => "uelys", + ElysDenom::Eden => "ueden", + ElysDenom::EdenBoost => "uedenb", + ElysDenom::Usdc => "uusdc", + ElysDenom::USDC => "USDC", + ElysDenom::ElysSource => "elys", + ElysDenom::AnySource => "", + } + } +} diff --git a/bindings/src/account_history/types/earn_detail/earn_detail.rs b/bindings/src/account_history/types/earn_detail/earn_detail.rs new file mode 100644 index 00000000..dae43827 --- /dev/null +++ b/bindings/src/account_history/types/earn_detail/earn_detail.rs @@ -0,0 +1,117 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Decimal, Int128, Uint128}; + +#[cw_serde] +#[derive(Default)] +pub struct AprEdenBoost { + pub uusdc: Uint128, + pub ueden: Uint128, +} +#[cw_serde] +pub struct AprUsdc { + pub uusdc: Int128, + pub ueden: Int128, +} + +// implement default +impl Default for AprUsdc { + fn default() -> Self { + Self { + uusdc: Int128::zero(), + ueden: Int128::zero(), + } + } +} + +#[cw_serde] +pub struct AprElys { + pub uusdc: Uint128, + pub ueden: Uint128, + pub uedenb: Uint128, +} + +// implement default +impl Default for AprElys { + fn default() -> Self { + Self { + uusdc: Uint128::zero(), + ueden: Uint128::zero(), + uedenb: Uint128::zero(), + } + } +} + +#[cw_serde] +pub struct BalanceBorrowed { + pub usd_amount: Decimal, + pub percentage: Decimal, +} + +// implement default +impl Default for BalanceBorrowed { + fn default() -> Self { + Self { + usd_amount: Decimal::zero(), + percentage: Decimal::zero(), + } + } +} + +#[cw_serde] +pub struct QueryAprResponse { + pub apr: Uint128, +} + +// implement default +impl Default for QueryAprResponse { + fn default() -> Self { + Self { + apr: Uint128::zero(), + } + } +} + +#[cw_serde] +pub struct BalanceReward { + pub asset: String, + pub amount: Uint128, + pub usd_amount: Option, +} + +// implement default +impl Default for BalanceReward { + fn default() -> Self { + Self { + asset: "".to_string(), + amount: Uint128::zero(), + usd_amount: None, + } + } +} + +#[cw_serde] +pub struct StakingValidator { + // The validator identity. + pub id: String, + // The validator address. + pub address: String, + // The validator name. + pub name: String, + // Voting power percentage for this validator. + pub voting_power: Decimal, + // commission percentage for the validator. + pub commission: Decimal, +} + +// implement default +impl Default for StakingValidator { + fn default() -> Self { + Self { + id: "".to_string(), + address: "".to_string(), + name: "".to_string(), + voting_power: Decimal::zero(), + commission: Decimal::zero(), + } + } +} diff --git a/bindings/src/account_history/types/earn_program/eden_boost_earn.rs b/bindings/src/account_history/types/earn_program/eden_boost_earn.rs new file mode 100644 index 00000000..e260fca4 --- /dev/null +++ b/bindings/src/account_history/types/earn_program/eden_boost_earn.rs @@ -0,0 +1,35 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::Uint128; + +use crate::account_history::types::{earn_detail::earn_detail::AprEdenBoost, CoinValue}; + +#[cw_serde] +pub struct EdenBoostEarnProgram { + // should be 0 initially. In days + pub bonding_period: u64, + // The APR For the Eden Boost Earn Program. + pub apr: AprEdenBoost, + // available should be the user Eden Boost liquid balance on Elys and returned + // only if address is included in the request object. + pub available: Option, + // it should return how much Eden Boost the user has staked in this program ONLY. + // it should only be included if address is in the request object. + pub staked: Option, + // The rewards the user currently has on the Eden Boost Earn Program. + // It should be in the response only if the address is in the request object. + // rewards are either USDC or EDEN. + pub rewards: Option>, +} + +// implement default +impl Default for EdenBoostEarnProgram { + fn default() -> Self { + Self { + bonding_period: 0, + apr: AprEdenBoost::default(), + available: None, + staked: None, + rewards: None, + } + } +} diff --git a/bindings/src/account_history/types/earn_program/eden_earn.rs b/bindings/src/account_history/types/earn_program/eden_earn.rs new file mode 100644 index 00000000..c97b4f04 --- /dev/null +++ b/bindings/src/account_history/types/earn_program/eden_earn.rs @@ -0,0 +1,64 @@ +use crate::{ + account_history::types::{AprElys, CoinValue, ElysDenom}, + query_resp::StakedAvailable, + trade_shield::types::BalanceAvailable, + types::VestingDetail, +}; + +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Coin, Decimal, Uint128}; + +#[cw_serde] +pub struct EdenEarnProgram { + // should be 0 initially. In days + pub bonding_period: u64, + // The APR For the EDEN Earn Program. + pub apr: AprElys, + // available should be the user EDEN liquid balance on Elys and returned + // only if address is included in the request object. + pub available: Option, + // it should return how much EDEN the user has staked in this program ONLY. + // it should only be included if address is in the request object. + pub staked: Option, + // The rewards the user currently has on the EDEN Earn Program. + // It should be in the response only if the address is in the request object. + // rewards are either USDC, EDEN or EDEN Boost. + // Eden Boost doesnt have USD amount. + pub rewards: Option>, + // The sum of all the total_vest. + pub vesting: BalanceAvailable, + // A list of all the vesting details for the EDEN program. + // it should only be included if address is in the request object. + pub vesting_details: Option>, +} + +impl EdenEarnProgram { + fn to_coin(&self, amount: Uint128) -> Coin { + Coin::new(u128::from(amount), ElysDenom::Eden.as_str()) + } + + pub fn to_coin_available(&self) -> Coin { + self.to_coin(self.available.clone().unwrap_or_default().amount) + } + + pub fn to_coin_staked(&self) -> Coin { + self.to_coin(self.staked.clone().unwrap_or_default().amount) + } +} +// implement default +impl Default for EdenEarnProgram { + fn default() -> Self { + Self { + bonding_period: 0, + apr: AprElys::default(), + available: None, + staked: None, + rewards: None, + vesting: BalanceAvailable { + amount: Uint128::zero(), + usd_amount: Decimal::zero(), + }, + vesting_details: None, + } + } +} diff --git a/bindings/src/account_history/types/earn_program/elys_earn.rs b/bindings/src/account_history/types/earn_program/elys_earn.rs new file mode 100644 index 00000000..e389c301 --- /dev/null +++ b/bindings/src/account_history/types/earn_program/elys_earn.rs @@ -0,0 +1,46 @@ +use crate::{ + account_history::types::{AprElys, CoinValue}, + query_resp::StakedAvailable, + types::{BalanceAvailable, StakedPosition, UnstakedPosition}, +}; + +use cosmwasm_schema::cw_serde; + +#[cw_serde] +pub struct ElysEarnProgram { + // should be 0 initially. In days + pub bonding_period: u64, + // The APR For the Elys Earn Program. + pub apr: AprElys, + // available should be the user Elys liquid balance on Elys and returned + // only if address is included in the request object. + pub available: Option, + // it should return how much Elys the user has staked in this program ONLY. + // it should only be included if address is in the request object. + pub staked: Option, + // The rewards the user currently has on the Elys Earn Program. + // It should be in the response only if the address is in the request object. + // rewards are either USDC, EDEN or EDEN Boost. + pub rewards: Option>, + // All the positions the user has staked on the ELYS program. + // It should be in the response only if the address is in the request object. + pub staked_positions: Option>, + // The positions the user has decided to unstake. + // It should be in the response only if the address is in the request object. + pub unstaked_positions: Option>, +} + +// implement default +impl Default for ElysEarnProgram { + fn default() -> Self { + Self { + bonding_period: 14, + apr: AprElys::default(), + available: None, + staked: None, + rewards: None, + staked_positions: None, + unstaked_positions: None, + } + } +} diff --git a/bindings/src/account_history/types/earn_program/usdc_earn.rs b/bindings/src/account_history/types/earn_program/usdc_earn.rs new file mode 100644 index 00000000..d0b13493 --- /dev/null +++ b/bindings/src/account_history/types/earn_program/usdc_earn.rs @@ -0,0 +1,41 @@ +use crate::{ + account_history::types::{AprUsdc, CoinValue}, + query_resp::{BalanceBorrowed, StakedAvailable}, + types::BalanceAvailable, +}; + +use cosmwasm_schema::cw_serde; + +#[cw_serde] +pub struct UsdcEarnProgram { + // should be 0 initially. In days + pub bonding_period: u64, + // The APR For the USDC Earn Program. + pub apr: AprUsdc, + // available should be the user USDC liquid balance on Elys and returned + // only if address is included in the request object. + pub available: Option, + // it should return how much USDC the user has staked in this program ONLY. + // it should only be included if address is in the request object. + pub staked: Option, + // The rewards the user currently has on the USDC Earn Program. + // It should be in the response only if the address is in the request object. + // rewards are either USDC or EDEN. + pub rewards: Option>, + // The amount that has been borrowed from the user staked positions. + pub borrowed: Option, +} + +// implement default +impl Default for UsdcEarnProgram { + fn default() -> Self { + Self { + bonding_period: 0, + apr: AprUsdc::default(), + available: None, + staked: None, + rewards: None, + borrowed: None, + } + } +} diff --git a/bindings/src/account_history/types/liquid_asset.rs b/bindings/src/account_history/types/liquid_asset.rs new file mode 100644 index 00000000..60b473a1 --- /dev/null +++ b/bindings/src/account_history/types/liquid_asset.rs @@ -0,0 +1,35 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{DecCoin, Decimal256}; + +use super::CoinValue; + +#[cw_serde] +pub struct LiquidAsset { + pub total_liquid_asset_balance: DecCoin, + pub total_available_balance: DecCoin, + pub total_in_orders_balance: DecCoin, + pub available_asset_balance: Vec, + pub in_orders_asset_balance: Vec, + pub total_value_per_asset: Vec, +} + +// implement zero +impl LiquidAsset { + pub fn zero(value_denom: &String) -> Self { + Self { + total_liquid_asset_balance: DecCoin::new(Decimal256::zero(), value_denom.to_string()), + total_available_balance: DecCoin::new(Decimal256::zero(), value_denom.to_string()), + total_in_orders_balance: DecCoin::new(Decimal256::zero(), value_denom.to_string()), + available_asset_balance: vec![], + in_orders_asset_balance: vec![], + total_value_per_asset: vec![], + } + } +} + +// implement default +impl Default for LiquidAsset { + fn default() -> Self { + Self::zero(&"usdc".to_string()) + } +} diff --git a/bindings/src/account_history/types/metadata.rs b/bindings/src/account_history/types/metadata.rs new file mode 100644 index 00000000..2cd89353 --- /dev/null +++ b/bindings/src/account_history/types/metadata.rs @@ -0,0 +1,144 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Decimal, StdError, StdResult, Uint128}; + +use crate::{query_resp::QueryAprResponse, ElysQuerier}; + +use super::ElysDenom; + +#[cw_serde] +pub struct Metadata { + pub usdc_denom: String, + pub usdc_base_denom: String, + pub usdc_display_denom: String, + pub usdc_decimal: u64, + pub eden_decimal: u64, + pub usdc_apr_usdc: QueryAprResponse, + pub eden_apr_usdc: QueryAprResponse, + pub usdc_apr_edenb: QueryAprResponse, + pub eden_apr_edenb: QueryAprResponse, + pub usdc_apr_eden: QueryAprResponse, + pub eden_apr_eden: QueryAprResponse, + pub edenb_apr_eden: QueryAprResponse, + pub usdc_apr_elys: QueryAprResponse, + pub eden_apr_elys: QueryAprResponse, + pub edenb_apr_elys: QueryAprResponse, + pub uusdc_usd_price: Decimal, + pub uelys_price_in_uusdc: Decimal, +} + +impl Metadata { + pub fn update_prices(&self, querier: &ElysQuerier) -> StdResult { + let usdc_oracle_price = querier + .get_oracle_price( + self.usdc_display_denom.clone(), + ElysDenom::AnySource.as_str().to_string(), + 0, + ) + .map_err(|_| StdError::generic_err("an error occurred while getting usdc price"))?; + let uusdc_usd_price = usdc_oracle_price + .price + .price + .checked_div( + Decimal::from_atomics(Uint128::new(self.usdc_decimal as u128), 0) + .map_or(Decimal::zero(), |res| res), + ) + .map_or(Decimal::zero(), |res| res); + let uelys_price_in_uusdc = querier.get_asset_price(ElysDenom::Elys.as_str())?; + + Ok(Self { + uusdc_usd_price, + uelys_price_in_uusdc, + ..self.clone() + }) + } + + pub fn collect(querier: &ElysQuerier) -> StdResult { + let usdc_denom_entry = querier + .get_asset_profile(ElysDenom::Usdc.as_str().to_string()) + .map_err(|_| StdError::generic_err("an error occurred while getting usdc denom"))?; + let usdc_denom = usdc_denom_entry.entry.denom; + let usdc_base_denom = usdc_denom_entry.entry.base_denom; + let usdc_display_denom = usdc_denom_entry.entry.display_name; + let usdc_decimal = + u64::checked_pow(10, usdc_denom_entry.entry.decimals as u32).map_or(0, |res| res); + + let eden_denom_entry = querier + .get_asset_profile(ElysDenom::Eden.as_str().to_string()) + .map_err(|_| StdError::generic_err("an error occurred while getting eden denom"))?; + + let aprs = querier.get_incentive_aprs().unwrap_or_default(); + + Ok(Self { + usdc_denom, + usdc_base_denom, + usdc_display_denom, + usdc_decimal, + eden_decimal: u64::checked_pow(10, eden_denom_entry.entry.decimals as u32) + .map_or(0, |res| res), + + // APR section + usdc_apr_usdc: QueryAprResponse { + apr: aprs.usdc_apr_usdc, + }, + eden_apr_usdc: QueryAprResponse { + apr: aprs.eden_apr_usdc, + }, + + usdc_apr_edenb: QueryAprResponse { + apr: aprs.usdc_apr_edenb, + }, + eden_apr_edenb: QueryAprResponse { + apr: aprs.eden_apr_edenb, + }, + + usdc_apr_eden: QueryAprResponse { + apr: aprs.usdc_apr_eden, + }, + eden_apr_eden: QueryAprResponse { + apr: aprs.eden_apr_eden, + }, + edenb_apr_eden: QueryAprResponse { + apr: aprs.edenb_apr_eden, + }, + + usdc_apr_elys: QueryAprResponse { + apr: aprs.usdc_apr_elys, + }, + eden_apr_elys: QueryAprResponse { + apr: aprs.eden_apr_elys, + }, + edenb_apr_elys: QueryAprResponse { + apr: aprs.edenb_apr_elys, + }, + + // prices + uusdc_usd_price: Decimal::zero(), + uelys_price_in_uusdc: Decimal::zero(), + }) + } +} + +// default +impl Default for Metadata { + fn default() -> Self { + Metadata { + usdc_denom: "usdc".to_string(), + usdc_base_denom: "uusdc".to_string(), + usdc_display_denom: "USDC".to_string(), + usdc_decimal: 6, + eden_decimal: 6, + usdc_apr_usdc: QueryAprResponse::default(), + eden_apr_usdc: QueryAprResponse::default(), + usdc_apr_edenb: QueryAprResponse::default(), + eden_apr_edenb: QueryAprResponse::default(), + usdc_apr_eden: QueryAprResponse::default(), + eden_apr_eden: QueryAprResponse::default(), + edenb_apr_eden: QueryAprResponse::default(), + usdc_apr_elys: QueryAprResponse::default(), + eden_apr_elys: QueryAprResponse::default(), + edenb_apr_elys: QueryAprResponse::default(), + uusdc_usd_price: Decimal::zero(), + uelys_price_in_uusdc: Decimal::zero(), + } + } +} diff --git a/bindings/src/account_history/types/mod.rs b/bindings/src/account_history/types/mod.rs new file mode 100644 index 00000000..55ab0d64 --- /dev/null +++ b/bindings/src/account_history/types/mod.rs @@ -0,0 +1,47 @@ +mod account_snapshot; +mod coin_value; +mod liquid_asset; +mod metadata; +mod perpetual_assets; +mod pool_balances; +mod portfolio_balance_snapshot; +mod reward; +mod staked_assets; +mod total_balance; + +pub use account_snapshot::AccountSnapshot; +pub use coin_value::CoinValue; +pub use metadata::Metadata; +pub use portfolio_balance_snapshot::PortfolioBalanceSnapshot; + +pub mod earn_detail { + pub mod earn_detail; +} +pub use earn_detail::earn_detail::{ + AprElys, AprUsdc, BalanceBorrowed, BalanceReward, QueryAprResponse, StakingValidator, +}; + +pub mod earn_program { + pub mod eden_boost_earn; + pub use eden_boost_earn::EdenBoostEarnProgram; + + pub mod eden_earn; + pub use eden_earn::EdenEarnProgram; + + pub mod elys_earn; + pub use elys_earn::ElysEarnProgram; + + pub mod usdc_earn; + pub use usdc_earn::UsdcEarnProgram; +} + +pub mod denom; +pub use denom::ElysDenom; + +pub use total_balance::TotalBalance; + +pub use liquid_asset::LiquidAsset; +pub use perpetual_assets::*; +pub use pool_balances::PoolBalances; +pub use reward::Reward; +pub use staked_assets::StakedAssets; diff --git a/bindings/src/account_history/types/perpetual_assets.rs b/bindings/src/account_history/types/perpetual_assets.rs new file mode 100644 index 00000000..86b27933 --- /dev/null +++ b/bindings/src/account_history/types/perpetual_assets.rs @@ -0,0 +1,118 @@ +use crate::trade_shield::types::PerpetualPositionPlus; +use crate::types::PerpetualPosition; +use crate::ElysQuerier; +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{DecCoin, Decimal, Decimal256, SignedDecimal, StdError, StdResult, Uint128}; + +#[cw_serde] +pub struct PerpetualAssets { + pub total_perpetual_asset_balance: DecCoin, + pub perpetual_asset: Vec, +} + +#[cw_serde] +pub struct PerpetualAsset { + pub id: u64, + pub denom: String, + pub position: PerpetualPosition, + pub pnl: SignedDecimal, + pub collateral: DecCoin, + pub leverage: SignedDecimal, + pub size: DecCoin, + pub order_price: SignedDecimal, + pub liquidation: SignedDecimal, + pub health: SignedDecimal, + pub profit_price: DecCoin, + pub stop_loss: Option, + pub fees: Decimal, +} + +impl PerpetualAsset { + pub fn new( + mtp: PerpetualPositionPlus, + usdc_denom: String, + querier: &ElysQuerier<'_>, + ) -> StdResult { + let collateral_info = querier.asset_info(mtp.mtp.collateral_asset.clone())?; + let trading_asset_info = querier.asset_info(mtp.mtp.trading_asset.clone())?; + + Ok(PerpetualAsset { + id: mtp.mtp.id, + denom: mtp.mtp.collateral_asset.clone(), + position: PerpetualPosition::try_from_i32(mtp.mtp.position)?, + pnl: mtp.unrealized_pnl, + collateral: DecCoin { + denom: mtp.mtp.collateral_asset.clone(), + amount: Decimal256::from( + Decimal::from_atomics( + Uint128::new(mtp.mtp.collateral.i128() as u128), + collateral_info.asset_info.decimal as u32, + ) + .map_err(|e| { + StdError::generic_err(format!( + "failed to convert collateral to Decimal256: {}", + e + )) + })?, + ), + }, + leverage: mtp.mtp.leverage, + size: DecCoin { + denom: mtp.mtp.trading_asset.clone(), + amount: Decimal256::from( + Decimal::from_atomics( + Uint128::new(mtp.mtp.custody.i128() as u128), + trading_asset_info.asset_info.decimal as u32, + ) + .map_err(|e| { + StdError::generic_err(format!( + "failed to convert custody to Decimal256: {}", + e + )) + })?, + ), + }, + order_price: mtp.mtp.open_price, + liquidation: mtp.liquidation_price, + health: mtp.mtp.mtp_health, + profit_price: DecCoin { + denom: mtp.mtp.collateral_asset.clone(), + amount: Decimal256::try_from(mtp.mtp.take_profit_price).map_err(|e| { + StdError::generic_err(format!( + "failed to convert take_profit_price to Decimal256: {}", + e + )) + })?, + }, + stop_loss: match mtp.stop_loss_price { + Some(stop_loss) => Some(DecCoin { + amount: Decimal256::from(stop_loss.rate), + denom: usdc_denom.to_owned(), + }), + None => None, + }, + fees: Decimal::from_atomics( + Uint128::new(mtp.mtp.borrow_interest_paid_collateral.i128() as u128), + collateral_info.asset_info.decimal as u32, + ) + .map_err(|e| { + StdError::generic_err(format!( + "failed to convert borrow_interest_paid_collateral to Decimal256: {}", + e + )) + })?, + }) + } +} + +impl PerpetualAssets { + pub fn default() -> Self { + Self { + total_perpetual_asset_balance: DecCoin { + denom: "uusd".to_owned(), + amount: Decimal256::zero(), + }, + perpetual_asset: vec![], + } + } +} diff --git a/bindings/src/account_history/types/pool_balances.rs b/bindings/src/account_history/types/pool_balances.rs new file mode 100644 index 00000000..6fd886fa --- /dev/null +++ b/bindings/src/account_history/types/pool_balances.rs @@ -0,0 +1,13 @@ +use crate::query_resp::UserPoolResp; +use cosmwasm_schema::cw_serde; + +#[cw_serde] +pub struct PoolBalances { + pub balances: Vec, +} + +impl Default for PoolBalances { + fn default() -> Self { + Self { balances: vec![] } + } +} diff --git a/bindings/src/account_history/types/portfolio.rs b/bindings/src/account_history/types/portfolio.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/bindings/src/account_history/types/portfolio.rs @@ -0,0 +1 @@ + diff --git a/bindings/src/account_history/types/portfolio_balance_snapshot.rs b/bindings/src/account_history/types/portfolio_balance_snapshot.rs new file mode 100644 index 00000000..10908f7a --- /dev/null +++ b/bindings/src/account_history/types/portfolio_balance_snapshot.rs @@ -0,0 +1,24 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::Decimal256; +use cw_utils::Expiration; + +#[cw_serde] +pub struct PortfolioBalanceSnapshot { + pub date: Expiration, + pub total_balance_usd: Decimal256, +} + +impl PortfolioBalanceSnapshot { + pub fn zero() -> Self { + Self { + date: Expiration::Never {}, + total_balance_usd: Decimal256::zero(), + } + } +} + +impl Default for PortfolioBalanceSnapshot { + fn default() -> Self { + Self::zero() + } +} diff --git a/bindings/src/account_history/types/reward.rs b/bindings/src/account_history/types/reward.rs new file mode 100644 index 00000000..086fbd43 --- /dev/null +++ b/bindings/src/account_history/types/reward.rs @@ -0,0 +1,24 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::Decimal; + +#[cw_serde] +pub struct Reward { + pub usdc_usd: Decimal, + pub eden_usd: Decimal, + pub eden_boost: Decimal, + pub other_usd: Decimal, + pub total_usd: Decimal, +} + +// implement default +impl Default for Reward { + fn default() -> Self { + Self { + usdc_usd: Decimal::zero(), + eden_usd: Decimal::zero(), + eden_boost: Decimal::zero(), + other_usd: Decimal::zero(), + total_usd: Decimal::zero(), + } + } +} diff --git a/bindings/src/account_history/types/staked_asset.rs b/bindings/src/account_history/types/staked_asset.rs new file mode 100644 index 00000000..85f9d003 --- /dev/null +++ b/bindings/src/account_history/types/staked_asset.rs @@ -0,0 +1,13 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::Decimal; +use elys_bindings::types::EarnType; + +#[cw_serde] +pub struct StakedAsset { + pub program: EarnType, + pub bonding_period: u64, + pub apr: Decimal, + pub available: Decimal, + pub staked: Decimal, + pub rewards: Decimal, +} diff --git a/bindings/src/account_history/types/staked_assets.rs b/bindings/src/account_history/types/staked_assets.rs new file mode 100644 index 00000000..c74ba7fa --- /dev/null +++ b/bindings/src/account_history/types/staked_assets.rs @@ -0,0 +1,25 @@ +use cosmwasm_schema::cw_serde; + +use super::earn_program::{ + EdenBoostEarnProgram, EdenEarnProgram, ElysEarnProgram, UsdcEarnProgram, +}; + +#[cw_serde] +pub struct StakedAssets { + pub eden_boost_earn_program: EdenBoostEarnProgram, + pub eden_earn_program: EdenEarnProgram, + pub elys_earn_program: ElysEarnProgram, + pub usdc_earn_program: UsdcEarnProgram, +} + +// implement default +impl Default for StakedAssets { + fn default() -> Self { + Self { + eden_boost_earn_program: EdenBoostEarnProgram::default(), + eden_earn_program: EdenEarnProgram::default(), + elys_earn_program: ElysEarnProgram::default(), + usdc_earn_program: UsdcEarnProgram::default(), + } + } +} diff --git a/bindings/src/account_history/types/total_balance.rs b/bindings/src/account_history/types/total_balance.rs new file mode 100644 index 00000000..0362484f --- /dev/null +++ b/bindings/src/account_history/types/total_balance.rs @@ -0,0 +1,27 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::Decimal256; + +#[cw_serde] +pub struct TotalBalance { + pub total_balance: Decimal256, + pub portfolio_usd: Decimal256, + pub reward_usd: Decimal256, +} + +// implement zero +impl TotalBalance { + pub fn zero(_value_denom: &String) -> Self { + Self { + total_balance: Decimal256::zero(), + portfolio_usd: Decimal256::zero(), + reward_usd: Decimal256::zero(), + } + } +} + +// implement default +impl Default for TotalBalance { + fn default() -> Self { + Self::zero(&"usdc".to_string()) + } +} diff --git a/bindings/src/lib.rs b/bindings/src/lib.rs index ef75216a..84c479a4 100644 --- a/bindings/src/lib.rs +++ b/bindings/src/lib.rs @@ -12,4 +12,6 @@ pub use msg::*; pub use querier::ElysQuerier; pub use query::*; +pub mod account_history; + pub mod trade_shield; diff --git a/contracts/account-history-contract/.cargo/config b/contracts/account-history-contract/.cargo/config new file mode 100644 index 00000000..01cb7727 --- /dev/null +++ b/contracts/account-history-contract/.cargo/config @@ -0,0 +1,4 @@ +[alias] +wasm = "build --release --lib --target wasm32-unknown-unknown" +unit-test = "test --lib" +schema = "run --bin schema" \ No newline at end of file diff --git a/contracts/account-history-contract/Cargo.toml b/contracts/account-history-contract/Cargo.toml new file mode 100644 index 00000000..405e9973 --- /dev/null +++ b/contracts/account-history-contract/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "account-history-contract" +version = "0.0.0" +edition = "2021" + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +default = ["debug"] +debug = [] + +[dependencies] +cosmwasm-schema = { version = "1.1.4", default-features = false } +cosmwasm-std = { version = "1.1.4", features = [ + "staking", +], default-features = false } +serde = { version = "1.0.103", default-features = false, features = ["derive"] } +cw-storage-plus = { version = "1.2.0", default-features = false, features = [ + "iterator", +] } +thiserror = { version = "1", default-features = false } +schemars = { default-features = false, version = "0.8.1" } +cw-utils = { version = "0.13", default-features = false } +cw2 = { version = "1.0.1" } +elys-bindings = { path = "../../bindings" } +chrono = { version = "0.4.33", default-features = false, features = ["alloc"] } +semver = "1.0" + +[dev-dependencies] +trade_shield_contract = { path = "../trade-shield-contract" } +cw-multi-test = "0.13.4" +serde_json = "1.0.107" +elys-bindings = { path = "../../bindings", features = ["testing"] } +elys-bindings-test = { path = "../../bindings-test" } +anyhow = "1" +account-history-contract = { path = ".", features = ["debug"] } +cargo-husky.workspace = true diff --git a/contracts/account-history-contract/src/action/execute/add_user_address_to_queue.rs b/contracts/account-history-contract/src/action/execute/add_user_address_to_queue.rs new file mode 100644 index 00000000..314299df --- /dev/null +++ b/contracts/account-history-contract/src/action/execute/add_user_address_to_queue.rs @@ -0,0 +1,9 @@ +use crate::states::USER_ADDRESS_QUEUE; +use cosmwasm_std::{DepsMut, StdResult}; +use elys_bindings::ElysQuery; + +pub fn add_user_address_to_queue(deps: DepsMut, user_address: String) -> StdResult<()> { + USER_ADDRESS_QUEUE.push_front(deps.storage, &user_address)?; + + Ok(()) +} diff --git a/contracts/account-history-contract/src/action/execute/clean_up_storage.rs b/contracts/account-history-contract/src/action/execute/clean_up_storage.rs new file mode 100644 index 00000000..98a93352 --- /dev/null +++ b/contracts/account-history-contract/src/action/execute/clean_up_storage.rs @@ -0,0 +1,20 @@ +use cosmwasm_std::{DepsMut, Response, StdResult}; + +use crate::states::{HISTORY, OLD_HISTORY_2, USER_ADDRESS_QUEUE}; +use elys_bindings::{ElysMsg, ElysQuery}; + +pub fn clean_up_storage(deps: &mut DepsMut, limit: u64) -> StdResult> { + // Delete history values + for _ in 0..limit { + if let Some(val) = HISTORY.first(deps.storage)? { + HISTORY.remove(deps.storage, &val.0); + } + if let Some(val) = OLD_HISTORY_2.first(deps.storage)? { + OLD_HISTORY_2.remove(deps.storage, &val.0); + } + if !USER_ADDRESS_QUEUE.is_empty(deps.storage).unwrap() { + let _ = USER_ADDRESS_QUEUE.pop_front(deps.storage); + } + } + Ok(Response::default()) +} diff --git a/contracts/account-history-contract/src/action/mod.rs b/contracts/account-history-contract/src/action/mod.rs new file mode 100644 index 00000000..641ed9de --- /dev/null +++ b/contracts/account-history-contract/src/action/mod.rs @@ -0,0 +1,74 @@ +pub mod query { + mod get_liquid_assets; + use crate::error::ContractError; + mod exit_pool_estimation; + mod get_estaking_rewards; + mod get_masterchef_pending_rewards; + mod get_masterchef_pool_apr; + mod get_masterchef_stable_stake_apr; + mod get_membership_tier; + mod get_perpetual_asset; + mod get_pool_balances; + mod get_pools; + mod get_rewards; + mod join_pool_estimation; + mod pool_asset_estimation; + + #[cfg(feature = "debug")] + mod all; + #[cfg(feature = "debug")] + mod last_snapshot; + #[cfg(feature = "debug")] + mod params; + #[cfg(feature = "debug")] + mod user_snapshots; + #[cfg(feature = "debug")] + mod user_value; + + #[cfg(feature = "debug")] + pub use all::all; + #[cfg(feature = "debug")] + pub use last_snapshot::last_snapshot; + #[cfg(feature = "debug")] + pub use params::params; + #[cfg(feature = "debug")] + pub use user_snapshots::user_snapshots; + #[cfg(feature = "debug")] + pub use user_value::user_value; + + pub use exit_pool_estimation::exit_pool_estimation; + pub use get_pool_balances::get_pool_balances; + pub use get_pools::get_pools; + pub use join_pool_estimation::join_pool_estimation; + pub use pool_asset_estimation::pool_asset_estimation; + mod get_eden_boost_earn_program_details; + pub use get_eden_boost_earn_program_details::get_eden_boost_earn_program_details; + pub use get_liquid_assets::get_liquid_assets; + mod get_eden_earn_program_details; + pub use get_eden_earn_program_details::get_eden_earn_program_details; + mod get_elys_earn_program_details; + pub use get_elys_earn_program_details::get_elys_earn_program_details; + mod get_usdc_earn_program_details; + pub use get_usdc_earn_program_details::get_usdc_earn_program_details; + mod get_staked_assets; + pub use get_estaking_rewards::get_estaking_rewards; + pub use get_masterchef_pending_rewards::get_masterchef_pending_rewards; + pub use get_masterchef_pool_apr::get_masterchef_pool_apr; + pub use get_masterchef_stable_stake_apr::get_masterchef_stable_stake_apr; + pub use get_membership_tier::get_membership_tier; + pub use get_perpetual_asset::get_perpetuals_assets; + pub use get_rewards::get_rewards; + pub use get_staked_assets::get_staked_assets; +} + +pub mod execute { + mod add_user_address_to_queue; + mod clean_up_storage; + pub use add_user_address_to_queue::add_user_address_to_queue; + pub use clean_up_storage::clean_up_storage; +} + +pub mod sudo { + mod update_metadata_prices; + pub use update_metadata_prices::update_metadata_prices; +} diff --git a/contracts/account-history-contract/src/action/query/all.rs b/contracts/account-history-contract/src/action/query/all.rs new file mode 100644 index 00000000..cfab3810 --- /dev/null +++ b/contracts/account-history-contract/src/action/query/all.rs @@ -0,0 +1,29 @@ +use cosmwasm_std::{Deps, Order, StdResult}; +use elys_bindings::{ + account_history::{msg::query_resp::GetAllResp, types::PortfolioBalanceSnapshot}, + types::PageRequest, + ElysQuery, +}; + +use crate::states::HISTORY; + +pub fn all(deps: Deps, pagination: Option) -> StdResult { + let snapshot_list: Vec<(String, PortfolioBalanceSnapshot)> = HISTORY + .prefix_range(deps.storage, None, None, Order::Ascending) + .filter_map(|res| res.ok()) + .map(|(key, value)| (key, value)) + .collect(); + + let (snapshot_list, page_response) = match pagination { + Some(pagination) => { + let (snapshot_list, page_resp) = pagination.filter(snapshot_list)?; + (snapshot_list, Some(page_resp)) + } + None => (snapshot_list, None), + }; + + Ok(GetAllResp { + snapshot_list, + pagination: page_response, + }) +} diff --git a/contracts/account-history-contract/src/action/query/exit_pool_estimation.rs b/contracts/account-history-contract/src/action/query/exit_pool_estimation.rs new file mode 100644 index 00000000..4638e508 --- /dev/null +++ b/contracts/account-history-contract/src/action/query/exit_pool_estimation.rs @@ -0,0 +1,39 @@ +use cosmwasm_std::{Decimal, Deps, StdError, StdResult, Uint128}; +use elys_bindings::query_resp::{PoolFilterType, QueryExitPoolEstimationResponse}; +use elys_bindings::{ElysQuerier, ElysQuery}; + +/** + * Given a pool id and a fiat amount, determine the assets the user will + * get in return. + */ +pub fn exit_pool_estimation( + deps: Deps, + pool_id: u64, + exit_fiat_amount: Decimal, +) -> StdResult { + let querier = ElysQuerier::new(&deps.querier); + + let pool_response = + querier.get_all_pools(Some(vec![pool_id]), PoolFilterType::FilterAll as i32, None)?; + let pool = pool_response + .pools + .ok_or(StdError::generic_err("Failed to fetch pool"))? + .first() + .ok_or(StdError::generic_err("Pool not found"))? + .clone(); + let share_price = pool + .share_usd_price + .ok_or(StdError::generic_err("Unable to get share price"))?; + let share_amount = exit_fiat_amount + .checked_div(share_price) + .unwrap_or_default(); + + Ok(querier + .exit_pool_estimation(pool_id, Uint128::new(share_amount.atomics().into())) + .map_err(|err| { + StdError::generic_err(format!( + "exit_pool_estimation: Error getting estimation from chain:{:?}", + err + )) + })?) +} diff --git a/contracts/account-history-contract/src/action/query/get_eden_boost_earn_program_details.rs b/contracts/account-history-contract/src/action/query/get_eden_boost_earn_program_details.rs new file mode 100644 index 00000000..8562f2fc --- /dev/null +++ b/contracts/account-history-contract/src/action/query/get_eden_boost_earn_program_details.rs @@ -0,0 +1,55 @@ +use super::*; +use crate::msg::query_resp::earn::GetEdenBoostEarnProgramResp; +use cosmwasm_std::Deps; +use elys_bindings::{ + account_history::types::{ + earn_detail::earn_detail::AprEdenBoost, earn_program::EdenBoostEarnProgram, ElysDenom, + }, + query_resp::{QueryAprResponse, Validator}, + ElysQuerier, ElysQuery, +}; + +pub fn get_eden_boost_earn_program_details( + deps: &Deps, + address: Option, + asset: String, + usdc_apr: QueryAprResponse, + eden_apr: QueryAprResponse, +) -> Result { + let bonding_period = 0; + let denom = ElysDenom::EdenBoost.as_str(); + if asset != denom.to_string() { + return Err(ContractError::AssetDenomError {}); + } + + let querier = ElysQuerier::new(&deps.querier); + + let mut data = EdenBoostEarnProgram::default(); + + data.bonding_period = bonding_period; + data.apr = AprEdenBoost { + uusdc: usdc_apr.apr, + ueden: eden_apr.apr, + }; + + if let Some(addr) = address { + let all_rewards = querier + .get_estaking_rewards(addr.clone()) + .unwrap_or_default(); + let program_rewards = all_rewards + .get_validator_rewards(Validator::EdenBoost) + .to_coin_values(&querier) + .unwrap_or_default() + .into_iter() + .map(|coin| coin.1) + .collect(); + let available = querier.get_balance(addr.clone(), asset.clone())?; + let staked = querier.get_staked_balance(addr, asset)?; + + data.available = Some(available.amount); + data.staked = Some(staked.amount); + data.rewards = Some(program_rewards); + } + + Ok(GetEdenBoostEarnProgramResp { data }) +} diff --git a/contracts/account-history-contract/src/action/query/get_eden_earn_program_details.rs b/contracts/account-history-contract/src/action/query/get_eden_earn_program_details.rs new file mode 100644 index 00000000..24197a9f --- /dev/null +++ b/contracts/account-history-contract/src/action/query/get_eden_earn_program_details.rs @@ -0,0 +1,71 @@ +use super::*; +use crate::msg::query_resp::earn::GetEdenEarnProgramResp; +use cosmwasm_std::{Decimal, Deps}; +use elys_bindings::account_history::types::earn_program::EdenEarnProgram; +use elys_bindings::account_history::types::{AprElys, ElysDenom}; +use elys_bindings::query_resp::Validator; +use elys_bindings::{query_resp::QueryAprResponse, ElysQuerier, ElysQuery}; + +pub fn get_eden_earn_program_details( + deps: &Deps, + address: Option, + asset: String, + uusdc_usd_price: Decimal, + uelys_price_in_uusdc: Decimal, + usdc_apr: QueryAprResponse, + eden_apr: QueryAprResponse, + edenb_apr: QueryAprResponse, +) -> Result { + let bonding_period = 0; + let denom = ElysDenom::Eden.as_str(); + if asset != denom.to_string() { + return Err(ContractError::AssetDenomError {}); + } + + let querier = ElysQuerier::new(&deps.querier); + + let mut data = EdenEarnProgram::default(); + + data.bonding_period = bonding_period; + data.apr = AprElys { + uusdc: usdc_apr.apr, + ueden: eden_apr.apr, + uedenb: edenb_apr.apr, + }; + + if let Some(addr) = address { + let all_rewards = querier + .get_estaking_rewards(addr.clone()) + .unwrap_or_default(); + let program_rewards = all_rewards + .get_validator_rewards(Validator::Eden) + .to_coin_values(&querier) + .unwrap_or_default() + .into_iter() + .map(|coin| coin.1) + .collect(); + + let mut available = querier.get_balance(addr.clone(), asset.clone())?; + let staked = querier.get_staked_balance(addr.clone(), asset.clone())?; + let vesting_info = querier.get_vesting_info(addr)?; + + // have value in usd + let mut available_in_usd = uelys_price_in_uusdc + .checked_mul( + Decimal::from_atomics(available.amount, 0).map_or(Decimal::zero(), |res| res), + ) + .unwrap_or_default(); + available_in_usd = available_in_usd + .checked_mul(uusdc_usd_price) + .unwrap_or_default(); + available.usd_amount = available_in_usd; + + data.available = Some(available); + data.staked = Some(staked); + data.rewards = Some(program_rewards); + data.vesting = vesting_info.vesting; + data.vesting_details = vesting_info.vesting_details; + } + + Ok(GetEdenEarnProgramResp { data }) +} diff --git a/contracts/account-history-contract/src/action/query/get_elys_earn_program_details.rs b/contracts/account-history-contract/src/action/query/get_elys_earn_program_details.rs new file mode 100644 index 00000000..95534488 --- /dev/null +++ b/contracts/account-history-contract/src/action/query/get_elys_earn_program_details.rs @@ -0,0 +1,72 @@ +use super::*; +use crate::msg::query_resp::earn::GetElysEarnProgramResp; +use cosmwasm_std::{Decimal, Deps}; +use elys_bindings::{ + account_history::types::{earn_program::ElysEarnProgram, AprElys, ElysDenom}, + query_resp::QueryAprResponse, + ElysQuerier, ElysQuery, +}; + +pub fn get_elys_earn_program_details( + deps: &Deps, + address: Option, + asset: String, + uusdc_usd_price: Decimal, + uelys_price_in_uusdc: Decimal, + usdc_apr: QueryAprResponse, + eden_apr: QueryAprResponse, + edenb_apr: QueryAprResponse, +) -> Result { + let bonding_period = 14; + let denom = ElysDenom::Elys.as_str(); + if asset != denom.to_string() { + return Err(ContractError::AssetDenomError {}); + } + + let querier = ElysQuerier::new(&deps.querier); + + let mut data = ElysEarnProgram::default(); + + data.bonding_period = bonding_period; + data.apr = AprElys { + uusdc: usdc_apr.apr, + ueden: eden_apr.apr, + uedenb: edenb_apr.apr, + }; + + if let Some(addr) = address { + let all_rewards = querier + .get_estaking_rewards(addr.clone()) + .unwrap_or_default(); + let program_rewards = all_rewards + .get_elys_validators() + .to_coin_values(&querier) + .unwrap_or_default() + .into_iter() + .map(|coin| coin.1) + .collect(); + + let mut available = querier.get_balance(addr.clone(), asset.clone())?; + let staked = querier.get_staked_balance(addr.clone(), asset)?; + + let staked_positions = querier.get_staked_positions(addr.clone())?; + let unstaked_positions = querier.get_unstaked_positions(addr)?; + + // have value in usd + let mut available_in_usd = uelys_price_in_uusdc + .checked_mul(Decimal::from_atomics(available.amount, 0).unwrap_or_default()) + .unwrap_or_default(); + available_in_usd = available_in_usd + .checked_mul(uusdc_usd_price) + .unwrap_or_default(); + available.usd_amount = available_in_usd; + + data.available = Some(available); + data.staked = Some(staked); + data.rewards = Some(program_rewards); + data.staked_positions = staked_positions.staked_position; + data.unstaked_positions = unstaked_positions.unstaked_position; + } + + Ok(GetElysEarnProgramResp { data }) +} diff --git a/contracts/account-history-contract/src/action/query/get_estaking_rewards.rs b/contracts/account-history-contract/src/action/query/get_estaking_rewards.rs new file mode 100644 index 00000000..284c8503 --- /dev/null +++ b/contracts/account-history-contract/src/action/query/get_estaking_rewards.rs @@ -0,0 +1,28 @@ +use cosmwasm_std::{Deps, StdResult}; +use elys_bindings::account_history::msg::query_resp::estaking::GetEstakingRewardsResponse; +use elys_bindings::account_history::types::CoinValue; +use elys_bindings::{ElysQuerier, ElysQuery}; + +/** + * Given a user address, gets the Estaking rewards available. + */ +pub fn get_estaking_rewards( + deps: Deps, + address: String, +) -> StdResult { + let querier = ElysQuerier::new(&deps.querier); + + let response = querier.get_estaking_rewards(address).unwrap_or_default(); + + let fiat_coins = response.to_coin_values(&querier); + let rewards = fiat_coins + .unwrap_or_default() + .into_iter() + .map(|(_, v)| v.clone()) + .collect::>(); + + Ok(GetEstakingRewardsResponse { + rewards, + total: response.total, + }) +} diff --git a/contracts/account-history-contract/src/action/query/get_liquid_assets.rs b/contracts/account-history-contract/src/action/query/get_liquid_assets.rs new file mode 100644 index 00000000..96a7d603 --- /dev/null +++ b/contracts/account-history-contract/src/action/query/get_liquid_assets.rs @@ -0,0 +1,53 @@ +use cosmwasm_std::{Decimal, Deps, Env, StdResult}; +use elys_bindings::{account_history::types::CoinValue, ElysQuerier, ElysQuery}; + +use crate::{ + msg::query_resp::{GetLiquidAssetsResp, LiquidAsset}, + types::AccountSnapshotGenerator, +}; + +pub fn get_liquid_assets( + deps: Deps, + user_address: String, + _env: Env, +) -> StdResult { + let querier = ElysQuerier::new(&deps.querier); + + let generator = AccountSnapshotGenerator::new(&deps)?; + + let liquid_asset = generator.get_liquid_assets(&deps, &querier, &user_address)?; + + let mut liquid_assets: Vec = vec![]; + + for total in liquid_asset.total_value_per_asset.clone() { + let (available_amount, available_value) = + get_info(&liquid_asset.available_asset_balance, &total.denom); + let (in_order_amount, in_order_value) = + get_info(&liquid_asset.in_orders_asset_balance, &total.denom); + + liquid_assets.push(LiquidAsset { + denom: total.denom, + price: total.price, + available_amount, + available_value, + in_order_amount, + in_order_value, + total_amount: total.amount_token, + total_value: total.amount_usd, + }); + } + + Ok(GetLiquidAssetsResp { + liquid_assets, + total_liquid_asset_balance: liquid_asset.total_liquid_asset_balance.clone(), + }) +} + +fn get_info(list_info: &Vec, denom: &String) -> (Decimal, Decimal) { + list_info + .iter() + .find(|info| &info.denom == denom) + .map_or((Decimal::zero(), Decimal::zero()), |data| { + (data.amount_token, data.amount_usd) + }) +} diff --git a/contracts/account-history-contract/src/action/query/get_masterchef_pending_rewards.rs b/contracts/account-history-contract/src/action/query/get_masterchef_pending_rewards.rs new file mode 100644 index 00000000..4b995663 --- /dev/null +++ b/contracts/account-history-contract/src/action/query/get_masterchef_pending_rewards.rs @@ -0,0 +1,19 @@ +use cosmwasm_std::{Deps, StdResult}; +use elys_bindings::account_history::msg::query_resp::masterchef::GetMasterchefUserPendingRewardResponse; +use elys_bindings::{ElysQuerier, ElysQuery}; + +pub fn get_masterchef_pending_rewards( + deps: Deps, + address: String, +) -> StdResult { + let querier = ElysQuerier::new(&deps.querier); + + let resp = querier.get_masterchef_pending_rewards(address)?; + + let (rewards, total_rewards) = resp.to_coin_values(&querier)?; + + Ok(GetMasterchefUserPendingRewardResponse { + rewards, + total_rewards, + }) +} diff --git a/contracts/account-history-contract/src/action/query/get_masterchef_pool_apr.rs b/contracts/account-history-contract/src/action/query/get_masterchef_pool_apr.rs new file mode 100644 index 00000000..2af95c40 --- /dev/null +++ b/contracts/account-history-contract/src/action/query/get_masterchef_pool_apr.rs @@ -0,0 +1,17 @@ +use cosmwasm_std::{Deps, StdResult}; +use elys_bindings::{ + account_history::msg::query_resp::masterchef::MasterChefPoolAprResponse, ElysQuerier, ElysQuery, +}; + +pub fn get_masterchef_pool_apr( + deps: Deps, + pool_ids: Vec, +) -> StdResult { + let querier = ElysQuerier::new(&deps.querier); + + let resp = querier.get_masterchef_pool_apr(pool_ids)?; + + Ok(MasterChefPoolAprResponse { + data: resp.to_decimal(), + }) +} diff --git a/contracts/account-history-contract/src/action/query/get_masterchef_stable_stake_apr.rs b/contracts/account-history-contract/src/action/query/get_masterchef_stable_stake_apr.rs new file mode 100644 index 00000000..5c4129ac --- /dev/null +++ b/contracts/account-history-contract/src/action/query/get_masterchef_stable_stake_apr.rs @@ -0,0 +1,13 @@ +use cosmwasm_std::{Deps, StdResult}; +use elys_bindings::{query_resp::QueryStableStakeAprResponse, ElysQuerier, ElysQuery}; + +pub fn get_masterchef_stable_stake_apr( + deps: Deps, + denom: String, +) -> StdResult { + let querier = ElysQuerier::new(&deps.querier); + + let resp = querier.get_masterchef_stable_stake_apr(denom)?; + + Ok(resp) +} diff --git a/contracts/account-history-contract/src/action/query/get_membership_tier.rs b/contracts/account-history-contract/src/action/query/get_membership_tier.rs new file mode 100644 index 00000000..49ca73e8 --- /dev/null +++ b/contracts/account-history-contract/src/action/query/get_membership_tier.rs @@ -0,0 +1,23 @@ +use crate::msg::query_resp::MembershipTierResponse; +use cosmwasm_std::{Deps, Env, StdResult}; +use elys_bindings::ElysQuery; + +use super::user_snapshots; + +pub fn get_membership_tier( + env: Env, + deps: Deps, + user_address: String, +) -> StdResult { + let user_snapshots_list = user_snapshots(env, deps, user_address)?; + + match user_snapshots_list + .iter() + .min_by_key(|&snapshot| snapshot.total_balance_usd) + { + Some(snapshot) => Ok(MembershipTierResponse::calc( + snapshot.total_balance_usd.to_owned(), + )), + None => return Ok(MembershipTierResponse::zero()), + } +} diff --git a/contracts/account-history-contract/src/action/query/get_perpetual_asset.rs b/contracts/account-history-contract/src/action/query/get_perpetual_asset.rs new file mode 100644 index 00000000..172bf2fe --- /dev/null +++ b/contracts/account-history-contract/src/action/query/get_perpetual_asset.rs @@ -0,0 +1,14 @@ +use cosmwasm_std::{Deps, Env, StdResult}; +use elys_bindings::{account_history::types::PerpetualAssets, ElysQuery}; + +use crate::types::AccountSnapshotGenerator; + +pub fn get_perpetuals_assets( + deps: Deps, + address: String, + _env: Env, +) -> StdResult { + let generator = AccountSnapshotGenerator::new(&deps)?; + + generator.get_perpetuals(&deps, &address) +} diff --git a/contracts/account-history-contract/src/action/query/get_pool_balances.rs b/contracts/account-history-contract/src/action/query/get_pool_balances.rs new file mode 100644 index 00000000..9cc73d5b --- /dev/null +++ b/contracts/account-history-contract/src/action/query/get_pool_balances.rs @@ -0,0 +1,16 @@ +use crate::types::AccountSnapshotGenerator; +use cosmwasm_std::{Deps, Env, StdResult}; +use elys_bindings::query_resp::QueryUserPoolResponse; +use elys_bindings::ElysQuery; + +pub fn get_pool_balances( + deps: Deps, + address: String, + _env: Env, +) -> StdResult { + let generator = AccountSnapshotGenerator::new(&deps)?; + + let pool_balances_response = generator.get_pool_balances(&deps, &address)?; + + Ok(pool_balances_response) +} diff --git a/contracts/account-history-contract/src/action/query/get_pools.rs b/contracts/account-history-contract/src/action/query/get_pools.rs new file mode 100644 index 00000000..86771758 --- /dev/null +++ b/contracts/account-history-contract/src/action/query/get_pools.rs @@ -0,0 +1,16 @@ +use cosmwasm_std::{Deps, StdResult}; +use elys_bindings::{ + query_resp::PoolFilterType, query_resp::QueryEarnPoolResponse, types::PageRequest, ElysQuerier, + ElysQuery, +}; + +pub fn get_pools( + deps: Deps, + pool_ids: Option>, + filter_type: PoolFilterType, + pagination: Option, +) -> StdResult { + let querier = ElysQuerier::new(&deps.querier); + let resp = querier.get_all_pools(pool_ids, filter_type as i32, pagination)?; + Ok(resp) +} diff --git a/contracts/account-history-contract/src/action/query/get_pools_apr.rs b/contracts/account-history-contract/src/action/query/get_pools_apr.rs new file mode 100644 index 00000000..91324daf --- /dev/null +++ b/contracts/account-history-contract/src/action/query/get_pools_apr.rs @@ -0,0 +1,11 @@ +use cosmwasm_std::{Deps, StdResult}; +use elys_bindings::{query_resp::QueryIncentivePoolAprsResponse, ElysQuerier, ElysQuery}; + +pub fn get_pools_apr( + deps: Deps, + pool_ids: Option>, +) -> StdResult { + let querier = ElysQuerier::new(&deps.querier); + let resp = querier.get_pools_apr(pool_ids)?; + Ok(resp) +} diff --git a/contracts/account-history-contract/src/action/query/get_portfolio.rs b/contracts/account-history-contract/src/action/query/get_portfolio.rs new file mode 100644 index 00000000..84466d68 --- /dev/null +++ b/contracts/account-history-contract/src/action/query/get_portfolio.rs @@ -0,0 +1,11 @@ +use crate::{msg::query_resp::GetPortfolioResp, types::AccountSnapshotGenerator}; +use cosmwasm_std::{Deps, Env, SignedDecimal256, StdResult}; +use elys_bindings::{ElysQuerier, ElysQuery}; + +pub fn get_portfolio( + deps: Deps, + user_address: String, + env: Env, +) -> StdResult { + unimplemented!() +} diff --git a/contracts/account-history-contract/src/action/query/get_rewards.rs b/contracts/account-history-contract/src/action/query/get_rewards.rs new file mode 100644 index 00000000..ec1e1aad --- /dev/null +++ b/contracts/account-history-contract/src/action/query/get_rewards.rs @@ -0,0 +1,22 @@ +use crate::{msg::query_resp::GetRewardsResp, types::AccountSnapshotGenerator}; + +use cosmwasm_std::{Deps, Env, StdResult}; + +use elys_bindings::ElysQuery; + +pub fn get_rewards( + deps: Deps, + user_address: String, + _env: Env, +) -> StdResult { + let generator = AccountSnapshotGenerator::new(&deps)?; + + let rewards_response = generator.get_rewards(&deps, &user_address)?; + + let resp = GetRewardsResp { + rewards_map: rewards_response.rewards_map.clone(), + rewards: rewards_response.rewards.clone(), + }; + + Ok(resp) +} diff --git a/contracts/account-history-contract/src/action/query/get_staked_assets.rs b/contracts/account-history-contract/src/action/query/get_staked_assets.rs new file mode 100644 index 00000000..c898c1a4 --- /dev/null +++ b/contracts/account-history-contract/src/action/query/get_staked_assets.rs @@ -0,0 +1,24 @@ +use crate::msg::query_resp::StakedAssetsResponse; +use crate::types::AccountSnapshotGenerator; +use cosmwasm_std::{DecCoin, Decimal256, Deps, Env, StdResult}; +use elys_bindings::ElysQuery; + +pub fn get_staked_assets( + deps: Deps, + address: Option, + _env: Env, +) -> StdResult { + let generator = AccountSnapshotGenerator::new(&deps)?; + + let staked_assets_response = generator.get_staked_assets(&deps, address)?; + + Ok(StakedAssetsResponse { + total_staked_balance: DecCoin::new( + Decimal256::from(staked_assets_response.total_staked_balance.amount), + generator.metadata.usdc_denom, + ), + staked_assets: staked_assets_response.staked_assets.to_owned(), + total_balance: staked_assets_response.total_balance, + balance_break_down: staked_assets_response.balance_break_down, + }) +} diff --git a/contracts/account-history-contract/src/action/query/get_usdc_earn_program_details.rs b/contracts/account-history-contract/src/action/query/get_usdc_earn_program_details.rs new file mode 100644 index 00000000..a6de98f1 --- /dev/null +++ b/contracts/account-history-contract/src/action/query/get_usdc_earn_program_details.rs @@ -0,0 +1,66 @@ +use super::*; +use crate::msg::query_resp::earn::GetUsdcEarnProgramResp; +use cosmwasm_std::{Decimal, Deps}; +use elys_bindings::{ + account_history::types::{earn_program::UsdcEarnProgram, AprUsdc, ElysDenom}, + ElysQuerier, ElysQuery, +}; + +pub fn get_usdc_earn_program_details( + deps: &Deps, + address: Option, + usdc_denom: String, + usdc_base_denom: String, + uusdc_usd_price: Decimal, +) -> Result { + let pool_id = 32767u64; + let bonding_period = 0; + + let querier = ElysQuerier::new(&deps.querier); + + let eden_apr = querier + .get_masterchef_stable_stake_apr(ElysDenom::Eden.as_str().to_string()) + .unwrap_or_default(); + let usdc_apr = querier + .get_masterchef_stable_stake_apr(ElysDenom::Usdc.as_str().to_string()) + .unwrap_or_default(); + + let mut data = UsdcEarnProgram::default(); + + data.bonding_period = bonding_period; + data.apr = AprUsdc { + uusdc: usdc_apr.apr, + ueden: eden_apr.apr, + }; + + if let Some(addr) = address { + let rewards = querier + .get_masterchef_pending_rewards(addr.clone()) + .unwrap_or_default(); + let coin_values_rewards = rewards.to_coin_values(&querier).unwrap_or_default(); + let pool_rewards = coin_values_rewards.0[&pool_id].clone(); + + let mut available = querier.get_balance(addr.clone(), usdc_denom)?; + available.usd_amount = available + .usd_amount + .checked_mul(uusdc_usd_price) + .unwrap_or_default(); + + let mut staked = querier.get_staked_balance(addr, usdc_base_denom)?; + + let mut borrowed = querier.get_borrowed_balance()?; + borrowed.usd_amount = borrowed + .usd_amount + .checked_mul(uusdc_usd_price) + .unwrap_or_default(); + + staked.lockups = None; + + data.available = Some(available); + data.staked = Some(staked); + data.rewards = Some(pool_rewards); + data.borrowed = Some(borrowed); + } + + Ok(GetUsdcEarnProgramResp { data }) +} diff --git a/contracts/account-history-contract/src/action/query/join_pool_estimation.rs b/contracts/account-history-contract/src/action/query/join_pool_estimation.rs new file mode 100644 index 00000000..105bf633 --- /dev/null +++ b/contracts/account-history-contract/src/action/query/join_pool_estimation.rs @@ -0,0 +1,12 @@ +use cosmwasm_std::{Coin, Deps, StdResult}; +use elys_bindings::{query_resp::QueryJoinPoolEstimationResponse, ElysQuerier, ElysQuery}; + +pub fn join_pool_estimation( + deps: Deps, + pool_id: u64, + amounts_in: Vec, +) -> StdResult { + let querier = ElysQuerier::new(&deps.querier); + let resp = querier.join_pool_estimation(pool_id, amounts_in)?; + Ok(resp) +} diff --git a/contracts/account-history-contract/src/action/query/last_snapshot.rs b/contracts/account-history-contract/src/action/query/last_snapshot.rs new file mode 100644 index 00000000..93b3fe40 --- /dev/null +++ b/contracts/account-history-contract/src/action/query/last_snapshot.rs @@ -0,0 +1,20 @@ +use cosmwasm_std::{Deps, Env, StdResult}; +use elys_bindings::{account_history::types::PortfolioBalanceSnapshot, ElysQuery}; + +use crate::{states::HISTORY, utils::get_today}; + +pub fn last_snapshot( + deps: Deps, + user_address: String, + env: Env, +) -> StdResult { + let today = get_today(&env.block); + let key = today + &user_address; + + let snapshot = match HISTORY.may_load(deps.storage, &key)? { + Some(snapshot) => snapshot.clone(), + None => PortfolioBalanceSnapshot::default(), + }; + + Ok(snapshot) +} diff --git a/contracts/account-history-contract/src/action/query/params.rs b/contracts/account-history-contract/src/action/query/params.rs new file mode 100644 index 00000000..96c89388 --- /dev/null +++ b/contracts/account-history-contract/src/action/query/params.rs @@ -0,0 +1,29 @@ +use crate::{ + msg::query_resp::ParamsResp, + states::{ + DELETE_EPOCH, DELETE_OLD_DATA_ENABLED, EXPIRATION, METADATA, PROCESSED_ACCOUNT_PER_BLOCK, + TRADE_SHIELD_ADDRESS, UPDATE_ACCOUNT_ENABLED, + }, +}; +use cosmwasm_std::{Deps, StdResult}; +use elys_bindings::ElysQuery; + +pub fn params(deps: Deps) -> StdResult { + let expiration = EXPIRATION.load(deps.storage)?; + let processed_account_per_block = PROCESSED_ACCOUNT_PER_BLOCK.load(deps.storage)?; + let trade_shield_address = TRADE_SHIELD_ADDRESS.load(deps.storage)?; + let update_account_enabled = UPDATE_ACCOUNT_ENABLED.load(deps.storage)?; + let metadata = METADATA.load(deps.storage)?; + let delete_old_data_enabled = DELETE_OLD_DATA_ENABLED.load(deps.storage)?; + let delete_epoch = DELETE_EPOCH.load(deps.storage)?; + + Ok(ParamsResp { + expiration, + processed_account_per_block, + update_account_enabled, + trade_shield_address, + metadata, + delete_old_data_enabled, + delete_epoch, + }) +} diff --git a/contracts/account-history-contract/src/action/query/pool_asset_estimation.rs b/contracts/account-history-contract/src/action/query/pool_asset_estimation.rs new file mode 100644 index 00000000..7312c293 --- /dev/null +++ b/contracts/account-history-contract/src/action/query/pool_asset_estimation.rs @@ -0,0 +1,63 @@ +use cosmwasm_std::{DecCoin, Decimal, Decimal256, Deps, StdError, StdResult}; +use elys_bindings::query_resp::{PoolFilterType, QueryPoolAssetEstimationResponse}; +use elys_bindings::{ElysQuerier, ElysQuery}; +use std::collections::HashMap; + +/** + * Given an asset and a pool, determine the quantity of every other asset in the pool + * needed to keep the pool balanced. + * Useful to use in FE forms before calling join pool. + */ +pub fn pool_asset_estimation( + deps: Deps, + pool_id: u64, + asset: DecCoin, +) -> StdResult { + let querier = ElysQuerier::new(&deps.querier); + let asset_denom = asset.denom.to_string(); + + let pool_response = + querier.get_all_pools(Some(vec![pool_id]), PoolFilterType::FilterAll as i32, None)?; + let pool = match pool_response.pools { + Some(pools) => { + if let Some(pool) = pools.first() { + pool.clone() + } else { + return Err(StdError::generic_err("Pool not found")); + } + } + None => return Err(StdError::generic_err("Failed to fetch pool")), + }; + + let asset_usd_price = querier + .get_asset_price(asset.denom) + .unwrap_or(Decimal::zero()); + let asset_in_usd = asset.amount * Decimal256::from(asset_usd_price); + + // Ensure the current_pool_ratio is populated + let current_pool_ratio = match &pool.current_pool_ratio { + Some(ratio) => ratio, + None => return Err(StdError::generic_err("Current pool ratio is not populated")), + }; + + let asset_ratio = Decimal256::from(current_pool_ratio.get(&asset_denom).unwrap().clone()); + let total_in_usd = asset_in_usd / asset_ratio; + + let mut estimations = HashMap::new(); + for (denom, _) in current_pool_ratio.iter() { + if denom.to_string() != asset_denom { + let usd_price = querier.get_asset_price(denom).unwrap_or(Decimal::zero()); + let dec_price = Decimal256::from(usd_price); + let ratio = Decimal256::from(current_pool_ratio.get(denom).unwrap().clone()); + + let quantity = (total_in_usd * ratio) / dec_price; + + estimations.insert(denom.clone(), quantity); + } + } + + // Return the result + Ok(QueryPoolAssetEstimationResponse { + amounts: estimations, + }) +} diff --git a/contracts/account-history-contract/src/action/query/user_snapshots.rs b/contracts/account-history-contract/src/action/query/user_snapshots.rs new file mode 100644 index 00000000..a8d9c4ac --- /dev/null +++ b/contracts/account-history-contract/src/action/query/user_snapshots.rs @@ -0,0 +1,46 @@ +use chrono::NaiveDateTime; +use cosmwasm_std::{Deps, Env, StdResult, Timestamp}; +use cw_utils::Expiration; +use elys_bindings::{account_history::types::PortfolioBalanceSnapshot, ElysQuery}; + +use crate::{states::HISTORY, types::AccountSnapshotGenerator}; + +pub fn user_snapshots( + env: Env, + deps: Deps, + user_address: String, +) -> StdResult> { + let generator = AccountSnapshotGenerator::new(&deps)?; + let expiration = match generator.expiration { + Expiration::AtHeight(h) => Timestamp::from_seconds(h * 3), // since a block is created every 3 seconds + Expiration::AtTime(t) => t.clone(), + _ => panic!("never expire"), + }; + + let mut day_date = if env.block.time.seconds() < expiration.seconds() { + Timestamp::from_seconds(0) + } else { + env.block + .time + .minus_seconds(expiration.seconds()) + .plus_days(1) + }; + + let mut user_snapshots_list: Vec = vec![]; + + while day_date <= env.block.time { + let date = NaiveDateTime::from_timestamp_opt(day_date.seconds() as i64, 0) + .expect("Failed to convert block time to date") + .format("%Y-%m-%d") + .to_string(); + let key = date + &user_address; + + if let Some(portfolio) = HISTORY.may_load(deps.storage, &key)? { + user_snapshots_list.push(portfolio.to_owned()) + } + + day_date = day_date.plus_days(1); + } + + Ok(user_snapshots_list) +} diff --git a/contracts/account-history-contract/src/action/query/user_value.rs b/contracts/account-history-contract/src/action/query/user_value.rs new file mode 100644 index 00000000..6d253c81 --- /dev/null +++ b/contracts/account-history-contract/src/action/query/user_value.rs @@ -0,0 +1,23 @@ +use crate::msg::query_resp::UserValueResponse; +use cosmwasm_std::{Deps, Env, StdError, StdResult}; +use elys_bindings::ElysQuery; + +use super::user_snapshots; + +pub fn user_value( + env: Env, + deps: Deps, + user_address: String, +) -> StdResult { + let user_snapshots_list = user_snapshots(env, deps, user_address.clone())?; + + match user_snapshots_list + .iter() + .min_by_key(|&portfolio| portfolio.total_balance_usd) + { + Some(portfolio) => Ok(UserValueResponse { + value: portfolio.to_owned(), + }), + None => Err(StdError::not_found(format!("user :{user_address}"))), + } +} diff --git a/contracts/account-history-contract/src/action/sudo/update_metadata_prices.rs b/contracts/account-history-contract/src/action/sudo/update_metadata_prices.rs new file mode 100644 index 00000000..3d5293d5 --- /dev/null +++ b/contracts/account-history-contract/src/action/sudo/update_metadata_prices.rs @@ -0,0 +1,14 @@ +use crate::states::METADATA; +use cosmwasm_std::{DepsMut, Response, StdResult}; +use elys_bindings::{ElysMsg, ElysQuerier, ElysQuery}; + +pub fn update_metadata_prices(deps: DepsMut) -> StdResult> { + let querier = ElysQuerier::new(&deps.querier); + + // update metadata prices + let mut metadata = METADATA.load(deps.storage)?; + metadata = metadata.update_prices(&querier)?; + METADATA.save(deps.storage, &metadata)?; + + Ok(Response::default()) +} diff --git a/contracts/account-history-contract/src/bin/account-history-contract-schema.rs b/contracts/account-history-contract/src/bin/account-history-contract-schema.rs new file mode 100644 index 00000000..509eeeef --- /dev/null +++ b/contracts/account-history-contract/src/bin/account-history-contract-schema.rs @@ -0,0 +1,11 @@ +use account_history_contract::msg::{InstantiateMsg, QueryMsg}; +use cosmwasm_schema::write_api; +use cosmwasm_std::Empty; + +fn main() { + write_api! { + instantiate: InstantiateMsg, + execute: Empty, + query: QueryMsg + } +} diff --git a/contracts/account-history-contract/src/entry_point/execute.rs b/contracts/account-history-contract/src/entry_point/execute.rs new file mode 100644 index 00000000..de7a742b --- /dev/null +++ b/contracts/account-history-contract/src/entry_point/execute.rs @@ -0,0 +1,76 @@ +use cosmwasm_std::{entry_point, DepsMut, Env, MessageInfo, Response, StdError, StdResult}; +use elys_bindings::{account_history::msg::ExecuteMsg, ElysMsg, ElysQuery}; + +use crate::{ + action::execute::{add_user_address_to_queue, clean_up_storage}, + states::{ + DELETE_EPOCH, DELETE_OLD_DATA_ENABLED, HISTORY, OLD_HISTORY_2, PARAMS_ADMIN, + PROCESSED_ACCOUNT_PER_BLOCK, TRADE_SHIELD_ADDRESS, UPDATE_ACCOUNT_ENABLED, + }, +}; + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn execute( + mut deps: DepsMut, + _env: Env, + info: MessageInfo, + msg: ExecuteMsg, +) -> StdResult> { + match msg { + ExecuteMsg::AddUserAddressToQueue { user_address } => { + let trade_shield_address = match TRADE_SHIELD_ADDRESS.load(deps.storage)? { + Some(addr) => addr, + None => return Err(StdError::generic_err("Unauthorized")), + }; + if trade_shield_address.as_str() != info.sender.as_str() { + return Err(StdError::generic_err("Unauthorized")); + } + add_user_address_to_queue(deps, user_address)?; + Ok(Response::new()) + } + ExecuteMsg::ChangeParams { + update_account_enabled, + processed_account_per_block, + delete_old_data_enabled, + delete_epoch, + } => { + let params_admin = PARAMS_ADMIN.load(deps.storage)?; + + if params_admin.as_str() != info.sender.as_str() { + return Err(StdError::generic_err("Unauthorized")); + } + + if let Some(processed_account_per_block) = processed_account_per_block { + PROCESSED_ACCOUNT_PER_BLOCK.save(deps.storage, &processed_account_per_block)?; + } + + if let Some(update_account_enabled) = update_account_enabled { + UPDATE_ACCOUNT_ENABLED.save(deps.storage, &update_account_enabled)?; + } + + if let Some(delete_old_data_enabled) = delete_old_data_enabled { + DELETE_OLD_DATA_ENABLED.save(deps.storage, &delete_old_data_enabled)?; + } + + if let Some(delete_epoch) = delete_epoch { + DELETE_EPOCH.save(deps.storage, &delete_epoch)?; + } + Ok(Response::new()) + } + ExecuteMsg::CleanStorage { limit } => { + if info.sender != PARAMS_ADMIN.load(deps.storage)? { + return Err(StdError::generic_err("Unauthorized")); + } + let resp = clean_up_storage(&mut deps, limit)?; + Ok(resp) + } + ExecuteMsg::CleanStorageBulk {} => { + if info.sender != PARAMS_ADMIN.load(deps.storage)? { + return Err(StdError::generic_err("Unauthorized")); + } + HISTORY.clear(deps.storage); + OLD_HISTORY_2.clear(deps.storage); + Ok(Response::new()) + } + } +} diff --git a/contracts/account-history-contract/src/entry_point/instantiate.rs b/contracts/account-history-contract/src/entry_point/instantiate.rs new file mode 100644 index 00000000..0eaa7550 --- /dev/null +++ b/contracts/account-history-contract/src/entry_point/instantiate.rs @@ -0,0 +1,63 @@ +use cw2::set_contract_version; +use cw_utils::Expiration; +use elys_bindings::account_history::types::Metadata; + +use cosmwasm_std::{entry_point, DepsMut, Env, MessageInfo, Response, StdResult, Timestamp}; +use elys_bindings::trade_shield::states::PARAMS_ADMIN; +use elys_bindings::{ElysMsg, ElysQuerier, ElysQuery}; + +use crate::msg::InstantiateMsg; +use crate::states::{ + DELETE_EPOCH, DELETE_OLD_DATA_ENABLED, EXPIRATION, METADATA, PROCESSED_ACCOUNT_PER_BLOCK, + TRADE_SHIELD_ADDRESS, UPDATE_ACCOUNT_ENABLED, +}; + +// Version info, for migration info +pub const CONTRACT_NAME: &str = "account-history"; +pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn instantiate( + deps: DepsMut, + _env: Env, + info: MessageInfo, + msg: InstantiateMsg, +) -> StdResult> { + set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + // EXPIRATION + match msg.expiration { + Some(expiration) => EXPIRATION.save(deps.storage, &expiration)?, + None => EXPIRATION.save( + deps.storage, + &Expiration::AtTime(Timestamp::from_seconds(3 * 24 * 60 * 60)), + )?, + }; + + // PROCESSED_ACCOUNT_PER_BLOCK + let limit = match msg.limit { + Some(limit) => limit, + None => 1, + }; + + PARAMS_ADMIN.save(deps.storage, &info.sender.to_string())?; + + PROCESSED_ACCOUNT_PER_BLOCK.save(deps.storage, &limit)?; + + // TRADESHIELD ADDRESS + TRADE_SHIELD_ADDRESS.save(deps.storage, &msg.trade_shield_address)?; + + // METADATA + let querier = ElysQuerier::new(&deps.querier); + + let metadata = Metadata::collect(&querier)?; + + METADATA.save(deps.storage, &metadata)?; + + UPDATE_ACCOUNT_ENABLED.save(deps.storage, &true)?; + + DELETE_OLD_DATA_ENABLED.save(deps.storage, &true)?; + DELETE_EPOCH.save(deps.storage, &1000u64)?; + + // RESPONSE + Ok(Response::new()) +} diff --git a/contracts/account-history-contract/src/entry_point/migrate.rs b/contracts/account-history-contract/src/entry_point/migrate.rs new file mode 100644 index 00000000..d4647925 --- /dev/null +++ b/contracts/account-history-contract/src/entry_point/migrate.rs @@ -0,0 +1,74 @@ +use cosmwasm_std::{entry_point, DepsMut, Env, Response, StdError, StdResult, Timestamp}; +use cw2::set_contract_version; +use cw_utils::Expiration; +use elys_bindings::account_history::msg::MigrationMsg; +// use elys_bindings::account_history::types::Metadata; +use elys_bindings::{ElysMsg, ElysQuery}; +use semver::Version; + +use super::instantiate::{CONTRACT_NAME, CONTRACT_VERSION}; +use crate::states::{ + DELETE_EPOCH, DELETE_OLD_DATA_ENABLED, EXPIRATION, PARAMS_ADMIN, PROCESSED_ACCOUNT_PER_BLOCK, + TRADE_SHIELD_ADDRESS, +}; +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn migrate( + deps: DepsMut, + _env: Env, + msg: MigrationMsg, +) -> StdResult> { + // TRADESHIELD ADDRESS + if msg.trade_shield_address.is_some() { + TRADE_SHIELD_ADDRESS.save(deps.storage, &msg.trade_shield_address)?; + } + + // EXPIRATION + EXPIRATION.save( + deps.storage, + &Expiration::AtTime(Timestamp::from_seconds(3 * 24 * 60 * 60)), + )?; + + // PROCESSED_ACCOUNT_PER_BLOCK + let limit = match msg.limit { + Some(limit) => limit, + None => 1, + }; + + PROCESSED_ACCOUNT_PER_BLOCK.save(deps.storage, &limit)?; + DELETE_OLD_DATA_ENABLED.save(deps.storage, &true)?; + DELETE_EPOCH.save(deps.storage, &1000u64)?; + + // METADATA + // let querier = ElysQuerier::new(&deps.querier); + + // let metadata = Metadata::collect(&querier)?; + + // METADATA.save(deps.storage, &metadata)?; + + // RESPONSE + + let ver = cw2::get_contract_version(deps.storage)?; + // ensure we are migrating from an allowed contract + if ver.contract != CONTRACT_NAME { + return Err(StdError::generic_err("Can only upgrade from same type").into()); + } + let new_contract_version = Version::parse(CONTRACT_VERSION).unwrap(); + let actual_contract_version = Version::parse(ver.version.as_str()).unwrap(); + + if new_contract_version.le(&actual_contract_version) { + let err_version: String = format!( + "Error the version of account-history-contract {} has to be upper to {}", + new_contract_version.to_string(), + actual_contract_version.to_string() + ); + + return Err(StdError::generic_err(err_version).into()); + } + + let admin = "elys16xffmfa6k45j340cx5zyp66lqvuw62a0neaa7w".to_string(); + PARAMS_ADMIN.save(deps.storage, &admin)?; + + // set the new version + set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + Ok(Response::new()) +} diff --git a/contracts/account-history-contract/src/entry_point/mod.rs b/contracts/account-history-contract/src/entry_point/mod.rs new file mode 100644 index 00000000..db29648a --- /dev/null +++ b/contracts/account-history-contract/src/entry_point/mod.rs @@ -0,0 +1,11 @@ +mod execute; +mod instantiate; +mod migrate; +mod query; +mod sudo; + +pub use execute::execute; +pub use instantiate::instantiate; +pub use migrate::migrate; +pub use query::query; +pub use sudo::sudo; diff --git a/contracts/account-history-contract/src/entry_point/query.rs b/contracts/account-history-contract/src/entry_point/query.rs new file mode 100644 index 00000000..a629aaf2 --- /dev/null +++ b/contracts/account-history-contract/src/entry_point/query.rs @@ -0,0 +1,276 @@ +use crate::{ + action::query::{ + get_eden_boost_earn_program_details, get_eden_earn_program_details, + get_elys_earn_program_details, get_estaking_rewards, get_liquid_assets, + get_masterchef_pending_rewards, get_masterchef_pool_apr, get_masterchef_stable_stake_apr, + get_membership_tier, get_perpetuals_assets, get_pool_balances, get_rewards, + get_staked_assets, get_usdc_earn_program_details, + }, + states::{HISTORY, OLD_HISTORY_2, USER_ADDRESS_QUEUE}, + types::AccountSnapshotGenerator, +}; + +#[cfg(feature = "debug")] +use crate::action::query::{ + all, exit_pool_estimation, get_pools, join_pool_estimation, last_snapshot, params, + pool_asset_estimation, user_snapshots, user_value, +}; + +use cosmwasm_std::{entry_point, to_json_binary, Binary, Deps, Env, StdResult}; +use cw2::CONTRACT; +use elys_bindings::{ + account_history::{msg::query_resp::StorageSizeResp, types::ElysDenom}, + query_resp::QueryAprResponse, + ElysQuerier, ElysQuery, +}; + +use crate::msg::QueryMsg; + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { + use QueryMsg::*; + + match msg { + Accounts { pagination } => to_json_binary(&{ + let querrier = ElysQuerier::new(&deps.querier); + + let resp = querrier.accounts(pagination)?; + resp + }), + + GetLiquidAssets { user_address } => { + to_json_binary(&get_liquid_assets(deps, user_address, env)?) + } + GetStakedAssets { user_address } => { + to_json_binary(&get_staked_assets(deps, user_address, env)?) + } + GetPoolBalances { user_address } => { + to_json_binary(&get_pool_balances(deps, user_address, env)?) + } + GetRewards { user_address } => to_json_binary(&get_rewards(deps, user_address, env)?), + + GetMembershipTier { user_address } => { + to_json_binary(&get_membership_tier(env, deps, user_address)?) + } + GetPerpetualAssets { user_address } => { + to_json_binary(&get_perpetuals_assets(deps, user_address, env)?) + } + GetAssetPrice { asset } => { + let querier = ElysQuerier::new(&deps.querier); + to_json_binary(&querier.get_asset_price(asset)?) + } + + GetLiquidityPools { + pool_ids, + filter_type, + pagination, + } => to_json_binary(&get_pools(deps, pool_ids, filter_type, pagination)?), + + JoinPoolEstimation { + pool_id, + amounts_in, + } => to_json_binary(&join_pool_estimation(deps, pool_id, amounts_in)?), + + PoolAssetEstimation { pool_id, amount } => { + to_json_binary(&pool_asset_estimation(deps, pool_id, amount)?) + } + + ExitPoolEstimation { + pool_id, + exit_fiat_amount, + } => to_json_binary(&exit_pool_estimation(deps, pool_id, exit_fiat_amount)?), + + GetAssetPriceFromDenomInToDenomOut { + denom_in, + denom_out, + } => { + let querier = ElysQuerier::new(&deps.querier); + to_json_binary( + &querier.get_asset_price_from_denom_in_to_denom_out(denom_in, denom_out)?, + ) + } + + GetEstakingRewards { address } => to_json_binary(&get_estaking_rewards(deps, address)?), + + GetMasterchefParams {} => { + let querier = ElysQuerier::new(&deps.querier); + to_json_binary(&querier.masterchef_params()?) + } + + GetMasterchefPoolInfo { pool_id } => { + let querier = ElysQuerier::new(&deps.querier); + to_json_binary(&querier.masterchef_pool_info(pool_id)?) + } + + GetMasterchefPendingRewards { address } => { + to_json_binary(&get_masterchef_pending_rewards(deps, address)?) + } + + GetMasterChefPoolApr { pool_ids } => { + to_json_binary(&get_masterchef_pool_apr(deps, pool_ids)?) + } + + GetMasterchefStableStakeApr { denom } => { + to_json_binary(&get_masterchef_stable_stake_apr(deps, denom)?) + } + + // debug only + #[cfg(feature = "debug")] + Params {} => to_json_binary(¶ms(deps)?), + #[cfg(feature = "debug")] + All { pagination } => to_json_binary(&all(deps, pagination)?), + #[cfg(feature = "debug")] + UserSnapshots { user_address } => to_json_binary(&user_snapshots(env, deps, user_address)?), + #[cfg(feature = "debug")] + LastSnapshot { user_address } => to_json_binary(&last_snapshot(deps, user_address, env)?), + #[cfg(feature = "debug")] + UserValue { user_address } => to_json_binary(&user_value(env, deps, user_address)?), + #[cfg(feature = "debug")] + CommitmentStakedPositions { delegator_address } => { + let querier = ElysQuerier::new(&deps.querier); + to_json_binary(&querier.get_staked_positions(delegator_address)?) + } + #[cfg(feature = "debug")] + CommitmentUnStakedPositions { delegator_address } => { + let querier = ElysQuerier::new(&deps.querier); + to_json_binary(&querier.get_unstaked_positions(delegator_address)?) + } + #[cfg(feature = "debug")] + CommitmentStakedBalanceOfDenom { address, denom } => { + let querier = ElysQuerier::new(&deps.querier); + to_json_binary(&querier.get_staked_balance(address, denom)?) + } + #[cfg(feature = "debug")] + StableStakeBalanceOfBorrow {} => { + let querier = ElysQuerier::new(&deps.querier); + to_json_binary(&querier.get_borrowed_balance()?) + } + #[cfg(feature = "debug")] + StableStakeParams {} => { + let querier = ElysQuerier::new(&deps.querier); + to_json_binary(&querier.get_stable_stake_params()?) + } + #[cfg(feature = "debug")] + CommitmentVestingInfo { address } => { + let querier = ElysQuerier::new(&deps.querier); + to_json_binary(&querier.get_vesting_info(address)?) + } + #[cfg(feature = "debug")] + Balance { address, denom } => { + let querier = ElysQuerier::new(&deps.querier); + to_json_binary(&querier.get_balance(address, denom)?) + } + #[cfg(feature = "debug")] + AmmPriceByDenom { token_in, discount } => { + let querier = ElysQuerier::new(&deps.querier); + to_json_binary(&querier.get_amm_price_by_denom(token_in, discount)?) + } + #[cfg(feature = "debug")] + GetEdenEarnProgramDetails { address } => { + let querier = ElysQuerier::new(&deps.querier); + let aprs = querier.get_incentive_aprs().unwrap_or_default(); + + let generator = AccountSnapshotGenerator::new(&deps)?; + let program = get_eden_earn_program_details( + &deps, + Some(address.to_owned()), + ElysDenom::Eden.as_str().to_string(), + generator.metadata.uusdc_usd_price, + generator.metadata.uelys_price_in_uusdc, + QueryAprResponse { + apr: aprs.usdc_apr_eden, + }, + QueryAprResponse { + apr: aprs.eden_apr_eden, + }, + QueryAprResponse { + apr: aprs.edenb_apr_eden, + }, + ) + .unwrap_or_default(); + to_json_binary(&program) + } + #[cfg(feature = "debug")] + GetEdenBoostEarnProgramDetails { address } => { + let querier = ElysQuerier::new(&deps.querier); + let aprs = querier.get_incentive_aprs().unwrap_or_default(); + let program = get_eden_boost_earn_program_details( + &deps, + Some(address.to_owned()), + ElysDenom::EdenBoost.as_str().to_string(), + QueryAprResponse { + apr: aprs.usdc_apr_edenb, + }, + QueryAprResponse { + apr: aprs.eden_apr_edenb, + }, + ) + .unwrap_or_default(); + + to_json_binary(&program) + } + #[cfg(feature = "debug")] + GetElysEarnProgramDetails { address } => { + let querier = ElysQuerier::new(&deps.querier); + let aprs = querier.get_incentive_aprs().unwrap_or_default(); + + let generator = AccountSnapshotGenerator::new(&deps)?; + let program = get_elys_earn_program_details( + &deps, + Some(address.to_owned()), + ElysDenom::Elys.as_str().to_string(), + generator.metadata.uusdc_usd_price, + generator.metadata.uelys_price_in_uusdc, + QueryAprResponse { + apr: aprs.usdc_apr_elys, + }, + QueryAprResponse { + apr: aprs.eden_apr_elys, + }, + QueryAprResponse { + apr: aprs.edenb_apr_elys, + }, + ) + .unwrap_or_default(); + + to_json_binary(&program) + } + #[cfg(feature = "debug")] + GetUsdcEarnProgramDetails { address } => { + let generator = AccountSnapshotGenerator::new(&deps)?; + let program = get_usdc_earn_program_details( + &deps, + Some(address.to_owned()), + generator.metadata.usdc_denom.to_owned(), + generator.metadata.usdc_base_denom.to_owned(), + generator.metadata.uusdc_usd_price, + ) + .unwrap_or_default(); + + to_json_binary(&program) + } + #[cfg(feature = "debug")] + IncentiveAprs { .. } => { + let querier = ElysQuerier::new(&deps.querier); + let response = querier.get_incentive_aprs().unwrap_or_default(); + + to_json_binary(&response) + } + StorageSize {} => { + let user_address_queue_data_size = USER_ADDRESS_QUEUE.len(deps.storage)? as u128; + let history_data_size = HISTORY + .keys(deps.storage, None, None, cosmwasm_std::Order::Descending) + .count() as u128; + let old_history_2_data_size = OLD_HISTORY_2 + .keys(deps.storage, None, None, cosmwasm_std::Order::Descending) + .count() as u128; + + to_json_binary(&StorageSizeResp { + user_address_queue_data_size, + history_data_size, + old_history_2_data_size, + }) + } + Version {} => to_json_binary(&CONTRACT.load(deps.storage)?), + } +} diff --git a/contracts/account-history-contract/src/entry_point/sudo.rs b/contracts/account-history-contract/src/entry_point/sudo.rs new file mode 100644 index 00000000..950a6491 --- /dev/null +++ b/contracts/account-history-contract/src/entry_point/sudo.rs @@ -0,0 +1,20 @@ +use crate::action::execute::clean_up_storage; +use crate::action::sudo::update_metadata_prices; +use crate::states::DELETE_OLD_DATA_ENABLED; +use crate::{msg::SudoMsg, states::DELETE_EPOCH}; +use cosmwasm_std::{entry_point, DepsMut, Env, Response, StdResult}; +use elys_bindings::{ElysMsg, ElysQuery}; + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn sudo(mut deps: DepsMut, _env: Env, msg: SudoMsg) -> StdResult> { + match msg { + SudoMsg::ClockEndBlock {} => { + let epoch = DELETE_EPOCH.load(deps.storage)?; + if DELETE_OLD_DATA_ENABLED.load(deps.storage)? == true { + clean_up_storage(&mut deps, epoch)?; + } + update_metadata_prices(deps)?; + Ok(Response::new()) + } + } +} diff --git a/contracts/account-history-contract/src/error.rs b/contracts/account-history-contract/src/error.rs new file mode 100644 index 00000000..3672bd62 --- /dev/null +++ b/contracts/account-history-contract/src/error.rs @@ -0,0 +1,19 @@ +use cosmwasm_std::StdError; +use thiserror::Error; + +#[allow(dead_code)] +#[derive(Error, Debug, PartialEq)] +pub enum ContractError { + #[error("{0}")] + StdError(#[from] StdError), + #[error("PortfolioError")] + PortfolioError {}, + #[error("RewardError")] + RewardError {}, + #[error("TotalBalanceError")] + TotalBalanceError {}, + #[error("AssetDenomError")] + AssetDenomError {}, + #[error("{balance} is smaller than {amount}")] + InsufficientBalanceError { balance: u128, amount: u64 }, +} diff --git a/contracts/account-history-contract/src/lib.rs b/contracts/account-history-contract/src/lib.rs new file mode 100644 index 00000000..c2d0a800 --- /dev/null +++ b/contracts/account-history-contract/src/lib.rs @@ -0,0 +1,15 @@ +pub mod entry_point; + +pub use elys_bindings::account_history::msg; +pub use elys_bindings::account_history::types as bindings_account_history_types; + +pub mod utils; + +mod action; + +mod error; +mod states; +mod types; + +#[cfg(test)] +mod tests; diff --git a/contracts/account-history-contract/src/states/admin_address.rs b/contracts/account-history-contract/src/states/admin_address.rs new file mode 100644 index 00000000..e20f975e --- /dev/null +++ b/contracts/account-history-contract/src/states/admin_address.rs @@ -0,0 +1,3 @@ +use cw_storage_plus::Item; + +pub const PARAMS_ADMIN: Item = Item::new("params admin"); diff --git a/contracts/account-history-contract/src/states/enable_update_account.rs b/contracts/account-history-contract/src/states/enable_update_account.rs new file mode 100644 index 00000000..bc90f13c --- /dev/null +++ b/contracts/account-history-contract/src/states/enable_update_account.rs @@ -0,0 +1,3 @@ +use cw_storage_plus::Item; + +pub const UPDATE_ACCOUNT_ENABLED: Item = Item::new("update_account_enabled"); diff --git a/contracts/account-history-contract/src/states/expiration.rs b/contracts/account-history-contract/src/states/expiration.rs new file mode 100644 index 00000000..371be223 --- /dev/null +++ b/contracts/account-history-contract/src/states/expiration.rs @@ -0,0 +1,4 @@ +use cw_storage_plus::Item; +use cw_utils::Expiration; + +pub const EXPIRATION: Item = Item::new("expiration"); diff --git a/contracts/account-history-contract/src/states/history.rs b/contracts/account-history-contract/src/states/history.rs new file mode 100644 index 00000000..11717429 --- /dev/null +++ b/contracts/account-history-contract/src/states/history.rs @@ -0,0 +1,14 @@ +use std::collections::HashMap; + +use cw_storage_plus::{Item, Map}; +use elys_bindings::account_history::types::PortfolioBalanceSnapshot; + +pub const OLD_HISTORY_2: Map<&str, HashMap> = + Map::new("history_portfolio_balance_snapshot_2"); + +pub const HISTORY: Map<&str, PortfolioBalanceSnapshot> = + Map::new("history_portfolio_balance_snapshot_3"); + +pub const DELETE_OLD_DATA_ENABLED: Item = Item::new("delete_old_data_enabled"); + +pub const DELETE_EPOCH: Item = Item::new("delete_data_epoch"); diff --git a/contracts/account-history-contract/src/states/metadata.rs b/contracts/account-history-contract/src/states/metadata.rs new file mode 100644 index 00000000..b1f3aafc --- /dev/null +++ b/contracts/account-history-contract/src/states/metadata.rs @@ -0,0 +1,4 @@ +use cw_storage_plus::Item; +use elys_bindings::account_history::types::Metadata; + +pub const METADATA: Item = Item::new("metadata"); diff --git a/contracts/account-history-contract/src/states/mod.rs b/contracts/account-history-contract/src/states/mod.rs new file mode 100644 index 00000000..16595276 --- /dev/null +++ b/contracts/account-history-contract/src/states/mod.rs @@ -0,0 +1,17 @@ +mod admin_address; +mod enable_update_account; +mod expiration; +mod history; +mod metadata; +mod processed_account_per_block; +mod trade_shield_address; +mod user_address_queue; + +pub use admin_address::PARAMS_ADMIN; +pub use enable_update_account::UPDATE_ACCOUNT_ENABLED; +pub use expiration::EXPIRATION; +pub use history::{DELETE_EPOCH, DELETE_OLD_DATA_ENABLED, HISTORY, OLD_HISTORY_2}; +pub use metadata::METADATA; +pub use processed_account_per_block::PROCESSED_ACCOUNT_PER_BLOCK; +pub use trade_shield_address::TRADE_SHIELD_ADDRESS; +pub use user_address_queue::USER_ADDRESS_QUEUE; diff --git a/contracts/account-history-contract/src/states/processed_account_per_block.rs b/contracts/account-history-contract/src/states/processed_account_per_block.rs new file mode 100644 index 00000000..c169f7ce --- /dev/null +++ b/contracts/account-history-contract/src/states/processed_account_per_block.rs @@ -0,0 +1,3 @@ +use cw_storage_plus::Item; + +pub const PROCESSED_ACCOUNT_PER_BLOCK: Item = Item::new("processed account per block"); diff --git a/contracts/account-history-contract/src/states/trade_shield_address.rs b/contracts/account-history-contract/src/states/trade_shield_address.rs new file mode 100644 index 00000000..854103c7 --- /dev/null +++ b/contracts/account-history-contract/src/states/trade_shield_address.rs @@ -0,0 +1,3 @@ +use cw_storage_plus::Item; + +pub const TRADE_SHIELD_ADDRESS: Item> = Item::new("trade_shield_address2"); diff --git a/contracts/account-history-contract/src/states/user_address_queue.rs b/contracts/account-history-contract/src/states/user_address_queue.rs new file mode 100644 index 00000000..e598a898 --- /dev/null +++ b/contracts/account-history-contract/src/states/user_address_queue.rs @@ -0,0 +1,3 @@ +use cw_storage_plus::Deque; + +pub const USER_ADDRESS_QUEUE: Deque = Deque::new("user address queue 1"); diff --git a/contracts/account-history-contract/src/tests/get_all_pools.rs b/contracts/account-history-contract/src/tests/get_all_pools.rs new file mode 100644 index 00000000..764d5914 --- /dev/null +++ b/contracts/account-history-contract/src/tests/get_all_pools.rs @@ -0,0 +1,359 @@ +use std::collections::HashMap; +use std::str::FromStr; + +use crate::entry_point::instantiate; +use crate::{ + entry_point::{execute, query, sudo}, + msg::*, +}; +use anyhow::{bail, Error, Result as AnyResult}; +use cosmwasm_std::{ + coin, coins, to_json_binary, Addr, Coin, Decimal, Empty, Int128, StdError, Timestamp, Uint128, +}; +use cw_multi_test::{AppResponse, BasicAppBuilder, ContractWrapper, Executor, Module}; +use elys_bindings::account_history::types::CoinValue; +use elys_bindings::query_resp::{ + DelegationDelegatorReward, EstakingRewardsResponse, MasterchefUserPendingRewardData, + MasterchefUserPendingRewardResponse, PoolApr, PoolFilterType, PoolResp, QueryEarnPoolResponse, + QueryStableStakeAprResponse, Validator, +}; +use elys_bindings::types::{BalanceAvailable, PoolAsset}; +use elys_bindings::{ElysMsg, ElysQuery}; +use elys_bindings_test::{ + ElysModule, ACCOUNT, ASSET_INFO, LAST_MODULE_USED, PERPETUAL_OPENED_POSITION, PRICES, +}; +use trade_shield_contract::entry_point::{ + execute as trade_shield_execute, instantiate as trade_shield_init, query as trade_shield_query, +}; +use trade_shield_contract::msg::InstantiateMsg as TradeShieldInstantiateMsg; + +struct ElysModuleWrapper(ElysModule); + +impl Module for ElysModuleWrapper { + type QueryT = ElysQuery; + type ExecT = ElysMsg; + type SudoT = Empty; + + fn query( + &self, + api: &dyn cosmwasm_std::Api, + storage: &dyn cosmwasm_std::Storage, + querier: &dyn cosmwasm_std::Querier, + block: &cosmwasm_std::BlockInfo, + request: Self::QueryT, + ) -> AnyResult { + match request { + ElysQuery::AmmBalance { address, denom } => { + let resp = match (address.as_str(), denom.as_str()) { + ("user", "uedenb") => BalanceAvailable { + amount: Uint128::new(21798000), + usd_amount: Decimal::from_str("21798000").unwrap(), + }, + ( + "user", + "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", + ) => BalanceAvailable { + amount: Uint128::new(5333229342748), + usd_amount: Decimal::from_str("5333229342748").unwrap(), + }, + _ => BalanceAvailable { + amount: Uint128::zero(), + usd_amount: Decimal::zero(), + }, + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::MasterchefStableStakeApr { denom } => { + let resp = match denom.as_str() { + "uusdc" => QueryStableStakeAprResponse { + apr: Int128::zero(), + }, + "ueden" => QueryStableStakeAprResponse { + apr: Int128::zero(), + }, + _ => return Err(Error::new(StdError::not_found(denom))), + }; + Ok(to_json_binary(&resp)?) + } + + ElysQuery::MasterchefUserPendingReward { .. } => { + let resp = MasterchefUserPendingRewardResponse { + rewards: vec![MasterchefUserPendingRewardData { + pool_id: 32767u64, + reward: vec![Coin { + denom: "ueden".to_string(), + amount: Uint128::new(20), + }], + }], + total_rewards: vec![Coin { + denom: "ueden".to_string(), + amount: Uint128::new(20), + }], + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::AmmPriceByDenom { token_in, .. } => { + let spot_price = match token_in.denom.as_str() { + "uelys" => Decimal::from_str("3.5308010067676894").unwrap(), + "ueden" => Decimal::from_str("3.5308010067676894").unwrap(), + "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65" => { + Decimal::one() + } + "ibc/E2D2F6ADCC68AA3384B2F5DFACCA437923D137C14E86FB8A10207CF3BED0C8D4" => { + Decimal::from_str("9.02450744362719844").unwrap() + } + _ => return Err(Error::new(StdError::not_found(token_in.denom.as_str()))), + }; + Ok(to_json_binary(&spot_price)?) + } + ElysQuery::EstakingRewards { .. } => { + let resp = EstakingRewardsResponse { + rewards: vec![DelegationDelegatorReward { + validator_address: Validator::EdenBoost.to_string(), + reward: vec![Coin { + denom: "ueden".to_string(), + amount: Uint128::from_str("121").unwrap(), + }], + }], + total: vec![Coin { + denom: "uedenb".to_string(), + amount: Uint128::from_str("121").unwrap(), + }], + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::AmmEarnMiningPoolAll { .. } => { + let resp = QueryEarnPoolResponse { + pools: Some(vec![PoolResp { + pool_id: 1, + apr: Some(PoolApr { pool_id: 1, ..Default::default() }), + assets: vec![PoolAsset { + token: Coin { + denom: "uelys".to_string(), + amount: Uint128::new(100), + }, + weight: Uint128::new(1), + usd_value: Some(Decimal::from_str("353.08010067676894").unwrap()), + }], + pool_ratio: "".to_string(), + current_pool_ratio: None, + current_pool_ratio_string: None, + rewards_apr: Decimal::one(), + rewards_usd: Decimal::from_str("10").unwrap(), + reward_coins: vec![Coin { + denom: "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65".to_string(), + amount: Uint128::new(10), + }], + fiat_rewards: None, + borrow_apr: Decimal::from_str("2").unwrap(), + leverage_lp: Decimal::zero(), + perpetual: Decimal::zero(), + lp_token_price: None, + tvl: Decimal::zero(), + total_shares: Coin { denom: "uelys".to_string(), amount: Uint128::new(1000) }, + share_usd_price: Some(Decimal::from_str("3530.8010067676894").unwrap()), + swap_fee: Decimal::from_str("0.1").unwrap(), + fee_denom: "uelys".to_string(), + use_oracle: Some(true), + is_leveragelp: Some(true), + }]), + }; + Ok(to_json_binary(&resp)?) + } + + _ => self.0.query(api, storage, querier, block, request), + } + } + + fn execute( + &self, + api: &dyn cosmwasm_std::Api, + storage: &mut dyn cosmwasm_std::Storage, + router: &dyn cw_multi_test::CosmosRouter, + block: &cosmwasm_std::BlockInfo, + sender: Addr, + msg: Self::ExecT, + ) -> AnyResult + where + ExecC: std::fmt::Debug + + Clone + + PartialEq + + schemars::JsonSchema + + serde::de::DeserializeOwned + + 'static, + QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, + { + match msg { + _ => self.0.execute(api, storage, router, block, sender, msg), + } + } + + fn sudo( + &self, + _api: &dyn cosmwasm_std::Api, + _storage: &mut dyn cosmwasm_std::Storage, + _router: &dyn cw_multi_test::CosmosRouter, + _block: &cosmwasm_std::BlockInfo, + _msg: Self::SudoT, + ) -> AnyResult + where + ExecC: std::fmt::Debug + + Clone + + PartialEq + + schemars::JsonSchema + + serde::de::DeserializeOwned + + 'static, + QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, + { + bail!("sudo is not implemented for ElysModule") + } +} + +#[test] +fn get_all_pools() { + // Create a wallet for the "user" with an initial balance of 100 usdc + let wallet = vec![( + "user", + coins( + 100, + "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", + ), + )]; + + let mut addresses: Vec = vec![]; + let mut app = BasicAppBuilder::::new_custom() + .with_custom(ElysModuleWrapper(ElysModule {})) + .build(|roouter, _, storage| { + for (wallet_owner, wallet_contenent) in wallet { + roouter + .bank + .init_balance(storage, &Addr::unchecked(wallet_owner), wallet_contenent) + .unwrap(); + addresses.push(wallet_owner.to_owned()) + } + ACCOUNT.save(storage, &addresses).unwrap(); + PERPETUAL_OPENED_POSITION.save(storage, &vec![]).unwrap(); + ASSET_INFO.save(storage, &vec![]).unwrap(); + PRICES.save(storage, &vec![]).unwrap(); + LAST_MODULE_USED.save(storage, &None).unwrap(); + }); + + // trade shield deployment + let trade_shield_code = + ContractWrapper::new(trade_shield_execute, trade_shield_init, trade_shield_query); + let trade_shield_code_id = app.store_code(Box::new(trade_shield_code)); + let trade_shield_init = TradeShieldInstantiateMsg { + account_history_address: None, + }; + let trade_shield_address = app + .instantiate_contract( + trade_shield_code_id, + Addr::unchecked("owner"), + &trade_shield_init, + &[], + "Contract", + None, + ) + .unwrap() + .to_string(); + + // Create a contract wrapper and store its code. + let code = ContractWrapper::new(execute, instantiate, query).with_sudo(sudo); + let code_id = app.store_code(Box::new(code)); + + // Create a mock message to instantiate the contract with no initial orders. + let instantiate_msg = InstantiateMsg { + limit: Some(3), + expiration: Some(cw_utils::Expiration::AtTime(Timestamp::from_seconds( + 604800, + ))), + trade_shield_address: Some(trade_shield_address), + }; + + // Instantiate the contract with "owner" as the deployer. + let addr = app + .instantiate_contract( + code_id, + Addr::unchecked("owner"), + &instantiate_msg, + &[], + "Contract", + None, + ) + .unwrap(); + + let msg = app.wasm_sudo(addr.clone(), &SudoMsg::ClockEndBlock {}); + + println!("{:?}", msg); + + let resp: QueryEarnPoolResponse = app + .wrap() + .query_wasm_smart( + &addr, + &QueryMsg::GetLiquidityPools { + pool_ids: Some(vec![1u64]), + filter_type: PoolFilterType::FilterAll, + pagination: None, + }, + ) + .unwrap(); + + let mut current_pool_ratio = HashMap::new(); + current_pool_ratio.insert("uelys".to_string(), Decimal::one()); + + let mut current = HashMap::new(); + current.insert("uelys".to_string(), Decimal::one()); + + let expected = QueryEarnPoolResponse { + pools: Some( + [PoolResp { + pool_id: 1, + apr: Some(PoolApr { + pool_id: 1, + ..Default::default() + }), + assets: [PoolAsset { + token: coin(100, "uelys"), + weight: Uint128::one(), + usd_value: Some(Decimal::from_str("0.000353080100676768").unwrap()), + }] + .to_vec(), + pool_ratio: "".to_string(), + current_pool_ratio: Some(current), + current_pool_ratio_string: None, + rewards_apr: Decimal::one(), + rewards_usd: Decimal::from_str("10").unwrap(), + reward_coins: coins( + 10, + "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", + ) + .to_vec(), + fiat_rewards: Some( + [CoinValue { + denom: + "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65" + .to_string(), + amount_token: Decimal::from_str("0.00001").unwrap(), + price: Decimal::one(), + amount_usd: Decimal::from_str("0.00001").unwrap(), + }] + .to_vec(), + ), + borrow_apr: Decimal::from_str("2").unwrap(), + leverage_lp: Decimal::zero(), + perpetual: Decimal::zero(), + lp_token_price: None, + tvl: Decimal::zero(), + total_shares: coin(1000, "uelys"), + share_usd_price: Some(Decimal::zero()), + swap_fee: Decimal::from_str("0.1").unwrap(), + fee_denom: "uelys".to_string(), + use_oracle: Some(true), + is_leveragelp: Some(true), + }] + .to_vec(), + ), + }; + + assert_eq!(resp, expected); +} diff --git a/contracts/account-history-contract/src/tests/get_eden_boost_earn_program_details.rs b/contracts/account-history-contract/src/tests/get_eden_boost_earn_program_details.rs new file mode 100644 index 00000000..b188c0f9 --- /dev/null +++ b/contracts/account-history-contract/src/tests/get_eden_boost_earn_program_details.rs @@ -0,0 +1,295 @@ +use std::str::FromStr; + +use crate::entry_point::instantiate; +use crate::{ + entry_point::{execute, query, sudo}, + msg::*, +}; +use anyhow::{bail, Error, Result as AnyResult}; +use cosmwasm_std::{ + coins, to_json_binary, Addr, Coin, Decimal, Empty, Int128, StdError, Timestamp, Uint128, +}; +use cw_multi_test::{AppResponse, BasicAppBuilder, ContractWrapper, Executor, Module}; +use elys_bindings::account_history::msg::query_resp::earn::GetEdenBoostEarnProgramResp; +use elys_bindings::account_history::types::earn_detail::earn_detail::AprEdenBoost; +use elys_bindings::account_history::types::earn_program::EdenBoostEarnProgram; +use elys_bindings::account_history::types::CoinValue; +use elys_bindings::query_resp::{ + BalanceBorrowed, DelegationDelegatorReward, EstakingRewardsResponse, + MasterchefUserPendingRewardData, MasterchefUserPendingRewardResponse, + QueryStableStakeAprResponse, StakedAvailable, Validator, +}; +use elys_bindings::types::BalanceAvailable; +use elys_bindings::{ElysMsg, ElysQuery}; +use elys_bindings_test::{ + ElysModule, ACCOUNT, ASSET_INFO, LAST_MODULE_USED, PERPETUAL_OPENED_POSITION, PRICES, +}; +use trade_shield_contract::entry_point::{ + execute as trade_shield_execute, instantiate as trade_shield_init, query as trade_shield_query, +}; +use trade_shield_contract::msg::InstantiateMsg as TradeShieldInstantiateMsg; + +struct ElysModuleWrapper(ElysModule); + +impl Module for ElysModuleWrapper { + type QueryT = ElysQuery; + type ExecT = ElysMsg; + type SudoT = Empty; + + fn query( + &self, + api: &dyn cosmwasm_std::Api, + storage: &dyn cosmwasm_std::Storage, + querier: &dyn cosmwasm_std::Querier, + block: &cosmwasm_std::BlockInfo, + request: Self::QueryT, + ) -> AnyResult { + match request { + ElysQuery::AmmBalance { address, denom } => { + let resp = match (address.as_str(), denom.as_str()) { + ("user", "uedenb") => BalanceAvailable { + amount: Uint128::new(21798000), + usd_amount: Decimal::from_str("21798000").unwrap(), + }, + ( + "user", + "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", + ) => BalanceAvailable { + amount: Uint128::new(5333229342748), + usd_amount: Decimal::from_str("5333229342748").unwrap(), + }, + _ => BalanceAvailable { + amount: Uint128::zero(), + usd_amount: Decimal::zero(), + }, + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::MasterchefStableStakeApr { denom } => { + let resp = match denom.as_str() { + "uusdc" => QueryStableStakeAprResponse { + apr: Int128::zero(), + }, + "ueden" => QueryStableStakeAprResponse { + apr: Int128::zero(), + }, + _ => return Err(Error::new(StdError::not_found(denom))), + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::CommitmentStakedBalanceOfDenom { .. } => { + let resp = StakedAvailable { + usd_amount: Decimal::from_atomics(Uint128::new(100130012), 3).unwrap(), + amount: Uint128::new(100120000000), + lockups: None, + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::StableStakeBalanceOfBorrow {} => { + let resp = BalanceBorrowed { + usd_amount: Decimal::from_atomics(Uint128::new(3265035180871), 10).unwrap(), + percentage: Decimal::from_atomics(Uint128::new(0000238391578776388), 18) + .unwrap(), + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::MasterchefUserPendingReward { .. } => { + let resp = MasterchefUserPendingRewardResponse { + rewards: vec![MasterchefUserPendingRewardData { + pool_id: 32767u64, + reward: vec![Coin { + denom: "ueden".to_string(), + amount: Uint128::new(20), + }], + }], + total_rewards: vec![Coin { + denom: "ueden".to_string(), + amount: Uint128::new(20), + }], + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::AmmPriceByDenom { token_in, .. } => { + let spot_price = match token_in.denom.as_str() { + "uelys" => Decimal::from_str("3.5308010067676894").unwrap(), + "ueden" => Decimal::from_str("3.5308010067676894").unwrap(), + "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65" => { + Decimal::one() + } + "ibc/E2D2F6ADCC68AA3384B2F5DFACCA437923D137C14E86FB8A10207CF3BED0C8D4" => { + Decimal::from_str("9.02450744362719844").unwrap() + } + _ => return Err(Error::new(StdError::not_found(token_in.denom.as_str()))), + }; + Ok(to_json_binary(&spot_price)?) + } + ElysQuery::EstakingRewards { .. } => { + let resp = EstakingRewardsResponse { + rewards: vec![DelegationDelegatorReward { + validator_address: Validator::EdenBoost.to_string(), + reward: vec![Coin { + denom: "ueden".to_string(), + amount: Uint128::from_str("121").unwrap(), + }], + }], + total: vec![Coin { + denom: "uedenb".to_string(), + amount: Uint128::from_str("121").unwrap(), + }], + }; + Ok(to_json_binary(&resp)?) + } + _ => self.0.query(api, storage, querier, block, request), + } + } + + fn execute( + &self, + api: &dyn cosmwasm_std::Api, + storage: &mut dyn cosmwasm_std::Storage, + router: &dyn cw_multi_test::CosmosRouter, + block: &cosmwasm_std::BlockInfo, + sender: Addr, + msg: Self::ExecT, + ) -> AnyResult + where + ExecC: std::fmt::Debug + + Clone + + PartialEq + + schemars::JsonSchema + + serde::de::DeserializeOwned + + 'static, + QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, + { + match msg { + _ => self.0.execute(api, storage, router, block, sender, msg), + } + } + + fn sudo( + &self, + _api: &dyn cosmwasm_std::Api, + _storage: &mut dyn cosmwasm_std::Storage, + _router: &dyn cw_multi_test::CosmosRouter, + _block: &cosmwasm_std::BlockInfo, + _msg: Self::SudoT, + ) -> AnyResult + where + ExecC: std::fmt::Debug + + Clone + + PartialEq + + schemars::JsonSchema + + serde::de::DeserializeOwned + + 'static, + QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, + { + bail!("sudo is not implemented for ElysModule") + } +} + +#[test] +fn get_eden_boost_earn_program_details() { + // Create a wallet for the "user" with an initial balance of 100 usdc + let wallet = vec![( + "user", + coins( + 100, + "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", + ), + )]; + + let mut addresses: Vec = vec![]; + let mut app = BasicAppBuilder::::new_custom() + .with_custom(ElysModuleWrapper(ElysModule {})) + .build(|roouter, _, storage| { + for (wallet_owner, wallet_contenent) in wallet { + roouter + .bank + .init_balance(storage, &Addr::unchecked(wallet_owner), wallet_contenent) + .unwrap(); + addresses.push(wallet_owner.to_owned()) + } + ACCOUNT.save(storage, &addresses).unwrap(); + PERPETUAL_OPENED_POSITION.save(storage, &vec![]).unwrap(); + ASSET_INFO.save(storage, &vec![]).unwrap(); + PRICES.save(storage, &vec![]).unwrap(); + LAST_MODULE_USED.save(storage, &None).unwrap(); + }); + + // trade shield deployment + let trade_shield_code = + ContractWrapper::new(trade_shield_execute, trade_shield_init, trade_shield_query); + let trade_shield_code_id = app.store_code(Box::new(trade_shield_code)); + let trade_shield_init = TradeShieldInstantiateMsg { + account_history_address: None, + }; + let trade_shield_address = app + .instantiate_contract( + trade_shield_code_id, + Addr::unchecked("owner"), + &trade_shield_init, + &[], + "Contract", + None, + ) + .unwrap() + .to_string(); + + // Create a contract wrapper and store its code. + let code = ContractWrapper::new(execute, instantiate, query).with_sudo(sudo); + let code_id = app.store_code(Box::new(code)); + + // Create a mock message to instantiate the contract with no initial orders. + let instantiate_msg = InstantiateMsg { + limit: Some(3), + expiration: Some(cw_utils::Expiration::AtTime(Timestamp::from_seconds( + 604800, + ))), + trade_shield_address: Some(trade_shield_address), + }; + + // Instantiate the contract with "owner" as the deployer. + let addr = app + .instantiate_contract( + code_id, + Addr::unchecked("owner"), + &instantiate_msg, + &[], + "Contract", + None, + ) + .unwrap(); + + app.wasm_sudo(addr.clone(), &SudoMsg::ClockEndBlock {}) + .unwrap(); + + let resp: GetEdenBoostEarnProgramResp = app + .wrap() + .query_wasm_smart( + &addr, + &QueryMsg::GetEdenBoostEarnProgramDetails { + address: "user".to_string(), + }, + ) + .unwrap(); + + let expected = GetEdenBoostEarnProgramResp { + data: EdenBoostEarnProgram { + bonding_period: 0, + apr: AprEdenBoost { + uusdc: Uint128::zero(), + ueden: Uint128::zero(), + }, + available: Some(Uint128::new(21798000)), + staked: Some(Uint128::new(100120000000)), + rewards: Some(vec![CoinValue { + denom: "ueden".to_string(), + amount_token: Decimal::from_str("0.000121").unwrap(), + price: Decimal::from_str("3.5308010067676894").unwrap(), + amount_usd: Decimal::from_str("0.00042722692181889").unwrap(), + }]), + }, + }; + + assert_eq!(resp, expected); +} diff --git a/contracts/account-history-contract/src/tests/get_eden_earn_program_details.rs b/contracts/account-history-contract/src/tests/get_eden_earn_program_details.rs new file mode 100644 index 00000000..f521428f --- /dev/null +++ b/contracts/account-history-contract/src/tests/get_eden_earn_program_details.rs @@ -0,0 +1,308 @@ +use std::str::FromStr; + +use crate::entry_point::instantiate; +use crate::{ + entry_point::{execute, query, sudo}, + msg::*, +}; +use anyhow::{bail, Error, Result as AnyResult}; +use cosmwasm_std::{ + coins, to_json_binary, Addr, Coin, Decimal, Empty, Int128, StdError, Timestamp, Uint128, +}; +use cw_multi_test::{AppResponse, BasicAppBuilder, ContractWrapper, Executor, Module}; +use elys_bindings::account_history::msg::query_resp::earn::GetEdenEarnProgramResp; +use elys_bindings::account_history::types::earn_program::EdenEarnProgram; +use elys_bindings::account_history::types::{AprElys, CoinValue}; +use elys_bindings::query_resp::{ + BalanceBorrowed, DelegationDelegatorReward, EstakingRewardsResponse, + MasterchefUserPendingRewardData, MasterchefUserPendingRewardResponse, + QueryStableStakeAprResponse, StakedAvailable, +}; +use elys_bindings::types::BalanceAvailable; +use elys_bindings::{ElysMsg, ElysQuery}; +use elys_bindings_test::{ + ElysModule, ACCOUNT, ASSET_INFO, LAST_MODULE_USED, PERPETUAL_OPENED_POSITION, PRICES, +}; +use trade_shield_contract::entry_point::{ + execute as trade_shield_execute, instantiate as trade_shield_init, query as trade_shield_query, +}; +use trade_shield_contract::msg::InstantiateMsg as TradeShieldInstantiateMsg; + +struct ElysModuleWrapper(ElysModule); + +impl Module for ElysModuleWrapper { + type QueryT = ElysQuery; + type ExecT = ElysMsg; + type SudoT = Empty; + + fn query( + &self, + api: &dyn cosmwasm_std::Api, + storage: &dyn cosmwasm_std::Storage, + querier: &dyn cosmwasm_std::Querier, + block: &cosmwasm_std::BlockInfo, + request: Self::QueryT, + ) -> AnyResult { + match request { + ElysQuery::AmmBalance { address, denom } => { + let resp = match (address.as_str(), denom.as_str()) { + ("user", "ueden") => BalanceAvailable { + amount: Uint128::new(21798000), + usd_amount: Decimal::from_str("21798000").unwrap(), + }, + ( + "user", + "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", + ) => BalanceAvailable { + amount: Uint128::new(5333229342748), + usd_amount: Decimal::from_str("5333229342748").unwrap(), + }, + _ => BalanceAvailable { + amount: Uint128::zero(), + usd_amount: Decimal::zero(), + }, + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::MasterchefStableStakeApr { denom } => { + let resp = match denom.as_str() { + "uusdc" => QueryStableStakeAprResponse { + apr: Int128::zero(), + }, + "ueden" => QueryStableStakeAprResponse { + apr: Int128::zero(), + }, + _ => return Err(Error::new(StdError::not_found(denom))), + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::CommitmentStakedBalanceOfDenom { .. } => { + let resp = StakedAvailable { + usd_amount: Decimal::from_atomics(Uint128::new(100130012), 3).unwrap(), + amount: Uint128::new(100120000000), + lockups: None, + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::StableStakeBalanceOfBorrow {} => { + let resp = BalanceBorrowed { + usd_amount: Decimal::from_atomics(Uint128::new(3265035180871), 10).unwrap(), + percentage: Decimal::from_atomics(Uint128::new(0000238391578776388), 18) + .unwrap(), + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::MasterchefUserPendingReward { .. } => { + let resp = MasterchefUserPendingRewardResponse { + rewards: vec![MasterchefUserPendingRewardData { + pool_id: 32767u64, + reward: vec![Coin { + denom: "ueden".to_string(), + amount: Uint128::new(20), + }], + }], + total_rewards: vec![Coin { + denom: "ueden".to_string(), + amount: Uint128::new(20), + }], + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::AmmPriceByDenom { token_in, .. } => { + let spot_price = match token_in.denom.as_str() { + "uelys" => Decimal::from_str("3.5308010067676894").unwrap(), + "ueden" => Decimal::from_str("3.5308010067676894").unwrap(), + "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65" => { + Decimal::one() + } + "ibc/E2D2F6ADCC68AA3384B2F5DFACCA437923D137C14E86FB8A10207CF3BED0C8D4" => { + Decimal::from_str("9.02450744362719844").unwrap() + } + _ => return Err(Error::new(StdError::not_found(token_in.denom.as_str()))), + }; + Ok(to_json_binary(&spot_price)?) + } + ElysQuery::EstakingRewards { .. } => { + let resp = EstakingRewardsResponse { + rewards: vec![DelegationDelegatorReward { + validator_address: "elysvaloper1gnmpr8vvslp3shcq6e922xr0uq4aa2w5gdzht0" + .to_string(), + reward: vec![Coin { + denom: "ueden".to_string(), + amount: Uint128::from_str("121").unwrap(), + }], + }], + total: vec![Coin { + denom: "ueden".to_string(), + amount: Uint128::from_str("121").unwrap(), + }], + }; + Ok(to_json_binary(&resp)?) + } + _ => self.0.query(api, storage, querier, block, request), + } + } + + fn execute( + &self, + api: &dyn cosmwasm_std::Api, + storage: &mut dyn cosmwasm_std::Storage, + router: &dyn cw_multi_test::CosmosRouter, + block: &cosmwasm_std::BlockInfo, + sender: Addr, + msg: Self::ExecT, + ) -> AnyResult + where + ExecC: std::fmt::Debug + + Clone + + PartialEq + + schemars::JsonSchema + + serde::de::DeserializeOwned + + 'static, + QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, + { + match msg { + _ => self.0.execute(api, storage, router, block, sender, msg), + } + } + + fn sudo( + &self, + _api: &dyn cosmwasm_std::Api, + _storage: &mut dyn cosmwasm_std::Storage, + _router: &dyn cw_multi_test::CosmosRouter, + _block: &cosmwasm_std::BlockInfo, + _msg: Self::SudoT, + ) -> AnyResult + where + ExecC: std::fmt::Debug + + Clone + + PartialEq + + schemars::JsonSchema + + serde::de::DeserializeOwned + + 'static, + QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, + { + bail!("sudo is not implemented for ElysModule") + } +} + +#[test] +fn get_eden_earn_program_details() { + // Create a wallet for the "user" with an initial balance of 100 usdc + let wallet = vec![( + "user", + coins( + 100, + "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", + ), + )]; + + let mut addresses: Vec = vec![]; + let mut app = BasicAppBuilder::::new_custom() + .with_custom(ElysModuleWrapper(ElysModule {})) + .build(|roouter, _, storage| { + for (wallet_owner, wallet_contenent) in wallet { + roouter + .bank + .init_balance(storage, &Addr::unchecked(wallet_owner), wallet_contenent) + .unwrap(); + addresses.push(wallet_owner.to_owned()) + } + ACCOUNT.save(storage, &addresses).unwrap(); + PERPETUAL_OPENED_POSITION.save(storage, &vec![]).unwrap(); + ASSET_INFO.save(storage, &vec![]).unwrap(); + PRICES.save(storage, &vec![]).unwrap(); + LAST_MODULE_USED.save(storage, &None).unwrap(); + }); + + // trade shield deployment + let trade_shield_code = + ContractWrapper::new(trade_shield_execute, trade_shield_init, trade_shield_query); + let trade_shield_code_id = app.store_code(Box::new(trade_shield_code)); + let trade_shield_init = TradeShieldInstantiateMsg { + account_history_address: None, + }; + let trade_shield_address = app + .instantiate_contract( + trade_shield_code_id, + Addr::unchecked("owner"), + &trade_shield_init, + &[], + "Contract", + None, + ) + .unwrap() + .to_string(); + + // Create a contract wrapper and store its code. + let code = ContractWrapper::new(execute, instantiate, query).with_sudo(sudo); + let code_id = app.store_code(Box::new(code)); + + // Create a mock message to instantiate the contract with no initial orders. + let instantiate_msg = InstantiateMsg { + limit: Some(3), + expiration: Some(cw_utils::Expiration::AtTime(Timestamp::from_seconds( + 604800, + ))), + trade_shield_address: Some(trade_shield_address), + }; + + // Instantiate the contract with "owner" as the deployer. + let addr = app + .instantiate_contract( + code_id, + Addr::unchecked("owner"), + &instantiate_msg, + &[], + "Contract", + None, + ) + .unwrap(); + + app.wasm_sudo(addr.clone(), &SudoMsg::ClockEndBlock {}) + .unwrap(); + + let resp: GetEdenEarnProgramResp = app + .wrap() + .query_wasm_smart( + &addr, + &QueryMsg::GetEdenEarnProgramDetails { + address: "user".to_string(), + }, + ) + .unwrap(); + + let expected = GetEdenEarnProgramResp { + data: EdenEarnProgram { + bonding_period: 0, + apr: AprElys { + uusdc: Uint128::zero(), + ueden: Uint128::zero(), + uedenb: Uint128::zero(), + }, + available: Some(BalanceAvailable { + amount: Uint128::new(21798000), + usd_amount: Decimal::from_str("76.964400345522093541").unwrap(), + }), + staked: Some(StakedAvailable { + usd_amount: Decimal::from_str("100130.012").unwrap(), + amount: Uint128::new(100120000000), + lockups: None, + }), + rewards: Some(vec![CoinValue { + denom: "ueden".to_string(), + amount_token: Decimal::from_str("0.000121").unwrap(), + price: Decimal::from_str("3.5308010067676894").unwrap(), + amount_usd: Decimal::from_str("0.00042722692181889").unwrap(), + }]), + vesting: BalanceAvailable { + amount: Uint128::from_str("100").unwrap(), + usd_amount: Decimal::from_str("100").unwrap(), + }, + vesting_details: Some(vec![]), + }, + }; + + assert_eq!(resp, expected); +} diff --git a/contracts/account-history-contract/src/tests/get_elys_earn_program_detail.rs b/contracts/account-history-contract/src/tests/get_elys_earn_program_detail.rs new file mode 100644 index 00000000..9fce1ac3 --- /dev/null +++ b/contracts/account-history-contract/src/tests/get_elys_earn_program_detail.rs @@ -0,0 +1,304 @@ +use std::str::FromStr; + +use crate::entry_point::instantiate; +use crate::{ + entry_point::{execute, query, sudo}, + msg::*, +}; +use anyhow::{bail, Error, Result as AnyResult}; +use cosmwasm_std::{ + coins, to_json_binary, Addr, Coin, Decimal, Empty, Int128, StdError, Timestamp, Uint128, +}; +use cw_multi_test::{AppResponse, BasicAppBuilder, ContractWrapper, Executor, Module}; +use elys_bindings::account_history::msg::query_resp::earn::GetElysEarnProgramResp; +use elys_bindings::account_history::types::earn_program::ElysEarnProgram; +use elys_bindings::account_history::types::{AprElys, CoinValue}; +use elys_bindings::query_resp::{ + BalanceBorrowed, DelegationDelegatorReward, EstakingRewardsResponse, + MasterchefUserPendingRewardData, MasterchefUserPendingRewardResponse, + QueryStableStakeAprResponse, StakedAvailable, +}; +use elys_bindings::types::BalanceAvailable; +use elys_bindings::{ElysMsg, ElysQuery}; +use elys_bindings_test::{ + ElysModule, ACCOUNT, ASSET_INFO, LAST_MODULE_USED, PERPETUAL_OPENED_POSITION, PRICES, +}; +use trade_shield_contract::entry_point::{ + execute as trade_shield_execute, instantiate as trade_shield_init, query as trade_shield_query, +}; +use trade_shield_contract::msg::InstantiateMsg as TradeShieldInstantiateMsg; + +struct ElysModuleWrapper(ElysModule); + +impl Module for ElysModuleWrapper { + type QueryT = ElysQuery; + type ExecT = ElysMsg; + type SudoT = Empty; + + fn query( + &self, + api: &dyn cosmwasm_std::Api, + storage: &dyn cosmwasm_std::Storage, + querier: &dyn cosmwasm_std::Querier, + block: &cosmwasm_std::BlockInfo, + request: Self::QueryT, + ) -> AnyResult { + match request { + ElysQuery::AmmBalance { address, denom } => { + let resp = match (address.as_str(), denom.as_str()) { + ( + "user", + "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", + ) => BalanceAvailable { + amount: Uint128::new(1234), + usd_amount: Decimal::from_str("1234").unwrap(), + }, + ("user", "uelys") => BalanceAvailable { + amount: Uint128::new(45666543), + usd_amount: Decimal::from_str("45666543").unwrap(), + }, + _ => BalanceAvailable { + amount: Uint128::zero(), + usd_amount: Decimal::zero(), + }, + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::MasterchefStableStakeApr { denom } => { + let resp = match denom.as_str() { + "uusdc" => QueryStableStakeAprResponse { + apr: Int128::zero(), + }, + "ueden" => QueryStableStakeAprResponse { + apr: Int128::zero(), + }, + _ => return Err(Error::new(StdError::not_found(denom))), + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::CommitmentStakedBalanceOfDenom { .. } => { + let resp = StakedAvailable { + usd_amount: Decimal::from_atomics(Uint128::new(100130012), 3).unwrap(), + amount: Uint128::new(100120000000), + lockups: None, + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::StableStakeBalanceOfBorrow {} => { + let resp = BalanceBorrowed { + usd_amount: Decimal::from_atomics(Uint128::new(3265035180871), 10).unwrap(), + percentage: Decimal::from_atomics(Uint128::new(0000238391578776388), 18) + .unwrap(), + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::MasterchefUserPendingReward { .. } => { + let resp = MasterchefUserPendingRewardResponse { + rewards: vec![MasterchefUserPendingRewardData { + pool_id: 32767u64, + reward: vec![Coin { + denom: "ueden".to_string(), + amount: Uint128::new(20), + }], + }], + total_rewards: vec![Coin { + denom: "ueden".to_string(), + amount: Uint128::new(20), + }], + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::AmmPriceByDenom { token_in, .. } => { + let spot_price = match token_in.denom.as_str() { + "uelys" => Decimal::from_str("3.5308010067676894").unwrap(), + "ueden" => Decimal::from_str("3.5308010067676894").unwrap(), + "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65" => { + Decimal::one() + } + "ibc/E2D2F6ADCC68AA3384B2F5DFACCA437923D137C14E86FB8A10207CF3BED0C8D4" => { + Decimal::from_str("9.02450744362719844").unwrap() + } + _ => return Err(Error::new(StdError::not_found(token_in.denom.as_str()))), + }; + Ok(to_json_binary(&spot_price)?) + } + ElysQuery::EstakingRewards { .. } => { + let resp = EstakingRewardsResponse { + rewards: vec![DelegationDelegatorReward { + validator_address: "validator".to_string(), + reward: vec![Coin { + denom: "ueden".to_string(), + amount: Uint128::from_str("121").unwrap(), + }], + }], + total: vec![Coin { + denom: "ueden".to_string(), + amount: Uint128::from_str("121").unwrap(), + }], + }; + Ok(to_json_binary(&resp)?) + } + _ => self.0.query(api, storage, querier, block, request), + } + } + + fn execute( + &self, + api: &dyn cosmwasm_std::Api, + storage: &mut dyn cosmwasm_std::Storage, + router: &dyn cw_multi_test::CosmosRouter, + block: &cosmwasm_std::BlockInfo, + sender: Addr, + msg: Self::ExecT, + ) -> AnyResult + where + ExecC: std::fmt::Debug + + Clone + + PartialEq + + schemars::JsonSchema + + serde::de::DeserializeOwned + + 'static, + QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, + { + match msg { + _ => self.0.execute(api, storage, router, block, sender, msg), + } + } + + fn sudo( + &self, + _api: &dyn cosmwasm_std::Api, + _storage: &mut dyn cosmwasm_std::Storage, + _router: &dyn cw_multi_test::CosmosRouter, + _block: &cosmwasm_std::BlockInfo, + _msg: Self::SudoT, + ) -> AnyResult + where + ExecC: std::fmt::Debug + + Clone + + PartialEq + + schemars::JsonSchema + + serde::de::DeserializeOwned + + 'static, + QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, + { + bail!("sudo is not implemented for ElysModule") + } +} + +#[test] +fn get_elys_earn_program_details() { + // Create a wallet for the "user" with an initial balance of 100 usdc + let wallet = vec![( + "user", + coins( + 100, + "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", + ), + )]; + + let mut addresses: Vec = vec![]; + let mut app = BasicAppBuilder::::new_custom() + .with_custom(ElysModuleWrapper(ElysModule {})) + .build(|roouter, _, storage| { + for (wallet_owner, wallet_contenent) in wallet { + roouter + .bank + .init_balance(storage, &Addr::unchecked(wallet_owner), wallet_contenent) + .unwrap(); + addresses.push(wallet_owner.to_owned()) + } + ACCOUNT.save(storage, &addresses).unwrap(); + PERPETUAL_OPENED_POSITION.save(storage, &vec![]).unwrap(); + ASSET_INFO.save(storage, &vec![]).unwrap(); + PRICES.save(storage, &vec![]).unwrap(); + LAST_MODULE_USED.save(storage, &None).unwrap(); + }); + + // trade shield deployment + let trade_shield_code = + ContractWrapper::new(trade_shield_execute, trade_shield_init, trade_shield_query); + let trade_shield_code_id = app.store_code(Box::new(trade_shield_code)); + let trade_shield_init = TradeShieldInstantiateMsg { + account_history_address: None, + }; + let trade_shield_address = app + .instantiate_contract( + trade_shield_code_id, + Addr::unchecked("owner"), + &trade_shield_init, + &[], + "Contract", + None, + ) + .unwrap() + .to_string(); + + // Create a contract wrapper and store its code. + let code = ContractWrapper::new(execute, instantiate, query).with_sudo(sudo); + let code_id = app.store_code(Box::new(code)); + + // Create a mock message to instantiate the contract with no initial orders. + let instantiate_msg = InstantiateMsg { + limit: Some(3), + expiration: Some(cw_utils::Expiration::AtTime(Timestamp::from_seconds( + 604800, + ))), + trade_shield_address: Some(trade_shield_address), + }; + + // Instantiate the contract with "owner" as the deployer. + let addr = app + .instantiate_contract( + code_id, + Addr::unchecked("owner"), + &instantiate_msg, + &[], + "Contract", + None, + ) + .unwrap(); + + app.wasm_sudo(addr.clone(), &SudoMsg::ClockEndBlock {}) + .unwrap(); + + let resp: GetElysEarnProgramResp = app + .wrap() + .query_wasm_smart( + &addr, + &QueryMsg::GetElysEarnProgramDetails { + address: "user".to_string(), + }, + ) + .unwrap(); + + let expected = GetElysEarnProgramResp { + data: ElysEarnProgram { + bonding_period: 14, + apr: AprElys { + uusdc: Uint128::zero(), + ueden: Uint128::zero(), + uedenb: Uint128::zero(), + }, + available: Some(BalanceAvailable { + amount: Uint128::new(45666543), + usd_amount: Decimal::from_str("161.239475999999978995").unwrap(), + }), + staked: Some(StakedAvailable { + usd_amount: Decimal::from_atomics(Uint128::new(100130012), 3).unwrap(), + amount: Uint128::new(100120000000), + lockups: None, + }), + rewards: Some(vec![CoinValue { + denom: "ueden".to_string(), + amount_token: Decimal::from_str("0.000121").unwrap(), + price: Decimal::from_str("3.5308010067676894").unwrap(), + amount_usd: Decimal::from_str("0.00042722692181889").unwrap(), + }]), + staked_positions: None, + unstaked_positions: None, + }, + }; + + assert_eq!(resp, expected); +} diff --git a/contracts/account-history-contract/src/tests/get_liquid_assets.rs b/contracts/account-history-contract/src/tests/get_liquid_assets.rs new file mode 100644 index 00000000..8ec7fb13 --- /dev/null +++ b/contracts/account-history-contract/src/tests/get_liquid_assets.rs @@ -0,0 +1,541 @@ +use std::str::FromStr; + +use crate::entry_point::instantiate; +use crate::tests::get_liquid_assets::query_resp::{GetLiquidAssetsResp, LiquidAsset}; +use crate::{ + entry_point::{execute, query, sudo}, + msg::*, +}; +use anyhow::{bail, Error, Result as AnyResult}; +use cosmwasm_std::{ + coin, to_json_binary, Addr, Coin, DecCoin, Decimal, Decimal256, Empty, Int128, SignedDecimal, + StdError, Timestamp, Uint128, +}; +use cw_multi_test::{AppResponse, BasicAppBuilder, ContractWrapper, Executor, Module}; +use elys_bindings::query_resp::{ + AmmSwapEstimationByDenomResponse, DelegationDelegatorReward, Entry, EstakingRewardsResponse, + MasterchefUserPendingRewardData, MasterchefUserPendingRewardResponse, OracleAssetInfoResponse, + QueryGetEntryResponse, QueryGetPriceResponse, QueryStableStakeAprResponse, Validator, +}; +use elys_bindings::types::{BalanceAvailable, OracleAssetInfo, Price}; +use elys_bindings::{ElysMsg, ElysQuery}; +use elys_bindings_test::{ + ElysModule, ACCOUNT, ASSET_INFO, LAST_MODULE_USED, PERPETUAL_OPENED_POSITION, PRICES, +}; +use trade_shield_contract::entry_point::{ + execute as trade_shield_execute, instantiate as trade_shield_init, query as trade_shield_query, +}; +use trade_shield_contract::msg::InstantiateMsg as TradeShieldInstantiateMsg; + +struct ElysModuleWrapper(ElysModule); + +impl Module for ElysModuleWrapper { + type QueryT = ElysQuery; + type ExecT = ElysMsg; + type SudoT = Empty; + + fn query( + &self, + api: &dyn cosmwasm_std::Api, + storage: &dyn cosmwasm_std::Storage, + querier: &dyn cosmwasm_std::Querier, + block: &cosmwasm_std::BlockInfo, + request: Self::QueryT, + ) -> AnyResult { + match request { + ElysQuery::AmmBalance { .. } => { + let resp = BalanceAvailable { + amount: Uint128::new(0), + usd_amount: Decimal::zero(), + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::AssetProfileEntry { base_denom } => { + let resp = match base_denom.as_str() { + "uusdc" => QueryGetEntryResponse { + entry: Entry { + address: "".to_string(), + authority: "elys10d07y265gmmuvt4z0w9aw880jnsr700j6z2zm3".to_string(), + base_denom: "uusdc".to_string(), + commit_enabled: true, + decimals: 6, + denom: "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65".to_string(), + display_name: "USDC".to_string(), + display_symbol: "uUSDC".to_string(), + external_symbol: "uUSDC".to_string(), + ibc_channel_id: "channel-12".to_string(), + ibc_counterparty_chain_id: "".to_string(), + ibc_counterparty_channel_id: "channel-19".to_string(), + ibc_counterparty_denom: "".to_string(), + network: "".to_string(), + path: "transfer/channel-12".to_string(), + permissions: vec![], + transfer_limit: "".to_string(), + unit_denom: "uusdc".to_string(), + withdraw_enabled: true, + }, + }, + "ueden" => QueryGetEntryResponse { + entry: Entry { + address: "".to_string(), + authority: "elys10d07y265gmmuvt4z0w9aw880jnsr700j6z2zm3".to_string(), + base_denom: "ueden".to_string(), + commit_enabled: true, + decimals: 6, + denom: "ueden".to_string(), + display_name: "EDEN".to_string(), + display_symbol: "".to_string(), + external_symbol: "".to_string(), + ibc_channel_id: "".to_string(), + ibc_counterparty_chain_id: "".to_string(), + ibc_counterparty_channel_id: "".to_string(), + ibc_counterparty_denom: "".to_string(), + network: "".to_string(), + path: "".to_string(), + permissions: vec![], + transfer_limit: "".to_string(), + unit_denom: "".to_string(), + withdraw_enabled: true, + }, + }, + "uelys" => QueryGetEntryResponse { + entry: Entry { + address: "".to_string(), + authority: "elys10d07y265gmmuvt4z0w9aw880jnsr700j6z2zm3".to_string(), + base_denom: "uelys".to_string(), + commit_enabled: true, + decimals: 6, + denom: "uelys".to_string(), + display_name: "ELYS".to_string(), + display_symbol: "".to_string(), + external_symbol: "".to_string(), + ibc_channel_id: "".to_string(), + ibc_counterparty_chain_id: "".to_string(), + ibc_counterparty_channel_id: "".to_string(), + ibc_counterparty_denom: "".to_string(), + network: "".to_string(), + path: "".to_string(), + permissions: vec![], + transfer_limit: "".to_string(), + unit_denom: "".to_string(), + withdraw_enabled: true, + }, + }, + _ => return Err(Error::new(StdError::not_found(base_denom))), + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::AmmPriceByDenom { token_in, .. } => { + let spot_price = match token_in.denom.as_str() { + "uelys" => Decimal::from_str("3.5308010067676894").unwrap(), + "ueden" => Decimal::from_str("3.5308010067676894").unwrap(), + "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65" => { + Decimal::one() + } + "ibc/E2D2F6ADCC68AA3384B2F5DFACCA437923D137C14E86FB8A10207CF3BED0C8D4" => { + Decimal::from_str("9.02450744362719844").unwrap() + } + _ => return Err(Error::new(StdError::not_found(token_in.denom.as_str()))), + }; + Ok(to_json_binary(&spot_price)?) + } + ElysQuery::OraclePrice { asset, .. } => { + let resp = match asset.as_str() { + "USDC" => QueryGetPriceResponse { + price: Price { + asset: "USDC".to_string(), + price: Decimal::one(), + source: "uelys".to_string(), + provider: "elys1wzm8dvpxpxxf26y4xn85w5adakcenprg4cq2uf".to_string(), + // set timestamp to now + timestamp: block.time.seconds(), + block_height: block.height, + }, + }, + _ => return Err(Error::new(StdError::not_found(asset))), + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::OracleAssetInfo { denom } => { + let resp = match denom.as_str() { + "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65" => { + OracleAssetInfoResponse { + asset_info: OracleAssetInfo { + band_ticker: "USDC".to_string(), + decimal: 6, + denom: "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65".to_string(), + display: "USDC".to_string(), + elys_ticker: "USDC".to_string(), + }, + } + } + "ibc/47BD209179859CDE4A2806763D7189B6E6FE13A17880FE2B42DE1E6C1E329E23" => { + OracleAssetInfoResponse { + asset_info: OracleAssetInfo { + band_ticker: "OSMO".to_string(), + decimal: 6, + denom: "ibc/47BD209179859CDE4A2806763D7189B6E6FE13A17880FE2B42DE1E6C1E329E23".to_string(), + display: "OSMO".to_string(), + elys_ticker: "OSMO".to_string(), + }, + } + } + "ibc/977D5388D2FBE72D9A33FE2423BF8F4DADF3B591207CC98A295B9ACF81E4DE40" => { + OracleAssetInfoResponse { + asset_info: OracleAssetInfo { + band_ticker: "JUNO".to_string(), + decimal: 6, + denom: "ibc/977D5388D2FBE72D9A33FE2423BF8F4DADF3B591207CC98A295B9ACF81E4DE40".to_string(), + display: "JUNO".to_string(), + elys_ticker: "JUNO".to_string(), + }, + } + } + "ibc/E2D2F6ADCC68AA3384B2F5DFACCA437923D137C14E86FB8A10207CF3BED0C8D4" => { + OracleAssetInfoResponse { + asset_info: OracleAssetInfo { + band_ticker: "ATOM".to_string(), + decimal: 6, + denom: "ibc/E2D2F6ADCC68AA3384B2F5DFACCA437923D137C14E86FB8A10207CF3BED0C8D4".to_string(), + display: "ATOM".to_string(), + elys_ticker: "ATOM".to_string(), + }, + } + }, + _ => return Err(Error::new(StdError::not_found(denom))), + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::EstakingRewards { .. } => { + let resp = EstakingRewardsResponse { + rewards: vec![DelegationDelegatorReward { + validator_address: Validator::EdenBoost.to_string(), + reward: vec![Coin { + denom: "ueden".to_string(), + amount: Uint128::from_str("121").unwrap(), + }], + }], + total: vec![Coin { + denom: "uedenb".to_string(), + amount: Uint128::from_str("121").unwrap(), + }], + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::MasterchefStableStakeApr { .. } => { + let resp = QueryStableStakeAprResponse { + apr: Int128::new(12), + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::AmmSwapEstimationByDenom { .. } => { + let resp = AmmSwapEstimationByDenomResponse { + in_route: None, + out_route: None, + spot_price: Decimal::from_str("3.5").unwrap(), + amount: Coin { + denom: + "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65" + .to_string(), + amount: Uint128::new(100), + }, + swap_fee: SignedDecimal::one(), + discount: SignedDecimal::from_str("20").unwrap(), + available_liquidity: Coin { + denom: "uelys".to_string(), + amount: Uint128::new(100000), + }, + weight_balance_ratio: SignedDecimal::one(), + price_impact: SignedDecimal::zero(), + slippage: Decimal::zero(), + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::MasterchefUserPendingReward { .. } => { + let resp = MasterchefUserPendingRewardResponse { + rewards: vec![MasterchefUserPendingRewardData { + pool_id: 32767u64, + reward: vec![Coin { + denom: "ueden".to_string(), + amount: Uint128::new(20), + }], + }], + total_rewards: vec![Coin { + denom: "ueden".to_string(), + amount: Uint128::new(20), + }], + }; + Ok(to_json_binary(&resp)?) + } + _ => self.0.query(api, storage, querier, block, request), + } + } + + fn execute( + &self, + api: &dyn cosmwasm_std::Api, + storage: &mut dyn cosmwasm_std::Storage, + router: &dyn cw_multi_test::CosmosRouter, + block: &cosmwasm_std::BlockInfo, + sender: Addr, + msg: Self::ExecT, + ) -> AnyResult + where + ExecC: std::fmt::Debug + + Clone + + PartialEq + + schemars::JsonSchema + + serde::de::DeserializeOwned + + 'static, + QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, + { + match msg { + _ => self.0.execute(api, storage, router, block, sender, msg), + } + } + + fn sudo( + &self, + _api: &dyn cosmwasm_std::Api, + _storage: &mut dyn cosmwasm_std::Storage, + _router: &dyn cw_multi_test::CosmosRouter, + _block: &cosmwasm_std::BlockInfo, + _msg: Self::SudoT, + ) -> AnyResult + where + ExecC: std::fmt::Debug + + Clone + + PartialEq + + schemars::JsonSchema + + serde::de::DeserializeOwned + + 'static, + QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, + { + bail!("sudo is not implemented for ElysModule") + } +} + +#[test] +fn get_liquid_assets() { + // Create a wallet for the "user" with an initial balance of 100 usdc + let wallet = vec![( + "user", + vec![ + coin( + 21798000, + "ibc/0E1517E2771CA7C03F2ED3F9BAECCAEADF0BFD79B89679E834933BC0F179AD98", + ), + coin( + 5333229342748, + "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", + ), + coin( + 2704998, + "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + ), + coin( + 594000000000200000, + "ibc/2FBCFC209420E6CECED6EE0BC599E74349759352CE953E27A6871BB3D84BC058", + ), + coin( + 1085352, + "ibc/326A89923D85047E6418A671FBACCAFA2686B01A16ED4A0AD92954FCE1485910", + ), + coin( + 168400000000000000, + "ibc/43881AB3B3D05FD9D3606D7F57CBE6EEEA89D18AC66AF9E2915ED43940E71CFD", + ), + coin( + 49765000, + "ibc/4DAE26570FD24ABA40E2BE4137E39D946C78B00B248D3F78B0919567C4371156", + ), + coin( + 9100000, + "ibc/977D5388D2FBE72D9A33FE2423BF8F4DADF3B591207CC98A295B9ACF81E4DE40", + ), + coin( + 141000000000000000, + "ibc/E059CD828E5009D4CF03C4494BEA73749250287FC98DD46E19F9016B918BF49D", + ), + coin( + 37403942, + "ibc/E2D2F6ADCC68AA3384B2F5DFACCA437923D137C14E86FB8A10207CF3BED0C8D4", + ), + coin( + 79979999999749000, + "ibc/FB22E35236996F6B0B1C9D407E8A379A7B1F4083F1960907A1622F022AE450E1", + ), + coin(45666543, "uelys"), + coin(45666543, "ueden"), + ], + )]; + + let mut addresses: Vec = vec![]; + let mut app = BasicAppBuilder::::new_custom() + .with_custom(ElysModuleWrapper(ElysModule {})) + .build(|roouter, _, storage| { + for (wallet_owner, wallet_contenent) in wallet { + roouter + .bank + .init_balance(storage, &Addr::unchecked(wallet_owner), wallet_contenent) + .unwrap(); + addresses.push(wallet_owner.to_owned()) + } + ACCOUNT.save(storage, &addresses).unwrap(); + PERPETUAL_OPENED_POSITION.save(storage, &vec![]).unwrap(); + ASSET_INFO.save(storage, &vec![]).unwrap(); + PRICES.save(storage, &vec![]).unwrap(); + LAST_MODULE_USED.save(storage, &None).unwrap(); + }); + + // trade shield deployment + let trade_shield_code = + ContractWrapper::new(trade_shield_execute, trade_shield_init, trade_shield_query); + let trade_shield_code_id = app.store_code(Box::new(trade_shield_code)); + let trade_shield_init = TradeShieldInstantiateMsg { + account_history_address: None, + }; + let trade_shield_address = app + .instantiate_contract( + trade_shield_code_id, + Addr::unchecked("owner"), + &trade_shield_init, + &[], + "Contract", + None, + ) + .unwrap() + .to_string(); + + // Create a contract wrapper and store its code. + let code = ContractWrapper::new(execute, instantiate, query).with_sudo(sudo); + let code_id = app.store_code(Box::new(code)); + + // Create a mock message to instantiate the contract with no initial orders. + let instantiate_msg = InstantiateMsg { + limit: Some(3), + expiration: Some(cw_utils::Expiration::AtTime(Timestamp::from_seconds( + 604800, + ))), + trade_shield_address: Some(trade_shield_address), + }; + + // Instantiate the contract with "owner" as the deployer. + let addr = app + .instantiate_contract( + code_id, + Addr::unchecked("owner"), + &instantiate_msg, + &[], + "Contract", + None, + ) + .unwrap(); + + app.wasm_sudo(addr.clone(), &SudoMsg::ClockEndBlock {}) + .unwrap(); + + // Query the contract for the existing order. + let resp: GetLiquidAssetsResp = app + .wrap() + .query_wasm_smart( + &addr, + &QueryMsg::GetLiquidAssets { + user_address: "user".to_string(), + }, + ) + .unwrap(); + + let mut expected: GetLiquidAssetsResp = GetLiquidAssetsResp { + liquid_assets: vec![ + LiquidAsset { + denom: "uelys".to_string(), + price: Decimal::from_str("3.5308010067676894").unwrap(), + available_amount: Decimal::from_str("45.666543").unwrap(), + available_value: Decimal::from_str("161.239475999999978995").unwrap(), + in_order_amount: Decimal::zero(), + in_order_value: Decimal::zero(), + total_amount: Decimal::from_str("45.666543").unwrap(), + total_value: Decimal::from_str("161.239475999999978995").unwrap(), + }, + LiquidAsset { + denom: "ueden".to_string(), + price: Decimal::from_str("3.5308010067676894").unwrap(), + available_amount: Decimal::from_str("45.666543").unwrap(), + available_value: Decimal::from_str("161.239475999999978995").unwrap(), + in_order_amount: Decimal::zero(), + in_order_value: Decimal::zero(), + total_amount: Decimal::from_str("45.666543").unwrap(), + total_value: Decimal::from_str("161.239475999999978995").unwrap(), + }, + LiquidAsset { + denom: "ibc/E2D2F6ADCC68AA3384B2F5DFACCA437923D137C14E86FB8A10207CF3BED0C8D4" + .to_string(), + price: Decimal::from_str("9.02450744362719844").unwrap(), + available_amount: Decimal::from_str("37.403942").unwrap(), + available_value: Decimal::from_str("337.552153000000000072").unwrap(), + in_order_amount: Decimal::zero(), + in_order_value: Decimal::zero(), + total_amount: Decimal::from_str("37.403942").unwrap(), + total_value: Decimal::from_str("337.552153000000000072").unwrap(), + }, + LiquidAsset { + denom: "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65" + .to_string(), + price: Decimal::one(), + available_amount: Decimal::from_str("5333229.342748").unwrap(), + available_value: Decimal::from_str("5333229.342748").unwrap(), + in_order_amount: Decimal::zero(), + in_order_value: Decimal::zero(), + total_amount: Decimal::from_str("5333229.342748").unwrap(), + total_value: Decimal::from_str("5333229.342748").unwrap(), + }, + ], + total_liquid_asset_balance: DecCoin::new( + Decimal256::zero(), + "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", + ), + }; + + for i in expected.liquid_assets.iter() { + expected.total_liquid_asset_balance.amount += Decimal256::from(i.total_value.clone()); + } + + // test if the response is the same as the expected + assert_eq!(resp.liquid_assets.len(), expected.liquid_assets.len()); + + assert_eq!( + resp.liquid_assets + .iter() + .find(|l| l.denom.as_str() == "uelys") + .cloned(), + Some(expected.liquid_assets[0].clone()) + ); + assert_eq!( + resp.liquid_assets + .iter() + .find(|l| l.denom.as_str() == "ueden") + .cloned(), + Some(expected.liquid_assets[1].clone()) + ); + assert_eq!( + resp.liquid_assets + .iter() + .find(|l| l.denom.as_str() + == "ibc/E2D2F6ADCC68AA3384B2F5DFACCA437923D137C14E86FB8A10207CF3BED0C8D4") + .cloned(), + Some(expected.liquid_assets[2].clone()) + ); + assert_eq!( + resp.liquid_assets + .iter() + .find(|l| l.denom.as_str() + == "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65") + .cloned(), + Some(expected.liquid_assets[3].clone()) + ); + assert_eq!( + resp.total_liquid_asset_balance, + expected.total_liquid_asset_balance + ); +} diff --git a/contracts/account-history-contract/src/tests/get_rewards.rs b/contracts/account-history-contract/src/tests/get_rewards.rs new file mode 100644 index 00000000..b5cba5f1 --- /dev/null +++ b/contracts/account-history-contract/src/tests/get_rewards.rs @@ -0,0 +1,287 @@ +use std::str::FromStr; + +use crate::entry_point::instantiate; +use crate::{ + entry_point::{execute, query, sudo}, + msg::*, +}; +use anyhow::{bail, Error, Result as AnyResult}; +use cosmwasm_std::{ + coins, to_json_binary, Addr, Coin, Decimal, Empty, Int128, StdError, Timestamp, Uint128, +}; +use cw_multi_test::{AppResponse, BasicAppBuilder, ContractWrapper, Executor, Module}; +use elys_bindings::account_history::msg::query_resp::GetRewardsResp; +use elys_bindings::account_history::types::{CoinValue, Reward}; +use elys_bindings::query_resp::{ + DelegationDelegatorReward, EstakingRewardsResponse, MasterchefUserPendingRewardData, + MasterchefUserPendingRewardResponse, QueryStableStakeAprResponse, Validator, +}; +use elys_bindings::types::BalanceAvailable; +use elys_bindings::{ElysMsg, ElysQuery}; +use elys_bindings_test::{ + ElysModule, ACCOUNT, ASSET_INFO, LAST_MODULE_USED, PERPETUAL_OPENED_POSITION, PRICES, +}; +use trade_shield_contract::entry_point::{ + execute as trade_shield_execute, instantiate as trade_shield_init, query as trade_shield_query, +}; +use trade_shield_contract::msg::InstantiateMsg as TradeShieldInstantiateMsg; + +struct ElysModuleWrapper(ElysModule); + +impl Module for ElysModuleWrapper { + type QueryT = ElysQuery; + type ExecT = ElysMsg; + type SudoT = Empty; + + fn query( + &self, + api: &dyn cosmwasm_std::Api, + storage: &dyn cosmwasm_std::Storage, + querier: &dyn cosmwasm_std::Querier, + block: &cosmwasm_std::BlockInfo, + request: Self::QueryT, + ) -> AnyResult { + match request { + ElysQuery::AmmBalance { address, denom } => { + let resp = match (address.as_str(), denom.as_str()) { + ("user", "uedenb") => BalanceAvailable { + amount: Uint128::new(21798000), + usd_amount: Decimal::from_str("21798000").unwrap(), + }, + ( + "user", + "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", + ) => BalanceAvailable { + amount: Uint128::new(5333229342748), + usd_amount: Decimal::from_str("5333229342748").unwrap(), + }, + _ => BalanceAvailable { + amount: Uint128::zero(), + usd_amount: Decimal::zero(), + }, + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::MasterchefStableStakeApr { denom } => { + let resp = match denom.as_str() { + "uusdc" => QueryStableStakeAprResponse { + apr: Int128::zero(), + }, + "ueden" => QueryStableStakeAprResponse { + apr: Int128::zero(), + }, + _ => return Err(Error::new(StdError::not_found(denom))), + }; + Ok(to_json_binary(&resp)?) + } + + ElysQuery::MasterchefUserPendingReward { .. } => { + let resp = MasterchefUserPendingRewardResponse { + rewards: vec![MasterchefUserPendingRewardData { + pool_id: 32767u64, + reward: vec![Coin { + denom: "ueden".to_string(), + amount: Uint128::new(20), + }], + }], + total_rewards: vec![Coin { + denom: "ueden".to_string(), + amount: Uint128::new(20), + }], + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::AmmPriceByDenom { token_in, .. } => { + let spot_price = match token_in.denom.as_str() { + "uelys" => Decimal::from_str("3.5308010067676894").unwrap(), + "ueden" => Decimal::from_str("3.5308010067676894").unwrap(), + "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65" => { + Decimal::one() + } + "ibc/E2D2F6ADCC68AA3384B2F5DFACCA437923D137C14E86FB8A10207CF3BED0C8D4" => { + Decimal::from_str("9.02450744362719844").unwrap() + } + _ => return Err(Error::new(StdError::not_found(token_in.denom.as_str()))), + }; + Ok(to_json_binary(&spot_price)?) + } + ElysQuery::EstakingRewards { .. } => { + let resp = EstakingRewardsResponse { + rewards: vec![DelegationDelegatorReward { + validator_address: Validator::EdenBoost.to_string(), + reward: vec![Coin { + denom: "ueden".to_string(), + amount: Uint128::from_str("121").unwrap(), + }], + }], + total: vec![Coin { + denom: "uedenb".to_string(), + amount: Uint128::from_str("121").unwrap(), + }], + }; + Ok(to_json_binary(&resp)?) + } + _ => self.0.query(api, storage, querier, block, request), + } + } + + fn execute( + &self, + api: &dyn cosmwasm_std::Api, + storage: &mut dyn cosmwasm_std::Storage, + router: &dyn cw_multi_test::CosmosRouter, + block: &cosmwasm_std::BlockInfo, + sender: Addr, + msg: Self::ExecT, + ) -> AnyResult + where + ExecC: std::fmt::Debug + + Clone + + PartialEq + + schemars::JsonSchema + + serde::de::DeserializeOwned + + 'static, + QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, + { + match msg { + _ => self.0.execute(api, storage, router, block, sender, msg), + } + } + + fn sudo( + &self, + _api: &dyn cosmwasm_std::Api, + _storage: &mut dyn cosmwasm_std::Storage, + _router: &dyn cw_multi_test::CosmosRouter, + _block: &cosmwasm_std::BlockInfo, + _msg: Self::SudoT, + ) -> AnyResult + where + ExecC: std::fmt::Debug + + Clone + + PartialEq + + schemars::JsonSchema + + serde::de::DeserializeOwned + + 'static, + QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, + { + bail!("sudo is not implemented for ElysModule") + } +} + +#[test] +fn get_rewards() { + // Create a wallet for the "user" with an initial balance of 100 usdc + let wallet = vec![( + "user", + coins( + 100, + "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", + ), + )]; + + let mut addresses: Vec = vec![]; + let mut app = BasicAppBuilder::::new_custom() + .with_custom(ElysModuleWrapper(ElysModule {})) + .build(|roouter, _, storage| { + for (wallet_owner, wallet_contenent) in wallet { + roouter + .bank + .init_balance(storage, &Addr::unchecked(wallet_owner), wallet_contenent) + .unwrap(); + addresses.push(wallet_owner.to_owned()) + } + ACCOUNT.save(storage, &addresses).unwrap(); + PERPETUAL_OPENED_POSITION.save(storage, &vec![]).unwrap(); + ASSET_INFO.save(storage, &vec![]).unwrap(); + PRICES.save(storage, &vec![]).unwrap(); + LAST_MODULE_USED.save(storage, &None).unwrap(); + }); + + // trade shield deployment + let trade_shield_code = + ContractWrapper::new(trade_shield_execute, trade_shield_init, trade_shield_query); + let trade_shield_code_id = app.store_code(Box::new(trade_shield_code)); + let trade_shield_init = TradeShieldInstantiateMsg { + account_history_address: None, + }; + let trade_shield_address = app + .instantiate_contract( + trade_shield_code_id, + Addr::unchecked("owner"), + &trade_shield_init, + &[], + "Contract", + None, + ) + .unwrap() + .to_string(); + + // Create a contract wrapper and store its code. + let code = ContractWrapper::new(execute, instantiate, query).with_sudo(sudo); + let code_id = app.store_code(Box::new(code)); + + // Create a mock message to instantiate the contract with no initial orders. + let instantiate_msg = InstantiateMsg { + limit: Some(3), + expiration: Some(cw_utils::Expiration::AtTime(Timestamp::from_seconds( + 604800, + ))), + trade_shield_address: Some(trade_shield_address), + }; + + // Instantiate the contract with "owner" as the deployer. + let addr = app + .instantiate_contract( + code_id, + Addr::unchecked("owner"), + &instantiate_msg, + &[], + "Contract", + None, + ) + .unwrap(); + + app.wasm_sudo(addr.clone(), &SudoMsg::ClockEndBlock {}) + .unwrap(); + + let resp: GetRewardsResp = app + .wrap() + .query_wasm_smart( + &addr, + &QueryMsg::GetRewards { + user_address: "user".to_string(), + }, + ) + .unwrap(); + + assert_eq!( + resp.rewards_map, + Reward { + usdc_usd: Decimal::zero(), + eden_usd: Decimal::from_str("0.000070616020135353").unwrap(), + eden_boost: Decimal::from_str("0.000121").unwrap(), + other_usd: Decimal::zero(), + total_usd: Decimal::from_str("0.000070616020135353").unwrap() + } + ); + + assert_eq!( + resp.rewards.contains(&CoinValue { + denom: "ueden".to_string(), + amount_token: Decimal::from_str("0.00002").unwrap(), + price: Decimal::from_str("3.5308010067676894").unwrap(), + amount_usd: Decimal::from_str("0.000070616020135353").unwrap(), + }), + true + ); + assert_eq!( + resp.rewards.contains(&CoinValue { + denom: "uedenb".to_string(), + amount_token: Decimal::from_str("0.000121").unwrap(), + price: Decimal::zero(), + amount_usd: Decimal::zero(), + }), + true + ); +} diff --git a/contracts/account-history-contract/src/tests/get_staked_assets.rs b/contracts/account-history-contract/src/tests/get_staked_assets.rs new file mode 100644 index 00000000..7dad9124 --- /dev/null +++ b/contracts/account-history-contract/src/tests/get_staked_assets.rs @@ -0,0 +1,850 @@ +use std::str::FromStr; + +use crate::entry_point::instantiate; +use crate::tests::get_staked_assets::query_resp::StakedAssetsResponse; +use crate::{ + entry_point::{execute, query, sudo}, + msg::*, +}; +use anyhow::{bail, Error, Result as AnyResult}; +use cosmwasm_std::{ + coins, to_json_binary, Addr, Coin, DecCoin, Decimal, Decimal256, Empty, Int128, StdError, + Timestamp, Uint128, +}; +use cw_multi_test::{AppResponse, BasicAppBuilder, ContractWrapper, Executor, Module}; +use elys_bindings::account_history::msg::query_resp::StakeAssetBalanceBreakdown; +use elys_bindings::account_history::types::earn_detail::earn_detail::AprEdenBoost; +use elys_bindings::account_history::types::earn_program::{ + EdenBoostEarnProgram, EdenEarnProgram, ElysEarnProgram, UsdcEarnProgram, +}; +use elys_bindings::account_history::types::{ + AprElys, AprUsdc, CoinValue, QueryAprResponse, StakedAssets, +}; +use elys_bindings::query_resp::{ + BalanceBorrowed, DelegationDelegatorReward, Entry, EstakingRewardsResponse, Lockup, + MasterchefUserPendingRewardData, MasterchefUserPendingRewardResponse, QueryAprsResponse, + QueryGetEntryResponse, QueryGetPriceResponse, QueryStableStakeAprResponse, + QueryStakedPositionResponse, QueryUnstakedPositionResponse, QueryVestingInfoResponse, + StakedAvailable, Validator, +}; +use elys_bindings::types::{ + BalanceAvailable, Price, StakedPosition, StakingValidator, UnstakedPosition, +}; +use elys_bindings::{ElysMsg, ElysQuery}; +use elys_bindings_test::{ + ElysModule, ACCOUNT, ASSET_INFO, LAST_MODULE_USED, PERPETUAL_OPENED_POSITION, PRICES, +}; +use trade_shield_contract::entry_point::{ + execute as trade_shield_execute, instantiate as trade_shield_init, query as trade_shield_query, +}; +use trade_shield_contract::msg::InstantiateMsg as TradeShieldInstantiateMsg; + +struct ElysModuleWrapper(ElysModule); + +impl Module for ElysModuleWrapper { + type QueryT = ElysQuery; + type ExecT = ElysMsg; + type SudoT = Empty; + + fn query( + &self, + api: &dyn cosmwasm_std::Api, + storage: &dyn cosmwasm_std::Storage, + querier: &dyn cosmwasm_std::Querier, + block: &cosmwasm_std::BlockInfo, + request: Self::QueryT, + ) -> AnyResult { + match request { + ElysQuery::AssetProfileEntry { base_denom } => { + let resp = match base_denom.as_str() { + "uusdc" => QueryGetEntryResponse { + entry: Entry { + address: "".to_string(), + authority: "elys10d07y265gmmuvt4z0w9aw880jnsr700j6z2zm3".to_string(), + base_denom: "uusdc".to_string(), + commit_enabled: true, + decimals: 6, + denom: "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65".to_string(), + display_name: "USDC".to_string(), + display_symbol: "uUSDC".to_string(), + external_symbol: "uUSDC".to_string(), + ibc_channel_id: "channel-12".to_string(), + ibc_counterparty_chain_id: "".to_string(), + ibc_counterparty_channel_id: "channel-19".to_string(), + ibc_counterparty_denom: "".to_string(), + network: "".to_string(), + path: "transfer/channel-12".to_string(), + permissions: vec![], + transfer_limit: "".to_string(), + unit_denom: "uusdc".to_string(), + withdraw_enabled: true, + }, + }, + "ueden" => QueryGetEntryResponse { + entry: Entry { + address: "".to_string(), + authority: "elys10d07y265gmmuvt4z0w9aw880jnsr700j6z2zm3".to_string(), + base_denom: "ueden".to_string(), + commit_enabled: true, + decimals: 6, + denom: "ueden".to_string(), + display_name: "EDEN".to_string(), + display_symbol: "".to_string(), + external_symbol: "".to_string(), + ibc_channel_id: "".to_string(), + ibc_counterparty_chain_id: "".to_string(), + ibc_counterparty_channel_id: "".to_string(), + ibc_counterparty_denom: "".to_string(), + network: "".to_string(), + path: "".to_string(), + permissions: vec![], + transfer_limit: "".to_string(), + unit_denom: "".to_string(), + withdraw_enabled: true, + }, + }, + "uelys" => QueryGetEntryResponse { + entry: Entry { + address: "".to_string(), + authority: "elys10d07y265gmmuvt4z0w9aw880jnsr700j6z2zm3".to_string(), + base_denom: "uelys".to_string(), + commit_enabled: true, + decimals: 6, + denom: "uelys".to_string(), + display_name: "ELYS".to_string(), + display_symbol: "".to_string(), + external_symbol: "".to_string(), + ibc_channel_id: "".to_string(), + ibc_counterparty_chain_id: "".to_string(), + ibc_counterparty_channel_id: "".to_string(), + ibc_counterparty_denom: "".to_string(), + network: "".to_string(), + path: "".to_string(), + permissions: vec![], + transfer_limit: "".to_string(), + unit_denom: "".to_string(), + withdraw_enabled: true, + }, + }, + _ => return Err(Error::new(StdError::not_found(base_denom))), + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::AmmPriceByDenom { token_in, .. } => { + let spot_price = match token_in.denom.as_str() { + "uelys" => Decimal::from_str("3.5308010067676894").unwrap(), + _ => return Err(Error::new(StdError::not_found(token_in.denom.as_str()))), + }; + Ok(to_json_binary(&spot_price)?) + } + ElysQuery::EstakingRewards { .. } => { + let resp = EstakingRewardsResponse { + rewards: vec![DelegationDelegatorReward { + validator_address: Validator::EdenBoost.to_string(), + reward: vec![Coin { + denom: "ueden".to_string(), + amount: Uint128::from_str("121").unwrap(), + }], + }], + total: vec![Coin { + denom: "uedenb".to_string(), + amount: Uint128::from_str("121").unwrap(), + }], + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::MasterchefStableStakeApr { .. } => { + let resp = QueryStableStakeAprResponse { + apr: Int128::new(12), + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::OraclePrice { asset, .. } => { + let resp = match asset.as_str() { + "USDC" => QueryGetPriceResponse { + price: Price { + asset: "USDC".to_string(), + price: Decimal::one(), + source: "uelys".to_string(), + provider: "elys1wzm8dvpxpxxf26y4xn85w5adakcenprg4cq2uf".to_string(), + // set timestamp to now + timestamp: block.time.seconds(), + block_height: block.height, + }, + }, + _ => return Err(Error::new(StdError::not_found(asset))), + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::MasterchefUserPendingReward { .. } => { + let resp = MasterchefUserPendingRewardResponse { + rewards: vec![MasterchefUserPendingRewardData { + pool_id: 32767u64, + reward: vec![Coin { + denom: "ueden".to_string(), + amount: Uint128::new(20), + }], + }], + total_rewards: vec![Coin { + denom: "ueden".to_string(), + amount: Uint128::new(20), + }], + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::CommitmentStakedPositions { delegator_address } => { + let resp = match delegator_address.as_str() { + "user" => QueryStakedPositionResponse { + staked_position: Some(vec![StakedPosition { + id: "2".to_string(), + validator: StakingValidator { + id: Some(String::from("1")), + address: "elysvaloper1ng8sen6z5xzcfjtyrsedpe43hglymq040x3cpw" + .to_string(), + name: "nirvana".to_string(), + voting_power: Decimal::from_str("25.6521469796402094").unwrap(), + commission: Decimal::from_str("0.1").unwrap(), + }, + staked: BalanceAvailable { + amount: Uint128::new(10000000), + usd_amount: Decimal::from_str("35.308010067676894").unwrap(), + }, + }]), + }, + _ => return Err(Error::new(StdError::not_found(delegator_address))), + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::CommitmentUnStakedPositions { delegator_address } => { + let resp = match delegator_address.as_str() { + "user" => QueryUnstakedPositionResponse { + unstaked_position: Some(vec![UnstakedPosition { + id: "1".to_string(), + validator: StakingValidator { + id: Some(String::from("1")), + address: "elysvaloper1ng8sen6z5xzcfjtyrsedpe43hglymq040x3cpw" + .to_string(), + name: "nirvana".to_string(), + voting_power: Decimal::from_str("25.6521469796402094").unwrap(), + commission: Decimal::from_str("0.1").unwrap(), + }, + remaining_time: 1707328694, + unstaked: BalanceAvailable { + amount: Uint128::new(100038144098), + usd_amount: Decimal::from_str("353214.779896389585407707").unwrap(), + }, + }]), + }, + _ => return Err(Error::new(StdError::not_found(delegator_address))), + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::AmmBalance { address, denom } => { + let resp = match (address.as_str(), denom.as_str()) { + ( + "user", + "ibc/0E1517E2771CA7C03F2ED3F9BAECCAEADF0BFD79B89679E834933BC0F179AD98", + ) => BalanceAvailable { + amount: Uint128::new(21798000), + usd_amount: Decimal::from_str("21798000").unwrap(), + }, + ( + "user", + "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", + ) => BalanceAvailable { + amount: Uint128::new(5333229342748), + usd_amount: Decimal::from_str("5333229342748").unwrap(), + }, + ( + "user", + "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", + ) => BalanceAvailable { + amount: Uint128::new(2704998), + usd_amount: Decimal::from_str("2704998").unwrap(), + }, + ( + "user", + "ibc/2FBCFC209420E6CECED6EE0BC599E74349759352CE953E27A6871BB3D84BC058", + ) => BalanceAvailable { + amount: Uint128::new(594000000000200000), + usd_amount: Decimal::from_str("594000000000200000").unwrap(), + }, + ( + "user", + "ibc/326A89923D85047E6418A671FBACCAFA2686B01A16ED4A0AD92954FCE1485910", + ) => BalanceAvailable { + amount: Uint128::new(1085352), + usd_amount: Decimal::from_str("1085352").unwrap(), + }, + ( + "user", + "ibc/43881AB3B3D05FD9D3606D7F57CBE6EEEA89D18AC66AF9E2915ED43940E71CFD", + ) => BalanceAvailable { + amount: Uint128::new(168400000000000000), + usd_amount: Decimal::from_str("168400000000000000").unwrap(), + }, + ( + "user", + "ibc/4DAE26570FD24ABA40E2BE4137E39D946C78B00B248D3F78B0919567C4371156", + ) => BalanceAvailable { + amount: Uint128::new(49765000), + usd_amount: Decimal::from_str("49765000").unwrap(), + }, + ( + "user", + "ibc/977D5388D2FBE72D9A33FE2423BF8F4DADF3B591207CC98A295B9ACF81E4DE40", + ) => BalanceAvailable { + amount: Uint128::new(9100000), + usd_amount: Decimal::from_str("9100000").unwrap(), + }, + ( + "user", + "ibc/E059CD828E5009D4CF03C4494BEA73749250287FC98DD46E19F9016B918BF49D", + ) => BalanceAvailable { + amount: Uint128::new(141000000000000000), + usd_amount: Decimal::from_str("141000000000000000").unwrap(), + }, + ( + "user", + "ibc/E2D2F6ADCC68AA3384B2F5DFACCA437923D137C14E86FB8A10207CF3BED0C8D4", + ) => BalanceAvailable { + amount: Uint128::new(37403942), + usd_amount: Decimal::from_str("37403942").unwrap(), + }, + ( + "user", + "ibc/FB22E35236996F6B0B1C9D407E8A379A7B1F4083F1960907A1622F022AE450E1", + ) => BalanceAvailable { + amount: Uint128::new(79979999999749000), + usd_amount: Decimal::from_str("79979999999749000").unwrap(), + }, + ("user", "uelys") => BalanceAvailable { + amount: Uint128::new(45666543), + usd_amount: Decimal::from_str("45666543").unwrap(), + }, + _ => BalanceAvailable { + amount: Uint128::zero(), + usd_amount: Decimal::zero(), + }, + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::CommitmentStakedBalanceOfDenom { denom, .. } => { + let resp: StakedAvailable = match denom.as_str() { + "uusdc" => StakedAvailable { + usd_amount: Decimal::zero(), + amount: Uint128::zero(), + lockups: None, + }, + "uelys" => StakedAvailable { + usd_amount: Decimal::from_str("35.308010067676894").unwrap(), + amount: Uint128::new(10000000), + lockups: Some(vec![]), + }, + "ueden" => StakedAvailable { + usd_amount: Decimal::from_str("9136.339725178804921781").unwrap(), + amount: Uint128::new(2587611057), + lockups: Some(vec![Lockup { + amount: Int128::new(5200770174), + // use now time + unlock_timestamp: block.time.seconds(), + }]), + }, + "uedenb" => StakedAvailable { + usd_amount: Decimal::zero(), + amount: Uint128::zero(), + lockups: None, + }, + _ => return Err(Error::new(StdError::not_found(denom))), + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::StableStakeBalanceOfBorrow {} => { + let resp = BalanceBorrowed { + usd_amount: Decimal::from_str("204000000001").unwrap(), + percentage: Decimal::one(), + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::IncentiveApr { + withdraw_type, + denom, + } => { + let resp: QueryAprResponse = match (withdraw_type, denom.as_str()) { + (1, "uusdc") => QueryAprResponse { + apr: Uint128::new(100), + }, + (1, "ueden") => QueryAprResponse { + apr: Uint128::new(168), + }, + (4, "uusdc") => QueryAprResponse { + apr: Uint128::zero(), + }, + (4, "ueden") => QueryAprResponse { + apr: Uint128::new(29), + }, + (3, "uusdc") => QueryAprResponse { + apr: Uint128::zero(), + }, + (3, "ueden") => QueryAprResponse { + apr: Uint128::new(29), + }, + (3, "uedenb") => QueryAprResponse { + apr: Uint128::new(100), + }, + (2, "uusdc") => QueryAprResponse { + apr: Uint128::zero(), + }, + (2, "ueden") => QueryAprResponse { + apr: Uint128::new(29), + }, + (2, "uedenb") => QueryAprResponse { + apr: Uint128::new(100), + }, + _ => return Err(Error::new(StdError::not_found(denom))), + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::IncentiveAprs {} => Ok(to_json_binary(&QueryAprsResponse { + usdc_apr_usdc: Uint128::new(100), + usdc_apr_edenb: Uint128::zero(), + usdc_apr_eden: Uint128::zero(), + usdc_apr_elys: Uint128::zero(), + eden_apr_usdc: Uint128::new(168), + eden_apr_edenb: Uint128::new(29), + eden_apr_elys: Uint128::new(29), + eden_apr_eden: Uint128::new(29), + edenb_apr_eden: Uint128::new(100), + edenb_apr_elys: Uint128::new(100), + })?), + ElysQuery::CommitmentVestingInfo { .. } => { + let resp = QueryVestingInfoResponse { + vesting: BalanceAvailable { + amount: Uint128::zero(), + usd_amount: Decimal::zero(), + }, + vesting_details: Some(vec![]), + }; + Ok(to_json_binary(&resp)?) + } + _ => self.0.query(api, storage, querier, block, request), + } + } + + fn execute( + &self, + api: &dyn cosmwasm_std::Api, + storage: &mut dyn cosmwasm_std::Storage, + router: &dyn cw_multi_test::CosmosRouter, + block: &cosmwasm_std::BlockInfo, + sender: Addr, + msg: Self::ExecT, + ) -> AnyResult + where + ExecC: std::fmt::Debug + + Clone + + PartialEq + + schemars::JsonSchema + + serde::de::DeserializeOwned + + 'static, + QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, + { + match msg { + _ => self.0.execute(api, storage, router, block, sender, msg), + } + } + + fn sudo( + &self, + _api: &dyn cosmwasm_std::Api, + _storage: &mut dyn cosmwasm_std::Storage, + _router: &dyn cw_multi_test::CosmosRouter, + _block: &cosmwasm_std::BlockInfo, + _msg: Self::SudoT, + ) -> AnyResult + where + ExecC: std::fmt::Debug + + Clone + + PartialEq + + schemars::JsonSchema + + serde::de::DeserializeOwned + + 'static, + QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, + { + bail!("sudo is not implemented for ElysModule") + } +} + +#[test] +fn get_staked_assets() { + // Create a wallet for the "user" with an initial balance of 100 usdc + let wallet = vec![( + "user", + coins( + 200__000_000, + "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", + ), + )]; + + let mut addresses: Vec = vec![]; + let mut app = BasicAppBuilder::::new_custom() + .with_custom(ElysModuleWrapper(ElysModule {})) + .build(|roouter, _, storage| { + for (wallet_owner, wallet_contenent) in wallet { + roouter + .bank + .init_balance(storage, &Addr::unchecked(wallet_owner), wallet_contenent) + .unwrap(); + addresses.push(wallet_owner.to_owned()) + } + ACCOUNT.save(storage, &addresses).unwrap(); + PERPETUAL_OPENED_POSITION.save(storage, &vec![]).unwrap(); + ASSET_INFO.save(storage, &vec![]).unwrap(); + PRICES.save(storage, &vec![]).unwrap(); + LAST_MODULE_USED.save(storage, &None).unwrap(); + }); + + // trade shield deployment + let trade_shield_code = + ContractWrapper::new(trade_shield_execute, trade_shield_init, trade_shield_query); + let trade_shield_code_id = app.store_code(Box::new(trade_shield_code)); + let trade_shield_init = TradeShieldInstantiateMsg { + account_history_address: None, + }; + let trade_shield_address = app + .instantiate_contract( + trade_shield_code_id, + Addr::unchecked("owner"), + &trade_shield_init, + &[], + "Contract", + None, + ) + .unwrap() + .to_string(); + + // Create a contract wrapper and store its code. + let code = ContractWrapper::new(execute, instantiate, query).with_sudo(sudo); + let code_id = app.store_code(Box::new(code)); + + // Create a mock message to instantiate the contract with no initial orders. + let instantiate_msg = InstantiateMsg { + limit: Some(3), + expiration: Some(cw_utils::Expiration::AtTime(Timestamp::from_seconds( + 604800, + ))), + trade_shield_address: Some(trade_shield_address), + }; + + // Instantiate the contract with "owner" as the deployer. + let addr = app + .instantiate_contract( + code_id, + Addr::unchecked("owner"), + &instantiate_msg, + &[], + "Contract", + None, + ) + .unwrap(); + + app.wasm_sudo(addr.clone(), &SudoMsg::ClockEndBlock {}) + .unwrap(); + + // Query the contract for the existing order. + let resp: StakedAssetsResponse = app + .wrap() + .query_wasm_smart( + &addr, + &QueryMsg::GetStakedAssets { + user_address: Some("user".to_string()), + }, + ) + .unwrap(); + + let balance_break_down = StakeAssetBalanceBreakdown { + vesting: Decimal::zero(), + unstaking: vec![UnstakedPosition { + id: "1".to_string(), + validator: StakingValidator { + id: Some(String::from("1")), + address: "elysvaloper1ng8sen6z5xzcfjtyrsedpe43hglymq040x3cpw".to_string(), + name: "nirvana".to_string(), + voting_power: Decimal::from_str("25.6521469796402094").unwrap(), + commission: Decimal::from_str("0.1").unwrap(), + }, + remaining_time: 1707328694, + unstaked: BalanceAvailable { + amount: Uint128::new(100038144098), + usd_amount: Decimal::from_str("353214.779896389585407707").unwrap(), + }, + }] + .iter() + .fold(Decimal::zero(), |acc, item| { + acc.checked_add(item.unstaked.usd_amount) + .unwrap_or_default() + }), + staked: Decimal::from_str("9171.647735246481815781").unwrap(), + }; + + let expected: StakedAssetsResponse = StakedAssetsResponse { + total_staked_balance: DecCoin::new( + Decimal256::from_str("9171.647735246481815781").unwrap(), + "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65".to_string(), + ), + total_balance: balance_break_down.total(), + balance_break_down, + staked_assets: StakedAssets { + eden_boost_earn_program: EdenBoostEarnProgram { + bonding_period: 0, + apr: AprEdenBoost { + uusdc: Uint128::zero(), + ueden: Uint128::new(29), + }, + available: Some(Uint128::zero()), + staked: Some(Uint128::zero()), + rewards: Some(vec![CoinValue { + denom: "ueden".to_string(), + amount_token: Decimal::from_str("0.000121").unwrap(), + price: Decimal::from_atomics(Uint128::new(35308010067676894), 16).unwrap(), + amount_usd: Decimal::from_str("0.00042722692181889").unwrap(), + }]), + }, + eden_earn_program: EdenEarnProgram { + bonding_period: 0, + apr: AprElys { + uusdc: Uint128::zero(), + ueden: Uint128::new(29), + uedenb: Uint128::new(100), + }, + available: Some(BalanceAvailable { + amount: Uint128::new(0), + usd_amount: Decimal::zero(), + }), + staked: Some(StakedAvailable { + usd_amount: Decimal::from_str("9136.339725178804921781").unwrap(), + amount: Uint128::new(2587611057), + lockups: Some(vec![Lockup { + amount: Int128::new(5200770174), + unlock_timestamp: 1571797419, + }]), + }), + rewards: Some(vec![]), + vesting: BalanceAvailable { + amount: Uint128::from_str("0").unwrap(), + usd_amount: Decimal::from_str("0").unwrap(), + }, + vesting_details: Some(vec![]), + }, + elys_earn_program: ElysEarnProgram { + bonding_period: 14, + apr: AprElys { + uusdc: Uint128::zero(), + ueden: Uint128::new(29), + uedenb: Uint128::new(100), + }, + available: Some(BalanceAvailable { + amount: Uint128::new(45666543), + usd_amount: Decimal::from_str("161.239475999999978995").unwrap(), + }), + staked: Some(StakedAvailable { + usd_amount: Decimal::from_str("35.308010067676894").unwrap(), + amount: Uint128::new(10000000), + lockups: Some(vec![]), + }), + rewards: Some(vec![]), + staked_positions: Some(vec![StakedPosition { + id: "2".to_string(), + validator: StakingValidator { + id: Some("1".to_string()), + address: "elysvaloper1ng8sen6z5xzcfjtyrsedpe43hglymq040x3cpw".to_string(), + name: "nirvana".to_string(), + voting_power: Decimal::from_str("25.6521469796402094").unwrap(), + commission: Decimal::from_str("0.1").unwrap(), + }, + staked: BalanceAvailable { + amount: Uint128::from_str("10000000").unwrap(), + usd_amount: Decimal::from_str("35.308010067676894").unwrap(), + }, + }]), + unstaked_positions: Some(vec![UnstakedPosition { + id: "1".to_string(), + validator: StakingValidator { + id: Some("1".to_string()), + address: "elysvaloper1ng8sen6z5xzcfjtyrsedpe43hglymq040x3cpw".to_string(), + name: "nirvana".to_string(), + voting_power: Decimal::from_str("25.6521469796402094").unwrap(), + commission: Decimal::from_str("0.1").unwrap(), + }, + remaining_time: 1707328694, + unstaked: BalanceAvailable { + amount: Uint128::from_str("100038144098").unwrap(), + usd_amount: Decimal::from_str("353214.779896389585407707").unwrap(), + }, + }]), + }, + usdc_earn_program: UsdcEarnProgram { + bonding_period: 0, + apr: AprUsdc { + uusdc: Int128::new(12), + ueden: Int128::new(12), + }, + available: Some(BalanceAvailable { + amount: Uint128::new(5333229342748), + usd_amount: Decimal::from_str("5333229.342748").unwrap(), + }), + staked: Some(StakedAvailable { + usd_amount: Decimal::zero(), + amount: Uint128::zero(), + lockups: None, + }), + rewards: Some(vec![CoinValue { + denom: "ueden".to_string(), + amount_token: Decimal::from_atomics(Uint128::new(000002), 5).unwrap(), + price: Decimal::from_atomics(Uint128::new(35308010067676894), 16).unwrap(), + amount_usd: Decimal::from_str("0.000070616020135353").unwrap(), + }]), + borrowed: Some(BalanceBorrowed { + usd_amount: Decimal::from_str("204000.000001").unwrap(), + percentage: Decimal::one(), + }), + }, + }, + }; + + // test if the response is the same as the expected + + // staked assets + + // USDC program + assert_eq!( + resp.staked_assets.usdc_earn_program.bonding_period, + expected.staked_assets.usdc_earn_program.bonding_period + ); + assert_eq!( + resp.staked_assets.usdc_earn_program.apr, + expected.staked_assets.usdc_earn_program.apr + ); + assert_eq!( + resp.staked_assets.usdc_earn_program.available, + expected.staked_assets.usdc_earn_program.available + ); + assert_eq!( + resp.staked_assets.usdc_earn_program.staked, + expected.staked_assets.usdc_earn_program.staked + ); + assert_eq!( + resp.staked_assets.usdc_earn_program.rewards, + expected.staked_assets.usdc_earn_program.rewards + ); + assert_eq!( + resp.staked_assets.usdc_earn_program.borrowed, + expected.staked_assets.usdc_earn_program.borrowed + ); + assert_eq!( + resp.staked_assets.usdc_earn_program, + expected.staked_assets.usdc_earn_program + ); + + // ELYS program + assert_eq!( + resp.staked_assets.elys_earn_program.bonding_period, + expected.staked_assets.elys_earn_program.bonding_period + ); + assert_eq!( + resp.staked_assets.elys_earn_program.apr, + expected.staked_assets.elys_earn_program.apr + ); + assert_eq!( + resp.staked_assets.elys_earn_program.available, + expected.staked_assets.elys_earn_program.available + ); + assert_eq!( + resp.staked_assets.elys_earn_program.staked, + expected.staked_assets.elys_earn_program.staked + ); + assert_eq!( + resp.staked_assets.elys_earn_program.rewards, + expected.staked_assets.elys_earn_program.rewards + ); + assert_eq!( + resp.staked_assets.elys_earn_program.staked_positions, + expected.staked_assets.elys_earn_program.staked_positions + ); + assert_eq!( + resp.staked_assets.elys_earn_program.unstaked_positions, + expected.staked_assets.elys_earn_program.unstaked_positions + ); + assert_eq!( + resp.staked_assets.elys_earn_program, + expected.staked_assets.elys_earn_program + ); + + // EDEN program + assert_eq!( + resp.staked_assets.eden_earn_program.bonding_period, + expected.staked_assets.eden_earn_program.bonding_period + ); + assert_eq!( + resp.staked_assets.eden_earn_program.apr, + expected.staked_assets.eden_earn_program.apr + ); + assert_eq!( + resp.staked_assets.eden_earn_program.available, + expected.staked_assets.eden_earn_program.available + ); + assert_eq!( + resp.staked_assets.eden_earn_program.staked, + expected.staked_assets.eden_earn_program.staked + ); + assert_eq!( + resp.staked_assets.eden_earn_program.rewards, + expected.staked_assets.eden_earn_program.rewards + ); + assert_eq!( + resp.staked_assets.eden_earn_program.vesting, + expected.staked_assets.eden_earn_program.vesting + ); + assert_eq!( + resp.staked_assets.eden_earn_program.vesting_details, + expected.staked_assets.eden_earn_program.vesting_details + ); + assert_eq!( + resp.staked_assets.eden_earn_program, + expected.staked_assets.eden_earn_program + ); + + // EDEN BOOST program + assert_eq!( + resp.staked_assets.eden_boost_earn_program.bonding_period, + expected + .staked_assets + .eden_boost_earn_program + .bonding_period + ); + assert_eq!( + resp.staked_assets.eden_boost_earn_program.apr, + expected.staked_assets.eden_boost_earn_program.apr + ); + assert_eq!( + resp.staked_assets.eden_boost_earn_program.available, + expected.staked_assets.eden_boost_earn_program.available + ); + assert_eq!( + resp.staked_assets.eden_boost_earn_program.staked, + expected.staked_assets.eden_boost_earn_program.staked + ); + assert_eq!( + resp.staked_assets.eden_boost_earn_program.rewards, + expected.staked_assets.eden_boost_earn_program.rewards + ); + assert_eq!( + resp.staked_assets.eden_boost_earn_program, + expected.staked_assets.eden_boost_earn_program + ); + + assert_eq!(resp.staked_assets, expected.staked_assets); + + assert_eq!(resp.total_staked_balance, expected.total_staked_balance); + assert_eq!(resp, expected); +} diff --git a/contracts/account-history-contract/src/tests/get_usdc_earn_program_details.rs b/contracts/account-history-contract/src/tests/get_usdc_earn_program_details.rs new file mode 100644 index 00000000..a3b5a726 --- /dev/null +++ b/contracts/account-history-contract/src/tests/get_usdc_earn_program_details.rs @@ -0,0 +1,305 @@ +use std::str::FromStr; + +use crate::entry_point::instantiate; +use crate::{ + entry_point::{execute, query, sudo}, + msg::*, +}; +use anyhow::{bail, Error, Result as AnyResult}; +use cosmwasm_std::{ + coins, to_json_binary, Addr, Coin, Decimal, Empty, Int128, StdError, Timestamp, Uint128, +}; +use cw_multi_test::{AppResponse, BasicAppBuilder, ContractWrapper, Executor, Module}; +use elys_bindings::account_history::msg::query_resp::earn::GetUsdcEarnProgramResp; +use elys_bindings::account_history::types::earn_program::UsdcEarnProgram; +use elys_bindings::account_history::types::{AprUsdc, CoinValue}; +use elys_bindings::query_resp::{ + BalanceBorrowed, DelegationDelegatorReward, EstakingRewardsResponse, + MasterchefUserPendingRewardData, MasterchefUserPendingRewardResponse, + QueryStableStakeAprResponse, StakedAvailable, +}; +use elys_bindings::types::BalanceAvailable; +use elys_bindings::{ElysMsg, ElysQuery}; +use elys_bindings_test::{ + ElysModule, ACCOUNT, ASSET_INFO, LAST_MODULE_USED, PERPETUAL_OPENED_POSITION, PRICES, +}; +use trade_shield_contract::entry_point::{ + execute as trade_shield_execute, instantiate as trade_shield_init, query as trade_shield_query, +}; +use trade_shield_contract::msg::InstantiateMsg as TradeShieldInstantiateMsg; + +struct ElysModuleWrapper(ElysModule); + +impl Module for ElysModuleWrapper { + type QueryT = ElysQuery; + type ExecT = ElysMsg; + type SudoT = Empty; + + fn query( + &self, + api: &dyn cosmwasm_std::Api, + storage: &dyn cosmwasm_std::Storage, + querier: &dyn cosmwasm_std::Querier, + block: &cosmwasm_std::BlockInfo, + request: Self::QueryT, + ) -> AnyResult { + match request { + ElysQuery::AmmBalance { address, denom } => { + let resp = match (address.as_str(), denom.as_str()) { + ( + "user", + "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", + ) => BalanceAvailable { + amount: Uint128::new(1234), + usd_amount: Decimal::from_str("1234").unwrap(), + }, + ("user", "uelys") => BalanceAvailable { + amount: Uint128::new(45666543), + usd_amount: Decimal::from_str("45666543").unwrap(), + }, + _ => BalanceAvailable { + amount: Uint128::zero(), + usd_amount: Decimal::zero(), + }, + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::MasterchefStableStakeApr { denom } => { + let resp = match denom.as_str() { + "uusdc" => QueryStableStakeAprResponse { + apr: Int128::zero(), + }, + "ueden" => QueryStableStakeAprResponse { + apr: Int128::zero(), + }, + _ => return Err(Error::new(StdError::not_found(denom))), + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::CommitmentStakedBalanceOfDenom { .. } => { + let resp = StakedAvailable { + usd_amount: Decimal::from_atomics(Uint128::new(100130012), 3).unwrap(), + amount: Uint128::new(100120000000), + lockups: None, + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::StableStakeBalanceOfBorrow {} => { + let resp = BalanceBorrowed { + usd_amount: Decimal::from_atomics(Uint128::new(3265035180871), 10).unwrap(), + percentage: Decimal::from_atomics(Uint128::new(0000238391578776388), 18) + .unwrap(), + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::MasterchefUserPendingReward { .. } => { + let resp = MasterchefUserPendingRewardResponse { + rewards: vec![MasterchefUserPendingRewardData { + pool_id: 32767u64, + reward: vec![Coin { + denom: "ueden".to_string(), + amount: Uint128::new(20), + }], + }], + total_rewards: vec![Coin { + denom: "ueden".to_string(), + amount: Uint128::new(20), + }], + }; + Ok(to_json_binary(&resp)?) + } + ElysQuery::AmmPriceByDenom { token_in, .. } => { + let spot_price = match token_in.denom.as_str() { + "uelys" => Decimal::from_str("3.5308010067676894").unwrap(), + "ueden" => Decimal::from_str("3.5308010067676894").unwrap(), + "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65" => { + Decimal::one() + } + "ibc/E2D2F6ADCC68AA3384B2F5DFACCA437923D137C14E86FB8A10207CF3BED0C8D4" => { + Decimal::from_str("9.02450744362719844").unwrap() + } + _ => return Err(Error::new(StdError::not_found(token_in.denom.as_str()))), + }; + Ok(to_json_binary(&spot_price)?) + } + ElysQuery::EstakingRewards { .. } => { + let resp = EstakingRewardsResponse { + rewards: vec![DelegationDelegatorReward { + validator_address: "validator".to_string(), + reward: vec![Coin { + denom: "ueden".to_string(), + amount: Uint128::from_str("121").unwrap(), + }], + }], + total: vec![Coin { + denom: "ueden".to_string(), + amount: Uint128::from_str("121").unwrap(), + }], + }; + Ok(to_json_binary(&resp)?) + } + _ => self.0.query(api, storage, querier, block, request), + } + } + + fn execute( + &self, + api: &dyn cosmwasm_std::Api, + storage: &mut dyn cosmwasm_std::Storage, + router: &dyn cw_multi_test::CosmosRouter, + block: &cosmwasm_std::BlockInfo, + sender: Addr, + msg: Self::ExecT, + ) -> AnyResult + where + ExecC: std::fmt::Debug + + Clone + + PartialEq + + schemars::JsonSchema + + serde::de::DeserializeOwned + + 'static, + QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, + { + match msg { + _ => self.0.execute(api, storage, router, block, sender, msg), + } + } + + fn sudo( + &self, + _api: &dyn cosmwasm_std::Api, + _storage: &mut dyn cosmwasm_std::Storage, + _router: &dyn cw_multi_test::CosmosRouter, + _block: &cosmwasm_std::BlockInfo, + _msg: Self::SudoT, + ) -> AnyResult + where + ExecC: std::fmt::Debug + + Clone + + PartialEq + + schemars::JsonSchema + + serde::de::DeserializeOwned + + 'static, + QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, + { + bail!("sudo is not implemented for ElysModule") + } +} + +#[test] +fn get_usdc_earn_program_details() { + // Create a wallet for the "user" with an initial balance of 100 usdc + let wallet = vec![( + "user", + coins( + 100, + "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", + ), + )]; + + let mut addresses: Vec = vec![]; + let mut app = BasicAppBuilder::::new_custom() + .with_custom(ElysModuleWrapper(ElysModule {})) + .build(|roouter, _, storage| { + for (wallet_owner, wallet_contenent) in wallet { + roouter + .bank + .init_balance(storage, &Addr::unchecked(wallet_owner), wallet_contenent) + .unwrap(); + addresses.push(wallet_owner.to_owned()) + } + ACCOUNT.save(storage, &addresses).unwrap(); + PERPETUAL_OPENED_POSITION.save(storage, &vec![]).unwrap(); + ASSET_INFO.save(storage, &vec![]).unwrap(); + PRICES.save(storage, &vec![]).unwrap(); + LAST_MODULE_USED.save(storage, &None).unwrap(); + }); + + // trade shield deployment + let trade_shield_code = + ContractWrapper::new(trade_shield_execute, trade_shield_init, trade_shield_query); + let trade_shield_code_id = app.store_code(Box::new(trade_shield_code)); + let trade_shield_init = TradeShieldInstantiateMsg { + account_history_address: None, + }; + let trade_shield_address = app + .instantiate_contract( + trade_shield_code_id, + Addr::unchecked("owner"), + &trade_shield_init, + &[], + "Contract", + None, + ) + .unwrap() + .to_string(); + + // Create a contract wrapper and store its code. + let code = ContractWrapper::new(execute, instantiate, query).with_sudo(sudo); + let code_id = app.store_code(Box::new(code)); + + // Create a mock message to instantiate the contract with no initial orders. + let instantiate_msg = InstantiateMsg { + limit: Some(3), + expiration: Some(cw_utils::Expiration::AtTime(Timestamp::from_seconds( + 604800, + ))), + trade_shield_address: Some(trade_shield_address), + }; + + // Instantiate the contract with "owner" as the deployer. + let addr = app + .instantiate_contract( + code_id, + Addr::unchecked("owner"), + &instantiate_msg, + &[], + "Contract", + None, + ) + .unwrap(); + + app.wasm_sudo(addr.clone(), &SudoMsg::ClockEndBlock {}) + .unwrap(); + + let resp: GetUsdcEarnProgramResp = app + .wrap() + .query_wasm_smart( + &addr, + &QueryMsg::GetUsdcEarnProgramDetails { + address: "user".to_string(), + }, + ) + .unwrap(); + + let expected = GetUsdcEarnProgramResp { + data: UsdcEarnProgram { + bonding_period: 0, + apr: AprUsdc { + uusdc: Int128::zero(), + ueden: Int128::zero(), + }, + available: Some(BalanceAvailable { + amount: Uint128::new(1234), + usd_amount: Decimal::from_str("0.001234").unwrap(), + }), + staked: Some(StakedAvailable { + usd_amount: Decimal::from_atomics(Uint128::new(100130012), 3).unwrap(), + amount: Uint128::new(100120000000), + lockups: None, + }), + rewards: Some(vec![CoinValue { + denom: "ueden".to_string(), + amount_token: Decimal::from_atomics(Uint128::new(000002), 5).unwrap(), + price: Decimal::from_atomics(Uint128::new(35308010067676894), 16).unwrap(), + amount_usd: Decimal::from_str("0.000070616020135353").unwrap(), + }]), + borrowed: Some(BalanceBorrowed { + usd_amount: Decimal::from_str("0.0003265035180871").unwrap(), + percentage: Decimal::from_atomics(Uint128::new(0000238391578776388), 18).unwrap(), + }), + }, + }; + + assert_eq!(resp, expected); +} diff --git a/contracts/account-history-contract/src/tests/mod.rs b/contracts/account-history-contract/src/tests/mod.rs new file mode 100644 index 00000000..f5708269 --- /dev/null +++ b/contracts/account-history-contract/src/tests/mod.rs @@ -0,0 +1,8 @@ +mod get_all_pools; +mod get_eden_boost_earn_program_details; +mod get_eden_earn_program_details; +mod get_elys_earn_program_detail; +mod get_liquid_assets; +mod get_rewards; +mod get_staked_assets; +mod get_usdc_earn_program_details; diff --git a/contracts/account-history-contract/src/types/account_snapshot_generator.rs b/contracts/account-history-contract/src/types/account_snapshot_generator.rs new file mode 100644 index 00000000..5c582ea2 --- /dev/null +++ b/contracts/account-history-contract/src/types/account_snapshot_generator.rs @@ -0,0 +1,778 @@ +use std::collections::HashMap; + +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{ + Coin, DecCoin, Decimal, Decimal256, Deps, Env, QuerierWrapper, StdError, StdResult, Uint128, +}; +use cw_utils::Expiration; +use elys_bindings::{ + account_history::{ + msg::query_resp::{GetRewardsResp, StakeAssetBalanceBreakdown, StakedAssetsResponse}, + types::{ + AccountSnapshot, CoinValue, ElysDenom, LiquidAsset, Metadata, PerpetualAsset, + PerpetualAssets, PoolBalances, PortfolioBalanceSnapshot, Reward, StakedAssets, + TotalBalance, + }, + }, + query_resp::{ + CommittedTokens, OracleAssetInfoResponse, PoolFilterType, PoolResp, QueryAprResponse, + QueryUserPoolResponse, UserPoolResp, + }, + trade_shield::{ + msg::{ + query_resp::{ + GetPerpetualOrdersResp, GetPerpetualPositionsForAddressResp, GetSpotOrdersResp, + }, + QueryMsg::{GetPerpetualOrders, GetSpotOrders, PerpetualGetPositionsForAddress}, + }, + types::{ + OracleAssetInfo, PerpetualOrder, PerpetualOrderPlus, PerpetualOrderType, SpotOrder, + Status, + }, + }, + ElysQuerier, ElysQuery, +}; + +use crate::{ + action::query::{ + get_eden_boost_earn_program_details, get_eden_earn_program_details, + get_elys_earn_program_details, get_pools, get_usdc_earn_program_details, + }, + states::{EXPIRATION, METADATA, TRADE_SHIELD_ADDRESS}, +}; + +#[cw_serde] +pub struct AccountSnapshotGenerator { + pub trade_shield_address: Option, + pub expiration: Expiration, + pub metadata: Metadata, +} + +impl AccountSnapshotGenerator { + pub fn new(deps: &Deps) -> StdResult { + let expiration = EXPIRATION.load(deps.storage)?; + let trade_shield_address = TRADE_SHIELD_ADDRESS.load(deps.storage)?; + let metadata = METADATA.load(deps.storage)?; + + Ok(Self { + trade_shield_address, + expiration, + metadata, + }) + } + + pub fn generate_portfolio_balance_snapshot_for_address( + &self, + querier: &ElysQuerier, + deps: &Deps, + env: &Env, + address: &String, + ) -> StdResult { + let snapshot = self.generate_account_snapshot_for_address(querier, deps, env, address)?; + + Ok(PortfolioBalanceSnapshot { + date: snapshot.date, + total_balance_usd: snapshot.total_balance.total_balance.clone(), + }) + } + + pub fn generate_account_snapshot_for_address( + &self, + querier: &ElysQuerier, + deps: &Deps, + env: &Env, + address: &String, + ) -> StdResult { + let liquid_assets_response = self.get_liquid_assets(&deps, querier, &address)?; // âś… Fixme + let staked_assets_response = self.get_staked_assets(&deps, Some(address.clone()))?; + let rewards_response = self.get_rewards(&deps, &address)?; + let perpetual_response = self.get_perpetuals(&deps, &address)?; + let pool_balances_response = self.get_pool_balances(&deps, &address)?; + + let date = match self.expiration { + Expiration::AtHeight(_) => Expiration::AtHeight(env.block.height), + Expiration::AtTime(_) => Expiration::AtTime(env.block.time), + Expiration::Never {} => panic!("never expire"), + }; + + let mut total_liquidity_position_balance = Decimal256::zero(); + for pool in pool_balances_response.pools.iter() { + total_liquidity_position_balance = + total_liquidity_position_balance.checked_add(Decimal256::from(pool.available))?; + } + + let reward = rewards_response.rewards_map; + let portfolio_usd = liquid_assets_response + .total_liquid_asset_balance + .amount + .checked_add(Decimal256::from(staked_assets_response.total_balance))? + .checked_add( + perpetual_response + .total_perpetual_asset_balance + .amount + .clone(), + )? + .checked_add(total_liquidity_position_balance)?; + + let reward_usd = Decimal256::from(reward.total_usd.clone()); + let total_balance = portfolio_usd.checked_add(reward_usd.clone())?; + + // Adds the records all the time as we should return data to the FE even if it is 0 balanced. + Ok(AccountSnapshot { + date, + total_balance: TotalBalance { + total_balance, + portfolio_usd: portfolio_usd.clone(), + reward_usd, + }, + reward, + pool_balances: PoolBalances { + balances: pool_balances_response.pools, + }, + liquid_asset: liquid_assets_response, + staked_assets: staked_assets_response.staked_assets, + perpetual_assets: perpetual_response, + }) + } + + pub fn get_pools_user_rewards( + &self, + deps: &Deps, + address: &String, + pools: Vec, + ) -> ( + Decimal, + HashMap, + HashMap>, + ) { + let querier = ElysQuerier::new(&deps.querier); + + let all_rewards = querier + .get_masterchef_pending_rewards(address.clone()) + .unwrap_or_default(); + let rewards_per_pool = all_rewards + .rewards_to_coin_values(&querier) + .unwrap_or_default(); + + let mut total_rewards = Decimal::zero(); + let mut total_breakdown: HashMap = HashMap::new(); + pools.iter().for_each(|pool| { + let pool_rewards = rewards_per_pool.get(&(pool.pool_id as u64)); + + match pool_rewards { + Some(rewards) => { + rewards.iter().for_each(|reward| { + total_rewards = total_rewards + .checked_add(reward.amount_usd) + .unwrap_or_default(); + + if let Some(breakdown_reward) = total_breakdown.get_mut(&reward.denom) { + // Update the amounts + breakdown_reward.amount_token = breakdown_reward + .amount_token + .checked_add(reward.amount_token) + .unwrap_or_default(); + breakdown_reward.amount_usd = breakdown_reward + .amount_usd + .checked_add(reward.amount_usd) + .unwrap_or_default(); + } else { + // Create a new default reward if it doesn't exist + let default_reward = CoinValue::new( + reward.denom.clone(), + Decimal::zero(), + reward.price, + Decimal::zero(), + ); + let breakdown_reward = total_breakdown + .entry(reward.denom.clone()) + .or_insert(default_reward); + + // Update the amounts + breakdown_reward.amount_token = breakdown_reward + .amount_token + .checked_add(reward.amount_token) + .unwrap_or_default(); + breakdown_reward.amount_usd = breakdown_reward + .amount_usd + .checked_add(reward.amount_usd) + .unwrap_or_default(); + } + }); + } + None => {} + }; + }); + + (total_rewards, total_breakdown, rewards_per_pool) + } + + pub fn get_pool_balances( + &self, + deps: &Deps, + address: &String, + ) -> StdResult { + let querier = ElysQuerier::new(&deps.querier); + let commitments = querier.get_commitments(address.clone())?.commitments; + + struct IdSortedPoolBalance { + pub id: u64, + pub balance: CommittedTokens, + } + + let pool_balances: Vec = + commitments.committed_tokens.map_or(vec![], |res| { + res.iter() + .filter(|coin| coin.denom.starts_with("amm/pool/")) + .cloned() + .collect() + }); + + let pool_data: Vec = pool_balances + .iter() + .map(|coin| { + let id = coin + .denom + .split("/") + .last() + .unwrap_or("0") + .parse::() + .unwrap_or(0u64); + IdSortedPoolBalance { + id, + balance: coin.clone(), + } + }) + .collect(); + + // For each pool_data, fetch the pool with that ID + let mut pool_resp: Vec = Vec::new(); + for user_pool in pool_data { + let pool_id = user_pool.id; + let pool = get_pools(*deps, Some(vec![pool_id]), PoolFilterType::FilterAll, None)?; + let pool = pool + .pools + .unwrap_or_default() + .first() + .map_or(PoolResp::default(), |pool| pool.clone()); + + let balance_uint = Uint128::new(user_pool.balance.amount.i128() as u128); + let share_price = pool.share_usd_price.or(Some(Decimal::zero())).unwrap(); + + // Assumes that pool.assets are in the desired displaying sort order. + let balance_breakdown = pool + .assets + .clone() + .into_iter() + .map(|asset| { + pool.current_pool_ratio.clone().map_or(None, |ratios| { + let denom = asset.token.denom.clone(); + let ratio = ratios.get(&denom); + let asset_price = querier.get_asset_price(denom.clone()); + + match (asset_price, ratio) { + (Ok(price), Some(ratio)) => { + let asset_shares = + Decimal::from_atomics(balance_uint, 18).unwrap() * ratio; + let shares_usd = asset_shares * share_price; + let asset_amount = shares_usd / price; + + Some(CoinValue::new(denom, asset_amount, price, shares_usd)) + } + (_, _) => None, + } + }) + }) + .collect(); + + pool_resp.push(UserPoolResp { + pool, + balance: user_pool.balance, + available: Decimal::from_atomics(balance_uint, 18).unwrap() * share_price, + balance_breakdown, + }); + } + + let pools: Vec = pool_resp + .iter() + .map(|user_pool| user_pool.pool.clone()) + .collect(); + + let (total_rewards, total_rewards_breakdown, rewards_per_pool) = + self.get_pools_user_rewards(&deps, address, pools); + + Ok(QueryUserPoolResponse { + pools: pool_resp, + total_rewards, + total_rewards_breakdown, + rewards_per_pool, + }) + } + + pub fn get_liquid_assets( + &self, + deps: &Deps, + querier: &ElysQuerier, + address: &String, + ) -> StdResult { + let mut account_balances = deps.querier.query_all_balances(address)?; + let orders_balances = + self.get_all_orders(&deps.querier, &self.trade_shield_address, &address)?; + + let eden_program = get_eden_earn_program_details( + deps, + Some(address.to_owned()), + ElysDenom::Eden.as_str().to_string(), + self.metadata.uusdc_usd_price, + self.metadata.uelys_price_in_uusdc, + self.metadata.usdc_apr_eden.to_owned(), + self.metadata.eden_apr_eden.to_owned(), + self.metadata.edenb_apr_eden.to_owned(), + ) + .unwrap_or_default(); + + let available = eden_program.data.to_coin_available(); + if available.amount > Uint128::zero() { + account_balances.push(available); + } + + // `total_value_per_asset` that maps references to denoms (`&String`) to values of type + // `Coin`. it is used to store the coin of unique denom. + let mut total_value_per_asset: HashMap<&String, Coin> = HashMap::new(); + // `price_per_asset` that maps references to denoms to tuples containing a `Decimal` and an unsigned 64-bit integer. + let mut price_per_asset: HashMap<&String, (Decimal, u64)> = HashMap::new(); + + for available in account_balances.iter() { + total_value_per_asset + .entry(&available.denom) + .and_modify(|e| e.amount += available.amount) + .or_insert_with(|| available.clone()); + } + + for in_order in orders_balances.iter() { + total_value_per_asset + .entry(&in_order.denom) + .and_modify(|e| e.amount += in_order.amount) + .or_insert_with(|| in_order.clone()); + } + + // memoization of price and decimal point for each unique denom in `total_value_per_asset` + for (key, _) in total_value_per_asset.iter() { + let val = Self::get_price_and_decimal_point(key.to_string(), querier); + if let Ok(v) = val { + price_per_asset.insert(key, v); + } + } + + // closure for converting coin to coinvalue using from_price_and_coin + let coin_to_coin_value = |coin: &Coin| { + let price_and_point = price_per_asset.get(&coin.denom); + if price_and_point.is_none() { + return None; + } + CoinValue::from_price_and_coin(coin, price_and_point.unwrap().clone()).ok() + }; + + let available_asset_balance: Vec = account_balances + .iter() + .filter_map(coin_to_coin_value) + .collect(); + + let in_orders_asset_balance: Vec = orders_balances + .iter() + .filter_map(coin_to_coin_value) + .collect(); + + let mut total_available_balance = + DecCoin::new(Decimal256::zero(), &self.metadata.usdc_denom); + let mut total_in_orders_balance = + DecCoin::new(Decimal256::zero(), &self.metadata.usdc_denom); + + for balance in &available_asset_balance { + total_available_balance.amount = total_available_balance + .amount + .checked_add(Decimal256::from(balance.amount_usd.clone()))? + } + + for balance in &in_orders_asset_balance { + total_in_orders_balance.amount = total_in_orders_balance + .amount + .checked_add(Decimal256::from(balance.amount_usd.clone()))? + } + + let total_value_per_asset: Vec = total_value_per_asset + .values() + .filter_map(coin_to_coin_value) + .collect(); + + // calculating total liquid assets + let total_liquid_asset_balance = DecCoin::new( + total_available_balance.amount + total_in_orders_balance.amount, + &self.metadata.usdc_denom, + ); + + Ok(LiquidAsset { + total_liquid_asset_balance, + total_available_balance, + total_in_orders_balance, + available_asset_balance, + in_orders_asset_balance, + total_value_per_asset, + }) + } + + pub fn get_staked_assets( + &self, + deps: &Deps, + address: Option, + ) -> StdResult { + let querier = ElysQuerier::new(&deps.querier); + + let aprs = querier.get_incentive_aprs().unwrap_or_default(); + + // create staked_assets variable that is a StakedAssets struct + let mut staked_assets = StakedAssets::default(); + let mut total_staked_balance = Decimal::zero(); + + // usdc program + let usdc_details = get_usdc_earn_program_details( + deps, + address.clone(), + self.metadata.usdc_denom.to_owned(), + self.metadata.usdc_base_denom.to_owned(), + self.metadata.uusdc_usd_price, + ) + .unwrap_or_default(); + + let staked_asset_usdc = usdc_details.data; + total_staked_balance = total_staked_balance + .checked_add( + staked_asset_usdc + .clone() + .staked + .unwrap_or_default() + .usd_amount, + ) + .unwrap_or_default(); + staked_assets.usdc_earn_program = staked_asset_usdc; + + // elys program + let elys_details = get_elys_earn_program_details( + deps, + address.clone(), + ElysDenom::Elys.as_str().to_string(), + self.metadata.uusdc_usd_price, + self.metadata.uelys_price_in_uusdc, + QueryAprResponse { + apr: aprs.usdc_apr_elys, + }, + QueryAprResponse { + apr: aprs.eden_apr_elys, + }, + QueryAprResponse { + apr: aprs.edenb_apr_elys, + }, + ) + .unwrap_or_default(); + + let staked_asset_elys = elys_details.data; + total_staked_balance = total_staked_balance + .checked_add( + staked_asset_elys + .clone() + .staked + .unwrap_or_default() + .usd_amount, + ) + .unwrap_or_default(); + staked_assets.elys_earn_program = staked_asset_elys.clone(); + + let unstaking = staked_asset_elys + .unstaked_positions + .map_or(Decimal::zero(), |x| { + x.iter().fold(Decimal::zero(), |acc, position| { + acc.checked_add(position.unstaked.usd_amount) + .unwrap_or_default() + }) + }); + + // eden program + let eden_details = get_eden_earn_program_details( + deps, + address.clone(), + ElysDenom::Eden.as_str().to_string(), + self.metadata.uusdc_usd_price, + self.metadata.uelys_price_in_uusdc, + QueryAprResponse { + apr: aprs.usdc_apr_eden, + }, + QueryAprResponse { + apr: aprs.eden_apr_eden, + }, + QueryAprResponse { + apr: aprs.edenb_apr_eden, + }, + ) + .unwrap_or_default(); + + let staked_asset_eden = eden_details.data; + total_staked_balance = total_staked_balance + .checked_add( + staked_asset_eden + .clone() + .staked + .unwrap_or_default() + .usd_amount, + ) + .unwrap_or_default(); + let vesting = staked_asset_eden.vesting.usd_amount; + + staked_assets.eden_earn_program = staked_asset_eden; + + let edenb_details = get_eden_boost_earn_program_details( + deps, + address, + ElysDenom::EdenBoost.as_str().to_string(), + QueryAprResponse { + apr: aprs.usdc_apr_edenb, + }, + QueryAprResponse { + apr: aprs.eden_apr_edenb, + }, + ) + .unwrap_or_default(); + + let staked_asset_edenb = edenb_details.data; + staked_assets.eden_boost_earn_program = staked_asset_edenb; + let balance_break_down = StakeAssetBalanceBreakdown { + staked: Decimal::from(total_staked_balance), + unstaking, + vesting, + }; + + Ok(StakedAssetsResponse { + staked_assets, + total_staked_balance: DecCoin::new( + Decimal256::from(total_staked_balance), + self.metadata.usdc_denom.to_owned(), + ), + total_balance: balance_break_down.total(), + balance_break_down, + }) + } + + pub fn get_all_orders( + &self, + querier: &QuerierWrapper, + trade_shield_address: &Option, + owner: &String, + ) -> StdResult> { + let trade_shield_address = match trade_shield_address { + Some(trade_shield_address) => trade_shield_address, + None => return Ok(vec![]), + }; + + let spot_order: GetSpotOrdersResp = querier + .query_wasm_smart( + trade_shield_address, + &GetSpotOrders { + pagination: None, + order_owner: Some(owner.clone()), + order_type: None, + order_status: Some(Status::Pending), + }, + ) + .map_err(|e| StdError::generic_err(format!("GetSpotOrders failed {}", e)))?; + let perpetual_order: GetPerpetualOrdersResp = querier + .query_wasm_smart( + trade_shield_address, + &GetPerpetualOrders { + pagination: None, + order_owner: Some(owner.clone()), + order_type: Some(PerpetualOrderType::LimitOpen), + order_status: Some(Status::Pending), + }, + ) + .map_err(|e| StdError::generic_err(format!("GetPerpetualOrders failed {}", e)))?; + let mut map: HashMap = HashMap::new(); + + for SpotOrder { order_amount, .. } in spot_order.orders { + map.entry(order_amount.denom) + .and_modify(|e| *e += order_amount.amount) + .or_insert(order_amount.amount); + } + for PerpetualOrderPlus { + order: PerpetualOrder { collateral, .. }, + .. + } in perpetual_order.orders + { + map.entry(collateral.denom) + .and_modify(|e| *e += collateral.amount) + .or_insert(collateral.amount); + } + + let consolidated_coins: Vec = map + .into_iter() + .map(|(denom, amount)| Coin { denom, amount }) + .collect(); + Ok(consolidated_coins) + } + + pub fn get_perpetuals( + &self, + deps: &Deps, + address: &String, + ) -> StdResult { + let trade_shield_address = match self.trade_shield_address.clone() { + Some(trade_shield_address) => trade_shield_address, + None => return Ok(PerpetualAssets::default()), + }; + + let GetPerpetualPositionsForAddressResp { mtps, .. } = deps + .querier + .query_wasm_smart( + trade_shield_address, + &PerpetualGetPositionsForAddress { + address: address.to_string(), + pagination: None, + }, + ) + .map_err(|_| StdError::generic_err("an error occurred while getting perpetuals"))?; + let mut perpetual_vec: Vec = vec![]; + let querier = ElysQuerier::new(&deps.querier); + + for mtp in mtps { + match PerpetualAsset::new(mtp, self.metadata.usdc_denom.to_owned(), &querier) { + Ok(perpetual_asset) => perpetual_vec.push(perpetual_asset), + Err(_) => continue, + } + } + + let total_perpetual_asset_balance_amount = perpetual_vec + .iter() + .map(|perpetual| perpetual.size.amount) + .fold(Decimal256::zero(), |acc, item| acc + item); + let total_perpetual_asset_balance = DecCoin::new( + total_perpetual_asset_balance_amount, + self.metadata.usdc_denom.to_owned(), + ); + + Ok(PerpetualAssets { + total_perpetual_asset_balance, + perpetual_asset: perpetual_vec, + }) + } + + pub fn get_rewards( + &self, + deps: &Deps, + address: &String, + ) -> StdResult { + let querier = ElysQuerier::new(&deps.querier); + + // Elys Eden and Eden Boost Program rewards + let estaking_rewards = querier + .get_estaking_rewards(address.clone()) + .unwrap_or_default(); + // All pool rewards including USDC program rewards + let masterchef_rewards = querier.get_masterchef_pending_rewards(address.to_string())?; + + // Concatenate all staking reward vectors into one + let all_staking_rewards: Vec<&Coin> = estaking_rewards + .total + .iter() + .chain(&masterchef_rewards.total_rewards) + .collect(); + + // Accumulate amounts for each denomination + let mut denom_amounts: HashMap = HashMap::new(); + all_staking_rewards.iter().for_each(|coin| { + denom_amounts + .entry(coin.denom.clone()) + .and_modify(|amount| { + *amount += coin.amount; + }) + .or_insert(coin.amount); + }); + + // Convert accumulated amounts to CoinValue instances + let mut reward_map: HashMap = HashMap::new(); + for (denom, amount) in denom_amounts { + let dec_coin_value = CoinValue::from_coin( + &Coin { + denom: denom.clone(), + amount, + }, + &querier, + ) + .unwrap_or_default(); + + reward_map.insert(denom, dec_coin_value); + } + + let total_usd: Decimal = reward_map.values().map(|v| v.amount_usd).sum(); + + // Calculate other_usd as the sum of all amount_usd values in reward_map + // excluding USDC, Eden, and EdenBoost + let mut other_usd: Decimal = Decimal::zero(); + for (denom, value) in &reward_map { + if denom != &self.metadata.usdc_denom + && denom != &ElysDenom::Eden.as_str().to_string() + && denom != &ElysDenom::EdenBoost.as_str().to_string() + { + other_usd += value.amount_usd; + } + } + + let reward = Reward { + usdc_usd: reward_map + .entry(self.metadata.usdc_denom.clone()) + .or_default() + .amount_usd, + eden_usd: reward_map + .entry(ElysDenom::Eden.as_str().to_string()) + .or_default() + .amount_usd, + eden_boost: reward_map + .entry(ElysDenom::EdenBoost.as_str().to_string()) + .or_default() + .amount_token, + other_usd, + total_usd, + }; + + // Construct rewards_vec as the values of all rewards_map entries + let rewards_vec: Vec = reward_map.into_iter().map(|(_, v)| v).collect(); + + let resp = GetRewardsResp { + rewards_map: reward, + rewards: rewards_vec, + }; + + Ok(resp) + } + + fn get_price_and_decimal_point( + denom: String, + querier: &ElysQuerier<'_>, + ) -> StdResult<(Decimal, u64)> { + let OracleAssetInfoResponse { asset_info } = + querier + .asset_info(denom.clone()) + .unwrap_or(OracleAssetInfoResponse { + asset_info: OracleAssetInfo { + denom: denom.clone(), + display: denom.clone(), + band_ticker: denom.clone(), + elys_ticker: denom.clone(), + decimal: 6, + }, + }); + + let price = querier + .get_asset_price(denom.clone()) + .map_err(|e| StdError::generic_err(format!("failed to get_asset_price: {}", e)))?; + + Ok((price, asset_info.decimal)) + } +} diff --git a/contracts/account-history-contract/src/types/mod.rs b/contracts/account-history-contract/src/types/mod.rs new file mode 100644 index 00000000..3f77e314 --- /dev/null +++ b/contracts/account-history-contract/src/types/mod.rs @@ -0,0 +1,3 @@ +mod account_snapshot_generator; + +pub use account_snapshot_generator::AccountSnapshotGenerator; diff --git a/contracts/account-history-contract/src/utils.rs b/contracts/account-history-contract/src/utils.rs new file mode 100644 index 00000000..2fafc0f4 --- /dev/null +++ b/contracts/account-history-contract/src/utils.rs @@ -0,0 +1,11 @@ +use chrono::NaiveDateTime; +use cosmwasm_std::BlockInfo; + +pub fn get_raw_today(block_info: &BlockInfo) -> NaiveDateTime { + NaiveDateTime::from_timestamp_opt(block_info.time.seconds() as i64, 0) + .expect("Failed to convert block time to date") +} + +pub fn get_today(block_info: &BlockInfo) -> String { + get_raw_today(block_info).format("%Y-%m-%d").to_string() +} From 52cfaa6e5990782af9cabeaad81449027a92305a Mon Sep 17 00:00:00 2001 From: CryptoKage2306 <26vivek06@gmail.com> Date: Tue, 13 Aug 2024 18:48:53 +0530 Subject: [PATCH 2/7] feat: fix contract --- Cargo.lock | 48 +++++++++++++++++++ .../estaking/get_estaking_rewards_response.rs | 2 +- .../msg/query_resp/get_rewards_resp.rs | 3 +- .../get_masterchef_pending_rewards.rs | 2 +- .../types/earn_program/eden_boost_earn.rs | 3 +- .../types/earn_program/eden_earn.rs | 3 +- .../types/earn_program/elys_earn.rs | 3 +- .../types/earn_program/usdc_earn.rs | 3 +- .../src/account_history/types/liquid_asset.rs | 2 +- bindings/src/query_resp.rs | 1 - .../src/trade_shield/msg/instantiate_msg.rs | 4 +- bindings/src/trade_shield/msg/migrate_msg.rs | 4 +- .../src/action/query/get_estaking_rewards.rs | 2 +- .../src/action/query/get_liquid_assets.rs | 2 +- .../src/types/account_snapshot_generator.rs | 9 ++-- 15 files changed, 73 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 656f9828..6c6b8b7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,30 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "account-history-contract" +version = "0.0.0" +dependencies = [ + "account-history-contract", + "anyhow", + "cargo-husky", + "chrono", + "cosmwasm-schema", + "cosmwasm-std", + "cw-multi-test", + "cw-storage-plus 1.2.0", + "cw-utils", + "cw2", + "elys-bindings", + "elys-bindings-test", + "schemars", + "semver", + "serde", + "serde_json", + "thiserror", + "trade_shield_contract", +] + [[package]] name = "ahash" version = "0.7.8" @@ -19,6 +43,12 @@ version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + [[package]] name = "base16ct" version = "0.2.0" @@ -91,6 +121,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "num-traits", +] + [[package]] name = "const-oid" version = "0.9.6" @@ -527,6 +566,15 @@ version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell" version = "1.19.0" diff --git a/bindings/src/account_history/msg/query_resp/estaking/get_estaking_rewards_response.rs b/bindings/src/account_history/msg/query_resp/estaking/get_estaking_rewards_response.rs index b1d2a699..ee98b493 100644 --- a/bindings/src/account_history/msg/query_resp/estaking/get_estaking_rewards_response.rs +++ b/bindings/src/account_history/msg/query_resp/estaking/get_estaking_rewards_response.rs @@ -1,7 +1,7 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::Coin; -use crate::account_history::types::CoinValue; +use crate::trade_shield::types::CoinValue; #[cw_serde] pub struct GetEstakingRewardsResponse { diff --git a/bindings/src/account_history/msg/query_resp/get_rewards_resp.rs b/bindings/src/account_history/msg/query_resp/get_rewards_resp.rs index 6afdc875..0682c5ed 100644 --- a/bindings/src/account_history/msg/query_resp/get_rewards_resp.rs +++ b/bindings/src/account_history/msg/query_resp/get_rewards_resp.rs @@ -1,4 +1,5 @@ -use crate::account_history::types::{CoinValue, Reward}; +use crate::account_history::types::Reward; +use crate::trade_shield::types::CoinValue; use cosmwasm_schema::cw_serde; diff --git a/bindings/src/account_history/msg/query_resp/masterchef/get_masterchef_pending_rewards.rs b/bindings/src/account_history/msg/query_resp/masterchef/get_masterchef_pending_rewards.rs index b3d30886..31596dd9 100644 --- a/bindings/src/account_history/msg/query_resp/masterchef/get_masterchef_pending_rewards.rs +++ b/bindings/src/account_history/msg/query_resp/masterchef/get_masterchef_pending_rewards.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use cosmwasm_schema::cw_serde; -use crate::account_history::types::CoinValue; +use crate::trade_shield::types::CoinValue; #[cw_serde] #[derive(Default)] diff --git a/bindings/src/account_history/types/earn_program/eden_boost_earn.rs b/bindings/src/account_history/types/earn_program/eden_boost_earn.rs index e260fca4..768bae3f 100644 --- a/bindings/src/account_history/types/earn_program/eden_boost_earn.rs +++ b/bindings/src/account_history/types/earn_program/eden_boost_earn.rs @@ -1,7 +1,8 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::Uint128; -use crate::account_history::types::{earn_detail::earn_detail::AprEdenBoost, CoinValue}; +use crate::account_history::types::earn_detail::earn_detail::AprEdenBoost; +use crate::trade_shield::types::CoinValue; #[cw_serde] pub struct EdenBoostEarnProgram { diff --git a/bindings/src/account_history/types/earn_program/eden_earn.rs b/bindings/src/account_history/types/earn_program/eden_earn.rs index c97b4f04..07e708e3 100644 --- a/bindings/src/account_history/types/earn_program/eden_earn.rs +++ b/bindings/src/account_history/types/earn_program/eden_earn.rs @@ -1,7 +1,8 @@ use crate::{ - account_history::types::{AprElys, CoinValue, ElysDenom}, + account_history::types::{AprElys, ElysDenom}, query_resp::StakedAvailable, trade_shield::types::BalanceAvailable, + trade_shield::types::CoinValue, types::VestingDetail, }; diff --git a/bindings/src/account_history/types/earn_program/elys_earn.rs b/bindings/src/account_history/types/earn_program/elys_earn.rs index e389c301..f73824f5 100644 --- a/bindings/src/account_history/types/earn_program/elys_earn.rs +++ b/bindings/src/account_history/types/earn_program/elys_earn.rs @@ -1,6 +1,7 @@ use crate::{ - account_history::types::{AprElys, CoinValue}, + account_history::types::AprElys, query_resp::StakedAvailable, + trade_shield::types::CoinValue, types::{BalanceAvailable, StakedPosition, UnstakedPosition}, }; diff --git a/bindings/src/account_history/types/earn_program/usdc_earn.rs b/bindings/src/account_history/types/earn_program/usdc_earn.rs index d0b13493..2eb2d686 100644 --- a/bindings/src/account_history/types/earn_program/usdc_earn.rs +++ b/bindings/src/account_history/types/earn_program/usdc_earn.rs @@ -1,6 +1,7 @@ use crate::{ - account_history::types::{AprUsdc, CoinValue}, + account_history::types::AprUsdc, query_resp::{BalanceBorrowed, StakedAvailable}, + trade_shield::types::CoinValue, types::BalanceAvailable, }; diff --git a/bindings/src/account_history/types/liquid_asset.rs b/bindings/src/account_history/types/liquid_asset.rs index 60b473a1..a20a13c9 100644 --- a/bindings/src/account_history/types/liquid_asset.rs +++ b/bindings/src/account_history/types/liquid_asset.rs @@ -1,7 +1,7 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::{DecCoin, Decimal256}; -use super::CoinValue; +use crate::trade_shield::types::CoinValue; #[cw_serde] pub struct LiquidAsset { diff --git a/bindings/src/query_resp.rs b/bindings/src/query_resp.rs index 47f76165..855a352c 100644 --- a/bindings/src/query_resp.rs +++ b/bindings/src/query_resp.rs @@ -627,7 +627,6 @@ pub struct LeveragelpPosition { pub interest_rate_hour_usd: Decimal, } - #[cw_serde] pub struct LeveragelpPositionResponse { pub position: Option, diff --git a/bindings/src/trade_shield/msg/instantiate_msg.rs b/bindings/src/trade_shield/msg/instantiate_msg.rs index 1ca3d31c..c9d1ad02 100644 --- a/bindings/src/trade_shield/msg/instantiate_msg.rs +++ b/bindings/src/trade_shield/msg/instantiate_msg.rs @@ -1,4 +1,6 @@ use cosmwasm_schema::cw_serde; #[cw_serde] -pub struct InstantiateMsg {} +pub struct InstantiateMsg { + pub account_history_address: Option, +} diff --git a/bindings/src/trade_shield/msg/migrate_msg.rs b/bindings/src/trade_shield/msg/migrate_msg.rs index 0903fe8b..23721cfc 100644 --- a/bindings/src/trade_shield/msg/migrate_msg.rs +++ b/bindings/src/trade_shield/msg/migrate_msg.rs @@ -1,4 +1,6 @@ use cosmwasm_schema::cw_serde; #[cw_serde] -pub struct MigrateMsg {} +pub struct MigrateMsg { + pub account_history_address: Option, +} diff --git a/contracts/account-history-contract/src/action/query/get_estaking_rewards.rs b/contracts/account-history-contract/src/action/query/get_estaking_rewards.rs index 284c8503..dac85c21 100644 --- a/contracts/account-history-contract/src/action/query/get_estaking_rewards.rs +++ b/contracts/account-history-contract/src/action/query/get_estaking_rewards.rs @@ -1,6 +1,6 @@ use cosmwasm_std::{Deps, StdResult}; use elys_bindings::account_history::msg::query_resp::estaking::GetEstakingRewardsResponse; -use elys_bindings::account_history::types::CoinValue; +use elys_bindings::trade_shield::types::CoinValue; use elys_bindings::{ElysQuerier, ElysQuery}; /** diff --git a/contracts/account-history-contract/src/action/query/get_liquid_assets.rs b/contracts/account-history-contract/src/action/query/get_liquid_assets.rs index 96a7d603..cfc8c23c 100644 --- a/contracts/account-history-contract/src/action/query/get_liquid_assets.rs +++ b/contracts/account-history-contract/src/action/query/get_liquid_assets.rs @@ -1,5 +1,5 @@ use cosmwasm_std::{Decimal, Deps, Env, StdResult}; -use elys_bindings::{account_history::types::CoinValue, ElysQuerier, ElysQuery}; +use elys_bindings::{trade_shield::types::CoinValue, ElysQuerier, ElysQuery}; use crate::{ msg::query_resp::{GetLiquidAssetsResp, LiquidAsset}, diff --git a/contracts/account-history-contract/src/types/account_snapshot_generator.rs b/contracts/account-history-contract/src/types/account_snapshot_generator.rs index 5c582ea2..cc53b331 100644 --- a/contracts/account-history-contract/src/types/account_snapshot_generator.rs +++ b/contracts/account-history-contract/src/types/account_snapshot_generator.rs @@ -9,9 +9,8 @@ use elys_bindings::{ account_history::{ msg::query_resp::{GetRewardsResp, StakeAssetBalanceBreakdown, StakedAssetsResponse}, types::{ - AccountSnapshot, CoinValue, ElysDenom, LiquidAsset, Metadata, PerpetualAsset, - PerpetualAssets, PoolBalances, PortfolioBalanceSnapshot, Reward, StakedAssets, - TotalBalance, + AccountSnapshot, ElysDenom, LiquidAsset, Metadata, PerpetualAsset, PerpetualAssets, + PoolBalances, PortfolioBalanceSnapshot, Reward, StakedAssets, TotalBalance, }, }, query_resp::{ @@ -26,8 +25,8 @@ use elys_bindings::{ QueryMsg::{GetPerpetualOrders, GetSpotOrders, PerpetualGetPositionsForAddress}, }, types::{ - OracleAssetInfo, PerpetualOrder, PerpetualOrderPlus, PerpetualOrderType, SpotOrder, - Status, + CoinValue, OracleAssetInfo, PerpetualOrder, PerpetualOrderPlus, PerpetualOrderType, + SpotOrder, Status, }, }, ElysQuerier, ElysQuery, From 60e3bdfb8edc35c76a35dc1f448b4a1d45236b57 Mon Sep 17 00:00:00 2001 From: CryptoKage2306 <26vivek06@gmail.com> Date: Tue, 13 Aug 2024 18:56:54 +0530 Subject: [PATCH 3/7] refactor: remove tests --- contracts/account-history-contract/src/lib.rs | 3 - .../src/tests/get_all_pools.rs | 359 -------- .../get_eden_boost_earn_program_details.rs | 295 ------ .../tests/get_eden_earn_program_details.rs | 308 ------- .../src/tests/get_elys_earn_program_detail.rs | 304 ------- .../src/tests/get_liquid_assets.rs | 541 ----------- .../src/tests/get_rewards.rs | 287 ------ .../src/tests/get_staked_assets.rs | 850 ------------------ .../tests/get_usdc_earn_program_details.rs | 305 ------- .../account-history-contract/src/tests/mod.rs | 8 - 10 files changed, 3260 deletions(-) delete mode 100644 contracts/account-history-contract/src/tests/get_all_pools.rs delete mode 100644 contracts/account-history-contract/src/tests/get_eden_boost_earn_program_details.rs delete mode 100644 contracts/account-history-contract/src/tests/get_eden_earn_program_details.rs delete mode 100644 contracts/account-history-contract/src/tests/get_elys_earn_program_detail.rs delete mode 100644 contracts/account-history-contract/src/tests/get_liquid_assets.rs delete mode 100644 contracts/account-history-contract/src/tests/get_rewards.rs delete mode 100644 contracts/account-history-contract/src/tests/get_staked_assets.rs delete mode 100644 contracts/account-history-contract/src/tests/get_usdc_earn_program_details.rs delete mode 100644 contracts/account-history-contract/src/tests/mod.rs diff --git a/contracts/account-history-contract/src/lib.rs b/contracts/account-history-contract/src/lib.rs index c2d0a800..1705184a 100644 --- a/contracts/account-history-contract/src/lib.rs +++ b/contracts/account-history-contract/src/lib.rs @@ -10,6 +10,3 @@ mod action; mod error; mod states; mod types; - -#[cfg(test)] -mod tests; diff --git a/contracts/account-history-contract/src/tests/get_all_pools.rs b/contracts/account-history-contract/src/tests/get_all_pools.rs deleted file mode 100644 index 764d5914..00000000 --- a/contracts/account-history-contract/src/tests/get_all_pools.rs +++ /dev/null @@ -1,359 +0,0 @@ -use std::collections::HashMap; -use std::str::FromStr; - -use crate::entry_point::instantiate; -use crate::{ - entry_point::{execute, query, sudo}, - msg::*, -}; -use anyhow::{bail, Error, Result as AnyResult}; -use cosmwasm_std::{ - coin, coins, to_json_binary, Addr, Coin, Decimal, Empty, Int128, StdError, Timestamp, Uint128, -}; -use cw_multi_test::{AppResponse, BasicAppBuilder, ContractWrapper, Executor, Module}; -use elys_bindings::account_history::types::CoinValue; -use elys_bindings::query_resp::{ - DelegationDelegatorReward, EstakingRewardsResponse, MasterchefUserPendingRewardData, - MasterchefUserPendingRewardResponse, PoolApr, PoolFilterType, PoolResp, QueryEarnPoolResponse, - QueryStableStakeAprResponse, Validator, -}; -use elys_bindings::types::{BalanceAvailable, PoolAsset}; -use elys_bindings::{ElysMsg, ElysQuery}; -use elys_bindings_test::{ - ElysModule, ACCOUNT, ASSET_INFO, LAST_MODULE_USED, PERPETUAL_OPENED_POSITION, PRICES, -}; -use trade_shield_contract::entry_point::{ - execute as trade_shield_execute, instantiate as trade_shield_init, query as trade_shield_query, -}; -use trade_shield_contract::msg::InstantiateMsg as TradeShieldInstantiateMsg; - -struct ElysModuleWrapper(ElysModule); - -impl Module for ElysModuleWrapper { - type QueryT = ElysQuery; - type ExecT = ElysMsg; - type SudoT = Empty; - - fn query( - &self, - api: &dyn cosmwasm_std::Api, - storage: &dyn cosmwasm_std::Storage, - querier: &dyn cosmwasm_std::Querier, - block: &cosmwasm_std::BlockInfo, - request: Self::QueryT, - ) -> AnyResult { - match request { - ElysQuery::AmmBalance { address, denom } => { - let resp = match (address.as_str(), denom.as_str()) { - ("user", "uedenb") => BalanceAvailable { - amount: Uint128::new(21798000), - usd_amount: Decimal::from_str("21798000").unwrap(), - }, - ( - "user", - "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", - ) => BalanceAvailable { - amount: Uint128::new(5333229342748), - usd_amount: Decimal::from_str("5333229342748").unwrap(), - }, - _ => BalanceAvailable { - amount: Uint128::zero(), - usd_amount: Decimal::zero(), - }, - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::MasterchefStableStakeApr { denom } => { - let resp = match denom.as_str() { - "uusdc" => QueryStableStakeAprResponse { - apr: Int128::zero(), - }, - "ueden" => QueryStableStakeAprResponse { - apr: Int128::zero(), - }, - _ => return Err(Error::new(StdError::not_found(denom))), - }; - Ok(to_json_binary(&resp)?) - } - - ElysQuery::MasterchefUserPendingReward { .. } => { - let resp = MasterchefUserPendingRewardResponse { - rewards: vec![MasterchefUserPendingRewardData { - pool_id: 32767u64, - reward: vec![Coin { - denom: "ueden".to_string(), - amount: Uint128::new(20), - }], - }], - total_rewards: vec![Coin { - denom: "ueden".to_string(), - amount: Uint128::new(20), - }], - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::AmmPriceByDenom { token_in, .. } => { - let spot_price = match token_in.denom.as_str() { - "uelys" => Decimal::from_str("3.5308010067676894").unwrap(), - "ueden" => Decimal::from_str("3.5308010067676894").unwrap(), - "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65" => { - Decimal::one() - } - "ibc/E2D2F6ADCC68AA3384B2F5DFACCA437923D137C14E86FB8A10207CF3BED0C8D4" => { - Decimal::from_str("9.02450744362719844").unwrap() - } - _ => return Err(Error::new(StdError::not_found(token_in.denom.as_str()))), - }; - Ok(to_json_binary(&spot_price)?) - } - ElysQuery::EstakingRewards { .. } => { - let resp = EstakingRewardsResponse { - rewards: vec![DelegationDelegatorReward { - validator_address: Validator::EdenBoost.to_string(), - reward: vec![Coin { - denom: "ueden".to_string(), - amount: Uint128::from_str("121").unwrap(), - }], - }], - total: vec![Coin { - denom: "uedenb".to_string(), - amount: Uint128::from_str("121").unwrap(), - }], - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::AmmEarnMiningPoolAll { .. } => { - let resp = QueryEarnPoolResponse { - pools: Some(vec![PoolResp { - pool_id: 1, - apr: Some(PoolApr { pool_id: 1, ..Default::default() }), - assets: vec![PoolAsset { - token: Coin { - denom: "uelys".to_string(), - amount: Uint128::new(100), - }, - weight: Uint128::new(1), - usd_value: Some(Decimal::from_str("353.08010067676894").unwrap()), - }], - pool_ratio: "".to_string(), - current_pool_ratio: None, - current_pool_ratio_string: None, - rewards_apr: Decimal::one(), - rewards_usd: Decimal::from_str("10").unwrap(), - reward_coins: vec![Coin { - denom: "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65".to_string(), - amount: Uint128::new(10), - }], - fiat_rewards: None, - borrow_apr: Decimal::from_str("2").unwrap(), - leverage_lp: Decimal::zero(), - perpetual: Decimal::zero(), - lp_token_price: None, - tvl: Decimal::zero(), - total_shares: Coin { denom: "uelys".to_string(), amount: Uint128::new(1000) }, - share_usd_price: Some(Decimal::from_str("3530.8010067676894").unwrap()), - swap_fee: Decimal::from_str("0.1").unwrap(), - fee_denom: "uelys".to_string(), - use_oracle: Some(true), - is_leveragelp: Some(true), - }]), - }; - Ok(to_json_binary(&resp)?) - } - - _ => self.0.query(api, storage, querier, block, request), - } - } - - fn execute( - &self, - api: &dyn cosmwasm_std::Api, - storage: &mut dyn cosmwasm_std::Storage, - router: &dyn cw_multi_test::CosmosRouter, - block: &cosmwasm_std::BlockInfo, - sender: Addr, - msg: Self::ExecT, - ) -> AnyResult - where - ExecC: std::fmt::Debug - + Clone - + PartialEq - + schemars::JsonSchema - + serde::de::DeserializeOwned - + 'static, - QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, - { - match msg { - _ => self.0.execute(api, storage, router, block, sender, msg), - } - } - - fn sudo( - &self, - _api: &dyn cosmwasm_std::Api, - _storage: &mut dyn cosmwasm_std::Storage, - _router: &dyn cw_multi_test::CosmosRouter, - _block: &cosmwasm_std::BlockInfo, - _msg: Self::SudoT, - ) -> AnyResult - where - ExecC: std::fmt::Debug - + Clone - + PartialEq - + schemars::JsonSchema - + serde::de::DeserializeOwned - + 'static, - QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, - { - bail!("sudo is not implemented for ElysModule") - } -} - -#[test] -fn get_all_pools() { - // Create a wallet for the "user" with an initial balance of 100 usdc - let wallet = vec![( - "user", - coins( - 100, - "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", - ), - )]; - - let mut addresses: Vec = vec![]; - let mut app = BasicAppBuilder::::new_custom() - .with_custom(ElysModuleWrapper(ElysModule {})) - .build(|roouter, _, storage| { - for (wallet_owner, wallet_contenent) in wallet { - roouter - .bank - .init_balance(storage, &Addr::unchecked(wallet_owner), wallet_contenent) - .unwrap(); - addresses.push(wallet_owner.to_owned()) - } - ACCOUNT.save(storage, &addresses).unwrap(); - PERPETUAL_OPENED_POSITION.save(storage, &vec![]).unwrap(); - ASSET_INFO.save(storage, &vec![]).unwrap(); - PRICES.save(storage, &vec![]).unwrap(); - LAST_MODULE_USED.save(storage, &None).unwrap(); - }); - - // trade shield deployment - let trade_shield_code = - ContractWrapper::new(trade_shield_execute, trade_shield_init, trade_shield_query); - let trade_shield_code_id = app.store_code(Box::new(trade_shield_code)); - let trade_shield_init = TradeShieldInstantiateMsg { - account_history_address: None, - }; - let trade_shield_address = app - .instantiate_contract( - trade_shield_code_id, - Addr::unchecked("owner"), - &trade_shield_init, - &[], - "Contract", - None, - ) - .unwrap() - .to_string(); - - // Create a contract wrapper and store its code. - let code = ContractWrapper::new(execute, instantiate, query).with_sudo(sudo); - let code_id = app.store_code(Box::new(code)); - - // Create a mock message to instantiate the contract with no initial orders. - let instantiate_msg = InstantiateMsg { - limit: Some(3), - expiration: Some(cw_utils::Expiration::AtTime(Timestamp::from_seconds( - 604800, - ))), - trade_shield_address: Some(trade_shield_address), - }; - - // Instantiate the contract with "owner" as the deployer. - let addr = app - .instantiate_contract( - code_id, - Addr::unchecked("owner"), - &instantiate_msg, - &[], - "Contract", - None, - ) - .unwrap(); - - let msg = app.wasm_sudo(addr.clone(), &SudoMsg::ClockEndBlock {}); - - println!("{:?}", msg); - - let resp: QueryEarnPoolResponse = app - .wrap() - .query_wasm_smart( - &addr, - &QueryMsg::GetLiquidityPools { - pool_ids: Some(vec![1u64]), - filter_type: PoolFilterType::FilterAll, - pagination: None, - }, - ) - .unwrap(); - - let mut current_pool_ratio = HashMap::new(); - current_pool_ratio.insert("uelys".to_string(), Decimal::one()); - - let mut current = HashMap::new(); - current.insert("uelys".to_string(), Decimal::one()); - - let expected = QueryEarnPoolResponse { - pools: Some( - [PoolResp { - pool_id: 1, - apr: Some(PoolApr { - pool_id: 1, - ..Default::default() - }), - assets: [PoolAsset { - token: coin(100, "uelys"), - weight: Uint128::one(), - usd_value: Some(Decimal::from_str("0.000353080100676768").unwrap()), - }] - .to_vec(), - pool_ratio: "".to_string(), - current_pool_ratio: Some(current), - current_pool_ratio_string: None, - rewards_apr: Decimal::one(), - rewards_usd: Decimal::from_str("10").unwrap(), - reward_coins: coins( - 10, - "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", - ) - .to_vec(), - fiat_rewards: Some( - [CoinValue { - denom: - "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65" - .to_string(), - amount_token: Decimal::from_str("0.00001").unwrap(), - price: Decimal::one(), - amount_usd: Decimal::from_str("0.00001").unwrap(), - }] - .to_vec(), - ), - borrow_apr: Decimal::from_str("2").unwrap(), - leverage_lp: Decimal::zero(), - perpetual: Decimal::zero(), - lp_token_price: None, - tvl: Decimal::zero(), - total_shares: coin(1000, "uelys"), - share_usd_price: Some(Decimal::zero()), - swap_fee: Decimal::from_str("0.1").unwrap(), - fee_denom: "uelys".to_string(), - use_oracle: Some(true), - is_leveragelp: Some(true), - }] - .to_vec(), - ), - }; - - assert_eq!(resp, expected); -} diff --git a/contracts/account-history-contract/src/tests/get_eden_boost_earn_program_details.rs b/contracts/account-history-contract/src/tests/get_eden_boost_earn_program_details.rs deleted file mode 100644 index b188c0f9..00000000 --- a/contracts/account-history-contract/src/tests/get_eden_boost_earn_program_details.rs +++ /dev/null @@ -1,295 +0,0 @@ -use std::str::FromStr; - -use crate::entry_point::instantiate; -use crate::{ - entry_point::{execute, query, sudo}, - msg::*, -}; -use anyhow::{bail, Error, Result as AnyResult}; -use cosmwasm_std::{ - coins, to_json_binary, Addr, Coin, Decimal, Empty, Int128, StdError, Timestamp, Uint128, -}; -use cw_multi_test::{AppResponse, BasicAppBuilder, ContractWrapper, Executor, Module}; -use elys_bindings::account_history::msg::query_resp::earn::GetEdenBoostEarnProgramResp; -use elys_bindings::account_history::types::earn_detail::earn_detail::AprEdenBoost; -use elys_bindings::account_history::types::earn_program::EdenBoostEarnProgram; -use elys_bindings::account_history::types::CoinValue; -use elys_bindings::query_resp::{ - BalanceBorrowed, DelegationDelegatorReward, EstakingRewardsResponse, - MasterchefUserPendingRewardData, MasterchefUserPendingRewardResponse, - QueryStableStakeAprResponse, StakedAvailable, Validator, -}; -use elys_bindings::types::BalanceAvailable; -use elys_bindings::{ElysMsg, ElysQuery}; -use elys_bindings_test::{ - ElysModule, ACCOUNT, ASSET_INFO, LAST_MODULE_USED, PERPETUAL_OPENED_POSITION, PRICES, -}; -use trade_shield_contract::entry_point::{ - execute as trade_shield_execute, instantiate as trade_shield_init, query as trade_shield_query, -}; -use trade_shield_contract::msg::InstantiateMsg as TradeShieldInstantiateMsg; - -struct ElysModuleWrapper(ElysModule); - -impl Module for ElysModuleWrapper { - type QueryT = ElysQuery; - type ExecT = ElysMsg; - type SudoT = Empty; - - fn query( - &self, - api: &dyn cosmwasm_std::Api, - storage: &dyn cosmwasm_std::Storage, - querier: &dyn cosmwasm_std::Querier, - block: &cosmwasm_std::BlockInfo, - request: Self::QueryT, - ) -> AnyResult { - match request { - ElysQuery::AmmBalance { address, denom } => { - let resp = match (address.as_str(), denom.as_str()) { - ("user", "uedenb") => BalanceAvailable { - amount: Uint128::new(21798000), - usd_amount: Decimal::from_str("21798000").unwrap(), - }, - ( - "user", - "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", - ) => BalanceAvailable { - amount: Uint128::new(5333229342748), - usd_amount: Decimal::from_str("5333229342748").unwrap(), - }, - _ => BalanceAvailable { - amount: Uint128::zero(), - usd_amount: Decimal::zero(), - }, - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::MasterchefStableStakeApr { denom } => { - let resp = match denom.as_str() { - "uusdc" => QueryStableStakeAprResponse { - apr: Int128::zero(), - }, - "ueden" => QueryStableStakeAprResponse { - apr: Int128::zero(), - }, - _ => return Err(Error::new(StdError::not_found(denom))), - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::CommitmentStakedBalanceOfDenom { .. } => { - let resp = StakedAvailable { - usd_amount: Decimal::from_atomics(Uint128::new(100130012), 3).unwrap(), - amount: Uint128::new(100120000000), - lockups: None, - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::StableStakeBalanceOfBorrow {} => { - let resp = BalanceBorrowed { - usd_amount: Decimal::from_atomics(Uint128::new(3265035180871), 10).unwrap(), - percentage: Decimal::from_atomics(Uint128::new(0000238391578776388), 18) - .unwrap(), - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::MasterchefUserPendingReward { .. } => { - let resp = MasterchefUserPendingRewardResponse { - rewards: vec![MasterchefUserPendingRewardData { - pool_id: 32767u64, - reward: vec![Coin { - denom: "ueden".to_string(), - amount: Uint128::new(20), - }], - }], - total_rewards: vec![Coin { - denom: "ueden".to_string(), - amount: Uint128::new(20), - }], - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::AmmPriceByDenom { token_in, .. } => { - let spot_price = match token_in.denom.as_str() { - "uelys" => Decimal::from_str("3.5308010067676894").unwrap(), - "ueden" => Decimal::from_str("3.5308010067676894").unwrap(), - "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65" => { - Decimal::one() - } - "ibc/E2D2F6ADCC68AA3384B2F5DFACCA437923D137C14E86FB8A10207CF3BED0C8D4" => { - Decimal::from_str("9.02450744362719844").unwrap() - } - _ => return Err(Error::new(StdError::not_found(token_in.denom.as_str()))), - }; - Ok(to_json_binary(&spot_price)?) - } - ElysQuery::EstakingRewards { .. } => { - let resp = EstakingRewardsResponse { - rewards: vec![DelegationDelegatorReward { - validator_address: Validator::EdenBoost.to_string(), - reward: vec![Coin { - denom: "ueden".to_string(), - amount: Uint128::from_str("121").unwrap(), - }], - }], - total: vec![Coin { - denom: "uedenb".to_string(), - amount: Uint128::from_str("121").unwrap(), - }], - }; - Ok(to_json_binary(&resp)?) - } - _ => self.0.query(api, storage, querier, block, request), - } - } - - fn execute( - &self, - api: &dyn cosmwasm_std::Api, - storage: &mut dyn cosmwasm_std::Storage, - router: &dyn cw_multi_test::CosmosRouter, - block: &cosmwasm_std::BlockInfo, - sender: Addr, - msg: Self::ExecT, - ) -> AnyResult - where - ExecC: std::fmt::Debug - + Clone - + PartialEq - + schemars::JsonSchema - + serde::de::DeserializeOwned - + 'static, - QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, - { - match msg { - _ => self.0.execute(api, storage, router, block, sender, msg), - } - } - - fn sudo( - &self, - _api: &dyn cosmwasm_std::Api, - _storage: &mut dyn cosmwasm_std::Storage, - _router: &dyn cw_multi_test::CosmosRouter, - _block: &cosmwasm_std::BlockInfo, - _msg: Self::SudoT, - ) -> AnyResult - where - ExecC: std::fmt::Debug - + Clone - + PartialEq - + schemars::JsonSchema - + serde::de::DeserializeOwned - + 'static, - QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, - { - bail!("sudo is not implemented for ElysModule") - } -} - -#[test] -fn get_eden_boost_earn_program_details() { - // Create a wallet for the "user" with an initial balance of 100 usdc - let wallet = vec![( - "user", - coins( - 100, - "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", - ), - )]; - - let mut addresses: Vec = vec![]; - let mut app = BasicAppBuilder::::new_custom() - .with_custom(ElysModuleWrapper(ElysModule {})) - .build(|roouter, _, storage| { - for (wallet_owner, wallet_contenent) in wallet { - roouter - .bank - .init_balance(storage, &Addr::unchecked(wallet_owner), wallet_contenent) - .unwrap(); - addresses.push(wallet_owner.to_owned()) - } - ACCOUNT.save(storage, &addresses).unwrap(); - PERPETUAL_OPENED_POSITION.save(storage, &vec![]).unwrap(); - ASSET_INFO.save(storage, &vec![]).unwrap(); - PRICES.save(storage, &vec![]).unwrap(); - LAST_MODULE_USED.save(storage, &None).unwrap(); - }); - - // trade shield deployment - let trade_shield_code = - ContractWrapper::new(trade_shield_execute, trade_shield_init, trade_shield_query); - let trade_shield_code_id = app.store_code(Box::new(trade_shield_code)); - let trade_shield_init = TradeShieldInstantiateMsg { - account_history_address: None, - }; - let trade_shield_address = app - .instantiate_contract( - trade_shield_code_id, - Addr::unchecked("owner"), - &trade_shield_init, - &[], - "Contract", - None, - ) - .unwrap() - .to_string(); - - // Create a contract wrapper and store its code. - let code = ContractWrapper::new(execute, instantiate, query).with_sudo(sudo); - let code_id = app.store_code(Box::new(code)); - - // Create a mock message to instantiate the contract with no initial orders. - let instantiate_msg = InstantiateMsg { - limit: Some(3), - expiration: Some(cw_utils::Expiration::AtTime(Timestamp::from_seconds( - 604800, - ))), - trade_shield_address: Some(trade_shield_address), - }; - - // Instantiate the contract with "owner" as the deployer. - let addr = app - .instantiate_contract( - code_id, - Addr::unchecked("owner"), - &instantiate_msg, - &[], - "Contract", - None, - ) - .unwrap(); - - app.wasm_sudo(addr.clone(), &SudoMsg::ClockEndBlock {}) - .unwrap(); - - let resp: GetEdenBoostEarnProgramResp = app - .wrap() - .query_wasm_smart( - &addr, - &QueryMsg::GetEdenBoostEarnProgramDetails { - address: "user".to_string(), - }, - ) - .unwrap(); - - let expected = GetEdenBoostEarnProgramResp { - data: EdenBoostEarnProgram { - bonding_period: 0, - apr: AprEdenBoost { - uusdc: Uint128::zero(), - ueden: Uint128::zero(), - }, - available: Some(Uint128::new(21798000)), - staked: Some(Uint128::new(100120000000)), - rewards: Some(vec![CoinValue { - denom: "ueden".to_string(), - amount_token: Decimal::from_str("0.000121").unwrap(), - price: Decimal::from_str("3.5308010067676894").unwrap(), - amount_usd: Decimal::from_str("0.00042722692181889").unwrap(), - }]), - }, - }; - - assert_eq!(resp, expected); -} diff --git a/contracts/account-history-contract/src/tests/get_eden_earn_program_details.rs b/contracts/account-history-contract/src/tests/get_eden_earn_program_details.rs deleted file mode 100644 index f521428f..00000000 --- a/contracts/account-history-contract/src/tests/get_eden_earn_program_details.rs +++ /dev/null @@ -1,308 +0,0 @@ -use std::str::FromStr; - -use crate::entry_point::instantiate; -use crate::{ - entry_point::{execute, query, sudo}, - msg::*, -}; -use anyhow::{bail, Error, Result as AnyResult}; -use cosmwasm_std::{ - coins, to_json_binary, Addr, Coin, Decimal, Empty, Int128, StdError, Timestamp, Uint128, -}; -use cw_multi_test::{AppResponse, BasicAppBuilder, ContractWrapper, Executor, Module}; -use elys_bindings::account_history::msg::query_resp::earn::GetEdenEarnProgramResp; -use elys_bindings::account_history::types::earn_program::EdenEarnProgram; -use elys_bindings::account_history::types::{AprElys, CoinValue}; -use elys_bindings::query_resp::{ - BalanceBorrowed, DelegationDelegatorReward, EstakingRewardsResponse, - MasterchefUserPendingRewardData, MasterchefUserPendingRewardResponse, - QueryStableStakeAprResponse, StakedAvailable, -}; -use elys_bindings::types::BalanceAvailable; -use elys_bindings::{ElysMsg, ElysQuery}; -use elys_bindings_test::{ - ElysModule, ACCOUNT, ASSET_INFO, LAST_MODULE_USED, PERPETUAL_OPENED_POSITION, PRICES, -}; -use trade_shield_contract::entry_point::{ - execute as trade_shield_execute, instantiate as trade_shield_init, query as trade_shield_query, -}; -use trade_shield_contract::msg::InstantiateMsg as TradeShieldInstantiateMsg; - -struct ElysModuleWrapper(ElysModule); - -impl Module for ElysModuleWrapper { - type QueryT = ElysQuery; - type ExecT = ElysMsg; - type SudoT = Empty; - - fn query( - &self, - api: &dyn cosmwasm_std::Api, - storage: &dyn cosmwasm_std::Storage, - querier: &dyn cosmwasm_std::Querier, - block: &cosmwasm_std::BlockInfo, - request: Self::QueryT, - ) -> AnyResult { - match request { - ElysQuery::AmmBalance { address, denom } => { - let resp = match (address.as_str(), denom.as_str()) { - ("user", "ueden") => BalanceAvailable { - amount: Uint128::new(21798000), - usd_amount: Decimal::from_str("21798000").unwrap(), - }, - ( - "user", - "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", - ) => BalanceAvailable { - amount: Uint128::new(5333229342748), - usd_amount: Decimal::from_str("5333229342748").unwrap(), - }, - _ => BalanceAvailable { - amount: Uint128::zero(), - usd_amount: Decimal::zero(), - }, - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::MasterchefStableStakeApr { denom } => { - let resp = match denom.as_str() { - "uusdc" => QueryStableStakeAprResponse { - apr: Int128::zero(), - }, - "ueden" => QueryStableStakeAprResponse { - apr: Int128::zero(), - }, - _ => return Err(Error::new(StdError::not_found(denom))), - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::CommitmentStakedBalanceOfDenom { .. } => { - let resp = StakedAvailable { - usd_amount: Decimal::from_atomics(Uint128::new(100130012), 3).unwrap(), - amount: Uint128::new(100120000000), - lockups: None, - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::StableStakeBalanceOfBorrow {} => { - let resp = BalanceBorrowed { - usd_amount: Decimal::from_atomics(Uint128::new(3265035180871), 10).unwrap(), - percentage: Decimal::from_atomics(Uint128::new(0000238391578776388), 18) - .unwrap(), - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::MasterchefUserPendingReward { .. } => { - let resp = MasterchefUserPendingRewardResponse { - rewards: vec![MasterchefUserPendingRewardData { - pool_id: 32767u64, - reward: vec![Coin { - denom: "ueden".to_string(), - amount: Uint128::new(20), - }], - }], - total_rewards: vec![Coin { - denom: "ueden".to_string(), - amount: Uint128::new(20), - }], - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::AmmPriceByDenom { token_in, .. } => { - let spot_price = match token_in.denom.as_str() { - "uelys" => Decimal::from_str("3.5308010067676894").unwrap(), - "ueden" => Decimal::from_str("3.5308010067676894").unwrap(), - "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65" => { - Decimal::one() - } - "ibc/E2D2F6ADCC68AA3384B2F5DFACCA437923D137C14E86FB8A10207CF3BED0C8D4" => { - Decimal::from_str("9.02450744362719844").unwrap() - } - _ => return Err(Error::new(StdError::not_found(token_in.denom.as_str()))), - }; - Ok(to_json_binary(&spot_price)?) - } - ElysQuery::EstakingRewards { .. } => { - let resp = EstakingRewardsResponse { - rewards: vec![DelegationDelegatorReward { - validator_address: "elysvaloper1gnmpr8vvslp3shcq6e922xr0uq4aa2w5gdzht0" - .to_string(), - reward: vec![Coin { - denom: "ueden".to_string(), - amount: Uint128::from_str("121").unwrap(), - }], - }], - total: vec![Coin { - denom: "ueden".to_string(), - amount: Uint128::from_str("121").unwrap(), - }], - }; - Ok(to_json_binary(&resp)?) - } - _ => self.0.query(api, storage, querier, block, request), - } - } - - fn execute( - &self, - api: &dyn cosmwasm_std::Api, - storage: &mut dyn cosmwasm_std::Storage, - router: &dyn cw_multi_test::CosmosRouter, - block: &cosmwasm_std::BlockInfo, - sender: Addr, - msg: Self::ExecT, - ) -> AnyResult - where - ExecC: std::fmt::Debug - + Clone - + PartialEq - + schemars::JsonSchema - + serde::de::DeserializeOwned - + 'static, - QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, - { - match msg { - _ => self.0.execute(api, storage, router, block, sender, msg), - } - } - - fn sudo( - &self, - _api: &dyn cosmwasm_std::Api, - _storage: &mut dyn cosmwasm_std::Storage, - _router: &dyn cw_multi_test::CosmosRouter, - _block: &cosmwasm_std::BlockInfo, - _msg: Self::SudoT, - ) -> AnyResult - where - ExecC: std::fmt::Debug - + Clone - + PartialEq - + schemars::JsonSchema - + serde::de::DeserializeOwned - + 'static, - QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, - { - bail!("sudo is not implemented for ElysModule") - } -} - -#[test] -fn get_eden_earn_program_details() { - // Create a wallet for the "user" with an initial balance of 100 usdc - let wallet = vec![( - "user", - coins( - 100, - "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", - ), - )]; - - let mut addresses: Vec = vec![]; - let mut app = BasicAppBuilder::::new_custom() - .with_custom(ElysModuleWrapper(ElysModule {})) - .build(|roouter, _, storage| { - for (wallet_owner, wallet_contenent) in wallet { - roouter - .bank - .init_balance(storage, &Addr::unchecked(wallet_owner), wallet_contenent) - .unwrap(); - addresses.push(wallet_owner.to_owned()) - } - ACCOUNT.save(storage, &addresses).unwrap(); - PERPETUAL_OPENED_POSITION.save(storage, &vec![]).unwrap(); - ASSET_INFO.save(storage, &vec![]).unwrap(); - PRICES.save(storage, &vec![]).unwrap(); - LAST_MODULE_USED.save(storage, &None).unwrap(); - }); - - // trade shield deployment - let trade_shield_code = - ContractWrapper::new(trade_shield_execute, trade_shield_init, trade_shield_query); - let trade_shield_code_id = app.store_code(Box::new(trade_shield_code)); - let trade_shield_init = TradeShieldInstantiateMsg { - account_history_address: None, - }; - let trade_shield_address = app - .instantiate_contract( - trade_shield_code_id, - Addr::unchecked("owner"), - &trade_shield_init, - &[], - "Contract", - None, - ) - .unwrap() - .to_string(); - - // Create a contract wrapper and store its code. - let code = ContractWrapper::new(execute, instantiate, query).with_sudo(sudo); - let code_id = app.store_code(Box::new(code)); - - // Create a mock message to instantiate the contract with no initial orders. - let instantiate_msg = InstantiateMsg { - limit: Some(3), - expiration: Some(cw_utils::Expiration::AtTime(Timestamp::from_seconds( - 604800, - ))), - trade_shield_address: Some(trade_shield_address), - }; - - // Instantiate the contract with "owner" as the deployer. - let addr = app - .instantiate_contract( - code_id, - Addr::unchecked("owner"), - &instantiate_msg, - &[], - "Contract", - None, - ) - .unwrap(); - - app.wasm_sudo(addr.clone(), &SudoMsg::ClockEndBlock {}) - .unwrap(); - - let resp: GetEdenEarnProgramResp = app - .wrap() - .query_wasm_smart( - &addr, - &QueryMsg::GetEdenEarnProgramDetails { - address: "user".to_string(), - }, - ) - .unwrap(); - - let expected = GetEdenEarnProgramResp { - data: EdenEarnProgram { - bonding_period: 0, - apr: AprElys { - uusdc: Uint128::zero(), - ueden: Uint128::zero(), - uedenb: Uint128::zero(), - }, - available: Some(BalanceAvailable { - amount: Uint128::new(21798000), - usd_amount: Decimal::from_str("76.964400345522093541").unwrap(), - }), - staked: Some(StakedAvailable { - usd_amount: Decimal::from_str("100130.012").unwrap(), - amount: Uint128::new(100120000000), - lockups: None, - }), - rewards: Some(vec![CoinValue { - denom: "ueden".to_string(), - amount_token: Decimal::from_str("0.000121").unwrap(), - price: Decimal::from_str("3.5308010067676894").unwrap(), - amount_usd: Decimal::from_str("0.00042722692181889").unwrap(), - }]), - vesting: BalanceAvailable { - amount: Uint128::from_str("100").unwrap(), - usd_amount: Decimal::from_str("100").unwrap(), - }, - vesting_details: Some(vec![]), - }, - }; - - assert_eq!(resp, expected); -} diff --git a/contracts/account-history-contract/src/tests/get_elys_earn_program_detail.rs b/contracts/account-history-contract/src/tests/get_elys_earn_program_detail.rs deleted file mode 100644 index 9fce1ac3..00000000 --- a/contracts/account-history-contract/src/tests/get_elys_earn_program_detail.rs +++ /dev/null @@ -1,304 +0,0 @@ -use std::str::FromStr; - -use crate::entry_point::instantiate; -use crate::{ - entry_point::{execute, query, sudo}, - msg::*, -}; -use anyhow::{bail, Error, Result as AnyResult}; -use cosmwasm_std::{ - coins, to_json_binary, Addr, Coin, Decimal, Empty, Int128, StdError, Timestamp, Uint128, -}; -use cw_multi_test::{AppResponse, BasicAppBuilder, ContractWrapper, Executor, Module}; -use elys_bindings::account_history::msg::query_resp::earn::GetElysEarnProgramResp; -use elys_bindings::account_history::types::earn_program::ElysEarnProgram; -use elys_bindings::account_history::types::{AprElys, CoinValue}; -use elys_bindings::query_resp::{ - BalanceBorrowed, DelegationDelegatorReward, EstakingRewardsResponse, - MasterchefUserPendingRewardData, MasterchefUserPendingRewardResponse, - QueryStableStakeAprResponse, StakedAvailable, -}; -use elys_bindings::types::BalanceAvailable; -use elys_bindings::{ElysMsg, ElysQuery}; -use elys_bindings_test::{ - ElysModule, ACCOUNT, ASSET_INFO, LAST_MODULE_USED, PERPETUAL_OPENED_POSITION, PRICES, -}; -use trade_shield_contract::entry_point::{ - execute as trade_shield_execute, instantiate as trade_shield_init, query as trade_shield_query, -}; -use trade_shield_contract::msg::InstantiateMsg as TradeShieldInstantiateMsg; - -struct ElysModuleWrapper(ElysModule); - -impl Module for ElysModuleWrapper { - type QueryT = ElysQuery; - type ExecT = ElysMsg; - type SudoT = Empty; - - fn query( - &self, - api: &dyn cosmwasm_std::Api, - storage: &dyn cosmwasm_std::Storage, - querier: &dyn cosmwasm_std::Querier, - block: &cosmwasm_std::BlockInfo, - request: Self::QueryT, - ) -> AnyResult { - match request { - ElysQuery::AmmBalance { address, denom } => { - let resp = match (address.as_str(), denom.as_str()) { - ( - "user", - "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", - ) => BalanceAvailable { - amount: Uint128::new(1234), - usd_amount: Decimal::from_str("1234").unwrap(), - }, - ("user", "uelys") => BalanceAvailable { - amount: Uint128::new(45666543), - usd_amount: Decimal::from_str("45666543").unwrap(), - }, - _ => BalanceAvailable { - amount: Uint128::zero(), - usd_amount: Decimal::zero(), - }, - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::MasterchefStableStakeApr { denom } => { - let resp = match denom.as_str() { - "uusdc" => QueryStableStakeAprResponse { - apr: Int128::zero(), - }, - "ueden" => QueryStableStakeAprResponse { - apr: Int128::zero(), - }, - _ => return Err(Error::new(StdError::not_found(denom))), - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::CommitmentStakedBalanceOfDenom { .. } => { - let resp = StakedAvailable { - usd_amount: Decimal::from_atomics(Uint128::new(100130012), 3).unwrap(), - amount: Uint128::new(100120000000), - lockups: None, - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::StableStakeBalanceOfBorrow {} => { - let resp = BalanceBorrowed { - usd_amount: Decimal::from_atomics(Uint128::new(3265035180871), 10).unwrap(), - percentage: Decimal::from_atomics(Uint128::new(0000238391578776388), 18) - .unwrap(), - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::MasterchefUserPendingReward { .. } => { - let resp = MasterchefUserPendingRewardResponse { - rewards: vec![MasterchefUserPendingRewardData { - pool_id: 32767u64, - reward: vec![Coin { - denom: "ueden".to_string(), - amount: Uint128::new(20), - }], - }], - total_rewards: vec![Coin { - denom: "ueden".to_string(), - amount: Uint128::new(20), - }], - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::AmmPriceByDenom { token_in, .. } => { - let spot_price = match token_in.denom.as_str() { - "uelys" => Decimal::from_str("3.5308010067676894").unwrap(), - "ueden" => Decimal::from_str("3.5308010067676894").unwrap(), - "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65" => { - Decimal::one() - } - "ibc/E2D2F6ADCC68AA3384B2F5DFACCA437923D137C14E86FB8A10207CF3BED0C8D4" => { - Decimal::from_str("9.02450744362719844").unwrap() - } - _ => return Err(Error::new(StdError::not_found(token_in.denom.as_str()))), - }; - Ok(to_json_binary(&spot_price)?) - } - ElysQuery::EstakingRewards { .. } => { - let resp = EstakingRewardsResponse { - rewards: vec![DelegationDelegatorReward { - validator_address: "validator".to_string(), - reward: vec![Coin { - denom: "ueden".to_string(), - amount: Uint128::from_str("121").unwrap(), - }], - }], - total: vec![Coin { - denom: "ueden".to_string(), - amount: Uint128::from_str("121").unwrap(), - }], - }; - Ok(to_json_binary(&resp)?) - } - _ => self.0.query(api, storage, querier, block, request), - } - } - - fn execute( - &self, - api: &dyn cosmwasm_std::Api, - storage: &mut dyn cosmwasm_std::Storage, - router: &dyn cw_multi_test::CosmosRouter, - block: &cosmwasm_std::BlockInfo, - sender: Addr, - msg: Self::ExecT, - ) -> AnyResult - where - ExecC: std::fmt::Debug - + Clone - + PartialEq - + schemars::JsonSchema - + serde::de::DeserializeOwned - + 'static, - QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, - { - match msg { - _ => self.0.execute(api, storage, router, block, sender, msg), - } - } - - fn sudo( - &self, - _api: &dyn cosmwasm_std::Api, - _storage: &mut dyn cosmwasm_std::Storage, - _router: &dyn cw_multi_test::CosmosRouter, - _block: &cosmwasm_std::BlockInfo, - _msg: Self::SudoT, - ) -> AnyResult - where - ExecC: std::fmt::Debug - + Clone - + PartialEq - + schemars::JsonSchema - + serde::de::DeserializeOwned - + 'static, - QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, - { - bail!("sudo is not implemented for ElysModule") - } -} - -#[test] -fn get_elys_earn_program_details() { - // Create a wallet for the "user" with an initial balance of 100 usdc - let wallet = vec![( - "user", - coins( - 100, - "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", - ), - )]; - - let mut addresses: Vec = vec![]; - let mut app = BasicAppBuilder::::new_custom() - .with_custom(ElysModuleWrapper(ElysModule {})) - .build(|roouter, _, storage| { - for (wallet_owner, wallet_contenent) in wallet { - roouter - .bank - .init_balance(storage, &Addr::unchecked(wallet_owner), wallet_contenent) - .unwrap(); - addresses.push(wallet_owner.to_owned()) - } - ACCOUNT.save(storage, &addresses).unwrap(); - PERPETUAL_OPENED_POSITION.save(storage, &vec![]).unwrap(); - ASSET_INFO.save(storage, &vec![]).unwrap(); - PRICES.save(storage, &vec![]).unwrap(); - LAST_MODULE_USED.save(storage, &None).unwrap(); - }); - - // trade shield deployment - let trade_shield_code = - ContractWrapper::new(trade_shield_execute, trade_shield_init, trade_shield_query); - let trade_shield_code_id = app.store_code(Box::new(trade_shield_code)); - let trade_shield_init = TradeShieldInstantiateMsg { - account_history_address: None, - }; - let trade_shield_address = app - .instantiate_contract( - trade_shield_code_id, - Addr::unchecked("owner"), - &trade_shield_init, - &[], - "Contract", - None, - ) - .unwrap() - .to_string(); - - // Create a contract wrapper and store its code. - let code = ContractWrapper::new(execute, instantiate, query).with_sudo(sudo); - let code_id = app.store_code(Box::new(code)); - - // Create a mock message to instantiate the contract with no initial orders. - let instantiate_msg = InstantiateMsg { - limit: Some(3), - expiration: Some(cw_utils::Expiration::AtTime(Timestamp::from_seconds( - 604800, - ))), - trade_shield_address: Some(trade_shield_address), - }; - - // Instantiate the contract with "owner" as the deployer. - let addr = app - .instantiate_contract( - code_id, - Addr::unchecked("owner"), - &instantiate_msg, - &[], - "Contract", - None, - ) - .unwrap(); - - app.wasm_sudo(addr.clone(), &SudoMsg::ClockEndBlock {}) - .unwrap(); - - let resp: GetElysEarnProgramResp = app - .wrap() - .query_wasm_smart( - &addr, - &QueryMsg::GetElysEarnProgramDetails { - address: "user".to_string(), - }, - ) - .unwrap(); - - let expected = GetElysEarnProgramResp { - data: ElysEarnProgram { - bonding_period: 14, - apr: AprElys { - uusdc: Uint128::zero(), - ueden: Uint128::zero(), - uedenb: Uint128::zero(), - }, - available: Some(BalanceAvailable { - amount: Uint128::new(45666543), - usd_amount: Decimal::from_str("161.239475999999978995").unwrap(), - }), - staked: Some(StakedAvailable { - usd_amount: Decimal::from_atomics(Uint128::new(100130012), 3).unwrap(), - amount: Uint128::new(100120000000), - lockups: None, - }), - rewards: Some(vec![CoinValue { - denom: "ueden".to_string(), - amount_token: Decimal::from_str("0.000121").unwrap(), - price: Decimal::from_str("3.5308010067676894").unwrap(), - amount_usd: Decimal::from_str("0.00042722692181889").unwrap(), - }]), - staked_positions: None, - unstaked_positions: None, - }, - }; - - assert_eq!(resp, expected); -} diff --git a/contracts/account-history-contract/src/tests/get_liquid_assets.rs b/contracts/account-history-contract/src/tests/get_liquid_assets.rs deleted file mode 100644 index 8ec7fb13..00000000 --- a/contracts/account-history-contract/src/tests/get_liquid_assets.rs +++ /dev/null @@ -1,541 +0,0 @@ -use std::str::FromStr; - -use crate::entry_point::instantiate; -use crate::tests::get_liquid_assets::query_resp::{GetLiquidAssetsResp, LiquidAsset}; -use crate::{ - entry_point::{execute, query, sudo}, - msg::*, -}; -use anyhow::{bail, Error, Result as AnyResult}; -use cosmwasm_std::{ - coin, to_json_binary, Addr, Coin, DecCoin, Decimal, Decimal256, Empty, Int128, SignedDecimal, - StdError, Timestamp, Uint128, -}; -use cw_multi_test::{AppResponse, BasicAppBuilder, ContractWrapper, Executor, Module}; -use elys_bindings::query_resp::{ - AmmSwapEstimationByDenomResponse, DelegationDelegatorReward, Entry, EstakingRewardsResponse, - MasterchefUserPendingRewardData, MasterchefUserPendingRewardResponse, OracleAssetInfoResponse, - QueryGetEntryResponse, QueryGetPriceResponse, QueryStableStakeAprResponse, Validator, -}; -use elys_bindings::types::{BalanceAvailable, OracleAssetInfo, Price}; -use elys_bindings::{ElysMsg, ElysQuery}; -use elys_bindings_test::{ - ElysModule, ACCOUNT, ASSET_INFO, LAST_MODULE_USED, PERPETUAL_OPENED_POSITION, PRICES, -}; -use trade_shield_contract::entry_point::{ - execute as trade_shield_execute, instantiate as trade_shield_init, query as trade_shield_query, -}; -use trade_shield_contract::msg::InstantiateMsg as TradeShieldInstantiateMsg; - -struct ElysModuleWrapper(ElysModule); - -impl Module for ElysModuleWrapper { - type QueryT = ElysQuery; - type ExecT = ElysMsg; - type SudoT = Empty; - - fn query( - &self, - api: &dyn cosmwasm_std::Api, - storage: &dyn cosmwasm_std::Storage, - querier: &dyn cosmwasm_std::Querier, - block: &cosmwasm_std::BlockInfo, - request: Self::QueryT, - ) -> AnyResult { - match request { - ElysQuery::AmmBalance { .. } => { - let resp = BalanceAvailable { - amount: Uint128::new(0), - usd_amount: Decimal::zero(), - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::AssetProfileEntry { base_denom } => { - let resp = match base_denom.as_str() { - "uusdc" => QueryGetEntryResponse { - entry: Entry { - address: "".to_string(), - authority: "elys10d07y265gmmuvt4z0w9aw880jnsr700j6z2zm3".to_string(), - base_denom: "uusdc".to_string(), - commit_enabled: true, - decimals: 6, - denom: "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65".to_string(), - display_name: "USDC".to_string(), - display_symbol: "uUSDC".to_string(), - external_symbol: "uUSDC".to_string(), - ibc_channel_id: "channel-12".to_string(), - ibc_counterparty_chain_id: "".to_string(), - ibc_counterparty_channel_id: "channel-19".to_string(), - ibc_counterparty_denom: "".to_string(), - network: "".to_string(), - path: "transfer/channel-12".to_string(), - permissions: vec![], - transfer_limit: "".to_string(), - unit_denom: "uusdc".to_string(), - withdraw_enabled: true, - }, - }, - "ueden" => QueryGetEntryResponse { - entry: Entry { - address: "".to_string(), - authority: "elys10d07y265gmmuvt4z0w9aw880jnsr700j6z2zm3".to_string(), - base_denom: "ueden".to_string(), - commit_enabled: true, - decimals: 6, - denom: "ueden".to_string(), - display_name: "EDEN".to_string(), - display_symbol: "".to_string(), - external_symbol: "".to_string(), - ibc_channel_id: "".to_string(), - ibc_counterparty_chain_id: "".to_string(), - ibc_counterparty_channel_id: "".to_string(), - ibc_counterparty_denom: "".to_string(), - network: "".to_string(), - path: "".to_string(), - permissions: vec![], - transfer_limit: "".to_string(), - unit_denom: "".to_string(), - withdraw_enabled: true, - }, - }, - "uelys" => QueryGetEntryResponse { - entry: Entry { - address: "".to_string(), - authority: "elys10d07y265gmmuvt4z0w9aw880jnsr700j6z2zm3".to_string(), - base_denom: "uelys".to_string(), - commit_enabled: true, - decimals: 6, - denom: "uelys".to_string(), - display_name: "ELYS".to_string(), - display_symbol: "".to_string(), - external_symbol: "".to_string(), - ibc_channel_id: "".to_string(), - ibc_counterparty_chain_id: "".to_string(), - ibc_counterparty_channel_id: "".to_string(), - ibc_counterparty_denom: "".to_string(), - network: "".to_string(), - path: "".to_string(), - permissions: vec![], - transfer_limit: "".to_string(), - unit_denom: "".to_string(), - withdraw_enabled: true, - }, - }, - _ => return Err(Error::new(StdError::not_found(base_denom))), - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::AmmPriceByDenom { token_in, .. } => { - let spot_price = match token_in.denom.as_str() { - "uelys" => Decimal::from_str("3.5308010067676894").unwrap(), - "ueden" => Decimal::from_str("3.5308010067676894").unwrap(), - "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65" => { - Decimal::one() - } - "ibc/E2D2F6ADCC68AA3384B2F5DFACCA437923D137C14E86FB8A10207CF3BED0C8D4" => { - Decimal::from_str("9.02450744362719844").unwrap() - } - _ => return Err(Error::new(StdError::not_found(token_in.denom.as_str()))), - }; - Ok(to_json_binary(&spot_price)?) - } - ElysQuery::OraclePrice { asset, .. } => { - let resp = match asset.as_str() { - "USDC" => QueryGetPriceResponse { - price: Price { - asset: "USDC".to_string(), - price: Decimal::one(), - source: "uelys".to_string(), - provider: "elys1wzm8dvpxpxxf26y4xn85w5adakcenprg4cq2uf".to_string(), - // set timestamp to now - timestamp: block.time.seconds(), - block_height: block.height, - }, - }, - _ => return Err(Error::new(StdError::not_found(asset))), - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::OracleAssetInfo { denom } => { - let resp = match denom.as_str() { - "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65" => { - OracleAssetInfoResponse { - asset_info: OracleAssetInfo { - band_ticker: "USDC".to_string(), - decimal: 6, - denom: "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65".to_string(), - display: "USDC".to_string(), - elys_ticker: "USDC".to_string(), - }, - } - } - "ibc/47BD209179859CDE4A2806763D7189B6E6FE13A17880FE2B42DE1E6C1E329E23" => { - OracleAssetInfoResponse { - asset_info: OracleAssetInfo { - band_ticker: "OSMO".to_string(), - decimal: 6, - denom: "ibc/47BD209179859CDE4A2806763D7189B6E6FE13A17880FE2B42DE1E6C1E329E23".to_string(), - display: "OSMO".to_string(), - elys_ticker: "OSMO".to_string(), - }, - } - } - "ibc/977D5388D2FBE72D9A33FE2423BF8F4DADF3B591207CC98A295B9ACF81E4DE40" => { - OracleAssetInfoResponse { - asset_info: OracleAssetInfo { - band_ticker: "JUNO".to_string(), - decimal: 6, - denom: "ibc/977D5388D2FBE72D9A33FE2423BF8F4DADF3B591207CC98A295B9ACF81E4DE40".to_string(), - display: "JUNO".to_string(), - elys_ticker: "JUNO".to_string(), - }, - } - } - "ibc/E2D2F6ADCC68AA3384B2F5DFACCA437923D137C14E86FB8A10207CF3BED0C8D4" => { - OracleAssetInfoResponse { - asset_info: OracleAssetInfo { - band_ticker: "ATOM".to_string(), - decimal: 6, - denom: "ibc/E2D2F6ADCC68AA3384B2F5DFACCA437923D137C14E86FB8A10207CF3BED0C8D4".to_string(), - display: "ATOM".to_string(), - elys_ticker: "ATOM".to_string(), - }, - } - }, - _ => return Err(Error::new(StdError::not_found(denom))), - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::EstakingRewards { .. } => { - let resp = EstakingRewardsResponse { - rewards: vec![DelegationDelegatorReward { - validator_address: Validator::EdenBoost.to_string(), - reward: vec![Coin { - denom: "ueden".to_string(), - amount: Uint128::from_str("121").unwrap(), - }], - }], - total: vec![Coin { - denom: "uedenb".to_string(), - amount: Uint128::from_str("121").unwrap(), - }], - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::MasterchefStableStakeApr { .. } => { - let resp = QueryStableStakeAprResponse { - apr: Int128::new(12), - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::AmmSwapEstimationByDenom { .. } => { - let resp = AmmSwapEstimationByDenomResponse { - in_route: None, - out_route: None, - spot_price: Decimal::from_str("3.5").unwrap(), - amount: Coin { - denom: - "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65" - .to_string(), - amount: Uint128::new(100), - }, - swap_fee: SignedDecimal::one(), - discount: SignedDecimal::from_str("20").unwrap(), - available_liquidity: Coin { - denom: "uelys".to_string(), - amount: Uint128::new(100000), - }, - weight_balance_ratio: SignedDecimal::one(), - price_impact: SignedDecimal::zero(), - slippage: Decimal::zero(), - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::MasterchefUserPendingReward { .. } => { - let resp = MasterchefUserPendingRewardResponse { - rewards: vec![MasterchefUserPendingRewardData { - pool_id: 32767u64, - reward: vec![Coin { - denom: "ueden".to_string(), - amount: Uint128::new(20), - }], - }], - total_rewards: vec![Coin { - denom: "ueden".to_string(), - amount: Uint128::new(20), - }], - }; - Ok(to_json_binary(&resp)?) - } - _ => self.0.query(api, storage, querier, block, request), - } - } - - fn execute( - &self, - api: &dyn cosmwasm_std::Api, - storage: &mut dyn cosmwasm_std::Storage, - router: &dyn cw_multi_test::CosmosRouter, - block: &cosmwasm_std::BlockInfo, - sender: Addr, - msg: Self::ExecT, - ) -> AnyResult - where - ExecC: std::fmt::Debug - + Clone - + PartialEq - + schemars::JsonSchema - + serde::de::DeserializeOwned - + 'static, - QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, - { - match msg { - _ => self.0.execute(api, storage, router, block, sender, msg), - } - } - - fn sudo( - &self, - _api: &dyn cosmwasm_std::Api, - _storage: &mut dyn cosmwasm_std::Storage, - _router: &dyn cw_multi_test::CosmosRouter, - _block: &cosmwasm_std::BlockInfo, - _msg: Self::SudoT, - ) -> AnyResult - where - ExecC: std::fmt::Debug - + Clone - + PartialEq - + schemars::JsonSchema - + serde::de::DeserializeOwned - + 'static, - QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, - { - bail!("sudo is not implemented for ElysModule") - } -} - -#[test] -fn get_liquid_assets() { - // Create a wallet for the "user" with an initial balance of 100 usdc - let wallet = vec![( - "user", - vec![ - coin( - 21798000, - "ibc/0E1517E2771CA7C03F2ED3F9BAECCAEADF0BFD79B89679E834933BC0F179AD98", - ), - coin( - 5333229342748, - "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", - ), - coin( - 2704998, - "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", - ), - coin( - 594000000000200000, - "ibc/2FBCFC209420E6CECED6EE0BC599E74349759352CE953E27A6871BB3D84BC058", - ), - coin( - 1085352, - "ibc/326A89923D85047E6418A671FBACCAFA2686B01A16ED4A0AD92954FCE1485910", - ), - coin( - 168400000000000000, - "ibc/43881AB3B3D05FD9D3606D7F57CBE6EEEA89D18AC66AF9E2915ED43940E71CFD", - ), - coin( - 49765000, - "ibc/4DAE26570FD24ABA40E2BE4137E39D946C78B00B248D3F78B0919567C4371156", - ), - coin( - 9100000, - "ibc/977D5388D2FBE72D9A33FE2423BF8F4DADF3B591207CC98A295B9ACF81E4DE40", - ), - coin( - 141000000000000000, - "ibc/E059CD828E5009D4CF03C4494BEA73749250287FC98DD46E19F9016B918BF49D", - ), - coin( - 37403942, - "ibc/E2D2F6ADCC68AA3384B2F5DFACCA437923D137C14E86FB8A10207CF3BED0C8D4", - ), - coin( - 79979999999749000, - "ibc/FB22E35236996F6B0B1C9D407E8A379A7B1F4083F1960907A1622F022AE450E1", - ), - coin(45666543, "uelys"), - coin(45666543, "ueden"), - ], - )]; - - let mut addresses: Vec = vec![]; - let mut app = BasicAppBuilder::::new_custom() - .with_custom(ElysModuleWrapper(ElysModule {})) - .build(|roouter, _, storage| { - for (wallet_owner, wallet_contenent) in wallet { - roouter - .bank - .init_balance(storage, &Addr::unchecked(wallet_owner), wallet_contenent) - .unwrap(); - addresses.push(wallet_owner.to_owned()) - } - ACCOUNT.save(storage, &addresses).unwrap(); - PERPETUAL_OPENED_POSITION.save(storage, &vec![]).unwrap(); - ASSET_INFO.save(storage, &vec![]).unwrap(); - PRICES.save(storage, &vec![]).unwrap(); - LAST_MODULE_USED.save(storage, &None).unwrap(); - }); - - // trade shield deployment - let trade_shield_code = - ContractWrapper::new(trade_shield_execute, trade_shield_init, trade_shield_query); - let trade_shield_code_id = app.store_code(Box::new(trade_shield_code)); - let trade_shield_init = TradeShieldInstantiateMsg { - account_history_address: None, - }; - let trade_shield_address = app - .instantiate_contract( - trade_shield_code_id, - Addr::unchecked("owner"), - &trade_shield_init, - &[], - "Contract", - None, - ) - .unwrap() - .to_string(); - - // Create a contract wrapper and store its code. - let code = ContractWrapper::new(execute, instantiate, query).with_sudo(sudo); - let code_id = app.store_code(Box::new(code)); - - // Create a mock message to instantiate the contract with no initial orders. - let instantiate_msg = InstantiateMsg { - limit: Some(3), - expiration: Some(cw_utils::Expiration::AtTime(Timestamp::from_seconds( - 604800, - ))), - trade_shield_address: Some(trade_shield_address), - }; - - // Instantiate the contract with "owner" as the deployer. - let addr = app - .instantiate_contract( - code_id, - Addr::unchecked("owner"), - &instantiate_msg, - &[], - "Contract", - None, - ) - .unwrap(); - - app.wasm_sudo(addr.clone(), &SudoMsg::ClockEndBlock {}) - .unwrap(); - - // Query the contract for the existing order. - let resp: GetLiquidAssetsResp = app - .wrap() - .query_wasm_smart( - &addr, - &QueryMsg::GetLiquidAssets { - user_address: "user".to_string(), - }, - ) - .unwrap(); - - let mut expected: GetLiquidAssetsResp = GetLiquidAssetsResp { - liquid_assets: vec![ - LiquidAsset { - denom: "uelys".to_string(), - price: Decimal::from_str("3.5308010067676894").unwrap(), - available_amount: Decimal::from_str("45.666543").unwrap(), - available_value: Decimal::from_str("161.239475999999978995").unwrap(), - in_order_amount: Decimal::zero(), - in_order_value: Decimal::zero(), - total_amount: Decimal::from_str("45.666543").unwrap(), - total_value: Decimal::from_str("161.239475999999978995").unwrap(), - }, - LiquidAsset { - denom: "ueden".to_string(), - price: Decimal::from_str("3.5308010067676894").unwrap(), - available_amount: Decimal::from_str("45.666543").unwrap(), - available_value: Decimal::from_str("161.239475999999978995").unwrap(), - in_order_amount: Decimal::zero(), - in_order_value: Decimal::zero(), - total_amount: Decimal::from_str("45.666543").unwrap(), - total_value: Decimal::from_str("161.239475999999978995").unwrap(), - }, - LiquidAsset { - denom: "ibc/E2D2F6ADCC68AA3384B2F5DFACCA437923D137C14E86FB8A10207CF3BED0C8D4" - .to_string(), - price: Decimal::from_str("9.02450744362719844").unwrap(), - available_amount: Decimal::from_str("37.403942").unwrap(), - available_value: Decimal::from_str("337.552153000000000072").unwrap(), - in_order_amount: Decimal::zero(), - in_order_value: Decimal::zero(), - total_amount: Decimal::from_str("37.403942").unwrap(), - total_value: Decimal::from_str("337.552153000000000072").unwrap(), - }, - LiquidAsset { - denom: "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65" - .to_string(), - price: Decimal::one(), - available_amount: Decimal::from_str("5333229.342748").unwrap(), - available_value: Decimal::from_str("5333229.342748").unwrap(), - in_order_amount: Decimal::zero(), - in_order_value: Decimal::zero(), - total_amount: Decimal::from_str("5333229.342748").unwrap(), - total_value: Decimal::from_str("5333229.342748").unwrap(), - }, - ], - total_liquid_asset_balance: DecCoin::new( - Decimal256::zero(), - "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", - ), - }; - - for i in expected.liquid_assets.iter() { - expected.total_liquid_asset_balance.amount += Decimal256::from(i.total_value.clone()); - } - - // test if the response is the same as the expected - assert_eq!(resp.liquid_assets.len(), expected.liquid_assets.len()); - - assert_eq!( - resp.liquid_assets - .iter() - .find(|l| l.denom.as_str() == "uelys") - .cloned(), - Some(expected.liquid_assets[0].clone()) - ); - assert_eq!( - resp.liquid_assets - .iter() - .find(|l| l.denom.as_str() == "ueden") - .cloned(), - Some(expected.liquid_assets[1].clone()) - ); - assert_eq!( - resp.liquid_assets - .iter() - .find(|l| l.denom.as_str() - == "ibc/E2D2F6ADCC68AA3384B2F5DFACCA437923D137C14E86FB8A10207CF3BED0C8D4") - .cloned(), - Some(expected.liquid_assets[2].clone()) - ); - assert_eq!( - resp.liquid_assets - .iter() - .find(|l| l.denom.as_str() - == "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65") - .cloned(), - Some(expected.liquid_assets[3].clone()) - ); - assert_eq!( - resp.total_liquid_asset_balance, - expected.total_liquid_asset_balance - ); -} diff --git a/contracts/account-history-contract/src/tests/get_rewards.rs b/contracts/account-history-contract/src/tests/get_rewards.rs deleted file mode 100644 index b5cba5f1..00000000 --- a/contracts/account-history-contract/src/tests/get_rewards.rs +++ /dev/null @@ -1,287 +0,0 @@ -use std::str::FromStr; - -use crate::entry_point::instantiate; -use crate::{ - entry_point::{execute, query, sudo}, - msg::*, -}; -use anyhow::{bail, Error, Result as AnyResult}; -use cosmwasm_std::{ - coins, to_json_binary, Addr, Coin, Decimal, Empty, Int128, StdError, Timestamp, Uint128, -}; -use cw_multi_test::{AppResponse, BasicAppBuilder, ContractWrapper, Executor, Module}; -use elys_bindings::account_history::msg::query_resp::GetRewardsResp; -use elys_bindings::account_history::types::{CoinValue, Reward}; -use elys_bindings::query_resp::{ - DelegationDelegatorReward, EstakingRewardsResponse, MasterchefUserPendingRewardData, - MasterchefUserPendingRewardResponse, QueryStableStakeAprResponse, Validator, -}; -use elys_bindings::types::BalanceAvailable; -use elys_bindings::{ElysMsg, ElysQuery}; -use elys_bindings_test::{ - ElysModule, ACCOUNT, ASSET_INFO, LAST_MODULE_USED, PERPETUAL_OPENED_POSITION, PRICES, -}; -use trade_shield_contract::entry_point::{ - execute as trade_shield_execute, instantiate as trade_shield_init, query as trade_shield_query, -}; -use trade_shield_contract::msg::InstantiateMsg as TradeShieldInstantiateMsg; - -struct ElysModuleWrapper(ElysModule); - -impl Module for ElysModuleWrapper { - type QueryT = ElysQuery; - type ExecT = ElysMsg; - type SudoT = Empty; - - fn query( - &self, - api: &dyn cosmwasm_std::Api, - storage: &dyn cosmwasm_std::Storage, - querier: &dyn cosmwasm_std::Querier, - block: &cosmwasm_std::BlockInfo, - request: Self::QueryT, - ) -> AnyResult { - match request { - ElysQuery::AmmBalance { address, denom } => { - let resp = match (address.as_str(), denom.as_str()) { - ("user", "uedenb") => BalanceAvailable { - amount: Uint128::new(21798000), - usd_amount: Decimal::from_str("21798000").unwrap(), - }, - ( - "user", - "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", - ) => BalanceAvailable { - amount: Uint128::new(5333229342748), - usd_amount: Decimal::from_str("5333229342748").unwrap(), - }, - _ => BalanceAvailable { - amount: Uint128::zero(), - usd_amount: Decimal::zero(), - }, - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::MasterchefStableStakeApr { denom } => { - let resp = match denom.as_str() { - "uusdc" => QueryStableStakeAprResponse { - apr: Int128::zero(), - }, - "ueden" => QueryStableStakeAprResponse { - apr: Int128::zero(), - }, - _ => return Err(Error::new(StdError::not_found(denom))), - }; - Ok(to_json_binary(&resp)?) - } - - ElysQuery::MasterchefUserPendingReward { .. } => { - let resp = MasterchefUserPendingRewardResponse { - rewards: vec![MasterchefUserPendingRewardData { - pool_id: 32767u64, - reward: vec![Coin { - denom: "ueden".to_string(), - amount: Uint128::new(20), - }], - }], - total_rewards: vec![Coin { - denom: "ueden".to_string(), - amount: Uint128::new(20), - }], - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::AmmPriceByDenom { token_in, .. } => { - let spot_price = match token_in.denom.as_str() { - "uelys" => Decimal::from_str("3.5308010067676894").unwrap(), - "ueden" => Decimal::from_str("3.5308010067676894").unwrap(), - "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65" => { - Decimal::one() - } - "ibc/E2D2F6ADCC68AA3384B2F5DFACCA437923D137C14E86FB8A10207CF3BED0C8D4" => { - Decimal::from_str("9.02450744362719844").unwrap() - } - _ => return Err(Error::new(StdError::not_found(token_in.denom.as_str()))), - }; - Ok(to_json_binary(&spot_price)?) - } - ElysQuery::EstakingRewards { .. } => { - let resp = EstakingRewardsResponse { - rewards: vec![DelegationDelegatorReward { - validator_address: Validator::EdenBoost.to_string(), - reward: vec![Coin { - denom: "ueden".to_string(), - amount: Uint128::from_str("121").unwrap(), - }], - }], - total: vec![Coin { - denom: "uedenb".to_string(), - amount: Uint128::from_str("121").unwrap(), - }], - }; - Ok(to_json_binary(&resp)?) - } - _ => self.0.query(api, storage, querier, block, request), - } - } - - fn execute( - &self, - api: &dyn cosmwasm_std::Api, - storage: &mut dyn cosmwasm_std::Storage, - router: &dyn cw_multi_test::CosmosRouter, - block: &cosmwasm_std::BlockInfo, - sender: Addr, - msg: Self::ExecT, - ) -> AnyResult - where - ExecC: std::fmt::Debug - + Clone - + PartialEq - + schemars::JsonSchema - + serde::de::DeserializeOwned - + 'static, - QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, - { - match msg { - _ => self.0.execute(api, storage, router, block, sender, msg), - } - } - - fn sudo( - &self, - _api: &dyn cosmwasm_std::Api, - _storage: &mut dyn cosmwasm_std::Storage, - _router: &dyn cw_multi_test::CosmosRouter, - _block: &cosmwasm_std::BlockInfo, - _msg: Self::SudoT, - ) -> AnyResult - where - ExecC: std::fmt::Debug - + Clone - + PartialEq - + schemars::JsonSchema - + serde::de::DeserializeOwned - + 'static, - QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, - { - bail!("sudo is not implemented for ElysModule") - } -} - -#[test] -fn get_rewards() { - // Create a wallet for the "user" with an initial balance of 100 usdc - let wallet = vec![( - "user", - coins( - 100, - "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", - ), - )]; - - let mut addresses: Vec = vec![]; - let mut app = BasicAppBuilder::::new_custom() - .with_custom(ElysModuleWrapper(ElysModule {})) - .build(|roouter, _, storage| { - for (wallet_owner, wallet_contenent) in wallet { - roouter - .bank - .init_balance(storage, &Addr::unchecked(wallet_owner), wallet_contenent) - .unwrap(); - addresses.push(wallet_owner.to_owned()) - } - ACCOUNT.save(storage, &addresses).unwrap(); - PERPETUAL_OPENED_POSITION.save(storage, &vec![]).unwrap(); - ASSET_INFO.save(storage, &vec![]).unwrap(); - PRICES.save(storage, &vec![]).unwrap(); - LAST_MODULE_USED.save(storage, &None).unwrap(); - }); - - // trade shield deployment - let trade_shield_code = - ContractWrapper::new(trade_shield_execute, trade_shield_init, trade_shield_query); - let trade_shield_code_id = app.store_code(Box::new(trade_shield_code)); - let trade_shield_init = TradeShieldInstantiateMsg { - account_history_address: None, - }; - let trade_shield_address = app - .instantiate_contract( - trade_shield_code_id, - Addr::unchecked("owner"), - &trade_shield_init, - &[], - "Contract", - None, - ) - .unwrap() - .to_string(); - - // Create a contract wrapper and store its code. - let code = ContractWrapper::new(execute, instantiate, query).with_sudo(sudo); - let code_id = app.store_code(Box::new(code)); - - // Create a mock message to instantiate the contract with no initial orders. - let instantiate_msg = InstantiateMsg { - limit: Some(3), - expiration: Some(cw_utils::Expiration::AtTime(Timestamp::from_seconds( - 604800, - ))), - trade_shield_address: Some(trade_shield_address), - }; - - // Instantiate the contract with "owner" as the deployer. - let addr = app - .instantiate_contract( - code_id, - Addr::unchecked("owner"), - &instantiate_msg, - &[], - "Contract", - None, - ) - .unwrap(); - - app.wasm_sudo(addr.clone(), &SudoMsg::ClockEndBlock {}) - .unwrap(); - - let resp: GetRewardsResp = app - .wrap() - .query_wasm_smart( - &addr, - &QueryMsg::GetRewards { - user_address: "user".to_string(), - }, - ) - .unwrap(); - - assert_eq!( - resp.rewards_map, - Reward { - usdc_usd: Decimal::zero(), - eden_usd: Decimal::from_str("0.000070616020135353").unwrap(), - eden_boost: Decimal::from_str("0.000121").unwrap(), - other_usd: Decimal::zero(), - total_usd: Decimal::from_str("0.000070616020135353").unwrap() - } - ); - - assert_eq!( - resp.rewards.contains(&CoinValue { - denom: "ueden".to_string(), - amount_token: Decimal::from_str("0.00002").unwrap(), - price: Decimal::from_str("3.5308010067676894").unwrap(), - amount_usd: Decimal::from_str("0.000070616020135353").unwrap(), - }), - true - ); - assert_eq!( - resp.rewards.contains(&CoinValue { - denom: "uedenb".to_string(), - amount_token: Decimal::from_str("0.000121").unwrap(), - price: Decimal::zero(), - amount_usd: Decimal::zero(), - }), - true - ); -} diff --git a/contracts/account-history-contract/src/tests/get_staked_assets.rs b/contracts/account-history-contract/src/tests/get_staked_assets.rs deleted file mode 100644 index 7dad9124..00000000 --- a/contracts/account-history-contract/src/tests/get_staked_assets.rs +++ /dev/null @@ -1,850 +0,0 @@ -use std::str::FromStr; - -use crate::entry_point::instantiate; -use crate::tests::get_staked_assets::query_resp::StakedAssetsResponse; -use crate::{ - entry_point::{execute, query, sudo}, - msg::*, -}; -use anyhow::{bail, Error, Result as AnyResult}; -use cosmwasm_std::{ - coins, to_json_binary, Addr, Coin, DecCoin, Decimal, Decimal256, Empty, Int128, StdError, - Timestamp, Uint128, -}; -use cw_multi_test::{AppResponse, BasicAppBuilder, ContractWrapper, Executor, Module}; -use elys_bindings::account_history::msg::query_resp::StakeAssetBalanceBreakdown; -use elys_bindings::account_history::types::earn_detail::earn_detail::AprEdenBoost; -use elys_bindings::account_history::types::earn_program::{ - EdenBoostEarnProgram, EdenEarnProgram, ElysEarnProgram, UsdcEarnProgram, -}; -use elys_bindings::account_history::types::{ - AprElys, AprUsdc, CoinValue, QueryAprResponse, StakedAssets, -}; -use elys_bindings::query_resp::{ - BalanceBorrowed, DelegationDelegatorReward, Entry, EstakingRewardsResponse, Lockup, - MasterchefUserPendingRewardData, MasterchefUserPendingRewardResponse, QueryAprsResponse, - QueryGetEntryResponse, QueryGetPriceResponse, QueryStableStakeAprResponse, - QueryStakedPositionResponse, QueryUnstakedPositionResponse, QueryVestingInfoResponse, - StakedAvailable, Validator, -}; -use elys_bindings::types::{ - BalanceAvailable, Price, StakedPosition, StakingValidator, UnstakedPosition, -}; -use elys_bindings::{ElysMsg, ElysQuery}; -use elys_bindings_test::{ - ElysModule, ACCOUNT, ASSET_INFO, LAST_MODULE_USED, PERPETUAL_OPENED_POSITION, PRICES, -}; -use trade_shield_contract::entry_point::{ - execute as trade_shield_execute, instantiate as trade_shield_init, query as trade_shield_query, -}; -use trade_shield_contract::msg::InstantiateMsg as TradeShieldInstantiateMsg; - -struct ElysModuleWrapper(ElysModule); - -impl Module for ElysModuleWrapper { - type QueryT = ElysQuery; - type ExecT = ElysMsg; - type SudoT = Empty; - - fn query( - &self, - api: &dyn cosmwasm_std::Api, - storage: &dyn cosmwasm_std::Storage, - querier: &dyn cosmwasm_std::Querier, - block: &cosmwasm_std::BlockInfo, - request: Self::QueryT, - ) -> AnyResult { - match request { - ElysQuery::AssetProfileEntry { base_denom } => { - let resp = match base_denom.as_str() { - "uusdc" => QueryGetEntryResponse { - entry: Entry { - address: "".to_string(), - authority: "elys10d07y265gmmuvt4z0w9aw880jnsr700j6z2zm3".to_string(), - base_denom: "uusdc".to_string(), - commit_enabled: true, - decimals: 6, - denom: "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65".to_string(), - display_name: "USDC".to_string(), - display_symbol: "uUSDC".to_string(), - external_symbol: "uUSDC".to_string(), - ibc_channel_id: "channel-12".to_string(), - ibc_counterparty_chain_id: "".to_string(), - ibc_counterparty_channel_id: "channel-19".to_string(), - ibc_counterparty_denom: "".to_string(), - network: "".to_string(), - path: "transfer/channel-12".to_string(), - permissions: vec![], - transfer_limit: "".to_string(), - unit_denom: "uusdc".to_string(), - withdraw_enabled: true, - }, - }, - "ueden" => QueryGetEntryResponse { - entry: Entry { - address: "".to_string(), - authority: "elys10d07y265gmmuvt4z0w9aw880jnsr700j6z2zm3".to_string(), - base_denom: "ueden".to_string(), - commit_enabled: true, - decimals: 6, - denom: "ueden".to_string(), - display_name: "EDEN".to_string(), - display_symbol: "".to_string(), - external_symbol: "".to_string(), - ibc_channel_id: "".to_string(), - ibc_counterparty_chain_id: "".to_string(), - ibc_counterparty_channel_id: "".to_string(), - ibc_counterparty_denom: "".to_string(), - network: "".to_string(), - path: "".to_string(), - permissions: vec![], - transfer_limit: "".to_string(), - unit_denom: "".to_string(), - withdraw_enabled: true, - }, - }, - "uelys" => QueryGetEntryResponse { - entry: Entry { - address: "".to_string(), - authority: "elys10d07y265gmmuvt4z0w9aw880jnsr700j6z2zm3".to_string(), - base_denom: "uelys".to_string(), - commit_enabled: true, - decimals: 6, - denom: "uelys".to_string(), - display_name: "ELYS".to_string(), - display_symbol: "".to_string(), - external_symbol: "".to_string(), - ibc_channel_id: "".to_string(), - ibc_counterparty_chain_id: "".to_string(), - ibc_counterparty_channel_id: "".to_string(), - ibc_counterparty_denom: "".to_string(), - network: "".to_string(), - path: "".to_string(), - permissions: vec![], - transfer_limit: "".to_string(), - unit_denom: "".to_string(), - withdraw_enabled: true, - }, - }, - _ => return Err(Error::new(StdError::not_found(base_denom))), - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::AmmPriceByDenom { token_in, .. } => { - let spot_price = match token_in.denom.as_str() { - "uelys" => Decimal::from_str("3.5308010067676894").unwrap(), - _ => return Err(Error::new(StdError::not_found(token_in.denom.as_str()))), - }; - Ok(to_json_binary(&spot_price)?) - } - ElysQuery::EstakingRewards { .. } => { - let resp = EstakingRewardsResponse { - rewards: vec![DelegationDelegatorReward { - validator_address: Validator::EdenBoost.to_string(), - reward: vec![Coin { - denom: "ueden".to_string(), - amount: Uint128::from_str("121").unwrap(), - }], - }], - total: vec![Coin { - denom: "uedenb".to_string(), - amount: Uint128::from_str("121").unwrap(), - }], - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::MasterchefStableStakeApr { .. } => { - let resp = QueryStableStakeAprResponse { - apr: Int128::new(12), - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::OraclePrice { asset, .. } => { - let resp = match asset.as_str() { - "USDC" => QueryGetPriceResponse { - price: Price { - asset: "USDC".to_string(), - price: Decimal::one(), - source: "uelys".to_string(), - provider: "elys1wzm8dvpxpxxf26y4xn85w5adakcenprg4cq2uf".to_string(), - // set timestamp to now - timestamp: block.time.seconds(), - block_height: block.height, - }, - }, - _ => return Err(Error::new(StdError::not_found(asset))), - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::MasterchefUserPendingReward { .. } => { - let resp = MasterchefUserPendingRewardResponse { - rewards: vec![MasterchefUserPendingRewardData { - pool_id: 32767u64, - reward: vec![Coin { - denom: "ueden".to_string(), - amount: Uint128::new(20), - }], - }], - total_rewards: vec![Coin { - denom: "ueden".to_string(), - amount: Uint128::new(20), - }], - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::CommitmentStakedPositions { delegator_address } => { - let resp = match delegator_address.as_str() { - "user" => QueryStakedPositionResponse { - staked_position: Some(vec![StakedPosition { - id: "2".to_string(), - validator: StakingValidator { - id: Some(String::from("1")), - address: "elysvaloper1ng8sen6z5xzcfjtyrsedpe43hglymq040x3cpw" - .to_string(), - name: "nirvana".to_string(), - voting_power: Decimal::from_str("25.6521469796402094").unwrap(), - commission: Decimal::from_str("0.1").unwrap(), - }, - staked: BalanceAvailable { - amount: Uint128::new(10000000), - usd_amount: Decimal::from_str("35.308010067676894").unwrap(), - }, - }]), - }, - _ => return Err(Error::new(StdError::not_found(delegator_address))), - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::CommitmentUnStakedPositions { delegator_address } => { - let resp = match delegator_address.as_str() { - "user" => QueryUnstakedPositionResponse { - unstaked_position: Some(vec![UnstakedPosition { - id: "1".to_string(), - validator: StakingValidator { - id: Some(String::from("1")), - address: "elysvaloper1ng8sen6z5xzcfjtyrsedpe43hglymq040x3cpw" - .to_string(), - name: "nirvana".to_string(), - voting_power: Decimal::from_str("25.6521469796402094").unwrap(), - commission: Decimal::from_str("0.1").unwrap(), - }, - remaining_time: 1707328694, - unstaked: BalanceAvailable { - amount: Uint128::new(100038144098), - usd_amount: Decimal::from_str("353214.779896389585407707").unwrap(), - }, - }]), - }, - _ => return Err(Error::new(StdError::not_found(delegator_address))), - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::AmmBalance { address, denom } => { - let resp = match (address.as_str(), denom.as_str()) { - ( - "user", - "ibc/0E1517E2771CA7C03F2ED3F9BAECCAEADF0BFD79B89679E834933BC0F179AD98", - ) => BalanceAvailable { - amount: Uint128::new(21798000), - usd_amount: Decimal::from_str("21798000").unwrap(), - }, - ( - "user", - "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", - ) => BalanceAvailable { - amount: Uint128::new(5333229342748), - usd_amount: Decimal::from_str("5333229342748").unwrap(), - }, - ( - "user", - "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2", - ) => BalanceAvailable { - amount: Uint128::new(2704998), - usd_amount: Decimal::from_str("2704998").unwrap(), - }, - ( - "user", - "ibc/2FBCFC209420E6CECED6EE0BC599E74349759352CE953E27A6871BB3D84BC058", - ) => BalanceAvailable { - amount: Uint128::new(594000000000200000), - usd_amount: Decimal::from_str("594000000000200000").unwrap(), - }, - ( - "user", - "ibc/326A89923D85047E6418A671FBACCAFA2686B01A16ED4A0AD92954FCE1485910", - ) => BalanceAvailable { - amount: Uint128::new(1085352), - usd_amount: Decimal::from_str("1085352").unwrap(), - }, - ( - "user", - "ibc/43881AB3B3D05FD9D3606D7F57CBE6EEEA89D18AC66AF9E2915ED43940E71CFD", - ) => BalanceAvailable { - amount: Uint128::new(168400000000000000), - usd_amount: Decimal::from_str("168400000000000000").unwrap(), - }, - ( - "user", - "ibc/4DAE26570FD24ABA40E2BE4137E39D946C78B00B248D3F78B0919567C4371156", - ) => BalanceAvailable { - amount: Uint128::new(49765000), - usd_amount: Decimal::from_str("49765000").unwrap(), - }, - ( - "user", - "ibc/977D5388D2FBE72D9A33FE2423BF8F4DADF3B591207CC98A295B9ACF81E4DE40", - ) => BalanceAvailable { - amount: Uint128::new(9100000), - usd_amount: Decimal::from_str("9100000").unwrap(), - }, - ( - "user", - "ibc/E059CD828E5009D4CF03C4494BEA73749250287FC98DD46E19F9016B918BF49D", - ) => BalanceAvailable { - amount: Uint128::new(141000000000000000), - usd_amount: Decimal::from_str("141000000000000000").unwrap(), - }, - ( - "user", - "ibc/E2D2F6ADCC68AA3384B2F5DFACCA437923D137C14E86FB8A10207CF3BED0C8D4", - ) => BalanceAvailable { - amount: Uint128::new(37403942), - usd_amount: Decimal::from_str("37403942").unwrap(), - }, - ( - "user", - "ibc/FB22E35236996F6B0B1C9D407E8A379A7B1F4083F1960907A1622F022AE450E1", - ) => BalanceAvailable { - amount: Uint128::new(79979999999749000), - usd_amount: Decimal::from_str("79979999999749000").unwrap(), - }, - ("user", "uelys") => BalanceAvailable { - amount: Uint128::new(45666543), - usd_amount: Decimal::from_str("45666543").unwrap(), - }, - _ => BalanceAvailable { - amount: Uint128::zero(), - usd_amount: Decimal::zero(), - }, - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::CommitmentStakedBalanceOfDenom { denom, .. } => { - let resp: StakedAvailable = match denom.as_str() { - "uusdc" => StakedAvailable { - usd_amount: Decimal::zero(), - amount: Uint128::zero(), - lockups: None, - }, - "uelys" => StakedAvailable { - usd_amount: Decimal::from_str("35.308010067676894").unwrap(), - amount: Uint128::new(10000000), - lockups: Some(vec![]), - }, - "ueden" => StakedAvailable { - usd_amount: Decimal::from_str("9136.339725178804921781").unwrap(), - amount: Uint128::new(2587611057), - lockups: Some(vec![Lockup { - amount: Int128::new(5200770174), - // use now time - unlock_timestamp: block.time.seconds(), - }]), - }, - "uedenb" => StakedAvailable { - usd_amount: Decimal::zero(), - amount: Uint128::zero(), - lockups: None, - }, - _ => return Err(Error::new(StdError::not_found(denom))), - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::StableStakeBalanceOfBorrow {} => { - let resp = BalanceBorrowed { - usd_amount: Decimal::from_str("204000000001").unwrap(), - percentage: Decimal::one(), - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::IncentiveApr { - withdraw_type, - denom, - } => { - let resp: QueryAprResponse = match (withdraw_type, denom.as_str()) { - (1, "uusdc") => QueryAprResponse { - apr: Uint128::new(100), - }, - (1, "ueden") => QueryAprResponse { - apr: Uint128::new(168), - }, - (4, "uusdc") => QueryAprResponse { - apr: Uint128::zero(), - }, - (4, "ueden") => QueryAprResponse { - apr: Uint128::new(29), - }, - (3, "uusdc") => QueryAprResponse { - apr: Uint128::zero(), - }, - (3, "ueden") => QueryAprResponse { - apr: Uint128::new(29), - }, - (3, "uedenb") => QueryAprResponse { - apr: Uint128::new(100), - }, - (2, "uusdc") => QueryAprResponse { - apr: Uint128::zero(), - }, - (2, "ueden") => QueryAprResponse { - apr: Uint128::new(29), - }, - (2, "uedenb") => QueryAprResponse { - apr: Uint128::new(100), - }, - _ => return Err(Error::new(StdError::not_found(denom))), - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::IncentiveAprs {} => Ok(to_json_binary(&QueryAprsResponse { - usdc_apr_usdc: Uint128::new(100), - usdc_apr_edenb: Uint128::zero(), - usdc_apr_eden: Uint128::zero(), - usdc_apr_elys: Uint128::zero(), - eden_apr_usdc: Uint128::new(168), - eden_apr_edenb: Uint128::new(29), - eden_apr_elys: Uint128::new(29), - eden_apr_eden: Uint128::new(29), - edenb_apr_eden: Uint128::new(100), - edenb_apr_elys: Uint128::new(100), - })?), - ElysQuery::CommitmentVestingInfo { .. } => { - let resp = QueryVestingInfoResponse { - vesting: BalanceAvailable { - amount: Uint128::zero(), - usd_amount: Decimal::zero(), - }, - vesting_details: Some(vec![]), - }; - Ok(to_json_binary(&resp)?) - } - _ => self.0.query(api, storage, querier, block, request), - } - } - - fn execute( - &self, - api: &dyn cosmwasm_std::Api, - storage: &mut dyn cosmwasm_std::Storage, - router: &dyn cw_multi_test::CosmosRouter, - block: &cosmwasm_std::BlockInfo, - sender: Addr, - msg: Self::ExecT, - ) -> AnyResult - where - ExecC: std::fmt::Debug - + Clone - + PartialEq - + schemars::JsonSchema - + serde::de::DeserializeOwned - + 'static, - QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, - { - match msg { - _ => self.0.execute(api, storage, router, block, sender, msg), - } - } - - fn sudo( - &self, - _api: &dyn cosmwasm_std::Api, - _storage: &mut dyn cosmwasm_std::Storage, - _router: &dyn cw_multi_test::CosmosRouter, - _block: &cosmwasm_std::BlockInfo, - _msg: Self::SudoT, - ) -> AnyResult - where - ExecC: std::fmt::Debug - + Clone - + PartialEq - + schemars::JsonSchema - + serde::de::DeserializeOwned - + 'static, - QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, - { - bail!("sudo is not implemented for ElysModule") - } -} - -#[test] -fn get_staked_assets() { - // Create a wallet for the "user" with an initial balance of 100 usdc - let wallet = vec![( - "user", - coins( - 200__000_000, - "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", - ), - )]; - - let mut addresses: Vec = vec![]; - let mut app = BasicAppBuilder::::new_custom() - .with_custom(ElysModuleWrapper(ElysModule {})) - .build(|roouter, _, storage| { - for (wallet_owner, wallet_contenent) in wallet { - roouter - .bank - .init_balance(storage, &Addr::unchecked(wallet_owner), wallet_contenent) - .unwrap(); - addresses.push(wallet_owner.to_owned()) - } - ACCOUNT.save(storage, &addresses).unwrap(); - PERPETUAL_OPENED_POSITION.save(storage, &vec![]).unwrap(); - ASSET_INFO.save(storage, &vec![]).unwrap(); - PRICES.save(storage, &vec![]).unwrap(); - LAST_MODULE_USED.save(storage, &None).unwrap(); - }); - - // trade shield deployment - let trade_shield_code = - ContractWrapper::new(trade_shield_execute, trade_shield_init, trade_shield_query); - let trade_shield_code_id = app.store_code(Box::new(trade_shield_code)); - let trade_shield_init = TradeShieldInstantiateMsg { - account_history_address: None, - }; - let trade_shield_address = app - .instantiate_contract( - trade_shield_code_id, - Addr::unchecked("owner"), - &trade_shield_init, - &[], - "Contract", - None, - ) - .unwrap() - .to_string(); - - // Create a contract wrapper and store its code. - let code = ContractWrapper::new(execute, instantiate, query).with_sudo(sudo); - let code_id = app.store_code(Box::new(code)); - - // Create a mock message to instantiate the contract with no initial orders. - let instantiate_msg = InstantiateMsg { - limit: Some(3), - expiration: Some(cw_utils::Expiration::AtTime(Timestamp::from_seconds( - 604800, - ))), - trade_shield_address: Some(trade_shield_address), - }; - - // Instantiate the contract with "owner" as the deployer. - let addr = app - .instantiate_contract( - code_id, - Addr::unchecked("owner"), - &instantiate_msg, - &[], - "Contract", - None, - ) - .unwrap(); - - app.wasm_sudo(addr.clone(), &SudoMsg::ClockEndBlock {}) - .unwrap(); - - // Query the contract for the existing order. - let resp: StakedAssetsResponse = app - .wrap() - .query_wasm_smart( - &addr, - &QueryMsg::GetStakedAssets { - user_address: Some("user".to_string()), - }, - ) - .unwrap(); - - let balance_break_down = StakeAssetBalanceBreakdown { - vesting: Decimal::zero(), - unstaking: vec![UnstakedPosition { - id: "1".to_string(), - validator: StakingValidator { - id: Some(String::from("1")), - address: "elysvaloper1ng8sen6z5xzcfjtyrsedpe43hglymq040x3cpw".to_string(), - name: "nirvana".to_string(), - voting_power: Decimal::from_str("25.6521469796402094").unwrap(), - commission: Decimal::from_str("0.1").unwrap(), - }, - remaining_time: 1707328694, - unstaked: BalanceAvailable { - amount: Uint128::new(100038144098), - usd_amount: Decimal::from_str("353214.779896389585407707").unwrap(), - }, - }] - .iter() - .fold(Decimal::zero(), |acc, item| { - acc.checked_add(item.unstaked.usd_amount) - .unwrap_or_default() - }), - staked: Decimal::from_str("9171.647735246481815781").unwrap(), - }; - - let expected: StakedAssetsResponse = StakedAssetsResponse { - total_staked_balance: DecCoin::new( - Decimal256::from_str("9171.647735246481815781").unwrap(), - "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65".to_string(), - ), - total_balance: balance_break_down.total(), - balance_break_down, - staked_assets: StakedAssets { - eden_boost_earn_program: EdenBoostEarnProgram { - bonding_period: 0, - apr: AprEdenBoost { - uusdc: Uint128::zero(), - ueden: Uint128::new(29), - }, - available: Some(Uint128::zero()), - staked: Some(Uint128::zero()), - rewards: Some(vec![CoinValue { - denom: "ueden".to_string(), - amount_token: Decimal::from_str("0.000121").unwrap(), - price: Decimal::from_atomics(Uint128::new(35308010067676894), 16).unwrap(), - amount_usd: Decimal::from_str("0.00042722692181889").unwrap(), - }]), - }, - eden_earn_program: EdenEarnProgram { - bonding_period: 0, - apr: AprElys { - uusdc: Uint128::zero(), - ueden: Uint128::new(29), - uedenb: Uint128::new(100), - }, - available: Some(BalanceAvailable { - amount: Uint128::new(0), - usd_amount: Decimal::zero(), - }), - staked: Some(StakedAvailable { - usd_amount: Decimal::from_str("9136.339725178804921781").unwrap(), - amount: Uint128::new(2587611057), - lockups: Some(vec![Lockup { - amount: Int128::new(5200770174), - unlock_timestamp: 1571797419, - }]), - }), - rewards: Some(vec![]), - vesting: BalanceAvailable { - amount: Uint128::from_str("0").unwrap(), - usd_amount: Decimal::from_str("0").unwrap(), - }, - vesting_details: Some(vec![]), - }, - elys_earn_program: ElysEarnProgram { - bonding_period: 14, - apr: AprElys { - uusdc: Uint128::zero(), - ueden: Uint128::new(29), - uedenb: Uint128::new(100), - }, - available: Some(BalanceAvailable { - amount: Uint128::new(45666543), - usd_amount: Decimal::from_str("161.239475999999978995").unwrap(), - }), - staked: Some(StakedAvailable { - usd_amount: Decimal::from_str("35.308010067676894").unwrap(), - amount: Uint128::new(10000000), - lockups: Some(vec![]), - }), - rewards: Some(vec![]), - staked_positions: Some(vec![StakedPosition { - id: "2".to_string(), - validator: StakingValidator { - id: Some("1".to_string()), - address: "elysvaloper1ng8sen6z5xzcfjtyrsedpe43hglymq040x3cpw".to_string(), - name: "nirvana".to_string(), - voting_power: Decimal::from_str("25.6521469796402094").unwrap(), - commission: Decimal::from_str("0.1").unwrap(), - }, - staked: BalanceAvailable { - amount: Uint128::from_str("10000000").unwrap(), - usd_amount: Decimal::from_str("35.308010067676894").unwrap(), - }, - }]), - unstaked_positions: Some(vec![UnstakedPosition { - id: "1".to_string(), - validator: StakingValidator { - id: Some("1".to_string()), - address: "elysvaloper1ng8sen6z5xzcfjtyrsedpe43hglymq040x3cpw".to_string(), - name: "nirvana".to_string(), - voting_power: Decimal::from_str("25.6521469796402094").unwrap(), - commission: Decimal::from_str("0.1").unwrap(), - }, - remaining_time: 1707328694, - unstaked: BalanceAvailable { - amount: Uint128::from_str("100038144098").unwrap(), - usd_amount: Decimal::from_str("353214.779896389585407707").unwrap(), - }, - }]), - }, - usdc_earn_program: UsdcEarnProgram { - bonding_period: 0, - apr: AprUsdc { - uusdc: Int128::new(12), - ueden: Int128::new(12), - }, - available: Some(BalanceAvailable { - amount: Uint128::new(5333229342748), - usd_amount: Decimal::from_str("5333229.342748").unwrap(), - }), - staked: Some(StakedAvailable { - usd_amount: Decimal::zero(), - amount: Uint128::zero(), - lockups: None, - }), - rewards: Some(vec![CoinValue { - denom: "ueden".to_string(), - amount_token: Decimal::from_atomics(Uint128::new(000002), 5).unwrap(), - price: Decimal::from_atomics(Uint128::new(35308010067676894), 16).unwrap(), - amount_usd: Decimal::from_str("0.000070616020135353").unwrap(), - }]), - borrowed: Some(BalanceBorrowed { - usd_amount: Decimal::from_str("204000.000001").unwrap(), - percentage: Decimal::one(), - }), - }, - }, - }; - - // test if the response is the same as the expected - - // staked assets - - // USDC program - assert_eq!( - resp.staked_assets.usdc_earn_program.bonding_period, - expected.staked_assets.usdc_earn_program.bonding_period - ); - assert_eq!( - resp.staked_assets.usdc_earn_program.apr, - expected.staked_assets.usdc_earn_program.apr - ); - assert_eq!( - resp.staked_assets.usdc_earn_program.available, - expected.staked_assets.usdc_earn_program.available - ); - assert_eq!( - resp.staked_assets.usdc_earn_program.staked, - expected.staked_assets.usdc_earn_program.staked - ); - assert_eq!( - resp.staked_assets.usdc_earn_program.rewards, - expected.staked_assets.usdc_earn_program.rewards - ); - assert_eq!( - resp.staked_assets.usdc_earn_program.borrowed, - expected.staked_assets.usdc_earn_program.borrowed - ); - assert_eq!( - resp.staked_assets.usdc_earn_program, - expected.staked_assets.usdc_earn_program - ); - - // ELYS program - assert_eq!( - resp.staked_assets.elys_earn_program.bonding_period, - expected.staked_assets.elys_earn_program.bonding_period - ); - assert_eq!( - resp.staked_assets.elys_earn_program.apr, - expected.staked_assets.elys_earn_program.apr - ); - assert_eq!( - resp.staked_assets.elys_earn_program.available, - expected.staked_assets.elys_earn_program.available - ); - assert_eq!( - resp.staked_assets.elys_earn_program.staked, - expected.staked_assets.elys_earn_program.staked - ); - assert_eq!( - resp.staked_assets.elys_earn_program.rewards, - expected.staked_assets.elys_earn_program.rewards - ); - assert_eq!( - resp.staked_assets.elys_earn_program.staked_positions, - expected.staked_assets.elys_earn_program.staked_positions - ); - assert_eq!( - resp.staked_assets.elys_earn_program.unstaked_positions, - expected.staked_assets.elys_earn_program.unstaked_positions - ); - assert_eq!( - resp.staked_assets.elys_earn_program, - expected.staked_assets.elys_earn_program - ); - - // EDEN program - assert_eq!( - resp.staked_assets.eden_earn_program.bonding_period, - expected.staked_assets.eden_earn_program.bonding_period - ); - assert_eq!( - resp.staked_assets.eden_earn_program.apr, - expected.staked_assets.eden_earn_program.apr - ); - assert_eq!( - resp.staked_assets.eden_earn_program.available, - expected.staked_assets.eden_earn_program.available - ); - assert_eq!( - resp.staked_assets.eden_earn_program.staked, - expected.staked_assets.eden_earn_program.staked - ); - assert_eq!( - resp.staked_assets.eden_earn_program.rewards, - expected.staked_assets.eden_earn_program.rewards - ); - assert_eq!( - resp.staked_assets.eden_earn_program.vesting, - expected.staked_assets.eden_earn_program.vesting - ); - assert_eq!( - resp.staked_assets.eden_earn_program.vesting_details, - expected.staked_assets.eden_earn_program.vesting_details - ); - assert_eq!( - resp.staked_assets.eden_earn_program, - expected.staked_assets.eden_earn_program - ); - - // EDEN BOOST program - assert_eq!( - resp.staked_assets.eden_boost_earn_program.bonding_period, - expected - .staked_assets - .eden_boost_earn_program - .bonding_period - ); - assert_eq!( - resp.staked_assets.eden_boost_earn_program.apr, - expected.staked_assets.eden_boost_earn_program.apr - ); - assert_eq!( - resp.staked_assets.eden_boost_earn_program.available, - expected.staked_assets.eden_boost_earn_program.available - ); - assert_eq!( - resp.staked_assets.eden_boost_earn_program.staked, - expected.staked_assets.eden_boost_earn_program.staked - ); - assert_eq!( - resp.staked_assets.eden_boost_earn_program.rewards, - expected.staked_assets.eden_boost_earn_program.rewards - ); - assert_eq!( - resp.staked_assets.eden_boost_earn_program, - expected.staked_assets.eden_boost_earn_program - ); - - assert_eq!(resp.staked_assets, expected.staked_assets); - - assert_eq!(resp.total_staked_balance, expected.total_staked_balance); - assert_eq!(resp, expected); -} diff --git a/contracts/account-history-contract/src/tests/get_usdc_earn_program_details.rs b/contracts/account-history-contract/src/tests/get_usdc_earn_program_details.rs deleted file mode 100644 index a3b5a726..00000000 --- a/contracts/account-history-contract/src/tests/get_usdc_earn_program_details.rs +++ /dev/null @@ -1,305 +0,0 @@ -use std::str::FromStr; - -use crate::entry_point::instantiate; -use crate::{ - entry_point::{execute, query, sudo}, - msg::*, -}; -use anyhow::{bail, Error, Result as AnyResult}; -use cosmwasm_std::{ - coins, to_json_binary, Addr, Coin, Decimal, Empty, Int128, StdError, Timestamp, Uint128, -}; -use cw_multi_test::{AppResponse, BasicAppBuilder, ContractWrapper, Executor, Module}; -use elys_bindings::account_history::msg::query_resp::earn::GetUsdcEarnProgramResp; -use elys_bindings::account_history::types::earn_program::UsdcEarnProgram; -use elys_bindings::account_history::types::{AprUsdc, CoinValue}; -use elys_bindings::query_resp::{ - BalanceBorrowed, DelegationDelegatorReward, EstakingRewardsResponse, - MasterchefUserPendingRewardData, MasterchefUserPendingRewardResponse, - QueryStableStakeAprResponse, StakedAvailable, -}; -use elys_bindings::types::BalanceAvailable; -use elys_bindings::{ElysMsg, ElysQuery}; -use elys_bindings_test::{ - ElysModule, ACCOUNT, ASSET_INFO, LAST_MODULE_USED, PERPETUAL_OPENED_POSITION, PRICES, -}; -use trade_shield_contract::entry_point::{ - execute as trade_shield_execute, instantiate as trade_shield_init, query as trade_shield_query, -}; -use trade_shield_contract::msg::InstantiateMsg as TradeShieldInstantiateMsg; - -struct ElysModuleWrapper(ElysModule); - -impl Module for ElysModuleWrapper { - type QueryT = ElysQuery; - type ExecT = ElysMsg; - type SudoT = Empty; - - fn query( - &self, - api: &dyn cosmwasm_std::Api, - storage: &dyn cosmwasm_std::Storage, - querier: &dyn cosmwasm_std::Querier, - block: &cosmwasm_std::BlockInfo, - request: Self::QueryT, - ) -> AnyResult { - match request { - ElysQuery::AmmBalance { address, denom } => { - let resp = match (address.as_str(), denom.as_str()) { - ( - "user", - "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", - ) => BalanceAvailable { - amount: Uint128::new(1234), - usd_amount: Decimal::from_str("1234").unwrap(), - }, - ("user", "uelys") => BalanceAvailable { - amount: Uint128::new(45666543), - usd_amount: Decimal::from_str("45666543").unwrap(), - }, - _ => BalanceAvailable { - amount: Uint128::zero(), - usd_amount: Decimal::zero(), - }, - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::MasterchefStableStakeApr { denom } => { - let resp = match denom.as_str() { - "uusdc" => QueryStableStakeAprResponse { - apr: Int128::zero(), - }, - "ueden" => QueryStableStakeAprResponse { - apr: Int128::zero(), - }, - _ => return Err(Error::new(StdError::not_found(denom))), - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::CommitmentStakedBalanceOfDenom { .. } => { - let resp = StakedAvailable { - usd_amount: Decimal::from_atomics(Uint128::new(100130012), 3).unwrap(), - amount: Uint128::new(100120000000), - lockups: None, - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::StableStakeBalanceOfBorrow {} => { - let resp = BalanceBorrowed { - usd_amount: Decimal::from_atomics(Uint128::new(3265035180871), 10).unwrap(), - percentage: Decimal::from_atomics(Uint128::new(0000238391578776388), 18) - .unwrap(), - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::MasterchefUserPendingReward { .. } => { - let resp = MasterchefUserPendingRewardResponse { - rewards: vec![MasterchefUserPendingRewardData { - pool_id: 32767u64, - reward: vec![Coin { - denom: "ueden".to_string(), - amount: Uint128::new(20), - }], - }], - total_rewards: vec![Coin { - denom: "ueden".to_string(), - amount: Uint128::new(20), - }], - }; - Ok(to_json_binary(&resp)?) - } - ElysQuery::AmmPriceByDenom { token_in, .. } => { - let spot_price = match token_in.denom.as_str() { - "uelys" => Decimal::from_str("3.5308010067676894").unwrap(), - "ueden" => Decimal::from_str("3.5308010067676894").unwrap(), - "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65" => { - Decimal::one() - } - "ibc/E2D2F6ADCC68AA3384B2F5DFACCA437923D137C14E86FB8A10207CF3BED0C8D4" => { - Decimal::from_str("9.02450744362719844").unwrap() - } - _ => return Err(Error::new(StdError::not_found(token_in.denom.as_str()))), - }; - Ok(to_json_binary(&spot_price)?) - } - ElysQuery::EstakingRewards { .. } => { - let resp = EstakingRewardsResponse { - rewards: vec![DelegationDelegatorReward { - validator_address: "validator".to_string(), - reward: vec![Coin { - denom: "ueden".to_string(), - amount: Uint128::from_str("121").unwrap(), - }], - }], - total: vec![Coin { - denom: "ueden".to_string(), - amount: Uint128::from_str("121").unwrap(), - }], - }; - Ok(to_json_binary(&resp)?) - } - _ => self.0.query(api, storage, querier, block, request), - } - } - - fn execute( - &self, - api: &dyn cosmwasm_std::Api, - storage: &mut dyn cosmwasm_std::Storage, - router: &dyn cw_multi_test::CosmosRouter, - block: &cosmwasm_std::BlockInfo, - sender: Addr, - msg: Self::ExecT, - ) -> AnyResult - where - ExecC: std::fmt::Debug - + Clone - + PartialEq - + schemars::JsonSchema - + serde::de::DeserializeOwned - + 'static, - QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, - { - match msg { - _ => self.0.execute(api, storage, router, block, sender, msg), - } - } - - fn sudo( - &self, - _api: &dyn cosmwasm_std::Api, - _storage: &mut dyn cosmwasm_std::Storage, - _router: &dyn cw_multi_test::CosmosRouter, - _block: &cosmwasm_std::BlockInfo, - _msg: Self::SudoT, - ) -> AnyResult - where - ExecC: std::fmt::Debug - + Clone - + PartialEq - + schemars::JsonSchema - + serde::de::DeserializeOwned - + 'static, - QueryC: cosmwasm_std::CustomQuery + serde::de::DeserializeOwned + 'static, - { - bail!("sudo is not implemented for ElysModule") - } -} - -#[test] -fn get_usdc_earn_program_details() { - // Create a wallet for the "user" with an initial balance of 100 usdc - let wallet = vec![( - "user", - coins( - 100, - "ibc/2180E84E20F5679FCC760D8C165B60F42065DEF7F46A72B447CFF1B7DC6C0A65", - ), - )]; - - let mut addresses: Vec = vec![]; - let mut app = BasicAppBuilder::::new_custom() - .with_custom(ElysModuleWrapper(ElysModule {})) - .build(|roouter, _, storage| { - for (wallet_owner, wallet_contenent) in wallet { - roouter - .bank - .init_balance(storage, &Addr::unchecked(wallet_owner), wallet_contenent) - .unwrap(); - addresses.push(wallet_owner.to_owned()) - } - ACCOUNT.save(storage, &addresses).unwrap(); - PERPETUAL_OPENED_POSITION.save(storage, &vec![]).unwrap(); - ASSET_INFO.save(storage, &vec![]).unwrap(); - PRICES.save(storage, &vec![]).unwrap(); - LAST_MODULE_USED.save(storage, &None).unwrap(); - }); - - // trade shield deployment - let trade_shield_code = - ContractWrapper::new(trade_shield_execute, trade_shield_init, trade_shield_query); - let trade_shield_code_id = app.store_code(Box::new(trade_shield_code)); - let trade_shield_init = TradeShieldInstantiateMsg { - account_history_address: None, - }; - let trade_shield_address = app - .instantiate_contract( - trade_shield_code_id, - Addr::unchecked("owner"), - &trade_shield_init, - &[], - "Contract", - None, - ) - .unwrap() - .to_string(); - - // Create a contract wrapper and store its code. - let code = ContractWrapper::new(execute, instantiate, query).with_sudo(sudo); - let code_id = app.store_code(Box::new(code)); - - // Create a mock message to instantiate the contract with no initial orders. - let instantiate_msg = InstantiateMsg { - limit: Some(3), - expiration: Some(cw_utils::Expiration::AtTime(Timestamp::from_seconds( - 604800, - ))), - trade_shield_address: Some(trade_shield_address), - }; - - // Instantiate the contract with "owner" as the deployer. - let addr = app - .instantiate_contract( - code_id, - Addr::unchecked("owner"), - &instantiate_msg, - &[], - "Contract", - None, - ) - .unwrap(); - - app.wasm_sudo(addr.clone(), &SudoMsg::ClockEndBlock {}) - .unwrap(); - - let resp: GetUsdcEarnProgramResp = app - .wrap() - .query_wasm_smart( - &addr, - &QueryMsg::GetUsdcEarnProgramDetails { - address: "user".to_string(), - }, - ) - .unwrap(); - - let expected = GetUsdcEarnProgramResp { - data: UsdcEarnProgram { - bonding_period: 0, - apr: AprUsdc { - uusdc: Int128::zero(), - ueden: Int128::zero(), - }, - available: Some(BalanceAvailable { - amount: Uint128::new(1234), - usd_amount: Decimal::from_str("0.001234").unwrap(), - }), - staked: Some(StakedAvailable { - usd_amount: Decimal::from_atomics(Uint128::new(100130012), 3).unwrap(), - amount: Uint128::new(100120000000), - lockups: None, - }), - rewards: Some(vec![CoinValue { - denom: "ueden".to_string(), - amount_token: Decimal::from_atomics(Uint128::new(000002), 5).unwrap(), - price: Decimal::from_atomics(Uint128::new(35308010067676894), 16).unwrap(), - amount_usd: Decimal::from_str("0.000070616020135353").unwrap(), - }]), - borrowed: Some(BalanceBorrowed { - usd_amount: Decimal::from_str("0.0003265035180871").unwrap(), - percentage: Decimal::from_atomics(Uint128::new(0000238391578776388), 18).unwrap(), - }), - }, - }; - - assert_eq!(resp, expected); -} diff --git a/contracts/account-history-contract/src/tests/mod.rs b/contracts/account-history-contract/src/tests/mod.rs deleted file mode 100644 index f5708269..00000000 --- a/contracts/account-history-contract/src/tests/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -mod get_all_pools; -mod get_eden_boost_earn_program_details; -mod get_eden_earn_program_details; -mod get_elys_earn_program_detail; -mod get_liquid_assets; -mod get_rewards; -mod get_staked_assets; -mod get_usdc_earn_program_details; From bfbe4fcdf4ca6cd4d99899ad5eb988266e128908 Mon Sep 17 00:00:00 2001 From: CryptoKage2306 <26vivek06@gmail.com> Date: Tue, 13 Aug 2024 19:33:57 +0530 Subject: [PATCH 4/7] update: scripts --- scripts/build.sh | 1 + scripts/deploy.sh | 32 ++++++++++++++++++++++++++++++-- scripts/test_history.sh | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 2 deletions(-) create mode 100755 scripts/test_history.sh diff --git a/scripts/build.sh b/scripts/build.sh index 177ffd23..bf741744 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -4,6 +4,7 @@ if [ "${CI:-}" = "true" ]; then echo "CI is true, running the block of commands..." export VERSION=$(git describe --tags --match 'v*' --abbrev=0 | sed 's/^v//') + sed -i "s/^version = .*/version = \"$VERSION\"/" contracts/account-history-contract/Cargo.toml sed -i "s/^version = .*/version = \"$VERSION\"/" contracts/trade-shield-contract/Cargo.toml cargo update fi diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 2c3a4073..4735c839 100755 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -20,10 +20,11 @@ if [ -n "$CI" ]; then # contract addresses TS_CONTRACT_ADDRESS=elys1m3hduhk4uzxn8mxuvpz02ysndxfwgy5mq60h4c34qqn67xud584qeee3m4 + AH_CONTRACT_ADDRESS=elys1s37xz7tzrru2cpl96juu9lfqrsd4jh73j9slyv440q5vttx2uyesetjpne # set elysd path ELYSD=/tmp/elysd - URL=https://github.com/elys-network/elys/releases/download/v0.39.0/elysd-v0.39.0-linux-amd64 + URL=https://github.com/elys-network/elys/releases/download/v0.29.26/elysd-v0.29.26-linux-amd64 # download elysd and binary to path wget $URL -O $ELYSD @@ -97,8 +98,35 @@ wait_for_tx $txhash export ts_contract_address=$(elysd q tx $txhash --node $NODE | extract_contract_address) echo "ts_contract_address: $ts_contract_address" +# store and init/migrate account history contract +txhash=$(elysd tx wasm store artifacts/account_history_contract.wasm $OPTIONS --sequence $(($sequence + 5)) | extract_txhash) +echo "ah store txhash: $txhash" +wait_for_tx $txhash +codeid=$(elysd q tx $txhash --node $NODE | extract_code_id) +echo "ah code id: $codeid" +if [ -n "$AH_CONTRACT_ADDRESS" ]; then + txhash=$(elysd tx wasm migrate $OPTIONS --sequence $(($sequence + 6)) $AH_CONTRACT_ADDRESS $codeid '{ + "trade_shield_address": "'"$TS_CONTRACT_ADDRESS"'", + "limit": 1 + }' | extract_txhash) + echo "ah migrate txhash: $txhash" +else + txhash=$(elysd tx wasm init $OPTIONS --sequence $(($sequence + 6)) --label "ah" --admin $NAME $codeid '{ + "limit": 300, + "expiration": { + "at_time": "604800000000000" + }, + "trade_shield_address": "'"$ts_contract_address"'" + }' | extract_txhash) + echo "ah init txhash: $txhash" +fi +wait_for_tx $txhash +ah_contract_address=$(elysd q tx $txhash --node $NODE | extract_contract_address) +echo "ah_contract_address: $ah_contract_address" + # print environment variables to set printf "\nset those environment variables to use the contracts:\n\n" printf "export NODE=%s\n" "$NODE" printf "export NAME=%s\n" "$NAME" -printf "export TS_CONTRACT_ADDRESS=%s\n" "$ts_contract_address" \ No newline at end of file +printf "export TS_CONTRACT_ADDRESS=%s\n" "$ts_contract_address" +printf "export AH_CONTRACT_ADDRESS=%s\n" "$ah_contract_address" \ No newline at end of file diff --git a/scripts/test_history.sh b/scripts/test_history.sh new file mode 100755 index 00000000..6594df03 --- /dev/null +++ b/scripts/test_history.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +# set -x +extract_txhash() { awk -F 'txhash: ' '/txhash:/{print $2; exit}'; } +extract_code_id() { awk -F 'key: code_id|value: ' '/key: code_id/ { getline; gsub(/"/, "", $2); print $2; exit }'; } +extract_contract_address() { awk -F 'key: _contract_address|value: ' '/key: _contract_address/ { getline; gsub(/"/, "", $2); print $2; exit }'; } + +OPTIONS="--from validator --gas auto --gas-adjustment=1.3 --fees 100000uelys -b sync -y --keyring-backend=test --chain-id=elystestnet-1" + +docker run --rm -v "$(pwd)":/code \ + --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ + --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ + cosmwasm/workspace-optimizer:0.14.0 + +# store and init trade shield contract +txhash=$(elysd tx wasm store artifacts/trade_shield_contract.wasm $OPTIONS | extract_txhash) +sleep 10 +codeid=$(elysd q tx $txhash | extract_code_id) +txhash=$(elysd tx wasm init $codeid '{}' $OPTIONS --label "Contract" --admin validator | extract_txhash) +sleep 10 +addr=$(elysd q tx $txhash | extract_contract_address) + +echo tradeshield : $addr + + +# store and init account history contract +txhash=$(elysd tx wasm store artifacts/account_history_contract.wasm $OPTIONS | extract_txhash) +sleep 10 +codeid=$(elysd q tx $txhash | extract_code_id) +msg=$(echo '{"limit" : 300, "expiration": {"at_time":"604800000000000"}, "trade_shield_address" :"'$addr'"}') +txhash=$(elysd tx wasm init $codeid "$msg" $OPTIONS --label "Contract" --admin validator | extract_txhash) +sleep 10 +addr=$(elysd q tx $txhash | extract_contract_address) +echo history : $addr +elysd tx wasm exec $addr '{}' --from validator --gas-prices 0.25uelys --gas auto --gas-adjustment 1.3 -b sync -y --keyring-backend=test --chain-id=elystestnet-1 +elysd q wasm contract-state smart $addr '{"all" : {}}' +# elysd q wasm contract-state smart $addr2 '{"get_liquid_assets" : {"user_address" : "WRITE THE USER ADDRESS"}}' \ No newline at end of file From 6159122c5353d93d3a45d89cf6f24dfcd13d838c Mon Sep 17 00:00:00 2001 From: CryptoKage2306 <26vivek06@gmail.com> Date: Tue, 13 Aug 2024 20:18:39 +0530 Subject: [PATCH 5/7] update: sudo --- contracts/account-history-contract/src/entry_point/sudo.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/contracts/account-history-contract/src/entry_point/sudo.rs b/contracts/account-history-contract/src/entry_point/sudo.rs index 950a6491..7d8d69d8 100644 --- a/contracts/account-history-contract/src/entry_point/sudo.rs +++ b/contracts/account-history-contract/src/entry_point/sudo.rs @@ -1,5 +1,4 @@ use crate::action::execute::clean_up_storage; -use crate::action::sudo::update_metadata_prices; use crate::states::DELETE_OLD_DATA_ENABLED; use crate::{msg::SudoMsg, states::DELETE_EPOCH}; use cosmwasm_std::{entry_point, DepsMut, Env, Response, StdResult}; @@ -13,7 +12,6 @@ pub fn sudo(mut deps: DepsMut, _env: Env, msg: SudoMsg) -> StdResult< if DELETE_OLD_DATA_ENABLED.load(deps.storage)? == true { clean_up_storage(&mut deps, epoch)?; } - update_metadata_prices(deps)?; Ok(Response::new()) } } From e8ec10821b9ddd513c7a88dd49d2c970cc709b7e Mon Sep 17 00:00:00 2001 From: CryptoKage2306 <26vivek06@gmail.com> Date: Wed, 14 Aug 2024 00:37:07 +0530 Subject: [PATCH 6/7] refactor: update ts bindings --- bindings/src/trade_shield/msg/instantiate_msg.rs | 4 +--- bindings/src/trade_shield/msg/migrate_msg.rs | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/bindings/src/trade_shield/msg/instantiate_msg.rs b/bindings/src/trade_shield/msg/instantiate_msg.rs index c9d1ad02..1ca3d31c 100644 --- a/bindings/src/trade_shield/msg/instantiate_msg.rs +++ b/bindings/src/trade_shield/msg/instantiate_msg.rs @@ -1,6 +1,4 @@ use cosmwasm_schema::cw_serde; #[cw_serde] -pub struct InstantiateMsg { - pub account_history_address: Option, -} +pub struct InstantiateMsg {} diff --git a/bindings/src/trade_shield/msg/migrate_msg.rs b/bindings/src/trade_shield/msg/migrate_msg.rs index 23721cfc..0903fe8b 100644 --- a/bindings/src/trade_shield/msg/migrate_msg.rs +++ b/bindings/src/trade_shield/msg/migrate_msg.rs @@ -1,6 +1,4 @@ use cosmwasm_schema::cw_serde; #[cw_serde] -pub struct MigrateMsg { - pub account_history_address: Option, -} +pub struct MigrateMsg {} From 5767b0673e04bcf7949d6b62f6b268da95e05ec8 Mon Sep 17 00:00:00 2001 From: CryptoKage2306 <26vivek06@gmail.com> Date: Wed, 14 Aug 2024 00:51:03 +0530 Subject: [PATCH 7/7] refactor: script --- .../src/entry_point/migrate.rs | 44 +------------------ scripts/deploy.sh | 2 +- 2 files changed, 3 insertions(+), 43 deletions(-) diff --git a/contracts/account-history-contract/src/entry_point/migrate.rs b/contracts/account-history-contract/src/entry_point/migrate.rs index d4647925..ea48b240 100644 --- a/contracts/account-history-contract/src/entry_point/migrate.rs +++ b/contracts/account-history-contract/src/entry_point/migrate.rs @@ -1,33 +1,16 @@ -use cosmwasm_std::{entry_point, DepsMut, Env, Response, StdError, StdResult, Timestamp}; -use cw2::set_contract_version; +use cosmwasm_std::{entry_point, DepsMut, Env, Response, StdResult, Timestamp}; use cw_utils::Expiration; use elys_bindings::account_history::msg::MigrationMsg; // use elys_bindings::account_history::types::Metadata; use elys_bindings::{ElysMsg, ElysQuery}; -use semver::Version; -use super::instantiate::{CONTRACT_NAME, CONTRACT_VERSION}; -use crate::states::{ - DELETE_EPOCH, DELETE_OLD_DATA_ENABLED, EXPIRATION, PARAMS_ADMIN, PROCESSED_ACCOUNT_PER_BLOCK, - TRADE_SHIELD_ADDRESS, -}; +use crate::states::{DELETE_EPOCH, DELETE_OLD_DATA_ENABLED, PROCESSED_ACCOUNT_PER_BLOCK}; #[cfg_attr(not(feature = "library"), entry_point)] pub fn migrate( deps: DepsMut, _env: Env, msg: MigrationMsg, ) -> StdResult> { - // TRADESHIELD ADDRESS - if msg.trade_shield_address.is_some() { - TRADE_SHIELD_ADDRESS.save(deps.storage, &msg.trade_shield_address)?; - } - - // EXPIRATION - EXPIRATION.save( - deps.storage, - &Expiration::AtTime(Timestamp::from_seconds(3 * 24 * 60 * 60)), - )?; - // PROCESSED_ACCOUNT_PER_BLOCK let limit = match msg.limit { Some(limit) => limit, @@ -47,28 +30,5 @@ pub fn migrate( // RESPONSE - let ver = cw2::get_contract_version(deps.storage)?; - // ensure we are migrating from an allowed contract - if ver.contract != CONTRACT_NAME { - return Err(StdError::generic_err("Can only upgrade from same type").into()); - } - let new_contract_version = Version::parse(CONTRACT_VERSION).unwrap(); - let actual_contract_version = Version::parse(ver.version.as_str()).unwrap(); - - if new_contract_version.le(&actual_contract_version) { - let err_version: String = format!( - "Error the version of account-history-contract {} has to be upper to {}", - new_contract_version.to_string(), - actual_contract_version.to_string() - ); - - return Err(StdError::generic_err(err_version).into()); - } - - let admin = "elys16xffmfa6k45j340cx5zyp66lqvuw62a0neaa7w".to_string(); - PARAMS_ADMIN.save(deps.storage, &admin)?; - - // set the new version - set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; Ok(Response::new()) } diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 4735c839..ddb5fb02 100755 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -107,7 +107,7 @@ echo "ah code id: $codeid" if [ -n "$AH_CONTRACT_ADDRESS" ]; then txhash=$(elysd tx wasm migrate $OPTIONS --sequence $(($sequence + 6)) $AH_CONTRACT_ADDRESS $codeid '{ "trade_shield_address": "'"$TS_CONTRACT_ADDRESS"'", - "limit": 1 + "limit": 50 }' | extract_txhash) echo "ah migrate txhash: $txhash" else