Skip to main content

Request Withdrawal Signature

Description

Reserves the requested amount of DF, signs an EIP-712 SpotReleaseFunds release authorization, and returns the data the caller needs to submit to the vault contract on-chain.

This is step 1 of the two-step withdrawal flow. After receiving the signature, the caller submits it to ZtdxSpotVault.withdraw() on BSC. For the full flow including the vault call and status polling, see Withdraw Flow.

Atomicity: Within a single database transaction, available is decremented and frozen is incremented by amount. If the on-chain call is not made before deadline, a reaper task marks the record expired and returns the funds to available. See Withdraw Flow → Expiry Reaper.

MVP supports only DF.

HTTP Request

POST /spot/withdraw/request (JWT only)

API-key callers receive 403 API Key permission denied: withdraws not allowed. See General Info → Authentication.

Weight

0 — no per-IP weight limit today (MVP).

Request Parameters

NameTypeRequiredDescription
tokenSTRINGYESToken to withdraw. MVP: DF only.
amountDECIMALYESDecimal amount as a string (e.g. "100"). Must be at least the server-configured minimum (SPOT_WITHDRAW_MIN_AMOUNT_DF).

There is no recipient parameter. The EIP-712 account field is always set to the caller's address, and the vault checks msg.sender == account — a third party cannot replay the signature for a different recipient.

Response Example

200 OK

{
"id": "9f2a1c4e-5b67-4d8a-bf93-2e1f4a6c8d10",
"nonce": 42,
"signature": "0x...65-byte hex...",
"deadline": 1778402000,
"vault_address": "0x4Fe0b354c5865ee9deb979a99030d757ae47664a",
"chain_id": 97,
"amount": "100",
"amount_in_wei": "100000000000000000000"
}
FieldNotes
idUUID of the withdrawal record. Use with GET /spot/withdrawals/:id to poll status.
noncePer-(user, chain) monotonic nonce. Prevents on-chain replay.
signature65-byte 0x-prefixed EIP-712 signature. Pass verbatim to vault.withdraw().
deadlineUnix seconds. The on-chain withdraw() call must execute strictly before this timestamp.
vault_addressThe ZtdxSpotVault contract address to call withdraw() on.
chain_idEVM network the signature targets — 97 (BSC Testnet).
amountDecimal string of the reserved amount.
amount_in_weiWei-scaled amount placed in the EIP-712 value field. Feed this into vault.withdraw() — preferred over amount because it removes decimal/precision ambiguity.

The response does not include token or status fields — the record is always signed on success; fetch GET /spot/withdrawals/:id for the full record.

Error Responses

HTTPerror
400unsupported token: <token> — token other than DF requested.
400amount below minimum <min>amount is below the server-configured minimum. There is no separate non-positive check — zero or negative amounts also fail this check.
400insufficient balance — spot available < amount.
400invalid user address — the authenticated address could not be parsed.
403API Key permission denied: withdraws not allowed — caller authenticated via API Key.
409you have a pending withdrawal — submit it on-chain or wait for it to expire before signing a new one — a signed withdrawal at or after the contract's current nonce already exists.
409withdrawal slot conflict — try again in a moment — the nonce slot is occupied by a non-expired row.
500chain query failed — reading the vault's on-chain nonce failed.
500signer unavailable / sign failed — the backend signer could not be initialized or signing failed.
500internal — unexpected database error; investigate logs.
503spot subsystem disabled — server has the spot subsystem turned off.
503spot blockchain not initialized — the BSC blockchain service is not running.

Full list: Error Codes.

Code Examples

cURL (JWT)

JWT="your_jwt_token"

curl -s -X POST "https://api.prex.world/api/v1/spot/withdraw/request" \
-H "Authorization: Bearer ${JWT}" \
-H "Content-Type: application/json" \
-d '{
"token": "DF",
"amount": "100"
}'

Python

import requests

BASE_URL = "https://api.prex.world/api/v1"
JWT = "your_jwt_token"

resp = requests.post(
f"{BASE_URL}/spot/withdraw/request",
headers={
"Authorization": f"Bearer {JWT}",
"Content-Type": "application/json",
},
json={"token": "DF", "amount": "100"},
timeout=5,
)
resp.raise_for_status()
sig = resp.json()

print(f"withdrawal id : {sig['id']}")
print(f"nonce : {sig['nonce']}")
print(f"deadline : {sig['deadline']}")
print(f"signature : {sig['signature'][:20]}...")
# Next: call vault.withdraw(DF_TOKEN, amount_in_wei, deadline, signature)
# See withdraw-flow.md for the full end-to-end code example.