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, 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 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 207}208209pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {210 type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;211212 213 type WeightInfo: WeightInfo;214}215216#[cfg(feature = "runtime-benchmarks")]217mod benchmarking;218219220221decl_storage! {222 trait Store for Module<T: Trait> as Nft {223224 225 NextCollectionID: u64;226 CreatedCollectionCount: u64;227 ChainVersion: u64;228 ItemListIndex: map hasher(blake2_128_concat) u64 => u64;229230 231 pub ChainLimit get(fn chain_limit) config(): ChainLimits;232233 234 CollectionCount: u64;235 pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;236237 238 pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;239 pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;240 pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;241242 243 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;244245 246 pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;247248 249 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;250 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;251 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;252253 254 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;255256 257 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;258 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>>;259 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;260261 262 pub ContractOwner get(fn contract_owner): map hasher(identity) T::AccountId => T::AccountId;263 pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(identity) T::AccountId => bool;264 }265 add_extra_genesis {266 build(|config: &GenesisConfig<T>| {267 268 for (_num, _c) in &config.collection {269 <Module<T>>::init_collection(_c);270 }271272 for (_num, _q, _i) in &config.nft_item_id {273 <Module<T>>::init_nft_token(_i);274 }275276 for (_num, _q, _i) in &config.fungible_item_id {277 <Module<T>>::init_fungible_token(_i);278 }279280 for (_num, _q, _i) in &config.refungible_item_id {281 <Module<T>>::init_refungible_token(_i);282 }283 })284 }285}286287decl_event!(288 pub enum Event<T>289 where290 AccountId = <T as system::Trait>::AccountId,291 {292 293 294 295 296 297 298 299 300 301 Created(u64, u8, AccountId),302303 304 305 306 307 308 309 310 ItemCreated(u64, u64),311312 313 314 315 316 317 318 319 ItemDestroyed(u64, u64),320 }321);322323decl_module! {324 pub struct Module<T: Trait> for enum Call where origin: T::Origin {325326 fn deposit_event() = default;327328 fn on_initialize(now: T::BlockNumber) -> Weight {329330 if ChainVersion::get() < 2331 {332 let value = NextCollectionID::get();333 CreatedCollectionCount::put(value);334 ChainVersion::put(2);335 }336337 0338 }339340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 #[weight = T::WeightInfo::create_collection()]357 pub fn create_collection(origin,358 collection_name: Vec<u16>,359 collection_description: Vec<u16>,360 token_prefix: Vec<u8>,361 mode: CollectionMode) -> DispatchResult {362363 364 let who = ensure_signed(origin)?;365 let custom_data_size = match mode {366 CollectionMode::NFT(size) => {367368 369 ensure!(size < ChainLimit::get().custom_data_limit, "Custom data size bound exceeded");370 size371 },372 CollectionMode::ReFungible(size, _) => {373374 375 ensure!(size < ChainLimit::get().custom_data_limit, "Custom data size bound exceeded");376 size377 },378 _ => 0379 };380381 let decimal_points = match mode {382 CollectionMode::Fungible(points) => points,383 CollectionMode::ReFungible(_, points) => points,384 _ => 0385 };386387 388 ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, "Total collections bound exceeded");389390 391 ensure!(decimal_points <= 4, "decimal_points parameter must be lower than 4");392393 let mut name = collection_name.to_vec();394 name.push(0);395 ensure!(name.len() <= 64, "Collection name can not be longer than 63 char");396397 let mut description = collection_description.to_vec();398 description.push(0);399 ensure!(name.len() <= 256, "Collection description can not be longer than 255 char");400401 let mut prefix = token_prefix.to_vec();402 prefix.push(0);403 ensure!(prefix.len() <= 16, "Token prefix can not be longer than 15 char");404405 406 let next_id = CreatedCollectionCount::get()407 .checked_add(1)408 .expect("collection id error");409410 411 let total = CollectionCount::get()412 .checked_add(1)413 .expect("collection counter error");414415 CreatedCollectionCount::put(next_id);416 CollectionCount::put(total);417418 419 let new_collection = CollectionType {420 owner: who.clone(),421 name: name,422 mode: mode.clone(),423 mint_mode: false,424 access: AccessMode::Normal,425 description: description,426 decimal_points: decimal_points,427 token_prefix: prefix,428 offchain_schema: Vec::new(),429 custom_data_size: custom_data_size,430 sponsor: T::AccountId::default(),431 unconfirmed_sponsor: T::AccountId::default(),432 };433434 435 <Collection<T>>::insert(next_id, new_collection);436437 438 Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));439440 Ok(())441 }442443 444 445 446 447 448 449 450 451 452 #[weight = T::WeightInfo::destroy_collection()]453 pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {454455 let sender = ensure_signed(origin)?;456 Self::check_owner_permissions(collection_id, sender)?;457458 <AddressTokens<T>>::remove_prefix(collection_id);459 <ApprovedList<T>>::remove_prefix(collection_id);460 <Balance<T>>::remove_prefix(collection_id);461 <ItemListIndex>::remove(collection_id);462 <AdminList<T>>::remove(collection_id);463 <Collection<T>>::remove(collection_id);464 <WhiteList<T>>::remove(collection_id);465466 <NftItemList<T>>::remove_prefix(collection_id);467 <FungibleItemList<T>>::remove_prefix(collection_id);468 <ReFungibleItemList<T>>::remove_prefix(collection_id);469470 <NftTransferBasket<T>>::remove_prefix(collection_id);471 <FungibleTransferBasket<T>>::remove_prefix(collection_id);472 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);473474 if CollectionCount::get() > 0475 {476 477 let total = CollectionCount::get()478 .checked_sub(1)479 .expect("collection counter error");480481 CollectionCount::put(total);482 }483484 Ok(())485 }486487 488 489 490 491 492 493 494 495 496 497 498 499 #[weight = T::WeightInfo::add_to_white_list()]500 pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{501502 let sender = ensure_signed(origin)?;503 Self::check_owner_or_admin_permissions(collection_id, sender)?;504505 let mut white_list_collection: Vec<T::AccountId>;506 if <WhiteList<T>>::contains_key(collection_id) {507 white_list_collection = <WhiteList<T>>::get(collection_id);508 if !white_list_collection.contains(&address.clone())509 {510 white_list_collection.push(address.clone());511 }512 }513 else {514 white_list_collection = Vec::new();515 white_list_collection.push(address.clone());516 }517518 <WhiteList<T>>::insert(collection_id, white_list_collection);519 Ok(())520 }521522 523 524 525 526 527 528 529 530 531 532 533 534 #[weight = T::WeightInfo::remove_from_white_list()]535 pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{536537 let sender = ensure_signed(origin)?;538 Self::check_owner_or_admin_permissions(collection_id, sender)?;539540 if <WhiteList<T>>::contains_key(collection_id) {541 let mut white_list_collection = <WhiteList<T>>::get(collection_id);542 if white_list_collection.contains(&address.clone())543 {544 white_list_collection.retain(|i| *i != address.clone());545 <WhiteList<T>>::insert(collection_id, white_list_collection);546 }547 }548549 Ok(())550 }551552 553 554 555 556 557 558 559 560 561 562 563 #[weight = T::WeightInfo::set_public_access_mode()]564 pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult565 {566 let sender = ensure_signed(origin)?;567568 Self::check_owner_permissions(collection_id, sender)?;569 let mut target_collection = <Collection<T>>::get(collection_id);570 target_collection.access = mode;571 <Collection<T>>::insert(collection_id, target_collection);572573 Ok(())574 }575576 577 578 579 580 581 582 583 584 585 586 587 588 589 #[weight = T::WeightInfo::set_mint_permission()]590 pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult591 {592 let sender = ensure_signed(origin)?;593594 Self::check_owner_permissions(collection_id, sender)?;595 let mut target_collection = <Collection<T>>::get(collection_id);596 target_collection.mint_mode = mint_permission;597 <Collection<T>>::insert(collection_id, target_collection);598599 Ok(())600 }601602 603 604 605 606 607 608 609 610 611 612 613 #[weight = T::WeightInfo::change_collection_owner()]614 pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {615616 let sender = ensure_signed(origin)?;617 Self::check_owner_permissions(collection_id, sender)?;618 let mut target_collection = <Collection<T>>::get(collection_id);619 target_collection.owner = new_owner;620 <Collection<T>>::insert(collection_id, target_collection);621622 Ok(())623 }624625 626 627 628 629 630 631 632 633 634 635 636 637 638 #[weight = T::WeightInfo::add_collection_admin()]639 pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {640641 let sender = ensure_signed(origin)?;642 Self::check_owner_or_admin_permissions(collection_id, sender)?;643 let mut admin_arr: Vec<T::AccountId> = Vec::new();644645 if <AdminList<T>>::contains_key(collection_id)646 {647 admin_arr = <AdminList<T>>::get(collection_id);648 ensure!(!admin_arr.contains(&new_admin_id), "Account already has admin role");649 }650651 652 ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, "Number of collection admins bound exceeded");653654 admin_arr.push(new_admin_id);655 <AdminList<T>>::insert(collection_id, admin_arr);656657 Ok(())658 }659660 661 662 663 664 665 666 667 668 669 670 671 672 #[weight = T::WeightInfo::remove_collection_admin()]673 pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {674675 let sender = ensure_signed(origin)?;676 Self::check_owner_or_admin_permissions(collection_id, sender)?;677678 if <AdminList<T>>::contains_key(collection_id)679 {680 let mut admin_arr = <AdminList<T>>::get(collection_id);681 admin_arr.retain(|i| *i != account_id);682 <AdminList<T>>::insert(collection_id, admin_arr);683 }684685 Ok(())686 }687688 689 690 691 692 693 694 695 696 697 #[weight = T::WeightInfo::set_collection_sponsor()]698 pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {699700 let sender = ensure_signed(origin)?;701 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");702703 let mut target_collection = <Collection<T>>::get(collection_id);704 ensure!(sender == target_collection.owner, "You do not own this collection");705706 target_collection.unconfirmed_sponsor = new_sponsor;707 <Collection<T>>::insert(collection_id, target_collection);708709 Ok(())710 }711712 713 714 715 716 717 718 719 #[weight = T::WeightInfo::confirm_sponsorship()]720 pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {721722 let sender = ensure_signed(origin)?;723 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");724725 let mut target_collection = <Collection<T>>::get(collection_id);726 ensure!(sender == target_collection.unconfirmed_sponsor, "This address is not set as sponsor, use setCollectionSponsor first");727728 target_collection.sponsor = target_collection.unconfirmed_sponsor;729 target_collection.unconfirmed_sponsor = T::AccountId::default();730 <Collection<T>>::insert(collection_id, target_collection);731732 Ok(())733 }734735 736 737 738 739 740 741 742 743 744 #[weight = T::WeightInfo::remove_collection_sponsor()]745 pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {746747 let sender = ensure_signed(origin)?;748 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");749750 let mut target_collection = <Collection<T>>::get(collection_id);751 ensure!(sender == target_collection.owner, "You do not own this collection");752753 target_collection.sponsor = T::AccountId::default();754 <Collection<T>>::insert(collection_id, target_collection);755756 Ok(())757 }758759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782783 #[weight = T::WeightInfo::create_item(properties.len())]784 pub fn create_item(origin, collection_id: u64, properties: Vec<u8>, owner: T::AccountId) -> DispatchResult {785786 let sender = ensure_signed(origin)?;787 Self::collection_exists(collection_id)?;788 let target_collection = <Collection<T>>::get(collection_id);789790 if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {791 ensure!(target_collection.mint_mode == true, "Public minting is not allowed for this collection");792 Self::check_white_list(collection_id, &owner)?;793 Self::check_white_list(collection_id, &sender)?;794 }795796 match target_collection.mode797 {798 CollectionMode::NFT(_) => {799800 801 ensure!(target_collection.custom_data_size >= properties.len() as u32, "Size of item is too large");802803 804 let item = NftItemType {805 collection: collection_id,806 owner: owner,807 data: properties.clone(),808 };809810 Self::add_nft_item(item)?;811812 },813 CollectionMode::Fungible(_) => {814815 816 ensure!(properties.len() as u32 == 0, "Size of item must be 0 with fungible type");817818 let item = FungibleItemType {819 collection: collection_id,820 owner: owner,821 value: (10 as u128).pow(target_collection.decimal_points)822 };823824 Self::add_fungible_item(item)?;825 },826 CollectionMode::ReFungible(_, _) => {827828 829 ensure!(target_collection.custom_data_size >= properties.len() as u32, "Size of item is too large");830831 let mut owner_list = Vec::new();832 let value = (10 as u128).pow(target_collection.decimal_points);833 owner_list.push(Ownership {owner: owner.clone(), fraction: value});834835 let item = ReFungibleItemType {836 collection: collection_id,837 owner: owner_list,838 data: properties.clone()839 };840841 Self::add_refungible_item(item)?;842 },843 _ => { ensure!(1 == 0,"just error"); }844845 };846847 848 Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));849850 Ok(())851 }852853 854 855 856 857 858 859 860 861 862 863 864 865 866 #[weight = T::WeightInfo::burn_item()]867 pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {868869 let sender = ensure_signed(origin)?;870 Self::collection_exists(collection_id)?;871872 873 let target_collection = <Collection<T>>::get(collection_id);874 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||875 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),876 "Only item owner, collection owner and admins can modify item");877878 if target_collection.access == AccessMode::WhiteList {879 Self::check_white_list(collection_id, &sender)?;880 }881882 match target_collection.mode883 {884 CollectionMode::NFT(_) => Self::burn_nft_item(collection_id, item_id)?,885 CollectionMode::Fungible(_) => Self::burn_fungible_item(collection_id, item_id)?,886 CollectionMode::ReFungible(_, _) => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,887 _ => ()888 };889890 891 Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));892893 Ok(())894 }895896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 #[weight = T::WeightInfo::transfer()]920 pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {921922 let sender = ensure_signed(origin)?;923924 925 let target_collection = <Collection<T>>::get(collection_id);926 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||927 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),928 "Only item owner, collection owner and admins can modify item");929930 if target_collection.access == AccessMode::WhiteList {931 Self::check_white_list(collection_id, &sender)?;932 Self::check_white_list(collection_id, &recipient)?;933 }934935 match target_collection.mode936 {937 CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,938 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,939 CollectionMode::ReFungible(_, _) => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,940 _ => ()941 };942943 Ok(())944 }945946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 #[weight = T::WeightInfo::approve()]962 pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {963964 let sender = ensure_signed(origin)?;965966 967 let target_collection = <Collection<T>>::get(collection_id);968 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||969 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),970 "Only item owner, collection owner and admins can approve");971972 if target_collection.access == AccessMode::WhiteList {973 Self::check_white_list(collection_id, &sender)?;974 Self::check_white_list(collection_id, &approved)?;975 }976977 978 let amount = 100000000;979980 let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));981 if list_exists {982983 let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));984 let item_contains = list.iter().any(|i| i.approved == approved);985986 if !item_contains {987 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });988 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);989 }990 } else {991992 let mut list = Vec::new();993 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });994 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);995 }996997 Ok(())998 }999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 #[weight = T::WeightInfo::transfer_from()]1020 pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {10211022 let sender = ensure_signed(origin)?;1023 let mut appoved_transfer = false;10241025 1026 if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1027 let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1028 let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1029 if opt_item.is_some()1030 {1031 appoved_transfer = true;1032 ensure!(opt_item.unwrap().amount >= value, "Requested value more than approved");1033 }1034 }10351036 1037 let target_collection = <Collection<T>>::get(collection_id);1038 ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1039 "Only item owner, collection owner and admins can modify items");10401041 if target_collection.access == AccessMode::WhiteList {1042 Self::check_white_list(collection_id, &sender)?;1043 Self::check_white_list(collection_id, &recipient)?;1044 }10451046 1047 let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1048 .into_iter().filter(|i| i.approved != sender.clone()).collect();1049 <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);105010511052 match target_collection.mode1053 {1054 CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, from, recipient)?,1055 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1056 CollectionMode::ReFungible(_, _) => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1057 _ => ()1058 };10591060 Ok(())1061 }10621063 1064 #[weight = 0]1065 pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {10661067 1068 1069 1070 10711072 10731074 10751076 Ok(())1077 }10781079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 #[weight = T::WeightInfo::set_offchain_schema()]1092 pub fn set_offchain_schema(1093 origin,1094 collection_id: u64,1095 schema: Vec<u8>1096 ) -> DispatchResult {1097 let sender = ensure_signed(origin)?;1098 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;10991100 let mut target_collection = <Collection<T>>::get(collection_id);1101 target_collection.offchain_schema = schema;1102 <Collection<T>>::insert(collection_id, target_collection);11031104 Ok(())1105 }11061107 1108 #[weight = 0]1109 pub fn set_chain_limits(1110 origin,1111 limits: ChainLimits1112 ) -> DispatchResult {1113 ensure_root(origin)?;1114 <ChainLimit>::put(limits);1115 Ok(())1116 }11171118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 #[weight = 0]1130 pub fn enable_contract_sponsoring(1131 origin,1132 contract_address: T::AccountId,1133 enable: bool1134 ) -> DispatchResult {1135 let sender = ensure_signed(origin)?;1136 let mut is_owner = false;1137 if <ContractOwner<T>>::contains_key(contract_address.clone()) {1138 let owner = <ContractOwner<T>>::get(&contract_address);1139 is_owner = sender == owner;1140 }1141 ensure!(is_owner, "Only contract owner may call this method");11421143 <ContractSelfSponsoring<T>>::insert(contract_address, enable);1144 Ok(())1145 }11461147 }1148}11491150impl<T: Trait> Module<T> {1151 fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1152 let current_index = <ItemListIndex>::get(item.collection)1153 .checked_add(1)1154 .expect("Item list index id error");1155 let itemcopy = item.clone();1156 let owner = item.owner.clone();1157 let value = item.value as u64;11581159 Self::add_token_index(item.collection, current_index, owner.clone())?;11601161 <ItemListIndex>::insert(item.collection, current_index);1162 <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);11631164 1165 let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1166 <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1167 1168 1169 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1170 .checked_add(value)1171 .unwrap();1172 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);11731174 Ok(())1175 }11761177 fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1178 let current_index = <ItemListIndex>::get(item.collection)1179 .checked_add(1)1180 .expect("Item list index id error");1181 let itemcopy = item.clone();11821183 let value = item.owner.first().unwrap().fraction as u64;1184 let owner = item.owner.first().unwrap().owner.clone();11851186 Self::add_token_index(item.collection, current_index, owner.clone())?;11871188 <ItemListIndex>::insert(item.collection, current_index);1189 <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);11901191 1192 let block_number: T::BlockNumber = 0.into();1193 <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);11941195 1196 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1197 .checked_add(value)1198 .unwrap();1199 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);12001201 Ok(())1202 }12031204 fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1205 let current_index = <ItemListIndex>::get(item.collection)1206 .checked_add(1)1207 .expect("Item list index id error");12081209 let item_owner = item.owner.clone();1210 let collection_id = item.collection.clone();1211 Self::add_token_index(collection_id, current_index, item.owner.clone())?;12121213 <ItemListIndex>::insert(collection_id, current_index);1214 <NftItemList<T>>::insert(collection_id, current_index, item);12151216 1217 let block_number: T::BlockNumber = 0.into();1218 <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);12191220 1221 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1222 .checked_add(1)1223 .unwrap();1224 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);12251226 Ok(())1227 }12281229 fn burn_refungible_item(1230 collection_id: u64,1231 item_id: u64,1232 owner: T::AccountId,1233 ) -> DispatchResult {1234 ensure!(1235 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1236 "Item does not exists"1237 );1238 let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1239 let item = collection1240 .owner1241 .iter()1242 .filter(|&i| i.owner == owner)1243 .next()1244 .unwrap();1245 Self::remove_token_index(collection_id, item_id, owner.clone())?;12461247 1248 <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));12491250 1251 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1252 .checked_sub(item.fraction as u64)1253 .unwrap();1254 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);12551256 <ReFungibleItemList<T>>::remove(collection_id, item_id);12571258 Ok(())1259 }12601261 fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1262 ensure!(1263 <NftItemList<T>>::contains_key(collection_id, item_id),1264 "Item does not exists"1265 );1266 let item = <NftItemList<T>>::get(collection_id, item_id);1267 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;12681269 1270 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));12711272 1273 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1274 .checked_sub(1)1275 .unwrap();1276 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1277 <NftItemList<T>>::remove(collection_id, item_id);12781279 Ok(())1280 }12811282 fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1283 ensure!(1284 <FungibleItemList<T>>::contains_key(collection_id, item_id),1285 "Item does not exists"1286 );1287 let item = <FungibleItemList<T>>::get(collection_id, item_id);1288 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;12891290 1291 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));12921293 1294 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1295 .checked_sub(item.value as u64)1296 .unwrap();1297 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);12981299 <FungibleItemList<T>>::remove(collection_id, item_id);13001301 Ok(())1302 }13031304 fn collection_exists(collection_id: u64) -> DispatchResult {1305 ensure!(1306 <Collection<T>>::contains_key(collection_id),1307 "This collection does not exist"1308 );1309 Ok(())1310 }13111312 fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1313 Self::collection_exists(collection_id)?;13141315 let target_collection = <Collection<T>>::get(collection_id);1316 ensure!(1317 subject == target_collection.owner,1318 "You do not own this collection"1319 );13201321 Ok(())1322 }13231324 fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1325 let target_collection = <Collection<T>>::get(collection_id);1326 let mut result: bool = subject == target_collection.owner;1327 let exists = <AdminList<T>>::contains_key(collection_id);13281329 if !result & exists {1330 if <AdminList<T>>::get(collection_id).contains(&subject) {1331 result = true1332 }1333 }13341335 result1336 }13371338 fn check_owner_or_admin_permissions(1339 collection_id: u64,1340 subject: T::AccountId,1341 ) -> DispatchResult {1342 Self::collection_exists(collection_id)?;1343 let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());13441345 ensure!(1346 result,1347 "You do not have permissions to modify this collection"1348 );1349 Ok(())1350 }13511352 fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1353 let target_collection = <Collection<T>>::get(collection_id);13541355 match target_collection.mode {1356 CollectionMode::NFT(_) => {1357 <NftItemList<T>>::get(collection_id, item_id).owner == subject1358 }1359 CollectionMode::Fungible(_) => {1360 <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1361 }1362 CollectionMode::ReFungible(_, _) => {1363 <ReFungibleItemList<T>>::get(collection_id, item_id)1364 .owner1365 .iter()1366 .any(|i| i.owner == subject)1367 }1368 CollectionMode::Invalid => false,1369 }1370 }13711372 fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1373 let mes = "Address is not in white list";1374 ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1375 let wl = <WhiteList<T>>::get(collection_id);1376 ensure!(wl.contains(address), mes);13771378 Ok(())1379 }13801381 fn transfer_fungible(1382 collection_id: u64,1383 item_id: u64,1384 value: u64,1385 owner: T::AccountId,1386 new_owner: T::AccountId,1387 ) -> DispatchResult {1388 ensure!(1389 <FungibleItemList<T>>::contains_key(collection_id, item_id),1390 "Item not exists"1391 );13921393 let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1394 let amount = full_item.value;13951396 ensure!(amount >= value.into(), "Item balance not enouth");13971398 1399 let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1400 .checked_sub(value)1401 .unwrap();1402 <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);14031404 let mut new_owner_account_id = 0;1405 let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1406 if new_owner_items.len() > 0 {1407 new_owner_account_id = new_owner_items[0];1408 }14091410 let val64 = value.into();14111412 1413 if amount == val64 && new_owner_account_id == 0 {1414 1415 1416 let mut new_full_item = full_item.clone();1417 new_full_item.owner = new_owner.clone();1418 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);14191420 1421 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1422 .checked_add(value)1423 .unwrap();1424 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);14251426 1427 Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1428 } else {1429 let mut new_full_item = full_item.clone();1430 new_full_item.value -= val64;14311432 1433 if new_owner_account_id > 0 {1434 1435 let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1436 item.value += val64;14371438 1439 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1440 .checked_add(value)1441 .unwrap();1442 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);14431444 <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1445 } else {1446 1447 let item = FungibleItemType {1448 collection: collection_id,1449 owner: new_owner.clone(),1450 value: val64,1451 };14521453 Self::add_fungible_item(item)?;1454 }14551456 if amount == val64 {1457 Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;14581459 1460 <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1461 <FungibleItemList<T>>::remove(collection_id, item_id);1462 }14631464 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1465 }14661467 Ok(())1468 }14691470 fn transfer_refungible(1471 collection_id: u64,1472 item_id: u64,1473 value: u64,1474 owner: T::AccountId,1475 new_owner: T::AccountId,1476 ) -> DispatchResult {1477 ensure!(1478 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1479 "Item not exists"1480 );14811482 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1483 let item = full_item1484 .owner1485 .iter()1486 .filter(|i| i.owner == owner)1487 .next()1488 .unwrap();1489 let amount = item.fraction;14901491 ensure!(amount >= value.into(), "Item balance not enouth");14921493 1494 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1495 .checked_sub(value)1496 .unwrap();1497 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);14981499 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1500 .checked_add(value)1501 .unwrap();1502 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);15031504 let old_owner = item.owner.clone();1505 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1506 let val64 = value.into();15071508 1509 if amount == val64 && !new_owner_has_account {1510 1511 1512 let mut new_full_item = full_item.clone();1513 new_full_item1514 .owner1515 .iter_mut()1516 .find(|i| i.owner == owner)1517 .unwrap()1518 .owner = new_owner.clone();1519 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);15201521 1522 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1523 } else {1524 let mut new_full_item = full_item.clone();1525 new_full_item1526 .owner1527 .iter_mut()1528 .find(|i| i.owner == owner)1529 .unwrap()1530 .fraction -= val64;15311532 1533 if new_owner_has_account {1534 1535 new_full_item1536 .owner1537 .iter_mut()1538 .find(|i| i.owner == new_owner)1539 .unwrap()1540 .fraction += val64;1541 } else {1542 1543 new_full_item.owner.push(Ownership {1544 owner: new_owner.clone(),1545 fraction: val64,1546 });1547 Self::add_token_index(collection_id, item_id, new_owner.clone())?;1548 }15491550 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1551 }15521553 Ok(())1554 }15551556 fn transfer_nft(1557 collection_id: u64,1558 item_id: u64,1559 sender: T::AccountId,1560 new_owner: T::AccountId,1561 ) -> DispatchResult {1562 ensure!(1563 <NftItemList<T>>::contains_key(collection_id, item_id),1564 "Item not exists"1565 );15661567 let mut item = <NftItemList<T>>::get(collection_id, item_id);15681569 ensure!(1570 sender == item.owner,1571 "sender parameter and item owner must be equal"1572 );15731574 1575 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1576 .checked_sub(1)1577 .unwrap();1578 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);15791580 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1581 .checked_add(1)1582 .unwrap();1583 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);15841585 1586 let old_owner = item.owner.clone();1587 item.owner = new_owner.clone();1588 <NftItemList<T>>::insert(collection_id, item_id, item);15891590 1591 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;15921593 1594 <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1595 Ok(())1596 }15971598 fn init_collection(item: &CollectionType<T::AccountId>) {1599 1600 assert!(1601 item.decimal_points <= 4,1602 "decimal_points parameter must be lower than 4"1603 );1604 assert!(1605 item.name.len() <= 64,1606 "Collection name can not be longer than 63 char"1607 );1608 assert!(1609 item.name.len() <= 256,1610 "Collection description can not be longer than 255 char"1611 );1612 assert!(1613 item.token_prefix.len() <= 16,1614 "Token prefix can not be longer than 15 char"1615 );16161617 1618 let next_id = CreatedCollectionCount::get()1619 .checked_add(1)1620 .expect("collection id error");16211622 CreatedCollectionCount::put(next_id);1623 }16241625 fn init_nft_token(item: &NftItemType<T::AccountId>) {1626 let current_index = <ItemListIndex>::get(item.collection)1627 .checked_add(1)1628 .expect("Item list index id error");16291630 let item_owner = item.owner.clone();1631 let collection_id = item.collection.clone();1632 Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();16331634 <ItemListIndex>::insert(collection_id, current_index);16351636 1637 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1638 .checked_add(1)1639 .unwrap();1640 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);1641 }16421643 fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {1644 let current_index = <ItemListIndex>::get(item.collection)1645 .checked_add(1)1646 .expect("Item list index id error");1647 let owner = item.owner.clone();1648 let value = item.value as u64;16491650 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();16511652 <ItemListIndex>::insert(item.collection, current_index);16531654 1655 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1656 .checked_add(value)1657 .unwrap();1658 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1659 }16601661 fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {1662 let current_index = <ItemListIndex>::get(item.collection)1663 .checked_add(1)1664 .expect("Item list index id error");16651666 let value = item.owner.first().unwrap().fraction as u64;1667 let owner = item.owner.first().unwrap().owner.clone();16681669 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();16701671 <ItemListIndex>::insert(item.collection, current_index);16721673 1674 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1675 .checked_add(value)1676 .unwrap();1677 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1678 }16791680 fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {16811682 1683 if <AccountItemCount<T>>::contains_key(owner.clone()) {16841685 1686 let count = <AccountItemCount<T>>::get(owner.clone());1687 ensure!(count < ChainLimit::get().account_token_ownership_limit, "Owned tokens by a single address bound exceeded");16881689 <AccountItemCount<T>>::insert(owner.clone(), 1690 count.checked_add(1).unwrap());1691 }1692 else {1693 <AccountItemCount<T>>::insert(owner.clone(), 1);1694 }16951696 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.push(item_index.clone());1703 }17041705 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);1706 } else {1707 let mut itm = Vec::new();1708 itm.push(item_index.clone());1709 <AddressTokens<T>>::insert(collection_id, owner, itm);1710 1711 }17121713 Ok(())1714 }17151716 fn remove_token_index(1717 collection_id: u64,1718 item_index: u64,1719 owner: T::AccountId,1720 ) -> DispatchResult {17211722 1723 <AccountItemCount<T>>::insert(owner.clone(), 1724 <AccountItemCount<T>>::get(owner.clone()).checked_sub(1).unwrap());172517261727 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1728 if list_exists {1729 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1730 let item_contains = list.contains(&item_index.clone());17311732 if item_contains {1733 list.retain(|&item| item != item_index);1734 <AddressTokens<T>>::insert(collection_id, owner, list);1735 }1736 }17371738 Ok(())1739 }17401741 fn move_token_index(1742 collection_id: u64,1743 item_index: u64,1744 old_owner: T::AccountId,1745 new_owner: T::AccountId,1746 ) -> DispatchResult {1747 Self::remove_token_index(collection_id, item_index, old_owner)?;1748 Self::add_token_index(collection_id, item_index, new_owner)?;17491750 Ok(())1751 }1752}1753175417551756175717581759pub type Multiplier = FixedU128;17601761type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1762 <T as system::Trait>::AccountId,1763>>::Balance;1764type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1765 <T as system::Trait>::AccountId,1766>>::NegativeImbalance;1767176817691770#[derive(Encode, Decode, Clone, Eq, PartialEq)]1771pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(1772 #[codec(compact)] BalanceOf<T>1773);17741775impl<T: Trait + Send + Sync> sp_std::fmt::Debug1776 for ChargeTransactionPayment<T>1777{1778 #[cfg(feature = "std")]1779 fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1780 write!(f, "ChargeTransactionPayment<{:?}>", self.0)1781 }1782 #[cfg(not(feature = "std"))]1783 fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1784 Ok(())1785 }1786}17871788impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>1789where1790 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,1791 BalanceOf<T>: Send + Sync + FixedPointOperand,1792{1793 1794 pub fn from(fee: BalanceOf<T>) -> Self {1795 Self(fee)1796 }17971798 pub fn traditional_fee(1799 len: usize,1800 info: &DispatchInfoOf<T::Call>,1801 tip: BalanceOf<T>,1802 ) -> BalanceOf<T>1803 where1804 T::Call: Dispatchable<Info = DispatchInfo>,1805 {1806 <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)1807 }18081809 fn withdraw_fee(1810 &self,1811 who: &T::AccountId,1812 call: &T::Call,1813 info: &DispatchInfoOf<T::Call>,1814 len: usize,1815 ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {1816 let tip = self.0;18171818 1819 1820 1821 1822 1823 1824 1825 let fee = Self::traditional_fee(len, info, tip);18261827 1828 1829 let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {1830 Some(Call::create_item(collection_id, _properties, _owner)) => {1831 <Collection<T>>::get(collection_id).sponsor1832 }1833 Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {1834 let _collection_mode = <Collection<T>>::get(collection_id).mode;18351836 1837 let sponsor_transfer = match _collection_mode {1838 CollectionMode::NFT(_) => {1839 let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);1840 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;1841 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();1842 if block_number >= limit_time {1843 <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);1844 true1845 }1846 else {1847 false1848 }1849 }1850 CollectionMode::Fungible(_) => {1851 let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);1852 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;1853 if basket.iter().any(|i| i.address == _new_owner.clone())1854 {1855 let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();1856 let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();1857 if block_number >= limit_time {1858 basket.retain(|x| x.address == item.address);1859 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });1860 <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);1861 true1862 }1863 else {1864 false1865 }1866 }1867 else {1868 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});1869 true1870 }1871 }1872 CollectionMode::ReFungible(_, _) => {1873 let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);1874 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;1875 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();1876 if block_number >= limit_time {1877 <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);1878 true1879 } else {1880 false1881 }1882 }1883 _ => {1884 false1885 },1886 };18871888 if !sponsor_transfer {1889 T::AccountId::default()1890 } else {1891 <Collection<T>>::get(collection_id).sponsor1892 }1893 }18941895 _ => T::AccountId::default(),1896 };18971898 1899 sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {19001901 1902 Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {19031904 let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(1905 code_hash,1906 &data,1907 &who,1908 );1909 <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());19101911 T::AccountId::default()1912 },19131914 1915 Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {19161917 let mut sp = T::AccountId::default();1918 let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());1919 if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {1920 if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {1921 sp = called_contract;1922 }1923 }19241925 sp1926 },19271928 _ => sponsor,1929 };19301931 let mut who_pays_fee: T::AccountId = sponsor.clone();1932 if sponsor == T::AccountId::default() {1933 who_pays_fee = who.clone();1934 }19351936 1937 if fee.is_zero() {1938 return Ok((fee, None));1939 }19401941 match <T as transaction_payment::Trait>::Currency::withdraw(1942 &who_pays_fee,1943 fee,1944 if tip.is_zero() {1945 WithdrawReason::TransactionPayment.into()1946 } else {1947 WithdrawReason::TransactionPayment | WithdrawReason::Tip1948 },1949 ExistenceRequirement::KeepAlive,1950 ) {1951 Ok(imbalance) => Ok((fee, Some(imbalance))),1952 Err(_) => Err(InvalidTransaction::Payment.into()),1953 }1954 }1955}195619571958impl<T: Trait + Send + Sync> SignedExtension1959 for ChargeTransactionPayment<T>1960where1961 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,1962 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,1963{1964 const IDENTIFIER: &'static str = "ChargeTransactionPayment";1965 type AccountId = T::AccountId;1966 type Call = T::Call;1967 type AdditionalSigned = ();1968 type Pre = (1969 BalanceOf<T>,1970 Self::AccountId,1971 Option<NegativeImbalanceOf<T>>,1972 BalanceOf<T>,1973 );1974 fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {1975 Ok(())1976 }19771978 fn validate(1979 &self,1980 _who: &Self::AccountId,1981 _call: &Self::Call,1982 _info: &DispatchInfoOf<Self::Call>,1983 _len: usize,1984 ) -> TransactionValidity {1985 Ok(ValidTransaction::default())1986 }19871988 fn pre_dispatch(1989 self,1990 who: &Self::AccountId,1991 call: &Self::Call,1992 info: &DispatchInfoOf<Self::Call>,1993 len: usize,1994 ) -> Result<Self::Pre, TransactionValidityError> {1995 let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;1996 Ok((self.0, who.clone(), imbalance, fee))1997 }19981999 fn post_dispatch(2000 pre: Self::Pre,2001 info: &DispatchInfoOf<Self::Call>,2002 post_info: &PostDispatchInfoOf<Self::Call>,2003 len: usize,2004 _result: &DispatchResult,2005 ) -> Result<(), TransactionValidityError> {2006 let (tip, who, imbalance, fee) = pre;2007 if let Some(payed) = imbalance {2008 let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2009 len as u32, info, post_info, tip,2010 );2011 let refund = fee.saturating_sub(actual_fee);2012 let actual_payment =2013 match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2014 &who, refund,2015 ) {2016 Ok(refund_imbalance) => {2017 2018 2019 match payed.offset(refund_imbalance) {2020 Ok(actual_payment) => actual_payment,2021 Err(_) => return Err(InvalidTransaction::Payment.into()),2022 }2023 }2024 2025 2026 Err(_) => payed,2027 };2028 let imbalances = actual_payment.split(tip);2029 <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2030 Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2031 );2032 }2033 Ok(())2034 }2035}2036203720382039