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
6 changes: 4 additions & 2 deletions Architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ This design makes it **impossible** to implement conflicting extensions:
```rust
// ✅ This works - using Enumerable
impl NonFungibleToken for MyContract {
type ContractType = Enumerable;
type ContractType = Compose<(Enumerable,)>;
// ... implementations
}
impl NonFungibleEnumerable for MyContract {
Expand Down Expand Up @@ -199,7 +199,9 @@ pub struct MyToken;

#[contractimpl(contracttrait)]
impl FungibleToken for MyToken {
type ContractType = Base;
// `Compose` resolves the list of contract types (the behavior-overriding
// ones, not additive extensions like `Burnable`) to the right one.
type ContractType = Compose<(Base,)>;
// The macro fills in every method body, no manual overrides needed.
// Alternatively, custom overrides can be provided here (optional)
}
Expand Down
4 changes: 2 additions & 2 deletions examples/fungible-allowlist/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use stellar_macros::only_role;
use stellar_tokens::fungible::{
allowlist::{AllowList, FungibleAllowList},
burnable::FungibleBurnable,
Base, FungibleToken,
Base, Compose, FungibleToken,
};

#[contract]
Expand Down Expand Up @@ -46,7 +46,7 @@ impl ExampleContract {

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

#[contractimpl(contracttrait)]
Expand Down
4 changes: 2 additions & 2 deletions examples/fungible-blocklist/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use stellar_macros::only_role;
use stellar_tokens::fungible::{
blocklist::{BlockList, FungibleBlockList},
burnable::FungibleBurnable,
Base, FungibleToken,
Base, Compose, FungibleToken,
};

#[contract]
Expand Down Expand Up @@ -52,7 +52,7 @@ impl ExampleContract {

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

#[contractimpl(contracttrait)]
Expand Down
4 changes: 2 additions & 2 deletions examples/fungible-capped/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
use soroban_sdk::{contract, contractimpl, Address, Env, MuxedAddress, String};
use stellar_tokens::fungible::{
capped::{check_cap, set_cap},
Base, FungibleToken,
Base, Compose, FungibleToken,
};

#[contract]
Expand All @@ -30,5 +30,5 @@ impl ExampleContract {

#[contractimpl(contracttrait)]
impl FungibleToken for ExampleContract {
type ContractType = Base;
type ContractType = Compose<(Base,)>;
}
4 changes: 2 additions & 2 deletions examples/fungible-governor-timelock/token/src/token.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_access::ownable::{set_owner, Ownable};
use stellar_governance::votes::Votes;
use stellar_macros::only_owner;
use stellar_tokens::fungible::{votes::FungibleVotes, Base, FungibleToken};
use stellar_tokens::fungible::{votes::FungibleVotes, Base, Compose, FungibleToken};

#[contract]
pub struct TokenContract;
Expand All @@ -27,7 +27,7 @@ impl TokenContract {

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

#[contractimpl(contracttrait)]
Expand Down
4 changes: 2 additions & 2 deletions examples/fungible-governor/token/src/token.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_access::ownable::{set_owner, Ownable};
use stellar_governance::votes::Votes;
use stellar_macros::only_owner;
use stellar_tokens::fungible::{votes::FungibleVotes, Base, FungibleToken};
use stellar_tokens::fungible::{votes::FungibleVotes, Base, Compose, FungibleToken};

#[contract]
pub struct TokenContract;
Expand All @@ -27,7 +27,7 @@ impl TokenContract {

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

#[contractimpl(contracttrait)]
Expand Down
4 changes: 2 additions & 2 deletions examples/fungible-pausable/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ 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, FungibleToken};
use stellar_tokens::fungible::{burnable::FungibleBurnable, Base, Compose, FungibleToken};

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

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

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

fn total_supply(e: &Env) -> i128 {
Self::ContractType::total_supply(e)
Expand Down
4 changes: 2 additions & 2 deletions 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, FungibleToken},
fungible::{Base, Compose, FungibleToken},
vault::{FungibleVault, Vault},
};

Expand All @@ -29,7 +29,7 @@ impl ExampleContract {

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

// Allows override of decimals and other base functions.

Expand Down
4 changes: 2 additions & 2 deletions examples/fungible-votes/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ 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, FungibleToken,
burnable::FungibleBurnable, votes::FungibleVotes, Base, Compose, FungibleToken,
};

#[contract]
Expand All @@ -24,7 +24,7 @@ impl ExampleContract {

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

#[contractimpl(contracttrait)]
Expand Down
6 changes: 4 additions & 2 deletions examples/nft-access-control/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
use soroban_sdk::{contract, contractimpl, vec, Address, Env, String, Symbol, Vec};
use stellar_access::access_control::{set_admin, AccessControl};
use stellar_macros::{has_any_role, has_role, only_admin, only_any_role, only_role};
use stellar_tokens::non_fungible::{burnable::NonFungibleBurnable, Base, NonFungibleToken};
use stellar_tokens::non_fungible::{
burnable::NonFungibleBurnable, Base, Compose, NonFungibleToken,
};

#[contract]
pub struct ExampleContract;
Expand Down Expand Up @@ -46,7 +48,7 @@ impl ExampleContract {

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

// for this contract, the `burn*` functions are only meant to be called by
Expand Down
4 changes: 2 additions & 2 deletions examples/nft-consecutive/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use soroban_sdk::{contract, contractimpl, contracttype, Address, Env, String};
use stellar_tokens::non_fungible::{
burnable::NonFungibleBurnable,
consecutive::{Consecutive, NonFungibleConsecutive},
Base, ContractOverrides, NonFungibleToken,
Base, Compose, ContractOverrides, NonFungibleToken,
};

#[contracttype]
Expand Down Expand Up @@ -39,7 +39,7 @@ impl ExampleContract {
// `#[contractimpl(contracttrait)]` macro.
#[contractimpl(contracttrait)]
impl NonFungibleToken for ExampleContract {
type ContractType = Consecutive;
type ContractType = Compose<(Consecutive,)>;

fn balance(e: &Env, owner: Address) -> u32 {
Self::ContractType::balance(e, &owner)
Expand Down
4 changes: 2 additions & 2 deletions examples/nft-enumerable/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use soroban_sdk::{contract, contractimpl, contracttype, Address, Env, String};
use stellar_tokens::non_fungible::{
burnable::NonFungibleBurnable,
enumerable::{Enumerable, NonFungibleEnumerable},
Base, NonFungibleToken,
Base, Compose, NonFungibleToken,
};

#[contracttype]
Expand Down Expand Up @@ -36,7 +36,7 @@ impl ExampleContract {

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

#[contractimpl(contracttrait)]
Expand Down
6 changes: 4 additions & 2 deletions examples/nft-royalties/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
use soroban_sdk::{contract, contractimpl, symbol_short, Address, Env, String, Symbol, Vec};
use stellar_access::access_control::{self as access_control, AccessControl};
use stellar_macros::{only_admin, only_role};
use stellar_tokens::non_fungible::{royalties::NonFungibleRoyalties, Base, NonFungibleToken};
use stellar_tokens::non_fungible::{
royalties::NonFungibleRoyalties, Base, Compose, NonFungibleToken,
};

#[contract]
pub struct ExampleContract;
Expand Down Expand Up @@ -53,7 +55,7 @@ impl ExampleContract {

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

#[contractimpl(contracttrait)]
Expand Down
6 changes: 4 additions & 2 deletions examples/nft-sequential-minting/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
//! Demonstrates an example usage of the NFT default base implementation.

use soroban_sdk::{contract, contractimpl, contracttype, Address, Env, String};
use stellar_tokens::non_fungible::{burnable::NonFungibleBurnable, Base, NonFungibleToken};
use stellar_tokens::non_fungible::{
burnable::NonFungibleBurnable, Base, Compose, NonFungibleToken,
};

#[contracttype]
pub enum DataKey {
Expand All @@ -30,7 +32,7 @@ impl ExampleContract {

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

#[contractimpl(contracttrait)]
Expand Down
4 changes: 2 additions & 2 deletions 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, FungibleToken},
fungible::{Base, Compose, FungibleToken},
rwa::{RWAToken, RWA},
};

Expand Down Expand Up @@ -54,7 +54,7 @@ impl Pausable for RWATokenContract {

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

#[contractimpl(contracttrait)]
Expand Down
5 changes: 3 additions & 2 deletions packages/tokens/src/fungible/extensions/burnable/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ use crate::fungible::{overrides::BurnableOverrides, FungibleToken};
///
/// The burn logic is selected automatically based on the `ContractType` set
/// on the `FungibleToken` implementation. If the contract uses
/// `type ContractType = AllowList`, burning checks the allowlist; with
/// `type ContractType = Base` it uses the vanilla behavior, and so on. There
/// `type ContractType = Compose<(AllowList,)>`, burning checks the allowlist;
/// with `type ContractType = Compose<(Base,)>` it uses the vanilla behavior,
/// and so on. There
/// is no need to interact with the override machinery, it works in the
/// background.
#[contracttrait]
Expand Down
109 changes: 109 additions & 0 deletions packages/tokens/src/fungible/extensions/combinations/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
//! # Contract Type Composition for Fungible Token.
//!
//! A contract selects exactly one `ContractType` on its
//! [`crate::fungible::FungibleToken`] implementation, and that single slot
//! decides all overridable behavior. The contract type is selected uniformly
//! with [`Compose`], by listing the contract types that override the `Base`
//! behavior. [`Compose`] resolves the list to the curated contract type at
//! compile time.
//!
//! Note that [`Compose`] only selects the `ContractType`; it says nothing
//! about extensions that add new functionality without overriding the base
//! behavior. Those (e.g. [`crate::fungible::burnable::FungibleBurnable`] or
//! the [`crate::fungible::capped`] helpers) have no contract type and are
//! simply implemented alongside; there is no `Compose<(Burnable,)>`.
//!
//! Usage:
//!
//! ```ignore
//! #[contractimpl(contracttrait)]
//! impl FungibleToken for MyToken {
//! type ContractType = Compose<(AllowList,)>;
//! }
//!
//! #[contractimpl(contracttrait)]
//! impl FungibleAllowList for MyToken {
//! // ...
//! }
//! ```
//!
//! No multi-type combinations are curated for fungible tokens yet; every
//! valid list currently holds a single contract type. Invalid lists do not
//! compile: `AllowList` and `BlockList` are mutually exclusive, and
//! implementing an extension trait the list does not back (e.g.
//! [`crate::fungible::allowlist::FungibleAllowList`] without `AllowList` in
//! the list) is rejected by that trait's bound.

use crate::{
fungible::{
extensions::{allowlist::AllowList, blocklist::BlockList, votes::FungibleVotes},
Base, ContractOverrides,
},
rwa::RWA,
vault::Vault,
};

/// Resolves a list of contract types to the combined contract type, e.g.
/// `Compose<(AllowList,)>`.
///
/// This is shorthand for `<L as Composable>::Out`; refer to [`Composable`] for
/// the valid lists.
pub type Compose<L> = <L as Composable>::Out;

/// Type-level lookup backing [`Compose`]: each valid contract type list
/// resolves to its combined contract type through the `Out` associated type.
///
/// Single contract types resolve to themselves (with or without the
/// one-element tuple form). Mutually exclusive contract types (e.g.
/// `AllowList` and `BlockList`) have no implementation, so combining them
/// does not compile.
#[diagnostic::on_unimplemented(
message = "`{Self}` is not a valid contract type combination",
note = "valid single contract types: `Base`, `AllowList`, `BlockList`, `RWA`, `Vault`, \
`FungibleVotes`",
note = "no multi-type combinations are curated for fungible tokens yet",
note = "`AllowList` and `BlockList` are mutually exclusive"
)]
pub trait Composable {
type Out: ContractOverrides;
}

// Single contract types resolve to themselves. The bare form and the
// one-element tuple form are equivalent, so a stray trailing comma does not
// change the meaning.
impl Composable for Base {
type Out = Base;
}
impl Composable for (Base,) {
type Out = Base;
}
impl Composable for AllowList {
type Out = AllowList;
}
impl Composable for (AllowList,) {
type Out = AllowList;
}
impl Composable for BlockList {
type Out = BlockList;
}
impl Composable for (BlockList,) {
type Out = BlockList;
}
impl Composable for RWA {
type Out = RWA;
}
impl Composable for (RWA,) {
type Out = RWA;
}
impl Composable for Vault {
type Out = Vault;
}
impl Composable for (Vault,) {
type Out = Vault;
}
impl Composable for FungibleVotes {
type Out = FungibleVotes;
}
impl Composable for (FungibleVotes,) {
type Out = FungibleVotes;
}
Loading
Loading