@flashnet/sdk 0.3.24 → 0.3.25
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 +329 -65
- 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 +1 -1
- package/dist/cjs/src/utils/tokenAddress.d.ts.map +1 -1
- package/dist/cjs/src/utils/tokenAddress.js +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 +329 -65
- 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 +1 -1
- package/dist/esm/src/utils/tokenAddress.d.ts.map +1 -1
- package/dist/esm/src/utils/tokenAddress.js +1 -1
- package/package.json +1 -1
|
@@ -6,6 +6,7 @@ var index$1 = require('../config/index.js');
|
|
|
6
6
|
var index = require('../types/index.js');
|
|
7
7
|
var index$2 = require('../utils/index.js');
|
|
8
8
|
var auth = require('../utils/auth.js');
|
|
9
|
+
var hex = require('../utils/hex.js');
|
|
9
10
|
var intents = require('../utils/intents.js');
|
|
10
11
|
var signer = require('../utils/signer.js');
|
|
11
12
|
var sparkAddress = require('../utils/spark-address.js');
|
|
@@ -31,6 +32,16 @@ class FlashnetClient {
|
|
|
31
32
|
publicKey = "";
|
|
32
33
|
sparkAddress = "";
|
|
33
34
|
isAuthenticated = false;
|
|
35
|
+
// Ephemeral caches for config endpoints and ping
|
|
36
|
+
featureStatusCache;
|
|
37
|
+
minAmountsCache;
|
|
38
|
+
allowedAssetsCache;
|
|
39
|
+
pingCache;
|
|
40
|
+
// TTLs (milliseconds)
|
|
41
|
+
static FEATURE_STATUS_TTL_MS = 5000; // 5s
|
|
42
|
+
static MIN_AMOUNTS_TTL_MS = 5000; // 5s
|
|
43
|
+
static ALLOWED_ASSETS_TTL_MS = 60000; // 60s
|
|
44
|
+
static PING_TTL_MS = 2000; // 2s
|
|
34
45
|
/**
|
|
35
46
|
* Get the underlying wallet instance for direct wallet operations
|
|
36
47
|
*/
|
|
@@ -227,7 +238,7 @@ class FlashnetClient {
|
|
|
227
238
|
for (const [tokenPubkey, tokenData] of balance.tokenBalances.entries()) {
|
|
228
239
|
const info = tokenData.tokenMetadata;
|
|
229
240
|
// Convert raw token identifier to hex and human-readable forms
|
|
230
|
-
const tokenIdentifierHex =
|
|
241
|
+
const tokenIdentifierHex = hex.getHexFromUint8Array(info.rawTokenIdentifier);
|
|
231
242
|
const tokenAddress$1 = tokenAddress.encodeSparkHumanReadableTokenIdentifier(info.rawTokenIdentifier, this.sparkNetwork);
|
|
232
243
|
tokenBalances.set(tokenPubkey, {
|
|
233
244
|
balance: BigInt(tokenData.balance),
|
|
@@ -325,6 +336,8 @@ class FlashnetClient {
|
|
|
325
336
|
*/
|
|
326
337
|
async createConstantProductPool(params) {
|
|
327
338
|
await this.ensureInitialized();
|
|
339
|
+
await this.ensureAmmOperationAllowed("allow_pool_creation");
|
|
340
|
+
await this.assertAllowedAssetBForPoolCreation(this.toHexTokenIdentifier(params.assetBAddress));
|
|
328
341
|
// Check if we need to add initial liquidity
|
|
329
342
|
if (params.initialLiquidity) {
|
|
330
343
|
await this.checkBalance({
|
|
@@ -364,7 +377,7 @@ class FlashnetClient {
|
|
|
364
377
|
totalHostFeeRateBps: params.totalHostFeeRateBps.toString(),
|
|
365
378
|
hostNamespace: params.hostNamespace || "",
|
|
366
379
|
nonce,
|
|
367
|
-
signature:
|
|
380
|
+
signature: hex.getHexFromUint8Array(signature),
|
|
368
381
|
};
|
|
369
382
|
const response = await this.typedApi.createConstantProductPool(request);
|
|
370
383
|
// Add initial liquidity if specified
|
|
@@ -373,6 +386,31 @@ class FlashnetClient {
|
|
|
373
386
|
}
|
|
374
387
|
return response;
|
|
375
388
|
}
|
|
389
|
+
// Validate and normalize inputs to bigint
|
|
390
|
+
static parsePositiveIntegerToBigInt(value, name) {
|
|
391
|
+
if (typeof value === "bigint") {
|
|
392
|
+
if (value <= 0n) {
|
|
393
|
+
throw new Error(`${name} must be positive integer`);
|
|
394
|
+
}
|
|
395
|
+
return value;
|
|
396
|
+
}
|
|
397
|
+
if (typeof value === "number") {
|
|
398
|
+
if (!Number.isFinite(value) || !Number.isInteger(value) || value <= 0) {
|
|
399
|
+
throw new Error(`${name} must be positive integer`);
|
|
400
|
+
}
|
|
401
|
+
return BigInt(value);
|
|
402
|
+
}
|
|
403
|
+
try {
|
|
404
|
+
const v = BigInt(value);
|
|
405
|
+
if (v <= 0n) {
|
|
406
|
+
throw new Error(`${name} must be positive integer`);
|
|
407
|
+
}
|
|
408
|
+
return v;
|
|
409
|
+
}
|
|
410
|
+
catch {
|
|
411
|
+
throw new Error(`${name} must be positive integer`);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
376
414
|
/**
|
|
377
415
|
* Calculates virtual reserves for a bonding curve AMM.
|
|
378
416
|
*
|
|
@@ -387,38 +425,13 @@ class FlashnetClient {
|
|
|
387
425
|
* @returns An object containing `virtualReserveA`, `virtualReserveB`, and `threshold`.
|
|
388
426
|
*/
|
|
389
427
|
static calculateVirtualReserves(params) {
|
|
390
|
-
// Validate and normalize inputs to bigint
|
|
391
|
-
const parsePositiveIntegerToBigInt = (value, name) => {
|
|
392
|
-
if (typeof value === "bigint") {
|
|
393
|
-
if (value <= 0n) {
|
|
394
|
-
throw new Error(`${name} must be positive integer`);
|
|
395
|
-
}
|
|
396
|
-
return value;
|
|
397
|
-
}
|
|
398
|
-
if (typeof value === "number") {
|
|
399
|
-
if (!Number.isFinite(value) || !Number.isInteger(value) || value <= 0) {
|
|
400
|
-
throw new Error(`${name} must be positive integer`);
|
|
401
|
-
}
|
|
402
|
-
return BigInt(value);
|
|
403
|
-
}
|
|
404
|
-
try {
|
|
405
|
-
const v = BigInt(value);
|
|
406
|
-
if (v <= 0n) {
|
|
407
|
-
throw new Error(`${name} must be positive integer`);
|
|
408
|
-
}
|
|
409
|
-
return v;
|
|
410
|
-
}
|
|
411
|
-
catch {
|
|
412
|
-
throw new Error(`${name} must be positive integer`);
|
|
413
|
-
}
|
|
414
|
-
};
|
|
415
428
|
if (!Number.isFinite(params.graduationThresholdPct) ||
|
|
416
429
|
!Number.isInteger(params.graduationThresholdPct) ||
|
|
417
430
|
params.graduationThresholdPct <= 0) {
|
|
418
431
|
throw new Error("Graduation threshold percentage must be a positive integer");
|
|
419
432
|
}
|
|
420
|
-
const supply = parsePositiveIntegerToBigInt(params.initialTokenSupply, "Initial token supply");
|
|
421
|
-
const targetB = parsePositiveIntegerToBigInt(params.targetRaise, "Target raise");
|
|
433
|
+
const supply = FlashnetClient.parsePositiveIntegerToBigInt(params.initialTokenSupply, "Initial token supply");
|
|
434
|
+
const targetB = FlashnetClient.parsePositiveIntegerToBigInt(params.targetRaise, "Target raise");
|
|
422
435
|
const graduationThresholdPct = BigInt(params.graduationThresholdPct);
|
|
423
436
|
// Check feasibility: f - g*(1-f) > 0 where f is graduationThresholdPct/100 and g is 1
|
|
424
437
|
const MIN_GRADUATION_THRESHOLD_PCT = 50n;
|
|
@@ -433,16 +446,22 @@ class FlashnetClient {
|
|
|
433
446
|
// (2n * graduationThresholdPct - 100n) / 100n
|
|
434
447
|
// 1/denom is 100n / (2n * graduationThresholdPct - 100n)
|
|
435
448
|
// Calculate virtual reserves and round down to integers
|
|
436
|
-
//
|
|
437
|
-
const
|
|
438
|
-
const
|
|
439
|
-
const
|
|
440
|
-
const
|
|
441
|
-
//
|
|
442
|
-
|
|
443
|
-
const
|
|
444
|
-
|
|
445
|
-
const
|
|
449
|
+
// totalA is (supply * f * f) / denom
|
|
450
|
+
const totalANumerator = BigInt(supply) * graduationThresholdPct * graduationThresholdPct;
|
|
451
|
+
const totalADenominator = 200n * graduationThresholdPct - 10000n;
|
|
452
|
+
const totalARemainder = totalANumerator % totalADenominator;
|
|
453
|
+
const totalA = (totalANumerator - totalARemainder) / totalADenominator;
|
|
454
|
+
// Since there is an initial supply of tokens, we subtract that amount
|
|
455
|
+
// to get the remaining "virtual" amount for pricing
|
|
456
|
+
const virtualA = totalA - supply;
|
|
457
|
+
// totalB is (targetB * g * (1 - f)) / denom
|
|
458
|
+
const totalBNumerator = BigInt(targetB) * (100n - graduationThresholdPct);
|
|
459
|
+
const totalBDenominator = 2n * graduationThresholdPct - 100n;
|
|
460
|
+
const totalBRemainder = totalBNumerator % totalBDenominator;
|
|
461
|
+
const totalB = (totalBNumerator - totalBRemainder) / totalBDenominator;
|
|
462
|
+
// Bonding curve starts with no deposited amount of asset B,
|
|
463
|
+
// so virtualB is all of totalB
|
|
464
|
+
const virtualB = totalB;
|
|
446
465
|
// Calculate threshold as amount of asset A (not percentage)
|
|
447
466
|
const threshold = (BigInt(supply) * graduationThresholdPct) / 100n;
|
|
448
467
|
return {
|
|
@@ -459,18 +478,20 @@ class FlashnetClient {
|
|
|
459
478
|
*/
|
|
460
479
|
async createSingleSidedPool(params) {
|
|
461
480
|
await this.ensureInitialized();
|
|
481
|
+
await this.ensureAmmOperationAllowed("allow_pool_creation");
|
|
482
|
+
await this.assertAllowedAssetBForPoolCreation(this.toHexTokenIdentifier(params.assetBAddress));
|
|
462
483
|
if (!params.hostNamespace && params.totalHostFeeRateBps < 10) {
|
|
463
484
|
throw new Error(`Host fee must be greater than 10 bps when no host namespace is provided`);
|
|
464
485
|
}
|
|
465
|
-
//
|
|
466
|
-
const
|
|
467
|
-
const
|
|
468
|
-
const
|
|
486
|
+
// Validate reserves are valid positive integers before any operations
|
|
487
|
+
const assetAInitialReserve = FlashnetClient.parsePositiveIntegerToBigInt(params.assetAInitialReserve, "Asset A Initial Reserve").toString();
|
|
488
|
+
const virtualReserveA = FlashnetClient.parsePositiveIntegerToBigInt(params.virtualReserveA, "Virtual Reserve A").toString();
|
|
489
|
+
const virtualReserveB = FlashnetClient.parsePositiveIntegerToBigInt(params.virtualReserveB, "Virtual Reserve B").toString();
|
|
469
490
|
await this.checkBalance({
|
|
470
491
|
balancesToCheck: [
|
|
471
492
|
{
|
|
472
493
|
assetAddress: params.assetAAddress,
|
|
473
|
-
amount:
|
|
494
|
+
amount: assetAInitialReserve,
|
|
474
495
|
},
|
|
475
496
|
],
|
|
476
497
|
errorPrefix: "Insufficient balance for pool creation: ",
|
|
@@ -482,9 +503,9 @@ class FlashnetClient {
|
|
|
482
503
|
poolOwnerPublicKey,
|
|
483
504
|
assetAAddress: this.toHexTokenIdentifier(params.assetAAddress),
|
|
484
505
|
assetBAddress: this.toHexTokenIdentifier(params.assetBAddress),
|
|
485
|
-
assetAInitialReserve
|
|
486
|
-
virtualReserveA
|
|
487
|
-
virtualReserveB
|
|
506
|
+
assetAInitialReserve,
|
|
507
|
+
virtualReserveA,
|
|
508
|
+
virtualReserveB,
|
|
488
509
|
threshold: params.threshold.toString(),
|
|
489
510
|
lpFeeRateBps: params.lpFeeRateBps.toString(),
|
|
490
511
|
totalHostFeeRateBps: params.totalHostFeeRateBps.toString(),
|
|
@@ -497,15 +518,15 @@ class FlashnetClient {
|
|
|
497
518
|
poolOwnerPublicKey,
|
|
498
519
|
assetAAddress: this.toHexTokenIdentifier(params.assetAAddress),
|
|
499
520
|
assetBAddress: this.toHexTokenIdentifier(params.assetBAddress),
|
|
500
|
-
assetAInitialReserve
|
|
501
|
-
virtualReserveA
|
|
502
|
-
virtualReserveB
|
|
521
|
+
assetAInitialReserve,
|
|
522
|
+
virtualReserveA,
|
|
523
|
+
virtualReserveB,
|
|
503
524
|
threshold: params.threshold.toString(),
|
|
504
525
|
lpFeeRateBps: params.lpFeeRateBps.toString(),
|
|
505
526
|
totalHostFeeRateBps: params.totalHostFeeRateBps.toString(),
|
|
506
527
|
hostNamespace: params.hostNamespace,
|
|
507
528
|
nonce,
|
|
508
|
-
signature:
|
|
529
|
+
signature: hex.getHexFromUint8Array(signature),
|
|
509
530
|
};
|
|
510
531
|
const createResponse = await this.typedApi.createSingleSidedPool(request);
|
|
511
532
|
if (params.disableInitialDeposit) {
|
|
@@ -519,7 +540,7 @@ class FlashnetClient {
|
|
|
519
540
|
const assetATransferId = await this.transferAsset({
|
|
520
541
|
receiverSparkAddress: lpSparkAddress,
|
|
521
542
|
assetAddress: params.assetAAddress,
|
|
522
|
-
amount:
|
|
543
|
+
amount: assetAInitialReserve,
|
|
523
544
|
});
|
|
524
545
|
const confirmResponse = await this.confirmInitialDeposit(createResponse.poolId, assetATransferId, poolOwnerPublicKey);
|
|
525
546
|
if (!confirmResponse.confirmed) {
|
|
@@ -548,7 +569,7 @@ class FlashnetClient {
|
|
|
548
569
|
poolId,
|
|
549
570
|
assetASparkTransferId,
|
|
550
571
|
nonce,
|
|
551
|
-
signature:
|
|
572
|
+
signature: hex.getHexFromUint8Array(signature),
|
|
552
573
|
poolOwnerPublicKey: poolOwnerPublicKey ?? this.publicKey,
|
|
553
574
|
};
|
|
554
575
|
return this.typedApi.confirmInitialDeposit(request);
|
|
@@ -559,6 +580,7 @@ class FlashnetClient {
|
|
|
559
580
|
*/
|
|
560
581
|
async simulateSwap(params) {
|
|
561
582
|
await this.ensureInitialized();
|
|
583
|
+
await this.ensurePingOk();
|
|
562
584
|
return this.typedApi.simulateSwap(params);
|
|
563
585
|
}
|
|
564
586
|
/**
|
|
@@ -566,6 +588,14 @@ class FlashnetClient {
|
|
|
566
588
|
*/
|
|
567
589
|
async executeSwap(params) {
|
|
568
590
|
await this.ensureInitialized();
|
|
591
|
+
// Gate by feature flags and ping, and enforce min-amount policy before transfers
|
|
592
|
+
await this.ensureAmmOperationAllowed("allow_swaps");
|
|
593
|
+
await this.assertSwapMeetsMinAmounts({
|
|
594
|
+
assetInAddress: params.assetInAddress,
|
|
595
|
+
assetOutAddress: params.assetOutAddress,
|
|
596
|
+
amountIn: params.amountIn,
|
|
597
|
+
minAmountOut: params.minAmountOut,
|
|
598
|
+
});
|
|
569
599
|
// Transfer assets to pool using new address encoding
|
|
570
600
|
const lpSparkAddress = sparkAddress.encodeSparkAddressNew({
|
|
571
601
|
identityPublicKey: params.poolId,
|
|
@@ -584,6 +614,14 @@ class FlashnetClient {
|
|
|
584
614
|
}
|
|
585
615
|
async executeSwapIntent(params) {
|
|
586
616
|
await this.ensureInitialized();
|
|
617
|
+
// Also enforce gating and min amounts for direct intent usage
|
|
618
|
+
await this.ensureAmmOperationAllowed("allow_swaps");
|
|
619
|
+
await this.assertSwapMeetsMinAmounts({
|
|
620
|
+
assetInAddress: params.assetInAddress,
|
|
621
|
+
assetOutAddress: params.assetOutAddress,
|
|
622
|
+
amountIn: params.amountIn,
|
|
623
|
+
minAmountOut: params.minAmountOut,
|
|
624
|
+
});
|
|
587
625
|
// Generate swap intent
|
|
588
626
|
const nonce = index$2.generateNonce();
|
|
589
627
|
const intentMessage = intents.generatePoolSwapIntentMessage({
|
|
@@ -613,7 +651,7 @@ class FlashnetClient {
|
|
|
613
651
|
totalIntegratorFeeRateBps: params.integratorFeeRateBps?.toString() || "0",
|
|
614
652
|
integratorPublicKey: params.integratorPublicKey || "",
|
|
615
653
|
nonce,
|
|
616
|
-
signature:
|
|
654
|
+
signature: hex.getHexFromUint8Array(signature),
|
|
617
655
|
};
|
|
618
656
|
const response = await this.typedApi.executeSwap(request);
|
|
619
657
|
// Check if the swap was accepted
|
|
@@ -634,6 +672,7 @@ class FlashnetClient {
|
|
|
634
672
|
throw new Error("Route swap cannot have more than 4 hops");
|
|
635
673
|
}
|
|
636
674
|
await this.ensureInitialized();
|
|
675
|
+
await this.ensurePingOk();
|
|
637
676
|
return this.typedApi.simulateRouteSwap(params);
|
|
638
677
|
}
|
|
639
678
|
/**
|
|
@@ -641,6 +680,18 @@ class FlashnetClient {
|
|
|
641
680
|
*/
|
|
642
681
|
async executeRouteSwap(params) {
|
|
643
682
|
await this.ensureInitialized();
|
|
683
|
+
await this.ensureAmmOperationAllowed("allow_route_swaps");
|
|
684
|
+
// Validate min-amount policy for route: check initial input and final output asset
|
|
685
|
+
const finalOutputAsset = params.hops[params.hops.length - 1]?.assetOutAddress;
|
|
686
|
+
if (!finalOutputAsset) {
|
|
687
|
+
throw new Error("Route swap requires at least one hop with output asset");
|
|
688
|
+
}
|
|
689
|
+
await this.assertSwapMeetsMinAmounts({
|
|
690
|
+
assetInAddress: params.initialAssetAddress,
|
|
691
|
+
assetOutAddress: finalOutputAsset,
|
|
692
|
+
amountIn: params.inputAmount,
|
|
693
|
+
minAmountOut: params.minAmountOut,
|
|
694
|
+
});
|
|
644
695
|
// Validate hops array
|
|
645
696
|
if (params.hops.length > 4) {
|
|
646
697
|
throw new Error("Route swap cannot have more than 4 hops");
|
|
@@ -710,7 +761,7 @@ class FlashnetClient {
|
|
|
710
761
|
maxRouteSlippageBps: params.maxRouteSlippageBps.toString(),
|
|
711
762
|
minAmountOut: params.minAmountOut,
|
|
712
763
|
nonce,
|
|
713
|
-
signature:
|
|
764
|
+
signature: hex.getHexFromUint8Array(signature),
|
|
714
765
|
integratorFeeRateBps: params.integratorFeeRateBps?.toString() || "0",
|
|
715
766
|
integratorPublicKey: params.integratorPublicKey || "",
|
|
716
767
|
};
|
|
@@ -731,6 +782,7 @@ class FlashnetClient {
|
|
|
731
782
|
*/
|
|
732
783
|
async simulateAddLiquidity(params) {
|
|
733
784
|
await this.ensureInitialized();
|
|
785
|
+
await this.ensurePingOk();
|
|
734
786
|
return this.typedApi.simulateAddLiquidity(params);
|
|
735
787
|
}
|
|
736
788
|
/**
|
|
@@ -738,8 +790,15 @@ class FlashnetClient {
|
|
|
738
790
|
*/
|
|
739
791
|
async addLiquidity(params) {
|
|
740
792
|
await this.ensureInitialized();
|
|
793
|
+
await this.ensureAmmOperationAllowed("allow_add_liquidity");
|
|
741
794
|
// Get pool details to know which assets we're dealing with
|
|
742
795
|
const pool = await this.getPool(params.poolId);
|
|
796
|
+
// Enforce min-amount policy for inputs based on pool assets
|
|
797
|
+
await this.assertAddLiquidityMeetsMinAmounts({
|
|
798
|
+
poolId: params.poolId,
|
|
799
|
+
assetAAmount: params.assetAAmount,
|
|
800
|
+
assetBAmount: params.assetBAmount,
|
|
801
|
+
});
|
|
743
802
|
// Transfer assets to pool using new address encoding
|
|
744
803
|
const lpSparkAddress = sparkAddress.encodeSparkAddressNew({
|
|
745
804
|
identityPublicKey: params.poolId,
|
|
@@ -783,7 +842,7 @@ class FlashnetClient {
|
|
|
783
842
|
assetAMinAmountIn: params.assetAMinAmountIn.toString(),
|
|
784
843
|
assetBMinAmountIn: params.assetBMinAmountIn.toString(),
|
|
785
844
|
nonce,
|
|
786
|
-
signature:
|
|
845
|
+
signature: hex.getHexFromUint8Array(signature),
|
|
787
846
|
};
|
|
788
847
|
const response = await this.typedApi.addLiquidity(request);
|
|
789
848
|
// Check if the liquidity addition was accepted
|
|
@@ -801,6 +860,7 @@ class FlashnetClient {
|
|
|
801
860
|
*/
|
|
802
861
|
async simulateRemoveLiquidity(params) {
|
|
803
862
|
await this.ensureInitialized();
|
|
863
|
+
await this.ensurePingOk();
|
|
804
864
|
return this.typedApi.simulateRemoveLiquidity(params);
|
|
805
865
|
}
|
|
806
866
|
/**
|
|
@@ -808,6 +868,7 @@ class FlashnetClient {
|
|
|
808
868
|
*/
|
|
809
869
|
async removeLiquidity(params) {
|
|
810
870
|
await this.ensureInitialized();
|
|
871
|
+
await this.ensureAmmOperationAllowed("allow_withdraw_liquidity");
|
|
811
872
|
// Check LP token balance
|
|
812
873
|
const position = await this.getLpPosition(params.poolId);
|
|
813
874
|
const lpTokensOwned = position.lpTokensOwned;
|
|
@@ -815,6 +876,11 @@ class FlashnetClient {
|
|
|
815
876
|
if (index$2.compareDecimalStrings(lpTokensOwned, tokensToRemove) < 0) {
|
|
816
877
|
throw new Error(`Insufficient LP tokens. Owned: ${lpTokensOwned}, Requested: ${tokensToRemove}`);
|
|
817
878
|
}
|
|
879
|
+
// Pre-simulate and enforce min-amount policy for outputs
|
|
880
|
+
await this.assertRemoveLiquidityMeetsMinAmounts({
|
|
881
|
+
poolId: params.poolId,
|
|
882
|
+
lpTokensToRemove: params.lpTokensToRemove,
|
|
883
|
+
});
|
|
818
884
|
// Generate remove liquidity intent
|
|
819
885
|
const nonce = index$2.generateNonce();
|
|
820
886
|
const intentMessage = intents.generateRemoveLiquidityIntentMessage({
|
|
@@ -831,7 +897,7 @@ class FlashnetClient {
|
|
|
831
897
|
poolId: params.poolId,
|
|
832
898
|
lpTokensToRemove: params.lpTokensToRemove,
|
|
833
899
|
nonce,
|
|
834
|
-
signature:
|
|
900
|
+
signature: hex.getHexFromUint8Array(signature),
|
|
835
901
|
};
|
|
836
902
|
const response = await this.typedApi.removeLiquidity(request);
|
|
837
903
|
// Check if the liquidity removal was accepted
|
|
@@ -847,6 +913,7 @@ class FlashnetClient {
|
|
|
847
913
|
*/
|
|
848
914
|
async registerHost(params) {
|
|
849
915
|
await this.ensureInitialized();
|
|
916
|
+
await this.ensurePingOk();
|
|
850
917
|
const feeRecipient = params.feeRecipientPublicKey || this.publicKey;
|
|
851
918
|
const nonce = index$2.generateNonce();
|
|
852
919
|
// Generate intent
|
|
@@ -864,7 +931,7 @@ class FlashnetClient {
|
|
|
864
931
|
minFeeBps: params.minFeeBps,
|
|
865
932
|
feeRecipientPublicKey: feeRecipient,
|
|
866
933
|
nonce,
|
|
867
|
-
signature:
|
|
934
|
+
signature: hex.getHexFromUint8Array(signature),
|
|
868
935
|
};
|
|
869
936
|
return this.typedApi.registerHost(request);
|
|
870
937
|
}
|
|
@@ -880,6 +947,7 @@ class FlashnetClient {
|
|
|
880
947
|
*/
|
|
881
948
|
async getPoolHostFees(hostNamespace, poolId) {
|
|
882
949
|
await this.ensureInitialized();
|
|
950
|
+
await this.ensurePingOk();
|
|
883
951
|
return this.typedApi.getPoolHostFees({ hostNamespace, poolId });
|
|
884
952
|
}
|
|
885
953
|
/**
|
|
@@ -894,6 +962,7 @@ class FlashnetClient {
|
|
|
894
962
|
*/
|
|
895
963
|
async withdrawHostFees(params) {
|
|
896
964
|
await this.ensureInitialized();
|
|
965
|
+
await this.ensureAmmOperationAllowed("allow_withdraw_fees");
|
|
897
966
|
const nonce = index$2.generateNonce();
|
|
898
967
|
const intentMessage = intents.generateWithdrawHostFeesIntentMessage({
|
|
899
968
|
hostPublicKey: this.publicKey,
|
|
@@ -908,7 +977,7 @@ class FlashnetClient {
|
|
|
908
977
|
lpIdentityPublicKey: params.lpIdentityPublicKey,
|
|
909
978
|
assetBAmount: params.assetBAmount,
|
|
910
979
|
nonce,
|
|
911
|
-
signature:
|
|
980
|
+
signature: hex.getHexFromUint8Array(signature),
|
|
912
981
|
};
|
|
913
982
|
const response = await this.typedApi.withdrawHostFees(request);
|
|
914
983
|
// Check if the withdrawal was accepted
|
|
@@ -923,6 +992,7 @@ class FlashnetClient {
|
|
|
923
992
|
*/
|
|
924
993
|
async getHostFees(hostNamespace) {
|
|
925
994
|
await this.ensureInitialized();
|
|
995
|
+
await this.ensurePingOk();
|
|
926
996
|
const request = {
|
|
927
997
|
hostNamespace,
|
|
928
998
|
};
|
|
@@ -940,6 +1010,7 @@ class FlashnetClient {
|
|
|
940
1010
|
*/
|
|
941
1011
|
async getPoolIntegratorFees(poolId) {
|
|
942
1012
|
await this.ensureInitialized();
|
|
1013
|
+
await this.ensurePingOk();
|
|
943
1014
|
return this.typedApi.getPoolIntegratorFees({ poolId });
|
|
944
1015
|
}
|
|
945
1016
|
/**
|
|
@@ -947,6 +1018,7 @@ class FlashnetClient {
|
|
|
947
1018
|
*/
|
|
948
1019
|
async withdrawIntegratorFees(params) {
|
|
949
1020
|
await this.ensureInitialized();
|
|
1021
|
+
await this.ensureAmmOperationAllowed("allow_withdraw_fees");
|
|
950
1022
|
const nonce = index$2.generateNonce();
|
|
951
1023
|
const intentMessage = intents.generateWithdrawIntegratorFeesIntentMessage({
|
|
952
1024
|
integratorPublicKey: this.publicKey,
|
|
@@ -962,7 +1034,7 @@ class FlashnetClient {
|
|
|
962
1034
|
lpIdentityPublicKey: params.lpIdentityPublicKey,
|
|
963
1035
|
assetBAmount: params.assetBAmount,
|
|
964
1036
|
nonce,
|
|
965
|
-
signature:
|
|
1037
|
+
signature: hex.getHexFromUint8Array(signature),
|
|
966
1038
|
};
|
|
967
1039
|
const response = await this.typedApi.withdrawIntegratorFees(request);
|
|
968
1040
|
// Check if the withdrawal was accepted
|
|
@@ -988,6 +1060,7 @@ class FlashnetClient {
|
|
|
988
1060
|
*/
|
|
989
1061
|
async createEscrow(params) {
|
|
990
1062
|
await this.ensureInitialized();
|
|
1063
|
+
await this.ensurePingOk();
|
|
991
1064
|
const nonce = index$2.generateNonce();
|
|
992
1065
|
// The intent message requires a different structure for recipients and conditions
|
|
993
1066
|
const intentRecipients = params.recipients.map((r) => ({
|
|
@@ -1016,7 +1089,7 @@ class FlashnetClient {
|
|
|
1016
1089
|
abandonHost: params.abandonHost,
|
|
1017
1090
|
abandonConditions: params.abandonConditions,
|
|
1018
1091
|
nonce,
|
|
1019
|
-
signature:
|
|
1092
|
+
signature: hex.getHexFromUint8Array(signature),
|
|
1020
1093
|
};
|
|
1021
1094
|
const createResponse = await this.typedApi.createEscrow(request);
|
|
1022
1095
|
const autoFund = params.autoFund !== false;
|
|
@@ -1039,6 +1112,7 @@ class FlashnetClient {
|
|
|
1039
1112
|
*/
|
|
1040
1113
|
async fundEscrow(params) {
|
|
1041
1114
|
await this.ensureInitialized();
|
|
1115
|
+
await this.ensurePingOk();
|
|
1042
1116
|
// 1. Balance check
|
|
1043
1117
|
await this.checkBalance({
|
|
1044
1118
|
balancesToCheck: [
|
|
@@ -1063,6 +1137,7 @@ class FlashnetClient {
|
|
|
1063
1137
|
});
|
|
1064
1138
|
}
|
|
1065
1139
|
async executeFundEscrowIntent(params) {
|
|
1140
|
+
await this.ensurePingOk();
|
|
1066
1141
|
// Generate intent
|
|
1067
1142
|
const nonce = index$2.generateNonce();
|
|
1068
1143
|
const intentMessage = intents.generateFundEscrowIntentMessage({
|
|
@@ -1077,7 +1152,7 @@ class FlashnetClient {
|
|
|
1077
1152
|
const request = {
|
|
1078
1153
|
...params,
|
|
1079
1154
|
nonce,
|
|
1080
|
-
signature:
|
|
1155
|
+
signature: hex.getHexFromUint8Array(signature),
|
|
1081
1156
|
};
|
|
1082
1157
|
return this.typedApi.fundEscrow(request);
|
|
1083
1158
|
}
|
|
@@ -1089,6 +1164,7 @@ class FlashnetClient {
|
|
|
1089
1164
|
*/
|
|
1090
1165
|
async claimEscrow(params) {
|
|
1091
1166
|
await this.ensureInitialized();
|
|
1167
|
+
await this.ensurePingOk();
|
|
1092
1168
|
const nonce = index$2.generateNonce();
|
|
1093
1169
|
const intentMessage = intents.generateClaimEscrowIntentMessage({
|
|
1094
1170
|
escrowId: params.escrowId,
|
|
@@ -1100,7 +1176,7 @@ class FlashnetClient {
|
|
|
1100
1176
|
const request = {
|
|
1101
1177
|
escrowId: params.escrowId,
|
|
1102
1178
|
nonce,
|
|
1103
|
-
signature:
|
|
1179
|
+
signature: hex.getHexFromUint8Array(signature),
|
|
1104
1180
|
};
|
|
1105
1181
|
return this.typedApi.claimEscrow(request);
|
|
1106
1182
|
}
|
|
@@ -1143,6 +1219,7 @@ class FlashnetClient {
|
|
|
1143
1219
|
*/
|
|
1144
1220
|
async clawback(params) {
|
|
1145
1221
|
await this.ensureInitialized();
|
|
1222
|
+
await this.ensurePingOk();
|
|
1146
1223
|
const nonce = index$2.generateNonce();
|
|
1147
1224
|
const intentMessage = intents.generateClawbackIntentMessage({
|
|
1148
1225
|
senderPublicKey: this.publicKey,
|
|
@@ -1157,7 +1234,7 @@ class FlashnetClient {
|
|
|
1157
1234
|
sparkTransferId: params.sparkTransferId,
|
|
1158
1235
|
lpIdentityPublicKey: params.lpIdentityPublicKey,
|
|
1159
1236
|
nonce,
|
|
1160
|
-
signature:
|
|
1237
|
+
signature: hex.getHexFromUint8Array(signature),
|
|
1161
1238
|
};
|
|
1162
1239
|
const response = await this.typedApi.clawback(request);
|
|
1163
1240
|
if (!response.accepted) {
|
|
@@ -1252,6 +1329,13 @@ class FlashnetClient {
|
|
|
1252
1329
|
* Helper method to add initial liquidity after pool creation
|
|
1253
1330
|
*/
|
|
1254
1331
|
async addInitialLiquidity(poolId, assetAAddress, assetBAddress, assetAAmount, assetBAmount, assetAMinAmountIn, assetBMinAmountIn) {
|
|
1332
|
+
// Enforce gating and min-amount policy for initial liquidity
|
|
1333
|
+
await this.ensureAmmOperationAllowed("allow_add_liquidity");
|
|
1334
|
+
await this.assertAddLiquidityMeetsMinAmounts({
|
|
1335
|
+
poolId,
|
|
1336
|
+
assetAAmount,
|
|
1337
|
+
assetBAmount,
|
|
1338
|
+
});
|
|
1255
1339
|
const lpSparkAddress = sparkAddress.encodeSparkAddressNew({
|
|
1256
1340
|
identityPublicKey: poolId,
|
|
1257
1341
|
network: this.sparkNetwork,
|
|
@@ -1293,7 +1377,7 @@ class FlashnetClient {
|
|
|
1293
1377
|
assetAMinAmountIn: assetAMinAmountIn.toString(),
|
|
1294
1378
|
assetBMinAmountIn: assetBMinAmountIn.toString(),
|
|
1295
1379
|
nonce,
|
|
1296
|
-
signature:
|
|
1380
|
+
signature: hex.getHexFromUint8Array(signature),
|
|
1297
1381
|
};
|
|
1298
1382
|
const response = await this.typedApi.addLiquidity(request);
|
|
1299
1383
|
// Check if the initial liquidity addition was accepted
|
|
@@ -1308,6 +1392,186 @@ class FlashnetClient {
|
|
|
1308
1392
|
async cleanup() {
|
|
1309
1393
|
await this._wallet.cleanupConnections();
|
|
1310
1394
|
}
|
|
1395
|
+
// ===== Config and Policy Enforcement Helpers =====
|
|
1396
|
+
async ensureAmmOperationAllowed(requiredFeature) {
|
|
1397
|
+
await this.ensurePingOk();
|
|
1398
|
+
const featureMap = await this.getFeatureStatusMap();
|
|
1399
|
+
if (featureMap.get("master_kill_switch")) {
|
|
1400
|
+
throw new Error("Service is temporarily disabled by master kill switch");
|
|
1401
|
+
}
|
|
1402
|
+
if (!featureMap.get(requiredFeature)) {
|
|
1403
|
+
throw new Error(`Operation not allowed: feature '${requiredFeature}' is disabled`);
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
async ensurePingOk() {
|
|
1407
|
+
const now = Date.now();
|
|
1408
|
+
if (this.pingCache && this.pingCache.expiryMs > now) {
|
|
1409
|
+
if (!this.pingCache.ok) {
|
|
1410
|
+
throw new Error("Settlement service unavailable. Only read (GET) operations are allowed right now.");
|
|
1411
|
+
}
|
|
1412
|
+
return;
|
|
1413
|
+
}
|
|
1414
|
+
const ping = await this.typedApi.ping();
|
|
1415
|
+
const ok = !!ping &&
|
|
1416
|
+
typeof ping.status === "string" &&
|
|
1417
|
+
ping.status.toLowerCase() === "ok";
|
|
1418
|
+
this.pingCache = { ok, expiryMs: now + FlashnetClient.PING_TTL_MS };
|
|
1419
|
+
if (!ok) {
|
|
1420
|
+
throw new Error("Settlement service unavailable. Only read (GET) operations are allowed right now.");
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
async getFeatureStatusMap() {
|
|
1424
|
+
const now = Date.now();
|
|
1425
|
+
if (this.featureStatusCache && this.featureStatusCache.expiryMs > now) {
|
|
1426
|
+
const map = new Map();
|
|
1427
|
+
for (const item of this.featureStatusCache.data) {
|
|
1428
|
+
map.set(item.feature_name, Boolean(item.enabled));
|
|
1429
|
+
}
|
|
1430
|
+
return map;
|
|
1431
|
+
}
|
|
1432
|
+
const data = await this.typedApi.getFeatureStatus();
|
|
1433
|
+
this.featureStatusCache = {
|
|
1434
|
+
data,
|
|
1435
|
+
expiryMs: now + FlashnetClient.FEATURE_STATUS_TTL_MS,
|
|
1436
|
+
};
|
|
1437
|
+
const map = new Map();
|
|
1438
|
+
for (const item of data) {
|
|
1439
|
+
map.set(item.feature_name, Boolean(item.enabled));
|
|
1440
|
+
}
|
|
1441
|
+
return map;
|
|
1442
|
+
}
|
|
1443
|
+
async getEnabledMinAmountsMap() {
|
|
1444
|
+
const now = Date.now();
|
|
1445
|
+
if (this.minAmountsCache && this.minAmountsCache.expiryMs > now) {
|
|
1446
|
+
return this.minAmountsCache.map;
|
|
1447
|
+
}
|
|
1448
|
+
const config = await this.typedApi.getMinAmounts();
|
|
1449
|
+
const map = new Map();
|
|
1450
|
+
for (const item of config) {
|
|
1451
|
+
if (item.enabled) {
|
|
1452
|
+
const key = item.asset_identifier.toLowerCase();
|
|
1453
|
+
const value = BigInt(String(item.min_amount));
|
|
1454
|
+
map.set(key, value);
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
this.minAmountsCache = {
|
|
1458
|
+
map,
|
|
1459
|
+
expiryMs: now + FlashnetClient.MIN_AMOUNTS_TTL_MS,
|
|
1460
|
+
};
|
|
1461
|
+
return map;
|
|
1462
|
+
}
|
|
1463
|
+
getHexAddress(addr) {
|
|
1464
|
+
return this.toHexTokenIdentifier(addr).toLowerCase();
|
|
1465
|
+
}
|
|
1466
|
+
async assertSwapMeetsMinAmounts(params) {
|
|
1467
|
+
const minMap = await this.getEnabledMinAmountsMap();
|
|
1468
|
+
if (minMap.size === 0) {
|
|
1469
|
+
return;
|
|
1470
|
+
}
|
|
1471
|
+
const inHex = this.getHexAddress(params.assetInAddress);
|
|
1472
|
+
const outHex = this.getHexAddress(params.assetOutAddress);
|
|
1473
|
+
const minIn = minMap.get(inHex);
|
|
1474
|
+
const minOut = minMap.get(outHex);
|
|
1475
|
+
const amountIn = BigInt(String(params.amountIn));
|
|
1476
|
+
const minAmountOut = BigInt(String(params.minAmountOut));
|
|
1477
|
+
if (minIn && minOut) {
|
|
1478
|
+
if (amountIn < minIn) {
|
|
1479
|
+
throw new Error(`Minimum amount not met for input asset. Required \
|
|
1480
|
+
${minIn.toString()}, provided ${amountIn.toString()}`);
|
|
1481
|
+
}
|
|
1482
|
+
return;
|
|
1483
|
+
}
|
|
1484
|
+
if (minIn) {
|
|
1485
|
+
if (amountIn < minIn) {
|
|
1486
|
+
throw new Error(`Minimum amount not met for input asset. Required \
|
|
1487
|
+
${minIn.toString()}, provided ${amountIn.toString()}`);
|
|
1488
|
+
}
|
|
1489
|
+
return;
|
|
1490
|
+
}
|
|
1491
|
+
if (minOut) {
|
|
1492
|
+
const relaxed = minOut / 2n; // 50% relaxation for slippage
|
|
1493
|
+
if (minAmountOut < relaxed) {
|
|
1494
|
+
throw new Error(`Minimum amount not met for output asset. Required at least \
|
|
1495
|
+
${relaxed.toString()} (50% relaxed), provided minAmountOut ${minAmountOut.toString()}`);
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
async assertAddLiquidityMeetsMinAmounts(params) {
|
|
1500
|
+
const minMap = await this.getEnabledMinAmountsMap();
|
|
1501
|
+
if (minMap.size === 0) {
|
|
1502
|
+
return;
|
|
1503
|
+
}
|
|
1504
|
+
const pool = await this.getPool(params.poolId);
|
|
1505
|
+
const aHex = pool.assetAAddress.toLowerCase();
|
|
1506
|
+
const bHex = pool.assetBAddress.toLowerCase();
|
|
1507
|
+
const aMin = minMap.get(aHex);
|
|
1508
|
+
const bMin = minMap.get(bHex);
|
|
1509
|
+
if (aMin) {
|
|
1510
|
+
const aAmt = BigInt(String(params.assetAAmount));
|
|
1511
|
+
if (aAmt < aMin) {
|
|
1512
|
+
throw new Error(`Minimum amount not met for Asset A. Required ${aMin.toString()}, provided ${aAmt.toString()}`);
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
if (bMin) {
|
|
1516
|
+
const bAmt = BigInt(String(params.assetBAmount));
|
|
1517
|
+
if (bAmt < bMin) {
|
|
1518
|
+
throw new Error(`Minimum amount not met for Asset B. Required ${bMin.toString()}, provided ${bAmt.toString()}`);
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
async assertRemoveLiquidityMeetsMinAmounts(params) {
|
|
1523
|
+
const minMap = await this.getEnabledMinAmountsMap();
|
|
1524
|
+
if (minMap.size === 0) {
|
|
1525
|
+
return;
|
|
1526
|
+
}
|
|
1527
|
+
const simulation = await this.simulateRemoveLiquidity({
|
|
1528
|
+
poolId: params.poolId,
|
|
1529
|
+
providerPublicKey: this.publicKey,
|
|
1530
|
+
lpTokensToRemove: String(params.lpTokensToRemove),
|
|
1531
|
+
});
|
|
1532
|
+
const pool = await this.getPool(params.poolId);
|
|
1533
|
+
const aHex = pool.assetAAddress.toLowerCase();
|
|
1534
|
+
const bHex = pool.assetBAddress.toLowerCase();
|
|
1535
|
+
const aMin = minMap.get(aHex);
|
|
1536
|
+
const bMin = minMap.get(bHex);
|
|
1537
|
+
if (aMin) {
|
|
1538
|
+
const predictedAOut = BigInt(String(simulation.assetAAmount));
|
|
1539
|
+
const relaxedA = aMin / 2n; // apply 50% relaxation for outputs
|
|
1540
|
+
if (predictedAOut < relaxedA) {
|
|
1541
|
+
throw new Error(`Minimum amount not met for Asset A on withdrawal. Required at least ${relaxedA.toString()} (50% relaxed), predicted ${predictedAOut.toString()}`);
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
if (bMin) {
|
|
1545
|
+
const predictedBOut = BigInt(String(simulation.assetBAmount));
|
|
1546
|
+
const relaxedB = bMin / 2n;
|
|
1547
|
+
if (predictedBOut < relaxedB) {
|
|
1548
|
+
throw new Error(`Minimum amount not met for Asset B on withdrawal. Required at least ${relaxedB.toString()} (50% relaxed), predicted ${predictedBOut.toString()}`);
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
async assertAllowedAssetBForPoolCreation(assetBHex) {
|
|
1553
|
+
const now = Date.now();
|
|
1554
|
+
let allowed;
|
|
1555
|
+
if (this.allowedAssetsCache && this.allowedAssetsCache.expiryMs > now) {
|
|
1556
|
+
allowed = this.allowedAssetsCache.data;
|
|
1557
|
+
}
|
|
1558
|
+
else {
|
|
1559
|
+
allowed = await this.typedApi.getAllowedAssets();
|
|
1560
|
+
this.allowedAssetsCache = {
|
|
1561
|
+
data: allowed,
|
|
1562
|
+
expiryMs: now + FlashnetClient.ALLOWED_ASSETS_TTL_MS,
|
|
1563
|
+
};
|
|
1564
|
+
}
|
|
1565
|
+
if (!allowed || allowed.length === 0) {
|
|
1566
|
+
// Wildcard allowance
|
|
1567
|
+
return;
|
|
1568
|
+
}
|
|
1569
|
+
const isAllowed = allowed.some((it) => it.enabled &&
|
|
1570
|
+
it.asset_identifier.toLowerCase() === assetBHex.toLowerCase());
|
|
1571
|
+
if (!isAllowed) {
|
|
1572
|
+
throw new Error(`Asset B is not allowed for pool creation: ${assetBHex}`);
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1311
1575
|
}
|
|
1312
1576
|
|
|
1313
1577
|
exports.FlashnetClient = FlashnetClient;
|