@gala-chain/gswap-sdk 1.2.0 → 2.0.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/README.md +42 -8
  2. package/dist/browser/classes/apply_results.js +1 -1
  3. package/dist/browser/classes/apply_results.js.map +1 -1
  4. package/dist/browser/classes/assets.js +1 -1
  5. package/dist/browser/classes/assets.js.map +1 -1
  6. package/dist/browser/classes/dex_fees.js +1 -1
  7. package/dist/browser/classes/dex_fees.js.map +1 -1
  8. package/dist/browser/classes/gateway.js +1 -1
  9. package/dist/browser/classes/gateway.js.map +1 -1
  10. package/dist/browser/classes/gdex_stream.js +1 -1
  11. package/dist/browser/classes/gdex_stream.js.map +1 -1
  12. package/dist/browser/classes/gswap.js +1 -1
  13. package/dist/browser/classes/gswap.js.map +1 -1
  14. package/dist/browser/classes/gswap_error_catalog.js +2 -0
  15. package/dist/browser/classes/gswap_error_catalog.js.map +1 -0
  16. package/dist/browser/classes/gswap_error_phase_table.js +2 -0
  17. package/dist/browser/classes/gswap_error_phase_table.js.map +1 -0
  18. package/dist/browser/classes/gswap_sdk_error.js +1 -1
  19. package/dist/browser/classes/gswap_sdk_error.js.map +1 -1
  20. package/dist/browser/classes/http_client.js +1 -1
  21. package/dist/browser/classes/http_client.js.map +1 -1
  22. package/dist/browser/classes/leaderboard.js +2 -0
  23. package/dist/browser/classes/leaderboard.js.map +1 -0
  24. package/dist/browser/classes/market.js +2 -0
  25. package/dist/browser/classes/market.js.map +1 -0
  26. package/dist/browser/classes/pending_swap_fate.js.map +1 -1
  27. package/dist/browser/classes/pending_swaps.js +1 -1
  28. package/dist/browser/classes/pending_swaps.js.map +1 -1
  29. package/dist/browser/classes/pools.js +1 -1
  30. package/dist/browser/classes/pools.js.map +1 -1
  31. package/dist/browser/classes/positions.js +1 -1
  32. package/dist/browser/classes/positions.js.map +1 -1
  33. package/dist/browser/classes/quoting.js +1 -1
  34. package/dist/browser/classes/quoting.js.map +1 -1
  35. package/dist/browser/classes/shadow_submit.js.map +1 -1
  36. package/dist/browser/classes/storage_adapters.js.map +1 -1
  37. package/dist/browser/classes/submitted_transaction.js +1 -1
  38. package/dist/browser/classes/submitted_transaction.js.map +1 -1
  39. package/dist/browser/classes/swaps.js +1 -1
  40. package/dist/browser/classes/swaps.js.map +1 -1
  41. package/dist/browser/classes/symbols.js +1 -1
  42. package/dist/browser/classes/symbols.js.map +1 -1
  43. package/dist/browser/index.js +1 -1
  44. package/dist/browser/types/env.js.map +1 -1
  45. package/dist/browser/types/fees.js.map +1 -1
  46. package/dist/browser/types/market.js +2 -0
  47. package/dist/browser/types/market.js.map +1 -0
  48. package/dist/browser/utils/classification.js.map +1 -1
  49. package/dist/browser/utils/confirmation_schedule.js.map +1 -1
  50. package/dist/browser/utils/confirmation_state.js +1 -1
  51. package/dist/browser/utils/confirmation_state.js.map +1 -1
  52. package/dist/browser/utils/debug.js.map +1 -1
  53. package/dist/browser/utils/dto_builders.js +1 -1
  54. package/dist/browser/utils/dto_builders.js.map +1 -1
  55. package/dist/browser/utils/failure_classification.js +1 -1
  56. package/dist/browser/utils/failure_classification.js.map +1 -1
  57. package/dist/browser/utils/failure_guards.js +1 -1
  58. package/dist/browser/utils/failure_guards.js.map +1 -1
  59. package/dist/browser/utils/lenient.js +1 -1
  60. package/dist/browser/utils/lenient.js.map +1 -1
  61. package/dist/browser/utils/ordering.js +1 -1
  62. package/dist/browser/utils/ordering.js.map +1 -1
  63. package/dist/browser/utils/reconnection.js +1 -1
  64. package/dist/browser/utils/reconnection.js.map +1 -1
  65. package/dist/browser/utils/resolve_pre_send.js +2 -0
  66. package/dist/browser/utils/resolve_pre_send.js.map +1 -0
  67. package/dist/browser/utils/server_message.js.map +1 -1
  68. package/dist/browser/utils/shadow_errors.js +1 -1
  69. package/dist/browser/utils/shadow_errors.js.map +1 -1
  70. package/dist/browser/utils/submit_state.js.map +1 -1
  71. package/dist/browser/utils/tick_align.js.map +1 -1
  72. package/dist/browser/utils/tick_math.js.map +1 -1
  73. package/dist/browser/utils/tick_range.js.map +1 -1
  74. package/dist/browser/utils/ticks.js +1 -1
  75. package/dist/browser/utils/ticks.js.map +1 -1
  76. package/dist/browser/utils/validation.js +1 -1
  77. package/dist/browser/utils/validation.js.map +1 -1
  78. package/dist/index.cjs +1033 -2925
  79. package/dist/index.cjs.map +1 -1
  80. package/dist/index.d.cts +439 -1604
  81. package/dist/index.d.ts +439 -1604
  82. package/dist/index.mjs +964 -2810
  83. package/dist/index.mjs.map +1 -1
  84. package/package.json +36 -60
  85. package/dist/browser/classes/error_catalog.js +0 -2
  86. package/dist/browser/classes/error_catalog.js.map +0 -1
  87. package/dist/browser/classes/error_phase_table.js +0 -2
  88. package/dist/browser/classes/error_phase_table.js.map +0 -1
  89. package/dist/browser/classes/error_phases.js +0 -2
  90. package/dist/browser/classes/error_phases.js.map +0 -1
  91. package/dist/browser/classes/signers.js +0 -2
  92. package/dist/browser/classes/signers.js.map +0 -1
  93. package/dist/browser/utils/axios_requestor.js +0 -2
  94. package/dist/browser/utils/axios_requestor.js.map +0 -1
  95. package/dist/browser/utils/chain_dto_signing.js +0 -3
  96. package/dist/browser/utils/chain_dto_signing.js.map +0 -1
  97. package/dist/browser/utils/class_key.js +0 -2
  98. package/dist/browser/utils/class_key.js.map +0 -1
  99. package/dist/browser/utils/eip712.js +0 -4
  100. package/dist/browser/utils/eip712.js.map +0 -1
  101. package/dist/browser/utils/error_brand.js +0 -2
  102. package/dist/browser/utils/error_brand.js.map +0 -1
  103. package/dist/browser/utils/ethers_provider.js +0 -2
  104. package/dist/browser/utils/ethers_provider.js.map +0 -1
  105. package/dist/browser/utils/failure_outcomes.js +0 -2
  106. package/dist/browser/utils/failure_outcomes.js.map +0 -1
  107. package/dist/browser/utils/failure_view.js +0 -2
  108. package/dist/browser/utils/failure_view.js.map +0 -1
  109. package/dist/browser/utils/fetch_failure.js +0 -2
  110. package/dist/browser/utils/fetch_failure.js.map +0 -1
  111. package/dist/browser/utils/pre_send.js +0 -2
  112. package/dist/browser/utils/pre_send.js.map +0 -1
  113. package/dist/browser/utils/signing.js +0 -3
  114. package/dist/browser/utils/signing.js.map +0 -1
  115. package/dist/browser/utils/transport.js +0 -2
  116. package/dist/browser/utils/transport.js.map +0 -1
  117. package/dist/browser/utils/typed_data.js +0 -2
  118. package/dist/browser/utils/typed_data.js.map +0 -1
  119. package/dist/browser/utils/unique_key.js +0 -2
  120. package/dist/browser/utils/unique_key.js.map +0 -1
  121. package/dist/browser/utils/unique_key_syntax.js +0 -2
  122. package/dist/browser/utils/unique_key_syntax.js.map +0 -1
  123. package/dist/browser/utils/wallet_rejection.js +0 -2
  124. package/dist/browser/utils/wallet_rejection.js.map +0 -1
  125. package/dist/browser/version.js +0 -2
  126. package/dist/browser/version.js.map +0 -1
  127. package/dist/browser/wallet.js +0 -2
  128. package/dist/browser/wallet.js.map +0 -1
  129. package/dist/wallet.cjs +0 -159
  130. package/dist/wallet.cjs.map +0 -1
  131. package/dist/wallet.d.cts +0 -74
  132. package/dist/wallet.d.ts +0 -74
  133. package/dist/wallet.mjs +0 -156
  134. package/dist/wallet.mjs.map +0 -1
  135. /package/dist/browser/_virtual/{_@oxc-project_runtime@0.151.0 → _@oxc-project_runtime@0.153.0}/helpers/esm/defineProperty.js +0 -0
  136. /package/dist/browser/_virtual/{_@oxc-project_runtime@0.151.0 → _@oxc-project_runtime@0.153.0}/helpers/esm/toPrimitive.js +0 -0
  137. /package/dist/browser/_virtual/{_@oxc-project_runtime@0.151.0 → _@oxc-project_runtime@0.153.0}/helpers/esm/toPropertyKey.js +0 -0
  138. /package/dist/browser/_virtual/{_@oxc-project_runtime@0.151.0 → _@oxc-project_runtime@0.153.0}/helpers/esm/typeof.js +0 -0
@@ -0,0 +1,2 @@
1
+ import{GSwapSDKError as e}from"./gswap_sdk_error.js";import{asEntryRecord as t,entryOk as n,entryRejected as r,readEntries as i}from"../utils/lenient.js";var a=class{constructor(e,t,n={}){this.dexBackendBaseUrl=e,this.httpClient=t,this.options=n}async getSeasons(e){let t=await this.httpClient.sendGetRequest(this.dexBackendBaseUrl,`/leaderboard`,`/seasons`,void 0,o(e));return i(`seasons`,t,l,e?.onInvalidEntry??this.options.onInvalidEntry)}async getCurrentSeason(e){let t=(e?.now??/* @__PURE__ */ new Date).getTime();return(await this.getSeasons(e)).find(e=>e.start.getTime()<=t&&t<=e.end.getTime())??null}async getLeaderboard(n,r){if(typeof n!=`number`||!Number.isSafeInteger(n)||n<1)throw new e(`INVALID_SEASON_ID`,{value:String(n)});let a=await this.httpClient.sendGetRequest(this.dexBackendBaseUrl,`/leaderboard`,``,{seasonId:String(n)},o(r)),s=t(a)?.leaderboard;if(!Array.isArray(s))throw new e(`INVALID_CHAIN_RESPONSE`,{resource:`leaderboard`},{body:a,write:!1});return{seasonId:n,entries:i(`leaderboard`,s,p,r?.onInvalidEntry??this.options.onInvalidEntry)}}async getCurrentLeaderboard(e){let t=await this.getCurrentSeason(e);return t===null?null:this.getLeaderboard(t.id,e)}};function o(e){return e?.signal===void 0?void 0:{signal:e.signal}}function s(e){return typeof e==`number`&&Number.isSafeInteger(e)&&e>=1?e:void 0}function c(e){if(typeof e!=`string`)return;let t=new Date(e);return Number.isNaN(t.getTime())?void 0:t}function l(e){let i=t(e);if(i===void 0)return r(`not an object`);let a=s(i.id);if(a===void 0)return r(`id is not an integer of at least 1`);let o=i.name;if(typeof o!=`string`)return r(`name is not a string`);let l=c(i.start),u=c(i.end);if(l===void 0||u===void 0)return r(`start or end is not a date`);if(l.getTime()>u.getTime())return r(`start is after end`);let d=i.rules_id;return typeof d!=`number`||!Number.isSafeInteger(d)||d<0?r(`rules_id is not an integer`):n({id:a,name:o,start:l,end:u,rulesId:d})}const u=/^\d+(?:\.\d+)?$/u;function d(e){let t=typeof e==`number`?String(e):e;return typeof t==`string`&&u.test(t)?t:void 0}function f(e){let n=t(e);if(n===void 0)return;let{name:r,type:i,order:a}=n;if(typeof r==`string`&&typeof i==`string`&&typeof a==`number`&&Number.isFinite(a))return{name:r,type:i,order:a}}function p(e){let i=t(e);if(i===void 0)return r(`not an object`);let a=i.wallet;if(typeof a!=`string`||a===``)return r(`wallet is not a string`);let o=s(i.rank);if(o===void 0)return r(`rank is not an integer of at least 1`);let c=i.total_xp,l=c==null?null:d(c);if(l===void 0)return r(`total_xp is not a decimal`);let u=d(i.distribution_percent),p=d(i.liquidity_xp),m=d(i.trading_xp);if(u===void 0||p===void 0||m===void 0)return r(`distribution_percent, liquidity_xp or trading_xp is not a decimal`);let h=i.mastery_titles;if(!Array.isArray(h))return r(`mastery_titles is not a list`);let g=[];for(let e of h){let t=f(e);if(t===void 0)return r(`a mastery title is not { name, type, order }`);g.push(t)}return n({wallet:a,rank:o,totalXp:l,distributionPercent:u,liquidityXp:p,tradingXp:m,masteryTitles:g})}export{a as Leaderboard};
2
+ //# sourceMappingURL=leaderboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"leaderboard.js","names":["Leaderboard","dexBackendBaseUrl","httpClient","options","response","signalOption","readEntries","parseSeason","now","season","seasonId","GSwapSDKError","rows","asEntryRecord","\"leaderboard\"","parseEntry","positiveInteger","value","isoDate","date","record","entry","entryRejected","id","\"id\"","name","\"name\"","start","\"start\"","end","\"end\"","rulesId","\"rules_id\"","entryOk","DECIMAL","decimalOf","text","parseTitle","type","order","wallet","\"wallet\"","rank","\"rank\"","rawTotal","\"total_xp\"","totalXp","distributionPercent","\"distribution_percent\"","liquidityXp","\"liquidity_xp\"","tradingXp","\"trading_xp\"","rawTitles","\"mastery_titles\"","masteryTitles","raw","title"],"sources":["../../../src/classes/leaderboard.ts"],"sourcesContent":["import type {\n LeaderboardEntry,\n LeaderboardSeason,\n MasteryTitle,\n SeasonLeaderboard,\n} from '../types/market.js';\nimport {\n asEntryRecord,\n entryOk,\n entryRejected,\n readEntries,\n type EntryParse,\n type InvalidEntryHandler,\n} from '../utils/lenient.js';\nimport { GSwapSDKError } from './gswap_sdk_error.js';\nimport type { HttpClient } from './http_client.js';\n\n/** Options of every leaderboard read. */\nexport interface LeaderboardReadOptions {\n signal?: AbortSignal | undefined;\n /** Overrides the service-level handler for this call. */\n onInvalidEntry?: InvalidEntryHandler | undefined;\n}\n\n/** Options of the reads that pick the season running now. */\nexport interface CurrentSeasonOptions extends LeaderboardReadOptions {\n /** The instant \"now\" is (default: the current time). */\n now?: Date | undefined;\n}\n\n/**\n * The swap backend's trading-competition reads: seasons (`GET /leaderboard/seasons`) and a\n * season's rankings (`GET /leaderboard?seasonId=`). Public reads: no signer, no wallet. A row that\n * is not usable is skipped and reported to `onInvalidEntry`; an answer that is not the expected\n * list is `INVALID_CHAIN_RESPONSE`.\n */\nexport class Leaderboard {\n /**\n * Creates a leaderboard service. `options.onInvalidEntry` is told about every season or entry\n * row that was skipped as unusable.\n *\n * @example\n * ```ts\n * const leaderboard = new Leaderboard('https://dex-backend-prod1.defi.gala.com', httpClient);\n * ```\n */\n constructor(\n private readonly dexBackendBaseUrl: string,\n private readonly httpClient: HttpClient,\n private readonly options: { onInvalidEntry?: InvalidEntryHandler | undefined } = {},\n ) {}\n\n /**\n * Every active season, in the backend's order (newest first).\n *\n * @example\n * ```ts\n * const seasons = await gSwap.leaderboard.getSeasons();\n * console.log(seasons.map((season) => season.name));\n * ```\n */\n public async getSeasons(options?: LeaderboardReadOptions): Promise<LeaderboardSeason[]> {\n const response = await this.httpClient.sendGetRequest<unknown>(\n this.dexBackendBaseUrl,\n '/leaderboard',\n '/seasons',\n undefined,\n signalOption(options),\n );\n return readEntries(\n 'seasons',\n response,\n parseSeason,\n options?.onInvalidEntry ?? this.options.onInvalidEntry,\n );\n }\n\n /**\n * The season running at `options.now` (default: now; `start` and `end` both count as inside),\n * or `null` between seasons. When seasons overlap, the first in the backend's order.\n *\n * @example\n * ```ts\n * const season = await gSwap.leaderboard.getCurrentSeason();\n * console.log(season === null ? 'no season running' : season.name);\n * ```\n */\n public async getCurrentSeason(options?: CurrentSeasonOptions): Promise<LeaderboardSeason | null> {\n const now = (options?.now ?? new Date()).getTime();\n const seasons = await this.getSeasons(options);\n return (\n seasons.find((season) => season.start.getTime() <= now && now <= season.end.getTime()) ?? null\n );\n }\n\n /**\n * A season's rankings (the backend's top 100 wallets). `seasonId` must be an integer of at\n * least 1 (`INVALID_SEASON_ID` before anything is sent); a season with no rankings, or none at\n * all, is an empty list.\n *\n * @example\n * ```ts\n * const { entries } = await gSwap.leaderboard.getLeaderboard(1);\n * console.log(entries[0]?.wallet, entries[0]?.totalXp);\n * ```\n */\n public async getLeaderboard(\n seasonId: number,\n options?: LeaderboardReadOptions,\n ): Promise<SeasonLeaderboard> {\n if (typeof seasonId !== 'number' || !Number.isSafeInteger(seasonId) || seasonId < 1) {\n throw new GSwapSDKError('INVALID_SEASON_ID', { value: String(seasonId) });\n }\n const response = await this.httpClient.sendGetRequest<unknown>(\n this.dexBackendBaseUrl,\n '/leaderboard',\n '',\n { seasonId: String(seasonId) },\n signalOption(options),\n );\n const rows = asEntryRecord(response)?.['leaderboard'];\n if (!Array.isArray(rows)) {\n throw new GSwapSDKError(\n 'INVALID_CHAIN_RESPONSE',\n { resource: 'leaderboard' },\n { body: response, write: false },\n );\n }\n const entries = readEntries(\n 'leaderboard',\n rows,\n parseEntry,\n options?.onInvalidEntry ?? this.options.onInvalidEntry,\n );\n return { seasonId, entries };\n }\n\n /**\n * The rankings of the season running at `options.now` (see\n * {@link Leaderboard.getCurrentSeason}), or `null` between seasons.\n *\n * @example\n * ```ts\n * const current = await gSwap.leaderboard.getCurrentLeaderboard();\n * if (current !== null) console.log(current.seasonId, current.entries.length);\n * ```\n */\n public async getCurrentLeaderboard(\n options?: CurrentSeasonOptions,\n ): Promise<SeasonLeaderboard | null> {\n const season = await this.getCurrentSeason(options);\n return season === null ? null : this.getLeaderboard(season.id, options);\n }\n}\n\nfunction signalOption(\n options: LeaderboardReadOptions | undefined,\n): { signal: AbortSignal } | undefined {\n return options?.signal === undefined ? undefined : { signal: options.signal };\n}\n\nfunction positiveInteger(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isSafeInteger(value) && value >= 1 ? value : undefined;\n}\n\nfunction isoDate(value: unknown): Date | undefined {\n if (typeof value !== 'string') return undefined;\n const date = new Date(value);\n return Number.isNaN(date.getTime()) ? undefined : date;\n}\n\nfunction parseSeason(entry: unknown): EntryParse<LeaderboardSeason> {\n const record = asEntryRecord(entry);\n if (record === undefined) return entryRejected('not an object');\n const id = positiveInteger(record['id']);\n if (id === undefined) return entryRejected('id is not an integer of at least 1');\n const name = record['name'];\n if (typeof name !== 'string') return entryRejected('name is not a string');\n const start = isoDate(record['start']);\n const end = isoDate(record['end']);\n if (start === undefined || end === undefined) return entryRejected('start or end is not a date');\n if (start.getTime() > end.getTime()) return entryRejected('start is after end');\n const rulesId = record['rules_id'];\n if (typeof rulesId !== 'number' || !Number.isSafeInteger(rulesId) || rulesId < 0) {\n return entryRejected('rules_id is not an integer');\n }\n return entryOk({ id, name, start, end, rulesId });\n}\n\n/** A non-negative decimal, as the backend's DECIMAL columns are serialized (or a finite number). */\nconst DECIMAL = /^\\d+(?:\\.\\d+)?$/u;\n\nfunction decimalOf(value: unknown): string | undefined {\n // A number is accepted only when it prints as a plain decimal (never `1e+21`).\n const text = typeof value === 'number' ? String(value) : value;\n return typeof text === 'string' && DECIMAL.test(text) ? text : undefined;\n}\n\nfunction parseTitle(value: unknown): MasteryTitle | undefined {\n const record = asEntryRecord(value);\n if (record === undefined) return undefined;\n const { name, type, order } = record;\n if (typeof name !== 'string' || typeof type !== 'string') return undefined;\n if (typeof order !== 'number' || !Number.isFinite(order)) return undefined;\n return { name, type, order };\n}\n\nfunction parseEntry(entry: unknown): EntryParse<LeaderboardEntry> {\n const record = asEntryRecord(entry);\n if (record === undefined) return entryRejected('not an object');\n const wallet = record['wallet'];\n if (typeof wallet !== 'string' || wallet === '') return entryRejected('wallet is not a string');\n const rank = positiveInteger(record['rank']);\n if (rank === undefined) return entryRejected('rank is not an integer of at least 1');\n // The backend leaves total_xp out for a ranked wallet it has no XP breakdown for.\n const rawTotal = record['total_xp'];\n const totalXp = rawTotal === undefined || rawTotal === null ? null : decimalOf(rawTotal);\n if (totalXp === undefined) return entryRejected('total_xp is not a decimal');\n const distributionPercent = decimalOf(record['distribution_percent']);\n const liquidityXp = decimalOf(record['liquidity_xp']);\n const tradingXp = decimalOf(record['trading_xp']);\n if (distributionPercent === undefined || liquidityXp === undefined || tradingXp === undefined) {\n return entryRejected('distribution_percent, liquidity_xp or trading_xp is not a decimal');\n }\n const rawTitles = record['mastery_titles'];\n if (!Array.isArray(rawTitles)) return entryRejected('mastery_titles is not a list');\n const masteryTitles: MasteryTitle[] = [];\n for (const raw of rawTitles as unknown[]) {\n const title = parseTitle(raw);\n if (title === undefined) return entryRejected('a mastery title is not { name, type, order }');\n masteryTitles.push(title);\n }\n return entryOk({\n wallet,\n rank,\n totalXp,\n distributionPercent,\n liquidityXp,\n tradingXp,\n masteryTitles,\n });\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{GSwapSDKError as e}from"./gswap_sdk_error.js";import{asEntryRecord as t,entryOk as n,entryRejected as r,readEntries as i}from"../utils/lenient.js";import{PRICE_SERIES_TIMEFRAMES as a}from"../types/market.js";import{compositeKeyOf as o,isTokenClassKey as s}from"@gala-chain/gala-core-sdk";const c=/^[A-Za-z0-9._$+/:|-]{1,120}$/u;var l=class{constructor(e,t,n={}){this.dexBackendBaseUrl=e,this.httpClient=t,this.options=n}async getPrices(n,r){if(!Array.isArray(n)||n.length<1||n.length>500)throw new e(`INVALID_TOKEN_BATCH`,{method:`getPrices`,count:Array.isArray(n)?n.length:0,max:500});let i=n.map(f),a=/* @__PURE__ */ new Map;for(let n of d([...new Set(i)])){let i=await this.httpClient.sendGetRequest(this.dexBackendBaseUrl,`/market`,`/price`,{assetIds:n.join(`,`)},h(r)),o=t(i);if(o===void 0)throw new e(`INVALID_CHAIN_RESPONSE`,{resource:`market prices`},{body:i,write:!1});for(let e of n)Object.hasOwn(o,e)&&a.set(e,o[e])}let o=r?.onInvalidEntry??this.options.onInvalidEntry;return i.map((e,t)=>{if(!a.has(e))return;let n=a.get(e),r=v(e,n);if(r.ok)return r.value;try{o?.({source:`prices`,index:t,entry:n,reason:r.reason})}catch{}})}async getPrice(e,t){let[n]=await this.getPrices([e],t);return n}async getPriceSeries(n,r,o){if(!a.includes(r))throw new e(`INVALID_TIMEFRAME`,{value:String(r),allowed:a.join(`, `)});let s=f(n),c=await this.httpClient.sendGetRequest(this.dexBackendBaseUrl,`/market`,`/price-series`,{token:s,timeframe:r},h(o)),l=t(c),u=l===void 0?void 0:g(l.currentPrice),d=l===void 0?void 0:g(l.change24h);if(l===void 0||!Array.isArray(l.prices)||u===void 0||d===void 0)throw new e(`INVALID_CHAIN_RESPONSE`,{resource:`price series`},{body:c,write:!1});return{token:s,timeframe:r,prices:i(`priceSeries`,l.prices,y,o?.onInvalidEntry??this.options.onInvalidEntry),currentPrice:u,change24h:d}}async getVolumeSummary(n){let r=await this.httpClient.sendGetRequest(this.dexBackendBaseUrl,`/v2/trade`,`/volume`,void 0,h(n)),i=x(t(t(r)?.data));if(i===void 0)throw new e(`INVALID_CHAIN_RESPONSE`,{resource:`volume summary`},{body:r,write:!1});return i}};const u=8e3;function d(e){let t=[],n=[],r=0;for(let i of e){let e=encodeURIComponent(i).length+3;n.length>0&&(r+e>8e3||n.length===500)&&(t.push(n),n=[],r=0),n.push(i),r+=e}return n.length>0&&t.push(n),t}function f(t){if(typeof t!=`string`){if(!s(t))throw new e(`INVALID_TOKEN_CLASS_KEY`,{tokenClassKey:m(t)});return p(o(t))}return p(t)}function p(t){if(!c.test(t))throw new e(`INVALID_MARKET_TOKEN`,{token:t});return t}function m(e){try{return JSON.stringify(e)??String(e)}catch{return Object.prototype.toString.call(e)}}function h(e){return e?.signal===void 0?void 0:{signal:e.signal}}function g(e){return e===null?null:typeof e==`number`&&Number.isFinite(e)?e:void 0}function _(e){return typeof e==`number`&&Number.isFinite(e)?e:void 0}function v(e,i){let a=t(i);if(a===void 0)return r(`not an object`);let o=g(a.price);if(o===void 0||o!==null&&o<0)return r(`price is not a non-negative number or null`);let s=g(a.change24h);if(s===void 0)return r(`change24h is not a number or null`);let c=a.listed;return typeof c==`boolean`?n({token:e,price:o,change24h:s,listed:c}):r(`listed is not a boolean`)}function y(e){let i=t(e),a=_(i?.time),o=_(i?.price);return a===void 0||!Number.isInteger(a)||a<0?r(`time is not unix seconds`):o===void 0||o<0?r(`price is not a non-negative number`):n({time:a,price:o})}const b=[`tvl`,`volume1d`,`volume7d`,`volume30d`,`trades1d`,`volume1dDelta`,`volume7dDelta`,`volume30dDelta`];function x(e){if(e===void 0)return;let t={};for(let n of b){let r=_(e[n]);if(r===void 0)return;t[n]=r}let n=t.trades1d;if(!(n===void 0||!Number.isInteger(n)||n<0))return t}export{u as MARKET_PRICE_QUERY_BYTES,l as Market,d as priceBatches};
2
+ //# sourceMappingURL=market.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"market.js","names":["MARKET_TOKEN_PATTERN","Market","dexBackendBaseUrl","httpClient","options","tokens","GSwapSDKError","ids","marketTokenOf","answered","batch","priceBatches","response","signalOption","answer","asEntryRecord","id","onInvalidEntry","entry","parsed","parseMarketPrice","price","token","PRICE_SERIES_TIMEFRAMES","timeframe","currentPrice","nullableFinite","\"currentPrice\"","change24h","\"change24h\"","\"prices\"","readEntries","parsePricePoint","summary","parseVolumeSummary","\"data\"","MARKET_PRICE_QUERY_BYTES","batches","bytes","size","isTokenClassKey","describeValue","checkedMarketToken","compositeKeyOf","value","finite","record","entryRejected","\"price\"","listed","\"listed\"","entryOk","time","\"time\"","VOLUME_FIELDS","data","field","trades"],"sources":["../../../src/classes/market.ts"],"sourcesContent":["import { compositeKeyOf, isTokenClassKey } from '@gala-chain/gala-core-sdk';\nimport {\n MARKET_PRICE_BATCH_LIMIT,\n PRICE_SERIES_TIMEFRAMES,\n type MarketPrice,\n type PriceSeries,\n type PriceSeriesPoint,\n type PriceSeriesTimeframe,\n type VolumeSummary,\n} from '../types/market.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 { GSwapSDKError } from './gswap_sdk_error.js';\nimport type { HttpClient } from './http_client.js';\n\n/**\n * The ids `GET /market/price` and `GET /market/price-series` accept (the backend's own allowlist):\n * a symbol, or a class key in the `$` or `|` form. A comma would split the price request.\n */\nconst MARKET_TOKEN_PATTERN = /^[A-Za-z0-9._$+/:|-]{1,120}$/u;\n\n/** Options of every market read. */\nexport interface MarketReadOptions {\n signal?: AbortSignal | undefined;\n}\n\n/** Options of {@link Market.getPrices} and {@link Market.getPriceSeries}. */\nexport interface MarketListOptions extends MarketReadOptions {\n /** Overrides the service-level handler for this call. */\n onInvalidEntry?: InvalidEntryHandler | undefined;\n}\n\n/**\n * Market data from the swap backend: USD prices (`GET /market/price`), price series\n * (`GET /market/price-series`) and the platform volume totals (`GET /v2/trade/volume`). Public\n * reads: no signer, no wallet.\n *\n * A token is named by a symbol (`GALA`) or a class key (`GALA$Unit$none$none`,\n * `GALA|Unit|none|none` or the object). The backend answers each token under the exact id it was\n * asked for, so the answer is read back under that id: a token the backend has no entry for is\n * `undefined`, never another token's price.\n */\nexport class Market {\n /**\n * Creates a market service. `options.onInvalidEntry` is told about every price or price point\n * that was skipped as unusable.\n *\n * @example\n * ```ts\n * const market = new Market('https://dex-backend-prod1.defi.gala.com', httpClient);\n * ```\n */\n constructor(\n private readonly dexBackendBaseUrl: string,\n private readonly httpClient: HttpClient,\n private readonly options: { onInvalidEntry?: InvalidEntryHandler | undefined } = {},\n ) {}\n\n /**\n * USD prices of 1 to 500 tokens, in the order asked (a token named twice is asked for once).\n * The ids go in as many requests as their length needs (see {@link MARKET_PRICE_QUERY_BYTES}),\n * one after another; any request failing fails the call. A token the backend did not answer\n * for is `undefined`; one it answered for unusably is `undefined` too, and reported to\n * `onInvalidEntry`. An answer that is not an object is `INVALID_CHAIN_RESPONSE`.\n *\n * @example\n * ```ts\n * const [gala, gusdc] = await gSwap.market.getPrices(['GALA$Unit$none$none', 'GUSDC$Unit$none$none']);\n * console.log(gala?.price, gusdc?.price);\n * ```\n */\n public async getPrices(\n tokens: readonly TokenRef[],\n options?: MarketListOptions,\n ): Promise<Array<MarketPrice | undefined>> {\n if (!Array.isArray(tokens) || tokens.length < 1 || tokens.length > MARKET_PRICE_BATCH_LIMIT) {\n throw new GSwapSDKError('INVALID_TOKEN_BATCH', {\n method: 'getPrices',\n count: Array.isArray(tokens) ? tokens.length : 0,\n max: MARKET_PRICE_BATCH_LIMIT,\n });\n }\n const ids = tokens.map(marketTokenOf);\n // One request per batch, one after another (the endpoint allows 60 a minute): each batch's\n // answer is read under the ids that batch asked for, never another batch's.\n const answered = new Map<string, unknown>();\n for (const batch of priceBatches([...new Set(ids)])) {\n const response = await this.httpClient.sendGetRequest<unknown>(\n this.dexBackendBaseUrl,\n '/market',\n '/price',\n { assetIds: batch.join(',') },\n signalOption(options),\n );\n const answer = asEntryRecord(response);\n if (answer === undefined) {\n throw new GSwapSDKError(\n 'INVALID_CHAIN_RESPONSE',\n { resource: 'market prices' },\n { body: response, write: false },\n );\n }\n for (const id of batch) {\n if (Object.hasOwn(answer, id)) answered.set(id, answer[id]);\n }\n }\n const onInvalidEntry = options?.onInvalidEntry ?? this.options.onInvalidEntry;\n return ids.map((id, index) => {\n if (!answered.has(id)) return undefined;\n const entry = answered.get(id);\n const parsed = parseMarketPrice(id, entry);\n if (parsed.ok) return parsed.value;\n try {\n onInvalidEntry?.({ source: 'prices', index, entry, reason: parsed.reason });\n } catch {\n // A diagnostics hook must not turn a lenient read into a failed one.\n }\n return undefined;\n });\n }\n\n /**\n * The USD price of one token, or `undefined` when the backend has no entry for it (see\n * {@link Market.getPrices}).\n *\n * @example\n * ```ts\n * const gala = await gSwap.market.getPrice('GALA$Unit$none$none');\n * if (gala?.price != null) console.log(`GALA: $${gala.price}`);\n * ```\n */\n public async getPrice(\n token: TokenRef,\n options?: MarketListOptions,\n ): Promise<MarketPrice | undefined> {\n const [price] = await this.getPrices([token], options);\n return price;\n }\n\n /**\n * A token's USD price line for one window (1D, 1W, 1M, 1Y or ALL; any other is\n * `INVALID_TIMEFRAME` before anything is sent). The whole series in one answer, oldest first; a\n * token the backend has no prices for is an empty series. An unusable point is skipped and\n * reported to `onInvalidEntry`; an answer that is not a series is `INVALID_CHAIN_RESPONSE`.\n *\n * @example\n * ```ts\n * const series = await gSwap.market.getPriceSeries('GALA$Unit$none$none', '1W');\n * console.log(series.prices.length, series.currentPrice);\n * ```\n */\n public async getPriceSeries(\n token: TokenRef,\n timeframe: PriceSeriesTimeframe,\n options?: MarketListOptions,\n ): Promise<PriceSeries> {\n if (!(PRICE_SERIES_TIMEFRAMES as readonly unknown[]).includes(timeframe)) {\n throw new GSwapSDKError('INVALID_TIMEFRAME', {\n value: String(timeframe),\n allowed: PRICE_SERIES_TIMEFRAMES.join(', '),\n });\n }\n const id = marketTokenOf(token);\n const response = await this.httpClient.sendGetRequest<unknown>(\n this.dexBackendBaseUrl,\n '/market',\n '/price-series',\n { token: id, timeframe },\n signalOption(options),\n );\n const answer = asEntryRecord(response);\n const currentPrice = answer === undefined ? undefined : nullableFinite(answer['currentPrice']);\n const change24h = answer === undefined ? undefined : nullableFinite(answer['change24h']);\n if (\n answer === undefined ||\n !Array.isArray(answer['prices']) ||\n currentPrice === undefined ||\n change24h === undefined\n ) {\n throw new GSwapSDKError(\n 'INVALID_CHAIN_RESPONSE',\n { resource: 'price series' },\n { body: response, write: false },\n );\n }\n const prices = readEntries(\n 'priceSeries',\n answer['prices'],\n parsePricePoint,\n options?.onInvalidEntry ?? this.options.onInvalidEntry,\n );\n return { token: id, timeframe, prices, currentPrice, change24h };\n }\n\n /**\n * TVL, volume and trade totals across every current-contract pool. Every field must be a finite\n * number, or the answer is `INVALID_CHAIN_RESPONSE`.\n *\n * @example\n * ```ts\n * const volume = await gSwap.market.getVolumeSummary();\n * console.log(`24h: $${volume.volume1d.toFixed(0)} (${(volume.volume1dDelta * 100).toFixed(1)} %)`);\n * ```\n */\n public async getVolumeSummary(options?: MarketReadOptions): Promise<VolumeSummary> {\n const response = await this.httpClient.sendGetRequest<unknown>(\n this.dexBackendBaseUrl,\n '/v2/trade',\n '/volume',\n undefined,\n signalOption(options),\n );\n const summary = parseVolumeSummary(asEntryRecord(asEntryRecord(response)?.['data']));\n if (summary === undefined) {\n throw new GSwapSDKError(\n 'INVALID_CHAIN_RESPONSE',\n { resource: 'volume summary' },\n { body: response, write: false },\n );\n }\n return summary;\n }\n}\n\n/**\n * The most query bytes one `GET /market/price` carries. The backend refuses a request line over\n * 16 KB (Node's default header limit: 300 launchpad keys, 17.7 KB encoded, answer HTTP 431,\n * recorded on prod 2026-10-13), so a long list is split well under it (about 135 launchpad keys\n * per request, 270 short ones).\n */\nexport const MARKET_PRICE_QUERY_BYTES = 8_000;\n\n/**\n * The ids in request-sized batches: at most {@link MARKET_PRICE_BATCH_LIMIT} ids and\n * {@link MARKET_PRICE_QUERY_BYTES} of encoded `assetIds` each (the standard encoding's size, the\n * larger of the two, with a `%2C` per id).\n */\nexport function priceBatches(ids: readonly string[]): string[][] {\n const batches: string[][] = [];\n let batch: string[] = [];\n let bytes = 0;\n for (const id of ids) {\n const size = encodeURIComponent(id).length + 3;\n if (\n batch.length > 0 &&\n (bytes + size > MARKET_PRICE_QUERY_BYTES || batch.length === MARKET_PRICE_BATCH_LIMIT)\n ) {\n batches.push(batch);\n batch = [];\n bytes = 0;\n }\n batch.push(id);\n bytes += size;\n }\n if (batch.length > 0) batches.push(batch);\n return batches;\n}\n\n/**\n * The id a market read sends for a token: a string as given (a symbol or a class key in either\n * form), an object key in its `$` form. Anything the backend's allowlist refuses is refused here.\n */\nfunction marketTokenOf(token: TokenRef): string {\n if (typeof token !== 'string') {\n if (!isTokenClassKey(token)) {\n throw new GSwapSDKError('INVALID_TOKEN_CLASS_KEY', {\n tokenClassKey: describeValue(token),\n });\n }\n return checkedMarketToken(compositeKeyOf(token));\n }\n return checkedMarketToken(token);\n}\n\nfunction checkedMarketToken(id: string): string {\n if (!MARKET_TOKEN_PATTERN.test(id)) {\n throw new GSwapSDKError('INVALID_MARKET_TOKEN', { token: id });\n }\n return id;\n}\n\nfunction describeValue(value: unknown): string {\n try {\n return JSON.stringify(value) ?? String(value);\n } catch {\n return Object.prototype.toString.call(value);\n }\n}\n\nfunction signalOption(options: MarketReadOptions | undefined): { signal: AbortSignal } | undefined {\n return options?.signal === undefined ? undefined : { signal: options.signal };\n}\n\n/** A finite number, `null` for `null`, `undefined` for anything else (an unusable value). */\nfunction nullableFinite(value: unknown): number | null | undefined {\n if (value === null) return null;\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\nfunction finite(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\nfunction parseMarketPrice(token: string, entry: unknown): EntryParse<MarketPrice> {\n const record = asEntryRecord(entry);\n if (record === undefined) return entryRejected('not an object');\n const price = nullableFinite(record['price']);\n if (price === undefined || (price !== null && price < 0)) {\n return entryRejected('price is not a non-negative number or null');\n }\n const change24h = nullableFinite(record['change24h']);\n if (change24h === undefined) return entryRejected('change24h is not a number or null');\n const listed = record['listed'];\n if (typeof listed !== 'boolean') return entryRejected('listed is not a boolean');\n return entryOk({ token, price, change24h, listed });\n}\n\nfunction parsePricePoint(entry: unknown): EntryParse<PriceSeriesPoint> {\n const record = asEntryRecord(entry);\n const time = finite(record?.['time']);\n const price = finite(record?.['price']);\n if (time === undefined || !Number.isInteger(time) || time < 0) {\n return entryRejected('time is not unix seconds');\n }\n if (price === undefined || price < 0) return entryRejected('price is not a non-negative number');\n return entryOk({ time, price });\n}\n\nconst VOLUME_FIELDS = [\n 'tvl',\n 'volume1d',\n 'volume7d',\n 'volume30d',\n 'trades1d',\n 'volume1dDelta',\n 'volume7dDelta',\n 'volume30dDelta',\n] as const satisfies ReadonlyArray<keyof VolumeSummary>;\n\nfunction parseVolumeSummary(data: Record<string, unknown> | undefined): VolumeSummary | undefined {\n if (data === undefined) return undefined;\n const summary: Partial<Record<keyof VolumeSummary, number>> = {};\n for (const field of VOLUME_FIELDS) {\n const value = finite(data[field]);\n if (value === undefined) return undefined;\n summary[field] = value;\n }\n const trades = summary.trades1d;\n if (trades === undefined || !Number.isInteger(trades) || trades < 0) return undefined;\n return summary as VolumeSummary;\n}\n"],"mappings":"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"}
@@ -1 +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
+ {"version":3,"file":"pending_swap_fate.js","names":["PENDING_SWAP_FATES","FATE_OF_OUTCOME","pendingSwapFateOfOutcome","outcome","pendingSwapFateFor","classifyFailure","error"],"sources":["../../../src/classes/pending_swap_fate.ts"],"sourcesContent":["import { classifyFailure } from '../utils/failure_classification.js';\nimport type { FailureOutcome } from '@gala-chain/gala-core-sdk';\n\n/**\n * What to do with a pending swap after confirming it failed (the answer of\n * {@link pendingSwapFateFor}).\n *\n * - `forget`: the chain REFUSED the trade, so it did not happen. Drop it AND tell the user.\n * - `forget_quietly`: the confirmation can never succeed (the key is refused: `invalid_input`).\n * Drop it without claiming anything about the trade.\n * - `keep`: the trade may still have been applied (a timeout, a broken connection, a gateway\n * hiccup, anything not proven). Leave it pending and look again later.\n *\n * This module holds the decision and nothing of the store, so an app shell that only needs the\n * fate pays for `classifyFailure` and this table, not for `PendingSwapStore`.\n */\nexport const PENDING_SWAP_FATES = ['forget', 'forget_quietly', 'keep'] as const;\n\n/** One member of {@link PENDING_SWAP_FATES}. */\nexport type PendingSwapFate = (typeof PENDING_SWAP_FATES)[number];\n\n/**\n * The fate of a pending swap for each failure OUTCOME. Only a chain rejection proves the trade did\n * not happen; an input that can never be confirmed is dropped quietly; every other outcome leaves\n * the trade possibly applied. Typed over `FailureOutcome`, so a new outcome does not compile until\n * it is decided here.\n */\nconst FATE_OF_OUTCOME = {\n rejected: 'forget',\n invalid_input: 'forget_quietly',\n unknown: 'keep',\n not_queued: 'keep',\n not_sent: 'keep',\n transient: 'keep',\n} as const satisfies Record<FailureOutcome, PendingSwapFate>;\n\n/**\n * {@link pendingSwapFateFor} from an outcome the app already holds (for example the\n * `classifyFailure(error).outcome` it stored on its own failure wrapper). An outcome this SDK\n * version does not know (a newer SDK at run time) is `keep`: never forget what may have happened.\n */\nexport function pendingSwapFateOfOutcome(outcome: FailureOutcome): PendingSwapFate {\n return Object.hasOwn(FATE_OF_OUTCOME, outcome) ? FATE_OF_OUTCOME[outcome] : 'keep';\n}\n\n/**\n * The fate of a queued swap whose confirmation (`confirmAppliedTrade`) just failed with `error`,\n * from the SDK's classification alone: `forget` for a chain rejection (`rejected`), `forget_quietly`\n * for a key that can never be confirmed (`invalid_input`), `keep` for every other outcome\n * (`unknown`, `not_queued`, `not_sent`, `transient`) and for any value the SDK cannot judge. It\n * accepts an SDK error or a plain-object view of one (see `FailureView`). It replaces the wallet's\n * `pendingSwapActionFor` and frontend_v2's `confirmationStanceOf`.\n *\n * @example\n * ```ts\n * try {\n * await confirmAppliedTrade(client, swap);\n * } catch (error) {\n * if ((await store.settle(swap.uniqueKey, error)) === 'forget') tellTheUserItFailed();\n * }\n * ```\n */\nexport function pendingSwapFateFor(error: unknown): PendingSwapFate {\n return pendingSwapFateOfOutcome(classifyFailure(error, { afterQueued: true }).outcome);\n}\n"],"mappings":"qEAgBA,MAAaA,EAAqB,CAAC,SAAU,iBAAkB,MAAM,EAW/DC,EAAkB,CACtB,SAAU,SACV,cAAe,iBACf,QAAS,OACT,WAAY,OACZ,SAAU,OACV,UAAW,MACb,EAOA,SAAgBC,EAAyB,EAA0C,CACjF,OAAO,OAAO,OAAOD,EAAiBE,CAAO,EAAIF,EAAgBE,GAAW,MAC9E,CAmBA,SAAgBC,EAAmB,EAAiC,CAClE,OAAOF,EAAyBG,EAAgBC,EAAO,CAAE,YAAa,EAAK,CAAC,CAAC,CAAC,OAAO,CACvF"}
@@ -1,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{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};
1
+ import e from"../_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/defineProperty.js";import{pendingSwapFateFor as t}from"./pending_swap_fate.js";import{isValidUniqueKey as n}from"@gala-chain/gala-core-sdk";const r=`pendingSwaps.v1`,i=6048e5,a=[`expired`,`invalid_unique_key`,`chain_rejected`,`unconfirmable`];var o=class{constructor(t,n={}){this.storage=t,e(this,`storageKey`,void 0),e(this,`darkFlagField`,void 0),e(this,`insertAt`,void 0),e(this,`listOrder`,void 0),e(this,`now`,void 0),e(this,`onPrune`,void 0),e(this,`queue`,Promise.resolve()),e(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(e){return n(e.uniqueKey)?this.enqueue(async t=>{let n=(await this.read(t)).filter(t=>t.uniqueKey!==e.uniqueKey).map(this.toStored),r=this.toStored(e);await t.set(this.storageKey,this.insertAt===`end`?[...n,r]:[r,...n])}):(this.report([{swap:e,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,n){let r=t(n);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(e,t){return n(e.uniqueKey)?t-e.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 { 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
+ {"version":3,"file":"pending_swaps.js","names":["_defineProperty","PENDING_SWAPS_STORAGE_KEY","PENDING_SWAP_EXPIRY_MS","PENDING_SWAP_PRUNE_REASONS","PendingSwapStore","storage","swap","options","isValidUniqueKey","others","entry","stored","current","uniqueKey","fate","pendingSwapFateFor","error","removed","now","pruned","kept","reason","mine","wallet","right","left","run","operation","value","entries","item","flag","requestTransactionId","token0Symbol","token1Symbol","createdAt"],"sources":["../../../src/classes/pending_swaps.ts"],"sourcesContent":["import { isValidUniqueKey } from '@gala-chain/gala-core-sdk';\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 e from"../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/defineProperty.js";import{GSwapSDKError as t}from"./gswap_sdk_error.js";import{validateFee as n}from"../utils/validation.js";import{HttpClient as r}from"./http_client.js";import{asEntryRecord as i,entryOk as a,entryRejected as o,finiteNumberOf as s,readEntries as c}from"../utils/lenient.js";import{orderSymbols as l,readOrderedSymbols as u}from"../utils/ordering.js";import{queryTokenOf as d,rawTokenOf as f,tradingSymbolOf as p}from"./symbols.js";var m=class{constructor(t,n,i={}){this.gateway=t,this.symbols=n,this.options=i,e(this,`http`,void 0),this.http=new r(t.httpRequestor,t.requestTimeoutMs??3e4,t.wire)}async getPools(e){let t=await this.http.sendGetRequest(this.gateway.dexBackendBaseUrl,`/v2/trade`,`/pools`,_(e),e?.signal===void 0?void 0:{signal:e.signal});return c(`pools`,t.data,x,e?.onInvalidEntry??this.options.onInvalidEntry)}async getPool(e,t,r){return n(r),this.getBackend(`/pool`,await this.backendParams(e,t,r))}async getSlot0(e,t,r){return n(r),this.getBackend(`/slot0`,await this.backendParams(e,t,r))}async getCompositePool(e,t,r){if(n(r),this.symbols.passthrough===!0)return this.getBackend(`/composite-pool`,{token0:f(e),token1:f(t),fee:String(r)});let i=await p(this.symbols,e),a=await p(this.symbols,t),o=u(l(i,a));return this.getBackend(`/composite-pool`,{token0:o.token0,token1:o.token1,fee:String(r)})}async backendParams(e,t,n){return{token0:await d(this.symbols,e),token1:await d(this.symbols,t),fee:String(n)}}async getBackend(e,n){try{return(await this.http.sendGetRequest(this.gateway.dexBackendBaseUrl,`/v2/trade`,e,n)).data}catch(e){throw e instanceof t?e:new t(`REQUEST_FAILED`,{operation:`pool`},{cause:e})}}};const h=[`tvl`,`volume1d`,`volume30d`],g=1e3;function _(e){if(e===void 0)return;let n={};if(e.search!==void 0){if(e.search.length>64)throw new t(`SEARCH_TOO_LONG`,{length:e.search.length,maxLength:64});n.search=e.search}if(e.sortBy!==void 0){if(!h.includes(e.sortBy))throw new t(`INVALID_SORT_BY`,{value:String(e.sortBy),allowed:h.join(`,`)});n.sortBy=e.sortBy}if(e.sortOrder!==void 0){if(e.sortOrder!==`asc`&&e.sortOrder!==`desc`)throw new t(`INVALID_SORT_ORDER`,{value:String(e.sortOrder)});n.sortOrder=e.sortOrder}if(e.page!==void 0){if(!Number.isInteger(e.page)||e.page<1)throw new t(`INVALID_PAGE`,{value:e.page});n.page=String(e.page)}if(e.limit!==void 0){if(!Number.isInteger(e.limit)||e.limit<1||e.limit>g)throw new t(`INVALID_LIMIT`,{value:e.limit,min:1,max:g});n.limit=String(e.limit)}return Object.keys(n).length===0?void 0:n}const v=[`poolName`,`token0CompositeKey`,`token1CompositeKey`,`token0Image`,`token1Image`,`creator`,`price`,`sqrtPrice`,`token0Tvl`,`token1Tvl`],y=[`tickSpacing`,`protocolFees`,`tradingFees`,`tick`,`volume1d`,`volume7d`,`volume30d`,`trades1d`,`fee24h`,`token0Price`,`token1Price`],b=[`fee`,`tvlUsd`,`apr1d`];function x(e){let t=i(e);if(t===void 0)return o(`the row is not an object`);for(let e of[`token0`,`token1`,`poolRef`])if(typeof t[e]!=`string`)return o(`${e} is not a string`);if(t.contractVersion!==void 0&&t.contractVersion!==`v2`)return o(`contractVersion is not v2`);let n={...t};for(let e of b){let r=t[e];if(r===void 0&&e!==`fee`)continue;let i=s(r);if(i===void 0)return o(`${e} is not a number`);n[e]=i}for(let e of v)if(t[e]!==void 0&&typeof t[e]!=`string`)return o(`${e} is not a string`);for(let e of y){let n=t[e];if(n!==void 0&&(typeof n!=`number`||!Number.isFinite(n)))return o(`${e} is not a number`)}return a(n)}export{m as Pools};
1
+ import e from"../_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/defineProperty.js";import{GSwapSDKError as t,isGSwapError as n}from"./gswap_sdk_error.js";import{validateFee as r}from"../utils/validation.js";import{HttpClient as i}from"./http_client.js";import{asEntryRecord as a,entryOk as o,entryRejected as s,finiteNumberOf as c,readEntries as l}from"../utils/lenient.js";import{orderSymbols as u,readOrderedSymbols as d}from"../utils/ordering.js";import{queryTokenOf as f,rawTokenOf as p,tradingSymbolOf as m}from"./symbols.js";var h=class{constructor(t,n,r={}){this.gateway=t,this.symbols=n,this.options=r,e(this,`http`,void 0),this.http=new i(t.httpRequestor,t.requestTimeoutMs??3e4,t.wire)}async getPools(e){let t=await this.http.sendGetRequest(this.gateway.dexBackendBaseUrl,`/v2/trade`,`/pools`,v(e),e?.signal===void 0?void 0:{signal:e.signal});return l(`pools`,t.data,S,e?.onInvalidEntry??this.options.onInvalidEntry)}watchNewPools(e,n={}){let r=n.intervalMs??3e4;if(typeof r!=`number`||!Number.isFinite(r)||r<1)throw new t(`INVALID_POLL_INTERVAL`,{value:String(r)});let i,a=!1,o=!1,s=e=>{try{n.onError?.(e)}catch{}},c=e=>(n.minTvl===void 0||(e.tvlUsd??0)>=n.minTvl)&&(n.tokens===void 0||n.tokens.includes(e.token0)||n.tokens.includes(e.token1)),l=async()=>{if(!(a||o)){o=!0;try{let t=await this.getPools();if(i===void 0){i=new Set(t.map(e=>e.poolRef));return}for(let n of t){if(a)return;if(!i.has(n.poolRef)&&(i.add(n.poolRef),c(n)))try{e(n)}catch(e){s(e)}}}catch(e){a||s(e)}finally{o=!1}}};l();let u=setInterval(()=>void l(),r);return()=>{a=!0,clearInterval(u)}}async getPool(e,t,n){return r(n),this.getBackend(`/pool`,await this.backendParams(e,t,n))}async getSlot0(e,t,n){return r(n),this.getBackend(`/slot0`,await this.backendParams(e,t,n))}async getCompositePool(e,t,n){if(r(n),this.symbols.passthrough===!0)return this.getBackend(`/composite-pool`,{token0:p(e),token1:p(t),fee:String(n)});let i=await m(this.symbols,e),a=await m(this.symbols,t),o=d(u(i,a));return this.getBackend(`/composite-pool`,{token0:o.token0,token1:o.token1,fee:String(n)})}async backendParams(e,t,n){return{token0:await f(this.symbols,e),token1:await f(this.symbols,t),fee:String(n)}}async getBackend(e,r){try{return(await this.http.sendGetRequest(this.gateway.dexBackendBaseUrl,`/v2/trade`,e,r)).data}catch(e){throw n(e)?e:new t(`REQUEST_FAILED`,{operation:`pool`},{cause:e,write:!1})}}};const g=[`tvl`,`volume1d`,`volume30d`],_=1e3;function v(e){if(e===void 0)return;let n={};if(e.search!==void 0){if(e.search.length>64)throw new t(`SEARCH_TOO_LONG`,{length:e.search.length,maxLength:64});n.search=e.search}if(e.sortBy!==void 0){if(!g.includes(e.sortBy))throw new t(`INVALID_SORT_BY`,{value:String(e.sortBy),allowed:g.join(`,`)});n.sortBy=e.sortBy}if(e.sortOrder!==void 0){if(e.sortOrder!==`asc`&&e.sortOrder!==`desc`)throw new t(`INVALID_SORT_ORDER`,{value:String(e.sortOrder)});n.sortOrder=e.sortOrder}if(e.page!==void 0){if(!Number.isInteger(e.page)||e.page<1)throw new t(`INVALID_PAGE`,{value:e.page});n.page=String(e.page)}if(e.limit!==void 0){if(!Number.isInteger(e.limit)||e.limit<1||e.limit>_)throw new t(`INVALID_LIMIT`,{value:e.limit,min:1,max:_});n.limit=String(e.limit)}return Object.keys(n).length===0?void 0:n}const y=[`poolName`,`token0CompositeKey`,`token1CompositeKey`,`token0Image`,`token1Image`,`creator`,`price`,`sqrtPrice`,`token0Tvl`,`token1Tvl`],b=[`tickSpacing`,`protocolFees`,`tradingFees`,`tick`,`volume1d`,`volume7d`,`volume30d`,`trades1d`,`fee24h`,`token0Price`,`token1Price`],x=[`fee`,`tvlUsd`,`apr1d`];function S(e){let t=a(e);if(t===void 0)return s(`the row is not an object`);for(let e of[`token0`,`token1`,`poolRef`])if(typeof t[e]!=`string`)return s(`${e} is not a string`);if(t.contractVersion!==void 0&&t.contractVersion!==`v2`)return s(`contractVersion is not v2`);let n={...t};for(let e of x){let r=t[e];if(r===void 0&&e!==`fee`)continue;let i=c(r);if(i===void 0)return s(`${e} is not a number`);n[e]=i}for(let e of y)if(t[e]!==void 0&&typeof t[e]!=`string`)return s(`${e} is not a string`);for(let e of b){let n=t[e];if(n!==void 0&&(typeof n!=`number`||!Number.isFinite(n)))return s(`${e} is not a number`)}return o(n)}export{h as Pools};
2
2
  //# sourceMappingURL=pools.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pools.js","names":[],"sources":["../../../src/classes/pools.ts"],"sourcesContent":["import type { FEE_TIER } from '../types/fees.js';\nimport type { CompositePool, PoolInfo, PoolListInfo, Slot0 } from '../types/v2_results.js';\nimport type { WireOptions } from '../types/wire.js';\nimport {\n asEntryRecord,\n entryOk,\n entryRejected,\n finiteNumberOf,\n readEntries,\n type EntryParse,\n type InvalidEntryHandler,\n} from '../utils/lenient.js';\nimport { orderSymbols, readOrderedSymbols, type TokenRef } from '../utils/ordering.js';\nimport type { ChainGateway } from './gateway.js';\nimport { GSwapSDKError } from './gswap_sdk_error.js';\nimport { HttpClient } from './http_client.js';\nimport { queryTokenOf, rawTokenOf, tradingSymbolOf, type TokenResolver } from './symbols.js';\nimport { validateFee } from '../utils/validation.js';\n\ninterface BackendEnvelope<T> {\n data: T;\n}\n\ntype PoolGateway = Pick<ChainGateway, 'httpRequestor' | 'dexBackendBaseUrl'> & {\n requestTimeoutMs?: number | undefined;\n wire?: WireOptions | undefined;\n};\n\n/** The sort keys of `GET /v2/trade/pools`. */\nexport type PoolSortBy = 'tvl' | 'volume1d' | 'volume30d';\n\n/** Options of {@link Pools.getPools}. Omit all of `search`, `sortBy`, `sortOrder`, `page`, `limit` for the full, unsorted list. */\nexport interface PoolListOptions {\n signal?: AbortSignal | undefined;\n /** Case-insensitive match on the pair's symbols; `A/B` or `A B` matches either order. At most 64 characters. */\n search?: string | undefined;\n sortBy?: PoolSortBy | undefined;\n sortOrder?: 'asc' | 'desc' | undefined;\n /** 1-based page number; only meaningful together with `limit`. */\n page?: number | undefined;\n /** Rows per page, 1-1000; omitted returns every row. */\n limit?: number | undefined;\n /** Overrides the service-level handler for this call. */\n onInvalidEntry?: InvalidEntryHandler | undefined;\n}\n\n/** Read-only pool and current-price access for the current GalaChainDex contract. */\nexport class Pools {\n /**\n * Creates a pool service. `options.onInvalidEntry` is told about every pool row that was\n * skipped as unusable (see {@link InvalidEntryHandler}).\n *\n * @example\n * ```ts\n * const pools = new Pools(gateway, symbols);\n * ```\n */\n constructor(\n private readonly gateway: PoolGateway,\n private readonly symbols: TokenResolver,\n private readonly options: { onInvalidEntry?: InvalidEntryHandler | undefined } = {},\n ) {\n this.http = new HttpClient(\n gateway.httpRequestor,\n gateway.requestTimeoutMs ?? 30_000,\n gateway.wire,\n );\n }\n\n private readonly http: HttpClient;\n\n /**\n * Fetches current-contract pools from the swap backend, optionally searched, sorted and paged\n * by the backend. A row that is not a usable pool is skipped and reported to `onInvalidEntry`\n * instead of failing the whole list; an answer that is not a list at all is\n * `INVALID_CHAIN_RESPONSE`.\n *\n * @example\n * ```ts\n * const top = await service.getPools({ sortBy: 'tvl', sortOrder: 'desc', page: 1, limit: 8 });\n * console.log(top.length);\n * ```\n */\n public async getPools(options?: PoolListOptions): Promise<PoolListInfo[]> {\n const response = await this.http.sendGetRequest<BackendEnvelope<unknown>>(\n this.gateway.dexBackendBaseUrl,\n '/v2/trade',\n '/pools',\n poolListParams(options),\n options?.signal === undefined ? undefined : { signal: options.signal },\n );\n return readEntries(\n 'pools',\n response.data,\n parsePoolListInfo,\n options?.onInvalidEntry ?? this.options.onInvalidEntry,\n );\n }\n\n /**\n * Fetches one pool from the backend, preserving whether the request order was flipped.\n *\n * @example\n * ```ts\n * const pool = await service.getPool('GUSDC', 'GALA', 3000);\n * console.log(pool.flippedFromRequest);\n * ```\n */\n public async getPool(token0: TokenRef, token1: TokenRef, fee: FEE_TIER): Promise<PoolInfo> {\n validateFee(fee);\n return this.getBackend<PoolInfo>('/pool', await this.backendParams(token0, token1, fee));\n }\n\n /**\n * Fetches the current sqrt price, price, and tick for one pool.\n *\n * @example\n * ```ts\n * const slot0 = await service.getSlot0('GALA', 'GUSDC', 3000);\n * console.log(slot0.tick);\n * ```\n */\n public async getSlot0(token0: TokenRef, token1: TokenRef, fee: FEE_TIER): Promise<Slot0> {\n validateFee(fee);\n return this.getBackend<Slot0>('/slot0', await this.backendParams(token0, token1, fee));\n }\n\n /**\n * Fetches the complete chain composite pool snapshot, in canonical symbol order.\n *\n * @example\n * ```ts\n * const composite = await service.getCompositePool('GALA', 'GUSDC', 3000);\n * console.log(composite.positions?.length ?? 0);\n * ```\n */\n public async getCompositePool(\n token0: TokenRef,\n token1: TokenRef,\n fee: FEE_TIER,\n ): Promise<CompositePool> {\n validateFee(fee);\n if (this.symbols.passthrough === true) {\n return this.getBackend<CompositePool>('/composite-pool', {\n token0: rawTokenOf(token0),\n token1: rawTokenOf(token1),\n fee: String(fee),\n });\n }\n const first = await tradingSymbolOf(this.symbols, token0);\n const second = await tradingSymbolOf(this.symbols, token1);\n const ordered = readOrderedSymbols(orderSymbols(first, second));\n return this.getBackend<CompositePool>('/composite-pool', {\n token0: ordered.token0,\n token1: ordered.token1,\n fee: String(fee),\n });\n }\n\n private async backendParams(\n token0: TokenRef,\n token1: TokenRef,\n fee: FEE_TIER,\n ): Promise<Record<string, string>> {\n return {\n token0: await queryTokenOf(this.symbols, token0),\n token1: await queryTokenOf(this.symbols, token1),\n fee: String(fee),\n };\n }\n\n private async getBackend<T>(endpoint: string, params: Record<string, string>): Promise<T> {\n try {\n const response = await this.http.sendGetRequest<BackendEnvelope<T>>(\n this.gateway.dexBackendBaseUrl,\n '/v2/trade',\n endpoint,\n params,\n );\n return response.data;\n } catch (error: unknown) {\n if (error instanceof GSwapSDKError) throw error;\n throw new GSwapSDKError('REQUEST_FAILED', { operation: 'pool' }, { cause: error });\n }\n }\n}\n\nconst SORT_KEYS: readonly string[] = ['tvl', 'volume1d', 'volume30d'];\nconst MAX_SEARCH_LENGTH = 64;\nconst MAX_PAGE_SIZE = 1000;\n\n/** The pool-list query, in the order the apps write it: search, sortBy, sortOrder, page, limit. */\nfunction poolListParams(options: PoolListOptions | undefined): Record<string, string> | undefined {\n if (options === undefined) return undefined;\n const params: Record<string, string> = {};\n if (options.search !== undefined) {\n if (options.search.length > MAX_SEARCH_LENGTH) {\n throw new GSwapSDKError('SEARCH_TOO_LONG', {\n length: options.search.length,\n maxLength: MAX_SEARCH_LENGTH,\n });\n }\n params['search'] = options.search;\n }\n if (options.sortBy !== undefined) {\n if (!SORT_KEYS.includes(options.sortBy)) {\n throw new GSwapSDKError('INVALID_SORT_BY', {\n value: String(options.sortBy),\n allowed: SORT_KEYS.join(','),\n });\n }\n params['sortBy'] = options.sortBy;\n }\n if (options.sortOrder !== undefined) {\n if (options.sortOrder !== 'asc' && options.sortOrder !== 'desc') {\n throw new GSwapSDKError('INVALID_SORT_ORDER', { value: String(options.sortOrder) });\n }\n params['sortOrder'] = options.sortOrder;\n }\n if (options.page !== undefined) {\n if (!Number.isInteger(options.page) || options.page < 1) {\n throw new GSwapSDKError('INVALID_PAGE', { value: options.page });\n }\n params['page'] = String(options.page);\n }\n if (options.limit !== undefined) {\n if (!Number.isInteger(options.limit) || options.limit < 1 || options.limit > MAX_PAGE_SIZE) {\n throw new GSwapSDKError('INVALID_LIMIT', {\n value: options.limit,\n min: 1,\n max: MAX_PAGE_SIZE,\n });\n }\n params['limit'] = String(options.limit);\n }\n return Object.keys(params).length === 0 ? undefined : params;\n}\n\nconst OPTIONAL_POOL_STRINGS = [\n 'poolName',\n 'token0CompositeKey',\n 'token1CompositeKey',\n 'token0Image',\n 'token1Image',\n 'creator',\n 'price',\n 'sqrtPrice',\n 'token0Tvl',\n 'token1Tvl',\n] as const;\n\nconst OPTIONAL_POOL_NUMBERS = [\n 'tickSpacing',\n 'protocolFees',\n 'tradingFees',\n 'tick',\n 'volume1d',\n 'volume7d',\n 'volume30d',\n 'trades1d',\n 'fee24h',\n 'token0Price',\n 'token1Price',\n] as const;\n\n/** Fields the apps accept as a number or a numeric string (the wallet does `Number(row.x)`). */\nconst NUMERIC_POOL_FIELDS = ['fee', 'tvlUsd', 'apr1d'] as const;\n\n/**\n * Reads one pool-list row the way the apps do: the identity (`token0`, `token1`, `poolRef`) and\n * `fee` must be usable, every other field is optional but, when present, must have its type.\n * `fee`, `tvlUsd` and `apr1d` may arrive as numeric strings and are returned as numbers. Fields\n * the SDK does not know are kept.\n */\nfunction parsePoolListInfo(entry: unknown): EntryParse<PoolListInfo> {\n const row = asEntryRecord(entry);\n if (row === undefined) return entryRejected('the row is not an object');\n for (const key of ['token0', 'token1', 'poolRef'] as const) {\n if (typeof row[key] !== 'string') return entryRejected(`${key} is not a string`);\n }\n if (row['contractVersion'] !== undefined && row['contractVersion'] !== 'v2') {\n return entryRejected('contractVersion is not v2');\n }\n const normalized: Record<string, unknown> = { ...row };\n for (const key of NUMERIC_POOL_FIELDS) {\n const raw = row[key];\n if (raw === undefined && key !== 'fee') continue;\n const value = finiteNumberOf(raw);\n if (value === undefined) return entryRejected(`${key} is not a number`);\n normalized[key] = value;\n }\n for (const key of OPTIONAL_POOL_STRINGS) {\n if (row[key] !== undefined && typeof row[key] !== 'string') {\n return entryRejected(`${key} is not a string`);\n }\n }\n for (const key of OPTIONAL_POOL_NUMBERS) {\n const value = row[key];\n if (value !== undefined && (typeof value !== 'number' || !Number.isFinite(value))) {\n return entryRejected(`${key} is not a number`);\n }\n }\n return entryOk(normalized as unknown as PoolListInfo);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"pools.js","names":["_defineProperty","Pools","gateway","symbols","options","HttpClient","response","poolListParams","readEntries","parsePoolListInfo","intervalMs","GSwapSDKError","known","stopped","polling","report","error","wanted","pool","poll","pools","onPool","timer","validateFee","fee","token0","token1","rawTokenOf","first","tradingSymbolOf","second","ordered","readOrderedSymbols","orderSymbols","queryTokenOf","endpoint","params","isGSwapError","SORT_KEYS","MAX_PAGE_SIZE","\"search\"","\"sortBy\"","\"sortOrder\"","\"page\"","\"limit\"","OPTIONAL_POOL_STRINGS","OPTIONAL_POOL_NUMBERS","NUMERIC_POOL_FIELDS","row","asEntryRecord","entry","entryRejected","key","\"contractVersion\"","normalized","raw","value","finiteNumberOf","entryOk"],"sources":["../../../src/classes/pools.ts"],"sourcesContent":["import type { FEE_TIER } from '../types/fees.js';\nimport type { CompositePool, PoolInfo, PoolListInfo, Slot0 } from '../types/v2_results.js';\nimport type { WireOptions } from '@gala-chain/gala-core-sdk';\nimport {\n asEntryRecord,\n entryOk,\n entryRejected,\n finiteNumberOf,\n readEntries,\n type EntryParse,\n type InvalidEntryHandler,\n} from '../utils/lenient.js';\nimport { orderSymbols, readOrderedSymbols, type TokenRef } from '../utils/ordering.js';\nimport type { ChainGateway } from './gateway.js';\nimport { GSwapSDKError, isGSwapError } from './gswap_sdk_error.js';\nimport { HttpClient } from './http_client.js';\nimport { queryTokenOf, rawTokenOf, tradingSymbolOf, type TokenResolver } from './symbols.js';\nimport { validateFee } from '../utils/validation.js';\n\ninterface BackendEnvelope<T> {\n data: T;\n}\n\ntype PoolGateway = Pick<ChainGateway, 'httpRequestor' | 'dexBackendBaseUrl'> & {\n requestTimeoutMs?: number | undefined;\n wire?: WireOptions | undefined;\n};\n\n/** The sort keys of `GET /v2/trade/pools`. */\nexport type PoolSortBy = 'tvl' | 'volume1d' | 'volume30d';\n\n/** Options of {@link Pools.getPools}. Omit all of `search`, `sortBy`, `sortOrder`, `page`, `limit` for the full, unsorted list. */\nexport interface PoolListOptions {\n signal?: AbortSignal | undefined;\n /** Case-insensitive match on the pair's symbols; `A/B` or `A B` matches either order. At most 64 characters. */\n search?: string | undefined;\n sortBy?: PoolSortBy | undefined;\n sortOrder?: 'asc' | 'desc' | undefined;\n /** 1-based page number; only meaningful together with `limit`. */\n page?: number | undefined;\n /** Rows per page, 1-1000; omitted returns every row. */\n limit?: number | undefined;\n /** Overrides the service-level handler for this call. */\n onInvalidEntry?: InvalidEntryHandler | undefined;\n}\n\n/** Options of {@link Pools.watchNewPools}. */\nexport interface WatchNewPoolsOptions {\n /** Milliseconds between polls (default 30 000; a finite number of at least 1). */\n intervalMs?: number | undefined;\n /** Report only a pool whose `tvlUsd` is at least this (a pool without `tvlUsd` counts as 0). */\n minTvl?: number | undefined;\n /** Report only a pool with one of these symbols as `token0` or `token1`. */\n tokens?: readonly string[] | undefined;\n /** Told about a poll that failed and about an `onPool` that threw; polling goes on either way. */\n onError?: ((error: unknown) => void) | undefined;\n}\n\n/** Read-only pool and current-price access for the current GalaChainDex contract. */\nexport class Pools {\n /**\n * Creates a pool service. `options.onInvalidEntry` is told about every pool row that was\n * skipped as unusable (see {@link InvalidEntryHandler}).\n *\n * @example\n * ```ts\n * const pools = new Pools(gateway, symbols);\n * ```\n */\n constructor(\n private readonly gateway: PoolGateway,\n private readonly symbols: TokenResolver,\n private readonly options: { onInvalidEntry?: InvalidEntryHandler | undefined } = {},\n ) {\n this.http = new HttpClient(\n gateway.httpRequestor,\n gateway.requestTimeoutMs ?? 30_000,\n gateway.wire,\n );\n }\n\n private readonly http: HttpClient;\n\n /**\n * Fetches current-contract pools from the swap backend, optionally searched, sorted and paged\n * by the backend. A row that is not a usable pool is skipped and reported to `onInvalidEntry`\n * instead of failing the whole list; an answer that is not a list at all is\n * `INVALID_CHAIN_RESPONSE`.\n *\n * @example\n * ```ts\n * const top = await service.getPools({ sortBy: 'tvl', sortOrder: 'desc', page: 1, limit: 8 });\n * console.log(top.length);\n * ```\n */\n public async getPools(options?: PoolListOptions): Promise<PoolListInfo[]> {\n const response = await this.http.sendGetRequest<BackendEnvelope<unknown>>(\n this.gateway.dexBackendBaseUrl,\n '/v2/trade',\n '/pools',\n poolListParams(options),\n options?.signal === undefined ? undefined : { signal: options.signal },\n );\n return readEntries(\n 'pools',\n response.data,\n parsePoolListInfo,\n options?.onInvalidEntry ?? this.options.onInvalidEntry,\n );\n }\n\n /**\n * Watches for pools created from now on, by polling {@link Pools.getPools} every `intervalMs`\n * (the polling alternative to the realtime stream's `GSwap.stream.subscribePoolCreated`, for a\n * client that cannot hold a WebSocket open). The first answer is the baseline: a pool that\n * existed then is never reported. Each later pool is judged once, when it first appears, against\n * `minTvl` and `tokens`. A poll never overlaps the previous one (a tick that finds one still in\n * flight is skipped), and nothing is reported after the returned function stops the watcher.\n *\n * @example\n * ```ts\n * const stop = gSwap.pools.watchNewPools((pool) => console.log('new pool', pool.poolRef), {\n * intervalMs: 60_000,\n * tokens: ['GALA'],\n * });\n * // later\n * stop();\n * ```\n */\n public watchNewPools(\n onPool: (pool: PoolListInfo) => void,\n options: WatchNewPoolsOptions = {},\n ): () => void {\n const intervalMs = options.intervalMs ?? 30_000;\n if (typeof intervalMs !== 'number' || !Number.isFinite(intervalMs) || intervalMs < 1) {\n throw new GSwapSDKError('INVALID_POLL_INTERVAL', { value: String(intervalMs) });\n }\n let known: Set<string> | undefined;\n let stopped = false;\n let polling = false;\n const report = (error: unknown): void => {\n try {\n options.onError?.(error);\n } catch {\n // A diagnostics hook must not stop the watcher.\n }\n };\n const wanted = (pool: PoolListInfo): boolean =>\n (options.minTvl === undefined || (pool.tvlUsd ?? 0) >= options.minTvl) &&\n (options.tokens === undefined ||\n options.tokens.includes(pool.token0) ||\n options.tokens.includes(pool.token1));\n const poll = async (): Promise<void> => {\n if (stopped || polling) return;\n polling = true;\n try {\n const pools = await this.getPools();\n if (known === undefined) {\n known = new Set(pools.map((pool) => pool.poolRef));\n return;\n }\n for (const pool of pools) {\n if (stopped) return;\n if (known.has(pool.poolRef)) continue;\n known.add(pool.poolRef);\n if (!wanted(pool)) continue;\n try {\n onPool(pool);\n } catch (error: unknown) {\n report(error);\n }\n }\n } catch (error: unknown) {\n if (!stopped) report(error);\n } finally {\n polling = false;\n }\n };\n void poll();\n const timer = setInterval(() => void poll(), intervalMs);\n return () => {\n stopped = true;\n clearInterval(timer);\n };\n }\n\n /**\n * Fetches one pool from the backend, preserving whether the request order was flipped.\n *\n * @example\n * ```ts\n * const pool = await service.getPool('GUSDC', 'GALA', 3000);\n * console.log(pool.flippedFromRequest);\n * ```\n */\n public async getPool(token0: TokenRef, token1: TokenRef, fee: FEE_TIER): Promise<PoolInfo> {\n validateFee(fee);\n return this.getBackend<PoolInfo>('/pool', await this.backendParams(token0, token1, fee));\n }\n\n /**\n * Fetches the current sqrt price, price, and tick for one pool.\n *\n * @example\n * ```ts\n * const slot0 = await service.getSlot0('GALA', 'GUSDC', 3000);\n * console.log(slot0.tick);\n * ```\n */\n public async getSlot0(token0: TokenRef, token1: TokenRef, fee: FEE_TIER): Promise<Slot0> {\n validateFee(fee);\n return this.getBackend<Slot0>('/slot0', await this.backendParams(token0, token1, fee));\n }\n\n /**\n * Fetches the complete chain composite pool snapshot, in canonical symbol order.\n *\n * @example\n * ```ts\n * const composite = await service.getCompositePool('GALA', 'GUSDC', 3000);\n * console.log(composite.positions?.length ?? 0);\n * ```\n */\n public async getCompositePool(\n token0: TokenRef,\n token1: TokenRef,\n fee: FEE_TIER,\n ): Promise<CompositePool> {\n validateFee(fee);\n if (this.symbols.passthrough === true) {\n return this.getBackend<CompositePool>('/composite-pool', {\n token0: rawTokenOf(token0),\n token1: rawTokenOf(token1),\n fee: String(fee),\n });\n }\n const first = await tradingSymbolOf(this.symbols, token0);\n const second = await tradingSymbolOf(this.symbols, token1);\n const ordered = readOrderedSymbols(orderSymbols(first, second));\n return this.getBackend<CompositePool>('/composite-pool', {\n token0: ordered.token0,\n token1: ordered.token1,\n fee: String(fee),\n });\n }\n\n private async backendParams(\n token0: TokenRef,\n token1: TokenRef,\n fee: FEE_TIER,\n ): Promise<Record<string, string>> {\n return {\n token0: await queryTokenOf(this.symbols, token0),\n token1: await queryTokenOf(this.symbols, token1),\n fee: String(fee),\n };\n }\n\n private async getBackend<T>(endpoint: string, params: Record<string, string>): Promise<T> {\n try {\n const response = await this.http.sendGetRequest<BackendEnvelope<T>>(\n this.gateway.dexBackendBaseUrl,\n '/v2/trade',\n endpoint,\n params,\n );\n return response.data;\n } catch (error: unknown) {\n // Any SDK error passes through, core's transport errors (`REQUEST_TIMEOUT`,\n // `TRANSPORT_ERROR`, a plain `GalaSDKError`) included: only a foreign failure is wrapped.\n if (isGSwapError(error)) throw error;\n throw new GSwapSDKError(\n 'REQUEST_FAILED',\n { operation: 'pool' },\n { cause: error, write: false },\n );\n }\n }\n}\n\nconst SORT_KEYS: readonly string[] = ['tvl', 'volume1d', 'volume30d'];\nconst MAX_SEARCH_LENGTH = 64;\nconst MAX_PAGE_SIZE = 1000;\n\n/** The pool-list query, in the order the apps write it: search, sortBy, sortOrder, page, limit. */\nfunction poolListParams(options: PoolListOptions | undefined): Record<string, string> | undefined {\n if (options === undefined) return undefined;\n const params: Record<string, string> = {};\n if (options.search !== undefined) {\n if (options.search.length > MAX_SEARCH_LENGTH) {\n throw new GSwapSDKError('SEARCH_TOO_LONG', {\n length: options.search.length,\n maxLength: MAX_SEARCH_LENGTH,\n });\n }\n params['search'] = options.search;\n }\n if (options.sortBy !== undefined) {\n if (!SORT_KEYS.includes(options.sortBy)) {\n throw new GSwapSDKError('INVALID_SORT_BY', {\n value: String(options.sortBy),\n allowed: SORT_KEYS.join(','),\n });\n }\n params['sortBy'] = options.sortBy;\n }\n if (options.sortOrder !== undefined) {\n if (options.sortOrder !== 'asc' && options.sortOrder !== 'desc') {\n throw new GSwapSDKError('INVALID_SORT_ORDER', { value: String(options.sortOrder) });\n }\n params['sortOrder'] = options.sortOrder;\n }\n if (options.page !== undefined) {\n if (!Number.isInteger(options.page) || options.page < 1) {\n throw new GSwapSDKError('INVALID_PAGE', { value: options.page });\n }\n params['page'] = String(options.page);\n }\n if (options.limit !== undefined) {\n if (!Number.isInteger(options.limit) || options.limit < 1 || options.limit > MAX_PAGE_SIZE) {\n throw new GSwapSDKError('INVALID_LIMIT', {\n value: options.limit,\n min: 1,\n max: MAX_PAGE_SIZE,\n });\n }\n params['limit'] = String(options.limit);\n }\n return Object.keys(params).length === 0 ? undefined : params;\n}\n\nconst OPTIONAL_POOL_STRINGS = [\n 'poolName',\n 'token0CompositeKey',\n 'token1CompositeKey',\n 'token0Image',\n 'token1Image',\n 'creator',\n 'price',\n 'sqrtPrice',\n 'token0Tvl',\n 'token1Tvl',\n] as const;\n\nconst OPTIONAL_POOL_NUMBERS = [\n 'tickSpacing',\n 'protocolFees',\n 'tradingFees',\n 'tick',\n 'volume1d',\n 'volume7d',\n 'volume30d',\n 'trades1d',\n 'fee24h',\n 'token0Price',\n 'token1Price',\n] as const;\n\n/** Fields the apps accept as a number or a numeric string (the wallet does `Number(row.x)`). */\nconst NUMERIC_POOL_FIELDS = ['fee', 'tvlUsd', 'apr1d'] as const;\n\n/**\n * Reads one pool-list row the way the apps do: the identity (`token0`, `token1`, `poolRef`) and\n * `fee` must be usable, every other field is optional but, when present, must have its type.\n * `fee`, `tvlUsd` and `apr1d` may arrive as numeric strings and are returned as numbers. Fields\n * the SDK does not know are kept.\n */\nfunction parsePoolListInfo(entry: unknown): EntryParse<PoolListInfo> {\n const row = asEntryRecord(entry);\n if (row === undefined) return entryRejected('the row is not an object');\n for (const key of ['token0', 'token1', 'poolRef'] as const) {\n if (typeof row[key] !== 'string') return entryRejected(`${key} is not a string`);\n }\n if (row['contractVersion'] !== undefined && row['contractVersion'] !== 'v2') {\n return entryRejected('contractVersion is not v2');\n }\n const normalized: Record<string, unknown> = { ...row };\n for (const key of NUMERIC_POOL_FIELDS) {\n const raw = row[key];\n if (raw === undefined && key !== 'fee') continue;\n const value = finiteNumberOf(raw);\n if (value === undefined) return entryRejected(`${key} is not a number`);\n normalized[key] = value;\n }\n for (const key of OPTIONAL_POOL_STRINGS) {\n if (row[key] !== undefined && typeof row[key] !== 'string') {\n return entryRejected(`${key} is not a string`);\n }\n }\n for (const key of OPTIONAL_POOL_NUMBERS) {\n const value = row[key];\n if (value !== undefined && (typeof value !== 'number' || !Number.isFinite(value))) {\n return entryRejected(`${key} is not a number`);\n }\n }\n return entryOk(normalized as unknown as PoolListInfo);\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{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};
1
+ import{ALLOWED_FEE_TIERS_TEXT as e}from"./gswap_error_catalog.js";import t from"../_virtual/_@oxc-project_runtime@0.153.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{HttpClient as l}from"./http_client.js";import{asEntryRecord as u,entryOk as d,entryRejected as f,readEntries as p}from"../utils/lenient.js";import{parseTokenClassKey as m}from"../utils/ordering.js";import{resolvePreSend as h}from"../utils/resolve_pre_send.js";import{rawTokenOf as g}from"./symbols.js";import{alignTickDown as _,alignTickUp as v,assertTickRange as y}from"../utils/ticks.js";import{assertCanonicalV2Dto as b,buildAddLiquidityDto as x,buildCollectPositionFeesDto as S,buildCreatePoolDto as C,buildRemoveLiquidityDto as w}from"../utils/dto_builders.js";import{assertUniqueKey as T,compositeKeyOf as E,createUniqueKey as D,resolveUniqueKeyPrefix as O,signPreSend as k}from"@gala-chain/gala-core-sdk";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 l(e.httpRequestor,e.requestTimeoutMs??3e4,e.wire),this.uniqueKeyPrefix=O(a.uniqueKeyPrefix),this.onInvalidEntry=a.onInvalidEntry}async getUserPositions(e){let t=await this.get(`/positions`,{user:e});return p(`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,write:!1})}}}async positionParams(e){if(this.symbols.passthrough===!0)return{token0:g(e.token0),token1:g(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:g(e.token0),token1:g(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:g(e.token0),token1:g(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);y(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?x({...r,depositQuantityToken1:e.amount}):x({...r,depositQuantityToken0:e.amount})}async submitAddLiquidity(e){return b(`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=_(a(t),i),c=v(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(y(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 w({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){b(`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 y(n.tickLower,n.tickUpper,e.fee),S({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 b(`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 C(t?{...f,startingPrice:d}:{...f,startingSqrtPrice:d})}async submitCreatePool(e){b(`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 k(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 r(e)&&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?D(this.uniqueKeyPrefix):(T(e),e)}orderPair(e,t){return h(`${g(e)}/${g(t)}`,()=>this.symbols.orderPair(e,t))}async resolveCreateToken(e,t){try{let r=typeof e==`object`?E(e):e,i=await h(g(e),()=>this.symbols.resolve(r));if(t!==void 0&&t!==``&&t!==i.symbol)throw new n(`SYMBOL_DISAGREES_WITH_REGISTRY`,{token:g(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:g(e)});return{symbol:i.symbol,classKey:a??m({collection:i.collection,category:i.category,type:i.type,additionalKey:i.additionalKey})}}catch(i){if(!r(i,`UNKNOWN_TOKEN`))throw i;let a;try{a=m(e)}catch{throw i}if(t===void 0||t===``)throw new n(`TOKEN_SYMBOL_NOT_REGISTERED`,{token:g(e)});if(!o(t))throw new n(`INVALID_TRADING_SYMBOL`,{symbol:t});return{symbol:t,classKey:a}}}tryParseClassKey(e){try{return m(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=u(e);if(t===void 0)return f(`the row is not an object`);for(let e of N)if(typeof t[e]!=`string`)return f(`${e} is not a string`);for(let e of P){let n=t[e];if(typeof n!=`number`||!Number.isFinite(n))return f(`${e} is not a number`)}if(typeof t.inRange!=`boolean`)return f(`inRange is not a boolean`);for(let e of F)if(t[e]!==void 0&&typeof t[e]!=`string`)return f(`${e} is not a string`);let n=t.currentTick;if(n!==void 0&&(typeof n!=`number`||!Number.isFinite(n)))return f(`currentTick is not a number`);let r=t.poolRef??t.pool??``;if(typeof r!=`string`)return f(`pool is not a string`);let i=Object.fromEntries([...F,`currentTick`].flatMap(e=>t[e]===void 0?[]:[[e,t[e]]]));return d({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,write:!1})}export{M as Positions};
2
2
  //# sourceMappingURL=positions.js.map