@dhedge/v2-sdk 1.9.8 → 1.10.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.
Files changed (47) hide show
  1. package/README.md +34 -0
  2. package/dist/config.d.ts +8 -2
  3. package/dist/entities/pool.d.ts +39 -8
  4. package/dist/services/flatmoney/keeperFee.d.ts +6 -0
  5. package/dist/services/flatmoney/stableLp.d.ts +9 -0
  6. package/dist/services/flatmoney/stableModule.d.ts +4 -0
  7. package/dist/services/uniswap/V3Liquidity.d.ts +8 -7
  8. package/dist/services/uniswap/V3Trade.d.ts +1 -2
  9. package/dist/services/velodrome/liquidity.d.ts +7 -3
  10. package/dist/services/velodrome/staking.d.ts +1 -0
  11. package/dist/test/constants.d.ts +14 -0
  12. package/dist/test/utils/testingHelper.d.ts +8 -1
  13. package/dist/test/wallet.d.ts +1 -0
  14. package/dist/types.d.ts +1 -0
  15. package/dist/v2-sdk.cjs.development.js +14286 -10087
  16. package/dist/v2-sdk.cjs.development.js.map +1 -1
  17. package/dist/v2-sdk.cjs.production.min.js +1 -1
  18. package/dist/v2-sdk.cjs.production.min.js.map +1 -1
  19. package/dist/v2-sdk.esm.js +14292 -10093
  20. package/dist/v2-sdk.esm.js.map +1 -1
  21. package/package.json +2 -2
  22. package/src/abi/IERC721.json +217 -0
  23. package/src/abi/IVelodromeCLGauge.json +165 -0
  24. package/src/abi/IVelodromeNonfungiblePositionManager.json +408 -0
  25. package/src/abi/flatmoney/DelayedOrder.json +547 -0
  26. package/src/abi/flatmoney/IFlatcoinVault.json +570 -0
  27. package/src/abi/flatmoney/KeeperFee.json +364 -0
  28. package/src/abi/flatmoney/StableModule.json +770 -0
  29. package/src/config.ts +33 -6
  30. package/src/entities/pool.ts +208 -82
  31. package/src/services/flatmoney/keeperFee.ts +84 -0
  32. package/src/services/flatmoney/stableLp.ts +135 -0
  33. package/src/services/flatmoney/stableModule.ts +43 -0
  34. package/src/services/lyra/trade.ts +1 -1
  35. package/src/services/uniswap/V3Liquidity.ts +141 -18
  36. package/src/services/uniswap/V3Trade.ts +1 -2
  37. package/src/services/velodrome/liquidity.ts +77 -5
  38. package/src/services/velodrome/staking.ts +6 -0
  39. package/src/test/constants.ts +22 -6
  40. package/src/test/flatmoney.test.ts +164 -0
  41. package/src/test/uniswap.test.ts +24 -15
  42. package/src/test/utils/testingHelper.ts +29 -4
  43. package/src/test/velodromeCL.test.ts +223 -0
  44. package/src/test/wallet.ts +4 -1
  45. package/src/types.ts +1 -0
  46. package/dist/services/uniswap/types.d.ts +0 -3
  47. package/src/services/uniswap/types.ts +0 -16
package/src/config.ts CHANGED
@@ -98,12 +98,20 @@ export const aaveAddressProvider: AddressDappNetworkMap = {
98
98
  [Dapp.AAVEV3]: "0xe20fCBdBfFC4Dd138cE8b2E6FBb6CB49777ad64D"
99
99
  }
100
100
  };
101
- export const nonfungiblePositionManagerAddress: AddressNetworkMap = {
102
- [Network.POLYGON]: "0xC36442b4a4522E871399CD717aBDD847Ab11FE88",
103
- [Network.OPTIMISM]: "0xC36442b4a4522E871399CD717aBDD847Ab11FE88",
104
- [Network.ARBITRUM]: "0xC36442b4a4522E871399CD717aBDD847Ab11FE88",
105
- // https://docs.uniswap.org/contracts/v3/reference/deployments
106
- [Network.BASE]: "0x03a520b32C04BF3bEEf7BEb72E919cf822Ed34f1"
101
+ export const nonfungiblePositionManagerAddress: AddressDappNetworkMap = {
102
+ [Network.POLYGON]: {
103
+ [Dapp.UNISWAPV3]: "0xC36442b4a4522E871399CD717aBDD847Ab11FE88"
104
+ },
105
+ [Network.OPTIMISM]: {
106
+ [Dapp.UNISWAPV3]: "0xC36442b4a4522E871399CD717aBDD847Ab11FE88",
107
+ [Dapp.VELODROMECL]: "0xbb5dfe1380333cee4c2eebd7202c80de2256adf4"
108
+ },
109
+ [Network.ARBITRUM]: {
110
+ [Dapp.UNISWAPV3]: "0xC36442b4a4522E871399CD717aBDD847Ab11FE88"
111
+ },
112
+ [Network.BASE]: {
113
+ [Dapp.UNISWAPV3]: "0x03a520b32C04BF3bEEf7BEb72E919cf822Ed34f1"
114
+ }
107
115
  };
108
116
 
109
117
  export const networkChainIdMap: NetworkChainIdMap = {
@@ -138,3 +146,22 @@ export const UNISWAPV3_QUOTER_ADDRESS =
138
146
 
139
147
  export const SYNTHETIX_TRACKING_CODE =
140
148
  "0x4448454447450000000000000000000000000000000000000000000000000000";
149
+
150
+ export const flatMoneyContractAddresses: Readonly<Partial<
151
+ Record<
152
+ Network,
153
+ {
154
+ DelayedOrder: string;
155
+ FlatcoinVault: string;
156
+ StableModule: string;
157
+ RETH: string;
158
+ }
159
+ >
160
+ >> = {
161
+ [Network.BASE]: {
162
+ DelayedOrder: "0x6D857e9D24a7566bB72a3FB0847A3E0e4E1c2879",
163
+ FlatcoinVault: "0x95Fa1ddc9a78273f795e67AbE8f1Cd2Cd39831fF",
164
+ StableModule: "0xb95fB324b8A2fAF8ec4f76e3dF46C718402736e2",
165
+ RETH: "0xb6fe221fe9eef5aba221c348ba20a1bf5e73624c"
166
+ }
167
+ };
@@ -3,13 +3,14 @@
3
3
  import { Contract, ethers, Wallet, BigNumber } from "ethers";
4
4
 
5
5
  import IERC20 from "../abi/IERC20.json";
6
+
7
+ import IERC721 from "../abi/IERC721.json";
6
8
  import IMiniChefV2 from "../abi/IMiniChefV2.json";
7
9
  import ILendingPool from "../abi/ILendingPool.json";
8
10
  import ISynthetix from "../abi/ISynthetix.json";
9
11
  import IUniswapV2Router from "../abi/IUniswapV2Router.json";
10
12
  import INonfungiblePositionManager from "../abi/INonfungiblePositionManager.json";
11
13
  import IAaveIncentivesController from "../abi/IAaveIncentivesController.json";
12
- import IArrakisV1RouterStaking from "../abi/IArrakisV1RouterStaking.json";
13
14
  import ILiquidityGaugeV4 from "../abi/ILiquidityGaugeV4.json";
14
15
  import IBalancerRewardsGauge from "../abi/IBalancerRewardsGauge.json";
15
16
 
@@ -34,19 +35,23 @@ import {
34
35
 
35
36
  import { Utils } from "./utils";
36
37
  import {
37
- getUniswapV3Liquidity,
38
- getUniswapV3MintParams
38
+ getDecreaseLiquidityTxData,
39
+ getIncreaseLiquidityTxData,
40
+ getUniswapV3MintTxData
39
41
  } from "../services/uniswap/V3Liquidity";
40
- import { FeeAmount } from "@uniswap/v3-sdk";
41
42
  import { getUniswapV3SwapTxData } from "../services/uniswap/V3Trade";
42
43
  import { getEasySwapperTxData } from "../services/toros/easySwapper";
43
44
  import { getAaveV3ClaimTxData } from "../services/aave/incentives";
44
45
  import {
45
46
  getVelodromeAddLiquidityTxData,
47
+ getVelodromeCLDecreaseStakedLiquidityTxData,
48
+ getVelodromeCLIncreaseStakedLiquidityTxData,
49
+ getVelodromeClOwner,
46
50
  getVelodromeRemoveLiquidityTxData
47
51
  } from "../services/velodrome/liquidity";
48
52
  import {
49
53
  getVelodromeClaimTxData,
54
+ getVelodromeCLClaimTxData,
50
55
  getVelodromeStakeTxData
51
56
  } from "../services/velodrome/staking";
52
57
  import { getLyraOptionTxData } from "../services/lyra/trade";
@@ -64,6 +69,11 @@ import {
64
69
  getExitVestTxData
65
70
  } from "../services/ramses/vesting";
66
71
  import { getPoolTxOrGasEstimate } from "../utils/contract";
72
+ import {
73
+ cancelOrderViaFlatMoney,
74
+ mintUnitViaFlatMoney,
75
+ redeemUnitViaFlatMoney
76
+ } from "../services/flatmoney/stableLp";
67
77
 
68
78
  export class Pool {
69
79
  public readonly poolLogic: Contract;
@@ -256,7 +266,7 @@ export class Pool {
256
266
  ): Promise<any> {
257
267
  const iERC20 = new ethers.utils.Interface(IERC20.abi);
258
268
  const approveTxData = iERC20.encodeFunctionData("approve", [
259
- nonfungiblePositionManagerAddress[this.network],
269
+ nonfungiblePositionManagerAddress[this.network][Dapp.UNISWAPV3],
260
270
  amount
261
271
  ]);
262
272
  const tx = await getPoolTxOrGasEstimate(
@@ -296,6 +306,35 @@ export class Pool {
296
306
  return tx;
297
307
  }
298
308
 
309
+ /**
310
+ * Approve NFT for provided spender address
311
+ * @param {string} spender Spender address
312
+ * @param {string} asset Address of asset
313
+ * @param {string} tokenId NFT id
314
+ * @param {any} options Transaction options
315
+ * @param {boolean} estimateGas Simulate/estimate gas
316
+ * @returns {Promise<any>} Transaction
317
+ */
318
+ async approveSpenderNFT(
319
+ spender: string,
320
+ asset: string,
321
+ tokenId: string,
322
+ options: any = null,
323
+ estimateGas = false
324
+ ): Promise<any> {
325
+ const iERC721 = new ethers.utils.Interface(IERC721.abi);
326
+ const approveTxData = iERC721.encodeFunctionData("approve", [
327
+ spender,
328
+ tokenId
329
+ ]);
330
+ const tx = await getPoolTxOrGasEstimate(
331
+ this,
332
+ [asset, approveTxData, options],
333
+ estimateGas
334
+ );
335
+ return tx;
336
+ }
337
+
299
338
  /**
300
339
  * Trade an asset into another asset
301
340
  * @param {Dapp} dapp Platform like Sushiswap or Uniswap
@@ -504,7 +543,7 @@ export class Pool {
504
543
  * Stake liquidity pool tokens in gauge contract
505
544
  * @param {Dapp} dapp Platform like Balancer or Velodrome
506
545
  * @param {string} gauge Gauge contract address
507
- * @param {BigNumber | string} amount Amount of liquidity pool tokens
546
+ * @param {BigNumber | string} amount Amount of liquidity pool tokens or token ID for Velodrome CL
508
547
  * @param {any} options Transaction options
509
548
  * @param {boolean} estimateGas Simulate/estimate gas
510
549
  * @returns {Promise<any>} Transaction
@@ -532,6 +571,7 @@ export class Pool {
532
571
  break;
533
572
  case Dapp.VELODROMEV2:
534
573
  case Dapp.AERODROME:
574
+ case Dapp.VELODROMECL:
535
575
  stakeTxData = getVelodromeStakeTxData(amount, true);
536
576
  break;
537
577
  default:
@@ -579,7 +619,7 @@ export class Pool {
579
619
  /**
580
620
  * Unstake liquidity pool tokens from Velodrome or Balancer gauge
581
621
  * @param {string} gauge Gauge contract address
582
- * @param {BigNumber | string} amount Amount of liquidity pool tokens
622
+ * @param {BigNumber | string} amount Amount of liquidity pool tokens or CL token ID
583
623
  * @param {any} options Transaction options
584
624
  * @param {boolean} estimateGas Simulate/estimate gas
585
625
  * @returns {Promise<any>} Transaction
@@ -941,6 +981,7 @@ export class Pool {
941
981
 
942
982
  /**
943
983
  * Create UniswapV3 liquidity pool
984
+ * @param {dapp} Platform either UniswapV3 or VelodromeCL
944
985
  * @param {string} assetA First asset
945
986
  * @param {string} assetB Second asset
946
987
  * @param {BigNumber | string} amountA Amount first asset
@@ -949,12 +990,13 @@ export class Pool {
949
990
  * @param { number } maxPrice Upper price range (assetB per assetA)
950
991
  * @param { number } minTick Lower tick range
951
992
  * @param { number } maxTick Upper tick range
952
- * @param { FeeAmount } feeAmount Fee tier (Low 0.05%, Medium 0.3%, High 1%)
993
+ * @param { number } feeAmountOrTickSpacing Fee tier UniswapV3 or tick spacing VelodromeCL
953
994
  * @param {any} options Transaction options
954
995
  * @param {boolean} estimateGas Simulate/estimate gas
955
996
  * @returns {Promise<any>} Transaction
956
997
  */
957
998
  async addLiquidityUniswapV3(
999
+ dapp: Dapp.UNISWAPV3 | Dapp.VELODROMECL,
958
1000
  assetA: string,
959
1001
  assetB: string,
960
1002
  amountA: BigNumber | string,
@@ -963,7 +1005,7 @@ export class Pool {
963
1005
  maxPrice: number | null,
964
1006
  minTick: number | null,
965
1007
  maxTick: number | null,
966
- feeAmount: FeeAmount,
1008
+ feeAmountOrTickSpacing: number,
967
1009
  options: any = null,
968
1010
  estimateGas = false
969
1011
  ): Promise<any> {
@@ -972,12 +1014,11 @@ export class Pool {
972
1014
  (minTick === null || maxTick === null)
973
1015
  )
974
1016
  throw new Error("Need to provide price or tick range");
1017
+ if ((minPrice || maxPrice) && dapp === Dapp.VELODROMECL)
1018
+ throw new Error("no price conversion for Velodrome CL");
975
1019
 
976
- const iNonfungiblePositionManager = new ethers.utils.Interface(
977
- INonfungiblePositionManager.abi
978
- );
979
-
980
- const mintTxParams = await getUniswapV3MintParams(
1020
+ const mintTxData = await getUniswapV3MintTxData(
1021
+ dapp,
981
1022
  this,
982
1023
  assetA,
983
1024
  assetB,
@@ -987,15 +1028,16 @@ export class Pool {
987
1028
  maxPrice,
988
1029
  minTick,
989
1030
  maxTick,
990
- feeAmount
991
- );
992
- const mintTxData = iNonfungiblePositionManager.encodeFunctionData(
993
- Transaction.MINT,
994
- [mintTxParams]
1031
+ feeAmountOrTickSpacing
995
1032
  );
1033
+
996
1034
  const tx = await getPoolTxOrGasEstimate(
997
1035
  this,
998
- [nonfungiblePositionManagerAddress[this.network], mintTxData, options],
1036
+ [
1037
+ nonfungiblePositionManagerAddress[this.network][dapp],
1038
+ mintTxData,
1039
+ options
1040
+ ],
999
1041
  estimateGas
1000
1042
  );
1001
1043
  return tx;
@@ -1017,46 +1059,37 @@ export class Pool {
1017
1059
  options: any = null,
1018
1060
  estimateGas = false
1019
1061
  ): Promise<any> {
1020
- let txData;
1021
1062
  let dappAddress;
1022
- if (dapp === Dapp.UNISWAPV3) {
1023
- dappAddress = nonfungiblePositionManagerAddress[this.network];
1024
- const abi = new ethers.utils.Interface(INonfungiblePositionManager.abi);
1025
- const liquidity = (await getUniswapV3Liquidity(tokenId, this))
1026
- .mul(Math.round(amount * 1e4))
1027
- .div(1e6);
1028
- const decreaseLiquidityTxData = abi.encodeFunctionData(
1029
- Transaction.DECREASE_LIQUIDITY,
1030
- [[tokenId, liquidity, 0, 0, await getDeadline(this)]]
1031
- );
1032
- const collectTxData = abi.encodeFunctionData(Transaction.COLLECT, [
1033
- [tokenId, this.address, MaxUint128, MaxUint128]
1034
- ]);
1035
-
1036
- const multicallParams = [decreaseLiquidityTxData, collectTxData];
1037
-
1038
- if (amount === 100) {
1039
- const burnTxData = abi.encodeFunctionData(Transaction.BURN, [tokenId]);
1040
- multicallParams.push(burnTxData);
1041
- }
1042
- txData = abi.encodeFunctionData(Transaction.MULTI_CALL, [
1043
- multicallParams
1044
- ]);
1045
- } else if (dapp === Dapp.ARRAKIS) {
1046
- dappAddress = routerAddress[this.network][dapp];
1047
- const abi = new ethers.utils.Interface(IArrakisV1RouterStaking.abi);
1048
- const liquidity = (await this.utils.getBalance(tokenId, this.address))
1049
- .mul(Math.round(amount * 1e4))
1050
- .div(1e6);
1051
- txData = abi.encodeFunctionData(Transaction.REMOVE_LIQUIDITY_UNSTAKE, [
1052
- tokenId,
1053
- liquidity,
1054
- 0,
1055
- 0,
1056
- this.address
1057
- ]);
1063
+ let isStaked = false;
1064
+ let txData;
1065
+ switch (dapp) {
1066
+ case Dapp.UNISWAPV3:
1067
+ dappAddress = nonfungiblePositionManagerAddress[this.network][dapp];
1068
+ break;
1069
+ case Dapp.VELODROMECL:
1070
+ const tokenIdOwner = await getVelodromeClOwner(this, tokenId);
1071
+ if (tokenIdOwner.toLowerCase() === this.address.toLowerCase()) {
1072
+ dappAddress = nonfungiblePositionManagerAddress[this.network][dapp];
1073
+ } else {
1074
+ //staked in gauge
1075
+ dappAddress = tokenIdOwner;
1076
+ isStaked = true;
1077
+ }
1078
+ break;
1079
+ case Dapp.ARRAKIS:
1080
+ dappAddress = routerAddress[this.network][dapp];
1081
+ break;
1082
+ default:
1083
+ throw new Error("dapp not supported");
1084
+ }
1085
+ if (!isStaked) {
1086
+ txData = await getDecreaseLiquidityTxData(this, dapp, tokenId, amount);
1058
1087
  } else {
1059
- throw new Error("dapp not supported");
1088
+ txData = await getVelodromeCLDecreaseStakedLiquidityTxData(
1089
+ this,
1090
+ tokenId,
1091
+ amount
1092
+ );
1060
1093
  }
1061
1094
  const tx = await getPoolTxOrGasEstimate(
1062
1095
  this,
@@ -1067,7 +1100,7 @@ export class Pool {
1067
1100
  }
1068
1101
 
1069
1102
  /**
1070
- * Increase liquidity of an UniswapV3 or Arrakis liquidity pool
1103
+ * Increase liquidity of an UniswapV, VelodromeCL or Arrakis liquidity pool
1071
1104
  * @param {Dapp} dapp Platform either UniswapV3 or Arrakis
1072
1105
  * @param {string} tokenId Token Id of UniswapV3 position
1073
1106
  * @param {BigNumber | string} amountA Amount first asset
@@ -1084,28 +1117,44 @@ export class Pool {
1084
1117
  options: any = null,
1085
1118
  estimateGas = false
1086
1119
  ): Promise<any> {
1087
- let txData;
1088
1120
  let dappAddress;
1089
- if (dapp === Dapp.UNISWAPV3) {
1090
- dappAddress = nonfungiblePositionManagerAddress[this.network];
1091
- const abi = new ethers.utils.Interface(INonfungiblePositionManager.abi);
1092
- txData = abi.encodeFunctionData(Transaction.INCREASE_LIQUIDITY, [
1093
- [tokenId, amountA, amountB, 0, 0, await getDeadline(this)]
1094
- ]);
1095
- } else if (dapp === Dapp.ARRAKIS) {
1096
- dappAddress = routerAddress[this.network][dapp];
1097
- const abi = new ethers.utils.Interface(IArrakisV1RouterStaking.abi);
1098
- txData = abi.encodeFunctionData(Transaction.ADD_LIQUIDITY_STAKE, [
1121
+ let isStaked = false;
1122
+ let txData;
1123
+ switch (dapp) {
1124
+ case Dapp.UNISWAPV3:
1125
+ dappAddress = nonfungiblePositionManagerAddress[this.network][dapp];
1126
+ break;
1127
+ case Dapp.VELODROMECL:
1128
+ const tokenIdOwner = await getVelodromeClOwner(this, tokenId);
1129
+ if (tokenIdOwner.toLowerCase() === this.address.toLowerCase()) {
1130
+ dappAddress = nonfungiblePositionManagerAddress[this.network][dapp];
1131
+ } else {
1132
+ //staked in gauge
1133
+ dappAddress = tokenIdOwner;
1134
+ isStaked = true;
1135
+ }
1136
+ break;
1137
+ case Dapp.ARRAKIS:
1138
+ dappAddress = routerAddress[this.network][dapp];
1139
+ break;
1140
+ default:
1141
+ throw new Error("dapp not supported");
1142
+ }
1143
+ if (!isStaked) {
1144
+ txData = await getIncreaseLiquidityTxData(
1145
+ this,
1146
+ dapp,
1099
1147
  tokenId,
1100
1148
  amountA,
1101
- amountB,
1102
- 0,
1103
- 0,
1104
- 0,
1105
- this.address
1106
- ]);
1149
+ amountB
1150
+ );
1107
1151
  } else {
1108
- throw new Error("dapp not supported");
1152
+ txData = await getVelodromeCLIncreaseStakedLiquidityTxData(
1153
+ this,
1154
+ tokenId,
1155
+ amountA,
1156
+ amountB
1157
+ );
1109
1158
  }
1110
1159
  const tx = await getPoolTxOrGasEstimate(
1111
1160
  this,
@@ -1131,12 +1180,12 @@ export class Pool {
1131
1180
  ): Promise<any> {
1132
1181
  let txData;
1133
1182
  let contractAddress;
1183
+ const iNonfungiblePositionManager = new ethers.utils.Interface(
1184
+ INonfungiblePositionManager.abi
1185
+ );
1134
1186
  switch (dapp) {
1135
1187
  case Dapp.UNISWAPV3:
1136
- contractAddress = nonfungiblePositionManagerAddress[this.network];
1137
- const iNonfungiblePositionManager = new ethers.utils.Interface(
1138
- INonfungiblePositionManager.abi
1139
- );
1188
+ contractAddress = nonfungiblePositionManagerAddress[this.network][dapp];
1140
1189
  txData = iNonfungiblePositionManager.encodeFunctionData(
1141
1190
  Transaction.COLLECT,
1142
1191
  [[tokenId, this.address, MaxUint128, MaxUint128]]
@@ -1158,6 +1207,21 @@ export class Pool {
1158
1207
  contractAddress = tokenId;
1159
1208
  txData = getVelodromeClaimTxData(this, tokenId, true);
1160
1209
  break;
1210
+ case Dapp.VELODROMECL:
1211
+ const tokenIdOwner = await getVelodromeClOwner(this, tokenId);
1212
+ if (tokenIdOwner.toLowerCase() === this.address.toLowerCase()) {
1213
+ contractAddress =
1214
+ nonfungiblePositionManagerAddress[this.network][dapp];
1215
+ txData = iNonfungiblePositionManager.encodeFunctionData(
1216
+ Transaction.COLLECT,
1217
+ [[tokenId, this.address, MaxUint128, MaxUint128]]
1218
+ );
1219
+ } else {
1220
+ //staked in gauge
1221
+ contractAddress = tokenIdOwner;
1222
+ txData = getVelodromeCLClaimTxData(tokenId);
1223
+ }
1224
+ break;
1161
1225
  default:
1162
1226
  throw new Error("dapp not supported");
1163
1227
  }
@@ -1185,7 +1249,7 @@ export class Pool {
1185
1249
  assetFrom: string,
1186
1250
  assetTo: string,
1187
1251
  amountIn: BigNumber | string,
1188
- feeAmount: FeeAmount,
1252
+ feeAmount: number,
1189
1253
  slippage = 0.5,
1190
1254
  options: any = null,
1191
1255
  estimateGas = false
@@ -1629,4 +1693,66 @@ export class Pool {
1629
1693
  );
1630
1694
  return tx;
1631
1695
  }
1696
+
1697
+ /** deposit rETH to mint UNIT via the Flat Money protocol
1698
+ *
1699
+ * @param { BigNumber | string } depositAmount Amount of rETH to deposit
1700
+ * @param { number } slippage slippage, 0.5 represents 0.5%
1701
+ * @param { number | null } maxKeeperFeeInUsd 5 represents $5; null will skip the maxKeeperFee check
1702
+ * @param {any} options Transaction options
1703
+ * @param {boolean} estimateGas Simulate/estimate gas
1704
+ * @returns {Promise<any>} Transaction
1705
+ */
1706
+ async mintUnitViaFlatMoney(
1707
+ depositAmount: ethers.BigNumber | string,
1708
+ slippage = 0.5,
1709
+ maxKeeperFeeInUsd: number | null,
1710
+ options: any = null,
1711
+ estimateGas = false
1712
+ ): Promise<any> {
1713
+ const tx = await mintUnitViaFlatMoney(
1714
+ this,
1715
+ depositAmount,
1716
+ slippage,
1717
+ maxKeeperFeeInUsd,
1718
+ options,
1719
+ estimateGas
1720
+ );
1721
+ return tx;
1722
+ }
1723
+
1724
+ /** redeem UNIT via the Flat Money protocol
1725
+ *
1726
+ * @param { BigNumber | string } depositAmount Amount of UNIT to withdraw
1727
+ * @param { number } slippage slippage, 0.5 represents 0.5%
1728
+ * @param { number | null } maxKeeperFeeInUsd 5 represents $5; null will skip the maxKeeperFee check
1729
+ * @param {any} options Transaction options
1730
+ * @param {boolean} estimateGas Simulate/estimate gas
1731
+ * @returns {Promise<any>} Transaction
1732
+ */
1733
+ async redeemUnitViaFlatMoney(
1734
+ withdrawAmount: ethers.BigNumber | string,
1735
+ slippage = 0.5,
1736
+ maxKeeperFeeInUsd: number | null,
1737
+ options: any = null,
1738
+ estimateGas = false
1739
+ ): Promise<any> {
1740
+ const tx = await redeemUnitViaFlatMoney(
1741
+ this,
1742
+ withdrawAmount,
1743
+ slippage,
1744
+ maxKeeperFeeInUsd,
1745
+ options,
1746
+ estimateGas
1747
+ );
1748
+ return tx;
1749
+ }
1750
+
1751
+ async cancelOrderViaFlatMoney(
1752
+ options: any = null,
1753
+ estimateGas = false
1754
+ ): Promise<any> {
1755
+ const tx = await cancelOrderViaFlatMoney(this, options, estimateGas);
1756
+ return tx;
1757
+ }
1632
1758
  }
@@ -0,0 +1,84 @@
1
+ import { Contract, ethers } from "ethers";
2
+ import { Pool } from "../../entities";
3
+ import IFlatcoinVaultAbi from "../../abi/flatmoney/IFlatcoinVault.json";
4
+ import KeeperFeeAbi from "../../abi/flatmoney/KeeperFee.json";
5
+ import { flatMoneyContractAddresses } from "../../config";
6
+ import BigNumber from "bignumber.js";
7
+
8
+ export const getKeeperFeeContract = async (pool: Pool): Promise<Contract> => {
9
+ const flatMoneyContracts = flatMoneyContractAddresses[pool.network];
10
+ if (!flatMoneyContracts) {
11
+ throw new Error(
12
+ `getStableModuleContract: network of ${pool.network} not supported`
13
+ );
14
+ }
15
+ const flatcoinVaultContract = new Contract(
16
+ flatMoneyContracts.FlatcoinVault,
17
+ IFlatcoinVaultAbi,
18
+ pool.signer
19
+ );
20
+ const key = ethers.utils.formatBytes32String("keeperFee");
21
+ const keeperFeeContractAddress: string = await flatcoinVaultContract.callStatic.moduleAddress(
22
+ key
23
+ );
24
+
25
+ const keeperFeeContract = new ethers.Contract(
26
+ keeperFeeContractAddress,
27
+ KeeperFeeAbi,
28
+ pool.signer
29
+ );
30
+ return keeperFeeContract;
31
+ };
32
+
33
+ export const getKeeperFee = async (
34
+ pool: Pool,
35
+ maxKeeperFeeInUsd: number | null
36
+ ): Promise<ethers.BigNumber> => {
37
+ const keeperFeeContract = await getKeeperFeeContract(pool);
38
+ const gasPrice = await pool.signer.provider.getGasPrice();
39
+
40
+ let keeperfee: ethers.BigNumber;
41
+ if (gasPrice) {
42
+ keeperfee = await keeperFeeContract["getKeeperFee(uint256)"](
43
+ new BigNumber(gasPrice.toString()).times(1.2).toFixed(0)
44
+ );
45
+ } else {
46
+ keeperfee = await keeperFeeContract["getKeeperFee()"]();
47
+ }
48
+
49
+ const keeperFeeInUsd = await getKeeperFeeInUsd(pool, keeperfee);
50
+
51
+ if (
52
+ Number.isFinite(maxKeeperFeeInUsd) &&
53
+ keeperFeeInUsd.gt(ethers.BigNumber.from(maxKeeperFeeInUsd).toString())
54
+ ) {
55
+ throw new Error("mintUnitViaFlatMoney: keeperFee too large");
56
+ }
57
+
58
+ return keeperfee;
59
+ };
60
+
61
+ export const getKeeperFeeInUsd = async (
62
+ pool: Pool,
63
+ keeperFee: ethers.BigNumber
64
+ ): Promise<BigNumber> => {
65
+ const flatMoneyContracts = flatMoneyContractAddresses[pool.network];
66
+ if (!flatMoneyContracts) {
67
+ throw new Error(
68
+ `getKeeperFeeInUsd: network of ${pool.network} not supported`
69
+ );
70
+ }
71
+ const fundComposition = await pool.getComposition();
72
+ const filteredFc = fundComposition.filter(
73
+ fc =>
74
+ fc.asset.toLocaleLowerCase() ===
75
+ flatMoneyContracts.RETH.toLocaleLowerCase()
76
+ );
77
+
78
+ if (!filteredFc[0])
79
+ throw new Error(`getKeeperFeeInUsd: required asset not enabled yet`);
80
+
81
+ const rateD1 = new BigNumber(filteredFc[0].rate.toString()).div(1e18);
82
+
83
+ return rateD1.times(keeperFee.toString()).div(1e18);
84
+ };