@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.
- package/README.md +180 -45
- package/dist/config.d.ts +2 -0
- package/dist/entities/pool.d.ts +121 -1
- package/dist/services/hyperliquid/constants.d.ts +16 -0
- package/dist/services/hyperliquid/index.d.ts +6 -0
- package/dist/services/hyperliquid/marketData.d.ts +12 -0
- package/dist/services/hyperliquid/positionData.d.ts +1 -0
- package/dist/services/toros/limitOrder.d.ts +8 -0
- package/dist/test/constants.d.ts +7 -1
- package/dist/test/wallet.d.ts +1 -0
- package/dist/types.d.ts +12 -2
- package/dist/v2-sdk.cjs.development.js +1523 -32
- package/dist/v2-sdk.cjs.development.js.map +1 -1
- package/dist/v2-sdk.cjs.production.min.js +1 -1
- package/dist/v2-sdk.cjs.production.min.js.map +1 -1
- package/dist/v2-sdk.esm.js +1502 -12
- package/dist/v2-sdk.esm.js.map +1 -1
- package/package.json +3 -2
- package/src/abi/hyperliquid/ICoreDepositWallet.json +130 -0
- package/src/abi/hyperliquid/ICoreWriter.json +1 -0
- package/src/abi/toros/IPoolLimitOrderManager.json +78 -0
- package/src/config.ts +31 -9
- package/src/entities/pool.ts +382 -4
- package/src/services/hyperliquid/constants.ts +23 -0
- package/src/services/hyperliquid/index.ts +178 -0
- package/src/services/hyperliquid/marketData.ts +157 -0
- package/src/services/hyperliquid/positionData.ts +33 -0
- package/src/services/toros/limitOrder.ts +86 -0
- package/src/test/constants.ts +11 -5
- package/src/test/hyperliquid.test.ts +107 -0
- package/src/test/pool.test.ts +37 -45
- package/src/test/torosLimitOrder.test.ts +130 -0
- package/src/test/wallet.ts +2 -1
- 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
|
+
}
|
package/src/test/constants.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
+
});
|
package/src/test/pool.test.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
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
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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].
|
|
55
|
-
// { asset: CONTRACT_ADDRESS[network].USDE, isDeposit: true },
|
|
49
|
+
// { asset: CONTRACT_ADDRESS[network].USDC, isDeposit: true },
|
|
56
50
|
// {
|
|
57
|
-
// asset: "
|
|
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].
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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.
|
|
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
|
+
});
|
package/src/test/wallet.ts
CHANGED