Skip to content

Commit

Permalink
feat: psp22 standard for decrease_allowance and burn pallet metho…
Browse files Browse the repository at this point in the history
…ds (#322)
  • Loading branch information
chungquantin authored Oct 2, 2024
1 parent 5083a46 commit dcd465d
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 58 deletions.
17 changes: 12 additions & 5 deletions pallets/api/src/fungibles/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type AccountIdOf<T> = <T as frame_system::Config>::AccountId;
type TokenIdOf<T> = <AssetsOf<T> as Inspect<<T as frame_system::Config>::AccountId>>::AssetId;
type TokenIdParameterOf<T> = <T as pallet_assets::Config<AssetsInstanceOf<T>>>::AssetIdParameter;
type AssetsOf<T> = pallet_assets::Pallet<T, AssetsInstanceOf<T>>;
type AssetsErrorOf<T> = pallet_assets::Error<T, AssetsInstanceOf<T>>;
type AssetsInstanceOf<T> = <T as Config>::AssetsInstance;
type AssetsWeightInfoOf<T> = <T as pallet_assets::Config<AssetsInstanceOf<T>>>::WeightInfo;
type BalanceOf<T> = <AssetsOf<T> as Inspect<<T as frame_system::Config>::AccountId>>::Balance;
Expand All @@ -33,7 +34,7 @@ pub mod pallet {
};
use frame_system::pallet_prelude::*;
use sp_runtime::{
traits::{StaticLookup, Zero},
traits::{CheckedSub, StaticLookup, Zero},
Saturating,
};
use sp_std::vec::Vec;
Expand Down Expand Up @@ -338,13 +339,14 @@ pub mod pallet {
let token_param: TokenIdParameterOf<T> = token.clone().into();

// Cancel the approval and approve `new_allowance` if difference is more than zero.
let new_allowance =
current_allowance.checked_sub(&value).ok_or(AssetsErrorOf::<T>::Unapproved)?;
AssetsOf::<T>::cancel_approval(
origin.clone(),
token_param.clone(),
spender_source.clone(),
)
.map_err(|e| e.with_weight(WeightOf::<T>::approve(0, 1)))?;
let new_allowance = current_allowance.saturating_sub(value);
let weight = if new_allowance.is_zero() {
WeightOf::<T>::approve(0, 1)
} else {
Expand Down Expand Up @@ -460,21 +462,26 @@ pub mod pallet {
/// - `account` - The account from which the tokens will be destroyed.
/// - `value` - The number of tokens to destroy.
#[pallet::call_index(20)]
#[pallet::weight(AssetsWeightInfoOf::<T>::burn())]
#[pallet::weight(<T as Config>::WeightInfo::balance_of() + AssetsWeightInfoOf::<T>::burn())]
pub fn burn(
origin: OriginFor<T>,
token: TokenIdOf<T>,
account: AccountIdOf<T>,
value: BalanceOf<T>,
) -> DispatchResult {
) -> DispatchResultWithPostInfo {
let current_balance = AssetsOf::<T>::balance(token.clone(), &account);
if current_balance < value {
return Err(AssetsErrorOf::<T>::BalanceLow
.with_weight(<T as Config>::WeightInfo::balance_of()));
}
AssetsOf::<T>::burn(
origin,
token.clone().into(),
T::Lookup::unlookup(account.clone()),
value,
)?;
Self::deposit_event(Event::Transfer { token, from: Some(account), to: None, value });
Ok(())
Ok(().into())
}
}

Expand Down
41 changes: 27 additions & 14 deletions pallets/api/src/fungibles/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,20 +272,28 @@ fn decrease_allowance_works() {
BadOrigin.with_weight(WeightInfo::approve(0, 0))
);
}
assets::create_mint_and_approve(owner, token, owner, value, spender, value);
assert_eq!(Assets::allowance(token, &owner, &spender), value);
// Check error works for `Assets::cancel_approval()`. No error test for `approve_transfer`
// because it is not possible.
assert_ok!(Assets::freeze_asset(signed(owner), token));
assert_noop!(
Fungibles::decrease_allowance(signed(owner), token, spender, value / 2),
AssetsError::Unknown.with_weight(WeightInfo::approve(0, 1))
AssetsError::AssetNotLive.with_weight(WeightInfo::approve(0, 1))
);
assets::create_mint_and_approve(owner, token, owner, value, spender, value);
assert_eq!(Assets::allowance(token, &owner, &spender), value);
assert_ok!(Assets::thaw_asset(signed(owner), token));
// Owner balance is not changed if decreased by zero.
assert_eq!(
Fungibles::decrease_allowance(signed(owner), token, spender, 0),
Ok(Some(WeightInfo::approve(0, 0)).into())
);
assert_eq!(Assets::allowance(token, &owner, &spender), value);
// "Unapproved" error is returned if the current allowance is less than amount to decrease
// with.
assert_noop!(
Fungibles::decrease_allowance(signed(owner), token, spender, value * 2),
AssetsError::Unapproved
);
// Decrease allowance successfully.
assert_eq!(
Fungibles::decrease_allowance(signed(owner), token, spender, value / 2),
Expand All @@ -295,13 +303,6 @@ fn decrease_allowance_works() {
System::assert_last_event(
Event::Approval { token, owner, spender, value: value / 2 }.into(),
);
// Saturating if current allowance is decreased more than the owner balance.
assert_eq!(
Fungibles::decrease_allowance(signed(owner), token, spender, value),
Ok(Some(WeightInfo::approve(0, 1)).into())
);
assert_eq!(Assets::allowance(token, &owner, &spender), 0);
System::assert_last_event(Event::Approval { token, owner, spender, value: 0 }.into());
});
}

Expand Down Expand Up @@ -416,10 +417,22 @@ fn burn_works() {
let from = BOB;
let total_supply = value * 2;

// Check error works for `Assets::burn()`.
assert_noop!(Fungibles::burn(signed(owner), token, from, value), AssetsError::Unknown);
// "BalanceLow" error is returned if token is not created.
assert_noop!(
Fungibles::burn(signed(owner), token, from, value),
AssetsError::BalanceLow.with_weight(WeightInfo::balance_of())
);
assets::create_and_mint_to(owner, token, from, total_supply);
assert_eq!(Assets::total_supply(TOKEN), total_supply);
// Check error works for `Assets::burn()`.
assert_ok!(Assets::freeze_asset(signed(owner), token));
assert_noop!(Fungibles::burn(signed(owner), token, from, value), AssetsError::AssetNotLive);
assert_ok!(Assets::thaw_asset(signed(owner), token));
// "BalanceLow" error is returned if the balance is less than amount to burn.
assert_noop!(
Fungibles::burn(signed(owner), token, from, total_supply * 2),
AssetsError::BalanceLow.with_weight(WeightInfo::balance_of())
);
let balance_before_burn = Assets::balance(token, &from);
assert_ok!(Fungibles::burn(signed(owner), token, from, value));
assert_eq!(Assets::total_supply(TOKEN), total_supply - value);
Expand Down Expand Up @@ -677,8 +690,8 @@ mod read_weights {
}

mod ensure_codec_indexes {
use super::{Encode, RuntimeCall, *};
use crate::{fungibles, fungibles::Call::*, mock::RuntimeCall::Fungibles};
use super::{Encode, *};
use crate::{fungibles, mock::RuntimeCall::Fungibles};

#[test]
fn ensure_read_variant_indexes() {
Expand Down
55 changes: 27 additions & 28 deletions pallets/api/src/fungibles/weights.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//! Autogenerated weights for `fungibles`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 40.0.0
//! DATE: 2024-09-13, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! DATE: 2024-10-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `R0GUE`, CPU: `<UNKNOWN>`
//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024`
Expand Down Expand Up @@ -57,12 +57,12 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `413 + c * (102 ±0)`
// Estimated: `3675`
// Minimum execution time: 20_000_000 picoseconds.
Weight::from_parts(1_473_469, 3675)
// Standard Error: 89_329
.saturating_add(Weight::from_parts(19_606_122, 0).saturating_mul(a.into()))
// Standard Error: 89_329
.saturating_add(Weight::from_parts(30_920_408, 0).saturating_mul(c.into()))
// Minimum execution time: 26_000_000 picoseconds.
Weight::from_parts(2_877_551, 3675)
// Standard Error: 100_168
.saturating_add(Weight::from_parts(24_846_938, 0).saturating_mul(a.into()))
// Standard Error: 100_168
.saturating_add(Weight::from_parts(38_375_510, 0).saturating_mul(c.into()))
.saturating_add(T::DbWeight::get().reads(2_u64))
.saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(c.into())))
.saturating_add(T::DbWeight::get().writes(2_u64))
Expand All @@ -74,7 +74,7 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `6`
// Estimated: `3675`
// Minimum execution time: 1_000_000 picoseconds.
// Minimum execution time: 2_000_000 picoseconds.
Weight::from_parts(2_000_000, 3675)
.saturating_add(T::DbWeight::get().reads(1_u64))
}
Expand All @@ -84,7 +84,7 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `6`
// Estimated: `3599`
// Minimum execution time: 2_000_000 picoseconds.
// Minimum execution time: 3_000_000 picoseconds.
Weight::from_parts(3_000_000, 3599)
.saturating_add(T::DbWeight::get().reads(1_u64))
}
Expand All @@ -94,8 +94,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `6`
// Estimated: `3613`
// Minimum execution time: 4_000_000 picoseconds.
Weight::from_parts(4_000_000, 3613)
// Minimum execution time: 5_000_000 picoseconds.
Weight::from_parts(5_000_000, 3613)
.saturating_add(T::DbWeight::get().reads(1_u64))
}
/// Storage: `Assets::Metadata` (r:1 w:0)
Expand All @@ -104,7 +104,7 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `6`
// Estimated: `3605`
// Minimum execution time: 1_000_000 picoseconds.
// Minimum execution time: 2_000_000 picoseconds.
Weight::from_parts(2_000_000, 3605)
.saturating_add(T::DbWeight::get().reads(1_u64))
}
Expand All @@ -124,7 +124,7 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `6`
// Estimated: `3605`
// Minimum execution time: 1_000_000 picoseconds.
// Minimum execution time: 2_000_000 picoseconds.
Weight::from_parts(2_000_000, 3605)
.saturating_add(T::DbWeight::get().reads(1_u64))
}
Expand All @@ -134,7 +134,7 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Proof Size summary in bytes:
// Measured: `6`
// Estimated: `3675`
// Minimum execution time: 1_000_000 picoseconds.
// Minimum execution time: 2_000_000 picoseconds.
Weight::from_parts(2_000_000, 3675)
.saturating_add(T::DbWeight::get().reads(1_u64))
}
Expand All @@ -154,12 +154,12 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `413 + c * (102 ±0)`
// Estimated: `3675`
// Minimum execution time: 20_000_000 picoseconds.
Weight::from_parts(1_473_469, 3675)
// Standard Error: 89_329
.saturating_add(Weight::from_parts(19_606_122, 0).saturating_mul(a.into()))
// Standard Error: 89_329
.saturating_add(Weight::from_parts(30_920_408, 0).saturating_mul(c.into()))
// Minimum execution time: 26_000_000 picoseconds.
Weight::from_parts(2_877_551, 3675)
// Standard Error: 100_168
.saturating_add(Weight::from_parts(24_846_938, 0).saturating_mul(a.into()))
// Standard Error: 100_168
.saturating_add(Weight::from_parts(38_375_510, 0).saturating_mul(c.into()))
.saturating_add(RocksDbWeight::get().reads(2_u64))
.saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(c.into())))
.saturating_add(RocksDbWeight::get().writes(2_u64))
Expand All @@ -171,7 +171,7 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `6`
// Estimated: `3675`
// Minimum execution time: 1_000_000 picoseconds.
// Minimum execution time: 2_000_000 picoseconds.
Weight::from_parts(2_000_000, 3675)
.saturating_add(RocksDbWeight::get().reads(1_u64))
}
Expand All @@ -181,7 +181,7 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `6`
// Estimated: `3599`
// Minimum execution time: 2_000_000 picoseconds.
// Minimum execution time: 3_000_000 picoseconds.
Weight::from_parts(3_000_000, 3599)
.saturating_add(RocksDbWeight::get().reads(1_u64))
}
Expand All @@ -191,8 +191,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `6`
// Estimated: `3613`
// Minimum execution time: 4_000_000 picoseconds.
Weight::from_parts(4_000_000, 3613)
// Minimum execution time: 5_000_000 picoseconds.
Weight::from_parts(5_000_000, 3613)
.saturating_add(RocksDbWeight::get().reads(1_u64))
}
/// Storage: `Assets::Metadata` (r:1 w:0)
Expand All @@ -201,7 +201,7 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `6`
// Estimated: `3605`
// Minimum execution time: 1_000_000 picoseconds.
// Minimum execution time: 2_000_000 picoseconds.
Weight::from_parts(2_000_000, 3605)
.saturating_add(RocksDbWeight::get().reads(1_u64))
}
Expand All @@ -221,7 +221,7 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `6`
// Estimated: `3605`
// Minimum execution time: 1_000_000 picoseconds.
// Minimum execution time: 2_000_000 picoseconds.
Weight::from_parts(2_000_000, 3605)
.saturating_add(RocksDbWeight::get().reads(1_u64))
}
Expand All @@ -231,9 +231,8 @@ impl WeightInfo for () {
// Proof Size summary in bytes:
// Measured: `6`
// Estimated: `3675`
// Minimum execution time: 1_000_000 picoseconds.
// Minimum execution time: 2_000_000 picoseconds.
Weight::from_parts(2_000_000, 3675)
.saturating_add(RocksDbWeight::get().reads(1_u64))
}
}

22 changes: 11 additions & 11 deletions pop-api/integration-tests/src/fungibles/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,11 +253,6 @@ fn decrease_allowance_works() {
let addr = instantiate(CONTRACT, INIT_VALUE, vec![]);
let amount: Balance = 100 * UNIT;

// Token does not exist.
assert_eq!(
decrease_allowance(&addr, 0, &BOB, amount),
Err(Module { index: 52, error: [3, 0] }),
);
// Create token and mint `amount` to contract address, then approve Bob to spend `amount`.
let token = assets::create_mint_and_approve(&addr, 0, &addr, amount, &BOB, amount);
// Token is not live, i.e. frozen or being destroyed.
Expand All @@ -267,15 +262,20 @@ fn decrease_allowance_works() {
Err(Module { index: 52, error: [16, 0] }),
);
assets::thaw(&addr, token);
// "Unapproved" error is returned if the current allowance is less than `value`.
assert_eq!(
decrease_allowance(&addr, token, &BOB, amount * 2),
Err(Module { index: 52, error: [10, 0] }),
);
// Successfully decrease allowance.
let allowance_before = Assets::allowance(token, &addr, &BOB);
assert_ok!(decrease_allowance(&addr, 0, &BOB, amount / 2 - 1 * UNIT));
assert_ok!(decrease_allowance(&addr, token, &BOB, amount / 2 - 1 * UNIT));
let allowance_after = Assets::allowance(token, &addr, &BOB);
assert_eq!(allowance_before - allowance_after, amount / 2 - 1 * UNIT);
// Token is not live, i.e. frozen or being destroyed.
assets::start_destroy(&addr, token);
assert_eq!(
decrease_allowance(&addr, token, &BOB, amount),
decrease_allowance(&addr, token, &BOB, 1 * UNIT),
Err(Module { index: 52, error: [16, 0] }),
);
});
Expand Down Expand Up @@ -530,14 +530,14 @@ fn burn_works() {
let amount: Balance = 100 * UNIT;

// Token does not exist.
assert_eq!(burn(&addr, 1, &BOB, amount), Err(Module { index: 52, error: [3, 0] }));
assert_eq!(burn(&addr, 1, &BOB, 0), Err(Module { index: 52, error: [3, 0] }));
let token = assets::create(&ALICE, 1, 1);
// Bob has no tokens and therefore doesn't exist.
assert_eq!(burn(&addr, token, &BOB, 1), Err(Module { index: 52, error: [1, 0] }));
// Bob has no tokens.
assert_eq!(burn(&addr, token, &BOB, amount), Err(Module { index: 52, error: [0, 0] }));
// Burning can only be done by the manager.
assets::mint(&ALICE, token, &BOB, amount);
assert_eq!(burn(&addr, token, &BOB, 1), Err(Module { index: 52, error: [2, 0] }));
let token = assets::create_and_mint_to(&addr, 2, &BOB, amount);
let token = assets::create_and_mint_to(&addr, 2, &BOB, amount * 2);
// Token is not live, i.e. frozen or being destroyed.
assets::freeze(&addr, token);
assert_eq!(burn(&addr, token, &BOB, amount), Err(Module { index: 52, error: [16, 0] }));
Expand Down

0 comments on commit dcd465d

Please sign in to comment.