Excalibur Soroban PvP Settlement
Technical Architecture
Excalibur is a competitive PvP gaming arena where two players stake USDC or EURC and fight in real-time matches. Every kill resolves into an atomic transfer from loser to winner. Outcomes are deterministic and skill-based. This document specifies how the existing Tezos implementation migrates to Stellar/Soroban, what gets built, what gets integrated, and how on-chain activity is made verifiable to reviewers.
1System overview
The architecture combines an offchain low-latency match server (preserves in-game UX) with an on-chain Soroban settlement layer (preserves trust and verifiability). Kill events are signed offchain by the game server during the match and batched to Soroban every 5 seconds.
2What we build vs what we integrate
Reviewers consistently ask for this split, so we make it explicit upfront. The core innovation is on the BUILD side. The INTEGRATE side extends the user-facing surface using existing Stellar building blocks.
| Layer | Component | Mode | Owner |
|---|---|---|---|
| Settlement | MatchEscrow.rs Soroban contract | BUILD (Rust, MIT) | Thomas + Briac |
| Settlement | Game server kill batching service | BUILD (Rust/Go) | Briac |
| Settlement | Unity Stellar SDK | BUILD (C#) | Sam + Thomas |
| Wallets | Stellar Wallets Kit (Freighter, Albedo, LOBSTR, xBull) | INTEGRATE | Sam |
| Wallets | Privy embedded wallets (email / Google) | INTEGRATE | Sam |
| Assets | USDC + EURC via Stellar Asset Contract | INTEGRATE | Thomas |
| Funding | Stellar Broker (multi-asset funding) | INTEGRATE | Thomas |
| Funding | Anchor Platform (fiat → USDC ramp) | INTEGRATE | Thomas + Titou |
3MatchEscrow.rs — Soroban contract
The core innovation. A single contract holds both players' stakes for the duration of a match, resolves kills as atomic transfers between participants, and finalizes the match with a 5% protocol fee. Open-source under MIT license; reusable by any other PvP gaming project on Stellar.
3.1 Contract storage
// MatchEscrow.rs — storage layout (simplified)
pub struct Match {
pub match_id: BytesN<32>,
pub player_a: Address,
pub player_b: Address,
pub asset: Address, // USDC SAC or EURC SAC
pub stake_per_player: i128, // e.g. 10_000_000 = 10 USDC (7 decimals)
pub server_pubkey: BytesN<32>,// Ed25519 pubkey of authorized game server
pub state: MatchState, // Pending | Active | Settling | Closed
pub balance_a: i128,
pub balance_b: i128,
pub created_at: u64,
pub last_activity: u64,
pub timeout_seconds: u64, // default 600s
}
pub enum MatchState {
Pending, // contract created, awaiting deposits
Active, // both players deposited, match in progress
Settling, // server submitted final state, dispute window open
Closed, // funds released to winner + protocol
}
3.2 Public API
impl MatchEscrow {
// Create a new match. Anyone can call; both players must subsequently deposit.
pub fn init(
env: Env,
match_id: BytesN<32>,
player_a: Address,
player_b: Address,
asset: Address,
stake_per_player: i128,
server_pubkey: BytesN<32>,
) -> Match;
// Player deposits their stake. Requires player.require_auth().
pub fn deposit(env: Env, match_id: BytesN<32>, player: Address);
// Server submits a batch of signed kill events.
// Each event: (loser, winner, amount, nonce, signature).
// Verified on-chain against server_pubkey (Ed25519).
pub fn submit_kill_batch(
env: Env,
match_id: BytesN<32>,
events: Vec<KillEvent>,
);
// Server signals match end. Opens a dispute window of N seconds.
pub fn finalize_match(
env: Env,
match_id: BytesN<32>,
final_state_signature: BytesN<64>,
);
// After dispute window: protocol fee (5%) sent to treasury,
// remaining balance to winner. Atomic Splits primitive used.
pub fn settle(env: Env, match_id: BytesN<32>);
// Either player can claim if server abandons match (after timeout).
// Refunds via Claimable Balance to avoid contract holding stale funds.
pub fn timeout_refund(env: Env, match_id: BytesN<32>);
// Player disputes a specific kill event during the dispute window.
// Requires proof of out-of-order nonce or invalid signature.
pub fn dispute(env: Env, match_id: BytesN<32>, evidence: Bytes);
}
3.3 Why Soroban primitives, specifically
Atomic Splits
Native multi-recipient atomic payout. One settle() call pushes funds to winner + protocol treasury in one tx, with one fee. No two-step pattern, no failure recovery code needed.
Claimable Balance
Used for timeout_refund(). If the server abandons a match, players can claim their stake back without the contract holding stale state. The Claimable Balance is the refund vehicle.
Stellar Asset Contract
USDC and EURC both expose a SAC interface on Stellar. MatchEscrow.rs treats them identically — same contract handles both assets via the asset address parameter.
require_auth() + Ed25519 verification
Player authorizations use require_auth() (native Soroban). Server signatures use BytesN<64> Ed25519 verified onchain via env.crypto().ed25519_verify().
4Kill batching: hybrid optimistic UI + on-chain settlement
The most-asked question: how can kills be onchain without breaking gameplay latency? Answer: kills are signed offchain by the game server in <5ms (Ed25519), shown to the player immediately (optimistic UI), then batched to Soroban every 5 seconds. From the player's perspective the kill is instant. From the chain's perspective the kill is final within 10 seconds.
4.1 Sequence — single kill
4.2 Why this preserves trust
- Server signatures are public: the server_pubkey is recorded in the contract at init(). Players verify offchain that the kills they see are the same kills submitted onchain.
- Dispute window: after finalize_match(), a configurable window (default 60s) lets either player submit evidence of fraudulent kills.
- Nonce monotonicity: each kill carries a strictly increasing nonce; the contract rejects out-of-order submissions, preventing kill reordering attacks.
- Multisig fallback: for high-stakes matches, server_pubkey can be a 2-of-3 multisig (game server + community verifier + Excalibur ops), preventing a compromised server from solo-signing fraudulent kills.
- No server custody: the server never holds funds. Funds are inside the Soroban contract. The server only signs state transitions.
5End-to-end match flow
Reviewers requested an explicit progression from prototype to live mainnet. Here it is.
6Wallet integration paths
Two paths cover the two distinct audiences: crypto-native players (already hold a Stellar wallet) and mainstream gamers (no crypto experience).
6.1 Crypto-native — Stellar Wallets Kit
// Unity-side, C# wrapper over @creit.tech/stellar-wallets-kit (Web)
// exposed to Unity via a WebView bridge.
var kit = new StellarWalletsKit({
network: WalletNetwork.PUBLIC,
selectedWalletId: XBULL_ID, // or Freighter, Albedo, LOBSTR
modules: allowAllModules()
});
await kit.openModal({
onWalletSelected: async (option) => {
await kit.setWallet(option.id);
var { address } = await kit.getAddress();
excaliburClient.setPlayerWallet(address);
}
});
6.2 Mainstream gamers — Privy embedded wallets
// Sign-in with email or Google → Privy provisions custodial Stellar wallet
// transparently. Player never sees a seed phrase.
import { PrivyProvider } from '@privy-io/react-auth';
<PrivyProvider
appId="excalibur"
config={{
embeddedWallets: { createOnLogin: 'users-without-wallets' },
supportedChains: [stellarMainnet],
loginMethods: ['email', 'google']
}}>
<ExcaliburApp />
</PrivyProvider>
For Privy users, the deposit() call still goes through the embedded wallet — the user authorizes the spend via their Privy session, and the SDK signs and submits the Soroban transaction.
7Funding flows
Three funding paths cover all entry points.
| Path | User profile | Stellar building block |
|---|---|---|
| Direct USDC deposit | Crypto-native with existing USDC on Stellar | Stellar Asset Contract (USDC SAC) |
| Multi-asset deposit | Crypto-native with XLM or other Stellar assets | Stellar Broker (converts on-the-fly to USDC/EURC; contract still receives the settlement asset) |
| Fiat to USDC | Mainstream gamer paying with card or SEPA | Anchor Platform (EUR/KRW → USDC); for EU users, EURC variant for MiCA compliance |
Critically, the MatchEscrow contract logic does not care which path funded the deposit. The settlement asset is always USDC or EURC. The funding layer is decoupled from the contract layer.
8Verifiability checklist for reviewers
Each tranche delivers a specific set of reviewer-verifiable artifacts. We list them here so reviewers know exactly what to look at when they evaluate each milestone.
| Tranche | Verifiable artifacts |
|---|---|
| MVP (T1) |
• Public GitHub repo (MIT license) • MatchEscrow.rs source + Rust unit test suite passing • Contract compiled and deployed to local Soroban testnet • Unity SDK published on GitHub • Server batching service code public |
| Testnet (T2) |
• Public testnet contract address • Explorer transaction links for: init, deposit, kill batches, finalize, settle • Video demo of full match flow on testnet • Stellar Wallets Kit integration screen recordings • Privy onboarding flow screen recording |
| Mainnet (T3) |
• Public mainnet contract address • Mainnet transaction links for USDC and EURC matches • Stellar Broker funding transaction links • Anchor Platform fiat ramp transaction links • Mainnet activity dashboard (live during KBW) • Post-launch technical report (volume, gas, latency) |
9Stack summary
| Layer | Tech |
|---|---|
| Smart contract | Rust + Soroban SDK |
| Game server | Rust (axum) or Go — Ed25519 signing, kill batching, dispute manager |
| Indexer | Soroban RPC + custom event consumer (PostgreSQL) |
| Unity client | Unity 2022 LTS + custom Stellar SDK (C#) + WebView bridge for SWK |
| Wallet (web) | Stellar Wallets Kit (Freighter, Albedo, LOBSTR, xBull) |
| Wallet (embedded) | Privy with Stellar support |
| Assets | USDC (Stellar Asset Contract), EURC (Stellar Asset Contract) |
| Funding | Stellar Broker (multi-asset) · Anchor Platform (fiat ramp) |
| Infra | Cloudflare for static, AWS for game server cluster, Stellar RPC (Validation Cloud or self-hosted) |
| Open-source license | MIT (contract + SDK + batching service) |
10Why this matters for Stellar
- First PvP gaming title where per-kill on-chain settlement is unit-positive. Validates Stellar's core technical claim (sub-cent fees + 5s finality) in a consumer category Stellar has not yet seeded.
- EURC at scale in a consumer product. MiCA-compliant euro stablecoin in a real, user-facing application; a showcase for European regulatory positioning.
- Reusable open-source primitive. MatchEscrow.rs ships MIT-licensed; any other PvP game on Stellar can fork it. Lowers the bar for new gaming projects on Soroban.
- Korean market entry. Excalibur's October 1, 2026 launch is positioned at Korea Blockchain Week with on-site Hacker House activation, providing a high-visibility Korean market entry point for Stellar.
- Bridges Web2 gamers to Stellar. Privy + Anchor funnel demonstrates email-only onboarding to a live Soroban product — a template Stellar can point other consumer apps toward.