Skip to content

Commit

Permalink
Add FactoryCallbacks, build out test-contract
Browse files Browse the repository at this point in the history
  • Loading branch information
Jake Hartnell committed Sep 13, 2023
1 parent 9e904d9 commit 7b5e097
Show file tree
Hide file tree
Showing 10 changed files with 607 additions and 25 deletions.
431 changes: 424 additions & 7 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ syn = { version = "1.0", features = ["derive"] }
test-context = "0.1"
thiserror = { version = "1.0" }
token-bindings = "0.11.0"
vending-factory = "2.4.0"
vending-minter = "2.4.0"
wynd-utils = "0.4"

# One commit ahead of version 0.3.0. Allows initialization with an
Expand Down
1 change: 1 addition & 0 deletions contracts/voting/dao-voting-token-staked/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ cw-multi-test = { git = "https://github.com/CosmWasm/cw-multi-test.git", rev = "
cw-tokenfactory-issuer = { workspace = true }
dao-proposal-single = { workspace = true }
dao-proposal-hook-counter = { workspace = true }
dao-test-custom-factory = { workspace = true }
dao-testing = { workspace = true, features = ["test-tube"] }
osmosis-std = { workpsace = true }
osmosis-test-tube = { workspace = true }
Expand Down
27 changes: 20 additions & 7 deletions contracts/voting/dao-voting-token-staked/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use dao_voting::{

use crate::error::ContractError;
use crate::msg::{
DenomResponse, ExecuteMsg, GetHooksResponse, InitialBalance, InstantiateMsg,
DenomResponse, ExecuteMsg, FactoryCallback, GetHooksResponse, InitialBalance, InstantiateMsg,
ListStakersResponse, MigrateMsg, NewTokenInfo, QueryMsg, StakerBalanceResponse, TokenInfo,
};
use crate::state::{
Expand Down Expand Up @@ -716,15 +716,28 @@ pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result<Response, ContractEr
}
}
FACTORY_EXECUTE_REPLY_ID => {
// Parse reply
let res = parse_reply_execute_data(msg)?;

// TODO validate active threshold is set. Some contracts such as a minter,
// contract may not have any supply until tokens are minted.

match res.data {
Some(data) => {
// TODO parse data and save token contract address / denom
unimplemented!()
// Parse info from the callback, this will fail
// if incorrectly formatted.
let info: FactoryCallback = from_binary(&data)?;

// Save Denom
DENOM.save(deps.storage, &info.denom)?;

// Save token issuer contract if one is returned
if let Some(token_contract) = info.token_contract {
TOKEN_ISSUER_CONTRACT
.save(deps.storage, &deps.api.addr_validate(&token_contract)?)?;
}

// TODO validate active threshold is set? Some contracts such as a minter,
// contract may not have any supply until tokens are minted.

// TODO also include token contract
Ok(Response::new().add_attribute("denom", info.denom))
}
// TODO better error
None => return Err(ContractError::Unauthorized {}),
Expand Down
6 changes: 6 additions & 0 deletions contracts/voting/dao-voting-token-staked/src/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,9 @@ pub struct DenomResponse {
pub struct GetHooksResponse {
pub hooks: Vec<String>,
}

#[cw_serde]
pub struct FactoryCallback {
pub denom: String,
pub token_contract: Option<String>,
}
10 changes: 9 additions & 1 deletion test-contracts/dao-test-custom-factory/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,19 @@ library = []
cosmwasm-std = { workspace = true }
cosmwasm-schema = { workspace = true }
cosmwasm-storage = { workspace = true }
cw-storage-plus = { workspace = true }
cw2 = { workspace = true }
cw-ownable = { workspace = true }
cw-storage-plus = { workspace = true }
cw-utils = { workspace = true }
thiserror = { workspace = true }
dao-dao-macros = { workspace = true }
dao-interface = { workspace = true }
dao-voting-cw721-staked = { workspace = true, features = ["library"] }
dao-voting-token-staked = { workspace = true, features = ["library"] }
vending-factory = { workspace = true, features = ["library"] }
vending-minter = { workspace = true, features = ["library"] }
cw-tokenfactory-issuer = { workspace = true, features = ["library"] }
sg721-base = { workspace = true, features = ["library"] }

[dev-dependencies]
cw-multi-test = { workspace = true }
148 changes: 138 additions & 10 deletions test-contracts/dao-test-custom-factory/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
#[cfg(not(feature = "library"))]
use cosmwasm_std::entry_point;
use cosmwasm_std::{
to_binary, Binary, CosmosMsg, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdResult,
WasmMsg,
to_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdResult, SubMsg,
Uint128, WasmMsg,
};
use cw2::set_contract_version;
use cw_storage_plus::Item;
use cw_tokenfactory_issuer::msg::{
ExecuteMsg as IssuerExecuteMsg, InstantiateMsg as IssuerInstantiateMsg,
};
use cw_utils::parse_reply_instantiate_data;
use dao_voting_token_staked::msg::{
FactoryCallback, InitialBalance, NewTokenInfo, QueryMsg as TokenVotingQueryMsg,
};

use crate::{
error::ContractError,
msg::{ExecuteMsg, InstantiateMsg, QueryMsg},
};

const CONTRACT_NAME: &str = "CARGO_PKG_NAME";
const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME");
const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");

const INSTANTIATE_BASE_MINTER_REPLY_ID: u64 = 1;
const INSTANTIATE_ISSUER_REPLY_ID: u64 = 2;

const DAO: Item<Addr> = Item::new("dao");
const TOKEN_INFO: Item<NewTokenInfo> = Item::new("token_info");

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn instantiate(
Expand All @@ -39,23 +51,54 @@ pub fn execute(
ExecuteMsg::StargazeBaseMinterFactory(msg) => {
execute_stargaze_base_minter_factory(deps, env, info, msg)
}
ExecuteMsg::TokenFactoryFactory(token) => {
execute_token_factory_factory(deps, env, info, token)
}
}
}

/// An example factory that instantiates a cw_tokenfactory_issuer contract
/// A more realistic example would be something like a DeFi Pool or Augmented
/// bonding curve.
pub fn execute_token_factory_factory(
deps: DepsMut,
env: Env,
_env: Env,
info: MessageInfo,
msg: WasmMsg,
token: NewTokenInfo,
) -> Result<Response, ContractError> {
unimplemented!()
// Query for DAO
let dao: Addr = deps
.querier
.query_wasm_smart(info.sender, &TokenVotingQueryMsg::Dao {})?;

// Save DAO and TOKEN_INFO for use in replies
DAO.save(deps.storage, &dao)?;
TOKEN_INFO.save(deps.storage, &token)?;

// Instantiate new contract, further setup is handled in the
// SubMsg reply.
let msg = SubMsg::reply_on_success(
WasmMsg::Instantiate {
admin: Some(dao.to_string()),
code_id: token.token_issuer_code_id,
msg: to_binary(&IssuerInstantiateMsg::NewToken {
subdenom: token.subdenom,
})?,
funds: vec![],
label: "cw_tokenfactory_issuer".to_string(),
},
INSTANTIATE_ISSUER_REPLY_ID,
);

Ok(Response::new().add_submessage(msg))
}

/// Example Stargaze factory.
pub fn execute_stargaze_base_minter_factory(
deps: DepsMut,
env: Env,
info: MessageInfo,
msg: WasmMsg,
_deps: DepsMut,
_env: Env,
_info: MessageInfo,
_msg: WasmMsg,
) -> Result<Response, ContractError> {
// TODO query voting contract (the sender) for the DAO address
// TODO replace the Stargaze info to set the DAO address
Expand Down Expand Up @@ -86,6 +129,91 @@ pub fn reply(deps: DepsMut, env: Env, msg: Reply) -> Result<Response, ContractEr
INSTANTIATE_BASE_MINTER_REPLY_ID => {
unimplemented!()
}
INSTANTIATE_ISSUER_REPLY_ID => {
// Load DAO address and TOKEN_INFO
let dao = DAO.load(deps.storage)?;
let token = TOKEN_INFO.load(deps.storage)?;

// Parse issuer address from instantiate reply
let issuer_addr = parse_reply_instantiate_data(msg)?.contract_address;

// Format the denom
let denom = format!("factory/{}/{}", &issuer_addr, token.subdenom);

let initial_supply = token
.initial_balances
.iter()
.fold(Uint128::zero(), |previous, new_balance| {
previous + new_balance.amount
});
let total_supply = initial_supply + token.initial_dao_balance.unwrap_or_default();

// TODO query active threshold and validate the count

// Msgs to be executed to finalize setup
let mut msgs: Vec<WasmMsg> = vec![];

// Grant an allowance to mint the initial supply
msgs.push(WasmMsg::Execute {
contract_addr: issuer_addr.clone(),
msg: to_binary(&IssuerExecuteMsg::SetMinterAllowance {
address: env.contract.address.to_string(),
allowance: total_supply,
})?,
funds: vec![],
});

// Call issuer contract to mint tokens for initial balances
token
.initial_balances
.iter()
.for_each(|b: &InitialBalance| {
msgs.push(WasmMsg::Execute {
contract_addr: issuer_addr.clone(),
msg: to_binary(&IssuerExecuteMsg::Mint {
to_address: b.address.clone(),
amount: b.amount,
})
.unwrap_or_default(),
funds: vec![],
});
});

// Add initial DAO balance to initial_balances if nonzero.
if let Some(initial_dao_balance) = token.initial_dao_balance {
if !initial_dao_balance.is_zero() {
msgs.push(WasmMsg::Execute {
contract_addr: issuer_addr.clone(),
msg: to_binary(&IssuerExecuteMsg::Mint {
to_address: dao.to_string(),
amount: initial_dao_balance,
})?,
funds: vec![],
});
}
}

// Begin update issuer contract owner to be the DAO, this is a
// two-step ownership transfer.
// DAO must pass a prop to Accept Ownership
msgs.push(WasmMsg::Execute {
contract_addr: issuer_addr.clone(),
msg: to_binary(&IssuerExecuteMsg::UpdateOwnership(
cw_ownable::Action::TransferOwnership {
new_owner: dao.to_string(),
expiry: None,
},
))?,
funds: vec![],
});

Ok(Response::new()
.add_messages(msgs)
.set_data(to_binary(&FactoryCallback {
denom,
token_contract: Some(issuer_addr.to_string()),
})?))
}
_ => Err(ContractError::UnknownReplyId { id: msg.id }),
}
}
4 changes: 4 additions & 0 deletions test-contracts/dao-test-custom-factory/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
use cosmwasm_std::StdError;
use cw_utils::ParseReplyError;
use thiserror::Error;

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

#[error(transparent)]
ParseReplyError(#[from] ParseReplyError),

#[error("Unauthorized")]
Unauthorized {},

Expand Down
1 change: 1 addition & 0 deletions test-contracts/dao-test-custom-factory/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ mod error;
pub mod msg;

pub use crate::error::ContractError;
pub use dao_voting_token_staked::msg::{InitialBalance, NewTokenInfo};
2 changes: 2 additions & 0 deletions test-contracts/dao-test-custom-factory/src/msg.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use cosmwasm_schema::{cw_serde, QueryResponses};
use cosmwasm_std::WasmMsg;
use dao_voting_token_staked::msg::NewTokenInfo;

#[cw_serde]
pub struct InstantiateMsg {}

#[cw_serde]
pub enum ExecuteMsg {
StargazeBaseMinterFactory(WasmMsg),
TokenFactoryFactory(NewTokenInfo),
}

#[cw_serde]
Expand Down

0 comments on commit 7b5e097

Please sign in to comment.