diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index 1001663b..3b089a55 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -83,6 +83,8 @@ pub mod pallet { /// Configure the pallet by specifying the parameters and types on which it depends. #[pallet::config] pub trait Config: frame_system::Config + pallet_assets::Config { + /// Because this pallet emits events, it depends on the runtime's definition of an event. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; /// The instance of pallet assets it is tightly coupled to. type AssetsInstance; /// Weight information for dispatchables in this pallet. @@ -92,6 +94,49 @@ pub mod pallet { #[pallet::pallet] pub struct Pallet(_); + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// Event emitted when allowance by `owner` to `spender` changes. + Approval { + /// Account providing allowance. + owner: AccountIdOf, + /// Allowance beneficiary. + spender: AccountIdOf, + /// New allowance amount. + value: BalanceOf, + }, + /// Event emitted when transfer of tokens occurs. + Transfer { + /// Transfer sender. `None` in case of minting new tokens. + from: Option>, + /// Transfer recipient. `None` in case of burning tokens. + to: Option>, + /// Amount of tokens transferred (or minted/burned). + value: BalanceOf, + }, + /// Event emitted when a token is created. + Create { + /// Token ID. + id: AssetIdOf, + /// Owner of the token created. + owner: AccountIdOf, + /// Admin of the token created. + admin: AccountIdOf, + }, + /// Event emitted when new token metadata is set. + SetMetadata { + /// Token ID. + id: AssetIdOf, + /// Token name. + name: Vec, + /// Token symbol. + symbol: Vec, + /// Token decimals. + decimals: u8, + }, + } + #[pallet::call] impl Pallet { /// Transfers `value` amount of tokens from the caller's account to account `to`, with additional @@ -106,11 +151,18 @@ pub mod pallet { pub fn transfer( origin: OriginFor, id: AssetIdOf, - target: AccountIdOf, - amount: BalanceOf, + to: AccountIdOf, + value: BalanceOf, ) -> DispatchResult { - let target = T::Lookup::unlookup(target); - AssetsOf::::transfer_keep_alive(origin, id.into(), target, amount) + AssetsOf::::transfer_keep_alive( + origin.clone(), + id.into(), + T::Lookup::unlookup(to.clone()), + value, + )?; + let from = ensure_signed(origin)?; + Self::deposit_event(Event::Transfer { from: Some(from), to: Some(to), value }); + Ok(()) } /// Transfers `value` amount of tokens from the delegated account approved by the `owner` to @@ -118,7 +170,7 @@ pub mod pallet { /// /// # Parameters /// - `id` - The ID of the asset. - /// - `owner` - The account from which the asset balance will be withdrawn. + /// - `from` - The account from which the asset balance will be withdrawn. /// - `to` - The recipient account. /// - `value` - The number of tokens to transfer. #[pallet::call_index(4)] @@ -126,13 +178,19 @@ pub mod pallet { pub fn transfer_from( origin: OriginFor, id: AssetIdOf, - owner: AccountIdOf, - target: AccountIdOf, - amount: BalanceOf, + from: AccountIdOf, + to: AccountIdOf, + value: BalanceOf, ) -> DispatchResult { - let owner = T::Lookup::unlookup(owner); - let target = T::Lookup::unlookup(target); - AssetsOf::::transfer_approved(origin, id.into(), owner, target, amount) + AssetsOf::::transfer_approved( + origin, + id.into(), + T::Lookup::unlookup(from.clone()), + T::Lookup::unlookup(to.clone()), + value, + )?; + Self::deposit_event(Event::Transfer { from: Some(from), to: Some(to), value }); + Ok(()) } /// Approves an account to spend a specified number of tokens on behalf of the caller. @@ -149,10 +207,10 @@ pub mod pallet { spender: AccountIdOf, value: BalanceOf, ) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin.clone()) + let owner = ensure_signed(origin.clone()) .map_err(|e| e.with_weight(Self::weight_approve(0, 0)))?; - let current_allowance = AssetsOf::::allowance(id.clone(), &who, &spender); - let spender = T::Lookup::unlookup(spender); + let current_allowance = AssetsOf::::allowance(id.clone(), &owner, &spender); + let spender_unlookup = T::Lookup::unlookup(spender.clone()); let id: AssetIdParameterOf = id.into(); // If the new value is equal to the current allowance, do nothing. @@ -165,21 +223,26 @@ pub mod pallet { AssetsOf::::approve_transfer( origin, id, - spender, + spender_unlookup, value.saturating_sub(current_allowance), ) .map_err(|e| e.with_weight(Self::weight_approve(1, 0)))?; Self::weight_approve(1, 0) } else { // If the new value is less than the current allowance, cancel the approval and set the new value - AssetsOf::::cancel_approval(origin.clone(), id.clone(), spender.clone()) - .map_err(|e| e.with_weight(Self::weight_approve(0, 1)))?; + AssetsOf::::cancel_approval( + origin.clone(), + id.clone(), + spender_unlookup.clone(), + ) + .map_err(|e| e.with_weight(Self::weight_approve(0, 1)))?; if value.is_zero() { return Ok(Some(Self::weight_approve(0, 1)).into()); } - AssetsOf::::approve_transfer(origin, id, spender, value)?; + AssetsOf::::approve_transfer(origin, id, spender_unlookup, value)?; Self::weight_approve(1, 1) }; + Self::deposit_event(Event::Approval { owner, spender, value }); Ok(Some(return_weight).into()) } @@ -197,8 +260,16 @@ pub mod pallet { spender: AccountIdOf, value: BalanceOf, ) -> DispatchResult { - let spender = T::Lookup::unlookup(spender); - AssetsOf::::approve_transfer(origin, id.into(), spender, value) + let owner = ensure_signed(origin.clone())?; + AssetsOf::::approve_transfer( + origin, + id.clone().into(), + T::Lookup::unlookup(spender.clone()), + value, + )?; + let value = AssetsOf::::allowance(id, &owner, &spender); + Self::deposit_event(Event::Approval { owner, spender, value }); + Ok(()) } /// Decreases the allowance of a spender. @@ -215,24 +286,27 @@ pub mod pallet { spender: AccountIdOf, value: BalanceOf, ) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin.clone()) + let owner = ensure_signed(origin.clone()) .map_err(|e| e.with_weight(Self::weight_approve(0, 0)))?; - let current_allowance = AssetsOf::::allowance(id.clone(), &who, &spender); - let spender = T::Lookup::unlookup(spender); + let current_allowance = AssetsOf::::allowance(id.clone(), &owner, &spender); + let spender_unlookup = T::Lookup::unlookup(spender.clone()); let id: AssetIdParameterOf = id.into(); if value.is_zero() { return Ok(Some(Self::weight_approve(0, 0)).into()); } // Cancel the aproval and set the new value if `current_allowance` is more than zero. - AssetsOf::::cancel_approval(origin.clone(), id.clone(), spender.clone()) + AssetsOf::::cancel_approval(origin.clone(), id.clone(), spender_unlookup.clone()) .map_err(|e| e.with_weight(Self::weight_approve(0, 1)))?; let new_allowance = current_allowance.saturating_sub(value); - if new_allowance.is_zero() { - return Ok(Some(Self::weight_approve(0, 1)).into()); - } - AssetsOf::::approve_transfer(origin, id, spender, new_allowance)?; - Ok(().into()) + let weight = if new_allowance.is_zero() { + Self::weight_approve(0, 1) + } else { + AssetsOf::::approve_transfer(origin, id, spender_unlookup, new_allowance)?; + Self::weight_approve(1, 1) + }; + Self::deposit_event(Event::Approval { owner, spender, value }); + Ok(Some(weight).into()) } /// Create a new token with a given asset ID. @@ -249,8 +323,15 @@ pub mod pallet { admin: AccountIdOf, min_balance: BalanceOf, ) -> DispatchResult { - let admin = T::Lookup::unlookup(admin); - AssetsOf::::create(origin, id.into(), admin, min_balance) + AssetsOf::::create( + origin.clone(), + id.into(), + T::Lookup::unlookup(admin.clone()), + min_balance, + )?; + let owner = ensure_signed(origin)?; + Self::deposit_event(Event::Create { id, owner, admin }); + Ok(()) } /// Start the process of destroying a token with a given asset ID. @@ -281,7 +362,9 @@ pub mod pallet { symbol: Vec, decimals: u8, ) -> DispatchResult { - AssetsOf::::set_metadata(origin, id.into(), name, symbol, decimals) + AssetsOf::::set_metadata(origin, id.into(), name.clone(), symbol.clone(), decimals)?; + Self::deposit_event(Event::SetMetadata { id, name, symbol, decimals }); + Ok(()) } /// Clear the metadata for a token with a given asset ID. @@ -294,11 +377,11 @@ pub mod pallet { AssetsOf::::clear_metadata(origin, id.into()) } - /// Creates `amount` tokens and assigns them to `account`, increasing the total supply. + /// Creates `value` amount of tokens and assigns them to `account`, increasing the total supply. /// /// # Parameters /// - `id` - The ID of the asset. - /// - `owner` - The account to be credited with the created tokens. + /// - `account` - The account to be credited with the created tokens. /// - `value` - The number of tokens to mint. #[pallet::call_index(19)] #[pallet::weight(AssetsWeightInfoOf::::mint())] @@ -306,13 +389,14 @@ pub mod pallet { origin: OriginFor, id: AssetIdOf, account: AccountIdOf, - amount: BalanceOf, + value: BalanceOf, ) -> DispatchResult { - let account = T::Lookup::unlookup(account); - AssetsOf::::mint(origin, id.into(), account, amount) + AssetsOf::::mint(origin, id.into(), T::Lookup::unlookup(account.clone()), value)?; + Self::deposit_event(Event::Transfer { from: None, to: Some(account), value }); + Ok(()) } - /// Destroys `amount` tokens from `account`, reducing the total supply. + /// Destroys `value` tokens from `account`, reducing the total supply. /// /// # Parameters /// - `id` - The ID of the asset. @@ -324,10 +408,11 @@ pub mod pallet { origin: OriginFor, id: AssetIdOf, account: AccountIdOf, - amount: BalanceOf, + value: BalanceOf, ) -> DispatchResult { - let account = T::Lookup::unlookup(account); - AssetsOf::::burn(origin, id.into(), account, amount) + AssetsOf::::burn(origin, id.into(), T::Lookup::unlookup(account.clone()), value)?; + Self::deposit_event(Event::Transfer { from: Some(account), to: None, value }); + Ok(()) } } diff --git a/pallets/api/src/mock.rs b/pallets/api/src/mock.rs index b20e2635..77e17394 100644 --- a/pallets/api/src/mock.rs +++ b/pallets/api/src/mock.rs @@ -97,6 +97,7 @@ impl pallet_assets::Config for Test { type BenchmarkHelper = (); } impl crate::fungibles::Config for Test { + type RuntimeEvent = RuntimeEvent; type AssetsInstance = AssetsInstance; type WeightInfo = (); } diff --git a/pop-api/src/v0/assets/fungibles.rs b/pop-api/src/v0/assets/fungibles.rs index 27b96ae9..47e66b84 100644 --- a/pop-api/src/v0/assets/fungibles.rs +++ b/pop-api/src/v0/assets/fungibles.rs @@ -58,6 +58,36 @@ mod constants { pub(super) const BURN: u8 = 20; } +pub mod events { + use super::*; + + /// Event emitted when allowance by `owner` to `spender` changes. + #[ink::event] + pub struct Approval { + /// Account providing allowance. + #[ink(topic)] + pub owner: AccountId, + /// Allowance beneficiary. + #[ink(topic)] + pub spender: AccountId, + /// New allowance amount. + pub value: u128, + } + + /// Event emitted when transfer of tokens occurs. + #[ink::event] + pub struct Transfer { + /// Transfer sender. `None` in case of minting new tokens. + #[ink(topic)] + pub from: Option, + /// Transfer recipient. `None` in case of burning tokens. + #[ink(topic)] + pub to: Option, + /// Amount of tokens transferred (or minted/burned). + pub value: u128, + } +} + /// Returns the total token supply for a given asset ID. /// /// # Parameters @@ -122,12 +152,12 @@ pub fn allowance(id: AssetId, owner: AccountId, spender: AccountId) -> Result Result<()> { +pub fn transfer(id: AssetId, to: AccountId, value: Balance) -> Result<()> { build_dispatch(TRANSFER) .input::<(AssetId, AccountId, Balance)>() .output::, true>() .handle_error_code::() - .call(&(id, target, amount)) + .call(&(id, to, value)) } /// Transfers `value` tokens on behalf of `from` to account `to` with additional `data` @@ -143,12 +173,12 @@ pub fn transfer(id: AssetId, target: AccountId, amount: Balance) -> Result<()> { /// # Returns /// Returns `Ok(())` if successful, or an error if the transfer fails. #[inline] -pub fn transfer_from(id: AssetId, from: AccountId, to: AccountId, amount: Balance) -> Result<()> { +pub fn transfer_from(id: AssetId, from: AccountId, to: AccountId, value: Balance) -> Result<()> { build_dispatch(TRANSFER_FROM) .input::<(AssetId, AccountId, AccountId, Balance)>() .output::, true>() .handle_error_code::() - .call(&(id, from, to, amount)) + .call(&(id, from, to, value)) } /// Approves an account to spend a specified number of tokens on behalf of the caller. @@ -161,12 +191,12 @@ pub fn transfer_from(id: AssetId, from: AccountId, to: AccountId, amount: Balanc /// # Returns /// Returns `Ok(())` if successful, or an error if the approval fails. #[inline] -pub fn approve(id: AssetId, spender: AccountId, amount: Balance) -> Result<()> { +pub fn approve(id: AssetId, spender: AccountId, value: Balance) -> Result<()> { build_dispatch(APPROVE) .input::<(AssetId, AccountId, Balance)>() .output::, true>() .handle_error_code::() - .call(&(id, spender, amount)) + .call(&(id, spender, value)) } /// Increases the allowance of a spender. @@ -205,7 +235,7 @@ pub fn decrease_allowance(id: AssetId, spender: AccountId, value: Balance) -> Re .call(&(id, spender, value)) } -/// Creates `amount` tokens and assigns them to `account`, increasing the total supply. +/// Creates `value` tokens and assigns them to `account`, increasing the total supply. /// /// # Parameters /// - `id` - The ID of the asset. @@ -214,15 +244,15 @@ pub fn decrease_allowance(id: AssetId, spender: AccountId, value: Balance) -> Re /// /// # Returns /// Returns `Ok(())` if successful, or an error if the operation fails. -pub fn mint(id: AssetId, account: AccountId, amount: Balance) -> Result<()> { +pub fn mint(id: AssetId, account: AccountId, value: Balance) -> Result<()> { build_dispatch(MINT) .input::<(AssetId, AccountId, Balance)>() .output::, true>() .handle_error_code::() - .call(&(id, account, amount)) + .call(&(id, account, value)) } -/// Destroys `amount` tokens from `account`, reducing the total supply. +/// Destroys `value` tokens from `account`, reducing the total supply. /// /// # Parameters /// - `id` - The ID of the asset. @@ -231,12 +261,12 @@ pub fn mint(id: AssetId, account: AccountId, amount: Balance) -> Result<()> { /// /// # Returns /// Returns `Ok(())` if successful, or an error if the operation fails. -pub fn burn(id: AssetId, account: AccountId, amount: Balance) -> Result<()> { +pub fn burn(id: AssetId, account: AccountId, value: Balance) -> Result<()> { build_dispatch(BURN) .input::<(AssetId, AccountId, Balance)>() .output::, true>() .handle_error_code::() - .call(&(id, account, amount)) + .call(&(id, account, value)) } pub mod metadata {