difftreelog
feat sponsor token property modifications
in: master
3 files changed
pallets/unique/src/lib.rsdiffbeforeafterboth--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -242,6 +242,7 @@
/// Collection id (controlled?2), token id (controlled?2)
#[deprecated]
pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;
+ pub TokenPropertyBasket get(fn token_property_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;
/// Approval sponsoring
pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;
runtime/common/src/eth_sponsoring.rsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! Implements EVM sponsoring logic via TransactionValidityHack1819use evm_coder::{Call, abi::AbiReader};20use pallet_common::{CollectionHandle, eth::map_eth_to_id};21use sp_core::H160;22use sp_std::prelude::*;23use up_sponsorship::SponsorshipHandler;24use core::marker::PhantomData;25use core::convert::TryInto;26use pallet_evm::account::CrossAccountId;27use up_data_structs::{TokenId, CreateItemData, CreateNftData, CollectionMode};28use pallet_unique::Config as UniqueConfig;2930use crate::sponsoring::*;3132use pallet_nonfungible::erc::{33 UniqueNFTCall, ERC721UniqueExtensionsCall, ERC721MintableCall, ERC721Call,34};35use pallet_fungible::erc::{UniqueFungibleCall, ERC20Call};36use pallet_fungible::Config as FungibleConfig;37use pallet_nonfungible::Config as NonfungibleConfig;38use pallet_refungible::Config as RefungibleConfig;3940pub struct UniqueEthSponsorshipHandler<T: UniqueConfig>(PhantomData<*const T>);41impl<T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig>42 SponsorshipHandler<T::CrossAccountId, (H160, Vec<u8>)> for UniqueEthSponsorshipHandler<T>43{44 fn get_sponsor(who: &T::CrossAccountId, call: &(H160, Vec<u8>)) -> Option<T::CrossAccountId> {45 let collection_id = map_eth_to_id(&call.0)?;46 let collection = <CollectionHandle<T>>::new(collection_id)?;47 let sponsor = collection.sponsorship.sponsor()?.clone();48 let (method_id, mut reader) = AbiReader::new_call(&call.1).ok()?;49 Some(T::CrossAccountId::from_sub(match &collection.mode {50 CollectionMode::NFT => {51 let call = <UniqueNFTCall<T>>::parse(method_id, &mut reader).ok()??;52 match call {53 UniqueNFTCall::ERC721UniqueExtensions(54 ERC721UniqueExtensionsCall::Transfer { token_id, .. },55 ) => {56 let token_id: TokenId = token_id.try_into().ok()?;57 withdraw_transfer::<T>(&collection, &who, &token_id).map(|()| sponsor)58 }59 UniqueNFTCall::ERC721Mintable(60 ERC721MintableCall::Mint { token_id, .. }61 | ERC721MintableCall::MintWithTokenUri { token_id, .. },62 ) => {63 let _token_id: TokenId = token_id.try_into().ok()?;64 withdraw_create_item::<T>(65 &collection,66 &who,67 &CreateItemData::NFT(CreateNftData::default()),68 )69 .map(|()| sponsor)70 }71 UniqueNFTCall::ERC721(ERC721Call::TransferFrom { token_id, from, .. }) => {72 let token_id: TokenId = token_id.try_into().ok()?;73 let from = T::CrossAccountId::from_eth(from);74 withdraw_transfer::<T>(&collection, &from, &token_id).map(|()| sponsor)75 }76 UniqueNFTCall::ERC721(ERC721Call::Approve { token_id, .. }) => {77 let token_id: TokenId = token_id.try_into().ok()?;78 withdraw_approve::<T>(&collection, who.as_sub(), &token_id)79 .map(|()| sponsor)80 }81 _ => None,82 }83 }84 CollectionMode::Fungible(_) => {85 let call = <UniqueFungibleCall<T>>::parse(method_id, &mut reader).ok()??;86 #[allow(clippy::single_match)]87 match call {88 UniqueFungibleCall::ERC20(ERC20Call::Transfer { .. }) => {89 withdraw_transfer::<T>(&collection, who, &TokenId::default())90 .map(|()| sponsor)91 }92 UniqueFungibleCall::ERC20(ERC20Call::TransferFrom { from, .. }) => {93 let from = T::CrossAccountId::from_eth(from);94 withdraw_transfer::<T>(&collection, &from, &TokenId::default())95 .map(|()| sponsor)96 }97 UniqueFungibleCall::ERC20(ERC20Call::Approve { .. }) => {98 withdraw_approve::<T>(&collection, who.as_sub(), &TokenId::default())99 .map(|()| sponsor)100 }101 _ => None,102 }103 }104 _ => None,105 }?))106 }107}1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! Implements EVM sponsoring logic via TransactionValidityHack1819use evm_coder::{Call, abi::AbiReader};20use pallet_common::{CollectionHandle, eth::map_eth_to_id};21use sp_core::H160;22use sp_std::prelude::*;23use up_sponsorship::SponsorshipHandler;24use core::marker::PhantomData;25use core::convert::TryInto;26use pallet_evm::account::CrossAccountId;27use up_data_structs::{TokenId, CreateItemData, CreateNftData, CollectionMode};28use pallet_unique::Config as UniqueConfig;2930use crate::sponsoring::*;3132use pallet_nonfungible::erc::{33 UniqueNFTCall, ERC721UniqueExtensionsCall, ERC721MintableCall, ERC721Call, TokenPropertiesCall,34};35use pallet_fungible::erc::{UniqueFungibleCall, ERC20Call};36use pallet_fungible::Config as FungibleConfig;37use pallet_nonfungible::Config as NonfungibleConfig;38use pallet_refungible::Config as RefungibleConfig;3940pub struct UniqueEthSponsorshipHandler<T: UniqueConfig>(PhantomData<*const T>);41impl<T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig>42 SponsorshipHandler<T::CrossAccountId, (H160, Vec<u8>)> for UniqueEthSponsorshipHandler<T>43{44 fn get_sponsor(who: &T::CrossAccountId, call: &(H160, Vec<u8>)) -> Option<T::CrossAccountId> {45 let collection_id = map_eth_to_id(&call.0)?;46 let collection = <CollectionHandle<T>>::new(collection_id)?;47 let sponsor = collection.sponsorship.sponsor()?.clone();48 let (method_id, mut reader) = AbiReader::new_call(&call.1).ok()?;49 Some(T::CrossAccountId::from_sub(match &collection.mode {50 CollectionMode::NFT => {51 let call = <UniqueNFTCall<T>>::parse(method_id, &mut reader).ok()??;52 match call {53 UniqueNFTCall::TokenProperties(54 TokenPropertiesCall::SetProperty { token_id, key, value, .. },55 ) => {56 let token_id: TokenId = token_id.try_into().ok()?;57 withdraw_set_token_property::<T>(58 &collection,59 &who,60 &token_id,61 key.len() + value.len(),62 ).map(|()| sponsor)63 }64 UniqueNFTCall::ERC721UniqueExtensions(65 ERC721UniqueExtensionsCall::Transfer { token_id, .. },66 ) => {67 let token_id: TokenId = token_id.try_into().ok()?;68 withdraw_transfer::<T>(&collection, &who, &token_id).map(|()| sponsor)69 }70 UniqueNFTCall::ERC721Mintable(71 ERC721MintableCall::Mint { token_id, .. }72 | ERC721MintableCall::MintWithTokenUri { token_id, .. },73 ) => {74 let _token_id: TokenId = token_id.try_into().ok()?;75 withdraw_create_item::<T>(76 &collection,77 &who,78 &CreateItemData::NFT(CreateNftData::default()),79 )80 .map(|()| sponsor)81 }82 UniqueNFTCall::ERC721(ERC721Call::TransferFrom { token_id, from, .. }) => {83 let token_id: TokenId = token_id.try_into().ok()?;84 let from = T::CrossAccountId::from_eth(from);85 withdraw_transfer::<T>(&collection, &from, &token_id).map(|()| sponsor)86 }87 UniqueNFTCall::ERC721(ERC721Call::Approve { token_id, .. }) => {88 let token_id: TokenId = token_id.try_into().ok()?;89 withdraw_approve::<T>(&collection, who.as_sub(), &token_id)90 .map(|()| sponsor)91 }92 _ => None,93 }94 }95 CollectionMode::Fungible(_) => {96 let call = <UniqueFungibleCall<T>>::parse(method_id, &mut reader).ok()??;97 #[allow(clippy::single_match)]98 match call {99 UniqueFungibleCall::ERC20(ERC20Call::Transfer { .. }) => {100 withdraw_transfer::<T>(&collection, who, &TokenId::default())101 .map(|()| sponsor)102 }103 UniqueFungibleCall::ERC20(ERC20Call::TransferFrom { from, .. }) => {104 let from = T::CrossAccountId::from_eth(from);105 withdraw_transfer::<T>(&collection, &from, &TokenId::default())106 .map(|()| sponsor)107 }108 UniqueFungibleCall::ERC20(ERC20Call::Approve { .. }) => {109 withdraw_approve::<T>(&collection, who.as_sub(), &TokenId::default())110 .map(|()| sponsor)111 }112 _ => None,113 }114 }115 _ => None,116 }?))117 }118}runtime/common/src/sponsoring.rsdiffbeforeafterboth--- a/runtime/common/src/sponsoring.rs
+++ b/runtime/common/src/sponsoring.rs
@@ -29,8 +29,8 @@
use pallet_evm::account::CrossAccountId;
use pallet_unique::{
Call as UniqueCall, Config as UniqueConfig, FungibleApproveBasket, RefungibleApproveBasket,
- NftApproveBasket, CreateItemBasket, ReFungibleTransferBasket, FungibleTransferBasket,
- NftTransferBasket,
+ NftApproveBasket, CreateItemBasket, ReFungibleTransferBasket,
+ FungibleTransferBasket, NftTransferBasket, TokenPropertyBasket,
};
use pallet_fungible::Config as FungibleConfig;
use pallet_nonfungible::Config as NonfungibleConfig;
@@ -39,6 +39,51 @@
pub trait Config: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig {}
impl<T> Config for T where T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig {}
+// TODO: permission check?
+pub fn withdraw_set_token_property<T: Config>(
+ collection: &CollectionHandle<T>,
+ who: &T::CrossAccountId,
+ item_id: &TokenId,
+ data_size: usize,
+) -> Option<()> {
+ // preliminary sponsoring correctness check
+ match collection.mode {
+ CollectionMode::NFT => {
+ let owner = pallet_nonfungible::TokenData::<T>::get((collection.id, item_id))?.owner;
+ if !owner.conv_eq(who) {
+ return None;
+ }
+ }
+ CollectionMode::Fungible(_) => {
+ // Fungible tokens have no properties
+ return None;
+ }
+ CollectionMode::ReFungible => {
+ if !<pallet_refungible::Owned<T>>::get((collection.id, who, item_id)) {
+ return None;
+ }
+ }
+ }
+
+ if data_size > collection.limits.sponsored_data_size() as usize {
+ return None;
+ }
+
+ let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
+ let limit = collection.limits.sponsored_data_rate_limit()?;
+
+ if let Some(last_tx_block) = TokenPropertyBasket::<T>::get(collection.id, item_id) {
+ let timeout = last_tx_block + limit.into();
+ if block_number < timeout {
+ return None;
+ }
+ }
+
+ <TokenPropertyBasket<T>>::insert(collection.id, item_id, block_number);
+
+ Some(())
+}
+
pub fn withdraw_transfer<T: Config>(
collection: &CollectionHandle<T>,
who: &T::CrossAccountId,
@@ -190,6 +235,22 @@
{
fn get_sponsor(who: &T::AccountId, call: &C) -> Option<T::AccountId> {
match IsSubType::<UniqueCall<T>>::is_sub_type(call)? {
+ UniqueCall::set_token_properties {
+ collection_id,
+ token_id,
+ properties,
+ ..
+ } => {
+ let (sponsor, collection) = load::<T>(*collection_id)?;
+ withdraw_set_token_property(
+ &collection,
+ &T::CrossAccountId::from_sub(who.clone()),
+ &token_id,
+ // No overflow may happen, as data larger than usize can't reach here
+ properties.iter().map(|p| p.key.len() + p.value.len()).sum()
+ )
+ .map(|()| sponsor)
+ }
UniqueCall::create_item {
collection_id,
data,