Skip to content

Commit

Permalink
Bug fixes, implement fees
Browse files Browse the repository at this point in the history
  • Loading branch information
JakeHartnell authored and Jake Hartnell committed Mar 26, 2024
1 parent c882469 commit 4080c01
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 32 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ integer-cbrt = "0.1.2"
once_cell = "1.18"
osmosis-std = "0.20.1"
osmosis-std-derive = "0.20.1"
osmosis-test-tube = "20.1.1"
osmosis-test-tube = "20.1.2"
proc-macro2 = "1.0"
prost = { version = "0.12.3", features = ["prost-derive"] }
prost-types = { version = "0.12.3", default-features = false }
Expand Down
68 changes: 49 additions & 19 deletions contracts/external/cw-abc/src/commands.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use cosmwasm_std::{
ensure, to_json_binary, Addr, BankMsg, Coin, CosmosMsg, Decimal as StdDecimal, DepsMut, Env,
MessageInfo, QuerierWrapper, Response, StdError, StdResult, Storage, SubMsg, Uint128, WasmMsg,
MessageInfo, QuerierWrapper, Response, StdError, StdResult, Storage, Uint128, WasmMsg,
};
use cw_tokenfactory_issuer::msg::ExecuteMsg as IssuerExecuteMsg;
use cw_utils::must_pay;
use rust_decimal::Decimal;
use std::collections::HashSet;
use std::ops::Deref;
use token_bindings::{TokenFactoryMsg, TokenFactoryQuery};
Expand All @@ -12,8 +13,8 @@ use crate::abc::{CommonsPhase, CurveType};
use crate::contract::CwAbcResult;
use crate::msg::UpdatePhaseConfigMsg;
use crate::state::{
CURVE_STATE, CURVE_TYPE, DONATIONS, HATCHERS, HATCHER_ALLOWLIST, MAX_SUPPLY, PHASE,
PHASE_CONFIG, SUPPLY_DENOM, TOKEN_ISSUER_CONTRACT,
CURVE_STATE, CURVE_TYPE, DONATIONS, FEES_RECIPIENT, HATCHERS, HATCHER_ALLOWLIST, MAX_SUPPLY,
PHASE, PHASE_CONFIG, SUPPLY_DENOM, TOKEN_ISSUER_CONTRACT,
};
use crate::ContractError;

Expand Down Expand Up @@ -95,17 +96,29 @@ pub fn execute_buy(deps: DepsMut<TokenFactoryQuery>, _env: Env, info: MessageInf

// Mint tokens for sender by calling mint on the cw-tokenfactory-issuer contract
let issuer_addr = TOKEN_ISSUER_CONTRACT.load(deps.storage)?;
let mint_msg = WasmMsg::Execute {
let mut msgs: Vec<CosmosMsg<TokenFactoryMsg>> = vec![CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: issuer_addr.to_string(),
msg: to_json_binary(&IssuerExecuteMsg::Mint {
to_address: info.sender.to_string(),
amount: minted,
})?,
funds: vec![],
})];

// Send funding to fee recipient
if funded > Uint128::zero() {
let fees_recipient = FEES_RECIPIENT.load(deps.storage)?;
msgs.push(CosmosMsg::Bank(BankMsg::Send {
to_address: fees_recipient.to_string(),
amount: vec![Coin {
amount: funded,
denom: curve_state.reserve_denom,
}],
}))
};

Ok(Response::new()
.add_message(mint_msg)
.add_messages(msgs)
.add_attribute("action", "buy")
.add_attribute("from", info.sender)
.add_attribute("reserved", reserved)
Expand Down Expand Up @@ -164,7 +177,7 @@ pub fn execute_sell(deps: DepsMut<TokenFactoryQuery>, _env: Env, info: MessageIn
CosmosMsg::<TokenFactoryMsg>::Wasm(WasmMsg::Execute {
contract_addr: issuer_addr.to_string(),
msg: to_json_binary(&IssuerExecuteMsg::Burn {
from_address: info.sender.to_string(),
from_address: issuer_addr.to_string(),
amount: burn_amount,
})?,
funds: vec![],
Expand All @@ -184,28 +197,44 @@ pub fn execute_sell(deps: DepsMut<TokenFactoryQuery>, _env: Env, info: MessageIn

// Calculate the new reserve based on the new supply
let new_reserve = curve.reserve(curve_state.supply);
curve_state.reserve = new_reserve;
curve_state.funding += taxed_amount;
CURVE_STATE.save(deps.storage, &curve_state)?;

// Calculate how many reserve tokens to release based on the sell amount
let released_reserve = curve_state
.reserve
.checked_sub(new_reserve)
.map_err(StdError::overflow)?;

// Now send the tokens to the sender
let msg_send = SubMsg::new(CosmosMsg::Bank(BankMsg::Send {
to_address: info.sender.to_string(),
amount: vec![Coin {
amount: released_reserve,
denom: curve_state.reserve_denom,
}],
}));
// Update the curve state
curve_state.reserve = new_reserve;
curve_state.funding += taxed_amount;
CURVE_STATE.save(deps.storage, &curve_state)?;

// Now send the tokens to the sender and any fees to the DAO
let mut send_msgs: Vec<CosmosMsg<TokenFactoryMsg>> =
vec![CosmosMsg::<TokenFactoryMsg>::Bank(BankMsg::Send {
to_address: info.sender.to_string(),
amount: vec![Coin {
// TODO Subtract the taxed amount from the released reserve
amount: released_reserve,
denom: curve_state.reserve_denom.clone(),
}],
})];

// Send exit fees to the to the fee recipient
if taxed_amount > Uint128::zero() {
let fees_recipient = FEES_RECIPIENT.load(deps.storage)?;
send_msgs.push(CosmosMsg::Bank(BankMsg::Send {
to_address: fees_recipient.to_string(),
amount: vec![Coin {
amount: taxed_amount,
denom: curve_state.reserve_denom,
}],
}))
}

Ok(Response::<TokenFactoryMsg>::new()
.add_messages(burn_msgs)
.add_submessage(msg_send)
.add_messages(send_msgs)
.add_attribute("action", "burn")
.add_attribute("from", info.sender)
.add_attribute("amount", burn_amount)
Expand All @@ -223,9 +252,10 @@ fn calculate_exit_tax(storage: &dyn Storage, sell_amount: Uint128) -> CwAbcResul
let exit_tax = match &phase {
CommonsPhase::Hatch => phase_config.hatch.exit_tax,
CommonsPhase::Open => phase_config.open.exit_tax,
CommonsPhase::Closed => return Err(ContractError::CommonsClosed {}),
CommonsPhase::Closed => return Ok(Uint128::zero()),
};

// TODO more normal check?
debug_assert!(
exit_tax <= StdDecimal::percent(100),
"Exit tax must be <= 100%"
Expand Down
16 changes: 9 additions & 7 deletions contracts/external/cw-abc/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ use token_bindings::{TokenFactoryMsg, TokenFactoryQuery};
use crate::abc::{CommonsPhase, CurveFn};
use crate::curves::DecimalPlaces;
use crate::error::ContractError;
use crate::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, UpdatePhaseConfigMsg};
use crate::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};
use crate::state::{
CurveState, CURVE_STATE, CURVE_TYPE, HATCHER_ALLOWLIST, MAX_SUPPLY, PHASE, PHASE_CONFIG,
SUPPLY_DENOM, TOKEN_INSTANTIATION_INFO, TOKEN_ISSUER_CONTRACT,
CurveState, CURVE_STATE, CURVE_TYPE, FEES_RECIPIENT, HATCHER_ALLOWLIST, MAX_SUPPLY, PHASE,
PHASE_CONFIG, SUPPLY_DENOM, TOKEN_INSTANTIATION_INFO, TOKEN_ISSUER_CONTRACT,
};
use crate::{commands, queries};

Expand All @@ -28,21 +28,19 @@ const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");

const INSTANTIATE_TOKEN_FACTORY_ISSUER_REPLY_ID: u64 = 0;

// By default, the prefix for token factory tokens is "factory"
const DENOM_PREFIX: &str = "factory";

pub type CwAbcResult<T = Response<TokenFactoryMsg>> = Result<T, ContractError>;

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn instantiate(
deps: DepsMut<TokenFactoryQuery>,
env: Env,
_env: Env,
info: MessageInfo,
msg: InstantiateMsg,
) -> CwAbcResult {
set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;

let InstantiateMsg {
fees_recipient,
supply,
reserve,
curve_type,
Expand All @@ -59,6 +57,9 @@ pub fn instantiate(

phase_config.validate()?;

// Validate and store the fees recipient
FEES_RECIPIENT.save(deps.storage, &deps.api.addr_validate(&fees_recipient)?)?;

// Save new token info for use in reply
TOKEN_INSTANTIATION_INFO.save(deps.storage, &supply)?;

Expand Down Expand Up @@ -197,6 +198,7 @@ pub fn reply(
TOKEN_INSTANTIATION_INFO.remove(deps.storage);

// Format the denom and save it
// By default, the prefix for token factory tokens is "factory"
let denom = format!("factory/{}/{}", &issuer_addr, token_info.subdenom);

SUPPLY_DENOM.save(deps.storage, &denom)?;
Expand Down
3 changes: 3 additions & 0 deletions contracts/external/cw-abc/src/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ use crate::abc::{CommonsPhase, CommonsPhaseConfig, CurveType, MinMax, ReserveTok

#[cw_serde]
pub struct InstantiateMsg {
/// The recipient for any fees collected from bonding curve operation
pub fees_recipient: String,

/// The code id of the cw-tokenfactory-issuer contract
pub token_issuer_code_id: u64,

Expand Down
3 changes: 3 additions & 0 deletions contracts/external/cw-abc/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ pub const CURVE_STATE: Item<CurveState> = Item::new("curve_state");

pub const CURVE_TYPE: Item<CurveType> = Item::new("curve_type");

/// The recipient for fees generated from bonding curve operation
pub const FEES_RECIPIENT: Item<Addr> = Item::new("fees_recipient");

/// The denom used for the supply token
pub const SUPPLY_DENOM: Item<String> = Item::new("denom");

Expand Down
9 changes: 5 additions & 4 deletions contracts/external/cw-abc/src/test_tube/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ fn test_happy_path() {
contract_balance.balance,
Some(Coin {
denom: RESERVE.to_string(),
amount: "1000".to_string(),
amount: "900".to_string(), // Minus 10% to fees_recipient
})
);

Expand Down Expand Up @@ -152,19 +152,19 @@ fn test_happy_path() {
user_balance.balance,
Some(Coin {
denom: denom.clone(),
amount: "8800".to_string(),
amount: "8900".to_string(),
})
);
assert_eq!(
contract_balance.balance,
Some(Coin {
denom: RESERVE.to_string(),
amount: "990".to_string(),
amount: "880".to_string(),
})
);

// Buy enough tokens to end the hatch phase
abc.execute(&ExecuteMsg::Buy {}, &coins(1000000, RESERVE), &accounts[0])
abc.execute(&ExecuteMsg::Buy {}, &coins(999999, RESERVE), &accounts[1])
.unwrap();

// Contract is now in open phase
Expand Down Expand Up @@ -280,6 +280,7 @@ fn test_allowlist() {
let app = OsmosisTestApp::new();
let builder = TestEnvBuilder::new();
let instantiate_msg = InstantiateMsg {
fees_recipient: "replaced to accounts[0]".to_string(),
token_issuer_code_id: 0,
supply: SupplyToken {
subdenom: DENOM.to_string(),
Expand Down
4 changes: 3 additions & 1 deletion contracts/external/cw-abc/src/test_tube/test_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ impl TestEnvBuilder {
let abc = CwAbc::deploy(
app,
&InstantiateMsg {
fees_recipient: accounts[0].address(),
token_issuer_code_id: issuer_id,
supply: SupplyToken {
subdenom: DENOM.to_string(),
Expand Down Expand Up @@ -168,8 +169,9 @@ impl TestEnvBuilder {

let issuer_id = TokenfactoryIssuer::upload(app, &accounts[0])?;

// Override issuer_id
// Override issuer_id and fees_recipient
msg.token_issuer_code_id = issuer_id;
msg.fees_recipient = accounts[0].address();

let abc = CwAbc::deploy(app, &msg, &accounts[0])?;

Expand Down
1 change: 1 addition & 0 deletions contracts/external/cw-abc/src/testing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ pub fn default_instantiate_msg(
curve_type: CurveType,
) -> InstantiateMsg {
InstantiateMsg {
fees_recipient: TEST_CREATOR.to_string(),
token_issuer_code_id: 1,
supply: SupplyToken {
subdenom: TEST_SUPPLY_DENOM.to_string(),
Expand Down

0 comments on commit 4080c01

Please sign in to comment.