cw_admin_factory/
contract.rs

1#[cfg(not(feature = "library"))]
2use cosmwasm_std::entry_point;
3use cosmwasm_std::{
4    to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Reply, Response, StdResult, SubMsg,
5    WasmMsg,
6};
7
8use cw2::set_contract_version;
9use cw_utils::parse_reply_instantiate_data;
10
11use crate::error::ContractError;
12use crate::msg::{AdminResponse, ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};
13use crate::state::{ADMIN, EXPECT};
14
15pub(crate) const CONTRACT_NAME: &str = "crates.io:cw-admin-factory";
16pub(crate) const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
17
18pub const INSTANTIATE_CONTRACT_REPLY_ID: u64 = 0;
19pub const INSTANTIATE2_CONTRACT_REPLY_ID: u64 = 2;
20
21#[cfg_attr(not(feature = "library"), entry_point)]
22pub fn instantiate(
23    deps: DepsMut,
24    _env: Env,
25    info: MessageInfo,
26    msg: InstantiateMsg,
27) -> Result<Response, ContractError> {
28    set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
29
30    let admin = msg.admin.map(|a| deps.api.addr_validate(&a)).transpose()?;
31    ADMIN.save(deps.storage, &admin)?;
32
33    Ok(Response::new()
34        .add_attribute("method", "instantiate")
35        .add_attribute("creator", info.sender))
36}
37
38#[cfg_attr(not(feature = "library"), entry_point)]
39pub fn execute(
40    deps: DepsMut,
41    env: Env,
42    info: MessageInfo,
43    msg: ExecuteMsg,
44) -> Result<Response, ContractError> {
45    match msg {
46        ExecuteMsg::InstantiateContractWithSelfAdmin {
47            instantiate_msg: msg,
48            code_id,
49            label,
50        } => instantiate_contract(deps, env, info, msg, code_id, label),
51        ExecuteMsg::Instantiate2ContractWithSelfAdmin {
52            instantiate_msg: msg,
53            code_id,
54            label,
55            salt,
56            expect,
57        } => instantiate2_contract(deps, env, info, msg, code_id, label, salt, expect),
58    }
59}
60
61pub fn instantiate_contract(
62    deps: DepsMut,
63    env: Env,
64    info: MessageInfo,
65    instantiate_msg: Binary,
66    code_id: u64,
67    label: String,
68) -> Result<Response, ContractError> {
69    // If admin set, require the sender to be the admin.
70    if let Some(admin) = ADMIN.load(deps.storage)? {
71        if admin != info.sender {
72            return Err(ContractError::Unauthorized {});
73        }
74    }
75
76    // Instantiate the specified contract with factory as the admin.
77    let instantiate = WasmMsg::Instantiate {
78        admin: Some(env.contract.address.to_string()),
79        code_id,
80        msg: instantiate_msg,
81        funds: info.funds,
82        label,
83    };
84
85    let msg = SubMsg::reply_on_success(instantiate, INSTANTIATE_CONTRACT_REPLY_ID);
86    Ok(Response::default()
87        .add_attribute("action", "instantiate_contract_with_self_admin")
88        .add_submessage(msg))
89}
90
91#[allow(clippy::too_many_arguments)]
92pub fn instantiate2_contract(
93    deps: DepsMut,
94    env: Env,
95    info: MessageInfo,
96    instantiate_msg: Binary,
97    code_id: u64,
98    label: String,
99    salt: Binary,
100    expect: Option<String>,
101) -> Result<Response, ContractError> {
102    // If admin set, require the sender to be the admin.
103    if let Some(admin) = ADMIN.load(deps.storage)? {
104        if admin != info.sender {
105            return Err(ContractError::Unauthorized {});
106        }
107    }
108
109    if let Some(expect) = expect {
110        let expect = deps.api.addr_validate(&expect)?;
111        EXPECT.save(deps.storage, &expect)?;
112    }
113
114    // Instantiate the specified contract with factory as the admin.
115    let instantiate = WasmMsg::Instantiate2 {
116        admin: Some(env.contract.address.to_string()),
117        code_id,
118        msg: instantiate_msg,
119        funds: info.funds,
120        label,
121        salt,
122    };
123
124    let msg = SubMsg::reply_on_success(instantiate, INSTANTIATE2_CONTRACT_REPLY_ID);
125    Ok(Response::default()
126        .add_attribute("action", "instantiate2_contract_with_self_admin")
127        .add_submessage(msg))
128}
129
130#[cfg_attr(not(feature = "library"), entry_point)]
131pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
132    match msg {
133        QueryMsg::Admin {} => Ok(to_json_binary(&AdminResponse {
134            admin: ADMIN.load(deps.storage)?,
135        })?),
136    }
137}
138
139#[cfg_attr(not(feature = "library"), entry_point)]
140pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result<Response, ContractError> {
141    let msg_id = msg.id;
142    match msg_id {
143        INSTANTIATE_CONTRACT_REPLY_ID | INSTANTIATE2_CONTRACT_REPLY_ID => {
144            let res = parse_reply_instantiate_data(msg)?;
145            let contract_addr = deps.api.addr_validate(&res.contract_address)?;
146
147            if msg_id == INSTANTIATE2_CONTRACT_REPLY_ID {
148                // If saved an expected address, verify it matches and clear it.
149                let expect = EXPECT.may_load(deps.storage)?;
150                if let Some(expect) = expect {
151                    EXPECT.remove(deps.storage);
152                    if contract_addr != expect {
153                        return Err(ContractError::UnexpectedContractAddress {
154                            expected: expect.to_string(),
155                            actual: contract_addr.to_string(),
156                        });
157                    }
158                }
159            }
160
161            // Make the contract its own admin.
162            let msg = WasmMsg::UpdateAdmin {
163                contract_addr: contract_addr.to_string(),
164                admin: contract_addr.to_string(),
165            };
166
167            Ok(Response::default()
168                .add_attribute("set contract admin as itself", contract_addr)
169                .add_message(msg))
170        }
171        _ => Err(ContractError::UnknownReplyID {}),
172    }
173}
174
175#[cfg_attr(not(feature = "library"), entry_point)]
176pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
177    // Set contract to version to latest
178    set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
179    Ok(Response::default())
180}