@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/README.md +63 -35
- package/dist/browser/index.js +2 -24
- package/dist/browser/index.js.map +1 -1
- package/dist/index.cjs +2033 -1345
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1008 -774
- package/dist/index.d.ts +1008 -774
- package/dist/index.mjs +2012 -1337
- package/dist/index.mjs.map +1 -1
- package/package.json +37 -26
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
|
-
|
|
35
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
96
|
-
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
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
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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
|
-
|
|
136
|
-
|
|
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
|
-
|
|
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
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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 <
|
|
403
|
+
if (tickLower < GC_MIN_TICK || tickUpper > GC_MAX_TICK) {
|
|
243
404
|
throw new GSwapSDKError(
|
|
244
|
-
|
|
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:
|
|
251
|
-
maxTick:
|
|
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 (!
|
|
258
|
-
throw new GSwapSDKError(
|
|
259
|
-
|
|
260
|
-
|
|
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
|
|
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
|
|
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(
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
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
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
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
|
-
|
|
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
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
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
|
-
|
|
406
|
-
|
|
407
|
-
|
|
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/
|
|
412
|
-
var
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
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
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
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
|
-
|
|
672
|
+
} else {
|
|
673
|
+
throw new GSwapSDKError(bodyMessage(body), "CONFIRMATION_FAILED", {
|
|
674
|
+
status: response.status,
|
|
675
|
+
body,
|
|
676
|
+
url
|
|
677
|
+
});
|
|
455
678
|
}
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
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
|
-
|
|
693
|
+
uniqueKey: this.uniqueKey,
|
|
694
|
+
applyTransactionId,
|
|
695
|
+
blockNumber,
|
|
696
|
+
status: response.status,
|
|
697
|
+
body,
|
|
698
|
+
url
|
|
473
699
|
}
|
|
474
700
|
);
|
|
475
701
|
}
|
|
476
|
-
|
|
477
|
-
|
|
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
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
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
|
-
|
|
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
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
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
|
-
|
|
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/
|
|
508
|
-
var
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
/**
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
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 =
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
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
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
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
|
-
|
|
644
|
-
|
|
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
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
""
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
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
|
-
|
|
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
|
-
|
|
679
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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/
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
function
|
|
721
|
-
if (
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
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
|
|
736
|
-
if (
|
|
737
|
-
throw new GSwapSDKError(
|
|
738
|
-
|
|
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
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
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
|
-
*
|
|
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
|
|
1119
|
+
async getPool(token0, token1, fee) {
|
|
786
1120
|
validateFee(fee);
|
|
787
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
819
|
-
|
|
820
|
-
|
|
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
|
-
*
|
|
844
|
-
*
|
|
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
|
-
|
|
847
|
-
|
|
848
|
-
const
|
|
849
|
-
const
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
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(
|
|
862
|
-
this.
|
|
863
|
-
this.
|
|
864
|
-
this.
|
|
865
|
-
this.
|
|
866
|
-
this.
|
|
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
|
-
|
|
870
|
-
|
|
871
|
-
|
|
1232
|
+
gateway;
|
|
1233
|
+
symbols;
|
|
1234
|
+
signer;
|
|
1235
|
+
walletAddress;
|
|
1236
|
+
http;
|
|
872
1237
|
/**
|
|
873
|
-
*
|
|
874
|
-
*
|
|
875
|
-
* @param
|
|
876
|
-
* @
|
|
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('
|
|
881
|
-
* console.log(positions);
|
|
1244
|
+
* const positions = await gSwap.positions.getUserPositions('client|012345678901234567890123');
|
|
882
1245
|
* ```
|
|
883
1246
|
*/
|
|
884
|
-
async getUserPositions(
|
|
885
|
-
const
|
|
886
|
-
|
|
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
|
|
897
|
-
*
|
|
898
|
-
*
|
|
899
|
-
* @param
|
|
900
|
-
* @
|
|
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(
|
|
908
|
-
*
|
|
909
|
-
*
|
|
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(
|
|
918
|
-
const
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
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
|
|
930
|
-
*
|
|
931
|
-
* @param args
|
|
932
|
-
* @
|
|
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
|
|
943
|
-
*
|
|
944
|
-
*
|
|
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
|
|
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
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
tickLower: args.tickLower
|
|
965
|
-
tickUpper: args.tickUpper
|
|
966
|
-
amount:
|
|
967
|
-
|
|
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
|
-
*
|
|
980
|
-
*
|
|
981
|
-
* @param
|
|
982
|
-
* @returns
|
|
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
|
|
986
|
-
*
|
|
987
|
-
*
|
|
988
|
-
* }
|
|
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
|
|
994
|
-
|
|
995
|
-
const
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
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
|
-
*
|
|
1003
|
-
*
|
|
1004
|
-
* @param args
|
|
1005
|
-
* @
|
|
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
|
|
1019
|
-
*
|
|
1020
|
-
*
|
|
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
|
-
*
|
|
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
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
validateNumericAmount(args.
|
|
1044
|
-
|
|
1045
|
-
const
|
|
1046
|
-
|
|
1047
|
-
|
|
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
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
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
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
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
|
-
*
|
|
1088
|
-
*
|
|
1089
|
-
*
|
|
1090
|
-
* @param args
|
|
1091
|
-
* @
|
|
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
|
|
1105
|
-
*
|
|
1106
|
-
*
|
|
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
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
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
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
);
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
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
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
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
|
|
1182
|
-
*
|
|
1183
|
-
*
|
|
1184
|
-
* @param args
|
|
1185
|
-
* @
|
|
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
|
-
*
|
|
1197
|
-
*
|
|
1198
|
-
*
|
|
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
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
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.
|
|
1223
|
-
|
|
1224
|
-
}
|
|
1225
|
-
const
|
|
1226
|
-
|
|
1227
|
-
|
|
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:
|
|
1239
|
-
tickUpper:
|
|
1240
|
-
|
|
1241
|
-
|
|
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
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
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
|
|
1266
|
-
*
|
|
1267
|
-
* @param args
|
|
1268
|
-
* @
|
|
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
|
-
*
|
|
1280
|
-
*
|
|
1281
|
-
*
|
|
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
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
const
|
|
1303
|
-
|
|
1304
|
-
|
|
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
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
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
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
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
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
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
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
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
|
|
1373
|
-
|
|
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
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
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
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
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
|
-
|
|
1744
|
+
dexBackendBaseUrl;
|
|
1745
|
+
http;
|
|
1746
|
+
symbols;
|
|
1404
1747
|
/**
|
|
1405
|
-
*
|
|
1406
|
-
*
|
|
1407
|
-
* @
|
|
1408
|
-
*
|
|
1409
|
-
*
|
|
1410
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
1440
|
-
*
|
|
1441
|
-
* @
|
|
1442
|
-
*
|
|
1443
|
-
*
|
|
1444
|
-
*
|
|
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
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
const
|
|
1453
|
-
const
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
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
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
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
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
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
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
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
|
-
|
|
1505
|
-
|
|
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
|
-
|
|
1508
|
-
|
|
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
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
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
|
|
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
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
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
|
-
*
|
|
1538
|
-
*
|
|
1539
|
-
*
|
|
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
|
-
* ```
|
|
1550
|
-
*
|
|
1551
|
-
*
|
|
1552
|
-
* '
|
|
1553
|
-
*
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
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
|
-
|
|
2139
|
+
chainCallTimeoutMs;
|
|
1620
2140
|
signer;
|
|
2141
|
+
walletAddress;
|
|
2142
|
+
gateway;
|
|
2143
|
+
symbols;
|
|
2144
|
+
quoting;
|
|
2145
|
+
pools;
|
|
2146
|
+
positions;
|
|
2147
|
+
swaps;
|
|
2148
|
+
assets;
|
|
1621
2149
|
/**
|
|
1622
|
-
*
|
|
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
|
-
*
|
|
1628
|
-
* const
|
|
2154
|
+
* const gSwap = new GSwap({ env: 'stage' });
|
|
2155
|
+
* const symbols = await gSwap.symbols.list();
|
|
1629
2156
|
* ```
|
|
1630
2157
|
*/
|
|
1631
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
1639
|
-
* const
|
|
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
|
-
*
|
|
1642
|
-
*
|
|
1643
|
-
*
|
|
1644
|
-
*
|
|
1645
|
-
*
|
|
1646
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
1666
|
-
* const
|
|
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
|
-
|
|
2436
|
+
constructor(walletAddress, options = {}) {
|
|
2437
|
+
this.walletAddress = normalizeEthereumAccount(walletAddress, true);
|
|
2438
|
+
this.effectiveScheme = options.scheme ?? "native";
|
|
2439
|
+
}
|
|
1676
2440
|
/**
|
|
1677
|
-
*
|
|
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
|
-
*
|
|
1683
|
-
*
|
|
1684
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
1690
|
-
*
|
|
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
|
-
*
|
|
1695
|
-
* await
|
|
1696
|
-
*
|
|
1697
|
-
*
|
|
1698
|
-
*
|
|
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
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
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
|
-
*
|
|
1708
|
-
*
|
|
1709
|
-
*
|
|
1710
|
-
*
|
|
1711
|
-
* @
|
|
1712
|
-
*
|
|
1713
|
-
*
|
|
1714
|
-
*
|
|
1715
|
-
*
|
|
1716
|
-
*
|
|
1717
|
-
*
|
|
1718
|
-
*
|
|
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
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1854
|
-
|
|
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
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
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
|
-
|
|
2555
|
+
readOrderedSymbols,
|
|
2556
|
+
sqrtPriceToTick,
|
|
2557
|
+
tickFromPrice,
|
|
2558
|
+
tickToSqrtPrice,
|
|
1871
2559
|
validateFee,
|
|
1872
2560
|
validateNumericAmount,
|
|
1873
2561
|
validatePriceValues,
|