Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.vela.monolithsystematic.com/llms.txt

Use this file to discover all available pages before exploring further.

Vela uses ECDSA (secp256k1) signatures for all authentication. There are no API keys or passwords. Your Ethereum wallet is your identity, and your signatures prove you control it.

Signature Scheme

Vela uses the same elliptic curve as Ethereum: secp256k1. Signatures follow the EIP-191 personal sign standard, which prepends a standard prefix to prevent signed messages from being mistaken for Ethereum transactions:
prefixed_message = "\x19Ethereum Signed Message:\n" + len(message) + message
hash = keccak256(prefixed_message)
signature = sign(hash, private_key)
The resulting signature is a 65-byte (130 hex character) value: r (32 bytes) + s (32 bytes) + v (1 byte, recovery id: 27 or 28).

Order Signing

Every order submitted to Vela must be signed by the account’s private key. The signing payload is the canonical JSON serialization of the order parameters:
const order = {
  market_id: "ETH-USDC",
  side: "bid",
  price: "3200000000",
  quantity: "1000000",
  order_type: "limit",
  time_in_force: "gtc",
  nonce: 1
};

const message = JSON.stringify(order);
const signature = await signer.signMessage(message);
The field order in the JSON matters for signature verification. Always use the canonical field order shown above: market_id, side, price, quantity, order_type, time_in_force, nonce.
The signed order is submitted as:
{
  "order": {
    "market_id": "ETH-USDC",
    "side": "bid",
    "price": "3200000000",
    "quantity": "1000000",
    "order_type": "limit",
    "time_in_force": "gtc",
    "nonce": 1
  },
  "signature": "0x...",
  "address": "0xYourEthereumAddress"
}
The engine recovers the signer address from the signature and verifies it matches the address field. If verification fails, the order is rejected with INVALID_SIGNATURE.

WebSocket Authentication

WebSocket private channels use a challenge-response flow:
1

Request challenge

{
  "type": "request_challenge",
  "address": "0xYourAddress"
}
2

Receive challenge

{
  "type": "challenge",
  "nonce": "7f3a91c2e8d40b56"
}
3

Sign and respond

const message = `Vela Exchange\nNonce: ${challenge.nonce}`;
const signature = await signer.signMessage(message);

ws.send(JSON.stringify({
  type: "auth",
  address: "0xYourAddress",
  signature
}));

Nonce Replay Prevention

The nonce field in each order is a per-account monotonic integer. The engine tracks a nonce high-water mark per account and rejects any order with nonce ≤ high_water_mark. This prevents replay attacks: a signed order cannot be re-submitted by a third party who intercepts it, because the nonce has already been consumed. Nonce management best practices:
class NonceManager {
  constructor(startNonce) {
    this.nonce = startNonce;
  }
  
  next() {
    return this.nonce++;
  }
}

// Seed from server on startup
const { nonce_high_water } = await fetchBalances(address);
const nonces = new NonceManager(nonce_high_water + 1);

Implementation: ethers.js

import { ethers } from 'ethers';

const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();

async function signOrder(order) {
  const message = JSON.stringify(order);
  return await signer.signMessage(message);
}

async function submitOrder(order) {
  const signature = await signOrder(order);
  const address = await signer.getAddress();
  
  const response = await fetch('https://vela-engine.fly.dev/orders', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ order, signature, address })
  });
  
  return response.json();
}

Address Derivation

The engine derives the signer address from the signature using the standard Ethereum address derivation:
  1. Recover the public key from (hash, r, s, v)
  2. Take keccak256 of the uncompressed public key (64 bytes, without the 0x04 prefix)
  3. Take the last 20 bytes → Ethereum address
This is implemented using the k256 Rust crate in the engine.

Security Properties

PropertyGuarantee
AuthenticationOnly the holder of the private key can produce valid signatures
Order integrityAny modification to an order invalidates the signature
Replay preventionNonce high-water mark prevents reuse of signed orders
No key exposureThe private key never leaves the signer’s device
Standard schemeEIP-191 personal_sign — auditable and battle-tested