Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 

Repository files navigation

WalletSuite for Stellar: Technical Architecture

Stellar Community Fund, Build Award, Integration Track. Technical architecture document (SCF application Q16).

Status: complete design, ready to build. Every decision below is made. Alternatives were evaluated and resolved. This document specifies what we will build on Stellar, how it maps onto a wallet stack that is already in production on four chain families, and why the crypto and integration risk is low.


Table of contents

  1. Executive summary
  2. Current production platform (honest baseline)
  3. Stellar chain enablement (the substrate all four integrations require)
    • 3.1 MPC signing layer
    • 3.2 Backend chain family
    • 3.3 Account lifecycle and sponsorship
    • 3.4 MCP and AI-agent layer
    • 3.5 REST and data surface for Stellar
    • 3.6 Alternatives considered and resolved
    • 3.7 Amount precision and data types
  4. Per-integration architecture
    • 4.1 Anchor Platform (wallet-side SEP surface)
    • 4.2 Soroswap (DeFi aggregator)
    • 4.3 Stellar Disbursement Platform (SDP)
    • 4.4 CCTP (cross-chain USDC)
  5. Security model
  6. Testing and rollout
  7. Risks and mitigations
  8. Milestone mapping

1. Executive summary

WalletSuite for Stellar brings non-custodial MPC wallets, fiat on and off ramps, swaps, and bulk disbursements to Stellar through one API that both developers and AI agents can operate.

We already run this stack in production. Today WalletSuite serves four chain families end to end: Ethereum and the wider EVM set, Tron, Solana, and Bitcoin. The grant makes Stellar the fifth family and wires it into four building blocks from the SCF Integration List:

# Building block List category What we build
1 Anchor Platform (SDF-maintained) On/Off-Ramping Wallet-side client of the Anchor Platform SEP surface: SEP-1 discovery, SEP-10 web auth where the MPC signer signs the challenge, SEP-24 interactive deposit and withdraw with status polling. Validated against the SDF reference anchor on testnet and at least one production anchor on mainnet.
2 Soroswap DeFi (aggregator) Kotlin client of the Soroswap Aggregator API for quotes and optimal routes across Soroban AMMs and the classic DEX, unsigned swap XDR, MPC signing, Soroban RPC submission. Wired into our existing swap-engine provider abstraction.
3 Stellar Disbursement Platform (SDF-maintained) Payments / bulk disbursements WalletSuite wallets made SDP-ready: deep-link handling, on-demand sponsored account and trustline creation, SEP-24 registration. A self-hosted SDP instance demonstrates end-to-end disbursements into WalletSuite MPC wallets.
4 CCTP (Circle) Cross-Chain Native USDC transfer between our existing EVM chains and Stellar: burn on EVM with our existing secp256k1 signer, attestation via Circle's public API, mint on Stellar through the CCTP Soroban contracts. Productized as a treasury-flow API.

The ecosystem value is concrete. Existing WalletSuite clients and every new developer gain one API to Stellar's anchors, DEX liquidity, disbursement rails, and cross-chain USDC. Uniquely, AI agents gain that same access through our MCP server, whose initial release is in final development. This expands the utility of Stellar for developers and users, which is what the Integration Track funds.

None of the four integrations is possible without Stellar-native wallet enablement first. That enablement (a ChainKind::Stellar in the MPC engine, a STELLAR chain family in the backend, and Stellar exposure through the existing MCP tools) is the direct and necessary cost of the chosen integrations, not a side project. Section 3 specifies it.

Why this architecture is low-risk

The hard part of any new chain is signing, and for Stellar the hard part is already solved in our stack. Our MPC engine already runs an ed25519 threshold signer in production. It is the same signer we use for Solana today. Stellar's signing contract is exactly the contract that signer already fulfills: build a transaction, hash it to a 32-byte digest, and produce one raw 64-byte ed25519 signature over that digest that verifies under a single ed25519 public key. That aggregate public key becomes the Stellar account's master key directly, so there is no on-chain multisig to set up and no protocol change to make. We introduce no new cryptography for Stellar. We reuse the ed25519 curve we already ship. The founder personally built and shipped the Stellar integration inside Trust Wallet as Lead Blockchain Engineer, so the transaction-envelope and StrKey details are known quantities from shipped experience, not research. The wallet-side SEP flows are fully specified in this document (Sections 4.1 through 4.4) rather than claimed as previously shipped. The one crypto item that deserves explicit confirmation, RFC 8032 signature compatibility of the threshold scheme against a Stellar verifier, is scheduled as a week-1 validation task with a stated fallback (Section 3.1). Everything else is integration work against documented APIs, three of the four blocks carry SCF list estimates of two weeks or less, and no committed deliverable depends on a partnership approval outside our control.


2. Current production platform (honest baseline)

This section states plainly what exists today and what does not. Zero Stellar code exists in any WalletSuite repository right now. Building it is the purpose of this application. Everything below is built and running unless marked otherwise.

2.1 MPC engine (built)

  • Protocol: 2-of-2 threshold signing. The client is party 0 and holds one share. The WalletSuite cosigner is party 1 and holds the other. The full private key is never reconstructed at any point. There is no recovery in the current release: a lost client share strands the wallet's funds, so callers persist shares durably. An optional recovery add-on is planned as a later product option.
  • Curves: exactly two. Secp256k1 via threshold ECDSA, and Ed25519 via a published, peer-reviewed threshold EdDSA protocol. A single dual-curve wallet holds one share blob per curve under a shared key id derived from the secp256k1 master public key.
  • Reach: the engine derives addresses for roughly 20 configured networks, and the platform's wider wallet layer derives and validates addresses on 80+ networks. All EVM networks share one secp256k1 EIP-55 derivation and one signing path, so they are the same address reused per chain id, not separate integrations. Two curves cover the signing needs of most major chains. Four families are in production end to end.
  • Core and bindings: one Rust core with Node and Python bindings. A browser and WASM binding is the planned next layer and does not exist yet.
  • Sign surface: the SDK exposes per-chain-family typed methods (for example sign_evm_1559 and sign_evm_7702, plus Bitcoin, Solana, and Tron modules), not a single generic hash signer. Adding Stellar means adding a Stellar signing path, described in Section 3.1.

2.2 Data and transaction API (built)

A Kotlin and Spring Boot service exposes a unified multichain surface: balances, asset metadata, prices, fee quotes, prepare-sign, broadcast, and transaction status and history. It ships 30 controllers and 48 documented endpoints. The dispatch core is a chain registry organized by chain family; four families resolve today (Ethereum, Tron, Solana, Bitcoin) and every other chain key is rejected. Adding Stellar means a new family branch and a new RPC client family, detailed in Section 3.2.

Supporting subsystems in production: outbound customer webhooks (ingest, outbox, dispatcher, Standard Webhooks HMAC), usage metering (outbox to the auth service), and a multi-provider RPC layer with per-provider health and circuit breaking.

2.3 Swap engine (in progress)

A multi-provider swap engine is under active development on a feature branch and is not yet merged. It defines a SwapProvider service-provider interface (quote, build, cross-chain support flags) with a set of liquidity-provider adapters, plus fee, analytics, and provider-health subsystems. Soroswap plugs into this same interface (Section 4.2). We state its status plainly: the abstraction exists on the branch and is being finalized, and the Stellar Soroswap adapter is designed against that finalized interface.

2.4 MCP server for AI agents (initial release in final development)

The MCP server is in final development, with its initial version nearing release. It exposes 14 tools, grouped by function: create_wallet and get_wallet_address; get_balance, get_all_balances, get_price, get_fee_quote, get_tx_history, and get_tx_status; prepare_transfer, prepare_serialized_unsigned_tx, sign_transaction, and send_transaction; then resolve_asset and create_custom_policy. It is policy-gated by five capability bands, and every sensitive call is written to a tamper-evident append-only audit log. Most tools forward a chain string to the backend, so they reach a new chain as soon as the backend resolves it. Section 3.4 covers the additive changes Stellar needs here.

2.5 Traction (built)

WalletSuite is generating revenue with signed paying clients today across the four production families. The grant lets those clients, and every new one, reach Stellar rails through the same API. Exact financial figures live in the application form, not in this architecture document.


3. Stellar chain enablement

All four integrations sit on top of one substrate: native Stellar support in the MPC engine, the backend, and the MCP layer. This section is the load-bearing part of the design.

3.0 System context

graph TB
    subgraph Callers
        DEV[Developer apps<br/>REST + SDK]
        AGENT[AI agents<br/>MCP client]
        CLIENT[Existing paying clients]
    end

    subgraph WalletSuite
        API[Kotlin/Spring API<br/>balances, prices, fees,<br/>prepare-sign, broadcast, history]
        MCP[MCP server, initial release<br/>14 tools, policy bands,<br/>tamper-evident audit log]
        MPC[MPC engine<br/>2-of-2 threshold<br/>secp256k1 + ed25519]
        COSIGNER[WalletSuite cosigner<br/>party 1]
    end

    subgraph StellarNetwork[Stellar network]
        HORIZON[Horizon REST]
        SOROBAN[Soroban RPC]
        LEDGER[(Stellar ledger)]
    end

    subgraph Building blocks
        ANCHOR[Anchor Platform anchors<br/>SEP-1/10/24]
        SOROSWAP[Soroswap Aggregator API]
        SDP[Stellar Disbursement Platform]
        CCTP[Circle CCTP + attestation API]
    end

    DEV --> API
    CLIENT --> API
    AGENT --> MCP
    MCP --> API
    API --> MPC
    MPC <--> COSIGNER
    API --> HORIZON
    API --> SOROBAN
    HORIZON --> LEDGER
    SOROBAN --> LEDGER
    API --> ANCHOR
    API --> SOROSWAP
    API --> SDP
    API --> CCTP
    SOROSWAP --> SOROBAN
    CCTP --> SOROBAN
Loading

The MPC signer and cosigner never learn Stellar transaction semantics. They receive a 32-byte digest and return a 64-byte ed25519 signature. Everything Stellar-specific lives in the backend and the integration clients.

3.1 MPC signing layer

Decision: add a ChainKind::Stellar variant that binds to the existing Ed25519 curve. Reuse the threshold-EdDSA ceremony unchanged. Add exactly two Stellar-specific pieces: StrKey address derivation, and a Stellar transaction signing path that hashes an XDR payload and wraps the resulting signature.

Address derivation (StrKey)

A Stellar account id is the account's ed25519 public key wrapped in StrKey. Our engine already produces the raw 32-byte ed25519 public key for a wallet (this is exactly what we do for Solana). StrKey adds a deterministic, non-cryptographic encoding on top:

  1. Take the version byte. For a public G-address the value is 0x30, which base32-encodes to a leading G. For a muxed M-address the version byte is 0x60, which encodes to a leading M.
  2. Append the 32-byte ed25519 public key. For a muxed account, append the 32-byte key plus an 8-byte sub-account id.
  3. Append a 2-byte CRC16-XModem checksum computed over the version byte and payload.
  4. Base32-encode the whole buffer (RFC 4648, no padding). The result is the G... or M... address.

This is pure encoding, verifiable byte for byte against java-stellar-sdk and against the reference vectors in the SEP and StrKey specs. It introduces no cryptography.

Transaction signing path

Stellar's signing contract is deliberately narrow. The signer proves control of an ed25519 key over a 32-byte digest. Nothing else. The exact path:

sequenceDiagram
    participant BE as Backend (STELLAR family)
    participant MPC as MPC engine (ed25519)
    participant CS as Cosigner (party 1)

    BE->>BE: Build Transaction (ops, seq, fee, timebounds)
    BE->>BE: taggedTransaction = union{ ENVELOPE_TYPE_TX, tx }
    BE->>BE: networkId = SHA256(network passphrase)
    BE->>BE: payload = XDR( TransactionSignaturePayload{ networkId, taggedTransaction } )
    BE->>BE: digest = SHA256(payload)   // 32 bytes
    BE->>MPC: sign(share, ChainKind::Stellar, digest)
    MPC<<->>CS: 2-of-2 threshold-EdDSA ceremony over digest
    MPC-->>BE: signature (64 bytes, ed25519)
    BE->>BE: hint = last 4 bytes of signer public key
    BE->>BE: DecoratedSignature{ hint, signature }
    BE->>BE: attach to TransactionEnvelope.signatures
    BE-->>BE: XDR envelope ready to submit
Loading

Concretely:

  • The network passphrase is Public Global Stellar Network ; September 2015 on mainnet and Test SDF Network ; September 2015 on testnet. networkId = SHA256(passphrase).
  • TransactionSignaturePayload = { networkId: Hash(32 bytes), taggedTransaction: union }. The union is discriminated by ENVELOPE_TYPE_TX for a normal transaction and ENVELOPE_TYPE_TX_FEE_BUMP for a fee-bump transaction.
  • XDR-encode that payload, then SHA256 it to a 32-byte digest.
  • The engine performs the threshold-EdDSA ceremony over that digest and returns a standard 64-byte ed25519 signature.
  • Wrap the signature as DecoratedSignature { hint: 4 bytes, signature: 64 bytes }, where the hint is the last 4 bytes of the signer's ed25519 public key. Attach it to the envelope's signature list.

The MPC-aggregate ed25519 public key is the account master key. Stellar sees one ed25519 key and one ed25519 signature. It has no awareness that the key is threshold-held. No SetOptions multisig, no signer-weight juggling, and no on-chain setup is required. Stellar's native signer-weight system remains available if a hybrid custody model is ever wanted, but the committed design does not use it.

The signature contract matches capabilities our signer already exercises in production: like our Solana path, Stellar consumes a raw ed25519 signature over a digest, so no new cryptographic construction is required. The engineering work lies in everything around the signature: the envelope model, sequence semantics, the account and trustline lifecycle, and the Soroban authorization layer, which have no equivalent in our existing chain families.

Worked example (byte layout)

Both encodings are fixed-size and checkable by hand, which is one reason they carry no cryptographic risk.

StrKey for a public G-address is 35 bytes total, base32-encoded to exactly 56 characters:

version byte     0x30                     1 byte    encodes to a leading 'G'
ed25519 pubkey   the account public key   32 bytes
CRC16-XModem     checksum over the above  2 bytes
total            35 bytes  =>  base32 (RFC 4648, no padding)  =>  56 chars

A muxed M-address adds an 8-byte sub-id: version byte 0x60, 43 bytes total, 69 characters, leading M.

The signing digest is built once per transaction:

networkId      = SHA256( network passphrase )                    32 bytes
taggedTx       = XDR( union{ ENVELOPE_TYPE_TX, transaction } )   variable
signatureBase  = XDR( TransactionSignaturePayload{ networkId, taggedTx } )
digest         = SHA256( signatureBase )                         32 bytes   (this is signed)

The MPC signer receives digest and nothing else. It returns 64 bytes. The decorated signature attached to the envelope is hint (4 bytes) followed by signature (64 bytes), where the hint is the last 4 bytes of the account public key. A skeptical reviewer can reproduce every step above with java-stellar-sdk and a stock SHA-256, which is exactly how the week-1 validation task confirms it.

Soroban authorization entries

The path above authorizes a classic transaction through the single envelope signature. Soroban contract calls that move user funds (Soroswap swaps, CCTP mint) add one authorization layer: each InvokeHostFunction operation carries SorobanAuthorizationEntry entries returned by simulation, and how each entry is authorized depends on its credentials. The design handles both cases with the same signer:

  • Primary path: source-account credentials. We build or rebuild Soroban transactions with the user's MPC account as the transaction source and use SOROBAN_CREDENTIALS_SOURCE_ACCOUNT wherever the returned XDR permits. In that case the envelope signature already authorizes the invocation and no separate auth-entry signature is required. This is the default we target.
  • Address-credential path. Where an integration returns an auth entry with address credentials, that entry needs its own ed25519 signature. The same threshold-ed25519 signer signs SHA256(XDR(HashIDPreimage ENVELOPE_TYPE_SOROBAN_AUTHORIZATION{ networkId, nonce, signatureExpirationLedger, invocation })). That is one more 32-byte digest through the identical MPC signer, produced alongside the envelope signature. We take the nonce from the simulated entry, set signatureExpirationLedger a safe margin past the current ledger, and re-simulate and re-sign if the entry expires before submission.

The Soroswap flow consumes externally built swap XDR, so we do not assume source-account credentials there. The adapter inspects each returned auth entry and routes it through whichever of the two paths its credentials require before submission.

RFC 8032 compatibility (week-1 validation task, with fallback)

The one item worth confirming rather than assuming: a threshold-EdDSA scheme must emit a signature that a stock RFC 8032 verifier accepts under the single aggregate public key. Some threshold schemes hash the nonce or challenge in a non-standard way, or emit a non-canonical S, which a strict verifier can reject.

Why the risk is low, not merely asserted: our ed25519 threshold signer already runs in production for Solana, and the Solana runtime rejects signatures that are not valid canonical ed25519. The property we need is therefore strongly indicated by the production Solana signer, and proven for Stellar specifically by the week-1 test against libsodium, ed25519-dalek verify_strict, and java-stellar-sdk. Stellar adds its own digest construction (the network-id-prefixed payload above) on top of the identical signature primitive.

The week-1 validation task, scheduled at the very start of Tranche 1 and gating the milestone-1 date, is explicit:

  1. Run a 2-of-2 DKG on the ed25519 curve and derive the aggregate public key and its G-address.
  2. Produce a threshold signature over a set of known 32-byte digests, including real Stellar transaction digests built with java-stellar-sdk.
  3. Verify each signature under three independent stock verifiers: libsodium crypto_sign_verify_detached, ed25519-dalek verify_strict, and java-stellar-sdk's own signature verification (KeyPair.verify).
  4. Submit a signed transaction to testnet and confirm the network accepts it.

Fallback if any strict verifier rejects a signature: the threshold-EdDSA implementation is a dependency we vendor and own, and the only realistic failure modes are non-canonical encoding of R or an S value not reduced mod the group order L. Both are recoverable cases: where the deviation is a re-encoding or a reduction we control, we enforce the RFC 8032 canonical form in the signature-assembly code before wrapping the DecoratedSignature. Anything deeper than a recoverable encoding issue is covered by the independent-signer fallback below. If, against expectation, the aggregation scheme proved fundamentally incompatible, the design still lands cleanly because Stellar accounts support adding an independent ed25519 signer: we would attach a standard-signer key we control and disable the master key, keeping custody threshold-held one layer out. We do not expect to need either fallback. The task exists to prove the clean path on day one.

3.2 Backend chain family

Decision: add a STELLAR value to ChainFamily and a SupportedChain.STELLAR descriptor (key = "stellar", chainId = null, nativeSymbol = "XLM", nativeDecimals = 7), then implement a Stellar RPC client family alongside the existing rpc/evm, rpc/tron, rpc/solana, and rpc/bitcoin packages. Stellar uses 7 decimals: the smallest unit is the stroop, and 1 XLM equals 10,000,000 stroops.

graph TB
    subgraph Backend Stellar additions
        FAM[ChainFamily.STELLAR<br/>SupportedChain key=stellar]
        HZ[HorizonClient<br/>accounts, balances+trustlines,<br/>payments, submit, history]
        SR[SorobanRpcClient<br/>simulate, send, getTransaction,<br/>getLedgerEntries, getEvents]
        FEE[StellarFeeModel<br/>base fee + surge<br/>fee-stats endpoint]
        SEQ[SequenceAllocator<br/>per-account serialized]
        BUILD[Stellar prepare-sign builder<br/>payment, path-payment, change-trust]
        VAL[StrKey validator<br/>G / M addresses]
    end

    subgraph Integration clients
        SEPC[Anchor SEP client<br/>SEP-1/10/24]
        SWAPC[Soroswap adapter]
        SDPC[SDP adapter]
        CCTPC[CCTP orchestrator]
    end

    FAM --> HZ
    FAM --> SR
    FAM --> FEE
    FAM --> SEQ
    FAM --> BUILD
    FAM --> VAL
    BUILD --> SEQ
    SEPC --> HZ
    SWAPC --> SR
    SDPC --> HZ
    CCTPC --> SR
Loading

Horizon and Soroban RPC clients

Two endpoints, because Stellar splits classic data from smart-contract data:

  • Horizon (REST): account load (balances including per-asset trustline balances, subentry count, current sequence number), payments and effects, transaction submission, and history. Mature and stable.
  • Soroban RPC (JSON-RPC): simulateTransaction, sendTransaction, getTransaction, getLedgerEntries, and getEvents, required for any Soroban contract call. Horizon does not serve Soroban contract state, so Soroswap and CCTP need this second client.

Real-time observation of incoming payments, which the SEP-24 deposit, SDP disbursement, and CCTP mint flows all depend on, uses Horizon's server-sent-events payment stream with a saved cursor. If a stream drops, the client resumes from the cursor and falls back to polling, so no incoming transfer is missed and none is double-counted. Payment observation is idempotent on the transaction hash.

Both clients are health-checked and fail over across redundant providers using the same multi-provider RPC pattern already in the backend (SDF public infrastructure plus at least one commercial Soroban RPC provider).

JVM dependency

We depend on java-stellar-sdk for XDR building, Horizon integration, and Soroban RPC integration. It is JVM-native, so there is no FFI or sidecar. It is actively maintained (release 3.1.0 dated 2026-06-12, roughly three weeks before this design). One implementation task is called out explicitly: confirm the canonical Maven artifact before pinning a version, because a community continuation (lightsail-network/java-stellar-sdk) exists alongside the org-owned stellar/java-stellar-sdk. We verify which one Maven Central publishes and pin accordingly, rather than assuming.

Fee model

Stellar's fee model is unlike EVM gas or UTXO fee rates. Each operation carries a base fee, with a network minimum of 100 stroops per operation. When ledgers are congested the network applies surge pricing and orders transactions by the fee they bid per operation. Our StellarFeeModel reads the Horizon fee-stats endpoint to quote a competitive per-operation fee (for example a chosen percentile of recent ledger fees) and multiplies by the operation count. The low, medium, and high fee priorities exposed by our existing fee API map onto fee-stats percentiles. Because fees are bid per operation, the quote is deterministic given the operation count and the fee-stats snapshot.

Sequence-number management

Every Stellar account has a monotonically increasing 64-bit sequence number. A transaction must set sequence = account.sequence + 1 and succeeds only on an exact match. There are no nonce ranges and no out-of-order execution. Concurrent signing against one account therefore needs explicit sequencing, the same class of problem as EVM nonce management, which the backend already solves.

The SequenceAllocator design:

  • One serialized allocator per account, keyed by G-address. Concurrent prepare-sign requests for the same account are ordered through a per-account actor or lock, so no two in-flight transactions claim the same sequence.
  • On cold start or reconciliation the allocator loads the on-chain sequence from Horizon, then hands out last_assigned + 1 locally and tracks in-flight values.
  • On a submission that fails with tx_bad_seq, the allocator resynchronizes from Horizon and reassigns, then the transaction is rebuilt and re-signed.
  • For high-throughput sponsor and fee-payer accounts (Section 3.3), the design uses the channel-accounts pattern: several source accounts fronting one logical sponsor let independent sequence streams run in parallel, removing the single-account serialization bottleneck. This is the standard Stellar approach for concurrent submission at scale.

Prepare-sign envelope

The Stellar prepare-sign builder produces unsigned XDR for the operation types the integrations need: Payment and PathPaymentStrictSend/PathPaymentStrictReceive for value movement and swaps, and ChangeTrust for establishing trustlines. It sets the source account, the allocated sequence number, time bounds, and the fee, then returns the XDR so the existing prepare-sign to sign to broadcast flow (including the MCP prepare_serialized_unsigned_tx and sign_transaction tools) works unchanged in shape. For Soroban operations the builder produces an InvokeHostFunction operation and runs the simulate step described in Section 4.2 before signing.

Address validation

A StrKey validator accepts G... (public account) and M... (muxed) addresses by checking the version byte, base32 decoding, and CRC16 checksum. It rejects malformed input at the system boundary before any transaction is built.

3.3 Account lifecycle and sponsorship

Decision: end users start with zero XLM. WalletSuite sponsors account creation and trustlines using sponsored reserves, and sponsors transaction fees using fee-bump transactions. This is the design that makes an MPC WaaS usable by non-crypto users on Stellar.

  • Account creation: a new account is created by a CreateAccount operation sent from an existing funded account. A bare account must hold a minimum balance of (2 + subentry_count) * base_reserve, with the base reserve currently 0.5 XLM.
  • Trustlines: a classic non-XLM asset (for example Circle USDC, issued by GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN) requires a ChangeTrust operation before the account can hold it. Each trustline is a subentry and raises the reserve by 0.5 XLM.
  • Sponsored reserves: WalletSuite wraps creation and trustline setup in a sponsorship sandwich: BeginSponsoringFutureReserves(sponsored = newAccount), then the sponsored CreateAccount (starting balance may be zero when sponsored) and one or more sponsored ChangeTrust operations, then EndSponsoringFutureReserves. The sponsored XLM never leaves the sponsor account: the ledger records an encumbrance (numSponsoring) that raises the sponsor's own minimum balance, so no XLM sits in user accounts. Reserves are recoverable with the correct signatures: removing a trustline or merging an account reclaims the reserve and is signed by the wallet itself (cooperative by design, and a contractual wind-down obligation of the platform client whose capital funds the float), while the sponsor's unilateral RevokeSponsorship transfers the reserve to the account once the account can carry it, or to a replacement sponsor.
  • Fee sponsorship: a fee-bump transaction wraps an inner transaction signed by the user's MPC key inside an outer envelope whose feeSource is a WalletSuite fee account. The inner transaction is not re-signed. The fee account pays. End users need zero XLM to transact.
  • Sponsorship economics and controls: sponsorship is a per-client capability with explicit limits, not an open-ended commitment. Sponsored reserves are locked working capital, not spend: the sponsor treasury holds roughly 1.5 XLM per typical end user (account plus one trustline), reclaimable when entries are removed and transferable later via the protocol's RevokeSponsorship operation, so a user or platform can take over its own reserves once it holds XLM. Fee-bump is a per-transaction choice; any transaction can instead pay its own fee. The sponsor treasury is funded per platform client as part of that client's commercial terms (prefunded or metered, the same way clients pay for usage today), sits behind per-client flags and caps in the existing plan gating, and can be disabled for new accounts at any time without affecting existing accounts. At scale the float stays bounded by design: accounts are created on first use rather than at registration, trustlines are sponsored on demand rather than upfront, dormant entries are reclaimed back to the treasury, and pooled muxed accounts remain the documented option where per-user on-chain accounts are unnecessary. During the grant, only the testnet and mainnet-pilot sponsorship reserves are carried by the project, inside the budgeted infrastructure line.
  • Muxed accounts: noted as a scaling option, not committed. One funded pooled account plus per-user 64-bit sub-ids can represent many logical users for accounting without a base reserve per user. Because custody in our model is per underlying key, muxed accounts are an internal-accounting optimization we may adopt later, not part of the committed milestones.

3.4 MCP and AI-agent layer

Decision: no new MCP tools. Add Stellar as a chain value the existing tools accept, so agents can hold and operate policy-governed Stellar wallets through the MCP server.

The 14 tools already forward a chain string to the backend. get_balance, get_all_balances, get_price, get_fee_quote, get_tx_history, get_tx_status, prepare_transfer, prepare_serialized_unsigned_tx, send_transaction, and resolve_asset reach Stellar the moment the backend resolves the stellar key. Two additive changes complete the picture:

  1. Add a Stellar family to the MCP server's built-in chain snapshot so validate_address accepts StrKey G... and M... addresses. Today that snapshot validates EVM and Tron address shapes.
  2. Add stellar to the local-signing chain set if local agent-side signing of Stellar transactions is wanted in addition to the backend MPC path.

Policy bands and the tamper-evident audit log already gate and record every tool call, so a Stellar wallet operated by an agent inherits the same governance as every other chain with no new mechanism. The outcome is that Stellar becomes reachable by AI agents through an open MCP server (initial release in final development), which no other Stellar wallet stack offers today.

3.5 REST and data surface for Stellar

Stellar slots into the existing endpoint shapes. No new API style is introduced. The chain parameter accepts stellar, and responses carry Stellar-native fields where the model requires them. The one place Stellar genuinely diverges from EVM and Solana is balances, because a Stellar account holds assets per trustline rather than per token contract.

  • Balances. The balance response for chain=stellar returns the native XLM balance plus one entry per trustline (asset code, issuer, balance, limit, and authorization flag), along with the account subentry count and current sequence number as metadata.
  • Fee quote. Returns a per-operation base fee derived from the Horizon fee-stats endpoint, mapped to low, medium, and high through fee-stats percentiles, plus the operation count assumed for the quote.
  • Prepare-sign. The request accepts a Stellar operation kind (payment, path-payment, or change-trust), the source G-address, the asset (native, or code plus issuer), the amount in stroops, and optional sponsorship and fee-bump flags. The response is the unsigned XDR plus the allocated sequence number and the signing digest.
  • Broadcast and status. Submission goes through Horizon for classic operations or Soroban RPC for contract calls. Status polling maps Horizon result codes and Soroban getTransaction states onto the existing status vocabulary.
  • History. Read from Horizon payments and effects, normalized into the existing history response shape.
Surface Existing shape Stellar addition
GET balances per token per-trustline balances plus subentry count and sequence
GET fee quote gas or fee rate per-operation base fee from fee-stats
POST prepare-sign chain-native builder payment, path-payment, change-trust XDR plus sponsorship
POST broadcast RPC submit Horizon submit or Soroban sendTransaction
GET tx status chain-native Horizon result codes or Soroban getTransaction
GET history normalized Horizon payments and effects

3.6 Alternatives considered and resolved

This architecture is complete because the design choices are settled, not still open. The load-bearing decisions and the reason each was resolved:

  • MPC-aggregate key as master key, not Stellar-native multisig. Using the single aggregate ed25519 key as the account master key means Stellar sees one ordinary key, with no on-chain signer list to configure or attack. Native signer weights remain available for a future hybrid model, but adopting them now would add on-chain state for no custody benefit. Resolved in favor of the single-key path.
  • SEP-24 over SEP-6. SEP-24 delegates KYC and payment-method collection to the anchor's hosted webview, so WalletSuite never builds or holds that data. SEP-6 would force us to implement SEP-12 KYC collection ourselves at higher UX and compliance cost. Resolved in favor of SEP-24.
  • JVM-native Kotlin SEP client over a Node sidecar. The SEP client belongs in the same runtime as the transaction path it drives: java-stellar-sdk already covers transaction building, Horizon, and Soroban RPC on the JVM, so building the SEP-10 and SEP-24 client in Kotlin keeps one runtime, one test suite, and one operational surface instead of introducing a sidecar to deploy and monitor. Resolved in favor of the JVM-native client.
  • Self-hosted SDP instance over registry-listing-only. A self-hosted SDP proves end-to-end disbursements without waiting on an SDF-run approval, so the demonstrable deliverable has no external gate. The registry listing is pursued in parallel as upside. Resolved in favor of self-hosting for the milestone.
  • One real account per wallet now, muxed accounts later. Per-account custody is clean and matches the rest of the platform. Muxed sub-accounts are an internal-accounting optimization we can layer on later without changing custody. Resolved to ship per-account first.
  • Sponsored reserves and fee-bump over requiring users to hold XLM. Requiring end users to fund reserves and fees would break the WaaS onboarding experience. Sponsorship makes accounts usable from zero XLM. Resolved in favor of sponsorship.

3.7 Amount precision and data types

Amount handling is a common source of wallet bugs, so the precision model is fixed across the stack. Classic Stellar amounts (XLM and classic assets) have 7 decimal places and travel on the wire as signed 64-bit integers in stroops, where one unit of an asset is 10,000,000 stroops. Soroban token amounts are 128-bit integers (i128). The backend carries every amount as a smallest-units integer end to end, matching the platform's existing smallest-units policy for prepare-sign, and converts to and from human units only at the API boundary. Three consequences the design enforces:

  • No floating point touches an amount at any layer. Human-unit strings are parsed to integer stroops (or i128 for Soroban) at the boundary and range-checked before use.
  • The 7-decimal classic model is distinct from EVM 18-decimal and Solana 9-decimal, so the Stellar chain descriptor pins nativeDecimals = 7 and the balance and prepare-sign paths read it rather than a shared constant.
  • Soroban i128 amounts are handled as big integers, never truncated to 64 bits, so large-supply token transfers through Soroswap and CCTP stay exact.

4. Per-integration architecture

Each integration is one section with a sequence diagram. All four rest on Section 3.

4.1 Anchor Platform (wallet-side SEP surface)

What integrating Anchor Platform means for a wallet. The Anchor Platform is SDF software that anchors run to expose the standard SEP surface (SEP-1, SEP-10, SEP-24, and more). A wallet integrates that ecosystem by implementing the wallet side of those SEPs and driving anchors that run the Anchor Platform. That is exactly what a WaaS does, and it is the intended integration path for a wallet. We build a generic wallet-side SEP client, validate it against the SDF reference anchor (which runs the Anchor Platform) on testnet, then against at least one production anchor on mainnet. The result: every Anchor-Platform-powered anchor becomes reachable from any WalletSuite wallet or AI agent.

Scope decision. SEP-1, SEP-10, and SEP-24 are committed. SEP-6 and SEP-31 are out of scope: SEP-6 duplicates SEP-24 at a higher UX cost (the wallet would have to build KYC collection instead of delegating to the anchor's hosted webview), and SEP-31 implies a sending or receiving anchor posture with money-transmission compliance implications that do not fit a wallet-side grant. SEP-24 is the committed path and is the one the SDP also uses.

sequenceDiagram
    participant U as End user / agent
    participant WS as WalletSuite backend
    participant MPC as MPC signer (ed25519)
    participant A as Anchor (Anchor Platform)
    participant SN as Stellar network

    Note over WS,A: SEP-1 discovery
    WS->>A: GET /.well-known/stellar.toml
    A-->>WS: WEB_AUTH_ENDPOINT, SIGNING_KEY, TRANSFER_SERVER_SEP0024

    Note over WS,A: SEP-10 web auth
    WS->>A: GET challenge for account G...
    A-->>WS: challenge transaction (XDR), source = anchor SIGNING_KEY, seq 0
    WS->>WS: verify anchor SIGNING_KEY signature, source, network passphrase, manage_data nonce (64-byte base64), home_domain / web_auth_domain, timebounds
    WS->>WS: build signature digest (Section 3.1)
    WS->>MPC: sign(share, Stellar, digest)
    MPC-->>WS: 64-byte ed25519 signature -> DecoratedSignature
    WS->>A: POST signed challenge XDR
    A-->>WS: JWT

    Note over U,A: SEP-24 interactive deposit
    U->>WS: start deposit(asset)
    WS->>A: POST /transactions/deposit/interactive (JWT, asset)
    A-->>WS: interactive URL + transaction id
    WS-->>U: open webview at interactive URL
    U->>A: complete KYC, amount, payment method (anchor-hosted UI)
    loop poll until terminal
        WS->>A: GET /transaction?id= (JWT)
        A-->>WS: status: incomplete -> pending_user_transfer_start -> pending_anchor -> completed
    end
    A->>SN: send Stellar payment (asset, memo) to G...
    WS->>SN: watch account payments for matching memo
    SN-->>WS: incoming payment observed
    WS-->>U: deposit complete
Loading

SEP-10 signing. The challenge is itself a Stellar transaction: its source is the anchor's SIGNING_KEY, its sequence number is 0 (so it can never be submitted to the ledger), and it carries a manage_data operation whose source is our account. Before signing, the wallet verifies that the challenge is itself signed by the anchor's SIGNING_KEY (taken from the anchor's stellar.toml), that the source and network passphrase are correct, that the manage_data value is a 64-byte base64 nonce, that the home_domain and web_auth_domain match the expected anchor, and that the timebounds are current. Only then does it sign the challenge with the account's key using the exact path in Section 3.1. Signing a SEP-10 challenge is not a special case. It is one more 32-byte digest through the same MPC signer.

SEP-24 webview handoff. After SEP-10 returns a JWT, the wallet requests an interactive deposit or withdraw, receives an anchor-hosted URL and a transaction id, and opens the URL in a webview or browser tab. KYC, amount, and payment-method selection happen entirely on the anchor's hosted UI, so WalletSuite never handles that PII. The design allocation in the budget covers the wallet-side webview host and the status UI, not the KYC forms.

Status polling state machine. The wallet polls GET /transaction?id= and advances through incomplete, pending_user_transfer_start, pending_anchor, and completed, with error and refunded as terminal off-ramps. For a deposit, the terminal step is observing the incoming Stellar payment to our account carrying the expected memo.

Withdrawal path. A withdrawal reuses the same SEP-10 auth and SEP-24 interactive setup, then inverts the value leg. After the user confirms amount and destination in the anchor webview, the wallet builds a Stellar payment of the specified asset to the anchor's account with the anchor-provided memo, signs it through the MPC path in Section 3.1, and submits it. The wallet then polls to completed while the anchor pays out to the user's off-chain destination. The memo is what lets the anchor match the on-chain payment to the withdrawal request, so memo presence and format are validated before the payment is signed, never after.

Production anchor shortlist and fallback. The client is anchor-agnostic by construction: it discovers every anchor's configuration from that anchor's stellar.toml, so it is not coupled to any single anchor. The mainnet milestone requires at least one production SEP-24 anchor. We shortlist three candidates and pursue them in parallel: MyKobo (EURC, European corridor) as the primary, with Anclap (Latin American stablecoin corridors) and a regional stablecoin anchor such as Cowrie or ClickPesa (African corridors) as alternates. These are stablecoin SEP-24 anchors with low onboarding friction, unlike a commercial cash-payout network that requires a signed commercial agreement and is therefore not on the milestone critical path. The decisive fallback: because only one production anchor is required and the client works against any of them without code changes, a stall with one candidate does not threaten the milestone. The SDF reference anchor on testnet is always available as the baseline. MoneyGram Access, if pursued, is a post-grant parallel track via SDF partnerships, never a committed dependency.

4.2 Soroswap (DeFi aggregator)

Decision: implement a SoroswapProvider against the SwapProvider interface, add a STELLAR value to the swap engine's transaction-type enum, and a SOROSWAP entry to the provider-id enum. The adapter fetches quotes and unsigned swap XDR from the Soroswap Aggregator API, signs with the MPC signer, and submits through Soroban RPC. Soroswap is the most self-contained of the four blocks: no partnership gating on the critical path, and a direct fit for the existing swap-engine provider abstraction.

This mirrors how the swap engine already handles Solana. The default build implementation in SwapProvider applies EVM overrides to cached calldata, and non-EVM providers whose quote endpoint defers transaction construction (for example the Jupiter Solana adapter) override build to issue a second call that returns a chain-native unsigned transaction. SoroswapProvider follows that established pattern: supports("stellar", "stellar") is true, quote fetches the route, and build returns the unsigned Soroban XDR.

sequenceDiagram
    participant U as End user / agent
    participant WS as WalletSuite swap engine
    participant SS as Soroswap Aggregator API
    participant MPC as MPC signer (ed25519)
    participant SR as Soroban RPC
    participant SN as Stellar network

    U->>WS: quote(sell asset, buy asset, amount, slippage)
    WS->>SS: GET /quote (route across Soroban AMMs + classic DEX)
    SS-->>WS: best route + expected out + price impact
    WS-->>U: SwapQuote (SOROSWAP provider)
    U->>WS: execute(quote)
    WS->>SS: build unsigned swap XDR (InvokeHostFunction)
    SS-->>WS: unsigned transaction XDR
    WS->>SR: simulateTransaction(XDR)
    SR-->>WS: footprint, resource fees, auth entries
    alt auth entry uses address credentials
        WS->>MPC: sign SOROBAN_AUTHORIZATION preimage digest (Section 3.1)
        MPC-->>WS: auth entry signature
    end
    WS->>WS: assemble tx with source-account or signed auth entries + resources + sequence
    WS->>WS: build signature digest (Section 3.1)
    WS->>MPC: sign(share, Stellar, digest)
    MPC-->>WS: signature -> DecoratedSignature
    WS->>SR: sendTransaction(signed XDR)
    SR-->>WS: pending hash
    loop poll
        WS->>SR: getTransaction(hash)
        SR-->>WS: SUCCESS with result
    end
    WS-->>U: swap settled on mainnet
Loading

Soroban submission detail. A Soroban contract call is not a one-shot submit. The adapter first calls simulateTransaction to obtain the footprint (the ledger entries the call reads and writes), the resource fees, and any Soroban authorization entries. Each authorization entry is resolved per Section 3.1: source-account-credentialed entries are covered by the envelope signature, and address-credential entries get their own threshold-ed25519 signature over the ENVELOPE_TYPE_SOROBAN_AUTHORIZATION preimage before submission. It then rebuilds the transaction with those resource parameters and the sequence number from the allocator, signs it, calls sendTransaction, and polls getTransaction to a terminal result. Two Soroban-specific conditions are handled explicitly. If simulation reports that a contract instance or storage entry has expired past its TTL (an archived ledger entry), the adapter submits a RestoreFootprint operation to bring those entries back before the call executes. And because resource fees can shift between simulate and submit, the adapter treats the simulated fee as a bid with headroom and re-simulates and rebuilds on a resource-fee rejection rather than failing the swap. This simulate-then-submit lifecycle is Soroban-specific and is handled inside the Soroswap adapter and the CCTP orchestrator, both of which use the shared SorobanRpcClient.

Slippage and expiry. The quote carries a minimum-out derived from the caller's slippage tolerance, and the built transaction encodes that minimum plus a ledger-close deadline. A swap that would execute worse than the tolerance, or after the deadline, fails on chain rather than filling at a bad price. This is enforced in the Soroswap contract-call parameters, not only in the client, so it holds even if submission is delayed.

Fee model. Our integrator fee is injected through the swap engine's existing fee mechanism, so WalletSuite never custodies the user's funds during a swap. The user's MPC-held account is the source and the destination.

4.3 Stellar Disbursement Platform (SDP)

Decision: make WalletSuite wallets SDP-ready, and run a self-hosted SDP instance to demonstrate end-to-end disbursements into WalletSuite MPC wallets. Pursue the SDF wallet-registry listing in parallel. The registry listing is explicitly not milestone-gated, because it depends on an SDF-run approval step outside our control.

Wallet-readiness has three parts, and each reuses substrate already built for the other integrations:

  1. Deep-link handling. The SDP sends a recipient a registration deep link. The wallet handles it, extracts the disbursement context, and starts the SEP-24-style registration flow. This reuses the SEP-24 client from Section 4.1.
  2. On-demand account and trustline creation. A recipient who has never used Stellar has no account and no trustline for the disbursed asset. The wallet creates both on demand using sponsored reserves (Section 3.3), so the recipient needs zero XLM to receive funds.
  3. SEP-24 registration. The wallet completes the registration handshake so the SDP can match the recipient to the disbursement and release the payment.
sequenceDiagram
    participant ORG as Disbursing org
    participant SDP as SDP instance (self-hosted)
    participant WS as WalletSuite wallet backend
    participant MPC as MPC signer (ed25519)
    participant SN as Stellar network

    ORG->>SDP: upload disbursement (recipients, asset, amounts)
    SDP-->>WS: registration deep link to recipient
    WS->>WS: parse deep link, resolve recipient MPC wallet
    alt account or trustline missing
        WS->>WS: build sponsored CreateAccount + ChangeTrust
        WS->>MPC: sign sponsorship + user ops
        MPC-->>WS: signatures
        WS->>SN: submit sponsored creation (fee-bumped)
        SN-->>WS: account funded, trustline established
    end
    WS->>SDP: SEP-24 registration (SEP-10 auth then interactive)
    SDP->>SN: send disbursement payment to recipient G...
    WS->>SN: watch for incoming payment
    SN-->>WS: payment received
    WS-->>ORG: disbursement delivered (verifiable on ledger)
Loading

Running our own SDP instance removes the external gate from the demonstrable deliverable. We control both ends: the SDP sends, the WalletSuite wallets receive, and the whole flow is provable on-ledger on testnet at Tranche 2 and in a mainnet pilot at Tranche 3. The wallet-registry listing, which lets third-party SDP operators disburse to WalletSuite wallets, is pursued from day one but never blocks a milestone.

Controlling both ends is what makes this a legitimate technical proof rather than a shortcut. It removes the external SDF-approval gate while still exercising the full protocol surface: disbursement upload, deep-link registration, SEP-10 auth, SEP-24 interactive registration, on-ledger payment, and reconciliation. Nothing about the protocol is stubbed. Recipient registration for a programmatic MPC wallet is handled the same way it would be for a human-held wallet: because we operate both the SEP-24 registration flow and the receiver wallets, the OTP or verification step is completed by our test harness. A real disbursing org would verify real recipients through the identical flow; only the party completing the verification changes.

Reconciliation. Each delivered disbursement is matched to its on-ledger payment by transaction hash and memo, so the demonstrable outcome is not just a status flag but a ledger record the disbursing org can independently verify. This reuses the same payment-watching path the SEP-24 deposit flow uses.

4.4 CCTP (cross-chain USDC)

Decision: productize native USDC transfer between our existing EVM chains and Stellar as a treasury-flow API. Burn on the EVM source with our existing secp256k1 signer, fetch the attestation from Circle's public API, and mint on Stellar through the CCTP Soroban contracts. This reuses our production EVM signing path unchanged; the new engineering is the Stellar mint leg, its Soroban authorization handling, and the trustline lifecycle around it.

sequenceDiagram
    participant U as Treasury client / agent
    participant WS as WalletSuite backend
    participant MPCe as MPC signer (secp256k1)
    participant EVM as EVM source chain
    participant IRIS as Circle attestation API
    participant MPCs as MPC signer (ed25519)
    participant SR as Soroban RPC
    participant XLM as Stellar network

    U->>WS: transfer USDC (EVM -> Stellar, amount, recipient G...)
    WS->>WS: build depositForBurn tx (existing EVM builder)
    WS->>MPCe: sign_evm_1559(share, tx)
    MPCe-->>WS: signed EVM tx
    WS->>EVM: broadcast burn
    EVM-->>WS: burn tx mined, message + nonce emitted
    loop poll
        WS->>IRIS: GET attestation(message hash)
        IRIS-->>WS: attestation (once finalized)
    end
    alt recipient lacks USDC trustline
        WS->>XLM: sponsored ChangeTrust for USDC (Section 3.3)
    end
    WS->>WS: build receiveMessage InvokeHostFunction (message + attestation)
    WS->>SR: simulateTransaction -> footprint, fees, auth entries
    alt auth entry uses address credentials
        WS->>MPCs: sign SOROBAN_AUTHORIZATION preimage digest (Section 3.1)
        MPCs-->>WS: auth entry signature
    end
    WS->>WS: assemble (source-account or signed auth entries) + digest (Section 3.1)
    WS->>MPCs: sign(share, Stellar, digest)
    MPCs-->>WS: DecoratedSignature
    WS->>SR: sendTransaction (mint on Stellar)
    SR-->>WS: SUCCESS
    WS-->>U: native USDC delivered to recipient on Stellar
Loading

The burn leg is the EVM path we already run in production: build the depositForBurn call to the CCTP contract, sign with sign_evm_1559, and broadcast. The attestation leg is a poll against Circle's public attestation service, with no signing. The mint leg is a Soroban receiveMessage call carrying the message and attestation, submitted through the simulate-then-submit lifecycle from Section 4.2. We build the mint transaction with the recipient's MPC account as the source, so its authorization entries use source-account credentials where the contract permits and are otherwise signed through the ENVELOPE_TYPE_SOROBAN_AUTHORIZATION auth-entry path in Section 3.1 before submission. The recipient needs a USDC trustline, which we create with sponsored reserves if it is missing, so the whole flow is one API call from the client's perspective. Reverse direction (Stellar to EVM) is the mirror image: burn on Stellar via Soroban, attest, mint on EVM. The exact CCTP contract addresses on Stellar are pinned from Circle's published deployment at implementation time, the same discipline as the java-stellar-sdk artifact check.

Finality and latency. Circle issues the attestation only after the burn reaches the source chain's required finality, so a cross-chain USDC transfer is inherently asynchronous. The treasury-flow API models it as a tracked operation with observable states (burn submitted, burn finalized, attestation ready, mint submitted, mint confirmed) rather than a synchronous call. Clients poll the operation or receive a webhook on the terminal state. This matches how the platform already models long-running cross-chain work and sets correct latency expectations for a treasury integration.


5. Security model

The Stellar work introduces no new cryptography and no new custody model. It inherits the guarantees the platform already enforces.

  • 2-of-2 threshold custody. Every Stellar signature requires both the client share (party 0) and the WalletSuite cosigner (party 1). The full private key is never assembled at any point in the ceremony, on either side. Without the client share, funds cannot move, and neither party alone can sign. There is no recovery in the current design; an optional recovery add-on is planned as a later product option.
  • What the cosigner can and cannot do. The cosigner can participate in a signing ceremony only when the client presents its share and initiates the ceremony. It cannot sign on its own, cannot reconstruct the key, and cannot move funds without the client. It sees the 32-byte digest it is asked to co-sign, not a decrypted private key.
  • No on-chain multisig, so no on-chain attack surface added. Because the MPC-aggregate ed25519 key is the account master key, Stellar sees a single ordinary key. There is no SetOptions signer list to misconfigure, no threshold weights to get wrong, and no additional signer whose compromise could authorize a transaction.
  • Policy gating. Agent access runs through the MCP server's capability bands. A wallet operated by an AI agent is constrained to the tools its band allows, and Stellar wallets inherit that gating with no new mechanism.
  • Tamper-evident audit log. Every sensitive MCP tool call is appended to a tamper-evident, append-only audit log. Stellar signing and broadcasting are recorded like every other chain.
  • Key-share storage. The SDK never persists shares. The caller owns share storage and passes a share per operation. This is unchanged for Stellar: a Stellar wallet is one ed25519 share blob under a key id, stored exactly as a Solana share is today.
  • Input validation at the boundary. StrKey addresses are validated (version byte, base32, CRC16) before any transaction is built. Amounts are handled in stroops as integers. Trustline presence is checked before a non-XLM transfer is prepared.
  • Why no new cryptography. Stellar reuses the ed25519 curve and the threshold-EdDSA ceremony already in production for Solana. The only additions are non-cryptographic: StrKey encoding, XDR payload construction, and SHA-256 hashing, all of which are standard and verifiable against java-stellar-sdk. The RFC 8032 confirmation task (Section 3.1) proves this on day one.

5.1 Compromise scenarios

The 2-of-2 threshold holds custody in every single-party compromise:

  • Client share compromised, cosigner intact. An attacker with only the client share still cannot sign, because signing requires the cosigner to join the ceremony, and the cosigner enforces authentication and policy before it participates. Custody holds.
  • Cosigner compromised, client share intact. An attacker who reaches the cosigner still cannot sign without the client share and cannot reconstruct the key from party 1 material alone. Custody holds.
  • Backend or MCP compromised. An attacker who reaches the backend or MCP server can request signatures, but every request still passes through policy bands, is written to the tamper-evident audit log, and still requires both MPC parties to complete the ceremony. Any such activity is tamper-evident after the fact.
  • The deliberate tradeoff. Neither party can move funds alone, which is the point. The cost is the no-recovery property: loss of the client share is unrecoverable in the current release, which is why the SDK requires the caller to persist shares durably. An optional recovery add-on is planned as a later product option. This is identical to the model already running for the four production families and is not changed by Stellar.

6. Testing and rollout

Testing follows the platform's existing model: unit tests for utilities and services, integration tests against real infrastructure, and recorded end-to-end demos per deliverable. Controllers, DTOs, and RPC clients sit in the coverage-excluded set, while service and utility logic is measured against the enforced minimum.

One scope note: WalletSuite's production service exposes mainnet only, on every chain family it supports. Stellar's public testnet appears in milestones 1 and 2 as the SCF-standard validation environment, run in our staging setup. It is not a new product surface, and nothing testnet-facing ships to the production API.

6.1 Per-milestone testnet plan

  • Tranche 1 (MVP, testnet). Fund test accounts through Friendbot (the testnet faucet that funds new accounts with test XLM). Prove the full chain path on testnet: 2-of-2 DKG to a G-address, XLM and trustline-asset balances, a fee quote, and a payment prepared, MPC-signed, broadcast, and confirmed. Run SEP-10 auth against the SDF reference anchor. Demonstrate the MCP tools (initial release) operating on Stellar testnet. The RFC 8032 validation task (Section 3.1) runs first and gates this milestone. Deliverable artifacts: a public demo video and a documentation page.
  • Tranche 2 (Testnet, all four integrations). SEP-24 deposit and withdraw against a testnet Anchor Platform anchor. A Soroswap quote and swap executed through Soroban RPC on testnet. Our self-hosted SDP disbursing to WalletSuite wallets on testnet, including sponsored account and trustline creation. A CCTP USDC transfer between an EVM testnet and Stellar testnet. Each integration ships with a recorded end-to-end demo.
  • Tranche 3 (Mainnet launch). All four integrations live on mainnet: at least one production anchor SEP-24 flow, a Soroswap mainnet swap, an SDP mainnet disbursement pilot, and a CCTP mainnet transfer. A round of professional user testing on mainnet. Public documentation, an updated OpenAPI specification and Postman collection, and SDK examples. Production monitoring and alerting. Availability to existing paying clients.

6.2 Demo artifacts per deliverable

Every deliverable is Clear, Measurable, Verifiable, and Outcome-based, matching the SCF deliverable bar. Examples of the exact phrasing we hold ourselves to: "end-to-end SEP-24 deposit recorded on testnet," "Soroswap swap flow demo recorded on testnet," "CCTP USDC transfer from EVM to Stellar recorded on mainnet." Each is a recorded flow plus the on-ledger transaction hashes that prove it.

6.3 Monitoring, alerting, and mainnet checklist

Stellar RPC endpoints (Horizon and Soroban RPC) join the existing multi-provider health checks with per-provider circuit breaking and failover. Alerting covers submission failures, tx_bad_seq reconciliation rates, sponsor and fee-account XLM balance floors, and anchor SEP endpoint availability.

Mainnet launch checklist:

  • RFC 8032 validation passed against three stock verifiers and a live testnet submission.
  • Sequence allocator soak-tested under concurrent load, including forced tx_bad_seq recovery.
  • Sponsor and fee accounts funded, with balance-floor alerts armed.
  • At least one production anchor SEP-24 flow completed end to end on mainnet.
  • Soroswap mainnet swap, SDP mainnet disbursement, and CCTP mainnet transfer each confirmed on-ledger.
  • Monitoring dashboards and alerts live for all Stellar endpoints and accounts.
  • Documentation, OpenAPI, Postman, and SDK examples published.

6.4 Submission error handling and idempotency

Stellar's exact-match sequencing makes submission error handling explicit rather than incidental. The design handles each failure mode deterministically:

  • tx_bad_seq. The sequence allocator resynchronizes from Horizon, reassigns, and the transaction is rebuilt and re-signed. Because the signing digest includes the sequence number, a reassigned sequence produces a new digest and a fresh signature. There is no way to accidentally reuse a signature across sequences.
  • Timeouts. Every transaction carries time bounds. A transaction not included before its upper time bound is safely dead and cannot be replayed later, so a timed-out submission is retried by building a new transaction, never by resubmitting the old envelope.
  • Duplicate protection. A transaction hash is deterministic in its contents, so an accidental resubmission of the identical signed envelope is idempotent at the network. The backend also tracks in-flight submissions per account to avoid issuing two transactions for one intent.
  • Soroban simulate failures. A simulateTransaction that reports an error (an exceeded resource budget, or a failing contract assertion) is surfaced before signing. The user never signs a call that would fail on chain. Only simulated-clean transactions are signed and sent.
  • Archived ledger entries and resource-fee drift. If simulation shows a contract instance or storage entry has expired past its TTL, a RestoreFootprint operation is submitted to bring those entries back before the call executes. Because Soroban resource fees can change between simulate and submit, a resource-fee rejection triggers a re-simulation and rebuild rather than a hard failure.
  • Fee spikes. Under surge pricing a submission can be rejected for an insufficient fee. The fee-bump wrapper raises the fee on a signed inner transaction without re-signing it, so a fee spike is handled by re-wrapping the envelope, not by re-collecting a user signature.

6.5 Professional user testing

The mainnet milestone includes a round of professional user testing, run against real anchors, real Soroswap liquidity, our SDP instance, and live CCTP transfers. Testers exercise the four integrations end to end from wallet creation through settlement, on mainnet, using the documentation and SDK examples shipped in the same milestone. Findings feed a fix pass before the milestone is claimed complete. This is funded development and QA work, not marketing or user acquisition, which are outside the grant budget.

6.6 Test coverage

Stellar code follows the platform's existing test model and coverage gate. Service and utility logic (StrKey encoding and validation, the fee model, the sequence allocator, the prepare-sign builder, and the SEP client state machine) is unit-tested and counts toward the enforced coverage minimum. Controllers, DTOs, and RPC clients sit in the coverage-excluded set, consistent with the rest of the codebase, and are exercised by integration tests against testnet and by the recorded end-to-end demos. The RFC 8032 validation harness (Section 3.1) is a permanent CI test, not a one-off, so any future change to the signing path is caught against stock verifiers before it can ship.


7. Risks and mitigations

Risk Owner Mitigation
RFC 8032 edge cases in threshold-EdDSA (non-canonical R or S) MPC engine lead Week-1 validation against libsodium, ed25519-dalek strict, and java-stellar-sdk plus a live testnet submission. The same ed25519 signer already verifies in production for Solana. The threshold-EdDSA implementation is vendored and owned, so canonical encoding is enforced in signature assembly if needed. Fallback: attach an independent standard ed25519 signer and disable the master key.
Sequence-number contention under concurrent signing Backend lead Per-account serialized SequenceAllocator with Horizon reconciliation on tx_bad_seq. Channel-accounts pattern for high-throughput sponsor and fee accounts to parallelize independent sequence streams. Same class of solution as the existing EVM nonce management.
Trustline UX (end users cannot receive non-XLM assets without a trustline) Backend and design Sponsored reserves create the trustline on demand, funded by WalletSuite, so end users need zero XLM. Prepare-sign checks trustline presence before a non-XLM transfer and creates it in the same sponsored bundle.
Anchor availability or onboarding delay Integrations lead Anchor-agnostic client driven by SEP-1 discovery. Shortlist of three production candidates (MyKobo, Anclap, a regional stablecoin anchor) pursued in parallel. Only one is required for mainnet. SDF reference anchor always available on testnet. No commercial cash-payout dependency on the critical path.
Soroban RPC provider choice and reliability DevOps Run against SDF public Soroban RPC plus at least one commercial provider, health-checked with failover through the existing multi-provider RPC layer.
XLM reserve and fee costs at scale Founders / treasury Sponsored reserves and fee-bump funded from a WalletSuite treasury account. Per-account reserve is small (roughly 1 to 1.5 XLM) and recoverable by merging accounts or removing trustlines. Balance-floor alerts prevent silent depletion.
SDP wallet-registry listing depends on SDF approval Integrations lead Not milestone-gated. A self-hosted SDP instance demonstrates the full flow end to end without the listing. The listing is pursued in parallel as upside.
CCTP contract availability and correctness on Stellar mainnet Backend lead We control and already run the EVM burn leg in production. Attestation is a public Circle API. The Stellar mint leg is a documented Soroban call whose contract addresses are pinned from Circle's published deployment at implementation time.
java-stellar-sdk canonical artifact ambiguity Backend lead Confirm which artifact Maven Central publishes (org-owned versus community continuation) before pinning a version, rather than assuming.
Soroswap depends on the in-progress swap-engine abstraction (unmerged feature branch) Backend lead The abstraction is our own code under active finalization, independent of the grant. The SoroswapProvider is built against the finalized SwapProvider interface, so the dependency is internal and controllable, not an external gate.
Backend Soroban RPC client is a shared prerequisite for both Soroban blocks (Soroswap and CCTP), both landing at Milestone 2 Backend lead The Soroban RPC client lands first; the Soroswap and CCTP integrations are sequenced to start after it. The founders carry that path if the engineer hire slips.
Stellar protocol upgrade during the build window changes Soroban interfaces or fees Backend lead SDF announces protocol upgrades well in advance and runs them on testnet first. We pin SDK and RPC versions, track the upgrade calendar, and revalidate the Soroban lifecycle on testnet after any upgrade before promoting to mainnet.

8. Milestone mapping

The three milestones follow the SCF structure: Payment #0 is 10% on acceptance, then Tranche 1 (MVP) is 20%, Tranche 2 (Testnet) is 30%, and Tranche 3 (Mainnet) is 40%. The final tranche is the mainnet launch. Assumed start is 2026-08-03, with target dates T1 around 2026-09-15, T2 around 2026-10-26, and T3 around 2026-11-30 (dates align to the SCF round calendar at acceptance).

Architecture component T1 (MVP, testnet) T2 (Testnet, integrations) T3 (Mainnet)
RFC 8032 validation task (Section 3.1) Complete, gates T1
MPC ChainKind::Stellar, StrKey derivation, XDR signing path Built Hardened Mainnet
Backend STELLAR family, Horizon + Soroban RPC clients Built Mainnet
Stellar fee model (base fee + surge, fee-stats) Built Mainnet
Sequence allocator (per-account, channel accounts) Built Soak-tested Mainnet
Prepare-sign builder (payment, path-payment, change-trust) Payment path Path-payment + change-trust Mainnet
StrKey address validation (G / M) Built Mainnet
Account lifecycle + sponsored reserves + fee-bump Basic account creation Sponsored account + trustline Mainnet
MCP Stellar exposure (14 tools, StrKey validation) MCP tools (initial release) operating on Stellar testnet Mainnet
Anchor Platform SEP-1/10/24 client SEP-10 vs SDF reference anchor SEP-24 deposit + withdraw vs testnet anchor >=1 production anchor SEP-24
Soroswap adapter (quote, XDR, MPC sign, Soroban submit) Quote + swap on testnet Mainnet swap
SDP wallet-readiness + self-hosted SDP instance Disbursement to WalletSuite wallets on testnet Mainnet disbursement pilot
CCTP orchestrator (burn EVM, attest, mint Stellar) USDC EVM to Stellar on testnet Mainnet transfer
Docs, OpenAPI, Postman, SDK examples Docs page Per-integration demos Full docs + OpenAPI + Postman + SDK
Monitoring, alerting, professional user testing Live monitoring + user testing round

Each row lands where the milestone contents in the SCF submission require it. Tranche 1 stands up Stellar as a chain family end to end on testnet plus SEP-10 auth. Tranche 2 delivers all four integrations end to end on testnet with recorded demos. Tranche 3 takes all four live on mainnet with production monitoring, documentation, professional user testing, and availability to existing paying clients.


Appendix A: Stellar primitive to component map

This map ties every Stellar-specific primitive to the component that owns it and the reuse it draws on, so the reviewer can see there are no orphaned pieces.

Stellar primitive Owning component Reuses / builds on
ed25519 threshold signing MPC engine, ChainKind::Stellar on the Ed25519 curve Same curve and ceremony as Solana (production)
StrKey G / M encoding MPC engine address module Raw ed25519 pubkey already produced for Solana
Transaction signing digest Backend Stellar prepare-sign builder Standard SHA-256 plus java-stellar-sdk XDR
Payment operation Prepare-sign builder Existing prepare-sign to sign to broadcast flow
PathPaymentStrictSend / Receive Prepare-sign builder, Soroswap adapter Swap-engine SwapProvider interface
ChangeTrust (trustlines) Prepare-sign builder, account lifecycle Sponsored-reserve bundle
CreateAccount Account lifecycle Sponsored-reserve bundle
Sponsored reserves Account lifecycle Fee-bump and treasury sponsor account
Fee-bump Account lifecycle Treasury fee account
Sequence numbers SequenceAllocator Same class as EVM nonce management
Fee model (base fee, surge) StellarFeeModel Existing fee API low / medium / high
SEP-1 discovery Anchor SEP client Horizon client
SEP-10 web auth Anchor SEP client The Section 3.1 signing path
SEP-24 interactive deposit / withdraw Anchor SEP client, webview host SEP-10 JWT, payment watcher
Soroswap quote and swap XDR Soroswap adapter Swap-engine SPI, Soroban RPC client
Soroban simulate then submit Soroban RPC client Soroswap adapter and CCTP orchestrator
SDP wallet-readiness SDP adapter SEP-24 client, sponsored reserves
CCTP burn on EVM CCTP orchestrator Existing sign_evm_1559 (production)
CCTP mint on Stellar CCTP orchestrator Soroban RPC client, sponsored USDC trustline
Stellar exposure to AI agents MCP server Existing 14 tools, policy bands, audit log

Every primitive above resolves to a component that either already exists in production or is a bounded addition on top of one. That is the core of the low-risk argument: Stellar is a new chain family assembled from parts we already run, not a new platform.


Document status

This document specifies a complete design. The four committed integrations (Anchor Platform wallet-side SEP surface, Soroswap, SDP, and CCTP) and the Stellar chain enablement they rest on are fully resolved. The signing path, address encoding, account lifecycle, fee model, sequencing, data surface, and per-integration flows are all decided; alternatives are evaluated and closed in Section 3.6, and risks are owned and mitigated in Section 7. No Stellar code exists in the WalletSuite repositories at the time of writing. Building exactly what is specified here is the purpose of the grant. The one item held open by design, RFC 8032 signature-format confirmation, is scheduled as the first task of Tranche 1 with a stated fallback, and the property it confirms is already exercised in production by our Solana signer.

About

WalletSuite for Stellar: technical architecture for the SCF Build Award (Integration Track)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors