Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Token weighted group #167

Merged
merged 22 commits into from
Dec 14, 2020
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
47 changes: 14 additions & 33 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
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
9 changes: 5 additions & 4 deletions contracts/cw3-flex-multisig/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,8 @@ mod tests {

use cw0::Duration;
use cw2::{query_contract_info, ContractVersion};
use cw4::{Cw4HandleMsg, Member};
use cw4::Member;
use cw4_group::helpers::Cw4GroupContract;
use cw_multi_test::{next_block, App, Contract, ContractWrapper, SimpleBank};

use super::*;
Expand Down Expand Up @@ -559,7 +560,7 @@ mod tests {

// 3. (Optional) Set the multisig as the group owner
if multisig_as_group_admin {
let update_admin = Cw4HandleMsg::UpdateAdmin {
ethanfrey marked this conversation as resolved.
Show resolved Hide resolved
let update_admin = cw4_group::msg::HandleMsg::UpdateAdmin {
admin: Some(flex_addr.clone()),
};
app.execute_contract(OWNER, &group_addr, &update_admin, &[])
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
18 changes: 16 additions & 2 deletions contracts/cw4-group/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,19 @@ 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 two messages:

`UpdateAdmin{admin}` - changes (or clears) the admin for the contract
ethanfrey marked this conversation as resolved.
Show resolved Hide resolved

`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.
ethanfrey marked this conversation as resolved.
Show resolved Hide resolved

as well as one query:

`Admin{}` - Returns the `admin` address, or `None` if unset.

Loading