Skip to content

Commit

Permalink
Support instantiate2 in cw-admin-factory (DA0-DA0#851)
Browse files Browse the repository at this point in the history
  • Loading branch information
NoahSaso committed Jul 18, 2024
1 parent 8c37b9e commit ebc7914
Show file tree
Hide file tree
Showing 21 changed files with 2,111 additions and 10 deletions.
9 changes: 9 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ overflow-checks = true
[workspace.dependencies]
anyhow = { version = "1.0" }
assert_matches = "1.5"
bech32 = "0.9.1"
cosm-orc = { version = "4.0" }
cosm-tome = "0.2"
cosmos-sdk-proto = "0.19"
Expand Down
19 changes: 16 additions & 3 deletions contracts/external/cw-admin-factory/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name ="cw-admin-factory"
name = "cw-admin-factory"
authors = ["Jake Hartnell", "blue-note", "ekez <ekez@withoutdoing.com>"]
description = "A CosmWasm factory contract for instantiating a contract as its own admin."
edition = { workspace = true }
Expand All @@ -15,18 +15,31 @@ crate-type = ["cdylib", "rlib"]
backtraces = ["cosmwasm-std/backtraces"]
# use library feature to disable all instantiate/execute/query exports
library = []
# use test tube feature to enable test-tube integration tests, for example
# cargo test --features "test-tube"
test-tube = []
# when writing tests you may wish to enable test-tube as a default feature
# default = ["test-tube"]

[dependencies]
cosmwasm-std = { workspace = true }
cosmwasm-std = { workspace = true, features = ["cosmwasm_1_2"] }
cosmwasm-schema = { workspace = true }
cw-storage-plus = { workspace = true }
cw2 = { workspace = true }
thiserror = { workspace = true }
cw-utils = { workspace = true }

[dev-dependencies]
bech32 = { workspace = true }
cosmwasm-schema = { workspace = true }
cw-admin-factory = { workspace = true }
cw-multi-test = { workspace = true }
cw20-base = { workspace = true, features = ["library"] }
cw4 = { workspace = true }
dao-dao-core = { workspace = true, features = ["library"] }
dao-interface = { workspace = true }
cw20-base = { workspace = true, features = ["library"] }
dao-proposal-single = { workspace = true }
dao-testing = { workspace = true }
dao-voting = { workspace = true }
dao-voting-cw4 = { workspace = true }
osmosis-test-tube = { workspace = true }
45 changes: 44 additions & 1 deletion contracts/external/cw-admin-factory/schema/cw-admin-factory.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"title": "ExecuteMsg",
"oneOf": [
{
"description": "Instantiates the target contract with the provided instantiate message and code id and updates the contract's admin to be itself.",
"description": "Instantiates the target contract with the provided instantiate message, code ID, and label and updates the contract's admin to be itself.",
"type": "object",
"required": [
"instantiate_contract_with_self_admin"
Expand Down Expand Up @@ -52,6 +52,49 @@
}
},
"additionalProperties": false
},
{
"description": "Instantiates the target contract with the provided instantiate message, code ID, label, and salt, via instantiate2 to give a predictable address, and updates the contract's admin to be itself.",
"type": "object",
"required": [
"instantiate2_contract_with_self_admin"
],
"properties": {
"instantiate2_contract_with_self_admin": {
"type": "object",
"required": [
"code_id",
"instantiate_msg",
"label",
"salt"
],
"properties": {
"code_id": {
"type": "integer",
"format": "uint64",
"minimum": 0.0
},
"expect": {
"description": "Optionally specify the expected address and fail if it doesn't match the instantiated contract. This makes it easy for a consumer to validate that they are using the correct address elsewhere.",
"type": [
"string",
"null"
]
},
"instantiate_msg": {
"$ref": "#/definitions/Binary"
},
"label": {
"type": "string"
},
"salt": {
"$ref": "#/definitions/Binary"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
],
"definitions": {
Expand Down
72 changes: 68 additions & 4 deletions contracts/external/cw-admin-factory/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ use cw_utils::parse_reply_instantiate_data;

use crate::error::ContractError;
use crate::msg::{AdminResponse, ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};
use crate::state::ADMIN;
use crate::state::{ADMIN, EXPECT};

pub(crate) const CONTRACT_NAME: &str = "crates.io:cw-admin-factory";
pub(crate) const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");

pub const INSTANTIATE_CONTRACT_REPLY_ID: u64 = 0;
pub const INSTANTIATE2_CONTRACT_REPLY_ID: u64 = 2;

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn instantiate(
Expand Down Expand Up @@ -46,6 +48,13 @@ pub fn execute(
code_id,
label,
} => instantiate_contract(deps, env, info, msg, code_id, label),
ExecuteMsg::Instantiate2ContractWithSelfAdmin {
instantiate_msg: msg,
code_id,
label,
salt,
expect,
} => instantiate2_contract(deps, env, info, msg, code_id, label, salt, expect),
}
}

Expand Down Expand Up @@ -75,7 +84,46 @@ pub fn instantiate_contract(

let msg = SubMsg::reply_on_success(instantiate, INSTANTIATE_CONTRACT_REPLY_ID);
Ok(Response::default()
.add_attribute("action", "instantiate_cw_core")
.add_attribute("action", "instantiate_contract_with_self_admin")
.add_submessage(msg))
}

#[allow(clippy::too_many_arguments)]
pub fn instantiate2_contract(
deps: DepsMut,
env: Env,
info: MessageInfo,
instantiate_msg: Binary,
code_id: u64,
label: String,
salt: Binary,
expect: Option<String>,
) -> Result<Response, ContractError> {
// If admin set, require the sender to be the admin.
if let Some(admin) = ADMIN.load(deps.storage)? {
if admin != info.sender {
return Err(ContractError::Unauthorized {});
}
}

if let Some(expect) = expect {
let expect = deps.api.addr_validate(&expect)?;
EXPECT.save(deps.storage, &expect)?;
}

// Instantiate the specified contract with factory as the admin.
let instantiate = WasmMsg::Instantiate2 {
admin: Some(env.contract.address.to_string()),
code_id,
msg: instantiate_msg,
funds: info.funds,
label,
salt,
};

let msg = SubMsg::reply_on_success(instantiate, INSTANTIATE2_CONTRACT_REPLY_ID);
Ok(Response::default()
.add_attribute("action", "instantiate2_contract_with_self_admin")
.add_submessage(msg))
}

Expand All @@ -90,10 +138,26 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result<Response, ContractError> {
match msg.id {
INSTANTIATE_CONTRACT_REPLY_ID => {
let msg_id = msg.id;
match msg_id {
INSTANTIATE_CONTRACT_REPLY_ID | INSTANTIATE2_CONTRACT_REPLY_ID => {
let res = parse_reply_instantiate_data(msg)?;
let contract_addr = deps.api.addr_validate(&res.contract_address)?;

if msg_id == INSTANTIATE2_CONTRACT_REPLY_ID {
// If saved an expected address, verify it matches and clear it.
let expect = EXPECT.may_load(deps.storage)?;
if let Some(expect) = expect {
EXPECT.remove(deps.storage);
if contract_addr != expect {
return Err(ContractError::UnexpectedContractAddress {
expected: expect.to_string(),
actual: contract_addr.to_string(),
});
}
}
}

// Make the contract its own admin.
let msg = WasmMsg::UpdateAdmin {
contract_addr: contract_addr.to_string(),
Expand Down
3 changes: 3 additions & 0 deletions contracts/external/cw-admin-factory/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,7 @@ pub enum ContractError {

#[error("An unknown reply ID was received.")]
UnknownReplyID {},

#[error("Expected contract address {expected} but instantiated {actual}.")]
UnexpectedContractAddress { expected: String, actual: String },
}
Loading

0 comments on commit ebc7914

Please sign in to comment.