Skip to content

ST0x-Technology/st0x.issuance

Repository files navigation

ST0x Issuance Bot

A Rust-based issuance bot that acts as the Issuer in Alpaca's Instant Tokenization Network (ITN). The bot implements the Issuer-side endpoints that Alpaca calls during mint/redeem operations, and coordinates with the Rain OffchainAssetReceiptVault contracts to execute the actual on-chain minting and burning of tokenized shares.

Overview

The issuance bot serves as the bridge between traditional equity holdings (at Alpaca) and on-chain semi-fungible tokenized representations (Rain SFT contracts). This is general infrastructure - any Authorized Participant (AP) can use it to mint and redeem tokenized equities.

Key Features

  • Account Linking: Connect AP accounts to the system
  • Asset Management: Configure which tokenized assets are supported
  • Minting: Convert traditional equity holdings to on-chain tokens
  • Redemption: Burn on-chain tokens and return underlying equity
  • Event Sourcing: Complete audit trail with time-travel debugging capabilities
  • CQRS Architecture: Separation of command and query responsibilities for scalability

Architecture

Event Sourcing & CQRS

The system uses Event Sourcing (ES) and Command Query Responsibility Segregation (CQRS) patterns:

  • Commands: Requests to perform actions (e.g., InitiateMint, ConfirmJournal)
  • Events: Immutable facts about what happened (e.g., MintInitiated, TokensMinted)
  • Aggregates: Business entities that process commands and produce events (Mint, Redemption, Account, TokenizedAsset)
  • Views: Read-optimized projections built from events for efficient querying
  • Event Store: Single source of truth - append-only log of all domain events in SQLite

Core Components

  • HTTP Server: Rocket.rs-based server implementing Alpaca ITN Issuer endpoints
  • Blockchain Client: Alloy-based client for interacting with Rain vault contracts
  • Alpaca Integration: Client for Alpaca's API endpoints
  • Monitor Service: Watches redemption wallet for incoming token transfers
  • SQLite Database: Event store and view repositories

Development Setup

Prerequisites

  • Nix with flakes enabled

Getting Started

  1. Clone the repository:

    git clone https://github.com/ST0x-Technology/st0x.issuance.git
    cd st0x.issuance
  2. Enter development environment:

    nix develop
  3. Set up environment variables:

    cp .env.example .env
    # Edit .env with your configuration
  4. Create and migrate database:

    sqlx db create
    sqlx migrate run
  5. Run tests:

    cargo test -q
  6. Start the server:

    cargo run

Authentication

Endpoints require API key authentication with IP whitelisting and rate limiting.

Configuration:

# Generate API key (min 32 chars)
ISSUER_API_KEY=$(openssl rand -hex 32)

# Configure IP whitelist (CIDR notation)
ALPACA_IP_RANGES="1.2.3.0/24,5.6.7.8/32"

Request format:

curl -X POST https://issuer.example.com/inkind/issuance \
  -H "X-API-KEY: <api-key>" \
  -H "Content-Type: application/json"

Security: API key constant-time comparison, 10 failed auth attempts/IP/min rate limit

Development Commands

Building & Running

cargo build              # Build the project
cargo run                # Run the HTTP server

Testing

cargo test --workspace   # Run all tests (including crates/)
cargo test -q            # Run all tests quietly
cargo test -q --lib      # Run library tests only
cargo test -q <name>     # Run specific test

Database Management

sqlx db create           # Create the database
sqlx migrate run         # Apply migrations
sqlx migrate revert      # Revert last migration
sqlx migrate reset -y    # Drop DB and re-run all migrations

Code Quality

cargo fmt                                                                                  # Format code
cargo fmt --all -- --check                                                                 # Check formatting
cargo clippy --workspace --all-targets --all-features -- -D clippy::all -D warnings        # Run linting

Project Structure

st0x.issuance/
├── src/
│   ├── lib.rs               # Library entry point with rocket setup
│   ├── main.rs              # Binary entry point (minimal)
│   ├── test_utils.rs        # Shared test utilities
│   ├── account/             # Account aggregate and endpoints
│   │   ├── mod.rs           # Aggregate, commands, events
│   │   ├── api.rs           # HTTP endpoints
│   │   └── view.rs          # Read model projections
│   ├── mint/                # Mint aggregate and endpoints
│   │   ├── mod.rs           # Aggregate, commands, events
│   │   ├── cmd.rs           # Command definitions
│   │   ├── event.rs         # Event definitions
│   │   ├── api/             # HTTP endpoints
│   │   └── view.rs          # Read model projections
│   ├── tokenized_asset/     # TokenizedAsset aggregate and endpoints
│   │   └── ...              # Similar structure to above
│   ├── alpaca/              # Alpaca API service
│   │   ├── mod.rs           # Service trait and types
│   │   ├── service.rs       # Real HTTP implementation
│   │   └── mock.rs          # Mock implementation for testing
│   └── blockchain/          # Blockchain service and types
│       ├── mod.rs           # Service trait and types
│       ├── service.rs       # Real Alloy implementation
│       └── mock.rs          # Mock implementation for testing
├── tests/                   # End-to-end integration tests
│   ├── e2e_mint_flow.rs     # Complete mint flow with Anvil
│   └── e2e_redemption_flow.rs  # Redemption flow with Anvil
├── crates/
│   └── sqlite-es/           # SQLite event store implementation
├── migrations/              # Database migrations
├── AGENTS.md                # AI agent development guidelines
├── CLAUDE.md                # Claude Code instructions
├── SPEC.md                  # Detailed specification
├── ROADMAP.md               # Development roadmap
└── README.md                # This file

Note: This project uses package by feature organization, not package by layer. Each feature module (account/, mint/, tokenized_asset/) contains all related code: types, errors, commands, events, aggregates, views, and endpoints.

API Endpoints

Endpoints We Implement (for Alpaca)

  • POST /accounts/connect - Link AP account to our system
  • POST /accounts/{client_id}/wallets - Whitelist a wallet for an AP account
  • GET /tokenized-assets - List supported tokenized assets
  • POST /inkind/issuance - Receive mint request from Alpaca
  • POST /inkind/issuance/confirm - Receive journal confirmation from Alpaca

Endpoints We Call (Alpaca)

  • POST /v1/accounts/{account_id}/tokenization/callback/mint - Confirm mint completed
  • POST /v1/accounts/{account_id}/tokenization/redeem - Initiate redemption
  • GET /v1/accounts/{account_id}/tokenization/requests - Poll request status

Mint Flow

  1. AP requests mint → Alpaca calls our /inkind/issuance endpoint
  2. We validate and respond with issuer_request_id
  3. Alpaca journals shares from AP to our custodian account
  4. Alpaca confirms journal → we receive /inkind/issuance/confirm
  5. We mint tokens on-chain via vault.deposit()
  6. We call Alpaca's callback endpoint

Redemption Flow

  1. AP sends tokens to our redemption wallet → we detect transfer
  2. We call Alpaca's redeem endpoint
  3. We poll for journal completion
  4. We burn tokens on-chain via vault.withdraw()

Configuration

Configuration is managed through environment variables. See .env.example for all available options.

Key configuration areas:

  • HTTP server settings
  • Alpaca API credentials
  • Blockchain RPC endpoints
  • Database connection
  • Operational parameters (gas limits, poll intervals, etc.)

Testing Strategy

The project uses Given-When-Then testing for aggregate logic:

MintTestFramework::with(mock_services)
    .given(vec![MintInitiated { /* ... */ }])
    .when(ConfirmJournal { issuer_request_id: "123" })
    .then_expect_events(vec![
        JournalConfirmed { /* ... */ },
        MintingStarted { /* ... */ }
    ]);

This approach enables:

  • Testing business logic in isolation
  • Clear test intent and readability
  • Complete coverage of state transitions
  • Easy mocking of external services

End-to-End Testing with Anvil

E2E tests in tests/ use Anvil (local Ethereum blockchain) for realistic on-chain testing:

  • LocalEvm: Test infrastructure that deploys vault contracts to Anvil
  • Real blockchain interactions: Tests execute actual on-chain deposits and transfers
  • WebSocket monitoring: Tests verify event subscriptions and real-time detection
  • In-memory database: Tests use SQLite in-memory for fast, isolated execution
  • Mock external APIs: Alpaca API calls use httpmock for deterministic testing

E2E tests validate complete flows from HTTP request through CQRS to on-chain execution.

Documentation

  • SPEC.md - Detailed specification of the system
  • ROADMAP.md - Development roadmap and milestones
  • AGENTS.md - Development guidelines for AI agents
  • CLAUDE.md - Instructions for Claude Code

Contributing

This project follows strict development practices focused on code quality and maintainability:

Architecture & Design

  • Event Sourcing & CQRS: All state changes captured as immutable events
  • Type-Driven Design: Use algebraic data types to make invalid states unrepresentable
  • Functional Patterns: Prefer functional programming patterns and iterators over imperative loops
  • Feature Development: Implement complete vertical slices (HTTP → commands → events → views)

Code Quality Standards

  • No Lint Suppression: Never use #[allow(clippy::*)] without explicit permission - fix the underlying code instead
  • Financial Data Integrity: All numeric conversions and financial operations must use explicit error handling - never silently cap, truncate, or provide default values
  • Error Handling: Avoid unwrap() even after validation - use proper error propagation
  • Visibility Levels: Keep visibility as restrictive as possible (pub(crate) over pub, private over pub(crate))
  • Comments: Only comment when adding context that cannot be expressed through code structure - avoid redundant comments

Workflow

Before submitting changes, always run in order:

  1. cargo test -q - Run all tests first
  2. cargo clippy --all-targets --all-features -- -D clippy::all - Fix all linting issues
  3. cargo fmt - Format code last

For detailed architectural patterns and design decisions, see SPEC.md.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •  

Languages