Transfer policy
Transfer policy определяет, как обрабатывать исходящие транзакции (withdrawal) ещё до того, как они уходят в блокчейн. Это центральный элемент управления рисками: на этой стадии решается, кому, куда, сколько и при каких условиях разрешено выводить средства.
Когда применяется
Проверка Transfer policy — третья стадия обработки исходящей транзакции: после базовых валидаций (баланс, fee, активность vault) и AML Screening, но до того, как транзакция уходит на подписание и в блокчейн. В этой точке решение политики — последний барьер перед началом подписания.
Action — что делает правило
| Action | Что произойдёт с транзакцией |
|---|---|
ALLOW |
Транзакция проходит автоматически, без аппрува. Если правило указывает подписантов — они будут назначены этой транзакции. |
REQUEST_APPROVAL |
Транзакция переходит в статус awaiting_approvals. Указанным аппруверам уходит push/WS-уведомление; решение принимается по правилам апрува. |
BLOCK |
Транзакция получает терминальный статус blocked_by_policy и больше никак не двинется — для повторной попытки клиент должен создать новую. |
Структура правила
Правило состоит из четырёх логических блоков:
WHO → Initiator (кто инициировал транзакцию)
FROM → Source (откуда выводим)
TO → Destination (куда выводим)
WHAT → Asset + Limitation (какой актив и в каких пределах)
Все блоки соединяются логическим И: правило срабатывает, только
если совпадают все указанные условия. Дополнительно для ALLOW и
REQUEST_APPROVAL правило описывает, кто будет подписывать (Signer)
и кто должен подтвердить (Approval).
Initiator — инициатор транзакции
Кто запустил withdrawal: пользователь или сервисный аккаунт.
| Поле | Описание |
|---|---|
any_initiator |
true — правило срабатывает для любого инициатора. Удобно для широких разрешающих/блокирующих правил. |
initiators |
Карта uuid → type. Конкретный список инициаторов; type — USER или SERVICE_ACCOUNT. Если запросивший подходит и его тип совпадает — матч. |
Зачем разделять User и Service Account
Часто полезно разрешать сервисным аккаунтам только мелкие
автоматические выводы (например, выплаты пользователям до 100 USD),
а крупные операции отдавать пользователям с обязательным
аппрувом. Это делается двумя правилами с разными initiators.
Source — откуда списываем
Источник средств для исходящей транзакции — всегда один из vault workspace.
| Поле | Описание |
|---|---|
any_source |
true — любой vault workspace. |
vault.any_vault |
true — любой vault (эквивалент any_source). |
vault.vault_uuids |
Конкретный список vault, к которым применимо правило. |
whitelisted.* |
Зарезервировано — реальные исходящие транзакции всегда идут из vault, но поле есть для симметрии с Destination. |
Destination — куда отправляем
Назначение бывает нескольких типов: внутренний vault (перевод между vault workspace), whitelisted-кошелёк (адрес из белого списка) или one-time address (свободно введённый адрес).
| Поле | Описание |
|---|---|
any_destination |
true — любое назначение. Самое широкое условие; используйте в дефолтных правилах. |
one_time_addresses_only |
true — правило срабатывает только для свободно введённых адресов. Хороший крючок для требования аппрува на любые «новые» адреса. |
vault.any_vault / vault.vault_uuids |
Перевод во внутренний vault: любой или конкретный. |
whitelisted.any_whitelisted_address |
Любой адрес из whitelist workspace. |
whitelisted.any_whitelisted_internal_address |
Любой whitelisted внутренний адрес. |
whitelisted.any_whitelisted_external_address |
Любой whitelisted внешний адрес. |
whitelisted.any_whitelisted_contract_address |
Любой whitelisted адрес смарт-контракта. |
whitelisted.whitelisted_external_addresses |
Конкретный список внешних адресов. |
whitelisted.whitelisted_internal_addresses |
Конкретный список внутренних. |
whitelisted.whitelisted_contract_addresses |
Конкретный список контрактных. |
One-time addresses
Включите one_time_addresses_only в правило с
REQUEST_APPROVAL — это самый простой способ гарантировать,
что любая транзакция на «новый» адрес проходит через ручное
подтверждение.
Asset — какой актив и какой порог
Описывает актив и порог суммы, при превышении которого правило активируется.
| Поле | Описание |
|---|---|
any_asset |
true — правило действует для любого актива. В этом режиме сумма всегда сравнивается в USD-эквиваленте. |
asset_id |
Конкретный актив (например, BTC, ETH, USDT-TRC20). |
amount |
Пороговое значение (decimal-строка). Правило матчится, если сумма транзакции ≥ amount. Для дефолтных правил укажите 0 — тогда правило применяется к любым суммам. |
currency |
USD — сравнивать в USD-эквиваленте; NATIVE — в единицах самого актива (например, в BTC). При any_asset = true всегда используется USD. |
Семантика порога
Условие — «сумма больше или равна порогу». Чтобы правило
срабатывало для всех сумм, поставьте amount = 0. Чтобы
разделить «мелкие» и «крупные» переводы, заведите два правила:
одно с amount = 1000 (например, требует аппрув от 1000 USD),
второе ниже по списку с amount = 0 (мелкие — auto-allow).
Signer — кто подпишет
Применимо для ALLOW и REQUEST_APPROVAL.
| Поле | Описание |
|---|---|
signer_uuids |
Конкретный список подписантов, которым будет назначена транзакция. |
is_initiator_signer |
true — подписывает сам инициатор (если у него есть право SIGN_TRANSACTION и привязанное устройство). |
Approval — кто и как должен подтвердить
Применимо только для REQUEST_APPROVAL. Описывает, какие именно
подписи аппруверов нужны, чтобы транзакция была одобрена.
| Поле | Описание |
|---|---|
initiator_can_approve |
true — инициатор может сам выступить в роли одного из аппруверов. По умолчанию false, чтобы избежать «само-аппрува». |
users_sets |
Карта именованных наборов аппруверов. Каждый набор — { user_uuids, threshold }. Имя набора (A, B, …) используется в expression. |
expression |
Логическое выражение, описывающее, какие наборы должны выполниться. Поддерживает &&, \|\|, (…) и имена наборов. Примеры: A, A && B, (A && B) \|\| C. |
Внутри одного набора threshold задаёт минимальное число подписей.
Например, набор A со списком из 5 финансистов и threshold = 2
требует подписи любых двух из них. Если выражение
(A && B) || C, транзакция проходит, если выполнен набор C
целиком или оба набора A и B.
Имена наборов
По умолчанию один безымянный набор называется A
(DefaultApprovalSetName = "A"). При работе через UI имена
обычно проставляются автоматически.
Limitation — лимит на сумму
Описывает, как считается порог суммы из блока Asset: по одной транзакции или накопительно за окно времени.
SINGLE_TX (по одной транзакции)
Самый простой вариант. Правило срабатывает, если сумма текущей
транзакции ≥ asset.amount. Историю предыдущих транзакций не
учитывает.
TIMEFRAME (накопительный лимит за окно)
Правило срабатывает, если сумма всех подходящих транзакций за
последние hours часов (включая текущую) превысит порог.
Поля *_mode определяют, как именно агрегируются суммы:
| Поле | Значения | Что означает |
|---|---|---|
hours |
положительное число | Размер окна в часах (sliding window). Максимум — 15 дней (360 часов). |
initiator_mode |
PER_INITIATOR |
Считать отдельно для каждого инициатора. У каждого пользователя/SA — свой счётчик. |
ACCUMULATED_INITIATORS |
Один общий счётчик на всех инициаторов вместе. | |
source_mode |
PER_SOURCE |
Отдельный счётчик для каждого vault-источника. |
ACCUMULATED_SOURCES |
Общий счётчик по всем источникам. | |
destination_mode |
PER_DESTINATION |
Отдельный счётчик для каждого получателя. |
ACCUMULATED_DESTINATIONS |
Общий счётчик по всем получателям. |
Примеры
- «Каждый сотрудник — не более 10 000 USD в сутки»:
hours = 24,initiator_mode = PER_INITIATOR,source_mode = ACCUMULATED_SOURCES,destination_mode = ACCUMULATED_DESTINATIONS,amount = 10000,currency = USD. - «Со всех vault'ов не больше 1 BTC в час суммарно»:
hours = 1, все*_mode = ACCUMULATED_*,asset_id = BTC,amount = 1,currency = NATIVE.
Резервирование лимита
Когда транзакция входит в awaiting_approvals, её сумма
резервируется в счётчике и блокирует дальнейшие переводы
до достижения порога. Если транзакция позже отклонится
(declined/expired/failed), резерв автоматически
освобождается и счётчик уменьшается.
Поведение по умолчанию
При первом обращении к политике в workspace создаются два правила:
- Default allow (position 1) —
ALLOW,any_initiator,any_source,any_destination,any_asset,amount=0,is_initiator_signer=true. Разрешает любые транзакции с подписанием инициатором. - Default block (position 2) —
BLOCK, помечен какglobal, не редактируется. Срабатывает, если ни одно пользовательское правило не подошло (страховка от «дыр»).
Если политика отсутствует или деактивирована — все транзакции
автоматически одобряются системой с пометкой
no active transfer policy, transaction automatically approved.
Не оставляйте default allow в проде
Дефолтное allow all создано исключительно для быстрого старта.
В реальной конфигурации замените или сдвиньте его вниз, чтобы
выше срабатывали правила с конкретными лимитами и аппрувами.
Типовые сценарии
1. Whitelist-only режим
Разрешить переводы только на адреса из white-list, всё остальное — блокировать.
| # | Action | Destination | Asset | Limitation |
|---|---|---|---|---|
| 1 | ALLOW |
whitelisted.any_whitelisted_address |
any_asset, 0 USD |
SINGLE_TX |
| 2 | BLOCK |
any_destination |
any_asset, 0 USD |
SINGLE_TX |
2. Двойной контроль для крупных сумм
Мелкие — авто-разрешать, крупные — два аппрува, очень крупные — блокировать.
| # | Action | Asset.amount/currency | Approval |
|---|---|---|---|
| 1 | BLOCK |
100000 USD |
— |
| 2 | REQUEST_APPROVAL |
1000 USD |
A && B (директор и финансист) |
| 3 | ALLOW |
0 USD |
— |
3. Любой новый адрес — через подтверждение
| # | Action | Destination | Approval |
|---|---|---|---|
| 1 | REQUEST_APPROVAL |
one_time_addresses_only = true |
A (любой из админов) |
| 2 | ALLOW |
any_destination |
— |
4. Дневной лимит на сотрудника
| # | Action | Initiator | Limitation |
|---|---|---|---|
| 1 | BLOCK |
any_initiator |
TIMEFRAME 24h, PER_INITIATOR, accumulated by src/dst, amount=10000 USD |
| 2 | ALLOW |
any_initiator |
SINGLE_TX, 0 |