diff --git a/pop-api/src/v0/fungibles/errors.rs b/pop-api/src/v0/fungibles/errors.rs new file mode 100644 index 00000000..a5c548a6 --- /dev/null +++ b/pop-api/src/v0/fungibles/errors.rs @@ -0,0 +1,247 @@ +//! A set of errors for use in smart contracts that interact with the fungibles api. This includes errors compliant to standards. + +use super::*; +use ink::prelude::string::{String, ToString}; + +/// Represents various errors related to fungible tokens. +/// +/// The `FungiblesError` provides a detailed and specific set of error types that can occur when +/// interacting with fungible tokens. Each variant signifies a particular error +/// condition, facilitating precise error handling and debugging. +/// +/// It is designed to be lightweight, including only the essential errors relevant to fungible token +/// operations. The `Other` variant serves as a catch-all for any unexpected errors. For more +/// detailed debugging, the `Other` variant can be converted into the richer `Error` type defined in +/// the primitives crate. +/// NOTE: The `FungiblesError` is WIP +#[derive(Debug, PartialEq, Eq)] +#[ink::scale_derive(Encode, Decode, TypeInfo)] +pub enum FungiblesError { + /// An unspecified or unknown error occurred. + Other(StatusCode), + /// The token is not live; either frozen or being destroyed. + NotLive, + /// Not enough allowance to fulfill a request is available. + InsufficientAllowance, + /// Not enough balance to fulfill a request is available. + InsufficientBalance, + /// The token ID is already taken. + InUse, + /// Minimum balance should be non-zero. + MinBalanceZero, + /// The account to alter does not exist. + NoAccount, + /// The signing account has no permission to do the operation. + NoPermission, + /// The given token ID is unknown. + Unknown, + /// No balance for creation of tokens or fees. + // TODO: Originally `pallet_balances::Error::InsufficientBalance` but collides with the + // `InsufficientBalance` error that is used for `pallet_assets::Error::BalanceLow` to adhere + // to the standard. This deserves a second look. + NoBalance, +} + +impl From for FungiblesError { + /// Converts a `StatusCode` to a `FungiblesError`. + /// + /// This conversion maps a `StatusCode`, returned by the runtime, to a more descriptive + /// `FungiblesError`. This provides better context and understanding of the error, allowing + /// developers to handle the most important errors effectively. + fn from(value: StatusCode) -> Self { + let encoded = value.0.to_le_bytes(); + match encoded { + // Balances. + [_, BALANCES, 2, _] => FungiblesError::NoBalance, + // Assets. + [_, ASSETS, 0, _] => FungiblesError::NoAccount, + [_, ASSETS, 1, _] => FungiblesError::NoPermission, + [_, ASSETS, 2, _] => FungiblesError::Unknown, + [_, ASSETS, 3, _] => FungiblesError::InUse, + [_, ASSETS, 5, _] => FungiblesError::MinBalanceZero, + [_, ASSETS, 7, _] => FungiblesError::InsufficientAllowance, + [_, ASSETS, 10, _] => FungiblesError::NotLive, + _ => FungiblesError::Other(value), + } + } +} + +/// The PSP22 error. +// TODO: Issue https://github.com/r0gue-io/pop-node/issues/298 +#[derive(Debug, PartialEq, Eq)] +#[ink::scale_derive(Encode, Decode, TypeInfo)] +pub enum PSP22Error { + /// Custom error type for implementation-based errors. + Custom(String), + /// Returned when an account does not have enough tokens to complete the operation. + InsufficientBalance, + /// Returned if there is not enough allowance to complete the operation. + InsufficientAllowance, + /// Returned if recipient's address is zero. + ZeroRecipientAddress, + /// Returned if sender's address is zero. + ZeroSenderAddress, + /// Returned if a safe transfer check failed. + SafeTransferCheckFailed(String), +} + +impl From for PSP22Error { + /// Converts a `StatusCode` to a `PSP22Error`. + fn from(value: StatusCode) -> Self { + let encoded = value.0.to_le_bytes(); + match encoded { + // BalanceLow. + [_, ASSETS, 0, _] => PSP22Error::InsufficientBalance, + // Unapproved. + [_, ASSETS, 10, _] => PSP22Error::InsufficientAllowance, + // Unknown. + [_, ASSETS, 3, _] => PSP22Error::Custom(String::from("Unknown")), + _ => PSP22Error::Custom(value.0.to_string()), + } + } +} + +#[cfg(test)] +mod tests { + use super::{FungiblesError, PSP22Error}; + use crate::{ + constants::{ASSETS, BALANCES}, + primitives::{ + ArithmeticError::*, + Error::{self, *}, + TokenError::*, + TransactionalError::*, + }, + StatusCode, + }; + use ink::prelude::string::String; + use ink::scale::{Decode, Encode}; + + fn error_into_status_code(error: Error) -> StatusCode { + let mut encoded_error = error.encode(); + encoded_error.resize(4, 0); + let value = u32::from_le_bytes( + encoded_error.try_into().expect("qed, resized to 4 bytes line above"), + ); + value.into() + } + + fn into_error>(error: Error) -> T { + error_into_status_code(error).into() + } + + // If we ever want to change the conversion from bytes to `u32`. + #[test] + fn status_code_vs_encoded() { + assert_eq!(u32::decode(&mut &[3u8, 10, 2, 0][..]).unwrap(), 133635u32); + assert_eq!(u32::decode(&mut &[3u8, 52, 0, 0][..]).unwrap(), 13315u32); + assert_eq!(u32::decode(&mut &[3u8, 52, 1, 0][..]).unwrap(), 78851u32); + assert_eq!(u32::decode(&mut &[3u8, 52, 2, 0][..]).unwrap(), 144387u32); + assert_eq!(u32::decode(&mut &[3u8, 52, 3, 0][..]).unwrap(), 209923u32); + assert_eq!(u32::decode(&mut &[3u8, 52, 5, 0][..]).unwrap(), 340995u32); + assert_eq!(u32::decode(&mut &[3u8, 52, 7, 0][..]).unwrap(), 472067u32); + assert_eq!(u32::decode(&mut &[3u8, 52, 10, 0][..]).unwrap(), 668675u32); + } + + #[test] + fn converting_status_code_into_fungibles_error_works() { + let other_errors = vec![ + Other, + CannotLookup, + BadOrigin, + // `ModuleError` other than assets module. + Module { index: 2, error: [5, 0] }, + ConsumerRemaining, + NoProviders, + TooManyConsumers, + Token(OnlyProvider), + Arithmetic(Overflow), + Transactional(NoLayer), + Exhausted, + Corruption, + Unavailable, + RootNotAllowed, + Unknown { dispatch_error_index: 5, error_index: 5, error: 1 }, + DecodingFailed, + ]; + for error in other_errors { + let status_code: StatusCode = error_into_status_code(error); + let fungibles_error: FungiblesError = status_code.into(); + assert_eq!(fungibles_error, FungiblesError::Other(status_code)) + } + + assert_eq!( + into_error::(Module { index: BALANCES, error: [2, 0] }), + FungiblesError::NoBalance + ); + assert_eq!( + into_error::(Module { index: ASSETS, error: [0, 0] }), + FungiblesError::NoAccount + ); + assert_eq!( + into_error::(Module { index: ASSETS, error: [1, 0] }), + FungiblesError::NoPermission + ); + assert_eq!( + into_error::(Module { index: ASSETS, error: [2, 0] }), + FungiblesError::Unknown + ); + assert_eq!( + into_error::(Module { index: ASSETS, error: [3, 0] }), + FungiblesError::InUse + ); + assert_eq!( + into_error::(Module { index: ASSETS, error: [5, 0] }), + FungiblesError::MinBalanceZero + ); + assert_eq!( + into_error::(Module { index: ASSETS, error: [7, 0] }), + FungiblesError::InsufficientAllowance + ); + assert_eq!( + into_error::(Module { index: ASSETS, error: [10, 0] }), + FungiblesError::NotLive + ); + } + + #[test] + fn converting_status_code_into_psp22_error_works() { + let other_errors = vec![ + Other, + CannotLookup, + BadOrigin, + // `ModuleError` other than assets module. + Module { index: 2, error: [5, 0] }, + ConsumerRemaining, + NoProviders, + TooManyConsumers, + Token(OnlyProvider), + Arithmetic(Overflow), + Transactional(NoLayer), + Exhausted, + Corruption, + Unavailable, + RootNotAllowed, + Unknown { dispatch_error_index: 5, error_index: 5, error: 1 }, + DecodingFailed, + ]; + for error in other_errors { + let status_code: StatusCode = error_into_status_code(error); + let fungibles_error: PSP22Error = status_code.into(); + assert_eq!(fungibles_error, PSP22Error::Custom(status_code.0.to_string())) + } + + assert_eq!( + into_error::(Module { index: ASSETS, error: [0, 0] }), + PSP22Error::InsufficientBalance + ); + assert_eq!( + into_error::(Module { index: ASSETS, error: [10, 0] }), + PSP22Error::InsufficientAllowance + ); + assert_eq!( + into_error::(Module { index: ASSETS, error: [3, 0] }), + PSP22Error::Custom(String::from("Unknown")) + ); + } +} diff --git a/pop-api/src/v0/fungibles/events.rs b/pop-api/src/v0/fungibles/events.rs new file mode 100644 index 00000000..130ead65 --- /dev/null +++ b/pop-api/src/v0/fungibles/events.rs @@ -0,0 +1,85 @@ +//! A set of events for use in smart contracts interacting with the fungibles API. +//! +//! The `Transfer` and `Approval` events conform to the PSP-22 standard. The other events +//! (`Create`, `StartDestroy`, `SetMetadata`, `ClearMetadata`) are provided for convenience. +//! +//! These events are not emitted by the API itself but can be used in your contracts to +//! track token operations. Be mindful of the costs associated with emitting events. +//! +//! For more details, refer to [ink! events](https://use.ink/basics/events). + +use super::*; + +/// Event emitted when allowance by `owner` to `spender` changes. +// Differing style: event name abides by the PSP22 standard. +#[ink::event] +pub struct Approval { + /// The owner providing the allowance. + #[ink(topic)] + pub owner: AccountId, + /// The beneficiary of the allowance. + #[ink(topic)] + pub spender: AccountId, + /// The new allowance amount. + pub value: u128, +} + +/// Event emitted when transfer of tokens occurs. +// Differing style: event name abides by the PSP22 standard. +#[ink::event] +pub struct Transfer { + /// The source of the transfer. `None` when minting. + #[ink(topic)] + pub from: Option, + /// The recipient of the transfer. `None` when burning. + #[ink(topic)] + pub to: Option, + /// The amount transferred (or minted/burned). + pub value: u128, +} + +/// Event emitted when a token is created. +#[ink::event] +pub struct Created { + /// The token identifier. + #[ink(topic)] + pub id: TokenId, + /// The creator of the token. + #[ink(topic)] + pub creator: AccountId, + /// The administrator of the token. + #[ink(topic)] + pub admin: AccountId, +} + +/// Event emitted when a token is in the process of being destroyed. +#[ink::event] +pub struct DestroyStarted { + /// The token. + #[ink(topic)] + pub token: TokenId, +} + +/// Event emitted when new metadata is set for a token. +#[ink::event] +pub struct MetadataSet { + /// The token. + #[ink(topic)] + pub token: TokenId, + /// The name of the token. + #[ink(topic)] + pub name: Vec, + /// The symbol of the token. + #[ink(topic)] + pub symbol: Vec, + /// The decimals of the token. + pub decimals: u8, +} + +/// Event emitted when metadata is cleared for a token. +#[ink::event] +pub struct MetadataCleared { + /// The token. + #[ink(topic)] + pub token: TokenId, +} diff --git a/pop-api/src/v0/fungibles.rs b/pop-api/src/v0/fungibles/mod.rs similarity index 56% rename from pop-api/src/v0/fungibles.rs rename to pop-api/src/v0/fungibles/mod.rs index 99c1261a..032af4d1 100644 --- a/pop-api/src/v0/fungibles.rs +++ b/pop-api/src/v0/fungibles/mod.rs @@ -6,145 +6,22 @@ //! 3. Management //! 4. PSP-22 Mintable & Burnable -use constants::*; -use ink::prelude::vec::Vec; -pub use management::*; -pub use metadata::*; - use crate::{ constants::{ASSETS, BALANCES, FUNGIBLES}, primitives::{AccountId, Balance, TokenId}, ChainExtensionMethodApi, Result, StatusCode, }; +use constants::*; +pub use errors::*; +pub use events::*; +use ink::prelude::vec::Vec; +pub use management::*; +pub use metadata::*; +pub use traits::*; -// Helper method to build a dispatch call. -// -// Parameters: -// - 'dispatchable': The index of the dispatchable function within the module. -fn build_dispatch(dispatchable: u8) -> ChainExtensionMethodApi { - crate::v0::build_dispatch(FUNGIBLES, dispatchable) -} - -// Helper method to build a call to read state. -// -// Parameters: -// - 'state_query': The index of the runtime state query. -fn build_read_state(state_query: u8) -> ChainExtensionMethodApi { - crate::v0::build_read_state(FUNGIBLES, state_query) -} - -mod constants { - /// 1. PSP-22 Interface: - pub(super) const TOTAL_SUPPLY: u8 = 0; - pub(super) const BALANCE_OF: u8 = 1; - pub(super) const ALLOWANCE: u8 = 2; - pub(super) const TRANSFER: u8 = 3; - pub(super) const TRANSFER_FROM: u8 = 4; - pub(super) const APPROVE: u8 = 5; - pub(super) const INCREASE_ALLOWANCE: u8 = 6; - pub(super) const DECREASE_ALLOWANCE: u8 = 7; - - /// 2. PSP-22 Metadata Interface: - pub(super) const TOKEN_NAME: u8 = 8; - pub(super) const TOKEN_SYMBOL: u8 = 9; - pub(super) const TOKEN_DECIMALS: u8 = 10; - - /// 3. Asset Management: - pub(super) const CREATE: u8 = 11; - pub(super) const START_DESTROY: u8 = 12; - pub(super) const SET_METADATA: u8 = 16; - pub(super) const CLEAR_METADATA: u8 = 17; - pub(super) const TOKEN_EXISTS: u8 = 18; - - /// 4. PSP-22 Mintable & Burnable interface: - pub(super) const MINT: u8 = 19; - pub(super) const BURN: u8 = 20; -} - -/// A set of events for use in smart contracts interacting with the fungibles API. -/// -/// The `Transfer` and `Approval` events conform to the PSP-22 standard. The other events -/// (`Create`, `StartDestroy`, `SetMetadata`, `ClearMetadata`) are provided for convenience. -/// -/// These events are not emitted by the API itself but can be used in your contracts to -/// track token operations. Be mindful of the costs associated with emitting events. -/// -/// For more details, refer to [ink! events](https://use.ink/basics/events). -pub mod events { - use super::*; - - /// Event emitted when allowance by `owner` to `spender` changes. - #[ink::event] - pub struct Approval { - /// The owner providing the allowance. - #[ink(topic)] - pub owner: AccountId, - /// The beneficiary of the allowance. - #[ink(topic)] - pub spender: AccountId, - /// The new allowance amount. - pub value: u128, - } - - /// Event emitted when transfer of tokens occurs. - #[ink::event] - pub struct Transfer { - /// The source of the transfer. `None` when minting. - #[ink(topic)] - pub from: Option, - /// The recipient of the transfer. `None` when burning. - #[ink(topic)] - pub to: Option, - /// The amount transferred (or minted/burned). - pub value: u128, - } - - /// Event emitted when a token is created. - #[ink::event] - pub struct Created { - /// The token identifier. - #[ink(topic)] - pub id: TokenId, - /// The creator of the token. - #[ink(topic)] - pub creator: AccountId, - /// The administrator of the token. - #[ink(topic)] - pub admin: AccountId, - } - - /// Event emitted when a token is in the process of being destroyed. - #[ink::event] - pub struct DestroyStarted { - /// The token. - #[ink(topic)] - pub token: TokenId, - } - - /// Event emitted when new metadata is set for a token. - #[ink::event] - pub struct MetadataSet { - /// The token. - #[ink(topic)] - pub token: TokenId, - /// The name of the token. - #[ink(topic)] - pub name: Vec, - /// The symbol of the token. - #[ink(topic)] - pub symbol: Vec, - /// The decimals of the token. - pub decimals: u8, - } - - /// Event emitted when metadata is cleared for a token. - #[ink::event] - pub struct MetadataCleared { - /// The token. - #[ink(topic)] - pub token: TokenId, - } -} +pub mod errors; +pub mod events; +pub mod traits; /// Returns the total token supply for a specified token. /// @@ -420,169 +297,46 @@ pub mod management { } } -/// Represents various errors related to fungible tokens. -/// -/// The `FungiblesError` provides a detailed and specific set of error types that can occur when -/// interacting with fungible tokens. Each variant signifies a particular error -/// condition, facilitating precise error handling and debugging. -/// -/// It is designed to be lightweight, including only the essential errors relevant to fungible token -/// operations. The `Other` variant serves as a catch-all for any unexpected errors. For more -/// detailed debugging, the `Other` variant can be converted into the richer `Error` type defined in -/// the primitives crate. -#[derive(Debug, PartialEq, Eq)] -#[ink::scale_derive(Encode, Decode, TypeInfo)] -pub enum FungiblesError { - /// An unspecified or unknown error occurred. - Other(StatusCode), - /// The token is not live; either frozen or being destroyed. - NotLive, - /// Not enough allowance to fulfill a request is available. - InsufficientAllowance, - /// Not enough balance to fulfill a request is available. - InsufficientBalance, - /// The token ID is already taken. - InUse, - /// Minimum balance should be non-zero. - MinBalanceZero, - /// The account to alter does not exist. - NoAccount, - /// The signing account has no permission to do the operation. - NoPermission, - /// The given token ID is unknown. - Unknown, - /// No balance for creation of tokens or fees. - // TODO: Originally `pallet_balances::Error::InsufficientBalance` but collides with the - // `InsufficientBalance` error that is used for `pallet_assets::Error::BalanceLow` to adhere - // to the standard. This deserves a second look. - NoBalance, -} - -impl From for FungiblesError { - /// Converts a `StatusCode` to a `FungiblesError`. - /// - /// This conversion maps a `StatusCode`, returned by the runtime, to a more descriptive - /// `FungiblesError`. This provides better context and understanding of the error, allowing - /// developers to handle the most important errors effectively. - fn from(value: StatusCode) -> Self { - let encoded = value.0.to_le_bytes(); - match encoded { - // Balances. - [_, BALANCES, 2, _] => FungiblesError::NoBalance, - // Assets. - [_, ASSETS, 0, _] => FungiblesError::NoAccount, - [_, ASSETS, 1, _] => FungiblesError::NoPermission, - [_, ASSETS, 2, _] => FungiblesError::Unknown, - [_, ASSETS, 3, _] => FungiblesError::InUse, - [_, ASSETS, 5, _] => FungiblesError::MinBalanceZero, - [_, ASSETS, 7, _] => FungiblesError::InsufficientAllowance, - [_, ASSETS, 10, _] => FungiblesError::NotLive, - _ => FungiblesError::Other(value), - } - } -} - -#[cfg(test)] -mod tests { - use ink::scale::{Decode, Encode}; - - use super::FungiblesError; - use crate::{ - constants::{ASSETS, BALANCES}, - primitives::{ - ArithmeticError::*, - Error::{self, *}, - TokenError::*, - TransactionalError::*, - }, - StatusCode, - }; +mod constants { + /// 1. PSP-22 + pub(super) const TOTAL_SUPPLY: u8 = 0; + pub(super) const BALANCE_OF: u8 = 1; + pub(super) const ALLOWANCE: u8 = 2; + pub(super) const TRANSFER: u8 = 3; + pub(super) const TRANSFER_FROM: u8 = 4; + pub(super) const APPROVE: u8 = 5; + pub(super) const INCREASE_ALLOWANCE: u8 = 6; + pub(super) const DECREASE_ALLOWANCE: u8 = 7; - fn error_into_status_code(error: Error) -> StatusCode { - let mut encoded_error = error.encode(); - encoded_error.resize(4, 0); - let value = u32::from_le_bytes( - encoded_error.try_into().expect("qed, resized to 4 bytes line above"), - ); - value.into() - } + /// 2. PSP-22 Metadata + pub(super) const TOKEN_NAME: u8 = 8; + pub(super) const TOKEN_SYMBOL: u8 = 9; + pub(super) const TOKEN_DECIMALS: u8 = 10; - fn into_fungibles_error(error: Error) -> FungiblesError { - let status_code: StatusCode = error_into_status_code(error); - status_code.into() - } + /// 3. Management + pub(super) const CREATE: u8 = 11; + pub(super) const START_DESTROY: u8 = 12; + pub(super) const SET_METADATA: u8 = 16; + pub(super) const CLEAR_METADATA: u8 = 17; + pub(super) const TOKEN_EXISTS: u8 = 18; - // If we ever want to change the conversion from bytes to `u32`. - #[test] - fn status_code_vs_encoded() { - assert_eq!(u32::decode(&mut &[3u8, 10, 2, 0][..]).unwrap(), 133635u32); - assert_eq!(u32::decode(&mut &[3u8, 52, 0, 0][..]).unwrap(), 13315u32); - assert_eq!(u32::decode(&mut &[3u8, 52, 1, 0][..]).unwrap(), 78851u32); - assert_eq!(u32::decode(&mut &[3u8, 52, 2, 0][..]).unwrap(), 144387u32); - assert_eq!(u32::decode(&mut &[3u8, 52, 3, 0][..]).unwrap(), 209923u32); - assert_eq!(u32::decode(&mut &[3u8, 52, 5, 0][..]).unwrap(), 340995u32); - assert_eq!(u32::decode(&mut &[3u8, 52, 7, 0][..]).unwrap(), 472067u32); - assert_eq!(u32::decode(&mut &[3u8, 52, 10, 0][..]).unwrap(), 668675u32); - } + /// 4. PSP-22 Mintable & Burnable + pub(super) const MINT: u8 = 19; + pub(super) const BURN: u8 = 20; +} - #[test] - fn converting_status_code_into_fungibles_error_works() { - let other_errors = vec![ - Other, - CannotLookup, - BadOrigin, - // `ModuleError` other than assets module. - Module { index: 2, error: [5, 0] }, - ConsumerRemaining, - NoProviders, - TooManyConsumers, - Token(OnlyProvider), - Arithmetic(Overflow), - Transactional(NoLayer), - Exhausted, - Corruption, - Unavailable, - RootNotAllowed, - Unknown { dispatch_error_index: 5, error_index: 5, error: 1 }, - DecodingFailed, - ]; - for error in other_errors { - let status_code: StatusCode = error_into_status_code(error); - let fungibles_error: FungiblesError = status_code.into(); - assert_eq!(fungibles_error, FungiblesError::Other(status_code)) - } +// Helper method to build a dispatch call. +// +// Parameters: +// - 'dispatchable': The index of the dispatchable function within the module. +fn build_dispatch(dispatchable: u8) -> ChainExtensionMethodApi { + crate::v0::build_dispatch(FUNGIBLES, dispatchable) +} - assert_eq!( - into_fungibles_error(Module { index: BALANCES, error: [2, 0] }), - FungiblesError::NoBalance - ); - assert_eq!( - into_fungibles_error(Module { index: ASSETS, error: [0, 0] }), - FungiblesError::NoAccount - ); - assert_eq!( - into_fungibles_error(Module { index: ASSETS, error: [1, 0] }), - FungiblesError::NoPermission - ); - assert_eq!( - into_fungibles_error(Module { index: ASSETS, error: [2, 0] }), - FungiblesError::Unknown - ); - assert_eq!( - into_fungibles_error(Module { index: ASSETS, error: [3, 0] }), - FungiblesError::InUse - ); - assert_eq!( - into_fungibles_error(Module { index: ASSETS, error: [5, 0] }), - FungiblesError::MinBalanceZero - ); - assert_eq!( - into_fungibles_error(Module { index: ASSETS, error: [7, 0] }), - FungiblesError::InsufficientAllowance - ); - assert_eq!( - into_fungibles_error(Module { index: ASSETS, error: [10, 0] }), - FungiblesError::NotLive - ); - } +// Helper method to build a call to read state. +// +// Parameters: +// - 'state_query': The index of the runtime state query. +fn build_read_state(state_query: u8) -> ChainExtensionMethodApi { + crate::v0::build_read_state(FUNGIBLES, state_query) } diff --git a/pop-api/src/v0/fungibles/traits.rs b/pop-api/src/v0/fungibles/traits.rs new file mode 100644 index 00000000..92ad55e3 --- /dev/null +++ b/pop-api/src/v0/fungibles/traits.rs @@ -0,0 +1,121 @@ +//! Traits that can be used by contracts. Including standard compliant traits. + +use super::*; +use core::result::Result; +use ink::prelude::string::String; + +/// The PSP22 trait. +#[ink::trait_definition] +pub trait Psp22 { + /// Returns the total token supply. + #[ink(message)] + fn total_supply(&self) -> Balance; + + /// Returns the account balance for the specified `owner`. + /// + /// # Parameters + /// - `owner` - The account whose balance is being queried. + #[ink(message)] + fn balance_of(&self, owner: AccountId) -> Balance; + + /// Returns the allowance for a `spender` approved by an `owner`. + /// + /// # Parameters + /// - `owner` - The account that owns the tokens. + /// - `spender` - The account that is allowed to spend the tokens. + #[ink(message)] + fn allowance(&self, owner: AccountId, spender: AccountId) -> Balance; + + /// Transfers `value` amount of tokens from the caller's account to account `to` + /// with additional `data` in unspecified format. + /// + /// # Parameters + /// - `to` - The recipient account. + /// - `value` - The number of tokens to transfer. + /// - `data` - Additional data in unspecified format. + #[ink(message)] + fn transfer(&mut self, to: AccountId, value: Balance, data: Vec) -> Result<(), PSP22Error>; + + /// Transfers `value` tokens on behalf of `from` to the account `to` + /// with additional `data` in unspecified format. + /// + /// # Parameters + /// - `from` - The account from which the token balance will be withdrawn. + /// - `to` - The recipient account. + /// - `value` - The number of tokens to transfer. + /// - `data` - Additional data with unspecified format. + #[ink(message)] + fn transfer_from( + &mut self, + from: AccountId, + to: AccountId, + value: Balance, + data: Vec, + ) -> Result<(), PSP22Error>; + + /// Approves `spender` to spend `value` amount of tokens on behalf of the caller. + /// + /// Successive calls of this method overwrite previous values. + /// + /// # Parameters + /// - `spender` - The account that is allowed to spend the tokens. + /// - `value` - The number of tokens to approve. + #[ink(message)] + fn approve(&mut self, spender: AccountId, value: Balance) -> Result<(), PSP22Error>; + + /// Increases the allowance of `spender` by `value` amount of tokens. + /// + /// # Parameters + /// - `spender` - The account that is allowed to spend the tokens. + /// - `value` - The number of tokens to increase the allowance by. + #[ink(message)] + fn increase_allowance(&mut self, spender: AccountId, value: Balance) -> Result<(), PSP22Error>; + + /// Decreases the allowance of `spender` by `value` amount of tokens. + /// + /// # Parameters + /// - `spender` - The account that is allowed to spend the tokens. + /// - `value` - The number of tokens to decrease the allowance by. + #[ink(message)] + fn decrease_allowance(&mut self, spender: AccountId, value: Balance) -> Result<(), PSP22Error>; +} + +/// The PSP22 Metadata trait. +#[ink::trait_definition] +pub trait Psp22Metadata { + /// Returns the token name. + #[ink(message)] + fn token_name(&self) -> Option; + + /// Returns the token symbol. + #[ink(message)] + fn token_symbol(&self) -> Option; + + /// Returns the token decimals. + #[ink(message)] + fn token_decimals(&self) -> u8; +} + +/// The PSP22 Mintable trait. +#[ink::trait_definition] +pub trait Psp22Mintable { + /// Creates `value` amount of tokens and assigns them to `account`, increasing the total supply. + /// + /// # Parameters + /// - `account` - The account to be credited with the created tokens. + /// - `value` - The number of tokens to mint. + #[ink(message)] + fn mint(&mut self, account: AccountId, value: Balance) -> Result<(), PSP22Error>; +} + +/// The PSP22 Burnable trait. +#[ink::trait_definition] +pub trait Psp22Burnable { + /// Destroys `value` amount of tokens from `account`, reducing the total supply. + /// + /// # Parameters + /// - `account` - The account from which the tokens will be destroyed. + /// - `value` - The number of tokens to destroy. + #[ink(message)] + fn burn(&mut self, account: AccountId, value: Balance) -> Result<(), PSP22Error>; +}