@dhedge/v2-sdk 1.2.1 → 1.3.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.
- package/dist/config.d.ts +5 -0
- package/dist/entities/pool.d.ts +64 -2
- package/dist/entities/utils.d.ts +6 -0
- package/dist/services/uniswap/V3Liquidity.d.ts +9 -0
- package/dist/services/uniswap/V3Trade.d.ts +4 -0
- package/dist/services/uniswap/types.d.ts +3 -0
- package/dist/test/constants.d.ts +3 -3
- package/dist/test/txOptions.d.ts +1 -0
- package/dist/types.d.ts +16 -5
- package/dist/v2-sdk.cjs.development.js +3838 -1477
- 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 +3839 -1478
- package/dist/v2-sdk.esm.js.map +1 -1
- package/package.json +4 -1
- package/src/abi/IERC20.json +15 -1
- package/src/abi/INonfungiblePositionManager.json +1221 -0
- package/src/abi/ISynthetix.json +139 -0
- package/src/abi/IUniswapV3Quoter.json +195 -0
- package/src/abi/IUniswapV3Router.json +221 -0
- package/src/config.ts +31 -8
- package/src/entities/dhedge.ts +4 -2
- package/src/entities/pool.ts +254 -26
- package/src/entities/utils.ts +12 -0
- package/src/services/uniswap/V3Liquidity.ts +134 -0
- package/src/services/uniswap/V3Trade.ts +47 -0
- package/src/services/uniswap/types.ts +16 -0
- package/src/test/constants.ts +7 -3
- package/src/test/pool.test.ts +1 -1
- package/src/test/synthetix.test.ts +34 -0
- package/src/test/txOptions.ts +15 -0
- package/src/test/uniswap.test.ts +127 -0
- package/src/test/wallet.ts +5 -1
- package/src/types.ts +16 -5
package/src/entities/pool.ts
CHANGED
|
@@ -6,10 +6,19 @@ import { Contract, ethers, Wallet, BigNumber } from "ethers";
|
|
|
6
6
|
import IERC20 from "../abi/IERC20.json";
|
|
7
7
|
import IMiniChefV2 from "../abi/IMiniChefV2.json";
|
|
8
8
|
import ILendingPool from "../abi/ILendingPool.json";
|
|
9
|
+
import ISynthetix from "../abi/ISynthetix.json";
|
|
9
10
|
import IUniswapV2Router from "../abi/IUniswapV2Router.json";
|
|
11
|
+
import INonfungiblePositionManager from "../abi/INonfungiblePositionManager.json";
|
|
10
12
|
import IBalancerMerkleOrchard from "../abi/IBalancerMerkleOrchard.json";
|
|
11
13
|
import IAaveIncentivesController from "../abi/IAaveIncentivesController.json";
|
|
12
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
deadline,
|
|
16
|
+
MaxUint128,
|
|
17
|
+
nonfungiblePositionManagerAddress,
|
|
18
|
+
routerAddress,
|
|
19
|
+
stakingAddress,
|
|
20
|
+
SYNTHETIX_TRACKING_CODE
|
|
21
|
+
} from "../config";
|
|
13
22
|
import {
|
|
14
23
|
Dapp,
|
|
15
24
|
Transaction,
|
|
@@ -20,10 +29,17 @@ import {
|
|
|
20
29
|
|
|
21
30
|
import { Utils } from "./utils";
|
|
22
31
|
import { ClaimService } from "../services/claim-balancer/claim.service";
|
|
32
|
+
import {
|
|
33
|
+
getUniswapV3Liquidity,
|
|
34
|
+
getUniswapV3MintParams
|
|
35
|
+
} from "../services/uniswap/V3Liquidity";
|
|
36
|
+
import { FeeAmount } from "@uniswap/v3-sdk";
|
|
37
|
+
import { getUniswapV3SwapTxData } from "../services/uniswap/V3Trade";
|
|
23
38
|
|
|
24
39
|
export class Pool {
|
|
25
40
|
public readonly poolLogic: Contract;
|
|
26
41
|
public readonly managerLogic: Contract;
|
|
42
|
+
public readonly factory: Contract;
|
|
27
43
|
public readonly signer: Wallet;
|
|
28
44
|
public readonly address: string;
|
|
29
45
|
public readonly utils: Utils;
|
|
@@ -34,7 +50,8 @@ export class Pool {
|
|
|
34
50
|
signer: Wallet,
|
|
35
51
|
poolLogic: Contract,
|
|
36
52
|
mangerLogic: Contract,
|
|
37
|
-
utils: Utils
|
|
53
|
+
utils: Utils,
|
|
54
|
+
factory: Contract
|
|
38
55
|
) {
|
|
39
56
|
this.network = network;
|
|
40
57
|
this.poolLogic = poolLogic;
|
|
@@ -42,6 +59,7 @@ export class Pool {
|
|
|
42
59
|
this.managerLogic = mangerLogic;
|
|
43
60
|
this.signer = signer;
|
|
44
61
|
this.utils = utils;
|
|
62
|
+
this.factory = factory;
|
|
45
63
|
}
|
|
46
64
|
|
|
47
65
|
/**
|
|
@@ -170,13 +188,39 @@ export class Pool {
|
|
|
170
188
|
return tx;
|
|
171
189
|
}
|
|
172
190
|
|
|
191
|
+
/**
|
|
192
|
+
* Approve the liquidity pool token for staking
|
|
193
|
+
* @param {Dapp} dapp Platform like Sushiswap or Uniswap
|
|
194
|
+
* @param {string} asset Address of liquidity pool token
|
|
195
|
+
* @param {BigNumber | string} amount Aamount to be approved
|
|
196
|
+
* @param {any} options Transaction options
|
|
197
|
+
* @returns {Promise<any>} Transaction
|
|
198
|
+
*/
|
|
199
|
+
async approveUniswapV3Liquidity(
|
|
200
|
+
asset: string,
|
|
201
|
+
amount: BigNumber | string,
|
|
202
|
+
options: any = null
|
|
203
|
+
): Promise<any> {
|
|
204
|
+
const iERC20 = new ethers.utils.Interface(IERC20.abi);
|
|
205
|
+
const approveTxData = iERC20.encodeFunctionData("approve", [
|
|
206
|
+
nonfungiblePositionManagerAddress[this.network],
|
|
207
|
+
amount
|
|
208
|
+
]);
|
|
209
|
+
const tx = await this.poolLogic.execTransaction(
|
|
210
|
+
asset,
|
|
211
|
+
approveTxData,
|
|
212
|
+
options
|
|
213
|
+
);
|
|
214
|
+
return tx;
|
|
215
|
+
}
|
|
216
|
+
|
|
173
217
|
/**
|
|
174
218
|
* Trade an asset into another asset
|
|
175
219
|
* @param {Dapp} dapp Platform like Sushiswap or Uniswap
|
|
176
220
|
* @param {string} assetFrom Asset to trade from
|
|
177
221
|
* @param {string} assetTo Asset to trade into
|
|
178
222
|
* @param {BigNumber | string} amountIn Amount
|
|
179
|
-
* @param {
|
|
223
|
+
* @param {number} slippage Slippage tolerance in %
|
|
180
224
|
* @param {any} options Transaction options
|
|
181
225
|
* @returns {Promise<any>} Transaction
|
|
182
226
|
*/
|
|
@@ -205,6 +249,19 @@ export class Pool {
|
|
|
205
249
|
amountIn,
|
|
206
250
|
slippage
|
|
207
251
|
);
|
|
252
|
+
} else if (dapp === Dapp.SYNTHETIX) {
|
|
253
|
+
const iSynthetix = new ethers.utils.Interface(ISynthetix.abi);
|
|
254
|
+
const assets = [assetFrom, assetTo].map(asset =>
|
|
255
|
+
ethers.utils.formatBytes32String(asset)
|
|
256
|
+
);
|
|
257
|
+
const daoAddress = await this.factory.owner();
|
|
258
|
+
swapTxData = iSynthetix.encodeFunctionData(Transaction.SWAP_SYNTHS, [
|
|
259
|
+
assets[0],
|
|
260
|
+
amountIn,
|
|
261
|
+
assets[1],
|
|
262
|
+
daoAddress,
|
|
263
|
+
SYNTHETIX_TRACKING_CODE
|
|
264
|
+
]);
|
|
208
265
|
} else {
|
|
209
266
|
const iUniswapV2Router = new ethers.utils.Interface(IUniswapV2Router.abi);
|
|
210
267
|
const minAmountOut = await this.utils.getMinAmountOut(
|
|
@@ -219,7 +276,7 @@ export class Pool {
|
|
|
219
276
|
minAmountOut,
|
|
220
277
|
[assetFrom, assetTo],
|
|
221
278
|
this.address,
|
|
222
|
-
|
|
279
|
+
deadline
|
|
223
280
|
]);
|
|
224
281
|
}
|
|
225
282
|
const tx = await this.poolLogic.execTransaction(
|
|
@@ -251,16 +308,7 @@ export class Pool {
|
|
|
251
308
|
const iUniswapV2Router = new ethers.utils.Interface(IUniswapV2Router.abi);
|
|
252
309
|
const addLiquidityTxData = iUniswapV2Router.encodeFunctionData(
|
|
253
310
|
Transaction.ADD_LIQUIDITY,
|
|
254
|
-
[
|
|
255
|
-
assetA,
|
|
256
|
-
assetB,
|
|
257
|
-
amountA,
|
|
258
|
-
amountB,
|
|
259
|
-
0,
|
|
260
|
-
0,
|
|
261
|
-
this.address,
|
|
262
|
-
Math.floor(Date.now() / 1000) + 60 * 20 // 20 minutes from the current Unix time
|
|
263
|
-
]
|
|
311
|
+
[assetA, assetB, amountA, amountB, 0, 0, this.address, deadline]
|
|
264
312
|
);
|
|
265
313
|
const tx = await this.poolLogic.execTransaction(
|
|
266
314
|
routerAddress[this.network][dapp],
|
|
@@ -289,15 +337,7 @@ export class Pool {
|
|
|
289
337
|
const iUniswapV2Router = new ethers.utils.Interface(IUniswapV2Router.abi);
|
|
290
338
|
const removeLiquidityTxData = iUniswapV2Router.encodeFunctionData(
|
|
291
339
|
Transaction.REMOVE_LIQUIDITY,
|
|
292
|
-
[
|
|
293
|
-
assetA,
|
|
294
|
-
assetB,
|
|
295
|
-
amount,
|
|
296
|
-
0,
|
|
297
|
-
0,
|
|
298
|
-
this.address,
|
|
299
|
-
Math.floor(Date.now() / 1000) + 60 * 20 // 20 minutes from the current Unix time
|
|
300
|
-
]
|
|
340
|
+
[assetA, assetB, amount, 0, 0, this.address, deadline]
|
|
301
341
|
);
|
|
302
342
|
const tx = await this.poolLogic.execTransaction(
|
|
303
343
|
routerAddress[this.network][dapp],
|
|
@@ -657,16 +697,13 @@ export class Pool {
|
|
|
657
697
|
iAaveIncentivesController,
|
|
658
698
|
this.signer
|
|
659
699
|
);
|
|
660
|
-
|
|
661
700
|
const amount = await aaveIncentivesController.getUserUnclaimedRewards(
|
|
662
701
|
this.address
|
|
663
702
|
);
|
|
664
|
-
|
|
665
703
|
const claimTxData = iAaveIncentivesController.encodeFunctionData(
|
|
666
704
|
Transaction.CLAIM_REWARDS,
|
|
667
705
|
[assets, amount, this.address]
|
|
668
706
|
);
|
|
669
|
-
|
|
670
707
|
const tx = await this.poolLogic.execTransaction(
|
|
671
708
|
aaveIncentivesAddress,
|
|
672
709
|
claimTxData,
|
|
@@ -674,4 +711,195 @@ export class Pool {
|
|
|
674
711
|
);
|
|
675
712
|
return tx;
|
|
676
713
|
}
|
|
714
|
+
|
|
715
|
+
/**
|
|
716
|
+
* Create UniswapV3 liquidity pool
|
|
717
|
+
* @param {string} assetA First asset
|
|
718
|
+
* @param {string} assetB Second asset
|
|
719
|
+
* @param {BigNumber | string} amountA Amount first asset
|
|
720
|
+
* @param {BigNumber | string} amountB Amount second asset
|
|
721
|
+
* @param { number } minPrice Lower price range (assetB per assetA)
|
|
722
|
+
* @param { number } maxPrice Upper price range (assetB per assetA)
|
|
723
|
+
* @param { number } minTick Lower tick range
|
|
724
|
+
* @param { number } maxTick Upper tick range
|
|
725
|
+
* @param { FeeAmount } feeAmount Fee tier (Low 0.05%, Medium 0.3%, High 1%)
|
|
726
|
+
* @param {any} options Transaction options
|
|
727
|
+
* @returns {Promise<any>} Transaction
|
|
728
|
+
*/
|
|
729
|
+
async addLiquidityUniswapV3(
|
|
730
|
+
assetA: string,
|
|
731
|
+
assetB: string,
|
|
732
|
+
amountA: BigNumber | string,
|
|
733
|
+
amountB: BigNumber | string,
|
|
734
|
+
minPrice: number | null,
|
|
735
|
+
maxPrice: number | null,
|
|
736
|
+
minTick: number | null,
|
|
737
|
+
maxTick: number | null,
|
|
738
|
+
feeAmount: FeeAmount,
|
|
739
|
+
options: any = null
|
|
740
|
+
): Promise<any> {
|
|
741
|
+
if ((!minPrice || !maxPrice) && (!minTick || !maxTick))
|
|
742
|
+
throw new Error("Need to provide price or tick range");
|
|
743
|
+
|
|
744
|
+
const iNonfungiblePositionManager = new ethers.utils.Interface(
|
|
745
|
+
INonfungiblePositionManager.abi
|
|
746
|
+
);
|
|
747
|
+
|
|
748
|
+
const mintTxParams = await getUniswapV3MintParams(
|
|
749
|
+
this,
|
|
750
|
+
assetA,
|
|
751
|
+
assetB,
|
|
752
|
+
amountA,
|
|
753
|
+
amountB,
|
|
754
|
+
minPrice,
|
|
755
|
+
maxPrice,
|
|
756
|
+
minTick,
|
|
757
|
+
maxTick,
|
|
758
|
+
feeAmount
|
|
759
|
+
);
|
|
760
|
+
const mintTxData = iNonfungiblePositionManager.encodeFunctionData(
|
|
761
|
+
Transaction.MINT,
|
|
762
|
+
[mintTxParams]
|
|
763
|
+
);
|
|
764
|
+
const tx = await this.poolLogic.execTransaction(
|
|
765
|
+
nonfungiblePositionManagerAddress[this.network],
|
|
766
|
+
mintTxData,
|
|
767
|
+
options
|
|
768
|
+
);
|
|
769
|
+
return tx;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
/**
|
|
773
|
+
* Remove liquidity from an UniswapV3 liquidity pool
|
|
774
|
+
* @param {string} tokenId Token Id of UniswapV3 position
|
|
775
|
+
* @param {number} amount Amount in percent of assets to be removed
|
|
776
|
+
* @param {any} options Transaction options
|
|
777
|
+
* @returns {Promise<any>} Transaction
|
|
778
|
+
*/
|
|
779
|
+
async removeLiquidityUniswapV3(
|
|
780
|
+
tokenId: string,
|
|
781
|
+
amount = 100,
|
|
782
|
+
options: any = null
|
|
783
|
+
): Promise<any> {
|
|
784
|
+
const iNonfungiblePositionManager = new ethers.utils.Interface(
|
|
785
|
+
INonfungiblePositionManager.abi
|
|
786
|
+
);
|
|
787
|
+
const liquidity = (await getUniswapV3Liquidity(tokenId, this))
|
|
788
|
+
.mul(amount)
|
|
789
|
+
.div(100);
|
|
790
|
+
const decreaseLiquidityTxData = iNonfungiblePositionManager.encodeFunctionData(
|
|
791
|
+
Transaction.DECREASE_LIQUIDITY,
|
|
792
|
+
[[tokenId, liquidity, 0, 0, deadline]]
|
|
793
|
+
);
|
|
794
|
+
const collectTxData = iNonfungiblePositionManager.encodeFunctionData(
|
|
795
|
+
Transaction.COLLECT,
|
|
796
|
+
[[tokenId, this.address, MaxUint128, MaxUint128]]
|
|
797
|
+
);
|
|
798
|
+
|
|
799
|
+
const multicallParams = [decreaseLiquidityTxData, collectTxData];
|
|
800
|
+
|
|
801
|
+
if (amount === 100) {
|
|
802
|
+
const burnTxData = iNonfungiblePositionManager.encodeFunctionData(
|
|
803
|
+
Transaction.BURN,
|
|
804
|
+
[tokenId]
|
|
805
|
+
);
|
|
806
|
+
multicallParams.push(burnTxData);
|
|
807
|
+
}
|
|
808
|
+
const multicallTxData = iNonfungiblePositionManager.encodeFunctionData(
|
|
809
|
+
Transaction.MULTI_CALL,
|
|
810
|
+
[multicallParams]
|
|
811
|
+
);
|
|
812
|
+
const tx = await this.poolLogic.execTransaction(
|
|
813
|
+
nonfungiblePositionManagerAddress[this.network],
|
|
814
|
+
multicallTxData,
|
|
815
|
+
options
|
|
816
|
+
);
|
|
817
|
+
return tx;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
/**
|
|
821
|
+
* Increase liquidity of an UniswapV3 liquidity pool
|
|
822
|
+
* @param {string} tokenId Token Id of UniswapV3 position
|
|
823
|
+
* @param {BigNumber | string} amountA Amount first asset
|
|
824
|
+
* @param {BigNumber | string} amountB Amount second asset
|
|
825
|
+
* @param {any} options Transaction options
|
|
826
|
+
* @returns {Promise<any>} Transaction
|
|
827
|
+
*/
|
|
828
|
+
async increaseLiquidityUniswapV3(
|
|
829
|
+
tokenId: string,
|
|
830
|
+
amountA: BigNumber | string,
|
|
831
|
+
amountB: BigNumber | string,
|
|
832
|
+
options: any = null
|
|
833
|
+
): Promise<any> {
|
|
834
|
+
const iNonfungiblePositionManager = new ethers.utils.Interface(
|
|
835
|
+
INonfungiblePositionManager.abi
|
|
836
|
+
);
|
|
837
|
+
const increaseLiquidityTxData = iNonfungiblePositionManager.encodeFunctionData(
|
|
838
|
+
Transaction.INCREASE_LIQUIDITY,
|
|
839
|
+
[[tokenId, amountA, amountB, 0, 0, deadline]]
|
|
840
|
+
);
|
|
841
|
+
const tx = await this.poolLogic.execTransaction(
|
|
842
|
+
nonfungiblePositionManagerAddress[this.network],
|
|
843
|
+
increaseLiquidityTxData,
|
|
844
|
+
options
|
|
845
|
+
);
|
|
846
|
+
return tx;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
/**
|
|
850
|
+
* Claim fees of an UniswapV3 liquidity pool
|
|
851
|
+
* @param {string} tokenId Token Id of UniswapV3 position
|
|
852
|
+
* @param {any} options Transaction options
|
|
853
|
+
* @returns {Promise<any>} Transaction
|
|
854
|
+
*/
|
|
855
|
+
async claimFeesUniswapV3(tokenId: string, options: any = null): Promise<any> {
|
|
856
|
+
const iNonfungiblePositionManager = new ethers.utils.Interface(
|
|
857
|
+
INonfungiblePositionManager.abi
|
|
858
|
+
);
|
|
859
|
+
const collectTxData = iNonfungiblePositionManager.encodeFunctionData(
|
|
860
|
+
Transaction.COLLECT,
|
|
861
|
+
[[tokenId, this.address, MaxUint128, MaxUint128]]
|
|
862
|
+
);
|
|
863
|
+
const tx = await this.poolLogic.execTransaction(
|
|
864
|
+
nonfungiblePositionManagerAddress[this.network],
|
|
865
|
+
collectTxData,
|
|
866
|
+
options
|
|
867
|
+
);
|
|
868
|
+
return tx;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
/**
|
|
872
|
+
* Trade an asset into another asset
|
|
873
|
+
* @param {Dapp} dapp Platform like Sushiswap or Uniswap
|
|
874
|
+
* @param {string} assetFrom Asset to trade from
|
|
875
|
+
* @param {string} assetTo Asset to trade into
|
|
876
|
+
* @param {BigNumber | string} amountIn Amount
|
|
877
|
+
* @param { FeeAmount } feeAmount Fee tier (Low 0.05%, Medium 0.3%, High 1%)
|
|
878
|
+
* @param {number} slippage Slippage tolerance in %
|
|
879
|
+
* @param {any} options Transaction options
|
|
880
|
+
* @returns {Promise<any>} Transaction
|
|
881
|
+
*/
|
|
882
|
+
async tradeUniswapV3(
|
|
883
|
+
assetFrom: string,
|
|
884
|
+
assetTo: string,
|
|
885
|
+
amountIn: BigNumber | string,
|
|
886
|
+
feeAmount: FeeAmount,
|
|
887
|
+
slippage = 0.5,
|
|
888
|
+
options: any = null
|
|
889
|
+
): Promise<any> {
|
|
890
|
+
const swapxData = await getUniswapV3SwapTxData(
|
|
891
|
+
this,
|
|
892
|
+
assetFrom,
|
|
893
|
+
assetTo,
|
|
894
|
+
amountIn,
|
|
895
|
+
slippage,
|
|
896
|
+
feeAmount
|
|
897
|
+
);
|
|
898
|
+
const tx = await this.poolLogic.execTransaction(
|
|
899
|
+
routerAddress[this.network][Dapp.UNISWAPV3],
|
|
900
|
+
swapxData,
|
|
901
|
+
options
|
|
902
|
+
);
|
|
903
|
+
return tx;
|
|
904
|
+
}
|
|
677
905
|
}
|
package/src/entities/utils.ts
CHANGED
|
@@ -125,6 +125,18 @@ export class Utils {
|
|
|
125
125
|
return balance;
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Returns the decimals of an asset (ERC20) token
|
|
130
|
+
* @param {string} asset string token address
|
|
131
|
+
* @returns { number } Balance of asset
|
|
132
|
+
*/
|
|
133
|
+
|
|
134
|
+
async getDecimals(asset: string): Promise<number> {
|
|
135
|
+
const iERC20 = new ethers.Contract(asset, IERC20.abi, this.signer);
|
|
136
|
+
const decimals = await iERC20.decimals();
|
|
137
|
+
return decimals;
|
|
138
|
+
}
|
|
139
|
+
|
|
128
140
|
/**
|
|
129
141
|
* Return the minimum amount out for a trade between two assets
|
|
130
142
|
* given the trade amount and slippage
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { Price, Token } from "@uniswap/sdk-core";
|
|
2
|
+
import {
|
|
3
|
+
encodeSqrtRatioX96,
|
|
4
|
+
FeeAmount,
|
|
5
|
+
nearestUsableTick,
|
|
6
|
+
priceToClosestTick,
|
|
7
|
+
TICK_SPACINGS,
|
|
8
|
+
TickMath
|
|
9
|
+
} from "@uniswap/v3-sdk";
|
|
10
|
+
import { ethers } from "ethers";
|
|
11
|
+
import JSBI from "jsbi";
|
|
12
|
+
import { Pool } from "../..";
|
|
13
|
+
import {
|
|
14
|
+
networkChainIdMap,
|
|
15
|
+
nonfungiblePositionManagerAddress
|
|
16
|
+
} from "../../config";
|
|
17
|
+
import { UniswapV3MintParams } from "./types";
|
|
18
|
+
import INonfungiblePositionManager from "../../abi/INonfungiblePositionManager.json";
|
|
19
|
+
|
|
20
|
+
export function tryParsePrice(
|
|
21
|
+
baseToken: Token,
|
|
22
|
+
quoteToken: Token,
|
|
23
|
+
value: string
|
|
24
|
+
): Price<Token, Token> {
|
|
25
|
+
const [whole, fraction] = value.split(".");
|
|
26
|
+
|
|
27
|
+
const decimals = fraction?.length ?? 0;
|
|
28
|
+
const withoutDecimals = JSBI.BigInt((whole ?? "") + (fraction ?? ""));
|
|
29
|
+
|
|
30
|
+
return new Price(
|
|
31
|
+
baseToken,
|
|
32
|
+
quoteToken,
|
|
33
|
+
JSBI.multiply(
|
|
34
|
+
JSBI.BigInt(10 ** decimals),
|
|
35
|
+
JSBI.BigInt(10 ** baseToken.decimals)
|
|
36
|
+
),
|
|
37
|
+
JSBI.multiply(withoutDecimals, JSBI.BigInt(10 ** quoteToken.decimals))
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function tryParseTick(
|
|
42
|
+
baseToken: Token,
|
|
43
|
+
quoteToken: Token,
|
|
44
|
+
feeAmount: FeeAmount,
|
|
45
|
+
value: string
|
|
46
|
+
): number {
|
|
47
|
+
const price = tryParsePrice(baseToken, quoteToken, value);
|
|
48
|
+
|
|
49
|
+
let tick: number;
|
|
50
|
+
|
|
51
|
+
// check price is within min/max bounds, if outside return min/max
|
|
52
|
+
const sqrtRatioX96 = encodeSqrtRatioX96(price.numerator, price.denominator);
|
|
53
|
+
|
|
54
|
+
if (JSBI.greaterThanOrEqual(sqrtRatioX96, TickMath.MAX_SQRT_RATIO)) {
|
|
55
|
+
tick = TickMath.MAX_TICK;
|
|
56
|
+
} else if (JSBI.lessThanOrEqual(sqrtRatioX96, TickMath.MIN_SQRT_RATIO)) {
|
|
57
|
+
tick = TickMath.MIN_TICK;
|
|
58
|
+
} else {
|
|
59
|
+
// this function is agnostic to the base, will always return the correct tick
|
|
60
|
+
tick = priceToClosestTick(price);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return nearestUsableTick(tick, TICK_SPACINGS[feeAmount]);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function getUniswapV3MintParams(
|
|
67
|
+
pool: Pool,
|
|
68
|
+
assetA: string,
|
|
69
|
+
assetB: string,
|
|
70
|
+
amountA: string | ethers.BigNumber,
|
|
71
|
+
amountB: string | ethers.BigNumber,
|
|
72
|
+
minPrice: number | null,
|
|
73
|
+
maxPrice: number | null,
|
|
74
|
+
minTick: number | null,
|
|
75
|
+
maxTick: number | null,
|
|
76
|
+
feeAmount: FeeAmount
|
|
77
|
+
): Promise<UniswapV3MintParams> {
|
|
78
|
+
let tickLower = 0;
|
|
79
|
+
let tickUpper = 0;
|
|
80
|
+
const chainId = networkChainIdMap[pool.network];
|
|
81
|
+
const decimals = await Promise.all(
|
|
82
|
+
[assetA, assetB].map(asset => pool.utils.getDecimals(asset))
|
|
83
|
+
);
|
|
84
|
+
const tokenA = new Token(chainId, assetA, decimals[0]);
|
|
85
|
+
const tokenB = new Token(chainId, assetB, decimals[1]);
|
|
86
|
+
const [token0, token1] = tokenA.sortsBefore(tokenB)
|
|
87
|
+
? [tokenA, tokenB]
|
|
88
|
+
: [tokenB, tokenA];
|
|
89
|
+
const invertPrice = !tokenA.equals(token0);
|
|
90
|
+
|
|
91
|
+
if (minPrice && maxPrice) {
|
|
92
|
+
tickLower = invertPrice
|
|
93
|
+
? tryParseTick(token1, token0, feeAmount, maxPrice.toString())
|
|
94
|
+
: tryParseTick(token0, token1, feeAmount, minPrice.toString());
|
|
95
|
+
tickUpper = invertPrice
|
|
96
|
+
? tryParseTick(token1, token0, feeAmount, minPrice.toString())
|
|
97
|
+
: tryParseTick(token0, token1, feeAmount, maxPrice.toString());
|
|
98
|
+
} else if (minTick && maxTick) {
|
|
99
|
+
tickLower = minTick;
|
|
100
|
+
tickUpper = maxTick;
|
|
101
|
+
}
|
|
102
|
+
const [amount0, amount1] = invertPrice
|
|
103
|
+
? [amountB, amountA]
|
|
104
|
+
: [amountA, amountB];
|
|
105
|
+
|
|
106
|
+
const deadline = Math.floor(Date.now() / 1000) + 60 * 20;
|
|
107
|
+
|
|
108
|
+
return [
|
|
109
|
+
token0.address,
|
|
110
|
+
token1.address,
|
|
111
|
+
feeAmount,
|
|
112
|
+
tickLower,
|
|
113
|
+
tickUpper,
|
|
114
|
+
amount0,
|
|
115
|
+
amount1,
|
|
116
|
+
"0",
|
|
117
|
+
"0",
|
|
118
|
+
pool.address,
|
|
119
|
+
deadline
|
|
120
|
+
];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export async function getUniswapV3Liquidity(
|
|
124
|
+
tokenId: string,
|
|
125
|
+
pool: Pool
|
|
126
|
+
): Promise<ethers.BigNumber> {
|
|
127
|
+
const iNonfungiblePositionManager = new ethers.Contract(
|
|
128
|
+
nonfungiblePositionManagerAddress[pool.network],
|
|
129
|
+
INonfungiblePositionManager.abi,
|
|
130
|
+
pool.signer
|
|
131
|
+
);
|
|
132
|
+
const result = await iNonfungiblePositionManager.positions(tokenId);
|
|
133
|
+
return result.liquidity;
|
|
134
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { FeeAmount } from "@uniswap/v3-sdk";
|
|
2
|
+
import { ethers } from "ethers";
|
|
3
|
+
import { Pool } from "../..";
|
|
4
|
+
|
|
5
|
+
import IUniswapV3Router from "../../abi/IUniswapV3Router.json";
|
|
6
|
+
import IUniswapV3Quoter from "../../abi/IUniswapV3Quoter.json";
|
|
7
|
+
import { UNISWAPV3_QUOTER_ADDRESS } from "../../config";
|
|
8
|
+
|
|
9
|
+
export async function getUniswapV3SwapTxData(
|
|
10
|
+
pool: Pool,
|
|
11
|
+
assetA: string,
|
|
12
|
+
assetB: string,
|
|
13
|
+
amountIn: string | ethers.BigNumber,
|
|
14
|
+
slippage: number,
|
|
15
|
+
feeAmount: FeeAmount
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17
|
+
): Promise<any> {
|
|
18
|
+
const quoterContract = new ethers.Contract(
|
|
19
|
+
UNISWAPV3_QUOTER_ADDRESS,
|
|
20
|
+
IUniswapV3Quoter.abi,
|
|
21
|
+
pool.signer
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
const quotedAmountOut: ethers.BigNumber = await quoterContract.callStatic.quoteExactInputSingle(
|
|
25
|
+
assetA,
|
|
26
|
+
assetB,
|
|
27
|
+
feeAmount,
|
|
28
|
+
amountIn.toString(),
|
|
29
|
+
0
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const minAmountOut = quotedAmountOut.mul((100 - slippage) * 100).div(10000);
|
|
33
|
+
|
|
34
|
+
const iUniswapV3Router = new ethers.utils.Interface(IUniswapV3Router.abi);
|
|
35
|
+
const swapTx = iUniswapV3Router.encodeFunctionData("exactInputSingle", [
|
|
36
|
+
[
|
|
37
|
+
assetA,
|
|
38
|
+
assetB,
|
|
39
|
+
feeAmount,
|
|
40
|
+
pool.address,
|
|
41
|
+
amountIn.toString(),
|
|
42
|
+
minAmountOut.toString(),
|
|
43
|
+
0
|
|
44
|
+
]
|
|
45
|
+
]);
|
|
46
|
+
return swapTx;
|
|
47
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { FeeAmount } from "@uniswap/v3-sdk";
|
|
2
|
+
import { ethers } from "ethers";
|
|
3
|
+
|
|
4
|
+
export type UniswapV3MintParams = [
|
|
5
|
+
string,
|
|
6
|
+
string,
|
|
7
|
+
FeeAmount,
|
|
8
|
+
number,
|
|
9
|
+
number,
|
|
10
|
+
ethers.BigNumber | string,
|
|
11
|
+
ethers.BigNumber | string,
|
|
12
|
+
ethers.BigNumber | string,
|
|
13
|
+
ethers.BigNumber | string,
|
|
14
|
+
string,
|
|
15
|
+
number
|
|
16
|
+
];
|
package/src/test/constants.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
export const USDC = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174";
|
|
1
|
+
//export const USDC = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174";
|
|
2
|
+
//Optimism
|
|
3
|
+
export const USDC = "0x7F5c764cBc14f9669B88837ca1490cCa17c31607";
|
|
2
4
|
export const USDT = "0xc2132D05D31c914a87C6611C10748AEb04B58e8F";
|
|
3
5
|
export const DAI = "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063";
|
|
4
6
|
export const TUSD = "0x2e1ad108ff1d8c782fcbbb89aad783ac49586756";
|
|
5
|
-
export const WETH = "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619";
|
|
7
|
+
//export const WETH = "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619";
|
|
8
|
+
//Optimism
|
|
9
|
+
export const WETH = "0x4200000000000000000000000000000000000006";
|
|
6
10
|
export const WBTC = "0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6";
|
|
7
11
|
export const SUSHI = "0x0b3f868e0be5597d5db7feb59e1cadbb0fdda50a";
|
|
8
12
|
export const WMATIC = "0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270";
|
|
@@ -10,4 +14,4 @@ export const BAL = "0x9a71012B13CA4d3D0Cdc72A177DF3ef03b0E76A3";
|
|
|
10
14
|
export const AMUSDC = "0x1a13f4ca1d028320a707d99520abfefca3998b7f";
|
|
11
15
|
export const VDEBTWETH = "0xede17e9d79fc6f9ff9250d9eefbdb88cc18038b5";
|
|
12
16
|
|
|
13
|
-
export const TEST_POOL = "
|
|
17
|
+
export const TEST_POOL = "0xf36f550907872faaa02477f791df3ce33fe38854";
|
package/src/test/pool.test.ts
CHANGED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { Dhedge } from "..";
|
|
3
|
+
import { Dapp, Network } from "../types";
|
|
4
|
+
import { TEST_POOL } from "./constants";
|
|
5
|
+
//import { getTxOptions } from "./txOptions";
|
|
6
|
+
|
|
7
|
+
import { wallet } from "./wallet";
|
|
8
|
+
|
|
9
|
+
let dhedge: Dhedge;
|
|
10
|
+
let options: any;
|
|
11
|
+
jest.setTimeout(100000);
|
|
12
|
+
|
|
13
|
+
describe("pool", () => {
|
|
14
|
+
beforeAll(async () => {
|
|
15
|
+
dhedge = new Dhedge(wallet, Network.OPTIMISM);
|
|
16
|
+
//options = await getTxOptions();
|
|
17
|
+
options = { gasLimit: "3000000" };
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should swap sETH into sUSD on Synthetix", async () => {
|
|
21
|
+
const pool = await dhedge.loadPool(TEST_POOL);
|
|
22
|
+
const result = await pool.trade(
|
|
23
|
+
Dapp.SYNTHETIX,
|
|
24
|
+
"sETH",
|
|
25
|
+
"sUSD",
|
|
26
|
+
"1000000000000000",
|
|
27
|
+
undefined,
|
|
28
|
+
options
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
console.log(result);
|
|
32
|
+
expect(result).not.toBe(null);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import BigNumber from "bignumber.js";
|
|
3
|
+
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5
|
+
export const getTxOptions = async (): Promise<any> => {
|
|
6
|
+
const result = await axios("https://gasstation-mainnet.matic.network/v2");
|
|
7
|
+
|
|
8
|
+
return {
|
|
9
|
+
gasLimit: "3000000",
|
|
10
|
+
maxPriorityFeePerGas: new BigNumber(result.data.fast.maxPriorityFee)
|
|
11
|
+
.shiftedBy(9)
|
|
12
|
+
.toFixed(0),
|
|
13
|
+
maxFeePerGas: new BigNumber(result.data.fast.maxFee).shiftedBy(9).toFixed(0)
|
|
14
|
+
};
|
|
15
|
+
};
|