@gala-chain/gswap-sdk 1.2.0-rc.9 → 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.
@@ -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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"}
@@ -1,2 +1,2 @@
1
- import{GSwapSDKError as e,getStringProperty as t}from"./gswap_sdk_error.js";const n=[`SHADOW_UNAVAILABLE`,`SHADOW_DISABLED`,`SHADOW_UNSUPPORTED_TYPE`,`SHADOW_RELAY_REJECTED`],r=[`unavailable`,`disabled`,`rejected`,`unknown`];function i(e){return e===503?`unavailable`:e===400?`disabled`:`unknown`}function a(e,t){return t===`SHADOW_UNAVAILABLE`?`unavailable`:t===`SHADOW_RELAY_REJECTED`?`rejected`:t===`SHADOW_DISABLED`||t===`SHADOW_UNSUPPORTED_TYPE`?`disabled`:i(e)}function o(n,r,i){let{httpStatus:o,body:c}=i,l=s(c);if(o>=200&&o<300&&l?.error===!1){let t=s(l.data),i=typeof t?.transactionId==`string`?t.transactionId:void 0;if(n===`RequestTrade`){let n=r.uniqueKey;if(t?.mode!==`queued`||typeof n!=`string`||t.uniqueKey!==n)throw new e(`SHADOW_NOT_QUEUED`,{},{status:o,body:c,shadow:!0,...typeof n==`string`?{uniqueKey:n}:{}});return{ok:!0,bundleId:i??``,queued:{uniqueKey:n,requestTransactionId:i??null}}}if(t?.mode===`sync`)return{ok:!0,bundleId:i??``}}let u=a(o,t(c,`code`));return u===`rejected`?{ok:!1,kind:`rejected`,retryable:!1,message:t(c,`message`)??``}:u===`unavailable`?{ok:!1,kind:u,retryable:!0}:{ok:!1,kind:u,retryable:!1}}function s(e){return typeof e==`object`&&e?e:void 0}export{r as SHADOW_FAILURE_KINDS,n as SHADOW_SUBMIT_CODES,a as classifyShadowSubmitError,i as classifyShadowSubmitStatus,o as readShadowSubmitOutcome};
1
+ import{GSwapSDKError as e,getStringProperty as t}from"./gswap_sdk_error.js";const n=[`SHADOW_UNAVAILABLE`,`SHADOW_UNSUPPORTED_TYPE`,`SHADOW_RELAY_REJECTED`],r=[`unavailable`,`disabled`,`rejected`,`unknown`];function i(e){return e===503?`unavailable`:e===400?`disabled`:`unknown`}function a(e,t){return t===`SHADOW_UNAVAILABLE`?`unavailable`:t===`SHADOW_RELAY_REJECTED`?`rejected`:t===`SHADOW_UNSUPPORTED_TYPE`?`disabled`:i(e)}function o(n,r,i){let{httpStatus:o,body:c}=i,l=s(c);if(o>=200&&o<300&&l?.error===!1){let t=s(l.data),i=typeof t?.transactionId==`string`?t.transactionId:void 0;if(n===`RequestTrade`){let n=r.uniqueKey;if(t?.mode!==`queued`||typeof n!=`string`||t.uniqueKey!==n)throw new e(`SHADOW_NOT_QUEUED`,{},{status:o,body:c,shadow:!0,...typeof n==`string`?{uniqueKey:n}:{}});return{ok:!0,bundleId:i??``,queued:{uniqueKey:n,requestTransactionId:i??null}}}if(t?.mode===`sync`)return{ok:!0,bundleId:i??``}}let u=a(o,t(c,`code`));return u===`rejected`?{ok:!1,kind:`rejected`,retryable:!1,message:t(c,`message`)??``}:u===`unavailable`?{ok:!1,kind:u,retryable:!0}:{ok:!1,kind:u,retryable:!1}}function s(e){return typeof e==`object`&&e?e:void 0}export{r as SHADOW_FAILURE_KINDS,n as SHADOW_SUBMIT_CODES,a as classifyShadowSubmitError,i as classifyShadowSubmitStatus,o as readShadowSubmitOutcome};
2
2
  //# sourceMappingURL=shadow_submit.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"shadow_submit.js","names":[],"sources":["../../../src/classes/shadow_submit.ts"],"sourcesContent":["import { getStringProperty, GSwapSDKError } from './gswap_sdk_error.js';\n\n/** The raw answer to a shadow (dark) write: every HTTP status resolves. */\nexport interface ShadowSubmitResult {\n httpStatus: number;\n /** Parsed JSON body, or the raw text when the body was not JSON. */\n body: unknown;\n}\n\n/**\n * Machine-readable codes defi-backend returns in a shadow failure body (`{ code, message }`, with\n * no `statusCode`; the HTTP status carries the number).\n *\n * - `SHADOW_UNAVAILABLE` (503): marking failed or infra not configured; retryable. Never fall back\n * silently to a public swap.\n * - `SHADOW_DISABLED` (400): the feature is off for this environment.\n * - `SHADOW_UNSUPPORTED_TYPE` (400): shadow requested on a non-swap method.\n * - `SHADOW_RELAY_REJECTED`: terminal business rejection (slippage, balance, ...), not retryable;\n * `message` is the gateway's verbatim string.\n */\nexport const SHADOW_SUBMIT_CODES = [\n 'SHADOW_UNAVAILABLE',\n 'SHADOW_DISABLED',\n 'SHADOW_UNSUPPORTED_TYPE',\n 'SHADOW_RELAY_REJECTED',\n] as const;\n\n/** One member of {@link SHADOW_SUBMIT_CODES}. */\nexport type ShadowSubmitCode = (typeof SHADOW_SUBMIT_CODES)[number];\n\n/**\n * Every `kind` of a failed {@link ShadowSubmitOutcome}, for an app that maps each to its own text\n * (`Record<ShadowFailureKind, string>`): `unavailable` (retryable), `disabled`, `rejected` (carries\n * the gateway's `message`) and `unknown`.\n */\nexport const SHADOW_FAILURE_KINDS = ['unavailable', 'disabled', 'rejected', 'unknown'] as const;\n\n/** One member of {@link SHADOW_FAILURE_KINDS}. */\nexport type ShadowFailureKind = (typeof SHADOW_FAILURE_KINDS)[number];\n\n/** A RequestTrade the gateway queued: the handle to confirm it with `confirmAppliedTrade`. */\nexport interface ShadowQueuedRequest {\n uniqueKey: string;\n requestTransactionId: string | null;\n}\n\n/** A shadow write the gateway refused or did not answer: `kind` says why (see {@link SHADOW_FAILURE_KINDS}). */\nexport type ShadowSubmitFailure =\n | { ok: false; kind: 'unavailable'; retryable: true }\n | { ok: false; kind: 'disabled'; retryable: false }\n | { ok: false; kind: 'rejected'; retryable: false; message: string }\n | { ok: false; kind: 'unknown'; retryable: false };\n\n/**\n * The normalized outcome of a shadow RequestTrade (a dark swap). A success (`ok: true`) ALWAYS\n * carries `queued`, the exact key the gateway acknowledged: a RequestTrade answer that does not\n * queue the signed key is not an outcome but a thrown `SHADOW_NOT_QUEUED`. So after `if (outcome.ok)`\n * the app reads `outcome.queued` with no `undefined` branch.\n */\nexport type ShadowSubmitOutcome =\n | {\n ok: true;\n /** The request transaction id (`''` when the gateway gave none). */\n bundleId: string;\n /** Confirm with `confirmAppliedTrade`. */\n queued: ShadowQueuedRequest;\n }\n | ShadowSubmitFailure;\n\n/**\n * The success of a shadow write that is NOT a RequestTrade and executed synchronously\n * (`mode: 'sync'`, a liquidity write): there is nothing queued to confirm, so `queued` is absent.\n */\nexport interface ShadowSyncSuccess {\n ok: true;\n /** The transaction id (`''` when the gateway gave none). */\n bundleId: string;\n queued?: undefined;\n}\n\n/** Maps an HTTP status to a failure kind: 503 retryable, 400 disabled, anything else unknown. */\nexport function classifyShadowSubmitStatus(status: number): ShadowFailureKind {\n if (status === 503) return 'unavailable';\n if (status === 400) return 'disabled';\n return 'unknown';\n}\n\n/**\n * Prefers the body `code` (stable across status remaps) and falls back to the HTTP status.\n * `SHADOW_UNAVAILABLE` is the only retryable case; `SHADOW_RELAY_REJECTED` is a terminal gateway\n * rejection; the two other 400 codes and any unknown 400 map to \"disabled\".\n */\nexport function classifyShadowSubmitError(status: number, code?: string): ShadowFailureKind {\n if (code === 'SHADOW_UNAVAILABLE') return 'unavailable';\n if (code === 'SHADOW_RELAY_REJECTED') return 'rejected';\n if (code === 'SHADOW_DISABLED' || code === 'SHADOW_UNSUPPORTED_TYPE') return 'disabled';\n return classifyShadowSubmitStatus(status);\n}\n\n/**\n * Normalizes a shadow write's raw answer. For `'RequestTrade'` the result is a\n * {@link ShadowSubmitOutcome}, whose success always has `queued`; any other method may also be a\n * {@link ShadowSyncSuccess} (no `queued`).\n *\n * A 2xx envelope with `error === false` is a success when it is a queued RequestTrade that\n * acknowledges the EXACT signed `uniqueKey`, or a synchronous (`mode: 'sync'`) liquidity write.\n * Everything else is classified as a failure from the status and the body's `code`.\n *\n * @throws GSwapSDKError SHADOW_NOT_QUEUED when a RequestTrade success does not queue the\n * signed `uniqueKey` (a synchronous or mismatched answer must never be accepted as a swap).\n */\nexport function readShadowSubmitOutcome(\n method: 'RequestTrade',\n signedBody: Record<string, unknown>,\n result: ShadowSubmitResult,\n): ShadowSubmitOutcome;\nexport function readShadowSubmitOutcome(\n method: string,\n signedBody: Record<string, unknown>,\n result: ShadowSubmitResult,\n): ShadowSubmitOutcome | ShadowSyncSuccess;\nexport function readShadowSubmitOutcome(\n method: string,\n signedBody: Record<string, unknown>,\n result: ShadowSubmitResult,\n): ShadowSubmitOutcome | ShadowSyncSuccess {\n const { httpStatus, body } = result;\n const envelope = asRecord(body);\n if (httpStatus >= 200 && httpStatus < 300 && envelope?.['error'] === false) {\n const data = asRecord(envelope['data']);\n const rawTransactionId =\n typeof data?.['transactionId'] === 'string' ? data['transactionId'] : undefined;\n if (method === 'RequestTrade') {\n const requestedKey = signedBody['uniqueKey'];\n if (\n data?.['mode'] !== 'queued' ||\n typeof requestedKey !== 'string' ||\n data['uniqueKey'] !== requestedKey\n ) {\n throw new GSwapSDKError(\n 'SHADOW_NOT_QUEUED',\n {},\n {\n status: httpStatus,\n body,\n shadow: true,\n ...(typeof requestedKey === 'string' ? { uniqueKey: requestedKey } : {}),\n },\n );\n }\n return {\n ok: true,\n bundleId: rawTransactionId ?? '',\n queued: { uniqueKey: requestedKey, requestTransactionId: rawTransactionId ?? null },\n };\n }\n if (data?.['mode'] === 'sync') return { ok: true, bundleId: rawTransactionId ?? '' };\n }\n\n const kind = classifyShadowSubmitError(httpStatus, getStringProperty(body, 'code'));\n if (kind === 'rejected') {\n return {\n ok: false,\n kind: 'rejected',\n retryable: false,\n message: getStringProperty(body, 'message') ?? '',\n };\n }\n return kind === 'unavailable'\n ? { ok: false, kind, retryable: true }\n : { ok: false, kind, retryable: false };\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n return typeof value === 'object' && value !== null\n ? (value as Record<string, unknown>)\n : undefined;\n}\n"],"mappings":"4EAoBA,MAAa,EAAsB,CACjC,qBACA,kBACA,0BACA,uBACF,EAUa,EAAuB,CAAC,cAAe,WAAY,WAAY,SAAS,EA8CrF,SAAgB,EAA2B,EAAmC,CAG5E,OAFI,IAAW,IAAY,cACvB,IAAW,IAAY,WACpB,SACT,CAOA,SAAgB,EAA0B,EAAgB,EAAkC,CAI1F,OAHI,IAAS,qBAA6B,cACtC,IAAS,wBAAgC,WACzC,IAAS,mBAAqB,IAAS,0BAAkC,WACtE,EAA2B,CAAM,CAC1C,CAwBA,SAAgB,EACd,EACA,EACA,EACyC,CACzC,GAAM,CAAE,aAAY,QAAS,EACvB,EAAW,EAAS,CAAI,EAC9B,GAAI,GAAc,KAAO,EAAa,KAAO,GAAW,QAAa,GAAO,CAC1E,IAAM,EAAO,EAAS,EAAS,IAAO,EAChC,EACJ,OAAO,GAAO,eAAqB,SAAW,EAAK,cAAmB,IAAA,GACxE,GAAI,IAAW,eAAgB,CAC7B,IAAM,EAAe,EAAW,UAChC,GACE,GAAO,OAAY,UACnB,OAAO,GAAiB,UACxB,EAAK,YAAiB,EAEtB,MAAM,IAAI,EACR,oBACA,CAAC,EACD,CACE,OAAQ,EACR,OACA,OAAQ,GACR,GAAI,OAAO,GAAiB,SAAW,CAAE,UAAW,CAAa,EAAI,CAAC,CACxE,CACF,EAEF,MAAO,CACL,GAAI,GACJ,SAAU,GAAoB,GAC9B,OAAQ,CAAE,UAAW,EAAc,qBAAsB,GAAoB,IAAK,CACpF,CACF,CACA,GAAI,GAAO,OAAY,OAAQ,MAAO,CAAE,GAAI,GAAM,SAAU,GAAoB,EAAG,CACrF,CAEA,IAAM,EAAO,EAA0B,EAAY,EAAkB,EAAM,MAAM,CAAC,EASlF,OARI,IAAS,WACJ,CACL,GAAI,GACJ,KAAM,WACN,UAAW,GACX,QAAS,EAAkB,EAAM,SAAS,GAAK,EACjD,EAEK,IAAS,cACZ,CAAE,GAAI,GAAO,OAAM,UAAW,EAAK,EACnC,CAAE,GAAI,GAAO,OAAM,UAAW,EAAM,CAC1C,CAEA,SAAS,EAAS,EAAqD,CACrE,OAAO,OAAO,GAAU,UAAY,EAC/B,EACD,IAAA,EACN"}
1
+ {"version":3,"file":"shadow_submit.js","names":[],"sources":["../../../src/classes/shadow_submit.ts"],"sourcesContent":["import { getStringProperty, GSwapSDKError } from './gswap_sdk_error.js';\n\n/** The raw answer to a shadow (dark) write: every HTTP status resolves. */\nexport interface ShadowSubmitResult {\n httpStatus: number;\n /** Parsed JSON body, or the raw text when the body was not JSON. */\n body: unknown;\n}\n\n/**\n * Machine-readable codes defi-backend returns in a shadow failure body (`{ code, message }`, with\n * no `statusCode`; the HTTP status carries the number).\n *\n * - `SHADOW_UNAVAILABLE` (503): marking failed or infra not configured; retryable. Never fall back\n * silently to a public swap.\n * - `SHADOW_UNSUPPORTED_TYPE` (400): shadow requested on a non-swap method.\n * - `SHADOW_RELAY_REJECTED`: terminal business rejection (slippage, balance, ...), not retryable;\n * `message` is the gateway's verbatim string.\n */\nexport const SHADOW_SUBMIT_CODES = [\n 'SHADOW_UNAVAILABLE',\n 'SHADOW_UNSUPPORTED_TYPE',\n 'SHADOW_RELAY_REJECTED',\n] as const;\n\n/** One member of {@link SHADOW_SUBMIT_CODES}. */\nexport type ShadowSubmitCode = (typeof SHADOW_SUBMIT_CODES)[number];\n\n/**\n * Every `kind` of a failed {@link ShadowSubmitOutcome}, for an app that maps each to its own text\n * (`Record<ShadowFailureKind, string>`): `unavailable` (retryable), `disabled` (the shadow route\n * answered 400: the request is not a dark-swappable one, e.g. `SHADOW_UNSUPPORTED_TYPE`; Shadow\n * Swaps themselves are enabled in every environment), `rejected` (carries the gateway's `message`)\n * and `unknown`.\n */\nexport const SHADOW_FAILURE_KINDS = ['unavailable', 'disabled', 'rejected', 'unknown'] as const;\n\n/** One member of {@link SHADOW_FAILURE_KINDS}. */\nexport type ShadowFailureKind = (typeof SHADOW_FAILURE_KINDS)[number];\n\n/** A RequestTrade the gateway queued: the handle to confirm it with `confirmAppliedTrade`. */\nexport interface ShadowQueuedRequest {\n uniqueKey: string;\n requestTransactionId: string | null;\n}\n\n/** A shadow write the gateway refused or did not answer: `kind` says why (see {@link SHADOW_FAILURE_KINDS}). */\nexport type ShadowSubmitFailure =\n | { ok: false; kind: 'unavailable'; retryable: true }\n | { ok: false; kind: 'disabled'; retryable: false }\n | { ok: false; kind: 'rejected'; retryable: false; message: string }\n | { ok: false; kind: 'unknown'; retryable: false };\n\n/**\n * The normalized outcome of a shadow RequestTrade (a dark swap). A success (`ok: true`) ALWAYS\n * carries `queued`, the exact key the gateway acknowledged: a RequestTrade answer that does not\n * queue the signed key is not an outcome but a thrown `SHADOW_NOT_QUEUED`. So after `if (outcome.ok)`\n * the app reads `outcome.queued` with no `undefined` branch.\n */\nexport type ShadowSubmitOutcome =\n | {\n ok: true;\n /** The request transaction id (`''` when the gateway gave none). */\n bundleId: string;\n /** Confirm with `confirmAppliedTrade`. */\n queued: ShadowQueuedRequest;\n }\n | ShadowSubmitFailure;\n\n/**\n * The success of a shadow write that is NOT a RequestTrade and executed synchronously\n * (`mode: 'sync'`, a liquidity write): there is nothing queued to confirm, so `queued` is absent.\n */\nexport interface ShadowSyncSuccess {\n ok: true;\n /** The transaction id (`''` when the gateway gave none). */\n bundleId: string;\n queued?: undefined;\n}\n\n/** Maps an HTTP status to a failure kind: 503 retryable, 400 disabled, anything else unknown. */\nexport function classifyShadowSubmitStatus(status: number): ShadowFailureKind {\n if (status === 503) return 'unavailable';\n if (status === 400) return 'disabled';\n return 'unknown';\n}\n\n/**\n * Prefers the body `code` (stable across status remaps) and falls back to the HTTP status.\n * `SHADOW_UNAVAILABLE` is the only retryable case; `SHADOW_RELAY_REJECTED` is a terminal gateway\n * rejection; `SHADOW_UNSUPPORTED_TYPE` and any other 400 map to \"disabled\".\n */\nexport function classifyShadowSubmitError(status: number, code?: string): ShadowFailureKind {\n if (code === 'SHADOW_UNAVAILABLE') return 'unavailable';\n if (code === 'SHADOW_RELAY_REJECTED') return 'rejected';\n if (code === 'SHADOW_UNSUPPORTED_TYPE') return 'disabled';\n return classifyShadowSubmitStatus(status);\n}\n\n/**\n * Normalizes a shadow write's raw answer. For `'RequestTrade'` the result is a\n * {@link ShadowSubmitOutcome}, whose success always has `queued`; any other method may also be a\n * {@link ShadowSyncSuccess} (no `queued`).\n *\n * A 2xx envelope with `error === false` is a success when it is a queued RequestTrade that\n * acknowledges the EXACT signed `uniqueKey`, or a synchronous (`mode: 'sync'`) liquidity write.\n * Everything else is classified as a failure from the status and the body's `code`.\n *\n * @throws GSwapSDKError SHADOW_NOT_QUEUED when a RequestTrade success does not queue the\n * signed `uniqueKey` (a synchronous or mismatched answer must never be accepted as a swap).\n */\nexport function readShadowSubmitOutcome(\n method: 'RequestTrade',\n signedBody: Record<string, unknown>,\n result: ShadowSubmitResult,\n): ShadowSubmitOutcome;\nexport function readShadowSubmitOutcome(\n method: string,\n signedBody: Record<string, unknown>,\n result: ShadowSubmitResult,\n): ShadowSubmitOutcome | ShadowSyncSuccess;\nexport function readShadowSubmitOutcome(\n method: string,\n signedBody: Record<string, unknown>,\n result: ShadowSubmitResult,\n): ShadowSubmitOutcome | ShadowSyncSuccess {\n const { httpStatus, body } = result;\n const envelope = asRecord(body);\n if (httpStatus >= 200 && httpStatus < 300 && envelope?.['error'] === false) {\n const data = asRecord(envelope['data']);\n const rawTransactionId =\n typeof data?.['transactionId'] === 'string' ? data['transactionId'] : undefined;\n if (method === 'RequestTrade') {\n const requestedKey = signedBody['uniqueKey'];\n if (\n data?.['mode'] !== 'queued' ||\n typeof requestedKey !== 'string' ||\n data['uniqueKey'] !== requestedKey\n ) {\n throw new GSwapSDKError(\n 'SHADOW_NOT_QUEUED',\n {},\n {\n status: httpStatus,\n body,\n shadow: true,\n ...(typeof requestedKey === 'string' ? { uniqueKey: requestedKey } : {}),\n },\n );\n }\n return {\n ok: true,\n bundleId: rawTransactionId ?? '',\n queued: { uniqueKey: requestedKey, requestTransactionId: rawTransactionId ?? null },\n };\n }\n if (data?.['mode'] === 'sync') return { ok: true, bundleId: rawTransactionId ?? '' };\n }\n\n const kind = classifyShadowSubmitError(httpStatus, getStringProperty(body, 'code'));\n if (kind === 'rejected') {\n return {\n ok: false,\n kind: 'rejected',\n retryable: false,\n message: getStringProperty(body, 'message') ?? '',\n };\n }\n return kind === 'unavailable'\n ? { ok: false, kind, retryable: true }\n : { ok: false, kind, retryable: false };\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n return typeof value === 'object' && value !== null\n ? (value as Record<string, unknown>)\n : undefined;\n}\n"],"mappings":"4EAmBA,MAAa,EAAsB,CACjC,qBACA,0BACA,uBACF,EAYa,EAAuB,CAAC,cAAe,WAAY,WAAY,SAAS,EA8CrF,SAAgB,EAA2B,EAAmC,CAG5E,OAFI,IAAW,IAAY,cACvB,IAAW,IAAY,WACpB,SACT,CAOA,SAAgB,EAA0B,EAAgB,EAAkC,CAI1F,OAHI,IAAS,qBAA6B,cACtC,IAAS,wBAAgC,WACzC,IAAS,0BAAkC,WACxC,EAA2B,CAAM,CAC1C,CAwBA,SAAgB,EACd,EACA,EACA,EACyC,CACzC,GAAM,CAAE,aAAY,QAAS,EACvB,EAAW,EAAS,CAAI,EAC9B,GAAI,GAAc,KAAO,EAAa,KAAO,GAAW,QAAa,GAAO,CAC1E,IAAM,EAAO,EAAS,EAAS,IAAO,EAChC,EACJ,OAAO,GAAO,eAAqB,SAAW,EAAK,cAAmB,IAAA,GACxE,GAAI,IAAW,eAAgB,CAC7B,IAAM,EAAe,EAAW,UAChC,GACE,GAAO,OAAY,UACnB,OAAO,GAAiB,UACxB,EAAK,YAAiB,EAEtB,MAAM,IAAI,EACR,oBACA,CAAC,EACD,CACE,OAAQ,EACR,OACA,OAAQ,GACR,GAAI,OAAO,GAAiB,SAAW,CAAE,UAAW,CAAa,EAAI,CAAC,CACxE,CACF,EAEF,MAAO,CACL,GAAI,GACJ,SAAU,GAAoB,GAC9B,OAAQ,CAAE,UAAW,EAAc,qBAAsB,GAAoB,IAAK,CACpF,CACF,CACA,GAAI,GAAO,OAAY,OAAQ,MAAO,CAAE,GAAI,GAAM,SAAU,GAAoB,EAAG,CACrF,CAEA,IAAM,EAAO,EAA0B,EAAY,EAAkB,EAAM,MAAM,CAAC,EASlF,OARI,IAAS,WACJ,CACL,GAAI,GACJ,KAAM,WACN,UAAW,GACX,QAAS,EAAkB,EAAM,SAAS,GAAK,EACjD,EAEK,IAAS,cACZ,CAAE,GAAI,GAAO,OAAM,UAAW,EAAK,EACnC,CAAE,GAAI,GAAO,OAAM,UAAW,EAAM,CAC1C,CAEA,SAAS,EAAS,EAAqD,CACrE,OAAO,OAAO,GAAU,UAAY,EAC/B,EACD,IAAA,EACN"}
@@ -1 +1 @@
1
- {"version":3,"file":"signers.js","names":["globalObject"],"sources":["../../../src/classes/signers.ts"],"sourcesContent":["import { GSwapSDKError, isGSwapError } from './gswap_sdk_error.js';\nimport {\n buildEIP712Payload,\n EIP712_CAPABLE_METHODS,\n eip712DomainFor,\n type EIP712DomainValue,\n type EIP712Types,\n} from '../utils/eip712.js';\nimport {\n ethAddressOfPrivateKey,\n fixpointPrefixFor,\n normalizePrivateKey,\n personalSignPrefixFor,\n serialize,\n signNative,\n} from '../utils/signing.js';\n\n// Personal-sign prefix, computed the way THE CHAIN verifies it.\n//\n// GalaChain (SDK 3.x) recovers a personal-signed DTO from\n// keccak256(<prefix field> + <canonical JSON of the DTO WITHOUT the prefix field>)\n// and a browser wallet's `personal_sign` (EIP-191) hashes\n// \"\\u0019Ethereum Signed Message:\\n\" + <byte length of message> + <message>.\n// They agree exactly when the wallet signs the bare canonical DTO and `prefix` is the EIP-191\n// header for that message's UTF-8 BYTE length. Any other message still yields a valid-looking\n// signature — it just recovers to an address nobody holds. This SDK used to embed `prefix` in\n// the signed message (the SDK 1.10 client recipe): observed on stage 2026-09-23, such a\n// MetaMask-signed CreatePool executed on chain under a phantom creator, and defi-backend's\n// bouncer now rejects that shape outright.\n//\n// `@gala-chain/connect`'s prefix helper must NOT be used for wallet `personal_sign`: its\n// fixpoint counts the prefix itself and targets the SDK's raw-payload signer. The same header\n// is also what Gala Wallet's `gala_signChainDto` (personal-sign scheme) prepends before hashing,\n// so one value serves both paths.\n//\n// The header itself is computed by `personalSignPrefixFor` in ../utils/signing.ts.\n\n/** The signing schemes supported by the Gala Wallet signer. */\nexport type GalaWalletScheme = 'native' | 'personal-sign';\n\n/** The request surface used by an EIP-1193 browser wallet. */\nexport interface BrowserWalletProvider {\n request(args: { method: string; params?: unknown[] }): Promise<unknown>;\n}\n\ninterface GalaWalletProvider {\n request(args: { method: string; params?: unknown[] }): Promise<unknown>;\n /** Gala Wallet's \"act as this account\" switch; absent on other providers. */\n setAddress?: (address: string) => Promise<void>;\n}\n\ninterface WindowWithGala {\n gala?: GalaWalletProvider;\n}\n\ninterface GlobalWithWindow {\n window?: WindowWithGala;\n}\n\n/** A DTO together with the signature that belongs in the gateway request body. */\nexport type SignedDto<T extends Record<string, unknown>> = T & { signature: string };\n\n/**\n * A signer for complete GalaChain write bodies.\n *\n * Implementations return the DTO fields together with the signature that belongs in the\n * gateway request body. Signer identity is recovered from that signature; implementations do\n * not add signer hints, typed-data metadata, or a domain.\n */\nexport interface GalaChainSigner {\n /**\n * Signs a DTO for a named GalaChain contract method.\n *\n * @example\n * ```typescript\n * const signed = await signer.signObject('RequestTrade', { token0: 'GALA', token1: 'GUSDC' });\n * ```\n */\n signObject<T extends Record<string, unknown>>(methodName: string, dto: T): Promise<SignedDto<T>>;\n}\n\nfunction getWalletProvider(): GalaWalletProvider {\n const globalObject = globalThis as GlobalWithWindow;\n const wallet = globalObject.window?.gala;\n if (!wallet) {\n throw new GSwapSDKError('GALA_WALLET_NOT_AVAILABLE', {});\n }\n return wallet;\n}\n\nfunction getNativeSignature(value: unknown): string {\n if (typeof value !== 'string') {\n throw new GSwapSDKError('INVALID_SIGNATURE', { scheme: 'native' });\n }\n // 130 hex digits (r || s || v), bare as Gala Wallet returns it or `0x`-prefixed; passed on\n // verbatim, because the backend recovers the signer from either spelling.\n if (!/^(?:0x)?[0-9a-fA-F]{130}$/u.test(value)) {\n throw new GSwapSDKError('INVALID_SIGNATURE', { scheme: 'native' });\n }\n return value;\n}\n\nfunction getPersonalSignature(value: unknown): string {\n if (typeof value !== 'string' || !/^(?:0x)?[0-9a-fA-F]{130}$/u.test(value)) {\n throw new GSwapSDKError('INVALID_SIGNATURE', { scheme: 'personal-sign' });\n }\n return value;\n}\n\n/**\n * The `0x`-prefixed Ethereum address a Gala account is signed by, or `undefined` when the account\n * names no key (a `client|` alias or a bare GalaChain id is not derivable from a key). Accepts\n * `0x` plus 40 hex digits and `eth|` plus 40 hex digits, with or without `0x`; the caller's casing\n * is kept. For {@link LazyPrivateKeySignerOptions.expectedAddress}, which should only be set for\n * an account whose key can be checked.\n *\n * @example\n * ```ts\n * createLazyPrivateKeySigner(getKey, { expectedAddress: ethereumAddressOfAccount(account) });\n * ```\n */\nexport function ethereumAddressOfAccount(account: string): string | undefined {\n const match = /^(?:eth\\|(?:0x)?|0x)([0-9a-fA-F]{40})$/u.exec(account);\n return match?.[1] === undefined ? undefined : `0x${match[1]}`;\n}\n\nfunction normalizeEthereumAccount(address: string, allowGalaPrefix: boolean): string {\n const normalized = allowGalaPrefix ? (ethereumAddressOfAccount(address) ?? address) : address;\n if (!/^0x[0-9a-fA-F]{40}$/u.test(normalized)) {\n throw new GSwapSDKError('INVALID_WALLET_ADDRESS', { field: 'walletAddress', value: address });\n }\n // The caller's casing is kept on purpose: frontend_v2 and the wallet hand the wallet the\n // checksummed account, and EIP-1193 / Gala Wallet accept any case, so passing the value through\n // unchanged is the only choice that cannot differ from what they send.\n return normalized;\n}\n\n/**\n * The account handed to Gala Wallet as the signer address. An Ethereum account is held to the\n * same strict check as ever (`0x` plus 40 hex digits; `eth|` plus 40 hex digits, with or without\n * `0x`, is normalized to it). A GalaChain id the wallet can also be asked to sign for is passed\n * through verbatim: `client|` plus 24 hex digits, or the bare 24 hex digits that frontend_v2 keeps\n * in `state.user.wallet` after a direct `client|` login. Nothing else is accepted.\n */\nfunction normalizeGalaWalletAccount(address: string): string {\n const eth = /^eth\\|(?:0x)?([0-9a-fA-F]{40})$/u.exec(address);\n if (eth?.[1] !== undefined) return `0x${eth[1]}`;\n if (/^0x[0-9a-fA-F]{40}$/u.test(address) || /^(?:client\\|)?[0-9a-fA-F]{24}$/u.test(address)) {\n return address;\n }\n throw new GSwapSDKError('INVALID_WALLET_ADDRESS', { field: 'walletAddress', value: address });\n}\n\n/** An identity Gala Wallet can act as: an account form above, any `eth|`/`client|` alias, verbatim. */\nfunction assertWalletIdentity(identity: string): void {\n if (\n !/^(?:eth\\|(?:0x)?[0-9a-fA-F]{40}|0x[0-9a-fA-F]{40}|(?:client\\|)?[0-9a-fA-F]{24})$/u.test(\n identity,\n )\n ) {\n throw new GSwapSDKError('INVALID_WALLET_ADDRESS', { field: 'selectAddress', value: identity });\n }\n}\n\n/** Provider-wide queue: `setAddress` + the signature request must not interleave across signers. */\nconst walletIdentityQueue = new WeakMap<object, Promise<unknown>>();\n\n/**\n * Runs `signWith` with `identity` selected on a Gala Wallet that supports `setAddress`. Gala\n * Wallet holds ONE current account, so two signatures for different identities in flight at once\n * would sign as whichever selection came last; the pair (select, sign) is therefore serialized\n * per provider. A failed selection fails the signature (nothing is requested).\n */\nfunction withSelectedIdentity<T>(\n wallet: GalaWalletProvider,\n identity: string | undefined,\n signWith: () => Promise<T>,\n): Promise<T> {\n const { setAddress } = wallet;\n if (identity === undefined || typeof setAddress !== 'function') return signWith();\n const previous = walletIdentityQueue.get(wallet) ?? Promise.resolve();\n const run = previous\n .catch(() => undefined)\n .then(async () => {\n await setAddress.call(wallet, identity);\n return signWith();\n });\n walletIdentityQueue.set(wallet, run);\n return run;\n}\n\nfunction personalSign<T extends Record<string, unknown>>(\n provider: GalaWalletProvider | BrowserWalletProvider,\n dto: T,\n walletAddress: string,\n methodName?: string,\n): Promise<T & { prefix: string; signature: string }> {\n const prefix = personalSignPrefixFor(dto);\n const payload = { ...dto, prefix };\n // A browser wallet's `personal_sign` applies the EIP-191 header ITSELF, so it must sign the\n // bare DTO; Gala Wallet's `gala_signChainDto` (personal-sign scheme) instead extracts the\n // `prefix` field from what it is handed and prepends it before hashing, so it is handed the\n // prefixed payload. Both submit `{ ...dto, prefix, signature }`.\n const params =\n methodName !== undefined\n ? [serialize(payload), walletAddress, methodName]\n : [serialize(dto), walletAddress];\n\n return provider\n .request({ method: methodName !== undefined ? 'gala_signChainDto' : 'personal_sign', params })\n .then((value) => ({ ...payload, signature: getPersonalSignature(value) }));\n}\n\n/**\n * Signs a DTO via genuine EIP-712 (`eth_signTypedData_v4`) — correctly resolves the `client|`\n * identity, unlike `personal_sign`, for the operations in `EIP712_CAPABLE_METHODS`.\n *\n * Constructs the raw JSON-RPC payload by hand (`{domain, types: {...types, EIP712Domain},\n * primaryType, message}`) rather than depending on ethers.js, matching exactly what\n * `ethers.TypedDataEncoder.getPayload`/`Wallet.signTypedData` produce internally — verified\n * byte-for-byte equivalent (same recovered address from the same signature against both\n * constructions) before this shipped, since gswap-sdk deliberately has no ethers dependency.\n */\nasync function signEIP712<T extends Record<string, unknown>>(\n provider: BrowserWalletProvider,\n methodName: string,\n dto: T,\n walletAddress: string,\n): Promise<T & { types: EIP712Types; domain: EIP712DomainValue; signature: string }> {\n const { completedDto, types } = buildEIP712Payload(methodName, dto);\n // The wallet's LIVE chain id, read on every signature: a user can switch networks after\n // connecting, and a cached or hardcoded id would sign a domain MetaMask itself rejects.\n const domain = eip712DomainFor(await provider.request({ method: 'eth_chainId', params: [] }));\n // The body `prefix` is not part of the EIP-712 `types`, so it is never hashed; it carries the\n // connect-style fixpoint value only to keep the submitted body byte-identical to frontend_v2's.\n const prefix = fixpointPrefixFor(completedDto);\n const message = { ...completedDto, prefix };\n const eip712Domain = [\n { name: 'name', type: 'string' },\n { name: 'chainId', type: 'uint256' },\n ];\n const payload = {\n domain,\n types: { ...types, EIP712Domain: eip712Domain },\n primaryType: methodName,\n message,\n };\n\n const value = await provider.request({\n method: 'eth_signTypedData_v4',\n params: [walletAddress, JSON.stringify(payload)],\n });\n // Key order matches frontend_v2's submitted body: ..., prefix, signature, types, domain.\n return {\n ...(message as T & { prefix: string }),\n signature: getPersonalSignature(value),\n types,\n domain,\n };\n}\n\n/**\n * A GalaChain native signer backed by a secp256k1 private key.\n *\n * Native signing produces the bare GalaChain signature format, without a personal-sign prefix\n * or EIP-712 metadata. The private-key identity is recovered by the chain.\n */\nexport class PrivateKeySigner implements GalaChainSigner {\n private readonly privateKey: Uint8Array;\n\n /**\n * Creates a native signer from a hex or base64 secp256k1 private key.\n *\n * @example\n * ```typescript\n * const signer = new PrivateKeySigner('1'.padStart(64, '0'));\n * const signed = await signer.signObject('RequestTrade', { token0: 'GALA', token1: 'GUSDC' });\n * ```\n */\n constructor(privateKey: string) {\n this.privateKey = normalizePrivateKey(privateKey);\n }\n\n /**\n * Signs the DTO with GalaChain native signing.\n *\n * @example\n * ```typescript\n * const signed = await signer.signObject('RequestTrade', {\n * token0: 'GALA',\n * token1: 'GUSDC',\n * fee: 3000,\n * });\n * ```\n */\n public signObject<T extends Record<string, unknown>>(\n _methodName: string,\n dto: T,\n ): Promise<SignedDto<T>> {\n const signature = signNative(this.privateKey, dto);\n return Promise.resolve({ ...dto, signature });\n }\n}\n\n/** Supplies a private key at sign time: a hex/base64 string, possibly fetched asynchronously. */\nexport type PrivateKeyProvider = () => Promise<string> | string;\n\n/** Options for {@link createLazyPrivateKeySigner}. */\nexport interface LazyPrivateKeySignerOptions {\n /**\n * When set, every signature first checks that the fetched key belongs to this account (`0x...`\n * or the Gala `eth|0x...` / `eth|<40 hex>` form, case-insensitive) and refuses to sign otherwise\n * (`SIGNER_ADDRESS_MISMATCH`). Use it when the key lives in a slot another surface can switch\n * between the moment a request was approved and the moment it is signed.\n */\n expectedAddress?: string | undefined;\n}\n\n/**\n * Calls the app's key provider. Whatever it throws or rejects with is the app's own failure to\n * produce a key before anything was signed or sent, so it becomes `KEY_PROVIDER_FAILED` (the\n * original is `cause`); an SDK error is already catalogued and passes through.\n */\nasync function keyFrom(getPrivateKey: PrivateKeyProvider): Promise<unknown> {\n try {\n return await getPrivateKey();\n } catch (cause: unknown) {\n throw isGSwapError(cause) ? cause : new GSwapSDKError('KEY_PROVIDER_FAILED', {}, { cause });\n }\n}\n\n/**\n * Creates the native-signing equivalent of {@link PrivateKeySigner} for apps that must not hold a\n * key in a long-lived object (an extension that reads its session key at sign time).\n *\n * `getPrivateKey` is called once per `signObject` call, never cached, never shared between calls\n * (concurrent signatures each fetch their own key), and the normalized key bytes are zeroed\n * before `signObject` settles, also on failure. A rejection or exception from `getPrivateKey` (the\n * app's \"wallet is locked\", a storage read that failed) is a `KEY_PROVIDER_FAILED` error with the\n * original error as `cause`: nothing has been sent, so `classifyFailure` says `not_sent` and Retry\n * is safe, and the app needs no error class of its own to tell this apart. A provider that returns\n * no usable string (`''`, `undefined`) is `INVALID_PRIVATE_KEY` with `reason: 'no_key_provided'`,\n * classified the same way; any other key that is not a valid secp256k1 key string is an\n * `INVALID_PRIVATE_KEY` error. An SDK error the provider throws itself is passed through.\n * Signatures are byte-identical to `new PrivateKeySigner(key).signObject(...)`.\n *\n * A JavaScript string cannot be wiped; the provider owns the lifetime of the string it returns.\n *\n * @example\n * ```typescript\n * const signer = createLazyPrivateKeySigner(\n * () => SecurityHelper.getSessionSecret(SecurityHelper.SESSION_PRIVATE_KEY_KEY),\n * { expectedAddress: account.address },\n * );\n * const gswap = new GSwap({ signer });\n * ```\n */\nexport function createLazyPrivateKeySigner(\n getPrivateKey: PrivateKeyProvider,\n options: LazyPrivateKeySignerOptions = {},\n): GalaChainSigner {\n const expected = options.expectedAddress;\n const expectedLower =\n expected === undefined ? undefined : normalizeEthereumAccount(expected, true).toLowerCase();\n\n return {\n async signObject<T extends Record<string, unknown>>(\n _methodName: string,\n dto: T,\n ): Promise<SignedDto<T>> {\n const provided: unknown = await keyFrom(getPrivateKey);\n if (typeof provided !== 'string' || provided === '') {\n throw new GSwapSDKError('INVALID_PRIVATE_KEY', { reason: 'no_key_provided' });\n }\n const key = normalizePrivateKey(provided);\n try {\n if (expectedLower !== undefined && ethAddressOfPrivateKey(key) !== expectedLower) {\n throw new GSwapSDKError('SIGNER_ADDRESS_MISMATCH', {});\n }\n return { ...dto, signature: signNative(key, dto) };\n } finally {\n key.fill(0);\n }\n },\n };\n}\n\n/**\n * A Gala Wallet signer using native GalaChain signing, with an opt-in personal-sign mode.\n *\n * The selected Ethereum account is passed to Gala Wallet. There is NO automatic fallback: a\n * wallet that rejects `native` surfaces its error to the caller, because the only alternative,\n * `personal-sign`, silently recovers a phantom signer on every released Gala Wallet.\n *\n * `scheme: 'personal-sign'` calls\n * `gala_signChainDto([serialize({ ...dto, prefix }), walletAddress, methodName])` and REQUIRES\n * a wallet build that contains wallet_extension commit 3ad7ae6 (strips the embedded `prefix`\n * field and hashes `prefix` + the prefix-less canonical body, the chain's scheme). Wallets\n * released before that fix hash the string with the `prefix` field still inside it and the\n * signature recovers to an address nobody holds, so only opt in once the fixed wallet is\n * released. See SIGNING-PARITY.md.\n */\nexport class GalaWalletSigner implements GalaChainSigner {\n /** The wallet address supplied to Gala Wallet for each signing request. */\n public readonly walletAddress: string;\n\n /** The signing scheme used for every request; it never changes after construction. */\n public readonly scheme: GalaWalletScheme;\n\n /**\n * Creates a Gala Wallet signer.\n *\n * @example\n * ```typescript\n * const signer = new GalaWalletSigner('0x0123456789012345678901234567890123456789');\n * const signed = await signer.signObject('RequestTrade', { token0: 'GALA', token1: 'GUSDC' });\n * ```\n *\n * @param walletAddress - The account passed to Gala Wallet as the signer address. An Ethereum\n * account is `0x` plus 40 hex digits (Gala Wallet's `eth|...` form is normalized to it and held\n * to the same check); a GalaChain id is `client|` plus 24 hex digits, or the bare 24 hex digits\n * frontend_v2 keeps for a direct `client|` login, and is passed through unchanged.\n * @param options - Select native signing (default) or personal-sign explicitly.\n * `'personal-sign'` requires a Gala Wallet build with wallet_extension commit 3ad7ae6.\n * `selectAddress` is the identity (`client|...` / `eth|...`) Gala Wallet is switched to with\n * `window.gala.setAddress` before every signature, when the wallet offers it.\n */\n constructor(\n walletAddress: string,\n options: { scheme?: GalaWalletScheme | undefined; selectAddress?: string | undefined } = {},\n ) {\n this.walletAddress = normalizeGalaWalletAccount(walletAddress);\n this.scheme = options.scheme ?? 'native';\n if (options.selectAddress !== undefined) assertWalletIdentity(options.selectAddress);\n this.selectAddress = options.selectAddress;\n }\n\n /** The identity Gala Wallet is switched to before each signature, if one was given. */\n public readonly selectAddress: string | undefined;\n\n /**\n * Signs a DTO through `gala_signChainDto`.\n *\n * @example\n * ```typescript\n * const signed = await signer.signObject('AddLiquidity', {\n * token0: 'GALA',\n * token1: 'GUSDC',\n * tickLower: -60,\n * tickUpper: 60,\n * });\n * ```\n */\n public async signObject<T extends Record<string, unknown>>(\n methodName: string,\n dto: T,\n ): Promise<SignedDto<T>> {\n const wallet = getWalletProvider();\n\n return withSelectedIdentity(wallet, this.selectAddress, async () => {\n if (this.scheme === 'personal-sign') {\n return personalSign(wallet, dto, this.walletAddress, methodName);\n }\n const signature = await wallet.request({\n method: 'gala_signChainDto',\n params: [serialize(dto), this.walletAddress, methodName, 'native'],\n });\n return { ...dto, signature: getNativeSignature(signature) };\n });\n }\n}\n\n/**\n * A personal-sign signer for MetaMask and other EIP-1193 browser wallets.\n *\n * The chain recovers this signature using the EIP-191 wrap applied by the wallet to\n * `personal_sign`. This is the browser-wallet path used by the v2 frontend and resolves the\n * bare `eth|` identity.\n */\nexport class BrowserWalletSigner implements GalaChainSigner {\n /**\n * Creates a browser-wallet signer.\n *\n * Defaults to `personal_sign` — unchanged from every existing integration, since it only ever\n * resolves the bare `eth|` identity and changing that silently would be a real behavior\n * break for anyone already depending on it. Pass `{ scheme: 'eip712' }` to correctly resolve\n * a registered `client|` alias instead, for `RequestTrade`/`AddLiquidity`/`RemoveLiquidity`/\n * `CollectPositionFees` (`EIP712_CAPABLE_METHODS`) — `CreatePool` and any other method still\n * falls back to `personal_sign` even in `'eip712'` mode, since its EIP-712 signing is a\n * separate, unsolved chain-side problem.\n *\n * @example\n * ```typescript\n * const signer = new BrowserWalletSigner(window.ethereum, '0x0123456789012345678901234567890123456789');\n * const signed = await signer.signObject('RequestTrade', { token0: 'GALA', token1: 'GUSDC' });\n * ```\n * @example\n * ```typescript\n * // Correctly resolves a registered client| alias instead of the bare eth| address.\n * const signer = new BrowserWalletSigner(window.ethereum, walletAddress, { scheme: 'eip712' });\n * ```\n */\n constructor(\n private readonly provider: BrowserWalletProvider,\n walletAddress: string,\n options: { scheme?: 'personal-sign' | 'eip712' } = {},\n ) {\n this.walletAddress = normalizeEthereumAccount(walletAddress, false);\n this.scheme = options.scheme ?? 'personal-sign';\n }\n\n public readonly walletAddress: string;\n private readonly scheme: 'personal-sign' | 'eip712';\n\n /**\n * Signs the DTO with `eth_signTypedData_v4` (when constructed with `{ scheme: 'eip712' }` and\n * `methodName` is in `EIP712_CAPABLE_METHODS`) or the EIP-1193 `personal_sign` method\n * (otherwise — the default).\n *\n * @example\n * ```typescript\n * const signed = await signer.signObject('RequestTrade', {\n * token0: 'GALA',\n * token1: 'GUSDC',\n * fee: 3000,\n * });\n * ```\n */\n public signObject<T extends Record<string, unknown>>(\n methodName: string,\n dto: T,\n ): Promise<SignedDto<T>> {\n if (this.scheme === 'eip712' && EIP712_CAPABLE_METHODS.has(methodName)) {\n return signEIP712(this.provider, methodName, dto, this.walletAddress);\n }\n return personalSign(this.provider, dto, this.walletAddress);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"signers.js","names":["globalObject"],"sources":["../../../src/classes/signers.ts"],"sourcesContent":["import { GSwapSDKError, isGSwapError } from './gswap_sdk_error.js';\nimport {\n buildEIP712Payload,\n EIP712_CAPABLE_METHODS,\n eip712DomainFor,\n type EIP712DomainValue,\n type EIP712Types,\n} from '../utils/eip712.js';\nimport {\n ethAddressOfPrivateKey,\n fixpointPrefixFor,\n normalizePrivateKey,\n personalSignPrefixFor,\n serialize,\n signNative,\n} from '../utils/signing.js';\n\n// Personal-sign prefix, computed the way THE CHAIN verifies it.\n//\n// GalaChain (SDK 3.x) recovers a personal-signed DTO from\n// keccak256(<prefix field> + <canonical JSON of the DTO WITHOUT the prefix field>)\n// and a browser wallet's `personal_sign` (EIP-191) hashes\n// \"\\u0019Ethereum Signed Message:\\n\" + <byte length of message> + <message>.\n// They agree exactly when the wallet signs the bare canonical DTO and `prefix` is the EIP-191\n// header for that message's UTF-8 BYTE length. Any other message still yields a valid-looking\n// signature — it just recovers to an address nobody holds. This SDK used to embed `prefix` in\n// the signed message (the SDK 1.10 client recipe): observed on stage 2026-09-23, such a\n// MetaMask-signed CreatePool executed on chain under a phantom creator, and defi-backend's\n// bouncer now rejects that shape outright.\n//\n// `@gala-chain/connect`'s prefix helper must NOT be used for wallet `personal_sign`: its\n// fixpoint counts the prefix itself and targets the SDK's raw-payload signer. The same header\n// is also what Gala Wallet's `gala_signChainDto` (personal-sign scheme) prepends before hashing,\n// so one value serves both paths.\n//\n// The header itself is computed by `personalSignPrefixFor` in ../utils/signing.ts.\n\n/** The signing schemes supported by the Gala Wallet signer. */\nexport type GalaWalletScheme = 'native' | 'personal-sign';\n\n/** The request surface used by an EIP-1193 browser wallet. */\nexport interface BrowserWalletProvider {\n request(args: { method: string; params?: unknown[] }): Promise<unknown>;\n}\n\ninterface GalaWalletProvider {\n request(args: { method: string; params?: unknown[] }): Promise<unknown>;\n /** Gala Wallet's \"act as this account\" switch; absent on other providers. */\n setAddress?: (address: string) => Promise<void>;\n}\n\ninterface WindowWithGala {\n gala?: GalaWalletProvider;\n}\n\ninterface GlobalWithWindow {\n window?: WindowWithGala;\n}\n\n/** A DTO together with the signature that belongs in the gateway request body. */\nexport type SignedDto<T extends Record<string, unknown>> = T & { signature: string };\n\n/**\n * A signer for complete GalaChain write bodies.\n *\n * Implementations return the DTO fields together with the signature that belongs in the\n * gateway request body. Signer identity is recovered from that signature; implementations do\n * not add signer hints, typed-data metadata, or a domain.\n */\nexport interface GalaChainSigner {\n /**\n * Signs a DTO for a named GalaChain contract method.\n *\n * @example\n * ```typescript\n * const signed = await signer.signObject('RequestTrade', { token0: 'GALA', token1: 'GUSDC' });\n * ```\n */\n signObject<T extends Record<string, unknown>>(methodName: string, dto: T): Promise<SignedDto<T>>;\n}\n\nfunction getWalletProvider(): GalaWalletProvider {\n const globalObject = globalThis as GlobalWithWindow;\n const wallet = globalObject.window?.gala;\n if (!wallet) {\n throw new GSwapSDKError('GALA_WALLET_NOT_AVAILABLE', {});\n }\n return wallet;\n}\n\nfunction getNativeSignature(value: unknown): string {\n if (typeof value !== 'string') {\n throw new GSwapSDKError('INVALID_SIGNATURE', { scheme: 'native' });\n }\n // 130 hex digits (r || s || v), bare as Gala Wallet returns it or `0x`-prefixed; passed on\n // verbatim, because the backend recovers the signer from either spelling.\n if (!/^(?:0x)?[0-9a-fA-F]{130}$/u.test(value)) {\n throw new GSwapSDKError('INVALID_SIGNATURE', { scheme: 'native' });\n }\n return value;\n}\n\nfunction getPersonalSignature(value: unknown): string {\n if (typeof value !== 'string' || !/^(?:0x)?[0-9a-fA-F]{130}$/u.test(value)) {\n throw new GSwapSDKError('INVALID_SIGNATURE', { scheme: 'personal-sign' });\n }\n return value;\n}\n\n/**\n * The `0x`-prefixed Ethereum address a Gala account is signed by, or `undefined` when the account\n * names no key (a `client|` alias or a bare GalaChain id is not derivable from a key). Accepts\n * `0x` plus 40 hex digits and `eth|` plus 40 hex digits, with or without `0x`; the caller's casing\n * is kept. For {@link LazyPrivateKeySignerOptions.expectedAddress}, which should only be set for\n * an account whose key can be checked.\n *\n * @example\n * ```ts\n * createLazyPrivateKeySigner(getKey, { expectedAddress: ethereumAddressOfAccount(account) });\n * ```\n */\nexport function ethereumAddressOfAccount(account: string): string | undefined {\n const match = /^(?:eth\\|(?:0x)?|0x)([0-9a-fA-F]{40})$/u.exec(account);\n return match?.[1] === undefined ? undefined : `0x${match[1]}`;\n}\n\nfunction normalizeEthereumAccount(address: string, allowGalaPrefix: boolean): string {\n const normalized = allowGalaPrefix ? (ethereumAddressOfAccount(address) ?? address) : address;\n if (!/^0x[0-9a-fA-F]{40}$/u.test(normalized)) {\n throw new GSwapSDKError('INVALID_WALLET_ADDRESS', { field: 'walletAddress', value: address });\n }\n // The caller's casing is kept on purpose: frontend_v2 and the wallet hand the wallet the\n // checksummed account, and EIP-1193 / Gala Wallet accept any case, so passing the value through\n // unchanged is the only choice that cannot differ from what they send.\n return normalized;\n}\n\n/**\n * The account handed to Gala Wallet as the signer address. An Ethereum account is held to the\n * same strict check as ever (`0x` plus 40 hex digits; `eth|` plus 40 hex digits, with or without\n * `0x`, is normalized to it). A GalaChain id the wallet can also be asked to sign for is passed\n * through verbatim: `client|` plus 24 hex digits, or the bare 24 hex digits that frontend_v2 keeps\n * in `state.user.wallet` after a direct `client|` login. Nothing else is accepted.\n */\nfunction normalizeGalaWalletAccount(address: string): string {\n const eth = /^eth\\|(?:0x)?([0-9a-fA-F]{40})$/u.exec(address);\n if (eth?.[1] !== undefined) return `0x${eth[1]}`;\n if (/^0x[0-9a-fA-F]{40}$/u.test(address) || /^(?:client\\|)?[0-9a-fA-F]{24}$/u.test(address)) {\n return address;\n }\n throw new GSwapSDKError('INVALID_WALLET_ADDRESS', { field: 'walletAddress', value: address });\n}\n\n/** An identity Gala Wallet can act as: an account form above, any `eth|`/`client|` alias, verbatim. */\nfunction assertWalletIdentity(identity: string): void {\n if (\n !/^(?:eth\\|(?:0x)?[0-9a-fA-F]{40}|0x[0-9a-fA-F]{40}|(?:client\\|)?[0-9a-fA-F]{24})$/u.test(\n identity,\n )\n ) {\n throw new GSwapSDKError('INVALID_WALLET_ADDRESS', { field: 'selectAddress', value: identity });\n }\n}\n\n/** Provider-wide queue: `setAddress` + the signature request must not interleave across signers. */\nconst walletIdentityQueue = new WeakMap<object, Promise<unknown>>();\n\n/**\n * Runs `signWith` with `identity` selected on a Gala Wallet that supports `setAddress`. Gala\n * Wallet holds ONE current account, so two signatures for different identities in flight at once\n * would sign as whichever selection came last; the pair (select, sign) is therefore serialized\n * per provider. A failed selection fails the signature (nothing is requested).\n */\nfunction withSelectedIdentity<T>(\n wallet: GalaWalletProvider,\n identity: string | undefined,\n signWith: () => Promise<T>,\n): Promise<T> {\n const { setAddress } = wallet;\n if (identity === undefined || typeof setAddress !== 'function') return signWith();\n const previous = walletIdentityQueue.get(wallet) ?? Promise.resolve();\n const run = previous\n .catch(() => undefined)\n .then(async () => {\n await setAddress.call(wallet, identity);\n return signWith();\n });\n walletIdentityQueue.set(wallet, run);\n return run;\n}\n\nfunction personalSign<T extends Record<string, unknown>>(\n provider: GalaWalletProvider | BrowserWalletProvider,\n dto: T,\n walletAddress: string,\n methodName?: string,\n): Promise<T & { prefix: string; signature: string }> {\n const prefix = personalSignPrefixFor(dto);\n const payload = { ...dto, prefix };\n // A browser wallet's `personal_sign` applies the EIP-191 header ITSELF, so it must sign the\n // bare DTO; Gala Wallet's `gala_signChainDto` (personal-sign scheme) instead extracts the\n // `prefix` field from what it is handed and prepends it before hashing, so it is handed the\n // prefixed payload. Both submit `{ ...dto, prefix, signature }`.\n const params =\n methodName !== undefined\n ? [serialize(payload), walletAddress, methodName]\n : [serialize(dto), walletAddress];\n\n return provider\n .request({ method: methodName !== undefined ? 'gala_signChainDto' : 'personal_sign', params })\n .then((value) => ({ ...payload, signature: getPersonalSignature(value) }));\n}\n\n/**\n * Signs a DTO via genuine EIP-712 (`eth_signTypedData_v4`) — correctly resolves the `client|`\n * identity, unlike `personal_sign`, for the operations in `EIP712_CAPABLE_METHODS`.\n *\n * Constructs the raw JSON-RPC payload by hand (`{domain, types: {...types, EIP712Domain},\n * primaryType, message}`) rather than depending on ethers.js, matching exactly what\n * `ethers.TypedDataEncoder.getPayload`/`Wallet.signTypedData` produce internally — verified\n * byte-for-byte equivalent (same recovered address from the same signature against both\n * constructions) before this shipped, since gswap-sdk deliberately has no ethers dependency.\n */\nasync function signEIP712<T extends Record<string, unknown>>(\n provider: BrowserWalletProvider,\n methodName: string,\n dto: T,\n walletAddress: string,\n): Promise<T & { types: EIP712Types; domain: EIP712DomainValue; signature: string }> {\n const { completedDto, types } = buildEIP712Payload(methodName, dto);\n // The wallet's LIVE chain id, read on every signature: a user can switch networks after\n // connecting, and a cached or hardcoded id would sign a domain MetaMask itself rejects.\n const domain = eip712DomainFor(await provider.request({ method: 'eth_chainId', params: [] }));\n // The body `prefix` is not part of the EIP-712 `types`, so it is never hashed; it carries the\n // connect-style fixpoint value only to keep the submitted body byte-identical to frontend_v2's.\n const prefix = fixpointPrefixFor(completedDto);\n const message = { ...completedDto, prefix };\n const eip712Domain = [\n { name: 'name', type: 'string' },\n { name: 'chainId', type: 'uint256' },\n ];\n const payload = {\n domain,\n types: { ...types, EIP712Domain: eip712Domain },\n primaryType: methodName,\n message,\n };\n\n const value = await provider.request({\n method: 'eth_signTypedData_v4',\n params: [walletAddress, JSON.stringify(payload)],\n });\n // Key order matches frontend_v2's submitted body: ..., prefix, signature, types, domain.\n return {\n ...(message as T & { prefix: string }),\n signature: getPersonalSignature(value),\n types,\n domain,\n };\n}\n\n/**\n * A GalaChain native signer backed by a secp256k1 private key.\n *\n * Native signing produces the bare GalaChain signature format, without a personal-sign prefix\n * or EIP-712 metadata. The private-key identity is recovered by the chain.\n */\nexport class PrivateKeySigner implements GalaChainSigner {\n private readonly privateKey: Uint8Array;\n\n /**\n * Creates a native signer from a hex or base64 secp256k1 private key.\n *\n * @example\n * ```typescript\n * const signer = new PrivateKeySigner('1'.padStart(64, '0'));\n * const signed = await signer.signObject('RequestTrade', { token0: 'GALA', token1: 'GUSDC' });\n * ```\n */\n constructor(privateKey: string) {\n this.privateKey = normalizePrivateKey(privateKey);\n }\n\n /**\n * Signs the DTO with GalaChain native signing.\n *\n * @example\n * ```typescript\n * const signed = await signer.signObject('RequestTrade', {\n * token0: 'GALA',\n * token1: 'GUSDC',\n * fee: 3000,\n * });\n * ```\n */\n public signObject<T extends Record<string, unknown>>(\n _methodName: string,\n dto: T,\n ): Promise<SignedDto<T>> {\n const signature = signNative(this.privateKey, dto);\n return Promise.resolve({ ...dto, signature });\n }\n}\n\n/** Supplies a private key at sign time: a hex/base64 string, possibly fetched asynchronously. */\nexport type PrivateKeyProvider = () => Promise<string> | string;\n\n/** Options for {@link createLazyPrivateKeySigner}. */\nexport interface LazyPrivateKeySignerOptions {\n /**\n * When set, every signature first checks that the fetched key belongs to this account (`0x...`\n * or the Gala `eth|0x...` / `eth|<40 hex>` form, case-insensitive) and refuses to sign otherwise\n * (`SIGNER_ADDRESS_MISMATCH`). Use it when the key lives in a slot another surface can switch\n * between the moment a request was approved and the moment it is signed.\n */\n expectedAddress?: string | undefined;\n}\n\n/**\n * Calls the app's key provider. Whatever it throws or rejects with is the app's own failure to\n * produce a key before anything was signed or sent, so it becomes `KEY_PROVIDER_FAILED` (the\n * original is `cause`); an SDK error is already catalogued and passes through.\n */\nasync function keyFrom(getPrivateKey: PrivateKeyProvider): Promise<unknown> {\n try {\n return await getPrivateKey();\n } catch (cause: unknown) {\n throw isGSwapError(cause) ? cause : new GSwapSDKError('KEY_PROVIDER_FAILED', {}, { cause });\n }\n}\n\n/**\n * Creates the native-signing equivalent of {@link PrivateKeySigner} for apps that must not hold a\n * key in a long-lived object (an extension that reads its session key at sign time).\n *\n * `getPrivateKey` is called once per `signObject` call, never cached, never shared between calls\n * (concurrent signatures each fetch their own key), and the normalized key bytes are zeroed\n * before `signObject` settles, also on failure. A rejection or exception from `getPrivateKey` (the\n * app's \"wallet is locked\", a storage read that failed) is a `KEY_PROVIDER_FAILED` error with the\n * original error as `cause`: nothing has been sent, so `classifyFailure` says `not_sent` and Retry\n * is safe, and the app needs no error class of its own to tell this apart. A provider that returns\n * no usable string (`''`, `undefined`) is `INVALID_PRIVATE_KEY` with `reason: 'no_key_provided'`,\n * classified the same way; any other key that is not a valid secp256k1 key string is an\n * `INVALID_PRIVATE_KEY` error. An SDK error the provider throws itself is passed through.\n * Signatures are byte-identical to `new PrivateKeySigner(key).signObject(...)`.\n *\n * A JavaScript string cannot be wiped; the provider owns the lifetime of the string it returns.\n *\n * @example\n * ```typescript\n * const signer = createLazyPrivateKeySigner(\n * () => SecurityHelper.getSessionSecret(SecurityHelper.SESSION_PRIVATE_KEY_KEY),\n * { expectedAddress: account.address },\n * );\n * const gswap = new GSwap({ signer });\n * ```\n */\nexport function createLazyPrivateKeySigner(\n getPrivateKey: PrivateKeyProvider,\n options: LazyPrivateKeySignerOptions = {},\n): GalaChainSigner {\n const expected = options.expectedAddress;\n const expectedLower =\n expected === undefined ? undefined : normalizeEthereumAccount(expected, true).toLowerCase();\n\n return {\n async signObject<T extends Record<string, unknown>>(\n _methodName: string,\n dto: T,\n ): Promise<SignedDto<T>> {\n const provided: unknown = await keyFrom(getPrivateKey);\n if (typeof provided !== 'string' || provided === '') {\n throw new GSwapSDKError('INVALID_PRIVATE_KEY', { reason: 'no_key_provided' });\n }\n const key = normalizePrivateKey(provided);\n try {\n if (expectedLower !== undefined && ethAddressOfPrivateKey(key) !== expectedLower) {\n throw new GSwapSDKError('SIGNER_ADDRESS_MISMATCH', {});\n }\n return { ...dto, signature: signNative(key, dto) };\n } finally {\n key.fill(0);\n }\n },\n };\n}\n\n/**\n * A Gala Wallet signer using native GalaChain signing, with an opt-in personal-sign mode.\n *\n * The selected Ethereum account is passed to Gala Wallet. There is NO automatic fallback: a\n * wallet that rejects `native` surfaces its error to the caller, because the only alternative,\n * `personal-sign`, silently recovers a phantom signer on every released Gala Wallet.\n *\n * `scheme: 'personal-sign'` calls\n * `gala_signChainDto([serialize({ ...dto, prefix }), walletAddress, methodName])` and REQUIRES\n * a wallet build that contains wallet_extension commit 34ca4e4 (strips the embedded `prefix`\n * field and hashes `prefix` + the prefix-less canonical body, the chain's scheme). Wallets\n * released before that fix hash the string with the `prefix` field still inside it and the\n * signature recovers to an address nobody holds, so only opt in once the fixed wallet is\n * released. See SIGNING-PARITY.md.\n */\nexport class GalaWalletSigner implements GalaChainSigner {\n /** The wallet address supplied to Gala Wallet for each signing request. */\n public readonly walletAddress: string;\n\n /** The signing scheme used for every request; it never changes after construction. */\n public readonly scheme: GalaWalletScheme;\n\n /**\n * Creates a Gala Wallet signer.\n *\n * @example\n * ```typescript\n * const signer = new GalaWalletSigner('0x0123456789012345678901234567890123456789');\n * const signed = await signer.signObject('RequestTrade', { token0: 'GALA', token1: 'GUSDC' });\n * ```\n *\n * @param walletAddress - The account passed to Gala Wallet as the signer address. An Ethereum\n * account is `0x` plus 40 hex digits (Gala Wallet's `eth|...` form is normalized to it and held\n * to the same check); a GalaChain id is `client|` plus 24 hex digits, or the bare 24 hex digits\n * frontend_v2 keeps for a direct `client|` login, and is passed through unchanged.\n * @param options - Select native signing (default) or personal-sign explicitly.\n * `'personal-sign'` requires a Gala Wallet build with wallet_extension commit 34ca4e4.\n * `selectAddress` is the identity (`client|...` / `eth|...`) Gala Wallet is switched to with\n * `window.gala.setAddress` before every signature, when the wallet offers it.\n */\n constructor(\n walletAddress: string,\n options: { scheme?: GalaWalletScheme | undefined; selectAddress?: string | undefined } = {},\n ) {\n this.walletAddress = normalizeGalaWalletAccount(walletAddress);\n this.scheme = options.scheme ?? 'native';\n if (options.selectAddress !== undefined) assertWalletIdentity(options.selectAddress);\n this.selectAddress = options.selectAddress;\n }\n\n /** The identity Gala Wallet is switched to before each signature, if one was given. */\n public readonly selectAddress: string | undefined;\n\n /**\n * Signs a DTO through `gala_signChainDto`.\n *\n * @example\n * ```typescript\n * const signed = await signer.signObject('AddLiquidity', {\n * token0: 'GALA',\n * token1: 'GUSDC',\n * tickLower: -60,\n * tickUpper: 60,\n * });\n * ```\n */\n public async signObject<T extends Record<string, unknown>>(\n methodName: string,\n dto: T,\n ): Promise<SignedDto<T>> {\n const wallet = getWalletProvider();\n\n return withSelectedIdentity(wallet, this.selectAddress, async () => {\n if (this.scheme === 'personal-sign') {\n return personalSign(wallet, dto, this.walletAddress, methodName);\n }\n const signature = await wallet.request({\n method: 'gala_signChainDto',\n params: [serialize(dto), this.walletAddress, methodName, 'native'],\n });\n return { ...dto, signature: getNativeSignature(signature) };\n });\n }\n}\n\n/**\n * A personal-sign signer for MetaMask and other EIP-1193 browser wallets.\n *\n * The chain recovers this signature using the EIP-191 wrap applied by the wallet to\n * `personal_sign`. This is the browser-wallet path used by the v2 frontend and resolves the\n * bare `eth|` identity.\n */\nexport class BrowserWalletSigner implements GalaChainSigner {\n /**\n * Creates a browser-wallet signer.\n *\n * Defaults to `personal_sign` — unchanged from every existing integration, since it only ever\n * resolves the bare `eth|` identity and changing that silently would be a real behavior\n * break for anyone already depending on it. Pass `{ scheme: 'eip712' }` to correctly resolve\n * a registered `client|` alias instead, for `RequestTrade`/`AddLiquidity`/`RemoveLiquidity`/\n * `CollectPositionFees` (`EIP712_CAPABLE_METHODS`) — `CreatePool` and any other method still\n * falls back to `personal_sign` even in `'eip712'` mode, since its EIP-712 signing is a\n * separate, unsolved chain-side problem.\n *\n * @example\n * ```typescript\n * const signer = new BrowserWalletSigner(window.ethereum, '0x0123456789012345678901234567890123456789');\n * const signed = await signer.signObject('RequestTrade', { token0: 'GALA', token1: 'GUSDC' });\n * ```\n * @example\n * ```typescript\n * // Correctly resolves a registered client| alias instead of the bare eth| address.\n * const signer = new BrowserWalletSigner(window.ethereum, walletAddress, { scheme: 'eip712' });\n * ```\n */\n constructor(\n private readonly provider: BrowserWalletProvider,\n walletAddress: string,\n options: { scheme?: 'personal-sign' | 'eip712' } = {},\n ) {\n this.walletAddress = normalizeEthereumAccount(walletAddress, false);\n this.scheme = options.scheme ?? 'personal-sign';\n }\n\n /** The selected `0x` Ethereum account every signature is requested for. */\n public readonly walletAddress: string;\n private readonly scheme: 'personal-sign' | 'eip712';\n\n /**\n * Signs the DTO with `eth_signTypedData_v4` (when constructed with `{ scheme: 'eip712' }` and\n * `methodName` is in `EIP712_CAPABLE_METHODS`) or the EIP-1193 `personal_sign` method\n * (otherwise — the default).\n *\n * @example\n * ```typescript\n * const signed = await signer.signObject('RequestTrade', {\n * token0: 'GALA',\n * token1: 'GUSDC',\n * fee: 3000,\n * });\n * ```\n */\n public signObject<T extends Record<string, unknown>>(\n methodName: string,\n dto: T,\n ): Promise<SignedDto<T>> {\n if (this.scheme === 'eip712' && EIP712_CAPABLE_METHODS.has(methodName)) {\n return signEIP712(this.provider, methodName, dto, this.walletAddress);\n }\n return personalSign(this.provider, dto, this.walletAddress);\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"submitted_transaction.js","names":[],"sources":["../../../src/classes/submitted_transaction.ts"],"sourcesContent":["import type { ConfirmOptions } from '../types/confirm_options.js';\nimport type { HttpRequestor } from '../types/http_requestor.js';\nimport type { AppliedTrade, Position } from '../types/v2_results.js';\nimport type { WireOptions } from '../types/wire.js';\nimport { createConfirmationSchedule, throwIfAborted } from '../utils/confirmation_schedule.js';\nimport type { SubmitStatus } from '../utils/submit_state.js';\nimport { armTimeout } from '../utils/transport.js';\nimport { confirmAppliedTrade } from './apply_results.js';\nimport { GSwapSDKError } from './gswap_sdk_error.js';\n\n/** Result of one position-confirmation poll. */\nexport type PositionConfirmationResult =\n { confirmed: true; position: Position | null } | { confirmed: false; position?: Position | null };\n\n/** A submitted v2 transaction whose confirmation mode is explicit in the gateway response. */\nexport class SubmittedTransaction<TConfirmation = AppliedTrade | Position | null> {\n public readonly method: string;\n public readonly uniqueKey: string;\n public readonly transactionId: string | null;\n public readonly blockNumber: number | null;\n /** The gateway's `data.mode`: `'sync'` (executed, `result` is the chain result) or `'queued'`. */\n public readonly mode: 'sync' | 'queued';\n /**\n * What the gateway's answer established: `'executed'` for `mode: 'sync'`, `'queued'` for\n * `mode: 'queued'` (accepted, NOT executed: `await confirm()` for the outcome). A refused answer\n * never becomes a transaction, so there is no `'rejected'` here; see {@link classifySubmitEnvelope}\n * for classifying a raw envelope.\n */\n public readonly status: SubmitStatus;\n /** The synchronous gateway result, or `undefined` when `mode === 'queued'`. */\n public readonly result: unknown;\n /**\n * The gateway's whole JSON answer as it came (`{ status, message, error, data }`), for an app\n * that hands the backend's own envelope to its callers; `undefined` for a transaction built from\n * a uniqueKey alone.\n */\n public readonly envelope: unknown;\n private readonly backendBaseUrl: string;\n private readonly requestor: HttpRequestor;\n private readonly requestTimeoutMs: number;\n private readonly wire: WireOptions | undefined;\n private readonly positionConfirmation:\n ((signal: AbortSignal) => Promise<PositionConfirmationResult>) | undefined;\n\n /** Construct a submitted transaction from a successful gateway response.\n *\n * @example\n * ```ts\n * const transaction = new SubmittedTransaction({\n * method: 'RequestTrade', mode: 'queued', uniqueKey: 'swap-1',\n * transactionId: 'request-tx', result: undefined,\n * dexBackendBaseUrl: 'https://dex.example', httpRequestor,\n * });\n * ```\n */\n constructor(options: {\n method: string;\n uniqueKey: string;\n transactionId: string | null;\n mode: 'sync' | 'queued';\n result: unknown;\n dexBackendBaseUrl: string;\n httpRequestor: HttpRequestor;\n blockNumber?: number | null | undefined;\n envelope?: unknown;\n requestTimeoutMs?: number | undefined;\n /** Wire options of the apply-results polls `confirm()` makes. */\n wire?: WireOptions | undefined;\n positionConfirmation?:\n ((signal: AbortSignal) => Promise<PositionConfirmationResult>) | undefined;\n }) {\n this.method = options.method;\n this.uniqueKey = options.uniqueKey;\n this.transactionId = options.transactionId;\n this.blockNumber = options.blockNumber ?? null;\n this.mode = options.mode;\n this.status = options.mode === 'queued' ? 'queued' : 'executed';\n this.result = options.result;\n this.envelope = options.envelope;\n this.backendBaseUrl = options.dexBackendBaseUrl.replace(/\\/$/u, '');\n this.requestor = options.httpRequestor;\n this.requestTimeoutMs = options.requestTimeoutMs ?? 30_000;\n this.wire = options.wire;\n this.positionConfirmation = options.positionConfirmation;\n }\n\n /** Confirm a queued RequestTrade or prove the affected position for a liquidity write.\n *\n * For RequestTrade, calling this method is required to learn the outcome: the queued\n * acknowledgement proves acceptance, not execution.\n *\n * @example\n * ```ts\n * const applied = await submitted.confirm({ initialDelayMs: 3_000 });\n * ```\n */\n public async confirm(options?: ConfirmOptions): Promise<TConfirmation> {\n if (this.method === 'RequestTrade') {\n return this.confirmApplied(options) as Promise<TConfirmation>;\n }\n return this.confirmPosition(options) as Promise<TConfirmation>;\n }\n\n private confirmApplied(options?: ConfirmOptions): Promise<AppliedTrade> {\n return confirmAppliedTrade(\n {\n dexBackendBaseUrl: this.backendBaseUrl,\n httpRequestor: this.requestor,\n requestTimeoutMs: this.requestTimeoutMs,\n wire: this.wire,\n },\n { uniqueKey: this.uniqueKey, requestTransactionId: this.transactionId },\n options,\n );\n }\n\n /** Confirm a liquidity write against the position's observable effect.\n *\n * @returns The position after the write, or `null` for a confirmed full close.\n * @example\n * ```ts\n * const position = await submitted.confirm({ timeoutMs: 60_000 });\n * ```\n */\n private async confirmPosition(options?: ConfirmOptions): Promise<Position | null> {\n if (this.positionConfirmation === undefined) throw GSwapSDKError.noWalletAddressError();\n // A liquidity write has already executed when the gateway answers, so the first re-read\n // happens immediately unless the caller asks for a delay.\n const callerSignal = options?.signal;\n const abortError = (cause?: unknown): Error =>\n new GSwapSDKError('CONFIRMATION_ABORTED', {}, { uniqueKey: this.uniqueKey, cause });\n const schedule = createConfirmationSchedule(options, 0, abortError);\n await schedule.waitForRead();\n while (true) {\n const controller = new AbortController();\n const disarm = armTimeout(controller, this.requestTimeoutMs);\n const forwardAbort = (): void => controller.abort();\n callerSignal?.addEventListener('abort', forwardAbort, { once: true });\n try {\n const result = await this.positionConfirmation(controller.signal);\n if (result.confirmed) return result.position;\n } catch (error: unknown) {\n throwIfAborted(callerSignal, abortError);\n throw error;\n } finally {\n disarm();\n callerSignal?.removeEventListener('abort', forwardAbort);\n }\n if (!(await schedule.waitForRead())) break;\n }\n throw GSwapSDKError.confirmationTimeoutError(this.uniqueKey);\n }\n}\n"],"mappings":"2VAeA,IAAa,EAAb,KAAkF,CAwChF,YAAY,EAeT,CAtDa,EAAA,KAAA,SAAA,IAAA,EAAA,EACA,EAAA,KAAA,YAAA,IAAA,EAAA,EACA,EAAA,KAAA,gBAAA,IAAA,EAAA,EACA,EAAA,KAAA,cAAA,IAAA,EAAA,EAEA,EAAA,KAAA,OAAA,IAAA,EAAA,EAOA,EAAA,KAAA,SAAA,IAAA,EAAA,EAEA,EAAA,KAAA,SAAA,IAAA,EAAA,EAMA,EAAA,KAAA,WAAA,IAAA,EAAA,EACC,EAAA,KAAA,iBAAA,IAAA,EAAA,EACA,EAAA,KAAA,YAAA,IAAA,EAAA,EACA,EAAA,KAAA,mBAAA,IAAA,EAAA,EACA,EAAA,KAAA,OAAA,IAAA,EAAA,EACA,EAAA,KAAA,uBAAA,IAAA,EAAA,EA8Bf,KAAK,OAAS,EAAQ,OACtB,KAAK,UAAY,EAAQ,UACzB,KAAK,cAAgB,EAAQ,cAC7B,KAAK,YAAc,EAAQ,aAAe,KAC1C,KAAK,KAAO,EAAQ,KACpB,KAAK,OAAS,EAAQ,OAAS,SAAW,SAAW,WACrD,KAAK,OAAS,EAAQ,OACtB,KAAK,SAAW,EAAQ,SACxB,KAAK,eAAiB,EAAQ,kBAAkB,QAAQ,OAAQ,EAAE,EAClE,KAAK,UAAY,EAAQ,cACzB,KAAK,iBAAmB,EAAQ,kBAAoB,IACpD,KAAK,KAAO,EAAQ,KACpB,KAAK,qBAAuB,EAAQ,oBACtC,CAYA,MAAa,QAAQ,EAAkD,CAIrE,OAHI,KAAK,SAAW,eACX,KAAK,eAAe,CAAO,EAE7B,KAAK,gBAAgB,CAAO,CACrC,CAEA,eAAuB,EAAiD,CACtE,OAAO,EACL,CACE,kBAAmB,KAAK,eACxB,cAAe,KAAK,UACpB,iBAAkB,KAAK,iBACvB,KAAM,KAAK,IACb,EACA,CAAE,UAAW,KAAK,UAAW,qBAAsB,KAAK,aAAc,EACtE,CACF,CACF,CAUA,MAAc,gBAAgB,EAAoD,CAChF,GAAI,KAAK,uBAAyB,IAAA,GAAW,MAAM,EAAc,qBAAqB,EAGtF,IAAM,EAAe,GAAS,OACxB,EAAc,GAClB,IAAI,EAAc,uBAAwB,CAAC,EAAG,CAAE,UAAW,KAAK,UAAW,OAAM,CAAC,EAC9E,EAAW,EAA2B,EAAS,EAAG,CAAU,EAElE,IADA,MAAM,EAAS,YAAY,IACd,CACX,IAAM,EAAa,IAAI,gBACjB,EAAS,EAAW,EAAY,KAAK,gBAAgB,EACrD,MAA2B,EAAW,MAAM,EAClD,GAAc,iBAAiB,QAAS,EAAc,CAAE,KAAM,EAAK,CAAC,EACpE,GAAI,CACF,IAAM,EAAS,MAAM,KAAK,qBAAqB,EAAW,MAAM,EAChE,GAAI,EAAO,UAAW,OAAO,EAAO,QACtC,OAAS,EAAgB,CAEvB,MADA,EAAe,EAAc,CAAU,EACjC,CACR,QAAU,CACR,EAAO,EACP,GAAc,oBAAoB,QAAS,CAAY,CACzD,CACA,GAAI,CAAE,MAAM,EAAS,YAAY,EAAI,KACvC,CACA,MAAM,EAAc,yBAAyB,KAAK,SAAS,CAC7D,CACF"}
1
+ {"version":3,"file":"submitted_transaction.js","names":[],"sources":["../../../src/classes/submitted_transaction.ts"],"sourcesContent":["import type { ConfirmOptions } from '../types/confirm_options.js';\nimport type { HttpRequestor } from '../types/http_requestor.js';\nimport type { AppliedTrade, Position } from '../types/v2_results.js';\nimport type { WireOptions } from '../types/wire.js';\nimport { createConfirmationSchedule, throwIfAborted } from '../utils/confirmation_schedule.js';\nimport type { SubmitStatus } from '../utils/submit_state.js';\nimport { armTimeout } from '../utils/transport.js';\nimport { confirmAppliedTrade } from './apply_results.js';\nimport { GSwapSDKError } from './gswap_sdk_error.js';\n\n/** Result of one position-confirmation poll. */\nexport type PositionConfirmationResult =\n { confirmed: true; position: Position | null } | { confirmed: false; position?: Position | null };\n\n/** A submitted v2 transaction whose confirmation mode is explicit in the gateway response. */\nexport class SubmittedTransaction<TConfirmation = AppliedTrade | Position | null> {\n /** The chain method that was submitted (`RequestTrade`, `AddLiquidity`, ...). */\n public readonly method: string;\n /** The write's durable correlation key: persist it, and confirm from it alone later. */\n public readonly uniqueKey: string;\n /** The request transaction id the gateway reported, or `null` when it gave none. */\n public readonly transactionId: string | null;\n /** The block of the transaction, when the answer carried one; otherwise `null`. */\n public readonly blockNumber: number | null;\n /** The gateway's `data.mode`: `'sync'` (executed, `result` is the chain result) or `'queued'`. */\n public readonly mode: 'sync' | 'queued';\n /**\n * What the gateway's answer established: `'executed'` for `mode: 'sync'`, `'queued'` for\n * `mode: 'queued'` (accepted, NOT executed: `await confirm()` for the outcome). A refused answer\n * never becomes a transaction, so there is no `'rejected'` here; see {@link classifySubmitEnvelope}\n * for classifying a raw envelope.\n */\n public readonly status: SubmitStatus;\n /** The synchronous gateway result, or `undefined` when `mode === 'queued'`. */\n public readonly result: unknown;\n /**\n * The gateway's whole JSON answer as it came (`{ status, message, error, data }`), for an app\n * that hands the backend's own envelope to its callers; `undefined` for a transaction built from\n * a uniqueKey alone.\n */\n public readonly envelope: unknown;\n private readonly backendBaseUrl: string;\n private readonly requestor: HttpRequestor;\n private readonly requestTimeoutMs: number;\n private readonly wire: WireOptions | undefined;\n private readonly positionConfirmation:\n ((signal: AbortSignal) => Promise<PositionConfirmationResult>) | undefined;\n\n /** Construct a submitted transaction from a successful gateway response.\n *\n * @example\n * ```ts\n * const transaction = new SubmittedTransaction({\n * method: 'RequestTrade', mode: 'queued', uniqueKey: 'swap-1',\n * transactionId: 'request-tx', result: undefined,\n * dexBackendBaseUrl: 'https://dex.example', httpRequestor,\n * });\n * ```\n */\n constructor(options: {\n method: string;\n uniqueKey: string;\n transactionId: string | null;\n mode: 'sync' | 'queued';\n result: unknown;\n dexBackendBaseUrl: string;\n httpRequestor: HttpRequestor;\n blockNumber?: number | null | undefined;\n envelope?: unknown;\n requestTimeoutMs?: number | undefined;\n /** Wire options of the apply-results polls `confirm()` makes. */\n wire?: WireOptions | undefined;\n positionConfirmation?:\n ((signal: AbortSignal) => Promise<PositionConfirmationResult>) | undefined;\n }) {\n this.method = options.method;\n this.uniqueKey = options.uniqueKey;\n this.transactionId = options.transactionId;\n this.blockNumber = options.blockNumber ?? null;\n this.mode = options.mode;\n this.status = options.mode === 'queued' ? 'queued' : 'executed';\n this.result = options.result;\n this.envelope = options.envelope;\n this.backendBaseUrl = options.dexBackendBaseUrl.replace(/\\/$/u, '');\n this.requestor = options.httpRequestor;\n this.requestTimeoutMs = options.requestTimeoutMs ?? 30_000;\n this.wire = options.wire;\n this.positionConfirmation = options.positionConfirmation;\n }\n\n /** Confirm a queued RequestTrade or prove the affected position for a liquidity write.\n *\n * For RequestTrade, calling this method is required to learn the outcome: the queued\n * acknowledgement proves acceptance, not execution.\n *\n * @example\n * ```ts\n * const applied = await submitted.confirm({ initialDelayMs: 3_000 });\n * ```\n */\n public async confirm(options?: ConfirmOptions): Promise<TConfirmation> {\n if (this.method === 'RequestTrade') {\n return this.confirmApplied(options) as Promise<TConfirmation>;\n }\n return this.confirmPosition(options) as Promise<TConfirmation>;\n }\n\n private confirmApplied(options?: ConfirmOptions): Promise<AppliedTrade> {\n return confirmAppliedTrade(\n {\n dexBackendBaseUrl: this.backendBaseUrl,\n httpRequestor: this.requestor,\n requestTimeoutMs: this.requestTimeoutMs,\n wire: this.wire,\n },\n { uniqueKey: this.uniqueKey, requestTransactionId: this.transactionId },\n options,\n );\n }\n\n /** Confirm a liquidity write against the position's observable effect.\n *\n * @returns The position after the write, or `null` for a confirmed full close.\n * @example\n * ```ts\n * const position = await submitted.confirm({ timeoutMs: 60_000 });\n * ```\n */\n private async confirmPosition(options?: ConfirmOptions): Promise<Position | null> {\n if (this.positionConfirmation === undefined) throw GSwapSDKError.noWalletAddressError();\n // A liquidity write has already executed when the gateway answers, so the first re-read\n // happens immediately unless the caller asks for a delay.\n const callerSignal = options?.signal;\n const abortError = (cause?: unknown): Error =>\n new GSwapSDKError('CONFIRMATION_ABORTED', {}, { uniqueKey: this.uniqueKey, cause });\n const schedule = createConfirmationSchedule(options, 0, abortError);\n await schedule.waitForRead();\n while (true) {\n const controller = new AbortController();\n const disarm = armTimeout(controller, this.requestTimeoutMs);\n const forwardAbort = (): void => controller.abort();\n callerSignal?.addEventListener('abort', forwardAbort, { once: true });\n try {\n const result = await this.positionConfirmation(controller.signal);\n if (result.confirmed) return result.position;\n } catch (error: unknown) {\n throwIfAborted(callerSignal, abortError);\n throw error;\n } finally {\n disarm();\n callerSignal?.removeEventListener('abort', forwardAbort);\n }\n if (!(await schedule.waitForRead())) break;\n }\n throw GSwapSDKError.confirmationTimeoutError(this.uniqueKey);\n }\n}\n"],"mappings":"2VAeA,IAAa,EAAb,KAAkF,CA4ChF,YAAY,EAeT,CAzDa,EAAA,KAAA,SAAA,IAAA,EAAA,EAEA,EAAA,KAAA,YAAA,IAAA,EAAA,EAEA,EAAA,KAAA,gBAAA,IAAA,EAAA,EAEA,EAAA,KAAA,cAAA,IAAA,EAAA,EAEA,EAAA,KAAA,OAAA,IAAA,EAAA,EAOA,EAAA,KAAA,SAAA,IAAA,EAAA,EAEA,EAAA,KAAA,SAAA,IAAA,EAAA,EAMA,EAAA,KAAA,WAAA,IAAA,EAAA,EACC,EAAA,KAAA,iBAAA,IAAA,EAAA,EACA,EAAA,KAAA,YAAA,IAAA,EAAA,EACA,EAAA,KAAA,mBAAA,IAAA,EAAA,EACA,EAAA,KAAA,OAAA,IAAA,EAAA,EACA,EAAA,KAAA,uBAAA,IAAA,EAAA,EA8Bf,KAAK,OAAS,EAAQ,OACtB,KAAK,UAAY,EAAQ,UACzB,KAAK,cAAgB,EAAQ,cAC7B,KAAK,YAAc,EAAQ,aAAe,KAC1C,KAAK,KAAO,EAAQ,KACpB,KAAK,OAAS,EAAQ,OAAS,SAAW,SAAW,WACrD,KAAK,OAAS,EAAQ,OACtB,KAAK,SAAW,EAAQ,SACxB,KAAK,eAAiB,EAAQ,kBAAkB,QAAQ,OAAQ,EAAE,EAClE,KAAK,UAAY,EAAQ,cACzB,KAAK,iBAAmB,EAAQ,kBAAoB,IACpD,KAAK,KAAO,EAAQ,KACpB,KAAK,qBAAuB,EAAQ,oBACtC,CAYA,MAAa,QAAQ,EAAkD,CAIrE,OAHI,KAAK,SAAW,eACX,KAAK,eAAe,CAAO,EAE7B,KAAK,gBAAgB,CAAO,CACrC,CAEA,eAAuB,EAAiD,CACtE,OAAO,EACL,CACE,kBAAmB,KAAK,eACxB,cAAe,KAAK,UACpB,iBAAkB,KAAK,iBACvB,KAAM,KAAK,IACb,EACA,CAAE,UAAW,KAAK,UAAW,qBAAsB,KAAK,aAAc,EACtE,CACF,CACF,CAUA,MAAc,gBAAgB,EAAoD,CAChF,GAAI,KAAK,uBAAyB,IAAA,GAAW,MAAM,EAAc,qBAAqB,EAGtF,IAAM,EAAe,GAAS,OACxB,EAAc,GAClB,IAAI,EAAc,uBAAwB,CAAC,EAAG,CAAE,UAAW,KAAK,UAAW,OAAM,CAAC,EAC9E,EAAW,EAA2B,EAAS,EAAG,CAAU,EAElE,IADA,MAAM,EAAS,YAAY,IACd,CACX,IAAM,EAAa,IAAI,gBACjB,EAAS,EAAW,EAAY,KAAK,gBAAgB,EACrD,MAA2B,EAAW,MAAM,EAClD,GAAc,iBAAiB,QAAS,EAAc,CAAE,KAAM,EAAK,CAAC,EACpE,GAAI,CACF,IAAM,EAAS,MAAM,KAAK,qBAAqB,EAAW,MAAM,EAChE,GAAI,EAAO,UAAW,OAAO,EAAO,QACtC,OAAS,EAAgB,CAEvB,MADA,EAAe,EAAc,CAAU,EACjC,CACR,QAAU,CACR,EAAO,EACP,GAAc,oBAAoB,QAAS,CAAY,CACzD,CACA,GAAI,CAAE,MAAM,EAAS,YAAY,EAAI,KACvC,CACA,MAAM,EAAc,yBAAyB,KAAK,SAAS,CAC7D,CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"chain_dto_signing.js","names":[],"sources":["../../../src/utils/chain_dto_signing.ts"],"sourcesContent":["/**\n * String-key signing entry points for apps that hold a private key only briefly (the Gala Wallet\n * extension reads its session key at sign time and must not keep it in a long-lived object).\n *\n * These are thin, byte-exact ports of the functions the wallet shipped in\n * `wallet_extension/src/helpers/wallet/signing.ts` (including the fail-closed personal-sign\n * payload resolver from its commit 3ad7ae6), re-expressed over the SDK's own primitives. Every\n * function takes the key as a string (`0x`-optional hex, or base64) or as bytes, normalizes it per\n * call, and zeroes the normalized copy before returning, so nothing in this module retains a key.\n * A JavaScript string cannot be wiped; the caller owns the lifetime of the string it passes in.\n */\nimport BigNumber from 'bignumber.js';\nimport { GSwapSDKError } from '../classes/gswap_sdk_error.js';\nimport {\n normalizePrivateKey,\n serialize,\n signPersonalMessage,\n signSerializedNative,\n} from './signing.js';\n\n/** A secp256k1 private key as a hex/base64 string or as raw 32 bytes. */\nexport type PrivateKeyInput = string | Uint8Array;\n\n/**\n * Runs `action` with a private normalized copy of the key, zeroed afterwards (also when `action`\n * throws). Bytes given by the caller are copied, never modified: the caller owns them.\n */\nexport function withPrivateKey<T>(privateKey: PrivateKeyInput, action: (key: Uint8Array) => T): T {\n const key = normalizePrivateKey(privateKey);\n try {\n return action(key);\n } finally {\n key.fill(0);\n }\n}\n\n/**\n * GalaChain NATIVE signature over an already-serialized DTO: keccak256 of the exact bytes with no\n * prefix and no envelope (wallet `signChainDtoNative`). The caller owns serialization, so the\n * bytes a user reviewed and the bytes signed cannot diverge.\n *\n * @returns 130 lowercase hex characters, `r || s || v` with `v` = `1b`/`1c`, no `0x`.\n */\nexport function signChainDtoNative(privateKey: PrivateKeyInput, serializedDto: string): string {\n return withPrivateKey(privateKey, (key) => signSerializedNative(key, serializedDto));\n}\n\n/**\n * The personal-sign hash primitive (wallet `signChainDtoHash`): keccak256(`prefix` + `message`),\n * both verbatim, with no further EIP-191 wrap. `message` must be the DTO's serialization WITHOUT\n * its `prefix` field; callers holding a dApp-supplied string go through `signChainDtoPersonalSign`,\n * which derives both inputs from it.\n */\nexport function signChainDtoHash(\n privateKey: PrivateKeyInput,\n prefix: string,\n message: string,\n): string {\n return withPrivateKey(privateKey, (key) => signPersonalMessage(key, prefix, message));\n}\n\nconst PERSONAL_SIGN_HEADER = String.fromCharCode(0x19) + 'Ethereum Signed Message:\\n';\nconst PERSONAL_SIGN_LENGTH_PATTERN = /^(?:0|[1-9][0-9]*)$/u;\n\n// A header with a body starting `{` can never collide with another EIP-191 message's hash, and\n// anything that is not a header (arbitrary bytes the user cannot see on an approval card) is\n// refused. The number is NOT required to equal the byte length: the chain hashes whatever prefix\n// the submitted DTO carries, and defi-backend's bouncer rejects a mismatched header loudly.\nfunction isPersonalSignHeader(prefix: string): boolean {\n return (\n prefix.startsWith(PERSONAL_SIGN_HEADER) &&\n PERSONAL_SIGN_LENGTH_PATTERN.test(prefix.slice(PERSONAL_SIGN_HEADER.length))\n );\n}\n\n/**\n * Top-level fields that would make the chain (`getPayloadToSign`) or defi-backend's personal-sign\n * verifier hash something other than `prefix + serialize(dto minus prefix)`. An unsigned DTO from\n * a dApp never legitimately carries any of them, so they fail closed. Order is part of the error\n * message the wallet shows its user.\n */\nconst PERSONAL_SIGN_FORBIDDEN_FIELDS = [\n 'signature',\n 'multisig',\n 'trace',\n 'types',\n 'domain',\n 'stringsInstructions',\n] as const;\n\n/** What a `gala_signChainDto` personal-sign request hashes. */\nexport interface PersonalSignChainDtoPayload {\n /** The `prefix` the chain prepends: the DTO's own `prefix` field, or `''` when it has none. */\n prefix: string;\n /** The exact message hashed after `prefix`. */\n message: string;\n}\n\n/**\n * Derives what to hash for a `gala_signChainDto` personal-sign request from the exact string the\n * dApp sent (and the approval card displayed): wallet `resolvePersonalSignChainDtoPayload`.\n *\n * The dApp submits `{ ...dto, prefix, signature }`, which the chain recovers from\n * `keccak256(prefix + serialize(dto WITHOUT prefix))`. So when the string embeds a `prefix`\n * field, that field is removed and the remainder re-serialized; the prefix used is the dApp's own\n * value byte for byte. A string with no `prefix` field is hashed verbatim with an empty prefix\n * (chain-equivalent to a native signature of that exact string).\n *\n * Fails closed (throws `GSwapSDKError` `PERSONAL_SIGN_NOT_JSON`, `PERSONAL_SIGN_NOT_OBJECT`,\n * `PERSONAL_SIGN_BAD_PREFIX`, `PERSONAL_SIGN_ENVELOPE_FIELDS` or `PERSONAL_SIGN_NOT_CANONICAL`) on:\n * malformed JSON; JSON that is not a plain object; a `prefix` that is not an EIP-191\n * \"Ethereum Signed Message\" header; any signing-envelope field (`signature`, `multisig`, `trace`,\n * `types`, `domain`, `stringsInstructions`); and a string that is not already the canonical\n * serialization of what it parses to (duplicate keys, lossy numbers, key-order tricks the card\n * would render differently). Stripping `prefix` is therefore the ONLY byte change between the\n * reviewed string and the signed message.\n */\nexport function resolvePersonalSignChainDtoPayload(data: string): PersonalSignChainDtoPayload {\n let parsed: unknown;\n try {\n parsed = JSON.parse(data);\n } catch {\n throw new GSwapSDKError('PERSONAL_SIGN_NOT_JSON', {});\n }\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new GSwapSDKError('PERSONAL_SIGN_NOT_OBJECT', {});\n }\n const dto = parsed as Record<string, unknown>;\n if (!Object.prototype.hasOwnProperty.call(dto, 'prefix')) {\n return { prefix: '', message: data };\n }\n\n const { prefix, ...withoutPrefix } = dto;\n if (typeof prefix !== 'string' || !isPersonalSignHeader(prefix)) {\n throw new GSwapSDKError('PERSONAL_SIGN_BAD_PREFIX', {});\n }\n const forbidden = PERSONAL_SIGN_FORBIDDEN_FIELDS.filter((field) =>\n Object.prototype.hasOwnProperty.call(withoutPrefix, field),\n );\n if (forbidden.length > 0) {\n throw new GSwapSDKError('PERSONAL_SIGN_ENVELOPE_FIELDS', { fields: forbidden.join(', ') });\n }\n if (serialize(dto) !== data) {\n throw new GSwapSDKError('PERSONAL_SIGN_NOT_CANONICAL', {});\n }\n return { prefix, message: serialize(withoutPrefix) };\n}\n\n/**\n * Signs a GalaChain personal-sign scheme DTO from the exact string a dApp sent through\n * `gala_signChainDto` (wallet `signChainDtoPersonalSign`). See\n * `resolvePersonalSignChainDtoPayload` for what is signed and what is refused; the signature\n * recovers, under the chain's `getPayloadToSign`, to this key for the DTO the dApp submits with\n * its `prefix`, and equals a `personal_sign` of the bare body when that prefix is the EIP-191\n * header of the bare body's byte length.\n *\n * @throws GSwapSDKError (`PERSONAL_SIGN_*`, see `resolvePersonalSignChainDtoPayload`) before any\n * signing when the input is refused.\n */\nexport function signChainDtoPersonalSign(privateKey: PrivateKeyInput, data: string): string {\n const { prefix, message } = resolvePersonalSignChainDtoPayload(data);\n return signChainDtoHash(privateKey, prefix, message);\n}\n\n/**\n * Raw object signing (wallet `signObject`), used for transfers, bridge and fee-dry-run DTOs the\n * wallet signs itself. Removes ONLY `signature`, signs `keccak256(serialize(rest))` natively\n * (no `prefix` handling, and `trace` / `multisig` stay in the signed bytes, unlike\n * `signNative`), and returns `{ signature, ...rest }` with the signature as the FIRST key, exactly\n * like the wallet. Callers that must not sign extra fields strip them first.\n */\nexport function signRawObject(\n privateKey: PrivateKeyInput,\n obj: Record<string, unknown>,\n): { signature: string } & Record<string, unknown> {\n const rest = { ...obj };\n delete rest['signature'];\n return {\n signature: signChainDtoNative(privateKey, serialize(rest)),\n ...rest,\n };\n}\n\n/**\n * Drops the `EIP712Domain` entry from a dApp-supplied types map (a typed-data encoder derives the\n * domain type itself and rejects an explicit one). The order of the remaining types is preserved.\n */\nexport function stripEip712DomainType<T>(types: Record<string, T>): Record<string, T> {\n const stripped: Record<string, T> = {};\n for (const typeName of Object.keys(types)) {\n if (typeName !== 'EIP712Domain') stripped[typeName] = types[typeName] as T;\n }\n return stripped;\n}\n\n/**\n * `collection$category$type$additionalKey` for a token-class object (wallet `decodeTokenData`).\n * Lenient on purpose: any missing or non-string part becomes `''`, so a display layer never\n * throws on odd input.\n */\nexport function tokenClassDollarKey(token: Record<string, unknown> | null | undefined): string {\n const part = (field: string): string => {\n const value = token?.[field];\n return typeof value === 'string' ? value : '';\n };\n return `${part('collection')}$${part('category')}$${part('type')}$${part('additionalKey')}`;\n}\n\n/**\n * A quantity in the form GalaChain's `IsBigNumber` validator accepts: FIXED notation, no leading\n * zeros, no trailing decimal zeros (`BigNumber#toFixed()`; never `toString()`, which turns\n * `0.00000001` into `1e-8`, and never a JS number in between).\n *\n * @throws GSwapSDKError (`INVALID_DECIMAL`) for a value that is not a finite number. The wallet's\n * `cleanNumber` returned the string `'NaN'` there; signing that text can only fail on chain.\n */\nexport function canonicalDecimalString(value: string | number): string {\n let parsed = new BigNumber(Number.NaN);\n try {\n parsed = new BigNumber(value);\n } catch {\n // BigNumber throws instead of returning NaN when its DEBUG flag is on; both mean \"invalid\".\n }\n if (!parsed.isFinite()) {\n throw new GSwapSDKError('INVALID_DECIMAL', { value: String(value) });\n }\n return parsed.toFixed();\n}\n"],"mappings":"+MA2BA,SAAgB,EAAkB,EAA6B,EAAmC,CAChG,IAAM,EAAM,EAAoB,CAAU,EAC1C,GAAI,CACF,OAAO,EAAO,CAAG,CACnB,QAAU,CACR,EAAI,KAAK,CAAC,CACZ,CACF,CASA,SAAgB,EAAmB,EAA6B,EAA+B,CAC7F,OAAO,EAAe,EAAa,GAAQ,EAAqB,EAAK,CAAa,CAAC,CACrF,CAQA,SAAgB,EACd,EACA,EACA,EACQ,CACR,OAAO,EAAe,EAAa,GAAQ,EAAoB,EAAK,EAAQ,CAAO,CAAC,CACtF,CAEA,MACM,EAA+B,uBAMrC,SAAS,EAAqB,EAAyB,CACrD,OACE,EAAO,WAAW;CAAoB,GACtC,EAA6B,KAAK,EAAO,MAAM,EAA2B,CAAC,CAE/E,CAQA,MAAM,EAAiC,CACrC,YACA,WACA,QACA,QACA,SACA,qBACF,EA6BA,SAAgB,EAAmC,EAA2C,CAC5F,IAAI,EACJ,GAAI,CACF,EAAS,KAAK,MAAM,CAAI,CAC1B,MAAQ,CACN,MAAM,IAAI,EAAc,yBAA0B,CAAC,CAAC,CACtD,CACA,GAAuB,OAAO,GAAW,WAArC,GAAiD,MAAM,QAAQ,CAAM,EACvE,MAAM,IAAI,EAAc,2BAA4B,CAAC,CAAC,EAExD,IAAM,EAAM,EACZ,GAAI,CAAC,OAAO,UAAU,eAAe,KAAK,EAAK,QAAQ,EACrD,MAAO,CAAE,OAAQ,GAAI,QAAS,CAAK,EAGrC,GAAM,CAAE,SAAQ,GAAG,GAAkB,EACrC,GAAI,OAAO,GAAW,UAAY,CAAC,EAAqB,CAAM,EAC5D,MAAM,IAAI,EAAc,2BAA4B,CAAC,CAAC,EAExD,IAAM,EAAY,EAA+B,OAAQ,GACvD,OAAO,UAAU,eAAe,KAAK,EAAe,CAAK,CAC3D,EACA,GAAI,EAAU,OAAS,EACrB,MAAM,IAAI,EAAc,gCAAiC,CAAE,OAAQ,EAAU,KAAK,IAAI,CAAE,CAAC,EAE3F,GAAI,EAAU,CAAG,IAAM,EACrB,MAAM,IAAI,EAAc,8BAA+B,CAAC,CAAC,EAE3D,MAAO,CAAE,SAAQ,QAAS,EAAU,CAAa,CAAE,CACrD,CAaA,SAAgB,EAAyB,EAA6B,EAAsB,CAC1F,GAAM,CAAE,SAAQ,WAAY,EAAmC,CAAI,EACnE,OAAO,EAAiB,EAAY,EAAQ,CAAO,CACrD,CASA,SAAgB,EACd,EACA,EACiD,CACjD,IAAM,EAAO,CAAE,GAAG,CAAI,EAEtB,OADA,OAAO,EAAK,UACL,CACL,UAAW,EAAmB,EAAY,EAAU,CAAI,CAAC,EACzD,GAAG,CACL,CACF,CAMA,SAAgB,EAAyB,EAA6C,CACpF,IAAM,EAA8B,CAAC,EACrC,IAAK,IAAM,KAAY,OAAO,KAAK,CAAK,EAClC,IAAa,iBAAgB,EAAS,GAAY,EAAM,IAE9D,OAAO,CACT,CAOA,SAAgB,EAAoB,EAA2D,CAC7F,IAAM,EAAQ,GAA0B,CACtC,IAAM,EAAQ,IAAQ,GACtB,OAAO,OAAO,GAAU,SAAW,EAAQ,EAC7C,EACA,MAAO,GAAG,EAAK,YAAY,EAAE,GAAG,EAAK,UAAU,EAAE,GAAG,EAAK,MAAM,EAAE,GAAG,EAAK,eAAe,GAC1F,CAUA,SAAgB,EAAuB,EAAgC,CACrE,IAAI,EAAS,IAAI,EAAU,GAAU,EACrC,GAAI,CACF,EAAS,IAAI,EAAU,CAAK,CAC9B,MAAQ,CAER,CACA,GAAI,CAAC,EAAO,SAAS,EACnB,MAAM,IAAI,EAAc,kBAAmB,CAAE,MAAO,OAAO,CAAK,CAAE,CAAC,EAErE,OAAO,EAAO,QAAQ,CACxB"}
1
+ {"version":3,"file":"chain_dto_signing.js","names":[],"sources":["../../../src/utils/chain_dto_signing.ts"],"sourcesContent":["/**\n * String-key signing entry points for apps that hold a private key only briefly (the Gala Wallet\n * extension reads its session key at sign time and must not keep it in a long-lived object).\n *\n * These are thin, byte-exact ports of the functions the wallet shipped in\n * `wallet_extension/src/helpers/wallet/signing.ts` (including the fail-closed personal-sign\n * payload resolver from its commit 34ca4e4), re-expressed over the SDK's own primitives. Every\n * function takes the key as a string (`0x`-optional hex, or base64) or as bytes, normalizes it per\n * call, and zeroes the normalized copy before returning, so nothing in this module retains a key.\n * A JavaScript string cannot be wiped; the caller owns the lifetime of the string it passes in.\n */\nimport BigNumber from 'bignumber.js';\nimport { GSwapSDKError } from '../classes/gswap_sdk_error.js';\nimport {\n normalizePrivateKey,\n serialize,\n signPersonalMessage,\n signSerializedNative,\n} from './signing.js';\n\n/** A secp256k1 private key as a hex/base64 string or as raw 32 bytes. */\nexport type PrivateKeyInput = string | Uint8Array;\n\n/**\n * Runs `action` with a private normalized copy of the key, zeroed afterwards (also when `action`\n * throws). Bytes given by the caller are copied, never modified: the caller owns them.\n */\nexport function withPrivateKey<T>(privateKey: PrivateKeyInput, action: (key: Uint8Array) => T): T {\n const key = normalizePrivateKey(privateKey);\n try {\n return action(key);\n } finally {\n key.fill(0);\n }\n}\n\n/**\n * GalaChain NATIVE signature over an already-serialized DTO: keccak256 of the exact bytes with no\n * prefix and no envelope (wallet `signChainDtoNative`). The caller owns serialization, so the\n * bytes a user reviewed and the bytes signed cannot diverge.\n *\n * @returns 130 lowercase hex characters, `r || s || v` with `v` = `1b`/`1c`, no `0x`.\n */\nexport function signChainDtoNative(privateKey: PrivateKeyInput, serializedDto: string): string {\n return withPrivateKey(privateKey, (key) => signSerializedNative(key, serializedDto));\n}\n\n/**\n * The personal-sign hash primitive (wallet `signChainDtoHash`): keccak256(`prefix` + `message`),\n * both verbatim, with no further EIP-191 wrap. `message` must be the DTO's serialization WITHOUT\n * its `prefix` field; callers holding a dApp-supplied string go through `signChainDtoPersonalSign`,\n * which derives both inputs from it.\n */\nexport function signChainDtoHash(\n privateKey: PrivateKeyInput,\n prefix: string,\n message: string,\n): string {\n return withPrivateKey(privateKey, (key) => signPersonalMessage(key, prefix, message));\n}\n\nconst PERSONAL_SIGN_HEADER = String.fromCharCode(0x19) + 'Ethereum Signed Message:\\n';\nconst PERSONAL_SIGN_LENGTH_PATTERN = /^(?:0|[1-9][0-9]*)$/u;\n\n// A header with a body starting `{` can never collide with another EIP-191 message's hash, and\n// anything that is not a header (arbitrary bytes the user cannot see on an approval card) is\n// refused. The number is NOT required to equal the byte length: the chain hashes whatever prefix\n// the submitted DTO carries, and defi-backend's bouncer rejects a mismatched header loudly.\nfunction isPersonalSignHeader(prefix: string): boolean {\n return (\n prefix.startsWith(PERSONAL_SIGN_HEADER) &&\n PERSONAL_SIGN_LENGTH_PATTERN.test(prefix.slice(PERSONAL_SIGN_HEADER.length))\n );\n}\n\n/**\n * Top-level fields that would make the chain (`getPayloadToSign`) or defi-backend's personal-sign\n * verifier hash something other than `prefix + serialize(dto minus prefix)`. An unsigned DTO from\n * a dApp never legitimately carries any of them, so they fail closed. Order is part of the error\n * message the wallet shows its user.\n */\nconst PERSONAL_SIGN_FORBIDDEN_FIELDS = [\n 'signature',\n 'multisig',\n 'trace',\n 'types',\n 'domain',\n 'stringsInstructions',\n] as const;\n\n/** What a `gala_signChainDto` personal-sign request hashes. */\nexport interface PersonalSignChainDtoPayload {\n /** The `prefix` the chain prepends: the DTO's own `prefix` field, or `''` when it has none. */\n prefix: string;\n /** The exact message hashed after `prefix`. */\n message: string;\n}\n\n/**\n * Derives what to hash for a `gala_signChainDto` personal-sign request from the exact string the\n * dApp sent (and the approval card displayed): wallet `resolvePersonalSignChainDtoPayload`.\n *\n * The dApp submits `{ ...dto, prefix, signature }`, which the chain recovers from\n * `keccak256(prefix + serialize(dto WITHOUT prefix))`. So when the string embeds a `prefix`\n * field, that field is removed and the remainder re-serialized; the prefix used is the dApp's own\n * value byte for byte. A string with no `prefix` field is hashed verbatim with an empty prefix\n * (chain-equivalent to a native signature of that exact string).\n *\n * Fails closed (throws `GSwapSDKError` `PERSONAL_SIGN_NOT_JSON`, `PERSONAL_SIGN_NOT_OBJECT`,\n * `PERSONAL_SIGN_BAD_PREFIX`, `PERSONAL_SIGN_ENVELOPE_FIELDS` or `PERSONAL_SIGN_NOT_CANONICAL`) on:\n * malformed JSON; JSON that is not a plain object; a `prefix` that is not an EIP-191\n * \"Ethereum Signed Message\" header; any signing-envelope field (`signature`, `multisig`, `trace`,\n * `types`, `domain`, `stringsInstructions`); and a string that is not already the canonical\n * serialization of what it parses to (duplicate keys, lossy numbers, key-order tricks the card\n * would render differently). Stripping `prefix` is therefore the ONLY byte change between the\n * reviewed string and the signed message.\n */\nexport function resolvePersonalSignChainDtoPayload(data: string): PersonalSignChainDtoPayload {\n let parsed: unknown;\n try {\n parsed = JSON.parse(data);\n } catch {\n throw new GSwapSDKError('PERSONAL_SIGN_NOT_JSON', {});\n }\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new GSwapSDKError('PERSONAL_SIGN_NOT_OBJECT', {});\n }\n const dto = parsed as Record<string, unknown>;\n if (!Object.prototype.hasOwnProperty.call(dto, 'prefix')) {\n return { prefix: '', message: data };\n }\n\n const { prefix, ...withoutPrefix } = dto;\n if (typeof prefix !== 'string' || !isPersonalSignHeader(prefix)) {\n throw new GSwapSDKError('PERSONAL_SIGN_BAD_PREFIX', {});\n }\n const forbidden = PERSONAL_SIGN_FORBIDDEN_FIELDS.filter((field) =>\n Object.prototype.hasOwnProperty.call(withoutPrefix, field),\n );\n if (forbidden.length > 0) {\n throw new GSwapSDKError('PERSONAL_SIGN_ENVELOPE_FIELDS', { fields: forbidden.join(', ') });\n }\n if (serialize(dto) !== data) {\n throw new GSwapSDKError('PERSONAL_SIGN_NOT_CANONICAL', {});\n }\n return { prefix, message: serialize(withoutPrefix) };\n}\n\n/**\n * Signs a GalaChain personal-sign scheme DTO from the exact string a dApp sent through\n * `gala_signChainDto` (wallet `signChainDtoPersonalSign`). See\n * `resolvePersonalSignChainDtoPayload` for what is signed and what is refused; the signature\n * recovers, under the chain's `getPayloadToSign`, to this key for the DTO the dApp submits with\n * its `prefix`, and equals a `personal_sign` of the bare body when that prefix is the EIP-191\n * header of the bare body's byte length.\n *\n * @throws GSwapSDKError (`PERSONAL_SIGN_*`, see `resolvePersonalSignChainDtoPayload`) before any\n * signing when the input is refused.\n */\nexport function signChainDtoPersonalSign(privateKey: PrivateKeyInput, data: string): string {\n const { prefix, message } = resolvePersonalSignChainDtoPayload(data);\n return signChainDtoHash(privateKey, prefix, message);\n}\n\n/**\n * Raw object signing (wallet `signObject`), used for transfers, bridge and fee-dry-run DTOs the\n * wallet signs itself. Removes ONLY `signature`, signs `keccak256(serialize(rest))` natively\n * (no `prefix` handling, and `trace` / `multisig` stay in the signed bytes, unlike\n * `signNative`), and returns `{ signature, ...rest }` with the signature as the FIRST key, exactly\n * like the wallet. Callers that must not sign extra fields strip them first.\n */\nexport function signRawObject(\n privateKey: PrivateKeyInput,\n obj: Record<string, unknown>,\n): { signature: string } & Record<string, unknown> {\n const rest = { ...obj };\n delete rest['signature'];\n return {\n signature: signChainDtoNative(privateKey, serialize(rest)),\n ...rest,\n };\n}\n\n/**\n * Drops the `EIP712Domain` entry from a dApp-supplied types map (a typed-data encoder derives the\n * domain type itself and rejects an explicit one). The order of the remaining types is preserved.\n */\nexport function stripEip712DomainType<T>(types: Record<string, T>): Record<string, T> {\n const stripped: Record<string, T> = {};\n for (const typeName of Object.keys(types)) {\n if (typeName !== 'EIP712Domain') stripped[typeName] = types[typeName] as T;\n }\n return stripped;\n}\n\n/**\n * `collection$category$type$additionalKey` for a token-class object (wallet `decodeTokenData`).\n * Lenient on purpose: any missing or non-string part becomes `''`, so a display layer never\n * throws on odd input.\n */\nexport function tokenClassDollarKey(token: Record<string, unknown> | null | undefined): string {\n const part = (field: string): string => {\n const value = token?.[field];\n return typeof value === 'string' ? value : '';\n };\n return `${part('collection')}$${part('category')}$${part('type')}$${part('additionalKey')}`;\n}\n\n/**\n * A quantity in the form GalaChain's `IsBigNumber` validator accepts: FIXED notation, no leading\n * zeros, no trailing decimal zeros (`BigNumber#toFixed()`; never `toString()`, which turns\n * `0.00000001` into `1e-8`, and never a JS number in between).\n *\n * @throws GSwapSDKError (`INVALID_DECIMAL`) for a value that is not a finite number. The wallet's\n * `cleanNumber` returned the string `'NaN'` there; signing that text can only fail on chain.\n */\nexport function canonicalDecimalString(value: string | number): string {\n let parsed = new BigNumber(Number.NaN);\n try {\n parsed = new BigNumber(value);\n } catch {\n // BigNumber throws instead of returning NaN when its DEBUG flag is on; both mean \"invalid\".\n }\n if (!parsed.isFinite()) {\n throw new GSwapSDKError('INVALID_DECIMAL', { value: String(value) });\n }\n return parsed.toFixed();\n}\n"],"mappings":"+MA2BA,SAAgB,EAAkB,EAA6B,EAAmC,CAChG,IAAM,EAAM,EAAoB,CAAU,EAC1C,GAAI,CACF,OAAO,EAAO,CAAG,CACnB,QAAU,CACR,EAAI,KAAK,CAAC,CACZ,CACF,CASA,SAAgB,EAAmB,EAA6B,EAA+B,CAC7F,OAAO,EAAe,EAAa,GAAQ,EAAqB,EAAK,CAAa,CAAC,CACrF,CAQA,SAAgB,EACd,EACA,EACA,EACQ,CACR,OAAO,EAAe,EAAa,GAAQ,EAAoB,EAAK,EAAQ,CAAO,CAAC,CACtF,CAEA,MACM,EAA+B,uBAMrC,SAAS,EAAqB,EAAyB,CACrD,OACE,EAAO,WAAW;CAAoB,GACtC,EAA6B,KAAK,EAAO,MAAM,EAA2B,CAAC,CAE/E,CAQA,MAAM,EAAiC,CACrC,YACA,WACA,QACA,QACA,SACA,qBACF,EA6BA,SAAgB,EAAmC,EAA2C,CAC5F,IAAI,EACJ,GAAI,CACF,EAAS,KAAK,MAAM,CAAI,CAC1B,MAAQ,CACN,MAAM,IAAI,EAAc,yBAA0B,CAAC,CAAC,CACtD,CACA,GAAuB,OAAO,GAAW,WAArC,GAAiD,MAAM,QAAQ,CAAM,EACvE,MAAM,IAAI,EAAc,2BAA4B,CAAC,CAAC,EAExD,IAAM,EAAM,EACZ,GAAI,CAAC,OAAO,UAAU,eAAe,KAAK,EAAK,QAAQ,EACrD,MAAO,CAAE,OAAQ,GAAI,QAAS,CAAK,EAGrC,GAAM,CAAE,SAAQ,GAAG,GAAkB,EACrC,GAAI,OAAO,GAAW,UAAY,CAAC,EAAqB,CAAM,EAC5D,MAAM,IAAI,EAAc,2BAA4B,CAAC,CAAC,EAExD,IAAM,EAAY,EAA+B,OAAQ,GACvD,OAAO,UAAU,eAAe,KAAK,EAAe,CAAK,CAC3D,EACA,GAAI,EAAU,OAAS,EACrB,MAAM,IAAI,EAAc,gCAAiC,CAAE,OAAQ,EAAU,KAAK,IAAI,CAAE,CAAC,EAE3F,GAAI,EAAU,CAAG,IAAM,EACrB,MAAM,IAAI,EAAc,8BAA+B,CAAC,CAAC,EAE3D,MAAO,CAAE,SAAQ,QAAS,EAAU,CAAa,CAAE,CACrD,CAaA,SAAgB,EAAyB,EAA6B,EAAsB,CAC1F,GAAM,CAAE,SAAQ,WAAY,EAAmC,CAAI,EACnE,OAAO,EAAiB,EAAY,EAAQ,CAAO,CACrD,CASA,SAAgB,EACd,EACA,EACiD,CACjD,IAAM,EAAO,CAAE,GAAG,CAAI,EAEtB,OADA,OAAO,EAAK,UACL,CACL,UAAW,EAAmB,EAAY,EAAU,CAAI,CAAC,EACzD,GAAG,CACL,CACF,CAMA,SAAgB,EAAyB,EAA6C,CACpF,IAAM,EAA8B,CAAC,EACrC,IAAK,IAAM,KAAY,OAAO,KAAK,CAAK,EAClC,IAAa,iBAAgB,EAAS,GAAY,EAAM,IAE9D,OAAO,CACT,CAOA,SAAgB,EAAoB,EAA2D,CAC7F,IAAM,EAAQ,GAA0B,CACtC,IAAM,EAAQ,IAAQ,GACtB,OAAO,OAAO,GAAU,SAAW,EAAQ,EAC7C,EACA,MAAO,GAAG,EAAK,YAAY,EAAE,GAAG,EAAK,UAAU,EAAE,GAAG,EAAK,MAAM,EAAE,GAAG,EAAK,eAAe,GAC1F,CAUA,SAAgB,EAAuB,EAAgC,CACrE,IAAI,EAAS,IAAI,EAAU,GAAU,EACrC,GAAI,CACF,EAAS,IAAI,EAAU,CAAK,CAC9B,MAAQ,CAER,CACA,GAAI,CAAC,EAAO,SAAS,EACnB,MAAM,IAAI,EAAc,kBAAmB,CAAE,MAAO,OAAO,CAAK,CAAE,CAAC,EAErE,OAAO,EAAO,QAAQ,CACxB"}
@@ -1 +1 @@
1
- {"version":3,"file":"classification.js","names":[],"sources":["../../../src/utils/classification.ts"],"sourcesContent":["import type { FailureOutcome } from './failure_outcomes.js';\n\n/**\n * The classification values an app shell may need on their own: what an outcome allows. This module\n * is deliberately tiny and imports nothing at run time (the outcome type is erased), so an app\n * shell that builds `classification('not_sent')` or reads `NOT_QUEUED_FINAL` loads a few hundred\n * bytes, not the table of error codes that lives with `classifyFailure`.\n */\n\n/** The result of `classifyFailure`. */\nexport interface FailureClassification {\n readonly outcome: FailureOutcome;\n /**\n * May the app offer the user Retry: repeat the operation as a NEW request (a fresh key for a\n * write) without any risk of applying it twice? Always `false` for `unknown` (confirm the same\n * key instead) and `invalid_input` (the same call fails again). It says a repeat is SAFE, not\n * that it will succeed, and it is the SDK's floor: an app may still withhold Retry (the\n * wallet and frontend_v2 do after a Dark swap: see `isShadowError`).\n */\n readonly retryable: boolean;\n}\n\n/**\n * The SDK's default Retry answer for each outcome. A decision that departs from it (a refusal the\n * gateway will repeat, `SHADOW_NOT_QUEUED`) is written out in the table of `classifyFailure`.\n */\nconst DEFAULT_RETRYABLE = {\n rejected: true,\n unknown: false,\n not_queued: true,\n not_sent: true,\n invalid_input: false,\n transient: true,\n} as const satisfies Record<FailureOutcome, boolean>;\n\n/**\n * Builds the classification the SDK itself gives an outcome, so an app that classifies a step of\n * its OWN (one that is not an SDK call) uses the SDK's type and its values instead of a\n * hard-coded `{ outcome, retryable }` literal. `retryable` is the SDK's default for that outcome\n * (`false` for `unknown` and `invalid_input`, `true` for the rest). Each call returns a new\n * object, safe to mutate or freeze.\n *\n * `options.retryable: false` WITHHOLDS Retry from an outcome that would otherwise allow it: the\n * SDK's own `not_queued` for a refusal the gateway will repeat is `classification('not_queued',\n * { retryable: false })` (also exported, frozen, as {@link NOT_QUEUED_FINAL}). An app may take\n * Retry away from an outcome but never grant it to one that cannot have it, so only `false` is\n * accepted.\n *\n * @example\n * ```ts\n * // The app refreshes a quote before it asks the SDK to sign; the refresh failed.\n * throw new AppSwapError(text, classification('not_sent'));\n * // The app refuses a request for a reason a repeat cannot fix.\n * throw new AppSwapError(text, classification('not_queued', { retryable: false }));\n * ```\n */\nexport function classification(\n outcome: FailureOutcome,\n options: { readonly retryable?: false } = {},\n): FailureClassification {\n return { outcome, retryable: options.retryable === false ? false : DEFAULT_RETRYABLE[outcome] };\n}\n\n/**\n * `classification('not_queued', { retryable: false })`: the gateway refused the write before\n * queueing it AND will refuse a repeat the same way (the version wall, a dark swap that did not\n * queue, an HTTP 4xx). Nothing is pending and Retry is not offered. It is frozen: use it as it is\n * (`classification: NOT_QUEUED_FINAL`) instead of spreading `classification('not_queued')` and\n * overriding `retryable`; `{ ...NOT_QUEUED_FINAL }` is a mutable copy.\n */\nexport const NOT_QUEUED_FINAL: Readonly<FailureClassification> = Object.freeze(\n classification('not_queued', { retryable: false }),\n);\n\n/**\n * `classification('not_sent')`: a step of the app's own failed before anything was signed or\n * sent (a quote refresh, a balance read), so nothing is pending and a repeat is safe.\n */\nexport function notSentClassification(): FailureClassification {\n return classification('not_sent');\n}\n"],"mappings":"AA0BA,MAAM,EAAoB,CACxB,SAAU,GACV,QAAS,GACT,WAAY,GACZ,SAAU,GACV,cAAe,GACf,UAAW,EACb,EAuBA,SAAgB,EACd,EACA,EAA0C,CAAC,EACpB,CACvB,MAAO,CAAE,UAAS,UAAW,EAAQ,YAAc,IAAgB,EAAkB,EAAS,CAChG,CASA,MAAa,EAAoD,OAAO,OACtE,EAAe,aAAc,CAAE,UAAW,EAAM,CAAC,CACnD,EAMA,SAAgB,GAA+C,CAC7D,OAAO,EAAe,UAAU,CAClC"}
1
+ {"version":3,"file":"classification.js","names":[],"sources":["../../../src/utils/classification.ts"],"sourcesContent":["import type { FailureOutcome } from './failure_outcomes.js';\n\n/**\n * The classification values an app shell may need on their own: what an outcome allows. This module\n * is deliberately tiny and imports nothing at run time (the outcome type is erased), so an app\n * shell that builds `classification('not_sent')` or reads `NOT_QUEUED_FINAL` loads a few hundred\n * bytes, not the table of error codes that lives with `classifyFailure`.\n */\n\n/** The result of `classifyFailure`. */\nexport interface FailureClassification {\n readonly outcome: FailureOutcome;\n /**\n * May the app offer the user Retry: repeat the operation as a NEW request (a fresh key for a\n * write) without any risk of applying it twice? Always `false` for `unknown` (confirm the same\n * key instead) and `invalid_input` (the same call fails again). It says a repeat is SAFE, not\n * that it will succeed, and it is the SDK's floor: an app may still withhold Retry (the\n * wallet and frontend_v2 do after a Dark swap: see `isShadowError`).\n */\n readonly retryable: boolean;\n}\n\n/**\n * The SDK's default Retry answer for each outcome. A decision that departs from it (a refusal the\n * gateway will repeat, `SHADOW_NOT_QUEUED`) is written out in the table of `classifyFailure`.\n */\nconst DEFAULT_RETRYABLE = {\n rejected: true,\n unknown: false,\n not_queued: true,\n not_sent: true,\n invalid_input: false,\n transient: true,\n} as const satisfies Record<FailureOutcome, boolean>;\n\n/**\n * Builds the classification the SDK itself gives an outcome, so an app that classifies a step of\n * its OWN (one that is not an SDK call) uses the SDK's type and its values instead of a\n * hard-coded `{ outcome, retryable }` literal. `retryable` is the SDK's default for that outcome\n * (`false` for `unknown` and `invalid_input`, `true` for the rest). Each call returns a new\n * object, safe to mutate or freeze.\n *\n * `options.retryable: false` WITHHOLDS Retry from an outcome that would otherwise allow it: the\n * SDK's own `not_queued` for a refusal the gateway will repeat is\n * `classification('not_queued', { retryable: false })` (also exported, frozen, as\n * {@link NOT_QUEUED_FINAL}). An app may take Retry away from an outcome but never grant it to one\n * that cannot have it, so only `false` is accepted.\n *\n * @example\n * ```ts\n * // The app refreshes a quote before it asks the SDK to sign; the refresh failed.\n * throw new AppSwapError(text, classification('not_sent'));\n * // The app refuses a request for a reason a repeat cannot fix.\n * throw new AppSwapError(text, classification('not_queued', { retryable: false }));\n * ```\n */\nexport function classification(\n outcome: FailureOutcome,\n options: { readonly retryable?: false } = {},\n): FailureClassification {\n return { outcome, retryable: options.retryable === false ? false : DEFAULT_RETRYABLE[outcome] };\n}\n\n/**\n * `classification('not_queued', { retryable: false })`: the gateway refused the write before\n * queueing it AND will refuse a repeat the same way (the version wall, a dark swap that did not\n * queue, an HTTP 4xx). Nothing is pending and Retry is not offered. It is frozen: use it as it is\n * (`classification: NOT_QUEUED_FINAL`) instead of spreading `classification('not_queued')` and\n * overriding `retryable`; `{ ...NOT_QUEUED_FINAL }` is a mutable copy.\n */\nexport const NOT_QUEUED_FINAL: Readonly<FailureClassification> = Object.freeze(\n classification('not_queued', { retryable: false }),\n);\n\n/**\n * `classification('not_sent')`: a step of the app's own failed before anything was signed or\n * sent (a quote refresh, a balance read), so nothing is pending and a repeat is safe.\n */\nexport function notSentClassification(): FailureClassification {\n return classification('not_sent');\n}\n"],"mappings":"AA0BA,MAAM,EAAoB,CACxB,SAAU,GACV,QAAS,GACT,WAAY,GACZ,SAAU,GACV,cAAe,GACf,UAAW,EACb,EAuBA,SAAgB,EACd,EACA,EAA0C,CAAC,EACpB,CACvB,MAAO,CAAE,UAAS,UAAW,EAAQ,YAAc,IAAgB,EAAkB,EAAS,CAChG,CASA,MAAa,EAAoD,OAAO,OACtE,EAAe,aAAc,CAAE,UAAW,EAAM,CAAC,CACnD,EAMA,SAAgB,GAA+C,CAC7D,OAAO,EAAe,UAAU,CAClC"}
@@ -1 +1 @@
1
- {"version":3,"file":"dto_builders.js","names":[],"sources":["../../../src/utils/dto_builders.ts"],"sourcesContent":["import BigNumber from 'bignumber.js';\nimport { GSwapSDKError } from '../classes/gswap_sdk_error.js';\nimport type { NumericAmount, SwapAmount } from '../types/amounts.js';\nimport type {\n AddLiquidityDTO,\n CollectPositionFeesDTO,\n CreatePoolDTO,\n RemoveLiquidityDTO,\n RequestTradeDTO,\n} from '../types/v2_dtos.js';\nimport type { GalaChainTokenClassKey } from '../types/token.js';\nimport { assertTickRange } from './ticks.js';\nimport { assertUniqueKey } from './unique_key.js';\nimport {\n assertCanonicalPair,\n isProposableTradingSymbol,\n validateFee,\n validateNumericAmount,\n} from './validation.js';\n\n// The pure builders of every v2 write DTO. They validate, canonicalize decimals\n// (`BigNumber#toFixed`) and fix the key order, and nothing else: no network, no signer, no\n// clock, no randomness — the `uniqueKey` is an input. The `Swaps` and `Positions` services call\n// exactly these functions, so a DTO an app builds here (to show in an approval popup, to sign on\n// another device, to submit later) is the DTO the services would have built.\n//\n// Inputs are already CANONICAL: `token0 < token1` by plain string comparison, ticks and prices in\n// that orientation. Orienting a caller's pair (and inverting its ticks and price) is the\n// services' job; a reversed pair is refused (`NON_CANONICAL_PAIR`) rather than silently signed.\n\nconst decimalString = (amount: NumericAmount): string => new BigNumber(amount).toFixed();\n\n/** The pool a RequestTrade trades in, its fee tier, the `uniqueKey` and what is traded. */\nexport type RequestTradeInput = {\n /** Canonical pool symbols: `token0 < token1`. Take them from a quote verbatim. */\n token0: string;\n token1: string;\n fee: number;\n /** Which side of the pool is sold: `true` when the input token is `token0`. */\n tokenInIsToken0: boolean;\n uniqueKey: string;\n} & SwapAmount;\n\n/**\n * Builds the RequestTrade DTO: `exactIn` sells that much of the input token (`sell0Qty` or\n * `sell1Qty`), `exactOut` buys that much of the output token (`buy1Qty` or `buy0Qty`), plus the\n * side's slippage bound and an optional `priceLimit`. Key order: `token0, token1, fee, uniqueKey,\n * <quantity>, amountOutMinimum | amountInMaximum, priceLimit`.\n *\n * @example\n * ```ts\n * const dto = buildRequestTradeDto({\n * token0: quote.token0Symbol, token1: quote.token1Symbol,\n * tokenInIsToken0: quote.tokenInIsToken0, fee: quote.fee,\n * uniqueKey: createUniqueKey('galaswap-operation-'),\n * exactIn: '100', amountOutMinimum: '14',\n * });\n * ```\n */\nexport function buildRequestTradeDto(input: RequestTradeInput): RequestTradeDTO {\n validateFee(input.fee);\n assertCanonicalPair(input.token0, input.token1);\n assertUniqueKey(input.uniqueKey);\n const dto: Record<string, unknown> = {\n token0: input.token0,\n token1: input.token1,\n fee: input.fee,\n uniqueKey: input.uniqueKey,\n };\n if ('exactIn' in input) {\n const exactIn = input.exactIn;\n if (exactIn === undefined) {\n throw new GSwapSDKError('EXACT_AMOUNT_REQUIRED', { field: 'exactIn' });\n }\n validateNumericAmount(exactIn, 'exactIn');\n dto[input.tokenInIsToken0 ? 'sell0Qty' : 'sell1Qty'] = decimalString(exactIn);\n if (input.amountOutMinimum !== undefined) {\n validateNumericAmount(input.amountOutMinimum, 'amountOutMinimum', true);\n dto['amountOutMinimum'] = decimalString(input.amountOutMinimum);\n }\n } else {\n const exactOut = input.exactOut;\n if (exactOut === undefined) {\n throw new GSwapSDKError('EXACT_AMOUNT_REQUIRED', { field: 'exactOut' });\n }\n validateNumericAmount(exactOut, 'exactOut');\n dto[input.tokenInIsToken0 ? 'buy1Qty' : 'buy0Qty'] = decimalString(exactOut);\n if (input.amountInMaximum !== undefined) {\n validateNumericAmount(input.amountInMaximum, 'amountInMaximum', true);\n dto['amountInMaximum'] = decimalString(input.amountInMaximum);\n }\n }\n if (input.priceLimit !== undefined) {\n validateNumericAmount(input.priceLimit, 'priceLimit');\n dto['priceLimit'] = decimalString(input.priceLimit);\n }\n return dto as unknown as RequestTradeDTO;\n}\n\n/** A canonical pool position: the pair, fee tier, tick range and the attempt's `uniqueKey`. */\nexport interface PoolPositionInput {\n token0: string;\n token1: string;\n fee: number;\n tickLower: number;\n tickUpper: number;\n uniqueKey: string;\n}\n\nfunction assertPoolPosition(input: PoolPositionInput): void {\n validateFee(input.fee);\n assertCanonicalPair(input.token0, input.token1);\n assertTickRange(input.tickLower, input.tickUpper, input.fee);\n assertUniqueKey(input.uniqueKey);\n}\n\n/** Exactly one canonical deposit side. */\nexport type AddLiquidityInput = PoolPositionInput &\n (\n | { depositQuantityToken0: NumericAmount; depositQuantityToken1?: undefined }\n | { depositQuantityToken0?: undefined; depositQuantityToken1: NumericAmount }\n );\n\n/**\n * Builds the AddLiquidity DTO: exactly one of `depositQuantityToken0` / `depositQuantityToken1`\n * (the other side is derived on chain). Key order: `token0, token1, fee, tickLower, tickUpper,\n * uniqueKey, <deposit>`.\n */\nexport function buildAddLiquidityDto(input: AddLiquidityInput): AddLiquidityDTO {\n assertPoolPosition(input);\n const side = onlyOne(\n input.depositQuantityToken0,\n input.depositQuantityToken1,\n 'depositQuantityToken0',\n 'depositQuantityToken1',\n );\n if (side === undefined) {\n throw new GSwapSDKError('EXACTLY_ONE_OF_REQUIRED', {\n first: 'depositQuantityToken0',\n second: 'depositQuantityToken1',\n });\n }\n validateNumericAmount(side.amount, side.name);\n return {\n token0: input.token0,\n token1: input.token1,\n fee: input.fee,\n tickLower: input.tickLower,\n tickUpper: input.tickUpper,\n uniqueKey: input.uniqueKey,\n [side.name]: decimalString(side.amount),\n } as AddLiquidityDTO;\n}\n\n/** At most one canonical withdrawal side; neither closes the position. */\nexport type RemoveLiquidityInput = PoolPositionInput & {\n withdrawalQuantityToken0?: NumericAmount | undefined;\n withdrawalQuantityToken1?: NumericAmount | undefined;\n};\n\n/**\n * Builds the RemoveLiquidity DTO: at most one of `withdrawalQuantityToken0` /\n * `withdrawalQuantityToken1`; omitting both closes the position and sweeps its fees.\n */\nexport function buildRemoveLiquidityDto(input: RemoveLiquidityInput): RemoveLiquidityDTO {\n assertPoolPosition(input);\n const side = onlyOne(\n input.withdrawalQuantityToken0,\n input.withdrawalQuantityToken1,\n 'withdrawalQuantityToken0',\n 'withdrawalQuantityToken1',\n true,\n );\n if (side !== undefined) validateNumericAmount(side.amount, side.name);\n return {\n token0: input.token0,\n token1: input.token1,\n fee: input.fee,\n tickLower: input.tickLower,\n tickUpper: input.tickUpper,\n uniqueKey: input.uniqueKey,\n ...(side === undefined ? {} : { [side.name]: decimalString(side.amount) }),\n };\n}\n\n/** Builds the CollectPositionFees DTO (the contract sweeps all of a position's fees). */\nexport function buildCollectPositionFeesDto(input: PoolPositionInput): CollectPositionFeesDTO {\n assertPoolPosition(input);\n return {\n token0: input.token0,\n token1: input.token1,\n fee: input.fee,\n tickLower: input.tickLower,\n tickUpper: input.tickUpper,\n uniqueKey: input.uniqueKey,\n };\n}\n\n/** A CreatePool in canonical orientation, with exactly one starting price. */\nexport type CreatePoolInput = {\n token0Key: GalaChainTokenClassKey;\n token1Key: GalaChainTokenClassKey;\n /** The symbols to register; `token0Symbol < token1Symbol` and both proposable. */\n token0Symbol: string;\n token1Symbol: string;\n fee: number;\n /** Optional display name; omitted when undefined or empty. */\n name?: string | undefined;\n isPrivate?: boolean | undefined;\n privateAccess?: string[] | undefined;\n uniqueKey: string;\n} & (\n | { startingPrice: NumericAmount; startingSqrtPrice?: undefined }\n | { startingPrice?: undefined; startingSqrtPrice: NumericAmount }\n);\n\n/**\n * Builds the CreatePool DTO. Key order: `token0Key, token1Key, fee, startingPrice |\n * startingSqrtPrice, token0Symbol, token1Symbol, uniqueKey, name, isPrivate, privateAccess`.\n * Prices are those of the canonical pair (token1 per token0); a price quoted the other way round\n * is inverted by the caller before it gets here.\n */\nexport function buildCreatePoolDto(input: CreatePoolInput): CreatePoolDTO {\n validateFee(input.fee);\n assertUniqueKey(input.uniqueKey);\n assertCanonicalPair(input.token0Symbol, input.token1Symbol);\n for (const symbol of [input.token0Symbol, input.token1Symbol]) {\n if (!isProposableTradingSymbol(symbol)) {\n throw new GSwapSDKError('INVALID_TRADING_SYMBOL', { symbol });\n }\n }\n for (const key of [input.token0Key, input.token1Key]) assertClassKey(key);\n const price = onlyOne(\n input.startingPrice,\n input.startingSqrtPrice,\n 'startingPrice',\n 'startingSqrtPrice',\n );\n if (price === undefined) {\n throw new GSwapSDKError('EXACTLY_ONE_OF_REQUIRED', {\n first: 'startingPrice',\n second: 'startingSqrtPrice',\n });\n }\n if (typeof price.amount === 'number') {\n throw new GSwapSDKError('AMOUNT_IS_JS_NUMBER', { field: price.name });\n }\n const parsed = new BigNumber(price.amount);\n if (!parsed.isFinite() || parsed.isLessThanOrEqualTo(0)) {\n throw new GSwapSDKError('INVALID_STARTING_PRICE', { value: String(price.amount) });\n }\n return {\n token0Key: input.token0Key,\n token1Key: input.token1Key,\n fee: input.fee,\n [price.name]: parsed.toFixed(),\n token0Symbol: input.token0Symbol,\n token1Symbol: input.token1Symbol,\n uniqueKey: input.uniqueKey,\n // An empty name is \"no name\", as in frontend_v2's builder (its form yields '').\n ...(input.name === undefined || input.name === '' ? {} : { name: input.name }),\n ...(input.isPrivate === undefined ? {} : { isPrivate: input.isPrivate }),\n ...(input.privateAccess === undefined ? {} : { privateAccess: input.privateAccess }),\n } as CreatePoolDTO;\n}\n\nfunction assertClassKey(key: GalaChainTokenClassKey): void {\n for (const part of ['collection', 'category', 'type', 'additionalKey'] as const) {\n if (typeof key[part] !== 'string' || key[part].length === 0) {\n throw new GSwapSDKError('INCOMPLETE_TOKEN_CLASS_KEY', {\n compositeKey: [key.collection, key.category, key.type, key.additionalKey].join('$'),\n part,\n });\n }\n }\n}\n\n/**\n * Of two mutually exclusive fields, the one that is set (`undefined` when neither is);\n * both set is an error (`AT_MOST_ONE_OF_ALLOWED` or `EXACTLY_ONE_OF_REQUIRED`), and so is\n * neither unless `allowNeither`.\n */\nfunction onlyOne<T>(\n first: T | undefined,\n second: T | undefined,\n firstName: string,\n secondName: string,\n allowNeither = false,\n): { name: string; amount: T } | undefined {\n if (first !== undefined && second !== undefined) {\n throw allowNeither\n ? new GSwapSDKError('AT_MOST_ONE_OF_ALLOWED', { first: firstName, second: secondName })\n : new GSwapSDKError('EXACTLY_ONE_OF_REQUIRED', { first: firstName, second: secondName });\n }\n if (first !== undefined) return { name: firstName, amount: first };\n if (second !== undefined) return { name: secondName, amount: second };\n return undefined;\n}\n\n/** The methods whose DTOs the builders produce. */\nexport type V2WriteMethod =\n 'RequestTrade' | 'AddLiquidity' | 'RemoveLiquidity' | 'CollectPositionFees' | 'CreatePool';\n\ntype DtoExpectation =\n 'string' | 'number' | 'boolean' | 'class_key' | 'string_array' | 'exactly_once';\n\nfunction invalidDto(\n method: string,\n field: string,\n expected: DtoExpectation,\n): GSwapSDKError<'INVALID_DTO'> {\n return new GSwapSDKError('INVALID_DTO', { method, field, expected });\n}\n\n/**\n * Rebuilds a DTO from its own fields with the builder of its method: the canonical form of the\n * DTO, with the same validation. Key order and decimal text are the builder's.\n */\nfunction rebuildV2Dto(\n method: V2WriteMethod,\n dto: Record<string, unknown>,\n): Record<string, unknown> {\n const text = (field: string): string => {\n const value = dto[field];\n if (typeof value !== 'string') throw invalidDto(method, field, 'string');\n return value;\n };\n const integer = (field: string): number => {\n const value = dto[field];\n if (typeof value !== 'number') throw invalidDto(method, field, 'number');\n return value;\n };\n const optionalText = (field: string): string | undefined =>\n dto[field] === undefined ? undefined : text(field);\n const classKey = (field: string): GalaChainTokenClassKey => {\n const value = dto[field];\n if (typeof value !== 'object' || value === null) throw invalidDto(method, field, 'class_key');\n const key = value as Record<string, unknown>;\n const part = (name: string): string => {\n if (typeof key[name] !== 'string') throw invalidDto(method, `${field}.${name}`, 'string');\n return key[name];\n };\n return {\n collection: part('collection'),\n category: part('category'),\n type: part('type'),\n additionalKey: part('additionalKey'),\n };\n };\n // The pool pair is not a field of CreatePool, so these are read per method, not up front.\n const base = () => ({ token0: text('token0'), token1: text('token1'), fee: integer('fee') });\n const uniqueKey = text('uniqueKey');\n const position = () => ({\n ...base(),\n uniqueKey,\n tickLower: integer('tickLower'),\n tickUpper: integer('tickUpper'),\n });\n switch (method) {\n case 'RequestTrade': {\n const qty = ['sell0Qty', 'sell1Qty', 'buy0Qty', 'buy1Qty'].filter(\n (f) => dto[f] !== undefined,\n );\n const [field] = qty;\n if (qty.length !== 1 || field === undefined) {\n throw invalidDto(method, 'sell0Qty|sell1Qty|buy0Qty|buy1Qty', 'exactly_once');\n }\n const tokenInIsToken0 = field === 'sell0Qty' || field === 'buy1Qty';\n const shared = { ...base(), uniqueKey, tokenInIsToken0 };\n const priceLimit = optionalText('priceLimit');\n return field.startsWith('sell')\n ? buildRequestTradeDto({\n ...shared,\n exactIn: text(field),\n amountOutMinimum: optionalText('amountOutMinimum'),\n priceLimit,\n })\n : buildRequestTradeDto({\n ...shared,\n exactOut: text(field),\n amountInMaximum: optionalText('amountInMaximum'),\n priceLimit,\n });\n }\n case 'AddLiquidity': {\n const token0Side = optionalText('depositQuantityToken0');\n const token1Side = optionalText('depositQuantityToken1');\n return buildAddLiquidityDto({\n ...position(),\n ...(token0Side === undefined\n ? { depositQuantityToken1: token1Side as string }\n : { depositQuantityToken0: token0Side }),\n });\n }\n case 'RemoveLiquidity':\n return buildRemoveLiquidityDto({\n ...position(),\n withdrawalQuantityToken0: optionalText('withdrawalQuantityToken0'),\n withdrawalQuantityToken1: optionalText('withdrawalQuantityToken1'),\n });\n case 'CollectPositionFees':\n return buildCollectPositionFeesDto(position());\n case 'CreatePool': {\n const price = optionalText('startingPrice');\n const sqrtPrice = optionalText('startingSqrtPrice');\n const privateAccess = dto['privateAccess'];\n if (\n privateAccess !== undefined &&\n (!Array.isArray(privateAccess) || privateAccess.some((entry) => typeof entry !== 'string'))\n ) {\n throw invalidDto(method, 'privateAccess', 'string_array');\n }\n if (dto['isPrivate'] !== undefined && typeof dto['isPrivate'] !== 'boolean') {\n throw invalidDto(method, 'isPrivate', 'boolean');\n }\n return buildCreatePoolDto({\n token0Key: classKey('token0Key'),\n token1Key: classKey('token1Key'),\n token0Symbol: text('token0Symbol'),\n token1Symbol: text('token1Symbol'),\n fee: integer('fee'),\n uniqueKey,\n name: optionalText('name'),\n isPrivate: dto['isPrivate'],\n privateAccess: privateAccess as string[] | undefined,\n ...(price === undefined\n ? { startingSqrtPrice: sqrtPrice as string }\n : { startingPrice: price }),\n });\n }\n }\n}\n\n/**\n * Asserts that `dto` is EXACTLY what the builder of `method` produces for its own fields — the\n * same fields, canonical decimal text, builder key order, nothing extra (no `signature`). The\n * `submit*` methods use it so that what an app showed a user (a DTO it built) is byte for byte\n * what gets signed; a DTO edited by hand, or built elsewhere, is refused rather than signed.\n *\n * @throws GSwapSDKError `INVALID_DTO` or `DTO_NOT_CANONICAL`.\n */\nexport function assertCanonicalV2Dto(method: V2WriteMethod, dto: Record<string, unknown>): void {\n const rebuilt = rebuildV2Dto(method, dto);\n if (JSON.stringify(rebuilt) !== JSON.stringify(dto)) {\n throw new GSwapSDKError('DTO_NOT_CANONICAL', { method });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"dto_builders.js","names":[],"sources":["../../../src/utils/dto_builders.ts"],"sourcesContent":["import BigNumber from 'bignumber.js';\nimport { GSwapSDKError } from '../classes/gswap_sdk_error.js';\nimport type { NumericAmount, SwapAmount } from '../types/amounts.js';\nimport type {\n AddLiquidityDTO,\n CollectPositionFeesDTO,\n CreatePoolDTO,\n RemoveLiquidityDTO,\n RequestTradeDTO,\n} from '../types/v2_dtos.js';\nimport type { GalaChainTokenClassKey } from '../types/token.js';\nimport { assertTickRange } from './ticks.js';\nimport { assertUniqueKey } from './unique_key.js';\nimport {\n assertCanonicalPair,\n isProposableTradingSymbol,\n validateFee,\n validateNumericAmount,\n} from './validation.js';\n\n// The pure builders of every v2 write DTO. They validate, canonicalize decimals\n// (`BigNumber#toFixed`) and fix the key order, and nothing else: no network, no signer, no\n// clock, no randomness — the `uniqueKey` is an input. The `Swaps` and `Positions` services call\n// exactly these functions, so a DTO an app builds here (to show in an approval popup, to sign on\n// another device, to submit later) is the DTO the services would have built.\n//\n// Inputs are already CANONICAL: `token0 < token1` by plain string comparison, ticks and prices in\n// that orientation. Orienting a caller's pair (and inverting its ticks and price) is the\n// services' job; a reversed pair is refused (`NON_CANONICAL_PAIR`) rather than silently signed.\n\nconst decimalString = (amount: NumericAmount): string => new BigNumber(amount).toFixed();\n\n/** The pool a RequestTrade trades in, its fee tier, the `uniqueKey` and what is traded. */\nexport type RequestTradeInput = {\n /** Canonical pool symbols: `token0 < token1`. Take them from a quote verbatim. */\n token0: string;\n token1: string;\n fee: number;\n /** Which side of the pool is sold: `true` when the input token is `token0`. */\n tokenInIsToken0: boolean;\n uniqueKey: string;\n} & SwapAmount;\n\n/**\n * Builds the RequestTrade DTO: `exactIn` sells that much of the input token (`sell0Qty` or\n * `sell1Qty`), `exactOut` buys that much of the output token (`buy1Qty` or `buy0Qty`), plus the\n * side's slippage bound and an optional `priceLimit`. Key order is\n * `token0, token1, fee, uniqueKey`, then the one quantity, then the slippage bound, then\n * `priceLimit`.\n *\n * @example\n * ```ts\n * const dto = buildRequestTradeDto({\n * token0: quote.token0Symbol, token1: quote.token1Symbol,\n * tokenInIsToken0: quote.tokenInIsToken0, fee: quote.fee,\n * uniqueKey: createUniqueKey('galaswap-operation-'),\n * exactIn: '100', amountOutMinimum: '14',\n * });\n * ```\n */\nexport function buildRequestTradeDto(input: RequestTradeInput): RequestTradeDTO {\n validateFee(input.fee);\n assertCanonicalPair(input.token0, input.token1);\n assertUniqueKey(input.uniqueKey);\n const dto: Record<string, unknown> = {\n token0: input.token0,\n token1: input.token1,\n fee: input.fee,\n uniqueKey: input.uniqueKey,\n };\n if ('exactIn' in input) {\n const exactIn = input.exactIn;\n if (exactIn === undefined) {\n throw new GSwapSDKError('EXACT_AMOUNT_REQUIRED', { field: 'exactIn' });\n }\n validateNumericAmount(exactIn, 'exactIn');\n dto[input.tokenInIsToken0 ? 'sell0Qty' : 'sell1Qty'] = decimalString(exactIn);\n if (input.amountOutMinimum !== undefined) {\n validateNumericAmount(input.amountOutMinimum, 'amountOutMinimum', true);\n dto['amountOutMinimum'] = decimalString(input.amountOutMinimum);\n }\n } else {\n const exactOut = input.exactOut;\n if (exactOut === undefined) {\n throw new GSwapSDKError('EXACT_AMOUNT_REQUIRED', { field: 'exactOut' });\n }\n validateNumericAmount(exactOut, 'exactOut');\n dto[input.tokenInIsToken0 ? 'buy1Qty' : 'buy0Qty'] = decimalString(exactOut);\n if (input.amountInMaximum !== undefined) {\n validateNumericAmount(input.amountInMaximum, 'amountInMaximum', true);\n dto['amountInMaximum'] = decimalString(input.amountInMaximum);\n }\n }\n if (input.priceLimit !== undefined) {\n validateNumericAmount(input.priceLimit, 'priceLimit');\n dto['priceLimit'] = decimalString(input.priceLimit);\n }\n return dto as unknown as RequestTradeDTO;\n}\n\n/** A canonical pool position: the pair, fee tier, tick range and the attempt's `uniqueKey`. */\nexport interface PoolPositionInput {\n token0: string;\n token1: string;\n fee: number;\n tickLower: number;\n tickUpper: number;\n uniqueKey: string;\n}\n\nfunction assertPoolPosition(input: PoolPositionInput): void {\n validateFee(input.fee);\n assertCanonicalPair(input.token0, input.token1);\n assertTickRange(input.tickLower, input.tickUpper, input.fee);\n assertUniqueKey(input.uniqueKey);\n}\n\n/** Exactly one canonical deposit side. */\nexport type AddLiquidityInput = PoolPositionInput &\n (\n | { depositQuantityToken0: NumericAmount; depositQuantityToken1?: undefined }\n | { depositQuantityToken0?: undefined; depositQuantityToken1: NumericAmount }\n );\n\n/**\n * Builds the AddLiquidity DTO: exactly one of `depositQuantityToken0` / `depositQuantityToken1`\n * (the other side is derived on chain). Key order: `token0, token1, fee, tickLower, tickUpper,\n * uniqueKey, <deposit>`.\n */\nexport function buildAddLiquidityDto(input: AddLiquidityInput): AddLiquidityDTO {\n assertPoolPosition(input);\n const side = onlyOne(\n input.depositQuantityToken0,\n input.depositQuantityToken1,\n 'depositQuantityToken0',\n 'depositQuantityToken1',\n );\n if (side === undefined) {\n throw new GSwapSDKError('EXACTLY_ONE_OF_REQUIRED', {\n first: 'depositQuantityToken0',\n second: 'depositQuantityToken1',\n });\n }\n validateNumericAmount(side.amount, side.name);\n return {\n token0: input.token0,\n token1: input.token1,\n fee: input.fee,\n tickLower: input.tickLower,\n tickUpper: input.tickUpper,\n uniqueKey: input.uniqueKey,\n [side.name]: decimalString(side.amount),\n } as AddLiquidityDTO;\n}\n\n/** At most one canonical withdrawal side; neither closes the position. */\nexport type RemoveLiquidityInput = PoolPositionInput & {\n withdrawalQuantityToken0?: NumericAmount | undefined;\n withdrawalQuantityToken1?: NumericAmount | undefined;\n};\n\n/**\n * Builds the RemoveLiquidity DTO: at most one of `withdrawalQuantityToken0` /\n * `withdrawalQuantityToken1`; omitting both closes the position and sweeps its fees.\n */\nexport function buildRemoveLiquidityDto(input: RemoveLiquidityInput): RemoveLiquidityDTO {\n assertPoolPosition(input);\n const side = onlyOne(\n input.withdrawalQuantityToken0,\n input.withdrawalQuantityToken1,\n 'withdrawalQuantityToken0',\n 'withdrawalQuantityToken1',\n true,\n );\n if (side !== undefined) validateNumericAmount(side.amount, side.name);\n return {\n token0: input.token0,\n token1: input.token1,\n fee: input.fee,\n tickLower: input.tickLower,\n tickUpper: input.tickUpper,\n uniqueKey: input.uniqueKey,\n ...(side === undefined ? {} : { [side.name]: decimalString(side.amount) }),\n };\n}\n\n/** Builds the CollectPositionFees DTO (the contract sweeps all of a position's fees). */\nexport function buildCollectPositionFeesDto(input: PoolPositionInput): CollectPositionFeesDTO {\n assertPoolPosition(input);\n return {\n token0: input.token0,\n token1: input.token1,\n fee: input.fee,\n tickLower: input.tickLower,\n tickUpper: input.tickUpper,\n uniqueKey: input.uniqueKey,\n };\n}\n\n/** A CreatePool in canonical orientation, with exactly one starting price. */\nexport type CreatePoolInput = {\n token0Key: GalaChainTokenClassKey;\n token1Key: GalaChainTokenClassKey;\n /** The symbols to register; `token0Symbol < token1Symbol` and both proposable. */\n token0Symbol: string;\n token1Symbol: string;\n fee: number;\n /** Optional display name; omitted when undefined or empty. */\n name?: string | undefined;\n isPrivate?: boolean | undefined;\n privateAccess?: string[] | undefined;\n uniqueKey: string;\n} & (\n | { startingPrice: NumericAmount; startingSqrtPrice?: undefined }\n | { startingPrice?: undefined; startingSqrtPrice: NumericAmount }\n);\n\n/**\n * Builds the CreatePool DTO. Key order: `token0Key, token1Key, fee, startingPrice |\n * startingSqrtPrice, token0Symbol, token1Symbol, uniqueKey, name, isPrivate, privateAccess`.\n * Prices are those of the canonical pair (token1 per token0); a price quoted the other way round\n * is inverted by the caller before it gets here.\n */\nexport function buildCreatePoolDto(input: CreatePoolInput): CreatePoolDTO {\n validateFee(input.fee);\n assertUniqueKey(input.uniqueKey);\n assertCanonicalPair(input.token0Symbol, input.token1Symbol);\n for (const symbol of [input.token0Symbol, input.token1Symbol]) {\n if (!isProposableTradingSymbol(symbol)) {\n throw new GSwapSDKError('INVALID_TRADING_SYMBOL', { symbol });\n }\n }\n for (const key of [input.token0Key, input.token1Key]) assertClassKey(key);\n const price = onlyOne(\n input.startingPrice,\n input.startingSqrtPrice,\n 'startingPrice',\n 'startingSqrtPrice',\n );\n if (price === undefined) {\n throw new GSwapSDKError('EXACTLY_ONE_OF_REQUIRED', {\n first: 'startingPrice',\n second: 'startingSqrtPrice',\n });\n }\n if (typeof price.amount === 'number') {\n throw new GSwapSDKError('AMOUNT_IS_JS_NUMBER', { field: price.name });\n }\n const parsed = new BigNumber(price.amount);\n if (!parsed.isFinite() || parsed.isLessThanOrEqualTo(0)) {\n throw new GSwapSDKError('INVALID_STARTING_PRICE', { value: String(price.amount) });\n }\n return {\n token0Key: input.token0Key,\n token1Key: input.token1Key,\n fee: input.fee,\n [price.name]: parsed.toFixed(),\n token0Symbol: input.token0Symbol,\n token1Symbol: input.token1Symbol,\n uniqueKey: input.uniqueKey,\n // An empty name is \"no name\", as in frontend_v2's builder (its form yields '').\n ...(input.name === undefined || input.name === '' ? {} : { name: input.name }),\n ...(input.isPrivate === undefined ? {} : { isPrivate: input.isPrivate }),\n ...(input.privateAccess === undefined ? {} : { privateAccess: input.privateAccess }),\n } as CreatePoolDTO;\n}\n\nfunction assertClassKey(key: GalaChainTokenClassKey): void {\n for (const part of ['collection', 'category', 'type', 'additionalKey'] as const) {\n if (typeof key[part] !== 'string' || key[part].length === 0) {\n throw new GSwapSDKError('INCOMPLETE_TOKEN_CLASS_KEY', {\n compositeKey: [key.collection, key.category, key.type, key.additionalKey].join('$'),\n part,\n });\n }\n }\n}\n\n/**\n * Of two mutually exclusive fields, the one that is set (`undefined` when neither is);\n * both set is an error (`AT_MOST_ONE_OF_ALLOWED` or `EXACTLY_ONE_OF_REQUIRED`), and so is\n * neither unless `allowNeither`.\n */\nfunction onlyOne<T>(\n first: T | undefined,\n second: T | undefined,\n firstName: string,\n secondName: string,\n allowNeither = false,\n): { name: string; amount: T } | undefined {\n if (first !== undefined && second !== undefined) {\n throw allowNeither\n ? new GSwapSDKError('AT_MOST_ONE_OF_ALLOWED', { first: firstName, second: secondName })\n : new GSwapSDKError('EXACTLY_ONE_OF_REQUIRED', { first: firstName, second: secondName });\n }\n if (first !== undefined) return { name: firstName, amount: first };\n if (second !== undefined) return { name: secondName, amount: second };\n return undefined;\n}\n\n/** The methods whose DTOs the builders produce. */\nexport type V2WriteMethod =\n 'RequestTrade' | 'AddLiquidity' | 'RemoveLiquidity' | 'CollectPositionFees' | 'CreatePool';\n\ntype DtoExpectation =\n 'string' | 'number' | 'boolean' | 'class_key' | 'string_array' | 'exactly_once';\n\nfunction invalidDto(\n method: string,\n field: string,\n expected: DtoExpectation,\n): GSwapSDKError<'INVALID_DTO'> {\n return new GSwapSDKError('INVALID_DTO', { method, field, expected });\n}\n\n/**\n * Rebuilds a DTO from its own fields with the builder of its method: the canonical form of the\n * DTO, with the same validation. Key order and decimal text are the builder's.\n */\nfunction rebuildV2Dto(\n method: V2WriteMethod,\n dto: Record<string, unknown>,\n): Record<string, unknown> {\n const text = (field: string): string => {\n const value = dto[field];\n if (typeof value !== 'string') throw invalidDto(method, field, 'string');\n return value;\n };\n const integer = (field: string): number => {\n const value = dto[field];\n if (typeof value !== 'number') throw invalidDto(method, field, 'number');\n return value;\n };\n const optionalText = (field: string): string | undefined =>\n dto[field] === undefined ? undefined : text(field);\n const classKey = (field: string): GalaChainTokenClassKey => {\n const value = dto[field];\n if (typeof value !== 'object' || value === null) throw invalidDto(method, field, 'class_key');\n const key = value as Record<string, unknown>;\n const part = (name: string): string => {\n if (typeof key[name] !== 'string') throw invalidDto(method, `${field}.${name}`, 'string');\n return key[name];\n };\n return {\n collection: part('collection'),\n category: part('category'),\n type: part('type'),\n additionalKey: part('additionalKey'),\n };\n };\n // The pool pair is not a field of CreatePool, so these are read per method, not up front.\n const base = () => ({ token0: text('token0'), token1: text('token1'), fee: integer('fee') });\n const uniqueKey = text('uniqueKey');\n const position = () => ({\n ...base(),\n uniqueKey,\n tickLower: integer('tickLower'),\n tickUpper: integer('tickUpper'),\n });\n switch (method) {\n case 'RequestTrade': {\n const qty = ['sell0Qty', 'sell1Qty', 'buy0Qty', 'buy1Qty'].filter(\n (f) => dto[f] !== undefined,\n );\n const [field] = qty;\n if (qty.length !== 1 || field === undefined) {\n throw invalidDto(method, 'sell0Qty|sell1Qty|buy0Qty|buy1Qty', 'exactly_once');\n }\n const tokenInIsToken0 = field === 'sell0Qty' || field === 'buy1Qty';\n const shared = { ...base(), uniqueKey, tokenInIsToken0 };\n const priceLimit = optionalText('priceLimit');\n return field.startsWith('sell')\n ? buildRequestTradeDto({\n ...shared,\n exactIn: text(field),\n amountOutMinimum: optionalText('amountOutMinimum'),\n priceLimit,\n })\n : buildRequestTradeDto({\n ...shared,\n exactOut: text(field),\n amountInMaximum: optionalText('amountInMaximum'),\n priceLimit,\n });\n }\n case 'AddLiquidity': {\n const token0Side = optionalText('depositQuantityToken0');\n const token1Side = optionalText('depositQuantityToken1');\n return buildAddLiquidityDto({\n ...position(),\n ...(token0Side === undefined\n ? { depositQuantityToken1: token1Side as string }\n : { depositQuantityToken0: token0Side }),\n });\n }\n case 'RemoveLiquidity':\n return buildRemoveLiquidityDto({\n ...position(),\n withdrawalQuantityToken0: optionalText('withdrawalQuantityToken0'),\n withdrawalQuantityToken1: optionalText('withdrawalQuantityToken1'),\n });\n case 'CollectPositionFees':\n return buildCollectPositionFeesDto(position());\n case 'CreatePool': {\n const price = optionalText('startingPrice');\n const sqrtPrice = optionalText('startingSqrtPrice');\n const privateAccess = dto['privateAccess'];\n if (\n privateAccess !== undefined &&\n (!Array.isArray(privateAccess) || privateAccess.some((entry) => typeof entry !== 'string'))\n ) {\n throw invalidDto(method, 'privateAccess', 'string_array');\n }\n if (dto['isPrivate'] !== undefined && typeof dto['isPrivate'] !== 'boolean') {\n throw invalidDto(method, 'isPrivate', 'boolean');\n }\n return buildCreatePoolDto({\n token0Key: classKey('token0Key'),\n token1Key: classKey('token1Key'),\n token0Symbol: text('token0Symbol'),\n token1Symbol: text('token1Symbol'),\n fee: integer('fee'),\n uniqueKey,\n name: optionalText('name'),\n isPrivate: dto['isPrivate'],\n privateAccess: privateAccess as string[] | undefined,\n ...(price === undefined\n ? { startingSqrtPrice: sqrtPrice as string }\n : { startingPrice: price }),\n });\n }\n }\n}\n\n/**\n * Asserts that `dto` is EXACTLY what the builder of `method` produces for its own fields — the\n * same fields, canonical decimal text, builder key order, nothing extra (no `signature`). The\n * `submit*` methods use it so that what an app showed a user (a DTO it built) is byte for byte\n * what gets signed; a DTO edited by hand, or built elsewhere, is refused rather than signed.\n *\n * @throws GSwapSDKError `INVALID_DTO` or `DTO_NOT_CANONICAL`.\n */\nexport function assertCanonicalV2Dto(method: V2WriteMethod, dto: Record<string, unknown>): void {\n const rebuilt = rebuildV2Dto(method, dto);\n if (JSON.stringify(rebuilt) !== JSON.stringify(dto)) {\n throw new GSwapSDKError('DTO_NOT_CANONICAL', { method });\n }\n}\n"],"mappings":"0TA8BA,MAAM,EAAiB,GAAkC,IAAI,EAAU,CAAM,CAAC,CAAC,QAAQ,EA8BvF,SAAgB,EAAqB,EAA2C,CAC9E,EAAY,EAAM,GAAG,EACrB,EAAoB,EAAM,OAAQ,EAAM,MAAM,EAC9C,EAAgB,EAAM,SAAS,EAC/B,IAAM,EAA+B,CACnC,OAAQ,EAAM,OACd,OAAQ,EAAM,OACd,IAAK,EAAM,IACX,UAAW,EAAM,SACnB,EACA,GAAI,YAAa,EAAO,CACtB,IAAM,EAAU,EAAM,QACtB,GAAI,IAAY,IAAA,GACd,MAAM,IAAI,EAAc,wBAAyB,CAAE,MAAO,SAAU,CAAC,EAEvE,EAAsB,EAAS,SAAS,EACxC,EAAI,EAAM,gBAAkB,WAAa,YAAc,EAAc,CAAO,EACxE,EAAM,mBAAqB,IAAA,KAC7B,EAAsB,EAAM,iBAAkB,mBAAoB,EAAI,EACtE,EAAI,iBAAsB,EAAc,EAAM,gBAAgB,EAElE,KAAO,CACL,IAAM,EAAW,EAAM,SACvB,GAAI,IAAa,IAAA,GACf,MAAM,IAAI,EAAc,wBAAyB,CAAE,MAAO,UAAW,CAAC,EAExE,EAAsB,EAAU,UAAU,EAC1C,EAAI,EAAM,gBAAkB,UAAY,WAAa,EAAc,CAAQ,EACvE,EAAM,kBAAoB,IAAA,KAC5B,EAAsB,EAAM,gBAAiB,kBAAmB,EAAI,EACpE,EAAI,gBAAqB,EAAc,EAAM,eAAe,EAEhE,CAKA,OAJI,EAAM,aAAe,IAAA,KACvB,EAAsB,EAAM,WAAY,YAAY,EACpD,EAAI,WAAgB,EAAc,EAAM,UAAU,GAE7C,CACT,CAYA,SAAS,EAAmB,EAAgC,CAC1D,EAAY,EAAM,GAAG,EACrB,EAAoB,EAAM,OAAQ,EAAM,MAAM,EAC9C,EAAgB,EAAM,UAAW,EAAM,UAAW,EAAM,GAAG,EAC3D,EAAgB,EAAM,SAAS,CACjC,CAcA,SAAgB,EAAqB,EAA2C,CAC9E,EAAmB,CAAK,EACxB,IAAM,EAAO,EACX,EAAM,sBACN,EAAM,sBACN,wBACA,uBACF,EACA,GAAI,IAAS,IAAA,GACX,MAAM,IAAI,EAAc,0BAA2B,CACjD,MAAO,wBACP,OAAQ,uBACV,CAAC,EAGH,OADA,EAAsB,EAAK,OAAQ,EAAK,IAAI,EACrC,CACL,OAAQ,EAAM,OACd,OAAQ,EAAM,OACd,IAAK,EAAM,IACX,UAAW,EAAM,UACjB,UAAW,EAAM,UACjB,UAAW,EAAM,WAChB,EAAK,MAAO,EAAc,EAAK,MAAM,CACxC,CACF,CAYA,SAAgB,EAAwB,EAAiD,CACvF,EAAmB,CAAK,EACxB,IAAM,EAAO,EACX,EAAM,yBACN,EAAM,yBACN,2BACA,2BACA,EACF,EAEA,OADI,IAAS,IAAA,IAAW,EAAsB,EAAK,OAAQ,EAAK,IAAI,EAC7D,CACL,OAAQ,EAAM,OACd,OAAQ,EAAM,OACd,IAAK,EAAM,IACX,UAAW,EAAM,UACjB,UAAW,EAAM,UACjB,UAAW,EAAM,UACjB,GAAI,IAAS,IAAA,GAAY,CAAC,EAAI,EAAG,EAAK,MAAO,EAAc,EAAK,MAAM,CAAE,CAC1E,CACF,CAGA,SAAgB,EAA4B,EAAkD,CAE5F,OADA,EAAmB,CAAK,EACjB,CACL,OAAQ,EAAM,OACd,OAAQ,EAAM,OACd,IAAK,EAAM,IACX,UAAW,EAAM,UACjB,UAAW,EAAM,UACjB,UAAW,EAAM,SACnB,CACF,CA0BA,SAAgB,EAAmB,EAAuC,CACxE,EAAY,EAAM,GAAG,EACrB,EAAgB,EAAM,SAAS,EAC/B,EAAoB,EAAM,aAAc,EAAM,YAAY,EAC1D,IAAK,IAAM,IAAU,CAAC,EAAM,aAAc,EAAM,YAAY,EAC1D,GAAI,CAAC,EAA0B,CAAM,EACnC,MAAM,IAAI,EAAc,yBAA0B,CAAE,QAAO,CAAC,EAGhE,IAAK,IAAM,IAAO,CAAC,EAAM,UAAW,EAAM,SAAS,EAAG,EAAe,CAAG,EACxE,IAAM,EAAQ,EACZ,EAAM,cACN,EAAM,kBACN,gBACA,mBACF,EACA,GAAI,IAAU,IAAA,GACZ,MAAM,IAAI,EAAc,0BAA2B,CACjD,MAAO,gBACP,OAAQ,mBACV,CAAC,EAEH,GAAI,OAAO,EAAM,QAAW,SAC1B,MAAM,IAAI,EAAc,sBAAuB,CAAE,MAAO,EAAM,IAAK,CAAC,EAEtE,IAAM,EAAS,IAAI,EAAU,EAAM,MAAM,EACzC,GAAI,CAAC,EAAO,SAAS,GAAK,EAAO,oBAAoB,CAAC,EACpD,MAAM,IAAI,EAAc,yBAA0B,CAAE,MAAO,OAAO,EAAM,MAAM,CAAE,CAAC,EAEnF,MAAO,CACL,UAAW,EAAM,UACjB,UAAW,EAAM,UACjB,IAAK,EAAM,KACV,EAAM,MAAO,EAAO,QAAQ,EAC7B,aAAc,EAAM,aACpB,aAAc,EAAM,aACpB,UAAW,EAAM,UAEjB,GAAI,EAAM,OAAS,IAAA,IAAa,EAAM,OAAS,GAAK,CAAC,EAAI,CAAE,KAAM,EAAM,IAAK,EAC5E,GAAI,EAAM,YAAc,IAAA,GAAY,CAAC,EAAI,CAAE,UAAW,EAAM,SAAU,EACtE,GAAI,EAAM,gBAAkB,IAAA,GAAY,CAAC,EAAI,CAAE,cAAe,EAAM,aAAc,CACpF,CACF,CAEA,SAAS,EAAe,EAAmC,CACzD,IAAK,IAAM,IAAQ,CAAC,aAAc,WAAY,OAAQ,eAAe,EACnE,GAAI,OAAO,EAAI,IAAU,UAAY,EAAI,EAAK,CAAC,SAAW,EACxD,MAAM,IAAI,EAAc,6BAA8B,CACpD,aAAc,CAAC,EAAI,WAAY,EAAI,SAAU,EAAI,KAAM,EAAI,aAAa,CAAC,CAAC,KAAK,GAAG,EAClF,MACF,CAAC,CAGP,CAOA,SAAS,EACP,EACA,EACA,EACA,EACA,EAAe,GAC0B,CACzC,GAAI,IAAU,IAAA,IAAa,IAAW,IAAA,GACpC,MAAM,EACF,IAAI,EAAc,yBAA0B,CAAE,MAAO,EAAW,OAAQ,CAAW,CAAC,EACpF,IAAI,EAAc,0BAA2B,CAAE,MAAO,EAAW,OAAQ,CAAW,CAAC,EAE3F,GAAI,IAAU,IAAA,GAAW,MAAO,CAAE,KAAM,EAAW,OAAQ,CAAM,EACjE,GAAI,IAAW,IAAA,GAAW,MAAO,CAAE,KAAM,EAAY,OAAQ,CAAO,CAEtE,CASA,SAAS,EACP,EACA,EACA,EAC8B,CAC9B,OAAO,IAAI,EAAc,cAAe,CAAE,SAAQ,QAAO,UAAS,CAAC,CACrE,CAMA,SAAS,EACP,EACA,EACyB,CACzB,IAAM,EAAQ,GAA0B,CACtC,IAAM,EAAQ,EAAI,GAClB,GAAI,OAAO,GAAU,SAAU,MAAM,EAAW,EAAQ,EAAO,QAAQ,EACvE,OAAO,CACT,EACM,EAAW,GAA0B,CACzC,IAAM,EAAQ,EAAI,GAClB,GAAI,OAAO,GAAU,SAAU,MAAM,EAAW,EAAQ,EAAO,QAAQ,EACvE,OAAO,CACT,EACM,EAAgB,GACpB,EAAI,KAAW,IAAA,GAAY,IAAA,GAAY,EAAK,CAAK,EAC7C,EAAY,GAA0C,CAC1D,IAAM,EAAQ,EAAI,GAClB,GAAI,OAAO,GAAU,WAAY,EAAgB,MAAM,EAAW,EAAQ,EAAO,WAAW,EAC5F,IAAM,EAAM,EACN,EAAQ,GAAyB,CACrC,GAAI,OAAO,EAAI,IAAU,SAAU,MAAM,EAAW,EAAQ,GAAG,EAAM,GAAG,IAAQ,QAAQ,EACxF,OAAO,EAAI,EACb,EACA,MAAO,CACL,WAAY,EAAK,YAAY,EAC7B,SAAU,EAAK,UAAU,EACzB,KAAM,EAAK,MAAM,EACjB,cAAe,EAAK,eAAe,CACrC,CACF,EAEM,OAAc,CAAE,OAAQ,EAAK,QAAQ,EAAG,OAAQ,EAAK,QAAQ,EAAG,IAAK,EAAQ,KAAK,CAAE,GACpF,EAAY,EAAK,WAAW,EAC5B,OAAkB,CACtB,GAAG,EAAK,EACR,YACA,UAAW,EAAQ,WAAW,EAC9B,UAAW,EAAQ,WAAW,CAChC,GACA,OAAQ,EAAR,CACE,IAAK,eAAgB,CACnB,IAAM,EAAM,CAAC,WAAY,WAAY,UAAW,SAAS,CAAC,CAAC,OACxD,GAAM,EAAI,KAAO,IAAA,EACpB,EACM,CAAC,GAAS,EAChB,GAAI,EAAI,SAAW,GAAK,IAAU,IAAA,GAChC,MAAM,EAAW,EAAQ,oCAAqC,cAAc,EAE9E,IAAM,EAAkB,IAAU,YAAc,IAAU,UACpD,EAAS,CAAE,GAAG,EAAK,EAAG,YAAW,iBAAgB,EACjD,EAAa,EAAa,YAAY,EAC5C,OAAO,EAAM,WAAW,MAAM,EAC1B,EAAqB,CACnB,GAAG,EACH,QAAS,EAAK,CAAK,EACnB,iBAAkB,EAAa,kBAAkB,EACjD,YACF,CAAC,EACD,EAAqB,CACnB,GAAG,EACH,SAAU,EAAK,CAAK,EACpB,gBAAiB,EAAa,iBAAiB,EAC/C,YACF,CAAC,CACP,CACA,IAAK,eAAgB,CACnB,IAAM,EAAa,EAAa,uBAAuB,EACjD,EAAa,EAAa,uBAAuB,EACvD,OAAO,EAAqB,CAC1B,GAAG,EAAS,EACZ,GAAI,IAAe,IAAA,GACf,CAAE,sBAAuB,CAAqB,EAC9C,CAAE,sBAAuB,CAAW,CAC1C,CAAC,CACH,CACA,IAAK,kBACH,OAAO,EAAwB,CAC7B,GAAG,EAAS,EACZ,yBAA0B,EAAa,0BAA0B,EACjE,yBAA0B,EAAa,0BAA0B,CACnE,CAAC,EACH,IAAK,sBACH,OAAO,EAA4B,EAAS,CAAC,EAC/C,IAAK,aAAc,CACjB,IAAM,EAAQ,EAAa,eAAe,EACpC,EAAY,EAAa,mBAAmB,EAC5C,EAAgB,EAAI,cAC1B,GACE,IAAkB,IAAA,KACjB,CAAC,MAAM,QAAQ,CAAa,GAAK,EAAc,KAAM,GAAU,OAAO,GAAU,QAAQ,GAEzF,MAAM,EAAW,EAAQ,gBAAiB,cAAc,EAE1D,GAAI,EAAI,YAAiB,IAAA,IAAa,OAAO,EAAI,WAAiB,UAChE,MAAM,EAAW,EAAQ,YAAa,SAAS,EAEjD,OAAO,EAAmB,CACxB,UAAW,EAAS,WAAW,EAC/B,UAAW,EAAS,WAAW,EAC/B,aAAc,EAAK,cAAc,EACjC,aAAc,EAAK,cAAc,EACjC,IAAK,EAAQ,KAAK,EAClB,YACA,KAAM,EAAa,MAAM,EACzB,UAAW,EAAI,UACA,gBACf,GAAI,IAAU,IAAA,GACV,CAAE,kBAAmB,CAAoB,EACzC,CAAE,cAAe,CAAM,CAC7B,CAAC,CACH,CACF,CACF,CAUA,SAAgB,EAAqB,EAAuB,EAAoC,CAC9F,IAAM,EAAU,EAAa,EAAQ,CAAG,EACxC,GAAI,KAAK,UAAU,CAAO,IAAM,KAAK,UAAU,CAAG,EAChD,MAAM,IAAI,EAAc,oBAAqB,CAAE,QAAO,CAAC,CAE3D"}