difftreelog
fix clippy warnings
in: master
36 files changed
node/cli/src/chain_spec.rsdiffbeforeafterboth--- a/node/cli/src/chain_spec.rs
+++ b/node/cli/src/chain_spec.rs
@@ -110,7 +110,7 @@
/// Helper function to generate a crypto pair from seed
pub fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public {
- TPublic::Pair::from_string(&format!("//{}", seed), None)
+ TPublic::Pair::from_string(&format!("//{seed}"), None)
.expect("static values are valid; qed")
.public()
}
node/cli/src/command.rsdiffbeforeafterboth--- a/node/cli/src/command.rs
+++ b/node/cli/src/command.rs
@@ -83,7 +83,7 @@
"" | "local" => Box::new(chain_spec::local_testnet_config()),
path => {
let path = std::path::PathBuf::from(path);
- let chain_spec = Box::new(chain_spec::OpalChainSpec::from_json_file(path.clone())?)
+ let chain_spec = Box::new(chain_spec::OpalChainSpec::from_json_file(path)?)
as Box<dyn sc_service::ChainSpec>;
match chain_spec.runtime_id() {
@@ -352,7 +352,7 @@
&polkadot_cli,
config.tokio_handle.clone(),
)
- .map_err(|err| format!("Relay chain argument error: {}", err))?;
+ .map_err(|err| format!("Relay chain argument error: {err}"))?;
cmd.run(config, polkadot_config)
})
@@ -464,7 +464,7 @@
runner.run_node_until_exit(|config| async move {
let hwbench = if !cli.no_hardware_benchmarks {
config.database.path().map(|database_path| {
- let _ = std::fs::create_dir_all(&database_path);
+ let _ = std::fs::create_dir_all(database_path);
sc_sysinfo::gather_hwbench(Some(database_path))
})
} else {
@@ -513,7 +513,7 @@
let state_version =
RelayChainCli::native_runtime_version(&config.chain_spec).state_version();
let block: Block = generate_genesis_block(&*config.chain_spec, state_version)
- .map_err(|e| format!("{:?}", e))?;
+ .map_err(|e| format!("{e:?}"))?;
let genesis_state = format!("0x{:?}", HexDisplay::from(&block.header().encode()));
let genesis_hash = format!("0x{:?}", HexDisplay::from(&block.header().hash().0));
@@ -522,7 +522,7 @@
&polkadot_cli,
config.tokio_handle.clone(),
)
- .map_err(|err| format!("Relay chain argument error: {}", err))?;
+ .map_err(|err| format!("Relay chain argument error: {err}"))?;
info!("Parachain id: {:?}", para_id);
info!("Parachain Account: {}", parachain_account);
node/cli/src/service.rsdiffbeforeafterboth--- a/node/cli/src/service.rs
+++ b/node/cli/src/service.rs
@@ -698,7 +698,7 @@
{
let slot_duration = cumulus_client_consensus_aura::slot_duration(&*client)?;
- let block_import = ParachainBlockImport::new(client.clone(), backend.clone());
+ let block_import = ParachainBlockImport::new(client.clone(), backend);
cumulus_client_consensus_aura::import_queue::<
sp_consensus_aura::sr25519::AuthorityPair,
@@ -709,7 +709,7 @@
_,
>(cumulus_client_consensus_aura::ImportQueueParams {
block_import,
- client: client.clone(),
+ client,
create_inherent_data_providers: move |_, _| async move {
let time = sp_timestamp::InherentDataProvider::from_system_time();
@@ -787,7 +787,7 @@
telemetry.clone(),
);
- let block_import = ParachainBlockImport::new(client.clone(), backend.clone());
+ let block_import = ParachainBlockImport::new(client.clone(), backend);
Ok(AuraConsensus::build::<
sp_consensus_aura::sr25519::AuthorityPair,
@@ -864,7 +864,7 @@
ExecutorDispatch: NativeExecutionDispatch + 'static,
{
Ok(sc_consensus_manual_seal::import_queue(
- Box::new(client.clone()),
+ Box::new(client),
&task_manager.spawn_essential_handle(),
config.prometheus_registry(),
))
@@ -956,7 +956,7 @@
let collator = config.role.is_authority();
- let select_chain = maybe_select_chain.clone();
+ let select_chain = maybe_select_chain;
if collator {
let block_import =
node/rpc/src/lib.rsdiffbeforeafterboth--- a/node/rpc/src/lib.rs
+++ b/node/rpc/src/lib.rs
@@ -289,7 +289,7 @@
io.merge(
Net::new(
client.clone(),
- network.clone(),
+ network,
// Whether to format the `peer_count` response as Hex (default) or not.
true,
)
pallets/app-promotion/src/lib.rsdiffbeforeafterboth--- a/pallets/app-promotion/src/lib.rs
+++ b/pallets/app-promotion/src/lib.rs
@@ -296,7 +296,7 @@
if !block_pending.is_empty() {
block_pending.into_iter().for_each(|(staker, amount)| {
- Self::get_frozen_balance(&staker).map(|b| {
+ if let Some(b) = Self::get_frozen_balance(&staker) {
let new_state = b.checked_sub(&amount).unwrap_or_default();
// In this case, setting a new state for the frozen funds cannot fail
@@ -305,7 +305,7 @@
// that we cannot (in the current implementation) unfreeze more funds
// than were originally frozen by the pallet. Either way, `on_initialize()` cannot fail.
Self::set_freeze_unchecked(&staker, new_state);
- });
+ };
});
}
@@ -598,8 +598,8 @@
// this value is set for the stakers to whom the recalculation will be performed
let next_recalc_block = current_recalc_block + config.recalculation_interval;
- let mut storage_iterator = Self::get_next_calculated_key()
- .map_or(Staked::<T>::iter(), |key| Staked::<T>::iter_from(key));
+ let storage_iterator =
+ Self::get_next_calculated_key().map_or(Staked::<T>::iter(), Staked::<T>::iter_from);
PreviousCalculatedRecord::<T>::set(None);
@@ -658,10 +658,8 @@
// stakers_number - keeps the remaining number of iterations (staker addresses to handle)
// next_recalc_block_for_stake - is taken from the state and stores the starting relay block from which reward should be paid out
// income_acc - stores the reward amount to pay to the staker address (accumulates over all address stake records)
- while let Some((
- (current_id, staked_block),
- (amount, next_recalc_block_for_stake),
- )) = storage_iterator.next()
+ for ((current_id, staked_block), (amount, next_recalc_block_for_stake)) in
+ storage_iterator
{
// last_id is not equal current_id when we switch to handling a new staker address
// or just start handling the very first address. In the latter case last_id will be None and
@@ -859,11 +857,11 @@
if acc_amount < balance_per_block {
let res = (block, balance_per_block - acc_amount);
acc_amount = <BalanceOf<T>>::default();
- return Some(res);
+ Some(res)
} else {
acc_amount -= balance_per_block;
will_deleted_stakes_count += 1;
- return Some((block, <BalanceOf<T>>::default()));
+ Some((block, <BalanceOf<T>>::default()))
}
})
.collect::<Vec<_>>();
@@ -926,7 +924,7 @@
if amount.is_zero() {
<<T as Config>::Currency as MutateFreeze<T::AccountId>>::thaw(
&T::FreezeIdentifier::get(),
- &staker,
+ staker,
)
} else {
<<T as Config>::Currency as MutateFreeze<T::AccountId>>::set_freeze(
@@ -1026,10 +1024,10 @@
) {
let income = Self::calculate_income(base, iters);
- base.checked_add(&income).map(|res| {
+ if let Some(res) = base.checked_add(&income) {
<Staked<T>>::insert((staker, staked_block), (res, next_recalc_block));
*income_acc += income;
- });
+ };
}
fn calculate_income<I>(base: I, iters: u32) -> I
pallets/app-promotion/src/types.rsdiffbeforeafterboth--- a/pallets/app-promotion/src/types.rs
+++ b/pallets/app-promotion/src/types.rs
@@ -149,16 +149,16 @@
Self {
recalculation_interval: config
.recalculation_interval
- .unwrap_or_else(|| T::RecalculationInterval::get()),
+ .unwrap_or_else(T::RecalculationInterval::get),
pending_interval: config
.pending_interval
- .unwrap_or_else(|| T::PendingInterval::get()),
+ .unwrap_or_else(T::PendingInterval::get),
interval_income: config
.interval_income
- .unwrap_or_else(|| T::IntervalIncome::get()),
+ .unwrap_or_else(T::IntervalIncome::get),
max_stakers_per_calculation: config
.max_stakers_per_calculation
- .unwrap_or_else(|| MAX_NUMBER_PAYOUTS),
+ .unwrap_or(MAX_NUMBER_PAYOUTS),
}
}
}
pallets/balances-adapter/src/lib.rsdiffbeforeafterboth--- a/pallets/balances-adapter/src/lib.rs
+++ b/pallets/balances-adapter/src/lib.rs
@@ -31,6 +31,12 @@
}
}
+impl<T: Config> Default for NativeFungibleHandle<T> {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
impl<T: Config> WithRecorder<T> for NativeFungibleHandle<T> {
fn recorder(&self) -> &pallet_evm_coder_substrate::SubstrateRecorder<T> {
&self.0
pallets/common/src/eth.rsdiffbeforeafterboth--- a/pallets/common/src/eth.rs
+++ b/pallets/common/src/eth.rs
@@ -136,7 +136,7 @@
fn try_from(from: up_data_structs::Property) -> Result<Self, Self::Error> {
let key = evm_coder::types::String::from_utf8(from.key.into())
- .map_err(|e| Self::Error::Revert(format!("utf8 conversion error: {}", e)))?;
+ .map_err(|e| Self::Error::Revert(format!("utf8 conversion error: {e}")))?;
let value = evm_coder::types::Bytes(from.value.to_vec());
Ok(Property { key, value })
}
@@ -201,10 +201,7 @@
pub fn new(field: CollectionLimitField, value: Option<u32>) -> Self {
Self {
field,
- value: match value {
- Some(value) => Some(value.into()),
- None => None,
- },
+ value: value.map(|value| value.into()),
}
}
/// Whether the field contains a value.
@@ -222,8 +219,7 @@
.ok_or::<Self::Error>("can't convert `None` value to boolean".into())?;
let value = Some(value.try_into().map_err(|error| {
Self::Error::Revert(format!(
- "can't convert value to u32 \"{}\" because: \"{error}\"",
- value
+ "can't convert value to u32 \"{value}\" because: \"{error}\""
))
})?);
@@ -249,10 +245,8 @@
limits.sponsored_data_size = value;
}
CollectionLimitField::SponsoredDataRateLimit => {
- limits.sponsored_data_rate_limit = match value {
- Some(value) => Some(up_data_structs::SponsoringRateLimit::Blocks(value)),
- None => None,
- };
+ limits.sponsored_data_rate_limit =
+ value.map(up_data_structs::SponsoringRateLimit::Blocks);
}
CollectionLimitField::TokenLimit => {
limits.token_limit = value;
@@ -454,9 +448,9 @@
}
}
-impl Into<up_data_structs::AccessMode> for AccessMode {
- fn into(self) -> up_data_structs::AccessMode {
- match self {
+impl From<AccessMode> for up_data_structs::AccessMode {
+ fn from(value: AccessMode) -> Self {
+ match value {
AccessMode::Normal => up_data_structs::AccessMode::Normal,
AccessMode::AllowList => up_data_structs::AccessMode::AllowList,
}
pallets/evm-coder-substrate/src/lib.rsdiffbeforeafterboth--- a/pallets/evm-coder-substrate/src/lib.rs
+++ b/pallets/evm-coder-substrate/src/lib.rs
@@ -260,9 +260,9 @@
message: Some(msg), ..
}) => ExError::Revert(msg.into()),
DispatchError::Module(ModuleError { index, error, .. }) => {
- ExError::Revert(format!("error {:?} in pallet {}", error, index))
+ ExError::Revert(format!("error {error:?} in pallet {index}"))
}
- e => ExError::Revert(format!("substrate error: {:?}", e)),
+ e => ExError::Revert(format!("substrate error: {e:?}")),
}
}
pallets/evm-contract-helpers/src/eth.rsdiffbeforeafterboth--- a/pallets/evm-contract-helpers/src/eth.rs
+++ b/pallets/evm-contract-helpers/src/eth.rs
@@ -184,10 +184,9 @@
/// @param contractAddress The contract for which a sponsor is requested.
/// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.
fn sponsor(&self, contract_address: Address) -> Result<Option<eth::CrossAddress>> {
- Ok(match Pallet::<T>::get_sponsor(contract_address) {
- Some(ref value) => Some(eth::CrossAddress::from_sub_cross_account::<T>(value)),
- None => None,
- })
+ Ok(Pallet::<T>::get_sponsor(contract_address)
+ .as_ref()
+ .map(eth::CrossAddress::from_sub_cross_account::<T>))
}
/// Check tat contract has confirmed sponsor.
@@ -275,7 +274,7 @@
self.recorder().consume_sstore()?;
<Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;
- <Pallet<T>>::set_sponsoring_fee_limit(contract_address, fee_limit.into())
+ <Pallet<T>>::set_sponsoring_fee_limit(contract_address, fee_limit)
.map_err(dispatch_to_evm::<T>)?;
Ok(())
}
pallets/evm-contract-helpers/src/lib.rsdiffbeforeafterboth--- a/pallets/evm-contract-helpers/src/lib.rs
+++ b/pallets/evm-contract-helpers/src/lib.rs
@@ -376,7 +376,7 @@
<SponsoringMode<T>>::get(contract)
.or_else(|| {
#[allow(deprecated)]
- <SelfSponsoring<T>>::get(contract).then(|| SponsoringModeT::Allowlisted)
+ <SelfSponsoring<T>>::get(contract).then_some(SponsoringModeT::Allowlisted)
})
.unwrap_or_default()
}
@@ -410,7 +410,7 @@
/// Is user added to allowlist, or he is owner of specified contract
pub fn allowed(contract: H160, user: H160) -> bool {
- <Allowlist<T>>::get(&contract, &user) || <Owner<T>>::get(&contract) == user
+ <Allowlist<T>>::get(contract, user) || <Owner<T>>::get(contract) == user
}
/// Toggle contract allowlist access
@@ -425,7 +425,7 @@
/// Throw error if user is not allowed to reconfigure target contract
pub fn ensure_owner(contract: H160, user: H160) -> DispatchResult {
- ensure!(<Owner<T>>::get(&contract) == user, Error::<T>::NoPermission);
+ ensure!(<Owner<T>>::get(contract) == user, Error::<T>::NoPermission);
Ok(())
}
}
pallets/evm-migration/src/lib.rsdiffbeforeafterboth--- a/pallets/evm-migration/src/lib.rs
+++ b/pallets/evm-migration/src/lib.rs
@@ -78,7 +78,7 @@
pub fn begin(origin: OriginFor<T>, address: H160) -> DispatchResult {
ensure_root(origin)?;
ensure!(
- <PalletEvm<T>>::is_account_empty(&address) && !<MigrationPending<T>>::get(&address),
+ <PalletEvm<T>>::is_account_empty(&address) && !<MigrationPending<T>>::get(address),
<Error<T>>::AccountNotEmpty,
);
@@ -97,12 +97,12 @@
) -> DispatchResult {
ensure_root(origin)?;
ensure!(
- <MigrationPending<T>>::get(&address),
+ <MigrationPending<T>>::get(address),
<Error<T>>::AccountIsNotMigrating,
);
for (k, v) in data {
- <pallet_evm::AccountStorages<T>>::insert(&address, k, v);
+ <pallet_evm::AccountStorages<T>>::insert(address, k, v);
}
Ok(())
}
@@ -115,11 +115,11 @@
pub fn finish(origin: OriginFor<T>, address: H160, code: Vec<u8>) -> DispatchResult {
ensure_root(origin)?;
ensure!(
- <MigrationPending<T>>::get(&address),
+ <MigrationPending<T>>::get(address),
<Error<T>>::AccountIsNotMigrating,
);
- <pallet_evm::AccountCodes<T>>::insert(&address, code);
+ <pallet_evm::AccountCodes<T>>::insert(address, code);
<MigrationPending<T>>::remove(address);
Ok(())
}
@@ -166,7 +166,7 @@
pub struct OnMethodCall<T>(PhantomData<T>);
impl<T: Config> pallet_evm::OnMethodCall<T> for OnMethodCall<T> {
fn is_reserved(contract: &H160) -> bool {
- <MigrationPending<T>>::get(&contract)
+ <MigrationPending<T>>::get(contract)
}
fn is_used(_contract: &H160) -> bool {
pallets/foreign-assets/src/impl_fungibles.rsdiffbeforeafterboth--- a/pallets/foreign-assets/src/impl_fungibles.rs
+++ b/pallets/foreign-assets/src/impl_fungibles.rs
@@ -333,7 +333,7 @@
&Value::new(0),
)?;
- Ok(amount.into())
+ Ok(amount)
}
}
}
pallets/foreign-assets/src/lib.rsdiffbeforeafterboth--- a/pallets/foreign-assets/src/lib.rs
+++ b/pallets/foreign-assets/src/lib.rs
@@ -161,7 +161,7 @@
fn get_currency_id(multi_location: MultiLocation) -> Option<CurrencyId> {
log::trace!(target: "fassets::get_currency_id", "call");
- Pallet::<T>::location_to_currency_ids(multi_location).map(|id| AssetIds::ForeignAssetId(id))
+ Pallet::<T>::location_to_currency_ids(multi_location).map(AssetIds::ForeignAssetId)
}
}
@@ -378,7 +378,7 @@
foreign_asset_id,
|maybe_location| -> DispatchResult {
ensure!(maybe_location.is_none(), Error::<T>::MultiLocationExisted);
- *maybe_location = Some(location.clone());
+ *maybe_location = Some(*location);
AssetMetadatas::<T>::try_mutate(
AssetIds::ForeignAssetId(foreign_asset_id),
@@ -422,7 +422,7 @@
// modify location
if location != old_multi_locations {
- LocationToCurrencyIds::<T>::remove(old_multi_locations.clone());
+ LocationToCurrencyIds::<T>::remove(*old_multi_locations);
LocationToCurrencyIds::<T>::try_mutate(
location,
|maybe_currency_ids| -> DispatchResult {
@@ -437,7 +437,7 @@
)?;
}
*maybe_asset_metadatas = Some(metadata.clone());
- *old_multi_locations = location.clone();
+ *old_multi_locations = *location;
Ok(())
},
)
pallets/identity/src/types.rsdiffbeforeafterboth--- a/pallets/identity/src/types.rs
+++ b/pallets/identity/src/types.rs
@@ -104,7 +104,7 @@
Data::Raw(ref x) => {
let l = x.len().min(32);
let mut r = vec![l as u8 + 1; l + 1];
- r[1..].copy_from_slice(&x[..l as usize]);
+ r[1..].copy_from_slice(&x[..l]);
r
}
Data::BlakeTwo256(ref h) => once(34u8).chain(h.iter().cloned()).collect(),
@@ -287,7 +287,7 @@
fn decode<I: codec::Input>(input: &mut I) -> sp_std::result::Result<Self, codec::Error> {
let field = u64::decode(input)?;
Ok(Self(
- <BitFlags<IdentityField>>::from_bits(field as u64).map_err(|_| "invalid value")?,
+ <BitFlags<IdentityField>>::from_bits(field).map_err(|_| "invalid value")?,
))
}
}
pallets/nonfungible/src/erc.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -20,6 +20,8 @@
//! Method implementations are mostly doing parameter conversion and calling Nonfungible Pallet methods.
extern crate alloc;
+
+use alloc::string::ToString;
use core::{
char::{REPLACEMENT_CHARACTER, decode_utf16},
convert::TryInto,
@@ -356,8 +358,7 @@
.transpose()
.map_err(|e| {
Error::Revert(alloc::format!(
- "Can not convert value \"baseURI\" to string with error \"{}\"",
- e
+ "Can not convert value \"baseURI\" to string with error \"{e}\""
))
})?;
@@ -675,7 +676,7 @@
.try_into()
.map_err(|_| "token uri is too long")?,
})
- .map_err(|e| Error::Revert(alloc::format!("Can't add property: {:?}", e)))?;
+ .map_err(|e| Error::Revert(alloc::format!("Can't add property: {e:?}")))?;
<Pallet<T>>::create_item(
self,
@@ -717,7 +718,7 @@
.map(Clone::clone)
.ok_or_else(|| {
let key = String::from_utf8(key.clone().into_inner()).unwrap_or_default();
- Error::Revert(alloc::format!("No permission for key {}", key))
+ Error::Revert(alloc::format!("No permission for key {key}"))
})?;
Ok(a)
}
@@ -752,14 +753,14 @@
/// @param tokenId Id for the token.
#[solidity(hide)]
fn cross_owner_of(&self, token_id: U256) -> Result<eth::CrossAddress> {
- Self::owner_of_cross(&self, token_id)
+ Self::owner_of_cross(self, token_id)
}
/// Returns the owner (in cross format) of the token.
///
/// @param tokenId Id for the token.
fn owner_of_cross(&self, token_id: U256) -> Result<eth::CrossAddress> {
- Self::token_owner(&self, token_id.try_into()?)
+ Self::token_owner(self, token_id.try_into()?)
.map(|o| eth::CrossAddress::from_sub_cross_account::<T>(&o))
.map_err(|_| Error::Revert("token not found".into()))
}
@@ -789,7 +790,7 @@
.collect::<Result<Vec<_>>>()?;
<Self as CommonCollectionOperations<T>>::token_properties(
- &self,
+ self,
token_id.try_into()?,
if keys.is_empty() { None } else { Some(keys) },
)
@@ -1021,7 +1022,7 @@
.try_into()
.map_err(|_| "token uri is too long")?,
})
- .map_err(|e| Error::Revert(alloc::format!("Can't add property: {:?}", e)))?;
+ .map_err(|e| Error::Revert(alloc::format!("Can't add property: {e:?}")))?;
data.push(CreateItemData::<T> {
properties,
@@ -1056,7 +1057,7 @@
.map(eth::Property::try_into)
.collect::<Result<Vec<_>>>()?
.try_into()
- .map_err(|_| Error::Revert(alloc::format!("too many properties")))?;
+ .map_err(|_| Error::Revert("too many properties".to_string()))?;
let caller = T::CrossAccountId::from_eth(caller);
pallets/nonfungible/src/lib.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -166,10 +166,7 @@
#[pallet::config]
pub trait Config:
- frame_system::Config
- + pallet_common::Config
- + pallet_structure::Config
- + pallet_evm::Config
+ frame_system::Config + pallet_common::Config + pallet_structure::Config + pallet_evm::Config
{
type WeightInfo: WeightInfo;
}
@@ -860,13 +857,7 @@
<PalletStructure<T>>::unnest_if_nested(&token_data.owner, collection.id, token);
- <TokenData<T>>::insert(
- (collection.id, token),
- ItemData {
- owner: to.clone(),
- ..token_data
- },
- );
+ <TokenData<T>>::insert((collection.id, token), ItemData { owner: to.clone() });
if let Some(balance_to) = balance_to {
// from != to
pallets/refungible/src/erc.rsdiffbeforeafterboth--- a/pallets/refungible/src/erc.rs
+++ b/pallets/refungible/src/erc.rs
@@ -21,6 +21,7 @@
extern crate alloc;
+use alloc::string::ToString;
use core::{
char::{REPLACEMENT_CHARACTER, decode_utf16},
convert::TryInto,
@@ -353,8 +354,7 @@
.transpose()
.map_err(|e| {
Error::Revert(alloc::format!(
- "Can not convert value \"baseURI\" to string with error \"{}\"",
- e
+ "Can not convert value \"baseURI\" to string with error \"{e}\""
))
})?;
@@ -482,8 +482,8 @@
.recorder
.weight_calls_budget(<StructureWeight<T>>::find_parent());
- let balance = balance(&self, token, &from)?;
- ensure_single_owner(&self, token, balance)?;
+ let balance = balance(self, token, &from)?;
+ ensure_single_owner(self, token, balance)?;
<Pallet<T>>::transfer_from(self, &caller, &from, &to, token, balance, &budget)
.map_err(dispatch_to_evm::<T>)?;
@@ -575,8 +575,8 @@
let caller = T::CrossAccountId::from_eth(caller);
let token = token_id.try_into()?;
- let balance = balance(&self, token, &caller)?;
- ensure_single_owner(&self, token, balance)?;
+ let balance = balance(self, token, &caller)?;
+ ensure_single_owner(self, token, balance)?;
<Pallet<T>>::burn(self, &caller, token, balance).map_err(dispatch_to_evm::<T>)?;
Ok(())
@@ -622,7 +622,7 @@
return Err("item id should be next".into());
}
- let users = [(to.clone(), 1)]
+ let users = [(to, 1)]
.into_iter()
.collect::<BTreeMap<_, _>>()
.try_into()
@@ -706,9 +706,9 @@
.try_into()
.map_err(|_| "token uri is too long")?,
})
- .map_err(|e| Error::Revert(alloc::format!("Can't add property: {:?}", e)))?;
+ .map_err(|e| Error::Revert(alloc::format!("Can't add property: {e:?}")))?;
- let users = [(to.clone(), 1)]
+ let users = [(to, 1)]
.into_iter()
.collect::<BTreeMap<_, _>>()
.try_into()
@@ -750,7 +750,7 @@
.map(Clone::clone)
.ok_or_else(|| {
let key = String::from_utf8(key.clone().into_inner()).unwrap_or_default();
- Error::Revert(alloc::format!("No permission for key {}", key))
+ Error::Revert(alloc::format!("No permission for key {key}"))
})?;
Ok(a)
}
@@ -785,14 +785,14 @@
/// @param tokenId Id for the token.
#[solidity(hide)]
fn cross_owner_of(&self, token_id: U256) -> Result<eth::CrossAddress> {
- Self::owner_of_cross(&self, token_id)
+ Self::owner_of_cross(self, token_id)
}
/// Returns the owner (in cross format) of the token.
///
/// @param tokenId Id for the token.
fn owner_of_cross(&self, token_id: U256) -> Result<eth::CrossAddress> {
- Self::token_owner(&self, token_id.try_into()?)
+ Self::token_owner(self, token_id.try_into()?)
.map(|o| eth::CrossAddress::from_sub_cross_account::<T>(&o))
.or_else(|err| match err {
TokenOwnerError::NotFound => Err(Error::Revert("token not found".into())),
@@ -827,7 +827,7 @@
.collect::<Result<Vec<_>>>()?;
<Self as CommonCollectionOperations<T>>::token_properties(
- &self,
+ self,
token_id.try_into()?,
if keys.is_empty() { None } else { Some(keys) },
)
@@ -1004,7 +1004,7 @@
}
expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;
}
- let users = [(to.clone(), 1)]
+ let users = [(to, 1)]
.into_iter()
.collect::<BTreeMap<_, _>>()
.try_into()
@@ -1046,7 +1046,7 @@
.weight_calls_budget(<StructureWeight<T>>::find_parent());
let mut data = Vec::with_capacity(tokens.len());
- let users: BoundedBTreeMap<_, _, _> = [(to.clone(), 1)]
+ let users: BoundedBTreeMap<_, _, _> = [(to, 1)]
.into_iter()
.collect::<BTreeMap<_, _>>()
.try_into()
@@ -1067,7 +1067,7 @@
.try_into()
.map_err(|_| "token uri is too long")?,
})
- .map_err(|e| Error::Revert(alloc::format!("Can't add property: {:?}", e)))?;
+ .map_err(|e| Error::Revert(alloc::format!("Can't add property: {e:?}")))?;
let create_item_data = CreateItemData::<T> {
users: users.clone(),
@@ -1103,7 +1103,7 @@
.map(eth::Property::try_into)
.collect::<Result<Vec<_>>>()?
.try_into()
- .map_err(|_| Error::Revert(alloc::format!("too many properties")))?;
+ .map_err(|_| Error::Revert("too many properties".to_string()))?;
let caller = T::CrossAccountId::from_eth(caller);
pallets/refungible/src/lib.rsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! # Refungible Pallet18//!19//! The Refungible pallet provides functionality for handling refungible collections and tokens.20//!21//! - [`Config`]22//! - [`RefungibleHandle`]23//! - [`Pallet`]24//! - [`CommonWeights`](common::CommonWeights)25//!26//! ## Overview27//!28//! The Refungible pallet provides functions for:29//!30//! - RFT collection creation and removal31//! - Minting and burning of RFT tokens32//! - Partition and repartition of RFT tokens33//! - Retrieving number of pieces of RFT token34//! - Retrieving account balances35//! - Transfering RFT token pieces36//! - Burning RFT token pieces37//! - Setting and checking allowance for RFT tokens38//!39//! ### Terminology40//!41//! - **RFT token:** Non fungible token that was partitioned to pieces. If an account owns all42//! of the RFT token pieces than it owns the RFT token and can repartition it.43//!44//! - **RFT Collection:** A collection of RFT tokens. All RFT tokens are part of a collection.45//! Each collection has its own settings and set of permissions.46//!47//! - **RFT token piece:** A fungible part of an RFT token.48//!49//! - **Balance:** RFT token pieces owned by an account50//!51//! - **Allowance:** Maximum number of RFT token pieces that one account is allowed to52//! transfer from the balance of another account53//!54//! - **Burning:** The process of “deleting” a token from a collection or removing token pieces from55//! an account balance.56//!57//! ### Implementations58//!59//! The Refungible pallet provides implementations for the following traits. If these traits provide60//! the functionality that you need, then you can avoid coupling with the Refungible pallet.61//!62//! - [`CommonWeightInfo`](pallet_common::CommonWeightInfo): Functions for retrieval of transaction weight63//! - [`CommonCollectionOperations`](pallet_common::CommonCollectionOperations): Functions for dealing64//! with collections65//! - [`RefungibleExtensions`](pallet_common::RefungibleExtensions): Functions specific for refungible66//! collection67//!68//! ## Interface69//!70//! ### Dispatchable Functions71//!72//! - `init_collection` - Create RFT collection. RFT collection can be configured to allow or deny access for73//! some accounts.74//! - `destroy_collection` - Destroy exising RFT collection. There should be no tokens in the collection.75//! - `burn` - Burn some amount of RFT token pieces owned by account. Burns the RFT token if no pieces left.76//! - `transfer` - Transfer some amount of RFT token pieces. Transfers should be enabled for RFT collection.77//! Nests the RFT token if RFT token pieces are sent to another token.78//! - `create_item` - Mint RFT token in collection. Sender should have permission to mint tokens.79//! - `set_allowance` - Set allowance for another account to transfer balance from sender's account.80//! - `repartition` - Repartition token to selected number of pieces. Sender should own all existing pieces.81//!82//! ## Assumptions83//!84//! * Total number of pieces for one token shouldn't exceed `up_data_structs::MAX_REFUNGIBLE_PIECES`.85//! * Total number of tokens of all types shouldn't be greater than `up_data_structs::MAX_TOKEN_PREFIX_LENGTH`.86//! * Sender should be in collection's allow list to perform operations on tokens.8788#![cfg_attr(not(feature = "std"), no_std)]8990use crate::erc_token::ERC20Events;91use crate::erc::ERC721Events;9293use core::ops::Deref;94use evm_coder::ToLog;95use frame_support::{ensure, storage::with_transaction, transactional};96use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};97use pallet_evm_coder_substrate::WithRecorder;98use pallet_common::{99 CommonCollectionOperations, Error as CommonError, eth::collection_id_to_address,100 Event as CommonEvent, Pallet as PalletCommon,101};102use pallet_structure::Pallet as PalletStructure;103use sp_core::{Get, H160};104use sp_runtime::{ArithmeticError, DispatchError, DispatchResult, TransactionOutcome};105use sp_std::{vec::Vec, vec, collections::btree_map::BTreeMap};106use up_data_structs::{107 AccessMode, budget::Budget, CollectionId, CollectionFlags, CreateCollectionData,108 mapping::TokenAddressMapping, MAX_REFUNGIBLE_PIECES, Property, PropertyKey,109 PropertyKeyPermission, PropertyScope, PropertyValue, TokenId, TrySetProperty,110 PropertiesPermissionMap, CreateRefungibleExMultipleOwners, TokenOwnerError,111 TokenProperties as TokenPropertiesT,112};113114pub use pallet::*;115#[cfg(feature = "runtime-benchmarks")]116pub mod benchmarking;117pub mod common;118pub mod erc;119pub mod erc_token;120pub mod weights;121122pub type CreateItemData<T> =123 CreateRefungibleExMultipleOwners<<T as pallet_evm::Config>::CrossAccountId>;124pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;125126#[frame_support::pallet]127pub mod pallet {128 use super::*;129 use frame_support::{130 Blake2_128, Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key,131 traits::StorageVersion,132 };133 use up_data_structs::{CollectionId, TokenId};134 use super::weights::WeightInfo;135136 #[pallet::error]137 pub enum Error<T> {138 /// Not Refungible item data used to mint in Refungible collection.139 NotRefungibleDataUsedToMintFungibleCollectionToken,140 /// Maximum refungibility exceeded.141 WrongRefungiblePieces,142 /// Refungible token can't be repartitioned by user who isn't owns all pieces.143 RepartitionWhileNotOwningAllPieces,144 /// Refungible token can't nest other tokens.145 RefungibleDisallowsNesting,146 /// Setting item properties is not allowed.147 SettingPropertiesNotAllowed,148 }149150 #[pallet::config]151 pub trait Config:152 frame_system::Config + pallet_common::Config + pallet_structure::Config153 {154 type WeightInfo: WeightInfo;155 }156157 const STORAGE_VERSION: StorageVersion = StorageVersion::new(2);158159 #[pallet::pallet]160 #[pallet::storage_version(STORAGE_VERSION)]161 pub struct Pallet<T>(_);162163 /// Total amount of minted tokens in a collection.164 #[pallet::storage]165 pub type TokensMinted<T: Config> =166 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;167168 /// Amount of tokens burnt in a collection.169 #[pallet::storage]170 pub type TokensBurnt<T: Config> =171 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;172173 /// Amount of pieces a refungible token is split into.174 #[pallet::storage]175 #[pallet::getter(fn token_properties)]176 pub type TokenProperties<T: Config> = StorageNMap<177 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),178 Value = TokenPropertiesT,179 QueryKind = ValueQuery,180 >;181182 /// Total amount of pieces for token183 #[pallet::storage]184 pub type TotalSupply<T: Config> = StorageNMap<185 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),186 Value = u128,187 QueryKind = ValueQuery,188 >;189190 /// Used to enumerate tokens owned by account.191 #[pallet::storage]192 pub type Owned<T: Config> = StorageNMap<193 Key = (194 Key<Twox64Concat, CollectionId>,195 Key<Blake2_128Concat, T::CrossAccountId>,196 Key<Twox64Concat, TokenId>,197 ),198 Value = bool,199 QueryKind = ValueQuery,200 >;201202 /// Amount of tokens (not pieces) partially owned by an account within a collection.203 #[pallet::storage]204 pub type AccountBalance<T: Config> = StorageNMap<205 Key = (206 Key<Twox64Concat, CollectionId>,207 // Owner208 Key<Blake2_128Concat, T::CrossAccountId>,209 ),210 Value = u32,211 QueryKind = ValueQuery,212 >;213214 /// Amount of token pieces owned by account.215 #[pallet::storage]216 pub type Balance<T: Config> = StorageNMap<217 Key = (218 Key<Twox64Concat, CollectionId>,219 Key<Twox64Concat, TokenId>,220 // Owner221 Key<Blake2_128Concat, T::CrossAccountId>,222 ),223 Value = u128,224 QueryKind = ValueQuery,225 >;226227 /// Allowance set by a token owner for another user to perform one of certain transactions on a number of pieces of a token.228 #[pallet::storage]229 pub type Allowance<T: Config> = StorageNMap<230 Key = (231 Key<Twox64Concat, CollectionId>,232 Key<Twox64Concat, TokenId>,233 // Owner234 Key<Blake2_128, T::CrossAccountId>,235 // Spender236 Key<Blake2_128Concat, T::CrossAccountId>,237 ),238 Value = u128,239 QueryKind = ValueQuery,240 >;241242 /// Spender set by a wallet owner that could perform certain transactions on all tokens in the wallet.243 #[pallet::storage]244 pub type CollectionAllowance<T: Config> = StorageNMap<245 Key = (246 Key<Twox64Concat, CollectionId>,247 Key<Blake2_128Concat, T::CrossAccountId>, // Owner248 Key<Blake2_128Concat, T::CrossAccountId>, // Spender249 ),250 Value = bool,251 QueryKind = ValueQuery,252 >;253}254255pub struct RefungibleHandle<T: Config>(pallet_common::CollectionHandle<T>);256impl<T: Config> RefungibleHandle<T> {257 pub fn cast(inner: pallet_common::CollectionHandle<T>) -> Self {258 Self(inner)259 }260 pub fn into_inner(self) -> pallet_common::CollectionHandle<T> {261 self.0262 }263 pub fn common_mut(&mut self) -> &mut pallet_common::CollectionHandle<T> {264 &mut self.0265 }266}267268impl<T: Config> Deref for RefungibleHandle<T> {269 type Target = pallet_common::CollectionHandle<T>;270271 fn deref(&self) -> &Self::Target {272 &self.0273 }274}275276impl<T: Config> WithRecorder<T> for RefungibleHandle<T> {277 fn recorder(&self) -> &pallet_evm_coder_substrate::SubstrateRecorder<T> {278 self.0.recorder()279 }280 fn into_recorder(self) -> pallet_evm_coder_substrate::SubstrateRecorder<T> {281 self.0.into_recorder()282 }283}284285impl<T: Config> Pallet<T> {286 /// Get number of RFT tokens in collection287 pub fn total_supply(collection: &RefungibleHandle<T>) -> u32 {288 <TokensMinted<T>>::get(collection.id) - <TokensBurnt<T>>::get(collection.id)289 }290291 /// Check that RFT token exists292 ///293 /// - `token`: Token ID.294 pub fn token_exists(collection: &RefungibleHandle<T>, token: TokenId) -> bool {295 <TotalSupply<T>>::contains_key((collection.id, token))296 }297298 pub fn set_scoped_token_property(299 collection_id: CollectionId,300 token_id: TokenId,301 scope: PropertyScope,302 property: Property,303 ) -> DispatchResult {304 TokenProperties::<T>::try_mutate((collection_id, token_id), |properties| {305 properties.try_scoped_set(scope, property.key, property.value)306 })307 .map_err(<CommonError<T>>::from)?;308309 Ok(())310 }311312 pub fn set_scoped_token_properties(313 collection_id: CollectionId,314 token_id: TokenId,315 scope: PropertyScope,316 properties: impl Iterator<Item = Property>,317 ) -> DispatchResult {318 TokenProperties::<T>::try_mutate((collection_id, token_id), |stored_properties| {319 stored_properties.try_scoped_set_from_iter(scope, properties)320 })321 .map_err(<CommonError<T>>::from)?;322323 Ok(())324 }325}326327// unchecked calls skips any permission checks328impl<T: Config> Pallet<T> {329 /// Create RFT collection330 ///331 /// `init_collection` will take non-refundable deposit for collection creation.332 ///333 /// - `data`: Contains settings for collection limits and permissions.334 pub fn init_collection(335 owner: T::CrossAccountId,336 payer: T::CrossAccountId,337 data: CreateCollectionData<T::AccountId>,338 flags: CollectionFlags,339 ) -> Result<CollectionId, DispatchError> {340 <PalletCommon<T>>::init_collection(owner, payer, data, flags)341 }342343 /// Destroy RFT collection344 ///345 /// `destroy_collection` will throw error if collection contains any tokens.346 /// Only owner can destroy collection.347 pub fn destroy_collection(348 collection: RefungibleHandle<T>,349 sender: &T::CrossAccountId,350 ) -> DispatchResult {351 let id = collection.id;352353 if Self::collection_has_tokens(id) {354 return Err(<CommonError<T>>::CantDestroyNotEmptyCollection.into());355 }356357 // =========358359 PalletCommon::destroy_collection(collection.0, sender)?;360361 <TokensMinted<T>>::remove(id);362 <TokensBurnt<T>>::remove(id);363 let _ = <TotalSupply<T>>::clear_prefix((id,), u32::MAX, None);364 let _ = <Balance<T>>::clear_prefix((id,), u32::MAX, None);365 let _ = <Allowance<T>>::clear_prefix((id,), u32::MAX, None);366 let _ = <Owned<T>>::clear_prefix((id,), u32::MAX, None);367 let _ = <AccountBalance<T>>::clear_prefix((id,), u32::MAX, None);368 Ok(())369 }370371 fn collection_has_tokens(collection_id: CollectionId) -> bool {372 <TotalSupply<T>>::iter_prefix((collection_id,))373 .next()374 .is_some()375 }376377 pub fn burn_token_unchecked(378 collection: &RefungibleHandle<T>,379 owner: &T::CrossAccountId,380 token_id: TokenId,381 ) -> DispatchResult {382 let burnt = <TokensBurnt<T>>::get(collection.id)383 .checked_add(1)384 .ok_or(ArithmeticError::Overflow)?;385386 <TokensBurnt<T>>::insert(collection.id, burnt);387 <TokenProperties<T>>::remove((collection.id, token_id));388 <TotalSupply<T>>::remove((collection.id, token_id));389 let _ = <Balance<T>>::clear_prefix((collection.id, token_id), u32::MAX, None);390 let _ = <Allowance<T>>::clear_prefix((collection.id, token_id), u32::MAX, None);391 <PalletEvm<T>>::deposit_log(392 ERC721Events::Transfer {393 from: *owner.as_eth(),394 to: H160::default(),395 token_id: token_id.into(),396 }397 .to_log(collection_id_to_address(collection.id)),398 );399 Ok(())400 }401402 /// Burn RFT token pieces403 ///404 /// `burn` will decrease total amount of token pieces and amount owned by sender.405 /// `burn` can be called even if there are multiple owners of the RFT token.406 /// If sender wouldn't have any pieces left after `burn` than she will stop being407 /// one of the owners of the token. If there is no account that owns any pieces of408 /// the token than token will be burned too.409 ///410 /// - `amount`: Amount of token pieces to burn.411 /// - `token`: Token who's pieces should be burned412 /// - `collection`: Collection that contains the token413 pub fn burn(414 collection: &RefungibleHandle<T>,415 owner: &T::CrossAccountId,416 token: TokenId,417 amount: u128,418 ) -> DispatchResult {419 if <Balance<T>>::get((collection.id, token, owner)) == 0 {420 return Err(<CommonError<T>>::TokenValueTooLow.into());421 }422423 let total_supply = <TotalSupply<T>>::get((collection.id, token))424 .checked_sub(amount)425 .ok_or(<CommonError<T>>::TokenValueTooLow)?;426427 // This was probally last owner of this token?428 if total_supply == 0 {429 // Ensure user actually owns this amount430 ensure!(431 <Balance<T>>::get((collection.id, token, owner)) == amount,432 <CommonError<T>>::TokenValueTooLow433 );434 let account_balance = <AccountBalance<T>>::get((collection.id, owner))435 .checked_sub(1)436 // Should not occur437 .ok_or(ArithmeticError::Underflow)?;438439 // =========440441 <Owned<T>>::remove((collection.id, owner, token));442 <PalletStructure<T>>::unnest_if_nested(owner, collection.id, token);443 <AccountBalance<T>>::insert((collection.id, owner), account_balance);444 Self::burn_token_unchecked(collection, owner, token)?;445 <PalletEvm<T>>::deposit_log(446 ERC20Events::Transfer {447 from: *owner.as_eth(),448 to: H160::default(),449 value: amount.into(),450 }451 .to_log(collection_id_to_address(collection.id)),452 );453 <PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(454 collection.id,455 token,456 owner.clone(),457 amount,458 ));459 return Ok(());460 }461462 let balance = <Balance<T>>::get((collection.id, token, owner))463 .checked_sub(amount)464 .ok_or(<CommonError<T>>::TokenValueTooLow)?;465 let account_balance = if balance == 0 {466 <AccountBalance<T>>::get((collection.id, owner))467 .checked_sub(1)468 // Should not occur469 .ok_or(ArithmeticError::Underflow)?470 } else {471 0472 };473474 // =========475476 if balance == 0 {477 <Owned<T>>::remove((collection.id, owner, token));478 <PalletStructure<T>>::unnest_if_nested(owner, collection.id, token);479 <Balance<T>>::remove((collection.id, token, owner));480 <AccountBalance<T>>::insert((collection.id, owner), account_balance);481482 if let Ok(user) = Self::token_owner(collection.id, token) {483 <PalletEvm<T>>::deposit_log(484 ERC721Events::Transfer {485 from: erc::ADDRESS_FOR_PARTIALLY_OWNED_TOKENS,486 to: *user.as_eth(),487 token_id: token.into(),488 }489 .to_log(collection_id_to_address(collection.id)),490 );491 }492 } else {493 <Balance<T>>::insert((collection.id, token, owner), balance);494 }495 <TotalSupply<T>>::insert((collection.id, token), total_supply);496497 <PalletEvm<T>>::deposit_log(498 ERC20Events::Transfer {499 from: *owner.as_eth(),500 to: H160::default(),501 value: amount.into(),502 }503 .to_log(T::EvmTokenAddressMapping::token_to_address(504 collection.id,505 token,506 )),507 );508 <PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(509 collection.id,510 token,511 owner.clone(),512 amount,513 ));514 Ok(())515 }516517 /// A batch operation to add, edit or remove properties for a token.518 /// It sets or removes a token's properties according to519 /// `properties_updates` contents:520 /// * sets a property under the <key> with the value provided `(<key>, Some(<value>))`521 /// * removes a property under the <key> if the value is `None` `(<key>, None)`.522 ///523 /// - `nesting_budget`: Limit for searching parents in-depth to check ownership.524 /// - `is_token_create`: Indicates that method is called during token initialization.525 /// Allows to bypass ownership check.526 ///527 /// All affected properties should have `mutable` permission528 /// to be **deleted** or to be **set more than once**,529 /// and the sender should have permission to edit those properties.530 ///531 /// This function fires an event for each property change.532 /// In case of an error, all the changes (including the events) will be reverted533 /// since the function is transactional.534 #[transactional]535 fn modify_token_properties(536 collection: &RefungibleHandle<T>,537 sender: &T::CrossAccountId,538 token_id: TokenId,539 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,540 is_token_create: bool,541 nesting_budget: &dyn Budget,542 ) -> DispatchResult {543 let is_token_owner = || -> Result<bool, DispatchError> {544 let balance = collection.balance(sender.clone(), token_id);545 let total_pieces: u128 =546 Self::total_pieces(collection.id, token_id).unwrap_or(u128::MAX);547 if balance != total_pieces {548 return Ok(false);549 }550551 let is_bundle_owner = <PalletStructure<T>>::check_indirectly_owned(552 sender.clone(),553 collection.id,554 token_id,555 None,556 nesting_budget,557 )?;558559 Ok(is_bundle_owner)560 };561562 let stored_properties = <TokenProperties<T>>::get((collection.id, token_id));563564 <PalletCommon<T>>::modify_token_properties(565 collection,566 sender,567 token_id,568 properties_updates,569 is_token_create,570 stored_properties,571 is_token_owner,572 |properties| <TokenProperties<T>>::set((collection.id, token_id), properties),573 erc::ERC721TokenEvent::TokenChanged {574 token_id: token_id.into(),575 }576 .to_log(T::ContractAddress::get()),577 )578 }579580 pub fn set_token_properties(581 collection: &RefungibleHandle<T>,582 sender: &T::CrossAccountId,583 token_id: TokenId,584 properties: impl Iterator<Item = Property>,585 is_token_create: bool,586 nesting_budget: &dyn Budget,587 ) -> DispatchResult {588 Self::modify_token_properties(589 collection,590 sender,591 token_id,592 properties.map(|p| (p.key, Some(p.value))),593 is_token_create,594 nesting_budget,595 )596 }597598 pub fn set_token_property(599 collection: &RefungibleHandle<T>,600 sender: &T::CrossAccountId,601 token_id: TokenId,602 property: Property,603 nesting_budget: &dyn Budget,604 ) -> DispatchResult {605 let is_token_create = false;606607 Self::set_token_properties(608 collection,609 sender,610 token_id,611 [property].into_iter(),612 is_token_create,613 nesting_budget,614 )615 }616617 pub fn delete_token_properties(618 collection: &RefungibleHandle<T>,619 sender: &T::CrossAccountId,620 token_id: TokenId,621 property_keys: impl Iterator<Item = PropertyKey>,622 nesting_budget: &dyn Budget,623 ) -> DispatchResult {624 let is_token_create = false;625626 Self::modify_token_properties(627 collection,628 sender,629 token_id,630 property_keys.into_iter().map(|key| (key, None)),631 is_token_create,632 nesting_budget,633 )634 }635636 pub fn delete_token_property(637 collection: &RefungibleHandle<T>,638 sender: &T::CrossAccountId,639 token_id: TokenId,640 property_key: PropertyKey,641 nesting_budget: &dyn Budget,642 ) -> DispatchResult {643 Self::delete_token_properties(644 collection,645 sender,646 token_id,647 [property_key].into_iter(),648 nesting_budget,649 )650 }651652 /// Transfer RFT token pieces from one account to another.653 ///654 /// If the sender is no longer owns any pieces after the `transfer` than she stops being an owner of the token.655 ///656 /// - `from`: Owner of token pieces to transfer.657 /// - `to`: Recepient of transfered token pieces.658 /// - `amount`: Amount of token pieces to transfer.659 /// - `token`: Token whos pieces should be transfered660 /// - `collection`: Collection that contains the token661 pub fn transfer(662 collection: &RefungibleHandle<T>,663 from: &T::CrossAccountId,664 to: &T::CrossAccountId,665 token: TokenId,666 amount: u128,667 nesting_budget: &dyn Budget,668 ) -> DispatchResult {669 ensure!(670 collection.limits.transfers_enabled(),671 <CommonError<T>>::TransferNotAllowed672 );673674 if collection.permissions.access() == AccessMode::AllowList {675 collection.check_allowlist(from)?;676 collection.check_allowlist(to)?;677 }678 <PalletCommon<T>>::ensure_correct_receiver(to)?;679680 let initial_balance_from = <Balance<T>>::get((collection.id, token, from));681682 if initial_balance_from == 0 {683 return Err(<CommonError<T>>::TokenValueTooLow.into());684 }685686 let updated_balance_from = initial_balance_from687 .checked_sub(amount)688 .ok_or(<CommonError<T>>::TokenValueTooLow)?;689 let mut create_target = false;690 let from_to_differ = from != to;691 let updated_balance_to = if from != to && amount != 0 {692 let old_balance = <Balance<T>>::get((collection.id, token, to));693 if old_balance == 0 {694 create_target = true;695 }696 Some(697 old_balance698 .checked_add(amount)699 .ok_or(ArithmeticError::Overflow)?,700 )701 } else {702 None703 };704705 let account_balance_from = if updated_balance_from == 0 {706 Some(707 <AccountBalance<T>>::get((collection.id, from))708 .checked_sub(1)709 // Should not occur710 .ok_or(ArithmeticError::Underflow)?,711 )712 } else {713 None714 };715 // Account data is created in token, AccountBalance should be increased716 // But only if from != to as we shouldn't check overflow in this case717 let account_balance_to = if create_target && from_to_differ {718 let account_balance_to = <AccountBalance<T>>::get((collection.id, to))719 .checked_add(1)720 .ok_or(ArithmeticError::Overflow)?;721 ensure!(722 account_balance_to < collection.limits.account_token_ownership_limit(),723 <CommonError<T>>::AccountTokenLimitExceeded,724 );725726 Some(account_balance_to)727 } else {728 None729 };730731 // =========732733 if let Some(updated_balance_to) = updated_balance_to {734 // from != to && amount != 0735736 <PalletStructure<T>>::nest_if_sent_to_token(737 from.clone(),738 to,739 collection.id,740 token,741 nesting_budget,742 )?;743744 if updated_balance_from == 0 {745 <Balance<T>>::remove((collection.id, token, from));746 <PalletStructure<T>>::unnest_if_nested(from, collection.id, token);747 } else {748 <Balance<T>>::insert((collection.id, token, from), updated_balance_from);749 }750 <Balance<T>>::insert((collection.id, token, to), updated_balance_to);751 if let Some(account_balance_from) = account_balance_from {752 <AccountBalance<T>>::insert((collection.id, from), account_balance_from);753 <Owned<T>>::remove((collection.id, from, token));754 }755 if let Some(account_balance_to) = account_balance_to {756 <AccountBalance<T>>::insert((collection.id, to), account_balance_to);757 <Owned<T>>::insert((collection.id, to, token), true);758 }759 }760761 <PalletEvm<T>>::deposit_log(762 ERC20Events::Transfer {763 from: *from.as_eth(),764 to: *to.as_eth(),765 value: amount.into(),766 }767 .to_log(T::EvmTokenAddressMapping::token_to_address(768 collection.id,769 token,770 )),771 );772773 <PalletCommon<T>>::deposit_event(CommonEvent::Transfer(774 collection.id,775 token,776 from.clone(),777 to.clone(),778 amount,779 ));780781 let total_supply = <TotalSupply<T>>::get((collection.id, token));782783 if amount == total_supply {784 // if token was fully owned by `from` and will be fully owned by `to` after transfer785 <PalletEvm<T>>::deposit_log(786 ERC721Events::Transfer {787 from: *from.as_eth(),788 to: *to.as_eth(),789 token_id: token.into(),790 }791 .to_log(collection_id_to_address(collection.id)),792 );793 } else if let Some(updated_balance_to) = updated_balance_to {794 // if `from` not equals `to`. This condition is needed to avoid sending event795 // when `from` fully owns token and sends part of token pieces to itself.796 if initial_balance_from == total_supply {797 // if token was fully owned by `from` and will be only partially owned by `to`798 // and `from` after transfer799 <PalletEvm<T>>::deposit_log(800 ERC721Events::Transfer {801 from: *from.as_eth(),802 to: erc::ADDRESS_FOR_PARTIALLY_OWNED_TOKENS,803 token_id: token.into(),804 }805 .to_log(collection_id_to_address(collection.id)),806 );807 } else if updated_balance_to == total_supply {808 // if token was partially owned by `from` and will be fully owned by `to` after transfer809 <PalletEvm<T>>::deposit_log(810 ERC721Events::Transfer {811 from: erc::ADDRESS_FOR_PARTIALLY_OWNED_TOKENS,812 to: *to.as_eth(),813 token_id: token.into(),814 }815 .to_log(collection_id_to_address(collection.id)),816 );817 }818 }819820 Ok(())821 }822823 /// Batched operation to create multiple RFT tokens.824 ///825 /// Same as `create_item` but creates multiple tokens.826 ///827 /// - `data`: Same as 'data` in `create_item` but contains data for multiple tokens.828 pub fn create_multiple_items(829 collection: &RefungibleHandle<T>,830 sender: &T::CrossAccountId,831 data: Vec<CreateItemData<T>>,832 nesting_budget: &dyn Budget,833 ) -> DispatchResult {834 if !collection.is_owner_or_admin(sender) {835 ensure!(836 collection.permissions.mint_mode(),837 <CommonError<T>>::PublicMintingNotAllowed838 );839 collection.check_allowlist(sender)?;840841 for item in data.iter() {842 for user in item.users.keys() {843 collection.check_allowlist(user)?;844 }845 }846 }847848 for item in data.iter() {849 for (owner, _) in item.users.iter() {850 <PalletCommon<T>>::ensure_correct_receiver(owner)?;851 }852 }853854 // Total pieces per tokens855 let totals = data856 .iter()857 .map(|data| {858 Ok(data859 .users860 .iter()861 .map(|u| u.1)862 .try_fold(0u128, |acc, v| acc.checked_add(*v))863 .ok_or(ArithmeticError::Overflow)?)864 })865 .collect::<Result<Vec<_>, DispatchError>>()?;866 for total in &totals {867 ensure!(868 *total <= MAX_REFUNGIBLE_PIECES,869 <Error<T>>::WrongRefungiblePieces870 );871 }872873 let first_token_id = <TokensMinted<T>>::get(collection.id);874 let tokens_minted = first_token_id875 .checked_add(data.len() as u32)876 .ok_or(ArithmeticError::Overflow)?;877 ensure!(878 tokens_minted < collection.limits.token_limit(),879 <CommonError<T>>::CollectionTokenLimitExceeded880 );881882 let mut balances = BTreeMap::new();883 for data in &data {884 for owner in data.users.keys() {885 let balance = balances886 .entry(owner)887 .or_insert_with(|| <AccountBalance<T>>::get((collection.id, owner)));888 *balance = balance.checked_add(1).ok_or(ArithmeticError::Overflow)?;889890 ensure!(891 *balance <= collection.limits.account_token_ownership_limit(),892 <CommonError<T>>::AccountTokenLimitExceeded,893 );894 }895 }896897 for (i, token) in data.iter().enumerate() {898 let token_id = TokenId(first_token_id + i as u32 + 1);899 for (to, _) in token.users.iter() {900 <PalletStructure<T>>::check_nesting(901 sender.clone(),902 to,903 collection.id,904 token_id,905 nesting_budget,906 )?;907 }908 }909910 // =========911912 with_transaction(|| {913 for (i, data) in data.iter().enumerate() {914 let token_id = first_token_id + i as u32 + 1;915 <TotalSupply<T>>::insert((collection.id, token_id), totals[i]);916917 for (user, amount) in data.users.iter() {918 if *amount == 0 {919 continue;920 }921 <Balance<T>>::insert((collection.id, token_id, &user), amount);922 <Owned<T>>::insert((collection.id, &user, TokenId(token_id)), true);923 <PalletStructure<T>>::nest_if_sent_to_token_unchecked(924 user,925 collection.id,926 TokenId(token_id),927 );928 }929930 if let Err(e) = Self::set_token_properties(931 collection,932 sender,933 TokenId(token_id),934 data.properties.clone().into_iter(),935 true,936 nesting_budget,937 ) {938 return TransactionOutcome::Rollback(Err(e));939 }940 }941 TransactionOutcome::Commit(Ok(()))942 })?;943944 <TokensMinted<T>>::insert(collection.id, tokens_minted);945946 for (account, balance) in balances {947 <AccountBalance<T>>::insert((collection.id, account), balance);948 }949950 for (i, token) in data.into_iter().enumerate() {951 let token_id = first_token_id + i as u32 + 1;952953 let receivers = token954 .users955 .into_iter()956 .filter(|(_, amount)| *amount > 0)957 .collect::<Vec<_>>();958959 if let [(user, _)] = receivers.as_slice() {960 // if there is exactly one receiver961 <PalletEvm<T>>::deposit_log(962 ERC721Events::Transfer {963 from: H160::default(),964 to: *user.as_eth(),965 token_id: token_id.into(),966 }967 .to_log(collection_id_to_address(collection.id)),968 );969 } else if let [_, ..] = receivers.as_slice() {970 // if there is more than one receiver971 <PalletEvm<T>>::deposit_log(972 ERC721Events::Transfer {973 from: H160::default(),974 to: erc::ADDRESS_FOR_PARTIALLY_OWNED_TOKENS,975 token_id: token_id.into(),976 }977 .to_log(collection_id_to_address(collection.id)),978 );979 }980981 for (user, amount) in receivers.into_iter() {982 <PalletEvm<T>>::deposit_log(983 ERC20Events::Transfer {984 from: H160::default(),985 to: *user.as_eth(),986 value: amount.into(),987 }988 .to_log(T::EvmTokenAddressMapping::token_to_address(989 collection.id,990 TokenId(token_id),991 )),992 );993 <PalletCommon<T>>::deposit_event(CommonEvent::ItemCreated(994 collection.id,995 TokenId(token_id),996 user,997 amount,998 ));999 }1000 }1001 Ok(())1002 }10031004 pub fn set_allowance_unchecked(1005 collection: &RefungibleHandle<T>,1006 sender: &T::CrossAccountId,1007 spender: &T::CrossAccountId,1008 token: TokenId,1009 amount: u128,1010 ) {1011 if amount == 0 {1012 <Allowance<T>>::remove((collection.id, token, sender, spender));1013 } else {1014 <Allowance<T>>::insert((collection.id, token, sender, spender), amount);1015 }10161017 <PalletEvm<T>>::deposit_log(1018 ERC20Events::Approval {1019 owner: *sender.as_eth(),1020 spender: *spender.as_eth(),1021 value: amount.into(),1022 }1023 .to_log(T::EvmTokenAddressMapping::token_to_address(1024 collection.id,1025 token,1026 )),1027 );1028 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(1029 collection.id,1030 token,1031 sender.clone(),1032 spender.clone(),1033 amount,1034 ))1035 }10361037 /// Set allowance for the spender to `transfer` or `burn` sender's token pieces.1038 ///1039 /// - `amount`: Amount of token pieces the spender is allowed to `transfer` or `burn.1040 pub fn set_allowance(1041 collection: &RefungibleHandle<T>,1042 sender: &T::CrossAccountId,1043 spender: &T::CrossAccountId,1044 token: TokenId,1045 amount: u128,1046 ) -> DispatchResult {1047 if collection.permissions.access() == AccessMode::AllowList {1048 collection.check_allowlist(sender)?;1049 collection.check_allowlist(spender)?;1050 }10511052 <PalletCommon<T>>::ensure_correct_receiver(spender)?;10531054 if <Balance<T>>::get((collection.id, token, sender)) < amount {1055 ensure!(1056 collection.ignores_owned_amount(sender) && Self::token_exists(collection, token),1057 <CommonError<T>>::CantApproveMoreThanOwned1058 );1059 }10601061 // =========10621063 Self::set_allowance_unchecked(collection, sender, spender, token, amount);1064 Ok(())1065 }10661067 /// Set allowance to spend from sender's eth mirror1068 ///1069 /// - `from`: Address of sender's eth mirror.1070 /// - `to`: Adress of spender.1071 /// - `amount`: Amount of token pieces the spender is allowed to `transfer` or `burn.1072 pub fn set_allowance_from(1073 collection: &RefungibleHandle<T>,1074 sender: &T::CrossAccountId,1075 from: &T::CrossAccountId,1076 to: &T::CrossAccountId,1077 token_id: TokenId,1078 amount: u128,1079 ) -> DispatchResult {1080 if collection.permissions.access() == AccessMode::AllowList {1081 collection.check_allowlist(sender)?;1082 collection.check_allowlist(from)?;1083 collection.check_allowlist(to)?;1084 }10851086 <PalletCommon<T>>::ensure_correct_receiver(to)?;10871088 ensure!(1089 sender.conv_eq(from),1090 <CommonError<T>>::AddressIsNotEthMirror1091 );10921093 if <Balance<T>>::get((collection.id, token_id, from)) < amount {1094 ensure!(1095 collection.limits.owner_can_transfer()1096 && (collection.is_owner_or_admin(sender) || collection.is_owner_or_admin(from))1097 && Self::token_exists(collection, token_id),1098 <CommonError<T>>::CantApproveMoreThanOwned1099 );1100 }11011102 // =========11031104 Self::set_allowance_unchecked(collection, from, to, token_id, amount);1105 Ok(())1106 }11071108 /// Returns allowance, which should be set after transaction1109 fn check_allowed(1110 collection: &RefungibleHandle<T>,1111 spender: &T::CrossAccountId,1112 from: &T::CrossAccountId,1113 token: TokenId,1114 amount: u128,1115 nesting_budget: &dyn Budget,1116 ) -> Result<Option<u128>, DispatchError> {1117 if spender.conv_eq(from) {1118 return Ok(None);1119 }1120 if collection.permissions.access() == AccessMode::AllowList {1121 // `from`, `to` checked in [`transfer`]1122 collection.check_allowlist(spender)?;1123 }11241125 if collection.ignores_token_restrictions(spender) {1126 return Ok(Self::compute_allowance_decrease(1127 collection, token, from, &spender, amount,1128 ));1129 }11301131 if let Some(source) = T::CrossTokenAddressMapping::address_to_token(from) {1132 // TODO: should collection owner be allowed to perform this transfer?1133 ensure!(1134 <PalletStructure<T>>::check_indirectly_owned(1135 spender.clone(),1136 source.0,1137 source.1,1138 None,1139 nesting_budget1140 )?,1141 <CommonError<T>>::ApprovedValueTooLow,1142 );1143 return Ok(None);1144 }11451146 let allowance = Self::compute_allowance_decrease(collection, token, from, &spender, amount);1147 if allowance.is_some() {1148 return Ok(allowance);1149 }11501151 // Allowance (if any) would be reduced if spender is also wallet operator1152 if <CollectionAllowance<T>>::get((collection.id, from, spender)) {1153 return Ok(allowance);1154 }11551156 Err(<CommonError<T>>::ApprovedValueTooLow.into())1157 }11581159 /// Returns `Some(amount)` if the `spender` have allowance to spend this amount.1160 /// Otherwise, it returns `None`.1161 fn compute_allowance_decrease(1162 collection: &RefungibleHandle<T>,1163 token: TokenId,1164 from: &T::CrossAccountId,1165 spender: &T::CrossAccountId,1166 amount: u128,1167 ) -> Option<u128> {1168 <Allowance<T>>::get((collection.id, token, from, spender)).checked_sub(amount)1169 }11701171 /// Transfer RFT token pieces from one account to another.1172 ///1173 /// Same as the [`transfer`] but spender doesn't needs to be an owner of the token pieces.1174 /// The owner should set allowance for the spender to transfer pieces.1175 ///1176 /// [`transfer`]: struct.Pallet.html#method.transfer1177 pub fn transfer_from(1178 collection: &RefungibleHandle<T>,1179 spender: &T::CrossAccountId,1180 from: &T::CrossAccountId,1181 to: &T::CrossAccountId,1182 token: TokenId,1183 amount: u128,1184 nesting_budget: &dyn Budget,1185 ) -> DispatchResult {1186 let allowance =1187 Self::check_allowed(collection, spender, from, token, amount, nesting_budget)?;11881189 // =========11901191 Self::transfer(collection, from, to, token, amount, nesting_budget)?;1192 if let Some(allowance) = allowance {1193 Self::set_allowance_unchecked(collection, from, spender, token, allowance);1194 }1195 Ok(())1196 }11971198 /// Burn RFT token pieces from the account.1199 ///1200 /// Same as the [`burn`] but spender doesn't need to be an owner of the token pieces. The owner should1201 /// set allowance for the spender to burn pieces1202 ///1203 /// [`burn`]: struct.Pallet.html#method.burn1204 pub fn burn_from(1205 collection: &RefungibleHandle<T>,1206 spender: &T::CrossAccountId,1207 from: &T::CrossAccountId,1208 token: TokenId,1209 amount: u128,1210 nesting_budget: &dyn Budget,1211 ) -> DispatchResult {1212 let allowance =1213 Self::check_allowed(collection, spender, from, token, amount, nesting_budget)?;12141215 // =========12161217 Self::burn(collection, from, token, amount)?;1218 if let Some(allowance) = allowance {1219 Self::set_allowance_unchecked(collection, from, spender, token, allowance);1220 }1221 Ok(())1222 }12231224 /// Create RFT token.1225 ///1226 /// The sender should be the owner/admin of the collection or collection should be configured1227 /// to allow public minting.1228 ///1229 /// - `data`: Contains list of users who will become the owners of the token pieces and amount1230 /// of token pieces they will receive.1231 pub fn create_item(1232 collection: &RefungibleHandle<T>,1233 sender: &T::CrossAccountId,1234 data: CreateItemData<T>,1235 nesting_budget: &dyn Budget,1236 ) -> DispatchResult {1237 Self::create_multiple_items(collection, sender, vec![data], nesting_budget)1238 }12391240 /// Repartition RFT token.1241 ///1242 /// `repartition` will set token balance of the sender and total amount of token pieces.1243 /// Sender should own all of the token pieces. `repartition' could be done even if some1244 /// token pieces were burned before.1245 ///1246 /// - `amount`: Total amount of token pieces that the token will have after `repartition`.1247 pub fn repartition(1248 collection: &RefungibleHandle<T>,1249 owner: &T::CrossAccountId,1250 token: TokenId,1251 amount: u128,1252 ) -> DispatchResult {1253 ensure!(1254 amount <= MAX_REFUNGIBLE_PIECES,1255 <Error<T>>::WrongRefungiblePieces1256 );1257 ensure!(amount > 0, <CommonError<T>>::TokenValueTooLow);1258 // Ensure user owns all pieces1259 let total_pieces = Self::total_pieces(collection.id, token).unwrap_or(u128::MAX);1260 let balance = <Balance<T>>::get((collection.id, token, owner));1261 ensure!(1262 total_pieces == balance,1263 <Error<T>>::RepartitionWhileNotOwningAllPieces1264 );12651266 <Balance<T>>::insert((collection.id, token, owner), amount);1267 <TotalSupply<T>>::insert((collection.id, token), amount);12681269 if amount > total_pieces {1270 let mint_amount = amount - total_pieces;1271 <PalletEvm<T>>::deposit_log(1272 ERC20Events::Transfer {1273 from: H160::default(),1274 to: *owner.as_eth(),1275 value: mint_amount.into(),1276 }1277 .to_log(T::EvmTokenAddressMapping::token_to_address(1278 collection.id,1279 token,1280 )),1281 );1282 <PalletCommon<T>>::deposit_event(CommonEvent::ItemCreated(1283 collection.id,1284 token,1285 owner.clone(),1286 mint_amount,1287 ));1288 } else if total_pieces > amount {1289 let burn_amount = total_pieces - amount;1290 <PalletEvm<T>>::deposit_log(1291 ERC20Events::Transfer {1292 from: *owner.as_eth(),1293 to: H160::default(),1294 value: burn_amount.into(),1295 }1296 .to_log(T::EvmTokenAddressMapping::token_to_address(1297 collection.id,1298 token,1299 )),1300 );1301 <PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(1302 collection.id,1303 token,1304 owner.clone(),1305 burn_amount,1306 ));1307 }13081309 Ok(())1310 }13111312 fn token_owner(1313 collection_id: CollectionId,1314 token_id: TokenId,1315 ) -> Result<T::CrossAccountId, TokenOwnerError> {1316 let mut owner = None;1317 let mut count = 0;1318 for key in Balance::<T>::iter_key_prefix((collection_id, token_id)) {1319 count += 1;1320 if count > 1 {1321 return Err(TokenOwnerError::MultipleOwners);1322 }1323 owner = Some(key);1324 }1325 owner.ok_or(TokenOwnerError::NotFound)1326 }13271328 fn total_pieces(collection_id: CollectionId, token_id: TokenId) -> Option<u128> {1329 <TotalSupply<T>>::try_get((collection_id, token_id)).ok()1330 }13311332 pub fn set_collection_properties(1333 collection: &RefungibleHandle<T>,1334 sender: &T::CrossAccountId,1335 properties: Vec<Property>,1336 ) -> DispatchResult {1337 <PalletCommon<T>>::set_collection_properties(collection, sender, properties.into_iter())1338 }13391340 pub fn delete_collection_properties(1341 collection: &RefungibleHandle<T>,1342 sender: &T::CrossAccountId,1343 property_keys: Vec<PropertyKey>,1344 ) -> DispatchResult {1345 <PalletCommon<T>>::delete_collection_properties(1346 collection,1347 sender,1348 property_keys.into_iter(),1349 )1350 }13511352 pub fn set_token_property_permissions(1353 collection: &RefungibleHandle<T>,1354 sender: &T::CrossAccountId,1355 property_permissions: Vec<PropertyKeyPermission>,1356 ) -> DispatchResult {1357 <PalletCommon<T>>::set_token_property_permissions(collection, sender, property_permissions)1358 }13591360 pub fn token_property_permission(collection_id: CollectionId) -> PropertiesPermissionMap {1361 <PalletCommon<T>>::property_permissions(collection_id)1362 }13631364 pub fn set_scoped_token_property_permissions(1365 collection: &RefungibleHandle<T>,1366 sender: &T::CrossAccountId,1367 scope: PropertyScope,1368 property_permissions: Vec<PropertyKeyPermission>,1369 ) -> DispatchResult {1370 <PalletCommon<T>>::set_scoped_token_property_permissions(1371 collection,1372 sender,1373 scope,1374 property_permissions,1375 )1376 }13771378 /// Returns 10 token in no particular order.1379 ///1380 /// There is no direct way to get token holders in ascending order,1381 /// since `iter_prefix` returns values in no particular order.1382 /// Therefore, getting the 10 largest holders with a large value of holders1383 /// can lead to impact memory allocation + sorting with `n * log (n)`.1384 pub fn token_owners(1385 collection_id: CollectionId,1386 token: TokenId,1387 ) -> Option<Vec<T::CrossAccountId>> {1388 let res: Vec<T::CrossAccountId> = <Balance<T>>::iter_prefix((collection_id, token))1389 .map(|(owner, _amount)| owner)1390 .take(10)1391 .collect();13921393 if res.is_empty() {1394 None1395 } else {1396 Some(res)1397 }1398 }13991400 /// Sets or unsets the approval of a given operator.1401 ///1402 /// The `operator` is allowed to transfer all token pieces of the `owner` on their behalf.1403 /// - `owner`: Token owner1404 /// - `operator`: Operator1405 /// - `approve`: Should operator status be granted or revoked?1406 pub fn set_allowance_for_all(1407 collection: &RefungibleHandle<T>,1408 owner: &T::CrossAccountId,1409 spender: &T::CrossAccountId,1410 approve: bool,1411 ) -> DispatchResult {1412 <PalletCommon<T>>::set_allowance_for_all(1413 collection,1414 owner,1415 spender,1416 approve,1417 || <CollectionAllowance<T>>::insert((collection.id, owner, spender), approve),1418 ERC721Events::ApprovalForAll {1419 owner: *owner.as_eth(),1420 operator: *spender.as_eth(),1421 approved: approve,1422 }1423 .to_log(collection_id_to_address(collection.id)),1424 )1425 }14261427 /// Tells whether the given `owner` approves the `operator`.1428 pub fn allowance_for_all(1429 collection: &RefungibleHandle<T>,1430 owner: &T::CrossAccountId,1431 spender: &T::CrossAccountId,1432 ) -> bool {1433 <CollectionAllowance<T>>::get((collection.id, owner, spender))1434 }14351436 pub fn repair_item(collection: &RefungibleHandle<T>, token: TokenId) -> DispatchResult {1437 <TokenProperties<T>>::mutate((collection.id, token), |properties| {1438 properties.recompute_consumed_space();1439 });14401441 Ok(())1442 }1443}1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! # Refungible Pallet18//!19//! The Refungible pallet provides functionality for handling refungible collections and tokens.20//!21//! - [`Config`]22//! - [`RefungibleHandle`]23//! - [`Pallet`]24//! - [`CommonWeights`](common::CommonWeights)25//!26//! ## Overview27//!28//! The Refungible pallet provides functions for:29//!30//! - RFT collection creation and removal31//! - Minting and burning of RFT tokens32//! - Partition and repartition of RFT tokens33//! - Retrieving number of pieces of RFT token34//! - Retrieving account balances35//! - Transfering RFT token pieces36//! - Burning RFT token pieces37//! - Setting and checking allowance for RFT tokens38//!39//! ### Terminology40//!41//! - **RFT token:** Non fungible token that was partitioned to pieces. If an account owns all42//! of the RFT token pieces than it owns the RFT token and can repartition it.43//!44//! - **RFT Collection:** A collection of RFT tokens. All RFT tokens are part of a collection.45//! Each collection has its own settings and set of permissions.46//!47//! - **RFT token piece:** A fungible part of an RFT token.48//!49//! - **Balance:** RFT token pieces owned by an account50//!51//! - **Allowance:** Maximum number of RFT token pieces that one account is allowed to52//! transfer from the balance of another account53//!54//! - **Burning:** The process of “deleting” a token from a collection or removing token pieces from55//! an account balance.56//!57//! ### Implementations58//!59//! The Refungible pallet provides implementations for the following traits. If these traits provide60//! the functionality that you need, then you can avoid coupling with the Refungible pallet.61//!62//! - [`CommonWeightInfo`](pallet_common::CommonWeightInfo): Functions for retrieval of transaction weight63//! - [`CommonCollectionOperations`](pallet_common::CommonCollectionOperations): Functions for dealing64//! with collections65//! - [`RefungibleExtensions`](pallet_common::RefungibleExtensions): Functions specific for refungible66//! collection67//!68//! ## Interface69//!70//! ### Dispatchable Functions71//!72//! - `init_collection` - Create RFT collection. RFT collection can be configured to allow or deny access for73//! some accounts.74//! - `destroy_collection` - Destroy exising RFT collection. There should be no tokens in the collection.75//! - `burn` - Burn some amount of RFT token pieces owned by account. Burns the RFT token if no pieces left.76//! - `transfer` - Transfer some amount of RFT token pieces. Transfers should be enabled for RFT collection.77//! Nests the RFT token if RFT token pieces are sent to another token.78//! - `create_item` - Mint RFT token in collection. Sender should have permission to mint tokens.79//! - `set_allowance` - Set allowance for another account to transfer balance from sender's account.80//! - `repartition` - Repartition token to selected number of pieces. Sender should own all existing pieces.81//!82//! ## Assumptions83//!84//! * Total number of pieces for one token shouldn't exceed `up_data_structs::MAX_REFUNGIBLE_PIECES`.85//! * Total number of tokens of all types shouldn't be greater than `up_data_structs::MAX_TOKEN_PREFIX_LENGTH`.86//! * Sender should be in collection's allow list to perform operations on tokens.8788#![cfg_attr(not(feature = "std"), no_std)]8990use crate::erc_token::ERC20Events;91use crate::erc::ERC721Events;9293use core::ops::Deref;94use evm_coder::ToLog;95use frame_support::{ensure, storage::with_transaction, transactional};96use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};97use pallet_evm_coder_substrate::WithRecorder;98use pallet_common::{99 CommonCollectionOperations, Error as CommonError, eth::collection_id_to_address,100 Event as CommonEvent, Pallet as PalletCommon,101};102use pallet_structure::Pallet as PalletStructure;103use sp_core::{Get, H160};104use sp_runtime::{ArithmeticError, DispatchError, DispatchResult, TransactionOutcome};105use sp_std::{vec::Vec, vec, collections::btree_map::BTreeMap};106use up_data_structs::{107 AccessMode, budget::Budget, CollectionId, CollectionFlags, CreateCollectionData,108 mapping::TokenAddressMapping, MAX_REFUNGIBLE_PIECES, Property, PropertyKey,109 PropertyKeyPermission, PropertyScope, PropertyValue, TokenId, TrySetProperty,110 PropertiesPermissionMap, CreateRefungibleExMultipleOwners, TokenOwnerError,111 TokenProperties as TokenPropertiesT,112};113114pub use pallet::*;115#[cfg(feature = "runtime-benchmarks")]116pub mod benchmarking;117pub mod common;118pub mod erc;119pub mod erc_token;120pub mod weights;121122pub type CreateItemData<T> =123 CreateRefungibleExMultipleOwners<<T as pallet_evm::Config>::CrossAccountId>;124pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;125126#[frame_support::pallet]127pub mod pallet {128 use super::*;129 use frame_support::{130 Blake2_128, Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key,131 traits::StorageVersion,132 };133 use up_data_structs::{CollectionId, TokenId};134 use super::weights::WeightInfo;135136 #[pallet::error]137 pub enum Error<T> {138 /// Not Refungible item data used to mint in Refungible collection.139 NotRefungibleDataUsedToMintFungibleCollectionToken,140 /// Maximum refungibility exceeded.141 WrongRefungiblePieces,142 /// Refungible token can't be repartitioned by user who isn't owns all pieces.143 RepartitionWhileNotOwningAllPieces,144 /// Refungible token can't nest other tokens.145 RefungibleDisallowsNesting,146 /// Setting item properties is not allowed.147 SettingPropertiesNotAllowed,148 }149150 #[pallet::config]151 pub trait Config:152 frame_system::Config + pallet_common::Config + pallet_structure::Config153 {154 type WeightInfo: WeightInfo;155 }156157 const STORAGE_VERSION: StorageVersion = StorageVersion::new(2);158159 #[pallet::pallet]160 #[pallet::storage_version(STORAGE_VERSION)]161 pub struct Pallet<T>(_);162163 /// Total amount of minted tokens in a collection.164 #[pallet::storage]165 pub type TokensMinted<T: Config> =166 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;167168 /// Amount of tokens burnt in a collection.169 #[pallet::storage]170 pub type TokensBurnt<T: Config> =171 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;172173 /// Amount of pieces a refungible token is split into.174 #[pallet::storage]175 #[pallet::getter(fn token_properties)]176 pub type TokenProperties<T: Config> = StorageNMap<177 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),178 Value = TokenPropertiesT,179 QueryKind = ValueQuery,180 >;181182 /// Total amount of pieces for token183 #[pallet::storage]184 pub type TotalSupply<T: Config> = StorageNMap<185 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),186 Value = u128,187 QueryKind = ValueQuery,188 >;189190 /// Used to enumerate tokens owned by account.191 #[pallet::storage]192 pub type Owned<T: Config> = StorageNMap<193 Key = (194 Key<Twox64Concat, CollectionId>,195 Key<Blake2_128Concat, T::CrossAccountId>,196 Key<Twox64Concat, TokenId>,197 ),198 Value = bool,199 QueryKind = ValueQuery,200 >;201202 /// Amount of tokens (not pieces) partially owned by an account within a collection.203 #[pallet::storage]204 pub type AccountBalance<T: Config> = StorageNMap<205 Key = (206 Key<Twox64Concat, CollectionId>,207 // Owner208 Key<Blake2_128Concat, T::CrossAccountId>,209 ),210 Value = u32,211 QueryKind = ValueQuery,212 >;213214 /// Amount of token pieces owned by account.215 #[pallet::storage]216 pub type Balance<T: Config> = StorageNMap<217 Key = (218 Key<Twox64Concat, CollectionId>,219 Key<Twox64Concat, TokenId>,220 // Owner221 Key<Blake2_128Concat, T::CrossAccountId>,222 ),223 Value = u128,224 QueryKind = ValueQuery,225 >;226227 /// Allowance set by a token owner for another user to perform one of certain transactions on a number of pieces of a token.228 #[pallet::storage]229 pub type Allowance<T: Config> = StorageNMap<230 Key = (231 Key<Twox64Concat, CollectionId>,232 Key<Twox64Concat, TokenId>,233 // Owner234 Key<Blake2_128, T::CrossAccountId>,235 // Spender236 Key<Blake2_128Concat, T::CrossAccountId>,237 ),238 Value = u128,239 QueryKind = ValueQuery,240 >;241242 /// Spender set by a wallet owner that could perform certain transactions on all tokens in the wallet.243 #[pallet::storage]244 pub type CollectionAllowance<T: Config> = StorageNMap<245 Key = (246 Key<Twox64Concat, CollectionId>,247 Key<Blake2_128Concat, T::CrossAccountId>, // Owner248 Key<Blake2_128Concat, T::CrossAccountId>, // Spender249 ),250 Value = bool,251 QueryKind = ValueQuery,252 >;253}254255pub struct RefungibleHandle<T: Config>(pallet_common::CollectionHandle<T>);256impl<T: Config> RefungibleHandle<T> {257 pub fn cast(inner: pallet_common::CollectionHandle<T>) -> Self {258 Self(inner)259 }260 pub fn into_inner(self) -> pallet_common::CollectionHandle<T> {261 self.0262 }263 pub fn common_mut(&mut self) -> &mut pallet_common::CollectionHandle<T> {264 &mut self.0265 }266}267268impl<T: Config> Deref for RefungibleHandle<T> {269 type Target = pallet_common::CollectionHandle<T>;270271 fn deref(&self) -> &Self::Target {272 &self.0273 }274}275276impl<T: Config> WithRecorder<T> for RefungibleHandle<T> {277 fn recorder(&self) -> &pallet_evm_coder_substrate::SubstrateRecorder<T> {278 self.0.recorder()279 }280 fn into_recorder(self) -> pallet_evm_coder_substrate::SubstrateRecorder<T> {281 self.0.into_recorder()282 }283}284285impl<T: Config> Pallet<T> {286 /// Get number of RFT tokens in collection287 pub fn total_supply(collection: &RefungibleHandle<T>) -> u32 {288 <TokensMinted<T>>::get(collection.id) - <TokensBurnt<T>>::get(collection.id)289 }290291 /// Check that RFT token exists292 ///293 /// - `token`: Token ID.294 pub fn token_exists(collection: &RefungibleHandle<T>, token: TokenId) -> bool {295 <TotalSupply<T>>::contains_key((collection.id, token))296 }297298 pub fn set_scoped_token_property(299 collection_id: CollectionId,300 token_id: TokenId,301 scope: PropertyScope,302 property: Property,303 ) -> DispatchResult {304 TokenProperties::<T>::try_mutate((collection_id, token_id), |properties| {305 properties.try_scoped_set(scope, property.key, property.value)306 })307 .map_err(<CommonError<T>>::from)?;308309 Ok(())310 }311312 pub fn set_scoped_token_properties(313 collection_id: CollectionId,314 token_id: TokenId,315 scope: PropertyScope,316 properties: impl Iterator<Item = Property>,317 ) -> DispatchResult {318 TokenProperties::<T>::try_mutate((collection_id, token_id), |stored_properties| {319 stored_properties.try_scoped_set_from_iter(scope, properties)320 })321 .map_err(<CommonError<T>>::from)?;322323 Ok(())324 }325}326327// unchecked calls skips any permission checks328impl<T: Config> Pallet<T> {329 /// Create RFT collection330 ///331 /// `init_collection` will take non-refundable deposit for collection creation.332 ///333 /// - `data`: Contains settings for collection limits and permissions.334 pub fn init_collection(335 owner: T::CrossAccountId,336 payer: T::CrossAccountId,337 data: CreateCollectionData<T::AccountId>,338 flags: CollectionFlags,339 ) -> Result<CollectionId, DispatchError> {340 <PalletCommon<T>>::init_collection(owner, payer, data, flags)341 }342343 /// Destroy RFT collection344 ///345 /// `destroy_collection` will throw error if collection contains any tokens.346 /// Only owner can destroy collection.347 pub fn destroy_collection(348 collection: RefungibleHandle<T>,349 sender: &T::CrossAccountId,350 ) -> DispatchResult {351 let id = collection.id;352353 if Self::collection_has_tokens(id) {354 return Err(<CommonError<T>>::CantDestroyNotEmptyCollection.into());355 }356357 // =========358359 PalletCommon::destroy_collection(collection.0, sender)?;360361 <TokensMinted<T>>::remove(id);362 <TokensBurnt<T>>::remove(id);363 let _ = <TotalSupply<T>>::clear_prefix((id,), u32::MAX, None);364 let _ = <Balance<T>>::clear_prefix((id,), u32::MAX, None);365 let _ = <Allowance<T>>::clear_prefix((id,), u32::MAX, None);366 let _ = <Owned<T>>::clear_prefix((id,), u32::MAX, None);367 let _ = <AccountBalance<T>>::clear_prefix((id,), u32::MAX, None);368 Ok(())369 }370371 fn collection_has_tokens(collection_id: CollectionId) -> bool {372 <TotalSupply<T>>::iter_prefix((collection_id,))373 .next()374 .is_some()375 }376377 pub fn burn_token_unchecked(378 collection: &RefungibleHandle<T>,379 owner: &T::CrossAccountId,380 token_id: TokenId,381 ) -> DispatchResult {382 let burnt = <TokensBurnt<T>>::get(collection.id)383 .checked_add(1)384 .ok_or(ArithmeticError::Overflow)?;385386 <TokensBurnt<T>>::insert(collection.id, burnt);387 <TokenProperties<T>>::remove((collection.id, token_id));388 <TotalSupply<T>>::remove((collection.id, token_id));389 let _ = <Balance<T>>::clear_prefix((collection.id, token_id), u32::MAX, None);390 let _ = <Allowance<T>>::clear_prefix((collection.id, token_id), u32::MAX, None);391 <PalletEvm<T>>::deposit_log(392 ERC721Events::Transfer {393 from: *owner.as_eth(),394 to: H160::default(),395 token_id: token_id.into(),396 }397 .to_log(collection_id_to_address(collection.id)),398 );399 Ok(())400 }401402 /// Burn RFT token pieces403 ///404 /// `burn` will decrease total amount of token pieces and amount owned by sender.405 /// `burn` can be called even if there are multiple owners of the RFT token.406 /// If sender wouldn't have any pieces left after `burn` than she will stop being407 /// one of the owners of the token. If there is no account that owns any pieces of408 /// the token than token will be burned too.409 ///410 /// - `amount`: Amount of token pieces to burn.411 /// - `token`: Token who's pieces should be burned412 /// - `collection`: Collection that contains the token413 pub fn burn(414 collection: &RefungibleHandle<T>,415 owner: &T::CrossAccountId,416 token: TokenId,417 amount: u128,418 ) -> DispatchResult {419 if <Balance<T>>::get((collection.id, token, owner)) == 0 {420 return Err(<CommonError<T>>::TokenValueTooLow.into());421 }422423 let total_supply = <TotalSupply<T>>::get((collection.id, token))424 .checked_sub(amount)425 .ok_or(<CommonError<T>>::TokenValueTooLow)?;426427 // This was probally last owner of this token?428 if total_supply == 0 {429 // Ensure user actually owns this amount430 ensure!(431 <Balance<T>>::get((collection.id, token, owner)) == amount,432 <CommonError<T>>::TokenValueTooLow433 );434 let account_balance = <AccountBalance<T>>::get((collection.id, owner))435 .checked_sub(1)436 // Should not occur437 .ok_or(ArithmeticError::Underflow)?;438439 // =========440441 <Owned<T>>::remove((collection.id, owner, token));442 <PalletStructure<T>>::unnest_if_nested(owner, collection.id, token);443 <AccountBalance<T>>::insert((collection.id, owner), account_balance);444 Self::burn_token_unchecked(collection, owner, token)?;445 <PalletEvm<T>>::deposit_log(446 ERC20Events::Transfer {447 from: *owner.as_eth(),448 to: H160::default(),449 value: amount.into(),450 }451 .to_log(collection_id_to_address(collection.id)),452 );453 <PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(454 collection.id,455 token,456 owner.clone(),457 amount,458 ));459 return Ok(());460 }461462 let balance = <Balance<T>>::get((collection.id, token, owner))463 .checked_sub(amount)464 .ok_or(<CommonError<T>>::TokenValueTooLow)?;465 let account_balance = if balance == 0 {466 <AccountBalance<T>>::get((collection.id, owner))467 .checked_sub(1)468 // Should not occur469 .ok_or(ArithmeticError::Underflow)?470 } else {471 0472 };473474 // =========475476 if balance == 0 {477 <Owned<T>>::remove((collection.id, owner, token));478 <PalletStructure<T>>::unnest_if_nested(owner, collection.id, token);479 <Balance<T>>::remove((collection.id, token, owner));480 <AccountBalance<T>>::insert((collection.id, owner), account_balance);481482 if let Ok(user) = Self::token_owner(collection.id, token) {483 <PalletEvm<T>>::deposit_log(484 ERC721Events::Transfer {485 from: erc::ADDRESS_FOR_PARTIALLY_OWNED_TOKENS,486 to: *user.as_eth(),487 token_id: token.into(),488 }489 .to_log(collection_id_to_address(collection.id)),490 );491 }492 } else {493 <Balance<T>>::insert((collection.id, token, owner), balance);494 }495 <TotalSupply<T>>::insert((collection.id, token), total_supply);496497 <PalletEvm<T>>::deposit_log(498 ERC20Events::Transfer {499 from: *owner.as_eth(),500 to: H160::default(),501 value: amount.into(),502 }503 .to_log(T::EvmTokenAddressMapping::token_to_address(504 collection.id,505 token,506 )),507 );508 <PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(509 collection.id,510 token,511 owner.clone(),512 amount,513 ));514 Ok(())515 }516517 /// A batch operation to add, edit or remove properties for a token.518 /// It sets or removes a token's properties according to519 /// `properties_updates` contents:520 /// * sets a property under the <key> with the value provided `(<key>, Some(<value>))`521 /// * removes a property under the <key> if the value is `None` `(<key>, None)`.522 ///523 /// - `nesting_budget`: Limit for searching parents in-depth to check ownership.524 /// - `is_token_create`: Indicates that method is called during token initialization.525 /// Allows to bypass ownership check.526 ///527 /// All affected properties should have `mutable` permission528 /// to be **deleted** or to be **set more than once**,529 /// and the sender should have permission to edit those properties.530 ///531 /// This function fires an event for each property change.532 /// In case of an error, all the changes (including the events) will be reverted533 /// since the function is transactional.534 #[transactional]535 fn modify_token_properties(536 collection: &RefungibleHandle<T>,537 sender: &T::CrossAccountId,538 token_id: TokenId,539 properties_updates: impl Iterator<Item = (PropertyKey, Option<PropertyValue>)>,540 is_token_create: bool,541 nesting_budget: &dyn Budget,542 ) -> DispatchResult {543 let is_token_owner = || -> Result<bool, DispatchError> {544 let balance = collection.balance(sender.clone(), token_id);545 let total_pieces: u128 =546 Self::total_pieces(collection.id, token_id).unwrap_or(u128::MAX);547 if balance != total_pieces {548 return Ok(false);549 }550551 let is_bundle_owner = <PalletStructure<T>>::check_indirectly_owned(552 sender.clone(),553 collection.id,554 token_id,555 None,556 nesting_budget,557 )?;558559 Ok(is_bundle_owner)560 };561562 let stored_properties = <TokenProperties<T>>::get((collection.id, token_id));563564 <PalletCommon<T>>::modify_token_properties(565 collection,566 sender,567 token_id,568 properties_updates,569 is_token_create,570 stored_properties,571 is_token_owner,572 |properties| <TokenProperties<T>>::set((collection.id, token_id), properties),573 erc::ERC721TokenEvent::TokenChanged {574 token_id: token_id.into(),575 }576 .to_log(T::ContractAddress::get()),577 )578 }579580 pub fn set_token_properties(581 collection: &RefungibleHandle<T>,582 sender: &T::CrossAccountId,583 token_id: TokenId,584 properties: impl Iterator<Item = Property>,585 is_token_create: bool,586 nesting_budget: &dyn Budget,587 ) -> DispatchResult {588 Self::modify_token_properties(589 collection,590 sender,591 token_id,592 properties.map(|p| (p.key, Some(p.value))),593 is_token_create,594 nesting_budget,595 )596 }597598 pub fn set_token_property(599 collection: &RefungibleHandle<T>,600 sender: &T::CrossAccountId,601 token_id: TokenId,602 property: Property,603 nesting_budget: &dyn Budget,604 ) -> DispatchResult {605 let is_token_create = false;606607 Self::set_token_properties(608 collection,609 sender,610 token_id,611 [property].into_iter(),612 is_token_create,613 nesting_budget,614 )615 }616617 pub fn delete_token_properties(618 collection: &RefungibleHandle<T>,619 sender: &T::CrossAccountId,620 token_id: TokenId,621 property_keys: impl Iterator<Item = PropertyKey>,622 nesting_budget: &dyn Budget,623 ) -> DispatchResult {624 let is_token_create = false;625626 Self::modify_token_properties(627 collection,628 sender,629 token_id,630 property_keys.into_iter().map(|key| (key, None)),631 is_token_create,632 nesting_budget,633 )634 }635636 pub fn delete_token_property(637 collection: &RefungibleHandle<T>,638 sender: &T::CrossAccountId,639 token_id: TokenId,640 property_key: PropertyKey,641 nesting_budget: &dyn Budget,642 ) -> DispatchResult {643 Self::delete_token_properties(644 collection,645 sender,646 token_id,647 [property_key].into_iter(),648 nesting_budget,649 )650 }651652 /// Transfer RFT token pieces from one account to another.653 ///654 /// If the sender is no longer owns any pieces after the `transfer` than she stops being an owner of the token.655 ///656 /// - `from`: Owner of token pieces to transfer.657 /// - `to`: Recepient of transfered token pieces.658 /// - `amount`: Amount of token pieces to transfer.659 /// - `token`: Token whos pieces should be transfered660 /// - `collection`: Collection that contains the token661 pub fn transfer(662 collection: &RefungibleHandle<T>,663 from: &T::CrossAccountId,664 to: &T::CrossAccountId,665 token: TokenId,666 amount: u128,667 nesting_budget: &dyn Budget,668 ) -> DispatchResult {669 ensure!(670 collection.limits.transfers_enabled(),671 <CommonError<T>>::TransferNotAllowed672 );673674 if collection.permissions.access() == AccessMode::AllowList {675 collection.check_allowlist(from)?;676 collection.check_allowlist(to)?;677 }678 <PalletCommon<T>>::ensure_correct_receiver(to)?;679680 let initial_balance_from = <Balance<T>>::get((collection.id, token, from));681682 if initial_balance_from == 0 {683 return Err(<CommonError<T>>::TokenValueTooLow.into());684 }685686 let updated_balance_from = initial_balance_from687 .checked_sub(amount)688 .ok_or(<CommonError<T>>::TokenValueTooLow)?;689 let mut create_target = false;690 let from_to_differ = from != to;691 let updated_balance_to = if from != to && amount != 0 {692 let old_balance = <Balance<T>>::get((collection.id, token, to));693 if old_balance == 0 {694 create_target = true;695 }696 Some(697 old_balance698 .checked_add(amount)699 .ok_or(ArithmeticError::Overflow)?,700 )701 } else {702 None703 };704705 let account_balance_from = if updated_balance_from == 0 {706 Some(707 <AccountBalance<T>>::get((collection.id, from))708 .checked_sub(1)709 // Should not occur710 .ok_or(ArithmeticError::Underflow)?,711 )712 } else {713 None714 };715 // Account data is created in token, AccountBalance should be increased716 // But only if from != to as we shouldn't check overflow in this case717 let account_balance_to = if create_target && from_to_differ {718 let account_balance_to = <AccountBalance<T>>::get((collection.id, to))719 .checked_add(1)720 .ok_or(ArithmeticError::Overflow)?;721 ensure!(722 account_balance_to < collection.limits.account_token_ownership_limit(),723 <CommonError<T>>::AccountTokenLimitExceeded,724 );725726 Some(account_balance_to)727 } else {728 None729 };730731 // =========732733 if let Some(updated_balance_to) = updated_balance_to {734 // from != to && amount != 0735736 <PalletStructure<T>>::nest_if_sent_to_token(737 from.clone(),738 to,739 collection.id,740 token,741 nesting_budget,742 )?;743744 if updated_balance_from == 0 {745 <Balance<T>>::remove((collection.id, token, from));746 <PalletStructure<T>>::unnest_if_nested(from, collection.id, token);747 } else {748 <Balance<T>>::insert((collection.id, token, from), updated_balance_from);749 }750 <Balance<T>>::insert((collection.id, token, to), updated_balance_to);751 if let Some(account_balance_from) = account_balance_from {752 <AccountBalance<T>>::insert((collection.id, from), account_balance_from);753 <Owned<T>>::remove((collection.id, from, token));754 }755 if let Some(account_balance_to) = account_balance_to {756 <AccountBalance<T>>::insert((collection.id, to), account_balance_to);757 <Owned<T>>::insert((collection.id, to, token), true);758 }759 }760761 <PalletEvm<T>>::deposit_log(762 ERC20Events::Transfer {763 from: *from.as_eth(),764 to: *to.as_eth(),765 value: amount.into(),766 }767 .to_log(T::EvmTokenAddressMapping::token_to_address(768 collection.id,769 token,770 )),771 );772773 <PalletCommon<T>>::deposit_event(CommonEvent::Transfer(774 collection.id,775 token,776 from.clone(),777 to.clone(),778 amount,779 ));780781 let total_supply = <TotalSupply<T>>::get((collection.id, token));782783 if amount == total_supply {784 // if token was fully owned by `from` and will be fully owned by `to` after transfer785 <PalletEvm<T>>::deposit_log(786 ERC721Events::Transfer {787 from: *from.as_eth(),788 to: *to.as_eth(),789 token_id: token.into(),790 }791 .to_log(collection_id_to_address(collection.id)),792 );793 } else if let Some(updated_balance_to) = updated_balance_to {794 // if `from` not equals `to`. This condition is needed to avoid sending event795 // when `from` fully owns token and sends part of token pieces to itself.796 if initial_balance_from == total_supply {797 // if token was fully owned by `from` and will be only partially owned by `to`798 // and `from` after transfer799 <PalletEvm<T>>::deposit_log(800 ERC721Events::Transfer {801 from: *from.as_eth(),802 to: erc::ADDRESS_FOR_PARTIALLY_OWNED_TOKENS,803 token_id: token.into(),804 }805 .to_log(collection_id_to_address(collection.id)),806 );807 } else if updated_balance_to == total_supply {808 // if token was partially owned by `from` and will be fully owned by `to` after transfer809 <PalletEvm<T>>::deposit_log(810 ERC721Events::Transfer {811 from: erc::ADDRESS_FOR_PARTIALLY_OWNED_TOKENS,812 to: *to.as_eth(),813 token_id: token.into(),814 }815 .to_log(collection_id_to_address(collection.id)),816 );817 }818 }819820 Ok(())821 }822823 /// Batched operation to create multiple RFT tokens.824 ///825 /// Same as `create_item` but creates multiple tokens.826 ///827 /// - `data`: Same as 'data` in `create_item` but contains data for multiple tokens.828 pub fn create_multiple_items(829 collection: &RefungibleHandle<T>,830 sender: &T::CrossAccountId,831 data: Vec<CreateItemData<T>>,832 nesting_budget: &dyn Budget,833 ) -> DispatchResult {834 if !collection.is_owner_or_admin(sender) {835 ensure!(836 collection.permissions.mint_mode(),837 <CommonError<T>>::PublicMintingNotAllowed838 );839 collection.check_allowlist(sender)?;840841 for item in data.iter() {842 for user in item.users.keys() {843 collection.check_allowlist(user)?;844 }845 }846 }847848 for item in data.iter() {849 for (owner, _) in item.users.iter() {850 <PalletCommon<T>>::ensure_correct_receiver(owner)?;851 }852 }853854 // Total pieces per tokens855 let totals = data856 .iter()857 .map(|data| {858 Ok(data859 .users860 .iter()861 .map(|u| u.1)862 .try_fold(0u128, |acc, v| acc.checked_add(*v))863 .ok_or(ArithmeticError::Overflow)?)864 })865 .collect::<Result<Vec<_>, DispatchError>>()?;866 for total in &totals {867 ensure!(868 *total <= MAX_REFUNGIBLE_PIECES,869 <Error<T>>::WrongRefungiblePieces870 );871 }872873 let first_token_id = <TokensMinted<T>>::get(collection.id);874 let tokens_minted = first_token_id875 .checked_add(data.len() as u32)876 .ok_or(ArithmeticError::Overflow)?;877 ensure!(878 tokens_minted < collection.limits.token_limit(),879 <CommonError<T>>::CollectionTokenLimitExceeded880 );881882 let mut balances = BTreeMap::new();883 for data in &data {884 for owner in data.users.keys() {885 let balance = balances886 .entry(owner)887 .or_insert_with(|| <AccountBalance<T>>::get((collection.id, owner)));888 *balance = balance.checked_add(1).ok_or(ArithmeticError::Overflow)?;889890 ensure!(891 *balance <= collection.limits.account_token_ownership_limit(),892 <CommonError<T>>::AccountTokenLimitExceeded,893 );894 }895 }896897 for (i, token) in data.iter().enumerate() {898 let token_id = TokenId(first_token_id + i as u32 + 1);899 for (to, _) in token.users.iter() {900 <PalletStructure<T>>::check_nesting(901 sender.clone(),902 to,903 collection.id,904 token_id,905 nesting_budget,906 )?;907 }908 }909910 // =========911912 with_transaction(|| {913 for (i, data) in data.iter().enumerate() {914 let token_id = first_token_id + i as u32 + 1;915 <TotalSupply<T>>::insert((collection.id, token_id), totals[i]);916917 for (user, amount) in data.users.iter() {918 if *amount == 0 {919 continue;920 }921 <Balance<T>>::insert((collection.id, token_id, &user), amount);922 <Owned<T>>::insert((collection.id, &user, TokenId(token_id)), true);923 <PalletStructure<T>>::nest_if_sent_to_token_unchecked(924 user,925 collection.id,926 TokenId(token_id),927 );928 }929930 if let Err(e) = Self::set_token_properties(931 collection,932 sender,933 TokenId(token_id),934 data.properties.clone().into_iter(),935 true,936 nesting_budget,937 ) {938 return TransactionOutcome::Rollback(Err(e));939 }940 }941 TransactionOutcome::Commit(Ok(()))942 })?;943944 <TokensMinted<T>>::insert(collection.id, tokens_minted);945946 for (account, balance) in balances {947 <AccountBalance<T>>::insert((collection.id, account), balance);948 }949950 for (i, token) in data.into_iter().enumerate() {951 let token_id = first_token_id + i as u32 + 1;952953 let receivers = token954 .users955 .into_iter()956 .filter(|(_, amount)| *amount > 0)957 .collect::<Vec<_>>();958959 if let [(user, _)] = receivers.as_slice() {960 // if there is exactly one receiver961 <PalletEvm<T>>::deposit_log(962 ERC721Events::Transfer {963 from: H160::default(),964 to: *user.as_eth(),965 token_id: token_id.into(),966 }967 .to_log(collection_id_to_address(collection.id)),968 );969 } else if let [_, ..] = receivers.as_slice() {970 // if there is more than one receiver971 <PalletEvm<T>>::deposit_log(972 ERC721Events::Transfer {973 from: H160::default(),974 to: erc::ADDRESS_FOR_PARTIALLY_OWNED_TOKENS,975 token_id: token_id.into(),976 }977 .to_log(collection_id_to_address(collection.id)),978 );979 }980981 for (user, amount) in receivers.into_iter() {982 <PalletEvm<T>>::deposit_log(983 ERC20Events::Transfer {984 from: H160::default(),985 to: *user.as_eth(),986 value: amount.into(),987 }988 .to_log(T::EvmTokenAddressMapping::token_to_address(989 collection.id,990 TokenId(token_id),991 )),992 );993 <PalletCommon<T>>::deposit_event(CommonEvent::ItemCreated(994 collection.id,995 TokenId(token_id),996 user,997 amount,998 ));999 }1000 }1001 Ok(())1002 }10031004 pub fn set_allowance_unchecked(1005 collection: &RefungibleHandle<T>,1006 sender: &T::CrossAccountId,1007 spender: &T::CrossAccountId,1008 token: TokenId,1009 amount: u128,1010 ) {1011 if amount == 0 {1012 <Allowance<T>>::remove((collection.id, token, sender, spender));1013 } else {1014 <Allowance<T>>::insert((collection.id, token, sender, spender), amount);1015 }10161017 <PalletEvm<T>>::deposit_log(1018 ERC20Events::Approval {1019 owner: *sender.as_eth(),1020 spender: *spender.as_eth(),1021 value: amount.into(),1022 }1023 .to_log(T::EvmTokenAddressMapping::token_to_address(1024 collection.id,1025 token,1026 )),1027 );1028 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(1029 collection.id,1030 token,1031 sender.clone(),1032 spender.clone(),1033 amount,1034 ))1035 }10361037 /// Set allowance for the spender to `transfer` or `burn` sender's token pieces.1038 ///1039 /// - `amount`: Amount of token pieces the spender is allowed to `transfer` or `burn.1040 pub fn set_allowance(1041 collection: &RefungibleHandle<T>,1042 sender: &T::CrossAccountId,1043 spender: &T::CrossAccountId,1044 token: TokenId,1045 amount: u128,1046 ) -> DispatchResult {1047 if collection.permissions.access() == AccessMode::AllowList {1048 collection.check_allowlist(sender)?;1049 collection.check_allowlist(spender)?;1050 }10511052 <PalletCommon<T>>::ensure_correct_receiver(spender)?;10531054 if <Balance<T>>::get((collection.id, token, sender)) < amount {1055 ensure!(1056 collection.ignores_owned_amount(sender) && Self::token_exists(collection, token),1057 <CommonError<T>>::CantApproveMoreThanOwned1058 );1059 }10601061 // =========10621063 Self::set_allowance_unchecked(collection, sender, spender, token, amount);1064 Ok(())1065 }10661067 /// Set allowance to spend from sender's eth mirror1068 ///1069 /// - `from`: Address of sender's eth mirror.1070 /// - `to`: Adress of spender.1071 /// - `amount`: Amount of token pieces the spender is allowed to `transfer` or `burn.1072 pub fn set_allowance_from(1073 collection: &RefungibleHandle<T>,1074 sender: &T::CrossAccountId,1075 from: &T::CrossAccountId,1076 to: &T::CrossAccountId,1077 token_id: TokenId,1078 amount: u128,1079 ) -> DispatchResult {1080 if collection.permissions.access() == AccessMode::AllowList {1081 collection.check_allowlist(sender)?;1082 collection.check_allowlist(from)?;1083 collection.check_allowlist(to)?;1084 }10851086 <PalletCommon<T>>::ensure_correct_receiver(to)?;10871088 ensure!(1089 sender.conv_eq(from),1090 <CommonError<T>>::AddressIsNotEthMirror1091 );10921093 if <Balance<T>>::get((collection.id, token_id, from)) < amount {1094 ensure!(1095 collection.limits.owner_can_transfer()1096 && (collection.is_owner_or_admin(sender) || collection.is_owner_or_admin(from))1097 && Self::token_exists(collection, token_id),1098 <CommonError<T>>::CantApproveMoreThanOwned1099 );1100 }11011102 // =========11031104 Self::set_allowance_unchecked(collection, from, to, token_id, amount);1105 Ok(())1106 }11071108 /// Returns allowance, which should be set after transaction1109 fn check_allowed(1110 collection: &RefungibleHandle<T>,1111 spender: &T::CrossAccountId,1112 from: &T::CrossAccountId,1113 token: TokenId,1114 amount: u128,1115 nesting_budget: &dyn Budget,1116 ) -> Result<Option<u128>, DispatchError> {1117 if spender.conv_eq(from) {1118 return Ok(None);1119 }1120 if collection.permissions.access() == AccessMode::AllowList {1121 // `from`, `to` checked in [`transfer`]1122 collection.check_allowlist(spender)?;1123 }11241125 if collection.ignores_token_restrictions(spender) {1126 return Ok(Self::compute_allowance_decrease(1127 collection, token, from, spender, amount,1128 ));1129 }11301131 if let Some(source) = T::CrossTokenAddressMapping::address_to_token(from) {1132 // TODO: should collection owner be allowed to perform this transfer?1133 ensure!(1134 <PalletStructure<T>>::check_indirectly_owned(1135 spender.clone(),1136 source.0,1137 source.1,1138 None,1139 nesting_budget1140 )?,1141 <CommonError<T>>::ApprovedValueTooLow,1142 );1143 return Ok(None);1144 }11451146 let allowance = Self::compute_allowance_decrease(collection, token, from, spender, amount);1147 if allowance.is_some() {1148 return Ok(allowance);1149 }11501151 // Allowance (if any) would be reduced if spender is also wallet operator1152 if <CollectionAllowance<T>>::get((collection.id, from, spender)) {1153 return Ok(allowance);1154 }11551156 Err(<CommonError<T>>::ApprovedValueTooLow.into())1157 }11581159 /// Returns `Some(amount)` if the `spender` have allowance to spend this amount.1160 /// Otherwise, it returns `None`.1161 fn compute_allowance_decrease(1162 collection: &RefungibleHandle<T>,1163 token: TokenId,1164 from: &T::CrossAccountId,1165 spender: &T::CrossAccountId,1166 amount: u128,1167 ) -> Option<u128> {1168 <Allowance<T>>::get((collection.id, token, from, spender)).checked_sub(amount)1169 }11701171 /// Transfer RFT token pieces from one account to another.1172 ///1173 /// Same as the [`transfer`] but spender doesn't needs to be an owner of the token pieces.1174 /// The owner should set allowance for the spender to transfer pieces.1175 ///1176 /// [`transfer`]: struct.Pallet.html#method.transfer1177 pub fn transfer_from(1178 collection: &RefungibleHandle<T>,1179 spender: &T::CrossAccountId,1180 from: &T::CrossAccountId,1181 to: &T::CrossAccountId,1182 token: TokenId,1183 amount: u128,1184 nesting_budget: &dyn Budget,1185 ) -> DispatchResult {1186 let allowance =1187 Self::check_allowed(collection, spender, from, token, amount, nesting_budget)?;11881189 // =========11901191 Self::transfer(collection, from, to, token, amount, nesting_budget)?;1192 if let Some(allowance) = allowance {1193 Self::set_allowance_unchecked(collection, from, spender, token, allowance);1194 }1195 Ok(())1196 }11971198 /// Burn RFT token pieces from the account.1199 ///1200 /// Same as the [`burn`] but spender doesn't need to be an owner of the token pieces. The owner should1201 /// set allowance for the spender to burn pieces1202 ///1203 /// [`burn`]: struct.Pallet.html#method.burn1204 pub fn burn_from(1205 collection: &RefungibleHandle<T>,1206 spender: &T::CrossAccountId,1207 from: &T::CrossAccountId,1208 token: TokenId,1209 amount: u128,1210 nesting_budget: &dyn Budget,1211 ) -> DispatchResult {1212 let allowance =1213 Self::check_allowed(collection, spender, from, token, amount, nesting_budget)?;12141215 // =========12161217 Self::burn(collection, from, token, amount)?;1218 if let Some(allowance) = allowance {1219 Self::set_allowance_unchecked(collection, from, spender, token, allowance);1220 }1221 Ok(())1222 }12231224 /// Create RFT token.1225 ///1226 /// The sender should be the owner/admin of the collection or collection should be configured1227 /// to allow public minting.1228 ///1229 /// - `data`: Contains list of users who will become the owners of the token pieces and amount1230 /// of token pieces they will receive.1231 pub fn create_item(1232 collection: &RefungibleHandle<T>,1233 sender: &T::CrossAccountId,1234 data: CreateItemData<T>,1235 nesting_budget: &dyn Budget,1236 ) -> DispatchResult {1237 Self::create_multiple_items(collection, sender, vec![data], nesting_budget)1238 }12391240 /// Repartition RFT token.1241 ///1242 /// `repartition` will set token balance of the sender and total amount of token pieces.1243 /// Sender should own all of the token pieces. `repartition' could be done even if some1244 /// token pieces were burned before.1245 ///1246 /// - `amount`: Total amount of token pieces that the token will have after `repartition`.1247 pub fn repartition(1248 collection: &RefungibleHandle<T>,1249 owner: &T::CrossAccountId,1250 token: TokenId,1251 amount: u128,1252 ) -> DispatchResult {1253 ensure!(1254 amount <= MAX_REFUNGIBLE_PIECES,1255 <Error<T>>::WrongRefungiblePieces1256 );1257 ensure!(amount > 0, <CommonError<T>>::TokenValueTooLow);1258 // Ensure user owns all pieces1259 let total_pieces = Self::total_pieces(collection.id, token).unwrap_or(u128::MAX);1260 let balance = <Balance<T>>::get((collection.id, token, owner));1261 ensure!(1262 total_pieces == balance,1263 <Error<T>>::RepartitionWhileNotOwningAllPieces1264 );12651266 <Balance<T>>::insert((collection.id, token, owner), amount);1267 <TotalSupply<T>>::insert((collection.id, token), amount);12681269 if amount > total_pieces {1270 let mint_amount = amount - total_pieces;1271 <PalletEvm<T>>::deposit_log(1272 ERC20Events::Transfer {1273 from: H160::default(),1274 to: *owner.as_eth(),1275 value: mint_amount.into(),1276 }1277 .to_log(T::EvmTokenAddressMapping::token_to_address(1278 collection.id,1279 token,1280 )),1281 );1282 <PalletCommon<T>>::deposit_event(CommonEvent::ItemCreated(1283 collection.id,1284 token,1285 owner.clone(),1286 mint_amount,1287 ));1288 } else if total_pieces > amount {1289 let burn_amount = total_pieces - amount;1290 <PalletEvm<T>>::deposit_log(1291 ERC20Events::Transfer {1292 from: *owner.as_eth(),1293 to: H160::default(),1294 value: burn_amount.into(),1295 }1296 .to_log(T::EvmTokenAddressMapping::token_to_address(1297 collection.id,1298 token,1299 )),1300 );1301 <PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(1302 collection.id,1303 token,1304 owner.clone(),1305 burn_amount,1306 ));1307 }13081309 Ok(())1310 }13111312 fn token_owner(1313 collection_id: CollectionId,1314 token_id: TokenId,1315 ) -> Result<T::CrossAccountId, TokenOwnerError> {1316 let mut owner = None;1317 let mut count = 0;1318 for key in Balance::<T>::iter_key_prefix((collection_id, token_id)) {1319 count += 1;1320 if count > 1 {1321 return Err(TokenOwnerError::MultipleOwners);1322 }1323 owner = Some(key);1324 }1325 owner.ok_or(TokenOwnerError::NotFound)1326 }13271328 fn total_pieces(collection_id: CollectionId, token_id: TokenId) -> Option<u128> {1329 <TotalSupply<T>>::try_get((collection_id, token_id)).ok()1330 }13311332 pub fn set_collection_properties(1333 collection: &RefungibleHandle<T>,1334 sender: &T::CrossAccountId,1335 properties: Vec<Property>,1336 ) -> DispatchResult {1337 <PalletCommon<T>>::set_collection_properties(collection, sender, properties.into_iter())1338 }13391340 pub fn delete_collection_properties(1341 collection: &RefungibleHandle<T>,1342 sender: &T::CrossAccountId,1343 property_keys: Vec<PropertyKey>,1344 ) -> DispatchResult {1345 <PalletCommon<T>>::delete_collection_properties(1346 collection,1347 sender,1348 property_keys.into_iter(),1349 )1350 }13511352 pub fn set_token_property_permissions(1353 collection: &RefungibleHandle<T>,1354 sender: &T::CrossAccountId,1355 property_permissions: Vec<PropertyKeyPermission>,1356 ) -> DispatchResult {1357 <PalletCommon<T>>::set_token_property_permissions(collection, sender, property_permissions)1358 }13591360 pub fn token_property_permission(collection_id: CollectionId) -> PropertiesPermissionMap {1361 <PalletCommon<T>>::property_permissions(collection_id)1362 }13631364 pub fn set_scoped_token_property_permissions(1365 collection: &RefungibleHandle<T>,1366 sender: &T::CrossAccountId,1367 scope: PropertyScope,1368 property_permissions: Vec<PropertyKeyPermission>,1369 ) -> DispatchResult {1370 <PalletCommon<T>>::set_scoped_token_property_permissions(1371 collection,1372 sender,1373 scope,1374 property_permissions,1375 )1376 }13771378 /// Returns 10 token in no particular order.1379 ///1380 /// There is no direct way to get token holders in ascending order,1381 /// since `iter_prefix` returns values in no particular order.1382 /// Therefore, getting the 10 largest holders with a large value of holders1383 /// can lead to impact memory allocation + sorting with `n * log (n)`.1384 pub fn token_owners(1385 collection_id: CollectionId,1386 token: TokenId,1387 ) -> Option<Vec<T::CrossAccountId>> {1388 let res: Vec<T::CrossAccountId> = <Balance<T>>::iter_prefix((collection_id, token))1389 .map(|(owner, _amount)| owner)1390 .take(10)1391 .collect();13921393 if res.is_empty() {1394 None1395 } else {1396 Some(res)1397 }1398 }13991400 /// Sets or unsets the approval of a given operator.1401 ///1402 /// The `operator` is allowed to transfer all token pieces of the `owner` on their behalf.1403 /// - `owner`: Token owner1404 /// - `operator`: Operator1405 /// - `approve`: Should operator status be granted or revoked?1406 pub fn set_allowance_for_all(1407 collection: &RefungibleHandle<T>,1408 owner: &T::CrossAccountId,1409 spender: &T::CrossAccountId,1410 approve: bool,1411 ) -> DispatchResult {1412 <PalletCommon<T>>::set_allowance_for_all(1413 collection,1414 owner,1415 spender,1416 approve,1417 || <CollectionAllowance<T>>::insert((collection.id, owner, spender), approve),1418 ERC721Events::ApprovalForAll {1419 owner: *owner.as_eth(),1420 operator: *spender.as_eth(),1421 approved: approve,1422 }1423 .to_log(collection_id_to_address(collection.id)),1424 )1425 }14261427 /// Tells whether the given `owner` approves the `operator`.1428 pub fn allowance_for_all(1429 collection: &RefungibleHandle<T>,1430 owner: &T::CrossAccountId,1431 spender: &T::CrossAccountId,1432 ) -> bool {1433 <CollectionAllowance<T>>::get((collection.id, owner, spender))1434 }14351436 pub fn repair_item(collection: &RefungibleHandle<T>, token: TokenId) -> DispatchResult {1437 <TokenProperties<T>>::mutate((collection.id, token), |properties| {1438 properties.recompute_consumed_space();1439 });14401441 Ok(())1442 }1443}pallets/scheduler-v2/src/lib.rsdiffbeforeafterboth--- a/pallets/scheduler-v2/src/lib.rs
+++ b/pallets/scheduler-v2/src/lib.rs
@@ -969,7 +969,7 @@
call: ScheduledCall<T>,
) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {
// ensure id it is unique
- if Lookup::<T>::contains_key(&id) {
+ if Lookup::<T>::contains_key(id) {
return Err(Error::<T>::FailedToSchedule.into());
}
pallets/structure/src/lib.rsdiffbeforeafterboth--- a/pallets/structure/src/lib.rs
+++ b/pallets/structure/src/lib.rs
@@ -280,7 +280,7 @@
) -> DispatchResultWithPostInfo {
let dispatch = T::CollectionDispatch::dispatch(collection)?;
let dispatch = dispatch.as_dyn();
- dispatch.burn_item_recursively(from.clone(), token, self_budget, breadth_budget)
+ dispatch.burn_item_recursively(from, token, self_budget, breadth_budget)
}
/// Check if `token` indirectly owned by `user`
@@ -396,7 +396,7 @@
account: &T::CrossAccountId,
action: impl FnOnce(&dyn CommonCollectionOperations<T>, TokenId) -> DispatchResult,
) -> DispatchResult {
- if is_collection(&account.as_eth()) {
+ if is_collection(account.as_eth()) {
fail!(<Error<T>>::CantNestTokenUnderCollection);
}
let Some((collection, token)) = T::CrossTokenAddressMapping::address_to_token(account) else {
pallets/unique/src/eth/mod.rsdiffbeforeafterboth--- a/pallets/unique/src/eth/mod.rs
+++ b/pallets/unique/src/eth/mod.rs
@@ -113,13 +113,9 @@
let collection_helpers_address =
T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());
- let collection_id = T::CollectionDispatch::create(
- caller.clone(),
- collection_helpers_address,
- data,
- Default::default(),
- )
- .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;
+ let collection_id =
+ T::CollectionDispatch::create(caller, collection_helpers_address, data, Default::default())
+ .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;
let address = pallet_common::eth::collection_id_to_address(collection_id);
Ok(address)
}
@@ -132,8 +128,7 @@
.expect("Collection creation price should be convertible to u128");
if value != creation_price {
return Err(format!(
- "Sent amount not equals to collection creation price ({0})",
- creation_price
+ "Sent amount not equals to collection creation price ({creation_price})",
)
.into());
}
@@ -383,8 +378,7 @@
map_eth_to_id(&collection_address)
.map(|id| id.0)
.ok_or(Error::Revert(format!(
- "failed to convert address {} into collectionId.",
- collection_address
+ "failed to convert address {collection_address} into collectionId."
)))
}
}
@@ -422,5 +416,5 @@
generate_stubgen!(collection_helper_iface, CollectionHelpersCall<()>, false);
fn error_field_too_long(feild: &str, bound: usize) -> Error {
- Error::Revert(format!("{} is too long. Max length is {}.", feild, bound))
+ Error::Revert(format!("{feild} is too long. Max length is {bound}."))
}
pallets/unique/src/lib.rsdiffbeforeafterboth--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -507,7 +507,7 @@
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
let new_owner = T::CrossAccountId::from_sub(new_owner);
let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;
- target_collection.change_owner(sender, new_owner.clone())
+ target_collection.change_owner(sender, new_owner)
}
/// Add an admin to a collection.
@@ -667,7 +667,7 @@
/// * `owner`: Address of the initial owner of the item.
/// * `data`: Token data describing the item to store on chain.
#[pallet::call_index(11)]
- #[pallet::weight(T::CommonWeightInfo::create_item(&data))]
+ #[pallet::weight(T::CommonWeightInfo::create_item(data))]
pub fn create_item(
origin: OriginFor<T>,
collection_id: CollectionId,
@@ -701,7 +701,7 @@
/// * `owner`: Address of the initial owner of the tokens.
/// * `items_data`: Vector of data describing each item to be created.
#[pallet::call_index(12)]
- #[pallet::weight(T::CommonWeightInfo::create_multiple_items(&items_data))]
+ #[pallet::weight(T::CommonWeightInfo::create_multiple_items(items_data))]
pub fn create_multiple_items(
origin: OriginFor<T>,
collection_id: CollectionId,
@@ -889,7 +889,7 @@
/// * `collection_id`: ID of the collection to which the tokens would belong.
/// * `data`: Explicit item creation data.
#[pallet::call_index(18)]
- #[pallet::weight(T::CommonWeightInfo::create_multiple_items_ex(&data))]
+ #[pallet::weight(T::CommonWeightInfo::create_multiple_items_ex(data))]
pub fn create_multiple_items_ex(
origin: OriginFor<T>,
collection_id: CollectionId,
@@ -1313,7 +1313,7 @@
collection_id: CollectionId,
) -> DispatchResult {
let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;
- target_collection.force_set_sponsor(sponsor.clone())
+ target_collection.force_set_sponsor(sponsor)
}
/// Force remove `sponsor` for `collection`.
primitives/data-structs/src/bounded.rsdiffbeforeafterboth--- a/primitives/data-structs/src/bounded.rs
+++ b/primitives/data-structs/src/bounded.rs
@@ -63,7 +63,7 @@
V: fmt::Debug,
{
use core::fmt::Debug;
- (&v as &Vec<V>).fmt(f)
+ (v as &Vec<V>).fmt(f)
}
#[cfg(feature = "serde1")]
@@ -114,7 +114,7 @@
V: fmt::Debug,
{
use core::fmt::Debug;
- (&v as &BTreeMap<K, V>).fmt(f)
+ (v as &BTreeMap<K, V>).fmt(f)
}
#[cfg(feature = "serde1")]
@@ -157,5 +157,5 @@
K: fmt::Debug + Ord,
{
use core::fmt::Debug;
- (&v as &BTreeSet<K>).fmt(f)
+ (v as &BTreeSet<K>).fmt(f)
}
primitives/data-structs/src/lib.rsdiffbeforeafterboth--- a/primitives/data-structs/src/lib.rs
+++ b/primitives/data-structs/src/lib.rs
@@ -536,7 +536,7 @@
type Target = Vec<u8>;
fn deref(&self) -> &Self::Target {
- return &self.0;
+ &self.0
}
}
@@ -816,6 +816,11 @@
Self(Default::default())
}
}
+impl Default for OwnerRestrictedSet {
+ fn default() -> Self {
+ Self::new()
+ }
+}
impl core::ops::Deref for OwnerRestrictedSet {
type Target = OwnerRestrictedSetInner;
fn deref(&self) -> &Self::Target {
@@ -1098,9 +1103,9 @@
pub value: PropertyValue,
}
-impl Into<(PropertyKey, PropertyValue)> for Property {
- fn into(self) -> (PropertyKey, PropertyValue) {
- (self.key, self.value)
+impl From<Property> for (PropertyKey, PropertyValue) {
+ fn from(value: Property) -> Self {
+ (value.key, value.value)
}
}
@@ -1116,9 +1121,9 @@
pub permission: PropertyPermission,
}
-impl Into<(PropertyKey, PropertyPermission)> for PropertyKeyPermission {
- fn into(self) -> (PropertyKey, PropertyPermission) {
- (self.key, self.permission)
+impl From<PropertyKeyPermission> for (PropertyKey, PropertyPermission) {
+ fn from(value: PropertyKeyPermission) -> Self {
+ (value.key, value.permission)
}
}
@@ -1415,7 +1420,7 @@
value: Self::Value,
) -> Result<Option<Self::Value>, PropertiesError> {
let key_size = scoped_slice_size(scope, &key);
- let value_size = slice_size(&value) as u32;
+ let value_size = slice_size(&value);
if self.consumed_space + value_size + key_size > S && !cfg!(feature = "runtime-benchmarks")
{
@@ -1425,7 +1430,7 @@
let old_value = self.map.try_scoped_set(scope, key, value)?;
if let Some(old_value) = old_value.as_ref() {
- let old_value_size = slice_size(&old_value);
+ let old_value_size = slice_size(old_value);
self.consumed_space = self.consumed_space.saturating_sub(old_value_size) + value_size;
} else {
self.consumed_space += key_size + value_size;
runtime/common/config/xcm/foreignassets.rsdiffbeforeafterboth--- a/runtime/common/config/xcm/foreignassets.rs
+++ b/runtime/common/config/xcm/foreignassets.rs
@@ -65,7 +65,7 @@
return ConvertAssetId::convert_ref(AssetIds::NativeAssetId(NativeCurrency::Here));
}
- match XcmForeignAssetIdMapping::<Runtime>::get_currency_id(id.clone()) {
+ match XcmForeignAssetIdMapping::<Runtime>::get_currency_id(*id) {
Some(AssetIds::ForeignAssetId(foreign_asset_id)) => {
ConvertAssetId::convert_ref(AssetIds::ForeignAssetId(foreign_asset_id))
}
@@ -206,9 +206,7 @@
return Some(AssetIds::NativeAssetId(NativeCurrency::Parent));
}
- if let Some(currency_id) =
- XcmForeignAssetIdMapping::<Runtime>::get_currency_id(location.clone())
- {
+ if let Some(currency_id) = XcmForeignAssetIdMapping::<Runtime>::get_currency_id(location) {
return Some(currency_id);
}
runtime/common/ethereum/precompiles/mod.rsdiffbeforeafterboth--- a/runtime/common/ethereum/precompiles/mod.rs
+++ b/runtime/common/ethereum/precompiles/mod.rs
@@ -37,6 +37,16 @@
[hash(1), hash(20482)]
}
}
+
+impl<R> Default for UniquePrecompiles<R>
+where
+ R: pallet_evm::Config,
+{
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
impl<R> PrecompileSet for UniquePrecompiles<R>
where
R: pallet_evm::Config,
runtime/common/ethereum/precompiles/sr25519.rsdiffbeforeafterboth--- a/runtime/common/ethereum/precompiles/sr25519.rs
+++ b/runtime/common/ethereum/precompiles/sr25519.rs
@@ -64,7 +64,7 @@
// Parse arguments
let public: sr25519::Public =
- sr25519::Public::unchecked_from(input.read::<H256>(gasometer)?).into();
+ sr25519::Public::unchecked_from(input.read::<H256>(gasometer)?);
let signature_bytes: Vec<u8> = input.read::<Bytes>(gasometer)?.into();
let message: Vec<u8> = input.read::<Bytes>(gasometer)?.into();
runtime/common/ethereum/precompiles/utils/data.rsdiffbeforeafterboth--- a/runtime/common/ethereum/precompiles/utils/data.rs
+++ b/runtime/common/ethereum/precompiles/utils/data.rs
@@ -60,7 +60,7 @@
}
impl Into<Vec<u8>> for Bytes {
- fn into(self: Self) -> Vec<u8> {
+ fn into(self) -> Vec<u8> {
self.0
}
}
runtime/common/ethereum/precompiles/utils/mod.rsdiffbeforeafterboth--- a/runtime/common/ethereum/precompiles/utils/mod.rs
+++ b/runtime/common/ethereum/precompiles/utils/mod.rs
@@ -73,7 +73,6 @@
}
}
- #[must_use]
/// Check that a function call is compatible with the context it is
/// called into.
pub fn check_function_modifier(
runtime/common/ethereum/sponsoring.rsdiffbeforeafterboth--- a/runtime/common/ethereum/sponsoring.rs
+++ b/runtime/common/ethereum/sponsoring.rs
@@ -78,7 +78,7 @@
let token_id: TokenId = token_id.try_into().ok()?;
withdraw_set_token_property::<T>(
&collection,
- &who,
+ who,
&token_id,
key.len() + value.len(),
)
@@ -88,7 +88,7 @@
ERC721UniqueExtensionsCall::Transfer { token_id, .. },
) => {
let token_id: TokenId = token_id.try_into().ok()?;
- withdraw_transfer::<T>(&collection, &who, &token_id).map(|()| sponsor)
+ withdraw_transfer::<T>(&collection, who, &token_id).map(|()| sponsor)
}
UniqueNFTCall::ERC721UniqueMintable(
ERC721UniqueMintableCall::Mint { .. }
@@ -97,7 +97,7 @@
| ERC721UniqueMintableCall::MintWithTokenUriCheckId { .. },
) => withdraw_create_item::<T>(
&collection,
- &who,
+ who,
&CreateItemData::NFT(CreateNftData::default()),
)
.map(|()| sponsor),
runtime/common/ethereum/sponsoring/refungible.rsdiffbeforeafterboth--- a/runtime/common/ethereum/sponsoring/refungible.rs
+++ b/runtime/common/ethereum/sponsoring/refungible.rs
@@ -16,7 +16,6 @@
//! Implements EVM sponsoring logic via TransactionValidityHack
-use core::convert::TryInto;
use pallet_common::CollectionHandle;
use pallet_evm::account::CrossAccountId;
use pallet_fungible::Config as FungibleConfig;
@@ -95,7 +94,7 @@
..
} => {
let token_id = TokenId::try_from(token_id).ok()?;
- withdraw_set_token_property::<T>(&collection, &who, &token_id, key.len() + value.len())
+ withdraw_set_token_property::<T>(&collection, who, &token_id, key.len() + value.len())
}
}
}
@@ -242,7 +241,7 @@
MintCross { .. } => withdraw_create_item::<T>(
&collection,
- &who,
+ who,
&CreateItemData::NFT(CreateNftData::default()),
),
@@ -250,7 +249,7 @@
| TransferFromCross { token_id, .. }
| Transfer { token_id, .. } => {
let token_id = TokenId::try_from(token_id).ok()?;
- withdraw_transfer::<T>(&collection, &who, &token_id)
+ withdraw_transfer::<T>(&collection, who, &token_id)
}
}
}
@@ -275,7 +274,7 @@
| MintWithTokenUri { .. }
| MintWithTokenUriCheckId { .. } => withdraw_create_item::<T>(
&collection,
- &who,
+ who,
&CreateItemData::NFT(CreateNftData::default()),
),
}
@@ -311,18 +310,15 @@
Transfer { .. } => {
let RefungibleTokenHandle(handle, token_id) = token;
- let token_id = token_id.try_into().ok()?;
- withdraw_transfer::<T>(&handle, &who, &token_id)
+ withdraw_transfer::<T>(&handle, who, &token_id)
}
TransferFrom { from, .. } => {
let RefungibleTokenHandle(handle, token_id) = token;
- let token_id = token_id.try_into().ok()?;
let from = T::CrossAccountId::from_eth(from);
withdraw_transfer::<T>(&handle, &from, &token_id)
}
Approve { .. } => {
let RefungibleTokenHandle(handle, token_id) = token;
- let token_id = token_id.try_into().ok()?;
withdraw_approve::<T>(&handle, who.as_sub(), &token_id)
}
}
@@ -351,13 +347,11 @@
TransferCross { .. } | TransferFromCross { .. } => {
let RefungibleTokenHandle(handle, token_id) = token;
- let token_id = token_id.try_into().ok()?;
- withdraw_transfer::<T>(&handle, &who, &token_id)
+ withdraw_transfer::<T>(&handle, who, &token_id)
}
ApproveCross { .. } => {
let RefungibleTokenHandle(handle, token_id) = token;
- let token_id = token_id.try_into().ok()?;
withdraw_approve::<T>(&handle, who.as_sub(), &token_id)
}
}
runtime/common/mod.rsdiffbeforeafterboth--- a/runtime/common/mod.rs
+++ b/runtime/common/mod.rs
@@ -204,10 +204,7 @@
&[],
);
- let should_upgrade = match version {
- None => true,
- Some(_) => false,
- };
+ let should_upgrade = version.is_none();
if should_upgrade {
log::info!(
@@ -220,7 +217,7 @@
.cloned()
.filter_map(|authority_id| {
weight.saturating_accrue(<Runtime as frame_system::Config>::DbWeight::get().reads_writes(1, 1));
- let vec = authority_id.clone().to_raw_vec();
+ let vec = authority_id.to_raw_vec();
let slice = vec.as_slice();
let array: Option<[u8; 32]> = match slice.try_into() {
Ok(a) => Some(a),
@@ -248,20 +245,20 @@
.into_iter()
.map(|(acc, aura)| {
(
- acc.clone(), // account id
- acc, // validator id
- SessionKeys { aura: aura.clone() }, // session keys
+ acc.clone(), // account id
+ acc, // validator id
+ SessionKeys { aura }, // session keys
)
})
.collect::<Vec<_>>();
- for (account, val, keys) in keys.iter().cloned() {
+ for (account, val, keys) in keys.iter() {
for id in <Runtime as pallet_session::Config>::Keys::key_ids() {
- <pallet_session::KeyOwner<Runtime>>::insert((*id, keys.get_raw(*id)), &val)
+ <pallet_session::KeyOwner<Runtime>>::insert((*id, keys.get_raw(*id)), val)
}
- <pallet_session::NextKeys<Runtime>>::insert(&val, &keys);
+ <pallet_session::NextKeys<Runtime>>::insert(val, keys);
// todo exercise caution, the following is taken from genesis
- if frame_system::Pallet::<Runtime>::inc_consumers_without_limit(&account)
+ if frame_system::Pallet::<Runtime>::inc_consumers_without_limit(account)
.is_err()
{
log::warn!(
@@ -271,7 +268,7 @@
// genesis) so it's really not a big deal and we assume that the user wants to
// do this since it's the only way a non-endowed account can contain a session
// key.
- frame_system::Pallet::<Runtime>::inc_providers(&account);
+ frame_system::Pallet::<Runtime>::inc_providers(account);
}
}
runtime/common/runtime_apis.rsdiffbeforeafterboth--- a/runtime/common/runtime_apis.rs
+++ b/runtime/common/runtime_apis.rs
@@ -84,7 +84,7 @@
fn topmost_token_owner(collection: CollectionId, token: TokenId) -> Result<Option<CrossAccountId>, DispatchError> {
let budget = up_data_structs::budget::Value::new(10);
- Ok(<pallet_structure::Pallet<Runtime>>::find_topmost_owner(collection, token, &budget)?)
+ <pallet_structure::Pallet<Runtime>>::find_topmost_owner(collection, token, &budget)
}
fn token_children(collection: CollectionId, token: TokenId) -> Result<Vec<TokenChild>, DispatchError> {
Ok(<pallet_nonfungible::Pallet<Runtime>>::token_children_ids(collection, token))
runtime/common/sponsoring.rsdiffbeforeafterboth--- a/runtime/common/sponsoring.rs
+++ b/runtime/common/sponsoring.rs
@@ -240,7 +240,7 @@
withdraw_set_token_property(
&collection,
&T::CrossAccountId::from_sub(who.clone()),
- &token_id,
+ token_id,
// No overflow may happen, as data larger than usize can't reach here
properties.iter().map(|p| p.key.len() + p.value.len()).sum(),
)
test-pallets/utils/src/lib.rsdiffbeforeafterboth--- a/test-pallets/utils/src/lib.rs
+++ b/test-pallets/utils/src/lib.rs
@@ -170,7 +170,7 @@
fn ensure_origin_and_enabled(origin: OriginFor<T>) -> DispatchResult {
ensure_signed(origin)?;
<Enabled<T>>::get()
- .then(|| ())
+ .then_some(())
.ok_or(<Error<T>>::TestPalletDisabled.into())
}
}