accueil

Premiers pas

Authentification

Sign-In With Solana pour l’API Seeryx : le message, le jeton dans X-Auth-Token ou Bearer, et la clé de session de trading qui signe vos ordres.

Vue d’ensemble

AppelsPrérequis
Données de marché : marchés, manches, carnets, transactions, événements, profilsRien
Votre compte : liquidités, positions, ordres, activitéUn jeton de connexion dans X-Auth-Token
Ordres, annulations, scissions, fusionsLe jeton et un en-tête Idempotency-Key ; sur seeryx.com, chaque ordre, scission et fusion porte aussi signed, produit par votre clé de session de trading
Dépôts, retraits, la session elle-mêmeLe jeton, et la signature de votre portefeuille sur la transaction préparée par le serveur

Connexion avec un portefeuille

Seeryx utilise Sign-In With Solana : votre portefeuille signe un court message texte, ce qui ne coûte rien et n’envoie rien sur la blockchain.

  1. POST /api/auth/nonce avec { "address": "<your wallet>" } renvoie le message. Il est valable 5 minutes et ne peut servir qu’une fois.
  2. Vérifiez que la ligne 1 nomme ce site et la ligne 2 votre adresse, puis signez les octets UTF-8 exacts du message avec la clé Ed25519 du portefeuille et encodez la signature de 64 octets en base58.
  3. POST /api/auth/verify avec address, message et signature renvoie token. La première connexion crée le compte.
Le message
seeryx.com wants you to sign in with your Solana account:
4hfqTJ6sVSszXZGCYjXEr4YuAXz9nyLut8725NP9mVvm

Sign in to Seeryx. This does not send a transaction or cost a fee.

URI: https://seeryx.com
Version: 1
Chain ID: mainnet
Nonce: a4b5fb6f571c43b4b1c5057abaa8cea2
Issued At: 2026-10-13T18:29:28.000Z
Expiration Time: 2026-10-13T18:34:28.000Z
Chain ID désigne le cluster Solana du serveur.
seeryx.ts
// seeryx.ts: a small client for the Seeryx API. Node 22+, npm i @solana/kit
import { readFileSync } from 'node:fs';
import { createKeyPairFromBytes, getAddressFromPublicKey, getBase58Decoder, signBytes } from '@solana/kit';
import type { Address } from '@solana/kit';
import { apiUrl } from './api-url.mjs';

export const BASE = process.env.SEERYX_URL ?? 'https://seeryx.com';
export const ACCESS = process.env.SEERYX_ACCESS ?? ''; // only on a site with access codes
let token = '';

export interface Round {
  slug: string; start: number; end: number; open: number | null; close: number | null;
  outcome: 'up' | 'down' | null; status: 'upcoming' | 'live' | 'settled';
}
export interface Book { bids: [price: number, shares: number][]; asks: [price: number, shares: number][] }
export interface Account {
  positions: { slug: string; side: 'up' | 'down'; shares: number; cost: number }[];
  orders: {
    id: number; slug: string; side: 'up' | 'down'; action: 'buy' | 'sell'; shares: number; limit: number;
  }[];
  balance: { available: number; total: number };
}

export class ApiError extends Error {
  constructor(
    message: string, readonly status: number, readonly code?: string, readonly retryAfter?: number,
  ) {
    super(message);
  }
}

// One request. Writes get a fresh Idempotency-Key unless you pass the key of the attempt you are retrying.
export async function api<T>(path: string, init: { method?: string; body?: unknown; key?: string } = {}) {
  const method = init.method ?? 'GET';
  const headers: Record<string, string> = { 'Content-Type': 'application/json' };
  if (token) headers['X-Auth-Token'] = token;
  if (ACCESS) headers['X-Access-Token'] = ACCESS;
  if (method !== 'GET') headers['Idempotency-Key'] = init.key ?? crypto.randomUUID();
  const body = init.body === undefined ? undefined : JSON.stringify(init.body);
  const res = await fetch(apiUrl(BASE, path), { method, headers, body, redirect: 'error' });
  const data = await res.json().catch(() => ({}));
  if (!res.ok) {
    const wait = Number(res.headers.get('Retry-After')) || undefined;
    throw new ApiError(data.error ?? `HTTP ${res.status}`, res.status, data.code, wait);
  }
  return data as T;
}

export interface Wallet { pair: CryptoKeyPair; address: Address }

// A wallet file as solana-keygen writes it: a JSON array of 64 numbers.
export async function loadWallet(path = process.env.SEERYX_WALLET ?? 'wallet.json'): Promise<Wallet> {
  const pair = await createKeyPairFromBytes(new Uint8Array(JSON.parse(readFileSync(path, 'utf8'))));
  return { pair, address: await getAddressFromPublicKey(pair.publicKey) };
}

// Sign-In With Solana: ask for the message, check it, sign its exact bytes, trade the signature for a token.
export async function signIn(wallet: Wallet) {
  const { address } = wallet;
  const { message } = await api<{ message: string }>('/auth/nonce', { method: 'POST', body: { address } });
  const [first, second] = message.split('\n');
  const site = `${new URL(BASE).hostname} wants you to sign in with your Solana account:`;
  if (first !== site || second !== address) {
    throw new Error('Unexpected sign-in message: not this site or not this wallet');
  }
  const bytes = await signBytes(wallet.pair.privateKey, new TextEncoder().encode(message));
  const signature = getBase58Decoder().decode(bytes);
  const body = { address, message, signature };
  ({ token } = await api<{ token: string }>('/auth/verify', { method: 'POST', body }));
  return token; // valid for 7 days
}

Jetons de connexion

En-tête
Envoyez le jeton sous la forme X-Auth-Token: <token> ou Authorization: Bearer <token> dans chaque requête authentifiée, et comme token dans l’abonnement au compte du WebSocket.
Durée de validité
7 jours. Reconnectez-vous avant leur expiration, ou lorsqu’un appel répond 403 (seeryx.com) ou 401 avec "Invalid or expired token".
Cookies
Le site lui-même se connecte avec un cookie HttpOnly et un jeton CSRF, que seules les pages du site peuvent utiliser. Les clients de l’API utilisent l’en-tête du jeton et n’ont besoin ni de l’un ni de l’autre.
Limites
30 requêtes par minute et par adresse IP pour chacun des endpoints /auth/nonce et /auth/verify.

La clé de session de trading

Sur seeryx.com, chaque opération est réglée sur Solana par le programme Seeryx, et le programme n’accepte que des ordres signés pour votre compte. Au lieu de solliciter le portefeuille à chaque ordre, le portefeuille approuve une seule fois une clé de session :

ChampTypeDescription
keyobligatoireclé publiqueUne nouvelle clé Ed25519 créée par votre bot. Elle signe les ordres, les scissions et les fusions.
expiresAtobligatoiresecondes UnixLe moment où la clé cesse de fonctionner : au plus maxSessionTtl de GET /api/funds à partir de maintenant (24 heures).
limitobligatoireUSDCLe montant maximal que la clé peut dépenser au total en achats et en scissions.
maxTradeobligatoireUSDCLe montant maximal qu’un ordre peut dépenser ; au plus limit.
  • Elle peut négocier, jamais retirer. La clé ne peut signer que des ordres, des scissions et des fusions pour votre compte, dans ses limites. Les retraits nécessitent toujours le portefeuille lui-même.
  • Une seule à la fois. Une nouvelle session remplace l’ancienne clé ; les ordres en attente signés par l’ancienne clé sont annulés dès que la nouvelle est utilisée.
  • Les ordres à cours limité ne doivent pas lui survivre. Un ordre en attente est refusé s’il devait rester dans le carnet au-delà de la fin de la session : ouvrez une nouvelle session avant de coter une manche qui se clôture plus tard (les manches d’un jour, par exemple).
  • Gratuite. Seeryx paie les frais de réseau et le loyer du compte de la transaction de session. Votre premier dépôt doit d’abord avoir ouvert votre compte (d’ici là : 409 "Deposit first").
  • Fermez-la plus tôt avec POST /api/relay { "action": "close_session" } : vos ordres en attente sont annulés et la clé cesse aussitôt de fonctionner.

Comment ouvrir la session et signer avec elle, octet par octet : Signature des ordres.

Déconnexion

  • POST /api/auth/logout met fin au jeton que vous envoyez ; POST /api/auth/logout-all met fin à tous les jetons du compte.
  • Aucun des deux ne met fin à la clé de session de trading : fermez-la avec close_session, ou laissez-la expirer.
curl
curl -s -X POST https://seeryx.com/api/auth/logout-all -H "X-Auth-Token: $TOKEN"
# {"success":true}

Endpoints de connexion

Connexion avec un portefeuille Solana : demandez un message, signez ses octets exacts avec la clé du portefeuille, échangez la signature contre un jeton. Rien n’est envoyé sur la blockchain et rien n’est payé.

POST/api/auth/nonce

Obtenir le message à signer

Public
ChampTypeDescription
addresscorpsobligatoirestringAdresse du portefeuille Solana (base58)
Requête
curl -X POST "https://seeryx.com/api/auth/nonce" \
  -H "Content-Type: application/json" \
  -d '{"address":"4hfqTJ6sVSszXZGCYjXEr4YuAXz9nyLut8725NP9mVvm"}'
Réponse
{
  "message": "seeryx.com wants you to sign in with your Solana account:\n4hfqTJ6sVSszXZGCYjXEr4YuAXz9nyLut8725NP9mVvm\n\nSign in to Seeryx. This does not send a transaction or cost a fee.\n\nURI: https://seeryx.com\nVersion: 1\nChain ID: mainnet\nNonce: a4b5fb6f571c43b4b1c5057abaa8cea2\nIssued At: 2026-10-13T18:29:28.000Z\nExpiration Time: 2026-10-13T18:34:28.000Z",
  "nonce": "a4b5fb6f571c43b4b1c5057abaa8cea2"
}
  • Le message est valable 5 minutes et ne peut servir qu’une fois. Chain ID désigne le cluster Solana du serveur.
StatutCas
400Ce n’est pas une adresse Solana
429Plus de 30 requêtes par minute depuis votre adresse IP
POST/api/auth/verify

Échanger le message signé contre un jeton

Public
ChampTypeDescription
addresscorpsobligatoirestringLa même adresse de portefeuille
messagecorpsobligatoirestringLe message exactement tel que reçu
signaturecorpsobligatoirestringSignature Ed25519 des octets du message (UTF-8), en base58
Requête
curl -X POST "https://seeryx.com/api/auth/verify" \
  -H "Content-Type: application/json" \
  -d '{"address":"4hfqTJ6sVSszXZGCYjXEr4YuAXz9nyLut8725NP9mVvm","message":"...","signature":"5Kq3..."}'
Réponse
{ "token": "eyJ0eXAiOiJKV1Qi...", "user": { "id": 3, "address": "4hfqTJ6sVSszXZGCYjXEr4YuAXz9nyLut8725NP9mVvm", "createdAt": "2026-10-13 18:29:28" } }
  • Le jeton est valable 7 jours. Envoyez-le dans X-Auth-Token (ou Authorization: Bearer) dans chaque requête authentifiée. La première connexion crée le compte.
StatutCas
401"Signature verification failed" ou "Invalid or expired login message"
403Ce compte est bloqué
429Plus de 30 requêtes par minute depuis votre adresse IP
GET/api/auth/me

Le compte connecté et ses liquidités

Connexion requise
Requête
curl "https://seeryx.com/api/auth/me" \
  -H "X-Auth-Token: $TOKEN"
Réponse
{ "user": { "id": 3, "address": "4hfqTJ6sVSszXZGCYjXEr4YuAXz9nyLut8725NP9mVvm", "username": null, "avatar": null },
  "balance": { "total": 474.097965, "available": 474.097965, "locked": 0, "currency": "USDC" } }
POST/api/auth/logout

Mettre fin à ce jeton de connexion

Connexion requise
Requête
curl -X POST "https://seeryx.com/api/auth/logout" \
  -H "X-Auth-Token: $TOKEN"
Réponse
{ "success": true }
  • Les autres jetons du compte continuent de fonctionner. Cela ne met pas fin à la clé de session de trading : fermez-la avec POST /api/relay close_session.
POST/api/auth/logout-all

Mettre fin à tous les jetons de connexion du compte

Connexion requise
Requête
curl -X POST "https://seeryx.com/api/auth/logout-all" \
  -H "X-Auth-Token: $TOKEN"
Réponse
{ "success": true }
  • Tous les jetons émis jusqu’ici cessent de fonctionner, celui-ci compris ; reconnectez-vous pour en obtenir un nouveau.
POST/api/auth/username

Définir ou modifier le nom d’utilisateur public

Connexion requise
ChampTypeDescription
usernamecorpsobligatoirestringDe 2 à 20 lettres, chiffres ou _ ; unique sans tenir compte de la casse
Requête
curl -X POST "https://seeryx.com/api/auth/username" \
  -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"username":"satoshi_bot"}'
Réponse
{ "success": true, "username": "satoshi_bot" }
  • Le nom apparaît dans le classement et sur votre profil public ; sans nom, ceux-ci affichent votre adresse de portefeuille abrégée.