difftreelog
chore fix code review requests
in: master
36 files changed
client/rpc/src/lib.rsdiffbeforeafterboth--- a/client/rpc/src/lib.rs
+++ b/client/rpc/src/lib.rs
@@ -248,8 +248,8 @@
) -> Result<Option<String>>;
/// Get whether an operator is approved by a given owner.
- #[method(name = "unique_isApprovedForAll")]
- fn is_approved_for_all(
+ #[method(name = "unique_allowanceForAll")]
+ fn allowance_for_all(
&self,
collection: CollectionId,
owner: CrossAccountId,
@@ -579,7 +579,7 @@
pass_method!(effective_collection_limits(collection_id: CollectionId) -> Option<CollectionLimits>, unique_api);
pass_method!(total_pieces(collection_id: CollectionId, token_id: TokenId) -> Option<String> => |o| o.map(|number| number.to_string()) , unique_api);
pass_method!(token_owners(collection: CollectionId, token: TokenId) -> Vec<CrossAccountId>, unique_api);
- pass_method!(is_approved_for_all(collection: CollectionId, owner: CrossAccountId, operator: CrossAccountId) -> bool, unique_api);
+ pass_method!(allowance_for_all(collection: CollectionId, owner: CrossAccountId, operator: CrossAccountId) -> bool, unique_api);
}
impl<C, Block, BlockNumber, CrossAccountId, AccountId>
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -1535,7 +1535,7 @@
fn token_owner() -> Weight;
/// The price of setting approval for all
- fn set_approval_for_all() -> Weight;
+ fn set_allowance_for_all() -> Weight;
}
/// Weight info extension trait for refungible pallet.
@@ -1844,11 +1844,11 @@
/// Get extension for RFT collection.
fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>>;
- /// An operator is allowed to transfer all tokens of the sender on their behalf.
+ /// The `operator` is allowed to transfer all tokens of the `owner` on their behalf.
/// * `owner` - Token owner
/// * `operator` - Operator
/// * `approve` - Should operator status be granted or revoked?
- fn set_approval_for_all(
+ fn set_allowance_for_all(
&self,
owner: T::CrossAccountId,
operator: T::CrossAccountId,
@@ -1856,7 +1856,7 @@
) -> DispatchResultWithPostInfo;
/// Tells whether the given `owner` approves the `operator`.
- fn is_approved_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;
+ fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool;
}
/// Extension for RFT collection.
pallets/fungible/src/common.rsdiffbeforeafterboth--- a/pallets/fungible/src/common.rs
+++ b/pallets/fungible/src/common.rs
@@ -108,7 +108,7 @@
Weight::zero()
}
- fn set_approval_for_all() -> Weight {
+ fn set_allowance_for_all() -> Weight {
Weight::zero()
}
}
@@ -429,7 +429,7 @@
<TotalSupply<T>>::try_get(self.id).ok()
}
- fn set_approval_for_all(
+ fn set_allowance_for_all(
&self,
_owner: T::CrossAccountId,
_operator: T::CrossAccountId,
@@ -438,7 +438,7 @@
fail!(<Error<T>>::SettingApprovalForAllNotAllowed)
}
- fn is_approved_for_all(&self, _owner: T::CrossAccountId, _operator: T::CrossAccountId) -> bool {
+ fn allowance_for_all(&self, _owner: T::CrossAccountId, _operator: T::CrossAccountId) -> bool {
false
}
}
pallets/nonfungible/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/benchmarking.rs
+++ b/pallets/nonfungible/src/benchmarking.rs
@@ -223,17 +223,17 @@
}: {collection.token_owner(item)}
- set_approval_for_all {
+ set_allowance_for_all {
bench_init!{
owner: sub; collection: collection(owner);
operator: cross_from_sub(owner); owner: cross_sub;
};
- }: {<Pallet<T>>::set_approval_for_all(&collection, &owner, &operator, true)}
+ }: {<Pallet<T>>::set_allowance_for_all(&collection, &owner, &operator, true)}
- is_approved_for_all {
+ allowance_for_all {
bench_init!{
owner: sub; collection: collection(owner);
operator: cross_from_sub(owner); owner: cross_sub;
};
- }: {<Pallet<T>>::is_approved_for_all(&collection, &owner, &operator)}
+ }: {<Pallet<T>>::allowance_for_all(&collection, &owner, &operator)}
}
pallets/nonfungible/src/common.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/common.rs
+++ b/pallets/nonfungible/src/common.rs
@@ -123,8 +123,8 @@
<SelfWeightOf<T>>::token_owner()
}
- fn set_approval_for_all() -> Weight {
- <SelfWeightOf<T>>::set_approval_for_all()
+ fn set_allowance_for_all() -> Weight {
+ <SelfWeightOf<T>>::set_allowance_for_all()
}
}
@@ -517,19 +517,19 @@
}
}
- fn set_approval_for_all(
+ fn set_allowance_for_all(
&self,
owner: T::CrossAccountId,
operator: T::CrossAccountId,
approve: bool,
) -> DispatchResultWithPostInfo {
with_weight(
- <Pallet<T>>::set_approval_for_all(self, &owner, &operator, approve),
- <CommonWeights<T>>::set_approval_for_all(),
+ <Pallet<T>>::set_allowance_for_all(self, &owner, &operator, approve),
+ <CommonWeights<T>>::set_allowance_for_all(),
)
}
- fn is_approved_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool {
- <Pallet<T>>::is_approved_for_all(self, &owner, &operator)
+ fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool {
+ <Pallet<T>>::allowance_for_all(self, &owner, &operator)
}
}
pallets/nonfungible/src/erc.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -472,8 +472,8 @@
/// @notice Sets or unsets the approval of a given operator.
/// The `operator` is allowed to transfer all tokens of the `caller` on their behalf.
/// @param operator Operator
- /// @param approved Is operator enabled or disabled
- #[weight(<SelfWeightOf<T>>::set_approval_for_all())]
+ /// @param approved Should operator status be granted or revoked?
+ #[weight(<SelfWeightOf<T>>::set_allowance_for_all())]
fn set_approval_for_all(
&mut self,
caller: caller,
@@ -483,7 +483,7 @@
let caller = T::CrossAccountId::from_eth(caller);
let operator = T::CrossAccountId::from_eth(operator);
- <Pallet<T>>::set_approval_for_all(self, &caller, &operator, approved)
+ <Pallet<T>>::set_allowance_for_all(self, &caller, &operator, approved)
.map_err(dispatch_to_evm::<T>)?;
Ok(())
}
@@ -494,13 +494,13 @@
Err("not implemented".into())
}
- /// @notice Tells whether an operator is approved by a given owner.
- #[weight(<SelfWeightOf<T>>::is_approved_for_all())]
+ /// @notice Tells whether the given `owner` approves the `operator`.
+ #[weight(<SelfWeightOf<T>>::allowance_for_all())]
fn is_approved_for_all(&self, owner: address, operator: address) -> Result<bool> {
let owner = T::CrossAccountId::from_eth(owner);
let operator = T::CrossAccountId::from_eth(operator);
- Ok(<Pallet<T>>::is_approved_for_all(self, &owner, &operator))
+ Ok(<Pallet<T>>::allowance_for_all(self, &owner, &operator))
}
/// @notice Returns collection helper contract address
pallets/nonfungible/src/lib.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -274,7 +274,7 @@
/// Operator set by a wallet owner that could perform certain transactions on all tokens in the wallet.
#[pallet::storage]
- pub type WalletOperator<T: Config> = StorageNMap<
+ pub type CollectionAllowance<T: Config> = StorageNMap<
Key = (
Key<Twox64Concat, CollectionId>,
Key<Blake2_128Concat, T::CrossAccountId>,
@@ -450,7 +450,7 @@
<TokensBurnt<T>>::remove(id);
let _ = <Allowance<T>>::clear_prefix((id,), u32::MAX, None);
let _ = <AccountBalance<T>>::clear_prefix((id,), u32::MAX, None);
- let _ = <WalletOperator<T>>::clear_prefix((id,), u32::MAX, None);
+ let _ = <CollectionAllowance<T>>::clear_prefix((id,), u32::MAX, None);
Ok(())
}
@@ -1206,7 +1206,7 @@
if <Allowance<T>>::get((collection.id, token)).as_ref() == Some(spender) {
return Ok(());
}
- if <WalletOperator<T>>::get((collection.id, from, spender)) == Some(true) {
+ if <CollectionAllowance<T>>::get((collection.id, from, spender)) {
return Ok(());
}
ensure!(
@@ -1345,11 +1345,11 @@
/// Sets or unsets the approval of a given operator.
///
- /// An operator is allowed to transfer all token pieces of the sender on their behalf.
+ /// The `operator` is allowed to transfer all token pieces of the `owner` on their behalf.
/// - `owner`: Token owner
/// - `operator`: Operator
- /// - `approve`: Is operator enabled or disabled
- pub fn set_approval_for_all(
+ /// - `approve`: Should operator status be granted or revoked?
+ pub fn set_allowance_for_all(
collection: &NonfungibleHandle<T>,
owner: &T::CrossAccountId,
operator: &T::CrossAccountId,
@@ -1364,7 +1364,7 @@
// =========
- <WalletOperator<T>>::insert((collection.id, owner, operator), approve);
+ <CollectionAllowance<T>>::insert((collection.id, owner, operator), approve);
<PalletEvm<T>>::deposit_log(
ERC721Events::ApprovalForAll {
owner: *owner.as_eth(),
@@ -1382,12 +1382,12 @@
Ok(())
}
- /// Tells whether an operator is approved by a given owner.
- pub fn is_approved_for_all(
+ /// Tells whether the given `owner` approves the `operator`.
+ pub fn allowance_for_all(
collection: &NonfungibleHandle<T>,
owner: &T::CrossAccountId,
operator: &T::CrossAccountId,
) -> bool {
- <WalletOperator<T>>::get((collection.id, owner, operator)).unwrap_or(false)
+ <CollectionAllowance<T>>::get((collection.id, owner, operator))
}
}
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
@@ -1021,9 +1021,9 @@
}
/// @notice Sets or unsets the approval of a given operator.
- /// An operator is allowed to transfer all tokens of the sender on their behalf.
+ /// The `operator` is allowed to transfer all tokens of the `caller` on their behalf.
/// @param operator Operator
- /// @param approved Is operator enabled or disabled
+ /// @param approved Should operator status be granted or revoked?
/// @dev EVM selector for this function is: 0xa22cb465,
/// or in textual repr: setApprovalForAll(address,bool)
function setApprovalForAll(address operator, bool approved) public {
@@ -1043,7 +1043,7 @@
return 0x0000000000000000000000000000000000000000;
}
- /// @notice Tells whether an operator is approved by a given owner.
+ /// @notice Tells whether the given `owner` approves the `operator`.
/// @dev EVM selector for this function is: 0xe985e9c5,
/// or in textual repr: isApprovedForAll(address,address)
function isApprovedForAll(address owner, address operator) public view returns (bool) {
pallets/nonfungible/src/weights.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/weights.rs
+++ b/pallets/nonfungible/src/weights.rs
@@ -48,8 +48,8 @@
fn set_token_properties(b: u32, ) -> Weight;
fn delete_token_properties(b: u32, ) -> Weight;
fn token_owner() -> Weight;
- fn set_approval_for_all() -> Weight;
- fn is_approved_for_all() -> Weight;
+ fn set_allowance_for_all() -> Weight;
+ fn allowance_for_all() -> Weight;
}
/// Weights for pallet_nonfungible using the Substrate node and recommended hardware.
@@ -199,12 +199,12 @@
.saturating_add(T::DbWeight::get().reads(1 as u64))
}
// Storage: Nonfungible WalletOperator (r:0 w:1)
- fn set_approval_for_all() -> Weight {
+ fn set_allowance_for_all() -> Weight {
Weight::from_ref_time(16_231_000 as u64)
.saturating_add(T::DbWeight::get().writes(1 as u64))
}
// Storage: Nonfungible WalletOperator (r:1 w:0)
- fn is_approved_for_all() -> Weight {
+ fn allowance_for_all() -> Weight {
Weight::from_ref_time(6_161_000 as u64)
.saturating_add(T::DbWeight::get().reads(1 as u64))
}
@@ -356,12 +356,12 @@
.saturating_add(RocksDbWeight::get().reads(1 as u64))
}
// Storage: Nonfungible WalletOperator (r:0 w:1)
- fn set_approval_for_all() -> Weight {
+ fn set_allowance_for_all() -> Weight {
Weight::from_ref_time(16_231_000 as u64)
.saturating_add(RocksDbWeight::get().writes(1 as u64))
}
// Storage: Nonfungible WalletOperator (r:1 w:0)
- fn is_approved_for_all() -> Weight {
+ fn allowance_for_all() -> Weight {
Weight::from_ref_time(6_161_000 as u64)
.saturating_add(RocksDbWeight::get().reads(1 as u64))
}
pallets/refungible/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/refungible/src/benchmarking.rs
+++ b/pallets/refungible/src/benchmarking.rs
@@ -291,17 +291,17 @@
let item = create_max_item(&collection, &sender, [(owner.clone(), 100)])?;
}: {<Pallet<T>>::token_owner(collection.id, item)}
- set_approval_for_all {
+ set_allowance_for_all {
bench_init!{
owner: sub; collection: collection(owner);
operator: cross_from_sub(owner); owner: cross_sub;
};
- }: {<Pallet<T>>::set_approval_for_all(&collection, &owner, &operator, true)}
+ }: {<Pallet<T>>::set_allowance_for_all(&collection, &owner, &operator, true)}
- is_approved_for_all {
+ allowance_for_all {
bench_init!{
owner: sub; collection: collection(owner);
operator: cross_from_sub(owner); owner: cross_sub;
};
- }: {<Pallet<T>>::is_approved_for_all(&collection, &owner, &operator)}
+ }: {<Pallet<T>>::allowance_for_all(&collection, &owner, &operator)}
}
pallets/refungible/src/common.rsdiffbeforeafterboth--- a/pallets/refungible/src/common.rs
+++ b/pallets/refungible/src/common.rs
@@ -153,8 +153,8 @@
<SelfWeightOf<T>>::token_owner()
}
- fn set_approval_for_all() -> Weight {
- <SelfWeightOf<T>>::set_approval_for_all()
+ fn set_allowance_for_all() -> Weight {
+ <SelfWeightOf<T>>::set_allowance_for_all()
}
}
@@ -521,20 +521,20 @@
<Pallet<T>>::total_pieces(self.id, token)
}
- fn set_approval_for_all(
+ fn set_allowance_for_all(
&self,
owner: T::CrossAccountId,
operator: T::CrossAccountId,
approve: bool,
) -> DispatchResultWithPostInfo {
with_weight(
- <Pallet<T>>::set_approval_for_all(self, &owner, &operator, approve),
- <CommonWeights<T>>::set_approval_for_all(),
+ <Pallet<T>>::set_allowance_for_all(self, &owner, &operator, approve),
+ <CommonWeights<T>>::set_allowance_for_all(),
)
}
- fn is_approved_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool {
- <Pallet<T>>::is_approved_for_all(self, &owner, &operator)
+ fn allowance_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool {
+ <Pallet<T>>::allowance_for_all(self, &owner, &operator)
}
}
pallets/refungible/src/erc.rsdiffbeforeafterboth--- a/pallets/refungible/src/erc.rs
+++ b/pallets/refungible/src/erc.rs
@@ -462,10 +462,10 @@
}
/// @notice Sets or unsets the approval of a given operator.
- /// An operator is allowed to transfer all tokens of the sender on their behalf.
+ /// The `operator` is allowed to transfer all token pieces of the `caller` on their behalf.
/// @param operator Operator
- /// @param approved Is operator enabled or disabled
- #[weight(<SelfWeightOf<T>>::set_approval_for_all())]
+ /// @param approved Should operator status be granted or revoked?
+ #[weight(<SelfWeightOf<T>>::set_allowance_for_all())]
fn set_approval_for_all(
&mut self,
caller: caller,
@@ -475,7 +475,7 @@
let caller = T::CrossAccountId::from_eth(caller);
let operator = T::CrossAccountId::from_eth(operator);
- <Pallet<T>>::set_approval_for_all(self, &caller, &operator, approved)
+ <Pallet<T>>::set_allowance_for_all(self, &caller, &operator, approved)
.map_err(dispatch_to_evm::<T>)?;
Ok(())
}
@@ -486,13 +486,13 @@
Err("not implemented".into())
}
- /// @notice Tells whether an operator is approved by a given owner.
- #[weight(<SelfWeightOf<T>>::is_approved_for_all())]
+ /// @notice Tells whether the given `owner` approves the `operator`.
+ #[weight(<SelfWeightOf<T>>::allowance_for_all())]
fn is_approved_for_all(&self, owner: address, operator: address) -> Result<bool> {
let owner = T::CrossAccountId::from_eth(owner);
let operator = T::CrossAccountId::from_eth(operator);
- Ok(<Pallet<T>>::is_approved_for_all(self, &owner, &operator))
+ Ok(<Pallet<T>>::allowance_for_all(self, &owner, &operator))
}
/// @notice Returns collection helper contract address
pallets/refungible/src/lib.rsdiffbeforeafterboth--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -275,14 +275,14 @@
/// Operator set by a wallet owner that could perform certain transactions on all tokens in the wallet.
#[pallet::storage]
- pub type WalletOperator<T: Config> = StorageNMap<
+ pub type CollectionAllowance<T: Config> = StorageNMap<
Key = (
Key<Twox64Concat, CollectionId>,
Key<Blake2_128Concat, T::CrossAccountId>,
Key<Blake2_128Concat, T::CrossAccountId>,
),
Value = bool,
- QueryKind = OptionQuery,
+ QueryKind = ValueQuery,
>;
#[pallet::hooks]
@@ -1174,8 +1174,8 @@
let allowance =
<Allowance<T>>::get((collection.id, token, from, &spender)).checked_sub(amount);
- // Allowance if any would be reduced if spender is also wallet operator
- if <WalletOperator<T>>::get((collection.id, from, spender)) == Some(true) {
+ // Allowance (if any) would be reduced if spender is also wallet operator
+ if <CollectionAllowance<T>>::get((collection.id, from, spender)) {
return Ok(allowance);
}
@@ -1408,11 +1408,11 @@
/// Sets or unsets the approval of a given operator.
///
- /// An operator is allowed to transfer all tokens of the sender on their behalf.
+ /// The `operator` is allowed to transfer all token pieces of the `owner` on their behalf.
/// - `owner`: Token owner
/// - `operator`: Operator
- /// - `approve`: Is operator enabled or disabled
- pub fn set_approval_for_all(
+ /// - `approve`: Should operator status be granted or revoked?
+ pub fn set_allowance_for_all(
collection: &RefungibleHandle<T>,
owner: &T::CrossAccountId,
operator: &T::CrossAccountId,
@@ -1427,7 +1427,7 @@
// =========
- <WalletOperator<T>>::insert((collection.id, owner, operator), approve);
+ <CollectionAllowance<T>>::insert((collection.id, owner, operator), approve);
<PalletEvm<T>>::deposit_log(
ERC721Events::ApprovalForAll {
owner: *owner.as_eth(),
@@ -1445,12 +1445,12 @@
Ok(())
}
- /// Tells whether an operator is approved by a given owner.
- pub fn is_approved_for_all(
+ /// Tells whether the given `owner` approves the `operator`.
+ pub fn allowance_for_all(
collection: &RefungibleHandle<T>,
owner: &T::CrossAccountId,
operator: &T::CrossAccountId,
) -> bool {
- <WalletOperator<T>>::get((collection.id, owner, operator)).unwrap_or(false)
+ <CollectionAllowance<T>>::get((collection.id, owner, operator))
}
}
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
@@ -1018,9 +1018,9 @@
}
/// @notice Sets or unsets the approval of a given operator.
- /// An operator is allowed to transfer all tokens of the sender on their behalf.
+ /// The `operator` is allowed to transfer all token pieces of the `caller` on their behalf.
/// @param operator Operator
- /// @param approved Is operator enabled or disabled
+ /// @param approved Should operator status be granted or revoked?
/// @dev EVM selector for this function is: 0xa22cb465,
/// or in textual repr: setApprovalForAll(address,bool)
function setApprovalForAll(address operator, bool approved) public {
@@ -1040,7 +1040,7 @@
return 0x0000000000000000000000000000000000000000;
}
- /// @notice Tells whether an operator is approved by a given owner.
+ /// @notice Tells whether the given `owner` approves the `operator`.
/// @dev EVM selector for this function is: 0xe985e9c5,
/// or in textual repr: isApprovedForAll(address,address)
function isApprovedForAll(address owner, address operator) public view returns (bool) {
pallets/refungible/src/weights.rsdiffbeforeafterboth--- a/pallets/refungible/src/weights.rs
+++ b/pallets/refungible/src/weights.rs
@@ -55,8 +55,8 @@
fn delete_token_properties(b: u32, ) -> Weight;
fn repartition_item() -> Weight;
fn token_owner() -> Weight;
- fn set_approval_for_all() -> Weight;
- fn is_approved_for_all() -> Weight;
+ fn set_allowance_for_all() -> Weight;
+ fn allowance_for_all() -> Weight;
}
/// Weights for pallet_refungible using the Substrate node and recommended hardware.
@@ -263,12 +263,12 @@
.saturating_add(T::DbWeight::get().reads(2 as u64))
}
// Storage: Refungible WalletOperator (r:0 w:1)
- fn set_approval_for_all() -> Weight {
+ fn set_allowance_for_all() -> Weight {
Weight::from_ref_time(16_150_000 as u64)
.saturating_add(T::DbWeight::get().writes(1 as u64))
}
// Storage: Refungible WalletOperator (r:1 w:0)
- fn is_approved_for_all() -> Weight {
+ fn allowance_for_all() -> Weight {
Weight::from_ref_time(5_901_000 as u64)
.saturating_add(T::DbWeight::get().reads(1 as u64))
}
@@ -477,12 +477,12 @@
.saturating_add(RocksDbWeight::get().reads(2 as u64))
}
// Storage: Refungible WalletOperator (r:0 w:1)
- fn set_approval_for_all() -> Weight {
+ fn set_allowance_for_all() -> Weight {
Weight::from_ref_time(16_150_000 as u64)
.saturating_add(RocksDbWeight::get().writes(1 as u64))
}
// Storage: Refungible WalletOperator (r:1 w:0)
- fn is_approved_for_all() -> Weight {
+ fn allowance_for_all() -> Weight {
Weight::from_ref_time(5_901_000 as u64)
.saturating_add(RocksDbWeight::get().reads(1 as u64))
}
pallets/unique/src/eth/mod.rsdiffbeforeafterboth--- a/pallets/unique/src/eth/mod.rs
+++ b/pallets/unique/src/eth/mod.rs
@@ -36,7 +36,7 @@
use sp_std::vec;
use up_data_structs::{
CollectionDescription, CollectionMode, CollectionName, CollectionTokenPrefix,
- CreateCollectionData, CollectionId,
+ CreateCollectionData,
};
use crate::{weights::WeightInfo, Config, SelfWeightOf};
pallets/unique/src/lib.rsdiffbeforeafterboth--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -1129,15 +1129,15 @@
/// Sets or unsets the approval of a given operator.
///
- /// An operator is allowed to transfer all tokens of the sender on their behalf.
+ /// The `operator` is allowed to transfer all tokens of the `owner` on their behalf.
///
/// # Arguments
///
/// * `owner`: Token owner
/// * `operator`: Operator
- /// * `approve`: Is operator enabled or disabled
- #[weight = T::CommonWeightInfo::set_approval_for_all()]
- pub fn set_approval_for_all(
+ /// * `approve`: Should operator status be granted or revoked?
+ #[weight = T::CommonWeightInfo::set_allowance_for_all()]
+ pub fn set_allowance_for_all(
origin,
collection_id: CollectionId,
operator: T::CrossAccountId,
@@ -1145,7 +1145,7 @@
) -> DispatchResultWithPostInfo {
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
dispatch_tx::<T, _>(collection_id, |d| {
- d.set_approval_for_all(sender, operator, approve)
+ d.set_allowance_for_all(sender, operator, approve)
})
}
}
primitives/rpc/src/lib.rsdiffbeforeafterboth--- a/primitives/rpc/src/lib.rs
+++ b/primitives/rpc/src/lib.rs
@@ -134,6 +134,6 @@
fn token_owners(collection: CollectionId, token: TokenId) -> Result<Vec<CrossAccountId>>;
/// Get whether an operator is approved by a given owner.
- fn is_approved_for_all(collection: CollectionId, owner: CrossAccountId, operator: CrossAccountId) -> Result<bool>;
+ fn allowance_for_all(collection: CollectionId, owner: CrossAccountId, operator: CrossAccountId) -> Result<bool>;
}
}
runtime/common/runtime_apis.rsdiffbeforeafterboth--- a/runtime/common/runtime_apis.rs
+++ b/runtime/common/runtime_apis.rs
@@ -188,8 +188,8 @@
dispatch_unique_runtime!(collection.total_pieces(token_id))
}
- fn is_approved_for_all(collection: CollectionId, owner: CrossAccountId, operator: CrossAccountId) -> Result<bool, DispatchError> {
- dispatch_unique_runtime!(collection.is_approved_for_all(owner, operator))
+ fn allowance_for_all(collection: CollectionId, owner: CrossAccountId, operator: CrossAccountId) -> Result<bool, DispatchError> {
+ dispatch_unique_runtime!(collection.allowance_for_all(owner, operator))
}
}
runtime/common/weights.rsdiffbeforeafterboth--- a/runtime/common/weights.rs
+++ b/runtime/common/weights.rs
@@ -121,8 +121,8 @@
max_weight_of!(token_owner())
}
- fn set_approval_for_all() -> Weight {
- max_weight_of!(set_approval_for_all())
+ fn set_allowance_for_all() -> Weight {
+ max_weight_of!(set_allowance_for_all())
}
}
tests/src/approve.test.tsdiffbeforeafterboth--- a/tests/src/approve.test.ts
+++ b/tests/src/approve.test.ts
@@ -617,26 +617,31 @@
itSub('[nft] Enable and disable approval', async ({helper}) => {
const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
- await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});
- const checkBeforeApproval = await helper.nft.isApprovedForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address});
+
+ const checkBeforeApproval = await helper.nft.allowanceForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address});
expect(checkBeforeApproval).to.be.false;
- await helper.nft.setApprovalForAll(alice, collectionId, {Substrate: bob.address}, true);
- const checkAfterApproval = await helper.nft.isApprovedForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address});
+
+ await helper.nft.setAllowanceForAll(alice, collectionId, {Substrate: bob.address}, true);
+ const checkAfterApproval = await helper.nft.allowanceForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address});
expect(checkAfterApproval).to.be.true;
- await helper.nft.setApprovalForAll(alice, collectionId, {Substrate: bob.address}, false);
- const checkAfterDisapproval = await helper.nft.isApprovedForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address});
+
+ await helper.nft.setAllowanceForAll(alice, collectionId, {Substrate: bob.address}, false);
+ const checkAfterDisapproval = await helper.nft.allowanceForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address});
expect(checkAfterDisapproval).to.be.false;
});
itSub.ifWithPallets('[rft] Enable and disable approval', [Pallets.ReFungible], async ({helper}) => {
const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
- const checkBeforeApproval = await helper.rft.isApprovedForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address});
+
+ const checkBeforeApproval = await helper.rft.allowanceForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address});
expect(checkBeforeApproval).to.be.false;
- await helper.rft.setApprovalForAll(alice, collectionId, {Substrate: bob.address}, true);
- const checkAfterApproval = await helper.rft.isApprovedForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address});
+
+ await helper.rft.setAllowanceForAll(alice, collectionId, {Substrate: bob.address}, true);
+ const checkAfterApproval = await helper.rft.allowanceForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address});
expect(checkAfterApproval).to.be.true;
- await helper.rft.setApprovalForAll(alice, collectionId, {Substrate: bob.address}, false);
- const checkAfterDisapproval = await helper.rft.isApprovedForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address});
+
+ await helper.rft.setAllowanceForAll(alice, collectionId, {Substrate: bob.address}, false);
+ const checkAfterDisapproval = await helper.rft.allowanceForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address});
expect(checkAfterDisapproval).to.be.false;
});
});
tests/src/eth/api/UniqueNFT.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueNFT.sol
+++ b/tests/src/eth/api/UniqueNFT.sol
@@ -672,9 +672,9 @@
function approve(address approved, uint256 tokenId) external;
/// @notice Sets or unsets the approval of a given operator.
- /// An operator is allowed to transfer all tokens of the sender on their behalf.
+ /// The `operator` is allowed to transfer all tokens of the `caller` on their behalf.
/// @param operator Operator
- /// @param approved Is operator enabled or disabled
+ /// @param approved Should operator status be granted or revoked?
/// @dev EVM selector for this function is: 0xa22cb465,
/// or in textual repr: setApprovalForAll(address,bool)
function setApprovalForAll(address operator, bool approved) external;
@@ -684,7 +684,7 @@
/// or in textual repr: getApproved(uint256)
function getApproved(uint256 tokenId) external view returns (address);
- /// @notice Tells whether an operator is approved by a given owner.
+ /// @notice Tells whether the given `owner` approves the `operator`.
/// @dev EVM selector for this function is: 0xe985e9c5,
/// or in textual repr: isApprovedForAll(address,address)
function isApprovedForAll(address owner, address operator) external view returns (bool);
tests/src/eth/api/UniqueRefungible.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueRefungible.sol
+++ b/tests/src/eth/api/UniqueRefungible.sol
@@ -669,9 +669,9 @@
function approve(address approved, uint256 tokenId) external;
/// @notice Sets or unsets the approval of a given operator.
- /// An operator is allowed to transfer all tokens of the sender on their behalf.
+ /// The `operator` is allowed to transfer all token pieces of the `caller` on their behalf.
/// @param operator Operator
- /// @param approved Is operator enabled or disabled
+ /// @param approved Should operator status be granted or revoked?
/// @dev EVM selector for this function is: 0xa22cb465,
/// or in textual repr: setApprovalForAll(address,bool)
function setApprovalForAll(address operator, bool approved) external;
@@ -681,7 +681,7 @@
/// or in textual repr: getApproved(uint256)
function getApproved(uint256 tokenId) external view returns (address);
- /// @notice Tells whether an operator is approved by a given owner.
+ /// @notice Tells whether the given `owner` approves the `operator`.
/// @dev EVM selector for this function is: 0xe985e9c5,
/// or in textual repr: isApprovedForAll(address,address)
function isApprovedForAll(address owner, address operator) external view returns (bool);
tests/src/eth/nonFungible.test.tsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {itEth, usingEthPlaygrounds, expect, EthUniqueHelper} from './util';18import {IKeyringPair} from '@polkadot/types/types';19import {Contract} from 'web3-eth-contract';202122describe('NFT: Information getting', () => {23 let donor: IKeyringPair;24 let alice: IKeyringPair;2526 before(async function() {27 await usingEthPlaygrounds(async (helper, privateKey) => {28 donor = await privateKey({filename: __filename});29 [alice] = await helper.arrange.createAccounts([10n], donor);30 });31 });3233 itEth('totalSupply', async ({helper}) => {34 const collection = await helper.nft.mintCollection(alice, {});35 await collection.mintToken(alice);3637 const caller = await helper.eth.createAccountWithBalance(donor);3839 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);40 const totalSupply = await contract.methods.totalSupply().call();4142 expect(totalSupply).to.equal('1');43 });4445 itEth('balanceOf', async ({helper}) => {46 const collection = await helper.nft.mintCollection(alice, {});47 const caller = await helper.eth.createAccountWithBalance(donor);4849 await collection.mintToken(alice, {Ethereum: caller});50 await collection.mintToken(alice, {Ethereum: caller});51 await collection.mintToken(alice, {Ethereum: caller});5253 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);54 const balance = await contract.methods.balanceOf(caller).call();5556 expect(balance).to.equal('3');57 });5859 itEth('ownerOf', async ({helper}) => {60 const collection = await helper.nft.mintCollection(alice, {});61 const caller = await helper.eth.createAccountWithBalance(donor);6263 const token = await collection.mintToken(alice, {Ethereum: caller});6465 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);6667 const owner = await contract.methods.ownerOf(token.tokenId).call();6869 expect(owner).to.equal(caller);70 });7172 itEth('name/symbol is available regardless of ERC721Metadata support', async ({helper}) => {73 const collection = await helper.nft.mintCollection(alice, {name: 'test', tokenPrefix: 'TEST'});74 const caller = helper.eth.createAccount();7576 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);7778 expect(await contract.methods.name().call()).to.equal('test');79 expect(await contract.methods.symbol().call()).to.equal('TEST');80 });81});8283describe('Check ERC721 token URI for NFT', () => {84 let donor: IKeyringPair;8586 before(async function() {87 await usingEthPlaygrounds(async (_helper, privateKey) => {88 donor = await privateKey({filename: __filename});89 });90 });9192 async function setup(helper: EthUniqueHelper, baseUri: string, propertyKey?: string, propertyValue?: string): Promise<{contract: Contract, nextTokenId: string}> {93 const owner = await helper.eth.createAccountWithBalance(donor);94 const receiver = helper.eth.createAccount();9596 const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Mint collection', 'a', 'b', baseUri);97 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);9899 const result = await contract.methods.mint(receiver).send();100 const tokenId = result.events.Transfer.returnValues.tokenId;101 expect(tokenId).to.be.equal('1');102103 if (propertyKey && propertyValue) {104 // Set URL or suffix105 await contract.methods.setProperties(tokenId, [{key: propertyKey, value: Buffer.from(propertyValue)}]).send();106 }107108 const event = result.events.Transfer;109 expect(event.address).to.be.equal(collectionAddress);110 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');111 expect(event.returnValues.to).to.be.equal(receiver);112 expect(event.returnValues.tokenId).to.be.equal(tokenId);113114 return {contract, nextTokenId: tokenId};115 }116117 itEth('Empty tokenURI', async ({helper}) => {118 const {contract, nextTokenId} = await setup(helper, '');119 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('');120 });121122 itEth('TokenURI from url', async ({helper}) => {123 const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URI', 'Token URI');124 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Token URI');125 });126127 itEth('TokenURI from baseURI', async ({helper}) => {128 const {contract, nextTokenId} = await setup(helper, 'BaseURI_');129 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_');130 });131132 itEth('TokenURI from baseURI + suffix', async ({helper}) => {133 const suffix = '/some/suffix';134 const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URISuffix', suffix);135 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + suffix);136 });137});138139describe('NFT: Plain calls', () => {140 let donor: IKeyringPair;141 let minter: IKeyringPair;142 let bob: IKeyringPair;143 let charlie: IKeyringPair;144145 before(async function() {146 await usingEthPlaygrounds(async (helper, privateKey) => {147 donor = await privateKey({filename: __filename});148 [minter, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);149 });150 });151152 itEth('Can perform mint() & get crossOwner()', async ({helper}) => {153 const owner = await helper.eth.createAccountWithBalance(donor);154 const receiver = helper.eth.createAccount();155156 const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Mint collection', '6', '6', '');157 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);158159 const result = await contract.methods.mintWithTokenURI(receiver, 'Test URI').send();160 const tokenId = result.events.Transfer.returnValues.tokenId;161 expect(tokenId).to.be.equal('1');162163 const event = result.events.Transfer;164 expect(event.address).to.be.equal(collectionAddress);165 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');166 expect(event.returnValues.to).to.be.equal(receiver);167168 expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');169 console.log(await contract.methods.crossOwnerOf(tokenId).call());170 expect(await contract.methods.crossOwnerOf(tokenId).call()).to.be.like([receiver, '0']);171 // TODO: this wont work right now, need release 919000 first172 // await helper.methods.setOffchainSchema(collectionIdAddress, 'https://offchain-service.local/token-info/{id}').send();173 // const tokenUri = await contract.methods.tokenURI(nextTokenId).call();174 // expect(tokenUri).to.be.equal(`https://offchain-service.local/token-info/${nextTokenId}`);175 });176177 //TODO: CORE-302 add eth methods178 itEth.skip('Can perform mintBulk()', async ({helper}) => {179 const caller = await helper.eth.createAccountWithBalance(donor);180 const receiver = helper.eth.createAccount();181182 const collection = await helper.nft.mintCollection(minter);183 await collection.addAdmin(minter, {Ethereum: caller});184185 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);186 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', caller);187 {188 const bulkSize = 3;189 const nextTokenId = await contract.methods.nextTokenId().call();190 expect(nextTokenId).to.be.equal('1');191 const result = await contract.methods.mintBulkWithTokenURI(192 receiver,193 Array.from({length: bulkSize}, (_, i) => (194 [+nextTokenId + i, `Test URI ${i}`]195 )),196 ).send({from: caller});197198 const events = result.events.Transfer.sort((a: any, b: any) => +a.returnValues.tokenId - b.returnValues.tokenId);199 for (let i = 0; i < bulkSize; i++) {200 const event = events[i];201 expect(event.address).to.equal(collectionAddress);202 expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');203 expect(event.returnValues.to).to.equal(receiver);204 expect(event.returnValues.tokenId).to.equal(`${+nextTokenId+i}`);205206 expect(await contract.methods.tokenURI(+nextTokenId + i).call()).to.be.equal(`Test URI ${i}`);207 }208 }209 });210211 itEth('Can perform burn()', async ({helper}) => {212 const caller = await helper.eth.createAccountWithBalance(donor);213214 const collection = await helper.nft.mintCollection(minter, {});215 const {tokenId} = await collection.mintToken(minter, {Ethereum: caller});216217 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);218 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', caller);219220 {221 const result = await contract.methods.burn(tokenId).send({from: caller});222223 const event = result.events.Transfer;224 expect(event.address).to.be.equal(collectionAddress);225 expect(event.returnValues.from).to.be.equal(caller);226 expect(event.returnValues.to).to.be.equal('0x0000000000000000000000000000000000000000');227 expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);228 }229 });230231 itEth('Can perform approve()', async ({helper}) => {232 const owner = await helper.eth.createAccountWithBalance(donor);233 const spender = helper.eth.createAccount();234235 const collection = await helper.nft.mintCollection(minter, {});236 const {tokenId} = await collection.mintToken(minter, {Ethereum: owner});237238 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);239 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);240241 {242 const result = await contract.methods.approve(spender, tokenId).send({from: owner});243244 const event = result.events.Approval;245 expect(event.address).to.be.equal(collectionAddress);246 expect(event.returnValues.owner).to.be.equal(owner);247 expect(event.returnValues.approved).to.be.equal(spender);248 expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);249 }250 });251252 itEth('Can perform setApprovalForAll()', async ({helper}) => {253 const owner = await helper.eth.createAccountWithBalance(donor);254 const operator = helper.eth.createAccount();255256 const collection = await helper.nft.mintCollection(minter, {});257258 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);259 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);260261 const approvedBefore = await contract.methods.isApprovedForAll(owner, operator).call();262 expect(approvedBefore).to.be.equal(false);263264 {265 const result = await contract.methods.setApprovalForAll(operator, true).send({from: owner});266267 expect(result.events.ApprovalForAll).to.be.like({268 address: collectionAddress,269 event: 'ApprovalForAll',270 returnValues: {271 owner,272 operator,273 approved: true,274 },275 });276277 const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call();278 expect(approvedAfter).to.be.equal(true);279 }280281 {282 const result = await contract.methods.setApprovalForAll(operator, false).send({from: owner});283284 expect(result.events.ApprovalForAll).to.be.like({285 address: collectionAddress,286 event: 'ApprovalForAll',287 returnValues: {288 owner,289 operator,290 approved: false,291 },292 });293294 const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call();295 expect(approvedAfter).to.be.equal(false);296 }297 });298299 itEth('Can perform burn with ApprovalForAll', async ({helper}) => {300 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});301302 const owner = await helper.eth.createAccountWithBalance(donor);303 const operator = await helper.eth.createAccountWithBalance(donor, 100n);304305 const token = await collection.mintToken(minter, {Ethereum: owner});306307 const address = helper.ethAddress.fromCollectionId(collection.collectionId);308 const contract = helper.ethNativeContract.collection(address, 'nft');309310 {311 await contract.methods.setApprovalForAll(operator, true).send({from: owner});312 const ownerCross = helper.ethCrossAccount.fromAddress(owner);313 const result = await contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: operator});314 const events = result.events.Transfer;315316 expect(events).to.be.like({317 address,318 event: 'Transfer',319 returnValues: {320 from: owner,321 to: '0x0000000000000000000000000000000000000000',322 tokenId: token.tokenId.toString(),323 },324 });325 }326 });327 328 itEth('Can perform transfer with ApprovalForAll', async ({helper}) => {329 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});330331 const owner = await helper.eth.createAccountWithBalance(donor);332 const operator = await helper.eth.createAccountWithBalance(donor);333 const receiver = charlie;334335 const token = await collection.mintToken(minter, {Ethereum: owner});336337 const address = helper.ethAddress.fromCollectionId(collection.collectionId);338 const contract = helper.ethNativeContract.collection(address, 'nft');339340 {341 await contract.methods.setApprovalForAll(operator, true).send({from: owner});342 const ownerCross = helper.ethCrossAccount.fromAddress(owner);343 const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);344 const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: operator});345 const event = result.events.Transfer;346 expect(event).to.be.like({347 address: helper.ethAddress.fromCollectionId(collection.collectionId),348 event: 'Transfer',349 returnValues: {350 from: owner,351 to: helper.address.substrateToEth(receiver.address),352 tokenId: token.tokenId.toString(),353 },354 });355 }356357 expect(await token.getOwner()).to.be.like({Substrate: receiver.address});358 });359360 itEth('Can perform burnFromCross()', async ({helper}) => {361 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});362 const ownerSub = bob;363 const ownerCrossSub = helper.ethCrossAccount.fromKeyringPair(ownerSub);364 const ownerEth = await helper.eth.createAccountWithBalance(donor, 100n);365 const ownerCrossEth = helper.ethCrossAccount.fromAddress(ownerEth);366367 const burnerEth = await helper.eth.createAccountWithBalance(donor, 100n);368 const burnerCrossEth = helper.ethCrossAccount.fromAddress(burnerEth);369370 const token1 = await collection.mintToken(minter, {Substrate: ownerSub.address});371 const token2 = await collection.mintToken(minter, {Ethereum: ownerEth});372373 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);374 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft');375376 // Approve tokens from substrate and ethereum:377 await token1.approve(ownerSub, {Ethereum: burnerEth});378 await collectionEvm.methods.approveCross(burnerCrossEth, token2.tokenId).send({from: ownerEth});379380 // can burnFromCross:381 const result1 = await collectionEvm.methods.burnFromCross(ownerCrossSub, token1.tokenId).send({from: burnerEth});382 const result2 = await collectionEvm.methods.burnFromCross(ownerCrossEth, token2.tokenId).send({from: burnerEth});383 const events1 = result1.events.Transfer;384 const events2 = result2.events.Transfer;385386 // Check events for burnFromCross (substrate and ethereum):387 [388 [events1, token1, helper.address.substrateToEth(ownerSub.address)], 389 [events2, token2, ownerEth],390 ].map(burnData => {391 expect(burnData[0]).to.be.like({392 address: collectionAddress,393 event: 'Transfer',394 returnValues: {395 from: burnData[2],396 to: '0x0000000000000000000000000000000000000000',397 tokenId: burnData[1].tokenId.toString(),398 },399 });400 });401402 expect(await token1.doesExist()).to.be.false;403 expect(await token2.doesExist()).to.be.false;404 });405406 itEth('Can perform approveCross()', async ({helper}) => {407 // arrange: create accounts408 const owner = await helper.eth.createAccountWithBalance(donor, 100n);409 const ownerCross = helper.ethCrossAccount.fromAddress(owner);410 const receiverSub = charlie;411 const recieverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub);412 const receiverEth = await helper.eth.createAccountWithBalance(donor, 100n);413 const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);414415 // arrange: create collection and tokens:416 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});417 const token1 = await collection.mintToken(minter, {Ethereum: owner});418 const token2 = await collection.mintToken(minter, {Ethereum: owner});419420 const collectionEvm = helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft');421422 // Can approveCross substrate and ethereum address:423 const resultSub = await collectionEvm.methods.approveCross(recieverCrossSub, token1.tokenId).send({from: owner});424 const resultEth = await collectionEvm.methods.approveCross(receiverCrossEth, token2.tokenId).send({from: owner});425 const eventSub = resultSub.events.Approval;426 const eventEth = resultEth.events.Approval;427 expect(eventSub).to.be.like({428 address: helper.ethAddress.fromCollectionId(collection.collectionId),429 event: 'Approval',430 returnValues: {431 owner,432 approved: helper.address.substrateToEth(receiverSub.address),433 tokenId: token1.tokenId.toString(),434 },435 });436 expect(eventEth).to.be.like({437 address: helper.ethAddress.fromCollectionId(collection.collectionId),438 event: 'Approval',439 returnValues: {440 owner,441 approved: receiverEth,442 tokenId: token2.tokenId.toString(),443 },444 });445446 // Substrate address can transferFrom approved tokens:447 await helper.nft.transferTokenFrom(receiverSub, collection.collectionId, token1.tokenId, {Ethereum: owner}, {Substrate: receiverSub.address});448 expect(await helper.nft.getTokenOwner(collection.collectionId, token1.tokenId)).to.deep.eq({Substrate: receiverSub.address});449 // Ethereum address can transferFromCross approved tokens:450 await collectionEvm.methods.transferFromCross(ownerCross, receiverCrossEth, token2.tokenId).send({from: receiverEth});451 expect(await helper.nft.getTokenOwner(collection.collectionId, token2.tokenId)).to.deep.eq({Ethereum: receiverEth.toLowerCase()});452 });453454 itEth('Can reaffirm approved address', async ({helper}) => {455 const owner = await helper.eth.createAccountWithBalance(donor, 100n);456 const ownerCrossEth = helper.ethCrossAccount.fromAddress(owner);457 const [receiver1, receiver2] = await helper.arrange.createAccounts([100n, 100n], donor);458 const receiver1Cross = helper.ethCrossAccount.fromKeyringPair(receiver1);459 const receiver2Cross = helper.ethCrossAccount.fromKeyringPair(receiver2);460 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});461 const token1 = await collection.mintToken(minter, {Ethereum: owner});462 const token2 = await collection.mintToken(minter, {Ethereum: owner});463 const collectionEvm = helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft');464465 // Can approve and reaffirm approved address:466 await collectionEvm.methods.approveCross(receiver1Cross, token1.tokenId).send({from: owner});467 await collectionEvm.methods.approveCross(receiver2Cross, token1.tokenId).send({from: owner});468469 // receiver1 cannot transferFrom:470 await expect(helper.nft.transferTokenFrom(receiver1, collection.collectionId, token1.tokenId, {Ethereum: owner}, {Substrate: receiver1.address})).to.be.rejected;471 // receiver2 can transferFrom:472 await helper.nft.transferTokenFrom(receiver2, collection.collectionId, token1.tokenId, {Ethereum: owner}, {Substrate: receiver2.address});473474 // can set approved address to self address to remove approval:475 await collectionEvm.methods.approveCross(receiver1Cross, token2.tokenId).send({from: owner});476 await collectionEvm.methods.approveCross(ownerCrossEth, token2.tokenId).send({from: owner});477478 // receiver1 cannot transfer token anymore:479 await expect(helper.nft.transferTokenFrom(receiver1, collection.collectionId, token2.tokenId, {Ethereum: owner}, {Substrate: receiver1.address})).to.be.rejected;480 });481482 itEth('Can perform transferFrom()', async ({helper}) => {483 const owner = await helper.eth.createAccountWithBalance(donor);484 const spender = await helper.eth.createAccountWithBalance(donor);485 const receiver = helper.eth.createAccount();486487 const collection = await helper.nft.mintCollection(minter, {});488 const {tokenId} = await collection.mintToken(minter, {Ethereum: owner});489490 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);491 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);492493 await contract.methods.approve(spender, tokenId).send({from: owner});494495 {496 const result = await contract.methods.transferFrom(owner, receiver, tokenId).send({from: spender});497498 const event = result.events.Transfer;499 expect(event.address).to.be.equal(collectionAddress);500 expect(event.returnValues.from).to.be.equal(owner);501 expect(event.returnValues.to).to.be.equal(receiver);502 expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);503 }504505 {506 const balance = await contract.methods.balanceOf(receiver).call();507 expect(+balance).to.equal(1);508 }509510 {511 const balance = await contract.methods.balanceOf(owner).call();512 expect(+balance).to.equal(0);513 }514 });515516 itEth('Can perform transferFromCross()', async ({helper}) => {517 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});518519 const [owner, receiver] = await helper.arrange.createAccounts([100n, 100n], donor);520 const spender = await helper.eth.createAccountWithBalance(donor);521522 const token = await collection.mintToken(minter, {Substrate: owner.address});523524 const address = helper.ethAddress.fromCollectionId(collection.collectionId);525 const contract = helper.ethNativeContract.collection(address, 'nft');526527 await token.approve(owner, {Ethereum: spender});528529 {530 const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);531 const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);532 const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender});533 const event = result.events.Transfer;534 expect(event).to.be.like({535 address: helper.ethAddress.fromCollectionId(collection.collectionId),536 event: 'Transfer',537 returnValues: {538 from: helper.address.substrateToEth(owner.address),539 to: helper.address.substrateToEth(receiver.address),540 tokenId: token.tokenId.toString(),541 },542 });543 }544545 expect(await token.getOwner()).to.be.like({Substrate: receiver.address});546 });547548 itEth('Can perform transfer()', async ({helper}) => {549 const collection = await helper.nft.mintCollection(minter, {});550 const owner = await helper.eth.createAccountWithBalance(donor);551 const receiver = helper.eth.createAccount();552553 const {tokenId} = await collection.mintToken(minter, {Ethereum: owner});554555 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);556 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);557558 {559 const result = await contract.methods.transfer(receiver, tokenId).send({from: owner});560561 const event = result.events.Transfer;562 expect(event.address).to.be.equal(collectionAddress);563 expect(event.returnValues.from).to.be.equal(owner);564 expect(event.returnValues.to).to.be.equal(receiver);565 expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);566 }567568 {569 const balance = await contract.methods.balanceOf(owner).call();570 expect(+balance).to.equal(0);571 }572573 {574 const balance = await contract.methods.balanceOf(receiver).call();575 expect(+balance).to.equal(1);576 }577 });578 579 itEth('Can perform transferCross()', async ({helper}) => {580 const collection = await helper.nft.mintCollection(minter, {});581 const owner = await helper.eth.createAccountWithBalance(donor);582 const receiverEth = await helper.eth.createAccountWithBalance(donor);583 const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);584 const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter);585 586 const {tokenId} = await collection.mintToken(minter, {Ethereum: owner});587588 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);589 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);590591 {592 // Can transferCross to ethereum address:593 const result = await collectionEvm.methods.transferCross(receiverCrossEth, tokenId).send({from: owner});594 // Check events:595 const event = result.events.Transfer;596 expect(event.address).to.be.equal(collectionAddress);597 expect(event.returnValues.from).to.be.equal(owner);598 expect(event.returnValues.to).to.be.equal(receiverEth);599 expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);600 601 // owner has balance = 0:602 const ownerBalance = await collectionEvm.methods.balanceOf(owner).call();603 expect(+ownerBalance).to.equal(0);604 // receiver owns token:605 const receiverBalance = await collectionEvm.methods.balanceOf(receiverEth).call();606 expect(+receiverBalance).to.equal(1);607 expect(await helper.nft.getTokenOwner(collection.collectionId, tokenId)).to.deep.eq({Ethereum: receiverEth.toLowerCase()});608 }609 610 {611 // Can transferCross to substrate address:612 const substrateResult = await collectionEvm.methods.transferCross(receiverCrossSub, tokenId).send({from: receiverEth});613 // Check events:614 const event = substrateResult.events.Transfer;615 expect(event.address).to.be.equal(collectionAddress);616 expect(event.returnValues.from).to.be.equal(receiverEth);617 expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(minter.address));618 expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);619 620 // owner has balance = 0:621 const ownerBalance = await collectionEvm.methods.balanceOf(receiverEth).call();622 expect(+ownerBalance).to.equal(0);623 // receiver owns token:624 const receiverBalance = await helper.nft.getTokensByAddress(collection.collectionId, {Substrate: minter.address});625 expect(receiverBalance).to.contain(tokenId);626 }627 });628629 ['transfer', 'transferCross'].map(testCase => itEth(`Cannot ${testCase} non-owned token`, async ({helper}) => {630 const sender = await helper.eth.createAccountWithBalance(donor);631 const tokenOwner = await helper.eth.createAccountWithBalance(donor);632 const receiverSub = minter;633 const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter);634635 const collection = await helper.nft.mintCollection(minter, {});636 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);637 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', sender);638639 await collection.mintToken(minter, {Ethereum: sender});640 const nonSendersToken = await collection.mintToken(minter, {Ethereum: tokenOwner});641642 // Cannot transferCross someone else's token:643 const receiver = testCase === 'transfer' ? helper.address.substrateToEth(receiverSub.address) : receiverCrossSub;644 await expect(collectionEvm.methods[testCase](receiver, nonSendersToken.tokenId).send({from: sender})).to.be.rejected;645 // Cannot transfer token if it does not exist:646 await expect(collectionEvm.methods[testCase](receiver, 999999).send({from: sender})).to.be.rejected;647 }));648});649650describe('NFT: Fees', () => {651 let donor: IKeyringPair;652 let alice: IKeyringPair;653 let bob: IKeyringPair;654 let charlie: IKeyringPair;655656 before(async function() {657 await usingEthPlaygrounds(async (helper, privateKey) => {658 donor = await privateKey({filename: __filename});659 [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);660 });661 });662663 itEth('approve() call fee is less than 0.2UNQ', async ({helper}) => {664 const owner = await helper.eth.createAccountWithBalance(donor);665 const spender = helper.eth.createAccount();666667 const collection = await helper.nft.mintCollection(alice, {});668 const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});669670 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner);671672 const cost = await helper.eth.recordCallFee(owner, () => contract.methods.approve(spender, tokenId).send({from: owner}));673 expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));674 });675676 itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {677 const owner = await helper.eth.createAccountWithBalance(donor);678 const spender = await helper.eth.createAccountWithBalance(donor);679680 const collection = await helper.nft.mintCollection(alice, {});681 const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});682683 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner);684685 await contract.methods.approve(spender, tokenId).send({from: owner});686687 const cost = await helper.eth.recordCallFee(spender, () => contract.methods.transferFrom(owner, spender, tokenId).send({from: spender}));688 expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));689 });690691 itEth('Can perform transferFromCross()', async ({helper}) => {692 const collectionMinter = alice;693 const owner = bob;694 const receiver = charlie;695 const collection = await helper.nft.mintCollection(collectionMinter, {name: 'A', description: 'B', tokenPrefix: 'C'});696697 const spender = await helper.eth.createAccountWithBalance(donor, 100n);698699 const token = await collection.mintToken(collectionMinter, {Substrate: owner.address});700701 const address = helper.ethAddress.fromCollectionId(collection.collectionId);702 const contract = helper.ethNativeContract.collection(address, 'nft');703704 await token.approve(owner, {Ethereum: spender});705706 {707 const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);708 const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);709 const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender});710 const event = result.events.Transfer;711 expect(event).to.be.like({712 address: helper.ethAddress.fromCollectionId(collection.collectionId),713 event: 'Transfer',714 returnValues: {715 from: helper.address.substrateToEth(owner.address),716 to: helper.address.substrateToEth(receiver.address),717 tokenId: token.tokenId.toString(),718 },719 });720 }721722 expect(await token.getOwner()).to.be.like({Substrate: receiver.address});723 });724725 itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {726 const owner = await helper.eth.createAccountWithBalance(donor);727 const receiver = helper.eth.createAccount();728729 const collection = await helper.nft.mintCollection(alice, {});730 const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});731732 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner);733734 const cost = await helper.eth.recordCallFee(owner, () => contract.methods.transfer(receiver, tokenId).send({from: owner}));735 expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));736 });737});738739describe('NFT: Substrate calls', () => {740 let donor: IKeyringPair;741 let alice: IKeyringPair;742743 before(async function() {744 await usingEthPlaygrounds(async (helper, privateKey) => {745 donor = await privateKey({filename: __filename});746 [alice] = await helper.arrange.createAccounts([20n], donor);747 });748 });749750 itEth('Events emitted for mint()', async ({helper}) => {751 const collection = await helper.nft.mintCollection(alice, {});752 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);753 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');754755 const events: any = [];756 contract.events.allEvents((_: any, event: any) => {757 events.push(event);758 });759760 const {tokenId} = await collection.mintToken(alice);761 if (events.length == 0) await helper.wait.newBlocks(1);762 const event = events[0];763764 expect(event.event).to.be.equal('Transfer');765 expect(event.address).to.be.equal(collectionAddress);766 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');767 expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(alice.address));768 expect(event.returnValues.tokenId).to.be.equal(tokenId.toString());769 });770771 itEth('Events emitted for burn()', async ({helper}) => {772 const collection = await helper.nft.mintCollection(alice, {});773 const token = await collection.mintToken(alice);774775 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);776 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');777778 const events: any = [];779 contract.events.allEvents((_: any, event: any) => {780 events.push(event);781 });782783 await token.burn(alice);784 if (events.length == 0) await helper.wait.newBlocks(1);785 const event = events[0];786787 expect(event.event).to.be.equal('Transfer');788 expect(event.address).to.be.equal(collectionAddress);789 expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));790 expect(event.returnValues.to).to.be.equal('0x0000000000000000000000000000000000000000');791 expect(event.returnValues.tokenId).to.be.equal(token.tokenId.toString());792 });793794 itEth('Events emitted for approve()', async ({helper}) => {795 const receiver = helper.eth.createAccount();796797 const collection = await helper.nft.mintCollection(alice, {});798 const token = await collection.mintToken(alice);799800 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);801 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');802803 const events: any = [];804 contract.events.allEvents((_: any, event: any) => {805 events.push(event);806 });807808 await token.approve(alice, {Ethereum: receiver});809 if (events.length == 0) await helper.wait.newBlocks(1);810 const event = events[0];811812 expect(event.event).to.be.equal('Approval');813 expect(event.address).to.be.equal(collectionAddress);814 expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));815 expect(event.returnValues.approved).to.be.equal(receiver);816 expect(event.returnValues.tokenId).to.be.equal(token.tokenId.toString());817 });818819 itEth('Events emitted for transferFrom()', async ({helper}) => {820 const [bob] = await helper.arrange.createAccounts([10n], donor);821 const receiver = helper.eth.createAccount();822823 const collection = await helper.nft.mintCollection(alice, {});824 const token = await collection.mintToken(alice);825 await token.approve(alice, {Substrate: bob.address});826827 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);828 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');829830 const events: any = [];831 contract.events.allEvents((_: any, event: any) => {832 events.push(event);833 });834835 await token.transferFrom(bob, {Substrate: alice.address}, {Ethereum: receiver});836837 if (events.length == 0) await helper.wait.newBlocks(1);838 const event = events[0];839840 expect(event.address).to.be.equal(collectionAddress);841 expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));842 expect(event.returnValues.to).to.be.equal(receiver);843 expect(event.returnValues.tokenId).to.be.equal(`${token.tokenId}`);844 });845846 itEth('Events emitted for transfer()', async ({helper}) => {847 const receiver = helper.eth.createAccount();848849 const collection = await helper.nft.mintCollection(alice, {});850 const token = await collection.mintToken(alice);851852 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);853 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');854855 const events: any = [];856 contract.events.allEvents((_: any, event: any) => {857 events.push(event);858 });859860 await token.transfer(alice, {Ethereum: receiver});861862 if (events.length == 0) await helper.wait.newBlocks(1);863 const event = events[0];864865 expect(event.address).to.be.equal(collectionAddress);866 expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));867 expect(event.returnValues.to).to.be.equal(receiver);868 expect(event.returnValues.tokenId).to.be.equal(`${token.tokenId}`);869 });870});871872describe('Common metadata', () => {873 let donor: IKeyringPair;874 let alice: IKeyringPair;875876 before(async function() {877 await usingEthPlaygrounds(async (helper, privateKey) => {878 donor = await privateKey({filename: __filename});879 [alice] = await helper.arrange.createAccounts([20n], donor);880 });881 });882883 itEth('Returns collection name', async ({helper}) => {884 const caller = await helper.eth.createAccountWithBalance(donor);885 const tokenPropertyPermissions = [{886 key: 'URI',887 permission: {888 mutable: true,889 collectionAdmin: true,890 tokenOwner: false,891 },892 }];893 const collection = await helper.nft.mintCollection(894 alice,895 {896 name: 'oh River',897 tokenPrefix: 'CHANGE',898 properties: [{key: 'ERC721Metadata', value: '1'}],899 tokenPropertyPermissions,900 },901 );902903 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);904 const name = await contract.methods.name().call();905 expect(name).to.equal('oh River');906 });907908 itEth('Returns symbol name', async ({helper}) => {909 const caller = await helper.eth.createAccountWithBalance(donor);910 const tokenPropertyPermissions = [{911 key: 'URI',912 permission: {913 mutable: true,914 collectionAdmin: true,915 tokenOwner: false,916 },917 }];918 const collection = await helper.nft.mintCollection(919 alice,920 {921 name: 'oh River',922 tokenPrefix: 'CHANGE',923 properties: [{key: 'ERC721Metadata', value: '1'}],924 tokenPropertyPermissions,925 },926 );927928 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);929 const symbol = await contract.methods.symbol().call();930 expect(symbol).to.equal('CHANGE');931 });932});933934describe('Negative tests', () => {935 let donor: IKeyringPair;936 let minter: IKeyringPair;937 let alice: IKeyringPair;938 let bob: IKeyringPair;939940 before(async function() {941 await usingEthPlaygrounds(async (helper, privateKey) => {942 donor = await privateKey({filename: __filename});943 [minter, alice, bob] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);944 });945 });946947 itEth('[negative] Cant perform burn without approval', async ({helper}) => {948 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});949950 const owner = bob;951 const spender = await helper.eth.createAccountWithBalance(donor, 100n);952953 const token = await collection.mintToken(minter, {Substrate: owner.address});954955 const address = helper.ethAddress.fromCollectionId(collection.collectionId);956 const contract = helper.ethNativeContract.collection(address, 'nft');957958 {959 const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);960 await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected;961 }962 });963964 itEth('[negative] Cant perform transfer without approval', async ({helper}) => {965 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});966 const owner = bob;967 const receiver = alice;968969 const spender = await helper.eth.createAccountWithBalance(donor, 100n);970971 const token = await collection.mintToken(minter, {Substrate: owner.address});972973 const address = helper.ethAddress.fromCollectionId(collection.collectionId);974 const contract = helper.ethNativeContract.collection(address, 'nft');975976 {977 const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);978 const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);979 await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected;980 }981 });982});1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {itEth, usingEthPlaygrounds, expect, EthUniqueHelper} from './util';18import {IKeyringPair} from '@polkadot/types/types';19import {Contract} from 'web3-eth-contract';202122describe('NFT: Information getting', () => {23 let donor: IKeyringPair;24 let alice: IKeyringPair;2526 before(async function() {27 await usingEthPlaygrounds(async (helper, privateKey) => {28 donor = await privateKey({filename: __filename});29 [alice] = await helper.arrange.createAccounts([10n], donor);30 });31 });3233 itEth('totalSupply', async ({helper}) => {34 const collection = await helper.nft.mintCollection(alice, {});35 await collection.mintToken(alice);3637 const caller = await helper.eth.createAccountWithBalance(donor);3839 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);40 const totalSupply = await contract.methods.totalSupply().call();4142 expect(totalSupply).to.equal('1');43 });4445 itEth('balanceOf', async ({helper}) => {46 const collection = await helper.nft.mintCollection(alice, {});47 const caller = await helper.eth.createAccountWithBalance(donor);4849 await collection.mintToken(alice, {Ethereum: caller});50 await collection.mintToken(alice, {Ethereum: caller});51 await collection.mintToken(alice, {Ethereum: caller});5253 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);54 const balance = await contract.methods.balanceOf(caller).call();5556 expect(balance).to.equal('3');57 });5859 itEth('ownerOf', async ({helper}) => {60 const collection = await helper.nft.mintCollection(alice, {});61 const caller = await helper.eth.createAccountWithBalance(donor);6263 const token = await collection.mintToken(alice, {Ethereum: caller});6465 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);6667 const owner = await contract.methods.ownerOf(token.tokenId).call();6869 expect(owner).to.equal(caller);70 });7172 itEth('name/symbol is available regardless of ERC721Metadata support', async ({helper}) => {73 const collection = await helper.nft.mintCollection(alice, {name: 'test', tokenPrefix: 'TEST'});74 const caller = helper.eth.createAccount();7576 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);7778 expect(await contract.methods.name().call()).to.equal('test');79 expect(await contract.methods.symbol().call()).to.equal('TEST');80 });81});8283describe('Check ERC721 token URI for NFT', () => {84 let donor: IKeyringPair;8586 before(async function() {87 await usingEthPlaygrounds(async (_helper, privateKey) => {88 donor = await privateKey({filename: __filename});89 });90 });9192 async function setup(helper: EthUniqueHelper, baseUri: string, propertyKey?: string, propertyValue?: string): Promise<{contract: Contract, nextTokenId: string}> {93 const owner = await helper.eth.createAccountWithBalance(donor);94 const receiver = helper.eth.createAccount();9596 const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Mint collection', 'a', 'b', baseUri);97 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);9899 const result = await contract.methods.mint(receiver).send();100 const tokenId = result.events.Transfer.returnValues.tokenId;101 expect(tokenId).to.be.equal('1');102103 if (propertyKey && propertyValue) {104 // Set URL or suffix105 await contract.methods.setProperties(tokenId, [{key: propertyKey, value: Buffer.from(propertyValue)}]).send();106 }107108 const event = result.events.Transfer;109 expect(event.address).to.be.equal(collectionAddress);110 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');111 expect(event.returnValues.to).to.be.equal(receiver);112 expect(event.returnValues.tokenId).to.be.equal(tokenId);113114 return {contract, nextTokenId: tokenId};115 }116117 itEth('Empty tokenURI', async ({helper}) => {118 const {contract, nextTokenId} = await setup(helper, '');119 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('');120 });121122 itEth('TokenURI from url', async ({helper}) => {123 const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URI', 'Token URI');124 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Token URI');125 });126127 itEth('TokenURI from baseURI', async ({helper}) => {128 const {contract, nextTokenId} = await setup(helper, 'BaseURI_');129 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_');130 });131132 itEth('TokenURI from baseURI + suffix', async ({helper}) => {133 const suffix = '/some/suffix';134 const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URISuffix', suffix);135 expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + suffix);136 });137});138139describe('NFT: Plain calls', () => {140 let donor: IKeyringPair;141 let minter: IKeyringPair;142 let bob: IKeyringPair;143 let charlie: IKeyringPair;144145 before(async function() {146 await usingEthPlaygrounds(async (helper, privateKey) => {147 donor = await privateKey({filename: __filename});148 [minter, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);149 });150 });151152 itEth('Can perform mint() & get crossOwner()', async ({helper}) => {153 const owner = await helper.eth.createAccountWithBalance(donor);154 const receiver = helper.eth.createAccount();155156 const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Mint collection', '6', '6', '');157 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);158159 const result = await contract.methods.mintWithTokenURI(receiver, 'Test URI').send();160 const tokenId = result.events.Transfer.returnValues.tokenId;161 expect(tokenId).to.be.equal('1');162163 const event = result.events.Transfer;164 expect(event.address).to.be.equal(collectionAddress);165 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');166 expect(event.returnValues.to).to.be.equal(receiver);167168 expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');169 console.log(await contract.methods.crossOwnerOf(tokenId).call());170 expect(await contract.methods.crossOwnerOf(tokenId).call()).to.be.like([receiver, '0']);171 // TODO: this wont work right now, need release 919000 first172 // await helper.methods.setOffchainSchema(collectionIdAddress, 'https://offchain-service.local/token-info/{id}').send();173 // const tokenUri = await contract.methods.tokenURI(nextTokenId).call();174 // expect(tokenUri).to.be.equal(`https://offchain-service.local/token-info/${nextTokenId}`);175 });176177 //TODO: CORE-302 add eth methods178 itEth.skip('Can perform mintBulk()', async ({helper}) => {179 const caller = await helper.eth.createAccountWithBalance(donor);180 const receiver = helper.eth.createAccount();181182 const collection = await helper.nft.mintCollection(minter);183 await collection.addAdmin(minter, {Ethereum: caller});184185 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);186 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', caller);187 {188 const bulkSize = 3;189 const nextTokenId = await contract.methods.nextTokenId().call();190 expect(nextTokenId).to.be.equal('1');191 const result = await contract.methods.mintBulkWithTokenURI(192 receiver,193 Array.from({length: bulkSize}, (_, i) => (194 [+nextTokenId + i, `Test URI ${i}`]195 )),196 ).send({from: caller});197198 const events = result.events.Transfer.sort((a: any, b: any) => +a.returnValues.tokenId - b.returnValues.tokenId);199 for (let i = 0; i < bulkSize; i++) {200 const event = events[i];201 expect(event.address).to.equal(collectionAddress);202 expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');203 expect(event.returnValues.to).to.equal(receiver);204 expect(event.returnValues.tokenId).to.equal(`${+nextTokenId+i}`);205206 expect(await contract.methods.tokenURI(+nextTokenId + i).call()).to.be.equal(`Test URI ${i}`);207 }208 }209 });210211 itEth('Can perform burn()', async ({helper}) => {212 const caller = await helper.eth.createAccountWithBalance(donor);213214 const collection = await helper.nft.mintCollection(minter, {});215 const {tokenId} = await collection.mintToken(minter, {Ethereum: caller});216217 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);218 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', caller);219220 {221 const result = await contract.methods.burn(tokenId).send({from: caller});222223 const event = result.events.Transfer;224 expect(event.address).to.be.equal(collectionAddress);225 expect(event.returnValues.from).to.be.equal(caller);226 expect(event.returnValues.to).to.be.equal('0x0000000000000000000000000000000000000000');227 expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);228 }229 });230231 itEth('Can perform approve()', async ({helper}) => {232 const owner = await helper.eth.createAccountWithBalance(donor);233 const spender = helper.eth.createAccount();234235 const collection = await helper.nft.mintCollection(minter, {});236 const {tokenId} = await collection.mintToken(minter, {Ethereum: owner});237238 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);239 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);240241 {242 const result = await contract.methods.approve(spender, tokenId).send({from: owner});243244 const event = result.events.Approval;245 expect(event.address).to.be.equal(collectionAddress);246 expect(event.returnValues.owner).to.be.equal(owner);247 expect(event.returnValues.approved).to.be.equal(spender);248 expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);249 }250 });251252 itEth('Can perform setApprovalForAll()', async ({helper}) => {253 const owner = await helper.eth.createAccountWithBalance(donor);254 const operator = helper.eth.createAccount();255256 const collection = await helper.nft.mintCollection(minter, {});257258 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);259 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);260261 const approvedBefore = await contract.methods.isApprovedForAll(owner, operator).call();262 expect(approvedBefore).to.be.equal(false);263264 {265 const result = await contract.methods.setApprovalForAll(operator, true).send({from: owner});266267 expect(result.events.ApprovalForAll).to.be.like({268 address: collectionAddress,269 event: 'ApprovalForAll',270 returnValues: {271 owner,272 operator,273 approved: true,274 },275 });276277 const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call();278 expect(approvedAfter).to.be.equal(true);279 }280281 {282 const result = await contract.methods.setApprovalForAll(operator, false).send({from: owner});283284 expect(result.events.ApprovalForAll).to.be.like({285 address: collectionAddress,286 event: 'ApprovalForAll',287 returnValues: {288 owner,289 operator,290 approved: false,291 },292 });293294 const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call();295 expect(approvedAfter).to.be.equal(false);296 }297 });298299 itEth('Can perform burn with ApprovalForAll', async ({helper}) => {300 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});301302 const owner = await helper.eth.createAccountWithBalance(donor);303 const operator = await helper.eth.createAccountWithBalance(donor, 100n);304305 const token = await collection.mintToken(minter, {Ethereum: owner});306307 const address = helper.ethAddress.fromCollectionId(collection.collectionId);308 const contract = helper.ethNativeContract.collection(address, 'nft');309310 {311 await contract.methods.setApprovalForAll(operator, true).send({from: owner});312 const ownerCross = helper.ethCrossAccount.fromAddress(owner);313 const result = await contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: operator});314 const events = result.events.Transfer;315316 expect(events).to.be.like({317 address,318 event: 'Transfer',319 returnValues: {320 from: owner,321 to: '0x0000000000000000000000000000000000000000',322 tokenId: token.tokenId.toString(),323 },324 });325 }326 });327 328 itEth('Can perform transfer with ApprovalForAll', async ({helper}) => {329 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});330331 const owner = await helper.eth.createAccountWithBalance(donor);332 const operator = await helper.eth.createAccountWithBalance(donor);333 const receiver = charlie;334335 const token = await collection.mintToken(minter, {Ethereum: owner});336337 const address = helper.ethAddress.fromCollectionId(collection.collectionId);338 const contract = helper.ethNativeContract.collection(address, 'nft');339340 {341 await contract.methods.setApprovalForAll(operator, true).send({from: owner});342 const ownerCross = helper.ethCrossAccount.fromAddress(owner);343 const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);344 const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: operator});345 const event = result.events.Transfer;346 expect(event).to.be.like({347 address: helper.ethAddress.fromCollectionId(collection.collectionId),348 event: 'Transfer',349 returnValues: {350 from: owner,351 to: helper.address.substrateToEth(receiver.address),352 tokenId: token.tokenId.toString(),353 },354 });355 }356357 expect(await token.getOwner()).to.be.like({Substrate: receiver.address});358 });359360 itEth('Can perform burnFromCross()', async ({helper}) => {361 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});362 const ownerSub = bob;363 const ownerCrossSub = helper.ethCrossAccount.fromKeyringPair(ownerSub);364 const ownerEth = await helper.eth.createAccountWithBalance(donor, 100n);365 const ownerCrossEth = helper.ethCrossAccount.fromAddress(ownerEth);366367 const burnerEth = await helper.eth.createAccountWithBalance(donor, 100n);368 const burnerCrossEth = helper.ethCrossAccount.fromAddress(burnerEth);369370 const token1 = await collection.mintToken(minter, {Substrate: ownerSub.address});371 const token2 = await collection.mintToken(minter, {Ethereum: ownerEth});372373 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);374 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft');375376 // Approve tokens from substrate and ethereum:377 await token1.approve(ownerSub, {Ethereum: burnerEth});378 await collectionEvm.methods.approveCross(burnerCrossEth, token2.tokenId).send({from: ownerEth});379380 // can burnFromCross:381 const result1 = await collectionEvm.methods.burnFromCross(ownerCrossSub, token1.tokenId).send({from: burnerEth});382 const result2 = await collectionEvm.methods.burnFromCross(ownerCrossEth, token2.tokenId).send({from: burnerEth});383 const events1 = result1.events.Transfer;384 const events2 = result2.events.Transfer;385386 // Check events for burnFromCross (substrate and ethereum):387 [388 [events1, token1, helper.address.substrateToEth(ownerSub.address)], 389 [events2, token2, ownerEth],390 ].map(burnData => {391 expect(burnData[0]).to.be.like({392 address: collectionAddress,393 event: 'Transfer',394 returnValues: {395 from: burnData[2],396 to: '0x0000000000000000000000000000000000000000',397 tokenId: burnData[1].tokenId.toString(),398 },399 });400 });401402 expect(await token1.doesExist()).to.be.false;403 expect(await token2.doesExist()).to.be.false;404 });405406 itEth('Can perform approveCross()', async ({helper}) => {407 // arrange: create accounts408 const owner = await helper.eth.createAccountWithBalance(donor, 100n);409 const ownerCross = helper.ethCrossAccount.fromAddress(owner);410 const receiverSub = charlie;411 const recieverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub);412 const receiverEth = await helper.eth.createAccountWithBalance(donor, 100n);413 const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);414415 // arrange: create collection and tokens:416 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});417 const token1 = await collection.mintToken(minter, {Ethereum: owner});418 const token2 = await collection.mintToken(minter, {Ethereum: owner});419420 const collectionEvm = helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft');421422 // Can approveCross substrate and ethereum address:423 const resultSub = await collectionEvm.methods.approveCross(recieverCrossSub, token1.tokenId).send({from: owner});424 const resultEth = await collectionEvm.methods.approveCross(receiverCrossEth, token2.tokenId).send({from: owner});425 const eventSub = resultSub.events.Approval;426 const eventEth = resultEth.events.Approval;427 expect(eventSub).to.be.like({428 address: helper.ethAddress.fromCollectionId(collection.collectionId),429 event: 'Approval',430 returnValues: {431 owner,432 approved: helper.address.substrateToEth(receiverSub.address),433 tokenId: token1.tokenId.toString(),434 },435 });436 expect(eventEth).to.be.like({437 address: helper.ethAddress.fromCollectionId(collection.collectionId),438 event: 'Approval',439 returnValues: {440 owner,441 approved: receiverEth,442 tokenId: token2.tokenId.toString(),443 },444 });445446 // Substrate address can transferFrom approved tokens:447 await helper.nft.transferTokenFrom(receiverSub, collection.collectionId, token1.tokenId, {Ethereum: owner}, {Substrate: receiverSub.address});448 expect(await helper.nft.getTokenOwner(collection.collectionId, token1.tokenId)).to.deep.eq({Substrate: receiverSub.address});449 // Ethereum address can transferFromCross approved tokens:450 await collectionEvm.methods.transferFromCross(ownerCross, receiverCrossEth, token2.tokenId).send({from: receiverEth});451 expect(await helper.nft.getTokenOwner(collection.collectionId, token2.tokenId)).to.deep.eq({Ethereum: receiverEth.toLowerCase()});452 });453454 itEth('Can reaffirm approved address', async ({helper}) => {455 const owner = await helper.eth.createAccountWithBalance(donor, 100n);456 const ownerCrossEth = helper.ethCrossAccount.fromAddress(owner);457 const [receiver1, receiver2] = await helper.arrange.createAccounts([100n, 100n], donor);458 const receiver1Cross = helper.ethCrossAccount.fromKeyringPair(receiver1);459 const receiver2Cross = helper.ethCrossAccount.fromKeyringPair(receiver2);460 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});461 const token1 = await collection.mintToken(minter, {Ethereum: owner});462 const token2 = await collection.mintToken(minter, {Ethereum: owner});463 const collectionEvm = helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft');464465 // Can approve and reaffirm approved address:466 await collectionEvm.methods.approveCross(receiver1Cross, token1.tokenId).send({from: owner});467 await collectionEvm.methods.approveCross(receiver2Cross, token1.tokenId).send({from: owner});468469 // receiver1 cannot transferFrom:470 await expect(helper.nft.transferTokenFrom(receiver1, collection.collectionId, token1.tokenId, {Ethereum: owner}, {Substrate: receiver1.address})).to.be.rejected;471 // receiver2 can transferFrom:472 await helper.nft.transferTokenFrom(receiver2, collection.collectionId, token1.tokenId, {Ethereum: owner}, {Substrate: receiver2.address});473474 // can set approved address to self address to remove approval:475 await collectionEvm.methods.approveCross(receiver1Cross, token2.tokenId).send({from: owner});476 await collectionEvm.methods.approveCross(ownerCrossEth, token2.tokenId).send({from: owner});477478 // receiver1 cannot transfer token anymore:479 await expect(helper.nft.transferTokenFrom(receiver1, collection.collectionId, token2.tokenId, {Ethereum: owner}, {Substrate: receiver1.address})).to.be.rejected;480 });481482 itEth('Can perform transferFrom()', async ({helper}) => {483 const owner = await helper.eth.createAccountWithBalance(donor);484 const spender = await helper.eth.createAccountWithBalance(donor);485 const receiver = helper.eth.createAccount();486487 const collection = await helper.nft.mintCollection(minter, {});488 const {tokenId} = await collection.mintToken(minter, {Ethereum: owner});489490 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);491 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);492493 await contract.methods.approve(spender, tokenId).send({from: owner});494495 {496 const result = await contract.methods.transferFrom(owner, receiver, tokenId).send({from: spender});497498 const event = result.events.Transfer;499 expect(event.address).to.be.equal(collectionAddress);500 expect(event.returnValues.from).to.be.equal(owner);501 expect(event.returnValues.to).to.be.equal(receiver);502 expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);503 }504505 {506 const balance = await contract.methods.balanceOf(receiver).call();507 expect(+balance).to.equal(1);508 }509510 {511 const balance = await contract.methods.balanceOf(owner).call();512 expect(+balance).to.equal(0);513 }514 });515516 itEth('Can perform transferFromCross()', async ({helper}) => {517 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});518519 const [owner, receiver] = await helper.arrange.createAccounts([100n, 100n], donor);520 const spender = await helper.eth.createAccountWithBalance(donor);521522 const token = await collection.mintToken(minter, {Substrate: owner.address});523524 const address = helper.ethAddress.fromCollectionId(collection.collectionId);525 const contract = helper.ethNativeContract.collection(address, 'nft');526527 await token.approve(owner, {Ethereum: spender});528529 {530 const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);531 const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);532 const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender});533 const event = result.events.Transfer;534 expect(event).to.be.like({535 address: helper.ethAddress.fromCollectionId(collection.collectionId),536 event: 'Transfer',537 returnValues: {538 from: helper.address.substrateToEth(owner.address),539 to: helper.address.substrateToEth(receiver.address),540 tokenId: token.tokenId.toString(),541 },542 });543 }544545 expect(await token.getOwner()).to.be.like({Substrate: receiver.address});546 });547548 itEth('Can perform transfer()', async ({helper}) => {549 const collection = await helper.nft.mintCollection(minter, {});550 const owner = await helper.eth.createAccountWithBalance(donor);551 const receiver = helper.eth.createAccount();552553 const {tokenId} = await collection.mintToken(minter, {Ethereum: owner});554555 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);556 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);557558 {559 const result = await contract.methods.transfer(receiver, tokenId).send({from: owner});560561 const event = result.events.Transfer;562 expect(event.address).to.be.equal(collectionAddress);563 expect(event.returnValues.from).to.be.equal(owner);564 expect(event.returnValues.to).to.be.equal(receiver);565 expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);566 }567568 {569 const balance = await contract.methods.balanceOf(owner).call();570 expect(+balance).to.equal(0);571 }572573 {574 const balance = await contract.methods.balanceOf(receiver).call();575 expect(+balance).to.equal(1);576 }577 });578 579 itEth('Can perform transferCross()', async ({helper}) => {580 const collection = await helper.nft.mintCollection(minter, {});581 const owner = await helper.eth.createAccountWithBalance(donor);582 const receiverEth = await helper.eth.createAccountWithBalance(donor);583 const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);584 const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter);585 586 const {tokenId} = await collection.mintToken(minter, {Ethereum: owner});587588 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);589 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);590591 {592 // Can transferCross to ethereum address:593 const result = await collectionEvm.methods.transferCross(receiverCrossEth, tokenId).send({from: owner});594 // Check events:595 const event = result.events.Transfer;596 expect(event.address).to.be.equal(collectionAddress);597 expect(event.returnValues.from).to.be.equal(owner);598 expect(event.returnValues.to).to.be.equal(receiverEth);599 expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);600 601 // owner has balance = 0:602 const ownerBalance = await collectionEvm.methods.balanceOf(owner).call();603 expect(+ownerBalance).to.equal(0);604 // receiver owns token:605 const receiverBalance = await collectionEvm.methods.balanceOf(receiverEth).call();606 expect(+receiverBalance).to.equal(1);607 expect(await helper.nft.getTokenOwner(collection.collectionId, tokenId)).to.deep.eq({Ethereum: receiverEth.toLowerCase()});608 }609 610 {611 // Can transferCross to substrate address:612 const substrateResult = await collectionEvm.methods.transferCross(receiverCrossSub, tokenId).send({from: receiverEth});613 // Check events:614 const event = substrateResult.events.Transfer;615 expect(event.address).to.be.equal(collectionAddress);616 expect(event.returnValues.from).to.be.equal(receiverEth);617 expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(minter.address));618 expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);619 620 // owner has balance = 0:621 const ownerBalance = await collectionEvm.methods.balanceOf(receiverEth).call();622 expect(+ownerBalance).to.equal(0);623 // receiver owns token:624 const receiverBalance = await helper.nft.getTokensByAddress(collection.collectionId, {Substrate: minter.address});625 expect(receiverBalance).to.contain(tokenId);626 }627 });628629 ['transfer', 'transferCross'].map(testCase => itEth(`Cannot ${testCase} non-owned token`, async ({helper}) => {630 const sender = await helper.eth.createAccountWithBalance(donor);631 const tokenOwner = await helper.eth.createAccountWithBalance(donor);632 const receiverSub = minter;633 const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter);634635 const collection = await helper.nft.mintCollection(minter, {});636 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);637 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', sender);638639 await collection.mintToken(minter, {Ethereum: sender});640 const nonSendersToken = await collection.mintToken(minter, {Ethereum: tokenOwner});641642 // Cannot transferCross someone else's token:643 const receiver = testCase === 'transfer' ? helper.address.substrateToEth(receiverSub.address) : receiverCrossSub;644 await expect(collectionEvm.methods[testCase](receiver, nonSendersToken.tokenId).send({from: sender})).to.be.rejected;645 // Cannot transfer token if it does not exist:646 await expect(collectionEvm.methods[testCase](receiver, 999999).send({from: sender})).to.be.rejected;647 }));648});649650describe('NFT: Fees', () => {651 let donor: IKeyringPair;652 let alice: IKeyringPair;653 let bob: IKeyringPair;654 let charlie: IKeyringPair;655656 before(async function() {657 await usingEthPlaygrounds(async (helper, privateKey) => {658 donor = await privateKey({filename: __filename});659 [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);660 });661 });662663 itEth('approve() call fee is less than 0.2UNQ', async ({helper}) => {664 const owner = await helper.eth.createAccountWithBalance(donor);665 const spender = helper.eth.createAccount();666667 const collection = await helper.nft.mintCollection(alice, {});668 const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});669670 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner);671672 const cost = await helper.eth.recordCallFee(owner, () => contract.methods.approve(spender, tokenId).send({from: owner}));673 expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));674 });675676 itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {677 const owner = await helper.eth.createAccountWithBalance(donor);678 const spender = await helper.eth.createAccountWithBalance(donor);679680 const collection = await helper.nft.mintCollection(alice, {});681 const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});682683 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner);684685 await contract.methods.approve(spender, tokenId).send({from: owner});686687 const cost = await helper.eth.recordCallFee(spender, () => contract.methods.transferFrom(owner, spender, tokenId).send({from: spender}));688 expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));689 });690691 itEth('Can perform transferFromCross()', async ({helper}) => {692 const collectionMinter = alice;693 const owner = bob;694 const receiver = charlie;695 const collection = await helper.nft.mintCollection(collectionMinter, {name: 'A', description: 'B', tokenPrefix: 'C'});696697 const spender = await helper.eth.createAccountWithBalance(donor, 100n);698699 const token = await collection.mintToken(collectionMinter, {Substrate: owner.address});700701 const address = helper.ethAddress.fromCollectionId(collection.collectionId);702 const contract = helper.ethNativeContract.collection(address, 'nft');703704 await token.approve(owner, {Ethereum: spender});705706 {707 const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);708 const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);709 const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender});710 const event = result.events.Transfer;711 expect(event).to.be.like({712 address: helper.ethAddress.fromCollectionId(collection.collectionId),713 event: 'Transfer',714 returnValues: {715 from: helper.address.substrateToEth(owner.address),716 to: helper.address.substrateToEth(receiver.address),717 tokenId: token.tokenId.toString(),718 },719 });720 }721722 expect(await token.getOwner()).to.be.like({Substrate: receiver.address});723 });724725 itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {726 const owner = await helper.eth.createAccountWithBalance(donor);727 const receiver = helper.eth.createAccount();728729 const collection = await helper.nft.mintCollection(alice, {});730 const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});731732 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner);733734 const cost = await helper.eth.recordCallFee(owner, () => contract.methods.transfer(receiver, tokenId).send({from: owner}));735 expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));736 });737});738739describe('NFT: Substrate calls', () => {740 let donor: IKeyringPair;741 let alice: IKeyringPair;742743 before(async function() {744 await usingEthPlaygrounds(async (helper, privateKey) => {745 donor = await privateKey({filename: __filename});746 [alice] = await helper.arrange.createAccounts([20n], donor);747 });748 });749750 itEth('Events emitted for mint()', async ({helper}) => {751 const collection = await helper.nft.mintCollection(alice, {});752 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);753 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');754755 const events: any = [];756 contract.events.allEvents((_: any, event: any) => {757 events.push(event);758 });759760 const {tokenId} = await collection.mintToken(alice);761 if (events.length == 0) await helper.wait.newBlocks(1);762 const event = events[0];763764 expect(event.event).to.be.equal('Transfer');765 expect(event.address).to.be.equal(collectionAddress);766 expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');767 expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(alice.address));768 expect(event.returnValues.tokenId).to.be.equal(tokenId.toString());769 });770771 itEth('Events emitted for burn()', async ({helper}) => {772 const collection = await helper.nft.mintCollection(alice, {});773 const token = await collection.mintToken(alice);774775 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);776 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');777778 const events: any = [];779 contract.events.allEvents((_: any, event: any) => {780 events.push(event);781 });782783 await token.burn(alice);784 if (events.length == 0) await helper.wait.newBlocks(1);785 const event = events[0];786787 expect(event.event).to.be.equal('Transfer');788 expect(event.address).to.be.equal(collectionAddress);789 expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));790 expect(event.returnValues.to).to.be.equal('0x0000000000000000000000000000000000000000');791 expect(event.returnValues.tokenId).to.be.equal(token.tokenId.toString());792 });793794 itEth('Events emitted for approve()', async ({helper}) => {795 const receiver = helper.eth.createAccount();796797 const collection = await helper.nft.mintCollection(alice, {});798 const token = await collection.mintToken(alice);799800 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);801 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');802803 const events: any = [];804 contract.events.allEvents((_: any, event: any) => {805 events.push(event);806 });807808 await token.approve(alice, {Ethereum: receiver});809 if (events.length == 0) await helper.wait.newBlocks(1);810 const event = events[0];811812 expect(event.event).to.be.equal('Approval');813 expect(event.address).to.be.equal(collectionAddress);814 expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));815 expect(event.returnValues.approved).to.be.equal(receiver);816 expect(event.returnValues.tokenId).to.be.equal(token.tokenId.toString());817 });818819 itEth('Events emitted for transferFrom()', async ({helper}) => {820 const [bob] = await helper.arrange.createAccounts([10n], donor);821 const receiver = helper.eth.createAccount();822823 const collection = await helper.nft.mintCollection(alice, {});824 const token = await collection.mintToken(alice);825 await token.approve(alice, {Substrate: bob.address});826827 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);828 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');829830 const events: any = [];831 contract.events.allEvents((_: any, event: any) => {832 events.push(event);833 });834835 await token.transferFrom(bob, {Substrate: alice.address}, {Ethereum: receiver});836837 if (events.length == 0) await helper.wait.newBlocks(1);838 const event = events[0];839840 expect(event.address).to.be.equal(collectionAddress);841 expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));842 expect(event.returnValues.to).to.be.equal(receiver);843 expect(event.returnValues.tokenId).to.be.equal(`${token.tokenId}`);844 });845846 itEth('Events emitted for transfer()', async ({helper}) => {847 const receiver = helper.eth.createAccount();848849 const collection = await helper.nft.mintCollection(alice, {});850 const token = await collection.mintToken(alice);851852 const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);853 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');854855 const events: any = [];856 contract.events.allEvents((_: any, event: any) => {857 events.push(event);858 });859860 await token.transfer(alice, {Ethereum: receiver});861862 if (events.length == 0) await helper.wait.newBlocks(1);863 const event = events[0];864865 expect(event.address).to.be.equal(collectionAddress);866 expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));867 expect(event.returnValues.to).to.be.equal(receiver);868 expect(event.returnValues.tokenId).to.be.equal(`${token.tokenId}`);869 });870});871872describe('Common metadata', () => {873 let donor: IKeyringPair;874 let alice: IKeyringPair;875876 before(async function() {877 await usingEthPlaygrounds(async (helper, privateKey) => {878 donor = await privateKey({filename: __filename});879 [alice] = await helper.arrange.createAccounts([20n], donor);880 });881 });882883 itEth('Returns collection name', async ({helper}) => {884 const caller = await helper.eth.createAccountWithBalance(donor);885 const tokenPropertyPermissions = [{886 key: 'URI',887 permission: {888 mutable: true,889 collectionAdmin: true,890 tokenOwner: false,891 },892 }];893 const collection = await helper.nft.mintCollection(894 alice,895 {896 name: 'oh River',897 tokenPrefix: 'CHANGE',898 properties: [{key: 'ERC721Metadata', value: '1'}],899 tokenPropertyPermissions,900 },901 );902903 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);904 const name = await contract.methods.name().call();905 expect(name).to.equal('oh River');906 });907908 itEth('Returns symbol name', async ({helper}) => {909 const caller = await helper.eth.createAccountWithBalance(donor);910 const tokenPropertyPermissions = [{911 key: 'URI',912 permission: {913 mutable: true,914 collectionAdmin: true,915 tokenOwner: false,916 },917 }];918 const collection = await helper.nft.mintCollection(919 alice,920 {921 name: 'oh River',922 tokenPrefix: 'CHANGE',923 properties: [{key: 'ERC721Metadata', value: '1'}],924 tokenPropertyPermissions,925 },926 );927928 const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);929 const symbol = await contract.methods.symbol().call();930 expect(symbol).to.equal('CHANGE');931 });932});933934describe('Negative tests', () => {935 let donor: IKeyringPair;936 let minter: IKeyringPair;937 let alice: IKeyringPair;938939 before(async function() {940 await usingEthPlaygrounds(async (helper, privateKey) => {941 donor = await privateKey({filename: __filename});942 [minter, alice] = await helper.arrange.createAccounts([100n, 100n], donor);943 });944 });945946 itEth('[negative] Cant perform burn without approval', async ({helper}) => {947 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});948949 const owner = await helper.eth.createAccountWithBalance(donor, 100n);950 const spender = await helper.eth.createAccountWithBalance(donor, 100n);951952 const token = await collection.mintToken(minter, {Ethereum: owner});953954 const address = helper.ethAddress.fromCollectionId(collection.collectionId);955 const contract = helper.ethNativeContract.collection(address, 'nft');956957 const ownerCross = helper.ethCrossAccount.fromAddress(owner);958 await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected;959960 await contract.methods.setApprovalForAll(spender, true).send({from: owner});961 await contract.methods.setApprovalForAll(spender, false).send({from: owner});962963 await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected;964 });965966 itEth('[negative] Cant perform transfer without approval', async ({helper}) => {967 const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});968 const receiver = alice;969970 const owner = await helper.eth.createAccountWithBalance(donor, 100n);971 const spender = await helper.eth.createAccountWithBalance(donor, 100n);972973 const token = await collection.mintToken(minter, {Ethereum: owner});974975 const address = helper.ethAddress.fromCollectionId(collection.collectionId);976 const contract = helper.ethNativeContract.collection(address, 'nft');977978 const ownerCross = helper.ethCrossAccount.fromAddress(owner);979 const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);980981 await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected;982983 await contract.methods.setApprovalForAll(spender, true).send({from: owner});984 await contract.methods.setApprovalForAll(spender, false).send({from: owner});985 986 await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected;987 });988});tests/src/eth/reFungible.test.tsdiffbeforeafterboth--- a/tests/src/eth/reFungible.test.ts
+++ b/tests/src/eth/reFungible.test.ts
@@ -750,10 +750,14 @@
const address = helper.ethAddress.fromCollectionId(collection.collectionId);
const contract = helper.ethNativeContract.collection(address, 'rft');
- {
- const ownerCross = helper.ethCrossAccount.fromAddress(owner);
- await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected;
- }
+ const ownerCross = helper.ethCrossAccount.fromAddress(owner);
+
+ await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected;
+
+ await contract.methods.setApprovalForAll(spender, true).send({from: owner});
+ await contract.methods.setApprovalForAll(spender, false).send({from: owner});
+
+ await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected;
});
itEth('[negative] Cant perform transfer without approval', async ({helper}) => {
@@ -768,10 +772,14 @@
const address = helper.ethAddress.fromCollectionId(collection.collectionId);
const contract = helper.ethNativeContract.collection(address, 'rft');
- {
- const ownerCross = helper.ethCrossAccount.fromAddress(owner);
- const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);
- await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected;
- }
+ const ownerCross = helper.ethCrossAccount.fromAddress(owner);
+ const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);
+
+ await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected;
+
+ await contract.methods.setApprovalForAll(spender, true).send({from: owner});
+ await contract.methods.setApprovalForAll(spender, false).send({from: owner});
+
+ await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected;
});
});
tests/src/interfaces/augment-api-events.tsdiffbeforeafterboth--- a/tests/src/interfaces/augment-api-events.ts
+++ b/tests/src/interfaces/augment-api-events.ts
@@ -107,7 +107,7 @@
**/
Approved: AugmentedEvent<ApiType, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
/**
- * Amount pieces of token owned by `sender` was approved for `spender`.
+ * A `sender` approves operations on all owned tokens for `spender`.
**/
ApprovedForAll: AugmentedEvent<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, bool]>;
/**
tests/src/interfaces/augment-api-query.tsdiffbeforeafterboth--- a/tests/src/interfaces/augment-api-query.ts
+++ b/tests/src/interfaces/augment-api-query.ts
@@ -406,6 +406,10 @@
**/
allowance: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<Option<PalletEvmAccountBasicCrossAccountIdRepr>>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
/**
+ * Operator set by a wallet owner that could perform certain transactions on all tokens in the wallet.
+ **/
+ collectionAllowance: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, arg3: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<bool>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]>;
+ /**
* Used to enumerate tokens owned by account.
**/
owned: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, arg3: u32 | AnyNumber | Uint8Array) => Observable<bool>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, u32]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr, u32]>;
@@ -441,10 +445,6 @@
* Total amount of minted tokens in a collection.
**/
tokensMinted: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<u32>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
- /**
- * Operator set by a wallet owner that could perform certain transactions on all tokens in the wallet.
- **/
- walletOperator: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, arg3: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<Option<bool>>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]>;
/**
* Generic query
**/
@@ -625,6 +625,10 @@
**/
balance: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array, arg3: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<u128>, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
/**
+ * Operator set by a wallet owner that could perform certain transactions on all tokens in the wallet.
+ **/
+ collectionAllowance: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, arg3: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<bool>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]>;
+ /**
* Used to enumerate tokens owned by account.
**/
owned: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, arg3: u32 | AnyNumber | Uint8Array) => Observable<bool>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, u32]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr, u32]>;
@@ -648,10 +652,6 @@
* Total amount of pieces for token
**/
totalSupply: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<u128>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
- /**
- * Operator set by a wallet owner that could perform certain transactions on all tokens in the wallet.
- **/
- walletOperator: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, arg3: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<Option<bool>>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]>;
/**
* Generic query
**/
tests/src/interfaces/augment-api-rpc.tsdiffbeforeafterboth--- a/tests/src/interfaces/augment-api-rpc.ts
+++ b/tests/src/interfaces/augment-api-rpc.ts
@@ -684,6 +684,10 @@
**/
allowance: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, sender: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, spender: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<u128>>;
/**
+ * Tells whether the given `owner` approves the `operator`.
+ **/
+ allowanceForAll: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, owner: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, operator: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Option<bool>>>;
+ /**
* Check if a user is allowed to operate within a collection
**/
allowed: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, account: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable<bool>>;
@@ -719,10 +723,6 @@
* Get effective collection limits
**/
effectiveCollectionLimits: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Option<UpDataStructsCollectionLimits>>>;
- /**
- * Tells whether an operator is approved by a given owner.
- **/
- isApprovedForAll: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, owner: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, operator: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Option<bool>>>;
/**
* Get the last token ID created in a collection
**/
tests/src/interfaces/augment-api-tx.tsdiffbeforeafterboth--- a/tests/src/interfaces/augment-api-tx.ts
+++ b/tests/src/interfaces/augment-api-tx.ts
@@ -1547,15 +1547,15 @@
/**
* Sets or unsets the approval of a given operator.
*
- * An operator is allowed to transfer all tokens of the sender on their behalf.
+ * The `operator` is allowed to transfer all tokens of the `owner` on their behalf.
*
* # Arguments
*
* * `owner`: Token owner
* * `operator`: Operator
- * * `approve`: Is operator enabled or disabled
+ * * `approve`: Should operator status be granted or revoked?
**/
- setApprovalForAll: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, operator: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, approve: bool | boolean | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, bool]>;
+ setAllowanceForAll: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, operator: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, approve: bool | boolean | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, bool]>;
/**
* Set specific limits of a collection. Empty, or None fields mean chain default.
*
tests/src/interfaces/default/types.tsdiffbeforeafterboth--- a/tests/src/interfaces/default/types.ts
+++ b/tests/src/interfaces/default/types.ts
@@ -2312,13 +2312,13 @@
readonly tokenId: u32;
readonly amount: u128;
} & Struct;
- readonly isSetApprovalForAll: boolean;
- readonly asSetApprovalForAll: {
+ readonly isSetAllowanceForAll: boolean;
+ readonly asSetAllowanceForAll: {
readonly collectionId: u32;
readonly operator: PalletEvmAccountBasicCrossAccountIdRepr;
readonly approve: bool;
} & Struct;
- readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'SetCollectionProperties' | 'DeleteCollectionProperties' | 'SetTokenProperties' | 'DeleteTokenProperties' | 'SetTokenPropertyPermissions' | 'CreateMultipleItemsEx' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'TransferFrom' | 'SetCollectionLimits' | 'SetCollectionPermissions' | 'Repartition' | 'SetApprovalForAll';
+ readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'SetCollectionProperties' | 'DeleteCollectionProperties' | 'SetTokenProperties' | 'DeleteTokenProperties' | 'SetTokenPropertyPermissions' | 'CreateMultipleItemsEx' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'TransferFrom' | 'SetCollectionLimits' | 'SetCollectionPermissions' | 'Repartition' | 'SetAllowanceForAll';
}
/** @name PalletUniqueError */
tests/src/interfaces/lookup.tsdiffbeforeafterboth--- a/tests/src/interfaces/lookup.ts
+++ b/tests/src/interfaces/lookup.ts
@@ -2305,7 +2305,7 @@
tokenId: 'u32',
amount: 'u128',
},
- set_approval_for_all: {
+ set_allowance_for_all: {
collectionId: 'u32',
operator: 'PalletEvmAccountBasicCrossAccountIdRepr',
approve: 'bool'
tests/src/interfaces/types-lookup.tsdiffbeforeafterboth--- a/tests/src/interfaces/types-lookup.ts
+++ b/tests/src/interfaces/types-lookup.ts
@@ -2541,13 +2541,13 @@
readonly tokenId: u32;
readonly amount: u128;
} & Struct;
- readonly isSetApprovalForAll: boolean;
- readonly asSetApprovalForAll: {
+ readonly isSetAllowanceForAll: boolean;
+ readonly asSetAllowanceForAll: {
readonly collectionId: u32;
readonly operator: PalletEvmAccountBasicCrossAccountIdRepr;
readonly approve: bool;
} & Struct;
- readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'SetCollectionProperties' | 'DeleteCollectionProperties' | 'SetTokenProperties' | 'DeleteTokenProperties' | 'SetTokenPropertyPermissions' | 'CreateMultipleItemsEx' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'TransferFrom' | 'SetCollectionLimits' | 'SetCollectionPermissions' | 'Repartition' | 'SetApprovalForAll';
+ readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'SetCollectionProperties' | 'DeleteCollectionProperties' | 'SetTokenProperties' | 'DeleteTokenProperties' | 'SetTokenPropertyPermissions' | 'CreateMultipleItemsEx' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'TransferFrom' | 'SetCollectionLimits' | 'SetCollectionPermissions' | 'Repartition' | 'SetAllowanceForAll';
}
/** @name UpDataStructsCollectionMode (240) */
tests/src/interfaces/unique/definitions.tsdiffbeforeafterboth--- a/tests/src/interfaces/unique/definitions.ts
+++ b/tests/src/interfaces/unique/definitions.ts
@@ -175,8 +175,8 @@
[collectionParam, tokenParam],
'Option<u128>',
),
- isApprovedForAll: fun(
- 'Tells whether an operator is approved by a given owner.',
+ allowanceForAll: fun(
+ 'Tells whether the given `owner` approves the `operator`.',
[collectionParam, crossAccountParam('owner'), crossAccountParam('operator')],
'Option<bool>',
),
tests/src/util/playgrounds/unique.tsdiffbeforeafterboth--- a/tests/src/util/playgrounds/unique.ts
+++ b/tests/src/util/playgrounds/unique.ts
@@ -1415,26 +1415,26 @@
}
/**
- * Tells whether an operator is approved by a given owner.
+ * Tells whether the given `owner` approves the `operator`.
* @param collectionId ID of collection
* @param owner owner address
- * @param operator operator addrees
+ * @param operator operator addrees
* @returns true if operator is enabled
*/
- async isApprovedForAll(collectionId: number, owner: ICrossAccountId, operator: ICrossAccountId): Promise<boolean> {
- return (await this.helper.callRpc('api.rpc.unique.isApprovedForAll', [collectionId, owner, operator])).toJSON();
+ async allowanceForAll(collectionId: number, owner: ICrossAccountId, operator: ICrossAccountId): Promise<boolean> {
+ return (await this.helper.callRpc('api.rpc.unique.allowanceForAll', [collectionId, owner, operator])).toJSON();
}
/** Sets or unsets the approval of a given operator.
- * An operator is allowed to transfer all tokens of the sender on their behalf.
- * @param operator Operator
- * @param approved Is operator enabled or disabled
+ * The `operator` is allowed to transfer all tokens of the `caller` on their behalf.
+ * @param operator Operator
+ * @param approved Should operator status be granted or revoked?
* @returns ```true``` if extrinsic success, otherwise ```false```
*/
- async setApprovalForAll(signer: TSigner, collectionId: number, operator: ICrossAccountId, approved: boolean): Promise<boolean> {
+ async setAllowanceForAll(signer: TSigner, collectionId: number, operator: ICrossAccountId, approved: boolean): Promise<boolean> {
const result = await this.helper.executeExtrinsic(
signer,
- 'api.tx.unique.setApprovalForAll', [collectionId, operator, approved],
+ 'api.tx.unique.setAllowanceForAll', [collectionId, operator, approved],
true,
);
return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'ApprovedForAll');