Documentation

    Documentation

    Documentation

    Ring Platform Logo

    Завантаження документації...

    Підготовка контенту платформи Ring

    Ring Platform Logo

    Завантаження документації...

    Підготовка контенту платформи Ring

    Ring Platform Logo

    Завантаження документації...

    Підготовка контенту платформи Ring

    1. /
    2. /Wallet & Credit System

    Updated Jul 7, 20269 min listen

    1. /
    2. /Wallet & Credit System

    Updated Jul 7, 20269 min listen

    1. /
    2. /Wallet & Credit System

    Updated Jul 7, 20269 min listen

    Concepts, value, and typical clone scenarios — less code.

    Welcome to Ring
    Quick Reference
    Getting Started
    Prerequisites
    Installation
    First Success Validation
    Next Steps
    Architecture
    Data Model
    Real Time
    Discovery Mutation Sync
    Security
    Backend Services
    k8s-postgres-fcm Mode
    Firebase Integration
    Features
    Authentication
    Email AI-CRM
    Entities
    Opportunities
    Notifications
    Push Notifications with FCM (Ring-Powered)
    Tunnel Protocol
    Wallet & Credit System
    Multi-Vendor Store
    Referral Codes (Refcodes)
    Affiliate & Referral Enablement
    Payments Overview
    PaymentConductor
    SubscriptionConductor
    VideoConductor
    News Module - Digital Newspaper Experience
    Member Blogs
    Public Profile Pages
    Username Reservation System
    Scientific Editor
    Locale System
    Security & Compliance
    NFT Marketplace
    Token Staking System
    Performance Optimization Patterns
    Mobile Experience
    Wallet
    Wallet Security Tips
    API
    Authentication
    Email AI-CRM API
    Entities
    Opportunities
    Messaging API
    Notifications API
    Wallet API
    Store API
    Admin API
    Customization
    Quick Start — Your First Ring Clone
    Customization Guide
    Token Economics Setup
    Payment Gateway Integration
    Reference Ring deployments
    Branding
    Themes
    Web3
    Token launch jurisdictions
    Deployment
    Self-hosted deployment
    Vercel
    Docker
    Monitoring & Analytics
    Performance Optimization
    Backup & Recovery
    Development
    Local Setup
    Code Structure
    Documentation components
    Community tooling
    Ring MCP Server
    Generative Images (ImageConductor)
    Autonomous Newsroom (Grok)
    OSS vs enterprise
    Whitelabel Navigation
    Best Practices
    Workflow
    Code Style
    Performance
    Testing
    Deployment
    Debugging
    Contributing
    MCP
    ring-image-create
    ring-video-create
    Examples
    Quick Start
    White Label
    Real World
    Integrations
    Ethereum wallets (Wagmi v3)

    Quick entry (CTOs · auditors · agents)

    Welcome — mission & audiences
    Quick Reference
    Getting started
    Architecture & Auth.js
    Backend modes & databases (DB_BACKEND_MODE)
    Self-hosted
    Ring MCP Tools
    Ring MCP Server
    Token economics
    Token launch jurisdictions
    Deploy (Docker · k8s)
    Security & compliance reads
    ringdom.org — LegioX homebase
    Source — MIT license (GitHub)

    Concepts, value, and typical clone scenarios — less code.

    Welcome to Ring
    Quick Reference
    Getting Started
    Prerequisites
    Installation
    First Success Validation
    Next Steps
    Architecture
    Data Model
    Real Time
    Discovery Mutation Sync
    Security
    Backend Services
    k8s-postgres-fcm Mode
    Firebase Integration
    Features
    Authentication
    Email AI-CRM
    Entities
    Opportunities
    Notifications
    Push Notifications with FCM (Ring-Powered)
    Tunnel Protocol
    Wallet & Credit System
    Multi-Vendor Store
    Referral Codes (Refcodes)
    Affiliate & Referral Enablement
    Payments Overview
    PaymentConductor
    SubscriptionConductor
    VideoConductor
    News Module - Digital Newspaper Experience
    Member Blogs
    Public Profile Pages
    Username Reservation System
    Scientific Editor
    Locale System
    Security & Compliance
    NFT Marketplace
    Token Staking System
    Performance Optimization Patterns
    Mobile Experience
    Wallet
    Wallet Security Tips
    API
    Authentication
    Email AI-CRM API
    Entities
    Opportunities
    Messaging API
    Notifications API
    Wallet API
    Store API
    Admin API
    Customization
    Quick Start — Your First Ring Clone
    Customization Guide
    Token Economics Setup
    Payment Gateway Integration
    Reference Ring deployments
    Branding
    Themes
    Web3
    Token launch jurisdictions
    Deployment
    Self-hosted deployment
    Vercel
    Docker
    Monitoring & Analytics
    Performance Optimization
    Backup & Recovery
    Development
    Local Setup
    Code Structure
    Documentation components
    Community tooling
    Ring MCP Server
    Generative Images (ImageConductor)
    Autonomous Newsroom (Grok)
    OSS vs enterprise
    Whitelabel Navigation
    Best Practices
    Workflow
    Code Style
    Performance
    Testing
    Deployment
    Debugging
    Contributing
    MCP
    ring-image-create
    ring-video-create
    Examples
    Quick Start
    White Label
    Real World
    Integrations
    Ethereum wallets (Wagmi v3)

    Quick entry (CTOs · auditors · agents)

    Welcome — mission & audiences
    Quick Reference
    Getting started
    Architecture & Auth.js
    Backend modes & databases (DB_BACKEND_MODE)
    Self-hosted
    Ring MCP Tools
    Ring MCP Server
    Token economics
    Token launch jurisdictions
    Deploy (Docker · k8s)
    Security & compliance reads
    ringdom.org — LegioX homebase
    Source — MIT license (GitHub)

    Concepts, value, and typical clone scenarios — less code.

    Welcome to Ring
    Quick Reference
    Getting Started
    Prerequisites
    Installation
    First Success Validation
    Next Steps
    Architecture
    Data Model
    Real Time
    Discovery Mutation Sync
    Security
    Backend Services
    k8s-postgres-fcm Mode
    Firebase Integration
    Features
    Authentication
    Email AI-CRM
    Entities
    Opportunities
    Notifications
    Push Notifications with FCM (Ring-Powered)
    Tunnel Protocol
    Wallet & Credit System
    Multi-Vendor Store
    Referral Codes (Refcodes)
    Affiliate & Referral Enablement
    Payments Overview
    PaymentConductor
    SubscriptionConductor
    VideoConductor
    News Module - Digital Newspaper Experience
    Member Blogs
    Public Profile Pages
    Username Reservation System
    Scientific Editor
    Locale System
    Security & Compliance
    NFT Marketplace
    Token Staking System
    Performance Optimization Patterns
    Mobile Experience
    Wallet
    Wallet Security Tips
    API
    Authentication
    Email AI-CRM API
    Entities
    Opportunities
    Messaging API
    Notifications API
    Wallet API
    Store API
    Admin API
    Customization
    Quick Start — Your First Ring Clone
    Customization Guide
    Token Economics Setup
    Payment Gateway Integration
    Reference Ring deployments
    Branding
    Themes
    Web3
    Token launch jurisdictions
    Deployment
    Self-hosted deployment
    Vercel
    Docker
    Monitoring & Analytics
    Performance Optimization
    Backup & Recovery
    Development
    Local Setup
    Code Structure
    Documentation components
    Community tooling
    Ring MCP Server
    Generative Images (ImageConductor)
    Autonomous Newsroom (Grok)
    OSS vs enterprise
    Whitelabel Navigation
    Best Practices
    Workflow
    Code Style
    Performance
    Testing
    Deployment
    Debugging
    Contributing
    MCP
    ring-image-create
    ring-video-create
    Examples
    Quick Start
    White Label
    Real World
    Integrations
    Ethereum wallets (Wagmi v3)

    Quick entry (CTOs · auditors · agents)

    Welcome — mission & audiences
    Quick Reference
    Getting started
    Architecture & Auth.js
    Backend modes & databases (DB_BACKEND_MODE)
    Self-hosted
    Ring MCP Tools
    Ring MCP Server
    Token economics
    Token launch jurisdictions
    Deploy (Docker · k8s)
    Security & compliance reads
    ringdom.org — LegioX homebase
    Source — MIT license (GitHub)
    Docs
    Features
    Docs
    Features
    Docs
    Features

    Wallet & Credit System

    Filter with Founder / Developer in the docs sidebar. This page reflects the verified v1.6.4 codebase: PIN-wrapped encryption, atomic single-write provisioning, wallet_access_tokens, and oracle/platform settings persisted via db() (no private pg.Pool).

    Ring Platform's wallet system provides two complementary layers:

    1. Custodial native-token wallets — auto-provisioned for signed-in members on all enabled chains (Solana first, EVM/Base enabled per clone config). Private keys encrypted with WALLET_ENCRYPTION_KEY using AES-256-GCM + scrypt. PIN-wrapped v2 encryption for withdrawal operations.
    2. In-app credit ledger — fiat USD credits stored in credit_balance field on user documents. Used for store purchases, subscriptions, and conversions to native tokens via the Coindesk widget.

    Browser routes: /wallet (dashboard), /wallet/topup, /wallet/send, /wallet/staking.

    What members experience

    Wallet dashboard — /wallet

    Balance hero showing credit balance + native-token balances per chain. Desk widget for converting credits ↔ native tokens.

    Top-up — /wallet/topup

    Add credits via native-token transfer or WayForPay card payment (processing backend TBD).

    Send tokens — /wallet/send

    Gasless transfers to contacts — treasury sponsors transaction fees. Pick recipient from contacts or enter address.

    Store & credits

    Credit spend at checkout — ledger debits before on-chain settlement in some flows.

    Core concepts for clone operators

    Credit balance is always fiat USD. Members earn or top up credits. These credits can be converted to your clone's native token through the Coindesk widget on /wallet. The conversion rate is set via the oracle (platform_settings.web3.oracle.ringPerUsd) and managed by superadmins.

    Native token transfers are gasless. The clone treasury sponsors Solana transaction fees so members never need SOL for gas. An optional transfer tax can be configured per clone for high-frequency send projects.

    Wallet provisioning is automatic. When a member signs in (Google, Apple, or credentials), the system provisions wallets for all enabled chains via a single atomic setUserWallets() write — no JSONB race, max one wallet per chain. No manual setup required.

    PIN-wrapped encryption (2026-07-03): Wallet private keys are encrypted with AES-256-GCM at rest. Members who set a 4-digit PIN get their keys wrapped with PIN-derived entropy — the PIN is verified server-side before any withdrawal operation, never sent to the client in plaintext. Keys stored before PIN setup remain in v1 format (env-only encryption) and are lazily re-encrypted on first PIN-based operation.

    Typical member flows

    • First login — wallet auto-provisioned; credit balance initialized at 0.
    • Check balance — wallet page shows credit balance + native-token balance per chain.
    • Send tokens to a contact — pick recipient from contacts, enter amount, confirm. Gas sponsored by treasury.
    • Top up credits — add credits via native-token transfer (enter tx hash for on-chain verification).
    • Set a wallet PIN — profile/security page; 4-digit PIN activates v2 PIN-wrapped encryption for all existing wallets.
    • Authorize a withdrawal — enter PIN; server validates against the stored encryption envelope; single-use access token issued.
    • Convert credits to tokens — use the Desk widget: choose buy/sell direction, get a quote, execute at oracle rate.
    • Pay in store — credit balance debited at checkout.

    Custodial model: Ring signs transfers server-side for auto-provisioned wallets. External wallets (MetaMask via RainbowKit) connect client-side — they do not use custodial transfer routes. Credits and native-token balances are separate concepts.

    Related documentation

    Wallet API reference

    Server actions and SSOT file listing.

    Token economics

    Oracle rate, transfer tax, first-settler discounts.

    Store feature

    Credit spend at checkout.

    Authentication

    Google/Apple/social sign-in triggers wallet provisioning.

    Architecture

    The wallet system uses server actions (app/_actions/wallet.ts — 23 actions) as the primary integration layer. These actions call SSOT service files in features/wallet/ and lib/wallet/.

    Server actions inventory

    #ActionLayerPurpose
    1ensureUserWalletsWalletProvisions wallets for all enabled chains (atomic single-write)
    2listUserWalletsWalletLists wallets with on-chain balances
    3getWalletBalanceWalletNative-token balance via viem/Solana RPC
    4getWalletActivityActivityUnified feed: credit + chain transactions
    5getCreditHistoryCredit

    Related documentation

    Wallet API reference

    Complete server action signatures and response types.

    Authentication

    Social sign-in triggers wallet provisioning.

    Store feature

    Credit spend at checkout.

    Token economics

    Oracle rate, transfer tax, first-settler discounts.

    Wallet & Credit System

    Filter with Founder / Developer in the docs sidebar. This page reflects the verified v1.6.4 codebase: PIN-wrapped encryption, atomic single-write provisioning, wallet_access_tokens, and oracle/platform settings persisted via db() (no private pg.Pool).

    Ring Platform's wallet system provides two complementary layers:

    1. Custodial native-token wallets — auto-provisioned for signed-in members on all enabled chains (Solana first, EVM/Base enabled per clone config). Private keys encrypted with WALLET_ENCRYPTION_KEY using AES-256-GCM + scrypt. PIN-wrapped v2 encryption for withdrawal operations.
    2. In-app credit ledger — fiat USD credits stored in credit_balance field on user documents. Used for store purchases, subscriptions, and conversions to native tokens via the Coindesk widget.

    Browser routes: /wallet (dashboard), /wallet/topup, /wallet/send, /wallet/staking.

    What members experience

    Wallet dashboard — /wallet

    Balance hero showing credit balance + native-token balances per chain. Desk widget for converting credits ↔ native tokens.

    Top-up — /wallet/topup

    Add credits via native-token transfer or WayForPay card payment (processing backend TBD).

    Send tokens — /wallet/send

    Gasless transfers to contacts — treasury sponsors transaction fees. Pick recipient from contacts or enter address.

    Store & credits

    Credit spend at checkout — ledger debits before on-chain settlement in some flows.

    Core concepts for clone operators

    Credit balance is always fiat USD. Members earn or top up credits. These credits can be converted to your clone's native token through the Coindesk widget on /wallet. The conversion rate is set via the oracle (platform_settings.web3.oracle.ringPerUsd) and managed by superadmins.

    Native token transfers are gasless. The clone treasury sponsors Solana transaction fees so members never need SOL for gas. An optional transfer tax can be configured per clone for high-frequency send projects.

    Wallet provisioning is automatic. When a member signs in (Google, Apple, or credentials), the system provisions wallets for all enabled chains via a single atomic setUserWallets() write — no JSONB race, max one wallet per chain. No manual setup required.

    PIN-wrapped encryption (2026-07-03): Wallet private keys are encrypted with AES-256-GCM at rest. Members who set a 4-digit PIN get their keys wrapped with PIN-derived entropy — the PIN is verified server-side before any withdrawal operation, never sent to the client in plaintext. Keys stored before PIN setup remain in v1 format (env-only encryption) and are lazily re-encrypted on first PIN-based operation.

    Typical member flows

    • First login — wallet auto-provisioned; credit balance initialized at 0.
    • Check balance — wallet page shows credit balance + native-token balance per chain.
    • Send tokens to a contact — pick recipient from contacts, enter amount, confirm. Gas sponsored by treasury.
    • Top up credits — add credits via native-token transfer (enter tx hash for on-chain verification).
    • Set a wallet PIN — profile/security page; 4-digit PIN activates v2 PIN-wrapped encryption for all existing wallets.
    • Authorize a withdrawal — enter PIN; server validates against the stored encryption envelope; single-use access token issued.
    • Convert credits to tokens — use the Desk widget: choose buy/sell direction, get a quote, execute at oracle rate.
    • Pay in store — credit balance debited at checkout.

    Custodial model: Ring signs transfers server-side for auto-provisioned wallets. External wallets (MetaMask via RainbowKit) connect client-side — they do not use custodial transfer routes. Credits and native-token balances are separate concepts.

    Related documentation

    Wallet API reference

    Server actions and SSOT file listing.

    Token economics

    Oracle rate, transfer tax, first-settler discounts.

    Store feature

    Credit spend at checkout.

    Authentication

    Google/Apple/social sign-in triggers wallet provisioning.

    Architecture

    The wallet system uses server actions (app/_actions/wallet.ts — 23 actions) as the primary integration layer. These actions call SSOT service files in features/wallet/ and lib/wallet/.

    Server actions inventory

    #ActionLayerPurpose
    1ensureUserWalletsWalletProvisions wallets for all enabled chains (atomic single-write)
    2listUserWalletsWalletLists wallets with on-chain balances
    3getWalletBalanceWalletNative-token balance via viem/Solana RPC
    4getWalletActivityActivityUnified feed: credit + chain transactions
    5getCreditHistoryCredit

    Related documentation

    Wallet API reference

    Complete server action signatures and response types.

    Authentication

    Social sign-in triggers wallet provisioning.

    Store feature

    Credit spend at checkout.

    Token economics

    Oracle rate, transfer tax, first-settler discounts.

    Wallet & Credit System

    Filter with Founder / Developer in the docs sidebar. This page reflects the verified v1.6.4 codebase: PIN-wrapped encryption, atomic single-write provisioning, wallet_access_tokens, and oracle/platform settings persisted via db() (no private pg.Pool).

    Ring Platform's wallet system provides two complementary layers:

    1. Custodial native-token wallets — auto-provisioned for signed-in members on all enabled chains (Solana first, EVM/Base enabled per clone config). Private keys encrypted with WALLET_ENCRYPTION_KEY using AES-256-GCM + scrypt. PIN-wrapped v2 encryption for withdrawal operations.
    2. In-app credit ledger — fiat USD credits stored in credit_balance field on user documents. Used for store purchases, subscriptions, and conversions to native tokens via the Coindesk widget.

    Browser routes: /wallet (dashboard), /wallet/topup, /wallet/send, /wallet/staking.

    What members experience

    Wallet dashboard — /wallet

    Balance hero showing credit balance + native-token balances per chain. Desk widget for converting credits ↔ native tokens.

    Top-up — /wallet/topup

    Add credits via native-token transfer or WayForPay card payment (processing backend TBD).

    Send tokens — /wallet/send

    Gasless transfers to contacts — treasury sponsors transaction fees. Pick recipient from contacts or enter address.

    Store & credits

    Credit spend at checkout — ledger debits before on-chain settlement in some flows.

    Core concepts for clone operators

    Credit balance is always fiat USD. Members earn or top up credits. These credits can be converted to your clone's native token through the Coindesk widget on /wallet. The conversion rate is set via the oracle (platform_settings.web3.oracle.ringPerUsd) and managed by superadmins.

    Native token transfers are gasless. The clone treasury sponsors Solana transaction fees so members never need SOL for gas. An optional transfer tax can be configured per clone for high-frequency send projects.

    Wallet provisioning is automatic. When a member signs in (Google, Apple, or credentials), the system provisions wallets for all enabled chains via a single atomic setUserWallets() write — no JSONB race, max one wallet per chain. No manual setup required.

    PIN-wrapped encryption (2026-07-03): Wallet private keys are encrypted with AES-256-GCM at rest. Members who set a 4-digit PIN get their keys wrapped with PIN-derived entropy — the PIN is verified server-side before any withdrawal operation, never sent to the client in plaintext. Keys stored before PIN setup remain in v1 format (env-only encryption) and are lazily re-encrypted on first PIN-based operation.

    Typical member flows

    • First login — wallet auto-provisioned; credit balance initialized at 0.
    • Check balance — wallet page shows credit balance + native-token balance per chain.
    • Send tokens to a contact — pick recipient from contacts, enter amount, confirm. Gas sponsored by treasury.
    • Top up credits — add credits via native-token transfer (enter tx hash for on-chain verification).
    • Set a wallet PIN — profile/security page; 4-digit PIN activates v2 PIN-wrapped encryption for all existing wallets.
    • Authorize a withdrawal — enter PIN; server validates against the stored encryption envelope; single-use access token issued.
    • Convert credits to tokens — use the Desk widget: choose buy/sell direction, get a quote, execute at oracle rate.
    • Pay in store — credit balance debited at checkout.

    Custodial model: Ring signs transfers server-side for auto-provisioned wallets. External wallets (MetaMask via RainbowKit) connect client-side — they do not use custodial transfer routes. Credits and native-token balances are separate concepts.

    Related documentation

    Wallet API reference

    Server actions and SSOT file listing.

    Token economics

    Oracle rate, transfer tax, first-settler discounts.

    Store feature

    Credit spend at checkout.

    Authentication

    Google/Apple/social sign-in triggers wallet provisioning.

    Architecture

    The wallet system uses server actions (app/_actions/wallet.ts — 23 actions) as the primary integration layer. These actions call SSOT service files in features/wallet/ and lib/wallet/.

    Server actions inventory

    #ActionLayerPurpose
    1ensureUserWalletsWalletProvisions wallets for all enabled chains (atomic single-write)
    2listUserWalletsWalletLists wallets with on-chain balances
    3getWalletBalanceWalletNative-token balance via viem/Solana RPC
    4getWalletActivityActivityUnified feed: credit + chain transactions
    5getCreditHistoryCredit

    Related documentation

    Wallet API reference

    Complete server action signatures and response types.

    Authentication

    Social sign-in triggers wallet provisioning.

    Store feature

    Credit spend at checkout.

    Token economics

    Oracle rate, transfer tax, first-settler discounts.

    Paginated credit transaction history
    6getCreditBalanceCreditCurrent credit balance (always fiat USD)
    7topUpCreditsCreditAdd credits after on-chain verification
    8spendCreditsCreditSpend credits for purchases/services
    9transferCreditsCreditAdmin credit adjustment
    10processMembershipFeeCreditMonthly membership fee from credits
    11transferNativeTokensTokenGasless native-token transfer (treasury-sponsored)
    12getRingPerUsdRateOracleCurrent oracle rate
    13setRingPerUsdRateOracleUpdate oracle rate (admin-only)
    14signDeskQuoteDeskSign quote for credit↔token conversion
    15executeDeskQuoteDeskExecute signed quote (buy/sell/refund)
    16verifyDeskQuoteDeskVerify signed quote token
    17verifyTopUpTransactionVerifyVerify on-chain ERC20/SPL transfer
    18listWalletTransactionsActivityRead wallet_transactions collection directly
    19setPrimaryWalletWalletSet default wallet address
    20getSpendSummaryCreditPeriod-based credit spending summary
    21getRewardCreditAddEventSummaryCreditCredit rewards summary for user
    22createPinAccessTokenActionSecurityIssue single-use PIN-gated access token (React 19 useActionState)
    23revokePinAccessTokensActionSecurityRevoke all active PIN access tokens for current user

    Integration pattern

    Server actions follow the SSOT pattern: dynamic import of service → auth check → operation → revalidatePath('/[locale]/wallet') for cache invalidation.

    1. 1

      Import and call a server action

      Server actions can be called directly from client components using React 19's useActionState hook.

    2. 2

      Or use with form progressive enhancement

    SSOT service files

    ConcernFileKey exports
    Credit CRUDfeatures/wallet/services/credit-balance-service.tsaddCredits, spendCredits, addFiatUsd, spendFiatUsd, getCreditHistory, hasSufficientBalance
    Gasless transfersfeatures/wallet/chains/native-token-transfer-service.tstransferNativeTokenForUser(userId, toAddress, amount) — treasury sponsors gas
    Treasury opsfeatures/wallet/chains/solana/treasury-transfer-service.tstransferNativeTokenFromTreasury, transferNativeTokenToTreasury, burnRingFromUser
    Desk tradesfeatures/wallet/chains/solana/desk-service.tsquoteDesk, executeDesk — credit↔token conversion
    Oracle ratefeatures/wallet/services/ring-token-oracle.tsgetRingPerUsdRate, setRingPerUsdRate — platform_settings doc web3 via db().readDoc / updateDoc
    Wallet DBlib/wallet/user-wallet-db.tsgetNativeWallet, getUserWallets, setUserWallets, migrateUserWalletsToPin
    Wallet provisioningfeatures/wallet/services/ensure-wallet.tsensureWallet, ensureWallets (multi-chain with encryption, atomic single-write)
    Balance fetchfeatures/wallet/services/list-wallets.tslistWallets — gets on-chain balances via viem/Solana RPC
    Activity feedfeatures/wallet/services/wallet-activity-feed.tsgetWalletActivityFeed — unified credit+chain feed with filters
    Top-up verifyfeatures/wallet/services/topup-verification.tsverifyTopUpTransaction, reserveTopUpTxHash
    Encryptionlib/wallet/encrypt-wallet-secret.tsencryptWalletSecret, decryptSecretWithPin, reencryptLegacyWallet (v1→v2 migration)
    Wallet decryptlib/wallet/decrypt-user-wallet.tsdecryptUserWalletPrivateKey, decryptSolanaWalletSecretKey (Uint8Array), decryptSolanaWalletSecretKeyWithPin
    PIN access tokenslib/wallet/pin-access-token-db.tsissueAccessToken, consumeAccessToken, revokeActiveTokens
    Credit schemaslib/zod/credit-schemas.tsCreditTopUpRequestSchema, CreditSpendRequestSchema, CreditTransactionType
    Desk schemaslib/zod/desk-schemas.tsDeskExecuteRequestSchema, DeskQuoteRequestSchema

    Wallet pages (verified routes)

    RouteComponentDescription
    /walletwallet-client.tsxBalance hero (credit + native-token), DeskWidget, transaction feed
    /wallet/topuptopup-client.tsxNative-token transfer + WayForPay (backend TBD)
    /wallet/sendsend-tokens.tsxGasless transfers to contacts

    PIN-gated access flow

    PIN access token flow

    Encryption envelope format (v2)

    Wallets created after the 2026-07-03 refactor use the versioned v2 format:

    • No PIN: v2:scryptSalt:iv:encrypted:authTag — key derived from WALLET_ENCRYPTION_KEY + random salt.
    • With PIN: same + :sha256(pin) appended — key derived from WALLET_ENCRYPTION_KEY + ":" + sha256(pin) + random salt.
    • Fast reject: stored pinHash enables constant-time PIN rejection before running expensive scrypt (DoS defense).
    • Migration: migrateUserWalletsToPin(userId, pin) re-encrypts all legacy v1 wallets for a user.

    Legacy v1 wallets (iv:encrypted:authTag) remain readable via decryptWalletSecretLegacy().

    Access token properties

    • Storage: wallet_access_tokens table (JSONB, sha256-hashed, never raw token persisted)
    • TTL: 15 minutes (configurable via WALLET_ACCESS_TOKEN_TTL_SECONDS env var)
    • Single-use: consumed atomically via usedAt timestamp after successful validation
    • Auto-revoke: issuing a new token for the same (userId, scope) revokes all prior active tokens — prevents replay
    • Audit: every issuance creates a wallet_transactions row with type pin_access_granted

    Desk trade flow (credit ↔ token conversion)

    Desk trade flow

    WalletChain — SSOT identity model

    The chain identity model was consolidated in the 2026-07-03 refactor:

    TypeDefinitionPurpose
    NativeChain(typeof ringConfig.chains.native)Single chain identity ('solana' on ring-platform.org)
    SupportedChains(typeof ringConfig.chains.supported)[number]All chains the clone supports ('solana' | 'evm' | 'base')
    EnabledChains(typeof ringConfig.chains.enabled)[number]Subset of supported chains actively in use
    WalletChainEnabledChainsChains a user can hold a wallet for (widened from NativeChain for story 5)
    DEFAULT_WALLET_CHAIN'evm'Fallback for legacy chainless wallet rows (pre-Solana EVM era)

    All three previously scattered hardcoded fallbacks ('solana' in utils.ts, 'evm' in user-wallet-db.ts, 'native' bug in ensure-wallet.ts) now point to DEFAULT_WALLET_CHAIN.

    Credit schema (Zod SSOT)

    All credit operations use lib/zod/credit-schemas.ts. Valid transaction types: payment, airdrop, reimbursement, purchase, membership_fee, top_up, bonus, penalty, desk_buy, desk_sell, desk_refund.

    Credit balance is always fiat USD on ring-platform.org — never native-token denomination:

    Conversion formula

    Coindesk widget converts at the oracle rate:

    Rate stored in platform_settings collection, document id web3, field path oracle.ringPerUsd. ring-token-oracle.ts uses db() from @/lib/database — same persistence layer as platform-settings-service.ts. When PLATFORM_SETTINGS_DISABLE_DB=true, reads use RING_ORACLE_DEFAULT_RATE (fallback 100) and writes throw. Superadmin updates via setRingPerUsdRate action or POST /api/admin/web3/settings.

    Environment variables

    VariableRequiredUsed by
    WALLET_ENCRYPTION_KEYYesencrypt-wallet-secret.ts — AES-256-GCM encryption of private keys
    SOLANA_RPC_URLYessolana-client.ts — on-chain Solana operations
    SOLANA_TREASURY_PRIVATE_KEYYestreasury-transfer-service.ts — sponsored gas transfers
    WALLET_ACCESS_TOKEN_TTL_SECONDSNo (default: 900)pin-access-token-db.ts — single-use token TTL
    POLYGON_RPC_URLFor EVM chainsget-wallet-balance.ts — EVM balance fallback path
    ORACLE_QUOTE_SECRETFor deskring-token-oracle.ts — desk quote HMAC signing (falls back to WALLET_ENCRYPTION_KEY)
    RING_ORACLE_DEFAULT_RATENo (default 100)Oracle read fallback when DB row missing or PLATFORM_SETTINGS_DISABLE_DB=true
    PLATFORM_SETTINGS_DISABLE_DBNoWhen true, oracle/settings reads use env defaults; writes blocked

    See lib/wallet/encrypt-wallet-secret.ts for the v2 envelope format cryptographic spec.

    Atomic provisioning (race fix)

    ensureWallets() provisions all missing chain wallets into an in-memory Map keyed by chain, then persists via one setUserWallets() write. This eliminates the JSONB read-modify-write race that previous parallel Promise.all(provisionChainWallet()) designs produced, and guarantees the "max one wallet per enabled chain" invariant (appendWalletIfMissing enforces per-chain idempotency).

    text
    
    v2:<scryptSaltHex>:<ivHex>:<encryptedHex>:<authTagHex>[:<pinHashHex>]
    text
    
    100 user-credit-points (USD) → X native tokens at Y USD per 1 native token
    Paginated credit transaction history
    6getCreditBalanceCreditCurrent credit balance (always fiat USD)
    7topUpCreditsCreditAdd credits after on-chain verification
    8spendCreditsCreditSpend credits for purchases/services
    9transferCreditsCreditAdmin credit adjustment
    10processMembershipFeeCreditMonthly membership fee from credits
    11transferNativeTokensTokenGasless native-token transfer (treasury-sponsored)
    12getRingPerUsdRateOracleCurrent oracle rate
    13setRingPerUsdRateOracleUpdate oracle rate (admin-only)
    14signDeskQuoteDeskSign quote for credit↔token conversion
    15executeDeskQuoteDeskExecute signed quote (buy/sell/refund)
    16verifyDeskQuoteDeskVerify signed quote token
    17verifyTopUpTransactionVerifyVerify on-chain ERC20/SPL transfer
    18listWalletTransactionsActivityRead wallet_transactions collection directly
    19setPrimaryWalletWalletSet default wallet address
    20getSpendSummaryCreditPeriod-based credit spending summary
    21getRewardCreditAddEventSummaryCreditCredit rewards summary for user
    22createPinAccessTokenActionSecurityIssue single-use PIN-gated access token (React 19 useActionState)
    23revokePinAccessTokensActionSecurityRevoke all active PIN access tokens for current user

    Integration pattern

    Server actions follow the SSOT pattern: dynamic import of service → auth check → operation → revalidatePath('/[locale]/wallet') for cache invalidation.

    1. 1

      Import and call a server action

      Server actions can be called directly from client components using React 19's useActionState hook.

    2. 2

      Or use with form progressive enhancement

    SSOT service files

    ConcernFileKey exports
    Credit CRUDfeatures/wallet/services/credit-balance-service.tsaddCredits, spendCredits, addFiatUsd, spendFiatUsd, getCreditHistory, hasSufficientBalance
    Gasless transfersfeatures/wallet/chains/native-token-transfer-service.tstransferNativeTokenForUser(userId, toAddress, amount) — treasury sponsors gas
    Treasury opsfeatures/wallet/chains/solana/treasury-transfer-service.tstransferNativeTokenFromTreasury, transferNativeTokenToTreasury, burnRingFromUser
    Desk tradesfeatures/wallet/chains/solana/desk-service.tsquoteDesk, executeDesk — credit↔token conversion
    Oracle ratefeatures/wallet/services/ring-token-oracle.tsgetRingPerUsdRate, setRingPerUsdRate — platform_settings doc web3 via db().readDoc / updateDoc
    Wallet DBlib/wallet/user-wallet-db.tsgetNativeWallet, getUserWallets, setUserWallets, migrateUserWalletsToPin
    Wallet provisioningfeatures/wallet/services/ensure-wallet.tsensureWallet, ensureWallets (multi-chain with encryption, atomic single-write)
    Balance fetchfeatures/wallet/services/list-wallets.tslistWallets — gets on-chain balances via viem/Solana RPC
    Activity feedfeatures/wallet/services/wallet-activity-feed.tsgetWalletActivityFeed — unified credit+chain feed with filters
    Top-up verifyfeatures/wallet/services/topup-verification.tsverifyTopUpTransaction, reserveTopUpTxHash
    Encryptionlib/wallet/encrypt-wallet-secret.tsencryptWalletSecret, decryptSecretWithPin, reencryptLegacyWallet (v1→v2 migration)
    Wallet decryptlib/wallet/decrypt-user-wallet.tsdecryptUserWalletPrivateKey, decryptSolanaWalletSecretKey (Uint8Array), decryptSolanaWalletSecretKeyWithPin
    PIN access tokenslib/wallet/pin-access-token-db.tsissueAccessToken, consumeAccessToken, revokeActiveTokens
    Credit schemaslib/zod/credit-schemas.tsCreditTopUpRequestSchema, CreditSpendRequestSchema, CreditTransactionType
    Desk schemaslib/zod/desk-schemas.tsDeskExecuteRequestSchema, DeskQuoteRequestSchema

    Wallet pages (verified routes)

    RouteComponentDescription
    /walletwallet-client.tsxBalance hero (credit + native-token), DeskWidget, transaction feed
    /wallet/topuptopup-client.tsxNative-token transfer + WayForPay (backend TBD)
    /wallet/sendsend-tokens.tsxGasless transfers to contacts

    PIN-gated access flow

    PIN access token flow

    Encryption envelope format (v2)

    Wallets created after the 2026-07-03 refactor use the versioned v2 format:

    • No PIN: v2:scryptSalt:iv:encrypted:authTag — key derived from WALLET_ENCRYPTION_KEY + random salt.
    • With PIN: same + :sha256(pin) appended — key derived from WALLET_ENCRYPTION_KEY + ":" + sha256(pin) + random salt.
    • Fast reject: stored pinHash enables constant-time PIN rejection before running expensive scrypt (DoS defense).
    • Migration: migrateUserWalletsToPin(userId, pin) re-encrypts all legacy v1 wallets for a user.

    Legacy v1 wallets (iv:encrypted:authTag) remain readable via decryptWalletSecretLegacy().

    Access token properties

    • Storage: wallet_access_tokens table (JSONB, sha256-hashed, never raw token persisted)
    • TTL: 15 minutes (configurable via WALLET_ACCESS_TOKEN_TTL_SECONDS env var)
    • Single-use: consumed atomically via usedAt timestamp after successful validation
    • Auto-revoke: issuing a new token for the same (userId, scope) revokes all prior active tokens — prevents replay
    • Audit: every issuance creates a wallet_transactions row with type pin_access_granted

    Desk trade flow (credit ↔ token conversion)

    Desk trade flow

    WalletChain — SSOT identity model

    The chain identity model was consolidated in the 2026-07-03 refactor:

    TypeDefinitionPurpose
    NativeChain(typeof ringConfig.chains.native)Single chain identity ('solana' on ring-platform.org)
    SupportedChains(typeof ringConfig.chains.supported)[number]All chains the clone supports ('solana' | 'evm' | 'base')
    EnabledChains(typeof ringConfig.chains.enabled)[number]Subset of supported chains actively in use
    WalletChainEnabledChainsChains a user can hold a wallet for (widened from NativeChain for story 5)
    DEFAULT_WALLET_CHAIN'evm'Fallback for legacy chainless wallet rows (pre-Solana EVM era)

    All three previously scattered hardcoded fallbacks ('solana' in utils.ts, 'evm' in user-wallet-db.ts, 'native' bug in ensure-wallet.ts) now point to DEFAULT_WALLET_CHAIN.

    Credit schema (Zod SSOT)

    All credit operations use lib/zod/credit-schemas.ts. Valid transaction types: payment, airdrop, reimbursement, purchase, membership_fee, top_up, bonus, penalty, desk_buy, desk_sell, desk_refund.

    Credit balance is always fiat USD on ring-platform.org — never native-token denomination:

    Conversion formula

    Coindesk widget converts at the oracle rate:

    Rate stored in platform_settings collection, document id web3, field path oracle.ringPerUsd. ring-token-oracle.ts uses db() from @/lib/database — same persistence layer as platform-settings-service.ts. When PLATFORM_SETTINGS_DISABLE_DB=true, reads use RING_ORACLE_DEFAULT_RATE (fallback 100) and writes throw. Superadmin updates via setRingPerUsdRate action or POST /api/admin/web3/settings.

    Environment variables

    VariableRequiredUsed by
    WALLET_ENCRYPTION_KEYYesencrypt-wallet-secret.ts — AES-256-GCM encryption of private keys
    SOLANA_RPC_URLYessolana-client.ts — on-chain Solana operations
    SOLANA_TREASURY_PRIVATE_KEYYestreasury-transfer-service.ts — sponsored gas transfers
    WALLET_ACCESS_TOKEN_TTL_SECONDSNo (default: 900)pin-access-token-db.ts — single-use token TTL
    POLYGON_RPC_URLFor EVM chainsget-wallet-balance.ts — EVM balance fallback path
    ORACLE_QUOTE_SECRETFor deskring-token-oracle.ts — desk quote HMAC signing (falls back to WALLET_ENCRYPTION_KEY)
    RING_ORACLE_DEFAULT_RATENo (default 100)Oracle read fallback when DB row missing or PLATFORM_SETTINGS_DISABLE_DB=true
    PLATFORM_SETTINGS_DISABLE_DBNoWhen true, oracle/settings reads use env defaults; writes blocked

    See lib/wallet/encrypt-wallet-secret.ts for the v2 envelope format cryptographic spec.

    Atomic provisioning (race fix)

    ensureWallets() provisions all missing chain wallets into an in-memory Map keyed by chain, then persists via one setUserWallets() write. This eliminates the JSONB read-modify-write race that previous parallel Promise.all(provisionChainWallet()) designs produced, and guarantees the "max one wallet per enabled chain" invariant (appendWalletIfMissing enforces per-chain idempotency).

    text
    
    v2:<scryptSaltHex>:<ivHex>:<encryptedHex>:<authTagHex>[:<pinHashHex>]
    text
    
    100 user-credit-points (USD) → X native tokens at Y USD per 1 native token
    Paginated credit transaction history
    6getCreditBalanceCreditCurrent credit balance (always fiat USD)
    7topUpCreditsCreditAdd credits after on-chain verification
    8spendCreditsCreditSpend credits for purchases/services
    9transferCreditsCreditAdmin credit adjustment
    10processMembershipFeeCreditMonthly membership fee from credits
    11transferNativeTokensTokenGasless native-token transfer (treasury-sponsored)
    12getRingPerUsdRateOracleCurrent oracle rate
    13setRingPerUsdRateOracleUpdate oracle rate (admin-only)
    14signDeskQuoteDeskSign quote for credit↔token conversion
    15executeDeskQuoteDeskExecute signed quote (buy/sell/refund)
    16verifyDeskQuoteDeskVerify signed quote token
    17verifyTopUpTransactionVerifyVerify on-chain ERC20/SPL transfer
    18listWalletTransactionsActivityRead wallet_transactions collection directly
    19setPrimaryWalletWalletSet default wallet address
    20getSpendSummaryCreditPeriod-based credit spending summary
    21getRewardCreditAddEventSummaryCreditCredit rewards summary for user
    22createPinAccessTokenActionSecurityIssue single-use PIN-gated access token (React 19 useActionState)
    23revokePinAccessTokensActionSecurityRevoke all active PIN access tokens for current user

    Integration pattern

    Server actions follow the SSOT pattern: dynamic import of service → auth check → operation → revalidatePath('/[locale]/wallet') for cache invalidation.

    1. 1

      Import and call a server action

      Server actions can be called directly from client components using React 19's useActionState hook.

    2. 2

      Or use with form progressive enhancement

    SSOT service files

    ConcernFileKey exports
    Credit CRUDfeatures/wallet/services/credit-balance-service.tsaddCredits, spendCredits, addFiatUsd, spendFiatUsd, getCreditHistory, hasSufficientBalance
    Gasless transfersfeatures/wallet/chains/native-token-transfer-service.tstransferNativeTokenForUser(userId, toAddress, amount) — treasury sponsors gas
    Treasury opsfeatures/wallet/chains/solana/treasury-transfer-service.tstransferNativeTokenFromTreasury, transferNativeTokenToTreasury, burnRingFromUser
    Desk tradesfeatures/wallet/chains/solana/desk-service.tsquoteDesk, executeDesk — credit↔token conversion
    Oracle ratefeatures/wallet/services/ring-token-oracle.tsgetRingPerUsdRate, setRingPerUsdRate — platform_settings doc web3 via db().readDoc / updateDoc
    Wallet DBlib/wallet/user-wallet-db.tsgetNativeWallet, getUserWallets, setUserWallets, migrateUserWalletsToPin
    Wallet provisioningfeatures/wallet/services/ensure-wallet.tsensureWallet, ensureWallets (multi-chain with encryption, atomic single-write)
    Balance fetchfeatures/wallet/services/list-wallets.tslistWallets — gets on-chain balances via viem/Solana RPC
    Activity feedfeatures/wallet/services/wallet-activity-feed.tsgetWalletActivityFeed — unified credit+chain feed with filters
    Top-up verifyfeatures/wallet/services/topup-verification.tsverifyTopUpTransaction, reserveTopUpTxHash
    Encryptionlib/wallet/encrypt-wallet-secret.tsencryptWalletSecret, decryptSecretWithPin, reencryptLegacyWallet (v1→v2 migration)
    Wallet decryptlib/wallet/decrypt-user-wallet.tsdecryptUserWalletPrivateKey, decryptSolanaWalletSecretKey (Uint8Array), decryptSolanaWalletSecretKeyWithPin
    PIN access tokenslib/wallet/pin-access-token-db.tsissueAccessToken, consumeAccessToken, revokeActiveTokens
    Credit schemaslib/zod/credit-schemas.tsCreditTopUpRequestSchema, CreditSpendRequestSchema, CreditTransactionType
    Desk schemaslib/zod/desk-schemas.tsDeskExecuteRequestSchema, DeskQuoteRequestSchema

    Wallet pages (verified routes)

    RouteComponentDescription
    /walletwallet-client.tsxBalance hero (credit + native-token), DeskWidget, transaction feed
    /wallet/topuptopup-client.tsxNative-token transfer + WayForPay (backend TBD)
    /wallet/sendsend-tokens.tsxGasless transfers to contacts

    PIN-gated access flow

    PIN access token flow

    Encryption envelope format (v2)

    Wallets created after the 2026-07-03 refactor use the versioned v2 format:

    • No PIN: v2:scryptSalt:iv:encrypted:authTag — key derived from WALLET_ENCRYPTION_KEY + random salt.
    • With PIN: same + :sha256(pin) appended — key derived from WALLET_ENCRYPTION_KEY + ":" + sha256(pin) + random salt.
    • Fast reject: stored pinHash enables constant-time PIN rejection before running expensive scrypt (DoS defense).
    • Migration: migrateUserWalletsToPin(userId, pin) re-encrypts all legacy v1 wallets for a user.

    Legacy v1 wallets (iv:encrypted:authTag) remain readable via decryptWalletSecretLegacy().

    Access token properties

    • Storage: wallet_access_tokens table (JSONB, sha256-hashed, never raw token persisted)
    • TTL: 15 minutes (configurable via WALLET_ACCESS_TOKEN_TTL_SECONDS env var)
    • Single-use: consumed atomically via usedAt timestamp after successful validation
    • Auto-revoke: issuing a new token for the same (userId, scope) revokes all prior active tokens — prevents replay
    • Audit: every issuance creates a wallet_transactions row with type pin_access_granted

    Desk trade flow (credit ↔ token conversion)

    Desk trade flow

    WalletChain — SSOT identity model

    The chain identity model was consolidated in the 2026-07-03 refactor:

    TypeDefinitionPurpose
    NativeChain(typeof ringConfig.chains.native)Single chain identity ('solana' on ring-platform.org)
    SupportedChains(typeof ringConfig.chains.supported)[number]All chains the clone supports ('solana' | 'evm' | 'base')
    EnabledChains(typeof ringConfig.chains.enabled)[number]Subset of supported chains actively in use
    WalletChainEnabledChainsChains a user can hold a wallet for (widened from NativeChain for story 5)
    DEFAULT_WALLET_CHAIN'evm'Fallback for legacy chainless wallet rows (pre-Solana EVM era)

    All three previously scattered hardcoded fallbacks ('solana' in utils.ts, 'evm' in user-wallet-db.ts, 'native' bug in ensure-wallet.ts) now point to DEFAULT_WALLET_CHAIN.

    Credit schema (Zod SSOT)

    All credit operations use lib/zod/credit-schemas.ts. Valid transaction types: payment, airdrop, reimbursement, purchase, membership_fee, top_up, bonus, penalty, desk_buy, desk_sell, desk_refund.

    Credit balance is always fiat USD on ring-platform.org — never native-token denomination:

    Conversion formula

    Coindesk widget converts at the oracle rate:

    Rate stored in platform_settings collection, document id web3, field path oracle.ringPerUsd. ring-token-oracle.ts uses db() from @/lib/database — same persistence layer as platform-settings-service.ts. When PLATFORM_SETTINGS_DISABLE_DB=true, reads use RING_ORACLE_DEFAULT_RATE (fallback 100) and writes throw. Superadmin updates via setRingPerUsdRate action or POST /api/admin/web3/settings.

    Environment variables

    VariableRequiredUsed by
    WALLET_ENCRYPTION_KEYYesencrypt-wallet-secret.ts — AES-256-GCM encryption of private keys
    SOLANA_RPC_URLYessolana-client.ts — on-chain Solana operations
    SOLANA_TREASURY_PRIVATE_KEYYestreasury-transfer-service.ts — sponsored gas transfers
    WALLET_ACCESS_TOKEN_TTL_SECONDSNo (default: 900)pin-access-token-db.ts — single-use token TTL
    POLYGON_RPC_URLFor EVM chainsget-wallet-balance.ts — EVM balance fallback path
    ORACLE_QUOTE_SECRETFor deskring-token-oracle.ts — desk quote HMAC signing (falls back to WALLET_ENCRYPTION_KEY)
    RING_ORACLE_DEFAULT_RATENo (default 100)Oracle read fallback when DB row missing or PLATFORM_SETTINGS_DISABLE_DB=true
    PLATFORM_SETTINGS_DISABLE_DBNoWhen true, oracle/settings reads use env defaults; writes blocked

    See lib/wallet/encrypt-wallet-secret.ts for the v2 envelope format cryptographic spec.

    Atomic provisioning (race fix)

    ensureWallets() provisions all missing chain wallets into an in-memory Map keyed by chain, then persists via one setUserWallets() write. This eliminates the JSONB read-modify-write race that previous parallel Promise.all(provisionChainWallet()) designs produced, and guarantees the "max one wallet per enabled chain" invariant (appendWalletIfMissing enforces per-chain idempotency).

    text
    
    v2:<scryptSaltHex>:<ivHex>:<encryptedHex>:<authTagHex>[:<pinHashHex>]
    text
    
    100 user-credit-points (USD) → X native tokens at Y USD per 1 native token