git.delta.rocks / unique-network / refs/commits / dc9fdbfccd6a

difftreelog

Merge pull request #148 from usetech-llc/feature/NFTPAR-373_chain_extensions

Greg Zaitsev2021-06-17parents: #cbd9f34 #e5c09cb.patch.diff
in: master
Chain extensions

10 files changed

modified.devcontainer/Dockerfilediffbeforeafterboth
3RUN 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 clang
55
6RUN 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 /usr
8
6USER vscode9USER vscode
710
8RUN 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-contract
19
modifiedCargo.lockdiffbeforeafterboth
5392 "sp-std",5392 "sp-std",
5393]5393]
5394
5395[[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]
54095394
5410[[package]]5395[[package]]
5411name = "pallet-scheduler"5396name = "pallet-scheduler"
5428 "substrate-test-utils",5413 "substrate-test-utils",
5429]5414]
5415
5416[[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]
54305430
5431[[package]]5431[[package]]
5432name = "pallet-session"5432name = "pallet-session"
modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
567 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)?;
569
569 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 )?;
576
573 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)?;
598
594 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 )?;
597605
598 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 {
984992
985 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)?;
987995
988 Self::token_exists(&target_collection, item_id)?;996 Self::approve_internal(sender, spender, &collection, item_id, amount)?;
989997
990 // Transfer permissions check
991 let bypasses_limits = target_collection.limits.owner_can_transfer &&
997 let allowance_limit = if bypasses_limits {
998 None
999 } 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 };
1008
1009 if target_collection.access == AccessMode::WhiteList {
1010 Self::check_white_list(&target_collection, &sender)?;
1011 Self::check_white_list(&target_collection, &spender)?;
1012 }
1013
1014 let allowance: u128 = amount
1015 .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);
1021
1022 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 {
10481023
1049 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)?;
10511026
1052 // Check approval
1053 let approval: u128 = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));
1054
1055 // Limits check
1056 Self::is_correct_transfer(&target_collection, &recipient)?;1027 Self::transfer_from_internal(sender, from, recipient, &collection, item_id, value)?;
10571028
1058 // 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>::NoPermission
1066 );
1067
1068 if target_collection.access == AccessMode::WhiteList {
1069 Self::check_white_list(&target_collection, &sender)?;
1070 Self::check_white_list(&target_collection, &recipient)?;
1071 }
1072
1073 // Reduce approval by transferred amount or remove if remaining approval drops to 0
1074 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 }
1080
1081 match target_collection.mode
1082 {
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 };
1088
1089 Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, from, recipient, value));
1090 Ok(())1029 Ok(())
1091 }1030 }
10921031
1127 ) -> 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)?;
1070
1131 Self::token_exists(&target_collection, item_id)?;1071 Self::set_variable_meta_data_internal(sender, &collection, item_id, data)?;
1132
1133 ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);
1134
1135 // Modify permissions check
1136 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);
1139
1140 match target_collection.mode
1141 {
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 };
11471072
1148 Ok(())1073 Ok(())
1149 }1074 }
1516 Ok(())1441 Ok(())
1517 }1442 }
15181443
1444 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)?;
1452
1453 // Transfer permissions check
1454 let bypasses_limits = collection.limits.owner_can_transfer &&
1455 Self::is_owner_or_admin_permissions(
1456 &collection,
1457 sender.clone(),
1458 );
1459
1460 let allowance_limit = if bypasses_limits {
1461 None
1462 } 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 };
1471
1472 if collection.access == AccessMode::WhiteList {
1473 Self::check_white_list(&collection, &sender)?;
1474 Self::check_white_list(&collection, &spender)?;
1475 }
1476
1477 let allowance: u128 = amount
1478 .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);
1484
1485 Self::deposit_event(RawEvent::Approved(collection.id, item_id, sender, spender, allowance));
1486 Ok(())
1487 }
1488
1489 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 approval
1498 let approval: u128 = <Allowances<T>>::get(collection.id, (item_id, &from, &sender));
1499
1500 // Limits check
1501 Self::is_correct_transfer(&collection, &recipient)?;
1502
1503 // Transfer permissions check
1504 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>::NoPermission
1511 );
1512
1513 if collection.access == AccessMode::WhiteList {
1514 Self::check_white_list(&collection, &sender)?;
1515 Self::check_white_list(&collection, &recipient)?;
1516 }
1517
1518 // Reduce approval by transferred amount or remove if remaining approval drops to 0
1519 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 }
1525
1526 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 };
1538
1539 Ok(())
1540 }
1541
1542 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)?;
1549
1550 ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);
1551
1552 // Modify permissions check
1553 ensure!(Self::is_item_owner(sender.clone(), &collection, item_id) ||
1554 Self::is_owner_or_admin_permissions(&collection, sender.clone()),
1555 Error::<T>::NoPermission);
1556
1557 match collection.mode
1558 {
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 };
1564
1565 Ok(())
1566 }
1567
1568 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)?;
1575
1576 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 }
1582
1583 Ok(())
1584 }
1585
1586 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())?;
1593
1594 if whitelisted {
1595 <WhiteList<T>>::insert(collection.id, address, true);
1596 } else {
1597 <WhiteList<T>>::remove(collection.id, address);
1598 }
1599
1600 Ok(())
1601 }
15191602
1520 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;
modifiedruntime/src/chain_extension.rsdiffbeforeafterboth
19pub use pallet_nft::*;19pub use pallet_nft::*;
20use nft_data_structs::*;20use nft_data_structs::*;
21
22use crate::Vec;
2123
22/// Create item parameters24/// Create item parameters
23#[derive(Debug, PartialEq, Encode, Decode)]25#[derive(Debug, PartialEq, Encode, Decode)]
36 pub amount: u128,38 pub amount: u128,
37}39}
40
41#[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}
47
48#[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}
55
56#[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}
64
65#[derive(Debug, PartialEq, Encode, Decode)]
66pub struct NFTExtSetVariableMetaData {
67 pub collection_id: u32,
68 pub item_id: u32,
69 pub data: Vec<u8>,
70}
71
72#[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}
3878
39/// The chain Extension of NFT pallet79/// The chain Extension of NFT pallet
40pub struct NFTExtension;80pub struct NFTExtension;
81
82pub type NftWeightInfoOf<C> = <C as pallet_nft::Config>::WeightInfo;
4183
42impl<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 where
45 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-codec
49 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())?;
5397
54 let collection = pallet_nft::Module::<C>::get_collection(input.collection_id)?;98 let collection = pallet_nft::Module::<C>::get_collection(input.collection_id)?;
5599
56 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 Item
69 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()))?;
71115
72 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 items
127 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 ))?;
134
135 let collection = pallet_nft::Module::<C>::get_collection(input.collection_id)?;
136
137 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 // Approve
148 let mut env = env.buf_in_buf_out();
149 let input: NFTExtApprove<E> = env.read_as()?;
150 env.charge_weight(NftWeightInfoOf::<C>::approve())?;
151
152 let collection = pallet_nft::Module::<C>::get_collection(input.collection_id)?;
153
154 pallet_nft::Module::<C>::approve_internal(
155 env.ext().address().clone(),
156 input.spender,
157 &collection,
158 input.item_id,
159 input.amount,
160 )?;
161
162 Ok(RetVal::Converging(0))
163 },
164 4 => {
165 // Transfer from
166 let mut env = env.buf_in_buf_out();
167 let input: NFTExtTransferFrom<E> = env.read_as()?;
168 env.charge_weight(NftWeightInfoOf::<C>::transfer_from())?;
169
170 let collection = pallet_nft::Module::<C>::get_collection(input.collection_id)?;
171
172 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.amount
179 )?;
180
181 Ok(RetVal::Converging(0))
182 },
82 _ => {183 5 => {
184 // Set variable metadata
185 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())?;
188
189 let collection = pallet_nft::Module::<C>::get_collection(input.collection_id)?;
190
83 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 )?;
197
198 Ok(RetVal::Converging(0))
84 }199 },
200 6 => {
201 // Toggle whitelist
202 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())?;
205
206 let collection = pallet_nft::Module::<C>::get_collection(input.collection_id)?;
207
208 pallet_nft::Module::<C>::toggle_white_list_internal(
209 &env.ext().address().clone(),
210 &collection,
211 &input.address,
212 input.whitelisted,
213 )?;
214
215 Ok(RetVal::Converging(0))
216 }
217 _ => {
218 Err(DispatchError::Other("unknown chain_extension func_id"))
219 }
85 }220 }
86 }221 }
87}222}
modifiedsmart_contracs/transfer/Cargo.tomldiffbeforeafterboth
4authors = ["[Greg Zaitsev] <[your_email]>"]4authors = ["[Greg Zaitsev] <[your_email]>"]
5edition = "2018"5edition = "2018"
6
7[workspace]
68
7[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 }
1315
14scale = { 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"] }
1618
17[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",
modifiedsmart_contracs/transfer/lib.rsdiffbeforeafterboth
1#![cfg_attr(not(feature = "std"), no_std)]1#![cfg_attr(not(feature = "std"), no_std)]
2extern crate alloc;
3use alloc::vec::Vec;
24
3use ink_lang as ink;5use ink_lang as ink;
4use ink_env::{Environment, DefaultEnvironment};6use ink_env::{Environment, DefaultEnvironment};
36 }38 }
37}39}
40
41#[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}
56
57type DefaultAccountId = <DefaultEnvironment as Environment>::AccountId;
3858
39#[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 sender
44 ///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}
4880
49#[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;
5186
52 #[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 }
72143
73 }144 }
74145
modifiedtests/src/contracts.test.tsdiffbeforeafterboth
16} from "./util/contracthelpers";16} from "./util/contracthelpers";
1717
18import {18import {
19 addToWhiteListExpectSuccess,
20 approveExpectSuccess,
19 createCollectionExpectSuccess,21 createCollectionExpectSuccess,
20 createItemExpectSuccess,22 createItemExpectSuccess,
23 enablePublicMintingExpectSuccess,
24 enableWhiteListExpectSuccess,
21 getGenericResult25 getGenericResult,
26 isWhitelisted,
27 transferFromExpectSuccess
22} from "./util/helpers";28} from "./util/helpers";
2329
2430
25chai.use(chaiAsPromised);31chai.use(chaiAsPromised);
26const expect = chai.expect;32const expect = chai.expect;
2733
28const value = 0;34const value = 0;
29const gasLimit = 3000n * 1000000n;35const gasLimit = 9000n * 1000000n;
30const marketContractAddress = '5CYN9j3YvRkqxewoxeSvRbhAym4465C57uMmX5j4yz99L5H6';36const marketContractAddress = '5CYN9j3YvRkqxewoxeSvRbhAym4465C57uMmX5j4yz99L5H6';
37
38describe('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);
43
44 const bob = privateKey("//Bob");
45 const flip = contract.tx.flip(value, gasLimit);
46 await submitTransactionAsync(bob, flip);
47
48 const afterFlipGetResponse = await getFlipValue(contract, deployer);
49 expect(afterFlipGetResponse).not.to.be.eq(initialGetResponse, 'Flipping should change value.');
50 });
51 });
52
53 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});
3163
32describe('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");
69
70 // Prep work
71 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);
3776
38 const bob = privateKey("//Bob");77 const tokenBefore: any = (await api.query.nft.nftItemList(collectionId, tokenId) as any).unwrap();
78
79 // Transfer
39 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();
84
85 // tslint:disable-next-line:no-unused-expression
86 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 });
91
92 it('Mint CE', async () => {
93 await usingApi(async api => {
94 const alice = privateKey('//Alice');
95 const bob = privateKey('//Bob');
96
97 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);
103
104 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;
108
109 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 });
46119
47 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');
124
125 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);
131
49 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;
140
53 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 });
163
164 it('Approve CE', async () => {
165 await usingApi(async api => {
166 const alice = privateKey('//Alice');
167 const bob = privateKey('//Bob');
168 const charlie = privateKey('//Charlie');
169
170 const collectionId = await createCollectionExpectSuccess();
171 const [contract, deployer] = await deployTransferContract(api);
172 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', contract.address);
173
174 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;
178
179 await transferFromExpectSuccess(collectionId, tokenId, bob, contract.address.toString(), charlie, 1, 'NFT');
180 });
181 });
56182
57 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');
61
62 // Prep work
63 const collectionId = await createCollectionExpectSuccess();187 const charlie = privateKey('//Charlie');
188
64 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 // Transfer193
69 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;
198
72 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()
73
74 // tslint:disable-next-line:no-unused-expression
75 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 });
203
204 it('SetVariableMetaData CE', async () => {
205 await usingApi(async api => {
206 const alice = privateKey('//Alice');
207
208 const collectionId = await createCollectionExpectSuccess();
209 const [contract, deployer] = await deployTransferContract(api);
210 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', contract.address);
211
212 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;
216
217 const token: any = (await api.query.nft.nftItemList(collectionId, tokenId) as any).unwrap()
218 expect(token.VariableData.toString()).to.be.equal('0x121314');
219 });
220 });
221
222 it('ToggleWhiteList CE', async () => {
223 await usingApi(async api => {
224 const alice = privateKey('//Alice');
225 const bob = privateKey('//Bob');
226
227 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);
231
232 expect(await isWhitelisted(collectionId, bob.address)).to.be.false;
233
234 {
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;
239
240 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;
247
248 expect(await isWhitelisted(collectionId, bob.address)).to.be.false;
249 }
250 });
251 });
80});252});
81253
modifiedtests/src/transfer_contract/metadata.jsondiffbeforeafterboth
1{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": 1
92 }
93 },
94 {
95 "name": "collection_id",
96 "type": {
97 "displayName": [
98 "u32"
99 ],
100 "type": 4
101 }
102 },
103 {
104 "name": "data",
105 "type": {
106 "displayName": [
107 "CreateItemData"
108 ],
109 "type": 6
110 }
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": 1
131 }
132 },
133 {
134 "name": "collection_id",
135 "type": {
136 "displayName": [
137 "u32"
138 ],
139 "type": 4
140 }
141 },
142 {
143 "name": "data",
144 "type": {
145 "displayName": [
146 "Vec"
147 ],
148 "type": 8
149 }
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": 1
170 }
171 },
172 {
173 "name": "collection_id",
174 "type": {
175 "displayName": [
176 "u32"
177 ],
178 "type": 4
179 }
180 },
181 {
182 "name": "item_id",
183 "type": {
184 "displayName": [
185 "u32"
186 ],
187 "type": 4
188 }
189 },
190 {
191 "name": "amount",
192 "type": {
193 "displayName": [
194 "u128"
195 ],
196 "type": 5
197 }
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": 1
218 }
219 },
220 {
221 "name": "recipient",
222 "type": {
223 "displayName": [
224 "AccountId"
225 ],
226 "type": 1
227 }
228 },
229 {
230 "name": "collection_id",
231 "type": {
232 "displayName": [
233 "u32"
234 ],
235 "type": 4
236 }
237 },
238 {
239 "name": "item_id",
240 "type": {
241 "displayName": [
242 "u32"
243 ],
244 "type": 4
245 }
246 },
247 {
248 "name": "amount",
249 "type": {
250 "displayName": [
251 "u128"
252 ],
253 "type": 5
254 }
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": 4
275 }
276 },
277 {
278 "name": "item_id",
279 "type": {
280 "displayName": [
281 "u32"
282 ],
283 "type": 4
284 }
285 },
286 {
287 "name": "data",
288 "type": {
289 "displayName": [
290 "Vec"
291 ],
292 "type": 7
293 }
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": 4
314 }
315 },
316 {
317 "name": "address",
318 "type": {
319 "displayName": [
320 "AccountId"
321 ],
322 "type": 1
323 }
324 },
325 {
326 "name": "whitelisted",
327 "type": {
328 "displayName": [
329 "bool"
330 ],
331 "type": 9
332 }
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": 3
453 }
454 }
455 },
456 {
457 "def": {
458 "sequence": {
459 "type": 6
460 }
461 }
462 },
463 {
464 "def": {
465 "primitive": "bool"
466 }
467 }
130 ]468 ]
131}469}
modifiedtests/src/transfer_contract/nft_transfer.wasmdiffbeforeafterboth

binary blob — no preview

modifiedtests/src/util/helpers.tsdiffbeforeafterboth
636636
637export async function637export async function
638approveExpectSuccess(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-expression
647 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-expression
849}853}
850854
851export 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);
858
859 if (owner === '') {
860 owner = sender.address;
861 }
862862
863 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}
976976
977export 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) => {
979979
980 const whiteListedBefore = (await api.query.nft.whiteList(collectionId, address)).toJSON();980 const whiteListedBefore = (await api.query.nft.whiteList(collectionId, address)).toJSON();