@gala-chain/gswap-sdk 2.0.0 → 2.1.0-beta.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.
Files changed (72) hide show
  1. package/README.md +2 -2
  2. package/dist/browser/classes/apply_results.js.map +1 -1
  3. package/dist/browser/classes/assets.js.map +1 -1
  4. package/dist/browser/classes/dex_fees.js +1 -1
  5. package/dist/browser/classes/dex_fees.js.map +1 -1
  6. package/dist/browser/classes/gateway.js +1 -1
  7. package/dist/browser/classes/gateway.js.map +1 -1
  8. package/dist/browser/classes/gdex_stream.js +1 -1
  9. package/dist/browser/classes/gdex_stream.js.map +1 -1
  10. package/dist/browser/classes/gswap.js +1 -1
  11. package/dist/browser/classes/gswap.js.map +1 -1
  12. package/dist/browser/classes/gswap_error_catalog.js.map +1 -1
  13. package/dist/browser/classes/gswap_error_phase_table.js.map +1 -1
  14. package/dist/browser/classes/gswap_sdk_error.js +1 -1
  15. package/dist/browser/classes/gswap_sdk_error.js.map +1 -1
  16. package/dist/browser/classes/http_client.js.map +1 -1
  17. package/dist/browser/classes/leaderboard.js.map +1 -1
  18. package/dist/browser/classes/market.js.map +1 -1
  19. package/dist/browser/classes/onramper.js.map +1 -1
  20. package/dist/browser/classes/pending_swap_fate.js.map +1 -1
  21. package/dist/browser/classes/pending_swaps.js +1 -1
  22. package/dist/browser/classes/pending_swaps.js.map +1 -1
  23. package/dist/browser/classes/pools.js +1 -1
  24. package/dist/browser/classes/pools.js.map +1 -1
  25. package/dist/browser/classes/positions.js +1 -1
  26. package/dist/browser/classes/positions.js.map +1 -1
  27. package/dist/browser/classes/quoting.js +1 -1
  28. package/dist/browser/classes/quoting.js.map +1 -1
  29. package/dist/browser/classes/shadow_submit.js.map +1 -1
  30. package/dist/browser/classes/storage_adapters.js.map +1 -1
  31. package/dist/browser/classes/submitted_transaction.js +1 -1
  32. package/dist/browser/classes/submitted_transaction.js.map +1 -1
  33. package/dist/browser/classes/swaps.js +1 -1
  34. package/dist/browser/classes/swaps.js.map +1 -1
  35. package/dist/browser/classes/symbols.js +1 -1
  36. package/dist/browser/classes/symbols.js.map +1 -1
  37. package/dist/browser/types/env.js.map +1 -1
  38. package/dist/browser/types/fees.js.map +1 -1
  39. package/dist/browser/types/market.js.map +1 -1
  40. package/dist/browser/utils/chain_contract.js +2 -0
  41. package/dist/browser/utils/chain_contract.js.map +1 -0
  42. package/dist/browser/utils/classification.js.map +1 -1
  43. package/dist/browser/utils/confirmation_schedule.js.map +1 -1
  44. package/dist/browser/utils/confirmation_state.js.map +1 -1
  45. package/dist/browser/utils/debug.js.map +1 -1
  46. package/dist/browser/utils/dto_builders.js.map +1 -1
  47. package/dist/browser/utils/failure_classification.js.map +1 -1
  48. package/dist/browser/utils/failure_guards.js.map +1 -1
  49. package/dist/browser/utils/lenient.js.map +1 -1
  50. package/dist/browser/utils/ordering.js.map +1 -1
  51. package/dist/browser/utils/reconnection.js +1 -1
  52. package/dist/browser/utils/reconnection.js.map +1 -1
  53. package/dist/browser/utils/resolve_pre_send.js.map +1 -1
  54. package/dist/browser/utils/server_message.js.map +1 -1
  55. package/dist/browser/utils/shadow_errors.js.map +1 -1
  56. package/dist/browser/utils/submit_state.js.map +1 -1
  57. package/dist/browser/utils/tick_align.js.map +1 -1
  58. package/dist/browser/utils/tick_math.js.map +1 -1
  59. package/dist/browser/utils/tick_range.js.map +1 -1
  60. package/dist/browser/utils/ticks.js.map +1 -1
  61. package/dist/browser/utils/validation.js.map +1 -1
  62. package/dist/index.cjs +14 -6
  63. package/dist/index.cjs.map +1 -1
  64. package/dist/index.d.cts +2 -2
  65. package/dist/index.d.ts +2 -2
  66. package/dist/index.mjs +14 -6
  67. package/dist/index.mjs.map +1 -1
  68. package/package.json +3 -3
  69. /package/dist/browser/_virtual/{_@oxc-project_runtime@0.153.0 → _@oxc-project_runtime@0.154.0}/helpers/esm/defineProperty.js +0 -0
  70. /package/dist/browser/_virtual/{_@oxc-project_runtime@0.153.0 → _@oxc-project_runtime@0.154.0}/helpers/esm/toPrimitive.js +0 -0
  71. /package/dist/browser/_virtual/{_@oxc-project_runtime@0.153.0 → _@oxc-project_runtime@0.154.0}/helpers/esm/toPropertyKey.js +0 -0
  72. /package/dist/browser/_virtual/{_@oxc-project_runtime@0.153.0 → _@oxc-project_runtime@0.154.0}/helpers/esm/typeof.js +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"swaps.js","names":["_defineProperty","swapRouteFromQuote","validateFee","quote","assertCanonicalPair","tokenIn","tokenOut","Swaps","gateway","symbols","signer","walletAddress","userRef","resolveUniqueKeyPrefix","options","fee","amount","shadowFailures","uniqueKey","createUniqueKey","assertUniqueKey","tokenInSymbol","tradingSymbolOf","tokenOutSymbol","ordered","buildRequestTradeDto","signed","dto","result","readShadowSubmitOutcome","assertCanonicalV2Dto","GSwapSDKError","signPreSend","pool","readOrderedSymbols","orderSymbols","SubmittedTransaction"],"sources":["../../../src/classes/swaps.ts"],"sourcesContent":["import {\n assertUniqueKey,\n createUniqueKey,\n resolveUniqueKeyPrefix,\n signPreSend,\n type GalaChainSigner,\n type HttpRequestor,\n type UniqueKeyOptions,\n type WireOptions,\n} from '@gala-chain/gala-core-sdk';\nimport type { SwapAmount } from '../types/amounts.js';\nimport type { FEE_TIER } from '../types/fees.js';\nimport { orderSymbols, readOrderedSymbols, type TokenRef } from '../utils/ordering.js';\nimport { assertCanonicalV2Dto, buildRequestTradeDto } from '../utils/dto_builders.js';\nimport { shadowFailures } from '../utils/shadow_errors.js';\nimport { assertCanonicalPair, userRef, validateFee } from '../utils/validation.js';\nimport type { ChainSubmitOptions } from './gateway.js';\nimport { GSwapSDKError } from './gswap_sdk_error.js';\nimport {\n readShadowSubmitOutcome,\n type ShadowSubmitOutcome,\n type ShadowSubmitResult,\n} from './shadow_submit.js';\nimport { SubmittedTransaction } from './submitted_transaction.js';\nimport type { ConfirmOptions } from '../types/confirm_options.js';\nimport type { RequestTradeDTO } from '../types/v2_dtos.js';\nimport type { AppliedTrade, QuoteResult } from '../types/v2_results.js';\nimport { tradingSymbolOf, type TokenResolver } from './symbols.js';\n\ninterface SwapGateway {\n submit: (\n method: 'RequestTrade',\n signedBody: Record<string, unknown>,\n options?: ChainSubmitOptions,\n ) => Promise<SubmittedTransaction<AppliedTrade>>;\n submitShadow: (\n method: 'RequestTrade',\n signedBody: Record<string, unknown>,\n options?: ChainSubmitOptions,\n ) => Promise<ShadowSubmitResult>;\n readonly dexBackendBaseUrl: string;\n readonly httpRequestor: HttpRequestor;\n readonly requestTimeoutMs: number;\n readonly wire?: WireOptions | undefined;\n}\n\n/** The canonical pool a swap trades in, as a quote names it. */\nexport interface SwapPool {\n token0: string;\n token1: string;\n}\n\n/** Options of a swap. */\nexport interface SwapOptions {\n /**\n * The attempt's `uniqueKey` (1-128 characters of `A-Za-z0-9._:-`). Default: a fresh\n * `<uniqueKeyPrefix><uuid>`. Supply one to know the key before signing (to remember it, to\n * show it), or to retry the SAME attempt after a lost response; never reuse it for another trade.\n */\n uniqueKey?: string | undefined;\n /**\n * Pin the pool: take `token0` / `token1` verbatim from the quote the user saw, instead of\n * ordering `tokenIn` / `tokenOut` by string comparison. `tokenIn` and `tokenOut` must be exactly\n * these two symbols, and `token0` must sort before `token1`. See {@link swapRouteFromQuote}.\n */\n pool?: SwapPool | undefined;\n}\n\n/** Everything {@link Swaps.swap} needs to trade in the pool a quote priced. */\nexport interface SwapRoute {\n tokenIn: string;\n tokenOut: string;\n fee: FEE_TIER;\n pool: SwapPool;\n}\n\n/**\n * The route of a quote: its fee tier and pool, with `tokenIn` / `tokenOut` taken from\n * `tokenInIsToken0`, nothing re-derived. Use it to swap in exactly the pool a quote priced:\n *\n * @example\n * ```ts\n * const route = swapRouteFromQuote(quote);\n * await swaps.swap(route.tokenIn, route.tokenOut, route.fee, { exactIn: '100' }, { pool: route.pool });\n * ```\n */\nexport function swapRouteFromQuote(\n quote: Pick<QuoteResult, 'token0Symbol' | 'token1Symbol' | 'tokenInIsToken0' | 'fee'>,\n): SwapRoute {\n validateFee(quote.fee);\n assertCanonicalPair(quote.token0Symbol, quote.token1Symbol);\n const [tokenIn, tokenOut] = quote.tokenInIsToken0\n ? [quote.token0Symbol, quote.token1Symbol]\n : [quote.token1Symbol, quote.token0Symbol];\n return {\n tokenIn,\n tokenOut,\n fee: quote.fee,\n pool: { token0: quote.token0Symbol, token1: quote.token1Symbol },\n };\n}\n\n/** Executes queued current-contract RequestTrade operations through the Chain Gateway. */\nexport class Swaps {\n /**\n * Creates a swap service.\n *\n * @example\n * ```ts\n * const swaps = new Swaps(gateway, symbols, signer, 'client|alice');\n * ```\n */\n constructor(\n private readonly gateway: SwapGateway,\n private readonly symbols: TokenResolver,\n private readonly signer?: GalaChainSigner,\n walletAddress?: string,\n options: UniqueKeyOptions = {},\n ) {\n this.walletAddress =\n walletAddress === undefined ? undefined : userRef(walletAddress, 'walletAddress');\n this.uniqueKeyPrefix = resolveUniqueKeyPrefix(options.uniqueKeyPrefix);\n }\n\n /** The signing wallet in the gateway's spelling (`eth|<EIP-55>` or `client|<id>`). */\n private readonly walletAddress: string | undefined;\n private readonly uniqueKeyPrefix: string;\n\n /**\n * Signs and submits an exact-input or exact-output RequestTrade DTO. The returned transaction\n * is QUEUED; call `await tx.confirm()` to obtain the executed trade or chain rejection.\n *\n * @example\n * ```ts\n * const submitted = await swaps.swap('GALA', 'GUSDC', 3000, {\n * exactIn: '100',\n * amountOutMinimum: '14',\n * });\n * ```\n */\n public async swap(\n tokenIn: TokenRef,\n tokenOut: TokenRef,\n fee: FEE_TIER,\n amount: SwapAmount,\n options?: SwapOptions,\n ): Promise<SubmittedTransaction<AppliedTrade>> {\n return this.submitSwap(await this.buildSwap(tokenIn, tokenOut, fee, amount, options));\n }\n\n /**\n * Signs and submits a SHADOW (dark) swap: the same RequestTrade as {@link swap}, relayed with\n * `X-Shadow: true` so the swap backend marks it private before it reaches the contract. Requires a\n * `walletAddress`. Resolves the normalized outcome instead of throwing on a gateway refusal:\n * `ok: false` carries `unavailable` (retryable), `disabled`, `rejected` (with the gateway's\n * message) or `unknown`. A success carries `queued`, to be confirmed with\n * `confirmSwap(queued.uniqueKey)`; never fall back to a public swap on failure. Whatever this\n * THROWS (a validation error, the signer, a lost connection, `SHADOW_NOT_QUEUED`) has\n * `error.shadow === true` (`isShadowError`), so an app can apply its dark-swap policy to it.\n *\n * @example\n * ```ts\n * const outcome = await swaps.swapShadow('GALA', 'GUSDC', 3000, { exactIn: '100' });\n * if (outcome.ok) await swaps.confirmSwap(outcome.queued.uniqueKey);\n * ```\n */\n public async swapShadow(\n tokenIn: TokenRef,\n tokenOut: TokenRef,\n fee: FEE_TIER,\n amount: SwapAmount,\n options?: SwapOptions,\n ): Promise<ShadowSubmitOutcome> {\n return shadowFailures(async () =>\n this.submitSwapShadow(await this.buildSwap(tokenIn, tokenOut, fee, amount, options)),\n );\n }\n\n /**\n * Builds the RequestTrade DTO that {@link swap} would sign, without signing or sending it: to\n * show in an approval popup, to remember its `uniqueKey`, or to sign elsewhere. Submit exactly\n * this DTO with {@link submitSwap}. The pure counterpart, with canonical inputs and no\n * resolution, is `buildRequestTradeDto`.\n *\n * @example\n * ```ts\n * const dto = await swaps.buildSwap('GALA', 'GUSDC', 3000, { exactIn: '100' });\n * showApproval(dto);\n * const submitted = await swaps.submitSwap(dto);\n * ```\n */\n public async buildSwap(\n tokenIn: TokenRef,\n tokenOut: TokenRef,\n fee: FEE_TIER,\n amount: SwapAmount,\n options?: SwapOptions,\n ): Promise<RequestTradeDTO> {\n validateFee(fee);\n const uniqueKey = options?.uniqueKey ?? createUniqueKey(this.uniqueKeyPrefix);\n assertUniqueKey(uniqueKey);\n const tokenInSymbol = await tradingSymbolOf(this.symbols, tokenIn);\n const tokenOutSymbol = await tradingSymbolOf(this.symbols, tokenOut);\n const ordered = this.orientPool(tokenInSymbol, tokenOutSymbol, options?.pool);\n return buildRequestTradeDto({\n token0: ordered.token0,\n token1: ordered.token1,\n fee,\n tokenInIsToken0: tokenInSymbol === ordered.token0,\n uniqueKey,\n ...amount,\n });\n }\n\n /**\n * Signs and submits a DTO built by {@link buildSwap} or `buildRequestTradeDto`. The DTO must be\n * exactly what the builder produces (no signature, canonical decimals, builder key order), so\n * what an app showed is what gets signed.\n *\n * @example\n * ```ts\n * const submitted = await swaps.submitSwap(dto);\n * const applied = await submitted.confirm();\n * ```\n */\n public async submitSwap(dto: RequestTradeDTO): Promise<SubmittedTransaction<AppliedTrade>> {\n const signed = await this.signRequestTrade(dto);\n return this.gateway.submit('RequestTrade', signed, this.submitOptions());\n }\n\n /** Signs and shadow-submits a DTO built by {@link buildSwap}; see {@link swapShadow}. */\n public async submitSwapShadow(dto: RequestTradeDTO): Promise<ShadowSubmitOutcome> {\n return shadowFailures(async () => {\n const signed = await this.signRequestTrade(dto);\n const result = await this.gateway.submitShadow('RequestTrade', signed, this.submitOptions());\n return readShadowSubmitOutcome('RequestTrade', signed, result);\n });\n }\n\n private submitOptions(): ChainSubmitOptions {\n return this.walletAddress === undefined ? {} : { walletAddress: this.walletAddress };\n }\n\n private async signRequestTrade(dto: RequestTradeDTO): Promise<Record<string, unknown>> {\n assertCanonicalV2Dto('RequestTrade', dto);\n if (this.signer === undefined) throw GSwapSDKError.noSignerError();\n return signPreSend(this.signer, 'RequestTrade', dto);\n }\n\n /** The pool's two symbols in contract order: the quote's own when pinned, else string order. */\n private orientPool(\n tokenInSymbol: string,\n tokenOutSymbol: string,\n pool: SwapPool | undefined,\n ): SwapPool {\n if (pool === undefined) {\n return readOrderedSymbols(orderSymbols(tokenInSymbol, tokenOutSymbol));\n }\n const sameSet =\n (tokenInSymbol === pool.token0 && tokenOutSymbol === pool.token1) ||\n (tokenInSymbol === pool.token1 && tokenOutSymbol === pool.token0);\n if (!sameSet) {\n throw new GSwapSDKError('POOL_MISMATCH', {\n tokenIn: tokenInSymbol,\n tokenOut: tokenOutSymbol,\n poolToken0: pool.token0,\n poolToken1: pool.token1,\n });\n }\n return { token0: pool.token0, token1: pool.token1 };\n }\n\n /**\n * Confirm a swap by its durable `uniqueKey` without resubmitting it. This is the recovery\n * path after a `CONFIRMATION_TIMEOUT`, a `SUBMISSION_OUTCOME_UNKNOWN`, a `409` replay\n * conflict, or a process restart: it reads the same apply-results record that\n * `SubmittedTransaction.confirm()` reads (retained for 7 days) and resolves the applied trade\n * or throws the chain's rejection. The first read happens immediately unless\n * `initialDelayMs` is given.\n *\n * @example\n * ```ts\n * const applied = await swaps.confirmSwap('galaswap-operation-2f1c…');\n * ```\n */\n public async confirmSwap(uniqueKey: string, options?: ConfirmOptions): Promise<AppliedTrade> {\n assertUniqueKey(uniqueKey);\n const transaction = new SubmittedTransaction<AppliedTrade>({\n method: 'RequestTrade',\n uniqueKey,\n transactionId: null,\n mode: 'queued',\n result: undefined,\n dexBackendBaseUrl: this.gateway.dexBackendBaseUrl,\n httpRequestor: this.gateway.httpRequestor,\n requestTimeoutMs: this.gateway.requestTimeoutMs,\n wire: this.gateway.wire,\n });\n return transaction.confirm({ initialDelayMs: 0, ...options });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"swaps.js","names":["_defineProperty","swapRouteFromQuote","validateFee","quote","assertCanonicalPair","tokenIn","tokenOut","Swaps","gateway","symbols","signer","walletAddress","userRef","resolveUniqueKeyPrefix","options","fee","amount","shadowFailures","uniqueKey","createUniqueKey","assertUniqueKey","tokenInSymbol","tradingSymbolOf","tokenOutSymbol","ordered","buildRequestTradeDto","signed","dto","result","readShadowSubmitOutcome","assertCanonicalV2Dto","GSwapSDKError","signPreSend","DEX_CONTRACT_PATH","pool","readOrderedSymbols","orderSymbols","SubmittedTransaction"],"sources":["../../../src/classes/swaps.ts"],"sourcesContent":["import {\n assertUniqueKey,\n createUniqueKey,\n resolveUniqueKeyPrefix,\n signPreSend,\n type GalaChainSigner,\n type HttpRequestor,\n type UniqueKeyOptions,\n type WireOptions,\n} from '@gala-chain/gala-core-sdk';\nimport type { SwapAmount } from '../types/amounts.js';\nimport type { FEE_TIER } from '../types/fees.js';\nimport { orderSymbols, readOrderedSymbols, type TokenRef } from '../utils/ordering.js';\nimport { assertCanonicalV2Dto, buildRequestTradeDto } from '../utils/dto_builders.js';\nimport { DEX_CONTRACT_PATH } from '../utils/chain_contract.js';\nimport { shadowFailures } from '../utils/shadow_errors.js';\nimport { assertCanonicalPair, userRef, validateFee } from '../utils/validation.js';\nimport type { ChainSubmitOptions } from './gateway.js';\nimport { GSwapSDKError } from './gswap_sdk_error.js';\nimport {\n readShadowSubmitOutcome,\n type ShadowSubmitOutcome,\n type ShadowSubmitResult,\n} from './shadow_submit.js';\nimport { SubmittedTransaction } from './submitted_transaction.js';\nimport type { ConfirmOptions } from '../types/confirm_options.js';\nimport type { RequestTradeDTO } from '../types/v2_dtos.js';\nimport type { AppliedTrade, QuoteResult } from '../types/v2_results.js';\nimport { tradingSymbolOf, type TokenResolver } from './symbols.js';\n\ninterface SwapGateway {\n submit: (\n method: 'RequestTrade',\n signedBody: Record<string, unknown>,\n options?: ChainSubmitOptions,\n ) => Promise<SubmittedTransaction<AppliedTrade>>;\n submitShadow: (\n method: 'RequestTrade',\n signedBody: Record<string, unknown>,\n options?: ChainSubmitOptions,\n ) => Promise<ShadowSubmitResult>;\n readonly dexBackendBaseUrl: string;\n readonly httpRequestor: HttpRequestor;\n readonly requestTimeoutMs: number;\n readonly wire?: WireOptions | undefined;\n}\n\n/** The canonical pool a swap trades in, as a quote names it. */\nexport interface SwapPool {\n token0: string;\n token1: string;\n}\n\n/** Options of a swap. */\nexport interface SwapOptions {\n /**\n * The attempt's `uniqueKey` (1-128 characters of `A-Za-z0-9._:-`). Default: a fresh\n * `<uniqueKeyPrefix><uuid>`. Supply one to know the key before signing (to remember it, to\n * show it), or to retry the SAME attempt after a lost response; never reuse it for another trade.\n */\n uniqueKey?: string | undefined;\n /**\n * Pin the pool: take `token0` / `token1` verbatim from the quote the user saw, instead of\n * ordering `tokenIn` / `tokenOut` by string comparison. `tokenIn` and `tokenOut` must be exactly\n * these two symbols, and `token0` must sort before `token1`. See {@link swapRouteFromQuote}.\n */\n pool?: SwapPool | undefined;\n}\n\n/** Everything {@link Swaps.swap} needs to trade in the pool a quote priced. */\nexport interface SwapRoute {\n tokenIn: string;\n tokenOut: string;\n fee: FEE_TIER;\n pool: SwapPool;\n}\n\n/**\n * The route of a quote: its fee tier and pool, with `tokenIn` / `tokenOut` taken from\n * `tokenInIsToken0`, nothing re-derived. Use it to swap in exactly the pool a quote priced:\n *\n * @example\n * ```ts\n * const route = swapRouteFromQuote(quote);\n * await swaps.swap(route.tokenIn, route.tokenOut, route.fee, { exactIn: '100' }, { pool: route.pool });\n * ```\n */\nexport function swapRouteFromQuote(\n quote: Pick<QuoteResult, 'token0Symbol' | 'token1Symbol' | 'tokenInIsToken0' | 'fee'>,\n): SwapRoute {\n validateFee(quote.fee);\n assertCanonicalPair(quote.token0Symbol, quote.token1Symbol);\n const [tokenIn, tokenOut] = quote.tokenInIsToken0\n ? [quote.token0Symbol, quote.token1Symbol]\n : [quote.token1Symbol, quote.token0Symbol];\n return {\n tokenIn,\n tokenOut,\n fee: quote.fee,\n pool: { token0: quote.token0Symbol, token1: quote.token1Symbol },\n };\n}\n\n/** Executes queued current-contract RequestTrade operations through the Chain Gateway. */\nexport class Swaps {\n /**\n * Creates a swap service.\n *\n * @example\n * ```ts\n * const swaps = new Swaps(gateway, symbols, signer, 'client|alice');\n * ```\n */\n constructor(\n private readonly gateway: SwapGateway,\n private readonly symbols: TokenResolver,\n private readonly signer?: GalaChainSigner,\n walletAddress?: string,\n options: UniqueKeyOptions = {},\n ) {\n this.walletAddress =\n walletAddress === undefined ? undefined : userRef(walletAddress, 'walletAddress');\n this.uniqueKeyPrefix = resolveUniqueKeyPrefix(options.uniqueKeyPrefix);\n }\n\n /** The signing wallet in the gateway's spelling (`eth|<EIP-55>` or `client|<id>`). */\n private readonly walletAddress: string | undefined;\n private readonly uniqueKeyPrefix: string;\n\n /**\n * Signs and submits an exact-input or exact-output RequestTrade DTO. The returned transaction\n * is QUEUED; call `await tx.confirm()` to obtain the executed trade or chain rejection.\n *\n * @example\n * ```ts\n * const submitted = await swaps.swap('GALA', 'GUSDC', 3000, {\n * exactIn: '100',\n * amountOutMinimum: '14',\n * });\n * ```\n */\n public async swap(\n tokenIn: TokenRef,\n tokenOut: TokenRef,\n fee: FEE_TIER,\n amount: SwapAmount,\n options?: SwapOptions,\n ): Promise<SubmittedTransaction<AppliedTrade>> {\n return this.submitSwap(await this.buildSwap(tokenIn, tokenOut, fee, amount, options));\n }\n\n /**\n * Signs and submits a SHADOW (dark) swap: the same RequestTrade as {@link swap}, relayed with\n * `X-Shadow: true` so the swap backend marks it private before it reaches the contract. Requires a\n * `walletAddress`. Resolves the normalized outcome instead of throwing on a gateway refusal:\n * `ok: false` carries `unavailable` (retryable), `disabled`, `rejected` (with the gateway's\n * message) or `unknown`. A success carries `queued`, to be confirmed with\n * `confirmSwap(queued.uniqueKey)`; never fall back to a public swap on failure. Whatever this\n * THROWS (a validation error, the signer, a lost connection, `SHADOW_NOT_QUEUED`) has\n * `error.shadow === true` (`isShadowError`), so an app can apply its dark-swap policy to it.\n *\n * @example\n * ```ts\n * const outcome = await swaps.swapShadow('GALA', 'GUSDC', 3000, { exactIn: '100' });\n * if (outcome.ok) await swaps.confirmSwap(outcome.queued.uniqueKey);\n * ```\n */\n public async swapShadow(\n tokenIn: TokenRef,\n tokenOut: TokenRef,\n fee: FEE_TIER,\n amount: SwapAmount,\n options?: SwapOptions,\n ): Promise<ShadowSubmitOutcome> {\n return shadowFailures(async () =>\n this.submitSwapShadow(await this.buildSwap(tokenIn, tokenOut, fee, amount, options)),\n );\n }\n\n /**\n * Builds the RequestTrade DTO that {@link swap} would sign, without signing or sending it: to\n * show in an approval popup, to remember its `uniqueKey`, or to sign elsewhere. Submit exactly\n * this DTO with {@link submitSwap}. The pure counterpart, with canonical inputs and no\n * resolution, is `buildRequestTradeDto`.\n *\n * @example\n * ```ts\n * const dto = await swaps.buildSwap('GALA', 'GUSDC', 3000, { exactIn: '100' });\n * showApproval(dto);\n * const submitted = await swaps.submitSwap(dto);\n * ```\n */\n public async buildSwap(\n tokenIn: TokenRef,\n tokenOut: TokenRef,\n fee: FEE_TIER,\n amount: SwapAmount,\n options?: SwapOptions,\n ): Promise<RequestTradeDTO> {\n validateFee(fee);\n const uniqueKey = options?.uniqueKey ?? createUniqueKey(this.uniqueKeyPrefix);\n assertUniqueKey(uniqueKey);\n const tokenInSymbol = await tradingSymbolOf(this.symbols, tokenIn);\n const tokenOutSymbol = await tradingSymbolOf(this.symbols, tokenOut);\n const ordered = this.orientPool(tokenInSymbol, tokenOutSymbol, options?.pool);\n return buildRequestTradeDto({\n token0: ordered.token0,\n token1: ordered.token1,\n fee,\n tokenInIsToken0: tokenInSymbol === ordered.token0,\n uniqueKey,\n ...amount,\n });\n }\n\n /**\n * Signs and submits a DTO built by {@link buildSwap} or `buildRequestTradeDto`. The DTO must be\n * exactly what the builder produces (no signature, canonical decimals, builder key order), so\n * what an app showed is what gets signed.\n *\n * @example\n * ```ts\n * const submitted = await swaps.submitSwap(dto);\n * const applied = await submitted.confirm();\n * ```\n */\n public async submitSwap(dto: RequestTradeDTO): Promise<SubmittedTransaction<AppliedTrade>> {\n const signed = await this.signRequestTrade(dto);\n return this.gateway.submit('RequestTrade', signed, this.submitOptions());\n }\n\n /** Signs and shadow-submits a DTO built by {@link buildSwap}; see {@link swapShadow}. */\n public async submitSwapShadow(dto: RequestTradeDTO): Promise<ShadowSubmitOutcome> {\n return shadowFailures(async () => {\n const signed = await this.signRequestTrade(dto);\n const result = await this.gateway.submitShadow('RequestTrade', signed, this.submitOptions());\n return readShadowSubmitOutcome('RequestTrade', signed, result);\n });\n }\n\n private submitOptions(): ChainSubmitOptions {\n return this.walletAddress === undefined ? {} : { walletAddress: this.walletAddress };\n }\n\n private async signRequestTrade(dto: RequestTradeDTO): Promise<Record<string, unknown>> {\n assertCanonicalV2Dto('RequestTrade', dto);\n if (this.signer === undefined) throw GSwapSDKError.noSignerError();\n return signPreSend(this.signer, DEX_CONTRACT_PATH, 'RequestTrade', dto);\n }\n\n /** The pool's two symbols in contract order: the quote's own when pinned, else string order. */\n private orientPool(\n tokenInSymbol: string,\n tokenOutSymbol: string,\n pool: SwapPool | undefined,\n ): SwapPool {\n if (pool === undefined) {\n return readOrderedSymbols(orderSymbols(tokenInSymbol, tokenOutSymbol));\n }\n const sameSet =\n (tokenInSymbol === pool.token0 && tokenOutSymbol === pool.token1) ||\n (tokenInSymbol === pool.token1 && tokenOutSymbol === pool.token0);\n if (!sameSet) {\n throw new GSwapSDKError('POOL_MISMATCH', {\n tokenIn: tokenInSymbol,\n tokenOut: tokenOutSymbol,\n poolToken0: pool.token0,\n poolToken1: pool.token1,\n });\n }\n return { token0: pool.token0, token1: pool.token1 };\n }\n\n /**\n * Confirm a swap by its durable `uniqueKey` without resubmitting it. This is the recovery\n * path after a `CONFIRMATION_TIMEOUT`, a `SUBMISSION_OUTCOME_UNKNOWN`, a `409` replay\n * conflict, or a process restart: it reads the same apply-results record that\n * `SubmittedTransaction.confirm()` reads (retained for 7 days) and resolves the applied trade\n * or throws the chain's rejection. The first read happens immediately unless\n * `initialDelayMs` is given.\n *\n * @example\n * ```ts\n * const applied = await swaps.confirmSwap('galaswap-operation-2f1c…');\n * ```\n */\n public async confirmSwap(uniqueKey: string, options?: ConfirmOptions): Promise<AppliedTrade> {\n assertUniqueKey(uniqueKey);\n const transaction = new SubmittedTransaction<AppliedTrade>({\n method: 'RequestTrade',\n uniqueKey,\n transactionId: null,\n mode: 'queued',\n result: undefined,\n dexBackendBaseUrl: this.gateway.dexBackendBaseUrl,\n httpRequestor: this.gateway.httpRequestor,\n requestTimeoutMs: this.gateway.requestTimeoutMs,\n wire: this.gateway.wire,\n });\n return transaction.confirm({ initialDelayMs: 0, ...options });\n }\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import e from"../_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/defineProperty.js";import{GSwapSDKError as t}from"./gswap_sdk_error.js";import{isProposableTradingSymbol as n}from"../utils/validation.js";import{HttpClient as r}from"./http_client.js";import{asEntryRecord as i,entryOk as a,entryRejected as o,readEntries as s}from"../utils/lenient.js";import{orderSymbols as c,parseTokenClassKey as l}from"../utils/ordering.js";import{resolvePreSend as u}from"../utils/resolve_pre_send.js";import{compositeKeyOf as d}from"@gala-chain/gala-core-sdk";var f=class{constructor(t,n={}){this.gateway=t,this.options=n,e(this,`cachedSymbols`,void 0),e(this,`cachedAt`,0),e(this,`refreshInFlight`,void 0),e(this,`http`,void 0),this.http=new r(t.httpRequestor,t.requestTimeoutMs,t.wire)}async list(){return this.cachedSymbols!==void 0&&Date.now()-this.cachedAt<6e4?this.cachedSymbols:(this.refreshInFlight===void 0&&(this.refreshInFlight=this.http.sendGetRequest(this.gateway.dexBackendBaseUrl,`/v2/trade`,`/symbols`).then(e=>s(`symbols`,e.data,p,this.options.onInvalidEntry)).then(e=>(this.cachedSymbols=e,this.cachedAt=Date.now(),e)).finally(()=>{this.refreshInFlight=void 0})),this.refreshInFlight)}invalidate(){this.cachedSymbols=void 0,this.cachedAt=0}async refresh(){return this.invalidate(),this.refreshInFlight=void 0,this.list()}async resolve(e){let n=await this.list(),r=typeof e==`string`?m(e)?d(l(e)):void 0:d(e),i=n.find(t=>t.symbol===e||r!==void 0&&d(t)===r);if(i===void 0){n=await this.refresh();let i=n.find(t=>t.symbol===e||r!==void 0&&d(t)===r);if(i===void 0)throw t.unknownTokenError(e);return i}return i}async proposeSymbol(e,r,i={}){let a=[];try{a=await this.list()}catch(e){if(i.registryFailure!==`ignore`)throw e}let o=d(e),s=a.find(t=>t.collection===e.collection&&t.category===e.category&&t.type===e.type&&t.additionalKey===e.additionalKey);if(s!==void 0)return s.symbol;if(n(r))return r;throw new t(`NO_VALID_TRADING_SYMBOL`,{classKey:o,displaySymbol:r})}async orderPair(e,t){let[n,r]=await Promise.all([this.resolve(e),this.resolve(t)]),[i,a]=c(n.symbol,r.symbol);return{token0:i===n.symbol?n:r,token1:a===r.symbol?r:n,flipped:i!==n.symbol}}};function p(e){let t=i(e);if(t===void 0)return o(`the row is not an object`);let{symbol:n,collection:r,category:s,type:c,additionalKey:l,decimals:u}=t;return typeof n==`string`?n.length===0?o(`symbol is empty`):typeof r==`string`?typeof s==`string`?typeof c==`string`?typeof l==`string`?typeof u!=`number`||!Number.isInteger(u)||u<0?o(`decimals is not a non-negative integer`):a({symbol:n,collection:r,category:s,type:c,additionalKey:l,decimals:u}):o(`additionalKey is not a string`):o(`type is not a string`):o(`category is not a string`):o(`collection is not a string`):o(`symbol is not a string`)}function m(e){return e.includes(`|`)||e.includes(`$`)}function h(e){return typeof e==`string`?e:d(e)}async function g(e,n){let r=await u(h(n),()=>e.resolve(n));if(r.symbol.length>0)return r.symbol;throw new t(`SYMBOL_NOT_FOUND`,{token:h(n)})}async function _(e,t){return e.passthrough===!0?h(t):g(e,t)}var v=class{constructor(){e(this,`passthrough`,!0)}resolve(e){return typeof e==`string`&&!m(e)?Promise.resolve({symbol:e,collection:``,category:``,type:``,additionalKey:``,decimals:0}):Promise.reject(t.unknownTokenError(e))}async orderPair(e,t){let[n,r]=await Promise.all([this.resolve(e),this.resolve(t)]),[i]=c(n.symbol,r.symbol),a=i!==n.symbol;return{token0:a?r:n,token1:a?n:r,flipped:a}}invalidate(){}};export{v as CanonicalSymbols,f as Symbols,_ as queryTokenOf,h as rawTokenOf,g as tradingSymbolOf};
1
+ import e from"../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/defineProperty.js";import{GSwapSDKError as t}from"./gswap_sdk_error.js";import{isProposableTradingSymbol as n}from"../utils/validation.js";import{HttpClient as r}from"./http_client.js";import{asEntryRecord as i,entryOk as a,entryRejected as o,readEntries as s}from"../utils/lenient.js";import{orderSymbols as c,parseTokenClassKey as l}from"../utils/ordering.js";import{resolvePreSend as u}from"../utils/resolve_pre_send.js";import{compositeKeyOf as d}from"@gala-chain/gala-core-sdk";var f=class{constructor(t,n={}){this.gateway=t,this.options=n,e(this,`cachedSymbols`,void 0),e(this,`cachedAt`,0),e(this,`refreshInFlight`,void 0),e(this,`http`,void 0),this.http=new r(t.httpRequestor,t.requestTimeoutMs,t.wire)}async list(){return this.cachedSymbols!==void 0&&Date.now()-this.cachedAt<6e4?this.cachedSymbols:(this.refreshInFlight===void 0&&(this.refreshInFlight=this.http.sendGetRequest(this.gateway.dexBackendBaseUrl,`/v2/trade`,`/symbols`).then(e=>s(`symbols`,e.data,p,this.options.onInvalidEntry)).then(e=>(this.cachedSymbols=e,this.cachedAt=Date.now(),e)).finally(()=>{this.refreshInFlight=void 0})),this.refreshInFlight)}invalidate(){this.cachedSymbols=void 0,this.cachedAt=0}async refresh(){return this.invalidate(),this.refreshInFlight=void 0,this.list()}async resolve(e){let n=await this.list(),r=typeof e==`string`?m(e)?d(l(e)):void 0:d(e),i=n.find(t=>t.symbol===e||r!==void 0&&d(t)===r);if(i===void 0){n=await this.refresh();let i=n.find(t=>t.symbol===e||r!==void 0&&d(t)===r);if(i===void 0)throw t.unknownTokenError(e);return i}return i}async proposeSymbol(e,r,i={}){let a=[];try{a=await this.list()}catch(e){if(i.registryFailure!==`ignore`)throw e}let o=d(e),s=a.find(t=>t.collection===e.collection&&t.category===e.category&&t.type===e.type&&t.additionalKey===e.additionalKey);if(s!==void 0)return s.symbol;if(n(r))return r;throw new t(`NO_VALID_TRADING_SYMBOL`,{classKey:o,displaySymbol:r})}async orderPair(e,t){let[n,r]=await Promise.all([this.resolve(e),this.resolve(t)]),[i,a]=c(n.symbol,r.symbol);return{token0:i===n.symbol?n:r,token1:a===r.symbol?r:n,flipped:i!==n.symbol}}};function p(e){let t=i(e);if(t===void 0)return o(`the row is not an object`);let{symbol:n,collection:r,category:s,type:c,additionalKey:l,decimals:u}=t;return typeof n==`string`?n.length===0?o(`symbol is empty`):typeof r==`string`?typeof s==`string`?typeof c==`string`?typeof l==`string`?typeof u!=`number`||!Number.isInteger(u)||u<0?o(`decimals is not a non-negative integer`):a({symbol:n,collection:r,category:s,type:c,additionalKey:l,decimals:u}):o(`additionalKey is not a string`):o(`type is not a string`):o(`category is not a string`):o(`collection is not a string`):o(`symbol is not a string`)}function m(e){return e.includes(`|`)||e.includes(`$`)}function h(e){return typeof e==`string`?e:d(e)}async function g(e,n){let r=await u(h(n),()=>e.resolve(n));if(r.symbol.length>0)return r.symbol;throw new t(`SYMBOL_NOT_FOUND`,{token:h(n)})}async function _(e,t){return e.passthrough===!0?h(t):g(e,t)}var v=class{constructor(){e(this,`passthrough`,!0)}resolve(e){return typeof e==`string`&&!m(e)?Promise.resolve({symbol:e,collection:``,category:``,type:``,additionalKey:``,decimals:0}):Promise.reject(t.unknownTokenError(e))}async orderPair(e,t){let[n,r]=await Promise.all([this.resolve(e),this.resolve(t)]),[i]=c(n.symbol,r.symbol),a=i!==n.symbol;return{token0:a?r:n,token1:a?n:r,flipped:a}}invalidate(){}};export{v as CanonicalSymbols,f as Symbols,_ as queryTokenOf,h as rawTokenOf,g as tradingSymbolOf};
2
2
  //# sourceMappingURL=symbols.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"symbols.js","names":["_defineProperty","Symbols","gateway","options","HttpClient","readEntries","response","parseTradingSymbol","symbols","composite","ref","isCompositeKey","compositeKeyOf","parseTokenClassKey","resolved","entry","refreshed","GSwapSDKError","registry","error","wanted","classKey","registered","isProposableTradingSymbol","displaySymbol","resolvedA","resolvedB","a","b","token0Symbol","token1Symbol","orderSymbols","asEntryRecord","value","entryRejected","symbol","collection","category","type","additionalKey","decimals","entryOk","rawTokenOf","tradingSymbolOf","resolvePreSend","resolver","queryTokenOf","CanonicalSymbols","first","flipped"],"sources":["../../../src/classes/symbols.ts"],"sourcesContent":["import {\n compositeKeyOf,\n type GalaChainTokenClassKey,\n type WireOptions,\n} from '@gala-chain/gala-core-sdk';\nimport type { TradingSymbol } from '../types/v2_results.js';\nimport {\n asEntryRecord,\n entryOk,\n entryRejected,\n readEntries,\n type EntryParse,\n type InvalidEntryHandler,\n} from '../utils/lenient.js';\nimport type { TokenRef } from '../utils/ordering.js';\nimport { orderSymbols, parseTokenClassKey } from '../utils/ordering.js';\nimport { resolvePreSend } from '../utils/resolve_pre_send.js';\nimport { isProposableTradingSymbol } from '../utils/validation.js';\nimport { GSwapSDKError } from './gswap_sdk_error.js';\nimport type { ChainGateway } from './gateway.js';\nimport { HttpClient } from './http_client.js';\n\nconst SYMBOL_CACHE_TTL_MS = 60_000;\n\n/** Symbol registry client for the current GalaChainDex contract. */\nexport class Symbols {\n private cachedSymbols: TradingSymbol[] | undefined;\n private cachedAt = 0;\n private refreshInFlight: Promise<TradingSymbol[]> | undefined;\n\n private readonly http: HttpClient;\n\n /**\n * Create a symbol service backed by the swap backend. `onInvalidEntry` is told about every\n * registry row that was skipped as unusable (see {@link InvalidEntryHandler}).\n */\n constructor(\n private readonly gateway: Pick<ChainGateway, 'httpRequestor' | 'dexBackendBaseUrl'> & {\n requestTimeoutMs?: number | undefined;\n wire?: WireOptions | undefined;\n },\n private readonly options: { onInvalidEntry?: InvalidEntryHandler | undefined } = {},\n ) {\n this.http = new HttpClient(gateway.httpRequestor, gateway.requestTimeoutMs, gateway.wire);\n }\n\n /**\n * List all registered GalaChainDex trading symbols, cached for 60 seconds. A registry row that\n * is not a usable symbol is skipped (and reported to `onInvalidEntry`) instead of failing the\n * whole list.\n */\n public async list(): Promise<TradingSymbol[]> {\n if (this.cachedSymbols !== undefined && Date.now() - this.cachedAt < SYMBOL_CACHE_TTL_MS) {\n return this.cachedSymbols;\n }\n if (this.refreshInFlight !== undefined) return this.refreshInFlight;\n this.refreshInFlight = this.http\n .sendGetRequest<{ data: unknown }>(this.gateway.dexBackendBaseUrl, '/v2/trade', '/symbols')\n .then((response) =>\n readEntries('symbols', response.data, parseTradingSymbol, this.options.onInvalidEntry),\n )\n .then((symbols) => {\n this.cachedSymbols = symbols;\n this.cachedAt = Date.now();\n return symbols;\n })\n .finally(() => {\n this.refreshInFlight = undefined;\n });\n return this.refreshInFlight;\n }\n\n /** Invalidate the cached symbol registry after a write changes registration state.\n *\n * @example\n * ```ts\n * gSwap.symbols.invalidate();\n * ```\n */\n public invalidate(): void {\n this.cachedSymbols = undefined;\n this.cachedAt = 0;\n }\n\n /** Force one registry refresh, bypassing the cache.\n *\n * @example\n * ```ts\n * const symbols = await gSwap.symbols.refresh();\n * ```\n */\n public async refresh(): Promise<TradingSymbol[]> {\n this.invalidate();\n // list() would otherwise return an ALREADY in-flight refresh that started before this\n // call (built from pre-invalidation state) — bypassing it too is what makes this\n // actually force a fresh fetch, not just satisfy the cache-freshness check.\n this.refreshInFlight = undefined;\n return this.list();\n }\n\n /** Resolve a symbol or token class key to its registered symbol metadata. */\n public async resolve(ref: TokenRef): Promise<TradingSymbol> {\n let symbols = await this.list();\n const composite =\n typeof ref === 'string'\n ? isCompositeKey(ref)\n ? compositeKeyOf(parseTokenClassKey(ref))\n : undefined\n : compositeKeyOf(ref);\n const resolved = symbols.find(\n (entry) =>\n entry.symbol === ref || (composite !== undefined && compositeKeyOf(entry) === composite),\n );\n if (resolved === undefined) {\n symbols = await this.refresh();\n const refreshed = symbols.find(\n (entry) =>\n entry.symbol === ref || (composite !== undefined && compositeKeyOf(entry) === composite),\n );\n if (refreshed === undefined) throw GSwapSDKError.unknownTokenError(ref);\n return refreshed;\n }\n return resolved;\n }\n\n /**\n * The trading symbol to put in a token's first CreatePool.\n *\n * 1. The symbol ALREADY registered for exactly this class key, if any: proposing another one\n * fails the gateway's registry-conflict check.\n * 2. Otherwise `displaySymbol` (the token's curated symbol), when it is proposable\n * ({@link isProposableTradingSymbol}).\n * 3. Otherwise a `NO_VALID_TRADING_SYMBOL` error: a symbol is never derived from the key (BNB's collection is\n * `GBNB`, a launchpad token's is the reserved word `Token`).\n *\n * `registryFailure: 'ignore'` treats an unreachable registry as an empty one and falls through\n * to step 2 (the gateway still rejects a conflicting symbol); the default `'throw'` propagates\n * the failure.\n *\n * @example\n * ```ts\n * const symbol = await gSwap.symbols.proposeSymbol(\n * { collection: 'GALA_TEST1', category: 'Unit', type: 'none', additionalKey: 'none' },\n * 'GALATEST',\n * );\n * ```\n */\n public async proposeSymbol(\n classKey: GalaChainTokenClassKey,\n displaySymbol: string,\n options: { registryFailure?: 'throw' | 'ignore' | undefined } = {},\n ): Promise<string> {\n let registry: TradingSymbol[] = [];\n try {\n registry = await this.list();\n } catch (error: unknown) {\n if (options.registryFailure !== 'ignore') throw error;\n }\n const wanted = compositeKeyOf(classKey);\n const registered = registry.find(\n (entry) =>\n entry.collection === classKey.collection &&\n entry.category === classKey.category &&\n entry.type === classKey.type &&\n entry.additionalKey === classKey.additionalKey,\n );\n if (registered !== undefined) return registered.symbol;\n if (isProposableTradingSymbol(displaySymbol)) return displaySymbol;\n throw new GSwapSDKError('NO_VALID_TRADING_SYMBOL', { classKey: wanted, displaySymbol });\n }\n\n /** Resolve and put two token references in canonical trading-symbol order. */\n public async orderPair(\n a: TokenRef,\n b: TokenRef,\n ): Promise<{\n token0: TradingSymbol;\n token1: TradingSymbol;\n flipped: boolean;\n }> {\n const [resolvedA, resolvedB] = await Promise.all([this.resolve(a), this.resolve(b)]);\n const [token0Symbol, token1Symbol] = orderSymbols(resolvedA.symbol, resolvedB.symbol);\n return {\n token0: token0Symbol === resolvedA.symbol ? resolvedA : resolvedB,\n token1: token1Symbol === resolvedB.symbol ? resolvedB : resolvedA,\n flipped: token0Symbol !== resolvedA.symbol,\n };\n }\n}\n\n/** Reads one registry row: all four class-key parts, a symbol and non-negative integer decimals. */\nfunction parseTradingSymbol(value: unknown): EntryParse<TradingSymbol> {\n const entry = asEntryRecord(value);\n if (entry === undefined) return entryRejected('the row is not an object');\n const { symbol, collection, category, type, additionalKey, decimals } = entry;\n if (typeof symbol !== 'string') return entryRejected('symbol is not a string');\n if (symbol.length === 0) return entryRejected('symbol is empty');\n if (typeof collection !== 'string') return entryRejected('collection is not a string');\n if (typeof category !== 'string') return entryRejected('category is not a string');\n if (typeof type !== 'string') return entryRejected('type is not a string');\n if (typeof additionalKey !== 'string') return entryRejected('additionalKey is not a string');\n if (typeof decimals !== 'number' || !Number.isInteger(decimals) || decimals < 0) {\n return entryRejected('decimals is not a non-negative integer');\n }\n return entryOk({ symbol, collection, category, type, additionalKey, decimals });\n}\n\nfunction isCompositeKey(ref: string): boolean {\n return ref.includes('|') || ref.includes('$');\n}\n\n/**\n * What the services need from a token resolver: how a token reference becomes a trading symbol,\n * and whether requests carry the caller's references untouched. {@link Symbols} is the registry\n * resolver (one `GET /v2/trade/symbols`, cached); {@link CanonicalSymbols} is the offline one.\n */\nexport interface TokenResolver {\n resolve(ref: TokenRef): Promise<TradingSymbol>;\n /**\n * True when reads send the caller's token references verbatim (a symbol, or a composite key as\n * the caller wrote it) and let the backend resolve and orient the pair — no registry request\n * and no client-side ordering or tick flip.\n */\n readonly passthrough?: boolean | undefined;\n}\n\n/** A token reference as the caller wrote it: a string verbatim, a class key as its `$` composite. */\nexport function rawTokenOf(ref: TokenRef): string {\n return typeof ref === 'string' ? ref : compositeKeyOf(ref);\n}\n\n/** The trading symbol of a reference, or `SYMBOL_NOT_FOUND` when the resolver has none. */\nexport async function tradingSymbolOf(resolver: TokenResolver, ref: TokenRef): Promise<string> {\n const resolved = await resolvePreSend(rawTokenOf(ref), () => resolver.resolve(ref));\n if (resolved.symbol.length > 0) return resolved.symbol;\n throw new GSwapSDKError('SYMBOL_NOT_FOUND', { token: rawTokenOf(ref) });\n}\n\n/** What a read request names a token by: the reference itself in pass-through mode, else its symbol. */\nexport async function queryTokenOf(resolver: TokenResolver, ref: TokenRef): Promise<string> {\n return resolver.passthrough === true ? rawTokenOf(ref) : tradingSymbolOf(resolver, ref);\n}\n\n/**\n * The offline resolver for apps that already hold the backend's canonical trading symbols (from\n * `/quote`, `/add-liq-estimate`, a position row) and must not make a registry request of their own.\n *\n * - `resolve` accepts a bare trading symbol and returns it as is; a class key (object, `$` or `|`\n * form) is not a symbol and is refused as `UNKNOWN_TOKEN`. `createPool` relies on that refusal to\n * fall back to the symbols its caller supplies.\n * - `orderPair` orders two symbols by plain string comparison, as the contract does.\n * - Reads are pass-through (see {@link TokenResolver.passthrough}): the caller's references go on\n * the wire verbatim, composite keys included, and the backend orients the pair.\n *\n * @example\n * ```ts\n * const swaps = new Swaps(gateway, new CanonicalSymbols(), signer, walletAddress);\n * await swaps.swap('GALA', 'GUSDC', 3000, { exactIn: '10' });\n * ```\n */\nexport class CanonicalSymbols implements TokenResolver {\n public readonly passthrough = true;\n\n /** Resolve a bare trading symbol to itself. */\n public resolve(ref: TokenRef): Promise<TradingSymbol> {\n if (typeof ref === 'string' && !isCompositeKey(ref)) {\n return Promise.resolve({\n symbol: ref,\n collection: '',\n category: '',\n type: '',\n additionalKey: '',\n decimals: 0,\n });\n }\n return Promise.reject(GSwapSDKError.unknownTokenError(ref));\n }\n\n /** Put two symbols in canonical (string) order. */\n public async orderPair(\n a: TokenRef,\n b: TokenRef,\n ): Promise<{ token0: TradingSymbol; token1: TradingSymbol; flipped: boolean }> {\n const [resolvedA, resolvedB] = await Promise.all([this.resolve(a), this.resolve(b)]);\n const [first] = orderSymbols(resolvedA.symbol, resolvedB.symbol);\n const flipped = first !== resolvedA.symbol;\n return {\n token0: flipped ? resolvedB : resolvedA,\n token1: flipped ? resolvedA : resolvedB,\n flipped,\n };\n }\n\n /** There is no registry state to drop. */\n public invalidate(): void {\n // Nothing is cached: every answer is derived from the reference itself.\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"symbols.js","names":["_defineProperty","Symbols","gateway","options","HttpClient","readEntries","response","parseTradingSymbol","symbols","composite","ref","isCompositeKey","compositeKeyOf","parseTokenClassKey","resolved","entry","refreshed","GSwapSDKError","registry","error","wanted","classKey","registered","isProposableTradingSymbol","displaySymbol","resolvedA","resolvedB","a","b","token0Symbol","token1Symbol","orderSymbols","asEntryRecord","value","entryRejected","symbol","collection","category","type","additionalKey","decimals","entryOk","rawTokenOf","tradingSymbolOf","resolvePreSend","resolver","queryTokenOf","CanonicalSymbols","first","flipped"],"sources":["../../../src/classes/symbols.ts"],"sourcesContent":["import {\n compositeKeyOf,\n type GalaChainTokenClassKey,\n type WireOptions,\n} from '@gala-chain/gala-core-sdk';\nimport type { TradingSymbol } from '../types/v2_results.js';\nimport {\n asEntryRecord,\n entryOk,\n entryRejected,\n readEntries,\n type EntryParse,\n type InvalidEntryHandler,\n} from '../utils/lenient.js';\nimport type { TokenRef } from '../utils/ordering.js';\nimport { orderSymbols, parseTokenClassKey } from '../utils/ordering.js';\nimport { resolvePreSend } from '../utils/resolve_pre_send.js';\nimport { isProposableTradingSymbol } from '../utils/validation.js';\nimport { GSwapSDKError } from './gswap_sdk_error.js';\nimport type { ChainGateway } from './gateway.js';\nimport { HttpClient } from './http_client.js';\n\nconst SYMBOL_CACHE_TTL_MS = 60_000;\n\n/** Symbol registry client for the current GalaChainDex contract. */\nexport class Symbols {\n private cachedSymbols: TradingSymbol[] | undefined;\n private cachedAt = 0;\n private refreshInFlight: Promise<TradingSymbol[]> | undefined;\n\n private readonly http: HttpClient;\n\n /**\n * Create a symbol service backed by the swap backend. `onInvalidEntry` is told about every\n * registry row that was skipped as unusable (see {@link InvalidEntryHandler}).\n */\n constructor(\n private readonly gateway: Pick<ChainGateway, 'httpRequestor' | 'dexBackendBaseUrl'> & {\n requestTimeoutMs?: number | undefined;\n wire?: WireOptions | undefined;\n },\n private readonly options: { onInvalidEntry?: InvalidEntryHandler | undefined } = {},\n ) {\n this.http = new HttpClient(gateway.httpRequestor, gateway.requestTimeoutMs, gateway.wire);\n }\n\n /**\n * List all registered GalaChainDex trading symbols, cached for 60 seconds. A registry row that\n * is not a usable symbol is skipped (and reported to `onInvalidEntry`) instead of failing the\n * whole list.\n */\n public async list(): Promise<TradingSymbol[]> {\n if (this.cachedSymbols !== undefined && Date.now() - this.cachedAt < SYMBOL_CACHE_TTL_MS) {\n return this.cachedSymbols;\n }\n if (this.refreshInFlight !== undefined) return this.refreshInFlight;\n this.refreshInFlight = this.http\n .sendGetRequest<{ data: unknown }>(this.gateway.dexBackendBaseUrl, '/v2/trade', '/symbols')\n .then((response) =>\n readEntries('symbols', response.data, parseTradingSymbol, this.options.onInvalidEntry),\n )\n .then((symbols) => {\n this.cachedSymbols = symbols;\n this.cachedAt = Date.now();\n return symbols;\n })\n .finally(() => {\n this.refreshInFlight = undefined;\n });\n return this.refreshInFlight;\n }\n\n /** Invalidate the cached symbol registry after a write changes registration state.\n *\n * @example\n * ```ts\n * gSwap.symbols.invalidate();\n * ```\n */\n public invalidate(): void {\n this.cachedSymbols = undefined;\n this.cachedAt = 0;\n }\n\n /** Force one registry refresh, bypassing the cache.\n *\n * @example\n * ```ts\n * const symbols = await gSwap.symbols.refresh();\n * ```\n */\n public async refresh(): Promise<TradingSymbol[]> {\n this.invalidate();\n // list() would otherwise return an ALREADY in-flight refresh that started before this\n // call (built from pre-invalidation state) — bypassing it too is what makes this\n // actually force a fresh fetch, not just satisfy the cache-freshness check.\n this.refreshInFlight = undefined;\n return this.list();\n }\n\n /** Resolve a symbol or token class key to its registered symbol metadata. */\n public async resolve(ref: TokenRef): Promise<TradingSymbol> {\n let symbols = await this.list();\n const composite =\n typeof ref === 'string'\n ? isCompositeKey(ref)\n ? compositeKeyOf(parseTokenClassKey(ref))\n : undefined\n : compositeKeyOf(ref);\n const resolved = symbols.find(\n (entry) =>\n entry.symbol === ref || (composite !== undefined && compositeKeyOf(entry) === composite),\n );\n if (resolved === undefined) {\n symbols = await this.refresh();\n const refreshed = symbols.find(\n (entry) =>\n entry.symbol === ref || (composite !== undefined && compositeKeyOf(entry) === composite),\n );\n if (refreshed === undefined) throw GSwapSDKError.unknownTokenError(ref);\n return refreshed;\n }\n return resolved;\n }\n\n /**\n * The trading symbol to put in a token's first CreatePool.\n *\n * 1. The symbol ALREADY registered for exactly this class key, if any: proposing another one\n * fails the gateway's registry-conflict check.\n * 2. Otherwise `displaySymbol` (the token's curated symbol), when it is proposable\n * ({@link isProposableTradingSymbol}).\n * 3. Otherwise a `NO_VALID_TRADING_SYMBOL` error: a symbol is never derived from the key (BNB's collection is\n * `GBNB`, a launchpad token's is the reserved word `Token`).\n *\n * `registryFailure: 'ignore'` treats an unreachable registry as an empty one and falls through\n * to step 2 (the gateway still rejects a conflicting symbol); the default `'throw'` propagates\n * the failure.\n *\n * @example\n * ```ts\n * const symbol = await gSwap.symbols.proposeSymbol(\n * { collection: 'GALA_TEST1', category: 'Unit', type: 'none', additionalKey: 'none' },\n * 'GALATEST',\n * );\n * ```\n */\n public async proposeSymbol(\n classKey: GalaChainTokenClassKey,\n displaySymbol: string,\n options: { registryFailure?: 'throw' | 'ignore' | undefined } = {},\n ): Promise<string> {\n let registry: TradingSymbol[] = [];\n try {\n registry = await this.list();\n } catch (error: unknown) {\n if (options.registryFailure !== 'ignore') throw error;\n }\n const wanted = compositeKeyOf(classKey);\n const registered = registry.find(\n (entry) =>\n entry.collection === classKey.collection &&\n entry.category === classKey.category &&\n entry.type === classKey.type &&\n entry.additionalKey === classKey.additionalKey,\n );\n if (registered !== undefined) return registered.symbol;\n if (isProposableTradingSymbol(displaySymbol)) return displaySymbol;\n throw new GSwapSDKError('NO_VALID_TRADING_SYMBOL', { classKey: wanted, displaySymbol });\n }\n\n /** Resolve and put two token references in canonical trading-symbol order. */\n public async orderPair(\n a: TokenRef,\n b: TokenRef,\n ): Promise<{\n token0: TradingSymbol;\n token1: TradingSymbol;\n flipped: boolean;\n }> {\n const [resolvedA, resolvedB] = await Promise.all([this.resolve(a), this.resolve(b)]);\n const [token0Symbol, token1Symbol] = orderSymbols(resolvedA.symbol, resolvedB.symbol);\n return {\n token0: token0Symbol === resolvedA.symbol ? resolvedA : resolvedB,\n token1: token1Symbol === resolvedB.symbol ? resolvedB : resolvedA,\n flipped: token0Symbol !== resolvedA.symbol,\n };\n }\n}\n\n/** Reads one registry row: all four class-key parts, a symbol and non-negative integer decimals. */\nfunction parseTradingSymbol(value: unknown): EntryParse<TradingSymbol> {\n const entry = asEntryRecord(value);\n if (entry === undefined) return entryRejected('the row is not an object');\n const { symbol, collection, category, type, additionalKey, decimals } = entry;\n if (typeof symbol !== 'string') return entryRejected('symbol is not a string');\n if (symbol.length === 0) return entryRejected('symbol is empty');\n if (typeof collection !== 'string') return entryRejected('collection is not a string');\n if (typeof category !== 'string') return entryRejected('category is not a string');\n if (typeof type !== 'string') return entryRejected('type is not a string');\n if (typeof additionalKey !== 'string') return entryRejected('additionalKey is not a string');\n if (typeof decimals !== 'number' || !Number.isInteger(decimals) || decimals < 0) {\n return entryRejected('decimals is not a non-negative integer');\n }\n return entryOk({ symbol, collection, category, type, additionalKey, decimals });\n}\n\nfunction isCompositeKey(ref: string): boolean {\n return ref.includes('|') || ref.includes('$');\n}\n\n/**\n * What the services need from a token resolver: how a token reference becomes a trading symbol,\n * and whether requests carry the caller's references untouched. {@link Symbols} is the registry\n * resolver (one `GET /v2/trade/symbols`, cached); {@link CanonicalSymbols} is the offline one.\n */\nexport interface TokenResolver {\n resolve(ref: TokenRef): Promise<TradingSymbol>;\n /**\n * True when reads send the caller's token references verbatim (a symbol, or a composite key as\n * the caller wrote it) and let the backend resolve and orient the pair — no registry request\n * and no client-side ordering or tick flip.\n */\n readonly passthrough?: boolean | undefined;\n}\n\n/** A token reference as the caller wrote it: a string verbatim, a class key as its `$` composite. */\nexport function rawTokenOf(ref: TokenRef): string {\n return typeof ref === 'string' ? ref : compositeKeyOf(ref);\n}\n\n/** The trading symbol of a reference, or `SYMBOL_NOT_FOUND` when the resolver has none. */\nexport async function tradingSymbolOf(resolver: TokenResolver, ref: TokenRef): Promise<string> {\n const resolved = await resolvePreSend(rawTokenOf(ref), () => resolver.resolve(ref));\n if (resolved.symbol.length > 0) return resolved.symbol;\n throw new GSwapSDKError('SYMBOL_NOT_FOUND', { token: rawTokenOf(ref) });\n}\n\n/** What a read request names a token by: the reference itself in pass-through mode, else its symbol. */\nexport async function queryTokenOf(resolver: TokenResolver, ref: TokenRef): Promise<string> {\n return resolver.passthrough === true ? rawTokenOf(ref) : tradingSymbolOf(resolver, ref);\n}\n\n/**\n * The offline resolver for apps that already hold the backend's canonical trading symbols (from\n * `/quote`, `/add-liq-estimate`, a position row) and must not make a registry request of their own.\n *\n * - `resolve` accepts a bare trading symbol and returns it as is; a class key (object, `$` or `|`\n * form) is not a symbol and is refused as `UNKNOWN_TOKEN`. `createPool` relies on that refusal to\n * fall back to the symbols its caller supplies.\n * - `orderPair` orders two symbols by plain string comparison, as the contract does.\n * - Reads are pass-through (see {@link TokenResolver.passthrough}): the caller's references go on\n * the wire verbatim, composite keys included, and the backend orients the pair.\n *\n * @example\n * ```ts\n * const swaps = new Swaps(gateway, new CanonicalSymbols(), signer, walletAddress);\n * await swaps.swap('GALA', 'GUSDC', 3000, { exactIn: '10' });\n * ```\n */\nexport class CanonicalSymbols implements TokenResolver {\n public readonly passthrough = true;\n\n /** Resolve a bare trading symbol to itself. */\n public resolve(ref: TokenRef): Promise<TradingSymbol> {\n if (typeof ref === 'string' && !isCompositeKey(ref)) {\n return Promise.resolve({\n symbol: ref,\n collection: '',\n category: '',\n type: '',\n additionalKey: '',\n decimals: 0,\n });\n }\n return Promise.reject(GSwapSDKError.unknownTokenError(ref));\n }\n\n /** Put two symbols in canonical (string) order. */\n public async orderPair(\n a: TokenRef,\n b: TokenRef,\n ): Promise<{ token0: TradingSymbol; token1: TradingSymbol; flipped: boolean }> {\n const [resolvedA, resolvedB] = await Promise.all([this.resolve(a), this.resolve(b)]);\n const [first] = orderSymbols(resolvedA.symbol, resolvedB.symbol);\n const flipped = first !== resolvedA.symbol;\n return {\n token0: flipped ? resolvedB : resolvedA,\n token1: flipped ? resolvedA : resolvedB,\n flipped,\n };\n }\n\n /** There is no registry state to drop. */\n public invalidate(): void {\n // Nothing is cached: every answer is derived from the reference itself.\n }\n}\n"],"mappings":"A,uiBAyBA,IAAaC,EAAb,KAAqB,CAWnB,YACE,EAIA,EAAiF,CAAC,EAClF,CALiB,KAAA,QAAAC,EAIA,KAAA,QAAAC,EAfXH,EAAA,KAAA,gBAAA,IAAA,EAAA,EACAA,EAAA,KAAA,WAAW,CAAA,EACXA,EAAA,KAAA,kBAAA,IAAA,EAAA,EAESA,EAAA,KAAA,OAAA,IAAA,EAAA,EAaf,KAAK,KAAO,IAAII,EAAWF,EAAQ,cAAeA,EAAQ,iBAAkBA,EAAQ,IAAI,CAC1F,CAOA,MAAa,MAAiC,CAkB5C,OAjBI,KAAK,gBAAkB,IAAA,IAAa,KAAK,IAAI,EAAI,KAAK,SAAW,IAC5D,KAAK,eAEV,KAAK,kBAAoB,IAAA,KAC7B,KAAK,gBAAkB,KAAK,KACzB,eAAkC,KAAK,QAAQ,kBAAmB,YAAa,UAAU,CAAC,CAC1F,KAAM,GACLG,EAAY,UAAWC,EAAS,KAAMC,EAAoB,KAAK,QAAQ,cAAc,CACvF,CAAC,CACA,KAAM,IACL,KAAK,cAAgBC,EACrB,KAAK,SAAW,KAAK,IAAI,EAClBA,EACR,CAAC,CACD,YAAc,CACb,KAAK,gBAAkB,IAAA,EACzB,CAAC,GACI,KAAK,gBACd,CASA,YAA0B,CACxB,KAAK,cAAgB,IAAA,GACrB,KAAK,SAAW,CAClB,CASA,MAAa,SAAoC,CAM/C,OALA,KAAK,WAAW,EAIhB,KAAK,gBAAkB,IAAA,GAChB,KAAK,KAAK,CACnB,CAGA,MAAa,QAAQ,EAAuC,CAC1D,IAAIA,EAAU,MAAM,KAAK,KAAK,EACxBC,EACJ,OAAOC,GAAQ,SACXC,EAAeD,CAAG,EAChBE,EAAeC,EAAmBH,CAAG,CAAC,EACtC,IAAA,GACFE,EAAeF,CAAG,EAClBI,EAAWN,EAAQ,KACtB,GACCO,EAAM,SAAWL,GAAQD,IAAc,IAAA,IAAaG,EAAeG,CAAK,IAAMN,CAClF,EACA,GAAIK,IAAa,IAAA,GAAW,CAC1B,EAAU,MAAM,KAAK,QAAQ,EAC7B,IAAME,EAAYR,EAAQ,KACvB,GACCO,EAAM,SAAWL,GAAQD,IAAc,IAAA,IAAaG,EAAeG,CAAK,IAAMN,CAClF,EACA,GAAIO,IAAc,IAAA,GAAW,MAAMC,EAAc,kBAAkBP,CAAG,EACtE,OAAOM,CACT,CACA,OAAOF,CACT,CAwBA,MAAa,cACX,EACA,EACA,EAAgE,CAAC,EAChD,CACjB,IAAII,EAA4B,CAAC,EACjC,GAAI,CACF,EAAW,MAAM,KAAK,KAAK,CAC7B,OAASC,EAAgB,CACvB,GAAIhB,EAAQ,kBAAoB,SAAU,MAAMgB,CAClD,CACA,IAAMC,EAASR,EAAeS,CAAQ,EAChCC,EAAaJ,EAAS,KACzB,GACCH,EAAM,aAAeM,EAAS,YAC9BN,EAAM,WAAaM,EAAS,UAC5BN,EAAM,OAASM,EAAS,MACxBN,EAAM,gBAAkBM,EAAS,aACrC,EACA,GAAIC,IAAe,IAAA,GAAW,OAAOA,EAAW,OAChD,GAAIC,EAA0BC,CAAa,EAAG,OAAOA,EACrD,MAAM,IAAIP,EAAc,0BAA2B,CAAE,SAAUG,EAAQ,eAAc,CAAC,CACxF,CAGA,MAAa,UACX,EACA,EAKC,CACD,GAAM,CAACK,EAAWC,GAAa,MAAM,QAAQ,IAAI,CAAC,KAAK,QAAQC,CAAC,EAAG,KAAK,QAAQC,CAAC,CAAC,CAAC,EAC7E,CAACC,EAAcC,GAAgBC,EAAaN,EAAU,OAAQC,EAAU,MAAM,EACpF,MAAO,CACL,OAAQG,IAAiBJ,EAAU,OAASA,EAAYC,EACxD,OAAQI,IAAiBJ,EAAU,OAASA,EAAYD,EACxD,QAASI,IAAiBJ,EAAU,MACtC,CACF,CACF,EAGA,SAASlB,EAAmB,EAA2C,CACrE,IAAMQ,EAAQiB,EAAcC,CAAK,EACjC,GAAIlB,IAAU,IAAA,GAAW,OAAOmB,EAAc,0BAA0B,EACxE,GAAM,CAAE,SAAQ,aAAY,WAAU,OAAM,gBAAe,YAAanB,EAUxE,OATI,OAAOoB,GAAW,SAClBA,EAAO,SAAW,EAAUD,EAAc,iBAAiB,EAC3D,OAAOE,GAAe,SACtB,OAAOC,GAAa,SACpB,OAAOC,GAAS,SAChB,OAAOC,GAAkB,SACzB,OAAOC,GAAa,UAAY,CAAC,OAAO,UAAUA,CAAQ,GAAKA,EAAW,EACrEN,EAAc,wCAAwC,EAExDO,EAAQ,CAAE,SAAQ,aAAY,WAAU,OAAM,gBAAe,UAAS,CAAC,EAJhCP,EAAc,+BAA+B,EADtDA,EAAc,sBAAsB,EADhCA,EAAc,0BAA0B,EADtCA,EAAc,4BAA4B,EAF9CA,EAAc,wBAAwB,CAU/E,CAEA,SAASvB,EAAe,EAAsB,CAC5C,OAAOD,EAAI,SAAS,GAAG,GAAKA,EAAI,SAAS,GAAG,CAC9C,CAkBA,SAAgBgC,EAAW,EAAuB,CAChD,OAAO,OAAOhC,GAAQ,SAAWA,EAAME,EAAeF,CAAG,CAC3D,CAGA,eAAsBiC,EAAgB,EAAyB,EAAgC,CAC7F,IAAM7B,EAAW,MAAM8B,EAAeF,EAAWhC,CAAG,MAASmC,EAAS,QAAQnC,CAAG,CAAC,EAClF,GAAII,EAAS,OAAO,OAAS,EAAG,OAAOA,EAAS,OAChD,MAAM,IAAIG,EAAc,mBAAoB,CAAE,MAAOyB,EAAWhC,CAAG,CAAE,CAAC,CACxE,CAGA,eAAsBoC,EAAa,EAAyB,EAAgC,CAC1F,OAAOD,EAAS,cAAgB,GAAOH,EAAWhC,CAAG,EAAIiC,EAAgBE,EAAUnC,CAAG,CACxF,CAmBA,IAAaqC,EAAb,KAAuD,C,cACrC/C,EAAA,KAAA,cAAc,EAAA,C,CAG9B,QAAe,EAAuC,CAWpD,OAVI,OAAOU,GAAQ,UAAY,CAACC,EAAeD,CAAG,EACzC,QAAQ,QAAQ,CACrB,OAAQA,EACR,WAAY,GACZ,SAAU,GACV,KAAM,GACN,cAAe,GACf,SAAU,CACZ,CAAC,EAEI,QAAQ,OAAOO,EAAc,kBAAkBP,CAAG,CAAC,CAC5D,CAGA,MAAa,UACX,EACA,EAC6E,CAC7E,GAAM,CAACe,EAAWC,GAAa,MAAM,QAAQ,IAAI,CAAC,KAAK,QAAQC,CAAC,EAAG,KAAK,QAAQC,CAAC,CAAC,CAAC,EAC7E,CAACoB,GAASjB,EAAaN,EAAU,OAAQC,EAAU,MAAM,EACzDuB,EAAUD,IAAUvB,EAAU,OACpC,MAAO,CACL,OAAQwB,EAAUvB,EAAYD,EAC9B,OAAQwB,EAAUxB,EAAYC,EAC9B,SACF,CACF,CAGA,YAA0B,CAE1B,CACF,E"}
@@ -1 +1 @@
1
- {"version":3,"file":"env.js","names":["GSWAP_ENVIRONMENTS"],"sources":["../../../src/types/env.ts"],"sourcesContent":["/** SDK deployment environment. */\nexport type GSwapEnv = 'prod' | 'stage';\n\n/** Backend URLs used by one SDK deployment environment. */\nexport interface GSwapEnvironmentConfig {\n dexBackendBaseUrl: string;\n /** The realtime stream endpoint (raw WebSocket, JSON-RPC 2.0). */\n dexBackendWsUrl: string;\n}\n\n/** Built-in GalaSwap deployment environments. */\nexport const GSWAP_ENVIRONMENTS: Readonly<Record<GSwapEnv, GSwapEnvironmentConfig>> = {\n prod: {\n dexBackendBaseUrl: 'https://dex-backend-prod1.defi.gala.com',\n dexBackendWsUrl: 'wss://dex-backend-prod1.defi.gala.com/ws',\n },\n stage: {\n dexBackendBaseUrl: 'https://swap-backend.stage.defi.ovh.gala.com',\n dexBackendWsUrl: 'wss://swap-backend.stage.defi.ovh.gala.com/ws',\n },\n};\n"],"mappings":"AAWA,MAAaA,EAAyE,CACpF,KAAM,CACJ,kBAAmB,0CACnB,gBAAiB,0CACnB,EACA,MAAO,CACL,kBAAmB,+CACnB,gBAAiB,+CACnB,CACF"}
1
+ {"version":3,"file":"env.js","names":["GSWAP_ENVIRONMENTS"],"sources":["../../../src/types/env.ts"],"sourcesContent":["/** SDK deployment environment. */\nexport type GSwapEnv = 'prod' | 'stage';\n\n/** Backend URLs used by one SDK deployment environment. */\nexport interface GSwapEnvironmentConfig {\n dexBackendBaseUrl: string;\n /** The realtime stream endpoint (raw WebSocket, JSON-RPC 2.0). */\n dexBackendWsUrl: string;\n}\n\n/** Built-in GalaSwap deployment environments. */\nexport const GSWAP_ENVIRONMENTS: Readonly<Record<GSwapEnv, GSwapEnvironmentConfig>> = {\n prod: {\n dexBackendBaseUrl: 'https://dex-backend-prod1.defi.gala.com',\n dexBackendWsUrl: 'wss://dex-backend-prod1.defi.gala.com/ws',\n },\n stage: {\n dexBackendBaseUrl: 'https://swap-backend.stage.defi.ovh.gala.com',\n dexBackendWsUrl: 'wss://swap-backend.stage.defi.ovh.gala.com/ws',\n },\n};\n"],"mappings":"AAWA,MAAaA,EAAyE,CACpF,KAAM,CACJ,kBAAmB,0CACnB,gBAAiB,0CACnB,EACA,MAAO,CACL,kBAAmB,+CACnB,gBAAiB,+CACnB,CACF,E"}
@@ -1 +1 @@
1
- {"version":3,"file":"fees.js","names":["FEE_TIER","\"PERCENT_00_00\"","\"PERCENT_00_05\"","\"PERCENT_00_30\"","\"PERCENT_01_00\"","TICK_SPACING_BY_FEE","ALL_FEE_TIERS"],"sources":["../../../src/types/fees.ts"],"sourcesContent":["/**\n * Fee tiers for pools.\n */\nexport enum FEE_TIER {\n /** No trading fee. */\n PERCENT_00_00 = 0,\n /** 0.05% fee tier */\n PERCENT_00_05 = 500,\n /** 0.3% fee tier */\n PERCENT_00_30 = 3000,\n /** 1.0% fee tier */\n PERCENT_01_00 = 10000,\n}\n\n/** Tick spacing required by the current GalaChainDex contract for each fee tier. */\nexport const TICK_SPACING_BY_FEE: Readonly<Record<FEE_TIER, number>> = {\n [FEE_TIER.PERCENT_00_00]: 200,\n [FEE_TIER.PERCENT_00_05]: 10,\n [FEE_TIER.PERCENT_00_30]: 60,\n [FEE_TIER.PERCENT_01_00]: 200,\n};\n\n/** All fee tiers supported by the current GalaChainDex contract. */\nexport const ALL_FEE_TIERS: readonly FEE_TIER[] = [\n FEE_TIER.PERCENT_00_00,\n FEE_TIER.PERCENT_00_05,\n FEE_TIER.PERCENT_00_30,\n FEE_TIER.PERCENT_01_00,\n];\n"],"mappings":"AAGA,IAAYA,EAAL,yBAAA,EAAA,OAEL,GAAA,EAAAC,cAAA,GAAA,gBAEA,EAAA,EAAAC,cAAA,KAAA,gBAEA,EAAA,EAAAC,cAAA,KAAA,gBAEA,EAAA,EAAAC,cAAA,KAAA,iBACF,EAAA,CAAA,CAAA,EAGA,MAAaC,EAA0D,CAC3C,EAAA,IACA,IAAA,GACA,IAAA,GACA,IAAA,GAC5B,EAGaC,EAAqC,cAKlD"}
1
+ {"version":3,"file":"fees.js","names":["FEE_TIER","\"PERCENT_00_00\"","\"PERCENT_00_05\"","\"PERCENT_00_30\"","\"PERCENT_01_00\"","TICK_SPACING_BY_FEE","ALL_FEE_TIERS"],"sources":["../../../src/types/fees.ts"],"sourcesContent":["/**\n * Fee tiers for pools.\n */\nexport enum FEE_TIER {\n /** No trading fee. */\n PERCENT_00_00 = 0,\n /** 0.05% fee tier */\n PERCENT_00_05 = 500,\n /** 0.3% fee tier */\n PERCENT_00_30 = 3000,\n /** 1.0% fee tier */\n PERCENT_01_00 = 10000,\n}\n\n/** Tick spacing required by the current GalaChainDex contract for each fee tier. */\nexport const TICK_SPACING_BY_FEE: Readonly<Record<FEE_TIER, number>> = {\n [FEE_TIER.PERCENT_00_00]: 200,\n [FEE_TIER.PERCENT_00_05]: 10,\n [FEE_TIER.PERCENT_00_30]: 60,\n [FEE_TIER.PERCENT_01_00]: 200,\n};\n\n/** All fee tiers supported by the current GalaChainDex contract. */\nexport const ALL_FEE_TIERS: readonly FEE_TIER[] = [\n FEE_TIER.PERCENT_00_00,\n FEE_TIER.PERCENT_00_05,\n FEE_TIER.PERCENT_00_30,\n FEE_TIER.PERCENT_01_00,\n];\n"],"mappings":"AAGA,IAAYA,EAAL,yBAAA,EAAA,C,MAEL,GAAA,EAAAC,cAAA,GAAA,gBAEA,EAAA,EAAAC,cAAA,KAAA,gBAEA,EAAA,EAAAC,cAAA,KAAA,gBAEA,EAAA,EAAAC,cAAA,KAAA,gB,CACF,EAAA,CAAA,CAAA,EAGA,MAAaC,EAA0D,CAC3C,EAAA,IACA,IAAA,GACA,IAAA,GACA,IAAA,GAC5B,EAGaC,EAAqC,C,aAKlD,E"}
@@ -1 +1 @@
1
- {"version":3,"file":"market.js","names":["PRICE_SERIES_TIMEFRAMES","MARKET_PRICE_BATCH_LIMIT"],"sources":["../../../src/types/market.ts"],"sourcesContent":["/**\n * The swap backend's market data and trading-competition reads: what `GSwap.market` and\n * `GSwap.leaderboard` return. Values are the backend's, unconverted: USD prices as numbers, times\n * as unix seconds or `Date`s, XP as decimal strings.\n */\n\n/** The windows `GET /market/price-series` serves (the backend refuses any other with a 400). */\nexport const PRICE_SERIES_TIMEFRAMES = ['1D', '1W', '1M', '1Y', 'ALL'] as const;\n\n/** One member of {@link PRICE_SERIES_TIMEFRAMES}. */\nexport type PriceSeriesTimeframe = (typeof PRICE_SERIES_TIMEFRAMES)[number];\n\n/** The most tokens one `GET /market/price` request may name (the backend's limit). */\nexport const MARKET_PRICE_BATCH_LIMIT = 500;\n\n/** One token's market price, as `GET /market/price` answered it. */\nexport interface MarketPrice {\n /** The id the price was asked for: the token string as given, or an object key's `$` form. */\n token: string;\n /** USD price; `null` when the backend knows the token but has no price for it. */\n price: number | null;\n /** 24-hour change in percent (`-2.1` is -2.1 %); `null` when unknown. */\n change24h: number | null;\n /** Whether the token is on the backend's CoinGecko list (what the swap app's \"trending\" uses). */\n listed: boolean;\n}\n\n/** One point of a price series. */\nexport interface PriceSeriesPoint {\n /** Unix time in seconds. */\n time: number;\n /** USD price at `time`. */\n price: number;\n}\n\n/** A token's USD price line, as `GET /market/price-series` answered it. */\nexport interface PriceSeries {\n /** The id the series was asked for: the token string as given, or an object key's `$` form. */\n token: string;\n timeframe: PriceSeriesTimeframe;\n /** In the backend's order (oldest first). Empty for a token the backend has no prices for. */\n prices: PriceSeriesPoint[];\n /** USD price now; `null` when unknown. */\n currentPrice: number | null;\n /** 24-hour change in percent; `null` when unknown. */\n change24h: number | null;\n}\n\n/**\n * Totals across every current-contract pool, as `GET /v2/trade/volume` answered them. USD amounts;\n * a delta is a fraction (`-0.11` is 11 % less than the previous period, `0` when that period had\n * no volume), not a percentage.\n */\nexport interface VolumeSummary {\n tvl: number;\n volume1d: number;\n volume7d: number;\n volume30d: number;\n /** Number of trades in the last 24 hours. */\n trades1d: number;\n volume1dDelta: number;\n volume7dDelta: number;\n volume30dDelta: number;\n}\n\n/** A trading-competition season, as `GET /leaderboard/seasons` answered it. */\nexport interface LeaderboardSeason {\n id: number;\n name: string;\n /** First instant of the season. */\n start: Date;\n /** Last instant of the season (inclusive). */\n end: Date;\n /** The scoring rules the season uses. */\n rulesId: number;\n}\n\n/** An achievement shown on a leaderboard entry. */\nexport interface MasteryTitle {\n name: string;\n /** For example `trade` or `liquidity`; the backend's own word, passed on as it is. */\n type: string;\n /** Display order. */\n order: number;\n}\n\n/** One wallet's row on a season's leaderboard. */\nexport interface LeaderboardEntry {\n wallet: string;\n /** 1 for the top wallet. */\n rank: number;\n /** Decimal string; `null` when the backend has no XP breakdown for the wallet yet. */\n totalXp: string | null;\n /** The wallet's share of the season's XP, in percent, as a decimal string (`'12.34'`). */\n distributionPercent: string;\n liquidityXp: string;\n tradingXp: string;\n masteryTitles: MasteryTitle[];\n}\n\n/** A season's leaderboard (the backend returns at most its top 100 wallets). */\nexport interface SeasonLeaderboard {\n seasonId: number;\n entries: LeaderboardEntry[];\n}\n"],"mappings":"AAOA,MAAaA,EAA0B,CAAC,KAAM,KAAM,KAAM,KAAM,KAAK,EAMxDC,EAA2B"}
1
+ {"version":3,"file":"market.js","names":["PRICE_SERIES_TIMEFRAMES","MARKET_PRICE_BATCH_LIMIT"],"sources":["../../../src/types/market.ts"],"sourcesContent":["/**\n * The swap backend's market data and trading-competition reads: what `GSwap.market` and\n * `GSwap.leaderboard` return. Values are the backend's, unconverted: USD prices as numbers, times\n * as unix seconds or `Date`s, XP as decimal strings.\n */\n\n/** The windows `GET /market/price-series` serves (the backend refuses any other with a 400). */\nexport const PRICE_SERIES_TIMEFRAMES = ['1D', '1W', '1M', '1Y', 'ALL'] as const;\n\n/** One member of {@link PRICE_SERIES_TIMEFRAMES}. */\nexport type PriceSeriesTimeframe = (typeof PRICE_SERIES_TIMEFRAMES)[number];\n\n/** The most tokens one `GET /market/price` request may name (the backend's limit). */\nexport const MARKET_PRICE_BATCH_LIMIT = 500;\n\n/** One token's market price, as `GET /market/price` answered it. */\nexport interface MarketPrice {\n /** The id the price was asked for: the token string as given, or an object key's `$` form. */\n token: string;\n /** USD price; `null` when the backend knows the token but has no price for it. */\n price: number | null;\n /** 24-hour change in percent (`-2.1` is -2.1 %); `null` when unknown. */\n change24h: number | null;\n /** Whether the token is on the backend's CoinGecko list (what the swap app's \"trending\" uses). */\n listed: boolean;\n}\n\n/** One point of a price series. */\nexport interface PriceSeriesPoint {\n /** Unix time in seconds. */\n time: number;\n /** USD price at `time`. */\n price: number;\n}\n\n/** A token's USD price line, as `GET /market/price-series` answered it. */\nexport interface PriceSeries {\n /** The id the series was asked for: the token string as given, or an object key's `$` form. */\n token: string;\n timeframe: PriceSeriesTimeframe;\n /** In the backend's order (oldest first). Empty for a token the backend has no prices for. */\n prices: PriceSeriesPoint[];\n /** USD price now; `null` when unknown. */\n currentPrice: number | null;\n /** 24-hour change in percent; `null` when unknown. */\n change24h: number | null;\n}\n\n/**\n * Totals across every current-contract pool, as `GET /v2/trade/volume` answered them. USD amounts;\n * a delta is a fraction (`-0.11` is 11 % less than the previous period, `0` when that period had\n * no volume), not a percentage.\n */\nexport interface VolumeSummary {\n tvl: number;\n volume1d: number;\n volume7d: number;\n volume30d: number;\n /** Number of trades in the last 24 hours. */\n trades1d: number;\n volume1dDelta: number;\n volume7dDelta: number;\n volume30dDelta: number;\n}\n\n/** A trading-competition season, as `GET /leaderboard/seasons` answered it. */\nexport interface LeaderboardSeason {\n id: number;\n name: string;\n /** First instant of the season. */\n start: Date;\n /** Last instant of the season (inclusive). */\n end: Date;\n /** The scoring rules the season uses. */\n rulesId: number;\n}\n\n/** An achievement shown on a leaderboard entry. */\nexport interface MasteryTitle {\n name: string;\n /** For example `trade` or `liquidity`; the backend's own word, passed on as it is. */\n type: string;\n /** Display order. */\n order: number;\n}\n\n/** One wallet's row on a season's leaderboard. */\nexport interface LeaderboardEntry {\n wallet: string;\n /** 1 for the top wallet. */\n rank: number;\n /** Decimal string; `null` when the backend has no XP breakdown for the wallet yet. */\n totalXp: string | null;\n /** The wallet's share of the season's XP, in percent, as a decimal string (`'12.34'`). */\n distributionPercent: string;\n liquidityXp: string;\n tradingXp: string;\n masteryTitles: MasteryTitle[];\n}\n\n/** A season's leaderboard (the backend returns at most its top 100 wallets). */\nexport interface SeasonLeaderboard {\n seasonId: number;\n entries: LeaderboardEntry[];\n}\n"],"mappings":"AAOA,MAAaA,EAA0B,CAAC,KAAM,KAAM,KAAM,KAAM,KAAK,EAMxDC,EAA2B,I"}
@@ -0,0 +1,2 @@
1
+ const e=`asset/dex-contract`;export{e as DEX_CONTRACT_PATH};
2
+ //# sourceMappingURL=chain_contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chain_contract.js","names":["DEX_CONTRACT_PATH"],"sources":["../../../src/utils/chain_contract.ts"],"sourcesContent":["import type { ChainContractPath } from '@gala-chain/gala-core-sdk';\n\n/**\n * The contract every v2 write goes to: its route under `/v1/chain/` and (through core's table) the\n * `dtoOperation` each DTO is signed with. One constant for both, so they cannot disagree.\n */\nexport const DEX_CONTRACT_PATH = 'asset/dex-contract' satisfies ChainContractPath;\n"],"mappings":"AAMA,MAAaA,EAAoB,qB"}
@@ -1 +1 @@
1
- {"version":3,"file":"classification.js","names":["DEFAULT_RETRYABLE","classification","options","outcome","NOT_QUEUED_FINAL","notSentClassification"],"sources":["../../../src/utils/classification.ts"],"sourcesContent":["import type { FailureOutcome } from '@gala-chain/gala-core-sdk';\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 * Gala Wallet does 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,MAAMA,EAAoB,CACxB,SAAU,GACV,QAAS,GACT,WAAY,GACZ,SAAU,GACV,cAAe,GACf,UAAW,EACb,EAuBA,SAAgBC,EACd,EACA,EAA0C,CAAC,EACpB,CACvB,MAAO,CAAE,UAAS,UAAWC,EAAQ,YAAc,IAAgBF,EAAkBG,EAAS,CAChG,CASA,MAAaC,EAAoD,OAAO,OACtEH,EAAe,aAAc,CAAE,UAAW,EAAM,CAAC,CACnD,EAMA,SAAgBI,GAA+C,CAC7D,OAAOJ,EAAe,UAAU,CAClC"}
1
+ {"version":3,"file":"classification.js","names":["DEFAULT_RETRYABLE","classification","options","outcome","NOT_QUEUED_FINAL","notSentClassification"],"sources":["../../../src/utils/classification.ts"],"sourcesContent":["import type { FailureOutcome } from '@gala-chain/gala-core-sdk';\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 * Gala Wallet does 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,MAAMA,EAAoB,CACxB,SAAU,GACV,QAAS,GACT,WAAY,GACZ,SAAU,GACV,cAAe,GACf,UAAW,EACb,EAuBA,SAAgBC,EACd,EACA,EAA0C,CAAC,EACpB,CACvB,MAAO,CAAE,UAAS,UAAWC,EAAQ,YAAc,IAAgBF,EAAkBG,EAAS,CAChG,CASA,MAAaC,EAAoD,OAAO,OACtEH,EAAe,aAAc,CAAE,UAAW,EAAM,CAAC,CACnD,EAMA,SAAgBI,GAA+C,CAC7D,OAAOJ,EAAe,UAAU,CAClC,C"}
@@ -1 +1 @@
1
- {"version":3,"file":"confirmation_schedule.js","names":["createConfirmationSchedule","timeoutMs","options","pollIntervalMs","initialDelayMs","defaultInitialDelayMs","signal","deadline","firstRead","attempt","throwIfAborted","abortError","remaining","delay","backoff","waitMs","retryAfterMs","jitter","onAbort","timer","reject","resolve","milliseconds"],"sources":["../../../src/utils/confirmation_schedule.ts"],"sourcesContent":["import type { ConfirmOptions } from '../types/confirm_options.js';\n\nconst DEFAULT_TIMEOUT_MS = 60_000;\nconst DEFAULT_POLL_INTERVAL_MS = 2_000;\nconst MIN_POLL_INTERVAL_MS = 500;\nconst MAX_BACKOFF_MS = 8_000;\n\n/** The pacing of confirmation reads: one initial delay, then jittered exponential backoff. */\nexport interface ConfirmationSchedule {\n /**\n * Waits for the next read. Resolves `true` when the caller should read, `false` when the\n * deadline leaves no room for another read. The very first call always resolves `true` (at\n * least one read is made, even when the deadline is already past). Rejects with the abort\n * error when the caller's signal aborts, before or during the wait.\n */\n waitForRead(retryAfterMs?: number): Promise<boolean>;\n}\n\n/**\n * Builds the schedule for one `confirm()` call. `abortError` creates the error thrown when\n * `options.signal` aborts, so each confirmation kind keeps its own error type and details.\n */\nexport function createConfirmationSchedule(\n options: ConfirmOptions | undefined,\n defaultInitialDelayMs: number,\n abortError: (cause?: unknown) => Error,\n): ConfirmationSchedule {\n const timeoutMs = Math.max(0, options?.timeoutMs ?? DEFAULT_TIMEOUT_MS);\n const pollIntervalMs = Math.max(\n MIN_POLL_INTERVAL_MS,\n options?.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS,\n );\n const initialDelayMs = Math.max(0, options?.initialDelayMs ?? defaultInitialDelayMs);\n const signal = options?.signal;\n const deadline = Date.now() + timeoutMs;\n let firstRead = true;\n let attempt = 0;\n return {\n async waitForRead(retryAfterMs?: number): Promise<boolean> {\n throwIfAborted(signal, abortError);\n if (firstRead) {\n firstRead = false;\n const remaining = Math.max(0, deadline - Date.now());\n await delay(Math.min(initialDelayMs, remaining), signal, abortError);\n return true;\n }\n const backoff = Math.min(MAX_BACKOFF_MS, pollIntervalMs * 2 ** Math.min(attempt, 5));\n attempt += 1;\n const waitMs = retryAfterMs === undefined ? jitter(backoff) : Math.max(0, retryAfterMs);\n if (Date.now() + waitMs > deadline) return false;\n await delay(waitMs, signal, abortError);\n return true;\n },\n };\n}\n\n/** Throws `abortError(signal.reason)` when the signal has aborted. */\nexport function throwIfAborted(\n signal: AbortSignal | undefined,\n abortError: (cause?: unknown) => Error,\n): void {\n if (signal?.aborted === true) throw abortError(signal.reason);\n}\n\nfunction delay(\n milliseconds: number,\n signal: AbortSignal | undefined,\n abortError: (cause?: unknown) => Error,\n): Promise<void> {\n if (signal?.aborted === true) return Promise.reject(abortError(signal.reason));\n return new Promise((resolve, reject) => {\n const onAbort = (): void => {\n clearTimeout(timer);\n reject(abortError(signal?.reason));\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, milliseconds);\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n\nfunction jitter(milliseconds: number): number {\n return Math.max(0, Math.round(milliseconds * (0.9 + Math.random() * 0.2)));\n}\n"],"mappings":"AAsBA,SAAgBA,EACd,EACA,EACA,EACsB,CACtB,IAAMC,EAAY,KAAK,IAAI,EAAGC,GAAS,WAAa,GAAkB,EAChEC,EAAiB,KAAK,IAC1B,IACAD,GAAS,gBAAkB,GAC7B,EACME,EAAiB,KAAK,IAAI,EAAGF,GAAS,gBAAkBG,CAAqB,EAC7EC,EAASJ,GAAS,OAClBK,EAAW,KAAK,IAAI,EAAIN,EAC1BO,EAAY,GACZC,EAAU,EACd,MAAO,CACL,MAAM,YAAY,EAAyC,CAEzD,GADAC,EAAeJ,EAAQK,CAAU,EAC7BH,EAAW,CACb,EAAY,GACZ,IAAMI,EAAY,KAAK,IAAI,EAAGL,EAAW,KAAK,IAAI,CAAC,EAEnD,OADA,MAAMM,EAAM,KAAK,IAAIT,EAAgBQ,CAAS,EAAGN,EAAQK,CAAU,EAC5D,EACT,CACA,IAAMG,EAAU,KAAK,IAAI,IAAgBX,EAAiB,GAAK,KAAK,IAAIM,EAAS,CAAC,CAAC,EACnF,GAAW,EACX,IAAMM,EAASC,IAAiB,IAAA,GAAYC,EAAOH,CAAO,EAAI,KAAK,IAAI,EAAGE,CAAY,EAGtF,OAFI,KAAK,IAAI,EAAID,EAASR,EAAiB,IAC3C,MAAMM,EAAME,EAAQT,EAAQK,CAAU,EAC/B,GACT,CACF,CACF,CAGA,SAAgBD,EACd,EACA,EACM,CACN,GAAIJ,GAAQ,UAAY,GAAM,MAAMK,EAAWL,EAAO,MAAM,CAC9D,CAEA,SAASO,EACP,EACA,EACA,EACe,CAEf,OADIP,GAAQ,UAAY,GAAa,QAAQ,OAAOK,EAAWL,EAAO,MAAM,CAAC,EACtE,IAAI,SAAS,EAAS,IAAW,CACtC,IAAMY,MAAsB,CAC1B,aAAaC,CAAK,EAClBC,EAAOT,EAAWL,GAAQ,MAAM,CAAC,CACnC,EACMa,EAAQ,eAAiB,CAC7Bb,GAAQ,oBAAoB,QAASY,CAAO,EAC5CG,EAAQ,CACV,EAAGC,CAAY,EACfhB,GAAQ,iBAAiB,QAASY,EAAS,CAAE,KAAM,EAAK,CAAC,CAC3D,CAAC,CACH,CAEA,SAASD,EAAO,EAA8B,CAC5C,OAAO,KAAK,IAAI,EAAG,KAAK,MAAMK,GAAgB,GAAM,KAAK,OAAO,EAAI,GAAI,CAAC,CAC3E"}
1
+ {"version":3,"file":"confirmation_schedule.js","names":["createConfirmationSchedule","timeoutMs","options","pollIntervalMs","initialDelayMs","defaultInitialDelayMs","signal","deadline","firstRead","attempt","throwIfAborted","abortError","remaining","delay","backoff","waitMs","retryAfterMs","jitter","onAbort","timer","reject","resolve","milliseconds"],"sources":["../../../src/utils/confirmation_schedule.ts"],"sourcesContent":["import type { ConfirmOptions } from '../types/confirm_options.js';\n\nconst DEFAULT_TIMEOUT_MS = 60_000;\nconst DEFAULT_POLL_INTERVAL_MS = 2_000;\nconst MIN_POLL_INTERVAL_MS = 500;\nconst MAX_BACKOFF_MS = 8_000;\n\n/** The pacing of confirmation reads: one initial delay, then jittered exponential backoff. */\nexport interface ConfirmationSchedule {\n /**\n * Waits for the next read. Resolves `true` when the caller should read, `false` when the\n * deadline leaves no room for another read. The very first call always resolves `true` (at\n * least one read is made, even when the deadline is already past). Rejects with the abort\n * error when the caller's signal aborts, before or during the wait.\n */\n waitForRead(retryAfterMs?: number): Promise<boolean>;\n}\n\n/**\n * Builds the schedule for one `confirm()` call. `abortError` creates the error thrown when\n * `options.signal` aborts, so each confirmation kind keeps its own error type and details.\n */\nexport function createConfirmationSchedule(\n options: ConfirmOptions | undefined,\n defaultInitialDelayMs: number,\n abortError: (cause?: unknown) => Error,\n): ConfirmationSchedule {\n const timeoutMs = Math.max(0, options?.timeoutMs ?? DEFAULT_TIMEOUT_MS);\n const pollIntervalMs = Math.max(\n MIN_POLL_INTERVAL_MS,\n options?.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS,\n );\n const initialDelayMs = Math.max(0, options?.initialDelayMs ?? defaultInitialDelayMs);\n const signal = options?.signal;\n const deadline = Date.now() + timeoutMs;\n let firstRead = true;\n let attempt = 0;\n return {\n async waitForRead(retryAfterMs?: number): Promise<boolean> {\n throwIfAborted(signal, abortError);\n if (firstRead) {\n firstRead = false;\n const remaining = Math.max(0, deadline - Date.now());\n await delay(Math.min(initialDelayMs, remaining), signal, abortError);\n return true;\n }\n const backoff = Math.min(MAX_BACKOFF_MS, pollIntervalMs * 2 ** Math.min(attempt, 5));\n attempt += 1;\n const waitMs = retryAfterMs === undefined ? jitter(backoff) : Math.max(0, retryAfterMs);\n if (Date.now() + waitMs > deadline) return false;\n await delay(waitMs, signal, abortError);\n return true;\n },\n };\n}\n\n/** Throws `abortError(signal.reason)` when the signal has aborted. */\nexport function throwIfAborted(\n signal: AbortSignal | undefined,\n abortError: (cause?: unknown) => Error,\n): void {\n if (signal?.aborted === true) throw abortError(signal.reason);\n}\n\nfunction delay(\n milliseconds: number,\n signal: AbortSignal | undefined,\n abortError: (cause?: unknown) => Error,\n): Promise<void> {\n if (signal?.aborted === true) return Promise.reject(abortError(signal.reason));\n return new Promise((resolve, reject) => {\n const onAbort = (): void => {\n clearTimeout(timer);\n reject(abortError(signal?.reason));\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, milliseconds);\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n\nfunction jitter(milliseconds: number): number {\n return Math.max(0, Math.round(milliseconds * (0.9 + Math.random() * 0.2)));\n}\n"],"mappings":"AAsBA,SAAgBA,EACd,EACA,EACA,EACsB,CACtB,IAAMC,EAAY,KAAK,IAAI,EAAGC,GAAS,WAAa,GAAkB,EAChEC,EAAiB,KAAK,IAC1B,IACAD,GAAS,gBAAkB,GAC7B,EACME,EAAiB,KAAK,IAAI,EAAGF,GAAS,gBAAkBG,CAAqB,EAC7EC,EAASJ,GAAS,OAClBK,EAAW,KAAK,IAAI,EAAIN,EAC1BO,EAAY,GACZC,EAAU,EACd,MAAO,CACL,MAAM,YAAY,EAAyC,CAEzD,GADAC,EAAeJ,EAAQK,CAAU,EAC7BH,EAAW,CACb,EAAY,GACZ,IAAMI,EAAY,KAAK,IAAI,EAAGL,EAAW,KAAK,IAAI,CAAC,EAEnD,OADA,MAAMM,EAAM,KAAK,IAAIT,EAAgBQ,CAAS,EAAGN,EAAQK,CAAU,EAC5D,EACT,CACA,IAAMG,EAAU,KAAK,IAAI,IAAgBX,EAAiB,GAAK,KAAK,IAAIM,EAAS,CAAC,CAAC,EACnF,GAAW,EACX,IAAMM,EAASC,IAAiB,IAAA,GAAYC,EAAOH,CAAO,EAAI,KAAK,IAAI,EAAGE,CAAY,EAGtF,OAFI,KAAK,IAAI,EAAID,EAASR,EAAiB,IAC3C,MAAMM,EAAME,EAAQT,EAAQK,CAAU,EAC/B,GACT,CACF,CACF,CAGA,SAAgBD,EACd,EACA,EACM,CACN,GAAIJ,GAAQ,UAAY,GAAM,MAAMK,EAAWL,EAAO,MAAM,CAC9D,CAEA,SAASO,EACP,EACA,EACA,EACe,CAEf,OADIP,GAAQ,UAAY,GAAa,QAAQ,OAAOK,EAAWL,EAAO,MAAM,CAAC,EACtE,IAAI,SAAS,EAAS,IAAW,CACtC,IAAMY,MAAsB,CAC1B,aAAaC,CAAK,EAClBC,EAAOT,EAAWL,GAAQ,MAAM,CAAC,CACnC,EACMa,EAAQ,eAAiB,CAC7Bb,GAAQ,oBAAoB,QAASY,CAAO,EAC5CG,EAAQ,CACV,EAAGC,CAAY,EACfhB,GAAQ,iBAAiB,QAASY,EAAS,CAAE,KAAM,EAAK,CAAC,CAC3D,CAAC,CACH,CAEA,SAASD,EAAO,EAA8B,CAC5C,OAAO,KAAK,IAAI,EAAG,KAAK,MAAMK,GAAgB,GAAM,KAAK,OAAO,EAAI,GAAI,CAAC,CAC3E,C"}
@@ -1 +1 @@
1
- {"version":3,"file":"confirmation_state.js","names":["STATE_OF_CODE","confirmationStateOf","code","toFailureView","error","known","isConfirmationAborted","isConfirmationTimeout"],"sources":["../../../src/utils/confirmation_state.ts"],"sourcesContent":["import { toFailureView } from '@gala-chain/gala-core-sdk';\nimport type { GSwapErrorCode } from '../classes/gswap_error_catalog.js';\n\n/**\n * Where a failed CONFIRMATION of a queued write stopped, as a closed set a screen can switch on:\n *\n * - `aborted`: the caller's own `AbortSignal` ended the wait (`CONFIRMATION_ABORTED`). The person\n * or the screen gave up waiting; the write may still land, and its `uniqueKey` is still the handle.\n * - `timeout`: the wait ran out before an apply result arrived (`CONFIRMATION_TIMEOUT`). The write\n * is still pending as far as anyone knows.\n * - `failed`: the apply-results read itself failed or answered something unusable\n * (`CONFIRMATION_FAILED`, `CONFIRMATION_MALFORMED`, `CONFIRMATION_KEY_MISMATCH`).\n *\n * All three are `unknown` to `classifyFailure`: never submit again, confirm the same key.\n */\nexport type ConfirmationState = 'aborted' | 'timeout' | 'failed';\n\n/** The state of every confirmation code (a typo is caught by the all-codes test). */\nconst STATE_OF_CODE = {\n CONFIRMATION_ABORTED: 'aborted',\n CONFIRMATION_TIMEOUT: 'timeout',\n CONFIRMATION_FAILED: 'failed',\n CONFIRMATION_MALFORMED: 'failed',\n CONFIRMATION_KEY_MISMATCH: 'failed',\n} as const satisfies Partial<Record<GSwapErrorCode, ConfirmationState>>;\n\n/**\n * The {@link ConfirmationState} of a failure, or `undefined` when it is not a failed confirmation.\n * Accepts an SDK error or a plain-object view of one (an app's wrapper with a `code`), and decides\n * from the code alone, so a screen never matches `'CONFIRMATION_ABORTED'` itself.\n *\n * @example\n * ```ts\n * switch (confirmationStateOf(error)) {\n * case 'aborted': return; // the person closed the dialog\n * case 'timeout': return showStillPending(error.uniqueKey);\n * case 'failed': return showCouldNotCheck(error.uniqueKey);\n * case undefined: return showFailure(error);\n * }\n * ```\n */\nexport function confirmationStateOf(error: unknown): ConfirmationState | undefined {\n const code = toFailureView(error)?.code;\n if (code === undefined) return undefined;\n return Object.entries(STATE_OF_CODE).find(([known]) => known === code)?.[1];\n}\n\n/** True when the wait for the apply result was ended by the caller's `AbortSignal`. */\nexport function isConfirmationAborted(error: unknown): boolean {\n return confirmationStateOf(error) === 'aborted';\n}\n\n/** True when the wait for the apply result ran out of time (`CONFIRMATION_TIMEOUT`). */\nexport function isConfirmationTimeout(error: unknown): boolean {\n return confirmationStateOf(error) === 'timeout';\n}\n"],"mappings":"0DAkBA,MAAMA,EAAgB,CACpB,qBAAsB,UACtB,qBAAsB,UACtB,oBAAqB,SACrB,uBAAwB,SACxB,0BAA2B,QAC7B,EAiBA,SAAgBC,EAAoB,EAA+C,CACjF,IAAMC,EAAOC,EAAcC,CAAK,CAAC,EAAE,KACnC,GAAIF,IAAS,IAAA,GACb,OAAO,OAAO,QAAQF,CAAa,CAAC,CAAC,MAAM,CAACK,KAAWA,IAAUH,CAAI,CAAC,GAAG,EAC3E,CAGA,SAAgBI,EAAsB,EAAyB,CAC7D,OAAOL,EAAoBG,CAAK,IAAM,SACxC,CAGA,SAAgBG,EAAsB,EAAyB,CAC7D,OAAON,EAAoBG,CAAK,IAAM,SACxC"}
1
+ {"version":3,"file":"confirmation_state.js","names":["STATE_OF_CODE","confirmationStateOf","code","toFailureView","error","known","isConfirmationAborted","isConfirmationTimeout"],"sources":["../../../src/utils/confirmation_state.ts"],"sourcesContent":["import { toFailureView } from '@gala-chain/gala-core-sdk';\nimport type { GSwapErrorCode } from '../classes/gswap_error_catalog.js';\n\n/**\n * Where a failed CONFIRMATION of a queued write stopped, as a closed set a screen can switch on:\n *\n * - `aborted`: the caller's own `AbortSignal` ended the wait (`CONFIRMATION_ABORTED`). The person\n * or the screen gave up waiting; the write may still land, and its `uniqueKey` is still the handle.\n * - `timeout`: the wait ran out before an apply result arrived (`CONFIRMATION_TIMEOUT`). The write\n * is still pending as far as anyone knows.\n * - `failed`: the apply-results read itself failed or answered something unusable\n * (`CONFIRMATION_FAILED`, `CONFIRMATION_MALFORMED`, `CONFIRMATION_KEY_MISMATCH`).\n *\n * All three are `unknown` to `classifyFailure`: never submit again, confirm the same key.\n */\nexport type ConfirmationState = 'aborted' | 'timeout' | 'failed';\n\n/** The state of every confirmation code (a typo is caught by the all-codes test). */\nconst STATE_OF_CODE = {\n CONFIRMATION_ABORTED: 'aborted',\n CONFIRMATION_TIMEOUT: 'timeout',\n CONFIRMATION_FAILED: 'failed',\n CONFIRMATION_MALFORMED: 'failed',\n CONFIRMATION_KEY_MISMATCH: 'failed',\n} as const satisfies Partial<Record<GSwapErrorCode, ConfirmationState>>;\n\n/**\n * The {@link ConfirmationState} of a failure, or `undefined` when it is not a failed confirmation.\n * Accepts an SDK error or a plain-object view of one (an app's wrapper with a `code`), and decides\n * from the code alone, so a screen never matches `'CONFIRMATION_ABORTED'` itself.\n *\n * @example\n * ```ts\n * switch (confirmationStateOf(error)) {\n * case 'aborted': return; // the person closed the dialog\n * case 'timeout': return showStillPending(error.uniqueKey);\n * case 'failed': return showCouldNotCheck(error.uniqueKey);\n * case undefined: return showFailure(error);\n * }\n * ```\n */\nexport function confirmationStateOf(error: unknown): ConfirmationState | undefined {\n const code = toFailureView(error)?.code;\n if (code === undefined) return undefined;\n return Object.entries(STATE_OF_CODE).find(([known]) => known === code)?.[1];\n}\n\n/** True when the wait for the apply result was ended by the caller's `AbortSignal`. */\nexport function isConfirmationAborted(error: unknown): boolean {\n return confirmationStateOf(error) === 'aborted';\n}\n\n/** True when the wait for the apply result ran out of time (`CONFIRMATION_TIMEOUT`). */\nexport function isConfirmationTimeout(error: unknown): boolean {\n return confirmationStateOf(error) === 'timeout';\n}\n"],"mappings":"A,0DAkBA,MAAMA,EAAgB,CACpB,qBAAsB,UACtB,qBAAsB,UACtB,oBAAqB,SACrB,uBAAwB,SACxB,0BAA2B,QAC7B,EAiBA,SAAgBC,EAAoB,EAA+C,CACjF,IAAMC,EAAOC,EAAcC,CAAK,CAAC,EAAE,KACnC,GAAIF,IAAS,IAAA,GACb,OAAO,OAAO,QAAQF,CAAa,CAAC,CAAC,MAAM,CAACK,KAAWA,IAAUH,CAAI,CAAC,GAAG,EAC3E,CAGA,SAAgBI,EAAsB,EAAyB,CAC7D,OAAOL,EAAoBG,CAAK,IAAM,SACxC,CAGA,SAAgBG,EAAsB,EAAyB,CAC7D,OAAON,EAAoBG,CAAK,IAAM,SACxC,C"}
@@ -1 +1 @@
1
- {"version":3,"file":"debug.js","names":["debug","debugLog"],"sources":["../../../src/utils/debug.ts"],"sourcesContent":["import debug from 'debug';\n\nexport const debugLog = debug('GSWAP_SDK');\n"],"mappings":"qBAEA,MAAaC,EAAWD,EAAM,WAAW"}
1
+ {"version":3,"file":"debug.js","names":["debug","debugLog"],"sources":["../../../src/utils/debug.ts"],"sourcesContent":["import debug from 'debug';\n\nexport const debugLog = debug('GSWAP_SDK');\n"],"mappings":"A,qBAEA,MAAaC,EAAWD,EAAM,WAAW,E"}
@@ -1 +1 @@
1
- {"version":3,"file":"dto_builders.js","names":["BigNumber","decimalString","amount","buildRequestTradeDto","validateFee","input","assertCanonicalPair","assertUniqueKey","dto","exactIn","GSwapSDKError","validateNumericAmount","\"amountOutMinimum\"","exactOut","\"amountInMaximum\"","\"priceLimit\"","assertPoolPosition","assertTickRange","buildAddLiquidityDto","side","onlyOne","buildRemoveLiquidityDto","buildCollectPositionFeesDto","buildCreatePoolDto","symbol","isProposableTradingSymbol","key","assertClassKey","price","parsed","part","first","second","allowNeither","firstName","secondName","invalidDto","rebuildV2Dto","text","value","field","method","integer","optionalText","classKey","name","base","uniqueKey","position","qty","f","tokenInIsToken0","shared","priceLimit","token0Side","token1Side","sqrtPrice","privateAccess","\"privateAccess\"","entry","\"isPrivate\"","assertCanonicalV2Dto","rebuilt"],"sources":["../../../src/utils/dto_builders.ts"],"sourcesContent":["import { assertUniqueKey, type GalaChainTokenClassKey } from '@gala-chain/gala-core-sdk';\nimport 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 { assertTickRange } from './ticks.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 the Gala web app's builder treats it (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":["BigNumber","decimalString","amount","buildRequestTradeDto","validateFee","input","assertCanonicalPair","assertUniqueKey","dto","exactIn","GSwapSDKError","validateNumericAmount","\"amountOutMinimum\"","exactOut","\"amountInMaximum\"","\"priceLimit\"","assertPoolPosition","assertTickRange","buildAddLiquidityDto","side","onlyOne","buildRemoveLiquidityDto","buildCollectPositionFeesDto","buildCreatePoolDto","symbol","isProposableTradingSymbol","key","assertClassKey","price","parsed","part","first","second","allowNeither","firstName","secondName","invalidDto","rebuildV2Dto","text","value","field","method","integer","optionalText","classKey","name","base","uniqueKey","position","qty","f","tokenInIsToken0","shared","priceLimit","token0Side","token1Side","sqrtPrice","privateAccess","\"privateAccess\"","entry","\"isPrivate\"","assertCanonicalV2Dto","rebuilt"],"sources":["../../../src/utils/dto_builders.ts"],"sourcesContent":["import { assertUniqueKey, type GalaChainTokenClassKey } from '@gala-chain/gala-core-sdk';\nimport 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 { assertTickRange } from './ticks.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 the Gala web app's builder treats it (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 +1 @@
1
- {"version":3,"file":"failure_classification.js","names":["REJECTED","classification","UNKNOWN","NOT_QUEUED","NOT_SENT","INVALID_INPUT","TRANSIENT","BY_CODE","NOT_QUEUED_FINAL","classifyInvalidPrivateKey","stringParamOf","view","statusOfView","numberParamOf","classifyHttpError","status","isWriteView","classifyGalaConnectHttpError","classifyUpdateRequired","classifyChainDispatchFailed","classifyGatewayWriteRejected","classifyFailure","result","classifyView","toFailureView","error","options","isCatalogCode","code","mayHaveBeenQueued"],"sources":["../../../src/utils/failure_classification.ts"],"sourcesContent":["import {\n isWriteView,\n numberParamOf,\n stringParamOf,\n toFailureView,\n type FailureView,\n type GALA_ERROR_CATALOG,\n} from '@gala-chain/gala-core-sdk';\nimport type { GSWAP_ERROR_CATALOG, GSwapErrorCode } from '../classes/gswap_error_catalog.js';\nimport { isCatalogCode } from '../classes/gswap_error_phase_table.js';\nimport { classification, NOT_QUEUED_FINAL, type FailureClassification } from './classification.js';\n\n/**\n * `classifyFailure` and `mayHaveBeenQueued`: the one module that holds the decision for every\n * catalog code (~135 names, a floor no minifier can remove). It needs the code table and nothing\n * else: the classification values live in `classification.ts`, the yes/no guards in\n * `failure_guards.ts`, and the error class and catalog are only named as types.\n */\n\nconst REJECTED: FailureClassification = classification('rejected');\nconst UNKNOWN: FailureClassification = classification('unknown');\nconst NOT_QUEUED: FailureClassification = classification('not_queued');\nconst NOT_SENT: FailureClassification = classification('not_sent');\nconst INVALID_INPUT: FailureClassification = classification('invalid_input');\nconst TRANSIENT: FailureClassification = classification('transient');\n\n/**\n * The classification of every code whose meaning does not depend on the failing answer. A code\n * missing here is a compile error (unless it is `pre_send`, see {@link CodeTable}), so adding a\n * catalog code forces a decision.\n */\nconst BY_CODE = {\n // ----------------------------------------------------------------------------------- transport\n REQUEST_TIMEOUT: TRANSIENT,\n TRANSPORT_ERROR: TRANSIENT,\n REQUEST_FAILED: TRANSIENT,\n\n // ------------------------------------------------------------------------------- write: unknown\n // No answer was read, or a 2xx answer that does not prove the write was queued under the signed\n // key: it may have been applied.\n SUBMISSION_OUTCOME_UNKNOWN: UNKNOWN,\n GATEWAY_REPORTED_ERROR: UNKNOWN,\n GATEWAY_DATA_MISSING: UNKNOWN,\n GATEWAY_MODE_INVALID: UNKNOWN,\n GATEWAY_SYNC_REQUEST_TRADE: UNKNOWN,\n MISSING_UNIQUE_KEY: UNKNOWN,\n UNIQUE_KEY_MISMATCH: UNKNOWN,\n // Core `GatewayWrites` (token-contract writes): no answer read, or a 2xx that proves nothing.\n GATEWAY_WRITE_OUTCOME_UNKNOWN: UNKNOWN,\n GATEWAY_RESPONSE_INVALID: UNKNOWN,\n // Core `./bridge` (GalaConnect bridge writes): a 2xx without the id or hash the next step needs,\n // or no answer read after the request left.\n BRIDGE_RESPONSE_INVALID: UNKNOWN,\n BRIDGE_WRITE_OUTCOME_UNKNOWN: UNKNOWN,\n // Any failure after GalaConnect accepted a bridge-out request: the tokens may be locked, so\n // never a retry under a fresh key, whatever the inner failure (a BridgeTokenOut 429 included).\n BRIDGE_OUT_OUTCOME_UNKNOWN: UNKNOWN,\n // A bridge-in deposit that failed once it was being sent (sending, confirming, registering): the\n // funds may have left the wallet. A status wait that timed out: the bridge may still complete.\n BRIDGE_DEPOSIT_OUTCOME_UNKNOWN: UNKNOWN,\n BRIDGE_STATUS_TIMEOUT: UNKNOWN,\n\n // --------------------------------------------------------------------- write: refused, not queued\n SHADOW_NOT_QUEUED: NOT_QUEUED_FINAL,\n METHOD_NOT_ALLOWED: NOT_QUEUED,\n SIGNATURE_INVALID: NOT_QUEUED,\n SIGNER_MISMATCH: NOT_QUEUED,\n DTO_INVALID: NOT_QUEUED,\n BOUNDS_VIOLATION: NOT_QUEUED,\n SYMBOL_CONFLICT: NOT_QUEUED,\n RATE_LIMITED: NOT_QUEUED,\n GATEWAY_REJECTED: NOT_QUEUED,\n\n // ----------------------------------------------------------------------- the chain's own verdict\n CHAIN_REJECTED: REJECTED,\n\n // ----------------------------------------------------------------------- reads of backend data\n INVALID_CHAIN_RESPONSE: TRANSIENT,\n INVALID_POSITION_RESPONSE: TRANSIENT,\n // Core `GatewayReads` (unsigned token-contract reads): nothing is applied, so a refusal or an\n // unusable answer is worth another try; a class that does not exist, or a data set larger than\n // the caller allowed, needs a different request.\n GATEWAY_READ_REJECTED: TRANSIENT,\n GATEWAY_READ_RESPONSE_INVALID: TRANSIENT,\n // Core `./bridge` GalaConnect reads (configurations, fee, status): the same as a gateway read;\n // a symbol GalaConnect has no verified configuration for needs a different request.\n BRIDGE_READ_RESPONSE_INVALID: TRANSIENT,\n BRIDGE_TOKEN_NOT_FOUND: INVALID_INPUT,\n // A token the wrappable list does not have (or its counterpart, or a channel a wrap does not\n // move between): a different request is needed.\n TOKEN_NOT_WRAPPABLE: INVALID_INPUT,\n // A failed or unusable read of the external chain before a bridge-in deposit: nothing was sent.\n BRIDGE_CHAIN_READ_FAILED: TRANSIENT,\n TOKEN_CLASS_NOT_FOUND: INVALID_INPUT,\n PAGINATION_LIMIT_EXCEEDED: INVALID_INPUT,\n\n // -------------------------------------------------------------------------------- confirmation\n CONFIRMATION_TIMEOUT: UNKNOWN,\n CONFIRMATION_ABORTED: UNKNOWN,\n CONFIRMATION_FAILED: UNKNOWN,\n CONFIRMATION_MALFORMED: UNKNOWN,\n CONFIRMATION_KEY_MISMATCH: UNKNOWN,\n\n // ------------------------------------------------------------------------------ the realtime stream\n STREAM_DISCONNECTED: TRANSIENT,\n STREAM_REQUEST_TIMEOUT: TRANSIENT,\n STREAM_CONNECT_FAILED: TRANSIENT,\n STREAM_CLOSED_BEFORE_CONNECT: TRANSIENT,\n STREAM_CONNECTION_CLOSED: TRANSIENT,\n\n // ----------------------------------------------- pre-send: an app-supplied callback failed\n // The signer, the key provider or the token resolver threw before anything was transmitted. The\n // original error is `cause`. A repeat cannot apply anything twice.\n SIGNING_FAILED: NOT_SENT,\n KEY_PROVIDER_FAILED: NOT_SENT,\n TOKEN_RESOLUTION_FAILED: NOT_SENT,\n TOKEN_NAME_RESOLUTION_FAILED: NOT_SENT,\n\n // ------------------------------------------- the call cannot work as written, found AFTER an answer\n // (a pre_send code needs no row: it is `invalid_input` by default, see `CodeTable`)\n NO_WALLET_ADDRESS: INVALID_INPUT,\n NO_POOL_AVAILABLE: INVALID_INPUT,\n INSUFFICIENT_LIQUIDITY: INVALID_INPUT,\n} as const satisfies CodeTable;\n\n/** Both catalogs as one type: every code a `GSwapSDKError` can carry reads its entry here. */\ntype Catalogs = typeof GALA_ERROR_CATALOG & typeof GSWAP_ERROR_CATALOG;\n\n/** A code (core or DEX) whose catalog phase is `pre_send`. */\ntype PreSendCode = {\n [C in GSwapErrorCode]: Catalogs[C]['phase'] extends 'pre_send' ? C : never;\n}[GSwapErrorCode];\n\n/** The codes whose classification depends on the failing answer, not on a table row. */\ntype AnswerDependentCode =\n | 'HTTP_ERROR'\n | 'UPDATE_REQUIRED'\n | 'CHAIN_DISPATCH_FAILED'\n | 'INVALID_PRIVATE_KEY'\n | 'GATEWAY_HTTP_ERROR'\n | 'GALACONNECT_HTTP_ERROR'\n | 'GATEWAY_WRITE_REJECTED'\n | 'BRIDGE_REQUEST_REJECTED';\n\n/**\n * Every code that is NOT `pre_send` needs a row (a new one is a compile error until it has one);\n * a `pre_send` code may have one (`SIGNING_FAILED` is `not_sent`). A `pre_send` code without a row\n * is `invalid_input`: nothing was transmitted, so it cannot be `unknown`, and it is not a\n * condition a repeat clears. That keeps the 90-odd validation codes out of the table, which is\n * what keeps `classifyFailure` small enough for an app shell.\n */\ntype CodeTable = {\n readonly [C in Exclude<GSwapErrorCode, PreSendCode | AnswerDependentCode>]: FailureClassification;\n} & {\n readonly [C in Exclude<PreSendCode, AnswerDependentCode>]?: FailureClassification;\n};\n\n/**\n * A key that is not valid is the input's fault (`invalid_input`), except `no_key_provided`: the\n * provider had nothing to give right now (a locked wallet, an empty slot). That is the same\n * situation as a provider that throws (`KEY_PROVIDER_FAILED`), and it clears once a key exists, so\n * it is `not_sent` and Retry is safe.\n */\nfunction classifyInvalidPrivateKey(view: FailureView): FailureClassification {\n return stringParamOf(view, 'reason') === 'no_key_provided' ? NOT_SENT : INVALID_INPUT;\n}\n\n/** The status of an HTTP failure: the one it carries, else its `status` param. */\nfunction statusOfView(view: FailureView): number | undefined {\n return view.status ?? numberParamOf(view, 'status');\n}\n\n/**\n * An HTTP failure with no gateway or chain code: its meaning depends on the status and on read vs\n * write. A view that states no status at all cannot be judged: `unknown`.\n */\nfunction classifyHttpError(view: FailureView): FailureClassification {\n const status = statusOfView(view);\n if (status === undefined) return UNKNOWN;\n if (isWriteView(view)) {\n // A gateway that answered 5xx or 408 may still have queued the write; 429 and 4xx did not.\n if (status === 429) return NOT_QUEUED;\n if (status === 408 || status >= 500) return UNKNOWN;\n return NOT_QUEUED_FINAL;\n }\n return status === 408 || status === 429 || status >= 500 ? TRANSIENT : INVALID_INPUT;\n}\n\n/**\n * A core `./bridge` GalaConnect HTTP failure: like {@link classifyHttpError}, except that a\n * WRITE answered 409 is `unknown`: GalaChain's \"this uniqueKey was already consumed\", after which\n * an earlier attempt under the key may be applied (a bridge-out that locked the tokens).\n */\nfunction classifyGalaConnectHttpError(view: FailureView): FailureClassification {\n return isWriteView(view) && statusOfView(view) === 409 ? UNKNOWN : classifyHttpError(view);\n}\n\n/**\n * The version wall (HTTP 426). On a write the gateway refused it before queueing anything, and a\n * repeat of the same app version is refused the same way, so it is `not_queued` with no Retry. On a\n * read nothing changed either, and only an update helps: `invalid_input`, no Retry. Both say \"show\n * the update screen\", not \"try again\".\n */\nfunction classifyUpdateRequired(view: FailureView): FailureClassification {\n return isWriteView(view) ? NOT_QUEUED_FINAL : INVALID_INPUT;\n}\n\n/**\n * The chain dispatch failed. The gateway echoes the upstream status: 409 is \"this uniqueKey was\n * already consumed\" (the first attempt may be applied), 422 is the chain evaluating the write and\n * refusing it (definite), another 5xx is an upstream outage after which the write may have landed,\n * and a 4xx is a refusal before anything was queued.\n */\nfunction classifyChainDispatchFailed(view: FailureView): FailureClassification {\n const status = view.status;\n if (status === undefined || status === 409 || status >= 500) return UNKNOWN;\n return status === 422 ? REJECTED : NOT_QUEUED;\n}\n\n/**\n * GalaChain answered a core `GatewayWrites` token-contract write with a `Status` other than 1: the\n * chain's verdict, so the write was not applied (`rejected`), except where the verdict does not\n * prove that: a 409 is \"this uniqueKey was already consumed\" (an earlier attempt under it may be\n * applied), and a 5xx, or no status at all, cannot be judged.\n */\nfunction classifyGatewayWriteRejected(view: FailureView): FailureClassification {\n const status = view.status;\n return status === undefined || status === 409 || status >= 500 ? UNKNOWN : REJECTED;\n}\n\n/**\n * Classifies any thrown value from its code (and, for a few codes, its HTTP status), never from\n * message text, so an app does not have to re-derive \"may I offer Retry?\" and \"is the outcome\n * unknown?\" for every code. The decision for each code is in this module's table; a new catalog\n * code cannot be added without one.\n *\n * A failure BEFORE the request left (`error.phase === 'pre_send'`, `isPreSendError`) can never be\n * `unknown` or `rejected`: it is `invalid_input` (the input has to change) or `not_sent` (nothing\n * was transmitted; a repeat is safe). Every app-supplied callback the SDK calls before it sends\n * (the signer, the key provider, a token resolver) has its failure wrapped into such an error\n * (`SIGNING_FAILED`, `KEY_PROVIDER_FAILED`, `TOKEN_RESOLUTION_FAILED`, the original as `cause`),\n * so an app NEVER needs an error class of its own to say \"nothing was sent\": a locked wallet,\n * a declined signature and a failed lookup all arrive as `not_sent`, retryable.\n *\n * It accepts a `GSwapSDKError` or a plain-object VIEW of one ({@link FailureView}: `{ code,\n * params?, status?, uniqueKey?, ... }`, the shape of the wrapper an app builds from an SDK error;\n * `toFailureView` reads any value), and decides from the same fields, so an app does not need to\n * keep the original error to ask. `{ afterQueued: true }` says the write is already queued (the\n * step being classified is a confirmation): `retryable` is then always `false`.\n *\n * Anything that is not an SDK error or such a view, or a view whose `code` the catalog does not\n * define, is `{ outcome: 'unknown', retryable: false }`: the SDK\n * cannot say what happened, and refusing Retry is the safe answer. That is only what escapes AFTER\n * the request left (an app requestor that rejects with its own error, a callback of yours that\n * throws once bytes were sent). An error your own code raises BEFORE it calls the SDK says so\n * itself: give it a code of its own and `outcome` (`{ code: 'POSITION_STALE', outcome: 'not_sent'\n * }`), and the SDK answers with that outcome's classification and `mayHaveBeenQueued` is `false`.\n * The declared `outcome` is read only for a code the catalog does not define.\n *\n * A failure is a WRITE's unless it states `write: false` (`GSwapSDKError.write`, set for every\n * SDK read): see {@link FailureView}. A view that lost its `uniqueKey` is therefore still judged as\n * a write (a 5xx stays `unknown`), never as a retryable read.\n *\n * To learn that a failure came from the dark (shadow) path, read `error.shadow` (a typed field of\n * every `GSwapSDKError`) or call `isShadowError(error)`, which also covers a non-SDK object that\n * escaped `swapShadow` / `submitSwapShadow` / `ChainGateway.submitShadow`. The classification is\n * the same as on the public path; an app that withholds Retry or a public fallback after a dark\n * swap applies that policy on top of `retryable`.\n *\n * @example\n * ```ts\n * try {\n * await swaps.swap('GALA', 'GUSDC', 3000, { exactIn: '100' });\n * } catch (error) {\n * const { outcome, retryable } = classifyFailure(error);\n * if (outcome === 'unknown') keepPendingAndConfirm(error); // never a second swap\n * else if (retryable) offerRetry(); // a new request under a fresh key\n * }\n * ```\n */\nexport function classifyFailure(\n error: unknown,\n options: ClassifyFailureOptions = {},\n): FailureClassification {\n const result = classifyView(toFailureView(error));\n // A `rejected` outcome is the chain's definite no: the queued write was evaluated and NOT\n // applied, so a new request under a fresh key cannot double-apply it and Retry stays as it is.\n return options.afterQueued === true && result.outcome !== 'rejected'\n ? { ...result, retryable: false }\n : result;\n}\n\n/** Options of {@link classifyFailure}. */\nexport interface ClassifyFailureOptions {\n /**\n * The write is ALREADY queued (the gateway acknowledged it with this `uniqueKey`), and what is\n * being classified is a later step: confirming it, reading its apply result. Whatever the\n * outcome, `retryable` is then `false`: a NEW request would be a second trade, so the app\n * confirms the same key (or forgets it) and never offers \"Try again\". `outcome` is unchanged.\n *\n * The one exception is `rejected`: the chain evaluated the queued write and refused it (an\n * apply result of `Status: 0`, `CHAIN_REJECTED`; a 422 dispatch). That PROVES it was not\n * applied, so a new request under a fresh key cannot make a second trade, and `retryable`\n * keeps the answer it has without this option (`true`). Every outcome that does not prove\n * \"not applied\" (`unknown`, `transient`, `not_queued`, ...) is `false`.\n */\n afterQueued?: boolean | undefined;\n}\n\n/**\n * The classification of a failure view: the table decision for its code, the status-dependent\n * decision for the codes whose meaning needs it, and for a code the catalog does not define\n * (a wallet error, an ethers `ACTION_REJECTED`, a typo, an app's own error): `unknown` unless the\n * view DECLARES its `outcome` (an app's error, `{ code: 'POSITION_STALE', outcome: 'not_sent' }`),\n * then the SDK's own classification of that outcome. A value with no view is `unknown`.\n */\nfunction classifyView(view: FailureView | undefined): FailureClassification {\n if (view === undefined) return { ...UNKNOWN };\n if (!isCatalogCode(view.code)) {\n if (view.outcome === undefined) return { ...UNKNOWN };\n // Withhold-only: `retryable: false` removes Retry from the declared outcome, never adds it.\n return view.retryable === false\n ? classification(view.outcome, { retryable: false })\n : classification(view.outcome);\n }\n const { code } = view;\n // A core gateway HTTP failure without a chain envelope reads exactly like HTTP_ERROR (by its\n // status, and read vs write); so does a GalaConnect one, except a write's 409.\n if (code === 'HTTP_ERROR' || code === 'GATEWAY_HTTP_ERROR') {\n return { ...classifyHttpError(view) };\n }\n if (code === 'GALACONNECT_HTTP_ERROR') return { ...classifyGalaConnectHttpError(view) };\n // GalaChain's refusal of a core gateway or GalaConnect bridge write: by the chain's code.\n if (code === 'GATEWAY_WRITE_REJECTED' || code === 'BRIDGE_REQUEST_REJECTED') {\n return { ...classifyGatewayWriteRejected(view) };\n }\n if (code === 'UPDATE_REQUIRED') return { ...classifyUpdateRequired(view) };\n if (code === 'CHAIN_DISPATCH_FAILED') return { ...classifyChainDispatchFailed(view) };\n if (code === 'INVALID_PRIVATE_KEY') return { ...classifyInvalidPrivateKey(view) };\n const row: FailureClassification | undefined = (\n BY_CODE as Readonly<Partial<Record<GSwapErrorCode, FailureClassification>>>\n )[code];\n return { ...(row ?? INVALID_INPUT) };\n}\n\n/**\n * True when the failed call was a WRITE that may nevertheless have been queued: its outcome is\n * `unknown` (`classifyFailure(error).outcome === 'unknown'`: a timeout or broken connection after\n * the request left, an acknowledgement that proves nothing, a gateway 409 or 5xx, a failed\n * confirmation). The app must keep `error.uniqueKey` and confirm it, and must NOT submit again\n * under a fresh key. Every other outcome (`rejected`, `not_queued`, `not_sent`, `invalid_input`,\n * `transient`) proves the write is not pending.\n *\n * A value the SDK cannot judge (an app requestor's own rejection after the request left) is\n * `unknown` too, so it is `true`; handle errors your own code raised BEFORE calling the SDK first.\n * Meant for the catch of a write; a read never has a pending key to keep.\n *\n * @example\n * ```ts\n * try {\n * await swaps.swap('GALA', 'GUSDC', 3000, { exactIn: '100' });\n * } catch (error) {\n * if (mayHaveBeenQueued(error)) keepPendingAndConfirm(error);\n * }\n * ```\n */\nexport function mayHaveBeenQueued(error: unknown): boolean {\n return classifyFailure(error).outcome === 'unknown';\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"failure_classification.js","names":["REJECTED","classification","UNKNOWN","NOT_QUEUED","NOT_SENT","INVALID_INPUT","TRANSIENT","BY_CODE","NOT_QUEUED_FINAL","classifyInvalidPrivateKey","stringParamOf","view","statusOfView","numberParamOf","classifyHttpError","status","isWriteView","classifyGalaConnectHttpError","classifyUpdateRequired","classifyChainDispatchFailed","classifyGatewayWriteRejected","classifyFailure","result","classifyView","toFailureView","error","options","isCatalogCode","code","mayHaveBeenQueued"],"sources":["../../../src/utils/failure_classification.ts"],"sourcesContent":["import {\n isWriteView,\n numberParamOf,\n stringParamOf,\n toFailureView,\n type FailureView,\n type GALA_ERROR_CATALOG,\n} from '@gala-chain/gala-core-sdk';\nimport type { GSWAP_ERROR_CATALOG, GSwapErrorCode } from '../classes/gswap_error_catalog.js';\nimport { isCatalogCode } from '../classes/gswap_error_phase_table.js';\nimport { classification, NOT_QUEUED_FINAL, type FailureClassification } from './classification.js';\n\n/**\n * `classifyFailure` and `mayHaveBeenQueued`: the one module that holds the decision for every\n * catalog code (~135 names, a floor no minifier can remove). It needs the code table and nothing\n * else: the classification values live in `classification.ts`, the yes/no guards in\n * `failure_guards.ts`, and the error class and catalog are only named as types.\n */\n\nconst REJECTED: FailureClassification = classification('rejected');\nconst UNKNOWN: FailureClassification = classification('unknown');\nconst NOT_QUEUED: FailureClassification = classification('not_queued');\nconst NOT_SENT: FailureClassification = classification('not_sent');\nconst INVALID_INPUT: FailureClassification = classification('invalid_input');\nconst TRANSIENT: FailureClassification = classification('transient');\n\n/**\n * The classification of every code whose meaning does not depend on the failing answer. A code\n * missing here is a compile error (unless it is `pre_send`, see {@link CodeTable}), so adding a\n * catalog code forces a decision.\n */\nconst BY_CODE = {\n // ----------------------------------------------------------------------------------- transport\n REQUEST_TIMEOUT: TRANSIENT,\n TRANSPORT_ERROR: TRANSIENT,\n REQUEST_FAILED: TRANSIENT,\n\n // ------------------------------------------------------------------------------- write: unknown\n // No answer was read, or a 2xx answer that does not prove the write was queued under the signed\n // key: it may have been applied.\n SUBMISSION_OUTCOME_UNKNOWN: UNKNOWN,\n GATEWAY_REPORTED_ERROR: UNKNOWN,\n GATEWAY_DATA_MISSING: UNKNOWN,\n GATEWAY_MODE_INVALID: UNKNOWN,\n GATEWAY_SYNC_REQUEST_TRADE: UNKNOWN,\n MISSING_UNIQUE_KEY: UNKNOWN,\n UNIQUE_KEY_MISMATCH: UNKNOWN,\n // Core `GatewayWrites` (token-contract writes): no answer read, or a 2xx that proves nothing.\n GATEWAY_WRITE_OUTCOME_UNKNOWN: UNKNOWN,\n GATEWAY_RESPONSE_INVALID: UNKNOWN,\n // Core `./bridge` (GalaConnect bridge writes): a 2xx without the id or hash the next step needs,\n // or no answer read after the request left.\n BRIDGE_RESPONSE_INVALID: UNKNOWN,\n BRIDGE_WRITE_OUTCOME_UNKNOWN: UNKNOWN,\n // Any failure after GalaConnect accepted a bridge-out request: the tokens may be locked, so\n // never a retry under a fresh key, whatever the inner failure (a BridgeTokenOut 429 included).\n BRIDGE_OUT_OUTCOME_UNKNOWN: UNKNOWN,\n // A bridge-in deposit that failed once it was being sent (sending, confirming, registering): the\n // funds may have left the wallet. A status wait that timed out: the bridge may still complete.\n BRIDGE_DEPOSIT_OUTCOME_UNKNOWN: UNKNOWN,\n BRIDGE_STATUS_TIMEOUT: UNKNOWN,\n\n // --------------------------------------------------------------------- write: refused, not queued\n SHADOW_NOT_QUEUED: NOT_QUEUED_FINAL,\n METHOD_NOT_ALLOWED: NOT_QUEUED,\n SIGNATURE_INVALID: NOT_QUEUED,\n SIGNER_MISMATCH: NOT_QUEUED,\n DTO_INVALID: NOT_QUEUED,\n BOUNDS_VIOLATION: NOT_QUEUED,\n SYMBOL_CONFLICT: NOT_QUEUED,\n RATE_LIMITED: NOT_QUEUED,\n GATEWAY_REJECTED: NOT_QUEUED,\n\n // ----------------------------------------------------------------------- the chain's own verdict\n CHAIN_REJECTED: REJECTED,\n\n // ----------------------------------------------------------------------- reads of backend data\n INVALID_CHAIN_RESPONSE: TRANSIENT,\n INVALID_POSITION_RESPONSE: TRANSIENT,\n // Core `GatewayReads` (unsigned token-contract reads): nothing is applied, so a refusal or an\n // unusable answer is worth another try; a class that does not exist, or a data set larger than\n // the caller allowed, needs a different request.\n GATEWAY_READ_REJECTED: TRANSIENT,\n GATEWAY_READ_RESPONSE_INVALID: TRANSIENT,\n // Core `./bridge` GalaConnect reads (configurations, fee, status): the same as a gateway read;\n // a symbol GalaConnect has no verified configuration for needs a different request.\n BRIDGE_READ_RESPONSE_INVALID: TRANSIENT,\n BRIDGE_TOKEN_NOT_FOUND: INVALID_INPUT,\n // A token the wrappable list does not have (or its counterpart, or a channel a wrap does not\n // move between): a different request is needed.\n TOKEN_NOT_WRAPPABLE: INVALID_INPUT,\n // A failed or unusable read of the external chain before a bridge-in deposit: nothing was sent.\n BRIDGE_CHAIN_READ_FAILED: TRANSIENT,\n TOKEN_CLASS_NOT_FOUND: INVALID_INPUT,\n PAGINATION_LIMIT_EXCEEDED: INVALID_INPUT,\n\n // -------------------------------------------------------------------------------- confirmation\n CONFIRMATION_TIMEOUT: UNKNOWN,\n CONFIRMATION_ABORTED: UNKNOWN,\n CONFIRMATION_FAILED: UNKNOWN,\n CONFIRMATION_MALFORMED: UNKNOWN,\n CONFIRMATION_KEY_MISMATCH: UNKNOWN,\n\n // ------------------------------------------------------------------------------ the realtime stream\n STREAM_DISCONNECTED: TRANSIENT,\n STREAM_REQUEST_TIMEOUT: TRANSIENT,\n STREAM_CONNECT_FAILED: TRANSIENT,\n STREAM_CLOSED_BEFORE_CONNECT: TRANSIENT,\n STREAM_CONNECTION_CLOSED: TRANSIENT,\n\n // ----------------------------------------------- pre-send: an app-supplied callback failed\n // The signer, the key provider or the token resolver threw before anything was transmitted. The\n // original error is `cause`. A repeat cannot apply anything twice.\n SIGNING_FAILED: NOT_SENT,\n KEY_PROVIDER_FAILED: NOT_SENT,\n TOKEN_RESOLUTION_FAILED: NOT_SENT,\n TOKEN_NAME_RESOLUTION_FAILED: NOT_SENT,\n\n // ------------------------------------------- the call cannot work as written, found AFTER an answer\n // (a pre_send code needs no row: it is `invalid_input` by default, see `CodeTable`)\n NO_WALLET_ADDRESS: INVALID_INPUT,\n NO_POOL_AVAILABLE: INVALID_INPUT,\n INSUFFICIENT_LIQUIDITY: INVALID_INPUT,\n} as const satisfies CodeTable;\n\n/** Both catalogs as one type: every code a `GSwapSDKError` can carry reads its entry here. */\ntype Catalogs = typeof GALA_ERROR_CATALOG & typeof GSWAP_ERROR_CATALOG;\n\n/** A code (core or DEX) whose catalog phase is `pre_send`. */\ntype PreSendCode = {\n [C in GSwapErrorCode]: Catalogs[C]['phase'] extends 'pre_send' ? C : never;\n}[GSwapErrorCode];\n\n/** The codes whose classification depends on the failing answer, not on a table row. */\ntype AnswerDependentCode =\n | 'HTTP_ERROR'\n | 'UPDATE_REQUIRED'\n | 'CHAIN_DISPATCH_FAILED'\n | 'INVALID_PRIVATE_KEY'\n | 'GATEWAY_HTTP_ERROR'\n | 'GALACONNECT_HTTP_ERROR'\n | 'GATEWAY_WRITE_REJECTED'\n | 'BRIDGE_REQUEST_REJECTED';\n\n/**\n * Every code that is NOT `pre_send` needs a row (a new one is a compile error until it has one);\n * a `pre_send` code may have one (`SIGNING_FAILED` is `not_sent`). A `pre_send` code without a row\n * is `invalid_input`: nothing was transmitted, so it cannot be `unknown`, and it is not a\n * condition a repeat clears. That keeps the 90-odd validation codes out of the table, which is\n * what keeps `classifyFailure` small enough for an app shell.\n */\ntype CodeTable = {\n readonly [C in Exclude<GSwapErrorCode, PreSendCode | AnswerDependentCode>]: FailureClassification;\n} & {\n readonly [C in Exclude<PreSendCode, AnswerDependentCode>]?: FailureClassification;\n};\n\n/**\n * A key that is not valid is the input's fault (`invalid_input`), except `no_key_provided`: the\n * provider had nothing to give right now (a locked wallet, an empty slot). That is the same\n * situation as a provider that throws (`KEY_PROVIDER_FAILED`), and it clears once a key exists, so\n * it is `not_sent` and Retry is safe.\n */\nfunction classifyInvalidPrivateKey(view: FailureView): FailureClassification {\n return stringParamOf(view, 'reason') === 'no_key_provided' ? NOT_SENT : INVALID_INPUT;\n}\n\n/** The status of an HTTP failure: the one it carries, else its `status` param. */\nfunction statusOfView(view: FailureView): number | undefined {\n return view.status ?? numberParamOf(view, 'status');\n}\n\n/**\n * An HTTP failure with no gateway or chain code: its meaning depends on the status and on read vs\n * write. A view that states no status at all cannot be judged: `unknown`.\n */\nfunction classifyHttpError(view: FailureView): FailureClassification {\n const status = statusOfView(view);\n if (status === undefined) return UNKNOWN;\n if (isWriteView(view)) {\n // A gateway that answered 5xx or 408 may still have queued the write; 429 and 4xx did not.\n if (status === 429) return NOT_QUEUED;\n if (status === 408 || status >= 500) return UNKNOWN;\n return NOT_QUEUED_FINAL;\n }\n return status === 408 || status === 429 || status >= 500 ? TRANSIENT : INVALID_INPUT;\n}\n\n/**\n * A core `./bridge` GalaConnect HTTP failure: like {@link classifyHttpError}, except that a\n * WRITE answered 409 is `unknown`: GalaChain's \"this uniqueKey was already consumed\", after which\n * an earlier attempt under the key may be applied (a bridge-out that locked the tokens).\n */\nfunction classifyGalaConnectHttpError(view: FailureView): FailureClassification {\n return isWriteView(view) && statusOfView(view) === 409 ? UNKNOWN : classifyHttpError(view);\n}\n\n/**\n * The version wall (HTTP 426). On a write the gateway refused it before queueing anything, and a\n * repeat of the same app version is refused the same way, so it is `not_queued` with no Retry. On a\n * read nothing changed either, and only an update helps: `invalid_input`, no Retry. Both say \"show\n * the update screen\", not \"try again\".\n */\nfunction classifyUpdateRequired(view: FailureView): FailureClassification {\n return isWriteView(view) ? NOT_QUEUED_FINAL : INVALID_INPUT;\n}\n\n/**\n * The chain dispatch failed. The gateway echoes the upstream status: 409 is \"this uniqueKey was\n * already consumed\" (the first attempt may be applied), 422 is the chain evaluating the write and\n * refusing it (definite), another 5xx is an upstream outage after which the write may have landed,\n * and a 4xx is a refusal before anything was queued.\n */\nfunction classifyChainDispatchFailed(view: FailureView): FailureClassification {\n const status = view.status;\n if (status === undefined || status === 409 || status >= 500) return UNKNOWN;\n return status === 422 ? REJECTED : NOT_QUEUED;\n}\n\n/**\n * GalaChain answered a core `GatewayWrites` token-contract write with a `Status` other than 1: the\n * chain's verdict, so the write was not applied (`rejected`), except where the verdict does not\n * prove that: a 409 is \"this uniqueKey was already consumed\" (an earlier attempt under it may be\n * applied), and a 5xx, or no status at all, cannot be judged.\n */\nfunction classifyGatewayWriteRejected(view: FailureView): FailureClassification {\n const status = view.status;\n return status === undefined || status === 409 || status >= 500 ? UNKNOWN : REJECTED;\n}\n\n/**\n * Classifies any thrown value from its code (and, for a few codes, its HTTP status), never from\n * message text, so an app does not have to re-derive \"may I offer Retry?\" and \"is the outcome\n * unknown?\" for every code. The decision for each code is in this module's table; a new catalog\n * code cannot be added without one.\n *\n * A failure BEFORE the request left (`error.phase === 'pre_send'`, `isPreSendError`) can never be\n * `unknown` or `rejected`: it is `invalid_input` (the input has to change) or `not_sent` (nothing\n * was transmitted; a repeat is safe). Every app-supplied callback the SDK calls before it sends\n * (the signer, the key provider, a token resolver) has its failure wrapped into such an error\n * (`SIGNING_FAILED`, `KEY_PROVIDER_FAILED`, `TOKEN_RESOLUTION_FAILED`, the original as `cause`),\n * so an app NEVER needs an error class of its own to say \"nothing was sent\": a locked wallet,\n * a declined signature and a failed lookup all arrive as `not_sent`, retryable.\n *\n * It accepts a `GSwapSDKError` or a plain-object VIEW of one ({@link FailureView}: `{ code,\n * params?, status?, uniqueKey?, ... }`, the shape of the wrapper an app builds from an SDK error;\n * `toFailureView` reads any value), and decides from the same fields, so an app does not need to\n * keep the original error to ask. `{ afterQueued: true }` says the write is already queued (the\n * step being classified is a confirmation): `retryable` is then always `false`.\n *\n * Anything that is not an SDK error or such a view, or a view whose `code` the catalog does not\n * define, is `{ outcome: 'unknown', retryable: false }`: the SDK\n * cannot say what happened, and refusing Retry is the safe answer. That is only what escapes AFTER\n * the request left (an app requestor that rejects with its own error, a callback of yours that\n * throws once bytes were sent). An error your own code raises BEFORE it calls the SDK says so\n * itself: give it a code of its own and `outcome` (`{ code: 'POSITION_STALE', outcome: 'not_sent'\n * }`), and the SDK answers with that outcome's classification and `mayHaveBeenQueued` is `false`.\n * The declared `outcome` is read only for a code the catalog does not define.\n *\n * A failure is a WRITE's unless it states `write: false` (`GSwapSDKError.write`, set for every\n * SDK read): see {@link FailureView}. A view that lost its `uniqueKey` is therefore still judged as\n * a write (a 5xx stays `unknown`), never as a retryable read.\n *\n * To learn that a failure came from the dark (shadow) path, read `error.shadow` (a typed field of\n * every `GSwapSDKError`) or call `isShadowError(error)`, which also covers a non-SDK object that\n * escaped `swapShadow` / `submitSwapShadow` / `ChainGateway.submitShadow`. The classification is\n * the same as on the public path; an app that withholds Retry or a public fallback after a dark\n * swap applies that policy on top of `retryable`.\n *\n * @example\n * ```ts\n * try {\n * await swaps.swap('GALA', 'GUSDC', 3000, { exactIn: '100' });\n * } catch (error) {\n * const { outcome, retryable } = classifyFailure(error);\n * if (outcome === 'unknown') keepPendingAndConfirm(error); // never a second swap\n * else if (retryable) offerRetry(); // a new request under a fresh key\n * }\n * ```\n */\nexport function classifyFailure(\n error: unknown,\n options: ClassifyFailureOptions = {},\n): FailureClassification {\n const result = classifyView(toFailureView(error));\n // A `rejected` outcome is the chain's definite no: the queued write was evaluated and NOT\n // applied, so a new request under a fresh key cannot double-apply it and Retry stays as it is.\n return options.afterQueued === true && result.outcome !== 'rejected'\n ? { ...result, retryable: false }\n : result;\n}\n\n/** Options of {@link classifyFailure}. */\nexport interface ClassifyFailureOptions {\n /**\n * The write is ALREADY queued (the gateway acknowledged it with this `uniqueKey`), and what is\n * being classified is a later step: confirming it, reading its apply result. Whatever the\n * outcome, `retryable` is then `false`: a NEW request would be a second trade, so the app\n * confirms the same key (or forgets it) and never offers \"Try again\". `outcome` is unchanged.\n *\n * The one exception is `rejected`: the chain evaluated the queued write and refused it (an\n * apply result of `Status: 0`, `CHAIN_REJECTED`; a 422 dispatch). That PROVES it was not\n * applied, so a new request under a fresh key cannot make a second trade, and `retryable`\n * keeps the answer it has without this option (`true`). Every outcome that does not prove\n * \"not applied\" (`unknown`, `transient`, `not_queued`, ...) is `false`.\n */\n afterQueued?: boolean | undefined;\n}\n\n/**\n * The classification of a failure view: the table decision for its code, the status-dependent\n * decision for the codes whose meaning needs it, and for a code the catalog does not define\n * (a wallet error, an ethers `ACTION_REJECTED`, a typo, an app's own error): `unknown` unless the\n * view DECLARES its `outcome` (an app's error, `{ code: 'POSITION_STALE', outcome: 'not_sent' }`),\n * then the SDK's own classification of that outcome. A value with no view is `unknown`.\n */\nfunction classifyView(view: FailureView | undefined): FailureClassification {\n if (view === undefined) return { ...UNKNOWN };\n if (!isCatalogCode(view.code)) {\n if (view.outcome === undefined) return { ...UNKNOWN };\n // Withhold-only: `retryable: false` removes Retry from the declared outcome, never adds it.\n return view.retryable === false\n ? classification(view.outcome, { retryable: false })\n : classification(view.outcome);\n }\n const { code } = view;\n // A core gateway HTTP failure without a chain envelope reads exactly like HTTP_ERROR (by its\n // status, and read vs write); so does a GalaConnect one, except a write's 409.\n if (code === 'HTTP_ERROR' || code === 'GATEWAY_HTTP_ERROR') {\n return { ...classifyHttpError(view) };\n }\n if (code === 'GALACONNECT_HTTP_ERROR') return { ...classifyGalaConnectHttpError(view) };\n // GalaChain's refusal of a core gateway or GalaConnect bridge write: by the chain's code.\n if (code === 'GATEWAY_WRITE_REJECTED' || code === 'BRIDGE_REQUEST_REJECTED') {\n return { ...classifyGatewayWriteRejected(view) };\n }\n if (code === 'UPDATE_REQUIRED') return { ...classifyUpdateRequired(view) };\n if (code === 'CHAIN_DISPATCH_FAILED') return { ...classifyChainDispatchFailed(view) };\n if (code === 'INVALID_PRIVATE_KEY') return { ...classifyInvalidPrivateKey(view) };\n const row: FailureClassification | undefined = (\n BY_CODE as Readonly<Partial<Record<GSwapErrorCode, FailureClassification>>>\n )[code];\n return { ...(row ?? INVALID_INPUT) };\n}\n\n/**\n * True when the failed call was a WRITE that may nevertheless have been queued: its outcome is\n * `unknown` (`classifyFailure(error).outcome === 'unknown'`: a timeout or broken connection after\n * the request left, an acknowledgement that proves nothing, a gateway 409 or 5xx, a failed\n * confirmation). The app must keep `error.uniqueKey` and confirm it, and must NOT submit again\n * under a fresh key. Every other outcome (`rejected`, `not_queued`, `not_sent`, `invalid_input`,\n * `transient`) proves the write is not pending.\n *\n * A value the SDK cannot judge (an app requestor's own rejection after the request left) is\n * `unknown` too, so it is `true`; handle errors your own code raised BEFORE calling the SDK first.\n * Meant for the catch of a write; a read never has a pending key to keep.\n *\n * @example\n * ```ts\n * try {\n * await swaps.swap('GALA', 'GUSDC', 3000, { exactIn: '100' });\n * } catch (error) {\n * if (mayHaveBeenQueued(error)) keepPendingAndConfirm(error);\n * }\n * ```\n */\nexport function mayHaveBeenQueued(error: unknown): boolean {\n return classifyFailure(error).outcome === 'unknown';\n}\n"],"mappings":"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"}