difftreelog
feat add tests
in: master
12 files changed
node/cli/src/chain_spec.rsdiffbeforeafterboth--- a/node/cli/src/chain_spec.rs
+++ b/node/cli/src/chain_spec.rs
@@ -267,7 +267,7 @@
pub fn development_config() -> DefaultChainSpec {
let mut properties = Map::new();
properties.insert("tokenSymbol".into(), default_runtime::TOKEN_SYMBOL.into());
- properties.insert("tokenDecimals".into(), 18.into());
+ properties.insert("tokenDecimals".into(), default_runtime::DECIMALS.into());
properties.insert(
"ss58Format".into(),
default_runtime::SS58Prefix::get().into(),
@@ -341,7 +341,7 @@
pub fn local_testnet_config() -> DefaultChainSpec {
let mut properties = Map::new();
properties.insert("tokenSymbol".into(), default_runtime::TOKEN_SYMBOL.into());
- properties.insert("tokenDecimals".into(), 18.into());
+ properties.insert("tokenDecimals".into(), default_runtime::DECIMALS.into());
properties.insert(
"ss58Format".into(),
default_runtime::SS58Prefix::get().into(),
pallets/balances-adapter/src/erc.rsdiffbeforeafterboth--- a/pallets/balances-adapter/src/erc.rs
+++ b/pallets/balances-adapter/src/erc.rs
@@ -38,14 +38,14 @@
#[solidity_interface(name = ERC20, events(ERC20Events), enum(derive(PreDispatch)), enum_attr(weight), expect_selector = 0x942e8b22)]
impl<T: Config> NativeFungibleHandle<T> {
- fn allowance(&self, owner: Address, spender: Address) -> Result<U256> {
+ fn allowance(&self, _owner: Address, _spender: Address) -> Result<U256> {
Ok(U256::zero())
}
// #[weight(<SelfWeightOf<T>>::approve())]
- fn approve(&mut self, caller: Caller, spender: Address, amount: U256) -> Result<bool> {
+ fn approve(&mut self, _caller: Caller, _spender: Address, _amount: U256) -> Result<bool> {
// self.consume_store_reads(1)?;
- Err("Approve not supported now".into())
+ Err("Approve not supported".into())
}
fn balance_of(&self, owner: Address) -> Result<U256> {
@@ -106,7 +106,7 @@
let to = T::CrossAccountId::from_eth(to);
let amount = amount.try_into().map_err(|_| "amount overflow")?;
- if from != to {
+ if from != caller {
return Err("no permission".into());
}
// let budget = self
@@ -171,7 +171,7 @@
let to = to.into_sub_cross_account::<T>()?;
let amount = amount.try_into().map_err(|_| "amount overflow")?;
- if from != to {
+ if from != caller {
return Err("no permission".into());
}
pallets/common/src/dispatch.rsdiffbeforeafterboth--- a/pallets/common/src/dispatch.rs
+++ b/pallets/common/src/dispatch.rs
@@ -83,7 +83,7 @@
///
/// * `sender` - The owner of the collection.
/// * `handle` - Collection handle.
- fn destroy(sender: T::CrossAccountId, handle: CollectionHandle<T>) -> DispatchResult;
+ fn destroy(sender: T::CrossAccountId, collection_id: CollectionId) -> DispatchResult;
/// Get a specialized collection from the handle.
///
pallets/unique/src/lib.rsdiffbeforeafterboth--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -1296,10 +1296,7 @@
sender: T::CrossAccountId,
collection_id: CollectionId,
) -> DispatchResult {
- let collection = <CollectionHandle<T>>::try_get(collection_id)?;
- collection.check_is_internal()?;
-
- T::CollectionDispatch::destroy(sender, collection)?;
+ T::CollectionDispatch::destroy(sender, collection_id)?;
// TODO: basket cleanup should be moved elsewhere
// Maybe runtime dispatch.rs should perform it?
runtime/common/config/pallets/mod.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/>.1617use alloc::string::{String, ToString};18use frame_support::parameter_types;19use sp_runtime::traits::AccountIdConversion;20use crate::{21 runtime_common::{22 dispatch::CollectionDispatchT,23 config::{substrate::TreasuryModuleId, ethereum::EvmCollectionHelpersAddress},24 weights::CommonWeights,25 RelayChainBlockNumberProvider,26 },27 Runtime, RuntimeEvent, RuntimeCall, RuntimeOrigin, RUNTIME_NAME, TOKEN_SYMBOL, Balances,28};29use frame_support::traits::{ConstU32, ConstU64, Currency};30use up_common::{31 types::{AccountId, Balance, BlockNumber},32 constants::*,33};34use up_data_structs::{35 mapping::{EvmTokenAddressMapping, CrossTokenAddressMapping},36};37use sp_arithmetic::Perbill;3839#[cfg(feature = "scheduler")]40pub mod scheduler;4142#[cfg(feature = "foreign-assets")]43pub mod foreign_asset;4445#[cfg(feature = "app-promotion")]46pub mod app_promotion;4748#[cfg(feature = "collator-selection")]49pub mod collator_selection;5051#[cfg(feature = "preimage")]52pub mod preimage;5354parameter_types! {55 pub const CollectionCreationPrice: Balance = 2 * UNIQUE;56 pub const Decimals: u8 = 32;57 pub TreasuryAccountId: AccountId = TreasuryModuleId::get().into_account_truncating();58 pub Name: String = RUNTIME_NAME.to_string();59 pub Symbol: String = TOKEN_SYMBOL.to_string();60}6162impl pallet_common::Config for Runtime {63 type WeightInfo = pallet_common::weights::SubstrateWeight<Self>;64 type RuntimeEvent = RuntimeEvent;65 type Currency = Balances;66 type CollectionCreationPrice = CollectionCreationPrice;67 type TreasuryAccountId = TreasuryAccountId;68 type CollectionDispatch = CollectionDispatchT<Self>;6970 type EvmTokenAddressMapping = EvmTokenAddressMapping;71 type CrossTokenAddressMapping = CrossTokenAddressMapping<Self::AccountId>;72 type ContractAddress = EvmCollectionHelpersAddress;73}7475impl pallet_structure::Config for Runtime {76 type RuntimeEvent = RuntimeEvent;77 type RuntimeCall = RuntimeCall;78 type WeightInfo = pallet_structure::weights::SubstrateWeight<Self>;79}8081impl pallet_fungible::Config for Runtime {82 type WeightInfo = pallet_fungible::weights::SubstrateWeight<Self>;83}84impl pallet_refungible::Config for Runtime {85 type WeightInfo = pallet_refungible::weights::SubstrateWeight<Self>;86}87impl pallet_nonfungible::Config for Runtime {88 type WeightInfo = pallet_nonfungible::weights::SubstrateWeight<Self>;89}90impl pallet_balances_adapter::Config for Runtime {91 type Currency = Balances;92 type CurrencyBalance = <Balances as Currency<Self::AccountId>>::Balance;93 type Decimals = Decimals;94 type Name = Name;95 type Symbol = Symbol;96}9798parameter_types! {99 pub const InflationBlockInterval: BlockNumber = 100; // every time per how many blocks inflation is applied100}101102/// Used for the pallet inflation103impl pallet_inflation::Config for Runtime {104 type Currency = Balances;105 type TreasuryAccountId = TreasuryAccountId;106 type InflationBlockInterval = InflationBlockInterval;107 type BlockNumberProvider = RelayChainBlockNumberProvider<Runtime>;108}109110impl pallet_unique::Config for Runtime {111 type WeightInfo = pallet_unique::weights::SubstrateWeight<Self>;112 type CommonWeightInfo = CommonWeights<Self>;113 type RefungibleExtensionsWeightInfo = CommonWeights<Self>;114}115116parameter_types! {117 pub AppPromotionDailyRate: Perbill = Perbill::from_rational(5u32, 10_000);118 pub const MaxCollators: u32 = MAX_COLLATORS;119 pub const LicenseBond: Balance = GENESIS_LICENSE_BOND;120 pub const SessionPeriod: BlockNumber = SESSION_LENGTH;121 pub const DayRelayBlocks: BlockNumber = RELAY_DAYS;122}123124impl pallet_configuration::Config for Runtime {125 type RuntimeEvent = RuntimeEvent;126 type Currency = Balances;127 type DefaultWeightToFeeCoefficient = ConstU64<{ up_common::constants::WEIGHT_TO_FEE_COEFF }>;128 type DefaultMinGasPrice = ConstU64<{ up_common::constants::MIN_GAS_PRICE }>;129 type DefaultCollatorSelectionMaxCollators = MaxCollators;130 type DefaultCollatorSelectionKickThreshold = SessionPeriod;131 type DefaultCollatorSelectionLicenseBond = LicenseBond;132 type MaxXcmAllowedLocations = ConstU32<16>;133 type AppPromotionDailyRate = AppPromotionDailyRate;134 type DayRelayBlocks = DayRelayBlocks;135 type WeightInfo = pallet_configuration::weights::SubstrateWeight<Self>;136}137138impl pallet_maintenance::Config for Runtime {139 type RuntimeEvent = RuntimeEvent;140 type RuntimeOrigin = RuntimeOrigin;141 type RuntimeCall = RuntimeCall;142 #[cfg(feature = "preimage")]143 type Preimages = crate::Preimage;144 #[cfg(not(feature = "preimage"))]145 type Preimages = ();146 type WeightInfo = pallet_maintenance::weights::SubstrateWeight<Self>;147}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/>.1617use alloc::string::{String, ToString};18use frame_support::parameter_types;19use sp_runtime::traits::AccountIdConversion;20use crate::{21 runtime_common::{22 dispatch::CollectionDispatchT,23 config::{substrate::TreasuryModuleId, ethereum::EvmCollectionHelpersAddress},24 weights::CommonWeights,25 RelayChainBlockNumberProvider,26 },27 Runtime, RuntimeEvent, RuntimeCall, RuntimeOrigin, RUNTIME_NAME, TOKEN_SYMBOL, DECIMALS,28 Balances,29};30use frame_support::traits::{ConstU32, ConstU64, Currency};31use up_common::{32 types::{AccountId, Balance, BlockNumber},33 constants::*,34};35use up_data_structs::{36 mapping::{EvmTokenAddressMapping, CrossTokenAddressMapping},37};38use sp_arithmetic::Perbill;3940#[cfg(feature = "scheduler")]41pub mod scheduler;4243#[cfg(feature = "foreign-assets")]44pub mod foreign_asset;4546#[cfg(feature = "app-promotion")]47pub mod app_promotion;4849#[cfg(feature = "collator-selection")]50pub mod collator_selection;5152#[cfg(feature = "preimage")]53pub mod preimage;5455parameter_types! {56 pub const CollectionCreationPrice: Balance = 2 * UNIQUE;57 pub const Decimals: u8 = DECIMALS;58 pub TreasuryAccountId: AccountId = TreasuryModuleId::get().into_account_truncating();59 pub Name: String = RUNTIME_NAME.to_string();60 pub Symbol: String = TOKEN_SYMBOL.to_string();61}6263impl pallet_common::Config for Runtime {64 type WeightInfo = pallet_common::weights::SubstrateWeight<Self>;65 type RuntimeEvent = RuntimeEvent;66 type Currency = Balances;67 type CollectionCreationPrice = CollectionCreationPrice;68 type TreasuryAccountId = TreasuryAccountId;69 type CollectionDispatch = CollectionDispatchT<Self>;7071 type EvmTokenAddressMapping = EvmTokenAddressMapping;72 type CrossTokenAddressMapping = CrossTokenAddressMapping<Self::AccountId>;73 type ContractAddress = EvmCollectionHelpersAddress;74}7576impl pallet_structure::Config for Runtime {77 type RuntimeEvent = RuntimeEvent;78 type RuntimeCall = RuntimeCall;79 type WeightInfo = pallet_structure::weights::SubstrateWeight<Self>;80}8182impl pallet_fungible::Config for Runtime {83 type WeightInfo = pallet_fungible::weights::SubstrateWeight<Self>;84}85impl pallet_refungible::Config for Runtime {86 type WeightInfo = pallet_refungible::weights::SubstrateWeight<Self>;87}88impl pallet_nonfungible::Config for Runtime {89 type WeightInfo = pallet_nonfungible::weights::SubstrateWeight<Self>;90}91impl pallet_balances_adapter::Config for Runtime {92 type Currency = Balances;93 type CurrencyBalance = <Balances as Currency<Self::AccountId>>::Balance;94 type Decimals = Decimals;95 type Name = Name;96 type Symbol = Symbol;97}9899parameter_types! {100 pub const InflationBlockInterval: BlockNumber = 100; // every time per how many blocks inflation is applied101}102103/// Used for the pallet inflation104impl pallet_inflation::Config for Runtime {105 type Currency = Balances;106 type TreasuryAccountId = TreasuryAccountId;107 type InflationBlockInterval = InflationBlockInterval;108 type BlockNumberProvider = RelayChainBlockNumberProvider<Runtime>;109}110111impl pallet_unique::Config for Runtime {112 type WeightInfo = pallet_unique::weights::SubstrateWeight<Self>;113 type CommonWeightInfo = CommonWeights<Self>;114 type RefungibleExtensionsWeightInfo = CommonWeights<Self>;115}116117parameter_types! {118 pub AppPromotionDailyRate: Perbill = Perbill::from_rational(5u32, 10_000);119 pub const MaxCollators: u32 = MAX_COLLATORS;120 pub const LicenseBond: Balance = GENESIS_LICENSE_BOND;121 pub const SessionPeriod: BlockNumber = SESSION_LENGTH;122 pub const DayRelayBlocks: BlockNumber = RELAY_DAYS;123}124125impl pallet_configuration::Config for Runtime {126 type RuntimeEvent = RuntimeEvent;127 type Currency = Balances;128 type DefaultWeightToFeeCoefficient = ConstU64<{ up_common::constants::WEIGHT_TO_FEE_COEFF }>;129 type DefaultMinGasPrice = ConstU64<{ up_common::constants::MIN_GAS_PRICE }>;130 type DefaultCollatorSelectionMaxCollators = MaxCollators;131 type DefaultCollatorSelectionKickThreshold = SessionPeriod;132 type DefaultCollatorSelectionLicenseBond = LicenseBond;133 type MaxXcmAllowedLocations = ConstU32<16>;134 type AppPromotionDailyRate = AppPromotionDailyRate;135 type DayRelayBlocks = DayRelayBlocks;136 type WeightInfo = pallet_configuration::weights::SubstrateWeight<Self>;137}138139impl pallet_maintenance::Config for Runtime {140 type RuntimeEvent = RuntimeEvent;141 type RuntimeOrigin = RuntimeOrigin;142 type RuntimeCall = RuntimeCall;143 #[cfg(feature = "preimage")]144 type Preimages = crate::Preimage;145 #[cfg(not(feature = "preimage"))]146 type Preimages = ();147 type WeightInfo = pallet_maintenance::weights::SubstrateWeight<Self>;148}runtime/common/dispatch.rsdiffbeforeafterboth--- a/runtime/common/dispatch.rs
+++ b/runtime/common/dispatch.rs
@@ -99,7 +99,10 @@
Ok(id)
}
- fn destroy(sender: T::CrossAccountId, collection: CollectionHandle<T>) -> DispatchResult {
+ fn destroy(sender: T::CrossAccountId, collection_id: CollectionId) -> DispatchResult {
+ let collection = <CollectionHandle<T>>::try_get(collection_id)?;
+ collection.check_is_internal()?;
+
match collection.mode {
CollectionMode::ReFungible => {
PalletRefungible::destroy_collection(RefungibleHandle::cast(collection), &sender)?
runtime/opal/src/lib.rsdiffbeforeafterboth--- a/runtime/opal/src/lib.rs
+++ b/runtime/opal/src/lib.rs
@@ -45,6 +45,7 @@
pub const RUNTIME_NAME: &str = "opal";
pub const TOKEN_SYMBOL: &str = "OPL";
+pub const DECIMALS: u8 = 18;
/// This runtime version.
pub const VERSION: RuntimeVersion = RuntimeVersion {
runtime/quartz/src/lib.rsdiffbeforeafterboth--- a/runtime/quartz/src/lib.rs
+++ b/runtime/quartz/src/lib.rs
@@ -48,6 +48,7 @@
#[cfg(not(feature = "become-sapphire"))]
pub const RUNTIME_NAME: &str = "quartz";
pub const TOKEN_SYMBOL: &str = "QTZ";
+pub const DECIMALS: u8 = 18;
/// This runtime version.
pub const VERSION: RuntimeVersion = RuntimeVersion {
runtime/unique/src/lib.rsdiffbeforeafterboth--- a/runtime/unique/src/lib.rs
+++ b/runtime/unique/src/lib.rs
@@ -45,6 +45,7 @@
pub const RUNTIME_NAME: &str = "unique";
pub const TOKEN_SYMBOL: &str = "UNQ";
+pub const DECIMALS: u8 = 18;
/// This runtime version.
pub const VERSION: RuntimeVersion = RuntimeVersion {
tests/src/eth/nativeFungible.test.tsdiffbeforeafterboth--- a/tests/src/eth/nativeFungible.test.ts
+++ b/tests/src/eth/nativeFungible.test.ts
@@ -15,29 +15,157 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
import {IKeyringPair} from '@polkadot/types/types';
-import {itEth, usingEthPlaygrounds} from './util';
+import {expect, itEth, usingEthPlaygrounds} from './util';
-describe('NativeFungible: Plain calls', () => {
+describe('NativeFungible: ERC20 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);
+ // [alice] = await helper.arrange.createAccounts([30n], donor);
});
});
- itEth.skip('Can perform approve()', async ({helper}) => {
+ itEth('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 expect(contract.methods.approve(spender, 100).call({from: owner})).to.be.rejectedWith('Approve not supported');
+ });
+
+ itEth('balanceOf()', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor, 123n);
+ const collectionAddress = helper.ethAddress.fromCollectionId(0);
+ const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+
+ const balance = await contract.methods.balanceOf(owner).call({from: owner});
+ expect(balance).to.be.eq('123000000000000000000');
+ });
+
+ itEth('decimals()', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const collectionAddress = helper.ethAddress.fromCollectionId(0);
+ const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+
+ const decimals = await contract.methods.decimals().call({from: owner});
+ expect(decimals).to.be.eq('18');
+ });
+
+ itEth('name()', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const collectionAddress = helper.ethAddress.fromCollectionId(0);
+ const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+
+ const name = await contract.methods.name().call({from: owner});
+ expect(name).to.be.eq('opal');
+ });
+
+ itEth('symbol()', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const collectionAddress = helper.ethAddress.fromCollectionId(0);
+ const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+
+ const name = await contract.methods.symbol().call({from: owner});
+ expect(name).to.be.eq('OPL');
+ });
+
+ itEth('totalSupply()', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const collectionAddress = helper.ethAddress.fromCollectionId(0);
+ const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+
+ const totalSupplyEth = BigInt(await contract.methods.totalSupply().call({from: owner}));
+ const totalSupplySub = await helper.balance.getTotalIssuance();
+ expect(totalSupplyEth).to.be.eq(totalSupplySub);
+ });
+
+ itEth('transfer()', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const receiver = await helper.eth.createAccountWithBalance(donor);
+ const collectionAddress = helper.ethAddress.fromCollectionId(0);
+ const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+
+ const balanceOwnerBefore = await helper.balance.getEthereum(owner);
+ const balanceReceiverBefore = await helper.balance.getEthereum(receiver);
+
+ await contract.methods.transfer(receiver, 50).send({from: owner});
+
+ const balanceOwnerAfter = await helper.balance.getEthereum(owner);
+ const balanceReceiverAfter = await helper.balance.getEthereum(receiver);
+
+ expect(balanceOwnerBefore - 50n > balanceOwnerAfter).to.be.true;
+ expect(balanceReceiverBefore === balanceReceiverAfter - 50n).to.be.true;
+ });
+ itEth('transferFrom()', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const receiver = await helper.eth.createAccountWithBalance(donor);
const collectionAddress = helper.ethAddress.fromCollectionId(0);
const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
- await contract.methods.approve(spender, 100).send({from: owner});
+ const balanceOwnerBefore = await helper.balance.getEthereum(owner);
+ const balanceReceiverBefore = await helper.balance.getEthereum(receiver);
+
+ await contract.methods.transferFrom(owner, receiver, 50).send({from: owner});
+
+ const balanceOwnerAfter = await helper.balance.getEthereum(owner);
+ const balanceReceiverAfter = await helper.balance.getEthereum(receiver);
+
+ expect(balanceOwnerBefore - 50n > balanceOwnerAfter).to.be.true;
+ expect(balanceReceiverBefore === balanceReceiverAfter - 50n).to.be.true;
+
+ await expect(contract.methods.transferFrom(receiver, receiver, 50).call({from: owner})).to.be.rejectedWith('no permission');
+ });
+});
+
+describe('NativeFungible: ERC20UniqueExtensions calls', () => {
+ let donor: IKeyringPair;
+
+ before(async function() {
+ await usingEthPlaygrounds(async (helper, privateKey) => {
+ donor = await privateKey({url: import.meta.url});
+ // [alice] = await helper.arrange.createAccounts([30n], donor);
+ });
+ });
+
+ itEth('transferCross()', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const receiver = await helper.ethCrossAccount.createAccountWithBalance(donor);
+ const collectionAddress = helper.ethAddress.fromCollectionId(0);
+ const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+
+ const balanceOwnerBefore = await helper.balance.getEthereum(owner);
+ const balanceReceiverBefore = await helper.balance.getEthereum(receiver.eth);
+
+ await contract.methods.transferCross(receiver, 50).send({from: owner});
+
+ const balanceOwnerAfter = await helper.balance.getEthereum(owner);
+ const balanceReceiverAfter = await helper.balance.getEthereum(receiver.eth);
+
+ expect(balanceOwnerBefore - 50n > balanceOwnerAfter).to.be.true;
+ expect(balanceReceiverBefore === balanceReceiverAfter - 50n).to.be.true;
+ });
+
+ itEth('transferFromCross()', async ({helper}) => {
+ const owner = await helper.ethCrossAccount.createAccountWithBalance(donor);
+ const receiver = await helper.ethCrossAccount.createAccountWithBalance(donor);
+ const collectionAddress = helper.ethAddress.fromCollectionId(0);
+ const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner.eth);
+
+ const balanceOwnerBefore = await helper.balance.getEthereum(owner.eth);
+ const balanceReceiverBefore = await helper.balance.getEthereum(receiver.eth);
+
+ await contract.methods.transferFromCross(owner, receiver, 50).send({from: owner.eth});
+
+ const balanceOwnerAfter = await helper.balance.getEthereum(owner.eth);
+ const balanceReceiverAfter = await helper.balance.getEthereum(receiver.eth);
+
+ expect(balanceOwnerBefore - 50n > balanceOwnerAfter).to.be.true;
+ expect(balanceReceiverBefore === balanceReceiverAfter - 50n).to.be.true;
+
+ await expect(contract.methods.transferFromCross(receiver, receiver, 50).call({from: owner.eth})).to.be.rejectedWith('no permission');
});
});
\ No newline at end of file
tests/src/eth/util/playgrounds/unique.dev.tsdiffbeforeafterboth--- a/tests/src/eth/util/playgrounds/unique.dev.ts
+++ b/tests/src/eth/util/playgrounds/unique.dev.ts
@@ -142,7 +142,7 @@
async collection(address: string, mode: TCollectionMode, caller?: string, mergeDeprecated = false) {
let abi;
- if (address === '0x17C4e6453cC49aaAAEaCA894E6d9683e00000000' && mode === 'ft') {
+ if (address === '0x17C4e6453cC49aaAAEaCA894E6d9683e00000000') {
abi = nativeFungibleAbi;
} else {
abi ={
tests/src/util/playgrounds/unique.tsdiffbeforeafterboth--- a/tests/src/util/playgrounds/unique.ts
+++ b/tests/src/util/playgrounds/unique.ts
@@ -2400,7 +2400,16 @@
return {free: accountInfo.free.toBigInt(), frozen: accountInfo.frozen.toBigInt(), reserved: accountInfo.reserved.toBigInt()};
}
- async getLocked(address: TSubstrateAccount): Promise<[{ id: string, amount: bigint, reason: string }]> {
+ /**
+ * Get total issuance
+ * @returns
+ */
+ async getTotalIssuance(): Promise<bigint> {
+ const total = (await this.helper.callRpc('api.query.balances.totalIssuance', []));
+ return total.toBigInt();
+ }
+
+ async getLocked(address: TSubstrateAccount): Promise<[{id: string, amount: bigint, reason: string}]> {
const locks = (await this.helper.callRpc('api.query.balances.locks', [address])).toHuman();
return locks.map((lock: any) => { return {id: lock.id, amount: BigInt(lock.amount.replace(/,/g, '')), reasons: lock.reasons}; });
}
@@ -2488,6 +2497,14 @@
}
/**
+ * Get total issuance
+ * @returns
+ */
+ getTotalIssuance(): Promise<bigint> {
+ return this.subBalanceGroup.getTotalIssuance();
+ }
+
+ /**
* Get locked balances
* @param address substrate address
* @returns locked balances with reason via api.query.balances.locks