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.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.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 frame_support::{dispatch::DispatchResult, ensure};18use pallet_evm::{PrecompileHandle, PrecompileResult};19use sp_core::H160;20use sp_runtime::DispatchError;21use sp_std::{borrow::ToOwned, vec::Vec};22use pallet_common::{23 CollectionById, CollectionHandle, CommonCollectionOperations, erc::CommonEvmHandler,24 eth::map_eth_to_id,25};26pub use pallet_common::dispatch::CollectionDispatch;27use pallet_fungible::{Pallet as PalletFungible, FungibleHandle};28use pallet_balances_adapter::{Pallet as PalletNativeFungible, NativeFungibleHandle};29use pallet_nonfungible::{Pallet as PalletNonfungible, NonfungibleHandle};30use pallet_refungible::{31 Pallet as PalletRefungible, RefungibleHandle, erc_token::RefungibleTokenHandle,32};33use up_data_structs::{34 CollectionMode, CreateCollectionData, MAX_DECIMAL_POINTS, mapping::TokenAddressMapping,35 CollectionId, CollectionFlags,36};3738#[cfg(not(feature = "refungible"))]39use pallet_common::unsupported;4041pub enum CollectionDispatchT<T>42where43 T: pallet_fungible::Config44 + pallet_nonfungible::Config45 + pallet_refungible::Config46 + pallet_balances_adapter::Config,47{48 Fungible(FungibleHandle<T>),49 Nonfungible(NonfungibleHandle<T>),50 Refungible(RefungibleHandle<T>),51 NativeFungible(NativeFungibleHandle<T>),52}5354impl<T> CollectionDispatch<T> for CollectionDispatchT<T>55where56 T: pallet_common::Config57 + pallet_unique::Config58 + pallet_fungible::Config59 + pallet_nonfungible::Config60 + pallet_refungible::Config61 + pallet_balances_adapter::Config,62{63 fn check_is_internal(&self) -> DispatchResult {64 match self {65 Self::Fungible(h) => h.check_is_internal(),66 Self::Nonfungible(h) => h.check_is_internal(),67 Self::Refungible(h) => h.check_is_internal(),68 Self::NativeFungible(h) => h.check_is_internal(),69 }70 }7172 fn create(73 sender: T::CrossAccountId,74 payer: T::CrossAccountId,75 data: CreateCollectionData<T::AccountId>,76 flags: CollectionFlags,77 ) -> Result<CollectionId, DispatchError> {78 let id = match data.mode {79 CollectionMode::NFT => {80 <PalletNonfungible<T>>::init_collection(sender, payer, data, flags)?81 }82 CollectionMode::Fungible(decimal_points) => {83 // check params84 ensure!(85 decimal_points <= MAX_DECIMAL_POINTS,86 pallet_unique::Error::<T>::CollectionDecimalPointLimitExceeded87 );88 <PalletFungible<T>>::init_collection(sender, payer, data, flags)?89 }9091 #[cfg(feature = "refungible")]92 CollectionMode::ReFungible => {93 <PalletRefungible<T>>::init_collection(sender, payer, data, flags)?94 }9596 #[cfg(not(feature = "refungible"))]97 CollectionMode::ReFungible => return unsupported!(T),98 };99 Ok(id)100 }101102 fn destroy(sender: T::CrossAccountId, collection: CollectionHandle<T>) -> DispatchResult {103 match collection.mode {104 CollectionMode::ReFungible => {105 PalletRefungible::destroy_collection(RefungibleHandle::cast(collection), &sender)?106 }107 CollectionMode::Fungible(_) => {108 PalletFungible::destroy_collection(FungibleHandle::cast(collection), &sender)?109 }110 CollectionMode::NFT => {111 PalletNonfungible::destroy_collection(NonfungibleHandle::cast(collection), &sender)?112 }113 }114 Ok(())115 }116117 fn dispatch(collection_id: CollectionId) -> Result<Self, DispatchError> {118 if collection_id == CollectionId(0) {119 return Ok(Self::NativeFungible(NativeFungibleHandle::new()));120 }121122 let handle = <CollectionHandle<T>>::try_get(collection_id)?;123 Ok(match handle.mode {124 CollectionMode::Fungible(_) => Self::Fungible(FungibleHandle::cast(handle)),125 CollectionMode::NFT => Self::Nonfungible(NonfungibleHandle::cast(handle)),126 CollectionMode::ReFungible => Self::Refungible(RefungibleHandle::cast(handle)),127 })128 }129130 fn as_dyn(&self) -> &dyn CommonCollectionOperations<T> {131 match self {132 Self::Fungible(h) => h,133 Self::Nonfungible(h) => h,134 Self::Refungible(h) => h,135 Self::NativeFungible(h) => h,136 }137 }138}139140impl<T> pallet_evm::OnMethodCall<T> for CollectionDispatchT<T>141where142 T: pallet_common::Config143 + pallet_unique::Config144 + pallet_fungible::Config145 + pallet_nonfungible::Config146 + pallet_refungible::Config147 + pallet_balances_adapter::Config,148 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,149{150 fn is_reserved(target: &H160) -> bool {151 map_eth_to_id(target).is_some()152 }153 fn is_used(target: &H160) -> bool {154 map_eth_to_id(target)155 .map(<CollectionById<T>>::contains_key)156 .unwrap_or(false)157 }158 fn get_code(target: &H160) -> Option<Vec<u8>> {159 if let Some(collection_id) = map_eth_to_id(target) {160 let collection = <CollectionById<T>>::get(collection_id)?;161 Some(162 match collection.mode {163 CollectionMode::NFT => <NonfungibleHandle<T>>::CODE,164 CollectionMode::Fungible(_) => <FungibleHandle<T>>::CODE,165 CollectionMode::ReFungible => <RefungibleHandle<T>>::CODE,166 }167 .to_owned(),168 )169 } else if let Some((collection_id, _token_id)) =170 <T as pallet_common::Config>::EvmTokenAddressMapping::address_to_token(target)171 {172 let collection = <CollectionById<T>>::get(collection_id)?;173 if collection.mode != CollectionMode::ReFungible {174 return None;175 }176 // TODO: check token existence177 Some(<RefungibleTokenHandle<T>>::CODE.to_owned())178 } else {179 None180 }181 }182 fn call(handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {183 if let Some(collection_id) = map_eth_to_id(&handle.code_address()) {184 if collection_id == CollectionId(0) {185 <NativeFungibleHandle<T>>::new().call(handle)186 } else {187 let collection = <CollectionHandle<T>>::new_with_gas_limit(188 collection_id,189 handle.remaining_gas(),190 )?;191192 match collection.mode {193 CollectionMode::Fungible(_) => FungibleHandle::cast(collection).call(handle),194 CollectionMode::NFT => NonfungibleHandle::cast(collection).call(handle),195 CollectionMode::ReFungible => RefungibleHandle::cast(collection).call(handle),196 }197 }198 } else if let Some((collection_id, token_id)) =199 <T as pallet_common::Config>::EvmTokenAddressMapping::address_to_token(200 &handle.code_address(),201 ) {202 let collection =203 <CollectionHandle<T>>::new_with_gas_limit(collection_id, handle.remaining_gas())?;204 if collection.mode != CollectionMode::ReFungible {205 return None;206 }207208 let h = RefungibleHandle::cast(collection);209 // TODO: check token existence210 RefungibleTokenHandle(h, token_id).call(handle)211 } else {212 None213 }214 }215}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