Blog

Developer Guide

Building Compliant Security Tokens on Stellar with ERC-3643: A Developer's Guide to Permissioned Assets

Stellar's membership in the ERC-3643 Association, combined with the network surpassing $2 billion in tokenized real-world assets, signals a clear direction: Stellar is becoming the preferred settlement layer for regulated, permissioned tokens. For developers, this means understanding how to issue compliant security tokens, manage permissioned transfers, and query on-chain compliance state through APIs.

What Is ERC-3643

ERC-3643, also known as T-REX (Token for Regulated EXchanges), is a standard originally created for Ethereum that defines how security tokens should handle identity verification, transfer restrictions, and compliance enforcement on-chain.

The standard has three core components:

ComponentPurposeStellar Equivalent
Identity RegistryMaps wallet addresses to verified identitiesIssuer account + SEP-0012 KYC
Compliance ContractEnforces transfer rules (jurisdiction, holding period, investor limits)Authorization flags + Soroban contracts
Token ContractThe security token itselfNative Stellar asset

Stellar does not implement ERC-3643 identically to Ethereum. Instead, Stellar's native asset model provides several of the same guarantees at the protocol level, and Soroban smart contracts fill the gaps.

Stellar's Native Compliance Features

Asset Authorization Flags

Every Stellar asset issuer can set flags that control how the asset behaves:

const StellarSdk = require('@stellar/stellar-sdk');
const server = new StellarSdk.Horizon.Server('https://horizon.stellar.org');

// Configure an issuer account with compliance flags
async function configureIssuer(issuerKeypair) {
  const account = await server.loadAccount(issuerKeypair.publicKey());

  const tx = new StellarSdk.TransactionBuilder(account, {
    fee: '100',
    networkPassphrase: StellarSdk.Networks.PUBLIC,
  })
    .addOperation(StellarSdk.Operation.setOptions({
      setFlags:
        StellarSdk.AuthRequiredFlag |
        StellarSdk.AuthRevocableFlag |
        StellarSdk.AuthClawbackEnabledFlag,
    }))
    .setTimeout(30)
    .build();

  tx.sign(issuerKeypair);
  return server.submitTransaction(tx);
}

Flag Behavior Matrix

FlagEffectUse Case
AUTH_REQUIREDHolders must be approved by issuer before receivingKYC enforcement
AUTH_REVOCABLEIssuer can freeze individual accountsRegulatory freeze orders
AUTH_CLAWBACK_ENABLEDIssuer can seize tokens from any holderCourt orders, compliance enforcement
AUTH_IMMUTABLEFlags cannot be changed once setLock configuration permanently

Trustline Authorization Flow

With AUTH_REQUIRED enabled, every new holder must be explicitly approved:

// Investor creates a trustline (requests to hold the asset)
async function createTrustline(investorKeypair, assetCode, issuerPublicKey) {
  const account = await server.loadAccount(investorKeypair.publicKey());
  const asset = new StellarSdk.Asset(assetCode, issuerPublicKey);

  const tx = new StellarSdk.TransactionBuilder(account, {
    fee: '100',
    networkPassphrase: StellarSdk.Networks.PUBLIC,
  })
    .addOperation(StellarSdk.Operation.changeTrust({ asset }))
    .setTimeout(30)
    .build();

  tx.sign(investorKeypair);
  return server.submitTransaction(tx);
}

// Issuer approves the trustline after KYC verification
async function approveTrustline(issuerKeypair, investorPublicKey, assetCode) {
  const issuerAccount = await server.loadAccount(issuerKeypair.publicKey());
  const asset = new StellarSdk.Asset(assetCode, issuerKeypair.publicKey());

  const tx = new StellarSdk.TransactionBuilder(issuerAccount, {
    fee: '100',
    networkPassphrase: StellarSdk.Networks.PUBLIC,
  })
    .addOperation(StellarSdk.Operation.setTrustLineFlags({
      trustor: investorPublicKey,
      asset,
      flags: {
        authorized: true,
        authorizedToMaintainLiabilities: false,
      },
    }))
    .setTimeout(30)
    .build();

  tx.sign(issuerKeypair);
  return server.submitTransaction(tx);
}

Adding ERC-3643 Compliance with Soroban

Stellar's native flags handle the basics. But ERC-3643 compliance requires additional logic: transfer restrictions between verified investors, holding period enforcement, and investor count limits. This is where Soroban contracts come in.

Compliance Contract Architecture

Identity Registry (Soroban) --> Compliance Module (Soroban) --> Token Issuer Logic (Off-chain)
Maps addresses to claims       Evaluates transfer rules        Approves/denies trustlines

Example: Identity Registry Contract

// Soroban contract: Identity Registry (simplified)
#[contract]
pub struct IdentityRegistry;

#[contractimpl]
impl IdentityRegistry {
    pub fn register_identity(
        env: Env,
        investor: Address,
        jurisdiction: Symbol,
        accreditation_level: u32,
        kyc_expiry: u64,
    ) {
        let admin: Address = env.storage().instance()
            .get(&symbol!("admin")).unwrap();
        admin.require_auth();

        env.storage().persistent().set(&investor, &IdentityClaim {
            jurisdiction,
            accreditation_level,
            kyc_expiry,
            registered_at: env.ledger().timestamp(),
        });
    }

    pub fn can_transfer(
        env: Env,
        from: Address,
        to: Address,
        _amount: i128,
    ) -> bool {
        let from_claim: IdentityClaim = env.storage().persistent()
            .get(&from).unwrap();
        let to_claim: IdentityClaim = env.storage().persistent()
            .get(&to).unwrap();
        let now = env.ledger().timestamp();

        // Rule 1: Both parties must have valid KYC
        if from_claim.kyc_expiry < now || to_claim.kyc_expiry < now {
            return false;
        }

        // Rule 2: US accredited investors only
        if to_claim.jurisdiction == symbol!("US")
            && to_claim.accreditation_level < 1 {
            return false;
        }

        // Rule 3: 12-month holding period
        let holding_period = 365 * 24 * 60 * 60;
        if now - from_claim.registered_at < holding_period {
            return false;
        }

        true
    }
}

Querying Compliant Asset State

List All Holders of a Permissioned Asset

const HORIZON = 'https://horizon.stellar.org';

async function getAuthorizedHolders(assetCode, issuerPublicKey) {
  const res = await fetch(
    `${HORIZON}/accounts?asset=${assetCode}:${issuerPublicKey}&limit=200`
  );
  const data = await res.json();

  return data._embedded.records.map(account => {
    const trustline = account.balances.find(
      b => b.asset_code === assetCode && b.asset_issuer === issuerPublicKey
    );

    return {
      accountId: account.id,
      balance: trustline?.balance,
      authorized: trustline?.is_authorized,
      frozen: !trustline?.is_authorized &&
              trustline?.is_authorized_to_maintain_liabilities,
    };
  }).filter(h => h.balance);
}

Monitor Authorization Changes

Track when the issuer approves, freezes, or claws back assets:

async function monitorAuthorizationChanges(issuerPublicKey) {
  const res = await fetch(
    `${HORIZON}/accounts/${issuerPublicKey}/operations?limit=200&order=desc`
  );
  const data = await res.json();

  return data._embedded.records
    .filter(op =>
      op.type === 'set_trust_line_flags' ||
      op.type === 'allow_trust' ||
      op.type === 'clawback'
    )
    .map(op => ({
      type: op.type,
      trustor: op.trustor,
      asset: op.asset_code,
      timestamp: op.created_at,
      transactionHash: op.transaction_hash,
    }));
}

Real-World Asset Categories on Stellar

The $2B+ RWA milestone includes multiple asset categories:

CategoryExamplesCompliance Level
Government bondsU.S. Treasuries, EU bondsHigh (accredited investors)
Corporate bondsInvestment-grade debtHigh
Real estateTokenized property sharesMedium-High
Fund sharesETF tokens, money market fundsHigh
Carbon creditsVerified emission reductionsMedium

Each category has different compliance requirements, but the Stellar infrastructure (auth flags + Soroban contracts) supports all of them.

How LumenQuery Helps

LumenQuery provides the API infrastructure to build and monitor compliant security tokens:

  • Horizon API: Query trustline authorization status, track payments, and monitor issuer operations via api.lumenquery.io
  • Soroban RPC: Interact with compliance contracts via rpc.lumenquery.io
  • Analytics Dashboard: Track security token issuance volume on /analytics/tokens
  • Smart Contract Explorer: Inspect compliance contract state on /contracts
  • Portfolio Intelligence: Monitor security token positions on /portfolio

  • *Build compliant security tokens on reliable infrastructure. LumenQuery provides managed Horizon API and Soroban RPC with compliance monitoring tools built in. Start free.*