@gala-chain/gswap-sdk 1.2.0-rc.8 → 1.2.0-rc.9

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 (42) hide show
  1. package/dist/browser/classes/pending_swap_fate.js +2 -0
  2. package/dist/browser/classes/pending_swap_fate.js.map +1 -0
  3. package/dist/browser/classes/pending_swaps.js +1 -1
  4. package/dist/browser/classes/pending_swaps.js.map +1 -1
  5. package/dist/browser/classes/positions.js +1 -1
  6. package/dist/browser/classes/positions.js.map +1 -1
  7. package/dist/browser/classes/storage_adapters.js +2 -0
  8. package/dist/browser/classes/storage_adapters.js.map +1 -0
  9. package/dist/browser/classes/symbols.js +1 -1
  10. package/dist/browser/classes/symbols.js.map +1 -1
  11. package/dist/browser/index.js +1 -1
  12. package/dist/browser/utils/class_key.js +2 -0
  13. package/dist/browser/utils/class_key.js.map +1 -0
  14. package/dist/browser/utils/classification.js +2 -0
  15. package/dist/browser/utils/classification.js.map +1 -0
  16. package/dist/browser/utils/dto_builders.js +1 -1
  17. package/dist/browser/utils/failure_classification.js +1 -1
  18. package/dist/browser/utils/failure_classification.js.map +1 -1
  19. package/dist/browser/utils/failure_guards.js +2 -0
  20. package/dist/browser/utils/failure_guards.js.map +1 -0
  21. package/dist/browser/utils/ordering.js +1 -1
  22. package/dist/browser/utils/ordering.js.map +1 -1
  23. package/dist/browser/utils/tick_align.js +2 -0
  24. package/dist/browser/utils/tick_align.js.map +1 -0
  25. package/dist/browser/utils/tick_math.js +2 -0
  26. package/dist/browser/utils/tick_math.js.map +1 -0
  27. package/dist/browser/utils/tick_range.js +2 -0
  28. package/dist/browser/utils/tick_range.js.map +1 -0
  29. package/dist/browser/utils/ticks.js +1 -1
  30. package/dist/browser/utils/ticks.js.map +1 -1
  31. package/dist/browser/utils/transport.js +1 -1
  32. package/dist/browser/utils/validation.js +1 -1
  33. package/dist/browser/utils/validation.js.map +1 -1
  34. package/dist/browser/version.js +1 -1
  35. package/dist/browser/version.js.map +1 -1
  36. package/dist/index.cjs +563 -340
  37. package/dist/index.cjs.map +1 -1
  38. package/dist/index.d.cts +338 -167
  39. package/dist/index.d.ts +338 -167
  40. package/dist/index.mjs +558 -341
  41. package/dist/index.mjs.map +1 -1
  42. package/package.json +1 -1
@@ -0,0 +1,2 @@
1
+ import{classifyFailure as e}from"../utils/failure_classification.js";const t=[`forget`,`forget_quietly`,`keep`],n={rejected:`forget`,invalid_input:`forget_quietly`,unknown:`keep`,not_queued:`keep`,not_sent:`keep`,transient:`keep`};function r(e){return Object.hasOwn(n,e)?n[e]:`keep`}function i(t){return r(e(t,{afterQueued:!0}).outcome)}export{t as PENDING_SWAP_FATES,i as pendingSwapFateFor,r as pendingSwapFateOfOutcome};
2
+ //# sourceMappingURL=pending_swap_fate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pending_swap_fate.js","names":[],"sources":["../../../src/classes/pending_swap_fate.ts"],"sourcesContent":["import { classifyFailure } from '../utils/failure_classification.js';\nimport type { FailureOutcome } from '../utils/failure_outcomes.js';\n\n/**\n * What to do with a pending swap after confirming it failed (the answer of\n * {@link pendingSwapFateFor}).\n *\n * - `forget`: the chain REFUSED the trade, so it did not happen. Drop it AND tell the user.\n * - `forget_quietly`: the confirmation can never succeed (the key is refused: `invalid_input`).\n * Drop it without claiming anything about the trade.\n * - `keep`: the trade may still have been applied (a timeout, a broken connection, a gateway\n * hiccup, anything not proven). Leave it pending and look again later.\n *\n * This module holds the decision and nothing of the store, so an app shell that only needs the\n * fate pays for `classifyFailure` and this table, not for `PendingSwapStore`.\n */\nexport const PENDING_SWAP_FATES = ['forget', 'forget_quietly', 'keep'] as const;\n\n/** One member of {@link PENDING_SWAP_FATES}. */\nexport type PendingSwapFate = (typeof PENDING_SWAP_FATES)[number];\n\n/**\n * The fate of a pending swap for each failure OUTCOME. Only a chain rejection proves the trade did\n * not happen; an input that can never be confirmed is dropped quietly; every other outcome leaves\n * the trade possibly applied. Typed over `FailureOutcome`, so a new outcome does not compile until\n * it is decided here.\n */\nconst FATE_OF_OUTCOME = {\n rejected: 'forget',\n invalid_input: 'forget_quietly',\n unknown: 'keep',\n not_queued: 'keep',\n not_sent: 'keep',\n transient: 'keep',\n} as const satisfies Record<FailureOutcome, PendingSwapFate>;\n\n/**\n * {@link pendingSwapFateFor} from an outcome the app already holds (for example the\n * `classifyFailure(error).outcome` it stored on its own failure wrapper). An outcome this SDK\n * version does not know (a newer SDK at run time) is `keep`: never forget what may have happened.\n */\nexport function pendingSwapFateOfOutcome(outcome: FailureOutcome): PendingSwapFate {\n return Object.hasOwn(FATE_OF_OUTCOME, outcome) ? FATE_OF_OUTCOME[outcome] : 'keep';\n}\n\n/**\n * The fate of a queued swap whose confirmation (`confirmAppliedTrade`) just failed with `error`,\n * from the SDK's classification alone: `forget` for a chain rejection (`rejected`), `forget_quietly`\n * for a key that can never be confirmed (`invalid_input`), `keep` for every other outcome\n * (`unknown`, `not_queued`, `not_sent`, `transient`) and for any value the SDK cannot judge. It\n * accepts an SDK error or a plain-object view of one (see `FailureView`). It replaces the wallet's\n * `pendingSwapActionFor` and frontend_v2's `confirmationStanceOf`.\n *\n * @example\n * ```ts\n * try {\n * await confirmAppliedTrade(client, swap);\n * } catch (error) {\n * if ((await store.settle(swap.uniqueKey, error)) === 'forget') tellTheUserItFailed();\n * }\n * ```\n */\nexport function pendingSwapFateFor(error: unknown): PendingSwapFate {\n return pendingSwapFateOfOutcome(classifyFailure(error, { afterQueued: true }).outcome);\n}\n"],"mappings":"qEAgBA,MAAa,EAAqB,CAAC,SAAU,iBAAkB,MAAM,EAW/D,EAAkB,CACtB,SAAU,SACV,cAAe,iBACf,QAAS,OACT,WAAY,OACZ,SAAU,OACV,UAAW,MACb,EAOA,SAAgB,EAAyB,EAA0C,CACjF,OAAO,OAAO,OAAO,EAAiB,CAAO,EAAI,EAAgB,GAAW,MAC9E,CAmBA,SAAgB,EAAmB,EAAiC,CAClE,OAAO,EAAyB,EAAgB,EAAO,CAAE,YAAa,EAAK,CAAC,CAAC,CAAC,OAAO,CACvF"}
@@ -1,2 +1,2 @@
1
- import{isValidUniqueKey as e}from"../utils/unique_key_syntax.js";import t from"../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/defineProperty.js";import{classifyFailure as n}from"../utils/failure_classification.js";const r=`pendingSwaps.v1`,i=6048e5;function a(e){let t=()=>typeof e==`function`?e():e;return{async get(e){let n=await t().get(e);return Object.hasOwn(n,e)?n[e]:void 0},async set(e,n){await t().set({[e]:n})}}}const o=[`expired`,`invalid_unique_key`,`chain_rejected`,`unconfirmable`],s=[`forget`,`forget_quietly`,`keep`],c={rejected:`forget`,invalid_input:`forget_quietly`,unknown:`keep`,not_queued:`keep`,not_sent:`keep`,transient:`keep`};function l(e){return Object.hasOwn(c,e)?c[e]:`keep`}function u(e){return l(n(e,{afterQueued:!0}).outcome)}var d=class{constructor(e,n={}){this.storage=e,t(this,`storageKey`,void 0),t(this,`darkFlagField`,void 0),t(this,`insertAt`,void 0),t(this,`listOrder`,void 0),t(this,`now`,void 0),t(this,`onPrune`,void 0),t(this,`queue`,Promise.resolve()),t(this,`toStored`,e=>({uniqueKey:e.uniqueKey,requestTransactionId:e.requestTransactionId,wallet:e.wallet,token0Symbol:e.token0Symbol,token1Symbol:e.token1Symbol,[this.darkFlagField]:e.isDarkSwap,createdAt:e.createdAt})),this.storageKey=n.storageKey??`pendingSwaps.v1`,this.darkFlagField=n.darkFlagField??`isDarkSwap`,this.insertAt=n.insertAt??`end`,this.listOrder=n.listOrder??`stored`,this.now=n.now??Date.now,this.onPrune=n.onPrune}remember(t){return e(t.uniqueKey)?this.enqueue(async e=>{let n=(await this.read(e)).filter(e=>e.uniqueKey!==t.uniqueKey).map(this.toStored),r=this.toStored(t);await e.set(this.storageKey,this.insertAt===`end`?[...n,r]:[r,...n])}):(this.report([{swap:t,reason:`invalid_unique_key`}]),Promise.resolve())}forget(e){return this.enqueue(async t=>{let n=await this.read(t);await t.set(this.storageKey,n.filter(t=>t.uniqueKey!==e).map(this.toStored))})}async settle(e,t){let n=u(t);if(n===`keep`)return n;let r=await this.enqueue(async t=>{let n=await this.read(t),r=n.find(t=>t.uniqueKey===e);if(r!==void 0)return await t.set(this.storageKey,n.filter(t=>t.uniqueKey!==e).map(this.toStored)),r});return r!==void 0&&this.report([{swap:r,reason:n===`forget`?`chain_rejected`:`unconfirmable`}]),n}async list(e){return await this.enqueue(async t=>{let n=await this.read(t),r=this.now(),i=[],a=[];for(let e of n){let t=this.pruneReasonOf(e,r);t===void 0?a.push(e):i.push({swap:e,reason:t})}i.length>0&&(await t.set(this.storageKey,a.map(this.toStored)),this.report(i));let o=a.filter(t=>t.wallet===e);return this.listOrder===`newest-first`?o.sort((e,t)=>t.createdAt-e.createdAt):o})??[]}pruneReasonOf(t,n){return e(t.uniqueKey)?n-t.createdAt<6048e5?void 0:`expired`:`invalid_unique_key`}report(e){for(let t of e)try{this.onPrune?.(t)}catch{}}enqueue(e){let t=this.storage;if(t===void 0)return Promise.resolve(void 0);let n=this.queue.then(async()=>{try{return await e(t)}catch{return}});return this.queue=n,n}async read(e){let t=await e.get(this.storageKey);if(!Array.isArray(t))return[];let n=[];for(let e of t){let t=this.fromStored(e);t!==void 0&&n.push(t)}return n}fromStored(e){if(typeof e!=`object`||!e)return;let t=e,n=t[this.darkFlagField],{uniqueKey:r,requestTransactionId:i,wallet:a,token0Symbol:o,token1Symbol:s,createdAt:c}=t;if(!(typeof r!=`string`||typeof i!=`string`&&i!==null||typeof a!=`string`||typeof o!=`string`||typeof s!=`string`||typeof n!=`boolean`||typeof c!=`number`||!Number.isFinite(c)))return{uniqueKey:r,requestTransactionId:i,wallet:a,token0Symbol:o,token1Symbol:s,isDarkSwap:n,createdAt:c}}};export{r as PENDING_SWAPS_STORAGE_KEY,i as PENDING_SWAP_EXPIRY_MS,s as PENDING_SWAP_FATES,o as PENDING_SWAP_PRUNE_REASONS,d as PendingSwapStore,u as pendingSwapFateFor,l as pendingSwapFateOfOutcome,a as storageAdapterFromChromeStorage};
1
+ import{isValidUniqueKey as e}from"../utils/unique_key_syntax.js";import t from"../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/defineProperty.js";import{pendingSwapFateFor as n}from"./pending_swap_fate.js";const r=`pendingSwaps.v1`,i=6048e5,a=[`expired`,`invalid_unique_key`,`chain_rejected`,`unconfirmable`];var o=class{constructor(e,n={}){this.storage=e,t(this,`storageKey`,void 0),t(this,`darkFlagField`,void 0),t(this,`insertAt`,void 0),t(this,`listOrder`,void 0),t(this,`now`,void 0),t(this,`onPrune`,void 0),t(this,`queue`,Promise.resolve()),t(this,`toStored`,e=>({uniqueKey:e.uniqueKey,requestTransactionId:e.requestTransactionId,wallet:e.wallet,token0Symbol:e.token0Symbol,token1Symbol:e.token1Symbol,[this.darkFlagField]:e.isDarkSwap,createdAt:e.createdAt})),this.storageKey=n.storageKey??`pendingSwaps.v1`,this.darkFlagField=n.darkFlagField??`isDarkSwap`,this.insertAt=n.insertAt??`end`,this.listOrder=n.listOrder??`stored`,this.now=n.now??Date.now,this.onPrune=n.onPrune}remember(t){return e(t.uniqueKey)?this.enqueue(async e=>{let n=(await this.read(e)).filter(e=>e.uniqueKey!==t.uniqueKey).map(this.toStored),r=this.toStored(t);await e.set(this.storageKey,this.insertAt===`end`?[...n,r]:[r,...n])}):(this.report([{swap:t,reason:`invalid_unique_key`}]),Promise.resolve())}forget(e){return this.enqueue(async t=>{let n=await this.read(t);await t.set(this.storageKey,n.filter(t=>t.uniqueKey!==e).map(this.toStored))})}async settle(e,t){let r=n(t);if(r===`keep`)return r;let i=await this.enqueue(async t=>{let n=await this.read(t),r=n.find(t=>t.uniqueKey===e);if(r!==void 0)return await t.set(this.storageKey,n.filter(t=>t.uniqueKey!==e).map(this.toStored)),r});return i!==void 0&&this.report([{swap:i,reason:r===`forget`?`chain_rejected`:`unconfirmable`}]),r}async list(e){return await this.enqueue(async t=>{let n=await this.read(t),r=this.now(),i=[],a=[];for(let e of n){let t=this.pruneReasonOf(e,r);t===void 0?a.push(e):i.push({swap:e,reason:t})}i.length>0&&(await t.set(this.storageKey,a.map(this.toStored)),this.report(i));let o=a.filter(t=>t.wallet===e);return this.listOrder===`newest-first`?o.sort((e,t)=>t.createdAt-e.createdAt):o})??[]}pruneReasonOf(t,n){return e(t.uniqueKey)?n-t.createdAt<6048e5?void 0:`expired`:`invalid_unique_key`}report(e){for(let t of e)try{this.onPrune?.(t)}catch{}}enqueue(e){let t=this.storage;if(t===void 0)return Promise.resolve(void 0);let n=this.queue.then(async()=>{try{return await e(t)}catch{return}});return this.queue=n,n}async read(e){let t=await e.get(this.storageKey);if(!Array.isArray(t))return[];let n=[];for(let e of t){let t=this.fromStored(e);t!==void 0&&n.push(t)}return n}fromStored(e){if(typeof e!=`object`||!e)return;let t=e,n=t[this.darkFlagField],{uniqueKey:r,requestTransactionId:i,wallet:a,token0Symbol:o,token1Symbol:s,createdAt:c}=t;if(!(typeof r!=`string`||typeof i!=`string`&&i!==null||typeof a!=`string`||typeof o!=`string`||typeof s!=`string`||typeof n!=`boolean`||typeof c!=`number`||!Number.isFinite(c)))return{uniqueKey:r,requestTransactionId:i,wallet:a,token0Symbol:o,token1Symbol:s,isDarkSwap:n,createdAt:c}}};export{r as PENDING_SWAPS_STORAGE_KEY,i as PENDING_SWAP_EXPIRY_MS,a as PENDING_SWAP_PRUNE_REASONS,o as PendingSwapStore};
2
2
  //# sourceMappingURL=pending_swaps.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pending_swaps.js","names":[],"sources":["../../../src/classes/pending_swaps.ts"],"sourcesContent":["import { classifyFailure } from '../utils/failure_classification.js';\nimport type { FailureOutcome } from '../utils/failure_outcomes.js';\nimport { isValidUniqueKey } from '../utils/unique_key_syntax.js';\n\n/** Key the wallet_extension has always used; its storage value is a plain array of entries. */\nexport const PENDING_SWAPS_STORAGE_KEY = 'pendingSwaps.v1';\n\n/** Entries older than this are dropped the next time a wallet's swaps are listed. */\nexport const PENDING_SWAP_EXPIRY_MS = 7 * 24 * 60 * 60 * 1000;\n\n/**\n * Async key/value storage the pending-swap store persists through. It stores STRUCTURED VALUES\n * (the store never serializes), so each platform decides how a value lands on disk:\n *\n * - `chrome.storage.local`: `get: async (k) => (await chrome.storage.local.get(k))[k]`,\n * `set: (k, v) => chrome.storage.local.set({ [k]: v })`. This is the wallet's stored shape.\n * - `localStorage` (frontend_v2): `get: async (k) => { const raw = localStorage.getItem(k);\n * if (raw === null) return undefined; try { return JSON.parse(raw); } catch { return raw; } }`,\n * `set: async (k, v) => localStorage.setItem(k, JSON.stringify(v))`. Corrupt JSON MUST come back\n * as a value (here the raw string), not as a rejection: a value that is not an array reads as\n * \"no entries\" and the next write repairs it, whereas a `get` that rejects forever makes every\n * `remember` a no-op.\n * - React Native: AsyncStorage with the same JSON wrapping and the same corrupt-JSON rule.\n *\n * `get` resolves `undefined` for a missing key. Either method may reject; the store treats that\n * as \"storage unavailable\" and never lets it surface (see {@link PendingSwapStore}).\n */\nexport interface StorageAdapter {\n get(key: string): Promise<unknown>;\n set(key: string, value: unknown): Promise<void>;\n}\n\n/**\n * The part of a `chrome.storage` area ({@link storageAdapterFromChromeStorage}) the adapter uses,\n * written structurally so the SDK needs no `chrome` types: `chrome.storage.local`,\n * `chrome.storage.session`, `browser.storage.local` (webextension-polyfill) and any object with\n * these two promise-returning methods satisfy it. It is the Manifest V3 promise form: the methods\n * are called WITHOUT a callback.\n */\nexport interface ChromeStorageAreaLike {\n get(key: string): Promise<Record<string, unknown>>;\n set(items: Record<string, unknown>): Promise<void>;\n}\n\n/**\n * A {@link StorageAdapter} over a `chrome.storage` area, the shape the wallet stores pending swaps\n * in: the value lands under the key as the structured value it is (the browser clones it; nothing\n * is serialized here). A missing key resolves `undefined`; a rejection of the area reaches the\n * store, which treats it as \"storage unavailable\".\n *\n * Pass a FUNCTION that returns the area to look it up at each call instead of when the adapter is\n * built: a module that is imported where `chrome` does not exist yet (a test, a closing extension\n * context) then fails only when the store touches storage, and that failure (a throw inside the\n * function) is the same \"storage unavailable\" rejection.\n *\n * @example\n * ```ts\n * const store = new PendingSwapStore(storageAdapterFromChromeStorage(chrome.storage.local));\n * const lazy = new PendingSwapStore(storageAdapterFromChromeStorage(() => chrome.storage.local));\n * ```\n */\nexport function storageAdapterFromChromeStorage(\n area: ChromeStorageAreaLike | (() => ChromeStorageAreaLike),\n): StorageAdapter {\n const areaNow = (): ChromeStorageAreaLike => (typeof area === 'function' ? area() : area);\n return {\n async get(key) {\n const items = await areaNow().get(key);\n return Object.hasOwn(items, key) ? items[key] : undefined;\n },\n async set(key, value) {\n await areaNow().set({ [key]: value });\n },\n };\n}\n\n/** A RequestTrade that was accepted by the gateway but whose outcome has not been read yet. */\nexport interface PendingSwap {\n uniqueKey: string;\n requestTransactionId: string | null;\n /** The wallet (account) that submitted the swap; `list` filters by it. */\n wallet: string;\n token0Symbol: string;\n token1Symbol: string;\n /** Whether the swap was a shadow (\"dark\") swap. */\n isDarkSwap: boolean;\n /** Epoch milliseconds when the swap was queued. */\n createdAt: number;\n}\n\n/**\n * Why the store dropped a pending swap (the `reason` of {@link PrunedPendingSwap}).\n *\n * - `expired`: older than {@link PENDING_SWAP_EXPIRY_MS}.\n * - `invalid_unique_key`: its `uniqueKey` is not a valid apply-results key (1 to 128 characters\n * of `A-Za-z0-9._:-`). `confirmAppliedTrade` refuses such a key with `INVALID_UNIQUE_KEY`\n * before reading anything and always will, so keeping the entry would retry it forever. The\n * SDK never signs such a key; an entry like this is corrupt or foreign.\n * - `chain_rejected`: {@link PendingSwapStore.settle} dropped it because a confirmation proved the\n * chain REFUSED the trade (`pendingSwapFateFor` is `forget`): it did not happen.\n * - `unconfirmable`: {@link PendingSwapStore.settle} dropped it because a confirmation failed in a\n * way that never changes (`forget_quietly`: the key itself is refused): nothing can be learned.\n */\nexport const PENDING_SWAP_PRUNE_REASONS = [\n 'expired',\n 'invalid_unique_key',\n 'chain_rejected',\n 'unconfirmable',\n] as const;\n\n/** One member of {@link PENDING_SWAP_PRUNE_REASONS}. */\nexport type PendingSwapPruneReason = (typeof PENDING_SWAP_PRUNE_REASONS)[number];\n\n/**\n * What to do with a pending swap after confirming it failed (the answer of\n * {@link pendingSwapFateFor}).\n *\n * - `forget`: the chain REFUSED the trade, so it did not happen. Drop it AND tell the user.\n * - `forget_quietly`: the confirmation can never succeed (the key is refused: `invalid_input`).\n * Drop it without claiming anything about the trade.\n * - `keep`: the trade may still have been applied (a timeout, a broken connection, a gateway\n * hiccup, anything not proven). Leave it pending and look again later.\n */\nexport const PENDING_SWAP_FATES = ['forget', 'forget_quietly', 'keep'] as const;\n\n/** One member of {@link PENDING_SWAP_FATES}. */\nexport type PendingSwapFate = (typeof PENDING_SWAP_FATES)[number];\n\n/**\n * The fate of a pending swap for each failure OUTCOME. Only a chain rejection proves the trade did\n * not happen; an input that can never be confirmed is dropped quietly; every other outcome leaves\n * the trade possibly applied. Typed over `FailureOutcome`, so a new outcome does not compile until\n * it is decided here.\n */\nconst FATE_OF_OUTCOME = {\n rejected: 'forget',\n invalid_input: 'forget_quietly',\n unknown: 'keep',\n not_queued: 'keep',\n not_sent: 'keep',\n transient: 'keep',\n} as const satisfies Record<FailureOutcome, PendingSwapFate>;\n\n/**\n * {@link pendingSwapFateFor} from an outcome the app already holds (for example the\n * `classifyFailure(error).outcome` it stored on its own failure wrapper). An outcome this SDK\n * version does not know (a newer SDK at run time) is `keep`: never forget what may have happened.\n */\nexport function pendingSwapFateOfOutcome(outcome: FailureOutcome): PendingSwapFate {\n return Object.hasOwn(FATE_OF_OUTCOME, outcome) ? FATE_OF_OUTCOME[outcome] : 'keep';\n}\n\n/**\n * The fate of a queued swap whose confirmation (`confirmAppliedTrade`) just failed with `error`,\n * from the SDK's classification alone: `forget` for a chain rejection (`rejected`), `forget_quietly`\n * for a key that can never be confirmed (`invalid_input`), `keep` for every other outcome\n * (`unknown`, `not_queued`, `not_sent`, `transient`) and for any value the SDK cannot judge. It\n * accepts an SDK error or a plain-object view of one (see `FailureView`). It replaces the wallet's\n * `pendingSwapActionFor` and frontend_v2's `confirmationStanceOf`.\n *\n * @example\n * ```ts\n * try {\n * await confirmAppliedTrade(client, swap);\n * } catch (error) {\n * if ((await store.settle(swap.uniqueKey, error)) === 'forget') tellTheUserItFailed();\n * }\n * ```\n */\nexport function pendingSwapFateFor(error: unknown): PendingSwapFate {\n return pendingSwapFateOfOutcome(classifyFailure(error, { afterQueued: true }).outcome);\n}\n\n/** A pending swap the store dropped, and why. */\nexport interface PrunedPendingSwap {\n swap: PendingSwap;\n reason: PendingSwapPruneReason;\n}\n\n/** Options for {@link PendingSwapStore}. */\nexport interface PendingSwapStoreOptions {\n /**\n * Called once per dropped entry (after the drop is persisted, for `list`; at once for a\n * `remember` that refuses an invalid key), so the app can tell the user or log why a swap it\n * was tracking is gone. Diagnostics only: what it does, or throws, never changes the store.\n */\n onPrune?: ((pruned: PrunedPendingSwap) => void) | undefined;\n /** Storage key. Default {@link PENDING_SWAPS_STORAGE_KEY}. */\n storageKey?: string | undefined;\n /**\n * The stored name of the dark-swap flag. `isDarkSwap` (default) is wallet_extension's shape;\n * `shadow` is frontend_v2's. Entries are always exposed to callers as `isDarkSwap`.\n */\n darkFlagField?: 'isDarkSwap' | 'shadow' | undefined;\n /**\n * Where `remember` puts a new entry in the stored array: `end` (default, wallet_extension) or\n * `start` (frontend_v2).\n */\n insertAt?: 'end' | 'start' | undefined;\n /**\n * The order of `list`: `stored` (default, wallet_extension: oldest first for `insertAt: 'end'`)\n * or `newest-first` (frontend_v2).\n */\n listOrder?: 'stored' | 'newest-first' | undefined;\n /** Clock, in epoch milliseconds. Defaults to `Date.now`. */\n now?: (() => number) | undefined;\n}\n\n/**\n * Remembers queued swaps so their outcome can be confirmed after the UI, popup or service worker\n * that submitted them is gone (`confirmAppliedTrade` with the stored `uniqueKey`).\n *\n * Guarantees:\n * - Persistence is best effort and NEVER throws: a failing or missing storage must not turn an\n * accepted request into a client-side resubmission. `list` resolves `[]` when storage fails.\n * - Operations of one store instance are serialized, so concurrent `remember` / `forget` calls\n * cannot lose each other's write (read-modify-write over an async adapter). Two store instances\n * over the same storage (for example popup and service worker) are not coordinated.\n * - Entries that do not match the expected shape are ignored and dropped on the next write.\n * - Entries expire after seven days (an entry exactly seven days old is expired; frontend_v2\n * kept it for that one millisecond); `list` removes them from storage, which frontend_v2 never\n * did on read.\n * - An entry whose `uniqueKey` is not a valid apply-results key is terminal, not pending:\n * `confirmAppliedTrade` fails it with `INVALID_UNIQUE_KEY` every time, so a resume loop would\n * retry it forever. `list` prunes it like an expired one, and `remember` refuses to save it;\n * both tell `onPrune` the reason (`PENDING_SWAP_PRUNE_REASONS`).\n * - New entries go to the end (or `insertAt: 'start'`); `list` returns the wallet's entries in\n * stored order (or `listOrder: 'newest-first'`).\n * - The stored shape is wallet_extension's by default; `storageKey`, `darkFlagField`, `insertAt`\n * and `listOrder` reproduce frontend_v2's. JSON key order inside an entry is not preserved.\n *\n * Passing no `storage` yields a store that remembers nothing.\n *\n * @example\n * ```ts\n * const store = new PendingSwapStore({\n * get: async (key) => (await chrome.storage.local.get(key))[key],\n * set: (key, value) => chrome.storage.local.set({ [key]: value }),\n * });\n * await store.remember({ uniqueKey, requestTransactionId, wallet, token0Symbol: 'GALA',\n * token1Symbol: 'GUSDC', isDarkSwap: false, createdAt: Date.now() });\n * for (const swap of await store.list(wallet)) await confirmAppliedTrade(client, swap);\n * ```\n */\nexport class PendingSwapStore {\n private readonly storageKey: string;\n private readonly darkFlagField: 'isDarkSwap' | 'shadow';\n private readonly insertAt: 'end' | 'start';\n private readonly listOrder: 'stored' | 'newest-first';\n private readonly now: () => number;\n private readonly onPrune: ((pruned: PrunedPendingSwap) => void) | undefined;\n private queue: Promise<unknown> = Promise.resolve();\n\n constructor(\n private readonly storage: StorageAdapter | undefined,\n options: PendingSwapStoreOptions = {},\n ) {\n this.storageKey = options.storageKey ?? PENDING_SWAPS_STORAGE_KEY;\n this.darkFlagField = options.darkFlagField ?? 'isDarkSwap';\n this.insertAt = options.insertAt ?? 'end';\n this.listOrder = options.listOrder ?? 'stored';\n this.now = options.now ?? Date.now;\n this.onPrune = options.onPrune;\n }\n\n /**\n * Saves (or replaces, by `uniqueKey`) a pending swap. A swap whose `uniqueKey` is not a valid\n * apply-results key is not saved (it could never be confirmed); it is reported to `onPrune` as\n * `invalid_unique_key`.\n */\n public remember(swap: PendingSwap): Promise<void> {\n if (!isValidUniqueKey(swap.uniqueKey)) {\n this.report([{ swap, reason: 'invalid_unique_key' }]);\n return Promise.resolve();\n }\n return this.enqueue(async (storage) => {\n const current = await this.read(storage);\n const others = current\n .filter((entry) => entry.uniqueKey !== swap.uniqueKey)\n .map(this.toStored);\n const stored = this.toStored(swap);\n await storage.set(\n this.storageKey,\n this.insertAt === 'end' ? [...others, stored] : [stored, ...others],\n );\n });\n }\n\n /** Removes a pending swap once its outcome is known. */\n public forget(uniqueKey: string): Promise<void> {\n return this.enqueue(async (storage) => {\n const current = await this.read(storage);\n await storage.set(\n this.storageKey,\n current.filter((entry) => entry.uniqueKey !== uniqueKey).map(this.toStored),\n );\n });\n }\n\n /**\n * Applies {@link pendingSwapFateFor} to a swap whose confirmation failed with `error`, and\n * returns the fate so the app can word it (tell the user on `forget`, say nothing on\n * `forget_quietly`, keep polling on `keep`):\n *\n * - `forget`: the entry is removed and `onPrune` hears `chain_rejected`.\n * - `forget_quietly`: the entry is removed and `onPrune` hears `unconfirmable`.\n * - `keep`: nothing changes and `onPrune` is not called.\n *\n * Like every store operation it is serialized and best effort: if storage fails the entry stays\n * (a later `settle` or `list` handles it) and the fate is still returned. A key that is not\n * stored (already forgotten) is not an error and `onPrune` is not called for it.\n */\n public async settle(uniqueKey: string, error: unknown): Promise<PendingSwapFate> {\n const fate = pendingSwapFateFor(error);\n if (fate === 'keep') return fate;\n const removed = await this.enqueue(async (storage) => {\n const current = await this.read(storage);\n const swap = current.find((entry) => entry.uniqueKey === uniqueKey);\n if (swap === undefined) return undefined;\n await storage.set(\n this.storageKey,\n current.filter((entry) => entry.uniqueKey !== uniqueKey).map(this.toStored),\n );\n return swap;\n });\n if (removed !== undefined) {\n this.report([\n { swap: removed, reason: fate === 'forget' ? 'chain_rejected' : 'unconfirmable' },\n ]);\n }\n return fate;\n }\n\n /**\n * The wallet's pending swaps that can still be confirmed (see `listOrder`). Entries that never\n * can be are pruned from storage, for every wallet, and reported to `onPrune`: expired ones\n * (`expired`) and ones whose `uniqueKey` is not a valid apply-results key\n * (`invalid_unique_key`: `confirmAppliedTrade` would fail `INVALID_UNIQUE_KEY` on it forever).\n */\n public async list(wallet: string): Promise<PendingSwap[]> {\n const result = await this.enqueue(async (storage) => {\n const current = await this.read(storage);\n const now = this.now();\n const pruned: PrunedPendingSwap[] = [];\n const kept: PendingSwap[] = [];\n for (const entry of current) {\n const reason = this.pruneReasonOf(entry, now);\n if (reason === undefined) kept.push(entry);\n else pruned.push({ swap: entry, reason });\n }\n if (pruned.length > 0) {\n await storage.set(this.storageKey, kept.map(this.toStored));\n this.report(pruned);\n }\n const mine = kept.filter((entry) => entry.wallet === wallet);\n return this.listOrder === 'newest-first'\n ? mine.sort((left, right) => right.createdAt - left.createdAt)\n : mine;\n });\n return result ?? [];\n }\n\n private pruneReasonOf(entry: PendingSwap, now: number): PendingSwapPruneReason | undefined {\n if (!isValidUniqueKey(entry.uniqueKey)) return 'invalid_unique_key';\n return now - entry.createdAt < PENDING_SWAP_EXPIRY_MS ? undefined : 'expired';\n }\n\n /** Tells `onPrune` about each dropped entry; the hook is diagnostics and cannot break the store. */\n private report(pruned: readonly PrunedPendingSwap[]): void {\n for (const entry of pruned) {\n try {\n this.onPrune?.(entry);\n } catch {\n // A diagnostics hook must not turn a store operation into a failure.\n }\n }\n }\n\n private enqueue<T>(operation: (storage: StorageAdapter) => Promise<T>): Promise<T | undefined> {\n const storage = this.storage;\n if (storage === undefined) return Promise.resolve(undefined);\n const run = this.queue.then(async () => {\n try {\n return await operation(storage);\n } catch {\n // Storage can be missing, full, or disappear while a context is closing. Persistence is\n // best effort by contract.\n return undefined;\n }\n });\n this.queue = run;\n return run;\n }\n\n private async read(storage: StorageAdapter): Promise<PendingSwap[]> {\n const value = await storage.get(this.storageKey);\n if (!Array.isArray(value)) return [];\n const entries: PendingSwap[] = [];\n for (const item of value as unknown[]) {\n const entry = this.fromStored(item);\n if (entry !== undefined) entries.push(entry);\n }\n return entries;\n }\n\n private readonly toStored = (swap: PendingSwap): Record<string, unknown> => ({\n uniqueKey: swap.uniqueKey,\n requestTransactionId: swap.requestTransactionId,\n wallet: swap.wallet,\n token0Symbol: swap.token0Symbol,\n token1Symbol: swap.token1Symbol,\n [this.darkFlagField]: swap.isDarkSwap,\n createdAt: swap.createdAt,\n });\n\n private fromStored(value: unknown): PendingSwap | undefined {\n if (value === null || typeof value !== 'object') return undefined;\n const entry = value as Record<string, unknown>;\n const flag = entry[this.darkFlagField];\n const { uniqueKey, requestTransactionId, wallet, token0Symbol, token1Symbol, createdAt } =\n entry;\n if (\n typeof uniqueKey !== 'string' ||\n !(typeof requestTransactionId === 'string' || requestTransactionId === null) ||\n typeof wallet !== 'string' ||\n typeof token0Symbol !== 'string' ||\n typeof token1Symbol !== 'string' ||\n typeof flag !== 'boolean' ||\n typeof createdAt !== 'number' ||\n !Number.isFinite(createdAt)\n ) {\n return undefined;\n }\n return {\n uniqueKey,\n requestTransactionId,\n wallet,\n token0Symbol,\n token1Symbol,\n isDarkSwap: flag,\n createdAt,\n };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"pending_swaps.js","names":[],"sources":["../../../src/classes/pending_swaps.ts"],"sourcesContent":["import { isValidUniqueKey } from '../utils/unique_key_syntax.js';\nimport { pendingSwapFateFor, type PendingSwapFate } from './pending_swap_fate.js';\nimport type { StorageAdapter } from './storage_adapters.js';\n\n/** Key the wallet_extension has always used; its storage value is a plain array of entries. */\nexport const PENDING_SWAPS_STORAGE_KEY = 'pendingSwaps.v1';\n\n/** Entries older than this are dropped the next time a wallet's swaps are listed. */\nexport const PENDING_SWAP_EXPIRY_MS = 7 * 24 * 60 * 60 * 1000;\n\n/** A RequestTrade that was accepted by the gateway but whose outcome has not been read yet. */\nexport interface PendingSwap {\n uniqueKey: string;\n requestTransactionId: string | null;\n /** The wallet (account) that submitted the swap; `list` filters by it. */\n wallet: string;\n token0Symbol: string;\n token1Symbol: string;\n /** Whether the swap was a shadow (\"dark\") swap. */\n isDarkSwap: boolean;\n /** Epoch milliseconds when the swap was queued. */\n createdAt: number;\n}\n\n/**\n * Why the store dropped a pending swap (the `reason` of {@link PrunedPendingSwap}).\n *\n * - `expired`: older than {@link PENDING_SWAP_EXPIRY_MS}.\n * - `invalid_unique_key`: its `uniqueKey` is not a valid apply-results key (1 to 128 characters\n * of `A-Za-z0-9._:-`). `confirmAppliedTrade` refuses such a key with `INVALID_UNIQUE_KEY`\n * before reading anything and always will, so keeping the entry would retry it forever. The\n * SDK never signs such a key; an entry like this is corrupt or foreign.\n * - `chain_rejected`: {@link PendingSwapStore.settle} dropped it because a confirmation proved the\n * chain REFUSED the trade (`pendingSwapFateFor` is `forget`): it did not happen.\n * - `unconfirmable`: {@link PendingSwapStore.settle} dropped it because a confirmation failed in a\n * way that never changes (`forget_quietly`: the key itself is refused): nothing can be learned.\n */\nexport const PENDING_SWAP_PRUNE_REASONS = [\n 'expired',\n 'invalid_unique_key',\n 'chain_rejected',\n 'unconfirmable',\n] as const;\n\n/** One member of {@link PENDING_SWAP_PRUNE_REASONS}. */\nexport type PendingSwapPruneReason = (typeof PENDING_SWAP_PRUNE_REASONS)[number];\n\n/** A pending swap the store dropped, and why. */\nexport interface PrunedPendingSwap {\n swap: PendingSwap;\n reason: PendingSwapPruneReason;\n}\n\n/** Options for {@link PendingSwapStore}. */\nexport interface PendingSwapStoreOptions {\n /**\n * Called once per dropped entry (after the drop is persisted, for `list`; at once for a\n * `remember` that refuses an invalid key), so the app can tell the user or log why a swap it\n * was tracking is gone. Diagnostics only: what it does, or throws, never changes the store.\n */\n onPrune?: ((pruned: PrunedPendingSwap) => void) | undefined;\n /** Storage key. Default {@link PENDING_SWAPS_STORAGE_KEY}. */\n storageKey?: string | undefined;\n /**\n * The stored name of the dark-swap flag. `isDarkSwap` (default) is wallet_extension's shape;\n * `shadow` is frontend_v2's. Entries are always exposed to callers as `isDarkSwap`.\n */\n darkFlagField?: 'isDarkSwap' | 'shadow' | undefined;\n /**\n * Where `remember` puts a new entry in the stored array: `end` (default, wallet_extension) or\n * `start` (frontend_v2).\n */\n insertAt?: 'end' | 'start' | undefined;\n /**\n * The order of `list`: `stored` (default, wallet_extension: oldest first for `insertAt: 'end'`)\n * or `newest-first` (frontend_v2).\n */\n listOrder?: 'stored' | 'newest-first' | undefined;\n /** Clock, in epoch milliseconds. Defaults to `Date.now`. */\n now?: (() => number) | undefined;\n}\n\n/**\n * Remembers queued swaps so their outcome can be confirmed after the UI, popup or service worker\n * that submitted them is gone (`confirmAppliedTrade` with the stored `uniqueKey`).\n *\n * Guarantees:\n * - Persistence is best effort and NEVER throws: a failing or missing storage must not turn an\n * accepted request into a client-side resubmission. `list` resolves `[]` when storage fails.\n * - Operations of one store instance are serialized, so concurrent `remember` / `forget` calls\n * cannot lose each other's write (read-modify-write over an async adapter). Two store instances\n * over the same storage (for example popup and service worker) are not coordinated.\n * - Entries that do not match the expected shape are ignored and dropped on the next write.\n * - Entries expire after seven days (an entry exactly seven days old is expired; frontend_v2\n * kept it for that one millisecond); `list` removes them from storage, which frontend_v2 never\n * did on read.\n * - An entry whose `uniqueKey` is not a valid apply-results key is terminal, not pending:\n * `confirmAppliedTrade` fails it with `INVALID_UNIQUE_KEY` every time, so a resume loop would\n * retry it forever. `list` prunes it like an expired one, and `remember` refuses to save it;\n * both tell `onPrune` the reason (`PENDING_SWAP_PRUNE_REASONS`).\n * - New entries go to the end (or `insertAt: 'start'`); `list` returns the wallet's entries in\n * stored order (or `listOrder: 'newest-first'`).\n * - The stored shape is wallet_extension's by default; `storageKey`, `darkFlagField`, `insertAt`\n * and `listOrder` reproduce frontend_v2's. JSON key order inside an entry is not preserved.\n *\n * Passing no `storage` yields a store that remembers nothing.\n *\n * @example\n * ```ts\n * const store = new PendingSwapStore({\n * get: async (key) => (await chrome.storage.local.get(key))[key],\n * set: (key, value) => chrome.storage.local.set({ [key]: value }),\n * });\n * await store.remember({ uniqueKey, requestTransactionId, wallet, token0Symbol: 'GALA',\n * token1Symbol: 'GUSDC', isDarkSwap: false, createdAt: Date.now() });\n * for (const swap of await store.list(wallet)) await confirmAppliedTrade(client, swap);\n * ```\n */\nexport class PendingSwapStore {\n private readonly storageKey: string;\n private readonly darkFlagField: 'isDarkSwap' | 'shadow';\n private readonly insertAt: 'end' | 'start';\n private readonly listOrder: 'stored' | 'newest-first';\n private readonly now: () => number;\n private readonly onPrune: ((pruned: PrunedPendingSwap) => void) | undefined;\n private queue: Promise<unknown> = Promise.resolve();\n\n constructor(\n private readonly storage: StorageAdapter | undefined,\n options: PendingSwapStoreOptions = {},\n ) {\n this.storageKey = options.storageKey ?? PENDING_SWAPS_STORAGE_KEY;\n this.darkFlagField = options.darkFlagField ?? 'isDarkSwap';\n this.insertAt = options.insertAt ?? 'end';\n this.listOrder = options.listOrder ?? 'stored';\n this.now = options.now ?? Date.now;\n this.onPrune = options.onPrune;\n }\n\n /**\n * Saves (or replaces, by `uniqueKey`) a pending swap. A swap whose `uniqueKey` is not a valid\n * apply-results key is not saved (it could never be confirmed); it is reported to `onPrune` as\n * `invalid_unique_key`.\n */\n public remember(swap: PendingSwap): Promise<void> {\n if (!isValidUniqueKey(swap.uniqueKey)) {\n this.report([{ swap, reason: 'invalid_unique_key' }]);\n return Promise.resolve();\n }\n return this.enqueue(async (storage) => {\n const current = await this.read(storage);\n const others = current\n .filter((entry) => entry.uniqueKey !== swap.uniqueKey)\n .map(this.toStored);\n const stored = this.toStored(swap);\n await storage.set(\n this.storageKey,\n this.insertAt === 'end' ? [...others, stored] : [stored, ...others],\n );\n });\n }\n\n /** Removes a pending swap once its outcome is known. */\n public forget(uniqueKey: string): Promise<void> {\n return this.enqueue(async (storage) => {\n const current = await this.read(storage);\n await storage.set(\n this.storageKey,\n current.filter((entry) => entry.uniqueKey !== uniqueKey).map(this.toStored),\n );\n });\n }\n\n /**\n * Applies {@link pendingSwapFateFor} to a swap whose confirmation failed with `error`, and\n * returns the fate so the app can word it (tell the user on `forget`, say nothing on\n * `forget_quietly`, keep polling on `keep`):\n *\n * - `forget`: the entry is removed and `onPrune` hears `chain_rejected`.\n * - `forget_quietly`: the entry is removed and `onPrune` hears `unconfirmable`.\n * - `keep`: nothing changes and `onPrune` is not called.\n *\n * Like every store operation it is serialized and best effort: if storage fails the entry stays\n * (a later `settle` or `list` handles it) and the fate is still returned. A key that is not\n * stored (already forgotten) is not an error and `onPrune` is not called for it.\n */\n public async settle(uniqueKey: string, error: unknown): Promise<PendingSwapFate> {\n const fate = pendingSwapFateFor(error);\n if (fate === 'keep') return fate;\n const removed = await this.enqueue(async (storage) => {\n const current = await this.read(storage);\n const swap = current.find((entry) => entry.uniqueKey === uniqueKey);\n if (swap === undefined) return undefined;\n await storage.set(\n this.storageKey,\n current.filter((entry) => entry.uniqueKey !== uniqueKey).map(this.toStored),\n );\n return swap;\n });\n if (removed !== undefined) {\n this.report([\n { swap: removed, reason: fate === 'forget' ? 'chain_rejected' : 'unconfirmable' },\n ]);\n }\n return fate;\n }\n\n /**\n * The wallet's pending swaps that can still be confirmed (see `listOrder`). Entries that never\n * can be are pruned from storage, for every wallet, and reported to `onPrune`: expired ones\n * (`expired`) and ones whose `uniqueKey` is not a valid apply-results key\n * (`invalid_unique_key`: `confirmAppliedTrade` would fail `INVALID_UNIQUE_KEY` on it forever).\n */\n public async list(wallet: string): Promise<PendingSwap[]> {\n const result = await this.enqueue(async (storage) => {\n const current = await this.read(storage);\n const now = this.now();\n const pruned: PrunedPendingSwap[] = [];\n const kept: PendingSwap[] = [];\n for (const entry of current) {\n const reason = this.pruneReasonOf(entry, now);\n if (reason === undefined) kept.push(entry);\n else pruned.push({ swap: entry, reason });\n }\n if (pruned.length > 0) {\n await storage.set(this.storageKey, kept.map(this.toStored));\n this.report(pruned);\n }\n const mine = kept.filter((entry) => entry.wallet === wallet);\n return this.listOrder === 'newest-first'\n ? mine.sort((left, right) => right.createdAt - left.createdAt)\n : mine;\n });\n return result ?? [];\n }\n\n private pruneReasonOf(entry: PendingSwap, now: number): PendingSwapPruneReason | undefined {\n if (!isValidUniqueKey(entry.uniqueKey)) return 'invalid_unique_key';\n return now - entry.createdAt < PENDING_SWAP_EXPIRY_MS ? undefined : 'expired';\n }\n\n /** Tells `onPrune` about each dropped entry; the hook is diagnostics and cannot break the store. */\n private report(pruned: readonly PrunedPendingSwap[]): void {\n for (const entry of pruned) {\n try {\n this.onPrune?.(entry);\n } catch {\n // A diagnostics hook must not turn a store operation into a failure.\n }\n }\n }\n\n private enqueue<T>(operation: (storage: StorageAdapter) => Promise<T>): Promise<T | undefined> {\n const storage = this.storage;\n if (storage === undefined) return Promise.resolve(undefined);\n const run = this.queue.then(async () => {\n try {\n return await operation(storage);\n } catch {\n // Storage can be missing, full, or disappear while a context is closing. Persistence is\n // best effort by contract.\n return undefined;\n }\n });\n this.queue = run;\n return run;\n }\n\n private async read(storage: StorageAdapter): Promise<PendingSwap[]> {\n const value = await storage.get(this.storageKey);\n if (!Array.isArray(value)) return [];\n const entries: PendingSwap[] = [];\n for (const item of value as unknown[]) {\n const entry = this.fromStored(item);\n if (entry !== undefined) entries.push(entry);\n }\n return entries;\n }\n\n private readonly toStored = (swap: PendingSwap): Record<string, unknown> => ({\n uniqueKey: swap.uniqueKey,\n requestTransactionId: swap.requestTransactionId,\n wallet: swap.wallet,\n token0Symbol: swap.token0Symbol,\n token1Symbol: swap.token1Symbol,\n [this.darkFlagField]: swap.isDarkSwap,\n createdAt: swap.createdAt,\n });\n\n private fromStored(value: unknown): PendingSwap | undefined {\n if (value === null || typeof value !== 'object') return undefined;\n const entry = value as Record<string, unknown>;\n const flag = entry[this.darkFlagField];\n const { uniqueKey, requestTransactionId, wallet, token0Symbol, token1Symbol, createdAt } =\n entry;\n if (\n typeof uniqueKey !== 'string' ||\n !(typeof requestTransactionId === 'string' || requestTransactionId === null) ||\n typeof wallet !== 'string' ||\n typeof token0Symbol !== 'string' ||\n typeof token1Symbol !== 'string' ||\n typeof flag !== 'boolean' ||\n typeof createdAt !== 'number' ||\n !Number.isFinite(createdAt)\n ) {\n return undefined;\n }\n return {\n uniqueKey,\n requestTransactionId,\n wallet,\n token0Symbol,\n token1Symbol,\n isDarkSwap: flag,\n createdAt,\n };\n }\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{ALLOWED_FEE_TIERS_TEXT as e}from"./error_catalog.js";import t from"../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/defineProperty.js";import{GSwapSDKError as n,isGSwapError as r}from"./gswap_sdk_error.js";import{TICK_SPACING_BY_FEE as i}from"../types/fees.js";import{alignTickDown as a,alignTickUp as o,assertTickRange as s,tickFromPrice as c}from"../utils/ticks.js";import{isProposableTradingSymbol as l,validateFee as u,validateNumericAmount as d}from"../utils/validation.js";import{resolvePreSend as f,signPreSend as p}from"../utils/pre_send.js";import{HttpClient as m}from"./http_client.js";import{asEntryRecord as h,entryOk as g,entryRejected as _,readEntries as v}from"../utils/lenient.js";import{compositeKeyOf as y,parseTokenClassKey as b}from"../utils/ordering.js";import{rawTokenOf as x}from"./symbols.js";import{assertUniqueKey as S,createUniqueKey as C,resolveUniqueKeyPrefix as w}from"../utils/unique_key.js";import{assertCanonicalV2Dto as T,buildAddLiquidityDto as E,buildCollectPositionFeesDto as D,buildCreatePoolDto as O,buildRemoveLiquidityDto as k}from"../utils/dto_builders.js";import A from"bignumber.js";function j(e){return{token0:e.token0,token1:e.token1,fee:e.fee,tickLower:e.tickLower,tickUpper:e.tickUpper}}var M=class{constructor(e,n,r,i,a={}){this.gateway=e,this.symbols=n,this.signer=r,this.walletAddress=i,t(this,`http`,void 0),t(this,`uniqueKeyPrefix`,void 0),t(this,`onInvalidEntry`,void 0),this.http=new m(e.httpRequestor,e.requestTimeoutMs??3e4,e.wire),this.uniqueKeyPrefix=w(a.uniqueKeyPrefix),this.onInvalidEntry=a.onInvalidEntry}async getUserPositions(e){let t=await this.get(`/positions`,{user:e});return v(`positions`,t.data,I,this.onInvalidEntry)}async getPosition(e,t){let n=await this.getNullable(`/position`,await this.positionParams(e),t);return n===null?null:L(n.data)}async readPosition(e,t){try{let n=await this.getPosition(e,t);return n===null?{kind:`not_found`}:{kind:`found`,data:n}}catch(e){if(t?.aborted===!0)throw e;return{kind:`unknown`,error:r(e)?e:new n(`REQUEST_FAILED`,{operation:`position`},{cause:e})}}}async positionParams(e){if(this.symbols.passthrough===!0)return{token0:x(e.token0),token1:x(e.token1),fee:`${e.fee}`,owner:e.owner,tickLower:`${e.tickLower}`,tickUpper:`${e.tickUpper}`};let t=await this.orderPair(e.token0,e.token1),n=this.canonicalTicks(e.tickLower,e.tickUpper,t.flipped);return{token0:t.token0.symbol,token1:t.token1.symbol,fee:`${e.fee}`,owner:e.owner,tickLower:`${n.tickLower}`,tickUpper:`${n.tickUpper}`}}async estimateAddLiquidity(e){return d(e.amount,`amount`),(await this.get(`/add-liq-estimate`,{token0:x(e.token0),token1:x(e.token1),fee:`${e.fee}`,tickLower:`${e.tickLower}`,tickUpper:`${e.tickUpper}`,amount:new A(e.amount).toFixed(),amountIsToken0:String(e.amountIsToken0)})).data}async estimateRemoveLiquidity(e){return d(e.liquidity,`liquidity`),(await this.get(`/remove-liq-estimate`,{token0:x(e.token0),token1:x(e.token1),fee:`${e.fee}`,tickLower:`${e.tickLower}`,tickUpper:`${e.tickUpper}`,liquidity:new A(e.liquidity).toFixed()})).data}async addLiquidityByTicks(e){return this.submitAddLiquidity(await this.buildAddLiquidity(e))}async buildAddLiquidity(e){u(e.fee);let t=await this.orderPair(e.token0,e.token1),n=this.canonicalTicks(e.tickLower,e.tickUpper,t.flipped);s(n.tickLower,n.tickUpper,e.fee),d(e.amount,`amount`);let r={token0:t.token0.symbol,token1:t.token1.symbol,fee:e.fee,tickLower:n.tickLower,tickUpper:n.tickUpper,uniqueKey:this.uniqueKeyFor(e.uniqueKey)};return t.flipped===e.amountIsToken0?E({...r,depositQuantityToken1:e.amount}):E({...r,depositQuantityToken0:e.amount})}async submitAddLiquidity(e){return T(`AddLiquidity`,e),this.submit(`AddLiquidity`,e,{kind:`add`,identity:j(e)})}async addLiquidityByPrice(e){u(e.fee),this.rejectNumber(e.minPrice,`minPrice`),this.rejectNumber(e.maxPrice,`maxPrice`);let t=new A(e.minPrice),r=new A(e.maxPrice);if(!t.isFinite()||!r.isFinite()||t.isLessThanOrEqualTo(0)||r.isLessThanOrEqualTo(0))throw new n(`INVALID_PRICE_BOUNDS`,{minPrice:String(e.minPrice),maxPrice:String(e.maxPrice)});if(t.isGreaterThan(r))throw new n(`PRICE_BOUNDS_ORDER`,{minPrice:String(e.minPrice),maxPrice:String(e.maxPrice)});let i=this.tickSpacing(e.fee),s=a(c(t),i),l=o(c(r),i);return this.addLiquidityByTicks({token0:e.token0,token1:e.token1,fee:e.fee,tickLower:s,tickUpper:l,amount:e.amount,amountIsToken0:e.amountIsToken0,uniqueKey:e.uniqueKey})}async removeLiquidity(e){return this.submitRemoveLiquidity(await this.buildRemoveLiquidity(e))}async buildRemoveLiquidity(e){u(e.fee);let t=await this.orderPair(e.token0,e.token1),r=this.canonicalTicks(e.tickLower,e.tickUpper,t.flipped);if(s(r.tickLower,r.tickUpper,e.fee),e.amount0!==void 0&&e.amount1!==void 0)throw new n(`AT_MOST_ONE_OF_ALLOWED`,{first:`amount0`,second:`amount1`});e.amount0!==void 0&&d(e.amount0,`amount0`),e.amount1!==void 0&&d(e.amount1,`amount1`);let[i,a]=t.flipped?[e.amount1,e.amount0]:[e.amount0,e.amount1];return k({token0:t.token0.symbol,token1:t.token1.symbol,fee:e.fee,tickLower:r.tickLower,tickUpper:r.tickUpper,uniqueKey:this.uniqueKeyFor(e.uniqueKey),withdrawalQuantityToken0:i,withdrawalQuantityToken1:a})}async submitRemoveLiquidity(e){T(`RemoveLiquidity`,e);let t=e.withdrawalQuantityToken0===void 0&&e.withdrawalQuantityToken1===void 0;return this.submit(`RemoveLiquidity`,e,{kind:t?`removeFull`:`removePartial`,identity:j(e)})}async collectPositionFees(e){return this.submitCollectPositionFees(await this.buildCollectPositionFees(e))}async buildCollectPositionFees(e){u(e.fee);let t=await this.orderPair(e.token0,e.token1),n=this.canonicalTicks(e.tickLower,e.tickUpper,t.flipped);return s(n.tickLower,n.tickUpper,e.fee),D({token0:t.token0.symbol,token1:t.token1.symbol,fee:e.fee,tickLower:n.tickLower,tickUpper:n.tickUpper,uniqueKey:this.uniqueKeyFor(e.uniqueKey)})}async submitCollectPositionFees(e){return T(`CollectPositionFees`,e),this.submit(`CollectPositionFees`,e,{kind:`collect`,identity:j(e)})}async createPool(e){return this.submitCreatePool(await this.buildCreatePool(e))}async buildCreatePool(e){let t=e.startingPrice!==void 0;if(t===(e.startingSqrtPrice!==void 0))throw new n(`EXACTLY_ONE_OF_REQUIRED`,{first:`startingPrice`,second:`startingSqrtPrice`});u(e.fee);let[r,i]=await Promise.all([this.resolveCreateToken(e.token0,e.token0Symbol),this.resolveCreateToken(e.token1,e.token1Symbol)]),a=r.symbol>i.symbol,[o,s]=a?[i,r]:[r,i],c=t?e.startingPrice:e.startingSqrtPrice;this.rejectNumber(c,t?`startingPrice`:`startingSqrtPrice`);let l=new A(c);if(!l.isFinite()||l.isLessThanOrEqualTo(0))throw new n(`INVALID_STARTING_PRICE`,{value:String(c)});let d=a?new A(1).dividedBy(l):l,f={token0Key:o.classKey,token1Key:s.classKey,token0Symbol:o.symbol,token1Symbol:s.symbol,fee:e.fee,uniqueKey:this.uniqueKeyFor(e.uniqueKey),name:e.name,isPrivate:e.isPrivate,privateAccess:e.privateAccess};return O(t?{...f,startingPrice:d}:{...f,startingSqrtPrice:d})}async submitCreatePool(e){T(`CreatePool`,e);let t=await this.submit(`CreatePool`,e,{kind:`createPool`,token0:e.token0Symbol,token1:e.token1Symbol,fee:e.fee});return this.symbols.invalidate?.(),t}async submit(e,t,r){if(this.signer===void 0)throw n.noSignerError();let i=await p(this.signer,e,t),a=this.walletAddress===void 0?{}:{walletAddress:this.walletAddress},o=this.walletAddress,s=this.buildPositionConfirmation(r,o),c={...a,...s===void 0?{}:{positionConfirmation:s}};return this.gateway.submit(e,i,c)}buildPositionConfirmation(e,t){if(e===void 0)return;if(e.kind===`createPool`){let{token0:t,token1:n,fee:r}=e;return async e=>await this.getNullable(`/pool`,{token0:t,token1:n,fee:`${r}`},e)===null?{confirmed:!1}:{confirmed:!0,position:null}}if(t===void 0)return;let{kind:n,identity:r}=e,i;return async e=>{let a=await this.getPosition({...r,owner:t},e);if(i===void 0)return i={liquidity:new A(a?.liquidity??0),fees:new A(a?.fees0??0).plus(new A(a?.fees1??0))},n===`removeFull`&&a===null?{confirmed:!0,position:null}:{confirmed:!1};switch(n){case`add`:return a!==null&&new A(a.liquidity).isGreaterThan(i.liquidity)?{confirmed:!0,position:a}:{confirmed:!1};case`removePartial`:return a!==null&&new A(a.liquidity).isLessThan(i.liquidity)?{confirmed:!0,position:a}:{confirmed:!1};case`removeFull`:return a===null?{confirmed:!0,position:null}:{confirmed:!1};case`collect`:{if(a===null)return{confirmed:!1};let e=new A(a.fees0??0).plus(new A(a.fees1??0));return e.isLessThan(i.fees)||i.fees.isZero()&&e.isZero()?{confirmed:!0,position:a}:{confirmed:!1}}}}}async get(e,t,n){return this.http.sendGetRequest(this.gateway.dexBackendBaseUrl,`/v2/trade`,e,t,n===void 0?void 0:{signal:n})}async getNullable(e,t,n){try{return await this.get(e,t,n)}catch(e){if(this.isNotFound(e))return null;throw e}}isNotFound(e){return e instanceof n&&e.status===404}canonicalTicks(e,t,n){return n?{tickLower:-t,tickUpper:-e}:{tickLower:e,tickUpper:t}}tickSpacing(t){let r=i[t];if(r===void 0)throw new n(`INVALID_FEE`,{field:`fee`,value:t,allowed:e});return r}rejectNumber(e,t){if(typeof e==`number`)throw new n(`AMOUNT_IS_JS_NUMBER`,{field:t})}uniqueKeyFor(e){return e===void 0?C(this.uniqueKeyPrefix):(S(e),e)}orderPair(e,t){return f(`${x(e)}/${x(t)}`,()=>this.symbols.orderPair(e,t))}async resolveCreateToken(e,t){try{let r=typeof e==`object`?y(e):e,i=await f(x(e),()=>this.symbols.resolve(r));if(t!==void 0&&t!==``&&t!==i.symbol)throw new n(`SYMBOL_DISAGREES_WITH_REGISTRY`,{token:x(e),suppliedSymbol:t,registeredSymbol:i.symbol});let a=this.tryParseClassKey(e);if(a===void 0&&i.collection===``)throw new n(`CREATE_POOL_NEEDS_CLASS_KEY`,{token:x(e)});return{symbol:i.symbol,classKey:a??b({collection:i.collection,category:i.category,type:i.type,additionalKey:i.additionalKey})}}catch(r){if(!(r instanceof n)||r.code!==`UNKNOWN_TOKEN`)throw r;let i;try{i=b(e)}catch{throw r}if(t===void 0||t===``)throw new n(`TOKEN_SYMBOL_NOT_REGISTERED`,{token:x(e)});if(!l(t))throw new n(`INVALID_TRADING_SYMBOL`,{symbol:t});return{symbol:t,classKey:i}}}tryParseClassKey(e){try{return b(e)}catch{return}}};const N=[`token0Symbol`,`token1Symbol`,`owner`,`liquidity`,`amount0`,`amount1`],P=[`fee`,`tickLower`,`tickUpper`],F=[`token0CompositeKey`,`token1CompositeKey`,`token0Image`,`token1Image`,`sqrtPrice`,`fees0`,`fees1`];function I(e){let t=h(e);if(t===void 0)return _(`the row is not an object`);for(let e of N)if(typeof t[e]!=`string`)return _(`${e} is not a string`);for(let e of P){let n=t[e];if(typeof n!=`number`||!Number.isFinite(n))return _(`${e} is not a number`)}if(typeof t.inRange!=`boolean`)return _(`inRange is not a boolean`);for(let e of F)if(t[e]!==void 0&&typeof t[e]!=`string`)return _(`${e} is not a string`);let n=t.currentTick;if(n!==void 0&&(typeof n!=`number`||!Number.isFinite(n)))return _(`currentTick is not a number`);let r=t.poolRef??t.pool??``;if(typeof r!=`string`)return _(`pool is not a string`);let i=Object.fromEntries([...F,`currentTick`].flatMap(e=>t[e]===void 0?[]:[[e,t[e]]]));return g({token0Symbol:t.token0Symbol,token1Symbol:t.token1Symbol,fee:t.fee,tickLower:t.tickLower,tickUpper:t.tickUpper,liquidity:t.liquidity,amount0:t.amount0,amount1:t.amount1,inRange:t.inRange,pool:r,owner:t.owner,...i})}function L(e){let t=I(e);if(t.ok)return t.value;throw new n(`INVALID_POSITION_RESPONSE`,{reason:t.reason},{body:e})}export{M as Positions};
1
+ import{ALLOWED_FEE_TIERS_TEXT as e}from"./error_catalog.js";import t from"../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/defineProperty.js";import{GSwapSDKError as n,isGSwapError as r}from"./gswap_sdk_error.js";import{TICK_SPACING_BY_FEE as i}from"../types/fees.js";import{tickFromPrice as a}from"../utils/tick_math.js";import{isProposableTradingSymbol as o,validateFee as s,validateNumericAmount as c}from"../utils/validation.js";import{resolvePreSend as l,signPreSend as u}from"../utils/pre_send.js";import{HttpClient as d}from"./http_client.js";import{asEntryRecord as f,entryOk as p,entryRejected as m,readEntries as h}from"../utils/lenient.js";import{compositeKeyOf as g}from"../utils/class_key.js";import{parseTokenClassKey as _}from"../utils/ordering.js";import{rawTokenOf as v}from"./symbols.js";import{alignTickDown as y,alignTickUp as b,assertTickRange as x}from"../utils/ticks.js";import{assertUniqueKey as S,createUniqueKey as C,resolveUniqueKeyPrefix as w}from"../utils/unique_key.js";import{assertCanonicalV2Dto as T,buildAddLiquidityDto as E,buildCollectPositionFeesDto as D,buildCreatePoolDto as O,buildRemoveLiquidityDto as k}from"../utils/dto_builders.js";import A from"bignumber.js";function j(e){return{token0:e.token0,token1:e.token1,fee:e.fee,tickLower:e.tickLower,tickUpper:e.tickUpper}}var M=class{constructor(e,n,r,i,a={}){this.gateway=e,this.symbols=n,this.signer=r,this.walletAddress=i,t(this,`http`,void 0),t(this,`uniqueKeyPrefix`,void 0),t(this,`onInvalidEntry`,void 0),this.http=new d(e.httpRequestor,e.requestTimeoutMs??3e4,e.wire),this.uniqueKeyPrefix=w(a.uniqueKeyPrefix),this.onInvalidEntry=a.onInvalidEntry}async getUserPositions(e){let t=await this.get(`/positions`,{user:e});return h(`positions`,t.data,I,this.onInvalidEntry)}async getPosition(e,t){let n=await this.getNullable(`/position`,await this.positionParams(e),t);return n===null?null:L(n.data)}async readPosition(e,t){try{let n=await this.getPosition(e,t);return n===null?{kind:`not_found`}:{kind:`found`,data:n}}catch(e){if(t?.aborted===!0)throw e;return{kind:`unknown`,error:r(e)?e:new n(`REQUEST_FAILED`,{operation:`position`},{cause:e})}}}async positionParams(e){if(this.symbols.passthrough===!0)return{token0:v(e.token0),token1:v(e.token1),fee:`${e.fee}`,owner:e.owner,tickLower:`${e.tickLower}`,tickUpper:`${e.tickUpper}`};let t=await this.orderPair(e.token0,e.token1),n=this.canonicalTicks(e.tickLower,e.tickUpper,t.flipped);return{token0:t.token0.symbol,token1:t.token1.symbol,fee:`${e.fee}`,owner:e.owner,tickLower:`${n.tickLower}`,tickUpper:`${n.tickUpper}`}}async estimateAddLiquidity(e){return c(e.amount,`amount`),(await this.get(`/add-liq-estimate`,{token0:v(e.token0),token1:v(e.token1),fee:`${e.fee}`,tickLower:`${e.tickLower}`,tickUpper:`${e.tickUpper}`,amount:new A(e.amount).toFixed(),amountIsToken0:String(e.amountIsToken0)})).data}async estimateRemoveLiquidity(e){return c(e.liquidity,`liquidity`),(await this.get(`/remove-liq-estimate`,{token0:v(e.token0),token1:v(e.token1),fee:`${e.fee}`,tickLower:`${e.tickLower}`,tickUpper:`${e.tickUpper}`,liquidity:new A(e.liquidity).toFixed()})).data}async addLiquidityByTicks(e){return this.submitAddLiquidity(await this.buildAddLiquidity(e))}async buildAddLiquidity(e){s(e.fee);let t=await this.orderPair(e.token0,e.token1),n=this.canonicalTicks(e.tickLower,e.tickUpper,t.flipped);x(n.tickLower,n.tickUpper,e.fee),c(e.amount,`amount`);let r={token0:t.token0.symbol,token1:t.token1.symbol,fee:e.fee,tickLower:n.tickLower,tickUpper:n.tickUpper,uniqueKey:this.uniqueKeyFor(e.uniqueKey)};return t.flipped===e.amountIsToken0?E({...r,depositQuantityToken1:e.amount}):E({...r,depositQuantityToken0:e.amount})}async submitAddLiquidity(e){return T(`AddLiquidity`,e),this.submit(`AddLiquidity`,e,{kind:`add`,identity:j(e)})}async addLiquidityByPrice(e){s(e.fee),this.rejectNumber(e.minPrice,`minPrice`),this.rejectNumber(e.maxPrice,`maxPrice`);let t=new A(e.minPrice),r=new A(e.maxPrice);if(!t.isFinite()||!r.isFinite()||t.isLessThanOrEqualTo(0)||r.isLessThanOrEqualTo(0))throw new n(`INVALID_PRICE_BOUNDS`,{minPrice:String(e.minPrice),maxPrice:String(e.maxPrice)});if(t.isGreaterThan(r))throw new n(`PRICE_BOUNDS_ORDER`,{minPrice:String(e.minPrice),maxPrice:String(e.maxPrice)});let i=this.tickSpacing(e.fee),o=y(a(t),i),c=b(a(r),i);return this.addLiquidityByTicks({token0:e.token0,token1:e.token1,fee:e.fee,tickLower:o,tickUpper:c,amount:e.amount,amountIsToken0:e.amountIsToken0,uniqueKey:e.uniqueKey})}async removeLiquidity(e){return this.submitRemoveLiquidity(await this.buildRemoveLiquidity(e))}async buildRemoveLiquidity(e){s(e.fee);let t=await this.orderPair(e.token0,e.token1),r=this.canonicalTicks(e.tickLower,e.tickUpper,t.flipped);if(x(r.tickLower,r.tickUpper,e.fee),e.amount0!==void 0&&e.amount1!==void 0)throw new n(`AT_MOST_ONE_OF_ALLOWED`,{first:`amount0`,second:`amount1`});e.amount0!==void 0&&c(e.amount0,`amount0`),e.amount1!==void 0&&c(e.amount1,`amount1`);let[i,a]=t.flipped?[e.amount1,e.amount0]:[e.amount0,e.amount1];return k({token0:t.token0.symbol,token1:t.token1.symbol,fee:e.fee,tickLower:r.tickLower,tickUpper:r.tickUpper,uniqueKey:this.uniqueKeyFor(e.uniqueKey),withdrawalQuantityToken0:i,withdrawalQuantityToken1:a})}async submitRemoveLiquidity(e){T(`RemoveLiquidity`,e);let t=e.withdrawalQuantityToken0===void 0&&e.withdrawalQuantityToken1===void 0;return this.submit(`RemoveLiquidity`,e,{kind:t?`removeFull`:`removePartial`,identity:j(e)})}async collectPositionFees(e){return this.submitCollectPositionFees(await this.buildCollectPositionFees(e))}async buildCollectPositionFees(e){s(e.fee);let t=await this.orderPair(e.token0,e.token1),n=this.canonicalTicks(e.tickLower,e.tickUpper,t.flipped);return x(n.tickLower,n.tickUpper,e.fee),D({token0:t.token0.symbol,token1:t.token1.symbol,fee:e.fee,tickLower:n.tickLower,tickUpper:n.tickUpper,uniqueKey:this.uniqueKeyFor(e.uniqueKey)})}async submitCollectPositionFees(e){return T(`CollectPositionFees`,e),this.submit(`CollectPositionFees`,e,{kind:`collect`,identity:j(e)})}async createPool(e){return this.submitCreatePool(await this.buildCreatePool(e))}async buildCreatePool(e){let t=e.startingPrice!==void 0;if(t===(e.startingSqrtPrice!==void 0))throw new n(`EXACTLY_ONE_OF_REQUIRED`,{first:`startingPrice`,second:`startingSqrtPrice`});s(e.fee);let[r,i]=await Promise.all([this.resolveCreateToken(e.token0,e.token0Symbol),this.resolveCreateToken(e.token1,e.token1Symbol)]),a=r.symbol>i.symbol,[o,c]=a?[i,r]:[r,i],l=t?e.startingPrice:e.startingSqrtPrice;this.rejectNumber(l,t?`startingPrice`:`startingSqrtPrice`);let u=new A(l);if(!u.isFinite()||u.isLessThanOrEqualTo(0))throw new n(`INVALID_STARTING_PRICE`,{value:String(l)});let d=a?new A(1).dividedBy(u):u,f={token0Key:o.classKey,token1Key:c.classKey,token0Symbol:o.symbol,token1Symbol:c.symbol,fee:e.fee,uniqueKey:this.uniqueKeyFor(e.uniqueKey),name:e.name,isPrivate:e.isPrivate,privateAccess:e.privateAccess};return O(t?{...f,startingPrice:d}:{...f,startingSqrtPrice:d})}async submitCreatePool(e){T(`CreatePool`,e);let t=await this.submit(`CreatePool`,e,{kind:`createPool`,token0:e.token0Symbol,token1:e.token1Symbol,fee:e.fee});return this.symbols.invalidate?.(),t}async submit(e,t,r){if(this.signer===void 0)throw n.noSignerError();let i=await u(this.signer,e,t),a=this.walletAddress===void 0?{}:{walletAddress:this.walletAddress},o=this.walletAddress,s=this.buildPositionConfirmation(r,o),c={...a,...s===void 0?{}:{positionConfirmation:s}};return this.gateway.submit(e,i,c)}buildPositionConfirmation(e,t){if(e===void 0)return;if(e.kind===`createPool`){let{token0:t,token1:n,fee:r}=e;return async e=>await this.getNullable(`/pool`,{token0:t,token1:n,fee:`${r}`},e)===null?{confirmed:!1}:{confirmed:!0,position:null}}if(t===void 0)return;let{kind:n,identity:r}=e,i;return async e=>{let a=await this.getPosition({...r,owner:t},e);if(i===void 0)return i={liquidity:new A(a?.liquidity??0),fees:new A(a?.fees0??0).plus(new A(a?.fees1??0))},n===`removeFull`&&a===null?{confirmed:!0,position:null}:{confirmed:!1};switch(n){case`add`:return a!==null&&new A(a.liquidity).isGreaterThan(i.liquidity)?{confirmed:!0,position:a}:{confirmed:!1};case`removePartial`:return a!==null&&new A(a.liquidity).isLessThan(i.liquidity)?{confirmed:!0,position:a}:{confirmed:!1};case`removeFull`:return a===null?{confirmed:!0,position:null}:{confirmed:!1};case`collect`:{if(a===null)return{confirmed:!1};let e=new A(a.fees0??0).plus(new A(a.fees1??0));return e.isLessThan(i.fees)||i.fees.isZero()&&e.isZero()?{confirmed:!0,position:a}:{confirmed:!1}}}}}async get(e,t,n){return this.http.sendGetRequest(this.gateway.dexBackendBaseUrl,`/v2/trade`,e,t,n===void 0?void 0:{signal:n})}async getNullable(e,t,n){try{return await this.get(e,t,n)}catch(e){if(this.isNotFound(e))return null;throw e}}isNotFound(e){return e instanceof n&&e.status===404}canonicalTicks(e,t,n){return n?{tickLower:-t,tickUpper:-e}:{tickLower:e,tickUpper:t}}tickSpacing(t){let r=i[t];if(r===void 0)throw new n(`INVALID_FEE`,{field:`fee`,value:t,allowed:e});return r}rejectNumber(e,t){if(typeof e==`number`)throw new n(`AMOUNT_IS_JS_NUMBER`,{field:t})}uniqueKeyFor(e){return e===void 0?C(this.uniqueKeyPrefix):(S(e),e)}orderPair(e,t){return l(`${v(e)}/${v(t)}`,()=>this.symbols.orderPair(e,t))}async resolveCreateToken(e,t){try{let r=typeof e==`object`?g(e):e,i=await l(v(e),()=>this.symbols.resolve(r));if(t!==void 0&&t!==``&&t!==i.symbol)throw new n(`SYMBOL_DISAGREES_WITH_REGISTRY`,{token:v(e),suppliedSymbol:t,registeredSymbol:i.symbol});let a=this.tryParseClassKey(e);if(a===void 0&&i.collection===``)throw new n(`CREATE_POOL_NEEDS_CLASS_KEY`,{token:v(e)});return{symbol:i.symbol,classKey:a??_({collection:i.collection,category:i.category,type:i.type,additionalKey:i.additionalKey})}}catch(r){if(!(r instanceof n)||r.code!==`UNKNOWN_TOKEN`)throw r;let i;try{i=_(e)}catch{throw r}if(t===void 0||t===``)throw new n(`TOKEN_SYMBOL_NOT_REGISTERED`,{token:v(e)});if(!o(t))throw new n(`INVALID_TRADING_SYMBOL`,{symbol:t});return{symbol:t,classKey:i}}}tryParseClassKey(e){try{return _(e)}catch{return}}};const N=[`token0Symbol`,`token1Symbol`,`owner`,`liquidity`,`amount0`,`amount1`],P=[`fee`,`tickLower`,`tickUpper`],F=[`token0CompositeKey`,`token1CompositeKey`,`token0Image`,`token1Image`,`sqrtPrice`,`fees0`,`fees1`];function I(e){let t=f(e);if(t===void 0)return m(`the row is not an object`);for(let e of N)if(typeof t[e]!=`string`)return m(`${e} is not a string`);for(let e of P){let n=t[e];if(typeof n!=`number`||!Number.isFinite(n))return m(`${e} is not a number`)}if(typeof t.inRange!=`boolean`)return m(`inRange is not a boolean`);for(let e of F)if(t[e]!==void 0&&typeof t[e]!=`string`)return m(`${e} is not a string`);let n=t.currentTick;if(n!==void 0&&(typeof n!=`number`||!Number.isFinite(n)))return m(`currentTick is not a number`);let r=t.poolRef??t.pool??``;if(typeof r!=`string`)return m(`pool is not a string`);let i=Object.fromEntries([...F,`currentTick`].flatMap(e=>t[e]===void 0?[]:[[e,t[e]]]));return p({token0Symbol:t.token0Symbol,token1Symbol:t.token1Symbol,fee:t.fee,tickLower:t.tickLower,tickUpper:t.tickUpper,liquidity:t.liquidity,amount0:t.amount0,amount1:t.amount1,inRange:t.inRange,pool:r,owner:t.owner,...i})}function L(e){let t=I(e);if(t.ok)return t.value;throw new n(`INVALID_POSITION_RESPONSE`,{reason:t.reason},{body:e})}export{M as Positions};
2
2
  //# sourceMappingURL=positions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"positions.js","names":[],"sources":["../../../src/classes/positions.ts"],"sourcesContent":["import BigNumber from 'bignumber.js';\nimport { ALLOWED_FEE_TIERS_TEXT } from './error_catalog.js';\nimport { GSwapSDKError, isGSwapError } from './gswap_sdk_error.js';\nimport type { ChainGateway, ChainSubmitOptions } from './gateway.js';\nimport type { PositionConfirmationResult, SubmittedTransaction } from './submitted_transaction.js';\nimport { HttpClient } from './http_client.js';\nimport type { GalaChainSigner } from './signers.js';\nimport { rawTokenOf, type Symbols, type TokenResolver } from './symbols.js';\nimport type { NumericAmount, PriceIn } from '../types/amounts.js';\nimport type { FEE_TIER } from '../types/fees.js';\nimport { TICK_SPACING_BY_FEE } from '../types/fees.js';\nimport type {\n AddLiquidityDTO,\n CollectPositionFeesDTO,\n CreatePoolDTO,\n RemoveLiquidityDTO,\n} from '../types/v2_dtos.js';\nimport type {\n AddLiquidityEstimate,\n Position,\n RemoveLiquidityEstimate,\n} from '../types/v2_results.js';\nimport type { WireOptions } from '../types/wire.js';\nimport {\n asEntryRecord,\n entryOk,\n entryRejected,\n readEntries,\n type EntryParse,\n type InvalidEntryHandler,\n} from '../utils/lenient.js';\nimport { type TokenRef, compositeKeyOf, parseTokenClassKey } from '../utils/ordering.js';\nimport { resolvePreSend, signPreSend } from '../utils/pre_send.js';\nimport {\n isProposableTradingSymbol,\n validateFee,\n validateNumericAmount,\n} from '../utils/validation.js';\nimport { alignTickDown, alignTickUp, assertTickRange, tickFromPrice } from '../utils/ticks.js';\nimport {\n assertCanonicalV2Dto,\n buildAddLiquidityDto,\n buildCollectPositionFeesDto,\n buildCreatePoolDto,\n buildRemoveLiquidityDto,\n} from '../utils/dto_builders.js';\nimport {\n assertUniqueKey,\n createUniqueKey,\n resolveUniqueKeyPrefix,\n type UniqueKeyOptions,\n} from '../utils/unique_key.js';\n\ntype PositionGateway = Pick<ChainGateway, 'httpRequestor' | 'dexBackendBaseUrl'> & {\n submit: (\n method: string,\n signedBody: Record<string, unknown>,\n options?: ChainSubmitOptions,\n ) => Promise<SubmittedTransaction<Position | null>>;\n requestTimeoutMs?: number | undefined;\n wire?: WireOptions | undefined;\n};\ntype PositionSymbols = TokenResolver & Pick<Symbols, 'orderPair'> & { invalidate?: () => void };\n\n/** Options of {@link Positions}: the `uniqueKey` prefix and a handler for skipped list rows. */\nexport interface PositionsOptions extends UniqueKeyOptions {\n /** Told about every position row of a list that was skipped as unusable. */\n onInvalidEntry?: InvalidEntryHandler | undefined;\n}\n\n/** A write's optional attempt identity. */\ninterface UniqueKeyArg {\n /**\n * The attempt's `uniqueKey` (1-128 characters of `A-Za-z0-9._:-`). Default: a fresh\n * `<uniqueKeyPrefix><uuid>`. Supply one to know the key before signing, or to retry the SAME\n * attempt after a lost response; never reuse it for another write.\n */\n uniqueKey?: string | undefined;\n}\n\n/** Arguments of {@link Positions.addLiquidityByTicks} and {@link Positions.buildAddLiquidity}. */\nexport interface AddLiquidityArgs extends UniqueKeyArg {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n tickLower: number;\n tickUpper: number;\n amount: NumericAmount;\n /** Whether `amount` is of `token0` as the caller passed it (not of the canonical token0). */\n amountIsToken0: boolean;\n}\n\n/** Arguments of {@link Positions.removeLiquidity} and {@link Positions.buildRemoveLiquidity}. */\nexport interface RemoveLiquidityArgs extends UniqueKeyArg {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n tickLower: number;\n tickUpper: number;\n /** Withdraw this much of the caller's `token0`; give at most one of `amount0` / `amount1`. */\n amount0?: NumericAmount | undefined;\n amount1?: NumericAmount | undefined;\n}\n\n/** Arguments of {@link Positions.collectPositionFees} and {@link Positions.buildCollectPositionFees}. */\nexport interface CollectPositionFeesArgs extends UniqueKeyArg {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n tickLower: number;\n tickUpper: number;\n}\n\n/** Arguments of {@link Positions.createPool} and {@link Positions.buildCreatePool}. */\nexport interface CreatePoolArgs extends UniqueKeyArg {\n token0: TokenRef;\n token1: TokenRef;\n token0Symbol?: string | undefined;\n token1Symbol?: string | undefined;\n fee: FEE_TIER;\n startingPrice?: NumericAmount | undefined;\n startingSqrtPrice?: NumericAmount | undefined;\n /** Optional pool display name, sent as the DTO's `name`; omitted when undefined or empty. */\n name?: string | undefined;\n isPrivate?: boolean | undefined;\n privateAccess?: string[] | undefined;\n}\n\n/** The pool position a liquidity DTO names, as its confirmation needs it. */\nfunction identityOf(\n dto: Pick<CollectPositionFeesDTO, 'token0' | 'token1' | 'fee' | 'tickLower' | 'tickUpper'>,\n): PositionIdentity {\n return {\n token0: dto.token0,\n token1: dto.token1,\n fee: dto.fee,\n tickLower: dto.tickLower,\n tickUpper: dto.tickUpper,\n };\n}\n\n/**\n * The outcome of {@link Positions.readPosition}. Only `not_found` is chain-confirmed absence (the\n * backend answers HTTP 404 only when the chain itself found no such position); `unknown` is \"no\n * evidence either way\" — a transport failure, a gateway timeout, any other error status or an\n * answer that is not a position — and must never be read as a removal.\n */\nexport type PositionRead =\n | { kind: 'found'; data: Position }\n | { kind: 'not_found' }\n | { kind: 'unknown'; error: GSwapSDKError };\n\ninterface BackendEnvelope<TData> {\n data: TData;\n}\n\ninterface AddLiquidityEstimateWire {\n amount0: string;\n amount1: string;\n liquidity: string;\n token0Symbol: string;\n token1Symbol: string;\n tickLower: number;\n tickUpper: number;\n amountIsCanonicalToken0: boolean;\n}\n\ninterface RemoveLiquidityEstimateWire {\n amount0: string;\n amount1: string;\n}\n\ninterface ResolvedCreateToken {\n symbol: string;\n classKey: ReturnType<typeof parseTokenClassKey>;\n}\n\ninterface PositionIdentity {\n token0: string;\n token1: string;\n fee: FEE_TIER;\n tickLower: number;\n tickUpper: number;\n}\n\n/** Tells `submit()` how to prove a write's own effect for `SubmittedTransaction.confirm()`. */\ntype PositionSubmitConfirmation =\n | { kind: 'add' | 'removePartial' | 'removeFull' | 'collect'; identity: PositionIdentity }\n | { kind: 'createPool'; token0: string; token1: string; fee: FEE_TIER };\n\n/**\n * Manages current-contract concentrated-liquidity positions.\n *\n * @example\n * ```typescript\n * const positions = gSwap.positions;\n * const mine = await positions.getUserPositions('client|012345678901234567890123');\n * console.log(mine.length);\n * ```\n */\nexport class Positions {\n constructor(\n private readonly gateway: PositionGateway,\n private readonly symbols: PositionSymbols,\n private readonly signer?: GalaChainSigner,\n private readonly walletAddress?: string,\n options: PositionsOptions = {},\n ) {\n this.http = new HttpClient(\n gateway.httpRequestor,\n gateway.requestTimeoutMs ?? 30_000,\n gateway.wire,\n );\n this.uniqueKeyPrefix = resolveUniqueKeyPrefix(options.uniqueKeyPrefix);\n this.onInvalidEntry = options.onInvalidEntry;\n }\n\n private readonly http: HttpClient;\n private readonly uniqueKeyPrefix: string;\n private readonly onInvalidEntry: InvalidEntryHandler | undefined;\n\n /**\n * Gets all current-contract positions owned by an address.\n *\n * @param owner - GalaChain owner identity.\n * @returns Enriched positions from the v2 trade backend, token images included. A row that is\n * not a usable position is skipped and reported to `onInvalidEntry`.\n * @example\n * ```typescript\n * const positions = await gSwap.positions.getUserPositions('client|012345678901234567890123');\n * ```\n */\n async getUserPositions(owner: string): Promise<Position[]> {\n const response = await this.get<unknown>('/positions', { user: owner });\n return readEntries('positions', response.data, parsePosition, this.onInvalidEntry);\n }\n\n /**\n * Gets one position, normalizing a caller-supplied reversed pair to canonical symbol order.\n * Reversed pairs also invert the price axis, so `[lower, upper]` becomes `[-upper, -lower]`.\n *\n * @param args - Pair, owner, fee, and position tick range.\n * @returns The position, or `null` when the backend returns 404.\n * @example\n * ```typescript\n * const position = await gSwap.positions.getPosition({\n * token0: 'GALA', token1: 'GUSDC', owner: 'client|012345678901234567890123',\n * fee: 3000, tickLower: -19200, tickUpper: 12000,\n * });\n * ```\n */\n async getPosition(\n args: {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n owner: string;\n tickLower: number;\n tickUpper: number;\n },\n signal?: AbortSignal,\n ): Promise<Position | null> {\n const response = await this.getNullable<unknown>(\n '/position',\n await this.positionParams(args),\n signal,\n );\n return response === null ? null : readPositionBody(response.data);\n }\n\n /**\n * Reads one position and classifies the outcome instead of throwing: `found` with the position\n * (fees included), `not_found` (the backend's HTTP 404, which it sends only when the chain\n * itself has no such position) or `unknown` (any other failure, carried as `error`). Use it\n * to confirm a write: `unknown` is \"no evidence either way\", never a confirmed absence.\n *\n * Only the caller's own abort rethrows. With a pass-through resolver ({@link CanonicalSymbols})\n * the pair and ticks go to the backend exactly as given and the backend orients them.\n *\n * @example\n * ```typescript\n * const read = await gSwap.positions.readPosition({\n * token0: 'GALA', token1: 'GUSDC', owner: 'client|012345678901234567890123',\n * fee: 3000, tickLower: -19200, tickUpper: 12000,\n * });\n * if (read.kind === 'found') console.log(read.data.fees0);\n * ```\n */\n async readPosition(\n args: {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n owner: string;\n tickLower: number;\n tickUpper: number;\n },\n signal?: AbortSignal,\n ): Promise<PositionRead> {\n try {\n const position = await this.getPosition(args, signal);\n return position === null ? { kind: 'not_found' } : { kind: 'found', data: position };\n } catch (error: unknown) {\n if (signal?.aborted === true) throw error;\n return {\n kind: 'unknown',\n error: isGSwapError(error)\n ? error\n : new GSwapSDKError('REQUEST_FAILED', { operation: 'position' }, { cause: error }),\n };\n }\n }\n\n /**\n * The `/position` query: canonical symbols and canonical ticks after a client-side flip, or,\n * for a pass-through resolver, the caller's references and ticks untouched.\n */\n private async positionParams(args: {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n owner: string;\n tickLower: number;\n tickUpper: number;\n }): Promise<Record<string, string>> {\n if (this.symbols.passthrough === true) {\n return {\n token0: rawTokenOf(args.token0),\n token1: rawTokenOf(args.token1),\n fee: `${args.fee}`,\n owner: args.owner,\n tickLower: `${args.tickLower}`,\n tickUpper: `${args.tickUpper}`,\n };\n }\n const pair = await this.orderPair(args.token0, args.token1);\n const ticks = this.canonicalTicks(args.tickLower, args.tickUpper, pair.flipped);\n return {\n token0: pair.token0.symbol,\n token1: pair.token1.symbol,\n fee: `${args.fee}`,\n owner: args.owner,\n tickLower: `${ticks.tickLower}`,\n tickUpper: `${ticks.tickUpper}`,\n };\n }\n\n /**\n * Estimates the other token amount and resulting liquidity for a deposit.\n *\n * @param args - Pair, tick range, deposit amount, and the amount's caller-side token index.\n * @returns Backend liquidity estimate.\n * @example\n * ```typescript\n * const estimate = await gSwap.positions.estimateAddLiquidity({\n * token0: 'GALA', token1: 'GUSDC', fee: 3000,\n * tickLower: -19200, tickUpper: 12000, amount: '100', amountIsToken0: true,\n * });\n * ```\n */\n async estimateAddLiquidity(args: {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n tickLower: number;\n tickUpper: number;\n amount: NumericAmount;\n amountIsToken0: boolean;\n }): Promise<AddLiquidityEstimate> {\n validateNumericAmount(args.amount, 'amount');\n const response = await this.get<AddLiquidityEstimateWire>('/add-liq-estimate', {\n token0: rawTokenOf(args.token0),\n token1: rawTokenOf(args.token1),\n fee: `${args.fee}`,\n tickLower: `${args.tickLower}`,\n tickUpper: `${args.tickUpper}`,\n amount: new BigNumber(args.amount).toFixed(),\n amountIsToken0: String(args.amountIsToken0),\n });\n return response.data;\n }\n\n /**\n * Estimates token amounts released by burning liquidity.\n *\n * @param args - Pair, tick range, fee tier, and liquidity quantity.\n * @returns Estimated token0 and token1 quantities.\n * @example\n * ```typescript\n * const estimate = await gSwap.positions.estimateRemoveLiquidity({\n * token0: 'GALA', token1: 'GUSDC', fee: 3000,\n * tickLower: -19200, tickUpper: 12000, liquidity: '1000',\n * });\n * ```\n */\n async estimateRemoveLiquidity(args: {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n tickLower: number;\n tickUpper: number;\n liquidity: NumericAmount;\n }): Promise<RemoveLiquidityEstimate> {\n validateNumericAmount(args.liquidity, 'liquidity');\n const response = await this.get<RemoveLiquidityEstimateWire>('/remove-liq-estimate', {\n token0: rawTokenOf(args.token0),\n token1: rawTokenOf(args.token1),\n fee: `${args.fee}`,\n tickLower: `${args.tickLower}`,\n tickUpper: `${args.tickUpper}`,\n liquidity: new BigNumber(args.liquidity).toFixed(),\n });\n return response.data;\n }\n\n /**\n * Adds liquidity using an explicit tick range. Exactly one canonical deposit field is emitted.\n *\n * @param args - Pair, fee, range, deposit amount, and caller-side token index.\n * @returns The synchronously submitted transaction.\n * @example\n * ```typescript\n * const tx = await gSwap.positions.addLiquidityByTicks({\n * token0: 'GALA', token1: 'GUSDC', fee: 3000,\n * tickLower: -19200, tickUpper: 12000, amount: '100', amountIsToken0: true,\n * });\n * await tx.confirm();\n * ```\n */\n async addLiquidityByTicks(args: AddLiquidityArgs) {\n return this.submitAddLiquidity(await this.buildAddLiquidity(args));\n }\n\n /**\n * Builds the AddLiquidity DTO that {@link addLiquidityByTicks} would sign, without signing or\n * sending it: to show in an approval popup, or to sign elsewhere. Submit exactly this DTO with\n * {@link submitAddLiquidity}. The pure counterpart, with canonical inputs and no resolution,\n * is `buildAddLiquidityDto`.\n *\n * @example\n * ```typescript\n * const dto = await gSwap.positions.buildAddLiquidity({\n * token0: 'GALA', token1: 'GUSDC', fee: 3000,\n * tickLower: -19200, tickUpper: 12000, amount: '100', amountIsToken0: true,\n * });\n * ```\n */\n async buildAddLiquidity(args: AddLiquidityArgs): Promise<AddLiquidityDTO> {\n validateFee(args.fee);\n const pair = await this.orderPair(args.token0, args.token1);\n const ticks = this.canonicalTicks(args.tickLower, args.tickUpper, pair.flipped);\n // Caller-facing checks first, so a refusal names the caller's own argument (`amount`).\n assertTickRange(ticks.tickLower, ticks.tickUpper, args.fee);\n validateNumericAmount(args.amount, 'amount');\n const base = {\n token0: pair.token0.symbol,\n token1: pair.token1.symbol,\n fee: args.fee,\n tickLower: ticks.tickLower,\n tickUpper: ticks.tickUpper,\n uniqueKey: this.uniqueKeyFor(args.uniqueKey),\n };\n return pair.flipped !== args.amountIsToken0\n ? buildAddLiquidityDto({ ...base, depositQuantityToken0: args.amount })\n : buildAddLiquidityDto({ ...base, depositQuantityToken1: args.amount });\n }\n\n /**\n * Signs and submits a DTO built by {@link buildAddLiquidity} or `buildAddLiquidityDto`; it must\n * be exactly what the builder produces. The returned transaction confirms by proving the\n * position's liquidity grew.\n */\n async submitAddLiquidity(dto: AddLiquidityDTO) {\n assertCanonicalV2Dto('AddLiquidity', dto);\n return this.submit('AddLiquidity', dto, { kind: 'add', identity: identityOf(dto) });\n }\n\n /**\n * Adds liquidity from prices, flooring the lower tick and ceiling the upper tick to fee spacing.\n * The resulting range is therefore no narrower than the requested price interval.\n *\n * @param args - Pair, fee, price range, deposit amount, and caller-side token index.\n * @returns The synchronously submitted transaction.\n * @example\n * ```typescript\n * const tx = await gSwap.positions.addLiquidityByPrice({\n * token0: 'GALA', token1: 'GUSDC', fee: 3000,\n * minPrice: '0.14', maxPrice: '0.16', amount: '100', amountIsToken0: true,\n * });\n * ```\n */\n async addLiquidityByPrice(args: {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n minPrice: PriceIn;\n maxPrice: PriceIn;\n amount: NumericAmount;\n amountIsToken0: boolean;\n /** The attempt's `uniqueKey`; default a fresh one (see {@link AddLiquidityArgs.uniqueKey}). */\n uniqueKey?: string | undefined;\n }) {\n validateFee(args.fee);\n this.rejectNumber(args.minPrice, 'minPrice');\n this.rejectNumber(args.maxPrice, 'maxPrice');\n const minPrice = new BigNumber(args.minPrice);\n const maxPrice = new BigNumber(args.maxPrice);\n if (\n !minPrice.isFinite() ||\n !maxPrice.isFinite() ||\n minPrice.isLessThanOrEqualTo(0) ||\n maxPrice.isLessThanOrEqualTo(0)\n ) {\n throw new GSwapSDKError('INVALID_PRICE_BOUNDS', {\n minPrice: String(args.minPrice),\n maxPrice: String(args.maxPrice),\n });\n }\n if (minPrice.isGreaterThan(maxPrice)) {\n throw new GSwapSDKError('PRICE_BOUNDS_ORDER', {\n minPrice: String(args.minPrice),\n maxPrice: String(args.maxPrice),\n });\n }\n const spacing = this.tickSpacing(args.fee);\n const tickLower = alignTickDown(tickFromPrice(minPrice), spacing);\n const tickUpper = alignTickUp(tickFromPrice(maxPrice), spacing);\n return this.addLiquidityByTicks({\n token0: args.token0,\n token1: args.token1,\n fee: args.fee,\n tickLower,\n tickUpper,\n amount: args.amount,\n amountIsToken0: args.amountIsToken0,\n uniqueKey: args.uniqueKey,\n });\n }\n\n /**\n * Removes liquidity by withdrawing at most one canonical token quantity.\n * Omitting both amounts closes the position and sweeps all accrued fees.\n *\n * @param args - Pair, fee, range, and optional caller-side withdrawal amount.\n * @returns The synchronously submitted transaction.\n * @example\n * ```typescript\n * const tx = await gSwap.positions.removeLiquidity({\n * token0: 'GALA', token1: 'GUSDC', fee: 3000,\n * tickLower: -19200, tickUpper: 12000,\n * });\n * ```\n */\n async removeLiquidity(args: RemoveLiquidityArgs) {\n return this.submitRemoveLiquidity(await this.buildRemoveLiquidity(args));\n }\n\n /**\n * Builds the RemoveLiquidity DTO that {@link removeLiquidity} would sign, without signing or\n * sending it. Submit exactly this DTO with {@link submitRemoveLiquidity}. The pure counterpart\n * is `buildRemoveLiquidityDto`.\n */\n async buildRemoveLiquidity(args: RemoveLiquidityArgs): Promise<RemoveLiquidityDTO> {\n validateFee(args.fee);\n const pair = await this.orderPair(args.token0, args.token1);\n const ticks = this.canonicalTicks(args.tickLower, args.tickUpper, pair.flipped);\n assertTickRange(ticks.tickLower, ticks.tickUpper, args.fee);\n if (args.amount0 !== undefined && args.amount1 !== undefined) {\n throw new GSwapSDKError('AT_MOST_ONE_OF_ALLOWED', { first: 'amount0', second: 'amount1' });\n }\n if (args.amount0 !== undefined) validateNumericAmount(args.amount0, 'amount0');\n if (args.amount1 !== undefined) validateNumericAmount(args.amount1, 'amount1');\n // The caller's amount0 / amount1 follow the caller's pair; the DTO's follow the canonical one.\n const [canonical0, canonical1] = pair.flipped\n ? [args.amount1, args.amount0]\n : [args.amount0, args.amount1];\n return buildRemoveLiquidityDto({\n token0: pair.token0.symbol,\n token1: pair.token1.symbol,\n fee: args.fee,\n tickLower: ticks.tickLower,\n tickUpper: ticks.tickUpper,\n uniqueKey: this.uniqueKeyFor(args.uniqueKey),\n withdrawalQuantityToken0: canonical0,\n withdrawalQuantityToken1: canonical1,\n });\n }\n\n /**\n * Signs and submits a DTO built by {@link buildRemoveLiquidity} or `buildRemoveLiquidityDto`;\n * it must be exactly what the builder produces. A DTO with no withdrawal quantity closes the\n * position, and the returned transaction then confirms by proving the position is gone.\n */\n async submitRemoveLiquidity(dto: RemoveLiquidityDTO) {\n assertCanonicalV2Dto('RemoveLiquidity', dto);\n const isFullClose =\n dto.withdrawalQuantityToken0 === undefined && dto.withdrawalQuantityToken1 === undefined;\n return this.submit('RemoveLiquidity', dto, {\n kind: isFullClose ? 'removeFull' : 'removePartial',\n identity: identityOf(dto),\n });\n }\n\n /**\n * Collects all accrued fees for a position; the contract does not support partial collection.\n *\n * @param args - Pair, fee, and tick range identifying the position.\n * @returns The synchronously submitted transaction.\n * @example\n * ```typescript\n * const tx = await gSwap.positions.collectPositionFees({\n * token0: 'GALA', token1: 'GUSDC', fee: 3000,\n * tickLower: -19200, tickUpper: 12000,\n * });\n * ```\n */\n async collectPositionFees(args: CollectPositionFeesArgs) {\n return this.submitCollectPositionFees(await this.buildCollectPositionFees(args));\n }\n\n /**\n * Builds the CollectPositionFees DTO that {@link collectPositionFees} would sign, without\n * signing or sending it. Submit exactly this DTO with {@link submitCollectPositionFees}. The\n * pure counterpart is `buildCollectPositionFeesDto`.\n */\n async buildCollectPositionFees(args: CollectPositionFeesArgs): Promise<CollectPositionFeesDTO> {\n validateFee(args.fee);\n const pair = await this.orderPair(args.token0, args.token1);\n const ticks = this.canonicalTicks(args.tickLower, args.tickUpper, pair.flipped);\n assertTickRange(ticks.tickLower, ticks.tickUpper, args.fee);\n return buildCollectPositionFeesDto({\n token0: pair.token0.symbol,\n token1: pair.token1.symbol,\n fee: args.fee,\n tickLower: ticks.tickLower,\n tickUpper: ticks.tickUpper,\n uniqueKey: this.uniqueKeyFor(args.uniqueKey),\n });\n }\n\n /**\n * Signs and submits a DTO built by {@link buildCollectPositionFees} or\n * `buildCollectPositionFeesDto`; it must be exactly what the builder produces.\n */\n async submitCollectPositionFees(dto: CollectPositionFeesDTO) {\n assertCanonicalV2Dto('CollectPositionFees', dto);\n return this.submit('CollectPositionFees', dto, { kind: 'collect', identity: identityOf(dto) });\n }\n\n /**\n * Creates a pool, resolving registered trading symbols.\n * Reversed symbol order inverts the supplied starting price or square-root price.\n *\n * A token class key carries no symbol, so for a token being proposed into the v2\n * trading-symbol registry for the first time (no existing pool has registered it yet) the\n * caller MUST pass `token0Symbol`/`token1Symbol` (the token class's symbol, from the token\n * list or FetchTokenClasses). The SDK never derives a symbol from the key, and throws a\n * TOKEN_SYMBOL_NOT_REGISTERED error when an unregistered token has none. For an already-registered token a\n * supplied symbol must match the registered one.\n *\n * @param args - Token class keys or references, fee, exactly one starting price field, and privacy options.\n * @returns The synchronously submitted transaction.\n * @example\n * ```typescript\n * const tx = await gSwap.positions.createPool({\n * token0: 'GALA|Unit|none|none', token1: 'GUSDC|Unit|none|none', fee: 3000,\n * startingPrice: '0.15',\n * });\n * ```\n */\n async createPool(args: CreatePoolArgs) {\n return this.submitCreatePool(await this.buildCreatePool(args));\n }\n\n /**\n * Builds the CreatePool DTO that {@link createPool} would sign, without signing or sending it:\n * symbols resolved or proposed, the pair put in canonical order and the price inverted when it\n * was reversed. Submit exactly this DTO with {@link submitCreatePool}. The pure counterpart,\n * with canonical inputs, is `buildCreatePoolDto`.\n */\n async buildCreatePool(args: CreatePoolArgs): Promise<CreatePoolDTO> {\n const hasPrice = args.startingPrice !== undefined;\n const hasSqrtPrice = args.startingSqrtPrice !== undefined;\n if (hasPrice === hasSqrtPrice) {\n throw new GSwapSDKError('EXACTLY_ONE_OF_REQUIRED', {\n first: 'startingPrice',\n second: 'startingSqrtPrice',\n });\n }\n validateFee(args.fee);\n const [tokenA, tokenB] = await Promise.all([\n this.resolveCreateToken(args.token0, args.token0Symbol),\n this.resolveCreateToken(args.token1, args.token1Symbol),\n ]);\n const flipped = tokenA.symbol > tokenB.symbol;\n const [token0, token1] = flipped ? [tokenB, tokenA] : [tokenA, tokenB];\n const sourcePriceInput = hasPrice\n ? (args.startingPrice as NumericAmount)\n : (args.startingSqrtPrice as NumericAmount);\n this.rejectNumber(sourcePriceInput, hasPrice ? 'startingPrice' : 'startingSqrtPrice');\n const sourcePrice = new BigNumber(sourcePriceInput);\n if (!sourcePrice.isFinite() || sourcePrice.isLessThanOrEqualTo(0)) {\n throw new GSwapSDKError('INVALID_STARTING_PRICE', { value: String(sourcePriceInput) });\n }\n const canonicalPrice = flipped ? new BigNumber(1).dividedBy(sourcePrice) : sourcePrice;\n const shared = {\n token0Key: token0.classKey,\n token1Key: token1.classKey,\n token0Symbol: token0.symbol,\n token1Symbol: token1.symbol,\n fee: args.fee,\n uniqueKey: this.uniqueKeyFor(args.uniqueKey),\n name: args.name,\n isPrivate: args.isPrivate,\n privateAccess: args.privateAccess,\n };\n return hasPrice\n ? buildCreatePoolDto({ ...shared, startingPrice: canonicalPrice })\n : buildCreatePoolDto({ ...shared, startingSqrtPrice: canonicalPrice });\n }\n\n /**\n * Signs and submits a DTO built by {@link buildCreatePool} or `buildCreatePoolDto`; it must be\n * exactly what the builder produces. The returned transaction confirms by the pool existing, and\n * the cached symbol registry is dropped, since the pool may have registered new symbols.\n */\n async submitCreatePool(dto: CreatePoolDTO) {\n assertCanonicalV2Dto('CreatePool', dto);\n const transaction = await this.submit('CreatePool', dto, {\n kind: 'createPool',\n token0: dto.token0Symbol,\n token1: dto.token1Symbol,\n fee: dto.fee,\n });\n this.symbols.invalidate?.();\n return transaction;\n }\n\n private async submit(\n method: string,\n dto: Record<string, unknown>,\n confirmation?: PositionSubmitConfirmation,\n ) {\n if (this.signer === undefined) throw GSwapSDKError.noSignerError();\n const signed = await signPreSend(this.signer, method, dto);\n const submitOptions =\n this.walletAddress === undefined ? {} : { walletAddress: this.walletAddress };\n const owner = this.walletAddress;\n const positionConfirmation = this.buildPositionConfirmation(confirmation, owner);\n const options: ChainSubmitOptions = {\n ...submitOptions,\n ...(positionConfirmation === undefined ? {} : { positionConfirmation }),\n };\n return this.gateway.submit(method, signed, options);\n }\n\n /**\n * Builds a confirmation closure that proves the write's own effect, not just that a\n * position row exists (true before most of these writes run too, for any operation on a\n * pre-existing position — existence alone would false-confirm before the write executes,\n * and would never confirm a full close, whose whole point is that the position stops\n * existing).\n *\n * The \"before\" state is captured lazily, on the confirmation closure's own first poll —\n * not eagerly here in `submit()` — so confirming a write never adds an extra round-trip to\n * the write itself; the cost of establishing the baseline is paid by `confirm()`, only if\n * and when the caller actually asks to confirm.\n */\n private buildPositionConfirmation(\n confirmation: PositionSubmitConfirmation | undefined,\n owner: string | undefined,\n ): ((signal: AbortSignal) => Promise<PositionConfirmationResult>) | undefined {\n if (confirmation === undefined) return undefined;\n\n if (confirmation.kind === 'createPool') {\n const { token0, token1, fee } = confirmation;\n return async (signal) => {\n const pool = await this.getNullable<unknown>(\n '/pool',\n { token0, token1, fee: `${fee}` },\n signal,\n );\n return pool === null ? { confirmed: false } : { confirmed: true, position: null };\n };\n }\n\n if (owner === undefined) return undefined;\n const { kind, identity } = confirmation;\n let baseline: { liquidity: BigNumber; fees: BigNumber } | undefined;\n\n return async (signal) => {\n const current = await this.getPosition({ ...identity, owner }, signal);\n\n if (baseline === undefined) {\n // First poll only establishes the baseline to compare future polls against — it can\n // never itself be the confirmed outcome (nothing has been observed to change yet).\n baseline = {\n liquidity: new BigNumber(current?.liquidity ?? 0),\n fees: new BigNumber(current?.fees0 ?? 0).plus(new BigNumber(current?.fees1 ?? 0)),\n };\n // A full close starting from \"already gone\" (e.g. a retried confirm after the position\n // was already swept) is confirmed immediately rather than waiting for a change that\n // already happened.\n return kind === 'removeFull' && current === null\n ? { confirmed: true, position: null }\n : { confirmed: false };\n }\n\n switch (kind) {\n case 'add':\n return current !== null &&\n new BigNumber(current.liquidity).isGreaterThan(baseline.liquidity)\n ? { confirmed: true, position: current }\n : { confirmed: false };\n case 'removePartial':\n return current !== null && new BigNumber(current.liquidity).isLessThan(baseline.liquidity)\n ? { confirmed: true, position: current }\n : { confirmed: false };\n case 'removeFull':\n return current === null ? { confirmed: true, position: null } : { confirmed: false };\n case 'collect': {\n if (current === null) return { confirmed: false };\n const currentFees = new BigNumber(current.fees0 ?? 0).plus(\n new BigNumber(current.fees1 ?? 0),\n );\n if (currentFees.isLessThan(baseline.fees)) return { confirmed: true, position: current };\n // Nothing to sweep in the first place — a legitimate no-op collect never moves\n // fees, so \"still zero\" is the confirmed outcome, not an unconfirmed one.\n if (baseline.fees.isZero() && currentFees.isZero())\n return { confirmed: true, position: current };\n return { confirmed: false };\n }\n }\n };\n }\n\n private async get<TData>(\n endpoint: string,\n params: Record<string, string>,\n signal?: AbortSignal,\n ): Promise<BackendEnvelope<TData>> {\n return this.http.sendGetRequest<BackendEnvelope<TData>>(\n this.gateway.dexBackendBaseUrl,\n '/v2/trade',\n endpoint,\n params,\n signal === undefined ? undefined : { signal },\n );\n }\n\n private async getNullable<TData>(\n endpoint: string,\n params: Record<string, string>,\n signal?: AbortSignal,\n ): Promise<BackendEnvelope<TData> | null> {\n try {\n return await this.get<TData>(endpoint, params, signal);\n } catch (error: unknown) {\n if (this.isNotFound(error)) return null;\n throw error;\n }\n }\n\n private isNotFound(error: unknown): boolean {\n return error instanceof GSwapSDKError && error.status === 404;\n }\n\n private canonicalTicks(tickLower: number, tickUpper: number, flipped: boolean) {\n return flipped ? { tickLower: -tickUpper, tickUpper: -tickLower } : { tickLower, tickUpper };\n }\n\n private tickSpacing(fee: number): number {\n const spacing = (TICK_SPACING_BY_FEE as Record<number, number>)[fee];\n if (spacing === undefined) {\n throw new GSwapSDKError('INVALID_FEE', {\n field: 'fee',\n value: fee,\n allowed: ALLOWED_FEE_TIERS_TEXT,\n });\n }\n return spacing;\n }\n\n private rejectNumber(value: unknown, parameterName: string): void {\n if (typeof value === 'number') {\n throw new GSwapSDKError('AMOUNT_IS_JS_NUMBER', { field: parameterName });\n }\n }\n\n /** The caller's `uniqueKey` (validated), else a fresh one. */\n private uniqueKeyFor(supplied: string | undefined): string {\n if (supplied === undefined) return createUniqueKey(this.uniqueKeyPrefix);\n assertUniqueKey(supplied);\n return supplied;\n }\n\n /**\n * The pair in canonical order. A resolver failure that is not an SDK error (an app's own\n * resolver, an app requestor's rejection during the registry read) is `TOKEN_RESOLUTION_FAILED`.\n */\n private orderPair(a: TokenRef, b: TokenRef) {\n return resolvePreSend(`${rawTokenOf(a)}/${rawTokenOf(b)}`, () => this.symbols.orderPair(a, b));\n }\n\n private async resolveCreateToken(\n ref: TokenRef,\n symbolOverride?: string,\n ): Promise<ResolvedCreateToken> {\n try {\n const lookupRef = typeof ref === 'object' ? compositeKeyOf(ref) : ref;\n const resolved = await resolvePreSend(rawTokenOf(ref), () => this.symbols.resolve(lookupRef));\n if (\n symbolOverride !== undefined &&\n symbolOverride !== '' &&\n symbolOverride !== resolved.symbol\n ) {\n throw new GSwapSDKError('SYMBOL_DISAGREES_WITH_REGISTRY', {\n token: rawTokenOf(ref),\n suppliedSymbol: symbolOverride,\n registeredSymbol: resolved.symbol,\n });\n }\n const suppliedClassKey = this.tryParseClassKey(ref);\n if (suppliedClassKey === undefined && resolved.collection === '') {\n // An offline resolver knows a bare symbol but not its class key; a pool needs the key.\n throw new GSwapSDKError('CREATE_POOL_NEEDS_CLASS_KEY', { token: rawTokenOf(ref) });\n }\n return {\n symbol: resolved.symbol,\n classKey:\n suppliedClassKey ??\n parseTokenClassKey({\n collection: resolved.collection,\n category: resolved.category,\n type: resolved.type,\n additionalKey: resolved.additionalKey,\n }),\n };\n } catch (error: unknown) {\n if (!(error instanceof GSwapSDKError) || error.code !== 'UNKNOWN_TOKEN') {\n throw error;\n }\n let classKey: ReturnType<typeof parseTokenClassKey>;\n try {\n classKey = parseTokenClassKey(ref);\n } catch {\n throw error;\n }\n // First-time registration: a class key carries NO symbol (collection and symbol disagree\n // for many tokens, launchpad tokens all share collection `Token`, and some collections\n // contain `$`), so the symbol to propose must be supplied by the caller from the token\n // class / registry. Never derive it from the key.\n if (symbolOverride === undefined || symbolOverride === '') {\n throw new GSwapSDKError('TOKEN_SYMBOL_NOT_REGISTERED', { token: rawTokenOf(ref) });\n }\n if (!isProposableTradingSymbol(symbolOverride)) {\n throw new GSwapSDKError('INVALID_TRADING_SYMBOL', { symbol: symbolOverride });\n }\n return { symbol: symbolOverride, classKey };\n }\n }\n\n private tryParseClassKey(ref: TokenRef): ReturnType<typeof parseTokenClassKey> | undefined {\n try {\n return parseTokenClassKey(ref);\n } catch {\n return undefined;\n }\n }\n}\n\nconst REQUIRED_POSITION_STRINGS = [\n 'token0Symbol',\n 'token1Symbol',\n 'owner',\n 'liquidity',\n 'amount0',\n 'amount1',\n] as const;\nconst REQUIRED_POSITION_NUMBERS = ['fee', 'tickLower', 'tickUpper'] as const;\nconst OPTIONAL_POSITION_STRINGS = [\n 'token0CompositeKey',\n 'token1CompositeKey',\n 'token0Image',\n 'token1Image',\n 'sqrtPrice',\n 'fees0',\n 'fees1',\n] as const;\n\n/**\n * Reads one position row of `/positions` or `/position`: the pair, owner, range, liquidity and\n * amounts must be usable; the composite keys, token images, current tick and price, and the\n * escrowed fees are carried when present. `pool` is the pool reference (`poolRef` wins over\n * `pool`; `''` when the row names neither).\n */\nfunction parsePosition(value: unknown): EntryParse<Position> {\n const wire = asEntryRecord(value);\n if (wire === undefined) return entryRejected('the row is not an object');\n for (const key of REQUIRED_POSITION_STRINGS) {\n if (typeof wire[key] !== 'string') return entryRejected(`${key} is not a string`);\n }\n for (const key of REQUIRED_POSITION_NUMBERS) {\n const number = wire[key];\n if (typeof number !== 'number' || !Number.isFinite(number)) {\n return entryRejected(`${key} is not a number`);\n }\n }\n if (typeof wire['inRange'] !== 'boolean') return entryRejected('inRange is not a boolean');\n for (const key of OPTIONAL_POSITION_STRINGS) {\n if (wire[key] !== undefined && typeof wire[key] !== 'string') {\n return entryRejected(`${key} is not a string`);\n }\n }\n const currentTick = wire['currentTick'];\n if (\n currentTick !== undefined &&\n (typeof currentTick !== 'number' || !Number.isFinite(currentTick))\n ) {\n return entryRejected('currentTick is not a number');\n }\n const pool = wire['poolRef'] ?? wire['pool'] ?? '';\n if (typeof pool !== 'string') return entryRejected('pool is not a string');\n const optional = Object.fromEntries(\n [...OPTIONAL_POSITION_STRINGS, 'currentTick' as const].flatMap((key) =>\n wire[key] === undefined ? [] : [[key, wire[key]]],\n ),\n );\n return entryOk({\n token0Symbol: wire['token0Symbol'] as string,\n token1Symbol: wire['token1Symbol'] as string,\n fee: wire['fee'] as number,\n tickLower: wire['tickLower'] as number,\n tickUpper: wire['tickUpper'] as number,\n liquidity: wire['liquidity'] as string,\n amount0: wire['amount0'] as string,\n amount1: wire['amount1'] as string,\n inRange: wire['inRange'],\n pool,\n owner: wire['owner'] as string,\n ...optional,\n });\n}\n\n/** The position inside a `/position` answer, or `INVALID_CHAIN_RESPONSE` when it is not one. */\nfunction readPositionBody(data: unknown): Position {\n const parsed = parsePosition(data);\n if (parsed.ok) return parsed.value;\n throw new GSwapSDKError('INVALID_POSITION_RESPONSE', { reason: parsed.reason }, { body: data });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"positions.js","names":[],"sources":["../../../src/classes/positions.ts"],"sourcesContent":["import BigNumber from 'bignumber.js';\nimport { ALLOWED_FEE_TIERS_TEXT } from './error_catalog.js';\nimport { GSwapSDKError, isGSwapError } from './gswap_sdk_error.js';\nimport type { ChainGateway, ChainSubmitOptions } from './gateway.js';\nimport type { PositionConfirmationResult, SubmittedTransaction } from './submitted_transaction.js';\nimport { HttpClient } from './http_client.js';\nimport type { GalaChainSigner } from './signers.js';\nimport { rawTokenOf, type Symbols, type TokenResolver } from './symbols.js';\nimport type { NumericAmount, PriceIn } from '../types/amounts.js';\nimport type { FEE_TIER } from '../types/fees.js';\nimport { TICK_SPACING_BY_FEE } from '../types/fees.js';\nimport type {\n AddLiquidityDTO,\n CollectPositionFeesDTO,\n CreatePoolDTO,\n RemoveLiquidityDTO,\n} from '../types/v2_dtos.js';\nimport type {\n AddLiquidityEstimate,\n Position,\n RemoveLiquidityEstimate,\n} from '../types/v2_results.js';\nimport type { WireOptions } from '../types/wire.js';\nimport {\n asEntryRecord,\n entryOk,\n entryRejected,\n readEntries,\n type EntryParse,\n type InvalidEntryHandler,\n} from '../utils/lenient.js';\nimport { compositeKeyOf } from '../utils/class_key.js';\nimport { type TokenRef, parseTokenClassKey } from '../utils/ordering.js';\nimport { resolvePreSend, signPreSend } from '../utils/pre_send.js';\nimport {\n isProposableTradingSymbol,\n validateFee,\n validateNumericAmount,\n} from '../utils/validation.js';\nimport { tickFromPrice } from '../utils/tick_math.js';\nimport { alignTickDown, alignTickUp, assertTickRange } from '../utils/ticks.js';\nimport {\n assertCanonicalV2Dto,\n buildAddLiquidityDto,\n buildCollectPositionFeesDto,\n buildCreatePoolDto,\n buildRemoveLiquidityDto,\n} from '../utils/dto_builders.js';\nimport {\n assertUniqueKey,\n createUniqueKey,\n resolveUniqueKeyPrefix,\n type UniqueKeyOptions,\n} from '../utils/unique_key.js';\n\ntype PositionGateway = Pick<ChainGateway, 'httpRequestor' | 'dexBackendBaseUrl'> & {\n submit: (\n method: string,\n signedBody: Record<string, unknown>,\n options?: ChainSubmitOptions,\n ) => Promise<SubmittedTransaction<Position | null>>;\n requestTimeoutMs?: number | undefined;\n wire?: WireOptions | undefined;\n};\ntype PositionSymbols = TokenResolver & Pick<Symbols, 'orderPair'> & { invalidate?: () => void };\n\n/** Options of {@link Positions}: the `uniqueKey` prefix and a handler for skipped list rows. */\nexport interface PositionsOptions extends UniqueKeyOptions {\n /** Told about every position row of a list that was skipped as unusable. */\n onInvalidEntry?: InvalidEntryHandler | undefined;\n}\n\n/** A write's optional attempt identity. */\ninterface UniqueKeyArg {\n /**\n * The attempt's `uniqueKey` (1-128 characters of `A-Za-z0-9._:-`). Default: a fresh\n * `<uniqueKeyPrefix><uuid>`. Supply one to know the key before signing, or to retry the SAME\n * attempt after a lost response; never reuse it for another write.\n */\n uniqueKey?: string | undefined;\n}\n\n/** Arguments of {@link Positions.addLiquidityByTicks} and {@link Positions.buildAddLiquidity}. */\nexport interface AddLiquidityArgs extends UniqueKeyArg {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n tickLower: number;\n tickUpper: number;\n amount: NumericAmount;\n /** Whether `amount` is of `token0` as the caller passed it (not of the canonical token0). */\n amountIsToken0: boolean;\n}\n\n/** Arguments of {@link Positions.removeLiquidity} and {@link Positions.buildRemoveLiquidity}. */\nexport interface RemoveLiquidityArgs extends UniqueKeyArg {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n tickLower: number;\n tickUpper: number;\n /** Withdraw this much of the caller's `token0`; give at most one of `amount0` / `amount1`. */\n amount0?: NumericAmount | undefined;\n amount1?: NumericAmount | undefined;\n}\n\n/** Arguments of {@link Positions.collectPositionFees} and {@link Positions.buildCollectPositionFees}. */\nexport interface CollectPositionFeesArgs extends UniqueKeyArg {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n tickLower: number;\n tickUpper: number;\n}\n\n/** Arguments of {@link Positions.createPool} and {@link Positions.buildCreatePool}. */\nexport interface CreatePoolArgs extends UniqueKeyArg {\n token0: TokenRef;\n token1: TokenRef;\n token0Symbol?: string | undefined;\n token1Symbol?: string | undefined;\n fee: FEE_TIER;\n startingPrice?: NumericAmount | undefined;\n startingSqrtPrice?: NumericAmount | undefined;\n /** Optional pool display name, sent as the DTO's `name`; omitted when undefined or empty. */\n name?: string | undefined;\n isPrivate?: boolean | undefined;\n privateAccess?: string[] | undefined;\n}\n\n/** The pool position a liquidity DTO names, as its confirmation needs it. */\nfunction identityOf(\n dto: Pick<CollectPositionFeesDTO, 'token0' | 'token1' | 'fee' | 'tickLower' | 'tickUpper'>,\n): PositionIdentity {\n return {\n token0: dto.token0,\n token1: dto.token1,\n fee: dto.fee,\n tickLower: dto.tickLower,\n tickUpper: dto.tickUpper,\n };\n}\n\n/**\n * The outcome of {@link Positions.readPosition}. Only `not_found` is chain-confirmed absence (the\n * backend answers HTTP 404 only when the chain itself found no such position); `unknown` is \"no\n * evidence either way\" — a transport failure, a gateway timeout, any other error status or an\n * answer that is not a position — and must never be read as a removal.\n */\nexport type PositionRead =\n | { kind: 'found'; data: Position }\n | { kind: 'not_found' }\n | { kind: 'unknown'; error: GSwapSDKError };\n\ninterface BackendEnvelope<TData> {\n data: TData;\n}\n\ninterface AddLiquidityEstimateWire {\n amount0: string;\n amount1: string;\n liquidity: string;\n token0Symbol: string;\n token1Symbol: string;\n tickLower: number;\n tickUpper: number;\n amountIsCanonicalToken0: boolean;\n}\n\ninterface RemoveLiquidityEstimateWire {\n amount0: string;\n amount1: string;\n}\n\ninterface ResolvedCreateToken {\n symbol: string;\n classKey: ReturnType<typeof parseTokenClassKey>;\n}\n\ninterface PositionIdentity {\n token0: string;\n token1: string;\n fee: FEE_TIER;\n tickLower: number;\n tickUpper: number;\n}\n\n/** Tells `submit()` how to prove a write's own effect for `SubmittedTransaction.confirm()`. */\ntype PositionSubmitConfirmation =\n | { kind: 'add' | 'removePartial' | 'removeFull' | 'collect'; identity: PositionIdentity }\n | { kind: 'createPool'; token0: string; token1: string; fee: FEE_TIER };\n\n/**\n * Manages current-contract concentrated-liquidity positions.\n *\n * @example\n * ```typescript\n * const positions = gSwap.positions;\n * const mine = await positions.getUserPositions('client|012345678901234567890123');\n * console.log(mine.length);\n * ```\n */\nexport class Positions {\n constructor(\n private readonly gateway: PositionGateway,\n private readonly symbols: PositionSymbols,\n private readonly signer?: GalaChainSigner,\n private readonly walletAddress?: string,\n options: PositionsOptions = {},\n ) {\n this.http = new HttpClient(\n gateway.httpRequestor,\n gateway.requestTimeoutMs ?? 30_000,\n gateway.wire,\n );\n this.uniqueKeyPrefix = resolveUniqueKeyPrefix(options.uniqueKeyPrefix);\n this.onInvalidEntry = options.onInvalidEntry;\n }\n\n private readonly http: HttpClient;\n private readonly uniqueKeyPrefix: string;\n private readonly onInvalidEntry: InvalidEntryHandler | undefined;\n\n /**\n * Gets all current-contract positions owned by an address.\n *\n * @param owner - GalaChain owner identity.\n * @returns Enriched positions from the v2 trade backend, token images included. A row that is\n * not a usable position is skipped and reported to `onInvalidEntry`.\n * @example\n * ```typescript\n * const positions = await gSwap.positions.getUserPositions('client|012345678901234567890123');\n * ```\n */\n async getUserPositions(owner: string): Promise<Position[]> {\n const response = await this.get<unknown>('/positions', { user: owner });\n return readEntries('positions', response.data, parsePosition, this.onInvalidEntry);\n }\n\n /**\n * Gets one position, normalizing a caller-supplied reversed pair to canonical symbol order.\n * Reversed pairs also invert the price axis, so `[lower, upper]` becomes `[-upper, -lower]`.\n *\n * @param args - Pair, owner, fee, and position tick range.\n * @returns The position, or `null` when the backend returns 404.\n * @example\n * ```typescript\n * const position = await gSwap.positions.getPosition({\n * token0: 'GALA', token1: 'GUSDC', owner: 'client|012345678901234567890123',\n * fee: 3000, tickLower: -19200, tickUpper: 12000,\n * });\n * ```\n */\n async getPosition(\n args: {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n owner: string;\n tickLower: number;\n tickUpper: number;\n },\n signal?: AbortSignal,\n ): Promise<Position | null> {\n const response = await this.getNullable<unknown>(\n '/position',\n await this.positionParams(args),\n signal,\n );\n return response === null ? null : readPositionBody(response.data);\n }\n\n /**\n * Reads one position and classifies the outcome instead of throwing: `found` with the position\n * (fees included), `not_found` (the backend's HTTP 404, which it sends only when the chain\n * itself has no such position) or `unknown` (any other failure, carried as `error`). Use it\n * to confirm a write: `unknown` is \"no evidence either way\", never a confirmed absence.\n *\n * Only the caller's own abort rethrows. With a pass-through resolver ({@link CanonicalSymbols})\n * the pair and ticks go to the backend exactly as given and the backend orients them.\n *\n * @example\n * ```typescript\n * const read = await gSwap.positions.readPosition({\n * token0: 'GALA', token1: 'GUSDC', owner: 'client|012345678901234567890123',\n * fee: 3000, tickLower: -19200, tickUpper: 12000,\n * });\n * if (read.kind === 'found') console.log(read.data.fees0);\n * ```\n */\n async readPosition(\n args: {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n owner: string;\n tickLower: number;\n tickUpper: number;\n },\n signal?: AbortSignal,\n ): Promise<PositionRead> {\n try {\n const position = await this.getPosition(args, signal);\n return position === null ? { kind: 'not_found' } : { kind: 'found', data: position };\n } catch (error: unknown) {\n if (signal?.aborted === true) throw error;\n return {\n kind: 'unknown',\n error: isGSwapError(error)\n ? error\n : new GSwapSDKError('REQUEST_FAILED', { operation: 'position' }, { cause: error }),\n };\n }\n }\n\n /**\n * The `/position` query: canonical symbols and canonical ticks after a client-side flip, or,\n * for a pass-through resolver, the caller's references and ticks untouched.\n */\n private async positionParams(args: {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n owner: string;\n tickLower: number;\n tickUpper: number;\n }): Promise<Record<string, string>> {\n if (this.symbols.passthrough === true) {\n return {\n token0: rawTokenOf(args.token0),\n token1: rawTokenOf(args.token1),\n fee: `${args.fee}`,\n owner: args.owner,\n tickLower: `${args.tickLower}`,\n tickUpper: `${args.tickUpper}`,\n };\n }\n const pair = await this.orderPair(args.token0, args.token1);\n const ticks = this.canonicalTicks(args.tickLower, args.tickUpper, pair.flipped);\n return {\n token0: pair.token0.symbol,\n token1: pair.token1.symbol,\n fee: `${args.fee}`,\n owner: args.owner,\n tickLower: `${ticks.tickLower}`,\n tickUpper: `${ticks.tickUpper}`,\n };\n }\n\n /**\n * Estimates the other token amount and resulting liquidity for a deposit.\n *\n * @param args - Pair, tick range, deposit amount, and the amount's caller-side token index.\n * @returns Backend liquidity estimate.\n * @example\n * ```typescript\n * const estimate = await gSwap.positions.estimateAddLiquidity({\n * token0: 'GALA', token1: 'GUSDC', fee: 3000,\n * tickLower: -19200, tickUpper: 12000, amount: '100', amountIsToken0: true,\n * });\n * ```\n */\n async estimateAddLiquidity(args: {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n tickLower: number;\n tickUpper: number;\n amount: NumericAmount;\n amountIsToken0: boolean;\n }): Promise<AddLiquidityEstimate> {\n validateNumericAmount(args.amount, 'amount');\n const response = await this.get<AddLiquidityEstimateWire>('/add-liq-estimate', {\n token0: rawTokenOf(args.token0),\n token1: rawTokenOf(args.token1),\n fee: `${args.fee}`,\n tickLower: `${args.tickLower}`,\n tickUpper: `${args.tickUpper}`,\n amount: new BigNumber(args.amount).toFixed(),\n amountIsToken0: String(args.amountIsToken0),\n });\n return response.data;\n }\n\n /**\n * Estimates token amounts released by burning liquidity.\n *\n * @param args - Pair, tick range, fee tier, and liquidity quantity.\n * @returns Estimated token0 and token1 quantities.\n * @example\n * ```typescript\n * const estimate = await gSwap.positions.estimateRemoveLiquidity({\n * token0: 'GALA', token1: 'GUSDC', fee: 3000,\n * tickLower: -19200, tickUpper: 12000, liquidity: '1000',\n * });\n * ```\n */\n async estimateRemoveLiquidity(args: {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n tickLower: number;\n tickUpper: number;\n liquidity: NumericAmount;\n }): Promise<RemoveLiquidityEstimate> {\n validateNumericAmount(args.liquidity, 'liquidity');\n const response = await this.get<RemoveLiquidityEstimateWire>('/remove-liq-estimate', {\n token0: rawTokenOf(args.token0),\n token1: rawTokenOf(args.token1),\n fee: `${args.fee}`,\n tickLower: `${args.tickLower}`,\n tickUpper: `${args.tickUpper}`,\n liquidity: new BigNumber(args.liquidity).toFixed(),\n });\n return response.data;\n }\n\n /**\n * Adds liquidity using an explicit tick range. Exactly one canonical deposit field is emitted.\n *\n * @param args - Pair, fee, range, deposit amount, and caller-side token index.\n * @returns The synchronously submitted transaction.\n * @example\n * ```typescript\n * const tx = await gSwap.positions.addLiquidityByTicks({\n * token0: 'GALA', token1: 'GUSDC', fee: 3000,\n * tickLower: -19200, tickUpper: 12000, amount: '100', amountIsToken0: true,\n * });\n * await tx.confirm();\n * ```\n */\n async addLiquidityByTicks(args: AddLiquidityArgs) {\n return this.submitAddLiquidity(await this.buildAddLiquidity(args));\n }\n\n /**\n * Builds the AddLiquidity DTO that {@link addLiquidityByTicks} would sign, without signing or\n * sending it: to show in an approval popup, or to sign elsewhere. Submit exactly this DTO with\n * {@link submitAddLiquidity}. The pure counterpart, with canonical inputs and no resolution,\n * is `buildAddLiquidityDto`.\n *\n * @example\n * ```typescript\n * const dto = await gSwap.positions.buildAddLiquidity({\n * token0: 'GALA', token1: 'GUSDC', fee: 3000,\n * tickLower: -19200, tickUpper: 12000, amount: '100', amountIsToken0: true,\n * });\n * ```\n */\n async buildAddLiquidity(args: AddLiquidityArgs): Promise<AddLiquidityDTO> {\n validateFee(args.fee);\n const pair = await this.orderPair(args.token0, args.token1);\n const ticks = this.canonicalTicks(args.tickLower, args.tickUpper, pair.flipped);\n // Caller-facing checks first, so a refusal names the caller's own argument (`amount`).\n assertTickRange(ticks.tickLower, ticks.tickUpper, args.fee);\n validateNumericAmount(args.amount, 'amount');\n const base = {\n token0: pair.token0.symbol,\n token1: pair.token1.symbol,\n fee: args.fee,\n tickLower: ticks.tickLower,\n tickUpper: ticks.tickUpper,\n uniqueKey: this.uniqueKeyFor(args.uniqueKey),\n };\n return pair.flipped !== args.amountIsToken0\n ? buildAddLiquidityDto({ ...base, depositQuantityToken0: args.amount })\n : buildAddLiquidityDto({ ...base, depositQuantityToken1: args.amount });\n }\n\n /**\n * Signs and submits a DTO built by {@link buildAddLiquidity} or `buildAddLiquidityDto`; it must\n * be exactly what the builder produces. The returned transaction confirms by proving the\n * position's liquidity grew.\n */\n async submitAddLiquidity(dto: AddLiquidityDTO) {\n assertCanonicalV2Dto('AddLiquidity', dto);\n return this.submit('AddLiquidity', dto, { kind: 'add', identity: identityOf(dto) });\n }\n\n /**\n * Adds liquidity from prices, flooring the lower tick and ceiling the upper tick to fee spacing.\n * The resulting range is therefore no narrower than the requested price interval.\n *\n * @param args - Pair, fee, price range, deposit amount, and caller-side token index.\n * @returns The synchronously submitted transaction.\n * @example\n * ```typescript\n * const tx = await gSwap.positions.addLiquidityByPrice({\n * token0: 'GALA', token1: 'GUSDC', fee: 3000,\n * minPrice: '0.14', maxPrice: '0.16', amount: '100', amountIsToken0: true,\n * });\n * ```\n */\n async addLiquidityByPrice(args: {\n token0: TokenRef;\n token1: TokenRef;\n fee: FEE_TIER;\n minPrice: PriceIn;\n maxPrice: PriceIn;\n amount: NumericAmount;\n amountIsToken0: boolean;\n /** The attempt's `uniqueKey`; default a fresh one (see {@link AddLiquidityArgs.uniqueKey}). */\n uniqueKey?: string | undefined;\n }) {\n validateFee(args.fee);\n this.rejectNumber(args.minPrice, 'minPrice');\n this.rejectNumber(args.maxPrice, 'maxPrice');\n const minPrice = new BigNumber(args.minPrice);\n const maxPrice = new BigNumber(args.maxPrice);\n if (\n !minPrice.isFinite() ||\n !maxPrice.isFinite() ||\n minPrice.isLessThanOrEqualTo(0) ||\n maxPrice.isLessThanOrEqualTo(0)\n ) {\n throw new GSwapSDKError('INVALID_PRICE_BOUNDS', {\n minPrice: String(args.minPrice),\n maxPrice: String(args.maxPrice),\n });\n }\n if (minPrice.isGreaterThan(maxPrice)) {\n throw new GSwapSDKError('PRICE_BOUNDS_ORDER', {\n minPrice: String(args.minPrice),\n maxPrice: String(args.maxPrice),\n });\n }\n const spacing = this.tickSpacing(args.fee);\n const tickLower = alignTickDown(tickFromPrice(minPrice), spacing);\n const tickUpper = alignTickUp(tickFromPrice(maxPrice), spacing);\n return this.addLiquidityByTicks({\n token0: args.token0,\n token1: args.token1,\n fee: args.fee,\n tickLower,\n tickUpper,\n amount: args.amount,\n amountIsToken0: args.amountIsToken0,\n uniqueKey: args.uniqueKey,\n });\n }\n\n /**\n * Removes liquidity by withdrawing at most one canonical token quantity.\n * Omitting both amounts closes the position and sweeps all accrued fees.\n *\n * @param args - Pair, fee, range, and optional caller-side withdrawal amount.\n * @returns The synchronously submitted transaction.\n * @example\n * ```typescript\n * const tx = await gSwap.positions.removeLiquidity({\n * token0: 'GALA', token1: 'GUSDC', fee: 3000,\n * tickLower: -19200, tickUpper: 12000,\n * });\n * ```\n */\n async removeLiquidity(args: RemoveLiquidityArgs) {\n return this.submitRemoveLiquidity(await this.buildRemoveLiquidity(args));\n }\n\n /**\n * Builds the RemoveLiquidity DTO that {@link removeLiquidity} would sign, without signing or\n * sending it. Submit exactly this DTO with {@link submitRemoveLiquidity}. The pure counterpart\n * is `buildRemoveLiquidityDto`.\n */\n async buildRemoveLiquidity(args: RemoveLiquidityArgs): Promise<RemoveLiquidityDTO> {\n validateFee(args.fee);\n const pair = await this.orderPair(args.token0, args.token1);\n const ticks = this.canonicalTicks(args.tickLower, args.tickUpper, pair.flipped);\n assertTickRange(ticks.tickLower, ticks.tickUpper, args.fee);\n if (args.amount0 !== undefined && args.amount1 !== undefined) {\n throw new GSwapSDKError('AT_MOST_ONE_OF_ALLOWED', { first: 'amount0', second: 'amount1' });\n }\n if (args.amount0 !== undefined) validateNumericAmount(args.amount0, 'amount0');\n if (args.amount1 !== undefined) validateNumericAmount(args.amount1, 'amount1');\n // The caller's amount0 / amount1 follow the caller's pair; the DTO's follow the canonical one.\n const [canonical0, canonical1] = pair.flipped\n ? [args.amount1, args.amount0]\n : [args.amount0, args.amount1];\n return buildRemoveLiquidityDto({\n token0: pair.token0.symbol,\n token1: pair.token1.symbol,\n fee: args.fee,\n tickLower: ticks.tickLower,\n tickUpper: ticks.tickUpper,\n uniqueKey: this.uniqueKeyFor(args.uniqueKey),\n withdrawalQuantityToken0: canonical0,\n withdrawalQuantityToken1: canonical1,\n });\n }\n\n /**\n * Signs and submits a DTO built by {@link buildRemoveLiquidity} or `buildRemoveLiquidityDto`;\n * it must be exactly what the builder produces. A DTO with no withdrawal quantity closes the\n * position, and the returned transaction then confirms by proving the position is gone.\n */\n async submitRemoveLiquidity(dto: RemoveLiquidityDTO) {\n assertCanonicalV2Dto('RemoveLiquidity', dto);\n const isFullClose =\n dto.withdrawalQuantityToken0 === undefined && dto.withdrawalQuantityToken1 === undefined;\n return this.submit('RemoveLiquidity', dto, {\n kind: isFullClose ? 'removeFull' : 'removePartial',\n identity: identityOf(dto),\n });\n }\n\n /**\n * Collects all accrued fees for a position; the contract does not support partial collection.\n *\n * @param args - Pair, fee, and tick range identifying the position.\n * @returns The synchronously submitted transaction.\n * @example\n * ```typescript\n * const tx = await gSwap.positions.collectPositionFees({\n * token0: 'GALA', token1: 'GUSDC', fee: 3000,\n * tickLower: -19200, tickUpper: 12000,\n * });\n * ```\n */\n async collectPositionFees(args: CollectPositionFeesArgs) {\n return this.submitCollectPositionFees(await this.buildCollectPositionFees(args));\n }\n\n /**\n * Builds the CollectPositionFees DTO that {@link collectPositionFees} would sign, without\n * signing or sending it. Submit exactly this DTO with {@link submitCollectPositionFees}. The\n * pure counterpart is `buildCollectPositionFeesDto`.\n */\n async buildCollectPositionFees(args: CollectPositionFeesArgs): Promise<CollectPositionFeesDTO> {\n validateFee(args.fee);\n const pair = await this.orderPair(args.token0, args.token1);\n const ticks = this.canonicalTicks(args.tickLower, args.tickUpper, pair.flipped);\n assertTickRange(ticks.tickLower, ticks.tickUpper, args.fee);\n return buildCollectPositionFeesDto({\n token0: pair.token0.symbol,\n token1: pair.token1.symbol,\n fee: args.fee,\n tickLower: ticks.tickLower,\n tickUpper: ticks.tickUpper,\n uniqueKey: this.uniqueKeyFor(args.uniqueKey),\n });\n }\n\n /**\n * Signs and submits a DTO built by {@link buildCollectPositionFees} or\n * `buildCollectPositionFeesDto`; it must be exactly what the builder produces.\n */\n async submitCollectPositionFees(dto: CollectPositionFeesDTO) {\n assertCanonicalV2Dto('CollectPositionFees', dto);\n return this.submit('CollectPositionFees', dto, { kind: 'collect', identity: identityOf(dto) });\n }\n\n /**\n * Creates a pool, resolving registered trading symbols.\n * Reversed symbol order inverts the supplied starting price or square-root price.\n *\n * A token class key carries no symbol, so for a token being proposed into the v2\n * trading-symbol registry for the first time (no existing pool has registered it yet) the\n * caller MUST pass `token0Symbol`/`token1Symbol` (the token class's symbol, from the token\n * list or FetchTokenClasses). The SDK never derives a symbol from the key, and throws a\n * TOKEN_SYMBOL_NOT_REGISTERED error when an unregistered token has none. For an already-registered token a\n * supplied symbol must match the registered one.\n *\n * @param args - Token class keys or references, fee, exactly one starting price field, and privacy options.\n * @returns The synchronously submitted transaction.\n * @example\n * ```typescript\n * const tx = await gSwap.positions.createPool({\n * token0: 'GALA|Unit|none|none', token1: 'GUSDC|Unit|none|none', fee: 3000,\n * startingPrice: '0.15',\n * });\n * ```\n */\n async createPool(args: CreatePoolArgs) {\n return this.submitCreatePool(await this.buildCreatePool(args));\n }\n\n /**\n * Builds the CreatePool DTO that {@link createPool} would sign, without signing or sending it:\n * symbols resolved or proposed, the pair put in canonical order and the price inverted when it\n * was reversed. Submit exactly this DTO with {@link submitCreatePool}. The pure counterpart,\n * with canonical inputs, is `buildCreatePoolDto`.\n */\n async buildCreatePool(args: CreatePoolArgs): Promise<CreatePoolDTO> {\n const hasPrice = args.startingPrice !== undefined;\n const hasSqrtPrice = args.startingSqrtPrice !== undefined;\n if (hasPrice === hasSqrtPrice) {\n throw new GSwapSDKError('EXACTLY_ONE_OF_REQUIRED', {\n first: 'startingPrice',\n second: 'startingSqrtPrice',\n });\n }\n validateFee(args.fee);\n const [tokenA, tokenB] = await Promise.all([\n this.resolveCreateToken(args.token0, args.token0Symbol),\n this.resolveCreateToken(args.token1, args.token1Symbol),\n ]);\n const flipped = tokenA.symbol > tokenB.symbol;\n const [token0, token1] = flipped ? [tokenB, tokenA] : [tokenA, tokenB];\n const sourcePriceInput = hasPrice\n ? (args.startingPrice as NumericAmount)\n : (args.startingSqrtPrice as NumericAmount);\n this.rejectNumber(sourcePriceInput, hasPrice ? 'startingPrice' : 'startingSqrtPrice');\n const sourcePrice = new BigNumber(sourcePriceInput);\n if (!sourcePrice.isFinite() || sourcePrice.isLessThanOrEqualTo(0)) {\n throw new GSwapSDKError('INVALID_STARTING_PRICE', { value: String(sourcePriceInput) });\n }\n const canonicalPrice = flipped ? new BigNumber(1).dividedBy(sourcePrice) : sourcePrice;\n const shared = {\n token0Key: token0.classKey,\n token1Key: token1.classKey,\n token0Symbol: token0.symbol,\n token1Symbol: token1.symbol,\n fee: args.fee,\n uniqueKey: this.uniqueKeyFor(args.uniqueKey),\n name: args.name,\n isPrivate: args.isPrivate,\n privateAccess: args.privateAccess,\n };\n return hasPrice\n ? buildCreatePoolDto({ ...shared, startingPrice: canonicalPrice })\n : buildCreatePoolDto({ ...shared, startingSqrtPrice: canonicalPrice });\n }\n\n /**\n * Signs and submits a DTO built by {@link buildCreatePool} or `buildCreatePoolDto`; it must be\n * exactly what the builder produces. The returned transaction confirms by the pool existing, and\n * the cached symbol registry is dropped, since the pool may have registered new symbols.\n */\n async submitCreatePool(dto: CreatePoolDTO) {\n assertCanonicalV2Dto('CreatePool', dto);\n const transaction = await this.submit('CreatePool', dto, {\n kind: 'createPool',\n token0: dto.token0Symbol,\n token1: dto.token1Symbol,\n fee: dto.fee,\n });\n this.symbols.invalidate?.();\n return transaction;\n }\n\n private async submit(\n method: string,\n dto: Record<string, unknown>,\n confirmation?: PositionSubmitConfirmation,\n ) {\n if (this.signer === undefined) throw GSwapSDKError.noSignerError();\n const signed = await signPreSend(this.signer, method, dto);\n const submitOptions =\n this.walletAddress === undefined ? {} : { walletAddress: this.walletAddress };\n const owner = this.walletAddress;\n const positionConfirmation = this.buildPositionConfirmation(confirmation, owner);\n const options: ChainSubmitOptions = {\n ...submitOptions,\n ...(positionConfirmation === undefined ? {} : { positionConfirmation }),\n };\n return this.gateway.submit(method, signed, options);\n }\n\n /**\n * Builds a confirmation closure that proves the write's own effect, not just that a\n * position row exists (true before most of these writes run too, for any operation on a\n * pre-existing position — existence alone would false-confirm before the write executes,\n * and would never confirm a full close, whose whole point is that the position stops\n * existing).\n *\n * The \"before\" state is captured lazily, on the confirmation closure's own first poll —\n * not eagerly here in `submit()` — so confirming a write never adds an extra round-trip to\n * the write itself; the cost of establishing the baseline is paid by `confirm()`, only if\n * and when the caller actually asks to confirm.\n */\n private buildPositionConfirmation(\n confirmation: PositionSubmitConfirmation | undefined,\n owner: string | undefined,\n ): ((signal: AbortSignal) => Promise<PositionConfirmationResult>) | undefined {\n if (confirmation === undefined) return undefined;\n\n if (confirmation.kind === 'createPool') {\n const { token0, token1, fee } = confirmation;\n return async (signal) => {\n const pool = await this.getNullable<unknown>(\n '/pool',\n { token0, token1, fee: `${fee}` },\n signal,\n );\n return pool === null ? { confirmed: false } : { confirmed: true, position: null };\n };\n }\n\n if (owner === undefined) return undefined;\n const { kind, identity } = confirmation;\n let baseline: { liquidity: BigNumber; fees: BigNumber } | undefined;\n\n return async (signal) => {\n const current = await this.getPosition({ ...identity, owner }, signal);\n\n if (baseline === undefined) {\n // First poll only establishes the baseline to compare future polls against — it can\n // never itself be the confirmed outcome (nothing has been observed to change yet).\n baseline = {\n liquidity: new BigNumber(current?.liquidity ?? 0),\n fees: new BigNumber(current?.fees0 ?? 0).plus(new BigNumber(current?.fees1 ?? 0)),\n };\n // A full close starting from \"already gone\" (e.g. a retried confirm after the position\n // was already swept) is confirmed immediately rather than waiting for a change that\n // already happened.\n return kind === 'removeFull' && current === null\n ? { confirmed: true, position: null }\n : { confirmed: false };\n }\n\n switch (kind) {\n case 'add':\n return current !== null &&\n new BigNumber(current.liquidity).isGreaterThan(baseline.liquidity)\n ? { confirmed: true, position: current }\n : { confirmed: false };\n case 'removePartial':\n return current !== null && new BigNumber(current.liquidity).isLessThan(baseline.liquidity)\n ? { confirmed: true, position: current }\n : { confirmed: false };\n case 'removeFull':\n return current === null ? { confirmed: true, position: null } : { confirmed: false };\n case 'collect': {\n if (current === null) return { confirmed: false };\n const currentFees = new BigNumber(current.fees0 ?? 0).plus(\n new BigNumber(current.fees1 ?? 0),\n );\n if (currentFees.isLessThan(baseline.fees)) return { confirmed: true, position: current };\n // Nothing to sweep in the first place — a legitimate no-op collect never moves\n // fees, so \"still zero\" is the confirmed outcome, not an unconfirmed one.\n if (baseline.fees.isZero() && currentFees.isZero())\n return { confirmed: true, position: current };\n return { confirmed: false };\n }\n }\n };\n }\n\n private async get<TData>(\n endpoint: string,\n params: Record<string, string>,\n signal?: AbortSignal,\n ): Promise<BackendEnvelope<TData>> {\n return this.http.sendGetRequest<BackendEnvelope<TData>>(\n this.gateway.dexBackendBaseUrl,\n '/v2/trade',\n endpoint,\n params,\n signal === undefined ? undefined : { signal },\n );\n }\n\n private async getNullable<TData>(\n endpoint: string,\n params: Record<string, string>,\n signal?: AbortSignal,\n ): Promise<BackendEnvelope<TData> | null> {\n try {\n return await this.get<TData>(endpoint, params, signal);\n } catch (error: unknown) {\n if (this.isNotFound(error)) return null;\n throw error;\n }\n }\n\n private isNotFound(error: unknown): boolean {\n return error instanceof GSwapSDKError && error.status === 404;\n }\n\n private canonicalTicks(tickLower: number, tickUpper: number, flipped: boolean) {\n return flipped ? { tickLower: -tickUpper, tickUpper: -tickLower } : { tickLower, tickUpper };\n }\n\n private tickSpacing(fee: number): number {\n const spacing = (TICK_SPACING_BY_FEE as Record<number, number>)[fee];\n if (spacing === undefined) {\n throw new GSwapSDKError('INVALID_FEE', {\n field: 'fee',\n value: fee,\n allowed: ALLOWED_FEE_TIERS_TEXT,\n });\n }\n return spacing;\n }\n\n private rejectNumber(value: unknown, parameterName: string): void {\n if (typeof value === 'number') {\n throw new GSwapSDKError('AMOUNT_IS_JS_NUMBER', { field: parameterName });\n }\n }\n\n /** The caller's `uniqueKey` (validated), else a fresh one. */\n private uniqueKeyFor(supplied: string | undefined): string {\n if (supplied === undefined) return createUniqueKey(this.uniqueKeyPrefix);\n assertUniqueKey(supplied);\n return supplied;\n }\n\n /**\n * The pair in canonical order. A resolver failure that is not an SDK error (an app's own\n * resolver, an app requestor's rejection during the registry read) is `TOKEN_RESOLUTION_FAILED`.\n */\n private orderPair(a: TokenRef, b: TokenRef) {\n return resolvePreSend(`${rawTokenOf(a)}/${rawTokenOf(b)}`, () => this.symbols.orderPair(a, b));\n }\n\n private async resolveCreateToken(\n ref: TokenRef,\n symbolOverride?: string,\n ): Promise<ResolvedCreateToken> {\n try {\n const lookupRef = typeof ref === 'object' ? compositeKeyOf(ref) : ref;\n const resolved = await resolvePreSend(rawTokenOf(ref), () => this.symbols.resolve(lookupRef));\n if (\n symbolOverride !== undefined &&\n symbolOverride !== '' &&\n symbolOverride !== resolved.symbol\n ) {\n throw new GSwapSDKError('SYMBOL_DISAGREES_WITH_REGISTRY', {\n token: rawTokenOf(ref),\n suppliedSymbol: symbolOverride,\n registeredSymbol: resolved.symbol,\n });\n }\n const suppliedClassKey = this.tryParseClassKey(ref);\n if (suppliedClassKey === undefined && resolved.collection === '') {\n // An offline resolver knows a bare symbol but not its class key; a pool needs the key.\n throw new GSwapSDKError('CREATE_POOL_NEEDS_CLASS_KEY', { token: rawTokenOf(ref) });\n }\n return {\n symbol: resolved.symbol,\n classKey:\n suppliedClassKey ??\n parseTokenClassKey({\n collection: resolved.collection,\n category: resolved.category,\n type: resolved.type,\n additionalKey: resolved.additionalKey,\n }),\n };\n } catch (error: unknown) {\n if (!(error instanceof GSwapSDKError) || error.code !== 'UNKNOWN_TOKEN') {\n throw error;\n }\n let classKey: ReturnType<typeof parseTokenClassKey>;\n try {\n classKey = parseTokenClassKey(ref);\n } catch {\n throw error;\n }\n // First-time registration: a class key carries NO symbol (collection and symbol disagree\n // for many tokens, launchpad tokens all share collection `Token`, and some collections\n // contain `$`), so the symbol to propose must be supplied by the caller from the token\n // class / registry. Never derive it from the key.\n if (symbolOverride === undefined || symbolOverride === '') {\n throw new GSwapSDKError('TOKEN_SYMBOL_NOT_REGISTERED', { token: rawTokenOf(ref) });\n }\n if (!isProposableTradingSymbol(symbolOverride)) {\n throw new GSwapSDKError('INVALID_TRADING_SYMBOL', { symbol: symbolOverride });\n }\n return { symbol: symbolOverride, classKey };\n }\n }\n\n private tryParseClassKey(ref: TokenRef): ReturnType<typeof parseTokenClassKey> | undefined {\n try {\n return parseTokenClassKey(ref);\n } catch {\n return undefined;\n }\n }\n}\n\nconst REQUIRED_POSITION_STRINGS = [\n 'token0Symbol',\n 'token1Symbol',\n 'owner',\n 'liquidity',\n 'amount0',\n 'amount1',\n] as const;\nconst REQUIRED_POSITION_NUMBERS = ['fee', 'tickLower', 'tickUpper'] as const;\nconst OPTIONAL_POSITION_STRINGS = [\n 'token0CompositeKey',\n 'token1CompositeKey',\n 'token0Image',\n 'token1Image',\n 'sqrtPrice',\n 'fees0',\n 'fees1',\n] as const;\n\n/**\n * Reads one position row of `/positions` or `/position`: the pair, owner, range, liquidity and\n * amounts must be usable; the composite keys, token images, current tick and price, and the\n * escrowed fees are carried when present. `pool` is the pool reference (`poolRef` wins over\n * `pool`; `''` when the row names neither).\n */\nfunction parsePosition(value: unknown): EntryParse<Position> {\n const wire = asEntryRecord(value);\n if (wire === undefined) return entryRejected('the row is not an object');\n for (const key of REQUIRED_POSITION_STRINGS) {\n if (typeof wire[key] !== 'string') return entryRejected(`${key} is not a string`);\n }\n for (const key of REQUIRED_POSITION_NUMBERS) {\n const number = wire[key];\n if (typeof number !== 'number' || !Number.isFinite(number)) {\n return entryRejected(`${key} is not a number`);\n }\n }\n if (typeof wire['inRange'] !== 'boolean') return entryRejected('inRange is not a boolean');\n for (const key of OPTIONAL_POSITION_STRINGS) {\n if (wire[key] !== undefined && typeof wire[key] !== 'string') {\n return entryRejected(`${key} is not a string`);\n }\n }\n const currentTick = wire['currentTick'];\n if (\n currentTick !== undefined &&\n (typeof currentTick !== 'number' || !Number.isFinite(currentTick))\n ) {\n return entryRejected('currentTick is not a number');\n }\n const pool = wire['poolRef'] ?? wire['pool'] ?? '';\n if (typeof pool !== 'string') return entryRejected('pool is not a string');\n const optional = Object.fromEntries(\n [...OPTIONAL_POSITION_STRINGS, 'currentTick' as const].flatMap((key) =>\n wire[key] === undefined ? [] : [[key, wire[key]]],\n ),\n );\n return entryOk({\n token0Symbol: wire['token0Symbol'] as string,\n token1Symbol: wire['token1Symbol'] as string,\n fee: wire['fee'] as number,\n tickLower: wire['tickLower'] as number,\n tickUpper: wire['tickUpper'] as number,\n liquidity: wire['liquidity'] as string,\n amount0: wire['amount0'] as string,\n amount1: wire['amount1'] as string,\n inRange: wire['inRange'],\n pool,\n owner: wire['owner'] as string,\n ...optional,\n });\n}\n\n/** The position inside a `/position` answer, or `INVALID_CHAIN_RESPONSE` when it is not one. */\nfunction readPositionBody(data: unknown): Position {\n const parsed = parsePosition(data);\n if (parsed.ok) return parsed.value;\n throw new GSwapSDKError('INVALID_POSITION_RESPONSE', { reason: parsed.reason }, { body: data });\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ function e(e){let t=()=>typeof e==`function`?e():e;return{async get(e){let n=await t().get(e);return Object.hasOwn(n,e)?n[e]:void 0},async set(e,n){await t().set({[e]:n})}}}function t(e){let t=()=>typeof e==`function`?e():e;return{async get(e){let n=t().getItem(e);if(n!==null)try{return JSON.parse(n)}catch{return n}},async set(e,n){t().setItem(e,JSON.stringify(n))}}}export{e as storageAdapterFromChromeStorage,t as webStorageAdapter};
2
+ //# sourceMappingURL=storage_adapters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage_adapters.js","names":[],"sources":["../../../src/classes/storage_adapters.ts"],"sourcesContent":["/**\n * Async key/value storage the pending-swap store persists through. It stores STRUCTURED VALUES\n * (the store never serializes), so each platform decides how a value lands on disk:\n *\n * - `chrome.storage.local`: `get: async (k) => (await chrome.storage.local.get(k))[k]`,\n * `set: (k, v) => chrome.storage.local.set({ [k]: v })`. This is the wallet's stored shape.\n * {@link storageAdapterFromChromeStorage} builds it.\n * - `localStorage` (frontend_v2): JSON text under the key. {@link webStorageAdapter} builds it,\n * and returns CORRUPT JSON as a value (the raw string), not as a rejection: a value that is not\n * an array reads as \"no entries\" and the next write repairs it, whereas a `get` that rejects\n * forever makes every `remember` a no-op.\n * - React Native: AsyncStorage with the same JSON wrapping and the same corrupt-JSON rule.\n *\n * `get` resolves `undefined` for a missing key. Either method may reject; the store treats that\n * as \"storage unavailable\" and never lets it surface (see `PendingSwapStore`).\n *\n * This module imports nothing, so an app shell that only builds an adapter pays for the adapter.\n */\nexport interface StorageAdapter {\n get(key: string): Promise<unknown>;\n set(key: string, value: unknown): Promise<void>;\n}\n\n/**\n * The part of a `chrome.storage` area ({@link storageAdapterFromChromeStorage}) the adapter uses,\n * written structurally so the SDK needs no `chrome` types: `chrome.storage.local`,\n * `chrome.storage.session`, `browser.storage.local` (webextension-polyfill) and any object with\n * these two promise-returning methods satisfy it. It is the Manifest V3 promise form: the methods\n * are called WITHOUT a callback.\n */\nexport interface ChromeStorageAreaLike {\n get(key: string): Promise<Record<string, unknown>>;\n set(items: Record<string, unknown>): Promise<void>;\n}\n\n/**\n * A {@link StorageAdapter} over a `chrome.storage` area, the shape the wallet stores pending swaps\n * in: the value lands under the key as the structured value it is (the browser clones it; nothing\n * is serialized here). A missing key resolves `undefined`; a rejection of the area reaches the\n * store, which treats it as \"storage unavailable\".\n *\n * Pass a FUNCTION that returns the area to look it up at each call instead of when the adapter is\n * built: a module that is imported where `chrome` does not exist yet (a test, a closing extension\n * context) then fails only when the store touches storage, and that failure (a throw inside the\n * function) is the same \"storage unavailable\" rejection.\n *\n * @example\n * ```ts\n * const store = new PendingSwapStore(storageAdapterFromChromeStorage(chrome.storage.local));\n * const lazy = new PendingSwapStore(storageAdapterFromChromeStorage(() => chrome.storage.local));\n * ```\n */\nexport function storageAdapterFromChromeStorage(\n area: ChromeStorageAreaLike | (() => ChromeStorageAreaLike),\n): StorageAdapter {\n const areaNow = (): ChromeStorageAreaLike => (typeof area === 'function' ? area() : area);\n return {\n async get(key) {\n const items = await areaNow().get(key);\n return Object.hasOwn(items, key) ? items[key] : undefined;\n },\n async set(key, value) {\n await areaNow().set({ [key]: value });\n },\n };\n}\n\n/**\n * The part of a Web Storage area ({@link webStorageAdapter}) the adapter uses, written\n * structurally so the SDK needs no DOM types: `localStorage`, `sessionStorage` and any object with\n * these two synchronous methods (a React Native MMKV wrapper, a test double) satisfy it.\n */\nexport interface WebStorageLike {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n}\n\n/**\n * A {@link StorageAdapter} over a Web Storage area (`localStorage`, `sessionStorage`), the shape\n * frontend_v2 stores pending swaps in: the value lands under the key as JSON text. A missing key\n * resolves `undefined`. CORRUPT JSON resolves as the raw string, never a rejection (the store\n * reads a value that is not an array as \"no entries\" and the next write repairs it, whereas a\n * `get` that rejects forever would make every `remember` a no-op). A throwing area (storage blocked\n * by the browser, quota exceeded on `setItem`) rejects, which the store treats as \"storage\n * unavailable\".\n *\n * Pass a FUNCTION that returns the area to look it up at each call instead of when the adapter is\n * built: where `localStorage` does not exist (server rendering, a test, a blocked context)\n * the access throws inside the function, which is the same \"storage unavailable\" rejection, and\n * the module that builds the adapter still loads.\n *\n * @example\n * ```ts\n * const store = new PendingSwapStore(webStorageAdapter(() => localStorage));\n * ```\n */\nexport function webStorageAdapter(area: WebStorageLike | (() => WebStorageLike)): StorageAdapter {\n const areaNow = (): WebStorageLike => (typeof area === 'function' ? area() : area);\n return {\n async get(key) {\n const raw = areaNow().getItem(key);\n if (raw === null) return undefined;\n try {\n return JSON.parse(raw) as unknown;\n } catch {\n return raw;\n }\n },\n async set(key, value) {\n areaNow().setItem(key, JSON.stringify(value));\n },\n };\n}\n"],"mappings":"AAoDA,SAAgB,EACd,EACgB,CAChB,IAAM,MAAwC,OAAO,GAAS,WAAa,EAAK,EAAI,EACpF,MAAO,CACL,MAAM,IAAI,EAAK,CACb,IAAM,EAAQ,MAAM,EAAQ,CAAC,CAAC,IAAI,CAAG,EACrC,OAAO,OAAO,OAAO,EAAO,CAAG,EAAI,EAAM,GAAO,IAAA,EAClD,EACA,MAAM,IAAI,EAAK,EAAO,CACpB,MAAM,EAAQ,CAAC,CAAC,IAAI,EAAG,GAAM,CAAM,CAAC,CACtC,CACF,CACF,CA+BA,SAAgB,EAAkB,EAA+D,CAC/F,IAAM,MAAiC,OAAO,GAAS,WAAa,EAAK,EAAI,EAC7E,MAAO,CACL,MAAM,IAAI,EAAK,CACb,IAAM,EAAM,EAAQ,CAAC,CAAC,QAAQ,CAAG,EACjC,GAAI,IAAQ,KACZ,GAAI,CACF,OAAO,KAAK,MAAM,CAAG,CACvB,MAAQ,CACN,OAAO,CACT,CACF,EACA,MAAM,IAAI,EAAK,EAAO,CACpB,EAAQ,CAAC,CAAC,QAAQ,EAAK,KAAK,UAAU,CAAK,CAAC,CAC9C,CACF,CACF"}
@@ -1,2 +1,2 @@
1
- import e from"../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/defineProperty.js";import{GSwapSDKError as t}from"./gswap_sdk_error.js";import{isProposableTradingSymbol as n}from"../utils/validation.js";import{resolvePreSend as r}from"../utils/pre_send.js";import{HttpClient as i}from"./http_client.js";import{asEntryRecord as a,entryOk as o,entryRejected as s,readEntries as c}from"../utils/lenient.js";import{compositeKeyOf as l,orderSymbols as u,parseTokenClassKey as d}from"../utils/ordering.js";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 i(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=>c(`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)?l(d(e)):void 0:l(e),i=n.find(t=>t.symbol===e||r!==void 0&&l(t)===r);if(i===void 0){n=await this.refresh();let i=n.find(t=>t.symbol===e||r!==void 0&&l(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=l(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]=u(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=a(e);if(t===void 0)return s(`the row is not an object`);let{symbol:n,collection:r,category:i,type:c,additionalKey:l,decimals:u}=t;return typeof n==`string`?n.length===0?s(`symbol is empty`):typeof r==`string`?typeof i==`string`?typeof c==`string`?typeof l==`string`?typeof u!=`number`||!Number.isInteger(u)||u<0?s(`decimals is not a non-negative integer`):o({symbol:n,collection:r,category:i,type:c,additionalKey:l,decimals:u}):s(`additionalKey is not a string`):s(`type is not a string`):s(`category is not a string`):s(`collection is not a string`):s(`symbol is not a string`)}function m(e){return e.includes(`|`)||e.includes(`$`)}function h(e){return typeof e==`string`?e:l(e)}async function g(e,n){let i=await r(h(n),()=>e.resolve(n));if(i.symbol.length>0)return i.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]=u(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.151.0/helpers/esm/defineProperty.js";import{GSwapSDKError as t}from"./gswap_sdk_error.js";import{isProposableTradingSymbol as n}from"../utils/validation.js";import{resolvePreSend as r}from"../utils/pre_send.js";import{HttpClient as i}from"./http_client.js";import{asEntryRecord as a,entryOk as o,entryRejected as s,readEntries as c}from"../utils/lenient.js";import{compositeKeyOf as l}from"../utils/class_key.js";import{orderSymbols as u,parseTokenClassKey as d}from"../utils/ordering.js";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 i(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=>c(`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)?l(d(e)):void 0:l(e),i=n.find(t=>t.symbol===e||r!==void 0&&l(t)===r);if(i===void 0){n=await this.refresh();let i=n.find(t=>t.symbol===e||r!==void 0&&l(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=l(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]=u(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=a(e);if(t===void 0)return s(`the row is not an object`);let{symbol:n,collection:r,category:i,type:c,additionalKey:l,decimals:u}=t;return typeof n==`string`?n.length===0?s(`symbol is empty`):typeof r==`string`?typeof i==`string`?typeof c==`string`?typeof l==`string`?typeof u!=`number`||!Number.isInteger(u)||u<0?s(`decimals is not a non-negative integer`):o({symbol:n,collection:r,category:i,type:c,additionalKey:l,decimals:u}):s(`additionalKey is not a string`):s(`type is not a string`):s(`category is not a string`):s(`collection is not a string`):s(`symbol is not a string`)}function m(e){return e.includes(`|`)||e.includes(`$`)}function h(e){return typeof e==`string`?e:l(e)}async function g(e,n){let i=await r(h(n),()=>e.resolve(n));if(i.symbol.length>0)return i.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]=u(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":[],"sources":["../../../src/classes/symbols.ts"],"sourcesContent":["import type { GalaChainTokenClassKey } from '../types/token.js';\nimport type { TradingSymbol } from '../types/v2_results.js';\nimport type { WireOptions } from '../types/wire.js';\nimport {\n asEntryRecord,\n entryOk,\n entryRejected,\n readEntries,\n type EntryParse,\n type InvalidEntryHandler,\n} from '../utils/lenient.js';\nimport type { TokenRef } from '../utils/ordering.js';\nimport { compositeKeyOf, orderSymbols, parseTokenClassKey } from '../utils/ordering.js';\nimport { resolvePreSend } from '../utils/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/** Alias exported for callers that use the contract's shorter type name. */\nexport type TokenClassKey = GalaChainTokenClassKey;\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":[],"sources":["../../../src/classes/symbols.ts"],"sourcesContent":["import type { GalaChainTokenClassKey } from '../types/token.js';\nimport type { TradingSymbol } from '../types/v2_results.js';\nimport type { WireOptions } from '../types/wire.js';\nimport {\n asEntryRecord,\n entryOk,\n entryRejected,\n readEntries,\n type EntryParse,\n type InvalidEntryHandler,\n} from '../utils/lenient.js';\nimport type { TokenRef } from '../utils/ordering.js';\nimport { compositeKeyOf } from '../utils/class_key.js';\nimport { orderSymbols, parseTokenClassKey } from '../utils/ordering.js';\nimport { resolvePreSend } from '../utils/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/** Alias exported for callers that use the contract's shorter type name. */\nexport type TokenClassKey = GalaChainTokenClassKey;\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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"}