@flashnet/sdk 0.3.24 → 0.3.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.d.ts +2 -2
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/src/api/typed-endpoints.d.ts +15 -0
- package/dist/cjs/src/api/typed-endpoints.d.ts.map +1 -1
- package/dist/cjs/src/api/typed-endpoints.js +22 -0
- package/dist/cjs/src/api/typed-endpoints.js.map +1 -1
- package/dist/cjs/src/client/FlashnetClient.d.ts +18 -0
- package/dist/cjs/src/client/FlashnetClient.d.ts.map +1 -1
- package/dist/cjs/src/client/FlashnetClient.js +338 -88
- package/dist/cjs/src/client/FlashnetClient.js.map +1 -1
- package/dist/cjs/src/config/index.js +2 -2
- package/dist/cjs/src/config/index.js.map +1 -1
- package/dist/cjs/src/types/index.d.ts +19 -0
- package/dist/cjs/src/types/index.d.ts.map +1 -1
- package/dist/cjs/src/types/index.js.map +1 -1
- package/dist/cjs/src/utils/auth.d.ts.map +1 -1
- package/dist/cjs/src/utils/auth.js +5 -3
- package/dist/cjs/src/utils/auth.js.map +1 -1
- package/dist/cjs/src/utils/hex.d.ts +3 -0
- package/dist/cjs/src/utils/hex.d.ts.map +1 -0
- package/dist/cjs/src/utils/hex.js +17 -0
- package/dist/cjs/src/utils/hex.js.map +1 -0
- package/dist/cjs/src/utils/index.js +2 -2
- package/dist/cjs/src/utils/spark-address.js +16 -16
- package/dist/cjs/src/utils/spark-address.js.map +1 -1
- package/dist/cjs/src/utils/tokenAddress.d.ts.map +1 -1
- package/dist/cjs/src/utils/tokenAddress.js +8 -8
- package/dist/cjs/src/utils/tokenAddress.js.map +1 -1
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/src/api/typed-endpoints.d.ts +15 -0
- package/dist/esm/src/api/typed-endpoints.d.ts.map +1 -1
- package/dist/esm/src/api/typed-endpoints.js +22 -0
- package/dist/esm/src/api/typed-endpoints.js.map +1 -1
- package/dist/esm/src/client/FlashnetClient.d.ts +18 -0
- package/dist/esm/src/client/FlashnetClient.d.ts.map +1 -1
- package/dist/esm/src/client/FlashnetClient.js +338 -88
- package/dist/esm/src/client/FlashnetClient.js.map +1 -1
- package/dist/esm/src/config/index.js +2 -2
- package/dist/esm/src/config/index.js.map +1 -1
- package/dist/esm/src/types/index.d.ts +19 -0
- package/dist/esm/src/types/index.d.ts.map +1 -1
- package/dist/esm/src/types/index.js.map +1 -1
- package/dist/esm/src/utils/auth.d.ts.map +1 -1
- package/dist/esm/src/utils/auth.js +5 -3
- package/dist/esm/src/utils/auth.js.map +1 -1
- package/dist/esm/src/utils/hex.d.ts +3 -0
- package/dist/esm/src/utils/hex.d.ts.map +1 -0
- package/dist/esm/src/utils/hex.js +14 -0
- package/dist/esm/src/utils/hex.js.map +1 -0
- package/dist/esm/src/utils/index.js +2 -2
- package/dist/esm/src/utils/spark-address.js +8 -8
- package/dist/esm/src/utils/spark-address.js.map +1 -1
- package/dist/esm/src/utils/tokenAddress.d.ts.map +1 -1
- package/dist/esm/src/utils/tokenAddress.js +2 -2
- package/package.json +7 -7
|
@@ -4,6 +4,7 @@ import { getClientEnvironmentName, resolveClientNetworkConfig, getClientNetworkC
|
|
|
4
4
|
import { getSparkNetworkFromLegacy, getClientEnvironmentFromLegacy, Network } from '../types/index.js';
|
|
5
5
|
import { generateNonce, compareDecimalStrings } from '../utils/index.js';
|
|
6
6
|
import { AuthManager } from '../utils/auth.js';
|
|
7
|
+
import { getHexFromUint8Array } from '../utils/hex.js';
|
|
7
8
|
import { generateConstantProductPoolInitializationIntentMessage, generatePoolInitializationIntentMessage, generatePoolConfirmInitialDepositIntentMessage, generatePoolSwapIntentMessage, generateRouteSwapIntentMessage, generateAddLiquidityIntentMessage, generateRemoveLiquidityIntentMessage, generateRegisterHostIntentMessage, generateWithdrawHostFeesIntentMessage, generateWithdrawIntegratorFeesIntentMessage, generateCreateEscrowIntentMessage, generateFundEscrowIntentMessage, generateClaimEscrowIntentMessage, generateClawbackIntentMessage } from '../utils/intents.js';
|
|
8
9
|
import { createWalletSigner } from '../utils/signer.js';
|
|
9
10
|
import { getSparkNetworkFromAddress, encodeSparkAddressNew } from '../utils/spark-address.js';
|
|
@@ -29,6 +30,16 @@ class FlashnetClient {
|
|
|
29
30
|
publicKey = "";
|
|
30
31
|
sparkAddress = "";
|
|
31
32
|
isAuthenticated = false;
|
|
33
|
+
// Ephemeral caches for config endpoints and ping
|
|
34
|
+
featureStatusCache;
|
|
35
|
+
minAmountsCache;
|
|
36
|
+
allowedAssetsCache;
|
|
37
|
+
pingCache;
|
|
38
|
+
// TTLs (milliseconds)
|
|
39
|
+
static FEATURE_STATUS_TTL_MS = 5000; // 5s
|
|
40
|
+
static MIN_AMOUNTS_TTL_MS = 5000; // 5s
|
|
41
|
+
static ALLOWED_ASSETS_TTL_MS = 60000; // 60s
|
|
42
|
+
static PING_TTL_MS = 2000; // 2s
|
|
32
43
|
/**
|
|
33
44
|
* Get the underlying wallet instance for direct wallet operations
|
|
34
45
|
*/
|
|
@@ -225,7 +236,7 @@ class FlashnetClient {
|
|
|
225
236
|
for (const [tokenPubkey, tokenData] of balance.tokenBalances.entries()) {
|
|
226
237
|
const info = tokenData.tokenMetadata;
|
|
227
238
|
// Convert raw token identifier to hex and human-readable forms
|
|
228
|
-
const tokenIdentifierHex =
|
|
239
|
+
const tokenIdentifierHex = getHexFromUint8Array(info.rawTokenIdentifier);
|
|
229
240
|
const tokenAddress = encodeSparkHumanReadableTokenIdentifier(info.rawTokenIdentifier, this.sparkNetwork);
|
|
230
241
|
tokenBalances.set(tokenPubkey, {
|
|
231
242
|
balance: BigInt(tokenData.balance),
|
|
@@ -323,6 +334,8 @@ class FlashnetClient {
|
|
|
323
334
|
*/
|
|
324
335
|
async createConstantProductPool(params) {
|
|
325
336
|
await this.ensureInitialized();
|
|
337
|
+
await this.ensureAmmOperationAllowed("allow_pool_creation");
|
|
338
|
+
await this.assertAllowedAssetBForPoolCreation(this.toHexTokenIdentifier(params.assetBAddress));
|
|
326
339
|
// Check if we need to add initial liquidity
|
|
327
340
|
if (params.initialLiquidity) {
|
|
328
341
|
await this.checkBalance({
|
|
@@ -362,7 +375,7 @@ class FlashnetClient {
|
|
|
362
375
|
totalHostFeeRateBps: params.totalHostFeeRateBps.toString(),
|
|
363
376
|
hostNamespace: params.hostNamespace || "",
|
|
364
377
|
nonce,
|
|
365
|
-
signature:
|
|
378
|
+
signature: getHexFromUint8Array(signature),
|
|
366
379
|
};
|
|
367
380
|
const response = await this.typedApi.createConstantProductPool(request);
|
|
368
381
|
// Add initial liquidity if specified
|
|
@@ -371,6 +384,31 @@ class FlashnetClient {
|
|
|
371
384
|
}
|
|
372
385
|
return response;
|
|
373
386
|
}
|
|
387
|
+
// Validate and normalize inputs to bigint
|
|
388
|
+
static parsePositiveIntegerToBigInt(value, name) {
|
|
389
|
+
if (typeof value === "bigint") {
|
|
390
|
+
if (value <= 0n) {
|
|
391
|
+
throw new Error(`${name} must be positive integer`);
|
|
392
|
+
}
|
|
393
|
+
return value;
|
|
394
|
+
}
|
|
395
|
+
if (typeof value === "number") {
|
|
396
|
+
if (!Number.isFinite(value) || !Number.isInteger(value) || value <= 0) {
|
|
397
|
+
throw new Error(`${name} must be positive integer`);
|
|
398
|
+
}
|
|
399
|
+
return BigInt(value);
|
|
400
|
+
}
|
|
401
|
+
try {
|
|
402
|
+
const v = BigInt(value);
|
|
403
|
+
if (v <= 0n) {
|
|
404
|
+
throw new Error(`${name} must be positive integer`);
|
|
405
|
+
}
|
|
406
|
+
return v;
|
|
407
|
+
}
|
|
408
|
+
catch {
|
|
409
|
+
throw new Error(`${name} must be positive integer`);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
374
412
|
/**
|
|
375
413
|
* Calculates virtual reserves for a bonding curve AMM.
|
|
376
414
|
*
|
|
@@ -385,69 +423,36 @@ class FlashnetClient {
|
|
|
385
423
|
* @returns An object containing `virtualReserveA`, `virtualReserveB`, and `threshold`.
|
|
386
424
|
*/
|
|
387
425
|
static calculateVirtualReserves(params) {
|
|
388
|
-
// Validate and normalize inputs to bigint
|
|
389
|
-
const parsePositiveIntegerToBigInt = (value, name) => {
|
|
390
|
-
if (typeof value === "bigint") {
|
|
391
|
-
if (value <= 0n) {
|
|
392
|
-
throw new Error(`${name} must be positive integer`);
|
|
393
|
-
}
|
|
394
|
-
return value;
|
|
395
|
-
}
|
|
396
|
-
if (typeof value === "number") {
|
|
397
|
-
if (!Number.isFinite(value) || !Number.isInteger(value) || value <= 0) {
|
|
398
|
-
throw new Error(`${name} must be positive integer`);
|
|
399
|
-
}
|
|
400
|
-
return BigInt(value);
|
|
401
|
-
}
|
|
402
|
-
try {
|
|
403
|
-
const v = BigInt(value);
|
|
404
|
-
if (v <= 0n) {
|
|
405
|
-
throw new Error(`${name} must be positive integer`);
|
|
406
|
-
}
|
|
407
|
-
return v;
|
|
408
|
-
}
|
|
409
|
-
catch {
|
|
410
|
-
throw new Error(`${name} must be positive integer`);
|
|
411
|
-
}
|
|
412
|
-
};
|
|
413
426
|
if (!Number.isFinite(params.graduationThresholdPct) ||
|
|
414
|
-
!Number.isInteger(params.graduationThresholdPct)
|
|
415
|
-
|
|
416
|
-
throw new Error("Graduation threshold percentage must be a positive integer");
|
|
427
|
+
!Number.isInteger(params.graduationThresholdPct)) {
|
|
428
|
+
throw new Error("Graduation threshold percentage must be an integer number of percent");
|
|
417
429
|
}
|
|
418
|
-
const supply = parsePositiveIntegerToBigInt(params.initialTokenSupply, "Initial token supply");
|
|
419
|
-
const targetB = parsePositiveIntegerToBigInt(params.targetRaise, "Target raise");
|
|
430
|
+
const supply = FlashnetClient.parsePositiveIntegerToBigInt(params.initialTokenSupply, "Initial token supply");
|
|
431
|
+
const targetB = FlashnetClient.parsePositiveIntegerToBigInt(params.targetRaise, "Target raise");
|
|
420
432
|
const graduationThresholdPct = BigInt(params.graduationThresholdPct);
|
|
421
|
-
//
|
|
422
|
-
const
|
|
423
|
-
const
|
|
424
|
-
if (graduationThresholdPct
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
//
|
|
433
|
-
//
|
|
434
|
-
|
|
435
|
-
const
|
|
436
|
-
const
|
|
437
|
-
|
|
438
|
-
const
|
|
439
|
-
|
|
440
|
-
const
|
|
441
|
-
|
|
442
|
-
const
|
|
443
|
-
|
|
444
|
-
// Calculate threshold as amount of asset A (not percentage)
|
|
445
|
-
const threshold = (BigInt(supply) * graduationThresholdPct) / 100n;
|
|
446
|
-
return {
|
|
447
|
-
virtualReserveA: virtualA,
|
|
448
|
-
virtualReserveB: virtualB,
|
|
449
|
-
threshold: threshold,
|
|
450
|
-
};
|
|
433
|
+
// Align bounds with Rust AMM (20%..95%), then check feasibility for g=1 (requires >50%).
|
|
434
|
+
const MIN_PCT = 20n;
|
|
435
|
+
const MAX_PCT = 95n;
|
|
436
|
+
if (graduationThresholdPct < MIN_PCT || graduationThresholdPct > MAX_PCT) {
|
|
437
|
+
throw new Error(`Graduation threshold percentage must be between ${MIN_PCT} and ${MAX_PCT}`);
|
|
438
|
+
}
|
|
439
|
+
// Feasibility: denom = f - g*(1-f) > 0 with g=1 -> 2f - 1 > 0 -> pct > 50
|
|
440
|
+
const denomNormalized = 2n * graduationThresholdPct - 100n; // equals 100*(f - (1-f))
|
|
441
|
+
if (denomNormalized <= 0n) {
|
|
442
|
+
throw new Error("Invalid configuration: threshold must be greater than 50% when LP fraction is 1.0");
|
|
443
|
+
}
|
|
444
|
+
// v_A = S * f^2 / (f - (1-f)) ; using integer math with pct where
|
|
445
|
+
// v_A = S * p^2 / (100 * (2p - 100))
|
|
446
|
+
const vANumerator = supply * graduationThresholdPct * graduationThresholdPct;
|
|
447
|
+
const vADenominator = 100n * denomNormalized;
|
|
448
|
+
const virtualA = vANumerator / vADenominator; // floor
|
|
449
|
+
// v_B = T * (1 - f) / (f - (1-f)) ; with pct => T * (100 - p) / (2p - 100)
|
|
450
|
+
const vBNumerator = targetB * (100n - graduationThresholdPct);
|
|
451
|
+
const vBDenominator = denomNormalized;
|
|
452
|
+
const virtualB = vBNumerator / vBDenominator; // floor
|
|
453
|
+
// Threshold amount in A
|
|
454
|
+
const threshold = (supply * graduationThresholdPct) / 100n;
|
|
455
|
+
return { virtualReserveA: virtualA, virtualReserveB: virtualB, threshold };
|
|
451
456
|
}
|
|
452
457
|
/**
|
|
453
458
|
* Create a single-sided pool with automatic initial deposit
|
|
@@ -457,18 +462,20 @@ class FlashnetClient {
|
|
|
457
462
|
*/
|
|
458
463
|
async createSingleSidedPool(params) {
|
|
459
464
|
await this.ensureInitialized();
|
|
465
|
+
await this.ensureAmmOperationAllowed("allow_pool_creation");
|
|
466
|
+
await this.assertAllowedAssetBForPoolCreation(this.toHexTokenIdentifier(params.assetBAddress));
|
|
460
467
|
if (!params.hostNamespace && params.totalHostFeeRateBps < 10) {
|
|
461
468
|
throw new Error(`Host fee must be greater than 10 bps when no host namespace is provided`);
|
|
462
469
|
}
|
|
463
|
-
//
|
|
464
|
-
const
|
|
465
|
-
const
|
|
466
|
-
const
|
|
470
|
+
// Validate reserves are valid positive integers before any operations
|
|
471
|
+
const assetAInitialReserve = FlashnetClient.parsePositiveIntegerToBigInt(params.assetAInitialReserve, "Asset A Initial Reserve").toString();
|
|
472
|
+
const virtualReserveA = FlashnetClient.parsePositiveIntegerToBigInt(params.virtualReserveA, "Virtual Reserve A").toString();
|
|
473
|
+
const virtualReserveB = FlashnetClient.parsePositiveIntegerToBigInt(params.virtualReserveB, "Virtual Reserve B").toString();
|
|
467
474
|
await this.checkBalance({
|
|
468
475
|
balancesToCheck: [
|
|
469
476
|
{
|
|
470
477
|
assetAddress: params.assetAAddress,
|
|
471
|
-
amount:
|
|
478
|
+
amount: assetAInitialReserve,
|
|
472
479
|
},
|
|
473
480
|
],
|
|
474
481
|
errorPrefix: "Insufficient balance for pool creation: ",
|
|
@@ -480,9 +487,9 @@ class FlashnetClient {
|
|
|
480
487
|
poolOwnerPublicKey,
|
|
481
488
|
assetAAddress: this.toHexTokenIdentifier(params.assetAAddress),
|
|
482
489
|
assetBAddress: this.toHexTokenIdentifier(params.assetBAddress),
|
|
483
|
-
assetAInitialReserve
|
|
484
|
-
virtualReserveA
|
|
485
|
-
virtualReserveB
|
|
490
|
+
assetAInitialReserve,
|
|
491
|
+
virtualReserveA,
|
|
492
|
+
virtualReserveB,
|
|
486
493
|
threshold: params.threshold.toString(),
|
|
487
494
|
lpFeeRateBps: params.lpFeeRateBps.toString(),
|
|
488
495
|
totalHostFeeRateBps: params.totalHostFeeRateBps.toString(),
|
|
@@ -495,15 +502,15 @@ class FlashnetClient {
|
|
|
495
502
|
poolOwnerPublicKey,
|
|
496
503
|
assetAAddress: this.toHexTokenIdentifier(params.assetAAddress),
|
|
497
504
|
assetBAddress: this.toHexTokenIdentifier(params.assetBAddress),
|
|
498
|
-
assetAInitialReserve
|
|
499
|
-
virtualReserveA
|
|
500
|
-
virtualReserveB
|
|
505
|
+
assetAInitialReserve,
|
|
506
|
+
virtualReserveA,
|
|
507
|
+
virtualReserveB,
|
|
501
508
|
threshold: params.threshold.toString(),
|
|
502
509
|
lpFeeRateBps: params.lpFeeRateBps.toString(),
|
|
503
510
|
totalHostFeeRateBps: params.totalHostFeeRateBps.toString(),
|
|
504
511
|
hostNamespace: params.hostNamespace,
|
|
505
512
|
nonce,
|
|
506
|
-
signature:
|
|
513
|
+
signature: getHexFromUint8Array(signature),
|
|
507
514
|
};
|
|
508
515
|
const createResponse = await this.typedApi.createSingleSidedPool(request);
|
|
509
516
|
if (params.disableInitialDeposit) {
|
|
@@ -517,7 +524,7 @@ class FlashnetClient {
|
|
|
517
524
|
const assetATransferId = await this.transferAsset({
|
|
518
525
|
receiverSparkAddress: lpSparkAddress,
|
|
519
526
|
assetAddress: params.assetAAddress,
|
|
520
|
-
amount:
|
|
527
|
+
amount: assetAInitialReserve,
|
|
521
528
|
});
|
|
522
529
|
const confirmResponse = await this.confirmInitialDeposit(createResponse.poolId, assetATransferId, poolOwnerPublicKey);
|
|
523
530
|
if (!confirmResponse.confirmed) {
|
|
@@ -546,7 +553,7 @@ class FlashnetClient {
|
|
|
546
553
|
poolId,
|
|
547
554
|
assetASparkTransferId,
|
|
548
555
|
nonce,
|
|
549
|
-
signature:
|
|
556
|
+
signature: getHexFromUint8Array(signature),
|
|
550
557
|
poolOwnerPublicKey: poolOwnerPublicKey ?? this.publicKey,
|
|
551
558
|
};
|
|
552
559
|
return this.typedApi.confirmInitialDeposit(request);
|
|
@@ -557,6 +564,7 @@ class FlashnetClient {
|
|
|
557
564
|
*/
|
|
558
565
|
async simulateSwap(params) {
|
|
559
566
|
await this.ensureInitialized();
|
|
567
|
+
await this.ensurePingOk();
|
|
560
568
|
return this.typedApi.simulateSwap(params);
|
|
561
569
|
}
|
|
562
570
|
/**
|
|
@@ -564,6 +572,14 @@ class FlashnetClient {
|
|
|
564
572
|
*/
|
|
565
573
|
async executeSwap(params) {
|
|
566
574
|
await this.ensureInitialized();
|
|
575
|
+
// Gate by feature flags and ping, and enforce min-amount policy before transfers
|
|
576
|
+
await this.ensureAmmOperationAllowed("allow_swaps");
|
|
577
|
+
await this.assertSwapMeetsMinAmounts({
|
|
578
|
+
assetInAddress: params.assetInAddress,
|
|
579
|
+
assetOutAddress: params.assetOutAddress,
|
|
580
|
+
amountIn: params.amountIn,
|
|
581
|
+
minAmountOut: params.minAmountOut,
|
|
582
|
+
});
|
|
567
583
|
// Transfer assets to pool using new address encoding
|
|
568
584
|
const lpSparkAddress = encodeSparkAddressNew({
|
|
569
585
|
identityPublicKey: params.poolId,
|
|
@@ -582,6 +598,14 @@ class FlashnetClient {
|
|
|
582
598
|
}
|
|
583
599
|
async executeSwapIntent(params) {
|
|
584
600
|
await this.ensureInitialized();
|
|
601
|
+
// Also enforce gating and min amounts for direct intent usage
|
|
602
|
+
await this.ensureAmmOperationAllowed("allow_swaps");
|
|
603
|
+
await this.assertSwapMeetsMinAmounts({
|
|
604
|
+
assetInAddress: params.assetInAddress,
|
|
605
|
+
assetOutAddress: params.assetOutAddress,
|
|
606
|
+
amountIn: params.amountIn,
|
|
607
|
+
minAmountOut: params.minAmountOut,
|
|
608
|
+
});
|
|
585
609
|
// Generate swap intent
|
|
586
610
|
const nonce = generateNonce();
|
|
587
611
|
const intentMessage = generatePoolSwapIntentMessage({
|
|
@@ -611,7 +635,7 @@ class FlashnetClient {
|
|
|
611
635
|
totalIntegratorFeeRateBps: params.integratorFeeRateBps?.toString() || "0",
|
|
612
636
|
integratorPublicKey: params.integratorPublicKey || "",
|
|
613
637
|
nonce,
|
|
614
|
-
signature:
|
|
638
|
+
signature: getHexFromUint8Array(signature),
|
|
615
639
|
};
|
|
616
640
|
const response = await this.typedApi.executeSwap(request);
|
|
617
641
|
// Check if the swap was accepted
|
|
@@ -632,6 +656,7 @@ class FlashnetClient {
|
|
|
632
656
|
throw new Error("Route swap cannot have more than 4 hops");
|
|
633
657
|
}
|
|
634
658
|
await this.ensureInitialized();
|
|
659
|
+
await this.ensurePingOk();
|
|
635
660
|
return this.typedApi.simulateRouteSwap(params);
|
|
636
661
|
}
|
|
637
662
|
/**
|
|
@@ -639,6 +664,18 @@ class FlashnetClient {
|
|
|
639
664
|
*/
|
|
640
665
|
async executeRouteSwap(params) {
|
|
641
666
|
await this.ensureInitialized();
|
|
667
|
+
await this.ensureAmmOperationAllowed("allow_route_swaps");
|
|
668
|
+
// Validate min-amount policy for route: check initial input and final output asset
|
|
669
|
+
const finalOutputAsset = params.hops[params.hops.length - 1]?.assetOutAddress;
|
|
670
|
+
if (!finalOutputAsset) {
|
|
671
|
+
throw new Error("Route swap requires at least one hop with output asset");
|
|
672
|
+
}
|
|
673
|
+
await this.assertSwapMeetsMinAmounts({
|
|
674
|
+
assetInAddress: params.initialAssetAddress,
|
|
675
|
+
assetOutAddress: finalOutputAsset,
|
|
676
|
+
amountIn: params.inputAmount,
|
|
677
|
+
minAmountOut: params.minAmountOut,
|
|
678
|
+
});
|
|
642
679
|
// Validate hops array
|
|
643
680
|
if (params.hops.length > 4) {
|
|
644
681
|
throw new Error("Route swap cannot have more than 4 hops");
|
|
@@ -708,7 +745,7 @@ class FlashnetClient {
|
|
|
708
745
|
maxRouteSlippageBps: params.maxRouteSlippageBps.toString(),
|
|
709
746
|
minAmountOut: params.minAmountOut,
|
|
710
747
|
nonce,
|
|
711
|
-
signature:
|
|
748
|
+
signature: getHexFromUint8Array(signature),
|
|
712
749
|
integratorFeeRateBps: params.integratorFeeRateBps?.toString() || "0",
|
|
713
750
|
integratorPublicKey: params.integratorPublicKey || "",
|
|
714
751
|
};
|
|
@@ -729,6 +766,7 @@ class FlashnetClient {
|
|
|
729
766
|
*/
|
|
730
767
|
async simulateAddLiquidity(params) {
|
|
731
768
|
await this.ensureInitialized();
|
|
769
|
+
await this.ensurePingOk();
|
|
732
770
|
return this.typedApi.simulateAddLiquidity(params);
|
|
733
771
|
}
|
|
734
772
|
/**
|
|
@@ -736,8 +774,15 @@ class FlashnetClient {
|
|
|
736
774
|
*/
|
|
737
775
|
async addLiquidity(params) {
|
|
738
776
|
await this.ensureInitialized();
|
|
777
|
+
await this.ensureAmmOperationAllowed("allow_add_liquidity");
|
|
739
778
|
// Get pool details to know which assets we're dealing with
|
|
740
779
|
const pool = await this.getPool(params.poolId);
|
|
780
|
+
// Enforce min-amount policy for inputs based on pool assets
|
|
781
|
+
await this.assertAddLiquidityMeetsMinAmounts({
|
|
782
|
+
poolId: params.poolId,
|
|
783
|
+
assetAAmount: params.assetAAmount,
|
|
784
|
+
assetBAmount: params.assetBAmount,
|
|
785
|
+
});
|
|
741
786
|
// Transfer assets to pool using new address encoding
|
|
742
787
|
const lpSparkAddress = encodeSparkAddressNew({
|
|
743
788
|
identityPublicKey: params.poolId,
|
|
@@ -781,7 +826,7 @@ class FlashnetClient {
|
|
|
781
826
|
assetAMinAmountIn: params.assetAMinAmountIn.toString(),
|
|
782
827
|
assetBMinAmountIn: params.assetBMinAmountIn.toString(),
|
|
783
828
|
nonce,
|
|
784
|
-
signature:
|
|
829
|
+
signature: getHexFromUint8Array(signature),
|
|
785
830
|
};
|
|
786
831
|
const response = await this.typedApi.addLiquidity(request);
|
|
787
832
|
// Check if the liquidity addition was accepted
|
|
@@ -799,6 +844,7 @@ class FlashnetClient {
|
|
|
799
844
|
*/
|
|
800
845
|
async simulateRemoveLiquidity(params) {
|
|
801
846
|
await this.ensureInitialized();
|
|
847
|
+
await this.ensurePingOk();
|
|
802
848
|
return this.typedApi.simulateRemoveLiquidity(params);
|
|
803
849
|
}
|
|
804
850
|
/**
|
|
@@ -806,6 +852,7 @@ class FlashnetClient {
|
|
|
806
852
|
*/
|
|
807
853
|
async removeLiquidity(params) {
|
|
808
854
|
await this.ensureInitialized();
|
|
855
|
+
await this.ensureAmmOperationAllowed("allow_withdraw_liquidity");
|
|
809
856
|
// Check LP token balance
|
|
810
857
|
const position = await this.getLpPosition(params.poolId);
|
|
811
858
|
const lpTokensOwned = position.lpTokensOwned;
|
|
@@ -813,6 +860,11 @@ class FlashnetClient {
|
|
|
813
860
|
if (compareDecimalStrings(lpTokensOwned, tokensToRemove) < 0) {
|
|
814
861
|
throw new Error(`Insufficient LP tokens. Owned: ${lpTokensOwned}, Requested: ${tokensToRemove}`);
|
|
815
862
|
}
|
|
863
|
+
// Pre-simulate and enforce min-amount policy for outputs
|
|
864
|
+
await this.assertRemoveLiquidityMeetsMinAmounts({
|
|
865
|
+
poolId: params.poolId,
|
|
866
|
+
lpTokensToRemove: params.lpTokensToRemove,
|
|
867
|
+
});
|
|
816
868
|
// Generate remove liquidity intent
|
|
817
869
|
const nonce = generateNonce();
|
|
818
870
|
const intentMessage = generateRemoveLiquidityIntentMessage({
|
|
@@ -829,7 +881,7 @@ class FlashnetClient {
|
|
|
829
881
|
poolId: params.poolId,
|
|
830
882
|
lpTokensToRemove: params.lpTokensToRemove,
|
|
831
883
|
nonce,
|
|
832
|
-
signature:
|
|
884
|
+
signature: getHexFromUint8Array(signature),
|
|
833
885
|
};
|
|
834
886
|
const response = await this.typedApi.removeLiquidity(request);
|
|
835
887
|
// Check if the liquidity removal was accepted
|
|
@@ -845,6 +897,7 @@ class FlashnetClient {
|
|
|
845
897
|
*/
|
|
846
898
|
async registerHost(params) {
|
|
847
899
|
await this.ensureInitialized();
|
|
900
|
+
await this.ensurePingOk();
|
|
848
901
|
const feeRecipient = params.feeRecipientPublicKey || this.publicKey;
|
|
849
902
|
const nonce = generateNonce();
|
|
850
903
|
// Generate intent
|
|
@@ -862,7 +915,7 @@ class FlashnetClient {
|
|
|
862
915
|
minFeeBps: params.minFeeBps,
|
|
863
916
|
feeRecipientPublicKey: feeRecipient,
|
|
864
917
|
nonce,
|
|
865
|
-
signature:
|
|
918
|
+
signature: getHexFromUint8Array(signature),
|
|
866
919
|
};
|
|
867
920
|
return this.typedApi.registerHost(request);
|
|
868
921
|
}
|
|
@@ -878,6 +931,7 @@ class FlashnetClient {
|
|
|
878
931
|
*/
|
|
879
932
|
async getPoolHostFees(hostNamespace, poolId) {
|
|
880
933
|
await this.ensureInitialized();
|
|
934
|
+
await this.ensurePingOk();
|
|
881
935
|
return this.typedApi.getPoolHostFees({ hostNamespace, poolId });
|
|
882
936
|
}
|
|
883
937
|
/**
|
|
@@ -892,6 +946,7 @@ class FlashnetClient {
|
|
|
892
946
|
*/
|
|
893
947
|
async withdrawHostFees(params) {
|
|
894
948
|
await this.ensureInitialized();
|
|
949
|
+
await this.ensureAmmOperationAllowed("allow_withdraw_fees");
|
|
895
950
|
const nonce = generateNonce();
|
|
896
951
|
const intentMessage = generateWithdrawHostFeesIntentMessage({
|
|
897
952
|
hostPublicKey: this.publicKey,
|
|
@@ -906,7 +961,7 @@ class FlashnetClient {
|
|
|
906
961
|
lpIdentityPublicKey: params.lpIdentityPublicKey,
|
|
907
962
|
assetBAmount: params.assetBAmount,
|
|
908
963
|
nonce,
|
|
909
|
-
signature:
|
|
964
|
+
signature: getHexFromUint8Array(signature),
|
|
910
965
|
};
|
|
911
966
|
const response = await this.typedApi.withdrawHostFees(request);
|
|
912
967
|
// Check if the withdrawal was accepted
|
|
@@ -921,6 +976,7 @@ class FlashnetClient {
|
|
|
921
976
|
*/
|
|
922
977
|
async getHostFees(hostNamespace) {
|
|
923
978
|
await this.ensureInitialized();
|
|
979
|
+
await this.ensurePingOk();
|
|
924
980
|
const request = {
|
|
925
981
|
hostNamespace,
|
|
926
982
|
};
|
|
@@ -938,6 +994,7 @@ class FlashnetClient {
|
|
|
938
994
|
*/
|
|
939
995
|
async getPoolIntegratorFees(poolId) {
|
|
940
996
|
await this.ensureInitialized();
|
|
997
|
+
await this.ensurePingOk();
|
|
941
998
|
return this.typedApi.getPoolIntegratorFees({ poolId });
|
|
942
999
|
}
|
|
943
1000
|
/**
|
|
@@ -945,6 +1002,7 @@ class FlashnetClient {
|
|
|
945
1002
|
*/
|
|
946
1003
|
async withdrawIntegratorFees(params) {
|
|
947
1004
|
await this.ensureInitialized();
|
|
1005
|
+
await this.ensureAmmOperationAllowed("allow_withdraw_fees");
|
|
948
1006
|
const nonce = generateNonce();
|
|
949
1007
|
const intentMessage = generateWithdrawIntegratorFeesIntentMessage({
|
|
950
1008
|
integratorPublicKey: this.publicKey,
|
|
@@ -960,7 +1018,7 @@ class FlashnetClient {
|
|
|
960
1018
|
lpIdentityPublicKey: params.lpIdentityPublicKey,
|
|
961
1019
|
assetBAmount: params.assetBAmount,
|
|
962
1020
|
nonce,
|
|
963
|
-
signature:
|
|
1021
|
+
signature: getHexFromUint8Array(signature),
|
|
964
1022
|
};
|
|
965
1023
|
const response = await this.typedApi.withdrawIntegratorFees(request);
|
|
966
1024
|
// Check if the withdrawal was accepted
|
|
@@ -986,6 +1044,7 @@ class FlashnetClient {
|
|
|
986
1044
|
*/
|
|
987
1045
|
async createEscrow(params) {
|
|
988
1046
|
await this.ensureInitialized();
|
|
1047
|
+
await this.ensurePingOk();
|
|
989
1048
|
const nonce = generateNonce();
|
|
990
1049
|
// The intent message requires a different structure for recipients and conditions
|
|
991
1050
|
const intentRecipients = params.recipients.map((r) => ({
|
|
@@ -1014,7 +1073,7 @@ class FlashnetClient {
|
|
|
1014
1073
|
abandonHost: params.abandonHost,
|
|
1015
1074
|
abandonConditions: params.abandonConditions,
|
|
1016
1075
|
nonce,
|
|
1017
|
-
signature:
|
|
1076
|
+
signature: getHexFromUint8Array(signature),
|
|
1018
1077
|
};
|
|
1019
1078
|
const createResponse = await this.typedApi.createEscrow(request);
|
|
1020
1079
|
const autoFund = params.autoFund !== false;
|
|
@@ -1037,6 +1096,7 @@ class FlashnetClient {
|
|
|
1037
1096
|
*/
|
|
1038
1097
|
async fundEscrow(params) {
|
|
1039
1098
|
await this.ensureInitialized();
|
|
1099
|
+
await this.ensurePingOk();
|
|
1040
1100
|
// 1. Balance check
|
|
1041
1101
|
await this.checkBalance({
|
|
1042
1102
|
balancesToCheck: [
|
|
@@ -1061,6 +1121,7 @@ class FlashnetClient {
|
|
|
1061
1121
|
});
|
|
1062
1122
|
}
|
|
1063
1123
|
async executeFundEscrowIntent(params) {
|
|
1124
|
+
await this.ensurePingOk();
|
|
1064
1125
|
// Generate intent
|
|
1065
1126
|
const nonce = generateNonce();
|
|
1066
1127
|
const intentMessage = generateFundEscrowIntentMessage({
|
|
@@ -1075,7 +1136,7 @@ class FlashnetClient {
|
|
|
1075
1136
|
const request = {
|
|
1076
1137
|
...params,
|
|
1077
1138
|
nonce,
|
|
1078
|
-
signature:
|
|
1139
|
+
signature: getHexFromUint8Array(signature),
|
|
1079
1140
|
};
|
|
1080
1141
|
return this.typedApi.fundEscrow(request);
|
|
1081
1142
|
}
|
|
@@ -1087,6 +1148,7 @@ class FlashnetClient {
|
|
|
1087
1148
|
*/
|
|
1088
1149
|
async claimEscrow(params) {
|
|
1089
1150
|
await this.ensureInitialized();
|
|
1151
|
+
await this.ensurePingOk();
|
|
1090
1152
|
const nonce = generateNonce();
|
|
1091
1153
|
const intentMessage = generateClaimEscrowIntentMessage({
|
|
1092
1154
|
escrowId: params.escrowId,
|
|
@@ -1098,7 +1160,7 @@ class FlashnetClient {
|
|
|
1098
1160
|
const request = {
|
|
1099
1161
|
escrowId: params.escrowId,
|
|
1100
1162
|
nonce,
|
|
1101
|
-
signature:
|
|
1163
|
+
signature: getHexFromUint8Array(signature),
|
|
1102
1164
|
};
|
|
1103
1165
|
return this.typedApi.claimEscrow(request);
|
|
1104
1166
|
}
|
|
@@ -1141,6 +1203,7 @@ class FlashnetClient {
|
|
|
1141
1203
|
*/
|
|
1142
1204
|
async clawback(params) {
|
|
1143
1205
|
await this.ensureInitialized();
|
|
1206
|
+
await this.ensurePingOk();
|
|
1144
1207
|
const nonce = generateNonce();
|
|
1145
1208
|
const intentMessage = generateClawbackIntentMessage({
|
|
1146
1209
|
senderPublicKey: this.publicKey,
|
|
@@ -1155,7 +1218,7 @@ class FlashnetClient {
|
|
|
1155
1218
|
sparkTransferId: params.sparkTransferId,
|
|
1156
1219
|
lpIdentityPublicKey: params.lpIdentityPublicKey,
|
|
1157
1220
|
nonce,
|
|
1158
|
-
signature:
|
|
1221
|
+
signature: getHexFromUint8Array(signature),
|
|
1159
1222
|
};
|
|
1160
1223
|
const response = await this.typedApi.clawback(request);
|
|
1161
1224
|
if (!response.accepted) {
|
|
@@ -1250,6 +1313,13 @@ class FlashnetClient {
|
|
|
1250
1313
|
* Helper method to add initial liquidity after pool creation
|
|
1251
1314
|
*/
|
|
1252
1315
|
async addInitialLiquidity(poolId, assetAAddress, assetBAddress, assetAAmount, assetBAmount, assetAMinAmountIn, assetBMinAmountIn) {
|
|
1316
|
+
// Enforce gating and min-amount policy for initial liquidity
|
|
1317
|
+
await this.ensureAmmOperationAllowed("allow_add_liquidity");
|
|
1318
|
+
await this.assertAddLiquidityMeetsMinAmounts({
|
|
1319
|
+
poolId,
|
|
1320
|
+
assetAAmount,
|
|
1321
|
+
assetBAmount,
|
|
1322
|
+
});
|
|
1253
1323
|
const lpSparkAddress = encodeSparkAddressNew({
|
|
1254
1324
|
identityPublicKey: poolId,
|
|
1255
1325
|
network: this.sparkNetwork,
|
|
@@ -1291,7 +1361,7 @@ class FlashnetClient {
|
|
|
1291
1361
|
assetAMinAmountIn: assetAMinAmountIn.toString(),
|
|
1292
1362
|
assetBMinAmountIn: assetBMinAmountIn.toString(),
|
|
1293
1363
|
nonce,
|
|
1294
|
-
signature:
|
|
1364
|
+
signature: getHexFromUint8Array(signature),
|
|
1295
1365
|
};
|
|
1296
1366
|
const response = await this.typedApi.addLiquidity(request);
|
|
1297
1367
|
// Check if the initial liquidity addition was accepted
|
|
@@ -1306,6 +1376,186 @@ class FlashnetClient {
|
|
|
1306
1376
|
async cleanup() {
|
|
1307
1377
|
await this._wallet.cleanupConnections();
|
|
1308
1378
|
}
|
|
1379
|
+
// ===== Config and Policy Enforcement Helpers =====
|
|
1380
|
+
async ensureAmmOperationAllowed(requiredFeature) {
|
|
1381
|
+
await this.ensurePingOk();
|
|
1382
|
+
const featureMap = await this.getFeatureStatusMap();
|
|
1383
|
+
if (featureMap.get("master_kill_switch")) {
|
|
1384
|
+
throw new Error("Service is temporarily disabled by master kill switch");
|
|
1385
|
+
}
|
|
1386
|
+
if (!featureMap.get(requiredFeature)) {
|
|
1387
|
+
throw new Error(`Operation not allowed: feature '${requiredFeature}' is disabled`);
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
async ensurePingOk() {
|
|
1391
|
+
const now = Date.now();
|
|
1392
|
+
if (this.pingCache && this.pingCache.expiryMs > now) {
|
|
1393
|
+
if (!this.pingCache.ok) {
|
|
1394
|
+
throw new Error("Settlement service unavailable. Only read (GET) operations are allowed right now.");
|
|
1395
|
+
}
|
|
1396
|
+
return;
|
|
1397
|
+
}
|
|
1398
|
+
const ping = await this.typedApi.ping();
|
|
1399
|
+
const ok = !!ping &&
|
|
1400
|
+
typeof ping.status === "string" &&
|
|
1401
|
+
ping.status.toLowerCase() === "ok";
|
|
1402
|
+
this.pingCache = { ok, expiryMs: now + FlashnetClient.PING_TTL_MS };
|
|
1403
|
+
if (!ok) {
|
|
1404
|
+
throw new Error("Settlement service unavailable. Only read (GET) operations are allowed right now.");
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
async getFeatureStatusMap() {
|
|
1408
|
+
const now = Date.now();
|
|
1409
|
+
if (this.featureStatusCache && this.featureStatusCache.expiryMs > now) {
|
|
1410
|
+
const map = new Map();
|
|
1411
|
+
for (const item of this.featureStatusCache.data) {
|
|
1412
|
+
map.set(item.feature_name, Boolean(item.enabled));
|
|
1413
|
+
}
|
|
1414
|
+
return map;
|
|
1415
|
+
}
|
|
1416
|
+
const data = await this.typedApi.getFeatureStatus();
|
|
1417
|
+
this.featureStatusCache = {
|
|
1418
|
+
data,
|
|
1419
|
+
expiryMs: now + FlashnetClient.FEATURE_STATUS_TTL_MS,
|
|
1420
|
+
};
|
|
1421
|
+
const map = new Map();
|
|
1422
|
+
for (const item of data) {
|
|
1423
|
+
map.set(item.feature_name, Boolean(item.enabled));
|
|
1424
|
+
}
|
|
1425
|
+
return map;
|
|
1426
|
+
}
|
|
1427
|
+
async getEnabledMinAmountsMap() {
|
|
1428
|
+
const now = Date.now();
|
|
1429
|
+
if (this.minAmountsCache && this.minAmountsCache.expiryMs > now) {
|
|
1430
|
+
return this.minAmountsCache.map;
|
|
1431
|
+
}
|
|
1432
|
+
const config = await this.typedApi.getMinAmounts();
|
|
1433
|
+
const map = new Map();
|
|
1434
|
+
for (const item of config) {
|
|
1435
|
+
if (item.enabled) {
|
|
1436
|
+
const key = item.asset_identifier.toLowerCase();
|
|
1437
|
+
const value = BigInt(String(item.min_amount));
|
|
1438
|
+
map.set(key, value);
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
this.minAmountsCache = {
|
|
1442
|
+
map,
|
|
1443
|
+
expiryMs: now + FlashnetClient.MIN_AMOUNTS_TTL_MS,
|
|
1444
|
+
};
|
|
1445
|
+
return map;
|
|
1446
|
+
}
|
|
1447
|
+
getHexAddress(addr) {
|
|
1448
|
+
return this.toHexTokenIdentifier(addr).toLowerCase();
|
|
1449
|
+
}
|
|
1450
|
+
async assertSwapMeetsMinAmounts(params) {
|
|
1451
|
+
const minMap = await this.getEnabledMinAmountsMap();
|
|
1452
|
+
if (minMap.size === 0) {
|
|
1453
|
+
return;
|
|
1454
|
+
}
|
|
1455
|
+
const inHex = this.getHexAddress(params.assetInAddress);
|
|
1456
|
+
const outHex = this.getHexAddress(params.assetOutAddress);
|
|
1457
|
+
const minIn = minMap.get(inHex);
|
|
1458
|
+
const minOut = minMap.get(outHex);
|
|
1459
|
+
const amountIn = BigInt(String(params.amountIn));
|
|
1460
|
+
const minAmountOut = BigInt(String(params.minAmountOut));
|
|
1461
|
+
if (minIn && minOut) {
|
|
1462
|
+
if (amountIn < minIn) {
|
|
1463
|
+
throw new Error(`Minimum amount not met for input asset. Required \
|
|
1464
|
+
${minIn.toString()}, provided ${amountIn.toString()}`);
|
|
1465
|
+
}
|
|
1466
|
+
return;
|
|
1467
|
+
}
|
|
1468
|
+
if (minIn) {
|
|
1469
|
+
if (amountIn < minIn) {
|
|
1470
|
+
throw new Error(`Minimum amount not met for input asset. Required \
|
|
1471
|
+
${minIn.toString()}, provided ${amountIn.toString()}`);
|
|
1472
|
+
}
|
|
1473
|
+
return;
|
|
1474
|
+
}
|
|
1475
|
+
if (minOut) {
|
|
1476
|
+
const relaxed = minOut / 2n; // 50% relaxation for slippage
|
|
1477
|
+
if (minAmountOut < relaxed) {
|
|
1478
|
+
throw new Error(`Minimum amount not met for output asset. Required at least \
|
|
1479
|
+
${relaxed.toString()} (50% relaxed), provided minAmountOut ${minAmountOut.toString()}`);
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
async assertAddLiquidityMeetsMinAmounts(params) {
|
|
1484
|
+
const minMap = await this.getEnabledMinAmountsMap();
|
|
1485
|
+
if (minMap.size === 0) {
|
|
1486
|
+
return;
|
|
1487
|
+
}
|
|
1488
|
+
const pool = await this.getPool(params.poolId);
|
|
1489
|
+
const aHex = pool.assetAAddress.toLowerCase();
|
|
1490
|
+
const bHex = pool.assetBAddress.toLowerCase();
|
|
1491
|
+
const aMin = minMap.get(aHex);
|
|
1492
|
+
const bMin = minMap.get(bHex);
|
|
1493
|
+
if (aMin) {
|
|
1494
|
+
const aAmt = BigInt(String(params.assetAAmount));
|
|
1495
|
+
if (aAmt < aMin) {
|
|
1496
|
+
throw new Error(`Minimum amount not met for Asset A. Required ${aMin.toString()}, provided ${aAmt.toString()}`);
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
if (bMin) {
|
|
1500
|
+
const bAmt = BigInt(String(params.assetBAmount));
|
|
1501
|
+
if (bAmt < bMin) {
|
|
1502
|
+
throw new Error(`Minimum amount not met for Asset B. Required ${bMin.toString()}, provided ${bAmt.toString()}`);
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
async assertRemoveLiquidityMeetsMinAmounts(params) {
|
|
1507
|
+
const minMap = await this.getEnabledMinAmountsMap();
|
|
1508
|
+
if (minMap.size === 0) {
|
|
1509
|
+
return;
|
|
1510
|
+
}
|
|
1511
|
+
const simulation = await this.simulateRemoveLiquidity({
|
|
1512
|
+
poolId: params.poolId,
|
|
1513
|
+
providerPublicKey: this.publicKey,
|
|
1514
|
+
lpTokensToRemove: String(params.lpTokensToRemove),
|
|
1515
|
+
});
|
|
1516
|
+
const pool = await this.getPool(params.poolId);
|
|
1517
|
+
const aHex = pool.assetAAddress.toLowerCase();
|
|
1518
|
+
const bHex = pool.assetBAddress.toLowerCase();
|
|
1519
|
+
const aMin = minMap.get(aHex);
|
|
1520
|
+
const bMin = minMap.get(bHex);
|
|
1521
|
+
if (aMin) {
|
|
1522
|
+
const predictedAOut = BigInt(String(simulation.assetAAmount));
|
|
1523
|
+
const relaxedA = aMin / 2n; // apply 50% relaxation for outputs
|
|
1524
|
+
if (predictedAOut < relaxedA) {
|
|
1525
|
+
throw new Error(`Minimum amount not met for Asset A on withdrawal. Required at least ${relaxedA.toString()} (50% relaxed), predicted ${predictedAOut.toString()}`);
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
if (bMin) {
|
|
1529
|
+
const predictedBOut = BigInt(String(simulation.assetBAmount));
|
|
1530
|
+
const relaxedB = bMin / 2n;
|
|
1531
|
+
if (predictedBOut < relaxedB) {
|
|
1532
|
+
throw new Error(`Minimum amount not met for Asset B on withdrawal. Required at least ${relaxedB.toString()} (50% relaxed), predicted ${predictedBOut.toString()}`);
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
async assertAllowedAssetBForPoolCreation(assetBHex) {
|
|
1537
|
+
const now = Date.now();
|
|
1538
|
+
let allowed;
|
|
1539
|
+
if (this.allowedAssetsCache && this.allowedAssetsCache.expiryMs > now) {
|
|
1540
|
+
allowed = this.allowedAssetsCache.data;
|
|
1541
|
+
}
|
|
1542
|
+
else {
|
|
1543
|
+
allowed = await this.typedApi.getAllowedAssets();
|
|
1544
|
+
this.allowedAssetsCache = {
|
|
1545
|
+
data: allowed,
|
|
1546
|
+
expiryMs: now + FlashnetClient.ALLOWED_ASSETS_TTL_MS,
|
|
1547
|
+
};
|
|
1548
|
+
}
|
|
1549
|
+
if (!allowed || allowed.length === 0) {
|
|
1550
|
+
// Wildcard allowance
|
|
1551
|
+
return;
|
|
1552
|
+
}
|
|
1553
|
+
const isAllowed = allowed.some((it) => it.enabled &&
|
|
1554
|
+
it.asset_identifier.toLowerCase() === assetBHex.toLowerCase());
|
|
1555
|
+
if (!isAllowed) {
|
|
1556
|
+
throw new Error(`Asset B is not allowed for pool creation: ${assetBHex}`);
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1309
1559
|
}
|
|
1310
1560
|
|
|
1311
1561
|
export { FlashnetClient };
|