The CCXT exchange class for Sidepit. Canonical copies live here; the working ccxt
checkout (branch sidepit-integration in a ccxt/ccxt clone) carries the same files at:
| Here | In the ccxt repo |
|---|---|
sidepit.ts |
ts/src/sidepit.ts |
pro/sidepit.ts |
ts/src/pro/sidepit.ts (WebSocket / ccxt.pro) |
abstract/sidepit.ts |
ts/src/abstract/sidepit.ts (generated-style implicit API) |
smoke.mjs |
smoke test, one PASS/FAIL line per CCXT method |
Registration (4 lines) goes in ts/ccxt.ts: REST import + exchanges map entry +
pro import + pro map entry, alphabetical (poloniex … sidepit … tokocrypto).
- Reads: plain HTTP/WS against the public gateway (
python-client/facade/in this repo). Account reads are address-parameterized; no credentials transit anywhere. - Writes: the TS class itself serializes the
Transactionprotobuf (hand-rolled writer, ~60 lines) and signs — SHA256 over the exact bytes → secp256k1 ECDSA compact (low-s, via CCXT's bundled noble-curves) → hex,signature_version=0— then POSTs the serializedSignedTransactiontoPOST /relay. The gateway holds no keys. - Credentials:
apiKey= the account'sbc1q…address (sidepit_id);secret= the 64-hex secp256k1 private key of the signing key. Delegate (hot/cold) mode: secret = the hot key,options.traderId= the hot key's address, apiKey stays the custody address. - Byte-exactness is load-bearing: the TS serializer is verified byte-for-byte
against the Python SDK (
sidepit_trader/signer.py) including ns timestamps beyond 2^53 (string math viaPrecise, no BigInt — transpiler-safe), proto3 default-omission (signature_version=0emits nothing), and sint32 zigzag forside.
- Nothing resolves on submission. DLOB one-second deterministic auctions;
createOrderreturnsstatus: 'open'optimistically with the deterministic id{sidepit_id}:{timestamp_ns}(strictly increasing ns nonce); outcomes are observational viafetchOrder/fetchOpenOrders/watchOrders/fetchRejections. - Native IOC market orders (
has.createMarketOrder = true): omit price;NewOrder.price=0takes available opposite liquidity and cancels every unfilled remainder in that auction. - Prices: native sats-per-USD → unified BTC-per-USD (
× 1e-8), converted exactly once (satsToPrice/priceToSats). Markets are explicitUSD/BTC:BTC-<expiry>, inverse dated forwards. - Balance basis:
free= serveravailable_margin;available_balance(yesterday's settled figure, static intraday) is info-only.entryPricecarries the resets-daily-at-settlement caveat. - RejectCodes map by NAME (
exceptions.exact):RC_MARGIN → InsufficientFunds,RC_DUP → InvalidNonce,RC_VERIFY/RC_ID → AuthenticationError,RC_CDUP/RC_CREJ → OrderNotFound(expected outcomes in cancel-heavy flows — seeoptions.rejectCodesfor the expected flags).
# in the ccxt clone (branch sidepit-integration)
npm run tsBuild # TS -> JS
npx tsx build/transpile.ts sidepit # -> python/ccxt/sidepit.py, php/sidepit.php
npx tsx build/transpileWS.ts sidepit # -> python/ccxt/pro/sidepit.py, php/pro/
# gateway (this repo, python-client/):
python -m facade.server
# smoke: state-adaptive — closed-hours expectations vs full live cycle when OPEN
node integrations/ccxt/smoke.mjsTranspiler constraints honored (regex-based): no for…of, no floating comments between
methods, no blank lines inside methods, no return new X() (use
throwExactlyMatchedException), no BigInt.
The smoke run covers closed-hours reads and, with an explicitly funded identity, the full client-signed relay cycle: createOrder → observed outcome → cancelOrder → observed cancellation, plus watchTicker over WS. It also verifies offline that the native market wire omits the protobuf price field. Not yet submitted upstream—that is a later step.