Skip to main content

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_deposits with status = "confirmed".
  • spot_balances.available for the depositing user is incremented by the credited amount.

On-Chain Components

ItemNetworkValue
BSC chain idBSC Testnet97
Vault contract (ZtdxSpotVault)BSC Testnet0x4Fe0b354c5865ee9deb979a99030d757ae47664a
DF token (ERC-20)BSC Testnet0x8063a43ed88397c1B10DA23dcC60ba1E7A0Bf555
DF decimals18

Confirmation Depth & Latency

ParameterValue
SPOT_BSC_CONFIRMATION_DEPTH20 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_at timestamp in the response is when the backend credited the balance. The block_number tells you which BSC block contained the SpotDeposit event.

See also: GET /spot/deposits reference, General Info.