diff --git a/ecosystem/README.md b/ecosystem/README.md index d331c32ef..afa06098f 100644 --- a/ecosystem/README.md +++ b/ecosystem/README.md @@ -73,33 +73,34 @@ All SEPs have individuals fulfilling the following roles: ### Draft Proposals -| Number | Title | Author | Status | -| ----------------------- | -------------------------------------------------------------- | -------------------------------------------------- | -------------------- | -| [SEP-0015](sep-0015.md) | Attachment Convention | Interstellar | Draft | -| [SEP-0016](sep-0016.md) | Account Transfer Permissionless Payment Protocol (@p2p) | Jeremy Rubin | Draft | -| [SEP-0017](sep-0017.md) | Issuer account funding protocol (CAP-13 Based) | Tom Quisel | Draft | -| [SEP-0019](sep-0019.md) | Bootstrapping Multisig Transaction Submission | Paul Selden, Nikhil Saraf | Draft | -| [SEP-0021](sep-0021.md) | On-chain signature & transaction sharing | Mister.Ticot | Draft | -| [SEP-0022](sep-0022.md) | IPFS Support | Samuel B. Sendelbach | Draft | -| [SEP-0030](sep-0030.md) | Recoverysigner: multi-party key management of Stellar accounts | Leigh McCulloch, Lindsay Lin | Draft | -| [SEP-0032](sep-0032.md) | Asset Address | Leigh McCulloch | Draft | -| [SEP-0034](sep-0034.md) | Wallet Attribution for Anchors | Jake Urban and Leigh McCulloch | Final Comment Period | -| [SEP-0035](sep-0035.md) | Operation IDs | Isaiah Turner, Debnil Sur, Scott Fleckenstein | Draft | -| [SEP-0037](sep-0037.md) | Address Directory API | OrbitLens | Draft | -| [SEP-0038](sep-0038.md) | Anchor RFQ API | Jake Urban and Leigh McCulloch | Draft | -| [SEP-0039](sep-0039.md) | Interoperability Recommendations for NFTs | SDF, Litemint.io | Active | -| [SEP-0040](sep-0040.md) | Oracle Consumer Interface | Alex Mootz, OrbitLens, Markus Paulson-Luna | Draft | -| [SEP-0041](sep-0041.md) | Soroban Token Interface | Jonathan Jove, Siddharth Suresh | Draft | -| [SEP-0045](sep-0045.md) | Stellar Web Authentication for Contract Accounts | Philip Liu, Marcelo Salloum, Leigh McCulloch | Draft | -| [SEP-0047](sep-0047.md) | Contract Interface Discovery | Leigh McCulloch | Draft | -| [SEP-0049](sep-0049.md) | Upgradeable Contracts | OpenZeppelin, Boyan Barakov, Özgün Özerk | Draft | -| [SEP-0050](sep-0050.md) | Non-Fungible Tokens | OpenZeppelin, Boyan Barakov, Özgün Özerk | Draft | -| [SEP-0051](sep-0051.md) | XDR-JSON | Leigh McCulloch | Draft | -| [SEP-0052](sep-0052.md) | Key Sharing Method for Stellar Keys | Pamphile Roy, Jun Luo | Draft | -| [SEP-0053](sep-0053.md) | Sign and Verify Messages | Jun Luo, Pamphile Roy, OrbitLens, Piyal Basu | Draft | -| [SEP-0054](sep-0054.md) | Ledger Metadata Storage | Tamir Sen | Draft | -| [SEP-0055](sep-0055.md) | Contract Build Info | Nando Vieira | Draft | -| [SEP-0056](sep-0056.md) | Tokenized Vault Standard | OpenZeppelin, Boyan Barakov, Özgün Özerk, Sentinel | Draft | +| Number | Title | Author | Status | +| ----------------------- | -------------------------------------------------------------- | ---------------------------------------------------------- | -------------------- | +| [SEP-0015](sep-0015.md) | Attachment Convention | Interstellar | Draft | +| [SEP-0016](sep-0016.md) | Account Transfer Permissionless Payment Protocol (@p2p) | Jeremy Rubin | Draft | +| [SEP-0017](sep-0017.md) | Issuer account funding protocol (CAP-13 Based) | Tom Quisel | Draft | +| [SEP-0019](sep-0019.md) | Bootstrapping Multisig Transaction Submission | Paul Selden, Nikhil Saraf | Draft | +| [SEP-0021](sep-0021.md) | On-chain signature & transaction sharing | Mister.Ticot | Draft | +| [SEP-0022](sep-0022.md) | IPFS Support | Samuel B. Sendelbach | Draft | +| [SEP-0030](sep-0030.md) | Recoverysigner: multi-party key management of Stellar accounts | Leigh McCulloch, Lindsay Lin | Draft | +| [SEP-0032](sep-0032.md) | Asset Address | Leigh McCulloch | Draft | +| [SEP-0034](sep-0034.md) | Wallet Attribution for Anchors | Jake Urban and Leigh McCulloch | Final Comment Period | +| [SEP-0035](sep-0035.md) | Operation IDs | Isaiah Turner, Debnil Sur, Scott Fleckenstein | Draft | +| [SEP-0037](sep-0037.md) | Address Directory API | OrbitLens | Draft | +| [SEP-0038](sep-0038.md) | Anchor RFQ API | Jake Urban and Leigh McCulloch | Draft | +| [SEP-0039](sep-0039.md) | Interoperability Recommendations for NFTs | SDF, Litemint.io | Active | +| [SEP-0040](sep-0040.md) | Oracle Consumer Interface | Alex Mootz, OrbitLens, Markus Paulson-Luna | Draft | +| [SEP-0041](sep-0041.md) | Soroban Token Interface | Jonathan Jove, Siddharth Suresh | Draft | +| [SEP-0045](sep-0045.md) | Stellar Web Authentication for Contract Accounts | Philip Liu, Marcelo Salloum, Leigh McCulloch | Draft | +| [SEP-0047](sep-0047.md) | Contract Interface Discovery | Leigh McCulloch | Draft | +| [SEP-0049](sep-0049.md) | Upgradeable Contracts | OpenZeppelin, Boyan Barakov, Özgün Özerk | Draft | +| [SEP-0050](sep-0050.md) | Non-Fungible Tokens | OpenZeppelin, Boyan Barakov, Özgün Özerk | Draft | +| [SEP-0051](sep-0051.md) | XDR-JSON | Leigh McCulloch | Draft | +| [SEP-0052](sep-0052.md) | Key Sharing Method for Stellar Keys | Pamphile Roy, Jun Luo | Draft | +| [SEP-0053](sep-0053.md) | Sign and Verify Messages | Jun Luo, Pamphile Roy, OrbitLens, Piyal Basu | Draft | +| [SEP-0054](sep-0054.md) | Ledger Metadata Storage | Tamir Sen | Draft | +| [SEP-0055](sep-0055.md) | Contract Build Info | Nando Vieira | Draft | +| [SEP-0056](sep-0056.md) | Tokenized Vault Standard | OpenZeppelin, Boyan Barakov, Özgün Özerk, Sentinel | Draft | +| [SEP-0057](sep-0057.md) | T-REX (Token for Regulated EXchanges) | OpenZeppelin, Boyan Barakov, Özgün Özerk, Dennis O'Connell | Draft | ### Abandoned Proposals diff --git a/ecosystem/sep-0057.md b/ecosystem/sep-0057.md new file mode 100644 index 000000000..2fea19aae --- /dev/null +++ b/ecosystem/sep-0057.md @@ -0,0 +1,1364 @@ +## Preamble + +``` +SEP: 0057 +Title: T-REX (Token for Regulated EXchanges) +Author: OpenZeppelin, Boyan Barakov <@brozorec>, Özgün Özerk <@ozgunozerk>, Dennis O'Connell <@droconnel22> +Status: Draft +Created: 2025-11-26 +Updated: 2025-12-15 +Version: 0.1.0 +Discussion: https://github.com/orgs/stellar/discussions/1814 +``` + +## Summary + +This proposal defines a comprehensive suite of smart contracts for T-REX (Token +for Regulated EXchanges) - colloquially known as RWA token - on Stellar. T-REX +tokens represent tokenized real-world assets such as securities, real estate, +or other regulated financial instruments that require compliance with +regulatory frameworks. T-REX tokens are **permissioned tokens**, ensuring +secure and compliant transactions for all parties involved in the token +transfer. + +This standard is based on the T-REX (Token for Regulated Exchanges) framework, +as implemented in ERC-3643 (https://github.com/ERC-3643/ERC-3643), but +introduces significant architectural improvements for flexibility and +modularity. + +## Motivation + +Real World Assets (RWAs) represent a significant opportunity for blockchain +adoption, enabling the tokenization of traditional financial instruments and +physical assets. However, unlike standard fungible tokens, RWAs must comply +with complex regulatory requirements including but not limited to: + +- **Know Your Customer (KYC) and Anti-Money Laundering (AML)** compliance +- **Identity verification** and investor accreditation +- **Freezing capabilities** for regulatory enforcement +- **Recovery mechanisms** for lost or compromised wallets +- **Compliance hooks** for regulatory reporting + +The T-REX standard provides a comprehensive framework for compliant security +tokens. This SEP adapts T-REX to the Stellar ecosystem, enabling: + +- **Modular hook-based compliance framework** with pluggable compliance rules +- **Flexible identity verification** supporting multiple approaches + (claim-based, Merkle tree, zero-knowledge, etc.) +- **Sophisticated freezing mechanisms** at both address and token levels +- **Administrative controls** with role-based access control (RBAC) + +## Architecture Overview + +This T-REX standard introduces an approach built around **loose coupling** and +**implementation abstraction**. + +### Core Design Principles + +1. **Separation of Concerns**: Core token functionality is cleanly separated + from compliance and identity verification +2. **Implementation Flexibility**: Compliance and identity systems are treated + as pluggable implementation details +3. **Shared Infrastructure**: Components can be shared across multiple token + contracts to reduce deployment and management costs +4. **Regulatory Adaptability**: The system can adapt to different regulatory + frameworks without core changes + +### Component Architecture + +The Stellar T-REX consists of several interconnected but loosely coupled +components: + +``` +┌─────────────────┐ ┌──────────────────┐ ┌─────────────────────┐ +│ T-REX │───▶│ Compliance │───▶│ Compliance Modules │ +│ (Core) │ │ │ │ (Pluggable Rules) │ +└─────────────────┘ └──────────────────┘ └─────────────────────┘ + │ + ▼ +┌─────────────────┐ ┌──────────────────┐ ┌─────────────────-─┐ +│ Identity │───▶│ Claim Topics & │ │ Identity Registry │ +│ Verifier │ │ Issuers │───▶│ │ +└─────────────────┘ └──────────────────┘ └─────────────────-─┘ +``` + +This design enables the same core T-REX interface to work with vastly different +regulatory and technical requirements for identity verification, such as Merkle +trees, Zero-Knowledge proofs, claim-based systems, and others. Furthermore, the +modular hook-based system supports diverse regulatory requirements through +pluggable compliance rules. + +## Interface + +The T-REX interface extends the **fungible token** +([SEP-41](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0041.md)) +with regulatory required features: freezing, pausing and recovery. + +### Architecture Overview + +The T-REX token contract requires only **two external functions** to operate: + +```rust +// Compliance validation - returns true if transfer is allowed +fn can_transfer(e: &Env, from: Address, to: Address, amount: i128) -> bool; + +// Identity verification - panics if user is not verified +fn verify_identity(e: &Env, user_address: &Address); +``` + +- `can_transfer()` is expected to be exposed from a "Compliance" contract. +- `verify_identity()` is expected to be exposed from an "Identity Verifier" + contract. + +These functions are **deliberately abstracted** as implementation details, +enabling: + +- **Regulatory Flexibility**: Different jurisdictions can implement different + compliance logic +- **Technical Flexibility**: Various identity verification approaches (ZK, + Merkle trees, claims) +- **Cost Optimization**: Shared contracts across multiple tokens +- **Future-Proofing**: New compliance approaches without interface changes + +In other words, the only thing required by this T-REX design, is that the token +should be able to call these expected functions made available by the +compliance and identity verification contracts. + +### Contract Connection Interface + +The token contract provides simple setter/getter functions for external +contracts: + +```rust +// Compliance Contract Management +fn set_compliance(e: &Env, compliance: Address, operator: Address); +fn compliance(e: &Env) -> Address; + +// Identity Verifier Contract Management +fn set_identity_verifier(e: &Env, identity_verifier: Address, operator: Address); +fn identity_verifier(e: &Env) -> Address; +``` + +### Integration Pattern + +To deploy a compliant T-REX token and make it functional: + +1. **Deploy Core Token Contract** +2. **Deploy/Connect Compliance Contract** +3. **Deploy/Connect Identity Verifier** +4. **Configure Connections**: Link the T-REX token to its compliance and + identity verifier contracts using `set_compliance()` and + `set_identity_verifier()`. Additionally, configure internal connections + within the compliance stack (e.g., linking compliance contract to compliance + modules) and identity stack (e.g., linking identity verifier to claim + topics/issuers or custom registries) as needed for your implementation. + +```rust +use soroban_sdk::{Address, Env, String}; +use stellar_contract_utils::pausable::Pausable; +use crate::fungible::FungibleToken; + +/// Real World Asset Token Trait +/// +/// The `RWAToken` trait defines the core functionality for Real World Asset +/// tokens, implementing the T-REX standard for regulated securities. It +/// provides a comprehensive interface for managing compliant token transfers, +/// identity verification, compliance rules, and administrative controls. +/// +/// This trait extends basic fungible token functionality with regulatory +/// features required for security tokens. +pub trait RWAToken: TokenInterface { + // ################## CORE TOKEN FUNCTIONS ################## + + /// Returns the total amount of tokens in circulation. + /// + /// # Arguments + /// + /// * `e` - Access to the Soroban environment. + fn total_supply(e: &Env) -> i128; + + /// Forces a transfer of tokens between two whitelisted wallets. + /// This function can only be called by the operator with necessary + /// privileges. RBAC checks are expected to be enforced on the + /// `operator`. + /// + /// # Arguments + /// + /// * `e` - Access to the Soroban environment. + /// * `from` - The address of the sender. + /// * `to` - The address of the receiver. + /// * `amount` - The number of tokens to transfer. + /// * `operator` - The address of the operator. + /// + /// # Events + /// + /// * topics - `["transfer", from: Address, to: Address]` + /// * data - `[amount: i128]` + fn forced_transfer(e: &Env, from: Address, to: Address, amount: i128, operator: Address); + + /// Mints tokens to a wallet. Tokens can only be minted to verified + /// addresses. This function can only be called by the operator with + /// necessary privileges. RBAC checks are expected to be enforced on the + /// `operator`. + /// + /// # Arguments + /// + /// * `e` - Access to the Soroban environment. + /// * `to` - Address to mint the tokens to. + /// * `amount` - Amount of tokens to mint. + /// * `operator` - The address of the operator. + /// + /// # Events + /// + /// * topics - `["mint", to: Address]` + /// * data - `[amount: i128]` + fn mint(e: &Env, to: Address, amount: i128, operator: Address); + + /// Burns tokens from a wallet. + /// This function can only be called by the operator with necessary + /// privileges. RBAC checks are expected to be enforced on the + /// `operator`. + /// + /// # Arguments + /// + /// * `e` - Access to the Soroban environment. + /// * `user_address` - Address to burn the tokens from. + /// * `amount` - Amount of tokens to burn. + /// * `operator` - The address of the operator. + /// + /// # Events + /// + /// * topics - `["burn", user_address: Address]` + /// * data - `[amount: i128]` + fn burn(e: &Env, user_address: Address, amount: i128, operator: Address); + + /// Recovery function used to force transfer tokens from a old account + /// to a new account for an investor. + /// This function can only be called by the operator with necessary + /// privileges. RBAC checks are expected to be enforced on the + /// `operator`. + /// + /// # Arguments + /// + /// * `e` - Access to the Soroban environment. + /// * `old_account` - The wallet that the investor lost. + /// * `new_account` - The newly provided wallet for token transfer. + /// * `operator` - The address of the operator. + /// + /// # Events + /// + /// * topics - `["transfer", old_account: Address, new_account: Address]` + /// * data - `[amount: i128]` + /// * topics - `["recovery", old_account: Address, new_account: Address]` + /// * data - `[]` + fn recover_balance( + e: &Env, + old_account: Address, + new_account: Address, + operator: Address, + ) -> bool; + + /// Sets the frozen status for an address. Frozen addresses cannot send or + /// receive tokens. This function can only be called by the operator + /// with necessary privileges. RBAC checks are expected to be enforced + /// on the `operator`. + /// + /// # Arguments + /// + /// * `e` - Access to the Soroban environment. + /// * `user_address` - The address for which to update frozen status. + /// * `freeze` - Frozen status of the address. + /// * `operator` - The address of the operator. + /// + /// # Events + /// + /// * topics - `["address_frozen", user_address: Address, is_frozen: bool, + /// operator: Address]` + /// * data - `[]` + fn set_address_frozen(e: &Env, user_address: Address, freeze: bool, operator: Address); + + /// Freezes a specified amount of tokens for a given address. + /// This function can only be called by the operator with necessary + /// privileges. RBAC checks are expected to be enforced on the + /// `operator`. + /// + /// # Arguments + /// + /// * `e` - Access to the Soroban environment. + /// * `user_address` - The address for which to freeze tokens. + /// * `amount` - Amount of tokens to be frozen. + /// * `operator` - The address of the operator. + /// + /// # Events + /// + /// * topics - `["tokens_frozen", user_address: Address]` + /// * data - `[amount: i128]` + fn freeze_partial_tokens(e: &Env, user_address: Address, amount: i128, operator: Address); + + /// Unfreezes a specified amount of tokens for a given address. + /// This function can only be called by the operator with necessary + /// privileges. RBAC checks are expected to be enforced on the + /// `operator`. + /// + /// # Arguments + /// + /// * `e` - Access to the Soroban environment. + /// * `user_address` - The address for which to unfreeze tokens. + /// * `amount` - Amount of tokens to be unfrozen. + /// * `operator` - The address of the operator. + /// + /// # Events + /// + /// * topics - `["tokens_unfrozen", user_address: Address]` + /// * data - `[amount: i128]` + fn unfreeze_partial_tokens(e: &Env, user_address: Address, amount: i128, operator: Address); + + /// Returns the freezing status of a wallet. + /// + /// # Arguments + /// + /// * `e` - Access to the Soroban environment. + /// * `user_address` - The address of the wallet to check. + fn is_frozen(e: &Env, user_address: Address) -> bool; + + /// Returns the amount of tokens that are partially frozen on a wallet. + /// + /// # Arguments + /// + /// * `e` - Access to the Soroban environment. + /// * `user_address` - The address of the wallet to check. + fn get_frozen_tokens(e: &Env, user_address: Address) -> i128; + + // ################## METADATA FUNCTIONS ################## + + /// Returns the version of the token (T-REX version). + /// + /// # Arguments + /// + /// * `e` - Access to the Soroban environment. + fn version(e: &Env) -> String; + + /// Returns the address of the onchain ID of the token. + /// + /// # Arguments + /// + /// * `e` - Access to the Soroban environment. + fn onchain_id(e: &Env) -> Address; + + // ################## COMPLIANCE AND IDENTITY FUNCTIONS ################## + + /// Sets the compliance contract of the token. + /// This function can only be called by the operator with necessary + /// privileges. RBAC checks are expected to be enforced on the + /// `operator`. + /// + /// # Arguments + /// + /// * `e` - Access to the Soroban environment. + /// * `compliance` - The address of the compliance contract to set. + /// * `operator` - The address of the operator. + /// + /// # Events + /// + /// * topics - `["compliance_set", compliance: Address]` + /// * data - `[]` + fn set_compliance(e: &Env, compliance: Address, operator: Address); + + /// Sets the identity verifier contract of the token. + /// + /// This function can only be called by the operator with necessary + /// privileges. RBAC checks are expected to be enforced on the + /// `operator`. + /// + /// # Arguments + /// + /// * `e` - Access to the Soroban environment. + /// * `identity_verifier` - The address of the identity verifier contract to + /// set. + /// * `operator` - The address of the operator. + /// + /// # Events + /// + /// * topics - `["identity_verifier_set", identity_verifier: Address]` + /// * data - `[]` + fn set_identity_verifier(e: &Env, identity_verifier: Address, operator: Address); + + /// Returns the Compliance contract linked to the token. + /// + /// # Arguments + /// + /// * `e` - Access to the Soroban environment. + fn compliance(e: &Env) -> Address; + + /// Returns the Identity Verifier contract linked to the token. + /// + /// # Arguments + /// + /// * `e` - Access to the Soroban environment. + fn identity_verifier(e: &Env) -> Address; + + // ################## PAUSABLE FUNCTIONS ################## + + /// Returns true if the contract is paused, and false otherwise. + /// + /// # Arguments + /// + /// * `e` - Access to Soroban environment. + /// * `caller` - The address of the caller. + fn pause(e: &Env, caller: Address); + + /// Triggers `Unpaused` state. + /// + /// # Arguments + /// + /// * `e` - Access to Soroban environment. + /// * `caller` - The address of the caller. + fn unpause(e: &Env, caller: Address); + +} +``` + +## Events + +### Transfer Event + +The transfer event is emitted when tokens are transferred from one address to +another, including forced transfers and recovery operations. + +```rust +#[contractevent] +pub struct Transfer { + /// The address holding the tokens that were transferred + #[topic] + pub from: Address, + /// The address that received the tokens + #[topic] + pub to: Address, + /// The amount of tokens transferred + pub amount: i128, +} +``` + +### Mint Event + +The mint event is emitted when tokens are minted to a verified address. + +```rust +#[contractevent] +pub struct Mint { + /// The address receiving the newly minted tokens + #[topic] + pub to: Address, + /// The amount of tokens minted + pub amount: i128, +} +``` + +### Burn Event + +The burn event is emitted when tokens are burned from an address. + +```rust +#[contractevent] +pub struct Burn { + /// The address from which tokens were burned + #[topic] + pub from: Address, + /// The amount of tokens burned + pub amount: i128, +} +``` + +### Recovery Event + +The recovery event is emitted when tokens are successfully recovered from a +lost wallet to a new wallet. + +```rust +#[contractevent] +pub struct Recovery { + /// The old account address from which tokens were recovered + #[topic] + pub old_account: Address, + /// The new account address that received the recovered tokens + #[topic] + pub new_account: Address, +} +``` + +### Address Frozen Event + +The address frozen event is emitted when an address is frozen or unfrozen. + +```rust +#[contractevent] +pub struct AddressFrozen { + /// The address that was frozen/unfrozen + #[topic] + pub user_address: Address, + /// The frozen status (true for frozen, false for unfrozen) + #[topic] + pub is_frozen: bool, + /// The operator who performed the action + #[topic] + pub operator: Address, +} +``` + +### Tokens Frozen Event + +The tokens frozen event is emitted when a specific amount of tokens is frozen +for an address. + +```rust +#[contractevent] +pub struct TokensFrozen { + /// The address for which tokens were frozen + #[topic] + pub user_address: Address, + /// The amount of tokens frozen + pub amount: i128, +} +``` + +### Tokens Unfrozen Event + +The tokens unfrozen event is emitted when a specific amount of tokens is +unfrozen for an address. + +```rust +#[contractevent] +pub struct TokensUnfrozen { + /// The address for which tokens were unfrozen + #[topic] + pub user_address: Address, + /// The amount of tokens unfrozen + pub amount: i128, +} +``` + +### Compliance Set Event + +The compliance set event is emitted when the compliance contract is updated. + +```rust +#[contractevent] +pub struct ComplianceSet { + /// The address of the new compliance contract + #[topic] + pub compliance: Address, +} +``` + +### Identity Verifier Set Event + +The identity verifier set event is emitted when the identity verifier contract +is updated. + +```rust +#[contractevent] +pub struct IdentityVerifierSet { + /// The address of the new identity verifier contract + #[topic] + pub identity_verifier: Address, +} +``` + +## Reference Implementation: Component Deep Dive + +### 1. Identity Verification System + +**Philosophy**: The entire identity stack is treated as an implementation +detail, enabling maximum regulatory and technical flexibility. + +#### The IdentityVerifier Trait + +```rust +pub trait IdentityVerifier { + /// Core verification function - panics if user is not verified + fn verify_identity(e: &Env, user_address: &Address); + + // Setters and getters for the claim topics and issuers contract + fn set_claim_topics_and_issuers(e: &Env, contract: Address, operator: Address); + fn claim_topics_and_issuers(e: &Env) -> Address; +} +``` + +#### Implementation Strategies + +Different regulatory environments may require different approaches. Here are +some examples: + +**1. Claim-Based Verification (Reference Implementation)** + +- **Use Case**: Traditional KYC/AML with trusted issuers +- **Components**: ClaimTopicsAndIssuers + IdentityRegistryStorage + + IdentityClaims + ClaimIssuer +- **Benefits**: Familiar to regulators, rich metadata support + +**2. Merkle Tree Verification** + +- **Use Case**: Privacy-focused compliance with efficient proofs +- **Components**: ClaimTopicsAndIssuers + Merkle root storage + proof + validation +- **Benefits**: Minimal storage, efficient verification + +**3. Zero-Knowledge Verification** + +- **Use Case**: Privacy-preserving compliance +- **Components**: ClaimTopicsAndIssuers + ZK circuit + proof verification +- **Benefits**: Maximum privacy, selective disclosure + +**4. Custom Approaches** + +- **Use Case**: Jurisdiction-specific requirements +- **Components**: ClaimTopicsAndIssuers + Custom verification logic +- **Benefits**: Tailored to specific regulatory needs + +#### Reference Implementation Architecture + +The claim-based reference implementation demonstrates the full complexity of +traditional T-REX compliance: + +``` +┌─────────────────────┐ +│ Identity Verifier │ +│ (Orchestrator) │ +│ │ +└─────────────────────┘ + │ + ├────▶┌───────────────────────────┐ + │ │ Claim Topics & Issuers │ + │ │ (Shared Registry) │ + │ └───────────────────────────┘ + │ + ├────▶┌───────────────────────────┐ + │ │ Identity Registry Storage │ + │ │ (Investor Profiles) │ + │ └───────────────────────────┘ + │ + ├────▶┌───────────────────────────┐ + │ │ Identity Claims │ + │ │ (Investor Claims Storage) │ + │ └───────────────────────────┘ + │ + └────▶┌───────────────────────────┐ + │ Claim Issuer │ + │ (Claims Validation) │ + └───────────────────────────┘ +``` + +**Key Components:** + +- **ClaimTopicsAndIssuers**: Registry contract managing both trusted issuers + and required claim types (KYC=1, AML=2, etc.). +- **IdentityRegistryStorage**: Component storing investors identity profiles + with country relations and metadata. +- **IdentityClaims**: Required for every investor as a separate contract or as + an extension to existing identity management systems. Its goal is to store + cryptographic claims issued to that investor by trusted claim issuers. This + component is under investor's control. Its structure mirrors the evolving + OnchainID specification + (https://github.com/ERC-3643/ERCs/blob/erc-oid/ERCS/erc-xxxx.md). +- **ClaimIssuer**: Contracts belonging to trusted 3rd parties to validate + cryptographic claims about investors' attributes by using multiple signature + schemes (Ed25519, Secp256k1, Secp256r1). + +Note that, Claim-Based Identity Verification is a reference implementation, it +is not a part of the specification. For detailed interface specifications of +these components, see the +[Appendix: Claim-Based Identity Verification Reference Implementation](#appendix-claim-based-identity-verification-reference-implementation) +section. + +### 2. Compliance System + +Modular hook-based architecture supporting diverse regulatory requirements +through pluggable compliance modules. + +#### The Compliance Trait + +```rust +pub trait Compliance { + // Module management + fn add_module_to(e: &Env, hook: ComplianceHook, module: Address, operator: Address); + fn remove_module_from(e: &Env, hook: ComplianceHook, module: Address, operator: Address); + + // Validation hooks (READ-only) + fn can_transfer(e: &Env, from: Address, to: Address, amount: i128) -> bool; + fn can_create(e: &Env, to: Address, amount: i128) -> bool; + + // State update hooks (called after successful operations) + fn transferred(e: &Env, from: Address, to: Address, amount: i128); + fn created(e: &Env, to: Address, amount: i128); + fn destroyed(e: &Env, from: Address, amount: i128); +} +``` + +#### Hook-Based Architecture + +The compliance system uses a sophisticated hook mechanism: + +```rust +pub enum ComplianceHook { + CanTransfer, // Pre-validation: Check if transfer meets compliance rules + CanCreate, // Pre-validation: Check if mint operation is compliant + Transferred, // Post-event: Update state after successful transfer + Created, // Post-event: Update state after successful mint + Destroyed, // Post-event: Update state after successful burn +} +``` + +#### Compliance Module Examples + +**Transfer Limits Module**: + +- Hook: `CanTransfer` + `Transferred` +- Logic: Enforce daily/monthly transfer limits per user + +**Jurisdiction Restrictions Module**: + +- Hook: `CanTransfer` +- Logic: Restrict transfers to blocked jurisdictions + +**Holding Period Module**: + +- Hook: `CanTransfer` + `Created` +- Logic: Enforce minimum holding periods for newly minted tokens + +**Investor Accreditation Module**: + +- Hook: `CanCreate` +- Logic: Verify investor accreditation before minting + +#### Shared Compliance Infrastructure + +Compliance contracts are designed to be **shared across multiple T-REX +tokens**, reducing deployment costs and ensuring consistent regulatory +enforcement: + +``` +┌─────────────┐ ┌─────────────────────┐ ┌──────────────────┐ +│ Token A │───▶│ │───▶│ Transfer Limits │ +├─────────────┤ │ Shared Compliance │ │ Module │ +│ Token B │───▶│ Contract │───▶├──────────────────┤ +├─────────────┤ │ │ │ Jurisdiction │ +│ Token C │───▶│ │ │ Module │ +└─────────────┘ └─────────────────────┘ └──────────────────┘ +``` + +### 3. Advanced Token Controls + +#### Freezing Mechanisms + +The system supports multiple freezing strategies to accommodate for different +regulatory requirements: + +**Address-Level Freezing**: + +```rust +fn set_address_frozen(e: &Env, user_address: Address, freeze: bool, operator: Address); +fn is_frozen(e: &Env, user_address: Address) -> bool; +``` + +- **Use Case**: Complete account suspension for regulatory investigations +- **Effect**: Prevents all token operations (send/receive) + +**Partial Token Freezing**: + +```rust +fn freeze_partial_tokens(e: &Env, user_address: Address, amount: i128, operator: Address); +fn unfreeze_partial_tokens(e: &Env, user_address: Address, amount: i128, operator: Address); +fn get_frozen_tokens(e: &Env, user_address: Address) -> i128; +``` + +- **Use Case**: Escrow scenarios, disputed transactions +- **Effect**: Freezes specific token amounts while allowing operations on + remaining balance + +#### Recovery System + +Two distinct recovery flows are supported: + +1. Identity Recovery: Managed in the Identity Stack (Identity Registry + Storage), transfers the identity contract reference and profile (including + country data) from old account to new account, and creates a recovery + mapping. + +```rust +fn recover_identity(e: &Env, old_account: Address, new_account: Address, operator: Address); +``` + +2. Balance Recovery: Managed by the T-REX token, transfers tokens from old to + new account after verifying the identity recovery mapping exists + +```rust +fn recover_balance(e: &Env, old_account: Address, new_account: Address, operator: Address) -> bool; +``` + +#### Forced Transfers + +For regulatory compliance (court orders, sanctions): + +```rust +fn forced_transfer(e: &Env, from: Address, to: Address, amount: i128, operator: Address); +``` + +- **Use Case**: Court-ordered asset transfers, regulatory seizures +- **Authorization**: Requires operator with forced transfer permissions +- **Compliance**: Bypasses normal compliance validation checks (operator + responsibility) + +### 4. Access Control & Governance + +T-REX tokens require proper access control to ensure that sensitive operations +are only performed by authorized entities: + +- **Operator Authorization**: All administrative functions require `operator` + authorization +- **Flexible Access Control**: While the T-REX interface itself doesn't + prescribe a specific access control model, implementations can integrate with + external access control systems as needed +- **Compliance Integration**: Access control permissions should be integrated + with compliance rules to ensure regulatory requirements are met + +## Extensions + +The T-REX standard can be extended with additional functionality beyond the +core specification. Extensions are optional modules that add specialized +features while maintaining compatibility with the base T-REX interface. + +### Document Manager (ERC-1643) + +The Document Manager extension provides document management capabilities for +T-REX tokens, following the ERC-1643 standard. This is particularly useful for +attaching legal documents, prospectuses, or regulatory disclosures to token +contracts. + +**Key Features:** + +- Attach documents with URI, hash, and timestamp +- Update existing document metadata +- Remove documents from the contract +- Retrieve individual or all documents + +**Use Cases:** + +- Legal agreements and terms of service +- Offering memorandums and prospectuses +- Regulatory compliance documents +- Audit reports and certifications + +### Custom Extensions + +Implementers are free to create custom extensions tailored to their specific +requirements. + +## Deviations from ERC-3643 + +Several limitations in the original ERC-3643 standard and its reference +implementation were identified and respectively addressed: + +**1. Tight Coupling Issues** + +- **Problem**: ERC-3643 tightly couples identity verification with specific + contract structures +- **Solution**: Abstract identity verification as pluggable implementation + detail + +**2. Inflexible Identity Models** + +- **Problem**: Hardcoded ERC-734/735 identity contracts don't translate to all + blockchain architectures +- **Solution**: Support multiple identity verification approaches (Merkle, ZK, + claims, custom) + +**3. Redundant Contract Hierarchies** + +- **Problem**: Complex IdentityRegistry ↔ IdentityRegistryStorage relationships +- **Solution**: Direct access patterns + +**4. Limited Compliance Flexibility** + +- **Problem**: Monolithic compliance validation +- **Solution**: Modular hook-based compliance system + +## Upgrade and Migration Strategies + +**Compliance Evolution**: + +- Modular compliance system supports rule updates without token redeployment +- New compliance modules can be added for evolving regulations + +**Identity System Migration**: + +- Abstract identity verification enables migration between verification + approaches +- Gradual migration strategies for existing user bases + +For general contract upgrade patterns and best practices, refer to +[SEP-49: Upgradeable Contracts](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0049.md). + +--- + +## Appendix: Claim-Based Identity Verification Reference Implementation + +> **IMPORTANT NOTICE**: This appendix describes a **reference implementation** +> and is **NOT a part of the T-REX specification**. The T-REX standard only +> requires that an `IdentityVerifier` contract exposing a `verify_identity()` +> function. The claim-based approach described here is one possible +> implementation among many (others include Merkle tree verification, +> zero-knowledge proofs, or custom approaches). This section is provided for +> informational purposes to demonstrate a complete, production-ready +> implementation that follows traditional KYC/AML compliance patterns. + +### Overview + +The claim-based approach uses cryptographic claims issued by trusted +authorities (KYC providers, compliance firms) to verify investor identities. +This implementation consists of four main components that work together: + +1. **ClaimTopicsAndIssuers**: Trust registry defining required claim types and + authorized issuers +2. **IdentityRegistryStorage**: Storage for investor identity profiles and + country relations +3. **IdentityClaims**: Per-investor contract storing cryptographic claims +4. **ClaimIssuer**: Validator contracts operated by trusted third parties + +### ClaimTopicsAndIssuers Interface + +The `ClaimTopicsAndIssuers` contract acts as the trust registry, defining which +claim topics are required for token participation and which issuers are +authorized to provide those claims. + +**Purpose:** + +- Maintains a registry of trusted claim issuers (e.g., KYC providers, + compliance firms) +- Defines which types of claims are required (e.g., KYC=1, AML=2, Accredited + Investor=3) +- Maps each trusted issuer to the specific claim topics they are authorized to + issue +- Can be shared across multiple tokens to reduce deployment costs + +**Interface:** + +```rust +pub trait ClaimTopicsAndIssuers { + // ################## CLAIM TOPICS ################## + + /// Adds a claim topic (for example: KYC=1, AML=2). + /// + /// Only an operator with sufficient permissions should be able to call this + /// function. + /// + /// # Arguments + /// + /// * `e` - Access to the Soroban environment. + /// * `claim_topic` - The claim topic index. + /// + /// # Events + /// + /// * topics - `["claim_added", claim_topic: u32]` + /// * data - `[]` + fn add_claim_topic(e: &Env, claim_topic: u32, operator: Address); + + /// Removes a claim topic (for example: KYC=1, AML=2). + /// + /// Only an operator with sufficient permissions should be able to call this + /// function. + /// + /// # Arguments + /// + /// * `e` - Access to the Soroban environment. + /// * `claim_topic` - The claim topic index. + /// + /// # Events + /// + /// * topics - `["claim_removed", claim_topic: u32]` + /// * data - `[]` + fn remove_claim_topic(e: &Env, claim_topic: u32, operator: Address); + + /// Returns the claim topics for the security token. + /// + /// # Arguments + /// + /// * `e` - Access to the Soroban environment. + fn get_claim_topics(e: &Env) -> Vec; + + // ################## TRUSTED ISSUERS ################## + + /// Registers a claim issuer contract as trusted claim issuer. + /// + /// Only an operator with sufficient permissions should be able to call this + /// function. + /// + /// # Arguments + /// + /// * `e` - Access to the Soroban environment. + /// * `trusted_issuer` - The claim issuer contract address of the trusted + /// claim issuer. + /// * `claim_topics` - The set of claim topics that the trusted issuer is + /// allowed to emit. + /// + /// # Events + /// + /// * topics - `["issuer_added", trusted_issuer: Address]` + /// * data - `[claim_topics: Vec]` + fn add_trusted_issuer( + e: &Env, + trusted_issuer: Address, + claim_topics: Vec, + operator: Address, + ); + + /// Removes the claim issuer contract of a trusted claim issuer. + /// + /// Only an operator with sufficient permissions should be able to call this + /// function. + /// + /// # Arguments + /// + /// * `e` - Access to the Soroban environment. + /// * `trusted_issuer` - The claim issuer to remove. + /// + /// # Events + /// + /// * topics - `["issuer_removed", trusted_issuer: Address]` + /// * data - `[]` + fn remove_trusted_issuer(e: &Env, trusted_issuer: Address, operator: Address); + + /// Updates the set of claim topics that a trusted issuer is allowed to + /// emit. + /// + /// Only an operator with sufficient permissions should be able to call this + /// function. + /// + /// # Arguments + /// + /// * `e` - Access to the Soroban environment. + /// * `trusted_issuer` - The claim issuer to update. + /// * `claim_topics` - The set of claim topics that the trusted issuer is + /// allowed to emit. + /// + /// # Events + /// + /// * topics - `["topics_updated", trusted_issuer: Address]` + /// * data - `[claim_topics: Vec]` + fn update_issuer_claim_topics( + e: &Env, + trusted_issuer: Address, + claim_topics: Vec, + operator: Address, + ); + + /// Returns all the trusted claim issuers stored. + /// + /// # Arguments + /// + /// * `e` - Access to the Soroban environment. + fn get_trusted_issuers(e: &Env) -> Vec
; + + /// Returns all the trusted issuers allowed for a given claim topic. + /// + /// # Arguments + /// + /// * `e` - Access to the Soroban environment. + /// * `claim_topic` - The claim topic to get the trusted issuers for. + fn get_claim_topic_issuers(e: &Env, claim_topic: u32) -> Vec
; + + /// Returns all the claim topics and their corresponding trusted issuers as + /// a Mapping. + /// + /// # Arguments + /// + /// * `e` - Access to the Soroban environment. + fn get_claim_topics_and_issuers(e: &Env) -> Map>; + + /// Checks if the claim issuer contract is trusted. + /// + /// # Arguments + /// + /// * `e` - Access to the Soroban environment. + /// * `issuer` - The address of the claim issuer contract. + fn is_trusted_issuer(e: &Env, issuer: Address) -> bool; + + /// Returns all the claim topics of trusted claim issuer. + /// + /// # Arguments + /// + /// * `e` - Access to the Soroban environment. + /// * `trusted_issuer` - The trusted issuer concerned. + fn get_trusted_issuer_claim_topics(e: &Env, trusted_issuer: Address) -> Vec; + + /// Checks if the trusted claim issuer is allowed to emit a certain claim + /// topic. + /// + /// # Arguments + /// + /// * `e` - Access to the Soroban environment. + /// * `issuer` - The address of the trusted issuer's claim issuer contract. + /// * `claim_topic` - The claim topic that has to be checked to know if the + /// issuer is allowed to emit it. + fn has_claim_topic(e: &Env, issuer: Address, claim_topic: u32) -> bool; +} +``` + +### IdentityRegistryStorage Interface + +The `IdentityRegistryStorage` contract stores identity information for verified +investors, including their identity contract references and country relations. + +**Purpose:** + +- Maps wallet addresses to their onchain identity contracts +- Stores country information for regulatory compliance +- Supports both individual and organizational identities +- Manages recovery account mappings for lost wallet scenarios + +**Interface:** + +```rust +pub trait IdentityRegistryStorage: TokenBinder { + /// The specific type used for country data in this implementation. + type CountryData: FromVal; + + /// Stores a new identity with a set of country data entries. + /// + /// # Arguments + /// + /// * `e` - The Soroban environment. + /// * `account` - The account address to associate with the identity. + /// * `identity` - The identity address to store. + /// * `country_data_list` - A vector of initial country data entries. + /// * `operator` - The address authorizing the invocation. + /// + /// # Events + /// + /// * topics - `["identity_stored", account: Address, identity: Address]` + /// * data - `[]` + fn add_identity( + e: &Env, + account: Address, + identity: Address, + country_data_list: Vec, + operator: Address, + ); + + /// Removes an identity and all associated country data entries. + /// + /// # Arguments + /// + /// * `e` - The Soroban environment. + /// * `account` - The account address whose identity is being removed. + /// * `operator` - The address authorizing the invocation. + /// + /// # Events + /// + /// * topics - `["identity_unstored", account: Address, identity: Address]` + /// * data - `[]` + /// + /// Emits for each country data removed: + /// * topics - `["country_removed", account: Address, country_data: + /// CountryData]` + /// * data - `[]` + fn remove_identity(e: &Env, account: Address, operator: Address); + + /// Modifies an existing identity. + /// + /// # Arguments + /// + /// * `e` - The Soroban environment. + /// * `account` - The account address whose identity is being modified. + /// * `new_identity` - The new identity address. + /// * `operator` - The address authorizing the invocation. + /// + /// # Events + /// + /// * topics - `["identity_modified", old_identity: Address, new_identity: + /// Address]` + /// * data - `[]` + fn modify_identity(e: &Env, account: Address, identity: Address, operator: Address); + + /// Recovers an identity by transferring it from an old account to a new + /// account. + /// + /// # Arguments + /// + /// * `e` - The Soroban environment. + /// * `old_account` - The account address from which to recover the + /// identity. + /// * `new_account` - The account address to which the identity will be + /// transferred. + /// * `operator` - The address authorizing the invocation. + /// + /// # Events + /// + /// * topics - `["identity_recovered", old_account: Address, new_account: + /// Address]` + /// * data - `[]` + fn recover_identity(e: &Env, old_account: Address, new_account: Address, operator: Address); + + /// Retrieves the stored identity for a given account. + /// + /// # Arguments + /// + /// * `e` - The Soroban environment. + /// * `account` - The account address to query. + fn stored_identity(e: &Env, account: Address) -> Address; + + /// Retrieves the recovery target address for a recovered account. + /// + /// Returns `Some(new_account)` if the account has been recovered to a new + /// account, or `None` if the account has not been recovered. + /// + /// # Arguments + /// + /// * `e` - The Soroban environment. + /// * `old_account` - The old account address to check. + fn get_recovered_to(e: &Env, old_account: Address) -> Option
; +} +``` + +### IdentityClaims Interface + +The `IdentityClaims` contract manages on-chain identity claims with +cryptographic signatures. This contract is controlled by the investor and +stores claims issued by trusted authorities. + +**Purpose:** + +- Store cryptographic claims issued to an investor +- Manage claim lifecycle (add, retrieve, remove) +- Support multiple claims per topic from different issuers +- Enable claim-based identity verification + +**Interface:** + +```rust +pub trait IdentityClaims { + /// Adds a new claim to the identity or updates an existing one. + /// + /// # Arguments + /// + /// * `e` - The Soroban environment. + /// * `topic` - The claim topic (u32 identifier). + /// * `scheme` - The signature scheme used. + /// * `issuer` - The address of the claim issuer. + /// * `signature` - The cryptographic signature of the claim. + /// * `data` - The claim data. + /// * `uri` - Optional URI for additional claim information. + /// + /// # Events + /// + /// * topics - `["claim_added", claim_id: BytesN<32>, topic: u32]` + /// * data - `[]` + /// + /// OR (for updates): + /// + /// * topics - `["claim_changed", claim_id: BytesN<32>, topic: u32]` + /// * data - `[]` + fn add_claim( + e: &Env, + topic: u32, + scheme: u32, + issuer: Address, + signature: Bytes, + data: Bytes, + uri: String, + ) -> BytesN<32>; + + /// Retrieves a claim by its ID. + /// + /// # Arguments + /// + /// * `e` - The Soroban environment. + /// * `claim_id` - The unique claim identifier. + fn get_claim(e: &Env, claim_id: BytesN<32>) -> Claim; + + /// Retrieves all claim IDs for a specific topic. + /// + /// # Arguments + /// + /// * `e` - The Soroban environment. + /// * `topic` - The claim topic to filter by. + fn get_claim_ids_by_topic(e: &Env, topic: u32) -> Vec>; +} +``` + +**Claim Structure:** + +Each claim contains: + +- **Topic**: Numeric identifier (e.g., KYC=1, AML=2) +- **Scheme**: Signature algorithm identifier +- **Issuer**: Address of the claim issuer contract +- **Signature**: Cryptographic signature +- **Data**: Claim information (can include expiration, metadata) +- **URI**: Optional URI for additional information + +### ClaimIssuer Interface + +The `ClaimIssuer` contract validates cryptographic claims about investors. +These contracts are operated by trusted third parties (KYC providers, +compliance firms). + +**Purpose:** + +- Validate cryptographic signatures on claims +- Support multiple signature schemes (Ed25519, Secp256k1, Secp256r1) +- Manage claim revocation and expiration +- Provide key management for signing authorities + +**Interface:** + +```rust +pub trait ClaimIssuer { + /// Validates whether a claim is valid for a given identity. Panics if claim + /// is invalid. + /// + /// # Arguments + /// + /// * `e` - The Soroban environment. + /// * `identity` - The identity address the claim is about. + /// * `claim_topic` - The topic of the claim to validate. + /// * `scheme` - The signature scheme used. + /// * `sig_data` - The signature data as bytes: public key, signature and + /// other data required by the concrete signature scheme. + /// * `claim_data` - The claim data to validate. + fn is_claim_valid( + e: &Env, + identity: Address, + claim_topic: u32, + scheme: u32, + sig_data: Bytes, + claim_data: Bytes, + ); +} +``` + +### Verification Flow + +The following describes how these components work together during token +operations: + +1. **Off-chain**: Investor submits identity documents to KYC Provider +2. **Off-chain**: KYC Provider verifies identity and signs claim with private + key +3. **On-chain**: Signed claim is stored in investor's IdentityClaims contract +4. **On-chain**: During token operations (transfer/mint), the token contract: + - Calls IdentityVerifier's `verify_identity()` + - Required claim topics from ClaimTopicsAndIssuers is retrieved + - Claims from investor's IdentityClaims contract are fetched + - ClaimIssuer is called to validate signatures + - Revocation status is checked + - Issuer trust is verified via ClaimTopicsAndIssuers + +**Note**: The KYC Provider and Claim Issuer are the same entity. Signing +happens off-chain; only the signed claim and revocation data are stored +on-chain. + +--- + +## Changelog + +- `v0.1.0` - Initial draft