@gala-chain/gswap-sdk 2.0.0-rc.2 → 2.0.0-rc.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,13 +5,29 @@ contract. It uses trading symbols for pool operations, signs writes with the
5
5
  signer supplied by the application, and submits them through the Chain
6
6
  Gateway. Swap requests are queued for later application.
7
7
 
8
+ Pools are identified by trading symbols: the SDK resolves class keys through the
9
+ symbol registry and orders `token0` and `token1` with plain string ordering. The
10
+ public surface is grouped by responsibility:
11
+
12
+ - `gSwap.quoting` runs read-only exact-input and exact-output simulations.
13
+ - `gSwap.swaps` submits queued `RequestTrade` DTOs.
14
+ - `gSwap.pools` reads pools, slot0, and composite pool snapshots.
15
+ - `gSwap.positions` reads positions and submits liquidity operations.
16
+ - `gSwap.assets` reads user balances; `gSwap.symbols` resolves symbols.
17
+
18
+ All reads and writes go through the configured swap backend. The SDK signs the
19
+ DTO, posts writes to the method-specific chain-gateway backend route, and returns
20
+ a `SubmittedTransaction`. Swap acknowledgements are queued; call `confirm()` to
21
+ read the applied trade or chain rejection. There is no bundler or socket
22
+ waiter, and EIP-712 is only an opt-in scheme of `BrowserWalletSigner`.
23
+
8
24
  ## Install
9
25
 
10
26
  ```bash
11
27
  npm install @gala-chain/gswap-sdk@next @gala-chain/gala-core-sdk@next
12
28
  ```
13
29
 
14
- `@gala-chain/gala-core-sdk` is a peer dependency (range `>=1.0.0-rc.6 <2`):
30
+ `@gala-chain/gala-core-sdk` is a peer dependency (range <!-- docs:var peer:@gala-chain/gala-core-sdk -->>=1.0.0-rc.7 <2<!-- /docs:var -->):
15
31
  install it next to the SDK. 2.0 is a release candidate under the `next`
16
32
  dist-tag; `latest` is still 1.2.0, so an install without `@next` gets 1.x.
17
33
  Pin exact versions for a wallet or app build.
@@ -28,7 +44,7 @@ unique-key helpers and the wallet helpers are imported from the core package.
28
44
  and some identifiers were renamed (`isGSwapErrorLike` is `isGalaErrorLike`).
29
45
  The full old-path to new-path table is in
30
46
  [`../gala-core-sdk/MIGRATING-FROM-GSWAP.md`](../gala-core-sdk/MIGRATING-FROM-GSWAP.md);
31
- see also [MIGRATION-SIGNING.md](./MIGRATION-SIGNING.md).
47
+ see also [Migration from 0.x](./docs/other/migration-from-0.x.md).
32
48
 
33
49
  ## Quick start
34
50
 
@@ -65,7 +81,7 @@ the backend accepts, each read under the
65
81
  exact id it was asked for), price series (`market.getPriceSeries`, windows `1D` `1W` `1M` `1Y`
66
82
  `ALL`), platform volume (`market.getVolumeSummary`) and the trading-competition seasons and
67
83
  rankings (`leaderboard.getSeasons`, `getCurrentSeason`, `getLeaderboard`,
68
- `getCurrentLeaderboard`). See [Market Data and Leaderboard](./docs/docs/tutorial-basics/market-data.md).
84
+ `getCurrentLeaderboard`). See [Market Data and Leaderboard](./docs/tutorial-basics/market-data.md).
69
85
 
70
86
  ```typescript
71
87
  const gala = await gSwap.market.getPrice('GALA$Unit$none$none'); // undefined: no entry
@@ -91,10 +107,10 @@ const url = await onramper.generateWidgetUrl({ walletAddress: '0x…', defaultCr
91
107
  The SDK has one network boundary: the swap backend. It does not call public
92
108
  GalaChain gateway hosts directly.
93
109
 
94
- | Preset | `dexBackendBaseUrl` |
95
- | ------- | ---------------------------------------------- |
96
- | `stage` | `https://swap-backend.stage.defi.ovh.gala.com` |
97
- | `prod` | `https://dex-backend-prod1.defi.gala.com` |
110
+ | Preset | Targets |
111
+ | ------- | --------------------------------------------------------------- |
112
+ | `stage` | The staging swap backend (the preset sets `dexBackendBaseUrl`) |
113
+ | `prod` | `https://dex-backend-prod1.defi.gala.com` |
98
114
 
99
115
  Use `dexBackendBaseUrl` for a test or private deployment override.
100
116
 
@@ -104,19 +120,21 @@ Use `dexBackendBaseUrl` for a test or private deployment override.
104
120
  adds/removes/collects, pool and balance changes, new pools and blocks, with automatic reconnect.
105
121
 
106
122
  ```typescript
123
+ import { GSwap } from '@gala-chain/gswap-sdk';
124
+
107
125
  const gSwap = new GSwap({ env: 'stage' });
108
126
  await gSwap.stream.connect();
109
127
  await gSwap.stream.subscribeLiquidity((e) => console.log(e.action, e.owner, e.amount0, e.amount1));
110
128
  ```
111
129
 
112
- See the [realtime stream guide](./docs/docs/tutorial-basics/realtime-stream.md).
130
+ See the [realtime stream guide](./docs/tutorial-basics/realtime-stream.md).
113
131
 
114
132
  ## Signers and identity
115
133
 
116
134
  | Signer | Scheme | Identity resolved by the chain |
117
135
  | --------------------- | ------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------- |
118
136
  | `PrivateKeySigner` | GalaChain native signature | Recovered from the private key |
119
- | `GalaWalletSigner` | Native `gala_signChainDto`; opt-in `personal-sign` needs a wallet build with fix 34ca4e4 (unreleased) | Selected account (`0x...`, `eth\|...`, `client\|...`) |
137
+ | `GalaWalletSigner` | Native `gala_signChainDto`; opt-in `personal-sign` needs a Gala Wallet build that signs the bare DTO (see Signing and Identity) | Selected account (`0x...`, `eth\|...`, `client\|...`) |
120
138
  | `BrowserWalletSigner` | EIP-1193 `personal_sign`; opt-in `{ scheme: 'eip712' }` (`eth_signTypedData_v4`) | Selected Ethereum account (`eip712` also resolves a `client\|` alias) |
121
139
 
122
140
  The signers are exported by `@gala-chain/gala-core-sdk`, not by this package.
@@ -127,7 +145,7 @@ the chain prepends when it recovers the signer. EIP-712 is opt-in on
127
145
  `BrowserWalletSigner` for `RequestTrade`, `AddLiquidity`, `RemoveLiquidity` and
128
146
  `CollectPositionFees` (`CreatePool` still uses personal-sign); it is not the
129
147
  default and not part of the Gala Wallet path. See
130
- [Signing and Identity](./docs/docs/other/signing-and-identity.md).
148
+ [Signing and Identity](./docs/other/signing-and-identity.md).
131
149
 
132
150
  ## Wallet and app integration
133
151
 
@@ -138,23 +156,34 @@ polling with `AbortSignal`, `AppliedTradeError`), `PendingSwapStore` (async `Sto
138
156
  for a web app),
139
157
  `DexFees` (DryRun fee quotes), `swaps.swapShadow` (dark swaps through `X-Shadow`), a configurable
140
158
  `uniqueKeyPrefix`, and the realtime stream with an injected `WebSocket` (and an `apiKey` for
141
- server-side clients). See [App and Wallet Integration](./docs/docs/other/app-integration.md).
159
+ server-side clients). See [App and Wallet Integration](./docs/other/app-integration.md).
142
160
 
143
161
  Errors carry a stable `code` and flat `params` that an app localizes; `classifyFailure`,
144
162
  `pendingSwapFateFor`, `isNoMarket` and the other deciders answer retry and outcome questions from the
145
- code, never from message text ([Error Handling](./docs/docs/other/error-handling.md)). App-shell code
163
+ code, never from message text ([Error Handling](./docs/other/error-handling.md)). App-shell code
146
164
  that must not load the SDK imports its wallet helpers from `@gala-chain/gala-core-sdk/wallet`
147
- ([Bundle size](./docs/docs/other/bundle-size.md)). Tick and price math, including the contract's
148
- in-range rule, is in [Tick and Price Math](./docs/docs/other/tick-and-price-math.md).
165
+ ([Bundle size](./docs/other/bundle-size.md)). Tick and price math, including the contract's
166
+ in-range rule, is in [Tick and Price Math](./docs/other/tick-and-price-math.md).
149
167
 
150
168
  ## Documentation
151
169
 
152
- Read the [gSwap SDK documentation](https://galachain.github.io/gswap-sdk/docs/intro)
153
- for setup, quoting, trading, liquidity, signing, migration, and API reference
154
- guides. Working examples are in [`examples/cli`](./examples/cli),
155
- [`examples/full_dex`](./examples/full_dex) and [`examples/demos`](./examples/demos) (runnable
170
+ Start with [Getting started](./docs/getting-started.md), then follow the tutorials
171
+ for [quoting](./docs/tutorial-basics/quoting.md), [trading](./docs/tutorial-basics/trading.md)
172
+ and [liquidity management](./docs/tutorial-basics/liquidity-management.md).
173
+
174
+ For apps and wallets: [Error handling](./docs/other/error-handling.md) (the error
175
+ catalog, `classifyFailure`, localization),
176
+ [App and wallet integration](./docs/other/app-integration.md) (pending-swap storage,
177
+ wire options, signing primitives, dark swaps),
178
+ [Signing and identity](./docs/other/signing-and-identity.md),
179
+ [Bundle size](./docs/other/bundle-size.md) (the `@gala-chain/gala-core-sdk/wallet`
180
+ subpath and shell-safe helpers) and
181
+ [Tick and price math](./docs/other/tick-and-price-math.md). The API reference is generated from the
182
+ package's TSDoc. The site at galachain.github.io/gswap-sdk describes the 1.x API
183
+ only. Working examples are in `examples/cli`,
184
+ `examples/full_dex` and `examples/demos` of the package (runnable
156
185
  scripts for swaps, quotes, pools, liquidity positions, fees, market data, leaderboards and pool
157
- watching; moved here from launchpad-sdk, which has no DEX).
186
+ watching).
158
187
 
159
188
  ## License
160
189
 
@@ -1 +1 @@
1
- {"version":3,"file":"gateway.js","names":["_defineProperty","ChainGateway","trimTrailingSlash","options","fetchRequestor","userRef","validateTimeoutMs","method","GSwapSDKError","url","walletAddress","headers","buildRequestHeaders","signedBody","uniqueKey","getStringProperty","context","responseContext","response","body","gatewayError","envelope","asRecord","\"error\"","serverMessageOfBody","data","\"data\"","\"Data\"","transactionId","mode","rawTransactionId","\"transactionId\"","bodyBlockNumber","\"blockNumber\"","invalidMode","isBlockNumber","assertQueuedUnderSignedKey","embeddedResult","\"result\"","resultStatus","\"Status\"","headerTransactionId","readHeader","resolvedTransactionId","blockNumber","readBlockNumberHeader","SubmittedTransaction","shadowFailures","payload","cause","outcomeUnknown","requestWithTimeout","error","isGSwapError","answered","readResponseBody","answer","status","text","value","signedUniqueKey","echoed","\"uniqueKey\"","missing","object","nested","getObjectProperty","code","message","retryAfterMs","readRetryAfter"],"sources":["../../../src/classes/gateway.ts"],"sourcesContent":["import {\n buildRequestHeaders,\n fetchRequestor,\n readBlockNumberHeader,\n readHeader,\n readResponseBody,\n readRetryAfter,\n requestWithTimeout,\n validateTimeoutMs,\n type GalaErrorContext,\n type HttpRequestor,\n type HTTPResponse,\n type WireOptions,\n} from '@gala-chain/gala-core-sdk';\nimport {\n getObjectProperty,\n getStringProperty,\n GSwapSDKError,\n isGSwapError,\n UPDATE_REQUIRED_STATUS,\n} from './gswap_sdk_error.js';\nimport { serverMessageOfBody } from '../utils/server_message.js';\nimport { shadowFailures } from '../utils/shadow_errors.js';\nimport { userRef } from '../utils/validation.js';\nimport type { SubmissionOutcomeUnknownReason } from './gswap_error_catalog.js';\nimport type { ShadowSubmitResult } from './shadow_submit.js';\nimport { SubmittedTransaction, type PositionConfirmationResult } from './submitted_transaction.js';\nimport type { AppliedTrade, Position } from '../types/v2_results.js';\n\n/** Options for a chain-gateway client. */\nexport interface ChainGatewayOptions {\n dexBackendBaseUrl: string;\n httpRequestor?: HttpRequestor | undefined;\n walletAddress?: string | undefined;\n /**\n * Timeout of every gateway request in milliseconds (default 30 000); also the per-read timeout\n * of the services built on this gateway and of `confirm()`. `Infinity` arms no timer, for an\n * app whose HTTP stack owns request lifetimes.\n */\n chainCallTimeoutMs?: number | undefined;\n /**\n * Wire format of every request of this gateway and of the services built on it: the headers\n * (`userAgent`, `headers`) of reads AND of the POSTs that submit writes, and the query encoding\n * of reads. `X-Wallet-Address` and `X-Shadow` always win over a same-named `wire.headers` entry.\n */\n wire?: WireOptions | undefined;\n /**\n * Tells the gateway which rejections of YOUR `httpRequestor` mean \"no HTTP answer arrived\" (the\n * connection failed, was reset or dropped), as opposed to an answer your requestor turned into\n * an error (an interceptor that rejects on a 4xx, a version wall). Used only when submitting a\n * write: a rejection it accepts becomes `SUBMISSION_OUTCOME_UNKNOWN` with `reason: 'transport'`\n * (the request may have been queued; the original error is `cause`), and any other rejection\n * reaches the caller unchanged. The SDK's own `TRANSPORT_ERROR` (the default `fetch` requestor,\n * `httpRequestorFromAxios`) always counts, so this is only for requestors that reject with\n * their own errors. Never throw from it; a throw is treated as \"not a transport failure\".\n *\n * @example\n * ```ts\n * new ChainGateway({\n * dexBackendBaseUrl,\n * httpRequestor: galaFetch, // a fetch wrapper: its TypeError is the network failing\n * isTransportFailure: (error) => error instanceof TypeError,\n * });\n * ```\n */\n isTransportFailure?: ((error: unknown) => boolean) | undefined;\n /**\n * Tells the gateway which rejections of YOUR `httpRequestor` are an HTTP ANSWER, and what the\n * answer was: a requestor built on an HTTP client that rejects every non-2xx (an interceptor\n * that throws on a 4xx, a version wall that throws on a 426) loses the answer the SDK reads\n * errors from. Return `{ status, body }` (the parsed JSON, or the raw text; `headers` for\n * `Retry-After`) for such a rejection and the write is handled exactly as if the requestor had\n * resolved that answer: the gateway's own code becomes its catalogued error\n * (`DTO_INVALID`, `RATE_LIMITED`, `CHAIN_DISPATCH_FAILED`, ...), an uncoded body `HTTP_ERROR`,\n * both carrying the signed `uniqueKey` and classified by `classifyFailure` as a write. Return\n * `undefined` for any other rejection: it reaches the caller unchanged. Used only when\n * submitting a write; `isTransportFailure` is asked first (no answer proves nothing, so\n * \"maybe queued\" wins), and a throw from this hook, or a `status` outside 100-599, means \"not an\n * answer\". Never throw from it.\n *\n * @example\n * ```ts\n * new ChainGateway({\n * dexBackendBaseUrl,\n * httpRequestor: httpRequestorFromAxios(instance, { rejectOnHttpError: true }),\n * answerOfRejection: (error) =>\n * error instanceof GatewayError && error.status !== undefined\n * ? { status: error.status, body: error.data }\n * : undefined,\n * });\n * ```\n */\n answerOfRejection?: ((error: unknown) => RejectedAnswer | undefined) | undefined;\n}\n\n/** The HTTP answer a rejection of the app's requestor stands for (see `ChainGatewayOptions.answerOfRejection`). */\nexport interface RejectedAnswer {\n /** The HTTP status of the answer (100-599). */\n status: number;\n /** The answer's body: parsed JSON when it was JSON, else the raw text. */\n body: unknown;\n /** The answer's headers, for `Retry-After`. */\n headers?: Record<string, string | undefined> | undefined;\n}\n\n/** Optional attribution and operation-specific confirmation hooks for a write. */\nexport interface ChainSubmitOptions {\n walletAddress?: string | undefined;\n positionConfirmation?: ((signal: AbortSignal) => Promise<PositionConfirmationResult>) | undefined;\n}\n\n/** Thin client for backend-routed v2 writes: synchronous liquidity writes and queued RequestTrade. */\nexport class ChainGateway {\n private readonly requestor: HttpRequestor;\n private readonly walletAddress: string | undefined;\n private readonly chainCallTimeoutMs: number;\n private readonly isTransportFailure: ((error: unknown) => boolean) | undefined;\n private readonly answerOfRejectionHook:\n ((error: unknown) => RejectedAnswer | undefined) | undefined;\n /** The request timeout in milliseconds (`Infinity` when no timer is armed). */\n public readonly requestTimeoutMs: number;\n /** The swap backend base URL every request is sent to, without a trailing slash. */\n public readonly dexBackendBaseUrl: string;\n /** Wire format shared by the read services built on this gateway. */\n public readonly wire: WireOptions;\n\n /** Create a chain gateway client. */\n constructor(options: ChainGatewayOptions) {\n this.dexBackendBaseUrl = trimTrailingSlash(options.dexBackendBaseUrl);\n this.requestor = options.httpRequestor ?? fetchRequestor;\n this.walletAddress =\n options.walletAddress === undefined\n ? undefined\n : userRef(options.walletAddress, 'walletAddress');\n this.chainCallTimeoutMs = validateTimeoutMs(\n options.chainCallTimeoutMs ?? 30_000,\n 'chainCallTimeoutMs',\n );\n this.requestTimeoutMs = this.chainCallTimeoutMs;\n this.wire = options.wire ?? {};\n this.isTransportFailure = options.isTransportFailure;\n this.answerOfRejectionHook = options.answerOfRejection;\n }\n\n /**\n * Submit a signed v2 DTO through the chain gateway.\n *\n * @throws GSwapSDKError `SUBMISSION_OUTCOME_UNKNOWN` when no answer was read: `params.reason` is\n * `'timeout'` (the POST exceeded `chainCallTimeoutMs`) or `'transport'` (the connection failed\n * or broke off). Either way the write MAY have been queued: keep `error.uniqueKey` and confirm\n * it. Whether to offer the user a Retry (a NEW request under a fresh key) is the app's policy;\n * the SDK states only that the outcome is unknown. Reads keep `TRANSPORT_ERROR`.\n */\n public async submit(\n method: 'RequestTrade',\n signedBody: Record<string, unknown>,\n options?: ChainSubmitOptions,\n ): Promise<SubmittedTransaction<AppliedTrade>>;\n public async submit(\n method: string,\n signedBody: Record<string, unknown>,\n options?: ChainSubmitOptions,\n ): Promise<SubmittedTransaction<Position | null>>;\n public async submit(\n method: string,\n signedBody: Record<string, unknown>,\n options?: ChainSubmitOptions,\n ): Promise<SubmittedTransaction> {\n // The synchronous swap was removed from the contract and from this SDK. This is the only\n // place the retired name may appear in the write path; the class guard in\n // tests/foundation.test.ts pins that nothing signs or submits it.\n if (method === 'Trade') {\n throw new GSwapSDKError('METHOD_NOT_SUPPORTED', { method });\n }\n const url = this.writeUrl(method);\n const walletAddress = this.walletOf(options);\n const headers = buildRequestHeaders(this.wire, {\n json: true,\n fixed: walletAddress === undefined ? {} : { 'X-Wallet-Address': walletAddress },\n });\n const { response, body } = await this.post(method, url, headers, signedBody);\n // Every failure after an answer names the write it is about, so an app can confirm it and a\n // failure classifier can tell a write's HTTP failure from a read's.\n const uniqueKey = getStringProperty(signedBody, 'uniqueKey') ?? '';\n const context = responseContext(response, body, url, uniqueKey);\n\n if (!response.ok) {\n throw gatewayError(response, body, url, uniqueKey);\n }\n\n const envelope = asRecord(body);\n // `error: true` is the gateway refusing the write, even inside a 2xx. It is never a transaction.\n if (envelope?.['error'] === true) {\n throw new GSwapSDKError(\n 'GATEWAY_REPORTED_ERROR',\n { serverMessage: serverMessageOfBody(envelope) },\n context,\n );\n }\n const data = asRecord(envelope?.['data'] ?? envelope?.['Data']);\n if (data === undefined) {\n throw new GSwapSDKError('GATEWAY_DATA_MISSING', {}, context);\n }\n\n const transactionId = getStringProperty(data, 'transactionId');\n const mode = getStringProperty(data, 'mode');\n const rawTransactionId = data['transactionId'];\n const bodyBlockNumber = data['blockNumber'];\n const invalidMode = (): GSwapSDKError<'GATEWAY_MODE_INVALID'> =>\n new GSwapSDKError('GATEWAY_MODE_INVALID', {}, context);\n if (mode !== 'sync' && mode !== 'queued') {\n throw invalidMode();\n }\n if (\n ('transactionId' in data && typeof rawTransactionId !== 'string') ||\n (bodyBlockNumber !== undefined && bodyBlockNumber !== null && !isBlockNumber(bodyBlockNumber))\n ) {\n throw invalidMode();\n }\n if (method === 'RequestTrade' && mode === 'sync') {\n throw new GSwapSDKError('GATEWAY_SYNC_REQUEST_TRADE', {}, context);\n }\n if (method === 'RequestTrade' && mode === 'queued') {\n assertQueuedUnderSignedKey(response, body, url, data, signedBody);\n } else if (mode === 'queued' || !('result' in data)) {\n throw invalidMode();\n }\n\n // Only synchronous writes carry an executed chain result in the gateway response.\n if (mode === 'sync') {\n const embeddedResult = asRecord(data['result']);\n if (embeddedResult !== undefined) {\n const resultStatus = embeddedResult['Status'];\n if (resultStatus !== undefined && resultStatus !== 1) {\n throw GSwapSDKError.fromChainError(\n getStringProperty(embeddedResult, 'ErrorKey') ?? 'CHAIN_ERROR',\n getStringProperty(embeddedResult, 'Message') ?? '',\n context,\n );\n }\n }\n }\n\n const headerTransactionId = readHeader(response, 'x-transaction-id');\n const resolvedTransactionId =\n transactionId === undefined || transactionId === '' ? headerTransactionId : transactionId;\n const blockNumber: number | undefined =\n bodyBlockNumber === null || bodyBlockNumber === undefined\n ? readBlockNumberHeader(response)\n : isBlockNumber(bodyBlockNumber)\n ? bodyBlockNumber\n : undefined;\n return new SubmittedTransaction({\n method,\n uniqueKey,\n transactionId:\n resolvedTransactionId === undefined || resolvedTransactionId === ''\n ? null\n : resolvedTransactionId,\n blockNumber: blockNumber ?? null,\n mode,\n result: mode === 'queued' ? undefined : data['result'],\n envelope: body,\n dexBackendBaseUrl: this.dexBackendBaseUrl,\n httpRequestor: this.requestor,\n requestTimeoutMs: this.chainCallTimeoutMs,\n wire: this.wire,\n ...(options?.positionConfirmation === undefined\n ? {}\n : { positionConfirmation: options.positionConfirmation }),\n });\n }\n\n /**\n * Submit a signed v2 DTO as a SHADOW (dark) write: the same route and body as {@link submit},\n * plus `X-Wallet-Address` and `X-Shadow: true`, so defi-backend marks the transaction private\n * before it reaches the contract. The wallet address is required (the backend resolves the\n * owner from it).\n *\n * Unlike `submit`, EVERY HTTP status resolves: the result is the raw `{ httpStatus, body }`,\n * because shadow failures are classified from the status together with the body's `code`\n * (`SHADOW_UNAVAILABLE`, `SHADOW_UNSUPPORTED_TYPE`, `SHADOW_RELAY_REJECTED`);\n * see `readShadowSubmitOutcome`. Only a failure to get an answer rejects, and it rejects with\n * `SUBMISSION_OUTCOME_UNKNOWN` (`params.reason` `'timeout'` or `'transport'`): the request may\n * have been accepted. Every error it throws has `error.shadow === true`, and a non-SDK object\n * that escapes (your requestor's own rejection) answers `isShadowError(error)`.\n *\n * @example\n * ```ts\n * const result = await gateway.submitShadow('RequestTrade', signedDto, { walletAddress });\n * const outcome = readShadowSubmitOutcome('RequestTrade', signedDto, result);\n * ```\n */\n public async submitShadow(\n method: string,\n signedBody: Record<string, unknown>,\n options?: ChainSubmitOptions,\n ): Promise<ShadowSubmitResult> {\n return shadowFailures(() => this.sendShadow(method, signedBody, options));\n }\n\n private async sendShadow(\n method: string,\n signedBody: Record<string, unknown>,\n options?: ChainSubmitOptions,\n ): Promise<ShadowSubmitResult> {\n if (method === 'Trade') {\n throw new GSwapSDKError('METHOD_NOT_SUPPORTED', { method });\n }\n const walletAddress = this.walletOf(options);\n if (walletAddress === undefined) {\n throw new GSwapSDKError('SHADOW_SUBMIT_NEEDS_WALLET_ADDRESS', {});\n }\n // Header order is frontend_v2's: X-Wallet-Address, then X-Shadow.\n const headers = buildRequestHeaders(this.wire, {\n json: true,\n fixed: { 'X-Wallet-Address': walletAddress, 'X-Shadow': 'true' },\n });\n const { response, body } = await this.post(method, this.writeUrl(method), headers, signedBody);\n return { httpStatus: response.status, body };\n }\n\n /**\n * The `X-Wallet-Address` of a write: the per-call wallet or the gateway's, in the gateway's\n * spelling (`eth|<EIP-55>`, `client|<id>` as written). A per-call wallet that is no user\n * reference is refused before anything is sent.\n */\n private walletOf(options: ChainSubmitOptions | undefined): string | undefined {\n return options?.walletAddress === undefined\n ? this.walletAddress\n : userRef(options.walletAddress, 'walletAddress');\n }\n\n private writeUrl(method: string): string {\n return `${this.dexBackendBaseUrl}/v1/chain/asset/dex-contract/${method}`;\n }\n\n /**\n * POSTs a signed body and reads the answer. Neither a timeout nor a transport failure proves the\n * write was not submitted, so both are `SUBMISSION_OUTCOME_UNKNOWN` (`params.reason` says which),\n * never a retryable read failure. A rejection that is an answer (see `isTransportFailure`) is\n * rethrown unchanged.\n */\n private async post(\n method: string,\n url: string,\n headers: Record<string, string>,\n signedBody: Record<string, unknown>,\n ): Promise<{ response: HTTPResponse; body: unknown }> {\n // Serialized BEFORE the request is attempted and outside the try below: a body that cannot be\n // serialized (a cycle, a BigInt) was never sent, so it must not be mistaken for a transport\n // failure the app's `isTransportFailure` might claim, which would call it \"maybe queued\".\n let payload: string;\n try {\n payload = JSON.stringify(signedBody);\n } catch (cause: unknown) {\n throw new GSwapSDKError('SIGNING_FAILED', { method, reason: 'failed' }, { cause });\n }\n const outcomeUnknown = (\n reason: SubmissionOutcomeUnknownReason,\n cause: unknown,\n ): GSwapSDKError<'SUBMISSION_OUTCOME_UNKNOWN'> =>\n new GSwapSDKError(\n 'SUBMISSION_OUTCOME_UNKNOWN',\n { reason },\n { uniqueKey: getStringProperty(signedBody, 'uniqueKey') ?? '', url, cause },\n );\n let response: HTTPResponse;\n try {\n response = await requestWithTimeout(\n this.requestor,\n url,\n {\n method: 'POST',\n headers,\n body: payload,\n },\n this.chainCallTimeoutMs,\n );\n } catch (error: unknown) {\n if (isGSwapError(error, 'REQUEST_TIMEOUT')) throw outcomeUnknown('timeout', error);\n if (isGSwapError(error, 'TRANSPORT_ERROR') || this.isTransportRejection(error)) {\n throw outcomeUnknown('transport', error);\n }\n // A rejection that IS an HTTP answer (the app's interceptor rejecting on a non-2xx, a\n // version wall) is handled exactly as the answer would have been, so the app does not\n // rebuild the SDK's error from it. Anything else the app's requestor threw reaches the\n // caller unchanged.\n const answered = this.answerOfRejection(error);\n if (answered !== undefined) return answered;\n throw error;\n }\n try {\n return { response, body: await readResponseBody(response) };\n } catch (error: unknown) {\n // The answer's headers arrived but its body broke off: the status alone does not say what\n // the gateway did with the write.\n throw outcomeUnknown('transport', error);\n }\n }\n\n /**\n * The HTTP answer a rejection of the app's requestor stands for, as the response the gateway\n * would have read. `undefined` when the app named none, when its hook throws, or when what it\n * returned is not an HTTP status; then the rejection is not an answer.\n */\n private answerOfRejection(error: unknown): { response: HTTPResponse; body: unknown } | undefined {\n let answer: RejectedAnswer | undefined;\n try {\n answer = this.answerOfRejectionHook?.(error);\n } catch {\n return undefined;\n }\n if (answer === undefined) return undefined;\n const { status, body, headers } = answer;\n if (!Number.isInteger(status) || status < 100 || status > 599) return undefined;\n const text = typeof body === 'string' ? body : '';\n const response: HTTPResponse = {\n ok: status >= 200 && status < 300,\n status,\n json: async () => body,\n text: async () => text,\n headers: headers ?? {},\n };\n return { response, body };\n }\n\n private isTransportRejection(error: unknown): boolean {\n try {\n return this.isTransportFailure?.(error) === true;\n } catch {\n return false;\n }\n }\n\n /** Expose the requestor for sibling read services that share this gateway transport. */\n public get httpRequestor(): HttpRequestor {\n return this.requestor;\n }\n}\n\nfunction trimTrailingSlash(value: string): string {\n return value.replace(/\\/$/u, '');\n}\n\nfunction responseContext(\n response: HTTPResponse,\n body: unknown,\n url: string,\n uniqueKey?: string,\n): GalaErrorContext {\n return {\n status: response.status,\n body,\n url,\n ...(uniqueKey === undefined || uniqueKey === '' ? {} : { uniqueKey }),\n };\n}\n\n/**\n * A queued RequestTrade acknowledgement is a swap only if it names the exact key that was signed:\n * the key is the only handle `confirm()` and every recovery path have on the trade, so an\n * acknowledgement without it (or with another) proves nothing about THIS request. Fails closed:\n * an absent echo is as unacceptable as a wrong one. The code is `MISSING_UNIQUE_KEY` or\n * `UNIQUE_KEY_MISMATCH`, `error.uniqueKey` the signed key.\n *\n * Only RequestTrade is checked: a synchronous write is proven by its own executed chain result,\n * and the apps never required an echo for it.\n */\nfunction assertQueuedUnderSignedKey(\n response: HTTPResponse,\n body: unknown,\n url: string,\n data: Record<string, unknown>,\n signedBody: Record<string, unknown>,\n): void {\n const signedUniqueKey = getStringProperty(signedBody, 'uniqueKey') ?? '';\n const echoed = data['uniqueKey'];\n if (typeof echoed === 'string' && echoed !== '' && echoed === signedUniqueKey) return;\n const missing = echoed === undefined || echoed === null || echoed === '';\n const context: GalaErrorContext = {\n ...responseContext(response, body, url),\n uniqueKey: signedUniqueKey,\n };\n throw missing\n ? new GSwapSDKError('MISSING_UNIQUE_KEY', {}, context)\n : new GSwapSDKError('UNIQUE_KEY_MISMATCH', {}, context);\n}\n\nfunction gatewayError(\n response: HTTPResponse,\n body: unknown,\n url: string,\n uniqueKey: string,\n): GSwapSDKError {\n // The version wall is decided by the status alone, whatever the body names: nothing was queued.\n if (response.status === UPDATE_REQUIRED_STATUS) {\n return new GSwapSDKError(\n 'UPDATE_REQUIRED',\n { status: response.status },\n responseContext(response, body, url, uniqueKey),\n );\n }\n const object = asRecord(body);\n const nested = getObjectProperty(body, 'error');\n const code =\n getStringProperty(object, 'code') ??\n getStringProperty(nested, 'code') ??\n getStringProperty(nested, 'ErrorKey') ??\n 'HTTP_ERROR';\n // Missing server text is an empty param, never SDK-written English standing in for it. A body\n // that is not JSON (an HTML 502 page, a WAF block) is kept as error.bodyText.\n const message = serverMessageOfBody(body);\n const retryAfterMs = response.status === 429 ? readRetryAfter(response) : undefined;\n if (code === 'HTTP_ERROR') {\n return new GSwapSDKError(\n 'HTTP_ERROR',\n { status: response.status, url, serverMessage: message },\n responseContext(response, body, url, uniqueKey),\n );\n }\n return GSwapSDKError.fromGatewayBounce(\n code,\n message,\n response.status,\n body,\n url,\n retryAfterMs,\n uniqueKey === '' ? undefined : uniqueKey,\n );\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n return typeof value === 'object' && value !== null\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\nfunction isBlockNumber(value: unknown): value is number {\n return typeof value === 'number' && Number.isInteger(value) && value >= 0;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"gateway.js","names":["_defineProperty","ChainGateway","trimTrailingSlash","options","fetchRequestor","userRef","validateTimeoutMs","method","GSwapSDKError","url","walletAddress","headers","buildRequestHeaders","signedBody","uniqueKey","getStringProperty","context","responseContext","response","body","gatewayError","envelope","asRecord","\"error\"","serverMessageOfBody","data","\"data\"","\"Data\"","transactionId","mode","rawTransactionId","\"transactionId\"","bodyBlockNumber","\"blockNumber\"","invalidMode","isBlockNumber","assertQueuedUnderSignedKey","embeddedResult","\"result\"","resultStatus","\"Status\"","headerTransactionId","readHeader","resolvedTransactionId","blockNumber","readBlockNumberHeader","SubmittedTransaction","shadowFailures","payload","cause","outcomeUnknown","requestWithTimeout","error","isGSwapError","answered","readResponseBody","answer","status","text","value","signedUniqueKey","echoed","\"uniqueKey\"","missing","object","nested","getObjectProperty","code","message","retryAfterMs","readRetryAfter"],"sources":["../../../src/classes/gateway.ts"],"sourcesContent":["import {\n buildRequestHeaders,\n fetchRequestor,\n readBlockNumberHeader,\n readHeader,\n readResponseBody,\n readRetryAfter,\n requestWithTimeout,\n validateTimeoutMs,\n type GalaErrorContext,\n type HttpRequestor,\n type HTTPResponse,\n type WireOptions,\n} from '@gala-chain/gala-core-sdk';\nimport {\n getObjectProperty,\n getStringProperty,\n GSwapSDKError,\n isGSwapError,\n UPDATE_REQUIRED_STATUS,\n} from './gswap_sdk_error.js';\nimport { serverMessageOfBody } from '../utils/server_message.js';\nimport { shadowFailures } from '../utils/shadow_errors.js';\nimport { userRef } from '../utils/validation.js';\nimport type { SubmissionOutcomeUnknownReason } from './gswap_error_catalog.js';\nimport type { ShadowSubmitResult } from './shadow_submit.js';\nimport { SubmittedTransaction, type PositionConfirmationResult } from './submitted_transaction.js';\nimport type { AppliedTrade, Position } from '../types/v2_results.js';\n\n/** Options for a chain-gateway client. */\nexport interface ChainGatewayOptions {\n dexBackendBaseUrl: string;\n httpRequestor?: HttpRequestor | undefined;\n walletAddress?: string | undefined;\n /**\n * Timeout of every gateway request in milliseconds (default 30 000); also the per-read timeout\n * of the services built on this gateway and of `confirm()`. `Infinity` arms no timer, for an\n * app whose HTTP stack owns request lifetimes.\n */\n chainCallTimeoutMs?: number | undefined;\n /**\n * Wire format of every request of this gateway and of the services built on it: the headers\n * (`userAgent`, `headers`) of reads AND of the POSTs that submit writes, and the query encoding\n * of reads. `X-Wallet-Address` and `X-Shadow` always win over a same-named `wire.headers` entry.\n */\n wire?: WireOptions | undefined;\n /**\n * Tells the gateway which rejections of YOUR `httpRequestor` mean \"no HTTP answer arrived\" (the\n * connection failed, was reset or dropped), as opposed to an answer your requestor turned into\n * an error (an interceptor that rejects on a 4xx, a version wall). Used only when submitting a\n * write: a rejection it accepts becomes `SUBMISSION_OUTCOME_UNKNOWN` with `reason: 'transport'`\n * (the request may have been queued; the original error is `cause`), and any other rejection\n * reaches the caller unchanged. The SDK's own `TRANSPORT_ERROR` (the default `fetch` requestor,\n * `httpRequestorFromAxios`) always counts, so this is only for requestors that reject with\n * their own errors. Never throw from it; a throw is treated as \"not a transport failure\".\n *\n * @example\n * ```ts\n * new ChainGateway({\n * dexBackendBaseUrl,\n * httpRequestor: galaFetch, // a fetch wrapper: its TypeError is the network failing\n * isTransportFailure: (error) => error instanceof TypeError,\n * });\n * ```\n */\n isTransportFailure?: ((error: unknown) => boolean) | undefined;\n /**\n * Tells the gateway which rejections of YOUR `httpRequestor` are an HTTP ANSWER, and what the\n * answer was: a requestor built on an HTTP client that rejects every non-2xx (an interceptor\n * that throws on a 4xx, a version wall that throws on a 426) loses the answer the SDK reads\n * errors from. Return `{ status, body }` (the parsed JSON, or the raw text; `headers` for\n * `Retry-After`) for such a rejection and the write is handled exactly as if the requestor had\n * resolved that answer: the gateway's own code becomes its catalogued error\n * (`DTO_INVALID`, `RATE_LIMITED`, `CHAIN_DISPATCH_FAILED`, ...), an uncoded body `HTTP_ERROR`,\n * both carrying the signed `uniqueKey` and classified by `classifyFailure` as a write. Return\n * `undefined` for any other rejection: it reaches the caller unchanged. Used only when\n * submitting a write; `isTransportFailure` is asked first (no answer proves nothing, so\n * \"maybe queued\" wins), and a throw from this hook, or a `status` outside 100-599, means \"not an\n * answer\". Never throw from it.\n *\n * @example\n * ```ts\n * new ChainGateway({\n * dexBackendBaseUrl,\n * httpRequestor: httpRequestorFromAxios(instance, { rejectOnHttpError: true }),\n * answerOfRejection: (error) =>\n * error instanceof GatewayError && error.status !== undefined\n * ? { status: error.status, body: error.data }\n * : undefined,\n * });\n * ```\n */\n answerOfRejection?: ((error: unknown) => RejectedAnswer | undefined) | undefined;\n}\n\n/** The HTTP answer a rejection of the app's requestor stands for (see `ChainGatewayOptions.answerOfRejection`). */\nexport interface RejectedAnswer {\n /** The HTTP status of the answer (100-599). */\n status: number;\n /** The answer's body: parsed JSON when it was JSON, else the raw text. */\n body: unknown;\n /** The answer's headers, for `Retry-After`. */\n headers?: Record<string, string | undefined> | undefined;\n}\n\n/** Optional attribution and operation-specific confirmation hooks for a write. */\nexport interface ChainSubmitOptions {\n walletAddress?: string | undefined;\n positionConfirmation?: ((signal: AbortSignal) => Promise<PositionConfirmationResult>) | undefined;\n}\n\n/** Thin client for backend-routed v2 writes: synchronous liquidity writes and queued RequestTrade. */\nexport class ChainGateway {\n private readonly requestor: HttpRequestor;\n private readonly walletAddress: string | undefined;\n private readonly chainCallTimeoutMs: number;\n private readonly isTransportFailure: ((error: unknown) => boolean) | undefined;\n private readonly answerOfRejectionHook:\n ((error: unknown) => RejectedAnswer | undefined) | undefined;\n /** The request timeout in milliseconds (`Infinity` when no timer is armed). */\n public readonly requestTimeoutMs: number;\n /** The swap backend base URL every request is sent to, without a trailing slash. */\n public readonly dexBackendBaseUrl: string;\n /** Wire format shared by the read services built on this gateway. */\n public readonly wire: WireOptions;\n\n /** Create a chain gateway client. */\n constructor(options: ChainGatewayOptions) {\n this.dexBackendBaseUrl = trimTrailingSlash(options.dexBackendBaseUrl);\n this.requestor = options.httpRequestor ?? fetchRequestor;\n this.walletAddress =\n options.walletAddress === undefined\n ? undefined\n : userRef(options.walletAddress, 'walletAddress');\n this.chainCallTimeoutMs = validateTimeoutMs(\n options.chainCallTimeoutMs ?? 30_000,\n 'chainCallTimeoutMs',\n );\n this.requestTimeoutMs = this.chainCallTimeoutMs;\n this.wire = options.wire ?? {};\n this.isTransportFailure = options.isTransportFailure;\n this.answerOfRejectionHook = options.answerOfRejection;\n }\n\n /**\n * Submit a signed v2 DTO through the chain gateway.\n *\n * @throws GSwapSDKError `SUBMISSION_OUTCOME_UNKNOWN` when no answer was read: `params.reason` is\n * `'timeout'` (the POST exceeded `chainCallTimeoutMs`) or `'transport'` (the connection failed\n * or broke off). Either way the write MAY have been queued: keep `error.uniqueKey` and confirm\n * it. Whether to offer the user a Retry (a NEW request under a fresh key) is the app's policy;\n * the SDK states only that the outcome is unknown. Reads keep `TRANSPORT_ERROR`.\n */\n public async submit(\n method: 'RequestTrade',\n signedBody: Record<string, unknown>,\n options?: ChainSubmitOptions,\n ): Promise<SubmittedTransaction<AppliedTrade>>;\n public async submit(\n method: string,\n signedBody: Record<string, unknown>,\n options?: ChainSubmitOptions,\n ): Promise<SubmittedTransaction<Position | null>>;\n public async submit(\n method: string,\n signedBody: Record<string, unknown>,\n options?: ChainSubmitOptions,\n ): Promise<SubmittedTransaction> {\n // The synchronous swap was removed from the contract and from this SDK. This is the only\n // place the retired name may appear in the write path; the class guard in\n // tests/foundation.test.ts pins that nothing signs or submits it.\n if (method === 'Trade') {\n throw new GSwapSDKError('METHOD_NOT_SUPPORTED', { method });\n }\n const url = this.writeUrl(method);\n const walletAddress = this.walletOf(options);\n const headers = buildRequestHeaders(this.wire, {\n json: true,\n fixed: walletAddress === undefined ? {} : { 'X-Wallet-Address': walletAddress },\n });\n const { response, body } = await this.post(method, url, headers, signedBody);\n // Every failure after an answer names the write it is about, so an app can confirm it and a\n // failure classifier can tell a write's HTTP failure from a read's.\n const uniqueKey = getStringProperty(signedBody, 'uniqueKey') ?? '';\n const context = responseContext(response, body, url, uniqueKey);\n\n if (!response.ok) {\n throw gatewayError(response, body, url, uniqueKey);\n }\n\n const envelope = asRecord(body);\n // `error: true` is the gateway refusing the write, even inside a 2xx. It is never a transaction.\n if (envelope?.['error'] === true) {\n throw new GSwapSDKError(\n 'GATEWAY_REPORTED_ERROR',\n { serverMessage: serverMessageOfBody(envelope) },\n context,\n );\n }\n const data = asRecord(envelope?.['data'] ?? envelope?.['Data']);\n if (data === undefined) {\n throw new GSwapSDKError('GATEWAY_DATA_MISSING', {}, context);\n }\n\n const transactionId = getStringProperty(data, 'transactionId');\n const mode = getStringProperty(data, 'mode');\n const rawTransactionId = data['transactionId'];\n const bodyBlockNumber = data['blockNumber'];\n const invalidMode = (): GSwapSDKError<'GATEWAY_MODE_INVALID'> =>\n new GSwapSDKError('GATEWAY_MODE_INVALID', {}, context);\n if (mode !== 'sync' && mode !== 'queued') {\n throw invalidMode();\n }\n if (\n ('transactionId' in data && typeof rawTransactionId !== 'string') ||\n (bodyBlockNumber !== undefined && bodyBlockNumber !== null && !isBlockNumber(bodyBlockNumber))\n ) {\n throw invalidMode();\n }\n if (method === 'RequestTrade' && mode === 'sync') {\n throw new GSwapSDKError('GATEWAY_SYNC_REQUEST_TRADE', {}, context);\n }\n if (method === 'RequestTrade' && mode === 'queued') {\n assertQueuedUnderSignedKey(response, body, url, data, signedBody);\n } else if (mode === 'queued' || !('result' in data)) {\n throw invalidMode();\n }\n\n // Only synchronous writes carry an executed chain result in the gateway response.\n if (mode === 'sync') {\n const embeddedResult = asRecord(data['result']);\n if (embeddedResult !== undefined) {\n const resultStatus = embeddedResult['Status'];\n if (resultStatus !== undefined && resultStatus !== 1) {\n throw GSwapSDKError.fromChainError(\n getStringProperty(embeddedResult, 'ErrorKey') ?? 'CHAIN_ERROR',\n getStringProperty(embeddedResult, 'Message') ?? '',\n context,\n );\n }\n }\n }\n\n const headerTransactionId = readHeader(response, 'x-transaction-id');\n const resolvedTransactionId =\n transactionId === undefined || transactionId === '' ? headerTransactionId : transactionId;\n const blockNumber: number | undefined =\n bodyBlockNumber === null || bodyBlockNumber === undefined\n ? readBlockNumberHeader(response)\n : isBlockNumber(bodyBlockNumber)\n ? bodyBlockNumber\n : undefined;\n return new SubmittedTransaction({\n method,\n uniqueKey,\n transactionId:\n resolvedTransactionId === undefined || resolvedTransactionId === ''\n ? null\n : resolvedTransactionId,\n blockNumber: blockNumber ?? null,\n mode,\n result: mode === 'queued' ? undefined : data['result'],\n envelope: body,\n dexBackendBaseUrl: this.dexBackendBaseUrl,\n httpRequestor: this.requestor,\n requestTimeoutMs: this.chainCallTimeoutMs,\n wire: this.wire,\n ...(options?.positionConfirmation === undefined\n ? {}\n : { positionConfirmation: options.positionConfirmation }),\n });\n }\n\n /**\n * Submit a signed v2 DTO as a SHADOW (dark) write: the same route and body as {@link submit},\n * plus `X-Wallet-Address` and `X-Shadow: true`, so the swap backend marks the transaction private\n * before it reaches the contract. The wallet address is required (the backend resolves the\n * owner from it).\n *\n * Unlike `submit`, EVERY HTTP status resolves: the result is the raw `{ httpStatus, body }`,\n * because shadow failures are classified from the status together with the body's `code`\n * (`SHADOW_UNAVAILABLE`, `SHADOW_UNSUPPORTED_TYPE`, `SHADOW_RELAY_REJECTED`);\n * see `readShadowSubmitOutcome`. Only a failure to get an answer rejects, and it rejects with\n * `SUBMISSION_OUTCOME_UNKNOWN` (`params.reason` `'timeout'` or `'transport'`): the request may\n * have been accepted. Every error it throws has `error.shadow === true`, and a non-SDK object\n * that escapes (your requestor's own rejection) answers `isShadowError(error)`.\n *\n * @example\n * ```ts\n * const result = await gateway.submitShadow('RequestTrade', signedDto, { walletAddress });\n * const outcome = readShadowSubmitOutcome('RequestTrade', signedDto, result);\n * ```\n */\n public async submitShadow(\n method: string,\n signedBody: Record<string, unknown>,\n options?: ChainSubmitOptions,\n ): Promise<ShadowSubmitResult> {\n return shadowFailures(() => this.sendShadow(method, signedBody, options));\n }\n\n private async sendShadow(\n method: string,\n signedBody: Record<string, unknown>,\n options?: ChainSubmitOptions,\n ): Promise<ShadowSubmitResult> {\n if (method === 'Trade') {\n throw new GSwapSDKError('METHOD_NOT_SUPPORTED', { method });\n }\n const walletAddress = this.walletOf(options);\n if (walletAddress === undefined) {\n throw new GSwapSDKError('SHADOW_SUBMIT_NEEDS_WALLET_ADDRESS', {});\n }\n // Header order is the one the Gala web apps send: X-Wallet-Address, then X-Shadow.\n const headers = buildRequestHeaders(this.wire, {\n json: true,\n fixed: { 'X-Wallet-Address': walletAddress, 'X-Shadow': 'true' },\n });\n const { response, body } = await this.post(method, this.writeUrl(method), headers, signedBody);\n return { httpStatus: response.status, body };\n }\n\n /**\n * The `X-Wallet-Address` of a write: the per-call wallet or the gateway's, in the gateway's\n * spelling (`eth|<EIP-55>`, `client|<id>` as written). A per-call wallet that is no user\n * reference is refused before anything is sent.\n */\n private walletOf(options: ChainSubmitOptions | undefined): string | undefined {\n return options?.walletAddress === undefined\n ? this.walletAddress\n : userRef(options.walletAddress, 'walletAddress');\n }\n\n private writeUrl(method: string): string {\n return `${this.dexBackendBaseUrl}/v1/chain/asset/dex-contract/${method}`;\n }\n\n /**\n * POSTs a signed body and reads the answer. Neither a timeout nor a transport failure proves the\n * write was not submitted, so both are `SUBMISSION_OUTCOME_UNKNOWN` (`params.reason` says which),\n * never a retryable read failure. A rejection that is an answer (see `isTransportFailure`) is\n * rethrown unchanged.\n */\n private async post(\n method: string,\n url: string,\n headers: Record<string, string>,\n signedBody: Record<string, unknown>,\n ): Promise<{ response: HTTPResponse; body: unknown }> {\n // Serialized BEFORE the request is attempted and outside the try below: a body that cannot be\n // serialized (a cycle, a BigInt) was never sent, so it must not be mistaken for a transport\n // failure the app's `isTransportFailure` might claim, which would call it \"maybe queued\".\n let payload: string;\n try {\n payload = JSON.stringify(signedBody);\n } catch (cause: unknown) {\n throw new GSwapSDKError('SIGNING_FAILED', { method, reason: 'failed' }, { cause });\n }\n const outcomeUnknown = (\n reason: SubmissionOutcomeUnknownReason,\n cause: unknown,\n ): GSwapSDKError<'SUBMISSION_OUTCOME_UNKNOWN'> =>\n new GSwapSDKError(\n 'SUBMISSION_OUTCOME_UNKNOWN',\n { reason },\n { uniqueKey: getStringProperty(signedBody, 'uniqueKey') ?? '', url, cause },\n );\n let response: HTTPResponse;\n try {\n response = await requestWithTimeout(\n this.requestor,\n url,\n {\n method: 'POST',\n headers,\n body: payload,\n },\n this.chainCallTimeoutMs,\n );\n } catch (error: unknown) {\n if (isGSwapError(error, 'REQUEST_TIMEOUT')) throw outcomeUnknown('timeout', error);\n if (isGSwapError(error, 'TRANSPORT_ERROR') || this.isTransportRejection(error)) {\n throw outcomeUnknown('transport', error);\n }\n // A rejection that IS an HTTP answer (the app's interceptor rejecting on a non-2xx, a\n // version wall) is handled exactly as the answer would have been, so the app does not\n // rebuild the SDK's error from it. Anything else the app's requestor threw reaches the\n // caller unchanged.\n const answered = this.answerOfRejection(error);\n if (answered !== undefined) return answered;\n throw error;\n }\n try {\n return { response, body: await readResponseBody(response) };\n } catch (error: unknown) {\n // The answer's headers arrived but its body broke off: the status alone does not say what\n // the gateway did with the write.\n throw outcomeUnknown('transport', error);\n }\n }\n\n /**\n * The HTTP answer a rejection of the app's requestor stands for, as the response the gateway\n * would have read. `undefined` when the app named none, when its hook throws, or when what it\n * returned is not an HTTP status; then the rejection is not an answer.\n */\n private answerOfRejection(error: unknown): { response: HTTPResponse; body: unknown } | undefined {\n let answer: RejectedAnswer | undefined;\n try {\n answer = this.answerOfRejectionHook?.(error);\n } catch {\n return undefined;\n }\n if (answer === undefined) return undefined;\n const { status, body, headers } = answer;\n if (!Number.isInteger(status) || status < 100 || status > 599) return undefined;\n const text = typeof body === 'string' ? body : '';\n const response: HTTPResponse = {\n ok: status >= 200 && status < 300,\n status,\n json: async () => body,\n text: async () => text,\n headers: headers ?? {},\n };\n return { response, body };\n }\n\n private isTransportRejection(error: unknown): boolean {\n try {\n return this.isTransportFailure?.(error) === true;\n } catch {\n return false;\n }\n }\n\n /** Expose the requestor for sibling read services that share this gateway transport. */\n public get httpRequestor(): HttpRequestor {\n return this.requestor;\n }\n}\n\nfunction trimTrailingSlash(value: string): string {\n return value.replace(/\\/$/u, '');\n}\n\nfunction responseContext(\n response: HTTPResponse,\n body: unknown,\n url: string,\n uniqueKey?: string,\n): GalaErrorContext {\n return {\n status: response.status,\n body,\n url,\n ...(uniqueKey === undefined || uniqueKey === '' ? {} : { uniqueKey }),\n };\n}\n\n/**\n * A queued RequestTrade acknowledgement is a swap only if it names the exact key that was signed:\n * the key is the only handle `confirm()` and every recovery path have on the trade, so an\n * acknowledgement without it (or with another) proves nothing about THIS request. Fails closed:\n * an absent echo is as unacceptable as a wrong one. The code is `MISSING_UNIQUE_KEY` or\n * `UNIQUE_KEY_MISMATCH`, `error.uniqueKey` the signed key.\n *\n * Only RequestTrade is checked: a synchronous write is proven by its own executed chain result,\n * and the apps never required an echo for it.\n */\nfunction assertQueuedUnderSignedKey(\n response: HTTPResponse,\n body: unknown,\n url: string,\n data: Record<string, unknown>,\n signedBody: Record<string, unknown>,\n): void {\n const signedUniqueKey = getStringProperty(signedBody, 'uniqueKey') ?? '';\n const echoed = data['uniqueKey'];\n if (typeof echoed === 'string' && echoed !== '' && echoed === signedUniqueKey) return;\n const missing = echoed === undefined || echoed === null || echoed === '';\n const context: GalaErrorContext = {\n ...responseContext(response, body, url),\n uniqueKey: signedUniqueKey,\n };\n throw missing\n ? new GSwapSDKError('MISSING_UNIQUE_KEY', {}, context)\n : new GSwapSDKError('UNIQUE_KEY_MISMATCH', {}, context);\n}\n\nfunction gatewayError(\n response: HTTPResponse,\n body: unknown,\n url: string,\n uniqueKey: string,\n): GSwapSDKError {\n // The version wall is decided by the status alone, whatever the body names: nothing was queued.\n if (response.status === UPDATE_REQUIRED_STATUS) {\n return new GSwapSDKError(\n 'UPDATE_REQUIRED',\n { status: response.status },\n responseContext(response, body, url, uniqueKey),\n );\n }\n const object = asRecord(body);\n const nested = getObjectProperty(body, 'error');\n const code =\n getStringProperty(object, 'code') ??\n getStringProperty(nested, 'code') ??\n getStringProperty(nested, 'ErrorKey') ??\n 'HTTP_ERROR';\n // Missing server text is an empty param, never SDK-written English standing in for it. A body\n // that is not JSON (an HTML 502 page, a WAF block) is kept as error.bodyText.\n const message = serverMessageOfBody(body);\n const retryAfterMs = response.status === 429 ? readRetryAfter(response) : undefined;\n if (code === 'HTTP_ERROR') {\n return new GSwapSDKError(\n 'HTTP_ERROR',\n { status: response.status, url, serverMessage: message },\n responseContext(response, body, url, uniqueKey),\n );\n }\n return GSwapSDKError.fromGatewayBounce(\n code,\n message,\n response.status,\n body,\n url,\n retryAfterMs,\n uniqueKey === '' ? undefined : uniqueKey,\n );\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n return typeof value === 'object' && value !== null\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\nfunction isBlockNumber(value: unknown): value is number {\n return typeof value === 'number' && Number.isInteger(value) && value >= 0;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"gdex_stream.js","names":["_defineProperty","GDEX_SUBPROTOCOL","LIQUIDITY_ACTIONS","isGdexLiquidityEvent","value","\"version\"","pool","\"poolHash\"","\"transactionId\"","\"action\"","\"owner\"","\"amount0\"","\"amount1\"","\"blockNumber\"","\"timestamp\"","GdexStreamError","message","code","data","readSubscriptionId","id","GdexStreamService","config","GSWAP_ENVIRONMENTS","ReconnectionBackoff","implementation","GSwapSDKError","socket","method","params","frame","\"params\"","timeout","reject","resolve","error","selector","ref","userRef","owner","tokenClassKey","opts","cb","filter","event","debugLog","opened","pending","errorMessage","subscriptionId","\"subscription\"","result","\"result\"","subscription","options","handle","serverId","delay","attempt","onResult","retryAfterMs","\"retryAfterMs\""],"sources":["../../../src/classes/gdex_stream.ts"],"sourcesContent":["import { GSWAP_ENVIRONMENTS } from '../types/env.js';\nimport { debugLog } from '../utils/debug.js';\nimport { userRef } from '../utils/validation.js';\nimport { ReconnectionBackoff } from '../utils/reconnection.js';\nimport { GSwapSDKError } from './gswap_sdk_error.js';\n\nconst GDEX_SUBPROTOCOL = 'gdex.jsonrpc.v1';\nconst RATE_LIMITED = -32003;\nconst MAX_RESUBSCRIBE_RATE_RETRIES = 5;\nconst WEBSOCKET_OPEN = 1;\n\n/** Options for {@link GdexStreamService}. */\nexport interface GdexStreamConfig {\n /** Stream endpoint. Defaults to production (`wss://dex-backend-prod1.defi.gala.com/ws`). */\n url?: string | undefined;\n /** Reconnect attempts after an unexpected close. Default 5. */\n reconnectAttempts?: number | undefined;\n /** Delay before the first reconnect attempt; doubles each attempt. Default 2000 ms. */\n reconnectDelayMs?: number | undefined;\n /** How long a request waits for its response. Default 15000 ms. */\n requestTimeoutMs?: number | undefined;\n /**\n * A standards-compatible WebSocket constructor. Required on Node.js older than 22, which has no\n * global `WebSocket` (pass the one from the `ws` package).\n */\n webSocketImpl?: unknown;\n /**\n * Presents `x-api-key` on the WebSocket upgrade request, which defi-backend uses to serve the\n * PRIVILEGED view (shadow swaps with the wallet unredacted). Without it, or with a wrong key, the\n * connection is simply public: the server never says whether a privileged mode exists.\n *\n * It must travel as an HTTP header, because the server reads no other credential, and a browser\n * `WebSocket` cannot set headers. So this option needs a constructor that accepts them as its\n * third argument (`{ headers }`): the `ws` package on Node.js, or React Native's built-in\n * `WebSocket`. It therefore REQUIRES `webSocketImpl` (the platform default is never assumed to\n * honor headers, and a key that is silently dropped would look like a working privileged\n * connection); the constructor throws otherwise. Use it from server-side code only: a key\n * shipped inside a browser or app bundle is not a secret. Browser clients stay public.\n */\n apiKey?: string | undefined;\n}\n\n/** The channels of the realtime stream. */\nexport type GdexChannel = 'pool' | 'balance' | 'poolCreated' | 'swap' | 'liquidity' | 'blocks';\n\n/** A pool named by its two token class keys (pipe form) and fee tier. */\nexport interface GdexPoolKey {\n token0ClassKey: string;\n token1ClassKey: string;\n fee: number;\n}\n\n/** A pool named either by its key or by its hash (`v2:<poolRef>` for a v2 pool). */\nexport type GdexPoolSelector = { pool: GdexPoolKey } | { poolHash: string };\n\n/** Options common to every subscription. */\nexport interface GdexSubscribeOptions {\n /** Send the current state right after the subscribe response. */\n snapshot?: boolean | undefined;\n /** Trim each event to these fields; identity and envelope fields always stay. */\n fields?: string[] | undefined;\n}\n\n/** Filters for {@link GdexStreamService.subscribeLiquidity}; all optional. */\nexport interface GdexLiquidityFilter {\n /** Only this pool, named by its key. */\n pool?: GdexPoolKey | undefined;\n /** Only this pool, named by its hash (`v2:<poolRef>` for a v2 pool). */\n poolHash?: string | undefined;\n /** Only events for this LP wallet (the position's owner). */\n owner?: string | undefined;\n /** Only this transaction. */\n transactionId?: string | undefined;\n /** Contract version filter. */\n version?: 'v1' | 'v2' | 'all' | undefined;\n}\n\n/** One committed add, remove or collect, as the `liquidity` channel reports it. */\nexport interface GdexLiquidityEvent {\n version: 'v1' | 'v2';\n pool: GdexPoolKey;\n poolHash: string;\n transactionId: string;\n /** `add`, `remove` or `collect`. */\n action: 'add' | 'remove' | 'collect';\n /** The position's owner (the LP wallet). */\n owner: string;\n /** v1: the position id. v2: the chain position key `<pool>|<tickLow>|<tickHigh>|<owner>`. */\n positionId?: string;\n tickLower?: number;\n tickUpper?: number;\n /** v2 only: the position's liquidity after the operation; `\"0\"` means it is closed. */\n liquidityAfter?: string;\n /** Principal moved: deposited by an add, withdrawn by a remove, `\"0\"` for a collect. */\n amount0: string;\n amount1: string;\n /** Fees paid out. v2: always present (`\"0\"` for an add). v1: present on a collect only. */\n fees0?: string;\n fees1?: string;\n /** v2 only. */\n token0Symbol?: string;\n token1Symbol?: string;\n blockNumber: number;\n timestamp: string;\n}\n\nconst LIQUIDITY_ACTIONS: readonly unknown[] = ['add', 'remove', 'collect'];\n\n/** Whether a stream payload carries every field of a {@link GdexLiquidityEvent}. */\nexport function isGdexLiquidityEvent(\n value: Record<string, unknown>,\n): value is Record<string, unknown> & GdexLiquidityEvent {\n const { pool } = value;\n return (\n (value['version'] === 'v1' || value['version'] === 'v2') &&\n typeof pool === 'object' &&\n pool !== null &&\n typeof value['poolHash'] === 'string' &&\n typeof value['transactionId'] === 'string' &&\n LIQUIDITY_ACTIONS.includes(value['action']) &&\n typeof value['owner'] === 'string' &&\n typeof value['amount0'] === 'string' &&\n typeof value['amount1'] === 'string' &&\n typeof value['blockNumber'] === 'number' &&\n typeof value['timestamp'] === 'string'\n );\n}\n\n/** A live subscription. */\nexport interface GdexSubscription {\n subscriptionId: string;\n unsubscribe(): Promise<void>;\n}\n\n/** A JSON-RPC error answered by the stream. */\nexport class GdexStreamError extends Error {\n /** The JSON-RPC error code. */\n readonly code: number;\n /** The error's optional `data` member, as the server sent it. */\n readonly data?: unknown;\n\n constructor(code: number, message: string, data?: unknown) {\n super(message);\n this.name = 'GdexStreamError';\n this.code = code;\n if (data !== undefined) this.data = data;\n }\n}\n\ninterface WebSocketLike {\n readonly readyState: number;\n send(data: string): void;\n close(code?: number, reason?: string): void;\n onopen: ((event: unknown) => void) | null;\n onmessage: ((event: { data: unknown }) => void) | null;\n onclose: ((event: { code?: number; reason?: string }) => void) | null;\n onerror: ((event: unknown) => void) | null;\n}\n\ninterface WebSocketConstructor {\n new (\n url: string,\n protocols?: string | string[],\n options?: { headers: Record<string, string> },\n ): WebSocketLike;\n}\n\ninterface PendingRequest {\n resolve(value: unknown): void;\n reject(error: Error): void;\n timeout: ReturnType<typeof setTimeout>;\n /**\n * Runs SYNCHRONOUSLY when the response frame is handled, before any frame that follows it in\n * the same read. Subscriptions register here: the server writes a snapshot right after the\n * subscribe response, and a runtime that delivers both frames without yielding to microtasks\n * (Node's `ws` does, for frames in one chunk) would otherwise drop the snapshot because the\n * awaiting caller has not registered the subscription yet.\n */\n onResult?: ((value: unknown) => void) | undefined;\n}\n\ntype EventCallback = (event: Record<string, unknown>) => void;\n\ninterface ActiveSubscription {\n serverId: string;\n readonly params: Record<string, unknown>;\n readonly callback: EventCallback;\n readonly handle: MutableGdexSubscription;\n active: boolean;\n}\n\ninterface MutableGdexSubscription extends GdexSubscription {\n subscriptionId: string;\n}\n\nfunction readSubscriptionId(value: unknown): string {\n const id =\n typeof value === 'object' && value !== null\n ? (value as { subscriptionId?: unknown }).subscriptionId\n : undefined;\n if (typeof id !== 'string') {\n throw new GdexStreamError(-32603, 'GDEX subscribe response is missing its subscriptionId');\n }\n return id;\n}\n\ninterface JsonRpcResponse {\n id?: unknown;\n method?: unknown;\n result?: unknown;\n error?: { code?: unknown; message?: unknown; data?: unknown };\n params?: unknown;\n}\n\n/**\n * Client for the gSwap realtime stream (gdex WebSocket Protocol v1): JSON-RPC 2.0 over one raw\n * WebSocket, with automatic reconnect and resubscribe.\n *\n * @example\n * ```typescript\n * const stream = new GdexStreamService();\n * await stream.connect();\n * await stream.subscribeLiquidity(event => console.log(event.action, event.owner), {\n * owner: 'client|618ae395c1c653111d3315be',\n * });\n * ```\n */\nexport class GdexStreamService {\n private readonly url: string;\n private readonly requestTimeoutMs: number;\n private readonly apiKey: string | undefined;\n private readonly webSocketConstructor: WebSocketConstructor;\n private readonly backoff: ReconnectionBackoff;\n private socket: WebSocketLike | undefined;\n private connectPromise: Promise<void> | undefined;\n private nextRequestId = 0;\n private readonly pending = new Map<number, PendingRequest>();\n private readonly subscriptionsByServerId = new Map<string, ActiveSubscription>();\n private readonly activeSubscriptions = new Set<ActiveSubscription>();\n private intentionalClose = false;\n private reconnecting = false;\n private reconnectTimer: ReturnType<typeof setTimeout> | undefined;\n private reconnectDelayResolve: (() => void) | undefined;\n\n constructor(config: GdexStreamConfig = {}) {\n this.url = config.url ?? GSWAP_ENVIRONMENTS.prod.dexBackendWsUrl;\n this.requestTimeoutMs = config.requestTimeoutMs ?? 15_000;\n this.backoff = new ReconnectionBackoff({\n maxAttempts: config.reconnectAttempts ?? 5,\n baseDelayMs: config.reconnectDelayMs ?? 2_000,\n });\n\n const implementation =\n config.webSocketImpl ?? (globalThis as { WebSocket?: unknown }).WebSocket;\n if (typeof implementation !== 'function') {\n throw new GSwapSDKError('STREAM_WEBSOCKET_UNAVAILABLE', {});\n }\n this.webSocketConstructor = implementation as WebSocketConstructor;\n if (config.apiKey !== undefined) {\n if (config.apiKey === '' || config.webSocketImpl === undefined) {\n throw new GSwapSDKError('STREAM_API_KEY_UNUSABLE', {});\n }\n this.apiKey = config.apiKey;\n }\n }\n\n /** Open the connection. Resolves at once when already connected. */\n connect(): Promise<void> {\n if (this.isConnected()) return Promise.resolve();\n if (this.connectPromise) return this.connectPromise;\n this.intentionalClose = false;\n this.clearReconnectTimer();\n this.connectPromise = this.openSocket();\n return this.connectPromise;\n }\n\n /** Close the connection and stop reconnecting. */\n disconnect(): void {\n this.intentionalClose = true;\n this.reconnecting = false;\n this.clearReconnectTimer();\n this.rejectPending(new GSwapSDKError('STREAM_DISCONNECTED', {}));\n const socket = this.socket;\n this.socket = undefined;\n this.connectPromise = undefined;\n socket?.close(1000, 'client disconnect');\n }\n\n /** `true` while the socket is open (false while connecting, reconnecting or after `disconnect()`). */\n isConnected(): boolean {\n return this.socket?.readyState === WEBSOCKET_OPEN;\n }\n\n /** Raw JSON-RPC escape hatch. */\n request<T = unknown>(method: string, params?: Record<string, unknown>): Promise<T> {\n return this.send<T>(method, params);\n }\n\n private send<T>(\n method: string,\n params: Record<string, unknown> | undefined,\n onResult?: (value: unknown) => void,\n ): Promise<T> {\n if (!this.isConnected() || !this.socket) {\n return Promise.reject(new GSwapSDKError('STREAM_NOT_CONNECTED', {}));\n }\n\n const id = ++this.nextRequestId;\n const frame: Record<string, unknown> = { jsonrpc: '2.0', id, method };\n if (params !== undefined) frame['params'] = params;\n\n return new Promise<T>((resolve, reject) => {\n const timeout = setTimeout(() => {\n this.pending.delete(id);\n reject(\n new GSwapSDKError('STREAM_REQUEST_TIMEOUT', { method, timeoutMs: this.requestTimeoutMs }),\n );\n }, this.requestTimeoutMs);\n this.pending.set(id, { resolve: (value) => resolve(value as T), reject, timeout, onResult });\n\n try {\n this.socket?.send(JSON.stringify(frame));\n } catch (error) {\n clearTimeout(timeout);\n this.pending.delete(id);\n reject(error instanceof Error ? error : new Error(String(error)));\n }\n });\n }\n\n /** `gdex_serverInfo`: the server's own description of itself, as it sends it. */\n serverInfo(): Promise<Record<string, unknown>> {\n return this.request('gdex_serverInfo');\n }\n\n /** `gdex_ping`: a round trip that answers `{ pong, serverTime }`. */\n ping(): Promise<{ pong: boolean; serverTime: string }> {\n return this.request('gdex_ping');\n }\n\n /** `gdex_getPool`: the current state of one pool, selected by its key or hash. */\n getPool(selector: GdexPoolSelector): Promise<Record<string, unknown>> {\n return this.request('gdex_getPool', selector);\n }\n\n /** `gdex_getBalance`: an owner's balances, or only the one of `tokenClassKey` when given. */\n async getBalance(owner: string, tokenClassKey?: string): Promise<Record<string, unknown>> {\n const ref = userRef(owner, 'owner');\n return this.request(\n 'gdex_getBalance',\n tokenClassKey === undefined ? { owner: ref } : { owner: ref, tokenClassKey },\n );\n }\n\n /**\n * `gdex_getPositions`: an owner's positions, optionally in one pool, a page at a time. Pass the\n * returned `bookmark` back in as `bookmark` for the next page.\n */\n async getPositions(opts: {\n owner: string;\n pool?: GdexPoolKey;\n poolHash?: string;\n limit?: number;\n bookmark?: string;\n }): Promise<{ positions: Record<string, unknown>[]; bookmark: string }> {\n return this.request('gdex_getPositions', { ...opts, owner: userRef(opts.owner, 'owner') });\n }\n\n /** `gdex_getBlockHeight`: the block number the server reports, with its timestamp. */\n getBlockHeight(): Promise<{ blockNumber: number; timestamp: string }> {\n return this.request('gdex_getBlockHeight');\n }\n\n /** `gdex_quote`: a quote over the stream; give exactly one of `amountIn` or `amountOut`. */\n quote(params: {\n tokenIn: string;\n tokenOut: string;\n fee?: number;\n amountIn?: string;\n amountOut?: string;\n }): Promise<Record<string, unknown>> {\n return this.request('gdex_quote', params);\n }\n\n /** `gdex_getToken`: the server's record of one token class, as it sends it. */\n getToken(tokenClassKey: string): Promise<Record<string, unknown>> {\n return this.request('gdex_getToken', { tokenClassKey });\n }\n\n /** One event per committed swap. Omit the pool to receive every pool's swaps. */\n subscribeSwaps(\n cb: (event: Record<string, unknown>) => void,\n opts: GdexSubscribeOptions & {\n pool?: GdexPoolKey;\n poolHash?: string;\n transactionId?: string;\n } = {},\n ): Promise<GdexSubscription> {\n return this.subscribe('swap', cb, opts);\n }\n\n /**\n * State changes of one pool (price, tick, reserves, liquidity). These events carry no acting wallet:\n * on v2 `creator` is whoever created the pool, not whoever just traded or added liquidity. For\n * \"who did it\", use {@link subscribeLiquidity} (`owner`) or {@link subscribeSwaps} (`sender`).\n */\n subscribePool(\n selector: GdexPoolSelector,\n cb: (event: Record<string, unknown>) => void,\n opts: GdexSubscribeOptions = {},\n ): Promise<GdexSubscription> {\n return this.subscribe('pool', cb, { ...selector, ...opts });\n }\n\n /** Balance changes of one wallet. */\n async subscribeBalances(\n owner: string,\n cb: (event: Record<string, unknown>) => void,\n opts: GdexSubscribeOptions & { tokenClassKey?: string } = {},\n ): Promise<GdexSubscription> {\n return this.subscribe('balance', cb, { owner: userRef(owner, 'owner'), ...opts });\n }\n\n /** Each newly created pool. */\n subscribePoolCreated(\n cb: (event: Record<string, unknown>) => void,\n opts: GdexSubscribeOptions & { tokenClassKey?: string } = {},\n ): Promise<GdexSubscription> {\n return this.subscribe('poolCreated', cb, opts);\n }\n\n /**\n * One event per committed AddLiquidity, RemoveLiquidity and CollectPositionFees, on both contract\n * versions. Every filter is optional; with none, every liquidity event arrives. These events never\n * appear on {@link subscribeSwaps}. There is no snapshot: read positions with {@link getPositions}.\n * Deduplicate by `transactionId` plus `positionId`: a v1 BatchSubmit can hold several operations\n * under one transaction id.\n */\n async subscribeLiquidity(\n cb: (event: GdexLiquidityEvent) => void,\n opts: GdexSubscribeOptions & GdexLiquidityFilter = {},\n ): Promise<GdexSubscription> {\n const filter =\n opts.owner === undefined ? opts : { ...opts, owner: userRef(opts.owner, 'owner') };\n return this.subscribe(\n 'liquidity',\n (event) => {\n if (isGdexLiquidityEvent(event)) cb(event);\n else debugLog('Ignoring a malformed liquidity event from the GDEX stream');\n },\n filter,\n );\n }\n\n /** Each block-height advance. */\n subscribeBlocks(\n cb: (event: Record<string, unknown>) => void,\n opts: GdexSubscribeOptions = {},\n ): Promise<GdexSubscription> {\n return this.subscribe('blocks', cb, opts);\n }\n\n private openSocket(): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n let opened = false;\n const socket =\n this.apiKey === undefined\n ? new this.webSocketConstructor(this.url, GDEX_SUBPROTOCOL)\n : new this.webSocketConstructor(this.url, GDEX_SUBPROTOCOL, {\n headers: { 'x-api-key': this.apiKey },\n });\n this.socket = socket;\n\n socket.onopen = () => {\n if (this.socket !== socket) return;\n opened = true;\n this.connectPromise = undefined;\n if (!this.reconnecting) this.backoff.reset();\n debugLog('Connected to GDEX stream');\n resolve();\n };\n socket.onmessage = (event) => this.handleMessage(event.data);\n socket.onerror = () => {\n if (!opened) reject(new GSwapSDKError('STREAM_CONNECT_FAILED', { url: this.url }));\n };\n socket.onclose = (event) => {\n if (this.socket !== socket) return;\n this.socket = undefined;\n this.connectPromise = undefined;\n if (!opened) {\n reject(new GSwapSDKError('STREAM_CLOSED_BEFORE_CONNECT', { closeCode: event.code ?? 0 }));\n }\n this.rejectPending(\n new GSwapSDKError('STREAM_CONNECTION_CLOSED', { closeCode: event.code ?? 0 }),\n );\n if (!this.intentionalClose && !this.reconnecting) this.startReconnectLoop();\n };\n });\n }\n\n private handleMessage(data: unknown): void {\n if (typeof data !== 'string') {\n debugLog('Ignoring non-text GDEX WebSocket frame');\n return;\n }\n\n let message: JsonRpcResponse;\n try {\n message = JSON.parse(data) as JsonRpcResponse;\n } catch {\n debugLog('Ignoring invalid JSON from GDEX stream');\n return;\n }\n\n if (typeof message.id === 'number') {\n const pending = this.pending.get(message.id);\n if (!pending) return;\n clearTimeout(pending.timeout);\n this.pending.delete(message.id);\n if (message.error) {\n const code = typeof message.error.code === 'number' ? message.error.code : -32603;\n const errorMessage =\n typeof message.error.message === 'string' ? message.error.message : 'GDEX request failed';\n pending.reject(new GdexStreamError(code, errorMessage, message.error.data));\n } else {\n try {\n pending.onResult?.(message.result);\n } catch (error) {\n pending.reject(error instanceof Error ? error : new Error(String(error)));\n return;\n }\n pending.resolve(message.result);\n }\n return;\n }\n\n if (message.method !== 'gdex_subscription' || !this.isRecord(message.params)) return;\n const subscriptionId = message.params['subscription'];\n const result = message.params['result'];\n if (typeof subscriptionId !== 'string' || !this.isRecord(result)) return;\n const subscription = this.subscriptionsByServerId.get(subscriptionId);\n if (!subscription?.active) return;\n try {\n subscription.callback(result);\n } catch (error) {\n debugLog('GDEX subscription callback failed', error);\n }\n }\n\n private async subscribe(\n channel: GdexChannel,\n callback: EventCallback,\n options: object,\n ): Promise<GdexSubscription> {\n const params: Record<string, unknown> = { channel, ...options };\n const handle: MutableGdexSubscription = {\n subscriptionId: '',\n unsubscribe: async () => this.unsubscribe(subscription),\n };\n const subscription: ActiveSubscription = {\n serverId: '',\n params,\n callback,\n handle,\n active: true,\n };\n // Registered inside the response handler, not after the await: see PendingRequest.onResult.\n await this.send('gdex_subscribe', params, (value) => {\n const serverId = readSubscriptionId(value);\n subscription.serverId = serverId;\n handle.subscriptionId = serverId;\n this.activeSubscriptions.add(subscription);\n this.subscriptionsByServerId.set(serverId, subscription);\n });\n return handle;\n }\n\n private async unsubscribe(subscription: ActiveSubscription): Promise<void> {\n if (!subscription.active) return;\n subscription.active = false;\n this.activeSubscriptions.delete(subscription);\n this.subscriptionsByServerId.delete(subscription.serverId);\n if (this.isConnected()) {\n await this.request('gdex_unsubscribe', { subscriptionId: subscription.serverId });\n }\n }\n\n private startReconnectLoop(): void {\n if (this.reconnecting || this.intentionalClose) return;\n this.reconnecting = true;\n void this.reconnectLoop();\n }\n\n private async reconnectLoop(): Promise<void> {\n while (!this.intentionalClose && this.backoff.shouldRetry()) {\n const delay = this.backoff.nextDelayMs();\n this.backoff.recordAttempt();\n debugLog(\n `GDEX reconnect attempt ${this.backoff.attemptCount}/${this.backoff.maxAttempts} in ${delay}ms`,\n );\n await this.waitForReconnectDelay(delay);\n // `disconnect()` can run while the asynchronous delay is pending.\n if (this.intentionalClose) break;\n\n try {\n await this.openSocket();\n await this.resubscribeAll();\n this.backoff.reset();\n this.reconnecting = false;\n return;\n } catch (error) {\n debugLog(\n `GDEX reconnect failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n this.reconnecting = false;\n if (!this.intentionalClose) debugLog('GDEX reconnection attempts exhausted');\n }\n\n private async resubscribeAll(): Promise<void> {\n this.subscriptionsByServerId.clear();\n for (const subscription of this.activeSubscriptions) {\n if (!subscription.active) continue;\n const params = { ...subscription.params, snapshot: true };\n const result = await this.resubscribeWithRateLimitRetry(params, (value) => {\n const serverId = readSubscriptionId(value);\n // The consumer can unsubscribe while the asynchronous subscribe request is pending.\n if (!subscription.active) return;\n subscription.serverId = serverId;\n subscription.handle.subscriptionId = serverId;\n this.subscriptionsByServerId.set(serverId, subscription);\n });\n if (!subscription.active) {\n await this.request('gdex_unsubscribe', { subscriptionId: result.subscriptionId });\n }\n }\n }\n\n private async resubscribeWithRateLimitRetry(\n params: Record<string, unknown>,\n onResult: (value: unknown) => void,\n ): Promise<{ subscriptionId: string }> {\n for (let attempt = 0; ; attempt++) {\n try {\n return await this.send('gdex_subscribe', params, onResult);\n } catch (error) {\n if (\n !(error instanceof GdexStreamError) ||\n error.code !== RATE_LIMITED ||\n attempt >= MAX_RESUBSCRIBE_RATE_RETRIES\n ) {\n throw error;\n }\n const retryAfterMs = this.readRetryAfterMs(error.data);\n debugLog(`GDEX resubscribe rate limited; retrying in ${retryAfterMs}ms`);\n await new Promise<void>((resolve) => setTimeout(resolve, retryAfterMs));\n }\n }\n }\n\n private waitForReconnectDelay(delay: number): Promise<void> {\n return new Promise((resolve) => {\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = undefined;\n this.reconnectDelayResolve = undefined;\n resolve();\n }, delay);\n this.reconnectDelayResolve = resolve;\n });\n }\n\n private clearReconnectTimer(): void {\n if (this.reconnectTimer !== undefined) clearTimeout(this.reconnectTimer);\n this.reconnectTimer = undefined;\n const resolve = this.reconnectDelayResolve;\n this.reconnectDelayResolve = undefined;\n resolve?.();\n }\n\n private rejectPending(error: Error): void {\n for (const pending of this.pending.values()) {\n clearTimeout(pending.timeout);\n pending.reject(error);\n }\n this.pending.clear();\n }\n\n private readRetryAfterMs(data: unknown): number {\n if (\n !this.isRecord(data) ||\n typeof data['retryAfterMs'] !== 'number' ||\n data['retryAfterMs'] < 0\n )\n return 1_000;\n return data['retryAfterMs'];\n }\n\n private isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"gdex_stream.js","names":["_defineProperty","GDEX_SUBPROTOCOL","LIQUIDITY_ACTIONS","isGdexLiquidityEvent","value","\"version\"","pool","\"poolHash\"","\"transactionId\"","\"action\"","\"owner\"","\"amount0\"","\"amount1\"","\"blockNumber\"","\"timestamp\"","GdexStreamError","message","code","data","readSubscriptionId","id","GdexStreamService","config","GSWAP_ENVIRONMENTS","ReconnectionBackoff","implementation","GSwapSDKError","socket","method","params","frame","\"params\"","timeout","reject","resolve","error","selector","ref","userRef","owner","tokenClassKey","opts","cb","filter","event","debugLog","opened","pending","errorMessage","subscriptionId","\"subscription\"","result","\"result\"","subscription","options","handle","serverId","delay","attempt","onResult","retryAfterMs","\"retryAfterMs\""],"sources":["../../../src/classes/gdex_stream.ts"],"sourcesContent":["import { GSWAP_ENVIRONMENTS } from '../types/env.js';\nimport { debugLog } from '../utils/debug.js';\nimport { userRef } from '../utils/validation.js';\nimport { ReconnectionBackoff } from '../utils/reconnection.js';\nimport { GSwapSDKError } from './gswap_sdk_error.js';\n\nconst GDEX_SUBPROTOCOL = 'gdex.jsonrpc.v1';\nconst RATE_LIMITED = -32003;\nconst MAX_RESUBSCRIBE_RATE_RETRIES = 5;\nconst WEBSOCKET_OPEN = 1;\n\n/** Options for {@link GdexStreamService}. */\nexport interface GdexStreamConfig {\n /** Stream endpoint. Defaults to production (`wss://dex-backend-prod1.defi.gala.com/ws`). */\n url?: string | undefined;\n /** Reconnect attempts after an unexpected close. Default 5. */\n reconnectAttempts?: number | undefined;\n /** Delay before the first reconnect attempt; doubles each attempt. Default 2000 ms. */\n reconnectDelayMs?: number | undefined;\n /** How long a request waits for its response. Default 15000 ms. */\n requestTimeoutMs?: number | undefined;\n /**\n * A standards-compatible WebSocket constructor. Required on Node.js older than 22, which has no\n * global `WebSocket` (pass the one from the `ws` package).\n */\n webSocketImpl?: unknown;\n /**\n * Presents `x-api-key` on the WebSocket upgrade request, which the swap backend uses to serve the\n * PRIVILEGED view (shadow swaps with the wallet unredacted). Without it, or with a wrong key, the\n * connection is simply public: the server never says whether a privileged mode exists.\n *\n * It must travel as an HTTP header, because the server reads no other credential, and a browser\n * `WebSocket` cannot set headers. So this option needs a constructor that accepts them as its\n * third argument (`{ headers }`): the `ws` package on Node.js, or React Native's built-in\n * `WebSocket`. It therefore REQUIRES `webSocketImpl` (the platform default is never assumed to\n * honor headers, and a key that is silently dropped would look like a working privileged\n * connection); the constructor throws otherwise. Use it from server-side code only: a key\n * shipped inside a browser or app bundle is not a secret. Browser clients stay public.\n */\n apiKey?: string | undefined;\n}\n\n/** The channels of the realtime stream. */\nexport type GdexChannel = 'pool' | 'balance' | 'poolCreated' | 'swap' | 'liquidity' | 'blocks';\n\n/** A pool named by its two token class keys (pipe form) and fee tier. */\nexport interface GdexPoolKey {\n token0ClassKey: string;\n token1ClassKey: string;\n fee: number;\n}\n\n/** A pool named either by its key or by its hash (`v2:<poolRef>` for a v2 pool). */\nexport type GdexPoolSelector = { pool: GdexPoolKey } | { poolHash: string };\n\n/** Options common to every subscription. */\nexport interface GdexSubscribeOptions {\n /** Send the current state right after the subscribe response. */\n snapshot?: boolean | undefined;\n /** Trim each event to these fields; identity and envelope fields always stay. */\n fields?: string[] | undefined;\n}\n\n/** Filters for {@link GdexStreamService.subscribeLiquidity}; all optional. */\nexport interface GdexLiquidityFilter {\n /** Only this pool, named by its key. */\n pool?: GdexPoolKey | undefined;\n /** Only this pool, named by its hash (`v2:<poolRef>` for a v2 pool). */\n poolHash?: string | undefined;\n /** Only events for this LP wallet (the position's owner). */\n owner?: string | undefined;\n /** Only this transaction. */\n transactionId?: string | undefined;\n /** Contract version filter. */\n version?: 'v1' | 'v2' | 'all' | undefined;\n}\n\n/** One committed add, remove or collect, as the `liquidity` channel reports it. */\nexport interface GdexLiquidityEvent {\n version: 'v1' | 'v2';\n pool: GdexPoolKey;\n poolHash: string;\n transactionId: string;\n /** `add`, `remove` or `collect`. */\n action: 'add' | 'remove' | 'collect';\n /** The position's owner (the LP wallet). */\n owner: string;\n /** v1: the position id. v2: the chain position key `<pool>|<tickLow>|<tickHigh>|<owner>`. */\n positionId?: string;\n tickLower?: number;\n tickUpper?: number;\n /** v2 only: the position's liquidity after the operation; `\"0\"` means it is closed. */\n liquidityAfter?: string;\n /** Principal moved: deposited by an add, withdrawn by a remove, `\"0\"` for a collect. */\n amount0: string;\n amount1: string;\n /** Fees paid out. v2: always present (`\"0\"` for an add). v1: present on a collect only. */\n fees0?: string;\n fees1?: string;\n /** v2 only. */\n token0Symbol?: string;\n token1Symbol?: string;\n blockNumber: number;\n timestamp: string;\n}\n\nconst LIQUIDITY_ACTIONS: readonly unknown[] = ['add', 'remove', 'collect'];\n\n/** Whether a stream payload carries every field of a {@link GdexLiquidityEvent}. */\nexport function isGdexLiquidityEvent(\n value: Record<string, unknown>,\n): value is Record<string, unknown> & GdexLiquidityEvent {\n const { pool } = value;\n return (\n (value['version'] === 'v1' || value['version'] === 'v2') &&\n typeof pool === 'object' &&\n pool !== null &&\n typeof value['poolHash'] === 'string' &&\n typeof value['transactionId'] === 'string' &&\n LIQUIDITY_ACTIONS.includes(value['action']) &&\n typeof value['owner'] === 'string' &&\n typeof value['amount0'] === 'string' &&\n typeof value['amount1'] === 'string' &&\n typeof value['blockNumber'] === 'number' &&\n typeof value['timestamp'] === 'string'\n );\n}\n\n/** A live subscription. */\nexport interface GdexSubscription {\n subscriptionId: string;\n unsubscribe(): Promise<void>;\n}\n\n/** A JSON-RPC error answered by the stream. */\nexport class GdexStreamError extends Error {\n /** The JSON-RPC error code. */\n readonly code: number;\n /** The error's optional `data` member, as the server sent it. */\n readonly data?: unknown;\n\n constructor(code: number, message: string, data?: unknown) {\n super(message);\n this.name = 'GdexStreamError';\n this.code = code;\n if (data !== undefined) this.data = data;\n }\n}\n\ninterface WebSocketLike {\n readonly readyState: number;\n send(data: string): void;\n close(code?: number, reason?: string): void;\n onopen: ((event: unknown) => void) | null;\n onmessage: ((event: { data: unknown }) => void) | null;\n onclose: ((event: { code?: number; reason?: string }) => void) | null;\n onerror: ((event: unknown) => void) | null;\n}\n\ninterface WebSocketConstructor {\n new (\n url: string,\n protocols?: string | string[],\n options?: { headers: Record<string, string> },\n ): WebSocketLike;\n}\n\ninterface PendingRequest {\n resolve(value: unknown): void;\n reject(error: Error): void;\n timeout: ReturnType<typeof setTimeout>;\n /**\n * Runs SYNCHRONOUSLY when the response frame is handled, before any frame that follows it in\n * the same read. Subscriptions register here: the server writes a snapshot right after the\n * subscribe response, and a runtime that delivers both frames without yielding to microtasks\n * (Node's `ws` does, for frames in one chunk) would otherwise drop the snapshot because the\n * awaiting caller has not registered the subscription yet.\n */\n onResult?: ((value: unknown) => void) | undefined;\n}\n\ntype EventCallback = (event: Record<string, unknown>) => void;\n\ninterface ActiveSubscription {\n serverId: string;\n readonly params: Record<string, unknown>;\n readonly callback: EventCallback;\n readonly handle: MutableGdexSubscription;\n active: boolean;\n}\n\ninterface MutableGdexSubscription extends GdexSubscription {\n subscriptionId: string;\n}\n\nfunction readSubscriptionId(value: unknown): string {\n const id =\n typeof value === 'object' && value !== null\n ? (value as { subscriptionId?: unknown }).subscriptionId\n : undefined;\n if (typeof id !== 'string') {\n throw new GdexStreamError(-32603, 'GDEX subscribe response is missing its subscriptionId');\n }\n return id;\n}\n\ninterface JsonRpcResponse {\n id?: unknown;\n method?: unknown;\n result?: unknown;\n error?: { code?: unknown; message?: unknown; data?: unknown };\n params?: unknown;\n}\n\n/**\n * Client for the gSwap realtime stream (gdex WebSocket Protocol v1): JSON-RPC 2.0 over one raw\n * WebSocket, with automatic reconnect and resubscribe.\n *\n * @example\n * ```typescript\n * const stream = new GdexStreamService();\n * await stream.connect();\n * await stream.subscribeLiquidity(event => console.log(event.action, event.owner), {\n * owner: 'client|618ae395c1c653111d3315be',\n * });\n * ```\n */\nexport class GdexStreamService {\n private readonly url: string;\n private readonly requestTimeoutMs: number;\n private readonly apiKey: string | undefined;\n private readonly webSocketConstructor: WebSocketConstructor;\n private readonly backoff: ReconnectionBackoff;\n private socket: WebSocketLike | undefined;\n private connectPromise: Promise<void> | undefined;\n private nextRequestId = 0;\n private readonly pending = new Map<number, PendingRequest>();\n private readonly subscriptionsByServerId = new Map<string, ActiveSubscription>();\n private readonly activeSubscriptions = new Set<ActiveSubscription>();\n private intentionalClose = false;\n private reconnecting = false;\n private reconnectTimer: ReturnType<typeof setTimeout> | undefined;\n private reconnectDelayResolve: (() => void) | undefined;\n\n constructor(config: GdexStreamConfig = {}) {\n this.url = config.url ?? GSWAP_ENVIRONMENTS.prod.dexBackendWsUrl;\n this.requestTimeoutMs = config.requestTimeoutMs ?? 15_000;\n this.backoff = new ReconnectionBackoff({\n maxAttempts: config.reconnectAttempts ?? 5,\n baseDelayMs: config.reconnectDelayMs ?? 2_000,\n });\n\n const implementation =\n config.webSocketImpl ?? (globalThis as { WebSocket?: unknown }).WebSocket;\n if (typeof implementation !== 'function') {\n throw new GSwapSDKError('STREAM_WEBSOCKET_UNAVAILABLE', {});\n }\n this.webSocketConstructor = implementation as WebSocketConstructor;\n if (config.apiKey !== undefined) {\n if (config.apiKey === '' || config.webSocketImpl === undefined) {\n throw new GSwapSDKError('STREAM_API_KEY_UNUSABLE', {});\n }\n this.apiKey = config.apiKey;\n }\n }\n\n /** Open the connection. Resolves at once when already connected. */\n connect(): Promise<void> {\n if (this.isConnected()) return Promise.resolve();\n if (this.connectPromise) return this.connectPromise;\n this.intentionalClose = false;\n this.clearReconnectTimer();\n this.connectPromise = this.openSocket();\n return this.connectPromise;\n }\n\n /** Close the connection and stop reconnecting. */\n disconnect(): void {\n this.intentionalClose = true;\n this.reconnecting = false;\n this.clearReconnectTimer();\n this.rejectPending(new GSwapSDKError('STREAM_DISCONNECTED', {}));\n const socket = this.socket;\n this.socket = undefined;\n this.connectPromise = undefined;\n socket?.close(1000, 'client disconnect');\n }\n\n /** `true` while the socket is open (false while connecting, reconnecting or after `disconnect()`). */\n isConnected(): boolean {\n return this.socket?.readyState === WEBSOCKET_OPEN;\n }\n\n /** Raw JSON-RPC escape hatch. */\n request<T = unknown>(method: string, params?: Record<string, unknown>): Promise<T> {\n return this.send<T>(method, params);\n }\n\n private send<T>(\n method: string,\n params: Record<string, unknown> | undefined,\n onResult?: (value: unknown) => void,\n ): Promise<T> {\n if (!this.isConnected() || !this.socket) {\n return Promise.reject(new GSwapSDKError('STREAM_NOT_CONNECTED', {}));\n }\n\n const id = ++this.nextRequestId;\n const frame: Record<string, unknown> = { jsonrpc: '2.0', id, method };\n if (params !== undefined) frame['params'] = params;\n\n return new Promise<T>((resolve, reject) => {\n const timeout = setTimeout(() => {\n this.pending.delete(id);\n reject(\n new GSwapSDKError('STREAM_REQUEST_TIMEOUT', { method, timeoutMs: this.requestTimeoutMs }),\n );\n }, this.requestTimeoutMs);\n this.pending.set(id, { resolve: (value) => resolve(value as T), reject, timeout, onResult });\n\n try {\n this.socket?.send(JSON.stringify(frame));\n } catch (error) {\n clearTimeout(timeout);\n this.pending.delete(id);\n reject(error instanceof Error ? error : new Error(String(error)));\n }\n });\n }\n\n /** `gdex_serverInfo`: the server's own description of itself, as it sends it. */\n serverInfo(): Promise<Record<string, unknown>> {\n return this.request('gdex_serverInfo');\n }\n\n /** `gdex_ping`: a round trip that answers `{ pong, serverTime }`. */\n ping(): Promise<{ pong: boolean; serverTime: string }> {\n return this.request('gdex_ping');\n }\n\n /** `gdex_getPool`: the current state of one pool, selected by its key or hash. */\n getPool(selector: GdexPoolSelector): Promise<Record<string, unknown>> {\n return this.request('gdex_getPool', selector);\n }\n\n /** `gdex_getBalance`: an owner's balances, or only the one of `tokenClassKey` when given. */\n async getBalance(owner: string, tokenClassKey?: string): Promise<Record<string, unknown>> {\n const ref = userRef(owner, 'owner');\n return this.request(\n 'gdex_getBalance',\n tokenClassKey === undefined ? { owner: ref } : { owner: ref, tokenClassKey },\n );\n }\n\n /**\n * `gdex_getPositions`: an owner's positions, optionally in one pool, a page at a time. Pass the\n * returned `bookmark` back in as `bookmark` for the next page.\n */\n async getPositions(opts: {\n owner: string;\n pool?: GdexPoolKey;\n poolHash?: string;\n limit?: number;\n bookmark?: string;\n }): Promise<{ positions: Record<string, unknown>[]; bookmark: string }> {\n return this.request('gdex_getPositions', { ...opts, owner: userRef(opts.owner, 'owner') });\n }\n\n /** `gdex_getBlockHeight`: the block number the server reports, with its timestamp. */\n getBlockHeight(): Promise<{ blockNumber: number; timestamp: string }> {\n return this.request('gdex_getBlockHeight');\n }\n\n /** `gdex_quote`: a quote over the stream; give exactly one of `amountIn` or `amountOut`. */\n quote(params: {\n tokenIn: string;\n tokenOut: string;\n fee?: number;\n amountIn?: string;\n amountOut?: string;\n }): Promise<Record<string, unknown>> {\n return this.request('gdex_quote', params);\n }\n\n /** `gdex_getToken`: the server's record of one token class, as it sends it. */\n getToken(tokenClassKey: string): Promise<Record<string, unknown>> {\n return this.request('gdex_getToken', { tokenClassKey });\n }\n\n /** One event per committed swap. Omit the pool to receive every pool's swaps. */\n subscribeSwaps(\n cb: (event: Record<string, unknown>) => void,\n opts: GdexSubscribeOptions & {\n pool?: GdexPoolKey;\n poolHash?: string;\n transactionId?: string;\n } = {},\n ): Promise<GdexSubscription> {\n return this.subscribe('swap', cb, opts);\n }\n\n /**\n * State changes of one pool (price, tick, reserves, liquidity). These events carry no acting wallet:\n * on v2 `creator` is whoever created the pool, not whoever just traded or added liquidity. For\n * \"who did it\", use {@link subscribeLiquidity} (`owner`) or {@link subscribeSwaps} (`sender`).\n */\n subscribePool(\n selector: GdexPoolSelector,\n cb: (event: Record<string, unknown>) => void,\n opts: GdexSubscribeOptions = {},\n ): Promise<GdexSubscription> {\n return this.subscribe('pool', cb, { ...selector, ...opts });\n }\n\n /** Balance changes of one wallet. */\n async subscribeBalances(\n owner: string,\n cb: (event: Record<string, unknown>) => void,\n opts: GdexSubscribeOptions & { tokenClassKey?: string } = {},\n ): Promise<GdexSubscription> {\n return this.subscribe('balance', cb, { owner: userRef(owner, 'owner'), ...opts });\n }\n\n /** Each newly created pool. */\n subscribePoolCreated(\n cb: (event: Record<string, unknown>) => void,\n opts: GdexSubscribeOptions & { tokenClassKey?: string } = {},\n ): Promise<GdexSubscription> {\n return this.subscribe('poolCreated', cb, opts);\n }\n\n /**\n * One event per committed AddLiquidity, RemoveLiquidity and CollectPositionFees, on both contract\n * versions. Every filter is optional; with none, every liquidity event arrives. These events never\n * appear on {@link subscribeSwaps}. There is no snapshot: read positions with {@link getPositions}.\n * Deduplicate by `transactionId` plus `positionId`: a v1 BatchSubmit can hold several operations\n * under one transaction id.\n */\n async subscribeLiquidity(\n cb: (event: GdexLiquidityEvent) => void,\n opts: GdexSubscribeOptions & GdexLiquidityFilter = {},\n ): Promise<GdexSubscription> {\n const filter =\n opts.owner === undefined ? opts : { ...opts, owner: userRef(opts.owner, 'owner') };\n return this.subscribe(\n 'liquidity',\n (event) => {\n if (isGdexLiquidityEvent(event)) cb(event);\n else debugLog('Ignoring a malformed liquidity event from the GDEX stream');\n },\n filter,\n );\n }\n\n /** Each block-height advance. */\n subscribeBlocks(\n cb: (event: Record<string, unknown>) => void,\n opts: GdexSubscribeOptions = {},\n ): Promise<GdexSubscription> {\n return this.subscribe('blocks', cb, opts);\n }\n\n private openSocket(): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n let opened = false;\n const socket =\n this.apiKey === undefined\n ? new this.webSocketConstructor(this.url, GDEX_SUBPROTOCOL)\n : new this.webSocketConstructor(this.url, GDEX_SUBPROTOCOL, {\n headers: { 'x-api-key': this.apiKey },\n });\n this.socket = socket;\n\n socket.onopen = () => {\n if (this.socket !== socket) return;\n opened = true;\n this.connectPromise = undefined;\n if (!this.reconnecting) this.backoff.reset();\n debugLog('Connected to GDEX stream');\n resolve();\n };\n socket.onmessage = (event) => this.handleMessage(event.data);\n socket.onerror = () => {\n if (!opened) reject(new GSwapSDKError('STREAM_CONNECT_FAILED', { url: this.url }));\n };\n socket.onclose = (event) => {\n if (this.socket !== socket) return;\n this.socket = undefined;\n this.connectPromise = undefined;\n if (!opened) {\n reject(new GSwapSDKError('STREAM_CLOSED_BEFORE_CONNECT', { closeCode: event.code ?? 0 }));\n }\n this.rejectPending(\n new GSwapSDKError('STREAM_CONNECTION_CLOSED', { closeCode: event.code ?? 0 }),\n );\n if (!this.intentionalClose && !this.reconnecting) this.startReconnectLoop();\n };\n });\n }\n\n private handleMessage(data: unknown): void {\n if (typeof data !== 'string') {\n debugLog('Ignoring non-text GDEX WebSocket frame');\n return;\n }\n\n let message: JsonRpcResponse;\n try {\n message = JSON.parse(data) as JsonRpcResponse;\n } catch {\n debugLog('Ignoring invalid JSON from GDEX stream');\n return;\n }\n\n if (typeof message.id === 'number') {\n const pending = this.pending.get(message.id);\n if (!pending) return;\n clearTimeout(pending.timeout);\n this.pending.delete(message.id);\n if (message.error) {\n const code = typeof message.error.code === 'number' ? message.error.code : -32603;\n const errorMessage =\n typeof message.error.message === 'string' ? message.error.message : 'GDEX request failed';\n pending.reject(new GdexStreamError(code, errorMessage, message.error.data));\n } else {\n try {\n pending.onResult?.(message.result);\n } catch (error) {\n pending.reject(error instanceof Error ? error : new Error(String(error)));\n return;\n }\n pending.resolve(message.result);\n }\n return;\n }\n\n if (message.method !== 'gdex_subscription' || !this.isRecord(message.params)) return;\n const subscriptionId = message.params['subscription'];\n const result = message.params['result'];\n if (typeof subscriptionId !== 'string' || !this.isRecord(result)) return;\n const subscription = this.subscriptionsByServerId.get(subscriptionId);\n if (!subscription?.active) return;\n try {\n subscription.callback(result);\n } catch (error) {\n debugLog('GDEX subscription callback failed', error);\n }\n }\n\n private async subscribe(\n channel: GdexChannel,\n callback: EventCallback,\n options: object,\n ): Promise<GdexSubscription> {\n const params: Record<string, unknown> = { channel, ...options };\n const handle: MutableGdexSubscription = {\n subscriptionId: '',\n unsubscribe: async () => this.unsubscribe(subscription),\n };\n const subscription: ActiveSubscription = {\n serverId: '',\n params,\n callback,\n handle,\n active: true,\n };\n // Registered inside the response handler, not after the await: see PendingRequest.onResult.\n await this.send('gdex_subscribe', params, (value) => {\n const serverId = readSubscriptionId(value);\n subscription.serverId = serverId;\n handle.subscriptionId = serverId;\n this.activeSubscriptions.add(subscription);\n this.subscriptionsByServerId.set(serverId, subscription);\n });\n return handle;\n }\n\n private async unsubscribe(subscription: ActiveSubscription): Promise<void> {\n if (!subscription.active) return;\n subscription.active = false;\n this.activeSubscriptions.delete(subscription);\n this.subscriptionsByServerId.delete(subscription.serverId);\n if (this.isConnected()) {\n await this.request('gdex_unsubscribe', { subscriptionId: subscription.serverId });\n }\n }\n\n private startReconnectLoop(): void {\n if (this.reconnecting || this.intentionalClose) return;\n this.reconnecting = true;\n void this.reconnectLoop();\n }\n\n private async reconnectLoop(): Promise<void> {\n while (!this.intentionalClose && this.backoff.shouldRetry()) {\n const delay = this.backoff.nextDelayMs();\n this.backoff.recordAttempt();\n debugLog(\n `GDEX reconnect attempt ${this.backoff.attemptCount}/${this.backoff.maxAttempts} in ${delay}ms`,\n );\n await this.waitForReconnectDelay(delay);\n // `disconnect()` can run while the asynchronous delay is pending.\n if (this.intentionalClose) break;\n\n try {\n await this.openSocket();\n await this.resubscribeAll();\n this.backoff.reset();\n this.reconnecting = false;\n return;\n } catch (error) {\n debugLog(\n `GDEX reconnect failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n this.reconnecting = false;\n if (!this.intentionalClose) debugLog('GDEX reconnection attempts exhausted');\n }\n\n private async resubscribeAll(): Promise<void> {\n this.subscriptionsByServerId.clear();\n for (const subscription of this.activeSubscriptions) {\n if (!subscription.active) continue;\n const params = { ...subscription.params, snapshot: true };\n const result = await this.resubscribeWithRateLimitRetry(params, (value) => {\n const serverId = readSubscriptionId(value);\n // The consumer can unsubscribe while the asynchronous subscribe request is pending.\n if (!subscription.active) return;\n subscription.serverId = serverId;\n subscription.handle.subscriptionId = serverId;\n this.subscriptionsByServerId.set(serverId, subscription);\n });\n if (!subscription.active) {\n await this.request('gdex_unsubscribe', { subscriptionId: result.subscriptionId });\n }\n }\n }\n\n private async resubscribeWithRateLimitRetry(\n params: Record<string, unknown>,\n onResult: (value: unknown) => void,\n ): Promise<{ subscriptionId: string }> {\n for (let attempt = 0; ; attempt++) {\n try {\n return await this.send('gdex_subscribe', params, onResult);\n } catch (error) {\n if (\n !(error instanceof GdexStreamError) ||\n error.code !== RATE_LIMITED ||\n attempt >= MAX_RESUBSCRIBE_RATE_RETRIES\n ) {\n throw error;\n }\n const retryAfterMs = this.readRetryAfterMs(error.data);\n debugLog(`GDEX resubscribe rate limited; retrying in ${retryAfterMs}ms`);\n await new Promise<void>((resolve) => setTimeout(resolve, retryAfterMs));\n }\n }\n }\n\n private waitForReconnectDelay(delay: number): Promise<void> {\n return new Promise((resolve) => {\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = undefined;\n this.reconnectDelayResolve = undefined;\n resolve();\n }, delay);\n this.reconnectDelayResolve = resolve;\n });\n }\n\n private clearReconnectTimer(): void {\n if (this.reconnectTimer !== undefined) clearTimeout(this.reconnectTimer);\n this.reconnectTimer = undefined;\n const resolve = this.reconnectDelayResolve;\n this.reconnectDelayResolve = undefined;\n resolve?.();\n }\n\n private rejectPending(error: Error): void {\n for (const pending of this.pending.values()) {\n clearTimeout(pending.timeout);\n pending.reject(error);\n }\n this.pending.clear();\n }\n\n private readRetryAfterMs(data: unknown): number {\n if (\n !this.isRecord(data) ||\n typeof data['retryAfterMs'] !== 'number' ||\n data['retryAfterMs'] < 0\n )\n return 1_000;\n return data['retryAfterMs'];\n }\n\n private isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"gswap.js","names":["_defineProperty","GSwap","environment","GSWAP_ENVIRONMENTS","options","trimTrailingSlash","userRef","httpClient","HttpClient","ChainGateway","lenient","Symbols","resolver","CanonicalSymbols","Quoting","Pools","keyOptions","Positions","Swaps","Assets","Market","Leaderboard","Onramper","DexFees","GdexStreamService","value"],"sources":["../../../src/classes/gswap.ts"],"sourcesContent":["import { Assets } from './assets.js';\nimport { DexFees } from './dex_fees.js';\nimport { ChainGateway } from './gateway.js';\nimport { GdexStreamService } from './gdex_stream.js';\nimport { HttpClient } from './http_client.js';\nimport { Leaderboard } from './leaderboard.js';\nimport { Market } from './market.js';\nimport { Onramper } from './onramper.js';\nimport { Pools } from './pools.js';\nimport { Positions } from './positions.js';\nimport { Quoting } from './quoting.js';\nimport type { GalaChainSigner, HttpRequestor, WireOptions } from '@gala-chain/gala-core-sdk';\nimport { CanonicalSymbols, Symbols } from './symbols.js';\nimport type { InvalidEntryHandler } from '../utils/lenient.js';\nimport { userRef } from '../utils/validation.js';\nimport { Swaps } from './swaps.js';\nimport type { GSwapEnv } from '../types/env.js';\nimport { GSWAP_ENVIRONMENTS } from '../types/env.js';\n\n/** Options for constructing a backend-routed SDK client. */\nexport interface GSwapOptions {\n /** Signs every write. Without one, reads work and any write throws `NO_SIGNER`. */\n signer?: GalaChainSigner | undefined;\n /**\n * The selected account, sent as `X-Wallet-Address` on writes (attribution only; the chain\n * recovers the signer from the signature). Required for shadow writes. Any spelling of an\n * Ethereum account (`eth|<40 hex>` in any casing, `eth|0x...`, `0x...`, bare hex) is sent as\n * `eth|<EIP-55>`, `client|<id>` as written; a mixed-case spelling that is not the checksum is\n * refused at construction (`INVALID_WALLET_ADDRESS`).\n */\n walletAddress?: string | undefined;\n /** `'stage'` or `'prod'` swap backend preset; defaults to `'prod'`. */\n env?: GSwapEnv | undefined;\n /** Overrides the preset's swap backend base URL (a test or private deployment). */\n dexBackendBaseUrl?: string | undefined;\n /** HTTP transport for every request; defaults to the global `fetch`. */\n httpRequestor?: HttpRequestor | undefined;\n /** Request timeout in milliseconds (default 30 000); `Infinity` arms no timer. */\n chainCallTimeoutMs?: number | undefined;\n /** Query encoding and `User-Agent` of the read requests (see {@link WireOptions}). */\n wire?: WireOptions | undefined;\n /**\n * How tokens are turned into trading symbols. `'registry'` (default) reads the symbol registry\n * once (`GET /v2/trade/symbols`, cached) and accepts symbols or class keys. `'canonical'` makes\n * no registry request: the caller passes the backend's canonical trading symbols (from a quote,\n * an estimate or a position), reads send tokens and ticks exactly as given (composite keys\n * included) and the backend orients the pair; see {@link CanonicalSymbols}.\n */\n symbolResolution?: 'registry' | 'canonical' | undefined;\n /**\n * Told about every row of a pool, symbol, position, price, price-series, season or leaderboard\n * answer that was skipped as unusable (see {@link InvalidEntryHandler}); without it such rows are\n * skipped silently.\n */\n onInvalidEntry?: InvalidEntryHandler | undefined;\n /** Realtime stream endpoint. Defaults to the environment's, or is derived from `dexBackendBaseUrl`. */\n dexBackendWsUrl?: string | undefined;\n /** WebSocket constructor for the realtime stream; required on Node.js older than 22. */\n webSocketImpl?: unknown;\n /**\n * `x-api-key` for the privileged stream view; server-side only, and it requires `webSocketImpl`\n * (see `GdexStreamConfig.apiKey`).\n */\n streamApiKey?: string | undefined;\n /**\n * Prefix of every write's `uniqueKey` (`<prefix><uuid>`). Defaults to `galaswap-operation-`,\n * what frontend_v2 and wallet_extension send; defi-backend does not depend on it.\n */\n uniqueKeyPrefix?: string | undefined;\n}\n\n/**\n * Main entry point for the current GalaChainDex SDK: one client bundling the services that read\n * and write through the swap backend (`quoting`, `swaps`, `pools`, `positions`, `assets`,\n * `market`, `leaderboard`, `onramper`, `symbols`, `fees`, `stream`). See {@link GSwapOptions} for\n * configuration.\n */\nexport class GSwap {\n /** The swap backend base URL every request goes to, without a trailing slash. */\n public readonly dexBackendBaseUrl: string;\n /** The request timeout in milliseconds (default 30 000; `Infinity` arms no timer). */\n public readonly chainCallTimeoutMs: number;\n /** The signer given at construction, if any. */\n public readonly signer: GalaChainSigner | undefined;\n /**\n * The account given at construction, if any, in the gateway's spelling (`eth|<EIP-55>`;\n * `client|<id>` as given).\n */\n public readonly walletAddress: string | undefined;\n /** The chain-gateway client every other service reads and writes through. */\n public readonly gateway: ChainGateway;\n /** The trading-symbol registry. */\n public readonly symbols: Symbols;\n /** Read-only exact-input and exact-output quotes. */\n public readonly quoting: Quoting;\n /** Pool, slot0 and composite-pool reads. */\n public readonly pools: Pools;\n /** Position reads, liquidity estimates and liquidity / pool-creation writes. */\n public readonly positions: Positions;\n /** Queued swaps (`RequestTrade`), dark swaps and swap confirmation. */\n public readonly swaps: Swaps;\n /** User token balances. */\n public readonly assets: Assets;\n /** USD prices, price series and the platform volume totals. */\n public readonly market: Market;\n /** Trading-competition seasons and rankings. */\n public readonly leaderboard: Leaderboard;\n /** The Onramper (fiat on-ramp) widget URL. */\n public readonly onramper: Onramper;\n /** Network-fee quotes (DryRun) signed with this client's signer. */\n public readonly fees: DexFees;\n /** The realtime stream endpoint {@link GSwap.stream} connects to. */\n public readonly dexBackendWsUrl: string;\n private readonly webSocketImpl: unknown;\n private readonly streamApiKey: string | undefined;\n private streamClient: GdexStreamService | undefined;\n\n /**\n * Create an SDK client for prod (the default) or stage, or for a `dexBackendBaseUrl` of your own.\n *\n * @example\n * ```typescript\n * const gSwap = new GSwap({ env: 'stage' });\n * const symbols = await gSwap.symbols.list();\n * ```\n */\n constructor(options?: GSwapOptions) {\n const environment = GSWAP_ENVIRONMENTS[options?.env ?? 'prod'];\n this.dexBackendBaseUrl = trimTrailingSlash(\n options?.dexBackendBaseUrl ?? environment.dexBackendBaseUrl,\n );\n this.chainCallTimeoutMs = options?.chainCallTimeoutMs ?? 30_000;\n this.signer = options?.signer;\n this.walletAddress =\n options?.walletAddress === undefined\n ? undefined\n : userRef(options.walletAddress, 'walletAddress');\n\n const httpClient = new HttpClient(\n options?.httpRequestor,\n this.chainCallTimeoutMs,\n options?.wire,\n );\n this.gateway = new ChainGateway({\n dexBackendBaseUrl: this.dexBackendBaseUrl,\n chainCallTimeoutMs: this.chainCallTimeoutMs,\n ...(options?.wire === undefined ? {} : { wire: options.wire }),\n ...(options?.httpRequestor === undefined ? {} : { httpRequestor: options.httpRequestor }),\n ...(this.walletAddress === undefined ? {} : { walletAddress: this.walletAddress }),\n });\n const lenient = { onInvalidEntry: options?.onInvalidEntry };\n this.symbols = new Symbols(this.gateway, lenient);\n // The services resolve tokens through the registry by default; the offline resolver sends the\n // caller's canonical symbols and keys as they are and never reads the registry.\n const resolver =\n options?.symbolResolution === 'canonical' ? new CanonicalSymbols() : this.symbols;\n this.quoting = new Quoting(this.gateway, resolver);\n this.pools = new Pools(this.gateway, resolver, lenient);\n const keyOptions = { uniqueKeyPrefix: options?.uniqueKeyPrefix, ...lenient };\n this.positions = new Positions(\n this.gateway,\n resolver,\n this.signer,\n this.walletAddress,\n keyOptions,\n );\n this.swaps = new Swaps(this.gateway, resolver, this.signer, this.walletAddress, keyOptions);\n this.assets = new Assets(this.dexBackendBaseUrl, httpClient);\n this.market = new Market(this.dexBackendBaseUrl, httpClient, lenient);\n this.leaderboard = new Leaderboard(this.dexBackendBaseUrl, httpClient, lenient);\n this.onramper = new Onramper(this.dexBackendBaseUrl, httpClient);\n this.fees = new DexFees(this.gateway, this.signer);\n this.dexBackendWsUrl =\n options?.dexBackendWsUrl ??\n (options?.dexBackendBaseUrl === undefined\n ? environment.dexBackendWsUrl\n : `${this.dexBackendBaseUrl.replace(/^http/u, 'ws')}/ws`);\n this.webSocketImpl = options?.webSocketImpl;\n this.streamApiKey = options?.streamApiKey;\n }\n\n /**\n * The realtime stream client for this environment (created on first use, not connected until you\n * call `connect()`).\n *\n * @example\n * ```typescript\n * const gSwap = new GSwap({ env: 'stage' });\n * await gSwap.stream.connect();\n * await gSwap.stream.subscribeLiquidity((event) => console.log(event.action, event.amount0));\n * ```\n */\n get stream(): GdexStreamService {\n this.streamClient ??= new GdexStreamService({\n url: this.dexBackendWsUrl,\n ...(this.webSocketImpl === undefined ? {} : { webSocketImpl: this.webSocketImpl }),\n ...(this.streamApiKey === undefined ? {} : { apiKey: this.streamApiKey }),\n });\n return this.streamClient;\n }\n}\n\nfunction trimTrailingSlash(value: string): string {\n return value.replace(/\\/$/u, '');\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"gswap.js","names":["_defineProperty","GSwap","environment","GSWAP_ENVIRONMENTS","options","trimTrailingSlash","userRef","httpClient","HttpClient","ChainGateway","lenient","Symbols","resolver","CanonicalSymbols","Quoting","Pools","keyOptions","Positions","Swaps","Assets","Market","Leaderboard","Onramper","DexFees","GdexStreamService","value"],"sources":["../../../src/classes/gswap.ts"],"sourcesContent":["import { Assets } from './assets.js';\nimport { DexFees } from './dex_fees.js';\nimport { ChainGateway } from './gateway.js';\nimport { GdexStreamService } from './gdex_stream.js';\nimport { HttpClient } from './http_client.js';\nimport { Leaderboard } from './leaderboard.js';\nimport { Market } from './market.js';\nimport { Onramper } from './onramper.js';\nimport { Pools } from './pools.js';\nimport { Positions } from './positions.js';\nimport { Quoting } from './quoting.js';\nimport type { GalaChainSigner, HttpRequestor, WireOptions } from '@gala-chain/gala-core-sdk';\nimport { CanonicalSymbols, Symbols } from './symbols.js';\nimport type { InvalidEntryHandler } from '../utils/lenient.js';\nimport { userRef } from '../utils/validation.js';\nimport { Swaps } from './swaps.js';\nimport type { GSwapEnv } from '../types/env.js';\nimport { GSWAP_ENVIRONMENTS } from '../types/env.js';\n\n/** Options for constructing a backend-routed SDK client. */\nexport interface GSwapOptions {\n /** Signs every write. Without one, reads work and any write throws `NO_SIGNER`. */\n signer?: GalaChainSigner | undefined;\n /**\n * The selected account, sent as `X-Wallet-Address` on writes (attribution only; the chain\n * recovers the signer from the signature). Required for shadow writes. Any spelling of an\n * Ethereum account (`eth|<40 hex>` in any casing, `0x...`, bare hex) is sent as `eth|<EIP-55>`,\n * `client|<id>` as written; a mixed-case spelling that is not the checksum is refused at\n * construction (`INVALID_WALLET_ADDRESS`), and `eth|0x...` (not an address form) is refused as\n * core's `INVALID_ETH_USER_REF_PREFIX`.\n */\n walletAddress?: string | undefined;\n /** `'stage'` or `'prod'` swap backend preset; defaults to `'prod'`. */\n env?: GSwapEnv | undefined;\n /** Overrides the preset's swap backend base URL (a test or private deployment). */\n dexBackendBaseUrl?: string | undefined;\n /** HTTP transport for every request; defaults to the global `fetch`. */\n httpRequestor?: HttpRequestor | undefined;\n /** Request timeout in milliseconds (default 30 000); `Infinity` arms no timer. */\n chainCallTimeoutMs?: number | undefined;\n /** Query encoding and `User-Agent` of the read requests (see {@link WireOptions}). */\n wire?: WireOptions | undefined;\n /**\n * How tokens are turned into trading symbols. `'registry'` (default) reads the symbol registry\n * once (`GET /v2/trade/symbols`, cached) and accepts symbols or class keys. `'canonical'` makes\n * no registry request: the caller passes the backend's canonical trading symbols (from a quote,\n * an estimate or a position), reads send tokens and ticks exactly as given (composite keys\n * included) and the backend orients the pair; see {@link CanonicalSymbols}.\n */\n symbolResolution?: 'registry' | 'canonical' | undefined;\n /**\n * Told about every row of a pool, symbol, position, price, price-series, season or leaderboard\n * answer that was skipped as unusable (see {@link InvalidEntryHandler}); without it such rows are\n * skipped silently.\n */\n onInvalidEntry?: InvalidEntryHandler | undefined;\n /** Realtime stream endpoint. Defaults to the environment's, or is derived from `dexBackendBaseUrl`. */\n dexBackendWsUrl?: string | undefined;\n /** WebSocket constructor for the realtime stream; required on Node.js older than 22. */\n webSocketImpl?: unknown;\n /**\n * `x-api-key` for the privileged stream view; server-side only, and it requires `webSocketImpl`\n * (see `GdexStreamConfig.apiKey`).\n */\n streamApiKey?: string | undefined;\n /**\n * Prefix of every write's `uniqueKey` (`<prefix><uuid>`). Defaults to `galaswap-operation-`,\n * what the Gala Wallet and the gSwap web app send; the backend does not depend on it.\n */\n uniqueKeyPrefix?: string | undefined;\n}\n\n/**\n * Main entry point for the current GalaChainDex SDK: one client bundling the services that read\n * and write through the swap backend (`quoting`, `swaps`, `pools`, `positions`, `assets`,\n * `market`, `leaderboard`, `onramper`, `symbols`, `fees`, `stream`). See {@link GSwapOptions} for\n * configuration.\n */\nexport class GSwap {\n /** The swap backend base URL every request goes to, without a trailing slash. */\n public readonly dexBackendBaseUrl: string;\n /** The request timeout in milliseconds (default 30 000; `Infinity` arms no timer). */\n public readonly chainCallTimeoutMs: number;\n /** The signer given at construction, if any. */\n public readonly signer: GalaChainSigner | undefined;\n /**\n * The account given at construction, if any, in the gateway's spelling (`eth|<EIP-55>`;\n * `client|<id>` as given).\n */\n public readonly walletAddress: string | undefined;\n /** The chain-gateway client every other service reads and writes through. */\n public readonly gateway: ChainGateway;\n /** The trading-symbol registry. */\n public readonly symbols: Symbols;\n /** Read-only exact-input and exact-output quotes. */\n public readonly quoting: Quoting;\n /** Pool, slot0 and composite-pool reads. */\n public readonly pools: Pools;\n /** Position reads, liquidity estimates and liquidity / pool-creation writes. */\n public readonly positions: Positions;\n /** Queued swaps (`RequestTrade`), dark swaps and swap confirmation. */\n public readonly swaps: Swaps;\n /** User token balances. */\n public readonly assets: Assets;\n /** USD prices, price series and the platform volume totals. */\n public readonly market: Market;\n /** Trading-competition seasons and rankings. */\n public readonly leaderboard: Leaderboard;\n /** The Onramper (fiat on-ramp) widget URL. */\n public readonly onramper: Onramper;\n /** Network-fee quotes (DryRun) signed with this client's signer. */\n public readonly fees: DexFees;\n /** The realtime stream endpoint {@link GSwap.stream} connects to. */\n public readonly dexBackendWsUrl: string;\n private readonly webSocketImpl: unknown;\n private readonly streamApiKey: string | undefined;\n private streamClient: GdexStreamService | undefined;\n\n /**\n * Create an SDK client for prod (the default) or stage, or for a `dexBackendBaseUrl` of your own.\n *\n * @example\n * ```typescript\n * const gSwap = new GSwap({ env: 'stage' });\n * const symbols = await gSwap.symbols.list();\n * ```\n */\n constructor(options?: GSwapOptions) {\n const environment = GSWAP_ENVIRONMENTS[options?.env ?? 'prod'];\n this.dexBackendBaseUrl = trimTrailingSlash(\n options?.dexBackendBaseUrl ?? environment.dexBackendBaseUrl,\n );\n this.chainCallTimeoutMs = options?.chainCallTimeoutMs ?? 30_000;\n this.signer = options?.signer;\n this.walletAddress =\n options?.walletAddress === undefined\n ? undefined\n : userRef(options.walletAddress, 'walletAddress');\n\n const httpClient = new HttpClient(\n options?.httpRequestor,\n this.chainCallTimeoutMs,\n options?.wire,\n );\n this.gateway = new ChainGateway({\n dexBackendBaseUrl: this.dexBackendBaseUrl,\n chainCallTimeoutMs: this.chainCallTimeoutMs,\n ...(options?.wire === undefined ? {} : { wire: options.wire }),\n ...(options?.httpRequestor === undefined ? {} : { httpRequestor: options.httpRequestor }),\n ...(this.walletAddress === undefined ? {} : { walletAddress: this.walletAddress }),\n });\n const lenient = { onInvalidEntry: options?.onInvalidEntry };\n this.symbols = new Symbols(this.gateway, lenient);\n // The services resolve tokens through the registry by default; the offline resolver sends the\n // caller's canonical symbols and keys as they are and never reads the registry.\n const resolver =\n options?.symbolResolution === 'canonical' ? new CanonicalSymbols() : this.symbols;\n this.quoting = new Quoting(this.gateway, resolver);\n this.pools = new Pools(this.gateway, resolver, lenient);\n const keyOptions = { uniqueKeyPrefix: options?.uniqueKeyPrefix, ...lenient };\n this.positions = new Positions(\n this.gateway,\n resolver,\n this.signer,\n this.walletAddress,\n keyOptions,\n );\n this.swaps = new Swaps(this.gateway, resolver, this.signer, this.walletAddress, keyOptions);\n this.assets = new Assets(this.dexBackendBaseUrl, httpClient);\n this.market = new Market(this.dexBackendBaseUrl, httpClient, lenient);\n this.leaderboard = new Leaderboard(this.dexBackendBaseUrl, httpClient, lenient);\n this.onramper = new Onramper(this.dexBackendBaseUrl, httpClient);\n this.fees = new DexFees(this.gateway, this.signer);\n this.dexBackendWsUrl =\n options?.dexBackendWsUrl ??\n (options?.dexBackendBaseUrl === undefined\n ? environment.dexBackendWsUrl\n : `${this.dexBackendBaseUrl.replace(/^http/u, 'ws')}/ws`);\n this.webSocketImpl = options?.webSocketImpl;\n this.streamApiKey = options?.streamApiKey;\n }\n\n /**\n * The realtime stream client for this environment (created on first use, not connected until you\n * call `connect()`).\n *\n * @example\n * ```typescript\n * const gSwap = new GSwap({ env: 'stage' });\n * await gSwap.stream.connect();\n * await gSwap.stream.subscribeLiquidity((event) => console.log(event.action, event.amount0));\n * ```\n */\n get stream(): GdexStreamService {\n this.streamClient ??= new GdexStreamService({\n url: this.dexBackendWsUrl,\n ...(this.webSocketImpl === undefined ? {} : { webSocketImpl: this.webSocketImpl }),\n ...(this.streamApiKey === undefined ? {} : { apiKey: this.streamApiKey }),\n });\n return this.streamClient;\n }\n}\n\nfunction trimTrailingSlash(value: string): string {\n return value.replace(/\\/$/u, '');\n}\n"],"mappings":"2uBA8EA,IAAaC,EAAb,KAAmB,CAiDjB,YAAY,EAAwB,CA/CpBD,EAAA,KAAA,oBAAA,IAAA,EAAA,EAEAA,EAAA,KAAA,qBAAA,IAAA,EAAA,EAEAA,EAAA,KAAA,SAAA,IAAA,EAAA,EAKAA,EAAA,KAAA,gBAAA,IAAA,EAAA,EAEAA,EAAA,KAAA,UAAA,IAAA,EAAA,EAEAA,EAAA,KAAA,UAAA,IAAA,EAAA,EAEAA,EAAA,KAAA,UAAA,IAAA,EAAA,EAEAA,EAAA,KAAA,QAAA,IAAA,EAAA,EAEAA,EAAA,KAAA,YAAA,IAAA,EAAA,EAEAA,EAAA,KAAA,QAAA,IAAA,EAAA,EAEAA,EAAA,KAAA,SAAA,IAAA,EAAA,EAEAA,EAAA,KAAA,SAAA,IAAA,EAAA,EAEAA,EAAA,KAAA,cAAA,IAAA,EAAA,EAEAA,EAAA,KAAA,WAAA,IAAA,EAAA,EAEAA,EAAA,KAAA,OAAA,IAAA,EAAA,EAEAA,EAAA,KAAA,kBAAA,IAAA,EAAA,EACCA,EAAA,KAAA,gBAAA,IAAA,EAAA,EACAA,EAAA,KAAA,eAAA,IAAA,EAAA,EACTA,EAAA,KAAA,eAAA,IAAA,EAAA,EAYN,IAAME,EAAcC,EAAmBC,GAAS,KAAO,QACvD,KAAK,kBAAoBC,EACvBD,GAAS,mBAAqBF,EAAY,iBAC5C,EACA,KAAK,mBAAqBE,GAAS,oBAAsB,IACzD,KAAK,OAASA,GAAS,OACvB,KAAK,cACHA,GAAS,gBAAkB,IAAA,GACvB,IAAA,GACAE,EAAQF,EAAQ,cAAe,eAAe,EAEpD,IAAMG,EAAa,IAAIC,EACrBJ,GAAS,cACT,KAAK,mBACLA,GAAS,IACX,EACA,KAAK,QAAU,IAAIK,EAAa,CAC9B,kBAAmB,KAAK,kBACxB,mBAAoB,KAAK,mBACzB,GAAIL,GAAS,OAAS,IAAA,GAAY,CAAC,EAAI,CAAE,KAAMA,EAAQ,IAAK,EAC5D,GAAIA,GAAS,gBAAkB,IAAA,GAAY,CAAC,EAAI,CAAE,cAAeA,EAAQ,aAAc,EACvF,GAAI,KAAK,gBAAkB,IAAA,GAAY,CAAC,EAAI,CAAE,cAAe,KAAK,aAAc,CAClF,CAAC,EACD,IAAMM,EAAU,CAAE,eAAgBN,GAAS,cAAe,EAC1D,KAAK,QAAU,IAAIO,EAAQ,KAAK,QAASD,CAAO,EAGhD,IAAME,EACJR,GAAS,mBAAqB,YAAc,IAAIS,EAAqB,KAAK,QAC5E,KAAK,QAAU,IAAIC,EAAQ,KAAK,QAASF,CAAQ,EACjD,KAAK,MAAQ,IAAIG,EAAM,KAAK,QAASH,EAAUF,CAAO,EACtD,IAAMM,EAAa,CAAE,gBAAiBZ,GAAS,gBAAiB,GAAGM,CAAQ,EAC3E,KAAK,UAAY,IAAIO,EACnB,KAAK,QACLL,EACA,KAAK,OACL,KAAK,cACLI,CACF,EACA,KAAK,MAAQ,IAAIE,EAAM,KAAK,QAASN,EAAU,KAAK,OAAQ,KAAK,cAAeI,CAAU,EAC1F,KAAK,OAAS,IAAIG,EAAO,KAAK,kBAAmBZ,CAAU,EAC3D,KAAK,OAAS,IAAIa,EAAO,KAAK,kBAAmBb,EAAYG,CAAO,EACpE,KAAK,YAAc,IAAIW,EAAY,KAAK,kBAAmBd,EAAYG,CAAO,EAC9E,KAAK,SAAW,IAAIY,EAAS,KAAK,kBAAmBf,CAAU,EAC/D,KAAK,KAAO,IAAIgB,EAAQ,KAAK,QAAS,KAAK,MAAM,EACjD,KAAK,gBACHnB,GAAS,kBACRA,GAAS,oBAAsB,IAAA,GAC5BF,EAAY,gBACZ,GAAG,KAAK,kBAAkB,QAAQ,SAAU,IAAI,EAAE,MACxD,KAAK,cAAgBE,GAAS,cAC9B,KAAK,aAAeA,GAAS,YAC/B,CAaA,IAAI,QAA4B,CAM9B,OALA,KAAK,eAAL,KAAK,aAAiB,IAAIoB,EAAkB,CAC1C,IAAK,KAAK,gBACV,GAAI,KAAK,gBAAkB,IAAA,GAAY,CAAC,EAAI,CAAE,cAAe,KAAK,aAAc,EAChF,GAAI,KAAK,eAAiB,IAAA,GAAY,CAAC,EAAI,CAAE,OAAQ,KAAK,YAAa,CACzE,CAAC,GACM,KAAK,YACd,CACF,EAEA,SAASnB,EAAkB,EAAuB,CAChD,OAAOoB,EAAM,QAAQ,OAAQ,EAAE,CACjC"}
@@ -1 +1 @@
1
- {"version":3,"file":"pending_swap_fate.js","names":["PENDING_SWAP_FATES","FATE_OF_OUTCOME","pendingSwapFateOfOutcome","outcome","pendingSwapFateFor","classifyFailure","error"],"sources":["../../../src/classes/pending_swap_fate.ts"],"sourcesContent":["import { classifyFailure } from '../utils/failure_classification.js';\nimport type { FailureOutcome } from '@gala-chain/gala-core-sdk';\n\n/**\n * What to do with a pending swap after confirming it failed (the answer of\n * {@link pendingSwapFateFor}).\n *\n * - `forget`: the chain REFUSED the trade, so it did not happen. Drop it AND tell the user.\n * - `forget_quietly`: the confirmation can never succeed (the key is refused: `invalid_input`).\n * Drop it without claiming anything about the trade.\n * - `keep`: the trade may still have been applied (a timeout, a broken connection, a gateway\n * hiccup, anything not proven). Leave it pending and look again later.\n *\n * This module holds the decision and nothing of the store, so an app shell that only needs the\n * fate pays for `classifyFailure` and this table, not for `PendingSwapStore`.\n */\nexport const PENDING_SWAP_FATES = ['forget', 'forget_quietly', 'keep'] as const;\n\n/** One member of {@link PENDING_SWAP_FATES}. */\nexport type PendingSwapFate = (typeof PENDING_SWAP_FATES)[number];\n\n/**\n * The fate of a pending swap for each failure OUTCOME. Only a chain rejection proves the trade did\n * not happen; an input that can never be confirmed is dropped quietly; every other outcome leaves\n * the trade possibly applied. Typed over `FailureOutcome`, so a new outcome does not compile until\n * it is decided here.\n */\nconst FATE_OF_OUTCOME = {\n rejected: 'forget',\n invalid_input: 'forget_quietly',\n unknown: 'keep',\n not_queued: 'keep',\n not_sent: 'keep',\n transient: 'keep',\n} as const satisfies Record<FailureOutcome, PendingSwapFate>;\n\n/**\n * {@link pendingSwapFateFor} from an outcome the app already holds (for example the\n * `classifyFailure(error).outcome` it stored on its own failure wrapper). An outcome this SDK\n * version does not know (a newer SDK at run time) is `keep`: never forget what may have happened.\n */\nexport function pendingSwapFateOfOutcome(outcome: FailureOutcome): PendingSwapFate {\n return Object.hasOwn(FATE_OF_OUTCOME, outcome) ? FATE_OF_OUTCOME[outcome] : 'keep';\n}\n\n/**\n * The fate of a queued swap whose confirmation (`confirmAppliedTrade`) just failed with `error`,\n * from the SDK's classification alone: `forget` for a chain rejection (`rejected`), `forget_quietly`\n * for a key that can never be confirmed (`invalid_input`), `keep` for every other outcome\n * (`unknown`, `not_queued`, `not_sent`, `transient`) and for any value the SDK cannot judge. It\n * accepts an SDK error or a plain-object view of one (see `FailureView`). It replaces the wallet's\n * `pendingSwapActionFor` and frontend_v2's `confirmationStanceOf`.\n *\n * @example\n * ```ts\n * try {\n * await confirmAppliedTrade(client, swap);\n * } catch (error) {\n * if ((await store.settle(swap.uniqueKey, error)) === 'forget') tellTheUserItFailed();\n * }\n * ```\n */\nexport function pendingSwapFateFor(error: unknown): PendingSwapFate {\n return pendingSwapFateOfOutcome(classifyFailure(error, { afterQueued: true }).outcome);\n}\n"],"mappings":"qEAgBA,MAAaA,EAAqB,CAAC,SAAU,iBAAkB,MAAM,EAW/DC,EAAkB,CACtB,SAAU,SACV,cAAe,iBACf,QAAS,OACT,WAAY,OACZ,SAAU,OACV,UAAW,MACb,EAOA,SAAgBC,EAAyB,EAA0C,CACjF,OAAO,OAAO,OAAOD,EAAiBE,CAAO,EAAIF,EAAgBE,GAAW,MAC9E,CAmBA,SAAgBC,EAAmB,EAAiC,CAClE,OAAOF,EAAyBG,EAAgBC,EAAO,CAAE,YAAa,EAAK,CAAC,CAAC,CAAC,OAAO,CACvF"}
1
+ {"version":3,"file":"pending_swap_fate.js","names":["PENDING_SWAP_FATES","FATE_OF_OUTCOME","pendingSwapFateOfOutcome","outcome","pendingSwapFateFor","classifyFailure","error"],"sources":["../../../src/classes/pending_swap_fate.ts"],"sourcesContent":["import { classifyFailure } from '../utils/failure_classification.js';\nimport type { FailureOutcome } from '@gala-chain/gala-core-sdk';\n\n/**\n * What to do with a pending swap after confirming it failed (the answer of\n * {@link pendingSwapFateFor}).\n *\n * - `forget`: the chain REFUSED the trade, so it did not happen. Drop it AND tell the user.\n * - `forget_quietly`: the confirmation can never succeed (the key is refused: `invalid_input`).\n * Drop it without claiming anything about the trade.\n * - `keep`: the trade may still have been applied (a timeout, a broken connection, a gateway\n * hiccup, anything not proven). Leave it pending and look again later.\n *\n * This module holds the decision and nothing of the store, so an app shell that only needs the\n * fate pays for `classifyFailure` and this table, not for `PendingSwapStore`.\n */\nexport const PENDING_SWAP_FATES = ['forget', 'forget_quietly', 'keep'] as const;\n\n/** One member of {@link PENDING_SWAP_FATES}. */\nexport type PendingSwapFate = (typeof PENDING_SWAP_FATES)[number];\n\n/**\n * The fate of a pending swap for each failure OUTCOME. Only a chain rejection proves the trade did\n * not happen; an input that can never be confirmed is dropped quietly; every other outcome leaves\n * the trade possibly applied. Typed over `FailureOutcome`, so a new outcome does not compile until\n * it is decided here.\n */\nconst FATE_OF_OUTCOME = {\n rejected: 'forget',\n invalid_input: 'forget_quietly',\n unknown: 'keep',\n not_queued: 'keep',\n not_sent: 'keep',\n transient: 'keep',\n} as const satisfies Record<FailureOutcome, PendingSwapFate>;\n\n/**\n * {@link pendingSwapFateFor} from an outcome the app already holds (for example the\n * `classifyFailure(error).outcome` it stored on its own failure wrapper). An outcome this SDK\n * version does not know (a newer SDK at run time) is `keep`: never forget what may have happened.\n */\nexport function pendingSwapFateOfOutcome(outcome: FailureOutcome): PendingSwapFate {\n return Object.hasOwn(FATE_OF_OUTCOME, outcome) ? FATE_OF_OUTCOME[outcome] : 'keep';\n}\n\n/**\n * The fate of a queued swap whose confirmation (`confirmAppliedTrade`) just failed with `error`,\n * from the SDK's classification alone: `forget` for a chain rejection (`rejected`), `forget_quietly`\n * for a key that can never be confirmed (`invalid_input`), `keep` for every other outcome\n * (`unknown`, `not_queued`, `not_sent`, `transient`) and for any value the SDK cannot judge. It\n * accepts an SDK error or a plain-object view of one (see `FailureView`). It replaces the\n * hand-written \"forget it or keep it\" table an app would otherwise keep.\n *\n * @example\n * ```ts\n * try {\n * await confirmAppliedTrade(client, swap);\n * } catch (error) {\n * if ((await store.settle(swap.uniqueKey, error)) === 'forget') tellTheUserItFailed();\n * }\n * ```\n */\nexport function pendingSwapFateFor(error: unknown): PendingSwapFate {\n return pendingSwapFateOfOutcome(classifyFailure(error, { afterQueued: true }).outcome);\n}\n"],"mappings":"qEAgBA,MAAaA,EAAqB,CAAC,SAAU,iBAAkB,MAAM,EAW/DC,EAAkB,CACtB,SAAU,SACV,cAAe,iBACf,QAAS,OACT,WAAY,OACZ,SAAU,OACV,UAAW,MACb,EAOA,SAAgBC,EAAyB,EAA0C,CACjF,OAAO,OAAO,OAAOD,EAAiBE,CAAO,EAAIF,EAAgBE,GAAW,MAC9E,CAmBA,SAAgBC,EAAmB,EAAiC,CAClE,OAAOF,EAAyBG,EAAgBC,EAAO,CAAE,YAAa,EAAK,CAAC,CAAC,CAAC,OAAO,CACvF"}