difftreelog
feat balanceOfCross for fingible
in: master
8 files changed
pallets/fungible/src/erc.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/>.1617//! ERC-20 standart support implementation.1819extern crate alloc;20use core::char::{REPLACEMENT_CHARACTER, decode_utf16};21use core::convert::TryInto;22use evm_coder::AbiCoder;23use evm_coder::{abi::AbiType, ToLog, generate_stubgen, solidity_interface, types::*};24use up_data_structs::CollectionMode;25use pallet_common::{26 CollectionHandle,27 erc::{CommonEvmHandler, PrecompileResult, CollectionCall},28 eth::CrossAddress,29};30use sp_std::vec::Vec;31use pallet_evm::{account::CrossAccountId, PrecompileHandle};32use pallet_evm_coder_substrate::{33 call, dispatch_to_evm,34 execution::{PreDispatch, Result},35 frontier_contract,36};37use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};38use sp_core::{U256, Get};3940use crate::{41 Allowance, Balance, Config, FungibleHandle, Pallet, TotalSupply, SelfWeightOf,42 weights::WeightInfo,43};4445frontier_contract! {46 macro_rules! FungibleHandle_result {...}47 impl<T: Config> Contract for FungibleHandle<T> {...}48}4950#[derive(ToLog)]51pub enum ERC20Events {52 Transfer {53 #[indexed]54 from: Address,55 #[indexed]56 to: Address,57 value: U256,58 },59 Approval {60 #[indexed]61 owner: Address,62 #[indexed]63 spender: Address,64 value: U256,65 },66}6768#[derive(AbiCoder, Debug)]69pub struct AmountForAddress {70 to: Address,71 amount: U256,72}7374#[solidity_interface(name = ERC20, events(ERC20Events), enum(derive(PreDispatch)), enum_attr(weight), expect_selector = 0x942e8b22)]75impl<T: Config> FungibleHandle<T> {76 fn name(&self) -> Result<String> {77 Ok(decode_utf16(self.name.iter().copied())78 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))79 .collect::<String>())80 }81 fn symbol(&self) -> Result<String> {82 Ok(String::from_utf8_lossy(&self.token_prefix).into())83 }84 fn total_supply(&self) -> Result<U256> {85 self.consume_store_reads(1)?;86 Ok(<TotalSupply<T>>::get(self.id).into())87 }8889 fn decimals(&self) -> Result<u8> {90 Ok(if let CollectionMode::Fungible(decimals) = &self.mode {91 *decimals92 } else {93 unreachable!()94 })95 }96 fn balance_of(&self, owner: Address) -> Result<U256> {97 self.consume_store_reads(1)?;98 let owner = T::CrossAccountId::from_eth(owner);99 let balance = <Balance<T>>::get((self.id, owner));100 Ok(balance.into())101 }102 #[weight(<SelfWeightOf<T>>::transfer())]103 fn transfer(&mut self, caller: Caller, to: Address, amount: U256) -> Result<bool> {104 let caller = T::CrossAccountId::from_eth(caller);105 let to = T::CrossAccountId::from_eth(to);106 let amount = amount.try_into().map_err(|_| "amount overflow")?;107 let budget = self108 .recorder109 .weight_calls_budget(<StructureWeight<T>>::find_parent());110111 <Pallet<T>>::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?;112 Ok(true)113 }114115 #[weight(<SelfWeightOf<T>>::transfer_from())]116 fn transfer_from(117 &mut self,118 caller: Caller,119 from: Address,120 to: Address,121 amount: U256,122 ) -> Result<bool> {123 let caller = T::CrossAccountId::from_eth(caller);124 let from = T::CrossAccountId::from_eth(from);125 let to = T::CrossAccountId::from_eth(to);126 let amount = amount.try_into().map_err(|_| "amount overflow")?;127 let budget = self128 .recorder129 .weight_calls_budget(<StructureWeight<T>>::find_parent());130131 <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)132 .map_err(dispatch_to_evm::<T>)?;133 Ok(true)134 }135 #[weight(<SelfWeightOf<T>>::approve())]136 fn approve(&mut self, caller: Caller, spender: Address, amount: U256) -> Result<bool> {137 let caller = T::CrossAccountId::from_eth(caller);138 let spender = T::CrossAccountId::from_eth(spender);139 let amount = amount.try_into().map_err(|_| "amount overflow")?;140141 <Pallet<T>>::set_allowance(self, &caller, &spender, amount)142 .map_err(dispatch_to_evm::<T>)?;143 Ok(true)144 }145 fn allowance(&self, owner: Address, spender: Address) -> Result<U256> {146 self.consume_store_reads(1)?;147 let owner = T::CrossAccountId::from_eth(owner);148 let spender = T::CrossAccountId::from_eth(spender);149150 Ok(<Allowance<T>>::get((self.id, owner, spender)).into())151 }152}153154#[solidity_interface(name = ERC20Mintable, enum(derive(PreDispatch)), enum_attr(weight))]155impl<T: Config> FungibleHandle<T> {156 /// Mint tokens for `to` account.157 /// @param to account that will receive minted tokens158 /// @param amount amount of tokens to mint159 #[weight(<SelfWeightOf<T>>::create_item())]160 fn mint(&mut self, caller: Caller, to: Address, amount: U256) -> Result<bool> {161 let caller = T::CrossAccountId::from_eth(caller);162 let to = T::CrossAccountId::from_eth(to);163 let amount = amount.try_into().map_err(|_| "amount overflow")?;164 let budget = self165 .recorder166 .weight_calls_budget(<StructureWeight<T>>::find_parent());167 <Pallet<T>>::create_item(self, &caller, (to, amount), &budget)168 .map_err(dispatch_to_evm::<T>)?;169 Ok(true)170 }171}172173#[solidity_interface(name = ERC20UniqueExtensions, enum(derive(PreDispatch)), enum_attr(weight))]174impl<T: Config> FungibleHandle<T>175where176 T::AccountId: From<[u8; 32]>,177{178 /// @dev Function to check the amount of tokens that an owner allowed to a spender.179 /// @param owner crossAddress The address which owns the funds.180 /// @param spender crossAddress The address which will spend the funds.181 /// @return A uint256 specifying the amount of tokens still available for the spender.182 fn allowance_cross(&self, owner: CrossAddress, spender: CrossAddress) -> Result<U256> {183 let owner = owner.into_sub_cross_account::<T>()?;184 let spender = spender.into_sub_cross_account::<T>()?;185186 Ok(<Allowance<T>>::get((self.id, owner, spender)).into())187 }188189 /// @notice A description for the collection.190 fn description(&self) -> String {191 decode_utf16(self.description.iter().copied())192 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))193 .collect::<String>()194 }195196 #[weight(<SelfWeightOf<T>>::create_item())]197 fn mint_cross(&mut self, caller: Caller, to: CrossAddress, amount: U256) -> Result<bool> {198 let caller = T::CrossAccountId::from_eth(caller);199 let to = to.into_sub_cross_account::<T>()?;200 let amount = amount.try_into().map_err(|_| "amount overflow")?;201 let budget = self202 .recorder203 .weight_calls_budget(<StructureWeight<T>>::find_parent());204 <Pallet<T>>::create_item(&self, &caller, (to, amount), &budget)205 .map_err(dispatch_to_evm::<T>)?;206 Ok(true)207 }208209 #[weight(<SelfWeightOf<T>>::approve())]210 fn approve_cross(211 &mut self,212 caller: Caller,213 spender: CrossAddress,214 amount: U256,215 ) -> Result<bool> {216 let caller = T::CrossAccountId::from_eth(caller);217 let spender = spender.into_sub_cross_account::<T>()?;218 let amount = amount.try_into().map_err(|_| "amount overflow")?;219220 <Pallet<T>>::set_allowance(self, &caller, &spender, amount)221 .map_err(dispatch_to_evm::<T>)?;222 Ok(true)223 }224225 /// Burn tokens from account226 /// @dev Function that burns an `amount` of the tokens of a given account,227 /// deducting from the sender's allowance for said account.228 /// @param from The account whose tokens will be burnt.229 /// @param amount The amount that will be burnt.230 #[solidity(hide)]231 #[weight(<SelfWeightOf<T>>::burn_from())]232 fn burn_from(&mut self, caller: Caller, from: Address, amount: U256) -> Result<bool> {233 let caller = T::CrossAccountId::from_eth(caller);234 let from = T::CrossAccountId::from_eth(from);235 let amount = amount.try_into().map_err(|_| "amount overflow")?;236 let budget = self237 .recorder238 .weight_calls_budget(<StructureWeight<T>>::find_parent());239240 <Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)241 .map_err(dispatch_to_evm::<T>)?;242 Ok(true)243 }244245 /// Burn tokens from account246 /// @dev Function that burns an `amount` of the tokens of a given account,247 /// deducting from the sender's allowance for said account.248 /// @param from The account whose tokens will be burnt.249 /// @param amount The amount that will be burnt.250 #[weight(<SelfWeightOf<T>>::burn_from())]251 fn burn_from_cross(252 &mut self,253 caller: Caller,254 from: CrossAddress,255 amount: U256,256 ) -> Result<bool> {257 let caller = T::CrossAccountId::from_eth(caller);258 let from = from.into_sub_cross_account::<T>()?;259 let amount = amount.try_into().map_err(|_| "amount overflow")?;260 let budget = self261 .recorder262 .weight_calls_budget(<StructureWeight<T>>::find_parent());263264 <Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)265 .map_err(dispatch_to_evm::<T>)?;266 Ok(true)267 }268269 /// Mint tokens for multiple accounts.270 /// @param amounts array of pairs of account address and amount271 #[weight(<SelfWeightOf<T>>::create_multiple_items_ex(amounts.len() as u32))]272 fn mint_bulk(&mut self, caller: Caller, amounts: Vec<AmountForAddress>) -> Result<bool> {273 let caller = T::CrossAccountId::from_eth(caller);274 let budget = self275 .recorder276 .weight_calls_budget(<StructureWeight<T>>::find_parent());277 let amounts = amounts278 .into_iter()279 .map(|AmountForAddress { to, amount }| {280 Ok((281 T::CrossAccountId::from_eth(to),282 amount.try_into().map_err(|_| "amount overflow")?,283 ))284 })285 .collect::<Result<_>>()?;286287 <Pallet<T>>::create_multiple_items(self, &caller, amounts, &budget)288 .map_err(dispatch_to_evm::<T>)?;289 Ok(true)290 }291292 #[weight(<SelfWeightOf<T>>::transfer())]293 fn transfer_cross(&mut self, caller: Caller, to: CrossAddress, amount: U256) -> Result<bool> {294 let caller = T::CrossAccountId::from_eth(caller);295 let to = to.into_sub_cross_account::<T>()?;296 let amount = amount.try_into().map_err(|_| "amount overflow")?;297 let budget = self298 .recorder299 .weight_calls_budget(<StructureWeight<T>>::find_parent());300301 <Pallet<T>>::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?;302 Ok(true)303 }304305 #[weight(<SelfWeightOf<T>>::transfer_from())]306 fn transfer_from_cross(307 &mut self,308 caller: Caller,309 from: CrossAddress,310 to: CrossAddress,311 amount: U256,312 ) -> Result<bool> {313 let caller = T::CrossAccountId::from_eth(caller);314 let from = from.into_sub_cross_account::<T>()?;315 let to = to.into_sub_cross_account::<T>()?;316 let amount = amount.try_into().map_err(|_| "amount overflow")?;317 let budget = self318 .recorder319 .weight_calls_budget(<StructureWeight<T>>::find_parent());320321 <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)322 .map_err(dispatch_to_evm::<T>)?;323 Ok(true)324 }325326 /// @notice Returns collection helper contract address327 fn collection_helper_address(&self) -> Result<Address> {328 Ok(T::ContractAddress::get())329 }330}331332#[solidity_interface(333 name = UniqueFungible,334 is(335 ERC20,336 ERC20Mintable,337 ERC20UniqueExtensions,338 Collection(via(common_mut returns CollectionHandle<T>)),339 ),340 enum(derive(PreDispatch))341)]342impl<T: Config> FungibleHandle<T> where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]> {}343344generate_stubgen!(gen_impl, UniqueFungibleCall<()>, true);345generate_stubgen!(gen_iface, UniqueFungibleCall<()>, false);346347impl<T: Config> CommonEvmHandler for FungibleHandle<T>348where349 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,350{351 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueFungible.raw");352353 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {354 call::<T, UniqueFungibleCall<T>, _, _>(handle, self)355 }356}pallets/fungible/src/stubs/UniqueFungible.rawdiffbeforeafterbothbinary blob — no preview
pallets/fungible/src/stubs/UniqueFungible.soldiffbeforeafterboth--- a/pallets/fungible/src/stubs/UniqueFungible.sol
+++ b/pallets/fungible/src/stubs/UniqueFungible.sol
@@ -511,7 +511,7 @@
bytes value;
}
-/// @dev the ERC-165 identifier for this interface is 0x85d7dea6
+/// @dev the ERC-165 identifier for this interface is 0x69d14d3e
contract ERC20UniqueExtensions is Dummy, ERC165 {
/// @dev Function to check the amount of tokens that an owner allowed to a spender.
/// @param owner crossAddress The address which owns the funds.
@@ -630,6 +630,18 @@
dummy;
return 0x0000000000000000000000000000000000000000;
}
+
+ /// @notice Balance of account
+ /// @param owner An cross address for whom to query the balance
+ /// @return The number of fingibles owned by `owner`, possibly zero
+ /// @dev EVM selector for this function is: 0xec069398,
+ /// or in textual repr: balanceOfCross((address,uint256))
+ function balanceOfCross(CrossAddress memory owner) public view returns (uint256) {
+ require(false, stub_error);
+ owner;
+ dummy;
+ return 0;
+ }
}
struct AmountForAddress {
tests/src/eth/abi/fungible.jsondiffbeforeafterboth--- a/tests/src/eth/abi/fungible.json
+++ b/tests/src/eth/abi/fungible.json
@@ -181,6 +181,23 @@
{ "internalType": "uint256", "name": "sub", "type": "uint256" }
],
"internalType": "struct CrossAddress",
+ "name": "owner",
+ "type": "tuple"
+ }
+ ],
+ "name": "balanceOfCross",
+ "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "components": [
+ { "internalType": "address", "name": "eth", "type": "address" },
+ { "internalType": "uint256", "name": "sub", "type": "uint256" }
+ ],
+ "internalType": "struct CrossAddress",
"name": "from",
"type": "tuple"
},
tests/src/eth/api/UniqueFungible.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueFungible.sol
+++ b/tests/src/eth/api/UniqueFungible.sol
@@ -353,7 +353,7 @@
bytes value;
}
-/// @dev the ERC-165 identifier for this interface is 0x85d7dea6
+/// @dev the ERC-165 identifier for this interface is 0x69d14d3e
interface ERC20UniqueExtensions is Dummy, ERC165 {
/// @dev Function to check the amount of tokens that an owner allowed to a spender.
/// @param owner crossAddress The address which owns the funds.
@@ -416,6 +416,13 @@
/// @dev EVM selector for this function is: 0x1896cce6,
/// or in textual repr: collectionHelperAddress()
function collectionHelperAddress() external view returns (address);
+
+ /// @notice Balance of account
+ /// @param owner An cross address for whom to query the balance
+ /// @return The number of fingibles owned by `owner`, possibly zero
+ /// @dev EVM selector for this function is: 0xec069398,
+ /// or in textual repr: balanceOfCross((address,uint256))
+ function balanceOfCross(CrossAddress memory owner) external view returns (uint256);
}
struct AmountForAddress {
tests/src/eth/fungible.test.tsdiffbeforeafterboth--- a/tests/src/eth/fungible.test.ts
+++ b/tests/src/eth/fungible.test.ts
@@ -427,6 +427,22 @@
const toBalanceAfter = await collection.getBalance({Ethereum: receiver});
expect(toBalanceAfter - toBalanceBefore).to.be.eq(51n);
});
+
+ itEth('Check balanceOfCross()', async ({helper}) => {
+ const collection = await helper.ft.mintCollection(alice, {});
+ const owner = await helper.ethCrossAccount.createAccountWithBalance(donor);
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner.eth);
+
+ expect(BigInt(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})) === 0n).to.be.true;
+
+ let sum = 0n;
+ for (let i = 1n; i < 100n; i++) {
+ await collection.mint(alice, 100n, {Ethereum: owner.eth});
+ sum += 100n;
+ expect(BigInt(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})) === sum).to.be.true;
+ }
+ });
});
describe('Fungible: Fees', () => {
tests/src/eth/reFungible.test.tsdiffbeforeafterboth--- a/tests/src/eth/reFungible.test.ts
+++ b/tests/src/eth/reFungible.test.ts
@@ -585,7 +585,7 @@
expect(event.returnValues.tokenId).to.equal(tokenId.toString());
});
- itEth('Check crossBalanceOf()', async ({helper}) => {
+ itEth('Check balanceOfCross()', async ({helper}) => {
const collection = await helper.rft.mintCollection(minter, {});
const owner = await helper.ethCrossAccount.createAccountWithBalance(donor);
const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
tests/src/eth/tokens/minting.test.tsdiffbeforeafterboth--- a/tests/src/eth/tokens/minting.test.ts
+++ b/tests/src/eth/tokens/minting.test.ts
@@ -19,7 +19,7 @@
import {expect, itEth, usingEthPlaygrounds} from '../util';
-describe.only('Minting tokens', () => {
+describe('Minting tokens', () => {
let donor: IKeyringPair;
let alice: IKeyringPair;