-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* init fungible * finize fungible trait and storage * improve mod doc * change owner to from in transfers * add metadata functions to trait and fix docs * add tests for storage functions * add storage bumps * remove bump_instance * change visibility of EXTEND consts to public
- Loading branch information
Showing
27 changed files
with
5,045 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
resolver = "2" | ||
members = [ | ||
"contracts/utils/*", | ||
"contracts/token/*", | ||
"examples/*", | ||
] | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
[package] | ||
name = "openzeppelin-fungible-token" | ||
edition.workspace = true | ||
license.workspace = true | ||
repository.workspace = true | ||
publish = false | ||
version.workspace = true | ||
|
||
[lib] | ||
crate-type = ["lib", "cdylib"] | ||
doctest = false | ||
|
||
[dependencies] | ||
soroban-sdk = { workspace = true } | ||
|
||
[dev-dependencies] | ||
soroban-sdk = { workspace = true, features = ["testutils"] } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,238 @@ | ||
use soroban_sdk::{contractclient, contracterror, symbol_short, Address, Env, String}; | ||
|
||
/// Vanilla Fungible Token Trait | ||
/// | ||
/// The `FungibleToken` trait defines the core functionality for fungible | ||
/// tokens, adhering to SEP-41. It provides a standard interface for managing | ||
/// balances, allowances, and metadata associated with fungible tokens. | ||
/// Additionally, this trait includes the `total_supply()` function, which is | ||
/// not part of SEP-41 but is commonly used in token contracts. | ||
/// | ||
/// To fully comply with the SEP-41 specification one have to implement the | ||
/// `Burnable` trait in addition to this one. SEP-41 mandates support for token | ||
/// burning to be considered compliant. | ||
#[contractclient(name = "FungibleTokenClient")] | ||
pub trait FungibleToken { | ||
/// Returns the total amount of tokens in circulation. | ||
/// | ||
/// # Arguments | ||
/// | ||
/// * `e` - Access to the Soroban environment. | ||
/// | ||
/// # Notes | ||
/// | ||
/// We recommend using the [`crate::storage::total_supply()`] function from | ||
/// the `storage` module when implementing this function. | ||
fn total_supply(e: Env) -> i128; | ||
|
||
/// Returns the amount of tokens held by `account`. | ||
/// | ||
/// # Arguments | ||
/// | ||
/// * `e` - Access to the Soroban environment. | ||
/// * `account` - The address for which the balance is being queried. | ||
/// | ||
/// # Notes | ||
/// | ||
/// We recommend using the [`crate::storage::balance()`] function from | ||
/// the `storage` module when implementing this function. | ||
fn balance(e: Env, account: Address) -> i128; | ||
|
||
/// Returns the amount of tokens a `spender` is allowed to spend on behalf | ||
/// of an `owner`. | ||
/// | ||
/// # Arguments | ||
/// | ||
/// * `e` - Access to Soroban environment. | ||
/// * `owner` - The address holding the tokens. | ||
/// * `spender` - The address authorized to spend the tokens. | ||
/// | ||
/// # Notes | ||
/// | ||
/// We recommend using the [`crate::storage::allowance()`] function from | ||
/// the `storage` module when implementing this function. | ||
fn allowance(e: Env, owner: Address, spender: Address) -> i128; | ||
|
||
/// Transfers a `value` amount of tokens from `from` to `to`. | ||
/// | ||
/// # Arguments | ||
/// | ||
/// * `e` - Access to Soroban environment. | ||
/// * `from` - The address holding the tokens. | ||
/// * `to` - The address receiving the transferred tokens. | ||
/// * `value` - The value of tokens to be transferred. | ||
/// | ||
/// # Errors | ||
/// | ||
/// * [`FungibleTokenError::InsufficientBalance`] - When attempting to | ||
/// transfer more tokens than `from` current balance. | ||
/// | ||
/// # Events | ||
/// | ||
/// * topics - `["transfer", from: Address, to: Address]` | ||
/// * data - `[value: i128]` | ||
/// | ||
/// # Notes | ||
/// | ||
/// We recommend using the [`crate::storage::transfer()`] function from | ||
/// the `storage` module when implementing this function. | ||
fn transfer(e: Env, from: Address, to: Address, value: i128); | ||
|
||
/// Transfers a `value` amount of tokens from `from` to `to` using the | ||
/// allowance mechanism. `value` is then deducted from `spender allowance. | ||
/// | ||
/// # Arguments | ||
/// | ||
/// * `e` - Access to Soroban environment. | ||
/// * `spender` - The address authorizing the transfer, and having its | ||
/// allowance consumed during the transfer. | ||
/// * `from` - The address holding the tokens which will be transferred. | ||
/// * `to` - The address receiving the transferred tokens. | ||
/// * `value` - The amount of tokens to be transferred. | ||
/// | ||
/// # Errors | ||
/// | ||
/// * [`FungibleTokenError::InsufficientBalance`] - When attempting to | ||
/// transfer more tokens than `from` current balance. | ||
/// * [`FungibleTokenError::InsufficientAllowance`] - When attempting to | ||
/// transfer more tokens than `spender` current allowance. | ||
/// | ||
/// | ||
/// # Events | ||
/// | ||
/// * topics - `["transfer", from: Address, to: Address]` | ||
/// * data - `[value: i128]` | ||
/// | ||
/// # Notes | ||
/// | ||
/// We recommend using the [`crate::storage::transfer_from()`] function from | ||
/// the `storage` module when implementing this function. | ||
fn transfer_from(e: Env, spender: Address, from: Address, to: Address, value: i128); | ||
|
||
/// Sets the amount of tokens a `spender` is allowed to spend on behalf of | ||
/// an `owner`. Overrides any existing allowance set between `spender` and | ||
/// `owner`. | ||
/// | ||
/// # Arguments | ||
/// | ||
/// * `e` - Access to Soroban environment. | ||
/// * `owner` - The address holding the tokens. | ||
/// * `spender` - The address authorized to spend the tokens. | ||
/// * `value` - The amount of tokens made available to `spender`. | ||
/// * `live_until_ledger` - The ledger number at which the allowance | ||
/// expires. | ||
/// | ||
/// # Errors | ||
/// | ||
/// * [`FungibleTokenError::InvalidLiveUntilLedger`] - Occurs when | ||
/// attempting to set `live_until_ledger` that is less than the current | ||
/// ledger number and greater than `0`. | ||
/// | ||
/// # Events | ||
/// | ||
/// * topics - `["approve", from: Address, spender: Address]` | ||
/// * data - `[value: i128, live_until_ledger: u32]` | ||
/// | ||
/// # Notes | ||
/// | ||
/// We recommend using the [`crate::storage::approve()`] function from | ||
/// the `storage` module when implementing this function. | ||
fn approve(e: Env, owner: Address, spender: Address, value: i128, live_until_ledger: u32); | ||
|
||
/// Returns the number of decimals used to represent amounts of this token. | ||
/// | ||
/// # Arguments | ||
/// | ||
/// * `e` - Access to Soroban environment. | ||
/// | ||
/// # Notes | ||
/// | ||
/// We recommend using the [`crate::metadata::decimals()`] function from | ||
/// the `metadata` module when implementing this function. | ||
fn decimals(e: Env) -> u32; | ||
|
||
/// Returns the name for this token. | ||
/// | ||
/// # Arguments | ||
/// | ||
/// * `e` - Access to Soroban environment. | ||
/// | ||
/// # Notes | ||
/// | ||
/// We recommend using the [`crate::metadata::name()`] function from | ||
/// the `metadata` module when implementing this function. | ||
fn name(e: Env) -> String; | ||
|
||
/// Returns the symbol for this token. | ||
/// | ||
/// # Arguments | ||
/// | ||
/// * `e` - Access to Soroban environment. | ||
/// | ||
/// # Notes | ||
/// | ||
/// We recommend using the [`crate::metadata::symbol()`] function from | ||
/// the `metadata` module when implementing this function. | ||
fn symbol(e: Env) -> String; | ||
} | ||
|
||
// ################## ERRORS ################## | ||
|
||
#[contracterror] | ||
#[repr(u32)] | ||
pub enum FungibleTokenError { | ||
/// Indicates an error related to the current balance of account from which | ||
/// tokens are expected to be transferred. | ||
InsufficientBalance = 1, | ||
/// Indicates a failure with the allowance mechanism when a given spender | ||
/// doesn't have enough allowance. | ||
InsufficientAllowance = 2, | ||
/// Indicates an invalid value for `live_until_ledger` when setting an | ||
/// allowance. | ||
InvalidLiveUntilLedger = 3, | ||
} | ||
|
||
// ################## EVENTS ################## | ||
|
||
/// Emits an event indicating a transfer of tokens. | ||
/// | ||
/// # Arguments | ||
/// | ||
/// * `e` - Access to Soroban environment. | ||
/// * `from` - The address holding the tokens. | ||
/// * `to` - The address receiving the transferred tokens. | ||
/// * `value` - The value of tokens to be transferred. | ||
/// | ||
/// # Events | ||
/// | ||
/// * topics - `["transfer", from: Address, to: Address]` | ||
/// * data - `[value: i128]` | ||
pub fn emit_transfer(e: &Env, from: &Address, to: &Address, value: i128) { | ||
let topics = (symbol_short!("transfer"), from, to); | ||
e.events().publish(topics, value) | ||
} | ||
|
||
/// Emits an event indicating an allowance was set. | ||
/// | ||
/// # Arguments | ||
/// | ||
/// * `e` - Access to Soroban environment. | ||
/// * `owner` - The address holding the tokens. | ||
/// * `spender` - The address authorized to spend the tokens. | ||
/// * `value` - The amount of tokens made available to `spender`. | ||
/// * `live_until_ledger` - The ledger number at which the allowance expires. | ||
/// | ||
/// # Events | ||
/// | ||
/// * topics - `["approve", owner: Address, spender: Address]` | ||
/// * data - `[value: i128, live_until_ledger: u32]` | ||
pub fn emit_approve( | ||
e: &Env, | ||
owner: &Address, | ||
spender: &Address, | ||
value: i128, | ||
live_until_ledger: u32, | ||
) { | ||
let topics = (symbol_short!("approve"), owner, spender); | ||
e.events().publish(topics, (value, live_until_ledger)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
//! # Fungible Token Contract Module. | ||
//! | ||
//! Implements utilities for handling fungible tokens in a Soroban contract. | ||
//! | ||
//! This module provides essential storage functionalities required for managing | ||
//! balances, allowances, and total supply of fungible tokens. | ||
//! | ||
//! ## Design Overview | ||
//! | ||
//! This module is structured to provide flexibility to developers by splitting | ||
//! functionalities into higher-level and lower-level operations: | ||
//! | ||
//! - **High-Level Functions**: These include all necessary checks, | ||
//! verifications, authorizations, state-changing logic, and event emissions. | ||
//! They simplify usage by handling core logic securely. Users can directly | ||
//! call these functions for typical token operations without worrying about | ||
//! implementation details. | ||
//! | ||
//! - **Low-Level Functions**: These offer granular control for developers who | ||
//! need to compose their own workflows. Such functions expose internal | ||
//! mechanisms and require the caller to handle verifications and | ||
//! authorizations manually. | ||
//! | ||
//! By offering this dual-layered approach, developers can choose between | ||
//! convenience and customization, depending on their project requirements. | ||
//! | ||
//! ## Base Module and Extensions | ||
//! | ||
//! The base module implements: | ||
//! | ||
//! - Total supply management | ||
//! - Transfers and allowances | ||
//! | ||
//! To extend functionality, the module supports the following optional | ||
//! features: | ||
//! | ||
//! - Metadata: Provides additional information about the token, such as name, | ||
//! symbol, and decimals. | ||
//! - Mintable: Allows authorized entities to mint new tokens and increase the | ||
//! total supply. | ||
//! - Burnable: Enables token holders to destroy their tokens, reducing the | ||
//! total supply. | ||
//! | ||
//! ## Compatibility and Compliance | ||
//! | ||
//! The module is designed to ensure full compatibility with SEP-0041, making it | ||
//! easy to integrate into Soroban-based applications. It also closely mirrors | ||
//! the Ethereum ERC-20 standard, facilitating cross-ecosystem familiarity and | ||
//! ease of use. | ||
//! | ||
//! ## Notes for Developers | ||
//! | ||
//! - **Security Considerations**: While high-level functions handle necessary | ||
//! checks, users of low-level functions must take extra care to ensure | ||
//! correctness and security. | ||
//! - **Composable Design**: The modular structure encourages developers to | ||
//! extend functionality by combining provided primitives or creating custom | ||
//! extensions. | ||
#![no_std] | ||
|
||
pub mod fungible; | ||
pub mod storage; | ||
|
||
mod test; |
Oops, something went wrong.