@gala-chain/gswap-sdk 2.1.0-beta.3 → 2.1.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
- import e from"../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/defineProperty.js";import{GSwapSDKError as t}from"./gswap_sdk_error.js";import{buildRequestHeaders as n,createUniqueKey as r,fetchRequestor as i,readResponseBody as a,requestWithTimeout as o,signPreSend as s}from"@gala-chain/gala-core-sdk";const c=[`Swap`,`RequestTrade`,`AddLiquidity`,`RemoveLiquidity`,`CollectPositionFees`,`CreatePool`],l=`DryRun`,u=`galaconnect-operation-`;function d(e){return typeof e==`string`&&c.includes(e)}function f(e,t){return d(t)&&typeof e?.token0==`string`&&typeof e.token1==`string`}function p(e){let t={...e};for(let e of[`signature`,`prefix`,`types`,`domain`,`trace`])delete t[e];return t}function m(e,t,n,i,a=u,o=`asset/dexv3-contract`){return s(e,o,l,{callerPublicKey:t,method:n,dto:i,uniqueKey:r(a)})}var h=class{constructor(t,n){this.signer=n,e(this,`requestor`,void 0),e(this,`baseUrl`,void 0),e(this,`requestTimeoutMs`,void 0),e(this,`wire`,void 0),this.requestor=t.httpRequestor??i,this.baseUrl=t.dexBackendBaseUrl.replace(/\/$/u,``),this.requestTimeoutMs=t.requestTimeoutMs??3e4,this.wire=t.wire}async quote(e,r,i,s={}){if(this.signer===void 0)throw t.noSignerError();let c=s.channel??`asset`,l=s.contract??`dexv3-contract`,u=await m(this.signer,e,r,p(i),void 0,`asset/${l}`),d=`${this.baseUrl}/wallet/connect/galachain/api/${encodeURIComponent(c)}/${encodeURIComponent(l)}/${encodeURIComponent(r)}/fee`,f,h,_;try{let e=await o(this.requestor,d,{method:`POST`,headers:n(this.wire,{json:!0,defaults:{Accept:`application/json`}}),body:JSON.stringify(u)},this.requestTimeoutMs);f=e.status,h=e.ok,_=h?await a(e):void 0}catch{return null}return h?g(_):l===`dex-contract`&&(f===403||f===404)?{source:`default`,fees:[{feeInGala:`0`}]}:null}};function g(e){if(typeof e!=`object`||!e)return null;let t=e.fees;return!Array.isArray(t)||typeof t[0]?.feeInGala!=`string`?null:e}export{c as DEX_FEE_METHODS,l as DRY_RUN_METHOD,u as DRY_RUN_UNIQUE_KEY_PREFIX,h as DexFees,m as buildDryRunWrapper,f as isCurrentContractDexMessage,d as isDexFeeMethod,p as toChainDto};
1
+ import e from"../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/defineProperty.js";import{GSwapSDKError as t}from"./gswap_sdk_error.js";import{buildRequestHeaders as n,createUniqueKey as r,fetchRequestor as i,readResponseBody as a,requestWithTimeout as o,signPreSend as s}from"@gala-chain/gala-core-sdk";const c=[`Swap`,`RequestTrade`,`AddLiquidity`,`RemoveLiquidity`,`CollectPositionFees`,`CreatePool`],l=`DryRun`,u=`galaconnect-operation-`;function d(e){return typeof e==`string`&&c.includes(e)}function f(e,t){return d(t)&&typeof e?.token0==`string`&&typeof e.token1==`string`}function p(e){let t={...e};for(let e of[`signature`,`prefix`,`types`,`domain`,`dtoOperation`,`trace`])delete t[e];return t}function m(e,t,n,i,a=u,o=`asset/dexv3-contract`){return s(e,o,l,{callerPublicKey:t,method:n,dto:p(i),uniqueKey:r(a)})}var h=class{constructor(t,n){this.signer=n,e(this,`requestor`,void 0),e(this,`baseUrl`,void 0),e(this,`requestTimeoutMs`,void 0),e(this,`wire`,void 0),this.requestor=t.httpRequestor??i,this.baseUrl=t.dexBackendBaseUrl.replace(/\/$/u,``),this.requestTimeoutMs=t.requestTimeoutMs??3e4,this.wire=t.wire}async quote(e,r,i,s={}){if(this.signer===void 0)throw t.noSignerError();let c=s.channel??`asset`,l=s.contract??`dexv3-contract`,u=await m(this.signer,e,r,p(i),void 0,`asset/${l}`),d=`${this.baseUrl}/wallet/connect/galachain/api/${encodeURIComponent(c)}/${encodeURIComponent(l)}/${encodeURIComponent(r)}/fee`,f,h,_;try{let e=await o(this.requestor,d,{method:`POST`,headers:n(this.wire,{json:!0,defaults:{Accept:`application/json`}}),body:JSON.stringify(u)},this.requestTimeoutMs);f=e.status,h=e.ok,_=h?await a(e):void 0}catch{return null}return h?g(_):l===`dex-contract`&&(f===403||f===404)?{source:`default`,fees:[{feeInGala:`0`}]}:null}};function g(e){if(typeof e!=`object`||!e)return null;let t=e.fees;return!Array.isArray(t)||typeof t[0]?.feeInGala!=`string`?null:e}export{c as DEX_FEE_METHODS,l as DRY_RUN_METHOD,u as DRY_RUN_UNIQUE_KEY_PREFIX,h as DexFees,m as buildDryRunWrapper,f as isCurrentContractDexMessage,d as isDexFeeMethod,p as toChainDto};
2
2
  //# sourceMappingURL=dex_fees.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"dex_fees.js","names":["_defineProperty","DEX_FEE_METHODS","DRY_RUN_METHOD","DRY_RUN_UNIQUE_KEY_PREFIX","isDexFeeMethod","method","isCurrentContractDexMessage","message","\"token0\"","\"token1\"","toChainDto","chainDto","field","buildDryRunWrapper","signPreSend","signer","contractPath","createUniqueKey","uniqueKeyPrefix","DexFees","client","fetchRequestor","GSwapSDKError","channel","options","contract","wrapper","callerPublicKey","url","status","ok","body","response","requestWithTimeout","buildRequestHeaders","readResponseBody","readQuote","fees"],"sources":["../../../src/classes/dex_fees.ts"],"sourcesContent":["import {\n buildRequestHeaders,\n createUniqueKey,\n fetchRequestor,\n readResponseBody,\n requestWithTimeout,\n signPreSend,\n type ChainContractPath,\n type GalaChainSigner,\n type HttpRequestor,\n type WireOptions,\n} from '@gala-chain/gala-core-sdk';\nimport { GSwapSDKError } from './gswap_sdk_error.js';\n\n/** DEX methods the backend accepts a fee quote for (its allow-list enumerates the same six). */\nexport const DEX_FEE_METHODS = [\n 'Swap',\n 'RequestTrade',\n 'AddLiquidity',\n 'RemoveLiquidity',\n 'CollectPositionFees',\n 'CreatePool',\n] as const;\n/** A chain method whose network fee `DexFees.quote` can price (see `isDexFeeMethod`). */\nexport type DexFeeMethod = (typeof DEX_FEE_METHODS)[number];\n\n/** Which pool contract the DTO being quoted targets: legacy (V3) or current (GalaChainDex). */\nexport type DexFeeContract = 'dexv3-contract' | 'dex-contract';\n\n/** The chain method name the signed DryRun wrapper is signed under. */\nexport const DRY_RUN_METHOD = 'DryRun';\n\n/** `uniqueKey` prefix of the DryRun WRAPPER (distinct from the inner DTO's key). */\nexport const DRY_RUN_UNIQUE_KEY_PREFIX = 'galaconnect-operation-';\n\n/** True when `method` is a DEX method a fee can be quoted for. */\nexport function isDexFeeMethod(method: unknown): method is DexFeeMethod {\n return typeof method === 'string' && (DEX_FEE_METHODS as readonly string[]).includes(method);\n}\n\n/** Current-contract typed DEX messages use trading symbols instead of TokenClassKey objects. */\nexport function isCurrentContractDexMessage(\n message: Record<string, unknown> | null | undefined,\n method: unknown,\n): boolean {\n return (\n isDexFeeMethod(method) &&\n typeof message?.['token0'] === 'string' &&\n typeof message['token1'] === 'string'\n );\n}\n\n/**\n * The backend's response. It ALWAYS carries a usable `fees[0].feeInGala`: the backend decides what\n * to serve when the chain cannot price the operation, so the client never invents a fee.\n *\n * `source` is informational: `'dry-run'` is simulated on chain and authoritative (possibly a\n * legitimate `'0'`), `'default'` means the chain could not price it and the platform default was\n * served.\n */\nexport interface DexFeeQuote {\n fees: Array<{ feeInGala: string }>;\n source: 'dry-run' | 'default';\n}\n\n/**\n * Reduces an EIP-712 approval `message` to the DTO the chain actually receives: drops the signing\n * envelope (`signature`, `prefix`, `types`, `domain`) and transport metadata (`trace`).\n */\nexport function toChainDto(message: Record<string, unknown>): Record<string, unknown> {\n const chainDto: Record<string, unknown> = { ...message };\n for (const field of ['signature', 'prefix', 'types', 'domain', 'trace']) {\n delete chainDto[field];\n }\n return chainDto;\n}\n\n/**\n * Signs the DryRun WRAPPER the gateway accepts for every contract:\n * `{ callerPublicKey, method, dto: <inner DTO object>, uniqueKey, signature }`. The inner DTO\n * travels unsigned and as an object; no `signerPublicKey` is sent because the signature already\n * recovers it (the gateway rejects it as REDUNDANT_SIGNER_PUBLIC_KEY). The wrapper carries its own\n * `uniqueKey`, distinct from the DTO's.\n */\nexport function buildDryRunWrapper(\n signer: GalaChainSigner,\n callerPublicKey: string,\n method: string,\n dto: Record<string, unknown>,\n uniqueKeyPrefix: string = DRY_RUN_UNIQUE_KEY_PREFIX,\n contractPath: ChainContractPath = 'asset/dexv3-contract',\n): Promise<Record<string, unknown>> {\n return signPreSend(signer, contractPath, DRY_RUN_METHOD, {\n callerPublicKey,\n method,\n dto,\n // Core's RN-safe mint (`getRandomValues`, never `randomUUID`, which Hermes lacks).\n uniqueKey: createUniqueKey(uniqueKeyPrefix),\n });\n}\n\n/** The transport and backend a fee quote reads from. */\nexport interface DexFeesClient {\n /** Swap backend origin, e.g. `https://dex-backend-prod1.defi.gala.com`. */\n dexBackendBaseUrl: string;\n /** Defaults to the global `fetch`. */\n httpRequestor?: HttpRequestor | undefined;\n /** Per-request timeout in milliseconds (default 30 000). */\n requestTimeoutMs?: number | undefined;\n /**\n * Request headers of the fee POST (`userAgent`, `headers`); see {@link WireOptions}. The SDK's\n * `Accept: application/json` is a default that `wire.headers` may replace.\n */\n wire?: WireOptions | undefined;\n}\n\n/** Options for a single quote. */\nexport interface DexFeeQuoteOptions {\n /** GalaChain channel. Default `asset`. */\n channel?: string | undefined;\n /**\n * Contract the DTO targets. Default `dexv3-contract` (what every legacy caller quotes against);\n * a current-contract operation must pass `dex-contract` explicitly.\n */\n contract?: DexFeeContract | undefined;\n}\n\n/**\n * Network-fee quote for GalaChain DEX methods, derived from a server-side DryRun.\n *\n * The client only SIGNS the DryRun wrapper (with whatever {@link GalaChainSigner} it is given) and\n * the backend forwards it to the gateway, where the simulated writes carry the fee; the backend\n * never holds a key. Intended for wallets: the DTO quoted is the typed message a dApp handed over.\n *\n * @example\n * ```ts\n * const fees = new DexFees({ dexBackendBaseUrl }, new PrivateKeySigner(sessionKey));\n * const quote = await fees.quote(publicKey, 'RequestTrade', toChainDto(message), {\n * contract: 'dex-contract',\n * });\n * const feeInGala = quote?.fees[0]?.feeInGala;\n * ```\n */\nexport class DexFees {\n private readonly requestor: HttpRequestor;\n private readonly baseUrl: string;\n private readonly requestTimeoutMs: number;\n private readonly wire: WireOptions | undefined;\n\n constructor(\n client: DexFeesClient,\n private readonly signer: GalaChainSigner | undefined,\n ) {\n this.requestor = client.httpRequestor ?? fetchRequestor;\n this.baseUrl = client.dexBackendBaseUrl.replace(/\\/$/u, '');\n this.requestTimeoutMs = client.requestTimeoutMs ?? 30_000;\n this.wire = client.wire;\n }\n\n /**\n * Quotes the network fee for `method` applied to `message`. The message is reduced with\n * {@link toChainDto} first, so an EIP-712 approval message can be passed as is.\n *\n * Resolves `null` when the BACKEND could not be reached or answered unusably (offline, 4xx/5xx,\n * malformed body): a transport outcome, not a fee. Deciding what to charge when the chain is\n * silent is the backend's job (it serves `source: 'default'`), so the client never substitutes\n * a fee of its own, with ONE exception that mirrors the wallet: a `dex-contract` quote answered\n * 403 or 404 means the contract has no fee route yet and resolves `{ source: 'default', '0' }`.\n * Never throws on transport, so a fee lookup cannot block or break a signing screen.\n *\n * @throws GSwapSDKError NO_SIGNER when constructed without a signer; `SIGNING_FAILED` or\n * `KEY_PROVIDER_FAILED` (a pre-send error, the original as `cause`) when the signer fails, for\n * example a locked wallet.\n */\n public async quote(\n callerPublicKey: string,\n method: DexFeeMethod,\n message: Record<string, unknown>,\n options: DexFeeQuoteOptions = {},\n ): Promise<DexFeeQuote | null> {\n if (this.signer === undefined) throw GSwapSDKError.noSignerError();\n const channel = options.channel ?? 'asset';\n const contract = options.contract ?? 'dexv3-contract';\n const wrapper = await buildDryRunWrapper(\n this.signer,\n callerPublicKey,\n method,\n toChainDto(message),\n undefined,\n `asset/${contract}`,\n );\n const url = `${this.baseUrl}/wallet/connect/galachain/api/${encodeURIComponent(channel)}/${encodeURIComponent(contract)}/${encodeURIComponent(method)}/fee`;\n\n let status: number;\n let ok: boolean;\n let body: unknown;\n try {\n const response = await requestWithTimeout(\n this.requestor,\n url,\n {\n method: 'POST',\n headers: buildRequestHeaders(this.wire, {\n json: true,\n defaults: { Accept: 'application/json' },\n }),\n body: JSON.stringify(wrapper),\n },\n this.requestTimeoutMs,\n );\n status = response.status;\n ok = response.ok;\n body = ok ? await readResponseBody(response) : undefined;\n } catch {\n return null;\n }\n\n if (!ok) {\n if (contract === 'dex-contract' && (status === 403 || status === 404)) {\n return { source: 'default', fees: [{ feeInGala: '0' }] };\n }\n return null;\n }\n // Accept whatever fee the backend served: 'default' is as usable as 'dry-run'. Only a body\n // missing the fee outright is unusable.\n return readQuote(body);\n }\n}\n\nfunction readQuote(body: unknown): DexFeeQuote | null {\n if (typeof body !== 'object' || body === null) return null;\n const fees = (body as { fees?: unknown }).fees;\n if (\n !Array.isArray(fees) ||\n typeof (fees[0] as { feeInGala?: unknown } | undefined)?.feeInGala !== 'string'\n ) {\n return null;\n }\n return body as DexFeeQuote;\n}\n"],"mappings":"A,oTAeA,MAAaC,EAAkB,CAC7B,OACA,eACA,eACA,kBACA,sBACA,YACF,EAQaC,EAAiB,SAGjBC,EAA4B,yBAGzC,SAAgBC,EAAe,EAAyC,CACtE,OAAO,OAAOC,GAAW,UAAaJ,EAAsC,SAASI,CAAM,CAC7F,CAGA,SAAgBC,EACd,EACA,EACS,CACT,OACEF,EAAeC,CAAM,GACrB,OAAOE,GAAUC,QAAc,UAC/B,OAAOD,EAAQE,QAAc,QAEjC,CAmBA,SAAgBC,EAAW,EAA2D,CACpF,IAAMC,EAAoC,CAAE,GAAGJ,CAAQ,EACvD,IAAK,IAAMK,IAAS,CAAC,YAAa,SAAU,QAAS,SAAU,OAAO,EACpE,OAAOD,EAASC,GAElB,OAAOD,CACT,CASA,SAAgBE,EACd,EACA,EACA,EACA,EACA,EAA0BV,EAC1B,EAAkC,uBACA,CAClC,OAAOW,EAAYC,EAAQC,EAAcd,EAAgB,CACvD,kBACA,SACA,MAEA,UAAWe,EAAgBC,CAAe,CAC5C,CAAC,CACH,CA4CA,IAAaC,EAAb,KAAqB,CAMnB,YACE,EACA,EACA,CADiB,KAAA,OAAAJ,EAPFf,EAAA,KAAA,YAAA,IAAA,EAAA,EACAA,EAAA,KAAA,UAAA,IAAA,EAAA,EACAA,EAAA,KAAA,mBAAA,IAAA,EAAA,EACAA,EAAA,KAAA,OAAA,IAAA,EAAA,EAMf,KAAK,UAAYoB,EAAO,eAAiBC,EACzC,KAAK,QAAUD,EAAO,kBAAkB,QAAQ,OAAQ,EAAE,EAC1D,KAAK,iBAAmBA,EAAO,kBAAoB,IACnD,KAAK,KAAOA,EAAO,IACrB,CAiBA,MAAa,MACX,EACA,EACA,EACA,EAA8B,CAAC,EACF,CAC7B,GAAI,KAAK,SAAW,IAAA,GAAW,MAAME,EAAc,cAAc,EACjE,IAAMC,EAAUC,EAAQ,SAAW,QAC7BC,EAAWD,EAAQ,UAAY,iBAC/BE,EAAU,MAAMb,EACpB,KAAK,OACLc,EACAtB,EACAK,EAAWH,CAAO,EAClB,IAAA,GACA,SAASkB,GACX,EACMG,EAAM,GAAG,KAAK,QAAQ,gCAAgC,mBAAmBL,CAAO,EAAE,GAAG,mBAAmBE,CAAQ,EAAE,GAAG,mBAAmBpB,CAAM,EAAE,MAElJwB,EACAC,EACAC,EACJ,GAAI,CACF,IAAMC,EAAW,MAAMC,EACrB,KAAK,UACLL,EACA,CACE,OAAQ,OACR,QAASM,EAAoB,KAAK,KAAM,CACtC,KAAM,GACN,SAAU,CAAE,OAAQ,kBAAmB,CACzC,CAAC,EACD,KAAM,KAAK,UAAUR,CAAO,CAC9B,EACA,KAAK,gBACP,EACA,EAASM,EAAS,OAClB,EAAKA,EAAS,GACd,EAAOF,EAAK,MAAMK,EAAiBH,CAAQ,EAAI,IAAA,EACjD,MAAQ,CACN,OAAO,IACT,CAUA,OARKF,EAQEM,EAAUL,CAAI,EAPfN,IAAa,iBAAmBI,IAAW,KAAOA,IAAW,KACxD,CAAE,OAAQ,UAAW,KAAM,CAAC,CAAE,UAAW,GAAI,CAAC,CAAE,EAElD,IAKX,CACF,EAEA,SAASO,EAAU,EAAmC,CACpD,GAAI,OAAOL,GAAS,WAAYA,EAAe,OAAO,KACtD,IAAMM,EAAQN,EAA4B,KAO1C,MALE,CAAC,MAAM,QAAQM,CAAI,GACnB,OAAQA,EAAK,EAAE,EAA0C,WAAc,SAEhE,KAEFN,CACT,C"}
1
+ {"version":3,"file":"dex_fees.js","names":["_defineProperty","DEX_FEE_METHODS","DRY_RUN_METHOD","DRY_RUN_UNIQUE_KEY_PREFIX","isDexFeeMethod","method","isCurrentContractDexMessage","message","\"token0\"","\"token1\"","toChainDto","chainDto","field","buildDryRunWrapper","signPreSend","signer","contractPath","dto","createUniqueKey","uniqueKeyPrefix","DexFees","client","fetchRequestor","GSwapSDKError","channel","options","contract","wrapper","callerPublicKey","url","status","ok","body","response","requestWithTimeout","buildRequestHeaders","readResponseBody","readQuote","fees"],"sources":["../../../src/classes/dex_fees.ts"],"sourcesContent":["import {\n buildRequestHeaders,\n createUniqueKey,\n fetchRequestor,\n readResponseBody,\n requestWithTimeout,\n signPreSend,\n type ChainContractPath,\n type GalaChainSigner,\n type HttpRequestor,\n type WireOptions,\n} from '@gala-chain/gala-core-sdk';\nimport { GSwapSDKError } from './gswap_sdk_error.js';\n\n/** DEX methods the backend accepts a fee quote for (its allow-list enumerates the same six). */\nexport const DEX_FEE_METHODS = [\n 'Swap',\n 'RequestTrade',\n 'AddLiquidity',\n 'RemoveLiquidity',\n 'CollectPositionFees',\n 'CreatePool',\n] as const;\n/** A chain method whose network fee `DexFees.quote` can price (see `isDexFeeMethod`). */\nexport type DexFeeMethod = (typeof DEX_FEE_METHODS)[number];\n\n/** Which pool contract the DTO being quoted targets: legacy (V3) or current (GalaChainDex). */\nexport type DexFeeContract = 'dexv3-contract' | 'dex-contract';\n\n/** The chain method name the signed DryRun wrapper is signed under. */\nexport const DRY_RUN_METHOD = 'DryRun';\n\n/** `uniqueKey` prefix of the DryRun WRAPPER (distinct from the inner DTO's key). */\nexport const DRY_RUN_UNIQUE_KEY_PREFIX = 'galaconnect-operation-';\n\n/** True when `method` is a DEX method a fee can be quoted for. */\nexport function isDexFeeMethod(method: unknown): method is DexFeeMethod {\n return typeof method === 'string' && (DEX_FEE_METHODS as readonly string[]).includes(method);\n}\n\n/** Current-contract typed DEX messages use trading symbols instead of TokenClassKey objects. */\nexport function isCurrentContractDexMessage(\n message: Record<string, unknown> | null | undefined,\n method: unknown,\n): boolean {\n return (\n isDexFeeMethod(method) &&\n typeof message?.['token0'] === 'string' &&\n typeof message['token1'] === 'string'\n );\n}\n\n/**\n * The backend's response. It ALWAYS carries a usable `fees[0].feeInGala`: the backend decides what\n * to serve when the chain cannot price the operation, so the client never invents a fee.\n *\n * `source` is informational: `'dry-run'` is simulated on chain and authoritative (possibly a\n * legitimate `'0'`), `'default'` means the chain could not price it and the platform default was\n * served.\n */\nexport interface DexFeeQuote {\n fees: Array<{ feeInGala: string }>;\n source: 'dry-run' | 'default';\n}\n\n/**\n * Reduces an EIP-712 approval `message` to the DTO the chain actually receives: drops the signing\n * envelope (`signature`, `prefix`, `types`, `domain`), the `dtoOperation` it was signed to call (a\n * DryRun runs the DTO inside a wrapper signed for `DryRun`; left inside, the chain refuses it:\n * \"signed to call ...:RequestTrade, but the current operation is ...:DryRun\") and transport\n * metadata (`trace`).\n */\nexport function toChainDto(message: Record<string, unknown>): Record<string, unknown> {\n const chainDto: Record<string, unknown> = { ...message };\n for (const field of ['signature', 'prefix', 'types', 'domain', 'dtoOperation', 'trace']) {\n delete chainDto[field];\n }\n return chainDto;\n}\n\n/**\n * Signs the DryRun WRAPPER the gateway accepts for every contract:\n * `{ callerPublicKey, method, dto: <inner DTO object>, uniqueKey, signature }`. The inner DTO\n * travels unsigned and as an object; no `signerPublicKey` is sent because the signature already\n * recovers it (the gateway rejects it as REDUNDANT_SIGNER_PUBLIC_KEY). The wrapper carries its own\n * `uniqueKey`, distinct from the DTO's, and its own `dtoOperation` (`<Contract>:DryRun`); the inner DTO\n * carries none of the signing envelope or the `dtoOperation` of the call it simulates (see\n * {@link toChainDto}), whatever the caller passes, so a signed DTO can be handed in as is.\n */\nexport function buildDryRunWrapper(\n signer: GalaChainSigner,\n callerPublicKey: string,\n method: string,\n dto: Record<string, unknown>,\n uniqueKeyPrefix: string = DRY_RUN_UNIQUE_KEY_PREFIX,\n contractPath: ChainContractPath = 'asset/dexv3-contract',\n): Promise<Record<string, unknown>> {\n return signPreSend(signer, contractPath, DRY_RUN_METHOD, {\n callerPublicKey,\n method,\n dto: toChainDto(dto),\n // Core's RN-safe mint (`getRandomValues`, never `randomUUID`, which Hermes lacks).\n uniqueKey: createUniqueKey(uniqueKeyPrefix),\n });\n}\n\n/** The transport and backend a fee quote reads from. */\nexport interface DexFeesClient {\n /** Swap backend origin, e.g. `https://dex-backend-prod1.defi.gala.com`. */\n dexBackendBaseUrl: string;\n /** Defaults to the global `fetch`. */\n httpRequestor?: HttpRequestor | undefined;\n /** Per-request timeout in milliseconds (default 30 000). */\n requestTimeoutMs?: number | undefined;\n /**\n * Request headers of the fee POST (`userAgent`, `headers`); see {@link WireOptions}. The SDK's\n * `Accept: application/json` is a default that `wire.headers` may replace.\n */\n wire?: WireOptions | undefined;\n}\n\n/** Options for a single quote. */\nexport interface DexFeeQuoteOptions {\n /** GalaChain channel. Default `asset`. */\n channel?: string | undefined;\n /**\n * Contract the DTO targets. Default `dexv3-contract` (what every legacy caller quotes against);\n * a current-contract operation must pass `dex-contract` explicitly.\n */\n contract?: DexFeeContract | undefined;\n}\n\n/**\n * Network-fee quote for GalaChain DEX methods, derived from a server-side DryRun.\n *\n * The client only SIGNS the DryRun wrapper (with whatever {@link GalaChainSigner} it is given) and\n * the backend forwards it to the gateway, where the simulated writes carry the fee; the backend\n * never holds a key. Intended for wallets: the DTO quoted is the typed message a dApp handed over.\n *\n * @example\n * ```ts\n * const fees = new DexFees({ dexBackendBaseUrl }, new PrivateKeySigner(sessionKey));\n * const quote = await fees.quote(publicKey, 'RequestTrade', toChainDto(message), {\n * contract: 'dex-contract',\n * });\n * const feeInGala = quote?.fees[0]?.feeInGala;\n * ```\n */\nexport class DexFees {\n private readonly requestor: HttpRequestor;\n private readonly baseUrl: string;\n private readonly requestTimeoutMs: number;\n private readonly wire: WireOptions | undefined;\n\n constructor(\n client: DexFeesClient,\n private readonly signer: GalaChainSigner | undefined,\n ) {\n this.requestor = client.httpRequestor ?? fetchRequestor;\n this.baseUrl = client.dexBackendBaseUrl.replace(/\\/$/u, '');\n this.requestTimeoutMs = client.requestTimeoutMs ?? 30_000;\n this.wire = client.wire;\n }\n\n /**\n * Quotes the network fee for `method` applied to `message`. The message is reduced with\n * {@link toChainDto} first, so an EIP-712 approval message can be passed as is.\n *\n * Resolves `null` when the BACKEND could not be reached or answered unusably (offline, 4xx/5xx,\n * malformed body): a transport outcome, not a fee. Deciding what to charge when the chain is\n * silent is the backend's job (it serves `source: 'default'`), so the client never substitutes\n * a fee of its own, with ONE exception that mirrors the wallet: a `dex-contract` quote answered\n * 403 or 404 means the contract has no fee route yet and resolves `{ source: 'default', '0' }`.\n * Never throws on transport, so a fee lookup cannot block or break a signing screen.\n *\n * @throws GSwapSDKError NO_SIGNER when constructed without a signer; `SIGNING_FAILED` or\n * `KEY_PROVIDER_FAILED` (a pre-send error, the original as `cause`) when the signer fails, for\n * example a locked wallet.\n */\n public async quote(\n callerPublicKey: string,\n method: DexFeeMethod,\n message: Record<string, unknown>,\n options: DexFeeQuoteOptions = {},\n ): Promise<DexFeeQuote | null> {\n if (this.signer === undefined) throw GSwapSDKError.noSignerError();\n const channel = options.channel ?? 'asset';\n const contract = options.contract ?? 'dexv3-contract';\n const wrapper = await buildDryRunWrapper(\n this.signer,\n callerPublicKey,\n method,\n toChainDto(message),\n undefined,\n `asset/${contract}`,\n );\n const url = `${this.baseUrl}/wallet/connect/galachain/api/${encodeURIComponent(channel)}/${encodeURIComponent(contract)}/${encodeURIComponent(method)}/fee`;\n\n let status: number;\n let ok: boolean;\n let body: unknown;\n try {\n const response = await requestWithTimeout(\n this.requestor,\n url,\n {\n method: 'POST',\n headers: buildRequestHeaders(this.wire, {\n json: true,\n defaults: { Accept: 'application/json' },\n }),\n body: JSON.stringify(wrapper),\n },\n this.requestTimeoutMs,\n );\n status = response.status;\n ok = response.ok;\n body = ok ? await readResponseBody(response) : undefined;\n } catch {\n return null;\n }\n\n if (!ok) {\n if (contract === 'dex-contract' && (status === 403 || status === 404)) {\n return { source: 'default', fees: [{ feeInGala: '0' }] };\n }\n return null;\n }\n // Accept whatever fee the backend served: 'default' is as usable as 'dry-run'. Only a body\n // missing the fee outright is unusable.\n return readQuote(body);\n }\n}\n\nfunction readQuote(body: unknown): DexFeeQuote | null {\n if (typeof body !== 'object' || body === null) return null;\n const fees = (body as { fees?: unknown }).fees;\n if (\n !Array.isArray(fees) ||\n typeof (fees[0] as { feeInGala?: unknown } | undefined)?.feeInGala !== 'string'\n ) {\n return null;\n }\n return body as DexFeeQuote;\n}\n"],"mappings":"A,oTAeA,MAAaC,EAAkB,CAC7B,OACA,eACA,eACA,kBACA,sBACA,YACF,EAQaC,EAAiB,SAGjBC,EAA4B,yBAGzC,SAAgBC,EAAe,EAAyC,CACtE,OAAO,OAAOC,GAAW,UAAaJ,EAAsC,SAASI,CAAM,CAC7F,CAGA,SAAgBC,EACd,EACA,EACS,CACT,OACEF,EAAeC,CAAM,GACrB,OAAOE,GAAUC,QAAc,UAC/B,OAAOD,EAAQE,QAAc,QAEjC,CAsBA,SAAgBC,EAAW,EAA2D,CACpF,IAAMC,EAAoC,CAAE,GAAGJ,CAAQ,EACvD,IAAK,IAAMK,IAAS,CAAC,YAAa,SAAU,QAAS,SAAU,eAAgB,OAAO,EACpF,OAAOD,EAASC,GAElB,OAAOD,CACT,CAWA,SAAgBE,EACd,EACA,EACA,EACA,EACA,EAA0BV,EAC1B,EAAkC,uBACA,CAClC,OAAOW,EAAYC,EAAQC,EAAcd,EAAgB,CACvD,kBACA,SACA,IAAKQ,EAAWO,CAAG,EAEnB,UAAWC,EAAgBC,CAAe,CAC5C,CAAC,CACH,CA4CA,IAAaC,EAAb,KAAqB,CAMnB,YACE,EACA,EACA,CADiB,KAAA,OAAAL,EAPFf,EAAA,KAAA,YAAA,IAAA,EAAA,EACAA,EAAA,KAAA,UAAA,IAAA,EAAA,EACAA,EAAA,KAAA,mBAAA,IAAA,EAAA,EACAA,EAAA,KAAA,OAAA,IAAA,EAAA,EAMf,KAAK,UAAYqB,EAAO,eAAiBC,EACzC,KAAK,QAAUD,EAAO,kBAAkB,QAAQ,OAAQ,EAAE,EAC1D,KAAK,iBAAmBA,EAAO,kBAAoB,IACnD,KAAK,KAAOA,EAAO,IACrB,CAiBA,MAAa,MACX,EACA,EACA,EACA,EAA8B,CAAC,EACF,CAC7B,GAAI,KAAK,SAAW,IAAA,GAAW,MAAME,EAAc,cAAc,EACjE,IAAMC,EAAUC,EAAQ,SAAW,QAC7BC,EAAWD,EAAQ,UAAY,iBAC/BE,EAAU,MAAMd,EACpB,KAAK,OACLe,EACAvB,EACAK,EAAWH,CAAO,EAClB,IAAA,GACA,SAASmB,GACX,EACMG,EAAM,GAAG,KAAK,QAAQ,gCAAgC,mBAAmBL,CAAO,EAAE,GAAG,mBAAmBE,CAAQ,EAAE,GAAG,mBAAmBrB,CAAM,EAAE,MAElJyB,EACAC,EACAC,EACJ,GAAI,CACF,IAAMC,EAAW,MAAMC,EACrB,KAAK,UACLL,EACA,CACE,OAAQ,OACR,QAASM,EAAoB,KAAK,KAAM,CACtC,KAAM,GACN,SAAU,CAAE,OAAQ,kBAAmB,CACzC,CAAC,EACD,KAAM,KAAK,UAAUR,CAAO,CAC9B,EACA,KAAK,gBACP,EACA,EAASM,EAAS,OAClB,EAAKA,EAAS,GACd,EAAOF,EAAK,MAAMK,EAAiBH,CAAQ,EAAI,IAAA,EACjD,MAAQ,CACN,OAAO,IACT,CAUA,OARKF,EAQEM,EAAUL,CAAI,EAPfN,IAAa,iBAAmBI,IAAW,KAAOA,IAAW,KACxD,CAAE,OAAQ,UAAW,KAAM,CAAC,CAAE,UAAW,GAAI,CAAC,CAAE,EAElD,IAKX,CACF,EAEA,SAASO,EAAU,EAAmC,CACpD,GAAI,OAAOL,GAAS,WAAYA,EAAe,OAAO,KACtD,IAAMM,EAAQN,EAA4B,KAO1C,MALE,CAAC,MAAM,QAAQM,CAAI,GACnB,OAAQA,EAAK,EAAE,EAA0C,WAAc,SAEhE,KAEFN,CACT,C"}
package/dist/index.cjs CHANGED
@@ -1732,7 +1732,10 @@ function isCurrentContractDexMessage(message, method) {
1732
1732
  }
1733
1733
  /**
1734
1734
  * Reduces an EIP-712 approval `message` to the DTO the chain actually receives: drops the signing
1735
- * envelope (`signature`, `prefix`, `types`, `domain`) and transport metadata (`trace`).
1735
+ * envelope (`signature`, `prefix`, `types`, `domain`), the `dtoOperation` it was signed to call (a
1736
+ * DryRun runs the DTO inside a wrapper signed for `DryRun`; left inside, the chain refuses it:
1737
+ * "signed to call ...:RequestTrade, but the current operation is ...:DryRun") and transport
1738
+ * metadata (`trace`).
1736
1739
  */
1737
1740
  function toChainDto(message) {
1738
1741
  const chainDto = { ...message };
@@ -1741,6 +1744,7 @@ function toChainDto(message) {
1741
1744
  "prefix",
1742
1745
  "types",
1743
1746
  "domain",
1747
+ "dtoOperation",
1744
1748
  "trace"
1745
1749
  ]) delete chainDto[field];
1746
1750
  return chainDto;
@@ -1750,13 +1754,15 @@ function toChainDto(message) {
1750
1754
  * `{ callerPublicKey, method, dto: <inner DTO object>, uniqueKey, signature }`. The inner DTO
1751
1755
  * travels unsigned and as an object; no `signerPublicKey` is sent because the signature already
1752
1756
  * recovers it (the gateway rejects it as REDUNDANT_SIGNER_PUBLIC_KEY). The wrapper carries its own
1753
- * `uniqueKey`, distinct from the DTO's.
1757
+ * `uniqueKey`, distinct from the DTO's, and its own `dtoOperation` (`<Contract>:DryRun`); the inner DTO
1758
+ * carries none of the signing envelope or the `dtoOperation` of the call it simulates (see
1759
+ * {@link toChainDto}), whatever the caller passes, so a signed DTO can be handed in as is.
1754
1760
  */
1755
1761
  function buildDryRunWrapper(signer, callerPublicKey, method, dto, uniqueKeyPrefix = DRY_RUN_UNIQUE_KEY_PREFIX, contractPath = "asset/dexv3-contract") {
1756
1762
  return (0, _gala_chain_gala_core_sdk.signPreSend)(signer, contractPath, DRY_RUN_METHOD, {
1757
1763
  callerPublicKey,
1758
1764
  method,
1759
- dto,
1765
+ dto: toChainDto(dto),
1760
1766
  uniqueKey: (0, _gala_chain_gala_core_sdk.createUniqueKey)(uniqueKeyPrefix)
1761
1767
  });
1762
1768
  }