Skip to content

About

No description, website, or topics provided.

Resources

Code of conduct

Contributing

Security policy

Stars

8 stars

Watchers

0 watching

Forks

Latest commit

 

History

1,075 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

BettaPay Contracts

Soroban smart contracts for the BettaPay payment infrastructure on Stellar.

Payment reference read access

get_payment_reference and get_payments are intentionally public so off-chain settlement indexers and composing contracts can verify a payment without a merchant signature. A payment reference is a 32-byte value supplied by the merchant and must be generated with cryptographically secure randomness; it functions as a bearer capability for lookup. Indexers may consume payment_stored events for discovery and use these endpoints for targeted verification; raw Soroban state reads remain an equivalent alternative.

Structure

BettaPay-Contract/
├── Cargo.toml                  # Rust workspace root (both contracts)
├── settlement_contract/        # Merchant registration, fee splits, payment references
│   ├── Cargo.toml
│   └── src/lib.rs
├── governance_contract/        # Fee config, anchor registry, system params
│   ├── Cargo.toml
│   └── src/lib.rs
├── bettapay_common/            # Shared constants, types, error codes, and storage keys
├── docs/                       # Core protocol architecture and lifecycle specifications
│   ├── scheduled-operation-lifecycle.md # Timelocked queue, collision rules, and 9 operations
│   ├── recovery-lifecycle.md   # Emergency recovery flow, delays, and failure modes
│   ├── bootstrap-fallback.md   # 4-tier settlement rule fallback chain and emission rules
│   ├── storage-key-ownership.md # Storage key matrix, ownership, and TTL policies
│   ├── contract-limits.md      # Protocol constants and bounds reference
│   └── archive-and-tombstones.md # Merchant archiving and tombstone semantics
├── scripts/
│   ├── deploy_testnet.sh       # Build + deploy both contracts + init admin
│   ├── simulate.sh             # Simulate contract calls locally
│   ├── export_events.sh        # Dump contract events over a ledger range
│   ├── list_pending_ops.sh     # Show pending timelocked operations
│   ├── verify_wasm_hash.sh     # Compare deployed Wasm hash to local build
│   └── check_interface_parity.sh # Fail when the two contracts' interface versions drift
└── DEVELOPMENT.md              # Upgrade and storage-migration guide

Core Documentation

  • Scheduled Operation Lifecycle: State machine, allowed transitions, duplicate vs. genuine hash collision (codes 12 and 316), minimum 7-day timelock delay, emergency recovery veto, permissionless execution, and reference for all nine Operation variants.
  • Admin Recovery Lifecycle: End-to-end recovery sequence, actors, 7-day RECOVERY_DELAY_SECONDS, cancellation rules, failure modes (RecoveryNotPending 8, RecoveryDelayActive 9, RecoveryAlreadyPending 15), and scheduled operation veto semantics.
  • Bootstrap Fallback Semantics: The 4-tier resolution chain (merchant → default → governance → bootstrap), trigger conditions, field values (100 platform bps, 5 network bps, 0 delay, auto_settle false), and event emission rules.
  • Storage Key Ownership and TTL Policies: Authoritative mapping of all settlement, governance, and CommonDataKey keys to owning crates, storage kinds, and TTL bump policies.
  • Contract Limits: Numeric bounds, fee limits, batch limits, and their enforcement points.

Deployed Addresses (Testnet)

Contract Address
Settlement CBGBGKJSUY7XYB6HWW4CVAU6MW2KD25FSF45E5KCP53TKUK374MBZNFB
Governance CDPFWUTIXF5BC6BKNDLSQOZSDQCXAJNZFCZWHBE2RRHANRN25T3ILPZ7
Admin GCCHHKNI7GRA5QWC7RCTT3OHO7SKAUMKQA6IBWEQEO2SXI3GF376UHDD

Network: Test SDF Network ; September 2015

Toolchain Requirements

  • Rust (MSRV): 1.85.0. The minimum supported Rust version is the channel pinned in rust-toolchain.toml. CI builds and tests the workspace on it in a separate MSRV job; reproduce it locally with make msrv.
  • soroban-cli: 21.5.0. Pinned once as SOROBAN_VERSION in the Makefile. Install it with make install_soroban and check it with make check_soroban.

See CONTRIBUTING.md for details.

Quick Start

# Run all tests
cargo test

# Build WASM release binaries
cargo build --target wasm32-unknown-unknown --release

# Deploy to testnet (requires soroban CLI)
bash scripts/deploy_testnet.sh

CLI Usage Examples

All examples below assume you have the Soroban CLI installed and a funded testnet identity.

Build

# Build all contracts (release WASM)
cargo build --target wasm32-unknown-unknown --release

# Build a specific contract
cargo build --target wasm32-unknown-unknown --release -p settlement_contract
cargo build --target wasm32-unknown-unknown --release -p governance_contract

Test

# Run all tests
cargo test

# Run tests for a specific contract
cargo test -p settlement_contract
cargo test -p governance_contract

# Run a specific test by name
cargo test merchant_lifecycle_uses_canonical_topics -p settlement_contract

Deploy to Testnet

# One-command deployment (builds + deploys + initializes both contracts)
bash scripts/deploy_testnet.sh

# Or deploy step-by-step:
# 1. Generate and fund a key
soroban keys generate bettapay-admin --fund

# 2. Build and optimize WASM
make optimize

# 3. Deploy settlement contract
SETTLEMENT_ID=$(soroban contract deploy \
  --wasm target/optimized/settlement_contract_opt.wasm \
  --source-account bettapay-admin \
  --rpc-url https://soroban-testnet.stellar.org \
  --network-passphrase "Test SDF Network ; September 2015")

# 4. Deploy governance contract
GOVERNANCE_ID=$(soroban contract deploy \
  --wasm target/optimized/governance_contract_opt.wasm \
  --source-account bettapay-admin \
  --rpc-url https://soroban-testnet.stellar.org \
  --network-passphrase "Test SDF Network ; September 2015")

# 5. Initialize both contracts
ADMIN=$(soroban keys address bettapay-admin)
RECOVERY_ADDRESS="$ADMIN"

soroban contract invoke \
  --id "$SETTLEMENT_ID" \
  --source-account bettapay-admin \
  --rpc-url https://soroban-testnet.stellar.org \
  --network-passphrase "Test SDF Network ; September 2015" \
  -- \
  init --admins "[\"$ADMIN\"]" --threshold 1 --governance "$GOVERNANCE_ID" --recovery-address "$RECOVERY_ADDRESS"

soroban contract invoke \
  --id "$GOVERNANCE_ID" \
  --source-account bettapay-admin \
  --rpc-url https://soroban-testnet.stellar.org \
  --network-passphrase "Test SDF Network ; September 2015" \
  -- \
  init --admins "[\"$ADMIN\"]" --threshold 1 --recovery-address "$RECOVERY_ADDRESS"

Invoke Settlement Contract

# Set vars (adjust IDs as needed)
SETTLEMENT_ID=CBGBGKJSUY7XYB6HWW4CVAU6MW2KD25FSF45E5KCP53TKUK374MBZNFB
ADMIN=GCCHHKNI7GRA5QWC7RCTT3OHO7SKAUMKQA6IBWEQEO2SXI3GF376UHDD
MERCHANT=GXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
RPC=https://soroban-testnet.stellar.org
PASS="Test SDF Network ; September 2015"

# Check admin
soroban contract invoke --id "$SETTLEMENT_ID" --source-account bettapay-admin \
  --rpc-url "$RPC" --network-passphrase "$PASS" -- \
  get_admin

# Register a merchant
soroban contract invoke --id "$SETTLEMENT_ID" --source-account bettapay-admin \
  --rpc-url "$RPC" --network-passphrase "$PASS" -- \
  register_merchant --merchant "$MERCHANT"

# Check if merchant is registered
soroban contract invoke --id "$SETTLEMENT_ID" --source-account bettapay-admin \
  --rpc-url "$RPC" --network-passphrase "$PASS" -- \
  is_merchant_registered --merchant "$MERCHANT"

# Set a settlement rule (250 bps platform, 50 bps network)
soroban contract invoke --id "$SETTLEMENT_ID" --source-account bettapay-admin \
  --rpc-url "$RPC" --network-passphrase "$PASS" -- \
  set_settlement_rule \
  --merchant "$MERCHANT" \
  --rule '{"platform_fee_bps": 250, "network_fee_bps": 50, "settlement_delay_ledger": 0, "auto_settle": false}'

# Calculate fee split without storing
soroban contract invoke --id "$SETTLEMENT_ID" --source-account bettapay-admin \
  --rpc-url "$RPC" --network-passphrase "$PASS" -- \
  calculate_fee_split --merchant "$MERCHANT" --amount 10000

# Store a payment reference (32-byte hex hash)
soroban contract invoke --id "$SETTLEMENT_ID" --source-account "$MERCHANT" \
  --rpc-url "$RPC" --network-passphrase "$PASS" -- \
  store_payment_reference \
  --merchant "$MERCHANT" \
  --reference "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890" \
  --amount 10000

# Fetch a stored payment
soroban contract invoke --id "$SETTLEMENT_ID" --source-account bettapay-admin \
  --rpc-url "$RPC" --network-passphrase "$PASS" -- \
  get_payment_reference \
  --reference "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"

# Set a global default rule
soroban contract invoke --id "$SETTLEMENT_ID" --source-account bettapay-admin \
  --rpc-url "$RPC" --network-passphrase "$PASS" -- \
  set_default_rule \
  --new_rule '{"platform_fee_bps": 100, "network_fee_bps": 0, "settlement_delay_ledger": 0, "auto_settle": false}'

# Pause/unpause
soroban contract invoke --id "$SETTLEMENT_ID" --source-account bettapay-admin \
  --rpc-url "$RPC" --network-passphrase "$PASS" -- \
  pause

soroban contract invoke --id "$SETTLEMENT_ID" --source-account bettapay-admin \
  --rpc-url "$RPC" --network-passphrase "$PASS" -- \
  unpause

Invoke Governance Contract

GOVERNANCE_ID=CDPFWUTIXF5BC6BKNDLSQOZSDQCXAJNZFCZWHBE2RRHANRN25T3ILPZ7
ASSET=GXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
ANCHOR=GXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

# Get admin
soroban contract invoke --id "$GOVERNANCE_ID" --source-account bettapay-admin \
  --rpc-url "$RPC" --network-passphrase "$PASS" -- \
  get_admin

# Set fee config (platform 120 bps, network 35 bps)
soroban contract invoke --id "$GOVERNANCE_ID" --source-account bettapay-admin \
  --rpc-url "$RPC" --network-passphrase "$PASS" -- \
  set_fee_config \
  --caller "$ADMIN" \
  --config '{"platform_fee_bps": 120, "network_fee_bps": 35}'

# Read fee config
soroban contract invoke --id "$GOVERNANCE_ID" --source-account bettapay-admin \
  --rpc-url "$RPC" --network-passphrase "$PASS" -- \
  get_fee_config

# Update a system parameter
soroban contract invoke --id "$GOVERNANCE_ID" --source-account bettapay-admin \
  --rpc-url "$RPC" --network-passphrase "$PASS" -- \
  update_system_param --caller "$ADMIN" --key '"max_settle"' --value 1440

# Read a system parameter
soroban contract invoke --id "$GOVERNANCE_ID" --source-account bettapay-admin \
  --rpc-url "$RPC" --network-passphrase "$PASS" -- \
  get_system_param --key '"max_settle"'

# Register an anchor for an asset
soroban contract invoke --id "$GOVERNANCE_ID" --source-account bettapay-admin \
  --rpc-url "$RPC" --network-passphrase "$PASS" -- \
  upsert_anchor --caller "$ADMIN" --asset "$ASSET" --anchor "$ANCHOR"

# Read an anchor
soroban contract invoke --id "$GOVERNANCE_ID" --source-account bettapay-admin \
  --rpc-url "$RPC" --network-passphrase "$PASS" -- \
  get_anchor --asset "$ASSET"

# Remove an anchor
soroban contract invoke --id "$GOVERNANCE_ID" --source-account bettapay-admin \
  --rpc-url "$RPC" --network-passphrase "$PASS" -- \
  remove_anchor --caller "$ADMIN" --asset "$ASSET"

# Transfer admin to a new address
soroban contract invoke --id "$GOVERNANCE_ID" --source-account bettapay-admin \
  --rpc-url "$RPC" --network-passphrase "$PASS" -- \
  transfer_admin --caller "$ADMIN" --new_admin "GYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY"

Local Simulation and Testing

For local development and testing without deploying to testnet, use the simulate.sh script:

# Run complete local simulation (builds, deploys, and initializes both contracts)
bash scripts/simulate.sh

# This outputs contract IDs and source identity:
# Source identity: bettapay-sim
# Source address: GYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
# Settlement contract ID: CYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
# Governance contract ID: CYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY

After running simulate.sh, use the printed contract IDs for local testing:

# Example: Query settlement contract locally
SETTLEMENT_ID=CYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
GOVERNANCE_ID=CYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
SOURCE=bettapay-sim

# Get settlement contract admin
soroban contract invoke --id "$SETTLEMENT_ID" --source-account "$SOURCE" \
  --rpc-url https://soroban-testnet.stellar.org \
  --network-passphrase "Test SDF Network ; September 2015" -- \
  get_admin

# Register a test merchant
TEST_MERCHANT=GXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

soroban contract invoke --id "$SETTLEMENT_ID" --source-account "$SOURCE" \
  --rpc-url https://soroban-testnet.stellar.org \
  --network-passphrase "Test SDF Network ; September 2015" -- \
  register_merchant --merchant "$TEST_MERCHANT"

Environment Variable Setup

For repetitive testing, create a local .env file or set these variables:

# Testnet Configuration
export SOROBAN_RPC_URL="https://soroban-testnet.stellar.org"
export SOROBAN_NETWORK_PASSPHRASE="Test SDF Network ; September 2015"
export SOROBAN_ACCOUNT="bettapay-admin"

# Contract Addresses (update after deployment)
# After deployment, update the contract IDs in:
# - [BettaPay-Frontend](https://github.com/org/BettaPay-Frontend) `.env`
# - [BettaPay-Backend](https://github.com/org/BettaPay-Backend) `.env`
export SETTLEMENT_CONTRACT_ID="CBGBGKJSUY7XYB6HWW4CVAU6MW2KD25FSF45E5KCP53TKUK374MBZNFB"
export GOVERNANCE_CONTRACT_ID="CDPFWUTIXF5BC6BKNDLSQOZSDQCXAJNZFCZWHBE2RRHANRN25T3ILPZ7"
export ADMIN_ADDRESS="GCCHHKNI7GRA5QWC7RCTT3OHO7SKAUMKQA6IBWEQEO2SXI3GF376UHDD"

# Use variables in commands:
soroban contract invoke --id "$SETTLEMENT_CONTRACT_ID" --source-account "$SOROBAN_ACCOUNT" \
  --rpc-url "$SOROBAN_RPC_URL" --network-passphrase "$SOROBAN_NETWORK_PASSPHRASE" -- \
  get_admin

Common Tasks

Set up a new identity for testing

# Generate a new key
soroban keys generate test-merchant

# Fund it (Friendbot for testnet)
soroban keys fund test-merchant

# Get its address
MERCHANT_ADDR=$(soroban keys address test-merchant)
echo "Merchant address: $MERCHANT_ADDR"

Register and configure a merchant

SETTLEMENT_ID=CBGBGKJSUY7XYB6HWW4CVAU6MW2KD25FSF45E5KCP53TKUK374MBZNFB
ADMIN_ACCOUNT="bettapay-admin"
MERCHANT_ADDR=$(soroban keys address test-merchant)
RPC="https://soroban-testnet.stellar.org"
PASS="Test SDF Network ; September 2015"

# Register merchant
soroban contract invoke --id "$SETTLEMENT_ID" --source-account "$ADMIN_ACCOUNT" \
  --rpc-url "$RPC" --network-passphrase "$PASS" -- \
  register_merchant --merchant "$MERCHANT_ADDR"

# Set settlement rule (e.g., 250 bps platform fee, 50 bps network fee, immediate settlement)
soroban contract invoke --id "$SETTLEMENT_ID" --source-account "$ADMIN_ACCOUNT" \
  --rpc-url "$RPC" --network-passphrase "$PASS" -- \
  set_settlement_rule \
  --merchant "$MERCHANT_ADDR" \
  --rule '{"platform_fee_bps": 250, "network_fee_bps": 50, "settlement_delay_ledger": 0, "auto_settle": false}'

# Verify merchant is registered
soroban contract invoke --id "$SETTLEMENT_ID" --source-account "$ADMIN_ACCOUNT" \
  --rpc-url "$RPC" --network-passphrase "$PASS" -- \
  is_merchant_registered --merchant "$MERCHANT_ADDR"

Test settlement calculations

SETTLEMENT_ID=CBGBGKJSUY7XYB6HWW4CVAU6MW2KD25FSF45E5KCP53TKUK374MBZNFB
MERCHANT_ADDR=$(soroban keys address test-merchant)

# Calculate fees for a 10,000 stroops payment
soroban contract invoke --id "$SETTLEMENT_ID" --source-account bettapay-admin \
  --rpc-url "$RPC" --network-passphrase "$PASS" -- \
  calculate_fee_split --merchant "$MERCHANT_ADDR" --amount 10000

# Example output: {platform: 2500, network: 500, merchant: 7000}

Operator Scripts

Operator-facing helper scripts (full flags documented in each script's --help):

# Verify a deployed contract matches the local optimized Wasm artifact
bash scripts/verify_wasm_hash.sh --contract-id "$SETTLEMENT_ID" \
  --wasm target/optimized/settlement_contract_opt.wasm

# Export contract events over a ledger range to JSON or CSV
# (re-uses soroban_events.py helpers; --from-file replays a saved response)
bash scripts/export_events.sh --contract-id "$SETTLEMENT_ID" \
  --start-ledger 1000 --end-ledger 3000 --format csv --output events.csv

# List pending timelocked operations (from op_scheduled/op_executed/op_cancelled events)
bash scripts/list_pending_ops.sh --contract-id "$SETTLEMENT_ID" --format table

# Fail when the two contracts advertise different interface versions
make check_interface_parity

Event topic names are decoded against the constants in bettapay_common/src/events.rs so exporters and indexers cannot drift from the contracts.

Detailed API Specification

This section documents the public entry points, parameter types, return values, authorization rules, and error states for each contract. All financial amounts and fees (bps) are handled using integer types (i128 and u32 respectively) to maintain determinism on-chain.


Settlement Contract (settlement_contract)

Handles merchant onboarding, rules-based fee splits, and payment references anchor logging.

Custom Types & Structs

SettlementRule

Represents the fee distribution and delay configuration for a specific merchant or as a global default.

pub struct SettlementRule {
    pub platform_fee_bps: u32,         // Platform fee in basis points (1 bps = 0.01%)
    pub network_fee_bps: u32,          // Network fee in basis points
    pub settlement_delay_ledger: u32,  // Ledger count delay before funds settle
    pub auto_settle: bool,             // Automatic settlement toggle
}

JSON CLI representation:

{
  "platform_fee_bps": 250,
  "network_fee_bps": 50,
  "settlement_delay_ledger": 0,
  "auto_settle": false
}
FeeSplit

Calculated output for fee distributions.

pub struct FeeSplit {
    pub gross_amount: i128,          // Total paid amount
    pub platform_fee_amount: i128,   // Platform fee share (rounded up)
    pub network_fee_amount: i128,    // Network fee share (rounded up)
    pub merchant_amount: i128,       // Net merchant payout (remnants)
}
PaymentRecord

Stored record representation for logged payment references.

pub struct PaymentRecord {
    pub merchant: Address,
    pub amount: i128,
    pub platform_fee_amount: i128,
    pub network_fee_amount: i128,
    pub merchant_amount: i128,
    pub platform_fee_bps: u32,
    pub network_fee_bps: u32,
    pub ledger: u32,
    pub settlement_delay_ledger: u32,
    pub auto_settle: bool,
}

Settlement API Table

Function Inputs Output Auth / Guard Error Panics (SettlementError) Description
init admin: Address, governance: Address, recovery_address: Address () admin AlreadyInitialized, InvalidGovernance, InvalidRecoveryAddress Initializes the contract and stores the administrator, governance contract, and backup recovery address. Can only be called once.
get_admin None Address None NotInitialized Returns the current admin address.
get_governance None Address None NotInitialized Returns the configured governance contract address.
update_governance new_governance: Address () Stored Admin, not paused NotInitialized, Paused, InvalidGovernance Updates the governance contract address after validating get_fee_config.
initiate_recovery new_admin: Address () Recovery Address NotInitialized, InvalidAdmin, RecoveryAlreadyPending Starts delayed admin recovery. Vetoes all pending scheduled operations. See Recovery Lifecycle.
cancel_recovery signers: Vec<Address> () Initiator or Admin Multisig NotInitialized, Unauthorized, RecoveryNotPending Cancels a pending recovery during the 7-day delay. See Recovery Lifecycle.
execute_recovery None () None (Permissionless) RecoveryNotPending, RecoveryDelayActive Applies pending recovery after 7-day delay elapses. Consumes pending state atomically. See Recovery Lifecycle.
transfer_admin signers: Vec<Address>, new_admins: Vec<Address>, new_threshold: u32 () Stored Admin multisig NotInitialized, Unauthorized, InvalidAdmin, InvalidThreshold, SameAdmin Transfers administrative control and multisig threshold.
pause None () Stored Admin NotInitialized, Unauthorized Halts mutating operations (e.g. register merchant, store payment reference).
unpause None () Stored Admin NotInitialized, Unauthorized Resumes mutating contract operations.
is_paused None bool None None Returns whether the contract is currently paused.
schedule signers: Vec<Address>, operation: Operation, execute_in: u64 () Stored Admin multisig threshold Paused, Unauthorized, ExecutionNotReady, GovernanceCallFailed, OperationAlreadyScheduled, OperationHashCollision Schedules a timelocked administrative operation. Enforces minimum 7-day delay (MIN_TIMELOCK_DELAY_SECONDS). See Scheduled Operation Lifecycle.
execute executor: Address, operation: Operation () None (Permissionless) Paused, OperationNotScheduled, ExecutionNotReady, RecoveryDelayActive Permissionlessly executes a scheduled operation after timelock delay. Vetoed if recovery is pending (except CancelRecovery). See Scheduled Operation Lifecycle.
cancel signers: Vec<Address>, operation: Operation () Stored Admin multisig threshold Paused, Unauthorized, OperationNotScheduled Cancels a scheduled operation prior to execution. See Scheduled Operation Lifecycle.
register_merchant merchant: Address () Stored Admin Paused, InvalidAddress, MerchantExists Registers a new merchant. The merchant must not already exist and cannot be the zero address.
unregister_merchant merchant: Address () Stored Admin Paused, MerchantMissing Removes a merchant from the registry, deletes their specific rule, and orphans their payment records — post-unregister reads of those records fail with PaymentOrphaned.
set_settlement_rule merchant: Address, rule: SettlementRule () Stored Admin Paused, MerchantMissing, InvalidFeeBps, InvalidSettlementDelay Sets merchant-specific override fees and delay parameters. Sum of bps must be $\le 10,000$.
clear_settlement_rule merchant: Address () Stored Admin MerchantRuleNotSet Deletes a merchant-specific rule override, forcing a fallback to default → governance → bootstrap.
set_default_rule new_rule: SettlementRule () Stored Admin InvalidFeeBps, InvalidSettlementDelay Configures the global fallback rule applied when no merchant-specific rule exists.
get_default_rule None Option<SettlementRule> None None Fetches the global default settlement rule configuration, if any.
store_payment_reference merchant: Address, reference: BytesN<32>, amount: i128 FeeSplit merchant Paused, MerchantMissing, InvalidPaymentReference, InvalidAmount, DuplicatePaymentReference Records a payment hash on-chain and returns the fee split. Emits bootstrap_fallback when falling back to unconfigured bootstrap defaults. See Bootstrap Fallback Semantics.
is_merchant_registered merchant: Address bool None None Checks if a merchant is present in the registry.
get_settlement_rule merchant: Address Option<SettlementRule> None None Reads the merchant-specific settlement rule override.
calculate_fee_split merchant: Address, amount: i128 FeeSplit None MerchantMissing, InvalidAmount Performs a dry-run calculation of fees for the merchant without mutating storage. Read-only and never emits events. See Bootstrap Fallback Semantics.
get_payment_reference merchant: Address, reference: BytesN<32> Option<PaymentRecord> None None Returns the merchant's logged payment record for the reference. Refreshes the entry's TTL when queried.
get_payments merchant: Address, references: Vec<BytesN<32>> Vec<PaymentRecord> None None Batch retrieves the merchant's logged payment records. Missing references are ignored.
prune_payments merchant: Address, references: Vec<BytesN<32>> u32 Stored Admin BatchTooLarge Removes up to 100 of one merchant's payment records in one call. Missing or repeated references are skipped, so cleanup can be retried. Emits one payments_pruned event with the removed count.

Governance Contract (governance_contract)

Controls protocol-wide parameters, fee limits, upgraded code hashes, and trust anchors.

Custom Types & Structs

FeeConfig

Represents the default protocol fees.

pub struct FeeConfig {
    pub platform_fee_bps: u32, // Bounded by MIN_FEE_BPS (5) and MAX_FEE_BPS (5,000)
    pub network_fee_bps: u32,  // Combined sum must be <= 10,000 bps
}
AdminTransferred

Event payload structure for administrative transfer tracking.

pub struct AdminTransferred {
    pub old_admin: Address,
    pub new_admin: Address,
}

Governance API Table

Function Inputs Output Auth / Guard Error Panics (GovernanceError) Description
init admin: Address, recovery_address: Address () admin AlreadyInitialized, InvalidRecoveryAddress Initializes the governance contract and stores the administrator and backup recovery address.
is_initialized None bool None None Returns true if initialization has been completed.
get_admin None Address None NotInitialized Returns the current admin address.
get_recovery_address None Address None NotInitialized Returns the configured backup recovery address.
initiate_recovery new_admin: Address () Recovery Address NotInitialized, InvalidAdmin, RecoveryAlreadyPending Starts delayed admin recovery. See Recovery Lifecycle.
cancel_recovery None () Stored Admin NotInitialized, RecoveryNotPending Cancels a pending recovery during the 7-day delay. See Recovery Lifecycle.
execute_recovery None () None (Permissionless) RecoveryNotPending, RecoveryDelayActive Applies pending recovery after the 7-day delay. See Recovery Lifecycle.
upgrade caller: Address, new_wasm_hash: BytesN<32> () caller == admin Unauthorized Replaces the contract Wasm code with a new binary, leaving storage unchanged. Changing a stored type needs a migration — see DEVELOPMENT.md.
transfer_admin _caller: Address, new_admin: Address () Stored Admin InvalidAdmin Assigns the admin role to new_admin. Address cannot be zero or the current admin.
pause caller: Address () caller == admin Unauthorized Halts mutating governance config operations.
unpause caller: Address () caller == admin Unauthorized Resumes governance operations.
is_paused None bool None None Returns whether the governance modifications are halted.
update_system_param caller: Address, key: Symbol, value: i128 () caller == admin Unauthorized, InvalidParamValue Stores or overrides a numeric parameter value (must be $\ge 0$) under key.
get_system_param key: Symbol Option<i128> None None Fetches the system parameter value. Refreshes storage TTL on reads.
set_fee_config caller: Address, config: FeeConfig () caller == admin Unauthorized, InvalidFeeBps Updates the global default fee configuration. Limits: $5 \text{ bps} \le \text{bps} \le 5000 \text{ bps}$.
get_fee_config None Option<FeeConfig> None None Fetches the active system fee config.
upsert_anchor caller: Address, asset: Address, anchor: Address () caller == admin Unauthorized Registers or updates a trusted off-chain anchor address for a token asset.
remove_anchor caller: Address, asset: Address () caller == admin Unauthorized, AnchorMissing Removes the trusted anchor config for a token asset.
get_anchor asset: Address Option<Address> None None Reads the trusted anchor address associated with an asset.

Architecture Diagram

flowchart LR
    subgraph Clients
        Admin[Admin / Operators]
        Merchant[Merchant Services]
        Backend[Backend Services]
    end

    subgraph Contracts
        Governance[Governance Contract]
        Settlement[Settlement Contract]
    end

    Admin --> Governance
    Merchant --> Settlement
    Backend --> Governance
    Backend --> Settlement
    Governance -->|Fee config and anchors| Settlement
    Settlement -->|Payments and settlement events| Backend
Loading

This diagram highlights the main interaction pattern: the backend and operators call the contracts directly, while the settlement contract consumes governance configuration and emits settlement-related events back to the application layer.

Soroban SDK Version

soroban-sdk = "21.7.7"

Dependencies

No cross-contract calls. Both contracts are independently deployable and stateless across each other. The backend services call them via Stellar RPC.

Security

Found a security vulnerability? Please do not open a public GitHub issue. See SECURITY.md for the reporting process (GitHub Security Advisories preferred, email fallback), the vulnerability report template, the list of report owners, and our 90-day responsible disclosure window.

About

No description, website, or topics provided.

Resources

Code of conduct

Contributing

Security policy

Stars

8 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages