Skip to content

Commit

Permalink
Merge pull request #167 from CosmWasm/token-weighted-group
Browse files Browse the repository at this point in the history
Token weighted group
  • Loading branch information
ethanfrey authored Dec 14, 2020
2 parents 940775a + 092032e commit 5b4dfd7
Show file tree
Hide file tree
Showing 55 changed files with 2,236 additions and 310 deletions.
36 changes: 36 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ workflows:
- contract_cw3_fixed_multisig
- contract_cw3_flex_multisig
- contract_cw4_group
- contract_cw4_stake
- contract_cw20_atomic_swap
- contract_cw20_base
- contract_cw20_escrow
Expand Down Expand Up @@ -247,6 +248,41 @@ jobs:
- target
key: cargocache-cw4-group-rust:1.47.0-{{ checksum "~/project/Cargo.lock" }}

contract_cw4_stake:
docker:
- image: rust:1.47.0
working_directory: ~/project/contracts/cw4-stake
steps:
- checkout:
path: ~/project
- run:
name: Version information
command: rustc --version; cargo --version; rustup --version
- restore_cache:
keys:
- cargocache-cw4-stake-rust:1.47.0-{{ checksum "~/project/Cargo.lock" }}
- run:
name: Unit Tests
env: RUST_BACKTRACE=1
command: cargo unit-test --locked
- run:
name: Build and run schema generator
command: cargo schema --locked
- run:
name: Ensure checked-in schemas are up-to-date
command: |
CHANGES_IN_REPO=$(git status --porcelain)
if [[ -n "$CHANGES_IN_REPO" ]]; then
echo "Repository is dirty. Showing 'git status' and 'git --no-pager diff' for debugging now:"
git status && git --no-pager diff
exit 1
fi
- save_cache:
paths:
- /usr/local/cargo/registry
- target
key: cargocache-cw4-stake-rust:1.47.0-{{ checksum "~/project/Cargo.lock" }}

contract_cw20_base:
docker:
- image: rust:1.47.0
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,6 @@ target/
hash.txt
contracts.txt
artifacts/

# code coverage
tarpaulin-report.*
17 changes: 17 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ incremental = false
codegen-units = 1
incremental = false

[profile.release.package.cw4-stake]
codegen-units = 1
incremental = false

[profile.release.package.cw20-atomic-swap]
codegen-units = 1
incremental = false
Expand Down
4 changes: 2 additions & 2 deletions contracts/cw20-staking/schema/claims_response.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@
"type": "object",
"required": [
"amount",
"released"
"release_at"
],
"properties": {
"amount": {
"$ref": "#/definitions/Uint128"
},
"released": {
"release_at": {
"$ref": "#/definitions/Expiration"
}
}
Expand Down
51 changes: 16 additions & 35 deletions contracts/cw20-staking/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use cosmwasm_std::{
attr, coin, to_binary, BankMsg, Binary, Decimal, Deps, DepsMut, Env, HandleResponse, HumanAddr,
InitResponse, MessageInfo, QuerierWrapper, StakingMsg, StdError, StdResult, Uint128, WasmMsg,
};

use cw0::claim::{claim_tokens, create_claim, CLAIMS};
use cw2::set_contract_version;
use cw20_base::allowances::{
handle_burn_from, handle_decrease_allowance, handle_increase_allowance, handle_send_from,
Expand All @@ -15,8 +17,7 @@ use cw20_base::state::{token_info, MinterData, TokenInfo};
use crate::error::ContractError;
use crate::msg::{ClaimsResponse, HandleMsg, InitMsg, InvestmentResponse, QueryMsg};
use crate::state::{
claims, claims_read, invest_info, invest_info_read, total_supply, total_supply_read, Claim,
InvestmentInfo, Supply,
invest_info, invest_info_read, total_supply, total_supply_read, InvestmentInfo, Supply,
};

const FALLBACK_RATIO: Decimal = Decimal::one();
Expand Down Expand Up @@ -278,15 +279,12 @@ pub fn unbond(
supply.claims += unbond;
totals.save(&supply)?;

// add a claim to this user to get their tokens after the unbonding period
claims(deps.storage).update(sender_raw.as_slice(), |old| -> Result<_, ContractError> {
let mut claims = old.unwrap_or_default();
claims.push(Claim {
amount: unbond,
released: invest.unbonding_period.after(&env.block),
});
Ok(claims)
})?;
create_claim(
deps.storage,
&sender_raw,
unbond,
invest.unbonding_period.after(&env.block),
)?;

// unbond them
let res = HandleResponse {
Expand Down Expand Up @@ -317,26 +315,9 @@ pub fn claim(deps: DepsMut, env: Env, info: MessageInfo) -> Result<HandleRespons
}

// check how much to send - min(balance, claims[sender]), and reduce the claim
// Ensure we have enough balance to cover this and only send some claims if that is all we can cover
let sender_raw = deps.api.canonical_address(&info.sender)?;
// Ensure we have enough balance to cover this and
// only send some claims if that is all we can cover
let cap = balance.amount;
let mut to_send = Uint128(0);
claims(deps.storage).update(sender_raw.as_slice(), |claim| -> Result<_, ContractError> {
let (_send, waiting): (Vec<_>, _) =
claim.unwrap_or_default().iter().cloned().partition(|c| {
// if mature and we can pay, then include in _send
if c.released.is_expired(&env.block) && to_send + c.amount <= cap {
to_send += c.amount;
true
} else {
// not to send, leave in waiting and save again
false
}
});
Ok(waiting)
})?;

let to_send = claim_tokens(deps.storage, &sender_raw, &env.block, Some(balance.amount))?;
if to_send == Uint128(0) {
return Err(ContractError::NothingToClaim {});
}
Expand Down Expand Up @@ -459,8 +440,8 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {

pub fn query_claims(deps: Deps, address: HumanAddr) -> StdResult<ClaimsResponse> {
let address_raw = deps.api.canonical_address(&address)?;
let claims = claims_read(deps.storage)
.may_load(address_raw.as_slice())?
let claims = CLAIMS
.may_load(deps.storage, &address_raw)?
.unwrap_or_default();
Ok(ClaimsResponse { claims })
}
Expand Down Expand Up @@ -492,7 +473,7 @@ mod tests {
mock_dependencies, mock_env, mock_info, MockQuerier, MOCK_CONTRACT_ADDR,
};
use cosmwasm_std::{coins, Coin, CosmosMsg, Decimal, FullDelegation, Validator};
use cw0::{Duration, DAY, HOUR, WEEK};
use cw0::{claim::Claim, Duration, DAY, HOUR, WEEK};
use std::str::FromStr;

fn sample_validator<U: Into<HumanAddr>>(addr: U) -> Validator {
Expand Down Expand Up @@ -846,7 +827,7 @@ mod tests {
// proper claims
let expected_claims = vec![Claim {
amount: bobs_claim,
released: (DAY * 3).after(&env.block),
release_at: (DAY * 3).after(&env.block),
}];
assert_eq!(expected_claims, get_claims(deps.as_ref(), &bob));

Expand Down Expand Up @@ -889,7 +870,7 @@ mod tests {
let bobs_claim = Uint128(540);
let original_claims = vec![Claim {
amount: bobs_claim,
released: (DAY * 3).after(&env.block),
release_at: (DAY * 3).after(&env.block),
}];
assert_eq!(original_claims, get_claims(deps.as_ref(), &bob));

Expand Down
4 changes: 1 addition & 3 deletions contracts/cw20-staking/src/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use cosmwasm_std::{Binary, Coin, Decimal, HumanAddr, Uint128};
use cw0::Duration;
use cw0::{claim::Claim, Duration};
use cw20::Expiration;

use crate::state::Claim;

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct InitMsg {
/// name of the derivative token
Expand Down
24 changes: 2 additions & 22 deletions contracts/cw20-staking/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,12 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use cosmwasm_std::{CanonicalAddr, Decimal, HumanAddr, Storage, Uint128};
use cosmwasm_storage::{
bucket, bucket_read, singleton, singleton_read, Bucket, ReadonlyBucket, ReadonlySingleton,
Singleton,
};
use cw0::{Duration, Expiration};
use cosmwasm_storage::{singleton, singleton_read, ReadonlySingleton, Singleton};
use cw0::Duration;

pub const KEY_INVESTMENT: &[u8] = b"invest";
pub const KEY_TOTAL_SUPPLY: &[u8] = b"total_supply";

pub const PREFIX_CLAIMS: &[u8] = b"claim";

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct Claim {
pub amount: Uint128,
pub released: Expiration,
}

/// claims are the claims to money being unbonded, index by claimer address
pub fn claims(storage: &mut dyn Storage) -> Bucket<Vec<Claim>> {
bucket(storage, PREFIX_CLAIMS)
}

pub fn claims_read(storage: &dyn Storage) -> ReadonlyBucket<Vec<Claim>> {
bucket_read(storage, PREFIX_CLAIMS)
}

/// Investment info is fixed at initialization, and is used to control the function of the contract
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct InvestmentInfo {
Expand Down
2 changes: 1 addition & 1 deletion contracts/cw3-flex-multisig/schema/handle_msg.json
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@
"type": "string"
},
"MemberChangedHookMsg": {
"description": "MemberChangedHookMsg should be de/serialized under `MemberChangedHook()` variant in a HandleMsg",
"description": "MemberChangedHookMsg should be de/serialized under `MemberChangedHook()` variant in a HandleMsg. This contains a list of all diffs on the given transaction.",
"type": "object",
"required": [
"diffs"
Expand Down
5 changes: 3 additions & 2 deletions contracts/cw3-flex-multisig/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,7 @@ mod tests {
use cw0::Duration;
use cw2::{query_contract_info, ContractVersion};
use cw4::{Cw4HandleMsg, Member};
use cw4_group::helpers::Cw4GroupContract;
use cw_multi_test::{next_block, App, Contract, ContractWrapper, SimpleBank};

use super::*;
Expand Down Expand Up @@ -1056,7 +1057,7 @@ mod tests {
// adds NEWBIE with 2 power -> with snapshot, invalid vote
// removes VOTER3 -> with snapshot, can vote and pass proposal
let newbie: &str = "newbie";
let update_msg = Cw4HandleMsg::UpdateMembers {
let update_msg = cw4_group::msg::HandleMsg::UpdateMembers {
remove: vec![VOTER3.into()],
add: vec![member(VOTER2, 7), member(newbie, 2)],
};
Expand Down Expand Up @@ -1135,7 +1136,7 @@ mod tests {
);

// Start a proposal to remove VOTER3 from the set
let update_msg = Cw4Contract(group_addr.clone())
let update_msg = Cw4GroupContract::new(group_addr.clone())
.update_members(vec![VOTER3.into()], vec![])
.unwrap();
let update_proposal = HandleMsg::Propose {
Expand Down
5 changes: 5 additions & 0 deletions contracts/cw4-group/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ name = "cw4-group"
version = "0.3.2"
authors = ["Ethan Frey <[email protected]>"]
edition = "2018"
description = "Simple cw4 implementation of group membership controlled by admin "
license = "Apache-2.0"
repository = "https://github.com/CosmWasm/cosmwasm-plus"
homepage = "https://cosmwasm.com"
documentation = "https://docs.cosmwasm.com"

exclude = [
# Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication.
Expand Down
12 changes: 10 additions & 2 deletions contracts/cw4-group/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,13 @@ decision-making.

## Messages

Update messages and queries are defined by the
[cw4 spec](../../packages/cw4/README.md). Please refer to it for more info.
Basic update messages, queries, and hooks are defined by the
[cw4 spec](../../packages/cw4/README.md). Please refer to it for more info.

`cw4-group` adds one message to control the group membership:

`UpdateMembers{add, remove}` - takes a membership diff and adds/updates the
members, as well as removing any provided addresses. If an address is on both
lists, it will be removed. If it appears multiple times in `add`, only the
last occurance will be used.

Loading

0 comments on commit 5b4dfd7

Please sign in to comment.