difftreelog
refac: rename substrate methods to cross
in: master
27 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1077,13 +1077,13 @@
[[package]]
name = "console"
-version = "0.15.1"
+version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89eab4d20ce20cea182308bca13088fecea9c05f6776cf287205d41a0ed3c847"
+checksum = "c050367d967ced717c04b65d8c619d863ef9292ce0c5760028655a2fb298718c"
dependencies = [
"encode_unicode",
+ "lazy_static",
"libc",
- "once_cell",
"terminal_size",
"winapi",
]
@@ -1871,9 +1871,9 @@
[[package]]
name = "cxx"
-version = "1.0.79"
+version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f83d0ebf42c6eafb8d7c52f7e5f2d3003b89c7aa4fd2b79229209459a849af8"
+checksum = "19f39818dcfc97d45b03953c1292efc4e80954e1583c4aa770bac1383e2310a4"
dependencies = [
"cc",
"cxxbridge-flags",
@@ -1883,9 +1883,9 @@
[[package]]
name = "cxx-build"
-version = "1.0.79"
+version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07d050484b55975889284352b0ffc2ecbda25c0c55978017c132b29ba0818a86"
+checksum = "3e580d70777c116df50c390d1211993f62d40302881e54d4b79727acb83d0199"
dependencies = [
"cc",
"codespan-reporting",
@@ -1898,15 +1898,15 @@
[[package]]
name = "cxxbridge-flags"
-version = "1.0.79"
+version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99d2199b00553eda8012dfec8d3b1c75fce747cf27c169a270b3b99e3448ab78"
+checksum = "56a46460b88d1cec95112c8c363f0e2c39afdb237f60583b0b36343bf627ea9c"
[[package]]
name = "cxxbridge-macro"
-version = "1.0.79"
+version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcb67a6de1f602736dd7eaead0080cf3435df806c61b24b13328db128c58868f"
+checksum = "747b608fecf06b0d72d440f27acc99288207324b793be2c17991839f3d4995ea"
dependencies = [
"proc-macro2",
"quote",
@@ -3561,9 +3561,9 @@
[[package]]
name = "iana-time-zone-haiku"
-version = "0.1.1"
+version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
+checksum = "fde6edd6cef363e9359ed3c98ba64590ba9eecba2293eb5a723ab32aee8926aa"
dependencies = [
"cxx",
"cxx-build",
@@ -10774,9 +10774,9 @@
[[package]]
name = "serde_json"
-version = "1.0.87"
+version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45"
+checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074"
dependencies = [
"itoa",
"ryu",
@@ -12055,9 +12055,9 @@
[[package]]
name = "syn"
-version = "1.0.103"
+version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d"
+checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1"
dependencies = [
"proc-macro2",
"quote",
pallets/common/src/erc.rsdiffbeforeafterboth--- a/pallets/common/src/erc.rs
+++ b/pallets/common/src/erc.rs
@@ -34,8 +34,8 @@
use crate::{
Pallet, CollectionHandle, Config, CollectionProperties, SelfWeightOf,
eth::{
- convert_cross_account_to_uint256, convert_uint256_to_cross_account,
- convert_cross_account_to_tuple,
+ convert_cross_account_to_uint256, convert_cross_account_to_tuple,
+ convert_tuple_to_cross_account,
},
weights::WeightInfo,
};
@@ -139,26 +139,25 @@
save(self)
}
- // TODO: Temprorary off. Need refactor
- // /// Set the substrate sponsor of the collection.
- // ///
- // /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
- // ///
- // /// @param sponsor Substrate address of the sponsor from whose account funds will be debited for operations with the contract.
- // fn set_collection_sponsor_substrate(
- // &mut self,
- // caller: caller,
- // sponsor: uint256,
- // ) -> Result<void> {
- // self.consume_store_reads_and_writes(1, 1)?;
+ /// Set the sponsor of the collection.
+ ///
+ /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
+ ///
+ /// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract.
+ fn set_collection_sponsor_cross(
+ &mut self,
+ caller: caller,
+ sponsor: (address, uint256),
+ ) -> Result<void> {
+ self.consume_store_reads_and_writes(1, 1)?;
- // check_is_owner_or_admin(caller, self)?;
+ check_is_owner_or_admin(caller, self)?;
- // let sponsor = convert_uint256_to_cross_account::<T>(sponsor);
- // self.set_sponsor(sponsor.as_sub().clone())
- // .map_err(dispatch_to_evm::<T>)?;
- // save(self)
- // }
+ let sponsor = convert_tuple_to_cross_account::<T>(sponsor)?;
+ self.set_sponsor(sponsor.as_sub().clone())
+ .map_err(dispatch_to_evm::<T>)?;
+ save(self)
+ }
/// Whether there is a pending sponsor.
fn has_collection_pending_sponsor(&self) -> Result<bool> {
@@ -300,37 +299,35 @@
Ok(crate::eth::collection_id_to_address(self.id))
}
- // TODO: Temprorary off. Need refactor
- // /// Add collection admin by substrate address.
- // /// @param newAdmin Substrate administrator address.
- // fn add_collection_admin_substrate(
- // &mut self,
- // caller: caller,
- // new_admin: uint256,
- // ) -> Result<void> {
- // self.consume_store_writes(2)?;
+ /// Add collection admin.
+ /// @param newAdmin Cross account administrator address.
+ fn add_collection_admin_cross(
+ &mut self,
+ caller: caller,
+ new_admin: (address, uint256),
+ ) -> Result<void> {
+ self.consume_store_writes(2)?;
- // let caller = T::CrossAccountId::from_eth(caller);
- // let new_admin = convert_uint256_to_cross_account::<T>(new_admin);
- // <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;
- // Ok(())
- // }
+ let caller = T::CrossAccountId::from_eth(caller);
+ let new_admin = convert_tuple_to_cross_account::<T>(new_admin)?;
+ <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;
+ Ok(())
+ }
- // TODO: Temprorary off. Need refactor
- // /// Remove collection admin by substrate address.
- // /// @param admin Substrate administrator address.
- // fn remove_collection_admin_substrate(
- // &mut self,
- // caller: caller,
- // admin: uint256,
- // ) -> Result<void> {
- // self.consume_store_writes(2)?;
+ /// Remove collection admin.
+ /// @param admin Cross account administrator address.
+ fn remove_collection_admin_cross(
+ &mut self,
+ caller: caller,
+ admin: (address, uint256),
+ ) -> Result<void> {
+ self.consume_store_writes(2)?;
- // let caller = T::CrossAccountId::from_eth(caller);
- // let admin = convert_uint256_to_cross_account::<T>(admin);
- // <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;
- // Ok(())
- // }
+ let caller = T::CrossAccountId::from_eth(caller);
+ let admin = convert_tuple_to_cross_account::<T>(admin)?;
+ <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;
+ Ok(())
+ }
/// Add collection admin.
/// @param newAdmin Address of the added administrator.
@@ -479,22 +476,21 @@
Ok(())
}
- // TODO: Temprorary off. Need refactor
- // /// Add substrate user to allowed list.
- // ///
- // /// @param user User substrate address.
- // fn add_to_collection_allow_list_substrate(
- // &mut self,
- // caller: caller,
- // user: uint256,
- // ) -> Result<void> {
- // self.consume_store_writes(1)?;
+ /// Add user to allowed list.
+ ///
+ /// @param user User cross account address.
+ fn add_to_collection_allow_list_cross(
+ &mut self,
+ caller: caller,
+ user: (address, uint256),
+ ) -> Result<void> {
+ self.consume_store_writes(1)?;
- // let caller = T::CrossAccountId::from_eth(caller);
- // let user = convert_uint256_to_cross_account::<T>(user);
- // Pallet::<T>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;
- // Ok(())
- // }
+ let caller = T::CrossAccountId::from_eth(caller);
+ let user = convert_tuple_to_cross_account::<T>(user)?;
+ Pallet::<T>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;
+ Ok(())
+ }
/// Remove the user from the allowed list.
///
@@ -508,22 +504,21 @@
Ok(())
}
- // TODO: Temprorary off. Need refactor
- // /// Remove substrate user from allowed list.
- // ///
- // /// @param user User substrate address.
- // fn remove_from_collection_allow_list_substrate(
- // &mut self,
- // caller: caller,
- // user: uint256,
- // ) -> Result<void> {
- // self.consume_store_writes(1)?;
+ /// Remove user from allowed list.
+ ///
+ /// @param user User cross account address.
+ fn remove_from_collection_allow_list_cross(
+ &mut self,
+ caller: caller,
+ user: (address, uint256),
+ ) -> Result<void> {
+ self.consume_store_writes(1)?;
- // let caller = T::CrossAccountId::from_eth(caller);
- // let user = convert_uint256_to_cross_account::<T>(user);
- // Pallet::<T>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;
- // Ok(())
- // }
+ let caller = T::CrossAccountId::from_eth(caller);
+ let user = convert_tuple_to_cross_account::<T>(user)?;
+ Pallet::<T>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;
+ Ok(())
+ }
/// Switch permission for minting.
///
@@ -556,15 +551,14 @@
Ok(self.is_owner_or_admin(&user))
}
- // TODO: Temprorary off. Need refactor
- // /// Check that substrate account is the owner or admin of the collection
- // ///
- // /// @param user account to verify
- // /// @return "true" if account is the owner or admin
- // fn is_owner_or_admin_substrate(&self, user: uint256) -> Result<bool> {
- // let user = convert_uint256_to_cross_account::<T>(user);
- // Ok(self.is_owner_or_admin(&user))
- // }
+ /// Check that account is the owner or admin of the collection
+ ///
+ /// @param user User cross account to verify
+ /// @return "true" if account is the owner or admin
+ fn is_owner_or_admin_cross(&self, user: (address, uint256)) -> Result<bool> {
+ let user = convert_tuple_to_cross_account::<T>(user)?;
+ Ok(self.is_owner_or_admin(&user))
+ }
/// Returns collection type
///
@@ -580,7 +574,7 @@
/// Get collection owner.
///
- /// @return Tuple with sponsor address and his substrate mirror.
+ /// @return Tuble with sponsor address and his substrate mirror.
/// If address is canonical then substrate mirror is zero and vice versa.
fn collection_owner(&self) -> Result<(address, uint256)> {
Ok(convert_cross_account_to_tuple::<T>(
@@ -602,20 +596,6 @@
.map_err(dispatch_to_evm::<T>)
}
- // TODO: Temprorary off. Need refactor
- // /// Changes collection owner to another substrate account
- // ///
- // /// @dev Owner can be changed only by current owner
- // /// @param newOwner new owner substrate account
- // fn set_owner_substrate(&mut self, caller: caller, new_owner: uint256) -> Result<void> {
- // self.consume_store_writes(1)?;
-
- // let caller = T::CrossAccountId::from_eth(caller);
- // let new_owner = convert_uint256_to_cross_account::<T>(new_owner);
- // self.set_owner_internal(caller, new_owner)
- // .map_err(dispatch_to_evm::<T>)
- // }
-
/// Get collection administrators
///
/// @return Vector of tuples with admins address and his substrate mirror.
@@ -626,6 +606,19 @@
.collect();
Ok(result)
}
+
+ /// Changes collection owner to another account
+ ///
+ /// @dev Owner can be changed only by current owner
+ /// @param newOwner new owner cross account
+ fn set_owner_cross(&mut self, caller: caller, new_owner: (address, uint256)) -> Result<void> {
+ self.consume_store_writes(1)?;
+
+ let caller = T::CrossAccountId::from_eth(caller);
+ let new_owner = convert_tuple_to_cross_account::<T>(new_owner)?;
+ self.set_owner_internal(caller, new_owner)
+ .map_err(dispatch_to_evm::<T>)
+ }
}
/// ### Note
pallets/common/src/eth.rsdiffbeforeafterboth--- a/pallets/common/src/eth.rs
+++ b/pallets/common/src/eth.rs
@@ -85,3 +85,27 @@
(eth, Default::default())
}
}
+
+/// Convert tuple `(address, uint256)` to `CrossAccountId`.
+///
+/// If `address` in the tuple has *default* value, then the canonical form is substrate,
+/// if `uint256` has *default* value, then the ethereum form is canonical,
+/// if both values are *default* or *non default*, then this is considered an invalid address and `Error` is returned.
+pub fn convert_tuple_to_cross_account<T: Config>(
+ eth_cross_account_id: (address, uint256),
+) -> evm_coder::execution::Result<T::CrossAccountId>
+where
+ T::AccountId: From<[u8; 32]>,
+{
+ if eth_cross_account_id == Default::default() {
+ Err("All fields of cross account is zeroed".into())
+ } else if eth_cross_account_id.0 == Default::default() {
+ Ok(convert_uint256_to_cross_account::<T>(
+ eth_cross_account_id.1,
+ ))
+ } else if eth_cross_account_id.1 == Default::default() {
+ Ok(T::CrossAccountId::from_eth(eth_cross_account_id.0))
+ } else {
+ Err("All fields of cross account is non zeroed".into())
+ }
+}
pallets/fungible/src/erc.rsdiffbeforeafterboth--- a/pallets/fungible/src/erc.rs
+++ b/pallets/fungible/src/erc.rs
@@ -19,6 +19,7 @@
use core::char::{REPLACEMENT_CHARACTER, decode_utf16};
use core::convert::TryInto;
use evm_coder::{ToLog, execution::*, generate_stubgen, solidity_interface, types::*, weight};
+use pallet_common::eth::convert_tuple_to_cross_account;
use up_data_structs::CollectionMode;
use pallet_common::erc::{CommonEvmHandler, PrecompileResult};
use sp_std::vec::Vec;
@@ -149,7 +150,26 @@
}
#[solidity_interface(name = ERC20UniqueExtensions)]
-impl<T: Config> FungibleHandle<T> {
+impl<T: Config> FungibleHandle<T>
+where
+ T::AccountId: From<[u8; 32]>,
+{
+ #[weight(<SelfWeightOf<T>>::approve())]
+ fn approve_cross(
+ &mut self,
+ caller: caller,
+ spender: (address, uint256),
+ amount: uint256,
+ ) -> Result<bool> {
+ let caller = T::CrossAccountId::from_eth(caller);
+ let spender = convert_tuple_to_cross_account::<T>(spender)?;
+ let amount = amount.try_into().map_err(|_| "amount overflow")?;
+
+ <Pallet<T>>::set_allowance(self, &caller, &spender, amount)
+ .map_err(dispatch_to_evm::<T>)?;
+ Ok(true)
+ }
+
/// Burn tokens from account
/// @dev Function that burns an `amount` of the tokens of a given account,
/// deducting from the sender's allowance for said account.
@@ -169,6 +189,30 @@
Ok(true)
}
+ /// Burn tokens from account
+ /// @dev Function that burns an `amount` of the tokens of a given account,
+ /// deducting from the sender's allowance for said account.
+ /// @param from The account whose tokens will be burnt.
+ /// @param amount The amount that will be burnt.
+ #[weight(<SelfWeightOf<T>>::burn_from())]
+ fn burn_from_cross(
+ &mut self,
+ caller: caller,
+ from: (address, uint256),
+ amount: uint256,
+ ) -> Result<bool> {
+ let caller = T::CrossAccountId::from_eth(caller);
+ let from = convert_tuple_to_cross_account::<T>(from)?;
+ let amount = amount.try_into().map_err(|_| "amount overflow")?;
+ let budget = self
+ .recorder
+ .weight_calls_budget(<StructureWeight<T>>::find_parent());
+
+ <Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)
+ .map_err(dispatch_to_evm::<T>)?;
+ Ok(true)
+ }
+
/// Mint tokens for multiple accounts.
/// @param amounts array of pairs of account address and amount
#[weight(<SelfWeightOf<T>>::create_multiple_items_ex(amounts.len() as u32))]
@@ -191,6 +235,27 @@
.map_err(dispatch_to_evm::<T>)?;
Ok(true)
}
+
+ #[weight(<SelfWeightOf<T>>::transfer_from())]
+ fn transfer_from_cross(
+ &mut self,
+ caller: caller,
+ from: (address, uint256),
+ to: (address, uint256),
+ amount: uint256,
+ ) -> Result<bool> {
+ let caller = T::CrossAccountId::from_eth(caller);
+ let from = convert_tuple_to_cross_account::<T>(from)?;
+ let to = convert_tuple_to_cross_account::<T>(to)?;
+ let amount = amount.try_into().map_err(|_| "amount overflow")?;
+ let budget = self
+ .recorder
+ .weight_calls_budget(<StructureWeight<T>>::find_parent());
+
+ <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)
+ .map_err(dispatch_to_evm::<T>)?;
+ Ok(true)
+ }
}
#[solidity_interface(
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
@@ -18,7 +18,7 @@
}
/// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0x3af103fb
+/// @dev the ERC-165 identifier for this interface is 0x25d897dc
contract Collection is Dummy, ERC165 {
/// Set collection property.
///
@@ -72,6 +72,19 @@
dummy = 0;
}
+ /// Set the sponsor of the collection.
+ ///
+ /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
+ ///
+ /// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract.
+ /// @dev EVM selector for this function is: 0x84a1d5a8,
+ /// or in textual repr: setCollectionSponsorCross((address,uint256))
+ function setCollectionSponsorCross(Tuple6 memory sponsor) public {
+ require(false, stub_error);
+ sponsor;
+ dummy = 0;
+ }
+
/// Whether there is a pending sponsor.
/// @dev EVM selector for this function is: 0x058ac185,
/// or in textual repr: hasCollectionPendingSponsor()
@@ -155,6 +168,26 @@
}
/// Add collection admin.
+ /// @param newAdmin Cross account administrator address.
+ /// @dev EVM selector for this function is: 0x859aa7d6,
+ /// or in textual repr: addCollectionAdminCross((address,uint256))
+ function addCollectionAdminCross(Tuple6 memory newAdmin) public {
+ require(false, stub_error);
+ newAdmin;
+ dummy = 0;
+ }
+
+ /// Remove collection admin.
+ /// @param admin Cross account administrator address.
+ /// @dev EVM selector for this function is: 0x6c0cd173,
+ /// or in textual repr: removeCollectionAdminCross((address,uint256))
+ function removeCollectionAdminCross(Tuple6 memory admin) public {
+ require(false, stub_error);
+ admin;
+ dummy = 0;
+ }
+
+ /// Add collection admin.
/// @param newAdmin Address of the added administrator.
/// @dev EVM selector for this function is: 0x92e462c7,
/// or in textual repr: addCollectionAdmin(address)
@@ -234,6 +267,17 @@
dummy = 0;
}
+ /// Add user to allowed list.
+ ///
+ /// @param user User cross account address.
+ /// @dev EVM selector for this function is: 0xa0184a3a,
+ /// or in textual repr: addToCollectionAllowListCross((address,uint256))
+ function addToCollectionAllowListCross(Tuple6 memory user) public {
+ require(false, stub_error);
+ user;
+ dummy = 0;
+ }
+
/// Remove the user from the allowed list.
///
/// @param user Address of a removed user.
@@ -245,6 +289,17 @@
dummy = 0;
}
+ /// Remove user from allowed list.
+ ///
+ /// @param user User cross account address.
+ /// @dev EVM selector for this function is: 0x09ba452a,
+ /// or in textual repr: removeFromCollectionAllowListCross((address,uint256))
+ function removeFromCollectionAllowListCross(Tuple6 memory user) public {
+ require(false, stub_error);
+ user;
+ dummy = 0;
+ }
+
/// Switch permission for minting.
///
/// @param mode Enable if "true".
@@ -269,6 +324,19 @@
return false;
}
+ /// Check that account is the owner or admin of the collection
+ ///
+ /// @param user User cross account to verify
+ /// @return "true" if account is the owner or admin
+ /// @dev EVM selector for this function is: 0x3e75a905,
+ /// or in textual repr: isOwnerOrAdminCross((address,uint256))
+ function isOwnerOrAdminCross(Tuple6 memory user) public view returns (bool) {
+ require(false, stub_error);
+ user;
+ dummy;
+ return false;
+ }
+
/// Returns collection type
///
/// @return `Fungible` or `NFT` or `ReFungible`
@@ -282,7 +350,7 @@
/// Get collection owner.
///
- /// @return Tuple with sponsor address and his substrate mirror.
+ /// @return Tuble with sponsor address and his substrate mirror.
/// If address is canonical then substrate mirror is zero and vice versa.
/// @dev EVM selector for this function is: 0xdf727d3b,
/// or in textual repr: collectionOwner()
@@ -315,10 +383,32 @@
dummy;
return new Tuple6[](0);
}
+
+ /// Changes collection owner to another account
+ ///
+ /// @dev Owner can be changed only by current owner
+ /// @param newOwner new owner cross account
+ /// @dev EVM selector for this function is: 0xe5c9913f,
+ /// or in textual repr: setOwnerCross((address,uint256))
+ function setOwnerCross(Tuple6 memory newOwner) public {
+ require(false, stub_error);
+ newOwner;
+ dummy = 0;
+ }
}
-/// @dev the ERC-165 identifier for this interface is 0x63034ac5
+/// @dev the ERC-165 identifier for this interface is 0x032e5926
contract ERC20UniqueExtensions is Dummy, ERC165 {
+ /// @dev EVM selector for this function is: 0x0ecd0ab0,
+ /// or in textual repr: approveCross((address,uint256),uint256)
+ function approveCross(Tuple6 memory spender, uint256 amount) public returns (bool) {
+ require(false, stub_error);
+ spender;
+ amount;
+ dummy = 0;
+ return false;
+ }
+
/// Burn tokens from account
/// @dev Function that burns an `amount` of the tokens of a given account,
/// deducting from the sender's allowance for said account.
@@ -334,6 +424,21 @@
return false;
}
+ /// Burn tokens from account
+ /// @dev Function that burns an `amount` of the tokens of a given account,
+ /// deducting from the sender's allowance for said account.
+ /// @param from The account whose tokens will be burnt.
+ /// @param amount The amount that will be burnt.
+ /// @dev EVM selector for this function is: 0xbb2f5a58,
+ /// or in textual repr: burnFromCross((address,uint256),uint256)
+ function burnFromCross(Tuple6 memory from, uint256 amount) public returns (bool) {
+ require(false, stub_error);
+ from;
+ amount;
+ dummy = 0;
+ return false;
+ }
+
/// Mint tokens for multiple accounts.
/// @param amounts array of pairs of account address and amount
/// @dev EVM selector for this function is: 0x1acf2d55,
@@ -344,6 +449,21 @@
dummy = 0;
return false;
}
+
+ /// @dev EVM selector for this function is: 0xd5cf430b,
+ /// or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256)
+ function transferFromCross(
+ Tuple6 memory from,
+ Tuple6 memory to,
+ uint256 amount
+ ) public returns (bool) {
+ require(false, stub_error);
+ from;
+ to;
+ amount;
+ dummy = 0;
+ return false;
+ }
}
/// @dev anonymous struct
pallets/nonfungible/src/erc.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -35,6 +35,7 @@
use pallet_common::{
erc::{CommonEvmHandler, PrecompileResult, CollectionCall, static_property::key},
CollectionHandle, CollectionPropertyPermissions,
+ eth::convert_tuple_to_cross_account,
};
use pallet_evm::{account::CrossAccountId, PrecompileHandle};
use pallet_evm_coder_substrate::call;
@@ -198,7 +199,10 @@
/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
/// @dev See https://eips.ethereum.org/EIPS/eip-721
#[solidity_interface(name = ERC721Metadata, expect_selector = 0x5b5e139f)]
-impl<T: Config> NonfungibleHandle<T> {
+impl<T: Config> NonfungibleHandle<T>
+where
+ T::AccountId: From<[u8; 32]>,
+{
/// @notice A descriptive name for a collection of NFTs in this contract
/// @dev real implementation of this function lies in `ERC721UniqueExtensions`
#[solidity(hide, rename_selector = "name")]
@@ -603,7 +607,10 @@
/// @title Unique extensions for ERC721.
#[solidity_interface(name = ERC721UniqueExtensions)]
-impl<T: Config> NonfungibleHandle<T> {
+impl<T: Config> NonfungibleHandle<T>
+where
+ T::AccountId: From<[u8; 32]>,
+{
/// @notice A descriptive name for a collection of NFTs in this contract
fn name(&self) -> Result<string> {
Ok(decode_utf16(self.name.iter().copied())
@@ -616,6 +623,28 @@
Ok(string::from_utf8_lossy(&self.token_prefix).into())
}
+ /// @notice Set or reaffirm the approved address for an NFT
+ /// @dev The zero address indicates there is no approved address.
+ /// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized
+ /// operator of the current owner.
+ /// @param approved The new substrate address approved NFT controller
+ /// @param tokenId The NFT to approve
+ #[weight(<SelfWeightOf<T>>::approve())]
+ fn approve_cross(
+ &mut self,
+ caller: caller,
+ approved: (address, uint256),
+ token_id: uint256,
+ ) -> Result<void> {
+ let caller = T::CrossAccountId::from_eth(caller);
+ let approved = convert_tuple_to_cross_account::<T>(approved)?;
+ let token = token_id.try_into()?;
+
+ <Pallet<T>>::set_allowance(self, &caller, token, Some(&approved))
+ .map_err(dispatch_to_evm::<T>)?;
+ Ok(())
+ }
+
/// @notice Transfer ownership of an NFT
/// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
/// is the zero address. Throws if `tokenId` is not a valid NFT.
@@ -634,6 +663,32 @@
Ok(())
}
+ /// @notice Transfer ownership of an NFT from cross account address to cross account address
+ /// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
+ /// is the zero address. Throws if `tokenId` is not a valid NFT.
+ /// @param from Cross acccount address of current owner
+ /// @param to Cross acccount address of new owner
+ /// @param tokenId The NFT to transfer
+ #[weight(<SelfWeightOf<T>>::transfer())]
+ fn transfer_from_cross(
+ &mut self,
+ caller: caller,
+ from: (address, uint256),
+ to: (address, uint256),
+ token_id: uint256,
+ ) -> Result<void> {
+ let caller = T::CrossAccountId::from_eth(caller);
+ let from = convert_tuple_to_cross_account::<T>(from)?;
+ let to = convert_tuple_to_cross_account::<T>(to)?;
+ let token_id = token_id.try_into()?;
+ let budget = self
+ .recorder
+ .weight_calls_budget(<StructureWeight<T>>::find_parent());
+ Pallet::<T>::transfer_from(self, &caller, &from, &to, token_id, &budget)
+ .map_err(dispatch_to_evm::<T>)?;
+ Ok(())
+ }
+
/// @notice Burns a specific ERC721 token.
/// @dev Throws unless `msg.sender` is the current owner or an authorized
/// operator for this NFT. Throws if `from` is not the current owner. Throws
@@ -654,6 +709,31 @@
Ok(())
}
+ /// @notice Burns a specific ERC721 token.
+ /// @dev Throws unless `msg.sender` is the current owner or an authorized
+ /// operator for this NFT. Throws if `from` is not the current owner. Throws
+ /// if `to` is the zero address. Throws if `tokenId` is not a valid NFT.
+ /// @param from The current owner of the NFT
+ /// @param tokenId The NFT to transfer
+ #[weight(<SelfWeightOf<T>>::burn_from())]
+ fn burn_from_cross(
+ &mut self,
+ caller: caller,
+ from: (address, uint256),
+ token_id: uint256,
+ ) -> Result<void> {
+ let caller = T::CrossAccountId::from_eth(caller);
+ let from = convert_tuple_to_cross_account::<T>(from)?;
+ let token = token_id.try_into()?;
+ let budget = self
+ .recorder
+ .weight_calls_budget(<StructureWeight<T>>::find_parent());
+
+ <Pallet<T>>::burn_from(self, &caller, &from, token, &budget)
+ .map_err(dispatch_to_evm::<T>)?;
+ Ok(())
+ }
+
/// @notice Returns next free NFT ID.
fn next_token_id(&self) -> Result<uint256> {
self.consume_store_reads(1)?;
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
@@ -91,7 +91,7 @@
}
/// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0x3af103fb
+/// @dev the ERC-165 identifier for this interface is 0x25d897dc
contract Collection is Dummy, ERC165 {
/// Set collection property.
///
@@ -145,6 +145,19 @@
dummy = 0;
}
+ /// Set the sponsor of the collection.
+ ///
+ /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
+ ///
+ /// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract.
+ /// @dev EVM selector for this function is: 0x84a1d5a8,
+ /// or in textual repr: setCollectionSponsorCross((address,uint256))
+ function setCollectionSponsorCross(Tuple6 memory sponsor) public {
+ require(false, stub_error);
+ sponsor;
+ dummy = 0;
+ }
+
/// Whether there is a pending sponsor.
/// @dev EVM selector for this function is: 0x058ac185,
/// or in textual repr: hasCollectionPendingSponsor()
@@ -177,10 +190,10 @@
/// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.
/// @dev EVM selector for this function is: 0x6ec0a9f1,
/// or in textual repr: collectionSponsor()
- function collectionSponsor() public view returns (Tuple17 memory) {
+ function collectionSponsor() public view returns (Tuple6 memory) {
require(false, stub_error);
dummy;
- return Tuple17(0x0000000000000000000000000000000000000000, 0);
+ return Tuple6(0x0000000000000000000000000000000000000000, 0);
}
/// Set limits for the collection.
@@ -228,6 +241,26 @@
}
/// Add collection admin.
+ /// @param newAdmin Cross account administrator address.
+ /// @dev EVM selector for this function is: 0x859aa7d6,
+ /// or in textual repr: addCollectionAdminCross((address,uint256))
+ function addCollectionAdminCross(Tuple6 memory newAdmin) public {
+ require(false, stub_error);
+ newAdmin;
+ dummy = 0;
+ }
+
+ /// Remove collection admin.
+ /// @param admin Cross account administrator address.
+ /// @dev EVM selector for this function is: 0x6c0cd173,
+ /// or in textual repr: removeCollectionAdminCross((address,uint256))
+ function removeCollectionAdminCross(Tuple6 memory admin) public {
+ require(false, stub_error);
+ admin;
+ dummy = 0;
+ }
+
+ /// Add collection admin.
/// @param newAdmin Address of the added administrator.
/// @dev EVM selector for this function is: 0x92e462c7,
/// or in textual repr: addCollectionAdmin(address)
@@ -307,6 +340,17 @@
dummy = 0;
}
+ /// Add user to allowed list.
+ ///
+ /// @param user User cross account address.
+ /// @dev EVM selector for this function is: 0xa0184a3a,
+ /// or in textual repr: addToCollectionAllowListCross((address,uint256))
+ function addToCollectionAllowListCross(Tuple6 memory user) public {
+ require(false, stub_error);
+ user;
+ dummy = 0;
+ }
+
/// Remove the user from the allowed list.
///
/// @param user Address of a removed user.
@@ -318,6 +362,17 @@
dummy = 0;
}
+ /// Remove user from allowed list.
+ ///
+ /// @param user User cross account address.
+ /// @dev EVM selector for this function is: 0x09ba452a,
+ /// or in textual repr: removeFromCollectionAllowListCross((address,uint256))
+ function removeFromCollectionAllowListCross(Tuple6 memory user) public {
+ require(false, stub_error);
+ user;
+ dummy = 0;
+ }
+
/// Switch permission for minting.
///
/// @param mode Enable if "true".
@@ -342,6 +397,19 @@
return false;
}
+ /// Check that account is the owner or admin of the collection
+ ///
+ /// @param user User cross account to verify
+ /// @return "true" if account is the owner or admin
+ /// @dev EVM selector for this function is: 0x3e75a905,
+ /// or in textual repr: isOwnerOrAdminCross((address,uint256))
+ function isOwnerOrAdminCross(Tuple6 memory user) public view returns (bool) {
+ require(false, stub_error);
+ user;
+ dummy;
+ return false;
+ }
+
/// Returns collection type
///
/// @return `Fungible` or `NFT` or `ReFungible`
@@ -355,14 +423,14 @@
/// Get collection owner.
///
- /// @return Tuple with sponsor address and his substrate mirror.
+ /// @return Tuble with sponsor address and his substrate mirror.
/// If address is canonical then substrate mirror is zero and vice versa.
/// @dev EVM selector for this function is: 0xdf727d3b,
/// or in textual repr: collectionOwner()
- function collectionOwner() public view returns (Tuple17 memory) {
+ function collectionOwner() public view returns (Tuple6 memory) {
require(false, stub_error);
dummy;
- return Tuple17(0x0000000000000000000000000000000000000000, 0);
+ return Tuple6(0x0000000000000000000000000000000000000000, 0);
}
/// Changes collection owner to another account
@@ -383,17 +451,23 @@
/// If address is canonical then substrate mirror is zero and vice versa.
/// @dev EVM selector for this function is: 0x5813216b,
/// or in textual repr: collectionAdmins()
- function collectionAdmins() public view returns (Tuple17[] memory) {
+ function collectionAdmins() public view returns (Tuple6[] memory) {
require(false, stub_error);
dummy;
- return new Tuple17[](0);
+ return new Tuple6[](0);
}
-}
-/// @dev anonymous struct
-struct Tuple17 {
- address field_0;
- uint256 field_1;
+ /// Changes collection owner to another account
+ ///
+ /// @dev Owner can be changed only by current owner
+ /// @param newOwner new owner cross account
+ /// @dev EVM selector for this function is: 0xe5c9913f,
+ /// or in textual repr: setOwnerCross((address,uint256))
+ function setOwnerCross(Tuple6 memory newOwner) public {
+ require(false, stub_error);
+ newOwner;
+ dummy = 0;
+ }
}
/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
@@ -540,7 +614,7 @@
}
/// @title Unique extensions for ERC721.
-/// @dev the ERC-165 identifier for this interface is 0x4468500d
+/// @dev the ERC-165 identifier for this interface is 0x244543ee
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,
@@ -560,6 +634,21 @@
return "";
}
+ /// @notice Set or reaffirm the approved address for an NFT
+ /// @dev The zero address indicates there is no approved address.
+ /// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized
+ /// operator of the current owner.
+ /// @param approved The new substrate address approved NFT controller
+ /// @param tokenId The NFT to approve
+ /// @dev EVM selector for this function is: 0x0ecd0ab0,
+ /// or in textual repr: approveCross((address,uint256),uint256)
+ function approveCross(Tuple6 memory approved, uint256 tokenId) public {
+ require(false, stub_error);
+ approved;
+ tokenId;
+ dummy = 0;
+ }
+
/// @notice Transfer ownership of an NFT
/// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
/// is the zero address. Throws if `tokenId` is not a valid NFT.
@@ -574,6 +663,26 @@
dummy = 0;
}
+ /// @notice Transfer ownership of an NFT from cross account address to cross account address
+ /// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
+ /// is the zero address. Throws if `tokenId` is not a valid NFT.
+ /// @param from Cross acccount address of current owner
+ /// @param to Cross acccount address of new owner
+ /// @param tokenId The NFT to transfer
+ /// @dev EVM selector for this function is: 0xd5cf430b,
+ /// or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256)
+ function transferFromCross(
+ Tuple6 memory from,
+ Tuple6 memory to,
+ uint256 tokenId
+ ) public {
+ require(false, stub_error);
+ from;
+ to;
+ tokenId;
+ dummy = 0;
+ }
+
/// @notice Burns a specific ERC721 token.
/// @dev Throws unless `msg.sender` is the current owner or an authorized
/// operator for this NFT. Throws if `from` is not the current owner. Throws
@@ -589,6 +698,21 @@
dummy = 0;
}
+ /// @notice Burns a specific ERC721 token.
+ /// @dev Throws unless `msg.sender` is the current owner or an authorized
+ /// operator for this NFT. Throws if `from` is not the current owner. Throws
+ /// if `to` is the zero address. Throws if `tokenId` is not a valid NFT.
+ /// @param from The current owner of the NFT
+ /// @param tokenId The NFT to transfer
+ /// @dev EVM selector for this function is: 0xbb2f5a58,
+ /// or in textual repr: burnFromCross((address,uint256),uint256)
+ function burnFromCross(Tuple6 memory from, uint256 tokenId) public {
+ require(false, stub_error);
+ from;
+ tokenId;
+ dummy = 0;
+ }
+
/// @notice Returns next free NFT ID.
/// @dev EVM selector for this function is: 0x75794a3c,
/// or in textual repr: nextTokenId()
@@ -619,7 +743,7 @@
// /// @param tokens array of pairs of token ID and token URI for minted tokens
// /// @dev EVM selector for this function is: 0x36543006,
// /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])
- // function mintBulkWithTokenURI(address to, Tuple6[] memory tokens) public returns (bool) {
+ // function mintBulkWithTokenURI(address to, Tuple8[] memory tokens) public returns (bool) {
// require(false, stub_error);
// to;
// tokens;
@@ -630,11 +754,17 @@
}
/// @dev anonymous struct
-struct Tuple6 {
+struct Tuple8 {
uint256 field_0;
string field_1;
}
+/// @dev anonymous struct
+struct Tuple6 {
+ address field_0;
+ uint256 field_1;
+}
+
/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// @dev the ERC-165 identifier for this interface is 0x780e9d63
pallets/refungible/src/erc.rsdiffbeforeafterboth--- a/pallets/refungible/src/erc.rs
+++ b/pallets/refungible/src/erc.rs
@@ -30,6 +30,7 @@
use pallet_common::{
CollectionHandle, CollectionPropertyPermissions,
erc::{CommonEvmHandler, CollectionCall, static_property::key},
+ eth::convert_tuple_to_cross_account,
};
use pallet_evm::{account::CrossAccountId, PrecompileHandle};
use pallet_evm_coder_substrate::{call, dispatch_to_evm};
@@ -194,7 +195,10 @@
}
#[solidity_interface(name = ERC721Metadata)]
-impl<T: Config> RefungibleHandle<T> {
+impl<T: Config> RefungibleHandle<T>
+where
+ T::AccountId: From<[u8; 32]>,
+{
/// @notice A descriptive name for a collection of NFTs in this contract
/// @dev real implementation of this function lies in `ERC721UniqueExtensions`
#[solidity(hide, rename_selector = "name")]
@@ -631,7 +635,10 @@
/// @title Unique extensions for ERC721.
#[solidity_interface(name = ERC721UniqueExtensions)]
-impl<T: Config> RefungibleHandle<T> {
+impl<T: Config> RefungibleHandle<T>
+where
+ T::AccountId: From<[u8; 32]>,
+{
/// @notice A descriptive name for a collection of NFTs in this contract
fn name(&self) -> Result<string> {
Ok(decode_utf16(self.name.iter().copied())
@@ -659,14 +666,44 @@
.recorder
.weight_calls_budget(<StructureWeight<T>>::find_parent());
- let balance = balance(&self, token, &caller)?;
- ensure_single_owner(&self, token, balance)?;
+ let balance = balance(self, token, &caller)?;
+ ensure_single_owner(self, token, balance)?;
<Pallet<T>>::transfer(self, &caller, &to, token, balance, &budget)
.map_err(dispatch_to_evm::<T>)?;
Ok(())
}
+ /// @notice Transfer ownership of an RFT
+ /// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
+ /// is the zero address. Throws if `tokenId` is not a valid RFT.
+ /// Throws if RFT pieces have multiple owners.
+ /// @param to The new owner
+ /// @param tokenId The RFT to transfer
+ #[weight(<SelfWeightOf<T>>::transfer_creating_removing())]
+ fn transfer_from_cross(
+ &mut self,
+ caller: caller,
+ from: (address, uint256),
+ to: (address, uint256),
+ token_id: uint256,
+ ) -> Result<void> {
+ let caller = T::CrossAccountId::from_eth(caller);
+ let from = convert_tuple_to_cross_account::<T>(from)?;
+ let to = convert_tuple_to_cross_account::<T>(to)?;
+ let token_id = token_id.try_into()?;
+ let budget = self
+ .recorder
+ .weight_calls_budget(<StructureWeight<T>>::find_parent());
+
+ let balance = balance(self, token_id, &from)?;
+ ensure_single_owner(self, token_id, balance)?;
+
+ Pallet::<T>::transfer_from(self, &caller, &from, &to, token_id, balance, &budget)
+ .map_err(dispatch_to_evm::<T>)?;
+ Ok(())
+ }
+
/// @notice Burns a specific ERC721 token.
/// @dev Throws unless `msg.sender` is the current owner or an authorized
/// operator for this RFT. Throws if `from` is not the current owner. Throws
@@ -683,8 +720,37 @@
.recorder
.weight_calls_budget(<StructureWeight<T>>::find_parent());
- let balance = balance(&self, token, &caller)?;
- ensure_single_owner(&self, token, balance)?;
+ let balance = balance(self, token, &from)?;
+ ensure_single_owner(self, token, balance)?;
+
+ <Pallet<T>>::burn_from(self, &caller, &from, token, balance, &budget)
+ .map_err(dispatch_to_evm::<T>)?;
+ Ok(())
+ }
+
+ /// @notice Burns a specific ERC721 token.
+ /// @dev Throws unless `msg.sender` is the current owner or an authorized
+ /// operator for this RFT. Throws if `from` is not the current owner. Throws
+ /// if `to` is the zero address. Throws if `tokenId` is not a valid RFT.
+ /// Throws if RFT pieces have multiple owners.
+ /// @param from The current owner of the RFT
+ /// @param tokenId The RFT to transfer
+ #[weight(<SelfWeightOf<T>>::burn_from())]
+ fn burn_from_cross(
+ &mut self,
+ caller: caller,
+ from: (address, uint256),
+ token_id: uint256,
+ ) -> Result<void> {
+ let caller = T::CrossAccountId::from_eth(caller);
+ let from = convert_tuple_to_cross_account::<T>(from)?;
+ let token = token_id.try_into()?;
+ let budget = self
+ .recorder
+ .weight_calls_budget(<StructureWeight<T>>::find_parent());
+
+ let balance = balance(self, token, &from)?;
+ ensure_single_owner(self, token, balance)?;
<Pallet<T>>::burn_from(self, &caller, &from, token, balance, &budget)
.map_err(dispatch_to_evm::<T>)?;
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
@@ -91,7 +91,7 @@
}
/// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0x3af103fb
+/// @dev the ERC-165 identifier for this interface is 0x25d897dc
contract Collection is Dummy, ERC165 {
/// Set collection property.
///
@@ -145,6 +145,19 @@
dummy = 0;
}
+ /// Set the sponsor of the collection.
+ ///
+ /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
+ ///
+ /// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract.
+ /// @dev EVM selector for this function is: 0x84a1d5a8,
+ /// or in textual repr: setCollectionSponsorCross((address,uint256))
+ function setCollectionSponsorCross(Tuple6 memory sponsor) public {
+ require(false, stub_error);
+ sponsor;
+ dummy = 0;
+ }
+
/// Whether there is a pending sponsor.
/// @dev EVM selector for this function is: 0x058ac185,
/// or in textual repr: hasCollectionPendingSponsor()
@@ -177,10 +190,10 @@
/// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.
/// @dev EVM selector for this function is: 0x6ec0a9f1,
/// or in textual repr: collectionSponsor()
- function collectionSponsor() public view returns (Tuple17 memory) {
+ function collectionSponsor() public view returns (Tuple6 memory) {
require(false, stub_error);
dummy;
- return Tuple17(0x0000000000000000000000000000000000000000, 0);
+ return Tuple6(0x0000000000000000000000000000000000000000, 0);
}
/// Set limits for the collection.
@@ -228,6 +241,26 @@
}
/// Add collection admin.
+ /// @param newAdmin Cross account administrator address.
+ /// @dev EVM selector for this function is: 0x859aa7d6,
+ /// or in textual repr: addCollectionAdminCross((address,uint256))
+ function addCollectionAdminCross(Tuple6 memory newAdmin) public {
+ require(false, stub_error);
+ newAdmin;
+ dummy = 0;
+ }
+
+ /// Remove collection admin.
+ /// @param admin Cross account administrator address.
+ /// @dev EVM selector for this function is: 0x6c0cd173,
+ /// or in textual repr: removeCollectionAdminCross((address,uint256))
+ function removeCollectionAdminCross(Tuple6 memory admin) public {
+ require(false, stub_error);
+ admin;
+ dummy = 0;
+ }
+
+ /// Add collection admin.
/// @param newAdmin Address of the added administrator.
/// @dev EVM selector for this function is: 0x92e462c7,
/// or in textual repr: addCollectionAdmin(address)
@@ -307,6 +340,17 @@
dummy = 0;
}
+ /// Add user to allowed list.
+ ///
+ /// @param user User cross account address.
+ /// @dev EVM selector for this function is: 0xa0184a3a,
+ /// or in textual repr: addToCollectionAllowListCross((address,uint256))
+ function addToCollectionAllowListCross(Tuple6 memory user) public {
+ require(false, stub_error);
+ user;
+ dummy = 0;
+ }
+
/// Remove the user from the allowed list.
///
/// @param user Address of a removed user.
@@ -318,6 +362,17 @@
dummy = 0;
}
+ /// Remove user from allowed list.
+ ///
+ /// @param user User cross account address.
+ /// @dev EVM selector for this function is: 0x09ba452a,
+ /// or in textual repr: removeFromCollectionAllowListCross((address,uint256))
+ function removeFromCollectionAllowListCross(Tuple6 memory user) public {
+ require(false, stub_error);
+ user;
+ dummy = 0;
+ }
+
/// Switch permission for minting.
///
/// @param mode Enable if "true".
@@ -342,6 +397,19 @@
return false;
}
+ /// Check that account is the owner or admin of the collection
+ ///
+ /// @param user User cross account to verify
+ /// @return "true" if account is the owner or admin
+ /// @dev EVM selector for this function is: 0x3e75a905,
+ /// or in textual repr: isOwnerOrAdminCross((address,uint256))
+ function isOwnerOrAdminCross(Tuple6 memory user) public view returns (bool) {
+ require(false, stub_error);
+ user;
+ dummy;
+ return false;
+ }
+
/// Returns collection type
///
/// @return `Fungible` or `NFT` or `ReFungible`
@@ -355,14 +423,14 @@
/// Get collection owner.
///
- /// @return Tuple with sponsor address and his substrate mirror.
+ /// @return Tuble with sponsor address and his substrate mirror.
/// If address is canonical then substrate mirror is zero and vice versa.
/// @dev EVM selector for this function is: 0xdf727d3b,
/// or in textual repr: collectionOwner()
- function collectionOwner() public view returns (Tuple17 memory) {
+ function collectionOwner() public view returns (Tuple6 memory) {
require(false, stub_error);
dummy;
- return Tuple17(0x0000000000000000000000000000000000000000, 0);
+ return Tuple6(0x0000000000000000000000000000000000000000, 0);
}
/// Changes collection owner to another account
@@ -383,17 +451,23 @@
/// If address is canonical then substrate mirror is zero and vice versa.
/// @dev EVM selector for this function is: 0x5813216b,
/// or in textual repr: collectionAdmins()
- function collectionAdmins() public view returns (Tuple17[] memory) {
+ function collectionAdmins() public view returns (Tuple6[] memory) {
require(false, stub_error);
dummy;
- return new Tuple17[](0);
+ return new Tuple6[](0);
}
-}
-/// @dev anonymous struct
-struct Tuple17 {
- address field_0;
- uint256 field_1;
+ /// Changes collection owner to another account
+ ///
+ /// @dev Owner can be changed only by current owner
+ /// @param newOwner new owner cross account
+ /// @dev EVM selector for this function is: 0xe5c9913f,
+ /// or in textual repr: setOwnerCross((address,uint256))
+ function setOwnerCross(Tuple6 memory newOwner) public {
+ require(false, stub_error);
+ newOwner;
+ dummy = 0;
+ }
}
/// @dev the ERC-165 identifier for this interface is 0x5b5e139f
@@ -538,7 +612,7 @@
}
/// @title Unique extensions for ERC721.
-/// @dev the ERC-165 identifier for this interface is 0xef1eaacb
+/// @dev the ERC-165 identifier for this interface is 0x81feb398
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,
@@ -573,6 +647,26 @@
dummy = 0;
}
+ /// @notice Transfer ownership of an RFT
+ /// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
+ /// is the zero address. Throws if `tokenId` is not a valid RFT.
+ /// Throws if RFT pieces have multiple owners.
+ /// @param to The new owner
+ /// @param tokenId The RFT to transfer
+ /// @dev EVM selector for this function is: 0xd5cf430b,
+ /// or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256)
+ function transferFromCross(
+ Tuple6 memory from,
+ Tuple6 memory to,
+ uint256 tokenId
+ ) public {
+ require(false, stub_error);
+ from;
+ to;
+ tokenId;
+ dummy = 0;
+ }
+
/// @notice Burns a specific ERC721 token.
/// @dev Throws unless `msg.sender` is the current owner or an authorized
/// operator for this RFT. Throws if `from` is not the current owner. Throws
@@ -589,6 +683,22 @@
dummy = 0;
}
+ /// @notice Burns a specific ERC721 token.
+ /// @dev Throws unless `msg.sender` is the current owner or an authorized
+ /// operator for this RFT. Throws if `from` is not the current owner. Throws
+ /// if `to` is the zero address. Throws if `tokenId` is not a valid RFT.
+ /// Throws if RFT pieces have multiple owners.
+ /// @param from The current owner of the RFT
+ /// @param tokenId The RFT to transfer
+ /// @dev EVM selector for this function is: 0xbb2f5a58,
+ /// or in textual repr: burnFromCross((address,uint256),uint256)
+ function burnFromCross(Tuple6 memory from, uint256 tokenId) public {
+ require(false, stub_error);
+ from;
+ tokenId;
+ dummy = 0;
+ }
+
/// @notice Returns next free RFT ID.
/// @dev EVM selector for this function is: 0x75794a3c,
/// or in textual repr: nextTokenId()
@@ -620,7 +730,7 @@
// /// @param tokens array of pairs of token ID and token URI for minted tokens
// /// @dev EVM selector for this function is: 0x36543006,
// /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])
- // function mintBulkWithTokenURI(address to, Tuple6[] memory tokens) public returns (bool) {
+ // function mintBulkWithTokenURI(address to, Tuple8[] memory tokens) public returns (bool) {
// require(false, stub_error);
// to;
// tokens;
@@ -642,11 +752,17 @@
}
/// @dev anonymous struct
-struct Tuple6 {
+struct Tuple8 {
uint256 field_0;
string field_1;
}
+/// @dev anonymous struct
+struct Tuple6 {
+ address field_0;
+ uint256 field_1;
+}
+
/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// @dev the ERC-165 identifier for this interface is 0x780e9d63
tests/src/eth/allowlist.test.tsdiffbeforeafterboth--- a/tests/src/eth/allowlist.test.ts
+++ b/tests/src/eth/allowlist.test.ts
@@ -89,22 +89,22 @@
expect(await collectionEvm.methods.allowed(user).call({from: owner})).to.be.false;
});
- // TODO: Temprorary off. Need refactor
- // itEth('Collection allowlist can be added and removed by [sub] address', async ({helper}) => {
- // const owner = await helper.eth.createAccountWithBalance(donor);
- // const user = donor;
-
- // const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
- // const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-
- // expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.false;
- // await collectionEvm.methods.addToCollectionAllowListSubstrate(user.addressRaw).send({from: owner});
- // expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.true;
+ //itEth('Collection allowlist can be added and removed by [cross] address', async ({helper}) => {
+ // const owner = await helper.eth.createAccountWithBalance(donor);
+ // const user = donor;
+ //
+ // const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
+ // const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+ // const userCross = helper.ethCrossAccount.fromKeyringPair(user);
+ //
+ // expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.false;
+ // await collectionEvm.methods.addToCollectionAllowListCross(userCross).send({from: owner});
+ // expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.true;
+ //
+ // await collectionEvm.methods.removeFromCollectionAllowListCross(userCross).send({from: owner});
+ // expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.false;
+ //});
- // await collectionEvm.methods.removeFromCollectionAllowListSubstrate(user.addressRaw).send({from: owner});
- // expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.false;
- // });
-
itEth('Collection allowlist can not be add and remove [eth] address by not owner', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
const notOwner = await helper.eth.createAccountWithBalance(donor);
@@ -117,28 +117,27 @@
await expect(collectionEvm.methods.addToCollectionAllowList(user).call({from: notOwner})).to.be.rejectedWith('NoPermission');
expect(await collectionEvm.methods.allowed(user).call({from: owner})).to.be.false;
await collectionEvm.methods.addToCollectionAllowList(user).send({from: owner});
-
expect(await collectionEvm.methods.allowed(user).call({from: owner})).to.be.true;
await expect(collectionEvm.methods.removeFromCollectionAllowList(user).call({from: notOwner})).to.be.rejectedWith('NoPermission');
expect(await collectionEvm.methods.allowed(user).call({from: owner})).to.be.true;
});
- // TODO: Temprorary off. Need refactor
- // itEth('Collection allowlist can not be add and remove [sub] address by not owner', async ({helper}) => {
- // const owner = await helper.eth.createAccountWithBalance(donor);
- // const notOwner = await helper.eth.createAccountWithBalance(donor);
- // const user = donor;
-
- // const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
- // const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-
- // expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.false;
- // await expect(collectionEvm.methods.addToCollectionAllowListSubstrate(user.addressRaw).call({from: notOwner})).to.be.rejectedWith('NoPermission');
- // expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.false;
- // await collectionEvm.methods.addToCollectionAllowListSubstrate(user.addressRaw).send({from: owner});
-
- // expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.true;
- // await expect(collectionEvm.methods.removeFromCollectionAllowListSubstrate(user.addressRaw).call({from: notOwner})).to.be.rejectedWith('NoPermission');
- // expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.true;
- // });
+ //itEth('Collection allowlist can not be add and remove [cross] address by not owner', async ({helper}) => {
+ // const owner = await helper.eth.createAccountWithBalance(donor);
+ // const notOwner = await helper.eth.createAccountWithBalance(donor);
+ // const user = donor;
+ //
+ // const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
+ // const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+ //
+ // expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.false;
+ // const userCross = helper.ethCrossAccount.fromKeyringPair(user);
+ // await expect(collectionEvm.methods.addToCollectionAllowListCross(userCross).call({from: notOwner})).to.be.rejectedWith('NoPermission');
+ // expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.false;
+ // await collectionEvm.methods.addToCollectionAllowListCross(userCross).send({from: owner});
+ //
+ // expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.true;
+ // await expect(collectionEvm.methods.removeFromCollectionAllowListCross(userCross).call({from: notOwner})).to.be.rejectedWith('NoPermission');
+ // expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.true;
+ //});
});
tests/src/eth/api/UniqueFungible.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueFungible.sol
+++ b/tests/src/eth/api/UniqueFungible.sol
@@ -13,7 +13,7 @@
}
/// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0x3af103fb
+/// @dev the ERC-165 identifier for this interface is 0x25d897dc
interface Collection is Dummy, ERC165 {
/// Set collection property.
///
@@ -49,6 +49,15 @@
/// or in textual repr: setCollectionSponsor(address)
function setCollectionSponsor(address sponsor) external;
+ /// Set the sponsor of the collection.
+ ///
+ /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
+ ///
+ /// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract.
+ /// @dev EVM selector for this function is: 0x84a1d5a8,
+ /// or in textual repr: setCollectionSponsorCross((address,uint256))
+ function setCollectionSponsorCross(Tuple6 memory sponsor) external;
+
/// Whether there is a pending sponsor.
/// @dev EVM selector for this function is: 0x058ac185,
/// or in textual repr: hasCollectionPendingSponsor()
@@ -104,6 +113,18 @@
function contractAddress() external view returns (address);
/// Add collection admin.
+ /// @param newAdmin Cross account administrator address.
+ /// @dev EVM selector for this function is: 0x859aa7d6,
+ /// or in textual repr: addCollectionAdminCross((address,uint256))
+ function addCollectionAdminCross(Tuple6 memory newAdmin) external;
+
+ /// Remove collection admin.
+ /// @param admin Cross account administrator address.
+ /// @dev EVM selector for this function is: 0x6c0cd173,
+ /// or in textual repr: removeCollectionAdminCross((address,uint256))
+ function removeCollectionAdminCross(Tuple6 memory admin) external;
+
+ /// Add collection admin.
/// @param newAdmin Address of the added administrator.
/// @dev EVM selector for this function is: 0x92e462c7,
/// or in textual repr: addCollectionAdmin(address)
@@ -153,6 +174,13 @@
/// or in textual repr: addToCollectionAllowList(address)
function addToCollectionAllowList(address user) external;
+ /// Add user to allowed list.
+ ///
+ /// @param user User cross account address.
+ /// @dev EVM selector for this function is: 0xa0184a3a,
+ /// or in textual repr: addToCollectionAllowListCross((address,uint256))
+ function addToCollectionAllowListCross(Tuple6 memory user) external;
+
/// Remove the user from the allowed list.
///
/// @param user Address of a removed user.
@@ -160,6 +188,13 @@
/// or in textual repr: removeFromCollectionAllowList(address)
function removeFromCollectionAllowList(address user) external;
+ /// Remove user from allowed list.
+ ///
+ /// @param user User cross account address.
+ /// @dev EVM selector for this function is: 0x09ba452a,
+ /// or in textual repr: removeFromCollectionAllowListCross((address,uint256))
+ function removeFromCollectionAllowListCross(Tuple6 memory user) external;
+
/// Switch permission for minting.
///
/// @param mode Enable if "true".
@@ -175,6 +210,14 @@
/// or in textual repr: isOwnerOrAdmin(address)
function isOwnerOrAdmin(address user) external view returns (bool);
+ /// Check that account is the owner or admin of the collection
+ ///
+ /// @param user User cross account to verify
+ /// @return "true" if account is the owner or admin
+ /// @dev EVM selector for this function is: 0x3e75a905,
+ /// or in textual repr: isOwnerOrAdminCross((address,uint256))
+ function isOwnerOrAdminCross(Tuple6 memory user) external view returns (bool);
+
/// Returns collection type
///
/// @return `Fungible` or `NFT` or `ReFungible`
@@ -184,7 +227,7 @@
/// Get collection owner.
///
- /// @return Tuple with sponsor address and his substrate mirror.
+ /// @return Tuble with sponsor address and his substrate mirror.
/// If address is canonical then substrate mirror is zero and vice versa.
/// @dev EVM selector for this function is: 0xdf727d3b,
/// or in textual repr: collectionOwner()
@@ -205,10 +248,22 @@
/// @dev EVM selector for this function is: 0x5813216b,
/// or in textual repr: collectionAdmins()
function collectionAdmins() external view returns (Tuple6[] memory);
+
+ /// Changes collection owner to another account
+ ///
+ /// @dev Owner can be changed only by current owner
+ /// @param newOwner new owner cross account
+ /// @dev EVM selector for this function is: 0xe5c9913f,
+ /// or in textual repr: setOwnerCross((address,uint256))
+ function setOwnerCross(Tuple6 memory newOwner) external;
}
-/// @dev the ERC-165 identifier for this interface is 0x63034ac5
+/// @dev the ERC-165 identifier for this interface is 0x032e5926
interface ERC20UniqueExtensions is Dummy, ERC165 {
+ /// @dev EVM selector for this function is: 0x0ecd0ab0,
+ /// or in textual repr: approveCross((address,uint256),uint256)
+ function approveCross(Tuple6 memory spender, uint256 amount) external returns (bool);
+
/// Burn tokens from account
/// @dev Function that burns an `amount` of the tokens of a given account,
/// deducting from the sender's allowance for said account.
@@ -218,11 +273,28 @@
/// or in textual repr: burnFrom(address,uint256)
function burnFrom(address from, uint256 amount) external returns (bool);
+ /// Burn tokens from account
+ /// @dev Function that burns an `amount` of the tokens of a given account,
+ /// deducting from the sender's allowance for said account.
+ /// @param from The account whose tokens will be burnt.
+ /// @param amount The amount that will be burnt.
+ /// @dev EVM selector for this function is: 0xbb2f5a58,
+ /// or in textual repr: burnFromCross((address,uint256),uint256)
+ function burnFromCross(Tuple6 memory from, uint256 amount) external returns (bool);
+
/// Mint tokens for multiple accounts.
/// @param amounts array of pairs of account address and amount
/// @dev EVM selector for this function is: 0x1acf2d55,
/// or in textual repr: mintBulk((address,uint256)[])
function mintBulk(Tuple6[] memory amounts) external returns (bool);
+
+ /// @dev EVM selector for this function is: 0xd5cf430b,
+ /// or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256)
+ function transferFromCross(
+ Tuple6 memory from,
+ Tuple6 memory to,
+ uint256 amount
+ ) external returns (bool);
}
/// @dev anonymous struct
tests/src/eth/api/UniqueNFT.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueNFT.sol
+++ b/tests/src/eth/api/UniqueNFT.sol
@@ -62,7 +62,7 @@
}
/// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0x3af103fb
+/// @dev the ERC-165 identifier for this interface is 0x25d897dc
interface Collection is Dummy, ERC165 {
/// Set collection property.
///
@@ -98,6 +98,15 @@
/// or in textual repr: setCollectionSponsor(address)
function setCollectionSponsor(address sponsor) external;
+ /// Set the sponsor of the collection.
+ ///
+ /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
+ ///
+ /// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract.
+ /// @dev EVM selector for this function is: 0x84a1d5a8,
+ /// or in textual repr: setCollectionSponsorCross((address,uint256))
+ function setCollectionSponsorCross(Tuple6 memory sponsor) external;
+
/// Whether there is a pending sponsor.
/// @dev EVM selector for this function is: 0x058ac185,
/// or in textual repr: hasCollectionPendingSponsor()
@@ -120,7 +129,7 @@
/// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.
/// @dev EVM selector for this function is: 0x6ec0a9f1,
/// or in textual repr: collectionSponsor()
- function collectionSponsor() external view returns (Tuple17 memory);
+ function collectionSponsor() external view returns (Tuple6 memory);
/// Set limits for the collection.
/// @dev Throws error if limit not found.
@@ -153,6 +162,18 @@
function contractAddress() external view returns (address);
/// Add collection admin.
+ /// @param newAdmin Cross account administrator address.
+ /// @dev EVM selector for this function is: 0x859aa7d6,
+ /// or in textual repr: addCollectionAdminCross((address,uint256))
+ function addCollectionAdminCross(Tuple6 memory newAdmin) external;
+
+ /// Remove collection admin.
+ /// @param admin Cross account administrator address.
+ /// @dev EVM selector for this function is: 0x6c0cd173,
+ /// or in textual repr: removeCollectionAdminCross((address,uint256))
+ function removeCollectionAdminCross(Tuple6 memory admin) external;
+
+ /// Add collection admin.
/// @param newAdmin Address of the added administrator.
/// @dev EVM selector for this function is: 0x92e462c7,
/// or in textual repr: addCollectionAdmin(address)
@@ -202,6 +223,13 @@
/// or in textual repr: addToCollectionAllowList(address)
function addToCollectionAllowList(address user) external;
+ /// Add user to allowed list.
+ ///
+ /// @param user User cross account address.
+ /// @dev EVM selector for this function is: 0xa0184a3a,
+ /// or in textual repr: addToCollectionAllowListCross((address,uint256))
+ function addToCollectionAllowListCross(Tuple6 memory user) external;
+
/// Remove the user from the allowed list.
///
/// @param user Address of a removed user.
@@ -209,6 +237,13 @@
/// or in textual repr: removeFromCollectionAllowList(address)
function removeFromCollectionAllowList(address user) external;
+ /// Remove user from allowed list.
+ ///
+ /// @param user User cross account address.
+ /// @dev EVM selector for this function is: 0x09ba452a,
+ /// or in textual repr: removeFromCollectionAllowListCross((address,uint256))
+ function removeFromCollectionAllowListCross(Tuple6 memory user) external;
+
/// Switch permission for minting.
///
/// @param mode Enable if "true".
@@ -224,6 +259,14 @@
/// or in textual repr: isOwnerOrAdmin(address)
function isOwnerOrAdmin(address user) external view returns (bool);
+ /// Check that account is the owner or admin of the collection
+ ///
+ /// @param user User cross account to verify
+ /// @return "true" if account is the owner or admin
+ /// @dev EVM selector for this function is: 0x3e75a905,
+ /// or in textual repr: isOwnerOrAdminCross((address,uint256))
+ function isOwnerOrAdminCross(Tuple6 memory user) external view returns (bool);
+
/// Returns collection type
///
/// @return `Fungible` or `NFT` or `ReFungible`
@@ -233,11 +276,11 @@
/// Get collection owner.
///
- /// @return Tuple with sponsor address and his substrate mirror.
+ /// @return Tuble with sponsor address and his substrate mirror.
/// If address is canonical then substrate mirror is zero and vice versa.
/// @dev EVM selector for this function is: 0xdf727d3b,
/// or in textual repr: collectionOwner()
- function collectionOwner() external view returns (Tuple17 memory);
+ function collectionOwner() external view returns (Tuple6 memory);
/// Changes collection owner to another account
///
@@ -253,13 +296,15 @@
/// If address is canonical then substrate mirror is zero and vice versa.
/// @dev EVM selector for this function is: 0x5813216b,
/// or in textual repr: collectionAdmins()
- function collectionAdmins() external view returns (Tuple17[] memory);
-}
+ function collectionAdmins() external view returns (Tuple6[] memory);
-/// @dev anonymous struct
-struct Tuple17 {
- address field_0;
- uint256 field_1;
+ /// Changes collection owner to another account
+ ///
+ /// @dev Owner can be changed only by current owner
+ /// @param newOwner new owner cross account
+ /// @dev EVM selector for this function is: 0xe5c9913f,
+ /// or in textual repr: setOwnerCross((address,uint256))
+ function setOwnerCross(Tuple6 memory newOwner) external;
}
/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
@@ -357,7 +402,7 @@
}
/// @title Unique extensions for ERC721.
-/// @dev the ERC-165 identifier for this interface is 0x4468500d
+/// @dev the ERC-165 identifier for this interface is 0x244543ee
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,
@@ -369,6 +414,16 @@
/// or in textual repr: symbol()
function symbol() external view returns (string memory);
+ /// @notice Set or reaffirm the approved address for an NFT
+ /// @dev The zero address indicates there is no approved address.
+ /// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized
+ /// operator of the current owner.
+ /// @param approved The new substrate address approved NFT controller
+ /// @param tokenId The NFT to approve
+ /// @dev EVM selector for this function is: 0x0ecd0ab0,
+ /// or in textual repr: approveCross((address,uint256),uint256)
+ function approveCross(Tuple6 memory approved, uint256 tokenId) external;
+
/// @notice Transfer ownership of an NFT
/// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
/// is the zero address. Throws if `tokenId` is not a valid NFT.
@@ -378,6 +433,20 @@
/// or in textual repr: transfer(address,uint256)
function transfer(address to, uint256 tokenId) external;
+ /// @notice Transfer ownership of an NFT from cross account address to cross account address
+ /// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
+ /// is the zero address. Throws if `tokenId` is not a valid NFT.
+ /// @param from Cross acccount address of current owner
+ /// @param to Cross acccount address of new owner
+ /// @param tokenId The NFT to transfer
+ /// @dev EVM selector for this function is: 0xd5cf430b,
+ /// or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256)
+ function transferFromCross(
+ Tuple6 memory from,
+ Tuple6 memory to,
+ uint256 tokenId
+ ) external;
+
/// @notice Burns a specific ERC721 token.
/// @dev Throws unless `msg.sender` is the current owner or an authorized
/// operator for this NFT. Throws if `from` is not the current owner. Throws
@@ -388,6 +457,16 @@
/// or in textual repr: burnFrom(address,uint256)
function burnFrom(address from, uint256 tokenId) external;
+ /// @notice Burns a specific ERC721 token.
+ /// @dev Throws unless `msg.sender` is the current owner or an authorized
+ /// operator for this NFT. Throws if `from` is not the current owner. Throws
+ /// if `to` is the zero address. Throws if `tokenId` is not a valid NFT.
+ /// @param from The current owner of the NFT
+ /// @param tokenId The NFT to transfer
+ /// @dev EVM selector for this function is: 0xbb2f5a58,
+ /// or in textual repr: burnFromCross((address,uint256),uint256)
+ function burnFromCross(Tuple6 memory from, uint256 tokenId) external;
+
/// @notice Returns next free NFT ID.
/// @dev EVM selector for this function is: 0x75794a3c,
/// or in textual repr: nextTokenId()
@@ -408,16 +487,22 @@
// /// @param tokens array of pairs of token ID and token URI for minted tokens
// /// @dev EVM selector for this function is: 0x36543006,
// /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])
- // function mintBulkWithTokenURI(address to, Tuple6[] memory tokens) external returns (bool);
+ // function mintBulkWithTokenURI(address to, Tuple8[] memory tokens) external returns (bool);
}
/// @dev anonymous struct
-struct Tuple6 {
+struct Tuple8 {
uint256 field_0;
string field_1;
}
+/// @dev anonymous struct
+struct Tuple6 {
+ address field_0;
+ uint256 field_1;
+}
+
/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// @dev the ERC-165 identifier for this interface is 0x780e9d63
tests/src/eth/api/UniqueRefungible.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueRefungible.sol
+++ b/tests/src/eth/api/UniqueRefungible.sol
@@ -62,7 +62,7 @@
}
/// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0x3af103fb
+/// @dev the ERC-165 identifier for this interface is 0x25d897dc
interface Collection is Dummy, ERC165 {
/// Set collection property.
///
@@ -98,6 +98,15 @@
/// or in textual repr: setCollectionSponsor(address)
function setCollectionSponsor(address sponsor) external;
+ /// Set the sponsor of the collection.
+ ///
+ /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
+ ///
+ /// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract.
+ /// @dev EVM selector for this function is: 0x84a1d5a8,
+ /// or in textual repr: setCollectionSponsorCross((address,uint256))
+ function setCollectionSponsorCross(Tuple6 memory sponsor) external;
+
/// Whether there is a pending sponsor.
/// @dev EVM selector for this function is: 0x058ac185,
/// or in textual repr: hasCollectionPendingSponsor()
@@ -120,7 +129,7 @@
/// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.
/// @dev EVM selector for this function is: 0x6ec0a9f1,
/// or in textual repr: collectionSponsor()
- function collectionSponsor() external view returns (Tuple17 memory);
+ function collectionSponsor() external view returns (Tuple6 memory);
/// Set limits for the collection.
/// @dev Throws error if limit not found.
@@ -153,6 +162,18 @@
function contractAddress() external view returns (address);
/// Add collection admin.
+ /// @param newAdmin Cross account administrator address.
+ /// @dev EVM selector for this function is: 0x859aa7d6,
+ /// or in textual repr: addCollectionAdminCross((address,uint256))
+ function addCollectionAdminCross(Tuple6 memory newAdmin) external;
+
+ /// Remove collection admin.
+ /// @param admin Cross account administrator address.
+ /// @dev EVM selector for this function is: 0x6c0cd173,
+ /// or in textual repr: removeCollectionAdminCross((address,uint256))
+ function removeCollectionAdminCross(Tuple6 memory admin) external;
+
+ /// Add collection admin.
/// @param newAdmin Address of the added administrator.
/// @dev EVM selector for this function is: 0x92e462c7,
/// or in textual repr: addCollectionAdmin(address)
@@ -202,6 +223,13 @@
/// or in textual repr: addToCollectionAllowList(address)
function addToCollectionAllowList(address user) external;
+ /// Add user to allowed list.
+ ///
+ /// @param user User cross account address.
+ /// @dev EVM selector for this function is: 0xa0184a3a,
+ /// or in textual repr: addToCollectionAllowListCross((address,uint256))
+ function addToCollectionAllowListCross(Tuple6 memory user) external;
+
/// Remove the user from the allowed list.
///
/// @param user Address of a removed user.
@@ -209,6 +237,13 @@
/// or in textual repr: removeFromCollectionAllowList(address)
function removeFromCollectionAllowList(address user) external;
+ /// Remove user from allowed list.
+ ///
+ /// @param user User cross account address.
+ /// @dev EVM selector for this function is: 0x09ba452a,
+ /// or in textual repr: removeFromCollectionAllowListCross((address,uint256))
+ function removeFromCollectionAllowListCross(Tuple6 memory user) external;
+
/// Switch permission for minting.
///
/// @param mode Enable if "true".
@@ -224,6 +259,14 @@
/// or in textual repr: isOwnerOrAdmin(address)
function isOwnerOrAdmin(address user) external view returns (bool);
+ /// Check that account is the owner or admin of the collection
+ ///
+ /// @param user User cross account to verify
+ /// @return "true" if account is the owner or admin
+ /// @dev EVM selector for this function is: 0x3e75a905,
+ /// or in textual repr: isOwnerOrAdminCross((address,uint256))
+ function isOwnerOrAdminCross(Tuple6 memory user) external view returns (bool);
+
/// Returns collection type
///
/// @return `Fungible` or `NFT` or `ReFungible`
@@ -233,11 +276,11 @@
/// Get collection owner.
///
- /// @return Tuple with sponsor address and his substrate mirror.
+ /// @return Tuble with sponsor address and his substrate mirror.
/// If address is canonical then substrate mirror is zero and vice versa.
/// @dev EVM selector for this function is: 0xdf727d3b,
/// or in textual repr: collectionOwner()
- function collectionOwner() external view returns (Tuple17 memory);
+ function collectionOwner() external view returns (Tuple6 memory);
/// Changes collection owner to another account
///
@@ -253,13 +296,15 @@
/// If address is canonical then substrate mirror is zero and vice versa.
/// @dev EVM selector for this function is: 0x5813216b,
/// or in textual repr: collectionAdmins()
- function collectionAdmins() external view returns (Tuple17[] memory);
-}
+ function collectionAdmins() external view returns (Tuple6[] memory);
-/// @dev anonymous struct
-struct Tuple17 {
- address field_0;
- uint256 field_1;
+ /// Changes collection owner to another account
+ ///
+ /// @dev Owner can be changed only by current owner
+ /// @param newOwner new owner cross account
+ /// @dev EVM selector for this function is: 0xe5c9913f,
+ /// or in textual repr: setOwnerCross((address,uint256))
+ function setOwnerCross(Tuple6 memory newOwner) external;
}
/// @dev the ERC-165 identifier for this interface is 0x5b5e139f
@@ -355,7 +400,7 @@
}
/// @title Unique extensions for ERC721.
-/// @dev the ERC-165 identifier for this interface is 0xef1eaacb
+/// @dev the ERC-165 identifier for this interface is 0x81feb398
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,
@@ -377,6 +422,20 @@
/// or in textual repr: transfer(address,uint256)
function transfer(address to, uint256 tokenId) external;
+ /// @notice Transfer ownership of an RFT
+ /// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
+ /// is the zero address. Throws if `tokenId` is not a valid RFT.
+ /// Throws if RFT pieces have multiple owners.
+ /// @param to The new owner
+ /// @param tokenId The RFT to transfer
+ /// @dev EVM selector for this function is: 0xd5cf430b,
+ /// or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256)
+ function transferFromCross(
+ Tuple6 memory from,
+ Tuple6 memory to,
+ uint256 tokenId
+ ) external;
+
/// @notice Burns a specific ERC721 token.
/// @dev Throws unless `msg.sender` is the current owner or an authorized
/// operator for this RFT. Throws if `from` is not the current owner. Throws
@@ -388,6 +447,17 @@
/// or in textual repr: burnFrom(address,uint256)
function burnFrom(address from, uint256 tokenId) external;
+ /// @notice Burns a specific ERC721 token.
+ /// @dev Throws unless `msg.sender` is the current owner or an authorized
+ /// operator for this RFT. Throws if `from` is not the current owner. Throws
+ /// if `to` is the zero address. Throws if `tokenId` is not a valid RFT.
+ /// Throws if RFT pieces have multiple owners.
+ /// @param from The current owner of the RFT
+ /// @param tokenId The RFT to transfer
+ /// @dev EVM selector for this function is: 0xbb2f5a58,
+ /// or in textual repr: burnFromCross((address,uint256),uint256)
+ function burnFromCross(Tuple6 memory from, uint256 tokenId) external;
+
/// @notice Returns next free RFT ID.
/// @dev EVM selector for this function is: 0x75794a3c,
/// or in textual repr: nextTokenId()
@@ -409,7 +479,7 @@
// /// @param tokens array of pairs of token ID and token URI for minted tokens
// /// @dev EVM selector for this function is: 0x36543006,
// /// or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])
- // function mintBulkWithTokenURI(address to, Tuple6[] memory tokens) external returns (bool);
+ // function mintBulkWithTokenURI(address to, Tuple8[] memory tokens) external returns (bool);
/// Returns EVM address for refungible token
///
@@ -420,11 +490,17 @@
}
/// @dev anonymous struct
-struct Tuple6 {
+struct Tuple8 {
uint256 field_0;
string field_1;
}
+/// @dev anonymous struct
+struct Tuple6 {
+ address field_0;
+ uint256 field_1;
+}
+
/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// @dev the ERC-165 identifier for this interface is 0x780e9d63
tests/src/eth/base.test.tsdiffbeforeafterboth--- a/tests/src/eth/base.test.ts
+++ b/tests/src/eth/base.test.ts
@@ -116,8 +116,8 @@
await checkInterface(helper, '0x780e9d63', true, true);
});
- itEth('ERC721UniqueExtensions - 0x4468500d - support', async ({helper}) => {
- await checkInterface(helper, '0x4468500d', true, true);
+ itEth('ERC721UniqueExtensions - 0x244543ee - support', async ({helper}) => {
+ await checkInterface(helper, '0x244543ee', true, true);
});
itEth('ERC721Burnable - 0x42966c68 - support', async ({helper}) => {
tests/src/eth/collectionAdmin.test.tsdiffbeforeafterboth--- a/tests/src/eth/collectionAdmin.test.ts
+++ b/tests/src/eth/collectionAdmin.test.ts
@@ -50,17 +50,18 @@
.to.be.eq(newAdmin.toLocaleLowerCase());
});
- itEth.skip('Add substrate admin by owner', async ({helper}) => {
+ itEth('Add cross account admin by owner', async ({helper, privateKey}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
+
const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-
- const [newAdmin] = await helper.arrange.createAccounts([10n], donor);
- await collectionEvm.methods.addCollectionAdminSubstrate(newAdmin.addressRaw).send();
+
+ const newAdmin = await privateKey('//Bob');
+ const newAdminCross = helper.ethCrossAccount.fromKeyringPair(newAdmin);
+ await collectionEvm.methods.addCollectionAdminCross(newAdminCross).send();
- const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]);
- expect(adminList[0].asSubstrate.toString().toLocaleLowerCase())
- .to.be.eq(newAdmin.address.toLocaleLowerCase());
+ const adminList = await helper.collection.getAdmins(collectionId);
+ expect(adminList).to.be.like([{Substrate: newAdmin.address}]);
});
itEth('Verify owner or admin', async ({helper}) => {
tests/src/eth/fractionalizer/Fractionalizer.soldiffbeforeafterboth--- a/tests/src/eth/fractionalizer/Fractionalizer.sol
+++ b/tests/src/eth/fractionalizer/Fractionalizer.sol
@@ -84,7 +84,11 @@
) external payable onlyOwner {
require(rftCollection == address(0), "RFT collection is already set");
address collectionHelpers = 0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F;
- rftCollection = CollectionHelpers(collectionHelpers).createRFTCollection{value: msg.value}(_name, _description, _tokenPrefix);
+ rftCollection = CollectionHelpers(collectionHelpers).createRFTCollection{value: msg.value}(
+ _name,
+ _description,
+ _tokenPrefix
+ );
emit RFTCollectionSet(rftCollection);
}
tests/src/eth/fungible.test.tsdiffbeforeafterboth--- a/tests/src/eth/fungible.test.ts
+++ b/tests/src/eth/fungible.test.ts
@@ -148,6 +148,49 @@
}
});
+ itEth('Can perform burnFromCross()', async ({helper, privateKey}) => {
+ const alice = await privateKey('//Alice');
+ const sender = await helper.eth.createAccountWithBalance(alice, 100n);
+
+ const collection = await helper.ft.mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'}, 0);
+
+ await collection.mint(alice, 200n, {Substrate: alice.address});
+ await collection.approveTokens(alice, {Ethereum: sender}, 100n);
+
+ const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(address, 'ft');
+
+ const fromBalanceBefore = await collection.getBalance({Substrate: alice.address});
+
+ const ownerCross = helper.ethCrossAccount.fromKeyringPair(alice);
+ const result = await contract.methods.burnFromCross(ownerCross, 49).send({from: sender});
+ const events = result.events;
+
+ expect(events).to.be.like({
+ Transfer: {
+ address: helper.ethAddress.fromCollectionId(collection.collectionId),
+ event: 'Transfer',
+ returnValues: {
+ from: helper.address.substrateToEth(alice.address),
+ to: '0x0000000000000000000000000000000000000000',
+ value: '49',
+ },
+ },
+ Approval: {
+ address: helper.ethAddress.fromCollectionId(collection.collectionId),
+ returnValues: {
+ owner: helper.address.substrateToEth(alice.address),
+ spender: sender,
+ value: '51',
+ },
+ event: 'Approval',
+ },
+ });
+
+ const fromBalanceAfter = await collection.getBalance({Substrate: alice.address});
+ expect(fromBalanceBefore - fromBalanceAfter).to.be.eq(49n);
+ });
+
itEth('Can perform transferFrom()', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
const spender = await helper.eth.createAccountWithBalance(donor);
@@ -216,6 +259,54 @@
expect(+balance).to.equal(50);
}
});
+
+ itEth('Can perform transferFromCross()', async ({helper, privateKey}) => {
+ const alice = await privateKey('//Alice');
+ const sender = await helper.eth.createAccountWithBalance(alice, 100n);
+
+ const collection = await helper.ft.mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'}, 0);
+
+ const receiver = helper.eth.createAccount();
+
+ await collection.mint(alice, 200n, {Substrate: alice.address});
+ await collection.approveTokens(alice, {Ethereum: sender}, 100n);
+
+ const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(address, 'ft');
+
+ const from = helper.ethCrossAccount.fromKeyringPair(alice);
+ const to = helper.ethCrossAccount.fromAddress(receiver);
+
+ const fromBalanceBefore = await collection.getBalance({Substrate: alice.address});
+ const toBalanceBefore = await collection.getBalance({Ethereum: receiver});
+
+ const result = await contract.methods.transferFromCross(from, to, 51).send({from: sender});
+
+ expect(result.events).to.be.like({
+ Transfer: {
+ address,
+ event: 'Transfer',
+ returnValues: {
+ from: helper.address.substrateToEth(alice.address),
+ to: receiver,
+ value: '51',
+ },
+ },
+ Approval: {
+ address,
+ event: 'Approval',
+ returnValues: {
+ owner: helper.address.substrateToEth(alice.address),
+ spender: sender,
+ value: '49',
+ },
+ }});
+
+ const fromBalanceAfter = await collection.getBalance({Substrate: alice.address});
+ expect(fromBalanceBefore - fromBalanceAfter).to.be.eq(51n);
+ const toBalanceAfter = await collection.getBalance({Ethereum: receiver});
+ expect(toBalanceAfter - toBalanceBefore).to.be.eq(51n);
+ });
});
describe('Fungible: Fees', () => {
@@ -362,4 +453,44 @@
expect(event.returnValues.to).to.be.equal(receiver);
expect(event.returnValues.value).to.be.equal('51');
});
+
+ itEth('Events emitted for transferFromCross()', async ({helper, privateKey}) => {
+ const alice = await privateKey('//Alice');
+ const sender = await helper.eth.createAccountWithBalance(alice, 100n);
+
+ const collection = await helper.ft.mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'}, 0);
+
+ const receiver = helper.eth.createAccount();
+
+ await collection.mint(alice, 200n, {Substrate: alice.address});
+ await collection.approveTokens(alice, {Ethereum: sender}, 100n);
+
+ const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(address, 'ft');
+
+ const from = helper.ethCrossAccount.fromKeyringPair(alice);
+ const to = helper.ethCrossAccount.fromAddress(receiver);
+
+ const result = await contract.methods.transferFromCross(from, to, 51).send({from: sender});
+
+ expect(result.events).to.be.like({
+ Transfer: {
+ address,
+ event: 'Transfer',
+ returnValues: {
+ from: helper.address.substrateToEth(alice.address),
+ to: receiver,
+ value: '51',
+ },
+ },
+ Approval: {
+ address,
+ event: 'Approval',
+ returnValues: {
+ owner: helper.address.substrateToEth(alice.address),
+ spender: sender,
+ value: '49',
+ },
+ }});
+ });
});
tests/src/eth/fungibleAbi.jsondiffbeforeafterboth--- a/tests/src/eth/fungibleAbi.json
+++ b/tests/src/eth/fungibleAbi.json
@@ -60,6 +60,23 @@
},
{
"inputs": [
+ {
+ "components": [
+ { "internalType": "address", "name": "field_0", "type": "address" },
+ { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+ ],
+ "internalType": "struct Tuple6",
+ "name": "newAdmin",
+ "type": "tuple"
+ }
+ ],
+ "name": "addCollectionAdminCross",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
{ "internalType": "address", "name": "user", "type": "address" }
],
"name": "addToCollectionAllowList",
@@ -69,6 +86,23 @@
},
{
"inputs": [
+ {
+ "components": [
+ { "internalType": "address", "name": "field_0", "type": "address" },
+ { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+ ],
+ "internalType": "struct Tuple6",
+ "name": "user",
+ "type": "tuple"
+ }
+ ],
+ "name": "addToCollectionAllowListCross",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
{ "internalType": "address", "name": "owner", "type": "address" },
{ "internalType": "address", "name": "spender", "type": "address" }
],
@@ -98,6 +132,24 @@
},
{
"inputs": [
+ {
+ "components": [
+ { "internalType": "address", "name": "field_0", "type": "address" },
+ { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+ ],
+ "internalType": "struct Tuple6",
+ "name": "spender",
+ "type": "tuple"
+ },
+ { "internalType": "uint256", "name": "amount", "type": "uint256" }
+ ],
+ "name": "approveCross",
+ "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
{ "internalType": "address", "name": "owner", "type": "address" }
],
"name": "balanceOf",
@@ -117,6 +169,24 @@
},
{
"inputs": [
+ {
+ "components": [
+ { "internalType": "address", "name": "field_0", "type": "address" },
+ { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+ ],
+ "internalType": "struct Tuple6",
+ "name": "from",
+ "type": "tuple"
+ },
+ { "internalType": "uint256", "name": "amount", "type": "uint256" }
+ ],
+ "name": "burnFromCross",
+ "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
{ "internalType": "address", "name": "newOwner", "type": "address" }
],
"name": "changeCollectionOwner",
@@ -228,6 +298,23 @@
},
{
"inputs": [
+ {
+ "components": [
+ { "internalType": "address", "name": "field_0", "type": "address" },
+ { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+ ],
+ "internalType": "struct Tuple6",
+ "name": "user",
+ "type": "tuple"
+ }
+ ],
+ "name": "isOwnerOrAdminCross",
+ "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
{ "internalType": "address", "name": "to", "type": "address" },
{ "internalType": "uint256", "name": "amount", "type": "uint256" }
],
@@ -270,6 +357,23 @@
"type": "function"
},
{
+ "inputs": [
+ {
+ "components": [
+ { "internalType": "address", "name": "field_0", "type": "address" },
+ { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+ ],
+ "internalType": "struct Tuple6",
+ "name": "admin",
+ "type": "tuple"
+ }
+ ],
+ "name": "removeCollectionAdminCross",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
"inputs": [],
"name": "removeCollectionSponsor",
"outputs": [],
@@ -286,6 +390,23 @@
"type": "function"
},
{
+ "inputs": [
+ {
+ "components": [
+ { "internalType": "address", "name": "field_0", "type": "address" },
+ { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+ ],
+ "internalType": "struct Tuple6",
+ "name": "user",
+ "type": "tuple"
+ }
+ ],
+ "name": "removeFromCollectionAllowListCross",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
"inputs": [{ "internalType": "uint8", "name": "mode", "type": "uint8" }],
"name": "setCollectionAccess",
"outputs": [],
@@ -361,6 +482,40 @@
},
{
"inputs": [
+ {
+ "components": [
+ { "internalType": "address", "name": "field_0", "type": "address" },
+ { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+ ],
+ "internalType": "struct Tuple6",
+ "name": "sponsor",
+ "type": "tuple"
+ }
+ ],
+ "name": "setCollectionSponsorCross",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "components": [
+ { "internalType": "address", "name": "field_0", "type": "address" },
+ { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+ ],
+ "internalType": "struct Tuple6",
+ "name": "newOwner",
+ "type": "tuple"
+ }
+ ],
+ "name": "setOwnerCross",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
{ "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }
],
"name": "supportsInterface",
@@ -404,6 +559,33 @@
"type": "function"
},
{
+ "inputs": [
+ {
+ "components": [
+ { "internalType": "address", "name": "field_0", "type": "address" },
+ { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+ ],
+ "internalType": "struct Tuple6",
+ "name": "from",
+ "type": "tuple"
+ },
+ {
+ "components": [
+ { "internalType": "address", "name": "field_0", "type": "address" },
+ { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+ ],
+ "internalType": "struct Tuple6",
+ "name": "to",
+ "type": "tuple"
+ },
+ { "internalType": "uint256", "name": "amount", "type": "uint256" }
+ ],
+ "name": "transferFromCross",
+ "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
"inputs": [],
"name": "uniqueCollectionType",
"outputs": [{ "internalType": "string", "name": "", "type": "string" }],
tests/src/eth/nonFungible.test.tsdiffbeforeafterboth--- a/tests/src/eth/nonFungible.test.ts
+++ b/tests/src/eth/nonFungible.test.ts
@@ -246,6 +246,64 @@
}
});
+ itEth('Can perform burnFromCross()', async ({helper, privateKey}) => {
+ const alice = await privateKey('//Alice');
+ const collection = await helper.nft.mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'});
+
+ const owner = await privateKey('//Bob');
+ const spender = await helper.eth.createAccountWithBalance(alice, 100n);
+
+ const token = await collection.mintToken(alice, {Substrate: owner.address});
+
+ const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(address, 'nft');
+
+ {
+ await token.approve(owner, {Ethereum: spender});
+ const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);
+ const result = await contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender});
+ const events = result.events.Transfer;
+
+ expect(events).to.be.like({
+ address,
+ event: 'Transfer',
+ returnValues: {
+ from: helper.address.substrateToEth(owner.address),
+ to: '0x0000000000000000000000000000000000000000',
+ tokenId: token.tokenId.toString(),
+ },
+ });
+ }
+ });
+
+ itEth('Can perform approveCross()', async ({helper, privateKey}) => {
+ const alice = await privateKey('//Alice');
+ const collection = await helper.nft.mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'});
+
+ const owner = await helper.eth.createAccountWithBalance(alice, 100n);
+ const receiver = await privateKey('//Charlie');
+
+ const token = await collection.mintToken(alice, {Ethereum: owner});
+
+ const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(address, 'nft');
+
+ {
+ const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);
+ const result = await contract.methods.approveCross(recieverCross, token.tokenId).send({from: owner});
+ const event = result.events.Approval;
+ expect(event).to.be.like({
+ address: helper.ethAddress.fromCollectionId(collection.collectionId),
+ event: 'Approval',
+ returnValues: {
+ owner,
+ approved: helper.address.substrateToEth(receiver.address),
+ tokenId: token.tokenId.toString(),
+ },
+ });
+ }
+ });
+
itEth('Can perform transferFrom()', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
const spender = await helper.eth.createAccountWithBalance(donor);
@@ -351,6 +409,40 @@
expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
});
+ itEth('Can perform transferFromCross()', async ({helper, privateKey}) => {
+ const alice = await privateKey('//Alice');
+ const collection = await helper.nft.mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'});
+
+ const owner = await privateKey('//Bob');
+ const spender = await helper.eth.createAccountWithBalance(alice, 100n);
+ const receiver = await privateKey('//Charlie');
+
+ const token = await collection.mintToken(alice, {Substrate: owner.address});
+
+ const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(address, 'nft');
+
+ await token.approve(owner, {Ethereum: spender});
+
+ {
+ const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);
+ const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);
+ const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender});
+ const event = result.events.Transfer;
+ expect(event).to.be.like({
+ address: helper.ethAddress.fromCollectionId(collection.collectionId),
+ event: 'Transfer',
+ returnValues: {
+ from: helper.address.substrateToEth(owner.address),
+ to: helper.address.substrateToEth(receiver.address),
+ tokenId: token.tokenId.toString(),
+ },
+ });
+ }
+
+ expect(await token.getOwner()).to.be.like({Substrate: receiver.address});
+ });
+
itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
const receiver = helper.eth.createAccount();
tests/src/eth/nonFungibleAbi.jsondiffbeforeafterboth1[2 {3 "anonymous": false,4 "inputs": [5 {6 "indexed": true,7 "internalType": "address",8 "name": "owner",9 "type": "address"10 },11 {12 "indexed": true,13 "internalType": "address",14 "name": "approved",15 "type": "address"16 },17 {18 "indexed": true,19 "internalType": "uint256",20 "name": "tokenId",21 "type": "uint256"22 }23 ],24 "name": "Approval",25 "type": "event"26 },27 {28 "anonymous": false,29 "inputs": [30 {31 "indexed": true,32 "internalType": "address",33 "name": "owner",34 "type": "address"35 },36 {37 "indexed": true,38 "internalType": "address",39 "name": "operator",40 "type": "address"41 },42 {43 "indexed": false,44 "internalType": "bool",45 "name": "approved",46 "type": "bool"47 }48 ],49 "name": "ApprovalForAll",50 "type": "event"51 },52 {53 "anonymous": false,54 "inputs": [],55 "name": "MintingFinished",56 "type": "event"57 },58 {59 "anonymous": false,60 "inputs": [61 {62 "indexed": true,63 "internalType": "address",64 "name": "from",65 "type": "address"66 },67 {68 "indexed": true,69 "internalType": "address",70 "name": "to",71 "type": "address"72 },73 {74 "indexed": true,75 "internalType": "uint256",76 "name": "tokenId",77 "type": "uint256"78 }79 ],80 "name": "Transfer",81 "type": "event"82 },83 {84 "inputs": [85 { "internalType": "address", "name": "newAdmin", "type": "address" }86 ],87 "name": "addCollectionAdmin",88 "outputs": [],89 "stateMutability": "nonpayable",90 "type": "function"91 },92 {93 "inputs": [94 { "internalType": "address", "name": "user", "type": "address" }95 ],96 "name": "addToCollectionAllowList",97 "outputs": [],98 "stateMutability": "nonpayable",99 "type": "function"100 },101 {102 "inputs": [103 { "internalType": "address", "name": "user", "type": "address" }104 ],105 "name": "allowed",106 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],107 "stateMutability": "view",108 "type": "function"109 },110 {111 "inputs": [112 { "internalType": "address", "name": "approved", "type": "address" },113 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }114 ],115 "name": "approve",116 "outputs": [],117 "stateMutability": "nonpayable",118 "type": "function"119 },120 {121 "inputs": [122 { "internalType": "address", "name": "owner", "type": "address" }123 ],124 "name": "balanceOf",125 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],126 "stateMutability": "view",127 "type": "function"128 },129 {130 "inputs": [131 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }132 ],133 "name": "burn",134 "outputs": [],135 "stateMutability": "nonpayable",136 "type": "function"137 },138 {139 "inputs": [140 { "internalType": "address", "name": "from", "type": "address" },141 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }142 ],143 "name": "burnFrom",144 "outputs": [],145 "stateMutability": "nonpayable",146 "type": "function"147 },148 {149 "inputs": [150 { "internalType": "address", "name": "newOwner", "type": "address" }151 ],152 "name": "changeCollectionOwner",153 "outputs": [],154 "stateMutability": "nonpayable",155 "type": "function"156 },157 {158 "inputs": [],159 "name": "collectionAdmins",160 "outputs": [161 {162 "components": [163 { "internalType": "address", "name": "field_0", "type": "address" },164 { "internalType": "uint256", "name": "field_1", "type": "uint256" }165 ],166 "internalType": "struct Tuple17[]",167 "name": "",168 "type": "tuple[]"169 }170 ],171 "stateMutability": "view",172 "type": "function"173 },174 {175 "inputs": [],176 "name": "collectionOwner",177 "outputs": [178 {179 "components": [180 { "internalType": "address", "name": "field_0", "type": "address" },181 { "internalType": "uint256", "name": "field_1", "type": "uint256" }182 ],183 "internalType": "struct Tuple17",184 "name": "",185 "type": "tuple"186 }187 ],188 "stateMutability": "view",189 "type": "function"190 },191 {192 "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],193 "name": "collectionProperty",194 "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],195 "stateMutability": "view",196 "type": "function"197 },198 {199 "inputs": [],200 "name": "collectionSponsor",201 "outputs": [202 {203 "components": [204 { "internalType": "address", "name": "field_0", "type": "address" },205 { "internalType": "uint256", "name": "field_1", "type": "uint256" }206 ],207 "internalType": "struct Tuple17",208 "name": "",209 "type": "tuple"210 }211 ],212 "stateMutability": "view",213 "type": "function"214 },215 {216 "inputs": [],217 "name": "confirmCollectionSponsorship",218 "outputs": [],219 "stateMutability": "nonpayable",220 "type": "function"221 },222 {223 "inputs": [],224 "name": "contractAddress",225 "outputs": [{ "internalType": "address", "name": "", "type": "address" }],226 "stateMutability": "view",227 "type": "function"228 },229 {230 "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],231 "name": "deleteCollectionProperty",232 "outputs": [],233 "stateMutability": "nonpayable",234 "type": "function"235 },236 {237 "inputs": [238 { "internalType": "uint256", "name": "tokenId", "type": "uint256" },239 { "internalType": "string", "name": "key", "type": "string" }240 ],241 "name": "deleteProperty",242 "outputs": [],243 "stateMutability": "nonpayable",244 "type": "function"245 },246 {247 "inputs": [],248 "name": "finishMinting",249 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],250 "stateMutability": "nonpayable",251 "type": "function"252 },253 {254 "inputs": [255 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }256 ],257 "name": "getApproved",258 "outputs": [{ "internalType": "address", "name": "", "type": "address" }],259 "stateMutability": "view",260 "type": "function"261 },262 {263 "inputs": [],264 "name": "hasCollectionPendingSponsor",265 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],266 "stateMutability": "view",267 "type": "function"268 },269 {270 "inputs": [271 { "internalType": "address", "name": "owner", "type": "address" },272 { "internalType": "address", "name": "operator", "type": "address" }273 ],274 "name": "isApprovedForAll",275 "outputs": [{ "internalType": "address", "name": "", "type": "address" }],276 "stateMutability": "view",277 "type": "function"278 },279 {280 "inputs": [281 { "internalType": "address", "name": "user", "type": "address" }282 ],283 "name": "isOwnerOrAdmin",284 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],285 "stateMutability": "view",286 "type": "function"287 },288 {289 "inputs": [{ "internalType": "address", "name": "to", "type": "address" }],290 "name": "mint",291 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],292 "stateMutability": "nonpayable",293 "type": "function"294 },295 {296 "inputs": [297 { "internalType": "address", "name": "to", "type": "address" },298 { "internalType": "string", "name": "tokenUri", "type": "string" }299 ],300 "name": "mintWithTokenURI",301 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],302 "stateMutability": "nonpayable",303 "type": "function"304 },305 {306 "inputs": [],307 "name": "mintingFinished",308 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],309 "stateMutability": "view",310 "type": "function"311 },312 {313 "inputs": [],314 "name": "name",315 "outputs": [{ "internalType": "string", "name": "", "type": "string" }],316 "stateMutability": "view",317 "type": "function"318 },319 {320 "inputs": [],321 "name": "nextTokenId",322 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],323 "stateMutability": "view",324 "type": "function"325 },326 {327 "inputs": [328 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }329 ],330 "name": "ownerOf",331 "outputs": [{ "internalType": "address", "name": "", "type": "address" }],332 "stateMutability": "view",333 "type": "function"334 },335 {336 "inputs": [337 { "internalType": "uint256", "name": "tokenId", "type": "uint256" },338 { "internalType": "string", "name": "key", "type": "string" }339 ],340 "name": "property",341 "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],342 "stateMutability": "view",343 "type": "function"344 },345 {346 "inputs": [347 { "internalType": "address", "name": "admin", "type": "address" }348 ],349 "name": "removeCollectionAdmin",350 "outputs": [],351 "stateMutability": "nonpayable",352 "type": "function"353 },354 {355 "inputs": [],356 "name": "removeCollectionSponsor",357 "outputs": [],358 "stateMutability": "nonpayable",359 "type": "function"360 },361 {362 "inputs": [363 { "internalType": "address", "name": "user", "type": "address" }364 ],365 "name": "removeFromCollectionAllowList",366 "outputs": [],367 "stateMutability": "nonpayable",368 "type": "function"369 },370 {371 "inputs": [372 { "internalType": "address", "name": "from", "type": "address" },373 { "internalType": "address", "name": "to", "type": "address" },374 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }375 ],376 "name": "safeTransferFrom",377 "outputs": [],378 "stateMutability": "nonpayable",379 "type": "function"380 },381 {382 "inputs": [383 { "internalType": "address", "name": "from", "type": "address" },384 { "internalType": "address", "name": "to", "type": "address" },385 { "internalType": "uint256", "name": "tokenId", "type": "uint256" },386 { "internalType": "bytes", "name": "data", "type": "bytes" }387 ],388 "name": "safeTransferFrom",389 "outputs": [],390 "stateMutability": "nonpayable",391 "type": "function"392 },393 {394 "inputs": [395 { "internalType": "address", "name": "operator", "type": "address" },396 { "internalType": "bool", "name": "approved", "type": "bool" }397 ],398 "name": "setApprovalForAll",399 "outputs": [],400 "stateMutability": "nonpayable",401 "type": "function"402 },403 {404 "inputs": [{ "internalType": "uint8", "name": "mode", "type": "uint8" }],405 "name": "setCollectionAccess",406 "outputs": [],407 "stateMutability": "nonpayable",408 "type": "function"409 },410 {411 "inputs": [412 { "internalType": "string", "name": "limit", "type": "string" },413 { "internalType": "uint32", "name": "value", "type": "uint32" }414 ],415 "name": "setCollectionLimit",416 "outputs": [],417 "stateMutability": "nonpayable",418 "type": "function"419 },420 {421 "inputs": [422 { "internalType": "string", "name": "limit", "type": "string" },423 { "internalType": "bool", "name": "value", "type": "bool" }424 ],425 "name": "setCollectionLimit",426 "outputs": [],427 "stateMutability": "nonpayable",428 "type": "function"429 },430 {431 "inputs": [{ "internalType": "bool", "name": "mode", "type": "bool" }],432 "name": "setCollectionMintMode",433 "outputs": [],434 "stateMutability": "nonpayable",435 "type": "function"436 },437 {438 "inputs": [{ "internalType": "bool", "name": "enable", "type": "bool" }],439 "name": "setCollectionNesting",440 "outputs": [],441 "stateMutability": "nonpayable",442 "type": "function"443 },444 {445 "inputs": [446 { "internalType": "bool", "name": "enable", "type": "bool" },447 {448 "internalType": "address[]",449 "name": "collections",450 "type": "address[]"451 }452 ],453 "name": "setCollectionNesting",454 "outputs": [],455 "stateMutability": "nonpayable",456 "type": "function"457 },458 {459 "inputs": [460 { "internalType": "string", "name": "key", "type": "string" },461 { "internalType": "bytes", "name": "value", "type": "bytes" }462 ],463 "name": "setCollectionProperty",464 "outputs": [],465 "stateMutability": "nonpayable",466 "type": "function"467 },468 {469 "inputs": [470 { "internalType": "address", "name": "sponsor", "type": "address" }471 ],472 "name": "setCollectionSponsor",473 "outputs": [],474 "stateMutability": "nonpayable",475 "type": "function"476 },477 {478 "inputs": [479 { "internalType": "uint256", "name": "tokenId", "type": "uint256" },480 { "internalType": "string", "name": "key", "type": "string" },481 { "internalType": "bytes", "name": "value", "type": "bytes" }482 ],483 "name": "setProperty",484 "outputs": [],485 "stateMutability": "nonpayable",486 "type": "function"487 },488 {489 "inputs": [490 { "internalType": "string", "name": "key", "type": "string" },491 { "internalType": "bool", "name": "isMutable", "type": "bool" },492 { "internalType": "bool", "name": "collectionAdmin", "type": "bool" },493 { "internalType": "bool", "name": "tokenOwner", "type": "bool" }494 ],495 "name": "setTokenPropertyPermission",496 "outputs": [],497 "stateMutability": "nonpayable",498 "type": "function"499 },500 {501 "inputs": [502 { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }503 ],504 "name": "supportsInterface",505 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],506 "stateMutability": "view",507 "type": "function"508 },509 {510 "inputs": [],511 "name": "symbol",512 "outputs": [{ "internalType": "string", "name": "", "type": "string" }],513 "stateMutability": "view",514 "type": "function"515 },516 {517 "inputs": [518 { "internalType": "uint256", "name": "index", "type": "uint256" }519 ],520 "name": "tokenByIndex",521 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],522 "stateMutability": "view",523 "type": "function"524 },525 {526 "inputs": [527 { "internalType": "address", "name": "owner", "type": "address" },528 { "internalType": "uint256", "name": "index", "type": "uint256" }529 ],530 "name": "tokenOfOwnerByIndex",531 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],532 "stateMutability": "view",533 "type": "function"534 },535 {536 "inputs": [537 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }538 ],539 "name": "tokenURI",540 "outputs": [{ "internalType": "string", "name": "", "type": "string" }],541 "stateMutability": "view",542 "type": "function"543 },544 {545 "inputs": [],546 "name": "totalSupply",547 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],548 "stateMutability": "view",549 "type": "function"550 },551 {552 "inputs": [553 { "internalType": "address", "name": "to", "type": "address" },554 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }555 ],556 "name": "transfer",557 "outputs": [],558 "stateMutability": "nonpayable",559 "type": "function"560 },561 {562 "inputs": [563 { "internalType": "address", "name": "from", "type": "address" },564 { "internalType": "address", "name": "to", "type": "address" },565 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }566 ],567 "name": "transferFrom",568 "outputs": [],569 "stateMutability": "nonpayable",570 "type": "function"571 },572 {573 "inputs": [],574 "name": "uniqueCollectionType",575 "outputs": [{ "internalType": "string", "name": "", "type": "string" }],576 "stateMutability": "view",577 "type": "function"578 }579]1[2 {3 "anonymous": false,4 "inputs": [5 {6 "indexed": true,7 "internalType": "address",8 "name": "owner",9 "type": "address"10 },11 {12 "indexed": true,13 "internalType": "address",14 "name": "approved",15 "type": "address"16 },17 {18 "indexed": true,19 "internalType": "uint256",20 "name": "tokenId",21 "type": "uint256"22 }23 ],24 "name": "Approval",25 "type": "event"26 },27 {28 "anonymous": false,29 "inputs": [30 {31 "indexed": true,32 "internalType": "address",33 "name": "owner",34 "type": "address"35 },36 {37 "indexed": true,38 "internalType": "address",39 "name": "operator",40 "type": "address"41 },42 {43 "indexed": false,44 "internalType": "bool",45 "name": "approved",46 "type": "bool"47 }48 ],49 "name": "ApprovalForAll",50 "type": "event"51 },52 {53 "anonymous": false,54 "inputs": [],55 "name": "MintingFinished",56 "type": "event"57 },58 {59 "anonymous": false,60 "inputs": [61 {62 "indexed": true,63 "internalType": "address",64 "name": "from",65 "type": "address"66 },67 {68 "indexed": true,69 "internalType": "address",70 "name": "to",71 "type": "address"72 },73 {74 "indexed": true,75 "internalType": "uint256",76 "name": "tokenId",77 "type": "uint256"78 }79 ],80 "name": "Transfer",81 "type": "event"82 },83 {84 "inputs": [85 { "internalType": "address", "name": "newAdmin", "type": "address" }86 ],87 "name": "addCollectionAdmin",88 "outputs": [],89 "stateMutability": "nonpayable",90 "type": "function"91 },92 {93 "inputs": [94 {95 "components": [96 { "internalType": "address", "name": "field_0", "type": "address" },97 { "internalType": "uint256", "name": "field_1", "type": "uint256" }98 ],99 "internalType": "struct Tuple6",100 "name": "newAdmin",101 "type": "tuple"102 }103 ],104 "name": "addCollectionAdminCross",105 "outputs": [],106 "stateMutability": "nonpayable",107 "type": "function"108 },109 {110 "inputs": [111 { "internalType": "address", "name": "user", "type": "address" }112 ],113 "name": "addToCollectionAllowList",114 "outputs": [],115 "stateMutability": "nonpayable",116 "type": "function"117 },118 {119 "inputs": [120 {121 "components": [122 { "internalType": "address", "name": "field_0", "type": "address" },123 { "internalType": "uint256", "name": "field_1", "type": "uint256" }124 ],125 "internalType": "struct Tuple6",126 "name": "user",127 "type": "tuple"128 }129 ],130 "name": "addToCollectionAllowListCross",131 "outputs": [],132 "stateMutability": "nonpayable",133 "type": "function"134 },135 {136 "inputs": [137 { "internalType": "address", "name": "user", "type": "address" }138 ],139 "name": "allowed",140 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],141 "stateMutability": "view",142 "type": "function"143 },144 {145 "inputs": [146 { "internalType": "address", "name": "approved", "type": "address" },147 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }148 ],149 "name": "approve",150 "outputs": [],151 "stateMutability": "nonpayable",152 "type": "function"153 },154 {155 "inputs": [156 {157 "components": [158 { "internalType": "address", "name": "field_0", "type": "address" },159 { "internalType": "uint256", "name": "field_1", "type": "uint256" }160 ],161 "internalType": "struct Tuple6",162 "name": "approved",163 "type": "tuple"164 },165 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }166 ],167 "name": "approveCross",168 "outputs": [],169 "stateMutability": "nonpayable",170 "type": "function"171 },172 {173 "inputs": [174 { "internalType": "address", "name": "owner", "type": "address" }175 ],176 "name": "balanceOf",177 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],178 "stateMutability": "view",179 "type": "function"180 },181 {182 "inputs": [183 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }184 ],185 "name": "burn",186 "outputs": [],187 "stateMutability": "nonpayable",188 "type": "function"189 },190 {191 "inputs": [192 { "internalType": "address", "name": "from", "type": "address" },193 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }194 ],195 "name": "burnFrom",196 "outputs": [],197 "stateMutability": "nonpayable",198 "type": "function"199 },200 {201 "inputs": [202 {203 "components": [204 { "internalType": "address", "name": "field_0", "type": "address" },205 { "internalType": "uint256", "name": "field_1", "type": "uint256" }206 ],207 "internalType": "struct Tuple6",208 "name": "from",209 "type": "tuple"210 },211 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }212 ],213 "name": "burnFromCross",214 "outputs": [],215 "stateMutability": "nonpayable",216 "type": "function"217 },218 {219 "inputs": [220 { "internalType": "address", "name": "newOwner", "type": "address" }221 ],222 "name": "changeCollectionOwner",223 "outputs": [],224 "stateMutability": "nonpayable",225 "type": "function"226 },227 {228 "inputs": [],229 "name": "collectionAdmins",230 "outputs": [231 {232 "components": [233 { "internalType": "address", "name": "field_0", "type": "address" },234 { "internalType": "uint256", "name": "field_1", "type": "uint256" }235 ],236 "internalType": "struct Tuple6[]",237 "name": "",238 "type": "tuple[]"239 }240 ],241 "stateMutability": "view",242 "type": "function"243 },244 {245 "inputs": [],246 "name": "collectionOwner",247 "outputs": [248 {249 "components": [250 { "internalType": "address", "name": "field_0", "type": "address" },251 { "internalType": "uint256", "name": "field_1", "type": "uint256" }252 ],253 "internalType": "struct Tuple6",254 "name": "",255 "type": "tuple"256 }257 ],258 "stateMutability": "view",259 "type": "function"260 },261 {262 "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],263 "name": "collectionProperty",264 "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],265 "stateMutability": "view",266 "type": "function"267 },268 {269 "inputs": [],270 "name": "collectionSponsor",271 "outputs": [272 {273 "components": [274 { "internalType": "address", "name": "field_0", "type": "address" },275 { "internalType": "uint256", "name": "field_1", "type": "uint256" }276 ],277 "internalType": "struct Tuple6",278 "name": "",279 "type": "tuple"280 }281 ],282 "stateMutability": "view",283 "type": "function"284 },285 {286 "inputs": [],287 "name": "confirmCollectionSponsorship",288 "outputs": [],289 "stateMutability": "nonpayable",290 "type": "function"291 },292 {293 "inputs": [],294 "name": "contractAddress",295 "outputs": [{ "internalType": "address", "name": "", "type": "address" }],296 "stateMutability": "view",297 "type": "function"298 },299 {300 "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],301 "name": "deleteCollectionProperty",302 "outputs": [],303 "stateMutability": "nonpayable",304 "type": "function"305 },306 {307 "inputs": [308 { "internalType": "uint256", "name": "tokenId", "type": "uint256" },309 { "internalType": "string", "name": "key", "type": "string" }310 ],311 "name": "deleteProperty",312 "outputs": [],313 "stateMutability": "nonpayable",314 "type": "function"315 },316 {317 "inputs": [],318 "name": "finishMinting",319 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],320 "stateMutability": "nonpayable",321 "type": "function"322 },323 {324 "inputs": [325 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }326 ],327 "name": "getApproved",328 "outputs": [{ "internalType": "address", "name": "", "type": "address" }],329 "stateMutability": "view",330 "type": "function"331 },332 {333 "inputs": [],334 "name": "hasCollectionPendingSponsor",335 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],336 "stateMutability": "view",337 "type": "function"338 },339 {340 "inputs": [341 { "internalType": "address", "name": "owner", "type": "address" },342 { "internalType": "address", "name": "operator", "type": "address" }343 ],344 "name": "isApprovedForAll",345 "outputs": [{ "internalType": "address", "name": "", "type": "address" }],346 "stateMutability": "view",347 "type": "function"348 },349 {350 "inputs": [351 { "internalType": "address", "name": "user", "type": "address" }352 ],353 "name": "isOwnerOrAdmin",354 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],355 "stateMutability": "view",356 "type": "function"357 },358 {359 "inputs": [360 {361 "components": [362 { "internalType": "address", "name": "field_0", "type": "address" },363 { "internalType": "uint256", "name": "field_1", "type": "uint256" }364 ],365 "internalType": "struct Tuple6",366 "name": "user",367 "type": "tuple"368 }369 ],370 "name": "isOwnerOrAdminCross",371 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],372 "stateMutability": "view",373 "type": "function"374 },375 {376 "inputs": [{ "internalType": "address", "name": "to", "type": "address" }],377 "name": "mint",378 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],379 "stateMutability": "nonpayable",380 "type": "function"381 },382 {383 "inputs": [384 { "internalType": "address", "name": "to", "type": "address" },385 { "internalType": "string", "name": "tokenUri", "type": "string" }386 ],387 "name": "mintWithTokenURI",388 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],389 "stateMutability": "nonpayable",390 "type": "function"391 },392 {393 "inputs": [],394 "name": "mintingFinished",395 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],396 "stateMutability": "view",397 "type": "function"398 },399 {400 "inputs": [],401 "name": "name",402 "outputs": [{ "internalType": "string", "name": "", "type": "string" }],403 "stateMutability": "view",404 "type": "function"405 },406 {407 "inputs": [],408 "name": "nextTokenId",409 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],410 "stateMutability": "view",411 "type": "function"412 },413 {414 "inputs": [415 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }416 ],417 "name": "ownerOf",418 "outputs": [{ "internalType": "address", "name": "", "type": "address" }],419 "stateMutability": "view",420 "type": "function"421 },422 {423 "inputs": [424 { "internalType": "uint256", "name": "tokenId", "type": "uint256" },425 { "internalType": "string", "name": "key", "type": "string" }426 ],427 "name": "property",428 "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],429 "stateMutability": "view",430 "type": "function"431 },432 {433 "inputs": [434 { "internalType": "address", "name": "admin", "type": "address" }435 ],436 "name": "removeCollectionAdmin",437 "outputs": [],438 "stateMutability": "nonpayable",439 "type": "function"440 },441 {442 "inputs": [443 {444 "components": [445 { "internalType": "address", "name": "field_0", "type": "address" },446 { "internalType": "uint256", "name": "field_1", "type": "uint256" }447 ],448 "internalType": "struct Tuple6",449 "name": "admin",450 "type": "tuple"451 }452 ],453 "name": "removeCollectionAdminCross",454 "outputs": [],455 "stateMutability": "nonpayable",456 "type": "function"457 },458 {459 "inputs": [],460 "name": "removeCollectionSponsor",461 "outputs": [],462 "stateMutability": "nonpayable",463 "type": "function"464 },465 {466 "inputs": [467 { "internalType": "address", "name": "user", "type": "address" }468 ],469 "name": "removeFromCollectionAllowList",470 "outputs": [],471 "stateMutability": "nonpayable",472 "type": "function"473 },474 {475 "inputs": [476 {477 "components": [478 { "internalType": "address", "name": "field_0", "type": "address" },479 { "internalType": "uint256", "name": "field_1", "type": "uint256" }480 ],481 "internalType": "struct Tuple6",482 "name": "user",483 "type": "tuple"484 }485 ],486 "name": "removeFromCollectionAllowListCross",487 "outputs": [],488 "stateMutability": "nonpayable",489 "type": "function"490 },491 {492 "inputs": [493 { "internalType": "address", "name": "from", "type": "address" },494 { "internalType": "address", "name": "to", "type": "address" },495 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }496 ],497 "name": "safeTransferFrom",498 "outputs": [],499 "stateMutability": "nonpayable",500 "type": "function"501 },502 {503 "inputs": [504 { "internalType": "address", "name": "from", "type": "address" },505 { "internalType": "address", "name": "to", "type": "address" },506 { "internalType": "uint256", "name": "tokenId", "type": "uint256" },507 { "internalType": "bytes", "name": "data", "type": "bytes" }508 ],509 "name": "safeTransferFrom",510 "outputs": [],511 "stateMutability": "nonpayable",512 "type": "function"513 },514 {515 "inputs": [516 { "internalType": "address", "name": "operator", "type": "address" },517 { "internalType": "bool", "name": "approved", "type": "bool" }518 ],519 "name": "setApprovalForAll",520 "outputs": [],521 "stateMutability": "nonpayable",522 "type": "function"523 },524 {525 "inputs": [{ "internalType": "uint8", "name": "mode", "type": "uint8" }],526 "name": "setCollectionAccess",527 "outputs": [],528 "stateMutability": "nonpayable",529 "type": "function"530 },531 {532 "inputs": [533 { "internalType": "string", "name": "limit", "type": "string" },534 { "internalType": "uint32", "name": "value", "type": "uint32" }535 ],536 "name": "setCollectionLimit",537 "outputs": [],538 "stateMutability": "nonpayable",539 "type": "function"540 },541 {542 "inputs": [543 { "internalType": "string", "name": "limit", "type": "string" },544 { "internalType": "bool", "name": "value", "type": "bool" }545 ],546 "name": "setCollectionLimit",547 "outputs": [],548 "stateMutability": "nonpayable",549 "type": "function"550 },551 {552 "inputs": [{ "internalType": "bool", "name": "mode", "type": "bool" }],553 "name": "setCollectionMintMode",554 "outputs": [],555 "stateMutability": "nonpayable",556 "type": "function"557 },558 {559 "inputs": [{ "internalType": "bool", "name": "enable", "type": "bool" }],560 "name": "setCollectionNesting",561 "outputs": [],562 "stateMutability": "nonpayable",563 "type": "function"564 },565 {566 "inputs": [567 { "internalType": "bool", "name": "enable", "type": "bool" },568 {569 "internalType": "address[]",570 "name": "collections",571 "type": "address[]"572 }573 ],574 "name": "setCollectionNesting",575 "outputs": [],576 "stateMutability": "nonpayable",577 "type": "function"578 },579 {580 "inputs": [581 { "internalType": "string", "name": "key", "type": "string" },582 { "internalType": "bytes", "name": "value", "type": "bytes" }583 ],584 "name": "setCollectionProperty",585 "outputs": [],586 "stateMutability": "nonpayable",587 "type": "function"588 },589 {590 "inputs": [591 { "internalType": "address", "name": "sponsor", "type": "address" }592 ],593 "name": "setCollectionSponsor",594 "outputs": [],595 "stateMutability": "nonpayable",596 "type": "function"597 },598 {599 "inputs": [600 {601 "components": [602 { "internalType": "address", "name": "field_0", "type": "address" },603 { "internalType": "uint256", "name": "field_1", "type": "uint256" }604 ],605 "internalType": "struct Tuple6",606 "name": "sponsor",607 "type": "tuple"608 }609 ],610 "name": "setCollectionSponsorCross",611 "outputs": [],612 "stateMutability": "nonpayable",613 "type": "function"614 },615 {616 "inputs": [617 {618 "components": [619 { "internalType": "address", "name": "field_0", "type": "address" },620 { "internalType": "uint256", "name": "field_1", "type": "uint256" }621 ],622 "internalType": "struct Tuple6",623 "name": "newOwner",624 "type": "tuple"625 }626 ],627 "name": "setOwnerCross",628 "outputs": [],629 "stateMutability": "nonpayable",630 "type": "function"631 },632 {633 "inputs": [634 { "internalType": "uint256", "name": "tokenId", "type": "uint256" },635 { "internalType": "string", "name": "key", "type": "string" },636 { "internalType": "bytes", "name": "value", "type": "bytes" }637 ],638 "name": "setProperty",639 "outputs": [],640 "stateMutability": "nonpayable",641 "type": "function"642 },643 {644 "inputs": [645 { "internalType": "string", "name": "key", "type": "string" },646 { "internalType": "bool", "name": "isMutable", "type": "bool" },647 { "internalType": "bool", "name": "collectionAdmin", "type": "bool" },648 { "internalType": "bool", "name": "tokenOwner", "type": "bool" }649 ],650 "name": "setTokenPropertyPermission",651 "outputs": [],652 "stateMutability": "nonpayable",653 "type": "function"654 },655 {656 "inputs": [657 { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }658 ],659 "name": "supportsInterface",660 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],661 "stateMutability": "view",662 "type": "function"663 },664 {665 "inputs": [],666 "name": "symbol",667 "outputs": [{ "internalType": "string", "name": "", "type": "string" }],668 "stateMutability": "view",669 "type": "function"670 },671 {672 "inputs": [673 { "internalType": "uint256", "name": "index", "type": "uint256" }674 ],675 "name": "tokenByIndex",676 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],677 "stateMutability": "view",678 "type": "function"679 },680 {681 "inputs": [682 { "internalType": "address", "name": "owner", "type": "address" },683 { "internalType": "uint256", "name": "index", "type": "uint256" }684 ],685 "name": "tokenOfOwnerByIndex",686 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],687 "stateMutability": "view",688 "type": "function"689 },690 {691 "inputs": [692 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }693 ],694 "name": "tokenURI",695 "outputs": [{ "internalType": "string", "name": "", "type": "string" }],696 "stateMutability": "view",697 "type": "function"698 },699 {700 "inputs": [],701 "name": "totalSupply",702 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],703 "stateMutability": "view",704 "type": "function"705 },706 {707 "inputs": [708 { "internalType": "address", "name": "to", "type": "address" },709 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }710 ],711 "name": "transfer",712 "outputs": [],713 "stateMutability": "nonpayable",714 "type": "function"715 },716 {717 "inputs": [718 { "internalType": "address", "name": "from", "type": "address" },719 { "internalType": "address", "name": "to", "type": "address" },720 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }721 ],722 "name": "transferFrom",723 "outputs": [],724 "stateMutability": "nonpayable",725 "type": "function"726 },727 {728 "inputs": [729 {730 "components": [731 { "internalType": "address", "name": "field_0", "type": "address" },732 { "internalType": "uint256", "name": "field_1", "type": "uint256" }733 ],734 "internalType": "struct Tuple6",735 "name": "from",736 "type": "tuple"737 },738 {739 "components": [740 { "internalType": "address", "name": "field_0", "type": "address" },741 { "internalType": "uint256", "name": "field_1", "type": "uint256" }742 ],743 "internalType": "struct Tuple6",744 "name": "to",745 "type": "tuple"746 },747 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }748 ],749 "name": "transferFromCross",750 "outputs": [],751 "stateMutability": "nonpayable",752 "type": "function"753 },754 {755 "inputs": [],756 "name": "uniqueCollectionType",757 "outputs": [{ "internalType": "string", "name": "", "type": "string" }],758 "stateMutability": "view",759 "type": "function"760 }761]tests/src/eth/reFungible.test.tsdiffbeforeafterboth--- a/tests/src/eth/reFungible.test.ts
+++ b/tests/src/eth/reFungible.test.ts
@@ -227,6 +227,108 @@
}
});
+ itEth('Can perform burnFrom()', async ({helper, privateKey}) => {
+ const alice = await privateKey('//Alice');
+ const collection = await helper.rft.mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'});
+
+ const owner = await helper.eth.createAccountWithBalance(alice, 100n);
+ const spender = await helper.eth.createAccountWithBalance(alice, 100n);
+
+ const token = await collection.mintToken(alice, 100n, {Ethereum: owner});
+
+ const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(address, 'rft');
+
+ const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId);
+ const tokenContract = helper.ethNativeContract.rftToken(tokenAddress, owner);
+ await tokenContract.methods.repartition(15).send();
+ await tokenContract.methods.approve(spender, 15).send();
+
+ {
+ const result = await contract.methods.burnFrom(owner, token.tokenId).send({from: spender});
+ const event = result.events.Transfer;
+ expect(event).to.be.like({
+ address: helper.ethAddress.fromCollectionId(collection.collectionId),
+ event: 'Transfer',
+ returnValues: {
+ from: owner,
+ to: '0x0000000000000000000000000000000000000000',
+ tokenId: token.tokenId.toString(),
+ },
+ });
+ }
+
+ expect(await collection.getTokenBalance(token.tokenId, {Ethereum: owner})).to.be.eq(0n);
+ });
+
+ itEth('Can perform burnFromCross()', async ({helper, privateKey}) => {
+ const alice = await privateKey('//Alice');
+ const collection = await helper.rft.mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'});
+
+ const owner = await privateKey('//Bob');
+ const spender = await helper.eth.createAccountWithBalance(alice, 100n);
+
+ const token = await collection.mintToken(alice, 100n, {Substrate: owner.address});
+
+ const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(address, 'rft');
+
+ await token.repartition(owner, 15n);
+ await token.approve(owner, {Ethereum: spender}, 15n);
+
+ {
+ const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);
+ const result = await contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender});
+ const event = result.events.Transfer;
+ expect(event).to.be.like({
+ address: helper.ethAddress.fromCollectionId(collection.collectionId),
+ event: 'Transfer',
+ returnValues: {
+ from: helper.address.substrateToEth(owner.address),
+ to: '0x0000000000000000000000000000000000000000',
+ tokenId: token.tokenId.toString(),
+ },
+ });
+ }
+
+ expect(await collection.getTokenBalance(token.tokenId, {Substrate: owner.address})).to.be.eq(0n);
+ });
+
+ itEth('Can perform transferFromCross()', async ({helper, privateKey}) => {
+ const alice = await privateKey('//Alice');
+ const collection = await helper.rft.mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'});
+
+ const owner = await privateKey('//Bob');
+ const spender = await helper.eth.createAccountWithBalance(alice, 100n);
+ const receiver = await privateKey('//Charlie');
+
+ const token = await collection.mintToken(alice, 100n, {Substrate: owner.address});
+
+ const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(address, 'rft');
+
+ await token.repartition(owner, 15n);
+ await token.approve(owner, {Ethereum: spender}, 15n);
+
+ {
+ const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);
+ const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);
+ const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender});
+ const event = result.events.Transfer;
+ expect(event).to.be.like({
+ address: helper.ethAddress.fromCollectionId(collection.collectionId),
+ event: 'Transfer',
+ returnValues: {
+ from: helper.address.substrateToEth(owner.address),
+ to: helper.address.substrateToEth(receiver.address),
+ tokenId: token.tokenId.toString(),
+ },
+ });
+ }
+
+ expect(await token.getTop10Owners()).to.be.like([{Substrate: receiver.address}]);
+ });
+
itEth('Can perform transfer()', async ({helper}) => {
const caller = await helper.eth.createAccountWithBalance(donor);
const receiver = helper.eth.createAccount();
tests/src/eth/reFungibleAbi.jsondiffbeforeafterboth--- a/tests/src/eth/reFungibleAbi.json
+++ b/tests/src/eth/reFungibleAbi.json
@@ -91,6 +91,23 @@
},
{
"inputs": [
+ {
+ "components": [
+ { "internalType": "address", "name": "field_0", "type": "address" },
+ { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+ ],
+ "internalType": "struct Tuple6",
+ "name": "newAdmin",
+ "type": "tuple"
+ }
+ ],
+ "name": "addCollectionAdminCross",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
{ "internalType": "address", "name": "user", "type": "address" }
],
"name": "addToCollectionAllowList",
@@ -100,6 +117,23 @@
},
{
"inputs": [
+ {
+ "components": [
+ { "internalType": "address", "name": "field_0", "type": "address" },
+ { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+ ],
+ "internalType": "struct Tuple6",
+ "name": "user",
+ "type": "tuple"
+ }
+ ],
+ "name": "addToCollectionAllowListCross",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
{ "internalType": "address", "name": "user", "type": "address" }
],
"name": "allowed",
@@ -147,6 +181,24 @@
},
{
"inputs": [
+ {
+ "components": [
+ { "internalType": "address", "name": "field_0", "type": "address" },
+ { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+ ],
+ "internalType": "struct Tuple6",
+ "name": "from",
+ "type": "tuple"
+ },
+ { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+ ],
+ "name": "burnFromCross",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
{ "internalType": "address", "name": "newOwner", "type": "address" }
],
"name": "changeCollectionOwner",
@@ -163,7 +215,7 @@
{ "internalType": "address", "name": "field_0", "type": "address" },
{ "internalType": "uint256", "name": "field_1", "type": "uint256" }
],
- "internalType": "struct Tuple17[]",
+ "internalType": "struct Tuple6[]",
"name": "",
"type": "tuple[]"
}
@@ -180,7 +232,7 @@
{ "internalType": "address", "name": "field_0", "type": "address" },
{ "internalType": "uint256", "name": "field_1", "type": "uint256" }
],
- "internalType": "struct Tuple17",
+ "internalType": "struct Tuple6",
"name": "",
"type": "tuple"
}
@@ -204,7 +256,7 @@
{ "internalType": "address", "name": "field_0", "type": "address" },
{ "internalType": "uint256", "name": "field_1", "type": "uint256" }
],
- "internalType": "struct Tuple17",
+ "internalType": "struct Tuple6",
"name": "",
"type": "tuple"
}
@@ -286,6 +338,23 @@
"type": "function"
},
{
+ "inputs": [
+ {
+ "components": [
+ { "internalType": "address", "name": "field_0", "type": "address" },
+ { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+ ],
+ "internalType": "struct Tuple6",
+ "name": "user",
+ "type": "tuple"
+ }
+ ],
+ "name": "isOwnerOrAdminCross",
+ "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
"inputs": [{ "internalType": "address", "name": "to", "type": "address" }],
"name": "mint",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
@@ -352,6 +421,23 @@
"type": "function"
},
{
+ "inputs": [
+ {
+ "components": [
+ { "internalType": "address", "name": "field_0", "type": "address" },
+ { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+ ],
+ "internalType": "struct Tuple6",
+ "name": "admin",
+ "type": "tuple"
+ }
+ ],
+ "name": "removeCollectionAdminCross",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
"inputs": [],
"name": "removeCollectionSponsor",
"outputs": [],
@@ -369,6 +455,23 @@
},
{
"inputs": [
+ {
+ "components": [
+ { "internalType": "address", "name": "field_0", "type": "address" },
+ { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+ ],
+ "internalType": "struct Tuple6",
+ "name": "user",
+ "type": "tuple"
+ }
+ ],
+ "name": "removeFromCollectionAllowListCross",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
{ "internalType": "address", "name": "from", "type": "address" },
{ "internalType": "address", "name": "to", "type": "address" },
{ "internalType": "uint256", "name": "tokenId", "type": "uint256" }
@@ -476,6 +579,40 @@
},
{
"inputs": [
+ {
+ "components": [
+ { "internalType": "address", "name": "field_0", "type": "address" },
+ { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+ ],
+ "internalType": "struct Tuple6",
+ "name": "sponsor",
+ "type": "tuple"
+ }
+ ],
+ "name": "setCollectionSponsorCross",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "components": [
+ { "internalType": "address", "name": "field_0", "type": "address" },
+ { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+ ],
+ "internalType": "struct Tuple6",
+ "name": "newOwner",
+ "type": "tuple"
+ }
+ ],
+ "name": "setOwnerCross",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
{ "internalType": "uint256", "name": "tokenId", "type": "uint256" },
{ "internalType": "string", "name": "key", "type": "string" },
{ "internalType": "bytes", "name": "value", "type": "bytes" }
@@ -579,6 +716,33 @@
"type": "function"
},
{
+ "inputs": [
+ {
+ "components": [
+ { "internalType": "address", "name": "field_0", "type": "address" },
+ { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+ ],
+ "internalType": "struct Tuple6",
+ "name": "from",
+ "type": "tuple"
+ },
+ {
+ "components": [
+ { "internalType": "address", "name": "field_0", "type": "address" },
+ { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+ ],
+ "internalType": "struct Tuple6",
+ "name": "to",
+ "type": "tuple"
+ },
+ { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+ ],
+ "name": "transferFromCross",
+ "outputs": [],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
"inputs": [],
"name": "uniqueCollectionType",
"outputs": [{ "internalType": "string", "name": "", "type": "string" }],
tests/src/eth/util/playgrounds/types.tsdiffbeforeafterboth--- a/tests/src/eth/util/playgrounds/types.ts
+++ b/tests/src/eth/util/playgrounds/types.ts
@@ -13,3 +13,9 @@
event: string,
args: { [key: string]: string }
};
+export interface TEthCrossAccount {
+ readonly 0: string,
+ readonly 1: string | Uint8Array,
+ readonly field_0: string,
+ readonly field_1: string | Uint8Array,
+}
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
@@ -18,7 +18,7 @@
import {DevUniqueHelper} from '../../../util/playgrounds/unique.dev';
-import {ContractImports, CompiledContract, NormalizedEvent} from './types';
+import {ContractImports, CompiledContract, TEthCrossAccount, NormalizedEvent} from './types';
// Native contracts ABI
import collectionHelpersAbi from '../../collectionHelpersAbi.json';
@@ -343,6 +343,26 @@
export type EthUniqueHelperConstructor = new (...args: any[]) => EthUniqueHelper;
+export class EthCrossAccountGroup extends EthGroupBase {
+ fromAddress(address: TEthereumAccount): TEthCrossAccount {
+ return {
+ 0: address,
+ 1: '0',
+ field_0: address,
+ field_1: '0',
+ };
+ }
+
+ fromKeyringPair(keyring: IKeyringPair): TEthCrossAccount {
+ return {
+ 0: '0x0000000000000000000000000000000000000000',
+ 1: keyring.addressRaw,
+ field_0: '0x0000000000000000000000000000000000000000',
+ field_1: keyring.addressRaw,
+ };
+ }
+}
+
export class EthUniqueHelper extends DevUniqueHelper {
web3: Web3 | null = null;
web3Provider: WebsocketProvider | null = null;
@@ -351,6 +371,7 @@
ethAddress: EthAddressGroup;
ethNativeContract: NativeContractGroup;
ethContract: ContractGroup;
+ ethCrossAccount: EthCrossAccountGroup;
constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
options.helperBase = options.helperBase ?? EthUniqueHelper;
@@ -358,6 +379,7 @@
super(logger, options);
this.eth = new EthGroup(this);
this.ethAddress = new EthAddressGroup(this);
+ this.ethCrossAccount = new EthCrossAccountGroup(this);
this.ethNativeContract = new NativeContractGroup(this);
this.ethContract = new ContractGroup(this);
}