Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CollectionData with minimal props, name and symbol, all others are optional. #73

Merged
merged 2 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion contracts/sg-ics721/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ impl Ics721Execute for SgIcs721Contract {
name,
symbol,
num_tokens,
collection_info,
collection_info: Some(collection_info),
}))
}

Expand Down
6 changes: 3 additions & 3 deletions contracts/sg-ics721/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ use sg721_base::msg::CollectionInfoResponse;
pub struct SgCollectionData {
// CW721 specific props, copied from ics721::state::CollectionData
pub owner: Option<String>,
pub contract_info: ContractInfoResponse,
pub contract_info: Option<ContractInfoResponse>,
pub name: String,
pub symbol: String,
pub num_tokens: u64,
pub num_tokens: Option<u64>,
/// SG721 specific collection info
pub collection_info: CollectionInfoResponse,
pub collection_info: Option<CollectionInfoResponse>,
}

#[derive(Default)]
Expand Down
210 changes: 200 additions & 10 deletions contracts/sg-ics721/src/testing/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,9 +251,20 @@ impl MockApiBech32 {

#[cw_serde]
pub struct CustomClassData {
pub foo: Option<String>,
// even there is collection name, but it doesn't apply to CollectionData type
pub name: String,
// additional custom prop
pub foo: Option<String>,
}

#[cw_serde]
pub struct PartialCustomCollectionData {
pub owner: Option<String>,
pub name: String,
pub symbol: String,
// additional custom prop
pub foo: Option<String>,
pub bar: String,
}

struct Test {
Expand Down Expand Up @@ -562,7 +573,7 @@ fn test_do_instantiate_and_mint_weird_data() {
contract_info: Default::default(),
name: "name".to_string(),
symbol: "symbol".to_string(),
num_tokens: 1,
num_tokens: Some(1),
})
.unwrap(),
),
Expand Down Expand Up @@ -774,7 +785,7 @@ fn test_do_instantiate_and_mint() {
contract_info: Default::default(),
name: "ark".to_string(),
symbol: "protocol".to_string(),
num_tokens: 1,
num_tokens: Some(1),
})
.unwrap(),
),
Expand Down Expand Up @@ -926,7 +937,8 @@ fn test_do_instantiate_and_mint() {
.unwrap();
assert_eq!(base_owner, owner);
}
// test case: instantiate cw721 with CustomClassData (without owner, name, and symbol)
// test case: instantiate cw721 with CustomClassData (includes name, but without owner and symbol)
// results in nft contract using class id for name and symbol
{
let mut test = Test::new(false, None, sg721_base_contract());
let collection_contract_source_chain =
Expand Down Expand Up @@ -1086,6 +1098,184 @@ fn test_do_instantiate_and_mint() {

assert_eq!(owner.owner, nft_contract.to_string());

// check cw721 owner query matches ics721 owner query
let base_owner: cw721::OwnerOfResponse = test
.app
.wrap()
.query_wasm_smart(
nft_contract,
&cw721::Cw721QueryMsg::OwnerOf {
token_id: "1".to_string(),
include_expired: None,
},
)
.unwrap();
assert_eq!(base_owner, owner);
}
// test case: instantiate cw721 with PartialCustomCollectionData (includes name and symbol)
// results in nft contract using name and symbol
{
let mut test = Test::new(false, None, sg721_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(),
test.ics721.clone(),
&ExecuteMsg::Callback(CallbackMsg::CreateVouchers {
receiver: test.app.api().addr_make(NFT_OWNER_TARGET_CHAIN).to_string(),
create: VoucherCreation {
class: Class {
id: ClassId::new(class_id.clone()),
uri: Some("https://moonphase.is".to_string()),
data: Some(
// CustomClassData doesn't apply to CollectionData type and won't be considered
// collection name wont be transferred to instantiated nft contract
to_json_binary(&PartialCustomCollectionData {
owner: None,
name: "collection-name".to_string(),
symbol: "collection-symbol".to_string(),
bar: "bar".to_string(),
foo: Some(
test.app
.api()
.addr_make(COLLECTION_OWNER_TARGET_CHAIN)
.to_string(),
),
})
.unwrap(),
),
},
tokens: vec![
Token {
id: TokenId::new("1"),
uri: Some("https://moonphase.is/image.svg".to_string()),
data: None,
},
Token {
id: TokenId::new("2"),
uri: Some("https://foo.bar".to_string()),
data: None,
},
],
},
}),
&[],
)
.unwrap();
// 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, 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: class_id.to_string(),
},
)
.unwrap();

// check name and symbol contains class id for instantiated nft contract
let contract_info: cw721::ContractInfoResponse = test
.app
.wrap()
.query_wasm_smart(
nft_contract.clone(),
&Cw721QueryMsg::<Empty>::ContractInfo {},
)
.unwrap();
assert_eq!(
contract_info,
cw721::ContractInfoResponse {
name: "collection-name".to_string(),
symbol: "collection-symbol".to_string()
}
);

// check collection info is properly set
let collection_info: CollectionInfoResponse = test
.app
.wrap()
.query_wasm_smart(nft_contract.clone(), &Sg721QueryMsg::CollectionInfo {})
.unwrap();

assert_eq!(
collection_info,
CollectionInfoResponse {
// creator of ics721 contract is creator of nft contract, since no owner in ClassData provided
creator: test.app.api().addr_make(ICS721_CREATOR).to_string(),
description: "".to_string(),
image: "https://arkprotocol.io".to_string(),
external_link: None,
explicit_content: None,
start_trading_time: None,
royalty_info: None,
}
);

// Check that token_uri was set properly.
let token_info: cw721::NftInfoResponse<Empty> = test
.app
.wrap()
.query_wasm_smart(
nft_contract.clone(),
&cw721::Cw721QueryMsg::NftInfo {
token_id: "1".to_string(),
},
)
.unwrap();
assert_eq!(
token_info.token_uri,
Some("https://moonphase.is/image.svg".to_string())
);
let token_info: cw721::NftInfoResponse<Empty> = test
.app
.wrap()
.query_wasm_smart(
nft_contract.clone(),
&cw721::Cw721QueryMsg::NftInfo {
token_id: "2".to_string(),
},
)
.unwrap();
assert_eq!(token_info.token_uri, Some("https://foo.bar".to_string()));

// After transfer to target, test owner can do any action, like transfer, on collection
test.app
.execute_contract(
test.app.api().addr_make(NFT_OWNER_TARGET_CHAIN),
nft_contract.clone(),
&cw721_base::msg::ExecuteMsg::<Empty, Empty>::TransferNft {
recipient: nft_contract.to_string(), // new owner
token_id: "1".to_string(),
},
&[],
)
.unwrap();

// ics721 owner query and check nft contract owns it
let owner: cw721::OwnerOfResponse = test
.app
.wrap()
.query_wasm_smart(
test.ics721,
&QueryMsg::Owner {
token_id: "1".to_string(),
class_id: class_id.to_string(),
},
)
.unwrap();

assert_eq!(owner.owner, nft_contract.to_string());

// check cw721 owner query matches ics721 owner query
let base_owner: cw721::OwnerOfResponse = test
.app
Expand Down Expand Up @@ -1445,7 +1635,7 @@ fn test_do_instantiate_and_mint_no_instantiate() {
contract_info: Default::default(),
name: "name".to_string(),
symbol: "symbol".to_string(),
num_tokens: 1,
num_tokens: Some(1),
})
.unwrap(),
),
Expand Down Expand Up @@ -1586,7 +1776,7 @@ fn test_do_instantiate_and_mint_permissions() {
contract_info: Default::default(),
name: "name".to_string(),
symbol: "symbol".to_string(),
num_tokens: 1,
num_tokens: Some(1),
})
.unwrap(),
),
Expand Down Expand Up @@ -1784,19 +1974,19 @@ fn test_receive_nft() {
// collection data from source chain
test.source_cw721_owner.to_string(),
),
contract_info: expected_contract_info,
contract_info: Some(expected_contract_info),
name: "name".to_string(),
symbol: "symbol".to_string(),
num_tokens: 1,
collection_info: CollectionInfoResponse {
num_tokens: Some(1),
collection_info: Some(CollectionInfoResponse {
creator: test.ics721.to_string(),
description: "".to_string(),
image: "https://arkprotocol.io".to_string(),
external_link: None,
explicit_content: None,
start_trading_time: None,
royalty_info: None,
},
}),
})
.unwrap();
assert_eq!(
Expand Down
18 changes: 12 additions & 6 deletions packages/ics721/src/state.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use cosmwasm_schema::cw_serde;
use cosmwasm_schema::schemars::JsonSchema;
use cosmwasm_std::{Addr, Binary, ContractInfoResponse, Empty};
use cw_pause_once::PauseOrchestrator;
use cw_storage_plus::{Item, Map};
use serde::Deserialize;
use serde::{Deserialize, Serialize};

use crate::token_types::{Class, ClassId, TokenId};

Expand Down Expand Up @@ -50,15 +50,21 @@ pub struct UniversalNftInfoResponse {
extension: Empty,
}

/// Collection data provided by the (source) cw721 contract. This is pass as optional class data during interchain transfer to target chain.
/// Collection data send by ICS721 on source chain. It is an optional class data for interchain transfer to target chain.
/// ICS721 on target chain is free to use this data or not. Lik in case of `sg721-base` it uses owner for defining creator in collection info.
#[cw_serde]
/// `ics721-base` uses name and symbol for instantiating new cw721 contract.
// NB: Please not cw_serde includes `deny_unknown_fields`: https://github.com/CosmWasm/cosmwasm/blob/v1.5.0/packages/schema-derive/src/cw_serde.rs
// For incoming data, parsing needs to be more lenient/less strict, so we use `serde` directly.
#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug, PartialEq)]
#[allow(clippy::derive_partial_eq_without_eq)]
#[schemars(crate = "cosmwasm_schema::schemars")]
#[serde(crate = "cosmwasm_schema::serde")]
pub struct CollectionData {
pub owner: Option<String>,
pub contract_info: ContractInfoResponse,
pub contract_info: Option<ContractInfoResponse>,
pub name: String,
pub symbol: String,
pub num_tokens: u64,
pub num_tokens: Option<u64>,
}

#[derive(Deserialize)]
Expand Down
12 changes: 6 additions & 6 deletions packages/ics721/src/testing/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,10 +232,10 @@ fn test_receive_nft() {
class_data: Some(
to_json_binary(&CollectionData {
owner: Some(OWNER.to_string()),
contract_info: expected_contract_info.clone(),
contract_info: Some(expected_contract_info.clone()),
name: "name".to_string(),
symbol: "symbol".to_string(),
num_tokens: 1,
num_tokens: Some(1),
})
.unwrap()
),
Expand Down Expand Up @@ -313,10 +313,10 @@ fn test_receive_nft() {
class_data: Some(
to_json_binary(&CollectionData {
owner: Some(OWNER.to_string()),
contract_info: expected_contract_info,
contract_info: Some(expected_contract_info),
name: "name".to_string(),
symbol: "symbol".to_string(),
num_tokens: 1,
num_tokens: Some(1),
})
.unwrap()
),
Expand Down Expand Up @@ -468,10 +468,10 @@ fn test_receive_sets_uri() {
Some(
to_json_binary(&CollectionData {
owner: Some(OWNER.to_string()),
contract_info: expected_contract_info,
contract_info: Some(expected_contract_info),
name: "name".to_string(),
symbol: "symbol".to_string(),
num_tokens: 1,
num_tokens: Some(1),
})
.unwrap()
),
Expand Down
Loading