-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(nusd): finish execute entry point. Unit test add_denom and remov…
…e_denom
- Loading branch information
1 parent
4cfcfae
commit becc614
Showing
3 changed files
with
426 additions
and
0 deletions.
There are no files selected for viewing
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,309 @@ | ||
use cosmwasm_std::{entry_point, DepsMut, Env, MessageInfo, Response}; | ||
|
||
use cw2::set_contract_version; | ||
|
||
use crate::{ | ||
error::ContractError, | ||
events::{ | ||
denom_set_json, event_add_denom, event_change_denom, event_remove_denom, | ||
}, | ||
msgs::{ExecuteMsg, InstantiateMsg, MigrateMsg}, | ||
state::ACCEPTED_DENOMS, | ||
}; | ||
|
||
#[cfg_attr(not(feature = "library"), cosmwasm_std::entry_point)] | ||
pub fn execute( | ||
deps: DepsMut, | ||
env: Env, | ||
info: MessageInfo, | ||
msg: ExecuteMsg, | ||
) -> Result<Response, ContractError> { | ||
match msg { | ||
ExecuteMsg::ChangeDenom { from, to } => { | ||
cw_ownable::assert_owner(deps.storage, &info.sender)?; | ||
|
||
// "from" should be within the list of accepted denoms | ||
let mut denom_set = ACCEPTED_DENOMS.load(deps.storage)?; | ||
if !denom_set.contains(&from) { | ||
return Err(ContractError::RemoveNonexistentDenom { | ||
denom: from.clone(), | ||
denom_set, | ||
}); | ||
} | ||
|
||
// Remove `from` and add `to` to the set | ||
denom_set.remove(&from); | ||
denom_set.insert(to.clone()); | ||
ACCEPTED_DENOMS.save(deps.storage, &denom_set)?; | ||
|
||
let event = event_change_denom( | ||
from.as_str(), | ||
to.as_str(), | ||
denom_set_json(denom_set)?.as_str(), | ||
); | ||
|
||
Ok(Response::default().add_event(event)) | ||
} | ||
ExecuteMsg::AddDenom { denom } => { | ||
cw_ownable::assert_owner(deps.storage, &info.sender)?; | ||
|
||
let mut denom_set = ACCEPTED_DENOMS.load(deps.storage)?; | ||
if denom_set.contains(&denom) { | ||
return Err(ContractError::AddExistentDenom { | ||
denom, | ||
denom_set: denom_set.clone(), // Cloning the set for error details | ||
}); | ||
} | ||
denom_set.insert(denom.clone()); | ||
ACCEPTED_DENOMS.save(deps.storage, &denom_set)?; | ||
|
||
let event = | ||
event_add_denom(&denom, denom_set_json(denom_set)?.as_str()); | ||
Ok(Response::default().add_event(event)) | ||
} | ||
|
||
ExecuteMsg::RemoveDenom { denom } => { | ||
cw_ownable::assert_owner(deps.storage, &info.sender)?; | ||
let mut denom_set = ACCEPTED_DENOMS.load(deps.storage)?; | ||
let was_present = denom_set.remove(&denom); | ||
if !was_present { | ||
return Err(ContractError::RemoveNonexistentDenom { | ||
denom, | ||
denom_set, | ||
}); | ||
} | ||
ACCEPTED_DENOMS.save(deps.storage, &denom_set)?; | ||
|
||
let event = event_remove_denom( | ||
denom.as_str(), | ||
denom_set_json(denom_set)?.as_str(), | ||
); | ||
Ok(Response::default().add_event(event)) | ||
} | ||
|
||
ExecuteMsg::UpdateOwnership(action) => { | ||
Ok(execute_update_ownership(deps, env, info, action)?) | ||
} | ||
} | ||
} | ||
|
||
fn execute_update_ownership( | ||
deps: DepsMut, | ||
env: Env, | ||
info: MessageInfo, | ||
action: cw_ownable::Action, | ||
) -> Result<Response, cw_ownable::OwnershipError> { | ||
let ownership = | ||
cw_ownable::update_ownership(deps, &env.block, &info.sender, action)?; | ||
Ok(Response::new().add_attributes(ownership.into_attributes())) | ||
} | ||
|
||
#[cfg_attr(not(feature = "library"), entry_point)] | ||
pub fn migrate( | ||
deps: DepsMut, | ||
_env: Env, | ||
_msg: MigrateMsg, | ||
) -> Result<Response, ContractError> { | ||
// migrations::v2_0_0::migrate(deps, env, msg) | ||
|
||
// TODO: Handle state migration here. | ||
|
||
set_contract_version( | ||
deps.storage, | ||
format!("crates.io:{CONTRACT_NAME}"), | ||
CONTRACT_VERSION, | ||
)?; | ||
|
||
// TODO: from_version Fix this later. | ||
let from_version = "v0.1.0"; | ||
|
||
Ok(Response::new() | ||
.add_attribute("action", "migrate") | ||
.add_attribute("from_version", from_version) | ||
.add_attribute("to_version", CONTRACT_VERSION)) | ||
} | ||
|
||
pub const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); | ||
pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); | ||
|
||
#[cfg_attr(not(feature = "library"), cosmwasm_std::entry_point)] | ||
pub fn instantiate( | ||
deps: DepsMut, | ||
_env: Env, | ||
_info: MessageInfo, | ||
msg: InstantiateMsg, | ||
) -> Result<Response, ContractError> { | ||
set_contract_version( | ||
deps.storage, | ||
format!("crates.io:{CONTRACT_NAME}"), | ||
CONTRACT_VERSION, | ||
)?; | ||
cw_ownable::initialize_owner(deps.storage, deps.api, Some(&msg.owner))?; | ||
ACCEPTED_DENOMS.save(deps.storage, &msg.accepted_denoms)?; | ||
Ok(Response::default()) | ||
} | ||
|
||
#[cfg(test)] | ||
pub mod tests { | ||
use cosmwasm_std::Response; | ||
|
||
use crate::{ | ||
contract::execute, | ||
error::ContractError, | ||
msgs::{ExecuteMsg, QueryMsg}, | ||
queries::query, | ||
testing::{self, TestResult, TEST_DENOM}, | ||
}; | ||
|
||
#[test] | ||
fn add_denom() -> TestResult { | ||
let accepted_denoms_init = vec![]; | ||
let (mut deps, env, info) = | ||
testing::setup_contract(accepted_denoms_init)?; | ||
|
||
// Test adding a new denomination | ||
let denom = "testdenom".to_string(); | ||
let msg = ExecuteMsg::AddDenom { | ||
denom: denom.clone(), | ||
}; | ||
let res: Response = | ||
execute(deps.as_mut(), env.clone(), info.clone(), msg.clone())?; | ||
assert_eq!(0, res.messages.len()); | ||
|
||
// Ensure the proper event is emitted | ||
let event = &res.events[0]; | ||
assert_eq!(event.ty, "nusd_valuator/add_denom"); | ||
assert_eq!(event.attributes.len(), 2); | ||
|
||
// Query the registered denoms | ||
let query_res = | ||
query(deps.as_ref(), env.clone(), QueryMsg::AcceptedDenoms {})?; | ||
let denoms: Vec<String> = serde_json::from_slice(&query_res)?; | ||
assert_eq!(denoms, vec![denom.clone()]); | ||
Ok(()) | ||
} | ||
|
||
/// Attempting to add a denom that already exists should error. | ||
#[test] | ||
fn add_denom_err_existent() -> TestResult { | ||
let accepted_denoms_init: Vec<String> = | ||
[TEST_DENOM].iter().map(|s| s.to_string()).collect(); | ||
let (mut deps, env, info) = | ||
testing::setup_contract(accepted_denoms_init)?; | ||
let denom = TEST_DENOM.to_string(); | ||
let msg = ExecuteMsg::AddDenom { | ||
denom: denom.clone(), | ||
}; | ||
let err = execute(deps.as_mut(), env.clone(), info.clone(), msg.clone()) | ||
.expect_err("Expected error, but execute did not fail"); | ||
assert_eq!( | ||
err, | ||
ContractError::AddExistentDenom { | ||
denom: denom.clone(), | ||
denom_set: vec![denom.clone()].into_iter().collect() | ||
} | ||
); | ||
|
||
// Ensure the state remains unchanged | ||
let query_res = | ||
query(deps.as_ref(), env.clone(), QueryMsg::AcceptedDenoms {})?; | ||
let denoms: Vec<String> = serde_json::from_slice(&query_res)?; | ||
assert_eq!(denoms, vec![denom.clone()]); | ||
Ok(()) | ||
} | ||
|
||
#[test] | ||
fn remove_denom() -> TestResult { | ||
let accepted_denoms_init = vec![TEST_DENOM] | ||
.into_iter() | ||
.map(|s| s.to_string()) | ||
.collect(); | ||
let (mut deps, env, info) = | ||
testing::setup_contract(accepted_denoms_init)?; | ||
|
||
// Test removing a denomination | ||
let denom = TEST_DENOM.to_string(); | ||
let msg = ExecuteMsg::RemoveDenom { | ||
denom: denom.clone(), | ||
}; | ||
let res: Response = | ||
execute(deps.as_mut(), env.clone(), info.clone(), msg.clone())?; | ||
assert_eq!(0, res.messages.len()); | ||
|
||
// Ensure the proper event is emitted | ||
let event = &res.events[0]; | ||
assert_eq!(event.ty, "nusd_valuator/remove_denom"); | ||
assert_eq!(event.attributes.len(), 2); | ||
|
||
// Query the registered denoms (should be empty now) | ||
let want_denom_set: Vec<String> = vec![]; | ||
let query_res = | ||
query(deps.as_ref(), env.clone(), QueryMsg::AcceptedDenoms {})?; | ||
let denoms: Vec<String> = serde_json::from_slice(&query_res)?; | ||
assert_eq!(denoms, want_denom_set); | ||
|
||
// Attempt to remove the same denom again (should fail) | ||
let err = execute(deps.as_mut(), env.clone(), info.clone(), msg.clone()) | ||
.unwrap_err(); | ||
assert_eq!( | ||
err, | ||
ContractError::RemoveNonexistentDenom { | ||
denom, | ||
denom_set: want_denom_set.clone().into_iter().collect(), | ||
} | ||
); | ||
|
||
// Ensure the state remains unchanged | ||
let query_res = | ||
query(deps.as_ref(), env.clone(), QueryMsg::AcceptedDenoms {})?; | ||
let denoms: Vec<String> = serde_json::from_slice(&query_res)?; | ||
assert_eq!(denoms, want_denom_set); | ||
Ok(()) | ||
} | ||
|
||
/// test: Removin a denom that isn't in the set should error. | ||
#[test] | ||
fn remove_denom_err_nonexistent() -> TestResult { | ||
let accepted_denoms_init: Vec<String> = vec![TEST_DENOM] | ||
.into_iter() | ||
.map(|s| s.to_string()) | ||
.collect(); | ||
let (mut deps, env, info) = | ||
testing::setup_contract(accepted_denoms_init.clone())?; | ||
|
||
// Attempt to remove a denom that's not in the set (should fail) | ||
let want_denom_set = accepted_denoms_init.clone(); | ||
let denom = String::from("denom_not_in_set"); | ||
let msg = ExecuteMsg::RemoveDenom { | ||
denom: denom.clone(), | ||
}; | ||
let err = execute(deps.as_mut(), env.clone(), info.clone(), msg.clone()) | ||
.unwrap_err(); | ||
assert_eq!( | ||
err, | ||
ContractError::RemoveNonexistentDenom { | ||
denom, | ||
denom_set: want_denom_set.clone().into_iter().collect(), | ||
} | ||
); | ||
|
||
// Ensure the state remains unchanged | ||
let query_res = | ||
query(deps.as_ref(), env.clone(), QueryMsg::AcceptedDenoms {})?; | ||
let denoms: Vec<String> = serde_json::from_slice(&query_res)?; | ||
assert_eq!(denoms, want_denom_set); | ||
Ok(()) | ||
} | ||
|
||
// TODO: test change denom | ||
#[test] | ||
fn change_denom() -> TestResult { | ||
Ok(()) | ||
} | ||
|
||
// TODO: test update ownership | ||
#[test] | ||
fn update_ownership() -> TestResult { | ||
Ok(()) | ||
} | ||
} |
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,71 @@ | ||
use cosmwasm_std::{ | ||
to_json_binary, Binary, Coin, Deps, Env, StdResult, Uint128, | ||
}; | ||
use std::collections::BTreeSet; | ||
|
||
use crate::msgs::QueryMsg; | ||
use crate::state::ACCEPTED_DENOMS; | ||
|
||
#[cfg_attr(not(feature = "library"), cosmwasm_std::entry_point)] | ||
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> { | ||
match msg { | ||
QueryMsg::Mintable { from_coins } => { | ||
to_json_binary(&query_mintable(deps, from_coins)?) | ||
} | ||
QueryMsg::Redeemable { | ||
redeem_amount, | ||
to_denom, | ||
} => to_json_binary(&query_redeemable( | ||
deps, | ||
redeem_amount, | ||
to_denom.as_str(), | ||
)?), | ||
QueryMsg::AcceptedDenoms {} => { | ||
to_json_binary(&query_accepted_denoms(deps)?) | ||
} | ||
QueryMsg::RedeemableChoices { redeem_amount } => { | ||
to_json_binary(&query_redeemable_choices(deps, redeem_amount)?) | ||
} | ||
QueryMsg::Ownership {} => { | ||
to_json_binary(&cw_ownable::get_ownership(deps.storage)?) | ||
} | ||
} | ||
} | ||
|
||
pub fn query_accepted_denoms(deps: Deps) -> StdResult<BTreeSet<String>> { | ||
ACCEPTED_DENOMS.load(deps.storage) | ||
} | ||
|
||
// TODO: query_mintable | ||
pub fn query_mintable( | ||
_deps: Deps, | ||
_from_coins: BTreeSet<String>, | ||
) -> StdResult<Uint128> { | ||
todo!() | ||
} | ||
|
||
// TODO: query_redeemable | ||
pub fn query_redeemable( | ||
_deps: Deps, | ||
_redeem_amount: Uint128, | ||
_to_denom: &str, | ||
) -> StdResult<Uint128> { | ||
todo!() | ||
} | ||
|
||
pub fn query_redeemable_choices( | ||
deps: Deps, | ||
redeem_amount: Uint128, | ||
) -> StdResult<Vec<Coin>> { | ||
let accepted_denoms = query_accepted_denoms(deps)?; | ||
let choices: StdResult<Vec<Coin>> = accepted_denoms | ||
.iter() | ||
.map(|denom| { | ||
Ok(Coin { | ||
denom: denom.clone(), | ||
amount: query_redeemable(deps, redeem_amount, denom)?, | ||
}) | ||
}) | ||
.collect(); | ||
choices | ||
} |
Oops, something went wrong.