@gala-chain/gswap-sdk 0.0.9 → 1.0.0-rc.3

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.
package/dist/index.cjs CHANGED
@@ -30,25 +30,38 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ ALL_FEE_TIERS: () => ALL_FEE_TIERS,
33
34
  Assets: () => Assets,
34
- Bundler: () => Bundler,
35
- EventSocketClient: () => EventSocketClient,
36
- Events: () => Events,
35
+ BrowserWalletSigner: () => BrowserWalletSigner,
36
+ ChainGateway: () => ChainGateway,
37
37
  FEE_TIER: () => FEE_TIER,
38
+ GC_MAX_TICK: () => GC_MAX_TICK,
39
+ GC_MIN_TICK: () => GC_MIN_TICK,
40
+ GSWAP_ENVIRONMENTS: () => GSWAP_ENVIRONMENTS,
38
41
  GSwap: () => GSwap,
39
42
  GSwapSDKError: () => GSwapSDKError,
40
43
  GalaWalletSigner: () => GalaWalletSigner,
41
- PendingTransaction: () => PendingTransaction,
42
44
  Pools: () => Pools,
43
45
  Positions: () => Positions,
44
46
  PrivateKeySigner: () => PrivateKeySigner,
45
47
  Quoting: () => Quoting,
48
+ SubmittedTransaction: () => SubmittedTransaction,
46
49
  Swaps: () => Swaps,
47
- TradeEventEmitter: () => TradeEventEmitter,
48
- compareTokens: () => compareTokens,
49
- getTokenOrdering: () => getTokenOrdering,
50
+ Symbols: () => Symbols,
51
+ TICK_SPACING_BY_FEE: () => TICK_SPACING_BY_FEE,
52
+ alignTickDown: () => alignTickDown,
53
+ alignTickUp: () => alignTickUp,
54
+ assertTickRange: () => assertTickRange,
55
+ compositeKeyOf: () => compositeKeyOf,
56
+ getObjectProperty: () => getObjectProperty,
57
+ getStringProperty: () => getStringProperty,
58
+ orderSymbols: () => orderSymbols,
59
+ parseJson: () => parseJson,
50
60
  parseTokenClassKey: () => parseTokenClassKey,
51
- stringifyTokenClassKey: () => stringifyTokenClassKey,
61
+ readOrderedSymbols: () => readOrderedSymbols,
62
+ sqrtPriceToTick: () => sqrtPriceToTick,
63
+ tickFromPrice: () => tickFromPrice,
64
+ tickToSqrtPrice: () => tickToSqrtPrice,
52
65
  validateFee: () => validateFee,
53
66
  validateNumericAmount: () => validateNumericAmount,
54
67
  validatePriceValues: () => validatePriceValues,
@@ -60,41 +73,99 @@ __export(index_exports, {
60
73
  module.exports = __toCommonJS(index_exports);
61
74
 
62
75
  // src/utils/validation.ts
63
- var import_bignumber = __toESM(require("bignumber.js"), 1);
76
+ var import_bignumber2 = __toESM(require("bignumber.js"), 1);
64
77
 
65
78
  // src/classes/gswap_sdk_error.ts
66
79
  var GSwapSDKError = class _GSwapSDKError extends Error {
67
- constructor(message, code, details) {
80
+ constructor(message, code, details, chainMessage, retryAfterMs) {
68
81
  super(message);
69
82
  this.code = code;
70
83
  this.details = details;
71
84
  this.name = "GSwapSDKError";
72
- }
85
+ this.chainMessage = chainMessage;
86
+ this.retryAfterMs = retryAfterMs;
87
+ }
88
+ code;
89
+ details;
90
+ chainMessage;
91
+ retryAfterMs;
92
+ /** Create the standard missing-signer error. */
73
93
  static noSignerError() {
74
94
  return new _GSwapSDKError(
75
95
  "This method requires a signer. Please provide a signer to the GSwap constructor.",
76
96
  "NO_SIGNER"
77
97
  );
78
98
  }
99
+ /** Create the error used when confirming a position write without a configured wallet address. */
100
+ static noWalletAddressError() {
101
+ return new _GSwapSDKError(
102
+ "Cannot confirm this write: no walletAddress was provided to the GSwap constructor, so the resulting position cannot be looked up to confirm it landed on chain.",
103
+ "NO_WALLET_ADDRESS"
104
+ );
105
+ }
106
+ /** Create the error used when no pool can quote the requested pair. */
107
+ static noPoolAvailableError(tokenIn, tokenOut, fee) {
108
+ return new _GSwapSDKError("No pool is available for the requested pair.", "NO_POOL_AVAILABLE", {
109
+ tokenIn,
110
+ tokenOut,
111
+ ...fee === void 0 ? {} : { fee }
112
+ });
113
+ }
114
+ /** Create the error used when a pool lacks liquidity for a quote. */
115
+ static insufficientLiquidityError(tokenIn, tokenOut, fee) {
116
+ return new _GSwapSDKError(
117
+ "The selected pool has insufficient liquidity for the requested quote.",
118
+ "INSUFFICIENT_LIQUIDITY",
119
+ { tokenIn, tokenOut, ...fee === void 0 ? {} : { fee } }
120
+ );
121
+ }
122
+ /** Convert a chain-gateway bounce into a typed SDK error. */
123
+ static fromGatewayBounce(code, message, status, retryAfterMs) {
124
+ const details = { status, chainMessage: message };
125
+ if (retryAfterMs !== void 0) details["retryAfterMs"] = retryAfterMs;
126
+ const sdkMessage = code === "CHAIN_DISPATCH_FAILED" ? message : `Gateway rejected ${code}: ${message}`;
127
+ return new _GSwapSDKError(sdkMessage, code, details, message, retryAfterMs);
128
+ }
129
+ /** Convert a GalaChain `Status: 0` response into a typed SDK error. */
130
+ static fromChainError(errorKey, message, details) {
131
+ return new _GSwapSDKError(
132
+ `GalaChain error ${errorKey}: ${message}`,
133
+ errorKey,
134
+ { ...details, errorKey, message },
135
+ message
136
+ );
137
+ }
138
+ /** Create the error used when a token is not registered by the current DEX contract. */
139
+ static unknownTokenError(token) {
140
+ const tokenText = typeof token === "string" ? token : JSON.stringify(token);
141
+ return new _GSwapSDKError(
142
+ `Token ${tokenText} has no trading symbol on the new DEX contract (no gcdex pool includes it yet).`,
143
+ "UNKNOWN_TOKEN",
144
+ { token }
145
+ );
146
+ }
147
+ /** Create the error used when apply-results confirmation does not arrive in time. */
148
+ static confirmationTimeoutError(uniqueKey, cause) {
149
+ return new _GSwapSDKError(
150
+ "Transaction confirmation timed out; the trade may still apply. Confirm the same uniqueKey again later instead of resubmitting.",
151
+ "CONFIRMATION_TIMEOUT",
152
+ { uniqueKey, ...cause === void 0 ? {} : { cause } }
153
+ );
154
+ }
155
+ /** Convert an unexpected HTTP response into an SDK error. */
79
156
  static async fromErrorResponse(url, response) {
80
157
  const bodyText = await response.text();
81
- const bodyJson = (() => {
82
- try {
83
- return JSON.parse(bodyText);
84
- } catch {
85
- return void 0;
86
- }
87
- })();
88
- const errorKey = typeof bodyJson === "object" && bodyJson && "error" in bodyJson && typeof bodyJson.error === "object" && bodyJson.error && "ErrorKey" in bodyJson.error && typeof bodyJson.error.ErrorKey === "string" ? bodyJson.error.ErrorKey : void 0;
89
- const message = typeof bodyJson === "object" && bodyJson && "error" in bodyJson && typeof bodyJson.error === "object" && bodyJson.error && "Message" in bodyJson.error && typeof bodyJson.error.Message === "string" ? bodyJson.error.Message : void 0;
90
- if (errorKey && message) {
91
- return new _GSwapSDKError(`GalaChain Error ${errorKey} from ${url}: ${message}`, errorKey, {
92
- message,
158
+ const bodyJson = parseJson(bodyText);
159
+ const errorObject = getObjectProperty(bodyJson, "error");
160
+ const errorKey = getStringProperty(errorObject, "ErrorKey");
161
+ const message = getStringProperty(errorObject, "Message");
162
+ if (errorKey !== void 0 && message !== void 0) {
163
+ return new _GSwapSDKError(
164
+ `GalaChain Error ${errorKey} from ${url}: ${message}`,
93
165
  errorKey,
94
- status: response.status,
95
- body: bodyJson,
96
- url
97
- });
166
+ { message, errorKey, status: response.status, body: bodyJson, url },
167
+ message
168
+ );
98
169
  }
99
170
  return new _GSwapSDKError(`Unexpected HTTP Error ${response.status} from ${url}`, "HTTP_ERROR", {
100
171
  status: response.status,
@@ -102,49 +173,125 @@ var GSwapSDKError = class _GSwapSDKError extends Error {
102
173
  url
103
174
  });
104
175
  }
105
- static incorrectTokenOrderingError(specifiedToken0, specifiedToken1) {
106
- return new _GSwapSDKError(
107
- "Token ordering is incorrect. token0 should sort below token1.",
108
- "INCORRECT_TOKEN_ORDERING",
109
- {
110
- specifiedToken0,
111
- specifiedToken1
112
- }
113
- );
114
- }
115
- static transactionWaitTimeoutError(txId) {
116
- return new _GSwapSDKError("Transaction wait timed out.", "TRANSACTION_WAIT_TIMEOUT", {
117
- txId
118
- });
119
- }
120
- static transactionWaitFailedError(txId, detail) {
121
- const transactionHash = "transactionId" in detail && typeof detail.transactionId === "string" ? detail.transactionId : void 0;
122
- const { transactionId, ...rest } = detail;
123
- return new _GSwapSDKError("Transaction wait failed.", "TRANSACTION_WAIT_FAILED", {
124
- txId,
125
- transactionHash,
126
- ...rest
127
- });
176
+ };
177
+ function getObjectProperty(value, property) {
178
+ if (typeof value !== "object" || value === null || !(property in value)) return void 0;
179
+ const propertyValue = value[property];
180
+ return typeof propertyValue === "object" && propertyValue !== null ? propertyValue : void 0;
181
+ }
182
+ function getStringProperty(value, property) {
183
+ if (typeof value !== "object" || value === null || !(property in value)) return void 0;
184
+ const propertyValue = value[property];
185
+ return typeof propertyValue === "string" ? propertyValue : void 0;
186
+ }
187
+ function parseJson(text) {
188
+ try {
189
+ return JSON.parse(text);
190
+ } catch {
191
+ return void 0;
128
192
  }
129
- static socketConnectionRequiredError() {
130
- return new _GSwapSDKError(
131
- "This method requires a socket connection. Did you call connectSocket()?",
132
- "SOCKET_CONNECTION_REQUIRED"
193
+ }
194
+
195
+ // src/types/fees.ts
196
+ var FEE_TIER = /* @__PURE__ */ ((FEE_TIER2) => {
197
+ FEE_TIER2[FEE_TIER2["PERCENT_00_00"] = 0] = "PERCENT_00_00";
198
+ FEE_TIER2[FEE_TIER2["PERCENT_00_05"] = 500] = "PERCENT_00_05";
199
+ FEE_TIER2[FEE_TIER2["PERCENT_00_30"] = 3e3] = "PERCENT_00_30";
200
+ FEE_TIER2[FEE_TIER2["PERCENT_01_00"] = 1e4] = "PERCENT_01_00";
201
+ return FEE_TIER2;
202
+ })(FEE_TIER || {});
203
+ var TICK_SPACING_BY_FEE = {
204
+ [0 /* PERCENT_00_00 */]: 200,
205
+ [500 /* PERCENT_00_05 */]: 10,
206
+ [3e3 /* PERCENT_00_30 */]: 60,
207
+ [1e4 /* PERCENT_01_00 */]: 200
208
+ };
209
+ var ALL_FEE_TIERS = [
210
+ 0 /* PERCENT_00_00 */,
211
+ 500 /* PERCENT_00_05 */,
212
+ 3e3 /* PERCENT_00_30 */,
213
+ 1e4 /* PERCENT_01_00 */
214
+ ];
215
+
216
+ // src/utils/ticks.ts
217
+ var import_bignumber = __toESM(require("bignumber.js"), 1);
218
+ var import_decimal = require("decimal.js");
219
+ var GC_MIN_TICK = -887272;
220
+ var GC_MAX_TICK = 887272;
221
+ var DexMath = import_bignumber.default.clone({
222
+ DECIMAL_PLACES: 60,
223
+ ROUNDING_MODE: import_bignumber.default.ROUND_DOWN
224
+ });
225
+ var DexDecimal = import_decimal.Decimal.clone({ precision: 60 });
226
+ function tickToSqrtPrice(tick) {
227
+ return new import_bignumber.default(new DexDecimal(1.0001).pow(tick / 2).toString());
228
+ }
229
+ function sqrtPriceToTick(sqrtPrice) {
230
+ let value;
231
+ try {
232
+ value = new DexMath(sqrtPrice);
233
+ } catch {
234
+ return Number.NaN;
235
+ }
236
+ if (!value.isFinite() || value.isLessThanOrEqualTo(0)) return Number.NaN;
237
+ const approximate = Math.log(value.toNumber() ** 2) / Math.log(1.0001);
238
+ if (!Number.isFinite(approximate)) return Number.NaN;
239
+ let tick = Math.min(GC_MAX_TICK, Math.max(GC_MIN_TICK, Math.floor(approximate)));
240
+ while (tick < GC_MAX_TICK && tickToSqrtPrice(tick + 1).isLessThanOrEqualTo(value)) tick += 1;
241
+ while (tick > GC_MIN_TICK && tickToSqrtPrice(tick).isGreaterThan(value)) tick -= 1;
242
+ return tick;
243
+ }
244
+ function tickFromPrice(price) {
245
+ const priceText = typeof price === "string" ? price : typeof price === "number" ? price.toString() : import_bignumber.default.isBigNumber(price) ? price.toString() : "NaN";
246
+ const value = new DexDecimal(priceText);
247
+ if (!value.isFinite() || value.lessThanOrEqualTo(0)) return Number.NaN;
248
+ if (!Number.isFinite(value.toNumber())) return Number.NaN;
249
+ const tick = value.ln().dividedBy(new DexDecimal(1.0001).ln()).floor().toNumber();
250
+ return Number.isFinite(tick) ? tick : Number.NaN;
251
+ }
252
+ function alignTickDown(tick, spacing) {
253
+ assertSpacing(spacing);
254
+ return Math.floor(tick / spacing) * spacing;
255
+ }
256
+ function alignTickUp(tick, spacing) {
257
+ assertSpacing(spacing);
258
+ return Math.ceil(tick / spacing) * spacing;
259
+ }
260
+ function assertTickRange(lower, upper, fee) {
261
+ const spacing = TICK_SPACING_BY_FEE[fee];
262
+ if (spacing === void 0) throw new RangeError(`Unsupported fee tier: ${fee}`);
263
+ if (!Number.isInteger(lower) || !Number.isInteger(upper)) {
264
+ throw new RangeError("Ticks must be integers");
265
+ }
266
+ if (lower < GC_MIN_TICK || upper > GC_MAX_TICK || lower >= upper) {
267
+ throw new RangeError(
268
+ `Tick range must satisfy ${GC_MIN_TICK} <= lower < upper <= ${GC_MAX_TICK}`
133
269
  );
134
270
  }
135
- static noPoolAvailableError(tokenIn, tokenOut, fee) {
136
- const message = fee !== void 0 ? `No pool available for the specified token pair at fee tier ${fee}` : "No pools available for the specified token pair";
137
- return new _GSwapSDKError(message, "NO_POOL_AVAILABLE", {
138
- tokenIn,
139
- tokenOut,
140
- fee
141
- });
271
+ if (lower % spacing !== 0 || upper % spacing !== 0) {
272
+ throw new RangeError(`Ticks must be aligned to spacing ${spacing}`);
142
273
  }
143
- };
274
+ }
275
+ function assertSpacing(spacing) {
276
+ if (!Number.isInteger(spacing) || spacing <= 0)
277
+ throw new RangeError("Tick spacing must be positive");
278
+ }
144
279
 
145
280
  // src/utils/validation.ts
146
281
  function validateNumericAmount(amount, parameterName, allowZero = false) {
147
- const bnAmount = (0, import_bignumber.default)(amount);
282
+ if (typeof amount === "number") {
283
+ throw new GSwapSDKError(
284
+ `Invalid ${parameterName}: use a decimal string or BigNumber, not a JavaScript number`,
285
+ "VALIDATION_ERROR",
286
+ { type: "INVALID_NUMERIC_AMOUNT", parameterName, value: amount, reason: "number_input" }
287
+ );
288
+ }
289
+ let bnAmount;
290
+ try {
291
+ bnAmount = (0, import_bignumber2.default)(amount);
292
+ } catch {
293
+ bnAmount = (0, import_bignumber2.default)(NaN);
294
+ }
148
295
  if (!bnAmount.isFinite()) {
149
296
  throw new GSwapSDKError(
150
297
  `Invalid ${parameterName}: must be a finite number`,
@@ -179,10 +326,24 @@ function validateNumericAmount(amount, parameterName, allowZero = false) {
179
326
  }
180
327
  }
181
328
  function validatePriceValues(spotPrice, lowerPrice, upperPrice) {
182
- const bnSpotPrice = (0, import_bignumber.default)(spotPrice);
183
- const bnLowerPrice = (0, import_bignumber.default)(lowerPrice);
184
- let bnUpperPrice = (0, import_bignumber.default)(upperPrice);
185
- bnUpperPrice = bnUpperPrice.isFinite() ? bnUpperPrice : (0, import_bignumber.default)(1e18);
329
+ if ([spotPrice, lowerPrice, upperPrice].some((value) => typeof value === "number")) {
330
+ throw new GSwapSDKError(
331
+ "Invalid price values: use decimal strings or BigNumber values, not JavaScript numbers",
332
+ "VALIDATION_ERROR",
333
+ { type: "INVALID_NUMERIC_AMOUNT", reason: "number_input" }
334
+ );
335
+ }
336
+ const parse = (value) => {
337
+ try {
338
+ return (0, import_bignumber2.default)(value);
339
+ } catch {
340
+ return (0, import_bignumber2.default)(NaN);
341
+ }
342
+ };
343
+ const bnSpotPrice = parse(spotPrice);
344
+ const bnLowerPrice = parse(lowerPrice);
345
+ let bnUpperPrice = parse(upperPrice);
346
+ bnUpperPrice = bnUpperPrice.isFinite() ? bnUpperPrice : (0, import_bignumber2.default)(1e18);
186
347
  if (!bnSpotPrice.isFinite() || !bnLowerPrice.isFinite() || !bnUpperPrice.isFinite() || !bnSpotPrice.isPositive() || !bnLowerPrice.isPositive() || !bnUpperPrice.isPositive()) {
187
348
  throw new GSwapSDKError(
188
349
  "Invalid price values: all prices must be finite and positive",
@@ -239,26 +400,30 @@ function validateTickRange(tickLower, tickUpper) {
239
400
  }
240
401
  );
241
402
  }
242
- if (tickLower < -886800 || tickUpper > 886800) {
403
+ if (tickLower < GC_MIN_TICK || tickUpper > GC_MAX_TICK) {
243
404
  throw new GSwapSDKError(
244
- "Invalid tick range: ticks must be between -886800 and 886800",
405
+ `Invalid tick range: ticks must be between ${GC_MIN_TICK} and ${GC_MAX_TICK}`,
245
406
  "VALIDATION_ERROR",
246
407
  {
247
408
  type: "INVALID_TICK_BOUNDS",
248
409
  tickLower,
249
410
  tickUpper,
250
- minTick: -886800,
251
- maxTick: 886800
411
+ minTick: GC_MIN_TICK,
412
+ maxTick: GC_MAX_TICK
252
413
  }
253
414
  );
254
415
  }
255
416
  }
256
417
  function validateFee(fee) {
257
- if (!Number.isInteger(fee) || fee < 0) {
258
- throw new GSwapSDKError("Invalid fee: must be a non-negative integer", "VALIDATION_ERROR", {
259
- type: "INVALID_FEE",
260
- value: fee
261
- });
418
+ if (!ALL_FEE_TIERS.some((tier) => Number(tier) === fee)) {
419
+ throw new GSwapSDKError(
420
+ "Invalid fee tier: must be one of 0, 500, 3000, or 10000",
421
+ "VALIDATION_ERROR",
422
+ {
423
+ type: "INVALID_FEE",
424
+ value: fee
425
+ }
426
+ );
262
427
  }
263
428
  }
264
429
  function validateTickSpacing(tickSpacing) {
@@ -302,6 +467,8 @@ var Assets = class {
302
467
  this.dexBackendBaseUrl = dexBackendBaseUrl;
303
468
  this.httpClient = httpClient;
304
469
  }
470
+ dexBackendBaseUrl;
471
+ httpClient;
305
472
  /**
306
473
  * Gets all token assets owned by a user with their balances.
307
474
  * @param ownerAddress - The wallet address to get assets for.
@@ -320,408 +487,555 @@ var Assets = class {
320
487
  async getUserAssets(ownerAddress, page = 1, limit = 10) {
321
488
  validateWalletAddress(ownerAddress);
322
489
  if (!Number.isInteger(page) || page < 1) {
323
- throw new Error("Invalid page: must be a positive integer");
490
+ throw new GSwapSDKError("Invalid page: must be a positive integer", "VALIDATION_ERROR", {
491
+ page
492
+ });
324
493
  }
325
494
  if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
326
- throw new Error("Invalid limit: must be an integer between 1 and 100");
495
+ throw new GSwapSDKError(
496
+ "Invalid limit: must be an integer between 1 and 100",
497
+ "VALIDATION_ERROR",
498
+ { limit }
499
+ );
327
500
  }
328
- const json = await this.httpClient.sendGetRequest(this.dexBackendBaseUrl, "/user/assets", "", {
329
- address: ownerAddress,
330
- page: page.toString(),
331
- limit: limit.toString()
332
- });
333
- return {
334
- tokens: json.data.token.map((token) => ({
335
- ...token,
336
- decimals: parseInt(token.decimals, 10)
337
- })) || [],
338
- count: json.data.count || 0
339
- };
501
+ const json = await this.httpClient.sendGetRequest(
502
+ this.dexBackendBaseUrl,
503
+ "/user/assets",
504
+ "",
505
+ { address: ownerAddress, page: page.toString(), limit: limit.toString() }
506
+ );
507
+ return parseUserAssets(json);
340
508
  }
341
509
  };
342
-
343
- // src/classes/bundler.ts
344
- var import_crypto = __toESM(require("crypto"), 1);
345
-
346
- // src/utils/debug.ts
347
- var import_debug = __toESM(require("debug"), 1);
348
- var debugLog = (0, import_debug.default)("GSWAP_SDK");
349
-
350
- // src/classes/event_socket_client.ts
351
- var import_events = require("events");
352
- var import_socket = require("socket.io-client");
353
- var TradeEventEmitter = class extends import_events.EventEmitter {
354
- };
355
- var EventSocketClient = class extends TradeEventEmitter {
356
- socket = null;
357
- bundlerUrl;
358
- constructor(bundlerUrl) {
359
- super();
360
- this.bundlerUrl = bundlerUrl;
361
- }
362
- connect() {
363
- return new Promise((resolve, reject) => {
364
- var _a;
365
- if ((_a = this.socket) == null ? void 0 : _a.connected) {
366
- resolve();
367
- return;
510
+ function parseUserAssets(value) {
511
+ const data = getObjectProperty(value, "data");
512
+ const tokens = data == null ? void 0 : data["token"];
513
+ if (!Array.isArray(tokens) || !tokens.every(isRawUserAsset)) {
514
+ throw new GSwapSDKError(
515
+ "Backend returned an invalid user assets response.",
516
+ "INVALID_CHAIN_RESPONSE",
517
+ {
518
+ data: value
368
519
  }
369
- this.socket = (0, import_socket.io)(this.bundlerUrl, {
370
- transports: ["websocket"],
371
- autoConnect: true
372
- });
373
- this.socket.on("connect", () => {
374
- console.log("Connected to bundler socket:", this.bundlerUrl);
375
- this.emit("connect");
376
- resolve();
377
- });
378
- this.socket.on("connect_error", (error) => {
379
- console.error("Failed to connect to bundler socket:", error);
380
- this.emit("error", error);
381
- reject(error);
382
- });
383
- this.socket.on("disconnect", (reason) => {
384
- console.log("Disconnected from bundler socket:", reason);
385
- this.emit("disconnect", reason);
386
- });
387
- this.socket.onAny((eventName, ...args) => {
388
- var _a2;
389
- if (typeof eventName === "string") {
390
- const [responseData] = args;
391
- this.emit("transaction", eventName, {
392
- ...responseData,
393
- transactionHash: (_a2 = responseData.data) == null ? void 0 : _a2.transactionId
394
- });
395
- }
396
- });
397
- });
520
+ );
398
521
  }
399
- disconnect() {
400
- if (this.socket) {
401
- this.socket.disconnect();
402
- this.socket = null;
522
+ const count = data == null ? void 0 : data["count"];
523
+ return {
524
+ tokens: tokens.map((token) => ({ ...token, decimals: parseInt(token.decimals, 10) })),
525
+ count: typeof count === "number" ? count : 0
526
+ };
527
+ }
528
+ function isRawUserAsset(value) {
529
+ if (typeof value !== "object" || value === null) return false;
530
+ const entry = value;
531
+ return typeof entry["image"] === "string" && typeof entry["name"] === "string" && typeof entry["decimals"] === "string" && /^\d+$/u.test(entry["decimals"]) && typeof entry["verify"] === "boolean" && typeof entry["symbol"] === "string" && typeof entry["quantity"] === "string";
532
+ }
533
+
534
+ // src/utils/transport.ts
535
+ async function requestWithTimeout(requestor, url, options, timeoutMs) {
536
+ const controller = new AbortController();
537
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
538
+ const externalSignal = options.signal;
539
+ const abortFromCaller = () => controller.abort();
540
+ externalSignal == null ? void 0 : externalSignal.addEventListener("abort", abortFromCaller, { once: true });
541
+ try {
542
+ return await requestor(url, { ...options, signal: controller.signal });
543
+ } catch (error) {
544
+ if (controller.signal.aborted) {
545
+ throw new GSwapSDKError(`Request timed out after ${timeoutMs} ms.`, "REQUEST_TIMEOUT", {
546
+ url,
547
+ timeoutMs,
548
+ cause: error
549
+ });
403
550
  }
551
+ throw error;
552
+ } finally {
553
+ clearTimeout(timer);
554
+ externalSignal == null ? void 0 : externalSignal.removeEventListener("abort", abortFromCaller);
404
555
  }
405
- isConnected() {
406
- var _a;
407
- return ((_a = this.socket) == null ? void 0 : _a.connected) ?? false;
408
- }
409
- };
556
+ }
557
+ async function readResponseBody(response) {
558
+ const text = await response.text();
559
+ return parseJson(text) ?? text;
560
+ }
561
+ function readHeader(response, name) {
562
+ var _a;
563
+ const headers = response.headers;
564
+ if (headers === void 0) return void 0;
565
+ if (hasHeaderGetter(headers)) {
566
+ const value2 = headers.get(name);
567
+ return value2 === null || value2 === "" ? void 0 : value2;
568
+ }
569
+ const value = (_a = Object.entries(headers).find(([key]) => key.toLowerCase() === name.toLowerCase())) == null ? void 0 : _a[1];
570
+ return value === void 0 || value === "" ? void 0 : value;
571
+ }
572
+ function readRetryAfter(response) {
573
+ const raw = readHeader(response, "retry-after");
574
+ if (raw === void 0) return void 0;
575
+ const seconds = Number(raw);
576
+ return Number.isFinite(seconds) && seconds >= 0 ? seconds * 1e3 : void 0;
577
+ }
578
+ function readBlockNumberHeader(response, headerName = "x-block-number") {
579
+ const raw = readHeader(response, headerName);
580
+ if (raw === void 0) return void 0;
581
+ const blockNumber = Number(raw);
582
+ return Number.isInteger(blockNumber) && blockNumber >= 0 ? blockNumber : void 0;
583
+ }
584
+ function hasHeaderGetter(headers) {
585
+ return "get" in headers && typeof headers.get === "function";
586
+ }
410
587
 
411
- // src/classes/tx_waiter.ts
412
- var TransactionWaiter = class {
413
- enabled = false;
414
- promiseInfoForTxId = /* @__PURE__ */ new Map();
415
- setEnabled(enabled) {
416
- this.enabled = enabled;
417
- if (!enabled) {
418
- for (const [txId, promiseInfo] of this.promiseInfoForTxId.entries()) {
419
- clearTimeout(promiseInfo.timeoutId);
420
- promiseInfo.reject(
421
- GSwapSDKError.transactionWaitFailedError(txId, {
422
- message: "Transaction waiter disabled"
423
- })
588
+ // src/classes/submitted_transaction.ts
589
+ var SubmittedTransaction = class {
590
+ method;
591
+ uniqueKey;
592
+ transactionId;
593
+ blockNumber;
594
+ mode;
595
+ /** The synchronous gateway result, or `undefined` when `mode === 'queued'`. */
596
+ result;
597
+ backendBaseUrl;
598
+ requestor;
599
+ requestTimeoutMs;
600
+ positionConfirmation;
601
+ /** Construct a submitted transaction from a successful gateway response.
602
+ *
603
+ * @example
604
+ * ```ts
605
+ * const transaction = new SubmittedTransaction({
606
+ * method: 'RequestTrade', mode: 'queued', uniqueKey: 'swap-1',
607
+ * transactionId: 'request-tx', result: undefined,
608
+ * dexBackendBaseUrl: 'https://dex.example', httpRequestor,
609
+ * });
610
+ * ```
611
+ */
612
+ constructor(options) {
613
+ this.method = options.method;
614
+ this.uniqueKey = options.uniqueKey;
615
+ this.transactionId = options.transactionId;
616
+ this.blockNumber = options.blockNumber ?? null;
617
+ this.mode = options.mode;
618
+ this.result = options.result;
619
+ this.backendBaseUrl = options.dexBackendBaseUrl.replace(/\/$/u, "");
620
+ this.requestor = options.httpRequestor;
621
+ this.requestTimeoutMs = options.requestTimeoutMs ?? 3e4;
622
+ this.positionConfirmation = options.positionConfirmation;
623
+ }
624
+ /** Confirm a queued RequestTrade or prove the affected position for a liquidity write.
625
+ *
626
+ * For RequestTrade, calling this method is required to learn the outcome: the queued
627
+ * acknowledgement proves acceptance, not execution.
628
+ *
629
+ * @example
630
+ * ```ts
631
+ * const applied = await submitted.confirm({ initialDelayMs: 3_000 });
632
+ * ```
633
+ */
634
+ async confirm(options) {
635
+ if (this.method === "RequestTrade") {
636
+ return this.confirmApplied(options);
637
+ }
638
+ return this.confirmPosition(options);
639
+ }
640
+ async confirmApplied(options) {
641
+ const schedule = createConfirmationSchedule(options, 3e3);
642
+ await schedule.waitForRead();
643
+ const url = `${this.backendBaseUrl}/v1/chain/apply-results/${encodeURIComponent(this.uniqueKey)}?channel=asset&contract=dex-contract`;
644
+ let lastError;
645
+ while (true) {
646
+ let response;
647
+ let body;
648
+ try {
649
+ response = await requestWithTimeout(
650
+ this.requestor,
651
+ url,
652
+ { method: "GET" },
653
+ this.requestTimeoutMs
424
654
  );
655
+ body = await readResponseBody(response);
656
+ } catch (error) {
657
+ lastError = error;
658
+ if (!await schedule.waitForRead()) break;
659
+ continue;
425
660
  }
426
- this.promiseInfoForTxId.clear();
427
- }
428
- }
429
- registerTxId(txId, timeoutMs) {
430
- if (this.promiseInfoForTxId.has(txId)) {
431
- throw new GSwapSDKError(
432
- `Transaction ID is already registered`,
433
- "TRANSACTION_ID_ALREADY_REGISTERED",
434
- { txId }
435
- );
436
- }
437
- if (!this.enabled) {
438
- return;
439
- }
440
- let resolve;
441
- let reject;
442
- const promise = new Promise((res, rej) => {
443
- resolve = res;
444
- reject = rej;
445
- });
446
- const timeoutId = setTimeout(() => {
447
- const promiseInfo = this.promiseInfoForTxId.get(txId);
448
- if (promiseInfo) {
449
- if (promiseInfo.waited) {
450
- promiseInfo.reject(GSwapSDKError.transactionWaitTimeoutError(txId));
451
- } else {
452
- promiseInfo.resolve({ txId, transactionHash: txId, Data: {} });
661
+ if (response.status === 200) {
662
+ return this.readAppliedResult(response, body, url);
663
+ }
664
+ if (response.status === 404 || response.status === 429 || isTransient(response.status)) {
665
+ if (response.status === 429) {
666
+ const retryAfter = readRetryAfter(response);
667
+ if (retryAfter !== void 0) {
668
+ if (!await schedule.waitForRead(retryAfter)) break;
669
+ continue;
670
+ }
453
671
  }
454
- this.promiseInfoForTxId.delete(txId);
672
+ } else {
673
+ throw new GSwapSDKError(bodyMessage(body), "CONFIRMATION_FAILED", {
674
+ status: response.status,
675
+ body,
676
+ url
677
+ });
455
678
  }
456
- }, timeoutMs);
457
- this.promiseInfoForTxId.set(txId, {
458
- promise,
459
- resolve,
460
- reject,
461
- timeoutId,
462
- waited: false
463
- });
464
- }
465
- wait(txId) {
466
- const promise = this.promiseInfoForTxId.get(txId);
467
- if (!promise) {
468
- throw new GSwapSDKError(
469
- `Transaction ID is not registered for waiting`,
470
- "TRANSACTION_ID_NOT_REGISTERED",
679
+ if (!await schedule.waitForRead()) break;
680
+ }
681
+ throw GSwapSDKError.confirmationTimeoutError(this.uniqueKey, lastError);
682
+ }
683
+ readAppliedResult(response, body, url) {
684
+ const record = asRecord(body);
685
+ const status = record == null ? void 0 : record["Status"];
686
+ const applyTransactionId = readHeader(response, "x-apply-transaction-id") ?? null;
687
+ const blockNumber = readBlockNumberHeader(response, "x-apply-block-number") ?? null;
688
+ if (status === 0) {
689
+ throw GSwapSDKError.fromChainError(
690
+ getStringProperty(record, "ErrorKey") ?? "CHAIN_ERROR",
691
+ getStringProperty(record, "Message") ?? "The chain rejected this trade.",
471
692
  {
472
- txId
693
+ uniqueKey: this.uniqueKey,
694
+ applyTransactionId,
695
+ blockNumber,
696
+ status: response.status,
697
+ body,
698
+ url
473
699
  }
474
700
  );
475
701
  }
476
- promise.waited = true;
477
- return promise.promise;
702
+ if (status !== 1) throw malformedConfirmation(response, body, url);
703
+ const result = parseTradeExecution(record == null ? void 0 : record["Data"]);
704
+ if (result === void 0) throw malformedConfirmation(response, body, url);
705
+ return {
706
+ uniqueKey: this.uniqueKey,
707
+ transactionId: applyTransactionId,
708
+ blockNumber,
709
+ requestTransactionId: this.transactionId,
710
+ result
711
+ };
478
712
  }
479
- notifySuccess(txId, data) {
480
- const promiseInfo = this.promiseInfoForTxId.get(txId);
481
- if (!promiseInfo) {
482
- return;
713
+ /** Confirm a liquidity write against the position's observable effect.
714
+ *
715
+ * @returns The position after the write, or `null` for a confirmed full close.
716
+ * @example
717
+ * ```ts
718
+ * const position = await submitted.confirm({ timeoutMs: 60_000 });
719
+ * ```
720
+ */
721
+ async confirmPosition(options) {
722
+ if (this.positionConfirmation === void 0) throw GSwapSDKError.noWalletAddressError();
723
+ const schedule = createConfirmationSchedule(options, 0);
724
+ await schedule.waitForRead();
725
+ while (true) {
726
+ const controller = new AbortController();
727
+ const timer = setTimeout(() => controller.abort(), this.requestTimeoutMs);
728
+ try {
729
+ const result = await this.positionConfirmation(controller.signal);
730
+ if (result.confirmed) return result.position;
731
+ } finally {
732
+ clearTimeout(timer);
733
+ }
734
+ if (!await schedule.waitForRead()) break;
483
735
  }
484
- clearTimeout(promiseInfo.timeoutId);
485
- promiseInfo.resolve({
486
- txId,
487
- transactionHash: data.transactionId,
488
- Data: data.Data
489
- });
490
- this.promiseInfoForTxId.delete(txId);
736
+ throw GSwapSDKError.confirmationTimeoutError(this.uniqueKey);
491
737
  }
492
- notifyFailure(txId, detail) {
493
- const promiseInfo = this.promiseInfoForTxId.get(txId);
494
- if (!promiseInfo) {
495
- return;
496
- }
497
- clearTimeout(promiseInfo.timeoutId);
498
- if (promiseInfo.waited) {
499
- promiseInfo.reject(GSwapSDKError.transactionWaitFailedError(txId, detail));
500
- } else {
501
- promiseInfo.resolve({ txId, transactionHash: txId, Data: {} });
738
+ };
739
+ function createConfirmationSchedule(options, defaultInitialDelayMs) {
740
+ const timeoutMs = Math.max(0, (options == null ? void 0 : options.timeoutMs) ?? 6e4);
741
+ const pollIntervalMs = Math.max(500, (options == null ? void 0 : options.pollIntervalMs) ?? 2e3);
742
+ const initialDelayMs = Math.max(0, (options == null ? void 0 : options.initialDelayMs) ?? defaultInitialDelayMs);
743
+ const deadline = Date.now() + timeoutMs;
744
+ let firstRead = true;
745
+ let attempt = 0;
746
+ return {
747
+ async waitForRead(retryAfterMs) {
748
+ if (firstRead) {
749
+ firstRead = false;
750
+ const remaining = Math.max(0, deadline - Date.now());
751
+ await delay(Math.min(initialDelayMs, remaining));
752
+ return true;
753
+ }
754
+ const backoff = Math.min(8e3, pollIntervalMs * 2 ** Math.min(attempt, 5));
755
+ attempt += 1;
756
+ const waitMs = retryAfterMs === void 0 ? jitter(backoff) : retryAfterMs;
757
+ if (Date.now() + waitMs > deadline) return false;
758
+ await delay(waitMs);
759
+ return true;
502
760
  }
503
- this.promiseInfoForTxId.delete(txId);
761
+ };
762
+ }
763
+ function parseTradeExecution(value) {
764
+ const record = asRecord(value);
765
+ if (record === void 0 || typeof record["token0"] !== "string" || typeof record["token1"] !== "string" || typeof record["fee"] !== "number") {
766
+ return void 0;
767
+ }
768
+ const optionalFields = [
769
+ "sell0Qty",
770
+ "sell1Qty",
771
+ "buy0Qty",
772
+ "buy1Qty",
773
+ "tradingFees",
774
+ "protocolFees",
775
+ "totalFees"
776
+ ];
777
+ for (const field of optionalFields) {
778
+ if (field in record && typeof record[field] !== "string") return void 0;
504
779
  }
505
- };
780
+ return {
781
+ token0: record["token0"],
782
+ token1: record["token1"],
783
+ fee: record["fee"],
784
+ ...Object.fromEntries(
785
+ optionalFields.flatMap(
786
+ (field) => typeof record[field] === "string" ? [[field, record[field]]] : []
787
+ )
788
+ )
789
+ };
790
+ }
791
+ function malformedConfirmation(response, body, url) {
792
+ return new GSwapSDKError(
793
+ "Transaction confirmation returned a malformed body",
794
+ "CONFIRMATION_FAILED",
795
+ { status: response.status, body, url }
796
+ );
797
+ }
798
+ function isTransient(status) {
799
+ return status === 500 || status === 502 || status === 503 || status === 504;
800
+ }
801
+ function bodyMessage(body) {
802
+ return getStringProperty(body, "message") ?? getStringProperty(body, "Message") ?? getStringProperty(getObjectProperty(body, "error"), "message") ?? getStringProperty(getObjectProperty(body, "error"), "Message") ?? (typeof body === "string" ? body : "Transaction confirmation failed");
803
+ }
804
+ function asRecord(value) {
805
+ return typeof value === "object" && value !== null ? value : void 0;
806
+ }
807
+ function delay(milliseconds) {
808
+ return new Promise((resolve) => setTimeout(resolve, milliseconds));
809
+ }
810
+ function jitter(milliseconds) {
811
+ return Math.max(0, Math.round(milliseconds * (0.9 + Math.random() * 0.2)));
812
+ }
506
813
 
507
- // src/classes/events.ts
508
- var Events = class _Events {
509
- globalSocketClient;
510
- globalWaitHelper = new TransactionWaiter();
511
- connectionPromise;
512
- static instance = new _Events();
513
- static tradeEventEmitterConstructor = EventSocketClient;
514
- /**
515
- * Establishes a global socket connection for real-time event streaming.
516
- * This connection is shared across all service instances.
517
- * @param bundlerBaseUrl - Optional bundler base URL. If not provided, uses default.
518
- * @returns The connected EventSocketClient.
519
- * @example
520
- * ```typescript
521
- * await eventsService.connectEventSocket();
522
- *
523
- * // Socket is now available for transaction updates
524
- * ```
525
- */
526
- async connectEventSocket(bundlerBaseUrl) {
527
- var _a;
528
- if (this.connectionPromise) {
529
- return this.connectionPromise;
530
- }
531
- if ((_a = this.globalSocketClient) == null ? void 0 : _a.isConnected()) {
532
- return this.globalSocketClient;
814
+ // src/classes/gateway.ts
815
+ var ChainGateway = class {
816
+ requestor;
817
+ walletAddress;
818
+ chainCallTimeoutMs;
819
+ requestTimeoutMs;
820
+ dexBackendBaseUrl;
821
+ /** Create a chain gateway client. */
822
+ constructor(options) {
823
+ this.dexBackendBaseUrl = trimTrailingSlash(options.dexBackendBaseUrl);
824
+ this.requestor = options.httpRequestor ?? fetch.bind(globalThis);
825
+ this.walletAddress = options.walletAddress;
826
+ this.chainCallTimeoutMs = options.chainCallTimeoutMs ?? 3e4;
827
+ this.requestTimeoutMs = this.chainCallTimeoutMs;
828
+ }
829
+ async submit(method, signedBody, options) {
830
+ if (method === "Trade") {
831
+ throw new GSwapSDKError(
832
+ "The synchronous Trade method was removed; swaps are queued through RequestTrade.",
833
+ "METHOD_NOT_SUPPORTED",
834
+ { method }
835
+ );
533
836
  }
534
- const url = bundlerBaseUrl ?? "https://bundle-backend-prod1.defi.gala.com";
535
- this.connectionPromise = (async () => {
536
- const client = new _Events.tradeEventEmitterConstructor(url);
537
- await client.connect();
538
- if (!this.globalWaitHelper) {
539
- this.globalWaitHelper = new TransactionWaiter();
837
+ const url = `${this.dexBackendBaseUrl}/v1/chain/asset/dex-contract/${method}`;
838
+ const headers = { "content-type": "application/json" };
839
+ const walletAddress = (options == null ? void 0 : options.walletAddress) ?? this.walletAddress;
840
+ if (walletAddress !== void 0) headers["X-Wallet-Address"] = walletAddress;
841
+ let response;
842
+ try {
843
+ response = await requestWithTimeout(
844
+ this.requestor,
845
+ url,
846
+ {
847
+ method: "POST",
848
+ headers,
849
+ body: JSON.stringify(signedBody)
850
+ },
851
+ this.chainCallTimeoutMs
852
+ );
853
+ } catch (error) {
854
+ if (error instanceof GSwapSDKError && error.code === "REQUEST_TIMEOUT") {
855
+ throw new GSwapSDKError(
856
+ "The submission outcome is unknown because the gateway request timed out. Confirm before retrying.",
857
+ "SUBMISSION_OUTCOME_UNKNOWN",
858
+ { uniqueKey: getStringProperty(signedBody, "uniqueKey") ?? "", cause: error }
859
+ );
540
860
  }
541
- client.on("transaction", this.internalHandleSocketMessage.bind(this));
542
- return client;
543
- })();
544
- this.globalSocketClient = await this.connectionPromise;
545
- this.connectionPromise = void 0;
546
- this.globalWaitHelper.setEnabled(true);
547
- return this.globalSocketClient;
548
- }
549
- /**
550
- * Disconnects the global socket connection and cleans up resources.
551
- */
552
- disconnectEventSocket() {
553
- if (this.globalSocketClient) {
554
- this.globalSocketClient.off("transaction", this.internalHandleSocketMessage.bind(this));
555
- this.globalSocketClient.disconnect();
556
- this.globalSocketClient = void 0;
557
- this.globalWaitHelper.setEnabled(false);
861
+ throw error;
558
862
  }
559
- }
560
- /**
561
- * Checks if the global socket connection is active.
562
- * @returns True if connected, false otherwise.
563
- */
564
- eventSocketConnected() {
565
- var _a;
566
- return ((_a = this.globalSocketClient) == null ? void 0 : _a.isConnected()) ?? false;
567
- }
568
- /**
569
- * Registers a transaction ID for waiting and timeout handling.
570
- * @param txId - The transaction ID to register.
571
- * @param timeoutMs - Timeout in milliseconds.
572
- */
573
- registerTxId(txId, timeoutMs) {
574
- this.globalWaitHelper.registerTxId(txId, timeoutMs);
575
- }
576
- /**
577
- * Waits for a transaction to complete.
578
- * @param txId - The transaction ID to wait for.
579
- * @returns Promise that resolves when the transaction completes.
580
- */
581
- async wait(txId) {
582
- if (!this.eventSocketConnected()) {
583
- throw GSwapSDKError.socketConnectionRequiredError();
863
+ const body = await readResponseBody(response);
864
+ if (!response.ok) {
865
+ throw gatewayError(response, body, url);
584
866
  }
585
- return this.globalWaitHelper.wait(txId);
586
- }
587
- /**
588
- * Internal handler for socket messages.
589
- * @param response - The bundler response.
590
- * @param txId - The transaction ID.
591
- */
592
- internalHandleSocketMessage(txId, response) {
593
- if (response.status === "PROCESSED") {
594
- this.globalWaitHelper.notifySuccess(txId, response.data);
595
- } else if (response.status === "FAILED") {
596
- this.globalWaitHelper.notifyFailure(txId, response.data);
597
- } else {
598
- debugLog(`Unknown response status for transaction ${txId}:`, response.status);
867
+ const envelope = asRecord2(body);
868
+ const data = asRecord2((envelope == null ? void 0 : envelope["data"]) ?? (envelope == null ? void 0 : envelope["Data"]));
869
+ if (data === void 0) {
870
+ throw invalidGatewayResponse(response, body, url, "Expected a data object.");
599
871
  }
600
- }
601
- };
602
-
603
- // src/classes/pending_transaction.ts
604
- var PendingTransaction = class {
605
- constructor(transactionId, message, error, waitDelegate) {
606
- this.transactionId = transactionId;
607
- this.message = message;
608
- this.error = error;
609
- this.waitDelegate = waitDelegate;
610
- }
611
- /**
612
- * Waits for the transaction to be confirmed.
613
- * This should be called immediately after receiving the PendingTransaction
614
- * to ensure proper event handling. If you delay calling this method and the
615
- * transaction confirms before you call this method, an error will be thrown.
616
- *
617
- * @returns A promise that resolves when the transaction is confirmed.
618
- */
619
- wait() {
620
- return this.waitDelegate();
621
- }
622
- };
623
-
624
- // src/classes/bundler.ts
625
- function randomUUID() {
626
- if (globalThis.crypto) {
627
- return globalThis.crypto.randomUUID();
628
- }
629
- return import_crypto.default.randomUUID();
630
- }
631
- var Bundler = class {
632
- constructor(bundlerBaseUrl, bundlingAPIBasePath, transactionWaitTimeoutMs, signer, httpClient) {
633
- this.bundlerBaseUrl = bundlerBaseUrl;
634
- this.bundlingAPIBasePath = bundlingAPIBasePath;
635
- this.transactionWaitTimeoutMs = transactionWaitTimeoutMs;
636
- this.signer = signer;
637
- this.httpClient = httpClient;
638
- }
639
- async signObject(methodName, toSign) {
640
- if (!this.signer) {
641
- throw GSwapSDKError.noSignerError();
872
+ const transactionId = getStringProperty(data, "transactionId");
873
+ const mode = getStringProperty(data, "mode");
874
+ const rawTransactionId = data["transactionId"];
875
+ const bodyBlockNumber = data["blockNumber"];
876
+ const invalidModeMessage = 'Gateway response must contain data.mode="sync" with data.result, or data.mode="queued".';
877
+ if (mode !== "sync" && mode !== "queued") {
878
+ throw invalidGatewayResponse(response, body, url, invalidModeMessage);
642
879
  }
643
- const withUniqueKey = {
644
- ...toSign,
645
- uniqueKey: toSign.uniqueKey ?? `galaswap - operation - ${randomUUID()}`
646
- };
647
- debugLog("Signing object for method", methodName, withUniqueKey);
648
- return this.signer.signObject(methodName, withUniqueKey);
649
- }
650
- async sendBundlerRequest(method, body, stringsInstructions) {
651
- if (!this.hasSigner()) {
652
- throw GSwapSDKError.noSignerError();
880
+ if ("transactionId" in data && typeof rawTransactionId !== "string" || bodyBlockNumber !== void 0 && bodyBlockNumber !== null && !isBlockNumber(bodyBlockNumber)) {
881
+ throw invalidGatewayResponse(response, body, url, invalidModeMessage);
653
882
  }
654
- const requestBody = {
655
- method,
656
- signedDto: await this.signObject(method, body),
657
- stringsInstructions
658
- };
659
- debugLog("Sending bundler request", method, requestBody);
660
- const response = await this.httpClient.sendPostRequest(
661
- this.bundlerBaseUrl,
662
- this.bundlingAPIBasePath,
663
- "",
664
- requestBody
665
- );
666
- debugLog("Received bundler response", method, response);
667
- Events.instance.registerTxId(response.data, this.transactionWaitTimeoutMs);
668
- const transaction = new PendingTransaction(
669
- response.data,
670
- response.message,
671
- response.error,
672
- () => {
673
- return Events.instance.wait(response.data);
883
+ if (method === "RequestTrade" && mode === "sync") {
884
+ throw invalidGatewayResponse(
885
+ response,
886
+ body,
887
+ url,
888
+ "The gateway executed RequestTrade synchronously, which this SDK refuses; RequestTrade must be queued."
889
+ );
890
+ }
891
+ if (method === "RequestTrade" && mode === "queued") {
892
+ const signedUniqueKey = getStringProperty(signedBody, "uniqueKey");
893
+ if ("uniqueKey" in data && (typeof data["uniqueKey"] !== "string" || data["uniqueKey"] !== signedUniqueKey)) {
894
+ throw invalidGatewayResponse(
895
+ response,
896
+ body,
897
+ url,
898
+ "Gateway response data.uniqueKey must match the signed RequestTrade uniqueKey."
899
+ );
674
900
  }
675
- );
676
- return transaction;
901
+ } else if (mode === "queued" || !("result" in data)) {
902
+ throw invalidGatewayResponse(response, body, url, invalidModeMessage);
903
+ }
904
+ if (mode === "sync") {
905
+ const embeddedResult = asRecord2(data["result"]);
906
+ if (embeddedResult !== void 0) {
907
+ const resultStatus = embeddedResult["Status"];
908
+ if (resultStatus !== void 0 && resultStatus !== 1) {
909
+ throw GSwapSDKError.fromChainError(
910
+ getStringProperty(embeddedResult, "ErrorKey") ?? "CHAIN_ERROR",
911
+ getStringProperty(embeddedResult, "Message") ?? "The chain rejected this write.",
912
+ { status: response.status, body, url }
913
+ );
914
+ }
915
+ }
916
+ }
917
+ const uniqueKey = getStringProperty(signedBody, "uniqueKey") ?? "";
918
+ const headerTransactionId = readHeader(response, "x-transaction-id");
919
+ const resolvedTransactionId = transactionId === void 0 || transactionId === "" ? headerTransactionId : transactionId;
920
+ const blockNumber = bodyBlockNumber === null || bodyBlockNumber === void 0 ? readBlockNumberHeader(response) : isBlockNumber(bodyBlockNumber) ? bodyBlockNumber : void 0;
921
+ return new SubmittedTransaction({
922
+ method,
923
+ uniqueKey,
924
+ transactionId: resolvedTransactionId === void 0 || resolvedTransactionId === "" ? null : resolvedTransactionId,
925
+ blockNumber: blockNumber ?? null,
926
+ mode,
927
+ result: mode === "queued" ? void 0 : data["result"],
928
+ dexBackendBaseUrl: this.dexBackendBaseUrl,
929
+ httpRequestor: this.requestor,
930
+ requestTimeoutMs: this.chainCallTimeoutMs,
931
+ ...(options == null ? void 0 : options.positionConfirmation) === void 0 ? {} : { positionConfirmation: options.positionConfirmation }
932
+ });
677
933
  }
678
- hasSigner() {
679
- return this.signer !== void 0;
934
+ /** Expose the requestor for sibling read services that share this gateway transport. */
935
+ get httpRequestor() {
936
+ return this.requestor;
680
937
  }
681
938
  };
939
+ function trimTrailingSlash(value) {
940
+ return value.replace(/\/$/u, "");
941
+ }
942
+ function invalidGatewayResponse(response, body, url, message) {
943
+ return new GSwapSDKError(message, "INVALID_GATEWAY_RESPONSE", {
944
+ status: response.status,
945
+ body,
946
+ url
947
+ });
948
+ }
949
+ function gatewayError(response, body, url) {
950
+ const object = asRecord2(body);
951
+ const nested = getObjectProperty(body, "error");
952
+ const code = getStringProperty(object, "code") ?? getStringProperty(nested, "code") ?? getStringProperty(nested, "ErrorKey") ?? "HTTP_ERROR";
953
+ const message = getStringProperty(object, "message") ?? getStringProperty(nested, "message") ?? getStringProperty(nested, "Message") ?? (typeof body === "string" ? body : `Gateway request failed with HTTP ${response.status}`);
954
+ const retryAfterMs = response.status === 429 ? readRetryAfter(response) : void 0;
955
+ if (code === "HTTP_ERROR") {
956
+ return new GSwapSDKError(message, code, { status: response.status, body, url });
957
+ }
958
+ return GSwapSDKError.fromGatewayBounce(code, message, response.status, retryAfterMs);
959
+ }
960
+ function asRecord2(value) {
961
+ return typeof value === "object" && value !== null ? value : void 0;
962
+ }
963
+ function isBlockNumber(value) {
964
+ return typeof value === "number" && Number.isInteger(value) && value >= 0;
965
+ }
966
+
967
+ // src/utils/debug.ts
968
+ var import_debug = __toESM(require("debug"), 1);
969
+ var debugLog = (0, import_debug.default)("GSWAP_SDK");
970
+
971
+ // src/version.ts
972
+ var SDK_VERSION = true ? "1.0.0-rc.3" : "1.0.0-rc.3";
682
973
 
683
974
  // src/classes/http_client.ts
684
975
  var HttpClient = class {
685
- constructor(httpRequestor = fetch) {
976
+ /** Create an HTTP client around fetch or an injected requestor. */
977
+ constructor(httpRequestor = fetch, timeoutMs = 3e4) {
686
978
  this.httpRequestor = httpRequestor;
979
+ this.timeoutMs = timeoutMs;
687
980
  }
688
- async sendRequest(method, baseUrl, basePath, endpoint, body) {
981
+ httpRequestor;
982
+ timeoutMs;
983
+ async sendRequest(method, baseUrl, basePath, endpoint, body, options) {
689
984
  const url = `${baseUrl}${basePath}${endpoint}`;
690
985
  debugLog(`Sending request to ${url} with body:`, body);
691
- const response = await this.httpRequestor(url, {
692
- method,
693
- headers: {
694
- "Content-Type": "application/json",
695
- "User-Agent": "GalaChain-SDK/0.0"
986
+ const response = await requestWithTimeout(
987
+ this.httpRequestor,
988
+ url,
989
+ {
990
+ method,
991
+ headers: {
992
+ "Content-Type": "application/json",
993
+ "User-Agent": `GalaChain-SDK/${SDK_VERSION}`
994
+ },
995
+ ...body ? { body: JSON.stringify(body) } : {},
996
+ ...(options == null ? void 0 : options.signal) === void 0 ? {} : { signal: options.signal }
696
997
  },
697
- ...body ? { body: JSON.stringify(body) } : {}
698
- });
998
+ this.timeoutMs
999
+ );
699
1000
  if (!response.ok) {
700
1001
  throw await GSwapSDKError.fromErrorResponse(url, response);
701
1002
  }
702
- const json = await response.json();
1003
+ const json = await readResponseBody(response);
703
1004
  debugLog(`Response from ${baseUrl}${basePath}${endpoint}:`, json);
704
1005
  return json;
705
1006
  }
1007
+ /** Send a JSON POST request and decode its response. */
706
1008
  async sendPostRequest(baseUrl, basePath, endpoint, body) {
707
1009
  return this.sendRequest("POST", baseUrl, basePath, endpoint, body);
708
1010
  }
709
- async sendGetRequest(baseUrl, basePath, endpoint, params) {
1011
+ /** Send a GET request with URL-encoded query parameters and decode its response. */
1012
+ async sendGetRequest(baseUrl, basePath, endpoint, params, options) {
710
1013
  const searchParams = params ? new URLSearchParams(params) : void 0;
711
1014
  const endpointWithParams = searchParams ? `${endpoint}?${searchParams.toString()}` : endpoint;
712
- return this.sendRequest("GET", baseUrl, basePath, endpointWithParams, void 0);
1015
+ return this.sendRequest("GET", baseUrl, basePath, endpointWithParams, void 0, options);
713
1016
  }
714
1017
  };
715
1018
 
716
- // src/classes/pools.ts
717
- var import_bignumber2 = __toESM(require("bignumber.js"), 1);
718
-
719
- // src/utils/token.ts
720
- function stringifyTokenClassKey(tokenClassKey, separator = "|") {
721
- if (typeof tokenClassKey === "string") {
722
- return tokenClassKey;
723
- }
724
- return `${tokenClassKey.collection}${separator}${tokenClassKey.category}${separator}${tokenClassKey.type}${separator}${tokenClassKey.additionalKey}`;
1019
+ // src/utils/ordering.ts
1020
+ function orderSymbols(a, b) {
1021
+ return a < b ? [a, b] : [b, a];
1022
+ }
1023
+ function readOrderedSymbols(value) {
1024
+ if (Array.isArray(value) && value.length >= 2) {
1025
+ const token0 = value[0];
1026
+ const token1 = value[1];
1027
+ if (typeof token0 === "string" && typeof token1 === "string") return { token0, token1 };
1028
+ }
1029
+ if (typeof value === "object" && value !== null) {
1030
+ const record = value;
1031
+ const token0 = record["token0"];
1032
+ const token1 = record["token1"];
1033
+ if (typeof token0 === "string" && typeof token1 === "string") return { token0, token1 };
1034
+ }
1035
+ throw new GSwapSDKError("Unable to order trading symbols.", "VALIDATION_ERROR", { value });
1036
+ }
1037
+ function compositeKeyOf(token) {
1038
+ return [token.collection, token.category, token.type, token.additionalKey].join("$");
725
1039
  }
726
1040
  function parseTokenClassKey(tokenClassKey) {
727
1041
  if (typeof tokenClassKey === "object") {
@@ -732,12 +1046,17 @@ function parseTokenClassKey(tokenClassKey) {
732
1046
  additionalKey: tokenClassKey.additionalKey
733
1047
  };
734
1048
  }
735
- const [collection, category, type, additionalKey] = tokenClassKey.split("|");
736
- if (!collection || !category || !type || !additionalKey) {
737
- throw new GSwapSDKError(`Invalid token class key`, "INVALID_TOKEN_CLASS_KEY", {
738
- tokenClassKey
739
- });
1049
+ const parts = tokenClassKey.includes("|") ? tokenClassKey.split("|") : tokenClassKey.split("$");
1050
+ if (parts.length !== 4 || parts.some((part) => part.length === 0)) {
1051
+ throw new GSwapSDKError(
1052
+ "Invalid token class key: expected four non-empty parts",
1053
+ "VALIDATION_ERROR",
1054
+ {
1055
+ tokenClassKey
1056
+ }
1057
+ );
740
1058
  }
1059
+ const [collection, category, type, additionalKey] = parts;
741
1060
  return {
742
1061
  collection,
743
1062
  category,
@@ -745,1129 +1064,1498 @@ function parseTokenClassKey(tokenClassKey) {
745
1064
  additionalKey
746
1065
  };
747
1066
  }
748
- function compareTokens(first, second) {
749
- return stringifyTokenClassKey(first).localeCompare(stringifyTokenClassKey(second));
750
- }
751
- function getTokenOrdering(first, second, assertCorrectness, token1Data, token2Data) {
752
- const zeroForOne = compareTokens(first, second) < 0;
753
- if (zeroForOne) {
754
- return {
755
- token0: first,
756
- token1: second,
757
- zeroForOne,
758
- token0Attributes: token1Data,
759
- token1Attributes: token2Data
760
- };
761
- } else {
762
- if (assertCorrectness) {
763
- throw GSwapSDKError.incorrectTokenOrderingError(first, second);
764
- }
765
- return {
766
- token0: second,
767
- token1: first,
768
- zeroForOne,
769
- token0Attributes: token2Data,
770
- token1Attributes: token1Data
771
- };
772
- }
773
- }
774
1067
 
775
1068
  // src/classes/pools.ts
776
1069
  var Pools = class {
777
- constructor(gatewayBaseUrl, dexContractBasePath, httpClient) {
778
- this.gatewayBaseUrl = gatewayBaseUrl;
779
- this.dexContractBasePath = dexContractBasePath;
780
- this.httpClient = httpClient;
1070
+ /**
1071
+ * Creates a pool service.
1072
+ *
1073
+ * @example
1074
+ * ```ts
1075
+ * const pools = new Pools(gateway, symbols);
1076
+ * ```
1077
+ */
1078
+ constructor(gateway, symbols) {
1079
+ this.gateway = gateway;
1080
+ this.symbols = symbols;
1081
+ this.http = new HttpClient(gateway.httpRequestor, gateway.requestTimeoutMs ?? 3e4);
1082
+ }
1083
+ gateway;
1084
+ symbols;
1085
+ http;
1086
+ /**
1087
+ * Fetches every current-contract pool from the swap backend.
1088
+ *
1089
+ * @example
1090
+ * ```ts
1091
+ * const pools = await service.getPools();
1092
+ * console.log(pools.length);
1093
+ * ```
1094
+ */
1095
+ async getPools(options) {
1096
+ const response = await this.http.sendGetRequest(
1097
+ this.gateway.dexBackendBaseUrl,
1098
+ "/v2/trade",
1099
+ "/pools",
1100
+ void 0,
1101
+ (options == null ? void 0 : options.signal) === void 0 ? void 0 : { signal: options.signal }
1102
+ );
1103
+ if (!Array.isArray(response.data) || !response.data.every(isPoolListInfo)) {
1104
+ throw new GSwapSDKError("Backend returned an invalid pool list.", "INVALID_CHAIN_RESPONSE", {
1105
+ data: response.data
1106
+ });
1107
+ }
1108
+ return response.data;
781
1109
  }
782
1110
  /**
783
- * Gets pool data for a specific token pair and fee tier.
1111
+ * Fetches one pool from the backend, preserving whether the request order was flipped.
1112
+ *
1113
+ * @example
1114
+ * ```ts
1115
+ * const pool = await service.getPool('GUSDC', 'GALA', 3000);
1116
+ * console.log(pool.flippedFromRequest);
1117
+ * ```
784
1118
  */
785
- async getPoolData(token0, token1, fee) {
1119
+ async getPool(token0, token1, fee) {
786
1120
  validateFee(fee);
787
- const token0Class = parseTokenClassKey(token0);
788
- const token1Class = parseTokenClassKey(token1);
789
- const ordering = getTokenOrdering(token0Class, token1Class, false);
790
- const responseBody = await this.httpClient.sendPostRequest(this.gatewayBaseUrl, this.dexContractBasePath, "/GetPoolData", {
791
- token0: ordering.token0,
792
- token1: ordering.token1,
793
- fee
794
- });
795
- const rawData = responseBody.Data;
796
- return {
797
- bitmap: rawData.bitmap,
798
- fee: rawData.fee,
799
- feeGrowthGlobal0: (0, import_bignumber2.default)(rawData.feeGrowthGlobal0),
800
- feeGrowthGlobal1: (0, import_bignumber2.default)(rawData.feeGrowthGlobal1),
801
- grossPoolLiquidity: (0, import_bignumber2.default)(rawData.grossPoolLiquidity),
802
- liquidity: (0, import_bignumber2.default)(rawData.liquidity),
803
- maxLiquidityPerTick: (0, import_bignumber2.default)(rawData.maxLiquidityPerTick),
804
- protocolFees: rawData.protocolFees,
805
- protocolFeesToken0: (0, import_bignumber2.default)(rawData.protocolFeesToken0),
806
- protocolFeesToken1: (0, import_bignumber2.default)(rawData.protocolFeesToken1),
807
- sqrtPrice: (0, import_bignumber2.default)(rawData.sqrtPrice),
808
- tickSpacing: rawData.tickSpacing,
809
- token0: rawData.token0,
810
- token0ClassKey: rawData.token0ClassKey,
811
- token1: rawData.token1,
812
- token1ClassKey: rawData.token1ClassKey
813
- };
1121
+ return this.getBackend("/pool", await this.backendParams(token0, token1, fee));
814
1122
  }
815
1123
  /**
816
- * Calculates the nearest valid tick value for a given price.
1124
+ * Fetches the current sqrt price, price, and tick for one pool.
1125
+ *
1126
+ * @example
1127
+ * ```ts
1128
+ * const slot0 = await service.getSlot0('GALA', 'GUSDC', 3000);
1129
+ * console.log(slot0.tick);
1130
+ * ```
817
1131
  */
818
- calculateTicksForPrice(price, tickSpacing) {
819
- validateNumericAmount(price, "price", true);
820
- validateTickSpacing(tickSpacing);
821
- const priceNumber = (0, import_bignumber2.default)(price).toNumber();
822
- if (priceNumber === 0) {
823
- return -886800;
824
- }
825
- if (priceNumber === Infinity) {
826
- return 886800;
827
- }
828
- const uncoercedTicks = Math.round(Math.log(priceNumber) / Math.log(1.0001));
829
- const ticks = Math.floor(uncoercedTicks / tickSpacing) * tickSpacing;
830
- return Math.min(Math.max(ticks, -886800), 886800);
831
- }
832
- calculatePriceForTicks(tick) {
833
- if (tick === -886800) {
834
- return (0, import_bignumber2.default)("0");
835
- } else if (tick === 886800) {
836
- return (0, import_bignumber2.default)(Infinity);
837
- } else {
838
- const price = Math.pow(1.0001, tick);
839
- return (0, import_bignumber2.default)(price);
840
- }
1132
+ async getSlot0(token0, token1, fee) {
1133
+ validateFee(fee);
1134
+ return this.getBackend("/slot0", await this.backendParams(token0, token1, fee));
841
1135
  }
842
1136
  /**
843
- * Calculates the current spot price of a pool based on sqrt price.
844
- * Price is defined as the amount of outToken you would receive for 1 inToken.
1137
+ * Fetches the complete chain composite pool snapshot, in canonical symbol order.
1138
+ *
1139
+ * @example
1140
+ * ```ts
1141
+ * const composite = await service.getCompositePool('GALA', 'GUSDC', 3000);
1142
+ * console.log(composite.positions?.length ?? 0);
1143
+ * ```
845
1144
  */
846
- calculateSpotPrice(inToken, outToken, poolSqrtPrice) {
847
- validateNumericAmount(poolSqrtPrice, "poolSqrtPrice");
848
- const ordering = getTokenOrdering(inToken, outToken, false);
849
- const poolPrice = (0, import_bignumber2.default)(poolSqrtPrice).pow(2);
850
- if (ordering.zeroForOne) {
851
- return poolPrice;
852
- } else {
853
- return (0, import_bignumber2.default)(1).div(poolPrice);
1145
+ async getCompositePool(token0, token1, fee) {
1146
+ validateFee(fee);
1147
+ const first = await resolveSymbol(this.symbols, token0);
1148
+ const second = await resolveSymbol(this.symbols, token1);
1149
+ const ordered = readOrderedSymbols(orderSymbols(first, second));
1150
+ return this.getBackend("/composite-pool", {
1151
+ token0: ordered.token0,
1152
+ token1: ordered.token1,
1153
+ fee: String(fee)
1154
+ });
1155
+ }
1156
+ async backendParams(token0, token1, fee) {
1157
+ return {
1158
+ token0: await resolveSymbol(this.symbols, token0),
1159
+ token1: await resolveSymbol(this.symbols, token1),
1160
+ fee: String(fee)
1161
+ };
1162
+ }
1163
+ async getBackend(endpoint, params) {
1164
+ try {
1165
+ const response = await this.http.sendGetRequest(
1166
+ this.gateway.dexBackendBaseUrl,
1167
+ "/v2/trade",
1168
+ endpoint,
1169
+ params
1170
+ );
1171
+ return response.data;
1172
+ } catch (error) {
1173
+ if (error instanceof GSwapSDKError) throw error;
1174
+ throw new GSwapSDKError("Pool request failed.", "HTTP_ERROR", { cause: error });
854
1175
  }
855
1176
  }
856
1177
  };
1178
+ async function resolveSymbol(symbols, token) {
1179
+ const resolved = await symbols.resolve(token);
1180
+ if (resolved.symbol.length > 0) return resolved.symbol;
1181
+ throw new GSwapSDKError("Token could not be resolved to a trading symbol.", "SYMBOL_NOT_FOUND", {
1182
+ token
1183
+ });
1184
+ }
1185
+ function isPoolListInfo(value) {
1186
+ if (typeof value !== "object" || value === null) return false;
1187
+ const row = value;
1188
+ const requiredNumbers = ["fee", "tickSpacing", "protocolFees", "tradingFees"];
1189
+ const optionalStrings = [
1190
+ "token0CompositeKey",
1191
+ "token1CompositeKey",
1192
+ "creator",
1193
+ "price",
1194
+ "sqrtPrice",
1195
+ "token0Tvl",
1196
+ "token1Tvl"
1197
+ ];
1198
+ const optionalNumbers = [
1199
+ "tick",
1200
+ "tvlUsd",
1201
+ "volume1d",
1202
+ "volume7d",
1203
+ "volume30d",
1204
+ "trades1d",
1205
+ "fee24h",
1206
+ "apr1d",
1207
+ "token0Price",
1208
+ "token1Price"
1209
+ ];
1210
+ return row["contractVersion"] === "v2" && typeof row["token0"] === "string" && typeof row["token1"] === "string" && typeof row["poolName"] === "string" && typeof row["poolRef"] === "string" && requiredNumbers.every((key) => isFiniteNumber(row[key])) && optionalStrings.every((key) => isOptionalString(row[key])) && optionalNumbers.every((key) => isOptionalNumber(row[key]));
1211
+ }
1212
+ function isFiniteNumber(value) {
1213
+ return typeof value === "number" && Number.isFinite(value);
1214
+ }
1215
+ function isOptionalString(value) {
1216
+ return value === void 0 || typeof value === "string";
1217
+ }
1218
+ function isOptionalNumber(value) {
1219
+ return value === void 0 || isFiniteNumber(value);
1220
+ }
857
1221
 
858
1222
  // src/classes/positions.ts
859
1223
  var import_bignumber3 = __toESM(require("bignumber.js"), 1);
860
1224
  var Positions = class {
861
- constructor(gatewayBaseUrl, dexContractBasePath, bundlerService, poolService, httpClient, options) {
862
- this.gatewayBaseUrl = gatewayBaseUrl;
863
- this.dexContractBasePath = dexContractBasePath;
864
- this.options = options;
865
- this.httpClient = httpClient;
866
- this.bundlerService = bundlerService;
867
- this.poolService = poolService;
1225
+ constructor(gateway, symbols, signer, walletAddress) {
1226
+ this.gateway = gateway;
1227
+ this.symbols = symbols;
1228
+ this.signer = signer;
1229
+ this.walletAddress = walletAddress;
1230
+ this.http = new HttpClient(gateway.httpRequestor, gateway.requestTimeoutMs ?? 3e4);
868
1231
  }
869
- httpClient;
870
- bundlerService;
871
- poolService;
1232
+ gateway;
1233
+ symbols;
1234
+ signer;
1235
+ walletAddress;
1236
+ http;
872
1237
  /**
873
- * Get all liquidity positions for a specific wallet address.
874
- * @param ownerAddress - The wallet address to get positions for.
875
- * @param limit - Maximum number of positions to return.
876
- * @param bookmark - Pagination bookmark for retrieving additional results. If you call this function and it returns a bookmark that is not an empty string, you can pass that bookmark as this parameter in a subsequent call to fetch the next page.
877
- * @returns User positions and pagination bookmark
1238
+ * Gets all current-contract positions owned by an address.
1239
+ *
1240
+ * @param owner - GalaChain owner identity.
1241
+ * @returns Enriched positions from the v2 trade backend.
878
1242
  * @example
879
1243
  * ```typescript
880
- * const positions = await gSwap.positions.getUserPositions('eth|123...abc');
881
- * console.log(positions);
1244
+ * const positions = await gSwap.positions.getUserPositions('client|012345678901234567890123');
882
1245
  * ```
883
1246
  */
884
- async getUserPositions(ownerAddress, limit, bookmark) {
885
- const results = await this.sendUserPositionsRequest("/GetUserPositions", {
886
- user: ownerAddress,
887
- limit,
888
- bookmark
889
- });
890
- return {
891
- bookmark: results.nextBookMark,
892
- positions: results.positions
893
- };
1247
+ async getUserPositions(owner) {
1248
+ const response = await this.get("/positions", { user: owner });
1249
+ return response.data.map((position) => this.mapPosition(position));
894
1250
  }
895
1251
  /**
896
- * Gets detailed information about a specific liquidity position.
897
- * @param ownerAddress - The wallet address that owns the position.
898
- * @param position - Position parameters including tokens, fee, and tick range.
899
- * @param position.token0ClassKey - The first token in the position.
900
- * @param position.token1ClassKey - The second token in the position.
901
- * @param position.fee - The pool fee tier.
902
- * @param position.tickLower - The lower tick of the position range.
903
- * @param position.tickUpper - The upper tick of the position range.
904
- * @returns Detailed position information.
1252
+ * Gets one position, normalizing a caller-supplied reversed pair to canonical symbol order.
1253
+ * Reversed pairs also invert the price axis, so `[lower, upper]` becomes `[-upper, -lower]`.
1254
+ *
1255
+ * @param args - Pair, owner, fee, and position tick range.
1256
+ * @returns The position, or `null` when the backend returns 404.
905
1257
  * @example
906
1258
  * ```typescript
907
- * const position = await gSwap.positions.getPosition('eth|123...abc', {
908
- * token0ClassKey: 'GALA|Unit|none|none',
909
- * token1ClassKey: 'GUSDC|Unit|none|none',
910
- * fee: 500,
911
- * tickLower: -6000,
912
- * tickUpper: 6000
1259
+ * const position = await gSwap.positions.getPosition({
1260
+ * token0: 'GALA', token1: 'GUSDC', owner: 'client|012345678901234567890123',
1261
+ * fee: 3000, tickLower: -19200, tickUpper: 12000,
913
1262
  * });
914
- * console.log('Position:', position);
915
1263
  * ```
916
1264
  */
917
- async getPosition(ownerAddress, position) {
918
- const result = await this.sendPositionRequest("/GetPositions", {
919
- owner: ownerAddress,
920
- token0: parseTokenClassKey(position.token0ClassKey),
921
- token1: parseTokenClassKey(position.token1ClassKey),
922
- fee: position.fee,
923
- tickLower: position.tickLower,
924
- tickUpper: position.tickUpper
925
- });
926
- return result;
1265
+ async getPosition(args, signal) {
1266
+ const pair = await this.symbols.orderPair(args.token0, args.token1);
1267
+ const ticks = this.canonicalTicks(args.tickLower, args.tickUpper, pair.flipped);
1268
+ const response = await this.getNullable(
1269
+ "/position",
1270
+ {
1271
+ token0: pair.token0.symbol,
1272
+ token1: pair.token1.symbol,
1273
+ fee: `${args.fee}`,
1274
+ owner: args.owner,
1275
+ tickLower: `${ticks.tickLower}`,
1276
+ tickUpper: `${ticks.tickUpper}`
1277
+ },
1278
+ signal
1279
+ );
1280
+ return response === null ? null : this.mapPosition(response.data);
927
1281
  }
928
1282
  /**
929
- * Estimates the token amounts that would be received from removing liquidity from a position.
930
- * @param args - Parameters for estimating liquidity removal.
931
- * @param args.ownerAddress - The wallet address that owns the position.
932
- * @param args.positionId - The position identifier.
933
- * @param args.token0 - The first token in the pair.
934
- * @param args.token1 - The second token in the pair.
935
- * @param args.fee - The pool fee tier.
936
- * @param args.tickLower - The lower tick of the position range.
937
- * @param args.tickUpper - The upper tick of the position range.
938
- * @param args.amount - The amount of liquidity to remove.
939
- * @returns Estimated token amounts that would be received.
1283
+ * Estimates the other token amount and resulting liquidity for a deposit.
1284
+ *
1285
+ * @param args - Pair, tick range, deposit amount, and the amount's caller-side token index.
1286
+ * @returns Backend liquidity estimate.
940
1287
  * @example
941
1288
  * ```typescript
942
- * const estimation = await gSwap.positions.estimateRemoveLiquidity({
943
- * ownerAddress: 'client|635f048ab243d7eb7f5ba044',
944
- * positionId: '210f8e4dd1bbe1b4af4b977330ee7e4b68bc716f66e39c87a60fff0976ded3ea',
945
- * token0: 'GALA|Unit|none|none',
946
- * token1: 'GUSDT|Unit|none|none',
947
- * fee: 3000,
948
- * tickLower: -41100,
949
- * tickUpper: -40080,
950
- * amount: '1491.973332758921980256'
1289
+ * const estimate = await gSwap.positions.estimateAddLiquidity({
1290
+ * token0: 'GALA', token1: 'GUSDC', fee: 3000,
1291
+ * tickLower: -19200, tickUpper: 12000, amount: '100', amountIsToken0: true,
951
1292
  * });
952
- * console.log('Estimated tokens:', estimation);
953
1293
  * ```
954
1294
  */
955
- async estimateRemoveLiquidity(args) {
956
- validateWalletAddress(args.ownerAddress);
957
- validateFee(args.fee);
958
- validateTickRange(args.tickLower, args.tickUpper);
1295
+ async estimateAddLiquidity(args) {
959
1296
  validateNumericAmount(args.amount, "amount");
960
- const token0TokenClassKey = parseTokenClassKey(args.token0);
961
- const token1TokenClassKey = parseTokenClassKey(args.token1);
962
- const ordering = getTokenOrdering(token0TokenClassKey, token1TokenClassKey, false);
963
- const responseBody = await this.httpClient.sendPostRequest(this.gatewayBaseUrl, this.dexContractBasePath, "/GetRemoveLiquidityEstimation", {
964
- tickLower: args.tickLower,
965
- tickUpper: args.tickUpper,
966
- amount: (0, import_bignumber3.default)(args.amount).toFixed(),
967
- token0: ordering.token0,
968
- token1: ordering.token1,
969
- fee: args.fee,
970
- owner: args.ownerAddress,
971
- positionId: args.positionId
1297
+ const response = await this.get("/add-liq-estimate", {
1298
+ token0: this.requestToken(args.token0),
1299
+ token1: this.requestToken(args.token1),
1300
+ fee: `${args.fee}`,
1301
+ tickLower: `${args.tickLower}`,
1302
+ tickUpper: `${args.tickUpper}`,
1303
+ amount: new import_bignumber3.default(args.amount).toFixed(),
1304
+ amountIsToken0: String(args.amountIsToken0)
972
1305
  });
973
- return {
974
- amount0: (0, import_bignumber3.default)(responseBody.Data.amount0),
975
- amount1: (0, import_bignumber3.default)(responseBody.Data.amount1)
976
- };
1306
+ return response.data;
977
1307
  }
978
1308
  /**
979
- * Gets detailed information about a liquidity position by its ID.
980
- * @param ownerAddress - The wallet address that owns the position.
981
- * @param positionId - The unique identifier of the position.
982
- * @returns Detailed position information, or undefined if not found.
1309
+ * Estimates token amounts released by burning liquidity.
1310
+ *
1311
+ * @param args - Pair, tick range, fee tier, and liquidity quantity.
1312
+ * @returns Estimated token0 and token1 quantities.
983
1313
  * @example
984
1314
  * ```typescript
985
- * const position = await gSwap.positions.getPositionById('eth|123...abc', 'position-uuid-123');
986
- * if (position) {
987
- * console.log('Position found:', position);
988
- * } else {
989
- * console.log('Position not found');
990
- * }
1315
+ * const estimate = await gSwap.positions.estimateRemoveLiquidity({
1316
+ * token0: 'GALA', token1: 'GUSDC', fee: 3000,
1317
+ * tickLower: -19200, tickUpper: 12000, liquidity: '1000',
1318
+ * });
991
1319
  * ```
992
1320
  */
993
- async getPositionById(ownerAddress, positionId) {
994
- const userPositions = await this.getUserPositions(ownerAddress);
995
- const position = userPositions.positions.find((pos) => pos.positionId === positionId);
996
- if (!position) {
997
- return void 0;
998
- }
999
- return this.getPosition(ownerAddress, position);
1321
+ async estimateRemoveLiquidity(args) {
1322
+ validateNumericAmount(args.liquidity, "liquidity");
1323
+ const response = await this.get("/remove-liq-estimate", {
1324
+ token0: this.requestToken(args.token0),
1325
+ token1: this.requestToken(args.token1),
1326
+ fee: `${args.fee}`,
1327
+ tickLower: `${args.tickLower}`,
1328
+ tickUpper: `${args.tickUpper}`,
1329
+ liquidity: new import_bignumber3.default(args.liquidity).toFixed()
1330
+ });
1331
+ return response.data;
1000
1332
  }
1001
1333
  /**
1002
- * Creates a new position or adds liquidity to a position, using a specific tick range. Consider using `addLiquidityByPrice()` instead unless you want to use ticks directly.
1003
- * @param args - Parameters for adding liquidity.
1004
- * @param args.walletAddress - The wallet address adding liquidity.
1005
- * @param args.positionId - The position identifier. If you're creating a new position, this should be an empty string.
1006
- * @param args.token0 - The first token in the pair.
1007
- * @param args.token1 - The second token in the pair.
1008
- * @param args.fee - The pool fee tier.
1009
- * @param args.tickLower - The lower tick of the position range.
1010
- * @param args.tickUpper - The upper tick of the position range.
1011
- * @param args.amount0Desired - Desired (also maximum) amount of token0 to add.
1012
- * @param args.amount1Desired - Desired (also maximum) amount of token1 to add.
1013
- * @param args.amount0Min - Minimum amount of token0 to add (slippage protection).
1014
- * @param args.amount1Min - Minimum amount of token1 to add (slippage protection).
1015
- * @returns Pending transaction.
1334
+ * Adds liquidity using an explicit tick range. Exactly one canonical deposit field is emitted.
1335
+ *
1336
+ * @param args - Pair, fee, range, deposit amount, and caller-side token index.
1337
+ * @returns The synchronously submitted transaction.
1016
1338
  * @example
1017
1339
  * ```typescript
1018
- * const result = await gSwap.positions.addLiquidityByTicks({
1019
- * walletAddress: 'eth|123...abc',
1020
- * positionId: '',
1021
- * token0: 'GALA|Unit|none|none',
1022
- * token1: 'GUSDC|Unit|none|none',
1023
- * fee: 500,
1024
- * tickSpacing: 10,
1025
- * tickLower: -6000,
1026
- * tickUpper: 6000,
1027
- * amount0Desired: '100',
1028
- * amount1Desired: '50',
1029
- * amount0Min: '95',
1030
- * amount1Min: '47.5'
1340
+ * const tx = await gSwap.positions.addLiquidityByTicks({
1341
+ * token0: 'GALA', token1: 'GUSDC', fee: 3000,
1342
+ * tickLower: -19200, tickUpper: 12000, amount: '100', amountIsToken0: true,
1031
1343
  * });
1032
- * console.log('Liquidity added:', result);
1344
+ * await tx.confirm();
1033
1345
  * ```
1034
1346
  */
1035
1347
  async addLiquidityByTicks(args) {
1036
- var _a;
1037
- const walletAddress = args.walletAddress ?? ((_a = this.options) == null ? void 0 : _a.walletAddress);
1038
- validateWalletAddress(walletAddress);
1039
1348
  validateFee(args.fee);
1040
- validateTickRange(args.tickLower, args.tickUpper);
1041
- validateNumericAmount(args.amount0Desired, "amount0Desired", true);
1042
- validateNumericAmount(args.amount1Desired, "amount1Desired", true);
1043
- validateNumericAmount(args.amount0Min, "amount0Min", true);
1044
- validateNumericAmount(args.amount1Min, "amount1Min", true);
1045
- const token0TokenClassKey = parseTokenClassKey(args.token0);
1046
- const token1TokenClassKey = parseTokenClassKey(args.token1);
1047
- const ordering = getTokenOrdering(
1048
- token0TokenClassKey,
1049
- token1TokenClassKey,
1050
- true,
1051
- [args.amount0Desired, args.amount0Min],
1052
- [args.amount1Desired, args.amount1Min]
1053
- );
1054
- const toSign = {
1055
- token0: ordering.token0,
1056
- token1: ordering.token1,
1349
+ const pair = await this.symbols.orderPair(args.token0, args.token1);
1350
+ const ticks = this.canonicalTicks(args.tickLower, args.tickUpper, pair.flipped);
1351
+ assertTickRange(ticks.tickLower, ticks.tickUpper, args.fee);
1352
+ validateNumericAmount(args.amount, "amount");
1353
+ const amount = new import_bignumber3.default(args.amount).toFixed();
1354
+ const dto = {
1355
+ token0: pair.token0.symbol,
1356
+ token1: pair.token1.symbol,
1057
1357
  fee: args.fee,
1058
- owner: args.walletAddress,
1059
- tickLower: args.tickLower,
1060
- tickUpper: args.tickUpper,
1061
- amount0Desired: (0, import_bignumber3.default)(ordering.token0Attributes[0]).toFixed(),
1062
- amount1Desired: (0, import_bignumber3.default)(ordering.token1Attributes[0]).toFixed(),
1063
- amount0Min: (0, import_bignumber3.default)(ordering.token0Attributes[1]).toFixed(),
1064
- amount1Min: (0, import_bignumber3.default)(ordering.token1Attributes[1]).toFixed(),
1065
- positionId: args.positionId,
1066
- liquidityProvider: args.walletAddress
1358
+ tickLower: ticks.tickLower,
1359
+ tickUpper: ticks.tickUpper,
1360
+ uniqueKey: this.uniqueKey(),
1361
+ ...pair.flipped !== args.amountIsToken0 ? { depositQuantityToken0: amount } : { depositQuantityToken1: amount }
1067
1362
  };
1068
- const token0StringKey = stringifyTokenClassKey(ordering.token0, "$");
1069
- const token1StringKey = stringifyTokenClassKey(ordering.token1, "$");
1070
- const poolString = `$pool$${token0StringKey}$${token1StringKey}$${args.fee}`;
1071
- const userPositionString = `$userPosition$${args.walletAddress}`;
1072
- const tokenBalance0 = `$tokenBalance$${token0StringKey}$${args.walletAddress}`;
1073
- const tokenBalance1 = `$tokenBalance$${token1StringKey}$${args.walletAddress}`;
1074
- const tokenBalance0Pool = `$tokenBalance$${token0StringKey}$${poolString}`;
1075
- const tokenBalance1Pool = `$tokenBalance$${token1StringKey}$${poolString}`;
1076
- const stringsInstructions = [
1077
- poolString,
1078
- userPositionString,
1079
- tokenBalance0,
1080
- tokenBalance1,
1081
- tokenBalance0Pool,
1082
- tokenBalance1Pool
1083
- ];
1084
- return this.bundlerService.sendBundlerRequest("AddLiquidity", toSign, stringsInstructions);
1363
+ return this.submit("AddLiquidity", dto, {
1364
+ kind: "add",
1365
+ identity: {
1366
+ token0: pair.token0.symbol,
1367
+ token1: pair.token1.symbol,
1368
+ fee: args.fee,
1369
+ tickLower: ticks.tickLower,
1370
+ tickUpper: ticks.tickUpper
1371
+ }
1372
+ });
1085
1373
  }
1086
1374
  /**
1087
- * Creates a new position or adds liquidity to a position, using a specified price range. Note that this method automatically converts your minPrice and maxPrice to ticks, rounding down if necessary.
1088
- * @param args - Parameters for adding liquidity.
1089
- * @param args.walletAddress - The wallet address adding liquidity.
1090
- * @param args.positionId - The position identifier. This should be an empty string if you are creating a new position.
1091
- * @param args.token0 - The first token in the pair.
1092
- * @param args.token1 - The second token in the pair.
1093
- * @param args.fee - The pool fee tier.
1094
- * @param args.tickSpacing - The tick spacing for the pool.
1095
- * @param args.minPrice - The minimum price for the position range.
1096
- * @param args.maxPrice - The maximum price for the position range.
1097
- * @param args.amount0Desired - Desired (also maximum) amount of token0 to add.
1098
- * @param args.amount1Desired - Desired (also maximum) amount of token1 to add.
1099
- * @param args.amount0Min - Minimum amount of token0 to add (slippage protection).
1100
- * @param args.amount1Min - Minimum amount of token1 to add (slippage protection).
1101
- * @returns Pending transaction.
1375
+ * Adds liquidity from prices, flooring the lower tick and ceiling the upper tick to fee spacing.
1376
+ * The resulting range is therefore no narrower than the requested price interval.
1377
+ *
1378
+ * @param args - Pair, fee, price range, deposit amount, and caller-side token index.
1379
+ * @returns The synchronously submitted transaction.
1102
1380
  * @example
1103
1381
  * ```typescript
1104
- * const result = await gSwap.positions.addLiquidityByPrice({
1105
- * walletAddress: 'eth|123...abc',
1106
- * positionId: '',
1107
- * token0: 'GALA|Unit|none|none',
1108
- * token1: 'GUSDC|Unit|none|none',
1109
- * fee: 500,
1110
- * tickSpacing: 10,
1111
- * minPrice: '0.45',
1112
- * maxPrice: '0.55',
1113
- * amount0Desired: '100',
1114
- * amount1Desired: '50',
1115
- * amount0Min: '95',
1116
- * amount1Min: '47.5'
1382
+ * const tx = await gSwap.positions.addLiquidityByPrice({
1383
+ * token0: 'GALA', token1: 'GUSDC', fee: 3000,
1384
+ * minPrice: '0.14', maxPrice: '0.16', amount: '100', amountIsToken0: true,
1117
1385
  * });
1118
- * console.log('Liquidity added with price range:', result);
1119
1386
  * ```
1120
1387
  */
1121
1388
  async addLiquidityByPrice(args) {
1122
- var _a, _b, _c, _d, _e;
1123
- const walletAddress = args.walletAddress ?? ((_a = this.options) == null ? void 0 : _a.walletAddress);
1124
- validateWalletAddress(walletAddress);
1125
1389
  validateFee(args.fee);
1126
- validateTickSpacing(args.tickSpacing);
1127
- validateNumericAmount(args.minPrice, "minPrice", true);
1128
- validateNumericAmount(args.maxPrice, "maxPrice");
1129
- validateNumericAmount(args.amount0Desired, "amount0Desired", true);
1130
- validateNumericAmount(args.amount1Desired, "amount1Desired", true);
1131
- validateNumericAmount(args.amount0Min, "amount0Min", true);
1132
- validateNumericAmount(args.amount1Min, "amount1Min", true);
1133
- if ((0, import_bignumber3.default)(args.minPrice).isGreaterThan((0, import_bignumber3.default)(args.maxPrice))) {
1134
- throw new Error("Invalid price range: minPrice must be less than or equal to maxPrice");
1390
+ this.rejectNumber(args.minPrice, "minPrice");
1391
+ this.rejectNumber(args.maxPrice, "maxPrice");
1392
+ const minPrice = new import_bignumber3.default(args.minPrice);
1393
+ const maxPrice = new import_bignumber3.default(args.maxPrice);
1394
+ if (!minPrice.isFinite() || !maxPrice.isFinite() || minPrice.isLessThanOrEqualTo(0) || maxPrice.isLessThanOrEqualTo(0)) {
1395
+ throw new GSwapSDKError("Price bounds must be finite and positive.", "VALIDATION_ERROR");
1135
1396
  }
1136
- const token0TokenClassKey = parseTokenClassKey(args.token0);
1137
- const token1TokenClassKey = parseTokenClassKey(args.token1);
1138
- const ordering = getTokenOrdering(
1139
- token0TokenClassKey,
1140
- token1TokenClassKey,
1141
- true,
1142
- [args.amount0Desired, args.amount0Min],
1143
- [args.amount1Desired, args.amount1Min]
1144
- );
1145
- const minPriceTicks = this.poolService.calculateTicksForPrice(args.minPrice, args.tickSpacing);
1146
- const maxPriceTicks = this.poolService.calculateTicksForPrice(args.maxPrice, args.tickSpacing);
1147
- const tickLower = ordering.zeroForOne ? minPriceTicks : maxPriceTicks * -1;
1148
- const tickUpper = ordering.zeroForOne ? maxPriceTicks : minPriceTicks * -1;
1149
- const toSign = {
1150
- token0: ordering.token0,
1151
- token1: ordering.token1,
1397
+ if (minPrice.isGreaterThan(maxPrice)) {
1398
+ throw new GSwapSDKError(
1399
+ "minPrice must be less than or equal to maxPrice.",
1400
+ "VALIDATION_ERROR"
1401
+ );
1402
+ }
1403
+ const spacing = this.tickSpacing(args.fee);
1404
+ const tickLower = alignTickDown(tickFromPrice(minPrice), spacing);
1405
+ const tickUpper = alignTickUp(tickFromPrice(maxPrice), spacing);
1406
+ return this.addLiquidityByTicks({
1407
+ token0: args.token0,
1408
+ token1: args.token1,
1152
1409
  fee: args.fee,
1153
- owner: args.walletAddress,
1154
1410
  tickLower,
1155
1411
  tickUpper,
1156
- amount0Desired: (_b = ordering == null ? void 0 : ordering.token0Attributes) == null ? void 0 : _b[0],
1157
- amount1Desired: (_c = ordering == null ? void 0 : ordering.token1Attributes) == null ? void 0 : _c[0],
1158
- amount0Min: (_d = ordering == null ? void 0 : ordering.token0Attributes) == null ? void 0 : _d[1],
1159
- amount1Min: (_e = ordering == null ? void 0 : ordering.token1Attributes) == null ? void 0 : _e[1],
1160
- positionId: args.positionId
1161
- };
1162
- const token0StringKey = stringifyTokenClassKey(ordering.token0, "$");
1163
- const token1StringKey = stringifyTokenClassKey(ordering.token1, "$");
1164
- const poolString = `$pool$${token0StringKey}$${token1StringKey}$${args.fee}`;
1165
- const userPositionString = `$userPosition$${args.walletAddress}`;
1166
- const tokenBalance0 = `$tokenBalance$${token0StringKey}$${args.walletAddress}`;
1167
- const tokenBalance1 = `$tokenBalance$${token1StringKey}$${args.walletAddress}`;
1168
- const tokenBalance0Pool = `$tokenBalance$${token0StringKey}$${poolString}`;
1169
- const tokenBalance1Pool = `$tokenBalance$${token1StringKey}$${poolString}`;
1170
- const stringsInstructions = [
1171
- poolString,
1172
- userPositionString,
1173
- tokenBalance0,
1174
- tokenBalance1,
1175
- tokenBalance0Pool,
1176
- tokenBalance1Pool
1177
- ];
1178
- return this.bundlerService.sendBundlerRequest("AddLiquidity", toSign, stringsInstructions);
1412
+ amount: args.amount,
1413
+ amountIsToken0: args.amountIsToken0
1414
+ });
1179
1415
  }
1180
1416
  /**
1181
- * Removes liquidity from a position.
1182
- * @param args - Parameters for removing liquidity.
1183
- * @param args.walletAddress - The wallet address removing liquidity.
1184
- * @param args.positionId - The position identifier.
1185
- * @param args.token0 - The first token in the pair.
1186
- * @param args.token1 - The second token in the pair.
1187
- * @param args.fee - The pool fee tier.
1188
- * @param args.tickLower - The lower tick of the position range.
1189
- * @param args.tickUpper - The upper tick of the position range.
1190
- * @param args.amount - The amount of liquidity to remove.
1191
- * @param args.amount0Min - Minimum amount of token0 to receive (slippage protection, optional).
1192
- * @param args.amount1Min - Minimum amount of token1 to receive (slippage protection, optional).
1193
- * @returns Pending transaction.
1417
+ * Removes liquidity by withdrawing at most one canonical token quantity.
1418
+ * Omitting both amounts closes the position and sweeps all accrued fees.
1419
+ *
1420
+ * @param args - Pair, fee, range, and optional caller-side withdrawal amount.
1421
+ * @returns The synchronously submitted transaction.
1194
1422
  * @example
1195
1423
  * ```typescript
1196
- * // Remove 50% of liquidity from a position
1197
- * const result = await gSwap.positions.removeLiquidity({
1198
- * walletAddress: 'eth|123...abc',
1199
- * positionId: 'position-123',
1200
- * token0: 'GALA|Unit|none|none',
1201
- * token1: 'GUSDC|Unit|none|none',
1202
- * fee: 500,
1203
- * tickLower: -6000,
1204
- * tickUpper: 6000,
1205
- * amount: '50000000000000000000', // 50% of position liquidity
1206
- * amount0Min: '45',
1207
- * amount1Min: '22'
1424
+ * const tx = await gSwap.positions.removeLiquidity({
1425
+ * token0: 'GALA', token1: 'GUSDC', fee: 3000,
1426
+ * tickLower: -19200, tickUpper: 12000,
1208
1427
  * });
1209
- * console.log('Liquidity removed:', result);
1210
1428
  * ```
1211
1429
  */
1212
1430
  async removeLiquidity(args) {
1213
- var _a, _b, _c;
1214
- const walletAddress = args.walletAddress ?? ((_a = this.options) == null ? void 0 : _a.walletAddress);
1215
- validateWalletAddress(walletAddress);
1216
1431
  validateFee(args.fee);
1217
- validateTickRange(args.tickLower, args.tickUpper);
1218
- validateNumericAmount(args.amount, "amount");
1219
- if (args.amount0Min !== void 0) {
1220
- validateNumericAmount(args.amount0Min, "amount0Min", true);
1432
+ const pair = await this.symbols.orderPair(args.token0, args.token1);
1433
+ const ticks = this.canonicalTicks(args.tickLower, args.tickUpper, pair.flipped);
1434
+ assertTickRange(ticks.tickLower, ticks.tickUpper, args.fee);
1435
+ if (args.amount0 !== void 0 && args.amount1 !== void 0) {
1436
+ throw new GSwapSDKError("Provide at most one of amount0 or amount1.", "VALIDATION_ERROR");
1221
1437
  }
1222
- if (args.amount1Min !== void 0) {
1223
- validateNumericAmount(args.amount1Min, "amount1Min", true);
1224
- }
1225
- const token0TokenClassKey = parseTokenClassKey(args.token0);
1226
- const token1TokenClassKey = parseTokenClassKey(args.token1);
1227
- const ordering = getTokenOrdering(
1228
- token0TokenClassKey,
1229
- token1TokenClassKey,
1230
- true,
1231
- [args.amount0Min ?? 0],
1232
- [args.amount1Min ?? 0]
1233
- );
1234
- const toSign = {
1235
- token0: ordering.token0,
1236
- token1: ordering.token1,
1438
+ if (args.amount0 !== void 0) validateNumericAmount(args.amount0, "amount0");
1439
+ if (args.amount1 !== void 0) validateNumericAmount(args.amount1, "amount1");
1440
+ const withdrawal = args.amount0 !== void 0 ? pair.flipped ? { withdrawalQuantityToken1: new import_bignumber3.default(args.amount0).toFixed() } : { withdrawalQuantityToken0: new import_bignumber3.default(args.amount0).toFixed() } : args.amount1 !== void 0 ? pair.flipped ? { withdrawalQuantityToken0: new import_bignumber3.default(args.amount1).toFixed() } : { withdrawalQuantityToken1: new import_bignumber3.default(args.amount1).toFixed() } : {};
1441
+ const dto = {
1442
+ token0: pair.token0.symbol,
1443
+ token1: pair.token1.symbol,
1237
1444
  fee: args.fee,
1238
- tickLower: args.tickLower,
1239
- tickUpper: args.tickUpper,
1240
- amount: (0, import_bignumber3.default)(args.amount).toFixed(),
1241
- amount0Min: ((_b = ordering == null ? void 0 : ordering.token0Attributes) == null ? void 0 : _b[0]) || "0",
1242
- amount1Min: ((_c = ordering == null ? void 0 : ordering.token1Attributes) == null ? void 0 : _c[0]) || "0",
1243
- positionId: args.positionId,
1244
- recipient: args.walletAddress
1445
+ tickLower: ticks.tickLower,
1446
+ tickUpper: ticks.tickUpper,
1447
+ uniqueKey: this.uniqueKey(),
1448
+ ...withdrawal
1245
1449
  };
1246
- const token0StringKey = stringifyTokenClassKey(ordering.token0, "$");
1247
- const token1StringKey = stringifyTokenClassKey(ordering.token1, "$");
1248
- const poolString = `$pool$${token0StringKey}$${token1StringKey}$${args.fee}`;
1249
- const userPositionString = `$userPosition$${args.walletAddress}`;
1250
- const tokenBalance0 = `$tokenBalance$${token0StringKey}$${args.walletAddress}`;
1251
- const tokenBalance1 = `$tokenBalance$${token1StringKey}$${args.walletAddress}`;
1252
- const tokenBalance0Pool = `$tokenBalance$${token0StringKey}$${poolString}`;
1253
- const tokenBalance1Pool = `$tokenBalance$${token1StringKey}$${poolString}`;
1254
- const stringsInstructions = [
1255
- poolString,
1256
- userPositionString,
1257
- tokenBalance0,
1258
- tokenBalance1,
1259
- tokenBalance0Pool,
1260
- tokenBalance1Pool
1261
- ];
1262
- return this.bundlerService.sendBundlerRequest("RemoveLiquidity", toSign, stringsInstructions);
1450
+ const isFullClose = args.amount0 === void 0 && args.amount1 === void 0;
1451
+ return this.submit("RemoveLiquidity", dto, {
1452
+ kind: isFullClose ? "removeFull" : "removePartial",
1453
+ identity: {
1454
+ token0: pair.token0.symbol,
1455
+ token1: pair.token1.symbol,
1456
+ fee: args.fee,
1457
+ tickLower: ticks.tickLower,
1458
+ tickUpper: ticks.tickUpper
1459
+ }
1460
+ });
1263
1461
  }
1264
1462
  /**
1265
- * Collects accumulated fees from a liquidity position.
1266
- * @param args - Parameters for collecting fees.
1267
- * @param args.walletAddress - The wallet address collecting fees.
1268
- * @param args.positionId - The position identifier.
1269
- * @param args.token0 - The first token in the pair.
1270
- * @param args.token1 - The second token in the pair.
1271
- * @param args.fee - The pool fee tier.
1272
- * @param args.tickLower - The lower tick of the position range.
1273
- * @param args.tickUpper - The upper tick of the position range.
1274
- * @param args.amount0Requested - Desired amount of token0 fees to collect.
1275
- * @param args.amount1Requested - Desired amount of token1 fees to collect.
1276
- * @returns Pending transaction.
1463
+ * Collects all accrued fees for a position; the contract does not support partial collection.
1464
+ *
1465
+ * @param args - Pair, fee, and tick range identifying the position.
1466
+ * @returns The synchronously submitted transaction.
1277
1467
  * @example
1278
1468
  * ```typescript
1279
- * // Collect all accumulated fees from a position
1280
- * const result = await gSwap.positions.collectPositionFees({
1281
- * walletAddress: 'eth|123...abc',
1282
- * positionId: 'position-123',
1283
- * token0: 'GALA|Unit|none|none',
1284
- * token1: 'GUSDC|Unit|none|none',
1285
- * fee: 500,
1286
- * tickLower: -6000,
1287
- * tickUpper: 6000,
1288
- * amount0Requested: '1000000000000000000', // Max fees available
1289
- * amount1Requested: '500000000' // Max fees available
1469
+ * const tx = await gSwap.positions.collectPositionFees({
1470
+ * token0: 'GALA', token1: 'GUSDC', fee: 3000,
1471
+ * tickLower: -19200, tickUpper: 12000,
1290
1472
  * });
1291
- * console.log('Fees collected:', result);
1292
1473
  * ```
1293
1474
  */
1294
1475
  async collectPositionFees(args) {
1295
- var _a;
1296
- const walletAddress = args.walletAddress ?? ((_a = this.options) == null ? void 0 : _a.walletAddress);
1297
- validateWalletAddress(walletAddress);
1298
1476
  validateFee(args.fee);
1299
- validateTickRange(args.tickLower, args.tickUpper);
1300
- validateNumericAmount(args.amount0Requested, "amount0Requested", true);
1301
- validateNumericAmount(args.amount1Requested, "amount1Requested", true);
1302
- const token0TokenClassKey = parseTokenClassKey(args.token0);
1303
- const token1TokenClassKey = parseTokenClassKey(args.token1);
1304
- const ordering = getTokenOrdering(
1305
- token0TokenClassKey,
1306
- token1TokenClassKey,
1307
- true,
1308
- [args.amount0Requested],
1309
- [args.amount1Requested]
1310
- );
1311
- const toSign = {
1312
- token0: ordering.token0,
1313
- token1: ordering.token1,
1477
+ const pair = await this.symbols.orderPair(args.token0, args.token1);
1478
+ const ticks = this.canonicalTicks(args.tickLower, args.tickUpper, pair.flipped);
1479
+ assertTickRange(ticks.tickLower, ticks.tickUpper, args.fee);
1480
+ const dto = {
1481
+ token0: pair.token0.symbol,
1482
+ token1: pair.token1.symbol,
1314
1483
  fee: args.fee,
1315
- amount0Requested: (0, import_bignumber3.default)(ordering.token0Attributes[0]).toFixed(),
1316
- amount1Requested: (0, import_bignumber3.default)(ordering.token1Attributes[0]).toFixed(),
1317
- tickLower: args.tickLower,
1318
- tickUpper: args.tickUpper,
1319
- positionId: args.positionId,
1320
- recipient: args.walletAddress
1484
+ tickLower: ticks.tickLower,
1485
+ tickUpper: ticks.tickUpper,
1486
+ uniqueKey: this.uniqueKey()
1321
1487
  };
1322
- const token0StringKey = stringifyTokenClassKey(ordering.token0, "$");
1323
- const token1StringKey = stringifyTokenClassKey(ordering.token1, "$");
1324
- const poolString = `$pool$${token0StringKey}$${token1StringKey}$${args.fee}`;
1325
- const userPositionString = `$userPosition$${args.walletAddress}`;
1326
- const tokenBalance0 = `$tokenBalance$${token0StringKey}$${args.walletAddress}`;
1327
- const tokenBalance1 = `$tokenBalance$${token1StringKey}$${args.walletAddress}`;
1328
- const tokenBalance0Pool = `$tokenBalance$${token0StringKey}$${poolString}`;
1329
- const tokenBalance1Pool = `$tokenBalance$${token1StringKey}$${poolString}`;
1330
- const stringsInstructions = [
1331
- poolString,
1332
- userPositionString,
1333
- tokenBalance0,
1334
- tokenBalance1,
1335
- tokenBalance0Pool,
1336
- tokenBalance1Pool
1337
- ];
1338
- return this.bundlerService.sendBundlerRequest(
1339
- "CollectPositionFees",
1340
- toSign,
1341
- stringsInstructions
1342
- );
1488
+ return this.submit("CollectPositionFees", dto, {
1489
+ kind: "collect",
1490
+ identity: {
1491
+ token0: pair.token0.symbol,
1492
+ token1: pair.token1.symbol,
1493
+ fee: args.fee,
1494
+ tickLower: ticks.tickLower,
1495
+ tickUpper: ticks.tickUpper
1496
+ }
1497
+ });
1343
1498
  }
1344
- calculateOptimalPositionSize(tokenAmount, spotPrice, lowerPrice, upperPrice, tokenDecimals, otherTokenDecimals) {
1345
- validateNumericAmount(tokenAmount, "tokenAmount");
1346
- validatePriceValues(spotPrice, lowerPrice, upperPrice);
1347
- validateTokenDecimals(tokenDecimals, "tokenDecimals");
1348
- validateTokenDecimals(otherTokenDecimals, "otherTokenDecimals");
1349
- const bnTokenAmount = (0, import_bignumber3.default)(tokenAmount);
1350
- const bnSpotPrice = (0, import_bignumber3.default)(spotPrice);
1351
- const bnLowerPrice = (0, import_bignumber3.default)(lowerPrice);
1352
- let bnUpperPrice = (0, import_bignumber3.default)(upperPrice);
1353
- bnUpperPrice = bnUpperPrice.isFinite() ? bnUpperPrice : (0, import_bignumber3.default)(1e18);
1354
- const liquidityAmount = bnTokenAmount.times(10 ** (tokenDecimals - otherTokenDecimals)).times(bnSpotPrice.sqrt()).times(bnUpperPrice.sqrt()).div(bnUpperPrice.sqrt().minus(bnSpotPrice.sqrt()));
1355
- const yAmount = (0, import_bignumber3.default)(liquidityAmount).times(bnSpotPrice.sqrt().minus(bnLowerPrice.sqrt()));
1356
- const untruncated = yAmount.div((0, import_bignumber3.default)(10).pow(tokenDecimals - otherTokenDecimals));
1357
- return import_bignumber3.default.max(
1358
- (0, import_bignumber3.default)(untruncated.toFixed(otherTokenDecimals, import_bignumber3.default.ROUND_DOWN)),
1359
- 0
1360
- );
1499
+ /**
1500
+ * Creates a pool, resolving registered trading symbols and falling back to class collections.
1501
+ * Reversed symbol order inverts the supplied starting price or square-root price.
1502
+ *
1503
+ * @param args - Token class keys or references, fee, exactly one starting price field, and privacy options.
1504
+ * @returns The synchronously submitted transaction.
1505
+ * @example
1506
+ * ```typescript
1507
+ * const tx = await gSwap.positions.createPool({
1508
+ * token0: 'GALA|Unit|none|none', token1: 'GUSDC|Unit|none|none', fee: 3000,
1509
+ * startingPrice: '0.15',
1510
+ * });
1511
+ * ```
1512
+ */
1513
+ async createPool(args) {
1514
+ var _a, _b;
1515
+ const hasPrice = args.startingPrice !== void 0;
1516
+ const hasSqrtPrice = args.startingSqrtPrice !== void 0;
1517
+ if (hasPrice === hasSqrtPrice) {
1518
+ throw new GSwapSDKError(
1519
+ "Provide exactly one of startingPrice or startingSqrtPrice.",
1520
+ "VALIDATION_ERROR"
1521
+ );
1522
+ }
1523
+ validateFee(args.fee);
1524
+ const [tokenA, tokenB] = await Promise.all([
1525
+ this.resolveCreateToken(args.token0),
1526
+ this.resolveCreateToken(args.token1)
1527
+ ]);
1528
+ const flipped = tokenA.symbol > tokenB.symbol;
1529
+ const [token0, token1] = flipped ? [tokenB, tokenA] : [tokenA, tokenB];
1530
+ const sourcePriceInput = hasPrice ? args.startingPrice : args.startingSqrtPrice;
1531
+ this.rejectNumber(sourcePriceInput, hasPrice ? "startingPrice" : "startingSqrtPrice");
1532
+ const sourcePrice = new import_bignumber3.default(sourcePriceInput);
1533
+ if (!sourcePrice.isFinite() || sourcePrice.isLessThanOrEqualTo(0)) {
1534
+ throw new GSwapSDKError("Starting price must be finite and positive.", "VALIDATION_ERROR");
1535
+ }
1536
+ const canonicalPrice = flipped ? new import_bignumber3.default(1).dividedBy(sourcePrice) : sourcePrice;
1537
+ const dto = {
1538
+ token0Key: token0.classKey,
1539
+ token1Key: token1.classKey,
1540
+ token0Symbol: token0.symbol,
1541
+ token1Symbol: token1.symbol,
1542
+ fee: args.fee,
1543
+ uniqueKey: this.uniqueKey(),
1544
+ ...hasPrice ? { startingPrice: canonicalPrice.toFixed() } : { startingSqrtPrice: canonicalPrice.toFixed() },
1545
+ ...args.isPrivate === void 0 ? {} : { isPrivate: args.isPrivate },
1546
+ ...args.privateAccess === void 0 ? {} : { privateAccess: args.privateAccess }
1547
+ };
1548
+ const transaction = await this.submit("CreatePool", dto, {
1549
+ kind: "createPool",
1550
+ token0: token0.symbol,
1551
+ token1: token1.symbol,
1552
+ fee: args.fee
1553
+ });
1554
+ (_b = (_a = this.symbols).invalidate) == null ? void 0 : _b.call(_a);
1555
+ return transaction;
1361
1556
  }
1362
- async sendUserPositionsRequest(endpoint, body) {
1363
- const responseBody = await this.httpClient.sendPostRequest(this.gatewayBaseUrl, this.dexContractBasePath, endpoint, body);
1364
- return {
1365
- nextBookMark: responseBody.Data.nextBookMark,
1366
- positions: responseBody.Data.positions.map((position) => ({
1367
- ...position,
1368
- liquidity: (0, import_bignumber3.default)(position.liquidity)
1369
- }))
1557
+ async submit(method, dto, confirmation) {
1558
+ if (this.signer === void 0) throw GSwapSDKError.noSignerError();
1559
+ const signed = await this.signer.signObject(method, dto);
1560
+ const submitOptions = this.walletAddress === void 0 ? {} : { walletAddress: this.walletAddress };
1561
+ const owner = this.walletAddress;
1562
+ const positionConfirmation = this.buildPositionConfirmation(confirmation, owner);
1563
+ const options = {
1564
+ ...submitOptions,
1565
+ ...positionConfirmation === void 0 ? {} : { positionConfirmation }
1566
+ };
1567
+ return this.gateway.submit(method, signed, options);
1568
+ }
1569
+ /**
1570
+ * Builds a confirmation closure that proves the write's own effect, not just that a
1571
+ * position row exists (true before most of these writes run too, for any operation on a
1572
+ * pre-existing position — existence alone would false-confirm before the write executes,
1573
+ * and would never confirm a full close, whose whole point is that the position stops
1574
+ * existing).
1575
+ *
1576
+ * The "before" state is captured lazily, on the confirmation closure's own first poll —
1577
+ * not eagerly here in `submit()` — so confirming a write never adds an extra round-trip to
1578
+ * the write itself; the cost of establishing the baseline is paid by `confirm()`, only if
1579
+ * and when the caller actually asks to confirm.
1580
+ */
1581
+ buildPositionConfirmation(confirmation, owner) {
1582
+ if (confirmation === void 0) return void 0;
1583
+ if (confirmation.kind === "createPool") {
1584
+ const { token0, token1, fee } = confirmation;
1585
+ return async (signal) => {
1586
+ const pool = await this.getNullable(
1587
+ "/pool",
1588
+ { token0, token1, fee: `${fee}` },
1589
+ signal
1590
+ );
1591
+ return pool === null ? { confirmed: false } : { confirmed: true, position: null };
1592
+ };
1593
+ }
1594
+ if (owner === void 0) return void 0;
1595
+ const { kind, identity } = confirmation;
1596
+ let baseline;
1597
+ return async (signal) => {
1598
+ const current = await this.getPosition({ ...identity, owner }, signal);
1599
+ if (baseline === void 0) {
1600
+ baseline = {
1601
+ liquidity: new import_bignumber3.default((current == null ? void 0 : current.liquidity) ?? 0),
1602
+ fees: new import_bignumber3.default((current == null ? void 0 : current.fees0) ?? 0).plus(new import_bignumber3.default((current == null ? void 0 : current.fees1) ?? 0))
1603
+ };
1604
+ return kind === "removeFull" && current === null ? { confirmed: true, position: null } : { confirmed: false };
1605
+ }
1606
+ switch (kind) {
1607
+ case "add":
1608
+ return current !== null && new import_bignumber3.default(current.liquidity).isGreaterThan(baseline.liquidity) ? { confirmed: true, position: current } : { confirmed: false };
1609
+ case "removePartial":
1610
+ return current !== null && new import_bignumber3.default(current.liquidity).isLessThan(baseline.liquidity) ? { confirmed: true, position: current } : { confirmed: false };
1611
+ case "removeFull":
1612
+ return current === null ? { confirmed: true, position: null } : { confirmed: false };
1613
+ case "collect": {
1614
+ if (current === null) return { confirmed: false };
1615
+ const currentFees = new import_bignumber3.default(current.fees0 ?? 0).plus(
1616
+ new import_bignumber3.default(current.fees1 ?? 0)
1617
+ );
1618
+ if (currentFees.isLessThan(baseline.fees)) return { confirmed: true, position: current };
1619
+ if (baseline.fees.isZero() && currentFees.isZero())
1620
+ return { confirmed: true, position: current };
1621
+ return { confirmed: false };
1622
+ }
1623
+ }
1370
1624
  };
1371
1625
  }
1372
- async sendPositionRequest(endpoint, body) {
1373
- const responseBody = await this.httpClient.sendPostRequest(this.gatewayBaseUrl, this.dexContractBasePath, endpoint, body);
1626
+ async get(endpoint, params, signal) {
1627
+ return this.http.sendGetRequest(
1628
+ this.gateway.dexBackendBaseUrl,
1629
+ "/v2/trade",
1630
+ endpoint,
1631
+ params,
1632
+ signal === void 0 ? void 0 : { signal }
1633
+ );
1634
+ }
1635
+ async getNullable(endpoint, params, signal) {
1636
+ try {
1637
+ return await this.get(endpoint, params, signal);
1638
+ } catch (error) {
1639
+ if (this.isNotFound(error)) return null;
1640
+ throw error;
1641
+ }
1642
+ }
1643
+ isNotFound(error) {
1644
+ if (!(error instanceof GSwapSDKError) || error.details === void 0) return false;
1645
+ return error.details["status"] === 404;
1646
+ }
1647
+ mapPosition(position) {
1374
1648
  return {
1375
- ...responseBody.Data,
1376
- feeGrowthInside0Last: (0, import_bignumber3.default)(responseBody.Data.feeGrowthInside0Last),
1377
- feeGrowthInside1Last: (0, import_bignumber3.default)(responseBody.Data.feeGrowthInside1Last),
1378
- liquidity: (0, import_bignumber3.default)(responseBody.Data.liquidity),
1379
- tokensOwed0: (0, import_bignumber3.default)(responseBody.Data.tokensOwed0),
1380
- tokensOwed1: (0, import_bignumber3.default)(responseBody.Data.tokensOwed1)
1649
+ token0Symbol: position.token0Symbol,
1650
+ token1Symbol: position.token1Symbol,
1651
+ ...position.token0CompositeKey === void 0 ? {} : { token0CompositeKey: position.token0CompositeKey },
1652
+ ...position.token1CompositeKey === void 0 ? {} : { token1CompositeKey: position.token1CompositeKey },
1653
+ fee: position.fee,
1654
+ tickLower: position.tickLower,
1655
+ tickUpper: position.tickUpper,
1656
+ liquidity: position.liquidity,
1657
+ amount0: position.amount0,
1658
+ amount1: position.amount1,
1659
+ inRange: position.inRange,
1660
+ ...position.currentTick === void 0 ? {} : { currentTick: position.currentTick },
1661
+ ...position.sqrtPrice === void 0 ? {} : { sqrtPrice: position.sqrtPrice },
1662
+ pool: position.poolRef ?? position.pool ?? "",
1663
+ owner: position.owner,
1664
+ ...position.fees0 === void 0 ? {} : { fees0: position.fees0 },
1665
+ ...position.fees1 === void 0 ? {} : { fees1: position.fees1 }
1381
1666
  };
1382
1667
  }
1668
+ requestToken(ref) {
1669
+ return typeof ref === "string" ? ref : compositeKeyOf(ref);
1670
+ }
1671
+ canonicalTicks(tickLower, tickUpper, flipped) {
1672
+ return flipped ? { tickLower: -tickUpper, tickUpper: -tickLower } : { tickLower, tickUpper };
1673
+ }
1674
+ tickSpacing(fee) {
1675
+ const spacing = TICK_SPACING_BY_FEE[fee];
1676
+ if (spacing === void 0) {
1677
+ throw new GSwapSDKError(`Unsupported fee tier: ${fee}.`, "VALIDATION_ERROR");
1678
+ }
1679
+ return spacing;
1680
+ }
1681
+ rejectNumber(value, parameterName) {
1682
+ if (typeof value === "number") {
1683
+ throw new GSwapSDKError(
1684
+ `Invalid ${parameterName}: use a decimal string or BigNumber, not a JavaScript number`,
1685
+ "VALIDATION_ERROR"
1686
+ );
1687
+ }
1688
+ }
1689
+ uniqueKey() {
1690
+ return `gswap-sdk-${globalThis.crypto.randomUUID()}`;
1691
+ }
1692
+ async resolveCreateToken(ref) {
1693
+ try {
1694
+ const lookupRef = typeof ref === "object" ? compositeKeyOf(ref) : ref;
1695
+ const resolved = await this.symbols.resolve(lookupRef);
1696
+ const suppliedClassKey = this.tryParseClassKey(ref);
1697
+ return {
1698
+ symbol: resolved.symbol,
1699
+ classKey: suppliedClassKey ?? parseTokenClassKey({
1700
+ collection: resolved.collection,
1701
+ category: resolved.category,
1702
+ type: resolved.type,
1703
+ additionalKey: resolved.additionalKey
1704
+ })
1705
+ };
1706
+ } catch (error) {
1707
+ if (!(error instanceof GSwapSDKError) || error.code !== "UNKNOWN_TOKEN") {
1708
+ throw error;
1709
+ }
1710
+ let classKey;
1711
+ try {
1712
+ classKey = parseTokenClassKey(ref);
1713
+ } catch {
1714
+ throw error;
1715
+ }
1716
+ return { symbol: classKey.collection, classKey };
1717
+ }
1718
+ }
1719
+ tryParseClassKey(ref) {
1720
+ try {
1721
+ return parseTokenClassKey(ref);
1722
+ } catch {
1723
+ return void 0;
1724
+ }
1725
+ }
1383
1726
  };
1384
1727
 
1385
1728
  // src/classes/quoting.ts
1386
1729
  var import_bignumber4 = __toESM(require("bignumber.js"), 1);
1387
-
1388
- // src/types/fees.ts
1389
- var FEE_TIER = /* @__PURE__ */ ((FEE_TIER2) => {
1390
- FEE_TIER2[FEE_TIER2["PERCENT_00_05"] = 500] = "PERCENT_00_05";
1391
- FEE_TIER2[FEE_TIER2["PERCENT_00_30"] = 3e3] = "PERCENT_00_30";
1392
- FEE_TIER2[FEE_TIER2["PERCENT_01_00"] = 1e4] = "PERCENT_01_00";
1393
- return FEE_TIER2;
1394
- })(FEE_TIER || {});
1395
-
1396
- // src/classes/quoting.ts
1397
1730
  var Quoting = class {
1398
- constructor(gatewayBaseUrl, dexContractBasePath, httpClient) {
1399
- this.gatewayBaseUrl = gatewayBaseUrl;
1400
- this.dexContractBasePath = dexContractBasePath;
1401
- this.httpClient = httpClient ?? new HttpClient();
1731
+ /**
1732
+ * Creates a quote service.
1733
+ *
1734
+ * @example
1735
+ * ```ts
1736
+ * const quoting = new Quoting('https://swap-backend.stage.defi.ovh.gala.com', http, symbols);
1737
+ * ```
1738
+ */
1739
+ constructor(dexBackendBaseUrl, http, symbols) {
1740
+ this.dexBackendBaseUrl = dexBackendBaseUrl;
1741
+ this.http = http;
1742
+ this.symbols = symbols;
1402
1743
  }
1403
- httpClient;
1744
+ dexBackendBaseUrl;
1745
+ http;
1746
+ symbols;
1404
1747
  /**
1405
- * Gets a quote for an exact amount of token being sold.
1406
- * @param tokenIn - The input token to sell.
1407
- * @param tokenOut - The output token to buy.
1408
- * @param amountIn - The exact amount of input tokens you want to sell.
1409
- * @param fee - The pool fee tier. If not specified, will check all available fee tiers and return the best quote.
1410
- * @returns The expected number of tokens you will receive (buy) when/if you execute the swap.
1748
+ * Quotes the output for a positive exact-input amount.
1749
+ *
1750
+ * @example
1751
+ * ```ts
1752
+ * const quote = await quoting.quoteExactInput('GALA', 'GUSDC', '100');
1753
+ * console.log(quote.amountOut);
1754
+ * ```
1411
1755
  */
1412
1756
  async quoteExactInput(tokenIn, tokenOut, amountIn, fee) {
1413
1757
  validateNumericAmount(amountIn, "amountIn");
1414
- if (fee !== void 0) {
1415
- const result = await this.getSingleQuoteExactInput(tokenIn, tokenOut, fee, amountIn);
1416
- return result;
1417
- }
1418
- const allFees = [500 /* PERCENT_00_05 */, 3e3 /* PERCENT_00_30 */, 1e4 /* PERCENT_01_00 */];
1419
- const quotePromises = allFees.map(async (feeTier) => {
1420
- try {
1421
- return await this.getSingleQuoteExactInput(tokenIn, tokenOut, feeTier, amountIn);
1422
- } catch (err) {
1423
- if (err instanceof GSwapSDKError && (err.code === "CONFLICT" || err.code === "OBJECT_NOT_FOUND")) {
1424
- return void 0;
1425
- }
1426
- throw err;
1427
- }
1428
- });
1429
- const results = await Promise.all(quotePromises);
1430
- const quotes = results.filter((quote) => quote !== void 0);
1431
- if (quotes.length === 0) {
1432
- throw GSwapSDKError.noPoolAvailableError(tokenIn, tokenOut);
1433
- }
1434
- return quotes.reduce(
1435
- (best, current) => current.outTokenAmount.isGreaterThan(best.outTokenAmount) ? current : best
1436
- );
1758
+ return this.quote(tokenIn, tokenOut, { amountIn: toDecimalString(amountIn) }, fee);
1437
1759
  }
1438
1760
  /**
1439
- * Gets a quote for an exact amount of tokens being bought.
1440
- * @param tokenIn - The input token sell.
1441
- * @param tokenOut - The output token to buy.
1442
- * @param amountOut - The exact amount of output tokens you want to buy.
1443
- * @param fee - The pool fee tier. If not specified, will check all available fee tiers and return the best quote.
1444
- * @returns The expected number of tokens you will need to sell (input) to receive the specified amount of output tokens.
1761
+ * Quotes the input for a positive exact-output amount.
1762
+ *
1763
+ * @example
1764
+ * ```ts
1765
+ * const quote = await quoting.quoteExactOutput('GALA', 'GUSDC', '10', 3000);
1766
+ * console.log(quote.amountIn);
1767
+ * ```
1445
1768
  */
1446
1769
  async quoteExactOutput(tokenIn, tokenOut, amountOut, fee) {
1447
1770
  validateNumericAmount(amountOut, "amountOut");
1448
- if (fee !== void 0) {
1449
- const result = await this.getSingleQuoteExactOutput(tokenIn, tokenOut, amountOut, fee);
1450
- return result;
1451
- }
1452
- const allFees = [500 /* PERCENT_00_05 */, 3e3 /* PERCENT_00_30 */, 1e4 /* PERCENT_01_00 */];
1453
- const quotePromises = allFees.map(async (feeTier) => {
1454
- try {
1455
- return await this.getSingleQuoteExactOutput(tokenIn, tokenOut, amountOut, feeTier);
1456
- } catch (err) {
1457
- if (err instanceof GSwapSDKError && (err.code === "CONFLICT" || err.code === "OBJECT_NOT_FOUND")) {
1458
- return void 0;
1459
- }
1460
- throw err;
1461
- }
1462
- });
1463
- const results = await Promise.all(quotePromises);
1464
- const quotes = results.filter((quote) => quote !== void 0);
1465
- if (quotes.length === 0) {
1466
- throw GSwapSDKError.noPoolAvailableError(tokenIn, tokenOut);
1771
+ return this.quote(tokenIn, tokenOut, { amountOut: toDecimalString(amountOut) }, fee);
1772
+ }
1773
+ async quote(tokenIn, tokenOut, amount, fee) {
1774
+ validateOptionalFee(fee);
1775
+ const tokenInSymbol = await resolveSymbol2(this.symbols, tokenIn);
1776
+ const tokenOutSymbol = await resolveSymbol2(this.symbols, tokenOut);
1777
+ const params = {
1778
+ tokenIn: tokenInSymbol,
1779
+ tokenOut: tokenOutSymbol,
1780
+ ...amount
1781
+ };
1782
+ if (fee !== void 0) params["fee"] = String(fee);
1783
+ try {
1784
+ const response = await this.http.sendGetRequest(
1785
+ this.dexBackendBaseUrl,
1786
+ "/v2/trade",
1787
+ "/quote",
1788
+ params
1789
+ );
1790
+ return mapQuote(response.data);
1791
+ } catch (error) {
1792
+ throw mapQuoteError(error, tokenIn, tokenOut, fee);
1467
1793
  }
1468
- return quotes.reduce(
1469
- (best, current) => current.inTokenAmount.isLessThan(best.inTokenAmount) ? current : best
1470
- );
1471
1794
  }
1472
- async getSingleQuote(tokenIn, tokenOut, fee, amount, isExactInput) {
1473
- const tokenInClass = parseTokenClassKey(tokenIn);
1474
- const tokenOutClass = parseTokenClassKey(tokenOut);
1475
- const ordering = getTokenOrdering(tokenInClass, tokenOutClass, false);
1476
- const formattedAmount = isExactInput ? (0, import_bignumber4.default)(amount).toFixed() : (0, import_bignumber4.default)(amount).multipliedBy(-1).toFixed();
1477
- const response = await this.sendQuoteRequest("/QuoteExactAmount", {
1478
- ...ordering,
1479
- fee,
1480
- amount: formattedAmount
1795
+ };
1796
+ function mapQuote(value) {
1797
+ if (!isQuoteWire(value)) {
1798
+ throw new GSwapSDKError("Backend returned an invalid quote.", "INVALID_CHAIN_RESPONSE", {
1799
+ data: value
1481
1800
  });
1482
- const tokenInAmount = ordering.zeroForOne ? response.amount0 : response.amount1;
1483
- const tokenOutAmount = ordering.zeroForOne ? response.amount1 : response.amount0;
1484
- let currentPrice = response.currentSqrtPrice.pow(2);
1485
- let newPrice = response.newSqrtPrice.pow(2);
1486
- if (!ordering.zeroForOne) {
1487
- currentPrice = (0, import_bignumber4.default)(1).dividedBy(currentPrice);
1488
- newPrice = (0, import_bignumber4.default)(1).dividedBy(newPrice);
1801
+ }
1802
+ const wire = value;
1803
+ const currentPrice = wire.currentPrice !== void 0 ? new import_bignumber4.default(wire.currentPrice) : orientPrice(wire.currentSqrtPrice, wire.tokenInIsToken0);
1804
+ const newPrice = wire.newPrice !== void 0 ? new import_bignumber4.default(wire.newPrice) : orientPrice(wire.newSqrtPrice, wire.tokenInIsToken0);
1805
+ const priceImpact = newPrice.minus(currentPrice).dividedBy(currentPrice);
1806
+ return {
1807
+ contractVersion: wire.contractVersion,
1808
+ fee: wire.fee,
1809
+ amountIn: wire.amountIn,
1810
+ amountOut: wire.amountOut,
1811
+ ...wire.currentSqrtPrice === void 0 ? {} : { currentSqrtPrice: wire.currentSqrtPrice },
1812
+ ...wire.newSqrtPrice === void 0 ? {} : { newSqrtPrice: wire.newSqrtPrice },
1813
+ feeTier: wire.fee,
1814
+ newTick: wire.newTick,
1815
+ tradingFees: wire.tradingFees,
1816
+ protocolFees: wire.protocolFees,
1817
+ totalFees: wire.totalFees,
1818
+ feeTokenSymbol: wire.feeTokenSymbol,
1819
+ token0Symbol: wire.token0Symbol,
1820
+ token1Symbol: wire.token1Symbol,
1821
+ tokenInIsToken0: wire.tokenInIsToken0,
1822
+ currentPrice,
1823
+ newPrice,
1824
+ priceImpact
1825
+ };
1826
+ }
1827
+ var HighPrecision = import_bignumber4.default.clone({ DECIMAL_PLACES: 60 });
1828
+ function orientPrice(sqrtPrice, tokenInIsToken0) {
1829
+ const poolPrice = new HighPrecision(sqrtPrice).pow(2);
1830
+ return tokenInIsToken0 ? poolPrice : new HighPrecision(1).dividedBy(poolPrice);
1831
+ }
1832
+ function isQuoteWire(value) {
1833
+ if (typeof value !== "object" || value === null) return false;
1834
+ const wire = value;
1835
+ const requiredStrings = [
1836
+ "amountIn",
1837
+ "amountOut",
1838
+ "tradingFees",
1839
+ "protocolFees",
1840
+ "totalFees",
1841
+ "feeTokenSymbol",
1842
+ "token0Symbol",
1843
+ "token1Symbol"
1844
+ ];
1845
+ return wire["contractVersion"] === "v2" && typeof wire["fee"] === "number" && Number.isFinite(wire["fee"]) && requiredStrings.every((key) => typeof wire[key] === "string") && typeof wire["newTick"] === "number" && Number.isInteger(wire["newTick"]) && typeof wire["tokenInIsToken0"] === "boolean" && optionalPositiveDecimal(wire["currentSqrtPrice"]) && optionalPositiveDecimal(wire["newSqrtPrice"]) && optionalPositiveDecimal(wire["currentPrice"]) && optionalPositiveDecimal(wire["newPrice"]) && (wire["currentSqrtPrice"] !== void 0 || wire["currentPrice"] !== void 0) && (wire["newSqrtPrice"] !== void 0 || wire["newPrice"] !== void 0);
1846
+ }
1847
+ function optionalPositiveDecimal(value) {
1848
+ if (value === void 0) return true;
1849
+ if (typeof value !== "string") return false;
1850
+ try {
1851
+ const decimal = new import_bignumber4.default(value);
1852
+ return decimal.isFinite() && decimal.isGreaterThan(0);
1853
+ } catch {
1854
+ return false;
1855
+ }
1856
+ }
1857
+ function mapQuoteError(error, tokenIn, tokenOut, fee) {
1858
+ if (error instanceof GSwapSDKError) {
1859
+ const status = readStatus(error.details);
1860
+ const message = readMessage(error.details) ?? error.message;
1861
+ if (status === 404 || /no pool|no pools|pool does not exist/iu.test(message)) {
1862
+ return GSwapSDKError.noPoolAvailableError(tokenIn, tokenOut, fee);
1489
1863
  }
1490
- const priceImpact = newPrice.minus(currentPrice).dividedBy(currentPrice);
1491
- return {
1492
- amount0: response.amount0,
1493
- amount1: response.amount1,
1494
- currentPoolSqrtPrice: response.currentSqrtPrice,
1495
- newPoolSqrtPrice: response.newSqrtPrice,
1496
- currentPrice,
1497
- newPrice,
1498
- inTokenAmount: tokenInAmount.abs(),
1499
- outTokenAmount: tokenOutAmount.abs(),
1500
- priceImpact,
1501
- feeTier: fee
1502
- };
1864
+ if (status === 400 && /insufficient liquidity/iu.test(message)) {
1865
+ return GSwapSDKError.insufficientLiquidityError(tokenIn, tokenOut, fee);
1866
+ }
1867
+ return error;
1868
+ }
1869
+ return new GSwapSDKError("Quote request failed.", "HTTP_ERROR", {
1870
+ cause: error,
1871
+ tokenIn,
1872
+ tokenOut,
1873
+ fee
1874
+ });
1875
+ }
1876
+ function readStatus(details) {
1877
+ const status = details == null ? void 0 : details["status"];
1878
+ return typeof status === "number" ? status : void 0;
1879
+ }
1880
+ function readMessage(details) {
1881
+ const direct = details == null ? void 0 : details["message"];
1882
+ if (typeof direct === "string") return direct;
1883
+ const body = details == null ? void 0 : details["body"];
1884
+ if (typeof body !== "object" || body === null || Array.isArray(body)) return void 0;
1885
+ const message = body["message"];
1886
+ return typeof message === "string" ? message : void 0;
1887
+ }
1888
+ function validateOptionalFee(fee) {
1889
+ if (fee !== void 0 && !ALL_FEE_TIERS.some((tier) => Number(tier) === Number(fee))) {
1890
+ throw new GSwapSDKError(`Invalid fee tier: ${fee}`, "VALIDATION_ERROR", { fee });
1503
1891
  }
1504
- async getSingleQuoteExactInput(tokenIn, tokenOut, fee, amountIn) {
1505
- return this.getSingleQuote(tokenIn, tokenOut, fee, amountIn, true);
1892
+ }
1893
+ function toDecimalString(amount) {
1894
+ return new import_bignumber4.default(amount).toFixed();
1895
+ }
1896
+ async function resolveSymbol2(symbols, token) {
1897
+ const resolved = await symbols.resolve(token);
1898
+ if (resolved.symbol.length > 0) return resolved.symbol;
1899
+ throw new GSwapSDKError("Token could not be resolved to a trading symbol.", "SYMBOL_NOT_FOUND", {
1900
+ token
1901
+ });
1902
+ }
1903
+
1904
+ // src/classes/symbols.ts
1905
+ var SYMBOL_CACHE_TTL_MS = 6e4;
1906
+ var Symbols = class {
1907
+ /** Create a symbol service backed by the swap backend. */
1908
+ constructor(gateway) {
1909
+ this.gateway = gateway;
1910
+ this.http = new HttpClient(gateway.httpRequestor, gateway.requestTimeoutMs);
1911
+ }
1912
+ gateway;
1913
+ cachedSymbols;
1914
+ cachedAt = 0;
1915
+ refreshInFlight;
1916
+ http;
1917
+ /** List all registered GalaChainDex trading symbols, cached for 60 seconds. */
1918
+ async list() {
1919
+ if (this.cachedSymbols !== void 0 && Date.now() - this.cachedAt < SYMBOL_CACHE_TTL_MS) {
1920
+ return this.cachedSymbols;
1921
+ }
1922
+ if (this.refreshInFlight !== void 0) return this.refreshInFlight;
1923
+ this.refreshInFlight = this.http.sendGetRequest(this.gateway.dexBackendBaseUrl, "/v2/trade", "/symbols").then((response) => parseSymbols(response.data)).then((symbols) => {
1924
+ this.cachedSymbols = symbols;
1925
+ this.cachedAt = Date.now();
1926
+ return symbols;
1927
+ }).finally(() => {
1928
+ this.refreshInFlight = void 0;
1929
+ });
1930
+ return this.refreshInFlight;
1506
1931
  }
1507
- async getSingleQuoteExactOutput(tokenIn, tokenOut, amountOut, fee) {
1508
- return this.getSingleQuote(tokenIn, tokenOut, fee, amountOut, false);
1932
+ /** Invalidate the cached symbol registry after a write changes registration state.
1933
+ *
1934
+ * @example
1935
+ * ```ts
1936
+ * gSwap.symbols.invalidate();
1937
+ * ```
1938
+ */
1939
+ invalidate() {
1940
+ this.cachedSymbols = void 0;
1941
+ this.cachedAt = 0;
1509
1942
  }
1510
- async sendQuoteRequest(endpoint, body) {
1511
- const response = await this.httpClient.sendPostRequest(
1512
- this.gatewayBaseUrl,
1513
- this.dexContractBasePath,
1514
- endpoint,
1515
- body
1943
+ /** Force one registry refresh, bypassing the cache.
1944
+ *
1945
+ * @example
1946
+ * ```ts
1947
+ * const symbols = await gSwap.symbols.refresh();
1948
+ * ```
1949
+ */
1950
+ async refresh() {
1951
+ this.invalidate();
1952
+ this.refreshInFlight = void 0;
1953
+ return this.list();
1954
+ }
1955
+ /** Resolve a symbol or token class key to its registered symbol metadata. */
1956
+ async resolve(ref) {
1957
+ let symbols = await this.list();
1958
+ const composite = typeof ref === "string" ? isCompositeKey(ref) ? compositeKeyOf(parseTokenClassKey(ref)) : void 0 : compositeKeyOf(ref);
1959
+ const resolved = symbols.find(
1960
+ (entry) => entry.symbol === ref || composite !== void 0 && compositeKeyOf(entry) === composite
1516
1961
  );
1517
- const responseBody = response;
1962
+ if (resolved === void 0) {
1963
+ symbols = await this.refresh();
1964
+ const refreshed = symbols.find(
1965
+ (entry) => entry.symbol === ref || composite !== void 0 && compositeKeyOf(entry) === composite
1966
+ );
1967
+ if (refreshed === void 0) throw GSwapSDKError.unknownTokenError(ref);
1968
+ return refreshed;
1969
+ }
1970
+ return resolved;
1971
+ }
1972
+ /** Resolve and put two token references in canonical trading-symbol order. */
1973
+ async orderPair(a, b) {
1974
+ const [resolvedA, resolvedB] = await Promise.all([this.resolve(a), this.resolve(b)]);
1975
+ const [token0Symbol, token1Symbol] = orderSymbols(resolvedA.symbol, resolvedB.symbol);
1518
1976
  return {
1519
- amount0: (0, import_bignumber4.default)(responseBody.Data.amount0),
1520
- amount1: (0, import_bignumber4.default)(responseBody.Data.amount1),
1521
- currentSqrtPrice: (0, import_bignumber4.default)(responseBody.Data.currentSqrtPrice),
1522
- newSqrtPrice: (0, import_bignumber4.default)(responseBody.Data.newSqrtPrice)
1977
+ token0: token0Symbol === resolvedA.symbol ? resolvedA : resolvedB,
1978
+ token1: token1Symbol === resolvedB.symbol ? resolvedB : resolvedA,
1979
+ flipped: token0Symbol !== resolvedA.symbol
1523
1980
  };
1524
1981
  }
1525
1982
  };
1983
+ function parseSymbols(value) {
1984
+ if (!Array.isArray(value) || !value.every(isTradingSymbol)) {
1985
+ throw new GSwapSDKError("Backend returned an invalid symbol list.", "INVALID_CHAIN_RESPONSE", {
1986
+ data: value
1987
+ });
1988
+ }
1989
+ return value;
1990
+ }
1991
+ function isTradingSymbol(value) {
1992
+ if (typeof value !== "object" || value === null) return false;
1993
+ const entry = value;
1994
+ return typeof entry["symbol"] === "string" && typeof entry["collection"] === "string" && typeof entry["category"] === "string" && typeof entry["type"] === "string" && typeof entry["additionalKey"] === "string" && typeof entry["decimals"] === "number" && Number.isInteger(entry["decimals"]) && entry["decimals"] >= 0;
1995
+ }
1996
+ function isCompositeKey(ref) {
1997
+ return ref.includes("|") || ref.includes("$");
1998
+ }
1526
1999
 
1527
2000
  // src/classes/swaps.ts
1528
2001
  var import_bignumber5 = __toESM(require("bignumber.js"), 1);
1529
- var MIN_SQRT_PRICE_LIMIT = "0.000000000000000000094212147";
1530
- var MAX_SQRT_PRICE_LIMIT = "18446050999999999999";
2002
+ var UNIQUE_KEY_PATTERN = /^[A-Za-z0-9._:-]{1,128}$/u;
1531
2003
  var Swaps = class {
1532
- constructor(bundlerService, options) {
1533
- this.bundlerService = bundlerService;
1534
- this.options = options;
2004
+ /**
2005
+ * Creates a swap service.
2006
+ *
2007
+ * @example
2008
+ * ```ts
2009
+ * const swaps = new Swaps(gateway, symbols, signer, 'client|alice');
2010
+ * ```
2011
+ */
2012
+ constructor(gateway, symbols, signer, walletAddress) {
2013
+ this.gateway = gateway;
2014
+ this.symbols = symbols;
2015
+ this.signer = signer;
2016
+ this.walletAddress = walletAddress;
1535
2017
  }
2018
+ gateway;
2019
+ symbols;
2020
+ signer;
2021
+ walletAddress;
1536
2022
  /**
1537
- * Executes a token swap transaction.
1538
- * @param walletAddress - The wallet address executing the swap.
1539
- * @param tokenIn - The input token to sell.
1540
- * @param tokenOut - The output token to buy.
1541
- * @param fee - The pool fee tier.
1542
- * @param amount - Swap parameters specifying either exact input or exact output.
1543
- * @param amount.exactIn - For exact input swaps, the exact amount of input tokens to sell.
1544
- * @param amount.amountOutMinimum - For exact input swaps, the minimum amount of output tokens to buy (slippage protection).
1545
- * @param amount.exactOut - For exact output swaps, the exact amount of output tokens to buy.
1546
- * @param amount.amountInMaximum - For exact output swaps, the maximum amount of input tokens to sell (slippage protection).
1547
- * @returns Pending transaction.
2023
+ * Signs and submits an exact-input or exact-output RequestTrade DTO. The returned transaction
2024
+ * is QUEUED; call `await tx.confirm()` to obtain the executed trade or chain rejection.
2025
+ *
1548
2026
  * @example
1549
- * ```typescript
1550
- * // Exact input swap: sell 100 GALA for USDC
1551
- * const result = await swapsService.swap(
1552
- * 'GALA|Unit|none|none',
1553
- * 'GUSDC|Unit|none|none',
1554
- * 500,
1555
- * { exactIn: '100', amountOutMinimum: '45' },
1556
- * 'eth|123...abc', // your wallet address
1557
- * );
1558
- * console.log('Swap successful:', result);
2027
+ * ```ts
2028
+ * const submitted = await swaps.swap('GALA', 'GUSDC', 3000, {
2029
+ * exactIn: '100',
2030
+ * amountOutMinimum: '14',
2031
+ * });
1559
2032
  * ```
1560
2033
  */
1561
- async swap(tokenIn, tokenOut, fee, amount, walletAddress) {
1562
- var _a, _b;
1563
- walletAddress = walletAddress ?? ((_a = this.options) == null ? void 0 : _a.walletAddress);
1564
- validateWalletAddress(walletAddress);
2034
+ async swap(tokenIn, tokenOut, fee, amount) {
1565
2035
  validateFee(fee);
2036
+ const dto = await this.buildRequestTradeDto(tokenIn, tokenOut, fee, amount);
2037
+ if (this.signer === void 0) throw GSwapSDKError.noSignerError();
2038
+ const signedDto = await this.signer.signObject("RequestTrade", dto);
2039
+ const submitOptions = this.walletAddress === void 0 ? {} : { walletAddress: this.walletAddress };
2040
+ return this.gateway.submit("RequestTrade", signedDto, submitOptions);
2041
+ }
2042
+ /**
2043
+ * Confirm a swap by its durable `uniqueKey` without resubmitting it. This is the recovery
2044
+ * path after a `CONFIRMATION_TIMEOUT`, a `SUBMISSION_OUTCOME_UNKNOWN`, a `409` replay
2045
+ * conflict, or a process restart: it reads the same apply-results record that
2046
+ * `SubmittedTransaction.confirm()` reads (retained for 7 days) and resolves the applied trade
2047
+ * or throws the chain's rejection. The first read happens immediately unless
2048
+ * `initialDelayMs` is given.
2049
+ *
2050
+ * @example
2051
+ * ```ts
2052
+ * const applied = await swaps.confirmSwap('gswap-sdk-2f1c…');
2053
+ * ```
2054
+ */
2055
+ async confirmSwap(uniqueKey, options) {
2056
+ if (!UNIQUE_KEY_PATTERN.test(uniqueKey)) {
2057
+ throw new GSwapSDKError(
2058
+ 'uniqueKey must be 1-128 characters of A-Z, a-z, 0-9, ".", "_", ":" or "-".',
2059
+ "VALIDATION_ERROR",
2060
+ { uniqueKey }
2061
+ );
2062
+ }
2063
+ const transaction = new SubmittedTransaction({
2064
+ method: "RequestTrade",
2065
+ uniqueKey,
2066
+ transactionId: null,
2067
+ mode: "queued",
2068
+ result: void 0,
2069
+ dexBackendBaseUrl: this.gateway.dexBackendBaseUrl,
2070
+ httpRequestor: this.gateway.httpRequestor,
2071
+ requestTimeoutMs: this.gateway.requestTimeoutMs
2072
+ });
2073
+ return transaction.confirm({ initialDelayMs: 0, ...options });
2074
+ }
2075
+ async buildRequestTradeDto(tokenIn, tokenOut, fee, amount) {
2076
+ const tokenInSymbol = await resolveSymbol3(this.symbols, tokenIn);
2077
+ const tokenOutSymbol = await resolveSymbol3(this.symbols, tokenOut);
2078
+ const ordered = readOrderedSymbols(orderSymbols(tokenInSymbol, tokenOutSymbol));
2079
+ const tokenInIsToken0 = tokenInSymbol === ordered.token0;
2080
+ const dto = {
2081
+ token0: ordered.token0,
2082
+ token1: ordered.token1,
2083
+ fee,
2084
+ uniqueKey: `gswap-sdk-${globalThis.crypto.randomUUID()}`
2085
+ };
1566
2086
  if ("exactIn" in amount) {
1567
- validateNumericAmount(amount.exactIn, "exactIn");
2087
+ const exactIn = amount.exactIn;
2088
+ if (exactIn === void 0) {
2089
+ throw new GSwapSDKError("exactIn is required for an exact-input swap.", "VALIDATION_ERROR");
2090
+ }
2091
+ validateNumericAmount(exactIn, "exactIn");
2092
+ dto[tokenInIsToken0 ? "sell0Qty" : "sell1Qty"] = toDecimalString2(exactIn);
1568
2093
  if (amount.amountOutMinimum !== void 0) {
1569
2094
  validateNumericAmount(amount.amountOutMinimum, "amountOutMinimum", true);
2095
+ dto["amountOutMinimum"] = toDecimalString2(amount.amountOutMinimum);
1570
2096
  }
1571
2097
  } else {
1572
- validateNumericAmount(amount.exactOut, "exactOut");
2098
+ const exactOut = amount.exactOut;
2099
+ if (exactOut === void 0) {
2100
+ throw new GSwapSDKError(
2101
+ "exactOut is required for an exact-output swap.",
2102
+ "VALIDATION_ERROR"
2103
+ );
2104
+ }
2105
+ validateNumericAmount(exactOut, "exactOut");
2106
+ dto[tokenInIsToken0 ? "buy1Qty" : "buy0Qty"] = toDecimalString2(exactOut);
1573
2107
  if (amount.amountInMaximum !== void 0) {
1574
- validateNumericAmount(amount.amountInMaximum, "amountInMaximum");
2108
+ validateNumericAmount(amount.amountInMaximum, "amountInMaximum", true);
2109
+ dto["amountInMaximum"] = toDecimalString2(amount.amountInMaximum);
1575
2110
  }
1576
2111
  }
1577
- const ordering = getTokenOrdering(tokenIn, tokenOut, false);
1578
- const zeroForOne = stringifyTokenClassKey(tokenIn) === stringifyTokenClassKey(ordering.token0);
1579
- const rawAmount = "exactIn" in amount ? (0, import_bignumber5.default)(amount.exactIn).toFixed() : (0, import_bignumber5.default)(amount.exactOut).multipliedBy(-1).toFixed();
1580
- const rawAmountOutMinimum = "exactIn" in amount ? ((_b = amount.amountOutMinimum) == null ? void 0 : _b.toString()) ? (0, import_bignumber5.default)(amount.amountOutMinimum).multipliedBy(-1).toFixed() : void 0 : (0, import_bignumber5.default)(amount.exactOut ?? 0).multipliedBy(-1).toFixed();
1581
- const rawAmountInMaximum = "exactIn" in amount ? (0, import_bignumber5.default)(amount.exactIn).toFixed() : amount.amountInMaximum ? (0, import_bignumber5.default)(amount.amountInMaximum).toFixed() : void 0;
1582
- const toSign = {
1583
- token0: parseTokenClassKey(ordering.token0),
1584
- token1: parseTokenClassKey(ordering.token1),
1585
- fee,
1586
- amount: rawAmount,
1587
- zeroForOne,
1588
- sqrtPriceLimit: ordering.zeroForOne ? MIN_SQRT_PRICE_LIMIT : MAX_SQRT_PRICE_LIMIT,
1589
- recipient: walletAddress,
1590
- amountOutMinimum: rawAmountOutMinimum,
1591
- amountInMaximum: rawAmountInMaximum
1592
- };
1593
- const token0StringKey = stringifyTokenClassKey(ordering.token0, "$");
1594
- const token1StringKey = stringifyTokenClassKey(ordering.token1, "$");
1595
- const poolString = `$pool$${token0StringKey}$${token1StringKey}$${fee}`;
1596
- const tokenBalance0 = `$tokenBalance$${token0StringKey}$${walletAddress}`;
1597
- const tokenBalance1 = `$tokenBalance$${token1StringKey}$${walletAddress}`;
1598
- const tokenBalance0Pool = `$tokenBalance$${token0StringKey}$${poolString}`;
1599
- const tokenBalance1Pool = `$tokenBalance$${token1StringKey}$${poolString}`;
1600
- const stringsInstructions = [
1601
- poolString,
1602
- tokenBalance0,
1603
- tokenBalance1,
1604
- tokenBalance0Pool,
1605
- tokenBalance1Pool
1606
- ];
1607
- return this.bundlerService.sendBundlerRequest("Swap", toSign, stringsInstructions);
2112
+ return dto;
2113
+ }
2114
+ };
2115
+ function toDecimalString2(amount) {
2116
+ return new import_bignumber5.default(amount).toFixed();
2117
+ }
2118
+ async function resolveSymbol3(symbols, token) {
2119
+ const resolved = await symbols.resolve(token);
2120
+ if (resolved.symbol.length > 0) return resolved.symbol;
2121
+ throw new GSwapSDKError("Token could not be resolved to a trading symbol.", "SYMBOL_NOT_FOUND", {
2122
+ token
2123
+ });
2124
+ }
2125
+
2126
+ // src/types/env.ts
2127
+ var GSWAP_ENVIRONMENTS = {
2128
+ prod: {
2129
+ dexBackendBaseUrl: "https://dex-backend-prod1.defi.gala.com"
2130
+ },
2131
+ stage: {
2132
+ dexBackendBaseUrl: "https://swap-backend.stage.defi.ovh.gala.com"
1608
2133
  }
1609
2134
  };
1610
2135
 
1611
2136
  // src/classes/gswap.ts
1612
2137
  var GSwap = class {
1613
- gatewayBaseUrl;
1614
- dexContractBasePath;
1615
- tokenContractBasePath;
1616
- bundlerBaseUrl;
1617
- bundlingAPIBasePath;
1618
2138
  dexBackendBaseUrl;
1619
- transactionWaitTimeoutMs;
2139
+ chainCallTimeoutMs;
1620
2140
  signer;
2141
+ walletAddress;
2142
+ gateway;
2143
+ symbols;
2144
+ quoting;
2145
+ pools;
2146
+ positions;
2147
+ swaps;
2148
+ assets;
1621
2149
  /**
1622
- * Quoting functionality for price discovery and trade estimation.
1623
- * Use this to get quotes for token swaps without executing transactions.
2150
+ * Create an SDK client for prod or stage.
1624
2151
  *
1625
2152
  * @example
1626
2153
  * ```typescript
1627
- * // Get the best quote across all fee tiers
1628
- * const quote = await gSwap.quoting.quoteExactInput(tokenA, tokenB, amount);
2154
+ * const gSwap = new GSwap({ env: 'stage' });
2155
+ * const symbols = await gSwap.symbols.list();
1629
2156
  * ```
1630
2157
  */
1631
- quoting;
2158
+ constructor(options) {
2159
+ const environment = GSWAP_ENVIRONMENTS[(options == null ? void 0 : options.env) ?? "prod"];
2160
+ this.dexBackendBaseUrl = trimTrailingSlash2(
2161
+ (options == null ? void 0 : options.dexBackendBaseUrl) ?? environment.dexBackendBaseUrl
2162
+ );
2163
+ this.chainCallTimeoutMs = (options == null ? void 0 : options.chainCallTimeoutMs) ?? 3e4;
2164
+ this.signer = options == null ? void 0 : options.signer;
2165
+ this.walletAddress = options == null ? void 0 : options.walletAddress;
2166
+ const httpClient = new HttpClient(options == null ? void 0 : options.httpRequestor, this.chainCallTimeoutMs);
2167
+ this.gateway = new ChainGateway({
2168
+ dexBackendBaseUrl: this.dexBackendBaseUrl,
2169
+ chainCallTimeoutMs: this.chainCallTimeoutMs,
2170
+ ...(options == null ? void 0 : options.httpRequestor) === void 0 ? {} : { httpRequestor: options.httpRequestor },
2171
+ ...this.walletAddress === void 0 ? {} : { walletAddress: this.walletAddress }
2172
+ });
2173
+ this.symbols = new Symbols(this.gateway);
2174
+ this.quoting = new Quoting(this.dexBackendBaseUrl, httpClient, this.symbols);
2175
+ this.pools = new Pools(this.gateway, this.symbols);
2176
+ this.positions = new Positions(this.gateway, this.symbols, this.signer, this.walletAddress);
2177
+ this.swaps = new Swaps(this.gateway, this.symbols, this.signer, this.walletAddress);
2178
+ this.assets = new Assets(this.dexBackendBaseUrl, httpClient);
2179
+ }
2180
+ };
2181
+ function trimTrailingSlash2(value) {
2182
+ return value.replace(/\/$/u, "");
2183
+ }
2184
+
2185
+ // src/classes/signers.ts
2186
+ var import_api = require("@gala-chain/api");
2187
+
2188
+ // src/utils/eip712.ts
2189
+ function generateEIP712Types(typeName, params) {
2190
+ const types = {};
2191
+ types[typeName] = [];
2192
+ function fieldsFor(name) {
2193
+ const fields = types[name];
2194
+ if (!fields) throw new Error(`Missing EIP-712 type definition for ${name}`);
2195
+ return fields;
2196
+ }
2197
+ function addField(name, fieldValue, parentTypeName, onlyGetType = false) {
2198
+ if (Array.isArray(fieldValue)) {
2199
+ const type = fieldValue.length > 0 ? addField(name, fieldValue[0], parentTypeName, true) : "string";
2200
+ if (!onlyGetType) fieldsFor(parentTypeName).push({ name, type: (type ?? name) + "[]" });
2201
+ } else if (typeof fieldValue === "object" && fieldValue !== null) {
2202
+ if (types[name]) {
2203
+ throw new Error("Name collisions not yet supported");
2204
+ }
2205
+ types[name] = [];
2206
+ Object.entries(fieldValue).forEach(([key, value]) => {
2207
+ addField(key, value, name);
2208
+ });
2209
+ if (!onlyGetType) fieldsFor(parentTypeName).push({ name, type: name });
2210
+ } else {
2211
+ let eipType;
2212
+ switch (typeof fieldValue) {
2213
+ case "string":
2214
+ eipType = "string";
2215
+ break;
2216
+ case "number":
2217
+ eipType = "int256";
2218
+ break;
2219
+ case "boolean":
2220
+ eipType = "bool";
2221
+ break;
2222
+ case "bigint":
2223
+ case "symbol":
2224
+ case "undefined":
2225
+ case "function":
2226
+ case "object":
2227
+ throw new Error(
2228
+ `Unsupported type, ${typeof fieldValue}, value: ${Object.prototype.toString.call(fieldValue)}`
2229
+ );
2230
+ }
2231
+ if (onlyGetType) {
2232
+ return eipType;
2233
+ }
2234
+ fieldsFor(parentTypeName).push({ name, type: eipType });
2235
+ }
2236
+ return void 0;
2237
+ }
2238
+ Object.entries(params).forEach(([key, value]) => {
2239
+ addField(key, value, typeName);
2240
+ });
2241
+ return types;
2242
+ }
2243
+ var EIP712_DOMAIN = { name: "ethereum", chainId: 1 };
2244
+ var EIP712_CAPABLE_METHODS = /* @__PURE__ */ new Set([
2245
+ "RequestTrade",
2246
+ "AddLiquidity",
2247
+ "RemoveLiquidity",
2248
+ "CollectPositionFees"
2249
+ ]);
2250
+ function numericOr0(dto, field) {
2251
+ return typeof dto[field] === "number" ? dto[field] : 0;
2252
+ }
2253
+ function completeAddLiquidityFields(dto) {
2254
+ return {
2255
+ ...dto,
2256
+ amount0Desired: numericOr0(dto, "amount0Desired"),
2257
+ amount0Min: numericOr0(dto, "amount0Min"),
2258
+ amount1Desired: numericOr0(dto, "amount1Desired"),
2259
+ amount1Min: numericOr0(dto, "amount1Min"),
2260
+ positionId: dto["positionId"] ?? ""
2261
+ };
2262
+ }
2263
+ function completeRemoveLiquidityFields(dto) {
2264
+ return {
2265
+ ...dto,
2266
+ amount: numericOr0(dto, "amount"),
2267
+ amount0Min: numericOr0(dto, "amount0Min"),
2268
+ amount1Min: numericOr0(dto, "amount1Min"),
2269
+ positionId: dto["positionId"] ?? ""
2270
+ };
2271
+ }
2272
+ function completeCollectPositionFeesFields(dto) {
2273
+ return {
2274
+ ...dto,
2275
+ amount0Requested: numericOr0(dto, "amount0Requested"),
2276
+ amount1Requested: numericOr0(dto, "amount1Requested"),
2277
+ positionId: dto["positionId"] ?? ""
2278
+ };
2279
+ }
2280
+ var EIP712_FIELD_COMPLETERS = {
2281
+ AddLiquidity: completeAddLiquidityFields,
2282
+ RemoveLiquidity: completeRemoveLiquidityFields,
2283
+ CollectPositionFees: completeCollectPositionFeesFields
2284
+ };
2285
+ function buildEIP712Payload(methodName, dto) {
2286
+ const completer = EIP712_FIELD_COMPLETERS[methodName];
2287
+ const completedDto = completer ? completer(dto) : dto;
2288
+ const types = generateEIP712Types(methodName, completedDto);
2289
+ return { completedDto, types };
2290
+ }
2291
+
2292
+ // src/classes/signers.ts
2293
+ var ETHEREUM_SIGNED_MESSAGE = String.fromCharCode(25) + "Ethereum Signed Message:\n";
2294
+ function personalSignPrefixFor(dto) {
2295
+ const withoutPrefix = { ...dto };
2296
+ delete withoutPrefix["prefix"];
2297
+ const message = (0, import_api.serialize)(withoutPrefix);
2298
+ return ETHEREUM_SIGNED_MESSAGE + String(new TextEncoder().encode(message).length);
2299
+ }
2300
+ function getWalletProvider() {
2301
+ var _a;
2302
+ const globalObject = globalThis;
2303
+ const wallet = (_a = globalObject.window) == null ? void 0 : _a.gala;
2304
+ if (!wallet) {
2305
+ throw new GSwapSDKError(
2306
+ "Gala wallet is not available. Please ensure the Gala wallet is connected.",
2307
+ "GALA_WALLET_NOT_AVAILABLE"
2308
+ );
2309
+ }
2310
+ return wallet;
2311
+ }
2312
+ function getNativeSignature(value) {
2313
+ if (typeof value !== "string") {
2314
+ throw new GSwapSDKError("Wallet returned an invalid signature.", "INVALID_SIGNATURE");
2315
+ }
2316
+ if (!/^[0-9a-fA-F]{130}$/u.test(value)) {
2317
+ throw new GSwapSDKError("Wallet returned an invalid native signature.", "INVALID_SIGNATURE");
2318
+ }
2319
+ return value;
2320
+ }
2321
+ function getPersonalSignature(value) {
2322
+ if (typeof value !== "string" || !/^(?:0x)?[0-9a-fA-F]{130}$/u.test(value)) {
2323
+ throw new GSwapSDKError(
2324
+ "Wallet returned an invalid personal-sign signature.",
2325
+ "INVALID_SIGNATURE"
2326
+ );
2327
+ }
2328
+ return value;
2329
+ }
2330
+ function normalizeEthereumAccount(address, allowGalaPrefix) {
2331
+ const normalized = allowGalaPrefix && address.startsWith("eth|") ? address.slice(4) : address;
2332
+ if (!/^0x[0-9a-fA-F]{40}$/u.test(normalized)) {
2333
+ throw new GSwapSDKError(
2334
+ "Wallet address must be the selected Ethereum account in 0x-prefixed form.",
2335
+ "VALIDATION_ERROR",
2336
+ { type: "INVALID_WALLET_ADDRESS", value: address }
2337
+ );
2338
+ }
2339
+ return `0x${normalized.slice(2).toLowerCase()}`;
2340
+ }
2341
+ function getErrorMessage(error) {
2342
+ if (error instanceof Error) {
2343
+ return error.message;
2344
+ }
2345
+ if (typeof error === "string") {
2346
+ return error;
2347
+ }
2348
+ if (typeof error === "object" && error !== null && "message" in error) {
2349
+ const message = error.message;
2350
+ return typeof message === "string" ? message : "";
2351
+ }
2352
+ return "";
2353
+ }
2354
+ function isUnsupportedNativeSchemeError(error) {
2355
+ return /unsupported|unknown scheme/i.test(getErrorMessage(error));
2356
+ }
2357
+ function personalSign(provider, dto, walletAddress, methodName) {
2358
+ const prefix = personalSignPrefixFor(dto);
2359
+ const payload = { ...dto, prefix };
2360
+ const params = methodName !== void 0 ? [(0, import_api.serialize)(payload), walletAddress, methodName] : [(0, import_api.serialize)(dto), walletAddress];
2361
+ return provider.request({ method: methodName !== void 0 ? "gala_signChainDto" : "personal_sign", params }).then((value) => ({ ...payload, signature: getPersonalSignature(value) }));
2362
+ }
2363
+ function signEIP712(provider, methodName, dto, walletAddress) {
2364
+ const { completedDto, types } = buildEIP712Payload(methodName, dto);
2365
+ const prefix = personalSignPrefixFor(completedDto);
2366
+ const message = { ...completedDto, prefix };
2367
+ const eip712Domain = [
2368
+ { name: "name", type: "string" },
2369
+ { name: "chainId", type: "uint256" }
2370
+ ];
2371
+ const payload = {
2372
+ domain: EIP712_DOMAIN,
2373
+ types: { ...types, EIP712Domain: eip712Domain },
2374
+ primaryType: methodName,
2375
+ message
2376
+ };
2377
+ return provider.request({
2378
+ method: "eth_signTypedData_v4",
2379
+ params: [walletAddress, JSON.stringify(payload)]
2380
+ }).then((value) => ({
2381
+ ...message,
2382
+ types,
2383
+ domain: EIP712_DOMAIN,
2384
+ signature: getPersonalSignature(value)
2385
+ }));
2386
+ }
2387
+ var PrivateKeySigner = class {
2388
+ keyBuffer;
1632
2389
  /**
1633
- * Position management operations for liquidity positions.
1634
- * Use this to manage liquidity positions including creation, modification, and fee collection.
2390
+ * Creates a native signer from a hex or base64 secp256k1 private key.
1635
2391
  *
1636
2392
  * @example
1637
2393
  * ```typescript
1638
- * // Get all user positions
1639
- * const positions = await gSwap.positions.getUserPositions('eth|123...abc');
2394
+ * const signer = new PrivateKeySigner('1'.padStart(64, '0'));
2395
+ * const signed = await signer.signObject('RequestTrade', { token0: 'GALA', token1: 'GUSDC' });
2396
+ * ```
2397
+ */
2398
+ constructor(privateKey) {
2399
+ this.keyBuffer = import_api.signatures.normalizePrivateKey(privateKey);
2400
+ }
2401
+ /**
2402
+ * Signs the DTO with GalaChain native signing.
1640
2403
  *
1641
- * // Add liquidity to a position using price range
1642
- * const result = await gSwap.positions.addLiquidityByPrice({
1643
- * walletAddress: 'eth|123...abc',
1644
- * positionId: '',
1645
- * token0: 'GALA|Unit|none|none',
1646
- * token1: 'GUSDC|Unit|none|none',
1647
- * fee: 500,
1648
- * tickSpacing: 10,
1649
- * minPrice: '0.45',
1650
- * maxPrice: '0.55',
1651
- * amount0Desired: '100',
1652
- * amount1Desired: '50',
1653
- * amount0Min: '95',
1654
- * amount1Min: '47.5'
2404
+ * @example
2405
+ * ```typescript
2406
+ * const signed = await signer.signObject('RequestTrade', {
2407
+ * token0: 'GALA',
2408
+ * token1: 'GUSDC',
2409
+ * fee: 3000,
1655
2410
  * });
1656
2411
  * ```
1657
2412
  */
1658
- positions;
2413
+ signObject(_methodName, dto) {
2414
+ const signature = import_api.signatures.getSignature(dto, this.keyBuffer);
2415
+ return Promise.resolve({ ...dto, signature });
2416
+ }
2417
+ };
2418
+ var GalaWalletSigner = class {
2419
+ /** The wallet address supplied to Gala Wallet for each signing request. */
2420
+ walletAddress;
2421
+ /** The scheme currently used, including any automatic native-to-personal fallback. */
2422
+ effectiveScheme;
1659
2423
  /**
1660
- * Token swap operations for trading tokens.
1661
- * Use this to execute token swaps on the DEX.
2424
+ * Creates a Gala Wallet signer.
1662
2425
  *
1663
2426
  * @example
1664
2427
  * ```typescript
1665
- * // Execute an exact input swap: sell 100 GALA for USDC
1666
- * const result = await gSwap.swaps.swap(
1667
- * 'GALA|Unit|none|none',
1668
- * 'GUSDC|Unit|none|none',
1669
- * 500,
1670
- * { exactIn: '100', amountOutMinimum: '45' },
1671
- * 'eth|123...abc', // your wallet address
1672
- * );
2428
+ * const signer = new GalaWalletSigner('0x0123456789012345678901234567890123456789');
2429
+ * const signed = await signer.signObject('RequestTrade', { token0: 'GALA', token1: 'GUSDC' });
1673
2430
  * ```
2431
+ *
2432
+ * @param walletAddress - The selected Ethereum account (`0x` plus 40 hex digits); Gala Wallet's
2433
+ * `eth|0x...` form is also accepted and normalized.
2434
+ * @param options - Select native signing or personal-sign explicitly.
1674
2435
  */
1675
- swaps;
2436
+ constructor(walletAddress, options = {}) {
2437
+ this.walletAddress = normalizeEthereumAccount(walletAddress, true);
2438
+ this.effectiveScheme = options.scheme ?? "native";
2439
+ }
1676
2440
  /**
1677
- * User asset management operations.
1678
- * Use this to retrieve user token balances and asset information.
2441
+ * Signs a DTO through `gala_signChainDto`.
1679
2442
  *
1680
2443
  * @example
1681
2444
  * ```typescript
1682
- * // Get user assets with pagination
1683
- * const assets = await gSwap.assets.getUserAssets('eth|123...abc', 1, 20);
1684
- * console.log(`User has ${assets.count} different tokens`);
2445
+ * const signed = await signer.signObject('AddLiquidity', {
2446
+ * token0: 'GALA',
2447
+ * token1: 'GUSDC',
2448
+ * tickLower: -60,
2449
+ * tickUpper: 60,
2450
+ * });
1685
2451
  * ```
1686
2452
  */
1687
- assets;
2453
+ async signObject(methodName, dto) {
2454
+ const wallet = getWalletProvider();
2455
+ if (this.effectiveScheme === "personal-sign") {
2456
+ return personalSign(wallet, dto, this.walletAddress, methodName);
2457
+ }
2458
+ try {
2459
+ const signature = await wallet.request({
2460
+ method: "gala_signChainDto",
2461
+ params: [(0, import_api.serialize)(dto), this.walletAddress, methodName, "native"]
2462
+ });
2463
+ return { ...dto, signature: getNativeSignature(signature) };
2464
+ } catch (error) {
2465
+ if (!isUnsupportedNativeSchemeError(error)) {
2466
+ throw error;
2467
+ }
2468
+ this.effectiveScheme = "personal-sign";
2469
+ return personalSign(wallet, dto, this.walletAddress, methodName);
2470
+ }
2471
+ }
2472
+ };
2473
+ var BrowserWalletSigner = class {
1688
2474
  /**
1689
- * Event management operations for real-time socket connections.
1690
- * Use this to manage event streaming and transaction status updates.
2475
+ * Creates a browser-wallet signer.
2476
+ *
2477
+ * Defaults to `personal_sign` — unchanged from every existing integration, since it only ever
2478
+ * resolves the bare `eth|` identity and changing that silently would be a real behavior
2479
+ * break for anyone already depending on it. Pass `{ scheme: 'eip712' }` to correctly resolve
2480
+ * a registered `client|` alias instead, for `RequestTrade`/`AddLiquidity`/`RemoveLiquidity`/
2481
+ * `CollectPositionFees` (`EIP712_CAPABLE_METHODS`) — `CreatePool` and any other method still
2482
+ * falls back to `personal_sign` even in `'eip712'` mode, since its EIP-712 signing is a
2483
+ * separate, unsolved chain-side problem.
1691
2484
  *
1692
2485
  * @example
1693
2486
  * ```typescript
1694
- * // Connect to event socket for transaction updates
1695
- * await GSwap.events.connectEventSocket();
1696
- *
1697
- * // Check if socket is connected
1698
- * const isConnected = GSwap.events.eventSocketConnected();
2487
+ * const signer = new BrowserWalletSigner(window.ethereum, '0x0123456789012345678901234567890123456789');
2488
+ * const signed = await signer.signObject('RequestTrade', { token0: 'GALA', token1: 'GUSDC' });
2489
+ * ```
2490
+ * @example
2491
+ * ```typescript
2492
+ * // Correctly resolves a registered client| alias instead of the bare eth| address.
2493
+ * const signer = new BrowserWalletSigner(window.ethereum, walletAddress, { scheme: 'eip712' });
1699
2494
  * ```
1700
2495
  */
1701
- static events = Events.instance;
1702
- httpClient;
1703
- httpRequestor;
1704
- bundler;
1705
- pools;
2496
+ constructor(provider, walletAddress, options = {}) {
2497
+ this.provider = provider;
2498
+ this.walletAddress = normalizeEthereumAccount(walletAddress, false);
2499
+ this.scheme = options.scheme ?? "personal-sign";
2500
+ }
2501
+ provider;
2502
+ walletAddress;
2503
+ scheme;
1706
2504
  /**
1707
- * Creates a new SDK instance.
1708
- * @param options - Customization options.
1709
- * @param options.signer - The signer to use for authenticated operations. Required if you use any functionality that creates transactions (such as swapping). Not required for readonly operations.
1710
- * @param options.gatewayBaseUrl - Base URL for the GalaChain Gateway API.
1711
- * @param options.dexContractBasePath - Base path for DEX contract API endpoints, within the GalaChain Gateway API.
1712
- * @param options.tokenContractBasePath - Base path for token contract API endpoints, within the GalaChain Gateway API.
1713
- * @param options.bundlerBaseUrl - Base URL for the DEX transaction bundling API.
1714
- * @param options.bundlingAPIBasePath - Base path for transaction bundling API endpoints.
1715
- * @param options.dexBackendBaseUrl - Base URL for the DEX backend API (for user assets and other data).
1716
- * @param options.httpRequestor - Custom HTTP requestor to use for API calls. Defaults to the global `fetch` function.
1717
- * @param options.transactionWaitTimeoutMs - Timeout in milliseconds for waiting for transactions to complete. Defaults to 300,000 milliseconds (five minutes).
1718
- * @param options.walletAddress - Optional default wallet address for operations that require a wallet address (e.x. swapping). If not provided, you must specify the wallet address in each method call.
2505
+ * Signs the DTO with `eth_signTypedData_v4` (when constructed with `{ scheme: 'eip712' }` and
2506
+ * `methodName` is in `EIP712_CAPABLE_METHODS`) or the EIP-1193 `personal_sign` method
2507
+ * (otherwise — the default).
2508
+ *
2509
+ * @example
2510
+ * ```typescript
2511
+ * const signed = await signer.signObject('RequestTrade', {
2512
+ * token0: 'GALA',
2513
+ * token1: 'GUSDC',
2514
+ * fee: 3000,
2515
+ * });
2516
+ * ```
1719
2517
  */
1720
- constructor(options) {
1721
- var _a;
1722
- this.gatewayBaseUrl = ((_a = options == null ? void 0 : options.gatewayBaseUrl) == null ? void 0 : _a.replace(/\/$/, "")) ?? "https://gateway-mainnet.galachain.com";
1723
- this.dexContractBasePath = (options == null ? void 0 : options.dexContractBasePath) ?? "/api/asset/dexv3-contract";
1724
- this.tokenContractBasePath = (options == null ? void 0 : options.tokenContractBasePath) ?? "/api/asset/token-contract";
1725
- this.bundlerBaseUrl = (options == null ? void 0 : options.bundlerBaseUrl) ?? "https://bundle-backend-prod1.defi.gala.com";
1726
- this.bundlingAPIBasePath = (options == null ? void 0 : options.bundlingAPIBasePath) ?? "/bundle";
1727
- this.dexBackendBaseUrl = (options == null ? void 0 : options.dexBackendBaseUrl) ?? "https://dex-backend-prod1.defi.gala.com";
1728
- this.signer = options == null ? void 0 : options.signer;
1729
- this.transactionWaitTimeoutMs = (options == null ? void 0 : options.transactionWaitTimeoutMs) ?? 3e5;
1730
- this.httpRequestor = (options == null ? void 0 : options.httpRequestor) ?? fetch.bind(globalThis);
1731
- this.httpClient = new HttpClient(this.httpRequestor);
1732
- this.bundler = new Bundler(
1733
- this.bundlerBaseUrl,
1734
- this.bundlingAPIBasePath,
1735
- this.transactionWaitTimeoutMs,
1736
- this.signer,
1737
- this.httpClient
1738
- );
1739
- this.pools = new Pools(this.gatewayBaseUrl, this.dexContractBasePath, this.httpClient);
1740
- this.quoting = new Quoting(this.gatewayBaseUrl, this.dexContractBasePath, this.httpClient);
1741
- this.positions = new Positions(
1742
- this.gatewayBaseUrl,
1743
- this.dexContractBasePath,
1744
- this.bundler,
1745
- this.pools,
1746
- this.httpClient,
1747
- { walletAddress: options == null ? void 0 : options.walletAddress }
1748
- );
1749
- this.swaps = new Swaps(this.bundler, { walletAddress: options == null ? void 0 : options.walletAddress });
1750
- this.assets = new Assets(this.dexBackendBaseUrl, this.httpClient);
1751
- }
1752
- };
1753
-
1754
- // src/classes/signers.ts
1755
- var import_api = require("@gala-chain/api");
1756
- var import_connect = require("@gala-chain/connect");
1757
- var PrivateKeySigner = class {
1758
- keyBuffer;
1759
- constructor(privateKey) {
1760
- this.keyBuffer = import_api.signatures.normalizePrivateKey(privateKey);
1761
- }
1762
- async signObject(_methodName, obj) {
1763
- const signature = import_api.signatures.getSignature(obj, this.keyBuffer);
1764
- return { ...obj, signature };
1765
- }
1766
- };
1767
- var GalaWalletSigner = class _GalaWalletSigner {
1768
- constructor(walletAddress) {
1769
- this.walletAddress = walletAddress;
1770
- }
1771
- async signObject(methodName, obj) {
1772
- const globalGala = global.gala;
1773
- if (!globalGala) {
1774
- throw new GSwapSDKError(
1775
- "Gala wallet is not available. Please ensure the Gala wallet is connected.",
1776
- "GALA_WALLET_NOT_AVAILABLE"
1777
- );
2518
+ signObject(methodName, dto) {
2519
+ if (this.scheme === "eip712" && EIP712_CAPABLE_METHODS.has(methodName)) {
2520
+ return signEIP712(this.provider, methodName, dto, this.walletAddress);
1778
2521
  }
1779
- const domain = { name: "ethereum", chainId: 1 };
1780
- const types = _GalaWalletSigner.generateEIP712Types(methodName, obj);
1781
- const signRequest = {
1782
- domain,
1783
- types,
1784
- Primary_type: methodName,
1785
- message: {
1786
- ...obj,
1787
- prefix: (0, import_connect.calculatePersonalSignPrefix)(obj)
1788
- }
1789
- };
1790
- await globalGala.setAddress(this.walletAddress);
1791
- const signature = await globalGala.request({
1792
- method: "eth_signTypedData",
1793
- params: [JSON.stringify(signRequest), this.walletAddress]
1794
- });
1795
- return { ...obj, domain, types, signature };
1796
- }
1797
- static generateEIP712Types(typeName, params) {
1798
- const types = {};
1799
- types[typeName] = [];
1800
- function addField(name, fieldValue, parentTypeName, onlyGetType = false) {
1801
- if (fieldValue === void 0) {
1802
- } else if (Array.isArray(fieldValue)) {
1803
- const type = addField(name, fieldValue[0], parentTypeName, true);
1804
- if (!onlyGetType) types[parentTypeName].push({ name, type: (type ?? name) + "[]" });
1805
- } else if (typeof fieldValue === "object" && fieldValue !== null) {
1806
- if (types[name]) {
1807
- throw new GSwapSDKError(
1808
- "Name collisions not yet supported in EIP712 type generation",
1809
- "EIP712_NAME_COLLISION",
1810
- { name }
1811
- );
1812
- }
1813
- types[name] = [];
1814
- Object.entries(fieldValue).forEach(([key, value]) => {
1815
- addField(key, value, name);
1816
- });
1817
- if (!onlyGetType) types[parentTypeName].push({ name, type: name });
1818
- } else {
1819
- let eipType;
1820
- switch (typeof fieldValue) {
1821
- case "string":
1822
- eipType = "string";
1823
- break;
1824
- case "number":
1825
- eipType = "int256";
1826
- break;
1827
- case "boolean":
1828
- eipType = "bool";
1829
- break;
1830
- default:
1831
- throw new GSwapSDKError(
1832
- `Unsupported type for EIP712 signing: ${typeof fieldValue}`,
1833
- "EIP712_UNSUPPORTED_TYPE",
1834
- { type: typeof fieldValue, value: fieldValue }
1835
- );
1836
- }
1837
- if (onlyGetType) {
1838
- return eipType;
1839
- } else {
1840
- types[parentTypeName].push({ name, type: eipType });
1841
- }
1842
- }
1843
- }
1844
- Object.entries(params).forEach(([key, value]) => {
1845
- addField(key, value, typeName);
1846
- });
1847
- return types;
2522
+ return personalSign(this.provider, dto, this.walletAddress);
1848
2523
  }
1849
2524
  };
1850
2525
  // Annotate the CommonJS export names for ESM import in node:
1851
2526
  0 && (module.exports = {
2527
+ ALL_FEE_TIERS,
1852
2528
  Assets,
1853
- Bundler,
1854
- EventSocketClient,
1855
- Events,
2529
+ BrowserWalletSigner,
2530
+ ChainGateway,
1856
2531
  FEE_TIER,
2532
+ GC_MAX_TICK,
2533
+ GC_MIN_TICK,
2534
+ GSWAP_ENVIRONMENTS,
1857
2535
  GSwap,
1858
2536
  GSwapSDKError,
1859
2537
  GalaWalletSigner,
1860
- PendingTransaction,
1861
2538
  Pools,
1862
2539
  Positions,
1863
2540
  PrivateKeySigner,
1864
2541
  Quoting,
2542
+ SubmittedTransaction,
1865
2543
  Swaps,
1866
- TradeEventEmitter,
1867
- compareTokens,
1868
- getTokenOrdering,
2544
+ Symbols,
2545
+ TICK_SPACING_BY_FEE,
2546
+ alignTickDown,
2547
+ alignTickUp,
2548
+ assertTickRange,
2549
+ compositeKeyOf,
2550
+ getObjectProperty,
2551
+ getStringProperty,
2552
+ orderSymbols,
2553
+ parseJson,
1869
2554
  parseTokenClassKey,
1870
- stringifyTokenClassKey,
2555
+ readOrderedSymbols,
2556
+ sqrtPriceToTick,
2557
+ tickFromPrice,
2558
+ tickToSqrtPrice,
1871
2559
  validateFee,
1872
2560
  validateNumericAmount,
1873
2561
  validatePriceValues,