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,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, SaturatedConversion, Saturating,32 SignedExtension, Zero,33 },34 transaction_validity::{35 InvalidTransaction, TransactionPriority, TransactionValidity, TransactionValidityError,36 ValidTransaction,37 },38 FixedPointOperand, FixedU128,39};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 57 NFT(u32),58 59 Fungible(u32),60 61 ReFungible(u32, u32),62}6364impl Into<u8> for CollectionMode {65 fn into(self) -> u8 {66 match self {67 CollectionMode::Invalid => 0,68 CollectionMode::NFT(_) => 1,69 CollectionMode::Fungible(_) => 2,70 CollectionMode::ReFungible(_, _) => 3,71 }72 }73}7475#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]76#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]77pub enum AccessMode {78 Normal,79 WhiteList,80}81impl Default for AccessMode {82 fn default() -> Self {83 Self::Normal84 }85}8687impl Default for CollectionMode {88 fn default() -> Self {89 Self::Invalid90 }91}9293#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]94#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]95pub struct Ownership<AccountId> {96 pub owner: AccountId,97 pub fraction: u128,98}99100#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]101#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]102pub struct CollectionType<AccountId> {103 pub owner: AccountId,104 pub mode: CollectionMode,105 pub access: AccessMode,106 pub decimal_points: u32,107 pub name: Vec<u16>, 108 pub description: Vec<u16>, 109 pub token_prefix: Vec<u8>, 110 pub custom_data_size: u32,111 pub mint_mode: bool,112 pub offchain_schema: Vec<u8>,113 pub sponsor: AccountId, 114 pub unconfirmed_sponsor: AccountId, 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 data: Vec<u8>,130}131132#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]133#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]134pub struct FungibleItemType<AccountId> {135 pub collection: u64,136 pub owner: AccountId,137 pub value: u128,138}139140#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]141#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]142pub struct ReFungibleItemType<AccountId> {143 pub collection: u64,144 pub owner: Vec<Ownership<AccountId>>,145 pub data: Vec<u8>,146}147148#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]149#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]150pub struct ApprovePermissions<AccountId> {151 pub approved: AccountId,152 pub amount: u64,153}154155#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]156#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]157pub struct VestingItem<AccountId, Moment> {158 pub sender: AccountId,159 pub recipient: AccountId,160 pub collection_id: u64,161 pub item_id: u64,162 pub amount: u64,163 pub vesting_date: Moment,164}165166#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]167#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]168pub struct BasketItem<AccountId, BlockNumber> {169 pub address: AccountId,170 pub start_block: BlockNumber,171}172173#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]174#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]175pub struct ChainLimits {176 pub collection_numbers_limit: u64,177 pub account_token_ownership_limit: u64,178 pub collections_admins_limit: u64,179 pub custom_data_limit: u32,180181 182 pub nft_sponsor_transfer_timeout: u32,183 pub fungible_sponsor_transfer_timeout: u32,184 pub refungible_sponsor_transfer_timeout: u32,185}186187pub trait WeightInfo {188 fn create_collection() -> Weight;189 fn destroy_collection() -> Weight;190 fn add_to_white_list() -> Weight;191 fn remove_from_white_list() -> Weight;192 fn set_public_access_mode() -> Weight;193 fn set_mint_permission() -> Weight;194 fn change_collection_owner() -> Weight;195 fn add_collection_admin() -> Weight;196 fn remove_collection_admin() -> Weight;197 fn set_collection_sponsor() -> Weight;198 fn confirm_sponsorship() -> Weight;199 fn remove_collection_sponsor() -> Weight;200 fn create_item(s: usize, ) -> Weight;201 fn burn_item() -> Weight;202 fn transfer() -> Weight;203 fn approve() -> Weight;204 fn transfer_from() -> Weight;205 fn set_offchain_schema() -> Weight;206}207208pub trait Trait: system::Trait + Sized {209 type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;210211 212 type WeightInfo: WeightInfo;213}214215#[cfg(feature = "runtime-benchmarks")]216mod benchmarking;217218219220decl_storage! {221 trait Store for Module<T: Trait> as Nft {222223 224 NextCollectionID: u64;225 CreatedCollectionCount: u64;226 ChainVersion: u64;227 ItemListIndex: map hasher(blake2_128_concat) u64 => u64;228229 230 pub ChainLimit get(fn chain_limit) config(): ChainLimits;231232 233 CollectionCount: u64;234 pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;235236 237 pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;238 pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;239 pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;240241 242 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;243244 245 pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;246247 248 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;249 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;250 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;251252 253 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;254255 256 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;257 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>>;258 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;259260 261 pub ContractSponsor get(fn contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;262 pub UnconfirmedContractSponsor get(fn unconfirmed_contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;263 }264 add_extra_genesis {265 build(|config: &GenesisConfig<T>| {266 267 for (_num, _c) in &config.collection {268 <Module<T>>::init_collection(_c);269 }270271 for (_num, _q, _i) in &config.nft_item_id {272 <Module<T>>::init_nft_token(_i);273 }274275 for (_num, _q, _i) in &config.fungible_item_id {276 <Module<T>>::init_fungible_token(_i);277 }278279 for (_num, _q, _i) in &config.refungible_item_id {280 <Module<T>>::init_refungible_token(_i);281 }282 })283 }284}285286decl_event!(287 pub enum Event<T>288 where289 AccountId = <T as system::Trait>::AccountId,290 {291 292 293 294 295 296 297 298 299 300 Created(u64, u8, AccountId),301302 303 304 305 306 307 308 309 ItemCreated(u64, u64),310311 312 313 314 315 316 317 318 ItemDestroyed(u64, u64),319 }320);321322decl_module! {323 pub struct Module<T: Trait> for enum Call where origin: T::Origin {324325 fn deposit_event() = default;326327 fn on_initialize(now: T::BlockNumber) -> Weight {328329 if ChainVersion::get() < 2330 {331 let value = NextCollectionID::get();332 CreatedCollectionCount::put(value);333 ChainVersion::put(2);334 }335336 0337 }338339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 #[weight = T::WeightInfo::create_collection()]356 pub fn create_collection(origin,357 collection_name: Vec<u16>,358 collection_description: Vec<u16>,359 token_prefix: Vec<u8>,360 mode: CollectionMode) -> DispatchResult {361362 363 let who = ensure_signed(origin)?;364 let custom_data_size = match mode {365 CollectionMode::NFT(size) => {366367 368 ensure!(size < ChainLimit::get().custom_data_limit, "Custom data size bound exceeded");369 size370 },371 CollectionMode::ReFungible(size, _) => {372373 374 ensure!(size < ChainLimit::get().custom_data_limit, "Custom data size bound exceeded");375 size376 },377 _ => 0378 };379380 let decimal_points = match mode {381 CollectionMode::Fungible(points) => points,382 CollectionMode::ReFungible(_, points) => points,383 _ => 0384 };385386 387 ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, "Total collections bound exceeded");388389 390 ensure!(decimal_points <= 4, "decimal_points parameter must be lower than 4");391392 let mut name = collection_name.to_vec();393 name.push(0);394 ensure!(name.len() <= 64, "Collection name can not be longer than 63 char");395396 let mut description = collection_description.to_vec();397 description.push(0);398 ensure!(name.len() <= 256, "Collection description can not be longer than 255 char");399400 let mut prefix = token_prefix.to_vec();401 prefix.push(0);402 ensure!(prefix.len() <= 16, "Token prefix can not be longer than 15 char");403404 405 let next_id = CreatedCollectionCount::get()406 .checked_add(1)407 .expect("collection id error");408409 410 let total = CollectionCount::get()411 .checked_add(1)412 .expect("collection counter error");413414 CreatedCollectionCount::put(next_id);415 CollectionCount::put(total);416417 418 let new_collection = CollectionType {419 owner: who.clone(),420 name: name,421 mode: mode.clone(),422 mint_mode: false,423 access: AccessMode::Normal,424 description: description,425 decimal_points: decimal_points,426 token_prefix: prefix,427 offchain_schema: Vec::new(),428 custom_data_size: custom_data_size,429 sponsor: T::AccountId::default(),430 unconfirmed_sponsor: T::AccountId::default(),431 };432433 434 <Collection<T>>::insert(next_id, new_collection);435436 437 Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));438439 Ok(())440 }441442 443 444 445 446 447 448 449 450 451 #[weight = T::WeightInfo::destroy_collection()]452 pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {453454 let sender = ensure_signed(origin)?;455 Self::check_owner_permissions(collection_id, sender)?;456457 <AddressTokens<T>>::remove_prefix(collection_id);458 <ApprovedList<T>>::remove_prefix(collection_id);459 <Balance<T>>::remove_prefix(collection_id);460 <ItemListIndex>::remove(collection_id);461 <AdminList<T>>::remove(collection_id);462 <Collection<T>>::remove(collection_id);463 <WhiteList<T>>::remove(collection_id);464465 <NftItemList<T>>::remove_prefix(collection_id);466 <FungibleItemList<T>>::remove_prefix(collection_id);467 <ReFungibleItemList<T>>::remove_prefix(collection_id);468469 <NftTransferBasket<T>>::remove_prefix(collection_id);470 <FungibleTransferBasket<T>>::remove_prefix(collection_id);471 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);472473 if CollectionCount::get() > 0474 {475 476 let total = CollectionCount::get()477 .checked_sub(1)478 .expect("collection counter error");479480 CollectionCount::put(total);481 }482483 Ok(())484 }485486 487 488 489 490 491 492 493 494 495 496 497 498 #[weight = T::WeightInfo::add_to_white_list()]499 pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{500501 let sender = ensure_signed(origin)?;502 Self::check_owner_or_admin_permissions(collection_id, sender)?;503504 let mut white_list_collection: Vec<T::AccountId>;505 if <WhiteList<T>>::contains_key(collection_id) {506 white_list_collection = <WhiteList<T>>::get(collection_id);507 if !white_list_collection.contains(&address.clone())508 {509 white_list_collection.push(address.clone());510 }511 }512 else {513 white_list_collection = Vec::new();514 white_list_collection.push(address.clone());515 }516517 <WhiteList<T>>::insert(collection_id, white_list_collection);518 Ok(())519 }520521 522 523 524 525 526 527 528 529 530 531 532 533 #[weight = T::WeightInfo::remove_from_white_list()]534 pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{535536 let sender = ensure_signed(origin)?;537 Self::check_owner_or_admin_permissions(collection_id, sender)?;538539 if <WhiteList<T>>::contains_key(collection_id) {540 let mut white_list_collection = <WhiteList<T>>::get(collection_id);541 if white_list_collection.contains(&address.clone())542 {543 white_list_collection.retain(|i| *i != address.clone());544 <WhiteList<T>>::insert(collection_id, white_list_collection);545 }546 }547548 Ok(())549 }550551 552 553 554 555 556 557 558 559 560 561 562 #[weight = T::WeightInfo::set_public_access_mode()]563 pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult564 {565 let sender = ensure_signed(origin)?;566567 Self::check_owner_permissions(collection_id, sender)?;568 let mut target_collection = <Collection<T>>::get(collection_id);569 target_collection.access = mode;570 <Collection<T>>::insert(collection_id, target_collection);571572 Ok(())573 }574575 576 577 578 579 580 581 582 583 584 585 586 587 588 #[weight = T::WeightInfo::set_mint_permission()]589 pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult590 {591 let sender = ensure_signed(origin)?;592593 Self::check_owner_permissions(collection_id, sender)?;594 let mut target_collection = <Collection<T>>::get(collection_id);595 target_collection.mint_mode = mint_permission;596 <Collection<T>>::insert(collection_id, target_collection);597598 Ok(())599 }600601 602 603 604 605 606 607 608 609 610 611 612 #[weight = T::WeightInfo::change_collection_owner()]613 pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {614615 let sender = ensure_signed(origin)?;616 Self::check_owner_permissions(collection_id, sender)?;617 let mut target_collection = <Collection<T>>::get(collection_id);618 target_collection.owner = new_owner;619 <Collection<T>>::insert(collection_id, target_collection);620621 Ok(())622 }623624 625 626 627 628 629 630 631 632 633 634 635 636 637 #[weight = T::WeightInfo::add_collection_admin()]638 pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {639640 let sender = ensure_signed(origin)?;641 Self::check_owner_or_admin_permissions(collection_id, sender)?;642 let mut admin_arr: Vec<T::AccountId> = Vec::new();643644 if <AdminList<T>>::contains_key(collection_id)645 {646 admin_arr = <AdminList<T>>::get(collection_id);647 ensure!(!admin_arr.contains(&new_admin_id), "Account already has admin role");648 }649650 651 ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, "Number of collection admins bound exceeded");652653 admin_arr.push(new_admin_id);654 <AdminList<T>>::insert(collection_id, admin_arr);655656 Ok(())657 }658659 660 661 662 663 664 665 666 667 668 669 670 671 #[weight = T::WeightInfo::remove_collection_admin()]672 pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {673674 let sender = ensure_signed(origin)?;675 Self::check_owner_or_admin_permissions(collection_id, sender)?;676677 if <AdminList<T>>::contains_key(collection_id)678 {679 let mut admin_arr = <AdminList<T>>::get(collection_id);680 admin_arr.retain(|i| *i != account_id);681 <AdminList<T>>::insert(collection_id, admin_arr);682 }683684 Ok(())685 }686687 688 689 690 691 692 693 694 695 696 #[weight = T::WeightInfo::set_collection_sponsor()]697 pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {698699 let sender = ensure_signed(origin)?;700 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");701702 let mut target_collection = <Collection<T>>::get(collection_id);703 ensure!(sender == target_collection.owner, "You do not own this collection");704705 target_collection.unconfirmed_sponsor = new_sponsor;706 <Collection<T>>::insert(collection_id, target_collection);707708 Ok(())709 }710711 712 713 714 715 716 717 718 #[weight = T::WeightInfo::confirm_sponsorship()]719 pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {720721 let sender = ensure_signed(origin)?;722 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");723724 let mut target_collection = <Collection<T>>::get(collection_id);725 ensure!(sender == target_collection.unconfirmed_sponsor, "This address is not set as sponsor, use setCollectionSponsor first");726727 target_collection.sponsor = target_collection.unconfirmed_sponsor;728 target_collection.unconfirmed_sponsor = T::AccountId::default();729 <Collection<T>>::insert(collection_id, target_collection);730731 Ok(())732 }733734 735 736 737 738 739 740 741 742 743 #[weight = T::WeightInfo::remove_collection_sponsor()]744 pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {745746 let sender = ensure_signed(origin)?;747 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");748749 let mut target_collection = <Collection<T>>::get(collection_id);750 ensure!(sender == target_collection.owner, "You do not own this collection");751752 target_collection.sponsor = T::AccountId::default();753 <Collection<T>>::insert(collection_id, target_collection);754755 Ok(())756 }757758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781782 #[weight = T::WeightInfo::create_item(properties.len())]783 pub fn create_item(origin, collection_id: u64, properties: Vec<u8>, owner: T::AccountId) -> DispatchResult {784785 let sender = ensure_signed(origin)?;786 Self::collection_exists(collection_id)?;787 let target_collection = <Collection<T>>::get(collection_id);788789 if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {790 ensure!(target_collection.mint_mode == true, "Public minting is not allowed for this collection");791 Self::check_white_list(collection_id, &owner)?;792 Self::check_white_list(collection_id, &sender)?;793 }794795 match target_collection.mode796 {797 CollectionMode::NFT(_) => {798799 800 ensure!(target_collection.custom_data_size >= properties.len() as u32, "Size of item is too large");801802 803 let item = NftItemType {804 collection: collection_id,805 owner: owner,806 data: properties.clone(),807 };808809 Self::add_nft_item(item)?;810811 },812 CollectionMode::Fungible(_) => {813814 815 ensure!(properties.len() as u32 == 0, "Size of item must be 0 with fungible type");816817 let item = FungibleItemType {818 collection: collection_id,819 owner: owner,820 value: (10 as u128).pow(target_collection.decimal_points)821 };822823 Self::add_fungible_item(item)?;824 },825 CollectionMode::ReFungible(_, _) => {826827 828 ensure!(target_collection.custom_data_size >= properties.len() as u32, "Size of item is too large");829830 let mut owner_list = Vec::new();831 let value = (10 as u128).pow(target_collection.decimal_points);832 owner_list.push(Ownership {owner: owner.clone(), fraction: value});833834 let item = ReFungibleItemType {835 collection: collection_id,836 owner: owner_list,837 data: properties.clone()838 };839840 Self::add_refungible_item(item)?;841 },842 _ => { ensure!(1 == 0,"just error"); }843844 };845846 847 Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));848849 Ok(())850 }851852 853 854 855 856 857 858 859 860 861 862 863 864 865 #[weight = T::WeightInfo::burn_item()]866 pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {867868 let sender = ensure_signed(origin)?;869 Self::collection_exists(collection_id)?;870871 872 let target_collection = <Collection<T>>::get(collection_id);873 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||874 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),875 "Only item owner, collection owner and admins can modify item");876877 if target_collection.access == AccessMode::WhiteList {878 Self::check_white_list(collection_id, &sender)?;879 }880881 match target_collection.mode882 {883 CollectionMode::NFT(_) => Self::burn_nft_item(collection_id, item_id)?,884 CollectionMode::Fungible(_) => Self::burn_fungible_item(collection_id, item_id)?,885 CollectionMode::ReFungible(_, _) => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,886 _ => ()887 };888889 890 Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));891892 Ok(())893 }894895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 #[weight = T::WeightInfo::transfer()]919 pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {920921 let sender = ensure_signed(origin)?;922923 924 let target_collection = <Collection<T>>::get(collection_id);925 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||926 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),927 "Only item owner, collection owner and admins can modify item");928929 if target_collection.access == AccessMode::WhiteList {930 Self::check_white_list(collection_id, &sender)?;931 Self::check_white_list(collection_id, &recipient)?;932 }933934 match target_collection.mode935 {936 CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,937 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,938 CollectionMode::ReFungible(_, _) => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,939 _ => ()940 };941942 Ok(())943 }944945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 #[weight = T::WeightInfo::approve()]961 pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: 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 approve");970971 if target_collection.access == AccessMode::WhiteList {972 Self::check_white_list(collection_id, &sender)?;973 Self::check_white_list(collection_id, &approved)?;974 }975976 977 let amount = 100000000;978979 let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));980 if list_exists {981982 let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));983 let item_contains = list.iter().any(|i| i.approved == approved);984985 if !item_contains {986 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });987 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);988 }989 } else {990991 let mut list = Vec::new();992 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });993 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);994 }995996 Ok(())997 }998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 #[weight = T::WeightInfo::transfer_from()]1019 pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {10201021 let sender = ensure_signed(origin)?;1022 let mut appoved_transfer = false;10231024 1025 if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1026 let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1027 let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1028 if opt_item.is_some()1029 {1030 appoved_transfer = true;1031 ensure!(opt_item.unwrap().amount >= value, "Requested value more than approved");1032 }1033 }10341035 1036 let target_collection = <Collection<T>>::get(collection_id);1037 ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1038 "Only item owner, collection owner and admins can modify items");10391040 if target_collection.access == AccessMode::WhiteList {1041 Self::check_white_list(collection_id, &sender)?;1042 Self::check_white_list(collection_id, &recipient)?;1043 }10441045 1046 let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1047 .into_iter().filter(|i| i.approved != sender.clone()).collect();1048 <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);104910501051 match target_collection.mode1052 {1053 CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, from, recipient)?,1054 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1055 CollectionMode::ReFungible(_, _) => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1056 _ => ()1057 };10581059 Ok(())1060 }10611062 1063 #[weight = 0]1064 pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {10651066 1067 1068 1069 10701071 10721073 10741075 Ok(())1076 }10771078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 #[weight = T::WeightInfo::set_offchain_schema()]1091 pub fn set_offchain_schema(1092 origin,1093 collection_id: u64,1094 schema: Vec<u8>1095 ) -> DispatchResult {1096 let sender = ensure_signed(origin)?;1097 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;10981099 let mut target_collection = <Collection<T>>::get(collection_id);1100 target_collection.offchain_schema = schema;1101 <Collection<T>>::insert(collection_id, target_collection);11021103 Ok(())1104 }11051106 1107 #[weight = 0]1108 pub fn set_chain_limits(1109 origin,1110 limits: ChainLimits1111 ) -> DispatchResult {1112 ensure_root(origin)?;1113 <ChainLimit>::put(limits);1114 Ok(())1115 } 1116 }1117}11181119impl<T: Trait> Module<T> {1120 fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1121 let current_index = <ItemListIndex>::get(item.collection)1122 .checked_add(1)1123 .expect("Item list index id error");1124 let itemcopy = item.clone();1125 let owner = item.owner.clone();1126 let value = item.value as u64;11271128 Self::add_token_index(item.collection, current_index, owner.clone())?;11291130 <ItemListIndex>::insert(item.collection, current_index);1131 <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);11321133 1134 let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1135 <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1136 1137 1138 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1139 .checked_add(value)1140 .unwrap();1141 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);11421143 Ok(())1144 }11451146 fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1147 let current_index = <ItemListIndex>::get(item.collection)1148 .checked_add(1)1149 .expect("Item list index id error");1150 let itemcopy = item.clone();11511152 let value = item.owner.first().unwrap().fraction as u64;1153 let owner = item.owner.first().unwrap().owner.clone();11541155 Self::add_token_index(item.collection, current_index, owner.clone())?;11561157 <ItemListIndex>::insert(item.collection, current_index);1158 <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);11591160 1161 let block_number: T::BlockNumber = 0.into();1162 <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);11631164 1165 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1166 .checked_add(value)1167 .unwrap();1168 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);11691170 Ok(())1171 }11721173 fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1174 let current_index = <ItemListIndex>::get(item.collection)1175 .checked_add(1)1176 .expect("Item list index id error");11771178 let item_owner = item.owner.clone();1179 let collection_id = item.collection.clone();1180 Self::add_token_index(collection_id, current_index, item.owner.clone())?;11811182 <ItemListIndex>::insert(collection_id, current_index);1183 <NftItemList<T>>::insert(collection_id, current_index, item);11841185 1186 let block_number: T::BlockNumber = 0.into();1187 <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);11881189 1190 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1191 .checked_add(1)1192 .unwrap();1193 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);11941195 Ok(())1196 }11971198 fn burn_refungible_item(1199 collection_id: u64,1200 item_id: u64,1201 owner: T::AccountId,1202 ) -> DispatchResult {1203 ensure!(1204 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1205 "Item does not exists"1206 );1207 let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1208 let item = collection1209 .owner1210 .iter()1211 .filter(|&i| i.owner == owner)1212 .next()1213 .unwrap();1214 Self::remove_token_index(collection_id, item_id, owner.clone())?;12151216 1217 <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));12181219 1220 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1221 .checked_sub(item.fraction as u64)1222 .unwrap();1223 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);12241225 <ReFungibleItemList<T>>::remove(collection_id, item_id);12261227 Ok(())1228 }12291230 fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1231 ensure!(1232 <NftItemList<T>>::contains_key(collection_id, item_id),1233 "Item does not exists"1234 );1235 let item = <NftItemList<T>>::get(collection_id, item_id);1236 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;12371238 1239 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));12401241 1242 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1243 .checked_sub(1)1244 .unwrap();1245 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1246 <NftItemList<T>>::remove(collection_id, item_id);12471248 Ok(())1249 }12501251 fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1252 ensure!(1253 <FungibleItemList<T>>::contains_key(collection_id, item_id),1254 "Item does not exists"1255 );1256 let item = <FungibleItemList<T>>::get(collection_id, item_id);1257 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;12581259 1260 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));12611262 1263 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1264 .checked_sub(item.value as u64)1265 .unwrap();1266 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);12671268 <FungibleItemList<T>>::remove(collection_id, item_id);12691270 Ok(())1271 }12721273 fn collection_exists(collection_id: u64) -> DispatchResult {1274 ensure!(1275 <Collection<T>>::contains_key(collection_id),1276 "This collection does not exist"1277 );1278 Ok(())1279 }12801281 fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1282 Self::collection_exists(collection_id)?;12831284 let target_collection = <Collection<T>>::get(collection_id);1285 ensure!(1286 subject == target_collection.owner,1287 "You do not own this collection"1288 );12891290 Ok(())1291 }12921293 fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1294 let target_collection = <Collection<T>>::get(collection_id);1295 let mut result: bool = subject == target_collection.owner;1296 let exists = <AdminList<T>>::contains_key(collection_id);12971298 if !result & exists {1299 if <AdminList<T>>::get(collection_id).contains(&subject) {1300 result = true1301 }1302 }13031304 result1305 }13061307 fn check_owner_or_admin_permissions(1308 collection_id: u64,1309 subject: T::AccountId,1310 ) -> DispatchResult {1311 Self::collection_exists(collection_id)?;1312 let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());13131314 ensure!(1315 result,1316 "You do not have permissions to modify this collection"1317 );1318 Ok(())1319 }13201321 fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1322 let target_collection = <Collection<T>>::get(collection_id);13231324 match target_collection.mode {1325 CollectionMode::NFT(_) => {1326 <NftItemList<T>>::get(collection_id, item_id).owner == subject1327 }1328 CollectionMode::Fungible(_) => {1329 <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1330 }1331 CollectionMode::ReFungible(_, _) => {1332 <ReFungibleItemList<T>>::get(collection_id, item_id)1333 .owner1334 .iter()1335 .any(|i| i.owner == subject)1336 }1337 CollectionMode::Invalid => false,1338 }1339 }13401341 fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1342 let mes = "Address is not in white list";1343 ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1344 let wl = <WhiteList<T>>::get(collection_id);1345 ensure!(wl.contains(address), mes);13461347 Ok(())1348 }13491350 fn transfer_fungible(1351 collection_id: u64,1352 item_id: u64,1353 value: u64,1354 owner: T::AccountId,1355 new_owner: T::AccountId,1356 ) -> DispatchResult {1357 ensure!(1358 <FungibleItemList<T>>::contains_key(collection_id, item_id),1359 "Item not exists"1360 );13611362 let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1363 let amount = full_item.value;13641365 ensure!(amount >= value.into(), "Item balance not enouth");13661367 1368 let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1369 .checked_sub(value)1370 .unwrap();1371 <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);13721373 let mut new_owner_account_id = 0;1374 let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1375 if new_owner_items.len() > 0 {1376 new_owner_account_id = new_owner_items[0];1377 }13781379 let val64 = value.into();13801381 1382 if amount == val64 && new_owner_account_id == 0 {1383 1384 1385 let mut new_full_item = full_item.clone();1386 new_full_item.owner = new_owner.clone();1387 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);13881389 1390 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1391 .checked_add(value)1392 .unwrap();1393 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);13941395 1396 Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1397 } else {1398 let mut new_full_item = full_item.clone();1399 new_full_item.value -= val64;14001401 1402 if new_owner_account_id > 0 {1403 1404 let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1405 item.value += val64;14061407 1408 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1409 .checked_add(value)1410 .unwrap();1411 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);14121413 <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1414 } else {1415 1416 let item = FungibleItemType {1417 collection: collection_id,1418 owner: new_owner.clone(),1419 value: val64,1420 };14211422 Self::add_fungible_item(item)?;1423 }14241425 if amount == val64 {1426 Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;14271428 1429 <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1430 <FungibleItemList<T>>::remove(collection_id, item_id);1431 }14321433 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1434 }14351436 Ok(())1437 }14381439 fn transfer_refungible(1440 collection_id: u64,1441 item_id: u64,1442 value: u64,1443 owner: T::AccountId,1444 new_owner: T::AccountId,1445 ) -> DispatchResult {1446 ensure!(1447 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1448 "Item not exists"1449 );14501451 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1452 let item = full_item1453 .owner1454 .iter()1455 .filter(|i| i.owner == owner)1456 .next()1457 .unwrap();1458 let amount = item.fraction;14591460 ensure!(amount >= value.into(), "Item balance not enouth");14611462 1463 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1464 .checked_sub(value)1465 .unwrap();1466 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);14671468 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1469 .checked_add(value)1470 .unwrap();1471 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);14721473 let old_owner = item.owner.clone();1474 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1475 let val64 = value.into();14761477 1478 if amount == val64 && !new_owner_has_account {1479 1480 1481 let mut new_full_item = full_item.clone();1482 new_full_item1483 .owner1484 .iter_mut()1485 .find(|i| i.owner == owner)1486 .unwrap()1487 .owner = new_owner.clone();1488 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);14891490 1491 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1492 } else {1493 let mut new_full_item = full_item.clone();1494 new_full_item1495 .owner1496 .iter_mut()1497 .find(|i| i.owner == owner)1498 .unwrap()1499 .fraction -= val64;15001501 1502 if new_owner_has_account {1503 1504 new_full_item1505 .owner1506 .iter_mut()1507 .find(|i| i.owner == new_owner)1508 .unwrap()1509 .fraction += val64;1510 } else {1511 1512 new_full_item.owner.push(Ownership {1513 owner: new_owner.clone(),1514 fraction: val64,1515 });1516 Self::add_token_index(collection_id, item_id, new_owner.clone())?;1517 }15181519 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1520 }15211522 Ok(())1523 }15241525 fn transfer_nft(1526 collection_id: u64,1527 item_id: u64,1528 sender: T::AccountId,1529 new_owner: T::AccountId,1530 ) -> DispatchResult {1531 ensure!(1532 <NftItemList<T>>::contains_key(collection_id, item_id),1533 "Item not exists"1534 );15351536 let mut item = <NftItemList<T>>::get(collection_id, item_id);15371538 ensure!(1539 sender == item.owner,1540 "sender parameter and item owner must be equal"1541 );15421543 1544 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1545 .checked_sub(1)1546 .unwrap();1547 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);15481549 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1550 .checked_add(1)1551 .unwrap();1552 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);15531554 1555 let old_owner = item.owner.clone();1556 item.owner = new_owner.clone();1557 <NftItemList<T>>::insert(collection_id, item_id, item);15581559 1560 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;15611562 1563 <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1564 Ok(())1565 }15661567 fn init_collection(item: &CollectionType<T::AccountId>) {1568 1569 assert!(1570 item.decimal_points <= 4,1571 "decimal_points parameter must be lower than 4"1572 );1573 assert!(1574 item.name.len() <= 64,1575 "Collection name can not be longer than 63 char"1576 );1577 assert!(1578 item.name.len() <= 256,1579 "Collection description can not be longer than 255 char"1580 );1581 assert!(1582 item.token_prefix.len() <= 16,1583 "Token prefix can not be longer than 15 char"1584 );15851586 1587 let next_id = CreatedCollectionCount::get()1588 .checked_add(1)1589 .expect("collection id error");15901591 CreatedCollectionCount::put(next_id);1592 }15931594 fn init_nft_token(item: &NftItemType<T::AccountId>) {1595 let current_index = <ItemListIndex>::get(item.collection)1596 .checked_add(1)1597 .expect("Item list index id error");15981599 let item_owner = item.owner.clone();1600 let collection_id = item.collection.clone();1601 Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();16021603 <ItemListIndex>::insert(collection_id, current_index);16041605 1606 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1607 .checked_add(1)1608 .unwrap();1609 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);1610 }16111612 fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {1613 let current_index = <ItemListIndex>::get(item.collection)1614 .checked_add(1)1615 .expect("Item list index id error");1616 let owner = item.owner.clone();1617 let value = item.value as u64;16181619 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();16201621 <ItemListIndex>::insert(item.collection, current_index);16221623 1624 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1625 .checked_add(value)1626 .unwrap();1627 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1628 }16291630 fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {1631 let current_index = <ItemListIndex>::get(item.collection)1632 .checked_add(1)1633 .expect("Item list index id error");16341635 let value = item.owner.first().unwrap().fraction as u64;1636 let owner = item.owner.first().unwrap().owner.clone();16371638 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();16391640 <ItemListIndex>::insert(item.collection, current_index);16411642 1643 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1644 .checked_add(value)1645 .unwrap();1646 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1647 }16481649 fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {16501651 1652 if <AccountItemCount<T>>::contains_key(owner.clone()) {16531654 1655 let count = <AccountItemCount<T>>::get(owner.clone());1656 ensure!(count < ChainLimit::get().account_token_ownership_limit, "Owned tokens by a single address bound exceeded");16571658 <AccountItemCount<T>>::insert(owner.clone(), 1659 count.checked_add(1).unwrap());1660 }1661 else {1662 <AccountItemCount<T>>::insert(owner.clone(), 1);1663 }16641665 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1666 if list_exists {1667 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1668 let item_contains = list.contains(&item_index.clone());16691670 if !item_contains {1671 list.push(item_index.clone());1672 }16731674 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);1675 } else {1676 let mut itm = Vec::new();1677 itm.push(item_index.clone());1678 <AddressTokens<T>>::insert(collection_id, owner, itm);1679 1680 }16811682 Ok(())1683 }16841685 fn remove_token_index(1686 collection_id: u64,1687 item_index: u64,1688 owner: T::AccountId,1689 ) -> DispatchResult {16901691 1692 <AccountItemCount<T>>::insert(owner.clone(), 1693 <AccountItemCount<T>>::get(owner.clone()).checked_sub(1).unwrap());169416951696 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1697 if list_exists {1698 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1699 let item_contains = list.contains(&item_index.clone());17001701 if item_contains {1702 list.retain(|&item| item != item_index);1703 <AddressTokens<T>>::insert(collection_id, owner, list);1704 }1705 }17061707 Ok(())1708 }17091710 fn move_token_index(1711 collection_id: u64,1712 item_index: u64,1713 old_owner: T::AccountId,1714 new_owner: T::AccountId,1715 ) -> DispatchResult {1716 Self::remove_token_index(collection_id, item_index, old_owner)?;1717 Self::add_token_index(collection_id, item_index, new_owner)?;17181719 Ok(())1720 }1721}1722172317241725172617271728pub type Multiplier = FixedU128;17291730type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1731 <T as system::Trait>::AccountId,1732>>::Balance;1733type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1734 <T as system::Trait>::AccountId,1735>>::NegativeImbalance;1736173717381739#[derive(Encode, Decode, Clone, Eq, PartialEq)]1740pub struct ChargeTransactionPayment<T: transaction_payment::Trait + Send + Sync>(1741 #[codec(compact)] BalanceOf<T>,1742);17431744impl<T: Trait + transaction_payment::Trait + Send + Sync> sp_std::fmt::Debug1745 for ChargeTransactionPayment<T>1746{1747 #[cfg(feature = "std")]1748 fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1749 write!(f, "ChargeTransactionPayment<{:?}>", self.0)1750 }1751 #[cfg(not(feature = "std"))]1752 fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1753 Ok(())1754 }1755}17561757impl<T: Trait + transaction_payment::Trait + Send + Sync> ChargeTransactionPayment<T>1758where1759 T::Call:1760 Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>>,1761 BalanceOf<T>: Send + Sync + FixedPointOperand,1762{1763 1764 pub fn from(fee: BalanceOf<T>) -> Self {1765 Self(fee)1766 }17671768 pub fn traditional_fee(1769 len: usize,1770 info: &DispatchInfoOf<T::Call>,1771 tip: BalanceOf<T>,1772 ) -> BalanceOf<T>1773 where1774 T::Call: Dispatchable<Info = DispatchInfo>,1775 {1776 <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)1777 }17781779 fn withdraw_fee(1780 &self,1781 who: &T::AccountId,1782 call: &T::Call,1783 info: &DispatchInfoOf<T::Call>,1784 len: usize,1785 ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {1786 let tip = self.0;17871788 1789 1790 let fee = match call.is_sub_type() {1791 Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),1792 _ => Self::traditional_fee(len, info, tip), 1793 1794 };17951796 1797 1798 let sponsor: T::AccountId = match call.is_sub_type() {1799 Some(Call::create_item(collection_id, _properties, _owner)) => {1800 <Collection<T>>::get(collection_id).sponsor1801 }1802 Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {1803 let _collection_mode = <Collection<T>>::get(collection_id).mode;18041805 1806 let sponsor_transfer = match _collection_mode {1807 CollectionMode::NFT(_) => {1808 let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);1809 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;1810 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();1811 if block_number >= limit_time {1812 <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);1813 true1814 }1815 else {1816 false1817 }1818 }1819 CollectionMode::Fungible(_) => {1820 let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);1821 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;1822 if basket.iter().any(|i| i.address == _new_owner.clone())1823 {1824 let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();1825 let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();1826 if block_number >= limit_time {1827 basket.retain(|x| x.address == item.address);1828 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });1829 <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);1830 true1831 }1832 else {1833 false1834 }1835 }1836 else {1837 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});1838 true1839 }1840 }1841 CollectionMode::ReFungible(_, _) => {1842 let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);1843 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;1844 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();1845 if block_number >= limit_time {1846 <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);1847 true1848 } else {1849 false1850 }1851 }1852 _ => {1853 false1854 },1855 };18561857 if !sponsor_transfer {1858 T::AccountId::default()1859 } else {1860 <Collection<T>>::get(collection_id).sponsor1861 }1862 }18631864 _ => T::AccountId::default(),1865 };18661867 let mut who_pays_fee: T::AccountId = sponsor.clone();1868 if sponsor == T::AccountId::default() {1869 who_pays_fee = who.clone();1870 }18711872 1873 if fee.is_zero() {1874 return Ok((fee, None));1875 }18761877 match <T as transaction_payment::Trait>::Currency::withdraw(1878 &who_pays_fee,1879 fee,1880 if tip.is_zero() {1881 WithdrawReason::TransactionPayment.into()1882 } else {1883 WithdrawReason::TransactionPayment | WithdrawReason::Tip1884 },1885 ExistenceRequirement::KeepAlive,1886 ) {1887 Ok(imbalance) => Ok((fee, Some(imbalance))),1888 Err(_) => Err(InvalidTransaction::Payment.into()),1889 }1890 }1891}18921893impl<T: Trait + transaction_payment::Trait + Send + Sync> SignedExtension1894 for ChargeTransactionPayment<T>1895where1896 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,1897 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>>,1898{1899 const IDENTIFIER: &'static str = "ChargeTransactionPayment";1900 type AccountId = T::AccountId;1901 type Call = T::Call;1902 type AdditionalSigned = ();1903 type Pre = (1904 BalanceOf<T>,1905 Self::AccountId,1906 Option<NegativeImbalanceOf<T>>,1907 BalanceOf<T>,1908 );1909 fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {1910 Ok(())1911 }19121913 fn validate(1914 &self,1915 who: &Self::AccountId,1916 call: &Self::Call,1917 info: &DispatchInfoOf<Self::Call>,1918 len: usize,1919 ) -> TransactionValidity {1920 let (fee, _) = self.withdraw_fee(who, call, info, len)?;19211922 let mut r = ValidTransaction::default();1923 1924 1925 r.priority = fee.saturated_into::<TransactionPriority>();1926 Ok(r)1927 }19281929 fn pre_dispatch(1930 self,1931 who: &Self::AccountId,1932 call: &Self::Call,1933 info: &DispatchInfoOf<Self::Call>,1934 len: usize,1935 ) -> Result<Self::Pre, TransactionValidityError> {1936 let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;1937 Ok((self.0, who.clone(), imbalance, fee))1938 }19391940 fn post_dispatch(1941 pre: Self::Pre,1942 info: &DispatchInfoOf<Self::Call>,1943 post_info: &PostDispatchInfoOf<Self::Call>,1944 len: usize,1945 _result: &DispatchResult,1946 ) -> Result<(), TransactionValidityError> {1947 let (tip, who, imbalance, fee) = pre;1948 if let Some(payed) = imbalance {1949 let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(1950 len as u32, info, post_info, tip,1951 );1952 let refund = fee.saturating_sub(actual_fee);1953 let actual_payment =1954 match <T as transaction_payment::Trait>::Currency::deposit_into_existing(1955 &who, refund,1956 ) {1957 Ok(refund_imbalance) => {1958 1959 1960 match payed.offset(refund_imbalance) {1961 Ok(actual_payment) => actual_payment,1962 Err(_) => return Err(InvalidTransaction::Payment.into()),1963 }1964 }1965 1966 1967 Err(_) => payed,1968 };1969 let imbalances = actual_payment.split(tip);1970 <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(1971 Some(imbalances.0).into_iter().chain(Some(imbalances.1)),1972 );1973 }1974 Ok(())1975 }1976}197719781979