@gala-chain/gswap-sdk 1.2.0-rc.8 → 1.2.0
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 +25 -10
- package/dist/browser/classes/dex_fees.js.map +1 -1
- package/dist/browser/classes/gateway.js.map +1 -1
- package/dist/browser/classes/gdex_stream.js.map +1 -1
- package/dist/browser/classes/gswap.js.map +1 -1
- package/dist/browser/classes/gswap_sdk_error.js.map +1 -1
- package/dist/browser/classes/pending_swap_fate.js +2 -0
- package/dist/browser/classes/pending_swap_fate.js.map +1 -0
- package/dist/browser/classes/pending_swaps.js +1 -1
- package/dist/browser/classes/pending_swaps.js.map +1 -1
- package/dist/browser/classes/positions.js +1 -1
- package/dist/browser/classes/positions.js.map +1 -1
- package/dist/browser/classes/shadow_submit.js +1 -1
- package/dist/browser/classes/shadow_submit.js.map +1 -1
- package/dist/browser/classes/signers.js.map +1 -1
- package/dist/browser/classes/storage_adapters.js +2 -0
- package/dist/browser/classes/storage_adapters.js.map +1 -0
- package/dist/browser/classes/submitted_transaction.js.map +1 -1
- package/dist/browser/classes/symbols.js +1 -1
- package/dist/browser/classes/symbols.js.map +1 -1
- package/dist/browser/index.js +1 -1
- package/dist/browser/utils/chain_dto_signing.js.map +1 -1
- package/dist/browser/utils/class_key.js +2 -0
- package/dist/browser/utils/class_key.js.map +1 -0
- package/dist/browser/utils/classification.js +2 -0
- package/dist/browser/utils/classification.js.map +1 -0
- package/dist/browser/utils/dto_builders.js +1 -1
- package/dist/browser/utils/dto_builders.js.map +1 -1
- package/dist/browser/utils/eip712.js.map +1 -1
- package/dist/browser/utils/failure_classification.js +1 -1
- package/dist/browser/utils/failure_classification.js.map +1 -1
- package/dist/browser/utils/failure_guards.js +2 -0
- package/dist/browser/utils/failure_guards.js.map +1 -0
- package/dist/browser/utils/ordering.js +1 -1
- package/dist/browser/utils/ordering.js.map +1 -1
- package/dist/browser/utils/tick_align.js +2 -0
- package/dist/browser/utils/tick_align.js.map +1 -0
- package/dist/browser/utils/tick_math.js +2 -0
- package/dist/browser/utils/tick_math.js.map +1 -0
- package/dist/browser/utils/tick_range.js +2 -0
- package/dist/browser/utils/tick_range.js.map +1 -0
- package/dist/browser/utils/ticks.js +1 -1
- package/dist/browser/utils/ticks.js.map +1 -1
- package/dist/browser/utils/transport.js +1 -1
- package/dist/browser/utils/transport.js.map +1 -1
- package/dist/browser/utils/validation.js +1 -1
- package/dist/browser/utils/validation.js.map +1 -1
- package/dist/browser/version.js +1 -1
- package/dist/browser/version.js.map +1 -1
- package/dist/index.cjs +641 -336
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +470 -186
- package/dist/index.d.ts +470 -186
- package/dist/index.mjs +636 -337
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gswap_sdk_error.js","names":[],"sources":["../../../src/classes/gswap_sdk_error.ts"],"sourcesContent":["import type { GalaChainTokenClassKey } from '../types/token.js';\nimport type { HTTPResponse } from '../types/http_requestor.js';\nimport { GSWAP_ERROR_BRAND } from '../utils/error_brand.js';\nimport { toFailureView } from '../utils/failure_view.js';\nimport { phaseOfView } from './error_phase_table.js';\nimport { serverMessageOfBody } from '../utils/server_message.js';\nimport {\n GSWAP_ERROR_CATALOG,\n renderGSwapErrorTemplate,\n type GSwapErrorCode,\n type GSwapErrorParams,\n} from './error_catalog.js';\nimport type { GSwapErrorPhase } from './error_phases.js';\n\n/**\n * The HTTP status of a version wall: the backend refuses an app version that is too old (HTTP 426\n * Upgrade Required). It is `UPDATE_REQUIRED` on every request the SDK sends, whatever else the body\n * says; a confirmation read keeps `CONFIRMATION_FAILED` because the write it confirms may be applied.\n */\nexport const UPDATE_REQUIRED_STATUS = 426;\n\n/**\n * The facts of an error that are not placeholders of its message: where it happened and what the\n * server said. All optional; none is localized text.\n */\nexport interface GSwapErrorContext {\n /** The HTTP status of the response that caused the error. */\n status?: number | undefined;\n /**\n * The response body: parsed JSON when the failing response had it, else the raw text (which\n * the error then exposes as {@link GSwapSDKError.bodyText}).\n */\n body?: unknown;\n /** The URL the failing request went to. */\n url?: string | undefined;\n /** The `uniqueKey` of the write this error is about: the handle for `confirm()` and recovery. */\n uniqueKey?: string | undefined;\n /** The transaction id of the queued request, `null` when unknown. */\n requestTransactionId?: string | null | undefined;\n /** The transaction id of the apply, `null` when unknown. */\n applyTransactionId?: string | null | undefined;\n /** The block the apply landed in, `null` when unknown. */\n blockNumber?: number | null | undefined;\n /** How long to wait before retrying, from the server's `Retry-After`. */\n retryAfterMs?: number | undefined;\n /** The error that caused this one (a transport failure, a wallet rejection). */\n cause?: unknown;\n /**\n * The phase of THIS occurrence when it differs from the code's catalog phase (see\n * {@link GSwapErrorPhase}): for example `UNKNOWN_TOKEN` is `pre_send` from the symbol registry\n * but `post_send` when it is derived from a quote answer.\n */\n phase?: GSwapErrorPhase | undefined;\n /** True when the failing operation was on the shadow (dark) path; see {@link GSwapSDKError.shadow}. */\n shadow?: boolean | undefined;\n /**\n * `false` for the failure of a READ (see {@link GSwapSDKError.write}); anything else, including\n * absent, is a write (fail-closed). Ignored when `uniqueKey` is set: that is a write.\n */\n write?: boolean | undefined;\n /** The wallet's own code on the error a signer threw; see {@link GSwapSDKError.walletCode}. */\n walletCode?: number | string | undefined;\n}\n\n/** What {@link GSwapSDKError.fromStatus} may be told about the HTTP answer. */\nexport interface GSwapStatusErrorOptions {\n /** The URL the request went to. */\n url?: string | undefined;\n /** The signed `uniqueKey` of the WRITE the answer is for; its presence makes this a write. */\n uniqueKey?: string | undefined;\n /** Say it outright: `true` for a write (needs `uniqueKey`), `false` for a read. */\n write?: boolean | undefined;\n /** The server's own text, for `HTTP_ERROR.params.serverMessage` (`''` when none). */\n serverMessage?: string | undefined;\n /** The response body, kept as `error.body` (or `bodyText` for a string). */\n body?: unknown;\n /** How long the server asked to wait (`Retry-After`), in milliseconds. */\n retryAfterMs?: number | undefined;\n}\n\n/**\n * Error raised by the SDK for validation, gateway, chain, or transaction failures.\n *\n * Branch on {@link code} (stable) and render {@link params} through your own localization.\n * {@link message} is English generated from the catalog template for logs and developers: it\n * is not stable and must never be shown to a user or matched on.\n */\nexport class GSwapSDKError<C extends GSwapErrorCode = GSwapErrorCode> extends Error {\n /** The stable, machine-readable code. */\n public readonly code: C;\n /** The named placeholders of the error: a flat object of primitives, safe to localize with. */\n public readonly params: GSwapErrorParams<C>;\n public readonly retryAfterMs: number | undefined;\n /** The HTTP status of the response that caused this error, when there was one. */\n public readonly status: number | undefined;\n /**\n * The response body parsed as JSON, when the failing response had one; `undefined` for a body\n * that was not JSON (see {@link bodyText}). Never part of {@link message}.\n */\n public readonly body: unknown;\n /**\n * The raw text of a failing response whose body was NOT JSON (a proxy's HTML 502, a WAF page,\n * an empty body). It is kept here for diagnostics and is never copied into {@link message},\n * so it cannot reach a user interface by accident.\n */\n public readonly bodyText: string | undefined;\n /** The URL the failing request went to, when there was one. */\n public readonly url: string | undefined;\n /** The `uniqueKey` of the write this error is about, when there is one. */\n public readonly uniqueKey: string | undefined;\n /** The transaction id of the queued request, when known. */\n public readonly requestTransactionId: string | null | undefined;\n /** The transaction id of the apply, when known. */\n public readonly applyTransactionId: string | null | undefined;\n /** The block the apply landed in, when known. */\n public readonly blockNumber: number | null | undefined;\n /**\n * How far the operation had got when this was thrown, against the request it exists to send\n * (see {@link GSwapErrorPhase}): `pre_send` means NOTHING was transmitted, so a write that\n * failed this way cannot have been queued; `in_flight` means the request left and no answer was\n * read; `post_send` means an answer was read or the write was already accepted. Every catalog\n * code declares its phase, and a throw site can refine it. Read it with\n * {@link isPreSendError}; `classifyFailure` already folds it into `outcome` and `retryable`.\n */\n public readonly phase: GSwapErrorPhase;\n /**\n * True when this error came from the shadow (dark) path: `swapShadow`, `submitSwapShadow` or\n * `ChainGateway.submitShadow`, whatever failed there (a validation error, the signer, the\n * transport, `SHADOW_NOT_QUEUED`). The classification is the same as on the public path; this\n * is how an app applies its own dark-swap policy (the wallet and frontend_v2 never offer a\n * public fallback or Retry after one). A shadow gateway refusal that RESOLVES (`{ ok: false }`)\n * is not an error and has no flag: the call itself says it was shadow.\n */\n public readonly shadow: boolean;\n /**\n * Whether this failure is a WRITE's, as the guards read it: `false` only for the failure of a\n * READ (a quote, a pool list, an apply-results read), which has no `uniqueKey` to confirm and\n * nothing pending, so a 5xx there is `transient` and retryable. It is `true` for everything else,\n * and always when {@link uniqueKey} is set. That is fail-closed: a failure that does not say it\n * is a read is judged a write, where a 5xx is `unknown` and never retried. Apps that carry an\n * error through their own wrapper copy this field next to `uniqueKey` (`FailureView.write`).\n */\n public readonly write: boolean;\n /**\n * On `SIGNING_FAILED`: the code the wallet put on the error its signer threw, when it set one: an\n * EIP-1193 / JSON-RPC number such as `4001` (the person declined) or `-32603`, or an ethers /\n * viem string such as `'ACTION_REJECTED'`. It is read from the `cause`'s own `code` and is\n * always a short primitive; `undefined` when the wallet sent none, and on every other code. It is\n * a typed field, not a param: it is data for an app's analytics or its own wallet-specific\n * wording, never part of `message`, and `params.reason` stays the stable thing to branch on.\n */\n public readonly walletCode: number | string | undefined;\n\n constructor(code: C, params: GSwapErrorParams<C>, context: GSwapErrorContext = {}) {\n super(\n renderGSwapErrorTemplate(GSWAP_ERROR_CATALOG[code].template, params),\n context.cause === undefined ? undefined : { cause: context.cause },\n );\n this.name = 'GSwapSDKError';\n this.code = code;\n this.params = params;\n this.retryAfterMs = context.retryAfterMs;\n this.status = context.status;\n this.body = typeof context.body === 'string' ? undefined : context.body;\n this.bodyText = typeof context.body === 'string' ? context.body : undefined;\n this.url = context.url;\n this.uniqueKey = context.uniqueKey;\n this.requestTransactionId = context.requestTransactionId;\n this.applyTransactionId = context.applyTransactionId;\n this.blockNumber = context.blockNumber;\n this.phase = context.phase ?? GSWAP_ERROR_CATALOG[code].phase;\n this.shadow = context.shadow ?? false;\n this.write =\n context.uniqueKey !== undefined && context.uniqueKey !== '' ? true : context.write !== false;\n this.walletCode = context.walletCode;\n }\n\n /**\n * The SDK error brand (`Symbol.for('@gala-chain/gswap-sdk/error')`), always `true`. The\n * dependency-free helpers (`isWalletUserRejection`, `isGSwapErrorLike`) recognise an SDK error\n * by it instead of `instanceof`, so they work across copies of the SDK and without importing\n * this class. It lives on the prototype: it is not an own property and is never copied or logged.\n */\n public get [GSWAP_ERROR_BRAND](): true {\n return true;\n }\n\n /**\n * True when the failure means \"that market, pool or position does not exist\" rather than\n * \"something went wrong\": an HTTP 404, a pair with no pool at any fee tier\n * (`NO_POOL_AVAILABLE`), or a token the DEX contract has no trading symbol for\n * (`UNKNOWN_TOKEN`). A pool that exists but is too thin (`INSUFFICIENT_LIQUIDITY`) is NOT\n * not-found.\n */\n public get notFound(): boolean {\n return (\n this.status === 404 || this.code === 'NO_POOL_AVAILABLE' || this.code === 'UNKNOWN_TOKEN'\n );\n }\n\n /** Create the standard missing-signer error. */\n public static noSignerError(): GSwapSDKError<'NO_SIGNER'> {\n return new GSwapSDKError('NO_SIGNER', {});\n }\n\n /** Create the error used when confirming a position write without a configured wallet address. */\n public static noWalletAddressError(): GSwapSDKError<'NO_WALLET_ADDRESS'> {\n return new GSwapSDKError('NO_WALLET_ADDRESS', {});\n }\n\n /**\n * Create the error used when no pool can quote the requested pair. `source` is the backend\n * answer that said so; its status and body are kept.\n */\n public static noPoolAvailableError(\n tokenIn: GalaChainTokenClassKey | string,\n tokenOut: GalaChainTokenClassKey | string,\n fee?: number,\n source?: GSwapSDKError,\n ): GSwapSDKError<'NO_POOL_AVAILABLE'> {\n return new GSwapSDKError(\n 'NO_POOL_AVAILABLE',\n { tokenIn: tokenText(tokenIn), tokenOut: tokenText(tokenOut), fee: feeText(fee) },\n sourceContext(source),\n );\n }\n\n /** Create the error used when a pool lacks liquidity for a quote. `source` as for {@link noPoolAvailableError}. */\n public static insufficientLiquidityError(\n tokenIn: GalaChainTokenClassKey | string,\n tokenOut: GalaChainTokenClassKey | string,\n fee?: number,\n source?: GSwapSDKError,\n ): GSwapSDKError<'INSUFFICIENT_LIQUIDITY'> {\n return new GSwapSDKError(\n 'INSUFFICIENT_LIQUIDITY',\n { tokenIn: tokenText(tokenIn), tokenOut: tokenText(tokenOut), fee: feeText(fee) },\n sourceContext(source),\n );\n }\n\n /**\n * Convert a chain-gateway bounce into a typed SDK error: the gateway's own code when the SDK\n * knows it, `GATEWAY_REJECTED` (with the code as `params.gatewayCode`) when it does not.\n * `serverMessage` is the gateway's English text: for logs, not for a user interface. For\n * `CHAIN_DISPATCH_FAILED`, `params.errorKey` is the chain's `ErrorKey` from the body's\n * top-level `errorKey` (`''` when the gateway sent none): map it, not the text.\n */\n public static fromGatewayBounce(\n gatewayCode: string,\n serverMessage: string,\n status: number,\n body: unknown,\n url: string,\n retryAfterMs?: number,\n uniqueKey?: string,\n ): GSwapSDKError {\n const context: GSwapErrorContext = { status, body, url, retryAfterMs, uniqueKey };\n const params = { serverMessage };\n switch (gatewayCode) {\n case 'METHOD_NOT_ALLOWED':\n return new GSwapSDKError('METHOD_NOT_ALLOWED', params, context);\n case 'SIGNATURE_INVALID':\n return new GSwapSDKError('SIGNATURE_INVALID', params, context);\n case 'SIGNER_MISMATCH':\n return new GSwapSDKError('SIGNER_MISMATCH', params, context);\n case 'DTO_INVALID':\n return new GSwapSDKError('DTO_INVALID', params, context);\n case 'BOUNDS_VIOLATION':\n return new GSwapSDKError('BOUNDS_VIOLATION', params, context);\n case 'SYMBOL_CONFLICT':\n return new GSwapSDKError('SYMBOL_CONFLICT', params, context);\n case 'RATE_LIMITED':\n return new GSwapSDKError('RATE_LIMITED', params, context);\n case 'CHAIN_DISPATCH_FAILED':\n // The gateway adds the chain's own machine-readable key as a top-level `errorKey`, only\n // when the chain supplied one; an older gateway sends none, which is the empty string.\n return new GSwapSDKError(\n 'CHAIN_DISPATCH_FAILED',\n { ...params, errorKey: getStringProperty(body, 'errorKey') ?? '' },\n context,\n );\n default:\n return new GSwapSDKError('GATEWAY_REJECTED', { gatewayCode, serverMessage }, context);\n }\n }\n\n /** Convert a GalaChain `Status: 0` response into a typed SDK error. */\n public static fromChainError(\n errorKey: string,\n serverMessage: string,\n context?: GSwapErrorContext,\n ): GSwapSDKError<'CHAIN_REJECTED'> {\n return new GSwapSDKError('CHAIN_REJECTED', { errorKey, serverMessage }, context);\n }\n\n /**\n * Create the error used when a token is not registered by the current DEX contract. `source` is\n * the backend answer that said so (a quote); its status, body and url are kept.\n */\n public static unknownTokenError(\n token: GalaChainTokenClassKey | string,\n source?: GSwapSDKError,\n ): GSwapSDKError<'UNKNOWN_TOKEN'> {\n return new GSwapSDKError('UNKNOWN_TOKEN', { token: tokenText(token) }, sourceContext(source));\n }\n\n /** Create the error used when apply-results confirmation does not arrive in time. */\n public static confirmationTimeoutError(\n uniqueKey: string,\n cause?: unknown,\n ): GSwapSDKError<'CONFIRMATION_TIMEOUT'> {\n return new GSwapSDKError('CONFIRMATION_TIMEOUT', {}, { uniqueKey, cause });\n }\n\n /**\n * Builds, synchronously, the error the SDK itself throws for an HTTP answer of this `status`\n * that has no gateway or chain code: `UPDATE_REQUIRED` for the version wall (426, whatever\n * else is true), `HTTP_ERROR` for every other status. It is for an app that holds a resolved\n * HTTP answer the SDK did not read (a v1 endpoint, an interceptor's rejection) and wants the\n * SDK's classification of it (`classifyFailure`) instead of building the error by hand.\n *\n * Reads and writes differ exactly as they do in the gateway (`classifyFailure`): a WRITE\n * carries the signed `uniqueKey` (the handle to confirm it), a READ carries none. Pass\n * `uniqueKey` for a write, or `write: true` with it; `write: false` builds a read and drops any\n * `uniqueKey`. `write: true` without a non-empty `uniqueKey` throws `INVALID_UNIQUE_KEY`: a\n * write error with no key would be classified as a read, which is a different answer. Either\n * way an answer was read, so the phase is `post_send`.\n *\n * @example\n * ```ts\n * const read = GSwapSDKError.fromStatus(503, { url });\n * classifyFailure(read); // { outcome: 'transient', retryable: true }\n * const write = GSwapSDKError.fromStatus(503, { url, uniqueKey });\n * classifyFailure(write); // { outcome: 'unknown', retryable: false }: it may have been queued\n * classifyFailure(GSwapSDKError.fromStatus(426)); // invalid_input: only an update helps\n * ```\n */\n public static fromStatus(\n status: number,\n options: GSwapStatusErrorOptions = {},\n ): GSwapSDKError<'UPDATE_REQUIRED' | 'HTTP_ERROR'> {\n const { url, uniqueKey, write, serverMessage, body, retryAfterMs } = options;\n const isWrite = write ?? (uniqueKey !== undefined && uniqueKey !== '');\n if (isWrite && (uniqueKey === undefined || uniqueKey === '')) {\n throw new GSwapSDKError('INVALID_UNIQUE_KEY', { uniqueKey: uniqueKey ?? '' });\n }\n const context: GSwapErrorContext = {\n status,\n body,\n url,\n retryAfterMs,\n uniqueKey: isWrite ? uniqueKey : undefined,\n write: isWrite,\n phase: 'post_send',\n };\n return status === UPDATE_REQUIRED_STATUS\n ? new GSwapSDKError('UPDATE_REQUIRED', { status }, context)\n : new GSwapSDKError(\n 'HTTP_ERROR',\n { status, url: url ?? '', serverMessage: serverMessage ?? '' },\n context,\n );\n }\n\n /** Convert an unexpected HTTP response into an SDK error. */\n public static async fromErrorResponse(\n url: string,\n response: HTTPResponse,\n ): Promise<GSwapSDKError> {\n const bodyText = await response.text();\n const bodyJson: unknown = parseJson(bodyText);\n const errorObject = getObjectProperty(bodyJson, 'error');\n const errorKey = getStringProperty(errorObject, 'ErrorKey');\n const message = getStringProperty(errorObject, 'Message');\n // Every request that goes through `HttpClient` is a READ (a quote, a list, an apply-results\n // GET); writes go through the gateway, which builds its own errors with the signed key.\n const context: GSwapErrorContext = {\n status: response.status,\n body: bodyJson ?? bodyText,\n url,\n write: false,\n };\n\n if (response.status === UPDATE_REQUIRED_STATUS) {\n return new GSwapSDKError('UPDATE_REQUIRED', { status: response.status }, context);\n }\n\n if (errorKey !== undefined && message !== undefined) {\n return new GSwapSDKError('CHAIN_REJECTED', { errorKey, serverMessage: message }, context);\n }\n\n return new GSwapSDKError(\n 'HTTP_ERROR',\n { status: response.status, url, serverMessage: serverMessageOfBody(bodyJson) },\n context,\n );\n }\n}\n\n/** A token as the text a param carries: a string as is, a class key as its `$` composite. */\nfunction tokenText(token: GalaChainTokenClassKey | string): string {\n return typeof token === 'string'\n ? token\n : [token.collection, token.category, token.type, token.additionalKey].join('$');\n}\n\nfunction feeText(fee: number | undefined): string {\n return fee === undefined ? 'any' : String(fee);\n}\n\n/**\n * The HTTP facts of the error a classified error was derived from. An error derived from a\n * backend answer is `post_send`: the answer was read.\n */\nfunction sourceContext(source: GSwapSDKError | undefined): GSwapErrorContext {\n if (source === undefined) return {};\n return {\n status: source.status,\n body: source.body ?? source.bodyText,\n url: source.url,\n phase: 'post_send',\n write: source.write,\n };\n}\n\n/** Any thrown value that is an SDK error, narrowed to the union of every code. */\nexport type AnyGSwapError = { [C in GSwapErrorCode]: GSwapSDKError<C> }[GSwapErrorCode];\n\n/**\n * True when `value` is an SDK error and, when `code` is given, carries exactly that code. With a\n * code, `error.params` narrows to that code's params.\n *\n * @example\n * ```ts\n * if (isGSwapError(error, 'NON_CANONICAL_PAIR')) {\n * t('errors.NON_CANONICAL_PAIR', error.params); // { token0, token1 }\n * }\n * ```\n */\nexport function isGSwapError(value: unknown): value is AnyGSwapError;\nexport function isGSwapError<C extends GSwapErrorCode>(\n value: unknown,\n code: C,\n): value is GSwapSDKError<C>;\nexport function isGSwapError(value: unknown, code?: GSwapErrorCode): boolean {\n return value instanceof GSwapSDKError && (code === undefined || value.code === code);\n}\n\n/**\n * True when `value` is an SDK error that was thrown BEFORE anything was transmitted\n * (`error.phase === 'pre_send'`): validation, symbol resolution, the signer, the wallet, a key\n * provider. A write that failed this way cannot have been queued, so the failure never calls for\n * \"confirm before you retry\". Every failure of an app-supplied callback that runs BEFORE the\n * request leaves reaches you as an SDK error of this kind (`SIGNING_FAILED`,\n * `KEY_PROVIDER_FAILED`, `TOKEN_RESOLUTION_FAILED`, the original error as `cause`), so an app\n * never needs an error class of its own to tell \"nothing was sent\" apart.\n *\n * Whether to offer Retry is `classifyFailure(error).retryable`, not this: a pre-send validation\n * error is pre-send too, and repeating the same call fails the same way.\n *\n * It also answers for a plain-object view of an SDK error (`{ code, phase? }`, see\n * `FailureView`): the view's own `phase`, else the catalog's phase for its `code`. It is a plain\n * `boolean`, not a type guard (a view is not a `GSwapSDKError`); use `isGSwapError` to narrow.\n *\n * @example\n * ```ts\n * if (isPreSendError(error)) markNothingSent(); // no pending swap to confirm\n * ```\n */\nexport function isPreSendError(value: unknown): boolean {\n const view = toFailureView(value);\n return view !== undefined && phaseOfView(view) === 'pre_send';\n}\n\n/** Narrow an object property without trusting a cast at a network boundary. */\nexport function getObjectProperty(\n value: unknown,\n property: string,\n): Record<string, unknown> | undefined {\n if (typeof value !== 'object' || value === null || !(property in value)) return undefined;\n const propertyValue = (value as Record<string, unknown>)[property];\n return typeof propertyValue === 'object' && propertyValue !== null\n ? (propertyValue as Record<string, unknown>)\n : undefined;\n}\n\n/** Read a string property from an unknown object. */\nexport function getStringProperty(value: unknown, property: string): string | undefined {\n if (typeof value !== 'object' || value === null || !(property in value)) return undefined;\n const propertyValue = (value as Record<string, unknown>)[property];\n return typeof propertyValue === 'string' ? propertyValue : undefined;\n}\n\n/** Parse a response body, preserving non-JSON text for error reporting. */\nexport function parseJson(text: string): unknown {\n try {\n return JSON.parse(text) as unknown;\n } catch {\n return undefined;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"gswap_sdk_error.js","names":[],"sources":["../../../src/classes/gswap_sdk_error.ts"],"sourcesContent":["import type { GalaChainTokenClassKey } from '../types/token.js';\nimport type { HTTPResponse } from '../types/http_requestor.js';\nimport { GSWAP_ERROR_BRAND } from '../utils/error_brand.js';\nimport { toFailureView } from '../utils/failure_view.js';\nimport { phaseOfView } from './error_phase_table.js';\nimport { serverMessageOfBody } from '../utils/server_message.js';\nimport {\n GSWAP_ERROR_CATALOG,\n renderGSwapErrorTemplate,\n type GSwapErrorCode,\n type GSwapErrorParams,\n} from './error_catalog.js';\nimport type { GSwapErrorPhase } from './error_phases.js';\n\n/**\n * The HTTP status of a version wall: the backend refuses an app version that is too old (HTTP 426\n * Upgrade Required). It is `UPDATE_REQUIRED` on every request the SDK sends, whatever else the body\n * says; a confirmation read keeps `CONFIRMATION_FAILED` because the write it confirms may be applied.\n */\nexport const UPDATE_REQUIRED_STATUS = 426;\n\n/**\n * The facts of an error that are not placeholders of its message: where it happened and what the\n * server said. All optional; none is localized text.\n */\nexport interface GSwapErrorContext {\n /** The HTTP status of the response that caused the error. */\n status?: number | undefined;\n /**\n * The response body: parsed JSON when the failing response had it, else the raw text (which\n * the error then exposes as {@link GSwapSDKError.bodyText}).\n */\n body?: unknown;\n /** The URL the failing request went to. */\n url?: string | undefined;\n /** The `uniqueKey` of the write this error is about: the handle for `confirm()` and recovery. */\n uniqueKey?: string | undefined;\n /** The transaction id of the queued request, `null` when unknown. */\n requestTransactionId?: string | null | undefined;\n /** The transaction id of the apply, `null` when unknown. */\n applyTransactionId?: string | null | undefined;\n /** The block the apply landed in, `null` when unknown. */\n blockNumber?: number | null | undefined;\n /** How long to wait before retrying, from the server's `Retry-After`. */\n retryAfterMs?: number | undefined;\n /** The error that caused this one (a transport failure, a wallet rejection). */\n cause?: unknown;\n /**\n * The phase of THIS occurrence when it differs from the code's catalog phase (see\n * {@link GSwapErrorPhase}): for example `UNKNOWN_TOKEN` is `pre_send` from the symbol registry\n * but `post_send` when it is derived from a quote answer.\n */\n phase?: GSwapErrorPhase | undefined;\n /** True when the failing operation was on the shadow (dark) path; see {@link GSwapSDKError.shadow}. */\n shadow?: boolean | undefined;\n /**\n * `false` for the failure of a READ (see {@link GSwapSDKError.write}); anything else, including\n * absent, is a write (fail-closed). Ignored when `uniqueKey` is set: that is a write.\n */\n write?: boolean | undefined;\n /** The wallet's own code on the error a signer threw; see {@link GSwapSDKError.walletCode}. */\n walletCode?: number | string | undefined;\n}\n\n/** What {@link GSwapSDKError.fromStatus} may be told about the HTTP answer. */\nexport interface GSwapStatusErrorOptions {\n /** The URL the request went to. */\n url?: string | undefined;\n /** The signed `uniqueKey` of the WRITE the answer is for; its presence makes this a write. */\n uniqueKey?: string | undefined;\n /** Say it outright: `true` for a write (needs `uniqueKey`), `false` for a read. */\n write?: boolean | undefined;\n /** The server's own text, for `HTTP_ERROR.params.serverMessage` (`''` when none). */\n serverMessage?: string | undefined;\n /** The response body, kept as `error.body` (or `bodyText` for a string). */\n body?: unknown;\n /** How long the server asked to wait (`Retry-After`), in milliseconds. */\n retryAfterMs?: number | undefined;\n}\n\n/**\n * Error raised by the SDK for validation, gateway, chain, or transaction failures.\n *\n * Branch on {@link code} (stable) and render {@link params} through your own localization.\n * {@link message} is English generated from the catalog template for logs and developers: it\n * is not stable and must never be shown to a user or matched on.\n */\nexport class GSwapSDKError<C extends GSwapErrorCode = GSwapErrorCode> extends Error {\n /** The stable, machine-readable code. */\n public readonly code: C;\n /** The named placeholders of the error: a flat object of primitives, safe to localize with. */\n public readonly params: GSwapErrorParams<C>;\n /** The server's `Retry-After` in milliseconds (a `RATE_LIMITED` answer), when it sent one. */\n public readonly retryAfterMs: number | undefined;\n /** The HTTP status of the response that caused this error, when there was one. */\n public readonly status: number | undefined;\n /**\n * The response body parsed as JSON, when the failing response had one; `undefined` for a body\n * that was not JSON (see {@link bodyText}). Never part of {@link message}.\n */\n public readonly body: unknown;\n /**\n * The raw text of a failing response whose body was NOT JSON (a proxy's HTML 502, a WAF page,\n * an empty body). It is kept here for diagnostics and is never copied into {@link message},\n * so it cannot reach a user interface by accident.\n */\n public readonly bodyText: string | undefined;\n /** The URL the failing request went to, when there was one. */\n public readonly url: string | undefined;\n /** The `uniqueKey` of the write this error is about, when there is one. */\n public readonly uniqueKey: string | undefined;\n /** The transaction id of the queued request, when known. */\n public readonly requestTransactionId: string | null | undefined;\n /** The transaction id of the apply, when known. */\n public readonly applyTransactionId: string | null | undefined;\n /** The block the apply landed in, when known. */\n public readonly blockNumber: number | null | undefined;\n /**\n * How far the operation had got when this was thrown, against the request it exists to send\n * (see {@link GSwapErrorPhase}): `pre_send` means NOTHING was transmitted, so a write that\n * failed this way cannot have been queued; `in_flight` means the request left and no answer was\n * read; `post_send` means an answer was read or the write was already accepted. Every catalog\n * code declares its phase, and a throw site can refine it. Read it with\n * {@link isPreSendError}; `classifyFailure` already folds it into `outcome` and `retryable`.\n */\n public readonly phase: GSwapErrorPhase;\n /**\n * True when this error came from the shadow (dark) path: `swapShadow`, `submitSwapShadow` or\n * `ChainGateway.submitShadow`, whatever failed there (a validation error, the signer, the\n * transport, `SHADOW_NOT_QUEUED`). The classification is the same as on the public path; this\n * is how an app applies its own dark-swap policy (the wallet and frontend_v2 never offer a\n * public fallback or Retry after one). A shadow gateway refusal that RESOLVES (`{ ok: false }`)\n * is not an error and has no flag: the call itself says it was shadow.\n */\n public readonly shadow: boolean;\n /**\n * Whether this failure is a WRITE's, as the guards read it: `false` only for the failure of a\n * READ (a quote, a pool list, an apply-results read), which has no `uniqueKey` to confirm and\n * nothing pending, so a 5xx there is `transient` and retryable. It is `true` for everything else,\n * and always when {@link uniqueKey} is set. That is fail-closed: a failure that does not say it\n * is a read is judged a write, where a 5xx is `unknown` and never retried. Apps that carry an\n * error through their own wrapper copy this field next to `uniqueKey` (`FailureView.write`).\n */\n public readonly write: boolean;\n /**\n * On `SIGNING_FAILED`: the code the wallet put on the error its signer threw, when it set one: an\n * EIP-1193 / JSON-RPC number such as `4001` (the person declined) or `-32603`, or an ethers /\n * viem string such as `'ACTION_REJECTED'`. It is read from the `cause`'s own `code` and is\n * always a short primitive; `undefined` when the wallet sent none, and on every other code. It is\n * a typed field, not a param: it is data for an app's analytics or its own wallet-specific\n * wording, never part of `message`, and `params.reason` stays the stable thing to branch on.\n */\n public readonly walletCode: number | string | undefined;\n\n constructor(code: C, params: GSwapErrorParams<C>, context: GSwapErrorContext = {}) {\n super(\n renderGSwapErrorTemplate(GSWAP_ERROR_CATALOG[code].template, params),\n context.cause === undefined ? undefined : { cause: context.cause },\n );\n this.name = 'GSwapSDKError';\n this.code = code;\n this.params = params;\n this.retryAfterMs = context.retryAfterMs;\n this.status = context.status;\n this.body = typeof context.body === 'string' ? undefined : context.body;\n this.bodyText = typeof context.body === 'string' ? context.body : undefined;\n this.url = context.url;\n this.uniqueKey = context.uniqueKey;\n this.requestTransactionId = context.requestTransactionId;\n this.applyTransactionId = context.applyTransactionId;\n this.blockNumber = context.blockNumber;\n this.phase = context.phase ?? GSWAP_ERROR_CATALOG[code].phase;\n this.shadow = context.shadow ?? false;\n this.write =\n context.uniqueKey !== undefined && context.uniqueKey !== '' ? true : context.write !== false;\n this.walletCode = context.walletCode;\n }\n\n /**\n * The SDK error brand (`Symbol.for('@gala-chain/gswap-sdk/error')`), always `true`. The\n * dependency-free helpers (`isWalletUserRejection`, `isGSwapErrorLike`) recognise an SDK error\n * by it instead of `instanceof`, so they work across copies of the SDK and without importing\n * this class. It lives on the prototype: it is not an own property and is never copied or logged.\n */\n public get [GSWAP_ERROR_BRAND](): true {\n return true;\n }\n\n /**\n * True when the failure means \"that market, pool or position does not exist\" rather than\n * \"something went wrong\": an HTTP 404, a pair with no pool at any fee tier\n * (`NO_POOL_AVAILABLE`), or a token the DEX contract has no trading symbol for\n * (`UNKNOWN_TOKEN`). A pool that exists but is too thin (`INSUFFICIENT_LIQUIDITY`) is NOT\n * not-found.\n */\n public get notFound(): boolean {\n return (\n this.status === 404 || this.code === 'NO_POOL_AVAILABLE' || this.code === 'UNKNOWN_TOKEN'\n );\n }\n\n /** Create the standard missing-signer error. */\n public static noSignerError(): GSwapSDKError<'NO_SIGNER'> {\n return new GSwapSDKError('NO_SIGNER', {});\n }\n\n /** Create the error used when confirming a position write without a configured wallet address. */\n public static noWalletAddressError(): GSwapSDKError<'NO_WALLET_ADDRESS'> {\n return new GSwapSDKError('NO_WALLET_ADDRESS', {});\n }\n\n /**\n * Create the error used when no pool can quote the requested pair. `source` is the backend\n * answer that said so; its status and body are kept.\n */\n public static noPoolAvailableError(\n tokenIn: GalaChainTokenClassKey | string,\n tokenOut: GalaChainTokenClassKey | string,\n fee?: number,\n source?: GSwapSDKError,\n ): GSwapSDKError<'NO_POOL_AVAILABLE'> {\n return new GSwapSDKError(\n 'NO_POOL_AVAILABLE',\n { tokenIn: tokenText(tokenIn), tokenOut: tokenText(tokenOut), fee: feeText(fee) },\n sourceContext(source),\n );\n }\n\n /** Create the error used when a pool lacks liquidity for a quote. `source` as for {@link noPoolAvailableError}. */\n public static insufficientLiquidityError(\n tokenIn: GalaChainTokenClassKey | string,\n tokenOut: GalaChainTokenClassKey | string,\n fee?: number,\n source?: GSwapSDKError,\n ): GSwapSDKError<'INSUFFICIENT_LIQUIDITY'> {\n return new GSwapSDKError(\n 'INSUFFICIENT_LIQUIDITY',\n { tokenIn: tokenText(tokenIn), tokenOut: tokenText(tokenOut), fee: feeText(fee) },\n sourceContext(source),\n );\n }\n\n /**\n * Convert a chain-gateway bounce into a typed SDK error: the gateway's own code when the SDK\n * knows it, `GATEWAY_REJECTED` (with the code as `params.gatewayCode`) when it does not.\n * `serverMessage` is the gateway's English text: for logs, not for a user interface. For\n * `CHAIN_DISPATCH_FAILED`, `params.errorKey` is the chain's `ErrorKey` from the body's\n * top-level `errorKey` (`''` when the gateway sent none): map it, not the text.\n */\n public static fromGatewayBounce(\n gatewayCode: string,\n serverMessage: string,\n status: number,\n body: unknown,\n url: string,\n retryAfterMs?: number,\n uniqueKey?: string,\n ): GSwapSDKError {\n const context: GSwapErrorContext = { status, body, url, retryAfterMs, uniqueKey };\n const params = { serverMessage };\n switch (gatewayCode) {\n case 'METHOD_NOT_ALLOWED':\n return new GSwapSDKError('METHOD_NOT_ALLOWED', params, context);\n case 'SIGNATURE_INVALID':\n return new GSwapSDKError('SIGNATURE_INVALID', params, context);\n case 'SIGNER_MISMATCH':\n return new GSwapSDKError('SIGNER_MISMATCH', params, context);\n case 'DTO_INVALID':\n return new GSwapSDKError('DTO_INVALID', params, context);\n case 'BOUNDS_VIOLATION':\n return new GSwapSDKError('BOUNDS_VIOLATION', params, context);\n case 'SYMBOL_CONFLICT':\n return new GSwapSDKError('SYMBOL_CONFLICT', params, context);\n case 'RATE_LIMITED':\n return new GSwapSDKError('RATE_LIMITED', params, context);\n case 'CHAIN_DISPATCH_FAILED':\n // The gateway adds the chain's own machine-readable key as a top-level `errorKey`, only\n // when the chain supplied one; an older gateway sends none, which is the empty string.\n return new GSwapSDKError(\n 'CHAIN_DISPATCH_FAILED',\n { ...params, errorKey: getStringProperty(body, 'errorKey') ?? '' },\n context,\n );\n default:\n return new GSwapSDKError('GATEWAY_REJECTED', { gatewayCode, serverMessage }, context);\n }\n }\n\n /** Convert a GalaChain `Status: 0` response into a typed SDK error. */\n public static fromChainError(\n errorKey: string,\n serverMessage: string,\n context?: GSwapErrorContext,\n ): GSwapSDKError<'CHAIN_REJECTED'> {\n return new GSwapSDKError('CHAIN_REJECTED', { errorKey, serverMessage }, context);\n }\n\n /**\n * Create the error used when a token is not registered by the current DEX contract. `source` is\n * the backend answer that said so (a quote); its status, body and url are kept.\n */\n public static unknownTokenError(\n token: GalaChainTokenClassKey | string,\n source?: GSwapSDKError,\n ): GSwapSDKError<'UNKNOWN_TOKEN'> {\n return new GSwapSDKError('UNKNOWN_TOKEN', { token: tokenText(token) }, sourceContext(source));\n }\n\n /** Create the error used when apply-results confirmation does not arrive in time. */\n public static confirmationTimeoutError(\n uniqueKey: string,\n cause?: unknown,\n ): GSwapSDKError<'CONFIRMATION_TIMEOUT'> {\n return new GSwapSDKError('CONFIRMATION_TIMEOUT', {}, { uniqueKey, cause });\n }\n\n /**\n * Builds, synchronously, the error the SDK itself throws for an HTTP answer of this `status`\n * that has no gateway or chain code: `UPDATE_REQUIRED` for the version wall (426, whatever\n * else is true), `HTTP_ERROR` for every other status. It is for an app that holds a resolved\n * HTTP answer the SDK did not read (a v1 endpoint, an interceptor's rejection) and wants the\n * SDK's classification of it (`classifyFailure`) instead of building the error by hand.\n *\n * Reads and writes differ exactly as they do in the gateway (`classifyFailure`): a WRITE\n * carries the signed `uniqueKey` (the handle to confirm it), a READ carries none. Pass\n * `uniqueKey` for a write, or `write: true` with it; `write: false` builds a read and drops any\n * `uniqueKey`. `write: true` without a non-empty `uniqueKey` throws `INVALID_UNIQUE_KEY`: a\n * write error with no key would be classified as a read, which is a different answer. Either\n * way an answer was read, so the phase is `post_send`.\n *\n * @example\n * ```ts\n * const read = GSwapSDKError.fromStatus(503, { url });\n * classifyFailure(read); // { outcome: 'transient', retryable: true }\n * const write = GSwapSDKError.fromStatus(503, { url, uniqueKey });\n * classifyFailure(write); // { outcome: 'unknown', retryable: false }: it may have been queued\n * classifyFailure(GSwapSDKError.fromStatus(426)); // invalid_input: only an update helps\n * ```\n */\n public static fromStatus(\n status: number,\n options: GSwapStatusErrorOptions = {},\n ): GSwapSDKError<'UPDATE_REQUIRED' | 'HTTP_ERROR'> {\n const { url, uniqueKey, write, serverMessage, body, retryAfterMs } = options;\n const isWrite = write ?? (uniqueKey !== undefined && uniqueKey !== '');\n if (isWrite && (uniqueKey === undefined || uniqueKey === '')) {\n throw new GSwapSDKError('INVALID_UNIQUE_KEY', { uniqueKey: uniqueKey ?? '' });\n }\n const context: GSwapErrorContext = {\n status,\n body,\n url,\n retryAfterMs,\n uniqueKey: isWrite ? uniqueKey : undefined,\n write: isWrite,\n phase: 'post_send',\n };\n return status === UPDATE_REQUIRED_STATUS\n ? new GSwapSDKError('UPDATE_REQUIRED', { status }, context)\n : new GSwapSDKError(\n 'HTTP_ERROR',\n { status, url: url ?? '', serverMessage: serverMessage ?? '' },\n context,\n );\n }\n\n /** Convert an unexpected HTTP response into an SDK error. */\n public static async fromErrorResponse(\n url: string,\n response: HTTPResponse,\n ): Promise<GSwapSDKError> {\n const bodyText = await response.text();\n const bodyJson: unknown = parseJson(bodyText);\n const errorObject = getObjectProperty(bodyJson, 'error');\n const errorKey = getStringProperty(errorObject, 'ErrorKey');\n const message = getStringProperty(errorObject, 'Message');\n // Every request that goes through `HttpClient` is a READ (a quote, a list, an apply-results\n // GET); writes go through the gateway, which builds its own errors with the signed key.\n const context: GSwapErrorContext = {\n status: response.status,\n body: bodyJson ?? bodyText,\n url,\n write: false,\n };\n\n if (response.status === UPDATE_REQUIRED_STATUS) {\n return new GSwapSDKError('UPDATE_REQUIRED', { status: response.status }, context);\n }\n\n if (errorKey !== undefined && message !== undefined) {\n return new GSwapSDKError('CHAIN_REJECTED', { errorKey, serverMessage: message }, context);\n }\n\n return new GSwapSDKError(\n 'HTTP_ERROR',\n { status: response.status, url, serverMessage: serverMessageOfBody(bodyJson) },\n context,\n );\n }\n}\n\n/** A token as the text a param carries: a string as is, a class key as its `$` composite. */\nfunction tokenText(token: GalaChainTokenClassKey | string): string {\n return typeof token === 'string'\n ? token\n : [token.collection, token.category, token.type, token.additionalKey].join('$');\n}\n\nfunction feeText(fee: number | undefined): string {\n return fee === undefined ? 'any' : String(fee);\n}\n\n/**\n * The HTTP facts of the error a classified error was derived from. An error derived from a\n * backend answer is `post_send`: the answer was read.\n */\nfunction sourceContext(source: GSwapSDKError | undefined): GSwapErrorContext {\n if (source === undefined) return {};\n return {\n status: source.status,\n body: source.body ?? source.bodyText,\n url: source.url,\n phase: 'post_send',\n write: source.write,\n };\n}\n\n/** Any thrown value that is an SDK error, narrowed to the union of every code. */\nexport type AnyGSwapError = { [C in GSwapErrorCode]: GSwapSDKError<C> }[GSwapErrorCode];\n\n/**\n * True when `value` is an SDK error and, when `code` is given, carries exactly that code. With a\n * code, `error.params` narrows to that code's params.\n *\n * @example\n * ```ts\n * if (isGSwapError(error, 'NON_CANONICAL_PAIR')) {\n * t('errors.NON_CANONICAL_PAIR', error.params); // { token0, token1 }\n * }\n * ```\n */\nexport function isGSwapError(value: unknown): value is AnyGSwapError;\nexport function isGSwapError<C extends GSwapErrorCode>(\n value: unknown,\n code: C,\n): value is GSwapSDKError<C>;\nexport function isGSwapError(value: unknown, code?: GSwapErrorCode): boolean {\n return value instanceof GSwapSDKError && (code === undefined || value.code === code);\n}\n\n/**\n * True when `value` is an SDK error that was thrown BEFORE anything was transmitted\n * (`error.phase === 'pre_send'`): validation, symbol resolution, the signer, the wallet, a key\n * provider. A write that failed this way cannot have been queued, so the failure never calls for\n * \"confirm before you retry\". Every failure of an app-supplied callback that runs BEFORE the\n * request leaves reaches you as an SDK error of this kind (`SIGNING_FAILED`,\n * `KEY_PROVIDER_FAILED`, `TOKEN_RESOLUTION_FAILED`, the original error as `cause`), so an app\n * never needs an error class of its own to tell \"nothing was sent\" apart.\n *\n * Whether to offer Retry is `classifyFailure(error).retryable`, not this: a pre-send validation\n * error is pre-send too, and repeating the same call fails the same way.\n *\n * It also answers for a plain-object view of an SDK error (`{ code, phase? }`, see\n * `FailureView`): the view's own `phase`, else the catalog's phase for its `code`. It is a plain\n * `boolean`, not a type guard (a view is not a `GSwapSDKError`); use `isGSwapError` to narrow.\n *\n * @example\n * ```ts\n * if (isPreSendError(error)) markNothingSent(); // no pending swap to confirm\n * ```\n */\nexport function isPreSendError(value: unknown): boolean {\n const view = toFailureView(value);\n return view !== undefined && phaseOfView(view) === 'pre_send';\n}\n\n/** Narrow an object property without trusting a cast at a network boundary. */\nexport function getObjectProperty(\n value: unknown,\n property: string,\n): Record<string, unknown> | undefined {\n if (typeof value !== 'object' || value === null || !(property in value)) return undefined;\n const propertyValue = (value as Record<string, unknown>)[property];\n return typeof propertyValue === 'object' && propertyValue !== null\n ? (propertyValue as Record<string, unknown>)\n : undefined;\n}\n\n/** Read a string property from an unknown object. */\nexport function getStringProperty(value: unknown, property: string): string | undefined {\n if (typeof value !== 'object' || value === null || !(property in value)) return undefined;\n const propertyValue = (value as Record<string, unknown>)[property];\n return typeof propertyValue === 'string' ? propertyValue : undefined;\n}\n\n/** Parse a response body, preserving non-JSON text for error reporting. */\nexport function parseJson(text: string): unknown {\n try {\n return JSON.parse(text) as unknown;\n } catch {\n return undefined;\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{classifyFailure as e}from"../utils/failure_classification.js";const t=[`forget`,`forget_quietly`,`keep`],n={rejected:`forget`,invalid_input:`forget_quietly`,unknown:`keep`,not_queued:`keep`,not_sent:`keep`,transient:`keep`};function r(e){return Object.hasOwn(n,e)?n[e]:`keep`}function i(t){return r(e(t,{afterQueued:!0}).outcome)}export{t as PENDING_SWAP_FATES,i as pendingSwapFateFor,r as pendingSwapFateOfOutcome};
|
|
2
|
+
//# sourceMappingURL=pending_swap_fate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pending_swap_fate.js","names":[],"sources":["../../../src/classes/pending_swap_fate.ts"],"sourcesContent":["import { classifyFailure } from '../utils/failure_classification.js';\nimport type { FailureOutcome } from '../utils/failure_outcomes.js';\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,MAAa,EAAqB,CAAC,SAAU,iBAAkB,MAAM,EAW/D,EAAkB,CACtB,SAAU,SACV,cAAe,iBACf,QAAS,OACT,WAAY,OACZ,SAAU,OACV,UAAW,MACb,EAOA,SAAgB,EAAyB,EAA0C,CACjF,OAAO,OAAO,OAAO,EAAiB,CAAO,EAAI,EAAgB,GAAW,MAC9E,CAmBA,SAAgB,EAAmB,EAAiC,CAClE,OAAO,EAAyB,EAAgB,EAAO,CAAE,YAAa,EAAK,CAAC,CAAC,CAAC,OAAO,CACvF"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{isValidUniqueKey as e}from"../utils/unique_key_syntax.js";import t from"../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/defineProperty.js";import{
|
|
1
|
+
import{isValidUniqueKey as e}from"../utils/unique_key_syntax.js";import t from"../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/defineProperty.js";import{pendingSwapFateFor as n}from"./pending_swap_fate.js";const r=`pendingSwaps.v1`,i=6048e5,a=[`expired`,`invalid_unique_key`,`chain_rejected`,`unconfirmable`];var o=class{constructor(e,n={}){this.storage=e,t(this,`storageKey`,void 0),t(this,`darkFlagField`,void 0),t(this,`insertAt`,void 0),t(this,`listOrder`,void 0),t(this,`now`,void 0),t(this,`onPrune`,void 0),t(this,`queue`,Promise.resolve()),t(this,`toStored`,e=>({uniqueKey:e.uniqueKey,requestTransactionId:e.requestTransactionId,wallet:e.wallet,token0Symbol:e.token0Symbol,token1Symbol:e.token1Symbol,[this.darkFlagField]:e.isDarkSwap,createdAt:e.createdAt})),this.storageKey=n.storageKey??`pendingSwaps.v1`,this.darkFlagField=n.darkFlagField??`isDarkSwap`,this.insertAt=n.insertAt??`end`,this.listOrder=n.listOrder??`stored`,this.now=n.now??Date.now,this.onPrune=n.onPrune}remember(t){return e(t.uniqueKey)?this.enqueue(async e=>{let n=(await this.read(e)).filter(e=>e.uniqueKey!==t.uniqueKey).map(this.toStored),r=this.toStored(t);await e.set(this.storageKey,this.insertAt===`end`?[...n,r]:[r,...n])}):(this.report([{swap:t,reason:`invalid_unique_key`}]),Promise.resolve())}forget(e){return this.enqueue(async t=>{let n=await this.read(t);await t.set(this.storageKey,n.filter(t=>t.uniqueKey!==e).map(this.toStored))})}async settle(e,t){let r=n(t);if(r===`keep`)return r;let i=await this.enqueue(async t=>{let n=await this.read(t),r=n.find(t=>t.uniqueKey===e);if(r!==void 0)return await t.set(this.storageKey,n.filter(t=>t.uniqueKey!==e).map(this.toStored)),r});return i!==void 0&&this.report([{swap:i,reason:r===`forget`?`chain_rejected`:`unconfirmable`}]),r}async list(e){return await this.enqueue(async t=>{let n=await this.read(t),r=this.now(),i=[],a=[];for(let e of n){let t=this.pruneReasonOf(e,r);t===void 0?a.push(e):i.push({swap:e,reason:t})}i.length>0&&(await t.set(this.storageKey,a.map(this.toStored)),this.report(i));let o=a.filter(t=>t.wallet===e);return this.listOrder===`newest-first`?o.sort((e,t)=>t.createdAt-e.createdAt):o})??[]}pruneReasonOf(t,n){return e(t.uniqueKey)?n-t.createdAt<6048e5?void 0:`expired`:`invalid_unique_key`}report(e){for(let t of e)try{this.onPrune?.(t)}catch{}}enqueue(e){let t=this.storage;if(t===void 0)return Promise.resolve(void 0);let n=this.queue.then(async()=>{try{return await e(t)}catch{return}});return this.queue=n,n}async read(e){let t=await e.get(this.storageKey);if(!Array.isArray(t))return[];let n=[];for(let e of t){let t=this.fromStored(e);t!==void 0&&n.push(t)}return n}fromStored(e){if(typeof e!=`object`||!e)return;let t=e,n=t[this.darkFlagField],{uniqueKey:r,requestTransactionId:i,wallet:a,token0Symbol:o,token1Symbol:s,createdAt:c}=t;if(!(typeof r!=`string`||typeof i!=`string`&&i!==null||typeof a!=`string`||typeof o!=`string`||typeof s!=`string`||typeof n!=`boolean`||typeof c!=`number`||!Number.isFinite(c)))return{uniqueKey:r,requestTransactionId:i,wallet:a,token0Symbol:o,token1Symbol:s,isDarkSwap:n,createdAt:c}}};export{r as PENDING_SWAPS_STORAGE_KEY,i as PENDING_SWAP_EXPIRY_MS,a as PENDING_SWAP_PRUNE_REASONS,o as PendingSwapStore};
|
|
2
2
|
//# sourceMappingURL=pending_swaps.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pending_swaps.js","names":[],"sources":["../../../src/classes/pending_swaps.ts"],"sourcesContent":["import { classifyFailure } from '../utils/failure_classification.js';\nimport type { FailureOutcome } from '../utils/failure_outcomes.js';\nimport { isValidUniqueKey } from '../utils/unique_key_syntax.js';\n\n/** Key the wallet_extension has always used; its storage value is a plain array of entries. */\nexport const PENDING_SWAPS_STORAGE_KEY = 'pendingSwaps.v1';\n\n/** Entries older than this are dropped the next time a wallet's swaps are listed. */\nexport const PENDING_SWAP_EXPIRY_MS = 7 * 24 * 60 * 60 * 1000;\n\n/**\n * Async key/value storage the pending-swap store persists through. It stores STRUCTURED VALUES\n * (the store never serializes), so each platform decides how a value lands on disk:\n *\n * - `chrome.storage.local`: `get: async (k) => (await chrome.storage.local.get(k))[k]`,\n * `set: (k, v) => chrome.storage.local.set({ [k]: v })`. This is the wallet's stored shape.\n * - `localStorage` (frontend_v2): `get: async (k) => { const raw = localStorage.getItem(k);\n * if (raw === null) return undefined; try { return JSON.parse(raw); } catch { return raw; } }`,\n * `set: async (k, v) => localStorage.setItem(k, JSON.stringify(v))`. Corrupt JSON MUST come back\n * as a value (here the raw string), not as a rejection: a value that is not an array reads as\n * \"no entries\" and the next write repairs it, whereas a `get` that rejects forever makes every\n * `remember` a no-op.\n * - React Native: AsyncStorage with the same JSON wrapping and the same corrupt-JSON rule.\n *\n * `get` resolves `undefined` for a missing key. Either method may reject; the store treats that\n * as \"storage unavailable\" and never lets it surface (see {@link PendingSwapStore}).\n */\nexport interface StorageAdapter {\n get(key: string): Promise<unknown>;\n set(key: string, value: unknown): Promise<void>;\n}\n\n/**\n * The part of a `chrome.storage` area ({@link storageAdapterFromChromeStorage}) the adapter uses,\n * written structurally so the SDK needs no `chrome` types: `chrome.storage.local`,\n * `chrome.storage.session`, `browser.storage.local` (webextension-polyfill) and any object with\n * these two promise-returning methods satisfy it. It is the Manifest V3 promise form: the methods\n * are called WITHOUT a callback.\n */\nexport interface ChromeStorageAreaLike {\n get(key: string): Promise<Record<string, unknown>>;\n set(items: Record<string, unknown>): Promise<void>;\n}\n\n/**\n * A {@link StorageAdapter} over a `chrome.storage` area, the shape the wallet stores pending swaps\n * in: the value lands under the key as the structured value it is (the browser clones it; nothing\n * is serialized here). A missing key resolves `undefined`; a rejection of the area reaches the\n * store, which treats it as \"storage unavailable\".\n *\n * Pass a FUNCTION that returns the area to look it up at each call instead of when the adapter is\n * built: a module that is imported where `chrome` does not exist yet (a test, a closing extension\n * context) then fails only when the store touches storage, and that failure (a throw inside the\n * function) is the same \"storage unavailable\" rejection.\n *\n * @example\n * ```ts\n * const store = new PendingSwapStore(storageAdapterFromChromeStorage(chrome.storage.local));\n * const lazy = new PendingSwapStore(storageAdapterFromChromeStorage(() => chrome.storage.local));\n * ```\n */\nexport function storageAdapterFromChromeStorage(\n area: ChromeStorageAreaLike | (() => ChromeStorageAreaLike),\n): StorageAdapter {\n const areaNow = (): ChromeStorageAreaLike => (typeof area === 'function' ? area() : area);\n return {\n async get(key) {\n const items = await areaNow().get(key);\n return Object.hasOwn(items, key) ? items[key] : undefined;\n },\n async set(key, value) {\n await areaNow().set({ [key]: value });\n },\n };\n}\n\n/** A RequestTrade that was accepted by the gateway but whose outcome has not been read yet. */\nexport interface PendingSwap {\n uniqueKey: string;\n requestTransactionId: string | null;\n /** The wallet (account) that submitted the swap; `list` filters by it. */\n wallet: string;\n token0Symbol: string;\n token1Symbol: string;\n /** Whether the swap was a shadow (\"dark\") swap. */\n isDarkSwap: boolean;\n /** Epoch milliseconds when the swap was queued. */\n createdAt: number;\n}\n\n/**\n * Why the store dropped a pending swap (the `reason` of {@link PrunedPendingSwap}).\n *\n * - `expired`: older than {@link PENDING_SWAP_EXPIRY_MS}.\n * - `invalid_unique_key`: its `uniqueKey` is not a valid apply-results key (1 to 128 characters\n * of `A-Za-z0-9._:-`). `confirmAppliedTrade` refuses such a key with `INVALID_UNIQUE_KEY`\n * before reading anything and always will, so keeping the entry would retry it forever. The\n * SDK never signs such a key; an entry like this is corrupt or foreign.\n * - `chain_rejected`: {@link PendingSwapStore.settle} dropped it because a confirmation proved the\n * chain REFUSED the trade (`pendingSwapFateFor` is `forget`): it did not happen.\n * - `unconfirmable`: {@link PendingSwapStore.settle} dropped it because a confirmation failed in a\n * way that never changes (`forget_quietly`: the key itself is refused): nothing can be learned.\n */\nexport const PENDING_SWAP_PRUNE_REASONS = [\n 'expired',\n 'invalid_unique_key',\n 'chain_rejected',\n 'unconfirmable',\n] as const;\n\n/** One member of {@link PENDING_SWAP_PRUNE_REASONS}. */\nexport type PendingSwapPruneReason = (typeof PENDING_SWAP_PRUNE_REASONS)[number];\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 */\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\n/** A pending swap the store dropped, and why. */\nexport interface PrunedPendingSwap {\n swap: PendingSwap;\n reason: PendingSwapPruneReason;\n}\n\n/** Options for {@link PendingSwapStore}. */\nexport interface PendingSwapStoreOptions {\n /**\n * Called once per dropped entry (after the drop is persisted, for `list`; at once for a\n * `remember` that refuses an invalid key), so the app can tell the user or log why a swap it\n * was tracking is gone. Diagnostics only: what it does, or throws, never changes the store.\n */\n onPrune?: ((pruned: PrunedPendingSwap) => void) | undefined;\n /** Storage key. Default {@link PENDING_SWAPS_STORAGE_KEY}. */\n storageKey?: string | undefined;\n /**\n * The stored name of the dark-swap flag. `isDarkSwap` (default) is wallet_extension's shape;\n * `shadow` is frontend_v2's. Entries are always exposed to callers as `isDarkSwap`.\n */\n darkFlagField?: 'isDarkSwap' | 'shadow' | undefined;\n /**\n * Where `remember` puts a new entry in the stored array: `end` (default, wallet_extension) or\n * `start` (frontend_v2).\n */\n insertAt?: 'end' | 'start' | undefined;\n /**\n * The order of `list`: `stored` (default, wallet_extension: oldest first for `insertAt: 'end'`)\n * or `newest-first` (frontend_v2).\n */\n listOrder?: 'stored' | 'newest-first' | undefined;\n /** Clock, in epoch milliseconds. Defaults to `Date.now`. */\n now?: (() => number) | undefined;\n}\n\n/**\n * Remembers queued swaps so their outcome can be confirmed after the UI, popup or service worker\n * that submitted them is gone (`confirmAppliedTrade` with the stored `uniqueKey`).\n *\n * Guarantees:\n * - Persistence is best effort and NEVER throws: a failing or missing storage must not turn an\n * accepted request into a client-side resubmission. `list` resolves `[]` when storage fails.\n * - Operations of one store instance are serialized, so concurrent `remember` / `forget` calls\n * cannot lose each other's write (read-modify-write over an async adapter). Two store instances\n * over the same storage (for example popup and service worker) are not coordinated.\n * - Entries that do not match the expected shape are ignored and dropped on the next write.\n * - Entries expire after seven days (an entry exactly seven days old is expired; frontend_v2\n * kept it for that one millisecond); `list` removes them from storage, which frontend_v2 never\n * did on read.\n * - An entry whose `uniqueKey` is not a valid apply-results key is terminal, not pending:\n * `confirmAppliedTrade` fails it with `INVALID_UNIQUE_KEY` every time, so a resume loop would\n * retry it forever. `list` prunes it like an expired one, and `remember` refuses to save it;\n * both tell `onPrune` the reason (`PENDING_SWAP_PRUNE_REASONS`).\n * - New entries go to the end (or `insertAt: 'start'`); `list` returns the wallet's entries in\n * stored order (or `listOrder: 'newest-first'`).\n * - The stored shape is wallet_extension's by default; `storageKey`, `darkFlagField`, `insertAt`\n * and `listOrder` reproduce frontend_v2's. JSON key order inside an entry is not preserved.\n *\n * Passing no `storage` yields a store that remembers nothing.\n *\n * @example\n * ```ts\n * const store = new PendingSwapStore({\n * get: async (key) => (await chrome.storage.local.get(key))[key],\n * set: (key, value) => chrome.storage.local.set({ [key]: value }),\n * });\n * await store.remember({ uniqueKey, requestTransactionId, wallet, token0Symbol: 'GALA',\n * token1Symbol: 'GUSDC', isDarkSwap: false, createdAt: Date.now() });\n * for (const swap of await store.list(wallet)) await confirmAppliedTrade(client, swap);\n * ```\n */\nexport class PendingSwapStore {\n private readonly storageKey: string;\n private readonly darkFlagField: 'isDarkSwap' | 'shadow';\n private readonly insertAt: 'end' | 'start';\n private readonly listOrder: 'stored' | 'newest-first';\n private readonly now: () => number;\n private readonly onPrune: ((pruned: PrunedPendingSwap) => void) | undefined;\n private queue: Promise<unknown> = Promise.resolve();\n\n constructor(\n private readonly storage: StorageAdapter | undefined,\n options: PendingSwapStoreOptions = {},\n ) {\n this.storageKey = options.storageKey ?? PENDING_SWAPS_STORAGE_KEY;\n this.darkFlagField = options.darkFlagField ?? 'isDarkSwap';\n this.insertAt = options.insertAt ?? 'end';\n this.listOrder = options.listOrder ?? 'stored';\n this.now = options.now ?? Date.now;\n this.onPrune = options.onPrune;\n }\n\n /**\n * Saves (or replaces, by `uniqueKey`) a pending swap. A swap whose `uniqueKey` is not a valid\n * apply-results key is not saved (it could never be confirmed); it is reported to `onPrune` as\n * `invalid_unique_key`.\n */\n public remember(swap: PendingSwap): Promise<void> {\n if (!isValidUniqueKey(swap.uniqueKey)) {\n this.report([{ swap, reason: 'invalid_unique_key' }]);\n return Promise.resolve();\n }\n return this.enqueue(async (storage) => {\n const current = await this.read(storage);\n const others = current\n .filter((entry) => entry.uniqueKey !== swap.uniqueKey)\n .map(this.toStored);\n const stored = this.toStored(swap);\n await storage.set(\n this.storageKey,\n this.insertAt === 'end' ? [...others, stored] : [stored, ...others],\n );\n });\n }\n\n /** Removes a pending swap once its outcome is known. */\n public forget(uniqueKey: string): Promise<void> {\n return this.enqueue(async (storage) => {\n const current = await this.read(storage);\n await storage.set(\n this.storageKey,\n current.filter((entry) => entry.uniqueKey !== uniqueKey).map(this.toStored),\n );\n });\n }\n\n /**\n * Applies {@link pendingSwapFateFor} to a swap whose confirmation failed with `error`, and\n * returns the fate so the app can word it (tell the user on `forget`, say nothing on\n * `forget_quietly`, keep polling on `keep`):\n *\n * - `forget`: the entry is removed and `onPrune` hears `chain_rejected`.\n * - `forget_quietly`: the entry is removed and `onPrune` hears `unconfirmable`.\n * - `keep`: nothing changes and `onPrune` is not called.\n *\n * Like every store operation it is serialized and best effort: if storage fails the entry stays\n * (a later `settle` or `list` handles it) and the fate is still returned. A key that is not\n * stored (already forgotten) is not an error and `onPrune` is not called for it.\n */\n public async settle(uniqueKey: string, error: unknown): Promise<PendingSwapFate> {\n const fate = pendingSwapFateFor(error);\n if (fate === 'keep') return fate;\n const removed = await this.enqueue(async (storage) => {\n const current = await this.read(storage);\n const swap = current.find((entry) => entry.uniqueKey === uniqueKey);\n if (swap === undefined) return undefined;\n await storage.set(\n this.storageKey,\n current.filter((entry) => entry.uniqueKey !== uniqueKey).map(this.toStored),\n );\n return swap;\n });\n if (removed !== undefined) {\n this.report([\n { swap: removed, reason: fate === 'forget' ? 'chain_rejected' : 'unconfirmable' },\n ]);\n }\n return fate;\n }\n\n /**\n * The wallet's pending swaps that can still be confirmed (see `listOrder`). Entries that never\n * can be are pruned from storage, for every wallet, and reported to `onPrune`: expired ones\n * (`expired`) and ones whose `uniqueKey` is not a valid apply-results key\n * (`invalid_unique_key`: `confirmAppliedTrade` would fail `INVALID_UNIQUE_KEY` on it forever).\n */\n public async list(wallet: string): Promise<PendingSwap[]> {\n const result = await this.enqueue(async (storage) => {\n const current = await this.read(storage);\n const now = this.now();\n const pruned: PrunedPendingSwap[] = [];\n const kept: PendingSwap[] = [];\n for (const entry of current) {\n const reason = this.pruneReasonOf(entry, now);\n if (reason === undefined) kept.push(entry);\n else pruned.push({ swap: entry, reason });\n }\n if (pruned.length > 0) {\n await storage.set(this.storageKey, kept.map(this.toStored));\n this.report(pruned);\n }\n const mine = kept.filter((entry) => entry.wallet === wallet);\n return this.listOrder === 'newest-first'\n ? mine.sort((left, right) => right.createdAt - left.createdAt)\n : mine;\n });\n return result ?? [];\n }\n\n private pruneReasonOf(entry: PendingSwap, now: number): PendingSwapPruneReason | undefined {\n if (!isValidUniqueKey(entry.uniqueKey)) return 'invalid_unique_key';\n return now - entry.createdAt < PENDING_SWAP_EXPIRY_MS ? undefined : 'expired';\n }\n\n /** Tells `onPrune` about each dropped entry; the hook is diagnostics and cannot break the store. */\n private report(pruned: readonly PrunedPendingSwap[]): void {\n for (const entry of pruned) {\n try {\n this.onPrune?.(entry);\n } catch {\n // A diagnostics hook must not turn a store operation into a failure.\n }\n }\n }\n\n private enqueue<T>(operation: (storage: StorageAdapter) => Promise<T>): Promise<T | undefined> {\n const storage = this.storage;\n if (storage === undefined) return Promise.resolve(undefined);\n const run = this.queue.then(async () => {\n try {\n return await operation(storage);\n } catch {\n // Storage can be missing, full, or disappear while a context is closing. Persistence is\n // best effort by contract.\n return undefined;\n }\n });\n this.queue = run;\n return run;\n }\n\n private async read(storage: StorageAdapter): Promise<PendingSwap[]> {\n const value = await storage.get(this.storageKey);\n if (!Array.isArray(value)) return [];\n const entries: PendingSwap[] = [];\n for (const item of value as unknown[]) {\n const entry = this.fromStored(item);\n if (entry !== undefined) entries.push(entry);\n }\n return entries;\n }\n\n private readonly toStored = (swap: PendingSwap): Record<string, unknown> => ({\n uniqueKey: swap.uniqueKey,\n requestTransactionId: swap.requestTransactionId,\n wallet: swap.wallet,\n token0Symbol: swap.token0Symbol,\n token1Symbol: swap.token1Symbol,\n [this.darkFlagField]: swap.isDarkSwap,\n createdAt: swap.createdAt,\n });\n\n private fromStored(value: unknown): PendingSwap | undefined {\n if (value === null || typeof value !== 'object') return undefined;\n const entry = value as Record<string, unknown>;\n const flag = entry[this.darkFlagField];\n const { uniqueKey, requestTransactionId, wallet, token0Symbol, token1Symbol, createdAt } =\n entry;\n if (\n typeof uniqueKey !== 'string' ||\n !(typeof requestTransactionId === 'string' || requestTransactionId === null) ||\n typeof wallet !== 'string' ||\n typeof token0Symbol !== 'string' ||\n typeof token1Symbol !== 'string' ||\n typeof flag !== 'boolean' ||\n typeof createdAt !== 'number' ||\n !Number.isFinite(createdAt)\n ) {\n return undefined;\n }\n return {\n uniqueKey,\n requestTransactionId,\n wallet,\n token0Symbol,\n token1Symbol,\n isDarkSwap: flag,\n createdAt,\n };\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"pending_swaps.js","names":[],"sources":["../../../src/classes/pending_swaps.ts"],"sourcesContent":["import { isValidUniqueKey } from '../utils/unique_key_syntax.js';\nimport { pendingSwapFateFor, type PendingSwapFate } from './pending_swap_fate.js';\nimport type { StorageAdapter } from './storage_adapters.js';\n\n/** Key the wallet_extension has always used; its storage value is a plain array of entries. */\nexport const PENDING_SWAPS_STORAGE_KEY = 'pendingSwaps.v1';\n\n/** Entries older than this are dropped the next time a wallet's swaps are listed. */\nexport const PENDING_SWAP_EXPIRY_MS = 7 * 24 * 60 * 60 * 1000;\n\n/** A RequestTrade that was accepted by the gateway but whose outcome has not been read yet. */\nexport interface PendingSwap {\n uniqueKey: string;\n requestTransactionId: string | null;\n /** The wallet (account) that submitted the swap; `list` filters by it. */\n wallet: string;\n token0Symbol: string;\n token1Symbol: string;\n /** Whether the swap was a shadow (\"dark\") swap. */\n isDarkSwap: boolean;\n /** Epoch milliseconds when the swap was queued. */\n createdAt: number;\n}\n\n/**\n * Why the store dropped a pending swap (the `reason` of {@link PrunedPendingSwap}).\n *\n * - `expired`: older than {@link PENDING_SWAP_EXPIRY_MS}.\n * - `invalid_unique_key`: its `uniqueKey` is not a valid apply-results key (1 to 128 characters\n * of `A-Za-z0-9._:-`). `confirmAppliedTrade` refuses such a key with `INVALID_UNIQUE_KEY`\n * before reading anything and always will, so keeping the entry would retry it forever. The\n * SDK never signs such a key; an entry like this is corrupt or foreign.\n * - `chain_rejected`: {@link PendingSwapStore.settle} dropped it because a confirmation proved the\n * chain REFUSED the trade (`pendingSwapFateFor` is `forget`): it did not happen.\n * - `unconfirmable`: {@link PendingSwapStore.settle} dropped it because a confirmation failed in a\n * way that never changes (`forget_quietly`: the key itself is refused): nothing can be learned.\n */\nexport const PENDING_SWAP_PRUNE_REASONS = [\n 'expired',\n 'invalid_unique_key',\n 'chain_rejected',\n 'unconfirmable',\n] as const;\n\n/** One member of {@link PENDING_SWAP_PRUNE_REASONS}. */\nexport type PendingSwapPruneReason = (typeof PENDING_SWAP_PRUNE_REASONS)[number];\n\n/** A pending swap the store dropped, and why. */\nexport interface PrunedPendingSwap {\n swap: PendingSwap;\n reason: PendingSwapPruneReason;\n}\n\n/** Options for {@link PendingSwapStore}. */\nexport interface PendingSwapStoreOptions {\n /**\n * Called once per dropped entry (after the drop is persisted, for `list`; at once for a\n * `remember` that refuses an invalid key), so the app can tell the user or log why a swap it\n * was tracking is gone. Diagnostics only: what it does, or throws, never changes the store.\n */\n onPrune?: ((pruned: PrunedPendingSwap) => void) | undefined;\n /** Storage key. Default {@link PENDING_SWAPS_STORAGE_KEY}. */\n storageKey?: string | undefined;\n /**\n * The stored name of the dark-swap flag. `isDarkSwap` (default) is wallet_extension's shape;\n * `shadow` is frontend_v2's. Entries are always exposed to callers as `isDarkSwap`.\n */\n darkFlagField?: 'isDarkSwap' | 'shadow' | undefined;\n /**\n * Where `remember` puts a new entry in the stored array: `end` (default, wallet_extension) or\n * `start` (frontend_v2).\n */\n insertAt?: 'end' | 'start' | undefined;\n /**\n * The order of `list`: `stored` (default, wallet_extension: oldest first for `insertAt: 'end'`)\n * or `newest-first` (frontend_v2).\n */\n listOrder?: 'stored' | 'newest-first' | undefined;\n /** Clock, in epoch milliseconds. Defaults to `Date.now`. */\n now?: (() => number) | undefined;\n}\n\n/**\n * Remembers queued swaps so their outcome can be confirmed after the UI, popup or service worker\n * that submitted them is gone (`confirmAppliedTrade` with the stored `uniqueKey`).\n *\n * Guarantees:\n * - Persistence is best effort and NEVER throws: a failing or missing storage must not turn an\n * accepted request into a client-side resubmission. `list` resolves `[]` when storage fails.\n * - Operations of one store instance are serialized, so concurrent `remember` / `forget` calls\n * cannot lose each other's write (read-modify-write over an async adapter). Two store instances\n * over the same storage (for example popup and service worker) are not coordinated.\n * - Entries that do not match the expected shape are ignored and dropped on the next write.\n * - Entries expire after seven days (an entry exactly seven days old is expired; frontend_v2\n * kept it for that one millisecond); `list` removes them from storage, which frontend_v2 never\n * did on read.\n * - An entry whose `uniqueKey` is not a valid apply-results key is terminal, not pending:\n * `confirmAppliedTrade` fails it with `INVALID_UNIQUE_KEY` every time, so a resume loop would\n * retry it forever. `list` prunes it like an expired one, and `remember` refuses to save it;\n * both tell `onPrune` the reason (`PENDING_SWAP_PRUNE_REASONS`).\n * - New entries go to the end (or `insertAt: 'start'`); `list` returns the wallet's entries in\n * stored order (or `listOrder: 'newest-first'`).\n * - The stored shape is wallet_extension's by default; `storageKey`, `darkFlagField`, `insertAt`\n * and `listOrder` reproduce frontend_v2's. JSON key order inside an entry is not preserved.\n *\n * Passing no `storage` yields a store that remembers nothing.\n *\n * @example\n * ```ts\n * const store = new PendingSwapStore({\n * get: async (key) => (await chrome.storage.local.get(key))[key],\n * set: (key, value) => chrome.storage.local.set({ [key]: value }),\n * });\n * await store.remember({ uniqueKey, requestTransactionId, wallet, token0Symbol: 'GALA',\n * token1Symbol: 'GUSDC', isDarkSwap: false, createdAt: Date.now() });\n * for (const swap of await store.list(wallet)) await confirmAppliedTrade(client, swap);\n * ```\n */\nexport class PendingSwapStore {\n private readonly storageKey: string;\n private readonly darkFlagField: 'isDarkSwap' | 'shadow';\n private readonly insertAt: 'end' | 'start';\n private readonly listOrder: 'stored' | 'newest-first';\n private readonly now: () => number;\n private readonly onPrune: ((pruned: PrunedPendingSwap) => void) | undefined;\n private queue: Promise<unknown> = Promise.resolve();\n\n constructor(\n private readonly storage: StorageAdapter | undefined,\n options: PendingSwapStoreOptions = {},\n ) {\n this.storageKey = options.storageKey ?? PENDING_SWAPS_STORAGE_KEY;\n this.darkFlagField = options.darkFlagField ?? 'isDarkSwap';\n this.insertAt = options.insertAt ?? 'end';\n this.listOrder = options.listOrder ?? 'stored';\n this.now = options.now ?? Date.now;\n this.onPrune = options.onPrune;\n }\n\n /**\n * Saves (or replaces, by `uniqueKey`) a pending swap. A swap whose `uniqueKey` is not a valid\n * apply-results key is not saved (it could never be confirmed); it is reported to `onPrune` as\n * `invalid_unique_key`.\n */\n public remember(swap: PendingSwap): Promise<void> {\n if (!isValidUniqueKey(swap.uniqueKey)) {\n this.report([{ swap, reason: 'invalid_unique_key' }]);\n return Promise.resolve();\n }\n return this.enqueue(async (storage) => {\n const current = await this.read(storage);\n const others = current\n .filter((entry) => entry.uniqueKey !== swap.uniqueKey)\n .map(this.toStored);\n const stored = this.toStored(swap);\n await storage.set(\n this.storageKey,\n this.insertAt === 'end' ? [...others, stored] : [stored, ...others],\n );\n });\n }\n\n /** Removes a pending swap once its outcome is known. */\n public forget(uniqueKey: string): Promise<void> {\n return this.enqueue(async (storage) => {\n const current = await this.read(storage);\n await storage.set(\n this.storageKey,\n current.filter((entry) => entry.uniqueKey !== uniqueKey).map(this.toStored),\n );\n });\n }\n\n /**\n * Applies {@link pendingSwapFateFor} to a swap whose confirmation failed with `error`, and\n * returns the fate so the app can word it (tell the user on `forget`, say nothing on\n * `forget_quietly`, keep polling on `keep`):\n *\n * - `forget`: the entry is removed and `onPrune` hears `chain_rejected`.\n * - `forget_quietly`: the entry is removed and `onPrune` hears `unconfirmable`.\n * - `keep`: nothing changes and `onPrune` is not called.\n *\n * Like every store operation it is serialized and best effort: if storage fails the entry stays\n * (a later `settle` or `list` handles it) and the fate is still returned. A key that is not\n * stored (already forgotten) is not an error and `onPrune` is not called for it.\n */\n public async settle(uniqueKey: string, error: unknown): Promise<PendingSwapFate> {\n const fate = pendingSwapFateFor(error);\n if (fate === 'keep') return fate;\n const removed = await this.enqueue(async (storage) => {\n const current = await this.read(storage);\n const swap = current.find((entry) => entry.uniqueKey === uniqueKey);\n if (swap === undefined) return undefined;\n await storage.set(\n this.storageKey,\n current.filter((entry) => entry.uniqueKey !== uniqueKey).map(this.toStored),\n );\n return swap;\n });\n if (removed !== undefined) {\n this.report([\n { swap: removed, reason: fate === 'forget' ? 'chain_rejected' : 'unconfirmable' },\n ]);\n }\n return fate;\n }\n\n /**\n * The wallet's pending swaps that can still be confirmed (see `listOrder`). Entries that never\n * can be are pruned from storage, for every wallet, and reported to `onPrune`: expired ones\n * (`expired`) and ones whose `uniqueKey` is not a valid apply-results key\n * (`invalid_unique_key`: `confirmAppliedTrade` would fail `INVALID_UNIQUE_KEY` on it forever).\n */\n public async list(wallet: string): Promise<PendingSwap[]> {\n const result = await this.enqueue(async (storage) => {\n const current = await this.read(storage);\n const now = this.now();\n const pruned: PrunedPendingSwap[] = [];\n const kept: PendingSwap[] = [];\n for (const entry of current) {\n const reason = this.pruneReasonOf(entry, now);\n if (reason === undefined) kept.push(entry);\n else pruned.push({ swap: entry, reason });\n }\n if (pruned.length > 0) {\n await storage.set(this.storageKey, kept.map(this.toStored));\n this.report(pruned);\n }\n const mine = kept.filter((entry) => entry.wallet === wallet);\n return this.listOrder === 'newest-first'\n ? mine.sort((left, right) => right.createdAt - left.createdAt)\n : mine;\n });\n return result ?? [];\n }\n\n private pruneReasonOf(entry: PendingSwap, now: number): PendingSwapPruneReason | undefined {\n if (!isValidUniqueKey(entry.uniqueKey)) return 'invalid_unique_key';\n return now - entry.createdAt < PENDING_SWAP_EXPIRY_MS ? undefined : 'expired';\n }\n\n /** Tells `onPrune` about each dropped entry; the hook is diagnostics and cannot break the store. */\n private report(pruned: readonly PrunedPendingSwap[]): void {\n for (const entry of pruned) {\n try {\n this.onPrune?.(entry);\n } catch {\n // A diagnostics hook must not turn a store operation into a failure.\n }\n }\n }\n\n private enqueue<T>(operation: (storage: StorageAdapter) => Promise<T>): Promise<T | undefined> {\n const storage = this.storage;\n if (storage === undefined) return Promise.resolve(undefined);\n const run = this.queue.then(async () => {\n try {\n return await operation(storage);\n } catch {\n // Storage can be missing, full, or disappear while a context is closing. Persistence is\n // best effort by contract.\n return undefined;\n }\n });\n this.queue = run;\n return run;\n }\n\n private async read(storage: StorageAdapter): Promise<PendingSwap[]> {\n const value = await storage.get(this.storageKey);\n if (!Array.isArray(value)) return [];\n const entries: PendingSwap[] = [];\n for (const item of value as unknown[]) {\n const entry = this.fromStored(item);\n if (entry !== undefined) entries.push(entry);\n }\n return entries;\n }\n\n private readonly toStored = (swap: PendingSwap): Record<string, unknown> => ({\n uniqueKey: swap.uniqueKey,\n requestTransactionId: swap.requestTransactionId,\n wallet: swap.wallet,\n token0Symbol: swap.token0Symbol,\n token1Symbol: swap.token1Symbol,\n [this.darkFlagField]: swap.isDarkSwap,\n createdAt: swap.createdAt,\n });\n\n private fromStored(value: unknown): PendingSwap | undefined {\n if (value === null || typeof value !== 'object') return undefined;\n const entry = value as Record<string, unknown>;\n const flag = entry[this.darkFlagField];\n const { uniqueKey, requestTransactionId, wallet, token0Symbol, token1Symbol, createdAt } =\n entry;\n if (\n typeof uniqueKey !== 'string' ||\n !(typeof requestTransactionId === 'string' || requestTransactionId === null) ||\n typeof wallet !== 'string' ||\n typeof token0Symbol !== 'string' ||\n typeof token1Symbol !== 'string' ||\n typeof flag !== 'boolean' ||\n typeof createdAt !== 'number' ||\n !Number.isFinite(createdAt)\n ) {\n return undefined;\n }\n return {\n uniqueKey,\n requestTransactionId,\n wallet,\n token0Symbol,\n token1Symbol,\n isDarkSwap: flag,\n createdAt,\n };\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{ALLOWED_FEE_TIERS_TEXT as e}from"./error_catalog.js";import t from"../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/defineProperty.js";import{GSwapSDKError as n,isGSwapError as r}from"./gswap_sdk_error.js";import{TICK_SPACING_BY_FEE as i}from"../types/fees.js";import{alignTickDown as a,alignTickUp as o,assertTickRange as s,tickFromPrice as c}from"../utils/ticks.js";import{isProposableTradingSymbol as l,validateFee as u,validateNumericAmount as d}from"../utils/validation.js";import{resolvePreSend as f,signPreSend as p}from"../utils/pre_send.js";import{HttpClient as m}from"./http_client.js";import{asEntryRecord as h,entryOk as g,entryRejected as _,readEntries as v}from"../utils/lenient.js";import{compositeKeyOf as y,parseTokenClassKey as b}from"../utils/ordering.js";import{rawTokenOf as x}from"./symbols.js";import{assertUniqueKey as S,createUniqueKey as C,resolveUniqueKeyPrefix as w}from"../utils/unique_key.js";import{assertCanonicalV2Dto as T,buildAddLiquidityDto as E,buildCollectPositionFeesDto as D,buildCreatePoolDto as O,buildRemoveLiquidityDto as k}from"../utils/dto_builders.js";import A from"bignumber.js";function j(e){return{token0:e.token0,token1:e.token1,fee:e.fee,tickLower:e.tickLower,tickUpper:e.tickUpper}}var M=class{constructor(e,n,r,i,a={}){this.gateway=e,this.symbols=n,this.signer=r,this.walletAddress=i,t(this,`http`,void 0),t(this,`uniqueKeyPrefix`,void 0),t(this,`onInvalidEntry`,void 0),this.http=new m(e.httpRequestor,e.requestTimeoutMs??3e4,e.wire),this.uniqueKeyPrefix=w(a.uniqueKeyPrefix),this.onInvalidEntry=a.onInvalidEntry}async getUserPositions(e){let t=await this.get(`/positions`,{user:e});return v(`positions`,t.data,I,this.onInvalidEntry)}async getPosition(e,t){let n=await this.getNullable(`/position`,await this.positionParams(e),t);return n===null?null:L(n.data)}async readPosition(e,t){try{let n=await this.getPosition(e,t);return n===null?{kind:`not_found`}:{kind:`found`,data:n}}catch(e){if(t?.aborted===!0)throw e;return{kind:`unknown`,error:r(e)?e:new n(`REQUEST_FAILED`,{operation:`position`},{cause:e})}}}async positionParams(e){if(this.symbols.passthrough===!0)return{token0:x(e.token0),token1:x(e.token1),fee:`${e.fee}`,owner:e.owner,tickLower:`${e.tickLower}`,tickUpper:`${e.tickUpper}`};let t=await this.orderPair(e.token0,e.token1),n=this.canonicalTicks(e.tickLower,e.tickUpper,t.flipped);return{token0:t.token0.symbol,token1:t.token1.symbol,fee:`${e.fee}`,owner:e.owner,tickLower:`${n.tickLower}`,tickUpper:`${n.tickUpper}`}}async estimateAddLiquidity(e){return d(e.amount,`amount`),(await this.get(`/add-liq-estimate`,{token0:x(e.token0),token1:x(e.token1),fee:`${e.fee}`,tickLower:`${e.tickLower}`,tickUpper:`${e.tickUpper}`,amount:new A(e.amount).toFixed(),amountIsToken0:String(e.amountIsToken0)})).data}async estimateRemoveLiquidity(e){return d(e.liquidity,`liquidity`),(await this.get(`/remove-liq-estimate`,{token0:x(e.token0),token1:x(e.token1),fee:`${e.fee}`,tickLower:`${e.tickLower}`,tickUpper:`${e.tickUpper}`,liquidity:new A(e.liquidity).toFixed()})).data}async addLiquidityByTicks(e){return this.submitAddLiquidity(await this.buildAddLiquidity(e))}async buildAddLiquidity(e){u(e.fee);let t=await this.orderPair(e.token0,e.token1),n=this.canonicalTicks(e.tickLower,e.tickUpper,t.flipped);s(n.tickLower,n.tickUpper,e.fee),d(e.amount,`amount`);let r={token0:t.token0.symbol,token1:t.token1.symbol,fee:e.fee,tickLower:n.tickLower,tickUpper:n.tickUpper,uniqueKey:this.uniqueKeyFor(e.uniqueKey)};return t.flipped===e.amountIsToken0?E({...r,depositQuantityToken1:e.amount}):E({...r,depositQuantityToken0:e.amount})}async submitAddLiquidity(e){return T(`AddLiquidity`,e),this.submit(`AddLiquidity`,e,{kind:`add`,identity:j(e)})}async addLiquidityByPrice(e){u(e.fee),this.rejectNumber(e.minPrice,`minPrice`),this.rejectNumber(e.maxPrice,`maxPrice`);let t=new A(e.minPrice),r=new A(e.maxPrice);if(!t.isFinite()||!r.isFinite()||t.isLessThanOrEqualTo(0)||r.isLessThanOrEqualTo(0))throw new n(`INVALID_PRICE_BOUNDS`,{minPrice:String(e.minPrice),maxPrice:String(e.maxPrice)});if(t.isGreaterThan(r))throw new n(`PRICE_BOUNDS_ORDER`,{minPrice:String(e.minPrice),maxPrice:String(e.maxPrice)});let i=this.tickSpacing(e.fee),s=a(c(t),i),l=o(c(r),i);return this.addLiquidityByTicks({token0:e.token0,token1:e.token1,fee:e.fee,tickLower:s,tickUpper:l,amount:e.amount,amountIsToken0:e.amountIsToken0,uniqueKey:e.uniqueKey})}async removeLiquidity(e){return this.submitRemoveLiquidity(await this.buildRemoveLiquidity(e))}async buildRemoveLiquidity(e){u(e.fee);let t=await this.orderPair(e.token0,e.token1),r=this.canonicalTicks(e.tickLower,e.tickUpper,t.flipped);if(s(r.tickLower,r.tickUpper,e.fee),e.amount0!==void 0&&e.amount1!==void 0)throw new n(`AT_MOST_ONE_OF_ALLOWED`,{first:`amount0`,second:`amount1`});e.amount0!==void 0&&d(e.amount0,`amount0`),e.amount1!==void 0&&d(e.amount1,`amount1`);let[i,a]=t.flipped?[e.amount1,e.amount0]:[e.amount0,e.amount1];return k({token0:t.token0.symbol,token1:t.token1.symbol,fee:e.fee,tickLower:r.tickLower,tickUpper:r.tickUpper,uniqueKey:this.uniqueKeyFor(e.uniqueKey),withdrawalQuantityToken0:i,withdrawalQuantityToken1:a})}async submitRemoveLiquidity(e){T(`RemoveLiquidity`,e);let t=e.withdrawalQuantityToken0===void 0&&e.withdrawalQuantityToken1===void 0;return this.submit(`RemoveLiquidity`,e,{kind:t?`removeFull`:`removePartial`,identity:j(e)})}async collectPositionFees(e){return this.submitCollectPositionFees(await this.buildCollectPositionFees(e))}async buildCollectPositionFees(e){u(e.fee);let t=await this.orderPair(e.token0,e.token1),n=this.canonicalTicks(e.tickLower,e.tickUpper,t.flipped);return s(n.tickLower,n.tickUpper,e.fee),D({token0:t.token0.symbol,token1:t.token1.symbol,fee:e.fee,tickLower:n.tickLower,tickUpper:n.tickUpper,uniqueKey:this.uniqueKeyFor(e.uniqueKey)})}async submitCollectPositionFees(e){return T(`CollectPositionFees`,e),this.submit(`CollectPositionFees`,e,{kind:`collect`,identity:j(e)})}async createPool(e){return this.submitCreatePool(await this.buildCreatePool(e))}async buildCreatePool(e){let t=e.startingPrice!==void 0;if(t===(e.startingSqrtPrice!==void 0))throw new n(`EXACTLY_ONE_OF_REQUIRED`,{first:`startingPrice`,second:`startingSqrtPrice`});u(e.fee);let[r,i]=await Promise.all([this.resolveCreateToken(e.token0,e.token0Symbol),this.resolveCreateToken(e.token1,e.token1Symbol)]),a=r.symbol>i.symbol,[o,s]=a?[i,r]:[r,i],c=t?e.startingPrice:e.startingSqrtPrice;this.rejectNumber(c,t?`startingPrice`:`startingSqrtPrice`);let l=new A(c);if(!l.isFinite()||l.isLessThanOrEqualTo(0))throw new n(`INVALID_STARTING_PRICE`,{value:String(c)});let d=a?new A(1).dividedBy(l):l,f={token0Key:o.classKey,token1Key:s.classKey,token0Symbol:o.symbol,token1Symbol:s.symbol,fee:e.fee,uniqueKey:this.uniqueKeyFor(e.uniqueKey),name:e.name,isPrivate:e.isPrivate,privateAccess:e.privateAccess};return O(t?{...f,startingPrice:d}:{...f,startingSqrtPrice:d})}async submitCreatePool(e){T(`CreatePool`,e);let t=await this.submit(`CreatePool`,e,{kind:`createPool`,token0:e.token0Symbol,token1:e.token1Symbol,fee:e.fee});return this.symbols.invalidate?.(),t}async submit(e,t,r){if(this.signer===void 0)throw n.noSignerError();let i=await p(this.signer,e,t),a=this.walletAddress===void 0?{}:{walletAddress:this.walletAddress},o=this.walletAddress,s=this.buildPositionConfirmation(r,o),c={...a,...s===void 0?{}:{positionConfirmation:s}};return this.gateway.submit(e,i,c)}buildPositionConfirmation(e,t){if(e===void 0)return;if(e.kind===`createPool`){let{token0:t,token1:n,fee:r}=e;return async e=>await this.getNullable(`/pool`,{token0:t,token1:n,fee:`${r}`},e)===null?{confirmed:!1}:{confirmed:!0,position:null}}if(t===void 0)return;let{kind:n,identity:r}=e,i;return async e=>{let a=await this.getPosition({...r,owner:t},e);if(i===void 0)return i={liquidity:new A(a?.liquidity??0),fees:new A(a?.fees0??0).plus(new A(a?.fees1??0))},n===`removeFull`&&a===null?{confirmed:!0,position:null}:{confirmed:!1};switch(n){case`add`:return a!==null&&new A(a.liquidity).isGreaterThan(i.liquidity)?{confirmed:!0,position:a}:{confirmed:!1};case`removePartial`:return a!==null&&new A(a.liquidity).isLessThan(i.liquidity)?{confirmed:!0,position:a}:{confirmed:!1};case`removeFull`:return a===null?{confirmed:!0,position:null}:{confirmed:!1};case`collect`:{if(a===null)return{confirmed:!1};let e=new A(a.fees0??0).plus(new A(a.fees1??0));return e.isLessThan(i.fees)||i.fees.isZero()&&e.isZero()?{confirmed:!0,position:a}:{confirmed:!1}}}}}async get(e,t,n){return this.http.sendGetRequest(this.gateway.dexBackendBaseUrl,`/v2/trade`,e,t,n===void 0?void 0:{signal:n})}async getNullable(e,t,n){try{return await this.get(e,t,n)}catch(e){if(this.isNotFound(e))return null;throw e}}isNotFound(e){return e instanceof n&&e.status===404}canonicalTicks(e,t,n){return n?{tickLower:-t,tickUpper:-e}:{tickLower:e,tickUpper:t}}tickSpacing(t){let r=i[t];if(r===void 0)throw new n(`INVALID_FEE`,{field:`fee`,value:t,allowed:e});return r}rejectNumber(e,t){if(typeof e==`number`)throw new n(`AMOUNT_IS_JS_NUMBER`,{field:t})}uniqueKeyFor(e){return e===void 0?C(this.uniqueKeyPrefix):(S(e),e)}orderPair(e,t){return f(`${x(e)}/${x(t)}`,()=>this.symbols.orderPair(e,t))}async resolveCreateToken(e,t){try{let r=typeof e==`object`?y(e):e,i=await f(x(e),()=>this.symbols.resolve(r));if(t!==void 0&&t!==``&&t!==i.symbol)throw new n(`SYMBOL_DISAGREES_WITH_REGISTRY`,{token:x(e),suppliedSymbol:t,registeredSymbol:i.symbol});let a=this.tryParseClassKey(e);if(a===void 0&&i.collection===``)throw new n(`CREATE_POOL_NEEDS_CLASS_KEY`,{token:x(e)});return{symbol:i.symbol,classKey:a??b({collection:i.collection,category:i.category,type:i.type,additionalKey:i.additionalKey})}}catch(r){if(!(r instanceof n)||r.code!==`UNKNOWN_TOKEN`)throw r;let i;try{i=b(e)}catch{throw r}if(t===void 0||t===``)throw new n(`TOKEN_SYMBOL_NOT_REGISTERED`,{token:x(e)});if(!l(t))throw new n(`INVALID_TRADING_SYMBOL`,{symbol:t});return{symbol:t,classKey:i}}}tryParseClassKey(e){try{return b(e)}catch{return}}};const N=[`token0Symbol`,`token1Symbol`,`owner`,`liquidity`,`amount0`,`amount1`],P=[`fee`,`tickLower`,`tickUpper`],F=[`token0CompositeKey`,`token1CompositeKey`,`token0Image`,`token1Image`,`sqrtPrice`,`fees0`,`fees1`];function I(e){let t=h(e);if(t===void 0)return _(`the row is not an object`);for(let e of N)if(typeof t[e]!=`string`)return _(`${e} is not a string`);for(let e of P){let n=t[e];if(typeof n!=`number`||!Number.isFinite(n))return _(`${e} is not a number`)}if(typeof t.inRange!=`boolean`)return _(`inRange is not a boolean`);for(let e of F)if(t[e]!==void 0&&typeof t[e]!=`string`)return _(`${e} is not a string`);let n=t.currentTick;if(n!==void 0&&(typeof n!=`number`||!Number.isFinite(n)))return _(`currentTick is not a number`);let r=t.poolRef??t.pool??``;if(typeof r!=`string`)return _(`pool is not a string`);let i=Object.fromEntries([...F,`currentTick`].flatMap(e=>t[e]===void 0?[]:[[e,t[e]]]));return g({token0Symbol:t.token0Symbol,token1Symbol:t.token1Symbol,fee:t.fee,tickLower:t.tickLower,tickUpper:t.tickUpper,liquidity:t.liquidity,amount0:t.amount0,amount1:t.amount1,inRange:t.inRange,pool:r,owner:t.owner,...i})}function L(e){let t=I(e);if(t.ok)return t.value;throw new n(`INVALID_POSITION_RESPONSE`,{reason:t.reason},{body:e})}export{M as Positions};
|
|
1
|
+
import{ALLOWED_FEE_TIERS_TEXT as e}from"./error_catalog.js";import t from"../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/defineProperty.js";import{GSwapSDKError as n,isGSwapError as r}from"./gswap_sdk_error.js";import{TICK_SPACING_BY_FEE as i}from"../types/fees.js";import{tickFromPrice as a}from"../utils/tick_math.js";import{isProposableTradingSymbol as o,validateFee as s,validateNumericAmount as c}from"../utils/validation.js";import{resolvePreSend as l,signPreSend as u}from"../utils/pre_send.js";import{HttpClient as d}from"./http_client.js";import{asEntryRecord as f,entryOk as p,entryRejected as m,readEntries as h}from"../utils/lenient.js";import{compositeKeyOf as g}from"../utils/class_key.js";import{parseTokenClassKey as _}from"../utils/ordering.js";import{rawTokenOf as v}from"./symbols.js";import{alignTickDown as y,alignTickUp as b,assertTickRange as x}from"../utils/ticks.js";import{assertUniqueKey as S,createUniqueKey as C,resolveUniqueKeyPrefix as w}from"../utils/unique_key.js";import{assertCanonicalV2Dto as T,buildAddLiquidityDto as E,buildCollectPositionFeesDto as D,buildCreatePoolDto as O,buildRemoveLiquidityDto as k}from"../utils/dto_builders.js";import A from"bignumber.js";function j(e){return{token0:e.token0,token1:e.token1,fee:e.fee,tickLower:e.tickLower,tickUpper:e.tickUpper}}var M=class{constructor(e,n,r,i,a={}){this.gateway=e,this.symbols=n,this.signer=r,this.walletAddress=i,t(this,`http`,void 0),t(this,`uniqueKeyPrefix`,void 0),t(this,`onInvalidEntry`,void 0),this.http=new d(e.httpRequestor,e.requestTimeoutMs??3e4,e.wire),this.uniqueKeyPrefix=w(a.uniqueKeyPrefix),this.onInvalidEntry=a.onInvalidEntry}async getUserPositions(e){let t=await this.get(`/positions`,{user:e});return h(`positions`,t.data,I,this.onInvalidEntry)}async getPosition(e,t){let n=await this.getNullable(`/position`,await this.positionParams(e),t);return n===null?null:L(n.data)}async readPosition(e,t){try{let n=await this.getPosition(e,t);return n===null?{kind:`not_found`}:{kind:`found`,data:n}}catch(e){if(t?.aborted===!0)throw e;return{kind:`unknown`,error:r(e)?e:new n(`REQUEST_FAILED`,{operation:`position`},{cause:e})}}}async positionParams(e){if(this.symbols.passthrough===!0)return{token0:v(e.token0),token1:v(e.token1),fee:`${e.fee}`,owner:e.owner,tickLower:`${e.tickLower}`,tickUpper:`${e.tickUpper}`};let t=await this.orderPair(e.token0,e.token1),n=this.canonicalTicks(e.tickLower,e.tickUpper,t.flipped);return{token0:t.token0.symbol,token1:t.token1.symbol,fee:`${e.fee}`,owner:e.owner,tickLower:`${n.tickLower}`,tickUpper:`${n.tickUpper}`}}async estimateAddLiquidity(e){return c(e.amount,`amount`),(await this.get(`/add-liq-estimate`,{token0:v(e.token0),token1:v(e.token1),fee:`${e.fee}`,tickLower:`${e.tickLower}`,tickUpper:`${e.tickUpper}`,amount:new A(e.amount).toFixed(),amountIsToken0:String(e.amountIsToken0)})).data}async estimateRemoveLiquidity(e){return c(e.liquidity,`liquidity`),(await this.get(`/remove-liq-estimate`,{token0:v(e.token0),token1:v(e.token1),fee:`${e.fee}`,tickLower:`${e.tickLower}`,tickUpper:`${e.tickUpper}`,liquidity:new A(e.liquidity).toFixed()})).data}async addLiquidityByTicks(e){return this.submitAddLiquidity(await this.buildAddLiquidity(e))}async buildAddLiquidity(e){s(e.fee);let t=await this.orderPair(e.token0,e.token1),n=this.canonicalTicks(e.tickLower,e.tickUpper,t.flipped);x(n.tickLower,n.tickUpper,e.fee),c(e.amount,`amount`);let r={token0:t.token0.symbol,token1:t.token1.symbol,fee:e.fee,tickLower:n.tickLower,tickUpper:n.tickUpper,uniqueKey:this.uniqueKeyFor(e.uniqueKey)};return t.flipped===e.amountIsToken0?E({...r,depositQuantityToken1:e.amount}):E({...r,depositQuantityToken0:e.amount})}async submitAddLiquidity(e){return T(`AddLiquidity`,e),this.submit(`AddLiquidity`,e,{kind:`add`,identity:j(e)})}async addLiquidityByPrice(e){s(e.fee),this.rejectNumber(e.minPrice,`minPrice`),this.rejectNumber(e.maxPrice,`maxPrice`);let t=new A(e.minPrice),r=new A(e.maxPrice);if(!t.isFinite()||!r.isFinite()||t.isLessThanOrEqualTo(0)||r.isLessThanOrEqualTo(0))throw new n(`INVALID_PRICE_BOUNDS`,{minPrice:String(e.minPrice),maxPrice:String(e.maxPrice)});if(t.isGreaterThan(r))throw new n(`PRICE_BOUNDS_ORDER`,{minPrice:String(e.minPrice),maxPrice:String(e.maxPrice)});let i=this.tickSpacing(e.fee),o=y(a(t),i),c=b(a(r),i);return this.addLiquidityByTicks({token0:e.token0,token1:e.token1,fee:e.fee,tickLower:o,tickUpper:c,amount:e.amount,amountIsToken0:e.amountIsToken0,uniqueKey:e.uniqueKey})}async removeLiquidity(e){return this.submitRemoveLiquidity(await this.buildRemoveLiquidity(e))}async buildRemoveLiquidity(e){s(e.fee);let t=await this.orderPair(e.token0,e.token1),r=this.canonicalTicks(e.tickLower,e.tickUpper,t.flipped);if(x(r.tickLower,r.tickUpper,e.fee),e.amount0!==void 0&&e.amount1!==void 0)throw new n(`AT_MOST_ONE_OF_ALLOWED`,{first:`amount0`,second:`amount1`});e.amount0!==void 0&&c(e.amount0,`amount0`),e.amount1!==void 0&&c(e.amount1,`amount1`);let[i,a]=t.flipped?[e.amount1,e.amount0]:[e.amount0,e.amount1];return k({token0:t.token0.symbol,token1:t.token1.symbol,fee:e.fee,tickLower:r.tickLower,tickUpper:r.tickUpper,uniqueKey:this.uniqueKeyFor(e.uniqueKey),withdrawalQuantityToken0:i,withdrawalQuantityToken1:a})}async submitRemoveLiquidity(e){T(`RemoveLiquidity`,e);let t=e.withdrawalQuantityToken0===void 0&&e.withdrawalQuantityToken1===void 0;return this.submit(`RemoveLiquidity`,e,{kind:t?`removeFull`:`removePartial`,identity:j(e)})}async collectPositionFees(e){return this.submitCollectPositionFees(await this.buildCollectPositionFees(e))}async buildCollectPositionFees(e){s(e.fee);let t=await this.orderPair(e.token0,e.token1),n=this.canonicalTicks(e.tickLower,e.tickUpper,t.flipped);return x(n.tickLower,n.tickUpper,e.fee),D({token0:t.token0.symbol,token1:t.token1.symbol,fee:e.fee,tickLower:n.tickLower,tickUpper:n.tickUpper,uniqueKey:this.uniqueKeyFor(e.uniqueKey)})}async submitCollectPositionFees(e){return T(`CollectPositionFees`,e),this.submit(`CollectPositionFees`,e,{kind:`collect`,identity:j(e)})}async createPool(e){return this.submitCreatePool(await this.buildCreatePool(e))}async buildCreatePool(e){let t=e.startingPrice!==void 0;if(t===(e.startingSqrtPrice!==void 0))throw new n(`EXACTLY_ONE_OF_REQUIRED`,{first:`startingPrice`,second:`startingSqrtPrice`});s(e.fee);let[r,i]=await Promise.all([this.resolveCreateToken(e.token0,e.token0Symbol),this.resolveCreateToken(e.token1,e.token1Symbol)]),a=r.symbol>i.symbol,[o,c]=a?[i,r]:[r,i],l=t?e.startingPrice:e.startingSqrtPrice;this.rejectNumber(l,t?`startingPrice`:`startingSqrtPrice`);let u=new A(l);if(!u.isFinite()||u.isLessThanOrEqualTo(0))throw new n(`INVALID_STARTING_PRICE`,{value:String(l)});let d=a?new A(1).dividedBy(u):u,f={token0Key:o.classKey,token1Key:c.classKey,token0Symbol:o.symbol,token1Symbol:c.symbol,fee:e.fee,uniqueKey:this.uniqueKeyFor(e.uniqueKey),name:e.name,isPrivate:e.isPrivate,privateAccess:e.privateAccess};return O(t?{...f,startingPrice:d}:{...f,startingSqrtPrice:d})}async submitCreatePool(e){T(`CreatePool`,e);let t=await this.submit(`CreatePool`,e,{kind:`createPool`,token0:e.token0Symbol,token1:e.token1Symbol,fee:e.fee});return this.symbols.invalidate?.(),t}async submit(e,t,r){if(this.signer===void 0)throw n.noSignerError();let i=await u(this.signer,e,t),a=this.walletAddress===void 0?{}:{walletAddress:this.walletAddress},o=this.walletAddress,s=this.buildPositionConfirmation(r,o),c={...a,...s===void 0?{}:{positionConfirmation:s}};return this.gateway.submit(e,i,c)}buildPositionConfirmation(e,t){if(e===void 0)return;if(e.kind===`createPool`){let{token0:t,token1:n,fee:r}=e;return async e=>await this.getNullable(`/pool`,{token0:t,token1:n,fee:`${r}`},e)===null?{confirmed:!1}:{confirmed:!0,position:null}}if(t===void 0)return;let{kind:n,identity:r}=e,i;return async e=>{let a=await this.getPosition({...r,owner:t},e);if(i===void 0)return i={liquidity:new A(a?.liquidity??0),fees:new A(a?.fees0??0).plus(new A(a?.fees1??0))},n===`removeFull`&&a===null?{confirmed:!0,position:null}:{confirmed:!1};switch(n){case`add`:return a!==null&&new A(a.liquidity).isGreaterThan(i.liquidity)?{confirmed:!0,position:a}:{confirmed:!1};case`removePartial`:return a!==null&&new A(a.liquidity).isLessThan(i.liquidity)?{confirmed:!0,position:a}:{confirmed:!1};case`removeFull`:return a===null?{confirmed:!0,position:null}:{confirmed:!1};case`collect`:{if(a===null)return{confirmed:!1};let e=new A(a.fees0??0).plus(new A(a.fees1??0));return e.isLessThan(i.fees)||i.fees.isZero()&&e.isZero()?{confirmed:!0,position:a}:{confirmed:!1}}}}}async get(e,t,n){return this.http.sendGetRequest(this.gateway.dexBackendBaseUrl,`/v2/trade`,e,t,n===void 0?void 0:{signal:n})}async getNullable(e,t,n){try{return await this.get(e,t,n)}catch(e){if(this.isNotFound(e))return null;throw e}}isNotFound(e){return e instanceof n&&e.status===404}canonicalTicks(e,t,n){return n?{tickLower:-t,tickUpper:-e}:{tickLower:e,tickUpper:t}}tickSpacing(t){let r=i[t];if(r===void 0)throw new n(`INVALID_FEE`,{field:`fee`,value:t,allowed:e});return r}rejectNumber(e,t){if(typeof e==`number`)throw new n(`AMOUNT_IS_JS_NUMBER`,{field:t})}uniqueKeyFor(e){return e===void 0?C(this.uniqueKeyPrefix):(S(e),e)}orderPair(e,t){return l(`${v(e)}/${v(t)}`,()=>this.symbols.orderPair(e,t))}async resolveCreateToken(e,t){try{let r=typeof e==`object`?g(e):e,i=await l(v(e),()=>this.symbols.resolve(r));if(t!==void 0&&t!==``&&t!==i.symbol)throw new n(`SYMBOL_DISAGREES_WITH_REGISTRY`,{token:v(e),suppliedSymbol:t,registeredSymbol:i.symbol});let a=this.tryParseClassKey(e);if(a===void 0&&i.collection===``)throw new n(`CREATE_POOL_NEEDS_CLASS_KEY`,{token:v(e)});return{symbol:i.symbol,classKey:a??_({collection:i.collection,category:i.category,type:i.type,additionalKey:i.additionalKey})}}catch(r){if(!(r instanceof n)||r.code!==`UNKNOWN_TOKEN`)throw r;let i;try{i=_(e)}catch{throw r}if(t===void 0||t===``)throw new n(`TOKEN_SYMBOL_NOT_REGISTERED`,{token:v(e)});if(!o(t))throw new n(`INVALID_TRADING_SYMBOL`,{symbol:t});return{symbol:t,classKey:i}}}tryParseClassKey(e){try{return _(e)}catch{return}}};const N=[`token0Symbol`,`token1Symbol`,`owner`,`liquidity`,`amount0`,`amount1`],P=[`fee`,`tickLower`,`tickUpper`],F=[`token0CompositeKey`,`token1CompositeKey`,`token0Image`,`token1Image`,`sqrtPrice`,`fees0`,`fees1`];function I(e){let t=f(e);if(t===void 0)return m(`the row is not an object`);for(let e of N)if(typeof t[e]!=`string`)return m(`${e} is not a string`);for(let e of P){let n=t[e];if(typeof n!=`number`||!Number.isFinite(n))return m(`${e} is not a number`)}if(typeof t.inRange!=`boolean`)return m(`inRange is not a boolean`);for(let e of F)if(t[e]!==void 0&&typeof t[e]!=`string`)return m(`${e} is not a string`);let n=t.currentTick;if(n!==void 0&&(typeof n!=`number`||!Number.isFinite(n)))return m(`currentTick is not a number`);let r=t.poolRef??t.pool??``;if(typeof r!=`string`)return m(`pool is not a string`);let i=Object.fromEntries([...F,`currentTick`].flatMap(e=>t[e]===void 0?[]:[[e,t[e]]]));return p({token0Symbol:t.token0Symbol,token1Symbol:t.token1Symbol,fee:t.fee,tickLower:t.tickLower,tickUpper:t.tickUpper,liquidity:t.liquidity,amount0:t.amount0,amount1:t.amount1,inRange:t.inRange,pool:r,owner:t.owner,...i})}function L(e){let t=I(e);if(t.ok)return t.value;throw new n(`INVALID_POSITION_RESPONSE`,{reason:t.reason},{body:e})}export{M as Positions};
|
|
2
2
|
//# sourceMappingURL=positions.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"positions.js","names":[],"sources":["../../../src/classes/positions.ts"],"sourcesContent":["import BigNumber from 'bignumber.js';\nimport { ALLOWED_FEE_TIERS_TEXT } from './error_catalog.js';\nimport { GSwapSDKError, isGSwapError } from './gswap_sdk_error.js';\nimport type { ChainGateway, ChainSubmitOptions } from './gateway.js';\nimport type { PositionConfirmationResult, SubmittedTransaction } from './submitted_transaction.js';\nimport { HttpClient } from './http_client.js';\nimport type { GalaChainSigner } from './signers.js';\nimport { rawTokenOf, type Symbols, type TokenResolver } from './symbols.js';\nimport type { NumericAmount, PriceIn } from '../types/amounts.js';\nimport type { FEE_TIER } from '../types/fees.js';\nimport { TICK_SPACING_BY_FEE } from '../types/fees.js';\nimport type {\n AddLiquidityDTO,\n CollectPositionFeesDTO,\n CreatePoolDTO,\n RemoveLiquidityDTO,\n} from '../types/v2_dtos.js';\nimport type {\n AddLiquidityEstimate,\n Position,\n RemoveLiquidityEstimate,\n} from '../types/v2_results.js';\nimport type { WireOptions } from '../types/wire.js';\nimport {\n asEntryRecord,\n entryOk,\n entryRejected,\n readEntries,\n type EntryParse,\n type InvalidEntryHandler,\n} from '../utils/lenient.js';\nimport { type TokenRef, compositeKeyOf, parseTokenClassKey } from '../utils/ordering.js';\nimport { resolvePreSend, signPreSend } from '../utils/pre_send.js';\nimport {\n isProposableTradingSymbol,\n validateFee,\n validateNumericAmount,\n} from '../utils/validation.js';\nimport { alignTickDown, alignTickUp, assertTickRange, tickFromPrice } from '../utils/ticks.js';\nimport {\n assertCanonicalV2Dto,\n buildAddLiquidityDto,\n buildCollectPositionFeesDto,\n buildCreatePoolDto,\n buildRemoveLiquidityDto,\n} from '../utils/dto_builders.js';\nimport {\n assertUniqueKey,\n createUniqueKey,\n resolveUniqueKeyPrefix,\n type UniqueKeyOptions,\n} from '../utils/unique_key.js';\n\ntype PositionGateway = Pick<ChainGateway, 'httpRequestor' | 'dexBackendBaseUrl'> & {\n submit: (\n method: string,\n signedBody: Record<string, unknown>,\n options?: ChainSubmitOptions,\n ) => Promise<SubmittedTransaction<Position | null>>;\n requestTimeoutMs?: number | undefined;\n wire?: WireOptions | undefined;\n};\ntype PositionSymbols = TokenResolver & Pick<Symbols, 'orderPair'> & { invalidate?: () => void };\n\n/** Options of {@link Positions}: the `uniqueKey` prefix and a handler for skipped list rows. */\nexport interface PositionsOptions extends UniqueKeyOptions {\n /** Told about every position row of a list that was skipped as unusable. */\n onInvalidEntry?: InvalidEntryHandler | undefined;\n}\n\n/** A write's optional attempt identity. */\ninterface UniqueKeyArg {\n /**\n * The attempt's `uniqueKey` (1-128 characters of `A-Za-z0-9._:-`). Default: a fresh\n * `<uniqueKeyPrefix><uuid>`. Supply one to know the key before signing, or to retry the SAME\n * attempt after a lost response; never reuse it for another write.\n */\n uniqueKey?: string | undefined;\n}\n\n/** Arguments of {@link Positions.addLiquidityByTicks} and {@link Positions.buildAddLiquidity}. */\nexport interface AddLiquidityArgs extends UniqueKeyArg {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n tickLower: number;\n tickUpper: number;\n amount: NumericAmount;\n /** Whether `amount` is of `token0` as the caller passed it (not of the canonical token0). */\n amountIsToken0: boolean;\n}\n\n/** Arguments of {@link Positions.removeLiquidity} and {@link Positions.buildRemoveLiquidity}. */\nexport interface RemoveLiquidityArgs extends UniqueKeyArg {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n tickLower: number;\n tickUpper: number;\n /** Withdraw this much of the caller's `token0`; give at most one of `amount0` / `amount1`. */\n amount0?: NumericAmount | undefined;\n amount1?: NumericAmount | undefined;\n}\n\n/** Arguments of {@link Positions.collectPositionFees} and {@link Positions.buildCollectPositionFees}. */\nexport interface CollectPositionFeesArgs extends UniqueKeyArg {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n tickLower: number;\n tickUpper: number;\n}\n\n/** Arguments of {@link Positions.createPool} and {@link Positions.buildCreatePool}. */\nexport interface CreatePoolArgs extends UniqueKeyArg {\n token0: TokenRef;\n token1: TokenRef;\n token0Symbol?: string | undefined;\n token1Symbol?: string | undefined;\n fee: FEE_TIER;\n startingPrice?: NumericAmount | undefined;\n startingSqrtPrice?: NumericAmount | undefined;\n /** Optional pool display name, sent as the DTO's `name`; omitted when undefined or empty. */\n name?: string | undefined;\n isPrivate?: boolean | undefined;\n privateAccess?: string[] | undefined;\n}\n\n/** The pool position a liquidity DTO names, as its confirmation needs it. */\nfunction identityOf(\n dto: Pick<CollectPositionFeesDTO, 'token0' | 'token1' | 'fee' | 'tickLower' | 'tickUpper'>,\n): PositionIdentity {\n return {\n token0: dto.token0,\n token1: dto.token1,\n fee: dto.fee,\n tickLower: dto.tickLower,\n tickUpper: dto.tickUpper,\n };\n}\n\n/**\n * The outcome of {@link Positions.readPosition}. Only `not_found` is chain-confirmed absence (the\n * backend answers HTTP 404 only when the chain itself found no such position); `unknown` is \"no\n * evidence either way\" — a transport failure, a gateway timeout, any other error status or an\n * answer that is not a position — and must never be read as a removal.\n */\nexport type PositionRead =\n | { kind: 'found'; data: Position }\n | { kind: 'not_found' }\n | { kind: 'unknown'; error: GSwapSDKError };\n\ninterface BackendEnvelope<TData> {\n data: TData;\n}\n\ninterface AddLiquidityEstimateWire {\n amount0: string;\n amount1: string;\n liquidity: string;\n token0Symbol: string;\n token1Symbol: string;\n tickLower: number;\n tickUpper: number;\n amountIsCanonicalToken0: boolean;\n}\n\ninterface RemoveLiquidityEstimateWire {\n amount0: string;\n amount1: string;\n}\n\ninterface ResolvedCreateToken {\n symbol: string;\n classKey: ReturnType<typeof parseTokenClassKey>;\n}\n\ninterface PositionIdentity {\n token0: string;\n token1: string;\n fee: FEE_TIER;\n tickLower: number;\n tickUpper: number;\n}\n\n/** Tells `submit()` how to prove a write's own effect for `SubmittedTransaction.confirm()`. */\ntype PositionSubmitConfirmation =\n | { kind: 'add' | 'removePartial' | 'removeFull' | 'collect'; identity: PositionIdentity }\n | { kind: 'createPool'; token0: string; token1: string; fee: FEE_TIER };\n\n/**\n * Manages current-contract concentrated-liquidity positions.\n *\n * @example\n * ```typescript\n * const positions = gSwap.positions;\n * const mine = await positions.getUserPositions('client|012345678901234567890123');\n * console.log(mine.length);\n * ```\n */\nexport class Positions {\n constructor(\n private readonly gateway: PositionGateway,\n private readonly symbols: PositionSymbols,\n private readonly signer?: GalaChainSigner,\n private readonly walletAddress?: string,\n options: PositionsOptions = {},\n ) {\n this.http = new HttpClient(\n gateway.httpRequestor,\n gateway.requestTimeoutMs ?? 30_000,\n gateway.wire,\n );\n this.uniqueKeyPrefix = resolveUniqueKeyPrefix(options.uniqueKeyPrefix);\n this.onInvalidEntry = options.onInvalidEntry;\n }\n\n private readonly http: HttpClient;\n private readonly uniqueKeyPrefix: string;\n private readonly onInvalidEntry: InvalidEntryHandler | undefined;\n\n /**\n * Gets all current-contract positions owned by an address.\n *\n * @param owner - GalaChain owner identity.\n * @returns Enriched positions from the v2 trade backend, token images included. A row that is\n * not a usable position is skipped and reported to `onInvalidEntry`.\n * @example\n * ```typescript\n * const positions = await gSwap.positions.getUserPositions('client|012345678901234567890123');\n * ```\n */\n async getUserPositions(owner: string): Promise<Position[]> {\n const response = await this.get<unknown>('/positions', { user: owner });\n return readEntries('positions', response.data, parsePosition, this.onInvalidEntry);\n }\n\n /**\n * Gets one position, normalizing a caller-supplied reversed pair to canonical symbol order.\n * Reversed pairs also invert the price axis, so `[lower, upper]` becomes `[-upper, -lower]`.\n *\n * @param args - Pair, owner, fee, and position tick range.\n * @returns The position, or `null` when the backend returns 404.\n * @example\n * ```typescript\n * const position = await gSwap.positions.getPosition({\n * token0: 'GALA', token1: 'GUSDC', owner: 'client|012345678901234567890123',\n * fee: 3000, tickLower: -19200, tickUpper: 12000,\n * });\n * ```\n */\n async getPosition(\n args: {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n owner: string;\n tickLower: number;\n tickUpper: number;\n },\n signal?: AbortSignal,\n ): Promise<Position | null> {\n const response = await this.getNullable<unknown>(\n '/position',\n await this.positionParams(args),\n signal,\n );\n return response === null ? null : readPositionBody(response.data);\n }\n\n /**\n * Reads one position and classifies the outcome instead of throwing: `found` with the position\n * (fees included), `not_found` (the backend's HTTP 404, which it sends only when the chain\n * itself has no such position) or `unknown` (any other failure, carried as `error`). Use it\n * to confirm a write: `unknown` is \"no evidence either way\", never a confirmed absence.\n *\n * Only the caller's own abort rethrows. With a pass-through resolver ({@link CanonicalSymbols})\n * the pair and ticks go to the backend exactly as given and the backend orients them.\n *\n * @example\n * ```typescript\n * const read = await gSwap.positions.readPosition({\n * token0: 'GALA', token1: 'GUSDC', owner: 'client|012345678901234567890123',\n * fee: 3000, tickLower: -19200, tickUpper: 12000,\n * });\n * if (read.kind === 'found') console.log(read.data.fees0);\n * ```\n */\n async readPosition(\n args: {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n owner: string;\n tickLower: number;\n tickUpper: number;\n },\n signal?: AbortSignal,\n ): Promise<PositionRead> {\n try {\n const position = await this.getPosition(args, signal);\n return position === null ? { kind: 'not_found' } : { kind: 'found', data: position };\n } catch (error: unknown) {\n if (signal?.aborted === true) throw error;\n return {\n kind: 'unknown',\n error: isGSwapError(error)\n ? error\n : new GSwapSDKError('REQUEST_FAILED', { operation: 'position' }, { cause: error }),\n };\n }\n }\n\n /**\n * The `/position` query: canonical symbols and canonical ticks after a client-side flip, or,\n * for a pass-through resolver, the caller's references and ticks untouched.\n */\n private async positionParams(args: {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n owner: string;\n tickLower: number;\n tickUpper: number;\n }): Promise<Record<string, string>> {\n if (this.symbols.passthrough === true) {\n return {\n token0: rawTokenOf(args.token0),\n token1: rawTokenOf(args.token1),\n fee: `${args.fee}`,\n owner: args.owner,\n tickLower: `${args.tickLower}`,\n tickUpper: `${args.tickUpper}`,\n };\n }\n const pair = await this.orderPair(args.token0, args.token1);\n const ticks = this.canonicalTicks(args.tickLower, args.tickUpper, pair.flipped);\n return {\n token0: pair.token0.symbol,\n token1: pair.token1.symbol,\n fee: `${args.fee}`,\n owner: args.owner,\n tickLower: `${ticks.tickLower}`,\n tickUpper: `${ticks.tickUpper}`,\n };\n }\n\n /**\n * Estimates the other token amount and resulting liquidity for a deposit.\n *\n * @param args - Pair, tick range, deposit amount, and the amount's caller-side token index.\n * @returns Backend liquidity estimate.\n * @example\n * ```typescript\n * const estimate = await gSwap.positions.estimateAddLiquidity({\n * token0: 'GALA', token1: 'GUSDC', fee: 3000,\n * tickLower: -19200, tickUpper: 12000, amount: '100', amountIsToken0: true,\n * });\n * ```\n */\n async estimateAddLiquidity(args: {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n tickLower: number;\n tickUpper: number;\n amount: NumericAmount;\n amountIsToken0: boolean;\n }): Promise<AddLiquidityEstimate> {\n validateNumericAmount(args.amount, 'amount');\n const response = await this.get<AddLiquidityEstimateWire>('/add-liq-estimate', {\n token0: rawTokenOf(args.token0),\n token1: rawTokenOf(args.token1),\n fee: `${args.fee}`,\n tickLower: `${args.tickLower}`,\n tickUpper: `${args.tickUpper}`,\n amount: new BigNumber(args.amount).toFixed(),\n amountIsToken0: String(args.amountIsToken0),\n });\n return response.data;\n }\n\n /**\n * Estimates token amounts released by burning liquidity.\n *\n * @param args - Pair, tick range, fee tier, and liquidity quantity.\n * @returns Estimated token0 and token1 quantities.\n * @example\n * ```typescript\n * const estimate = await gSwap.positions.estimateRemoveLiquidity({\n * token0: 'GALA', token1: 'GUSDC', fee: 3000,\n * tickLower: -19200, tickUpper: 12000, liquidity: '1000',\n * });\n * ```\n */\n async estimateRemoveLiquidity(args: {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n tickLower: number;\n tickUpper: number;\n liquidity: NumericAmount;\n }): Promise<RemoveLiquidityEstimate> {\n validateNumericAmount(args.liquidity, 'liquidity');\n const response = await this.get<RemoveLiquidityEstimateWire>('/remove-liq-estimate', {\n token0: rawTokenOf(args.token0),\n token1: rawTokenOf(args.token1),\n fee: `${args.fee}`,\n tickLower: `${args.tickLower}`,\n tickUpper: `${args.tickUpper}`,\n liquidity: new BigNumber(args.liquidity).toFixed(),\n });\n return response.data;\n }\n\n /**\n * Adds liquidity using an explicit tick range. Exactly one canonical deposit field is emitted.\n *\n * @param args - Pair, fee, range, deposit amount, and caller-side token index.\n * @returns The synchronously submitted transaction.\n * @example\n * ```typescript\n * const tx = await gSwap.positions.addLiquidityByTicks({\n * token0: 'GALA', token1: 'GUSDC', fee: 3000,\n * tickLower: -19200, tickUpper: 12000, amount: '100', amountIsToken0: true,\n * });\n * await tx.confirm();\n * ```\n */\n async addLiquidityByTicks(args: AddLiquidityArgs) {\n return this.submitAddLiquidity(await this.buildAddLiquidity(args));\n }\n\n /**\n * Builds the AddLiquidity DTO that {@link addLiquidityByTicks} would sign, without signing or\n * sending it: to show in an approval popup, or to sign elsewhere. Submit exactly this DTO with\n * {@link submitAddLiquidity}. The pure counterpart, with canonical inputs and no resolution,\n * is `buildAddLiquidityDto`.\n *\n * @example\n * ```typescript\n * const dto = await gSwap.positions.buildAddLiquidity({\n * token0: 'GALA', token1: 'GUSDC', fee: 3000,\n * tickLower: -19200, tickUpper: 12000, amount: '100', amountIsToken0: true,\n * });\n * ```\n */\n async buildAddLiquidity(args: AddLiquidityArgs): Promise<AddLiquidityDTO> {\n validateFee(args.fee);\n const pair = await this.orderPair(args.token0, args.token1);\n const ticks = this.canonicalTicks(args.tickLower, args.tickUpper, pair.flipped);\n // Caller-facing checks first, so a refusal names the caller's own argument (`amount`).\n assertTickRange(ticks.tickLower, ticks.tickUpper, args.fee);\n validateNumericAmount(args.amount, 'amount');\n const base = {\n token0: pair.token0.symbol,\n token1: pair.token1.symbol,\n fee: args.fee,\n tickLower: ticks.tickLower,\n tickUpper: ticks.tickUpper,\n uniqueKey: this.uniqueKeyFor(args.uniqueKey),\n };\n return pair.flipped !== args.amountIsToken0\n ? buildAddLiquidityDto({ ...base, depositQuantityToken0: args.amount })\n : buildAddLiquidityDto({ ...base, depositQuantityToken1: args.amount });\n }\n\n /**\n * Signs and submits a DTO built by {@link buildAddLiquidity} or `buildAddLiquidityDto`; it must\n * be exactly what the builder produces. The returned transaction confirms by proving the\n * position's liquidity grew.\n */\n async submitAddLiquidity(dto: AddLiquidityDTO) {\n assertCanonicalV2Dto('AddLiquidity', dto);\n return this.submit('AddLiquidity', dto, { kind: 'add', identity: identityOf(dto) });\n }\n\n /**\n * Adds liquidity from prices, flooring the lower tick and ceiling the upper tick to fee spacing.\n * The resulting range is therefore no narrower than the requested price interval.\n *\n * @param args - Pair, fee, price range, deposit amount, and caller-side token index.\n * @returns The synchronously submitted transaction.\n * @example\n * ```typescript\n * const tx = await gSwap.positions.addLiquidityByPrice({\n * token0: 'GALA', token1: 'GUSDC', fee: 3000,\n * minPrice: '0.14', maxPrice: '0.16', amount: '100', amountIsToken0: true,\n * });\n * ```\n */\n async addLiquidityByPrice(args: {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n minPrice: PriceIn;\n maxPrice: PriceIn;\n amount: NumericAmount;\n amountIsToken0: boolean;\n /** The attempt's `uniqueKey`; default a fresh one (see {@link AddLiquidityArgs.uniqueKey}). */\n uniqueKey?: string | undefined;\n }) {\n validateFee(args.fee);\n this.rejectNumber(args.minPrice, 'minPrice');\n this.rejectNumber(args.maxPrice, 'maxPrice');\n const minPrice = new BigNumber(args.minPrice);\n const maxPrice = new BigNumber(args.maxPrice);\n if (\n !minPrice.isFinite() ||\n !maxPrice.isFinite() ||\n minPrice.isLessThanOrEqualTo(0) ||\n maxPrice.isLessThanOrEqualTo(0)\n ) {\n throw new GSwapSDKError('INVALID_PRICE_BOUNDS', {\n minPrice: String(args.minPrice),\n maxPrice: String(args.maxPrice),\n });\n }\n if (minPrice.isGreaterThan(maxPrice)) {\n throw new GSwapSDKError('PRICE_BOUNDS_ORDER', {\n minPrice: String(args.minPrice),\n maxPrice: String(args.maxPrice),\n });\n }\n const spacing = this.tickSpacing(args.fee);\n const tickLower = alignTickDown(tickFromPrice(minPrice), spacing);\n const tickUpper = alignTickUp(tickFromPrice(maxPrice), spacing);\n return this.addLiquidityByTicks({\n token0: args.token0,\n token1: args.token1,\n fee: args.fee,\n tickLower,\n tickUpper,\n amount: args.amount,\n amountIsToken0: args.amountIsToken0,\n uniqueKey: args.uniqueKey,\n });\n }\n\n /**\n * Removes liquidity by withdrawing at most one canonical token quantity.\n * Omitting both amounts closes the position and sweeps all accrued fees.\n *\n * @param args - Pair, fee, range, and optional caller-side withdrawal amount.\n * @returns The synchronously submitted transaction.\n * @example\n * ```typescript\n * const tx = await gSwap.positions.removeLiquidity({\n * token0: 'GALA', token1: 'GUSDC', fee: 3000,\n * tickLower: -19200, tickUpper: 12000,\n * });\n * ```\n */\n async removeLiquidity(args: RemoveLiquidityArgs) {\n return this.submitRemoveLiquidity(await this.buildRemoveLiquidity(args));\n }\n\n /**\n * Builds the RemoveLiquidity DTO that {@link removeLiquidity} would sign, without signing or\n * sending it. Submit exactly this DTO with {@link submitRemoveLiquidity}. The pure counterpart\n * is `buildRemoveLiquidityDto`.\n */\n async buildRemoveLiquidity(args: RemoveLiquidityArgs): Promise<RemoveLiquidityDTO> {\n validateFee(args.fee);\n const pair = await this.orderPair(args.token0, args.token1);\n const ticks = this.canonicalTicks(args.tickLower, args.tickUpper, pair.flipped);\n assertTickRange(ticks.tickLower, ticks.tickUpper, args.fee);\n if (args.amount0 !== undefined && args.amount1 !== undefined) {\n throw new GSwapSDKError('AT_MOST_ONE_OF_ALLOWED', { first: 'amount0', second: 'amount1' });\n }\n if (args.amount0 !== undefined) validateNumericAmount(args.amount0, 'amount0');\n if (args.amount1 !== undefined) validateNumericAmount(args.amount1, 'amount1');\n // The caller's amount0 / amount1 follow the caller's pair; the DTO's follow the canonical one.\n const [canonical0, canonical1] = pair.flipped\n ? [args.amount1, args.amount0]\n : [args.amount0, args.amount1];\n return buildRemoveLiquidityDto({\n token0: pair.token0.symbol,\n token1: pair.token1.symbol,\n fee: args.fee,\n tickLower: ticks.tickLower,\n tickUpper: ticks.tickUpper,\n uniqueKey: this.uniqueKeyFor(args.uniqueKey),\n withdrawalQuantityToken0: canonical0,\n withdrawalQuantityToken1: canonical1,\n });\n }\n\n /**\n * Signs and submits a DTO built by {@link buildRemoveLiquidity} or `buildRemoveLiquidityDto`;\n * it must be exactly what the builder produces. A DTO with no withdrawal quantity closes the\n * position, and the returned transaction then confirms by proving the position is gone.\n */\n async submitRemoveLiquidity(dto: RemoveLiquidityDTO) {\n assertCanonicalV2Dto('RemoveLiquidity', dto);\n const isFullClose =\n dto.withdrawalQuantityToken0 === undefined && dto.withdrawalQuantityToken1 === undefined;\n return this.submit('RemoveLiquidity', dto, {\n kind: isFullClose ? 'removeFull' : 'removePartial',\n identity: identityOf(dto),\n });\n }\n\n /**\n * Collects all accrued fees for a position; the contract does not support partial collection.\n *\n * @param args - Pair, fee, and tick range identifying the position.\n * @returns The synchronously submitted transaction.\n * @example\n * ```typescript\n * const tx = await gSwap.positions.collectPositionFees({\n * token0: 'GALA', token1: 'GUSDC', fee: 3000,\n * tickLower: -19200, tickUpper: 12000,\n * });\n * ```\n */\n async collectPositionFees(args: CollectPositionFeesArgs) {\n return this.submitCollectPositionFees(await this.buildCollectPositionFees(args));\n }\n\n /**\n * Builds the CollectPositionFees DTO that {@link collectPositionFees} would sign, without\n * signing or sending it. Submit exactly this DTO with {@link submitCollectPositionFees}. The\n * pure counterpart is `buildCollectPositionFeesDto`.\n */\n async buildCollectPositionFees(args: CollectPositionFeesArgs): Promise<CollectPositionFeesDTO> {\n validateFee(args.fee);\n const pair = await this.orderPair(args.token0, args.token1);\n const ticks = this.canonicalTicks(args.tickLower, args.tickUpper, pair.flipped);\n assertTickRange(ticks.tickLower, ticks.tickUpper, args.fee);\n return buildCollectPositionFeesDto({\n token0: pair.token0.symbol,\n token1: pair.token1.symbol,\n fee: args.fee,\n tickLower: ticks.tickLower,\n tickUpper: ticks.tickUpper,\n uniqueKey: this.uniqueKeyFor(args.uniqueKey),\n });\n }\n\n /**\n * Signs and submits a DTO built by {@link buildCollectPositionFees} or\n * `buildCollectPositionFeesDto`; it must be exactly what the builder produces.\n */\n async submitCollectPositionFees(dto: CollectPositionFeesDTO) {\n assertCanonicalV2Dto('CollectPositionFees', dto);\n return this.submit('CollectPositionFees', dto, { kind: 'collect', identity: identityOf(dto) });\n }\n\n /**\n * Creates a pool, resolving registered trading symbols.\n * Reversed symbol order inverts the supplied starting price or square-root price.\n *\n * A token class key carries no symbol, so for a token being proposed into the v2\n * trading-symbol registry for the first time (no existing pool has registered it yet) the\n * caller MUST pass `token0Symbol`/`token1Symbol` (the token class's symbol, from the token\n * list or FetchTokenClasses). The SDK never derives a symbol from the key, and throws a\n * TOKEN_SYMBOL_NOT_REGISTERED error when an unregistered token has none. For an already-registered token a\n * supplied symbol must match the registered one.\n *\n * @param args - Token class keys or references, fee, exactly one starting price field, and privacy options.\n * @returns The synchronously submitted transaction.\n * @example\n * ```typescript\n * const tx = await gSwap.positions.createPool({\n * token0: 'GALA|Unit|none|none', token1: 'GUSDC|Unit|none|none', fee: 3000,\n * startingPrice: '0.15',\n * });\n * ```\n */\n async createPool(args: CreatePoolArgs) {\n return this.submitCreatePool(await this.buildCreatePool(args));\n }\n\n /**\n * Builds the CreatePool DTO that {@link createPool} would sign, without signing or sending it:\n * symbols resolved or proposed, the pair put in canonical order and the price inverted when it\n * was reversed. Submit exactly this DTO with {@link submitCreatePool}. The pure counterpart,\n * with canonical inputs, is `buildCreatePoolDto`.\n */\n async buildCreatePool(args: CreatePoolArgs): Promise<CreatePoolDTO> {\n const hasPrice = args.startingPrice !== undefined;\n const hasSqrtPrice = args.startingSqrtPrice !== undefined;\n if (hasPrice === hasSqrtPrice) {\n throw new GSwapSDKError('EXACTLY_ONE_OF_REQUIRED', {\n first: 'startingPrice',\n second: 'startingSqrtPrice',\n });\n }\n validateFee(args.fee);\n const [tokenA, tokenB] = await Promise.all([\n this.resolveCreateToken(args.token0, args.token0Symbol),\n this.resolveCreateToken(args.token1, args.token1Symbol),\n ]);\n const flipped = tokenA.symbol > tokenB.symbol;\n const [token0, token1] = flipped ? [tokenB, tokenA] : [tokenA, tokenB];\n const sourcePriceInput = hasPrice\n ? (args.startingPrice as NumericAmount)\n : (args.startingSqrtPrice as NumericAmount);\n this.rejectNumber(sourcePriceInput, hasPrice ? 'startingPrice' : 'startingSqrtPrice');\n const sourcePrice = new BigNumber(sourcePriceInput);\n if (!sourcePrice.isFinite() || sourcePrice.isLessThanOrEqualTo(0)) {\n throw new GSwapSDKError('INVALID_STARTING_PRICE', { value: String(sourcePriceInput) });\n }\n const canonicalPrice = flipped ? new BigNumber(1).dividedBy(sourcePrice) : sourcePrice;\n const shared = {\n token0Key: token0.classKey,\n token1Key: token1.classKey,\n token0Symbol: token0.symbol,\n token1Symbol: token1.symbol,\n fee: args.fee,\n uniqueKey: this.uniqueKeyFor(args.uniqueKey),\n name: args.name,\n isPrivate: args.isPrivate,\n privateAccess: args.privateAccess,\n };\n return hasPrice\n ? buildCreatePoolDto({ ...shared, startingPrice: canonicalPrice })\n : buildCreatePoolDto({ ...shared, startingSqrtPrice: canonicalPrice });\n }\n\n /**\n * Signs and submits a DTO built by {@link buildCreatePool} or `buildCreatePoolDto`; it must be\n * exactly what the builder produces. The returned transaction confirms by the pool existing, and\n * the cached symbol registry is dropped, since the pool may have registered new symbols.\n */\n async submitCreatePool(dto: CreatePoolDTO) {\n assertCanonicalV2Dto('CreatePool', dto);\n const transaction = await this.submit('CreatePool', dto, {\n kind: 'createPool',\n token0: dto.token0Symbol,\n token1: dto.token1Symbol,\n fee: dto.fee,\n });\n this.symbols.invalidate?.();\n return transaction;\n }\n\n private async submit(\n method: string,\n dto: Record<string, unknown>,\n confirmation?: PositionSubmitConfirmation,\n ) {\n if (this.signer === undefined) throw GSwapSDKError.noSignerError();\n const signed = await signPreSend(this.signer, method, dto);\n const submitOptions =\n this.walletAddress === undefined ? {} : { walletAddress: this.walletAddress };\n const owner = this.walletAddress;\n const positionConfirmation = this.buildPositionConfirmation(confirmation, owner);\n const options: ChainSubmitOptions = {\n ...submitOptions,\n ...(positionConfirmation === undefined ? {} : { positionConfirmation }),\n };\n return this.gateway.submit(method, signed, options);\n }\n\n /**\n * Builds a confirmation closure that proves the write's own effect, not just that a\n * position row exists (true before most of these writes run too, for any operation on a\n * pre-existing position — existence alone would false-confirm before the write executes,\n * and would never confirm a full close, whose whole point is that the position stops\n * existing).\n *\n * The \"before\" state is captured lazily, on the confirmation closure's own first poll —\n * not eagerly here in `submit()` — so confirming a write never adds an extra round-trip to\n * the write itself; the cost of establishing the baseline is paid by `confirm()`, only if\n * and when the caller actually asks to confirm.\n */\n private buildPositionConfirmation(\n confirmation: PositionSubmitConfirmation | undefined,\n owner: string | undefined,\n ): ((signal: AbortSignal) => Promise<PositionConfirmationResult>) | undefined {\n if (confirmation === undefined) return undefined;\n\n if (confirmation.kind === 'createPool') {\n const { token0, token1, fee } = confirmation;\n return async (signal) => {\n const pool = await this.getNullable<unknown>(\n '/pool',\n { token0, token1, fee: `${fee}` },\n signal,\n );\n return pool === null ? { confirmed: false } : { confirmed: true, position: null };\n };\n }\n\n if (owner === undefined) return undefined;\n const { kind, identity } = confirmation;\n let baseline: { liquidity: BigNumber; fees: BigNumber } | undefined;\n\n return async (signal) => {\n const current = await this.getPosition({ ...identity, owner }, signal);\n\n if (baseline === undefined) {\n // First poll only establishes the baseline to compare future polls against — it can\n // never itself be the confirmed outcome (nothing has been observed to change yet).\n baseline = {\n liquidity: new BigNumber(current?.liquidity ?? 0),\n fees: new BigNumber(current?.fees0 ?? 0).plus(new BigNumber(current?.fees1 ?? 0)),\n };\n // A full close starting from \"already gone\" (e.g. a retried confirm after the position\n // was already swept) is confirmed immediately rather than waiting for a change that\n // already happened.\n return kind === 'removeFull' && current === null\n ? { confirmed: true, position: null }\n : { confirmed: false };\n }\n\n switch (kind) {\n case 'add':\n return current !== null &&\n new BigNumber(current.liquidity).isGreaterThan(baseline.liquidity)\n ? { confirmed: true, position: current }\n : { confirmed: false };\n case 'removePartial':\n return current !== null && new BigNumber(current.liquidity).isLessThan(baseline.liquidity)\n ? { confirmed: true, position: current }\n : { confirmed: false };\n case 'removeFull':\n return current === null ? { confirmed: true, position: null } : { confirmed: false };\n case 'collect': {\n if (current === null) return { confirmed: false };\n const currentFees = new BigNumber(current.fees0 ?? 0).plus(\n new BigNumber(current.fees1 ?? 0),\n );\n if (currentFees.isLessThan(baseline.fees)) return { confirmed: true, position: current };\n // Nothing to sweep in the first place — a legitimate no-op collect never moves\n // fees, so \"still zero\" is the confirmed outcome, not an unconfirmed one.\n if (baseline.fees.isZero() && currentFees.isZero())\n return { confirmed: true, position: current };\n return { confirmed: false };\n }\n }\n };\n }\n\n private async get<TData>(\n endpoint: string,\n params: Record<string, string>,\n signal?: AbortSignal,\n ): Promise<BackendEnvelope<TData>> {\n return this.http.sendGetRequest<BackendEnvelope<TData>>(\n this.gateway.dexBackendBaseUrl,\n '/v2/trade',\n endpoint,\n params,\n signal === undefined ? undefined : { signal },\n );\n }\n\n private async getNullable<TData>(\n endpoint: string,\n params: Record<string, string>,\n signal?: AbortSignal,\n ): Promise<BackendEnvelope<TData> | null> {\n try {\n return await this.get<TData>(endpoint, params, signal);\n } catch (error: unknown) {\n if (this.isNotFound(error)) return null;\n throw error;\n }\n }\n\n private isNotFound(error: unknown): boolean {\n return error instanceof GSwapSDKError && error.status === 404;\n }\n\n private canonicalTicks(tickLower: number, tickUpper: number, flipped: boolean) {\n return flipped ? { tickLower: -tickUpper, tickUpper: -tickLower } : { tickLower, tickUpper };\n }\n\n private tickSpacing(fee: number): number {\n const spacing = (TICK_SPACING_BY_FEE as Record<number, number>)[fee];\n if (spacing === undefined) {\n throw new GSwapSDKError('INVALID_FEE', {\n field: 'fee',\n value: fee,\n allowed: ALLOWED_FEE_TIERS_TEXT,\n });\n }\n return spacing;\n }\n\n private rejectNumber(value: unknown, parameterName: string): void {\n if (typeof value === 'number') {\n throw new GSwapSDKError('AMOUNT_IS_JS_NUMBER', { field: parameterName });\n }\n }\n\n /** The caller's `uniqueKey` (validated), else a fresh one. */\n private uniqueKeyFor(supplied: string | undefined): string {\n if (supplied === undefined) return createUniqueKey(this.uniqueKeyPrefix);\n assertUniqueKey(supplied);\n return supplied;\n }\n\n /**\n * The pair in canonical order. A resolver failure that is not an SDK error (an app's own\n * resolver, an app requestor's rejection during the registry read) is `TOKEN_RESOLUTION_FAILED`.\n */\n private orderPair(a: TokenRef, b: TokenRef) {\n return resolvePreSend(`${rawTokenOf(a)}/${rawTokenOf(b)}`, () => this.symbols.orderPair(a, b));\n }\n\n private async resolveCreateToken(\n ref: TokenRef,\n symbolOverride?: string,\n ): Promise<ResolvedCreateToken> {\n try {\n const lookupRef = typeof ref === 'object' ? compositeKeyOf(ref) : ref;\n const resolved = await resolvePreSend(rawTokenOf(ref), () => this.symbols.resolve(lookupRef));\n if (\n symbolOverride !== undefined &&\n symbolOverride !== '' &&\n symbolOverride !== resolved.symbol\n ) {\n throw new GSwapSDKError('SYMBOL_DISAGREES_WITH_REGISTRY', {\n token: rawTokenOf(ref),\n suppliedSymbol: symbolOverride,\n registeredSymbol: resolved.symbol,\n });\n }\n const suppliedClassKey = this.tryParseClassKey(ref);\n if (suppliedClassKey === undefined && resolved.collection === '') {\n // An offline resolver knows a bare symbol but not its class key; a pool needs the key.\n throw new GSwapSDKError('CREATE_POOL_NEEDS_CLASS_KEY', { token: rawTokenOf(ref) });\n }\n return {\n symbol: resolved.symbol,\n classKey:\n suppliedClassKey ??\n parseTokenClassKey({\n collection: resolved.collection,\n category: resolved.category,\n type: resolved.type,\n additionalKey: resolved.additionalKey,\n }),\n };\n } catch (error: unknown) {\n if (!(error instanceof GSwapSDKError) || error.code !== 'UNKNOWN_TOKEN') {\n throw error;\n }\n let classKey: ReturnType<typeof parseTokenClassKey>;\n try {\n classKey = parseTokenClassKey(ref);\n } catch {\n throw error;\n }\n // First-time registration: a class key carries NO symbol (collection and symbol disagree\n // for many tokens, launchpad tokens all share collection `Token`, and some collections\n // contain `$`), so the symbol to propose must be supplied by the caller from the token\n // class / registry. Never derive it from the key.\n if (symbolOverride === undefined || symbolOverride === '') {\n throw new GSwapSDKError('TOKEN_SYMBOL_NOT_REGISTERED', { token: rawTokenOf(ref) });\n }\n if (!isProposableTradingSymbol(symbolOverride)) {\n throw new GSwapSDKError('INVALID_TRADING_SYMBOL', { symbol: symbolOverride });\n }\n return { symbol: symbolOverride, classKey };\n }\n }\n\n private tryParseClassKey(ref: TokenRef): ReturnType<typeof parseTokenClassKey> | undefined {\n try {\n return parseTokenClassKey(ref);\n } catch {\n return undefined;\n }\n }\n}\n\nconst REQUIRED_POSITION_STRINGS = [\n 'token0Symbol',\n 'token1Symbol',\n 'owner',\n 'liquidity',\n 'amount0',\n 'amount1',\n] as const;\nconst REQUIRED_POSITION_NUMBERS = ['fee', 'tickLower', 'tickUpper'] as const;\nconst OPTIONAL_POSITION_STRINGS = [\n 'token0CompositeKey',\n 'token1CompositeKey',\n 'token0Image',\n 'token1Image',\n 'sqrtPrice',\n 'fees0',\n 'fees1',\n] as const;\n\n/**\n * Reads one position row of `/positions` or `/position`: the pair, owner, range, liquidity and\n * amounts must be usable; the composite keys, token images, current tick and price, and the\n * escrowed fees are carried when present. `pool` is the pool reference (`poolRef` wins over\n * `pool`; `''` when the row names neither).\n */\nfunction parsePosition(value: unknown): EntryParse<Position> {\n const wire = asEntryRecord(value);\n if (wire === undefined) return entryRejected('the row is not an object');\n for (const key of REQUIRED_POSITION_STRINGS) {\n if (typeof wire[key] !== 'string') return entryRejected(`${key} is not a string`);\n }\n for (const key of REQUIRED_POSITION_NUMBERS) {\n const number = wire[key];\n if (typeof number !== 'number' || !Number.isFinite(number)) {\n return entryRejected(`${key} is not a number`);\n }\n }\n if (typeof wire['inRange'] !== 'boolean') return entryRejected('inRange is not a boolean');\n for (const key of OPTIONAL_POSITION_STRINGS) {\n if (wire[key] !== undefined && typeof wire[key] !== 'string') {\n return entryRejected(`${key} is not a string`);\n }\n }\n const currentTick = wire['currentTick'];\n if (\n currentTick !== undefined &&\n (typeof currentTick !== 'number' || !Number.isFinite(currentTick))\n ) {\n return entryRejected('currentTick is not a number');\n }\n const pool = wire['poolRef'] ?? wire['pool'] ?? '';\n if (typeof pool !== 'string') return entryRejected('pool is not a string');\n const optional = Object.fromEntries(\n [...OPTIONAL_POSITION_STRINGS, 'currentTick' as const].flatMap((key) =>\n wire[key] === undefined ? [] : [[key, wire[key]]],\n ),\n );\n return entryOk({\n token0Symbol: wire['token0Symbol'] as string,\n token1Symbol: wire['token1Symbol'] as string,\n fee: wire['fee'] as number,\n tickLower: wire['tickLower'] as number,\n tickUpper: wire['tickUpper'] as number,\n liquidity: wire['liquidity'] as string,\n amount0: wire['amount0'] as string,\n amount1: wire['amount1'] as string,\n inRange: wire['inRange'],\n pool,\n owner: wire['owner'] as string,\n ...optional,\n });\n}\n\n/** The position inside a `/position` answer, or `INVALID_CHAIN_RESPONSE` when it is not one. */\nfunction readPositionBody(data: unknown): Position {\n const parsed = parsePosition(data);\n if (parsed.ok) return parsed.value;\n throw new GSwapSDKError('INVALID_POSITION_RESPONSE', { reason: parsed.reason }, { body: data });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"positions.js","names":[],"sources":["../../../src/classes/positions.ts"],"sourcesContent":["import BigNumber from 'bignumber.js';\nimport { ALLOWED_FEE_TIERS_TEXT } from './error_catalog.js';\nimport { GSwapSDKError, isGSwapError } from './gswap_sdk_error.js';\nimport type { ChainGateway, ChainSubmitOptions } from './gateway.js';\nimport type { PositionConfirmationResult, SubmittedTransaction } from './submitted_transaction.js';\nimport { HttpClient } from './http_client.js';\nimport type { GalaChainSigner } from './signers.js';\nimport { rawTokenOf, type Symbols, type TokenResolver } from './symbols.js';\nimport type { NumericAmount, PriceIn } from '../types/amounts.js';\nimport type { FEE_TIER } from '../types/fees.js';\nimport { TICK_SPACING_BY_FEE } from '../types/fees.js';\nimport type {\n AddLiquidityDTO,\n CollectPositionFeesDTO,\n CreatePoolDTO,\n RemoveLiquidityDTO,\n} from '../types/v2_dtos.js';\nimport type {\n AddLiquidityEstimate,\n Position,\n RemoveLiquidityEstimate,\n} from '../types/v2_results.js';\nimport type { WireOptions } from '../types/wire.js';\nimport {\n asEntryRecord,\n entryOk,\n entryRejected,\n readEntries,\n type EntryParse,\n type InvalidEntryHandler,\n} from '../utils/lenient.js';\nimport { compositeKeyOf } from '../utils/class_key.js';\nimport { type TokenRef, parseTokenClassKey } from '../utils/ordering.js';\nimport { resolvePreSend, signPreSend } from '../utils/pre_send.js';\nimport {\n isProposableTradingSymbol,\n validateFee,\n validateNumericAmount,\n} from '../utils/validation.js';\nimport { tickFromPrice } from '../utils/tick_math.js';\nimport { alignTickDown, alignTickUp, assertTickRange } from '../utils/ticks.js';\nimport {\n assertCanonicalV2Dto,\n buildAddLiquidityDto,\n buildCollectPositionFeesDto,\n buildCreatePoolDto,\n buildRemoveLiquidityDto,\n} from '../utils/dto_builders.js';\nimport {\n assertUniqueKey,\n createUniqueKey,\n resolveUniqueKeyPrefix,\n type UniqueKeyOptions,\n} from '../utils/unique_key.js';\n\ntype PositionGateway = Pick<ChainGateway, 'httpRequestor' | 'dexBackendBaseUrl'> & {\n submit: (\n method: string,\n signedBody: Record<string, unknown>,\n options?: ChainSubmitOptions,\n ) => Promise<SubmittedTransaction<Position | null>>;\n requestTimeoutMs?: number | undefined;\n wire?: WireOptions | undefined;\n};\ntype PositionSymbols = TokenResolver & Pick<Symbols, 'orderPair'> & { invalidate?: () => void };\n\n/** Options of {@link Positions}: the `uniqueKey` prefix and a handler for skipped list rows. */\nexport interface PositionsOptions extends UniqueKeyOptions {\n /** Told about every position row of a list that was skipped as unusable. */\n onInvalidEntry?: InvalidEntryHandler | undefined;\n}\n\n/** A write's optional attempt identity. */\ninterface UniqueKeyArg {\n /**\n * The attempt's `uniqueKey` (1-128 characters of `A-Za-z0-9._:-`). Default: a fresh\n * `<uniqueKeyPrefix><uuid>`. Supply one to know the key before signing, or to retry the SAME\n * attempt after a lost response; never reuse it for another write.\n */\n uniqueKey?: string | undefined;\n}\n\n/** Arguments of {@link Positions.addLiquidityByTicks} and {@link Positions.buildAddLiquidity}. */\nexport interface AddLiquidityArgs extends UniqueKeyArg {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n tickLower: number;\n tickUpper: number;\n amount: NumericAmount;\n /** Whether `amount` is of `token0` as the caller passed it (not of the canonical token0). */\n amountIsToken0: boolean;\n}\n\n/** Arguments of {@link Positions.removeLiquidity} and {@link Positions.buildRemoveLiquidity}. */\nexport interface RemoveLiquidityArgs extends UniqueKeyArg {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n tickLower: number;\n tickUpper: number;\n /** Withdraw this much of the caller's `token0`; give at most one of `amount0` / `amount1`. */\n amount0?: NumericAmount | undefined;\n amount1?: NumericAmount | undefined;\n}\n\n/** Arguments of {@link Positions.collectPositionFees} and {@link Positions.buildCollectPositionFees}. */\nexport interface CollectPositionFeesArgs extends UniqueKeyArg {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n tickLower: number;\n tickUpper: number;\n}\n\n/** Arguments of {@link Positions.createPool} and {@link Positions.buildCreatePool}. */\nexport interface CreatePoolArgs extends UniqueKeyArg {\n token0: TokenRef;\n token1: TokenRef;\n token0Symbol?: string | undefined;\n token1Symbol?: string | undefined;\n fee: FEE_TIER;\n startingPrice?: NumericAmount | undefined;\n startingSqrtPrice?: NumericAmount | undefined;\n /** Optional pool display name, sent as the DTO's `name`; omitted when undefined or empty. */\n name?: string | undefined;\n isPrivate?: boolean | undefined;\n privateAccess?: string[] | undefined;\n}\n\n/** The pool position a liquidity DTO names, as its confirmation needs it. */\nfunction identityOf(\n dto: Pick<CollectPositionFeesDTO, 'token0' | 'token1' | 'fee' | 'tickLower' | 'tickUpper'>,\n): PositionIdentity {\n return {\n token0: dto.token0,\n token1: dto.token1,\n fee: dto.fee,\n tickLower: dto.tickLower,\n tickUpper: dto.tickUpper,\n };\n}\n\n/**\n * The outcome of {@link Positions.readPosition}. Only `not_found` is chain-confirmed absence (the\n * backend answers HTTP 404 only when the chain itself found no such position); `unknown` is \"no\n * evidence either way\" — a transport failure, a gateway timeout, any other error status or an\n * answer that is not a position — and must never be read as a removal.\n */\nexport type PositionRead =\n | { kind: 'found'; data: Position }\n | { kind: 'not_found' }\n | { kind: 'unknown'; error: GSwapSDKError };\n\ninterface BackendEnvelope<TData> {\n data: TData;\n}\n\ninterface AddLiquidityEstimateWire {\n amount0: string;\n amount1: string;\n liquidity: string;\n token0Symbol: string;\n token1Symbol: string;\n tickLower: number;\n tickUpper: number;\n amountIsCanonicalToken0: boolean;\n}\n\ninterface RemoveLiquidityEstimateWire {\n amount0: string;\n amount1: string;\n}\n\ninterface ResolvedCreateToken {\n symbol: string;\n classKey: ReturnType<typeof parseTokenClassKey>;\n}\n\ninterface PositionIdentity {\n token0: string;\n token1: string;\n fee: FEE_TIER;\n tickLower: number;\n tickUpper: number;\n}\n\n/** Tells `submit()` how to prove a write's own effect for `SubmittedTransaction.confirm()`. */\ntype PositionSubmitConfirmation =\n | { kind: 'add' | 'removePartial' | 'removeFull' | 'collect'; identity: PositionIdentity }\n | { kind: 'createPool'; token0: string; token1: string; fee: FEE_TIER };\n\n/**\n * Manages current-contract concentrated-liquidity positions.\n *\n * @example\n * ```typescript\n * const positions = gSwap.positions;\n * const mine = await positions.getUserPositions('client|012345678901234567890123');\n * console.log(mine.length);\n * ```\n */\nexport class Positions {\n constructor(\n private readonly gateway: PositionGateway,\n private readonly symbols: PositionSymbols,\n private readonly signer?: GalaChainSigner,\n private readonly walletAddress?: string,\n options: PositionsOptions = {},\n ) {\n this.http = new HttpClient(\n gateway.httpRequestor,\n gateway.requestTimeoutMs ?? 30_000,\n gateway.wire,\n );\n this.uniqueKeyPrefix = resolveUniqueKeyPrefix(options.uniqueKeyPrefix);\n this.onInvalidEntry = options.onInvalidEntry;\n }\n\n private readonly http: HttpClient;\n private readonly uniqueKeyPrefix: string;\n private readonly onInvalidEntry: InvalidEntryHandler | undefined;\n\n /**\n * Gets all current-contract positions owned by an address.\n *\n * @param owner - GalaChain owner identity.\n * @returns Enriched positions from the v2 trade backend, token images included. A row that is\n * not a usable position is skipped and reported to `onInvalidEntry`.\n * @example\n * ```typescript\n * const positions = await gSwap.positions.getUserPositions('client|012345678901234567890123');\n * ```\n */\n async getUserPositions(owner: string): Promise<Position[]> {\n const response = await this.get<unknown>('/positions', { user: owner });\n return readEntries('positions', response.data, parsePosition, this.onInvalidEntry);\n }\n\n /**\n * Gets one position, normalizing a caller-supplied reversed pair to canonical symbol order.\n * Reversed pairs also invert the price axis, so `[lower, upper]` becomes `[-upper, -lower]`.\n *\n * @param args - Pair, owner, fee, and position tick range.\n * @returns The position, or `null` when the backend returns 404.\n * @example\n * ```typescript\n * const position = await gSwap.positions.getPosition({\n * token0: 'GALA', token1: 'GUSDC', owner: 'client|012345678901234567890123',\n * fee: 3000, tickLower: -19200, tickUpper: 12000,\n * });\n * ```\n */\n async getPosition(\n args: {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n owner: string;\n tickLower: number;\n tickUpper: number;\n },\n signal?: AbortSignal,\n ): Promise<Position | null> {\n const response = await this.getNullable<unknown>(\n '/position',\n await this.positionParams(args),\n signal,\n );\n return response === null ? null : readPositionBody(response.data);\n }\n\n /**\n * Reads one position and classifies the outcome instead of throwing: `found` with the position\n * (fees included), `not_found` (the backend's HTTP 404, which it sends only when the chain\n * itself has no such position) or `unknown` (any other failure, carried as `error`). Use it\n * to confirm a write: `unknown` is \"no evidence either way\", never a confirmed absence.\n *\n * Only the caller's own abort rethrows. With a pass-through resolver ({@link CanonicalSymbols})\n * the pair and ticks go to the backend exactly as given and the backend orients them.\n *\n * @example\n * ```typescript\n * const read = await gSwap.positions.readPosition({\n * token0: 'GALA', token1: 'GUSDC', owner: 'client|012345678901234567890123',\n * fee: 3000, tickLower: -19200, tickUpper: 12000,\n * });\n * if (read.kind === 'found') console.log(read.data.fees0);\n * ```\n */\n async readPosition(\n args: {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n owner: string;\n tickLower: number;\n tickUpper: number;\n },\n signal?: AbortSignal,\n ): Promise<PositionRead> {\n try {\n const position = await this.getPosition(args, signal);\n return position === null ? { kind: 'not_found' } : { kind: 'found', data: position };\n } catch (error: unknown) {\n if (signal?.aborted === true) throw error;\n return {\n kind: 'unknown',\n error: isGSwapError(error)\n ? error\n : new GSwapSDKError('REQUEST_FAILED', { operation: 'position' }, { cause: error }),\n };\n }\n }\n\n /**\n * The `/position` query: canonical symbols and canonical ticks after a client-side flip, or,\n * for a pass-through resolver, the caller's references and ticks untouched.\n */\n private async positionParams(args: {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n owner: string;\n tickLower: number;\n tickUpper: number;\n }): Promise<Record<string, string>> {\n if (this.symbols.passthrough === true) {\n return {\n token0: rawTokenOf(args.token0),\n token1: rawTokenOf(args.token1),\n fee: `${args.fee}`,\n owner: args.owner,\n tickLower: `${args.tickLower}`,\n tickUpper: `${args.tickUpper}`,\n };\n }\n const pair = await this.orderPair(args.token0, args.token1);\n const ticks = this.canonicalTicks(args.tickLower, args.tickUpper, pair.flipped);\n return {\n token0: pair.token0.symbol,\n token1: pair.token1.symbol,\n fee: `${args.fee}`,\n owner: args.owner,\n tickLower: `${ticks.tickLower}`,\n tickUpper: `${ticks.tickUpper}`,\n };\n }\n\n /**\n * Estimates the other token amount and resulting liquidity for a deposit.\n *\n * @param args - Pair, tick range, deposit amount, and the amount's caller-side token index.\n * @returns Backend liquidity estimate.\n * @example\n * ```typescript\n * const estimate = await gSwap.positions.estimateAddLiquidity({\n * token0: 'GALA', token1: 'GUSDC', fee: 3000,\n * tickLower: -19200, tickUpper: 12000, amount: '100', amountIsToken0: true,\n * });\n * ```\n */\n async estimateAddLiquidity(args: {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n tickLower: number;\n tickUpper: number;\n amount: NumericAmount;\n amountIsToken0: boolean;\n }): Promise<AddLiquidityEstimate> {\n validateNumericAmount(args.amount, 'amount');\n const response = await this.get<AddLiquidityEstimateWire>('/add-liq-estimate', {\n token0: rawTokenOf(args.token0),\n token1: rawTokenOf(args.token1),\n fee: `${args.fee}`,\n tickLower: `${args.tickLower}`,\n tickUpper: `${args.tickUpper}`,\n amount: new BigNumber(args.amount).toFixed(),\n amountIsToken0: String(args.amountIsToken0),\n });\n return response.data;\n }\n\n /**\n * Estimates token amounts released by burning liquidity.\n *\n * @param args - Pair, tick range, fee tier, and liquidity quantity.\n * @returns Estimated token0 and token1 quantities.\n * @example\n * ```typescript\n * const estimate = await gSwap.positions.estimateRemoveLiquidity({\n * token0: 'GALA', token1: 'GUSDC', fee: 3000,\n * tickLower: -19200, tickUpper: 12000, liquidity: '1000',\n * });\n * ```\n */\n async estimateRemoveLiquidity(args: {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n tickLower: number;\n tickUpper: number;\n liquidity: NumericAmount;\n }): Promise<RemoveLiquidityEstimate> {\n validateNumericAmount(args.liquidity, 'liquidity');\n const response = await this.get<RemoveLiquidityEstimateWire>('/remove-liq-estimate', {\n token0: rawTokenOf(args.token0),\n token1: rawTokenOf(args.token1),\n fee: `${args.fee}`,\n tickLower: `${args.tickLower}`,\n tickUpper: `${args.tickUpper}`,\n liquidity: new BigNumber(args.liquidity).toFixed(),\n });\n return response.data;\n }\n\n /**\n * Adds liquidity using an explicit tick range. Exactly one canonical deposit field is emitted.\n *\n * @param args - Pair, fee, range, deposit amount, and caller-side token index.\n * @returns The synchronously submitted transaction.\n * @example\n * ```typescript\n * const tx = await gSwap.positions.addLiquidityByTicks({\n * token0: 'GALA', token1: 'GUSDC', fee: 3000,\n * tickLower: -19200, tickUpper: 12000, amount: '100', amountIsToken0: true,\n * });\n * await tx.confirm();\n * ```\n */\n async addLiquidityByTicks(args: AddLiquidityArgs) {\n return this.submitAddLiquidity(await this.buildAddLiquidity(args));\n }\n\n /**\n * Builds the AddLiquidity DTO that {@link addLiquidityByTicks} would sign, without signing or\n * sending it: to show in an approval popup, or to sign elsewhere. Submit exactly this DTO with\n * {@link submitAddLiquidity}. The pure counterpart, with canonical inputs and no resolution,\n * is `buildAddLiquidityDto`.\n *\n * @example\n * ```typescript\n * const dto = await gSwap.positions.buildAddLiquidity({\n * token0: 'GALA', token1: 'GUSDC', fee: 3000,\n * tickLower: -19200, tickUpper: 12000, amount: '100', amountIsToken0: true,\n * });\n * ```\n */\n async buildAddLiquidity(args: AddLiquidityArgs): Promise<AddLiquidityDTO> {\n validateFee(args.fee);\n const pair = await this.orderPair(args.token0, args.token1);\n const ticks = this.canonicalTicks(args.tickLower, args.tickUpper, pair.flipped);\n // Caller-facing checks first, so a refusal names the caller's own argument (`amount`).\n assertTickRange(ticks.tickLower, ticks.tickUpper, args.fee);\n validateNumericAmount(args.amount, 'amount');\n const base = {\n token0: pair.token0.symbol,\n token1: pair.token1.symbol,\n fee: args.fee,\n tickLower: ticks.tickLower,\n tickUpper: ticks.tickUpper,\n uniqueKey: this.uniqueKeyFor(args.uniqueKey),\n };\n return pair.flipped !== args.amountIsToken0\n ? buildAddLiquidityDto({ ...base, depositQuantityToken0: args.amount })\n : buildAddLiquidityDto({ ...base, depositQuantityToken1: args.amount });\n }\n\n /**\n * Signs and submits a DTO built by {@link buildAddLiquidity} or `buildAddLiquidityDto`; it must\n * be exactly what the builder produces. The returned transaction confirms by proving the\n * position's liquidity grew.\n */\n async submitAddLiquidity(dto: AddLiquidityDTO) {\n assertCanonicalV2Dto('AddLiquidity', dto);\n return this.submit('AddLiquidity', dto, { kind: 'add', identity: identityOf(dto) });\n }\n\n /**\n * Adds liquidity from prices, flooring the lower tick and ceiling the upper tick to fee spacing.\n * The resulting range is therefore no narrower than the requested price interval.\n *\n * @param args - Pair, fee, price range, deposit amount, and caller-side token index.\n * @returns The synchronously submitted transaction.\n * @example\n * ```typescript\n * const tx = await gSwap.positions.addLiquidityByPrice({\n * token0: 'GALA', token1: 'GUSDC', fee: 3000,\n * minPrice: '0.14', maxPrice: '0.16', amount: '100', amountIsToken0: true,\n * });\n * ```\n */\n async addLiquidityByPrice(args: {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n minPrice: PriceIn;\n maxPrice: PriceIn;\n amount: NumericAmount;\n amountIsToken0: boolean;\n /** The attempt's `uniqueKey`; default a fresh one (see {@link AddLiquidityArgs.uniqueKey}). */\n uniqueKey?: string | undefined;\n }) {\n validateFee(args.fee);\n this.rejectNumber(args.minPrice, 'minPrice');\n this.rejectNumber(args.maxPrice, 'maxPrice');\n const minPrice = new BigNumber(args.minPrice);\n const maxPrice = new BigNumber(args.maxPrice);\n if (\n !minPrice.isFinite() ||\n !maxPrice.isFinite() ||\n minPrice.isLessThanOrEqualTo(0) ||\n maxPrice.isLessThanOrEqualTo(0)\n ) {\n throw new GSwapSDKError('INVALID_PRICE_BOUNDS', {\n minPrice: String(args.minPrice),\n maxPrice: String(args.maxPrice),\n });\n }\n if (minPrice.isGreaterThan(maxPrice)) {\n throw new GSwapSDKError('PRICE_BOUNDS_ORDER', {\n minPrice: String(args.minPrice),\n maxPrice: String(args.maxPrice),\n });\n }\n const spacing = this.tickSpacing(args.fee);\n const tickLower = alignTickDown(tickFromPrice(minPrice), spacing);\n const tickUpper = alignTickUp(tickFromPrice(maxPrice), spacing);\n return this.addLiquidityByTicks({\n token0: args.token0,\n token1: args.token1,\n fee: args.fee,\n tickLower,\n tickUpper,\n amount: args.amount,\n amountIsToken0: args.amountIsToken0,\n uniqueKey: args.uniqueKey,\n });\n }\n\n /**\n * Removes liquidity by withdrawing at most one canonical token quantity.\n * Omitting both amounts closes the position and sweeps all accrued fees.\n *\n * @param args - Pair, fee, range, and optional caller-side withdrawal amount.\n * @returns The synchronously submitted transaction.\n * @example\n * ```typescript\n * const tx = await gSwap.positions.removeLiquidity({\n * token0: 'GALA', token1: 'GUSDC', fee: 3000,\n * tickLower: -19200, tickUpper: 12000,\n * });\n * ```\n */\n async removeLiquidity(args: RemoveLiquidityArgs) {\n return this.submitRemoveLiquidity(await this.buildRemoveLiquidity(args));\n }\n\n /**\n * Builds the RemoveLiquidity DTO that {@link removeLiquidity} would sign, without signing or\n * sending it. Submit exactly this DTO with {@link submitRemoveLiquidity}. The pure counterpart\n * is `buildRemoveLiquidityDto`.\n */\n async buildRemoveLiquidity(args: RemoveLiquidityArgs): Promise<RemoveLiquidityDTO> {\n validateFee(args.fee);\n const pair = await this.orderPair(args.token0, args.token1);\n const ticks = this.canonicalTicks(args.tickLower, args.tickUpper, pair.flipped);\n assertTickRange(ticks.tickLower, ticks.tickUpper, args.fee);\n if (args.amount0 !== undefined && args.amount1 !== undefined) {\n throw new GSwapSDKError('AT_MOST_ONE_OF_ALLOWED', { first: 'amount0', second: 'amount1' });\n }\n if (args.amount0 !== undefined) validateNumericAmount(args.amount0, 'amount0');\n if (args.amount1 !== undefined) validateNumericAmount(args.amount1, 'amount1');\n // The caller's amount0 / amount1 follow the caller's pair; the DTO's follow the canonical one.\n const [canonical0, canonical1] = pair.flipped\n ? [args.amount1, args.amount0]\n : [args.amount0, args.amount1];\n return buildRemoveLiquidityDto({\n token0: pair.token0.symbol,\n token1: pair.token1.symbol,\n fee: args.fee,\n tickLower: ticks.tickLower,\n tickUpper: ticks.tickUpper,\n uniqueKey: this.uniqueKeyFor(args.uniqueKey),\n withdrawalQuantityToken0: canonical0,\n withdrawalQuantityToken1: canonical1,\n });\n }\n\n /**\n * Signs and submits a DTO built by {@link buildRemoveLiquidity} or `buildRemoveLiquidityDto`;\n * it must be exactly what the builder produces. A DTO with no withdrawal quantity closes the\n * position, and the returned transaction then confirms by proving the position is gone.\n */\n async submitRemoveLiquidity(dto: RemoveLiquidityDTO) {\n assertCanonicalV2Dto('RemoveLiquidity', dto);\n const isFullClose =\n dto.withdrawalQuantityToken0 === undefined && dto.withdrawalQuantityToken1 === undefined;\n return this.submit('RemoveLiquidity', dto, {\n kind: isFullClose ? 'removeFull' : 'removePartial',\n identity: identityOf(dto),\n });\n }\n\n /**\n * Collects all accrued fees for a position; the contract does not support partial collection.\n *\n * @param args - Pair, fee, and tick range identifying the position.\n * @returns The synchronously submitted transaction.\n * @example\n * ```typescript\n * const tx = await gSwap.positions.collectPositionFees({\n * token0: 'GALA', token1: 'GUSDC', fee: 3000,\n * tickLower: -19200, tickUpper: 12000,\n * });\n * ```\n */\n async collectPositionFees(args: CollectPositionFeesArgs) {\n return this.submitCollectPositionFees(await this.buildCollectPositionFees(args));\n }\n\n /**\n * Builds the CollectPositionFees DTO that {@link collectPositionFees} would sign, without\n * signing or sending it. Submit exactly this DTO with {@link submitCollectPositionFees}. The\n * pure counterpart is `buildCollectPositionFeesDto`.\n */\n async buildCollectPositionFees(args: CollectPositionFeesArgs): Promise<CollectPositionFeesDTO> {\n validateFee(args.fee);\n const pair = await this.orderPair(args.token0, args.token1);\n const ticks = this.canonicalTicks(args.tickLower, args.tickUpper, pair.flipped);\n assertTickRange(ticks.tickLower, ticks.tickUpper, args.fee);\n return buildCollectPositionFeesDto({\n token0: pair.token0.symbol,\n token1: pair.token1.symbol,\n fee: args.fee,\n tickLower: ticks.tickLower,\n tickUpper: ticks.tickUpper,\n uniqueKey: this.uniqueKeyFor(args.uniqueKey),\n });\n }\n\n /**\n * Signs and submits a DTO built by {@link buildCollectPositionFees} or\n * `buildCollectPositionFeesDto`; it must be exactly what the builder produces.\n */\n async submitCollectPositionFees(dto: CollectPositionFeesDTO) {\n assertCanonicalV2Dto('CollectPositionFees', dto);\n return this.submit('CollectPositionFees', dto, { kind: 'collect', identity: identityOf(dto) });\n }\n\n /**\n * Creates a pool, resolving registered trading symbols.\n * Reversed symbol order inverts the supplied starting price or square-root price.\n *\n * A token class key carries no symbol, so for a token being proposed into the v2\n * trading-symbol registry for the first time (no existing pool has registered it yet) the\n * caller MUST pass `token0Symbol`/`token1Symbol` (the token class's symbol, from the token\n * list or FetchTokenClasses). The SDK never derives a symbol from the key, and throws a\n * TOKEN_SYMBOL_NOT_REGISTERED error when an unregistered token has none. For an already-registered token a\n * supplied symbol must match the registered one.\n *\n * @param args - Token class keys or references, fee, exactly one starting price field, and privacy options.\n * @returns The synchronously submitted transaction.\n * @example\n * ```typescript\n * const tx = await gSwap.positions.createPool({\n * token0: 'GALA|Unit|none|none', token1: 'GUSDC|Unit|none|none', fee: 3000,\n * startingPrice: '0.15',\n * });\n * ```\n */\n async createPool(args: CreatePoolArgs) {\n return this.submitCreatePool(await this.buildCreatePool(args));\n }\n\n /**\n * Builds the CreatePool DTO that {@link createPool} would sign, without signing or sending it:\n * symbols resolved or proposed, the pair put in canonical order and the price inverted when it\n * was reversed. Submit exactly this DTO with {@link submitCreatePool}. The pure counterpart,\n * with canonical inputs, is `buildCreatePoolDto`.\n */\n async buildCreatePool(args: CreatePoolArgs): Promise<CreatePoolDTO> {\n const hasPrice = args.startingPrice !== undefined;\n const hasSqrtPrice = args.startingSqrtPrice !== undefined;\n if (hasPrice === hasSqrtPrice) {\n throw new GSwapSDKError('EXACTLY_ONE_OF_REQUIRED', {\n first: 'startingPrice',\n second: 'startingSqrtPrice',\n });\n }\n validateFee(args.fee);\n const [tokenA, tokenB] = await Promise.all([\n this.resolveCreateToken(args.token0, args.token0Symbol),\n this.resolveCreateToken(args.token1, args.token1Symbol),\n ]);\n const flipped = tokenA.symbol > tokenB.symbol;\n const [token0, token1] = flipped ? [tokenB, tokenA] : [tokenA, tokenB];\n const sourcePriceInput = hasPrice\n ? (args.startingPrice as NumericAmount)\n : (args.startingSqrtPrice as NumericAmount);\n this.rejectNumber(sourcePriceInput, hasPrice ? 'startingPrice' : 'startingSqrtPrice');\n const sourcePrice = new BigNumber(sourcePriceInput);\n if (!sourcePrice.isFinite() || sourcePrice.isLessThanOrEqualTo(0)) {\n throw new GSwapSDKError('INVALID_STARTING_PRICE', { value: String(sourcePriceInput) });\n }\n const canonicalPrice = flipped ? new BigNumber(1).dividedBy(sourcePrice) : sourcePrice;\n const shared = {\n token0Key: token0.classKey,\n token1Key: token1.classKey,\n token0Symbol: token0.symbol,\n token1Symbol: token1.symbol,\n fee: args.fee,\n uniqueKey: this.uniqueKeyFor(args.uniqueKey),\n name: args.name,\n isPrivate: args.isPrivate,\n privateAccess: args.privateAccess,\n };\n return hasPrice\n ? buildCreatePoolDto({ ...shared, startingPrice: canonicalPrice })\n : buildCreatePoolDto({ ...shared, startingSqrtPrice: canonicalPrice });\n }\n\n /**\n * Signs and submits a DTO built by {@link buildCreatePool} or `buildCreatePoolDto`; it must be\n * exactly what the builder produces. The returned transaction confirms by the pool existing, and\n * the cached symbol registry is dropped, since the pool may have registered new symbols.\n */\n async submitCreatePool(dto: CreatePoolDTO) {\n assertCanonicalV2Dto('CreatePool', dto);\n const transaction = await this.submit('CreatePool', dto, {\n kind: 'createPool',\n token0: dto.token0Symbol,\n token1: dto.token1Symbol,\n fee: dto.fee,\n });\n this.symbols.invalidate?.();\n return transaction;\n }\n\n private async submit(\n method: string,\n dto: Record<string, unknown>,\n confirmation?: PositionSubmitConfirmation,\n ) {\n if (this.signer === undefined) throw GSwapSDKError.noSignerError();\n const signed = await signPreSend(this.signer, method, dto);\n const submitOptions =\n this.walletAddress === undefined ? {} : { walletAddress: this.walletAddress };\n const owner = this.walletAddress;\n const positionConfirmation = this.buildPositionConfirmation(confirmation, owner);\n const options: ChainSubmitOptions = {\n ...submitOptions,\n ...(positionConfirmation === undefined ? {} : { positionConfirmation }),\n };\n return this.gateway.submit(method, signed, options);\n }\n\n /**\n * Builds a confirmation closure that proves the write's own effect, not just that a\n * position row exists (true before most of these writes run too, for any operation on a\n * pre-existing position — existence alone would false-confirm before the write executes,\n * and would never confirm a full close, whose whole point is that the position stops\n * existing).\n *\n * The \"before\" state is captured lazily, on the confirmation closure's own first poll —\n * not eagerly here in `submit()` — so confirming a write never adds an extra round-trip to\n * the write itself; the cost of establishing the baseline is paid by `confirm()`, only if\n * and when the caller actually asks to confirm.\n */\n private buildPositionConfirmation(\n confirmation: PositionSubmitConfirmation | undefined,\n owner: string | undefined,\n ): ((signal: AbortSignal) => Promise<PositionConfirmationResult>) | undefined {\n if (confirmation === undefined) return undefined;\n\n if (confirmation.kind === 'createPool') {\n const { token0, token1, fee } = confirmation;\n return async (signal) => {\n const pool = await this.getNullable<unknown>(\n '/pool',\n { token0, token1, fee: `${fee}` },\n signal,\n );\n return pool === null ? { confirmed: false } : { confirmed: true, position: null };\n };\n }\n\n if (owner === undefined) return undefined;\n const { kind, identity } = confirmation;\n let baseline: { liquidity: BigNumber; fees: BigNumber } | undefined;\n\n return async (signal) => {\n const current = await this.getPosition({ ...identity, owner }, signal);\n\n if (baseline === undefined) {\n // First poll only establishes the baseline to compare future polls against — it can\n // never itself be the confirmed outcome (nothing has been observed to change yet).\n baseline = {\n liquidity: new BigNumber(current?.liquidity ?? 0),\n fees: new BigNumber(current?.fees0 ?? 0).plus(new BigNumber(current?.fees1 ?? 0)),\n };\n // A full close starting from \"already gone\" (e.g. a retried confirm after the position\n // was already swept) is confirmed immediately rather than waiting for a change that\n // already happened.\n return kind === 'removeFull' && current === null\n ? { confirmed: true, position: null }\n : { confirmed: false };\n }\n\n switch (kind) {\n case 'add':\n return current !== null &&\n new BigNumber(current.liquidity).isGreaterThan(baseline.liquidity)\n ? { confirmed: true, position: current }\n : { confirmed: false };\n case 'removePartial':\n return current !== null && new BigNumber(current.liquidity).isLessThan(baseline.liquidity)\n ? { confirmed: true, position: current }\n : { confirmed: false };\n case 'removeFull':\n return current === null ? { confirmed: true, position: null } : { confirmed: false };\n case 'collect': {\n if (current === null) return { confirmed: false };\n const currentFees = new BigNumber(current.fees0 ?? 0).plus(\n new BigNumber(current.fees1 ?? 0),\n );\n if (currentFees.isLessThan(baseline.fees)) return { confirmed: true, position: current };\n // Nothing to sweep in the first place — a legitimate no-op collect never moves\n // fees, so \"still zero\" is the confirmed outcome, not an unconfirmed one.\n if (baseline.fees.isZero() && currentFees.isZero())\n return { confirmed: true, position: current };\n return { confirmed: false };\n }\n }\n };\n }\n\n private async get<TData>(\n endpoint: string,\n params: Record<string, string>,\n signal?: AbortSignal,\n ): Promise<BackendEnvelope<TData>> {\n return this.http.sendGetRequest<BackendEnvelope<TData>>(\n this.gateway.dexBackendBaseUrl,\n '/v2/trade',\n endpoint,\n params,\n signal === undefined ? undefined : { signal },\n );\n }\n\n private async getNullable<TData>(\n endpoint: string,\n params: Record<string, string>,\n signal?: AbortSignal,\n ): Promise<BackendEnvelope<TData> | null> {\n try {\n return await this.get<TData>(endpoint, params, signal);\n } catch (error: unknown) {\n if (this.isNotFound(error)) return null;\n throw error;\n }\n }\n\n private isNotFound(error: unknown): boolean {\n return error instanceof GSwapSDKError && error.status === 404;\n }\n\n private canonicalTicks(tickLower: number, tickUpper: number, flipped: boolean) {\n return flipped ? { tickLower: -tickUpper, tickUpper: -tickLower } : { tickLower, tickUpper };\n }\n\n private tickSpacing(fee: number): number {\n const spacing = (TICK_SPACING_BY_FEE as Record<number, number>)[fee];\n if (spacing === undefined) {\n throw new GSwapSDKError('INVALID_FEE', {\n field: 'fee',\n value: fee,\n allowed: ALLOWED_FEE_TIERS_TEXT,\n });\n }\n return spacing;\n }\n\n private rejectNumber(value: unknown, parameterName: string): void {\n if (typeof value === 'number') {\n throw new GSwapSDKError('AMOUNT_IS_JS_NUMBER', { field: parameterName });\n }\n }\n\n /** The caller's `uniqueKey` (validated), else a fresh one. */\n private uniqueKeyFor(supplied: string | undefined): string {\n if (supplied === undefined) return createUniqueKey(this.uniqueKeyPrefix);\n assertUniqueKey(supplied);\n return supplied;\n }\n\n /**\n * The pair in canonical order. A resolver failure that is not an SDK error (an app's own\n * resolver, an app requestor's rejection during the registry read) is `TOKEN_RESOLUTION_FAILED`.\n */\n private orderPair(a: TokenRef, b: TokenRef) {\n return resolvePreSend(`${rawTokenOf(a)}/${rawTokenOf(b)}`, () => this.symbols.orderPair(a, b));\n }\n\n private async resolveCreateToken(\n ref: TokenRef,\n symbolOverride?: string,\n ): Promise<ResolvedCreateToken> {\n try {\n const lookupRef = typeof ref === 'object' ? compositeKeyOf(ref) : ref;\n const resolved = await resolvePreSend(rawTokenOf(ref), () => this.symbols.resolve(lookupRef));\n if (\n symbolOverride !== undefined &&\n symbolOverride !== '' &&\n symbolOverride !== resolved.symbol\n ) {\n throw new GSwapSDKError('SYMBOL_DISAGREES_WITH_REGISTRY', {\n token: rawTokenOf(ref),\n suppliedSymbol: symbolOverride,\n registeredSymbol: resolved.symbol,\n });\n }\n const suppliedClassKey = this.tryParseClassKey(ref);\n if (suppliedClassKey === undefined && resolved.collection === '') {\n // An offline resolver knows a bare symbol but not its class key; a pool needs the key.\n throw new GSwapSDKError('CREATE_POOL_NEEDS_CLASS_KEY', { token: rawTokenOf(ref) });\n }\n return {\n symbol: resolved.symbol,\n classKey:\n suppliedClassKey ??\n parseTokenClassKey({\n collection: resolved.collection,\n category: resolved.category,\n type: resolved.type,\n additionalKey: resolved.additionalKey,\n }),\n };\n } catch (error: unknown) {\n if (!(error instanceof GSwapSDKError) || error.code !== 'UNKNOWN_TOKEN') {\n throw error;\n }\n let classKey: ReturnType<typeof parseTokenClassKey>;\n try {\n classKey = parseTokenClassKey(ref);\n } catch {\n throw error;\n }\n // First-time registration: a class key carries NO symbol (collection and symbol disagree\n // for many tokens, launchpad tokens all share collection `Token`, and some collections\n // contain `$`), so the symbol to propose must be supplied by the caller from the token\n // class / registry. Never derive it from the key.\n if (symbolOverride === undefined || symbolOverride === '') {\n throw new GSwapSDKError('TOKEN_SYMBOL_NOT_REGISTERED', { token: rawTokenOf(ref) });\n }\n if (!isProposableTradingSymbol(symbolOverride)) {\n throw new GSwapSDKError('INVALID_TRADING_SYMBOL', { symbol: symbolOverride });\n }\n return { symbol: symbolOverride, classKey };\n }\n }\n\n private tryParseClassKey(ref: TokenRef): ReturnType<typeof parseTokenClassKey> | undefined {\n try {\n return parseTokenClassKey(ref);\n } catch {\n return undefined;\n }\n }\n}\n\nconst REQUIRED_POSITION_STRINGS = [\n 'token0Symbol',\n 'token1Symbol',\n 'owner',\n 'liquidity',\n 'amount0',\n 'amount1',\n] as const;\nconst REQUIRED_POSITION_NUMBERS = ['fee', 'tickLower', 'tickUpper'] as const;\nconst OPTIONAL_POSITION_STRINGS = [\n 'token0CompositeKey',\n 'token1CompositeKey',\n 'token0Image',\n 'token1Image',\n 'sqrtPrice',\n 'fees0',\n 'fees1',\n] as const;\n\n/**\n * Reads one position row of `/positions` or `/position`: the pair, owner, range, liquidity and\n * amounts must be usable; the composite keys, token images, current tick and price, and the\n * escrowed fees are carried when present. `pool` is the pool reference (`poolRef` wins over\n * `pool`; `''` when the row names neither).\n */\nfunction parsePosition(value: unknown): EntryParse<Position> {\n const wire = asEntryRecord(value);\n if (wire === undefined) return entryRejected('the row is not an object');\n for (const key of REQUIRED_POSITION_STRINGS) {\n if (typeof wire[key] !== 'string') return entryRejected(`${key} is not a string`);\n }\n for (const key of REQUIRED_POSITION_NUMBERS) {\n const number = wire[key];\n if (typeof number !== 'number' || !Number.isFinite(number)) {\n return entryRejected(`${key} is not a number`);\n }\n }\n if (typeof wire['inRange'] !== 'boolean') return entryRejected('inRange is not a boolean');\n for (const key of OPTIONAL_POSITION_STRINGS) {\n if (wire[key] !== undefined && typeof wire[key] !== 'string') {\n return entryRejected(`${key} is not a string`);\n }\n }\n const currentTick = wire['currentTick'];\n if (\n currentTick !== undefined &&\n (typeof currentTick !== 'number' || !Number.isFinite(currentTick))\n ) {\n return entryRejected('currentTick is not a number');\n }\n const pool = wire['poolRef'] ?? wire['pool'] ?? '';\n if (typeof pool !== 'string') return entryRejected('pool is not a string');\n const optional = Object.fromEntries(\n [...OPTIONAL_POSITION_STRINGS, 'currentTick' as const].flatMap((key) =>\n wire[key] === undefined ? [] : [[key, wire[key]]],\n ),\n );\n return entryOk({\n token0Symbol: wire['token0Symbol'] as string,\n token1Symbol: wire['token1Symbol'] as string,\n fee: wire['fee'] as number,\n tickLower: wire['tickLower'] as number,\n tickUpper: wire['tickUpper'] as number,\n liquidity: wire['liquidity'] as string,\n amount0: wire['amount0'] as string,\n amount1: wire['amount1'] as string,\n inRange: wire['inRange'],\n pool,\n owner: wire['owner'] as string,\n ...optional,\n });\n}\n\n/** The position inside a `/position` answer, or `INVALID_CHAIN_RESPONSE` when it is not one. */\nfunction readPositionBody(data: unknown): Position {\n const parsed = parsePosition(data);\n if (parsed.ok) return parsed.value;\n throw new GSwapSDKError('INVALID_POSITION_RESPONSE', { reason: parsed.reason }, { body: data });\n}\n"],"mappings":"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"}
|