difftreelog
Merge pull request #148 from usetech-llc/feature/NFTPAR-373_chain_extensions
in: master
Chain extensions
10 files changed
.devcontainer/Dockerfilediffbeforeafterboth3RUN apt-get update && export DEBIAN_FRONTEND=noninteractive && \3RUN apt-get update && export DEBIAN_FRONTEND=noninteractive && \4 apt-get -y install --no-install-recommends libssl-dev pkg-config libclang-dev clang4 apt-get -y install --no-install-recommends libssl-dev pkg-config libclang-dev clang556RUN curl -L -o- https://github.com/WebAssembly/binaryen/releases/download/version_101/binaryen-version_101-x86_64-linux.tar.gz | \7 tar xz --strip-components=1 -C /usr86USER vscode9USER vscode7108RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh | bash && \11RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh | bash && \11 nvm install v12.20.1 && \14 nvm install v12.20.1 && \12 rustup toolchain install nightly-2021-03-01 && \15 rustup toolchain install nightly-2021-03-01 && \13 rustup default nightly-2021-03-01 && \16 rustup default nightly-2021-03-01 && \14 rustup target add wasm32-unknown-unknown17 rustup target add wasm32-unknown-unknown && \18 cargo install cargo-contract19Cargo.lockdiffbeforeafterboth5392 "sp-std",5392 "sp-std",5393]5393]53945395[[package]]5396name = "pallet-scheduler"5397version = "3.0.0"5398source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.3#c94e0cdfe5556680dca1996004751eeb114755d7"5399dependencies = [5400 "frame-benchmarking",5401 "frame-support",5402 "frame-system",5403 "log",5404 "parity-scale-codec",5405 "sp-io",5406 "sp-runtime",5407 "sp-std",5408]540953945410[[package]]5395[[package]]5411name = "pallet-scheduler"5396name = "pallet-scheduler"5428 "substrate-test-utils",5413 "substrate-test-utils",5429]5414]54155416[[package]]5417name = "pallet-scheduler"5418version = "3.0.0"5419source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.3#c94e0cdfe5556680dca1996004751eeb114755d7"5420dependencies = [5421 "frame-benchmarking",5422 "frame-support",5423 "frame-system",5424 "log",5425 "parity-scale-codec",5426 "sp-io",5427 "sp-runtime",5428 "sp-std",5429]543054305431[[package]]5431[[package]]5432name = "pallet-session"5432name = "pallet-session"pallets/nft/src/lib.rsdiffbeforeafterboth567 let sender = ensure_signed(origin)?;567 let sender = ensure_signed(origin)?;568 let collection = Self::get_collection(collection_id)?;568 let collection = Self::get_collection(collection_id)?;569569 Self::check_owner_or_admin_permissions(&collection, sender)?;570 Self::toggle_white_list_internal(570571 &sender,572 &collection,571 <WhiteList<T>>::insert(collection_id, address, true);573 &address,572 574 true,575 )?;576573 Ok(())577 Ok(())574 }578 }592 let sender = ensure_signed(origin)?;596 let sender = ensure_signed(origin)?;593 let collection = Self::get_collection(collection_id)?;597 let collection = Self::get_collection(collection_id)?;598594 Self::check_owner_or_admin_permissions(&collection, sender)?;599 Self::toggle_white_list_internal(595600 &sender,596 <WhiteList<T>>::remove(collection_id, address);601 &collection,602 &address,603 false,604 )?;597605598 Ok(())606 Ok(())983 pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {991 pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {984992985 let sender = ensure_signed(origin)?;993 let sender = ensure_signed(origin)?;986 let target_collection = Self::get_collection(collection_id)?;994 let collection = Self::get_collection(collection_id)?;987995988 Self::token_exists(&target_collection, item_id)?;996 Self::approve_internal(sender, spender, &collection, item_id, amount)?;989997990 // Transfer permissions check991 let bypasses_limits = target_collection.limits.owner_can_transfer &&997 let allowance_limit = if bypasses_limits {998 None999 } else if let Some(amount) = Self::owned_amount(1000 sender.clone(),1001 &target_collection,1002 item_id,1003 ) {1004 Some(amount)1005 } else {1006 fail!(Error::<T>::NoPermission);1007 };10081009 if target_collection.access == AccessMode::WhiteList {1010 Self::check_white_list(&target_collection, &sender)?;1011 Self::check_white_list(&target_collection, &spender)?;1012 }10131014 let allowance: u128 = amount1015 .checked_add(<Allowances<T>>::get(collection_id, (item_id, &sender, &spender)))1016 .ok_or(Error::<T>::NumOverflow)?;1017 if let Some(limit) = allowance_limit {1018 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1019 }1020 <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);10211022 Self::deposit_event(RawEvent::Approved(target_collection.id, item_id, sender, spender, allowance));1023 Ok(())998 Ok(())1024 }999 }1025 1000 1047 pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1022 pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {104810231049 let sender = ensure_signed(origin)?;1024 let sender = ensure_signed(origin)?;1050 let target_collection = Self::get_collection(collection_id)?;1025 let collection = Self::get_collection(collection_id)?;105110261052 // Check approval1053 let approval: u128 = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));10541055 // Limits check1056 Self::is_correct_transfer(&target_collection, &recipient)?;1027 Self::transfer_from_internal(sender, from, recipient, &collection, item_id, value)?;105710281058 // Transfer permissions check 1059 ensure!(1060 approval >= value || 1061 (1062 target_collection.limits.owner_can_transfer &&1063 Self::is_owner_or_admin_permissions(&target_collection, sender.clone())1064 ),1065 Error::<T>::NoPermission1066 );10671068 if target_collection.access == AccessMode::WhiteList {1069 Self::check_white_list(&target_collection, &sender)?;1070 Self::check_white_list(&target_collection, &recipient)?;1071 }10721073 // Reduce approval by transferred amount or remove if remaining approval drops to 01074 if approval.saturating_sub(value) > 0 {1075 <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1076 }1077 else {1078 <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1079 }10801081 match target_collection.mode1082 {1083 CollectionMode::NFT => Self::transfer_nft(&target_collection, item_id, from.clone(), recipient.clone())?,1084 CollectionMode::Fungible(_) => Self::transfer_fungible(&target_collection, value, &from, &recipient)?,1085 CollectionMode::ReFungible => Self::transfer_refungible(&target_collection, item_id, value, from.clone(), recipient.clone())?,1086 _ => ()1087 };10881089 Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, from, recipient, value));1090 Ok(())1029 Ok(())1091 }1030 }109210311127 ) -> DispatchResult {1066 ) -> DispatchResult {1128 let sender = ensure_signed(origin)?;1067 let sender = ensure_signed(origin)?;1129 1068 1130 let target_collection = Self::get_collection(collection_id)?;1069 let collection = Self::get_collection(collection_id)?;10701131 Self::token_exists(&target_collection, item_id)?;1071 Self::set_variable_meta_data_internal(sender, &collection, item_id, data)?;11321133 ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);11341135 // Modify permissions check1136 ensure!(Self::is_item_owner(sender.clone(), &target_collection, item_id) ||1137 Self::is_owner_or_admin_permissions(&target_collection, sender.clone()),1138 Error::<T>::NoPermission);11391140 match target_collection.mode1141 {1142 CollectionMode::NFT => Self::set_nft_variable_data(&target_collection, item_id, data)?,1143 CollectionMode::ReFungible => Self::set_re_fungible_variable_data(&target_collection, item_id, data)?,1144 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1145 _ => fail!(Error::<T>::UnexpectedCollectionType)1146 };114710721148 Ok(())1073 Ok(())1149 }1074 }1516 Ok(())1441 Ok(())1517 }1442 }151814431444 pub fn approve_internal(1445 sender: T::AccountId,1446 spender: T::AccountId,1447 collection: &CollectionHandle<T>,1448 item_id: TokenId,1449 amount: u128,1450 ) -> DispatchResult {1451 Self::token_exists(&collection, item_id)?;14521453 // Transfer permissions check1454 let bypasses_limits = collection.limits.owner_can_transfer &&1455 Self::is_owner_or_admin_permissions(1456 &collection,1457 sender.clone(),1458 );14591460 let allowance_limit = if bypasses_limits {1461 None1462 } else if let Some(amount) = Self::owned_amount(1463 sender.clone(),1464 &collection,1465 item_id,1466 ) {1467 Some(amount)1468 } else {1469 fail!(Error::<T>::NoPermission);1470 };14711472 if collection.access == AccessMode::WhiteList {1473 Self::check_white_list(&collection, &sender)?;1474 Self::check_white_list(&collection, &spender)?;1475 }14761477 let allowance: u128 = amount1478 .checked_add(<Allowances<T>>::get(collection.id, (item_id, &sender, &spender)))1479 .ok_or(Error::<T>::NumOverflow)?;1480 if let Some(limit) = allowance_limit {1481 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1482 }1483 <Allowances<T>>::insert(collection.id, (item_id, &sender, &spender), allowance);14841485 Self::deposit_event(RawEvent::Approved(collection.id, item_id, sender, spender, allowance));1486 Ok(())1487 }14881489 pub fn transfer_from_internal(1490 sender: T::AccountId,1491 from: T::AccountId,1492 recipient: T::AccountId,1493 collection: &CollectionHandle<T>,1494 item_id: TokenId,1495 amount: u128,1496 ) -> DispatchResult {1497 // Check approval1498 let approval: u128 = <Allowances<T>>::get(collection.id, (item_id, &from, &sender));14991500 // Limits check1501 Self::is_correct_transfer(&collection, &recipient)?;15021503 // Transfer permissions check1504 ensure!(1505 approval >= amount || 1506 (1507 collection.limits.owner_can_transfer &&1508 Self::is_owner_or_admin_permissions(&collection, sender.clone())1509 ),1510 Error::<T>::NoPermission1511 );15121513 if collection.access == AccessMode::WhiteList {1514 Self::check_white_list(&collection, &sender)?;1515 Self::check_white_list(&collection, &recipient)?;1516 }15171518 // Reduce approval by transferred amount or remove if remaining approval drops to 01519 let allowance = approval.saturating_sub(amount);1520 if allowance > 0 {1521 <Allowances<T>>::insert(collection.id, (item_id, &from, &sender), allowance);1522 } else {1523 <Allowances<T>>::remove(collection.id, (item_id, &from, &sender));1524 }15251526 match collection.mode {1527 CollectionMode::NFT => {1528 Self::transfer_nft(&collection, item_id, from.clone(), recipient.clone())?1529 }1530 CollectionMode::Fungible(_) => {1531 Self::transfer_fungible(&collection, amount, &from, &recipient)?1532 }1533 CollectionMode::ReFungible => {1534 Self::transfer_refungible(&collection, item_id, amount, from.clone(), recipient.clone())?1535 }1536 _ => ()1537 };15381539 Ok(())1540 }15411542 pub fn set_variable_meta_data_internal(1543 sender: T::AccountId,1544 collection: &CollectionHandle<T>, 1545 item_id: TokenId,1546 data: Vec<u8>,1547 ) -> DispatchResult {1548 Self::token_exists(&collection, item_id)?;15491550 ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);15511552 // Modify permissions check1553 ensure!(Self::is_item_owner(sender.clone(), &collection, item_id) ||1554 Self::is_owner_or_admin_permissions(&collection, sender.clone()),1555 Error::<T>::NoPermission);15561557 match collection.mode1558 {1559 CollectionMode::NFT => Self::set_nft_variable_data(&collection, item_id, data)?,1560 CollectionMode::ReFungible => Self::set_re_fungible_variable_data(&collection, item_id, data)?,1561 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1562 _ => fail!(Error::<T>::UnexpectedCollectionType)1563 };15641565 Ok(())1566 }15671568 pub fn create_multiple_items_internal(1569 sender: T::AccountId,1570 collection: &CollectionHandle<T>,1571 owner: T::AccountId,1572 items_data: Vec<CreateItemData>,1573 ) -> DispatchResult {1574 Self::can_create_items_in_collection(&collection, &sender, &owner, items_data.len() as u32)?;15751576 for data in &items_data {1577 Self::validate_create_item_args(&collection, data)?;1578 }1579 for data in &items_data {1580 Self::create_item_no_validation(&collection, owner.clone(), data.clone())?;1581 }15821583 Ok(())1584 }15851586 pub fn toggle_white_list_internal(1587 sender: &T::AccountId,1588 collection: &CollectionHandle<T>,1589 address: &T::AccountId,1590 whitelisted: bool,1591 ) -> DispatchResult {1592 Self::check_owner_or_admin_permissions(&collection, sender.clone())?;15931594 if whitelisted {1595 <WhiteList<T>>::insert(collection.id, address, true);1596 } else {1597 <WhiteList<T>>::remove(collection.id, address);1598 }15991600 Ok(())1601 }151916021520 fn is_correct_transfer(collection: &CollectionHandle<T>, recipient: &T::AccountId) -> DispatchResult {1603 fn is_correct_transfer(collection: &CollectionHandle<T>, recipient: &T::AccountId) -> DispatchResult {1521 let collection_id = collection.id;1604 let collection_id = collection.id;runtime/src/chain_extension.rsdiffbeforeafterboth19pub use pallet_nft::*;19pub use pallet_nft::*;20use nft_data_structs::*;20use nft_data_structs::*;2122use crate::Vec;212322/// Create item parameters24/// Create item parameters23#[derive(Debug, PartialEq, Encode, Decode)]25#[derive(Debug, PartialEq, Encode, Decode)]36 pub amount: u128,38 pub amount: u128,37}39}4041#[derive(Debug, PartialEq, Encode, Decode)]42pub struct NFTExtCreateMultipleItems<E: Ext> {43 pub owner: <E::T as SysConfig>::AccountId,44 pub collection_id: u32,45 pub data: Vec<CreateItemData>,46}4748#[derive(Debug, PartialEq, Encode, Decode)]49pub struct NFTExtApprove<E: Ext> {50 pub spender: <E::T as SysConfig>::AccountId,51 pub collection_id: u32,52 pub item_id: u32,53 pub amount: u128,54}5556#[derive(Debug, PartialEq, Encode, Decode)]57pub struct NFTExtTransferFrom<E: Ext> {58 pub owner: <E::T as SysConfig>::AccountId,59 pub recipient: <E::T as SysConfig>::AccountId,60 pub collection_id: u32,61 pub item_id: u32,62 pub amount: u128,63}6465#[derive(Debug, PartialEq, Encode, Decode)]66pub struct NFTExtSetVariableMetaData {67 pub collection_id: u32,68 pub item_id: u32,69 pub data: Vec<u8>, 70}7172#[derive(Debug, PartialEq, Encode, Decode)]73pub struct NFTExtToggleWhiteList<E: Ext> {74 pub collection_id: u32,75 pub address: <E::T as SysConfig>::AccountId,76 pub whitelisted: bool,77}387839/// The chain Extension of NFT pallet79/// The chain Extension of NFT pallet40pub struct NFTExtension;80pub struct NFTExtension;8182pub type NftWeightInfoOf<C> = <C as pallet_nft::Config>::WeightInfo;418342impl<C: Config + pallet_contracts::Config> ChainExtension<C> for NFTExtension {84impl<C: Config + pallet_contracts::Config> ChainExtension<C> for NFTExtension {43 fn call<E: Ext>(func_id: u32, env: Environment<E, InitState>) -> Result<RetVal, DispatchError>85 fn call<E: Ext>(func_id: u32, env: Environment<E, InitState>) -> Result<RetVal, DispatchError>44 where86 where45 E: Ext<T = C>,87 E: Ext<T = C>,88 C: pallet_nft::Config,46 <E::T as SysConfig>::AccountId: UncheckedFrom<<E::T as SysConfig>::Hash> + AsRef<[u8]>,89 <E::T as SysConfig>::AccountId: UncheckedFrom<<E::T as SysConfig>::Hash> + AsRef<[u8]>,47 {90 {48 // The memory of the vm stores buf in scale-codec91 // The memory of the vm stores buf in scale-codec49 match func_id {92 match func_id {50 0 => {93 0 => {51 let mut env = env.buf_in_buf_out();94 let mut env = env.buf_in_buf_out();52 let input: NFTExtTransfer<E> = env.read_as()?;95 let input: NFTExtTransfer<E> = env.read_as()?;96 env.charge_weight(NftWeightInfoOf::<C>::transfer())?;539754 let collection = pallet_nft::Module::<C>::get_collection(input.collection_id)?;98 let collection = pallet_nft::Module::<C>::get_collection(input.collection_id)?;559956 match pallet_nft::Module::<C>::transfer_internal(100 pallet_nft::Module::<C>::transfer_internal(57 env.ext().caller().clone(),101 env.ext().address().clone(),58 input.recipient,102 input.recipient,59 &collection,103 &collection,60 input.token_id,104 input.token_id,61 input.amount,105 input.amount,62 ) {106 )?;63 Ok(_) => Ok(RetVal::Converging(func_id)),107 64 _ => Err(DispatchError::Other("Transfer error"))108 Ok(RetVal::Converging(0))65 }66 },109 },67 1 => {110 1 => {68 // Create Item111 // Create Item69 let mut env = env.buf_in_buf_out();112 let mut env = env.buf_in_buf_out();70 let input: NFTExtCreateItem<E> = env.read_as()?;113 let input: NFTExtCreateItem<E> = env.read_as()?;114 env.charge_weight(NftWeightInfoOf::<C>::create_item(input.data.len()))?;7111572 match pallet_nft::Module::<C>::create_item_internal(116 pallet_nft::Module::<C>::create_item_internal(73 env.ext().address().clone(),117 env.ext().address().clone(),74 input.collection_id,118 input.collection_id,75 input.owner,119 input.owner,76 input.data,120 input.data,77 ) {121 )?;122 78 Ok(_) => Ok(RetVal::Converging(func_id)),123 Ok(RetVal::Converging(0))79 _ => Err(DispatchError::Other("CreateItem error"))80 }81 },124 },125 2 => {126 // Create multiple items127 let mut env = env.buf_in_buf_out();128 let input: NFTExtCreateMultipleItems<E> = env.read_as()?;129 env.charge_weight(NftWeightInfoOf::<C>::create_item(130 input.data.iter()131 .map(|i| i.len())132 .sum()133 ))?;134135 let collection = pallet_nft::Module::<C>::get_collection(input.collection_id)?;136137 pallet_nft::Module::<C>::create_multiple_items_internal(138 env.ext().address().clone(),139 &collection,140 input.owner,141 input.data,142 )?;143 144 Ok(RetVal::Converging(0))145 },146 3 => {147 // Approve148 let mut env = env.buf_in_buf_out();149 let input: NFTExtApprove<E> = env.read_as()?;150 env.charge_weight(NftWeightInfoOf::<C>::approve())?;151152 let collection = pallet_nft::Module::<C>::get_collection(input.collection_id)?;153154 pallet_nft::Module::<C>::approve_internal(155 env.ext().address().clone(),156 input.spender,157 &collection,158 input.item_id,159 input.amount,160 )?;161162 Ok(RetVal::Converging(0))163 },164 4 => {165 // Transfer from166 let mut env = env.buf_in_buf_out();167 let input: NFTExtTransferFrom<E> = env.read_as()?;168 env.charge_weight(NftWeightInfoOf::<C>::transfer_from())?;169170 let collection = pallet_nft::Module::<C>::get_collection(input.collection_id)?;171172 pallet_nft::Module::<C>::transfer_from_internal(173 env.ext().address().clone(),174 input.owner,175 input.recipient,176 &collection,177 input.item_id,178 input.amount179 )?;180181 Ok(RetVal::Converging(0))182 },82 _ => {183 5 => {184 // Set variable metadata185 let mut env = env.buf_in_buf_out();186 let input: NFTExtSetVariableMetaData = env.read_as()?;187 env.charge_weight(NftWeightInfoOf::<C>::set_variable_meta_data())?;188189 let collection = pallet_nft::Module::<C>::get_collection(input.collection_id)?;19083 panic!("Passed unknown func_id to test chain extension: {}", func_id);191 pallet_nft::Module::<C>::set_variable_meta_data_internal(192 env.ext().address().clone(),193 &collection,194 input.item_id,195 input.data,196 )?;197198 Ok(RetVal::Converging(0))84 }199 },200 6 => {201 // Toggle whitelist202 let mut env = env.buf_in_buf_out();203 let input: NFTExtToggleWhiteList<E> = env.read_as()?;204 env.charge_weight(NftWeightInfoOf::<C>::add_to_white_list())?;205206 let collection = pallet_nft::Module::<C>::get_collection(input.collection_id)?;207208 pallet_nft::Module::<C>::toggle_white_list_internal(209 &env.ext().address().clone(),210 &collection,211 &input.address,212 input.whitelisted,213 )?;214215 Ok(RetVal::Converging(0))216 }217 _ => {218 Err(DispatchError::Other("unknown chain_extension func_id"))219 }85 }220 }86 }221 }87}222}smart_contracs/transfer/Cargo.tomldiffbeforeafterboth4authors = ["[Greg Zaitsev] <[your_email]>"]4authors = ["[Greg Zaitsev] <[your_email]>"]5edition = "2018"5edition = "2018"67[workspace]687[dependencies]9[dependencies]8ink_primitives = { git = "https://github.com/usetech-llc/ink", branch = "unique", default-features = false }10ink_primitives = { default-features = false }9ink_metadata = { git = "https://github.com/usetech-llc/ink", branch = "unique", default-features = false, features = ["derive"], optional = true }11ink_metadata = { default-features = false, features = ["derive"], optional = true }10ink_env = { git = "https://github.com/usetech-llc/ink", branch = "unique", default-features = false }12ink_env = { default-features = false }11ink_storage = { git = "https://github.com/usetech-llc/ink", branch = "unique", default-features = false }13ink_storage = { default-features = false }12ink_lang = { git = "https://github.com/usetech-llc/ink", branch = "unique", default-features = false }14ink_lang = { default-features = false }131514scale = { package = "parity-scale-codec", version = "1.3", default-features = false, features = ["derive"] }16scale = { package = "parity-scale-codec", version = "2.1.1", default-features = false, features = ["derive"] }15scale-info = { version = "0.4.1", default-features = false, features = ["derive"], optional = true }17scale-info = { version = "0.6.0", default-features = false, features = ["derive"] }161817[lib]19[lib]18name = "nft_transfer"20name = "nft_transfer"28 "ink_metadata/std",30 "ink_metadata/std",29 "ink_env/std",31 "ink_env/std",30 "ink_storage/std",32 "ink_storage/std",33 "ink_lang/std",31 "ink_primitives/std",34 "ink_primitives/std",32 "scale/std",35 "scale/std",33 "scale-info/std",36 "scale-info/std",smart_contracs/transfer/lib.rsdiffbeforeafterboth1#![cfg_attr(not(feature = "std"), no_std)]1#![cfg_attr(not(feature = "std"), no_std)]2extern crate alloc;3use alloc::vec::Vec;243use ink_lang as ink;5use ink_lang as ink;4use ink_env::{Environment, DefaultEnvironment};6use ink_env::{Environment, DefaultEnvironment};36 }38 }37}39}4041#[derive(scale::Encode, scale::Decode, scale_info::TypeInfo)]42pub enum CreateItemData {43 Nft {44 const_data: Vec<u8>,45 variable_data: Vec<u8>,46 },47 Fungible {48 value: u128,49 },50 ReFungible {51 const_data: Vec<u8>,52 variable_data: Vec<u8>,53 pieces: u128,54 },55}5657type DefaultAccountId = <DefaultEnvironment as Environment>::AccountId;385839#[ink::chain_extension]59#[ink::chain_extension]40pub trait NftChainExtension {60pub trait NftChainExtension {43 /// Transfer one NFT token from sender63 /// Transfer one NFT token from sender44 ///64 ///45 #[ink(extension = 0, returns_result = false)]65 #[ink(extension = 0, returns_result = false)]46 fn transfer(recipient: <DefaultEnvironment as Environment>::AccountId, collection_id: u32, token_id: u32, amount: u128);66 fn transfer(recipient: DefaultAccountId, collection_id: u32, token_id: u32, amount: u128);67 #[ink(extension = 1, returns_result = false)]68 fn create_item(owner: DefaultAccountId, collection_id: u32, data: CreateItemData);69 #[ink(extension = 2, returns_result = false)]70 fn create_multiple_items(owner: DefaultAccountId, collection_id: u32, data: Vec<CreateItemData>);71 #[ink(extension = 3, returns_result = false)]72 fn approve(spender: DefaultAccountId, collection_id: u32, item_id: u32, amount: u128);73 #[ink(extension = 4, returns_result = false)]74 fn transfer_from(owner: DefaultAccountId, recipient: DefaultAccountId, collection_id: u32, item_id: u32, amount: u128);75 #[ink(extension = 5, returns_result = false)]76 fn set_variable_meta_data(collection_id: u32, item_id: u32, data: Vec<u8>);77 #[ink(extension = 6, returns_result = false)]78 fn toggle_white_list(collection_id: u32, address: DefaultAccountId, whitelisted: bool);47}79}488049#[ink::contract(env = crate::NftEnvironment)]81#[ink::contract(env = crate::NftEnvironment, dynamic_storage_allocator = true)]50mod nft_transfer {82mod nft_transfer {83 use alloc::vec::Vec;84 // use ink_storage::Vec;85 use crate::CreateItemData;518652 #[ink(storage)]87 #[ink(storage)]53 pub struct NftTransfer {88 pub struct NftTransfer {69 .extension()104 .extension()70 .transfer(recipient, collection_id, token_id, amount);105 .transfer(recipient, collection_id, token_id, amount);71 }106 }107 #[ink(message)]108 pub fn create_item(&mut self, recipient: AccountId, collection_id: u32, data: CreateItemData) {109 let _ = self.env()110 .extension()111 .create_item(recipient, collection_id, data);112 }113 #[ink(message)]114 pub fn create_multiple_items(&mut self, owner: AccountId, collection_id: u32, data: Vec<CreateItemData>) {115 let _ = self.env()116 .extension()117 .create_multiple_items(owner, collection_id, data);118 }119 #[ink(message)]120 pub fn approve(&mut self, spender: AccountId, collection_id: u32, item_id: u32, amount: u128) {121 let _ = self.env()122 .extension()123 .approve(spender, collection_id, item_id, amount);124 }125 #[ink(message)]126 pub fn transfer_from(&mut self, owner: AccountId, recipient: AccountId, collection_id: u32, item_id: u32, amount: u128) {127 let _ = self.env()128 .extension()129 .transfer_from(owner, recipient, collection_id, item_id, amount);130 }131 #[ink(message)]132 pub fn set_variable_meta_data(&mut self, collection_id: u32, item_id: u32, data: Vec<u8>) {133 let _ = self.env()134 .extension()135 .set_variable_meta_data(collection_id, item_id, data);136 }137 #[ink(message)]138 pub fn toggle_white_list(&mut self, collection_id: u32, address: AccountId, whitelisted: bool) {139 let _ = self.env()140 .extension()141 .toggle_white_list(collection_id, address, whitelisted);142 }7214373 }144 }74145tests/src/contracts.test.tsdiffbeforeafterboth16} from "./util/contracthelpers";16} from "./util/contracthelpers";171718import {18import {19 addToWhiteListExpectSuccess,20 approveExpectSuccess,19 createCollectionExpectSuccess,21 createCollectionExpectSuccess,20 createItemExpectSuccess,22 createItemExpectSuccess,23 enablePublicMintingExpectSuccess,24 enableWhiteListExpectSuccess,21 getGenericResult25 getGenericResult,26 isWhitelisted,27 transferFromExpectSuccess22} from "./util/helpers";28} from "./util/helpers";2329243025chai.use(chaiAsPromised);31chai.use(chaiAsPromised);26const expect = chai.expect;32const expect = chai.expect;273328const value = 0;34const value = 0;29const gasLimit = 3000n * 1000000n;35const gasLimit = 9000n * 1000000n;30const marketContractAddress = '5CYN9j3YvRkqxewoxeSvRbhAym4465C57uMmX5j4yz99L5H6';36const marketContractAddress = '5CYN9j3YvRkqxewoxeSvRbhAym4465C57uMmX5j4yz99L5H6';3738describe('Contracts', () => {39 it(`Can deploy smart contract Flipper, instantiate it and call it's get and flip messages.`, async () => {40 await usingApi(async api => {41 const [contract, deployer] = await deployFlipper(api);42 const initialGetResponse = await getFlipValue(contract, deployer);4344 const bob = privateKey("//Bob");45 const flip = contract.tx.flip(value, gasLimit);46 await submitTransactionAsync(bob, flip);4748 const afterFlipGetResponse = await getFlipValue(contract, deployer);49 expect(afterFlipGetResponse).not.to.be.eq(initialGetResponse, 'Flipping should change value.');50 });51 });5253 it('Can initialize contract instance', async () => {54 await usingApi(async (api) => {55 const metadata = JSON.parse(fs.readFileSync('./src/flipper/metadata.json').toString('utf-8'));56 const abi = new Abi(metadata);57 const newContractInstance = new Contract(api, abi, marketContractAddress);58 expect(newContractInstance).to.have.property('address');59 expect(newContractInstance.address.toString()).to.equal(marketContractAddress);60 });61 });62});316332describe('Contracts', () => {64describe.only('Chain extensions', () => {33 it(`Can deploy smart contract Flipper, instantiate it and call it's get and flip messages.`, async () => {65 it('Transfer CE', async () => {34 await usingApi(async api => {66 await usingApi(async api => {67 const alice = privateKey("//Alice");68 const bob = privateKey("//Bob");6970 // Prep work71 const collectionId = await createCollectionExpectSuccess();72 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');35 const [contract, deployer] = await deployFlipper(api);73 const [contract, deployer] = await deployTransferContract(api);36 const initialGetResponse = await getFlipValue(contract, deployer);74 const changeAdminTx = api.tx.nft.addCollectionAdmin(collectionId, contract.address);75 await submitTransactionAsync(alice, changeAdminTx);377638 const bob = privateKey("//Bob");77 const tokenBefore: any = (await api.query.nft.nftItemList(collectionId, tokenId) as any).unwrap();78 79 // Transfer39 const flip = contract.tx.flip(value, gasLimit);80 const transferTx = contract.tx.transfer(value, gasLimit, bob.address, collectionId, tokenId, 1);40 await submitTransactionAsync(bob, flip);81 const events = await submitTransactionAsync(alice, transferTx);4182 const result = getGenericResult(events);42 const afterFlipGetResponse = await getFlipValue(contract, deployer);83 const tokenAfter: any = (await api.query.nft.nftItemList(collectionId, tokenId) as any).unwrap();8485 // tslint:disable-next-line:no-unused-expression86 expect(result.success).to.be.true;43 expect(afterFlipGetResponse).not.to.be.eq(initialGetResponse, 'Flipping should change value.');87 expect(tokenBefore.Owner.toString()).to.be.equal(alice.address);88 expect(tokenAfter.Owner.toString()).to.be.equal(bob.address);44 });89 });45 });90 });9192 it('Mint CE', async () => {93 await usingApi(async api => {94 const alice = privateKey('//Alice');95 const bob = privateKey('//Bob');9697 const collectionId = await createCollectionExpectSuccess();98 const [contract, deployer] = await deployTransferContract(api);99 await enablePublicMintingExpectSuccess(alice, collectionId);100 await enableWhiteListExpectSuccess(alice, collectionId);101 await addToWhiteListExpectSuccess(alice, collectionId, contract.address);102 await addToWhiteListExpectSuccess(alice, collectionId, bob.address);103104 const transferTx = contract.tx.createItem(value, gasLimit, bob.address, collectionId, { Nft: {const_data: '0x010203', variable_data: '0x020304' }});105 const events = await submitTransactionAsync(alice, transferTx);106 const result = getGenericResult(events);107 expect(result.success).to.be.true;108109 const tokensAfter: any = (await api.query.nft.nftItemList.entries(collectionId) as any).map((kv: any) => kv[1].toJSON());110 expect(tokensAfter).to.be.deep.equal([111 {112 Owner: bob.address,113 ConstData: '0x010203',114 VariableData: '0x020304',115 },116 ]);117 });118 });4611947 it('Can initialize contract instance', async () => {120 it('Bulk mint CE', async () => {48 await usingApi(async (api) => {121 await usingApi(async api => {122 const alice = privateKey('//Alice');123 const bob = privateKey('//Bob');124125 const collectionId = await createCollectionExpectSuccess();126 const [contract, deployer] = await deployTransferContract(api);127 await enablePublicMintingExpectSuccess(alice, collectionId);128 await enableWhiteListExpectSuccess(alice, collectionId);129 await addToWhiteListExpectSuccess(alice, collectionId, contract.address);130 await addToWhiteListExpectSuccess(alice, collectionId, bob.address);13149 const metadata = JSON.parse(fs.readFileSync('./src/flipper/metadata.json').toString('utf-8'));132 const transferTx = contract.tx.createMultipleItems(value, gasLimit, bob.address, collectionId, [133 { Nft: { const_data: '0x010203', variable_data: '0x020304' } },134 { Nft: { const_data: '0x010204', variable_data: '0x020305' } },135 { Nft: { const_data: '0x010205', variable_data: '0x020306' } }136 ]);50 const abi = new Abi(metadata);137 const events = await submitTransactionAsync(alice, transferTx);51 const newContractInstance = new Contract(api, abi, marketContractAddress);138 const result = getGenericResult(events);52 expect(newContractInstance).to.have.property('address');139 expect(result.success).to.be.true;14053 expect(newContractInstance.address.toString()).to.equal(marketContractAddress);141 const tokensAfter: any = (await api.query.nft.nftItemList.entries(collectionId) as any)142 .map((kv: any) => kv[1].toJSON())143 .sort((a: any, b: any) => a.ConstData.localeCompare(b.ConstData));144 expect(tokensAfter).to.be.deep.equal([145 {146 Owner: bob.address,147 ConstData: '0x010203',148 VariableData: '0x020304',149 },150 {151 Owner: bob.address,152 ConstData: '0x010204',153 VariableData: '0x020305',154 },155 {156 Owner: bob.address,157 ConstData: '0x010205',158 VariableData: '0x020306',159 },160 ]);54 });161 });55 });162 });163164 it('Approve CE', async () => {165 await usingApi(async api => {166 const alice = privateKey('//Alice');167 const bob = privateKey('//Bob');168 const charlie = privateKey('//Charlie');169170 const collectionId = await createCollectionExpectSuccess();171 const [contract, deployer] = await deployTransferContract(api);172 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', contract.address);173174 const transferTx = contract.tx.approve(value, gasLimit, bob.address, collectionId, tokenId, 1);175 const events = await submitTransactionAsync(alice, transferTx);176 const result = getGenericResult(events);177 expect(result.success).to.be.true;178179 await transferFromExpectSuccess(collectionId, tokenId, bob, contract.address.toString(), charlie, 1, 'NFT');180 });181 });5618257 it('Can transfer NFT using smart contract.', async () => {183 it('TransferFrom CE', async () => {58 await usingApi(async api => {184 await usingApi(async api => {59 const alice = privateKey("//Alice");185 const alice = privateKey('//Alice');60 const bob = privateKey("//Bob");186 const bob = privateKey('//Bob');6162 // Prep work63 const collectionId = await createCollectionExpectSuccess();187 const charlie = privateKey('//Charlie');18864 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');189 const collectionId = await createCollectionExpectSuccess();65 const [contract, deployer] = await deployTransferContract(api);190 const [contract, deployer] = await deployTransferContract(api);66 const tokenBefore: any = (await api.query.nft.nftItemList(collectionId, tokenId) as any).unwrap();191 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address);67 192 await approveExpectSuccess(collectionId, tokenId, bob, contract.address.toString(), 1);68 // Transfer19369 const transferTx = contract.tx.transfer(value, gasLimit, bob.address, collectionId, tokenId, 1);194 const transferTx = contract.tx.transferFrom(value, gasLimit, bob.address, charlie.address, collectionId, tokenId, 1);70 const events = await submitTransactionAsync(alice, transferTx);195 const events = await submitTransactionAsync(alice, transferTx);71 const result = getGenericResult(events);196 const result = getGenericResult(events);197 expect(result.success).to.be.true;19872 const tokenAfter: any = (await api.query.nft.nftItemList(collectionId, tokenId) as any).unwrap();199 const token: any = (await api.query.nft.nftItemList(collectionId, tokenId) as any).unwrap()7374 // tslint:disable-next-line:no-unused-expression75 expect(result.success).to.be.true;76 expect(tokenBefore.Owner.toString()).to.be.equal(alice.address);200 expect(token.Owner.toString()).to.be.equal(charlie.address);77 expect(tokenAfter.Owner.toString()).to.be.equal(bob.address);78 });201 });79 });202 });203204 it('SetVariableMetaData CE', async () => {205 await usingApi(async api => {206 const alice = privateKey('//Alice');207208 const collectionId = await createCollectionExpectSuccess();209 const [contract, deployer] = await deployTransferContract(api);210 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', contract.address);211212 const transferTx = contract.tx.setVariableMetaData(value, gasLimit, collectionId, tokenId, '0x121314');213 const events = await submitTransactionAsync(alice, transferTx);214 const result = getGenericResult(events);215 expect(result.success).to.be.true;216217 const token: any = (await api.query.nft.nftItemList(collectionId, tokenId) as any).unwrap()218 expect(token.VariableData.toString()).to.be.equal('0x121314');219 });220 });221222 it('ToggleWhiteList CE', async () => {223 await usingApi(async api => {224 const alice = privateKey('//Alice');225 const bob = privateKey('//Bob');226227 const collectionId = await createCollectionExpectSuccess();228 const [contract, deployer] = await deployTransferContract(api);229 const changeAdminTx = api.tx.nft.addCollectionAdmin(collectionId, contract.address);230 await submitTransactionAsync(alice, changeAdminTx); 231232 expect(await isWhitelisted(collectionId, bob.address)).to.be.false;233234 {235 const transferTx = contract.tx.toggleWhiteList(value, gasLimit, collectionId, bob.address, true);236 const events = await submitTransactionAsync(alice, transferTx);237 const result = getGenericResult(events);238 expect(result.success).to.be.true;239240 expect(await isWhitelisted(collectionId, bob.address)).to.be.true;241 }242 {243 const transferTx = contract.tx.toggleWhiteList(value, gasLimit, collectionId, bob.address, false);244 const events = await submitTransactionAsync(alice, transferTx);245 const result = getGenericResult(events);246 expect(result.success).to.be.true;247248 expect(await isWhitelisted(collectionId, bob.address)).to.be.false;249 }250 });251 });80});252});81253tests/src/transfer_contract/metadata.jsondiffbeforeafterboth1{1{2 "metadataVersion": "0.1.0",2 "metadataVersion": "0.1.0",3 "source": {3 "source": {4 "hash": "0xfbbc0729acefed9d99f93f6cbb751d12e317958fd0fe183f5781329b37f1bf6e",4 "hash": "0xc6c3f47adeafe86d1674ed72c7179605787842f2f05a2d7da0dbabf3c4fa1aa8",5 "language": "ink! 3.0.0-rc2",5 "language": "ink! 3.0.0-rc3",6 "compiler": "rustc 1.51.0-nightly"6 "compiler": "rustc 1.52.0-nightly"7 },7 },8 "contract": {8 "contract": {9 "name": "nft_transfer",9 "name": "nft_transfer",24 "name": [24 "name": [25 "default"25 "default"26 ],26 ],27 "selector": "0x6a3712e2"27 "selector": "0xed4b9d1b"28 }28 }29 ],29 ],30 "docs": [],30 "docs": [],78 ],78 ],79 "payable": false,79 "payable": false,80 "returnType": null,80 "returnType": null,81 "selector": "0xfae3a09d"81 "selector": "0x84a15da1"82 }82 },83 {84 "args": [85 {86 "name": "recipient",87 "type": {88 "displayName": [89 "AccountId"90 ],91 "type": 192 }93 },94 {95 "name": "collection_id",96 "type": {97 "displayName": [98 "u32"99 ],100 "type": 4101 }102 },103 {104 "name": "data",105 "type": {106 "displayName": [107 "CreateItemData"108 ],109 "type": 6110 }111 }112 ],113 "docs": [],114 "mutates": true,115 "name": [116 "create_item"117 ],118 "payable": false,119 "returnType": null,120 "selector": "0xd7c3f083"121 },122 {123 "args": [124 {125 "name": "owner",126 "type": {127 "displayName": [128 "AccountId"129 ],130 "type": 1131 }132 },133 {134 "name": "collection_id",135 "type": {136 "displayName": [137 "u32"138 ],139 "type": 4140 }141 },142 {143 "name": "data",144 "type": {145 "displayName": [146 "Vec"147 ],148 "type": 8149 }150 }151 ],152 "docs": [],153 "mutates": true,154 "name": [155 "create_multiple_items"156 ],157 "payable": false,158 "returnType": null,159 "selector": "0x15f9a1eb"160 },161 {162 "args": [163 {164 "name": "spender",165 "type": {166 "displayName": [167 "AccountId"168 ],169 "type": 1170 }171 },172 {173 "name": "collection_id",174 "type": {175 "displayName": [176 "u32"177 ],178 "type": 4179 }180 },181 {182 "name": "item_id",183 "type": {184 "displayName": [185 "u32"186 ],187 "type": 4188 }189 },190 {191 "name": "amount",192 "type": {193 "displayName": [194 "u128"195 ],196 "type": 5197 }198 }199 ],200 "docs": [],201 "mutates": true,202 "name": [203 "approve"204 ],205 "payable": false,206 "returnType": null,207 "selector": "0x681266a0"208 },209 {210 "args": [211 {212 "name": "owner",213 "type": {214 "displayName": [215 "AccountId"216 ],217 "type": 1218 }219 },220 {221 "name": "recipient",222 "type": {223 "displayName": [224 "AccountId"225 ],226 "type": 1227 }228 },229 {230 "name": "collection_id",231 "type": {232 "displayName": [233 "u32"234 ],235 "type": 4236 }237 },238 {239 "name": "item_id",240 "type": {241 "displayName": [242 "u32"243 ],244 "type": 4245 }246 },247 {248 "name": "amount",249 "type": {250 "displayName": [251 "u128"252 ],253 "type": 5254 }255 }256 ],257 "docs": [],258 "mutates": true,259 "name": [260 "transfer_from"261 ],262 "payable": false,263 "returnType": null,264 "selector": "0x0b396f18"265 },266 {267 "args": [268 {269 "name": "collection_id",270 "type": {271 "displayName": [272 "u32"273 ],274 "type": 4275 }276 },277 {278 "name": "item_id",279 "type": {280 "displayName": [281 "u32"282 ],283 "type": 4284 }285 },286 {287 "name": "data",288 "type": {289 "displayName": [290 "Vec"291 ],292 "type": 7293 }294 }295 ],296 "docs": [],297 "mutates": true,298 "name": [299 "set_variable_meta_data"300 ],301 "payable": false,302 "returnType": null,303 "selector": "0xb0b26da2"304 },305 {306 "args": [307 {308 "name": "collection_id",309 "type": {310 "displayName": [311 "u32"312 ],313 "type": 4314 }315 },316 {317 "name": "address",318 "type": {319 "displayName": [320 "AccountId"321 ],322 "type": 1323 }324 },325 {326 "name": "whitelisted",327 "type": {328 "displayName": [329 "bool"330 ],331 "type": 9332 }333 }334 ],335 "docs": [],336 "mutates": true,337 "name": [338 "toggle_white_list"339 ],340 "payable": false,341 "returnType": null,342 "selector": "0x98574dac"343 }83 ]344 ]84 },345 },85 "storage": {346 "storage": {93 "composite": {354 "composite": {94 "fields": [355 "fields": [95 {356 {96 "type": 2357 "type": 2,358 "typeName": "[u8; 32]"97 }359 }98 ]360 ]99 }361 }126 "def": {388 "def": {127 "primitive": "u128"389 "primitive": "u128"128 }390 }129 }391 },392 {393 "def": {394 "variant": {395 "variants": [396 {397 "fields": [398 {399 "name": "const_data",400 "type": 7,401 "typeName": "Vec<u8>"402 },403 {404 "name": "variable_data",405 "type": 7,406 "typeName": "Vec<u8>"407 }408 ],409 "name": "Nft"410 },411 {412 "fields": [413 {414 "name": "value",415 "type": 5,416 "typeName": "u128"417 }418 ],419 "name": "Fungible"420 },421 {422 "fields": [423 {424 "name": "const_data",425 "type": 7,426 "typeName": "Vec<u8>"427 },428 {429 "name": "variable_data",430 "type": 7,431 "typeName": "Vec<u8>"432 },433 {434 "name": "pieces",435 "type": 5,436 "typeName": "u128"437 }438 ],439 "name": "ReFungible"440 }441 ]442 }443 },444 "path": [445 "nft_transfer",446 "CreateItemData"447 ]448 },449 {450 "def": {451 "sequence": {452 "type": 3453 }454 }455 },456 {457 "def": {458 "sequence": {459 "type": 6460 }461 }462 },463 {464 "def": {465 "primitive": "bool"466 }467 }130 ]468 ]131}469}tests/src/transfer_contract/nft_transfer.wasmdiffbeforeafterbothbinary blob — no preview
tests/src/util/helpers.tsdiffbeforeafterboth636636637export async function637export async function638approveExpectSuccess(collectionId: number,638approveExpectSuccess(collectionId: number,639 tokenId: number, owner: IKeyringPair, approved: IKeyringPair, amount: number | bigint = 1) {639 tokenId: number, owner: IKeyringPair, approved: IKeyringPair | string, amount: number | bigint = 1) {640 if (typeof approved !== 'string')641 approved = approved.address;640 await usingApi(async (api: ApiPromise) => {642 await usingApi(async (api: ApiPromise) => {641 const allowanceBefore =643 const allowanceBefore =642 await api.query.nft.allowances(collectionId, [tokenId, owner.address, approved.address]) as unknown as BN;644 await api.query.nft.allowances(collectionId, [tokenId, owner.address, approved] as any) as unknown as BN;643 const approveNftTx = await api.tx.nft.approve(approved.address, collectionId, tokenId, amount);645 const approveNftTx = await api.tx.nft.approve(approved, collectionId, tokenId, amount);644 const events = await submitTransactionAsync(owner, approveNftTx);646 const events = await submitTransactionAsync(owner, approveNftTx);645 const result = getCreateItemResult(events);647 const result = getCreateItemResult(events);646 // tslint:disable-next-line:no-unused-expression648 // tslint:disable-next-line:no-unused-expression647 expect(result.success).to.be.true;649 expect(result.success).to.be.true;648 const allowanceAfter =650 const allowanceAfter =649 await api.query.nft.allowances(collectionId, [tokenId, owner.address, approved.address]) as unknown as BN;651 await api.query.nft.allowances(collectionId, [tokenId, owner.address, approved] as any) as unknown as BN;650 expect(allowanceAfter.sub(allowanceBefore).toString()).to.be.equal(amount.toString());652 expect(allowanceAfter.sub(allowanceBefore).toString()).to.be.equal(amount.toString());651 });653 });652}654}655transferFromExpectSuccess(collectionId: number,657transferFromExpectSuccess(collectionId: number,656 tokenId: number,658 tokenId: number,657 accountApproved: IKeyringPair,659 accountApproved: IKeyringPair,658 accountFrom: IKeyringPair,660 accountFrom: IKeyringPair | string,659 accountTo: IKeyringPair,661 accountTo: IKeyringPair,660 value: number | bigint = 1,662 value: number | bigint = 1,661 type: string = 'NFT') {663 type: string = 'NFT') {664 if (typeof accountFrom !== 'string')665 accountFrom = accountFrom.address;662 await usingApi(async (api: ApiPromise) => {666 await usingApi(async (api: ApiPromise) => {663 let balanceBefore = new BN(0);667 let balanceBefore = new BN(0);664 if (type === 'Fungible') {668 if (type === 'Fungible') {665 balanceBefore = await api.query.nft.balance(collectionId, accountTo.address) as unknown as BN;669 balanceBefore = await api.query.nft.balance(collectionId, accountTo.address) as unknown as BN;666 }670 }667 const transferFromTx = await api.tx.nft.transferFrom(671 const transferFromTx = await api.tx.nft.transferFrom(668 accountFrom.address, accountTo.address, collectionId, tokenId, value);672 accountFrom, accountTo.address, collectionId, tokenId, value);669 const events = await submitTransactionAsync(accountApproved, transferFromTx);673 const events = await submitTransactionAsync(accountApproved, transferFromTx);670 const result = getCreateItemResult(events);674 const result = getCreateItemResult(events);671 // tslint:disable-next-line:no-unused-expression675 // tslint:disable-next-line:no-unused-expression849}853}850854851export async function createItemExpectSuccess(855export async function createItemExpectSuccess(852 sender: IKeyringPair, collectionId: number, createMode: string, owner: string = '') {856 sender: IKeyringPair, collectionId: number, createMode: string, owner: string | AccountId = sender.address) {853 let newItemId: number = 0;857 let newItemId: number = 0;854 await usingApi(async (api) => {858 await usingApi(async (api) => {855 const AItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString(), 10);859 const AItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString(), 10);856 const Aitem: any = (await api.query.nft.fungibleItemList(collectionId, owner)).toJSON();860 const Aitem: any = (await api.query.nft.fungibleItemList(collectionId, owner)).toJSON();857 const AItemBalance = new BigNumber(Aitem.Value);861 const AItemBalance = new BigNumber(Aitem.Value);858859 if (owner === '') {860 owner = sender.address;861 }862862863 let tx;863 let tx;864 if (createMode === 'Fungible') {864 if (createMode === 'Fungible') {887 }887 }888 expect(collectionId).to.be.equal(result.collectionId);888 expect(collectionId).to.be.equal(result.collectionId);889 expect(BItemCount).to.be.equal(result.itemId);889 expect(BItemCount).to.be.equal(result.itemId);890 expect(owner).to.be.equal(result.recipient);890 expect(owner.toString()).to.be.equal(result.recipient);891 newItemId = result.itemId;891 newItemId = result.itemId;892 });892 });893 return newItemId;893 return newItemId;974 return whitelisted;974 return whitelisted;975}975}976976977export async function addToWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number, address: string) {977export async function addToWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number, address: string | AccountId) {978 await usingApi(async (api) => {978 await usingApi(async (api) => {979979980 const whiteListedBefore = (await api.query.nft.whiteList(collectionId, address)).toJSON();980 const whiteListedBefore = (await api.query.nft.whiteList(collectionId, address)).toJSON();