@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
|
@@ -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,38 +423,13 @@ 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
427
|
!Number.isInteger(params.graduationThresholdPct) ||
|
|
415
428
|
params.graduationThresholdPct <= 0) {
|
|
416
429
|
throw new Error("Graduation threshold percentage must be a positive integer");
|
|
417
430
|
}
|
|
418
|
-
const supply = parsePositiveIntegerToBigInt(params.initialTokenSupply, "Initial token supply");
|
|
419
|
-
const targetB = parsePositiveIntegerToBigInt(params.targetRaise, "Target raise");
|
|
431
|
+
const supply = FlashnetClient.parsePositiveIntegerToBigInt(params.initialTokenSupply, "Initial token supply");
|
|
432
|
+
const targetB = FlashnetClient.parsePositiveIntegerToBigInt(params.targetRaise, "Target raise");
|
|
420
433
|
const graduationThresholdPct = BigInt(params.graduationThresholdPct);
|
|
421
434
|
// Check feasibility: f - g*(1-f) > 0 where f is graduationThresholdPct/100 and g is 1
|
|
422
435
|
const MIN_GRADUATION_THRESHOLD_PCT = 50n;
|
|
@@ -431,16 +444,22 @@ class FlashnetClient {
|
|
|
431
444
|
// (2n * graduationThresholdPct - 100n) / 100n
|
|
432
445
|
// 1/denom is 100n / (2n * graduationThresholdPct - 100n)
|
|
433
446
|
// Calculate virtual reserves and round down to integers
|
|
434
|
-
//
|
|
435
|
-
const
|
|
436
|
-
const
|
|
437
|
-
const
|
|
438
|
-
const
|
|
439
|
-
//
|
|
440
|
-
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
const
|
|
447
|
+
// totalA is (supply * f * f) / denom
|
|
448
|
+
const totalANumerator = BigInt(supply) * graduationThresholdPct * graduationThresholdPct;
|
|
449
|
+
const totalADenominator = 200n * graduationThresholdPct - 10000n;
|
|
450
|
+
const totalARemainder = totalANumerator % totalADenominator;
|
|
451
|
+
const totalA = (totalANumerator - totalARemainder) / totalADenominator;
|
|
452
|
+
// Since there is an initial supply of tokens, we subtract that amount
|
|
453
|
+
// to get the remaining "virtual" amount for pricing
|
|
454
|
+
const virtualA = totalA - supply;
|
|
455
|
+
// totalB is (targetB * g * (1 - f)) / denom
|
|
456
|
+
const totalBNumerator = BigInt(targetB) * (100n - graduationThresholdPct);
|
|
457
|
+
const totalBDenominator = 2n * graduationThresholdPct - 100n;
|
|
458
|
+
const totalBRemainder = totalBNumerator % totalBDenominator;
|
|
459
|
+
const totalB = (totalBNumerator - totalBRemainder) / totalBDenominator;
|
|
460
|
+
// Bonding curve starts with no deposited amount of asset B,
|
|
461
|
+
// so virtualB is all of totalB
|
|
462
|
+
const virtualB = totalB;
|
|
444
463
|
// Calculate threshold as amount of asset A (not percentage)
|
|
445
464
|
const threshold = (BigInt(supply) * graduationThresholdPct) / 100n;
|
|
446
465
|
return {
|
|
@@ -457,18 +476,20 @@ class FlashnetClient {
|
|
|
457
476
|
*/
|
|
458
477
|
async createSingleSidedPool(params) {
|
|
459
478
|
await this.ensureInitialized();
|
|
479
|
+
await this.ensureAmmOperationAllowed("allow_pool_creation");
|
|
480
|
+
await this.assertAllowedAssetBForPoolCreation(this.toHexTokenIdentifier(params.assetBAddress));
|
|
460
481
|
if (!params.hostNamespace && params.totalHostFeeRateBps < 10) {
|
|
461
482
|
throw new Error(`Host fee must be greater than 10 bps when no host namespace is provided`);
|
|
462
483
|
}
|
|
463
|
-
//
|
|
464
|
-
const
|
|
465
|
-
const
|
|
466
|
-
const
|
|
484
|
+
// Validate reserves are valid positive integers before any operations
|
|
485
|
+
const assetAInitialReserve = FlashnetClient.parsePositiveIntegerToBigInt(params.assetAInitialReserve, "Asset A Initial Reserve").toString();
|
|
486
|
+
const virtualReserveA = FlashnetClient.parsePositiveIntegerToBigInt(params.virtualReserveA, "Virtual Reserve A").toString();
|
|
487
|
+
const virtualReserveB = FlashnetClient.parsePositiveIntegerToBigInt(params.virtualReserveB, "Virtual Reserve B").toString();
|
|
467
488
|
await this.checkBalance({
|
|
468
489
|
balancesToCheck: [
|
|
469
490
|
{
|
|
470
491
|
assetAddress: params.assetAAddress,
|
|
471
|
-
amount:
|
|
492
|
+
amount: assetAInitialReserve,
|
|
472
493
|
},
|
|
473
494
|
],
|
|
474
495
|
errorPrefix: "Insufficient balance for pool creation: ",
|
|
@@ -480,9 +501,9 @@ class FlashnetClient {
|
|
|
480
501
|
poolOwnerPublicKey,
|
|
481
502
|
assetAAddress: this.toHexTokenIdentifier(params.assetAAddress),
|
|
482
503
|
assetBAddress: this.toHexTokenIdentifier(params.assetBAddress),
|
|
483
|
-
assetAInitialReserve
|
|
484
|
-
virtualReserveA
|
|
485
|
-
virtualReserveB
|
|
504
|
+
assetAInitialReserve,
|
|
505
|
+
virtualReserveA,
|
|
506
|
+
virtualReserveB,
|
|
486
507
|
threshold: params.threshold.toString(),
|
|
487
508
|
lpFeeRateBps: params.lpFeeRateBps.toString(),
|
|
488
509
|
totalHostFeeRateBps: params.totalHostFeeRateBps.toString(),
|
|
@@ -495,15 +516,15 @@ class FlashnetClient {
|
|
|
495
516
|
poolOwnerPublicKey,
|
|
496
517
|
assetAAddress: this.toHexTokenIdentifier(params.assetAAddress),
|
|
497
518
|
assetBAddress: this.toHexTokenIdentifier(params.assetBAddress),
|
|
498
|
-
assetAInitialReserve
|
|
499
|
-
virtualReserveA
|
|
500
|
-
virtualReserveB
|
|
519
|
+
assetAInitialReserve,
|
|
520
|
+
virtualReserveA,
|
|
521
|
+
virtualReserveB,
|
|
501
522
|
threshold: params.threshold.toString(),
|
|
502
523
|
lpFeeRateBps: params.lpFeeRateBps.toString(),
|
|
503
524
|
totalHostFeeRateBps: params.totalHostFeeRateBps.toString(),
|
|
504
525
|
hostNamespace: params.hostNamespace,
|
|
505
526
|
nonce,
|
|
506
|
-
signature:
|
|
527
|
+
signature: getHexFromUint8Array(signature),
|
|
507
528
|
};
|
|
508
529
|
const createResponse = await this.typedApi.createSingleSidedPool(request);
|
|
509
530
|
if (params.disableInitialDeposit) {
|
|
@@ -517,7 +538,7 @@ class FlashnetClient {
|
|
|
517
538
|
const assetATransferId = await this.transferAsset({
|
|
518
539
|
receiverSparkAddress: lpSparkAddress,
|
|
519
540
|
assetAddress: params.assetAAddress,
|
|
520
|
-
amount:
|
|
541
|
+
amount: assetAInitialReserve,
|
|
521
542
|
});
|
|
522
543
|
const confirmResponse = await this.confirmInitialDeposit(createResponse.poolId, assetATransferId, poolOwnerPublicKey);
|
|
523
544
|
if (!confirmResponse.confirmed) {
|
|
@@ -546,7 +567,7 @@ class FlashnetClient {
|
|
|
546
567
|
poolId,
|
|
547
568
|
assetASparkTransferId,
|
|
548
569
|
nonce,
|
|
549
|
-
signature:
|
|
570
|
+
signature: getHexFromUint8Array(signature),
|
|
550
571
|
poolOwnerPublicKey: poolOwnerPublicKey ?? this.publicKey,
|
|
551
572
|
};
|
|
552
573
|
return this.typedApi.confirmInitialDeposit(request);
|
|
@@ -557,6 +578,7 @@ class FlashnetClient {
|
|
|
557
578
|
*/
|
|
558
579
|
async simulateSwap(params) {
|
|
559
580
|
await this.ensureInitialized();
|
|
581
|
+
await this.ensurePingOk();
|
|
560
582
|
return this.typedApi.simulateSwap(params);
|
|
561
583
|
}
|
|
562
584
|
/**
|
|
@@ -564,6 +586,14 @@ class FlashnetClient {
|
|
|
564
586
|
*/
|
|
565
587
|
async executeSwap(params) {
|
|
566
588
|
await this.ensureInitialized();
|
|
589
|
+
// Gate by feature flags and ping, and enforce min-amount policy before transfers
|
|
590
|
+
await this.ensureAmmOperationAllowed("allow_swaps");
|
|
591
|
+
await this.assertSwapMeetsMinAmounts({
|
|
592
|
+
assetInAddress: params.assetInAddress,
|
|
593
|
+
assetOutAddress: params.assetOutAddress,
|
|
594
|
+
amountIn: params.amountIn,
|
|
595
|
+
minAmountOut: params.minAmountOut,
|
|
596
|
+
});
|
|
567
597
|
// Transfer assets to pool using new address encoding
|
|
568
598
|
const lpSparkAddress = encodeSparkAddressNew({
|
|
569
599
|
identityPublicKey: params.poolId,
|
|
@@ -582,6 +612,14 @@ class FlashnetClient {
|
|
|
582
612
|
}
|
|
583
613
|
async executeSwapIntent(params) {
|
|
584
614
|
await this.ensureInitialized();
|
|
615
|
+
// Also enforce gating and min amounts for direct intent usage
|
|
616
|
+
await this.ensureAmmOperationAllowed("allow_swaps");
|
|
617
|
+
await this.assertSwapMeetsMinAmounts({
|
|
618
|
+
assetInAddress: params.assetInAddress,
|
|
619
|
+
assetOutAddress: params.assetOutAddress,
|
|
620
|
+
amountIn: params.amountIn,
|
|
621
|
+
minAmountOut: params.minAmountOut,
|
|
622
|
+
});
|
|
585
623
|
// Generate swap intent
|
|
586
624
|
const nonce = generateNonce();
|
|
587
625
|
const intentMessage = generatePoolSwapIntentMessage({
|
|
@@ -611,7 +649,7 @@ class FlashnetClient {
|
|
|
611
649
|
totalIntegratorFeeRateBps: params.integratorFeeRateBps?.toString() || "0",
|
|
612
650
|
integratorPublicKey: params.integratorPublicKey || "",
|
|
613
651
|
nonce,
|
|
614
|
-
signature:
|
|
652
|
+
signature: getHexFromUint8Array(signature),
|
|
615
653
|
};
|
|
616
654
|
const response = await this.typedApi.executeSwap(request);
|
|
617
655
|
// Check if the swap was accepted
|
|
@@ -632,6 +670,7 @@ class FlashnetClient {
|
|
|
632
670
|
throw new Error("Route swap cannot have more than 4 hops");
|
|
633
671
|
}
|
|
634
672
|
await this.ensureInitialized();
|
|
673
|
+
await this.ensurePingOk();
|
|
635
674
|
return this.typedApi.simulateRouteSwap(params);
|
|
636
675
|
}
|
|
637
676
|
/**
|
|
@@ -639,6 +678,18 @@ class FlashnetClient {
|
|
|
639
678
|
*/
|
|
640
679
|
async executeRouteSwap(params) {
|
|
641
680
|
await this.ensureInitialized();
|
|
681
|
+
await this.ensureAmmOperationAllowed("allow_route_swaps");
|
|
682
|
+
// Validate min-amount policy for route: check initial input and final output asset
|
|
683
|
+
const finalOutputAsset = params.hops[params.hops.length - 1]?.assetOutAddress;
|
|
684
|
+
if (!finalOutputAsset) {
|
|
685
|
+
throw new Error("Route swap requires at least one hop with output asset");
|
|
686
|
+
}
|
|
687
|
+
await this.assertSwapMeetsMinAmounts({
|
|
688
|
+
assetInAddress: params.initialAssetAddress,
|
|
689
|
+
assetOutAddress: finalOutputAsset,
|
|
690
|
+
amountIn: params.inputAmount,
|
|
691
|
+
minAmountOut: params.minAmountOut,
|
|
692
|
+
});
|
|
642
693
|
// Validate hops array
|
|
643
694
|
if (params.hops.length > 4) {
|
|
644
695
|
throw new Error("Route swap cannot have more than 4 hops");
|
|
@@ -708,7 +759,7 @@ class FlashnetClient {
|
|
|
708
759
|
maxRouteSlippageBps: params.maxRouteSlippageBps.toString(),
|
|
709
760
|
minAmountOut: params.minAmountOut,
|
|
710
761
|
nonce,
|
|
711
|
-
signature:
|
|
762
|
+
signature: getHexFromUint8Array(signature),
|
|
712
763
|
integratorFeeRateBps: params.integratorFeeRateBps?.toString() || "0",
|
|
713
764
|
integratorPublicKey: params.integratorPublicKey || "",
|
|
714
765
|
};
|
|
@@ -729,6 +780,7 @@ class FlashnetClient {
|
|
|
729
780
|
*/
|
|
730
781
|
async simulateAddLiquidity(params) {
|
|
731
782
|
await this.ensureInitialized();
|
|
783
|
+
await this.ensurePingOk();
|
|
732
784
|
return this.typedApi.simulateAddLiquidity(params);
|
|
733
785
|
}
|
|
734
786
|
/**
|
|
@@ -736,8 +788,15 @@ class FlashnetClient {
|
|
|
736
788
|
*/
|
|
737
789
|
async addLiquidity(params) {
|
|
738
790
|
await this.ensureInitialized();
|
|
791
|
+
await this.ensureAmmOperationAllowed("allow_add_liquidity");
|
|
739
792
|
// Get pool details to know which assets we're dealing with
|
|
740
793
|
const pool = await this.getPool(params.poolId);
|
|
794
|
+
// Enforce min-amount policy for inputs based on pool assets
|
|
795
|
+
await this.assertAddLiquidityMeetsMinAmounts({
|
|
796
|
+
poolId: params.poolId,
|
|
797
|
+
assetAAmount: params.assetAAmount,
|
|
798
|
+
assetBAmount: params.assetBAmount,
|
|
799
|
+
});
|
|
741
800
|
// Transfer assets to pool using new address encoding
|
|
742
801
|
const lpSparkAddress = encodeSparkAddressNew({
|
|
743
802
|
identityPublicKey: params.poolId,
|
|
@@ -781,7 +840,7 @@ class FlashnetClient {
|
|
|
781
840
|
assetAMinAmountIn: params.assetAMinAmountIn.toString(),
|
|
782
841
|
assetBMinAmountIn: params.assetBMinAmountIn.toString(),
|
|
783
842
|
nonce,
|
|
784
|
-
signature:
|
|
843
|
+
signature: getHexFromUint8Array(signature),
|
|
785
844
|
};
|
|
786
845
|
const response = await this.typedApi.addLiquidity(request);
|
|
787
846
|
// Check if the liquidity addition was accepted
|
|
@@ -799,6 +858,7 @@ class FlashnetClient {
|
|
|
799
858
|
*/
|
|
800
859
|
async simulateRemoveLiquidity(params) {
|
|
801
860
|
await this.ensureInitialized();
|
|
861
|
+
await this.ensurePingOk();
|
|
802
862
|
return this.typedApi.simulateRemoveLiquidity(params);
|
|
803
863
|
}
|
|
804
864
|
/**
|
|
@@ -806,6 +866,7 @@ class FlashnetClient {
|
|
|
806
866
|
*/
|
|
807
867
|
async removeLiquidity(params) {
|
|
808
868
|
await this.ensureInitialized();
|
|
869
|
+
await this.ensureAmmOperationAllowed("allow_withdraw_liquidity");
|
|
809
870
|
// Check LP token balance
|
|
810
871
|
const position = await this.getLpPosition(params.poolId);
|
|
811
872
|
const lpTokensOwned = position.lpTokensOwned;
|
|
@@ -813,6 +874,11 @@ class FlashnetClient {
|
|
|
813
874
|
if (compareDecimalStrings(lpTokensOwned, tokensToRemove) < 0) {
|
|
814
875
|
throw new Error(`Insufficient LP tokens. Owned: ${lpTokensOwned}, Requested: ${tokensToRemove}`);
|
|
815
876
|
}
|
|
877
|
+
// Pre-simulate and enforce min-amount policy for outputs
|
|
878
|
+
await this.assertRemoveLiquidityMeetsMinAmounts({
|
|
879
|
+
poolId: params.poolId,
|
|
880
|
+
lpTokensToRemove: params.lpTokensToRemove,
|
|
881
|
+
});
|
|
816
882
|
// Generate remove liquidity intent
|
|
817
883
|
const nonce = generateNonce();
|
|
818
884
|
const intentMessage = generateRemoveLiquidityIntentMessage({
|
|
@@ -829,7 +895,7 @@ class FlashnetClient {
|
|
|
829
895
|
poolId: params.poolId,
|
|
830
896
|
lpTokensToRemove: params.lpTokensToRemove,
|
|
831
897
|
nonce,
|
|
832
|
-
signature:
|
|
898
|
+
signature: getHexFromUint8Array(signature),
|
|
833
899
|
};
|
|
834
900
|
const response = await this.typedApi.removeLiquidity(request);
|
|
835
901
|
// Check if the liquidity removal was accepted
|
|
@@ -845,6 +911,7 @@ class FlashnetClient {
|
|
|
845
911
|
*/
|
|
846
912
|
async registerHost(params) {
|
|
847
913
|
await this.ensureInitialized();
|
|
914
|
+
await this.ensurePingOk();
|
|
848
915
|
const feeRecipient = params.feeRecipientPublicKey || this.publicKey;
|
|
849
916
|
const nonce = generateNonce();
|
|
850
917
|
// Generate intent
|
|
@@ -862,7 +929,7 @@ class FlashnetClient {
|
|
|
862
929
|
minFeeBps: params.minFeeBps,
|
|
863
930
|
feeRecipientPublicKey: feeRecipient,
|
|
864
931
|
nonce,
|
|
865
|
-
signature:
|
|
932
|
+
signature: getHexFromUint8Array(signature),
|
|
866
933
|
};
|
|
867
934
|
return this.typedApi.registerHost(request);
|
|
868
935
|
}
|
|
@@ -878,6 +945,7 @@ class FlashnetClient {
|
|
|
878
945
|
*/
|
|
879
946
|
async getPoolHostFees(hostNamespace, poolId) {
|
|
880
947
|
await this.ensureInitialized();
|
|
948
|
+
await this.ensurePingOk();
|
|
881
949
|
return this.typedApi.getPoolHostFees({ hostNamespace, poolId });
|
|
882
950
|
}
|
|
883
951
|
/**
|
|
@@ -892,6 +960,7 @@ class FlashnetClient {
|
|
|
892
960
|
*/
|
|
893
961
|
async withdrawHostFees(params) {
|
|
894
962
|
await this.ensureInitialized();
|
|
963
|
+
await this.ensureAmmOperationAllowed("allow_withdraw_fees");
|
|
895
964
|
const nonce = generateNonce();
|
|
896
965
|
const intentMessage = generateWithdrawHostFeesIntentMessage({
|
|
897
966
|
hostPublicKey: this.publicKey,
|
|
@@ -906,7 +975,7 @@ class FlashnetClient {
|
|
|
906
975
|
lpIdentityPublicKey: params.lpIdentityPublicKey,
|
|
907
976
|
assetBAmount: params.assetBAmount,
|
|
908
977
|
nonce,
|
|
909
|
-
signature:
|
|
978
|
+
signature: getHexFromUint8Array(signature),
|
|
910
979
|
};
|
|
911
980
|
const response = await this.typedApi.withdrawHostFees(request);
|
|
912
981
|
// Check if the withdrawal was accepted
|
|
@@ -921,6 +990,7 @@ class FlashnetClient {
|
|
|
921
990
|
*/
|
|
922
991
|
async getHostFees(hostNamespace) {
|
|
923
992
|
await this.ensureInitialized();
|
|
993
|
+
await this.ensurePingOk();
|
|
924
994
|
const request = {
|
|
925
995
|
hostNamespace,
|
|
926
996
|
};
|
|
@@ -938,6 +1008,7 @@ class FlashnetClient {
|
|
|
938
1008
|
*/
|
|
939
1009
|
async getPoolIntegratorFees(poolId) {
|
|
940
1010
|
await this.ensureInitialized();
|
|
1011
|
+
await this.ensurePingOk();
|
|
941
1012
|
return this.typedApi.getPoolIntegratorFees({ poolId });
|
|
942
1013
|
}
|
|
943
1014
|
/**
|
|
@@ -945,6 +1016,7 @@ class FlashnetClient {
|
|
|
945
1016
|
*/
|
|
946
1017
|
async withdrawIntegratorFees(params) {
|
|
947
1018
|
await this.ensureInitialized();
|
|
1019
|
+
await this.ensureAmmOperationAllowed("allow_withdraw_fees");
|
|
948
1020
|
const nonce = generateNonce();
|
|
949
1021
|
const intentMessage = generateWithdrawIntegratorFeesIntentMessage({
|
|
950
1022
|
integratorPublicKey: this.publicKey,
|
|
@@ -960,7 +1032,7 @@ class FlashnetClient {
|
|
|
960
1032
|
lpIdentityPublicKey: params.lpIdentityPublicKey,
|
|
961
1033
|
assetBAmount: params.assetBAmount,
|
|
962
1034
|
nonce,
|
|
963
|
-
signature:
|
|
1035
|
+
signature: getHexFromUint8Array(signature),
|
|
964
1036
|
};
|
|
965
1037
|
const response = await this.typedApi.withdrawIntegratorFees(request);
|
|
966
1038
|
// Check if the withdrawal was accepted
|
|
@@ -986,6 +1058,7 @@ class FlashnetClient {
|
|
|
986
1058
|
*/
|
|
987
1059
|
async createEscrow(params) {
|
|
988
1060
|
await this.ensureInitialized();
|
|
1061
|
+
await this.ensurePingOk();
|
|
989
1062
|
const nonce = generateNonce();
|
|
990
1063
|
// The intent message requires a different structure for recipients and conditions
|
|
991
1064
|
const intentRecipients = params.recipients.map((r) => ({
|
|
@@ -1014,7 +1087,7 @@ class FlashnetClient {
|
|
|
1014
1087
|
abandonHost: params.abandonHost,
|
|
1015
1088
|
abandonConditions: params.abandonConditions,
|
|
1016
1089
|
nonce,
|
|
1017
|
-
signature:
|
|
1090
|
+
signature: getHexFromUint8Array(signature),
|
|
1018
1091
|
};
|
|
1019
1092
|
const createResponse = await this.typedApi.createEscrow(request);
|
|
1020
1093
|
const autoFund = params.autoFund !== false;
|
|
@@ -1037,6 +1110,7 @@ class FlashnetClient {
|
|
|
1037
1110
|
*/
|
|
1038
1111
|
async fundEscrow(params) {
|
|
1039
1112
|
await this.ensureInitialized();
|
|
1113
|
+
await this.ensurePingOk();
|
|
1040
1114
|
// 1. Balance check
|
|
1041
1115
|
await this.checkBalance({
|
|
1042
1116
|
balancesToCheck: [
|
|
@@ -1061,6 +1135,7 @@ class FlashnetClient {
|
|
|
1061
1135
|
});
|
|
1062
1136
|
}
|
|
1063
1137
|
async executeFundEscrowIntent(params) {
|
|
1138
|
+
await this.ensurePingOk();
|
|
1064
1139
|
// Generate intent
|
|
1065
1140
|
const nonce = generateNonce();
|
|
1066
1141
|
const intentMessage = generateFundEscrowIntentMessage({
|
|
@@ -1075,7 +1150,7 @@ class FlashnetClient {
|
|
|
1075
1150
|
const request = {
|
|
1076
1151
|
...params,
|
|
1077
1152
|
nonce,
|
|
1078
|
-
signature:
|
|
1153
|
+
signature: getHexFromUint8Array(signature),
|
|
1079
1154
|
};
|
|
1080
1155
|
return this.typedApi.fundEscrow(request);
|
|
1081
1156
|
}
|
|
@@ -1087,6 +1162,7 @@ class FlashnetClient {
|
|
|
1087
1162
|
*/
|
|
1088
1163
|
async claimEscrow(params) {
|
|
1089
1164
|
await this.ensureInitialized();
|
|
1165
|
+
await this.ensurePingOk();
|
|
1090
1166
|
const nonce = generateNonce();
|
|
1091
1167
|
const intentMessage = generateClaimEscrowIntentMessage({
|
|
1092
1168
|
escrowId: params.escrowId,
|
|
@@ -1098,7 +1174,7 @@ class FlashnetClient {
|
|
|
1098
1174
|
const request = {
|
|
1099
1175
|
escrowId: params.escrowId,
|
|
1100
1176
|
nonce,
|
|
1101
|
-
signature:
|
|
1177
|
+
signature: getHexFromUint8Array(signature),
|
|
1102
1178
|
};
|
|
1103
1179
|
return this.typedApi.claimEscrow(request);
|
|
1104
1180
|
}
|
|
@@ -1141,6 +1217,7 @@ class FlashnetClient {
|
|
|
1141
1217
|
*/
|
|
1142
1218
|
async clawback(params) {
|
|
1143
1219
|
await this.ensureInitialized();
|
|
1220
|
+
await this.ensurePingOk();
|
|
1144
1221
|
const nonce = generateNonce();
|
|
1145
1222
|
const intentMessage = generateClawbackIntentMessage({
|
|
1146
1223
|
senderPublicKey: this.publicKey,
|
|
@@ -1155,7 +1232,7 @@ class FlashnetClient {
|
|
|
1155
1232
|
sparkTransferId: params.sparkTransferId,
|
|
1156
1233
|
lpIdentityPublicKey: params.lpIdentityPublicKey,
|
|
1157
1234
|
nonce,
|
|
1158
|
-
signature:
|
|
1235
|
+
signature: getHexFromUint8Array(signature),
|
|
1159
1236
|
};
|
|
1160
1237
|
const response = await this.typedApi.clawback(request);
|
|
1161
1238
|
if (!response.accepted) {
|
|
@@ -1250,6 +1327,13 @@ class FlashnetClient {
|
|
|
1250
1327
|
* Helper method to add initial liquidity after pool creation
|
|
1251
1328
|
*/
|
|
1252
1329
|
async addInitialLiquidity(poolId, assetAAddress, assetBAddress, assetAAmount, assetBAmount, assetAMinAmountIn, assetBMinAmountIn) {
|
|
1330
|
+
// Enforce gating and min-amount policy for initial liquidity
|
|
1331
|
+
await this.ensureAmmOperationAllowed("allow_add_liquidity");
|
|
1332
|
+
await this.assertAddLiquidityMeetsMinAmounts({
|
|
1333
|
+
poolId,
|
|
1334
|
+
assetAAmount,
|
|
1335
|
+
assetBAmount,
|
|
1336
|
+
});
|
|
1253
1337
|
const lpSparkAddress = encodeSparkAddressNew({
|
|
1254
1338
|
identityPublicKey: poolId,
|
|
1255
1339
|
network: this.sparkNetwork,
|
|
@@ -1291,7 +1375,7 @@ class FlashnetClient {
|
|
|
1291
1375
|
assetAMinAmountIn: assetAMinAmountIn.toString(),
|
|
1292
1376
|
assetBMinAmountIn: assetBMinAmountIn.toString(),
|
|
1293
1377
|
nonce,
|
|
1294
|
-
signature:
|
|
1378
|
+
signature: getHexFromUint8Array(signature),
|
|
1295
1379
|
};
|
|
1296
1380
|
const response = await this.typedApi.addLiquidity(request);
|
|
1297
1381
|
// Check if the initial liquidity addition was accepted
|
|
@@ -1306,6 +1390,186 @@ class FlashnetClient {
|
|
|
1306
1390
|
async cleanup() {
|
|
1307
1391
|
await this._wallet.cleanupConnections();
|
|
1308
1392
|
}
|
|
1393
|
+
// ===== Config and Policy Enforcement Helpers =====
|
|
1394
|
+
async ensureAmmOperationAllowed(requiredFeature) {
|
|
1395
|
+
await this.ensurePingOk();
|
|
1396
|
+
const featureMap = await this.getFeatureStatusMap();
|
|
1397
|
+
if (featureMap.get("master_kill_switch")) {
|
|
1398
|
+
throw new Error("Service is temporarily disabled by master kill switch");
|
|
1399
|
+
}
|
|
1400
|
+
if (!featureMap.get(requiredFeature)) {
|
|
1401
|
+
throw new Error(`Operation not allowed: feature '${requiredFeature}' is disabled`);
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
async ensurePingOk() {
|
|
1405
|
+
const now = Date.now();
|
|
1406
|
+
if (this.pingCache && this.pingCache.expiryMs > now) {
|
|
1407
|
+
if (!this.pingCache.ok) {
|
|
1408
|
+
throw new Error("Settlement service unavailable. Only read (GET) operations are allowed right now.");
|
|
1409
|
+
}
|
|
1410
|
+
return;
|
|
1411
|
+
}
|
|
1412
|
+
const ping = await this.typedApi.ping();
|
|
1413
|
+
const ok = !!ping &&
|
|
1414
|
+
typeof ping.status === "string" &&
|
|
1415
|
+
ping.status.toLowerCase() === "ok";
|
|
1416
|
+
this.pingCache = { ok, expiryMs: now + FlashnetClient.PING_TTL_MS };
|
|
1417
|
+
if (!ok) {
|
|
1418
|
+
throw new Error("Settlement service unavailable. Only read (GET) operations are allowed right now.");
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
async getFeatureStatusMap() {
|
|
1422
|
+
const now = Date.now();
|
|
1423
|
+
if (this.featureStatusCache && this.featureStatusCache.expiryMs > now) {
|
|
1424
|
+
const map = new Map();
|
|
1425
|
+
for (const item of this.featureStatusCache.data) {
|
|
1426
|
+
map.set(item.feature_name, Boolean(item.enabled));
|
|
1427
|
+
}
|
|
1428
|
+
return map;
|
|
1429
|
+
}
|
|
1430
|
+
const data = await this.typedApi.getFeatureStatus();
|
|
1431
|
+
this.featureStatusCache = {
|
|
1432
|
+
data,
|
|
1433
|
+
expiryMs: now + FlashnetClient.FEATURE_STATUS_TTL_MS,
|
|
1434
|
+
};
|
|
1435
|
+
const map = new Map();
|
|
1436
|
+
for (const item of data) {
|
|
1437
|
+
map.set(item.feature_name, Boolean(item.enabled));
|
|
1438
|
+
}
|
|
1439
|
+
return map;
|
|
1440
|
+
}
|
|
1441
|
+
async getEnabledMinAmountsMap() {
|
|
1442
|
+
const now = Date.now();
|
|
1443
|
+
if (this.minAmountsCache && this.minAmountsCache.expiryMs > now) {
|
|
1444
|
+
return this.minAmountsCache.map;
|
|
1445
|
+
}
|
|
1446
|
+
const config = await this.typedApi.getMinAmounts();
|
|
1447
|
+
const map = new Map();
|
|
1448
|
+
for (const item of config) {
|
|
1449
|
+
if (item.enabled) {
|
|
1450
|
+
const key = item.asset_identifier.toLowerCase();
|
|
1451
|
+
const value = BigInt(String(item.min_amount));
|
|
1452
|
+
map.set(key, value);
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
this.minAmountsCache = {
|
|
1456
|
+
map,
|
|
1457
|
+
expiryMs: now + FlashnetClient.MIN_AMOUNTS_TTL_MS,
|
|
1458
|
+
};
|
|
1459
|
+
return map;
|
|
1460
|
+
}
|
|
1461
|
+
getHexAddress(addr) {
|
|
1462
|
+
return this.toHexTokenIdentifier(addr).toLowerCase();
|
|
1463
|
+
}
|
|
1464
|
+
async assertSwapMeetsMinAmounts(params) {
|
|
1465
|
+
const minMap = await this.getEnabledMinAmountsMap();
|
|
1466
|
+
if (minMap.size === 0) {
|
|
1467
|
+
return;
|
|
1468
|
+
}
|
|
1469
|
+
const inHex = this.getHexAddress(params.assetInAddress);
|
|
1470
|
+
const outHex = this.getHexAddress(params.assetOutAddress);
|
|
1471
|
+
const minIn = minMap.get(inHex);
|
|
1472
|
+
const minOut = minMap.get(outHex);
|
|
1473
|
+
const amountIn = BigInt(String(params.amountIn));
|
|
1474
|
+
const minAmountOut = BigInt(String(params.minAmountOut));
|
|
1475
|
+
if (minIn && minOut) {
|
|
1476
|
+
if (amountIn < minIn) {
|
|
1477
|
+
throw new Error(`Minimum amount not met for input asset. Required \
|
|
1478
|
+
${minIn.toString()}, provided ${amountIn.toString()}`);
|
|
1479
|
+
}
|
|
1480
|
+
return;
|
|
1481
|
+
}
|
|
1482
|
+
if (minIn) {
|
|
1483
|
+
if (amountIn < minIn) {
|
|
1484
|
+
throw new Error(`Minimum amount not met for input asset. Required \
|
|
1485
|
+
${minIn.toString()}, provided ${amountIn.toString()}`);
|
|
1486
|
+
}
|
|
1487
|
+
return;
|
|
1488
|
+
}
|
|
1489
|
+
if (minOut) {
|
|
1490
|
+
const relaxed = minOut / 2n; // 50% relaxation for slippage
|
|
1491
|
+
if (minAmountOut < relaxed) {
|
|
1492
|
+
throw new Error(`Minimum amount not met for output asset. Required at least \
|
|
1493
|
+
${relaxed.toString()} (50% relaxed), provided minAmountOut ${minAmountOut.toString()}`);
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
async assertAddLiquidityMeetsMinAmounts(params) {
|
|
1498
|
+
const minMap = await this.getEnabledMinAmountsMap();
|
|
1499
|
+
if (minMap.size === 0) {
|
|
1500
|
+
return;
|
|
1501
|
+
}
|
|
1502
|
+
const pool = await this.getPool(params.poolId);
|
|
1503
|
+
const aHex = pool.assetAAddress.toLowerCase();
|
|
1504
|
+
const bHex = pool.assetBAddress.toLowerCase();
|
|
1505
|
+
const aMin = minMap.get(aHex);
|
|
1506
|
+
const bMin = minMap.get(bHex);
|
|
1507
|
+
if (aMin) {
|
|
1508
|
+
const aAmt = BigInt(String(params.assetAAmount));
|
|
1509
|
+
if (aAmt < aMin) {
|
|
1510
|
+
throw new Error(`Minimum amount not met for Asset A. Required ${aMin.toString()}, provided ${aAmt.toString()}`);
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
if (bMin) {
|
|
1514
|
+
const bAmt = BigInt(String(params.assetBAmount));
|
|
1515
|
+
if (bAmt < bMin) {
|
|
1516
|
+
throw new Error(`Minimum amount not met for Asset B. Required ${bMin.toString()}, provided ${bAmt.toString()}`);
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
async assertRemoveLiquidityMeetsMinAmounts(params) {
|
|
1521
|
+
const minMap = await this.getEnabledMinAmountsMap();
|
|
1522
|
+
if (minMap.size === 0) {
|
|
1523
|
+
return;
|
|
1524
|
+
}
|
|
1525
|
+
const simulation = await this.simulateRemoveLiquidity({
|
|
1526
|
+
poolId: params.poolId,
|
|
1527
|
+
providerPublicKey: this.publicKey,
|
|
1528
|
+
lpTokensToRemove: String(params.lpTokensToRemove),
|
|
1529
|
+
});
|
|
1530
|
+
const pool = await this.getPool(params.poolId);
|
|
1531
|
+
const aHex = pool.assetAAddress.toLowerCase();
|
|
1532
|
+
const bHex = pool.assetBAddress.toLowerCase();
|
|
1533
|
+
const aMin = minMap.get(aHex);
|
|
1534
|
+
const bMin = minMap.get(bHex);
|
|
1535
|
+
if (aMin) {
|
|
1536
|
+
const predictedAOut = BigInt(String(simulation.assetAAmount));
|
|
1537
|
+
const relaxedA = aMin / 2n; // apply 50% relaxation for outputs
|
|
1538
|
+
if (predictedAOut < relaxedA) {
|
|
1539
|
+
throw new Error(`Minimum amount not met for Asset A on withdrawal. Required at least ${relaxedA.toString()} (50% relaxed), predicted ${predictedAOut.toString()}`);
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
if (bMin) {
|
|
1543
|
+
const predictedBOut = BigInt(String(simulation.assetBAmount));
|
|
1544
|
+
const relaxedB = bMin / 2n;
|
|
1545
|
+
if (predictedBOut < relaxedB) {
|
|
1546
|
+
throw new Error(`Minimum amount not met for Asset B on withdrawal. Required at least ${relaxedB.toString()} (50% relaxed), predicted ${predictedBOut.toString()}`);
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
async assertAllowedAssetBForPoolCreation(assetBHex) {
|
|
1551
|
+
const now = Date.now();
|
|
1552
|
+
let allowed;
|
|
1553
|
+
if (this.allowedAssetsCache && this.allowedAssetsCache.expiryMs > now) {
|
|
1554
|
+
allowed = this.allowedAssetsCache.data;
|
|
1555
|
+
}
|
|
1556
|
+
else {
|
|
1557
|
+
allowed = await this.typedApi.getAllowedAssets();
|
|
1558
|
+
this.allowedAssetsCache = {
|
|
1559
|
+
data: allowed,
|
|
1560
|
+
expiryMs: now + FlashnetClient.ALLOWED_ASSETS_TTL_MS,
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
if (!allowed || allowed.length === 0) {
|
|
1564
|
+
// Wildcard allowance
|
|
1565
|
+
return;
|
|
1566
|
+
}
|
|
1567
|
+
const isAllowed = allowed.some((it) => it.enabled &&
|
|
1568
|
+
it.asset_identifier.toLowerCase() === assetBHex.toLowerCase());
|
|
1569
|
+
if (!isAllowed) {
|
|
1570
|
+
throw new Error(`Asset B is not allowed for pool creation: ${assetBHex}`);
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1309
1573
|
}
|
|
1310
1574
|
|
|
1311
1575
|
export { FlashnetClient };
|