@gala-chain/gswap-sdk 1.2.0 → 2.0.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -8
- package/dist/browser/classes/apply_results.js +1 -1
- package/dist/browser/classes/apply_results.js.map +1 -1
- package/dist/browser/classes/assets.js +1 -1
- package/dist/browser/classes/assets.js.map +1 -1
- package/dist/browser/classes/dex_fees.js +1 -1
- package/dist/browser/classes/dex_fees.js.map +1 -1
- package/dist/browser/classes/gateway.js +1 -1
- package/dist/browser/classes/gateway.js.map +1 -1
- package/dist/browser/classes/gdex_stream.js +1 -1
- package/dist/browser/classes/gdex_stream.js.map +1 -1
- package/dist/browser/classes/gswap.js +1 -1
- package/dist/browser/classes/gswap.js.map +1 -1
- package/dist/browser/classes/gswap_error_catalog.js +2 -0
- package/dist/browser/classes/gswap_error_catalog.js.map +1 -0
- package/dist/browser/classes/gswap_error_phase_table.js +2 -0
- package/dist/browser/classes/gswap_error_phase_table.js.map +1 -0
- package/dist/browser/classes/gswap_sdk_error.js +1 -1
- package/dist/browser/classes/gswap_sdk_error.js.map +1 -1
- package/dist/browser/classes/http_client.js +1 -1
- package/dist/browser/classes/http_client.js.map +1 -1
- package/dist/browser/classes/leaderboard.js +2 -0
- package/dist/browser/classes/leaderboard.js.map +1 -0
- package/dist/browser/classes/market.js +2 -0
- package/dist/browser/classes/market.js.map +1 -0
- package/dist/browser/classes/pending_swap_fate.js.map +1 -1
- package/dist/browser/classes/pending_swaps.js +1 -1
- package/dist/browser/classes/pending_swaps.js.map +1 -1
- package/dist/browser/classes/pools.js +1 -1
- package/dist/browser/classes/pools.js.map +1 -1
- package/dist/browser/classes/positions.js +1 -1
- package/dist/browser/classes/positions.js.map +1 -1
- package/dist/browser/classes/quoting.js +1 -1
- package/dist/browser/classes/quoting.js.map +1 -1
- package/dist/browser/classes/shadow_submit.js.map +1 -1
- package/dist/browser/classes/storage_adapters.js.map +1 -1
- package/dist/browser/classes/submitted_transaction.js +1 -1
- package/dist/browser/classes/submitted_transaction.js.map +1 -1
- package/dist/browser/classes/swaps.js +1 -1
- package/dist/browser/classes/swaps.js.map +1 -1
- package/dist/browser/classes/symbols.js +1 -1
- package/dist/browser/classes/symbols.js.map +1 -1
- package/dist/browser/index.js +1 -1
- package/dist/browser/types/env.js.map +1 -1
- package/dist/browser/types/fees.js.map +1 -1
- package/dist/browser/types/market.js +2 -0
- package/dist/browser/types/market.js.map +1 -0
- package/dist/browser/utils/classification.js.map +1 -1
- package/dist/browser/utils/confirmation_schedule.js.map +1 -1
- package/dist/browser/utils/confirmation_state.js +1 -1
- package/dist/browser/utils/confirmation_state.js.map +1 -1
- package/dist/browser/utils/debug.js.map +1 -1
- package/dist/browser/utils/dto_builders.js +1 -1
- package/dist/browser/utils/dto_builders.js.map +1 -1
- package/dist/browser/utils/failure_classification.js +1 -1
- package/dist/browser/utils/failure_classification.js.map +1 -1
- package/dist/browser/utils/failure_guards.js +1 -1
- package/dist/browser/utils/failure_guards.js.map +1 -1
- package/dist/browser/utils/lenient.js +1 -1
- package/dist/browser/utils/lenient.js.map +1 -1
- package/dist/browser/utils/ordering.js +1 -1
- package/dist/browser/utils/ordering.js.map +1 -1
- package/dist/browser/utils/reconnection.js +1 -1
- package/dist/browser/utils/reconnection.js.map +1 -1
- package/dist/browser/utils/resolve_pre_send.js +2 -0
- package/dist/browser/utils/resolve_pre_send.js.map +1 -0
- package/dist/browser/utils/server_message.js.map +1 -1
- package/dist/browser/utils/shadow_errors.js +1 -1
- package/dist/browser/utils/shadow_errors.js.map +1 -1
- package/dist/browser/utils/submit_state.js.map +1 -1
- package/dist/browser/utils/tick_align.js.map +1 -1
- package/dist/browser/utils/tick_math.js.map +1 -1
- package/dist/browser/utils/tick_range.js.map +1 -1
- package/dist/browser/utils/ticks.js +1 -1
- package/dist/browser/utils/ticks.js.map +1 -1
- package/dist/browser/utils/validation.js +1 -1
- package/dist/browser/utils/validation.js.map +1 -1
- package/dist/index.cjs +1033 -2925
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +439 -1604
- package/dist/index.d.ts +439 -1604
- package/dist/index.mjs +964 -2810
- package/dist/index.mjs.map +1 -1
- package/package.json +36 -60
- package/dist/browser/classes/error_catalog.js +0 -2
- package/dist/browser/classes/error_catalog.js.map +0 -1
- package/dist/browser/classes/error_phase_table.js +0 -2
- package/dist/browser/classes/error_phase_table.js.map +0 -1
- package/dist/browser/classes/error_phases.js +0 -2
- package/dist/browser/classes/error_phases.js.map +0 -1
- package/dist/browser/classes/signers.js +0 -2
- package/dist/browser/classes/signers.js.map +0 -1
- package/dist/browser/utils/axios_requestor.js +0 -2
- package/dist/browser/utils/axios_requestor.js.map +0 -1
- package/dist/browser/utils/chain_dto_signing.js +0 -3
- package/dist/browser/utils/chain_dto_signing.js.map +0 -1
- package/dist/browser/utils/class_key.js +0 -2
- package/dist/browser/utils/class_key.js.map +0 -1
- package/dist/browser/utils/eip712.js +0 -4
- package/dist/browser/utils/eip712.js.map +0 -1
- package/dist/browser/utils/error_brand.js +0 -2
- package/dist/browser/utils/error_brand.js.map +0 -1
- package/dist/browser/utils/ethers_provider.js +0 -2
- package/dist/browser/utils/ethers_provider.js.map +0 -1
- package/dist/browser/utils/failure_outcomes.js +0 -2
- package/dist/browser/utils/failure_outcomes.js.map +0 -1
- package/dist/browser/utils/failure_view.js +0 -2
- package/dist/browser/utils/failure_view.js.map +0 -1
- package/dist/browser/utils/fetch_failure.js +0 -2
- package/dist/browser/utils/fetch_failure.js.map +0 -1
- package/dist/browser/utils/pre_send.js +0 -2
- package/dist/browser/utils/pre_send.js.map +0 -1
- package/dist/browser/utils/signing.js +0 -3
- package/dist/browser/utils/signing.js.map +0 -1
- package/dist/browser/utils/transport.js +0 -2
- package/dist/browser/utils/transport.js.map +0 -1
- package/dist/browser/utils/typed_data.js +0 -2
- package/dist/browser/utils/typed_data.js.map +0 -1
- package/dist/browser/utils/unique_key.js +0 -2
- package/dist/browser/utils/unique_key.js.map +0 -1
- package/dist/browser/utils/unique_key_syntax.js +0 -2
- package/dist/browser/utils/unique_key_syntax.js.map +0 -1
- package/dist/browser/utils/wallet_rejection.js +0 -2
- package/dist/browser/utils/wallet_rejection.js.map +0 -1
- package/dist/browser/version.js +0 -2
- package/dist/browser/version.js.map +0 -1
- package/dist/browser/wallet.js +0 -2
- package/dist/browser/wallet.js.map +0 -1
- package/dist/wallet.cjs +0 -159
- package/dist/wallet.cjs.map +0 -1
- package/dist/wallet.d.cts +0 -74
- package/dist/wallet.d.ts +0 -74
- package/dist/wallet.mjs +0 -156
- package/dist/wallet.mjs.map +0 -1
- /package/dist/browser/_virtual/{_@oxc-project_runtime@0.151.0 → _@oxc-project_runtime@0.153.0}/helpers/esm/defineProperty.js +0 -0
- /package/dist/browser/_virtual/{_@oxc-project_runtime@0.151.0 → _@oxc-project_runtime@0.153.0}/helpers/esm/toPrimitive.js +0 -0
- /package/dist/browser/_virtual/{_@oxc-project_runtime@0.151.0 → _@oxc-project_runtime@0.153.0}/helpers/esm/toPropertyKey.js +0 -0
- /package/dist/browser/_virtual/{_@oxc-project_runtime@0.151.0 → _@oxc-project_runtime@0.153.0}/helpers/esm/typeof.js +0 -0
package/dist/index.cjs
CHANGED
|
@@ -26,14 +26,12 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
26
26
|
}) : target, mod));
|
|
27
27
|
|
|
28
28
|
//#endregion
|
|
29
|
+
let _gala_chain_gala_core_sdk = require("@gala-chain/gala-core-sdk");
|
|
29
30
|
let bignumber_js = require("bignumber.js");
|
|
30
31
|
bignumber_js = __toESM(bignumber_js, 1);
|
|
31
32
|
let decimal_js = require("decimal.js");
|
|
32
33
|
let debug = require("debug");
|
|
33
34
|
debug = __toESM(debug, 1);
|
|
34
|
-
let _noble_curves_secp256k1 = require("@noble/curves/secp256k1");
|
|
35
|
-
let _noble_hashes_sha3 = require("@noble/hashes/sha3");
|
|
36
|
-
let _noble_hashes_utils = require("@noble/hashes/utils");
|
|
37
35
|
|
|
38
36
|
//#region src/utils/confirmation_schedule.ts
|
|
39
37
|
const DEFAULT_TIMEOUT_MS = 6e4;
|
|
@@ -125,352 +123,30 @@ function readServerMessage(error) {
|
|
|
125
123
|
}
|
|
126
124
|
|
|
127
125
|
//#endregion
|
|
128
|
-
//#region src/
|
|
129
|
-
/**
|
|
130
|
-
* The
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* True when `uniqueKey` is a usable write key: 1 to 128 characters of `A-Za-z0-9._:-`, the shape
|
|
138
|
-
* the apply-results route accepts. The one definition behind `assertUniqueKey`, `confirm()` and
|
|
139
|
-
* the pending-swap store, so they can never disagree about what a valid key is.
|
|
140
|
-
*/
|
|
141
|
-
function isValidUniqueKey(uniqueKey) {
|
|
142
|
-
return typeof uniqueKey === "string" && UNIQUE_KEY_PATTERN.test(uniqueKey);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
//#endregion
|
|
146
|
-
//#region src/utils/error_brand.ts
|
|
147
|
-
/**
|
|
148
|
-
* The brand every `GSwapSDKError` carries, and the duck-type check for the "failure shape". This
|
|
149
|
-
* module has NO imports on purpose: it is part of the light `@gala-chain/gswap-sdk/wallet` entry,
|
|
150
|
-
* which must never pull the error catalog or the main bundle in.
|
|
151
|
-
*/
|
|
152
|
-
/**
|
|
153
|
-
* The brand of an SDK error: `error[GSWAP_ERROR_BRAND] === true`. It is a registered symbol
|
|
154
|
-
* (`Symbol.for`), so two copies of the SDK in one page (a wallet bundle and an app bundle) still
|
|
155
|
-
* recognise each other's errors, which `instanceof` cannot do.
|
|
156
|
-
*/
|
|
157
|
-
const GSWAP_ERROR_BRAND = Symbol.for("@gala-chain/gswap-sdk/error");
|
|
158
|
-
function isRecord$4(value) {
|
|
159
|
-
return typeof value === "object" && value !== null;
|
|
160
|
-
}
|
|
161
|
-
/** True when `value` carries the SDK error brand (a real `GSwapSDKError`, from any copy of the SDK). */
|
|
162
|
-
function hasGSwapErrorBrand(value) {
|
|
163
|
-
return isRecord$4(value) && value[GSWAP_ERROR_BRAND] === true;
|
|
164
|
-
}
|
|
165
|
-
/**
|
|
166
|
-
* True when `value` is an SDK error (it carries the brand) or a plain-object VIEW of one: a string
|
|
167
|
-
* `code` and an object `params`, which is the shape of `GSwapSDKError` and of the wrapper objects
|
|
168
|
-
* an app builds from one (`{ code, params, status?, phase?, ... }`). A wallet's own error (an
|
|
169
|
-
* EIP-1193 numeric `code`, an ethers error with a string `code` and no `params`) is neither. It
|
|
170
|
-
* says the value has the SDK's shape, not that `code` is a code the SDK knows.
|
|
171
|
-
*
|
|
172
|
-
* @example
|
|
173
|
-
* ```ts
|
|
174
|
-
* if (isGSwapErrorLike(error)) report(error.code, error.params); // no `instanceof`, no SDK import
|
|
175
|
-
* ```
|
|
176
|
-
*/
|
|
177
|
-
function isGSwapErrorLike(value) {
|
|
178
|
-
if (hasGSwapErrorBrand(value)) return true;
|
|
179
|
-
return isRecord$4(value) && typeof value["code"] === "string" && isRecord$4(value["params"]) && !Array.isArray(value["params"]);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
//#endregion
|
|
183
|
-
//#region src/classes/error_phases.ts
|
|
184
|
-
/**
|
|
185
|
-
* How far an operation had got when it failed, measured against the request it exists to send:
|
|
186
|
-
* for a write, the POST that carries the signed DTO to the gateway; for a read, the GET.
|
|
187
|
-
*
|
|
188
|
-
* - `pre_send`: NOTHING was transmitted. Everything up to the request (validation, symbol
|
|
189
|
-
* resolution, the signer, the wallet, a key provider) ran and failed, so no write can have been
|
|
190
|
-
* queued. A write that failed `pre_send` is never "maybe applied".
|
|
191
|
-
* - `in_flight`: the request left (or may have left) and no answer was read: a timeout, a broken
|
|
192
|
-
* connection, a stream that dropped. For a write the outcome is unknown.
|
|
193
|
-
* - `post_send`: an answer was read, or the write was already accepted: a refusal, an
|
|
194
|
-
* acknowledgement that proves nothing, an unusable body, and every failure of a confirmation.
|
|
195
|
-
*
|
|
196
|
-
* Each catalog entry declares the phase its code is thrown in; a throw site where the same code
|
|
197
|
-
* can occur in another phase overrides it (`GSwapErrorContext.phase`). It is a typed property of
|
|
198
|
-
* every `GSwapSDKError` (`error.phase`) and `isPreSendError(error)` reads it.
|
|
199
|
-
*
|
|
200
|
-
* This module has no imports, on purpose: it is the closed set of phases, shared by the catalog,
|
|
201
|
-
* the light phase table (`error_phase_table.ts`) and the failure view.
|
|
202
|
-
*/
|
|
203
|
-
const GSWAP_ERROR_PHASES = [
|
|
204
|
-
"pre_send",
|
|
205
|
-
"in_flight",
|
|
206
|
-
"post_send"
|
|
207
|
-
];
|
|
208
|
-
|
|
209
|
-
//#endregion
|
|
210
|
-
//#region src/utils/failure_outcomes.ts
|
|
211
|
-
/**
|
|
212
|
-
* What a failure means for the operation that raised it, as one vocabulary for every app.
|
|
213
|
-
*
|
|
214
|
-
* - `rejected`: the CHAIN refused the request (a definite no). Nothing was applied, so a NEW
|
|
215
|
-
* request is safe.
|
|
216
|
-
* - `unknown`: the request MAY have been applied (a timeout, a broken connection, an
|
|
217
|
-
* acknowledgement that proves nothing, a failed confirmation). Never submit again under a fresh
|
|
218
|
-
* key: keep `error.uniqueKey` and confirm it.
|
|
219
|
-
* - `not_queued`: the gateway refused the write before queueing it. Nothing is pending.
|
|
220
|
-
* - `not_sent`: the request never left (`error.phase === 'pre_send'`) for a reason outside the
|
|
221
|
-
* call itself: the signer or the wallet failed (locked, unreachable, the person declined), the
|
|
222
|
-
* key provider or the token resolver failed, or no key was available yet. Nothing is pending and
|
|
223
|
-
* nothing was transmitted, so repeating the call is safe and may succeed once the cause (an
|
|
224
|
-
* unlocked wallet) is gone.
|
|
225
|
-
* - `invalid_input`: the call itself cannot work (a bad argument, a missing signer or wallet, an
|
|
226
|
-
* unknown token or pool, a corrupt stored key). Repeating the same call fails the same way.
|
|
227
|
-
* Such an error is also pre-send (`isPreSendError`), but the input is what has to change.
|
|
228
|
-
* - `transient`: a read or connection failed with no state change. Repeating the same call is safe
|
|
229
|
-
* and may succeed.
|
|
126
|
+
//#region src/classes/gswap_error_catalog.ts
|
|
127
|
+
/**
|
|
128
|
+
* The catalog of every error code gswap-sdk ITSELF throws (the DEX codes): the SINGLE source of
|
|
129
|
+
* truth for its `code`, the named parameters it carries and the English template its `message` is
|
|
130
|
+
* generated from. The codes of the signing, transport and wallet layer (`INVALID_PRIVATE_KEY`,
|
|
131
|
+
* `REQUEST_TIMEOUT`, `SIGNING_FAILED`, `TYPED_DATA_*`, ...) live in `@gala-chain/gala-core-sdk`'s
|
|
132
|
+
* `GALA_ERROR_CATALOG`; no code is defined in both. A `GSwapSDKError` can carry a code of either
|
|
133
|
+
* catalog (`GSwapErrorCode` is the union), and reads its template from both.
|
|
230
134
|
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
"invalid_input",
|
|
240
|
-
"transient"
|
|
241
|
-
];
|
|
242
|
-
|
|
243
|
-
//#endregion
|
|
244
|
-
//#region src/utils/failure_view.ts
|
|
245
|
-
function isRecord$3(value) {
|
|
246
|
-
return typeof value === "object" && value !== null;
|
|
247
|
-
}
|
|
248
|
-
/**
|
|
249
|
-
* Reads the failure view of any value: a `GSwapSDKError`, or a plain object with a string `code`
|
|
250
|
-
* (and, optionally, `params`, `status`, `phase`, `uniqueKey`, `shadow`, `notFound`). Anything else
|
|
251
|
-
* (a string, a wallet error with a numeric `code`, `null`) has no view and is `undefined`.
|
|
252
|
-
*
|
|
253
|
-
* Having a view does not mean the SDK knows the code: an ethers error is `{ code:
|
|
254
|
-
* 'ACTION_REJECTED' }` and has a view too. The SDK's guards compare the code with the catalog.
|
|
255
|
-
*
|
|
256
|
-
* @example
|
|
257
|
-
* ```ts
|
|
258
|
-
* const view = toFailureView(error); // an SDK error, or an app's `{ code, params, status }` wrapper
|
|
259
|
-
* if (view?.code === 'CHAIN_REJECTED') showChainRejection(view.params);
|
|
260
|
-
* ```
|
|
261
|
-
*/
|
|
262
|
-
function toFailureView(value) {
|
|
263
|
-
if (!isRecord$3(value)) return void 0;
|
|
264
|
-
const { code, params, status, phase, uniqueKey, write, outcome, retryable, shadow, notFound } = value;
|
|
265
|
-
if (typeof code !== "string" || code === "") return void 0;
|
|
266
|
-
return {
|
|
267
|
-
code,
|
|
268
|
-
params: isRecord$3(params) && !Array.isArray(params) ? params : void 0,
|
|
269
|
-
status: typeof status === "number" && Number.isFinite(status) ? status : void 0,
|
|
270
|
-
phase: GSWAP_ERROR_PHASES.find((known) => known === phase),
|
|
271
|
-
uniqueKey: typeof uniqueKey === "string" && uniqueKey !== "" ? uniqueKey : void 0,
|
|
272
|
-
write: typeof write === "boolean" ? write : void 0,
|
|
273
|
-
outcome: FAILURE_OUTCOMES.find((known) => known === outcome),
|
|
274
|
-
retryable: retryable === false ? false : void 0,
|
|
275
|
-
shadow: typeof shadow === "boolean" ? shadow : void 0,
|
|
276
|
-
notFound: typeof notFound === "boolean" ? notFound : void 0
|
|
277
|
-
};
|
|
278
|
-
}
|
|
279
|
-
/**
|
|
280
|
-
* True when a failure is a WRITE's: it names a `uniqueKey`, or it does not say `write: false`.
|
|
281
|
-
* Fail-closed (see {@link FailureView}): only an explicit read is a read.
|
|
282
|
-
*/
|
|
283
|
-
function isWriteView(view) {
|
|
284
|
-
return view.uniqueKey !== void 0 || view.write !== false;
|
|
285
|
-
}
|
|
286
|
-
/** A param of a view, when it is a string. */
|
|
287
|
-
function stringParamOf(view, name) {
|
|
288
|
-
var _view$params;
|
|
289
|
-
const value = (_view$params = view.params) === null || _view$params === void 0 ? void 0 : _view$params[name];
|
|
290
|
-
return typeof value === "string" ? value : void 0;
|
|
291
|
-
}
|
|
292
|
-
/** A param of a view, when it is a finite number. */
|
|
293
|
-
function numberParamOf(view, name) {
|
|
294
|
-
var _view$params2;
|
|
295
|
-
const value = (_view$params2 = view.params) === null || _view$params2 === void 0 ? void 0 : _view$params2[name];
|
|
296
|
-
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
//#endregion
|
|
300
|
-
//#region src/classes/error_phase_table.ts
|
|
301
|
-
/**
|
|
302
|
-
* The phase of every catalog code, WITHOUT the catalog. The failure guards (`classifyFailure`,
|
|
303
|
-
* `mayHaveBeenQueued`, ...) need only a code's phase, and the catalog's English templates are
|
|
304
|
-
* ~20 kB: reading the phase from here keeps an app shell that imports a guard from loading them.
|
|
305
|
-
* The catalog entry is still where a code is declared; this table is checked against it AT
|
|
306
|
-
* COMPILE TIME, in both directions (a missing code, an extra code or a different phase is a type
|
|
307
|
-
* error), so the two cannot drift.
|
|
308
|
-
*/
|
|
309
|
-
const GSWAP_ERROR_PHASE_BY_CODE = {
|
|
310
|
-
HTTP_ERROR: "post_send",
|
|
311
|
-
UPDATE_REQUIRED: "post_send",
|
|
312
|
-
REQUEST_TIMEOUT: "in_flight",
|
|
313
|
-
TRANSPORT_ERROR: "in_flight",
|
|
314
|
-
REQUEST_FAILED: "in_flight",
|
|
315
|
-
INVALID_TIMEOUT: "pre_send",
|
|
316
|
-
METHOD_NOT_SUPPORTED: "pre_send",
|
|
317
|
-
SUBMISSION_OUTCOME_UNKNOWN: "in_flight",
|
|
318
|
-
GATEWAY_REPORTED_ERROR: "post_send",
|
|
319
|
-
GATEWAY_DATA_MISSING: "post_send",
|
|
320
|
-
GATEWAY_MODE_INVALID: "post_send",
|
|
321
|
-
GATEWAY_SYNC_REQUEST_TRADE: "post_send",
|
|
322
|
-
MISSING_UNIQUE_KEY: "post_send",
|
|
323
|
-
UNIQUE_KEY_MISMATCH: "post_send",
|
|
324
|
-
SHADOW_NOT_QUEUED: "post_send",
|
|
325
|
-
SHADOW_SUBMIT_NEEDS_WALLET_ADDRESS: "pre_send",
|
|
326
|
-
METHOD_NOT_ALLOWED: "post_send",
|
|
327
|
-
SIGNATURE_INVALID: "post_send",
|
|
328
|
-
SIGNER_MISMATCH: "post_send",
|
|
329
|
-
DTO_INVALID: "post_send",
|
|
330
|
-
BOUNDS_VIOLATION: "post_send",
|
|
331
|
-
SYMBOL_CONFLICT: "post_send",
|
|
332
|
-
RATE_LIMITED: "post_send",
|
|
333
|
-
CHAIN_DISPATCH_FAILED: "post_send",
|
|
334
|
-
GATEWAY_REJECTED: "post_send",
|
|
335
|
-
CHAIN_REJECTED: "post_send",
|
|
336
|
-
INVALID_CHAIN_RESPONSE: "post_send",
|
|
337
|
-
INVALID_POSITION_RESPONSE: "post_send",
|
|
338
|
-
CONFIRMATION_TIMEOUT: "post_send",
|
|
339
|
-
CONFIRMATION_ABORTED: "post_send",
|
|
340
|
-
CONFIRMATION_FAILED: "post_send",
|
|
341
|
-
CONFIRMATION_MALFORMED: "post_send",
|
|
342
|
-
CONFIRMATION_KEY_MISMATCH: "post_send",
|
|
343
|
-
NO_SIGNER: "pre_send",
|
|
344
|
-
NO_WALLET_ADDRESS: "post_send",
|
|
345
|
-
NO_POOL_AVAILABLE: "post_send",
|
|
346
|
-
INSUFFICIENT_LIQUIDITY: "post_send",
|
|
347
|
-
UNKNOWN_TOKEN: "pre_send",
|
|
348
|
-
SYMBOL_NOT_FOUND: "pre_send",
|
|
349
|
-
STREAM_API_KEY_UNUSABLE: "pre_send",
|
|
350
|
-
STREAM_WEBSOCKET_UNAVAILABLE: "pre_send",
|
|
351
|
-
STREAM_NOT_CONNECTED: "pre_send",
|
|
352
|
-
STREAM_DISCONNECTED: "in_flight",
|
|
353
|
-
STREAM_REQUEST_TIMEOUT: "in_flight",
|
|
354
|
-
STREAM_CONNECT_FAILED: "in_flight",
|
|
355
|
-
STREAM_CLOSED_BEFORE_CONNECT: "in_flight",
|
|
356
|
-
STREAM_CONNECTION_CLOSED: "in_flight",
|
|
357
|
-
GALA_WALLET_NOT_AVAILABLE: "pre_send",
|
|
358
|
-
WALLET_UNAVAILABLE: "pre_send",
|
|
359
|
-
MALFORMED_WALLET_REQUEST: "pre_send",
|
|
360
|
-
UNSUPPORTED_WALLET_METHOD: "pre_send",
|
|
361
|
-
INVALID_SIGNATURE: "pre_send",
|
|
362
|
-
INVALID_CHAIN_ID: "pre_send",
|
|
363
|
-
SIGNER_ADDRESS_MISMATCH: "pre_send",
|
|
364
|
-
INVALID_PRIVATE_KEY: "pre_send",
|
|
365
|
-
SIGNING_FAILED: "pre_send",
|
|
366
|
-
KEY_PROVIDER_FAILED: "pre_send",
|
|
367
|
-
TOKEN_RESOLUTION_FAILED: "pre_send",
|
|
368
|
-
INVALID_SIGNING_HASH_LENGTH: "pre_send",
|
|
369
|
-
MISSING_WALLET_ADDRESS: "pre_send",
|
|
370
|
-
INVALID_WALLET_ADDRESS: "pre_send",
|
|
371
|
-
INVALID_DECIMAL: "pre_send",
|
|
372
|
-
PERSONAL_SIGN_NOT_JSON: "pre_send",
|
|
373
|
-
PERSONAL_SIGN_NOT_OBJECT: "pre_send",
|
|
374
|
-
PERSONAL_SIGN_BAD_PREFIX: "pre_send",
|
|
375
|
-
PERSONAL_SIGN_ENVELOPE_FIELDS: "pre_send",
|
|
376
|
-
PERSONAL_SIGN_NOT_CANONICAL: "pre_send",
|
|
377
|
-
EIP712_NAME_COLLISION: "pre_send",
|
|
378
|
-
EIP712_UNSUPPORTED_VALUE: "pre_send",
|
|
379
|
-
TYPED_DATA_NOT_STRING: "pre_send",
|
|
380
|
-
TYPED_DATA_UNPAIRED_SURROGATE: "pre_send",
|
|
381
|
-
TYPED_DATA_NOT_HEX_BYTES: "pre_send",
|
|
382
|
-
TYPED_DATA_NOT_SAFE_INTEGER: "pre_send",
|
|
383
|
-
TYPED_DATA_NOT_INTEGER: "pre_send",
|
|
384
|
-
TYPED_DATA_NOT_BOOLEAN: "pre_send",
|
|
385
|
-
TYPED_DATA_BAD_ADDRESS: "pre_send",
|
|
386
|
-
TYPED_DATA_BAD_CHECKSUM: "pre_send",
|
|
387
|
-
TYPED_DATA_BYTES_LENGTH: "pre_send",
|
|
388
|
-
TYPED_DATA_OUT_OF_BOUNDS: "pre_send",
|
|
389
|
-
TYPED_DATA_UNSUPPORTED_TYPE: "pre_send",
|
|
390
|
-
TYPED_DATA_FIELDS_NOT_LIST: "pre_send",
|
|
391
|
-
TYPED_DATA_FIELD_MALFORMED: "pre_send",
|
|
392
|
-
TYPED_DATA_INVALID_FIELD_NAME: "pre_send",
|
|
393
|
-
TYPED_DATA_DUPLICATE_FIELD: "pre_send",
|
|
394
|
-
TYPED_DATA_INVALID_TYPE_NAME: "pre_send",
|
|
395
|
-
TYPED_DATA_CIRCULAR_TYPE: "pre_send",
|
|
396
|
-
TYPED_DATA_UNKNOWN_FIELD_TYPE: "pre_send",
|
|
397
|
-
TYPED_DATA_NO_PRIMARY_TYPE: "pre_send",
|
|
398
|
-
TYPED_DATA_AMBIGUOUS_PRIMARY_TYPE: "pre_send",
|
|
399
|
-
TYPED_DATA_ARRAY_EXPECTED: "pre_send",
|
|
400
|
-
TYPED_DATA_ARRAY_LENGTH_MISMATCH: "pre_send",
|
|
401
|
-
TYPED_DATA_STRUCT_NOT_OBJECT: "pre_send",
|
|
402
|
-
TYPED_DATA_MISSING_FIELD: "pre_send",
|
|
403
|
-
TYPED_DATA_DOMAIN_NOT_OBJECT: "pre_send",
|
|
404
|
-
TYPED_DATA_INVALID_DOMAIN_KEY: "pre_send",
|
|
405
|
-
NON_CANONICAL_PAIR: "pre_send",
|
|
406
|
-
UNORDERABLE_SYMBOLS: "pre_send",
|
|
407
|
-
INVALID_TOKEN_CLASS_KEY: "pre_send",
|
|
408
|
-
INCOMPLETE_TOKEN_CLASS_KEY: "pre_send",
|
|
409
|
-
INVALID_TRADING_SYMBOL: "pre_send",
|
|
410
|
-
NO_VALID_TRADING_SYMBOL: "pre_send",
|
|
411
|
-
TOKEN_SYMBOL_NOT_REGISTERED: "pre_send",
|
|
412
|
-
SYMBOL_DISAGREES_WITH_REGISTRY: "pre_send",
|
|
413
|
-
CREATE_POOL_NEEDS_CLASS_KEY: "pre_send",
|
|
414
|
-
INVALID_UNIQUE_KEY: "pre_send",
|
|
415
|
-
INVALID_UNIQUE_KEY_PREFIX: "pre_send",
|
|
416
|
-
AMOUNT_IS_JS_NUMBER: "pre_send",
|
|
417
|
-
AMOUNT_NOT_FINITE: "pre_send",
|
|
418
|
-
AMOUNT_NOT_POSITIVE: "pre_send",
|
|
419
|
-
AMOUNT_NEGATIVE: "pre_send",
|
|
420
|
-
PRICE_VALUES_IS_JS_NUMBER: "pre_send",
|
|
421
|
-
INVALID_PRICE_VALUES: "pre_send",
|
|
422
|
-
INVALID_PRICE_RANGE: "pre_send",
|
|
423
|
-
INVALID_PRICE_BOUNDS: "pre_send",
|
|
424
|
-
PRICE_BOUNDS_ORDER: "pre_send",
|
|
425
|
-
INVALID_STARTING_PRICE: "pre_send",
|
|
426
|
-
INVALID_SQRT_PRICE: "pre_send",
|
|
427
|
-
INVALID_TOKEN_DECIMALS: "pre_send",
|
|
428
|
-
TICKS_NOT_INTEGERS: "pre_send",
|
|
429
|
-
INVALID_TICK_RANGE: "pre_send",
|
|
430
|
-
TICKS_OUT_OF_BOUNDS: "pre_send",
|
|
431
|
-
TICKS_NOT_ALIGNED: "pre_send",
|
|
432
|
-
INVALID_TICK_SPACING: "pre_send",
|
|
433
|
-
INVALID_FEE: "pre_send",
|
|
434
|
-
EXACT_AMOUNT_REQUIRED: "pre_send",
|
|
435
|
-
EXACTLY_ONE_OF_REQUIRED: "pre_send",
|
|
436
|
-
AT_MOST_ONE_OF_ALLOWED: "pre_send",
|
|
437
|
-
POOL_MISMATCH: "pre_send",
|
|
438
|
-
INVALID_DTO: "pre_send",
|
|
439
|
-
DTO_NOT_CANONICAL: "pre_send",
|
|
440
|
-
SEARCH_TOO_LONG: "pre_send",
|
|
441
|
-
INVALID_SORT_BY: "pre_send",
|
|
442
|
-
INVALID_SORT_ORDER: "pre_send",
|
|
443
|
-
INVALID_PAGE: "pre_send",
|
|
444
|
-
INVALID_LIMIT: "pre_send"
|
|
445
|
-
};
|
|
446
|
-
/** True for a code the SDK catalog defines. */
|
|
447
|
-
function isCatalogCode(code) {
|
|
448
|
-
return Object.hasOwn(GSWAP_ERROR_PHASE_BY_CODE, code);
|
|
449
|
-
}
|
|
450
|
-
/** The phase of a view: the one it states, else the catalog's phase for its code, else `undefined`. */
|
|
451
|
-
function phaseOfView(view) {
|
|
452
|
-
if (view.phase !== void 0) return view.phase;
|
|
453
|
-
return isCatalogCode(view.code) ? GSWAP_ERROR_PHASE_BY_CODE[view.code] : void 0;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
//#endregion
|
|
457
|
-
//#region src/classes/error_catalog.ts
|
|
458
|
-
/**
|
|
459
|
-
* Why a private key was refused (`INVALID_PRIVATE_KEY.params.reason`). A token for the app to map
|
|
460
|
-
* to its own text, never English and never any part of the key.
|
|
135
|
+
* - `code` is the only discriminator. It is stable: apps branch on it, never on message text.
|
|
136
|
+
* - `params` are the named placeholders of the error: a flat object of primitives (string,
|
|
137
|
+
* number, boolean) that an app feeds to its own localization (i18next, ICU, FormatJS).
|
|
138
|
+
* Raw server payloads are never params (they are `error.body` / `error.bodyText`), and no
|
|
139
|
+
* secret (private key, signature, raw DTO) ever is.
|
|
140
|
+
* - `template` is English with `{placeholder}` markers. `error.message` is generated from it
|
|
141
|
+
* and the params, so the two cannot drift. It is for logs and developers: it is not stable and
|
|
142
|
+
* must not be shown to users or matched on.
|
|
461
143
|
*
|
|
462
|
-
*
|
|
463
|
-
*
|
|
464
|
-
*
|
|
465
|
-
*
|
|
466
|
-
*
|
|
144
|
+
* Every placeholder in a template is a declared param and every declared param is a placeholder,
|
|
145
|
+
* except the few `unrendered` params that carry server text for the app and are kept out of
|
|
146
|
+
* `message` on purpose (pinned by tests/error_catalog.test.ts). Facts that are not placeholders (HTTP status, URL,
|
|
147
|
+
* `uniqueKey`, transaction ids, the raw body, the cause) are typed properties of the error, see
|
|
148
|
+
* `GalaErrorContext`.
|
|
467
149
|
*/
|
|
468
|
-
const INVALID_PRIVATE_KEY_REASONS = [
|
|
469
|
-
"wrong_length",
|
|
470
|
-
"bad_encoding",
|
|
471
|
-
"not_on_curve",
|
|
472
|
-
"no_key_provided"
|
|
473
|
-
];
|
|
474
150
|
/**
|
|
475
151
|
* Why the outcome of a write submission is unknown (`SUBMISSION_OUTCOME_UNKNOWN.params.reason`).
|
|
476
152
|
* Both say the same thing about the swap (it MAY have been queued: never resubmit under a fresh
|
|
@@ -482,20 +158,9 @@ const INVALID_PRIVATE_KEY_REASONS = [
|
|
|
482
158
|
* does NOT prove the swap was not sent.
|
|
483
159
|
*/
|
|
484
160
|
const SUBMISSION_OUTCOME_UNKNOWN_REASONS = ["timeout", "transport"];
|
|
485
|
-
/**
|
|
486
|
-
* Why a signer could not produce a signature (`SIGNING_FAILED.params.reason`). A token for the
|
|
487
|
-
* app to map to its own text.
|
|
488
|
-
*
|
|
489
|
-
* - `user_rejected`: the person declined in their wallet (an EIP-1193 `4001`, an ethers
|
|
490
|
-
* `ACTION_REJECTED`, or a wallet that words it "User rejected/denied ..."). Nothing is wrong:
|
|
491
|
-
* they may simply try again.
|
|
492
|
-
* - `failed`: anything else the signer threw (a locked wallet, an unreachable extension, a wallet
|
|
493
|
-
* that refused). The original error is `cause`.
|
|
494
|
-
*/
|
|
495
|
-
const SIGNING_FAILURE_REASONS = ["user_rejected", "failed"];
|
|
496
161
|
/** The fee tiers as they appear in `INVALID_FEE.allowed`. */
|
|
497
162
|
const ALLOWED_FEE_TIERS_TEXT = "0,500,3000,10000";
|
|
498
|
-
/** The whole catalog. Add a code here before throwing it anywhere. */
|
|
163
|
+
/** The whole DEX catalog. Add a code here before throwing it anywhere in gswap-sdk. */
|
|
499
164
|
const GSWAP_ERROR_CATALOG = {
|
|
500
165
|
HTTP_ERROR: {
|
|
501
166
|
phase: "post_send",
|
|
@@ -512,32 +177,11 @@ const GSWAP_ERROR_CATALOG = {
|
|
|
512
177
|
template: "The backend refused this app version and requires an update (HTTP {status}); nothing was queued.",
|
|
513
178
|
params: { status: "number" }
|
|
514
179
|
},
|
|
515
|
-
REQUEST_TIMEOUT: {
|
|
516
|
-
phase: "in_flight",
|
|
517
|
-
template: "Request to {url} timed out after {timeoutMs} ms.",
|
|
518
|
-
params: {
|
|
519
|
-
url: "string",
|
|
520
|
-
timeoutMs: "number"
|
|
521
|
-
}
|
|
522
|
-
},
|
|
523
|
-
TRANSPORT_ERROR: {
|
|
524
|
-
phase: "in_flight",
|
|
525
|
-
template: "The request to {url} failed before the server answered.",
|
|
526
|
-
params: { url: "string" }
|
|
527
|
-
},
|
|
528
180
|
REQUEST_FAILED: {
|
|
529
181
|
phase: "in_flight",
|
|
530
182
|
template: "The {operation} request failed.",
|
|
531
183
|
params: { operation: "string" }
|
|
532
184
|
},
|
|
533
|
-
INVALID_TIMEOUT: {
|
|
534
|
-
phase: "pre_send",
|
|
535
|
-
template: "{field} must be a positive number of milliseconds, or Infinity for no timeout (got {value}).",
|
|
536
|
-
params: {
|
|
537
|
-
field: "string",
|
|
538
|
-
value: "number"
|
|
539
|
-
}
|
|
540
|
-
},
|
|
541
185
|
METHOD_NOT_SUPPORTED: {
|
|
542
186
|
phase: "pre_send",
|
|
543
187
|
template: "The method {method} is not supported; swaps are queued through RequestTrade.",
|
|
@@ -767,370 +411,109 @@ const GSWAP_ERROR_CATALOG = {
|
|
|
767
411
|
template: "GDEX stream connection closed (code {closeCode}).",
|
|
768
412
|
params: { closeCode: "number" }
|
|
769
413
|
},
|
|
770
|
-
|
|
414
|
+
TOKEN_RESOLUTION_FAILED: {
|
|
771
415
|
phase: "pre_send",
|
|
772
|
-
template: "
|
|
773
|
-
params: {}
|
|
416
|
+
template: "Token {token} could not be looked up; nothing was sent.",
|
|
417
|
+
params: { token: "string" }
|
|
774
418
|
},
|
|
775
|
-
|
|
419
|
+
MISSING_WALLET_ADDRESS: {
|
|
776
420
|
phase: "pre_send",
|
|
777
|
-
template: "No wallet
|
|
421
|
+
template: "Invalid wallet address: No wallet address provided. Either provide a wallet address to the function you are calling, or set one when instantiating GSwapSDK.",
|
|
778
422
|
params: {}
|
|
779
423
|
},
|
|
780
|
-
|
|
781
|
-
phase: "pre_send",
|
|
782
|
-
template: "Wallet {method} request is malformed.",
|
|
783
|
-
params: { method: "string" }
|
|
784
|
-
},
|
|
785
|
-
UNSUPPORTED_WALLET_METHOD: {
|
|
424
|
+
NON_CANONICAL_PAIR: {
|
|
786
425
|
phase: "pre_send",
|
|
787
|
-
template: "
|
|
788
|
-
params: {
|
|
426
|
+
template: "The pair {token0}/{token1} is not in canonical order: token0 must sort before token1.",
|
|
427
|
+
params: {
|
|
428
|
+
token0: "string",
|
|
429
|
+
token1: "string"
|
|
430
|
+
}
|
|
789
431
|
},
|
|
790
|
-
|
|
432
|
+
UNORDERABLE_SYMBOLS: {
|
|
791
433
|
phase: "pre_send",
|
|
792
|
-
template: "
|
|
793
|
-
params: {
|
|
434
|
+
template: "Unable to order trading symbols: expected a pair of symbols, got {received}.",
|
|
435
|
+
params: { received: "string" }
|
|
794
436
|
},
|
|
795
|
-
|
|
437
|
+
INVALID_TOKEN_CLASS_KEY: {
|
|
796
438
|
phase: "pre_send",
|
|
797
|
-
template: "
|
|
798
|
-
params: {
|
|
439
|
+
template: "Invalid token class key \"{tokenClassKey}\": expected a collection followed by category, type and additionalKey",
|
|
440
|
+
params: { tokenClassKey: "string" }
|
|
799
441
|
},
|
|
800
|
-
|
|
442
|
+
INCOMPLETE_TOKEN_CLASS_KEY: {
|
|
801
443
|
phase: "pre_send",
|
|
802
|
-
template: "
|
|
803
|
-
params: {
|
|
444
|
+
template: "Token class key \"{compositeKey}\" is missing {part}.",
|
|
445
|
+
params: {
|
|
446
|
+
compositeKey: "string",
|
|
447
|
+
part: "string"
|
|
448
|
+
}
|
|
804
449
|
},
|
|
805
|
-
|
|
450
|
+
INVALID_TRADING_SYMBOL: {
|
|
806
451
|
phase: "pre_send",
|
|
807
|
-
template: "Invalid
|
|
808
|
-
params: {
|
|
452
|
+
template: "Invalid trading symbol \"{symbol}\": must be 1-32 ASCII letters and digits and not a reserved word (Token, Unit, none).",
|
|
453
|
+
params: { symbol: "string" }
|
|
809
454
|
},
|
|
810
|
-
|
|
455
|
+
NO_VALID_TRADING_SYMBOL: {
|
|
811
456
|
phase: "pre_send",
|
|
812
|
-
template: "
|
|
457
|
+
template: "No valid trading symbol for token class {classKey}: \"{displaySymbol}\" is not an acceptable symbol and none is registered",
|
|
813
458
|
params: {
|
|
814
|
-
|
|
815
|
-
|
|
459
|
+
classKey: "string",
|
|
460
|
+
displaySymbol: "string"
|
|
816
461
|
}
|
|
817
462
|
},
|
|
818
|
-
|
|
463
|
+
TOKEN_SYMBOL_NOT_REGISTERED: {
|
|
819
464
|
phase: "pre_send",
|
|
820
|
-
template: "
|
|
821
|
-
params: {}
|
|
465
|
+
template: "Token {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.",
|
|
466
|
+
params: { token: "string" }
|
|
822
467
|
},
|
|
823
|
-
|
|
468
|
+
SYMBOL_DISAGREES_WITH_REGISTRY: {
|
|
824
469
|
phase: "pre_send",
|
|
825
|
-
template: "
|
|
826
|
-
params: {
|
|
470
|
+
template: "Supplied symbol {suppliedSymbol} for {token} disagrees with the registered trading symbol {registeredSymbol}.",
|
|
471
|
+
params: {
|
|
472
|
+
token: "string",
|
|
473
|
+
suppliedSymbol: "string",
|
|
474
|
+
registeredSymbol: "string"
|
|
475
|
+
}
|
|
827
476
|
},
|
|
828
|
-
|
|
477
|
+
CREATE_POOL_NEEDS_CLASS_KEY: {
|
|
829
478
|
phase: "pre_send",
|
|
830
|
-
template: "
|
|
831
|
-
params: {
|
|
479
|
+
template: "createPool needs each token as a class key; {token} is a bare trading symbol, which does not carry one.",
|
|
480
|
+
params: { token: "string" }
|
|
832
481
|
},
|
|
833
|
-
|
|
482
|
+
AMOUNT_IS_JS_NUMBER: {
|
|
834
483
|
phase: "pre_send",
|
|
835
|
-
template: "Invalid
|
|
836
|
-
params: {}
|
|
484
|
+
template: "Invalid {field}: use a decimal string or BigNumber, not a JavaScript number",
|
|
485
|
+
params: { field: "string" }
|
|
837
486
|
},
|
|
838
|
-
|
|
487
|
+
AMOUNT_NOT_FINITE: {
|
|
839
488
|
phase: "pre_send",
|
|
840
|
-
template: "Invalid {field}:
|
|
489
|
+
template: "Invalid {field}: must be a finite number (got {value})",
|
|
841
490
|
params: {
|
|
842
491
|
field: "string",
|
|
843
492
|
value: "string"
|
|
844
493
|
}
|
|
845
494
|
},
|
|
846
|
-
|
|
847
|
-
phase: "pre_send",
|
|
848
|
-
template: "Invalid decimal quantity: {value}",
|
|
849
|
-
params: { value: "string" }
|
|
850
|
-
},
|
|
851
|
-
PERSONAL_SIGN_NOT_JSON: {
|
|
495
|
+
AMOUNT_NOT_POSITIVE: {
|
|
852
496
|
phase: "pre_send",
|
|
853
|
-
template: "
|
|
854
|
-
params: {
|
|
497
|
+
template: "Invalid {field}: must be positive (got {value})",
|
|
498
|
+
params: {
|
|
499
|
+
field: "string",
|
|
500
|
+
value: "string"
|
|
501
|
+
}
|
|
855
502
|
},
|
|
856
|
-
|
|
503
|
+
AMOUNT_NEGATIVE: {
|
|
857
504
|
phase: "pre_send",
|
|
858
|
-
template: "
|
|
859
|
-
params: {
|
|
505
|
+
template: "Invalid {field}: must not be negative (got {value})",
|
|
506
|
+
params: {
|
|
507
|
+
field: "string",
|
|
508
|
+
value: "string"
|
|
509
|
+
}
|
|
860
510
|
},
|
|
861
|
-
|
|
511
|
+
PRICE_VALUES_IS_JS_NUMBER: {
|
|
862
512
|
phase: "pre_send",
|
|
863
|
-
template: "
|
|
513
|
+
template: "Invalid price values: use decimal strings or BigNumber values, not JavaScript numbers",
|
|
864
514
|
params: {}
|
|
865
515
|
},
|
|
866
|
-
|
|
867
|
-
phase: "pre_send",
|
|
868
|
-
template: "Personal-sign DTO carries signing-envelope field(s): {fields}",
|
|
869
|
-
params: { fields: "string" }
|
|
870
|
-
},
|
|
871
|
-
PERSONAL_SIGN_NOT_CANONICAL: {
|
|
872
|
-
phase: "pre_send",
|
|
873
|
-
template: "Personal-sign DTO is not in canonical serialized form",
|
|
874
|
-
params: {}
|
|
875
|
-
},
|
|
876
|
-
EIP712_NAME_COLLISION: {
|
|
877
|
-
phase: "pre_send",
|
|
878
|
-
template: "Name collisions are not supported when deriving EIP-712 types ({name}).",
|
|
879
|
-
params: { name: "string" }
|
|
880
|
-
},
|
|
881
|
-
EIP712_UNSUPPORTED_VALUE: {
|
|
882
|
-
phase: "pre_send",
|
|
883
|
-
template: "Unsupported EIP-712 value of type {valueType}.",
|
|
884
|
-
params: { valueType: "string" }
|
|
885
|
-
},
|
|
886
|
-
TYPED_DATA_NOT_STRING: {
|
|
887
|
-
phase: "pre_send",
|
|
888
|
-
template: "Invalid typed data: {what} must be a string",
|
|
889
|
-
params: { what: "string" }
|
|
890
|
-
},
|
|
891
|
-
TYPED_DATA_UNPAIRED_SURROGATE: {
|
|
892
|
-
phase: "pre_send",
|
|
893
|
-
template: "Invalid typed data: {what} has an unpaired surrogate",
|
|
894
|
-
params: { what: "string" }
|
|
895
|
-
},
|
|
896
|
-
TYPED_DATA_NOT_HEX_BYTES: {
|
|
897
|
-
phase: "pre_send",
|
|
898
|
-
template: "Invalid typed data: {what} must be an even-length 0x hex string",
|
|
899
|
-
params: { what: "string" }
|
|
900
|
-
},
|
|
901
|
-
TYPED_DATA_NOT_SAFE_INTEGER: {
|
|
902
|
-
phase: "pre_send",
|
|
903
|
-
template: "Invalid typed data: {what} must be a safe integer",
|
|
904
|
-
params: { what: "string" }
|
|
905
|
-
},
|
|
906
|
-
TYPED_DATA_NOT_INTEGER: {
|
|
907
|
-
phase: "pre_send",
|
|
908
|
-
template: "Invalid typed data: {what} must be an integer",
|
|
909
|
-
params: { what: "string" }
|
|
910
|
-
},
|
|
911
|
-
TYPED_DATA_NOT_BOOLEAN: {
|
|
912
|
-
phase: "pre_send",
|
|
913
|
-
template: "Invalid typed data: bool must be a boolean",
|
|
914
|
-
params: {}
|
|
915
|
-
},
|
|
916
|
-
TYPED_DATA_BAD_ADDRESS: {
|
|
917
|
-
phase: "pre_send",
|
|
918
|
-
template: "Invalid typed data: address must be 0x followed by 40 hex digits",
|
|
919
|
-
params: {}
|
|
920
|
-
},
|
|
921
|
-
TYPED_DATA_BAD_CHECKSUM: {
|
|
922
|
-
phase: "pre_send",
|
|
923
|
-
template: "Invalid typed data: address has a bad checksum",
|
|
924
|
-
params: {}
|
|
925
|
-
},
|
|
926
|
-
TYPED_DATA_BYTES_LENGTH: {
|
|
927
|
-
phase: "pre_send",
|
|
928
|
-
template: "Invalid typed data: {type} must be exactly {width} bytes",
|
|
929
|
-
params: {
|
|
930
|
-
type: "string",
|
|
931
|
-
width: "number"
|
|
932
|
-
}
|
|
933
|
-
},
|
|
934
|
-
TYPED_DATA_OUT_OF_BOUNDS: {
|
|
935
|
-
phase: "pre_send",
|
|
936
|
-
template: "Invalid typed data: value out of bounds for {type}",
|
|
937
|
-
params: { type: "string" }
|
|
938
|
-
},
|
|
939
|
-
TYPED_DATA_UNSUPPORTED_TYPE: {
|
|
940
|
-
phase: "pre_send",
|
|
941
|
-
template: "Invalid typed data: unsupported type \"{type}\"",
|
|
942
|
-
params: { type: "string" }
|
|
943
|
-
},
|
|
944
|
-
TYPED_DATA_FIELDS_NOT_LIST: {
|
|
945
|
-
phase: "pre_send",
|
|
946
|
-
template: "Invalid typed data: type {typeName} must be a list of fields",
|
|
947
|
-
params: { typeName: "string" }
|
|
948
|
-
},
|
|
949
|
-
TYPED_DATA_FIELD_MALFORMED: {
|
|
950
|
-
phase: "pre_send",
|
|
951
|
-
template: "Invalid typed data: type {typeName} has a field without a string name and type",
|
|
952
|
-
params: { typeName: "string" }
|
|
953
|
-
},
|
|
954
|
-
TYPED_DATA_INVALID_FIELD_NAME: {
|
|
955
|
-
phase: "pre_send",
|
|
956
|
-
template: "Invalid typed data: invalid field name \"{name}\"",
|
|
957
|
-
params: { name: "string" }
|
|
958
|
-
},
|
|
959
|
-
TYPED_DATA_DUPLICATE_FIELD: {
|
|
960
|
-
phase: "pre_send",
|
|
961
|
-
template: "Invalid typed data: duplicate field \"{name}\" in {typeName}",
|
|
962
|
-
params: {
|
|
963
|
-
name: "string",
|
|
964
|
-
typeName: "string"
|
|
965
|
-
}
|
|
966
|
-
},
|
|
967
|
-
TYPED_DATA_INVALID_TYPE_NAME: {
|
|
968
|
-
phase: "pre_send",
|
|
969
|
-
template: "Invalid typed data: invalid type name \"{name}\"",
|
|
970
|
-
params: { name: "string" }
|
|
971
|
-
},
|
|
972
|
-
TYPED_DATA_CIRCULAR_TYPE: {
|
|
973
|
-
phase: "pre_send",
|
|
974
|
-
template: "Invalid typed data: circular type reference in {typeName}",
|
|
975
|
-
params: { typeName: "string" }
|
|
976
|
-
},
|
|
977
|
-
TYPED_DATA_UNKNOWN_FIELD_TYPE: {
|
|
978
|
-
phase: "pre_send",
|
|
979
|
-
template: "Invalid typed data: unknown type \"{type}\" in {typeName}",
|
|
980
|
-
params: {
|
|
981
|
-
type: "string",
|
|
982
|
-
typeName: "string"
|
|
983
|
-
}
|
|
984
|
-
},
|
|
985
|
-
TYPED_DATA_NO_PRIMARY_TYPE: {
|
|
986
|
-
phase: "pre_send",
|
|
987
|
-
template: "Invalid typed data: missing primary type",
|
|
988
|
-
params: {}
|
|
989
|
-
},
|
|
990
|
-
TYPED_DATA_AMBIGUOUS_PRIMARY_TYPE: {
|
|
991
|
-
phase: "pre_send",
|
|
992
|
-
template: "Invalid typed data: ambiguous primary types or unused types ({roots})",
|
|
993
|
-
params: { roots: "string" }
|
|
994
|
-
},
|
|
995
|
-
TYPED_DATA_ARRAY_EXPECTED: {
|
|
996
|
-
phase: "pre_send",
|
|
997
|
-
template: "Invalid typed data: {type} array value must be an array",
|
|
998
|
-
params: { type: "string" }
|
|
999
|
-
},
|
|
1000
|
-
TYPED_DATA_ARRAY_LENGTH_MISMATCH: {
|
|
1001
|
-
phase: "pre_send",
|
|
1002
|
-
template: "Invalid typed data: array length mismatch",
|
|
1003
|
-
params: {}
|
|
1004
|
-
},
|
|
1005
|
-
TYPED_DATA_STRUCT_NOT_OBJECT: {
|
|
1006
|
-
phase: "pre_send",
|
|
1007
|
-
template: "Invalid typed data: {typeName} value must be an object",
|
|
1008
|
-
params: { typeName: "string" }
|
|
1009
|
-
},
|
|
1010
|
-
TYPED_DATA_MISSING_FIELD: {
|
|
1011
|
-
phase: "pre_send",
|
|
1012
|
-
template: "Invalid typed data: {typeName} value is missing field \"{field}\"",
|
|
1013
|
-
params: {
|
|
1014
|
-
typeName: "string",
|
|
1015
|
-
field: "string"
|
|
1016
|
-
}
|
|
1017
|
-
},
|
|
1018
|
-
TYPED_DATA_DOMAIN_NOT_OBJECT: {
|
|
1019
|
-
phase: "pre_send",
|
|
1020
|
-
template: "Invalid typed data: domain must be an object",
|
|
1021
|
-
params: {}
|
|
1022
|
-
},
|
|
1023
|
-
TYPED_DATA_INVALID_DOMAIN_KEY: {
|
|
1024
|
-
phase: "pre_send",
|
|
1025
|
-
template: "Invalid typed data: invalid typed-data domain key {key}",
|
|
1026
|
-
params: { key: "string" }
|
|
1027
|
-
},
|
|
1028
|
-
NON_CANONICAL_PAIR: {
|
|
1029
|
-
phase: "pre_send",
|
|
1030
|
-
template: "The pair {token0}/{token1} is not in canonical order: token0 must sort before token1.",
|
|
1031
|
-
params: {
|
|
1032
|
-
token0: "string",
|
|
1033
|
-
token1: "string"
|
|
1034
|
-
}
|
|
1035
|
-
},
|
|
1036
|
-
UNORDERABLE_SYMBOLS: {
|
|
1037
|
-
phase: "pre_send",
|
|
1038
|
-
template: "Unable to order trading symbols: expected a pair of symbols, got {received}.",
|
|
1039
|
-
params: { received: "string" }
|
|
1040
|
-
},
|
|
1041
|
-
INVALID_TOKEN_CLASS_KEY: {
|
|
1042
|
-
phase: "pre_send",
|
|
1043
|
-
template: "Invalid token class key \"{tokenClassKey}\": expected a collection followed by category, type and additionalKey",
|
|
1044
|
-
params: { tokenClassKey: "string" }
|
|
1045
|
-
},
|
|
1046
|
-
INCOMPLETE_TOKEN_CLASS_KEY: {
|
|
1047
|
-
phase: "pre_send",
|
|
1048
|
-
template: "Token class key \"{compositeKey}\" is missing {part}.",
|
|
1049
|
-
params: {
|
|
1050
|
-
compositeKey: "string",
|
|
1051
|
-
part: "string"
|
|
1052
|
-
}
|
|
1053
|
-
},
|
|
1054
|
-
INVALID_TRADING_SYMBOL: {
|
|
1055
|
-
phase: "pre_send",
|
|
1056
|
-
template: "Invalid trading symbol \"{symbol}\": must be 1-32 ASCII letters and digits and not a reserved word (Token, Unit, none).",
|
|
1057
|
-
params: { symbol: "string" }
|
|
1058
|
-
},
|
|
1059
|
-
NO_VALID_TRADING_SYMBOL: {
|
|
1060
|
-
phase: "pre_send",
|
|
1061
|
-
template: "No valid trading symbol for token class {classKey}: \"{displaySymbol}\" is not an acceptable symbol and none is registered",
|
|
1062
|
-
params: {
|
|
1063
|
-
classKey: "string",
|
|
1064
|
-
displaySymbol: "string"
|
|
1065
|
-
}
|
|
1066
|
-
},
|
|
1067
|
-
TOKEN_SYMBOL_NOT_REGISTERED: {
|
|
1068
|
-
phase: "pre_send",
|
|
1069
|
-
template: "Token {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.",
|
|
1070
|
-
params: { token: "string" }
|
|
1071
|
-
},
|
|
1072
|
-
SYMBOL_DISAGREES_WITH_REGISTRY: {
|
|
1073
|
-
phase: "pre_send",
|
|
1074
|
-
template: "Supplied symbol {suppliedSymbol} for {token} disagrees with the registered trading symbol {registeredSymbol}.",
|
|
1075
|
-
params: {
|
|
1076
|
-
token: "string",
|
|
1077
|
-
suppliedSymbol: "string",
|
|
1078
|
-
registeredSymbol: "string"
|
|
1079
|
-
}
|
|
1080
|
-
},
|
|
1081
|
-
CREATE_POOL_NEEDS_CLASS_KEY: {
|
|
1082
|
-
phase: "pre_send",
|
|
1083
|
-
template: "createPool needs each token as a class key; {token} is a bare trading symbol, which does not carry one.",
|
|
1084
|
-
params: { token: "string" }
|
|
1085
|
-
},
|
|
1086
|
-
INVALID_UNIQUE_KEY: {
|
|
1087
|
-
phase: "pre_send",
|
|
1088
|
-
template: "The uniqueKey \"{uniqueKey}\" must be 1-128 characters of A-Z, a-z, 0-9, \".\", \"_\", \":\" or \"-\".",
|
|
1089
|
-
params: { uniqueKey: "string" }
|
|
1090
|
-
},
|
|
1091
|
-
INVALID_UNIQUE_KEY_PREFIX: {
|
|
1092
|
-
phase: "pre_send",
|
|
1093
|
-
template: "uniqueKeyPrefix \"{uniqueKeyPrefix}\" must be at most {maxLength} characters of A-Z, a-z, 0-9, \".\", \"_\", \":\" or \"-\".",
|
|
1094
|
-
params: {
|
|
1095
|
-
uniqueKeyPrefix: "string",
|
|
1096
|
-
maxLength: "number"
|
|
1097
|
-
}
|
|
1098
|
-
},
|
|
1099
|
-
AMOUNT_IS_JS_NUMBER: {
|
|
1100
|
-
phase: "pre_send",
|
|
1101
|
-
template: "Invalid {field}: use a decimal string or BigNumber, not a JavaScript number",
|
|
1102
|
-
params: { field: "string" }
|
|
1103
|
-
},
|
|
1104
|
-
AMOUNT_NOT_FINITE: {
|
|
1105
|
-
phase: "pre_send",
|
|
1106
|
-
template: "Invalid {field}: must be a finite number (got {value})",
|
|
1107
|
-
params: {
|
|
1108
|
-
field: "string",
|
|
1109
|
-
value: "string"
|
|
1110
|
-
}
|
|
1111
|
-
},
|
|
1112
|
-
AMOUNT_NOT_POSITIVE: {
|
|
1113
|
-
phase: "pre_send",
|
|
1114
|
-
template: "Invalid {field}: must be positive (got {value})",
|
|
1115
|
-
params: {
|
|
1116
|
-
field: "string",
|
|
1117
|
-
value: "string"
|
|
1118
|
-
}
|
|
1119
|
-
},
|
|
1120
|
-
AMOUNT_NEGATIVE: {
|
|
1121
|
-
phase: "pre_send",
|
|
1122
|
-
template: "Invalid {field}: must not be negative (got {value})",
|
|
1123
|
-
params: {
|
|
1124
|
-
field: "string",
|
|
1125
|
-
value: "string"
|
|
1126
|
-
}
|
|
1127
|
-
},
|
|
1128
|
-
PRICE_VALUES_IS_JS_NUMBER: {
|
|
1129
|
-
phase: "pre_send",
|
|
1130
|
-
template: "Invalid price values: use decimal strings or BigNumber values, not JavaScript numbers",
|
|
1131
|
-
params: {}
|
|
1132
|
-
},
|
|
1133
|
-
INVALID_PRICE_VALUES: {
|
|
516
|
+
INVALID_PRICE_VALUES: {
|
|
1134
517
|
phase: "pre_send",
|
|
1135
518
|
template: "Invalid price values: all prices must be finite and positive (spot {spotPrice}, lower {lowerPrice}, upper {upperPrice})",
|
|
1136
519
|
params: {
|
|
@@ -1309,156 +692,217 @@ const GSWAP_ERROR_CATALOG = {
|
|
|
1309
692
|
min: "number",
|
|
1310
693
|
max: "number"
|
|
1311
694
|
}
|
|
695
|
+
},
|
|
696
|
+
INVALID_MARKET_TOKEN: {
|
|
697
|
+
phase: "pre_send",
|
|
698
|
+
template: "A market read names a token by a symbol or class key of 1 to 120 letters, digits and . _ $ + / : | - characters (got \"{token}\").",
|
|
699
|
+
params: { token: "string" }
|
|
700
|
+
},
|
|
701
|
+
INVALID_TIMEFRAME: {
|
|
702
|
+
phase: "pre_send",
|
|
703
|
+
template: "timeframe \"{value}\" is not allowed; it must be one of {allowed}.",
|
|
704
|
+
params: {
|
|
705
|
+
value: "string",
|
|
706
|
+
allowed: "string"
|
|
707
|
+
}
|
|
708
|
+
},
|
|
709
|
+
INVALID_SEASON_ID: {
|
|
710
|
+
phase: "pre_send",
|
|
711
|
+
template: "seasonId must be an integer of at least 1 (got {value}).",
|
|
712
|
+
params: { value: "string" }
|
|
713
|
+
},
|
|
714
|
+
INVALID_POLL_INTERVAL: {
|
|
715
|
+
phase: "pre_send",
|
|
716
|
+
template: "intervalMs must be a finite number of milliseconds of at least 1 (got {value}).",
|
|
717
|
+
params: { value: "string" }
|
|
1312
718
|
}
|
|
1313
719
|
};
|
|
1314
720
|
/**
|
|
1315
|
-
* The
|
|
1316
|
-
*
|
|
721
|
+
* The catalog `GSwapSDKError` reads templates and phases from: core's entries and the DEX entries,
|
|
722
|
+
* merged (never redefined: the two key sets are disjoint, pinned by tests/error_catalog.test.ts).
|
|
723
|
+
*/
|
|
724
|
+
const GSWAP_SDK_ERROR_ENTRIES = {
|
|
725
|
+
..._gala_chain_gala_core_sdk.GALA_ERROR_CATALOG,
|
|
726
|
+
...GSWAP_ERROR_CATALOG
|
|
727
|
+
};
|
|
728
|
+
/**
|
|
729
|
+
* The English template of every DEX error code, `{placeholder}` markers included. An optional seed
|
|
730
|
+
* or fallback for an app's own translations; the SDK's `message` is generated from it. The core
|
|
731
|
+
* codes' templates are `galaErrorTemplates` from `@gala-chain/gala-core-sdk`: an app that seeds
|
|
732
|
+
* every code a `GSwapSDKError` can carry merges the two.
|
|
1317
733
|
*/
|
|
1318
734
|
const gswapErrorTemplates = Object.fromEntries(Object.entries(GSWAP_ERROR_CATALOG).map(([code, entry]) => [code, entry.template]));
|
|
1319
|
-
const PLACEHOLDER = /\{([A-Za-z0-9_]+)\}/gu;
|
|
1320
|
-
/** Fills a template's `{placeholders}` from `params`; an unknown placeholder is left as written. */
|
|
1321
|
-
function renderGSwapErrorTemplate(template, params) {
|
|
1322
|
-
return template.replace(PLACEHOLDER, (whole, name) => {
|
|
1323
|
-
const value = params[name];
|
|
1324
|
-
return typeof value === "string" || typeof value === "number" || typeof value === "boolean" ? String(value) : whole;
|
|
1325
|
-
});
|
|
1326
|
-
}
|
|
1327
735
|
|
|
1328
736
|
//#endregion
|
|
1329
|
-
//#region src/classes/
|
|
1330
|
-
/**
|
|
1331
|
-
* The HTTP status of a version wall: the backend refuses an app version that is too old (HTTP 426
|
|
1332
|
-
* Upgrade Required). It is `UPDATE_REQUIRED` on every request the SDK sends, whatever else the body
|
|
1333
|
-
* says; a confirmation read keeps `CONFIRMATION_FAILED` because the write it confirms may be applied.
|
|
1334
|
-
*/
|
|
1335
|
-
const UPDATE_REQUIRED_STATUS = 426;
|
|
737
|
+
//#region src/classes/gswap_error_phase_table.ts
|
|
1336
738
|
/**
|
|
1337
|
-
*
|
|
1338
|
-
*
|
|
1339
|
-
*
|
|
1340
|
-
*
|
|
1341
|
-
*
|
|
739
|
+
* The phase of every DEX catalog code, WITHOUT the catalog. The failure guards (`classifyFailure`,
|
|
740
|
+
* `mayHaveBeenQueued`, ...) need only a code's phase, and the catalog's English templates are
|
|
741
|
+
* ~15 kB: reading the phase from here keeps an app shell that imports a guard from loading them.
|
|
742
|
+
* The catalog entry is still where a code is declared; this table is checked against it AT
|
|
743
|
+
* COMPILE TIME, in both directions (a missing code, an extra code or a different phase is a type
|
|
744
|
+
* error), so the two cannot drift. The core codes' phases are core's `GALA_ERROR_PHASE_BY_CODE`.
|
|
1342
745
|
*/
|
|
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
|
-
|
|
746
|
+
const GSWAP_ERROR_PHASE_BY_CODE = {
|
|
747
|
+
HTTP_ERROR: "post_send",
|
|
748
|
+
UPDATE_REQUIRED: "post_send",
|
|
749
|
+
REQUEST_FAILED: "in_flight",
|
|
750
|
+
METHOD_NOT_SUPPORTED: "pre_send",
|
|
751
|
+
SUBMISSION_OUTCOME_UNKNOWN: "in_flight",
|
|
752
|
+
GATEWAY_REPORTED_ERROR: "post_send",
|
|
753
|
+
GATEWAY_DATA_MISSING: "post_send",
|
|
754
|
+
GATEWAY_MODE_INVALID: "post_send",
|
|
755
|
+
GATEWAY_SYNC_REQUEST_TRADE: "post_send",
|
|
756
|
+
MISSING_UNIQUE_KEY: "post_send",
|
|
757
|
+
UNIQUE_KEY_MISMATCH: "post_send",
|
|
758
|
+
SHADOW_NOT_QUEUED: "post_send",
|
|
759
|
+
SHADOW_SUBMIT_NEEDS_WALLET_ADDRESS: "pre_send",
|
|
760
|
+
METHOD_NOT_ALLOWED: "post_send",
|
|
761
|
+
SIGNATURE_INVALID: "post_send",
|
|
762
|
+
SIGNER_MISMATCH: "post_send",
|
|
763
|
+
DTO_INVALID: "post_send",
|
|
764
|
+
BOUNDS_VIOLATION: "post_send",
|
|
765
|
+
SYMBOL_CONFLICT: "post_send",
|
|
766
|
+
RATE_LIMITED: "post_send",
|
|
767
|
+
CHAIN_DISPATCH_FAILED: "post_send",
|
|
768
|
+
GATEWAY_REJECTED: "post_send",
|
|
769
|
+
CHAIN_REJECTED: "post_send",
|
|
770
|
+
INVALID_CHAIN_RESPONSE: "post_send",
|
|
771
|
+
INVALID_POSITION_RESPONSE: "post_send",
|
|
772
|
+
CONFIRMATION_TIMEOUT: "post_send",
|
|
773
|
+
CONFIRMATION_ABORTED: "post_send",
|
|
774
|
+
CONFIRMATION_FAILED: "post_send",
|
|
775
|
+
CONFIRMATION_MALFORMED: "post_send",
|
|
776
|
+
CONFIRMATION_KEY_MISMATCH: "post_send",
|
|
777
|
+
NO_SIGNER: "pre_send",
|
|
778
|
+
NO_WALLET_ADDRESS: "post_send",
|
|
779
|
+
NO_POOL_AVAILABLE: "post_send",
|
|
780
|
+
INSUFFICIENT_LIQUIDITY: "post_send",
|
|
781
|
+
UNKNOWN_TOKEN: "pre_send",
|
|
782
|
+
SYMBOL_NOT_FOUND: "pre_send",
|
|
783
|
+
STREAM_API_KEY_UNUSABLE: "pre_send",
|
|
784
|
+
STREAM_WEBSOCKET_UNAVAILABLE: "pre_send",
|
|
785
|
+
STREAM_NOT_CONNECTED: "pre_send",
|
|
786
|
+
STREAM_DISCONNECTED: "in_flight",
|
|
787
|
+
STREAM_REQUEST_TIMEOUT: "in_flight",
|
|
788
|
+
STREAM_CONNECT_FAILED: "in_flight",
|
|
789
|
+
STREAM_CLOSED_BEFORE_CONNECT: "in_flight",
|
|
790
|
+
STREAM_CONNECTION_CLOSED: "in_flight",
|
|
791
|
+
TOKEN_RESOLUTION_FAILED: "pre_send",
|
|
792
|
+
MISSING_WALLET_ADDRESS: "pre_send",
|
|
793
|
+
NON_CANONICAL_PAIR: "pre_send",
|
|
794
|
+
UNORDERABLE_SYMBOLS: "pre_send",
|
|
795
|
+
INVALID_TOKEN_CLASS_KEY: "pre_send",
|
|
796
|
+
INCOMPLETE_TOKEN_CLASS_KEY: "pre_send",
|
|
797
|
+
INVALID_TRADING_SYMBOL: "pre_send",
|
|
798
|
+
NO_VALID_TRADING_SYMBOL: "pre_send",
|
|
799
|
+
TOKEN_SYMBOL_NOT_REGISTERED: "pre_send",
|
|
800
|
+
SYMBOL_DISAGREES_WITH_REGISTRY: "pre_send",
|
|
801
|
+
CREATE_POOL_NEEDS_CLASS_KEY: "pre_send",
|
|
802
|
+
AMOUNT_IS_JS_NUMBER: "pre_send",
|
|
803
|
+
AMOUNT_NOT_FINITE: "pre_send",
|
|
804
|
+
AMOUNT_NOT_POSITIVE: "pre_send",
|
|
805
|
+
AMOUNT_NEGATIVE: "pre_send",
|
|
806
|
+
PRICE_VALUES_IS_JS_NUMBER: "pre_send",
|
|
807
|
+
INVALID_PRICE_VALUES: "pre_send",
|
|
808
|
+
INVALID_PRICE_RANGE: "pre_send",
|
|
809
|
+
INVALID_PRICE_BOUNDS: "pre_send",
|
|
810
|
+
PRICE_BOUNDS_ORDER: "pre_send",
|
|
811
|
+
INVALID_STARTING_PRICE: "pre_send",
|
|
812
|
+
INVALID_SQRT_PRICE: "pre_send",
|
|
813
|
+
INVALID_TOKEN_DECIMALS: "pre_send",
|
|
814
|
+
TICKS_NOT_INTEGERS: "pre_send",
|
|
815
|
+
INVALID_TICK_RANGE: "pre_send",
|
|
816
|
+
TICKS_OUT_OF_BOUNDS: "pre_send",
|
|
817
|
+
TICKS_NOT_ALIGNED: "pre_send",
|
|
818
|
+
INVALID_TICK_SPACING: "pre_send",
|
|
819
|
+
INVALID_FEE: "pre_send",
|
|
820
|
+
EXACT_AMOUNT_REQUIRED: "pre_send",
|
|
821
|
+
EXACTLY_ONE_OF_REQUIRED: "pre_send",
|
|
822
|
+
AT_MOST_ONE_OF_ALLOWED: "pre_send",
|
|
823
|
+
POOL_MISMATCH: "pre_send",
|
|
824
|
+
INVALID_DTO: "pre_send",
|
|
825
|
+
DTO_NOT_CANONICAL: "pre_send",
|
|
826
|
+
SEARCH_TOO_LONG: "pre_send",
|
|
827
|
+
INVALID_SORT_BY: "pre_send",
|
|
828
|
+
INVALID_SORT_ORDER: "pre_send",
|
|
829
|
+
INVALID_PAGE: "pre_send",
|
|
830
|
+
INVALID_LIMIT: "pre_send",
|
|
831
|
+
INVALID_MARKET_TOKEN: "pre_send",
|
|
832
|
+
INVALID_TIMEFRAME: "pre_send",
|
|
833
|
+
INVALID_SEASON_ID: "pre_send",
|
|
834
|
+
INVALID_POLL_INTERVAL: "pre_send"
|
|
835
|
+
};
|
|
836
|
+
/** True for a DEX code (gswap-sdk's own catalog). */
|
|
837
|
+
function isDexCatalogCode(code) {
|
|
838
|
+
return Object.hasOwn(GSWAP_ERROR_PHASE_BY_CODE, code);
|
|
839
|
+
}
|
|
840
|
+
/** True for a code a `GSwapSDKError` can carry: a core code or a DEX code. */
|
|
841
|
+
function isCatalogCode(code) {
|
|
842
|
+
return (0, _gala_chain_gala_core_sdk.isGalaCatalogCode)(code) || isDexCatalogCode(code);
|
|
843
|
+
}
|
|
844
|
+
/**
|
|
845
|
+
* The phase of a view: the one it states, else the catalog's phase for its code (core's or the
|
|
846
|
+
* DEX catalog's), else `undefined`.
|
|
847
|
+
*/
|
|
848
|
+
function phaseOfView(view) {
|
|
849
|
+
if (view.phase !== void 0) return view.phase;
|
|
850
|
+
if ((0, _gala_chain_gala_core_sdk.isGalaCatalogCode)(view.code)) return _gala_chain_gala_core_sdk.GALA_ERROR_PHASE_BY_CODE[view.code];
|
|
851
|
+
return isDexCatalogCode(view.code) ? GSWAP_ERROR_PHASE_BY_CODE[view.code] : void 0;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
//#endregion
|
|
855
|
+
//#region src/classes/gswap_sdk_error.ts
|
|
856
|
+
/**
|
|
857
|
+
* The HTTP status of a version wall: the backend refuses an app version that is too old (HTTP 426
|
|
858
|
+
* Upgrade Required). It is `UPDATE_REQUIRED` on every request the SDK sends, whatever else the body
|
|
859
|
+
* says; a confirmation read keeps `CONFIRMATION_FAILED` because the write it confirms may be applied.
|
|
860
|
+
*/
|
|
861
|
+
const UPDATE_REQUIRED_STATUS = 426;
|
|
862
|
+
/**
|
|
863
|
+
* Error raised by gswap-sdk for validation, gateway, chain, or transaction failures. It is a
|
|
864
|
+
* `GalaSDKError` (`@gala-chain/gala-core-sdk`), and can carry any code of the core catalog or of
|
|
865
|
+
* gswap-sdk's own DEX catalog: the signing, transport and wallet failures of a gswap flow are core
|
|
866
|
+
* codes, thrown either as a `GSwapSDKError` by gswap-sdk or as a plain `GalaSDKError` by core
|
|
867
|
+
* itself (a signer, the fetch requestor). Catch both with `isGSwapError` (or core's `isGalaError`);
|
|
868
|
+
* `instanceof GSwapSDKError` alone misses the second kind.
|
|
869
|
+
*
|
|
870
|
+
* Branch on {@link code} (stable) and render {@link params} through your own localization.
|
|
871
|
+
* {@link message} is English generated from the catalog template for logs and developers: it
|
|
872
|
+
* is not stable and must never be shown to a user or matched on.
|
|
873
|
+
*/
|
|
874
|
+
var GSwapSDKError = class GSwapSDKError extends _gala_chain_gala_core_sdk.GalaSDKError {
|
|
875
|
+
/** Core's catalog entries and the DEX entries: a `GSwapSDKError` can carry a code of either. */
|
|
876
|
+
static catalog = GSWAP_SDK_ERROR_ENTRIES;
|
|
877
|
+
constructor(code, params, context = {}) {
|
|
878
|
+
super(code, params, context);
|
|
879
|
+
this.name = "GSwapSDKError";
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* True when the failure means "that market, pool or position does not exist" rather than
|
|
883
|
+
* "something went wrong": an HTTP 404, a pair with no pool at any fee tier
|
|
884
|
+
* (`NO_POOL_AVAILABLE`), or a token the DEX contract has no trading symbol for
|
|
885
|
+
* (`UNKNOWN_TOKEN`). A pool that exists but is too thin (`INSUFFICIENT_LIQUIDITY`) is NOT
|
|
886
|
+
* not-found.
|
|
887
|
+
*/
|
|
888
|
+
get notFound() {
|
|
889
|
+
return super.notFound || this.code === "NO_POOL_AVAILABLE" || this.code === "UNKNOWN_TOKEN";
|
|
890
|
+
}
|
|
891
|
+
/** Create the standard missing-signer error. */
|
|
892
|
+
static noSignerError() {
|
|
893
|
+
return new GSwapSDKError("NO_SIGNER", {});
|
|
894
|
+
}
|
|
895
|
+
/** Create the error used when confirming a position write without a configured wallet address. */
|
|
896
|
+
static noWalletAddressError() {
|
|
897
|
+
return new GSwapSDKError("NO_WALLET_ADDRESS", {});
|
|
898
|
+
}
|
|
899
|
+
/**
|
|
900
|
+
* Create the error used when no pool can quote the requested pair. `source` is the backend
|
|
901
|
+
* answer that said so; its status and body are kept.
|
|
902
|
+
*/
|
|
903
|
+
static noPoolAvailableError(tokenIn, tokenOut, fee, source) {
|
|
904
|
+
return new GSwapSDKError("NO_POOL_AVAILABLE", {
|
|
905
|
+
tokenIn: tokenText(tokenIn),
|
|
1462
906
|
tokenOut: tokenText(tokenOut),
|
|
1463
907
|
fee: feeText(fee)
|
|
1464
908
|
}, sourceContext(source));
|
|
@@ -1571,7 +1015,7 @@ var GSwapSDKError = class GSwapSDKError extends Error {
|
|
|
1571
1015
|
/** Convert an unexpected HTTP response into an SDK error. */
|
|
1572
1016
|
static async fromErrorResponse(url, response) {
|
|
1573
1017
|
const bodyText = await response.text();
|
|
1574
|
-
const bodyJson = parseJson(bodyText);
|
|
1018
|
+
const bodyJson = (0, _gala_chain_gala_core_sdk.parseJson)(bodyText);
|
|
1575
1019
|
const errorObject = getObjectProperty(bodyJson, "error");
|
|
1576
1020
|
const errorKey = getStringProperty(errorObject, "ErrorKey");
|
|
1577
1021
|
const message = getStringProperty(errorObject, "Message");
|
|
@@ -1620,7 +1064,7 @@ function sourceContext(source) {
|
|
|
1620
1064
|
};
|
|
1621
1065
|
}
|
|
1622
1066
|
function isGSwapError(value, code) {
|
|
1623
|
-
return value instanceof
|
|
1067
|
+
return value instanceof _gala_chain_gala_core_sdk.GalaSDKError && (code === void 0 || value.code === code);
|
|
1624
1068
|
}
|
|
1625
1069
|
/**
|
|
1626
1070
|
* True when `value` is an SDK error that was thrown BEFORE anything was transmitted
|
|
@@ -1635,8 +1079,9 @@ function isGSwapError(value, code) {
|
|
|
1635
1079
|
* error is pre-send too, and repeating the same call fails the same way.
|
|
1636
1080
|
*
|
|
1637
1081
|
* It also answers for a plain-object view of an SDK error (`{ code, phase? }`, see
|
|
1638
|
-
* `FailureView`): the view's own `phase`, else the catalog's phase for its `code
|
|
1639
|
-
* `boolean`, not a type guard (a view is not a `GSwapSDKError`); use
|
|
1082
|
+
* `FailureView`): the view's own `phase`, else the catalog's phase for its `code` (a core code or
|
|
1083
|
+
* a DEX code). It is a plain `boolean`, not a type guard (a view is not a `GSwapSDKError`); use
|
|
1084
|
+
* `isGSwapError` to narrow.
|
|
1640
1085
|
*
|
|
1641
1086
|
* @example
|
|
1642
1087
|
* ```ts
|
|
@@ -1644,7 +1089,7 @@ function isGSwapError(value, code) {
|
|
|
1644
1089
|
* ```
|
|
1645
1090
|
*/
|
|
1646
1091
|
function isPreSendError(value) {
|
|
1647
|
-
const view = toFailureView(value);
|
|
1092
|
+
const view = (0, _gala_chain_gala_core_sdk.toFailureView)(value);
|
|
1648
1093
|
return view !== void 0 && phaseOfView(view) === "pre_send";
|
|
1649
1094
|
}
|
|
1650
1095
|
/** Narrow an object property without trusting a cast at a network boundary. */
|
|
@@ -1659,165 +1104,6 @@ function getStringProperty(value, property) {
|
|
|
1659
1104
|
const propertyValue = value[property];
|
|
1660
1105
|
return typeof propertyValue === "string" ? propertyValue : void 0;
|
|
1661
1106
|
}
|
|
1662
|
-
/** Parse a response body, preserving non-JSON text for error reporting. */
|
|
1663
|
-
function parseJson(text) {
|
|
1664
|
-
try {
|
|
1665
|
-
return JSON.parse(text);
|
|
1666
|
-
} catch {
|
|
1667
|
-
return;
|
|
1668
|
-
}
|
|
1669
|
-
}
|
|
1670
|
-
|
|
1671
|
-
//#endregion
|
|
1672
|
-
//#region src/version.ts
|
|
1673
|
-
/** Package version embedded by the bundler, with a source-test fallback. */
|
|
1674
|
-
const SDK_VERSION = "1.2.0";
|
|
1675
|
-
|
|
1676
|
-
//#endregion
|
|
1677
|
-
//#region src/utils/transport.ts
|
|
1678
|
-
/**
|
|
1679
|
-
* Arms the abort timer for a request. `Infinity` means "never time out": `setTimeout` would turn
|
|
1680
|
-
* it into 1 ms, so no timer is armed at all. Returns a function that disarms it.
|
|
1681
|
-
*/
|
|
1682
|
-
function armTimeout(controller, timeoutMs) {
|
|
1683
|
-
if (timeoutMs === Infinity) return () => void 0;
|
|
1684
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
1685
|
-
return () => clearTimeout(timer);
|
|
1686
|
-
}
|
|
1687
|
-
/**
|
|
1688
|
-
* Validates a request timeout in milliseconds: a positive finite number, or `Infinity` for none.
|
|
1689
|
-
*
|
|
1690
|
-
* @throws GSwapSDKError `INVALID_TIMEOUT` (`params.field` = `name`) otherwise.
|
|
1691
|
-
*/
|
|
1692
|
-
function validateTimeoutMs(timeoutMs, name) {
|
|
1693
|
-
if (timeoutMs === Infinity || Number.isFinite(timeoutMs) && timeoutMs > 0) return timeoutMs;
|
|
1694
|
-
throw new GSwapSDKError("INVALID_TIMEOUT", {
|
|
1695
|
-
field: name,
|
|
1696
|
-
value: timeoutMs
|
|
1697
|
-
});
|
|
1698
|
-
}
|
|
1699
|
-
const READABLE_QUERY_SAFE = /[A-Za-z0-9\-._~!'()*$|:,]/u;
|
|
1700
|
-
function encodeReadable(text) {
|
|
1701
|
-
let out = "";
|
|
1702
|
-
for (const char of text) if (char === " ") out += "+";
|
|
1703
|
-
else if (READABLE_QUERY_SAFE.test(char)) out += char;
|
|
1704
|
-
else out += encodeScalar(char);
|
|
1705
|
-
return out;
|
|
1706
|
-
}
|
|
1707
|
-
/** Percent-encodes one code point; a lone surrogate becomes U+FFFD, as `URLSearchParams` does. */
|
|
1708
|
-
function encodeScalar(char) {
|
|
1709
|
-
try {
|
|
1710
|
-
return encodeURIComponent(char);
|
|
1711
|
-
} catch {
|
|
1712
|
-
return "%EF%BF%BD";
|
|
1713
|
-
}
|
|
1714
|
-
}
|
|
1715
|
-
/** Serializes GET parameters, in insertion order, in the encoding the wire options select. */
|
|
1716
|
-
function buildQueryString(params, encoding = "standard") {
|
|
1717
|
-
if (encoding === "readable") return Object.entries(params).map(([key, value]) => `${encodeReadable(key)}=${encodeReadable(value)}`).join("&");
|
|
1718
|
-
return new URLSearchParams(params).toString();
|
|
1719
|
-
}
|
|
1720
|
-
/**
|
|
1721
|
-
* The request headers for ANY SDK request, GET or POST, from one place so the wire options reach
|
|
1722
|
-
* every request alike. Order, which is the order the requestor sees them in: `User-Agent`
|
|
1723
|
-
* (`wire.userAgent`, default `GalaChain-SDK/<version>`, `false` omits it), `parts.defaults`,
|
|
1724
|
-
* `wire.headers`, `Content-Type` (JSON bodies only), `parts.fixed`. A later entry replaces an
|
|
1725
|
-
* earlier one of the same name regardless of case, so a header is never sent twice.
|
|
1726
|
-
*/
|
|
1727
|
-
function buildRequestHeaders(wire, parts = {}) {
|
|
1728
|
-
const entries = /* @__PURE__ */ new Map();
|
|
1729
|
-
const put = (headers, last = false) => {
|
|
1730
|
-
for (const [name, value] of Object.entries(headers ?? {})) {
|
|
1731
|
-
if (last) entries.delete(name.toLowerCase());
|
|
1732
|
-
entries.set(name.toLowerCase(), [name, value]);
|
|
1733
|
-
}
|
|
1734
|
-
};
|
|
1735
|
-
if ((wire === null || wire === void 0 ? void 0 : wire.userAgent) !== false) put({ "User-Agent": (wire === null || wire === void 0 ? void 0 : wire.userAgent) ?? `GalaChain-SDK/${"1.2.0"}` });
|
|
1736
|
-
put(parts.defaults);
|
|
1737
|
-
put(wire === null || wire === void 0 ? void 0 : wire.headers);
|
|
1738
|
-
if (parts.json === true && !entries.has("content-type")) put({ "Content-Type": "application/json" });
|
|
1739
|
-
put(parts.fixed, true);
|
|
1740
|
-
return Object.fromEntries(entries.values());
|
|
1741
|
-
}
|
|
1742
|
-
/**
|
|
1743
|
-
* The default requestor: the global `fetch`, with a failure before any answer (offline, DNS,
|
|
1744
|
-
* CORS) coded as `TRANSPORT_ERROR` and the original error kept as `cause`. An aborted request is
|
|
1745
|
-
* left to {@link requestWithTimeout}, which knows whether the timeout or the caller aborted it.
|
|
1746
|
-
* A requestor the app supplies is not wrapped: its rejection reaches the caller unchanged.
|
|
1747
|
-
*/
|
|
1748
|
-
const fetchRequestor = async (url, init) => {
|
|
1749
|
-
try {
|
|
1750
|
-
return await fetch(url, init);
|
|
1751
|
-
} catch (error) {
|
|
1752
|
-
var _init$signal;
|
|
1753
|
-
if ((init === null || init === void 0 || (_init$signal = init.signal) === null || _init$signal === void 0 ? void 0 : _init$signal.aborted) === true) throw error;
|
|
1754
|
-
throw new GSwapSDKError("TRANSPORT_ERROR", { url }, {
|
|
1755
|
-
url,
|
|
1756
|
-
cause: error
|
|
1757
|
-
});
|
|
1758
|
-
}
|
|
1759
|
-
};
|
|
1760
|
-
async function requestWithTimeout(requestor, url, options, timeoutMs) {
|
|
1761
|
-
const controller = new AbortController();
|
|
1762
|
-
const disarm = armTimeout(controller, timeoutMs);
|
|
1763
|
-
const externalSignal = options.signal;
|
|
1764
|
-
const abortFromCaller = () => controller.abort();
|
|
1765
|
-
externalSignal === null || externalSignal === void 0 || externalSignal.addEventListener("abort", abortFromCaller, { once: true });
|
|
1766
|
-
try {
|
|
1767
|
-
return await requestor(url, {
|
|
1768
|
-
...options,
|
|
1769
|
-
signal: controller.signal
|
|
1770
|
-
});
|
|
1771
|
-
} catch (error) {
|
|
1772
|
-
if ((externalSignal === null || externalSignal === void 0 ? void 0 : externalSignal.aborted) === true) throw error;
|
|
1773
|
-
if (controller.signal.aborted) throw new GSwapSDKError("REQUEST_TIMEOUT", {
|
|
1774
|
-
url,
|
|
1775
|
-
timeoutMs
|
|
1776
|
-
}, {
|
|
1777
|
-
url,
|
|
1778
|
-
cause: error
|
|
1779
|
-
});
|
|
1780
|
-
throw error;
|
|
1781
|
-
} finally {
|
|
1782
|
-
disarm();
|
|
1783
|
-
externalSignal === null || externalSignal === void 0 || externalSignal.removeEventListener("abort", abortFromCaller);
|
|
1784
|
-
}
|
|
1785
|
-
}
|
|
1786
|
-
async function readResponseBody(response) {
|
|
1787
|
-
const text = await response.text();
|
|
1788
|
-
return parseJson(text) ?? text;
|
|
1789
|
-
}
|
|
1790
|
-
/** Read a case-insensitive response header from either supported header shape. */
|
|
1791
|
-
function readHeader(response, name) {
|
|
1792
|
-
var _Object$entries$find;
|
|
1793
|
-
const headers = response.headers;
|
|
1794
|
-
if (headers === void 0) return void 0;
|
|
1795
|
-
if (hasHeaderGetter(headers)) {
|
|
1796
|
-
const value = headers.get(name);
|
|
1797
|
-
return value === null || value === "" ? void 0 : value;
|
|
1798
|
-
}
|
|
1799
|
-
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];
|
|
1800
|
-
return value === void 0 || value === "" ? void 0 : value;
|
|
1801
|
-
}
|
|
1802
|
-
/** Read a Retry-After header (delta-seconds or an HTTP date) as non-negative milliseconds. */
|
|
1803
|
-
function readRetryAfter(response) {
|
|
1804
|
-
const raw = readHeader(response, "retry-after");
|
|
1805
|
-
if (raw === void 0) return void 0;
|
|
1806
|
-
const seconds = Number(raw);
|
|
1807
|
-
if (Number.isFinite(seconds) && seconds >= 0) return seconds * 1e3;
|
|
1808
|
-
const timestamp = Date.parse(raw);
|
|
1809
|
-
return Number.isNaN(timestamp) ? void 0 : Math.max(0, timestamp - Date.now());
|
|
1810
|
-
}
|
|
1811
|
-
/** Read a non-negative integer block number header. */
|
|
1812
|
-
function readBlockNumberHeader(response, headerName = "x-block-number") {
|
|
1813
|
-
const raw = readHeader(response, headerName);
|
|
1814
|
-
if (raw === void 0) return void 0;
|
|
1815
|
-
const blockNumber = Number(raw);
|
|
1816
|
-
return Number.isInteger(blockNumber) && blockNumber >= 0 ? blockNumber : void 0;
|
|
1817
|
-
}
|
|
1818
|
-
function hasHeaderGetter(headers) {
|
|
1819
|
-
return "get" in headers && typeof headers.get === "function";
|
|
1820
|
-
}
|
|
1821
1107
|
|
|
1822
1108
|
//#endregion
|
|
1823
1109
|
//#region src/classes/apply_results.ts
|
|
@@ -1876,8 +1162,8 @@ var AppliedTradeError = class extends GSwapSDKError {
|
|
|
1876
1162
|
*/
|
|
1877
1163
|
async function confirmAppliedTrade(client, input, options) {
|
|
1878
1164
|
const { uniqueKey } = input;
|
|
1879
|
-
if (!isValidUniqueKey(uniqueKey)) throw new AppliedTradeError("INVALID_UNIQUE_KEY", { uniqueKey: String(uniqueKey) }, { uniqueKey });
|
|
1880
|
-
const requestor = client.httpRequestor ?? fetchRequestor;
|
|
1165
|
+
if (!(0, _gala_chain_gala_core_sdk.isValidUniqueKey)(uniqueKey)) throw new AppliedTradeError("INVALID_UNIQUE_KEY", { uniqueKey: String(uniqueKey) }, { uniqueKey });
|
|
1166
|
+
const requestor = client.httpRequestor ?? _gala_chain_gala_core_sdk.fetchRequestor;
|
|
1881
1167
|
const requestTimeoutMs = client.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
1882
1168
|
const signal = options === null || options === void 0 ? void 0 : options.signal;
|
|
1883
1169
|
const abortError = (cause) => new AppliedTradeError("CONFIRMATION_ABORTED", {}, {
|
|
@@ -1893,12 +1179,12 @@ async function confirmAppliedTrade(client, input, options) {
|
|
|
1893
1179
|
let response;
|
|
1894
1180
|
let body;
|
|
1895
1181
|
try {
|
|
1896
|
-
response = await requestWithTimeout(requestor, url, {
|
|
1182
|
+
response = await (0, _gala_chain_gala_core_sdk.requestWithTimeout)(requestor, url, {
|
|
1897
1183
|
method: "GET",
|
|
1898
|
-
headers: buildRequestHeaders(client.wire),
|
|
1184
|
+
headers: (0, _gala_chain_gala_core_sdk.buildRequestHeaders)(client.wire),
|
|
1899
1185
|
...signal === void 0 ? {} : { signal }
|
|
1900
1186
|
}, requestTimeoutMs);
|
|
1901
|
-
body = await readResponseBody(response);
|
|
1187
|
+
body = await (0, _gala_chain_gala_core_sdk.readResponseBody)(response);
|
|
1902
1188
|
} catch (error) {
|
|
1903
1189
|
throwIfAborted(signal, abortError);
|
|
1904
1190
|
lastError = error;
|
|
@@ -1908,7 +1194,7 @@ async function confirmAppliedTrade(client, input, options) {
|
|
|
1908
1194
|
throwIfAborted(signal, abortError);
|
|
1909
1195
|
if (response.status === 200) return readAppliedResult(input, response, body, url);
|
|
1910
1196
|
if (response.status === 404 || response.status === 429 || isTransient(response.status)) {
|
|
1911
|
-
const retryAfter = response.status === 429 ? readRetryAfter(response) : void 0;
|
|
1197
|
+
const retryAfter = response.status === 429 ? (0, _gala_chain_gala_core_sdk.readRetryAfter)(response) : void 0;
|
|
1912
1198
|
if (!await schedule.waitForRead(retryAfter)) break;
|
|
1913
1199
|
continue;
|
|
1914
1200
|
}
|
|
@@ -1940,8 +1226,8 @@ function readAppliedResult(input, response, body, url) {
|
|
|
1940
1226
|
url
|
|
1941
1227
|
});
|
|
1942
1228
|
const status = record === null || record === void 0 ? void 0 : record["Status"];
|
|
1943
|
-
const applyTransactionId = readHeader(response, "x-apply-transaction-id") ?? null;
|
|
1944
|
-
const blockNumber = readBlockNumberHeader(response, "x-apply-block-number") ?? null;
|
|
1229
|
+
const applyTransactionId = (0, _gala_chain_gala_core_sdk.readHeader)(response, "x-apply-transaction-id") ?? null;
|
|
1230
|
+
const blockNumber = (0, _gala_chain_gala_core_sdk.readBlockNumberHeader)(response, "x-apply-block-number") ?? null;
|
|
1945
1231
|
if (status === 0) {
|
|
1946
1232
|
const errorKey = getStringProperty(record, "ErrorKey") ?? "CHAIN_ERROR";
|
|
1947
1233
|
const chainMessage = getStringProperty(record, "Message") ?? "";
|
|
@@ -1958,9 +1244,9 @@ function readAppliedResult(input, response, body, url) {
|
|
|
1958
1244
|
url
|
|
1959
1245
|
});
|
|
1960
1246
|
}
|
|
1961
|
-
if (status !== 1) throw malformed
|
|
1247
|
+
if (status !== 1) throw malformed(input, response, body, url);
|
|
1962
1248
|
const result = parseTradeExecution(record === null || record === void 0 ? void 0 : record["Data"]);
|
|
1963
|
-
if (result === void 0) throw malformed
|
|
1249
|
+
if (result === void 0) throw malformed(input, response, body, url);
|
|
1964
1250
|
return {
|
|
1965
1251
|
uniqueKey,
|
|
1966
1252
|
transactionId: applyTransactionId,
|
|
@@ -1969,7 +1255,7 @@ function readAppliedResult(input, response, body, url) {
|
|
|
1969
1255
|
result
|
|
1970
1256
|
};
|
|
1971
1257
|
}
|
|
1972
|
-
function malformed
|
|
1258
|
+
function malformed(input, response, body, url) {
|
|
1973
1259
|
return new AppliedTradeError("CONFIRMATION_MALFORMED", {}, {
|
|
1974
1260
|
uniqueKey: input.uniqueKey,
|
|
1975
1261
|
status: response.status,
|
|
@@ -2369,7 +1655,10 @@ var Assets = class {
|
|
|
2369
1655
|
function parseUserAssets(value) {
|
|
2370
1656
|
const data = getObjectProperty(value, "data");
|
|
2371
1657
|
const tokens = data === null || data === void 0 ? void 0 : data["token"];
|
|
2372
|
-
if (!Array.isArray(tokens) || !tokens.every(isRawUserAsset)) throw new GSwapSDKError("INVALID_CHAIN_RESPONSE", { resource: "user assets response" }, {
|
|
1658
|
+
if (!Array.isArray(tokens) || !tokens.every(isRawUserAsset)) throw new GSwapSDKError("INVALID_CHAIN_RESPONSE", { resource: "user assets response" }, {
|
|
1659
|
+
body: value,
|
|
1660
|
+
write: false
|
|
1661
|
+
});
|
|
2373
1662
|
const count = data === null || data === void 0 ? void 0 : data["count"];
|
|
2374
1663
|
return {
|
|
2375
1664
|
tokens: tokens.map((token) => ({
|
|
@@ -2385,178 +1674,6 @@ function isRawUserAsset(value) {
|
|
|
2385
1674
|
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";
|
|
2386
1675
|
}
|
|
2387
1676
|
|
|
2388
|
-
//#endregion
|
|
2389
|
-
//#region src/utils/wallet_rejection.ts
|
|
2390
|
-
/**
|
|
2391
|
-
* What the SDK knows about a failure thrown by a WALLET (the signer an app supplies, or wallet
|
|
2392
|
-
* code of the app's own): the code the wallet put on it, and whether it means the person declined.
|
|
2393
|
-
* Both read only the wallet's own error value; neither shows or forwards its text.
|
|
2394
|
-
*/
|
|
2395
|
-
/** Codes the EIP-1193 / ethers / viem families use for "the person declined in the wallet". */
|
|
2396
|
-
const REJECTION_CODES = [
|
|
2397
|
-
4001,
|
|
2398
|
-
"ACTION_REJECTED",
|
|
2399
|
-
"USER_REJECTED"
|
|
2400
|
-
];
|
|
2401
|
-
/** The `name` viem / wagmi give the error of a declined request. */
|
|
2402
|
-
const REJECTION_NAME = "UserRejectedRequestError";
|
|
2403
|
-
/**
|
|
2404
|
-
* Wordings of a decline, matched case-insensitively as substrings. This is a LAST RESORT for
|
|
2405
|
-
* wallets that set neither a `code` nor a `name`: it reads a third party's text, so it is broad on
|
|
2406
|
-
* purpose (a superset of the matchers the Gala wallet and frontend_v2 used) and can in principle
|
|
2407
|
-
* match an unrelated message that uses these words.
|
|
2408
|
-
*/
|
|
2409
|
-
const REJECTION_PHRASES = [
|
|
2410
|
-
"user rejected",
|
|
2411
|
-
"user denied",
|
|
2412
|
-
"user cancel",
|
|
2413
|
-
"rejected the request",
|
|
2414
|
-
"request rejected",
|
|
2415
|
-
"rejected by user",
|
|
2416
|
-
"action_rejected",
|
|
2417
|
-
"cancelled",
|
|
2418
|
-
"canceled"
|
|
2419
|
-
];
|
|
2420
|
-
/** Properties wallets nest their text under (EIP-1193, ethers, viem, Gala wallet). */
|
|
2421
|
-
const TEXT_KEYS = [
|
|
2422
|
-
"message",
|
|
2423
|
-
"Message",
|
|
2424
|
-
"shortMessage",
|
|
2425
|
-
"details",
|
|
2426
|
-
"errorDetails",
|
|
2427
|
-
"reason"
|
|
2428
|
-
];
|
|
2429
|
-
/** Wallet errors nest their text at most a level or two deep; the bound also stops a cyclic object. */
|
|
2430
|
-
const MAX_TEXT_DEPTH = 3;
|
|
2431
|
-
function isRecord$2(value) {
|
|
2432
|
-
return typeof value === "object" && value !== null;
|
|
2433
|
-
}
|
|
2434
|
-
function textsOf(value, depth = 0) {
|
|
2435
|
-
if (typeof value === "string") return [value];
|
|
2436
|
-
if (!isRecord$2(value) || depth >= MAX_TEXT_DEPTH) return [];
|
|
2437
|
-
return TEXT_KEYS.flatMap((key) => textsOf(value[key], depth + 1));
|
|
2438
|
-
}
|
|
2439
|
-
function wordsADecline(text) {
|
|
2440
|
-
const lower = text.toLowerCase();
|
|
2441
|
-
return REJECTION_PHRASES.some((phrase) => lower.includes(phrase)) || lower.includes("signature") && lower.includes("cancel");
|
|
2442
|
-
}
|
|
2443
|
-
/**
|
|
2444
|
-
* True when a wallet's error (or just its message text) says the person DECLINED the request in
|
|
2445
|
-
* their wallet. This is exactly the test behind `SIGNING_FAILED` with `params.reason ===
|
|
2446
|
-
* 'user_rejected'`, exported for wallet code outside the SDK (connect and registration flows, a
|
|
2447
|
-
* v1 write path) so an app keeps no matcher of its own.
|
|
2448
|
-
*
|
|
2449
|
-
* It is decided from the wallet's own fields first: a `code` of `4001` (EIP-1193), `'ACTION_REJECTED'`
|
|
2450
|
-
* (ethers) or `'USER_REJECTED'`, or `name === 'UserRejectedRequestError'` (viem). Only when those are
|
|
2451
|
-
* absent is the wallet's TEXT read (`message`, `Message`, `shortMessage`, `details`, `errorDetails`,
|
|
2452
|
-
* `reason`, up to three levels deep, or a bare string): "user rejected", "user denied", "user
|
|
2453
|
-
* cancelled", "request rejected", "rejected by user", "rejected the request", "cancelled" /
|
|
2454
|
-
* "canceled", or a "signature ... cancel" sentence. The wording fallback is a last resort for
|
|
2455
|
-
* wallets that do not set codes; prefer `code` and `name` wherever you control the wallet.
|
|
2456
|
-
*
|
|
2457
|
-
* An SDK error is never read by its text: a `SIGNING_FAILED` is a decline exactly when its
|
|
2458
|
-
* `params.reason` is `user_rejected`, and every other SDK error is not one. The same holds for a
|
|
2459
|
-
* plain-object VIEW of an SDK error (`{ code: 'SIGNING_FAILED', params: { reason } }`, the shape
|
|
2460
|
-
* of an app's wrapper): a string `code` plus an object `params` is decided by the code alone, with
|
|
2461
|
-
* the wallet code list above still honoured for a view that is not branded, and its English is
|
|
2462
|
-
* never read. This works without `instanceof`, so it is safe across two copies of the SDK.
|
|
2463
|
-
*
|
|
2464
|
-
* @example
|
|
2465
|
-
* ```ts
|
|
2466
|
-
* try {
|
|
2467
|
-
* await wallet.connect();
|
|
2468
|
-
* } catch (error) {
|
|
2469
|
-
* if (isWalletUserRejection(error)) return; // the person said no: not an error to report
|
|
2470
|
-
* report(error);
|
|
2471
|
-
* }
|
|
2472
|
-
* ```
|
|
2473
|
-
*/
|
|
2474
|
-
function isWalletUserRejection(value) {
|
|
2475
|
-
if (isGSwapErrorLike(value)) {
|
|
2476
|
-
if (value.code === "SIGNING_FAILED") return value.params["reason"] === "user_rejected";
|
|
2477
|
-
return !hasGSwapErrorBrand(value) && REJECTION_CODES.includes(value.code);
|
|
2478
|
-
}
|
|
2479
|
-
if (isRecord$2(value)) {
|
|
2480
|
-
if (REJECTION_CODES.includes(value["code"]) || value["name"] === REJECTION_NAME) return true;
|
|
2481
|
-
}
|
|
2482
|
-
return textsOf(value).some(wordsADecline);
|
|
2483
|
-
}
|
|
2484
|
-
/** The longest wallet code kept: real ones (`4001`, `ACTION_REJECTED`) are short. */
|
|
2485
|
-
const MAX_WALLET_CODE_LENGTH = 64;
|
|
2486
|
-
/**
|
|
2487
|
-
* The code a wallet put on its error: an EIP-1193 / JSON-RPC number (`4001`, `-32603`) or an
|
|
2488
|
-
* ethers / viem string (`'ACTION_REJECTED'`), read from the error's own top-level `code`. Anything
|
|
2489
|
-
* else (no code, a non-finite number, an empty or very long string, an object) is `undefined`, so
|
|
2490
|
-
* the value is always a safe primitive. Used for `GSwapSDKError.walletCode`.
|
|
2491
|
-
*
|
|
2492
|
-
* @example
|
|
2493
|
-
* ```ts
|
|
2494
|
-
* walletDeclineCode({ code: 4001, message: 'User rejected the request.' }); // 4001
|
|
2495
|
-
* walletDeclineCode(new Error('locked')); // undefined
|
|
2496
|
-
* ```
|
|
2497
|
-
*/
|
|
2498
|
-
function walletDeclineCode(value) {
|
|
2499
|
-
if (typeof value !== "object" || value === null) return void 0;
|
|
2500
|
-
const { code } = value;
|
|
2501
|
-
if (typeof code === "number") return Number.isFinite(code) ? code : void 0;
|
|
2502
|
-
if (typeof code === "string" && code.length > 0 && code.length <= MAX_WALLET_CODE_LENGTH) return code;
|
|
2503
|
-
}
|
|
2504
|
-
|
|
2505
|
-
//#endregion
|
|
2506
|
-
//#region src/utils/pre_send.ts
|
|
2507
|
-
/**
|
|
2508
|
-
* The boundary of "nothing was sent". Every place where the SDK hands control to code the APP
|
|
2509
|
-
* supplied BEFORE the request leaves (the signer, the token resolver) goes through here, so a
|
|
2510
|
-
* failure of that code reaches the caller as a catalogued, pre-send SDK error with the original as
|
|
2511
|
-
* `cause`, never as a bare value the SDK cannot classify. An SDK error thrown by that code is
|
|
2512
|
-
* already catalogued and passes through untouched. Nothing here is used after the request has
|
|
2513
|
-
* been sent: an app's own rejection AFTER send stays what it is, because the SDK cannot know what
|
|
2514
|
-
* the server did with the write.
|
|
2515
|
-
*/
|
|
2516
|
-
/**
|
|
2517
|
-
* Signs a DTO with the app's signer. Whatever the signer throws that is not already an SDK error
|
|
2518
|
-
* (a locked wallet, an unreachable extension, a wallet that declined) is `SIGNING_FAILED`
|
|
2519
|
-
* (`reason` `user_rejected` or `failed`, the original as `cause`), and a signer that returns
|
|
2520
|
-
* something other than an object is `SIGNING_FAILED` too: both are pre-send, so a retry is safe.
|
|
2521
|
-
*
|
|
2522
|
-
* @throws GSwapSDKError SIGNING_FAILED, or the SDK error the signer itself threw.
|
|
2523
|
-
*/
|
|
2524
|
-
async function signPreSend(signer, method, dto) {
|
|
2525
|
-
let signed;
|
|
2526
|
-
try {
|
|
2527
|
-
signed = await signer.signObject(method, dto);
|
|
2528
|
-
} catch (cause) {
|
|
2529
|
-
if (isGSwapError(cause)) throw cause;
|
|
2530
|
-
const reason = isWalletUserRejection(cause) ? "user_rejected" : "failed";
|
|
2531
|
-
throw new GSwapSDKError("SIGNING_FAILED", {
|
|
2532
|
-
method,
|
|
2533
|
-
reason
|
|
2534
|
-
}, {
|
|
2535
|
-
cause,
|
|
2536
|
-
walletCode: walletDeclineCode(cause)
|
|
2537
|
-
});
|
|
2538
|
-
}
|
|
2539
|
-
if (typeof signed !== "object" || signed === null) throw new GSwapSDKError("SIGNING_FAILED", {
|
|
2540
|
-
method,
|
|
2541
|
-
reason: "failed"
|
|
2542
|
-
}, { cause: signed });
|
|
2543
|
-
return signed;
|
|
2544
|
-
}
|
|
2545
|
-
/**
|
|
2546
|
-
* Runs a token-resolution step (a resolver's `resolve` / `orderPair`, which may read the registry
|
|
2547
|
-
* through the app's HTTP stack). A failure that is not an SDK error is `TOKEN_RESOLUTION_FAILED`
|
|
2548
|
-
* (`token` names what was being resolved, the original is `cause`); an SDK error passes through.
|
|
2549
|
-
*
|
|
2550
|
-
* @throws GSwapSDKError TOKEN_RESOLUTION_FAILED, or the SDK error the step itself threw.
|
|
2551
|
-
*/
|
|
2552
|
-
async function resolvePreSend(token, run) {
|
|
2553
|
-
try {
|
|
2554
|
-
return await run();
|
|
2555
|
-
} catch (cause) {
|
|
2556
|
-
throw isGSwapError(cause) ? cause : new GSwapSDKError("TOKEN_RESOLUTION_FAILED", { token }, { cause });
|
|
2557
|
-
}
|
|
2558
|
-
}
|
|
2559
|
-
|
|
2560
1677
|
//#endregion
|
|
2561
1678
|
//#region src/classes/dex_fees.ts
|
|
2562
1679
|
/** DEX methods the backend accepts a fee quote for (its allow-list enumerates the same six). */
|
|
@@ -2603,11 +1720,11 @@ function toChainDto(message) {
|
|
|
2603
1720
|
* `uniqueKey`, distinct from the DTO's.
|
|
2604
1721
|
*/
|
|
2605
1722
|
function buildDryRunWrapper(signer, callerPublicKey, method, dto, uniqueKeyPrefix = DRY_RUN_UNIQUE_KEY_PREFIX) {
|
|
2606
|
-
return signPreSend(signer, DRY_RUN_METHOD, {
|
|
1723
|
+
return (0, _gala_chain_gala_core_sdk.signPreSend)(signer, DRY_RUN_METHOD, {
|
|
2607
1724
|
callerPublicKey,
|
|
2608
1725
|
method,
|
|
2609
1726
|
dto,
|
|
2610
|
-
uniqueKey:
|
|
1727
|
+
uniqueKey: (0, _gala_chain_gala_core_sdk.createUniqueKey)(uniqueKeyPrefix)
|
|
2611
1728
|
});
|
|
2612
1729
|
}
|
|
2613
1730
|
/**
|
|
@@ -2634,7 +1751,7 @@ var DexFees = class {
|
|
|
2634
1751
|
wire;
|
|
2635
1752
|
constructor(client, signer) {
|
|
2636
1753
|
this.signer = signer;
|
|
2637
|
-
this.requestor = client.httpRequestor ?? fetchRequestor;
|
|
1754
|
+
this.requestor = client.httpRequestor ?? _gala_chain_gala_core_sdk.fetchRequestor;
|
|
2638
1755
|
this.baseUrl = client.dexBackendBaseUrl.replace(/\/$/u, "");
|
|
2639
1756
|
this.requestTimeoutMs = client.requestTimeoutMs ?? 3e4;
|
|
2640
1757
|
this.wire = client.wire;
|
|
@@ -2664,9 +1781,9 @@ var DexFees = class {
|
|
|
2664
1781
|
let ok;
|
|
2665
1782
|
let body;
|
|
2666
1783
|
try {
|
|
2667
|
-
const response = await requestWithTimeout(this.requestor, url, {
|
|
1784
|
+
const response = await (0, _gala_chain_gala_core_sdk.requestWithTimeout)(this.requestor, url, {
|
|
2668
1785
|
method: "POST",
|
|
2669
|
-
headers: buildRequestHeaders(this.wire, {
|
|
1786
|
+
headers: (0, _gala_chain_gala_core_sdk.buildRequestHeaders)(this.wire, {
|
|
2670
1787
|
json: true,
|
|
2671
1788
|
defaults: { Accept: "application/json" }
|
|
2672
1789
|
}),
|
|
@@ -2674,7 +1791,7 @@ var DexFees = class {
|
|
|
2674
1791
|
}, this.requestTimeoutMs);
|
|
2675
1792
|
status = response.status;
|
|
2676
1793
|
ok = response.ok;
|
|
2677
|
-
body = ok ? await readResponseBody(response) : void 0;
|
|
1794
|
+
body = ok ? await (0, _gala_chain_gala_core_sdk.readResponseBody)(response) : void 0;
|
|
2678
1795
|
} catch {
|
|
2679
1796
|
return null;
|
|
2680
1797
|
}
|
|
@@ -2725,13 +1842,15 @@ const shadowRejections = /* @__PURE__ */ new WeakSet();
|
|
|
2725
1842
|
function isShadowError(value) {
|
|
2726
1843
|
var _toFailureView;
|
|
2727
1844
|
if (isGSwapError(value)) return value.shadow;
|
|
2728
|
-
if (((_toFailureView = toFailureView(value)) === null || _toFailureView === void 0 ? void 0 : _toFailureView.shadow) === true) return true;
|
|
1845
|
+
if (((_toFailureView = (0, _gala_chain_gala_core_sdk.toFailureView)(value)) === null || _toFailureView === void 0 ? void 0 : _toFailureView.shadow) === true) return true;
|
|
2729
1846
|
return typeof value === "object" && value !== null && shadowRejections.has(value);
|
|
2730
1847
|
}
|
|
2731
1848
|
/**
|
|
2732
1849
|
* The same failure, marked as shadow. An SDK error is COPIED (same code, params, status, body,
|
|
2733
|
-
* url, ids, cause, phase and stack), never mutated: one error object can
|
|
2734
|
-
* callers (a registry read many swaps wait on), and only the shadow caller's
|
|
1850
|
+
* url, ids, cause, phase, read/write, wallet code and stack), never mutated: one error object can
|
|
1851
|
+
* be shared by several callers (a registry read many swaps wait on), and only the shadow caller's
|
|
1852
|
+
* copy is shadow. Everything the deciders read is carried over, so the copy classifies exactly as
|
|
1853
|
+
* the original (a failed READ stays a read, never a "maybe queued" write).
|
|
2735
1854
|
*/
|
|
2736
1855
|
function asShadowError(error) {
|
|
2737
1856
|
if (isGSwapError(error)) {
|
|
@@ -2747,6 +1866,8 @@ function asShadowError(error) {
|
|
|
2747
1866
|
retryAfterMs: error.retryAfterMs,
|
|
2748
1867
|
cause: error.cause,
|
|
2749
1868
|
phase: error.phase,
|
|
1869
|
+
write: error.write,
|
|
1870
|
+
walletCode: error.walletCode,
|
|
2750
1871
|
shadow: true
|
|
2751
1872
|
});
|
|
2752
1873
|
if (error.stack !== void 0) copy.stack = error.stack;
|
|
@@ -2829,6 +1950,12 @@ var SubmittedTransaction = class {
|
|
|
2829
1950
|
* For RequestTrade, calling this method is required to learn the outcome: the queued
|
|
2830
1951
|
* acknowledgement proves acceptance, not execution.
|
|
2831
1952
|
*
|
|
1953
|
+
* Every failure is the write's (it carries `uniqueKey`, so `mayHaveBeenQueued` is true): a
|
|
1954
|
+
* re-read that cannot succeed is `CONFIRMATION_FAILED` (a terminal HTTP status) or
|
|
1955
|
+
* `CONFIRMATION_MALFORMED` (an unusable answer); one that may (no answer, a 5xx, 404, 408,
|
|
1956
|
+
* 429) is read again until the deadline, then `CONFIRMATION_TIMEOUT` with the last read error
|
|
1957
|
+
* as `cause`. Confirm the same transaction again; never resubmit the write.
|
|
1958
|
+
*
|
|
2832
1959
|
* @example
|
|
2833
1960
|
* ```ts
|
|
2834
1961
|
* const applied = await submitted.confirm({ initialDelayMs: 3_000 });
|
|
@@ -2866,9 +1993,10 @@ var SubmittedTransaction = class {
|
|
|
2866
1993
|
});
|
|
2867
1994
|
const schedule = createConfirmationSchedule(options, 0, abortError);
|
|
2868
1995
|
await schedule.waitForRead();
|
|
1996
|
+
let lastError;
|
|
2869
1997
|
while (true) {
|
|
2870
1998
|
const controller = new AbortController();
|
|
2871
|
-
const disarm = armTimeout(controller, this.requestTimeoutMs);
|
|
1999
|
+
const disarm = (0, _gala_chain_gala_core_sdk.armTimeout)(controller, this.requestTimeoutMs);
|
|
2872
2000
|
const forwardAbort = () => controller.abort();
|
|
2873
2001
|
callerSignal === null || callerSignal === void 0 || callerSignal.addEventListener("abort", forwardAbort, { once: true });
|
|
2874
2002
|
try {
|
|
@@ -2876,27 +2004,60 @@ var SubmittedTransaction = class {
|
|
|
2876
2004
|
if (result.confirmed) return result.position;
|
|
2877
2005
|
} catch (error) {
|
|
2878
2006
|
throwIfAborted(callerSignal, abortError);
|
|
2879
|
-
|
|
2007
|
+
const terminal = terminalPositionReadFailure(this.uniqueKey, error);
|
|
2008
|
+
if (terminal !== void 0) throw terminal;
|
|
2009
|
+
lastError = error;
|
|
2880
2010
|
} finally {
|
|
2881
2011
|
disarm();
|
|
2882
2012
|
callerSignal === null || callerSignal === void 0 || callerSignal.removeEventListener("abort", forwardAbort);
|
|
2883
2013
|
}
|
|
2884
2014
|
if (!await schedule.waitForRead()) break;
|
|
2885
2015
|
}
|
|
2886
|
-
throw GSwapSDKError.confirmationTimeoutError(this.uniqueKey);
|
|
2016
|
+
throw GSwapSDKError.confirmationTimeoutError(this.uniqueKey, lastError);
|
|
2887
2017
|
}
|
|
2888
2018
|
};
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2019
|
+
/** HTTP answers a confirmation reads again (as the apply-results poll does), not terminal ones. */
|
|
2020
|
+
function isRetriedStatus(status) {
|
|
2021
|
+
return status === 404 || status === 408 || status === 429 || status >= 500;
|
|
2022
|
+
}
|
|
2023
|
+
/**
|
|
2024
|
+
* The confirmation error for a position re-read that failed TERMINALLY (an answer that reading
|
|
2025
|
+
* again cannot change), or `undefined` when it is worth reading again.
|
|
2026
|
+
*/
|
|
2027
|
+
function terminalPositionReadFailure(uniqueKey, error) {
|
|
2028
|
+
if (!isGSwapError(error)) return void 0;
|
|
2029
|
+
if (error.code === "INVALID_POSITION_RESPONSE" || error.code === "INVALID_CHAIN_RESPONSE") return new GSwapSDKError("CONFIRMATION_MALFORMED", {}, {
|
|
2030
|
+
uniqueKey,
|
|
2031
|
+
url: error.url,
|
|
2032
|
+
body: error.body ?? error.bodyText,
|
|
2033
|
+
cause: error
|
|
2034
|
+
});
|
|
2035
|
+
const status = error.status;
|
|
2036
|
+
if ((error.code === "HTTP_ERROR" || error.code === "UPDATE_REQUIRED") && status !== void 0 && !isRetriedStatus(status)) {
|
|
2037
|
+
const serverMessage = error.params["serverMessage"];
|
|
2038
|
+
return new GSwapSDKError("CONFIRMATION_FAILED", {
|
|
2039
|
+
status,
|
|
2040
|
+
serverMessage: typeof serverMessage === "string" ? serverMessage : ""
|
|
2041
|
+
}, {
|
|
2042
|
+
uniqueKey,
|
|
2043
|
+
status,
|
|
2044
|
+
url: error.url,
|
|
2045
|
+
body: error.body ?? error.bodyText,
|
|
2046
|
+
cause: error
|
|
2047
|
+
});
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
|
|
2051
|
+
//#endregion
|
|
2052
|
+
//#region src/classes/gateway.ts
|
|
2053
|
+
/** Thin client for backend-routed v2 writes: synchronous liquidity writes and queued RequestTrade. */
|
|
2054
|
+
var ChainGateway = class {
|
|
2055
|
+
requestor;
|
|
2056
|
+
walletAddress;
|
|
2057
|
+
chainCallTimeoutMs;
|
|
2058
|
+
isTransportFailure;
|
|
2059
|
+
answerOfRejectionHook;
|
|
2060
|
+
/** The request timeout in milliseconds (`Infinity` when no timer is armed). */
|
|
2900
2061
|
requestTimeoutMs;
|
|
2901
2062
|
/** The swap backend base URL every request is sent to, without a trailing slash. */
|
|
2902
2063
|
dexBackendBaseUrl;
|
|
@@ -2905,9 +2066,9 @@ var ChainGateway = class {
|
|
|
2905
2066
|
/** Create a chain gateway client. */
|
|
2906
2067
|
constructor(options) {
|
|
2907
2068
|
this.dexBackendBaseUrl = trimTrailingSlash$1(options.dexBackendBaseUrl);
|
|
2908
|
-
this.requestor = options.httpRequestor ?? fetchRequestor;
|
|
2069
|
+
this.requestor = options.httpRequestor ?? _gala_chain_gala_core_sdk.fetchRequestor;
|
|
2909
2070
|
this.walletAddress = options.walletAddress;
|
|
2910
|
-
this.chainCallTimeoutMs = validateTimeoutMs(options.chainCallTimeoutMs ?? 3e4, "chainCallTimeoutMs");
|
|
2071
|
+
this.chainCallTimeoutMs = (0, _gala_chain_gala_core_sdk.validateTimeoutMs)(options.chainCallTimeoutMs ?? 3e4, "chainCallTimeoutMs");
|
|
2911
2072
|
this.requestTimeoutMs = this.chainCallTimeoutMs;
|
|
2912
2073
|
this.wire = options.wire ?? {};
|
|
2913
2074
|
this.isTransportFailure = options.isTransportFailure;
|
|
@@ -2917,7 +2078,7 @@ var ChainGateway = class {
|
|
|
2917
2078
|
if (method === "Trade") throw new GSwapSDKError("METHOD_NOT_SUPPORTED", { method });
|
|
2918
2079
|
const url = this.writeUrl(method);
|
|
2919
2080
|
const walletAddress = (options === null || options === void 0 ? void 0 : options.walletAddress) ?? this.walletAddress;
|
|
2920
|
-
const headers = buildRequestHeaders(this.wire, {
|
|
2081
|
+
const headers = (0, _gala_chain_gala_core_sdk.buildRequestHeaders)(this.wire, {
|
|
2921
2082
|
json: true,
|
|
2922
2083
|
fixed: walletAddress === void 0 ? {} : { "X-Wallet-Address": walletAddress }
|
|
2923
2084
|
});
|
|
@@ -2946,9 +2107,9 @@ var ChainGateway = class {
|
|
|
2946
2107
|
if (resultStatus !== void 0 && resultStatus !== 1) throw GSwapSDKError.fromChainError(getStringProperty(embeddedResult, "ErrorKey") ?? "CHAIN_ERROR", getStringProperty(embeddedResult, "Message") ?? "", context);
|
|
2947
2108
|
}
|
|
2948
2109
|
}
|
|
2949
|
-
const headerTransactionId = readHeader(response, "x-transaction-id");
|
|
2110
|
+
const headerTransactionId = (0, _gala_chain_gala_core_sdk.readHeader)(response, "x-transaction-id");
|
|
2950
2111
|
const resolvedTransactionId = transactionId === void 0 || transactionId === "" ? headerTransactionId : transactionId;
|
|
2951
|
-
const blockNumber = bodyBlockNumber === null || bodyBlockNumber === void 0 ? readBlockNumberHeader(response) : isBlockNumber(bodyBlockNumber) ? bodyBlockNumber : void 0;
|
|
2112
|
+
const blockNumber = bodyBlockNumber === null || bodyBlockNumber === void 0 ? (0, _gala_chain_gala_core_sdk.readBlockNumberHeader)(response) : isBlockNumber(bodyBlockNumber) ? bodyBlockNumber : void 0;
|
|
2952
2113
|
return new SubmittedTransaction({
|
|
2953
2114
|
method,
|
|
2954
2115
|
uniqueKey,
|
|
@@ -2991,7 +2152,7 @@ var ChainGateway = class {
|
|
|
2991
2152
|
if (method === "Trade") throw new GSwapSDKError("METHOD_NOT_SUPPORTED", { method });
|
|
2992
2153
|
const walletAddress = (options === null || options === void 0 ? void 0 : options.walletAddress) ?? this.walletAddress;
|
|
2993
2154
|
if (walletAddress === void 0) throw new GSwapSDKError("SHADOW_SUBMIT_NEEDS_WALLET_ADDRESS", {});
|
|
2994
|
-
const headers = buildRequestHeaders(this.wire, {
|
|
2155
|
+
const headers = (0, _gala_chain_gala_core_sdk.buildRequestHeaders)(this.wire, {
|
|
2995
2156
|
json: true,
|
|
2996
2157
|
fixed: {
|
|
2997
2158
|
"X-Wallet-Address": walletAddress,
|
|
@@ -3030,7 +2191,7 @@ var ChainGateway = class {
|
|
|
3030
2191
|
});
|
|
3031
2192
|
let response;
|
|
3032
2193
|
try {
|
|
3033
|
-
response = await requestWithTimeout(this.requestor, url, {
|
|
2194
|
+
response = await (0, _gala_chain_gala_core_sdk.requestWithTimeout)(this.requestor, url, {
|
|
3034
2195
|
method: "POST",
|
|
3035
2196
|
headers,
|
|
3036
2197
|
body: payload
|
|
@@ -3045,7 +2206,7 @@ var ChainGateway = class {
|
|
|
3045
2206
|
try {
|
|
3046
2207
|
return {
|
|
3047
2208
|
response,
|
|
3048
|
-
body: await readResponseBody(response)
|
|
2209
|
+
body: await (0, _gala_chain_gala_core_sdk.readResponseBody)(response)
|
|
3049
2210
|
};
|
|
3050
2211
|
} catch (error) {
|
|
3051
2212
|
throw outcomeUnknown("transport", error);
|
|
@@ -3130,7 +2291,7 @@ function gatewayError(response, body, url, uniqueKey) {
|
|
|
3130
2291
|
const nested = getObjectProperty(body, "error");
|
|
3131
2292
|
const code = getStringProperty(object, "code") ?? getStringProperty(nested, "code") ?? getStringProperty(nested, "ErrorKey") ?? "HTTP_ERROR";
|
|
3132
2293
|
const message = serverMessageOfBody(body);
|
|
3133
|
-
const retryAfterMs = response.status === 429 ? readRetryAfter(response) : void 0;
|
|
2294
|
+
const retryAfterMs = response.status === 429 ? (0, _gala_chain_gala_core_sdk.readRetryAfter)(response) : void 0;
|
|
3134
2295
|
if (code === "HTTP_ERROR") return new GSwapSDKError("HTTP_ERROR", {
|
|
3135
2296
|
status: response.status,
|
|
3136
2297
|
url,
|
|
@@ -3623,7 +2784,7 @@ var HttpClient = class {
|
|
|
3623
2784
|
* Create an HTTP client around fetch or an injected requestor. `wire` selects the query
|
|
3624
2785
|
* encoding and the `User-Agent` (see {@link WireOptions}).
|
|
3625
2786
|
*/
|
|
3626
|
-
constructor(httpRequestor = fetchRequestor, timeoutMs = 3e4, wire = {}) {
|
|
2787
|
+
constructor(httpRequestor = _gala_chain_gala_core_sdk.fetchRequestor, timeoutMs = 3e4, wire = {}) {
|
|
3627
2788
|
this.httpRequestor = httpRequestor;
|
|
3628
2789
|
this.timeoutMs = timeoutMs;
|
|
3629
2790
|
this.wire = wire;
|
|
@@ -3632,14 +2793,14 @@ var HttpClient = class {
|
|
|
3632
2793
|
const url = `${baseUrl}${basePath}${endpoint}`;
|
|
3633
2794
|
debugLog(`Sending request to ${url} with body:`, body);
|
|
3634
2795
|
const hasBody = Boolean(body);
|
|
3635
|
-
const response = await requestWithTimeout(this.httpRequestor, url, {
|
|
2796
|
+
const response = await (0, _gala_chain_gala_core_sdk.requestWithTimeout)(this.httpRequestor, url, {
|
|
3636
2797
|
method,
|
|
3637
|
-
headers: buildRequestHeaders(this.wire, { json: hasBody }),
|
|
2798
|
+
headers: (0, _gala_chain_gala_core_sdk.buildRequestHeaders)(this.wire, { json: hasBody }),
|
|
3638
2799
|
...hasBody ? { body: JSON.stringify(body) } : {},
|
|
3639
2800
|
...(options === null || options === void 0 ? void 0 : options.signal) === void 0 ? {} : { signal: options.signal }
|
|
3640
|
-
}, this.timeoutMs);
|
|
2801
|
+
}, this.timeoutMs, { write: false });
|
|
3641
2802
|
if (!response.ok) throw await GSwapSDKError.fromErrorResponse(url, response);
|
|
3642
|
-
const json = await readResponseBody(response);
|
|
2803
|
+
const json = await (0, _gala_chain_gala_core_sdk.readResponseBody)(response);
|
|
3643
2804
|
debugLog(`Response from ${baseUrl}${basePath}${endpoint}:`, json);
|
|
3644
2805
|
return json;
|
|
3645
2806
|
}
|
|
@@ -3649,7 +2810,7 @@ var HttpClient = class {
|
|
|
3649
2810
|
}
|
|
3650
2811
|
/** Send a GET request with URL-encoded query parameters and decode its response. */
|
|
3651
2812
|
async sendGetRequest(baseUrl, basePath, endpoint, params, options) {
|
|
3652
|
-
const endpointWithParams = params ? `${endpoint}?${buildQueryString(params, this.wire.queryEncoding)}` : endpoint;
|
|
2813
|
+
const endpointWithParams = params ? `${endpoint}?${(0, _gala_chain_gala_core_sdk.buildQueryString)(params, this.wire.queryEncoding)}` : endpoint;
|
|
3653
2814
|
return this.sendRequest("GET", baseUrl, basePath, endpointWithParams, void 0, options);
|
|
3654
2815
|
}
|
|
3655
2816
|
};
|
|
@@ -3673,10 +2834,13 @@ function entryRejected(reason) {
|
|
|
3673
2834
|
/**
|
|
3674
2835
|
* Reads a list answer row by row: a row that cannot be used is skipped and reported, instead of
|
|
3675
2836
|
* rejecting the whole list because of one bad row. An answer that is not a list at all is still
|
|
3676
|
-
* `INVALID_CHAIN_RESPONSE
|
|
2837
|
+
* `INVALID_CHAIN_RESPONSE` (a read's: `write: false`): nothing in it can be trusted.
|
|
3677
2838
|
*/
|
|
3678
2839
|
function readEntries(source, data, parse, onInvalidEntry) {
|
|
3679
|
-
if (!Array.isArray(data)) throw new GSwapSDKError("INVALID_CHAIN_RESPONSE", { resource: `${source} list` }, {
|
|
2840
|
+
if (!Array.isArray(data)) throw new GSwapSDKError("INVALID_CHAIN_RESPONSE", { resource: `${source} list` }, {
|
|
2841
|
+
body: data,
|
|
2842
|
+
write: false
|
|
2843
|
+
});
|
|
3680
2844
|
const rows = [];
|
|
3681
2845
|
data.forEach((entry, index) => {
|
|
3682
2846
|
const parsed = parse(entry);
|
|
@@ -3709,97 +2873,462 @@ function finiteNumberOf(value) {
|
|
|
3709
2873
|
}
|
|
3710
2874
|
|
|
3711
2875
|
//#endregion
|
|
3712
|
-
//#region src/
|
|
2876
|
+
//#region src/classes/leaderboard.ts
|
|
3713
2877
|
/**
|
|
3714
|
-
*
|
|
3715
|
-
*
|
|
3716
|
-
*
|
|
3717
|
-
*
|
|
2878
|
+
* The swap backend's trading-competition reads: seasons (`GET /leaderboard/seasons`) and a
|
|
2879
|
+
* season's rankings (`GET /leaderboard?seasonId=`). Public reads: no signer, no wallet. A row that
|
|
2880
|
+
* is not usable is skipped and reported to `onInvalidEntry`; an answer that is not the expected
|
|
2881
|
+
* list is `INVALID_CHAIN_RESPONSE`.
|
|
3718
2882
|
*/
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
2883
|
+
var Leaderboard = class {
|
|
2884
|
+
dexBackendBaseUrl;
|
|
2885
|
+
httpClient;
|
|
2886
|
+
options;
|
|
2887
|
+
/**
|
|
2888
|
+
* Creates a leaderboard service. `options.onInvalidEntry` is told about every season or entry
|
|
2889
|
+
* row that was skipped as unusable.
|
|
2890
|
+
*
|
|
2891
|
+
* @example
|
|
2892
|
+
* ```ts
|
|
2893
|
+
* const leaderboard = new Leaderboard('https://dex-backend-prod1.defi.gala.com', httpClient);
|
|
2894
|
+
* ```
|
|
2895
|
+
*/
|
|
2896
|
+
constructor(dexBackendBaseUrl, httpClient, options = {}) {
|
|
2897
|
+
this.dexBackendBaseUrl = dexBackendBaseUrl;
|
|
2898
|
+
this.httpClient = httpClient;
|
|
2899
|
+
this.options = options;
|
|
2900
|
+
}
|
|
2901
|
+
/**
|
|
2902
|
+
* Every active season, in the backend's order (newest first).
|
|
2903
|
+
*
|
|
2904
|
+
* @example
|
|
2905
|
+
* ```ts
|
|
2906
|
+
* const seasons = await gSwap.leaderboard.getSeasons();
|
|
2907
|
+
* console.log(seasons.map((season) => season.name));
|
|
2908
|
+
* ```
|
|
2909
|
+
*/
|
|
2910
|
+
async getSeasons(options) {
|
|
2911
|
+
const response = await this.httpClient.sendGetRequest(this.dexBackendBaseUrl, "/leaderboard", "/seasons", void 0, signalOption$1(options));
|
|
2912
|
+
return readEntries("seasons", response, parseSeason, (options === null || options === void 0 ? void 0 : options.onInvalidEntry) ?? this.options.onInvalidEntry);
|
|
2913
|
+
}
|
|
2914
|
+
/**
|
|
2915
|
+
* The season running at `options.now` (default: now; `start` and `end` both count as inside),
|
|
2916
|
+
* or `null` between seasons. When seasons overlap, the first in the backend's order.
|
|
2917
|
+
*
|
|
2918
|
+
* @example
|
|
2919
|
+
* ```ts
|
|
2920
|
+
* const season = await gSwap.leaderboard.getCurrentSeason();
|
|
2921
|
+
* console.log(season === null ? 'no season running' : season.name);
|
|
2922
|
+
* ```
|
|
2923
|
+
*/
|
|
2924
|
+
async getCurrentSeason(options) {
|
|
2925
|
+
const now = ((options === null || options === void 0 ? void 0 : options.now) ?? /* @__PURE__ */ new Date()).getTime();
|
|
2926
|
+
return (await this.getSeasons(options)).find((season) => season.start.getTime() <= now && now <= season.end.getTime()) ?? null;
|
|
2927
|
+
}
|
|
2928
|
+
/**
|
|
2929
|
+
* A season's rankings (the backend's top 100 wallets). `seasonId` must be an integer of at
|
|
2930
|
+
* least 1 (`INVALID_SEASON_ID` before anything is sent); a season with no rankings, or none at
|
|
2931
|
+
* all, is an empty list.
|
|
2932
|
+
*
|
|
2933
|
+
* @example
|
|
2934
|
+
* ```ts
|
|
2935
|
+
* const { entries } = await gSwap.leaderboard.getLeaderboard(1);
|
|
2936
|
+
* console.log(entries[0]?.wallet, entries[0]?.totalXp);
|
|
2937
|
+
* ```
|
|
2938
|
+
*/
|
|
2939
|
+
async getLeaderboard(seasonId, options) {
|
|
2940
|
+
var _asEntryRecord;
|
|
2941
|
+
if (typeof seasonId !== "number" || !Number.isSafeInteger(seasonId) || seasonId < 1) throw new GSwapSDKError("INVALID_SEASON_ID", { value: String(seasonId) });
|
|
2942
|
+
const response = await this.httpClient.sendGetRequest(this.dexBackendBaseUrl, "/leaderboard", "", { seasonId: String(seasonId) }, signalOption$1(options));
|
|
2943
|
+
const rows = (_asEntryRecord = asEntryRecord(response)) === null || _asEntryRecord === void 0 ? void 0 : _asEntryRecord["leaderboard"];
|
|
2944
|
+
if (!Array.isArray(rows)) throw new GSwapSDKError("INVALID_CHAIN_RESPONSE", { resource: "leaderboard" }, {
|
|
2945
|
+
body: response,
|
|
2946
|
+
write: false
|
|
2947
|
+
});
|
|
2948
|
+
return {
|
|
2949
|
+
seasonId,
|
|
2950
|
+
entries: readEntries("leaderboard", rows, parseEntry, (options === null || options === void 0 ? void 0 : options.onInvalidEntry) ?? this.options.onInvalidEntry)
|
|
2951
|
+
};
|
|
2952
|
+
}
|
|
2953
|
+
/**
|
|
2954
|
+
* The rankings of the season running at `options.now` (see
|
|
2955
|
+
* {@link Leaderboard.getCurrentSeason}), or `null` between seasons.
|
|
2956
|
+
*
|
|
2957
|
+
* @example
|
|
2958
|
+
* ```ts
|
|
2959
|
+
* const current = await gSwap.leaderboard.getCurrentLeaderboard();
|
|
2960
|
+
* if (current !== null) console.log(current.seasonId, current.entries.length);
|
|
2961
|
+
* ```
|
|
2962
|
+
*/
|
|
2963
|
+
async getCurrentLeaderboard(options) {
|
|
2964
|
+
const season = await this.getCurrentSeason(options);
|
|
2965
|
+
return season === null ? null : this.getLeaderboard(season.id, options);
|
|
2966
|
+
}
|
|
2967
|
+
};
|
|
2968
|
+
function signalOption$1(options) {
|
|
2969
|
+
return (options === null || options === void 0 ? void 0 : options.signal) === void 0 ? void 0 : { signal: options.signal };
|
|
2970
|
+
}
|
|
2971
|
+
function positiveInteger(value) {
|
|
2972
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 1 ? value : void 0;
|
|
2973
|
+
}
|
|
2974
|
+
function isoDate(value) {
|
|
2975
|
+
if (typeof value !== "string") return void 0;
|
|
2976
|
+
const date = new Date(value);
|
|
2977
|
+
return Number.isNaN(date.getTime()) ? void 0 : date;
|
|
2978
|
+
}
|
|
2979
|
+
function parseSeason(entry) {
|
|
2980
|
+
const record = asEntryRecord(entry);
|
|
2981
|
+
if (record === void 0) return entryRejected("not an object");
|
|
2982
|
+
const id = positiveInteger(record["id"]);
|
|
2983
|
+
if (id === void 0) return entryRejected("id is not an integer of at least 1");
|
|
2984
|
+
const name = record["name"];
|
|
2985
|
+
if (typeof name !== "string") return entryRejected("name is not a string");
|
|
2986
|
+
const start = isoDate(record["start"]);
|
|
2987
|
+
const end = isoDate(record["end"]);
|
|
2988
|
+
if (start === void 0 || end === void 0) return entryRejected("start or end is not a date");
|
|
2989
|
+
if (start.getTime() > end.getTime()) return entryRejected("start is after end");
|
|
2990
|
+
const rulesId = record["rules_id"];
|
|
2991
|
+
if (typeof rulesId !== "number" || !Number.isSafeInteger(rulesId) || rulesId < 0) return entryRejected("rules_id is not an integer");
|
|
2992
|
+
return entryOk({
|
|
2993
|
+
id,
|
|
2994
|
+
name,
|
|
2995
|
+
start,
|
|
2996
|
+
end,
|
|
2997
|
+
rulesId
|
|
2998
|
+
});
|
|
3727
2999
|
}
|
|
3728
|
-
/**
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
const tail = segments.slice(-3);
|
|
3741
|
-
const collection = segments.slice(0, -3).join(delimiter);
|
|
3742
|
-
if (segments.length < 4 || collection.length === 0 || tail.some((part) => part.length === 0)) return;
|
|
3743
|
-
const [category, type, additionalKey] = tail;
|
|
3000
|
+
/** A non-negative decimal, as the backend's DECIMAL columns are serialized (or a finite number). */
|
|
3001
|
+
const DECIMAL = /^\d+(?:\.\d+)?$/u;
|
|
3002
|
+
function decimalOf(value) {
|
|
3003
|
+
const text = typeof value === "number" ? String(value) : value;
|
|
3004
|
+
return typeof text === "string" && DECIMAL.test(text) ? text : void 0;
|
|
3005
|
+
}
|
|
3006
|
+
function parseTitle(value) {
|
|
3007
|
+
const record = asEntryRecord(value);
|
|
3008
|
+
if (record === void 0) return void 0;
|
|
3009
|
+
const { name, type, order } = record;
|
|
3010
|
+
if (typeof name !== "string" || typeof type !== "string") return void 0;
|
|
3011
|
+
if (typeof order !== "number" || !Number.isFinite(order)) return void 0;
|
|
3744
3012
|
return {
|
|
3745
|
-
|
|
3746
|
-
category,
|
|
3013
|
+
name,
|
|
3747
3014
|
type,
|
|
3748
|
-
|
|
3015
|
+
order
|
|
3749
3016
|
};
|
|
3750
3017
|
}
|
|
3018
|
+
function parseEntry(entry) {
|
|
3019
|
+
const record = asEntryRecord(entry);
|
|
3020
|
+
if (record === void 0) return entryRejected("not an object");
|
|
3021
|
+
const wallet = record["wallet"];
|
|
3022
|
+
if (typeof wallet !== "string" || wallet === "") return entryRejected("wallet is not a string");
|
|
3023
|
+
const rank = positiveInteger(record["rank"]);
|
|
3024
|
+
if (rank === void 0) return entryRejected("rank is not an integer of at least 1");
|
|
3025
|
+
const rawTotal = record["total_xp"];
|
|
3026
|
+
const totalXp = rawTotal === void 0 || rawTotal === null ? null : decimalOf(rawTotal);
|
|
3027
|
+
if (totalXp === void 0) return entryRejected("total_xp is not a decimal");
|
|
3028
|
+
const distributionPercent = decimalOf(record["distribution_percent"]);
|
|
3029
|
+
const liquidityXp = decimalOf(record["liquidity_xp"]);
|
|
3030
|
+
const tradingXp = decimalOf(record["trading_xp"]);
|
|
3031
|
+
if (distributionPercent === void 0 || liquidityXp === void 0 || tradingXp === void 0) return entryRejected("distribution_percent, liquidity_xp or trading_xp is not a decimal");
|
|
3032
|
+
const rawTitles = record["mastery_titles"];
|
|
3033
|
+
if (!Array.isArray(rawTitles)) return entryRejected("mastery_titles is not a list");
|
|
3034
|
+
const masteryTitles = [];
|
|
3035
|
+
for (const raw of rawTitles) {
|
|
3036
|
+
const title = parseTitle(raw);
|
|
3037
|
+
if (title === void 0) return entryRejected("a mastery title is not { name, type, order }");
|
|
3038
|
+
masteryTitles.push(title);
|
|
3039
|
+
}
|
|
3040
|
+
return entryOk({
|
|
3041
|
+
wallet,
|
|
3042
|
+
rank,
|
|
3043
|
+
totalXp,
|
|
3044
|
+
distributionPercent,
|
|
3045
|
+
liquidityXp,
|
|
3046
|
+
tradingXp,
|
|
3047
|
+
masteryTitles
|
|
3048
|
+
});
|
|
3049
|
+
}
|
|
3050
|
+
|
|
3051
|
+
//#endregion
|
|
3052
|
+
//#region src/types/market.ts
|
|
3053
|
+
/**
|
|
3054
|
+
* The swap backend's market data and trading-competition reads: what `GSwap.market` and
|
|
3055
|
+
* `GSwap.leaderboard` return. Values are the backend's, unconverted: USD prices as numbers, times
|
|
3056
|
+
* as unix seconds or `Date`s, XP as decimal strings.
|
|
3057
|
+
*/
|
|
3058
|
+
/** The windows `GET /market/price-series` serves (the backend refuses any other with a 400). */
|
|
3059
|
+
const PRICE_SERIES_TIMEFRAMES = [
|
|
3060
|
+
"1D",
|
|
3061
|
+
"1W",
|
|
3062
|
+
"1M",
|
|
3063
|
+
"1Y",
|
|
3064
|
+
"ALL"
|
|
3065
|
+
];
|
|
3066
|
+
/** The most tokens one `GET /market/price` request may name (the backend's limit). */
|
|
3067
|
+
const MARKET_PRICE_BATCH_LIMIT = 500;
|
|
3068
|
+
|
|
3069
|
+
//#endregion
|
|
3070
|
+
//#region src/classes/market.ts
|
|
3751
3071
|
/**
|
|
3752
|
-
* The
|
|
3753
|
-
*
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
*
|
|
3758
|
-
*
|
|
3759
|
-
*
|
|
3072
|
+
* The ids `GET /market/price` and `GET /market/price-series` accept (the backend's own allowlist):
|
|
3073
|
+
* a symbol, or a class key in the `$` or `|` form. A comma would split the price request.
|
|
3074
|
+
*/
|
|
3075
|
+
const MARKET_TOKEN_PATTERN = /^[A-Za-z0-9._$+/:|-]{1,120}$/u;
|
|
3076
|
+
/**
|
|
3077
|
+
* Market data from the swap backend: USD prices (`GET /market/price`), price series
|
|
3078
|
+
* (`GET /market/price-series`) and the platform volume totals (`GET /v2/trade/volume`). Public
|
|
3079
|
+
* reads: no signer, no wallet.
|
|
3760
3080
|
*
|
|
3761
|
-
*
|
|
3762
|
-
*
|
|
3763
|
-
*
|
|
3764
|
-
*
|
|
3765
|
-
* ```
|
|
3081
|
+
* A token is named by a symbol (`GALA`) or a class key (`GALA$Unit$none$none`,
|
|
3082
|
+
* `GALA|Unit|none|none` or the object). The backend answers each token under the exact id it was
|
|
3083
|
+
* asked for, so the answer is read back under that id: a token the backend has no entry for is
|
|
3084
|
+
* `undefined`, never another token's price.
|
|
3766
3085
|
*/
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3086
|
+
var Market = class {
|
|
3087
|
+
dexBackendBaseUrl;
|
|
3088
|
+
httpClient;
|
|
3089
|
+
options;
|
|
3090
|
+
/**
|
|
3091
|
+
* Creates a market service. `options.onInvalidEntry` is told about every price or price point
|
|
3092
|
+
* that was skipped as unusable.
|
|
3093
|
+
*
|
|
3094
|
+
* @example
|
|
3095
|
+
* ```ts
|
|
3096
|
+
* const market = new Market('https://dex-backend-prod1.defi.gala.com', httpClient);
|
|
3097
|
+
* ```
|
|
3098
|
+
*/
|
|
3099
|
+
constructor(dexBackendBaseUrl, httpClient, options = {}) {
|
|
3100
|
+
this.dexBackendBaseUrl = dexBackendBaseUrl;
|
|
3101
|
+
this.httpClient = httpClient;
|
|
3102
|
+
this.options = options;
|
|
3103
|
+
}
|
|
3104
|
+
/**
|
|
3105
|
+
* USD prices of 1 to 500 tokens, in the order asked (a token named twice is asked for once).
|
|
3106
|
+
* The ids go in as many requests as their length needs (see {@link MARKET_PRICE_QUERY_BYTES}),
|
|
3107
|
+
* one after another; any request failing fails the call. A token the backend did not answer
|
|
3108
|
+
* for is `undefined`; one it answered for unusably is `undefined` too, and reported to
|
|
3109
|
+
* `onInvalidEntry`. An answer that is not an object is `INVALID_CHAIN_RESPONSE`.
|
|
3110
|
+
*
|
|
3111
|
+
* @example
|
|
3112
|
+
* ```ts
|
|
3113
|
+
* const [gala, gusdc] = await gSwap.market.getPrices(['GALA$Unit$none$none', 'GUSDC$Unit$none$none']);
|
|
3114
|
+
* console.log(gala?.price, gusdc?.price);
|
|
3115
|
+
* ```
|
|
3116
|
+
*/
|
|
3117
|
+
async getPrices(tokens, options) {
|
|
3118
|
+
if (!Array.isArray(tokens) || tokens.length < 1 || tokens.length > 500) throw new GSwapSDKError("INVALID_TOKEN_BATCH", {
|
|
3119
|
+
method: "getPrices",
|
|
3120
|
+
count: Array.isArray(tokens) ? tokens.length : 0,
|
|
3121
|
+
max: 500
|
|
3122
|
+
});
|
|
3123
|
+
const ids = tokens.map(marketTokenOf);
|
|
3124
|
+
const answered = /* @__PURE__ */ new Map();
|
|
3125
|
+
for (const batch of priceBatches([...new Set(ids)])) {
|
|
3126
|
+
const response = await this.httpClient.sendGetRequest(this.dexBackendBaseUrl, "/market", "/price", { assetIds: batch.join(",") }, signalOption(options));
|
|
3127
|
+
const answer = asEntryRecord(response);
|
|
3128
|
+
if (answer === void 0) throw new GSwapSDKError("INVALID_CHAIN_RESPONSE", { resource: "market prices" }, {
|
|
3129
|
+
body: response,
|
|
3130
|
+
write: false
|
|
3131
|
+
});
|
|
3132
|
+
for (const id of batch) if (Object.hasOwn(answer, id)) answered.set(id, answer[id]);
|
|
3133
|
+
}
|
|
3134
|
+
const onInvalidEntry = (options === null || options === void 0 ? void 0 : options.onInvalidEntry) ?? this.options.onInvalidEntry;
|
|
3135
|
+
return ids.map((id, index) => {
|
|
3136
|
+
if (!answered.has(id)) return void 0;
|
|
3137
|
+
const entry = answered.get(id);
|
|
3138
|
+
const parsed = parseMarketPrice(id, entry);
|
|
3139
|
+
if (parsed.ok) return parsed.value;
|
|
3140
|
+
try {
|
|
3141
|
+
onInvalidEntry === null || onInvalidEntry === void 0 || onInvalidEntry({
|
|
3142
|
+
source: "prices",
|
|
3143
|
+
index,
|
|
3144
|
+
entry,
|
|
3145
|
+
reason: parsed.reason
|
|
3146
|
+
});
|
|
3147
|
+
} catch {}
|
|
3148
|
+
});
|
|
3149
|
+
}
|
|
3150
|
+
/**
|
|
3151
|
+
* The USD price of one token, or `undefined` when the backend has no entry for it (see
|
|
3152
|
+
* {@link Market.getPrices}).
|
|
3153
|
+
*
|
|
3154
|
+
* @example
|
|
3155
|
+
* ```ts
|
|
3156
|
+
* const gala = await gSwap.market.getPrice('GALA$Unit$none$none');
|
|
3157
|
+
* if (gala?.price != null) console.log(`GALA: $${gala.price}`);
|
|
3158
|
+
* ```
|
|
3159
|
+
*/
|
|
3160
|
+
async getPrice(token, options) {
|
|
3161
|
+
const [price] = await this.getPrices([token], options);
|
|
3162
|
+
return price;
|
|
3163
|
+
}
|
|
3164
|
+
/**
|
|
3165
|
+
* A token's USD price line for one window (1D, 1W, 1M, 1Y or ALL; any other is
|
|
3166
|
+
* `INVALID_TIMEFRAME` before anything is sent). The whole series in one answer, oldest first; a
|
|
3167
|
+
* token the backend has no prices for is an empty series. An unusable point is skipped and
|
|
3168
|
+
* reported to `onInvalidEntry`; an answer that is not a series is `INVALID_CHAIN_RESPONSE`.
|
|
3169
|
+
*
|
|
3170
|
+
* @example
|
|
3171
|
+
* ```ts
|
|
3172
|
+
* const series = await gSwap.market.getPriceSeries('GALA$Unit$none$none', '1W');
|
|
3173
|
+
* console.log(series.prices.length, series.currentPrice);
|
|
3174
|
+
* ```
|
|
3175
|
+
*/
|
|
3176
|
+
async getPriceSeries(token, timeframe, options) {
|
|
3177
|
+
if (!PRICE_SERIES_TIMEFRAMES.includes(timeframe)) throw new GSwapSDKError("INVALID_TIMEFRAME", {
|
|
3178
|
+
value: String(timeframe),
|
|
3179
|
+
allowed: PRICE_SERIES_TIMEFRAMES.join(", ")
|
|
3180
|
+
});
|
|
3181
|
+
const id = marketTokenOf(token);
|
|
3182
|
+
const response = await this.httpClient.sendGetRequest(this.dexBackendBaseUrl, "/market", "/price-series", {
|
|
3183
|
+
token: id,
|
|
3184
|
+
timeframe
|
|
3185
|
+
}, signalOption(options));
|
|
3186
|
+
const answer = asEntryRecord(response);
|
|
3187
|
+
const currentPrice = answer === void 0 ? void 0 : nullableFinite(answer["currentPrice"]);
|
|
3188
|
+
const change24h = answer === void 0 ? void 0 : nullableFinite(answer["change24h"]);
|
|
3189
|
+
if (answer === void 0 || !Array.isArray(answer["prices"]) || currentPrice === void 0 || change24h === void 0) throw new GSwapSDKError("INVALID_CHAIN_RESPONSE", { resource: "price series" }, {
|
|
3190
|
+
body: response,
|
|
3191
|
+
write: false
|
|
3192
|
+
});
|
|
3773
3193
|
return {
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3194
|
+
token: id,
|
|
3195
|
+
timeframe,
|
|
3196
|
+
prices: readEntries("priceSeries", answer["prices"], parsePricePoint, (options === null || options === void 0 ? void 0 : options.onInvalidEntry) ?? this.options.onInvalidEntry),
|
|
3197
|
+
currentPrice,
|
|
3198
|
+
change24h
|
|
3778
3199
|
};
|
|
3779
|
-
} catch {
|
|
3780
|
-
return;
|
|
3781
3200
|
}
|
|
3201
|
+
/**
|
|
3202
|
+
* TVL, volume and trade totals across every current-contract pool. Every field must be a finite
|
|
3203
|
+
* number, or the answer is `INVALID_CHAIN_RESPONSE`.
|
|
3204
|
+
*
|
|
3205
|
+
* @example
|
|
3206
|
+
* ```ts
|
|
3207
|
+
* const volume = await gSwap.market.getVolumeSummary();
|
|
3208
|
+
* console.log(`24h: $${volume.volume1d.toFixed(0)} (${(volume.volume1dDelta * 100).toFixed(1)} %)`);
|
|
3209
|
+
* ```
|
|
3210
|
+
*/
|
|
3211
|
+
async getVolumeSummary(options) {
|
|
3212
|
+
var _asEntryRecord;
|
|
3213
|
+
const response = await this.httpClient.sendGetRequest(this.dexBackendBaseUrl, "/v2/trade", "/volume", void 0, signalOption(options));
|
|
3214
|
+
const summary = parseVolumeSummary(asEntryRecord((_asEntryRecord = asEntryRecord(response)) === null || _asEntryRecord === void 0 ? void 0 : _asEntryRecord["data"]));
|
|
3215
|
+
if (summary === void 0) throw new GSwapSDKError("INVALID_CHAIN_RESPONSE", { resource: "volume summary" }, {
|
|
3216
|
+
body: response,
|
|
3217
|
+
write: false
|
|
3218
|
+
});
|
|
3219
|
+
return summary;
|
|
3220
|
+
}
|
|
3221
|
+
};
|
|
3222
|
+
/**
|
|
3223
|
+
* The most query bytes one `GET /market/price` carries. The backend refuses a request line over
|
|
3224
|
+
* 16 KB (Node's default header limit: 300 launchpad keys, 17.7 KB encoded, answer HTTP 431,
|
|
3225
|
+
* recorded on prod 2026-10-13), so a long list is split well under it (about 135 launchpad keys
|
|
3226
|
+
* per request, 270 short ones).
|
|
3227
|
+
*/
|
|
3228
|
+
const MARKET_PRICE_QUERY_BYTES = 8e3;
|
|
3229
|
+
/**
|
|
3230
|
+
* The ids in request-sized batches: at most {@link MARKET_PRICE_BATCH_LIMIT} ids and
|
|
3231
|
+
* {@link MARKET_PRICE_QUERY_BYTES} of encoded `assetIds` each (the standard encoding's size, the
|
|
3232
|
+
* larger of the two, with a `%2C` per id).
|
|
3233
|
+
*/
|
|
3234
|
+
function priceBatches(ids) {
|
|
3235
|
+
const batches = [];
|
|
3236
|
+
let batch = [];
|
|
3237
|
+
let bytes = 0;
|
|
3238
|
+
for (const id of ids) {
|
|
3239
|
+
const size = encodeURIComponent(id).length + 3;
|
|
3240
|
+
if (batch.length > 0 && (bytes + size > 8e3 || batch.length === 500)) {
|
|
3241
|
+
batches.push(batch);
|
|
3242
|
+
batch = [];
|
|
3243
|
+
bytes = 0;
|
|
3244
|
+
}
|
|
3245
|
+
batch.push(id);
|
|
3246
|
+
bytes += size;
|
|
3247
|
+
}
|
|
3248
|
+
if (batch.length > 0) batches.push(batch);
|
|
3249
|
+
return batches;
|
|
3782
3250
|
}
|
|
3783
3251
|
/**
|
|
3784
|
-
*
|
|
3785
|
-
*
|
|
3786
|
-
* for a string that may ONLY be in the `$` form: a key that contains `|` is NOT read as the pipe
|
|
3787
|
-
* form here (it stays part of a segment), and only a string is accepted. The split is from the
|
|
3788
|
-
* right, so a collection that itself contains `$` (production's `$GMUSIC`, stage's
|
|
3789
|
-
* `$GMUSIC_TEST1`) stays whole; `'GALA'`, `'GALA$Unit$none'`, `'ETIME$$$'`, `'GALA$Unit$none$'` and
|
|
3790
|
-
* `'$$$$'` are `undefined`.
|
|
3791
|
-
*
|
|
3792
|
-
* It replaces an app's own `parseCompositeKey(key): Components | null` (`?? null`), which had
|
|
3793
|
-
* exactly these semantics.
|
|
3794
|
-
*
|
|
3795
|
-
* @example
|
|
3796
|
-
* ```ts
|
|
3797
|
-
* tryParseCompositeKey('$GMUSIC_TEST1$Unit$none$none')?.collection; // '$GMUSIC_TEST1'
|
|
3798
|
-
* tryParseCompositeKey('GALA$Unit$none'); // undefined
|
|
3799
|
-
* ```
|
|
3252
|
+
* The id a market read sends for a token: a string as given (a symbol or a class key in either
|
|
3253
|
+
* form), an object key in its `$` form. Anything the backend's allowlist refuses is refused here.
|
|
3800
3254
|
*/
|
|
3801
|
-
function
|
|
3802
|
-
|
|
3255
|
+
function marketTokenOf(token) {
|
|
3256
|
+
if (typeof token !== "string") {
|
|
3257
|
+
if (!(0, _gala_chain_gala_core_sdk.isTokenClassKey)(token)) throw new GSwapSDKError("INVALID_TOKEN_CLASS_KEY", { tokenClassKey: describeValue(token) });
|
|
3258
|
+
return checkedMarketToken((0, _gala_chain_gala_core_sdk.compositeKeyOf)(token));
|
|
3259
|
+
}
|
|
3260
|
+
return checkedMarketToken(token);
|
|
3261
|
+
}
|
|
3262
|
+
function checkedMarketToken(id) {
|
|
3263
|
+
if (!MARKET_TOKEN_PATTERN.test(id)) throw new GSwapSDKError("INVALID_MARKET_TOKEN", { token: id });
|
|
3264
|
+
return id;
|
|
3265
|
+
}
|
|
3266
|
+
function describeValue(value) {
|
|
3267
|
+
try {
|
|
3268
|
+
return JSON.stringify(value) ?? String(value);
|
|
3269
|
+
} catch {
|
|
3270
|
+
return Object.prototype.toString.call(value);
|
|
3271
|
+
}
|
|
3272
|
+
}
|
|
3273
|
+
function signalOption(options) {
|
|
3274
|
+
return (options === null || options === void 0 ? void 0 : options.signal) === void 0 ? void 0 : { signal: options.signal };
|
|
3275
|
+
}
|
|
3276
|
+
/** A finite number, `null` for `null`, `undefined` for anything else (an unusable value). */
|
|
3277
|
+
function nullableFinite(value) {
|
|
3278
|
+
if (value === null) return null;
|
|
3279
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
3280
|
+
}
|
|
3281
|
+
function finite(value) {
|
|
3282
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
3283
|
+
}
|
|
3284
|
+
function parseMarketPrice(token, entry) {
|
|
3285
|
+
const record = asEntryRecord(entry);
|
|
3286
|
+
if (record === void 0) return entryRejected("not an object");
|
|
3287
|
+
const price = nullableFinite(record["price"]);
|
|
3288
|
+
if (price === void 0 || price !== null && price < 0) return entryRejected("price is not a non-negative number or null");
|
|
3289
|
+
const change24h = nullableFinite(record["change24h"]);
|
|
3290
|
+
if (change24h === void 0) return entryRejected("change24h is not a number or null");
|
|
3291
|
+
const listed = record["listed"];
|
|
3292
|
+
if (typeof listed !== "boolean") return entryRejected("listed is not a boolean");
|
|
3293
|
+
return entryOk({
|
|
3294
|
+
token,
|
|
3295
|
+
price,
|
|
3296
|
+
change24h,
|
|
3297
|
+
listed
|
|
3298
|
+
});
|
|
3299
|
+
}
|
|
3300
|
+
function parsePricePoint(entry) {
|
|
3301
|
+
const record = asEntryRecord(entry);
|
|
3302
|
+
const time = finite(record === null || record === void 0 ? void 0 : record["time"]);
|
|
3303
|
+
const price = finite(record === null || record === void 0 ? void 0 : record["price"]);
|
|
3304
|
+
if (time === void 0 || !Number.isInteger(time) || time < 0) return entryRejected("time is not unix seconds");
|
|
3305
|
+
if (price === void 0 || price < 0) return entryRejected("price is not a non-negative number");
|
|
3306
|
+
return entryOk({
|
|
3307
|
+
time,
|
|
3308
|
+
price
|
|
3309
|
+
});
|
|
3310
|
+
}
|
|
3311
|
+
const VOLUME_FIELDS = [
|
|
3312
|
+
"tvl",
|
|
3313
|
+
"volume1d",
|
|
3314
|
+
"volume7d",
|
|
3315
|
+
"volume30d",
|
|
3316
|
+
"trades1d",
|
|
3317
|
+
"volume1dDelta",
|
|
3318
|
+
"volume7dDelta",
|
|
3319
|
+
"volume30dDelta"
|
|
3320
|
+
];
|
|
3321
|
+
function parseVolumeSummary(data) {
|
|
3322
|
+
if (data === void 0) return void 0;
|
|
3323
|
+
const summary = {};
|
|
3324
|
+
for (const field of VOLUME_FIELDS) {
|
|
3325
|
+
const value = finite(data[field]);
|
|
3326
|
+
if (value === void 0) return void 0;
|
|
3327
|
+
summary[field] = value;
|
|
3328
|
+
}
|
|
3329
|
+
const trades = summary.trades1d;
|
|
3330
|
+
if (trades === void 0 || !Number.isInteger(trades) || trades < 0) return void 0;
|
|
3331
|
+
return summary;
|
|
3803
3332
|
}
|
|
3804
3333
|
|
|
3805
3334
|
//#endregion
|
|
@@ -3857,11 +3386,30 @@ function parseTokenClassKey(tokenClassKey) {
|
|
|
3857
3386
|
type: tokenClassKey.type,
|
|
3858
3387
|
additionalKey: tokenClassKey.additionalKey
|
|
3859
3388
|
};
|
|
3860
|
-
const parsed = splitClassKey(tokenClassKey, tokenClassKey.includes("|") ? "|" : "$");
|
|
3389
|
+
const parsed = (0, _gala_chain_gala_core_sdk.splitClassKey)(tokenClassKey, tokenClassKey.includes("|") ? "|" : "$");
|
|
3861
3390
|
if (parsed === void 0) throw new GSwapSDKError("INVALID_TOKEN_CLASS_KEY", { tokenClassKey });
|
|
3862
3391
|
return parsed;
|
|
3863
3392
|
}
|
|
3864
3393
|
|
|
3394
|
+
//#endregion
|
|
3395
|
+
//#region src/utils/resolve_pre_send.ts
|
|
3396
|
+
/**
|
|
3397
|
+
* Runs a token-resolution step (a resolver's `resolve` / `orderPair`, which may read the registry
|
|
3398
|
+
* through the app's HTTP stack) on the "nothing was sent" boundary, like core's `signPreSend` does
|
|
3399
|
+
* for the signer. A failure that is not an SDK error is `TOKEN_RESOLUTION_FAILED` (`token` names
|
|
3400
|
+
* what was being resolved, the original is `cause`); an SDK error of any product (every one is a
|
|
3401
|
+
* `GalaSDKError`, core's transport errors included) passes through.
|
|
3402
|
+
*
|
|
3403
|
+
* @throws GSwapSDKError TOKEN_RESOLUTION_FAILED, or the SDK error the step itself threw.
|
|
3404
|
+
*/
|
|
3405
|
+
async function resolvePreSend(token, run) {
|
|
3406
|
+
try {
|
|
3407
|
+
return await run();
|
|
3408
|
+
} catch (cause) {
|
|
3409
|
+
throw (0, _gala_chain_gala_core_sdk.isGalaError)(cause) ? cause : new GSwapSDKError("TOKEN_RESOLUTION_FAILED", { token }, { cause });
|
|
3410
|
+
}
|
|
3411
|
+
}
|
|
3412
|
+
|
|
3865
3413
|
//#endregion
|
|
3866
3414
|
//#region src/classes/symbols.ts
|
|
3867
3415
|
const SYMBOL_CACHE_TTL_MS = 6e4;
|
|
@@ -3925,11 +3473,11 @@ var Symbols = class {
|
|
|
3925
3473
|
/** Resolve a symbol or token class key to its registered symbol metadata. */
|
|
3926
3474
|
async resolve(ref) {
|
|
3927
3475
|
let symbols = await this.list();
|
|
3928
|
-
const composite = typeof ref === "string" ? isCompositeKey(ref) ? compositeKeyOf(parseTokenClassKey(ref)) : void 0 : compositeKeyOf(ref);
|
|
3929
|
-
const resolved = symbols.find((entry) => entry.symbol === ref || composite !== void 0 && compositeKeyOf(entry) === composite);
|
|
3476
|
+
const composite = typeof ref === "string" ? isCompositeKey(ref) ? (0, _gala_chain_gala_core_sdk.compositeKeyOf)(parseTokenClassKey(ref)) : void 0 : (0, _gala_chain_gala_core_sdk.compositeKeyOf)(ref);
|
|
3477
|
+
const resolved = symbols.find((entry) => entry.symbol === ref || composite !== void 0 && (0, _gala_chain_gala_core_sdk.compositeKeyOf)(entry) === composite);
|
|
3930
3478
|
if (resolved === void 0) {
|
|
3931
3479
|
symbols = await this.refresh();
|
|
3932
|
-
const refreshed = symbols.find((entry) => entry.symbol === ref || composite !== void 0 && compositeKeyOf(entry) === composite);
|
|
3480
|
+
const refreshed = symbols.find((entry) => entry.symbol === ref || composite !== void 0 && (0, _gala_chain_gala_core_sdk.compositeKeyOf)(entry) === composite);
|
|
3933
3481
|
if (refreshed === void 0) throw GSwapSDKError.unknownTokenError(ref);
|
|
3934
3482
|
return refreshed;
|
|
3935
3483
|
}
|
|
@@ -3964,7 +3512,7 @@ var Symbols = class {
|
|
|
3964
3512
|
} catch (error) {
|
|
3965
3513
|
if (options.registryFailure !== "ignore") throw error;
|
|
3966
3514
|
}
|
|
3967
|
-
const wanted = compositeKeyOf(classKey);
|
|
3515
|
+
const wanted = (0, _gala_chain_gala_core_sdk.compositeKeyOf)(classKey);
|
|
3968
3516
|
const registered = registry.find((entry) => entry.collection === classKey.collection && entry.category === classKey.category && entry.type === classKey.type && entry.additionalKey === classKey.additionalKey);
|
|
3969
3517
|
if (registered !== void 0) return registered.symbol;
|
|
3970
3518
|
if (isProposableTradingSymbol(displaySymbol)) return displaySymbol;
|
|
@@ -4010,7 +3558,7 @@ function isCompositeKey(ref) {
|
|
|
4010
3558
|
}
|
|
4011
3559
|
/** A token reference as the caller wrote it: a string verbatim, a class key as its `$` composite. */
|
|
4012
3560
|
function rawTokenOf(ref) {
|
|
4013
|
-
return typeof ref === "string" ? ref : compositeKeyOf(ref);
|
|
3561
|
+
return typeof ref === "string" ? ref : (0, _gala_chain_gala_core_sdk.compositeKeyOf)(ref);
|
|
4014
3562
|
}
|
|
4015
3563
|
/** The trading symbol of a reference, or `SYMBOL_NOT_FOUND` when the resolver has none. */
|
|
4016
3564
|
async function tradingSymbolOf(resolver, ref) {
|
|
@@ -4108,6 +3656,70 @@ var Pools = class {
|
|
|
4108
3656
|
return readEntries("pools", response.data, parsePoolListInfo, (options === null || options === void 0 ? void 0 : options.onInvalidEntry) ?? this.options.onInvalidEntry);
|
|
4109
3657
|
}
|
|
4110
3658
|
/**
|
|
3659
|
+
* Watches for pools created from now on, by polling {@link Pools.getPools} every `intervalMs`
|
|
3660
|
+
* (the polling alternative to the realtime stream's `GSwap.stream.subscribePoolCreated`, for a
|
|
3661
|
+
* client that cannot hold a WebSocket open). The first answer is the baseline: a pool that
|
|
3662
|
+
* existed then is never reported. Each later pool is judged once, when it first appears, against
|
|
3663
|
+
* `minTvl` and `tokens`. A poll never overlaps the previous one (a tick that finds one still in
|
|
3664
|
+
* flight is skipped), and nothing is reported after the returned function stops the watcher.
|
|
3665
|
+
*
|
|
3666
|
+
* @example
|
|
3667
|
+
* ```ts
|
|
3668
|
+
* const stop = gSwap.pools.watchNewPools((pool) => console.log('new pool', pool.poolRef), {
|
|
3669
|
+
* intervalMs: 60_000,
|
|
3670
|
+
* tokens: ['GALA'],
|
|
3671
|
+
* });
|
|
3672
|
+
* // later
|
|
3673
|
+
* stop();
|
|
3674
|
+
* ```
|
|
3675
|
+
*/
|
|
3676
|
+
watchNewPools(onPool, options = {}) {
|
|
3677
|
+
const intervalMs = options.intervalMs ?? 3e4;
|
|
3678
|
+
if (typeof intervalMs !== "number" || !Number.isFinite(intervalMs) || intervalMs < 1) throw new GSwapSDKError("INVALID_POLL_INTERVAL", { value: String(intervalMs) });
|
|
3679
|
+
let known;
|
|
3680
|
+
let stopped = false;
|
|
3681
|
+
let polling = false;
|
|
3682
|
+
const report = (error) => {
|
|
3683
|
+
try {
|
|
3684
|
+
var _options$onError;
|
|
3685
|
+
(_options$onError = options.onError) === null || _options$onError === void 0 || _options$onError.call(options, error);
|
|
3686
|
+
} catch {}
|
|
3687
|
+
};
|
|
3688
|
+
const wanted = (pool) => (options.minTvl === void 0 || (pool.tvlUsd ?? 0) >= options.minTvl) && (options.tokens === void 0 || options.tokens.includes(pool.token0) || options.tokens.includes(pool.token1));
|
|
3689
|
+
const poll = async () => {
|
|
3690
|
+
if (stopped || polling) return;
|
|
3691
|
+
polling = true;
|
|
3692
|
+
try {
|
|
3693
|
+
const pools = await this.getPools();
|
|
3694
|
+
if (known === void 0) {
|
|
3695
|
+
known = new Set(pools.map((pool) => pool.poolRef));
|
|
3696
|
+
return;
|
|
3697
|
+
}
|
|
3698
|
+
for (const pool of pools) {
|
|
3699
|
+
if (stopped) return;
|
|
3700
|
+
if (known.has(pool.poolRef)) continue;
|
|
3701
|
+
known.add(pool.poolRef);
|
|
3702
|
+
if (!wanted(pool)) continue;
|
|
3703
|
+
try {
|
|
3704
|
+
onPool(pool);
|
|
3705
|
+
} catch (error) {
|
|
3706
|
+
report(error);
|
|
3707
|
+
}
|
|
3708
|
+
}
|
|
3709
|
+
} catch (error) {
|
|
3710
|
+
if (!stopped) report(error);
|
|
3711
|
+
} finally {
|
|
3712
|
+
polling = false;
|
|
3713
|
+
}
|
|
3714
|
+
};
|
|
3715
|
+
poll();
|
|
3716
|
+
const timer = setInterval(() => void poll(), intervalMs);
|
|
3717
|
+
return () => {
|
|
3718
|
+
stopped = true;
|
|
3719
|
+
clearInterval(timer);
|
|
3720
|
+
};
|
|
3721
|
+
}
|
|
3722
|
+
/**
|
|
4111
3723
|
* Fetches one pool from the backend, preserving whether the request order was flipped.
|
|
4112
3724
|
*
|
|
4113
3725
|
* @example
|
|
@@ -4169,8 +3781,11 @@ var Pools = class {
|
|
|
4169
3781
|
try {
|
|
4170
3782
|
return (await this.http.sendGetRequest(this.gateway.dexBackendBaseUrl, "/v2/trade", endpoint, params)).data;
|
|
4171
3783
|
} catch (error) {
|
|
4172
|
-
if (error
|
|
4173
|
-
throw new GSwapSDKError("REQUEST_FAILED", { operation: "pool" }, {
|
|
3784
|
+
if (isGSwapError(error)) throw error;
|
|
3785
|
+
throw new GSwapSDKError("REQUEST_FAILED", { operation: "pool" }, {
|
|
3786
|
+
cause: error,
|
|
3787
|
+
write: false
|
|
3788
|
+
});
|
|
4174
3789
|
}
|
|
4175
3790
|
}
|
|
4176
3791
|
};
|
|
@@ -4393,49 +4008,6 @@ function frameSlot0(slot0) {
|
|
|
4393
4008
|
};
|
|
4394
4009
|
}
|
|
4395
4010
|
|
|
4396
|
-
//#endregion
|
|
4397
|
-
//#region src/utils/unique_key.ts
|
|
4398
|
-
/**
|
|
4399
|
-
* The `uniqueKey` prefix frontend_v2 and wallet_extension have always sent
|
|
4400
|
-
* (`galaswap-operation-<uuid>`). defi-backend does not depend on it: the chain-write verifier
|
|
4401
|
-
* only requires a non-empty key of at most 256 characters, and apply-results accepts
|
|
4402
|
-
* `[A-Za-z0-9._:-]{1,128}`; the only code that mints a prefixed key itself is the server-side v1
|
|
4403
|
-
* trade builder, which nothing reads back by prefix.
|
|
4404
|
-
*/
|
|
4405
|
-
const DEFAULT_UNIQUE_KEY_PREFIX = "galaswap-operation-";
|
|
4406
|
-
/** A canonical UUID is 36 characters; the apply-results key limit is 128. */
|
|
4407
|
-
const MAX_UNIQUE_KEY_PREFIX_LENGTH = 92;
|
|
4408
|
-
const UNIQUE_KEY_PREFIX_PATTERN = /^[A-Za-z0-9._:-]*$/u;
|
|
4409
|
-
/**
|
|
4410
|
-
* Validates a caller-supplied `uniqueKey` prefix. The whole key (`<prefix><uuid>`) must stay a
|
|
4411
|
-
* valid apply-results key, otherwise `confirm()` could never read the outcome of the write.
|
|
4412
|
-
*
|
|
4413
|
-
* @throws GSwapSDKError `INVALID_UNIQUE_KEY_PREFIX` when the prefix has characters outside
|
|
4414
|
-
* `A-Za-z0-9._:-` or is longer than 92 characters.
|
|
4415
|
-
*/
|
|
4416
|
-
function resolveUniqueKeyPrefix(prefix) {
|
|
4417
|
-
if (prefix === void 0) return DEFAULT_UNIQUE_KEY_PREFIX;
|
|
4418
|
-
if (!UNIQUE_KEY_PREFIX_PATTERN.test(prefix) || prefix.length > MAX_UNIQUE_KEY_PREFIX_LENGTH) throw new GSwapSDKError("INVALID_UNIQUE_KEY_PREFIX", {
|
|
4419
|
-
uniqueKeyPrefix: prefix,
|
|
4420
|
-
maxLength: MAX_UNIQUE_KEY_PREFIX_LENGTH
|
|
4421
|
-
});
|
|
4422
|
-
return prefix;
|
|
4423
|
-
}
|
|
4424
|
-
/**
|
|
4425
|
-
* Validates a caller-supplied write `uniqueKey`: 1 to 128 characters of `A-Za-z0-9._:-`, the
|
|
4426
|
-
* shape the apply-results route accepts, so the write's outcome can always be read back with
|
|
4427
|
-
* `confirm()`. A key is one attempt's identity: reuse it only to retry that same attempt.
|
|
4428
|
-
*
|
|
4429
|
-
* @throws GSwapSDKError `INVALID_UNIQUE_KEY` otherwise.
|
|
4430
|
-
*/
|
|
4431
|
-
function assertUniqueKey(uniqueKey) {
|
|
4432
|
-
if (!isValidUniqueKey(uniqueKey)) throw new GSwapSDKError("INVALID_UNIQUE_KEY", { uniqueKey: String(uniqueKey) });
|
|
4433
|
-
}
|
|
4434
|
-
/** A fresh per-attempt key: `<prefix><random UUID>`. */
|
|
4435
|
-
function createUniqueKey(prefix) {
|
|
4436
|
-
return `${prefix}${globalThis.crypto.randomUUID()}`;
|
|
4437
|
-
}
|
|
4438
|
-
|
|
4439
4011
|
//#endregion
|
|
4440
4012
|
//#region src/utils/dto_builders.ts
|
|
4441
4013
|
const decimalString = (amount) => new bignumber_js.default(amount).toFixed();
|
|
@@ -4459,7 +4031,7 @@ const decimalString = (amount) => new bignumber_js.default(amount).toFixed();
|
|
|
4459
4031
|
function buildRequestTradeDto(input) {
|
|
4460
4032
|
validateFee(input.fee);
|
|
4461
4033
|
assertCanonicalPair(input.token0, input.token1);
|
|
4462
|
-
assertUniqueKey(input.uniqueKey);
|
|
4034
|
+
(0, _gala_chain_gala_core_sdk.assertUniqueKey)(input.uniqueKey);
|
|
4463
4035
|
const dto = {
|
|
4464
4036
|
token0: input.token0,
|
|
4465
4037
|
token1: input.token1,
|
|
@@ -4495,7 +4067,7 @@ function assertPoolPosition(input) {
|
|
|
4495
4067
|
validateFee(input.fee);
|
|
4496
4068
|
assertCanonicalPair(input.token0, input.token1);
|
|
4497
4069
|
assertTickRange(input.tickLower, input.tickUpper, input.fee);
|
|
4498
|
-
assertUniqueKey(input.uniqueKey);
|
|
4070
|
+
(0, _gala_chain_gala_core_sdk.assertUniqueKey)(input.uniqueKey);
|
|
4499
4071
|
}
|
|
4500
4072
|
/**
|
|
4501
4073
|
* Builds the AddLiquidity DTO: exactly one of `depositQuantityToken0` / `depositQuantityToken1`
|
|
@@ -4558,7 +4130,7 @@ function buildCollectPositionFeesDto(input) {
|
|
|
4558
4130
|
*/
|
|
4559
4131
|
function buildCreatePoolDto(input) {
|
|
4560
4132
|
validateFee(input.fee);
|
|
4561
|
-
assertUniqueKey(input.uniqueKey);
|
|
4133
|
+
(0, _gala_chain_gala_core_sdk.assertUniqueKey)(input.uniqueKey);
|
|
4562
4134
|
assertCanonicalPair(input.token0Symbol, input.token1Symbol);
|
|
4563
4135
|
for (const symbol of [input.token0Symbol, input.token1Symbol]) if (!isProposableTradingSymbol(symbol)) throw new GSwapSDKError("INVALID_TRADING_SYMBOL", { symbol });
|
|
4564
4136
|
for (const key of [input.token0Key, input.token1Key]) assertClassKey(key);
|
|
@@ -4781,7 +4353,7 @@ var Positions = class {
|
|
|
4781
4353
|
this.signer = signer;
|
|
4782
4354
|
this.walletAddress = walletAddress;
|
|
4783
4355
|
this.http = new HttpClient(gateway.httpRequestor, gateway.requestTimeoutMs ?? 3e4, gateway.wire);
|
|
4784
|
-
this.uniqueKeyPrefix = resolveUniqueKeyPrefix(options.uniqueKeyPrefix);
|
|
4356
|
+
this.uniqueKeyPrefix = (0, _gala_chain_gala_core_sdk.resolveUniqueKeyPrefix)(options.uniqueKeyPrefix);
|
|
4785
4357
|
this.onInvalidEntry = options.onInvalidEntry;
|
|
4786
4358
|
}
|
|
4787
4359
|
http;
|
|
@@ -4849,7 +4421,10 @@ var Positions = class {
|
|
|
4849
4421
|
if ((signal === null || signal === void 0 ? void 0 : signal.aborted) === true) throw error;
|
|
4850
4422
|
return {
|
|
4851
4423
|
kind: "unknown",
|
|
4852
|
-
error: isGSwapError(error) ? error : new GSwapSDKError("REQUEST_FAILED", { operation: "position" }, {
|
|
4424
|
+
error: isGSwapError(error) ? error : new GSwapSDKError("REQUEST_FAILED", { operation: "position" }, {
|
|
4425
|
+
cause: error,
|
|
4426
|
+
write: false
|
|
4427
|
+
})
|
|
4853
4428
|
};
|
|
4854
4429
|
}
|
|
4855
4430
|
}
|
|
@@ -5220,7 +4795,7 @@ var Positions = class {
|
|
|
5220
4795
|
}
|
|
5221
4796
|
async submit(method, dto, confirmation) {
|
|
5222
4797
|
if (this.signer === void 0) throw GSwapSDKError.noSignerError();
|
|
5223
|
-
const signed = await signPreSend(this.signer, method, dto);
|
|
4798
|
+
const signed = await (0, _gala_chain_gala_core_sdk.signPreSend)(this.signer, method, dto);
|
|
5224
4799
|
const submitOptions = this.walletAddress === void 0 ? {} : { walletAddress: this.walletAddress };
|
|
5225
4800
|
const owner = this.walletAddress;
|
|
5226
4801
|
const positionConfirmation = this.buildPositionConfirmation(confirmation, owner);
|
|
@@ -5316,7 +4891,7 @@ var Positions = class {
|
|
|
5316
4891
|
}
|
|
5317
4892
|
}
|
|
5318
4893
|
isNotFound(error) {
|
|
5319
|
-
return error
|
|
4894
|
+
return isGSwapError(error) && error.status === 404;
|
|
5320
4895
|
}
|
|
5321
4896
|
canonicalTicks(tickLower, tickUpper, flipped) {
|
|
5322
4897
|
return flipped ? {
|
|
@@ -5341,8 +4916,8 @@ var Positions = class {
|
|
|
5341
4916
|
}
|
|
5342
4917
|
/** The caller's `uniqueKey` (validated), else a fresh one. */
|
|
5343
4918
|
uniqueKeyFor(supplied) {
|
|
5344
|
-
if (supplied === void 0) return createUniqueKey(this.uniqueKeyPrefix);
|
|
5345
|
-
assertUniqueKey(supplied);
|
|
4919
|
+
if (supplied === void 0) return (0, _gala_chain_gala_core_sdk.createUniqueKey)(this.uniqueKeyPrefix);
|
|
4920
|
+
(0, _gala_chain_gala_core_sdk.assertUniqueKey)(supplied);
|
|
5346
4921
|
return supplied;
|
|
5347
4922
|
}
|
|
5348
4923
|
/**
|
|
@@ -5354,7 +4929,7 @@ var Positions = class {
|
|
|
5354
4929
|
}
|
|
5355
4930
|
async resolveCreateToken(ref, symbolOverride) {
|
|
5356
4931
|
try {
|
|
5357
|
-
const lookupRef = typeof ref === "object" ? compositeKeyOf(ref) : ref;
|
|
4932
|
+
const lookupRef = typeof ref === "object" ? (0, _gala_chain_gala_core_sdk.compositeKeyOf)(ref) : ref;
|
|
5358
4933
|
const resolved = await resolvePreSend(rawTokenOf(ref), () => this.symbols.resolve(lookupRef));
|
|
5359
4934
|
if (symbolOverride !== void 0 && symbolOverride !== "" && symbolOverride !== resolved.symbol) throw new GSwapSDKError("SYMBOL_DISAGREES_WITH_REGISTRY", {
|
|
5360
4935
|
token: rawTokenOf(ref),
|
|
@@ -5373,7 +4948,7 @@ var Positions = class {
|
|
|
5373
4948
|
})
|
|
5374
4949
|
};
|
|
5375
4950
|
} catch (error) {
|
|
5376
|
-
if (!(error
|
|
4951
|
+
if (!isGSwapError(error, "UNKNOWN_TOKEN")) throw error;
|
|
5377
4952
|
let classKey;
|
|
5378
4953
|
try {
|
|
5379
4954
|
classKey = parseTokenClassKey(ref);
|
|
@@ -5458,7 +5033,10 @@ function parsePosition(value) {
|
|
|
5458
5033
|
function readPositionBody(data) {
|
|
5459
5034
|
const parsed = parsePosition(data);
|
|
5460
5035
|
if (parsed.ok) return parsed.value;
|
|
5461
|
-
throw new GSwapSDKError("INVALID_POSITION_RESPONSE", { reason: parsed.reason }, {
|
|
5036
|
+
throw new GSwapSDKError("INVALID_POSITION_RESPONSE", { reason: parsed.reason }, {
|
|
5037
|
+
body: data,
|
|
5038
|
+
write: false
|
|
5039
|
+
});
|
|
5462
5040
|
}
|
|
5463
5041
|
|
|
5464
5042
|
//#endregion
|
|
@@ -5526,7 +5104,10 @@ var Quoting = class {
|
|
|
5526
5104
|
}
|
|
5527
5105
|
};
|
|
5528
5106
|
function mapQuote(value) {
|
|
5529
|
-
if (!isQuoteWire(value)) throw new GSwapSDKError("INVALID_CHAIN_RESPONSE", { resource: "quote" }, {
|
|
5107
|
+
if (!isQuoteWire(value)) throw new GSwapSDKError("INVALID_CHAIN_RESPONSE", { resource: "quote" }, {
|
|
5108
|
+
body: value,
|
|
5109
|
+
write: false
|
|
5110
|
+
});
|
|
5530
5111
|
const wire = value;
|
|
5531
5112
|
const currentPrice = wire.currentPrice !== void 0 ? new bignumber_js.default(wire.currentPrice) : orientPrice(wire.currentSqrtPrice, wire.tokenInIsToken0);
|
|
5532
5113
|
const newPrice = wire.newPrice !== void 0 ? new bignumber_js.default(wire.newPrice) : orientPrice(wire.newSqrtPrice, wire.tokenInIsToken0);
|
|
@@ -5611,7 +5192,10 @@ function mapQuoteError(error, tokenIn, tokenOut, fee, queried) {
|
|
|
5611
5192
|
if (error.status === 400 && /insufficient liquidity/iu.test(message)) return GSwapSDKError.insufficientLiquidityError(tokenIn, tokenOut, fee, error);
|
|
5612
5193
|
return error;
|
|
5613
5194
|
}
|
|
5614
|
-
return new GSwapSDKError("REQUEST_FAILED", { operation: "quote" }, {
|
|
5195
|
+
return new GSwapSDKError("REQUEST_FAILED", { operation: "quote" }, {
|
|
5196
|
+
cause: error,
|
|
5197
|
+
write: false
|
|
5198
|
+
});
|
|
5615
5199
|
}
|
|
5616
5200
|
function validateOptionalFee(fee) {
|
|
5617
5201
|
if (fee !== void 0 && !ALL_FEE_TIERS.some((tier) => Number(tier) === Number(fee))) throw new GSwapSDKError("INVALID_FEE", {
|
|
@@ -5765,7 +5349,7 @@ var Swaps = class {
|
|
|
5765
5349
|
this.symbols = symbols;
|
|
5766
5350
|
this.signer = signer;
|
|
5767
5351
|
this.walletAddress = walletAddress;
|
|
5768
|
-
this.uniqueKeyPrefix = resolveUniqueKeyPrefix(options.uniqueKeyPrefix);
|
|
5352
|
+
this.uniqueKeyPrefix = (0, _gala_chain_gala_core_sdk.resolveUniqueKeyPrefix)(options.uniqueKeyPrefix);
|
|
5769
5353
|
}
|
|
5770
5354
|
uniqueKeyPrefix;
|
|
5771
5355
|
/**
|
|
@@ -5817,8 +5401,8 @@ var Swaps = class {
|
|
|
5817
5401
|
*/
|
|
5818
5402
|
async buildSwap(tokenIn, tokenOut, fee, amount, options) {
|
|
5819
5403
|
validateFee(fee);
|
|
5820
|
-
const uniqueKey = (options === null || options === void 0 ? void 0 : options.uniqueKey) ?? createUniqueKey(this.uniqueKeyPrefix);
|
|
5821
|
-
assertUniqueKey(uniqueKey);
|
|
5404
|
+
const uniqueKey = (options === null || options === void 0 ? void 0 : options.uniqueKey) ?? (0, _gala_chain_gala_core_sdk.createUniqueKey)(this.uniqueKeyPrefix);
|
|
5405
|
+
(0, _gala_chain_gala_core_sdk.assertUniqueKey)(uniqueKey);
|
|
5822
5406
|
const tokenInSymbol = await tradingSymbolOf(this.symbols, tokenIn);
|
|
5823
5407
|
const tokenOutSymbol = await tradingSymbolOf(this.symbols, tokenOut);
|
|
5824
5408
|
const ordered = this.orientPool(tokenInSymbol, tokenOutSymbol, options === null || options === void 0 ? void 0 : options.pool);
|
|
@@ -5860,7 +5444,7 @@ var Swaps = class {
|
|
|
5860
5444
|
async signRequestTrade(dto) {
|
|
5861
5445
|
assertCanonicalV2Dto("RequestTrade", dto);
|
|
5862
5446
|
if (this.signer === void 0) throw GSwapSDKError.noSignerError();
|
|
5863
|
-
return signPreSend(this.signer, "RequestTrade", dto);
|
|
5447
|
+
return (0, _gala_chain_gala_core_sdk.signPreSend)(this.signer, "RequestTrade", dto);
|
|
5864
5448
|
}
|
|
5865
5449
|
/** The pool's two symbols in contract order: the quote's own when pinned, else string order. */
|
|
5866
5450
|
orientPool(tokenInSymbol, tokenOutSymbol, pool) {
|
|
@@ -5890,7 +5474,7 @@ var Swaps = class {
|
|
|
5890
5474
|
* ```
|
|
5891
5475
|
*/
|
|
5892
5476
|
async confirmSwap(uniqueKey, options) {
|
|
5893
|
-
assertUniqueKey(uniqueKey);
|
|
5477
|
+
(0, _gala_chain_gala_core_sdk.assertUniqueKey)(uniqueKey);
|
|
5894
5478
|
return new SubmittedTransaction({
|
|
5895
5479
|
method: "RequestTrade",
|
|
5896
5480
|
uniqueKey,
|
|
@@ -5913,7 +5497,8 @@ var Swaps = class {
|
|
|
5913
5497
|
/**
|
|
5914
5498
|
* Main entry point for the current GalaChainDex SDK: one client bundling the services that read
|
|
5915
5499
|
* and write through the swap backend (`quoting`, `swaps`, `pools`, `positions`, `assets`,
|
|
5916
|
-
* `symbols`, `fees`, `stream`). See {@link GSwapOptions} for
|
|
5500
|
+
* `market`, `leaderboard`, `symbols`, `fees`, `stream`). See {@link GSwapOptions} for
|
|
5501
|
+
* configuration.
|
|
5917
5502
|
*/
|
|
5918
5503
|
var GSwap = class {
|
|
5919
5504
|
/** The swap backend base URL every request goes to, without a trailing slash. */
|
|
@@ -5938,6 +5523,10 @@ var GSwap = class {
|
|
|
5938
5523
|
swaps;
|
|
5939
5524
|
/** User token balances. */
|
|
5940
5525
|
assets;
|
|
5526
|
+
/** USD prices, price series and the platform volume totals. */
|
|
5527
|
+
market;
|
|
5528
|
+
/** Trading-competition seasons and rankings. */
|
|
5529
|
+
leaderboard;
|
|
5941
5530
|
/** Network-fee quotes (DryRun) signed with this client's signer. */
|
|
5942
5531
|
fees;
|
|
5943
5532
|
/** The realtime stream endpoint {@link GSwap.stream} connects to. */
|
|
@@ -5980,6 +5569,8 @@ var GSwap = class {
|
|
|
5980
5569
|
this.positions = new Positions(this.gateway, resolver, this.signer, this.walletAddress, keyOptions);
|
|
5981
5570
|
this.swaps = new Swaps(this.gateway, resolver, this.signer, this.walletAddress, keyOptions);
|
|
5982
5571
|
this.assets = new Assets(this.dexBackendBaseUrl, httpClient);
|
|
5572
|
+
this.market = new Market(this.dexBackendBaseUrl, httpClient, lenient);
|
|
5573
|
+
this.leaderboard = new Leaderboard(this.dexBackendBaseUrl, httpClient, lenient);
|
|
5983
5574
|
this.fees = new DexFees(this.gateway, this.signer);
|
|
5984
5575
|
this.dexBackendWsUrl = (options === null || options === void 0 ? void 0 : options.dexBackendWsUrl) ?? ((options === null || options === void 0 ? void 0 : options.dexBackendBaseUrl) === void 0 ? environment.dexBackendWsUrl : `${this.dexBackendBaseUrl.replace(/^http/u, "ws")}/ws`);
|
|
5985
5576
|
this.webSocketImpl = options === null || options === void 0 ? void 0 : options.webSocketImpl;
|
|
@@ -6096,6 +5687,13 @@ const BY_CODE = {
|
|
|
6096
5687
|
GATEWAY_SYNC_REQUEST_TRADE: UNKNOWN,
|
|
6097
5688
|
MISSING_UNIQUE_KEY: UNKNOWN,
|
|
6098
5689
|
UNIQUE_KEY_MISMATCH: UNKNOWN,
|
|
5690
|
+
GATEWAY_WRITE_OUTCOME_UNKNOWN: UNKNOWN,
|
|
5691
|
+
GATEWAY_RESPONSE_INVALID: UNKNOWN,
|
|
5692
|
+
BRIDGE_RESPONSE_INVALID: UNKNOWN,
|
|
5693
|
+
BRIDGE_WRITE_OUTCOME_UNKNOWN: UNKNOWN,
|
|
5694
|
+
BRIDGE_OUT_OUTCOME_UNKNOWN: UNKNOWN,
|
|
5695
|
+
BRIDGE_DEPOSIT_OUTCOME_UNKNOWN: UNKNOWN,
|
|
5696
|
+
BRIDGE_STATUS_TIMEOUT: UNKNOWN,
|
|
6099
5697
|
SHADOW_NOT_QUEUED: NOT_QUEUED_FINAL,
|
|
6100
5698
|
METHOD_NOT_ALLOWED: NOT_QUEUED,
|
|
6101
5699
|
SIGNATURE_INVALID: NOT_QUEUED,
|
|
@@ -6108,6 +5706,14 @@ const BY_CODE = {
|
|
|
6108
5706
|
CHAIN_REJECTED: REJECTED,
|
|
6109
5707
|
INVALID_CHAIN_RESPONSE: TRANSIENT,
|
|
6110
5708
|
INVALID_POSITION_RESPONSE: TRANSIENT,
|
|
5709
|
+
GATEWAY_READ_REJECTED: TRANSIENT,
|
|
5710
|
+
GATEWAY_READ_RESPONSE_INVALID: TRANSIENT,
|
|
5711
|
+
BRIDGE_READ_RESPONSE_INVALID: TRANSIENT,
|
|
5712
|
+
BRIDGE_TOKEN_NOT_FOUND: INVALID_INPUT,
|
|
5713
|
+
TOKEN_NOT_WRAPPABLE: INVALID_INPUT,
|
|
5714
|
+
BRIDGE_CHAIN_READ_FAILED: TRANSIENT,
|
|
5715
|
+
TOKEN_CLASS_NOT_FOUND: INVALID_INPUT,
|
|
5716
|
+
PAGINATION_LIMIT_EXCEEDED: INVALID_INPUT,
|
|
6111
5717
|
CONFIRMATION_TIMEOUT: UNKNOWN,
|
|
6112
5718
|
CONFIRMATION_ABORTED: UNKNOWN,
|
|
6113
5719
|
CONFIRMATION_FAILED: UNKNOWN,
|
|
@@ -6121,6 +5727,7 @@ const BY_CODE = {
|
|
|
6121
5727
|
SIGNING_FAILED: NOT_SENT,
|
|
6122
5728
|
KEY_PROVIDER_FAILED: NOT_SENT,
|
|
6123
5729
|
TOKEN_RESOLUTION_FAILED: NOT_SENT,
|
|
5730
|
+
TOKEN_NAME_RESOLUTION_FAILED: NOT_SENT,
|
|
6124
5731
|
NO_WALLET_ADDRESS: INVALID_INPUT,
|
|
6125
5732
|
NO_POOL_AVAILABLE: INVALID_INPUT,
|
|
6126
5733
|
INSUFFICIENT_LIQUIDITY: INVALID_INPUT
|
|
@@ -6132,11 +5739,11 @@ const BY_CODE = {
|
|
|
6132
5739
|
* it is `not_sent` and Retry is safe.
|
|
6133
5740
|
*/
|
|
6134
5741
|
function classifyInvalidPrivateKey(view) {
|
|
6135
|
-
return stringParamOf(view, "reason") === "no_key_provided" ? NOT_SENT : INVALID_INPUT;
|
|
5742
|
+
return (0, _gala_chain_gala_core_sdk.stringParamOf)(view, "reason") === "no_key_provided" ? NOT_SENT : INVALID_INPUT;
|
|
6136
5743
|
}
|
|
6137
5744
|
/** The status of an HTTP failure: the one it carries, else its `status` param. */
|
|
6138
5745
|
function statusOfView(view) {
|
|
6139
|
-
return view.status ?? numberParamOf(view, "status");
|
|
5746
|
+
return view.status ?? (0, _gala_chain_gala_core_sdk.numberParamOf)(view, "status");
|
|
6140
5747
|
}
|
|
6141
5748
|
/**
|
|
6142
5749
|
* An HTTP failure with no gateway or chain code: its meaning depends on the status and on read vs
|
|
@@ -6145,7 +5752,7 @@ function statusOfView(view) {
|
|
|
6145
5752
|
function classifyHttpError(view) {
|
|
6146
5753
|
const status = statusOfView(view);
|
|
6147
5754
|
if (status === void 0) return UNKNOWN;
|
|
6148
|
-
if (isWriteView(view)) {
|
|
5755
|
+
if ((0, _gala_chain_gala_core_sdk.isWriteView)(view)) {
|
|
6149
5756
|
if (status === 429) return NOT_QUEUED;
|
|
6150
5757
|
if (status === 408 || status >= 500) return UNKNOWN;
|
|
6151
5758
|
return NOT_QUEUED_FINAL;
|
|
@@ -6153,13 +5760,21 @@ function classifyHttpError(view) {
|
|
|
6153
5760
|
return status === 408 || status === 429 || status >= 500 ? TRANSIENT : INVALID_INPUT;
|
|
6154
5761
|
}
|
|
6155
5762
|
/**
|
|
5763
|
+
* A core `./bridge` GalaConnect HTTP failure: like {@link classifyHttpError}, except that a
|
|
5764
|
+
* WRITE answered 409 is `unknown`: GalaChain's "this uniqueKey was already consumed", after which
|
|
5765
|
+
* an earlier attempt under the key may be applied (a bridge-out that locked the tokens).
|
|
5766
|
+
*/
|
|
5767
|
+
function classifyGalaConnectHttpError(view) {
|
|
5768
|
+
return (0, _gala_chain_gala_core_sdk.isWriteView)(view) && statusOfView(view) === 409 ? UNKNOWN : classifyHttpError(view);
|
|
5769
|
+
}
|
|
5770
|
+
/**
|
|
6156
5771
|
* The version wall (HTTP 426). On a write the gateway refused it before queueing anything, and a
|
|
6157
5772
|
* repeat of the same app version is refused the same way, so it is `not_queued` with no Retry. On a
|
|
6158
5773
|
* read nothing changed either, and only an update helps: `invalid_input`, no Retry. Both say "show
|
|
6159
5774
|
* the update screen", not "try again".
|
|
6160
5775
|
*/
|
|
6161
5776
|
function classifyUpdateRequired(view) {
|
|
6162
|
-
return isWriteView(view) ? NOT_QUEUED_FINAL : INVALID_INPUT;
|
|
5777
|
+
return (0, _gala_chain_gala_core_sdk.isWriteView)(view) ? NOT_QUEUED_FINAL : INVALID_INPUT;
|
|
6163
5778
|
}
|
|
6164
5779
|
/**
|
|
6165
5780
|
* The chain dispatch failed. The gateway echoes the upstream status: 409 is "this uniqueKey was
|
|
@@ -6173,7 +5788,17 @@ function classifyChainDispatchFailed(view) {
|
|
|
6173
5788
|
return status === 422 ? REJECTED : NOT_QUEUED;
|
|
6174
5789
|
}
|
|
6175
5790
|
/**
|
|
6176
|
-
*
|
|
5791
|
+
* GalaChain answered a core `GatewayWrites` token-contract write with a `Status` other than 1: the
|
|
5792
|
+
* chain's verdict, so the write was not applied (`rejected`), except where the verdict does not
|
|
5793
|
+
* prove that: a 409 is "this uniqueKey was already consumed" (an earlier attempt under it may be
|
|
5794
|
+
* applied), and a 5xx, or no status at all, cannot be judged.
|
|
5795
|
+
*/
|
|
5796
|
+
function classifyGatewayWriteRejected(view) {
|
|
5797
|
+
const status = view.status;
|
|
5798
|
+
return status === void 0 || status === 409 || status >= 500 ? UNKNOWN : REJECTED;
|
|
5799
|
+
}
|
|
5800
|
+
/**
|
|
5801
|
+
* Classifies any thrown value from its code (and, for a few codes, its HTTP status), never from
|
|
6177
5802
|
* message text, so an app does not have to re-derive "may I offer Retry?" and "is the outcome
|
|
6178
5803
|
* unknown?" for every code. The decision for each code is in this module's table; a new catalog
|
|
6179
5804
|
* code cannot be added without one.
|
|
@@ -6223,7 +5848,7 @@ function classifyChainDispatchFailed(view) {
|
|
|
6223
5848
|
* ```
|
|
6224
5849
|
*/
|
|
6225
5850
|
function classifyFailure(error, options = {}) {
|
|
6226
|
-
const result = classifyView(toFailureView(error));
|
|
5851
|
+
const result = classifyView((0, _gala_chain_gala_core_sdk.toFailureView)(error));
|
|
6227
5852
|
return options.afterQueued === true && result.outcome !== "rejected" ? {
|
|
6228
5853
|
...result,
|
|
6229
5854
|
retryable: false
|
|
@@ -6231,7 +5856,7 @@ function classifyFailure(error, options = {}) {
|
|
|
6231
5856
|
}
|
|
6232
5857
|
/**
|
|
6233
5858
|
* The classification of a failure view: the table decision for its code, the status-dependent
|
|
6234
|
-
* decision for the
|
|
5859
|
+
* decision for the codes whose meaning needs it, and for a code the catalog does not define
|
|
6235
5860
|
* (a wallet error, an ethers `ACTION_REJECTED`, a typo, an app's own error): `unknown` unless the
|
|
6236
5861
|
* view DECLARES its `outcome` (an app's error, `{ code: 'POSITION_STALE', outcome: 'not_sent' }`),
|
|
6237
5862
|
* then the SDK's own classification of that outcome. A value with no view is `unknown`.
|
|
@@ -6243,7 +5868,9 @@ function classifyView(view) {
|
|
|
6243
5868
|
return view.retryable === false ? classification(view.outcome, { retryable: false }) : classification(view.outcome);
|
|
6244
5869
|
}
|
|
6245
5870
|
const { code } = view;
|
|
6246
|
-
if (code === "HTTP_ERROR") return { ...classifyHttpError(view) };
|
|
5871
|
+
if (code === "HTTP_ERROR" || code === "GATEWAY_HTTP_ERROR") return { ...classifyHttpError(view) };
|
|
5872
|
+
if (code === "GALACONNECT_HTTP_ERROR") return { ...classifyGalaConnectHttpError(view) };
|
|
5873
|
+
if (code === "GATEWAY_WRITE_REJECTED" || code === "BRIDGE_REQUEST_REJECTED") return { ...classifyGatewayWriteRejected(view) };
|
|
6247
5874
|
if (code === "UPDATE_REQUIRED") return { ...classifyUpdateRequired(view) };
|
|
6248
5875
|
if (code === "CHAIN_DISPATCH_FAILED") return { ...classifyChainDispatchFailed(view) };
|
|
6249
5876
|
if (code === "INVALID_PRIVATE_KEY") return { ...classifyInvalidPrivateKey(view) };
|
|
@@ -6422,7 +6049,7 @@ var PendingSwapStore = class {
|
|
|
6422
6049
|
* `invalid_unique_key`.
|
|
6423
6050
|
*/
|
|
6424
6051
|
remember(swap) {
|
|
6425
|
-
if (!isValidUniqueKey(swap.uniqueKey)) {
|
|
6052
|
+
if (!(0, _gala_chain_gala_core_sdk.isValidUniqueKey)(swap.uniqueKey)) {
|
|
6426
6053
|
this.report([{
|
|
6427
6054
|
swap,
|
|
6428
6055
|
reason: "invalid_unique_key"
|
|
@@ -6500,7 +6127,7 @@ var PendingSwapStore = class {
|
|
|
6500
6127
|
}) ?? [];
|
|
6501
6128
|
}
|
|
6502
6129
|
pruneReasonOf(entry, now) {
|
|
6503
|
-
if (!isValidUniqueKey(entry.uniqueKey)) return "invalid_unique_key";
|
|
6130
|
+
if (!(0, _gala_chain_gala_core_sdk.isValidUniqueKey)(entry.uniqueKey)) return "invalid_unique_key";
|
|
6504
6131
|
return now - entry.createdAt < 6048e5 ? void 0 : "expired";
|
|
6505
6132
|
}
|
|
6506
6133
|
/** Tells `onPrune` about each dropped entry; the hook is diagnostics and cannot break the store. */
|
|
@@ -6561,796 +6188,12 @@ var PendingSwapStore = class {
|
|
|
6561
6188
|
};
|
|
6562
6189
|
|
|
6563
6190
|
//#endregion
|
|
6564
|
-
//#region src/
|
|
6565
|
-
/**
|
|
6566
|
-
* Genuine EIP-712 typed-data signing for the four v2 dex-contract operations proven (2026-09-16,
|
|
6567
|
-
* live bisection against stage — see defi-backend's `chain-findings-artifact` memory /
|
|
6568
|
-
* https://claude.ai/artifact/3N9MevSjoQdU7gqnVB3dfU) to recover the `client|` identity correctly
|
|
6569
|
-
* via `eth_signTypedData_v4`, unlike `personal_sign` (which only ever resolves the bare `eth|`
|
|
6570
|
-
* form — see `BrowserWalletSigner`'s own doc comment). Mirrors frontend_v2's
|
|
6571
|
-
* `src/Services/eip712Types.ts` + `readWriteUtilsV2.ts`'s `EIP712_FIELD_COMPLETERS` exactly —
|
|
6572
|
-
* ported here rather than shared as a package because gswap-sdk has no frontend_v2 dependency
|
|
6573
|
-
* and should not gain one for four small functions.
|
|
6574
|
-
*/
|
|
6191
|
+
//#region src/classes/storage_adapters.ts
|
|
6575
6192
|
/**
|
|
6576
|
-
*
|
|
6577
|
-
*
|
|
6578
|
-
*
|
|
6579
|
-
*
|
|
6580
|
-
* whose `types` leave a DTO field uncovered (`assertNoUnsignedFields`, the fix for the
|
|
6581
|
-
* 2026-08-18 unsigned-field-injection drain); when that happens signature recovery fails
|
|
6582
|
-
* outright and the chain reports "Missing signerPublicKey or signerAddress field in dto".
|
|
6583
|
-
*/
|
|
6584
|
-
function generateEIP712Types(typeName, params) {
|
|
6585
|
-
const types = {};
|
|
6586
|
-
types[typeName] = [];
|
|
6587
|
-
function fieldsFor(name) {
|
|
6588
|
-
const fields = types[name];
|
|
6589
|
-
/* c8 ignore next */
|
|
6590
|
-
if (!fields) throw new Error(`Missing EIP-712 type definition for ${name}`);
|
|
6591
|
-
return fields;
|
|
6592
|
-
}
|
|
6593
|
-
function addField(name, fieldValue, parentTypeName, onlyGetType = false) {
|
|
6594
|
-
if (Array.isArray(fieldValue)) {
|
|
6595
|
-
const type = fieldValue.length > 0 ? addField(name, fieldValue[0], parentTypeName, true) : "string";
|
|
6596
|
-
if (!onlyGetType) fieldsFor(parentTypeName).push({
|
|
6597
|
-
name,
|
|
6598
|
-
type: (type ?? name) + "[]"
|
|
6599
|
-
});
|
|
6600
|
-
} else if (typeof fieldValue === "object" && fieldValue !== null) {
|
|
6601
|
-
if (types[name]) throw new GSwapSDKError("EIP712_NAME_COLLISION", { name });
|
|
6602
|
-
types[name] = [];
|
|
6603
|
-
Object.entries(fieldValue).forEach(([key, value]) => {
|
|
6604
|
-
addField(key, value, name);
|
|
6605
|
-
});
|
|
6606
|
-
if (!onlyGetType) fieldsFor(parentTypeName).push({
|
|
6607
|
-
name,
|
|
6608
|
-
type: name
|
|
6609
|
-
});
|
|
6610
|
-
} else {
|
|
6611
|
-
let eipType;
|
|
6612
|
-
switch (typeof fieldValue) {
|
|
6613
|
-
case "string":
|
|
6614
|
-
eipType = "string";
|
|
6615
|
-
break;
|
|
6616
|
-
case "number":
|
|
6617
|
-
eipType = "int256";
|
|
6618
|
-
break;
|
|
6619
|
-
case "boolean":
|
|
6620
|
-
eipType = "bool";
|
|
6621
|
-
break;
|
|
6622
|
-
case "bigint":
|
|
6623
|
-
case "symbol":
|
|
6624
|
-
case "undefined":
|
|
6625
|
-
case "function":
|
|
6626
|
-
case "object": throw new GSwapSDKError("EIP712_UNSUPPORTED_VALUE", { valueType: typeof fieldValue });
|
|
6627
|
-
}
|
|
6628
|
-
if (onlyGetType) return eipType;
|
|
6629
|
-
fieldsFor(parentTypeName).push({
|
|
6630
|
-
name,
|
|
6631
|
-
type: eipType
|
|
6632
|
-
});
|
|
6633
|
-
}
|
|
6634
|
-
}
|
|
6635
|
-
Object.entries(params).forEach(([key, value]) => {
|
|
6636
|
-
addField(key, value, typeName);
|
|
6637
|
-
});
|
|
6638
|
-
return types;
|
|
6639
|
-
}
|
|
6640
|
-
/**
|
|
6641
|
-
* Builds the EIP-712 domain from the wallet's LIVE `eth_chainId` result, exactly like frontend_v2
|
|
6642
|
-
* (DEFI-1895): a wallet hashes whatever domain it is handed, and MetaMask refuses a `chainId` that
|
|
6643
|
-
* differs from its active network, so a hardcoded `1` breaks every wallet that is not on Mainnet.
|
|
6644
|
-
* defi-backend recovers the signer from the `domain` the payload carries (live-proven for chain
|
|
6645
|
-
* ids 1, 56 and 11155111), so following the wallet is correct on every network.
|
|
6646
|
-
*
|
|
6647
|
-
* @param chainId - The raw `eth_chainId` result: a `0x` hex string (the JSON-RPC form) or an integer.
|
|
6648
|
-
* @throws GSwapSDKError (`INVALID_CHAIN_ID`) when the wallet answers with anything else.
|
|
6649
|
-
*/
|
|
6650
|
-
function eip712DomainFor(chainId) {
|
|
6651
|
-
const parsed = typeof chainId === "string" && /^0x[0-9a-fA-F]+$/u.test(chainId) ? Number(chainId) : typeof chainId === "number" ? chainId : NaN;
|
|
6652
|
-
if (!Number.isSafeInteger(parsed) || parsed <= 0) throw new GSwapSDKError("INVALID_CHAIN_ID", { chainId: typeof chainId === "string" || typeof chainId === "number" ? String(chainId) : typeof chainId });
|
|
6653
|
-
return {
|
|
6654
|
-
name: "ethereum",
|
|
6655
|
-
chainId: parsed
|
|
6656
|
-
};
|
|
6657
|
-
}
|
|
6658
|
-
/**
|
|
6659
|
-
* The four v2 operations proven to recover the signer correctly via genuine EIP-712: `RequestTrade`
|
|
6660
|
-
* never had the problem (one numeric field). `AddLiquidity`/`RemoveLiquidity`/
|
|
6661
|
-
* `CollectPositionFees` each need their `types` completed with a handful of fields their real
|
|
6662
|
-
* server-side DTO always carries — see the `complete*Fields` functions below. `CreatePool` is a
|
|
6663
|
-
* genuinely different, unsolved problem (nested `TokenClassKey` structs, not a flat
|
|
6664
|
-
* missing-field gap) and is NOT in this set — callers must keep using `personal-sign` for it.
|
|
6665
|
-
*/
|
|
6666
|
-
const EIP712_CAPABLE_METHODS = /* @__PURE__ */ new Set([
|
|
6667
|
-
"RequestTrade",
|
|
6668
|
-
"AddLiquidity",
|
|
6669
|
-
"RemoveLiquidity",
|
|
6670
|
-
"CollectPositionFees"
|
|
6671
|
-
]);
|
|
6672
|
-
/**
|
|
6673
|
-
* Every `complete*Fields` function below shares the same two live-proven rules:
|
|
6674
|
-
* 1. Numeric completeness fields MUST be JS `number`, never numeric strings —
|
|
6675
|
-
* `generateEIP712Types` infers a field's Solidity type from `typeof`, and declaring one as
|
|
6676
|
-
* EIP-712 `string` instead of `int256` breaks recovery identically to omitting the field
|
|
6677
|
-
* entirely (verified live on AddLiquidity: `amount0Min: '0'` fails with the exact same
|
|
6678
|
-
* "Missing signerPublicKey" error as `amount0Min` being absent; `amount0Min: 0` succeeds).
|
|
6679
|
-
* 2. `positionId` has no such constraint — an empty string works, and does not target an
|
|
6680
|
-
* existing position (`positionId: ''` created a fresh position on AddLiquidity, not an add
|
|
6681
|
-
* to position id `''`; on RemoveLiquidity/CollectPositionFees the real position is still
|
|
6682
|
-
* resolved from `token0`/`token1`/`fee`/`tickLower`/`tickUpper`, not from this field).
|
|
6683
|
-
*/
|
|
6684
|
-
function numericOr0(dto, field) {
|
|
6685
|
-
return typeof dto[field] === "number" ? dto[field] : 0;
|
|
6686
|
-
}
|
|
6687
|
-
/**
|
|
6688
|
-
* `AddLiquidity`'s real server-side DTO always carries `amount0Desired`/`amount0Min`/
|
|
6689
|
-
* `amount1Desired`/`amount1Min`/`positionId` once reconstructed, regardless of which deposit
|
|
6690
|
-
* convention the caller used (`Positions.addLiquidityByTicks` only ever sends
|
|
6691
|
-
* `depositQuantityToken0`/`depositQuantityToken1`). Proven empirically by bisection: none of the
|
|
6692
|
-
* five alone fixes recovery, all five together does (live 201, tx `407d9b24...`, block
|
|
6693
|
-
* `2429022`).
|
|
6694
|
-
*/
|
|
6695
|
-
function completeAddLiquidityFields(dto) {
|
|
6696
|
-
return {
|
|
6697
|
-
...dto,
|
|
6698
|
-
amount0Desired: numericOr0(dto, "amount0Desired"),
|
|
6699
|
-
amount0Min: numericOr0(dto, "amount0Min"),
|
|
6700
|
-
amount1Desired: numericOr0(dto, "amount1Desired"),
|
|
6701
|
-
amount1Min: numericOr0(dto, "amount1Min"),
|
|
6702
|
-
positionId: dto["positionId"] ?? ""
|
|
6703
|
-
};
|
|
6704
|
-
}
|
|
6705
|
-
/**
|
|
6706
|
-
* `RemoveLiquidity`'s real server-side DTO always carries `amount`/`amount0Min`/`amount1Min`/
|
|
6707
|
-
* `positionId` — `Positions.removeLiquidity` only ever sends a `withdrawalQuantityTokenX` (or
|
|
6708
|
-
* neither, for a full close). Proven empirically: every subset of these four fails, all four
|
|
6709
|
-
* together succeeds (live, random-signer probe — recovery confirmed by the response moving from
|
|
6710
|
-
* "Missing signerPublicKey" to a genuine position-not-found 404).
|
|
6711
|
-
*/
|
|
6712
|
-
function completeRemoveLiquidityFields(dto) {
|
|
6713
|
-
return {
|
|
6714
|
-
...dto,
|
|
6715
|
-
amount: numericOr0(dto, "amount"),
|
|
6716
|
-
amount0Min: numericOr0(dto, "amount0Min"),
|
|
6717
|
-
amount1Min: numericOr0(dto, "amount1Min"),
|
|
6718
|
-
positionId: dto["positionId"] ?? ""
|
|
6719
|
-
};
|
|
6720
|
-
}
|
|
6721
|
-
/**
|
|
6722
|
-
* `CollectPositionFees`'s real server-side DTO always carries `amount0Requested`/
|
|
6723
|
-
* `amount1Requested`/`positionId` — `Positions.collectPositionFees` sends only the five identity
|
|
6724
|
-
* fields. Proven empirically the same way as `completeRemoveLiquidityFields`.
|
|
6725
|
-
*/
|
|
6726
|
-
function completeCollectPositionFeesFields(dto) {
|
|
6727
|
-
return {
|
|
6728
|
-
...dto,
|
|
6729
|
-
amount0Requested: numericOr0(dto, "amount0Requested"),
|
|
6730
|
-
amount1Requested: numericOr0(dto, "amount1Requested"),
|
|
6731
|
-
positionId: dto["positionId"] ?? ""
|
|
6732
|
-
};
|
|
6733
|
-
}
|
|
6734
|
-
const EIP712_FIELD_COMPLETERS = {
|
|
6735
|
-
AddLiquidity: completeAddLiquidityFields,
|
|
6736
|
-
RemoveLiquidity: completeRemoveLiquidityFields,
|
|
6737
|
-
CollectPositionFees: completeCollectPositionFeesFields
|
|
6738
|
-
};
|
|
6739
|
-
/**
|
|
6740
|
-
* Completes a DTO's fields for EIP-712 signing (a no-op for methods with no known gap, e.g.
|
|
6741
|
-
* `RequestTrade`) and derives its `types`. Exported separately from the signing call itself so tests
|
|
6742
|
-
* can assert on the exact payload without needing a live provider.
|
|
6743
|
-
*/
|
|
6744
|
-
function buildEIP712Payload(methodName, dto) {
|
|
6745
|
-
const completer = EIP712_FIELD_COMPLETERS[methodName];
|
|
6746
|
-
const completedDto = completer ? completer(dto) : dto;
|
|
6747
|
-
return {
|
|
6748
|
-
completedDto,
|
|
6749
|
-
types: generateEIP712Types(methodName, completedDto)
|
|
6750
|
-
};
|
|
6751
|
-
}
|
|
6752
|
-
|
|
6753
|
-
//#endregion
|
|
6754
|
-
//#region src/utils/signing.ts
|
|
6755
|
-
/**
|
|
6756
|
-
* SDK-owned signing primitives.
|
|
6757
|
-
*
|
|
6758
|
-
* Everything the SDK needs to produce a GalaChain signature lives here, with no dependency on
|
|
6759
|
-
* `@gala-chain/api` (which drags `secp256k1` native bindings, `reflect-metadata`, `class-validator`
|
|
6760
|
-
* and its own `bignumber.js` major into every consumer). The only runtime dependencies are the
|
|
6761
|
-
* audited, pure-JS `@noble/curves` (secp256k1) and `@noble/hashes` (keccak256), which run
|
|
6762
|
-
* unchanged in browsers, MV3 service workers, Node and React Native/Hermes.
|
|
6763
|
-
*
|
|
6764
|
-
* Every function here is pinned byte-for-byte to the signatures produced by `@gala-chain/api`
|
|
6765
|
-
* (`serialize`, `signatures.getSignature`), `@gala-chain/connect`, the Gala Wallet extension and
|
|
6766
|
-
* frontend_v2 by the golden-vector suite in `tests/signing-golden.test.ts`.
|
|
6767
|
-
*/
|
|
6768
|
-
const textEncoder$1 = new TextEncoder();
|
|
6769
|
-
function toDeterministicNode(node) {
|
|
6770
|
-
if (node === null || node === void 0) return node;
|
|
6771
|
-
if (node instanceof RegExp) return node.toString();
|
|
6772
|
-
const candidate = node;
|
|
6773
|
-
return typeof candidate.toJSON === "function" ? Reflect.apply(candidate.toJSON, node, []) : node;
|
|
6774
|
-
}
|
|
6775
|
-
function deterministicNode(rawNode) {
|
|
6776
|
-
const node = toDeterministicNode(rawNode);
|
|
6777
|
-
if (node === void 0) return void 0;
|
|
6778
|
-
if (typeof node !== "object" || node === null) return JSON.stringify(node);
|
|
6779
|
-
if (Array.isArray(node)) return `[${node.map((item) => deterministicNode(item) ?? "null").join(",")}]`;
|
|
6780
|
-
const record = node;
|
|
6781
|
-
const members = [];
|
|
6782
|
-
for (const key of Object.keys(record).sort()) {
|
|
6783
|
-
const value = deterministicNode(record[key]);
|
|
6784
|
-
if (value === void 0) continue;
|
|
6785
|
-
members.push(`${JSON.stringify(key)}:${value}`);
|
|
6786
|
-
}
|
|
6787
|
-
return `{${members.join(",")}}`;
|
|
6788
|
-
}
|
|
6789
|
-
/**
|
|
6790
|
-
* GalaChain's deterministic JSON: object keys sorted (UTF-16 code-unit order) at every depth, no
|
|
6791
|
-
* whitespace, `undefined`/function members dropped, `toJSON()` honored. Byte-identical to
|
|
6792
|
-
* `serialize` from `@gala-chain/api` / `json-stringify-deterministic` for every JSON-shaped
|
|
6793
|
-
* value, which is all a DTO ever is.
|
|
6794
|
-
*
|
|
6795
|
-
* @throws TypeError for circular structures and BigInt values (as `JSON.stringify` does) and
|
|
6796
|
-
* when the value itself has no JSON representation (`undefined`, a function, a symbol).
|
|
6797
|
-
*/
|
|
6798
|
-
function serialize(value) {
|
|
6799
|
-
JSON.stringify(value);
|
|
6800
|
-
const result = deterministicNode(value);
|
|
6801
|
-
if (result === void 0) throw new TypeError("Cannot serialize a value with no JSON representation.");
|
|
6802
|
-
return result;
|
|
6803
|
-
}
|
|
6804
|
-
/**
|
|
6805
|
-
* The exact string GalaChain hashes for a DTO: the (optional, string) `prefix` field PREPENDED to
|
|
6806
|
-
* the deterministic serialization of the DTO WITHOUT `signature`, `multisig`, `trace` and `prefix`.
|
|
6807
|
-
* Equivalent to `@gala-chain/api` 3.x `getPayloadToSign` for every non-EIP-712 DTO.
|
|
6808
|
-
*/
|
|
6809
|
-
function getPayloadToSign(dto) {
|
|
6810
|
-
const { prefix, ...rest } = dto;
|
|
6811
|
-
delete rest["signature"];
|
|
6812
|
-
delete rest["multisig"];
|
|
6813
|
-
delete rest["trace"];
|
|
6814
|
-
return (typeof prefix === "string" ? prefix : "") + serialize(rest);
|
|
6815
|
-
}
|
|
6816
|
-
const ETHEREUM_SIGNED_MESSAGE = String.fromCharCode(25) + "Ethereum Signed Message:\n";
|
|
6817
|
-
/**
|
|
6818
|
-
* The EIP-191 header for `message`: `"\x19Ethereum Signed Message:\n"` + the message's UTF-8 BYTE
|
|
6819
|
-
* length. This is what a wallet's `personal_sign` prepends, and what the chain recomputes when it
|
|
6820
|
-
* recovers a personal-signed DTO.
|
|
6821
|
-
*/
|
|
6822
|
-
function calculatePersonalSignPrefix(message) {
|
|
6823
|
-
return ETHEREUM_SIGNED_MESSAGE + String(textEncoder$1.encode(message).length);
|
|
6824
|
-
}
|
|
6825
|
-
/**
|
|
6826
|
-
* The EIP-191 header for `serialize(dto)` (any `prefix` already on the DTO is ignored). This is
|
|
6827
|
-
* deliberately the same string the signers hand a wallet to sign, so the header's length can never
|
|
6828
|
-
* disagree with the signed message. Wallet-neutral: it is the value both a browser wallet's
|
|
6829
|
-
* `personal_sign` and Gala Wallet's `gala_signChainDto` (personal-sign scheme) prepend.
|
|
6830
|
-
*/
|
|
6831
|
-
function personalSignPrefixFor(dto) {
|
|
6832
|
-
const withoutPrefix = { ...dto };
|
|
6833
|
-
delete withoutPrefix["prefix"];
|
|
6834
|
-
return calculatePersonalSignPrefix(serialize(withoutPrefix));
|
|
6835
|
-
}
|
|
6836
|
-
/**
|
|
6837
|
-
* The `@gala-chain/connect`-style prefix frontend_v2 puts in the body of an EIP-712 write: the
|
|
6838
|
-
* EIP-191 header whose number counts the prefix string ITSELF plus the body (the UTF-8 length of
|
|
6839
|
-
* `getPayloadToSign({ ...dto, prefix })`), found by fixpoint because adding the prefix changes the
|
|
6840
|
-
* length (a body of 99 bytes settles on 101).
|
|
6841
|
-
*
|
|
6842
|
-
* It is NOT a personal-sign prefix (a personal-sign write with this value recovers a phantom
|
|
6843
|
-
* signer; use `personalSignPrefixFor`). Under EIP-712 the field is an untyped extra that the
|
|
6844
|
-
* typed-data hash ignores and defi-backend recomputes, so it only exists to keep the submitted
|
|
6845
|
-
* body byte-identical to what frontend_v2 has always sent.
|
|
6846
|
-
*/
|
|
6847
|
-
function fixpointPrefixFor(dto) {
|
|
6848
|
-
const withoutPrefix = { ...dto };
|
|
6849
|
-
delete withoutPrefix["prefix"];
|
|
6850
|
-
let payloadLength = textEncoder$1.encode(getPayloadToSign(withoutPrefix)).length;
|
|
6851
|
-
let prefix = ETHEREUM_SIGNED_MESSAGE + String(payloadLength);
|
|
6852
|
-
let previousLength = -1;
|
|
6853
|
-
while (payloadLength !== previousLength) {
|
|
6854
|
-
previousLength = payloadLength;
|
|
6855
|
-
prefix = ETHEREUM_SIGNED_MESSAGE + String(payloadLength);
|
|
6856
|
-
payloadLength = textEncoder$1.encode(getPayloadToSign({
|
|
6857
|
-
...withoutPrefix,
|
|
6858
|
-
prefix
|
|
6859
|
-
})).length;
|
|
6860
|
-
}
|
|
6861
|
-
return prefix;
|
|
6862
|
-
}
|
|
6863
|
-
function invalidPrivateKey(reason) {
|
|
6864
|
-
return new GSwapSDKError("INVALID_PRIVATE_KEY", { reason });
|
|
6865
|
-
}
|
|
6866
|
-
const BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
6867
|
-
function decodeBase64(input) {
|
|
6868
|
-
if (!/^[A-Za-z0-9+/]+={0,2}$/u.test(input) || input.length % 4 !== 0) return void 0;
|
|
6869
|
-
const unpadded = input.replace(/=+$/u, "");
|
|
6870
|
-
const bytes = [];
|
|
6871
|
-
let buffer = 0;
|
|
6872
|
-
let bits = 0;
|
|
6873
|
-
for (const char of unpadded) {
|
|
6874
|
-
buffer = buffer << 6 | BASE64_ALPHABET.indexOf(char);
|
|
6875
|
-
bits += 6;
|
|
6876
|
-
if (bits >= 8) {
|
|
6877
|
-
bits -= 8;
|
|
6878
|
-
bytes.push(buffer >> bits & 255);
|
|
6879
|
-
}
|
|
6880
|
-
}
|
|
6881
|
-
return Uint8Array.from(bytes);
|
|
6882
|
-
}
|
|
6883
|
-
function decodeHex(input) {
|
|
6884
|
-
const bytes = new Uint8Array(input.length / 2);
|
|
6885
|
-
for (let index = 0; index < bytes.length; index += 1) bytes[index] = parseInt(input.slice(index * 2, index * 2 + 2), 16);
|
|
6886
|
-
return bytes;
|
|
6887
|
-
}
|
|
6888
|
-
/**
|
|
6889
|
-
* Normalizes a secp256k1 private key to a fresh 32-byte array (the caller may zero it). Also
|
|
6890
|
-
* accepts 32 raw bytes, which are validated and copied. For strings it accepts what `@gala-chain/api`'s
|
|
6891
|
-
* `normalizePrivateKey` accepted for a usable key: 64 hex characters (optionally `0x`-prefixed;
|
|
6892
|
-
* a 63-character hex key is left-padded with a zero) or a 44-character base64 string.
|
|
6893
|
-
*
|
|
6894
|
-
* @throws GSwapSDKError (`INVALID_PRIVATE_KEY`; `params.reason` is one of
|
|
6895
|
-
* `INVALID_PRIVATE_KEY_REASONS`: `wrong_length` for raw bytes that are not 32 long,
|
|
6896
|
-
* `bad_encoding` for any other string shape, `not_on_curve` for an out-of-range scalar).
|
|
6897
|
-
*/
|
|
6898
|
-
function normalizePrivateKey(input) {
|
|
6899
|
-
if (typeof input !== "string") {
|
|
6900
|
-
if (!(input instanceof Uint8Array) || input.length !== 32) throw invalidPrivateKey("wrong_length");
|
|
6901
|
-
if (!_noble_curves_secp256k1.secp256k1.utils.isValidSecretKey(input)) throw invalidPrivateKey("not_on_curve");
|
|
6902
|
-
return Uint8Array.from(input);
|
|
6903
|
-
}
|
|
6904
|
-
const withoutPrefix = input.startsWith("0x") ? input.slice(2) : input;
|
|
6905
|
-
let key;
|
|
6906
|
-
if (/^[0-9a-fA-F]{63,64}$/u.test(withoutPrefix)) key = decodeHex(withoutPrefix.length === 63 ? `0${withoutPrefix}` : withoutPrefix);
|
|
6907
|
-
else if (withoutPrefix.length === 44) key = decodeBase64(withoutPrefix);
|
|
6908
|
-
if ((key === null || key === void 0 ? void 0 : key.length) !== 32) throw invalidPrivateKey("bad_encoding");
|
|
6909
|
-
if (!_noble_curves_secp256k1.secp256k1.utils.isValidSecretKey(key)) throw invalidPrivateKey("not_on_curve");
|
|
6910
|
-
return key;
|
|
6911
|
-
}
|
|
6912
|
-
/**
|
|
6913
|
-
* Signs a 32-byte keccak256 hash and returns GalaChain's recoverable signature: 64 hex `r` +
|
|
6914
|
-
* 64 hex `s` + `1b`/`1c` (`v`), lowercase, no `0x`. Deterministic (RFC 6979) and low-S, exactly
|
|
6915
|
-
* what `@gala-chain/api` and the Gala Wallet produce. A DER signature carries no recovery byte
|
|
6916
|
-
* and cannot be used: the chain RECOVERS the signer from the signature.
|
|
6917
|
-
*/
|
|
6918
|
-
function signHash(privateKey, hash) {
|
|
6919
|
-
if (hash.length !== 32) throw new GSwapSDKError("INVALID_SIGNING_HASH_LENGTH", { length: hash.length });
|
|
6920
|
-
const signature = _noble_curves_secp256k1.secp256k1.sign(hash, privateKey, {
|
|
6921
|
-
prehash: false,
|
|
6922
|
-
lowS: true
|
|
6923
|
-
});
|
|
6924
|
-
return (0, _noble_hashes_utils.bytesToHex)(signature.toBytes("compact")) + (signature.recovery === 1 ? "1c" : "1b");
|
|
6925
|
-
}
|
|
6926
|
-
/**
|
|
6927
|
-
* The lowercase `0x` Ethereum address (last 20 bytes of keccak256 of the uncompressed public key)
|
|
6928
|
-
* of a normalized private key. The Gala `eth|` identity of a key is this address.
|
|
6929
|
-
*/
|
|
6930
|
-
function ethAddressOfPrivateKey(privateKey) {
|
|
6931
|
-
const publicKey = _noble_curves_secp256k1.secp256k1.getPublicKey(privateKey, false);
|
|
6932
|
-
return `0x${(0, _noble_hashes_utils.bytesToHex)((0, _noble_hashes_sha3.keccak_256)(publicKey.slice(1)).slice(-20))}`;
|
|
6933
|
-
}
|
|
6934
|
-
/** keccak256 of a string's UTF-8 bytes. */
|
|
6935
|
-
function keccak256Utf8(data) {
|
|
6936
|
-
return (0, _noble_hashes_sha3.keccak_256)(textEncoder$1.encode(data));
|
|
6937
|
-
}
|
|
6938
|
-
/**
|
|
6939
|
-
* GalaChain NATIVE signature over an already-serialized DTO: keccak256 of the exact bytes, with no
|
|
6940
|
-
* prefix and no signature envelope. The caller owns serialization, so the bytes shown to a user
|
|
6941
|
-
* and the bytes signed can never diverge.
|
|
6942
|
-
*/
|
|
6943
|
-
function signSerializedNative(privateKey, serializedDto) {
|
|
6944
|
-
return signHash(privateKey, keccak256Utf8(serializedDto));
|
|
6945
|
-
}
|
|
6946
|
-
/**
|
|
6947
|
-
* GalaChain NATIVE signature for a DTO: keccak256 of `getPayloadToSign(dto)`, signed with
|
|
6948
|
-
* secp256k1. Byte-identical to `signatures.getSignature` from `@gala-chain/api`.
|
|
6949
|
-
*/
|
|
6950
|
-
function signNative(privateKey, dto) {
|
|
6951
|
-
return signSerializedNative(privateKey, getPayloadToSign(dto));
|
|
6952
|
-
}
|
|
6953
|
-
/**
|
|
6954
|
-
* Personal-sign hash and signature: keccak256(`prefix` + `message`), both hashed VERBATIM with no
|
|
6955
|
-
* further EIP-191 wrapping. Passing `prefix = calculatePersonalSignPrefix(message)` reproduces a
|
|
6956
|
-
* wallet's `personal_sign` of `message`. Gala Wallet's `gala_signChainDto` (personal-sign
|
|
6957
|
-
* scheme) hashes `prefix` + the exact string it is handed, which can differ from the bare message
|
|
6958
|
-
* (see the `signatureOverPrefixlessBody` vectors); that quirk is why the two inputs stay separate.
|
|
6959
|
-
*/
|
|
6960
|
-
function signPersonalMessage(privateKey, prefix, message) {
|
|
6961
|
-
return signHash(privateKey, keccak256Utf8(prefix + message));
|
|
6962
|
-
}
|
|
6963
|
-
|
|
6964
|
-
//#endregion
|
|
6965
|
-
//#region src/classes/signers.ts
|
|
6966
|
-
function getWalletProvider() {
|
|
6967
|
-
var _globalObject$window;
|
|
6968
|
-
const wallet = (_globalObject$window = globalThis.window) === null || _globalObject$window === void 0 ? void 0 : _globalObject$window.gala;
|
|
6969
|
-
if (!wallet) throw new GSwapSDKError("GALA_WALLET_NOT_AVAILABLE", {});
|
|
6970
|
-
return wallet;
|
|
6971
|
-
}
|
|
6972
|
-
function getNativeSignature(value) {
|
|
6973
|
-
if (typeof value !== "string") throw new GSwapSDKError("INVALID_SIGNATURE", { scheme: "native" });
|
|
6974
|
-
if (!/^(?:0x)?[0-9a-fA-F]{130}$/u.test(value)) throw new GSwapSDKError("INVALID_SIGNATURE", { scheme: "native" });
|
|
6975
|
-
return value;
|
|
6976
|
-
}
|
|
6977
|
-
function getPersonalSignature(value) {
|
|
6978
|
-
if (typeof value !== "string" || !/^(?:0x)?[0-9a-fA-F]{130}$/u.test(value)) throw new GSwapSDKError("INVALID_SIGNATURE", { scheme: "personal-sign" });
|
|
6979
|
-
return value;
|
|
6980
|
-
}
|
|
6981
|
-
/**
|
|
6982
|
-
* The `0x`-prefixed Ethereum address a Gala account is signed by, or `undefined` when the account
|
|
6983
|
-
* names no key (a `client|` alias or a bare GalaChain id is not derivable from a key). Accepts
|
|
6984
|
-
* `0x` plus 40 hex digits and `eth|` plus 40 hex digits, with or without `0x`; the caller's casing
|
|
6985
|
-
* is kept. For {@link LazyPrivateKeySignerOptions.expectedAddress}, which should only be set for
|
|
6986
|
-
* an account whose key can be checked.
|
|
6987
|
-
*
|
|
6988
|
-
* @example
|
|
6989
|
-
* ```ts
|
|
6990
|
-
* createLazyPrivateKeySigner(getKey, { expectedAddress: ethereumAddressOfAccount(account) });
|
|
6991
|
-
* ```
|
|
6992
|
-
*/
|
|
6993
|
-
function ethereumAddressOfAccount(account) {
|
|
6994
|
-
const match = /^(?:eth\|(?:0x)?|0x)([0-9a-fA-F]{40})$/u.exec(account);
|
|
6995
|
-
return (match === null || match === void 0 ? void 0 : match[1]) === void 0 ? void 0 : `0x${match[1]}`;
|
|
6996
|
-
}
|
|
6997
|
-
function normalizeEthereumAccount(address, allowGalaPrefix) {
|
|
6998
|
-
const normalized = allowGalaPrefix ? ethereumAddressOfAccount(address) ?? address : address;
|
|
6999
|
-
if (!/^0x[0-9a-fA-F]{40}$/u.test(normalized)) throw new GSwapSDKError("INVALID_WALLET_ADDRESS", {
|
|
7000
|
-
field: "walletAddress",
|
|
7001
|
-
value: address
|
|
7002
|
-
});
|
|
7003
|
-
return normalized;
|
|
7004
|
-
}
|
|
7005
|
-
/**
|
|
7006
|
-
* The account handed to Gala Wallet as the signer address. An Ethereum account is held to the
|
|
7007
|
-
* same strict check as ever (`0x` plus 40 hex digits; `eth|` plus 40 hex digits, with or without
|
|
7008
|
-
* `0x`, is normalized to it). A GalaChain id the wallet can also be asked to sign for is passed
|
|
7009
|
-
* through verbatim: `client|` plus 24 hex digits, or the bare 24 hex digits that frontend_v2 keeps
|
|
7010
|
-
* in `state.user.wallet` after a direct `client|` login. Nothing else is accepted.
|
|
7011
|
-
*/
|
|
7012
|
-
function normalizeGalaWalletAccount(address) {
|
|
7013
|
-
const eth = /^eth\|(?:0x)?([0-9a-fA-F]{40})$/u.exec(address);
|
|
7014
|
-
if ((eth === null || eth === void 0 ? void 0 : eth[1]) !== void 0) return `0x${eth[1]}`;
|
|
7015
|
-
if (/^0x[0-9a-fA-F]{40}$/u.test(address) || /^(?:client\|)?[0-9a-fA-F]{24}$/u.test(address)) return address;
|
|
7016
|
-
throw new GSwapSDKError("INVALID_WALLET_ADDRESS", {
|
|
7017
|
-
field: "walletAddress",
|
|
7018
|
-
value: address
|
|
7019
|
-
});
|
|
7020
|
-
}
|
|
7021
|
-
/** An identity Gala Wallet can act as: an account form above, any `eth|`/`client|` alias, verbatim. */
|
|
7022
|
-
function assertWalletIdentity(identity) {
|
|
7023
|
-
if (!/^(?:eth\|(?:0x)?[0-9a-fA-F]{40}|0x[0-9a-fA-F]{40}|(?:client\|)?[0-9a-fA-F]{24})$/u.test(identity)) throw new GSwapSDKError("INVALID_WALLET_ADDRESS", {
|
|
7024
|
-
field: "selectAddress",
|
|
7025
|
-
value: identity
|
|
7026
|
-
});
|
|
7027
|
-
}
|
|
7028
|
-
/** Provider-wide queue: `setAddress` + the signature request must not interleave across signers. */
|
|
7029
|
-
const walletIdentityQueue = /* @__PURE__ */ new WeakMap();
|
|
7030
|
-
/**
|
|
7031
|
-
* Runs `signWith` with `identity` selected on a Gala Wallet that supports `setAddress`. Gala
|
|
7032
|
-
* Wallet holds ONE current account, so two signatures for different identities in flight at once
|
|
7033
|
-
* would sign as whichever selection came last; the pair (select, sign) is therefore serialized
|
|
7034
|
-
* per provider. A failed selection fails the signature (nothing is requested).
|
|
7035
|
-
*/
|
|
7036
|
-
function withSelectedIdentity(wallet, identity, signWith) {
|
|
7037
|
-
const { setAddress } = wallet;
|
|
7038
|
-
if (identity === void 0 || typeof setAddress !== "function") return signWith();
|
|
7039
|
-
const run = (walletIdentityQueue.get(wallet) ?? Promise.resolve()).catch(() => void 0).then(async () => {
|
|
7040
|
-
await setAddress.call(wallet, identity);
|
|
7041
|
-
return signWith();
|
|
7042
|
-
});
|
|
7043
|
-
walletIdentityQueue.set(wallet, run);
|
|
7044
|
-
return run;
|
|
7045
|
-
}
|
|
7046
|
-
function personalSign(provider, dto, walletAddress, methodName) {
|
|
7047
|
-
const prefix = personalSignPrefixFor(dto);
|
|
7048
|
-
const payload = {
|
|
7049
|
-
...dto,
|
|
7050
|
-
prefix
|
|
7051
|
-
};
|
|
7052
|
-
const params = methodName !== void 0 ? [
|
|
7053
|
-
serialize(payload),
|
|
7054
|
-
walletAddress,
|
|
7055
|
-
methodName
|
|
7056
|
-
] : [serialize(dto), walletAddress];
|
|
7057
|
-
return provider.request({
|
|
7058
|
-
method: methodName !== void 0 ? "gala_signChainDto" : "personal_sign",
|
|
7059
|
-
params
|
|
7060
|
-
}).then((value) => ({
|
|
7061
|
-
...payload,
|
|
7062
|
-
signature: getPersonalSignature(value)
|
|
7063
|
-
}));
|
|
7064
|
-
}
|
|
7065
|
-
/**
|
|
7066
|
-
* Signs a DTO via genuine EIP-712 (`eth_signTypedData_v4`) — correctly resolves the `client|`
|
|
7067
|
-
* identity, unlike `personal_sign`, for the operations in `EIP712_CAPABLE_METHODS`.
|
|
7068
|
-
*
|
|
7069
|
-
* Constructs the raw JSON-RPC payload by hand (`{domain, types: {...types, EIP712Domain},
|
|
7070
|
-
* primaryType, message}`) rather than depending on ethers.js, matching exactly what
|
|
7071
|
-
* `ethers.TypedDataEncoder.getPayload`/`Wallet.signTypedData` produce internally — verified
|
|
7072
|
-
* byte-for-byte equivalent (same recovered address from the same signature against both
|
|
7073
|
-
* constructions) before this shipped, since gswap-sdk deliberately has no ethers dependency.
|
|
7074
|
-
*/
|
|
7075
|
-
async function signEIP712(provider, methodName, dto, walletAddress) {
|
|
7076
|
-
const { completedDto, types } = buildEIP712Payload(methodName, dto);
|
|
7077
|
-
const domain = eip712DomainFor(await provider.request({
|
|
7078
|
-
method: "eth_chainId",
|
|
7079
|
-
params: []
|
|
7080
|
-
}));
|
|
7081
|
-
const prefix = fixpointPrefixFor(completedDto);
|
|
7082
|
-
const message = {
|
|
7083
|
-
...completedDto,
|
|
7084
|
-
prefix
|
|
7085
|
-
};
|
|
7086
|
-
const eip712Domain = [{
|
|
7087
|
-
name: "name",
|
|
7088
|
-
type: "string"
|
|
7089
|
-
}, {
|
|
7090
|
-
name: "chainId",
|
|
7091
|
-
type: "uint256"
|
|
7092
|
-
}];
|
|
7093
|
-
const payload = {
|
|
7094
|
-
domain,
|
|
7095
|
-
types: {
|
|
7096
|
-
...types,
|
|
7097
|
-
EIP712Domain: eip712Domain
|
|
7098
|
-
},
|
|
7099
|
-
primaryType: methodName,
|
|
7100
|
-
message
|
|
7101
|
-
};
|
|
7102
|
-
const value = await provider.request({
|
|
7103
|
-
method: "eth_signTypedData_v4",
|
|
7104
|
-
params: [walletAddress, JSON.stringify(payload)]
|
|
7105
|
-
});
|
|
7106
|
-
return {
|
|
7107
|
-
...message,
|
|
7108
|
-
signature: getPersonalSignature(value),
|
|
7109
|
-
types,
|
|
7110
|
-
domain
|
|
7111
|
-
};
|
|
7112
|
-
}
|
|
7113
|
-
/**
|
|
7114
|
-
* A GalaChain native signer backed by a secp256k1 private key.
|
|
7115
|
-
*
|
|
7116
|
-
* Native signing produces the bare GalaChain signature format, without a personal-sign prefix
|
|
7117
|
-
* or EIP-712 metadata. The private-key identity is recovered by the chain.
|
|
7118
|
-
*/
|
|
7119
|
-
var PrivateKeySigner = class {
|
|
7120
|
-
privateKey;
|
|
7121
|
-
/**
|
|
7122
|
-
* Creates a native signer from a hex or base64 secp256k1 private key.
|
|
7123
|
-
*
|
|
7124
|
-
* @example
|
|
7125
|
-
* ```typescript
|
|
7126
|
-
* const signer = new PrivateKeySigner('1'.padStart(64, '0'));
|
|
7127
|
-
* const signed = await signer.signObject('RequestTrade', { token0: 'GALA', token1: 'GUSDC' });
|
|
7128
|
-
* ```
|
|
7129
|
-
*/
|
|
7130
|
-
constructor(privateKey) {
|
|
7131
|
-
this.privateKey = normalizePrivateKey(privateKey);
|
|
7132
|
-
}
|
|
7133
|
-
/**
|
|
7134
|
-
* Signs the DTO with GalaChain native signing.
|
|
7135
|
-
*
|
|
7136
|
-
* @example
|
|
7137
|
-
* ```typescript
|
|
7138
|
-
* const signed = await signer.signObject('RequestTrade', {
|
|
7139
|
-
* token0: 'GALA',
|
|
7140
|
-
* token1: 'GUSDC',
|
|
7141
|
-
* fee: 3000,
|
|
7142
|
-
* });
|
|
7143
|
-
* ```
|
|
7144
|
-
*/
|
|
7145
|
-
signObject(_methodName, dto) {
|
|
7146
|
-
const signature = signNative(this.privateKey, dto);
|
|
7147
|
-
return Promise.resolve({
|
|
7148
|
-
...dto,
|
|
7149
|
-
signature
|
|
7150
|
-
});
|
|
7151
|
-
}
|
|
7152
|
-
};
|
|
7153
|
-
/**
|
|
7154
|
-
* Calls the app's key provider. Whatever it throws or rejects with is the app's own failure to
|
|
7155
|
-
* produce a key before anything was signed or sent, so it becomes `KEY_PROVIDER_FAILED` (the
|
|
7156
|
-
* original is `cause`); an SDK error is already catalogued and passes through.
|
|
7157
|
-
*/
|
|
7158
|
-
async function keyFrom(getPrivateKey) {
|
|
7159
|
-
try {
|
|
7160
|
-
return await getPrivateKey();
|
|
7161
|
-
} catch (cause) {
|
|
7162
|
-
throw isGSwapError(cause) ? cause : new GSwapSDKError("KEY_PROVIDER_FAILED", {}, { cause });
|
|
7163
|
-
}
|
|
7164
|
-
}
|
|
7165
|
-
/**
|
|
7166
|
-
* Creates the native-signing equivalent of {@link PrivateKeySigner} for apps that must not hold a
|
|
7167
|
-
* key in a long-lived object (an extension that reads its session key at sign time).
|
|
7168
|
-
*
|
|
7169
|
-
* `getPrivateKey` is called once per `signObject` call, never cached, never shared between calls
|
|
7170
|
-
* (concurrent signatures each fetch their own key), and the normalized key bytes are zeroed
|
|
7171
|
-
* before `signObject` settles, also on failure. A rejection or exception from `getPrivateKey` (the
|
|
7172
|
-
* app's "wallet is locked", a storage read that failed) is a `KEY_PROVIDER_FAILED` error with the
|
|
7173
|
-
* original error as `cause`: nothing has been sent, so `classifyFailure` says `not_sent` and Retry
|
|
7174
|
-
* is safe, and the app needs no error class of its own to tell this apart. A provider that returns
|
|
7175
|
-
* no usable string (`''`, `undefined`) is `INVALID_PRIVATE_KEY` with `reason: 'no_key_provided'`,
|
|
7176
|
-
* classified the same way; any other key that is not a valid secp256k1 key string is an
|
|
7177
|
-
* `INVALID_PRIVATE_KEY` error. An SDK error the provider throws itself is passed through.
|
|
7178
|
-
* Signatures are byte-identical to `new PrivateKeySigner(key).signObject(...)`.
|
|
7179
|
-
*
|
|
7180
|
-
* A JavaScript string cannot be wiped; the provider owns the lifetime of the string it returns.
|
|
7181
|
-
*
|
|
7182
|
-
* @example
|
|
7183
|
-
* ```typescript
|
|
7184
|
-
* const signer = createLazyPrivateKeySigner(
|
|
7185
|
-
* () => SecurityHelper.getSessionSecret(SecurityHelper.SESSION_PRIVATE_KEY_KEY),
|
|
7186
|
-
* { expectedAddress: account.address },
|
|
7187
|
-
* );
|
|
7188
|
-
* const gswap = new GSwap({ signer });
|
|
7189
|
-
* ```
|
|
7190
|
-
*/
|
|
7191
|
-
function createLazyPrivateKeySigner(getPrivateKey, options = {}) {
|
|
7192
|
-
const expected = options.expectedAddress;
|
|
7193
|
-
const expectedLower = expected === void 0 ? void 0 : normalizeEthereumAccount(expected, true).toLowerCase();
|
|
7194
|
-
return { async signObject(_methodName, dto) {
|
|
7195
|
-
const provided = await keyFrom(getPrivateKey);
|
|
7196
|
-
if (typeof provided !== "string" || provided === "") throw new GSwapSDKError("INVALID_PRIVATE_KEY", { reason: "no_key_provided" });
|
|
7197
|
-
const key = normalizePrivateKey(provided);
|
|
7198
|
-
try {
|
|
7199
|
-
if (expectedLower !== void 0 && ethAddressOfPrivateKey(key) !== expectedLower) throw new GSwapSDKError("SIGNER_ADDRESS_MISMATCH", {});
|
|
7200
|
-
return {
|
|
7201
|
-
...dto,
|
|
7202
|
-
signature: signNative(key, dto)
|
|
7203
|
-
};
|
|
7204
|
-
} finally {
|
|
7205
|
-
key.fill(0);
|
|
7206
|
-
}
|
|
7207
|
-
} };
|
|
7208
|
-
}
|
|
7209
|
-
/**
|
|
7210
|
-
* A Gala Wallet signer using native GalaChain signing, with an opt-in personal-sign mode.
|
|
7211
|
-
*
|
|
7212
|
-
* The selected Ethereum account is passed to Gala Wallet. There is NO automatic fallback: a
|
|
7213
|
-
* wallet that rejects `native` surfaces its error to the caller, because the only alternative,
|
|
7214
|
-
* `personal-sign`, silently recovers a phantom signer on every released Gala Wallet.
|
|
7215
|
-
*
|
|
7216
|
-
* `scheme: 'personal-sign'` calls
|
|
7217
|
-
* `gala_signChainDto([serialize({ ...dto, prefix }), walletAddress, methodName])` and REQUIRES
|
|
7218
|
-
* a wallet build that contains wallet_extension commit 34ca4e4 (strips the embedded `prefix`
|
|
7219
|
-
* field and hashes `prefix` + the prefix-less canonical body, the chain's scheme). Wallets
|
|
7220
|
-
* released before that fix hash the string with the `prefix` field still inside it and the
|
|
7221
|
-
* signature recovers to an address nobody holds, so only opt in once the fixed wallet is
|
|
7222
|
-
* released. See SIGNING-PARITY.md.
|
|
7223
|
-
*/
|
|
7224
|
-
var GalaWalletSigner = class {
|
|
7225
|
-
/** The wallet address supplied to Gala Wallet for each signing request. */
|
|
7226
|
-
walletAddress;
|
|
7227
|
-
/** The signing scheme used for every request; it never changes after construction. */
|
|
7228
|
-
scheme;
|
|
7229
|
-
/**
|
|
7230
|
-
* Creates a Gala Wallet signer.
|
|
7231
|
-
*
|
|
7232
|
-
* @example
|
|
7233
|
-
* ```typescript
|
|
7234
|
-
* const signer = new GalaWalletSigner('0x0123456789012345678901234567890123456789');
|
|
7235
|
-
* const signed = await signer.signObject('RequestTrade', { token0: 'GALA', token1: 'GUSDC' });
|
|
7236
|
-
* ```
|
|
7237
|
-
*
|
|
7238
|
-
* @param walletAddress - The account passed to Gala Wallet as the signer address. An Ethereum
|
|
7239
|
-
* account is `0x` plus 40 hex digits (Gala Wallet's `eth|...` form is normalized to it and held
|
|
7240
|
-
* to the same check); a GalaChain id is `client|` plus 24 hex digits, or the bare 24 hex digits
|
|
7241
|
-
* frontend_v2 keeps for a direct `client|` login, and is passed through unchanged.
|
|
7242
|
-
* @param options - Select native signing (default) or personal-sign explicitly.
|
|
7243
|
-
* `'personal-sign'` requires a Gala Wallet build with wallet_extension commit 34ca4e4.
|
|
7244
|
-
* `selectAddress` is the identity (`client|...` / `eth|...`) Gala Wallet is switched to with
|
|
7245
|
-
* `window.gala.setAddress` before every signature, when the wallet offers it.
|
|
7246
|
-
*/
|
|
7247
|
-
constructor(walletAddress, options = {}) {
|
|
7248
|
-
this.walletAddress = normalizeGalaWalletAccount(walletAddress);
|
|
7249
|
-
this.scheme = options.scheme ?? "native";
|
|
7250
|
-
if (options.selectAddress !== void 0) assertWalletIdentity(options.selectAddress);
|
|
7251
|
-
this.selectAddress = options.selectAddress;
|
|
7252
|
-
}
|
|
7253
|
-
/** The identity Gala Wallet is switched to before each signature, if one was given. */
|
|
7254
|
-
selectAddress;
|
|
7255
|
-
/**
|
|
7256
|
-
* Signs a DTO through `gala_signChainDto`.
|
|
7257
|
-
*
|
|
7258
|
-
* @example
|
|
7259
|
-
* ```typescript
|
|
7260
|
-
* const signed = await signer.signObject('AddLiquidity', {
|
|
7261
|
-
* token0: 'GALA',
|
|
7262
|
-
* token1: 'GUSDC',
|
|
7263
|
-
* tickLower: -60,
|
|
7264
|
-
* tickUpper: 60,
|
|
7265
|
-
* });
|
|
7266
|
-
* ```
|
|
7267
|
-
*/
|
|
7268
|
-
async signObject(methodName, dto) {
|
|
7269
|
-
const wallet = getWalletProvider();
|
|
7270
|
-
return withSelectedIdentity(wallet, this.selectAddress, async () => {
|
|
7271
|
-
if (this.scheme === "personal-sign") return personalSign(wallet, dto, this.walletAddress, methodName);
|
|
7272
|
-
const signature = await wallet.request({
|
|
7273
|
-
method: "gala_signChainDto",
|
|
7274
|
-
params: [
|
|
7275
|
-
serialize(dto),
|
|
7276
|
-
this.walletAddress,
|
|
7277
|
-
methodName,
|
|
7278
|
-
"native"
|
|
7279
|
-
]
|
|
7280
|
-
});
|
|
7281
|
-
return {
|
|
7282
|
-
...dto,
|
|
7283
|
-
signature: getNativeSignature(signature)
|
|
7284
|
-
};
|
|
7285
|
-
});
|
|
7286
|
-
}
|
|
7287
|
-
};
|
|
7288
|
-
/**
|
|
7289
|
-
* A personal-sign signer for MetaMask and other EIP-1193 browser wallets.
|
|
7290
|
-
*
|
|
7291
|
-
* The chain recovers this signature using the EIP-191 wrap applied by the wallet to
|
|
7292
|
-
* `personal_sign`. This is the browser-wallet path used by the v2 frontend and resolves the
|
|
7293
|
-
* bare `eth|` identity.
|
|
7294
|
-
*/
|
|
7295
|
-
var BrowserWalletSigner = class {
|
|
7296
|
-
provider;
|
|
7297
|
-
/**
|
|
7298
|
-
* Creates a browser-wallet signer.
|
|
7299
|
-
*
|
|
7300
|
-
* Defaults to `personal_sign` — unchanged from every existing integration, since it only ever
|
|
7301
|
-
* resolves the bare `eth|` identity and changing that silently would be a real behavior
|
|
7302
|
-
* break for anyone already depending on it. Pass `{ scheme: 'eip712' }` to correctly resolve
|
|
7303
|
-
* a registered `client|` alias instead, for `RequestTrade`/`AddLiquidity`/`RemoveLiquidity`/
|
|
7304
|
-
* `CollectPositionFees` (`EIP712_CAPABLE_METHODS`) — `CreatePool` and any other method still
|
|
7305
|
-
* falls back to `personal_sign` even in `'eip712'` mode, since its EIP-712 signing is a
|
|
7306
|
-
* separate, unsolved chain-side problem.
|
|
7307
|
-
*
|
|
7308
|
-
* @example
|
|
7309
|
-
* ```typescript
|
|
7310
|
-
* const signer = new BrowserWalletSigner(window.ethereum, '0x0123456789012345678901234567890123456789');
|
|
7311
|
-
* const signed = await signer.signObject('RequestTrade', { token0: 'GALA', token1: 'GUSDC' });
|
|
7312
|
-
* ```
|
|
7313
|
-
* @example
|
|
7314
|
-
* ```typescript
|
|
7315
|
-
* // Correctly resolves a registered client| alias instead of the bare eth| address.
|
|
7316
|
-
* const signer = new BrowserWalletSigner(window.ethereum, walletAddress, { scheme: 'eip712' });
|
|
7317
|
-
* ```
|
|
7318
|
-
*/
|
|
7319
|
-
constructor(provider, walletAddress, options = {}) {
|
|
7320
|
-
this.provider = provider;
|
|
7321
|
-
this.walletAddress = normalizeEthereumAccount(walletAddress, false);
|
|
7322
|
-
this.scheme = options.scheme ?? "personal-sign";
|
|
7323
|
-
}
|
|
7324
|
-
/** The selected `0x` Ethereum account every signature is requested for. */
|
|
7325
|
-
walletAddress;
|
|
7326
|
-
scheme;
|
|
7327
|
-
/**
|
|
7328
|
-
* Signs the DTO with `eth_signTypedData_v4` (when constructed with `{ scheme: 'eip712' }` and
|
|
7329
|
-
* `methodName` is in `EIP712_CAPABLE_METHODS`) or the EIP-1193 `personal_sign` method
|
|
7330
|
-
* (otherwise — the default).
|
|
7331
|
-
*
|
|
7332
|
-
* @example
|
|
7333
|
-
* ```typescript
|
|
7334
|
-
* const signed = await signer.signObject('RequestTrade', {
|
|
7335
|
-
* token0: 'GALA',
|
|
7336
|
-
* token1: 'GUSDC',
|
|
7337
|
-
* fee: 3000,
|
|
7338
|
-
* });
|
|
7339
|
-
* ```
|
|
7340
|
-
*/
|
|
7341
|
-
signObject(methodName, dto) {
|
|
7342
|
-
if (this.scheme === "eip712" && EIP712_CAPABLE_METHODS.has(methodName)) return signEIP712(this.provider, methodName, dto, this.walletAddress);
|
|
7343
|
-
return personalSign(this.provider, dto, this.walletAddress);
|
|
7344
|
-
}
|
|
7345
|
-
};
|
|
7346
|
-
|
|
7347
|
-
//#endregion
|
|
7348
|
-
//#region src/classes/storage_adapters.ts
|
|
7349
|
-
/**
|
|
7350
|
-
* A {@link StorageAdapter} over a `chrome.storage` area, the shape the wallet stores pending swaps
|
|
7351
|
-
* in: the value lands under the key as the structured value it is (the browser clones it; nothing
|
|
7352
|
-
* is serialized here). A missing key resolves `undefined`; a rejection of the area reaches the
|
|
7353
|
-
* store, which treats it as "storage unavailable".
|
|
6193
|
+
* A {@link StorageAdapter} over a `chrome.storage` area, the shape the wallet stores pending swaps
|
|
6194
|
+
* in: the value lands under the key as the structured value it is (the browser clones it; nothing
|
|
6195
|
+
* is serialized here). A missing key resolves `undefined`; a rejection of the area reaches the
|
|
6196
|
+
* store, which treats it as "storage unavailable".
|
|
7354
6197
|
*
|
|
7355
6198
|
* Pass a FUNCTION that returns the area to look it up at each call instead of when the adapter is
|
|
7356
6199
|
* built: a module that is imported where `chrome` does not exist yet (a test, a closing extension
|
|
@@ -7412,658 +6255,6 @@ function webStorageAdapter(area) {
|
|
|
7412
6255
|
};
|
|
7413
6256
|
}
|
|
7414
6257
|
|
|
7415
|
-
//#endregion
|
|
7416
|
-
//#region src/utils/chain_dto_signing.ts
|
|
7417
|
-
/**
|
|
7418
|
-
* String-key signing entry points for apps that hold a private key only briefly (the Gala Wallet
|
|
7419
|
-
* extension reads its session key at sign time and must not keep it in a long-lived object).
|
|
7420
|
-
*
|
|
7421
|
-
* These are thin, byte-exact ports of the functions the wallet shipped in
|
|
7422
|
-
* `wallet_extension/src/helpers/wallet/signing.ts` (including the fail-closed personal-sign
|
|
7423
|
-
* payload resolver from its commit 34ca4e4), re-expressed over the SDK's own primitives. Every
|
|
7424
|
-
* function takes the key as a string (`0x`-optional hex, or base64) or as bytes, normalizes it per
|
|
7425
|
-
* call, and zeroes the normalized copy before returning, so nothing in this module retains a key.
|
|
7426
|
-
* A JavaScript string cannot be wiped; the caller owns the lifetime of the string it passes in.
|
|
7427
|
-
*/
|
|
7428
|
-
/**
|
|
7429
|
-
* Runs `action` with a private normalized copy of the key, zeroed afterwards (also when `action`
|
|
7430
|
-
* throws). Bytes given by the caller are copied, never modified: the caller owns them.
|
|
7431
|
-
*/
|
|
7432
|
-
function withPrivateKey(privateKey, action) {
|
|
7433
|
-
const key = normalizePrivateKey(privateKey);
|
|
7434
|
-
try {
|
|
7435
|
-
return action(key);
|
|
7436
|
-
} finally {
|
|
7437
|
-
key.fill(0);
|
|
7438
|
-
}
|
|
7439
|
-
}
|
|
7440
|
-
/**
|
|
7441
|
-
* GalaChain NATIVE signature over an already-serialized DTO: keccak256 of the exact bytes with no
|
|
7442
|
-
* prefix and no envelope (wallet `signChainDtoNative`). The caller owns serialization, so the
|
|
7443
|
-
* bytes a user reviewed and the bytes signed cannot diverge.
|
|
7444
|
-
*
|
|
7445
|
-
* @returns 130 lowercase hex characters, `r || s || v` with `v` = `1b`/`1c`, no `0x`.
|
|
7446
|
-
*/
|
|
7447
|
-
function signChainDtoNative(privateKey, serializedDto) {
|
|
7448
|
-
return withPrivateKey(privateKey, (key) => signSerializedNative(key, serializedDto));
|
|
7449
|
-
}
|
|
7450
|
-
/**
|
|
7451
|
-
* The personal-sign hash primitive (wallet `signChainDtoHash`): keccak256(`prefix` + `message`),
|
|
7452
|
-
* both verbatim, with no further EIP-191 wrap. `message` must be the DTO's serialization WITHOUT
|
|
7453
|
-
* its `prefix` field; callers holding a dApp-supplied string go through `signChainDtoPersonalSign`,
|
|
7454
|
-
* which derives both inputs from it.
|
|
7455
|
-
*/
|
|
7456
|
-
function signChainDtoHash(privateKey, prefix, message) {
|
|
7457
|
-
return withPrivateKey(privateKey, (key) => signPersonalMessage(key, prefix, message));
|
|
7458
|
-
}
|
|
7459
|
-
const PERSONAL_SIGN_HEADER = String.fromCharCode(25) + "Ethereum Signed Message:\n";
|
|
7460
|
-
const PERSONAL_SIGN_LENGTH_PATTERN = /^(?:0|[1-9][0-9]*)$/u;
|
|
7461
|
-
function isPersonalSignHeader(prefix) {
|
|
7462
|
-
return prefix.startsWith(PERSONAL_SIGN_HEADER) && PERSONAL_SIGN_LENGTH_PATTERN.test(prefix.slice(26));
|
|
7463
|
-
}
|
|
7464
|
-
/**
|
|
7465
|
-
* Top-level fields that would make the chain (`getPayloadToSign`) or defi-backend's personal-sign
|
|
7466
|
-
* verifier hash something other than `prefix + serialize(dto minus prefix)`. An unsigned DTO from
|
|
7467
|
-
* a dApp never legitimately carries any of them, so they fail closed. Order is part of the error
|
|
7468
|
-
* message the wallet shows its user.
|
|
7469
|
-
*/
|
|
7470
|
-
const PERSONAL_SIGN_FORBIDDEN_FIELDS = [
|
|
7471
|
-
"signature",
|
|
7472
|
-
"multisig",
|
|
7473
|
-
"trace",
|
|
7474
|
-
"types",
|
|
7475
|
-
"domain",
|
|
7476
|
-
"stringsInstructions"
|
|
7477
|
-
];
|
|
7478
|
-
/**
|
|
7479
|
-
* Derives what to hash for a `gala_signChainDto` personal-sign request from the exact string the
|
|
7480
|
-
* dApp sent (and the approval card displayed): wallet `resolvePersonalSignChainDtoPayload`.
|
|
7481
|
-
*
|
|
7482
|
-
* The dApp submits `{ ...dto, prefix, signature }`, which the chain recovers from
|
|
7483
|
-
* `keccak256(prefix + serialize(dto WITHOUT prefix))`. So when the string embeds a `prefix`
|
|
7484
|
-
* field, that field is removed and the remainder re-serialized; the prefix used is the dApp's own
|
|
7485
|
-
* value byte for byte. A string with no `prefix` field is hashed verbatim with an empty prefix
|
|
7486
|
-
* (chain-equivalent to a native signature of that exact string).
|
|
7487
|
-
*
|
|
7488
|
-
* Fails closed (throws `GSwapSDKError` `PERSONAL_SIGN_NOT_JSON`, `PERSONAL_SIGN_NOT_OBJECT`,
|
|
7489
|
-
* `PERSONAL_SIGN_BAD_PREFIX`, `PERSONAL_SIGN_ENVELOPE_FIELDS` or `PERSONAL_SIGN_NOT_CANONICAL`) on:
|
|
7490
|
-
* malformed JSON; JSON that is not a plain object; a `prefix` that is not an EIP-191
|
|
7491
|
-
* "Ethereum Signed Message" header; any signing-envelope field (`signature`, `multisig`, `trace`,
|
|
7492
|
-
* `types`, `domain`, `stringsInstructions`); and a string that is not already the canonical
|
|
7493
|
-
* serialization of what it parses to (duplicate keys, lossy numbers, key-order tricks the card
|
|
7494
|
-
* would render differently). Stripping `prefix` is therefore the ONLY byte change between the
|
|
7495
|
-
* reviewed string and the signed message.
|
|
7496
|
-
*/
|
|
7497
|
-
function resolvePersonalSignChainDtoPayload(data) {
|
|
7498
|
-
let parsed;
|
|
7499
|
-
try {
|
|
7500
|
-
parsed = JSON.parse(data);
|
|
7501
|
-
} catch {
|
|
7502
|
-
throw new GSwapSDKError("PERSONAL_SIGN_NOT_JSON", {});
|
|
7503
|
-
}
|
|
7504
|
-
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) throw new GSwapSDKError("PERSONAL_SIGN_NOT_OBJECT", {});
|
|
7505
|
-
const dto = parsed;
|
|
7506
|
-
if (!Object.prototype.hasOwnProperty.call(dto, "prefix")) return {
|
|
7507
|
-
prefix: "",
|
|
7508
|
-
message: data
|
|
7509
|
-
};
|
|
7510
|
-
const { prefix, ...withoutPrefix } = dto;
|
|
7511
|
-
if (typeof prefix !== "string" || !isPersonalSignHeader(prefix)) throw new GSwapSDKError("PERSONAL_SIGN_BAD_PREFIX", {});
|
|
7512
|
-
const forbidden = PERSONAL_SIGN_FORBIDDEN_FIELDS.filter((field) => Object.prototype.hasOwnProperty.call(withoutPrefix, field));
|
|
7513
|
-
if (forbidden.length > 0) throw new GSwapSDKError("PERSONAL_SIGN_ENVELOPE_FIELDS", { fields: forbidden.join(", ") });
|
|
7514
|
-
if (serialize(dto) !== data) throw new GSwapSDKError("PERSONAL_SIGN_NOT_CANONICAL", {});
|
|
7515
|
-
return {
|
|
7516
|
-
prefix,
|
|
7517
|
-
message: serialize(withoutPrefix)
|
|
7518
|
-
};
|
|
7519
|
-
}
|
|
7520
|
-
/**
|
|
7521
|
-
* Signs a GalaChain personal-sign scheme DTO from the exact string a dApp sent through
|
|
7522
|
-
* `gala_signChainDto` (wallet `signChainDtoPersonalSign`). See
|
|
7523
|
-
* `resolvePersonalSignChainDtoPayload` for what is signed and what is refused; the signature
|
|
7524
|
-
* recovers, under the chain's `getPayloadToSign`, to this key for the DTO the dApp submits with
|
|
7525
|
-
* its `prefix`, and equals a `personal_sign` of the bare body when that prefix is the EIP-191
|
|
7526
|
-
* header of the bare body's byte length.
|
|
7527
|
-
*
|
|
7528
|
-
* @throws GSwapSDKError (`PERSONAL_SIGN_*`, see `resolvePersonalSignChainDtoPayload`) before any
|
|
7529
|
-
* signing when the input is refused.
|
|
7530
|
-
*/
|
|
7531
|
-
function signChainDtoPersonalSign(privateKey, data) {
|
|
7532
|
-
const { prefix, message } = resolvePersonalSignChainDtoPayload(data);
|
|
7533
|
-
return signChainDtoHash(privateKey, prefix, message);
|
|
7534
|
-
}
|
|
7535
|
-
/**
|
|
7536
|
-
* Raw object signing (wallet `signObject`), used for transfers, bridge and fee-dry-run DTOs the
|
|
7537
|
-
* wallet signs itself. Removes ONLY `signature`, signs `keccak256(serialize(rest))` natively
|
|
7538
|
-
* (no `prefix` handling, and `trace` / `multisig` stay in the signed bytes, unlike
|
|
7539
|
-
* `signNative`), and returns `{ signature, ...rest }` with the signature as the FIRST key, exactly
|
|
7540
|
-
* like the wallet. Callers that must not sign extra fields strip them first.
|
|
7541
|
-
*/
|
|
7542
|
-
function signRawObject(privateKey, obj) {
|
|
7543
|
-
const rest = { ...obj };
|
|
7544
|
-
delete rest["signature"];
|
|
7545
|
-
return {
|
|
7546
|
-
signature: signChainDtoNative(privateKey, serialize(rest)),
|
|
7547
|
-
...rest
|
|
7548
|
-
};
|
|
7549
|
-
}
|
|
7550
|
-
/**
|
|
7551
|
-
* Drops the `EIP712Domain` entry from a dApp-supplied types map (a typed-data encoder derives the
|
|
7552
|
-
* domain type itself and rejects an explicit one). The order of the remaining types is preserved.
|
|
7553
|
-
*/
|
|
7554
|
-
function stripEip712DomainType(types) {
|
|
7555
|
-
const stripped = {};
|
|
7556
|
-
for (const typeName of Object.keys(types)) if (typeName !== "EIP712Domain") stripped[typeName] = types[typeName];
|
|
7557
|
-
return stripped;
|
|
7558
|
-
}
|
|
7559
|
-
/**
|
|
7560
|
-
* `collection$category$type$additionalKey` for a token-class object (wallet `decodeTokenData`).
|
|
7561
|
-
* Lenient on purpose: any missing or non-string part becomes `''`, so a display layer never
|
|
7562
|
-
* throws on odd input.
|
|
7563
|
-
*/
|
|
7564
|
-
function tokenClassDollarKey(token) {
|
|
7565
|
-
const part = (field) => {
|
|
7566
|
-
const value = token === null || token === void 0 ? void 0 : token[field];
|
|
7567
|
-
return typeof value === "string" ? value : "";
|
|
7568
|
-
};
|
|
7569
|
-
return `${part("collection")}$${part("category")}$${part("type")}$${part("additionalKey")}`;
|
|
7570
|
-
}
|
|
7571
|
-
/**
|
|
7572
|
-
* A quantity in the form GalaChain's `IsBigNumber` validator accepts: FIXED notation, no leading
|
|
7573
|
-
* zeros, no trailing decimal zeros (`BigNumber#toFixed()`; never `toString()`, which turns
|
|
7574
|
-
* `0.00000001` into `1e-8`, and never a JS number in between).
|
|
7575
|
-
*
|
|
7576
|
-
* @throws GSwapSDKError (`INVALID_DECIMAL`) for a value that is not a finite number. The wallet's
|
|
7577
|
-
* `cleanNumber` returned the string `'NaN'` there; signing that text can only fail on chain.
|
|
7578
|
-
*/
|
|
7579
|
-
function canonicalDecimalString(value) {
|
|
7580
|
-
let parsed = new bignumber_js.default(NaN);
|
|
7581
|
-
try {
|
|
7582
|
-
parsed = new bignumber_js.default(value);
|
|
7583
|
-
} catch {}
|
|
7584
|
-
if (!parsed.isFinite()) throw new GSwapSDKError("INVALID_DECIMAL", { value: String(value) });
|
|
7585
|
-
return parsed.toFixed();
|
|
7586
|
-
}
|
|
7587
|
-
|
|
7588
|
-
//#endregion
|
|
7589
|
-
//#region src/utils/typed_data.ts
|
|
7590
|
-
/**
|
|
7591
|
-
* EIP-712 (`eth_signTypedData_v4`) hashing and signing for ARBITRARY dApp typed data, built on the
|
|
7592
|
-
* SDK's own primitives (`@noble/curves`, `@noble/hashes`) so a wallet can sign a dApp's typed-data
|
|
7593
|
-
* approval without ethers. It replaces the wallet's `new ethers.Wallet(key).signTypedData(...)`
|
|
7594
|
-
* and is pinned to it: the golden suite reproduces every fixture signature exactly and a seeded
|
|
7595
|
-
* differential fuzz compares the digest and signature with ethers for every supported type.
|
|
7596
|
-
*
|
|
7597
|
-
* Compared with ethers this encoder is deliberately STRICTER (it fails closed rather than coerce):
|
|
7598
|
-
* `bool` accepts only a boolean, numbers accept only safe integers / decimal or `0x` strings,
|
|
7599
|
-
* strings must be well-formed UTF-16, struct values must be objects whose fields are their own
|
|
7600
|
-
* properties, and every struct, field and type name must be a plain identifier. Anything ethers
|
|
7601
|
-
* accepts that is NOT in that set is refused here instead of being signed in a different shape
|
|
7602
|
-
* from what a user reviewed.
|
|
7603
|
-
*/
|
|
7604
|
-
const textEncoder = new TextEncoder();
|
|
7605
|
-
const IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/u;
|
|
7606
|
-
const TYPE_SYNTAX = /^([A-Za-z_$][A-Za-z0-9_$]*)((?:\[(?:0|[1-9][0-9]*)?\])*)$/u;
|
|
7607
|
-
const BIGINT_DECIMAL = /^-?[0-9]+$/u;
|
|
7608
|
-
const BIGINT_HEX = /^-?0x[0-9a-fA-F]+$/u;
|
|
7609
|
-
/** The EIP-712 domain members and their types, in the order ethers (and the spec) hash them. */
|
|
7610
|
-
const DOMAIN_FIELDS = [
|
|
7611
|
-
{
|
|
7612
|
-
name: "name",
|
|
7613
|
-
type: "string"
|
|
7614
|
-
},
|
|
7615
|
-
{
|
|
7616
|
-
name: "version",
|
|
7617
|
-
type: "string"
|
|
7618
|
-
},
|
|
7619
|
-
{
|
|
7620
|
-
name: "chainId",
|
|
7621
|
-
type: "uint256"
|
|
7622
|
-
},
|
|
7623
|
-
{
|
|
7624
|
-
name: "verifyingContract",
|
|
7625
|
-
type: "address"
|
|
7626
|
-
},
|
|
7627
|
-
{
|
|
7628
|
-
name: "salt",
|
|
7629
|
-
type: "bytes32"
|
|
7630
|
-
}
|
|
7631
|
-
];
|
|
7632
|
-
function concat(parts) {
|
|
7633
|
-
const out = new Uint8Array(parts.reduce((total, part) => total + part.length, 0));
|
|
7634
|
-
let offset = 0;
|
|
7635
|
-
for (const part of parts) {
|
|
7636
|
-
out.set(part, offset);
|
|
7637
|
-
offset += part.length;
|
|
7638
|
-
}
|
|
7639
|
-
return out;
|
|
7640
|
-
}
|
|
7641
|
-
function bigintToWord(value) {
|
|
7642
|
-
const word = /* @__PURE__ */ new Uint8Array(32);
|
|
7643
|
-
let rest = value < 0n ? value + (1n << 256n) : value;
|
|
7644
|
-
for (let index = 31; index >= 0; index -= 1) {
|
|
7645
|
-
word[index] = Number(rest & 255n);
|
|
7646
|
-
rest >>= 8n;
|
|
7647
|
-
}
|
|
7648
|
-
return word;
|
|
7649
|
-
}
|
|
7650
|
-
function hexToBytes(hex) {
|
|
7651
|
-
const body = hex.slice(2);
|
|
7652
|
-
const out = new Uint8Array(body.length / 2);
|
|
7653
|
-
for (let index = 0; index < out.length; index += 1) out[index] = parseInt(body.slice(index * 2, index * 2 + 2), 16);
|
|
7654
|
-
return out;
|
|
7655
|
-
}
|
|
7656
|
-
function toBytes(value, what) {
|
|
7657
|
-
if (value instanceof Uint8Array) return value;
|
|
7658
|
-
if (typeof value === "string" && /^0x(?:[0-9a-fA-F]{2})*$/u.test(value)) return hexToBytes(value);
|
|
7659
|
-
throw new GSwapSDKError("TYPED_DATA_NOT_HEX_BYTES", { what });
|
|
7660
|
-
}
|
|
7661
|
-
function toBigInt(value, what) {
|
|
7662
|
-
if (typeof value === "bigint") return value;
|
|
7663
|
-
if (typeof value === "number") {
|
|
7664
|
-
if (!Number.isSafeInteger(value)) throw new GSwapSDKError("TYPED_DATA_NOT_SAFE_INTEGER", { what });
|
|
7665
|
-
return BigInt(value);
|
|
7666
|
-
}
|
|
7667
|
-
if (typeof value === "string") {
|
|
7668
|
-
if (BIGINT_DECIMAL.test(value)) return BigInt(value);
|
|
7669
|
-
if (BIGINT_HEX.test(value)) return value.startsWith("-") ? -BigInt(value.slice(1)) : BigInt(value);
|
|
7670
|
-
}
|
|
7671
|
-
throw new GSwapSDKError("TYPED_DATA_NOT_INTEGER", { what });
|
|
7672
|
-
}
|
|
7673
|
-
function utf8(value, what) {
|
|
7674
|
-
if (typeof value !== "string") throw new GSwapSDKError("TYPED_DATA_NOT_STRING", { what });
|
|
7675
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
7676
|
-
const unit = value.charCodeAt(index);
|
|
7677
|
-
if (unit >= 55296 && unit <= 56319) {
|
|
7678
|
-
const next = value.charCodeAt(index + 1);
|
|
7679
|
-
if (!(next >= 56320 && next <= 57343)) throw new GSwapSDKError("TYPED_DATA_UNPAIRED_SURROGATE", { what });
|
|
7680
|
-
index += 1;
|
|
7681
|
-
} else if (unit >= 56320 && unit <= 57343) throw new GSwapSDKError("TYPED_DATA_UNPAIRED_SURROGATE", { what });
|
|
7682
|
-
}
|
|
7683
|
-
return textEncoder.encode(value);
|
|
7684
|
-
}
|
|
7685
|
-
/** EIP-55: a mixed-case address must carry the right checksum; all-lower / all-upper is accepted. */
|
|
7686
|
-
function addressBytes(value) {
|
|
7687
|
-
if (typeof value !== "string" || !/^0x[0-9a-fA-F]{40}$/u.test(value)) throw new GSwapSDKError("TYPED_DATA_BAD_ADDRESS", {});
|
|
7688
|
-
const digits = value.slice(2);
|
|
7689
|
-
if (/[A-F]/u.test(digits) && /[a-f]/u.test(digits)) {
|
|
7690
|
-
const hash = (0, _noble_hashes_sha3.keccak_256)(textEncoder.encode(digits.toLowerCase()));
|
|
7691
|
-
for (let index = 0; index < 40; index += 1) {
|
|
7692
|
-
const nibble = hash[index >> 1] >> (index % 2 === 0 ? 4 : 0) & 15;
|
|
7693
|
-
const char = digits.charAt(index);
|
|
7694
|
-
if (char !== (nibble >= 8 ? char.toUpperCase() : char.toLowerCase())) throw new GSwapSDKError("TYPED_DATA_BAD_CHECKSUM", {});
|
|
7695
|
-
}
|
|
7696
|
-
}
|
|
7697
|
-
return hexToBytes(value);
|
|
7698
|
-
}
|
|
7699
|
-
/** The encoder for a non-struct, non-array type, or undefined when `base` is not one. */
|
|
7700
|
-
function leafEncoder(base) {
|
|
7701
|
-
if (base === "string") return (value) => (0, _noble_hashes_sha3.keccak_256)(utf8(value, "string"));
|
|
7702
|
-
if (base === "bytes") return (value) => (0, _noble_hashes_sha3.keccak_256)(toBytes(value, "bytes"));
|
|
7703
|
-
if (base === "bool") return (value) => {
|
|
7704
|
-
if (typeof value !== "boolean") throw new GSwapSDKError("TYPED_DATA_NOT_BOOLEAN", {});
|
|
7705
|
-
return bigintToWord(value ? 1n : 0n);
|
|
7706
|
-
};
|
|
7707
|
-
if (base === "address") return (value) => concat([/* @__PURE__ */ new Uint8Array(12), addressBytes(value)]);
|
|
7708
|
-
const fixed = /^bytes([1-9][0-9]?)$/u.exec(base);
|
|
7709
|
-
if (fixed !== null) {
|
|
7710
|
-
const width = Number(fixed[1]);
|
|
7711
|
-
if (width > 32 || String(width) !== fixed[1]) return void 0;
|
|
7712
|
-
return (value) => {
|
|
7713
|
-
const bytes = toBytes(value, base);
|
|
7714
|
-
if (bytes.length !== width) throw new GSwapSDKError("TYPED_DATA_BYTES_LENGTH", {
|
|
7715
|
-
type: base,
|
|
7716
|
-
width
|
|
7717
|
-
});
|
|
7718
|
-
return concat([bytes, new Uint8Array(32 - width)]);
|
|
7719
|
-
};
|
|
7720
|
-
}
|
|
7721
|
-
const integer = /^(u?)int([1-9][0-9]*)$/u.exec(base);
|
|
7722
|
-
if (integer !== null) {
|
|
7723
|
-
const width = Number(integer[2]);
|
|
7724
|
-
if (width % 8 !== 0 || width > 256 || String(width) !== integer[2]) return void 0;
|
|
7725
|
-
const signed = integer[1] === "";
|
|
7726
|
-
const lower = signed ? -(1n << BigInt(width - 1)) : 0n;
|
|
7727
|
-
const upper = signed ? (1n << BigInt(width - 1)) - 1n : (1n << BigInt(width)) - 1n;
|
|
7728
|
-
return (value) => {
|
|
7729
|
-
const number = toBigInt(value, base);
|
|
7730
|
-
if (number < lower || number > upper) throw new GSwapSDKError("TYPED_DATA_OUT_OF_BOUNDS", { type: base });
|
|
7731
|
-
return bigintToWord(number);
|
|
7732
|
-
};
|
|
7733
|
-
}
|
|
7734
|
-
}
|
|
7735
|
-
function parseType(type) {
|
|
7736
|
-
const match = TYPE_SYNTAX.exec(type);
|
|
7737
|
-
if (match === null) throw new GSwapSDKError("TYPED_DATA_UNSUPPORTED_TYPE", { type });
|
|
7738
|
-
const dimensions = [...(match[2] ?? "").matchAll(/\[([0-9]*)\]/gu)].map((dimension) => dimension[1] === "" ? -1 : Number(dimension[1]));
|
|
7739
|
-
return {
|
|
7740
|
-
base: match[1],
|
|
7741
|
-
dimensions
|
|
7742
|
-
};
|
|
7743
|
-
}
|
|
7744
|
-
function parseFields(typeName, rawFields) {
|
|
7745
|
-
if (!Array.isArray(rawFields)) throw new GSwapSDKError("TYPED_DATA_FIELDS_NOT_LIST", { typeName });
|
|
7746
|
-
const seen = /* @__PURE__ */ new Set();
|
|
7747
|
-
return rawFields.map((raw) => {
|
|
7748
|
-
const field = raw;
|
|
7749
|
-
if (typeof (field === null || field === void 0 ? void 0 : field.name) !== "string" || typeof field.type !== "string") throw new GSwapSDKError("TYPED_DATA_FIELD_MALFORMED", { typeName });
|
|
7750
|
-
if (!IDENTIFIER.test(field.name)) throw new GSwapSDKError("TYPED_DATA_INVALID_FIELD_NAME", { name: field.name });
|
|
7751
|
-
if (seen.has(field.name)) throw new GSwapSDKError("TYPED_DATA_DUPLICATE_FIELD", {
|
|
7752
|
-
name: field.name,
|
|
7753
|
-
typeName
|
|
7754
|
-
});
|
|
7755
|
-
seen.add(field.name);
|
|
7756
|
-
const parsed = parseType(field.type);
|
|
7757
|
-
const base = parsed.base === "uint" || parsed.base === "int" ? `${parsed.base}256` : parsed.base;
|
|
7758
|
-
return {
|
|
7759
|
-
name: field.name,
|
|
7760
|
-
type: base + field.type.slice(parsed.base.length)
|
|
7761
|
-
};
|
|
7762
|
-
});
|
|
7763
|
-
}
|
|
7764
|
-
/**
|
|
7765
|
-
* Builds the struct encoder for a set of type definitions: validates the type graph (unknown or
|
|
7766
|
-
* circular references, duplicate fields) and infers the primary type as the single type no other
|
|
7767
|
-
* type references, exactly like ethers' `TypedDataEncoder`.
|
|
7768
|
-
*/
|
|
7769
|
-
function createStructEncoder(rawTypes) {
|
|
7770
|
-
const names = Object.keys(rawTypes);
|
|
7771
|
-
const fields = /* @__PURE__ */ new Map();
|
|
7772
|
-
for (const name of names) {
|
|
7773
|
-
if (!IDENTIFIER.test(name)) throw new GSwapSDKError("TYPED_DATA_INVALID_TYPE_NAME", { name });
|
|
7774
|
-
fields.set(name, parseFields(name, rawTypes[name]));
|
|
7775
|
-
}
|
|
7776
|
-
const children = /* @__PURE__ */ new Map();
|
|
7777
|
-
const parents = /* @__PURE__ */ new Map();
|
|
7778
|
-
for (const name of names) {
|
|
7779
|
-
children.set(name, /* @__PURE__ */ new Set());
|
|
7780
|
-
parents.set(name, /* @__PURE__ */ new Set());
|
|
7781
|
-
}
|
|
7782
|
-
for (const [name, list] of fields) for (const field of list) {
|
|
7783
|
-
var _children$get, _parents$get;
|
|
7784
|
-
const { base } = parseType(field.type);
|
|
7785
|
-
if (base === name) throw new GSwapSDKError("TYPED_DATA_CIRCULAR_TYPE", { typeName: name });
|
|
7786
|
-
if (leafEncoder(base) !== void 0) continue;
|
|
7787
|
-
if (!fields.has(base)) throw new GSwapSDKError("TYPED_DATA_UNKNOWN_FIELD_TYPE", {
|
|
7788
|
-
type: base,
|
|
7789
|
-
typeName: name
|
|
7790
|
-
});
|
|
7791
|
-
(_children$get = children.get(name)) === null || _children$get === void 0 || _children$get.add(base);
|
|
7792
|
-
(_parents$get = parents.get(base)) === null || _parents$get === void 0 || _parents$get.add(name);
|
|
7793
|
-
}
|
|
7794
|
-
const roots = names.filter((name) => {
|
|
7795
|
-
var _parents$get2;
|
|
7796
|
-
return ((_parents$get2 = parents.get(name)) === null || _parents$get2 === void 0 ? void 0 : _parents$get2.size) === 0;
|
|
7797
|
-
});
|
|
7798
|
-
if (roots.length === 0) throw new GSwapSDKError("TYPED_DATA_NO_PRIMARY_TYPE", {});
|
|
7799
|
-
if (roots.length > 1) throw new GSwapSDKError("TYPED_DATA_AMBIGUOUS_PRIMARY_TYPE", { roots: roots.join(", ") });
|
|
7800
|
-
const primaryType = roots[0];
|
|
7801
|
-
const visiting = /* @__PURE__ */ new Set();
|
|
7802
|
-
const checked = /* @__PURE__ */ new Set();
|
|
7803
|
-
const checkCircular = (name) => {
|
|
7804
|
-
if (visiting.has(name)) throw new GSwapSDKError("TYPED_DATA_CIRCULAR_TYPE", { typeName: name });
|
|
7805
|
-
if (checked.has(name)) return;
|
|
7806
|
-
visiting.add(name);
|
|
7807
|
-
for (const child of children.get(name) ?? []) checkCircular(child);
|
|
7808
|
-
visiting.delete(name);
|
|
7809
|
-
checked.add(name);
|
|
7810
|
-
};
|
|
7811
|
-
checkCircular(primaryType);
|
|
7812
|
-
const dependencies = (name, found) => {
|
|
7813
|
-
for (const child of children.get(name) ?? []) if (!found.has(child)) {
|
|
7814
|
-
found.add(child);
|
|
7815
|
-
dependencies(child, found);
|
|
7816
|
-
}
|
|
7817
|
-
return found;
|
|
7818
|
-
};
|
|
7819
|
-
const encodeOne = (name) => `${name}(${(fields.get(name) ?? []).map((field) => `${field.type} ${field.name}`).join(",")})`;
|
|
7820
|
-
const typeHashes = /* @__PURE__ */ new Map();
|
|
7821
|
-
for (const name of names) {
|
|
7822
|
-
const sorted = [...dependencies(name, /* @__PURE__ */ new Set())].filter((dep) => dep !== name).sort();
|
|
7823
|
-
typeHashes.set(name, (0, _noble_hashes_sha3.keccak_256)(textEncoder.encode([name, ...sorted].map(encodeOne).join(""))));
|
|
7824
|
-
}
|
|
7825
|
-
const encodeValue = (parsed, value, depth) => {
|
|
7826
|
-
const dimension = parsed.dimensions[parsed.dimensions.length - 1 - depth];
|
|
7827
|
-
if (dimension !== void 0) {
|
|
7828
|
-
if (!Array.isArray(value)) throw new GSwapSDKError("TYPED_DATA_ARRAY_EXPECTED", { type: parsed.base });
|
|
7829
|
-
if (dimension !== -1 && dimension !== value.length) throw new GSwapSDKError("TYPED_DATA_ARRAY_LENGTH_MISMATCH", {});
|
|
7830
|
-
return (0, _noble_hashes_sha3.keccak_256)(concat(value.map((item) => encodeValue(parsed, item, depth + 1))));
|
|
7831
|
-
}
|
|
7832
|
-
const leaf = leafEncoder(parsed.base);
|
|
7833
|
-
if (leaf !== void 0) return leaf(value);
|
|
7834
|
-
return hashStruct(parsed.base, value);
|
|
7835
|
-
};
|
|
7836
|
-
function hashStruct(typeName, value) {
|
|
7837
|
-
const list = fields.get(typeName);
|
|
7838
|
-
const typeHash = typeHashes.get(typeName);
|
|
7839
|
-
if (list === void 0 || typeHash === void 0) throw new Error(`Internal error: no encoder for struct ${typeName}`);
|
|
7840
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new GSwapSDKError("TYPED_DATA_STRUCT_NOT_OBJECT", { typeName });
|
|
7841
|
-
const record = value;
|
|
7842
|
-
const words = list.map((field) => {
|
|
7843
|
-
if (!Object.prototype.hasOwnProperty.call(record, field.name)) throw new GSwapSDKError("TYPED_DATA_MISSING_FIELD", {
|
|
7844
|
-
typeName,
|
|
7845
|
-
field: field.name
|
|
7846
|
-
});
|
|
7847
|
-
return encodeValue(parseType(field.type), record[field.name], 0);
|
|
7848
|
-
});
|
|
7849
|
-
return (0, _noble_hashes_sha3.keccak_256)(concat([typeHash, ...words]));
|
|
7850
|
-
}
|
|
7851
|
-
return {
|
|
7852
|
-
primaryType,
|
|
7853
|
-
hashStruct
|
|
7854
|
-
};
|
|
7855
|
-
}
|
|
7856
|
-
function hashDomain(domain) {
|
|
7857
|
-
if (domain === null || typeof domain !== "object" || Array.isArray(domain)) throw new GSwapSDKError("TYPED_DATA_DOMAIN_NOT_OBJECT", {});
|
|
7858
|
-
for (const key of Object.keys(domain)) if (!DOMAIN_FIELDS.some((field) => field.name === key)) throw new GSwapSDKError("TYPED_DATA_INVALID_DOMAIN_KEY", { key: JSON.stringify(key) });
|
|
7859
|
-
return createStructEncoder({ EIP712Domain: DOMAIN_FIELDS.filter((field) => Object.prototype.hasOwnProperty.call(domain, field.name)) }).hashStruct("EIP712Domain", domain);
|
|
7860
|
-
}
|
|
7861
|
-
/**
|
|
7862
|
-
* The EIP-712 digest `keccak256(0x1901 || domainSeparator || hashStruct(primaryType, message))`
|
|
7863
|
-
* that a wallet signs for `eth_signTypedData_v4`. The primary type is inferred, as ethers does.
|
|
7864
|
-
*
|
|
7865
|
-
* @throws GSwapSDKError (`TYPED_DATA_*`) for any type graph or value it cannot encode
|
|
7866
|
-
* exactly (see the module note on strictness).
|
|
7867
|
-
*/
|
|
7868
|
-
function hashTypedData(payload) {
|
|
7869
|
-
const domainHash = hashDomain(payload.domain);
|
|
7870
|
-
const encoder = createStructEncoder(payload.types);
|
|
7871
|
-
const structHash = encoder.hashStruct(encoder.primaryType, payload.message);
|
|
7872
|
-
return (0, _noble_hashes_sha3.keccak_256)(concat([
|
|
7873
|
-
Uint8Array.of(25, 1),
|
|
7874
|
-
domainHash,
|
|
7875
|
-
structHash
|
|
7876
|
-
]));
|
|
7877
|
-
}
|
|
7878
|
-
/**
|
|
7879
|
-
* Signs a dApp's EIP-712 typed-data approval (the wallet's `signTypedMessage` path), replacing
|
|
7880
|
-
* `new ethers.Wallet(key).signTypedData(domain, types, message)`. `types` must already have
|
|
7881
|
-
* `EIP712Domain` removed (`stripEip712DomainType`).
|
|
7882
|
-
*
|
|
7883
|
-
* @returns `0x` + r + s + v with `v` = `0x1b`/`0x1c` (27/28): ethers' serialized signature form,
|
|
7884
|
-
* recoverable with `verifyTypedData` / `eth_signTypedData_v4` verifiers. Rejects (never throws
|
|
7885
|
-
* synchronously) on invalid input.
|
|
7886
|
-
*/
|
|
7887
|
-
async function signTypedDataPayload(privateKey, payload) {
|
|
7888
|
-
const digest = hashTypedData(payload);
|
|
7889
|
-
return withPrivateKey(privateKey, (key) => `0x${signHash(key, digest)}`);
|
|
7890
|
-
}
|
|
7891
|
-
|
|
7892
|
-
//#endregion
|
|
7893
|
-
//#region src/utils/axios_requestor.ts
|
|
7894
|
-
function isRecord$1(value) {
|
|
7895
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7896
|
-
}
|
|
7897
|
-
/**
|
|
7898
|
-
* An axios interceptor may resolve something that is not a response at all (for example a string
|
|
7899
|
-
* for ECONNREFUSED). That is a transport failure, not an HTTP answer.
|
|
7900
|
-
*/
|
|
7901
|
-
function isAxiosLikeResponse(value) {
|
|
7902
|
-
return isRecord$1(value) && typeof value["status"] === "number";
|
|
7903
|
-
}
|
|
7904
|
-
function readAxiosHeader(headers, name) {
|
|
7905
|
-
if (!isRecord$1(headers)) return null;
|
|
7906
|
-
const getter = headers["get"];
|
|
7907
|
-
if (typeof getter === "function") {
|
|
7908
|
-
const value = getter.call(headers, name);
|
|
7909
|
-
return typeof value === "string" ? value : null;
|
|
7910
|
-
}
|
|
7911
|
-
const match = Object.entries(headers).find(([key]) => key.toLowerCase() === name.toLowerCase());
|
|
7912
|
-
return typeof (match === null || match === void 0 ? void 0 : match[1]) === "string" ? match[1] : null;
|
|
7913
|
-
}
|
|
7914
|
-
function toHttpResponse(answer, url) {
|
|
7915
|
-
if (!isAxiosLikeResponse(answer)) throw new GSwapSDKError("TRANSPORT_ERROR", { url }, { url });
|
|
7916
|
-
const { status, data, headers } = answer;
|
|
7917
|
-
return {
|
|
7918
|
-
ok: status >= 200 && status < 300,
|
|
7919
|
-
status,
|
|
7920
|
-
json: () => Promise.resolve(data),
|
|
7921
|
-
text: () => Promise.resolve(typeof data === "string" ? data : JSON.stringify(data)),
|
|
7922
|
-
headers: { get: (name) => readAxiosHeader(headers, name) }
|
|
7923
|
-
};
|
|
7924
|
-
}
|
|
7925
|
-
function headersFor(init, omit) {
|
|
7926
|
-
const omitted = new Set(omit.map((name) => name.toLowerCase()));
|
|
7927
|
-
const raw = init === null || init === void 0 ? void 0 : init.headers;
|
|
7928
|
-
const entries = raw === void 0 ? [] : raw instanceof Headers ? [...raw.entries()] : Array.isArray(raw) ? raw.map(([name, value]) => [String(name), String(value)]) : Object.entries(raw).map(([name, value]) => [name, String(value)]);
|
|
7929
|
-
return Object.fromEntries(entries.filter(([name]) => !omitted.has(name.toLowerCase())));
|
|
7930
|
-
}
|
|
7931
|
-
/**
|
|
7932
|
-
* An SDK {@link HttpRequestor} over an axios instance, so the SDK's requests travel through the
|
|
7933
|
-
* app's own base URL, interceptors and error handling.
|
|
7934
|
-
*
|
|
7935
|
-
* A POST's string body is parsed back to the object the SDK serialized it from (axios serializes
|
|
7936
|
-
* again, and an interceptor may read the object); a GET goes through `instance.get`. The answer
|
|
7937
|
-
* is an HTTP response whose `json()`/`text()` read `response.data`. An answer that is not an
|
|
7938
|
-
* axios response is a `TRANSPORT_ERROR` rejection, and so is a rejection that means no HTTP
|
|
7939
|
-
* answer arrived (the original error is `cause`): any rejection with `rejectOnHttpError: false`
|
|
7940
|
-
* (every status resolves there), an axios error without a `response` with `true`. Any other
|
|
7941
|
-
* rejection (an interceptor's error for a 4xx, say) is rethrown unchanged, and so is an abort.
|
|
7942
|
-
*
|
|
7943
|
-
* Pair it with `ChainGateway`'s `dexBackendBaseUrl: ''` when the instance owns the base URL.
|
|
7944
|
-
*
|
|
7945
|
-
* @example
|
|
7946
|
-
* ```ts
|
|
7947
|
-
* const submitGateway = new ChainGateway({
|
|
7948
|
-
* dexBackendBaseUrl: '',
|
|
7949
|
-
* httpRequestor: httpRequestorFromAxios(chainGatewayInstance, { rejectOnHttpError: true }),
|
|
7950
|
-
* });
|
|
7951
|
-
* ```
|
|
7952
|
-
*/
|
|
7953
|
-
function httpRequestorFromAxios(instance, options) {
|
|
7954
|
-
const omit = options.omitHeaders ?? ["content-type"];
|
|
7955
|
-
return async (uri, init) => {
|
|
7956
|
-
const signal = (init === null || init === void 0 ? void 0 : init.signal) ?? void 0;
|
|
7957
|
-
const config = {
|
|
7958
|
-
headers: headersFor(init, omit),
|
|
7959
|
-
...signal ? { signal } : {},
|
|
7960
|
-
...options.rejectOnHttpError ? {} : { validateStatus: () => true }
|
|
7961
|
-
};
|
|
7962
|
-
const isPost = ((init === null || init === void 0 ? void 0 : init.method) ?? "GET").toUpperCase() === "POST";
|
|
7963
|
-
const body = isPost && typeof (init === null || init === void 0 ? void 0 : init.body) === "string" ? JSON.parse(init.body) : void 0;
|
|
7964
|
-
let answer;
|
|
7965
|
-
try {
|
|
7966
|
-
answer = await (isPost ? instance.post(uri, body, config) : instance.get(uri, config));
|
|
7967
|
-
} catch (error) {
|
|
7968
|
-
if ((signal === null || signal === void 0 ? void 0 : signal.aborted) === true || !isTransportRejection(error, options.rejectOnHttpError)) throw error;
|
|
7969
|
-
throw new GSwapSDKError("TRANSPORT_ERROR", { url: uri }, {
|
|
7970
|
-
url: uri,
|
|
7971
|
-
cause: error
|
|
7972
|
-
});
|
|
7973
|
-
}
|
|
7974
|
-
return toHttpResponse(answer, uri);
|
|
7975
|
-
};
|
|
7976
|
-
}
|
|
7977
|
-
/**
|
|
7978
|
-
* Whether an axios rejection means no HTTP answer arrived. With `rejectOnHttpError: false` every
|
|
7979
|
-
* status resolves, so any rejection is one. With `true`, a 4xx/5xx rejects too, so only an axios
|
|
7980
|
-
* error that carries no `response` is (an interceptor's own error is an app answer and passes
|
|
7981
|
-
* through; an app whose interceptors turn network errors into their own class names it in
|
|
7982
|
-
* `ChainGatewayOptions.isTransportFailure`).
|
|
7983
|
-
*/
|
|
7984
|
-
function isTransportRejection(error, rejectOnHttpError) {
|
|
7985
|
-
if (!rejectOnHttpError) return true;
|
|
7986
|
-
return isRecord$1(error) && error["isAxiosError"] === true && error["response"] === void 0;
|
|
7987
|
-
}
|
|
7988
|
-
|
|
7989
|
-
//#endregion
|
|
7990
|
-
//#region src/utils/ethers_provider.ts
|
|
7991
|
-
function isRecord(value) {
|
|
7992
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7993
|
-
}
|
|
7994
|
-
function isTypedDataTypes(value) {
|
|
7995
|
-
return isRecord(value) && Object.values(value).every((fields) => Array.isArray(fields) && fields.every((field) => isRecord(field) && typeof field["name"] === "string" && typeof field["type"] === "string"));
|
|
7996
|
-
}
|
|
7997
|
-
function malformed(method) {
|
|
7998
|
-
return new GSwapSDKError("MALFORMED_WALLET_REQUEST", { method });
|
|
7999
|
-
}
|
|
8000
|
-
/** Splits `eth_signTypedData_v4` params (`[account, JSON payload]`) into what ethers' signer takes. */
|
|
8001
|
-
function readTypedDataRequest(params) {
|
|
8002
|
-
const raw = params === null || params === void 0 ? void 0 : params[1];
|
|
8003
|
-
let payload;
|
|
8004
|
-
try {
|
|
8005
|
-
payload = typeof raw === "string" ? JSON.parse(raw) : raw;
|
|
8006
|
-
} catch {
|
|
8007
|
-
throw malformed("eth_signTypedData_v4");
|
|
8008
|
-
}
|
|
8009
|
-
if (!isRecord(payload)) throw malformed("eth_signTypedData_v4");
|
|
8010
|
-
const { domain, types, message } = payload;
|
|
8011
|
-
if (!isRecord(domain) || !isTypedDataTypes(types) || !isRecord(message)) throw malformed("eth_signTypedData_v4");
|
|
8012
|
-
return {
|
|
8013
|
-
domain,
|
|
8014
|
-
types: stripEip712DomainType(types),
|
|
8015
|
-
message
|
|
8016
|
-
};
|
|
8017
|
-
}
|
|
8018
|
-
/**
|
|
8019
|
-
* An EIP-1193 provider over an ethers v6 provider, for {@link BrowserWalletSigner}: apps whose
|
|
8020
|
-
* wallet connection is an ethers `BrowserProvider` sign through the very ethers calls they always
|
|
8021
|
-
* made, with no hand-written shim.
|
|
8022
|
-
*
|
|
8023
|
-
* - `eth_chainId` is `provider.send('eth_chainId', [])`: the wallet's LIVE chain. Never
|
|
8024
|
-
* `provider.getNetwork()`, which ethers caches and does not invalidate on `chainChanged`, so a
|
|
8025
|
-
* user who switches networks after connecting would sign against a stale chainId.
|
|
8026
|
-
* - `eth_signTypedData_v4` parses the `[account, JSON]` params and calls
|
|
8027
|
-
* `signer.signTypedData(domain, types, message)` with `EIP712Domain` removed from `types`.
|
|
8028
|
-
* - `personal_sign` is `options.personalSign(message)` when given, else `signer.signMessage`.
|
|
8029
|
-
* - Anything else is `UNSUPPORTED_WALLET_METHOD`; a malformed request is
|
|
8030
|
-
* `MALFORMED_WALLET_REQUEST`; a missing provider is `WALLET_UNAVAILABLE`.
|
|
8031
|
-
*
|
|
8032
|
-
* `provider` may be `null`/`undefined` (nothing connected): a request that needs it then fails
|
|
8033
|
-
* with `WALLET_UNAVAILABLE` rather than at construction, so a signer can be built before the
|
|
8034
|
-
* connection state is known.
|
|
8035
|
-
*
|
|
8036
|
-
* @example
|
|
8037
|
-
* ```ts
|
|
8038
|
-
* const signer = new BrowserWalletSigner(eip1193ProviderFromEthers(browserProvider), account, {
|
|
8039
|
-
* scheme: 'eip712',
|
|
8040
|
-
* });
|
|
8041
|
-
* ```
|
|
8042
|
-
*/
|
|
8043
|
-
function eip1193ProviderFromEthers(provider, options = {}) {
|
|
8044
|
-
const connected = () => {
|
|
8045
|
-
if (provider === null || provider === void 0) throw new GSwapSDKError("WALLET_UNAVAILABLE", {});
|
|
8046
|
-
return provider;
|
|
8047
|
-
};
|
|
8048
|
-
return { request: async ({ method, params }) => {
|
|
8049
|
-
switch (method) {
|
|
8050
|
-
case "eth_chainId": return connected().send("eth_chainId", []);
|
|
8051
|
-
case "eth_signTypedData_v4": {
|
|
8052
|
-
const wallet = connected();
|
|
8053
|
-
const { domain, types, message } = readTypedDataRequest(params);
|
|
8054
|
-
return (await wallet.getSigner()).signTypedData(domain, types, message);
|
|
8055
|
-
}
|
|
8056
|
-
case "personal_sign": {
|
|
8057
|
-
const message = params === null || params === void 0 ? void 0 : params[0];
|
|
8058
|
-
if (typeof message !== "string") throw malformed("personal_sign");
|
|
8059
|
-
if (options.personalSign !== void 0) return options.personalSign(message);
|
|
8060
|
-
return (await connected().getSigner()).signMessage(message);
|
|
8061
|
-
}
|
|
8062
|
-
default: throw new GSwapSDKError("UNSUPPORTED_WALLET_METHOD", { method });
|
|
8063
|
-
}
|
|
8064
|
-
} };
|
|
8065
|
-
}
|
|
8066
|
-
|
|
8067
6258
|
//#endregion
|
|
8068
6259
|
//#region src/utils/confirmation_state.ts
|
|
8069
6260
|
/** The state of every confirmation code (a typo is caught by the all-codes test). */
|
|
@@ -8091,7 +6282,7 @@ const STATE_OF_CODE = {
|
|
|
8091
6282
|
*/
|
|
8092
6283
|
function confirmationStateOf(error) {
|
|
8093
6284
|
var _toFailureView, _Object$entries$find;
|
|
8094
|
-
const code = (_toFailureView = toFailureView(error)) === null || _toFailureView === void 0 ? void 0 : _toFailureView.code;
|
|
6285
|
+
const code = (_toFailureView = (0, _gala_chain_gala_core_sdk.toFailureView)(error)) === null || _toFailureView === void 0 ? void 0 : _toFailureView.code;
|
|
8095
6286
|
if (code === void 0) return void 0;
|
|
8096
6287
|
return (_Object$entries$find = Object.entries(STATE_OF_CODE).find(([known]) => known === code)) === null || _Object$entries$find === void 0 ? void 0 : _Object$entries$find[1];
|
|
8097
6288
|
}
|
|
@@ -8104,43 +6295,6 @@ function isConfirmationTimeout(error) {
|
|
|
8104
6295
|
return confirmationStateOf(error) === "timeout";
|
|
8105
6296
|
}
|
|
8106
6297
|
|
|
8107
|
-
//#endregion
|
|
8108
|
-
//#region src/utils/fetch_failure.ts
|
|
8109
|
-
/**
|
|
8110
|
-
* True when a rejection of a `fetch`-based requestor means NO HTTP ANSWER ARRIVED: the network
|
|
8111
|
-
* failed, the connection was reset, the host did not resolve, CORS blocked it. For a write that
|
|
8112
|
-
* is not a refusal (the request may have been queued), so this is the default
|
|
8113
|
-
* `ChainGatewayOptions.isTransportFailure` for a requestor that wraps `fetch` and rejects with
|
|
8114
|
-
* its own errors.
|
|
8115
|
-
*
|
|
8116
|
-
* - Yes: a `TypeError` (what `fetch` rejects with in every engine: `Failed to fetch`, `Load failed`,
|
|
8117
|
-
* `NetworkError when attempting to fetch resource`, undici's `fetch failed`), recognised by name
|
|
8118
|
-
* too so one from another realm (an extension page, an iframe) still counts, and a
|
|
8119
|
-
* `DOMException` named `NetworkError`.
|
|
8120
|
-
* - No: an abort (`AbortError`) or a timeout (`TimeoutError`). The SDK's own request layer knows
|
|
8121
|
-
* whether its timer or the caller's signal aborted, and reports those itself
|
|
8122
|
-
* (`REQUEST_TIMEOUT`, or the caller's own abort unchanged); claiming them here would turn the
|
|
8123
|
-
* caller's cancel into "maybe queued".
|
|
8124
|
-
* - No: anything else, including an SDK error and a value that is not an object.
|
|
8125
|
-
*
|
|
8126
|
-
* It never throws.
|
|
8127
|
-
*
|
|
8128
|
-
* @example
|
|
8129
|
-
* ```ts
|
|
8130
|
-
* new ChainGateway({ dexBackendBaseUrl, httpRequestor: galaFetch, isTransportFailure: isFetchTransportFailure });
|
|
8131
|
-
* ```
|
|
8132
|
-
*/
|
|
8133
|
-
function isFetchTransportFailure(error) {
|
|
8134
|
-
if (typeof error !== "object" || error === null) return false;
|
|
8135
|
-
try {
|
|
8136
|
-
const name = "name" in error ? error.name : void 0;
|
|
8137
|
-
if (name === "AbortError" || name === "TimeoutError") return false;
|
|
8138
|
-
return error instanceof TypeError || name === "TypeError" || name === "NetworkError";
|
|
8139
|
-
} catch {
|
|
8140
|
-
return false;
|
|
8141
|
-
}
|
|
8142
|
-
}
|
|
8143
|
-
|
|
8144
6298
|
//#endregion
|
|
8145
6299
|
//#region src/utils/failure_guards.ts
|
|
8146
6300
|
/**
|
|
@@ -8163,7 +6317,7 @@ function isFetchTransportFailure(error) {
|
|
|
8163
6317
|
* ```
|
|
8164
6318
|
*/
|
|
8165
6319
|
function isUniqueKeyConsumed(error) {
|
|
8166
|
-
const view = toFailureView(error);
|
|
6320
|
+
const view = (0, _gala_chain_gala_core_sdk.toFailureView)(error);
|
|
8167
6321
|
return (view === null || view === void 0 ? void 0 : view.code) === "CHAIN_DISPATCH_FAILED" && view.status === 409;
|
|
8168
6322
|
}
|
|
8169
6323
|
const UNPROVEN_ACKNOWLEDGEMENT_CODES = [
|
|
@@ -8199,7 +6353,7 @@ function isCodeIn(code, codes) {
|
|
|
8199
6353
|
* error (`{ code: 'ACTION_REJECTED' }`, a numeric `4001`) is never a member.
|
|
8200
6354
|
*/
|
|
8201
6355
|
function hasCodeIn(error, codes) {
|
|
8202
|
-
const view = toFailureView(error);
|
|
6356
|
+
const view = (0, _gala_chain_gala_core_sdk.toFailureView)(error);
|
|
8203
6357
|
return view !== void 0 && isCodeIn(view.code, codes);
|
|
8204
6358
|
}
|
|
8205
6359
|
/**
|
|
@@ -8252,10 +6406,10 @@ function isUninterpretedAnswer(error) {
|
|
|
8252
6406
|
* ```
|
|
8253
6407
|
*/
|
|
8254
6408
|
function chainErrorKeyOf(error) {
|
|
8255
|
-
const view = toFailureView(error);
|
|
6409
|
+
const view = (0, _gala_chain_gala_core_sdk.toFailureView)(error);
|
|
8256
6410
|
if (view === void 0) return void 0;
|
|
8257
6411
|
if (view.code !== "CHAIN_REJECTED" && view.code !== "CHAIN_DISPATCH_FAILED") return void 0;
|
|
8258
|
-
const errorKey = stringParamOf(view, "errorKey");
|
|
6412
|
+
const errorKey = (0, _gala_chain_gala_core_sdk.stringParamOf)(view, "errorKey");
|
|
8259
6413
|
return errorKey === void 0 || errorKey === "" ? void 0 : errorKey;
|
|
8260
6414
|
}
|
|
8261
6415
|
/** Codes that mean "there is no market for this" however the app reached them. */
|
|
@@ -8281,10 +6435,10 @@ const NO_MARKET_CODES = [
|
|
|
8281
6435
|
* ```
|
|
8282
6436
|
*/
|
|
8283
6437
|
function isNoMarket(error) {
|
|
8284
|
-
const view = toFailureView(error);
|
|
6438
|
+
const view = (0, _gala_chain_gala_core_sdk.toFailureView)(error);
|
|
8285
6439
|
if (view === void 0) return false;
|
|
8286
6440
|
if (isCodeIn(view.code, NO_MARKET_CODES)) return true;
|
|
8287
|
-
return !isWriteView(view) && (view.notFound === true || view.status === 404);
|
|
6441
|
+
return !(0, _gala_chain_gala_core_sdk.isWriteView)(view) && (view.notFound === true || view.status === 404);
|
|
8288
6442
|
}
|
|
8289
6443
|
|
|
8290
6444
|
//#endregion
|
|
@@ -8331,42 +6485,38 @@ function classifySubmitEnvelope(envelope) {
|
|
|
8331
6485
|
exports.ALL_FEE_TIERS = ALL_FEE_TIERS;
|
|
8332
6486
|
exports.AppliedTradeError = AppliedTradeError;
|
|
8333
6487
|
exports.Assets = Assets;
|
|
8334
|
-
exports.BrowserWalletSigner = BrowserWalletSigner;
|
|
8335
6488
|
exports.CanonicalSymbols = CanonicalSymbols;
|
|
8336
6489
|
exports.ChainGateway = ChainGateway;
|
|
8337
|
-
exports.DEFAULT_UNIQUE_KEY_PREFIX = DEFAULT_UNIQUE_KEY_PREFIX;
|
|
8338
6490
|
exports.DEX_FEE_METHODS = DEX_FEE_METHODS;
|
|
8339
6491
|
exports.DRY_RUN_METHOD = DRY_RUN_METHOD;
|
|
8340
6492
|
exports.DRY_RUN_UNIQUE_KEY_PREFIX = DRY_RUN_UNIQUE_KEY_PREFIX;
|
|
8341
6493
|
exports.DexFees = DexFees;
|
|
8342
|
-
exports.EIP712_CAPABLE_METHODS = EIP712_CAPABLE_METHODS;
|
|
8343
|
-
exports.FAILURE_OUTCOMES = FAILURE_OUTCOMES;
|
|
8344
6494
|
exports.FEE_TIER = FEE_TIER;
|
|
8345
6495
|
exports.GC_MAX_TICK = GC_MAX_TICK;
|
|
8346
6496
|
exports.GC_MIN_TICK = GC_MIN_TICK;
|
|
8347
6497
|
exports.GSWAP_ENVIRONMENTS = GSWAP_ENVIRONMENTS;
|
|
8348
6498
|
exports.GSWAP_ERROR_CATALOG = GSWAP_ERROR_CATALOG;
|
|
8349
|
-
exports.GSWAP_ERROR_PHASES = GSWAP_ERROR_PHASES;
|
|
8350
6499
|
exports.GSWAP_ERROR_PHASE_BY_CODE = GSWAP_ERROR_PHASE_BY_CODE;
|
|
8351
6500
|
exports.GSwap = GSwap;
|
|
8352
6501
|
exports.GSwapSDKError = GSwapSDKError;
|
|
8353
|
-
exports.GalaWalletSigner = GalaWalletSigner;
|
|
8354
6502
|
exports.GdexStreamError = GdexStreamError;
|
|
8355
6503
|
exports.GdexStreamService = GdexStreamService;
|
|
8356
|
-
exports.
|
|
6504
|
+
exports.Leaderboard = Leaderboard;
|
|
6505
|
+
exports.MARKET_PRICE_BATCH_LIMIT = MARKET_PRICE_BATCH_LIMIT;
|
|
6506
|
+
exports.MARKET_PRICE_QUERY_BYTES = MARKET_PRICE_QUERY_BYTES;
|
|
6507
|
+
exports.Market = Market;
|
|
8357
6508
|
exports.NOT_QUEUED_FINAL = NOT_QUEUED_FINAL;
|
|
8358
6509
|
exports.PENDING_SWAPS_STORAGE_KEY = PENDING_SWAPS_STORAGE_KEY;
|
|
8359
6510
|
exports.PENDING_SWAP_EXPIRY_MS = PENDING_SWAP_EXPIRY_MS;
|
|
8360
6511
|
exports.PENDING_SWAP_FATES = PENDING_SWAP_FATES;
|
|
8361
6512
|
exports.PENDING_SWAP_PRUNE_REASONS = PENDING_SWAP_PRUNE_REASONS;
|
|
6513
|
+
exports.PRICE_SERIES_TIMEFRAMES = PRICE_SERIES_TIMEFRAMES;
|
|
8362
6514
|
exports.PendingSwapStore = PendingSwapStore;
|
|
8363
6515
|
exports.Pools = Pools;
|
|
8364
6516
|
exports.Positions = Positions;
|
|
8365
|
-
exports.PrivateKeySigner = PrivateKeySigner;
|
|
8366
6517
|
exports.Quoting = Quoting;
|
|
8367
6518
|
exports.SHADOW_FAILURE_KINDS = SHADOW_FAILURE_KINDS;
|
|
8368
6519
|
exports.SHADOW_SUBMIT_CODES = SHADOW_SUBMIT_CODES;
|
|
8369
|
-
exports.SIGNING_FAILURE_REASONS = SIGNING_FAILURE_REASONS;
|
|
8370
6520
|
exports.SUBMISSION_OUTCOME_UNKNOWN_REASONS = SUBMISSION_OUTCOME_UNKNOWN_REASONS;
|
|
8371
6521
|
exports.SubmittedTransaction = SubmittedTransaction;
|
|
8372
6522
|
exports.Swaps = Swaps;
|
|
@@ -8380,47 +6530,29 @@ exports.alignTickUpOrNaN = alignTickUpOrNaN;
|
|
|
8380
6530
|
exports.assertCanonicalPair = assertCanonicalPair;
|
|
8381
6531
|
exports.assertCanonicalV2Dto = assertCanonicalV2Dto;
|
|
8382
6532
|
exports.assertTickRange = assertTickRange;
|
|
8383
|
-
exports.assertUniqueKey = assertUniqueKey;
|
|
8384
6533
|
exports.buildAddLiquidityDto = buildAddLiquidityDto;
|
|
8385
6534
|
exports.buildCollectPositionFeesDto = buildCollectPositionFeesDto;
|
|
8386
6535
|
exports.buildCreatePoolDto = buildCreatePoolDto;
|
|
8387
6536
|
exports.buildDryRunWrapper = buildDryRunWrapper;
|
|
8388
|
-
exports.buildEIP712Payload = buildEIP712Payload;
|
|
8389
6537
|
exports.buildRemoveLiquidityDto = buildRemoveLiquidityDto;
|
|
8390
6538
|
exports.buildRequestTradeDto = buildRequestTradeDto;
|
|
8391
|
-
exports.calculatePersonalSignPrefix = calculatePersonalSignPrefix;
|
|
8392
|
-
exports.canonicalDecimalString = canonicalDecimalString;
|
|
8393
6539
|
exports.chainErrorKeyOf = chainErrorKeyOf;
|
|
8394
6540
|
exports.classification = classification;
|
|
8395
6541
|
exports.classifyFailure = classifyFailure;
|
|
8396
6542
|
exports.classifyShadowSubmitError = classifyShadowSubmitError;
|
|
8397
6543
|
exports.classifyShadowSubmitStatus = classifyShadowSubmitStatus;
|
|
8398
6544
|
exports.classifySubmitEnvelope = classifySubmitEnvelope;
|
|
8399
|
-
exports.compositeKeyOf = compositeKeyOf;
|
|
8400
6545
|
exports.confirmAppliedTrade = confirmAppliedTrade;
|
|
8401
6546
|
exports.confirmationStateOf = confirmationStateOf;
|
|
8402
|
-
exports.createLazyPrivateKeySigner = createLazyPrivateKeySigner;
|
|
8403
|
-
exports.createUniqueKey = createUniqueKey;
|
|
8404
|
-
exports.eip1193ProviderFromEthers = eip1193ProviderFromEthers;
|
|
8405
|
-
exports.eip712DomainFor = eip712DomainFor;
|
|
8406
|
-
exports.ethAddressOfPrivateKey = ethAddressOfPrivateKey;
|
|
8407
|
-
exports.ethereumAddressOfAccount = ethereumAddressOfAccount;
|
|
8408
|
-
exports.fixpointPrefixFor = fixpointPrefixFor;
|
|
8409
6547
|
exports.frameSlot0 = frameSlot0;
|
|
8410
|
-
exports.generateEIP712Types = generateEIP712Types;
|
|
8411
6548
|
exports.getObjectProperty = getObjectProperty;
|
|
8412
|
-
exports.getPayloadToSign = getPayloadToSign;
|
|
8413
6549
|
exports.getStringProperty = getStringProperty;
|
|
8414
6550
|
exports.gswapErrorTemplates = gswapErrorTemplates;
|
|
8415
|
-
exports.hashTypedData = hashTypedData;
|
|
8416
|
-
exports.httpRequestorFromAxios = httpRequestorFromAxios;
|
|
8417
6551
|
exports.isConfirmationAborted = isConfirmationAborted;
|
|
8418
6552
|
exports.isConfirmationTimeout = isConfirmationTimeout;
|
|
8419
6553
|
exports.isCurrentContractDexMessage = isCurrentContractDexMessage;
|
|
8420
6554
|
exports.isDexFeeMethod = isDexFeeMethod;
|
|
8421
|
-
exports.isFetchTransportFailure = isFetchTransportFailure;
|
|
8422
6555
|
exports.isGSwapError = isGSwapError;
|
|
8423
|
-
exports.isGSwapErrorLike = isGSwapErrorLike;
|
|
8424
6556
|
exports.isGdexLiquidityEvent = isGdexLiquidityEvent;
|
|
8425
6557
|
exports.isNoMarket = isNoMarket;
|
|
8426
6558
|
exports.isPreSendError = isPreSendError;
|
|
@@ -8435,47 +6567,25 @@ exports.isTickInRange = isTickInRange;
|
|
|
8435
6567
|
exports.isUninterpretedAnswer = isUninterpretedAnswer;
|
|
8436
6568
|
exports.isUniqueKeyConsumed = isUniqueKeyConsumed;
|
|
8437
6569
|
exports.isUnprovenAcknowledgement = isUnprovenAcknowledgement;
|
|
8438
|
-
exports.isValidUniqueKey = isValidUniqueKey;
|
|
8439
|
-
exports.isWalletUserRejection = isWalletUserRejection;
|
|
8440
|
-
exports.isWriteView = isWriteView;
|
|
8441
6570
|
exports.mayHaveBeenQueued = mayHaveBeenQueued;
|
|
8442
|
-
exports.normalizePrivateKey = normalizePrivateKey;
|
|
8443
6571
|
exports.notSentClassification = notSentClassification;
|
|
8444
6572
|
exports.orderSymbols = orderSymbols;
|
|
8445
|
-
exports.parseJson = parseJson;
|
|
8446
6573
|
exports.parseTokenClassKey = parseTokenClassKey;
|
|
8447
6574
|
exports.pendingSwapFateFor = pendingSwapFateFor;
|
|
8448
6575
|
exports.pendingSwapFateOfOutcome = pendingSwapFateOfOutcome;
|
|
8449
|
-
exports.
|
|
6576
|
+
exports.priceBatches = priceBatches;
|
|
8450
6577
|
exports.queryTokenOf = queryTokenOf;
|
|
8451
6578
|
exports.rawTokenOf = rawTokenOf;
|
|
8452
6579
|
exports.readOrderedSymbols = readOrderedSymbols;
|
|
8453
6580
|
exports.readShadowSubmitOutcome = readShadowSubmitOutcome;
|
|
8454
|
-
exports.renderGSwapErrorTemplate = renderGSwapErrorTemplate;
|
|
8455
|
-
exports.resolvePersonalSignChainDtoPayload = resolvePersonalSignChainDtoPayload;
|
|
8456
|
-
exports.resolveUniqueKeyPrefix = resolveUniqueKeyPrefix;
|
|
8457
|
-
exports.serialize = serialize;
|
|
8458
|
-
exports.signChainDtoHash = signChainDtoHash;
|
|
8459
|
-
exports.signChainDtoNative = signChainDtoNative;
|
|
8460
|
-
exports.signChainDtoPersonalSign = signChainDtoPersonalSign;
|
|
8461
|
-
exports.signNative = signNative;
|
|
8462
|
-
exports.signPersonalMessage = signPersonalMessage;
|
|
8463
|
-
exports.signRawObject = signRawObject;
|
|
8464
|
-
exports.signSerializedNative = signSerializedNative;
|
|
8465
|
-
exports.signTypedDataPayload = signTypedDataPayload;
|
|
8466
6581
|
exports.sqrtPriceToTick = sqrtPriceToTick;
|
|
8467
6582
|
exports.storageAdapterFromChromeStorage = storageAdapterFromChromeStorage;
|
|
8468
|
-
exports.stripEip712DomainType = stripEip712DomainType;
|
|
8469
6583
|
exports.swapRouteFromQuote = swapRouteFromQuote;
|
|
8470
6584
|
exports.tickFromPrice = tickFromPrice;
|
|
8471
6585
|
exports.tickToPrice = tickToPrice;
|
|
8472
6586
|
exports.tickToSqrtPrice = tickToSqrtPrice;
|
|
8473
6587
|
exports.toChainDto = toChainDto;
|
|
8474
|
-
exports.toFailureView = toFailureView;
|
|
8475
|
-
exports.tokenClassDollarKey = tokenClassDollarKey;
|
|
8476
6588
|
exports.tradingSymbolOf = tradingSymbolOf;
|
|
8477
|
-
exports.tryParseCompositeKey = tryParseCompositeKey;
|
|
8478
|
-
exports.tryParseTokenClassKey = tryParseTokenClassKey;
|
|
8479
6589
|
exports.validateFee = validateFee;
|
|
8480
6590
|
exports.validateNumericAmount = validateNumericAmount;
|
|
8481
6591
|
exports.validatePriceValues = validatePriceValues;
|
|
@@ -8483,7 +6593,5 @@ exports.validateTickRange = validateTickRange;
|
|
|
8483
6593
|
exports.validateTickSpacing = validateTickSpacing;
|
|
8484
6594
|
exports.validateTokenDecimals = validateTokenDecimals;
|
|
8485
6595
|
exports.validateWalletAddress = validateWalletAddress;
|
|
8486
|
-
exports.walletDeclineCode = walletDeclineCode;
|
|
8487
6596
|
exports.webStorageAdapter = webStorageAdapter;
|
|
8488
|
-
exports.withPrivateKey = withPrivateKey;
|
|
8489
6597
|
//# sourceMappingURL=index.cjs.map
|