@dhedge/v2-sdk 2.1.5 → 2.1.7

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.
Files changed (34) hide show
  1. package/README.md +180 -45
  2. package/dist/config.d.ts +2 -0
  3. package/dist/entities/pool.d.ts +121 -1
  4. package/dist/services/hyperliquid/constants.d.ts +16 -0
  5. package/dist/services/hyperliquid/index.d.ts +6 -0
  6. package/dist/services/hyperliquid/marketData.d.ts +12 -0
  7. package/dist/services/hyperliquid/positionData.d.ts +1 -0
  8. package/dist/services/toros/limitOrder.d.ts +8 -0
  9. package/dist/test/constants.d.ts +7 -1
  10. package/dist/test/wallet.d.ts +1 -0
  11. package/dist/types.d.ts +12 -2
  12. package/dist/v2-sdk.cjs.development.js +1523 -32
  13. package/dist/v2-sdk.cjs.development.js.map +1 -1
  14. package/dist/v2-sdk.cjs.production.min.js +1 -1
  15. package/dist/v2-sdk.cjs.production.min.js.map +1 -1
  16. package/dist/v2-sdk.esm.js +1502 -12
  17. package/dist/v2-sdk.esm.js.map +1 -1
  18. package/package.json +3 -2
  19. package/src/abi/hyperliquid/ICoreDepositWallet.json +130 -0
  20. package/src/abi/hyperliquid/ICoreWriter.json +1 -0
  21. package/src/abi/toros/IPoolLimitOrderManager.json +78 -0
  22. package/src/config.ts +31 -9
  23. package/src/entities/pool.ts +382 -4
  24. package/src/services/hyperliquid/constants.ts +23 -0
  25. package/src/services/hyperliquid/index.ts +178 -0
  26. package/src/services/hyperliquid/marketData.ts +157 -0
  27. package/src/services/hyperliquid/positionData.ts +33 -0
  28. package/src/services/toros/limitOrder.ts +86 -0
  29. package/src/test/constants.ts +11 -5
  30. package/src/test/hyperliquid.test.ts +107 -0
  31. package/src/test/pool.test.ts +37 -45
  32. package/src/test/torosLimitOrder.test.ts +130 -0
  33. package/src/test/wallet.ts +2 -1
  34. package/src/types.ts +13 -2
@@ -0,0 +1,157 @@
1
+ import axios from "axios";
2
+ import { API_URL, dexIdNameMap } from "./constants";
3
+ import { BigNumber } from "bignumber.js";
4
+ import { ApiError } from "../..";
5
+
6
+ export const perpDexIndex = (assetId: number): number => {
7
+ return Math.max(Math.floor((assetId - 100000) / 10000), 0);
8
+ };
9
+
10
+ const assetIndex = (assetId: number): number => {
11
+ if (assetId > 100000) {
12
+ //builder-deployed perps
13
+ return (assetId - 100000) % 10000;
14
+ } else return assetId;
15
+ };
16
+
17
+ export const spotAssetIndex = (assetId: number): number => {
18
+ return assetId - 10000;
19
+ };
20
+
21
+ export const isSpotAsset = (assetId: number): boolean => {
22
+ return assetId > 10000 && assetId < 100000;
23
+ };
24
+
25
+ export const getMidPrice = async (
26
+ assetId: number,
27
+ assetName: string
28
+ ): Promise<number> => {
29
+ const response = await axios.post(API_URL, {
30
+ type: "allMids",
31
+ dex: dexIdNameMap[perpDexIndex(assetId)]
32
+ });
33
+ const raw = response.data[assetName];
34
+ if (raw === undefined || raw === null) {
35
+ throw new ApiError(
36
+ `Hyperliquid allMids response missing price for asset "${assetName}"`
37
+ );
38
+ }
39
+ const price = +raw;
40
+ if (isNaN(price)) {
41
+ throw new ApiError(
42
+ `Hyperliquid allMids returned non-numeric price for asset "${assetName}": ${raw}`
43
+ );
44
+ }
45
+ return price;
46
+ };
47
+
48
+ export const getAssetInfo = async (
49
+ assetId: number
50
+ ): Promise<{
51
+ assetName: string;
52
+ szDecimals: number;
53
+ baseTokenName?: string;
54
+ }> => {
55
+ if (isSpotAsset(assetId)) {
56
+ const response = await axios.post(API_URL, {
57
+ type: "spotMeta"
58
+ });
59
+ const asset = response.data.universe.find(
60
+ (e: { index: number }) => e.index === spotAssetIndex(assetId)
61
+ );
62
+ if (!asset) {
63
+ throw new ApiError(
64
+ `Hyperliquid spotMeta response contains no asset for assetId ${assetId} (index ${spotAssetIndex(
65
+ assetId
66
+ )})`
67
+ );
68
+ }
69
+ const baseToken = response.data.tokens[asset.tokens[0]];
70
+ return {
71
+ assetName: asset.name,
72
+ szDecimals: baseToken.szDecimals,
73
+ baseTokenName: baseToken.name
74
+ };
75
+ } else {
76
+ const dex = dexIdNameMap[perpDexIndex(assetId)];
77
+ const response = await axios.post(API_URL, {
78
+ type: "metaAndAssetCtxs",
79
+ dex
80
+ });
81
+ if (!Array.isArray(response.data) || response.data.length === 0) {
82
+ throw new ApiError(
83
+ `Hyperliquid metaAndAssetCtxs response has invalid data for assetId ${assetId} (dex ${String(
84
+ dex
85
+ )})`
86
+ );
87
+ }
88
+ const meta = response.data[0];
89
+ if (!meta || !Array.isArray(meta.universe)) {
90
+ throw new ApiError(
91
+ `Hyperliquid metaAndAssetCtxs response contains no universe for assetId ${assetId} (dex ${String(
92
+ dex
93
+ )})`
94
+ );
95
+ }
96
+ const assets = meta.universe;
97
+ const index = assetIndex(assetId);
98
+ if (index < 0 || index >= assets.length) {
99
+ throw new ApiError(
100
+ `Hyperliquid metaAndAssetCtxs response contains no asset for assetId ${assetId} (dex ${String(
101
+ dex
102
+ )}, index ${index}, universe length ${assets.length})`
103
+ );
104
+ }
105
+ const asset = assets[index];
106
+ return {
107
+ assetName: asset.name,
108
+ szDecimals: asset.szDecimals
109
+ };
110
+ }
111
+ };
112
+
113
+ export const calculatePrice = (
114
+ isSpotAsset: boolean,
115
+ szDecimals: number,
116
+ midPrice: number,
117
+ isBuy: boolean,
118
+ slippage: number
119
+ ): string => {
120
+ // 1. Apply slippage
121
+ const price = midPrice * (isBuy ? 1 + slippage / 100 : 1 - slippage / 100);
122
+
123
+ // 2. Round to 5 significant figures
124
+ const roundedSignificant = parseFloat(price.toPrecision(5));
125
+
126
+ // 3. For perp base decimals = 6
127
+ const baseDecimals = isSpotAsset ? 8 : 6;
128
+ const finalDecimals = baseDecimals - szDecimals;
129
+ const factor = Math.pow(10, finalDecimals);
130
+ const roundedDecimals = Math.round(roundedSignificant * factor) / factor;
131
+ return new BigNumber(roundedDecimals).times(1e8).toFixed(0);
132
+ };
133
+
134
+ export const calculateSize = (
135
+ szDecimals: number,
136
+ value: number,
137
+ price: number
138
+ ): string => {
139
+ const factor = Math.pow(10, szDecimals);
140
+ return new BigNumber(Math.round((value / price) * factor) / factor)
141
+ .times(1e8)
142
+ .toFixed(0);
143
+ };
144
+
145
+ export const scaleSize = (
146
+ szDecimals: number,
147
+ positionSize: number,
148
+ percentageToClose: number
149
+ ): string => {
150
+ const factor = Math.pow(10, szDecimals);
151
+ return new BigNumber(
152
+ Math.round(((positionSize * percentageToClose) / 100) * factor) / factor
153
+ )
154
+ .times(1e8)
155
+ .abs()
156
+ .toFixed(0);
157
+ };
@@ -0,0 +1,33 @@
1
+ import axios from "axios";
2
+ import { API_URL, dexIdNameMap } from "./constants";
3
+ import { perpDexIndex } from "./marketData";
4
+
5
+ export const getPositionSize = async (
6
+ assetId: number,
7
+ isSpot: boolean,
8
+ assetName: string,
9
+ user: string
10
+ ): Promise<number> => {
11
+ if (isSpot) {
12
+ const response = await axios.post(API_URL, {
13
+ type: "spotClearinghouseState",
14
+ user
15
+ });
16
+ const balance = response.data.balances.find(
17
+ (e: { coin: string }) => e.coin === assetName
18
+ );
19
+ if (!balance) throw new Error(`No balance found for asset ${assetName}`);
20
+ return +balance.total;
21
+ } else {
22
+ const response = await axios.post(API_URL, {
23
+ type: "clearinghouseState",
24
+ user,
25
+ dex: dexIdNameMap[perpDexIndex(assetId)]
26
+ });
27
+ const position = response.data.assetPositions.find(
28
+ (e: { position: { coin: string } }) => e.position.coin === assetName
29
+ );
30
+ if (!position) throw new Error(`No position found for asset ${assetName}`);
31
+ return +position.position.szi;
32
+ }
33
+ };
@@ -0,0 +1,86 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { ethers } from "ethers";
3
+ import { Pool } from "../..";
4
+ import { limitOrderAddress } from "../../config";
5
+ import { LimitOrderInfo } from "../../types";
6
+ import IPoolLimitOrderManager from "../../abi/toros/IPoolLimitOrderManager.json";
7
+
8
+ const iface = new ethers.utils.Interface(IPoolLimitOrderManager);
9
+
10
+ export function getLimitOrderId(
11
+ userAddress: string,
12
+ vaultAddress: string
13
+ ): string {
14
+ return ethers.utils.solidityKeccak256(
15
+ ["address", "address"],
16
+ [userAddress, vaultAddress]
17
+ );
18
+ }
19
+
20
+ export function getCreateLimitOrderTxData(info: LimitOrderInfo): string {
21
+ return iface.encodeFunctionData("createLimitOrder", [
22
+ [
23
+ info.amount,
24
+ info.stopLossPriceD18,
25
+ info.takeProfitPriceD18,
26
+ info.user,
27
+ info.pool,
28
+ info.pricingAsset
29
+ ]
30
+ ]);
31
+ }
32
+
33
+ export function getModifyLimitOrderTxData(info: LimitOrderInfo): string {
34
+ return iface.encodeFunctionData("modifyLimitOrder", [
35
+ [
36
+ info.amount,
37
+ info.stopLossPriceD18,
38
+ info.takeProfitPriceD18,
39
+ info.user,
40
+ info.pool,
41
+ info.pricingAsset
42
+ ]
43
+ ]);
44
+ }
45
+
46
+ export function getDeleteLimitOrderTxData(vaultAddress: string): string {
47
+ return iface.encodeFunctionData("deleteLimitOrder", [vaultAddress]);
48
+ }
49
+
50
+ export async function getTorosLimitOrder(
51
+ pool: Pool,
52
+ userAddress: string,
53
+ vaultAddress: string
54
+ ): Promise<LimitOrderInfo | null> {
55
+ const managerAddress = limitOrderAddress[pool.network];
56
+ if (!managerAddress) return null;
57
+
58
+ const orderId = getLimitOrderId(userAddress, vaultAddress);
59
+ const contract = new ethers.Contract(
60
+ managerAddress,
61
+ IPoolLimitOrderManager,
62
+ pool.signer
63
+ );
64
+
65
+ const result = await contract.limitOrders(orderId);
66
+ // If amount is zero, the order doesn't exist
67
+ if (result.amount.isZero()) return null;
68
+
69
+ return {
70
+ amount: result.amount,
71
+ stopLossPriceD18: result.stopLossPriceD18,
72
+ takeProfitPriceD18: result.takeProfitPriceD18,
73
+ user: result.user,
74
+ pool: result.pool,
75
+ pricingAsset: result.pricingAsset
76
+ };
77
+ }
78
+
79
+ export async function hasActiveTorosLimitOrder(
80
+ pool: Pool,
81
+ userAddress: string,
82
+ vaultAddress: string
83
+ ): Promise<boolean> {
84
+ const order = await getTorosLimitOrder(pool, userAddress, vaultAddress);
85
+ return order !== null;
86
+ }
@@ -44,7 +44,8 @@ export const TEST_POOL = {
44
44
  [Network.ARBITRUM]: "0x0b5f6591c8eb23e5a68102d3d39ebbb464ee5c14",
45
45
  [Network.BASE]: "0x4842b42F68524383F609aa46eAfc18c1459cE3cD",
46
46
  [Network.ETHEREUM]: "0xe8e74f664d2d6a919a18b911990db0979789b6f7",
47
- [Network.PLASMA]: "0xdad21646ebb0997eb59de1f6a68a67059daf4c31"
47
+ [Network.PLASMA]: "0xdad21646ebb0997eb59de1f6a68a67059daf4c31",
48
+ [Network.HYPERLIQUID]: "0x3b4427283148c30b13b33a564264c73443ed12d1"
48
49
  };
49
50
 
50
51
  export const CONTRACT_ADDRESS = {
@@ -170,8 +171,11 @@ export const CONTRACT_ADDRESS = {
170
171
  USDC: "",
171
172
  WETH: "0x9895d81bb462a195b4922ed7de0e3acd007c32cb",
172
173
  USDT: "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb",
173
- USDE: "0x5d3a1ff2b6bab83b63cd9ad0787074081a52ef34",
174
- TOROS: ""
174
+ USDE: "0x5d3a1ff2b6bab83b63cd9ad0787074081a52ef34"
175
+ },
176
+ [Network.HYPERLIQUID]: {
177
+ USDC: "0xb88339cb7199b77e23db6e890353e22632ba630f",
178
+ WETH: ""
175
179
  }
176
180
  };
177
181
 
@@ -183,7 +187,8 @@ export const USDC_BALANCEOF_SLOT = {
183
187
  [Network.POLYGON]: 0,
184
188
  [Network.BASE]: 9,
185
189
  [Network.ETHEREUM]: 9,
186
- [Network.PLASMA]: 9 //Currently no USDC on Plasma
190
+ [Network.PLASMA]: 9,
191
+ [Network.HYPERLIQUID]: 9
187
192
  };
188
193
 
189
194
  export const WETH_BALANCEOF_SLOT = {
@@ -192,5 +197,6 @@ export const WETH_BALANCEOF_SLOT = {
192
197
  [Network.POLYGON]: 0,
193
198
  [Network.BASE]: 0,
194
199
  [Network.ETHEREUM]: 3,
195
- [Network.PLASMA]: 1
200
+ [Network.PLASMA]: 1,
201
+ [Network.HYPERLIQUID]: 1
196
202
  };
@@ -0,0 +1,107 @@
1
+ /* eslint-disable @typescript-eslint/no-non-null-assertion */
2
+
3
+ import { Dhedge, Pool } from "..";
4
+
5
+ import { Network } from "../types";
6
+ import { TEST_POOL } from "./constants";
7
+
8
+ import { TestingRunParams, testingHelper } from "./utils/testingHelper";
9
+
10
+ // import { balanceDelta } from "./utils/token";
11
+
12
+ const testHyperliquid = ({ wallet, network }: TestingRunParams) => {
13
+ let dhedge: Dhedge;
14
+ let pool: Pool;
15
+ jest.setTimeout(100000);
16
+
17
+ describe(`pool on ${network}`, () => {
18
+ beforeAll(async () => {
19
+ dhedge = new Dhedge(wallet, network);
20
+ pool = await dhedge.loadPool(TEST_POOL[network]);
21
+ // // top up gas
22
+ // await provider.send("hardhat_setBalance", [
23
+ // wallet.address,
24
+ // "0x10000000000000000"
25
+ // ]);
26
+ // await provider.send("evm
27
+ // _mine", []);
28
+ // // top up USDC
29
+ // await setUSDCAmount({
30
+ // amount: new BigNumber(2).times(1e6).toFixed(0),
31
+ // userAddress: pool.address,
32
+ // network,
33
+ // provider
34
+ // });
35
+ });
36
+
37
+ // it("approves unlimited USDC on Hyperliquid Core Wallet", async () => {
38
+ // await pool.approve(
39
+ // Dapp.HYPERLIQUID,
40
+ // USDC,
41
+ // MAX_AMOUNT,
42
+ // await getTxOptions(network)
43
+ // );
44
+ // const usdcAllowanceDelta = await allowanceDelta(
45
+ // pool.address,
46
+ // USDC,
47
+ // routerAddress[network][Dapp.HYPERLIQUID]!,
48
+ // pool.signer
49
+ // );
50
+ // await expect(usdcAllowanceDelta.gt(0));
51
+ // });
52
+
53
+ // it("deposits USDC into Hyperliquid Core Wallet", async () => {
54
+ // await pool.depositHyperliquid(
55
+ // "30000000", // 5 USDC with 6 decimals,
56
+ // 4294967295
57
+ // );
58
+ // expect(
59
+ // (await balanceDelta(pool.address, USDC, pool.signer)).eq("-20000000")
60
+ // ).toBe(true);
61
+ // });
62
+
63
+ // it("move 5 USDC from Perp to Spot Wallet", async () => {
64
+ // const tx = await pool.perpToSpotHyperliquid(
65
+ // 0,
66
+ // "5000000" // 5 USDC with 6 decimals
67
+ // );
68
+ // expect(tx).toBeDefined();
69
+ // });
70
+
71
+ // it("withdraws USDC from Hyperliquid Spot Wallet", async () => {
72
+ // const tx = await pool.withdrawHyperliquid(
73
+ // "784577548" // 5 USDC with 6 decimals
74
+ // );
75
+ // expect(tx).toBeDefined();
76
+ // });
77
+
78
+ // it("sets up a BTC_USDC buy order", async () => {
79
+ // const tx = await pool.openMarketOrderHyperliquid(
80
+ // 10182, // HYPE SPOT
81
+ // true, // is long
82
+ // 25, // 25 USD value of BTC,
83
+ // 1, // 0.5% slippage,
84
+ // null,
85
+ // true
86
+ // );
87
+ // expect(tx).toBeDefined();
88
+ // });
89
+
90
+ it("closes a position on Hyperliquid", async () => {
91
+ const tx = await pool.closePositionHyperliquid(
92
+ 1, // ETH Perp,
93
+ 50, // percentage to close
94
+ 1, // 0.5% slippage,
95
+ null,
96
+ true
97
+ );
98
+ expect(tx).toBeDefined();
99
+ });
100
+ });
101
+ };
102
+
103
+ testingHelper({
104
+ network: Network.HYPERLIQUID,
105
+ testingRun: testHyperliquid,
106
+ onFork: false
107
+ });
@@ -1,12 +1,12 @@
1
- import { BigNumber } from "ethers";
2
- import { Dhedge, Pool } from "..";
3
- import { Network } from "../types";
4
- import { TEST_POOL } from "./constants";
1
+ import { Dhedge, Network, Pool } from "..";
2
+ import { CONTRACT_ADDRESS, TEST_POOL } from "./constants";
5
3
 
6
4
  import { testingHelper, TestingRunParams } from "./utils/testingHelper";
5
+ import { balanceDelta } from "./utils/token";
6
+ // import { allowanceDelta } from "./utils/token";
7
7
  // import { balanceDelta } from "./utils/token";
8
8
 
9
- const testPool = ({ wallet, network, provider }: TestingRunParams) => {
9
+ const testPool = ({ wallet, network }: TestingRunParams) => {
10
10
  let dhedge: Dhedge;
11
11
  let pool: Pool;
12
12
 
@@ -16,31 +16,26 @@ const testPool = ({ wallet, network, provider }: TestingRunParams) => {
16
16
  beforeAll(async () => {
17
17
  dhedge = new Dhedge(wallet, network);
18
18
  pool = await dhedge.loadPool(TEST_POOL[network]);
19
-
20
- await provider.send("hardhat_setBalance", [
21
- wallet.address,
22
- "0x10000000000000000"
23
- ]);
24
19
  });
25
20
 
26
- it("checks fund composition", async () => {
27
- const result = await pool.getComposition();
28
- // console.log(result);
29
- expect(result.length).toBeGreaterThan(0);
30
- });
21
+ // it("checks fund composition", async () => {
22
+ // const result = await pool.getComposition();
23
+ // console.log(result);
24
+ // expect(result.length).toBeGreaterThan(0);
25
+ // });
31
26
 
32
- it("sets max supply cap", async () => {
33
- const totalSupply: BigNumber = await pool.poolLogic.totalSupply();
34
- let initCap = totalSupply;
35
- if (totalSupply.eq(0)) {
36
- initCap = BigNumber.from(1000).mul(BigNumber.from(10).pow(18));
37
- }
38
- await pool.setMaxCap(initCap.mul(2), null, true);
39
- const tx = await pool.setMaxCap(initCap.mul(2));
40
- await tx.wait(1);
41
- const maxCapAfter: BigNumber = await pool.managerLogic.maxSupplyCap();
42
- expect(maxCapAfter).toEqual(initCap.mul(2));
43
- });
27
+ // it("sets max supply cap", async () => {
28
+ // const totalSupply: BigNumber = await pool.poolLogic.totalSupply();
29
+ // let initCap = totalSupply;
30
+ // if (totalSupply.eq(0)) {
31
+ // initCap = BigNumber.from(1000).mul(BigNumber.from(10).pow(18));
32
+ // }
33
+ // await pool.setMaxCap(initCap.mul(2), null, true);
34
+ // const tx = await pool.setMaxCap(initCap.mul(2));
35
+ // await tx.wait(1);
36
+ // const maxCapAfter: BigNumber = await pool.managerLogic.maxSupplyCap();
37
+ // expect(maxCapAfter).toEqual(initCap.mul(2));
38
+ // });
44
39
 
45
40
  // it("sets pool private", async () => {
46
41
  // const result = await pool.setPrivate(true);
@@ -51,10 +46,9 @@ const testPool = ({ wallet, network, provider }: TestingRunParams) => {
51
46
  // const assetsBefore = await pool.getComposition();
52
47
 
53
48
  // const newAssets: AssetEnabled[] = [
54
- // { asset: CONTRACT_ADDRESS[network].USDT, isDeposit: true },
55
- // { asset: CONTRACT_ADDRESS[network].USDE, isDeposit: true },
49
+ // { asset: CONTRACT_ADDRESS[network].USDC, isDeposit: true },
56
50
  // {
57
- // asset: "0x925a2A7214Ed92428B5b1B090F80b25700095e12",
51
+ // asset: "0x3333333333333333333333333333333333333333",
58
52
  // isDeposit: false
59
53
  // }
60
54
  // ];
@@ -65,7 +59,7 @@ const testPool = ({ wallet, network, provider }: TestingRunParams) => {
65
59
 
66
60
  // it("approves USDT balance of User for Deposit", async () => {
67
61
  // await pool.approveDeposit(
68
- // CONTRACT_ADDRESS[network].USDT,
62
+ // CONTRACT_ADDRESS[network].USDC,
69
63
  // ethers.constants.MaxUint256
70
64
  // );
71
65
  // const usdtAllowanceDelta = await allowanceDelta(
@@ -77,18 +71,15 @@ const testPool = ({ wallet, network, provider }: TestingRunParams) => {
77
71
  // expect(usdtAllowanceDelta.gt(0));
78
72
  // });
79
73
 
80
- // it("deposits 200 USDT into Pool", async () => {
81
- // await pool.deposit(
82
- // CONTRACT_ADDRESS[network].USDT,
83
- // (200000000).toString()
84
- // );
85
- // const poolTokenDelta = await balanceDelta(
86
- // pool.address,
87
- // USDT,
88
- // pool.signer
89
- // );
90
- // expect(poolTokenDelta.gt(0));
91
- // });
74
+ it("deposits 200 USDT into Pool", async () => {
75
+ await pool.deposit(CONTRACT_ADDRESS[network].USDC, (30000000).toString());
76
+ const poolTokenDelta = await balanceDelta(
77
+ pool.address,
78
+ CONTRACT_ADDRESS[network].USDC,
79
+ pool.signer
80
+ );
81
+ expect(poolTokenDelta.gt(0));
82
+ });
92
83
 
93
84
  // it("get available Manager Fee", async () => {
94
85
  // const result = await pool.getAvailableManagerFee();
@@ -120,6 +111,7 @@ const testPool = ({ wallet, network, provider }: TestingRunParams) => {
120
111
  // });
121
112
 
122
113
  testingHelper({
123
- network: Network.ARBITRUM,
124
- testingRun: testPool
114
+ network: Network.HYPERLIQUID,
115
+ testingRun: testPool,
116
+ onFork: false
125
117
  });
@@ -0,0 +1,130 @@
1
+ /* eslint-disable @typescript-eslint/no-non-null-assertion */
2
+
3
+ // import { ethers } from "ethers";
4
+ import { Dhedge, Pool } from "..";
5
+ // import { limitOrderAddress } from "../config";
6
+
7
+ import { Network } from "../types";
8
+ // import { MAX_AMOUNT } from "./constants";
9
+ import { testingHelper, TestingRunParams } from "./utils/testingHelper";
10
+
11
+ // import { allowanceDelta } from "./utils/token";
12
+
13
+ // cspell:ignore goldbull
14
+ const TOROS_GOLDBULL2X = "0xc8e7e840ca82804c14061a27aaca1b97a5a592ab";
15
+ // const GOLD_PRICING_ASSET = "0x7624cccCc59361D583F28BEC40D37e7771d2ef5D";
16
+
17
+ const testTorosLimitOrder = ({ wallet, network }: TestingRunParams) => {
18
+ // const ORDER_AMOUNT = ethers.utils.parseEther("0.001");
19
+ // const STOP_LOSS = ethers.utils.parseEther("3000");
20
+ // const TAKE_PROFIT = ethers.utils.parseEther("6000");
21
+
22
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
23
+ let dhedge: Dhedge;
24
+ let pool: Pool;
25
+ jest.setTimeout(100000);
26
+
27
+ describe(`toros limit order on ${network}`, () => {
28
+ beforeAll(async () => {
29
+ dhedge = new Dhedge(wallet, network);
30
+ pool = await dhedge.loadPool(
31
+ "0xdd3d575fae102a12aaa086d97c7a8814eff42ebc" // MCP test pool (with GOLDBULL2X)
32
+ );
33
+ });
34
+
35
+ it("approves Toros vault token for LimitOrderManager", async () => {
36
+ const tx = await pool.approveTorosLimitOrder(
37
+ TOROS_GOLDBULL2X,
38
+ MAX_AMOUNT
39
+ );
40
+ await tx.wait(1);
41
+ const delta = await allowanceDelta(
42
+ pool.address,
43
+ TOROS_GOLDBULL2X,
44
+ limitOrderAddress[network],
45
+ pool.signer
46
+ );
47
+ expect(delta.gt(0)).toBe(true);
48
+ });
49
+
50
+ it("creates a limit order", async () => {
51
+ const tx = await pool.createTorosLimitOrder(
52
+ TOROS_GOLDBULL2X,
53
+ ORDER_AMOUNT,
54
+ STOP_LOSS,
55
+ TAKE_PROFIT,
56
+ GOLD_PRICING_ASSET
57
+ );
58
+ await tx.wait(1);
59
+ const order = await pool.getTorosLimitOrder(
60
+ pool.address,
61
+ TOROS_GOLDBULL2X
62
+ );
63
+ expect(order).not.toBeNull();
64
+ expect(order!.amount.eq(ORDER_AMOUNT)).toBe(true);
65
+ expect(order!.stopLossPriceD18.eq(STOP_LOSS)).toBe(true);
66
+ expect(order!.takeProfitPriceD18.eq(TAKE_PROFIT)).toBe(true);
67
+ expect(order!.pool.toLowerCase()).toBe(TOROS_GOLDBULL2X.toLowerCase());
68
+ expect(order!.pricingAsset.toLowerCase()).toBe(
69
+ GOLD_PRICING_ASSET.toLowerCase()
70
+ );
71
+ });
72
+
73
+ it("modifies a toros limit order", async () => {
74
+ const order = await pool.getTorosLimitOrder(
75
+ pool.address,
76
+ TOROS_GOLDBULL2X
77
+ );
78
+ if (!order) throw new Error("No existing order found");
79
+
80
+ const newStopLoss = order.stopLossPriceD18
81
+ .mul(90)
82
+ .div(100)
83
+ .toString();
84
+ const newTakeProfit = order.takeProfitPriceD18
85
+ .mul(110)
86
+ .div(100)
87
+ .toString();
88
+
89
+ const tx = await pool.modifyTorosLimitOrder(
90
+ TOROS_GOLDBULL2X,
91
+ order.amount,
92
+ newStopLoss,
93
+ newTakeProfit,
94
+ order.pricingAsset
95
+ );
96
+ await tx.wait(1);
97
+
98
+ const modifiedOrder = await pool.getTorosLimitOrder(
99
+ pool.address,
100
+ TOROS_GOLDBULL2X
101
+ );
102
+ expect(modifiedOrder).not.toBeNull();
103
+ expect(modifiedOrder!.stopLossPriceD18.toString()).toBe(newStopLoss);
104
+ expect(modifiedOrder!.takeProfitPriceD18.toString()).toBe(newTakeProfit);
105
+ });
106
+
107
+ it("deletes a toros limit order", async () => {
108
+ const order = await pool.getTorosLimitOrder(
109
+ pool.address,
110
+ TOROS_GOLDBULL2X
111
+ );
112
+ if (!order) throw new Error("No existing order found");
113
+
114
+ const tx = await pool.deleteTorosLimitOrder(TOROS_GOLDBULL2X);
115
+ await tx.wait(1);
116
+
117
+ const deletedOrder = await pool.getTorosLimitOrder(
118
+ pool.address,
119
+ TOROS_GOLDBULL2X
120
+ );
121
+ expect(deletedOrder).toBeNull();
122
+ });
123
+ });
124
+ };
125
+
126
+ testingHelper({
127
+ network: Network.ARBITRUM,
128
+ testingRun: testTorosLimitOrder,
129
+ onFork: false
130
+ });
@@ -9,7 +9,8 @@ export const networkPortMap = {
9
9
  [Network.ARBITRUM]: 8540,
10
10
  [Network.BASE]: 8546,
11
11
  [Network.ETHEREUM]: 8547,
12
- [Network.PLASMA]: 8548
12
+ [Network.PLASMA]: 8548,
13
+ [Network.HYPERLIQUID]: 8549
13
14
  };
14
15
 
15
16
  export const getWalletData = (