Vault и деривация
Vault — это дерево адресов, которое вы проектируете сами. Сегменты деривации становятся детерминированными путями, а путь адреса — вашей моделью учёта.
Vault — это отдельное пространство вашей организации в V3 Custody: у него есть код (code, slug), имя и — главное — структура деривации, которую вы задаёте сами. Почти все маршруты API начинаются с кода Vault'а, а все адреса, балансы, политики и отчёты живут внутри него.
Введение уже описало Vault в двух словах. Здесь разберём, из чего он собран и как из ваших сегментов получаются детерминированные адреса.
Схему адресов вы проектируете сами
Обычный кошелёк выдаёт адреса «как получится». Vault идёт от обратного: вы описываете, какими осями различаются ваши адреса, а платформа превращает это в дерево путей деривации.
Каждый адрес — это лист дерева. Дорога от корня к листу состоит из сегментов, и каждый сегмент отвечает за одну ось: сеть, назначение, актив, клиент, стадия — набор выбираете вы. Порядок сегментов фиксирован: он и определяет форму пути.
Сеть — обязательный первый сегмент
В структуре есть один особый сегмент — network. Он определяет блокчейн и его coinType по стандарту BIP‑44 (например, TRON — 195, Ethereum — 60). Значения сети попадают в глобальный реестр Network, а путь всегда начинается с m/44/{coinType}/....
Один Vault может держать несколько сетей: к нему подключаются нужные coinType, и под каждую строится своя ветка.
Сегменты: статические и динамические
Остальные сегменты — это Route'ы, упорядоченные полем order (нормализуется от 0). Route бывает двух видов:
- Статический — закрытый список вариантов, который вы задаёте заранее. Например,
purpose=invoices|settlement,asset=usdt|usdc. Каждый вариант хранит свой индекс в пути; ненужные варианты можно отключить (disabled), не ломая уже выданные адреса. - Динамический — не список, а целочисленный индекс в диапазоне
[0, 2^31−1]. Он даёт новую ветку на каждую сущность: на клиента, на заявку, на депозит. Именно так под каждого клиента заводится отдельное поддерево, а адресов в нём — сколько угодно.
Смысл разделения простой: статические сегменты — это ваши категории (их немного, они известны заранее), динамические — ваши экземпляры (их много, они появляются на ходу).
Как из сегментов рождается адрес
Адрес создаётся выбором значения для каждого сегмента. Дальше платформа собирает канонический путь и заводит адрес:
Выбираются значения сегментов
Сеть (networkCode) и значения статических сегментов (staticSegments). Для динамических сегментов индекс подбирается автоматически.
Считается следующий индекс
Для этой комбинации сегментов берётся следующий свободный pathIndex (с повтором при конкурентном конфликте) — так два параллельных запроса не займут один путь.
Собирается канонический путь
Из значений строится путь вида m/44/{coinType}/{seg0}/{seg1}/... — по одному числу на сегмент, в фиксированном порядке.
Проверяется политикой
Создание адреса проходит через Policy Engine (action=create_address): он может разрешить, отклонить или отправить на подтверждение.
Пример создания адреса:
curl -X POST "[BASE_URL]/api/v1/vaults/demo_vault/addresses" \
-H "Authorization: Bearer $V3_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"networkCode": "tron_network",
"staticSegments": { "purpose": "invoices", "asset": "usdt" }
}'
await fetch("[BASE_URL]/api/v1/vaults/demo_vault/addresses", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.V3_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
networkCode: "tron_network",
staticSegments: { purpose: "invoices", asset: "usdt" },
}),
});
requests.post(
"[BASE_URL]/api/v1/vaults/demo_vault/addresses",
headers={"Authorization": f"Bearer {os.environ['V3_TOKEN']}"},
json={
"networkCode": "tron_network",
"staticSegments": {"purpose": "invoices", "asset": "usdt"},
},
)
Путь читается в обе стороны
Поскольку путь детерминирован, его можно собрать из сегментов и разобрать обратно:
POST /api/v1/vaults/{code}/derivation-path/build— собрать канонический путь из значений сегментов. Проверяет, что сеть подключена к Vault, присутствуют все Route'ы, статические варианты существуют и включены, а динамические значения — целые в допустимом диапазоне.POST /api/v1/vaults/{code}/derivation-path/parse— разобрать путь обратно на сеть и значение каждого сегмента. Ошибётся, если префикс неm/44/,coinTypeне подключён к Vault или число сегментов не совпадает со структурой.
Разберём путь на примере:
m / 44 / 195 / 0 / 0 / 42
44— стандарт BIP‑44.195—network: TRON (это и естьcoinType).0—purpose:invoices(статический вариант).0—asset:usdt(статический вариант).42—client: индекс 42 (динамический сегмент).
Почему дерево — это модель учёта
Форма пути — не техническая деталь, а способ вести учёт:
- По адресу видно назначение.
purpose=invoices,asset=usdt,client=42— сразу понятно, что это адрес для инвойсов конкретного клиента, а не безымянная строка. - Политики пишутся по тем же сегментам. Policy Engine выбирает правила по сети, активу, назначению — тем же осям, что и дерево. Не нужно перечислять адреса вручную.
- Отчёты сворачиваются по осям. Балансы и обороты агрегируются по сети, активу, назначению, клиенту — потому что это уже заложено в структуру.
Создание Vault
Vault со всей структурой деривации создаётся одним запросом POST /api/v1/vaults. В теле передаётся RouteMap — карта сегментов, обязательно со спец‑ключом network (его варианты добавляются в реестр Network по coinType); остальные ключи становятся Route'ами, а их order нормализуется от 0 в порядке следования.
Полную конфигурацию созданного Vault'а — подключённые сети (по coinType) и Route'ы (по order) со всеми вариантами — отдаёт GET /api/v1/vaults/{code}.
Точная схема тела RouteMap и всех полей — в Справочнике API на странице создания Vault'а. Здесь описан смысл структуры, а не формат запроса.