Deposits and withdrawals

The two forex money queues — why a deposit can only be reversed and never approved, what approving or rejecting a withdrawal actually moves, the fees charged on each, and the guards that refuse a deletion.

7 min readUpdated 3 August 2026deposits, withdrawals, fees, approval, reversal

These are the only two doors between a customer's platform wallet and their forex account, and they behave in opposite ways. A deposit is already done by the time you see it. A withdrawal has not happened yet and is waiting on you.

Both queues read core's transaction table — deposits under type FOREX_DEPOSIT, withdrawals under FOREX_WITHDRAW — filtered to this addon and gated on this addon's permissions.

Deposits

/admin/forex/deposit. Gated on access.forex.deposit; acting on a row needs edit.forex.deposit.

A deposit settles instantly

When a customer deposits, this happens in one transaction, with no approval step anywhere in it:

  1. The KYC gate deposit_forex is checked.

  2. The fee is calculated for the wallet type and currency.

  3. The wallet is debited the amount plus the fee.

  4. The forex account is credited the amount.

  5. The fee is credited to the platform.

The transaction row is written COMPLETED. The customer sees the balance move immediately and gets a receipt carrying the deposit's own reference.

Because forex deposits settle instantly, every row the screen listed was already COMPLETED while the handler refused every status change with Only pending transactions can be updated. Both buttons rejected every click, and there was no way at all to undo a mistaken deposit.

Reversing one

Open the row — /admin/forex/deposit/{id} — and submit a rejection. That is a reversal, and it is the only action this screen has.

What it does, in one transaction:

  • Takes the amount back out of the forex account the deposit credited — the specific account recorded on the transaction, not "this user's LIVE account".
  • Credits the customer's wallet the amount plus the fee, so they are made whole for everything they actually parted with.
  • Marks the transaction REJECTED and records the reversal in its metadata.

The forex account must still hold what the deposit gave it. If the customer has already invested or withdrawn the money, reversing would leave a negative balance, and the request is refused with both figures named. That is deliberate: a human has to decide what happens next.

The amount is not editable. It comes from the deposit being reversed.

A deposit that has already been reversed cannot be reversed again, and only a COMPLETED deposit can be reversed at all.

Deposit fees

The fee depends entirely on the plan's wallet type, and there is no forex-level fee setting anywhere.

Wallet type Fee
FIAT Zero, always. The currency table has no fee column, so fiat forex movements have always been free
SPOT The percentage fee on the exchange currency row, plus the network's fixed withdrawal fee when a chain is named and your provider is Binance or KuCoin
anything else Refused with Invalid wallet type

Fee calculation used to fetch the exchange's currency list unconditionally and treat any failure as fatal. On an install without working exchange credentials that lookup threw Currency not found, and a customer could neither fund a forex account nor take money back out.

The exchange is now consulted only when it can actually contribute a fixed network fee, and a failure costs that fee rather than the whole request.

Deposit protections

  • Rate limit — five financial operations per minute per caller.
  • Idempotency — every deposit must carry a client-supplied requestNonce of 8 to 128 characters, which builds a deterministic key. A retry after a timeout that had actually gone through collapses onto the same operation instead of debiting the wallet twice.
  • Fraud — more than ten deposits in 24 hours is refused, and so is any deposit worth more than 10,000 USD-equivalent. Both figures are hard-coded. The value ceiling is compared in USD equivalent, not raw units, so 15,000 JPY is no longer rejected while 9,000 BTC passes.

Withdrawals

/admin/forex/withdraw. Gated on access.forex.withdraw; acting on a row needs edit.forex.withdraw.

What has already happened when a row appears

Submission debits the customer's forex account the amount plus the fee immediately, and writes a PENDING transaction. Nothing is credited anywhere.

The amount has left the customer's forex account and arrived nowhere. Until you approve or reject, it exists only as a row. The platform scores this queue against a seven-day target and the admin dashboard shows the age of the oldest item still waiting.

The row is editable only while PENDING, and the only editable field is the description.

Approving

Open the row — /admin/forex/withdraw/{id} — and set the status to COMPLETED. In one transaction:

  • The customer's wallet is credited the amount the customer asked for, in the currency they asked for it in.
  • The daily and monthly withdrawal counters advance by the gross figure, rolling over whichever window has elapsed.
  • The platform fee is collected.

They come from the request the customer actually made. Submitting a different amount while approving is ignored and logged.

This matters because an earlier build took the figures from the approval payload: approving a 100 withdrawal as 5,000 credited 5,000 to a wallet against a forex account that had only given up 100, and the other 4,900 was created out of nothing. Separately, the credit used to be multiplied by the currency's USD rate — invisible for USDT, but a 1 BTC withdrawal credited roughly 64,000 BTC. A forex withdrawal moves money between a customer's own account and their own wallet in the same currency, so there is nothing to convert.

Approval re-checks the withdrawal caps. If approving would breach the customer's daily or monthly limit, it is refused — so a batch of pending requests cannot be cleared past the cap.

Rejecting

Set the status to REJECTED. The full gross amount originally debited — amount plus fee — is returned to the forex account the withdrawal came from, recorded on the transaction. No platform fee was ever collected, so there is nothing to reverse on that side.

Rejection is the correct way to undo a withdrawal request. It is not a penalty and the customer loses nothing.

Withdrawal protections

  • Rate limit — five financial operations per minute per caller.
  • Caps — per-account daily and monthly limits, validated at submission against the gross figure and counted on approval. Defaults are 5,000 and 50,000 in the account's own currency units. See Accounts.
  • Fraud — more than five withdrawals in 24 hours is refused outright. Three or more in 24 hours raises the risk score above the review threshold and the request is refused with This withdrawal requires additional verification.

The deletion guards

Both queues allow deletion, and both are guarded.

Deletion is refused while any selected withdrawal is still PENDING, and the message lists them with their amounts.

The reason is direct: the amount has already left the customer's forex account and is waiting on your decision. Deleting the row does neither of the two things that resolve it — the debit stands, no credit ever happens, and the record of why is gone. The money simply stops existing.

Reject it first, which refunds the account properly, and then delete the row if you do not want it.

Deposits carry no such guard, because a completed deposit has already resolved. Reverse it if it was a mistake; delete it only if you are certain you no longer need the record.

A working routine

  1. Start at the dashboard. The decision band tells you how many items are waiting on each queue and how old the oldest one is. If both read clear, there is nothing to do.

  2. Work withdrawals first. A customer is waiting for money that has already left their account. Deposits are already settled and nobody is blocked.

  3. For each withdrawal, check the customer and the account before approving. The figures are fixed; the only judgement is whether the request is legitimate.

  4. Approve or reject — never delete. Rejection refunds; deletion strands.

  5. Only reverse a deposit when it was genuinely wrong. Check the forex account still holds the money first, because the reversal will refuse otherwise.

If a queue is not draining, or an approval is failing, see Troubleshooting.