-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #45 from WHELP-project/44-create-new-contract-skel…
…eton Fee splitter: initial skeleton
- Loading branch information
Showing
13 changed files
with
308 additions
and
7 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
[alias] | ||
wasm = "build --release --lib --target wasm32-unknown-unknown" | ||
wasm-debug = "build --lib --target wasm32-unknown-unknown" | ||
unit-test = "test --lib" | ||
integration-test = "test --test integration" | ||
schema = "run --bin fee_splitter_schema" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
[package] | ||
name = "fee-splitter" | ||
version = { workspace = true } | ||
authors = ["Jakub <[email protected]>", "Kalo <[email protected]"] | ||
edition = { workspace = true } | ||
description = "Fee Splitter - split payments and distribute between multiple recipients" | ||
license = { workspace = true } | ||
|
||
[lib] | ||
crate-type = ["cdylib", "rlib"] | ||
|
||
[features] | ||
backtraces = ["cosmwasm-std/backtraces"] | ||
library = [] | ||
|
||
[dependencies] | ||
coreum-wasm-sdk = { workspace = true } | ||
cosmwasm-schema = { workspace = true } | ||
cosmwasm-std = { workspace = true } | ||
cw-storage-plus = { workspace = true } | ||
cw2 = { workspace = true } | ||
cw20 = { workspace = true } | ||
cw-utils = { workspace = true } | ||
thiserror = { workspace = true } | ||
dex = { workspace = true } | ||
dex-stake = { workspace = true } | ||
itertools = { workspace = true } | ||
|
||
[dev-dependencies] | ||
anyhow = { workspace = true } | ||
bindings-test = { workspace = true } | ||
cw-multi-test = { workspace = true } | ||
cw20-base = { workspace = true } | ||
# dex-factory = { workspace = true } | ||
dex-pool = { workspace = true } | ||
dex-stake = { workspace = true } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
WIP |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
use coreum_wasm_sdk::core::{CoreumMsg, CoreumQueries}; | ||
use cosmwasm_std::{ | ||
coin, entry_point, to_json_binary, Addr, BankMsg, Binary, Coin, CosmosMsg, Decimal, Deps, | ||
DepsMut, Env, MessageInfo, StdError, StdResult, WasmMsg, | ||
}; | ||
use cw20::{BalanceResponse, Cw20ExecuteMsg, Cw20QueryMsg}; | ||
use cw_storage_plus::Item; | ||
|
||
use crate::{ | ||
error::ContractError, | ||
msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, | ||
state::Config, | ||
}; | ||
|
||
/// Saves factory settings | ||
pub const CONFIG: Item<Config> = Item::new("config"); | ||
|
||
pub type Response = cosmwasm_std::Response<CoreumMsg>; | ||
pub type SubMsg = cosmwasm_std::SubMsg<CoreumMsg>; | ||
|
||
/// Contract name that is used for migration. | ||
const _CONTRACT_NAME: &str = "fee-splitter"; | ||
/// Contract version that is used for migration. | ||
const _CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); | ||
|
||
/// Creates a new contract with the specified parameters packed in the `msg` variable. | ||
/// | ||
/// * **msg** is message which contains the parameters used for creating the contract. | ||
#[cfg_attr(not(feature = "library"), entry_point)] | ||
pub fn instantiate( | ||
deps: DepsMut<CoreumQueries>, | ||
_env: Env, | ||
_info: MessageInfo, | ||
msg: InstantiateMsg, | ||
) -> Result<Response, ContractError> { | ||
let is_weights_valid = msg | ||
.addresses | ||
.iter() | ||
.map(|&(_, weight)| weight) | ||
.fold(Decimal::zero(), |acc, x| acc + x) | ||
.le(&Decimal::from_ratio(1u32, 1u32)); | ||
|
||
if !is_weights_valid { | ||
return Err(ContractError::InvalidWeights {}); | ||
} | ||
|
||
let config = Config { | ||
addresses: msg.addresses, | ||
}; | ||
|
||
CONFIG.save(deps.storage, &config)?; | ||
|
||
Ok(Response::new().add_attribute("initialized", "contract")) | ||
} | ||
|
||
#[cfg_attr(not(feature = "library"), entry_point)] | ||
pub fn execute( | ||
deps: Deps<CoreumQueries>, | ||
env: Env, | ||
msg: ExecuteMsg, | ||
) -> Result<Response, ContractError> { | ||
match msg.clone() { | ||
ExecuteMsg::SendTokens { | ||
native_denoms, | ||
cw20_addresses, | ||
} => execute_send_tokens(deps, env, native_denoms, cw20_addresses), | ||
} | ||
} | ||
|
||
fn execute_send_tokens( | ||
deps: Deps<CoreumQueries>, | ||
env: Env, | ||
native_denoms: Vec<String>, | ||
cw20_addresses: Vec<String>, | ||
) -> Result<Response, ContractError> { | ||
let config = query_config(deps)?; | ||
|
||
let contract_address = env.contract.address.to_string(); | ||
// gather balances of native tokens, either from function parameter or all | ||
let native_balances = native_denoms | ||
.into_iter() | ||
.map(|denom| deps.querier.query_balance(&env.contract.address, denom)) | ||
.collect::<StdResult<Vec<Coin>>>()?; | ||
|
||
// gather addresses of cw20 token contract, either from arguments or configuration | ||
let cw20_addresses = cw20_addresses | ||
.into_iter() | ||
.map(|address| deps.api.addr_validate(&address)) | ||
.collect::<StdResult<Vec<Addr>>>()?; | ||
|
||
let mut messages: Vec<CosmosMsg<CoreumMsg>> = vec![]; | ||
|
||
for (address, weight) in config.addresses { | ||
let amount = native_balances | ||
.iter() | ||
.filter_map(|bcoin| { | ||
let amount = bcoin.amount * weight; | ||
if amount.is_zero() { | ||
None | ||
} else { | ||
Some(coin((bcoin.amount * weight).u128(), &bcoin.denom)) | ||
} | ||
}) | ||
.collect::<Vec<Coin>>(); | ||
if !amount.is_empty() { | ||
let native_message = CosmosMsg::Bank(BankMsg::Send { | ||
to_address: address.to_string(), | ||
amount, | ||
}); | ||
messages.push(native_message); | ||
} | ||
|
||
cw20_addresses | ||
.iter() | ||
// filter out if balance is zero in order to avoid empty transfer error | ||
.filter_map(|token| { | ||
match deps.querier.query_wasm_smart::<BalanceResponse>( | ||
token, | ||
&Cw20QueryMsg::Balance { | ||
address: contract_address.clone(), | ||
}, | ||
) { | ||
Ok(r) => { | ||
if !r.balance.is_zero() { | ||
Some((token, r.balance)) | ||
} else { | ||
None | ||
} | ||
} | ||
// the only victim of current design | ||
Err(_) => None, | ||
} | ||
}) | ||
.try_for_each(|(token, balance)| { | ||
let msg = CosmosMsg::Wasm(WasmMsg::Execute { | ||
contract_addr: token.to_string(), | ||
msg: to_json_binary(&Cw20ExecuteMsg::Transfer { | ||
recipient: address.to_string(), | ||
amount: balance * weight, | ||
})?, | ||
funds: vec![], | ||
}); | ||
messages.push(msg); | ||
Ok::<(), StdError>(()) | ||
})?; | ||
} | ||
Ok(Response::new().add_messages(messages)) | ||
} | ||
|
||
#[cfg_attr(not(feature = "library"), entry_point)] | ||
pub fn query(deps: Deps<CoreumQueries>, _env: Env, msg: QueryMsg) -> StdResult<Binary> { | ||
match msg { | ||
QueryMsg::Config {} => to_json_binary(&query_config(deps)?), | ||
} | ||
} | ||
|
||
pub fn query_config(deps: Deps<CoreumQueries>) -> StdResult<Config> { | ||
let config = CONFIG.load(deps.storage)?; | ||
let resp = Config { | ||
addresses: config.addresses, | ||
}; | ||
|
||
Ok(resp) | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
#[test] | ||
#[ignore] | ||
fn instantiate_with_invalid_weights_should_throw_error() { | ||
todo!() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
use cosmwasm_std::StdError; | ||
use thiserror::Error; | ||
|
||
/// This enum describes factory contract errors | ||
#[derive(Error, Debug, PartialEq)] | ||
pub enum ContractError { | ||
#[error("{0}")] | ||
Std(#[from] StdError), | ||
|
||
#[error("Provided weights exceed maximum allowed value")] | ||
InvalidWeights {}, | ||
|
||
#[error("Unauthorized")] | ||
Unauthorized {}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
pub mod contract; | ||
pub mod error; | ||
pub mod msg; | ||
pub mod state; | ||
|
||
#[cfg(test)] | ||
mod testing; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
use cosmwasm_schema::{cw_serde, QueryResponses}; | ||
use cosmwasm_std::Decimal; | ||
|
||
#[cw_serde] | ||
pub struct InstantiateMsg { | ||
// List of addresses and their weights. | ||
// Weights must sum up to 1.0 | ||
pub addresses: Vec<(String, Decimal)>, | ||
// List of cw20 token addresses to check for balance | ||
pub cw20_contracts: Vec<String>, | ||
} | ||
|
||
#[cw_serde] | ||
pub enum ExecuteMsg { | ||
// Transfers tokens send to this contract based on weights from configuration. | ||
// Any user can execute it | ||
SendTokens { | ||
// Provide denoms of native tokens to check | ||
native_denoms: Vec<String>, | ||
// Provide addresses of cw20 contracts to check | ||
// If None, contract will query adresses from Config | ||
cw20_addresses: Vec<String>, | ||
}, | ||
} | ||
|
||
#[cw_serde] | ||
#[derive(QueryResponses)] | ||
pub enum QueryMsg { | ||
#[returns(crate::state::Config)] | ||
Config {}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
use cosmwasm_schema::cw_serde; | ||
use cosmwasm_std::Decimal; | ||
|
||
#[cw_serde] | ||
pub struct Config { | ||
// List of addresses and their weights. | ||
// Weights must sum up to 1.0 | ||
pub addresses: Vec<(String, Decimal)>, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters