Skip to content

Commit

Permalink
feat(erc20): implement Metadata extension
Browse files Browse the repository at this point in the history
  • Loading branch information
alexfertel committed Mar 28, 2024
1 parent 1c4b23b commit 7cf666a
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 4 deletions.
1 change: 1 addition & 0 deletions contracts/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ wavm-shims = { path = "../lib/wavm-shims" }
[features]
default = []
erc20 = []
erc20_metadata = ["erc20"]
erc721 = []

[lib]
Expand Down
125 changes: 125 additions & 0 deletions contracts/src/erc20/extensions/metadata.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
//! Optional metadata of the ERC-20 standard.
use alloc::string::String;
use alloy_primitives::U8;
use stylus_proc::{external, sol_storage};

/// Number of decimals used by default on implementors of [`Metadata`].
pub const DEFAULT_DECIMALS: u8 = 18;

sol_storage! {
/// Optional metadata of the ERC-20 standard.
pub struct Metadata {
/// Token name.
string _name;
/// Token symbol.
string _symbol;
/// Token symbol.
uint8 _decimals;
}
}

#[external]
impl Metadata {
/// Initializes a [`Metadata`] instance with the passed `name` and
/// `symbol`. It also sets `decimals` to [`DEFAULT_DECIMALS`].
///
/// Note that there are no setters for these fields. This makes them
/// immutable: they can only be set once at construction.
///
/// # Arguments
///
/// * `&mut self` - Write access to the contract's state.
/// * `name` - The name of the token.
/// * `symbol` - The symbol of the token.
pub fn constructor(&mut self, name: String, symbol: String) {
self._name.set_str(name);
self._symbol.set_str(symbol);
self._decimals.set(U8::from(DEFAULT_DECIMALS));
}

/// Returns the name of the token.
///
/// # Arguments
///
/// * `&self` - Read access to the contract's state.
pub fn name(&self) -> String {
self._name.get_string()
}

/// Returns the symbol of the token, usually a shorter version of the name.
///
/// # Arguments
///
/// * `&self` - Read access to the contract's state.
pub fn symbol(&self) -> String {
self._symbol.get_string()
}

/// Returns the number of decimals used to get a user-friendly
/// representation of values of this token.
///
/// For example, if `decimals` equals `2`, a balance of `505` tokens should
/// be displayed to a user as `5.05` (`505 / 10 ** 2`).
///
/// Tokens usually opt for a value of `18`, imitating the relationship
/// between Ether and Wei. This is the default value returned by this
/// function ([`DEFAULT_DECIMALS`]), unless it's overridden.
///
/// NOTE: This information is only used for *display* purposes: in
/// no way it affects any of the arithmetic of the contract, including
/// [`ERC20::balance_of`] and [`ERC20::transfer`].
pub fn decimals(&self) -> u8 {
// TODO: Use `U8` an avoid the conversion once https://github.com/OffchainLabs/stylus-sdk-rs/issues/117
// gets resolved.
self._decimals.get().byte(0)
}
}

#[cfg(test)]
mod tests {
use alloy_primitives::U256;
use stylus_sdk::storage::{StorageString, StorageType, StorageU8};

#[allow(unused_imports)]
use crate::test_utils;

use super::Metadata;
use super::DEFAULT_DECIMALS;

impl Default for Metadata {
fn default() -> Self {
let root = U256::ZERO;
Metadata {
_name: unsafe { StorageString::new(root, 0) },
_symbol: unsafe {
StorageString::new(root + U256::from(32), 0)
},
_decimals: unsafe { StorageU8::new(root + U256::from(64), 0) },
}
}
}

#[test]
fn constructs() {
test_utils::with_storage::<Metadata>(|meta| {
let name = meta.name();
let symbol = meta.symbol();
let decimals = meta.decimals();
assert_eq!(name, "");
assert_eq!(symbol, "");
assert_eq!(decimals, 0);

const NAME: &str = "Meta";
const SYMBOL: &str = "Symbol";

meta.constructor(NAME.to_owned(), SYMBOL.to_owned());

let name = meta.name();
let symbol = meta.symbol();
let decimals = meta.decimals();
assert_eq!(name, NAME);
assert_eq!(symbol, SYMBOL);
assert_eq!(decimals, DEFAULT_DECIMALS);
})
}
}
2 changes: 2 additions & 0 deletions contracts/src/erc20/extensions/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#[cfg(any(test, erc20_metadata))]
pub mod metadata;
8 changes: 4 additions & 4 deletions contracts/src/erc20/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use stylus_sdk::{
stylus_proc::{external, sol_storage},
};

pub mod extensions;

sol! {
/// Emitted when `value` tokens are moved from one account (`from`) to
/// another (`to`).
Expand Down Expand Up @@ -356,17 +358,15 @@ mod tests {
impl Default for ERC20 {
fn default() -> Self {
let root = U256::ZERO;
let token = ERC20 {
ERC20 {
_balances: unsafe { StorageMap::new(root, 0) },
_allowances: unsafe {
StorageMap::new(root + U256::from(32), 0)
},
_total_supply: unsafe {
StorageU256::new(root + U256::from(64), 0)
},
};

token
}
}
}

Expand Down

0 comments on commit 7cf666a

Please sign in to comment.