Skip to content

gswap-sdk v2: Chain Gateway writes, native signing, /v2 reads (1.0.0-rc.1) - #17

Closed
shoeman22 wants to merge 29 commits into
GalaChain:mainfrom
shoeman22:v2/rc1
Closed

shoeman22 wants to merge 29 commits into
GalaChain:mainfrom
shoeman22:v2/rc1

Conversation

@shoeman22

@shoeman22 shoeman22 commented Sep 3, 2026 •

Copy link
Copy Markdown

Summary

Moves @gala-chain/gswap-sdk from the legacy stack (dexv3 reads, bundler writes, EIP-712 signing — all of which stopped working when legacy became exit-only on 2026-09-01) onto the current GalaChainDex contract. Publishes as 1.0.0-rc.1 (breaking).

Writes go through the Chain Gateway

  • POST {dexBackend}/v1/chain/asset/dex-contract/{Trade|AddLiquidity|RemoveLiquidity|CollectPositionFees|CreatePool} with the signed v2 DTO; gateway bounces map to GSwapSDKError codes (METHOD_NOT_ALLOWED, SIGNATURE_INVALID, SIGNER_MISMATCH, DTO_INVALID, BOUNDS_VIOLATION, SYMBOL_CONFLICT, RATE_LIMITED, CHAIN_DISPATCH_FAILED).
  • Transactions are synchronous: SubmittedTransaction { transactionId, blockNumber, uniqueKey, result, confirm() }. transactionId and blockNumber come from the gateway's x-transaction-id / x-block-number response headers (body fields as fallback); confirm() resolves a Trade against GET /explore/transaction?uniqueKey= and a liquidity write against the position read, and always waits for the indexed row.
  • A gateway request timeout surfaces as SUBMISSION_OUTCOME_UNKNOWN (confirm before retrying), distinct from a pre-submit failure.
  • Bundler client, socket event client, PendingTransaction, stringsInstructions, positionId, zeroForOne/sqrtPriceLimit, class-key token0/token1 in write DTOs: deleted, no shims.

Signing — one scheme per signer type, EIP-712 retired

  • PrivateKeySigner: GalaChain-native (@gala-chain/api signatures.getSignature, bare 130-hex) — resolves the registered client| alias.
  • GalaWalletSigner: gala_signChainDto(serializedDto, address, method, 'native') (wallet ≥ 001b9c0), personal-sign fallback for older wallets.
  • BrowserWalletSigner (MetaMask / any EIP-1193): personal-sign with calculatePersonalSignPrefix — resolves the bare eth| identity (documented).
  • Never sends signerPublicKey / signerAddress.

v2 DTOs and reads — everything through the swap backend

  • Symbols instead of class keys (resolved and canonically ordered via GET /v2/trade/symbols); fee tiers 0|500|3000|10000; spacing-aligned ticks (src/utils/ticks.ts mirrors gc-math, Decimal-backed and fixture-checked against the backend at the tick boundaries).
  • Quotes, pools, slot0, composite pools, positions and estimates from /v2/trade/*. The SDK never calls a GalaChain gateway directly; gatewayBaseUrl and the raw chain-read client are gone.
  • Financial inputs are strings/BigNumber only — a JavaScript number is rejected before anything is signed.

Review rounds (both adversarial reviews addressed on this branch)

  • Round 1 (6933a80): tick-math parity with the backend Decimal fixtures, numeric-amount rejection, symbol-cache refresh after a miss and after createPool, request timeouts + abort, signer address normalisation, single-read of error bodies, DTO XOR types, result shapes, liquidity confirmation by position re-read, pending classification (missing route / Retry-After / backoff), package entrypoint smoke, response validation, pagination caps, malformed signatures, npm audit --omit=dev (SECURITY-NOTES.md).
  • Round 2 (62c1314 … d44f0ab): large-tick math no longer hangs, process.env option types accept explicit undefined, backend pool reads are typed and bounded, quote wire shape validated, fake example liquidity page removed and the swap example derives amountOutMinimum from the quote, legacy result types deleted, versioned User-Agent, browser-safe UUID, exact dependency pins.
  • 8666af3: confirm() keeps polling until the trade is indexed instead of returning null when the headers are already known.

Quality gates (defi-backend parity)

  • Strict TS (noUncheckedIndexedAccess, noPropertyAccessFromIndexSignature, noImplicitReturns, noUnused*, exactOptionalPropertyTypes), type-aware ESLint (--max-warnings=0), jscpd threshold 0, c8 ratchet, type-level tests, examples build, packed-tarball require/import smoke — npm run check:all green: 102 unit tests, coverage 99.68 / 99.68 / 100 / 95.82.
  • npm run test:live (opt-in, GSWAP_ENV=stage + GSWAP_LIVE_PRIVATE_KEY, serialised on /tmp/swap-e2e-live.lock): quote → swap → confirm() (block 2422335), add / collect / remove liquidity verified by position reads, CreatePool duplicate → SYMBOL_CONFLICT/already-exists, negative bound rejected locally and → BOUNDS_VIOLATION when submitted raw, throwaway signer → CHAIN_DISPATCH_FAILED insufficient balance.
  • Docs site (docs/, Docusaurus) builds clean.

Docs

README, docs/docs/** (getting started, pool concepts, signing/identity, transaction status, error handling, migration 0.x → 1.0), CLI and full-dex examples on the v2 API.

Release notes

  • Tag v1.0.0-rc.1 as a pre-release to trigger publish-npm.yml; latest stays 0.0.10 until a 1.0.0 is cut.
  • Known chain-side limits (documented, not SDK bugs): EIP-712 recovery fails for tick-carrying DTOs on the current contract; personal-sign signers do not get client| alias resolution.

… uniqueKey as a real field

- Trade bounds are validated locally again (negative amountOutMinimum/amountInMaximum
  throw VALIDATION_ERROR before anything is signed); the live test proves the gateway's
  BOUNDS_VIOLATION bounce by submitting a raw signed DTO through ChainGateway instead.
- confirm() returns the explore row with an enumerable uniqueKey (the row does not echo it);
  a 200 with a non-object body now throws CONFIRMATION_FAILED instead of returning raw text.
- Live stage re-run: Trade 0ced6609… block 2420883; add/collect/remove verified by reads.
Route all reads through the swap backend, harden v2 response and quote validation, preserve submission metadata, and remove retired API surfaces. Update examples and documentation for the current signing, confirmation, and slippage contracts.
Validate and expose the backend pool-summary shape separately from single-pool metadata so the new backend-only pools route is not represented by an unsafe cast.
Cover optional pool-list fields and reject malformed backend rows.
Keep the package version visible on backend HTTP requests.
The header-known short-circuit returned null on the first pending explore poll, so
callers lost the indexed confirmation whenever the indexer was a second behind the
gateway. transactionId and blockNumber are already exposed on the submission itself;
confirm() polls the explore read for the full timeout and its Trade result is non-null.
@shoeman22

Copy link
Copy Markdown
Author

Both adversarial review rounds are addressed on this branch (now at f93c408):

  • Round 1 (6933a80, 17 findings): backend-parity tick math, numeric-amount rejection, symbol-cache refresh, timeouts/abort, signer semantics, single body read, DTO XOR types, result shapes, liquidity confirmation via position re-read, pending classification, entrypoint smoke, response validation, pagination caps, malformed signatures, dependency audit.
  • Round 2 (62c1314…d44f0ab, 24 findings): large-tick math performance, exactOptionalPropertyTypes-safe option types, typed/bounded backend pool reads, quote wire validation, example cleanup, legacy type removal, versioned User-Agent, browser-safe UUID, exact pins.
  • 8666af3: confirm() now always waits for the indexed trade; the header short-circuit introduced in round 2 returned null when the indexer lagged the gateway by a poll.
  • The SDK no longer talks to a GalaChain gateway at all: symbols come from GET /v2/trade/symbols, pools/slot0/composite/positions from /v2/trade/*; transactionId/blockNumber are read from the gateway relay's x-transaction-id/x-block-number response headers.

Verification on this exact tree: npm run check:all green (102 tests, coverage 99.68/99.68/100/95.82, examples + packed-tarball smoke), docs site builds, and npm run test:live on stage passed end to end (trade block 2422335, add/collect/remove liquidity, duplicate pool, bounds bounce, insufficient-balance bounce).

@shoeman22 shoeman22 closed this by deleting the head repository Sep 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants