@flaunch/sdk 0.8.3-beta.3 → 0.8.3-beta.4

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/index.cjs.js CHANGED
@@ -5,7 +5,6 @@ Object.defineProperty(exports, '__esModule', { value: true });
5
5
  var drift = require('@delvtech/drift');
6
6
  var viem = require('viem');
7
7
  var axios = require('axios');
8
- var v3Sdk = require('@uniswap/v3-sdk');
9
8
  var driftViem = require('@delvtech/drift-viem');
10
9
 
11
10
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
@@ -5467,6 +5466,10 @@ const Permit2Address = {
5467
5466
  [base.id]: "0x000000000022D473030F116dDEE9F6B43aC78BA3",
5468
5467
  [baseSepolia.id]: "0x000000000022D473030F116dDEE9F6B43aC78BA3",
5469
5468
  };
5469
+ const UniV4PositionManagerAddress = {
5470
+ [base.id]: "0x7C5f5A4bBd8fD63184577525326123B519429bDc",
5471
+ [baseSepolia.id]: "0x4B2C77d209D3405F41a037Ec6c77F7F5b8e2ca80",
5472
+ };
5470
5473
  const USDCETHPoolKeys = {
5471
5474
  [base.id]: {
5472
5475
  currency0: viem.zeroAddress,
@@ -11208,6 +11211,11 @@ exports.Verifier = void 0;
11208
11211
  Verifier["WHITELIST"] = "whitelist";
11209
11212
  Verifier["ZORA"] = "zora";
11210
11213
  })(exports.Verifier || (exports.Verifier = {}));
11214
+ exports.LiquidityMode = void 0;
11215
+ (function (LiquidityMode) {
11216
+ LiquidityMode["FULL_RANGE"] = "full-range";
11217
+ LiquidityMode["CONCENTRATED"] = "concentrated";
11218
+ })(exports.LiquidityMode || (exports.LiquidityMode = {}));
11211
11219
  /**
11212
11220
  * Enumeration of Permissions for TreasuryManagers. Defaults to OPEN.
11213
11221
  */
@@ -14667,6 +14675,13 @@ class ReadMemecoin {
14667
14675
  tokenURI() {
14668
14676
  return this.contract.read("tokenURI");
14669
14677
  }
14678
+ /**
14679
+ * Gets the decimals of the token
14680
+ * @returns Promise<number> - The decimals of the token
14681
+ */
14682
+ decimals() {
14683
+ return this.contract.read("decimals");
14684
+ }
14670
14685
  /**
14671
14686
  * Gets the total supply of the token
14672
14687
  * @returns Promise<bigint> - The total supply
@@ -19518,26 +19533,21 @@ class ReadWriteTokenImporter extends ReadTokenImporter {
19518
19533
  constructor(chainId, address, drift$1 = drift.createDrift()) {
19519
19534
  super(chainId, address, drift$1);
19520
19535
  }
19521
- initialize({ memecoin, creatorFeeAllocationPercent, initialMarketCapUSD, verifier, }) {
19536
+ async initialize({ memecoin, creatorFeeAllocationPercent, initialMarketCapUSD, verifier, }) {
19522
19537
  const initialMCapInUSDCWei = viem.parseUnits(initialMarketCapUSD.toString(), 6);
19523
19538
  const creatorFeeAllocationInBps = creatorFeeAllocationPercent * 100;
19524
- // if the verifier is known then use that to save on gas
19525
- if (verifier) {
19526
- const verifierAddress = this.verifierAddress(verifier);
19527
- return this.contract.write("initialize", {
19528
- _memecoin: memecoin,
19529
- _creatorFeeAllocation: creatorFeeAllocationInBps,
19530
- _initialMarketCap: initialMCapInUSDCWei,
19531
- _verifier: verifierAddress,
19532
- });
19533
- }
19534
- else {
19535
- return this.contract.write("initialize", {
19539
+ // Passing in the verifier here, as drift doesn't recognize the other initialize function without the verifier param
19540
+ let _verifier = verifier
19541
+ ? this.verifierAddress(verifier)
19542
+ : await this.contract.read("verifyMemecoin", {
19536
19543
  _memecoin: memecoin,
19537
- _creatorFeeAllocation: creatorFeeAllocationInBps,
19538
- _initialMarketCap: initialMCapInUSDCWei,
19539
19544
  });
19540
- }
19545
+ return this.contract.write("initialize", {
19546
+ _memecoin: memecoin,
19547
+ _creatorFeeAllocation: creatorFeeAllocationInBps,
19548
+ _initialMarketCap: initialMCapInUSDCWei,
19549
+ _verifier,
19550
+ });
19541
19551
  }
19542
19552
  }
19543
19553
 
@@ -24384,6 +24394,8 @@ const TickFinder = {
24384
24394
  MIN_TICK: -887220,
24385
24395
  MAX_TICK: 887220,
24386
24396
  };
24397
+ const Q96 = 2n ** 96n;
24398
+ const Q192 = 2n ** 192n;
24387
24399
  const TICK_SPACING = 60;
24388
24400
  const getPoolId = (poolKey) => {
24389
24401
  // Pack the data in the same order as Solidity struct
@@ -24406,7 +24418,7 @@ const orderPoolKey = (poolKey) => {
24406
24418
  currency1,
24407
24419
  };
24408
24420
  };
24409
- const getValidTick = ({ tick, tickSpacing, roundDown, }) => {
24421
+ const getValidTick = ({ tick, tickSpacing, roundDown = true, }) => {
24410
24422
  // If the tick is already valid, exit early
24411
24423
  if (tick % tickSpacing === 0) {
24412
24424
  return tick;
@@ -24423,6 +24435,11 @@ const getValidTick = ({ tick, tickSpacing, roundDown, }) => {
24423
24435
  }
24424
24436
  return validTick;
24425
24437
  };
24438
+ // Rounds up or down to the nearest tick
24439
+ const getNearestUsableTick = ({ tick, tickSpacing, }) => {
24440
+ const rounded = Math.round(tick / tickSpacing) * tickSpacing;
24441
+ return Math.max(TickFinder.MIN_TICK, Math.min(TickFinder.MAX_TICK, rounded));
24442
+ };
24426
24443
  const getAmount0ForLiquidity = (sqrtRatioAX96, sqrtRatioBX96, liquidity) => {
24427
24444
  let [sqrtRatioA, sqrtRatioB] = [sqrtRatioAX96, sqrtRatioBX96];
24428
24445
  if (sqrtRatioA > sqrtRatioB) {
@@ -24446,7 +24463,60 @@ const getAmount1ForLiquidity = (sqrtRatioAX96, sqrtRatioBX96, liquidity) => {
24446
24463
  return amount1;
24447
24464
  };
24448
24465
  const getSqrtPriceX96FromTick = (tick) => {
24449
- return BigInt(v3Sdk.TickMath.getSqrtRatioAtTick(tick).toString());
24466
+ const absTick = tick < 0 ? BigInt(-tick) : BigInt(tick);
24467
+ if (absTick > TickFinder.MAX_TICK) {
24468
+ throw new Error("Tick out of range");
24469
+ }
24470
+ let ratio = (absTick & 1n) !== 0n
24471
+ ? 0xfffcb933bd6fad37aa2d162d1a594001n
24472
+ : 0x100000000000000000000000000000000n;
24473
+ if ((absTick & 2n) !== 0n)
24474
+ ratio = (ratio * 0xfff97272373d413259a46990580e213an) >> 128n;
24475
+ if ((absTick & 4n) !== 0n)
24476
+ ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdccn) >> 128n;
24477
+ if ((absTick & 8n) !== 0n)
24478
+ ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0n) >> 128n;
24479
+ if ((absTick & 16n) !== 0n)
24480
+ ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644n) >> 128n;
24481
+ if ((absTick & 32n) !== 0n)
24482
+ ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0n) >> 128n;
24483
+ if ((absTick & 64n) !== 0n)
24484
+ ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861n) >> 128n;
24485
+ if ((absTick & 128n) !== 0n)
24486
+ ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053n) >> 128n;
24487
+ if ((absTick & 256n) !== 0n)
24488
+ ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4n) >> 128n;
24489
+ if ((absTick & 512n) !== 0n)
24490
+ ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54n) >> 128n;
24491
+ if ((absTick & 1024n) !== 0n)
24492
+ ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3n) >> 128n;
24493
+ if ((absTick & 2048n) !== 0n)
24494
+ ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9n) >> 128n;
24495
+ if ((absTick & 4096n) !== 0n)
24496
+ ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825n) >> 128n;
24497
+ if ((absTick & 8192n) !== 0n)
24498
+ ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5n) >> 128n;
24499
+ if ((absTick & 16384n) !== 0n)
24500
+ ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7n) >> 128n;
24501
+ if ((absTick & 32768n) !== 0n)
24502
+ ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6n) >> 128n;
24503
+ if ((absTick & 65536n) !== 0n)
24504
+ ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9n) >> 128n;
24505
+ if ((absTick & 131072n) !== 0n)
24506
+ ratio = (ratio * 0x5d6af8dedb81196699c329225ee604n) >> 128n;
24507
+ if ((absTick & 262144n) !== 0n)
24508
+ ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98n) >> 128n;
24509
+ if ((absTick & 524288n) !== 0n)
24510
+ ratio = (ratio * 0x48a170391f7dc42444e8fa2n) >> 128n;
24511
+ if (tick > 0) {
24512
+ ratio = (2n ** 256n - 1n) / ratio;
24513
+ }
24514
+ // Convert from Q128.128 to Q128.96
24515
+ const resultShifted = ratio >> 32n;
24516
+ if (ratio % (1n << 32n) === 0n) {
24517
+ return resultShifted;
24518
+ }
24519
+ return resultShifted + 1n;
24450
24520
  };
24451
24521
  const calculateUnderlyingTokenBalances = (liquidity, tickLower, tickUpper, tickCurrent) => {
24452
24522
  const sqrtPriceCurrentX96 = getSqrtPriceX96FromTick(tickCurrent);
@@ -24469,6 +24539,137 @@ const calculateUnderlyingTokenBalances = (liquidity, tickLower, tickUpper, tickC
24469
24539
  }
24470
24540
  return { amount0, amount1 };
24471
24541
  };
24542
+ // Helper function to convert price ratio to tick with decimal handling
24543
+ const priceRatioToTick = ({ priceInput, isDirection1Per0, decimals0, decimals1, spacing, shouldGetNearestUsableTick = true, }) => {
24544
+ if (!priceInput || isNaN(Number(priceInput)))
24545
+ return 0;
24546
+ const inputPrice = Number(priceInput);
24547
+ try {
24548
+ // For Uniswap v3/v4, the tick represents price as: price = 1.0001^tick
24549
+ // where price = amount1/amount0 in their raw decimal format
24550
+ let priceRatio;
24551
+ if (isDirection1Per0) {
24552
+ // Input is token1 per token0 (e.g., memecoin per flETH)
24553
+ // Convert from human-readable to raw: divide by (10^decimals0 / 10^decimals1)
24554
+ priceRatio = inputPrice / Math.pow(10, decimals0 - decimals1);
24555
+ }
24556
+ else {
24557
+ // Input is token0 per token1 (e.g., flETH per memecoin)
24558
+ // Invert to get token1 per token0, then convert to raw
24559
+ priceRatio = 1 / inputPrice / Math.pow(10, decimals0 - decimals1);
24560
+ }
24561
+ // Calculate tick: tick = log(price) / log(1.0001)
24562
+ const tick = Math.log(priceRatio) / Math.log(1.0001);
24563
+ if (shouldGetNearestUsableTick) {
24564
+ return getValidTick({
24565
+ tick: Math.round(tick),
24566
+ tickSpacing: spacing,
24567
+ });
24568
+ }
24569
+ else {
24570
+ return Math.round(tick);
24571
+ }
24572
+ }
24573
+ catch (error) {
24574
+ console.error("Error converting price to tick:", error);
24575
+ // Fallback to basic calculation
24576
+ const rawTick = Math.floor(Math.log(inputPrice) / Math.log(1.0001));
24577
+ if (shouldGetNearestUsableTick) {
24578
+ return getValidTick({
24579
+ tick: rawTick,
24580
+ tickSpacing: spacing,
24581
+ });
24582
+ }
24583
+ else {
24584
+ return rawTick;
24585
+ }
24586
+ }
24587
+ };
24588
+ /**
24589
+ * Accurate liquidity calculation using proper Uniswap v3/v4 math
24590
+ */
24591
+ const getLiquidityFromAmounts = (params) => {
24592
+ const sqrtRatioCurrentX96 = getSqrtPriceX96FromTick(params.currentTick);
24593
+ let sqrtRatioAX96 = getSqrtPriceX96FromTick(params.tickLower);
24594
+ let sqrtRatioBX96 = getSqrtPriceX96FromTick(params.tickUpper);
24595
+ if (sqrtRatioAX96 > sqrtRatioBX96) {
24596
+ [sqrtRatioAX96, sqrtRatioBX96] = [sqrtRatioBX96, sqrtRatioAX96];
24597
+ }
24598
+ if (sqrtRatioCurrentX96 <= sqrtRatioAX96) {
24599
+ return maxLiquidityForAmount0Precise(sqrtRatioAX96, sqrtRatioBX96, params.amount0);
24600
+ }
24601
+ else if (sqrtRatioCurrentX96 < sqrtRatioBX96) {
24602
+ const liquidity0 = maxLiquidityForAmount0Precise(sqrtRatioCurrentX96, sqrtRatioBX96, params.amount0);
24603
+ const liquidity1 = maxLiquidityForAmount1(sqrtRatioAX96, sqrtRatioCurrentX96, params.amount1);
24604
+ return liquidity0 < liquidity1 ? liquidity0 : liquidity1;
24605
+ }
24606
+ else {
24607
+ return maxLiquidityForAmount1(sqrtRatioAX96, sqrtRatioBX96, params.amount1);
24608
+ }
24609
+ };
24610
+ /**
24611
+ * Returns a precise maximum amount of liquidity received for a given amount of token 0 by dividing by Q64 instead of Q96 in the intermediate step,
24612
+ * and shifting the subtracted ratio left by 32 bits.
24613
+ * @param sqrtRatioAX96 The price at the lower boundary
24614
+ * @param sqrtRatioBX96 The price at the upper boundary
24615
+ * @param amount0 The token0 amount
24616
+ * @returns liquidity for amount0, precise
24617
+ */
24618
+ function maxLiquidityForAmount0Precise(sqrtRatioAX96, sqrtRatioBX96, amount0) {
24619
+ if (sqrtRatioAX96 > sqrtRatioBX96) {
24620
+ [sqrtRatioAX96, sqrtRatioBX96] = [sqrtRatioBX96, sqrtRatioAX96];
24621
+ }
24622
+ const Q96 = 2n ** 96n;
24623
+ const numerator = amount0 * sqrtRatioAX96 * sqrtRatioBX96;
24624
+ const denominator = Q96 * (sqrtRatioBX96 - sqrtRatioAX96);
24625
+ return numerator / denominator;
24626
+ }
24627
+ /**
24628
+ * Computes the maximum amount of liquidity received for a given amount of token1
24629
+ * @param sqrtRatioAX96 The price at the lower tick boundary
24630
+ * @param sqrtRatioBX96 The price at the upper tick boundary
24631
+ * @param amount1 The token1 amount
24632
+ * @returns liquidity for amount1
24633
+ */
24634
+ function maxLiquidityForAmount1(sqrtRatioAX96, sqrtRatioBX96, amount1) {
24635
+ if (sqrtRatioAX96 > sqrtRatioBX96) {
24636
+ [sqrtRatioAX96, sqrtRatioBX96] = [sqrtRatioBX96, sqrtRatioAX96];
24637
+ }
24638
+ const Q96 = 2n ** 96n;
24639
+ return (amount1 * Q96) / (sqrtRatioBX96 - sqrtRatioAX96);
24640
+ }
24641
+ /**
24642
+ * Calculate the actual amounts needed for a given liquidity
24643
+ * This prevents MaximumAmountExceeded errors by ensuring we provide sufficient maximums
24644
+ */
24645
+ const getAmountsForLiquidity = (params) => {
24646
+ // Handle zero liquidity case
24647
+ if (params.liquidity === 0n) {
24648
+ return { amount0: 0n, amount1: 0n };
24649
+ }
24650
+ const sqrtRatioCurrentX96 = getSqrtPriceX96FromTick(params.currentTick);
24651
+ let sqrtRatioAX96 = getSqrtPriceX96FromTick(params.tickLower);
24652
+ let sqrtRatioBX96 = getSqrtPriceX96FromTick(params.tickUpper);
24653
+ if (sqrtRatioAX96 > sqrtRatioBX96) {
24654
+ [sqrtRatioAX96, sqrtRatioBX96] = [sqrtRatioBX96, sqrtRatioAX96];
24655
+ }
24656
+ let amount0 = 0n;
24657
+ let amount1 = 0n;
24658
+ if (sqrtRatioCurrentX96 <= sqrtRatioAX96) {
24659
+ // Current price is below the range, only token0 needed
24660
+ amount0 = getAmount0ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, params.liquidity);
24661
+ }
24662
+ else if (sqrtRatioCurrentX96 < sqrtRatioBX96) {
24663
+ // Current price is within the range, need both tokens
24664
+ amount0 = getAmount0ForLiquidity(sqrtRatioCurrentX96, sqrtRatioBX96, params.liquidity);
24665
+ amount1 = getAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioCurrentX96, params.liquidity);
24666
+ }
24667
+ else {
24668
+ // Current price is above the range, only token1 needed
24669
+ amount1 = getAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, params.liquidity);
24670
+ }
24671
+ return { amount0, amount1 };
24672
+ };
24472
24673
 
24473
24674
  const FastFlaunchZapAbi = [
24474
24675
  {
@@ -24533,6 +24734,58 @@ const FastFlaunchZapAbi = [
24533
24734
  },
24534
24735
  ];
24535
24736
 
24737
+ const FLETHAbi = [
24738
+ {
24739
+ inputs: [{ internalType: "uint256", name: "wethAmount", type: "uint256" }],
24740
+ name: "deposit",
24741
+ outputs: [],
24742
+ stateMutability: "payable",
24743
+ type: "function",
24744
+ },
24745
+ {
24746
+ inputs: [{ internalType: "uint256", name: "amount", type: "uint256" }],
24747
+ name: "withdraw",
24748
+ outputs: [],
24749
+ stateMutability: "nonpayable",
24750
+ type: "function",
24751
+ },
24752
+ {
24753
+ inputs: [{ internalType: "address", name: "account", type: "address" }],
24754
+ name: "balanceOf",
24755
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
24756
+ stateMutability: "view",
24757
+ type: "function",
24758
+ },
24759
+ {
24760
+ inputs: [],
24761
+ name: "totalSupply",
24762
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
24763
+ stateMutability: "view",
24764
+ type: "function",
24765
+ },
24766
+ {
24767
+ inputs: [],
24768
+ name: "name",
24769
+ outputs: [{ internalType: "string", name: "", type: "string" }],
24770
+ stateMutability: "view",
24771
+ type: "function",
24772
+ },
24773
+ {
24774
+ inputs: [],
24775
+ name: "symbol",
24776
+ outputs: [{ internalType: "string", name: "", type: "string" }],
24777
+ stateMutability: "view",
24778
+ type: "function",
24779
+ },
24780
+ {
24781
+ inputs: [],
24782
+ name: "decimals",
24783
+ outputs: [{ internalType: "uint8", name: "", type: "uint8" }],
24784
+ stateMutability: "view",
24785
+ type: "function",
24786
+ },
24787
+ ];
24788
+
24536
24789
  /**
24537
24790
  * Base class for interacting with Flaunch protocol in read-only mode
24538
24791
  */
@@ -25336,6 +25589,177 @@ class ReadFlaunchSDK {
25336
25589
  tokenImporterVerifyMemecoin(memecoin) {
25337
25590
  return this.readTokenImporter.verifyMemecoin(memecoin);
25338
25591
  }
25592
+ async calculateAddLiquidityTicks({ coinAddress, liquidityMode, minMarketCap, maxMarketCap, }) {
25593
+ const memecoin = new ReadMemecoin(coinAddress, this.drift);
25594
+ const coinTotalSupply = await memecoin.totalSupply();
25595
+ const coinDecimals = await memecoin.decimals();
25596
+ if (liquidityMode === exports.LiquidityMode.FULL_RANGE) {
25597
+ return {
25598
+ tickLower: getNearestUsableTick({
25599
+ tick: TickFinder.MIN_TICK,
25600
+ tickSpacing: TICK_SPACING,
25601
+ }),
25602
+ tickUpper: getNearestUsableTick({
25603
+ tick: TickFinder.MAX_TICK,
25604
+ tickSpacing: TICK_SPACING,
25605
+ }),
25606
+ coinTotalSupply,
25607
+ coinDecimals,
25608
+ };
25609
+ }
25610
+ else {
25611
+ const ethUsdPrice = await this.getETHUSDCPrice();
25612
+ const isFlethZero = this.flETHIsCurrencyZero(coinAddress);
25613
+ const minMarketCapNum = parseFloat(minMarketCap);
25614
+ const maxMarketCapNum = parseFloat(maxMarketCap);
25615
+ if (minMarketCapNum <= 0 ||
25616
+ maxMarketCapNum <= 0 ||
25617
+ minMarketCapNum >= maxMarketCapNum) {
25618
+ throw new Error("[ReadFlaunchSDK.addLiquidityCalculateTicks]: Invalid market cap range");
25619
+ }
25620
+ // Convert total supply to decimal format
25621
+ const totalSupplyDecimal = parseFloat(viem.formatEther(coinTotalSupply));
25622
+ // Calculate token price in USD at min and max market caps
25623
+ const minTokenPriceUsd = minMarketCapNum / totalSupplyDecimal;
25624
+ const maxTokenPriceUsd = maxMarketCapNum / totalSupplyDecimal;
25625
+ // Convert to token price in ETH
25626
+ const minTokenPriceEth = minTokenPriceUsd / ethUsdPrice;
25627
+ const maxTokenPriceEth = maxTokenPriceUsd / ethUsdPrice;
25628
+ const flETHDecimals = 18; // flETH has 18 decimals
25629
+ // Determine decimals based on token ordering
25630
+ const decimals0 = isFlethZero ? flETHDecimals : coinDecimals;
25631
+ const decimals1 = isFlethZero ? coinDecimals : flETHDecimals;
25632
+ // Convert to ticks using proper price direction handling
25633
+ const minTick = priceRatioToTick({
25634
+ priceInput: minTokenPriceEth.toString(),
25635
+ isDirection1Per0: !isFlethZero,
25636
+ decimals0,
25637
+ decimals1,
25638
+ spacing: TICK_SPACING,
25639
+ });
25640
+ const maxTick = priceRatioToTick({
25641
+ priceInput: maxTokenPriceEth.toString(),
25642
+ isDirection1Per0: !isFlethZero,
25643
+ decimals0,
25644
+ decimals1,
25645
+ spacing: TICK_SPACING,
25646
+ });
25647
+ return {
25648
+ tickLower: Math.min(minTick, maxTick),
25649
+ tickUpper: Math.max(minTick, maxTick),
25650
+ coinTotalSupply,
25651
+ coinDecimals,
25652
+ };
25653
+ }
25654
+ }
25655
+ async calculateAddLiquidityAmounts({ coinAddress, liquidityMode, coinOrEthInputAmount, inputToken, minMarketCap, maxMarketCap, }) {
25656
+ const { tickLower, tickUpper } = await this.calculateAddLiquidityTicks({
25657
+ coinAddress,
25658
+ liquidityMode,
25659
+ minMarketCap,
25660
+ maxMarketCap,
25661
+ });
25662
+ // get the current pool state for AnyPositionManager pool for the coin
25663
+ const poolState = await this.readStateView.poolSlot0({
25664
+ poolId: getPoolId(orderPoolKey({
25665
+ currency0: coinAddress,
25666
+ currency1: FLETHAddress[this.chainId],
25667
+ fee: 0,
25668
+ tickSpacing: TICK_SPACING,
25669
+ hooks: AnyPositionManagerAddress[this.chainId],
25670
+ })),
25671
+ });
25672
+ const currentTick = poolState.tick;
25673
+ // Determine currency ordering
25674
+ const isFlETHCurrency0 = this.flETHIsCurrencyZero(coinAddress);
25675
+ try {
25676
+ const sqrtRatioCurrentX96 = getSqrtPriceX96FromTick(currentTick);
25677
+ let sqrtRatioLowerX96 = getSqrtPriceX96FromTick(tickLower);
25678
+ let sqrtRatioUpperX96 = getSqrtPriceX96FromTick(tickUpper);
25679
+ if (sqrtRatioLowerX96 > sqrtRatioUpperX96) {
25680
+ [sqrtRatioLowerX96, sqrtRatioUpperX96] = [
25681
+ sqrtRatioUpperX96,
25682
+ sqrtRatioLowerX96,
25683
+ ];
25684
+ }
25685
+ let amount0Calculated;
25686
+ let amount1Calculated;
25687
+ // Determine which calculation to use based on input token and currency ordering
25688
+ const isCoinInput = inputToken === "coin";
25689
+ const inputAmount = coinOrEthInputAmount;
25690
+ if ((isCoinInput && !isFlETHCurrency0) || // coin input and coin is currency0
25691
+ (!isCoinInput && isFlETHCurrency0) // eth input and flETH is currency0
25692
+ ) {
25693
+ // We have amount0 and need to calculate amount1
25694
+ amount0Calculated = inputAmount;
25695
+ if (sqrtRatioCurrentX96 <= sqrtRatioLowerX96) {
25696
+ // Current price below range - no currency1 needed
25697
+ amount1Calculated = 0n;
25698
+ }
25699
+ else if (sqrtRatioCurrentX96 >= sqrtRatioUpperX96) {
25700
+ // Current price above range - proportional amount1 needed
25701
+ const ratio = (sqrtRatioUpperX96 * sqrtRatioUpperX96) / Q96;
25702
+ amount1Calculated = (inputAmount * ratio) / Q96;
25703
+ }
25704
+ else {
25705
+ // Current price in range - proportional amounts
25706
+ const intermediate1 = (sqrtRatioUpperX96 *
25707
+ sqrtRatioCurrentX96 *
25708
+ (sqrtRatioCurrentX96 - sqrtRatioLowerX96)) /
25709
+ Q96;
25710
+ const intermediate2 = (Q192 * (sqrtRatioUpperX96 - sqrtRatioCurrentX96)) / Q96;
25711
+ if (intermediate2 > 0n) {
25712
+ amount1Calculated = (inputAmount * intermediate1) / intermediate2;
25713
+ }
25714
+ else {
25715
+ amount1Calculated = 0n;
25716
+ }
25717
+ }
25718
+ }
25719
+ else {
25720
+ // We have amount1 and need to calculate amount0
25721
+ amount1Calculated = inputAmount;
25722
+ if (sqrtRatioCurrentX96 <= sqrtRatioLowerX96) {
25723
+ // Current price below range - proportional amount0 needed
25724
+ const ratio = (sqrtRatioLowerX96 * sqrtRatioLowerX96) / Q96;
25725
+ amount0Calculated = (inputAmount * Q96) / ratio;
25726
+ }
25727
+ else if (sqrtRatioCurrentX96 >= sqrtRatioUpperX96) {
25728
+ // Current price above range - no amount0 needed
25729
+ amount0Calculated = 0n;
25730
+ }
25731
+ else {
25732
+ // Current price in range - proportional amounts
25733
+ const intermediate1 = (sqrtRatioUpperX96 *
25734
+ sqrtRatioCurrentX96 *
25735
+ (sqrtRatioCurrentX96 - sqrtRatioLowerX96)) /
25736
+ Q96;
25737
+ const intermediate2 = (Q192 * (sqrtRatioUpperX96 - sqrtRatioCurrentX96)) / Q96;
25738
+ if (intermediate1 > 0n) {
25739
+ amount0Calculated = (inputAmount * intermediate2) / intermediate1;
25740
+ }
25741
+ else {
25742
+ amount0Calculated = 0n;
25743
+ }
25744
+ }
25745
+ }
25746
+ // Map amount0/amount1 back to coin/eth amounts based on currency ordering
25747
+ const [ethAmount, coinAmount] = isFlETHCurrency0
25748
+ ? [amount0Calculated, amount1Calculated]
25749
+ : [amount1Calculated, amount0Calculated];
25750
+ return {
25751
+ coinAmount,
25752
+ ethAmount,
25753
+ tickLower,
25754
+ tickUpper,
25755
+ currentTick,
25756
+ };
25757
+ }
25758
+ catch (error) {
25759
+ console.error("Error calculating liquidity amounts:", error);
25760
+ throw error;
25761
+ }
25762
+ }
25339
25763
  /**
25340
25764
  * Checks if an operator is approved for all flaunch tokens of an owner
25341
25765
  * @param version - The flaunch version to determine the correct contract address
@@ -25715,6 +26139,343 @@ class ReadWriteFlaunchSDK extends ReadFlaunchSDK {
25715
26139
  importMemecoin(params) {
25716
26140
  return this.readWriteTokenImporter.initialize(params);
25717
26141
  }
26142
+ async getAddLiquidityCalls(params) {
26143
+ const { coinAddress } = params;
26144
+ const flethAddress = FLETHAddress[this.chainId];
26145
+ let coinAmount;
26146
+ let flethAmount;
26147
+ let tickLower;
26148
+ let tickUpper;
26149
+ let currentTick;
26150
+ const poolKey = orderPoolKey({
26151
+ currency0: coinAddress,
26152
+ currency1: flethAddress,
26153
+ fee: 0,
26154
+ tickSpacing: this.TICK_SPACING,
26155
+ hooks: AnyPositionManagerAddress[this.chainId],
26156
+ });
26157
+ // Check if we need to calculate values or use direct values
26158
+ if ("tickLower" in params) {
26159
+ // Use the directly provided values
26160
+ coinAmount = params.coinAmount;
26161
+ flethAmount = params.flethAmount;
26162
+ tickLower = params.tickLower;
26163
+ tickUpper = params.tickUpper;
26164
+ const poolState = await this.readStateView.poolSlot0({
26165
+ poolId: getPoolId(poolKey),
26166
+ });
26167
+ currentTick = poolState.tick;
26168
+ }
26169
+ else {
26170
+ // Calculate the amounts
26171
+ const calculated = await this.calculateAddLiquidityAmounts({
26172
+ coinAddress,
26173
+ liquidityMode: params.liquidityMode,
26174
+ coinOrEthInputAmount: params.coinOrEthInputAmount,
26175
+ inputToken: params.inputToken,
26176
+ minMarketCap: params.minMarketCap,
26177
+ maxMarketCap: params.maxMarketCap,
26178
+ });
26179
+ coinAmount = calculated.coinAmount;
26180
+ flethAmount = calculated.ethAmount;
26181
+ tickLower = calculated.tickLower;
26182
+ tickUpper = calculated.tickUpper;
26183
+ currentTick = calculated.currentTick;
26184
+ }
26185
+ // Fetch approvals via multicall
26186
+ const userAddress = await this.drift.getSignerAddress();
26187
+ const permit2Address = Permit2Address[this.chainId];
26188
+ const results = await this.drift.multicall({
26189
+ calls: [
26190
+ // coin -> permit2
26191
+ {
26192
+ address: coinAddress,
26193
+ abi: viem.erc20Abi,
26194
+ fn: "allowance",
26195
+ args: {
26196
+ owner: userAddress,
26197
+ spender: permit2Address,
26198
+ },
26199
+ },
26200
+ // flETH -> permit2
26201
+ {
26202
+ address: flethAddress,
26203
+ abi: viem.erc20Abi,
26204
+ fn: "allowance",
26205
+ args: {
26206
+ owner: userAddress,
26207
+ spender: permit2Address,
26208
+ },
26209
+ },
26210
+ // coin --permit2--> uni position manager
26211
+ {
26212
+ address: permit2Address,
26213
+ abi: Permit2Abi,
26214
+ fn: "allowance",
26215
+ args: {
26216
+ 0: userAddress,
26217
+ 1: coinAddress,
26218
+ 2: UniV4PositionManagerAddress[this.chainId],
26219
+ },
26220
+ },
26221
+ // flETH --permit2--> uni position manager
26222
+ {
26223
+ address: permit2Address,
26224
+ abi: Permit2Abi,
26225
+ fn: "allowance",
26226
+ args: {
26227
+ 0: userAddress,
26228
+ 1: flethAddress,
26229
+ 2: UniV4PositionManagerAddress[this.chainId],
26230
+ },
26231
+ },
26232
+ // coin symbol
26233
+ {
26234
+ address: coinAddress,
26235
+ abi: viem.erc20Abi,
26236
+ fn: "symbol",
26237
+ },
26238
+ ],
26239
+ });
26240
+ const coinToPermit2 = results[0].value;
26241
+ const flethToPermit2 = results[1].value;
26242
+ const permit2ToUniPosManagerCoinAllowance = results[2].value;
26243
+ const permit2ToUniPosManagerFlethAllowance = results[3].value;
26244
+ const coinSymbol = results[4].value;
26245
+ const needsCoinApproval = coinToPermit2 < coinAmount;
26246
+ const needsFlethApproval = flethToPermit2 < flethAmount;
26247
+ const currentTime = Math.floor(Date.now() / 1000);
26248
+ const needsCoinPermit2Approval = permit2ToUniPosManagerCoinAllowance.amount < coinAmount ||
26249
+ permit2ToUniPosManagerCoinAllowance.expiration <= currentTime;
26250
+ const needsFlethPermit2Approval = flethAmount > 0n &&
26251
+ (permit2ToUniPosManagerFlethAllowance.amount < flethAmount ||
26252
+ permit2ToUniPosManagerFlethAllowance.expiration <= currentTime);
26253
+ const calls = [];
26254
+ // 1. Coin approval to Permit2
26255
+ if (needsCoinApproval) {
26256
+ calls.push({
26257
+ to: coinAddress,
26258
+ description: `Approve ${coinSymbol} for Permit2`,
26259
+ data: viem.encodeFunctionData({
26260
+ abi: viem.erc20Abi,
26261
+ functionName: "approve",
26262
+ args: [permit2Address, coinAmount],
26263
+ }),
26264
+ });
26265
+ }
26266
+ // 2. flETH approval to Permit2 (after wrapping)
26267
+ if (needsFlethApproval) {
26268
+ calls.push({
26269
+ to: flethAddress,
26270
+ description: `Approve flETH for Permit2`,
26271
+ data: viem.encodeFunctionData({
26272
+ abi: viem.erc20Abi,
26273
+ functionName: "approve",
26274
+ args: [permit2Address, flethAmount],
26275
+ }),
26276
+ });
26277
+ }
26278
+ // 3. Permit2 approval for coin to uni position manager
26279
+ const expiration = Math.floor(Date.now() / 1000) + 3600; // 1 hour from now
26280
+ if (needsCoinPermit2Approval) {
26281
+ calls.push({
26282
+ to: permit2Address,
26283
+ description: `Permit2 approval for ${coinSymbol} to UniV4PositionManager`,
26284
+ data: viem.encodeFunctionData({
26285
+ abi: Permit2Abi,
26286
+ functionName: "approve",
26287
+ args: [
26288
+ coinAddress,
26289
+ UniV4PositionManagerAddress[this.chainId],
26290
+ coinAmount,
26291
+ expiration,
26292
+ ],
26293
+ }),
26294
+ });
26295
+ }
26296
+ // 4. Permit2 approval for flETH to uni position manager
26297
+ if (needsFlethPermit2Approval) {
26298
+ calls.push({
26299
+ to: permit2Address,
26300
+ description: `Permit2 approval for flETH to UniV4PositionManager`,
26301
+ data: viem.encodeFunctionData({
26302
+ abi: Permit2Abi,
26303
+ functionName: "approve",
26304
+ args: [
26305
+ flethAddress,
26306
+ UniV4PositionManagerAddress[this.chainId],
26307
+ flethAmount,
26308
+ expiration,
26309
+ ],
26310
+ }),
26311
+ });
26312
+ }
26313
+ // 5. Wrap ETH to flETH
26314
+ if (flethAmount > 0n) {
26315
+ calls.push({
26316
+ to: flethAddress,
26317
+ description: "Wrap ETH to flETH",
26318
+ data: viem.encodeFunctionData({
26319
+ abi: FLETHAbi,
26320
+ functionName: "deposit",
26321
+ args: [0n], // wethAmount = 0, we're only sending ETH
26322
+ }),
26323
+ value: flethAmount,
26324
+ });
26325
+ }
26326
+ // === generate add liquidity call ===
26327
+ // Determine amounts for each currency based on pool key ordering
26328
+ const amount0 = poolKey.currency0 === coinAddress ? coinAmount : flethAmount;
26329
+ const amount1 = poolKey.currency0 === coinAddress ? flethAmount : coinAmount;
26330
+ // Calculate liquidity first using user's input amounts
26331
+ const initialLiquidity = getLiquidityFromAmounts({
26332
+ currentTick,
26333
+ tickLower,
26334
+ tickUpper,
26335
+ amount0,
26336
+ amount1,
26337
+ });
26338
+ // Calculate the actual amounts needed for this liquidity
26339
+ const actualAmounts = getAmountsForLiquidity({
26340
+ currentTick,
26341
+ tickLower,
26342
+ tickUpper,
26343
+ liquidity: initialLiquidity,
26344
+ });
26345
+ // Check if actual amounts exceed user input - if so, constrain them
26346
+ let finalLiquidity = initialLiquidity;
26347
+ let finalAmount0 = actualAmounts.amount0;
26348
+ let finalAmount1 = actualAmounts.amount1;
26349
+ // If actual amounts exceed user input, we need to recalculate with constraints
26350
+ if (actualAmounts.amount0 > amount0 || actualAmounts.amount1 > amount1) {
26351
+ console.log("Actual amounts exceed user input, constraining...");
26352
+ // Calculate liquidity constrained by each amount separately
26353
+ const liquidity0Constrained = getLiquidityFromAmounts({
26354
+ currentTick,
26355
+ tickLower,
26356
+ tickUpper,
26357
+ amount0,
26358
+ amount1: 0n, // Only constrain by amount0
26359
+ });
26360
+ const liquidity1Constrained = getLiquidityFromAmounts({
26361
+ currentTick,
26362
+ tickLower,
26363
+ tickUpper,
26364
+ amount0: 0n, // Only constrain by amount1
26365
+ amount1,
26366
+ });
26367
+ // Use the smaller liquidity to ensure we don't exceed either amount
26368
+ finalLiquidity =
26369
+ liquidity0Constrained < liquidity1Constrained
26370
+ ? liquidity0Constrained
26371
+ : liquidity1Constrained;
26372
+ // Recalculate amounts for the constrained liquidity
26373
+ const constrainedAmounts = getAmountsForLiquidity({
26374
+ currentTick,
26375
+ tickLower,
26376
+ tickUpper,
26377
+ liquidity: finalLiquidity,
26378
+ });
26379
+ finalAmount0 = constrainedAmounts.amount0;
26380
+ finalAmount1 = constrainedAmounts.amount1;
26381
+ }
26382
+ // IMPORTANT: Add conservative buffer to account for contract rounding differences
26383
+ // Reduce liquidity by 0.01% to ensure contract calculations stay within user bounds
26384
+ const liquidityBuffer = finalLiquidity / 10000n; // 0.01%
26385
+ const conservativeLiquidity = finalLiquidity - (liquidityBuffer > 1n ? liquidityBuffer : 1n);
26386
+ // Use conservative liquidity but keep user's original amounts as maximums
26387
+ // The conservative liquidity ensures the contract won't need more than user provided
26388
+ if (currentTick !== undefined) {
26389
+ // If pool is already initialized then use conservative liquidity
26390
+ // as a new pool would accept any liquidity amounts given by us
26391
+ finalLiquidity = conservativeLiquidity;
26392
+ }
26393
+ finalAmount0 = amount0; // Use user's full amount as maximum
26394
+ finalAmount1 = amount1; // Use user's full amount as maximum
26395
+ // Prepare mint position parameters (following swiss-knife pattern)
26396
+ const V4PMActions = {
26397
+ MINT_POSITION: "02",
26398
+ SETTLE_PAIR: "0d",
26399
+ };
26400
+ const v4Actions = ("0x" +
26401
+ V4PMActions.MINT_POSITION +
26402
+ V4PMActions.SETTLE_PAIR);
26403
+ // Validate hookData format
26404
+ const validHookData = "0x"; // Empty hook data for now
26405
+ const UniV4PM_MintPositionAbi = [
26406
+ {
26407
+ type: "tuple",
26408
+ components: [
26409
+ { type: "address", name: "currency0" },
26410
+ { type: "address", name: "currency1" },
26411
+ { type: "uint24", name: "fee" },
26412
+ { type: "int24", name: "tickSpacing" },
26413
+ { type: "address", name: "hooks" },
26414
+ ],
26415
+ },
26416
+ { type: "int24", name: "tickLower" },
26417
+ { type: "int24", name: "tickUpper" },
26418
+ { type: "uint256", name: "liquidity" },
26419
+ { type: "uint128", name: "amount0Max" },
26420
+ { type: "uint128", name: "amount1Max" },
26421
+ { type: "address", name: "owner" },
26422
+ { type: "bytes", name: "hookData" },
26423
+ ];
26424
+ const UniV4PM_SettlePairAbi = [
26425
+ {
26426
+ type: "tuple",
26427
+ components: [
26428
+ { type: "address", name: "currency0" },
26429
+ { type: "address", name: "currency1" },
26430
+ ],
26431
+ },
26432
+ ];
26433
+ const mintPositionParams = viem.encodeAbiParameters(UniV4PM_MintPositionAbi, [
26434
+ poolKey,
26435
+ tickLower,
26436
+ tickUpper,
26437
+ finalLiquidity,
26438
+ finalAmount0,
26439
+ finalAmount1,
26440
+ userAddress,
26441
+ validHookData,
26442
+ ]);
26443
+ const settlePairParams = viem.encodeAbiParameters(UniV4PM_SettlePairAbi, [
26444
+ {
26445
+ currency0: poolKey.currency0,
26446
+ currency1: poolKey.currency1,
26447
+ },
26448
+ ]);
26449
+ // 6. Add liquidity
26450
+ calls.push({
26451
+ to: UniV4PositionManagerAddress[this.chainId],
26452
+ data: viem.encodeFunctionData({
26453
+ abi: [
26454
+ {
26455
+ inputs: [
26456
+ { internalType: "bytes", name: "unlockData", type: "bytes" },
26457
+ { internalType: "uint256", name: "deadline", type: "uint256" },
26458
+ ],
26459
+ name: "modifyLiquidities",
26460
+ outputs: [],
26461
+ stateMutability: "payable",
26462
+ type: "function",
26463
+ },
26464
+ ],
26465
+ functionName: "modifyLiquidities",
26466
+ args: [
26467
+ viem.encodeAbiParameters([
26468
+ { type: "bytes", name: "actions" },
26469
+ { type: "bytes[]", name: "params" },
26470
+ ], [v4Actions, [mintPositionParams, settlePairParams]]),
26471
+ BigInt(Math.floor(Date.now() / 1000) + 3600), // 1 hour deadline
26472
+ ],
26473
+ }),
26474
+ value: 0n,
26475
+ description: "Add Liquidity",
26476
+ });
26477
+ return calls;
26478
+ }
25718
26479
  }
25719
26480
 
25720
26481
  /**
@@ -25804,6 +26565,8 @@ exports.Permit2Abi = Permit2Abi;
25804
26565
  exports.Permit2Address = Permit2Address;
25805
26566
  exports.PoolManagerAbi = PoolManagerAbi;
25806
26567
  exports.PoolManagerAddress = PoolManagerAddress;
26568
+ exports.Q192 = Q192;
26569
+ exports.Q96 = Q96;
25807
26570
  exports.QuoterAbi = QuoterAbi;
25808
26571
  exports.QuoterAddress = QuoterAddress;
25809
26572
  exports.ReadFlaunchSDK = ReadFlaunchSDK;
@@ -25821,6 +26584,7 @@ exports.TokenImporterAddress = TokenImporterAddress;
25821
26584
  exports.TreasuryManagerFactoryAbi = TreasuryManagerFactoryAbi;
25822
26585
  exports.TreasuryManagerFactoryAddress = TreasuryManagerFactoryAddress;
25823
26586
  exports.USDCETHPoolKeys = USDCETHPoolKeys;
26587
+ exports.UniV4PositionManagerAddress = UniV4PositionManagerAddress;
25824
26588
  exports.UniversalRouterAbi = UniversalRouterAbi;
25825
26589
  exports.UniversalRouterAddress = UniversalRouterAddress;
25826
26590
  exports.VirtualsVerifierAddress = VirtualsVerifierAddress;
@@ -25835,14 +26599,20 @@ exports.createFlaunch = createFlaunch;
25835
26599
  exports.ethToMemecoin = ethToMemecoin;
25836
26600
  exports.generateTokenUri = generateTokenUri;
25837
26601
  exports.getAmountWithSlippage = getAmountWithSlippage;
26602
+ exports.getAmountsForLiquidity = getAmountsForLiquidity;
26603
+ exports.getLiquidityFromAmounts = getLiquidityFromAmounts;
26604
+ exports.getNearestUsableTick = getNearestUsableTick;
25838
26605
  exports.getPermissionsAddress = getPermissionsAddress;
25839
26606
  exports.getPermit2TypedData = getPermit2TypedData;
25840
26607
  exports.getPoolId = getPoolId;
25841
26608
  exports.getSqrtPriceX96FromTick = getSqrtPriceX96FromTick;
25842
26609
  exports.getValidTick = getValidTick;
26610
+ exports.maxLiquidityForAmount0Precise = maxLiquidityForAmount0Precise;
26611
+ exports.maxLiquidityForAmount1 = maxLiquidityForAmount1;
25843
26612
  exports.memecoinToEthWithPermit2 = memecoinToEthWithPermit2;
25844
26613
  exports.orderPoolKey = orderPoolKey;
25845
26614
  exports.parseSwapData = parseSwapData;
26615
+ exports.priceRatioToTick = priceRatioToTick;
25846
26616
  exports.resolveIPFS = resolveIPFS;
25847
26617
  exports.uint256ToBytes32 = uint256ToBytes32;
25848
26618
  exports.uploadFileToIPFS = uploadFileToIPFS;