Skip to content

Commit

Permalink
mint with onchain metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
taitruong committed Aug 13, 2024
1 parent dcdc99c commit eb1af4d
Show file tree
Hide file tree
Showing 2 changed files with 179 additions and 13 deletions.
27 changes: 24 additions & 3 deletions packages/ics721/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ 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 cw721::{
msg::{NftExtensionMsg, RoyaltyInfoResponse},
NftExtension,
};
use cw_storage_plus::Map;
use ics721_types::{
ibc_types::{IbcOutgoingMsg, IbcOutgoingProxyMsg, NonFungibleTokenPacketData},
Expand Down Expand Up @@ -419,7 +422,7 @@ where
.flatten()
{
Some(metadata) => Some(metadata),
// incase there is none in the storage, we use the one from the cw721 contract
// incase there is none in the storage, this is the 'home' chain, so metadata is retrieved from the cw721 contract
None => info.extension.map(|ext| to_json_binary(&ext)).transpose()?,
};

Expand Down Expand Up @@ -724,11 +727,29 @@ where
&data,
)?;

// parse token data and check whether it is of type NftExtension
let extension: Option<NftExtensionMsg> = match data {
Some(data) => from_json::<NftExtension>(data)
.ok()
.map(|ext| NftExtensionMsg {
animation_url: ext.animation_url,
attributes: ext.attributes,
background_color: ext.background_color,
description: ext.description,
external_url: ext.external_url,
image: ext.image,
image_data: ext.image_data,
youtube_url: ext.youtube_url,
name: ext.name,
}),
None => None,
};

let msg = cw721_metadata_onchain::msg::ExecuteMsg::Mint {
token_id: id.into(),
token_uri: uri,
owner: receiver.to_string(),
extension: None, // TODO consider token data in NonFungibleTokenPacketData
extension,
};
Ok(WasmMsg::Execute {
contract_addr: nft_contract.to_string(),
Expand Down
165 changes: 155 additions & 10 deletions packages/ics721/src/testing/unit_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ use cosmwasm_std::{
from_json,
testing::{mock_dependencies, mock_env, mock_info, MockQuerier, MOCK_CONTRACT_ADDR},
to_json_binary, Addr, ContractResult, CosmosMsg, Decimal, DepsMut, Empty, IbcMsg, IbcTimeout,
Order, QuerierResult, Response, StdResult, SubMsg, Timestamp, WasmQuery,
Order, QuerierResult, Response, StdResult, SubMsg, Timestamp, WasmMsg, WasmQuery,
};
use cw721::{
msg::{
AllNftInfoResponse, CollectionInfoAndExtensionResponse, NftInfoResponse, NumTokensResponse,
AllNftInfoResponse, CollectionInfoAndExtensionResponse, NftExtensionMsg, NftInfoResponse,
NumTokensResponse,
},
CollectionExtension, DefaultOptionalCollectionExtension, DefaultOptionalNftExtension,
NftExtension, RoyaltyInfo,
Expand All @@ -27,15 +28,15 @@ use crate::{
Ics721Query,
},
state::{
CollectionData, CLASS_ID_TO_CLASS, CONTRACT_ADDR_LENGTH, CW721_ADMIN, CW721_CODE_ID,
IBC_RECEIVE_TOKEN_METADATA, INCOMING_PROXY, OUTGOING_CLASS_TOKEN_TO_CHANNEL,
OUTGOING_PROXY, PO,
ClassIdInfo, CollectionData, CLASS_ID_AND_NFT_CONTRACT_INFO, CLASS_ID_TO_CLASS,
CONTRACT_ADDR_LENGTH, CW721_ADMIN, CW721_CODE_ID, IBC_RECEIVE_TOKEN_METADATA,
INCOMING_PROXY, OUTGOING_CLASS_TOKEN_TO_CHANNEL, OUTGOING_PROXY, PO,
},
utils::get_collection_data,
};
use ics721_types::{
ibc_types::{IbcOutgoingMsg, NonFungibleTokenPacketData},
token_types::{ClassId, TokenId},
token_types::{ClassId, Token, TokenId},
};

const NFT_CONTRACT_1: &str = "nft1";
Expand All @@ -46,6 +47,11 @@ const OWNER_ADDR: &str = "owner";
const ADMIN_ADDR: &str = "admin";
const PAUSER_ADDR: &str = "pauser";

#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
pub struct UnknownMetadata {
pub unknown: String,
}

#[derive(Default)]
pub struct Ics721Contract {}
impl Ics721Execute<Empty> for Ics721Contract {
Expand Down Expand Up @@ -376,10 +382,6 @@ fn test_receive_nft() {
querier.update_wasm(mock_querier);

let mut deps = mock_dependencies();
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
struct UnknownMetadata {
pub unknown: String,
}
let token_id = "1";
IBC_RECEIVE_TOKEN_METADATA
.save(
Expand Down Expand Up @@ -645,6 +647,149 @@ fn test_receive_nft() {
}
}

#[test]
fn test_callback_mint() {
// test case: token data is NftExtension
{
let mut querier = MockQuerier::default();
querier.update_wasm(mock_querier);

let mut deps = mock_dependencies();
deps.querier = querier;
let class_id_info = ClassIdInfo {
class_id: ClassId::new(NFT_CONTRACT_1),
address: Addr::unchecked(NFT_CONTRACT_1),
};
CLASS_ID_AND_NFT_CONTRACT_INFO
.save(
deps.as_mut().storage,
&ClassId::new(NFT_CONTRACT_1),
&class_id_info,
)
.unwrap();

let token_id = "1";
let token = Token {
id: TokenId::new(token_id),
uri: None,
data: Some(to_json_binary(&NftExtension {
image: Some("https://ark.pass/image.png".to_string()),
external_url: Some("https://interchain.arkprotocol.io".to_string()),
description: Some("description".to_string()),
..Default::default()
}))
.transpose()
.unwrap(),
};
let res: cosmwasm_std::Response<_> = Ics721Contract::default()
.callback_mint(
deps.as_mut(),
ClassId::new(NFT_CONTRACT_1),
vec![token],
"receiver".to_string(),
)
.unwrap();
assert_eq!(res.messages.len(), 1);
// get mint message
let sub_msg = res.messages[0].clone();
match sub_msg.msg {
CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr, msg, ..
}) => {
assert_eq!(contract_addr, NFT_CONTRACT_1);
let msg: cw721_metadata_onchain::msg::ExecuteMsg = from_json(msg).unwrap();
match msg {
cw721_metadata_onchain::msg::ExecuteMsg::Mint {
token_id,
token_uri,
owner,
extension,
} => {
assert_eq!(token_id, "1");
assert_eq!(token_uri, None);
assert_eq!(owner, "receiver");
assert_eq!(
extension,
Some(NftExtensionMsg {
image: Some("https://ark.pass/image.png".to_string()),
external_url: Some("https://interchain.arkprotocol.io".to_string()),
description: Some("description".to_string()),
..Default::default()
})
);
}
_ => panic!("unexpected message type"),
}
}
_ => panic!("unexpected message type"),
}
}
// test case: token data is unknown
{
let mut querier = MockQuerier::default();
querier.update_wasm(mock_querier);

let mut deps = mock_dependencies();
deps.querier = querier;
let class_id_info = ClassIdInfo {
class_id: ClassId::new(NFT_CONTRACT_1),
address: Addr::unchecked(NFT_CONTRACT_1),
};
CLASS_ID_AND_NFT_CONTRACT_INFO
.save(
deps.as_mut().storage,
&ClassId::new(NFT_CONTRACT_1),
&class_id_info,
)
.unwrap();

let token_id = "1";
let token = Token {
id: TokenId::new(token_id),
uri: None,
data: Some(to_json_binary(&UnknownMetadata {
unknown: "unknown".to_string(),
}))
.transpose()
.unwrap(),
};
let res: cosmwasm_std::Response<_> = Ics721Contract::default()
.callback_mint(
deps.as_mut(),
ClassId::new(NFT_CONTRACT_1),
vec![token],
"receiver".to_string(),
)
.unwrap();
assert_eq!(res.messages.len(), 1);
// get mint message
let sub_msg = res.messages[0].clone();
match sub_msg.msg {
CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr, msg, ..
}) => {
assert_eq!(contract_addr, NFT_CONTRACT_1);
let msg: cw721_metadata_onchain::msg::ExecuteMsg = from_json(msg).unwrap();
match msg {
cw721_metadata_onchain::msg::ExecuteMsg::Mint {
token_id,
token_uri,
owner,
extension,
} => {
assert_eq!(token_id, "1");
assert_eq!(token_uri, None);
assert_eq!(owner, "receiver");
assert_eq!(extension, None);
}
_ => panic!("unexpected message type"),
}
}
_ => panic!("unexpected message type"),
}
}
}

#[test]
fn test_receive_sets_uri() {
let mut querier = MockQuerier::default();
Expand Down

0 comments on commit eb1af4d

Please sign in to comment.