1#![cfg_attr(not(feature = "std"), no_std)]23#[cfg(feature = "std")]4pub use std::*;56#[cfg(feature = "std")]7pub use serde::*;89use codec::{Decode, Encode};10pub use frame_support::{11 construct_runtime, decl_event, decl_module, decl_storage,12 dispatch::DispatchResult,13 ensure, parameter_types, fail,14 traits::{15 Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,16 Randomness, WithdrawReason,17 },18 weights::{19 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},20 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,21 WeightToFeePolynomial,22 },23 IsSubType, StorageValue,24};252627use frame_system::{self as system, ensure_signed, ensure_root};28use sp_runtime::sp_std::prelude::Vec;29use sp_runtime::{30 traits::{31 DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SignedExtension, Zero,32 },33 transaction_validity::{34 InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,35 },36 FixedPointOperand, FixedU128,37};38use pallet_contracts::ContractAddressFor;39use sp_runtime::traits::StaticLookup;4041#[cfg(test)]42mod mock;4344#[cfg(test)]45mod tests;4647mod default_weights;4849505152#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]53#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]54pub enum CollectionMode {55 Invalid,56 NFT,57 58 Fungible(u32),59 60 ReFungible(u32),61}6263impl Into<u8> for CollectionMode {64 fn into(self) -> u8 {65 match self {66 CollectionMode::Invalid => 0,67 CollectionMode::NFT => 1,68 CollectionMode::Fungible(_) => 2,69 CollectionMode::ReFungible(_) => 3,70 }71 }72}7374#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]75#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]76pub enum AccessMode {77 Normal,78 WhiteList,79}80impl Default for AccessMode {81 fn default() -> Self {82 Self::Normal83 }84}8586impl Default for CollectionMode {87 fn default() -> Self {88 Self::Invalid89 }90}9192#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]93#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]94pub struct Ownership<AccountId> {95 pub owner: AccountId,96 pub fraction: u128,97}9899#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]100#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]101pub struct CollectionType<AccountId> {102 pub owner: AccountId,103 pub mode: CollectionMode,104 pub access: AccessMode,105 pub decimal_points: u32,106 pub name: Vec<u16>, 107 pub description: Vec<u16>, 108 pub token_prefix: Vec<u8>, 109 pub mint_mode: bool,110 pub offchain_schema: Vec<u8>,111 pub sponsor: AccountId, 112 pub unconfirmed_sponsor: AccountId, 113 pub variable_on_chain_schema: Vec<u8>, 114 pub const_on_chain_schema: Vec<u8>, 115}116117#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]118#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]119pub struct CollectionAdminsType<AccountId> {120 pub admin: AccountId,121 pub collection_id: u64,122}123124#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]125#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]126pub struct NftItemType<AccountId> {127 pub collection: u64,128 pub owner: AccountId,129 pub const_data: Vec<u8>,130 pub variable_data: Vec<u8>,131}132133#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]134#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]135pub struct FungibleItemType<AccountId> {136 pub collection: u64,137 pub owner: AccountId,138 pub value: u128,139}140141#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]142#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]143pub struct ReFungibleItemType<AccountId> {144 pub collection: u64,145 pub owner: Vec<Ownership<AccountId>>,146 pub const_data: Vec<u8>,147 pub variable_data: Vec<u8>,148}149150#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]151#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]152pub struct ApprovePermissions<AccountId> {153 pub approved: AccountId,154 pub amount: u64,155}156157#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]158#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]159pub struct VestingItem<AccountId, Moment> {160 pub sender: AccountId,161 pub recipient: AccountId,162 pub collection_id: u64,163 pub item_id: u64,164 pub amount: u64,165 pub vesting_date: Moment,166}167168#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]169#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]170pub struct BasketItem<AccountId, BlockNumber> {171 pub address: AccountId,172 pub start_block: BlockNumber,173}174175#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]176#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]177pub struct ChainLimits {178 pub collection_numbers_limit: u64,179 pub account_token_ownership_limit: u64,180 pub collections_admins_limit: u64,181 pub custom_data_limit: u32,182183 184 pub nft_sponsor_transfer_timeout: u32,185 pub fungible_sponsor_transfer_timeout: u32,186 pub refungible_sponsor_transfer_timeout: u32,187}188189pub trait WeightInfo {190 fn create_collection() -> Weight;191 fn destroy_collection() -> Weight;192 fn add_to_white_list() -> Weight;193 fn remove_from_white_list() -> Weight;194 fn set_public_access_mode() -> Weight;195 fn set_mint_permission() -> Weight;196 fn change_collection_owner() -> Weight;197 fn add_collection_admin() -> Weight;198 fn remove_collection_admin() -> Weight;199 fn set_collection_sponsor() -> Weight;200 fn confirm_sponsorship() -> Weight;201 fn remove_collection_sponsor() -> Weight;202 fn create_item(s: usize) -> Weight;203 fn burn_item() -> Weight;204 fn transfer() -> Weight;205 fn approve() -> Weight;206 fn transfer_from() -> Weight;207 fn set_offchain_schema() -> Weight;208 fn set_const_on_chain_schema() -> Weight;209 fn set_variable_on_chain_schema() -> Weight;210 fn set_variable_meta_data() -> Weight;211 212}213214#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]215#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]216pub struct CreateNftData {217 pub const_data: Vec<u8>,218 pub variable_data: Vec<u8>,219}220221#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]222#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]223pub struct CreateFungibleData {224}225226#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]227#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]228pub struct CreateReFungibleData {229 pub const_data: Vec<u8>,230 pub variable_data: Vec<u8>,231}232233#[derive(Encode, Decode, Debug, Clone, PartialEq)]234#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]235pub enum CreateItemData {236 NFT(CreateNftData),237 Fungible(CreateFungibleData),238 ReFungible(CreateReFungibleData)239}240241impl CreateItemData {242 pub fn len(&self) -> usize {243 let len = match self {244 CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),245 CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),246 _ => 0247 };248 249 return len;250 }251}252253pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {254 type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;255256 257 type WeightInfo: WeightInfo;258}259260#[cfg(feature = "runtime-benchmarks")]261mod benchmarking;262263264265decl_storage! {266 trait Store for Module<T: Trait> as Nft {267268 269 NextCollectionID: u64;270 CreatedCollectionCount: u64;271 ChainVersion: u64;272 ItemListIndex: map hasher(blake2_128_concat) u64 => u64;273274 275 pub ChainLimit get(fn chain_limit) config(): ChainLimits;276277 278 CollectionCount: u64;279 pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;280281 282 pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;283 pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;284 pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;285286 287 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;288289 290 pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;291292 293 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;294 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;295 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;296297 298 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;299300 301 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;302 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => Vec<BasketItem<T::AccountId, T::BlockNumber>>;303 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;304305 306 pub ContractOwner get(fn contract_owner): map hasher(identity) T::AccountId => T::AccountId;307 pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(identity) T::AccountId => bool;308 }309 add_extra_genesis {310 build(|config: &GenesisConfig<T>| {311 312 for (_num, _c) in &config.collection {313 <Module<T>>::init_collection(_c);314 }315316 for (_num, _q, _i) in &config.nft_item_id {317 <Module<T>>::init_nft_token(_i);318 }319320 for (_num, _q, _i) in &config.fungible_item_id {321 <Module<T>>::init_fungible_token(_i);322 }323324 for (_num, _q, _i) in &config.refungible_item_id {325 <Module<T>>::init_refungible_token(_i);326 }327 })328 }329}330331decl_event!(332 pub enum Event<T>333 where334 AccountId = <T as system::Trait>::AccountId,335 {336 337 338 339 340 341 342 343 344 345 Created(u64, u8, AccountId),346347 348 349 350 351 352 353 354 ItemCreated(u64, u64),355356 357 358 359 360 361 362 363 ItemDestroyed(u64, u64),364 }365);366367decl_module! {368 pub struct Module<T: Trait> for enum Call where origin: T::Origin {369370 fn deposit_event() = default;371372 fn on_initialize(now: T::BlockNumber) -> Weight {373374 if ChainVersion::get() < 2375 {376 let value = NextCollectionID::get();377 CreatedCollectionCount::put(value);378 ChainVersion::put(2);379 }380381 0382 }383384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 #[weight = T::WeightInfo::create_collection()]401 pub fn create_collection(origin,402 collection_name: Vec<u16>,403 collection_description: Vec<u16>,404 token_prefix: Vec<u8>,405 mode: CollectionMode) -> DispatchResult {406407 408 let who = ensure_signed(origin)?;409410 let decimal_points = match mode {411 CollectionMode::Fungible(points) => points,412 CollectionMode::ReFungible(points) => points,413 _ => 0414 };415416 417 ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, "Total collections bound exceeded");418419 420 ensure!(decimal_points <= 4, "decimal_points parameter must be lower than 4");421422 let mut name = collection_name.to_vec();423 name.push(0);424 ensure!(name.len() <= 64, "Collection name can not be longer than 63 char");425426 let mut description = collection_description.to_vec();427 description.push(0);428 ensure!(name.len() <= 256, "Collection description can not be longer than 255 char");429430 let mut prefix = token_prefix.to_vec();431 prefix.push(0);432 ensure!(prefix.len() <= 16, "Token prefix can not be longer than 15 char");433434 435 let next_id = CreatedCollectionCount::get()436 .checked_add(1)437 .expect("collection id error");438439 440 let total = CollectionCount::get()441 .checked_add(1)442 .expect("collection counter error");443444 CreatedCollectionCount::put(next_id);445 CollectionCount::put(total);446447 448 let new_collection = CollectionType {449 owner: who.clone(),450 name: name,451 mode: mode.clone(),452 mint_mode: false,453 access: AccessMode::Normal,454 description: description,455 decimal_points: decimal_points,456 token_prefix: prefix,457 offchain_schema: Vec::new(),458 sponsor: T::AccountId::default(),459 unconfirmed_sponsor: T::AccountId::default(),460 variable_on_chain_schema: Vec::new(),461 const_on_chain_schema: Vec::new(),462 };463464 465 <Collection<T>>::insert(next_id, new_collection);466467 468 Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));469470 Ok(())471 }472473 474 475 476 477 478 479 480 481 482 #[weight = T::WeightInfo::destroy_collection()]483 pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {484485 let sender = ensure_signed(origin)?;486 Self::check_owner_permissions(collection_id, sender)?;487488 <AddressTokens<T>>::remove_prefix(collection_id);489 <ApprovedList<T>>::remove_prefix(collection_id);490 <Balance<T>>::remove_prefix(collection_id);491 <ItemListIndex>::remove(collection_id);492 <AdminList<T>>::remove(collection_id);493 <Collection<T>>::remove(collection_id);494 <WhiteList<T>>::remove(collection_id);495496 <NftItemList<T>>::remove_prefix(collection_id);497 <FungibleItemList<T>>::remove_prefix(collection_id);498 <ReFungibleItemList<T>>::remove_prefix(collection_id);499500 <NftTransferBasket<T>>::remove_prefix(collection_id);501 <FungibleTransferBasket<T>>::remove_prefix(collection_id);502 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);503504 if CollectionCount::get() > 0505 {506 507 let total = CollectionCount::get()508 .checked_sub(1)509 .expect("collection counter error");510511 CollectionCount::put(total);512 }513514 Ok(())515 }516517 518 519 520 521 522 523 524 525 526 527 528 529 #[weight = T::WeightInfo::add_to_white_list()]530 pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{531532 let sender = ensure_signed(origin)?;533 Self::check_owner_or_admin_permissions(collection_id, sender)?;534535 let mut white_list_collection: Vec<T::AccountId>;536 if <WhiteList<T>>::contains_key(collection_id) {537 white_list_collection = <WhiteList<T>>::get(collection_id);538 if !white_list_collection.contains(&address.clone())539 {540 white_list_collection.push(address.clone());541 }542 }543 else {544 white_list_collection = Vec::new();545 white_list_collection.push(address.clone());546 }547548 <WhiteList<T>>::insert(collection_id, white_list_collection);549 Ok(())550 }551552 553 554 555 556 557 558 559 560 561 562 563 564 #[weight = T::WeightInfo::remove_from_white_list()]565 pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{566567 let sender = ensure_signed(origin)?;568 Self::check_owner_or_admin_permissions(collection_id, sender)?;569570 if <WhiteList<T>>::contains_key(collection_id) {571 let mut white_list_collection = <WhiteList<T>>::get(collection_id);572 if white_list_collection.contains(&address.clone())573 {574 white_list_collection.retain(|i| *i != address.clone());575 <WhiteList<T>>::insert(collection_id, white_list_collection);576 }577 }578579 Ok(())580 }581582 583 584 585 586 587 588 589 590 591 592 593 #[weight = T::WeightInfo::set_public_access_mode()]594 pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult595 {596 let sender = ensure_signed(origin)?;597598 Self::check_owner_permissions(collection_id, sender)?;599 let mut target_collection = <Collection<T>>::get(collection_id);600 target_collection.access = mode;601 <Collection<T>>::insert(collection_id, target_collection);602603 Ok(())604 }605606 607 608 609 610 611 612 613 614 615 616 617 618 619 #[weight = T::WeightInfo::set_mint_permission()]620 pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult621 {622 let sender = ensure_signed(origin)?;623624 Self::check_owner_permissions(collection_id, sender)?;625 let mut target_collection = <Collection<T>>::get(collection_id);626 target_collection.mint_mode = mint_permission;627 <Collection<T>>::insert(collection_id, target_collection);628629 Ok(())630 }631632 633 634 635 636 637 638 639 640 641 642 643 #[weight = T::WeightInfo::change_collection_owner()]644 pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {645646 let sender = ensure_signed(origin)?;647 Self::check_owner_permissions(collection_id, sender)?;648 let mut target_collection = <Collection<T>>::get(collection_id);649 target_collection.owner = new_owner;650 <Collection<T>>::insert(collection_id, target_collection);651652 Ok(())653 }654655 656 657 658 659 660 661 662 663 664 665 666 667 668 #[weight = T::WeightInfo::add_collection_admin()]669 pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {670671 let sender = ensure_signed(origin)?;672 Self::check_owner_or_admin_permissions(collection_id, sender)?;673 let mut admin_arr: Vec<T::AccountId> = Vec::new();674675 if <AdminList<T>>::contains_key(collection_id)676 {677 admin_arr = <AdminList<T>>::get(collection_id);678 ensure!(!admin_arr.contains(&new_admin_id), "Account already has admin role");679 }680681 682 ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, "Number of collection admins bound exceeded");683684 admin_arr.push(new_admin_id);685 <AdminList<T>>::insert(collection_id, admin_arr);686687 Ok(())688 }689690 691 692 693 694 695 696 697 698 699 700 701 702 #[weight = T::WeightInfo::remove_collection_admin()]703 pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {704705 let sender = ensure_signed(origin)?;706 Self::check_owner_or_admin_permissions(collection_id, sender)?;707708 if <AdminList<T>>::contains_key(collection_id)709 {710 let mut admin_arr = <AdminList<T>>::get(collection_id);711 admin_arr.retain(|i| *i != account_id);712 <AdminList<T>>::insert(collection_id, admin_arr);713 }714715 Ok(())716 }717718 719 720 721 722 723 724 725 726 727 #[weight = T::WeightInfo::set_collection_sponsor()]728 pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {729730 let sender = ensure_signed(origin)?;731 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");732733 let mut target_collection = <Collection<T>>::get(collection_id);734 ensure!(sender == target_collection.owner, "You do not own this collection");735736 target_collection.unconfirmed_sponsor = new_sponsor;737 <Collection<T>>::insert(collection_id, target_collection);738739 Ok(())740 }741742 743 744 745 746 747 748 749 #[weight = T::WeightInfo::confirm_sponsorship()]750 pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {751752 let sender = ensure_signed(origin)?;753 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");754755 let mut target_collection = <Collection<T>>::get(collection_id);756 ensure!(sender == target_collection.unconfirmed_sponsor, "This address is not set as sponsor, use setCollectionSponsor first");757758 target_collection.sponsor = target_collection.unconfirmed_sponsor;759 target_collection.unconfirmed_sponsor = T::AccountId::default();760 <Collection<T>>::insert(collection_id, target_collection);761762 Ok(())763 }764765 766 767 768 769 770 771 772 773 774 #[weight = T::WeightInfo::remove_collection_sponsor()]775 pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {776777 let sender = ensure_signed(origin)?;778 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");779780 let mut target_collection = <Collection<T>>::get(collection_id);781 ensure!(sender == target_collection.owner, "You do not own this collection");782783 target_collection.sponsor = T::AccountId::default();784 <Collection<T>>::insert(collection_id, target_collection);785786 Ok(())787 }788789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812813 #[weight = T::WeightInfo::create_item(data.len())]814 pub fn create_item(origin, collection_id: u64, owner: T::AccountId, data: CreateItemData) -> DispatchResult {815816 let sender = ensure_signed(origin)?;817 Self::collection_exists(collection_id)?;818 let target_collection = <Collection<T>>::get(collection_id);819820 if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {821 ensure!(target_collection.mint_mode == true, "Public minting is not allowed for this collection.");822 Self::check_white_list(collection_id, &owner)?;823 Self::check_white_list(collection_id, &sender)?;824 }825826 match target_collection.mode827 {828 CollectionMode::NFT => {829 if let CreateItemData::NFT(data) = data {830 831 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");832 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");833 834 835 let item = NftItemType {836 collection: collection_id,837 owner: owner,838 const_data: data.const_data.clone(),839 variable_data: data.variable_data.clone() 840 };841 842 Self::add_nft_item(item)?;843 844 } else {845 fail!("Not NFT item data used to mint in NFT collection.");846 }847 },848 CollectionMode::Fungible(_) => {849 if let CreateItemData::Fungible(_) = data {850 851 let item = FungibleItemType {852 collection: collection_id,853 owner: owner,854 value: (10 as u128).pow(target_collection.decimal_points)855 };856 857 Self::add_fungible_item(item)?;858 } else {859 fail!("Not Fungible item data used to mint in Fungible collection.");860 }861 },862 CollectionMode::ReFungible(_) => {863 if let CreateItemData::ReFungible(data) = data {864 865 866 ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");867 ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");868 869 let mut owner_list = Vec::new();870 let value = (10 as u128).pow(target_collection.decimal_points);871 owner_list.push(Ownership {owner: owner.clone(), fraction: value});872 873 let item = ReFungibleItemType {874 collection: collection_id,875 owner: owner_list,876 const_data: data.const_data.clone(),877 variable_data: data.variable_data.clone() 878 };879 880 Self::add_refungible_item(item)?;881 } else {882 fail!("Not Re Fungible item data used to mint in Re Fungible collection.");883 }884 },885 _ => { ensure!(1 == 0,"Unexpected collection type."); }886 };887888 889 Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));890891 Ok(())892 }893894 895 896 897 898 899 900 901 902 903 904 905 906 907 #[weight = T::WeightInfo::burn_item()]908 pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {909910 let sender = ensure_signed(origin)?;911 Self::collection_exists(collection_id)?;912913 914 let target_collection = <Collection<T>>::get(collection_id);915 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||916 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),917 "Only item owner, collection owner and admins can modify item");918919 if target_collection.access == AccessMode::WhiteList {920 Self::check_white_list(collection_id, &sender)?;921 }922923 match target_collection.mode924 {925 CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,926 CollectionMode::Fungible(_) => Self::burn_fungible_item(collection_id, item_id)?,927 CollectionMode::ReFungible(_) => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,928 _ => ()929 };930931 932 Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));933934 Ok(())935 }936937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 #[weight = T::WeightInfo::transfer()]961 pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {962963 let sender = ensure_signed(origin)?;964965 966 let target_collection = <Collection<T>>::get(collection_id);967 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||968 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),969 "Only item owner, collection owner and admins can modify item");970971 if target_collection.access == AccessMode::WhiteList {972 Self::check_white_list(collection_id, &sender)?;973 Self::check_white_list(collection_id, &recipient)?;974 }975976 match target_collection.mode977 {978 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,979 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,980 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,981 _ => ()982 };983984 Ok(())985 }986987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 #[weight = T::WeightInfo::approve()]1003 pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {10041005 let sender = ensure_signed(origin)?;10061007 1008 let target_collection = <Collection<T>>::get(collection_id);1009 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1010 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1011 "Only item owner, collection owner and admins can approve");10121013 if target_collection.access == AccessMode::WhiteList {1014 Self::check_white_list(collection_id, &sender)?;1015 Self::check_white_list(collection_id, &approved)?;1016 }10171018 1019 let amount = 100000000;10201021 let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1022 if list_exists {10231024 let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1025 let item_contains = list.iter().any(|i| i.approved == approved);10261027 if !item_contains {1028 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1029 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1030 }1031 } else {10321033 let mut list = Vec::new();1034 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1035 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1036 }10371038 Ok(())1039 }1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 #[weight = T::WeightInfo::transfer_from()]1061 pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {10621063 let sender = ensure_signed(origin)?;1064 let mut appoved_transfer = false;10651066 1067 if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1068 let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1069 let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1070 if opt_item.is_some()1071 {1072 appoved_transfer = true;1073 ensure!(opt_item.unwrap().amount >= value, "Requested value more than approved");1074 }1075 }10761077 1078 let target_collection = <Collection<T>>::get(collection_id);1079 ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1080 "Only item owner, collection owner and admins can modify items");10811082 if target_collection.access == AccessMode::WhiteList {1083 Self::check_white_list(collection_id, &sender)?;1084 Self::check_white_list(collection_id, &recipient)?;1085 }10861087 1088 let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1089 .into_iter().filter(|i| i.approved != sender.clone()).collect();1090 <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);109110921093 match target_collection.mode1094 {1095 CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1096 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1097 CollectionMode::ReFungible(_) => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1098 _ => ()1099 };11001101 Ok(())1102 }11031104 1105 #[weight = 0]1106 pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {11071108 1109 1110 1111 11121113 11141115 11161117 Ok(())1118 }1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 #[weight = T::WeightInfo::set_variable_meta_data()]1133 pub fn set_variable_meta_data (1134 origin,1135 collection_id: u64,1136 item_id: u64,1137 data: Vec<u8>1138 ) -> DispatchResult {1139 let sender = ensure_signed(origin)?;1140 1141 Self::collection_exists(collection_id)?;11421143 1144 let target_collection = <Collection<T>>::get(collection_id);1145 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1146 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1147 "Only item owner, collection owner and admins can modify item");11481149 Self::item_exists(collection_id, item_id, &target_collection.mode)?;11501151 match target_collection.mode1152 {1153 CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1154 CollectionMode::ReFungible(_) => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1155 _ => ()1156 };11571158 Ok(())1159 }1160 11611162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 #[weight = T::WeightInfo::set_offchain_schema()]1175 pub fn set_offchain_schema(1176 origin,1177 collection_id: u64,1178 schema: Vec<u8>1179 ) -> DispatchResult {1180 let sender = ensure_signed(origin)?;1181 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;11821183 let mut target_collection = <Collection<T>>::get(collection_id);1184 target_collection.offchain_schema = schema;1185 <Collection<T>>::insert(collection_id, target_collection);11861187 Ok(())1188 }11891190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 #[weight = T::WeightInfo::set_const_on_chain_schema()]1203 pub fn set_const_on_chain_schema (1204 origin,1205 collection_id: u64,1206 schema: Vec<u8>1207 ) -> DispatchResult {1208 let sender = ensure_signed(origin)?;1209 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12101211 let mut target_collection = <Collection<T>>::get(collection_id);1212 target_collection.const_on_chain_schema = schema;1213 <Collection<T>>::insert(collection_id, target_collection);12141215 Ok(())1216 }12171218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 #[weight = T::WeightInfo::set_const_on_chain_schema()]1231 pub fn set_variable_on_chain_schema (1232 origin,1233 collection_id: u64,1234 schema: Vec<u8>1235 ) -> DispatchResult {1236 let sender = ensure_signed(origin)?;1237 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12381239 let mut target_collection = <Collection<T>>::get(collection_id);1240 target_collection.variable_on_chain_schema = schema;1241 <Collection<T>>::insert(collection_id, target_collection);12421243 Ok(())1244 }12451246 1247 #[weight = 0]1248 pub fn set_chain_limits(1249 origin,1250 limits: ChainLimits1251 ) -> DispatchResult {1252 ensure_root(origin)?;1253 <ChainLimit>::put(limits);1254 Ok(())1255 }12561257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 #[weight = 0]1269 pub fn enable_contract_sponsoring(1270 origin,1271 contract_address: T::AccountId,1272 enable: bool1273 ) -> DispatchResult {1274 let sender = ensure_signed(origin)?;1275 let mut is_owner = false;1276 if <ContractOwner<T>>::contains_key(contract_address.clone()) {1277 let owner = <ContractOwner<T>>::get(&contract_address);1278 is_owner = sender == owner;1279 }1280 ensure!(is_owner, "Only contract owner may call this method");12811282 <ContractSelfSponsoring<T>>::insert(contract_address, enable);1283 Ok(())1284 }12851286 }1287}12881289impl<T: Trait> Module<T> {1290 fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1291 let current_index = <ItemListIndex>::get(item.collection)1292 .checked_add(1)1293 .expect("Item list index id error");1294 let itemcopy = item.clone();1295 let owner = item.owner.clone();1296 let value = item.value as u64;12971298 Self::add_token_index(item.collection, current_index, owner.clone())?;12991300 <ItemListIndex>::insert(item.collection, current_index);1301 <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);13021303 1304 let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1305 <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1306 1307 1308 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1309 .checked_add(value)1310 .unwrap();1311 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);13121313 Ok(())1314 }13151316 fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1317 let current_index = <ItemListIndex>::get(item.collection)1318 .checked_add(1)1319 .expect("Item list index id error");1320 let itemcopy = item.clone();13211322 let value = item.owner.first().unwrap().fraction as u64;1323 let owner = item.owner.first().unwrap().owner.clone();13241325 Self::add_token_index(item.collection, current_index, owner.clone())?;13261327 <ItemListIndex>::insert(item.collection, current_index);1328 <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);13291330 1331 let block_number: T::BlockNumber = 0.into();1332 <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);13331334 1335 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1336 .checked_add(value)1337 .unwrap();1338 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);13391340 Ok(())1341 }13421343 fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1344 let current_index = <ItemListIndex>::get(item.collection)1345 .checked_add(1)1346 .expect("Item list index id error");13471348 let item_owner = item.owner.clone();1349 let collection_id = item.collection.clone();1350 Self::add_token_index(collection_id, current_index, item.owner.clone())?;13511352 <ItemListIndex>::insert(collection_id, current_index);1353 <NftItemList<T>>::insert(collection_id, current_index, item);13541355 1356 let block_number: T::BlockNumber = 0.into();1357 <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);13581359 1360 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1361 .checked_add(1)1362 .unwrap();1363 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);13641365 Ok(())1366 }13671368 fn burn_refungible_item(1369 collection_id: u64,1370 item_id: u64,1371 owner: T::AccountId,1372 ) -> DispatchResult {1373 ensure!(1374 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1375 "Item does not exists"1376 );1377 let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1378 let item = collection1379 .owner1380 .iter()1381 .filter(|&i| i.owner == owner)1382 .next()1383 .unwrap();1384 Self::remove_token_index(collection_id, item_id, owner.clone())?;13851386 1387 <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));13881389 1390 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1391 .checked_sub(item.fraction as u64)1392 .unwrap();1393 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);13941395 <ReFungibleItemList<T>>::remove(collection_id, item_id);13961397 Ok(())1398 }13991400 fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1401 ensure!(1402 <NftItemList<T>>::contains_key(collection_id, item_id),1403 "Item does not exists"1404 );1405 let item = <NftItemList<T>>::get(collection_id, item_id);1406 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;14071408 1409 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));14101411 1412 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1413 .checked_sub(1)1414 .unwrap();1415 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1416 <NftItemList<T>>::remove(collection_id, item_id);14171418 Ok(())1419 }14201421 fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1422 ensure!(1423 <FungibleItemList<T>>::contains_key(collection_id, item_id),1424 "Item does not exists"1425 );1426 let item = <FungibleItemList<T>>::get(collection_id, item_id);1427 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;14281429 1430 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));14311432 1433 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1434 .checked_sub(item.value as u64)1435 .unwrap();1436 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);14371438 <FungibleItemList<T>>::remove(collection_id, item_id);14391440 Ok(())1441 }14421443 fn collection_exists(collection_id: u64) -> DispatchResult {1444 ensure!(1445 <Collection<T>>::contains_key(collection_id),1446 "This collection does not exist"1447 );1448 Ok(())1449 }14501451 fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1452 Self::collection_exists(collection_id)?;14531454 let target_collection = <Collection<T>>::get(collection_id);1455 ensure!(1456 subject == target_collection.owner,1457 "You do not own this collection"1458 );14591460 Ok(())1461 }14621463 fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1464 let target_collection = <Collection<T>>::get(collection_id);1465 let mut result: bool = subject == target_collection.owner;1466 let exists = <AdminList<T>>::contains_key(collection_id);14671468 if !result & exists {1469 if <AdminList<T>>::get(collection_id).contains(&subject) {1470 result = true1471 }1472 }14731474 result1475 }14761477 fn check_owner_or_admin_permissions(1478 collection_id: u64,1479 subject: T::AccountId,1480 ) -> DispatchResult {1481 Self::collection_exists(collection_id)?;1482 let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());14831484 ensure!(1485 result,1486 "You do not have permissions to modify this collection"1487 );1488 Ok(())1489 }14901491 fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1492 let target_collection = <Collection<T>>::get(collection_id);14931494 match target_collection.mode {1495 CollectionMode::NFT => {1496 <NftItemList<T>>::get(collection_id, item_id).owner == subject1497 }1498 CollectionMode::Fungible(_) => {1499 <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1500 }1501 CollectionMode::ReFungible(_) => {1502 <ReFungibleItemList<T>>::get(collection_id, item_id)1503 .owner1504 .iter()1505 .any(|i| i.owner == subject)1506 }1507 CollectionMode::Invalid => false,1508 }1509 }15101511 fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1512 let mes = "Address is not in white list";1513 ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1514 let wl = <WhiteList<T>>::get(collection_id);1515 ensure!(wl.contains(address), mes);15161517 Ok(())1518 }15191520 fn transfer_fungible(1521 collection_id: u64,1522 item_id: u64,1523 value: u64,1524 owner: T::AccountId,1525 new_owner: T::AccountId,1526 ) -> DispatchResult {1527 ensure!(1528 <FungibleItemList<T>>::contains_key(collection_id, item_id),1529 "Item not exists"1530 );15311532 let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1533 let amount = full_item.value;15341535 ensure!(amount >= value.into(), "Item balance not enouth");15361537 1538 let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1539 .checked_sub(value)1540 .unwrap();1541 <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);15421543 let mut new_owner_account_id = 0;1544 let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1545 if new_owner_items.len() > 0 {1546 new_owner_account_id = new_owner_items[0];1547 }15481549 let val64 = value.into();15501551 1552 if amount == val64 && new_owner_account_id == 0 {1553 1554 1555 let mut new_full_item = full_item.clone();1556 new_full_item.owner = new_owner.clone();1557 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);15581559 1560 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1561 .checked_add(value)1562 .unwrap();1563 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);15641565 1566 Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1567 } else {1568 let mut new_full_item = full_item.clone();1569 new_full_item.value -= val64;15701571 1572 if new_owner_account_id > 0 {1573 1574 let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1575 item.value += val64;15761577 1578 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1579 .checked_add(value)1580 .unwrap();1581 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);15821583 <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1584 } else {1585 1586 let item = FungibleItemType {1587 collection: collection_id,1588 owner: new_owner.clone(),1589 value: val64,1590 };15911592 Self::add_fungible_item(item)?;1593 }15941595 if amount == val64 {1596 Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;15971598 1599 <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1600 <FungibleItemList<T>>::remove(collection_id, item_id);1601 }16021603 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1604 }16051606 Ok(())1607 }16081609 fn transfer_refungible(1610 collection_id: u64,1611 item_id: u64,1612 value: u64,1613 owner: T::AccountId,1614 new_owner: T::AccountId,1615 ) -> DispatchResult {1616 ensure!(1617 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1618 "Item not exists"1619 );16201621 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1622 let item = full_item1623 .owner1624 .iter()1625 .filter(|i| i.owner == owner)1626 .next()1627 .unwrap();1628 let amount = item.fraction;16291630 ensure!(amount >= value.into(), "Item balance not enouth");16311632 1633 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1634 .checked_sub(value)1635 .unwrap();1636 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);16371638 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1639 .checked_add(value)1640 .unwrap();1641 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);16421643 let old_owner = item.owner.clone();1644 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1645 let val64 = value.into();16461647 1648 if amount == val64 && !new_owner_has_account {1649 1650 1651 let mut new_full_item = full_item.clone();1652 new_full_item1653 .owner1654 .iter_mut()1655 .find(|i| i.owner == owner)1656 .unwrap()1657 .owner = new_owner.clone();1658 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);16591660 1661 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1662 } else {1663 let mut new_full_item = full_item.clone();1664 new_full_item1665 .owner1666 .iter_mut()1667 .find(|i| i.owner == owner)1668 .unwrap()1669 .fraction -= val64;16701671 1672 if new_owner_has_account {1673 1674 new_full_item1675 .owner1676 .iter_mut()1677 .find(|i| i.owner == new_owner)1678 .unwrap()1679 .fraction += val64;1680 } else {1681 1682 new_full_item.owner.push(Ownership {1683 owner: new_owner.clone(),1684 fraction: val64,1685 });1686 Self::add_token_index(collection_id, item_id, new_owner.clone())?;1687 }16881689 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1690 }16911692 Ok(())1693 }16941695 fn transfer_nft(1696 collection_id: u64,1697 item_id: u64,1698 sender: T::AccountId,1699 new_owner: T::AccountId,1700 ) -> DispatchResult {1701 ensure!(1702 <NftItemList<T>>::contains_key(collection_id, item_id),1703 "Item not exists"1704 );17051706 let mut item = <NftItemList<T>>::get(collection_id, item_id);17071708 ensure!(1709 sender == item.owner,1710 "sender parameter and item owner must be equal"1711 );17121713 1714 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1715 .checked_sub(1)1716 .unwrap();1717 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);17181719 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1720 .checked_add(1)1721 .unwrap();1722 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17231724 1725 let old_owner = item.owner.clone();1726 item.owner = new_owner.clone();1727 <NftItemList<T>>::insert(collection_id, item_id, item);17281729 1730 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;17311732 1733 <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1734 Ok(())1735 }1736 1737 fn item_exists(1738 collection_id: u64,1739 item_id: u64,1740 mode: &CollectionMode1741 ) -> DispatchResult {1742 match mode {1743 CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1744 CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1745 CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1746 _ => ()1747 };1748 1749 Ok(())1750 }17511752 fn set_re_fungible_variable_data(1753 collection_id: u64,1754 item_id: u64,1755 data: Vec<u8>1756 ) -> DispatchResult {1757 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);17581759 item.variable_data = data;17601761 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);17621763 Ok(())1764 }17651766 fn set_nft_variable_data(1767 collection_id: u64,1768 item_id: u64,1769 data: Vec<u8>1770 ) -> DispatchResult {1771 let mut item = <NftItemList<T>>::get(collection_id, item_id);1772 1773 item.variable_data = data;17741775 <NftItemList<T>>::insert(collection_id, item_id, item);1776 1777 Ok(())1778 }17791780 fn init_collection(item: &CollectionType<T::AccountId>) {1781 1782 assert!(1783 item.decimal_points <= 4,1784 "decimal_points parameter must be lower than 4"1785 );1786 assert!(1787 item.name.len() <= 64,1788 "Collection name can not be longer than 63 char"1789 );1790 assert!(1791 item.name.len() <= 256,1792 "Collection description can not be longer than 255 char"1793 );1794 assert!(1795 item.token_prefix.len() <= 16,1796 "Token prefix can not be longer than 15 char"1797 );17981799 1800 let next_id = CreatedCollectionCount::get()1801 .checked_add(1)1802 .expect("collection id error");18031804 CreatedCollectionCount::put(next_id);1805 }18061807 fn init_nft_token(item: &NftItemType<T::AccountId>) {1808 let current_index = <ItemListIndex>::get(item.collection)1809 .checked_add(1)1810 .expect("Item list index id error");18111812 let item_owner = item.owner.clone();1813 let collection_id = item.collection.clone();1814 Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();18151816 <ItemListIndex>::insert(collection_id, current_index);18171818 1819 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1820 .checked_add(1)1821 .unwrap();1822 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);1823 }18241825 fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {1826 let current_index = <ItemListIndex>::get(item.collection)1827 .checked_add(1)1828 .expect("Item list index id error");1829 let owner = item.owner.clone();1830 let value = item.value as u64;18311832 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();18331834 <ItemListIndex>::insert(item.collection, current_index);18351836 1837 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1838 .checked_add(value)1839 .unwrap();1840 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1841 }18421843 fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {1844 let current_index = <ItemListIndex>::get(item.collection)1845 .checked_add(1)1846 .expect("Item list index id error");18471848 let value = item.owner.first().unwrap().fraction as u64;1849 let owner = item.owner.first().unwrap().owner.clone();18501851 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();18521853 <ItemListIndex>::insert(item.collection, current_index);18541855 1856 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1857 .checked_add(value)1858 .unwrap();1859 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1860 }18611862 fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {18631864 1865 if <AccountItemCount<T>>::contains_key(owner.clone()) {18661867 1868 let count = <AccountItemCount<T>>::get(owner.clone());1869 ensure!(count < ChainLimit::get().account_token_ownership_limit, "Owned tokens by a single address bound exceeded");18701871 <AccountItemCount<T>>::insert(owner.clone(), 1872 count.checked_add(1).unwrap());1873 }1874 else {1875 <AccountItemCount<T>>::insert(owner.clone(), 1);1876 }18771878 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1879 if list_exists {1880 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1881 let item_contains = list.contains(&item_index.clone());18821883 if !item_contains {1884 list.push(item_index.clone());1885 }18861887 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);1888 } else {1889 let mut itm = Vec::new();1890 itm.push(item_index.clone());1891 <AddressTokens<T>>::insert(collection_id, owner, itm);1892 1893 }18941895 Ok(())1896 }18971898 fn remove_token_index(1899 collection_id: u64,1900 item_index: u64,1901 owner: T::AccountId,1902 ) -> DispatchResult {19031904 1905 <AccountItemCount<T>>::insert(owner.clone(), 1906 <AccountItemCount<T>>::get(owner.clone()).checked_sub(1).unwrap());190719081909 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1910 if list_exists {1911 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1912 let item_contains = list.contains(&item_index.clone());19131914 if item_contains {1915 list.retain(|&item| item != item_index);1916 <AddressTokens<T>>::insert(collection_id, owner, list);1917 }1918 }19191920 Ok(())1921 }19221923 fn move_token_index(1924 collection_id: u64,1925 item_index: u64,1926 old_owner: T::AccountId,1927 new_owner: T::AccountId,1928 ) -> DispatchResult {1929 Self::remove_token_index(collection_id, item_index, old_owner)?;1930 Self::add_token_index(collection_id, item_index, new_owner)?;19311932 Ok(())1933 }1934}1935193619371938193919401941pub type Multiplier = FixedU128;19421943type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1944 <T as system::Trait>::AccountId,1945>>::Balance;1946type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1947 <T as system::Trait>::AccountId,1948>>::NegativeImbalance;1949195019511952#[derive(Encode, Decode, Clone, Eq, PartialEq)]1953pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(1954 #[codec(compact)] BalanceOf<T>1955);19561957impl<T: Trait + Send + Sync> sp_std::fmt::Debug1958 for ChargeTransactionPayment<T>1959{1960 #[cfg(feature = "std")]1961 fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1962 write!(f, "ChargeTransactionPayment<{:?}>", self.0)1963 }1964 #[cfg(not(feature = "std"))]1965 fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1966 Ok(())1967 }1968}19691970impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>1971where1972 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,1973 BalanceOf<T>: Send + Sync + FixedPointOperand,1974{1975 1976 pub fn from(fee: BalanceOf<T>) -> Self {1977 Self(fee)1978 }19791980 pub fn traditional_fee(1981 len: usize,1982 info: &DispatchInfoOf<T::Call>,1983 tip: BalanceOf<T>,1984 ) -> BalanceOf<T>1985 where1986 T::Call: Dispatchable<Info = DispatchInfo>,1987 {1988 <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)1989 }19901991 fn withdraw_fee(1992 &self,1993 who: &T::AccountId,1994 call: &T::Call,1995 info: &DispatchInfoOf<T::Call>,1996 len: usize,1997 ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {1998 let tip = self.0;19992000 2001 2002 2003 2004 2005 2006 2007 let fee = Self::traditional_fee(len, info, tip);20082009 2010 2011 let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2012 Some(Call::create_item(collection_id, _properties, _owner)) => {2013 <Collection<T>>::get(collection_id).sponsor2014 }2015 Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2016 let _collection_mode = <Collection<T>>::get(collection_id).mode;20172018 2019 let sponsor_transfer = match _collection_mode {2020 CollectionMode::NFT => {2021 let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2022 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2023 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2024 if block_number >= limit_time {2025 <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2026 true2027 }2028 else {2029 false2030 }2031 }2032 CollectionMode::Fungible(_) => {2033 let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2034 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2035 if basket.iter().any(|i| i.address == _new_owner.clone())2036 {2037 let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2038 let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();2039 if block_number >= limit_time {2040 basket.retain(|x| x.address == item.address);2041 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2042 <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2043 true2044 }2045 else {2046 false2047 }2048 }2049 else {2050 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2051 true2052 }2053 }2054 CollectionMode::ReFungible(_) => {2055 let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2056 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2057 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2058 if block_number >= limit_time {2059 <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2060 true2061 } else {2062 false2063 }2064 }2065 _ => {2066 false2067 },2068 };20692070 if !sponsor_transfer {2071 T::AccountId::default()2072 } else {2073 <Collection<T>>::get(collection_id).sponsor2074 }2075 }20762077 _ => T::AccountId::default(),2078 };20792080 2081 sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {20822083 2084 Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {20852086 let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2087 code_hash,2088 &data,2089 &who,2090 );2091 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());20922093 T::AccountId::default()2094 },20952096 2097 Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {20982099 let mut sp = T::AccountId::default();2100 let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());2101 if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2102 if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2103 sp = called_contract;2104 }2105 }21062107 sp2108 },21092110 _ => sponsor,2111 };21122113 let mut who_pays_fee: T::AccountId = sponsor.clone();2114 if sponsor == T::AccountId::default() {2115 who_pays_fee = who.clone();2116 }21172118 2119 if fee.is_zero() {2120 return Ok((fee, None));2121 }21222123 match <T as transaction_payment::Trait>::Currency::withdraw(2124 &who_pays_fee,2125 fee,2126 if tip.is_zero() {2127 WithdrawReason::TransactionPayment.into()2128 } else {2129 WithdrawReason::TransactionPayment | WithdrawReason::Tip2130 },2131 ExistenceRequirement::KeepAlive,2132 ) {2133 Ok(imbalance) => Ok((fee, Some(imbalance))),2134 Err(_) => Err(InvalidTransaction::Payment.into()),2135 }2136 }2137}213821392140impl<T: Trait + Send + Sync> SignedExtension2141 for ChargeTransactionPayment<T>2142where2143 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2144 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2145{2146 const IDENTIFIER: &'static str = "ChargeTransactionPayment";2147 type AccountId = T::AccountId;2148 type Call = T::Call;2149 type AdditionalSigned = ();2150 type Pre = (2151 BalanceOf<T>,2152 Self::AccountId,2153 Option<NegativeImbalanceOf<T>>,2154 BalanceOf<T>,2155 );2156 fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2157 Ok(())2158 }21592160 fn validate(2161 &self,2162 _who: &Self::AccountId,2163 _call: &Self::Call,2164 _info: &DispatchInfoOf<Self::Call>,2165 _len: usize,2166 ) -> TransactionValidity {2167 Ok(ValidTransaction::default())2168 }21692170 fn pre_dispatch(2171 self,2172 who: &Self::AccountId,2173 call: &Self::Call,2174 info: &DispatchInfoOf<Self::Call>,2175 len: usize,2176 ) -> Result<Self::Pre, TransactionValidityError> {2177 let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2178 Ok((self.0, who.clone(), imbalance, fee))2179 }21802181 fn post_dispatch(2182 pre: Self::Pre,2183 info: &DispatchInfoOf<Self::Call>,2184 post_info: &PostDispatchInfoOf<Self::Call>,2185 len: usize,2186 _result: &DispatchResult,2187 ) -> Result<(), TransactionValidityError> {2188 let (tip, who, imbalance, fee) = pre;2189 if let Some(payed) = imbalance {2190 let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2191 len as u32, info, post_info, tip,2192 );2193 let refund = fee.saturating_sub(actual_fee);2194 let actual_payment =2195 match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2196 &who, refund,2197 ) {2198 Ok(refund_imbalance) => {2199 2200 2201 match payed.offset(refund_imbalance) {2202 Ok(actual_payment) => actual_payment,2203 Err(_) => return Err(InvalidTransaction::Payment.into()),2204 }2205 }2206 2207 2208 Err(_) => payed,2209 };2210 let imbalances = actual_payment.split(tip);2211 <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2212 Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2213 );2214 }2215 Ok(())2216 }2217}2218221922202221