# Distribution

Tokens are distributed from the issuer treasury to KYC-verified investors on their preferred blockchain.


# Distribution Flow

sequenceDiagram
    participant INV as Investor
    participant KYC as KYC Registry
    participant II as Issuer Interface
    participant ML as Master Ledger
    participant CF as Chain Fusion
    participant EXT as External Chain

    INV->>KYC: Complete KYC
    KYC-->>INV: Verified

    INV->>II: Purchase Request
    II->>KYC: Verify Status
    KYC-->>II: Verified

    INV->>II: Select Target Chain
    II->>ML: distribute(investor, amount, chain)

    alt ICP Chain
        ML->>ML: Transfer from Treasury
        ML-->>INV: Tokens on ICP
    else External Chain
        ML->>ML: Reserve from Treasury
        ML->>CF: request_mint(investor, amount, chain)
        CF->>EXT: Mint Tokens
        EXT-->>CF: Confirmed
        CF->>ML: confirm_distribution
        ML-->>INV: Tokens on External Chain
    end

# Investor Onboarding

# KYC Verification

Before receiving tokens, investors must complete KYC:

flowchart TB
    START[Start Onboarding] --> SUMSUB[Sumsub Verification]

    subgraph Sumsub
        DOC[Document Upload]
        FACE[Facial Recognition]
        AML[AML Screening]
    end

    SUMSUB --> DOC --> FACE --> AML

    AML --> RESULT{Approved?}
    RESULT -->|Yes| BIND[Bind to Principal]
    RESULT -->|No| REJECT[Rejected]

    BIND --> ELIGIBLE[Eligible for Distribution]

# KYC Levels

Level Requirements Limits
Basic ID + Selfie Up to $50,000
Enhanced Basic + Proof of Address Up to $500,000
Institutional Enhanced + Corporate Docs Unlimited

# Chain Selection

Investors choose their preferred chain for receiving tokens:

flowchart TB
    INV[Investor] --> SELECT{Select Chain}

    SELECT --> ICP[Internet Computer]
    SELECT --> EVM[EVM Chain]
    SELECT --> SOL[Solana]
    SELECT --> COSM[Cosmos]

    EVM --> ETH[Ethereum]
    EVM --> BASE[Base]
    EVM --> ARB[Arbitrum]

    COSM --> OSMO[Osmosis]

# Chain Requirements

Chain Address Format Example
ICP Principal abc12-defgh-...
Ethereum 0x + 40 hex 0x742d35Cc6634C0532925a3b844Bc9...
Base 0x + 40 hex 0x742d35Cc6634C0532925a3b844Bc9...
Solana Base58 7xKXtg2CW87d97TXJSDpbD5jBkhe...
Osmosis Bech32 osmo1abc123def456...

# Distribution Process

# On ICP

Direct transfer from issuer treasury:

sequenceDiagram
    participant II as Issuer Interface
    participant ML as Master Ledger
    participant INV as Investor

    II->>ML: distribute(token, investor, amount)
    ML->>ML: Check treasury balance
    ML->>ML: Verify investor KYC
    ML->>ML: Debit treasury
    ML->>ML: Credit investor
    ML->>ML: Record transaction
    ML-->>II: Transaction ID
    Note over INV: Balance updated

# On External Chains

Involves Chain Fusion for cross-chain minting:

sequenceDiagram
    participant II as Issuer Interface
    participant ML as Master Ledger
    participant CF as Chain Fusion
    participant EXT as External Chain

    II->>ML: distribute(token, investor, amount, chain)
    ML->>ML: Reserve tokens
    ML->>CF: request_mint(...)

    CF->>CF: Build transaction
    CF->>CF: Threshold signature
    CF->>EXT: Submit mint TX

    EXT-->>CF: TX Hash
    CF->>CF: Wait for finality
    EXT-->>CF: Confirmed

    CF->>ML: confirm_mint(request_id, tx_hash)
    ML->>ML: Complete distribution
    ML-->>II: Success

# Distribution Types

# Primary Distribution

Initial distribution to investors during offering period:

type PrimaryDistribution = record {
    token_id : TokenId;
    investor : Principal;
    amount : nat;
    target_chain : Chain;
    target_address : Text;
    purchase_price : nat;           // In settlement currency
    purchase_currency : Text;       // e.g., "USD", "USDC"
    subscription_date : Timestamp;
};

# Secondary Distribution

Issuer distributing from treasury after initial offering:

type SecondaryDistribution = record {
    token_id : TokenId;
    recipient : Principal;
    amount : nat;
    target_chain : Chain;
    target_address : Text;
    reason : DistributionReason;
};

type DistributionReason = variant {
    AdditionalPurchase;
    Replacement;            // Lost wallet recovery
    Transfer;               // Internal transfer
    Promotional;            // Marketing allocation
};

# Batch Distribution

For efficiency, multiple distributions can be batched:

flowchart LR
    BATCH[Batch Request] --> VALIDATE[Validate All]
    VALIDATE --> GROUP[Group by Chain]

    GROUP --> ICP_BATCH[ICP Batch]
    GROUP --> EVM_BATCH[EVM Batch]
    GROUP --> SOL_BATCH[Solana Batch]

    ICP_BATCH --> EXEC1[Execute]
    EVM_BATCH --> EXEC2[Execute]
    SOL_BATCH --> EXEC3[Execute]

# Batch Request

type BatchDistributeArgs = record {
    token_id : TokenId;
    distributions : vec DistributionEntry;
};

type DistributionEntry = record {
    recipient : Principal;
    amount : nat;
    target_chain : Chain;
    target_address : Text;
};

# Batch Limits

Chain Max per Batch Reason
ICP 1000 Canister message limit
Ethereum 100 Gas optimization
Base 200 Lower gas costs
Solana 50 Transaction size limit

# Address Verification

Before distribution, addresses are verified:

flowchart TB
    ADDR[Recipient Address] --> CHECK1{Valid Format?}
    CHECK1 -->|No| E1[Error: Invalid Format]
    CHECK1 -->|Yes| CHECK2{Not Blacklisted?}

    CHECK2 -->|No| E2[Error: Blocked Address]
    CHECK2 -->|Yes| CHECK3{Matches KYC Principal?}

    CHECK3 -->|No| E3[Error: Address Mismatch]
    CHECK3 -->|Yes| OK[Proceed]

# Address Binding

Investors can register addresses for each chain:

candid
service : {
    // Register address for a chain
    register_address : (Chain, Text) -> (Result<(), Error>);

    // Get registered addresses
    get_addresses : () -> (vec AddressBinding) query;

    // Verify address ownership (for EVM, sign message)
    verify_address : (Chain, Text, blob) -> (Result<(), Error>);
}

type AddressBinding = record {
    chain : Chain;
    address : Text;
    verified : bool;
    registered_at : Timestamp;
};

# Distribution Tracking

# Transaction Record

type DistributionRecord = record {
    id : TransactionId;
    token_id : TokenId;
    from : Principal;               // Treasury
    to : Principal;                 // Investor
    amount : nat;
    target_chain : Chain;
    target_address : Text;
    status : DistributionStatus;
    created_at : Timestamp;
    completed_at : opt Timestamp;
    external_tx_hash : opt Text;
};

type DistributionStatus = variant {
    Pending;
    Processing;
    Confirmed;
    Failed : Text;
};

# Query Interface

candid
service : {
    // Get distribution by ID
    get_distribution : (TransactionId) -> (opt DistributionRecord) query;

    // List distributions for a token
    list_distributions : (TokenId, Pagination) -> (vec DistributionRecord) query;

    // List distributions for an investor
    get_my_distributions : (Pagination) -> (vec DistributionRecord) query;
}

# Error Handling

# Distribution Errors

type DistributionError = variant {
    NotAuthorized;
    TokenNotFound;
    TokenNotActive;
    InsufficientTreasury;
    RecipientNotKyc;
    InvalidAddress : Text;
    ChainNotSupported;
    AddressBlocked;
    AddressNotVerified;
    AmountTooLow;
    AmountTooHigh;
    RateLimitExceeded;
    ChainFusionError : Text;
    InternalError : Text;
};

# Retry Logic

flowchart TB
    DIST[Distribution] --> SUBMIT[Submit to Chain]
    SUBMIT --> RESULT{Success?}

    RESULT -->|Yes| DONE[Complete]
    RESULT -->|No| CHECK{Retryable?}

    CHECK -->|Yes| RETRY[Retry with Backoff]
    RETRY --> SUBMIT

    CHECK -->|No| FAIL[Mark Failed]
    FAIL --> REFUND[Refund Treasury]

Failed distributions automatically refund the treasury reserve.


# Events

type DistributionEvent = variant {
    Initiated : record {
        distribution_id : TransactionId;
        token_id : TokenId;
        recipient : Principal;
        amount : nat;
        target_chain : Chain;
        timestamp : Timestamp;
    };
    Completed : record {
        distribution_id : TransactionId;
        external_tx_hash : opt Text;
        timestamp : Timestamp;
    };
    Failed : record {
        distribution_id : TransactionId;
        reason : Text;
        timestamp : Timestamp;
    };
};