difftreelog
fixed code review issues
in: master
8 files changed
Makefilediffbeforeafterboth--- a/Makefile
+++ b/Makefile
@@ -36,8 +36,8 @@
PACKAGE=pallet-nonfungible NAME=erc::gen_impl OUTPUT=$(NONFUNGIBLE_EVM_STUBS)/$@ ./.maintain/scripts/generate_sol.sh
UniqueRefungibleToken.sol:
- PACKAGE=pallet-refungible NAME=erc20::gen_iface OUTPUT=$(TESTS_API)/$@ ./.maintain/scripts/generate_sol.sh
- PACKAGE=pallet-refungible NAME=erc20::gen_impl OUTPUT=$(REFUNGIBLE_EVM_STUBS)/$@ ./.maintain/scripts/generate_sol.sh
+ PACKAGE=pallet-refungible NAME=erc_token::gen_iface OUTPUT=$(TESTS_API)/$@ ./.maintain/scripts/generate_sol.sh
+ PACKAGE=pallet-refungible NAME=erc_token::gen_impl OUTPUT=$(REFUNGIBLE_EVM_STUBS)/$@ ./.maintain/scripts/generate_sol.sh
ContractHelpers.sol:
PACKAGE=pallet-evm-contract-helpers NAME=eth::contract_helpers_iface OUTPUT=$(TESTS_API)/$@ ./.maintain/scripts/generate_sol.sh
pallets/refungible/src/erc.rsdiffbeforeafterboth--- /dev/null
+++ b/pallets/refungible/src/erc.rs
@@ -0,0 +1,32 @@
+extern crate alloc;
+use evm_coder::{generate_stubgen, solidity_interface, types::*};
+
+use pallet_common::{CollectionHandle, erc::CollectionCall, erc::CommonEvmHandler};
+
+use pallet_evm::PrecompileHandle;
+use pallet_evm_coder_substrate::call;
+
+use crate::{Config, RefungibleHandle};
+
+#[solidity_interface(
+ name = "UniqueRFT",
+ is(via("CollectionHandle<T>", common_mut, Collection),)
+)]
+impl<T: Config> RefungibleHandle<T> where T::AccountId: From<[u8; 32]> {}
+
+// Not a tests, but code generators
+generate_stubgen!(gen_impl, UniqueRFTCall<()>, true);
+generate_stubgen!(gen_iface, UniqueRFTCall<()>, false);
+
+impl<T: Config> CommonEvmHandler for RefungibleHandle<T>
+where
+ T::AccountId: From<[u8; 32]>,
+{
+ const CODE: &'static [u8] = include_bytes!("./stubs/UniqueRefungible.raw");
+ fn call(
+ self,
+ handle: &mut impl PrecompileHandle,
+ ) -> Option<pallet_common::erc::PrecompileResult> {
+ call::<T, UniqueRFTCall<T>, _, _>(handle, self)
+ }
+}
pallets/refungible/src/erc20.rsdiffbeforeafterboth--- a/pallets/refungible/src/erc20.rs
+++ /dev/null
@@ -1,204 +0,0 @@
-// Copyright 2019-2022 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 <http://www.gnu.org/licenses/>.
-
-extern crate alloc;
-use core::{
- char::{REPLACEMENT_CHARACTER, decode_utf16},
- convert::TryInto,
- ops::Deref,
-};
-use evm_coder::{ToLog, execution::*, generate_stubgen, solidity_interface, types::*, weight};
-use pallet_common::{
- CommonWeightInfo,
- erc::{CommonEvmHandler, PrecompileResult},
-};
-use pallet_evm::{account::CrossAccountId, PrecompileHandle};
-use pallet_evm_coder_substrate::{call, dispatch_to_evm, WithRecorder};
-use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};
-use sp_std::vec::Vec;
-use up_data_structs::{CollectionMode, TokenId};
-
-use crate::{
- Allowance, Balance, common::CommonWeights, Config, erc721::UniqueRFTCall, Pallet,
- RefungibleHandle, SelfWeightOf, weights::WeightInfo, TotalSupply,
-};
-
-pub struct RefungibleTokenHandle<T: Config>(pub RefungibleHandle<T>, pub TokenId);
-
-#[derive(ToLog)]
-pub enum ERC20Events {
- Transfer {
- #[indexed]
- from: address,
- #[indexed]
- to: address,
- value: uint256,
- },
- Approval {
- #[indexed]
- owner: address,
- #[indexed]
- spender: address,
- value: uint256,
- },
-}
-
-#[solidity_interface(name = "ERC20", events(ERC20Events))]
-impl<T: Config> RefungibleTokenHandle<T> {
- fn name(&self) -> Result<string> {
- Ok(decode_utf16(self.name.iter().copied())
- .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))
- .collect::<string>())
- }
- fn symbol(&self) -> Result<string> {
- Ok(string::from_utf8_lossy(&self.token_prefix).into())
- }
- fn total_supply(&self) -> Result<uint256> {
- self.consume_store_reads(1)?;
- Ok(<TotalSupply<T>>::get((self.id, self.1)).into())
- }
-
- fn decimals(&self) -> Result<uint8> {
- Ok(if let CollectionMode::Fungible(decimals) = &self.mode {
- *decimals
- } else {
- unreachable!()
- })
- }
- fn balance_of(&self, owner: address) -> Result<uint256> {
- self.consume_store_reads(1)?;
- let owner = T::CrossAccountId::from_eth(owner);
- let balance = <Balance<T>>::get((self.id, self.1, owner));
- Ok(balance.into())
- }
- #[weight(<CommonWeights<T>>::transfer())]
- fn transfer(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {
- let caller = T::CrossAccountId::from_eth(caller);
- let to = T::CrossAccountId::from_eth(to);
- let amount = amount.try_into().map_err(|_| "amount overflow")?;
- let budget = self
- .recorder
- .weight_calls_budget(<StructureWeight<T>>::find_parent());
-
- <Pallet<T>>::transfer(self, &caller, &to, self.1, amount, &budget)
- .map_err(|_| "transfer error")?;
- Ok(true)
- }
- #[weight(<CommonWeights<T>>::transfer_from())]
- fn transfer_from(
- &mut self,
- caller: caller,
- from: address,
- to: address,
- amount: uint256,
- ) -> Result<bool> {
- let caller = T::CrossAccountId::from_eth(caller);
- let from = T::CrossAccountId::from_eth(from);
- let to = T::CrossAccountId::from_eth(to);
- let amount = amount.try_into().map_err(|_| "amount overflow")?;
- let budget = self
- .recorder
- .weight_calls_budget(<StructureWeight<T>>::find_parent());
-
- <Pallet<T>>::transfer_from(self, &caller, &from, &to, self.1, amount, &budget)
- .map_err(dispatch_to_evm::<T>)?;
- Ok(true)
- }
- #[weight(<SelfWeightOf<T>>::approve())]
- fn approve(&mut self, caller: caller, spender: address, amount: uint256) -> Result<bool> {
- let caller = T::CrossAccountId::from_eth(caller);
- let spender = T::CrossAccountId::from_eth(spender);
- let amount = amount.try_into().map_err(|_| "amount overflow")?;
-
- <Pallet<T>>::set_allowance(self, &caller, &spender, self.1, amount)
- .map_err(dispatch_to_evm::<T>)?;
- Ok(true)
- }
- fn allowance(&self, owner: address, spender: address) -> Result<uint256> {
- self.consume_store_reads(1)?;
- let owner = T::CrossAccountId::from_eth(owner);
- let spender = T::CrossAccountId::from_eth(spender);
-
- Ok(<Allowance<T>>::get((self.id, self.1, owner, spender)).into())
- }
-}
-
-#[solidity_interface(name = "ERC20UniqueExtensions")]
-impl<T: Config> RefungibleTokenHandle<T> {
- #[weight(<SelfWeightOf<T>>::burn_from())]
- fn burn_from(&mut self, caller: caller, from: address, amount: uint256) -> Result<bool> {
- let caller = T::CrossAccountId::from_eth(caller);
- let from = T::CrossAccountId::from_eth(from);
- let amount = amount.try_into().map_err(|_| "amount overflow")?;
- let budget = self
- .recorder
- .weight_calls_budget(<StructureWeight<T>>::find_parent());
-
- <Pallet<T>>::burn_from(self, &caller, &from, self.1, amount, &budget)
- .map_err(dispatch_to_evm::<T>)?;
- Ok(true)
- }
-}
-
-impl<T: Config> RefungibleTokenHandle<T> {
- pub fn into_inner(self) -> RefungibleHandle<T> {
- self.0
- }
- pub fn common_mut(&mut self) -> &mut RefungibleHandle<T> {
- &mut self.0
- }
-}
-
-impl<T: Config> WithRecorder<T> for RefungibleTokenHandle<T> {
- fn recorder(&self) -> &pallet_evm_coder_substrate::SubstrateRecorder<T> {
- self.0.recorder()
- }
- fn into_recorder(self) -> pallet_evm_coder_substrate::SubstrateRecorder<T> {
- self.0.into_recorder()
- }
-}
-
-impl<T: Config> Deref for RefungibleTokenHandle<T> {
- type Target = RefungibleHandle<T>;
-
- fn deref(&self) -> &Self::Target {
- &self.0
- }
-}
-
-#[solidity_interface(
- name = "UniqueRefungibleToken",
- is(
- ERC20,
- ERC20UniqueExtensions,
- via("RefungibleHandle<T>", common_mut, UniqueRFT)
- )
-)]
-impl<T: Config> RefungibleTokenHandle<T> where T::AccountId: From<[u8; 32]> {}
-
-generate_stubgen!(gen_impl, UniqueRefungibleTokenCall<()>, true);
-generate_stubgen!(gen_iface, UniqueRefungibleTokenCall<()>, false);
-
-impl<T: Config> CommonEvmHandler for RefungibleTokenHandle<T>
-where
- T::AccountId: From<[u8; 32]>,
-{
- const CODE: &'static [u8] = include_bytes!("./stubs/UniqueRefungibleToken.raw");
-
- fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {
- call::<T, UniqueRefungibleTokenCall<T>, _, _>(handle, self)
- }
-}
pallets/refungible/src/erc721.rsdiffbeforeafterboth--- a/pallets/refungible/src/erc721.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-extern crate alloc;
-use evm_coder::{generate_stubgen, solidity_interface, types::*};
-
-use pallet_common::{CollectionHandle, erc::CollectionCall, erc::CommonEvmHandler};
-
-use pallet_evm::PrecompileHandle;
-use pallet_evm_coder_substrate::call;
-
-use crate::{Config, RefungibleHandle};
-
-#[solidity_interface(
- name = "UniqueRFT",
- is(via("CollectionHandle<T>", common_mut, Collection),)
-)]
-impl<T: Config> RefungibleHandle<T> where T::AccountId: From<[u8; 32]> {}
-
-// Not a tests, but code generators
-generate_stubgen!(gen_impl, UniqueRFTCall<()>, true);
-generate_stubgen!(gen_iface, UniqueRFTCall<()>, false);
-
-impl<T: Config> CommonEvmHandler for RefungibleHandle<T>
-where
- T::AccountId: From<[u8; 32]>,
-{
- const CODE: &'static [u8] = include_bytes!("./stubs/UniqueRefungible.raw");
- fn call(
- self,
- handle: &mut impl PrecompileHandle,
- ) -> Option<pallet_common::erc::PrecompileResult> {
- call::<T, UniqueRFTCall<T>, _, _>(handle, self)
- }
-}
pallets/refungible/src/erc_token.rsdiffbeforeafterboth--- /dev/null
+++ b/pallets/refungible/src/erc_token.rs
@@ -0,0 +1,195 @@
+// Copyright 2019-2022 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 <http://www.gnu.org/licenses/>.
+
+extern crate alloc;
+use core::{
+ char::{REPLACEMENT_CHARACTER, decode_utf16},
+ convert::TryInto,
+ ops::Deref,
+};
+use evm_coder::{ToLog, execution::*, generate_stubgen, solidity_interface, types::*, weight};
+use pallet_common::{
+ CommonWeightInfo,
+ erc::{CommonEvmHandler, PrecompileResult},
+};
+use pallet_evm::{account::CrossAccountId, PrecompileHandle};
+use pallet_evm_coder_substrate::{call, dispatch_to_evm, WithRecorder};
+use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};
+use sp_std::vec::Vec;
+use up_data_structs::TokenId;
+
+use crate::{
+ Allowance, Balance, common::CommonWeights, Config, Pallet, RefungibleHandle, SelfWeightOf,
+ weights::WeightInfo, TotalSupply,
+};
+
+pub struct RefungibleTokenHandle<T: Config>(pub RefungibleHandle<T>, pub TokenId);
+
+#[derive(ToLog)]
+pub enum ERC20Events {
+ Transfer {
+ #[indexed]
+ from: address,
+ #[indexed]
+ to: address,
+ value: uint256,
+ },
+ Approval {
+ #[indexed]
+ owner: address,
+ #[indexed]
+ spender: address,
+ value: uint256,
+ },
+}
+
+#[solidity_interface(name = "ERC20", events(ERC20Events))]
+impl<T: Config> RefungibleTokenHandle<T> {
+ fn name(&self) -> Result<string> {
+ Ok(decode_utf16(self.name.iter().copied())
+ .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))
+ .collect::<string>())
+ }
+ fn symbol(&self) -> Result<string> {
+ Ok(string::from_utf8_lossy(&self.token_prefix).into())
+ }
+ fn total_supply(&self) -> Result<uint256> {
+ self.consume_store_reads(1)?;
+ Ok(<TotalSupply<T>>::get((self.id, self.1)).into())
+ }
+
+ fn decimals(&self) -> Result<uint8> {
+ // Decimals aren't supported for refungible tokens
+ Ok(0)
+ }
+
+ fn balance_of(&self, owner: address) -> Result<uint256> {
+ self.consume_store_reads(1)?;
+ let owner = T::CrossAccountId::from_eth(owner);
+ let balance = <Balance<T>>::get((self.id, self.1, owner));
+ Ok(balance.into())
+ }
+ #[weight(<CommonWeights<T>>::transfer())]
+ fn transfer(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {
+ let caller = T::CrossAccountId::from_eth(caller);
+ let to = T::CrossAccountId::from_eth(to);
+ let amount = amount.try_into().map_err(|_| "amount overflow")?;
+ let budget = self
+ .recorder
+ .weight_calls_budget(<StructureWeight<T>>::find_parent());
+
+ <Pallet<T>>::transfer(self, &caller, &to, self.1, amount, &budget)
+ .map_err(|_| "transfer error")?;
+ Ok(true)
+ }
+ #[weight(<CommonWeights<T>>::transfer_from())]
+ fn transfer_from(
+ &mut self,
+ caller: caller,
+ from: address,
+ to: address,
+ amount: uint256,
+ ) -> Result<bool> {
+ let caller = T::CrossAccountId::from_eth(caller);
+ let from = T::CrossAccountId::from_eth(from);
+ let to = T::CrossAccountId::from_eth(to);
+ let amount = amount.try_into().map_err(|_| "amount overflow")?;
+ let budget = self
+ .recorder
+ .weight_calls_budget(<StructureWeight<T>>::find_parent());
+
+ <Pallet<T>>::transfer_from(self, &caller, &from, &to, self.1, amount, &budget)
+ .map_err(dispatch_to_evm::<T>)?;
+ Ok(true)
+ }
+ #[weight(<SelfWeightOf<T>>::approve())]
+ fn approve(&mut self, caller: caller, spender: address, amount: uint256) -> Result<bool> {
+ let caller = T::CrossAccountId::from_eth(caller);
+ let spender = T::CrossAccountId::from_eth(spender);
+ let amount = amount.try_into().map_err(|_| "amount overflow")?;
+
+ <Pallet<T>>::set_allowance(self, &caller, &spender, self.1, amount)
+ .map_err(dispatch_to_evm::<T>)?;
+ Ok(true)
+ }
+ fn allowance(&self, owner: address, spender: address) -> Result<uint256> {
+ self.consume_store_reads(1)?;
+ let owner = T::CrossAccountId::from_eth(owner);
+ let spender = T::CrossAccountId::from_eth(spender);
+
+ Ok(<Allowance<T>>::get((self.id, self.1, owner, spender)).into())
+ }
+}
+
+#[solidity_interface(name = "ERC20UniqueExtensions")]
+impl<T: Config> RefungibleTokenHandle<T> {
+ #[weight(<SelfWeightOf<T>>::burn_from())]
+ fn burn_from(&mut self, caller: caller, from: address, amount: uint256) -> Result<bool> {
+ let caller = T::CrossAccountId::from_eth(caller);
+ let from = T::CrossAccountId::from_eth(from);
+ let amount = amount.try_into().map_err(|_| "amount overflow")?;
+ let budget = self
+ .recorder
+ .weight_calls_budget(<StructureWeight<T>>::find_parent());
+
+ <Pallet<T>>::burn_from(self, &caller, &from, self.1, amount, &budget)
+ .map_err(dispatch_to_evm::<T>)?;
+ Ok(true)
+ }
+}
+
+impl<T: Config> RefungibleTokenHandle<T> {
+ pub fn into_inner(self) -> RefungibleHandle<T> {
+ self.0
+ }
+ pub fn common_mut(&mut self) -> &mut RefungibleHandle<T> {
+ &mut self.0
+ }
+}
+
+impl<T: Config> WithRecorder<T> for RefungibleTokenHandle<T> {
+ fn recorder(&self) -> &pallet_evm_coder_substrate::SubstrateRecorder<T> {
+ self.0.recorder()
+ }
+ fn into_recorder(self) -> pallet_evm_coder_substrate::SubstrateRecorder<T> {
+ self.0.into_recorder()
+ }
+}
+
+impl<T: Config> Deref for RefungibleTokenHandle<T> {
+ type Target = RefungibleHandle<T>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+#[solidity_interface(name = "UniqueRefungibleToken", is(ERC20, ERC20UniqueExtensions,))]
+impl<T: Config> RefungibleTokenHandle<T> where T::AccountId: From<[u8; 32]> {}
+
+generate_stubgen!(gen_impl, UniqueRefungibleTokenCall<()>, true);
+generate_stubgen!(gen_iface, UniqueRefungibleTokenCall<()>, false);
+
+impl<T: Config> CommonEvmHandler for RefungibleTokenHandle<T>
+where
+ T::AccountId: From<[u8; 32]>,
+{
+ const CODE: &'static [u8] = include_bytes!("./stubs/UniqueRefungibleToken.raw");
+
+ fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {
+ call::<T, UniqueRefungibleTokenCall<T>, _, _>(handle, self)
+ }
+}
pallets/refungible/src/lib.rsdiffbeforeafterboth--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -87,7 +87,7 @@
#![cfg_attr(not(feature = "std"), no_std)]
-use crate::erc20::ERC20Events;
+use crate::erc_token::ERC20Events;
use codec::{Encode, Decode, MaxEncodedLen};
use core::ops::Deref;
@@ -110,8 +110,8 @@
#[cfg(feature = "runtime-benchmarks")]
pub mod benchmarking;
pub mod common;
-pub mod erc20;
-pub mod erc721;
+pub mod erc;
+pub mod erc_token;
pub mod weights;
pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;
runtime/common/src/dispatch.rsdiffbeforeafterboth--- a/runtime/common/src/dispatch.rs
+++ b/runtime/common/src/dispatch.rs
@@ -25,7 +25,9 @@
pub use pallet_common::dispatch::CollectionDispatch;
use pallet_fungible::{Pallet as PalletFungible, FungibleHandle};
use pallet_nonfungible::{Pallet as PalletNonfungible, NonfungibleHandle};
-use pallet_refungible::{Pallet as PalletRefungible, RefungibleHandle, erc20::RefungibleTokenHandle};
+use pallet_refungible::{
+ Pallet as PalletRefungible, RefungibleHandle, erc_token::RefungibleTokenHandle,
+};
use up_data_structs::{
CollectionMode, CreateCollectionData, MAX_DECIMAL_POINTS, mapping::TokenAddressMapping,
};
tests/src/eth/reFungibleToken.test.tsdiffbeforeafterboth1// 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/>.1617import {approve, createCollection, createRefungibleToken, transfer, transferFrom, UNIQUE} from '../util/helpers';18import {createEthAccount, createEthAccountWithBalance, GAS_ARGS, itWeb3, normalizeEvents, recordEthFee, recordEvents, subToEth, tokenIdToAddress, transferBalanceToEth} from './util/helpers';19import reFungibleTokenAbi from './reFungibleTokenAbi.json';2021import chai from 'chai';22import chaiAsPromised from 'chai-as-promised';23chai.use(chaiAsPromised);24const expect = chai.expect;2526describe('Refungible token: Information getting', () => {27 itWeb3('totalSupply', async ({api, web3, privateKeyWrapper}) => {28 const alice = privateKeyWrapper('//Alice');2930 const collectionId = (await createCollection(api, alice, {name: 'token name', mode: {type: 'ReFungible'}})).collectionId;3132 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);3334 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: caller})).itemId;3536 const address = tokenIdToAddress(collectionId, tokenId);37 const contract = new web3.eth.Contract(reFungibleTokenAbi as any, address, {from: caller, ...GAS_ARGS});38 const totalSupply = await contract.methods.totalSupply().call();3940 expect(totalSupply).to.equal('200');41 });4243 itWeb3('balanceOf', async ({api, web3, privateKeyWrapper}) => {44 const alice = privateKeyWrapper('//Alice');4546 const collectionId = (await createCollection(api, alice, {name: 'token name', mode: {type: 'ReFungible'}})).collectionId;4748 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);4950 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: caller})).itemId;5152 const address = tokenIdToAddress(collectionId, tokenId);53 const contract = new web3.eth.Contract(reFungibleTokenAbi as any, address, {from: caller, ...GAS_ARGS});54 const balance = await contract.methods.balanceOf(caller).call();5556 expect(balance).to.equal('200');57 });58});5960describe('Refungible: Plain calls', () => {61 itWeb3('Can perform approve()', async ({web3, api, privateKeyWrapper}) => {62 const alice = privateKeyWrapper('//Alice');6364 const collectionId = (await createCollection(api, alice, {name: 'token name', mode: {type: 'ReFungible'}})).collectionId;6566 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);6768 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: owner})).itemId;6970 const address = tokenIdToAddress(collectionId, tokenId);7172 const spender = createEthAccount(web3);7374 const contract = new web3.eth.Contract(reFungibleTokenAbi as any, address, {from: owner, ...GAS_ARGS});7576 {77 const result = await contract.methods.approve(spender, 100).send({from: owner});78 const events = normalizeEvents(result.events);7980 expect(events).to.be.deep.equal([81 {82 address,83 event: 'Approval',84 args: {85 owner,86 spender,87 value: '100',88 },89 },90 ]);91 }9293 {94 const allowance = await contract.methods.allowance(owner, spender).call();95 expect(+allowance).to.equal(100);96 }97 });9899 itWeb3('Can perform transferFrom()', async ({web3, api, privateKeyWrapper}) => {100 const alice = privateKeyWrapper('//Alice');101102 const collectionId = (await createCollection(api, alice, {name: 'token name', mode: {type: 'ReFungible'}})).collectionId;103104 const owner = createEthAccount(web3);105 await transferBalanceToEth(api, alice, owner);106107 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: owner})).itemId;108109 const spender = createEthAccount(web3);110 await transferBalanceToEth(api, alice, spender);111112 const receiver = createEthAccount(web3);113114 const address = tokenIdToAddress(collectionId, tokenId);115 const contract = new web3.eth.Contract(reFungibleTokenAbi as any, address, {from: owner, ...GAS_ARGS});116117 await contract.methods.approve(spender, 100).send();118119 {120 const result = await contract.methods.transferFrom(owner, receiver, 49).send({from: spender});121 const events = normalizeEvents(result.events);122 expect(events).to.be.deep.equal([123 {124 address,125 event: 'Transfer',126 args: {127 from: owner,128 to: receiver,129 value: '49',130 },131 },132 {133 address,134 event: 'Approval',135 args: {136 owner,137 spender,138 value: '51',139 },140 },141 ]);142 }143144 {145 const balance = await contract.methods.balanceOf(receiver).call();146 expect(+balance).to.equal(49);147 }148149 {150 const balance = await contract.methods.balanceOf(owner).call();151 expect(+balance).to.equal(151);152 }153 });154155 itWeb3('Can perform transfer()', async ({web3, api, privateKeyWrapper}) => {156 const alice = privateKeyWrapper('//Alice');157158 const collectionId = (await createCollection(api, alice, {name: 'token name', mode: {type: 'ReFungible'}})).collectionId;159160 const owner = createEthAccount(web3);161 await transferBalanceToEth(api, alice, owner);162163 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: owner})).itemId;164165 const receiver = createEthAccount(web3);166 await transferBalanceToEth(api, alice, receiver);167168 const address = tokenIdToAddress(collectionId, tokenId);169 const contract = new web3.eth.Contract(reFungibleTokenAbi as any, address, {from: owner, ...GAS_ARGS});170171 {172 const result = await contract.methods.transfer(receiver, 50).send({from: owner});173 const events = normalizeEvents(result.events);174 expect(events).to.be.deep.equal([175 {176 address,177 event: 'Transfer',178 args: {179 from: owner,180 to: receiver,181 value: '50',182 },183 },184 ]);185 }186187 {188 const balance = await contract.methods.balanceOf(owner).call();189 expect(+balance).to.equal(150);190 }191192 {193 const balance = await contract.methods.balanceOf(receiver).call();194 expect(+balance).to.equal(50);195 }196 });197});198199describe('Refungible: Fees', () => {200 itWeb3('approve() call fee is less than 0.2UNQ', async ({web3, api, privateKeyWrapper}) => {201 const alice = privateKeyWrapper('//Alice');202203 const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;204205 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);206 const spender = createEthAccount(web3);207208 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: owner})).itemId;209210 const address = tokenIdToAddress(collectionId, tokenId);211 const contract = new web3.eth.Contract(reFungibleTokenAbi as any, address, {from: owner, ...GAS_ARGS});212213 const cost = await recordEthFee(api, owner, () => contract.methods.approve(spender, 100).send({from: owner}));214 expect(cost < BigInt(0.2 * Number(UNIQUE)));215 });216217 itWeb3('transferFrom() call fee is less than 0.2UNQ', async ({web3, api, privateKeyWrapper}) => {218 const alice = privateKeyWrapper('//Alice');219220 const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;221222 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);223 const spender = await createEthAccountWithBalance(api, web3, privateKeyWrapper);224225 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: owner})).itemId;226227 const address = tokenIdToAddress(collectionId, tokenId);228 const contract = new web3.eth.Contract(reFungibleTokenAbi as any, address, {from: owner, ...GAS_ARGS});229230 await contract.methods.approve(spender, 100).send({from: owner});231232 const cost = await recordEthFee(api, spender, () => contract.methods.transferFrom(owner, spender, 100).send({from: spender}));233 expect(cost < BigInt(0.2 * Number(UNIQUE)));234 });235236 itWeb3('transfer() call fee is less than 0.2UNQ', async ({web3, api, privateKeyWrapper}) => {237 const alice = privateKeyWrapper('//Alice');238239 const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;240241 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);242 const receiver = createEthAccount(web3);243244 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: owner})).itemId;245246 const address = tokenIdToAddress(collectionId, tokenId);247 const contract = new web3.eth.Contract(reFungibleTokenAbi as any, address, {from: owner, ...GAS_ARGS});248249 const cost = await recordEthFee(api, owner, () => contract.methods.transfer(receiver, 100).send({from: owner}));250 expect(cost < BigInt(0.2 * Number(UNIQUE)));251 });252});253254describe('Refungible: Substrate calls', () => {255 itWeb3('Events emitted for approve()', async ({web3, api, privateKeyWrapper}) => {256 const alice = privateKeyWrapper('//Alice');257258 const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;259260 const receiver = createEthAccount(web3);261262 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n)).itemId;263264 const address = tokenIdToAddress(collectionId, tokenId);265 const contract = new web3.eth.Contract(reFungibleTokenAbi as any, address);266267 const events = await recordEvents(contract, async () => {268 expect(await approve(api, collectionId, tokenId, alice, {Ethereum: receiver}, 100n)).to.be.true;269 });270271 expect(events).to.be.deep.equal([272 {273 address,274 event: 'Approval',275 args: {276 owner: subToEth(alice.address),277 spender: receiver,278 value: '100',279 },280 },281 ]);282 });283284 itWeb3('Events emitted for transferFrom()', async ({web3, api, privateKeyWrapper}) => {285 const alice = privateKeyWrapper('//Alice');286287 const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;288 const bob = privateKeyWrapper('//Bob');289290 const receiver = createEthAccount(web3);291292 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n)).itemId;293 expect(await approve(api, collectionId, tokenId, alice, bob.address, 100n)).to.be.true;294295 const address = tokenIdToAddress(collectionId, tokenId);296 const contract = new web3.eth.Contract(reFungibleTokenAbi as any, address);297298 const events = await recordEvents(contract, async () => {299 expect(await transferFrom(api, collectionId, tokenId, bob, alice, {Ethereum: receiver}, 51n)).to.be.true;300 });301302 expect(events).to.be.deep.equal([303 {304 address,305 event: 'Transfer',306 args: {307 from: subToEth(alice.address),308 to: receiver,309 value: '51',310 },311 },312 {313 address,314 event: 'Approval',315 args: {316 owner: subToEth(alice.address),317 spender: subToEth(bob.address),318 value: '49',319 },320 },321 ]);322 });323324 itWeb3('Events emitted for transfer()', async ({web3, api, privateKeyWrapper}) => {325 const alice = privateKeyWrapper('//Alice');326327 const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;328329 const receiver = createEthAccount(web3);330331 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n)).itemId;332333 const address = tokenIdToAddress(collectionId, tokenId);334 const contract = new web3.eth.Contract(reFungibleTokenAbi as any, address);335336 const events = await recordEvents(contract, async () => {337 expect(await transfer(api, collectionId, tokenId, alice, {Ethereum: receiver}, 51n)).to.be.true;338 });339340 expect(events).to.be.deep.equal([341 {342 address,343 event: 'Transfer',344 args: {345 from: subToEth(alice.address),346 to: receiver,347 value: '51',348 },349 },350 ]);351 });352});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/>.1617import {approve, createCollection, createRefungibleToken, transfer, transferFrom, UNIQUE} from '../util/helpers';18import {createEthAccount, createEthAccountWithBalance, GAS_ARGS, itWeb3, normalizeEvents, recordEthFee, recordEvents, subToEth, tokenIdToAddress, transferBalanceToEth} from './util/helpers';19import reFungibleTokenAbi from './reFungibleTokenAbi.json';2021import chai from 'chai';22import chaiAsPromised from 'chai-as-promised';23chai.use(chaiAsPromised);24const expect = chai.expect;2526describe('Refungible token: Information getting', () => {27 itWeb3('totalSupply', async ({api, web3, privateKeyWrapper}) => {28 const alice = privateKeyWrapper('//Alice');2930 const collectionId = (await createCollection(api, alice, {name: 'token name', mode: {type: 'ReFungible'}})).collectionId;3132 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);3334 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: caller})).itemId;3536 const address = tokenIdToAddress(collectionId, tokenId);37 const contract = new web3.eth.Contract(reFungibleTokenAbi as any, address, {from: caller, ...GAS_ARGS});38 const totalSupply = await contract.methods.totalSupply().call();3940 expect(totalSupply).to.equal('200');41 });4243 itWeb3('balanceOf', async ({api, web3, privateKeyWrapper}) => {44 const alice = privateKeyWrapper('//Alice');4546 const collectionId = (await createCollection(api, alice, {name: 'token name', mode: {type: 'ReFungible'}})).collectionId;4748 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);4950 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: caller})).itemId;5152 const address = tokenIdToAddress(collectionId, tokenId);53 const contract = new web3.eth.Contract(reFungibleTokenAbi as any, address, {from: caller, ...GAS_ARGS});54 const balance = await contract.methods.balanceOf(caller).call();5556 expect(balance).to.equal('200');57 });5859 itWeb3('decimals', async ({api, web3, privateKeyWrapper}) => {60 const alice = privateKeyWrapper('//Alice');6162 const collectionId = (await createCollection(api, alice, {name: 'token name', mode: {type: 'ReFungible'}})).collectionId;6364 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);6566 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: caller})).itemId;6768 const address = tokenIdToAddress(collectionId, tokenId);69 const contract = new web3.eth.Contract(reFungibleTokenAbi as any, address, {from: caller, ...GAS_ARGS});70 const decimals = await contract.methods.decimals().call();7172 expect(decimals).to.equal('0');73 });74});7576describe('Refungible: Plain calls', () => {77 itWeb3('Can perform approve()', async ({web3, api, privateKeyWrapper}) => {78 const alice = privateKeyWrapper('//Alice');7980 const collectionId = (await createCollection(api, alice, {name: 'token name', mode: {type: 'ReFungible'}})).collectionId;8182 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);8384 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: owner})).itemId;8586 const address = tokenIdToAddress(collectionId, tokenId);8788 const spender = createEthAccount(web3);8990 const contract = new web3.eth.Contract(reFungibleTokenAbi as any, address, {from: owner, ...GAS_ARGS});9192 {93 const result = await contract.methods.approve(spender, 100).send({from: owner});94 const events = normalizeEvents(result.events);9596 expect(events).to.be.deep.equal([97 {98 address,99 event: 'Approval',100 args: {101 owner,102 spender,103 value: '100',104 },105 },106 ]);107 }108109 {110 const allowance = await contract.methods.allowance(owner, spender).call();111 expect(+allowance).to.equal(100);112 }113 });114115 itWeb3('Can perform transferFrom()', async ({web3, api, privateKeyWrapper}) => {116 const alice = privateKeyWrapper('//Alice');117118 const collectionId = (await createCollection(api, alice, {name: 'token name', mode: {type: 'ReFungible'}})).collectionId;119120 const owner = createEthAccount(web3);121 await transferBalanceToEth(api, alice, owner);122123 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: owner})).itemId;124125 const spender = createEthAccount(web3);126 await transferBalanceToEth(api, alice, spender);127128 const receiver = createEthAccount(web3);129130 const address = tokenIdToAddress(collectionId, tokenId);131 const contract = new web3.eth.Contract(reFungibleTokenAbi as any, address, {from: owner, ...GAS_ARGS});132133 await contract.methods.approve(spender, 100).send();134135 {136 const result = await contract.methods.transferFrom(owner, receiver, 49).send({from: spender});137 const events = normalizeEvents(result.events);138 expect(events).to.be.deep.equal([139 {140 address,141 event: 'Transfer',142 args: {143 from: owner,144 to: receiver,145 value: '49',146 },147 },148 {149 address,150 event: 'Approval',151 args: {152 owner,153 spender,154 value: '51',155 },156 },157 ]);158 }159160 {161 const balance = await contract.methods.balanceOf(receiver).call();162 expect(+balance).to.equal(49);163 }164165 {166 const balance = await contract.methods.balanceOf(owner).call();167 expect(+balance).to.equal(151);168 }169 });170171 itWeb3('Can perform transfer()', async ({web3, api, privateKeyWrapper}) => {172 const alice = privateKeyWrapper('//Alice');173174 const collectionId = (await createCollection(api, alice, {name: 'token name', mode: {type: 'ReFungible'}})).collectionId;175176 const owner = createEthAccount(web3);177 await transferBalanceToEth(api, alice, owner);178179 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: owner})).itemId;180181 const receiver = createEthAccount(web3);182 await transferBalanceToEth(api, alice, receiver);183184 const address = tokenIdToAddress(collectionId, tokenId);185 const contract = new web3.eth.Contract(reFungibleTokenAbi as any, address, {from: owner, ...GAS_ARGS});186187 {188 const result = await contract.methods.transfer(receiver, 50).send({from: owner});189 const events = normalizeEvents(result.events);190 expect(events).to.be.deep.equal([191 {192 address,193 event: 'Transfer',194 args: {195 from: owner,196 to: receiver,197 value: '50',198 },199 },200 ]);201 }202203 {204 const balance = await contract.methods.balanceOf(owner).call();205 expect(+balance).to.equal(150);206 }207208 {209 const balance = await contract.methods.balanceOf(receiver).call();210 expect(+balance).to.equal(50);211 }212 });213});214215describe('Refungible: Fees', () => {216 itWeb3('approve() call fee is less than 0.2UNQ', async ({web3, api, privateKeyWrapper}) => {217 const alice = privateKeyWrapper('//Alice');218219 const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;220221 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);222 const spender = createEthAccount(web3);223224 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: owner})).itemId;225226 const address = tokenIdToAddress(collectionId, tokenId);227 const contract = new web3.eth.Contract(reFungibleTokenAbi as any, address, {from: owner, ...GAS_ARGS});228229 const cost = await recordEthFee(api, owner, () => contract.methods.approve(spender, 100).send({from: owner}));230 expect(cost < BigInt(0.2 * Number(UNIQUE)));231 });232233 itWeb3('transferFrom() call fee is less than 0.2UNQ', async ({web3, api, privateKeyWrapper}) => {234 const alice = privateKeyWrapper('//Alice');235236 const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;237238 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);239 const spender = await createEthAccountWithBalance(api, web3, privateKeyWrapper);240241 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: owner})).itemId;242243 const address = tokenIdToAddress(collectionId, tokenId);244 const contract = new web3.eth.Contract(reFungibleTokenAbi as any, address, {from: owner, ...GAS_ARGS});245246 await contract.methods.approve(spender, 100).send({from: owner});247248 const cost = await recordEthFee(api, spender, () => contract.methods.transferFrom(owner, spender, 100).send({from: spender}));249 expect(cost < BigInt(0.2 * Number(UNIQUE)));250 });251252 itWeb3('transfer() call fee is less than 0.2UNQ', async ({web3, api, privateKeyWrapper}) => {253 const alice = privateKeyWrapper('//Alice');254255 const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;256257 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);258 const receiver = createEthAccount(web3);259260 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n, {Ethereum: owner})).itemId;261262 const address = tokenIdToAddress(collectionId, tokenId);263 const contract = new web3.eth.Contract(reFungibleTokenAbi as any, address, {from: owner, ...GAS_ARGS});264265 const cost = await recordEthFee(api, owner, () => contract.methods.transfer(receiver, 100).send({from: owner}));266 expect(cost < BigInt(0.2 * Number(UNIQUE)));267 });268});269270describe('Refungible: Substrate calls', () => {271 itWeb3('Events emitted for approve()', async ({web3, api, privateKeyWrapper}) => {272 const alice = privateKeyWrapper('//Alice');273274 const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;275276 const receiver = createEthAccount(web3);277278 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n)).itemId;279280 const address = tokenIdToAddress(collectionId, tokenId);281 const contract = new web3.eth.Contract(reFungibleTokenAbi as any, address);282283 const events = await recordEvents(contract, async () => {284 expect(await approve(api, collectionId, tokenId, alice, {Ethereum: receiver}, 100n)).to.be.true;285 });286287 expect(events).to.be.deep.equal([288 {289 address,290 event: 'Approval',291 args: {292 owner: subToEth(alice.address),293 spender: receiver,294 value: '100',295 },296 },297 ]);298 });299300 itWeb3('Events emitted for transferFrom()', async ({web3, api, privateKeyWrapper}) => {301 const alice = privateKeyWrapper('//Alice');302303 const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;304 const bob = privateKeyWrapper('//Bob');305306 const receiver = createEthAccount(web3);307308 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n)).itemId;309 expect(await approve(api, collectionId, tokenId, alice, bob.address, 100n)).to.be.true;310311 const address = tokenIdToAddress(collectionId, tokenId);312 const contract = new web3.eth.Contract(reFungibleTokenAbi as any, address);313314 const events = await recordEvents(contract, async () => {315 expect(await transferFrom(api, collectionId, tokenId, bob, alice, {Ethereum: receiver}, 51n)).to.be.true;316 });317318 expect(events).to.be.deep.equal([319 {320 address,321 event: 'Transfer',322 args: {323 from: subToEth(alice.address),324 to: receiver,325 value: '51',326 },327 },328 {329 address,330 event: 'Approval',331 args: {332 owner: subToEth(alice.address),333 spender: subToEth(bob.address),334 value: '49',335 },336 },337 ]);338 });339340 itWeb3('Events emitted for transfer()', async ({web3, api, privateKeyWrapper}) => {341 const alice = privateKeyWrapper('//Alice');342343 const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId;344345 const receiver = createEthAccount(web3);346347 const tokenId = (await createRefungibleToken(api, alice, collectionId, 200n)).itemId;348349 const address = tokenIdToAddress(collectionId, tokenId);350 const contract = new web3.eth.Contract(reFungibleTokenAbi as any, address);351352 const events = await recordEvents(contract, async () => {353 expect(await transfer(api, collectionId, tokenId, alice, {Ethereum: receiver}, 51n)).to.be.true;354 });355356 expect(events).to.be.deep.equal([357 {358 address,359 event: 'Transfer',360 args: {361 from: subToEth(alice.address),362 to: receiver,363 value: '51',364 },365 },366 ]);367 });368});