Skip to content

Commit f38e805

Browse files
authored
Merge pull request #221 from juggernaut09/juggernaut/Add-cw20-support
Juggernaut/add cw20 support
2 parents a33a71f + 525822d commit f38e805

File tree

15 files changed

+161
-60
lines changed

15 files changed

+161
-60
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

contracts/cw1-subkeys/src/contract.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,7 @@ pub fn check_staking_permissions(
142142
return Err(ContractError::WithdrawPerm {});
143143
}
144144
}
145-
s => {
146-
panic!("Unsupported staking message: {:?}", s)
147-
}
145+
s => panic!("Unsupported staking message: {:?}", s),
148146
}
149147
Ok(true)
150148
}

contracts/cw4-stake/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ library = []
2929
cw0 = { path = "../../packages/cw0", version = "0.5.0" }
3030
cw2 = { path = "../../packages/cw2", version = "0.5.0" }
3131
cw4 = { path = "../../packages/cw4", version = "0.5.0" }
32+
cw20 = { path = "../../packages/cw20", version = "0.5.0" }
3233
cw-controllers = { path = "../../packages/controllers", version = "0.5.0" }
3334
cw-storage-plus = { path = "../../packages/storage-plus", version = "0.5.0", features = ["iterator"] }
3435
cosmwasm-std = { version = "0.14.0-alpha2" }

contracts/cw4-stake/schema/init_msg.json

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@
2121
},
2222
"denom": {
2323
"description": "denom of the token to stake",
24-
"type": "string"
24+
"allOf": [
25+
{
26+
"$ref": "#/definitions/Denom"
27+
}
28+
]
2529
},
2630
"min_bond": {
2731
"$ref": "#/definitions/Uint128"
@@ -34,6 +38,39 @@
3438
}
3539
},
3640
"definitions": {
41+
"Binary": {
42+
"description": "Binary is a wrapper around Vec<u8> to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec<u8>",
43+
"type": "string"
44+
},
45+
"CanonicalAddr": {
46+
"$ref": "#/definitions/Binary"
47+
},
48+
"Denom": {
49+
"anyOf": [
50+
{
51+
"type": "object",
52+
"required": [
53+
"native"
54+
],
55+
"properties": {
56+
"native": {
57+
"type": "string"
58+
}
59+
}
60+
},
61+
{
62+
"type": "object",
63+
"required": [
64+
"cw20"
65+
],
66+
"properties": {
67+
"cw20": {
68+
"$ref": "#/definitions/CanonicalAddr"
69+
}
70+
}
71+
}
72+
]
73+
},
3774
"Duration": {
3875
"description": "Duration is a delta of time. You can add it to a BlockInfo or Expiration to move that further in the future. Note that an height-based Duration and a time-based Expiration cannot be combined",
3976
"anyOf": [

contracts/cw4-stake/src/contract.rs

Lines changed: 64 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
use cosmwasm_std::{
22
attr, coin, coins, to_binary, BankMsg, Binary, CanonicalAddr, Coin, CosmosMsg, Deps, DepsMut,
3-
Env, HumanAddr, MessageInfo, Order, Response, StdResult, Storage, Uint128,
3+
Env, HumanAddr, MessageInfo, Order, Response, StdError, StdResult, Storage, Uint128,
44
};
5-
use cw0::maybe_canonical;
5+
6+
use cw0::{maybe_canonical, NativeBalance};
67
use cw2::set_contract_version;
8+
use cw20::{Balance, Denom};
79
use cw4::{
810
Member, MemberChangedHookMsg, MemberDiff, MemberListResponse, MemberResponse,
911
TotalWeightResponse,
@@ -58,42 +60,46 @@ pub fn execute(
5860
HandleMsg::UpdateAdmin { admin } => Ok(ADMIN.execute_update_admin(deps, info, admin)?),
5961
HandleMsg::AddHook { addr } => Ok(HOOKS.execute_add_hook(&ADMIN, deps, info, addr)?),
6062
HandleMsg::RemoveHook { addr } => Ok(HOOKS.execute_remove_hook(&ADMIN, deps, info, addr)?),
61-
HandleMsg::Bond {} => execute_bond(deps, env, info),
63+
HandleMsg::Bond {} => execute_bond(deps, env, Balance::from(info.funds), info.sender),
6264
HandleMsg::Unbond { tokens: amount } => execute_unbond(deps, env, info, amount),
6365
HandleMsg::Claim {} => execute_claim(deps, env, info),
6466
}
6567
}
6668

67-
pub fn execute_bond(deps: DepsMut, env: Env, info: MessageInfo) -> Result<Response, ContractError> {
69+
pub fn execute_bond(
70+
deps: DepsMut,
71+
env: Env,
72+
amount: Balance,
73+
sender: HumanAddr,
74+
) -> Result<Response, ContractError> {
6875
let cfg = CONFIG.load(deps.storage)?;
6976

7077
// ensure the sent denom was proper
7178
// NOTE: those clones are not needed (if we move denom, we return early),
7279
// but the compiler cannot see that (yet...)
73-
let sent = match info.funds.len() {
74-
0 => Err(ContractError::NoFunds {}),
75-
1 => {
76-
if info.funds[0].denom == cfg.denom {
77-
Ok(info.funds[0].amount)
80+
let amount = match (&cfg.denom, &amount) {
81+
(Denom::Native(want), Balance::Native(have)) => must_pay_funds(have, want),
82+
(Denom::Cw20(want), Balance::Cw20(have)) => {
83+
if want == &have.address {
84+
Ok(have.amount)
7885
} else {
79-
Err(ContractError::MissingDenom(cfg.denom.clone()))
86+
Err(ContractError::InvalidDenom(deps.api.human_address(&want)?))
8087
}
8188
}
82-
_ => Err(ContractError::ExtraDenoms(cfg.denom.clone())),
89+
_ => Err(ContractError::MixedNativeAndCw20(
90+
"Invalid address or denom".to_string(),
91+
)),
8392
}?;
84-
if sent.is_zero() {
85-
return Err(ContractError::NoFunds {});
86-
}
8793

8894
// update the sender's stake
89-
let sender_raw = deps.api.canonical_address(&info.sender)?;
95+
let sender_raw = deps.api.canonical_address(&sender)?;
9096
let new_stake = STAKE.update(deps.storage, &sender_raw, |stake| -> StdResult<_> {
91-
Ok(stake.unwrap_or_default() + sent)
97+
Ok(stake.unwrap_or_default() + amount)
9298
})?;
9399

94100
let messages = update_membership(
95101
deps.storage,
96-
info.sender.clone(),
102+
sender.clone(),
97103
&sender_raw,
98104
new_stake,
99105
&cfg,
@@ -102,8 +108,8 @@ pub fn execute_bond(deps: DepsMut, env: Env, info: MessageInfo) -> Result<Respon
102108

103109
let attributes = vec![
104110
attr("action", "bond"),
105-
attr("amount", sent),
106-
attr("sender", info.sender),
111+
attr("amount", amount),
112+
attr("sender", sender),
107113
];
108114
Ok(Response {
109115
submessages: vec![],
@@ -156,6 +162,22 @@ pub fn execute_unbond(
156162
})
157163
}
158164

165+
pub fn must_pay_funds(balance: &NativeBalance, denom: &str) -> Result<Uint128, ContractError> {
166+
match balance.0.len() {
167+
0 => Err(ContractError::NoFunds {}),
168+
1 => {
169+
let balance = &balance.0;
170+
let payment = balance[0].amount;
171+
if balance[0].denom == denom {
172+
Ok(payment)
173+
} else {
174+
Err(ContractError::MissingDenom(denom.to_string()))
175+
}
176+
}
177+
_ => Err(ContractError::ExtraDenoms(denom.to_string())),
178+
}
179+
}
180+
159181
fn update_membership(
160182
storage: &mut dyn Storage,
161183
sender: HumanAddr,
@@ -211,9 +233,15 @@ pub fn execute_claim(
211233
}
212234

213235
let config = CONFIG.load(deps.storage)?;
214-
let amount = coins(release.u128(), config.denom);
215-
let amount_str = coins_to_string(&amount);
236+
let amount;
237+
match &config.denom {
238+
Denom::Native(denom) => amount = coins(release.u128(), denom),
239+
Denom::Cw20(_canonical_addr) => {
240+
unimplemented!("The CW20 coins release functionality is in progress")
241+
}
242+
}
216243

244+
let amount_str = coins_to_string(&amount);
217245
let messages = vec![BankMsg::Send {
218246
to_address: info.sender.clone(),
219247
amount,
@@ -269,7 +297,14 @@ pub fn query_staked(deps: Deps, address: HumanAddr) -> StdResult<StakedResponse>
269297
let stake = STAKE
270298
.may_load(deps.storage, &address_raw)?
271299
.unwrap_or_default();
272-
let denom = CONFIG.load(deps.storage)?.denom;
300+
let denom = match CONFIG.load(deps.storage)?.denom {
301+
Denom::Native(want) => want,
302+
_ => {
303+
return Err(StdError::generic_err(
304+
"The stake for CW20 is not yet implemented",
305+
));
306+
}
307+
};
273308
Ok(StakedResponse {
274309
stake: coin(stake.u128(), denom),
275310
})
@@ -315,18 +350,21 @@ fn list_members(
315350

316351
#[cfg(test)]
317352
mod tests {
318-
use super::*;
319353
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
320354
use cosmwasm_std::{from_slice, Api, StdError, Storage};
321355
use cw0::Duration;
356+
use cw20::Denom;
322357
use cw4::{member_key, TOTAL_KEY};
323358
use cw_controllers::{AdminError, Claim, HookError};
324359

360+
use crate::error::ContractError;
361+
362+
use super::*;
363+
325364
const INIT_ADMIN: &str = "juan";
326365
const USER1: &str = "somebody";
327366
const USER2: &str = "else";
328367
const USER3: &str = "funny";
329-
330368
const DENOM: &str = "stake";
331369
const TOKENS_PER_WEIGHT: Uint128 = Uint128(1_000);
332370
const MIN_BOND: Uint128 = Uint128(5_000);
@@ -348,7 +386,7 @@ mod tests {
348386
unbonding_period: Duration,
349387
) {
350388
let msg = InitMsg {
351-
denom: DENOM.to_string(),
389+
denom: Denom::Native("stake".to_string()),
352390
tokens_per_weight,
353391
min_bond,
354392
unbonding_period,
@@ -528,7 +566,7 @@ mod tests {
528566
assert_eq!(minuend.as_str(), "5000");
529567
assert_eq!(subtrahend.as_str(), "5100");
530568
}
531-
e => panic!("Unexpected error: {}", e),
569+
e => panic!("Unexpected error: {:?}", e),
532570
}
533571
}
534572

contracts/cw4-stake/src/error.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use cosmwasm_std::StdError;
1+
use cosmwasm_std::{HumanAddr, StdError};
22
use thiserror::Error;
33

44
use cw_controllers::{AdminError, HookError};
@@ -26,6 +26,12 @@ pub enum ContractError {
2626
#[error("Sent unsupported denoms, must send '{0}' to stake")]
2727
ExtraDenoms(String),
2828

29+
#[error("Must send valid address to stake")]
30+
InvalidDenom(HumanAddr),
31+
32+
#[error("Missed address or denom")]
33+
MixedNativeAndCw20(String),
34+
2935
#[error("No funds sent")]
3036
NoFunds {},
3137
}

contracts/cw4-stake/src/msg.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1+
use cosmwasm_std::{Coin, HumanAddr, Uint128};
12
use schemars::JsonSchema;
23
use serde::{Deserialize, Serialize};
34

4-
use cosmwasm_std::{Coin, HumanAddr, Uint128};
55
use cw0::Duration;
6+
use cw20::Denom;
67
pub use cw_controllers::ClaimsResponse;
78

89
#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
910
pub struct InitMsg {
1011
/// denom of the token to stake
11-
pub denom: String,
12+
pub denom: Denom,
1213
pub tokens_per_weight: Uint128,
1314
pub min_bond: Uint128,
1415
pub unbonding_period: Duration,

contracts/cw4-stake/src/state.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
use cosmwasm_std::Uint128;
12
use schemars::JsonSchema;
23
use serde::{Deserialize, Serialize};
34

4-
use cosmwasm_std::Uint128;
55
use cw0::Duration;
6+
use cw20::Denom;
67
use cw4::TOTAL_KEY;
78
use cw_controllers::{Admin, Claims, Hooks};
89
use cw_storage_plus::{Item, Map, SnapshotMap, Strategy};
@@ -12,7 +13,7 @@ pub const CLAIMS: Claims = Claims::new("claims");
1213
#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
1314
pub struct Config {
1415
/// denom of the token to stake
15-
pub denom: String,
16+
pub denom: Denom,
1617
pub tokens_per_weight: Uint128,
1718
pub min_bond: Uint128,
1819
pub unbonding_period: Duration,

packages/cw0/src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ mod expiration;
33
mod pagination;
44
mod payment;
55

6-
pub use crate::balance::NativeBalance;
7-
pub use crate::expiration::{Duration, Expiration, DAY, HOUR, WEEK};
86
pub use pagination::{
97
calc_range_end_human, calc_range_start_human, calc_range_start_string, maybe_canonical,
108
};
119
pub use payment::{may_pay, must_pay, nonpayable, PaymentError};
10+
11+
pub use crate::balance::NativeBalance;
12+
pub use crate::expiration::{Duration, Expiration, DAY, HOUR, WEEK};

packages/cw20/src/balance.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1+
use cosmwasm_std::Coin;
12
use schemars::JsonSchema;
23
use serde::{Deserialize, Serialize};
34

4-
use cosmwasm_std::Coin;
55
use cw0::NativeBalance;
66

77
use crate::Cw20Coin;

0 commit comments

Comments
 (0)