Skip to main content

EVM Chains & Accounts

Sparky settles on EVM chains. This page explains how identity works, how you log in, and what "one deployment per chain" means for integrators.

1. Supported networks

NetworkChain IDDeploymentNotes
Avalanche C-Chain43114MainnetPrimary settlement chain
Arbitrum One42161Mainnet
BNB Chain56Mainnet
Avalanche Fuji43113TestnetTest USDT, no real value
Arbitrum Sepolia421614TestnetTest USDT, no real value

Each network is served by a separate backend process with its own base URL (see Base URLs). The chain a deployment settles on is fixed by its CHAIN_ID / RPC_URL configuration and is echoed in the EIP-712 domain returned by GET /api/v1/auth/nonce/{address}.

2. Account identity = EOA address

Your account on Sparky is your wallet address. There is no separate user id:

  • The backend keys balances, positions, orders, API keys, referral data and points by the lowercase EOA address.
  • accountAlias on /fapi/v2/balance is always "default" — one wallet address maps to exactly one account.
  • Both trading engines (Orderly gateway and Sparky native) use the same EOA; the Orderly side derives its account_id from the address, the native side authenticates it directly.

Per-chain isolation

Because every chain is a separate deployment:

  • Balances are per chain. Depositing into the Avalanche Vault credits only your Avalanche account.
  • Positions and orders are per chain. There is no cross-chain margin.
  • API keys are per chain. Create keys on each deployment you trade on (max 30 per account per deployment).
  • Referral codes, points and Earn subscriptions are per chain.

Use the same private key on every chain if you want a single identity; the backend never links deployments together.

3. Login (EIP-712 → JWT)

The native /api/v1/* surface uses a JWT obtained by signing an EIP-712 typed message. The /fapi/* surface does not use JWTs — it uses API keys created with a JWT (see API Keys).

Client ──① GET /api/v1/auth/nonce/{address} ──▶ nonce + typed_data
──② sign typed_data with the wallet (off-chain)
──③ POST /api/v1/auth/login {address, signature, timestamp} ──▶ { token, expires_at }

3.1 Get nonce and typed data

GET /api/v1/auth/nonce/{address}
{
"nonce": 1,
"typed_data": {
"types": {
"EIP712Domain": [
{ "name": "name", "type": "string" },
{ "name": "version", "type": "string" },
{ "name": "chainId", "type": "uint256" },
{ "name": "verifyingContract", "type": "address" }
],
"Login": [
{ "name": "wallet", "type": "address" },
{ "name": "nonce", "type": "uint256" },
{ "name": "timestamp", "type": "uint256" }
]
},
"domain": {
"name": "AXBlade",
"version": "1",
"chainId": 43114,
"verifyingContract": "0x0000000000000000000000000000000000000000"
},
"primaryType": "Login",
"message": {
"wallet": "0xYourWalletAddress",
"nonce": 1,
"timestamp": 1700000000
}
}
}

The domain.name is a legacy identifier kept for signature compatibility; always take the domain from this response rather than hard-coding it. chainId is the deployment's chain.

3.2 Sign

// ethers.js v6
const { types, domain, message } = typed_data;
const { EIP712Domain, ...signTypes } = types; // ethers adds the domain type itself
const signature = await signer.signTypedData(domain, signTypes, message);

3.3 Exchange for a JWT

POST /api/v1/auth/login
Content-Type: application/json
FieldTypeRequiredNotes
addressstringyesWallet address
signaturestringyes0x-prefixed EIP-712 signature
timestampnumberyesMust equal typed_data.message.timestamp (Unix seconds); rejected with TIMESTAMP_EXPIRED if more than 300 s from server time
{ "token": "eyJhbGciOiJIUzI1NiIs...", "expires_at": 1700086400 }

Send it as Authorization: Bearer <token> on every /api/v1/* call. Re-login when expires_at passes.

3.4 Privy embedded wallets

The Sparky web app uses a Privy-based product model: users sign in with email, Google or an external wallet, Privy provisions an embedded EOA, and the front end exchanges the Privy access token for a Sparky JWT via POST /api/v1/auth/privy-login. For those sessions, order placement, cancellation, TP/SL and position close are authorised by the JWT alone — the signature field on those request bodies is a legacy/API-compatibility field that Privy sessions no longer rely on.

Programmatic integrators normally skip Privy and use the EIP-712 flow above, then create API keys for /fapi.

4. Which surface to use

You are…UseAuth
A bot / market maker / SDK user/fapi/v1/*, /fapi/v2/*, /futures/data/*X-MBX-APIKEY + HMAC-SHA256
A front end or script that needs deposits/withdrawals, referral, points or Earn/api/v1/*Authorization: Bearer <JWT>
Reading public market dataeither /fapi/v1/* or /api/v1/markets/*none

API keys cannot withdraw. Withdrawals, referral actions and Earn subscriptions always require a wallet-derived JWT.