Skip to content
 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

225 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

C-Address Onboarding Bridge

Route funds directly to Soroban smart accounts (C-addresses) without requiring a traditional G-address. The onboarding layer for all Soroban dApps.

Rust Soroban TypeScript License


The Problem

The shift to C-addresses (Soroban smart accounts) is critical for next-generation dApps on Stellar, but two major adoption blockers persist:

  1. Funding friction: Users cannot easily fund a C-address without first using a traditional G-address
  2. Lack of tooling: No modern onboarding flow exists around the Smart Account standard by OpenZeppelin

In plain terms: new users can't interact with Soroban dApps directly. They need an old-style Stellar account first, which kills UX for mainstream adoption.

The Solution

A protocol and backend infrastructure that lets anyone fund a Soroban smart account (C-address) directly from a CEX withdrawal, a credit card, or an existing G-address — without the user understanding the underlying account model.

Comparison to Account Abstraction

Concept Ethereum Stellar (this project)
Smart accounts EIP-4337 Account Abstraction Soroban C-addresses
Funding layer Paymasters Onboarding Bridge contract
Wallet integration ERC-4337 SDK TypeScript SDK
Fiat on-ramp Moonpay/Transak integration Same (via off-ramp module)

Architecture

┌─────────────┐     ┌──────────────┐     ┌─────────────────┐
│   Wallet/   │────▶│  SDK Client  │────▶│   API Server    │
│    dApp     │     │  (TypeScript)│     │   (Express)     │
└─────────────┘     └──────────────┘     └────────┬────────┘
                                                   │
                    ┌──────────────────────────────┼──────────────────┐
                    │              ┌───────────────▼────────┐        │
                    │              │   Onboarding Bridge    │        │
                    │              │   Soroban Contract     │        │
                    │              └───────────┬────────────┘        │
                    │                          │                     │
                    │              ┌───────────▼────────────┐        │
                    │              │    C-Address Target    │        │
                    │              └────────────────────────┘        │
                    │                                                │
                    │  ┌──────────┐  ┌──────────┐  ┌─────────────┐  │
                    │  │  Moonpay  │  │  Transak  │  │ CEX Router  │  │
                    │  └──────────┘  └──────────┘  └─────────────┘  │
                    └────────────────────────────────────────────────┘

Funding Flows

G-Address ──▶ Bridge Contract ──▶ C-Address
CEX        ──▶ Bridge Contract ──▶ C-Address
Credit Card ──▶ Moonpay/Transak ──▶ Bridge Contract ──▶ C-Address

Dependency Vulnerability Monitoring

The repository runs automated dependency vulnerability scanning through GitHub Actions and scheduled audits. The workflow currently:

  • runs a weekly dependency audit on the default branch and for pull requests,
  • publishes a machine-readable audit report plus a human-readable summary artifact,
  • tracks severity-based patch SLAs of critical (24 hours), high (7 days), moderate (30 days), and low (next release),
  • sends Slack notifications when the workflow is configured with a webhook secret.

Accepted-risk overrides must be documented in the issue tracker with the dependency name, severity, owner, and expiry date, and they should be reviewed on the next scheduled scan.

Project Structure

├── contracts/onboarding-bridge/      # Soroban smart contract (Rust)
│   ├── Cargo.toml
│   └── src/
│       ├── lib.rs                    # Core contract logic
│       └── test.rs                   # Contract unit tests
│
├── api/                              # Express API server
│   ├── package.json
│   └── src/
│       ├── index.ts                  # Server entry point
│       ├── config.ts                 # Environment configuration
│       ├── routes/                   # REST API routes
│       │   ├── quote.ts              # GET  /api/v1/quote
│       │   ├── funding.ts            # POST /api/v1/fund
│       │   ├── status.ts             # GET  /api/v1/status/:txHash
│       │   ├── offramp.ts            # POST /api/v1/offramp/{moonpay,transak}
│       │   └── cex.ts                # POST /api/v1/cex/route
│       ├── services/                 # Business logic
│       │   ├── soroban.ts            # Soroban RPC client
│       │   ├── moonpay.ts            # Moonpay integration
│       │   ├── transak.ts            # Transak integration
│       │   └── cex.ts                # CEX routing
│       └── middleware/
│           └── error.ts              # Error handling
│
├── sdk/                              # TypeScript SDK
│   ├── package.json
│   └── src/
│       ├── index.ts                  # Public API exports
│       ├── bridge.ts                 # BridgeClient class
│       ├── types.ts                  # TypeScript type definitions
│       └── utils.ts                  # Address validation, fee calculation
│
├── offramp/                          # Standalone off-ramp modules
│   ├── moonpay.ts                    # Moonpay widget + webhook verification
│   └── transak.ts                    # Transak widget generation
│
├── cex/                              # CEX withdrawal routing
│   ├── withdrawal-router.ts          # Pluggable routing engine
│   └── README.md                     # Exchange integration guide
│
├── .env.example                      # Environment variables template
├── tsconfig.base.json                # Shared TypeScript config
└── package.json                      # Workspace root

Soroban Smart Contract

Contract Address

onboarding-bridge — deployed on Stellar testnet/mainnet.

Interface

Function Auth Description
initialize(admin, fee_bps) Admin One-time initialization
version() None Returns contract version
admin() None Returns admin address
fee_bps() None Returns fee in basis points
accumulated_fees() None Returns total fees collected
set_fee(new_fee_bps) Admin Update fee rate
fund_c_address(source, target, token, amount, memo) Source Route funds from G → C address
withdraw_fees(to, token, amount) Admin Withdraw accumulated fees
route_from_exchange(exchange, target, token, amount, memo) Exchange CEX → C-address routing

Fee Model

Fees are calculated in basis points (1 bps = 0.01%):

  • Configurable by admin (max 10000 bps = 100%)
  • Deducted from the transfer amount
  • Accumulated in the contract for admin withdrawal
  • Emitted in funded event for transparency

Events

// On initialization
(event: "initialize", admin: Address, fee_bps: u32)

// On funding
(event: "funded", source: Address, target: Address, amount: i128, fee: i128, memo: String)

// On fee update
(event: "set_fee", new_fee_bps: u32)

// On fee withdrawal
(event: "withdrawn", to: Address, amount: i128)

API Versioning

The API now supports URL-based versioning and Accept header versioning. Version negotiation is handled by the middleware and returns standard deprecation headers on v1 responses.

  • v1: current stable endpoint family with deprecation headers and a sunset date
  • v2: compatible successor endpoints that can coexist with v1
  • alpha -> beta -> stable -> deprecated -> sunset: lifecycle is documented in the changelog below

Version Lifecycle and Changelog

Version Status Notes
v1 deprecated Legacy endpoints with deprecation and sunset headers
v2 beta Newer routing surface with version negotiation support

API Reference

GET /api/v1/quote

Get a funding quote including fee estimates.

Query Parameters:

Param Type Description
sourceAsset string Asset code (e.g. XLM, USDC)
amount string Amount in stroops (integer string)
targetAddress string Destination C-address

Response:

{
  "estimatedFee": "3",
  "expectedReceive": "997",
  "feeBps": 30,
  "rate": "1.0"
}

POST /api/v1/fund

Submit a funding transaction to the Soroban bridge contract.

Request Body:

{
  "sourceAddress": "G...",
  "targetAddress": "C...",
  "tokenAddress": "CC...",
  "amount": "1000",
  "memo": "onboarding",
  "sourceSecretKey": "S..."
}

Response:

{
  "status": "success",
  "hash": "a1b2c3d4..."
}

GET /api/v1/status/:txHash

Check the status of a funding transaction.

Response:

{
  "status": "success",
  "hash": "a1b2c3d4..."
}

POST /api/v1/offramp/moonpay

Generate a Moonpay widget URL for credit card → C-address funding.

Request Body:

{
  "currencyCode": "xlm",
  "walletAddress": "C...",
  "walletNetwork": "stellar",
  "baseCurrencyAmount": 100,
  "baseCurrencyCode": "USD",
  "email": "user@example.com"
}

Response:

{
  "url": "https://buy.moonpay.com?apiKey=..."
}

POST /api/v1/offramp/transak

Generate a Transak widget URL.

Request Body:

{
  "walletAddress": "C...",
  "network": "stellar",
  "fiatCurrency": "USD",
  "cryptoCurrency": "XLM",
  "fiatAmount": 100
}

Response:

{
  "url": "https://global-stg.transak.com?apiKey=..."
}

POST /api/v1/cex/route

Route a CEX withdrawal to a C-address.

Request Body:

{
  "exchange": "binance",
  "sourceAsset": "XLM",
  "amount": "10000000",
  "targetCAddress": "C...",
  "targetNetwork": "stellar",
  "memo": "bridge:binance:ABCD1234"
}

Response:

{
  "status": "pending",
  "withdrawalId": "bin-1712345678-a1b2c3",
  "estimatedArrival": "5-30 minutes",
  "fee": "0.0001"
}

SDK Usage

Installation

npm install @c-address-bridge/sdk

Multi-language examples

Python, Rust, Go, and Java examples live in examples/. Each includes a thin HTTP client, Docker support, and CI verification:

node examples/mock-server/server.mjs   # terminal 1
export BRIDGE_BASE_URL=http://localhost:3099
cd examples/python && python main.py     # or rust / go / java

See examples/README.md and video walkthroughs.

Quick Start

import { BridgeClient, utils } from '@c-address-bridge/sdk';

const client = new BridgeClient({
  baseUrl: 'https://api.bridge.example.com',
  apiKey: 'your-api-key',
});

// Get a quote
const quote = await client.getQuote({
  sourceAsset: 'XLM',
  amount: '10000000',  // 1 XLM in stroops
  targetAddress: 'C...',
});

console.log(`Fee: ${quote.estimatedFee} stroops`);
console.log(`You receive: ${quote.expectedReceive} stroops`);

// Fund a C-address (two-step: prepare → sign → submit)
const prepared = await client.prepareFundingTransaction({
  sourceAddress: 'G...',
  targetAddress: 'C...',
  tokenAddress: 'CC...',
  amount: '10000000',
});

// Sign the prepared transaction with your wallet (off-SDK), then submit
const result = await client.submitSignedXdr({ signedXdr: '' });

console.log(`Transaction hash: ${result.hash}`);

// Check status
const status = await client.getStatus(result.hash);
console.log(`Status: ${status.status}`);

// Validate addresses
console.log(utils.isValidStellarAddress('C...'));  // true
console.log(utils.isCAddress('C...'));             // true
console.log(utils.isGAddress('G...'));             // true
console.log(utils.isGAddress('C...'));             // false

// Calculate fees
const fee = utils.calculateFee(1000n, 30);  // 30 bps fee
console.log(`Fee: ${fee} stroops`);

Off-ramp Widget URLs

// Moonpay — credit card to C-address
const moonpay = await client.createMoonpayUrl({
  walletAddress: 'C...',
  currencyCode: 'xlm',
  walletNetwork: 'stellar',
  baseCurrencyAmount: 100,
  baseCurrencyCode: 'USD',
});

// Transak — credit card to C-address
const transak = await client.createTransakUrl({
  walletAddress: 'C...',
  network: 'stellar',
  fiatCurrency: 'USD',
  cryptoCurrency: 'XLM',
  fiatAmount: 100,
});

CEX Withdrawal Routing

const result = await client.routeCexWithdrawal({
  exchange: 'coinbase',
  sourceAsset: 'USDC',
  amount: '5000000',
  targetCAddress: 'C...',
  targetNetwork: 'stellar',
});

CEX Integration

Exchanges can integrate the bridge by implementing the WithdrawalRouter:

import { WithdrawalRouter, defaultCexHandlers } from './cex/withdrawal-router';

const router = new WithdrawalRouter();
router.registerExchange('my-exchange', {
  name: 'my-exchange',
  apiBaseUrl: 'https://api.my-exchange.com',
}, async (req, config) => {
  // Implement withdrawal API call
  // Route through bridge contract
  return {
    success: true,
    withdrawalId: 'tx-...',
    status: 'pending',
    estimatedCompletion: '5-30 minutes',
  };
});

Memo format for tracking: bridge:{exchange_name}:{c_address_suffix}

defaultCexHandlers are non-functional placeholders. The binance, coinbase, and kraken handlers exported from cex/withdrawal-router.ts never call an exchange API — they immediately return a fake success: true / status: 'pending' result. They exist only to illustrate the handler shape used in the snippet above. Registering them directly instead of supplying your own handler will silently "succeed" without ever calling the exchange or moving funds.


Development

Prerequisites

Setup

# Clone
git clone https://github.com/C-Address-Onboarding-Bridge/C-Address-Onboarding-Bridge-Backend.git
cd C-Address-Onboarding-Bridge-Backend

# Install JS dependencies
npm install

# Copy environment config
cp .env.example .env

Build

# Build Soroban contract
cd contracts/onboarding-bridge
cargo build

# Build TypeScript packages
cd ../..
npm run build

Test

# Run Soroban contract tests
cargo test

# Run JS/TS tests
npm run test --workspaces

Run API Server

npm run dev -w api

Server starts at http://localhost:3001. Health check: GET /health.


Deployment

Automated Contract Deployment Pipeline

Smart contract deployments are fully automated through GitHub Actions and version-tagged git pushes. No manual CLI commands are needed.

How it works

Tag format Network Approval
contract/v1.2.0-dev.3 testnet None (auto)
contract/v1.2.0 mainnet Required reviewers

Push a tag to trigger the pipeline:

# Deploy to testnet (automated)
git tag contract/v1.2.0-dev.1
git push origin contract/v1.2.0-dev.1

# Deploy to mainnet (requires approval in GitHub → Environments → mainnet)
git tag contract/v1.2.0
git push origin contract/v1.2.0

You can also trigger it manually from Actions → Deploy Contract.

Pipeline steps

Tag push → CI gate → Build WASM → (approval for mainnet) → Deploy
                                                                ↓
                                              Initialize → Verify → Save artifact
                                                                ↓
                                              Generate report → Notify Slack
                                                                ↓
                                              Publish GitHub Release (tag pushes)
  1. CI gate — all existing tests must pass before deploy starts.
  2. Build WASMstellar contract build with the release profile; SHA-256 is computed and stored.
  3. Mainnet approval — the mainnet GitHub Environment enforces required reviewers. No code runs until approved.
  4. Deploystellar contract deploy uploads the WASM; contract ID is captured.
  5. Initializeinitialize() is called with admin addresses, threshold, and fee parameters.
  6. Verifyversion() and fee_bps() are invoked on-chain to confirm the contract is live and correctly configured.
  7. Save artifactdeployments/deployment-<network>.json is written with all deployment metadata.
  8. Report — a Markdown deployment report is generated at reports/deployment-report-<network>-<timestamp>.md.
  9. GitHub Release — the WASM binary, artifact JSON, and report are attached to the release.
  10. Slack notification — team is notified (if SLACK_WEBHOOK_URL is configured).

Required GitHub secrets

Configure these under Settings → Secrets and variables → Actions:

Secret Description
SOROBAN_SOURCE_ACCOUNT Testnet deployer secret key (S...)
SOROBAN_SOURCE_ACCOUNT_MAINNET Mainnet deployer secret key (S...)
CONTRACT_ADMIN_ADDRESSES Comma-separated admin addresses for testnet
CONTRACT_ADMIN_ADDRESSES_MAINNET Comma-separated admin addresses for mainnet
SOROBAN_NETWORK_PASSPHRASE_MAINNET Mainnet network passphrase
SLACK_WEBHOOK_URL Optional — Slack incoming webhook URL

Optional GitHub variables (with defaults)

Variable Default Description
SOROBAN_RPC_URL_TESTNET https://soroban-rpc.testnet.stellar.org Testnet RPC
SOROBAN_RPC_URL_MAINNET https://mainnet.sorobanrpc.com Mainnet RPC
CONTRACT_THRESHOLD 1 Testnet multi-sig threshold
CONTRACT_THRESHOLD_MAINNET 2 Mainnet multi-sig threshold
CONTRACT_FEE_BPS 30 Fee in basis points
CONTRACT_MAX_FEE_BPS 1000 Fee cap in basis points
CONTRACT_MIN_AMOUNT 100 Minimum fund amount (stroops)
CONTRACT_MAX_AMOUNT 1000000000000 Maximum fund amount (stroops)

Deployment artifacts

Every successful run saves a JSON artifact in deployments/:

// deployments/deployment-testnet.json
{
  "network": "testnet",
  "contractId": "C...",
  "deployTx": "<64-char hex>",
  "wasmHash": "<sha256>",
  "wasmSize": 38912,
  "rpcUrl": "https://soroban-rpc.testnet.stellar.org",
  "deployedAt": "2026-06-30T01:44:05Z",
  "gitTag": "contract/v1.2.0-dev.1",
  "gitSha": "<commit sha>",
  "deployedBy": "github-actor",
  "initParams": {
    "admins": ["G..."],
    "threshold": 1,
    "feeBps": 30,
    "maxFeeBps": 1000,
    "minAmount": 100,
    "maxAmount": 1000000000000
  }
}

Local / manual deployment

For one-off deployments outside of CI:

# Build and deploy to testnet
SOURCE_ACCOUNT="S..." \
ADMIN_ADDRESSES="G...addr1,G...addr2" \
THRESHOLD=1 \
BRIDGE_FEE_BPS=30 \
  bash scripts/deploy-contract.sh --network testnet

# Dry run (build + validate, no on-chain operations)
SOURCE_ACCOUNT="S..." ADMIN_ADDRESSES="G..." \
  bash scripts/deploy-contract.sh --network testnet --dry-run

# Force re-deploy (even if an existing live contract is found)
SOURCE_ACCOUNT="S..." ADMIN_ADDRESSES="G..." \
  bash scripts/deploy-contract.sh --network testnet --reinstall

# Skip build (reuse last compiled WASM)
SOURCE_ACCOUNT="S..." ADMIN_ADDRESSES="G..." \
  bash scripts/deploy-contract.sh --network testnet --skip-build

Contract Rollback

Soroban contracts are immutable — rolling back means routing your API server to a previously-deployed contract ID. The pipeline preserves the previous artifact as deployments/deployment-<network>.prev.json.

# Roll back testnet to the previous deployment
SOURCE_ACCOUNT="S..." bash scripts/rollback-contract.sh --network testnet

# List all available rollback targets
bash scripts/rollback-contract.sh --network testnet --list

# Target a specific artifact
SOURCE_ACCOUNT="S..." bash scripts/rollback-contract.sh \
  --network testnet \
  --artifact deployments/deployment-testnet.prev.json

# Dry run — verify the rollback target is live without modifying artifacts
SOURCE_ACCOUNT="S..." bash scripts/rollback-contract.sh \
  --network testnet --dry-run

After rollback completes, update BRIDGE_CONTRACT_ID in your API server environment and redeploy the API.

The GitHub Actions workflow (deploy-contract.yml) automatically attempts rollback if the deploy step fails.

Contract Verification

The deployment pipeline verifies the contract is correctly deployed after every run. You can also verify manually:

# Check the on-chain version
stellar contract invoke \
  --id <contract-id> \
  --rpc-url https://soroban-rpc.testnet.stellar.org \
  --network-passphrase "Test SDF Network ; September 2015" \
  --source <your-secret> \
  -- version

# Check the configured fee
stellar contract invoke \
  --id <contract-id> \
  --rpc-url https://soroban-rpc.testnet.stellar.org \
  --network-passphrase "Test SDF Network ; September 2015" \
  --source <your-secret> \
  -- fee_bps

# Verify WASM hash matches artifact
sha256sum target/wasm32v1-none/release/onboarding_bridge.wasm
# Compare with deployments/deployment-testnet.json .wasmHash

Deployment Reports

A Markdown report is generated after every deployment and attached as a GitHub Release asset. Generate one manually from an existing artifact:

NETWORK=testnet bash scripts/generate-deployment-report.sh
# Output: reports/deployment-report-testnet-<timestamp>.md
#         reports/deployment-report-latest.md

API Server

# Set environment variables
export SOROBAN_RPC_URL=https://soroban-rpc.testnet.stellar.org
export BRIDGE_CONTRACT_ID=<deployed-contract-id>
export BRIDGE_FEE_BPS=30
export MOONPAY_API_KEY=your-key
export TRANSAK_API_KEY=your-key
export PORT=3001

# Start
npm start -w api

# Or using Docker
docker build -t c-address-bridge-api .
docker run -p 3001:3001 --env-file .env c-address-bridge-api

Blue-Green Deployment

export BLUE_URL=https://blue.example.com
export GREEN_URL=https://green.example.com
export DEPLOY_GREEN_COMMAND='bash ./scripts/deploy.sh --network testnet'
export SWITCH_TRAFFIC_COMMAND='echo "switch traffic to {{color}}"'
export POST_SWITCH_CHECK_COMMAND='bash ./scripts/smoke-test.sh'

npm run deploy:blue-green

The blue-green flow deploys to the inactive environment, runs smoke tests, switches traffic, keeps the previous environment warm for rollback, drains old connections, and records the active color for the next release.


Security

Security documentation lives in docs/security/:

  • Threat Model — STRIDE analysis covering all six threat categories, trust boundaries, security assumptions, and known risks
  • Incident Response Plan — detection, containment, and recovery playbooks for API key compromise, webhook forgery, contract admin key loss, and supply-chain attacks
  • SECURITY.md — responsible disclosure policy, bug reporting instructions, scope, and past audit findings

To report a vulnerability, see SECURITY.md. Do not open a public issue.

Architecture Decision Records

Architectural decisions are documented in docs/adr/README.md. Use the ADR template at docs/adr/template.md for new proposals.

Environment Variables

Variable Required Default Description
SOROBAN_RPC_URL Yes https://soroban-rpc.testnet.stellar.org Soroban RPC endpoint
SOROBAN_NETWORK_PASSPHRASE Yes Test SDF Network ; September 2015 Network passphrase
BRIDGE_CONTRACT_ID Yes Deployed contract ID
BRIDGE_FEE_BPS No 30 Fee in basis points
MOONPAY_API_KEY For Moonpay Moonpay API key
MOONPAY_SECRET_KEY For webhooks Moonpay webhook secret
TRANSAK_API_KEY For Transak Transak API key
TRANSAK_ENVIRONMENT No STAGING STAGING or PRODUCTION
BINANCE_API_KEY / BINANCE_API_SECRET For Binance withdrawals Binance API credentials
COINBASE_API_KEY / COINBASE_API_SECRET For Coinbase withdrawals Coinbase API credentials
KRAKEN_API_KEY / KRAKEN_API_SECRET For Kraken withdrawals Kraken API credentials
CORS_ORIGINS No — (all origins) Comma-separated list of allowed CORS origins; leave empty to allow all (development only)
PORT No 3001 API server port
HOST No 0.0.0.0 API server host
LOG_LEVEL No info Pino log level

Test Results

┌─────────────────────────────┬────────────┬──────────┬─────────┐
│ Component                   │   Tests    │  Passed  │  Build  │
├─────────────────────────────┼────────────┼──────────┼─────────┤
│ Soroban Contract (Rust)     │     4      │    4     │   ✓     │
│ API Server (TypeScript)     │     7      │    7     │   ✓     │
│ TypeScript SDK              │     6      │    6     │   ✓     │
└─────────────────────────────┴────────────┴──────────┴─────────┘

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

MIT — see LICENSE for details.


Related Projects

About

No description, website, or topics provided.

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages