Skip to content

Commit

Permalink
fix class id, using sha267 guarantees salt is always of lenght 32
Browse files Browse the repository at this point in the history
  • Loading branch information
taitruong committed Nov 14, 2023
1 parent 0a95563 commit 4010f53
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 11 deletions.
2 changes: 1 addition & 1 deletion packages/ics721/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ cw-paginate-storage = { workspace = true }
cw721-proxy-derive = { workspace = true }
cw-pause-once = { workspace = true }
cw-cii = { workspace = true }
sha2 = { workspace = true }
zip-optional = { workspace = true }

[dev-dependencies]
Expand All @@ -29,4 +30,3 @@ cw-multi-test = { workspace = true }
cw2 = { workspace = true }
cw721-016 = { workspace = true }
cw721-rate-limited-proxy = { workspace = true }
sha2 = { workspace = true }
5 changes: 4 additions & 1 deletion packages/ics721/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use cosmwasm_std::StdError;
use cosmwasm_std::{StdError, Instantiate2AddressError};
use cw_pause_once::PauseError;
use cw_utils::ParseReplyError;
use thiserror::Error;
Expand All @@ -11,6 +11,9 @@ pub enum ContractError {
#[error(transparent)]
Pause(#[from] PauseError),

#[error(transparent)]
Instantiate2Error(#[from] Instantiate2AddressError),

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

Expand Down
11 changes: 8 additions & 3 deletions packages/ics721/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use cosmwasm_std::{
Empty, Env, IbcMsg, MessageInfo, Response, StdError, StdResult, SubMsg, WasmMsg,
};
use serde::{de::DeserializeOwned, Serialize};
use sha2::{Digest, Sha256};

use crate::{
ibc::{NonFungibleTokenPacketData, INSTANTIATE_CW721_REPLY_ID, INSTANTIATE_PROXY_REPLY_ID},
Expand Down Expand Up @@ -260,15 +261,19 @@ where
vec![]
} else {
let class_id = ClassId::new(class.id.clone());
let salt = class_id.as_bytes();
// for creating a predictable nft contract using, using instantiate2, we need: checksum, creator, and salt:
// - using class id as salt for instantiating nft contract guarantees a) predictable address and b) uniqueness
// for this salt must be of length 32 bytes, so we use sha256 to hash class id
let mut hasher = Sha256::new();
hasher.update(class_id.as_bytes());
let salt = hasher.finalize().to_vec();
// Get the canonical address of the contract creator
let canonical_creator = deps.api.addr_canonicalize(env.contract.address.as_str())?;
// get the checksum of the contract we're going to instantiate
let CodeInfoResponse { checksum, .. } = deps
.querier
.query_wasm_code_info(CW721_CODE_ID.load(deps.storage)?)?;
let canonical_cw721_addr = instantiate2_address(&checksum, &canonical_creator, salt)
.map_err(|_| StdError::generic_err("Could not calculate addr"))?;
let canonical_cw721_addr = instantiate2_address(&checksum, &canonical_creator, &salt)?;
let cw721_addr = deps.api.addr_humanize(&canonical_cw721_addr)?;

// Save classId <-> contract mappings.
Expand Down
16 changes: 10 additions & 6 deletions packages/ics721/src/testing/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
// owner, aka "minter"
const COLLECTION_OWNER_TARGET_CHAIN: &str = "collection-minter-target-chain";
const COLLECTION_OWNER_SOURCE_CHAIN: &str = "collection-minter-source-chain";
const COLLECTION_CONTRACT_SOURCE_CHAIN: &str = "collection-contract-source-chain";
const CHANNEL_TARGET_CHAIN: &str = "channel-1";
const BECH32_PREFIX_HRP: &str = "stars";
const NFT_OWNER_TARGET_CHAIN: &str = "nft-owner-target-chain";
const ICS721_ADMIN_AND_PAUSER: &str = "ics721-pauser";
Expand Down Expand Up @@ -561,6 +563,8 @@ fn test_do_instantiate_and_mint() {
// test case: instantiate cw721 with no ClassData (without owner, name, and symbol)
{
let mut test = Test::new(false, None, cw721_base_contract());
let collection_contract_source_chain = ClassId::new(test.app.api().addr_make(COLLECTION_CONTRACT_SOURCE_CHAIN));
let class_id = format!("wasm.{}/{}/{}", test.ics721, CHANNEL_TARGET_CHAIN, collection_contract_source_chain);
test.app
.execute_contract(
test.ics721.clone(),
Expand All @@ -569,7 +573,7 @@ fn test_do_instantiate_and_mint() {
receiver: test.app.api().addr_make(NFT_OWNER_TARGET_CHAIN).to_string(),
create: VoucherCreation {
class: Class {
id: ClassId::new("some/class/id"),
id: ClassId::new(class_id.clone()),
uri: Some("https://moonphase.is".to_string()),
data: None, // no class data
},
Expand All @@ -593,15 +597,15 @@ fn test_do_instantiate_and_mint() {
// Check entry added in CLASS_ID_TO_NFT_CONTRACT
let nft_contracts = test.query_nft_contracts();
assert_eq!(nft_contracts.len(), 1);
assert_eq!(nft_contracts[0].0, "some/class/id");
assert_eq!(nft_contracts[0].0, class_id.to_string());
// Get the address of the instantiated NFT.
let nft_contract: Addr = test
.app
.wrap()
.query_wasm_smart(
test.ics721.clone(),
&QueryMsg::NftContract {
class_id: "some/class/id".to_string(),
class_id: class_id.to_string(),
},
)
.unwrap();
Expand All @@ -618,8 +622,8 @@ fn test_do_instantiate_and_mint() {
assert_eq!(
contract_info,
cw721::ContractInfoResponse {
name: "some/class/id".to_string(), // name is set to class_id
symbol: "some/class/id".to_string() // symbol is set to class_id
name: class_id.to_string(), // name is set to class_id
symbol: class_id.to_string() // symbol is set to class_id
}
);

Expand Down Expand Up @@ -671,7 +675,7 @@ fn test_do_instantiate_and_mint() {
test.ics721,
&QueryMsg::Owner {
token_id: "1".to_string(),
class_id: "some/class/id".to_string(),
class_id: class_id.to_string(),
},
)
.unwrap();
Expand Down

0 comments on commit 4010f53

Please sign in to comment.