@dhedge/v2-sdk 1.4.3 → 1.5.0

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.
@@ -105,4 +105,13 @@ export class Dhedge {
105
105
  this.factory
106
106
  );
107
107
  }
108
+
109
+ /**
110
+ * Check if pool address is valid
111
+ * @param {string} address Pool address
112
+ * @returns {boolean} Is valid pool address
113
+ */
114
+ validatePool(address: string): Promise<boolean> {
115
+ return this.factory.isPool(address);
116
+ }
108
117
  }
@@ -40,6 +40,8 @@ import {
40
40
  } from "../services/uniswap/V3Liquidity";
41
41
  import { FeeAmount } from "@uniswap/v3-sdk";
42
42
  import { getUniswapV3SwapTxData } from "../services/uniswap/V3Trade";
43
+ import { getEasySwapperTxData } from "../services/toros/easySwapper";
44
+ import { getOneInchProtocols } from "../services/oneInch/protocols";
43
45
 
44
46
  export class Pool {
45
47
  public readonly poolLogic: Contract;
@@ -265,52 +267,68 @@ export class Pool {
265
267
  options: any = null
266
268
  ): Promise<any> {
267
269
  let swapTxData: string;
268
- if (dapp === Dapp.ONEINCH) {
269
- const chainId = networkChainIdMap[this.network];
270
- const apiUrl = `https://api.1inch.exchange/v4.0/${chainId}/swap?fromTokenAddress=${assetFrom}&toTokenAddress=${assetTo}&amount=${amountIn.toString()}&fromAddress=${
271
- this.address
272
- }&destReceiver=${
273
- this.address
274
- }&slippage=${slippage.toString()}&disableEstimate=true`;
275
- const response = await axios.get(apiUrl);
276
- swapTxData = response.data.tx.data;
277
- } else if (dapp === Dapp.BALANCER) {
278
- swapTxData = await this.utils.getBalancerSwapTx(
279
- this,
280
- assetFrom,
281
- assetTo,
282
- amountIn,
283
- slippage
284
- );
285
- } else if (dapp === Dapp.SYNTHETIX) {
286
- const iSynthetix = new ethers.utils.Interface(ISynthetix.abi);
287
- const assets = [assetFrom, assetTo].map(asset =>
288
- ethers.utils.formatBytes32String(asset)
289
- );
290
- const daoAddress = await this.factory.owner();
291
- swapTxData = iSynthetix.encodeFunctionData(Transaction.SWAP_SYNTHS, [
292
- assets[0],
293
- amountIn,
294
- assets[1],
295
- daoAddress,
296
- SYNTHETIX_TRACKING_CODE
297
- ]);
298
- } else {
299
- const iUniswapV2Router = new ethers.utils.Interface(IUniswapV2Router.abi);
300
- const minAmountOut = await this.utils.getMinAmountOut(
301
- dapp,
302
- assetFrom,
303
- assetTo,
304
- amountIn,
305
- slippage
306
- );
307
- swapTxData = iUniswapV2Router.encodeFunctionData(Transaction.SWAP, [
308
- amountIn,
309
- minAmountOut,
310
- [assetFrom, assetTo],
311
- this.address,
312
- deadline
313
- ]);
270
+ switch (dapp) {
271
+ case Dapp.ONEINCH:
272
+ const chainId = networkChainIdMap[this.network];
273
+ const protocols = await getOneInchProtocols(chainId);
274
+ const apiUrl = `https://api.1inch.exchange/v4.0/${chainId}/swap?fromTokenAddress=${assetFrom}&toTokenAddress=${assetTo}&amount=${amountIn.toString()}&fromAddress=${
275
+ this.address
276
+ }&destReceiver=${
277
+ this.address
278
+ }&slippage=${slippage.toString()}&disableEstimate=true${protocols}`;
279
+ const response = await axios.get(apiUrl);
280
+ swapTxData = response.data.tx.data;
281
+ break;
282
+ case Dapp.BALANCER:
283
+ swapTxData = await this.utils.getBalancerSwapTx(
284
+ this,
285
+ assetFrom,
286
+ assetTo,
287
+ amountIn,
288
+ slippage
289
+ );
290
+ break;
291
+ case Dapp.SYNTHETIX:
292
+ const iSynthetix = new ethers.utils.Interface(ISynthetix.abi);
293
+ const assets = [assetFrom, assetTo].map(asset =>
294
+ ethers.utils.formatBytes32String(asset)
295
+ );
296
+ const daoAddress = await this.factory.owner();
297
+ swapTxData = iSynthetix.encodeFunctionData(Transaction.SWAP_SYNTHS, [
298
+ assets[0],
299
+ amountIn,
300
+ assets[1],
301
+ daoAddress,
302
+ SYNTHETIX_TRACKING_CODE
303
+ ]);
304
+ break;
305
+ case Dapp.TOROS:
306
+ swapTxData = await getEasySwapperTxData(
307
+ this,
308
+ assetFrom,
309
+ assetTo,
310
+ ethers.BigNumber.from(amountIn),
311
+ slippage
312
+ );
313
+ break;
314
+ default:
315
+ const iUniswapV2Router = new ethers.utils.Interface(
316
+ IUniswapV2Router.abi
317
+ );
318
+ const minAmountOut = await this.utils.getMinAmountOut(
319
+ dapp,
320
+ assetFrom,
321
+ assetTo,
322
+ amountIn,
323
+ slippage
324
+ );
325
+ swapTxData = iUniswapV2Router.encodeFunctionData(Transaction.SWAP, [
326
+ amountIn,
327
+ minAmountOut,
328
+ [assetFrom, assetTo],
329
+ this.address,
330
+ deadline
331
+ ]);
314
332
  }
315
333
  const tx = await this.poolLogic.execTransaction(
316
334
  routerAddress[this.network][dapp],
@@ -821,7 +839,10 @@ export class Pool {
821
839
  feeAmount: FeeAmount,
822
840
  options: any = null
823
841
  ): Promise<any> {
824
- if ((!minPrice || !maxPrice) && (!minTick || !maxTick))
842
+ if (
843
+ (minPrice === null || maxPrice === null) &&
844
+ (minTick === null || maxTick === null)
845
+ )
825
846
  throw new Error("Need to provide price or tick range");
826
847
 
827
848
  const iNonfungiblePositionManager = new ethers.utils.Interface(
@@ -0,0 +1,25 @@
1
+ import { ethers, Pool } from "../..";
2
+
3
+ import IAaveV3PoolAddressProvider from "../../abi/IAaveV3PoolAddressProvider.json";
4
+ import IPriceOracle from "../../abi/IPriceOracle.json";
5
+
6
+ export async function getChainlinkPriceInUsd(
7
+ pool: Pool,
8
+ asset: string
9
+ ): Promise<ethers.BigNumber> {
10
+ //Workaround as Chainlink doesn't have feed registry on Polygon/Optimism
11
+ //Use oracle from Aave which uses Chainlink
12
+ const lendingPoolAddressProvider = new ethers.Contract(
13
+ "0xa97684ead0e402dC232d5A977953DF7ECBaB3CDb",
14
+ IAaveV3PoolAddressProvider.abi,
15
+ pool.signer
16
+ );
17
+
18
+ const priceOracleAddress = await lendingPoolAddressProvider.getPriceOracle();
19
+ const priceOracle = new ethers.Contract(
20
+ priceOracleAddress,
21
+ IPriceOracle.abi,
22
+ pool.signer
23
+ );
24
+ return await priceOracle.getAssetPrice(asset);
25
+ }
@@ -0,0 +1,18 @@
1
+ import axios from "axios";
2
+
3
+ const excludedProtocols = ["OPTIMISM_PMM6"]; //Clipper
4
+
5
+ export async function getOneInchProtocols(chainId: number): Promise<string> {
6
+ try {
7
+ const response = await axios.get(
8
+ `https://api.1inch.io/v4.0/${chainId}/liquidity-sources`
9
+ );
10
+ const protocols = response.data.protocols.map((e: { id: string }) => e.id);
11
+ const filteredProtocols = protocols.filter(
12
+ (e: string) => !excludedProtocols.includes(e)
13
+ );
14
+ return `&protocols=${filteredProtocols.join(",")}`;
15
+ } catch {
16
+ return "";
17
+ }
18
+ }
@@ -0,0 +1,110 @@
1
+ import { ethers } from "ethers";
2
+ import { Dapp, Pool } from "../..";
3
+
4
+ import IDhedgeEasySwapper from "../../abi/IDhedgeEasySwapper.json";
5
+ import { routerAddress } from "../../config";
6
+ import { getChainlinkPriceInUsd } from "../chainLink/price";
7
+ import { isPool, loadPool } from "./pool";
8
+
9
+ export async function getPoolDepositAsset(
10
+ pool: Pool,
11
+ poolAddress: string
12
+ ): Promise<string | undefined> {
13
+ const torosPool = await loadPool(pool, poolAddress);
14
+ const composition = await torosPool.getComposition();
15
+ return composition.find(e => e.isDeposit)?.asset;
16
+ }
17
+
18
+ export async function getTorosPoolTokenPrice(
19
+ pool: Pool,
20
+ poolAddress: string
21
+ ): Promise<ethers.BigNumber> {
22
+ const torosPool = await loadPool(pool, poolAddress);
23
+ return await torosPool.poolLogic.tokenPrice();
24
+ }
25
+
26
+ export async function getEasySwapperDepositQuote(
27
+ pool: Pool,
28
+ torosAsset: string,
29
+ investAsset: string,
30
+ depositAsset: string,
31
+ amountIn: ethers.BigNumber
32
+ ): Promise<ethers.BigNumber> {
33
+ const easySwapper = new ethers.Contract(
34
+ routerAddress[pool.network][Dapp.TOROS] as string,
35
+ IDhedgeEasySwapper.abi,
36
+ pool.signer
37
+ );
38
+
39
+ return await easySwapper.depositQuote(
40
+ torosAsset,
41
+ investAsset,
42
+ amountIn,
43
+ depositAsset
44
+ );
45
+ }
46
+
47
+ export async function getEasySwapperWithdrawalQuote(
48
+ pool: Pool,
49
+ torosAsset: string,
50
+ investAsset: string,
51
+ amountIn: ethers.BigNumber
52
+ ): Promise<ethers.BigNumber> {
53
+ const [torosTokenPrice, assetPrice, assetDecimals] = await Promise.all([
54
+ getTorosPoolTokenPrice(pool, torosAsset),
55
+ getChainlinkPriceInUsd(pool, investAsset),
56
+ pool.utils.getDecimals(investAsset)
57
+ ]);
58
+
59
+ return amountIn
60
+ .mul(torosTokenPrice)
61
+ .div(assetPrice)
62
+ .div(1e10)
63
+ .div(10 ** (18 - assetDecimals));
64
+ }
65
+
66
+ export async function getEasySwapperTxData(
67
+ pool: Pool,
68
+ assetFrom: string,
69
+ assetTo: string,
70
+ amountIn: ethers.BigNumber,
71
+ slippage: number
72
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
73
+ ): Promise<any> {
74
+ const isWithdrawal = await isPool(pool, assetFrom);
75
+ const [torosAsset, investAsset] = isWithdrawal
76
+ ? [assetFrom, assetTo]
77
+ : [assetTo, assetFrom];
78
+ const iDhedgeEasySwapper = new ethers.utils.Interface(IDhedgeEasySwapper.abi);
79
+ if (isWithdrawal) {
80
+ const minAmountOut = await getEasySwapperWithdrawalQuote(
81
+ pool,
82
+ torosAsset,
83
+ investAsset,
84
+ amountIn
85
+ );
86
+ return iDhedgeEasySwapper.encodeFunctionData("withdraw", [
87
+ torosAsset,
88
+ amountIn,
89
+ investAsset,
90
+ minAmountOut.mul(10000 - slippage * 100).div(10000)
91
+ ]);
92
+ } else {
93
+ const depositAsset = await getPoolDepositAsset(pool, torosAsset);
94
+ if (!depositAsset) throw new Error("no deposit assets");
95
+ const minAmountOut = await getEasySwapperDepositQuote(
96
+ pool,
97
+ torosAsset,
98
+ investAsset,
99
+ depositAsset,
100
+ amountIn
101
+ );
102
+ return iDhedgeEasySwapper.encodeFunctionData("deposit", [
103
+ torosAsset,
104
+ investAsset,
105
+ amountIn,
106
+ depositAsset,
107
+ minAmountOut.mul(10000 - slippage * 100).div(10000)
108
+ ]);
109
+ }
110
+ }
@@ -0,0 +1,14 @@
1
+ import { Dhedge, Pool } from "../..";
2
+
3
+ export async function loadPool(pool: Pool, poolAddress: string): Promise<Pool> {
4
+ const dhedge = new Dhedge(pool.signer, pool.network);
5
+ return await dhedge.loadPool(poolAddress);
6
+ }
7
+
8
+ export async function isPool(
9
+ pool: Pool,
10
+ poolAddress: string
11
+ ): Promise<boolean> {
12
+ const dhedge = new Dhedge(pool.signer, pool.network);
13
+ return await dhedge.validatePool(poolAddress);
14
+ }
@@ -1,6 +1,6 @@
1
1
  import { Dhedge } from "..";
2
2
  import { Dapp, Network } from "../types";
3
- import { TEST_POOL, USDC, WETH } from "./constants";
3
+ import { TEST_POOL, USDC, WBTC } from "./constants";
4
4
  import { getTxOptions } from "./txOptions";
5
5
 
6
6
  import { wallet } from "./wallet";
@@ -33,14 +33,14 @@ describe("pool", () => {
33
33
  // expect(result).not.toBe(null);
34
34
  // });
35
35
 
36
- it("trades 1 USDC into WETH on 1Inch", async () => {
36
+ it("trades 1 USDC into WBTC on 1Inch", async () => {
37
37
  let result;
38
38
  const pool = await dhedge.loadPool(TEST_POOL);
39
39
  try {
40
40
  result = await pool.trade(
41
41
  Dapp.ONEINCH,
42
42
  USDC,
43
- WETH,
43
+ WBTC,
44
44
  "1000000",
45
45
  0.5,
46
46
  options
@@ -1,10 +1,10 @@
1
1
  //Polygon
2
- export const USDC = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174";
3
- export const WETH = "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619";
4
- export const USDT = "0xc2132D05D31c914a87C6611C10748AEb04B58e8F";
5
- export const DAI = "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063";
6
- export const TUSD = "0x2e1ad108ff1d8c782fcbbb89aad783ac49586756";
7
- export const WBTC = "0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6";
2
+ // export const USDC = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174";
3
+ // //export const WETH = "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619";
4
+ // export const USDT = "0xc2132D05D31c914a87C6611C10748AEb04B58e8F";
5
+ // export const DAI = "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063";
6
+ // export const TUSD = "0x2e1ad108ff1d8c782fcbbb89aad783ac49586756";
7
+ // export const WBTC = "0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6";
8
8
  export const SUSHI = "0x0b3f868e0be5597d5db7feb59e1cadbb0fdda50a";
9
9
  export const WMATIC = "0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270";
10
10
  export const BAL = "0x9a71012B13CA4d3D0Cdc72A177DF3ef03b0E76A3";
@@ -16,10 +16,14 @@ export const STMATIC = "0x3A58a54C066FdC0f2D55FC9C89F0415C92eBf3C4";
16
16
  export const WMATIC_STMATIC_LP = "0xaF5E0B5425dE1F5a630A8cB5AA9D97B8141C908D";
17
17
  export const AARAKIS_WNATIC_STMATIC_GAUGE =
18
18
  "0x9928340f9E1aaAd7dF1D95E27bd9A5c715202a56";
19
+ export const ETHBULL3X = "0x460b60565cb73845d56564384ab84bf84c13e47d";
20
+ export const BTCBEAR2X = "0x3dbce2c8303609c17aa23b69ebe83c2f5c510ada";
19
21
 
20
22
  //Optimism
21
- // export const WETH = "0x4200000000000000000000000000000000000006";
22
- // export const USDC = "0x7F5c764cBc14f9669B88837ca1490cCa17c31607";
23
- // export const DAI = "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1";
23
+ export const WETH = "0x4200000000000000000000000000000000000006";
24
+ export const USDC = "0x7F5c764cBc14f9669B88837ca1490cCa17c31607";
25
+ export const DAI = "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1";
26
+ export const USDy = "0x1ec50880101022c11530a069690f5446d1464592";
27
+ export const WBTC = "0x68f180fcCe6836688e9084f035309E29Bf0A2095";
24
28
 
25
29
  export const TEST_POOL = "TEST_POOL";
@@ -0,0 +1,117 @@
1
+ import { Dhedge, ethers } from "..";
2
+ import {
3
+ getEasySwapperDepositQuote,
4
+ getEasySwapperWithdrawalQuote,
5
+ getPoolDepositAsset
6
+ } from "../services/toros/easySwapper";
7
+ import { Dapp, Network } from "../types";
8
+ import { BTCBEAR2X, ETHBULL3X, TEST_POOL, USDC, WBTC } from "./constants";
9
+ import { getTxOptions } from "./txOptions";
10
+
11
+ import { wallet } from "./wallet";
12
+
13
+ let dhedge: Dhedge;
14
+ let options: any;
15
+
16
+ jest.setTimeout(100000);
17
+
18
+ describe("pool", () => {
19
+ beforeAll(async () => {
20
+ dhedge = new Dhedge(wallet, Network.POLYGON);
21
+ options = await getTxOptions(Network.POLYGON);
22
+ });
23
+
24
+ it("gets Deposit Quote for ETHBULL3X invest with USDC", async () => {
25
+ let result;
26
+ const pool = await dhedge.loadPool(ETHBULL3X);
27
+ try {
28
+ const depositAsset = await getPoolDepositAsset(pool, ETHBULL3X);
29
+ if (!depositAsset) throw new Error("no deposit assets");
30
+
31
+ result = await getEasySwapperDepositQuote(
32
+ pool,
33
+ pool.address,
34
+ USDC,
35
+ depositAsset,
36
+ ethers.BigNumber.from("100000000")
37
+ );
38
+ console.log("deposit quote", result.toString());
39
+ } catch (e) {
40
+ console.log(e);
41
+ }
42
+ expect(result).not.toBe(null);
43
+ });
44
+
45
+ it("gets withdrawal Quote for BTCBEAR2X sell for WBTC", async () => {
46
+ let result;
47
+ const pool = await dhedge.loadPool(BTCBEAR2X);
48
+ try {
49
+ result = await getEasySwapperWithdrawalQuote(
50
+ pool,
51
+ pool.address,
52
+ WBTC,
53
+ ethers.BigNumber.from("9751590099507644982205")
54
+ );
55
+ console.log("withdrawal quote", result.toString());
56
+ } catch (e) {
57
+ console.log(e);
58
+ }
59
+ expect(result).not.toBe(null);
60
+ });
61
+
62
+ it("approves unlimited ETHBULL3X on Easyswapper", async () => {
63
+ let result;
64
+ const pool = await dhedge.loadPool(TEST_POOL);
65
+ try {
66
+ result = await pool.approve(
67
+ Dapp.TOROS,
68
+ ETHBULL3X,
69
+ ethers.constants.MaxInt256,
70
+ options
71
+ );
72
+ console.log(result);
73
+ } catch (e) {
74
+ console.log(e);
75
+ }
76
+ expect(result).not.toBe(null);
77
+ });
78
+
79
+ // it("buys ETHBULL3X for 1 USDC", async () => {
80
+ // let result;
81
+ // const pool = await dhedge.loadPool(TEST_POOL);
82
+ // try {
83
+ // result = await pool.trade(
84
+ // Dapp.TOROS,
85
+ // USDC,
86
+ // ETHBULL3X,
87
+ // "1000000",
88
+ // 0.5,
89
+ // options
90
+ // );
91
+ // console.log(result);
92
+ // } catch (e) {
93
+ // console.log(e);
94
+ // }
95
+ // expect(result).not.toBe(null);
96
+ // });
97
+
98
+ it("sells ETHBULL3X balance for USDC", async () => {
99
+ let result;
100
+ const pool = await dhedge.loadPool(TEST_POOL);
101
+ const balance = await pool.utils.getBalance(ETHBULL3X, pool.address);
102
+ try {
103
+ result = await pool.trade(
104
+ Dapp.TOROS,
105
+ ETHBULL3X,
106
+ USDC,
107
+ balance,
108
+ 3,
109
+ options
110
+ );
111
+ console.log(result);
112
+ } catch (e) {
113
+ console.log(e);
114
+ }
115
+ expect(result).not.toBe(null);
116
+ });
117
+ });
package/src/types.ts CHANGED
@@ -14,7 +14,8 @@ export enum Dapp {
14
14
  UNISWAPV3 = "uniswapV3",
15
15
  SYNTHETIX = "synthetix",
16
16
  AAVEV3 = "aavev3",
17
- ARRAKIS = "arrakis"
17
+ ARRAKIS = "arrakis",
18
+ TOROS = "toros"
18
19
  }
19
20
 
20
21
  export enum Transaction {