You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: POLICY.md
+135-9Lines changed: 135 additions & 9 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,14 +2,16 @@
2
2
3
3
This broker uses a JSON policy file to restrict:
4
4
5
-
-**Withdrawals**: permitted exchanges, networks, and destination address whitelist
5
+
-**Withdrawals**: permitted exchanges, networks, tokens, and destination address whitelist
6
+
-**Deposits**: permitted exchanges, networks, and tokens (gates deposit address fetching)
6
7
-**Orders**: permitted exchanges/pairs, plus optional directional conversion limits
7
8
8
9
The policy is loaded at startup. If you start the broker with a **policy file path**, the file is watched and policies are reloaded when the file changes.
In this example, a BINANCE + ARBITRUM withdraw uses the first rule (priority 4). A BINANCE + SOL withdraw falls to the second rule (priority 3). A KRAKEN + BEP20 withdraw uses the third rule (priority 2). Everything else hits the global catch-all (priority 1).
186
+
In this example, a BINANCE + ARBITRUM withdraw uses the first rule (priority 4) and only allows ETH, USDT, USDC, and ARB. A BINANCE + SOL withdraw falls to the second rule (priority 3), which has no `coins` restriction — any token is allowed. A KRAKEN + BEP20 withdraw uses the third rule (priority 2) and is restricted to BNB and USDT. Everything else hits the global catch-all (priority 1), which also allows any token.
159
187
160
188
Common rejection reasons:
161
189
162
190
- no matching exchange + network entry
163
191
- address not whitelisted
192
+
- token not in `coins` for the matched rule
164
193
165
194
---
166
195
@@ -261,12 +290,107 @@ Example:
261
290
262
291
|||
263
292
|---|---|
264
-
|**Type**|`{}` (empty object) |
293
+
|**Type**|`{ rule?: DepositRuleEntry[] }`|
294
+
|**Required**| Yes (the `deposit` key must be present) |
295
+
296
+
Deposit validation gates the `FetchDepositAddresses` action — a request to fetch a deposit address is rejected if the policy does not permit deposits for the given exchange, network, and token. The actual deposit confirmation is not gated by the policy (only the address fetch is).
297
+
298
+
### Backward compatibility
299
+
300
+
-**`"deposit": {}`** — no `rule` key or empty rule array: **all deposits are allowed**. This is backward compatible with policies written before deposit rules were added.
301
+
-**`"deposit": { "rule": [...] }`** — only deposits matching a rule are allowed.
302
+
303
+
---
304
+
305
+
### `deposit.rule: DepositRuleEntry[]`
306
+
307
+
**Optional.** When omitted or empty, all deposits are permitted.
308
+
309
+
Each entry scopes deposit permissions to an `exchange` + `network` combination, optionally restricted to specific tokens. When a `FetchDepositAddresses` request arrives, the broker finds the highest-priority matching rule and validates the token against it.
| 4 (highest) | exact match | exact match | Fully specific rule |
318
+
| 3 | exact match |`"*"`| Exchange-specific, any network |
319
+
| 2 |`"*"`| exact match | Network-specific, any exchange |
320
+
| 1 (lowest) |`"*"`|`"*"`| Global catch-all |
321
+
322
+
If rules are present but no entry matches, the request is rejected.
323
+
324
+
---
325
+
326
+
### `deposit.rule[].exchange`
327
+
328
+
|||
329
+
|---|---|
330
+
|**Type**|`string`|
265
331
|**Required**| Yes |
332
+
|**Normalisation**| Trimmed, uppercased |
266
333
267
-
`deposit` is required in the current policy schema, but it is **not enforced** today.
334
+
Same as `withdraw.rule[].exchange` — an exchange identifier (e.g. `"BINANCE"`) or `"*"` for wildcard.
335
+
336
+
---
337
+
338
+
### `deposit.rule[].network`
339
+
340
+
|||
341
+
|---|---|
342
+
|**Type**|`string`|
343
+
|**Required**| Yes |
344
+
|**Normalisation**| Trimmed, uppercased |
345
+
346
+
Same as `withdraw.rule[].network` — a network/chain identifier (e.g. `"ARBITRUM"`) or `"*"` for wildcard.
347
+
348
+
---
349
+
350
+
### `deposit.rule[].coins`
351
+
352
+
|||
353
+
|---|---|
354
+
|**Type**|`string[]`|
355
+
|**Required**| No |
356
+
|**Normalisation**| Each entry is trimmed, uppercased |
357
+
358
+
Optional array of CEX ticker symbols allowed for deposit under this rule. Behaves identically to `withdraw.rule[].coins`:
359
+
360
+
-**An array of ticker symbols** — e.g. `["ETH", "USDT"]`. Only these tokens may be deposited.
361
+
-**`["*"]`** — wildcard; allows any token (same as omitting the field).
362
+
-**Omitted / not present** — allows any token.
363
+
364
+
Matching is **case-insensitive**.
365
+
366
+
---
367
+
368
+
### Full deposit example
369
+
370
+
```json
371
+
{
372
+
"deposit": {
373
+
"rule": [
374
+
{
375
+
"exchange": "BINANCE",
376
+
"network": "ARBITRUM",
377
+
"coins": ["ETH", "USDT", "USDC", "ARB"]
378
+
},
379
+
{
380
+
"exchange": "*",
381
+
"network": "*"
382
+
}
383
+
]
384
+
}
385
+
}
386
+
```
387
+
388
+
In this example, a BINANCE + ARBITRUM deposit address request is matched by the first rule (priority 4) and is restricted to ETH, USDT, USDC, and ARB. Any other exchange/network combination hits the catch-all rule, which allows all tokens.
389
+
390
+
Common rejection reasons:
268
391
269
-
At present, deposits are effectively always allowed; this field is reserved for future deposit rule support. Set it to `{}`.
392
+
- deposit rules are present but no matching exchange + network entry
393
+
- token not in `coins` for the matched rule
270
394
271
395
---
272
396
@@ -285,5 +409,7 @@ The broker validates the policy JSON against a Joi schema when loading it.
285
409
286
410
-**Withdraw address rejected**: ensure the address is in the matching `withdraw.rule[].whitelist` entry (lowercase recommended).
287
411
-**Withdraw exchange/network rejected**: ensure there is a `withdraw.rule[]` entry whose `exchange` and `network` match (or wildcard-match) the request.
412
+
-**Withdraw token rejected**: ensure the matched `withdraw.rule[].coins` includes the token ticker (or omit `coins` to allow all).
413
+
-**Deposit address fetch rejected**: ensure `deposit.rule` contains a matching entry for the exchange + network + token, or use `"deposit": {}` to allow all.
288
414
-**Order rejected (market)**: ensure `order.rule.markets` contains a matching pattern for the exchange + pair.
289
415
-**Order rejected (limits)**: if `limits` is non-empty, ensure there's an entry for the exact `from` → `to` direction and the amount is within bounds.
0 commit comments