System Design

Data Format Schemas

Complete type definitions for all onchain data structures, messages, and events across both Stellar Soroban and Fairyring CosmWasm contracts.


Quick reference

ElGamal ciphertext64 bytes (2 × 32)

c1 and c2, each a Ristretto255 compressed point

ElGamal public key32 bytes

Compressed Ristretto255 point

tx_idu64

Monotonically increasing per account; replay guard

pending_credit_counteru64

Increments on each outgoing transfer

DenomCipherstring + 64 bytes

Token label + ElGamal ciphertext

Proof bundlevariable

ABI-packed equality + range + validity proofs

Cryptographic primitives

Shared between both contracts.

typescript
// ── Shared cryptographic primitives ────────────────────────────────

// 32-byte compressed Ristretto255 point representing the public key.
// Serialized as base64-encoded Binary in CosmWasm; Bytes in Soroban.
type ElGamalPublicKey = {
  key: Uint8Array; // 32 bytes
};

// Twisted ElGamal ciphertext: two 32-byte Ristretto255 curve points.
//   c1 = r · G          (ephemeral)
//   c2 = r · PK + m · G (encrypted value)
type ElGamalCiphertext = {
  c1: Uint8Array; // 32 bytes
  c2: Uint8Array; // 32 bytes
};

Fairyring: state storage types

Defined in state.rs and crypto.rs. Stored in CosmWasm cw-storage-plus Maps and Items.

typescript
// ── Fairyring CosmWasm storage types ───────────────────────────────

// A denomination-scoped encrypted balance slot.
// One entry per token in available_balance / pending_balance.
type DenomCipher = {
  denom: string;           // e.g. "USDC" or contract address string
  amount: ElGamalCiphertext;
};

// Full confidential account state (stored in ACCOUNTS map on Fairyring).
type ConfidentialAccount = {
  elgamal_pubkey: ElGamalPublicKey;

  // Spendable encrypted balances, one entry per denomination.
  available_balance: DenomCipher[];

  // Incoming transfers awaiting apply_pending, per denomination.
  pending_balance: DenomCipher[];

  // Incremented on each outgoing transfer; used as credit_id.
  pending_credit_counter: number; // u64

  // Monotonically increasing; replay protection.
  last_processed_id: number; // u64
};

// A pending cross-account credit record, stored by (sender, credit_id).
type PendingTransfer = {
  credit_id: number;          // u64 sender-scoped sequence number
  from: string;               // sender address
  to: string;                 // recipient address
  amount_cipher: ElGamalCiphertext; // encrypted under recipient's pubkey
  fee_cipher: ElGamalCiphertext | null;
  timestamp: number;          // Unix seconds at time of transfer
};

// Global contract configuration
type Config = {
  allowed_addresses: string[]; // BTreeSet<Addr> serialized
};

Fairyring: messages & queries

Defined in msg.rs. ExecuteMsg variants are submitted by FairyPort; QueryMsg variants are used by the SDK and relayer for state inspection.

typescript
// ── Fairyring ExecuteMsg variants ───────────────────────────────────

type InstantiateMsg = {
  allowed_addresses: string[];
};

// Proof bundles for transfer (sent by FairyPort after relaying from Stellar)
type TransferProofData = {
  equality_proof_data: Uint8Array;
  transfer_amount_ciphertext_validity_proof_data_with_ciphertext: Uint8Array;
  range_proof_data: Uint8Array;
};

type WithdrawProofData = {
  equality_proof: Uint8Array;
  range_proof: Uint8Array;
};

type ExecuteMsg =
  | {
      create_confidential_account: {
        owner: string;
        elgamal_pubkey: ElGamalPublicKey;
        tx_id: number;
      };
    }
  | {
      deposit: {
        owner: string;
        items: { denom: string; plain_amount: string }[]; // exactly one item
        tx_id: number;
        available_c1: Uint8Array; // current balance c1 (for idempotency)
        available_c2: Uint8Array; // current balance c2 (for idempotency)
      };
    }
  | {
      transfer_confidential: {
        sender: string;
        recipient: string;
        denom: string;
        transfer_proof_data: TransferProofData;
        tx_id: number;
        use_offchain_verify: boolean;
        // Required only when use_offchain_verify = true:
        sender_encrypted_amount?: ElGamalCiphertext;
        recipient_encrypted_amount?: ElGamalCiphertext;
        available_c1: Uint8Array;
        available_c2: Uint8Array;
      };
    }
  | { apply_pending: { owner: string; tx_id: number } }
  | {
      withdraw: {
        owner: string;
        denom: string;
        amount: string; // Uint128 as decimal string
        withdraw_proof_data: WithdrawProofData;
        tx_id: number;
        use_offchain_verify: boolean;
        available_c1: Uint8Array;
        available_c2: Uint8Array;
      };
    }
  | {
      update_allowed_addresses: {
        add: string[];
        remove: string[];
      };
    };

// ── Fairyring QueryMsg variants ──────────────────────────────────────

type QueryMsg =
  | { get_config: {} }
  | { get_account: { owner: string } }
  | { get_pending_transfer: { sender: string; credit_id: number } }
  | { list_pending_transfers: {} }
  | { list_accounts: {} };

// ── Fairyring custom query (chain ZKP module) ────────────────────────

type FairyQuery =
  | {
      verify_transfer_proofs: {
        equality_proof_data: Uint8Array;
        range_proof_data: Uint8Array;
        validity_proof_data: Uint8Array;
        sender_pubkey: Uint8Array;
        recipient_pubkey: Uint8Array;
        current_balance_commitment: Uint8Array;
        current_balance_handle: Uint8Array;
      };
    }
  | {
      verify_withdraw_proofs: {
        equality_proof_data: Uint8Array;
        range_proof_data: Uint8Array;
        user_pubkey: Uint8Array;
        ciphertext_commitment: Uint8Array;
        ciphertext_handle: Uint8Array;
        expected_nonce: number;
      };
    };

Soroban: storage types

Defined in types.rs. All types are annotated #[contracttype] for Soroban XDR serialization. Persistent storage is extended to configured TTL on each write.

typescript
// ── Stellar Soroban contract types (Rust contracttype) ──────────────

// Core account state stored per user address.
type AccountCore = {
  exists: boolean;
  finalized: boolean;          // true after create_account callback
  pending_action: boolean;     // true while any action is in flight
  tx_id: number;               // u64; monotonically increasing per account
  elgamal_pubkey: Uint8Array;  // 32 bytes; set after finalization
  pending_credit_counter: number; // u64; mirrors Fairyring counter
  minimum_pending_credit_counter: number; // u64; lower bound for apply
};

// Encrypted balance ciphertext (one per token per account)
type Ciphertext = {
  c1: Uint8Array;
  c2: Uint8Array;
};

// Pending state types (exactly one may exist per account at a time)

type PendingCreate = {
  exists: boolean;
  tx_id: number;
  elgamal_pubkey: Uint8Array;
};

type PendingDeposit = {
  exists: boolean;
  token: string;           // Stellar token contract address
  amount: bigint;          // i128; plaintext deposit amount
  tx_id: number;
  available_c1: Uint8Array; // snapshot of current ciphertext c1
  available_c2: Uint8Array; // snapshot of current ciphertext c2
};

type PendingTransfer = {
  exists: boolean;
  tx_id: number;
  recipient: string;
  token: string;
  fee_paid: bigint;         // i128
  use_offchain_verify: boolean;
  proof: Uint8Array;        // ABI-encoded ZK proof bundle
};

type PendingApply = {
  exists: boolean;
  tx_id: number;
};

type PendingWithdraw = {
  exists: boolean;
  tx_id: number;
  token: string;
  plain_amount: bigint;     // i128
  use_offchain_verify: boolean;
  proof: Uint8Array;
};

// ── Soroban storage key variants (DataKey enum) ──────────────────────

// Storage is partitioned by key type:
// Instance storage: Admin, Operators, FeeAccount, FeeAmount, FeeToken,
//                   Paused, InstanceBumpAmount, InstanceLifetimeThreshold,
//                   PersistentBumpAmount, PersistentLifetimeThreshold
//
// Persistent storage (keyed by address):
//   Account(owner)              → AccountCore
//   Available(owner, token)     → Ciphertext (available balance)
//   Pending(owner, token)       → Ciphertext (pending balance)
//   Locked(token)               → (token lock guard)
//   PendingDeposit(owner)       → PendingDeposit
//   PendingCreate(owner)        → PendingCreate
//   PendingTransfer(owner)      → PendingTransfer
//   PendingApply(owner)         → PendingApply
//   PendingWithdraw(owner)      → PendingWithdraw
//   PendingOwners               → Vec<Address> (list of pending accounts)
//   PendingKind(owner)          → PendingKind enum

Soroban: emitted events

Events emitted by the Soroban contract are the primary trigger for FairyPort. They follow Soroban's env.events().publish(topics, data) convention.

typescript
// ── Soroban events emitted by the contract ──────────────────────────
// All events follow (topic_tuple, data_tuple) structure.

// Emitted when a user requests account creation:
event CreateAccountRequested = {
  topics: ["CreateAccountRequested", owner: Address],
  data:   [tx_id: u64, elgamal_pubkey: Bytes],
};

// Emitted when a user requests deposit:
event DepositRequested = {
  topics: ["DepositRequested", owner: Address, token: Address],
  data:   [tx_id: u64, plain_amount: i128, avail_c1: Bytes, avail_c2: Bytes],
};

// Emitted when a user requests transfer:
event TransferRequested = {
  topics: ["TransferRequested", sender: Address, recipient: Address],
  data: [
    tx_id: u64,
    token: Address,
    proof: Bytes,             // ABI-encoded proof bundle
    use_offchain_verify: bool,
    sender_elgamal_pubkey: Bytes,
    recipient_elgamal_pubkey: Bytes,
    sender_avail_c1: Bytes,
    sender_avail_c2: Bytes,
  ],
};

// Emitted when a user requests apply_pending:
event ApplyPendingRequested = {
  topics: ["ApplyPendingRequested", owner: Address],
  data:   tx_id: u64,
};

// Emitted when a user requests withdraw:
event WithdrawRequested = {
  topics: ["WithdrawRequested", owner: Address, token: Address],
  data: [
    tx_id: u64,
    plain_amount: i128,
    proof: Bytes,
    use_offchain_verify: bool,
    elgamal_pubkey: Bytes,
    avail_c1: Bytes,
    avail_c2: Bytes,
  ],
};

Fairyring: wasm event attributes

Events emitted by the Fairyring CosmWasm contract are consumed by FairyPort to construct Stellar callback transactions. All numeric values are serialized as decimal strings; all binary fields are base64-encoded.

typescript
// ── Fairyring CosmWasm wasm events (response attributes) ────────────
// Each action emits a wasm event with these attributes.

// create_confidential_account response attributes:
{
  action: "create_confidential_account",
  owner: string,
  sender: string,       // relayer address
  tx_id: string,        // u64 as string
  elgamal_pubkey_b64: string, // base64
  tx_status: "ok" | "failed",
  error?: string,
}

// deposit response attributes (+ "deposit_item" sub-event):
{
  action: "deposit",
  owner: string,
  tx_id: string,
  tx_status: "ok" | "failed",
  // deposit_item sub-event:
  denom: string,
  pending_credit_counter: string,
  new_avail_c1: string,  // base64
  new_avail_c2: string,  // base64
}

// transfer_confidential response attributes:
{
  action: "transfer_confidential",
  from: string,
  to: string,
  denom: string,
  credit_id: string,
  sender_new_avail_c1: string,   // base64
  sender_new_avail_c2: string,   // base64
  recipient_new_pending_c1: string, // base64
  recipient_new_pending_c2: string, // base64
  sender_pending_credit_counter: string,
  recipient_pending_credit_counter: string,
  tx_id: string,
  tx_status: "ok" | "failed",
}

// apply_pending response attributes (+ "apply_pending_item" sub-event per denom):
{
  action: "apply_pending",
  owner: string,
  pending_credit_counter: string,
  tx_id: string,
  tx_status: "ok" | "failed",
  // apply_pending_item sub-event:
  denom: string,
  new_avail_c1: string, // base64
  new_avail_c2: string, // base64
}

// withdraw response attributes:
{
  action: "withdraw",
  owner: string,
  denom: string,
  amount: string,
  pending_credit_counter: string,
  new_avail_c1: string, // base64
  new_avail_c2: string, // base64
  tx_id: string,
  tx_status: "ok" | "failed",
}

Encoding notes

Field typeFairyring (CosmWasm)Stellar (Soroban)
Byte arrays (keys, proofs)cosmwasm_std::Binary (base64 JSON)soroban_sdk::Bytes (XDR)
Integers (amounts, counters)Uint128 / u64 (decimal string in JSON)i128 / u64 (XDR native)
Addressescosmwasm_std::Addr (bech32 string)soroban_sdk::Address (Stellar pubkey / contract)
Ciphertext in eventsbase64 attribute valuesBytes in event data tuple (XDR)
Proof bundle encodingBinary (bytemuck zero-copy struct layout)Bytes (ABI-packed layout for FFI verifier)