difftreelog
feat add ApproveForAll to Eth and Sub
in: master
39 files changed
client/rpc/src/lib.rsdiffbeforeafterboth--- a/client/rpc/src/lib.rs
+++ b/client/rpc/src/lib.rs
@@ -246,6 +246,16 @@
token_id: TokenId,
at: Option<BlockHash>,
) -> Result<Option<String>>;
+
+ /// Get whether an operator is approved by a given owner.
+ #[method(name = "unique_isApprovedForAll")]
+ fn is_approved_for_all(
+ &self,
+ collection: CollectionId,
+ owner: CrossAccountId,
+ operator: CrossAccountId,
+ at: Option<BlockHash>,
+ ) -> Result<bool>;
}
mod app_promotion_unique_rpc {
@@ -569,6 +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);
}
impl<C, Block, BlockNumber, CrossAccountId, AccountId>
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -472,6 +472,18 @@
u128,
),
+ /// Amount pieces of token owned by `sender` was approved for `spender`.
+ ApprovedForAll(
+ /// Id of collection to which item is belong.
+ CollectionId,
+ /// Owner of a wallet.
+ T::CrossAccountId,
+ /// Id for which operator status was granted or rewoked.
+ T::CrossAccountId,
+ /// Is operator status was granted or rewoked.
+ bool,
+ ),
+
/// The colletion property has been added or edited.
CollectionPropertySet(
/// Id of collection to which property has been set.
@@ -1521,6 +1533,9 @@
/// The price of retrieving token owner
fn token_owner() -> Weight;
+
+ /// The price of setting approval for all
+ fn set_approval_for_all() -> Weight;
}
/// Weight info extension trait for refungible pallet.
@@ -1828,6 +1843,20 @@
/// 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.
+ /// * `owner` - Token owner
+ /// * `operator` - Operator
+ /// * `approve` - Is operator enabled or disabled
+ fn set_approval_for_all(
+ &self,
+ owner: T::CrossAccountId,
+ operator: T::CrossAccountId,
+ approve: bool,
+ ) -> DispatchResultWithPostInfo;
+
+ /// Tells whether an operator is approved by a given owner.
+ fn is_approved_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
@@ -107,6 +107,10 @@
fn token_owner() -> Weight {
Weight::zero()
}
+
+ fn set_approval_for_all() -> Weight {
+ Weight::zero()
+ }
}
/// Implementation of `CommonCollectionOperations` for `FungibleHandle`. It wraps FungibleHandle Pallete
@@ -424,4 +428,17 @@
}
<TotalSupply<T>>::try_get(self.id).ok()
}
+
+ fn set_approval_for_all(
+ &self,
+ _owner: T::CrossAccountId,
+ _operator: T::CrossAccountId,
+ _approve: bool,
+ ) -> DispatchResultWithPostInfo {
+ fail!(<Error<T>>::SettingApprovalForAllNotAllowed)
+ }
+
+ fn is_approved_for_all(&self, _owner: T::CrossAccountId, _operator: T::CrossAccountId) -> bool {
+ false
+ }
}
pallets/fungible/src/lib.rsdiffbeforeafterboth--- a/pallets/fungible/src/lib.rs
+++ b/pallets/fungible/src/lib.rs
@@ -127,6 +127,8 @@
FungibleDisallowsNesting,
/// Setting item properties is not allowed.
SettingPropertiesNotAllowed,
+ /// Setting approval for all is not allowed.
+ SettingApprovalForAllNotAllowed,
}
#[pallet::config]
pallets/nonfungible/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/benchmarking.rs
+++ b/pallets/nonfungible/src/benchmarking.rs
@@ -222,4 +222,18 @@
let item = create_max_item(&collection, &owner, owner.clone())?;
}: {collection.token_owner(item)}
+
+ set_approval_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)}
+
+ is_approved_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)}
}
pallets/nonfungible/src/common.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/common.rs
+++ b/pallets/nonfungible/src/common.rs
@@ -122,6 +122,10 @@
fn token_owner() -> Weight {
<SelfWeightOf<T>>::token_owner()
}
+
+ fn set_approval_for_all() -> Weight {
+ <SelfWeightOf<T>>::set_approval_for_all()
+ }
}
fn map_create_data<T: Config>(
@@ -512,4 +516,20 @@
None
}
}
+
+ fn set_approval_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(),
+ )
+ }
+
+ fn is_approved_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool {
+ <Pallet<T>>::is_approved_for_all(self, &owner, &operator)
+ }
}
pallets/nonfungible/src/erc.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -469,15 +469,23 @@
Ok(())
}
- /// @dev Not implemented
+ /// @notice 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
+ #[weight(<SelfWeightOf<T>>::set_approval_for_all())]
fn set_approval_for_all(
&mut self,
- _caller: caller,
- _operator: address,
- _approved: bool,
+ caller: caller,
+ operator: address,
+ approved: bool,
) -> Result<void> {
- // TODO: Not implemetable
- Err("not implemented".into())
+ let caller = T::CrossAccountId::from_eth(caller);
+ let operator = T::CrossAccountId::from_eth(operator);
+
+ <Pallet<T>>::set_approval_for_all(self, &caller, &operator, approved)
+ .map_err(dispatch_to_evm::<T>)?;
+ Ok(())
}
/// @dev Not implemented
@@ -486,10 +494,13 @@
Err("not implemented".into())
}
- /// @dev Not implemented
- fn is_approved_for_all(&self, _owner: address, _operator: address) -> Result<address> {
- // TODO: Not implemetable
- Err("not implemented".into())
+ /// @notice Tells whether an operator is approved by a given owner.
+ #[weight(<SelfWeightOf<T>>::is_approved_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))
}
/// @notice Returns collection helper contract address
pallets/nonfungible/src/lib.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -272,6 +272,18 @@
QueryKind = OptionQuery,
>;
+ /// 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<
+ Key = (
+ Key<Twox64Concat, CollectionId>,
+ Key<Blake2_128Concat, T::CrossAccountId>,
+ Key<Blake2_128Concat, T::CrossAccountId>,
+ ),
+ Value = bool,
+ QueryKind = OptionQuery,
+ >;
+
/// Upgrade from the old schema to properties.
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
@@ -438,6 +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);
Ok(())
}
@@ -1193,6 +1206,9 @@
if <Allowance<T>>::get((collection.id, token)).as_ref() == Some(spender) {
return Ok(());
}
+ if <WalletOperator<T>>::get((collection.id, from, spender)) == Some(true) {
+ return Ok(());
+ }
ensure!(
collection.ignores_allowance(spender),
<CommonError<T>>::ApprovedValueTooLow
@@ -1326,4 +1342,52 @@
) -> DispatchResult {
Self::create_multiple_items(collection, sender, vec![data], nesting_budget)
}
+
+ /// Sets or unsets the approval of a given operator.
+ ///
+ /// An operator is allowed to transfer all token pieces of the sender on their behalf.
+ /// - `owner`: Token owner
+ /// - `operator`: Operator
+ /// - `approve`: Is operator enabled or disabled
+ pub fn set_approval_for_all(
+ collection: &NonfungibleHandle<T>,
+ owner: &T::CrossAccountId,
+ operator: &T::CrossAccountId,
+ approve: bool,
+ ) -> DispatchResult {
+ if collection.permissions.access() == AccessMode::AllowList {
+ collection.check_allowlist(owner)?;
+ collection.check_allowlist(operator)?;
+ }
+
+ <PalletCommon<T>>::ensure_correct_receiver(operator)?;
+
+ // =========
+
+ <WalletOperator<T>>::insert((collection.id, owner, operator), approve);
+ <PalletEvm<T>>::deposit_log(
+ ERC721Events::ApprovalForAll {
+ owner: *owner.as_eth(),
+ operator: *operator.as_eth(),
+ approved: approve,
+ }
+ .to_log(collection_id_to_address(collection.id)),
+ );
+ <PalletCommon<T>>::deposit_event(CommonEvent::ApprovedForAll(
+ collection.id,
+ owner.clone(),
+ operator.clone(),
+ approve,
+ ));
+ Ok(())
+ }
+
+ /// Tells whether an operator is approved by a given owner.
+ pub fn is_approved_for_all(
+ collection: &NonfungibleHandle<T>,
+ owner: &T::CrossAccountId,
+ operator: &T::CrossAccountId,
+ ) -> bool {
+ <WalletOperator<T>>::get((collection.id, owner, operator)).unwrap_or(false)
+ }
}
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
@@ -1020,7 +1020,10 @@
dummy = 0;
}
- /// @dev Not implemented
+ /// @notice 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
/// @dev EVM selector for this function is: 0xa22cb465,
/// or in textual repr: setApprovalForAll(address,bool)
function setApprovalForAll(address operator, bool approved) public {
@@ -1040,15 +1043,15 @@
return 0x0000000000000000000000000000000000000000;
}
- /// @dev Not implemented
+ /// @notice Tells whether an operator is approved by a given owner.
/// @dev EVM selector for this function is: 0xe985e9c5,
/// or in textual repr: isApprovedForAll(address,address)
- function isApprovedForAll(address owner, address operator) public view returns (address) {
+ function isApprovedForAll(address owner, address operator) public view returns (bool) {
require(false, stub_error);
owner;
operator;
dummy;
- return 0x0000000000000000000000000000000000000000;
+ return false;
}
/// @notice Returns collection helper contract address
pallets/nonfungible/src/weights.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/weights.rs
+++ b/pallets/nonfungible/src/weights.rs
@@ -26,6 +26,7 @@
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
#![allow(unused_imports)]
+#![allow(missing_docs)]
#![allow(clippy::unnecessary_cast)]
use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
@@ -47,6 +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;
}
/// Weights for pallet_nonfungible using the Substrate node and recommended hardware.
@@ -195,6 +198,16 @@
Weight::from_ref_time(4_366_000)
.saturating_add(T::DbWeight::get().reads(1 as u64))
}
+ // Storage: Nonfungible WalletOperator (r:0 w:1)
+ fn set_approval_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 {
+ Weight::from_ref_time(6_161_000 as u64)
+ .saturating_add(T::DbWeight::get().reads(1 as u64))
+ }
}
// For backwards compatibility and tests
@@ -342,4 +355,14 @@
Weight::from_ref_time(4_366_000)
.saturating_add(RocksDbWeight::get().reads(1 as u64))
}
+ // Storage: Nonfungible WalletOperator (r:0 w:1)
+ fn set_approval_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 {
+ 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
@@ -290,4 +290,18 @@
};
let item = create_max_item(&collection, &sender, [(owner.clone(), 100)])?;
}: {<Pallet<T>>::token_owner(collection.id, item)}
+
+ set_approval_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)}
+
+ is_approved_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)}
}
pallets/refungible/src/common.rsdiffbeforeafterboth--- a/pallets/refungible/src/common.rs
+++ b/pallets/refungible/src/common.rs
@@ -152,6 +152,10 @@
fn token_owner() -> Weight {
<SelfWeightOf<T>>::token_owner()
}
+
+ fn set_approval_for_all() -> Weight {
+ <SelfWeightOf<T>>::set_approval_for_all()
+ }
}
fn map_create_data<T: Config>(
@@ -516,6 +520,22 @@
fn total_pieces(&self, token: TokenId) -> Option<u128> {
<Pallet<T>>::total_pieces(self.id, token)
}
+
+ fn set_approval_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(),
+ )
+ }
+
+ fn is_approved_for_all(&self, owner: T::CrossAccountId, operator: T::CrossAccountId) -> bool {
+ <Pallet<T>>::is_approved_for_all(self, &owner, &operator)
+ }
}
impl<T: Config> RefungibleExtensions<T> for RefungibleHandle<T> {
pallets/refungible/src/erc.rsdiffbeforeafterboth--- a/pallets/refungible/src/erc.rs
+++ b/pallets/refungible/src/erc.rs
@@ -461,15 +461,23 @@
Err("not implemented".into())
}
- /// @dev Not implemented
+ /// @notice 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
+ #[weight(<SelfWeightOf<T>>::set_approval_for_all())]
fn set_approval_for_all(
&mut self,
- _caller: caller,
- _operator: address,
- _approved: bool,
+ caller: caller,
+ operator: address,
+ approved: bool,
) -> Result<void> {
- // TODO: Not implemetable
- Err("not implemented".into())
+ let caller = T::CrossAccountId::from_eth(caller);
+ let operator = T::CrossAccountId::from_eth(operator);
+
+ <Pallet<T>>::set_approval_for_all(self, &caller, &operator, approved)
+ .map_err(dispatch_to_evm::<T>)?;
+ Ok(())
}
/// @dev Not implemented
@@ -478,10 +486,13 @@
Err("not implemented".into())
}
- /// @dev Not implemented
- fn is_approved_for_all(&self, _owner: address, _operator: address) -> Result<address> {
- // TODO: Not implemetable
- Err("not implemented".into())
+ /// @notice Tells whether an operator is approved by a given owner.
+ #[weight(<SelfWeightOf<T>>::is_approved_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))
}
/// @notice Returns collection helper contract address
pallets/refungible/src/lib.rsdiffbeforeafterboth--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -273,6 +273,18 @@
QueryKind = ValueQuery,
>;
+ /// 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<
+ Key = (
+ Key<Twox64Concat, CollectionId>,
+ Key<Blake2_128Concat, T::CrossAccountId>,
+ Key<Blake2_128Concat, T::CrossAccountId>,
+ ),
+ Value = bool,
+ QueryKind = OptionQuery,
+ >;
+
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn on_runtime_upgrade() -> Weight {
@@ -1161,6 +1173,12 @@
}
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) {
+ return Ok(allowance);
+ }
+
if allowance.is_none() {
ensure!(
collection.ignores_allowance(spender),
@@ -1387,4 +1405,52 @@
Some(res)
}
}
+
+ /// Sets or unsets the approval of a given operator.
+ ///
+ /// An operator is allowed to transfer all tokens of the sender on their behalf.
+ /// - `owner`: Token owner
+ /// - `operator`: Operator
+ /// - `approve`: Is operator enabled or disabled
+ pub fn set_approval_for_all(
+ collection: &RefungibleHandle<T>,
+ owner: &T::CrossAccountId,
+ operator: &T::CrossAccountId,
+ approve: bool,
+ ) -> DispatchResult {
+ if collection.permissions.access() == AccessMode::AllowList {
+ collection.check_allowlist(owner)?;
+ collection.check_allowlist(operator)?;
+ }
+
+ <PalletCommon<T>>::ensure_correct_receiver(operator)?;
+
+ // =========
+
+ <WalletOperator<T>>::insert((collection.id, owner, operator), approve);
+ <PalletEvm<T>>::deposit_log(
+ ERC721Events::ApprovalForAll {
+ owner: *owner.as_eth(),
+ operator: *operator.as_eth(),
+ approved: approve,
+ }
+ .to_log(collection_id_to_address(collection.id)),
+ );
+ <PalletCommon<T>>::deposit_event(CommonEvent::ApprovedForAll(
+ collection.id,
+ owner.clone(),
+ operator.clone(),
+ approve,
+ ));
+ Ok(())
+ }
+
+ /// Tells whether an operator is approved by a given owner.
+ pub fn is_approved_for_all(
+ collection: &RefungibleHandle<T>,
+ owner: &T::CrossAccountId,
+ operator: &T::CrossAccountId,
+ ) -> bool {
+ <WalletOperator<T>>::get((collection.id, owner, operator)).unwrap_or(false)
+ }
}
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
@@ -1017,7 +1017,10 @@
dummy = 0;
}
- /// @dev Not implemented
+ /// @notice 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
/// @dev EVM selector for this function is: 0xa22cb465,
/// or in textual repr: setApprovalForAll(address,bool)
function setApprovalForAll(address operator, bool approved) public {
@@ -1037,15 +1040,15 @@
return 0x0000000000000000000000000000000000000000;
}
- /// @dev Not implemented
+ /// @notice Tells whether an operator is approved by a given owner.
/// @dev EVM selector for this function is: 0xe985e9c5,
/// or in textual repr: isApprovedForAll(address,address)
- function isApprovedForAll(address owner, address operator) public view returns (address) {
+ function isApprovedForAll(address owner, address operator) public view returns (bool) {
require(false, stub_error);
owner;
operator;
dummy;
- return 0x0000000000000000000000000000000000000000;
+ return false;
}
/// @notice Returns collection helper contract address
pallets/refungible/src/weights.rsdiffbeforeafterboth--- a/pallets/refungible/src/weights.rs
+++ b/pallets/refungible/src/weights.rs
@@ -3,7 +3,7 @@
//! Autogenerated weights for pallet_refungible
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
-//! DATE: 2022-08-15, STEPS: `50`, REPEAT: 80, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! DATE: 2022-11-25, STEPS: `50`, REPEAT: 80, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024
// Executed Command:
@@ -26,6 +26,7 @@
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
#![allow(unused_imports)]
+#![allow(missing_docs)]
#![allow(clippy::unnecessary_cast)]
use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
@@ -54,6 +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;
}
/// Weights for pallet_refungible using the Substrate node and recommended hardware.
@@ -259,6 +262,16 @@
Weight::from_ref_time(9_431_000)
.saturating_add(T::DbWeight::get().reads(2 as u64))
}
+ // Storage: Refungible WalletOperator (r:0 w:1)
+ fn set_approval_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 {
+ Weight::from_ref_time(5_901_000 as u64)
+ .saturating_add(T::DbWeight::get().reads(1 as u64))
+ }
}
// For backwards compatibility and tests
@@ -463,4 +476,14 @@
Weight::from_ref_time(9_431_000)
.saturating_add(RocksDbWeight::get().reads(2 as u64))
}
+ // Storage: Refungible WalletOperator (r:0 w:1)
+ fn set_approval_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 {
+ Weight::from_ref_time(5_901_000 as u64)
+ .saturating_add(RocksDbWeight::get().reads(1 as u64))
+ }
}
pallets/unique/src/lib.rsdiffbeforeafterboth--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -1126,6 +1126,28 @@
}
})
}
+
+ /// Sets or unsets the approval of a given operator.
+ ///
+ /// An operator is allowed to transfer all tokens of the sender 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(
+ origin,
+ collection_id: CollectionId,
+ operator: T::CrossAccountId,
+ approve: bool,
+ ) -> DispatchResultWithPostInfo {
+ let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
+ dispatch_tx::<T, _>(collection_id, |d| {
+ d.set_approval_for_all(sender, operator, approve)
+ })
+ }
}
}
primitives/rpc/src/lib.rsdiffbeforeafterboth--- a/primitives/rpc/src/lib.rs
+++ b/primitives/rpc/src/lib.rs
@@ -132,5 +132,8 @@
fn total_pieces(collection_id: CollectionId, token_id: TokenId) -> Result<Option<u128>>;
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>;
}
}
runtime/common/runtime_apis.rsdiffbeforeafterboth--- a/runtime/common/runtime_apis.rs
+++ b/runtime/common/runtime_apis.rs
@@ -187,6 +187,10 @@
fn total_pieces(collection: CollectionId, token_id: TokenId) -> Result<Option<u128>, DispatchError> {
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))
+ }
}
impl app_promotion_rpc::AppPromotionApi<Block, BlockNumber, CrossAccountId, AccountId> for Runtime {
runtime/common/weights.rsdiffbeforeafterboth--- a/runtime/common/weights.rs
+++ b/runtime/common/weights.rs
@@ -120,6 +120,10 @@
fn token_owner() -> Weight {
max_weight_of!(token_owner())
}
+
+ fn set_approval_for_all() -> Weight {
+ max_weight_of!(set_approval_for_all())
+ }
}
#[cfg(feature = "refungible")]
tests/src/approve.test.tsdiffbeforeafterboth--- a/tests/src/approve.test.ts
+++ b/tests/src/approve.test.ts
@@ -603,3 +603,40 @@
await expect(approveTx()).to.be.rejected;
});
});
+
+describe('Normal user can approve other users to be wallet operator:', () => {
+ let alice: IKeyringPair;
+ let bob: IKeyringPair;
+
+ before(async () => {
+ await usingPlaygrounds(async (helper, privateKey) => {
+ const donor = await privateKey({filename: __filename});
+ [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
+ });
+ });
+
+ 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 checkBeforeApprovalTx = () => helper.nft.isApprovedForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address});
+ expect(await checkBeforeApprovalTx()).to.be.false;
+ await helper.nft.setApprovalForAll(alice, collectionId, {Substrate: bob.address}, true);
+ const checkAfterApprovalTx = () => helper.nft.isApprovedForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address});
+ expect(await checkAfterApprovalTx()).to.be.true;
+ await helper.nft.setApprovalForAll(alice, collectionId, {Substrate: bob.address}, false);
+ const checkAfterDisapprovalTx = () => helper.nft.isApprovedForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address});
+ expect(await checkAfterDisapprovalTx()).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 checkBeforeApprovalTx = () => helper.rft.isApprovedForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address});
+ expect(await checkBeforeApprovalTx()).to.be.false;
+ await helper.rft.setApprovalForAll(alice, collectionId, {Substrate: bob.address}, true);
+ const checkAfterApprovalTx = () => helper.rft.isApprovedForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address});
+ expect(await checkAfterApprovalTx()).to.be.true;
+ await helper.rft.setApprovalForAll(alice, collectionId, {Substrate: bob.address}, false);
+ const checkAfterDisapprovalTx = () => helper.rft.isApprovedForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address});
+ expect(await checkAfterDisapprovalTx()).to.be.false;
+ });
+});
tests/src/eth/abi/nonFungible.jsondiffbeforeafterboth--- a/tests/src/eth/abi/nonFungible.json
+++ b/tests/src/eth/abi/nonFungible.json
@@ -384,7 +384,7 @@
{ "internalType": "address", "name": "operator", "type": "address" }
],
"name": "isApprovedForAll",
- "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+ "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"stateMutability": "view",
"type": "function"
},
tests/src/eth/abi/reFungible.jsondiffbeforeafterboth1[2 {3 "anonymous": false,4 "inputs": [5 {6 "indexed": true,7 "internalType": "address",8 "name": "owner",9 "type": "address"10 },11 {12 "indexed": true,13 "internalType": "address",14 "name": "approved",15 "type": "address"16 },17 {18 "indexed": true,19 "internalType": "uint256",20 "name": "tokenId",21 "type": "uint256"22 }23 ],24 "name": "Approval",25 "type": "event"26 },27 {28 "anonymous": false,29 "inputs": [30 {31 "indexed": true,32 "internalType": "address",33 "name": "owner",34 "type": "address"35 },36 {37 "indexed": true,38 "internalType": "address",39 "name": "operator",40 "type": "address"41 },42 {43 "indexed": false,44 "internalType": "bool",45 "name": "approved",46 "type": "bool"47 }48 ],49 "name": "ApprovalForAll",50 "type": "event"51 },52 {53 "anonymous": false,54 "inputs": [],55 "name": "MintingFinished",56 "type": "event"57 },58 {59 "anonymous": false,60 "inputs": [61 {62 "indexed": true,63 "internalType": "address",64 "name": "from",65 "type": "address"66 },67 {68 "indexed": true,69 "internalType": "address",70 "name": "to",71 "type": "address"72 },73 {74 "indexed": true,75 "internalType": "uint256",76 "name": "tokenId",77 "type": "uint256"78 }79 ],80 "name": "Transfer",81 "type": "event"82 },83 {84 "inputs": [85 {86 "components": [87 { "internalType": "address", "name": "eth", "type": "address" },88 { "internalType": "uint256", "name": "sub", "type": "uint256" }89 ],90 "internalType": "struct EthCrossAccount",91 "name": "newAdmin",92 "type": "tuple"93 }94 ],95 "name": "addCollectionAdminCross",96 "outputs": [],97 "stateMutability": "nonpayable",98 "type": "function"99 },100 {101 "inputs": [102 {103 "components": [104 { "internalType": "address", "name": "eth", "type": "address" },105 { "internalType": "uint256", "name": "sub", "type": "uint256" }106 ],107 "internalType": "struct EthCrossAccount",108 "name": "user",109 "type": "tuple"110 }111 ],112 "name": "addToCollectionAllowListCross",113 "outputs": [],114 "stateMutability": "nonpayable",115 "type": "function"116 },117 {118 "inputs": [119 {120 "components": [121 { "internalType": "address", "name": "eth", "type": "address" },122 { "internalType": "uint256", "name": "sub", "type": "uint256" }123 ],124 "internalType": "struct EthCrossAccount",125 "name": "user",126 "type": "tuple"127 }128 ],129 "name": "allowlistedCross",130 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],131 "stateMutability": "view",132 "type": "function"133 },134 {135 "inputs": [136 { "internalType": "address", "name": "approved", "type": "address" },137 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }138 ],139 "name": "approve",140 "outputs": [],141 "stateMutability": "nonpayable",142 "type": "function"143 },144 {145 "inputs": [146 { "internalType": "address", "name": "owner", "type": "address" }147 ],148 "name": "balanceOf",149 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],150 "stateMutability": "view",151 "type": "function"152 },153 {154 "inputs": [155 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }156 ],157 "name": "burn",158 "outputs": [],159 "stateMutability": "nonpayable",160 "type": "function"161 },162 {163 "inputs": [164 {165 "components": [166 { "internalType": "address", "name": "eth", "type": "address" },167 { "internalType": "uint256", "name": "sub", "type": "uint256" }168 ],169 "internalType": "struct EthCrossAccount",170 "name": "from",171 "type": "tuple"172 },173 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }174 ],175 "name": "burnFromCross",176 "outputs": [],177 "stateMutability": "nonpayable",178 "type": "function"179 },180 {181 "inputs": [182 {183 "components": [184 { "internalType": "address", "name": "eth", "type": "address" },185 { "internalType": "uint256", "name": "sub", "type": "uint256" }186 ],187 "internalType": "struct EthCrossAccount",188 "name": "newOwner",189 "type": "tuple"190 }191 ],192 "name": "changeCollectionOwnerCross",193 "outputs": [],194 "stateMutability": "nonpayable",195 "type": "function"196 },197 {198 "inputs": [],199 "name": "collectionAdmins",200 "outputs": [201 {202 "components": [203 { "internalType": "address", "name": "eth", "type": "address" },204 { "internalType": "uint256", "name": "sub", "type": "uint256" }205 ],206 "internalType": "struct EthCrossAccount[]",207 "name": "",208 "type": "tuple[]"209 }210 ],211 "stateMutability": "view",212 "type": "function"213 },214 {215 "inputs": [],216 "name": "collectionHelperAddress",217 "outputs": [{ "internalType": "address", "name": "", "type": "address" }],218 "stateMutability": "view",219 "type": "function"220 },221 {222 "inputs": [],223 "name": "collectionOwner",224 "outputs": [225 {226 "components": [227 { "internalType": "address", "name": "eth", "type": "address" },228 { "internalType": "uint256", "name": "sub", "type": "uint256" }229 ],230 "internalType": "struct EthCrossAccount",231 "name": "",232 "type": "tuple"233 }234 ],235 "stateMutability": "view",236 "type": "function"237 },238 {239 "inputs": [240 { "internalType": "string[]", "name": "keys", "type": "string[]" }241 ],242 "name": "collectionProperties",243 "outputs": [244 {245 "components": [246 { "internalType": "string", "name": "key", "type": "string" },247 { "internalType": "bytes", "name": "value", "type": "bytes" }248 ],249 "internalType": "struct Property[]",250 "name": "",251 "type": "tuple[]"252 }253 ],254 "stateMutability": "view",255 "type": "function"256 },257 {258 "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],259 "name": "collectionProperty",260 "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],261 "stateMutability": "view",262 "type": "function"263 },264 {265 "inputs": [],266 "name": "collectionSponsor",267 "outputs": [268 {269 "components": [270 { "internalType": "address", "name": "field_0", "type": "address" },271 { "internalType": "uint256", "name": "field_1", "type": "uint256" }272 ],273 "internalType": "struct Tuple29",274 "name": "",275 "type": "tuple"276 }277 ],278 "stateMutability": "view",279 "type": "function"280 },281 {282 "inputs": [],283 "name": "confirmCollectionSponsorship",284 "outputs": [],285 "stateMutability": "nonpayable",286 "type": "function"287 },288 {289 "inputs": [],290 "name": "contractAddress",291 "outputs": [{ "internalType": "address", "name": "", "type": "address" }],292 "stateMutability": "view",293 "type": "function"294 },295 {296 "inputs": [297 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }298 ],299 "name": "crossOwnerOf",300 "outputs": [301 {302 "components": [303 { "internalType": "address", "name": "eth", "type": "address" },304 { "internalType": "uint256", "name": "sub", "type": "uint256" }305 ],306 "internalType": "struct EthCrossAccount",307 "name": "",308 "type": "tuple"309 }310 ],311 "stateMutability": "view",312 "type": "function"313 },314 {315 "inputs": [316 { "internalType": "string[]", "name": "keys", "type": "string[]" }317 ],318 "name": "deleteCollectionProperties",319 "outputs": [],320 "stateMutability": "nonpayable",321 "type": "function"322 },323 {324 "inputs": [325 { "internalType": "uint256", "name": "tokenId", "type": "uint256" },326 { "internalType": "string[]", "name": "keys", "type": "string[]" }327 ],328 "name": "deleteProperties",329 "outputs": [],330 "stateMutability": "nonpayable",331 "type": "function"332 },333 {334 "inputs": [],335 "name": "description",336 "outputs": [{ "internalType": "string", "name": "", "type": "string" }],337 "stateMutability": "view",338 "type": "function"339 },340 {341 "inputs": [],342 "name": "finishMinting",343 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],344 "stateMutability": "nonpayable",345 "type": "function"346 },347 {348 "inputs": [349 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }350 ],351 "name": "getApproved",352 "outputs": [{ "internalType": "address", "name": "", "type": "address" }],353 "stateMutability": "view",354 "type": "function"355 },356 {357 "inputs": [],358 "name": "hasCollectionPendingSponsor",359 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],360 "stateMutability": "view",361 "type": "function"362 },363 {364 "inputs": [365 { "internalType": "address", "name": "owner", "type": "address" },366 { "internalType": "address", "name": "operator", "type": "address" }367 ],368 "name": "isApprovedForAll",369 "outputs": [{ "internalType": "address", "name": "", "type": "address" }],370 "stateMutability": "view",371 "type": "function"372 },373 {374 "inputs": [375 {376 "components": [377 { "internalType": "address", "name": "eth", "type": "address" },378 { "internalType": "uint256", "name": "sub", "type": "uint256" }379 ],380 "internalType": "struct EthCrossAccount",381 "name": "user",382 "type": "tuple"383 }384 ],385 "name": "isOwnerOrAdminCross",386 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],387 "stateMutability": "view",388 "type": "function"389 },390 {391 "inputs": [{ "internalType": "address", "name": "to", "type": "address" }],392 "name": "mint",393 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],394 "stateMutability": "nonpayable",395 "type": "function"396 },397 {398 "inputs": [399 { "internalType": "address", "name": "to", "type": "address" },400 { "internalType": "string", "name": "tokenUri", "type": "string" }401 ],402 "name": "mintWithTokenURI",403 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],404 "stateMutability": "nonpayable",405 "type": "function"406 },407 {408 "inputs": [],409 "name": "mintingFinished",410 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],411 "stateMutability": "view",412 "type": "function"413 },414 {415 "inputs": [],416 "name": "name",417 "outputs": [{ "internalType": "string", "name": "", "type": "string" }],418 "stateMutability": "view",419 "type": "function"420 },421 {422 "inputs": [],423 "name": "nextTokenId",424 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],425 "stateMutability": "view",426 "type": "function"427 },428 {429 "inputs": [430 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }431 ],432 "name": "ownerOf",433 "outputs": [{ "internalType": "address", "name": "", "type": "address" }],434 "stateMutability": "view",435 "type": "function"436 },437 {438 "inputs": [439 { "internalType": "uint256", "name": "tokenId", "type": "uint256" },440 { "internalType": "string[]", "name": "keys", "type": "string[]" }441 ],442 "name": "properties",443 "outputs": [444 {445 "components": [446 { "internalType": "string", "name": "key", "type": "string" },447 { "internalType": "bytes", "name": "value", "type": "bytes" }448 ],449 "internalType": "struct Property[]",450 "name": "",451 "type": "tuple[]"452 }453 ],454 "stateMutability": "view",455 "type": "function"456 },457 {458 "inputs": [459 { "internalType": "uint256", "name": "tokenId", "type": "uint256" },460 { "internalType": "string", "name": "key", "type": "string" }461 ],462 "name": "property",463 "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],464 "stateMutability": "view",465 "type": "function"466 },467 {468 "inputs": [469 {470 "components": [471 { "internalType": "address", "name": "eth", "type": "address" },472 { "internalType": "uint256", "name": "sub", "type": "uint256" }473 ],474 "internalType": "struct EthCrossAccount",475 "name": "admin",476 "type": "tuple"477 }478 ],479 "name": "removeCollectionAdminCross",480 "outputs": [],481 "stateMutability": "nonpayable",482 "type": "function"483 },484 {485 "inputs": [],486 "name": "removeCollectionSponsor",487 "outputs": [],488 "stateMutability": "nonpayable",489 "type": "function"490 },491 {492 "inputs": [493 {494 "components": [495 { "internalType": "address", "name": "eth", "type": "address" },496 { "internalType": "uint256", "name": "sub", "type": "uint256" }497 ],498 "internalType": "struct EthCrossAccount",499 "name": "user",500 "type": "tuple"501 }502 ],503 "name": "removeFromCollectionAllowListCross",504 "outputs": [],505 "stateMutability": "nonpayable",506 "type": "function"507 },508 {509 "inputs": [510 { "internalType": "address", "name": "from", "type": "address" },511 { "internalType": "address", "name": "to", "type": "address" },512 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }513 ],514 "name": "safeTransferFrom",515 "outputs": [],516 "stateMutability": "nonpayable",517 "type": "function"518 },519 {520 "inputs": [521 { "internalType": "address", "name": "from", "type": "address" },522 { "internalType": "address", "name": "to", "type": "address" },523 { "internalType": "uint256", "name": "tokenId", "type": "uint256" },524 { "internalType": "bytes", "name": "data", "type": "bytes" }525 ],526 "name": "safeTransferFromWithData",527 "outputs": [],528 "stateMutability": "nonpayable",529 "type": "function"530 },531 {532 "inputs": [533 { "internalType": "address", "name": "operator", "type": "address" },534 { "internalType": "bool", "name": "approved", "type": "bool" }535 ],536 "name": "setApprovalForAll",537 "outputs": [],538 "stateMutability": "nonpayable",539 "type": "function"540 },541 {542 "inputs": [{ "internalType": "uint8", "name": "mode", "type": "uint8" }],543 "name": "setCollectionAccess",544 "outputs": [],545 "stateMutability": "nonpayable",546 "type": "function"547 },548 {549 "inputs": [550 { "internalType": "string", "name": "limit", "type": "string" },551 { "internalType": "uint256", "name": "value", "type": "uint256" }552 ],553 "name": "setCollectionLimit",554 "outputs": [],555 "stateMutability": "nonpayable",556 "type": "function"557 },558 {559 "inputs": [{ "internalType": "bool", "name": "mode", "type": "bool" }],560 "name": "setCollectionMintMode",561 "outputs": [],562 "stateMutability": "nonpayable",563 "type": "function"564 },565 {566 "inputs": [{ "internalType": "bool", "name": "enable", "type": "bool" }],567 "name": "setCollectionNesting",568 "outputs": [],569 "stateMutability": "nonpayable",570 "type": "function"571 },572 {573 "inputs": [574 { "internalType": "bool", "name": "enable", "type": "bool" },575 {576 "internalType": "address[]",577 "name": "collections",578 "type": "address[]"579 }580 ],581 "name": "setCollectionNesting",582 "outputs": [],583 "stateMutability": "nonpayable",584 "type": "function"585 },586 {587 "inputs": [588 {589 "components": [590 { "internalType": "string", "name": "key", "type": "string" },591 { "internalType": "bytes", "name": "value", "type": "bytes" }592 ],593 "internalType": "struct Property[]",594 "name": "properties",595 "type": "tuple[]"596 }597 ],598 "name": "setCollectionProperties",599 "outputs": [],600 "stateMutability": "nonpayable",601 "type": "function"602 },603 {604 "inputs": [605 {606 "components": [607 { "internalType": "address", "name": "eth", "type": "address" },608 { "internalType": "uint256", "name": "sub", "type": "uint256" }609 ],610 "internalType": "struct EthCrossAccount",611 "name": "sponsor",612 "type": "tuple"613 }614 ],615 "name": "setCollectionSponsorCross",616 "outputs": [],617 "stateMutability": "nonpayable",618 "type": "function"619 },620 {621 "inputs": [622 { "internalType": "uint256", "name": "tokenId", "type": "uint256" },623 {624 "components": [625 { "internalType": "string", "name": "key", "type": "string" },626 { "internalType": "bytes", "name": "value", "type": "bytes" }627 ],628 "internalType": "struct Property[]",629 "name": "properties",630 "type": "tuple[]"631 }632 ],633 "name": "setProperties",634 "outputs": [],635 "stateMutability": "nonpayable",636 "type": "function"637 },638 {639 "inputs": [640 { "internalType": "string", "name": "key", "type": "string" },641 { "internalType": "bool", "name": "isMutable", "type": "bool" },642 { "internalType": "bool", "name": "collectionAdmin", "type": "bool" },643 { "internalType": "bool", "name": "tokenOwner", "type": "bool" }644 ],645 "name": "setTokenPropertyPermission",646 "outputs": [],647 "stateMutability": "nonpayable",648 "type": "function"649 },650 {651 "inputs": [652 { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }653 ],654 "name": "supportsInterface",655 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],656 "stateMutability": "view",657 "type": "function"658 },659 {660 "inputs": [],661 "name": "symbol",662 "outputs": [{ "internalType": "string", "name": "", "type": "string" }],663 "stateMutability": "view",664 "type": "function"665 },666 {667 "inputs": [668 { "internalType": "uint256", "name": "index", "type": "uint256" }669 ],670 "name": "tokenByIndex",671 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],672 "stateMutability": "view",673 "type": "function"674 },675 {676 "inputs": [677 { "internalType": "uint256", "name": "token", "type": "uint256" }678 ],679 "name": "tokenContractAddress",680 "outputs": [{ "internalType": "address", "name": "", "type": "address" }],681 "stateMutability": "view",682 "type": "function"683 },684 {685 "inputs": [686 { "internalType": "address", "name": "owner", "type": "address" },687 { "internalType": "uint256", "name": "index", "type": "uint256" }688 ],689 "name": "tokenOfOwnerByIndex",690 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],691 "stateMutability": "view",692 "type": "function"693 },694 {695 "inputs": [696 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }697 ],698 "name": "tokenURI",699 "outputs": [{ "internalType": "string", "name": "", "type": "string" }],700 "stateMutability": "view",701 "type": "function"702 },703 {704 "inputs": [],705 "name": "totalSupply",706 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],707 "stateMutability": "view",708 "type": "function"709 },710 {711 "inputs": [712 { "internalType": "address", "name": "to", "type": "address" },713 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }714 ],715 "name": "transfer",716 "outputs": [],717 "stateMutability": "nonpayable",718 "type": "function"719 },720 {721 "inputs": [722 {723 "components": [724 { "internalType": "address", "name": "eth", "type": "address" },725 { "internalType": "uint256", "name": "sub", "type": "uint256" }726 ],727 "internalType": "struct EthCrossAccount",728 "name": "to",729 "type": "tuple"730 },731 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }732 ],733 "name": "transferCross",734 "outputs": [],735 "stateMutability": "nonpayable",736 "type": "function"737 },738 {739 "inputs": [740 { "internalType": "address", "name": "from", "type": "address" },741 { "internalType": "address", "name": "to", "type": "address" },742 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }743 ],744 "name": "transferFrom",745 "outputs": [],746 "stateMutability": "nonpayable",747 "type": "function"748 },749 {750 "inputs": [751 {752 "components": [753 { "internalType": "address", "name": "eth", "type": "address" },754 { "internalType": "uint256", "name": "sub", "type": "uint256" }755 ],756 "internalType": "struct EthCrossAccount",757 "name": "from",758 "type": "tuple"759 },760 {761 "components": [762 { "internalType": "address", "name": "eth", "type": "address" },763 { "internalType": "uint256", "name": "sub", "type": "uint256" }764 ],765 "internalType": "struct EthCrossAccount",766 "name": "to",767 "type": "tuple"768 },769 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }770 ],771 "name": "transferFromCross",772 "outputs": [],773 "stateMutability": "nonpayable",774 "type": "function"775 },776 {777 "inputs": [],778 "name": "uniqueCollectionType",779 "outputs": [{ "internalType": "string", "name": "", "type": "string" }],780 "stateMutability": "view",781 "type": "function"782 }783]1[2 {3 "anonymous": false,4 "inputs": [5 {6 "indexed": true,7 "internalType": "address",8 "name": "owner",9 "type": "address"10 },11 {12 "indexed": true,13 "internalType": "address",14 "name": "approved",15 "type": "address"16 },17 {18 "indexed": true,19 "internalType": "uint256",20 "name": "tokenId",21 "type": "uint256"22 }23 ],24 "name": "Approval",25 "type": "event"26 },27 {28 "anonymous": false,29 "inputs": [30 {31 "indexed": true,32 "internalType": "address",33 "name": "owner",34 "type": "address"35 },36 {37 "indexed": true,38 "internalType": "address",39 "name": "operator",40 "type": "address"41 },42 {43 "indexed": false,44 "internalType": "bool",45 "name": "approved",46 "type": "bool"47 }48 ],49 "name": "ApprovalForAll",50 "type": "event"51 },52 {53 "anonymous": false,54 "inputs": [],55 "name": "MintingFinished",56 "type": "event"57 },58 {59 "anonymous": false,60 "inputs": [61 {62 "indexed": true,63 "internalType": "address",64 "name": "from",65 "type": "address"66 },67 {68 "indexed": true,69 "internalType": "address",70 "name": "to",71 "type": "address"72 },73 {74 "indexed": true,75 "internalType": "uint256",76 "name": "tokenId",77 "type": "uint256"78 }79 ],80 "name": "Transfer",81 "type": "event"82 },83 {84 "inputs": [85 {86 "components": [87 { "internalType": "address", "name": "eth", "type": "address" },88 { "internalType": "uint256", "name": "sub", "type": "uint256" }89 ],90 "internalType": "struct EthCrossAccount",91 "name": "newAdmin",92 "type": "tuple"93 }94 ],95 "name": "addCollectionAdminCross",96 "outputs": [],97 "stateMutability": "nonpayable",98 "type": "function"99 },100 {101 "inputs": [102 {103 "components": [104 { "internalType": "address", "name": "eth", "type": "address" },105 { "internalType": "uint256", "name": "sub", "type": "uint256" }106 ],107 "internalType": "struct EthCrossAccount",108 "name": "user",109 "type": "tuple"110 }111 ],112 "name": "addToCollectionAllowListCross",113 "outputs": [],114 "stateMutability": "nonpayable",115 "type": "function"116 },117 {118 "inputs": [119 {120 "components": [121 { "internalType": "address", "name": "eth", "type": "address" },122 { "internalType": "uint256", "name": "sub", "type": "uint256" }123 ],124 "internalType": "struct EthCrossAccount",125 "name": "user",126 "type": "tuple"127 }128 ],129 "name": "allowlistedCross",130 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],131 "stateMutability": "view",132 "type": "function"133 },134 {135 "inputs": [136 { "internalType": "address", "name": "approved", "type": "address" },137 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }138 ],139 "name": "approve",140 "outputs": [],141 "stateMutability": "nonpayable",142 "type": "function"143 },144 {145 "inputs": [146 { "internalType": "address", "name": "owner", "type": "address" }147 ],148 "name": "balanceOf",149 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],150 "stateMutability": "view",151 "type": "function"152 },153 {154 "inputs": [155 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }156 ],157 "name": "burn",158 "outputs": [],159 "stateMutability": "nonpayable",160 "type": "function"161 },162 {163 "inputs": [164 {165 "components": [166 { "internalType": "address", "name": "eth", "type": "address" },167 { "internalType": "uint256", "name": "sub", "type": "uint256" }168 ],169 "internalType": "struct EthCrossAccount",170 "name": "from",171 "type": "tuple"172 },173 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }174 ],175 "name": "burnFromCross",176 "outputs": [],177 "stateMutability": "nonpayable",178 "type": "function"179 },180 {181 "inputs": [182 {183 "components": [184 { "internalType": "address", "name": "eth", "type": "address" },185 { "internalType": "uint256", "name": "sub", "type": "uint256" }186 ],187 "internalType": "struct EthCrossAccount",188 "name": "newOwner",189 "type": "tuple"190 }191 ],192 "name": "changeCollectionOwnerCross",193 "outputs": [],194 "stateMutability": "nonpayable",195 "type": "function"196 },197 {198 "inputs": [],199 "name": "collectionAdmins",200 "outputs": [201 {202 "components": [203 { "internalType": "address", "name": "eth", "type": "address" },204 { "internalType": "uint256", "name": "sub", "type": "uint256" }205 ],206 "internalType": "struct EthCrossAccount[]",207 "name": "",208 "type": "tuple[]"209 }210 ],211 "stateMutability": "view",212 "type": "function"213 },214 {215 "inputs": [],216 "name": "collectionHelperAddress",217 "outputs": [{ "internalType": "address", "name": "", "type": "address" }],218 "stateMutability": "view",219 "type": "function"220 },221 {222 "inputs": [],223 "name": "collectionOwner",224 "outputs": [225 {226 "components": [227 { "internalType": "address", "name": "eth", "type": "address" },228 { "internalType": "uint256", "name": "sub", "type": "uint256" }229 ],230 "internalType": "struct EthCrossAccount",231 "name": "",232 "type": "tuple"233 }234 ],235 "stateMutability": "view",236 "type": "function"237 },238 {239 "inputs": [240 { "internalType": "string[]", "name": "keys", "type": "string[]" }241 ],242 "name": "collectionProperties",243 "outputs": [244 {245 "components": [246 { "internalType": "string", "name": "key", "type": "string" },247 { "internalType": "bytes", "name": "value", "type": "bytes" }248 ],249 "internalType": "struct Property[]",250 "name": "",251 "type": "tuple[]"252 }253 ],254 "stateMutability": "view",255 "type": "function"256 },257 {258 "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],259 "name": "collectionProperty",260 "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],261 "stateMutability": "view",262 "type": "function"263 },264 {265 "inputs": [],266 "name": "collectionSponsor",267 "outputs": [268 {269 "components": [270 { "internalType": "address", "name": "field_0", "type": "address" },271 { "internalType": "uint256", "name": "field_1", "type": "uint256" }272 ],273 "internalType": "struct Tuple29",274 "name": "",275 "type": "tuple"276 }277 ],278 "stateMutability": "view",279 "type": "function"280 },281 {282 "inputs": [],283 "name": "confirmCollectionSponsorship",284 "outputs": [],285 "stateMutability": "nonpayable",286 "type": "function"287 },288 {289 "inputs": [],290 "name": "contractAddress",291 "outputs": [{ "internalType": "address", "name": "", "type": "address" }],292 "stateMutability": "view",293 "type": "function"294 },295 {296 "inputs": [297 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }298 ],299 "name": "crossOwnerOf",300 "outputs": [301 {302 "components": [303 { "internalType": "address", "name": "eth", "type": "address" },304 { "internalType": "uint256", "name": "sub", "type": "uint256" }305 ],306 "internalType": "struct EthCrossAccount",307 "name": "",308 "type": "tuple"309 }310 ],311 "stateMutability": "view",312 "type": "function"313 },314 {315 "inputs": [316 { "internalType": "string[]", "name": "keys", "type": "string[]" }317 ],318 "name": "deleteCollectionProperties",319 "outputs": [],320 "stateMutability": "nonpayable",321 "type": "function"322 },323 {324 "inputs": [325 { "internalType": "uint256", "name": "tokenId", "type": "uint256" },326 { "internalType": "string[]", "name": "keys", "type": "string[]" }327 ],328 "name": "deleteProperties",329 "outputs": [],330 "stateMutability": "nonpayable",331 "type": "function"332 },333 {334 "inputs": [],335 "name": "description",336 "outputs": [{ "internalType": "string", "name": "", "type": "string" }],337 "stateMutability": "view",338 "type": "function"339 },340 {341 "inputs": [],342 "name": "finishMinting",343 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],344 "stateMutability": "nonpayable",345 "type": "function"346 },347 {348 "inputs": [349 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }350 ],351 "name": "getApproved",352 "outputs": [{ "internalType": "address", "name": "", "type": "address" }],353 "stateMutability": "view",354 "type": "function"355 },356 {357 "inputs": [],358 "name": "hasCollectionPendingSponsor",359 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],360 "stateMutability": "view",361 "type": "function"362 },363 {364 "inputs": [365 { "internalType": "address", "name": "owner", "type": "address" },366 { "internalType": "address", "name": "operator", "type": "address" }367 ],368 "name": "isApprovedForAll",369 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],370 "stateMutability": "view",371 "type": "function"372 },373 {374 "inputs": [375 {376 "components": [377 { "internalType": "address", "name": "eth", "type": "address" },378 { "internalType": "uint256", "name": "sub", "type": "uint256" }379 ],380 "internalType": "struct EthCrossAccount",381 "name": "user",382 "type": "tuple"383 }384 ],385 "name": "isOwnerOrAdminCross",386 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],387 "stateMutability": "view",388 "type": "function"389 },390 {391 "inputs": [{ "internalType": "address", "name": "to", "type": "address" }],392 "name": "mint",393 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],394 "stateMutability": "nonpayable",395 "type": "function"396 },397 {398 "inputs": [399 { "internalType": "address", "name": "to", "type": "address" },400 { "internalType": "string", "name": "tokenUri", "type": "string" }401 ],402 "name": "mintWithTokenURI",403 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],404 "stateMutability": "nonpayable",405 "type": "function"406 },407 {408 "inputs": [],409 "name": "mintingFinished",410 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],411 "stateMutability": "view",412 "type": "function"413 },414 {415 "inputs": [],416 "name": "name",417 "outputs": [{ "internalType": "string", "name": "", "type": "string" }],418 "stateMutability": "view",419 "type": "function"420 },421 {422 "inputs": [],423 "name": "nextTokenId",424 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],425 "stateMutability": "view",426 "type": "function"427 },428 {429 "inputs": [430 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }431 ],432 "name": "ownerOf",433 "outputs": [{ "internalType": "address", "name": "", "type": "address" }],434 "stateMutability": "view",435 "type": "function"436 },437 {438 "inputs": [439 { "internalType": "uint256", "name": "tokenId", "type": "uint256" },440 { "internalType": "string[]", "name": "keys", "type": "string[]" }441 ],442 "name": "properties",443 "outputs": [444 {445 "components": [446 { "internalType": "string", "name": "key", "type": "string" },447 { "internalType": "bytes", "name": "value", "type": "bytes" }448 ],449 "internalType": "struct Property[]",450 "name": "",451 "type": "tuple[]"452 }453 ],454 "stateMutability": "view",455 "type": "function"456 },457 {458 "inputs": [459 { "internalType": "uint256", "name": "tokenId", "type": "uint256" },460 { "internalType": "string", "name": "key", "type": "string" }461 ],462 "name": "property",463 "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],464 "stateMutability": "view",465 "type": "function"466 },467 {468 "inputs": [469 {470 "components": [471 { "internalType": "address", "name": "eth", "type": "address" },472 { "internalType": "uint256", "name": "sub", "type": "uint256" }473 ],474 "internalType": "struct EthCrossAccount",475 "name": "admin",476 "type": "tuple"477 }478 ],479 "name": "removeCollectionAdminCross",480 "outputs": [],481 "stateMutability": "nonpayable",482 "type": "function"483 },484 {485 "inputs": [],486 "name": "removeCollectionSponsor",487 "outputs": [],488 "stateMutability": "nonpayable",489 "type": "function"490 },491 {492 "inputs": [493 {494 "components": [495 { "internalType": "address", "name": "eth", "type": "address" },496 { "internalType": "uint256", "name": "sub", "type": "uint256" }497 ],498 "internalType": "struct EthCrossAccount",499 "name": "user",500 "type": "tuple"501 }502 ],503 "name": "removeFromCollectionAllowListCross",504 "outputs": [],505 "stateMutability": "nonpayable",506 "type": "function"507 },508 {509 "inputs": [510 { "internalType": "address", "name": "from", "type": "address" },511 { "internalType": "address", "name": "to", "type": "address" },512 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }513 ],514 "name": "safeTransferFrom",515 "outputs": [],516 "stateMutability": "nonpayable",517 "type": "function"518 },519 {520 "inputs": [521 { "internalType": "address", "name": "from", "type": "address" },522 { "internalType": "address", "name": "to", "type": "address" },523 { "internalType": "uint256", "name": "tokenId", "type": "uint256" },524 { "internalType": "bytes", "name": "data", "type": "bytes" }525 ],526 "name": "safeTransferFromWithData",527 "outputs": [],528 "stateMutability": "nonpayable",529 "type": "function"530 },531 {532 "inputs": [533 { "internalType": "address", "name": "operator", "type": "address" },534 { "internalType": "bool", "name": "approved", "type": "bool" }535 ],536 "name": "setApprovalForAll",537 "outputs": [],538 "stateMutability": "nonpayable",539 "type": "function"540 },541 {542 "inputs": [{ "internalType": "uint8", "name": "mode", "type": "uint8" }],543 "name": "setCollectionAccess",544 "outputs": [],545 "stateMutability": "nonpayable",546 "type": "function"547 },548 {549 "inputs": [550 { "internalType": "string", "name": "limit", "type": "string" },551 { "internalType": "uint256", "name": "value", "type": "uint256" }552 ],553 "name": "setCollectionLimit",554 "outputs": [],555 "stateMutability": "nonpayable",556 "type": "function"557 },558 {559 "inputs": [{ "internalType": "bool", "name": "mode", "type": "bool" }],560 "name": "setCollectionMintMode",561 "outputs": [],562 "stateMutability": "nonpayable",563 "type": "function"564 },565 {566 "inputs": [{ "internalType": "bool", "name": "enable", "type": "bool" }],567 "name": "setCollectionNesting",568 "outputs": [],569 "stateMutability": "nonpayable",570 "type": "function"571 },572 {573 "inputs": [574 { "internalType": "bool", "name": "enable", "type": "bool" },575 {576 "internalType": "address[]",577 "name": "collections",578 "type": "address[]"579 }580 ],581 "name": "setCollectionNesting",582 "outputs": [],583 "stateMutability": "nonpayable",584 "type": "function"585 },586 {587 "inputs": [588 {589 "components": [590 { "internalType": "string", "name": "key", "type": "string" },591 { "internalType": "bytes", "name": "value", "type": "bytes" }592 ],593 "internalType": "struct Property[]",594 "name": "properties",595 "type": "tuple[]"596 }597 ],598 "name": "setCollectionProperties",599 "outputs": [],600 "stateMutability": "nonpayable",601 "type": "function"602 },603 {604 "inputs": [605 {606 "components": [607 { "internalType": "address", "name": "eth", "type": "address" },608 { "internalType": "uint256", "name": "sub", "type": "uint256" }609 ],610 "internalType": "struct EthCrossAccount",611 "name": "sponsor",612 "type": "tuple"613 }614 ],615 "name": "setCollectionSponsorCross",616 "outputs": [],617 "stateMutability": "nonpayable",618 "type": "function"619 },620 {621 "inputs": [622 { "internalType": "uint256", "name": "tokenId", "type": "uint256" },623 {624 "components": [625 { "internalType": "string", "name": "key", "type": "string" },626 { "internalType": "bytes", "name": "value", "type": "bytes" }627 ],628 "internalType": "struct Property[]",629 "name": "properties",630 "type": "tuple[]"631 }632 ],633 "name": "setProperties",634 "outputs": [],635 "stateMutability": "nonpayable",636 "type": "function"637 },638 {639 "inputs": [640 { "internalType": "string", "name": "key", "type": "string" },641 { "internalType": "bool", "name": "isMutable", "type": "bool" },642 { "internalType": "bool", "name": "collectionAdmin", "type": "bool" },643 { "internalType": "bool", "name": "tokenOwner", "type": "bool" }644 ],645 "name": "setTokenPropertyPermission",646 "outputs": [],647 "stateMutability": "nonpayable",648 "type": "function"649 },650 {651 "inputs": [652 { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }653 ],654 "name": "supportsInterface",655 "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],656 "stateMutability": "view",657 "type": "function"658 },659 {660 "inputs": [],661 "name": "symbol",662 "outputs": [{ "internalType": "string", "name": "", "type": "string" }],663 "stateMutability": "view",664 "type": "function"665 },666 {667 "inputs": [668 { "internalType": "uint256", "name": "index", "type": "uint256" }669 ],670 "name": "tokenByIndex",671 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],672 "stateMutability": "view",673 "type": "function"674 },675 {676 "inputs": [677 { "internalType": "uint256", "name": "token", "type": "uint256" }678 ],679 "name": "tokenContractAddress",680 "outputs": [{ "internalType": "address", "name": "", "type": "address" }],681 "stateMutability": "view",682 "type": "function"683 },684 {685 "inputs": [686 { "internalType": "address", "name": "owner", "type": "address" },687 { "internalType": "uint256", "name": "index", "type": "uint256" }688 ],689 "name": "tokenOfOwnerByIndex",690 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],691 "stateMutability": "view",692 "type": "function"693 },694 {695 "inputs": [696 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }697 ],698 "name": "tokenURI",699 "outputs": [{ "internalType": "string", "name": "", "type": "string" }],700 "stateMutability": "view",701 "type": "function"702 },703 {704 "inputs": [],705 "name": "totalSupply",706 "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],707 "stateMutability": "view",708 "type": "function"709 },710 {711 "inputs": [712 { "internalType": "address", "name": "to", "type": "address" },713 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }714 ],715 "name": "transfer",716 "outputs": [],717 "stateMutability": "nonpayable",718 "type": "function"719 },720 {721 "inputs": [722 {723 "components": [724 { "internalType": "address", "name": "eth", "type": "address" },725 { "internalType": "uint256", "name": "sub", "type": "uint256" }726 ],727 "internalType": "struct EthCrossAccount",728 "name": "to",729 "type": "tuple"730 },731 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }732 ],733 "name": "transferCross",734 "outputs": [],735 "stateMutability": "nonpayable",736 "type": "function"737 },738 {739 "inputs": [740 { "internalType": "address", "name": "from", "type": "address" },741 { "internalType": "address", "name": "to", "type": "address" },742 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }743 ],744 "name": "transferFrom",745 "outputs": [],746 "stateMutability": "nonpayable",747 "type": "function"748 },749 {750 "inputs": [751 {752 "components": [753 { "internalType": "address", "name": "eth", "type": "address" },754 { "internalType": "uint256", "name": "sub", "type": "uint256" }755 ],756 "internalType": "struct EthCrossAccount",757 "name": "from",758 "type": "tuple"759 },760 {761 "components": [762 { "internalType": "address", "name": "eth", "type": "address" },763 { "internalType": "uint256", "name": "sub", "type": "uint256" }764 ],765 "internalType": "struct EthCrossAccount",766 "name": "to",767 "type": "tuple"768 },769 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }770 ],771 "name": "transferFromCross",772 "outputs": [],773 "stateMutability": "nonpayable",774 "type": "function"775 },776 {777 "inputs": [],778 "name": "uniqueCollectionType",779 "outputs": [{ "internalType": "string", "name": "", "type": "string" }],780 "stateMutability": "view",781 "type": "function"782 }783]tests/src/eth/api/UniqueNFT.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueNFT.sol
+++ b/tests/src/eth/api/UniqueNFT.sol
@@ -671,7 +671,10 @@
/// or in textual repr: approve(address,uint256)
function approve(address approved, uint256 tokenId) external;
- /// @dev Not implemented
+ /// @notice 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
/// @dev EVM selector for this function is: 0xa22cb465,
/// or in textual repr: setApprovalForAll(address,bool)
function setApprovalForAll(address operator, bool approved) external;
@@ -681,10 +684,10 @@
/// or in textual repr: getApproved(uint256)
function getApproved(uint256 tokenId) external view returns (address);
- /// @dev Not implemented
+ /// @notice Tells whether an operator is approved by a given owner.
/// @dev EVM selector for this function is: 0xe985e9c5,
/// or in textual repr: isApprovedForAll(address,address)
- function isApprovedForAll(address owner, address operator) external view returns (address);
+ function isApprovedForAll(address owner, address operator) external view returns (bool);
/// @notice Returns collection helper contract address
/// @dev EVM selector for this function is: 0x1896cce6,
tests/src/eth/api/UniqueRefungible.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueRefungible.sol
+++ b/tests/src/eth/api/UniqueRefungible.sol
@@ -668,7 +668,10 @@
/// or in textual repr: approve(address,uint256)
function approve(address approved, uint256 tokenId) external;
- /// @dev Not implemented
+ /// @notice 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
/// @dev EVM selector for this function is: 0xa22cb465,
/// or in textual repr: setApprovalForAll(address,bool)
function setApprovalForAll(address operator, bool approved) external;
@@ -678,10 +681,10 @@
/// or in textual repr: getApproved(uint256)
function getApproved(uint256 tokenId) external view returns (address);
- /// @dev Not implemented
+ /// @notice Tells whether an operator is approved by a given owner.
/// @dev EVM selector for this function is: 0xe985e9c5,
/// or in textual repr: isApprovedForAll(address,address)
- function isApprovedForAll(address owner, address operator) external view returns (address);
+ function isApprovedForAll(address owner, address operator) external view returns (bool);
/// @notice Returns collection helper contract address
/// @dev EVM selector for this function is: 0x1896cce6,
tests/src/eth/nonFungible.test.tsdiffbeforeafterboth--- a/tests/src/eth/nonFungible.test.ts
+++ b/tests/src/eth/nonFungible.test.ts
@@ -249,6 +249,114 @@
}
});
+ itEth('Can perform setApprovalForAll()', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const operator = helper.eth.createAccount();
+
+ const collection = await helper.nft.mintCollection(minter, {});
+
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+
+ const approvedBefore = await contract.methods.isApprovedForAll(owner, operator).call();
+ expect(approvedBefore).to.be.equal(false);
+
+ {
+ const result = await contract.methods.setApprovalForAll(operator, true).send({from: owner});
+
+ expect(result.events.ApprovalForAll).to.be.like({
+ address: collectionAddress,
+ event: 'ApprovalForAll',
+ returnValues: {
+ owner,
+ operator,
+ approved: true,
+ },
+ });
+
+ const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call();
+ expect(approvedAfter).to.be.equal(true);
+ }
+
+ {
+ const result = await contract.methods.setApprovalForAll(operator, false).send({from: owner});
+
+ expect(result.events.ApprovalForAll).to.be.like({
+ address: collectionAddress,
+ event: 'ApprovalForAll',
+ returnValues: {
+ owner,
+ operator,
+ approved: false,
+ },
+ });
+
+ const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call();
+ expect(approvedAfter).to.be.equal(false);
+ }
+ });
+
+ itEth('Can perform burn with ApprovalForAll', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
+
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const operator = await helper.eth.createAccountWithBalance(donor, 100n);
+
+ const token = await collection.mintToken(minter, {Ethereum: owner});
+
+ const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(address, 'nft');
+
+ {
+ await contract.methods.setApprovalForAll(operator, true).send({from: owner});
+ const ownerCross = helper.ethCrossAccount.fromAddress(owner);
+ const result = await contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: operator});
+ const events = result.events.Transfer;
+
+ expect(events).to.be.like({
+ address,
+ event: 'Transfer',
+ returnValues: {
+ from: owner,
+ to: '0x0000000000000000000000000000000000000000',
+ tokenId: token.tokenId.toString(),
+ },
+ });
+ }
+ });
+
+ itEth('Can perform transfer with ApprovalForAll', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
+
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const operator = await helper.eth.createAccountWithBalance(donor);
+ const receiver = charlie;
+
+ const token = await collection.mintToken(minter, {Ethereum: owner});
+
+ const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(address, 'nft');
+
+ {
+ await contract.methods.setApprovalForAll(operator, true).send({from: owner});
+ const ownerCross = helper.ethCrossAccount.fromAddress(owner);
+ const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);
+ const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: operator});
+ const event = result.events.Transfer;
+ expect(event).to.be.like({
+ address: helper.ethAddress.fromCollectionId(collection.collectionId),
+ event: 'Transfer',
+ returnValues: {
+ from: owner,
+ to: helper.address.substrateToEth(receiver.address),
+ tokenId: token.tokenId.toString(),
+ },
+ });
+ }
+
+ expect(await token.getOwner()).to.be.like({Substrate: receiver.address});
+ });
+
itEth('Can perform burnFromCross()', async ({helper}) => {
const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
const ownerSub = bob;
@@ -822,3 +930,53 @@
expect(symbol).to.equal('CHANGE');
});
});
+
+describe('Negative tests', () => {
+ let donor: IKeyringPair;
+ let minter: IKeyringPair;
+ let alice: IKeyringPair;
+ let bob: IKeyringPair;
+
+ before(async function() {
+ await usingEthPlaygrounds(async (helper, privateKey) => {
+ donor = await privateKey({filename: __filename});
+ [minter, alice, bob] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
+ });
+ });
+
+ itEth('[negative] Cant perform burn without approval', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
+
+ const owner = bob;
+ const spender = await helper.eth.createAccountWithBalance(donor, 100n);
+
+ const token = await collection.mintToken(minter, {Substrate: owner.address});
+
+ const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(address, 'nft');
+
+ {
+ const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);
+ await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected;
+ }
+ });
+
+ itEth('[negative] Cant perform transfer without approval', async ({helper}) => {
+ const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
+ const owner = bob;
+ const receiver = alice;
+
+ const spender = await helper.eth.createAccountWithBalance(donor, 100n);
+
+ const token = await collection.mintToken(minter, {Substrate: owner.address});
+
+ const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(address, 'nft');
+
+ {
+ const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);
+ const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);
+ await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected;
+ }
+ });
+});
tests/src/eth/reFungible.test.tsdiffbeforeafterboth--- a/tests/src/eth/reFungible.test.ts
+++ b/tests/src/eth/reFungible.test.ts
@@ -169,6 +169,136 @@
}
});
+ itEth('Can perform setApprovalForAll()', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const operator = helper.eth.createAccount();
+
+ const collection = await helper.rft.mintCollection(minter, {});
+
+ const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
+
+ const approvedBefore = await contract.methods.isApprovedForAll(owner, operator).call();
+ expect(approvedBefore).to.be.equal(false);
+
+ {
+ const result = await contract.methods.setApprovalForAll(operator, true).send({from: owner});
+
+ expect(result.events.ApprovalForAll).to.be.like({
+ address: collectionAddress,
+ event: 'ApprovalForAll',
+ returnValues: {
+ owner,
+ operator,
+ approved: true,
+ },
+ });
+
+ const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call();
+ expect(approvedAfter).to.be.equal(true);
+ }
+
+ {
+ const result = await contract.methods.setApprovalForAll(operator, false).send({from: owner});
+
+ expect(result.events.ApprovalForAll).to.be.like({
+ address: collectionAddress,
+ event: 'ApprovalForAll',
+ returnValues: {
+ owner,
+ operator,
+ approved: false,
+ },
+ });
+
+ const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call();
+ expect(approvedAfter).to.be.equal(false);
+ }
+ });
+
+ itEth('Can perform burn with ApprovalForAll', async ({helper}) => {
+ const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
+
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const operator = await helper.eth.createAccountWithBalance(donor, 100n);
+
+ const token = await collection.mintToken(minter, 100n, {Ethereum: owner});
+
+ const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(address, 'rft');
+
+ {
+ await contract.methods.setApprovalForAll(operator, true).send({from: owner});
+ const ownerCross = helper.ethCrossAccount.fromAddress(owner);
+ const result = await contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: operator});
+ const events = result.events.Transfer;
+
+ expect(events).to.be.like({
+ address,
+ event: 'Transfer',
+ returnValues: {
+ from: owner,
+ to: '0x0000000000000000000000000000000000000000',
+ tokenId: token.tokenId.toString(),
+ },
+ });
+ }
+ });
+
+ itEth('Can perform burn with approve and approvalForAll', async ({helper}) => {
+ const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
+
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const operator = await helper.eth.createAccountWithBalance(donor, 100n);
+
+ const token = await collection.mintToken(minter, 100n, {Ethereum: owner});
+
+ const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(address, 'rft');
+
+ const rftToken = helper.ethNativeContract.rftTokenById(token.collectionId, token.tokenId, owner);
+
+ {
+ await rftToken.methods.approve(operator, 15n).send({from: owner});
+ await contract.methods.setApprovalForAll(operator, true).send({from: owner});
+ await rftToken.methods.burnFrom(owner, 10n).send({from: operator});
+ const allowance = await rftToken.methods.allowance(owner, operator).call();
+ expect(allowance).to.be.equal('5');
+ }
+ });
+
+ itEth('Can perform transfer with ApprovalForAll', async ({helper}) => {
+ const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
+
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const operator = await helper.eth.createAccountWithBalance(donor);
+ const receiver = charlie;
+
+ const token = await collection.mintToken(minter, 100n, {Ethereum: owner});
+
+ const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+ const contract = helper.ethNativeContract.collection(address, 'rft');
+
+ {
+ await contract.methods.setApprovalForAll(operator, true).send({from: owner});
+ const ownerCross = helper.ethCrossAccount.fromAddress(owner);
+ const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);
+ const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: operator});
+ const event = result.events.Transfer;
+ expect(event).to.be.like({
+ address: helper.ethAddress.fromCollectionId(collection.collectionId),
+ event: 'Transfer',
+ returnValues: {
+ from: owner,
+ to: helper.address.substrateToEth(receiver.address),
+ tokenId: token.tokenId.toString(),
+ },
+ });
+ }
+
+ expect(await token.getTop10Owners()).to.be.like([{Substrate: receiver.address}]);
+ });
+
itEth('Can perform burn()', async ({helper}) => {
const caller = await helper.eth.createAccountWithBalance(donor);
const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Burny', '6', '6');
@@ -594,3 +724,52 @@
expect(symbol).to.equal('12');
});
});
+
+describe('Negative tests', () => {
+ let donor: IKeyringPair;
+ let minter: IKeyringPair;
+ let alice: IKeyringPair;
+
+ before(async function() {
+ await usingEthPlaygrounds(async (helper, privateKey) => {
+ donor = await privateKey({filename: __filename});
+ [minter, alice] = await helper.arrange.createAccounts([100n, 100n], donor);
+ });
+ });
+
+ itEth('[negative] Cant perform burn without approval', async ({helper}) => {
+ const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
+
+ const owner = await helper.eth.createAccountWithBalance(donor, 100n);
+ const spender = await helper.eth.createAccountWithBalance(donor, 100n);
+
+ const token = await collection.mintToken(minter, 100n, {Ethereum: owner});
+
+ 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;
+ }
+ });
+
+ itEth('[negative] Cant perform transfer without approval', async ({helper}) => {
+ const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
+ const owner = await helper.eth.createAccountWithBalance(donor, 100n);
+ const receiver = alice;
+
+ const spender = await helper.eth.createAccountWithBalance(donor, 100n);
+
+ const token = await collection.mintToken(minter, 100n, {Ethereum: owner});
+
+ 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;
+ }
+ });
+});
tests/src/interfaces/augment-api-errors.tsdiffbeforeafterboth--- a/tests/src/interfaces/augment-api-errors.ts
+++ b/tests/src/interfaces/augment-api-errors.ts
@@ -386,6 +386,10 @@
**/
NotFungibleDataUsedToMintFungibleCollectionToken: AugmentedError<ApiType>;
/**
+ * Setting approval for all is not allowed.
+ **/
+ SettingApprovalForAllNotAllowed: AugmentedError<ApiType>;
+ /**
* Setting item properties is not allowed.
**/
SettingPropertiesNotAllowed: AugmentedError<ApiType>;
tests/src/interfaces/augment-api-events.tsdiffbeforeafterboth--- a/tests/src/interfaces/augment-api-events.ts
+++ b/tests/src/interfaces/augment-api-events.ts
@@ -107,6 +107,10 @@
**/
Approved: AugmentedEvent<ApiType, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
/**
+ * Amount pieces of token owned by `sender` was approved for `spender`.
+ **/
+ ApprovedForAll: AugmentedEvent<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, bool]>;
+ /**
* New collection was created
**/
CollectionCreated: AugmentedEvent<ApiType, [u32, u8, AccountId32]>;
tests/src/interfaces/augment-api-query.tsdiffbeforeafterboth--- a/tests/src/interfaces/augment-api-query.ts
+++ b/tests/src/interfaces/augment-api-query.ts
@@ -442,6 +442,10 @@
**/
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
**/
[key: string]: QueryableStorageEntry<ApiType>;
@@ -645,6 +649,10 @@
**/
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
**/
[key: string]: QueryableStorageEntry<ApiType>;
tests/src/interfaces/augment-api-rpc.tsdiffbeforeafterboth--- a/tests/src/interfaces/augment-api-rpc.ts
+++ b/tests/src/interfaces/augment-api-rpc.ts
@@ -720,6 +720,10 @@
**/
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
**/
lastTokenId: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<u32>>;
tests/src/interfaces/augment-api-tx.tsdiffbeforeafterboth--- a/tests/src/interfaces/augment-api-tx.ts
+++ b/tests/src/interfaces/augment-api-tx.ts
@@ -1545,6 +1545,18 @@
**/
repartition: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, u32, u128]>;
/**
+ * Sets or unsets the approval of a given operator.
+ *
+ * An operator is allowed to transfer all tokens of the sender on their behalf.
+ *
+ * # Arguments
+ *
+ * * `owner`: Token owner
+ * * `operator`: Operator
+ * * `approve`: Is operator enabled or disabled
+ **/
+ setApprovalForAll: 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.
*
* # Permissions
tests/src/interfaces/default/types.tsdiffbeforeafterboth--- a/tests/src/interfaces/default/types.ts
+++ b/tests/src/interfaces/default/types.ts
@@ -1286,6 +1286,8 @@
readonly asTransfer: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
readonly isApproved: boolean;
readonly asApproved: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
+ readonly isApprovedForAll: boolean;
+ readonly asApprovedForAll: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, bool]>;
readonly isCollectionPropertySet: boolean;
readonly asCollectionPropertySet: ITuple<[u32, Bytes]>;
readonly isCollectionPropertyDeleted: boolean;
@@ -1296,7 +1298,7 @@
readonly asTokenPropertyDeleted: ITuple<[u32, u32, Bytes]>;
readonly isPropertyPermissionSet: boolean;
readonly asPropertyPermissionSet: ITuple<[u32, Bytes]>;
- readonly type: 'CollectionCreated' | 'CollectionDestroyed' | 'ItemCreated' | 'ItemDestroyed' | 'Transfer' | 'Approved' | 'CollectionPropertySet' | 'CollectionPropertyDeleted' | 'TokenPropertySet' | 'TokenPropertyDeleted' | 'PropertyPermissionSet';
+ readonly type: 'CollectionCreated' | 'CollectionDestroyed' | 'ItemCreated' | 'ItemDestroyed' | 'Transfer' | 'Approved' | 'ApprovedForAll' | 'CollectionPropertySet' | 'CollectionPropertyDeleted' | 'TokenPropertySet' | 'TokenPropertyDeleted' | 'PropertyPermissionSet';
}
/** @name PalletConfigurationCall */
@@ -1603,7 +1605,8 @@
readonly isFungibleItemsDontHaveData: boolean;
readonly isFungibleDisallowsNesting: boolean;
readonly isSettingPropertiesNotAllowed: boolean;
- readonly type: 'NotFungibleDataUsedToMintFungibleCollectionToken' | 'FungibleItemsHaveNoId' | 'FungibleItemsDontHaveData' | 'FungibleDisallowsNesting' | 'SettingPropertiesNotAllowed';
+ readonly isSettingApprovalForAllNotAllowed: boolean;
+ readonly type: 'NotFungibleDataUsedToMintFungibleCollectionToken' | 'FungibleItemsHaveNoId' | 'FungibleItemsDontHaveData' | 'FungibleDisallowsNesting' | 'SettingPropertiesNotAllowed' | 'SettingApprovalForAllNotAllowed';
}
/** @name PalletInflationCall */
@@ -2309,7 +2312,13 @@
readonly tokenId: u32;
readonly amount: u128;
} & 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';
+ readonly isSetApprovalForAll: boolean;
+ readonly asSetApprovalForAll: {
+ 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';
}
/** @name PalletUniqueError */
tests/src/interfaces/lookup.tsdiffbeforeafterboth--- a/tests/src/interfaces/lookup.ts
+++ b/tests/src/interfaces/lookup.ts
@@ -1046,6 +1046,7 @@
ItemDestroyed: '(u32,u32,PalletEvmAccountBasicCrossAccountIdRepr,u128)',
Transfer: '(u32,u32,PalletEvmAccountBasicCrossAccountIdRepr,PalletEvmAccountBasicCrossAccountIdRepr,u128)',
Approved: '(u32,u32,PalletEvmAccountBasicCrossAccountIdRepr,PalletEvmAccountBasicCrossAccountIdRepr,u128)',
+ ApprovedForAll: '(u32,PalletEvmAccountBasicCrossAccountIdRepr,PalletEvmAccountBasicCrossAccountIdRepr,bool)',
CollectionPropertySet: '(u32,Bytes)',
CollectionPropertyDeleted: '(u32,Bytes)',
TokenPropertySet: '(u32,u32,Bytes)',
@@ -1054,7 +1055,7 @@
}
},
/**
- * Lookup99: pallet_structure::pallet::Event<T>
+ * Lookup100: pallet_structure::pallet::Event<T>
**/
PalletStructureEvent: {
_enum: {
@@ -1062,7 +1063,7 @@
}
},
/**
- * Lookup100: pallet_rmrk_core::pallet::Event<T>
+ * Lookup101: pallet_rmrk_core::pallet::Event<T>
**/
PalletRmrkCoreEvent: {
_enum: {
@@ -1139,7 +1140,7 @@
}
},
/**
- * Lookup101: rmrk_traits::nft::AccountIdOrCollectionNftTuple<sp_core::crypto::AccountId32>
+ * Lookup102: rmrk_traits::nft::AccountIdOrCollectionNftTuple<sp_core::crypto::AccountId32>
**/
RmrkTraitsNftAccountIdOrCollectionNftTuple: {
_enum: {
@@ -2302,7 +2303,12 @@
repartition: {
collectionId: 'u32',
tokenId: 'u32',
- amount: 'u128'
+ amount: 'u128',
+ },
+ set_approval_for_all: {
+ collectionId: 'u32',
+ operator: 'PalletEvmAccountBasicCrossAccountIdRepr',
+ approve: 'bool'
}
}
},
@@ -3445,7 +3451,7 @@
* Lookup430: pallet_fungible::pallet::Error<T>
**/
PalletFungibleError: {
- _enum: ['NotFungibleDataUsedToMintFungibleCollectionToken', 'FungibleItemsHaveNoId', 'FungibleItemsDontHaveData', 'FungibleDisallowsNesting', 'SettingPropertiesNotAllowed']
+ _enum: ['NotFungibleDataUsedToMintFungibleCollectionToken', 'FungibleItemsHaveNoId', 'FungibleItemsDontHaveData', 'FungibleDisallowsNesting', 'SettingPropertiesNotAllowed', 'SettingApprovalForAllNotAllowed']
},
/**
* Lookup431: pallet_refungible::ItemData
tests/src/interfaces/types-lookup.tsdiffbeforeafterboth--- a/tests/src/interfaces/types-lookup.ts
+++ b/tests/src/interfaces/types-lookup.ts
@@ -1182,6 +1182,8 @@
readonly asTransfer: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
readonly isApproved: boolean;
readonly asApproved: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
+ readonly isApprovedForAll: boolean;
+ readonly asApprovedForAll: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, bool]>;
readonly isCollectionPropertySet: boolean;
readonly asCollectionPropertySet: ITuple<[u32, Bytes]>;
readonly isCollectionPropertyDeleted: boolean;
@@ -1192,17 +1194,17 @@
readonly asTokenPropertyDeleted: ITuple<[u32, u32, Bytes]>;
readonly isPropertyPermissionSet: boolean;
readonly asPropertyPermissionSet: ITuple<[u32, Bytes]>;
- readonly type: 'CollectionCreated' | 'CollectionDestroyed' | 'ItemCreated' | 'ItemDestroyed' | 'Transfer' | 'Approved' | 'CollectionPropertySet' | 'CollectionPropertyDeleted' | 'TokenPropertySet' | 'TokenPropertyDeleted' | 'PropertyPermissionSet';
+ readonly type: 'CollectionCreated' | 'CollectionDestroyed' | 'ItemCreated' | 'ItemDestroyed' | 'Transfer' | 'Approved' | 'ApprovedForAll' | 'CollectionPropertySet' | 'CollectionPropertyDeleted' | 'TokenPropertySet' | 'TokenPropertyDeleted' | 'PropertyPermissionSet';
}
- /** @name PalletStructureEvent (99) */
+ /** @name PalletStructureEvent (100) */
interface PalletStructureEvent extends Enum {
readonly isExecuted: boolean;
readonly asExecuted: Result<Null, SpRuntimeDispatchError>;
readonly type: 'Executed';
}
- /** @name PalletRmrkCoreEvent (100) */
+ /** @name PalletRmrkCoreEvent (101) */
interface PalletRmrkCoreEvent extends Enum {
readonly isCollectionCreated: boolean;
readonly asCollectionCreated: {
@@ -1292,7 +1294,7 @@
readonly type: 'CollectionCreated' | 'CollectionDestroyed' | 'IssuerChanged' | 'CollectionLocked' | 'NftMinted' | 'NftBurned' | 'NftSent' | 'NftAccepted' | 'NftRejected' | 'PropertySet' | 'ResourceAdded' | 'ResourceRemoval' | 'ResourceAccepted' | 'ResourceRemovalAccepted' | 'PrioritySet';
}
- /** @name RmrkTraitsNftAccountIdOrCollectionNftTuple (101) */
+ /** @name RmrkTraitsNftAccountIdOrCollectionNftTuple (102) */
interface RmrkTraitsNftAccountIdOrCollectionNftTuple extends Enum {
readonly isAccountId: boolean;
readonly asAccountId: AccountId32;
@@ -2539,7 +2541,13 @@
readonly tokenId: u32;
readonly amount: u128;
} & 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';
+ readonly isSetApprovalForAll: boolean;
+ readonly asSetApprovalForAll: {
+ 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';
}
/** @name UpDataStructsCollectionMode (240) */
@@ -3655,7 +3663,8 @@
readonly isFungibleItemsDontHaveData: boolean;
readonly isFungibleDisallowsNesting: boolean;
readonly isSettingPropertiesNotAllowed: boolean;
- readonly type: 'NotFungibleDataUsedToMintFungibleCollectionToken' | 'FungibleItemsHaveNoId' | 'FungibleItemsDontHaveData' | 'FungibleDisallowsNesting' | 'SettingPropertiesNotAllowed';
+ readonly isSettingApprovalForAllNotAllowed: boolean;
+ readonly type: 'NotFungibleDataUsedToMintFungibleCollectionToken' | 'FungibleItemsHaveNoId' | 'FungibleItemsDontHaveData' | 'FungibleDisallowsNesting' | 'SettingPropertiesNotAllowed' | 'SettingApprovalForAllNotAllowed';
}
/** @name PalletRefungibleItemData (431) */
tests/src/interfaces/unique/definitions.tsdiffbeforeafterboth--- a/tests/src/interfaces/unique/definitions.ts
+++ b/tests/src/interfaces/unique/definitions.ts
@@ -175,5 +175,10 @@
[collectionParam, tokenParam],
'Option<u128>',
),
+ isApprovedForAll: fun(
+ 'Tells whether an operator is approved by a given owner.',
+ [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
@@ -1413,6 +1413,32 @@
getTokenObject(_collectionId: number, _tokenId: number): any {
return null;
}
+
+ /**
+ * Tells whether an operator is approved by a given owner.
+ * @param collectionId ID of collection
+ * @param owner owner address
+ * @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();
+ }
+
+ /** 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
+ * @returns ```true``` if extrinsic success, otherwise ```false```
+ */
+ async setApprovalForAll(signer: TSigner, collectionId: number, operator: ICrossAccountId, approved: boolean): Promise<boolean> {
+ const result = await this.helper.executeExtrinsic(
+ signer,
+ 'api.tx.unique.setApprovalForAll', [collectionId, operator, approved],
+ true,
+ );
+ return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'ApprovedForAll');
+ }
}