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
| Network | Chain ID | Deployment | Notes |
|---|---|---|---|
| Avalanche C-Chain | 43114 | Mainnet | Primary settlement chain |
| Arbitrum One | 42161 | Mainnet | |
| BNB Chain | 56 | Mainnet | |
| Avalanche Fuji | 43113 | Testnet | Test USDT, no real value |
| Arbitrum Sepolia | 421614 | Testnet | Test 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.
accountAliason/fapi/v2/balanceis 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_idfrom 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
| Field | Type | Required | Notes |
|---|---|---|---|
address | string | yes | Wallet address |
signature | string | yes | 0x-prefixed EIP-712 signature |
timestamp | number | yes | Must 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… | Use | Auth |
|---|---|---|
| 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 data | either /fapi/v1/* or /api/v1/markets/* | none |
API keys cannot withdraw. Withdrawals, referral actions and Earn subscriptions always require a wallet-derived JWT.