From 05c3687ddf54bf180b4b55847500d30c0a2a4cc7 Mon Sep 17 00:00:00 2001 From: mr-t Date: Fri, 7 Jun 2024 12:17:26 +0200 Subject: [PATCH] test callback --- packages/ics721/src/ibc_packet_receive.rs | 10 +- packages/ics721/src/query.rs | 2 + packages/ics721/src/testing/ibc_tests.rs | 135 +++++++++++++++++----- 3 files changed, 111 insertions(+), 36 deletions(-) diff --git a/packages/ics721/src/ibc_packet_receive.rs b/packages/ics721/src/ibc_packet_receive.rs index 94ae0987..15e72060 100644 --- a/packages/ics721/src/ibc_packet_receive.rs +++ b/packages/ics721/src/ibc_packet_receive.rs @@ -13,7 +13,7 @@ use crate::{ load_nft_contract_for_class_id, query_get_instantiate2_nft_contract, query_nft_contract_for_class_id, }, - state::{CW721_CODE_ID, OUTGOING_CLASS_TOKEN_TO_CHANNEL, PO}, + state::{OUTGOING_CLASS_TOKEN_TO_CHANNEL, PO}, token_types::{VoucherCreation, VoucherRedemption}, ContractError, }; @@ -240,13 +240,7 @@ fn create_callback_msg( Some(nft_contract) => Ok(nft_contract), None => { // contract not yet instantiated, so we use instantiate2 to get the contract address - let cw721_code_id = CW721_CODE_ID.load(deps.storage)?; - query_get_instantiate2_nft_contract( - deps, - env, - local_class_id.clone(), - Some(cw721_code_id), - ) + query_get_instantiate2_nft_contract(deps, env, local_class_id.clone(), None) } } }?; diff --git a/packages/ics721/src/query.rs b/packages/ics721/src/query.rs index f0a43b3a..4a9aeecf 100644 --- a/packages/ics721/src/query.rs +++ b/packages/ics721/src/query.rs @@ -96,6 +96,8 @@ pub fn query_nft_contract_for_class_id( .map(|e| e.map(|(_, v)| v.address)) } +/// Returns the address of the instantiated NFT contract for the given class_id. +/// If cw721_code_id is None, it will use the default CW721_CODE_ID. pub fn query_get_instantiate2_nft_contract( deps: Deps, env: &Env, diff --git a/packages/ics721/src/testing/ibc_tests.rs b/packages/ics721/src/testing/ibc_tests.rs index d1ecb44b..55ee09d0 100644 --- a/packages/ics721/src/testing/ibc_tests.rs +++ b/packages/ics721/src/testing/ibc_tests.rs @@ -22,8 +22,8 @@ use crate::{ }; use ics721_types::{ ibc_types::NonFungibleTokenPacketData, - token_types::{ClassId, TokenId}, - types::{Ics721Callbacks, ReceiverExecuteMsg}, + token_types::{Class, ClassId, TokenId}, + types::{Ics721Callbacks, Ics721Memo, ReceiverExecuteMsg}, }; const CONTRACT_PORT: &str = "wasm.address1"; @@ -32,9 +32,11 @@ const CONNECTION_ID: &str = "connection-2"; const CHANNEL_ID: &str = "channel-1"; const DEFAULT_TIMEOUT: u64 = 42; // Seconds. -const ADDR1: &str = "addr1"; +const ADDR_ICS721: &str = "addr_ics721"; const RELAYER_ADDR: &str = "relayer"; const CW721_CODE_ID: u64 = 0; +const ADDR_CW721: &str = "addr_cw721"; +const ADDR_NFT_RECEIVER: &str = "nft_receiver"; #[derive(Default)] pub struct Ics721Contract {} @@ -48,6 +50,13 @@ impl Ics721Execute for Ics721Contract { impl Ics721Ibc for Ics721Contract {} impl Ics721Query for Ics721Contract {} +#[cw_serde] +pub struct CallbackData { + pub token_id: String, + /// NFT owner on source chain, on ack this sender also receives a POAP + pub sender: String, +} + fn mock_channel(channel_id: &str) -> IbcChannel { IbcChannel::new( IbcEndpoint { @@ -80,6 +89,24 @@ fn mock_packet(data: Binary) -> IbcPacket { ) } +fn create_memo( + sender: String, + token_id: String, + ack_callback_addr: Option, + receive_callback_addr: Option, +) -> Ics721Memo { + let callback_data = Some(to_json_binary(&CallbackData { sender, token_id }).unwrap()); + let callbacks = Ics721Callbacks { + ack_callback_data: callback_data.clone(), + ack_callback_addr, + receive_callback_data: callback_data, + receive_callback_addr, + }; + Ics721Memo { + callbacks: Some(callbacks), + } +} + fn add_channel(mut deps: DepsMut, env: Env, channel_id: &str) { let channel = mock_channel(channel_id); let open_msg = IbcChannelOpenMsg::new_init(channel.clone()); @@ -231,7 +258,7 @@ fn test_ibc_channel_open() { let env = mock_env(); // Instantiate the contract - do_instantiate(deps.as_mut(), env.clone(), ADDR1).unwrap(); + do_instantiate(deps.as_mut(), env.clone(), ADDR_ICS721).unwrap(); // Add channel calls open and connect valid add_channel(deps.as_mut(), env, "channel-1"); @@ -244,7 +271,7 @@ fn test_ibc_channel_open_ordered_channel() { let env = mock_env(); // Instantiate the contract - do_instantiate(deps.as_mut(), env.clone(), ADDR1).unwrap(); + do_instantiate(deps.as_mut(), env.clone(), ADDR_ICS721).unwrap(); let channel_id = "channel-1"; let channel = IbcChannel::new( @@ -274,7 +301,7 @@ fn test_ibc_channel_open_invalid_version() { let env = mock_env(); // Instantiate the contract - do_instantiate(deps.as_mut(), env.clone(), ADDR1).unwrap(); + do_instantiate(deps.as_mut(), env.clone(), ADDR_ICS721).unwrap(); let channel_id = "channel-1"; let channel = IbcChannel::new( @@ -304,7 +331,7 @@ fn test_ibc_channel_open_invalid_version_counterparty() { let env = mock_env(); // Instantiate the contract - do_instantiate(deps.as_mut(), env.clone(), ADDR1).unwrap(); + do_instantiate(deps.as_mut(), env.clone(), ADDR_ICS721).unwrap(); let channel_id = "channel-1"; let channel = IbcChannel::new( @@ -336,7 +363,7 @@ fn test_ibc_channel_connect() { let env = mock_env(); // Instantiate the contract - do_instantiate(deps.as_mut(), env.clone(), ADDR1).unwrap(); + do_instantiate(deps.as_mut(), env.clone(), ADDR_ICS721).unwrap(); // Add channel calls open and connect valid add_channel(deps.as_mut(), env, "channel-1"); @@ -349,7 +376,7 @@ fn test_ibc_channel_connect_ordered_channel() { let env = mock_env(); // Instantiate the contract - do_instantiate(deps.as_mut(), env.clone(), ADDR1).unwrap(); + do_instantiate(deps.as_mut(), env.clone(), ADDR_ICS721).unwrap(); let channel_id = "channel-1"; let channel = IbcChannel::new( @@ -379,7 +406,7 @@ fn test_ibc_channel_connect_invalid_version() { let env = mock_env(); // Instantiate the contract - do_instantiate(deps.as_mut(), env.clone(), ADDR1).unwrap(); + do_instantiate(deps.as_mut(), env.clone(), ADDR_ICS721).unwrap(); let channel_id = "channel-1"; let channel = IbcChannel::new( @@ -409,7 +436,7 @@ fn test_ibc_channel_connect_invalid_version_counterparty() { let env = mock_env(); // Instantiate the contract - do_instantiate(deps.as_mut(), env.clone(), ADDR1).unwrap(); + do_instantiate(deps.as_mut(), env.clone(), ADDR_ICS721).unwrap(); let channel_id = "channel-1"; let channel = IbcChannel::new( @@ -437,22 +464,51 @@ fn test_ibc_channel_connect_invalid_version_counterparty() { #[test] fn test_ibc_packet_receive() { - let data = to_json_binary(&NonFungibleTokenPacketData { - class_id: ClassId::new("id"), + let mut deps = mock_dependencies(); + let env = mock_env(); + crate::state::CW721_CODE_ID + .save(deps.as_mut().storage, &0) + .unwrap(); + let class_id = ClassId::new(ADDR_CW721); + let class = Class { + id: class_id.clone(), + // There is no collection-level uri nor data in the + // cw721 specification so we set those values to + // `None` for local, cw721 NFTs. + uri: None, + data: None, + }; + let sender = "violet"; + let token_id = "1"; + let memo = create_memo(sender.to_string(), token_id.to_string(), None, None); + let memo_string = Some(Binary::to_base64(&to_json_binary(&memo).unwrap())); + let nft_packet = NonFungibleTokenPacketData { + class_id, class_uri: None, class_data: None, - token_ids: vec![TokenId::new("1")], + token_ids: vec![TokenId::new(token_id.to_string())], token_uris: None, token_data: None, - sender: "violet".to_string(), - receiver: "blue".to_string(), - memo: None, - }) - .unwrap(); + sender: sender.to_string(), + receiver: ADDR_NFT_RECEIVER.to_string(), + memo: memo_string, + }; + let data = to_json_binary(&nft_packet).unwrap(); let ibc_packet = mock_packet(data); + let class_id_info = ClassIdInfo { + class_id: ClassId::new(format!( + "{}/{}/{}", + ibc_packet.dest.port_id, + ibc_packet.dest.channel_id, + class.id.clone() + )), + address: Addr::unchecked(ADDR_CW721), + }; + CLASS_ID_AND_NFT_CONTRACT_INFO + .save(deps.as_mut().storage, &class.id, &class_id_info) + .unwrap(); + let packet = IbcPacketReceiveMsg::new(ibc_packet.clone(), Addr::unchecked(RELAYER_ADDR)); - let mut deps = mock_dependencies(); - let env = mock_env(); PO.set_pauser(&mut deps.storage, &deps.api, None).unwrap(); let response = Ics721Contract::default() .ibc_packet_receive(deps.as_mut(), env, packet) @@ -475,9 +531,9 @@ fn test_ibc_packet_receive() { assert!(conjunction_msg.is_some()); let operands = conjunction_msg.unwrap(); - assert_eq!(operands.len(), 2); + assert_eq!(operands.len(), 3); - let add_incoming_msg = operands[1].clone(); + let add_incoming_msg = operands[2].clone(); match add_incoming_msg { WasmMsg::Execute { msg, .. } => { match from_json::(msg).ok() { @@ -493,7 +549,7 @@ fn test_ibc_packet_receive() { // assert there is only one class token to channel entry let class_id = format!( "{}/{}/{}", - ibc_packet.dest.port_id, ibc_packet.dest.channel_id, "id" + ibc_packet.dest.port_id, ibc_packet.dest.channel_id, ADDR_CW721 ); assert_eq!( class_token_to_channel_list, @@ -504,7 +560,30 @@ fn test_ibc_packet_receive() { }, _ => panic!("unexpected execute msg"), }, - _ => panic!("no callback msg"), + _ => panic!("not a callback msg"), + } + } + _ => panic!("unexpected wasm msg"), + } + + let receive_callback_msg = operands[1].clone(); + match receive_callback_msg { + WasmMsg::Execute { + contract_addr, + msg, + funds: _, + } => { + assert_eq!(contract_addr, ADDR_NFT_RECEIVER); + match from_json::(msg).ok() { + Some(ReceiverExecuteMsg::Ics721ReceiveCallback(msg)) => { + assert_eq!(msg.nft_contract, ADDR_CW721); + assert_eq!(msg.original_packet, nft_packet); + assert_eq!( + msg.msg, + memo.callbacks.unwrap().receive_callback_data.unwrap() + ); + } + _ => panic!("unexpected execute msg"), } } _ => panic!("unexpected wasm msg"), @@ -547,7 +626,7 @@ fn test_ibc_packet_receive_emits_memo() { token_uris: None, token_data: None, sender: "violet".to_string(), - receiver: "blue".to_string(), + receiver: ADDR_NFT_RECEIVER.to_string(), memo: Some("memo".to_string()), }) .unwrap(); @@ -715,7 +794,7 @@ fn test_different_memo_ignored() { token_uris: None, token_data: None, sender: "violet".to_string(), - receiver: "blue".to_string(), + receiver: ADDR_NFT_RECEIVER.to_string(), memo: Some( to_json_binary(&DifferentMemo { different: Some(Ics721Callbacks { @@ -768,7 +847,7 @@ fn test_ibc_packet_not_json_memo() { token_uris: None, token_data: None, sender: "violet".to_string(), - receiver: "blue".to_string(), + receiver: ADDR_NFT_RECEIVER.to_string(), memo: None, };