Skip to content

Commit

Permalink
Merge pull request #149 from hadronlabs-org/fix/lsm-amount
Browse files Browse the repository at this point in the history
fix: proper calc lsm amount
  • Loading branch information
oldremez authored Aug 8, 2024
2 parents 342a38a + 3f7a6d2 commit 5fbded7
Show file tree
Hide file tree
Showing 9 changed files with 177 additions and 56 deletions.
55 changes: 48 additions & 7 deletions contracts/core/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{
attr, ensure, ensure_eq, ensure_ne, to_json_binary, Addr, Attribute, BankMsg, BankQuery,
Binary, Coin, CosmosMsg, CustomQuery, Decimal, Deps, DepsMut, Env, MessageInfo, Order,
QueryRequest, Response, StdError, StdResult, Uint128, Uint64, WasmMsg,
Binary, Coin, CosmosMsg, CustomQuery, Decimal, Decimal256, Deps, DepsMut, Env, MessageInfo,
Order, QueryRequest, Response, StdError, StdResult, Uint128, Uint256, Uint64, WasmMsg,
};
use cw_storage_plus::Bound;
use drop_helpers::answer::response;
Expand Down Expand Up @@ -35,6 +35,7 @@ use drop_staking_base::{
},
};
use neutron_sdk::bindings::{msg::NeutronMsg, query::NeutronQuery};
use neutron_sdk::interchain_queries::v047::types::DECIMAL_FRACTIONAL;
use prost::Message;

pub type MessageWithFeeResponse<T> = (CosmosMsg<T>, Option<CosmosMsg<T>>);
Expand Down Expand Up @@ -1005,7 +1006,7 @@ fn execute_bond(
r#ref: Option<String>,
) -> ContractResult<Response<NeutronMsg>> {
let config = CONFIG.load(deps.storage)?;
let Coin { amount, denom } = cw_utils::one_coin(&info)?;
let Coin { mut amount, denom } = cw_utils::one_coin(&info)?;
if let Some(bond_limit) = config.bond_limit {
if BONDED_AMOUNT.load(deps.storage)? + amount > bond_limit {
return Err(ContractError::BondLimitExceeded {});
Expand All @@ -1018,7 +1019,13 @@ fn execute_bond(
let exchange_rate = query_exchange_rate(deps.as_ref(), &config)?;
attrs.push(attr("exchange_rate", exchange_rate.to_string()));

if let check_denom::DenomType::LsmShare(remote_denom) = denom_type {
if let check_denom::DenomType::LsmShare(remote_denom, validator) = denom_type {
amount = calc_lsm_share_underlying_amount(
deps.as_ref(),
&config.puppeteer_contract,
&amount,
validator,
)?;
if amount < config.lsm_min_bond_amount {
return Err(ContractError::LSMBondAmountIsBelowMinimum {
min_stake_amount: config.lsm_min_bond_amount,
Expand All @@ -1038,7 +1045,6 @@ fn execute_bond(
amount: vec![Coin::new(amount.u128(), denom)],
}));
}

let issue_amount = amount * (Decimal::one() / exchange_rate);
attrs.push(attr("issue_amount", issue_amount.to_string()));

Expand Down Expand Up @@ -1561,13 +1567,45 @@ fn get_pending_lsm_share_msg<T, X: CustomQuery>(
}
}

fn calc_lsm_share_underlying_amount<T: CustomQuery>(
deps: Deps<T>,
puppeteer_contract: &Addr,
lsm_share: &Uint128,
validator: String,
) -> ContractResult<Uint128> {
let delegations = deps
.querier
.query_wasm_smart::<drop_staking_base::msg::puppeteer::DelegationsResponse>(
puppeteer_contract,
&drop_puppeteer_base::msg::QueryMsg::Extension {
msg: drop_staking_base::msg::puppeteer::QueryExtMsg::Delegations {},
},
)?
.delegations
.delegations;
if delegations.is_empty() {
return Err(ContractError::NoDelegations {});
}
let validator_info = delegations
.iter()
.find(|one| one.validator == validator)
.ok_or(ContractError::ValidatorInfoNotFound {
validator: validator.clone(),
})?;
let share = Decimal256::from_atomics(*lsm_share, 0)?;
Ok(Uint128::try_from(
share.checked_mul(validator_info.share_ratio)?.atomics()
/ Uint256::from(DECIMAL_FRACTIONAL),
)?)
}

pub mod check_denom {
use super::*;

#[derive(PartialEq, Debug)]
pub enum DenomType {
Base,
LsmShare(String),
LsmShare(String, String),
}

// XXX: cosmos_sdk_proto defines these structures for me,
Expand Down Expand Up @@ -1645,7 +1683,10 @@ pub mod check_denom {
return Err(ContractError::InvalidDenom {});
}

Ok(DenomType::LsmShare(trace.base_denom))
Ok(DenomType::LsmShare(
trace.base_denom.to_string(),
validator.to_string(),
))
}
}

Expand Down
71 changes: 58 additions & 13 deletions contracts/core/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ use crate::contract::{
use cosmwasm_std::{
from_json,
testing::{mock_env, mock_info, MockApi, MockStorage},
to_json_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal, Event, OwnedDeps, Response, SubMsg,
Timestamp, Uint128, WasmMsg,
to_json_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal, Decimal256, Event, OwnedDeps,
Response, SubMsg, Timestamp, Uint128, WasmMsg,
};
use drop_helpers::testing::{mock_dependencies, WasmMockQuerier};
use drop_puppeteer_base::{msg::TransferReadyBatchesMsg, state::RedeemShareItem};
use drop_puppeteer_base::{
msg::TransferReadyBatchesMsg,
state::{Delegations, DropDelegation, RedeemShareItem},
};
use drop_staking_base::state::core::{FAILED_BATCH_ID, LAST_STAKER_RESPONSE};
use drop_staking_base::{
error::core::ContractError,
Expand All @@ -28,7 +31,7 @@ use drop_staking_base::{
};
use neutron_sdk::{
bindings::{msg::NeutronMsg, query::NeutronQuery},
interchain_queries::v045::types::{Balances, Delegations},
interchain_queries::v045::types::Balances,
sudo::msg::RequestPacket,
};
use std::vec;
Expand Down Expand Up @@ -579,13 +582,14 @@ fn test_tick_idle_unbonding_close() {
.add_wasm_query_response("puppeteer_contract", |_| {
to_json_binary(&DelegationsResponse {
delegations: Delegations {
delegations: vec![cosmwasm_std::Delegation {
delegations: vec![DropDelegation {
delegator: Addr::unchecked("ica_address"),
validator: "valoper_address".to_string(),
amount: Coin {
denom: "remote_denom".to_string(),
amount: Uint128::new(100_000),
},
share_ratio: Decimal256::one(),
}],
},
remote_height: 10u64,
Expand Down Expand Up @@ -717,13 +721,14 @@ fn test_tick_idle_claim_wo_unbond() {
.add_wasm_query_response("puppeteer_contract", |_| {
to_json_binary(&DelegationsResponse {
delegations: Delegations {
delegations: vec![cosmwasm_std::Delegation {
delegations: vec![DropDelegation {
delegator: Addr::unchecked("ica_address"),
validator: "valoper_address".to_string(),
amount: Coin {
denom: "remote_denom".to_string(),
amount: Uint128::new(100_000),
},
share_ratio: Decimal256::one(),
}],
},
remote_height: 10u64,
Expand Down Expand Up @@ -874,13 +879,14 @@ fn test_tick_idle_claim_with_unbond_transfer() {
.add_wasm_query_response("puppeteer_contract", |_| {
to_json_binary(&DelegationsResponse {
delegations: Delegations {
delegations: vec![cosmwasm_std::Delegation {
delegations: vec![DropDelegation {
delegator: Addr::unchecked("ica_address"),
validator: "valoper_address".to_string(),
amount: Coin {
denom: "remote_denom".to_string(),
amount: Uint128::new(100_000),
},
share_ratio: Decimal256::one(),
}],
},
remote_height: 12344u64,
Expand Down Expand Up @@ -2948,7 +2954,7 @@ fn test_bond_lsm_share_wrong_channel() {
fn test_bond_lsm_share_increase_exchange_rate() {
let mut deps = mock_dependencies(&[Coin {
denom: "ld_denom".to_string(),
amount: Uint128::new(1),
amount: Uint128::new(1001),
}]);
deps.querier.add_stargate_query_response(
"/ibc.applications.transfer.v1.Query/DenomTrace",
Expand Down Expand Up @@ -2994,11 +3000,33 @@ fn test_bond_lsm_share_increase_exchange_rate() {
.add_wasm_query_response("puppeteer_contract", |_| {
to_json_binary(&DelegationsResponse {
delegations: Delegations {
delegations: vec![],
delegations: vec![DropDelegation {
delegator: Addr::unchecked("delegator"),
validator: "valoper1".to_string(),
amount: Coin::new(1000, "remote_denom".to_string()),
share_ratio: Decimal256::one(),
}],
},
remote_height: 10u64,
local_height: 10u64,
timestamp: Timestamp::from_seconds(90001),
})
.unwrap()
});
deps.querier
.add_wasm_query_response("puppeteer_contract", |_| {
to_json_binary(&DelegationsResponse {
delegations: Delegations {
delegations: vec![DropDelegation {
delegator: Addr::unchecked("delegator"),
validator: "valoper1".to_string(),
amount: Coin::new(1000, "remote_denom".to_string()),
share_ratio: Decimal256::one(),
}],
},
remote_height: 0,
local_height: 0,
timestamp: Timestamp::from_nanos(1_000_000_202),
remote_height: 10u64,
local_height: 10u64,
timestamp: Timestamp::from_seconds(90001),
})
.unwrap()
});
Expand Down Expand Up @@ -3154,6 +3182,23 @@ fn test_bond_lsm_share_ok() {
})
.unwrap()
});
deps.querier
.add_wasm_query_response("puppeteer_contract", |_| {
to_json_binary(&DelegationsResponse {
delegations: Delegations {
delegations: vec![DropDelegation {
delegator: Addr::unchecked("delegator"),
validator: "valoper1".to_string(),
amount: Coin::new(1000, "remote_denom".to_string()),
share_ratio: Decimal256::one(),
}],
},
remote_height: 10u64,
local_height: 10u64,
timestamp: Timestamp::from_seconds(90001),
})
.unwrap()
});
let mut env = mock_env();
env.block.time = Timestamp::from_seconds(1000);
TOTAL_LSM_SHARES
Expand Down Expand Up @@ -3746,7 +3791,7 @@ mod check_denom {
.unwrap();
assert_eq!(
denom_type,
DenomType::LsmShare("valoper12345/1".to_string())
DenomType::LsmShare("valoper12345/1".to_string(), "valoper12345".to_string())
);
}
}
Expand Down
7 changes: 3 additions & 4 deletions contracts/puppeteer/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ use drop_puppeteer_base::{
},
proto::MsgIBCTransfer,
state::{
PuppeteerBase, RedeemShareItem, ReplyMsg, TxState, TxStateStatus, UnbondingDelegation,
ICA_ID, LOCAL_DENOM,
Delegations, PuppeteerBase, RedeemShareItem, ReplyMsg, TxState, TxStateStatus,
UnbondingDelegation, ICA_ID, LOCAL_DENOM,
},
};
use drop_staking_base::{
Expand All @@ -55,8 +55,7 @@ use drop_staking_base::{
use neutron_sdk::{
bindings::{msg::NeutronMsg, query::NeutronQuery, types::ProtobufAny},
interchain_queries::v045::{
new_register_delegator_unbonding_delegations_query_msg,
types::{Balances, Delegations},
new_register_delegator_unbonding_delegations_query_msg, types::Balances,
},
interchain_txs::helpers::decode_message_response,
sudo::msg::{RequestPacket, RequestPacketTimeoutHeight, SudoMsg},
Expand Down
17 changes: 10 additions & 7 deletions contracts/puppeteer/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use cosmwasm_schema::schemars;
use cosmwasm_std::{
coin, coins, from_json,
testing::{mock_env, mock_info},
to_json_binary, Addr, Binary, CosmosMsg, Delegation, DepsMut, Event, Response, StdError,
to_json_binary, Addr, Binary, CosmosMsg, Decimal256, DepsMut, Event, Response, StdError,
SubMsg, Timestamp, Uint128, Uint64,
};
use drop_helpers::{
Expand All @@ -14,7 +14,8 @@ use drop_helpers::{
testing::mock_dependencies,
};
use drop_puppeteer_base::state::{
BalancesAndDelegations, BalancesAndDelegationsState, PuppeteerBase, ReplyMsg,
BalancesAndDelegations, BalancesAndDelegationsState, Delegations, DropDelegation,
PuppeteerBase, ReplyMsg,
};
use drop_staking_base::{
msg::puppeteer::InstantiateMsg,
Expand All @@ -26,7 +27,7 @@ use neutron_sdk::{
query::{NeutronQuery, QueryRegisteredQueryResultResponse},
types::{InterchainQueryResult, StorageValue},
},
interchain_queries::v045::types::{Balances, Delegations},
interchain_queries::v045::types::Balances,
query::min_ibc_fee::MinIbcFeeResponse,
sudo::msg::SudoMsg,
NeutronError,
Expand Down Expand Up @@ -619,21 +620,23 @@ fn test_sudo_kv_query_result() {
},
delegations: Delegations {
delegations: vec![
Delegation {
DropDelegation {
delegator: Addr::unchecked(
"cosmos1nujy3vl3rww3cy8tf8pdru5jp3f9ppmkadws553ck3qryg2tjanqt39xnv"
),
validator: "cosmosvaloper1rndyjagfg0nsedl2uy5n92vssn8aj5n67t0nfx"
.to_string(),
amount: coin(13582465152, "stake")
amount: coin(13582465152, "stake"),
share_ratio: Decimal256::one()
},
Delegation {
DropDelegation {
delegator: Addr::unchecked(
"cosmos1nujy3vl3rww3cy8tf8pdru5jp3f9ppmkadws553ck3qryg2tjanqt39xnv"
),
validator: "cosmosvaloper1gh4vzw9wsfgl2h37qqnetet0m4wrzm7v7x3j9x"
.to_string(),
amount: coin(13582465152, "stake")
amount: coin(13582465152, "stake"),
share_ratio: Decimal256::one()
}
]
}
Expand Down
11 changes: 6 additions & 5 deletions contracts/strategy/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@ use crate::contract::instantiate;
use cosmwasm_schema::cw_serde;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::{
to_json_binary, Addr, Attribute, Binary, Decimal, Deps, Empty, Env, Event, Response, StdResult,
Timestamp, Uint128,
to_json_binary, Addr, Attribute, Binary, Decimal, Decimal256, Deps, Empty, Env, Event,
Response, StdResult, Timestamp, Uint128,
};
use cw_multi_test::{custom_app, App, Contract, ContractWrapper, Executor};
use drop_puppeteer_base::error::ContractError as PuppeteerContractError;
use drop_puppeteer_base::msg::QueryMsg as PuppeteerQueryMsg;
use drop_puppeteer_base::state::{Delegations, DropDelegation};
use drop_staking_base::error::distribution::ContractError as DistributionContractError;
use drop_staking_base::error::validatorset::ContractError as ValidatorSetContractError;
use drop_staking_base::msg::strategy::QueryMsg;
use drop_staking_base::msg::validatorset::QueryMsg as ValidatorSetQueryMsg;
use drop_staking_base::msg::{
distribution::QueryMsg as DistributionQueryMsg, strategy::InstantiateMsg,
};
use neutron_sdk::interchain_queries::v045::types::Delegations;

const CORE_CONTRACT_ADDR: &str = "core_contract";
const PUPPETEER_CONTRACT_ADDR: &str = "puppeteer_contract";
Expand Down Expand Up @@ -80,15 +80,16 @@ fn puppeteer_query(
PuppeteerQueryMsg::KVQueryIds {} => todo!(),
PuppeteerQueryMsg::Extension { msg } => match msg {
drop_staking_base::msg::puppeteer::QueryExtMsg::Delegations {} => {
let mut delegations_amount: Vec<cosmwasm_std::Delegation> = Vec::new();
let mut delegations_amount: Vec<DropDelegation> = Vec::new();
for i in 0..3 {
let delegation = cosmwasm_std::Delegation {
let delegation = DropDelegation {
validator: format!("valoper{}", i),
delegator: Addr::unchecked("delegator".to_owned() + i.to_string().as_str()),
amount: cosmwasm_std::Coin {
denom: "uatom".to_string(),
amount: Uint128::from(100u128),
},
share_ratio: Decimal256::one(),
};
delegations_amount.push(delegation);
}
Expand Down
Loading

0 comments on commit 5fbded7

Please sign in to comment.