difftreelog
Merge pull request #919 from UniqueNetwork/feature/crossBalanceOf
in: master
26 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}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/>.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 }330331 /// @notice Balance of account332 /// @param owner An cross address for whom to query the balance333 /// @return The number of fingibles owned by `owner`, possibly zero334 fn balance_of_cross(&self, owner: CrossAddress) -> Result<U256> {335 self.consume_store_reads(1)?;336 let balance = <Balance<T>>::get((self.id, owner.into_sub_cross_account::<T>()?));337 Ok(balance.into())338 }339}340341#[solidity_interface(342 name = UniqueFungible,343 is(344 ERC20,345 ERC20Mintable,346 ERC20UniqueExtensions,347 Collection(via(common_mut returns CollectionHandle<T>)),348 ),349 enum(derive(PreDispatch))350)]351impl<T: Config> FungibleHandle<T> where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]> {}352353generate_stubgen!(gen_impl, UniqueFungibleCall<()>, true);354generate_stubgen!(gen_iface, UniqueFungibleCall<()>, false);355356impl<T: Config> CommonEvmHandler for FungibleHandle<T>357where358 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,359{360 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueFungible.raw");361362 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {363 call::<T, UniqueFungibleCall<T>, _, _>(handle, self)364 }365}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 {
pallets/nonfungible/src/erc.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -749,12 +749,29 @@
/// Returns the owner (in cross format) of the token.
///
/// @param tokenId Id for the token.
+ #[solidity(hide)]
fn cross_owner_of(&self, token_id: U256) -> Result<eth::CrossAddress> {
+ Self::owner_of_cross(&self, token_id)
+ }
+
+ /// Returns the owner (in cross format) of the token.
+ ///
+ /// @param tokenId Id for the token.
+ fn owner_of_cross(&self, token_id: U256) -> Result<eth::CrossAddress> {
Self::token_owner(&self, token_id.try_into()?)
.map(|o| eth::CrossAddress::from_sub_cross_account::<T>(&o))
.map_err(|_| Error::Revert("token not found".into()))
}
+ /// @notice Count all NFTs assigned to an owner
+ /// @param owner An cross address for whom to query the balance
+ /// @return The number of NFTs owned by `owner`, possibly zero
+ fn balance_of_cross(&self, owner: eth::CrossAddress) -> Result<U256> {
+ self.consume_store_reads(1)?;
+ let balance = <AccountBalance<T>>::get((self.id, owner.into_sub_cross_account::<T>()?));
+ Ok(balance.into())
+ }
+
/// Returns the token properties.
///
/// @param tokenId Id for the token.
pallets/nonfungible/src/stubs/UniqueNFT.rawdiffbeforeafterbothbinary blob — no preview
pallets/nonfungible/src/stubs/UniqueNFT.soldiffbeforeafterboth--- a/pallets/nonfungible/src/stubs/UniqueNFT.sol
+++ b/pallets/nonfungible/src/stubs/UniqueNFT.sol
@@ -774,7 +774,7 @@
}
/// @title Unique extensions for ERC721.
-/// @dev the ERC-165 identifier for this interface is 0x16de3152
+/// @dev the ERC-165 identifier for this interface is 0x307b061a
contract ERC721UniqueExtensions is Dummy, ERC165 {
/// @notice A descriptive name for a collection of NFTs in this contract
/// @dev EVM selector for this function is: 0x06fdde03,
@@ -803,18 +803,42 @@
return "";
}
+ // /// Returns the owner (in cross format) of the token.
+ // ///
+ // /// @param tokenId Id for the token.
+ // /// @dev EVM selector for this function is: 0x2b29dace,
+ // /// or in textual repr: crossOwnerOf(uint256)
+ // function crossOwnerOf(uint256 tokenId) public view returns (CrossAddress memory) {
+ // require(false, stub_error);
+ // tokenId;
+ // dummy;
+ // return CrossAddress(0x0000000000000000000000000000000000000000,0);
+ // }
+
/// Returns the owner (in cross format) of the token.
///
/// @param tokenId Id for the token.
- /// @dev EVM selector for this function is: 0x2b29dace,
- /// or in textual repr: crossOwnerOf(uint256)
- function crossOwnerOf(uint256 tokenId) public view returns (CrossAddress memory) {
+ /// @dev EVM selector for this function is: 0xcaa3a4d0,
+ /// or in textual repr: ownerOfCross(uint256)
+ function ownerOfCross(uint256 tokenId) public view returns (CrossAddress memory) {
require(false, stub_error);
tokenId;
dummy;
return CrossAddress(0x0000000000000000000000000000000000000000, 0);
}
+ /// @notice Count all NFTs assigned to an owner
+ /// @param owner An cross address for whom to query the balance
+ /// @return The number of NFTs 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;
+ }
+
/// Returns the token properties.
///
/// @param tokenId Id for the token.
pallets/refungible/src/erc.rsdiffbeforeafterboth--- a/pallets/refungible/src/erc.rs
+++ b/pallets/refungible/src/erc.rs
@@ -783,7 +783,15 @@
/// Returns the owner (in cross format) of the token.
///
/// @param tokenId Id for the token.
+ #[solidity(hide)]
fn cross_owner_of(&self, token_id: U256) -> Result<eth::CrossAddress> {
+ Self::owner_of_cross(&self, token_id)
+ }
+
+ /// Returns the owner (in cross format) of the token.
+ ///
+ /// @param tokenId Id for the token.
+ fn owner_of_cross(&self, token_id: U256) -> Result<eth::CrossAddress> {
Self::token_owner(&self, token_id.try_into()?)
.map(|o| eth::CrossAddress::from_sub_cross_account::<T>(&o))
.or_else(|err| match err {
@@ -794,6 +802,15 @@
})
}
+ /// @notice Count all RFTs assigned to an owner
+ /// @param owner An cross address for whom to query the balance
+ /// @return The number of RFTs owned by `owner`, possibly zero
+ fn balance_of_cross(&self, owner: eth::CrossAddress) -> Result<U256> {
+ self.consume_store_reads(1)?;
+ let balance = <AccountBalance<T>>::get((self.id, owner.into_sub_cross_account::<T>()?));
+ Ok(balance.into())
+ }
+
/// Returns the token properties.
///
/// @param tokenId Id for the token.
pallets/refungible/src/erc_token.rsdiffbeforeafterboth--- a/pallets/refungible/src/erc_token.rs
+++ b/pallets/refungible/src/erc_token.rs
@@ -283,6 +283,16 @@
.map_err(dispatch_to_evm::<T>)?;
Ok(true)
}
+
+ /// @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
+ fn balance_of_cross(&self, owner: CrossAddress) -> Result<U256> {
+ self.consume_store_reads(1)?;
+ let balance = <Balance<T>>::get((self.id, self.1, owner.into_sub_cross_account::<T>()?));
+ Ok(balance.into())
+ }
+
/// @dev Function that changes total amount of the tokens.
/// Throws if `msg.sender` doesn't owns all of the tokens.
/// @param amount New total amount of the tokens.
pallets/refungible/src/stubs/UniqueRefungible.rawdiffbeforeafterbothbinary blob — no preview
pallets/refungible/src/stubs/UniqueRefungible.soldiffbeforeafterboth--- a/pallets/refungible/src/stubs/UniqueRefungible.sol
+++ b/pallets/refungible/src/stubs/UniqueRefungible.sol
@@ -774,7 +774,7 @@
}
/// @title Unique extensions for ERC721.
-/// @dev the ERC-165 identifier for this interface is 0xb365c124
+/// @dev the ERC-165 identifier for this interface is 0x95c0f66c
contract ERC721UniqueExtensions is Dummy, ERC165 {
/// @notice A descriptive name for a collection of NFTs in this contract
/// @dev EVM selector for this function is: 0x06fdde03,
@@ -803,18 +803,42 @@
return "";
}
+ // /// Returns the owner (in cross format) of the token.
+ // ///
+ // /// @param tokenId Id for the token.
+ // /// @dev EVM selector for this function is: 0x2b29dace,
+ // /// or in textual repr: crossOwnerOf(uint256)
+ // function crossOwnerOf(uint256 tokenId) public view returns (CrossAddress memory) {
+ // require(false, stub_error);
+ // tokenId;
+ // dummy;
+ // return CrossAddress(0x0000000000000000000000000000000000000000,0);
+ // }
+
/// Returns the owner (in cross format) of the token.
///
/// @param tokenId Id for the token.
- /// @dev EVM selector for this function is: 0x2b29dace,
- /// or in textual repr: crossOwnerOf(uint256)
- function crossOwnerOf(uint256 tokenId) public view returns (CrossAddress memory) {
+ /// @dev EVM selector for this function is: 0xcaa3a4d0,
+ /// or in textual repr: ownerOfCross(uint256)
+ function ownerOfCross(uint256 tokenId) public view returns (CrossAddress memory) {
require(false, stub_error);
tokenId;
dummy;
return CrossAddress(0x0000000000000000000000000000000000000000, 0);
}
+ /// @notice Count all RFTs assigned to an owner
+ /// @param owner An cross address for whom to query the balance
+ /// @return The number of RFTs 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;
+ }
+
/// Returns the token properties.
///
/// @param tokenId Id for the token.
pallets/refungible/src/stubs/UniqueRefungibleToken.rawdiffbeforeafterbothbinary blob — no preview
pallets/refungible/src/stubs/UniqueRefungibleToken.soldiffbeforeafterboth--- a/pallets/refungible/src/stubs/UniqueRefungibleToken.sol
+++ b/pallets/refungible/src/stubs/UniqueRefungibleToken.sol
@@ -36,7 +36,7 @@
}
}
-/// @dev the ERC-165 identifier for this interface is 0x01d536fc
+/// @dev the ERC-165 identifier for this interface is 0xedd3a564
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.
@@ -97,6 +97,18 @@
return false;
}
+ /// @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;
+ }
+
/// @dev Function that changes total amount of the tokens.
/// Throws if `msg.sender` doesn't owns all of the tokens.
/// @param amount New total amount of the tokens.
runtime/common/ethereum/sponsoring/refungible.rsdiffbeforeafterboth--- a/runtime/common/ethereum/sponsoring/refungible.rs
+++ b/runtime/common/ethereum/sponsoring/refungible.rs
@@ -227,6 +227,8 @@
| Symbol
| Description
| CrossOwnerOf { .. }
+ | OwnerOfCross { .. }
+ | BalanceOfCross { .. }
| Properties { .. }
| NextTokenId
| TokenContractAddress { .. }
@@ -341,9 +343,11 @@
ERC165Call(_, _) => None,
// Not sponsored
- AllowanceCross { .. } | BurnFrom { .. } | BurnFromCross { .. } | Repartition { .. } => {
- None
- }
+ AllowanceCross { .. }
+ | BalanceOfCross { .. }
+ | BurnFrom { .. }
+ | BurnFromCross { .. }
+ | Repartition { .. } => None,
TransferCross { .. } | TransferFromCross { .. } => {
let RefungibleTokenHandle(handle, token_id) = token;
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/abi/nonFungible.jsondiffbeforeafterboth--- a/tests/src/eth/abi/nonFungible.json
+++ b/tests/src/eth/abi/nonFungible.json
@@ -177,6 +177,23 @@
},
{
"inputs": [
+ {
+ "components": [
+ { "internalType": "address", "name": "eth", "type": "address" },
+ { "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": [
{ "internalType": "uint256", "name": "tokenId", "type": "uint256" }
],
"name": "burn",
@@ -386,25 +403,6 @@
},
{
"inputs": [
- { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
- ],
- "name": "crossOwnerOf",
- "outputs": [
- {
- "components": [
- { "internalType": "address", "name": "eth", "type": "address" },
- { "internalType": "uint256", "name": "sub", "type": "uint256" }
- ],
- "internalType": "struct CrossAddress",
- "name": "",
- "type": "tuple"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
{ "internalType": "string[]", "name": "keys", "type": "string[]" }
],
"name": "deleteCollectionProperties",
@@ -540,6 +538,25 @@
},
{
"inputs": [
+ { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+ ],
+ "name": "ownerOfCross",
+ "outputs": [
+ {
+ "components": [
+ { "internalType": "address", "name": "eth", "type": "address" },
+ { "internalType": "uint256", "name": "sub", "type": "uint256" }
+ ],
+ "internalType": "struct CrossAddress",
+ "name": "",
+ "type": "tuple"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
{ "internalType": "uint256", "name": "tokenId", "type": "uint256" },
{ "internalType": "string[]", "name": "keys", "type": "string[]" }
],
tests/src/eth/abi/reFungible.jsondiffbeforeafterboth--- a/tests/src/eth/abi/reFungible.json
+++ b/tests/src/eth/abi/reFungible.json
@@ -159,6 +159,23 @@
},
{
"inputs": [
+ {
+ "components": [
+ { "internalType": "address", "name": "eth", "type": "address" },
+ { "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": [
{ "internalType": "uint256", "name": "tokenId", "type": "uint256" }
],
"name": "burn",
@@ -368,25 +385,6 @@
},
{
"inputs": [
- { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
- ],
- "name": "crossOwnerOf",
- "outputs": [
- {
- "components": [
- { "internalType": "address", "name": "eth", "type": "address" },
- { "internalType": "uint256", "name": "sub", "type": "uint256" }
- ],
- "internalType": "struct CrossAddress",
- "name": "",
- "type": "tuple"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
{ "internalType": "string[]", "name": "keys", "type": "string[]" }
],
"name": "deleteCollectionProperties",
@@ -522,6 +520,25 @@
},
{
"inputs": [
+ { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+ ],
+ "name": "ownerOfCross",
+ "outputs": [
+ {
+ "components": [
+ { "internalType": "address", "name": "eth", "type": "address" },
+ { "internalType": "uint256", "name": "sub", "type": "uint256" }
+ ],
+ "internalType": "struct CrossAddress",
+ "name": "",
+ "type": "tuple"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
{ "internalType": "uint256", "name": "tokenId", "type": "uint256" },
{ "internalType": "string[]", "name": "keys", "type": "string[]" }
],
tests/src/eth/abi/reFungibleToken.jsondiffbeforeafterboth--- a/tests/src/eth/abi/reFungibleToken.json
+++ b/tests/src/eth/abi/reFungibleToken.json
@@ -130,6 +130,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/api/UniqueNFT.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueNFT.sol
+++ b/tests/src/eth/api/UniqueNFT.sol
@@ -533,7 +533,7 @@
}
/// @title Unique extensions for ERC721.
-/// @dev the ERC-165 identifier for this interface is 0x16de3152
+/// @dev the ERC-165 identifier for this interface is 0x307b061a
interface ERC721UniqueExtensions is Dummy, ERC165 {
/// @notice A descriptive name for a collection of NFTs in this contract
/// @dev EVM selector for this function is: 0x06fdde03,
@@ -550,12 +550,26 @@
/// or in textual repr: description()
function description() external view returns (string memory);
+ // /// Returns the owner (in cross format) of the token.
+ // ///
+ // /// @param tokenId Id for the token.
+ // /// @dev EVM selector for this function is: 0x2b29dace,
+ // /// or in textual repr: crossOwnerOf(uint256)
+ // function crossOwnerOf(uint256 tokenId) external view returns (CrossAddress memory);
+
/// Returns the owner (in cross format) of the token.
///
/// @param tokenId Id for the token.
- /// @dev EVM selector for this function is: 0x2b29dace,
- /// or in textual repr: crossOwnerOf(uint256)
- function crossOwnerOf(uint256 tokenId) external view returns (CrossAddress memory);
+ /// @dev EVM selector for this function is: 0xcaa3a4d0,
+ /// or in textual repr: ownerOfCross(uint256)
+ function ownerOfCross(uint256 tokenId) external view returns (CrossAddress memory);
+
+ /// @notice Count all NFTs assigned to an owner
+ /// @param owner An cross address for whom to query the balance
+ /// @return The number of NFTs 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);
/// Returns the token properties.
///
tests/src/eth/api/UniqueRefungible.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueRefungible.sol
+++ b/tests/src/eth/api/UniqueRefungible.sol
@@ -533,7 +533,7 @@
}
/// @title Unique extensions for ERC721.
-/// @dev the ERC-165 identifier for this interface is 0xb365c124
+/// @dev the ERC-165 identifier for this interface is 0x95c0f66c
interface ERC721UniqueExtensions is Dummy, ERC165 {
/// @notice A descriptive name for a collection of NFTs in this contract
/// @dev EVM selector for this function is: 0x06fdde03,
@@ -550,12 +550,26 @@
/// or in textual repr: description()
function description() external view returns (string memory);
+ // /// Returns the owner (in cross format) of the token.
+ // ///
+ // /// @param tokenId Id for the token.
+ // /// @dev EVM selector for this function is: 0x2b29dace,
+ // /// or in textual repr: crossOwnerOf(uint256)
+ // function crossOwnerOf(uint256 tokenId) external view returns (CrossAddress memory);
+
/// Returns the owner (in cross format) of the token.
///
/// @param tokenId Id for the token.
- /// @dev EVM selector for this function is: 0x2b29dace,
- /// or in textual repr: crossOwnerOf(uint256)
- function crossOwnerOf(uint256 tokenId) external view returns (CrossAddress memory);
+ /// @dev EVM selector for this function is: 0xcaa3a4d0,
+ /// or in textual repr: ownerOfCross(uint256)
+ function ownerOfCross(uint256 tokenId) external view returns (CrossAddress memory);
+
+ /// @notice Count all RFTs assigned to an owner
+ /// @param owner An cross address for whom to query the balance
+ /// @return The number of RFTs 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);
/// Returns the token properties.
///
tests/src/eth/api/UniqueRefungibleToken.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueRefungibleToken.sol
+++ b/tests/src/eth/api/UniqueRefungibleToken.sol
@@ -23,7 +23,7 @@
function parentTokenId() external view returns (uint256);
}
-/// @dev the ERC-165 identifier for this interface is 0x01d536fc
+/// @dev the ERC-165 identifier for this interface is 0xedd3a564
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.
@@ -60,6 +60,13 @@
/// or in textual repr: approveCross((address,uint256),uint256)
function approveCross(CrossAddress memory spender, uint256 amount) external returns (bool);
+ /// @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);
+
/// @dev Function that changes total amount of the tokens.
/// Throws if `msg.sender` doesn't owns all of the tokens.
/// @param amount New total amount of the tokens.
tests/src/eth/fungible.test.tsdiffbeforeafterboth--- a/tests/src/eth/fungible.test.ts
+++ b/tests/src/eth/fungible.test.ts
@@ -427,6 +427,33 @@
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 other = await helper.ethCrossAccount.createAccountWithBalance(donor);
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner.eth);
+
+ expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('0');
+ expect(await collectionEvm.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('0');
+
+ await collection.mint(alice, 100n, {Ethereum: owner.eth});
+ expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('100');
+ expect(await collectionEvm.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('0');
+
+ await collectionEvm.methods.transferCross(other, 50n).send({from: owner.eth});
+ expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('50');
+ expect(await collectionEvm.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('50');
+
+ await collectionEvm.methods.transferCross(other, 50n).send({from: owner.eth});
+ expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('0');
+ expect(await collectionEvm.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('100');
+
+ await collectionEvm.methods.transferCross(owner, 100n).send({from: other.eth});
+ expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('100');
+ expect(await collectionEvm.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('0');
+ });
});
describe('Fungible: Fees', () => {
tests/src/eth/nonFungible.test.tsdiffbeforeafterboth--- a/tests/src/eth/nonFungible.test.ts
+++ b/tests/src/eth/nonFungible.test.ts
@@ -662,6 +662,38 @@
// Cannot transfer token if it does not exist:
await expect(collectionEvm.methods[testCase](receiver, 999999).send({from: sender})).to.be.rejected;
}));
+
+ itEth('Check balanceOfCross()', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(minter, {});
+ const owner = await helper.ethCrossAccount.createAccountWithBalance(donor);
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner.eth);
+
+ expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('0');
+
+ for (let i = 1; i < 10; i++) {
+ await collection.mintToken(minter, {Ethereum: owner.eth});
+ expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq(i.toString());
+ }
+ });
+
+ itEth('Check ownerOfCross()', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(minter, {});
+ let owner = await helper.ethCrossAccount.createAccountWithBalance(donor);
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner.eth);
+ const {tokenId} = await collection.mintToken(minter, {Ethereum: owner.eth});
+
+ for (let i = 1n; i < 10n; i++) {
+ const ownerCross = await collectionEvm.methods.ownerOfCross(tokenId).call({from: owner.eth});
+ expect(ownerCross.eth).to.be.eq(owner.eth);
+ expect(ownerCross.sub).to.be.eq(owner.sub);
+
+ const newOwner = await helper.ethCrossAccount.createAccountWithBalance(donor);
+ await collectionEvm.methods.transferCross(newOwner, tokenId).send({from: owner.eth});
+ owner = newOwner;
+ }
+ });
});
describe('NFT: Fees', () => {
tests/src/eth/reFungible.test.tsdiffbeforeafterboth--- a/tests/src/eth/reFungible.test.ts
+++ b/tests/src/eth/reFungible.test.ts
@@ -584,6 +584,46 @@
expect(event.returnValues.to).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');
expect(event.returnValues.tokenId).to.equal(tokenId.toString());
});
+
+ 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);
+ const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner.eth);
+
+ expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('0');
+
+ for (let i = 1n; i < 10n; i++) {
+ await collection.mintToken(minter, 100n, {Ethereum: owner.eth});
+ expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq(i.toString());
+ }
+ });
+
+ itEth('Check ownerOfCross()', async ({helper}) => {
+ const collection = await helper.rft.mintCollection(minter, {});
+ let owner = await helper.ethCrossAccount.createAccountWithBalance(donor);
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner.eth);
+ const {tokenId} = await collection.mintToken(minter, 100n,{Ethereum: owner.eth});
+
+ for (let i = 1n; i < 10n; i++) {
+ const ownerCross = await collectionEvm.methods.ownerOfCross(tokenId).call({from: owner.eth});
+ expect(ownerCross.eth).to.be.eq(owner.eth);
+ expect(ownerCross.sub).to.be.eq(owner.sub);
+
+ const newOwner = await helper.ethCrossAccount.createAccountWithBalance(donor);
+ await collectionEvm.methods.transferCross(newOwner, tokenId).send({from: owner.eth});
+ owner = newOwner;
+ }
+
+ const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
+ const tokenContract = await helper.ethNativeContract.rftToken(tokenAddress, owner.eth, true);
+ const newOwner = await helper.ethCrossAccount.createAccountWithBalance(donor);
+ await tokenContract.methods.transferCross(newOwner, 50).send({from: owner.eth});
+ const ownerCross = await collectionEvm.methods.ownerOfCross(tokenId).call({from: owner.eth});
+ expect(ownerCross.eth.toUpperCase()).to.be.eq('0XFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF');
+ expect(ownerCross.sub).to.be.eq('0');
+ });
});
describe('RFT: Fees', () => {
tests/src/eth/reFungibleToken.test.tsdiffbeforeafterboth--- a/tests/src/eth/reFungibleToken.test.ts
+++ b/tests/src/eth/reFungibleToken.test.ts
@@ -466,6 +466,35 @@
expect(await collection.getTokenBalance(tokenId, {Substrate: ownerSub.address})).to.be.equal(150n);
}
});
+
+ itEth('Check balanceOfCross()', async ({helper}) => {
+ const collection = await helper.rft.mintCollection(alice, {});
+ const owner = await helper.ethCrossAccount.createAccountWithBalance(donor);
+ const other = await helper.ethCrossAccount.createAccountWithBalance(donor);
+ const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner.eth});
+ const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
+ const tokenContract = await helper.ethNativeContract.rftToken(tokenAddress, owner.eth);
+
+ expect(await tokenContract.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('200');
+ expect(await tokenContract.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('0');
+
+ await tokenContract.methods.repartition(100n).send({from: owner.eth});
+ expect(await tokenContract.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('100');
+ expect(await tokenContract.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('0');
+
+ await tokenContract.methods.transferCross(other, 50n).send({from: owner.eth});
+ expect(await tokenContract.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('50');
+ expect(await tokenContract.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('50');
+
+ await tokenContract.methods.transferCross(other, 50n).send({from: owner.eth});
+ expect(await tokenContract.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('0');
+ expect(await tokenContract.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('100');
+
+ await tokenContract.methods.repartition(1000n).send({from: other.eth});
+ await tokenContract.methods.transferCross(owner, 500n).send({from: other.eth});
+ expect(await tokenContract.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('500');
+ expect(await tokenContract.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('500');
+ });
});
describe('Refungible: Fees', () => {
tests/src/eth/tokens/minting.test.tsdiffbeforeafterboth--- a/tests/src/eth/tokens/minting.test.ts
+++ b/tests/src/eth/tokens/minting.test.ts
@@ -63,7 +63,7 @@
const tokenId = event.returnValues.tokenId;
expect(tokenId).to.be.equal('1');
expect(await helper.collection.getLastTokenId(collection.collectionId)).to.eq(1);
- expect(await contract.methods.crossOwnerOf(tokenId).call()).to.be.like([receiver, '0']);
+ expect(await contract.methods.ownerOfCross(tokenId).call()).to.be.like([receiver, '0']);
}
});
});
@@ -97,7 +97,7 @@
const tokenId = event.returnValues.tokenId;
expect(tokenId).to.be.equal('1');
expect(await helper.collection.getLastTokenId(collectionId)).to.eq(1);
- expect(await collection.methods.crossOwnerOf(tokenId).call()).to.be.like([receiver, '0']);
+ expect(await collection.methods.ownerOfCross(tokenId).call()).to.be.like([receiver, '0']);
}
});
});
@@ -131,7 +131,7 @@
const tokenId = event.returnValues.tokenId;
expect(tokenId).to.be.equal('1');
expect(await helper.collection.getLastTokenId(collectionId)).to.eq(1);
- expect(await collection.methods.crossOwnerOf(tokenId).call()).to.be.like([receiver, '0']);
+ expect(await collection.methods.ownerOfCross(tokenId).call()).to.be.like([receiver, '0']);
}
});
});
@@ -157,7 +157,7 @@
expect(event.returnValues.to).to.be.equal(receiver);
expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');
- expect(await contract.methods.crossOwnerOf(tokenId).call()).to.be.like([receiver, '0']);
+ expect(await contract.methods.ownerOfCross(tokenId).call()).to.be.like([receiver, '0']);
// TODO: this wont work right now, need release 919000 first
// await helper.methods.setOffchainSchema(collectionIdAddress, 'https://offchain-service.local/token-info/{id}').send();
// const tokenUri = await contract.methods.tokenURI(nextTokenId).call();