base_factory/
contract.rs

1#[cfg(not(feature = "library"))]
2use cosmwasm_std::entry_point;
3use cosmwasm_std::{
4    ensure, ensure_eq, to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, StdError,
5    StdResult, WasmMsg,
6};
7use cw2::set_contract_version;
8use cw_utils::must_pay;
9use semver::Version;
10use sg1::{checked_fair_burn, transfer_funds_to_launchpad_dao};
11use sg2::msg::UpdateMinterParamsMsg;
12use sg2::query::{AllowedCollectionCodeIdResponse, AllowedCollectionCodeIdsResponse, Sg2QueryMsg};
13use sg2::MinterParams;
14use sg_std::{Response, NATIVE_DENOM};
15
16use crate::error::ContractError;
17use crate::msg::{
18    BaseMinterCreateMsg, BaseSudoMsg, BaseUpdateParamsMsg, ExecuteMsg, InstantiateMsg,
19    ParamsResponse, SudoMsg,
20};
21use crate::state::SUDO_PARAMS;
22
23// version info for migration info
24const CONTRACT_NAME: &str = "crates.io:sg-base-factory";
25const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
26
27/// Can only be called by governance
28#[cfg_attr(not(feature = "library"), entry_point)]
29pub fn instantiate(
30    deps: DepsMut,
31    _env: Env,
32    _info: MessageInfo,
33    msg: InstantiateMsg,
34) -> Result<Response, ContractError> {
35    set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
36
37    SUDO_PARAMS.save(deps.storage, &msg.params)?;
38
39    Ok(Response::new())
40}
41
42#[cfg_attr(not(feature = "library"), entry_point)]
43pub fn execute(
44    deps: DepsMut,
45    env: Env,
46    info: MessageInfo,
47    msg: ExecuteMsg,
48) -> Result<Response, ContractError> {
49    match msg {
50        ExecuteMsg::CreateMinter(msg) => execute_create_minter(deps, env, info, msg),
51    }
52}
53
54pub fn execute_create_minter(
55    deps: DepsMut,
56    _env: Env,
57    info: MessageInfo,
58    msg: BaseMinterCreateMsg,
59) -> Result<Response, ContractError> {
60    let params = SUDO_PARAMS.load(deps.storage)?;
61    must_pay(&info, &params.creation_fee.denom)?;
62    must_be_allowed_collection(deps.as_ref(), msg.collection_params.code_id)?;
63
64    must_not_be_frozen(&params)?;
65
66    let mut res = Response::new();
67    if params.creation_fee.denom == NATIVE_DENOM {
68        checked_fair_burn(&info, params.creation_fee.amount.u128(), None, &mut res)?;
69    } else {
70        transfer_funds_to_launchpad_dao(
71            &info,
72            params.creation_fee.amount.u128(),
73            &params.creation_fee.denom,
74            &mut res,
75        )?;
76    }
77
78    let msg = WasmMsg::Instantiate {
79        admin: Some(info.sender.to_string()),
80        code_id: params.code_id,
81        msg: to_json_binary(&msg)?,
82        funds: vec![],
83        label: format!(
84            "Minter-{}-{}",
85            params.code_id,
86            msg.collection_params.name.trim()
87        ),
88    };
89
90    Ok(res
91        .add_attribute("action", "create_minter")
92        .add_message(msg))
93}
94
95pub fn must_not_be_frozen<T>(params: &MinterParams<T>) -> Result<(), ContractError> {
96    ensure!(!params.frozen, ContractError::Frozen {});
97    Ok(())
98}
99
100pub fn must_pay_exact_amount<T>(
101    params: &MinterParams<T>,
102    info: &MessageInfo,
103    accepted_denom: &str,
104) -> Result<(), ContractError> {
105    must_pay(info, accepted_denom)?;
106    // `must_pay` checks if the denom is ok and if there is only 1 denom sent so the below is safe
107    ensure!(
108        info.funds[0].amount == params.creation_fee.amount,
109        ContractError::InvalidCreationFeeAmount {}
110    );
111    Ok(())
112}
113
114pub fn must_be_allowed_collection(deps: Deps, code_id: u64) -> Result<(), ContractError> {
115    let res = query_allowed_collection_code_id(deps, code_id)?;
116    if !res.allowed {
117        return Err(ContractError::InvalidCollectionCodeId { code_id });
118    }
119    Ok(())
120}
121
122#[cfg_attr(not(feature = "library"), entry_point)]
123pub fn sudo(deps: DepsMut, env: Env, msg: BaseSudoMsg) -> Result<Response, ContractError> {
124    match msg {
125        SudoMsg::UpdateParams(params_msg) => sudo_update_params(deps, env, *params_msg),
126    }
127}
128
129/// Only governance can update contract params
130pub fn sudo_update_params(
131    deps: DepsMut,
132    _env: Env,
133    param_msg: BaseUpdateParamsMsg,
134) -> Result<Response, ContractError> {
135    let mut params = SUDO_PARAMS.load(deps.storage)?;
136
137    update_params(&mut params, param_msg)?;
138
139    SUDO_PARAMS.save(deps.storage, &params)?;
140
141    Ok(Response::new().add_attribute("action", "sudo_update_params"))
142}
143
144/// Base update params that can be used by other minter factories
145pub fn update_params<T, C>(
146    params: &mut MinterParams<C>,
147    param_msg: UpdateMinterParamsMsg<T>,
148) -> Result<(), ContractError> {
149    params.code_id = param_msg.code_id.unwrap_or(params.code_id);
150
151    if let Some(frozen) = param_msg.frozen {
152        params.frozen = frozen;
153    }
154
155    if let Some(creation_fee) = param_msg.creation_fee {
156        params.creation_fee = creation_fee;
157    }
158
159    if let Some(min_mint_price) = param_msg.min_mint_price {
160        ensure_eq!(
161            &min_mint_price.denom,
162            &NATIVE_DENOM,
163            ContractError::InvalidDenom {}
164        );
165        params.min_mint_price = min_mint_price;
166    }
167
168    // add new code ids, then rm code ids
169    if let Some(add_sg721_code_ids) = param_msg.add_sg721_code_ids {
170        for code_id in add_sg721_code_ids {
171            params.allowed_sg721_code_ids.push(code_id);
172        }
173    }
174    params.allowed_sg721_code_ids.dedup();
175    if let Some(rm_sg721_code_ids) = param_msg.rm_sg721_code_ids {
176        for code_id in rm_sg721_code_ids {
177            params.allowed_sg721_code_ids.retain(|&x| x != code_id);
178        }
179    }
180
181    params.mint_fee_bps = param_msg.mint_fee_bps.unwrap_or(params.mint_fee_bps);
182
183    params.max_trading_offset_secs = param_msg
184        .max_trading_offset_secs
185        .unwrap_or(params.max_trading_offset_secs);
186
187    Ok(())
188}
189
190#[cfg_attr(not(feature = "library"), entry_point)]
191pub fn query(deps: Deps, _env: Env, msg: Sg2QueryMsg) -> StdResult<Binary> {
192    match msg {
193        Sg2QueryMsg::Params {} => to_json_binary(&query_params(deps)?),
194        Sg2QueryMsg::AllowedCollectionCodeIds {} => {
195            to_json_binary(&query_allowed_collection_code_ids(deps)?)
196        }
197        Sg2QueryMsg::AllowedCollectionCodeId(code_id) => {
198            to_json_binary(&query_allowed_collection_code_id(deps, code_id)?)
199        }
200    }
201}
202
203fn query_params(deps: Deps) -> StdResult<ParamsResponse> {
204    let params = SUDO_PARAMS.load(deps.storage)?;
205    Ok(ParamsResponse { params })
206}
207
208fn query_allowed_collection_code_ids(deps: Deps) -> StdResult<AllowedCollectionCodeIdsResponse> {
209    let params = SUDO_PARAMS.load(deps.storage)?;
210    let code_ids = params.allowed_sg721_code_ids;
211    Ok(AllowedCollectionCodeIdsResponse { code_ids })
212}
213
214fn query_allowed_collection_code_id(
215    deps: Deps,
216    code_id: u64,
217) -> StdResult<AllowedCollectionCodeIdResponse> {
218    let params = SUDO_PARAMS.load(deps.storage)?;
219    let code_ids = params.allowed_sg721_code_ids;
220    let allowed = code_ids.contains(&code_id);
221    Ok(AllowedCollectionCodeIdResponse { allowed })
222}
223
224#[cfg_attr(not(feature = "library"), entry_point)]
225pub fn migrate(
226    deps: DepsMut,
227    _env: Env,
228    msg: Option<BaseUpdateParamsMsg>,
229) -> Result<Response, ContractError> {
230    let prev_contract_info = cw2::get_contract_version(deps.storage)?;
231    let prev_contract_name: String = prev_contract_info.contract;
232    let prev_contract_version: Version = prev_contract_info
233        .version
234        .parse()
235        .map_err(|_| StdError::generic_err("Unable to retrieve previous contract version"))?;
236
237    let new_version: Version = CONTRACT_VERSION
238        .parse()
239        .map_err(|_| StdError::generic_err("Invalid contract version"))?;
240
241    if prev_contract_name != CONTRACT_NAME {
242        return Err(StdError::generic_err("Cannot migrate to a different contract").into());
243    }
244
245    if prev_contract_version > new_version {
246        return Err(StdError::generic_err("Cannot migrate to a previous contract version").into());
247    }
248
249    if let Some(msg) = msg {
250        let mut params = SUDO_PARAMS.load(deps.storage)?;
251
252        update_params(&mut params, msg)?;
253
254        SUDO_PARAMS.save(deps.storage, &params)?;
255    }
256
257    Ok(Response::new().add_attribute("action", "migrate"))
258}