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.rsdiffbeforeafterboth1296 sender: T::CrossAccountId,1296 sender: T::CrossAccountId,1297 collection_id: CollectionId,1297 collection_id: CollectionId,1298 ) -> DispatchResult {1298 ) -> DispatchResult {1299 let collection = <CollectionHandle<T>>::try_get(collection_id)?;1300 collection.check_is_internal()?;13011302 T::CollectionDispatch::destroy(sender, collection)?;1299 T::CollectionDispatch::destroy(sender, collection_id)?;130313001304 // TODO: basket cleanup should be moved elsewhere1301 // TODO: basket cleanup should be moved elsewhere1305 // Maybe runtime dispatch.rs should perform it?1302 // Maybe runtime dispatch.rs should perform it?runtime/common/config/pallets/mod.rsdiffbeforeafterboth--- a/runtime/common/config/pallets/mod.rs
+++ b/runtime/common/config/pallets/mod.rs
@@ -24,7 +24,8 @@
weights::CommonWeights,
RelayChainBlockNumberProvider,
},
- Runtime, RuntimeEvent, RuntimeCall, RuntimeOrigin, RUNTIME_NAME, TOKEN_SYMBOL, Balances,
+ Runtime, RuntimeEvent, RuntimeCall, RuntimeOrigin, RUNTIME_NAME, TOKEN_SYMBOL, DECIMALS,
+ Balances,
};
use frame_support::traits::{ConstU32, ConstU64, Currency};
use up_common::{
@@ -53,7 +54,7 @@
parameter_types! {
pub const CollectionCreationPrice: Balance = 2 * UNIQUE;
- pub const Decimals: u8 = 32;
+ pub const Decimals: u8 = DECIMALS;
pub TreasuryAccountId: AccountId = TreasuryModuleId::get().into_account_truncating();
pub Name: String = RUNTIME_NAME.to_string();
pub Symbol: String = TOKEN_SYMBOL.to_string();
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