Skip to content

Commit eb1af4d

Browse files
committed
mint with onchain metadata
1 parent dcdc99c commit eb1af4d

File tree

2 files changed

+179
-13
lines changed

2 files changed

+179
-13
lines changed

packages/ics721/src/execute.rs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ use cosmwasm_std::{
44
from_json, to_json_binary, Addr, Binary, ContractInfoResponse, Deps, DepsMut, Empty, Env,
55
Event, IbcMsg, MessageInfo, Order, Response, StdResult, SubMsg, WasmMsg,
66
};
7-
use cw721::msg::RoyaltyInfoResponse;
7+
use cw721::{
8+
msg::{NftExtensionMsg, RoyaltyInfoResponse},
9+
NftExtension,
10+
};
811
use cw_storage_plus::Map;
912
use ics721_types::{
1013
ibc_types::{IbcOutgoingMsg, IbcOutgoingProxyMsg, NonFungibleTokenPacketData},
@@ -419,7 +422,7 @@ where
419422
.flatten()
420423
{
421424
Some(metadata) => Some(metadata),
422-
// incase there is none in the storage, we use the one from the cw721 contract
425+
// incase there is none in the storage, this is the 'home' chain, so metadata is retrieved from the cw721 contract
423426
None => info.extension.map(|ext| to_json_binary(&ext)).transpose()?,
424427
};
425428

@@ -724,11 +727,29 @@ where
724727
&data,
725728
)?;
726729

730+
// parse token data and check whether it is of type NftExtension
731+
let extension: Option<NftExtensionMsg> = match data {
732+
Some(data) => from_json::<NftExtension>(data)
733+
.ok()
734+
.map(|ext| NftExtensionMsg {
735+
animation_url: ext.animation_url,
736+
attributes: ext.attributes,
737+
background_color: ext.background_color,
738+
description: ext.description,
739+
external_url: ext.external_url,
740+
image: ext.image,
741+
image_data: ext.image_data,
742+
youtube_url: ext.youtube_url,
743+
name: ext.name,
744+
}),
745+
None => None,
746+
};
747+
727748
let msg = cw721_metadata_onchain::msg::ExecuteMsg::Mint {
728749
token_id: id.into(),
729750
token_uri: uri,
730751
owner: receiver.to_string(),
731-
extension: None, // TODO consider token data in NonFungibleTokenPacketData
752+
extension,
732753
};
733754
Ok(WasmMsg::Execute {
734755
contract_addr: nft_contract.to_string(),

packages/ics721/src/testing/unit_tests.rs

Lines changed: 155 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ use cosmwasm_std::{
33
from_json,
44
testing::{mock_dependencies, mock_env, mock_info, MockQuerier, MOCK_CONTRACT_ADDR},
55
to_json_binary, Addr, ContractResult, CosmosMsg, Decimal, DepsMut, Empty, IbcMsg, IbcTimeout,
6-
Order, QuerierResult, Response, StdResult, SubMsg, Timestamp, WasmQuery,
6+
Order, QuerierResult, Response, StdResult, SubMsg, Timestamp, WasmMsg, WasmQuery,
77
};
88
use cw721::{
99
msg::{
10-
AllNftInfoResponse, CollectionInfoAndExtensionResponse, NftInfoResponse, NumTokensResponse,
10+
AllNftInfoResponse, CollectionInfoAndExtensionResponse, NftExtensionMsg, NftInfoResponse,
11+
NumTokensResponse,
1112
},
1213
CollectionExtension, DefaultOptionalCollectionExtension, DefaultOptionalNftExtension,
1314
NftExtension, RoyaltyInfo,
@@ -27,15 +28,15 @@ use crate::{
2728
Ics721Query,
2829
},
2930
state::{
30-
CollectionData, CLASS_ID_TO_CLASS, CONTRACT_ADDR_LENGTH, CW721_ADMIN, CW721_CODE_ID,
31-
IBC_RECEIVE_TOKEN_METADATA, INCOMING_PROXY, OUTGOING_CLASS_TOKEN_TO_CHANNEL,
32-
OUTGOING_PROXY, PO,
31+
ClassIdInfo, CollectionData, CLASS_ID_AND_NFT_CONTRACT_INFO, CLASS_ID_TO_CLASS,
32+
CONTRACT_ADDR_LENGTH, CW721_ADMIN, CW721_CODE_ID, IBC_RECEIVE_TOKEN_METADATA,
33+
INCOMING_PROXY, OUTGOING_CLASS_TOKEN_TO_CHANNEL, OUTGOING_PROXY, PO,
3334
},
3435
utils::get_collection_data,
3536
};
3637
use ics721_types::{
3738
ibc_types::{IbcOutgoingMsg, NonFungibleTokenPacketData},
38-
token_types::{ClassId, TokenId},
39+
token_types::{ClassId, Token, TokenId},
3940
};
4041

4142
const NFT_CONTRACT_1: &str = "nft1";
@@ -46,6 +47,11 @@ const OWNER_ADDR: &str = "owner";
4647
const ADMIN_ADDR: &str = "admin";
4748
const PAUSER_ADDR: &str = "pauser";
4849

50+
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
51+
pub struct UnknownMetadata {
52+
pub unknown: String,
53+
}
54+
4955
#[derive(Default)]
5056
pub struct Ics721Contract {}
5157
impl Ics721Execute<Empty> for Ics721Contract {
@@ -376,10 +382,6 @@ fn test_receive_nft() {
376382
querier.update_wasm(mock_querier);
377383

378384
let mut deps = mock_dependencies();
379-
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
380-
struct UnknownMetadata {
381-
pub unknown: String,
382-
}
383385
let token_id = "1";
384386
IBC_RECEIVE_TOKEN_METADATA
385387
.save(
@@ -645,6 +647,149 @@ fn test_receive_nft() {
645647
}
646648
}
647649

650+
#[test]
651+
fn test_callback_mint() {
652+
// test case: token data is NftExtension
653+
{
654+
let mut querier = MockQuerier::default();
655+
querier.update_wasm(mock_querier);
656+
657+
let mut deps = mock_dependencies();
658+
deps.querier = querier;
659+
let class_id_info = ClassIdInfo {
660+
class_id: ClassId::new(NFT_CONTRACT_1),
661+
address: Addr::unchecked(NFT_CONTRACT_1),
662+
};
663+
CLASS_ID_AND_NFT_CONTRACT_INFO
664+
.save(
665+
deps.as_mut().storage,
666+
&ClassId::new(NFT_CONTRACT_1),
667+
&class_id_info,
668+
)
669+
.unwrap();
670+
671+
let token_id = "1";
672+
let token = Token {
673+
id: TokenId::new(token_id),
674+
uri: None,
675+
data: Some(to_json_binary(&NftExtension {
676+
image: Some("https://ark.pass/image.png".to_string()),
677+
external_url: Some("https://interchain.arkprotocol.io".to_string()),
678+
description: Some("description".to_string()),
679+
..Default::default()
680+
}))
681+
.transpose()
682+
.unwrap(),
683+
};
684+
let res: cosmwasm_std::Response<_> = Ics721Contract::default()
685+
.callback_mint(
686+
deps.as_mut(),
687+
ClassId::new(NFT_CONTRACT_1),
688+
vec![token],
689+
"receiver".to_string(),
690+
)
691+
.unwrap();
692+
assert_eq!(res.messages.len(), 1);
693+
// get mint message
694+
let sub_msg = res.messages[0].clone();
695+
match sub_msg.msg {
696+
CosmosMsg::Wasm(WasmMsg::Execute {
697+
contract_addr, msg, ..
698+
}) => {
699+
assert_eq!(contract_addr, NFT_CONTRACT_1);
700+
let msg: cw721_metadata_onchain::msg::ExecuteMsg = from_json(msg).unwrap();
701+
match msg {
702+
cw721_metadata_onchain::msg::ExecuteMsg::Mint {
703+
token_id,
704+
token_uri,
705+
owner,
706+
extension,
707+
} => {
708+
assert_eq!(token_id, "1");
709+
assert_eq!(token_uri, None);
710+
assert_eq!(owner, "receiver");
711+
assert_eq!(
712+
extension,
713+
Some(NftExtensionMsg {
714+
image: Some("https://ark.pass/image.png".to_string()),
715+
external_url: Some("https://interchain.arkprotocol.io".to_string()),
716+
description: Some("description".to_string()),
717+
..Default::default()
718+
})
719+
);
720+
}
721+
_ => panic!("unexpected message type"),
722+
}
723+
}
724+
_ => panic!("unexpected message type"),
725+
}
726+
}
727+
// test case: token data is unknown
728+
{
729+
let mut querier = MockQuerier::default();
730+
querier.update_wasm(mock_querier);
731+
732+
let mut deps = mock_dependencies();
733+
deps.querier = querier;
734+
let class_id_info = ClassIdInfo {
735+
class_id: ClassId::new(NFT_CONTRACT_1),
736+
address: Addr::unchecked(NFT_CONTRACT_1),
737+
};
738+
CLASS_ID_AND_NFT_CONTRACT_INFO
739+
.save(
740+
deps.as_mut().storage,
741+
&ClassId::new(NFT_CONTRACT_1),
742+
&class_id_info,
743+
)
744+
.unwrap();
745+
746+
let token_id = "1";
747+
let token = Token {
748+
id: TokenId::new(token_id),
749+
uri: None,
750+
data: Some(to_json_binary(&UnknownMetadata {
751+
unknown: "unknown".to_string(),
752+
}))
753+
.transpose()
754+
.unwrap(),
755+
};
756+
let res: cosmwasm_std::Response<_> = Ics721Contract::default()
757+
.callback_mint(
758+
deps.as_mut(),
759+
ClassId::new(NFT_CONTRACT_1),
760+
vec![token],
761+
"receiver".to_string(),
762+
)
763+
.unwrap();
764+
assert_eq!(res.messages.len(), 1);
765+
// get mint message
766+
let sub_msg = res.messages[0].clone();
767+
match sub_msg.msg {
768+
CosmosMsg::Wasm(WasmMsg::Execute {
769+
contract_addr, msg, ..
770+
}) => {
771+
assert_eq!(contract_addr, NFT_CONTRACT_1);
772+
let msg: cw721_metadata_onchain::msg::ExecuteMsg = from_json(msg).unwrap();
773+
match msg {
774+
cw721_metadata_onchain::msg::ExecuteMsg::Mint {
775+
token_id,
776+
token_uri,
777+
owner,
778+
extension,
779+
} => {
780+
assert_eq!(token_id, "1");
781+
assert_eq!(token_uri, None);
782+
assert_eq!(owner, "receiver");
783+
assert_eq!(extension, None);
784+
}
785+
_ => panic!("unexpected message type"),
786+
}
787+
}
788+
_ => panic!("unexpected message type"),
789+
}
790+
}
791+
}
792+
648793
#[test]
649794
fn test_receive_sets_uri() {
650795
let mut querier = MockQuerier::default();

0 commit comments

Comments
 (0)