Кошельки и биржи

Эта страница для тех, кто интегрирует Velum: кошельков, бирж, платёжных шлюзов и приложений. Velum не совместим с EVM: у него свой формат адресов, транзакций и подписи. Всё необходимое собрано здесь.

Технические параметры

Параметр Значение
Сеть {{chain}} (поле chain каждой транзакции)
Нативная монета VEL, 6 знаков (1 VEL = 1 000 000 µVEL)
Суммы Целые числа в минимальной единице токена
Время блока Цель 2 с (реальный темп измеряется в обозревателе) · эпоха: 1 800 блоков (≈ 1 ч)30 блоков (≈ 60 с)
Кривая и подпись ed25519 над каноническим JSON
Адрес vel + base58(открытый ключ ed25519, 32 Б)
Вывод ключей кошелька BIP-39 (24 слова) + SLIP-10 ed25519 по пути m/44'/7420'/аккаунт'/0'/индекс'
Комиссия за платёж 0 при стоимости меньше 1 000 € (с доказательством работы); 0,01 % от 1 000 €
Цена Собственный оракул: TWAP за 1 ч по {{pair}}
Порядок транзакций nonce кошелька, начинается с 0
ID транзакции Первые 48 hex-символов SHA-256(канонический JSON с sig)
Рекомендуемые подтверждения 6 (платежи), 30 (депозиты на биржах)
Узел JSON-RPC поверх WebSocket
Обозреватель REST API только для чтения по адресу /api

Адреса {#direcciones}

Адрес — это префикс vel и открытый ключ ed25519 (32 байта) в base58 (алфавит Bitcoin). Длина 46–47 символов.

import { ed25519 } from "@noble/curves/ed25519.js";
import { base58 } from "@scure/base";

// A Velum address is "vel" + base58(32-byte ed25519 public key). No checksum byte:
// validate by decoding and checking the length is exactly 32.
const address = (pub: Uint8Array) => "vel" + base58.encode(pub);
const isAddress = (a: string) =>
  a.startsWith("vel") && !a.startsWith("vel_") && (() => {
    try { return base58.decode(a.slice(3)).length === 32; } catch { return false; }
  })();
  • Контрольной суммы нет. Всегда проверяй адрес декодированием: должно получиться ровно 32 байта. Для адресов, вводимых вручную, советуй копирование или сканирование QR-кода.
  • Адреса, начинающиеся с vel_, — служебные счета без ключа (vel_pool, vel_reserve, vel_escrow). Не принимай выводы на них.
  • Пользователи могут делиться псевдонимом (@lucia) вместо адреса. Всегда разрешай его по блокчейну и показывай полученный адрес перед отправкой.

Вывод ключей {#claves}

Фраза BIP-39 из 24 слов порождает кошельки через SLIP-10 ed25519, все уровни усиленные (hardened):

Параметр Значение
Путь m/44'/7420'/аккаунт'/0'/индекс'
Кошелёк официального приложения m/44'/7420'/0'/0'/0'
Монета SLIP-44 7420 (ожидает регистрации)

Ключи личности и ящика для переписки выводятся из того же семени через HKDF-SHA512 (velum/v2/identity, velum/v2/mailbox) и не являются ключами кошелька: бирже или кошельку они не нужны.

import { mnemonicToSeedSync } from "@scure/bip39";
import { hkdf } from "@noble/hashes/hkdf.js";
import { sha512 } from "@noble/hashes/sha2.js";
import { walletFromPhrase, walletPath } from "@velum/sdk";

// Wallet keys: SLIP-10 ed25519, every level hardened: m/44'/7420'/account'/0'/index'
const hot = walletFromPhrase(phrase, 0, 0);       // m/44'/7420'/0'/0'/0' (the official app's wallet)
const user42 = walletFromPhrase(phrase, 0, 42);   // one deposit address per customer
walletPath(0, 42);                                // "m/44'/7420'/0'/0'/42'"

// Messaging keys are NOT wallet keys: they come from the same seed with HKDF-SHA512.
const seed = mnemonicToSeedSync(phrase);
const identity = hkdf(sha512, seed, undefined, new TextEncoder().encode("velum/v2/identity"), 32);

Через SDK:

import { walletFromPhrase, isAddress } from "@velum/sdk";

const { address, secret } = walletFromPhrase(phrase, 0, 0);
isAddress(address);                                // true: "vel" + base58(32-byte ed25519 public key)

Это тот же путь, что используют SDK @velum/sdk, аппаратные кошельки (Trezor, Ledger) и Snap для MetaMask. См. Экосистема.

Транзакции {#transacciones}

У всех транзакций есть общие поля:

Поле Тип Значение
type string Тип транзакции
chain string {{chain}}. При несовпадении транзакция отклоняется.
from адрес Подписывающий кошелёк
nonce целое Число предыдущих транзакций from (0, 1, 2…)
ts целое (мс) Время клиента. Справочное: блокчейн использует время блока.
sig hex (128) Подпись ed25519 кошелька

Подпись и канонический JSON

// Canonical JSON: keys sorted, no whitespace, undefined fields dropped. This is what gets signed and hashed.
export function canonical(v: unknown): string {
  if (v === null || typeof v !== "object") return JSON.stringify(v);
  if (Array.isArray(v)) return "[" + v.map(canonical).join(",") + "]";
  const o = v as Record<string, unknown>;
  return "{" + Object.keys(o).filter((k) => o[k] !== undefined).sort()
    .map((k) => JSON.stringify(k) + ":" + canonical(o[k])).join(",") + "}";
}

// sig = hex(ed25519.sign(utf8(canonical(tx without "sig")), walletSecret))
// txId = hex(sha256(utf8(canonical(tx with "sig")))).slice(0, 48)
  1. Собери объект без sig. Неиспользуемые необязательные поля пропусти (не ставь null).
  2. Сериализуй в канонический JSON: ключи отсортированы, без пробелов.
  3. Подпиши байты UTF-8 ключом кошелька. sig — подпись в hex.
  4. ID — это sha256(канонический JSON с sig) в hex, обрезанный до 48 символов.

Платёж (transfer)

{
  "type": "transfer",
  "chain": "{{chain}}",
  "from": "vel8Kx3vQ…",
  "to": "vel4Jt9mR…",
  "amount": 12500000,
  "fee": 0,
  "token": "tEUR",
  "memo": "inv-20481",
  "nonce": 17,
  "ts": 1793577600000,
  "sig": "9f2c…(128 hex)"
}
  • amount в минимальных единицах токена (µVEL, если token нет). Больше 0.
  • fee подчиняется правилу v3: 0, если стоимость платежа по цене оракула сети меньше 1 000 €; иначе 0,01 % суммы. Платежи с fee, равным 0, содержат печать работы pow (SHA-512, около 50 мс на телефоне), которая входит в подписываемые данные; её вычисляет buildTransfer из @velum/sdk. Точный формат описан в PROTOCOL.md и в spec/vectors/.
  • token необязателен: символ токена. Без него отправляется VEL.
  • memo необязательно, до 128 символов. Оно публично: используй его для ссылок на платёж, но никогда для персональных данных.

Регистрация узла (register)

{
  "type": "register", "chain": "{{chain}}", "from": "vel…node", "nonce": 0, "ts": 1793577600000,
  "name": "atico-madrid", "payout": "vel…wallet", "version": "2.0.0",
  "kind": "full", "endpoint": "wss://node.example.org:7420/ws",
  "sig": "…"
}

Бесплатно. kind — full (компьютер, создаёт блоки) или light (телефон). payout — кошелёк для выплат.

Токены (token.create, token.mint, pool.fund)

{ "type": "token.create", "chain": "{{chain}}", "from": "vel…", "nonce": 3, "ts": 1793577600000,
  "symbol": "CAFE", "name": "Café de barrio", "decimals": 2,
  "maxSupply": 100000000, "initial": 2500000, "mintable": true, "fee": 100000000, "sig": "…" }

{ "type": "token.mint", "chain": "{{chain}}", "from": "vel…issuer", "nonce": 4, "ts": 1793577660000,
  "symbol": "CAFE", "to": "vel…", "amount": 50000, "fee": 0, "sig": "…" }

{ "type": "pool.fund", "chain": "{{chain}}", "from": "vel…issuer", "nonce": 5, "ts": 1793577720000,
  "token": "CAFE", "amount": 1440000, "epochs": 1440, "fee": 0, "sig": "…" }

Правила для каждого поля — в разделе Свои токены.

Биржа (order.place, order.cancel)

{ "type": "order.place", "chain": "{{chain}}", "from": "vel…", "nonce": 9, "ts": 1793577600000,
  "pair": "VEL/tEUR", "side": "buy", "price": 12500, "amount": 250000000, "ttl": 1440, "sig": "…" }

{ "type": "order.cancel", "chain": "{{chain}}", "from": "vel…", "nonce": 10, "ts": 1793577660000,
  "id": "4c1f0a…", "sig": "…" }

price в минимальных единицах токена за 1 VEL; amount в µVEL (минимум 1 VEL). См. Биржа.

Аттестация устройства (device.attest)

{ "type": "device.attest", "chain": "{{chain}}", "from": "vel…", "nonce": 0, "ts": 1793577600000,
  "device": "e3b0c442…(64 hex = sha256 of the device public key)",
  "keyId": "base64…", "attestation": "base64 CBOR…", "sig": "…" }

Её отправляет приложение для iOS, чтобы зафиксировать, что это оригинальное приложение на настоящем iPhone. Интеграторам отправлять её не нужно.

Nonce

  • Первый nonce кошелька — 0; каждая принятая транзакция увеличивает его на 1.
  • Для следующего используй account.get → nonce + pending (pending — транзакции этого кошелька, ждущие в мемпуле узла).
  • Транзакция с уже использованным nonce отклоняется. Повторная отправка той же самой подписанной транзакции безопасна: дважды она не спишется.

Частые ошибки

Ошибка Причина
Cadena incorrecta chain не равно {{chain}}
Firma no válida Подписан неканонический JSON или JSON с sig внутри
Comisión incorrecta fee не соответствует правилу 1 000 € и цене оракула
Nonce no válido Отрицательный, нецелый или повторный
Nota demasiado larga memo длиннее 128 символов

Финальность и подтверждения {#finalidad}

Финальность пока вероятностная (см. Консенсус). Реорганизации короткие, обычно на один-два блока.

Сценарий Подтверждения Время
Чат, чаевые 1 Один блок
Магазин, платёжный шлюз 6 Несколько секунд
Депозит на бирже 30 ≈ 1 мин при целевом темпе
Крупный депозит Одна эпоха ≈ 1 ч≈ 1 мин

У транзакции n подтверждений, когда её блок находится на высоте h, а вершина цепочки на h + n − 1.

RPC узла (WebSocket) {#rpc}

Приложения и сервисы общаются с узлами JSON-кадрами поверх WebSocket:

  • Через публичный шлюз: wss://velum.consultorabim.com/hub?role=client (резервный: wss://velum.consultora-bim.workers.dev/hub).
  • Напрямую с узлом: ws://<host>:7420/ws?role=client. Бирже лучше держать собственный узел.
// Client → gateway (wss://velum.consultorabim.com/hub?role=client) or a node (ws://host:7420/ws?role=client)
type ClientToHub =
  | { t: "rpc"; node?: string; id: string; method: RpcMethod; params: any }
  | { t: "ping" };

type HubToClient =
  | { t: "welcome"; client: string; nodes: PeerInfo[] }
  | { t: "nodes"; nodes: PeerInfo[] }
  | { t: "rpc_res"; id: string; result?: any; error?: string; node?: string }
  | { t: "push"; node: string; event: "shard" | "head"; data: any }
  | { t: "pong" };

// Read-only live feed for explorers (wss://…/hub?role=watch)
type HubToWatch =
  | { t: "snapshot"; head: { height: number; hash: string } | null; nodes: PeerInfo[]; stats: Record<string, number> }
  | { t: "block"; block: Block }
  | { t: "nodes"; nodes: PeerInfo[] }
  | { t: "flow"; kind: "shard" | "receipt" | "tx"; id: string; from?: string; to?: string; ts: number };

При работе через шлюз node необязателен: если его не указать, шлюз сам выберет узел и сообщит какой в ответе.

Методы

Метод Параметры Результат
chain.head {} { height, hash, ts, node }
chain.block { height? , hash? } Block или null
chain.blocks { from, to } Block[]
account.get { address } { balance, nonce, pending } (VEL в µVEL)
account.history { address, limit? } [{ height, ts, tx }]
tx.submit { tx } { id }
| `faucet` | `{ address }` | `{ id, amount }` (только тестовая сеть) |

| shard.put | { shard } | { ok, carrier } | | mailbox.open | { mailboxes: MailboxOpen[] } | { shards, courier }, затем события push | | receipt.submit | { receipts } | { accepted } | | node.info | {} | Данные узла | | node.list | {} | Известные узлы | | stats | {} | Счётчики узла |

Пример без SDK:

const ws = new WebSocket("wss://velum.consultorabim.com/hub?role=client");
let seq = 0;
const pending = new Map<string, (r: any) => void>();
const rpc = (method: string, params: object) =>
  new Promise((resolve, reject) => {
    const id = String(++seq);
    pending.set(id, (m) => (m.error ? reject(new Error(m.error)) : resolve(m.result)));
    ws.send(JSON.stringify({ t: "rpc", id, method, params }));
  });
ws.onmessage = (e) => {
  const m = JSON.parse(e.data);
  if (m.t === "rpc_res") pending.get(m.id)?.(m), pending.delete(m.id);
};
ws.onopen = async () => {
  const head = await rpc("chain.head", {});               // { height, hash, ts, node }
  const acc = await rpc("account.get", { address: "vel…" }); // { balance, nonce, pending }
};

REST API обозревателя {#rest}

Обозреватель публикует API только для чтения с открытым CORS по адресу https://velum.consultorabim.com/api. Ответы в JSON; ошибки — { "error": "…" } с соответствующим HTTP-кодом.

Маршрут Возвращает
GET /api/stats Высота, хеш, эмиссия, Резерв, предложение, узлы, уровни и параметры
GET /api/oracle Цена сети: { eur, usd, usdt, ts, source } (если цены нет, значения null)
GET /api/blocks?limit=&before= Канонические блоки от новых к старым (макс. 100)
GET /api/block/:высота-или-хеш Подробности блока: платежи, баллы, награды
GET /api/block/:id?raw=1 Полный подписанный блок (последние 5 000)
GET /api/tx/:id Транзакция, подтверждения и подписанный JSON
GET /api/txs?type=transfer|register|all&limit=&before= Последние транзакции
GET /api/fragments?limit=&before= Квитанции о доставке по блокам
GET /api/nodes Узлы с уровнем, доставками, заработком и статусом
GET /api/address/:адрес Баланс, nonce, операции и награды
GET /api/node/:адрес То же самое для узлов
GET /api/search?q= Поиск по высоте, хешу, транзакции, адресу или имени узла
curl -s https://velum.consultorabim.com/api/stats
curl -s "https://velum.consultorabim.com/api/blocks?limit=25&before=120000"
curl -s https://velum.consultorabim.com/api/block/118204
curl -s "https://velum.consultorabim.com/api/block/118204?raw=1"   # signed block JSON
curl -s https://velum.consultorabim.com/api/tx/4c1f0a9e…
curl -s "https://velum.consultorabim.com/api/txs?type=all&limit=50"
curl -s https://velum.consultorabim.com/api/address/vel8Kx3vQ…
curl -s "https://velum.consultorabim.com/api/search?q=118204"

Для живой ленты подключись к wss://velum.consultorabim.com/hub?role=watch: придут snapshot, каждый новый block и список nodes.

API обозревателя удобен для запросов, но не является источником истины для движения средств. Бирже нужно проверять всё на собственном узле.

Депозиты и выводы на биржах {#exchanges}

Модель счетов

Два варианта, как и в других блокчейнах со счетами:

  • Отдельный адрес для каждого пользователя (рекомендуется). Выводи m/44'/7420'/0'/0'/N' для каждого клиента из холодной фразы. Не зависит от того, правильно ли пользователь ввёл memo.
  • Общий адрес + memo. Один адрес для депозитов и memo как идентификатор клиента. Если memo нет или оно неверное, депозит уходит на ручную проверку.

Отслеживание депозитов

import { CHAIN_ID } from "@velum/core";

const CONFIRMATIONS = 6;          // ≈ 12 s; see "Finality" above
let next = loadCursor();          // last processed height + 1

async function poll() {
  const head = (await rpc("chain.head", {})) as { height: number };
  while (next <= head.height - CONFIRMATIONS) {
    const block = (await rpc("chain.block", { height: next })) as Block;
    if (block.header.chain !== CHAIN_ID) throw new Error("wrong chain");
    for (const tx of block.txs) {
      if (tx.type !== "transfer" || !isOurDepositAddress(tx.to)) continue;
      const asset = tx.token ?? "VEL";
      credit({ txid: txId(tx), to: tx.to, asset, amount: tx.amount, memo: tx.memo, height: next });
    }
    saveCursor(++next);
  }
}
// Re-check the hash of the last credited blocks on every poll: if it changed, a short reorg happened.
  1. Следи за цепочкой блок за блоком через chain.head и chain.block на собственном узле.
  2. Обрабатывай только транзакции transfer на свои адреса. Актив — token, а если его нет, VEL.
  3. Зачисляй, когда у блока 30 подтверждений.
  4. Сохраняй хеш каждого обработанного блока. Если при следующем проходе он изменился, произошла реорганизация: откати и обработай заново с этого блока.
  5. Переводи средства в холодный кошелёк, когда удобно. Перемещения меньше 1 000 € идут без комиссии; более крупные стоят 0,01 %.

Выводы

import { makeTransfer, txId } from "@velum/core";

const acc = (await rpc("account.get", { address: hot.address })) as { nonce: number; pending: number };
const tx = makeTransfer(hot.secret, hot.address, userAddress, amountMicro, acc.nonce + acc.pending, { memo: withdrawalId });
const { id } = (await rpc("tx.submit", { tx })) as { id: string };   // id === txId(tx)
// Track id until it is CONFIRMATIONS blocks deep. Re-broadcasting the same signed tx is safe (same nonce).
  • Подписывай в изолированном сервисе; узел получает только уже подписанные транзакции.
  • Получай nonce через account.get (nonce + pending) и веди собственный счётчик, если отправляешь много транзакций подряд.
  • Проверяй, что адрес назначения корректен и не является служебным счётом vel_….
  • Если вывод не попал в блок за несколько блоков, отправь ту же самую подписанную транзакцию ещё раз. Не подписывай новую с другим nonce, пока не убедишься, что первая не прошла.

MetaMask Snaps {#snaps}

MetaMask работает с Ethereum, но Snaps позволяют добавлять сети, не совместимые с EVM. Snap для Velum должен:

  1. Вывести ключ ed25519. Через snap_getBip32Entropy с curve: "ed25519" (SLIP-10) по пути m/44'/7420'/0'/0'/0', тот же путь, что и в SDK. Число 7420 ожидает регистрации в SLIP-44.
  2. Сформировать адрес vel + base58 открытого ключа.
  3. Подписать канонический JSON транзакции ключом ed25519, предварительно запросив подтверждение пользователя через snap_dialog.
  4. Отправить транзакцию на узел по WebSocket (endowment:network-access) или вернуть её подписанной dapp.
{
  "version": "0.1.0",
  "proposedName": "Velum",
  "source": { "shasum": "…", "location": { "npm": { "filePath": "dist/bundle.js", "packageName": "velum-snap", "registry": "https://registry.npmjs.org/" } } },
  "initialPermissions": {
    "snap_getBip32Entropy": [{ "path": ["m", "44'", "7420'"], "curve": "ed25519" }],
    "endowment:rpc": { "dapps": true, "snaps": false },
    "snap_dialog": {},
    "endowment:network-access": {}
  },
  "manifestVersion": "0.1"
}
import type { OnRpcRequestHandler } from "@metamask/snaps-sdk";
import { confirmUi } from "./ui";                                   // renders the transfer for the user
import { ed25519 } from "@noble/curves/ed25519.js";
import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
import { base58 } from "@scure/base";
import { canonical, CHAIN_ID } from "@velum/core";

// SLIP-10 ed25519 at m/44'/7420'/0'/0'/0' — the same path the Velum SDK and hardware wallets use.
async function walletKey() {
  const node = await snap.request({
    method: "snap_getBip32Entropy",
    params: { path: ["m", "44'", "7420'", "0'", "0'", "0'"], curve: "ed25519" },
  });
  const secret = hexToBytes(node.privateKey!.slice(2));   // SLIP-10 ed25519 key, "0x…"
  const pub = ed25519.getPublicKey(secret);
  return { secret, address: "vel" + base58.encode(pub) };
}

export const onRpcRequest: OnRpcRequestHandler = async ({ request }) => {
  const k = await walletKey();
  if (request.method === "velum_getAddress") return k.address;
  if (request.method === "velum_signTransfer") {
    const { to, amount, nonce, token, memo, fee } = request.params as any;
    // fee: 0 below 1,000 € at the network oracle price (plus a small proof of work, see PROTOCOL.md), else 0.01 %
    const body = { type: "transfer", chain: CHAIN_ID, from: k.address, to, amount, fee, nonce, ts: Date.now(), token, memo };
    const ok = await snap.request({ method: "snap_dialog", params: { type: "confirmation", content: confirmUi(body) } });
    if (!ok) throw new Error("rejected");
    const sig = bytesToHex(ed25519.sign(new TextEncoder().encode(canonical(body)), k.secret));
    return { ...body, sig };
  }
  throw new Error("method not found");
};

Важно:

  • Путь тот же, но фраза — от MetaMask: аккаунт в Snap не совпадает с аккаунтом в приложении Velum, если только ты не используешь одну и ту же фразу в обоих местах.
  • Перед публикацией Snap должен пройти аудит и процедуру включения в каталог MetaMask.

Что нужно для листинга VEL на бирже {#listado}

Что обычно требует крупная биржа (Binance, Kraken, Coinbase…) для интеграции нового блокчейна и где это найти:

Требование Состояние
Узел, который биржа может запустить Запусти узел: бинарный файл без зависимостей, Linux x64/arm64
Стабильный и документированный RPC RPC узла
Публичный обозреватель Обозреватель и REST API
Спецификация формата транзакций и подписи Транзакции
Правило финальности Финальность
Открытый код узла Публичный репозиторий velum-node (готовится)
Внешний аудит безопасности Запланирован до запуска основной сети
Основная сеть velum-mainnet-1, без крана и тестовых токенов
Правовая база (MiCA в ЕС) Прорабатывается с юристами

Платёжные шлюзы {#pasarelas}

Velum подходит для приёма платежей в реальном времени: блоки за секунды и нулевая комиссия для платежей меньше 1 000 €. Всегда показывай стоимость в EUR, USD или USDT по цене оракула сети и сумму, которую получит магазин.

  1. Создавай отдельный адрес для каждого заказа (выводя m/44'/7420'/0'/0'/N') или используй постоянный адрес с ID заказа в memo.
  2. Показывай QR-код с адресом, суммой и токеном (например, {{quote}}).
  3. Следи за блоками через hub?role=watch или свой узел и отмечай заказ оплаченным после 6 подтверждений.
  4. Если принимаешь оплату в токене и хочешь рассчитываться в VEL (или наоборот), используй биржу.

SDK @velum/core {#sdk}

То же ядро, на котором работают приложение, узел и этот сайт. TypeScript без нативных зависимостей: работает в Bun, Node, браузерах, Workers и React Native.

Подключение

import { VelumClient, keysFromPhrase, generatePhrase, createDevice, createAccount } from "@velum/core";

const phrase = generatePhrase("en");                   // 24 words — store it offline
const keys = keysFromPhrase(phrase);
const account = createAccount(keys, createDevice("server-1"));

const client = new VelumClient({ account });           // defaults to the public gateways
client.on("status", (s) => console.log("network:", s));
client.connect();
await client.faucet();                                 // testnet only: 25 VEL

Отправка сообщения

client.on("message", (m) => console.log(m.payload.from, m.payload.body));
client.setContact(theirDeviceList);                    // signed DeviceList received out of band / via QR
const progress = await client.send(theirIdentity, { kind: "text", body: "Hola" });
client.on("progress", (p) => console.log(`${p.accepted}/${p.total} shards accepted`));

Платёж

import { parseAmount } from "@velum/core";

const { id } = await client.pay("vel4Jt9mR…", parseAmount("12,5")!);            // VEL (µVEL integer)
await client.pay("vel4Jt9mR…", 2_000_000, { token: "tEUR", memo: "order-981" }); // 2 tEUR, fee in VEL
const { balance, nonce } = await client.balance();

Выставление заявки

// Buy 250 VEL at 0.0125 tEUR each. price = smallest token units per 1 VEL (tEUR has 6 decimals).
await client.placeOrder({ token: "tEUR", side: "buy", price: 12_500, amount: 250_000_000, ttl: 1_440 });

Создание токена

await client.createToken({ symbol: "CAFE", name: "Café de barrio", decimals: 2, maxSupply: 100_000_000, initial: 2_500_000, mintable: true });
// Costs 100 VEL. Then reward nodes in CAFE for a day:
// makePoolFund(wallet.secret, wallet.address, nonce, "CAFE", 1_440_000, 1_440)

SDK также экспортирует конструкторы транзакций (makeTransfer, makeRegister, makeTokenCreate, makeTokenMint, makePoolFund, makeOrder, makeCancel), функции чтения состояния (supplyInfo, difficulty, bookOf, tradesOf) и форматирование сумм (formatAmount, parseAmount).