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