Skip to content

Commit f11c063

Browse files
committed
Fix staking contract
1 parent b394ad6 commit f11c063

File tree

2 files changed

+43
-29
lines changed

2 files changed

+43
-29
lines changed

contracts/staking/src/contract.rs

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use cosmwasm_std::{
2-
coin, entry_point, to_json_binary, BankMsg, Decimal, Deps, DepsMut, DistributionMsg, Env,
3-
MessageInfo, QuerierWrapper, QueryResponse, Response, StakingMsg, StdError, StdResult, Uint128,
4-
WasmMsg,
2+
entry_point, to_json_binary, BankMsg, Coin, Decimal, Decimal256, Deps, DepsMut,
3+
DistributionMsg, Env, MessageInfo, QuerierWrapper, QueryResponse, Response, StakingMsg,
4+
StdError, StdResult, Uint128, Uint256, WasmMsg,
55
};
66

77
use crate::errors::{StakingError, Unauthorized};
@@ -85,14 +85,16 @@ pub fn transfer(
8585
let rcpt_raw = deps.api.addr_canonicalize(&recipient)?;
8686
let sender_raw = deps.api.addr_canonicalize(info.sender.as_str())?;
8787

88-
let balance = may_load_map(deps.storage, PREFIX_BALANCE, &sender_raw)?.unwrap_or_default();
88+
let balance: Uint128 =
89+
may_load_map(deps.storage, PREFIX_BALANCE, &sender_raw)?.unwrap_or_default();
8990
save_map(
9091
deps.storage,
9192
PREFIX_BALANCE,
9293
&sender_raw,
9394
balance.checked_sub(send)?,
9495
)?;
95-
let balance = may_load_map(deps.storage, PREFIX_BALANCE, &rcpt_raw)?.unwrap_or_default();
96+
let balance: Uint128 =
97+
may_load_map(deps.storage, PREFIX_BALANCE, &rcpt_raw)?.unwrap_or_default();
9698
save_map(deps.storage, PREFIX_BALANCE, &rcpt_raw, balance + send)?;
9799

98100
let res = Response::new()
@@ -105,13 +107,13 @@ pub fn transfer(
105107

106108
// get_bonded returns the total amount of delegations from contract
107109
// it ensures they are all the same denom
108-
fn get_bonded(querier: &QuerierWrapper, contract_addr: impl Into<String>) -> StdResult<Uint128> {
110+
fn get_bonded(querier: &QuerierWrapper, contract_addr: impl Into<String>) -> StdResult<Uint256> {
109111
let bonds = querier.query_all_delegations(contract_addr)?;
110112
if bonds.is_empty() {
111-
return Ok(Uint128::new(0));
113+
return Ok(Uint256::zero());
112114
}
113115
let denom = bonds[0].amount.denom.as_str();
114-
bonds.iter().try_fold(Uint128::zero(), |acc, d| {
116+
bonds.iter().try_fold(Uint256::zero(), |acc, d| {
115117
if d.amount.denom.as_str() != denom {
116118
Err(StdError::generic_err(format!(
117119
"different denoms in bonds: '{}' vs '{}'",
@@ -123,7 +125,7 @@ fn get_bonded(querier: &QuerierWrapper, contract_addr: impl Into<String>) -> Std
123125
})
124126
}
125127

126-
fn assert_bonds(supply: &Supply, bonded: Uint128) -> StdResult<()> {
128+
fn assert_bonds(supply: &Supply, bonded: Uint256) -> StdResult<()> {
127129
if supply.bonded != bonded {
128130
Err(StdError::generic_err(format!(
129131
"Stored bonded {}, but query bonded: {}",
@@ -153,17 +155,19 @@ pub fn bond(deps: DepsMut, env: Env, info: MessageInfo) -> StdResult<Response> {
153155
let mut supply: Supply = load_item(deps.storage, KEY_TOTAL_SUPPLY)?;
154156
// TODO: this is just temporary check - we should use dynamic query or have a way to recover
155157
assert_bonds(&supply, bonded)?;
158+
// note that the conversion to Uint128 limits payment amounts to `u128::MAX`
156159
let to_mint = if supply.issued.is_zero() || bonded.is_zero() {
157-
payment.amount.mul_floor(FALLBACK_RATIO)
160+
Uint128::try_from(payment.amount.mul_floor(FALLBACK_RATIO))?
158161
} else {
159-
payment.amount.multiply_ratio(supply.issued, bonded)
162+
Uint128::try_from(payment.amount.multiply_ratio(supply.issued, bonded))?
160163
};
161164
supply.bonded = bonded + payment.amount;
162165
supply.issued += to_mint;
163166
save_item(deps.storage, KEY_TOTAL_SUPPLY, &supply)?;
164167

165168
// update the balance of the sender
166-
let balance = may_load_map(deps.storage, PREFIX_BALANCE, &sender_raw)?.unwrap_or_default();
169+
let balance: Uint128 =
170+
may_load_map(deps.storage, PREFIX_BALANCE, &sender_raw)?.unwrap_or_default();
167171
save_map(deps.storage, PREFIX_BALANCE, &sender_raw, balance + to_mint)?;
168172

169173
// bond them to the validator
@@ -196,7 +200,8 @@ pub fn unbond(deps: DepsMut, env: Env, info: MessageInfo, amount: Uint128) -> St
196200
let tax = amount.mul_floor(invest.exit_tax);
197201

198202
// deduct all from the account
199-
let balance = may_load_map(deps.storage, PREFIX_BALANCE, &sender_raw)?.unwrap_or_default();
203+
let balance: Uint128 =
204+
may_load_map(deps.storage, PREFIX_BALANCE, &sender_raw)?.unwrap_or_default();
200205
save_map(
201206
deps.storage,
202207
PREFIX_BALANCE,
@@ -205,7 +210,8 @@ pub fn unbond(deps: DepsMut, env: Env, info: MessageInfo, amount: Uint128) -> St
205210
)?;
206211
if tax > Uint128::new(0) {
207212
// add tax to the owner
208-
let balance = may_load_map(deps.storage, PREFIX_BALANCE, &owner_raw)?.unwrap_or_default();
213+
let balance: Uint128 =
214+
may_load_map(deps.storage, PREFIX_BALANCE, &owner_raw)?.unwrap_or_default();
209215
save_map(deps.storage, PREFIX_BALANCE, &owner_raw, balance + tax)?;
210216
}
211217

@@ -218,14 +224,15 @@ pub fn unbond(deps: DepsMut, env: Env, info: MessageInfo, amount: Uint128) -> St
218224
let mut supply: Supply = load_item(deps.storage, KEY_TOTAL_SUPPLY)?;
219225
// TODO: this is just temporary check - we should use dynamic query or have a way to recover
220226
assert_bonds(&supply, bonded)?;
221-
let unbond = remainder.multiply_ratio(bonded, supply.issued);
227+
let unbond = Uint256::from(remainder).multiply_ratio(bonded, supply.issued);
222228
supply.bonded = bonded.checked_sub(unbond)?;
223229
supply.issued = supply.issued.checked_sub(remainder)?;
224230
supply.claims += unbond;
225231
save_item(deps.storage, KEY_TOTAL_SUPPLY, &supply)?;
226232

227233
// add a claim to this user to get their tokens after the unbonding period
228-
let claim = may_load_map(deps.storage, PREFIX_CLAIMS, &sender_raw)?.unwrap_or_default();
234+
let claim: Uint256 =
235+
may_load_map(deps.storage, PREFIX_CLAIMS, &sender_raw)?.unwrap_or_default();
229236
save_map(deps.storage, PREFIX_CLAIMS, &sender_raw, claim + unbond)?;
230237

231238
// unbond them
@@ -236,7 +243,7 @@ pub fn unbond(deps: DepsMut, env: Env, info: MessageInfo, amount: Uint128) -> St
236243
.add_attribute("burnt", amount)
237244
.add_message(StakingMsg::Undelegate {
238245
validator: invest.validator,
239-
amount: coin(unbond.u128(), &invest.bond_denom),
246+
amount: Coin::new(unbond, &invest.bond_denom),
240247
});
241248
Ok(res)
242249
}
@@ -247,7 +254,7 @@ pub fn claim(deps: DepsMut, env: Env, info: MessageInfo) -> StdResult<Response>
247254
let mut balance = deps
248255
.querier
249256
.query_balance(env.contract.address, invest.bond_denom)?;
250-
if balance.amount < invest.min_withdrawal {
257+
if balance.amount < invest.min_withdrawal.into() {
251258
return Err(StdError::generic_err(
252259
"Insufficient balance in contract to process claim",
253260
));
@@ -325,7 +332,7 @@ pub fn _bond_all_tokens(
325332
let updated = update_item(deps.storage, KEY_TOTAL_SUPPLY, |mut supply: Supply| {
326333
balance.amount = balance.amount.checked_sub(supply.claims)?;
327334
// this just triggers the "no op" case if we don't have min_withdrawal left to reinvest
328-
balance.amount.checked_sub(invest.min_withdrawal)?;
335+
balance.amount.checked_sub(invest.min_withdrawal.into())?;
329336
supply.bonded += balance.amount;
330337
Ok(supply)
331338
});
@@ -393,11 +400,14 @@ pub fn query_investment(deps: Deps) -> StdResult<InvestmentResponse> {
393400
validator: invest.validator,
394401
min_withdrawal: invest.min_withdrawal,
395402
token_supply: supply.issued,
396-
staked_tokens: coin(supply.bonded.u128(), &invest.bond_denom),
403+
staked_tokens: Coin::new(supply.bonded, invest.bond_denom),
397404
nominal_value: if supply.issued.is_zero() {
398405
FALLBACK_RATIO
399406
} else {
400-
Decimal::from_ratio(supply.bonded, supply.issued)
407+
// TODO: use Decimal256???
408+
Decimal256::from_ratio(supply.bonded, supply.issued)
409+
.try_into()
410+
.map_err(|_| StdError::generic_err("nominal value too high"))?
401411
},
402412
};
403413
Ok(res)
@@ -409,7 +419,7 @@ mod tests {
409419
use cosmwasm_std::testing::{
410420
message_info, mock_dependencies, mock_env, MockQuerier, StakingQuerier, MOCK_CONTRACT_ADDR,
411421
};
412-
use cosmwasm_std::{coins, Addr, Coin, CosmosMsg, Decimal, FullDelegation, Validator};
422+
use cosmwasm_std::{coin, coins, Addr, Coin, CosmosMsg, Decimal, FullDelegation, Validator};
413423
use std::str::FromStr;
414424

415425
fn sample_validator(addr: &str) -> Validator {

contracts/staking/src/state.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize};
66
use cosmwasm_std::{
77
from_json,
88
storage_keys::{namespace_with_key, to_length_prefixed},
9-
to_json_vec, Addr, CanonicalAddr, Decimal, StdError, StdResult, Storage, Uint128,
9+
to_json_vec, Addr, CanonicalAddr, Decimal, StdError, StdResult, Storage, Uint128, Uint256,
1010
};
1111

1212
pub const KEY_INVESTMENT: &[u8] = b"invest";
@@ -16,11 +16,11 @@ pub const KEY_TOTAL_SUPPLY: &[u8] = b"total_supply";
1616
pub const PREFIX_BALANCE: &[u8] = b"balance";
1717
pub const PREFIX_CLAIMS: &[u8] = b"claim";
1818

19-
pub fn may_load_map(
19+
pub fn may_load_map<T: DeserializeOwned>(
2020
storage: &dyn Storage,
2121
prefix: &[u8],
2222
key: &CanonicalAddr,
23-
) -> StdResult<Option<Uint128>> {
23+
) -> StdResult<Option<T>> {
2424
storage
2525
.get(&namespace_with_key(&[prefix], key))
2626
.map(from_json)
@@ -31,13 +31,17 @@ pub fn save_map(
3131
storage: &mut dyn Storage,
3232
prefix: &[u8],
3333
key: &CanonicalAddr,
34-
value: Uint128,
34+
value: impl Serialize,
3535
) -> StdResult<()> {
3636
storage.set(&namespace_with_key(&[prefix], key), &to_json_vec(&value)?);
3737
Ok(())
3838
}
3939

40-
pub fn load_map(storage: &dyn Storage, prefix: &[u8], key: &CanonicalAddr) -> StdResult<Uint128> {
40+
pub fn load_map<T: DeserializeOwned>(
41+
storage: &dyn Storage,
42+
prefix: &[u8],
43+
key: &CanonicalAddr,
44+
) -> StdResult<T> {
4145
may_load_map(storage, prefix, key)?
4246
.ok_or_else(|| StdError::not_found(format!("map value for {key}")))
4347
}
@@ -76,9 +80,9 @@ pub struct Supply {
7680
/// issued is how many derivative tokens this contract has issued
7781
pub issued: Uint128,
7882
/// bonded is how many native tokens exist bonded to the validator
79-
pub bonded: Uint128,
83+
pub bonded: Uint256,
8084
/// claims is how many tokens need to be reserved paying back those who unbonded
81-
pub claims: Uint128,
85+
pub claims: Uint256,
8286
}
8387

8488
pub fn load_item<T: DeserializeOwned>(storage: &dyn Storage, key: &[u8]) -> StdResult<T> {

0 commit comments

Comments
 (0)