Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 2 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,13 @@ description = "Minimal implementation of PSP22 token standard in pure ink!"
exclude = [ ".github/*" ]

[dependencies]
ink = { version = "4.3", default-features = false }

scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
scale-info = { version = "2.9", default-features = false, features = ["derive"], optional = true }
ink = { version = "5.0.0-rc", default-features = false }

[lib]
path = "lib.rs"

[features]
default = ["std"]
std = [
"ink/std",
"scale/std",
"scale-info/std",
]
std = ["ink/std"]
contract = []
ink-as-dependency = []
81 changes: 27 additions & 54 deletions data.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
use crate::PSP22Error;
use crate::errors::PSP22Error;
use crate::events::{Approval, Transfer};
use ink::prelude::string::String;
use ink::{
prelude::{vec, vec::Vec},
primitives::AccountId,
storage::Mapping,
};

/// Temporary type for events emitted during operations that change the
/// Common wrapper type for events emitted during operations that change the
/// state of PSP22Data struct.
/// This is meant to be replaced with proper ink! events as soon as the
/// language allows for event definitions outside contracts.
pub enum PSP22Event {
Transfer {
from: Option<AccountId>,
to: Option<AccountId>,
value: u128,
},
Approval {
owner: AccountId,
spender: AccountId,
amount: u128,
},
Transfer(Transfer),
Approval(Approval),
}

// Shortcut for Approval PSP22Event constructor.
fn approval_event(owner: AccountId, spender: AccountId, amount: u128) -> PSP22Event {
PSP22Event::Approval(Approval {
owner,
spender,
amount,
})
}

// Shortcut for Transfer PSP22Event constructor.
fn transfer_event(from: Option<AccountId>, to: Option<AccountId>, value: u128) -> PSP22Event {
PSP22Event::Transfer(Transfer { from, to, value })
}

/// A class implementing the internal logic of a PSP22 token.
Expand Down Expand Up @@ -92,11 +97,7 @@ impl PSP22Data {
// Total supply is limited by u128.MAX so no overflow is possible
self.balances
.insert(to, &(to_balance.saturating_add(value)));
Ok(vec![PSP22Event::Transfer {
from: Some(caller),
to: Some(to),
value,
}])
Ok(vec![transfer_event(Some(caller), Some(to), value)])
}

/// Transfers `value` tokens from `from` to `to`, but using the allowance
Expand Down Expand Up @@ -142,16 +143,8 @@ impl PSP22Data {
self.balances
.insert(to, &(to_balance.saturating_add(value)));
Ok(vec![
PSP22Event::Approval {
owner: from,
spender: caller,
amount: allowance.saturating_sub(value),
},
PSP22Event::Transfer {
from: Some(from),
to: Some(to),
value,
},
approval_event(from, caller, allowance.saturating_sub(value)),
transfer_event(Some(from), Some(to), value),
])
}

Expand All @@ -171,11 +164,7 @@ impl PSP22Data {
} else {
self.allowances.insert((owner, spender), &value);
}
Ok(vec![PSP22Event::Approval {
owner,
spender,
amount: value,
}])
Ok(vec![approval_event(owner, spender, value)])
}

/// Increases the allowance granted by `owner` to `spender` by `delta_value`.
Expand All @@ -191,11 +180,7 @@ impl PSP22Data {
let allowance = self.allowance(owner, spender);
let amount = allowance.saturating_add(delta_value);
self.allowances.insert((owner, spender), &amount);
Ok(vec![PSP22Event::Approval {
owner,
spender,
amount,
}])
Ok(vec![approval_event(owner, spender, amount)])
}

/// Decreases the allowance granted by `owner` to `spender` by `delta_value`.
Expand All @@ -218,11 +203,7 @@ impl PSP22Data {
} else {
self.allowances.insert((owner, spender), &amount);
}
Ok(vec![PSP22Event::Approval {
owner,
spender,
amount,
}])
Ok(vec![approval_event(owner, spender, amount)])
}

/// Mints a `value` of new tokens to `to` account.
Expand All @@ -239,11 +220,7 @@ impl PSP22Data {
self.total_supply = new_supply;
let new_balance = self.balance_of(to).saturating_add(value);
self.balances.insert(to, &new_balance);
Ok(vec![PSP22Event::Transfer {
from: None,
to: Some(to),
value,
}])
Ok(vec![transfer_event(None, Some(to), value)])
}

/// Burns `value` tokens from `from` account.
Expand All @@ -261,10 +238,6 @@ impl PSP22Data {
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,
}])
Ok(vec![transfer_event(Some(from), None, value)])
}
}
4 changes: 2 additions & 2 deletions errors.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use ink::prelude::string::String;

#[derive(Debug, PartialEq, Eq, scale::Encode, scale::Decode)]
#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
#[derive(Debug, PartialEq, Eq)]
#[ink::scale_derive(Encode, Decode, TypeInfo)]
pub enum PSP22Error {
/// Custom error type for implementation-based errors.
Custom(String),
Expand Down
19 changes: 19 additions & 0 deletions events.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use ink::primitives::AccountId;

#[ink::event]
pub struct Approval {
#[ink(topic)]
pub owner: AccountId,
#[ink(topic)]
pub spender: AccountId,
pub amount: u128,
}

#[ink::event]
pub struct Transfer {
#[ink(topic)]
pub from: Option<AccountId>,
#[ink(topic)]
pub to: Option<AccountId>,
pub value: u128,
}
55 changes: 13 additions & 42 deletions lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

mod data;
mod errors;
mod events;
mod testing;
mod traits;

pub use data::{PSP22Data, PSP22Event};
pub use errors::PSP22Error;
pub use events::{Approval, Transfer};
pub use traits::{PSP22Burnable, PSP22Metadata, PSP22Mintable, PSP22};

// An example code of a smart contract using PSP22Data struct to implement
Expand All @@ -15,12 +17,11 @@ pub use traits::{PSP22Burnable, PSP22Metadata, PSP22Mintable, PSP22};
// Any contract can be easily enriched to act as PSP22 token by:
// (1) adding PSP22Data to contract storage
// (2) properly initializing it
// (3) defining the correct Transfer and Approval events
// (4) implementing PSP22 trait based on PSP22Data methods
// (5) properly emitting resulting events
// (3) implementing PSP22 trait based on PSP22Data methods
// (4) properly emitting resulting events
//
// It is a good practice to also implement the optional PSP22Metadata extension (6)
// and include unit tests (7).
// It is a good practice to also implement the optional PSP22Metadata extension (5)
// and include unit tests (6).
#[cfg(feature = "contract")]
#[ink::contract]
mod token {
Expand Down Expand Up @@ -51,50 +52,19 @@ mod token {
}
}

// A helper function translating a vector of PSP22Events into the proper
// ink event types (defined internally in this contract) and emitting them.
// (5)
// A helper function emitting events contained in a vector of PSP22Events.
// (4)
fn emit_events(&self, events: Vec<PSP22Event>) {
for event in events {
match event {
PSP22Event::Transfer { from, to, value } => {
self.env().emit_event(Transfer { from, to, value })
}
PSP22Event::Approval {
owner,
spender,
amount,
} => self.env().emit_event(Approval {
owner,
spender,
amount,
}),
PSP22Event::Transfer(e) => self.env().emit_event(e),
PSP22Event::Approval(e) => self.env().emit_event(e),
}
}
}
}

// (3)
#[ink(event)]
pub struct Approval {
#[ink(topic)]
owner: AccountId,
#[ink(topic)]
spender: AccountId,
amount: u128,
}

// (3)
#[ink(event)]
pub struct Transfer {
#[ink(topic)]
from: Option<AccountId>,
#[ink(topic)]
to: Option<AccountId>,
value: u128,
}

// (4)
impl PSP22 for Token {
#[ink(message)]
fn total_supply(&self) -> u128 {
Expand Down Expand Up @@ -172,7 +142,7 @@ mod token {
}
}

// (6)
// (5)
impl PSP22Metadata for Token {
#[ink(message)]
fn token_name(&self) -> Option<String> {
Expand All @@ -188,9 +158,10 @@ mod token {
}
}

// (7)
// (6)
#[cfg(test)]
mod tests {
use super::Token;
crate::tests!(Token, (|supply| Token::new(supply, None, None, 0)));
}
}
Loading