Deposit Flow
Overview
DF deposits are credited to the spot wallet by a backend listener that tails the BSC vault contract (ZtdxSpotVault) for SpotDeposit events. There is no deposit-initiation API — the user transacts directly with the vault on-chain, and the backend reconciles asynchronously.
After SPOT_BSC_CONFIRMATION_DEPTH blocks (currently 20) the listener considers the deposit final, credits spot_balances.available for the user, and inserts a row into spot_deposits. Idempotency is guaranteed by a (chain_id, tx_hash, log_index) unique key — replaying the same event is a no-op.
Lifecycle
User wallet
│
│ 1. DF.approve(vault, amount)
▼
DF ERC-20 contract ─────────────────────────────────────────────▶
│
│ 2. ZtdxSpotVault.deposit(token, amount) │
▼ │
ZtdxSpotVault (0x4Fe0b354c5865ee9deb979a99030d757ae47664a) │
│ │
│ emits SpotDeposit(account, token, amount) │
▼ │
BSC block N │
│ │
│ ≥ 20 confirmations (~40 s) │
▼ │
Backend listener (polls every 2 s) │
│ │
├─ INSERT spot_deposits (status = "confirmed") │
└─ spot_balances.available += amount ◀────────────────────────
Step 1 — Approve
The user approves the vault contract to pull amount of DF from their wallet:
DF.approve(vaultAddress, amount);
// vaultAddress = 0x4Fe0b354c5865ee9deb979a99030d757ae47664a
// amount = wei (1 DF = 10**18 wei because DF has 18 decimals)
Step 2 — Deposit
The user calls the vault's deposit function:
ZtdxSpotVault.deposit(address token, uint256 amount);
// token = 0x8063a43ed88397c1B10DA23dcC60ba1E7A0Bf555 (DF on BSC Testnet)
// amount = wei
The vault accepts the transfer and emits:
event SpotDeposit(address indexed account, address indexed token, uint256 amount);
Step 3 — Backend credits
The listener polls BSC every SPOT_BSC_POLL_INTERVAL_MS (2 000 ms). Once a SpotDeposit event has accumulated SPOT_BSC_CONFIRMATION_DEPTH (20) block confirmations:
- A row is inserted into
spot_depositswithstatus = "confirmed". spot_balances.availablefor the depositing user is incremented by the credited amount.
On-Chain Components
| Item | Network | Value |
|---|---|---|
| BSC chain id | BSC Testnet | 97 |
Vault contract (ZtdxSpotVault) | BSC Testnet | 0x4Fe0b354c5865ee9deb979a99030d757ae47664a |
| DF token (ERC-20) | BSC Testnet | 0x8063a43ed88397c1B10DA23dcC60ba1E7A0Bf555 |
| DF decimals | — | 18 |
Confirmation Depth & Latency
| Parameter | Value |
|---|---|
SPOT_BSC_CONFIRMATION_DEPTH | 20 blocks |
| BSC Testnet block time | ~2 seconds |
| Steady-state credit latency | ~40 seconds after deposit TX is mined |
| Cold-start catch-up rate | ~30 blocks/second |
During cold-start (e.g. after a server restart), the listener replays historical blocks at ~30 blocks/second until it reaches the chain tip. Any deposits made while the listener was offline are credited during catch-up.
Code Example: monitoring deposits
Poll GET /spot/deposits until a deposit with a known tx_hash appears with status = "confirmed".
import time
import requests
BASE_URL = "https://api-sepolia.p99.world/api/v1"
JWT = "your_jwt_token"
# The tx_hash of your on-chain deposit transaction.
EXPECTED_TX = "0xabc123..."
def get_deposits(limit: int = 50) -> list:
r = requests.get(
f"{BASE_URL}/spot/deposits",
headers={"Authorization": f"Bearer {JWT}"},
params={"limit": limit},
timeout=5,
)
r.raise_for_status()
return r.json()
print(f"Waiting for deposit {EXPECTED_TX[:12]}... to be confirmed...")
for attempt in range(60): # poll for up to 5 minutes
deposits = get_deposits()
match = next((d for d in deposits if d["tx_hash"] == EXPECTED_TX), None)
if match:
print(
f"Deposit confirmed! amount={match['amount']} {match['token']} "
f"block={match['block_number']} confirmed_at={match['confirmed_at']}"
)
break
print(f" attempt {attempt + 1}/60 — not yet visible, retrying in 5 s...")
time.sleep(5)
else:
print("Timed out waiting for deposit confirmation.")
The
confirmed_attimestamp in the response is when the backend credited the balance. Theblock_numbertells you which BSC block contained theSpotDepositevent.
See also: GET /spot/deposits reference, General Info.