Skip to content

Repository files navigation

Stellar PocketPay — Savings Vault Contract

CHANGELOG

Project Status and Scope

This project is currently intended for development, learning, and Stellar testnet usage. It is not production-ready or mainnet-ready.

The savings vault now uses internal balance tracking and real token transfers: deposit transfers tokens from the user to the contract, withdraw transfers tokens from the contract to the user, and locking operations manage which tokens are available to withdraw.

See Known Limitations for other current constraints.

Security Considerations

This contract is for educational and testnet use. Review the following before any mainnet deployment.

See the Admin Role document for details on what the initialize(admin) value records, what the admin can and cannot do today, and future admin design considerations. See the Emergency Pause and Admin Misuse Threat Model for malicious or compromised admin scenarios, withdrawal impact, recovery assumptions, mitigations, and residual risks. See the Vault Fee Model document for clarification on fee assumptions, accounting implications, and user transparency requirements.

Features

Function Description
initialize(admin, token) One-time setup; records the admin and token addresses
deposit(user, amount) Add funds to a user's vault
withdraw(user, amount) Remove funds from a user's vault
withdraw_lock(user, lock_id) Withdraw a specific matured lock entry
get_balance(user) Query available (unlocked) balance
lock_funds(user, amount, unlock_time) Lock funds until a Unix timestamp
get_locked_balance(user) Query locked balance
get_lock(user, lock_id) Read one lock record by ID
list_locks(user, offset, limit) Page through a user's lock records
can_withdraw(user) Check if locked funds are withdrawable
pause(admin, duration_secs) Activate emergency pause (blocks deposits/locks; withdrawals remain open)
unpause(admin) Deactivate an active pause
is_paused() Check whether the contract is currently paused
get_version() Query the deployed contract version

Deposit and custody

Deposits now transfer real tokens into the contract. Calling deposit transfers the specified amount from the user to the contract, and calls withdraw transfer the specified amount from the contract to the user. The contract's internal balance tracking ensures withdrawals are limited to unlocked funds.

The contract uses a Stellar Asset Contract (SAC) to manage token transfers, which is specified during contract initialization via the token parameter.


Prerequisites

Install the following before you begin:

  1. Rust (latest stable)

    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
  2. Soroban CLI

    cargo install --locked soroban-cli
  3. WASM target

    rustup target add wasm32-unknown-unknown

Build

Compile the contract to a WASM binary:

# Debug build
cargo build --target wasm32-unknown-unknown

# Optimized release build (recommended for deployment)
cargo build --target wasm32-unknown-unknown --release

# Optimized release build with an immediate WASM size report
make build-release

The compiled .wasm file will be at:

target/wasm32-unknown-unknown/release/savings_vault.wasm

Contract size report

Soroban contract size affects upload and deployment costs and can reveal unexpected binary growth. Use the release wrapper above to build and print the artifact size in both human-readable units and exact bytes:

WASM artifact: target/wasm32-unknown-unknown/release/savings_vault.wasm
WASM size: 5.73 KiB (5871 bytes)

To report the size of an existing release artifact without rebuilding it, run:

make wasm-size

The reporting command exits with an error and identifies the expected path when the WASM file is missing. CI pipelines should run make build-release (or make wasm-size after their release build) so contract-size changes remain visible in build logs.


Test

Run the full unit test suite:

cargo test

All tests run natively (no WASM needed) using the Soroban SDK test utilities.


Task Runner

Common tasks are available via make:

make test        # Run all tests
make build-wasm  # Build the contract WASM in release mode
make clean       # Clean build artifacts

Deploy to Testnet

1. Configure the Stellar Testnet

soroban network add \
  --global testnet \
  --rpc-url https://soroban-testnet.stellar.org:443 \
  --network-passphrase "Test SDF Network ; September 2015"

2. Create & Fund an Identity

soroban keys generate --global deployer --network testnet
soroban keys address deployer

Fund the account at Stellar Friendbot.

  1. Deploy the Contract

Use the automated deployment script to build the release WASM and deploy it to the network. Pass your configured identity (e.g., deployer) as the first argument:

./scripts/deploy-testnet.sh deployer

The script will verify prerequisites, compile the contract, deploy it, and print your new Contract ID. Save the returned Contract ID — you'll need it to invoke functions.

See the deployment output example to learn what successful output looks like and which Contract ID value to copy.

4. Initialize the Contract

soroban contract invoke \
  --id YOUR_CONTRACT_ID \
  --source deployer \
  --network testnet \
  -- \
  initialize \
  --admin deployer

5. Invoke Functions

# Deposit 1000 units
soroban contract invoke \
  --id YOUR_CONTRACT_ID \
  --source deployer \
  --network testnet \
  -- \
  deposit \
  --user deployer \
  --amount 1000

# Check balance
soroban contract invoke \
  --id YOUR_CONTRACT_ID \
  --source deployer \
  --network testnet \
  -- \
  get_balance \
  --user deployer

Project Structure

stellar-pocketpay-contracts/
├── Cargo.toml                          # Workspace root
├── .gitignore
├── README.md
└── contracts/
    └── savings_vault/
        ├── Cargo.toml                  # Contract crate
        └── src/
            ├── lib.rs                  # Contract implementation
            └── test.rs                 # Unit tests
└── docs/
    ├── admin-role.md                   # Admin role documentation
    ├── api-reference.md                # Function naming conventions
    ├── architecture.md                 # Architecture overview
    ├── contract-id-handoff.md          # Contract ID handoff guide
    ├── deployment-environments.md      # Deployment environment config
    ├── error-codes.md                  # Error code reference
    ├── events.md                       # Event schema documentation
    ├── state-machine.md                # Vault state machine documentation
    ├── pause-design.md                 # Pause / emergency stop research
    ├── admin-pause-threat-model.md    # Emergency pause and admin misuse threat model
    ├── storage-migration.md            # Storage versioning and migration guide
    ├── storage-ttl.md                  # Storage TTL guide
    ├── testing.md                      # Test naming conventions
    ├── troubleshooting.md              # Troubleshooting guide
    └── upgrade-strategy.md             # Upgrade strategy research

Documentation

  • Audit Preparation Checklist — Checklist of documentation, tests, threat model, and deployment details required before any external security review or audit.
  • Emergency Pause and Admin Misuse Threat Model — Threat scenarios, withdrawal impact, recovery assumptions, mitigations, limitations, and residual risks for admin-controlled pause mechanisms.
  • Vault Fee Model — Clarification of no-fee assumptions, accounting implications, user transparency requirements, design rationale, and framework for potential future fee support.
  • Storage Audit — Comprehensive details on the contract's storage layout, keys, mutating functions, and security invariants.
  • Storage Migration Guide — Safe storage versioning and migration strategy for future contract upgrades.
  • Deployment Environments — Network configuration for local, testnet, and future mainnet, including RPC URLs, identities, environment variables, and deployment commands.
  • Contract Error Reference - Current savings vault failure conditions and guidance for SDK and mobile callers.
  • SDK Error Mapping Guide — Maps contract errors to SDK handling expectations with user-facing and developer-facing examples.
  • State Machine Documentation — Contract lifecycle, user account states, valid and invalid transitions, and error states.
  • Architecture Documentation – Overview of project structure, state management, storage, SDK integration, and future extension points.
  • API Reference: Function Naming Conventions — Naming convention for SavingsVault's public functions (commands, single-value queries, collection queries, capability and state queries), with the full function list and category for each.
  • SDK ↔ Contract Sequence Diagrams – Mermaid sequence diagrams for balance query, deposit, withdraw, and error paths across mobile, SDK, Soroban RPC, and the vault contract.
  • Event Schema Documentation – Overview of event names, topics, payload schemas, and JSON examples for vault actions.
  • Vault Contract ID Handoff - How to pass a deployed vault contract ID safely to SDK configuration and the mobile app.
  • Documentation Style Guide — Conventions for Testnet wording, avoiding production claims, placeholders, command formatting, and linking related docs.
  • Sample Vault Interaction Walkthrough — End-to-end deploy, deposit, lock, query, and withdraw example with expected state changes and current limitations.
  • CLI Smoke Test Guide — Quick post-deployment verification flow using the Soroban CLI to confirm every contract function responds correctly on testnet or a local sandbox.
  • Balance Reconciliation Design Note — How internal accounting should reconcile with real token balances once SAC integration is implemented, including failure modes and invariants tests must enforce.
  • Version Metadata — How the get_version read-only function works, how SDKs and deployment scripts should use it, and how to bump the version.
  • Lock Read Helpers — Response shapes and pagination for get_lock and list_locks.
  • Test Coverage Summary — Maps initialization, deposit, withdrawal, and locking behaviours to the tests that cover them, plus known test gaps.
  • Failure Mode Catalogue — Comprehensive list of all contract failure modes with expected behavior and test coverage.
  • Test Naming Conventions — Naming pattern for unit tests under contracts/savings_vault/src/test/, with good/bad examples and coverage guidance.

Security Considerations

This contract is for educational and testnet use. Review the following before any mainnet deployment.

Authorization

  • Every state-changing function calls require_auth() on the user's address.
  • Only the signing user can deposit, withdraw, or lock their own funds.

Input Validation

  • Zero and negative amounts are rejected for deposits, withdrawals, and locks.
  • Withdrawals exceeding the available balance are rejected.
  • Lock amounts exceeding the available balance are rejected.
  • Unlock times in the past are rejected.
  • Pause duration of zero is rejected.

Re-initialization Protection

  • initialize() can only be called once; subsequent calls panic.

Emergency Pause

  • The admin can activate a time-bounded pause via pause(admin, duration_secs).
  • During a pause, deposit and lock_funds are blocked; withdraw and withdraw_lock remain available.
  • The pause auto-expires after duration_secs seconds (auto-unpause).
  • Only the admin can pause or unpause (single admin key; multi-sig recommended for mainnet).

Storage Design

  • User balances are stored in persistent storage (survives ledger expiry longer).
  • Admin and initialization flags use instance storage (tied to contract lifetime).

Known Limitations

  • No admin recovery: There is no mechanism for the admin to recover or migrate funds.

  • No upgrade mechanism: The contract does not implement upgrade(). See docs/upgrade-strategy.md for research into possible upgrade paths.

  • No on-chain events: No events are emitted for state changes (deposit, withdraw, lock, unlock). See docs/events.md for planned event schemas.

  • No custom error enum: Contract uses panic strings instead of a structured error enum for off-chain callers.

  • No custom error enum: Contract uses panic strings instead of a structured error enum for off-chain callers.

  • No custom error enum: Contract uses panic strings instead of a structured error enum for off-chain callers.

  • No custom error enum: Contract uses panic strings instead of a structured error enum for off-chain callers.

  • No custom error enum: Contract uses panic strings instead of a structured error enum for off-chain callers.

  • No custom error enum: Contract uses panic strings instead of a structured error enum for off-chain callers.


Deployment Notes

  • Testnet RPC: https://soroban-testnet.stellar.org:443
  • Network passphrase: Test SDF Network ; September 2015
  • Friendbot (free testnet XLM): https://friendbot.stellar.org
  • Soroban Explorer: stellar.expert
  • Deployment help: see the troubleshooting guide for common Soroban CLI, Friendbot, WASM, network, and invocation issues. For a full breakdown of environment-specific configuration, see the deployment environments guide.
  • Always test thoroughly on testnet before considering mainnet deployment.
  • Monitor contract storage TTL and extend as needed using soroban contract extend. See the Storage TTL Guide for persistent vs. instance storage details and example commands.

Documentation

For a full list of CLI command examples and arguments for each contract method, check out the Contract Invocation Examples.

Contributing

Contributions are welcome! This project is intentionally beginner-friendly.

See CONTRIBUTING.md for the full guide, including:

  • How to format code (cargo fmt)
  • How to lint code (cargo clippy -- -D warnings)
  • How to run the test suite (cargo test)
  • PR checklist and commit message conventions

Quick start:

# Fork & clone, then verify everything is green before making changes
cargo fmt --check
cargo clippy --tests -- -D warnings
cargo test

License

This project is licensed under the MIT License.

About

No description, website, or topics provided.

Resources

Code of conduct

Contributing

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages