Skip to content

Commit

Permalink
Use cw-denom and payment utils in gauge adapter
Browse files Browse the repository at this point in the history
TODO: 2 tests are failing, because the cw20 cannot be validated with cw-denom. This should be fixed with a cw-orch refactor.
  • Loading branch information
ismellike committed Jul 13, 2024
1 parent 015576b commit 969a41d
Show file tree
Hide file tree
Showing 11 changed files with 155 additions and 147 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 contracts/gauges/gauge-adapter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ cw20 = { workspace = true }
cw-utils = { workspace = true }
semver = { workspace = true }
thiserror = { workspace = true }
cw-denom = { workspace = true }

[dev-dependencies]
anyhow = { workspace = true }
Expand Down
178 changes: 83 additions & 95 deletions contracts/gauges/gauge-adapter/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
#[cfg(not(feature = "library"))]
use cosmwasm_std::entry_point;
use cosmwasm_std::{
coins, from_json, to_json_binary, Addr, BankMsg, Binary, CosmosMsg, Deps, DepsMut, Env,
MessageInfo, Order, Response, StdError, StdResult, Uint128, WasmMsg,
from_json, to_json_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Order, Response,
StdResult, Uint128,
};
use cw2::set_contract_version;
use cw20::{Cw20ExecuteMsg, Cw20ReceiveMsg};
use cw20::Cw20ReceiveMsg;
use cw_denom::UncheckedDenom;
use cw_utils::{one_coin, PaymentError};

use crate::error::ContractError;
use crate::msg::{AdapterQueryMsg, ExecuteMsg, InstantiateMsg, MigrateMsg, ReceiveMsg};
use crate::state::{AssetType, Config, Submission, CONFIG, SUBMISSIONS};
use crate::msg::{
AdapterQueryMsg, AssetUnchecked, ExecuteMsg, InstantiateMsg, MigrateMsg, ReceiveMsg,
};
use crate::state::{Config, Submission, CONFIG, SUBMISSIONS};

// Version info for migration info.
const CONTRACT_NAME: &str = "crates.io:marketing-gauge-adapter";
Expand Down Expand Up @@ -37,9 +41,12 @@ pub fn instantiate(

let config = Config {
admin: deps.api.addr_validate(&msg.admin)?,
required_deposit: msg.required_deposit,
required_deposit: msg
.required_deposit
.map(|x| x.into_checked(deps.as_ref()))
.transpose()?,
community_pool,
reward: msg.reward,
reward: msg.reward.into_checked(deps.as_ref())?,
};
CONFIG.save(deps.storage, &config)?;

Expand All @@ -56,17 +63,17 @@ pub fn execute(
match msg {
ExecuteMsg::Receive(msg) => receive_cw20_message(deps, info, msg),
ExecuteMsg::CreateSubmission { name, url, address } => {
// TODO this is very hacky
let received = info.funds.into_iter().next().unwrap_or_default();
execute::create_submission(
deps,
info.sender,
name,
url,
address,
received.amount,
AssetType::Native(received.denom),
)
let received = match one_coin(&info) {
Ok(coin) => Ok(Some(coin)),
Err(PaymentError::NoFunds {}) => Ok(None),
Err(error) => Err(error),
}?
.map(|x| AssetUnchecked {
denom: UncheckedDenom::Native(x.denom),
amount: x.amount,
});

execute::create_submission(deps, info.sender, name, url, address, received)
}
ExecuteMsg::ReturnDeposits {} => execute::return_deposits(deps, info.sender),
}
Expand All @@ -78,44 +85,17 @@ fn receive_cw20_message(
msg: Cw20ReceiveMsg,
) -> Result<Response, ContractError> {
match from_json(&msg.msg)? {
ReceiveMsg::CreateSubmission { name, url, address } => {
let denom = AssetType::Cw20(info.sender.to_string());
execute::create_submission(
deps,
Addr::unchecked(msg.sender),
name,
url,
address,
msg.amount,
denom,
)
}
}
}

fn create_bank_msg(
denom: &AssetType,
amount: Uint128,
recipient: Addr,
) -> Result<CosmosMsg, StdError> {
match denom {
AssetType::Cw20(address) => Ok(WasmMsg::Execute {
contract_addr: address.to_string(),
msg: to_json_binary(&Cw20ExecuteMsg::Transfer {
recipient: recipient.to_string(),
amount,
})?,
funds: vec![],
}
.into()),
AssetType::Native(denom) => {
let amount = coins(amount.u128(), denom);
Ok(BankMsg::Send {
to_address: recipient.to_string(),
amount,
}
.into())
}
ReceiveMsg::CreateSubmission { name, url, address } => execute::create_submission(
deps,
Addr::unchecked(msg.sender),
name,
url,
address,
Some(AssetUnchecked::new_cw20(
info.sender.as_str(),
msg.amount.u128(),
)),
),
}
}

Expand All @@ -130,8 +110,7 @@ pub mod execute {
name: String,
url: String,
address: String,
received_amount: Uint128,
received_denom: AssetType,
received: Option<AssetUnchecked>,
) -> Result<Response, ContractError> {
let address = deps.api.addr_validate(&address)?;

Expand All @@ -142,20 +121,23 @@ pub mod execute {
admin: _,
} = CONFIG.load(deps.storage)?;
if let Some(required_deposit) = required_deposit {
if AssetType::Native("".into()) == received_denom {
return Err(ContractError::DepositRequired {});
}
if required_deposit.denom != received_denom {
return Err(ContractError::InvalidDepositType {});
if let Some(received) = received {
let received_denom = received.denom.into_checked(deps.as_ref())?;

if required_deposit.denom != received_denom {
return Err(ContractError::InvalidDepositType {});
}
if received.amount != required_deposit.amount {
return Err(ContractError::InvalidDepositAmount {
correct_amount: required_deposit.amount,
});
}
} else {
return Err(ContractError::PaymentError(PaymentError::NoFunds {}));
}
if received_amount != required_deposit.amount {
return Err(ContractError::InvalidDepositAmount {
correct_amount: required_deposit.amount,
});
}
} else {
} else if let Some(received) = received {
// If no deposit is required, then any deposit invalidates a submission.
if !received_amount.is_zero() {
if !received.amount.is_zero() {
return Err(ContractError::InvalidDepositAmount {
correct_amount: Uint128::zero(),
});
Expand Down Expand Up @@ -190,11 +172,10 @@ pub mod execute {
.range(deps.storage, None, None, Order::Ascending)
.map(|item| {
let (_submission_recipient, submission) = item?;
create_bank_msg(
&required_deposit.denom,
required_deposit.amount,
submission.sender,
)

required_deposit
.denom
.get_transfer_to_message(&submission.sender, required_deposit.amount)
})
.collect::<StdResult<Vec<CosmosMsg>>>()?;

Expand All @@ -221,7 +202,7 @@ pub fn query(deps: Deps, _env: Env, msg: AdapterQueryMsg) -> StdResult<Binary> {
}

mod query {
use cosmwasm_std::{CosmosMsg, Decimal};
use cosmwasm_std::{CosmosMsg, Decimal, StdError};

use crate::msg::{
AllOptionsResponse, AllSubmissionsResponse, CheckOptionResponse, SampleGaugeMsgsResponse,
Expand Down Expand Up @@ -254,12 +235,16 @@ mod query {
let execute = winners
.into_iter()
.map(|(to_address, fraction)| {
// Gauge already sents chosen tally to this query by using results we send in
// Gauge already sends chosen tally to this query by using results we send in
// all_options query; they are already validated
create_bank_msg(
&reward.denom,
fraction * reward.amount,
Addr::unchecked(to_address),
let to_address = deps.api.addr_validate(&to_address)?;

reward.denom.get_transfer_to_message(
&to_address,
reward
.amount
.checked_mul_floor(fraction)
.map_err(|x| StdError::generic_err(x.to_string()))?,
)
})
.collect::<StdResult<Vec<CosmosMsg>>>()?;
Expand Down Expand Up @@ -306,20 +291,23 @@ mod tests {
use super::*;

use cosmwasm_std::{
coins,
testing::{mock_dependencies, mock_env, mock_info},
Decimal, Uint128,
BankMsg, CosmosMsg, Decimal, Uint128, WasmMsg,
};
use cw20::Cw20ExecuteMsg;
use cw_denom::CheckedDenom;

use crate::state::Asset;
use crate::{msg::AssetUnchecked, state::Asset};

#[test]
fn proper_initialization() {
let mut deps = mock_dependencies();
let msg = InstantiateMsg {
admin: "admin".to_owned(),
required_deposit: Some(Asset::new_cw20("wynd", 10_000_000)),
required_deposit: Some(AssetUnchecked::new_cw20("wynd", 10_000_000)),
community_pool: "community".to_owned(),
reward: Asset::new_native("ujuno", 150_000_000_000),
reward: AssetUnchecked::new_native("ujuno", 150_000_000_000),
};
instantiate(
deps.as_mut(),
Expand All @@ -335,21 +323,21 @@ mod tests {
assert_eq!(
config.required_deposit,
Some(Asset {
denom: AssetType::Cw20("wynd".to_owned()),
denom: CheckedDenom::Cw20(Addr::unchecked("wynd")),
amount: Uint128::new(10_000_000)
})
);
assert_eq!(config.community_pool, "community".to_owned());
assert_eq!(
config.reward,
Asset {
denom: AssetType::Native("ujuno".to_owned()),
denom: CheckedDenom::Native("ujuno".to_owned()),
amount: Uint128::new(150_000_000_000)
}
);

let msg = InstantiateMsg {
reward: Asset::new_native("ujuno", 10_000_000),
reward: AssetUnchecked::new_native("ujuno", 10_000_000),
..msg
};
instantiate(
Expand All @@ -363,7 +351,7 @@ mod tests {
assert_eq!(
config.reward,
Asset {
denom: AssetType::Native("ujuno".to_owned()),
denom: CheckedDenom::Native("ujuno".to_owned()),
amount: Uint128::new(10_000_000)
}
);
Expand All @@ -384,9 +372,9 @@ mod tests {
let reward = Uint128::new(150_000_000_000);
let msg = InstantiateMsg {
admin: "admin".to_owned(),
required_deposit: Some(Asset::new_cw20("wynd", 10_000_000)),
required_deposit: Some(AssetUnchecked::new_cw20("wynd", 10_000_000)),
community_pool: "community".to_owned(),
reward: Asset::new_native("ujuno", reward.into()),
reward: AssetUnchecked::new_native("ujuno", reward.into()),
};
instantiate(deps.as_mut(), mock_env(), mock_info("user", &[]), msg).unwrap();

Expand Down Expand Up @@ -432,9 +420,9 @@ mod tests {
let reward = Uint128::new(150_000_000_000);
let msg = InstantiateMsg {
admin: "admin".to_owned(),
required_deposit: Some(Asset::new_cw20("wynd", 10_000_000)),
required_deposit: Some(AssetUnchecked::new_cw20("wynd", 10_000_000)),
community_pool: "community".to_owned(),
reward: Asset::new_cw20("wynd", reward.into()),
reward: AssetUnchecked::new_cw20("wynd", reward.into()),
};
instantiate(deps.as_mut(), mock_env(), mock_info("user", &[]), msg).unwrap();

Expand Down Expand Up @@ -495,7 +483,7 @@ mod tests {
admin: "admin".to_owned(),
required_deposit: None,
community_pool: "community".to_owned(),
reward: Asset::new_native("ujuno", 150_000_000_000),
reward: AssetUnchecked::new_native("ujuno", 150_000_000_000),
};
instantiate(
deps.as_mut(),
Expand All @@ -509,7 +497,7 @@ mod tests {
assert_eq!(err, ContractError::NoDepositToRefund {});

let msg = InstantiateMsg {
required_deposit: Some(Asset::new_native("ujuno", 10_000_000)),
required_deposit: Some(AssetUnchecked::new_native("ujuno", 10_000_000)),
..msg
};
instantiate(deps.as_mut(), mock_env(), mock_info("user", &[]), msg).unwrap();
Expand Down
11 changes: 8 additions & 3 deletions contracts/gauges/gauge-adapter/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
use cosmwasm_std::{StdError, Uint128};
use cw_denom::DenomError;
use cw_utils::PaymentError;
use thiserror::Error;

#[derive(Error, Debug, PartialEq)]
pub enum ContractError {
#[error("{0}")]
Std(#[from] StdError),

#[error("{0}")]
PaymentError(#[from] PaymentError),

#[error("{0}")]
DenomError(#[from] DenomError),

#[error("Operation unauthorized - only admin can release deposits")]
Unauthorized {},

Expand All @@ -20,7 +28,4 @@ pub enum ContractError {

#[error("No deposit was required, therefore no deposit can be returned")]
NoDepositToRefund {},

#[error("Deposit required, cannot create submission.")]
DepositRequired {},
}
27 changes: 27 additions & 0 deletions contracts/gauges/gauge-adapter/src/helpers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use cosmwasm_std::Deps;
use cw_denom::{DenomError, UncheckedDenom};

use crate::{msg::AssetUnchecked, state::Asset};

impl AssetUnchecked {
pub fn into_checked(self, deps: Deps) -> Result<Asset, DenomError> {
Ok(Asset {
denom: self.denom.into_checked(deps)?,
amount: self.amount,
})
}

pub fn new_native(denom: &str, amount: u128) -> Self {
Self {
denom: UncheckedDenom::Native(denom.to_owned()),
amount: amount.into(),
}
}

pub fn new_cw20(denom: &str, amount: u128) -> Self {
Self {
denom: UncheckedDenom::Cw20(denom.to_owned()),
amount: amount.into(),
}
}
}
Loading

0 comments on commit 969a41d

Please sign in to comment.