diff --git a/.gitignore b/.gitignore index 6985cf1..c683bf1 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ Cargo.lock # MSVC Windows builds of rustc generate these, which store debugging information *.pdb + +.idea \ No newline at end of file diff --git a/data.rs b/data.rs index c65cb3c..fb86957 100644 --- a/data.rs +++ b/data.rs @@ -5,6 +5,8 @@ use ink::{ primitives::AccountId, storage::Mapping, }; +use ink::env::call::{build_call, ExecutionInput, Selector}; +use ink::env::DefaultEnvironment; /// Temporary type for events emitted during operations that change the /// state of PSP22Data struct. @@ -267,4 +269,115 @@ impl PSP22Data { value, }]) } + + /// Burns `value` tokens from `from` account. + pub fn burn_from(&mut self, + caller: AccountId, + from: AccountId, + value: u128 + ) -> Result, PSP22Error> { + if value == 0 { + return Ok(vec![]); + } + let allowance = self.allowance(from, caller); + if allowance < value { + return Err(PSP22Error::InsufficientAllowance); + } + let balance = self.balance_of(from); + if balance < value { + return Err(PSP22Error::InsufficientBalance); + } + + if allowance == value { + self.allowances.remove((from, caller)); + } else { + self.allowances + .insert((from, caller), &(allowance.saturating_sub(value))); + } + if balance == value { + self.balances.remove(from); + } else { + self.balances.insert(from, &(balance.saturating_sub(value))); + } + self.total_supply = self.total_supply.saturating_sub(value); + Ok(vec![PSP22Event::Transfer { + from: Some(from), + to: None, + value, + }]) + } + + /// Deposits a specified amount of tokens from the `underlying` token contract to this contract. + /// + /// This method transfers tokens from `sender` to the `contract` account (the current contract), + /// using the `underlying` token's `transfer_from` method. It's typically used in wrapper implementations. + /// + /// # Arguments + /// + /// * `underlying` - The AccountId of the underlying token contract. + /// * `sender` - The AccountId of the sender who is depositing tokens. + /// * `contract` - The AccountId of this contract, which will receive the tokens. + /// * `value` - The amount of tokens to be deposited. + /// + /// # Returns + /// + /// A `Result<(), PSP22Error>` indicating the success or failure of the operation. + pub fn deposit(&mut self, + underlying: AccountId, + sender: AccountId, + contract: AccountId, + value: u128 + ) -> Result<(), PSP22Error> { + pub const TRANSFER_FROM_SELECTOR: [u8; 4] = [84, 179, 199, 110]; + + build_call::() + .call(underlying) + .gas_limit(0) + .transferred_value(0) + .exec_input( + ExecutionInput::new(Selector::new(TRANSFER_FROM_SELECTOR)) + .push_arg(sender) + .push_arg(contract) + .push_arg(value) + .push_arg(Vec::::new()) + ) + .returns::>() + .invoke() + } + + /// Withdraws a specified amount of tokens from this contract to a specified account. + /// + /// This method transfers tokens from this contract to the `account` specified, + /// using the `underlying` token's `transfer` method. It's typically used in wrapper implementations. + /// + /// # Arguments + /// + /// * `underlying` - The AccountId of the underlying token contract. + /// * `account` - The AccountId where tokens will be withdrawn to. + /// * `value` - The amount of tokens to be withdrawn. + /// + /// # Returns + /// + /// A `Result<(), PSP22Error>` indicating the success or failure of the operation. + pub fn withdraw(&mut self, + underlying: AccountId, + account: AccountId, + value: u128 + ) -> Result<(), PSP22Error> { + pub const TRANSFER_SELECTOR: [u8; 4] = [219, 32, 249, 245]; + + build_call::() + .call(underlying) + .gas_limit(0) + .transferred_value(0) + .exec_input( + ExecutionInput::new(Selector::new(TRANSFER_SELECTOR)) + .push_arg(account) + .push_arg(value) + .push_arg(Vec::::new()) + ) + .returns::>() + .invoke() + } + } diff --git a/errors.rs b/errors.rs index fcf22ae..2575286 100644 --- a/errors.rs +++ b/errors.rs @@ -16,3 +16,12 @@ pub enum PSP22Error { /// Returned if a safe transfer check failed [deprecated]. SafeTransferCheckFailed(String), } + +/// Errors related to ownership operations. +/// +/// This enum is used for managing errors that occur in ownership-related +/// functionalities. +#[derive(Debug, PartialEq, Eq, scale::Encode, scale::Decode)] +#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] +pub enum OwnableError { +} diff --git a/traits.rs b/traits.rs index 7c9c5bf..685fdc5 100644 --- a/traits.rs +++ b/traits.rs @@ -4,6 +4,7 @@ use ink::{ }; use crate::errors::PSP22Error; +use crate::errors::OwnableError; #[ink::trait_definition] pub trait PSP22 { @@ -153,6 +154,23 @@ pub trait PSP22Burnable { /// Reverts with `InsufficientBalance` if the `value` exceeds the caller's balance. #[ink(message)] fn burn(&mut self, value: u128) -> Result<(), PSP22Error>; + + /// Burns `value` tokens from the "account" account id. Spends allowances. + /// + /// The selector for this message are + /// first 4 bytes of `blake2b_256("PSP22Burnable::burn_from")` + /// + /// # Events + /// + /// On success a `Transfer` event is emitted with `None` recipient. + /// + /// No-op if `value` is zero, returns success and no events are emitted. + /// + /// # Errors + /// + /// Reverts with `InsufficientBalance` if the `value` exceeds the caller's balance. + #[ink(message)] + fn burn_from(&mut self, account: AccountId, value: u128) -> Result<(), PSP22Error>; } #[ink::trait_definition] @@ -172,5 +190,109 @@ pub trait PSP22Mintable { /// Reverts with `Custom (max supply exceeded)` if the total supply increased by /// `value` exceeds maximal value of `u128` type. #[ink(message)] - fn mint(&mut self, value: u128) -> Result<(), PSP22Error>; + fn mint(&mut self, to: AccountId, value: u128) -> Result<(), PSP22Error>; +} + + +/// Trait for pausing and unpausing token transfers. +/// +/// This trait allows the contract owner to pause or unpause token transfers, +/// which can be useful in emergency situations or during maintenance. +#[ink::trait_definition] +pub trait PSP22Pausable { + /// Pauses all token transfers. + /// + /// This method is used to temporarily halt all transfer operations. + /// + /// # Returns + /// + /// A `Result<(), PSP22Error>` indicating whether the operation was successful. + #[ink(message)] + fn pause(&mut self) -> Result<(), PSP22Error>; + + /// Unpauses all token transfers. + /// + /// This method re-enables token transfer operations. + /// + /// # Returns + /// + /// A `Result<(), PSP22Error>` indicating whether the operation was successful. + #[ink(message)] + fn unpause(&mut self) -> Result<(), PSP22Error>; +} + +/// Trait for wrapping and unwrapping PSP22 tokens. +/// +/// This trait provides methods for depositing and withdrawing tokens, +/// often used in implementations that wrap other token standards. +#[ink::trait_definition] +pub trait PSP22Wrapper { + /// Deposits tokens into the contract for a specified account. + /// + /// This method allows a user to add tokens to the contract, which can be used + /// for various functionalities like staking or liquidity provision. + /// + /// # Arguments + /// + /// * `account` - The account for which the tokens will be deposited. + /// * `amount` - The amount of tokens to deposit. + /// + /// # Returns + /// + /// A `Result<(), PSP22Error>` indicating the success or failure of the operation. + #[ink(message)] + fn deposit_for(&mut self, account: AccountId, amount: u128) -> Result<(), PSP22Error>; + + /// Withdraws tokens from the contract to a specified account. + /// + /// This method allows users to withdraw their tokens from the contract. + /// + /// # Arguments + /// + /// * `account` - The account to which the tokens will be withdrawn. + /// * `amount` - The amount of tokens to withdraw. + /// + /// # Returns + /// + /// A `Result<(), PSP22Error>` indicating the success or failure of the operation. + #[ink(message)] + fn withdraw_to(&mut self, account: AccountId, amount: u128) -> Result<(), PSP22Error>; +} + +/// Trait for ownership-related functionalities. +/// +/// Provides methods for managing ownership of the contract, including +/// transferring and renouncing ownership. +#[ink::trait_definition] +pub trait Ownable { + /// Returns the address of the current owner. + /// + /// # Returns + /// + /// The `AccountId` of the current owner. + #[ink(message)] + fn owner(&self) -> Option; + + /// Renounces ownership of the contract. + /// + /// This method is used to permanently transfer control of the contract + /// away from the current owner, leaving it without an owner. + /// + /// # Returns + /// + /// A `Result<(), OwnableError>` indicating whether the operation was successful. + #[ink(message)] + fn renounce_ownership(&mut self) -> Result<(), OwnableError>; + + /// Transfers ownership of the contract to a new account. + /// + /// # Arguments + /// + /// * `new_owner` - The `AccountId` of the new owner. + /// + /// # Returns + /// + /// A `Result<(), OwnableError>` indicating whether the operation was successful. + #[ink(message)] + fn transfer_ownership(&mut self, new_owner: Option) -> Result<(), OwnableError>; }