--- /dev/null +++ b/pallets/balances-adapter/src/common.rs @@ -0,0 +1,352 @@ +use alloc::vec::Vec; +use core::marker::PhantomData; +use crate::{Config, NativeFungibleHandle}; +use pallet_common::{CommonCollectionOperations, CommonWeightInfo}; + +pub struct CommonWeights(PhantomData); +impl CommonWeightInfo for CommonWeights { + fn create_multiple_items( + amount: &[up_data_structs::CreateItemData], + ) -> frame_support::weights::Weight { + todo!() + } + + fn create_multiple_items_ex( + cost: &up_data_structs::CreateItemExData, + ) -> frame_support::weights::Weight { + todo!() + } + + fn burn_item() -> frame_support::weights::Weight { + todo!() + } + + fn set_collection_properties(amount: u32) -> frame_support::weights::Weight { + todo!() + } + + fn delete_collection_properties(amount: u32) -> frame_support::weights::Weight { + todo!() + } + + fn set_token_properties(amount: u32) -> frame_support::weights::Weight { + todo!() + } + + fn delete_token_properties(amount: u32) -> frame_support::weights::Weight { + todo!() + } + + fn set_token_property_permissions(amount: u32) -> frame_support::weights::Weight { + todo!() + } + + fn transfer() -> frame_support::weights::Weight { + todo!() + } + + fn approve() -> frame_support::weights::Weight { + todo!() + } + + fn approve_from() -> frame_support::weights::Weight { + todo!() + } + + fn transfer_from() -> frame_support::weights::Weight { + todo!() + } + + fn burn_from() -> frame_support::weights::Weight { + todo!() + } + + fn burn_recursively_self_raw() -> frame_support::weights::Weight { + todo!() + } + + fn burn_recursively_breadth_raw(amount: u32) -> frame_support::weights::Weight { + todo!() + } + + fn token_owner() -> frame_support::weights::Weight { + todo!() + } + + fn set_allowance_for_all() -> frame_support::weights::Weight { + todo!() + } + + fn force_repair_item() -> frame_support::weights::Weight { + todo!() + } +} + +/// Implementation of `CommonCollectionOperations` for `FungibleHandle`. It wraps FungibleHandle Pallete +/// methods and adds weight info. +impl CommonCollectionOperations for NativeFungibleHandle { + fn create_item( + &self, + sender: ::CrossAccountId, + to: ::CrossAccountId, + data: up_data_structs::CreateItemData, + nesting_budget: &dyn up_data_structs::budget::Budget, + ) -> frame_support::pallet_prelude::DispatchResultWithPostInfo { + todo!() + } + + fn create_multiple_items( + &self, + sender: ::CrossAccountId, + to: ::CrossAccountId, + data: Vec, + nesting_budget: &dyn up_data_structs::budget::Budget, + ) -> frame_support::pallet_prelude::DispatchResultWithPostInfo { + todo!() + } + + fn create_multiple_items_ex( + &self, + sender: ::CrossAccountId, + data: up_data_structs::CreateItemExData<::CrossAccountId>, + nesting_budget: &dyn up_data_structs::budget::Budget, + ) -> frame_support::pallet_prelude::DispatchResultWithPostInfo { + todo!() + } + + fn burn_item( + &self, + sender: ::CrossAccountId, + token: up_data_structs::TokenId, + amount: u128, + ) -> frame_support::pallet_prelude::DispatchResultWithPostInfo { + todo!() + } + + fn burn_item_recursively( + &self, + sender: ::CrossAccountId, + token: up_data_structs::TokenId, + self_budget: &dyn up_data_structs::budget::Budget, + breadth_budget: &dyn up_data_structs::budget::Budget, + ) -> frame_support::pallet_prelude::DispatchResultWithPostInfo { + todo!() + } + + fn set_collection_properties( + &self, + sender: ::CrossAccountId, + properties: Vec, + ) -> frame_support::pallet_prelude::DispatchResultWithPostInfo { + todo!() + } + + fn delete_collection_properties( + &self, + sender: &::CrossAccountId, + property_keys: Vec, + ) -> frame_support::pallet_prelude::DispatchResultWithPostInfo { + todo!() + } + + fn set_token_properties( + &self, + sender: ::CrossAccountId, + token_id: up_data_structs::TokenId, + properties: Vec, + budget: &dyn up_data_structs::budget::Budget, + ) -> frame_support::pallet_prelude::DispatchResultWithPostInfo { + todo!() + } + + fn delete_token_properties( + &self, + sender: ::CrossAccountId, + token_id: up_data_structs::TokenId, + property_keys: Vec, + budget: &dyn up_data_structs::budget::Budget, + ) -> frame_support::pallet_prelude::DispatchResultWithPostInfo { + todo!() + } + + fn set_token_property_permissions( + &self, + sender: &::CrossAccountId, + property_permissions: Vec, + ) -> frame_support::pallet_prelude::DispatchResultWithPostInfo { + todo!() + } + + fn transfer( + &self, + sender: ::CrossAccountId, + to: ::CrossAccountId, + token: up_data_structs::TokenId, + amount: u128, + budget: &dyn up_data_structs::budget::Budget, + ) -> frame_support::pallet_prelude::DispatchResultWithPostInfo { + todo!() + } + + fn approve( + &self, + sender: ::CrossAccountId, + spender: ::CrossAccountId, + token: up_data_structs::TokenId, + amount: u128, + ) -> frame_support::pallet_prelude::DispatchResultWithPostInfo { + todo!() + } + + fn approve_from( + &self, + sender: ::CrossAccountId, + from: ::CrossAccountId, + to: ::CrossAccountId, + token: up_data_structs::TokenId, + amount: u128, + ) -> frame_support::pallet_prelude::DispatchResultWithPostInfo { + todo!() + } + + fn transfer_from( + &self, + sender: ::CrossAccountId, + from: ::CrossAccountId, + to: ::CrossAccountId, + token: up_data_structs::TokenId, + amount: u128, + budget: &dyn up_data_structs::budget::Budget, + ) -> frame_support::pallet_prelude::DispatchResultWithPostInfo { + todo!() + } + + fn burn_from( + &self, + sender: ::CrossAccountId, + from: ::CrossAccountId, + token: up_data_structs::TokenId, + amount: u128, + budget: &dyn up_data_structs::budget::Budget, + ) -> frame_support::pallet_prelude::DispatchResultWithPostInfo { + todo!() + } + + fn check_nesting( + &self, + sender: ::CrossAccountId, + from: (up_data_structs::CollectionId, up_data_structs::TokenId), + under: up_data_structs::TokenId, + budget: &dyn up_data_structs::budget::Budget, + ) -> frame_support::sp_runtime::DispatchResult { + todo!() + } + + fn nest( + &self, + under: up_data_structs::TokenId, + to_nest: (up_data_structs::CollectionId, up_data_structs::TokenId), + ) { + todo!() + } + + fn unnest( + &self, + under: up_data_structs::TokenId, + to_nest: (up_data_structs::CollectionId, up_data_structs::TokenId), + ) { + todo!() + } + + fn account_tokens(&self, account: ::CrossAccountId) -> Vec { + todo!() + } + + fn collection_tokens(&self) -> Vec { + todo!() + } + + fn token_exists(&self, token: up_data_structs::TokenId) -> bool { + todo!() + } + + fn last_token_id(&self) -> up_data_structs::TokenId { + todo!() + } + + fn token_owner( + &self, + token: up_data_structs::TokenId, + ) -> Result<::CrossAccountId, up_data_structs::TokenOwnerError> { + todo!() + } + + fn token_owners(&self, token: up_data_structs::TokenId) -> Vec<::CrossAccountId> { + todo!() + } + + fn token_property( + &self, + token_id: up_data_structs::TokenId, + key: &up_data_structs::PropertyKey, + ) -> Option { + todo!() + } + + fn token_properties( + &self, + token: up_data_structs::TokenId, + keys: Option>, + ) -> Vec { + todo!() + } + + fn total_supply(&self) -> u32 { + todo!() + } + + fn account_balance(&self, account: ::CrossAccountId) -> u32 { + todo!() + } + + fn balance(&self, account: ::CrossAccountId, token: up_data_structs::TokenId) -> u128 { + todo!() + } + + fn total_pieces(&self, token: up_data_structs::TokenId) -> Option { + todo!() + } + + fn allowance( + &self, + sender: ::CrossAccountId, + spender: ::CrossAccountId, + token: up_data_structs::TokenId, + ) -> u128 { + todo!() + } + + fn refungible_extensions(&self) -> Option<&dyn pallet_common::RefungibleExtensions> { + todo!() + } + + fn set_allowance_for_all( + &self, + owner: ::CrossAccountId, + operator: ::CrossAccountId, + approve: bool, + ) -> frame_support::pallet_prelude::DispatchResultWithPostInfo { + todo!() + } + + fn allowance_for_all(&self, owner: ::CrossAccountId, operator: ::CrossAccountId) -> bool { + todo!() + } + + fn repair_item( + &self, + token: up_data_structs::TokenId, + ) -> frame_support::pallet_prelude::DispatchResultWithPostInfo { + todo!() + } +} --- a/pallets/balances-adapter/src/erc.rs +++ b/pallets/balances-adapter/src/erc.rs @@ -1,5 +1,5 @@ -use crate::Config; -use evm_coder::{abi::AbiType, AbiCoder, ToLog, generate_stubgen, solidity_interface, types::*}; +use crate::{Config, NativeFungibleHandle}; +use evm_coder::{abi::AbiType, ToLog, generate_stubgen, solidity_interface, types::*}; use frame_support::traits::{Currency, ExistenceRequirement}; use pallet_common::{ erc::{CommonEvmHandler, CrossAccountId, PrecompileHandle, PrecompileResult}, @@ -34,17 +34,6 @@ spender: Address, value: U256, }, -} - -pub struct NativeFungibleHandle(SubstrateRecorder); - -impl WithRecorder for NativeFungibleHandle { - fn recorder(&self) -> &pallet_evm_coder_substrate::SubstrateRecorder { - &self.0 - } - fn into_recorder(self) -> pallet_evm_coder_substrate::SubstrateRecorder { - self.0 - } } #[solidity_interface(name = ERC20, events(ERC20Events), enum(derive(PreDispatch)), enum_attr(weight), expect_selector = 0x942e8b22)] --- a/pallets/balances-adapter/src/lib.rs +++ b/pallets/balances-adapter/src/lib.rs @@ -4,17 +4,42 @@ extern crate alloc; pub use pallet::*; +use pallet_common::CollectionHandle; +use pallet_evm_coder_substrate::{WithRecorder, SubstrateRecorder}; +pub mod common; pub mod erc; +pub struct NativeFungibleHandle(CollectionHandle); +impl NativeFungibleHandle { + pub fn cast(inner: CollectionHandle) -> Self { + Self(inner) + } + + /// Casts [`NativeFungibleHandle`] into [`CollectionHandle`][`pallet_common::CollectionHandle`]. + pub fn into_inner(self) -> pallet_common::CollectionHandle { + self.0 + } +} + +impl WithRecorder for NativeFungibleHandle { + fn recorder(&self) -> &pallet_evm_coder_substrate::SubstrateRecorder { + &self.0.recorder + } + fn into_recorder(self) -> pallet_evm_coder_substrate::SubstrateRecorder { + self.0.recorder + } +} #[frame_support::pallet] pub mod pallet { use alloc::string::String; - use frame_support::traits::Get; + use frame_support::{traits::Get, sp_runtime::DispatchResult}; use sp_core::U256; #[pallet::config] - pub trait Config: frame_system::Config + pallet_evm_coder_substrate::Config { + pub trait Config: + frame_system::Config + pallet_evm_coder_substrate::Config + pallet_common::Config + { type Currency: frame_support::traits::Currency< Self::AccountId, Balance = Self::CurrencyBalance, @@ -28,6 +53,10 @@ #[pallet::pallet] pub struct Pallet(_); - #[pallet::call] - impl Pallet {} + // #[pallet::call] + impl Pallet { + pub fn dummy() -> DispatchResult { + Ok(()) + } + } } --- a/runtime/common/dispatch.rs +++ b/runtime/common/dispatch.rs @@ -25,6 +25,7 @@ }; pub use pallet_common::dispatch::CollectionDispatch; use pallet_fungible::{Pallet as PalletFungible, FungibleHandle}; +use pallet_balances_adapter::{Pallet as PalletNativeFungible, NativeFungibleHandle}; use pallet_nonfungible::{Pallet as PalletNonfungible, NonfungibleHandle}; use pallet_refungible::{ Pallet as PalletRefungible, RefungibleHandle, erc_token::RefungibleTokenHandle, @@ -39,11 +40,15 @@ pub enum CollectionDispatchT where - T: pallet_fungible::Config + pallet_nonfungible::Config + pallet_refungible::Config, + T: pallet_fungible::Config + + pallet_nonfungible::Config + + pallet_refungible::Config + + pallet_balances_adapter::Config, { Fungible(FungibleHandle), Nonfungible(NonfungibleHandle), Refungible(RefungibleHandle), + NativeFungible(NativeFungibleHandle), } impl CollectionDispatch for CollectionDispatchT where @@ -51,7 +56,8 @@ + pallet_unique::Config + pallet_fungible::Config + pallet_nonfungible::Config - + pallet_refungible::Config, + + pallet_refungible::Config + + pallet_balances_adapter::Config, { fn create( sender: T::CrossAccountId, @@ -100,7 +106,13 @@ fn dispatch(handle: CollectionHandle) -> Self { match handle.mode { - CollectionMode::Fungible(_) => Self::Fungible(FungibleHandle::cast(handle)), + CollectionMode::Fungible(_) => { + if handle.id != up_data_structs::CollectionId(0) { + Self::Fungible(FungibleHandle::cast(handle)) + } else { + Self::NativeFungible(NativeFungibleHandle::cast(handle)) + } + } CollectionMode::NFT => Self::Nonfungible(NonfungibleHandle::cast(handle)), CollectionMode::ReFungible => Self::Refungible(RefungibleHandle::cast(handle)), } @@ -111,6 +123,7 @@ Self::Fungible(f) => f.into_inner(), Self::Nonfungible(f) => f.into_inner(), Self::Refungible(f) => f.into_inner(), + Self::NativeFungible(f) => f.into_inner(), } } @@ -119,6 +132,7 @@ Self::Fungible(h) => h, Self::Nonfungible(h) => h, Self::Refungible(h) => h, + Self::NativeFungible(h) => h, } } } @@ -129,7 +143,8 @@ + pallet_unique::Config + pallet_fungible::Config + pallet_nonfungible::Config - + pallet_refungible::Config, + + pallet_refungible::Config + + pallet_balances_adapter::Config, T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>, { fn is_reserved(target: &H160) -> bool { @@ -174,6 +189,7 @@ Self::Fungible(h) => h.call(handle), Self::Nonfungible(h) => h.call(handle), Self::Refungible(h) => h.call(handle), + Self::NativeFungible(f) => todo!(), } } else if let Some((collection_id, token_id)) = ::EvmTokenAddressMapping::address_to_token( --- /dev/null +++ b/tests/src/eth/nativeFungible.test.ts @@ -0,0 +1,44 @@ +// Copyright 2019-2023 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Unique Network is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Unique Network. If not, see . + +import {IKeyringPair} from '@polkadot/types/types'; +import {itEth, usingEthPlaygrounds} from './util'; + +describe('NativeFungible: Plain calls', () => { + let donor: IKeyringPair; + let alice: IKeyringPair; + let owner: IKeyringPair; + + before(async function() { + await usingEthPlaygrounds(async (helper, privateKey) => { + donor = await privateKey({url: import.meta.url}); + [alice, owner] = await helper.arrange.createAccounts([30n, 20n], donor); + }); + }); + + itEth.only('Can perform approve()', async ({helper}) => { + const owner = await helper.eth.createAccountWithBalance(donor); + const spender = helper.eth.createAccount(); + const collection = await helper.ft.mintCollection(alice); + await collection.mint(alice, 200n, {Ethereum: owner}); + + const collectionAddress = helper.ethAddress.fromCollectionId(0); + const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner); + + + await contract.methods.approve(spender, 100).send({from: owner}); + }); +}); \ No newline at end of file --- a/tests/src/eth/util/playgrounds/unique.dev.ts +++ b/tests/src/eth/util/playgrounds/unique.dev.ts @@ -141,11 +141,16 @@ } async collection(address: string, mode: TCollectionMode, caller?: string, mergeDeprecated = false) { - let abi = { - 'nft': nonFungibleAbi, - 'rft': refungibleAbi, - 'ft': fungibleAbi, - }[mode]; + let abi; + if (address === '0' && mode === 'ft') { + abi = nativeFungibleAbi; + } else { + abi ={ + 'nft': nonFungibleAbi, + 'rft': refungibleAbi, + 'ft': fungibleAbi, + }[mode]; + } if (mergeDeprecated) { const deprecated = { 'nft': nonFungibleDeprecatedAbi,