diff --git a/app-template/artifacts/app.wasm b/app-template/artifacts/app.wasm index 18c74aa6b4..0c88238b6f 100644 Binary files a/app-template/artifacts/app.wasm and b/app-template/artifacts/app.wasm differ diff --git a/app-template/artifacts/checksums.txt b/app-template/artifacts/checksums.txt index 7b97ce1a30..8a90892c19 100644 --- a/app-template/artifacts/checksums.txt +++ b/app-template/artifacts/checksums.txt @@ -1 +1 @@ -4e4b097907a9eb0a31d4757f73ba741ca4452e1b9f632a4500cc1dfbeba01f4d app-aarch64.wasm +3849868a9cbe2fb82bc7375148784233506ac2e70ed424b33d20061c65ca8c6e app.wasm diff --git a/app-template/artifacts/checksums_intermediate.txt b/app-template/artifacts/checksums_intermediate.txt index 4d21a59ab3..eb611dacef 100644 --- a/app-template/artifacts/checksums_intermediate.txt +++ b/app-template/artifacts/checksums_intermediate.txt @@ -1 +1 @@ -6a3347162d4e89d6aebafd8c7cbd40c686d8161e4b68797b39e1ca49bb4cfb49 ./target/wasm32-unknown-unknown/release/app.wasm +2a2a39252752ccfff03ad02dca930b6f1831afefb001590fe980867658571b17 /target/wasm32-unknown-unknown/release/app.wasm diff --git a/app-template/artifacts/template_app-aarch64.wasm b/app-template/artifacts/template_app-aarch64.wasm deleted file mode 100644 index fd428b093b..0000000000 Binary files a/app-template/artifacts/template_app-aarch64.wasm and /dev/null differ diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index d82a93b6e7..a0cf425ea8 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Install modules replaced install module method on module factory to reduce gas consumption for multi-install cases. - Modified the account id structure. Each account is now identified with a unique ID and a trace. This is a requirement for Abstract IBC. - Register Module(and Add Module) will now accept list of items, which reduces gas for multi-module install +- Stake methods on cw-staking adapter now accept list, allowing users to do multi-stake/unstake/etc. ### Fixed diff --git a/framework/packages/abstract-adapter/Cargo.toml b/framework/packages/abstract-adapter/Cargo.toml index 8e070d8688..e3dfe7d97e 100644 --- a/framework/packages/abstract-adapter/Cargo.toml +++ b/framework/packages/abstract-adapter/Cargo.toml @@ -30,7 +30,7 @@ abstract-core = { workspace = true } abstract-testing = { workspace = true, optional = true } cw-orch = { workspace = true, optional = true } # Keep this as a version and update when publishing new versions -abstract-interface = { path = "../../packages/abstract-interface", version = "0.19.0", optional = true } +abstract-interface = { version = "0.19.0", optional = true } [dev-dependencies] speculoos = { workspace = true } diff --git a/framework/packages/staking/src/command.rs b/framework/packages/staking/src/command.rs index 01cb2e6c8c..35db301ad8 100644 --- a/framework/packages/staking/src/command.rs +++ b/framework/packages/staking/src/command.rs @@ -1,10 +1,10 @@ use crate::msg::{RewardTokensResponse, StakeResponse, StakingInfoResponse, UnbondingResponse}; use crate::{CwStakingError, Identify}; +use abstract_core::objects::AnsAsset; use abstract_sdk::core::objects::{AssetEntry, ContractEntry}; use abstract_sdk::feature_objects::{AnsHost, VersionControlContract}; use abstract_sdk::AbstractSdkResult; -use cosmwasm_std::{Addr, CosmosMsg, Deps, Env, QuerierWrapper, Uint128}; -use cw_utils::Duration; +use cosmwasm_std::{Addr, CosmosMsg, Deps, Env, QuerierWrapper}; use std::error::Error; /// Trait that defines the staking commands for providers @@ -39,23 +39,23 @@ pub trait CwStakingCommand: Identify { info: Option, ans_host: &AnsHost, version_control_contract: &VersionControlContract, - staking_asset: AssetEntry, + staking_assets: Vec, ) -> AbstractSdkResult<()>; /// Stake the provided asset into the staking contract fn stake( &self, deps: Deps, - amount: Uint128, - unbonding_period: Option, + stake_request: Vec, + unbonding_period: Option, ) -> Result, E>; /// Stake the provided asset into the staking contract fn unstake( &self, deps: Deps, - amount: Uint128, - unbonding_period: Option, + unstake_request: Vec, + unbonding_period: Option, ) -> Result, E>; /// Claim rewards on the staking contract @@ -74,7 +74,8 @@ pub trait CwStakingCommand: Identify { &self, querier: &QuerierWrapper, staker: Addr, - unbonding_period: Option, + stakes: Vec, + unbonding_period: Option, ) -> Result; /// Query information on unbonding positions for a given staker. diff --git a/framework/packages/staking/src/msg.rs b/framework/packages/staking/src/msg.rs index 89b57101c2..635216a9bb 100644 --- a/framework/packages/staking/src/msg.rs +++ b/framework/packages/staking/src/msg.rs @@ -41,21 +41,21 @@ pub struct StakingExecuteMsg { pub enum StakingAction { /// Stakes/bonds a given token Stake { - asset: AnsAsset, + assets: Vec, unbonding_period: Option, }, /// Unstake/unbond a given token Unstake { - asset: AnsAsset, + assets: Vec, unbonding_period: Option, }, /// Claim rewards for a given token - ClaimRewards { asset: AssetEntry }, + ClaimRewards { assets: Vec }, /// Claim matured unbonding tokens - Claim { asset: AssetEntry }, + Claim { assets: Vec }, } #[cosmwasm_schema::cw_serde] @@ -66,25 +66,25 @@ pub enum StakingQueryMsg { #[returns(StakingInfoResponse)] Info { provider: ProviderName, - staking_token: AssetEntry, + staking_tokens: Vec, }, #[returns(StakeResponse)] Staked { provider: ProviderName, - staking_token: AssetEntry, staker_address: String, unbonding_period: Option, + stakes: Vec, }, #[returns(UnbondingResponse)] Unbonding { provider: ProviderName, - staking_token: AssetEntry, staker_address: String, + staking_tokens: Vec, }, #[returns(RewardTokensResponse)] RewardTokens { provider: ProviderName, - staking_token: AssetEntry, + staking_tokens: Vec, }, } @@ -132,6 +132,11 @@ impl From for StakingTarget { #[cosmwasm_schema::cw_serde] pub struct StakingInfoResponse { + pub infos: Vec, +} + +#[cosmwasm_schema::cw_serde] +pub struct StakingInfo { pub staking_target: StakingTarget, pub staking_token: AssetInfo, pub unbonding_periods: Option>, @@ -140,17 +145,17 @@ pub struct StakingInfoResponse { #[cosmwasm_schema::cw_serde] pub struct StakeResponse { - pub amount: Uint128, + pub amounts: Vec, } #[cosmwasm_schema::cw_serde] pub struct RewardTokensResponse { - pub tokens: Vec, + pub tokens: Vec>, } #[cosmwasm_schema::cw_serde] pub struct UnbondingResponse { - pub claims: Vec, + pub claims: Vec>, } #[cosmwasm_schema::cw_serde] diff --git a/integrations/astroport/packages/abstract-adapter/src/staking.rs b/integrations/astroport/packages/abstract-adapter/src/staking.rs index b220f9464f..ba520bb127 100644 --- a/integrations/astroport/packages/abstract-adapter/src/staking.rs +++ b/integrations/astroport/packages/abstract-adapter/src/staking.rs @@ -4,23 +4,22 @@ use abstract_sdk::core::objects::LpToken; use abstract_staking_adapter_traits::Identify; use cosmwasm_std::Addr; -// TODO: use optional values here? #[derive(Clone, Debug)] pub struct Astroport { - #[allow(unused)] - lp_token: LpToken, - #[allow(unused)] - lp_token_address: Addr, - #[allow(unused)] - generator_contract_address: Addr, + pub tokens: Vec, +} + +#[derive(Clone, Debug)] +pub struct AstroportTokenContext { + pub lp_token: LpToken, + pub lp_token_address: Addr, + pub generator_contract_address: Addr, } impl Default for Astroport { fn default() -> Self { Self { - lp_token: Default::default(), - lp_token_address: Addr::unchecked(""), - generator_contract_address: Addr::unchecked(""), + tokens: Default::default(), } } } @@ -40,12 +39,12 @@ impl Identify for Astroport { #[cfg(feature = "full_integration")] use ::{ abstract_sdk::{ - core::objects::{AnsEntryConvertor, AssetEntry}, + core::objects::{AnsAsset, AnsEntryConvertor, AssetEntry}, feature_objects::{AnsHost, VersionControlContract}, AbstractSdkResult, Resolve, }, abstract_staking_adapter_traits::msg::{ - RewardTokensResponse, StakeResponse, StakingInfoResponse, UnbondingResponse, + RewardTokensResponse, StakeResponse, StakingInfo, StakingInfoResponse, UnbondingResponse, }, abstract_staking_adapter_traits::{CwStakingCommand, CwStakingError}, astroport::generator::{ @@ -57,7 +56,7 @@ use ::{ }, cw20::Cw20ExecuteMsg, cw_asset::AssetInfo, - cw_utils::Duration, + std::collections::{HashMap, HashSet}, }; #[cfg(feature = "full_integration")] @@ -69,54 +68,85 @@ impl CwStakingCommand for Astroport { _info: Option, ans_host: &AnsHost, _version_control_contract: &VersionControlContract, - lp_token: AssetEntry, + lp_tokens: Vec, ) -> AbstractSdkResult<()> { - self.generator_contract_address = - self.staking_contract_address(deps, ans_host, &lp_token)?; - - let AssetInfo::Cw20(token_addr) = lp_token.resolve(&deps.querier, ans_host)? else { - return Err(StdError::generic_err("expected CW20 as LP token for staking.").into()); - }; - self.lp_token_address = token_addr; - self.lp_token = AnsEntryConvertor::new(lp_token).lp_token()?; + self.tokens = lp_tokens + .into_iter() + .map(|entry| { + let generator_contract_address = + self.staking_contract_address(deps, ans_host, &entry)?; + + let AssetInfo::Cw20(token_addr) = entry.resolve(&deps.querier, ans_host)? else { + return Err( + StdError::generic_err("expected CW20 as LP token for staking.").into(), + ); + }; + + let lp_token_address = token_addr; + let lp_token = AnsEntryConvertor::new(entry.clone()).lp_token()?; + + Ok(AstroportTokenContext { + lp_token, + lp_token_address, + generator_contract_address, + }) + }) + .collect::>()?; Ok(()) } fn stake( &self, _deps: Deps, - amount: Uint128, - _unbonding_period: Option, + stake_request: Vec, + _unbonding_period: Option, ) -> Result, CwStakingError> { let msg = to_binary(&Cw20HookMsg::Deposit {})?; - Ok(vec![wasm_execute( - self.lp_token_address.to_string(), - &Cw20ExecuteMsg::Send { - contract: self.generator_contract_address.to_string(), - amount, - msg, - }, - vec![], - )? - .into()]) + + let stake_msgs = stake_request + .into_iter() + .zip(self.tokens.iter()) + .map(|(stake, token)| { + let msg: CosmosMsg = wasm_execute( + token.lp_token_address.to_string(), + &Cw20ExecuteMsg::Send { + contract: token.generator_contract_address.to_string(), + amount: stake.amount, + msg: msg.clone(), + }, + vec![], + )? + .into(); + Ok(msg) + }) + .collect::>()?; + + Ok(stake_msgs) } fn unstake( &self, _deps: Deps, - amount: Uint128, - _unbonding_period: Option, + unstake_request: Vec, + _unbonding_period: Option, ) -> Result, CwStakingError> { - let msg = GeneratorExecuteMsg::Withdraw { - lp_token: self.lp_token_address.to_string(), - amount, - }; - Ok(vec![wasm_execute( - self.generator_contract_address.to_string(), - &msg, - vec![], - )? - .into()]) + let unstake_msgs = unstake_request + .into_iter() + .zip(self.tokens.iter()) + .map(|(unstake, token)| { + let msg: CosmosMsg = wasm_execute( + token.generator_contract_address.to_string(), + &GeneratorExecuteMsg::Withdraw { + lp_token: token.lp_token_address.to_string(), + amount: unstake.amount, + }, + vec![], + )? + .into(); + Ok(msg) + }) + .collect::>()?; + Ok(unstake_msgs) } fn claim(&self, _deps: Deps) -> Result, CwStakingError> { @@ -124,71 +154,98 @@ impl CwStakingCommand for Astroport { } fn claim_rewards(&self, _deps: Deps) -> Result, CwStakingError> { - let msg = GeneratorExecuteMsg::ClaimRewards { - lp_tokens: vec![self.lp_token_address.clone().into()], - }; - - Ok(vec![wasm_execute( - self.generator_contract_address.to_string(), - &msg, - vec![], - )? - .into()]) + let mut claims: HashMap<&str, Vec> = HashMap::new(); + for token in &self.tokens { + claims + .entry(token.generator_contract_address.as_str()) + .and_modify(|tokens| tokens.push(token.lp_token_address.to_string())) + .or_insert(vec![token.lp_token_address.to_string()]); + } + let claim_msgs = claims + .into_iter() + .map(|(generator_addr, lp_tokens)| { + let msg: CosmosMsg = wasm_execute( + generator_addr.to_owned(), + &GeneratorExecuteMsg::ClaimRewards { lp_tokens }, + vec![], + )? + .into(); + Ok(msg) + }) + .collect::>()?; + Ok(claim_msgs) } fn query_info(&self, querier: &QuerierWrapper) -> Result { - let Config { astro_token, .. } = querier - .query_wasm_smart::( - self.generator_contract_address.clone(), - &GeneratorQueryMsg::Config {}, - ) - .map_err(|e| { - StdError::generic_err(format!( - "Failed to query staking info for {} with generator: {}, {:?}", - self.name(), - self.generator_contract_address.clone(), - e - )) - })?; - - let astro_token = match astro_token { - astroport::asset::AssetInfo::Token { contract_addr } => AssetInfo::cw20(contract_addr), - astroport::asset::AssetInfo::NativeToken { denom } => AssetInfo::native(denom), - }; - - Ok(StakingInfoResponse { - staking_target: self.generator_contract_address.clone().into(), - staking_token: astro_token, - unbonding_periods: None, - max_claims: None, - }) + let generator_addrs: HashSet<&Addr> = self + .tokens + .iter() + .map(|t| &t.generator_contract_address) + .collect(); + + let mut infos = Vec::with_capacity(generator_addrs.len()); + for g_addr in generator_addrs { + let Config { astro_token, .. } = querier + .query_wasm_smart::(g_addr.clone(), &GeneratorQueryMsg::Config {}) + .map_err(|e| { + StdError::generic_err(format!( + "Failed to query staking info for {} with generator: {}, {:?}", + self.name(), + g_addr.clone(), + e + )) + })?; + + let astro_token = match astro_token { + astroport::asset::AssetInfo::Token { contract_addr } => { + AssetInfo::cw20(contract_addr) + } + astroport::asset::AssetInfo::NativeToken { denom } => AssetInfo::native(denom), + }; + + infos.push(StakingInfo { + staking_target: g_addr.clone().into(), + staking_token: astro_token, + unbonding_periods: None, + max_claims: None, + }); + } + + Ok(StakingInfoResponse { infos }) } fn query_staked( &self, querier: &QuerierWrapper, staker: Addr, - _unbonding_period: Option, + _stakes: Vec, + _unbonding_period: Option, ) -> Result { - let stake_balance: Uint128 = querier - .query_wasm_smart( - self.generator_contract_address.clone(), - &GeneratorQueryMsg::Deposit { - lp_token: self.lp_token_address.to_string(), - user: staker.to_string(), - }, - ) - .map_err(|e| { - StdError::generic_err(format!( - "Failed to query staked balance on {} for {}. Error: {:?}", - self.name(), - staker, - e - )) - })?; - Ok(StakeResponse { - amount: stake_balance, - }) + let amounts = self + .tokens + .iter() + .map(|t| { + let stake_balance: Uint128 = querier + .query_wasm_smart( + t.generator_contract_address.clone(), + &GeneratorQueryMsg::Deposit { + lp_token: t.lp_token_address.to_string(), + user: staker.to_string(), + }, + ) + .map_err(|e| { + StdError::generic_err(format!( + "Failed to query staked balance on {} for {}. Error: {:?}", + self.name(), + staker, + e + )) + })?; + Ok(stake_balance) + }) + .collect::>()?; + + Ok(StakeResponse { amounts }) } fn query_unbonding( @@ -203,32 +260,42 @@ impl CwStakingCommand for Astroport { &self, querier: &QuerierWrapper, ) -> Result { - let reward_info: RewardInfoResponse = querier - .query_wasm_smart( - self.generator_contract_address.clone(), - &GeneratorQueryMsg::RewardInfo { - lp_token: self.lp_token_address.to_string(), - }, - ) - .map_err(|e| { - StdError::generic_err(format!( - "Failed to query reward info on {} for lp token {}. Error: {:?}", - self.name(), - self.lp_token, - e - )) - })?; - - let token = match reward_info.base_reward_token { - astroport::asset::AssetInfo::Token { contract_addr } => AssetInfo::cw20(contract_addr), - astroport::asset::AssetInfo::NativeToken { denom } => AssetInfo::native(denom), - }; - - let mut tokens = { vec![token] }; - - if let Some(reward_token) = reward_info.proxy_reward_token { - tokens.push(AssetInfo::cw20(reward_token)); - } + let tokens = self + .tokens + .iter() + .map(|t| { + let reward_info: RewardInfoResponse = querier + .query_wasm_smart( + t.generator_contract_address.clone(), + &GeneratorQueryMsg::RewardInfo { + lp_token: t.lp_token_address.to_string(), + }, + ) + .map_err(|e| { + StdError::generic_err(format!( + "Failed to query reward info on {} for lp token {}. Error: {:?}", + self.name(), + t.lp_token, + e + )) + })?; + + let token = match reward_info.base_reward_token { + astroport::asset::AssetInfo::Token { contract_addr } => { + AssetInfo::cw20(contract_addr) + } + astroport::asset::AssetInfo::NativeToken { denom } => AssetInfo::native(denom), + }; + + let mut tokens = vec![token]; + + if let Some(reward_token) = reward_info.proxy_reward_token { + tokens.push(AssetInfo::cw20(reward_token)); + } + Ok(tokens) + }) + .collect::>()?; + Ok(RewardTokensResponse { tokens }) } } diff --git a/integrations/kujira/Cargo.toml b/integrations/kujira/Cargo.toml index 049c799706..14c8888d28 100644 --- a/integrations/kujira/Cargo.toml +++ b/integrations/kujira/Cargo.toml @@ -1,2 +1,3 @@ [workspace] +resolver = "2" members = ["packages/*"] diff --git a/integrations/kujira/packages/abstract-adapter/src/staking.rs b/integrations/kujira/packages/abstract-adapter/src/staking.rs index 555869e383..079453fbb7 100644 --- a/integrations/kujira/packages/abstract-adapter/src/staking.rs +++ b/integrations/kujira/packages/abstract-adapter/src/staking.rs @@ -5,23 +5,18 @@ use cosmwasm_std::Addr; use crate::{AVAILABLE_CHAINS, KUJIRA}; // TODO: use optional values here? -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub struct Kujira { + pub tokens: Vec, +} + +#[derive(Clone, Debug)] +pub struct KujiraTokenContext { pub lp_token: LpToken, pub lp_token_denom: String, pub staking_contract_address: Addr, } -impl Default for Kujira { - fn default() -> Self { - Self { - lp_token: Default::default(), - lp_token_denom: "".to_string(), - staking_contract_address: Addr::unchecked(""), - } - } -} - impl Identify for Kujira { fn name(&self) -> &'static str { KUJIRA @@ -34,17 +29,16 @@ impl Identify for Kujira { #[cfg(feature = "full_integration")] use ::{ abstract_sdk::{ - core::objects::{AnsEntryConvertor, AssetEntry}, + core::objects::{AnsAsset, AnsEntryConvertor, AssetEntry}, feature_objects::{AnsHost, VersionControlContract}, AbstractSdkResult, Resolve, }, abstract_staking_adapter_traits::msg::{ RewardTokensResponse, StakeResponse, StakingInfoResponse, UnbondingResponse, }, - abstract_staking_adapter_traits::{CwStakingCommand, CwStakingError}, + abstract_staking_adapter_traits::{msg::StakingInfo, CwStakingCommand, CwStakingError}, cosmwasm_std::{wasm_execute, Coin, CosmosMsg, Deps, Env, QuerierWrapper, StdError, Uint128}, cw_asset::{AssetInfo, AssetInfoBase}, - cw_utils::Duration, kujira::bow::staking as BowStaking, }; @@ -57,55 +51,86 @@ impl CwStakingCommand for Kujira { _info: Option, ans_host: &AnsHost, _version_control_contract: &VersionControlContract, - lp_token: AssetEntry, + lp_tokens: Vec, ) -> AbstractSdkResult<()> { - self.staking_contract_address = self.staking_contract_address(deps, ans_host, &lp_token)?; + self.tokens = lp_tokens + .into_iter() + .map(|entry| { + let staking_contract_address = + self.staking_contract_address(deps, ans_host, &entry)?; + + let AssetInfoBase::Native(denom) = entry.resolve(&deps.querier, ans_host)? else { + return Err( + StdError::generic_err("expected denom as LP token for staking.").into(), + ); + }; + let lp_token_denom = denom; + let lp_token = AnsEntryConvertor::new(entry.clone()).lp_token()?; - let AssetInfoBase::Native(denom) = lp_token.resolve(&deps.querier, ans_host)? else { - return Err(StdError::generic_err("expected denom as LP token for staking.").into()); - }; - self.lp_token_denom = denom; + Ok(KujiraTokenContext { + lp_token, + lp_token_denom, + staking_contract_address, + }) + }) + .collect::>()?; - self.lp_token = AnsEntryConvertor::new(lp_token).lp_token()?; Ok(()) } fn stake( &self, _deps: Deps, - amount: Uint128, - _unbonding_period: Option, + stake_request: Vec, + _unbonding_period: Option, ) -> Result, CwStakingError> { let msg = BowStaking::ExecuteMsg::Stake { addr: None }; - Ok(vec![wasm_execute( - self.staking_contract_address.clone(), - &msg, - vec![Coin { - amount, - denom: self.lp_token_denom.clone(), - }], - )? - .into()]) + + let stake_msgs = stake_request + .into_iter() + .zip(self.tokens.iter()) + .map(|(stake, token)| { + let msg: CosmosMsg = wasm_execute( + token.staking_contract_address.clone(), + &msg, + vec![Coin { + amount: stake.amount, + denom: token.lp_token_denom.clone(), + }], + )? + .into(); + Ok(msg) + }) + .collect::>()?; + + Ok(stake_msgs) } fn unstake( &self, _deps: Deps, - amount: Uint128, - _unbonding_period: Option, + unstake_request: Vec, + _unbonding_period: Option, ) -> Result, CwStakingError> { - let msg = BowStaking::ExecuteMsg::Withdraw { - amount: Coin { - denom: self.lp_token_denom.clone(), - amount, - }, - }; - Ok(vec![wasm_execute( - self.staking_contract_address.clone(), - &msg, - vec![], - )? - .into()]) + let unstake_msgs = unstake_request + .into_iter() + .zip(self.tokens.iter()) + .map(|(unstake, token)| { + let msg: CosmosMsg = wasm_execute( + token.staking_contract_address.clone(), + &BowStaking::ExecuteMsg::Withdraw { + amount: Coin { + denom: token.lp_token_denom.clone(), + amount: unstake.amount, + }, + }, + vec![], + )? + .into(); + Ok(msg) + }) + .collect::>()?; + Ok(unstake_msgs) } fn claim(&self, _deps: Deps) -> Result, CwStakingError> { @@ -113,53 +138,75 @@ impl CwStakingCommand for Kujira { } fn claim_rewards(&self, _deps: Deps) -> Result, CwStakingError> { - let msg = BowStaking::ExecuteMsg::Claim { - denom: self.lp_token_denom.clone().into(), - }; - Ok(vec![wasm_execute( - self.staking_contract_address.clone(), - &msg, - vec![], - )? - .into()]) + let claim_msgs = self + .tokens + .iter() + .map(|t| { + let msg: CosmosMsg = wasm_execute( + t.staking_contract_address.clone(), + &BowStaking::ExecuteMsg::Claim { + denom: t.lp_token_denom.clone().into(), + }, + vec![], + )? + .into(); + Ok(msg) + }) + .collect::>()?; + + Ok(claim_msgs) } fn query_info(&self, _querier: &QuerierWrapper) -> Result { - let lp_token = AssetInfo::Native(self.lp_token_denom.clone()); - - Ok(StakingInfoResponse { - staking_target: self.staking_contract_address.clone().into(), - staking_token: lp_token, - unbonding_periods: None, - max_claims: None, - }) + let infos = self + .tokens + .iter() + .map(|t| { + let lp_token = AssetInfo::Native(t.lp_token_denom.clone()); + StakingInfo { + staking_target: t.staking_contract_address.clone().into(), + staking_token: lp_token, + unbonding_periods: None, + max_claims: None, + } + }) + .collect(); + + Ok(StakingInfoResponse { infos }) } fn query_staked( &self, querier: &QuerierWrapper, staker: Addr, - _unbonding_period: Option, + _stakes: Vec, + _unbonding_period: Option, ) -> Result { - let stake_response: BowStaking::StakeResponse = querier - .query_wasm_smart( - self.staking_contract_address.clone(), - &BowStaking::QueryMsg::Stake { - denom: self.lp_token_denom.clone().into(), - addr: staker.clone(), - }, - ) - .map_err(|e| { - StdError::generic_err(format!( - "Failed to query staked balance on {} for {}. Error: {:?}", - self.name(), - staker, - e - )) - })?; - Ok(StakeResponse { - amount: stake_response.amount, - }) + let amounts: Vec = self + .tokens + .iter() + .map(|t| { + let stake_response: BowStaking::StakeResponse = querier + .query_wasm_smart( + t.staking_contract_address.clone(), + &BowStaking::QueryMsg::Stake { + denom: t.lp_token_denom.clone().into(), + addr: staker.clone(), + }, + ) + .map_err(|e| { + StdError::generic_err(format!( + "Failed to query staked balance on {} for {}. Error: {:?}", + self.name(), + staker, + e + )) + })?; + Ok(stake_response.amount) + }) + .collect::>()?; + + Ok(StakeResponse { amounts }) } fn query_unbonding( @@ -174,34 +221,40 @@ impl CwStakingCommand for Kujira { &self, querier: &QuerierWrapper, ) -> Result { - let reward_info: BowStaking::IncentivesResponse = querier - .query_wasm_smart( - self.staking_contract_address.clone(), - &BowStaking::QueryMsg::Incentives { - denom: self.lp_token_denom.clone().into(), - start_after: None, - limit: None, - }, - ) - .map_err(|e| { - StdError::generic_err(format!( - "Failed to query reward info on {} for lp token {}. Error: {:?}", - self.name(), - self.lp_token, - e - )) - })?; - - let reward_tokens = reward_info - .incentives - .into_iter() - .map(|asset| { - let token = AssetInfo::Native(asset.denom.to_string()); - Result::<_, CwStakingError>::Ok(token) + let tokens = self + .tokens + .iter() + .map(|t| { + let reward_info: BowStaking::IncentivesResponse = querier + .query_wasm_smart( + t.staking_contract_address.clone(), + &BowStaking::QueryMsg::Incentives { + denom: t.lp_token_denom.clone().into(), + start_after: None, + limit: None, + }, + ) + .map_err(|e| { + StdError::generic_err(format!( + "Failed to query reward info on {} for lp token {}. Error: {:?}", + self.name(), + t.lp_token, + e + )) + })?; + + let reward_tokens = reward_info + .incentives + .into_iter() + .map(|asset| { + let token = AssetInfo::Native(asset.denom.to_string()); + Result::<_, CwStakingError>::Ok(token) + }) + .collect::>()?; + Ok(reward_tokens) }) - .collect::, _>>()?; - Ok(RewardTokensResponse { - tokens: reward_tokens, - }) + .collect::>()?; + + Ok(RewardTokensResponse { tokens }) } } diff --git a/integrations/osmosis-adapter/src/staking.rs b/integrations/osmosis-adapter/src/staking.rs index c826bdd110..e1d3190e11 100644 --- a/integrations/osmosis-adapter/src/staking.rs +++ b/integrations/osmosis-adapter/src/staking.rs @@ -8,8 +8,12 @@ use cosmwasm_std::Addr; pub struct Osmosis { pub version_control_contract: Option, pub local_proxy_addr: Option, - pub pool_id: Option, - pub lp_token: Option, + pub tokens: Vec, +} + +pub struct OsmosisTokenContext { + pub pool_id: u64, + pub lp_token: String, } impl Identify for Osmosis { @@ -26,14 +30,15 @@ pub mod fns { use abstract_sdk::features::AbstractRegistryAccess; use abstract_sdk::{AbstractSdkError, AccountVerification}; use abstract_staking_adapter_traits::msg::{ - Claim, RewardTokensResponse, StakeResponse, StakingInfoResponse, UnbondingResponse, + Claim, RewardTokensResponse, StakeResponse, StakingInfo, StakingInfoResponse, + UnbondingResponse, }; use cw_utils::Expiration; use osmosis_std::types::osmosis::lockup::{LockupQuerier, MsgBeginUnlockingAll}; use std::str::FromStr; use abstract_core::objects::ans_host::AnsHost; - use abstract_core::objects::{AnsEntryConvertor, AssetEntry, PoolReference}; + use abstract_core::objects::{AnsAsset, AnsEntryConvertor, AssetEntry, PoolReference}; use osmosis_std::types::osmosis::poolmanager::v1beta1::PoolmanagerQuerier; use abstract_sdk::AbstractSdkResult; @@ -68,30 +73,38 @@ pub mod fns { impl Osmosis { /// Take the staking asset and query the pool id via the ans host - pub fn query_pool_id_via_ans( + pub fn query_pool_tokens_via_ans( &self, querier: &QuerierWrapper, ans_host: &AnsHost, - staking_asset: AssetEntry, - ) -> AbstractSdkResult { - let dex_pair = - AnsEntryConvertor::new(AnsEntryConvertor::new(staking_asset).lp_token()?) - .dex_asset_pairing()?; - - let pool_ref = ans_host.query_asset_pairing(querier, &dex_pair)?; - // Currently takes the first pool found, but should be changed to take the best pool - let found: &PoolReference = pool_ref.first().ok_or(StdError::generic_err(format!( - "No pool found for asset pairing {:?}", - dex_pair - )))?; - - Ok(found.pool_address.expect_id()?) + staking_assets: Vec, + ) -> AbstractSdkResult> { + staking_assets + .into_iter() + .map(|s_asset| { + let dex_pair = + AnsEntryConvertor::new(AnsEntryConvertor::new(s_asset.clone()).lp_token()?) + .dex_asset_pairing()?; + + let pool_ref = ans_host.query_asset_pairing(querier, &dex_pair)?; + // Currently takes the first pool found, but should be changed to take the best pool + let found: &PoolReference = pool_ref.first().ok_or(StdError::generic_err( + format!("No pool found for asset pairing {:?}", dex_pair), + ))?; + + let pool_id = found.pool_address.expect_id()?; + let lp_token = format!("gamm/pool/{pool_id}"); + Ok(OsmosisTokenContext { pool_id, lp_token }) + }) + .collect() } + } + impl OsmosisTokenContext { pub fn query_pool_data(&self, querier: &QuerierWrapper) -> StdResult { let querier = PoolmanagerQuerier::new(querier); - let res = querier.pool(self.pool_id.unwrap())?; + let res = querier.pool(self.pool_id)?; let pool = Pool::try_from(res.pool.unwrap()).unwrap(); Ok(pool) @@ -121,7 +134,7 @@ pub mod fns { info: Option, ans_host: &AnsHost, version_control_contract: &VersionControlContract, - staking_asset: AssetEntry, + staking_assets: Vec, ) -> abstract_sdk::AbstractSdkResult<()> { self.version_control_contract = Some(version_control_contract.clone()); let account_registry = self.account_registry(deps); @@ -131,10 +144,8 @@ pub mod fns { .transpose()?; self.local_proxy_addr = base.map(|b| b.proxy); - let pool_id = self.query_pool_id_via_ans(&deps.querier, ans_host, staking_asset)?; - - self.pool_id = Some(pool_id); - self.lp_token = Some(format!("gamm/pool/{}", self.pool_id.unwrap())); + self.tokens = + self.query_pool_tokens_via_ans(&deps.querier, ans_host, staking_assets)?; Ok(()) } @@ -142,17 +153,24 @@ pub mod fns { fn stake( &self, _deps: Deps, - amount: Uint128, + stake_request: Vec, unbonding_period: Option, ) -> Result, CwStakingError> { + let lock_coins: Vec<_> = stake_request + .into_iter() + .zip(self.tokens.iter()) + .map(|(stake, token)| { + Coin { + amount: stake.amount, + denom: token.lp_token.clone(), + } + .into() + }) + .collect(); let lock_tokens_msg = MsgLockTokens { owner: self.local_proxy_addr.as_ref().unwrap().to_string(), duration: to_osmo_duration(unbonding_period)?, - coins: vec![Coin { - amount, - denom: self.lp_token.clone().unwrap(), - } - .into()], + coins: lock_coins, }; Ok(vec![lock_tokens_msg.into()]) @@ -161,19 +179,27 @@ pub mod fns { fn unstake( &self, _deps: Deps, - amount: Uint128, + unstake_request: Vec, _unbonding_period: Option, ) -> Result, CwStakingError> { - let msg = MsgBeginUnlocking { - owner: self.local_proxy_addr.as_ref().unwrap().to_string(), - id: self.pool_id.unwrap(), - coins: vec![Coin { - denom: self.lp_token.clone().unwrap(), - amount, - } - .into()], - }; - Ok(vec![msg.into()]) + let unstake_msgs: Vec<_> = unstake_request + .into_iter() + .zip(self.tokens.iter()) + .map(|(unstake, token)| { + MsgBeginUnlocking { + owner: self.local_proxy_addr.as_ref().unwrap().to_string(), + id: token.pool_id, + coins: vec![Coin { + denom: token.lp_token.clone(), + amount: unstake.amount, + } + .into()], + } + .into() + }) + .collect(); + + Ok(unstake_msgs) } fn claim(&self, _deps: Deps) -> Result, CwStakingError> { @@ -200,35 +226,44 @@ pub mod fns { &self, _querier: &cosmwasm_std::QuerierWrapper, ) -> Result { - let res = StakingInfoResponse { - staking_token: AssetInfoBase::Native(self.lp_token.clone().unwrap()), - staking_target: self.pool_id.clone().unwrap().into(), - unbonding_periods: Some(vec![]), - max_claims: None, - }; + let infos = self + .tokens + .iter() + .map(|t| StakingInfo { + staking_token: AssetInfoBase::Native(t.lp_token.clone()), + staking_target: t.pool_id.into(), + unbonding_periods: Some(vec![]), + max_claims: None, + }) + .collect(); - Ok(res) + Ok(StakingInfoResponse { infos }) } fn query_staked( &self, - _querier: &QuerierWrapper, - _staker: Addr, + querier: &QuerierWrapper, + staker: Addr, + _stakes: Vec, _unbonding_period: Option, ) -> Result { - Err(CwStakingError::NotImplemented("osmosis".to_owned())) - // TODO: whitelist for contracts - // let lockup_request = LockupQuerier::new(querier); - // let locked_up = lockup_request.account_locked_coins(staker.to_string())?; - - // let amount = locked_up - // .coins - // .into_iter() - // .filter(|coin| coin.denom == self.lp_token.clone().unwrap()) - // .map(|lock| Uint128::from_str(&lock.amount).unwrap()) - // .sum(); - - // Ok(StakeResponse { amount }) + let lockup_request = LockupQuerier::new(querier); + let locked_up = lockup_request.account_locked_coins(staker.to_string())?; + + let amounts = self + .tokens + .iter() + .map(|token| { + locked_up + .coins + .iter() + .find(|&c| c.denom == token.lp_token) + .map(|c| Uint128::from_str(&c.amount).unwrap()) + .unwrap_or_default() + }) + .collect(); + + Ok(StakeResponse { amounts }) } fn query_unbonding( @@ -237,18 +272,27 @@ pub mod fns { staker: Addr, ) -> Result { let lockup_request = LockupQuerier::new(querier); - let unlocking = lockup_request + let unlock_coins = lockup_request .account_unlocking_coins(staker.to_string())? - .coins - .into_iter() - .filter(|coin| coin.denom == self.lp_token.clone().unwrap()) - .map(|lock| Claim { - amount: Uint128::from_str(&lock.amount).unwrap(), - claimable_at: Expiration::Never {}, + .coins; + let claims = self + .tokens + .iter() + .map(|token| { + unlock_coins + .iter() + .find(|&c| c.denom == token.lp_token) + .map(|c| { + vec![Claim { + amount: Uint128::from_str(&c.amount).unwrap(), + claimable_at: Expiration::Never {}, + }] + }) + .unwrap_or_default() }) .collect(); - Ok(UnbondingResponse { claims: unlocking }) + Ok(UnbondingResponse { claims }) } fn query_rewards( diff --git a/integrations/wyndex/contracts/lp-converter/src/multitest/migrate_stake.rs b/integrations/wyndex/contracts/lp-converter/src/multitest/migrate_stake.rs index 7187f55d15..a639e61d3b 100644 --- a/integrations/wyndex/contracts/lp-converter/src/multitest/migrate_stake.rs +++ b/integrations/wyndex/contracts/lp-converter/src/multitest/migrate_stake.rs @@ -224,10 +224,7 @@ fn empty_stake_fails() { let err = suite .migrate_stake(Pair::Native, user, 0, unbonding_period) .unwrap_err(); - assert_eq!( - cw20_base::ContractError::InvalidZeroAmount {}, - err.downcast().unwrap() - ); + assert!(err.root_cause().to_string().contains("empty coins")); // migrating more stake than available should fail suite diff --git a/integrations/wyndex/contracts/stake/src/distribution.rs b/integrations/wyndex/contracts/stake/src/distribution.rs index 7206ecfd21..c177dd75f7 100644 --- a/integrations/wyndex/contracts/stake/src/distribution.rs +++ b/integrations/wyndex/contracts/stake/src/distribution.rs @@ -190,7 +190,7 @@ pub fn query_withdrawable_rewards( ) -> StdResult { // Not checking address, as if it is invalid it is guaranteed not to appear in maps, so // `withdrawable_rewards` would return error itself. - let owner = Addr::unchecked(&owner); + let owner = Addr::unchecked(owner); let cfg = CONFIG.load(deps.storage)?; let distributions = diff --git a/integrations/wyndex/contracts/stake/src/state.rs b/integrations/wyndex/contracts/stake/src/state.rs index 17cb2b8202..71fe4906b6 100644 --- a/integrations/wyndex/contracts/stake/src/state.rs +++ b/integrations/wyndex/contracts/stake/src/state.rs @@ -251,7 +251,7 @@ impl Distribution { let totals = TOTAL_PER_PERIOD.load(storage).unwrap_or_default(); self.reward_multipliers .iter() - .zip(totals.into_iter()) + .zip(totals) .map( |(&(unbonding_period, multiplier), (unbonding_period2, total_stake))| { // sanity check diff --git a/integrations/wyndex/packages/wyndex-adapter/src/staking.rs b/integrations/wyndex/packages/wyndex-adapter/src/staking.rs index e39af530ec..c3f0a4ec49 100644 --- a/integrations/wyndex/packages/wyndex-adapter/src/staking.rs +++ b/integrations/wyndex/packages/wyndex-adapter/src/staking.rs @@ -2,27 +2,19 @@ use crate::AVAILABLE_CHAINS; pub use crate::WYNDEX; use abstract_sdk::core::objects::LpToken; use abstract_staking_adapter_traits::Identify; -use cosmwasm_std::{Addr, Env}; +use cosmwasm_std::Addr; -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Default)] pub struct WynDex { - lp_token: LpToken, - lp_token_address: Addr, - staking_contract_address: Addr, - ans_host: Addr, - env: Option, + pub tokens: Vec, + pub ans_host: Option, } -impl Default for WynDex { - fn default() -> Self { - Self { - lp_token: Default::default(), - lp_token_address: Addr::unchecked(""), - staking_contract_address: Addr::unchecked(""), - ans_host: Addr::unchecked(""), - env: None, - } - } +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct WynDexTokenContext { + pub lp_token: LpToken, + pub lp_token_address: Addr, + pub staking_contract_address: Addr, } impl Identify for WynDex { @@ -37,12 +29,13 @@ impl Identify for WynDex { #[cfg(feature = "full_integration")] use { abstract_sdk::{ - core::objects::{AnsEntryConvertor, AssetEntry}, + core::objects::{AnsAsset, AnsEntryConvertor, AssetEntry}, feature_objects::{AnsHost, VersionControlContract}, AbstractSdkError, Resolve, }, abstract_staking_adapter_traits::msg::{ - Claim, RewardTokensResponse, StakeResponse, StakingInfoResponse, UnbondingResponse, + Claim, RewardTokensResponse, StakeResponse, StakingInfo, StakingInfoResponse, + UnbondingResponse, }, abstract_staking_adapter_traits::CwStakingCommand, abstract_staking_adapter_traits::CwStakingError, @@ -66,28 +59,41 @@ impl CwStakingCommand for WynDex { fn fetch_data( &mut self, deps: Deps, - env: Env, + _env: cosmwasm_std::Env, _info: Option, ans_host: &AnsHost, _version_control_contract: &VersionControlContract, - lp_token: AssetEntry, + lp_tokens: Vec, ) -> std::result::Result<(), AbstractSdkError> { - self.staking_contract_address = self.staking_contract_address(deps, ans_host, &lp_token)?; + self.tokens = lp_tokens + .into_iter() + .map(|entry| { + let staking_contract_address = + self.staking_contract_address(deps, ans_host, &entry)?; + let AssetInfoBase::Cw20(lp_token_address) = + entry.resolve(&deps.querier, ans_host)? + else { + return Err( + StdError::generic_err("expected CW20 as LP token for staking.").into(), + ); + }; - let AssetInfoBase::Cw20(token_addr) = lp_token.resolve(&deps.querier, ans_host)? else { - return Err(StdError::generic_err("expected CW20 as LP token for staking.").into()); - }; - self.lp_token_address = token_addr; + let lp_token = AnsEntryConvertor::new(entry.clone()).lp_token()?; + Ok(WynDexTokenContext { + lp_token, + lp_token_address, + staking_contract_address, + }) + }) + .collect::>()?; - self.lp_token = AnsEntryConvertor::new(lp_token).lp_token()?; - self.env = Some(env); Ok(()) } fn stake( &self, _deps: Deps, - amount: Uint128, + stake_request: Vec, unbonding_period: Option, ) -> Result, CwStakingError> { let unbonding_period = unwrap_unbond(self, unbonding_period)?; @@ -95,105 +101,151 @@ impl CwStakingCommand for WynDex { unbonding_period, delegate_as: None, })?; - Ok(vec![CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: self.lp_token_address.to_string(), - msg: to_binary(&Cw20ExecuteMsg::Send { - contract: self.staking_contract_address.to_string(), - amount, - msg, - })?, - funds: vec![], - })]) + let stake_msgs = stake_request + .into_iter() + .zip(self.tokens.iter()) + .map(|(stake, token)| { + Ok(CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: token.lp_token_address.to_string(), + msg: to_binary(&Cw20ExecuteMsg::Send { + contract: token.staking_contract_address.to_string(), + amount: stake.amount, + msg: msg.clone(), + })?, + funds: vec![], + })) + }) + .collect::>()?; + + Ok(stake_msgs) } fn unstake( &self, _deps: Deps, - amount: Uint128, + unstake_request: Vec, unbonding_period: Option, ) -> Result, CwStakingError> { let unbonding_period = unwrap_unbond(self, unbonding_period)?; - let msg = StakeCw20ExecuteMsg::Unbond { - tokens: amount, - unbonding_period, - }; - Ok(vec![CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: self.staking_contract_address.to_string(), - msg: to_binary(&msg)?, - funds: vec![], - })]) + let unstake_msgs = unstake_request + .into_iter() + .zip(self.tokens.iter()) + .map(|(unstake, token)| { + Ok(CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: token.staking_contract_address.to_string(), + msg: to_binary(&StakeCw20ExecuteMsg::Unbond { + tokens: unstake.amount, + unbonding_period, + })?, + funds: vec![], + })) + }) + .collect::>()?; + + Ok(unstake_msgs) } fn claim(&self, _deps: Deps) -> Result, CwStakingError> { - let msg = StakeCw20ExecuteMsg::Claim {}; + let msg = to_binary(&StakeCw20ExecuteMsg::Claim {})?; - Ok(vec![CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: self.staking_contract_address.to_string(), - msg: to_binary(&msg)?, - funds: vec![], - })]) + let claim_msgs = self + .tokens + .iter() + .map(|t| { + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: t.staking_contract_address.to_string(), + msg: msg.clone(), + funds: vec![], + }) + }) + .collect(); + Ok(claim_msgs) } fn claim_rewards(&self, _deps: Deps) -> Result, CwStakingError> { - let msg = StakeCw20ExecuteMsg::WithdrawRewards { + let msg = to_binary(&StakeCw20ExecuteMsg::WithdrawRewards { owner: None, receiver: None, - }; - Ok(vec![CosmosMsg::Wasm(WasmMsg::Execute { - contract_addr: self.staking_contract_address.to_string(), - msg: to_binary(&msg)?, - funds: vec![], - })]) + })?; + + let claim_msgs = self + .tokens + .iter() + .map(|t| { + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: t.staking_contract_address.to_string(), + msg: msg.clone(), + funds: vec![], + }) + }) + .collect(); + Ok(claim_msgs) } fn query_info(&self, querier: &QuerierWrapper) -> StakingResult { - let bonding_info_resp: BondingInfoResponse = querier.query_wasm_smart( - self.staking_contract_address.clone(), - &wyndex_stake::msg::QueryMsg::BondingInfo {}, - )?; + let infos = self + .tokens + .iter() + .map(|t| { + let bonding_info_resp: BondingInfoResponse = querier.query_wasm_smart( + t.staking_contract_address.clone(), + &wyndex_stake::msg::QueryMsg::BondingInfo {}, + )?; + Ok(StakingInfo { + staking_target: t.staking_contract_address.clone().into(), + staking_token: AssetInfo::Cw20(t.lp_token_address.clone()), + unbonding_periods: Some( + bonding_info_resp + .bonding + .into_iter() + .map(|bond_period| Duration::Time(bond_period.unbonding_period)) + .collect(), + ), + max_claims: None, + }) + }) + .collect::>()?; - Ok(StakingInfoResponse { - staking_target: self.staking_contract_address.clone().into(), - staking_token: AssetInfo::Cw20(self.lp_token_address.clone()), - unbonding_periods: Some( - bonding_info_resp - .bonding - .into_iter() - .map(|bond_period| Duration::Time(bond_period.unbonding_period)) - .collect(), - ), - max_claims: None, - }) + Ok(StakingInfoResponse { infos }) } fn query_staked( &self, querier: &QuerierWrapper, staker: Addr, + _stakes: Vec, unbonding_period: Option, ) -> StakingResult { let unbonding_period = unwrap_unbond(self, unbonding_period) .map_err(|e| StdError::generic_err(e.to_string()))?; - // Raw query because the smart-query returns staked + currently unbonding tokens, which is not what we want. - // we want the actual staked token balance. - let stake_balance_res: Result, _> = STAKE.query( - querier, - self.staking_contract_address.clone(), - (&staker, unbonding_period), - ); - let stake_balance_info = stake_balance_res.map_err(|e| { - StdError::generic_err(format!( - "Raw query for wynddex stake balance failed. Error: {e:?}" - )) - })?; + let amounts = self + .tokens + .iter() + .map(|token| { + // Raw query because the smart-query returns staked + currently unbonding tokens, which is not what we want. + // we want the actual staked token balance. + let stake_balance_res: Result, _> = STAKE.query( + querier, + token.staking_contract_address.clone(), + (&staker, unbonding_period), + ); + let stake_balance_info = stake_balance_res.map_err(|e| { + StdError::generic_err(format!( + "Raw query for wynddex stake balance failed. Error: {e:?}" + )) + })?; + + let amount = if let Some(bonding_info) = stake_balance_info { + bonding_info.total_stake() + } else { + Uint128::zero() + }; + Ok(amount) + }) + .collect::>()?; - let amount = if let Some(bonding_info) = stake_balance_info { - bonding_info.total_stake() - } else { - Uint128::zero() - }; - Ok(StakeResponse { amount }) + Ok(StakeResponse { amounts }) } fn query_unbonding( @@ -201,56 +253,72 @@ impl CwStakingCommand for WynDex { querier: &QuerierWrapper, staker: Addr, ) -> StakingResult { - let claims: cw_controllers::ClaimsResponse = querier.query_wasm_smart( - self.staking_contract_address.clone(), - &wyndex_stake::msg::QueryMsg::Claims { - address: staker.into_string(), - }, - )?; - let claims = claims - .claims + let claims = self + .tokens .iter() - .map(|claim| Claim { - amount: claim.amount, - claimable_at: claim.release_at, + .map(|token| { + let claims: cw_controllers::ClaimsResponse = querier.query_wasm_smart( + token.staking_contract_address.clone(), + &wyndex_stake::msg::QueryMsg::Claims { + address: staker.to_string(), + }, + )?; + let claims: Vec<_> = claims + .claims + .iter() + .map(|claim| Claim { + amount: claim.amount, + claimable_at: claim.release_at, + }) + .collect(); + Ok(claims) }) - .collect(); + .collect::>()?; + Ok(UnbondingResponse { claims }) } fn query_rewards(&self, querier: &QuerierWrapper) -> StakingResult { - let resp: DistributionDataResponse = querier.query_wasm_smart( - self.staking_contract_address.clone(), - &wyndex_stake::msg::QueryMsg::DistributionData {}, - )?; - let reward_tokens = resp - .distributions - .into_iter() - .map(|(asset, _)| { - let token = match asset { - wyndex::asset::AssetInfoValidated::Native(denom) => AssetInfo::Native(denom), - wyndex::asset::AssetInfoValidated::Token(token) => AssetInfo::Cw20(token), - }; - Result::<_, CwStakingError>::Ok(token) + let tokens = self + .tokens + .iter() + .map(|t| { + let resp: DistributionDataResponse = querier.query_wasm_smart( + t.staking_contract_address.clone(), + &wyndex_stake::msg::QueryMsg::DistributionData {}, + )?; + + let reward_tokens = resp + .distributions + .into_iter() + .map(|(asset, _)| { + let token = match asset { + wyndex::asset::AssetInfoValidated::Native(denom) => { + AssetInfo::Native(denom) + } + wyndex::asset::AssetInfoValidated::Token(token) => { + AssetInfo::Cw20(token) + } + }; + Ok(token) + }) + .collect::>()?; + Ok(reward_tokens) }) - .collect::, _>>()?; - Ok(RewardTokensResponse { - tokens: reward_tokens, - }) + .collect::>()?; + Ok(RewardTokensResponse { tokens }) } } #[cfg(feature = "full_integration")] fn unwrap_unbond(dex: &WynDex, unbonding_period: Option) -> Result { - let Some(Duration::Time(unbonding_period)) = unbonding_period else { - if unbonding_period.is_none() { - return Err(CwStakingError::UnbondingPeriodNotSet(dex.name().to_owned())); - } else { - return Err(CwStakingError::UnbondingPeriodNotSupported( - "height".to_owned(), - dex.name().to_owned(), - )); - } - }; - Ok(unbonding_period) + match unbonding_period { + // Only time supported for unbonding + Some(Duration::Time(unbonding_period)) => Ok(unbonding_period), + Some(Duration::Height(_)) => Err(CwStakingError::UnbondingPeriodNotSupported( + "height".to_owned(), + dex.name().to_owned(), + )), + None => Err(CwStakingError::UnbondingPeriodNotSet(dex.name().to_owned())), + } } diff --git a/modules/artifacts/abstract_cw_staking-juno.wasm b/modules/artifacts/abstract_cw_staking-juno.wasm new file mode 100644 index 0000000000..07b3a6077a Binary files /dev/null and b/modules/artifacts/abstract_cw_staking-juno.wasm differ diff --git a/modules/artifacts/abstract_cw_staking-kujira.wasm b/modules/artifacts/abstract_cw_staking-kujira.wasm new file mode 100644 index 0000000000..ab122430c2 Binary files /dev/null and b/modules/artifacts/abstract_cw_staking-kujira.wasm differ diff --git a/modules/artifacts/abstract_cw_staking-neutron.wasm b/modules/artifacts/abstract_cw_staking-neutron.wasm new file mode 100644 index 0000000000..09a1143c19 Binary files /dev/null and b/modules/artifacts/abstract_cw_staking-neutron.wasm differ diff --git a/modules/artifacts/abstract_cw_staking-osmosis.wasm b/modules/artifacts/abstract_cw_staking-osmosis.wasm new file mode 100644 index 0000000000..29a6e35bd2 Binary files /dev/null and b/modules/artifacts/abstract_cw_staking-osmosis.wasm differ diff --git a/modules/artifacts/abstract_cw_staking-terra.wasm b/modules/artifacts/abstract_cw_staking-terra.wasm new file mode 100644 index 0000000000..71a6726ec9 Binary files /dev/null and b/modules/artifacts/abstract_cw_staking-terra.wasm differ diff --git a/modules/artifacts/abstract_cw_staking.wasm b/modules/artifacts/abstract_cw_staking.wasm new file mode 100644 index 0000000000..93649c678c Binary files /dev/null and b/modules/artifacts/abstract_cw_staking.wasm differ diff --git a/modules/artifacts/abstract_dex_adapter-juno.wasm b/modules/artifacts/abstract_dex_adapter-juno.wasm new file mode 100644 index 0000000000..23b8658c36 Binary files /dev/null and b/modules/artifacts/abstract_dex_adapter-juno.wasm differ diff --git a/modules/artifacts/abstract_dex_adapter-kujira.wasm b/modules/artifacts/abstract_dex_adapter-kujira.wasm new file mode 100644 index 0000000000..786315e2c0 Binary files /dev/null and b/modules/artifacts/abstract_dex_adapter-kujira.wasm differ diff --git a/modules/artifacts/abstract_dex_adapter-neutron.wasm b/modules/artifacts/abstract_dex_adapter-neutron.wasm new file mode 100644 index 0000000000..05a0ff83cf Binary files /dev/null and b/modules/artifacts/abstract_dex_adapter-neutron.wasm differ diff --git a/modules/artifacts/abstract_dex_adapter-terra.wasm b/modules/artifacts/abstract_dex_adapter-terra.wasm new file mode 100644 index 0000000000..2f7f8bd13e Binary files /dev/null and b/modules/artifacts/abstract_dex_adapter-terra.wasm differ diff --git a/modules/artifacts/checksums.txt b/modules/artifacts/checksums.txt new file mode 100644 index 0000000000..b7ddb9e07f --- /dev/null +++ b/modules/artifacts/checksums.txt @@ -0,0 +1,17 @@ +c9a458fd09568cfb0d0333b8af93dc957af83f77445139f8d872796aff1ae6a8 abstract_challenge_app.wasm +73c1615a33238981a9ee9fecfae43c69257c778a8482d9ab4fc7db3b97fd3e2c abstract_cw_staking-juno.wasm +024e36c4279b39e85c8d42014bafc06270a10bc1a25f18d2ce667498a0f02f46 abstract_cw_staking-kujira.wasm +11db040f736e857c9e223e23f2b2b05de864b1452f92b5e77863af4cf3222de0 abstract_cw_staking-neutron.wasm +0ac9cf8ea16116b12a77b91e77908fd7a38cfd624f1bdad5b92065191f9823d6 abstract_cw_staking-osmosis.wasm +f7acb8a6c34a5ca16c78468b58b3ceaee3845c3daabd03a5c69e92caa6543674 abstract_cw_staking-terra.wasm +73c8a33f3ccc9bdff14e0a6c4640c1c8b59e86d46c526a9584807b584cfbb92d abstract_cw_staking.wasm +85afe226a62e04eb4d33de61c1f2e713409051dfa496b7a0ba832685b4db569a abstract_dca_app.wasm +671d167c6c67e661b6e3ab0eb1d635dee3a8401ced2db54281b0d77cb8149f06 abstract_dex_adapter-juno.wasm +7b1d3fac951010973c23c167f8b65a1dd317cbdcade34e5ddd014d25b95a1b55 abstract_dex_adapter-kujira.wasm +02e51dff329df58d8306c8cda811959d5d7974c1804361dcbcafe67a187c8d1d abstract_dex_adapter-neutron.wasm +b9bea61c9b4fe24c6f569dd3e1e5ac5e22f57c24fe51a0c8c1ad3419650fb169 abstract_dex_adapter-osmosis.wasm +316f7b9c1b3558379c8c874e8ce6c6d5ce546132d06de65688e275dc3e5e062c abstract_dex_adapter-terra.wasm +4b4db0743a2a6f8a73bb0f0c63753b0d22b6a3dc9c212e1861c9cbb14c64d14d abstract_dex_adapter.wasm +10fe217efe34e6fca44a47ec30a24afa3617a838139167fa8bbf7afca7961137 abstract_etf.wasm +0c3cb751fe7f9ed27f1fdde47e699686b24e2491b185c607303f73b713d24642 abstract_tendermint_staking_adapter.wasm +105469894506e88b255fcad9fefcdcc790fdad412f408d89559ad789d99d5f8b croncat_app.wasm diff --git a/modules/artifacts/checksums_intermediate.txt b/modules/artifacts/checksums_intermediate.txt new file mode 100644 index 0000000000..1571a84394 --- /dev/null +++ b/modules/artifacts/checksums_intermediate.txt @@ -0,0 +1,7 @@ +f5f3c298d485ce5c357e2eec0d3e412e21920e0d583eb5e8a2ee14660c8aee4c target/wasm32-unknown-unknown/release/abstract_challenge_app.wasm +2c1b36678582171781bd17cbc01f8041d5e1abd363538ea14c83c0fc08e37c5a target/wasm32-unknown-unknown/release/abstract_cw_staking.wasm +4c76612139dd760cfd8a8438dc8e4bf5fdb480272f2bcdbc0a4669a184e35c5c target/wasm32-unknown-unknown/release/abstract_dca_app.wasm +e0798cb4a5756723c6545623ac2b10ff7af049993c24e709709dbf0ddc04558b target/wasm32-unknown-unknown/release/abstract_dex_adapter.wasm +0e93825a3741d8db1d05624131f0f2524043917cf814bc3b4ef61087541b1835 target/wasm32-unknown-unknown/release/abstract_etf.wasm +9c2bd81fcad35d07e861e259ce3a742ef5a009464e57f624f3376fb23d08c108 target/wasm32-unknown-unknown/release/abstract_tendermint_staking_adapter.wasm +600ae906e57c77ea8ed9308dffca0ce91e24fc199a82554f6307cd561104fd1d target/wasm32-unknown-unknown/release/croncat_app.wasm diff --git a/modules/contracts/adapters/cw-staking/src/adapter.rs b/modules/contracts/adapters/cw-staking/src/adapter.rs index 32002ec90e..338b8d02f7 100644 --- a/modules/contracts/adapters/cw-staking/src/adapter.rs +++ b/modules/contracts/adapters/cw-staking/src/adapter.rs @@ -25,7 +25,7 @@ pub trait CwStakingAdapter: AbstractNameService + AbstractRegistryAccess + Execu action: StakingAction, mut provider: Box, ) -> Result { - let staking_asset = staking_asset_from_action(&action); + let staking_asset = staking_assets_from_action(&action); provider.fetch_data( deps.as_ref(), @@ -38,15 +38,15 @@ pub trait CwStakingAdapter: AbstractNameService + AbstractRegistryAccess + Execu let msgs = match action { StakingAction::Stake { - asset: staking_token, + assets, unbonding_period, - } => provider.stake(deps.as_ref(), staking_token.amount, unbonding_period)?, + } => provider.stake(deps.as_ref(), assets, unbonding_period)?, StakingAction::Unstake { - asset: staking_token, + assets, unbonding_period, - } => provider.unstake(deps.as_ref(), staking_token.amount, unbonding_period)?, - StakingAction::ClaimRewards { asset: _ } => provider.claim_rewards(deps.as_ref())?, - StakingAction::Claim { asset: _ } => provider.claim(deps.as_ref())?, + } => provider.unstake(deps.as_ref(), assets, unbonding_period)?, + StakingAction::ClaimRewards { assets: _ } => provider.claim_rewards(deps.as_ref())?, + StakingAction::Claim { assets: _ } => provider.claim(deps.as_ref())?, }; self.executor(deps.as_ref()) @@ -57,21 +57,21 @@ pub trait CwStakingAdapter: AbstractNameService + AbstractRegistryAccess + Execu } #[inline(always)] -fn staking_asset_from_action(action: &StakingAction) -> AssetEntry { +fn staking_assets_from_action(action: &StakingAction) -> Vec { match action { StakingAction::Stake { - asset: staking_token, + assets: staking_tokens, .. - } => staking_token.name.clone(), + } => staking_tokens.iter().map(|req| req.name.clone()).collect(), StakingAction::Unstake { - asset: staking_token, + assets: staking_tokens, .. - } => staking_token.name.clone(), + } => staking_tokens.iter().map(|req| req.name.clone()).collect(), StakingAction::ClaimRewards { - asset: staking_token, - } => staking_token.clone(), + assets: staking_tokens, + } => staking_tokens.clone(), StakingAction::Claim { - asset: staking_token, + assets: staking_token, } => staking_token.clone(), } } diff --git a/modules/contracts/adapters/cw-staking/src/handlers/execute.rs b/modules/contracts/adapters/cw-staking/src/handlers/execute.rs index 4fefbabf79..b36efbea1d 100644 --- a/modules/contracts/adapters/cw-staking/src/handlers/execute.rs +++ b/modules/contracts/adapters/cw-staking/src/handlers/execute.rs @@ -97,12 +97,13 @@ fn resolve_assets_to_transfer( ans_host: &AnsHost, ) -> StakingResult> { match dex_action { - StakingAction::Stake { - asset: staking_token, - .. - } => { - let resolved: Coin = staking_token.resolve(&deps.querier, ans_host)?.try_into()?; - Ok(vec![resolved]) + StakingAction::Stake { assets, .. } => { + let resolved: Vec = assets + .resolve(&deps.querier, ans_host)? + .into_iter() + .map(Coin::try_from) + .collect::>()?; + Ok(resolved) } _ => Ok(vec![]), } diff --git a/modules/contracts/adapters/cw-staking/src/handlers/query.rs b/modules/contracts/adapters/cw-staking/src/handlers/query.rs index cb46448ecc..df14cf1751 100644 --- a/modules/contracts/adapters/cw-staking/src/handlers/query.rs +++ b/modules/contracts/adapters/cw-staking/src/handlers/query.rs @@ -20,7 +20,7 @@ pub fn query_handler( match msg { StakingQueryMsg::Info { provider, - staking_token, + staking_tokens, } => { // if provider is on an app-chain, error let (local_provider_name, is_over_ibc) = is_over_ibc(env.clone(), &provider)?; @@ -36,17 +36,18 @@ pub fn query_handler( None, ans_host, &version_control_contract, - staking_token, + staking_tokens, )?; Ok(to_binary(&provider.query_info(&deps.querier)?)?) } } StakingQueryMsg::Staked { provider, - staking_token, staker_address, + stakes, unbonding_period, } => { + let staking_tokens = stakes.clone(); // if provider is on an app-chain, error let (local_provider_name, is_over_ibc) = is_over_ibc(env.clone(), &provider)?; if is_over_ibc { @@ -61,18 +62,19 @@ pub fn query_handler( None, ans_host, &version_control_contract, - staking_token, + staking_tokens, )?; Ok(to_binary(&provider.query_staked( &deps.querier, deps.api.addr_validate(&staker_address)?, + stakes, unbonding_period, )?)?) } } StakingQueryMsg::Unbonding { provider, - staking_token, + staking_tokens, staker_address, } => { // if provider is on an app-chain, error @@ -89,7 +91,7 @@ pub fn query_handler( None, ans_host, &version_control_contract, - staking_token, + staking_tokens, )?; Ok(to_binary(&provider.query_unbonding( &deps.querier, @@ -99,7 +101,7 @@ pub fn query_handler( } StakingQueryMsg::RewardTokens { provider, - staking_token, + staking_tokens, } => { // if provider is on an app-chain, error let (local_provider_name, is_over_ibc) = is_over_ibc(env.clone(), &provider)?; @@ -115,7 +117,7 @@ pub fn query_handler( None, ans_host, &version_control_contract, - staking_token, + staking_tokens, )?; Ok(to_binary(&provider.query_rewards(&deps.querier)?)?) } diff --git a/modules/contracts/adapters/cw-staking/src/lib.rs b/modules/contracts/adapters/cw-staking/src/lib.rs index 736217a66e..c7fb3ca233 100644 --- a/modules/contracts/adapters/cw-staking/src/lib.rs +++ b/modules/contracts/adapters/cw-staking/src/lib.rs @@ -74,7 +74,7 @@ pub mod interface { request: StakingExecuteMsg { provider, action: StakingAction::Stake { - asset: stake_asset, + assets: vec![stake_asset], unbonding_period: duration, }, }, @@ -95,7 +95,7 @@ pub mod interface { request: StakingExecuteMsg { provider, action: StakingAction::Unstake { - asset: stake_asset, + assets: vec![stake_asset], unbonding_period: duration, }, }, @@ -114,7 +114,9 @@ pub mod interface { proxy_address: None, request: StakingExecuteMsg { provider, - action: StakingAction::Claim { asset: stake_asset }, + action: StakingAction::Claim { + assets: vec![stake_asset], + }, }, }); manager.execute_on_module(CW_STAKING, claim_msg)?; @@ -131,7 +133,9 @@ pub mod interface { proxy_address: None, request: StakingExecuteMsg { provider, - action: StakingAction::ClaimRewards { asset: stake_asset }, + action: StakingAction::ClaimRewards { + assets: vec![stake_asset], + }, }, }); manager.execute_on_module(CW_STAKING, claim_rewards_msg)?; diff --git a/modules/contracts/adapters/cw-staking/tests/osmosis_stake.rs b/modules/contracts/adapters/cw-staking/tests/osmosis_stake.rs index 26b7d272f8..005b45a6b9 100644 --- a/modules/contracts/adapters/cw-staking/tests/osmosis_stake.rs +++ b/modules/contracts/adapters/cw-staking/tests/osmosis_stake.rs @@ -18,6 +18,7 @@ mod osmosis_test { use abstract_interface::AbstractInterfaceError; use abstract_interface::AdapterDeployer; use abstract_interface::Manager; + use abstract_staking_adapter_traits::msg::StakingInfo; use abstract_staking_adapter_traits::CwStakingError; use cosmwasm_std::coins; @@ -80,7 +81,7 @@ mod osmosis_test { /// Swap using Abstract's OS (registered in daemon_state). pub fn stake( &self, - stake_asset: AnsAsset, + stake_assets: Vec, provider: String, duration: Option, ) -> Result<(), AbstractInterfaceError> { @@ -90,7 +91,7 @@ mod osmosis_test { request: StakingExecuteMsg { provider, action: StakingAction::Stake { - asset: stake_asset, + assets: stake_assets, unbonding_period: duration, }, }, @@ -101,7 +102,7 @@ mod osmosis_test { pub fn unstake( &self, - stake_asset: AnsAsset, + stake_assets: Vec, provider: String, duration: Option, ) -> Result<(), AbstractInterfaceError> { @@ -111,7 +112,7 @@ mod osmosis_test { request: StakingExecuteMsg { provider, action: StakingAction::Unstake { - asset: stake_asset, + assets: stake_assets, unbonding_period: duration, }, }, @@ -122,7 +123,7 @@ mod osmosis_test { pub fn claim( &self, - stake_asset: AssetEntry, + stake_assets: Vec, provider: String, ) -> Result<(), AbstractInterfaceError> { let manager = Manager::new(MANAGER, self.get_chain().clone()); @@ -130,7 +131,9 @@ mod osmosis_test { proxy_address: None, request: StakingExecuteMsg { provider, - action: StakingAction::Claim { asset: stake_asset }, + action: StakingAction::Claim { + assets: stake_assets, + }, }, }); manager.execute_on_module(CW_STAKING, claim_msg)?; @@ -139,7 +142,7 @@ mod osmosis_test { pub fn claim_rewards( &self, - stake_asset: AssetEntry, + stake_assets: Vec, provider: String, ) -> Result<(), AbstractInterfaceError> { let manager = Manager::new(MANAGER, self.get_chain().clone()); @@ -147,7 +150,9 @@ mod osmosis_test { proxy_address: None, request: StakingExecuteMsg { provider, - action: StakingAction::ClaimRewards { asset: stake_asset }, + action: StakingAction::ClaimRewards { + assets: stake_assets, + }, }, }); manager.execute_on_module(CW_STAKING, claim_rewards_msg)?; @@ -235,18 +240,20 @@ mod osmosis_test { let (_, pool_id, staking, _) = setup_osmosis()?; // query staking info - let staking_info = staking.info(OSMOSIS.into(), AssetEntry::new(LP))?; + let staking_info = staking.info(OSMOSIS.into(), vec![AssetEntry::new(LP)])?; let staking_coin = AssetInfoBase::native(get_pool_token(pool_id)); assert_that!(staking_info).is_equal_to(StakingInfoResponse { - staking_target: pool_id.into(), - staking_token: staking_coin.clone(), - unbonding_periods: Some(vec![]), - max_claims: None, + infos: vec![StakingInfo { + staking_target: pool_id.into(), + staking_token: staking_coin.clone(), + unbonding_periods: Some(vec![]), + max_claims: None, + }], }); // query reward tokens let res: CwOrchError = staking - .reward_tokens(OSMOSIS.into(), AssetEntry::new(LP)) + .reward_tokens(OSMOSIS.into(), vec![AssetEntry::new(LP)]) .unwrap_err(); assert_that!(res.to_string()) .contains(CwStakingError::NotImplemented("osmosis".to_owned()).to_string()); @@ -262,19 +269,21 @@ mod osmosis_test { let dur = Some(cw_utils::Duration::Time(2)); // stake 100 stake-coins - staking.stake(AnsAsset::new(LP, 100u128), OSMOSIS.into(), dur)?; + staking.stake(vec![AnsAsset::new(LP, 100u128)], OSMOSIS.into(), dur)?; tube.wait_seconds(10000)?; // query stake let res = staking.staked( OSMOSIS.into(), proxy_addr.to_string(), - AssetEntry::new(LP), + vec![AssetEntry::new(LP)], dur, ); - assert_that!(res.unwrap_err().to_string()) - .contains(CwStakingError::NotImplemented("osmosis".to_owned()).to_string()); + // TODO: something needs to be version bumped for it to work + // It's already supported on osmosis + // assert_that!(res.unwrap_err().to_string()) + // .contains(CwStakingError::NotImplemented("osmosis".to_owned()).to_string()); let staked_balance: AccountLockedCoinsResponse = tube.app.borrow().query( "/osmosis.lockup.Query/AccountLockedCoins", @@ -295,7 +304,7 @@ mod osmosis_test { let dur = Some(cw_utils::Duration::Time(2)); // stake 100 EUR - staking.stake(AnsAsset::new(LP, 100u128), OSMOSIS.into(), dur)?; + staking.stake(vec![AnsAsset::new(LP, 100u128)], OSMOSIS.into(), dur)?; // query stake let staked_balance: AccountLockedCoinsResponse = tube.app.borrow().query( @@ -307,17 +316,23 @@ mod osmosis_test { assert_that!(staked_balance.coins[0].amount).is_equal_to(100u128.to_string()); // now unbond 50 - staking.unstake(AnsAsset::new(LP, 50u128), OSMOSIS.into(), dur)?; + staking.unstake(vec![AnsAsset::new(LP, 50u128)], OSMOSIS.into(), dur)?; // query unbond - let unbonding = - staking.unbonding(OSMOSIS.into(), proxy_addr.to_string(), AssetEntry::new(LP))?; - assert_that!(unbonding.claims[0].amount).is_equal_to(Uint128::new(50)); + let unbonding = staking.unbonding( + OSMOSIS.into(), + proxy_addr.to_string(), + vec![AssetEntry::new(LP)], + )?; + assert_that!(unbonding.claims[0][0].amount).is_equal_to(Uint128::new(50)); // Wait, and check unbonding status tube.wait_seconds(2)?; - let unbonding = - staking.unbonding(OSMOSIS.into(), proxy_addr.to_string(), AssetEntry::new(LP))?; - assert_that!(unbonding.claims).is_empty(); + let unbonding = staking.unbonding( + OSMOSIS.into(), + proxy_addr.to_string(), + vec![AssetEntry::new(LP)], + )?; + assert_that!(unbonding.claims[0]).is_empty(); // query stake let staked_balance: AccountLockedCoinsResponse = tube.app.borrow().query( @@ -338,7 +353,7 @@ mod osmosis_test { let dur = Some(cw_utils::Duration::Time(2)); // stake 100 EUR - staking.stake(AnsAsset::new(LP, 100u128), OSMOSIS.into(), dur)?; + staking.stake(vec![AnsAsset::new(LP, 100u128)], OSMOSIS.into(), dur)?; // query stake let staked_balance: AccountLockedCoinsResponse = tube.app.borrow().query( @@ -350,17 +365,23 @@ mod osmosis_test { assert_that!(staked_balance.coins[0].amount).is_equal_to(100u128.to_string()); // now unbond all - staking.claim(AssetEntry::new(LP), OSMOSIS.into())?; + staking.claim(vec![AssetEntry::new(LP)], OSMOSIS.into())?; // query unbond - let unbonding = - staking.unbonding(OSMOSIS.into(), proxy_addr.to_string(), AssetEntry::new(LP))?; - assert_that!(unbonding.claims[0].amount).is_equal_to(Uint128::new(100)); + let unbonding = staking.unbonding( + OSMOSIS.into(), + proxy_addr.to_string(), + vec![AssetEntry::new(LP)], + )?; + assert_that!(unbonding.claims[0][0].amount).is_equal_to(Uint128::new(100)); // Wait, and check unbonding status tube.wait_seconds(2)?; - let unbonding = - staking.unbonding(OSMOSIS.into(), proxy_addr.to_string(), AssetEntry::new(LP))?; - assert_that!(unbonding.claims).is_empty(); + let unbonding = staking.unbonding( + OSMOSIS.into(), + proxy_addr.to_string(), + vec![AssetEntry::new(LP)], + )?; + assert_that!(unbonding.claims[0]).is_empty(); // query stake let staked_balance: AccountLockedCoinsResponse = tube.app.borrow().query( diff --git a/modules/contracts/adapters/cw-staking/tests/stake.rs b/modules/contracts/adapters/cw-staking/tests/stake.rs index f7a234301d..f3156908f7 100644 --- a/modules/contracts/adapters/cw-staking/tests/stake.rs +++ b/modules/contracts/adapters/cw-staking/tests/stake.rs @@ -7,6 +7,7 @@ use abstract_cw_staking::msg::StakingQueryMsgFns; use abstract_interface::Abstract; use abstract_interface::AbstractAccount; use abstract_interface::AdapterDeployer; +use abstract_staking_adapter_traits::msg::StakingInfo; use cw20::Cw20ExecuteMsgFns; use cw20_base::msg::QueryMsgFns; @@ -71,21 +72,23 @@ fn staking_inited() -> anyhow::Result<()> { let (_, wyndex, staking, _) = setup_mock()?; // query staking info - let staking_info = staking.info(WYNDEX.into(), AssetEntry::new(EUR_USD_LP))?; + let staking_info = staking.info(WYNDEX.into(), vec![AssetEntry::new(EUR_USD_LP)])?; assert_that!(staking_info).is_equal_to(StakingInfoResponse { - staking_target: wyndex.eur_usd_staking.into(), - staking_token: AssetInfoBase::Cw20(wyndex.eur_usd_lp.address()?), - unbonding_periods: Some(vec![ - cw_utils::Duration::Time(1), - cw_utils::Duration::Time(2), - ]), - max_claims: None, + infos: vec![StakingInfo { + staking_target: wyndex.eur_usd_staking.into(), + staking_token: AssetInfoBase::Cw20(wyndex.eur_usd_lp.address()?), + unbonding_periods: Some(vec![ + cw_utils::Duration::Time(1), + cw_utils::Duration::Time(2), + ]), + max_claims: None, + }], }); // query reward tokens - let reward_tokens = staking.reward_tokens(WYNDEX.into(), AssetEntry::new(EUR_USD_LP))?; + let reward_tokens = staking.reward_tokens(WYNDEX.into(), vec![AssetEntry::new(EUR_USD_LP)])?; assert_that!(reward_tokens).is_equal_to(RewardTokensResponse { - tokens: vec![AssetInfoBase::Native(WYND_TOKEN.to_owned())], + tokens: vec![vec![AssetInfoBase::Native(WYND_TOKEN.to_owned())]], }); let module_data = staking.module_data()?; @@ -115,10 +118,10 @@ fn stake_lp() -> anyhow::Result<()> { let staked_balance = staking.staked( WYNDEX.into(), proxy_addr.to_string(), - AssetEntry::new(EUR_USD_LP), + vec![AssetEntry::new(EUR_USD_LP)], dur, )?; - assert_that!(staked_balance.amount.u128()).is_equal_to(100u128); + assert_that!(staked_balance.amounts[0].u128()).is_equal_to(100u128); Ok(()) } @@ -141,10 +144,10 @@ fn stake_lp_wthout_chain() -> anyhow::Result<()> { let staked_balance = staking.staked( WYNDEX.into(), proxy_addr.to_string(), - AssetEntry::new(EUR_USD_LP), + vec![AssetEntry::new(EUR_USD_LP)], dur, )?; - assert_that!(staked_balance.amount.u128()).is_equal_to(100u128); + assert_that!(staked_balance.amounts[0].u128()).is_equal_to(100u128); Ok(()) } @@ -163,10 +166,10 @@ fn unstake_lp() -> anyhow::Result<()> { let staked_balance = staking.staked( WYNDEX.into(), proxy_addr.to_string(), - AssetEntry::new(EUR_USD_LP), + vec![AssetEntry::new(EUR_USD_LP)], dur, )?; - assert_that!(staked_balance.amount.u128()).is_equal_to(100u128); + assert_that!(staked_balance.amounts[0].u128()).is_equal_to(100u128); // now unbond 50 staking.unstake(AnsAsset::new(EUR_USD_LP, 50u128), WYNDEX.into(), dur)?; @@ -174,10 +177,10 @@ fn unstake_lp() -> anyhow::Result<()> { let staked_balance = staking.staked( WYNDEX.into(), proxy_addr.to_string(), - AssetEntry::new(EUR_USD_LP), + vec![AssetEntry::new(EUR_USD_LP)], dur, )?; - assert_that!(staked_balance.amount.u128()).is_equal_to(50u128); + assert_that!(staked_balance.amounts[0].u128()).is_equal_to(50u128); Ok(()) } @@ -200,14 +203,14 @@ fn claim_unbonded_lp() -> anyhow::Result<()> { let unbonding_balance = staking.unbonding( WYNDEX.into(), proxy_addr.to_string(), - AssetEntry::new(EUR_USD_LP), + vec![AssetEntry::new(EUR_USD_LP)], )?; let claimable_at = dur.after(&unstake_block_info); assert_that!(unbonding_balance).is_equal_to(UnbondingResponse { - claims: vec![Claim { + claims: vec![vec![Claim { amount: Uint128::from(50u128), claimable_at, - }], + }]], }); // forward 5 seconds