pagina iniziale

Per iniziare

Guida rapida

Da zero al primo ordine su Seeryx: leggi un round in corso e il suo libro ordini, accedi con un wallet Solana, inserisci un ordine e annullalo.

Leggi un mercato

I dati di mercato sono pubblici. Chiedi il round BTC da 5 minuti in corso, poi il suo libro ordini e le sue ultime operazioni.

quickstart.mjs
// quickstart.mjs: the live BTC 5-minute round, its order book and its last trades. node quickstart.mjs
import { apiUrl } from './api-url.mjs';
const BASE = 'https://seeryx.com';
const get = async (path) => (await fetch(apiUrl(BASE, path), { redirect: 'error' })).json();

const round = await get('/markets/BTC/5m/current');
const up = await get(`/orderbook/${round.slug}?side=up`);
const trades = await get(`/trades/${round.slug}`);

const bid = up.bids[0]?.[0], ask = up.asks[0]?.[0]; // levels are [cents, shares], best first
console.log({ round: round.slug, openedAt: Number(round.open), ends: new Date(round.end * 1000).toISOString() });
const chance = bid && ask ? `${(bid + ask) / 2}%` : 'no quotes yet'; // the middle of bid and ask
console.log({ upBid: bid ?? '-', ask: ask ?? '-', chanceOfUp: chance });
console.log({ lastTrade: trades[0] ?? 'none yet' });
Risposta
{ "slug": "btc-updown-5m-1791916200", "asset": "BTC", "tf": "5m", "start": 1791916200, "end": 1791916500,
  "open": 83082.005, "close": null, "outcome": null, "status": "live" }

{ "bids": [[40, 100], [39, 100]], "asks": [[49, 100], [50, 100]] }

[{ "id": 13, "side": "down", "action": "buy", "shares": 10, "price": 53, "time": 1791916330063 }]
Il round, il libro ordini di Su come livelli [centesimi, azioni] a partire dal migliore, e le ultime operazioni.

Prepara un progetto

mkdir seeryx-bot && cd seeryx-bot
npm init -y && npm pkg set type=module
npm i @solana/kit && npm i -D tsx
# a wallet for the bot alone: a JSON array of 64 numbers, as solana-keygen writes it
solana-keygen new -o wallet.json && chmod 600 wallet.json

Prima di fare trading, deposita un po’ di USDC nel conto Seeryx di quel wallet: sul sito (collega lo stesso wallet) o dal codice, vedi Depositi e prelievi. Al wallet non servono SOL.

Accedi

Salva il client qui sotto come seeryx.ts (o seeryx.py), con api-url.mjs accanto per TypeScript. Accede con il wallet e invia il token con ogni richiesta; Autenticazione spiega ogni passaggio.

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
}
JavaScriptapi-url.mjs
// Keep server-supplied identifiers inside the configured API. Never follow redirects with account headers.
export function apiUrl(base, path) {
  const origin = new URL(base);
  if (!['http:', 'https:'].includes(origin.protocol) || origin.username || origin.password || origin.pathname !== '/' || origin.search || origin.hash) {
    throw new Error('API base must be an HTTP(S) origin without credentials');
  }
  const pathname = path.split('?')[0];
  if (!/^\/[A-Za-z0-9_-]+(?:\/[A-Za-z0-9_-]+)*\/?$/.test(pathname) || /[\u0000-\u0020\u007f#\\]/.test(path)) {
    throw new Error('Invalid API path');
  }
  const url = new URL(`/api${path}`, origin);
  if (url.origin !== origin.origin || !url.pathname.startsWith('/api/')) throw new Error('Invalid API destination');
  return url;
}

// JSON escapes terminal controls; explicitly remove log entry separators as well.
export const logValue = (value) => JSON.stringify(value)?.replace(/[\n\r]/g, '_').replace(/[\u2028\u2029]/g, '_');
Mantiene ogni richiesta all’interno dell’API ed esegue l’escape dei valori del server prima di scriverli nei log.

Inserisci un ordine

Su seeryx.com gli ordini sono firmati da una chiave di sessione di trading. Salva chain.ts e message.ts (o chain.py e message.py) da Firma degli ordini, poi esegui questo codice: apre una sessione (una firma del wallet, nessun SOL), lascia in attesa un acquisto di 5 azioni sotto il mercato e lo annulla.

first-order.ts
// first-order.ts: sign in, rest one small buy order below the market, then cancel it. npx tsx first-order.ts
import { api, loadWallet, signIn, type Book, type Round } from './seeryx';
import { signOrder, tradingSession, type Funds, type Order } from './chain';

const wallet = await loadWallet();
await signIn(wallet);
const funds = await api<Funds>('/funds');
// one wallet signature opens a trading session; its key then signs every order
const session = funds.onChainTrading ? await tradingSession(funds, wallet) : null;

const round = await api<Round>('/markets/BTC/5m/current');
const book = await api<Book>(`/orderbook/${round.slug}?side=up`);
const bid = book.bids[0]?.[0] ?? 10; // the best Up bid in cents
// 5 Up shares 5c under the best bid: it rests in the book (postOnly makes sure it never trades at once)
const order: Order = {
  slug: round.slug, side: 'up', action: 'buy', type: 'limit', shares: 5, limit: Math.max(1, Math.floor(bid) - 5), postOnly: true,
};
const body = session ? await signOrder(funds, session, wallet.address, order) : order;
const placed = await api<{ order: number | null }>('/orders', { method: 'POST', body });
console.log('resting order', placed.order, 'at', order.limit, 'c');

console.log((await api<{ orders: { id: number }[] }>('/account')).orders);
if (placed.order) console.log(await api(`/orders/${placed.order}`, { method: 'DELETE' })); // { success: true }

Annullalo

curl
# one order
curl -s -X DELETE https://seeryx.com/api/orders/65365 -H "X-Auth-Token: $TOKEN" -H "Idempotency-Key: $(uuidgen)"
# {"success":true}

# everything still resting in one round
curl -s -X DELETE "https://seeryx.com/api/orders?slug=btc-updown-5m-1791916200" -H "X-Auth-Token: $TOKEN" -H "Idempotency-Key: $(uuidgen)"
# {"success":true,"cancelled":[65366]}

Prossimi passi