Skip to content

Commit

Permalink
Merge pull request #686 from public-awesome/serkan/splits-multiple-de…
Browse files Browse the repository at this point in the history
…nom-support

Update splits contract to support multiple denoms
  • Loading branch information
jhernandezb authored May 14, 2024
2 parents 1dd1e0b + 257b410 commit e0414c5
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 38 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ members = [
"contracts/factories/*",
"contracts/minters/*",
"contracts/whitelists/*",
"contracts/splits",
"test-suite/",
"e2e",
]
Expand Down
67 changes: 41 additions & 26 deletions contracts/splits/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
#[cfg(not(feature = "library"))]
use cosmwasm_std::entry_point;
use cosmwasm_std::{
coins, to_json_binary, Addr, BankMsg, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response,
StdResult, SubMsg, Uint128,
coins, ensure, to_json_binary, Addr, BankMsg, Binary, Coin, CosmosMsg, Deps, DepsMut, Env,
MessageInfo, Reply, Response, StdResult, SubMsg, Uint128,
};
use cw2::set_contract_version;
use cw4::{Cw4Contract, Member, MemberListResponse, MemberResponse};
use cw_utils::{maybe_addr, parse_reply_instantiate_data};
use sg_std::NATIVE_DENOM;

use crate::error::ContractError;
use crate::msg::{ExecuteMsg, Group, InstantiateMsg, QueryMsg};
Expand Down Expand Up @@ -71,14 +70,17 @@ pub fn execute(
ExecuteMsg::UpdateAdmin { admin } => {
Ok(ADMIN.execute_update_admin(deps, info, maybe_addr(api, admin)?)?)
}
ExecuteMsg::Distribute {} => execute_distribute(deps.as_ref(), env, info),
ExecuteMsg::Distribute { denom_list } => {
execute_distribute(deps.as_ref(), env, info, denom_list)
}
}
}

pub fn execute_distribute(
deps: Deps,
env: Env,
info: MessageInfo,
denom_list: Option<Vec<String>>,
) -> Result<Response, ContractError> {
if !can_distribute(deps, info)? {
return Err(ContractError::Unauthorized {});
Expand All @@ -94,32 +96,45 @@ pub fn execute_distribute(
count: members_count,
});
}

let funds = deps
.querier
.query_balance(env.contract.address, NATIVE_DENOM)?;
if funds.amount.is_zero() {
return Err(ContractError::NoFunds {});
let mut funds: Vec<Coin> = Vec::new();
if let Some(denom_list) = denom_list {
for denom in denom_list.iter() {
let balance = deps
.querier
.query_balance(env.contract.address.clone(), denom)?;
if balance.amount.is_zero() {
continue;
}
funds.push(balance);
}
} else {
funds = deps.querier.query_all_balances(env.contract.address)?;
}

// To avoid rounding errors, distribute funds modulo the total weight.
// Keep remaining balance in the contract.
let multiplier = funds.amount / Uint128::from(total_weight);
if multiplier.is_zero() {
return Err(ContractError::NotEnoughFunds { min: total_weight });
}
ensure!(!funds.is_empty(), ContractError::NoFunds {});

let msgs = members
.iter()
.filter(|m| m.weight > 0)
.map(|member| {
let amount = multiplier * Uint128::from(member.weight);
BankMsg::Send {
to_address: member.addr.clone(),
amount: coins(amount.u128(), funds.denom.clone()),
let mut msgs: Vec<CosmosMsg> = Vec::new();
for member in members.iter().filter(|m| m.weight > 0) {
for coin in funds.iter() {
// To avoid rounding errors, distribute funds modulo the total weight.
// Keep remaining balance in the contract.
let multiplier = coin.amount / Uint128::from(total_weight);
if multiplier.is_zero() {
continue;
}
})
.collect::<Vec<_>>();

let amount = Uint128::from(member.weight) * multiplier;
msgs.push(CosmosMsg::Bank(BankMsg::Send {
to_address: member.addr.clone(),
amount: coins(amount.u128(), coin.denom.clone()),
}));
}
}

ensure!(
!msgs.is_empty(),
ContractError::NotEnoughFunds { min: total_weight }
);

Ok(Response::new()
.add_attribute("action", "distribute")
Expand Down
2 changes: 1 addition & 1 deletion contracts/splits/src/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub struct InstantiateMsg {
#[cw_serde]
pub enum ExecuteMsg {
UpdateAdmin { admin: Option<String> },
Distribute {},
Distribute { denom_list: Option<Vec<String>> },
}

#[cw_serde]
Expand Down
20 changes: 10 additions & 10 deletions test-suite/src/splits/tests/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ mod tests {

let (splits_addr, _) = setup_test_case(&mut app, vec![], false);

let msg = ExecuteMsg::Distribute {};
let msg = ExecuteMsg::Distribute { denom_list: None };

let err = app
.execute_contract(Addr::unchecked(OWNER), splits_addr, &msg, &[])
Expand All @@ -286,10 +286,10 @@ mod tests {

let (splits_addr, _) = setup_test_case(&mut app, init_funds, false);

let msg = ExecuteMsg::Distribute {};
let msg = ExecuteMsg::Distribute { denom_list: None };

app.execute_contract(
Addr::unchecked("non_memeber".to_string()),
Addr::unchecked("non_member".to_string()),
splits_addr,
&msg,
&[],
Expand All @@ -305,7 +305,7 @@ mod tests {

let (splits_addr, _) = setup_test_case_with_internal_group(&mut app, init_funds);

let msg = ExecuteMsg::Distribute {};
let msg = ExecuteMsg::Distribute { denom_list: None };

app.execute_contract(Addr::unchecked(OWNER), splits_addr.clone(), &msg, &[])
.unwrap();
Expand Down Expand Up @@ -345,7 +345,7 @@ mod tests {
setup_test_case_with_internal_group(&mut app, init_funds);
let total_weight = Cw4Contract(group_addr).total_weight(&app.wrap()).unwrap();

let msg = ExecuteMsg::Distribute {};
let msg = ExecuteMsg::Distribute { denom_list: None };

let err = app
.execute_contract(Addr::unchecked(OWNER), splits_addr, &msg, &[])
Expand All @@ -369,7 +369,7 @@ mod tests {
let multiplier = init_funds[0].amount / Uint128::from(total_weight);
let contract_balance = init_funds[0].amount - multiplier * Uint128::from(total_weight);

let msg = ExecuteMsg::Distribute {};
let msg = ExecuteMsg::Distribute { denom_list: None };

let _ = app
.execute_contract(Addr::unchecked(OWNER), splits_addr.clone(), &msg, &[])
Expand Down Expand Up @@ -403,7 +403,7 @@ mod tests {

let (splits_addr, _) = setup_test_case_with_overflow_group(&mut app, init_funds);

let msg = ExecuteMsg::Distribute {};
let msg = ExecuteMsg::Distribute { denom_list: None };
let err = app
.execute_contract(Addr::unchecked(OWNER), splits_addr, &msg, &[])
.unwrap_err();
Expand Down Expand Up @@ -433,7 +433,7 @@ mod tests {
.execute_contract(Addr::unchecked(OWNER), group_addr, &msg, &[])
.unwrap();

let msg = ExecuteMsg::Distribute {};
let msg = ExecuteMsg::Distribute { denom_list: None };
let _ = app
.execute_contract(Addr::unchecked(OWNER), splits_addr, &msg, &[])
.unwrap();
Expand Down Expand Up @@ -465,7 +465,7 @@ mod tests {
let contract_balance = init_funds[0].amount - multiplier * Uint128::from(total_weight);
let mut payouts = vec![];

let msg = ExecuteMsg::Distribute {};
let msg = ExecuteMsg::Distribute { denom_list: None };

let _ = app
.execute_contract(Addr::unchecked(OWNER), splits_addr.clone(), &msg, &[])
Expand Down Expand Up @@ -534,7 +534,7 @@ mod tests {
);

// distribute again and check accounting
let msg = ExecuteMsg::Distribute {};
let msg = ExecuteMsg::Distribute { denom_list: None };
let _ = app
.execute_contract(Addr::unchecked(OWNER), splits_addr.clone(), &msg, &[])
.unwrap();
Expand Down
2 changes: 1 addition & 1 deletion test-suite/src/vending_minter/tests/splits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ fn mint_and_split() {
);
assert!(res.is_ok());

let dist_msg = SplitsExecuteMsg::Distribute {};
let dist_msg = SplitsExecuteMsg::Distribute { denom_list: None };
let res = app.execute_contract(Addr::unchecked(OWNER), splits_addr, &dist_msg, &[]);
assert!(res.is_ok());

Expand Down

0 comments on commit e0414c5

Please sign in to comment.