@strobelabs/perpcity-sdk 0.4.3 → 0.5.1

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.mjs CHANGED
@@ -576,26 +576,19 @@ var PERP_MANAGER_ABI = [
576
576
  anonymous: false,
577
577
  inputs: [
578
578
  {
579
- indexed: false,
580
- internalType: "contract IFees",
581
- name: "feesModule",
582
- type: "address"
583
- }
584
- ],
585
- name: "FeesModuleRegistered",
586
- type: "event"
587
- },
588
- {
589
- anonymous: false,
590
- inputs: [
579
+ indexed: true,
580
+ internalType: "uint8",
581
+ name: "moduleType",
582
+ type: "uint8"
583
+ },
591
584
  {
592
- indexed: false,
593
- internalType: "contract ILockupPeriod",
594
- name: "lockupPeriodModule",
585
+ indexed: true,
586
+ internalType: "address",
587
+ name: "module",
595
588
  type: "address"
596
589
  }
597
590
  ],
598
- name: "LockupPeriodModuleRegistered",
591
+ name: "ModuleRegistered",
599
592
  type: "event"
600
593
  },
601
594
  {
@@ -623,19 +616,6 @@ var PERP_MANAGER_ABI = [
623
616
  name: "MarginAdjusted",
624
617
  type: "event"
625
618
  },
626
- {
627
- anonymous: false,
628
- inputs: [
629
- {
630
- indexed: false,
631
- internalType: "contract IMarginRatios",
632
- name: "marginRatiosModule",
633
- type: "address"
634
- }
635
- ],
636
- name: "MarginRatiosModuleRegistered",
637
- type: "event"
638
- },
639
619
  {
640
620
  anonymous: false,
641
621
  inputs: [
@@ -809,13 +789,13 @@ var PERP_MANAGER_ABI = [
809
789
  {
810
790
  indexed: false,
811
791
  internalType: "int256",
812
- name: "perpDelta",
792
+ name: "exitPerpDelta",
813
793
  type: "int256"
814
794
  },
815
795
  {
816
796
  indexed: false,
817
797
  internalType: "int256",
818
- name: "usdDelta",
798
+ name: "exitUsdDelta",
819
799
  type: "int256"
820
800
  },
821
801
  {
@@ -829,6 +809,42 @@ var PERP_MANAGER_ABI = [
829
809
  internalType: "int24",
830
810
  name: "tickUpper",
831
811
  type: "int24"
812
+ },
813
+ {
814
+ indexed: false,
815
+ internalType: "int256",
816
+ name: "netUsdDelta",
817
+ type: "int256"
818
+ },
819
+ {
820
+ indexed: false,
821
+ internalType: "int256",
822
+ name: "funding",
823
+ type: "int256"
824
+ },
825
+ {
826
+ indexed: false,
827
+ internalType: "uint256",
828
+ name: "utilizationFee",
829
+ type: "uint256"
830
+ },
831
+ {
832
+ indexed: false,
833
+ internalType: "uint256",
834
+ name: "adl",
835
+ type: "uint256"
836
+ },
837
+ {
838
+ indexed: false,
839
+ internalType: "uint256",
840
+ name: "liquidationFee",
841
+ type: "uint256"
842
+ },
843
+ {
844
+ indexed: false,
845
+ internalType: "int256",
846
+ name: "netMargin",
847
+ type: "int256"
832
848
  }
833
849
  ],
834
850
  name: "PositionClosed",
@@ -901,19 +917,6 @@ var PERP_MANAGER_ABI = [
901
917
  name: "PositionOpened",
902
918
  type: "event"
903
919
  },
904
- {
905
- anonymous: false,
906
- inputs: [
907
- {
908
- indexed: false,
909
- internalType: "contract ISqrtPriceImpactLimit",
910
- name: "sqrtPriceImpactLimitModule",
911
- type: "address"
912
- }
913
- ],
914
- name: "SqrtPriceImpactLimitModuleRegistered",
915
- type: "event"
916
- },
917
920
  {
918
921
  anonymous: false,
919
922
  inputs: [
@@ -1040,12 +1043,12 @@ var PERP_MANAGER_ABI = [
1040
1043
  },
1041
1044
  {
1042
1045
  internalType: "int256",
1043
- name: "perpDelta",
1046
+ name: "usdDelta",
1044
1047
  type: "int256"
1045
1048
  },
1046
1049
  {
1047
1050
  internalType: "uint128",
1048
- name: "usdLimit",
1051
+ name: "perpLimit",
1049
1052
  type: "uint128"
1050
1053
  }
1051
1054
  ],
@@ -1292,11 +1295,6 @@ var PERP_MANAGER_ABI = [
1292
1295
  internalType: "contract ISqrtPriceImpactLimit",
1293
1296
  name: "sqrtPriceImpactLimit",
1294
1297
  type: "address"
1295
- },
1296
- {
1297
- internalType: "uint160",
1298
- name: "startingSqrtPriceX96",
1299
- type: "uint160"
1300
1298
  }
1301
1299
  ],
1302
1300
  internalType: "struct IPerpManager.CreatePerpParams",
@@ -1417,69 +1415,17 @@ var PERP_MANAGER_ABI = [
1417
1415
  {
1418
1416
  inputs: [
1419
1417
  {
1420
- internalType: "contract IFees",
1421
- name: "",
1422
- type: "address"
1423
- }
1424
- ],
1425
- name: "isFeesRegistered",
1426
- outputs: [
1427
- {
1428
- internalType: "bool",
1429
- name: "",
1430
- type: "bool"
1431
- }
1432
- ],
1433
- stateMutability: "view",
1434
- type: "function"
1435
- },
1436
- {
1437
- inputs: [
1438
- {
1439
- internalType: "contract ILockupPeriod",
1440
- name: "",
1441
- type: "address"
1442
- }
1443
- ],
1444
- name: "isLockupPeriodRegistered",
1445
- outputs: [
1446
- {
1447
- internalType: "bool",
1448
- name: "",
1449
- type: "bool"
1450
- }
1451
- ],
1452
- stateMutability: "view",
1453
- type: "function"
1454
- },
1455
- {
1456
- inputs: [
1457
- {
1458
- internalType: "contract IMarginRatios",
1459
- name: "",
1460
- type: "address"
1461
- }
1462
- ],
1463
- name: "isMarginRatiosRegistered",
1464
- outputs: [
1465
- {
1466
- internalType: "bool",
1467
- name: "",
1468
- type: "bool"
1469
- }
1470
- ],
1471
- stateMutability: "view",
1472
- type: "function"
1473
- },
1474
- {
1475
- inputs: [
1418
+ internalType: "uint8",
1419
+ name: "moduleType",
1420
+ type: "uint8"
1421
+ },
1476
1422
  {
1477
- internalType: "contract ISqrtPriceImpactLimit",
1478
- name: "",
1423
+ internalType: "address",
1424
+ name: "module",
1479
1425
  type: "address"
1480
1426
  }
1481
1427
  ],
1482
- name: "isSqrtPriceImpactLimitRegistered",
1428
+ name: "isModuleRegistered",
1483
1429
  outputs: [
1484
1430
  {
1485
1431
  internalType: "bool",
@@ -1831,14 +1777,19 @@ var PERP_MANAGER_ABI = [
1831
1777
  type: "int256"
1832
1778
  },
1833
1779
  {
1834
- internalType: "uint256",
1780
+ internalType: "int256",
1835
1781
  name: "netMargin",
1836
- type: "uint256"
1782
+ type: "int256"
1837
1783
  },
1838
1784
  {
1839
1785
  internalType: "bool",
1840
1786
  name: "wasLiquidated",
1841
1787
  type: "bool"
1788
+ },
1789
+ {
1790
+ internalType: "uint256",
1791
+ name: "notional",
1792
+ type: "uint256"
1842
1793
  }
1843
1794
  ],
1844
1795
  stateMutability: "nonpayable",
@@ -1979,51 +1930,66 @@ var PERP_MANAGER_ABI = [
1979
1930
  {
1980
1931
  inputs: [
1981
1932
  {
1982
- internalType: "contract IFees",
1983
- name: "feesModule",
1984
- type: "address"
1985
- }
1986
- ],
1987
- name: "registerFeesModule",
1988
- outputs: [],
1989
- stateMutability: "nonpayable",
1990
- type: "function"
1991
- },
1992
- {
1993
- inputs: [
1933
+ internalType: "PoolId",
1934
+ name: "perpId",
1935
+ type: "bytes32"
1936
+ },
1994
1937
  {
1995
- internalType: "contract ILockupPeriod",
1996
- name: "lockupPeriodModule",
1997
- type: "address"
1938
+ internalType: "bool",
1939
+ name: "zeroForOne",
1940
+ type: "bool"
1941
+ },
1942
+ {
1943
+ internalType: "bool",
1944
+ name: "isExactIn",
1945
+ type: "bool"
1946
+ },
1947
+ {
1948
+ internalType: "uint256",
1949
+ name: "amount",
1950
+ type: "uint256"
1951
+ },
1952
+ {
1953
+ internalType: "uint160",
1954
+ name: "sqrtPriceLimitX96",
1955
+ type: "uint160"
1998
1956
  }
1999
1957
  ],
2000
- name: "registerLockupPeriodModule",
2001
- outputs: [],
2002
- stateMutability: "nonpayable",
2003
- type: "function"
2004
- },
2005
- {
2006
- inputs: [
1958
+ name: "quoteSwap",
1959
+ outputs: [
2007
1960
  {
2008
- internalType: "contract IMarginRatios",
2009
- name: "marginRatiosModule",
2010
- type: "address"
1961
+ internalType: "bytes",
1962
+ name: "unexpectedReason",
1963
+ type: "bytes"
1964
+ },
1965
+ {
1966
+ internalType: "int256",
1967
+ name: "perpDelta",
1968
+ type: "int256"
1969
+ },
1970
+ {
1971
+ internalType: "int256",
1972
+ name: "usdDelta",
1973
+ type: "int256"
2011
1974
  }
2012
1975
  ],
2013
- name: "registerMarginRatiosModule",
2014
- outputs: [],
2015
1976
  stateMutability: "nonpayable",
2016
1977
  type: "function"
2017
1978
  },
2018
1979
  {
2019
1980
  inputs: [
2020
1981
  {
2021
- internalType: "contract ISqrtPriceImpactLimit",
2022
- name: "sqrtPriceImpactLimitModule",
1982
+ internalType: "uint8",
1983
+ name: "moduleType",
1984
+ type: "uint8"
1985
+ },
1986
+ {
1987
+ internalType: "address",
1988
+ name: "module",
2023
1989
  type: "address"
2024
1990
  }
2025
1991
  ],
2026
- name: "registerSqrtPriceImpactLimitModule",
1992
+ name: "registerModule",
2027
1993
  outputs: [],
2028
1994
  stateMutability: "nonpayable",
2029
1995
  type: "function"
@@ -2409,6 +2375,10 @@ async function approveUsdc(context, amount, confirmations = DEFAULT_CONFIRMATION
2409
2375
  var NUMBER_1E6 = 1e6;
2410
2376
  var BIGINT_1E6 = 1000000n;
2411
2377
  var Q96 = 79228162514264337593543950336n;
2378
+ var MIN_TICK = -887272;
2379
+ var MAX_TICK = 887272;
2380
+ var MIN_PRICE = 1e-6;
2381
+ var MAX_PRICE = 1e6;
2412
2382
 
2413
2383
  // src/utils/conversions.ts
2414
2384
  function priceToSqrtPriceX96(price) {
@@ -2441,6 +2411,11 @@ function priceToTick(price, roundDown) {
2441
2411
  if (price <= 0) {
2442
2412
  throw new Error("Price must be positive");
2443
2413
  }
2414
+ if (price < MIN_PRICE || price > MAX_PRICE) {
2415
+ throw new Error(
2416
+ `Price ${price} is outside the representable range [${MIN_PRICE}, ${MAX_PRICE}]`
2417
+ );
2418
+ }
2444
2419
  const logPrice = Math.log(price) / Math.log(1.0001);
2445
2420
  return roundDown ? Math.floor(logPrice) : Math.ceil(logPrice);
2446
2421
  }
@@ -3479,7 +3454,6 @@ function getPerpTickSpacing(perpData) {
3479
3454
  import { decodeEventLog as decodeEventLog2, erc20Abi as erc20Abi3 } from "viem";
3480
3455
  async function createPerp(context, params) {
3481
3456
  return withErrorHandling(async () => {
3482
- const sqrtPriceX96 = priceToSqrtPriceX96(params.startingPrice);
3483
3457
  const deployments = context.deployments();
3484
3458
  const fees = params.fees ?? deployments.feesModule;
3485
3459
  const marginRatios = params.marginRatios ?? deployments.marginRatiosModule;
@@ -3493,8 +3467,7 @@ async function createPerp(context, params) {
3493
3467
  fees,
3494
3468
  marginRatios,
3495
3469
  lockupPeriod,
3496
- sqrtPriceImpactLimit,
3497
- startingSqrtPriceX96: sqrtPriceX96
3470
+ sqrtPriceImpactLimit
3498
3471
  };
3499
3472
  const { request } = await context.publicClient.simulateContract({
3500
3473
  address: context.deployments().perpManager,
@@ -3504,7 +3477,9 @@ async function createPerp(context, params) {
3504
3477
  account: context.walletClient.account
3505
3478
  });
3506
3479
  const txHash = await context.walletClient.writeContract(request);
3507
- const receipt = await context.publicClient.waitForTransactionReceipt({ hash: txHash });
3480
+ const receipt = await context.publicClient.waitForTransactionReceipt({
3481
+ hash: txHash
3482
+ });
3508
3483
  if (receipt.status === "reverted") {
3509
3484
  throw new Error(`Transaction reverted. Hash: ${txHash}`);
3510
3485
  }
@@ -3571,7 +3546,9 @@ async function openTakerPosition(context, perpId, params) {
3571
3546
  account: context.walletClient.account
3572
3547
  });
3573
3548
  const txHash = await context.walletClient.writeContract(request);
3574
- const receipt = await context.publicClient.waitForTransactionReceipt({ hash: txHash });
3549
+ const receipt = await context.publicClient.waitForTransactionReceipt({
3550
+ hash: txHash
3551
+ });
3575
3552
  if (receipt.status === "reverted") {
3576
3553
  throw new Error(`Transaction reverted. Hash: ${txHash}`);
3577
3554
  }
@@ -3596,6 +3573,82 @@ async function openTakerPosition(context, perpId, params) {
3596
3573
  return new OpenPosition(context, perpId, takerPosId, params.isLong, false, txHash);
3597
3574
  }, "openTakerPosition");
3598
3575
  }
3576
+ function buildMakerContractParams(context, marginScaled, params, alignedTickLower, alignedTickUpper, maxAmt0In, maxAmt1In) {
3577
+ return {
3578
+ holder: context.walletClient.account.address,
3579
+ margin: marginScaled,
3580
+ liquidity: params.liquidity,
3581
+ tickLower: alignedTickLower,
3582
+ tickUpper: alignedTickUpper,
3583
+ maxAmt0In,
3584
+ maxAmt1In
3585
+ };
3586
+ }
3587
+ function calculateAlignedTicks(priceLower, priceUpper, tickSpacing) {
3588
+ const tickLower = priceToTick(priceLower, true);
3589
+ const tickUpper = priceToTick(priceUpper, false);
3590
+ const alignedTickLower = Math.floor(tickLower / tickSpacing) * tickSpacing;
3591
+ const alignedTickUpper = Math.ceil(tickUpper / tickSpacing) * tickSpacing;
3592
+ if (alignedTickLower < MIN_TICK) {
3593
+ throw new Error(
3594
+ `Lower tick ${alignedTickLower} is below MIN_TICK (${MIN_TICK}). Increase priceLower.`
3595
+ );
3596
+ }
3597
+ if (alignedTickUpper > MAX_TICK) {
3598
+ throw new Error(
3599
+ `Upper tick ${alignedTickUpper} exceeds MAX_TICK (${MAX_TICK}). Decrease priceUpper.`
3600
+ );
3601
+ }
3602
+ if (alignedTickLower === alignedTickUpper) {
3603
+ throw new Error(
3604
+ "Price range too narrow: lower and upper ticks are equal after alignment. Widen the range."
3605
+ );
3606
+ }
3607
+ return { alignedTickLower, alignedTickUpper };
3608
+ }
3609
+ function alignMakerTicks(params, tickSpacing) {
3610
+ return calculateAlignedTicks(params.priceLower, params.priceUpper, tickSpacing);
3611
+ }
3612
+ var DEFAULT_MAKER_SLIPPAGE_TOLERANCE = 0.01;
3613
+ var MAX_UINT128 = 2n ** 128n - 1n;
3614
+ async function quoteOpenMakerPosition(context, perpId, params) {
3615
+ return withErrorHandling(async () => {
3616
+ if (params.margin <= 0) {
3617
+ throw new Error("Margin must be greater than 0");
3618
+ }
3619
+ if (params.priceLower >= params.priceUpper) {
3620
+ throw new Error("priceLower must be less than priceUpper");
3621
+ }
3622
+ const marginScaled = scale6Decimals(params.margin);
3623
+ const perpData = await context.getPerpData(perpId);
3624
+ const { alignedTickLower, alignedTickUpper } = alignMakerTicks(params, perpData.tickSpacing);
3625
+ const contractParams = buildMakerContractParams(
3626
+ context,
3627
+ marginScaled,
3628
+ params,
3629
+ alignedTickLower,
3630
+ alignedTickUpper,
3631
+ MAX_UINT128,
3632
+ MAX_UINT128
3633
+ );
3634
+ const [unexpectedReason, perpDelta, usdDelta] = await context.publicClient.readContract({
3635
+ address: context.deployments().perpManager,
3636
+ abi: PERP_MANAGER_ABI,
3637
+ functionName: "quoteOpenMakerPosition",
3638
+ args: [perpId, contractParams]
3639
+ });
3640
+ if (unexpectedReason !== "0x") {
3641
+ throw new Error(`Quote failed: ${unexpectedReason}`);
3642
+ }
3643
+ return { perpDelta, usdDelta };
3644
+ }, "quoteOpenMakerPosition");
3645
+ }
3646
+ function applySlippage(delta, slippageTolerance) {
3647
+ if (delta >= 0n) return 0n;
3648
+ const absDelta = -delta;
3649
+ const slippageBps = BigInt(Math.ceil(slippageTolerance * 1e4));
3650
+ return absDelta + absDelta * slippageBps / 10000n;
3651
+ }
3599
3652
  async function openMakerPosition(context, perpId, params) {
3600
3653
  return withErrorHandling(async () => {
3601
3654
  if (params.margin <= 0) {
@@ -3605,8 +3658,24 @@ async function openMakerPosition(context, perpId, params) {
3605
3658
  throw new Error("priceLower must be less than priceUpper");
3606
3659
  }
3607
3660
  const marginScaled = scale6Decimals(params.margin);
3608
- const maxAmt0InScaled = typeof params.maxAmt0In === "bigint" ? params.maxAmt0In : scale6Decimals(params.maxAmt0In);
3609
- const maxAmt1InScaled = typeof params.maxAmt1In === "bigint" ? params.maxAmt1In : scale6Decimals(params.maxAmt1In);
3661
+ const perpData = await context.getPerpData(perpId);
3662
+ const { alignedTickLower, alignedTickUpper } = alignMakerTicks(params, perpData.tickSpacing);
3663
+ let maxAmt0In;
3664
+ let maxAmt1In;
3665
+ if (params.maxAmt0In === void 0 !== (params.maxAmt1In === void 0)) {
3666
+ throw new Error(
3667
+ "Both maxAmt0In and maxAmt1In must be provided together or neither. Omit both to use automatic quote-based slippage calculation."
3668
+ );
3669
+ }
3670
+ if (params.maxAmt0In !== void 0 && params.maxAmt1In !== void 0) {
3671
+ maxAmt0In = typeof params.maxAmt0In === "bigint" ? params.maxAmt0In : scale6Decimals(params.maxAmt0In);
3672
+ maxAmt1In = typeof params.maxAmt1In === "bigint" ? params.maxAmt1In : scale6Decimals(params.maxAmt1In);
3673
+ } else {
3674
+ const quote = await quoteOpenMakerPosition(context, perpId, params);
3675
+ const slippage = params.slippageTolerance ?? DEFAULT_MAKER_SLIPPAGE_TOLERANCE;
3676
+ maxAmt0In = applySlippage(quote.perpDelta, slippage);
3677
+ maxAmt1In = applySlippage(quote.usdDelta, slippage);
3678
+ }
3610
3679
  const currentAllowance = await context.publicClient.readContract({
3611
3680
  address: context.deployments().usdc,
3612
3681
  abi: erc20Abi3,
@@ -3617,21 +3686,15 @@ async function openMakerPosition(context, perpId, params) {
3617
3686
  if (currentAllowance < marginScaled) {
3618
3687
  await approveUsdc(context, marginScaled);
3619
3688
  }
3620
- const perpData = await context.getPerpData(perpId);
3621
- const tickLower = priceToTick(params.priceLower, true);
3622
- const tickUpper = priceToTick(params.priceUpper, false);
3623
- const tickSpacing = perpData.tickSpacing;
3624
- const alignedTickLower = Math.floor(tickLower / tickSpacing) * tickSpacing;
3625
- const alignedTickUpper = Math.ceil(tickUpper / tickSpacing) * tickSpacing;
3626
- const contractParams = {
3627
- holder: context.walletClient.account.address,
3628
- margin: marginScaled,
3629
- liquidity: params.liquidity,
3630
- tickLower: alignedTickLower,
3631
- tickUpper: alignedTickUpper,
3632
- maxAmt0In: maxAmt0InScaled,
3633
- maxAmt1In: maxAmt1InScaled
3634
- };
3689
+ const contractParams = buildMakerContractParams(
3690
+ context,
3691
+ marginScaled,
3692
+ params,
3693
+ alignedTickLower,
3694
+ alignedTickUpper,
3695
+ maxAmt0In,
3696
+ maxAmt1In
3697
+ );
3635
3698
  const { request } = await context.publicClient.simulateContract({
3636
3699
  address: context.deployments().perpManager,
3637
3700
  abi: PERP_MANAGER_ABI,
@@ -3640,7 +3703,9 @@ async function openMakerPosition(context, perpId, params) {
3640
3703
  account: context.walletClient.account
3641
3704
  });
3642
3705
  const txHash = await context.walletClient.writeContract(request);
3643
- const receipt = await context.publicClient.waitForTransactionReceipt({ hash: txHash });
3706
+ const receipt = await context.publicClient.waitForTransactionReceipt({
3707
+ hash: txHash
3708
+ });
3644
3709
  if (receipt.status === "reverted") {
3645
3710
  throw new Error(`Transaction reverted. Hash: ${txHash}`);
3646
3711
  }
@@ -3827,6 +3892,10 @@ export {
3827
3892
  ErrorCategory,
3828
3893
  ErrorSource,
3829
3894
  InsufficientFundsError,
3895
+ MAX_PRICE,
3896
+ MAX_TICK,
3897
+ MIN_PRICE,
3898
+ MIN_TICK,
3830
3899
  NUMBER_1E6,
3831
3900
  OpenPosition,
3832
3901
  PERP_MANAGER_ABI,
@@ -3837,6 +3906,7 @@ export {
3837
3906
  TransactionRejectedError,
3838
3907
  ValidationError,
3839
3908
  approveUsdc,
3909
+ calculateAlignedTicks,
3840
3910
  calculateEntryPrice,
3841
3911
  calculateLeverage,
3842
3912
  calculateLiquidationPrice,
@@ -3871,6 +3941,7 @@ export {
3871
3941
  parseContractError,
3872
3942
  priceToSqrtPriceX96,
3873
3943
  priceToTick,
3944
+ quoteOpenMakerPosition,
3874
3945
  scale6Decimals,
3875
3946
  scaleFrom6Decimals,
3876
3947
  scaleFromX96,