Skip to content

Commit

Permalink
instantiate nft contract with collection data
Browse files Browse the repository at this point in the history
  • Loading branch information
taitruong committed Aug 12, 2024
1 parent d97600b commit 6b124ed
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 55 deletions.
26 changes: 23 additions & 3 deletions contracts/sg-ics721/src/execute.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use cosmwasm_std::{from_json, to_json_binary, Addr, Binary, Deps, DepsMut, Env, StdResult};
use cosmwasm_std::{
from_json, to_json_binary, Addr, Binary, ContractInfoResponse, Deps, DepsMut, Env, StdResult,
};
use cw721::{CollectionExtension, RoyaltyInfo};
use ics721::{execute::Ics721Execute, state::CollectionData, utils::get_collection_data};
use ics721_types::token_types::Class;

use sg721::RoyaltyInfoResponse;
use sg721_base::msg::{CollectionInfoResponse, QueryMsg};

use crate::state::{SgIcs721Contract, STARGAZE_ICON_PLACEHOLDER};
Expand Down Expand Up @@ -48,7 +51,7 @@ impl Ics721Execute for SgIcs721Contract {

fn init_msg(&self, deps: Deps, env: &Env, class: &Class) -> StdResult<Binary> {
// ics721 creator is used, in case no source owner in class data is provided (e.g. due to nft-transfer module).
let ics721_contract_info = deps
let ContractInfoResponse { creator, admin, .. } = deps
.querier
.query_wasm_contract_info(env.contract.address.to_string())?;
// use by default ClassId, in case there's no class data with name and symbol
Expand All @@ -60,7 +63,7 @@ impl Ics721Execute for SgIcs721Contract {
// source owner could be: 1. regular wallet, 2. contract, or 3. multisig
// bech32 calculation for 2. and 3. leads to unknown address
// therefore, we use ics721 creator as owner
creator: ics721_contract_info.creator,
creator: creator.clone(),
description: "".to_string(),
// use Stargaze icon as placeholder
image: STARGAZE_ICON_PLACEHOLDER.to_string(),
Expand All @@ -79,6 +82,23 @@ impl Ics721Execute for SgIcs721Contract {
if let Some(collection_data) = collection_data {
instantiate_msg.name = collection_data.name;
instantiate_msg.symbol = collection_data.symbol;
let admin_or_creator = admin.unwrap_or(creator.clone());
if let Some(collection_info_extension_msg) =
collection_data.extension.map(|ext| sg721::CollectionInfo {
creator: creator.clone(),
description: ext.description,
image: ext.image,
external_link: ext.external_link,
explicit_content: ext.explicit_content,
start_trading_time: ext.start_trading_time,
royalty_info: ext.royalty_info.map(|r| RoyaltyInfoResponse {
payment_address: admin_or_creator, // r.payment_address cant be used, since it is from another chain
share: r.share,
}),
})
{
instantiate_msg.collection_info = collection_info_extension_msg;
}
}

to_json_binary(&instantiate_msg)
Expand Down
51 changes: 36 additions & 15 deletions contracts/sg-ics721/src/testing/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ pub struct PartialCustomCollectionData {

struct Test {
app: MockApp,
admin_and_pauser: Option<String>,
// origin cw721 contract on source chain for interchain transfers to other target chains
source_cw721_owner: Addr,
source_cw721_id: u64,
Expand Down Expand Up @@ -353,7 +354,7 @@ impl Test {
incoming_proxy,
outgoing_proxy,
pauser: admin.clone(),
cw721_admin: admin,
cw721_admin: admin.clone(),
contract_addr_length: None,
},
&[],
Expand Down Expand Up @@ -393,6 +394,7 @@ impl Test {

Self {
app,
admin_and_pauser: admin,
source_cw721_owner,
source_cw721_id,
source_cw721,
Expand Down Expand Up @@ -833,7 +835,13 @@ fn test_do_instantiate_and_mint() {
}
// test case: instantiate cw721 with ClassData containing owner, name, and symbol
{
let mut test = Test::new(false, false, None, None, sg721_base_contract());
let mut test = Test::new(
false,
false,
None,
Some(COLLECTION_OWNER_SOURCE_CHAIN.to_string()), // admin is used for royalty payment address!
sg721_base_contract(),
);
let collection_contract_source_chain =
ClassId::new(test.app.api().addr_make(COLLECTION_CONTRACT_SOURCE_CHAIN));
let class_id = format!(
Expand Down Expand Up @@ -943,12 +951,15 @@ fn test_do_instantiate_and_mint() {
CollectionInfoResponse {
// creator based on owner from collection in soure chain
creator: test.app.api().addr_make(ICS721_CREATOR).to_string(),
description: "".to_string(),
image: STARGAZE_ICON_PLACEHOLDER.to_string(),
external_link: None,
explicit_content: None,
start_trading_time: None,
royalty_info: None,
description: "description".to_string(),
image: "https://ark.pass/image.png".to_string(),
external_link: Some("https://ark.pass".to_string()),
explicit_content: Some(false),
start_trading_time: Some(Timestamp::from_seconds(42)),
royalty_info: Some(RoyaltyInfoResponse {
payment_address: test.admin_and_pauser.unwrap(),
share: Decimal::one(),
}),
}
);

Expand Down Expand Up @@ -1862,7 +1873,13 @@ fn test_do_instantiate_and_mint_2_different_collections() {

#[test]
fn test_do_instantiate_and_mint_no_instantiate() {
let mut test = Test::new(false, false, None, None, sg721_base_contract());
let mut test = Test::new(
false,
false,
None,
Some(COLLECTION_OWNER_SOURCE_CHAIN.to_string()), // admin is used for royalty payment address!
sg721_base_contract(),
);
let collection_contract_source_chain =
ClassId::new(test.app.api().addr_make(COLLECTION_CONTRACT_SOURCE_CHAIN));
let class_id = format!(
Expand Down Expand Up @@ -1977,13 +1994,17 @@ fn test_do_instantiate_and_mint_no_instantiate() {
assert_eq!(
collection_info,
CollectionInfoResponse {
// creator based on owner from collection in soure chain
creator: test.app.api().addr_make(ICS721_CREATOR).to_string(),
description: "".to_string(),
image: STARGAZE_ICON_PLACEHOLDER.to_string(),
external_link: None,
explicit_content: None,
start_trading_time: None,
royalty_info: None,
description: "description".to_string(),
image: "https://ark.pass/image.png".to_string(),
external_link: Some("https://ark.pass".to_string()),
explicit_content: Some(false),
start_trading_time: Some(Timestamp::from_seconds(42)),
royalty_info: Some(RoyaltyInfoResponse {
payment_address: test.admin_and_pauser.unwrap(),
share: Decimal::one(),
}),
}
);

Expand Down
21 changes: 19 additions & 2 deletions packages/ics721/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use cosmwasm_std::{
from_json, to_json_binary, Addr, Binary, ContractInfoResponse, Deps, DepsMut, Empty, Env,
Event, IbcMsg, MessageInfo, Order, Response, StdResult, SubMsg, WasmMsg,
};
use cw721::msg::RoyaltyInfoResponse;
use cw_storage_plus::Map;
use ics721_types::{
ibc_types::{IbcOutgoingMsg, IbcOutgoingProxyMsg, NonFungibleTokenPacketData},
Expand Down Expand Up @@ -603,7 +604,7 @@ where
/// Default implementation using `cw721_base::msg::InstantiateMsg`
fn init_msg(&self, deps: Deps, env: &Env, class: &Class) -> StdResult<Binary> {
// use ics721 creator for withdraw address
let ContractInfoResponse { creator, .. } = deps
let ContractInfoResponse { creator, admin, .. } = deps
.querier
.query_wasm_contract_info(env.contract.address.to_string())?;

Expand All @@ -614,7 +615,7 @@ where
collection_info_extension: None, // TODO consider class data in NonFungibleTokenPacketData
creator: Some(creator.clone()), // TODO maybe better using cw721 admin?
minter: Some(env.contract.address.to_string()),
withdraw_address: Some(creator),
withdraw_address: Some(creator.clone()),
};

// use collection data for setting name and symbol
Expand All @@ -625,6 +626,22 @@ where
if let Some(collection_data) = collection_data {
instantiate_msg.name = collection_data.name;
instantiate_msg.symbol = collection_data.symbol;
let admin_or_creator = admin.unwrap_or(creator.clone());
let collection_info_extension_msg =
collection_data
.extension
.map(|ext| cw721::msg::CollectionExtensionMsg {
description: Some(ext.description),
image: Some(ext.image),
external_link: ext.external_link,
explicit_content: ext.explicit_content,
start_trading_time: ext.start_trading_time,
royalty_info: ext.royalty_info.map(|r| RoyaltyInfoResponse {
payment_address: admin_or_creator, // r.payment_address cant be used, since it is from another chain
share: r.share,
}),
});
instantiate_msg.collection_info_extension = collection_info_extension_msg;
}

to_json_binary(&instantiate_msg)
Expand Down
68 changes: 33 additions & 35 deletions packages/ics721/src/testing/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ pub struct PartialCustomCollectionData {

struct Test {
app: MockApp,
admin_and_pauser: Option<String>,
// origin cw721 contract on source chain for interchain transfers to other target chains
source_cw721_owner: Addr,
source_cw721_id: u64,
Expand Down Expand Up @@ -415,6 +416,7 @@ impl Test {

Self {
app,
admin_and_pauser: admin,
source_cw721_owner,
source_cw721_id,
source_cw721,
Expand Down Expand Up @@ -654,6 +656,7 @@ fn test_do_instantiate_and_mint_weird_data() {
"wasm.{}/{}/{}",
test.ics721, CHANNEL_TARGET_CHAIN, collection_contract_source_chain
);
let collection_owner_addr = test.app.api().addr_make(COLLECTION_OWNER_SOURCE_CHAIN);
test.app
.execute_contract(
test.ics721.clone(),
Expand All @@ -668,10 +671,7 @@ fn test_do_instantiate_and_mint_weird_data() {
to_json_binary(&CollectionData {
owner: Some(
// incoming collection data from source chain
test.app
.api()
.addr_make(COLLECTION_OWNER_SOURCE_CHAIN)
.to_string(),
collection_owner_addr.to_string(),
),
contract_info: Default::default(),
name: "name".to_string(),
Expand All @@ -682,10 +682,8 @@ fn test_do_instantiate_and_mint_weird_data() {
external_link: Some("https://ark.pass".to_string()),
image: "https://ark.pass/image.png".to_string(),
royalty_info: Some(RoyaltyInfo {
payment_address: Addr::unchecked(
"payment_address".to_string(),
),
share: Decimal::one(),
payment_address: collection_owner_addr,
share: Decimal::bps(1000),
}),
start_trading_time: Some(Timestamp::from_seconds(42)),
}),
Expand Down Expand Up @@ -852,13 +850,32 @@ fn test_do_instantiate_and_mint() {
}
// test case: instantiate cw721 with ClassData containing owner, name, and symbol
{
let mut test = Test::new(false, false, None, None, cw721_base_contract(), true);
let mut test = Test::new(
false,
false,
None,
Some(COLLECTION_OWNER_SOURCE_CHAIN.to_string()), // admin is used for royalty payment address!
cw721_base_contract(),
true,
);
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
);
let collection_owner_addr = Addr::unchecked(test.admin_and_pauser.clone().unwrap());
let collection_extension = Some(CollectionExtension {
description: "description".to_string(),
explicit_content: Some(false),
external_link: Some("https://ark.pass".to_string()),
image: "https://ark.pass/image.png".to_string(),
royalty_info: Some(RoyaltyInfo {
payment_address: collection_owner_addr.clone(),
share: Decimal::bps(1000),
}),
start_trading_time: Some(Timestamp::from_seconds(42)),
});
test.app
.execute_contract(
test.ics721.clone(),
Expand All @@ -873,27 +890,12 @@ fn test_do_instantiate_and_mint() {
to_json_binary(&CollectionData {
owner: Some(
// incoming collection data from source chain
test.app
.api()
.addr_make(COLLECTION_OWNER_SOURCE_CHAIN)
.to_string(),
collection_owner_addr.to_string(),
),
contract_info: Default::default(),
name: "ark".to_string(),
symbol: "protocol".to_string(),
extension: Some(CollectionExtension {
description: "description".to_string(),
explicit_content: Some(false),
external_link: Some("https://ark.pass".to_string()),
image: "https://ark.pass/image.png".to_string(),
royalty_info: Some(RoyaltyInfo {
payment_address: Addr::unchecked(
"payment_address".to_string(),
),
share: Decimal::one(),
}),
start_trading_time: Some(Timestamp::from_seconds(42)),
}),
extension: collection_extension.clone(),
num_tokens: Some(1),
})
.unwrap(),
Expand Down Expand Up @@ -946,7 +948,7 @@ fn test_do_instantiate_and_mint() {
CollectionInfoAndExtensionResponse {
name: "ark".to_string(),
symbol: "protocol".to_string(),
extension: None,
extension: collection_extension,
updated_at: contract_info.updated_at,
}
);
Expand Down Expand Up @@ -1625,6 +1627,7 @@ fn test_do_instantiate_and_mint_no_instantiate() {
// Check calling CreateVouchers twice with same class id
// on 2nd call it will not instantiate a new contract,
// instead it will just mint the token on existing contract
let collection_owner_addr = test.app.api().addr_make(COLLECTION_OWNER_SOURCE_CHAIN);
test.app
.execute_contract(
test.ics721.clone(),
Expand All @@ -1639,10 +1642,7 @@ fn test_do_instantiate_and_mint_no_instantiate() {
to_json_binary(&CollectionData {
owner: Some(
// incoming collection data from source chain
test.app
.api()
.addr_make(COLLECTION_OWNER_SOURCE_CHAIN)
.to_string(),
collection_owner_addr.to_string(),
),
contract_info: Default::default(),
name: "name".to_string(),
Expand All @@ -1653,10 +1653,8 @@ fn test_do_instantiate_and_mint_no_instantiate() {
external_link: Some("https://ark.pass".to_string()),
image: "https://ark.pass/image.png".to_string(),
royalty_info: Some(RoyaltyInfo {
payment_address: Addr::unchecked(
"payment_address".to_string(),
),
share: Decimal::one(),
payment_address: collection_owner_addr,
share: Decimal::bps(1000),
}),
start_trading_time: Some(Timestamp::from_seconds(42)),
}),
Expand Down

0 comments on commit 6b124ed

Please sign in to comment.