Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
15 changes: 12 additions & 3 deletions examples/fungible-allowlist/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
//! SEP-41-compliant fungible token. It includes essential features such as
//! controlled token transfers by an admin who can allow or disallow specific
//! accounts.
//!
//! It also demonstrates composing the contract type: `Compose<(AllowList,
//! TotalSupply)>` pairs the allowlist transfer policy with total supply
//! tracking.

use soroban_sdk::{
contract, contractimpl, symbol_short, Address, Env, MuxedAddress, String, Symbol, Vec,
Expand All @@ -13,7 +17,9 @@ use stellar_macros::only_role;
use stellar_tokens::fungible::{
allowlist::{AllowList, FungibleAllowList},
burnable::FungibleBurnable,
Base, Compose, FungibleToken,
combinations::Compose,
total_supply::{self, FungibleTotalSupply, TotalSupply},
Base, FungibleToken,
};

#[contract]
Expand All @@ -40,15 +46,18 @@ impl ExampleContract {
AllowList::allow_user(e, &admin);

// Mint initial supply to the admin
Base::mint(e, &admin, initial_supply);
total_supply::mint(e, &admin, initial_supply);
}
}

#[contractimpl(contracttrait)]
impl FungibleToken for ExampleContract {
type ContractType = Compose<(AllowList,)>;
type ContractType = Compose<(AllowList, TotalSupply)>;
}

#[contractimpl(contracttrait)]
impl FungibleTotalSupply for ExampleContract {}

#[contractimpl(contracttrait)]
impl FungibleAllowList for ExampleContract {
#[only_role(operator, "manager")]
Expand Down
12 changes: 8 additions & 4 deletions examples/fungible-capped/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
use soroban_sdk::{contract, contractimpl, Address, Env, MuxedAddress, String};
use stellar_tokens::fungible::{
capped::{check_cap, set_cap},
Base, Compose, FungibleToken,
total_supply::{self, FungibleTotalSupply, TotalSupply},
Compose, FungibleToken,
};

#[contract]
Expand All @@ -23,12 +24,15 @@ impl ExampleContract {
}

pub fn mint(e: &Env, to: Address, amount: i128) {
check_cap(e, amount, Base::total_supply(e));
Base::mint(e, &to, amount);
check_cap(e, amount, total_supply::total_supply(e));
total_supply::mint(e, &to, amount);
}
}

#[contractimpl(contracttrait)]
impl FungibleToken for ExampleContract {
type ContractType = Compose<(Base,)>;
type ContractType = Compose<(TotalSupply,)>;
}

#[contractimpl(contracttrait)]
impl FungibleTotalSupply for ExampleContract {}
19 changes: 11 additions & 8 deletions examples/fungible-pausable/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ use soroban_sdk::{
};
use stellar_contract_utils::pausable::{self as pausable, Pausable};
use stellar_macros::when_not_paused;
use stellar_tokens::fungible::{burnable::FungibleBurnable, Base, Compose, FungibleToken};
use stellar_tokens::fungible::{
burnable::FungibleBurnable,
total_supply::{self, FungibleTotalSupply, TotalSupply},
Base, Compose, ContractOverrides, FungibleToken,
};

pub const OWNER: Symbol = symbol_short!("OWNER");

Expand All @@ -39,7 +43,7 @@ impl ExampleContract {
initial_supply: i128,
) {
Base::set_metadata(e, 18, name, symbol);
Base::mint(e, &owner, initial_supply);
total_supply::mint(e, &owner, initial_supply);
e.storage().instance().set(&OWNER, &owner);
}

Expand All @@ -51,7 +55,7 @@ impl ExampleContract {
let owner: Address = e.storage().instance().get(&OWNER).expect("owner should be set");
owner.require_auth();

Base::mint(e, &to, amount);
total_supply::mint(e, &to, amount);
}
}

Expand Down Expand Up @@ -90,11 +94,7 @@ impl Pausable for ExampleContract {

#[contractimpl]
impl FungibleToken for ExampleContract {
type ContractType = Compose<(Base,)>;

fn total_supply(e: &Env) -> i128 {
Self::ContractType::total_supply(e)
}
type ContractType = Compose<(TotalSupply,)>;

fn balance(e: &Env, account: Address) -> i128 {
Self::ContractType::balance(e, &account)
Expand Down Expand Up @@ -131,6 +131,9 @@ impl FungibleToken for ExampleContract {
}
}

#[contractimpl(contracttrait)]
impl FungibleTotalSupply for ExampleContract {}

#[contractimpl]
impl FungibleBurnable for ExampleContract {
#[when_not_paused]
Expand Down
5 changes: 4 additions & 1 deletion examples/fungible-vault/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use soroban_sdk::{contract, contractimpl, Address, Env, MuxedAddress, String};
use stellar_tokens::{
fungible::{Base, Compose, FungibleToken},
fungible::{total_supply::FungibleTotalSupply, Base, Compose, FungibleToken},
vault::{FungibleVault, Vault},
};

Expand Down Expand Up @@ -38,5 +38,8 @@ impl FungibleToken for ExampleContract {
}
}

#[contractimpl(contracttrait)]
impl FungibleTotalSupply for ExampleContract {}

#[contractimpl(contracttrait)]
impl FungibleVault for ExampleContract {}
7 changes: 6 additions & 1 deletion examples/fungible-votes/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ use stellar_access::ownable::{set_owner, Ownable};
use stellar_governance::votes::Votes;
use stellar_macros::only_owner;
use stellar_tokens::fungible::{
burnable::FungibleBurnable, votes::FungibleVotes, Base, Compose, FungibleToken,
burnable::FungibleBurnable, total_supply::FungibleTotalSupply, votes::FungibleVotes, Base,
Compose, FungibleToken,
};

#[contract]
Expand All @@ -27,6 +28,10 @@ impl FungibleToken for ExampleContract {
type ContractType = Compose<(FungibleVotes,)>;
}

// The total supply is served from the voting checkpoints.
#[contractimpl(contracttrait)]
impl FungibleTotalSupply for ExampleContract {}

#[contractimpl(contracttrait)]
impl Votes for ExampleContract {}

Expand Down
5 changes: 4 additions & 1 deletion examples/rwa/token/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use stellar_access::access_control::{self as access_control, AccessControl};
use stellar_contract_utils::pausable::{self as pausable, Pausable};
use stellar_macros::{only_admin, only_role};
use stellar_tokens::{
fungible::{Base, Compose, FungibleToken},
fungible::{total_supply::FungibleTotalSupply, Base, Compose, FungibleToken},
rwa::{RWAToken, RWA},
};

Expand Down Expand Up @@ -57,6 +57,9 @@ impl FungibleToken for RWATokenContract {
type ContractType = Compose<(RWA,)>;
}

#[contractimpl(contracttrait)]
impl FungibleTotalSupply for RWATokenContract {}

#[contractimpl(contracttrait)]
impl RWAToken for RWATokenContract {
#[only_role(operator, "manager")]
Expand Down
10 changes: 9 additions & 1 deletion packages/tokens/src/fungible/extensions/allowlist/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ pub use storage::AllowList;

use crate::fungible::FungibleToken;

/// Marker trait for contract types that enforce the allowlist transfer
/// policy: [`AllowList`] itself, or a combination resolved by
/// [`crate::fungible::combinations::Compose`] that includes it, e.g.
/// `Compose<(AllowList, TotalSupply)>`.
pub trait AllowListContractType {}

impl AllowListContractType for AllowList {}

/// AllowList Trait for Fungible Token
///
/// The `FungibleAllowList` trait extends the `FungibleToken` trait to
Expand All @@ -30,7 +38,7 @@ use crate::fungible::FungibleToken;
/// "storage.rs", because the authorizations are to be handled in the access
/// control helpers or directly implemented
#[contracttrait]
pub trait FungibleAllowList: FungibleToken<ContractType = AllowList> {
pub trait FungibleAllowList: FungibleToken<ContractType: AllowListContractType> {
/// Returns the allowed status of an account.
///
/// # Arguments
Expand Down
10 changes: 9 additions & 1 deletion packages/tokens/src/fungible/extensions/blocklist/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ pub use storage::BlockList;

use crate::fungible::FungibleToken;

/// Marker trait for contract types that enforce the blocklist transfer
/// policy: [`BlockList`] itself, or a combination resolved by
/// [`crate::fungible::combinations::Compose`] that includes it, e.g.
/// `Compose<(BlockList, TotalSupply)>`.
pub trait BlockListContractType {}

impl BlockListContractType for BlockList {}

/// BlockList Trait for Fungible Token
///
/// The `FungibleBlockList` trait extends the `FungibleToken` trait to
Expand All @@ -30,7 +38,7 @@ use crate::fungible::FungibleToken;
/// "storage.rs", because the authorizations are to be handled in the access
/// control helpers or directly implemented.
#[contracttrait]
pub trait FungibleBlockList: FungibleToken<ContractType = BlockList> {
pub trait FungibleBlockList: FungibleToken<ContractType: BlockListContractType> {
/// Returns the blocked status of an account.
///
/// # Arguments
Expand Down
10 changes: 6 additions & 4 deletions packages/tokens/src/fungible/extensions/burnable/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ use crate::fungible::{overrides::BurnableOverrides, FungibleToken};
/// background.
#[contracttrait]
pub trait FungibleBurnable: FungibleToken<ContractType: BurnableOverrides> {
/// Destroys `amount` of tokens from `from`. Updates the total
/// supply accordingly.
/// Destroys `amount` of tokens from `from`. For supply-aware contract
/// types (e.g. [`crate::fungible::total_supply::TotalSupply`]), the total
/// supply is updated accordingly.
///
/// # Arguments
///
Expand All @@ -64,8 +65,9 @@ pub trait FungibleBurnable: FungibleToken<ContractType: BurnableOverrides> {
Self::ContractType::burn(e, &from, amount);
}

/// Destroys `amount` of tokens from `from`. Updates the total
/// supply accordingly.
/// Destroys `amount` of tokens from `from`. For supply-aware contract
/// types (e.g. [`crate::fungible::total_supply::TotalSupply`]), the total
/// supply is updated accordingly.
///
/// # Arguments
///
Expand Down
4 changes: 1 addition & 3 deletions packages/tokens/src/fungible/extensions/burnable/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ use soroban_sdk::{Address, Env};
use crate::fungible::{extensions::burnable::emit_burn, Base};

impl Base {
/// Destroys `amount` of tokens from `from`. Updates the total
/// supply accordingly.
/// Destroys `amount` of tokens from `from`.
///
/// # Arguments
///
Expand Down Expand Up @@ -32,7 +31,6 @@ impl Base {

/// Destroys `amount` of tokens from `from` using the allowance mechanism.
/// `amount` is then deducted from `spender` allowance.
/// Updates the total supply accordingly.
///
/// # Arguments
///
Expand Down
5 changes: 0 additions & 5 deletions packages/tokens/src/fungible/extensions/burnable/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ fn burn_works() {
Base::mint(&e, &account, 100);
Base::burn(&e, &account, 50);
assert_eq!(Base::balance(&e, &account), 50);
assert_eq!(Base::total_supply(&e), 50);

let mut event_assert = EventAssertion::new(&e, address.clone());
event_assert.assert_event_count(2);
Expand All @@ -40,7 +39,6 @@ fn burn_with_allowance_works() {
Base::burn_from(&e, &spender, &owner, 30);
assert_eq!(Base::balance(&e, &owner), 70);
assert_eq!(Base::balance(&e, &spender), 0);
assert_eq!(Base::total_supply(&e), 70);

let mut event_assert = EventAssertion::new(&e, address.clone());
event_assert.assert_event_count(3);
Expand All @@ -60,7 +58,6 @@ fn burn_with_insufficient_balance_panics() {
e.as_contract(&address, || {
Base::mint(&e, &account, 100);
assert_eq!(Base::balance(&e, &account), 100);
assert_eq!(Base::total_supply(&e), 100);
Base::burn(&e, &account, 101);
});
}
Expand All @@ -76,7 +73,6 @@ fn burn_with_no_allowance_panics() {
e.as_contract(&address, || {
Base::mint(&e, &owner, 100);
assert_eq!(Base::balance(&e, &owner), 100);
assert_eq!(Base::total_supply(&e), 100);
Base::burn_from(&e, &spender, &owner, 50);
});
}
Expand All @@ -94,7 +90,6 @@ fn burn_with_insufficient_allowance_panics() {
Base::approve(&e, &owner, &spender, 50, 100);
assert_eq!(Base::allowance(&e, &owner, &spender), 50);
assert_eq!(Base::balance(&e, &owner), 100);
assert_eq!(Base::total_supply(&e), 100);
Base::burn_from(&e, &spender, &owner, 60);
});
}
7 changes: 6 additions & 1 deletion packages/tokens/src/fungible/extensions/capped/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@
/// Instead, the `capped` extension modifies the business logic of the `mint`
/// function to enforce a supply cap.
///
/// The cap is checked against the tracked total supply, so this extension is
/// meant to be used together with
/// [`crate::fungible::total_supply::FungibleTotalSupply`] (or another
/// supply-aware contract type).
///
/// This module provides the following helper functions:
/// - `set_cap`: Sets the maximum token supply.
/// - `query_cap`: Returns the maximum token supply.
/// - `check_cap`: Panics if minting a specified `amount` added to the given
/// `total_supply` would exceed the cap. Should be used before calling
/// `mint()`.
/// [`crate::fungible::total_supply::mint`].
mod storage;
pub use self::storage::{check_cap, query_cap, set_cap, CapStorageKey};
#[cfg(test)]
Expand Down
Loading
Loading