Deposits & Withdrawals
Every Oracle user funds their account the same way: send USDC to a static per-user Solana address, a listener auto-bridges it to Fogo, and your in-app balance is credited within 1–5 minutes.
If you're already on Fogo, send USDC directly to your proxy wallet ATA and skip the bridge.
Primary chain pair: Solana → Fogo via Wormhole.
Architecture
User (Solana) ──SPL transfer──▶ Deposit Address ──Wormhole──▶ Proxy Wallet (Fogo) ──▶ Engine balance
User (Solana non-USDC) ──▶ Jupiter swap ──▶ USDC ──▶ Wormhole ──▶ …
User (Fogo, native) ──SPL transfer──▶ Proxy Wallet ATA (Fogo) ──▶ Engine balance
Withdrawal: Vault ──▶ Proxy Wallet ──▶ Wormhole ──▶ User (Solana)
Vault ──▶ Proxy Wallet ──▶ Direct SPL ──▶ User (Fogo native)
- Deposit Address — operator-controlled Solana wallet, deterministic per user. The listener picks up incoming transfers and bridges them.
- Proxy Wallet — Fogo PDA
[b"proxy", user_pubkey]owned byoracle-vault. The bridge target and the source/sink for engine balance.
Get your deposit address
GET /v1/deposit/bridge-address/{user_fogo_pubkey}
user_fogo_pubkey accepts base58 (standard Solana/Fogo format) or 32-byte hex.
Response:
{
"success": true,
"data": {
"chain": "solana",
"address": "4xR2kF7b8K...",
"instructions": "Send USDC (SPL token) or SOL from any Solana wallet ...",
"initialized": true,
"usdc_token": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"min_usdc_micro": 1000000,
"min_sol_lamports": 50000000,
"max_usdc_per_tx_micro": 10000000000,
"daily_cap_remaining_usdc_micro": 100000000000
}
}
The returned address is deterministic — same input → same output. Safe to cache per user.
Limits
The backend enforces three layers of limits:
1. Anti-dust minimums (all tiers)
| Asset | Min | Why |
|---|---|---|
| USDC | 1 USDC (1,000,000 µUSDC) | Below this, Wormhole + Solana fees exceed the deposit |
| SOL | 0.05 SOL (50,000,000 lamports) | Same — plus Jupiter swap cost |
Transfers below the minimum are rejected by the listener. Funds sit in the deposit address and can be recovered (see FAQ).
2. Per-transaction tier caps
Tier assignment lives on-chain in a UserTier PDA on oracle-vault. Absent PDA = retail default.
| Tier | Max USDC per tx | Typical user |
|---|---|---|
| 0 — Retail (default) | 10,000 USDC | Standard bettor |
| 1 — LP | 1,000,000 USDC | Market makers, liquidity providers |
| 2 — Institutional | 10,000,000 USDC | Named desks, whitelisted counterparties |
Upgrade flow (admin only):
POST /admin/bridge/user-tier
X-Admin-Key: <admin key>
{
"user": "<bs58 fogo pubkey>",
"tier": 1
}
The handler signs + submits a set_user_tier tx on Fogo. Subsequent GET /v1/deposit/bridge-address responses reflect the new tier in max_usdc_per_tx_micro. Redis caches the tier for 60s; if you need an immediate refresh, call POST /admin/bridge/invalidate-tier-cache with the user pubkey.
3. Global daily bridge cap
Across all users, the bridge executor enforces a rolling 24h aggregate cap:
- Cap: 100,000 USDC
- Reset: 00:00 UTC daily
- Counter:
daily_cap_remaining_usdc_microin the deposit-address response (live) - Behavior: incoming deposits above the cap are queued; listener retries after reset
Institutional desks needing more than 100k/day should coordinate with the Parti team to schedule a multi-day sequence or raise the cap for a named counterparty.
Bridge executor circuit breaker
The entire bridge can be toggled off by operator (BRIDGE_EXECUTOR_ENABLED=false). Used during incidents. Funds already in-flight are unaffected; new deposits sit in the deposit address until re-enabled.
Already on Fogo?
If your user already holds USDC on Fogo (bridged via Portal directly, or earned from another Fogo app), skip the Solana bridge entirely. Send USDC to the proxy wallet ATA:
proxy_wallet = PDA([b"proxy", user_fogo_pubkey], oracle_vault_program_id)
ata = ATA(proxy_wallet, FOGO_USDC_MINT)
The Fogo listener picks up the balance change within ~2 slots and credits the engine.
Limits don't apply to native Fogo deposits — the tier caps and the 100k daily cap only gate the cross-chain bridge path.
Send USDC (Solana → Fogo)
From any Solana wallet (Phantom, Backpack, Solflare, CLI), send SPL USDC to the address from /v1/deposit/bridge-address. Use the Solana mainnet USDC mint:
EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
Example via Solana CLI:
spl-token transfer --url https://api.mainnet-beta.solana.com \
EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v \
100 \
<your_deposit_address>
Non-USDC SPL tokens
Supported: any Jupiter-routable SPL token (SOL, BONK, WIF, JUP, etc.). The listener auto-swaps to USDC via Jupiter v6 before bridging. Max slippage: 1%. Slippage loss is borne by the user.
What happens next (automatic)
- Listener detects the SPL transfer within ~10 seconds (polls every 3s)
- Tier lookup + cap check. Under minimum → rejected. Over per-tx cap → rejected. Over daily cap → queued for retry.
- If non-USDC: Jupiter swap → USDC
- Wormhole
transfer_with_payloadon Solana (payload carries recipient proxy wallet) - Guardians sign VAA (~30s)
- Operator submits
complete_transfer_wrappedon Fogo - USDC lands in the user's proxy wallet USDC ATA
operator_depositvault ix credits the engine balance
Total: 1–5 minutes end-to-end in normal conditions. 90s best-case.
Check balance
GET /v1/balance/{user_fogo_pubkey}
{
"available": 100000000,
"locked": 0,
"total": 100000000
}
All values are micro-USDC (6 decimals). locked = funds held in open orders.
Check deposit status
GET /v1/deposit/status/{user_fogo_pubkey}
Returns the last 10 deposit attempts with phase: detected → swapped → bridged → credited (or failed / queued).
{
"deposits": [
{
"tx_signature": "5Kx8...",
"amount_micro_usdc": 50000000,
"source_token": "USDC",
"phase": "credited",
"timestamp": 1745000000
}
]
}
Withdraw
Symmetric to deposit:
POST /v1/withdraw
{
"fogo_address": "<user fogo pubkey>",
"amount": 50000000,
"recipient_solana": "<solana wallet>",
"signature": "<ed25519 over canonical withdraw message>",
"nonce": 42
}
Flow:
- Vault debits
availablebalance on Fogo transfer_wrappedburns the wrapped USDC on Fogo → emits Wormhole VAA- Relayer (or user) calls
complete_transfer_nativeon Solana - USDC lands in the recipient Solana wallet
If recipient_solana is omitted, the default is the user's deposit address — they can receive withdrawals at the same address they deposit to.
Proxy Wallets
Seeds: ["proxy", user_fogo_pubkey]
Program: oracle-vault (Fogo)
Per-user PDA, deterministic. Functions:
- Receives bridge arrivals from Wormhole
- Holds USDC temporarily between bridge and engine credit
- Source for Solana-direction withdrawals
- Provides a clear on-chain audit trail per user
GET /v1/proxy/{user}
{
"data": {
"fogo_address": "abc123...",
"proxy_wallet": "7yQ3mH9a...",
"proxy_usdc_ata": "FJx1k2m...",
"usdc_balance": 5000000,
"initialized": true
}
}
Gas Relay
Users don't need FOGO or SOL. The operator pays gas and charges back in USDC.
- 10% markup on gas costs (configurable via
FeeConfig.gas_markup_bps) - Gas debt tracked per user on-chain
- Withdrawals blocked until gas debt cleared
GET /v1/gas/price
{
"data": {
"sol_per_tx_lamports": 5000,
"sol_per_tx_usdc": 1,
"fogo_per_tx_usdc": 1,
"markup_bps": 1000
}
}
FAQ
Why a separate deposit address? Why not my proxy wallet? The proxy wallet lives on Fogo. To get funds Solana → Fogo, someone has to run the Wormhole bridge tx. The deposit address is an operator-controlled Solana wallet that runs it automatically — the same model every cross-chain exchange uses.
Is it safe? The operator holds the private key to my deposit address. Yes, transiently. The listener sweeps within seconds. Funds sit in the operator wallet for ~10–60s while the swap + bridge tx confirms; after that they're in the Wormhole bridge (decentralized), then your proxy wallet (your custody via the vault program). Zero-custody alternative: use Portal Bridge directly and set the recipient to your proxy wallet.
What if the listener goes down and my USDC sits in the deposit address? The address is deterministic from a master seed. Funds are recoverable even if the service is offline indefinitely — the operator rederives the keypair and manually bridges. No funds lost.
Why the 100k daily cap? Wormhole guardian + relay rate limits, plus a circuit-breaker against single-day catastrophic events. Institutional counterparties can bypass via coordinated multi-day sequences.
What about EVM / Bitcoin / other chains? Planned via relay.link integration once they add Fogo as a destination. Timeline: TBD. Today, bridge to Solana first (Wormhole, CCTP, or CEX) then deposit via the Solana flow.
Fees? - Solana tx fee (~$0.001) — operator pays out of deposit, no separate charge - Wormhole relay fee (~$0) — operator covers - Jupiter slippage on non-USDC swaps — max 1%, user pays - Oracle-side deposit fee — 0%