Carteras y exchanges
Esta página es para quien integra Velum: carteras, exchanges, pasarelas de pago y aplicaciones. Velum no es compatible con EVM: tiene su propio formato de direcciones, de transacciones y de firma. Todo lo necesario está aquí.
Resumen técnico
| Parámetro | Valor |
|---|---|
| Cadena | {{chain}} (campo chain de cada transacción) |
| Moneda nativa | VEL, 6 decimales (1 VEL = 1.000.000 µVEL) |
| Importes | Enteros en la unidad mínima del token |
| Tiempo de bloque | Objetivo 2 s (ritmo real medido en el explorador) · época: 1.800 bloques (≈ 1 h)30 bloques (≈ 60 s) |
| Curva y firma | ed25519 sobre JSON canónico |
| Dirección | vel + base58(clave pública ed25519 de 32 B) |
| Derivación de cartera | BIP-39 (24 palabras) + SLIP-10 ed25519 en m/44'/7420'/cuenta'/0'/índice' |
| Comisión de pago | 0 por debajo de 1.000 € de valor (con prueba de trabajo); 0,01 % desde 1.000 € |
| Precio | Oráculo propio: TWAP de 1 h de {{pair}} |
| Orden de transacciones | nonce por cartera, empieza en 0 |
| Id de transacción | 48 primeros hex de SHA-256(JSON canónico con sig) |
| Confirmaciones recomendadas | 6 (pagos), 30 (depósitos en exchanges) |
| Nodo | RPC JSON sobre WebSocket |
| Explorador | API REST de solo lectura en /api |
Direcciones {#direcciones}
Una dirección es el prefijo vel seguido de la clave pública ed25519 (32 bytes) en base58 (alfabeto de Bitcoin). Mide entre 46 y 47 caracteres.
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; }
})();
- No lleva suma de control. Valida siempre decodificando y comprobando que salen exactamente 32 bytes. Para direcciones introducidas a mano, recomienda copiar y pegar o escanear un QR.
- Las direcciones que empiezan por
vel_son cuentas especiales sin clave (vel_pool,vel_reserve,vel_escrow). No aceptes retiradas hacia ellas. - Los usuarios pueden compartir un alias (
@lucia) en lugar de la dirección. Resuélvelo siempre contra la cadena y enseña la dirección resultante antes de enviar.
Derivación de claves {#claves}
Una frase BIP-39 de 24 palabras genera las carteras con SLIP-10 ed25519, todos los niveles endurecidos:
| Dato | Valor |
|---|---|
| Ruta | m/44'/7420'/cuenta'/0'/índice' |
| Cartera de la app oficial | m/44'/7420'/0'/0'/0' |
| Moneda SLIP-44 | 7420 (pendiente de registrar) |
Las claves de identidad y buzón de la mensajería salen de la misma semilla con HKDF-SHA512 (velum/v2/identity, velum/v2/mailbox) y no son de cartera: un exchange o una cartera no las necesita.
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);
Con el SDK:
import { walletFromPhrase, isAddress } from "@velum/sdk";
const { address, secret } = walletFromPhrase(phrase, 0, 0);
isAddress(address); // true: "vel" + base58(32-byte ed25519 public key)
Es la misma ruta que usan el SDK
@velum/sdk, las carteras físicas (Trezor, Ledger) y el Snap de MetaMask. Ver Ecosistema.
Transacciones {#transacciones}
Todas las transacciones comparten estos campos:
| Campo | Tipo | Significado |
|---|---|---|
type |
string | Tipo de transacción |
chain |
string | {{chain}}. Si no coincide, se rechaza. |
from |
dirección | Cartera que firma |
nonce |
entero | Nº de transacciones previas de from (0, 1, 2…) |
ts |
entero (ms) | Hora del cliente. Informativa: la cadena usa la hora del bloque. |
sig |
hex (128) | Firma ed25519 de la cartera |
Firma y JSON canónico
// 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)
- Construye el objeto sin
sig. Omite los campos opcionales que no uses (no los pongas anull). - Serializa en JSON canónico: claves ordenadas, sin espacios.
- Firma los bytes UTF-8 con la clave de cartera.
sig= firma en hex. - El id es
sha256(canónico con sig)en hex, recortado a 48 caracteres.
Pago (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)"
}
amounten unidades mínimas del token (µVEL si no haytoken). Mayor que 0.feesigue la regla de v3: 0 si el valor del pago, al precio del oráculo de la red, es menor de 1.000 €; si no, el 0,01 % del importe. Los pagos confee0 llevan un sello de trabajopow(SHA-512, unos 50 ms en un móvil) que forma parte de lo firmado;buildTransferde@velum/sdklo calcula. El formato exacto está enPROTOCOL.mdy enspec/vectors/.tokenopcional: símbolo del token. Sin él, se envía VEL.memoopcional, hasta 128 caracteres. Es público: úsalo para referencias de pago, nunca para datos personales.
Alta de nodo (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": "…"
}
Gratis. kind es full (ordenador, produce bloques) o light (móvil). payout es la cartera que cobra.
Tokens (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": "…" }
Ver Tokens propios para las reglas de cada campo.
Mercado (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 en unidades mínimas del token por 1 VEL; amount en µVEL (mínimo 1 VEL). Ver Mercado.
Atestación de dispositivo (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": "…" }
La envía la app de iOS para registrar que es la app original en un iPhone real. Los integradores no necesitan enviarla.
Nonces
- El primer
noncede una cartera es 0; cada transacción aceptada lo sube en 1. - Para el siguiente, usa
account.get→nonce + pending(pending= transacciones de esa cartera que esperan en el mempool del nodo). - Una transacción con un
nonceya usado se rechaza. Reenviar la misma transacción firmada es seguro: no se cobra dos veces.
Errores habituales
| Error | Causa |
|---|---|
Cadena incorrecta |
chain no es {{chain}} |
Firma no válida |
Se firmó un JSON no canónico, o con sig dentro |
Comisión incorrecta |
fee no cuadra con la regla de 1.000 € y el precio del oráculo |
Nonce no válido |
Negativo, no entero o repetido |
Nota demasiado larga |
memo de más de 128 caracteres |
Finalidad y confirmaciones {#finalidad}
La finalidad es probabilística por ahora (ver Consenso). Las reorganizaciones son cortas, normalmente de uno o dos bloques.
| Caso | Confirmaciones | Tiempo |
|---|---|---|
| Chat, propinas | 1 | Un bloque |
| Comercio, pasarela de pago | 6 | Unos segundos |
| Depósito en exchange | 30 | ≈ 1 min con el ritmo objetivo |
| Depósito grande | Una época | ≈ 1 h≈ 1 min |
Una transacción tiene n confirmaciones cuando su bloque tiene la altura h y la cabeza de la cadena está en h + n − 1.
RPC del nodo (WebSocket) {#rpc}
Las apps y los servicios hablan con los nodos con tramas JSON sobre WebSocket:
- A través de la puerta pública:
wss://velum.consultorabim.com/hub?role=client(de respaldo:wss://velum.consultora-bim.workers.dev/hub). - Directamente con un nodo:
ws://<host>:7420/ws?role=client. Para un exchange, lo recomendable es tener tu propio nodo.
// 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 };
Por la puerta, node es opcional: si no lo pones, la puerta elige un nodo y te dice cuál en la respuesta.
Métodos
| Método | Parámetros | Resultado |
|---|---|---|
chain.head |
{} |
{ height, hash, ts, node } |
chain.block |
{ height? , hash? } |
Block o null |
chain.blocks |
{ from, to } |
Block[] |
account.get |
{ address } |
{ balance, nonce, pending } (VEL en µVEL) |
account.history |
{ address, limit? } |
[{ height, ts, tx }] |
tx.submit |
{ tx } |
{ id } |
| shard.put | { shard } | { ok, carrier } |
| mailbox.open | { mailboxes: MailboxOpen[] } | { shards, courier } y después eventos push |
| receipt.submit | { receipts } | { accepted } |
| node.info | {} | Datos del nodo |
| node.list | {} | Nodos conocidos |
| stats | {} | Contadores del nodo |
Ejemplo sin 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 }
};
API REST del explorador {#rest}
El explorador publica una API de solo lectura, con CORS abierto, en https://velum.consultorabim.com/api. Las respuestas son JSON; los errores, { "error": "…" } con su código HTTP.
| Ruta | Devuelve |
|---|---|
GET /api/stats |
Altura, hash, emisión, Reserva, oferta, nodos, niveles y parámetros |
GET /api/oracle |
Precio de la red: { eur, usd, usdt, ts, source } (sin precio: valores null) |
GET /api/blocks?limit=&before= |
Bloques canónicos, del más nuevo al más viejo (máx. 100) |
GET /api/block/:altura-o-hash |
Detalle del bloque: pagos, créditos, recompensas |
GET /api/block/:id?raw=1 |
El bloque firmado completo (últimos 5.000) |
GET /api/tx/:id |
Transacción, confirmaciones y JSON firmado |
GET /api/txs?type=transfer|register|all&limit=&before= |
Últimas transacciones |
GET /api/fragments?limit=&before= |
Recibos de entrega por bloque |
GET /api/nodes |
Nodos con nivel, entregas, ganancias y estado |
GET /api/address/:dirección |
Saldo, nonce, movimientos y recompensas |
GET /api/node/:dirección |
Igual que la anterior, para nodos |
GET /api/search?q= |
Busca altura, hash, transacción, dirección o nombre de nodo |
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"
Para un feed en vivo, conecta a wss://velum.consultorabim.com/hub?role=watch: recibirás snapshot, cada block nuevo y la lista de nodes.
La API del explorador es cómoda para consultar, pero no es una fuente de verdad para mover fondos. Un exchange debe verificar con su propio nodo.
Depósitos y retiradas en exchanges {#exchanges}
Modelo de cuentas
Dos opciones, como en otras cadenas de cuentas:
- Una dirección por usuario (recomendado). Deriva
m/44'/7420'/0'/0'/N'por cliente desde una frase fría. No depende de que el usuario escriba bien un memo. - Dirección compartida + memo. Una sola dirección de depósito y el
memocomo referencia del cliente. Si el memo falta o está mal, el depósito queda en revisión manual.
Detectar depósitos
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.
- Sigue la cadena bloque a bloque con
chain.headychain.blocken tu propio nodo. - Procesa solo transacciones
transferhacia tus direcciones. El activo estokeno, si no hay, VEL. - Acredita cuando el bloque tiene 30 confirmaciones.
- Guarda el hash de cada bloque procesado. Si en la siguiente pasada ha cambiado, hubo una reorganización: deshaz y vuelve a procesar desde ese bloque.
- Barre los fondos a la cartera fría cuando convenga. Los movimientos de menos de 1.000 € no pagan comisión; los mayores, el 0,01 %.
Retiradas
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).
- Firma en un servicio aislado; el nodo solo recibe transacciones ya firmadas.
- Pide el
nonceconaccount.get(nonce + pending) y lleva tu propio contador si envías muchas seguidas. - Comprueba que la dirección de destino es válida y no es una cuenta especial
vel_…. - Si una retirada no entra en unos bloques, reenvía la misma transacción firmada. No firmes otra con otro
noncehasta saber que la primera no entró.
MetaMask Snaps {#snaps}
MetaMask habla Ethereum, pero los Snaps permiten añadir cadenas que no son EVM. Un Snap de Velum necesita:
- Derivar la clave ed25519. Con
snap_getBip32Entropyycurve: "ed25519"(SLIP-10) enm/44'/7420'/0'/0'/0', la misma ruta que el SDK. El 7420 está pendiente de registrar en SLIP-44. - Formar la dirección
vel+ base58 de la clave pública. - Firmar el JSON canónico de la transacción con ed25519 tras pedir confirmación al usuario con
snap_dialog. - Enviar la transacción a un nodo por WebSocket (
endowment:network-access) o devolverla firmada a la 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");
};
Importante:
- La ruta es la misma, pero la frase es la de MetaMask: la cuenta del Snap no coincide con la de tu app de Velum salvo que uses la misma frase en los dos sitios.
- Antes de publicarlo, el Snap debe pasar la auditoría y el proceso de inclusión de MetaMask.
Lista para listar VEL en un exchange {#listado}
Lo que suele pedir un exchange grande (Binance, Kraken, Coinbase…) para integrar una cadena nueva, y dónde está cada cosa:
| Requisito | Estado |
|---|---|
| Nodo que el exchange pueda ejecutar | Monta un nodo: binario sin dependencias, Linux x64/arm64 |
| RPC estable y documentado | RPC del nodo |
| Explorador público | Explorador y API REST |
| Especificación del formato de transacción y firma | Transacciones |
| Regla de finalidad | Finalidad |
| Código abierto del nodo | Repositorio público velum-node (en preparación) |
| Auditoría externa de seguridad | Pendiente, antes de la red principal |
| Red principal | velum-mainnet-1, sin grifo ni tokens de prueba |
| Marco legal (MiCA en la UE) | En estudio con asesoría legal |
Pasarelas de pago {#pasarelas}
Velum sirve para cobrar en tiempo real: bloques en segundos y comisión 0 en pagos de menos de 1.000 €. Muestra siempre el valor en EUR, USD o USDT con el precio del oráculo de la red y lo que recibe el comercio.
- Genera una dirección por pedido (derivando
m/44'/7420'/0'/0'/N'), o usa una dirección fija con el id del pedido en elmemo. - Muestra un QR con la dirección, el importe y el token (por ejemplo,
{{quote}}). - Escucha los bloques por
hub?role=watcho por tu nodo y marca el pedido como pagado con 6 confirmaciones. - Si cobras en un token y quieres liquidar en VEL (o al revés), usa el mercado.
SDK @velum/core {#sdk}
El mismo núcleo que usan la app, el nodo y esta web. TypeScript, sin dependencias nativas: funciona en Bun, Node, navegadores, Workers y React Native.
Conectar
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
Enviar un mensaje
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`));
Pagar
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();
Poner una orden
// 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 });
Crear un token
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)
El SDK también exporta los constructores de transacciones (makeTransfer, makeRegister, makeTokenCreate, makeTokenMint, makePoolFund, makeOrder, makeCancel), las lecturas sobre el estado (supplyInfo, difficulty, bookOf, tradesOf) y los formatos de importe (formatAmount, parseAmount).