Authentication
Every mutating API call requires an ECDSA signature. Read-only endpoints are unauthenticated.
Signature Scheme
Vela uses secp256k1 ECDSA — the same elliptic curve used by Ethereum. Signatures are produced using the personal_sign method (EIP-191), which prepends the Ethereum message prefix before hashing, ensuring signatures cannot be confused with raw transaction signatures.
Signing an Order
The order parameters are serialized to a canonical byte string in the following format:
{market_id}:{side}:{price}:{quantity}:{order_type}:{time_in_force}:{user}:{nonce}For example:
"ETH-USDC:bid:3200000000:1000000:limit:gtc:0xAbCd...1234:42"
This string is passed to personal_sign. The resulting 65-byte signature (r, s, v) is hex-encoded and included in the request body as the signature field.
Nonce
Each order includes a nonce — a monotonically increasing integer unique to your account. The engine rejects any order whose nonce has already been used. Start at 1 and increment by 1 for each order. There is no server-side nonce endpoint; you manage your own nonce counter.
Address Recovery
When the engine receives a signed order, it recovers the signer address from the signature and verifies it matches the user field. If they do not match, the order is rejected with error code E_SIG_MISMATCH.
WebSocket Authentication
WebSocket connections for private channels use a separate challenge-response flow. See Private L3 Feeds for the full authentication sequence.
Example (JavaScript / ethers.js)
import { ethers } from 'ethers'
const provider = new ethers.BrowserProvider(window.ethereum)
const signer = await provider.getSigner()
const message = [
'ETH-USDC', 'bid', '3200000000', '1000000',
'limit', 'gtc', await signer.getAddress(), '42'
].join(':')
const signature = await signer.signMessage(message)
const response = await fetch('https://vela-engine.fly.dev/orders', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
market_id: 'ETH-USDC',
side: 'bid',
price: '3200000000',
quantity: '1000000',
order_type: 'limit',
time_in_force: 'gtc',
user: await signer.getAddress(),
nonce: 42,
signature,
}),
})