impermax-sdk 1.0.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 (103) hide show
  1. package/.idea/impermax-sdk.iml +12 -0
  2. package/.idea/misc.xml +6 -0
  3. package/.idea/modules.xml +8 -0
  4. package/.idea/workspace.xml +642 -0
  5. package/abis/ImpermaxABI.ts +3 -0
  6. package/abis/contracts/BAllowance.json +4735 -0
  7. package/abis/contracts/BDeployer.json +1195 -0
  8. package/abis/contracts/BInterestRateModel.json +10796 -0
  9. package/abis/contracts/BSetter.json +6219 -0
  10. package/abis/contracts/BStorage.json +2613 -0
  11. package/abis/contracts/Borrowable.json +19937 -0
  12. package/abis/contracts/CDeployer.json +1104 -0
  13. package/abis/contracts/CSetter.json +5094 -0
  14. package/abis/contracts/CStorage.json +516 -0
  15. package/abis/contracts/ClaimAggregator.json +2015 -0
  16. package/abis/contracts/Collateral.json +21615 -0
  17. package/abis/contracts/ERC20.json +819 -0
  18. package/abis/contracts/Factory.json +21986 -0
  19. package/abis/contracts/FarmingPool.json +8601 -0
  20. package/abis/contracts/IBDeployer.json +351 -0
  21. package/abis/contracts/IBorrowTracker.json +346 -0
  22. package/abis/contracts/IBorrowable.json +13207 -0
  23. package/abis/contracts/ICDeployer.json +294 -0
  24. package/abis/contracts/IClaimable.json +406 -0
  25. package/abis/contracts/ICollateral.json +8952 -0
  26. package/abis/contracts/IERC20.json +2376 -0
  27. package/abis/contracts/IFactory.json +3660 -0
  28. package/abis/contracts/IFarmingPool.json +3584 -0
  29. package/abis/contracts/IImpermaxCallee.json +679 -0
  30. package/abis/contracts/IMerkleDistributor.json +1134 -0
  31. package/abis/contracts/IPoolToken.json +5343 -0
  32. package/abis/contracts/IRouter01.json +6891 -0
  33. package/abis/contracts/IRouter02.json +7283 -0
  34. package/abis/contracts/ISimpleUniswapOracle.json +1469 -0
  35. package/abis/contracts/IStakedLPToken.json +7309 -0
  36. package/abis/contracts/IStakingRewards.json +1036 -0
  37. package/abis/contracts/IUniswapV2Callee.json +403 -0
  38. package/abis/contracts/IUniswapV2ERC20.json +3155 -0
  39. package/abis/contracts/IUniswapV2Factory.json +1690 -0
  40. package/abis/contracts/IUniswapV2Pair.json +6761 -0
  41. package/abis/contracts/IWETH.json +561 -0
  42. package/abis/contracts/ImpermaxChef.json +20945 -0
  43. package/abis/contracts/ImpermaxERC20.json +12095 -0
  44. package/abis/contracts/Math.json +1966 -0
  45. package/abis/contracts/MockERC20.json +8884 -0
  46. package/abis/contracts/PoolToken.json +10784 -0
  47. package/abis/contracts/Router01.json +43963 -0
  48. package/abis/contracts/SafeMath.json +6828 -0
  49. package/abis/contracts/SimpleUniswapOracle.json +9640 -0
  50. package/abis/contracts/TransferHelper.json +4875 -0
  51. package/abis/contracts/UQ112x112.json +1201 -0
  52. package/abis/contracts/UniswapV2ERC20.json +10969 -0
  53. package/abis/contracts/UniswapV2Factory.json +5521 -0
  54. package/abis/contracts/UniswapV2Library.json +13789 -0
  55. package/abis/contracts/UniswapV2Pair.json +30782 -0
  56. package/abis/contracts/WETH9.json +6613 -0
  57. package/config/amms.ts +199 -0
  58. package/config/contracts/claim-aggregators.ts +16 -0
  59. package/config/contracts/impermax-chef.ts +16 -0
  60. package/config/contracts/imxes.ts +16 -0
  61. package/config/contracts/merkle-distributors.ts +13 -0
  62. package/config/contracts/routers.ts +36 -0
  63. package/config/contracts/simple-uniswap-oracles.ts +33 -0
  64. package/config/contracts/weths.ts +18 -0
  65. package/config/debank-ids.ts +15 -0
  66. package/config/endpoints/merkle-distributors.ts +13 -0
  67. package/config/eth.ts +32 -0
  68. package/config/factories.ts +26 -0
  69. package/config/farms.ts +119 -0
  70. package/config/general.ts +8 -0
  71. package/config/subgraphs.ts +69 -0
  72. package/config/types.ts +81 -0
  73. package/impermax-router/Account.ts +123 -0
  74. package/impermax-router/AccountBorrowable.ts +110 -0
  75. package/impermax-router/AccountCollateral.ts +40 -0
  76. package/impermax-router/AccountLendingPool.ts +231 -0
  77. package/impermax-router/AccountPoolToken.ts +76 -0
  78. package/impermax-router/Borrowable.ts +86 -0
  79. package/impermax-router/Collateral.ts +26 -0
  80. package/impermax-router/ContractsHelper.ts +64 -0
  81. package/impermax-router/ImpermaxFactory.ts +47 -0
  82. package/impermax-router/Interactions.ts +94 -0
  83. package/impermax-router/InteractionsLendingPool.ts +129 -0
  84. package/impermax-router/InteractionsPoolToken.ts +187 -0
  85. package/impermax-router/LendingPool.ts +256 -0
  86. package/impermax-router/PoolToken.ts +112 -0
  87. package/impermax-router/index.ts +49 -0
  88. package/impermax-router/interfaces.ts +233 -0
  89. package/index.ts +5 -0
  90. package/package.json +23 -0
  91. package/subgraph/Account.ts +93 -0
  92. package/subgraph/AccountLendingPool.ts +60 -0
  93. package/subgraph/AccountPoolToken.ts +60 -0
  94. package/subgraph/LendingPool.ts +179 -0
  95. package/subgraph/PoolToken.ts +381 -0
  96. package/subgraph/PriceHelper.ts +150 -0
  97. package/subgraph/SolidexHelper.ts +54 -0
  98. package/subgraph/index.ts +166 -0
  99. package/subgraph/initializer.ts +509 -0
  100. package/subgraph/query.ts +224 -0
  101. package/tsconfig.json +16 -0
  102. package/utils/ether-utils.ts +22 -0
  103. package/utils/index.ts +12 -0
@@ -0,0 +1,81 @@
1
+ import { BigNumber } from 'ethers';
2
+
3
+ export enum Networks {
4
+ Ropsten = 'ropsten',
5
+ Mainnet = 'mainnet',
6
+ Polygon = 'polygon',
7
+ Arbitrum = 'arbitrum',
8
+ Avalanche = 'avalanche',
9
+ Moonriver = 'moonriver',
10
+ Aurora = 'aurora',
11
+ Cronos = 'cronos',
12
+ Fantom = 'fantom',
13
+ Harmony = 'harmony',
14
+ Moonbeam = 'moonbeam',
15
+ Sxnetwork = 'sxnetwork',
16
+ }
17
+
18
+ // FACTORIES
19
+ export enum Factory {
20
+ V2V1 = '1', // Uniswap V2 Factory V1
21
+ V2V1_1 = '2', // Uniswap V2 Factory V1 (removed factory parameter)
22
+ V2V1_2 = '3', // Uniswap V2 Factory V1 (updated interest rate model and no borrow fee)
23
+ V2V2 = '4', // Uniswap V2 Factory V2 (liquidation fee)
24
+ SOLV1_2 = '5', // Solidly Factory V1
25
+ SOLV2 = '6', // Solidly Factory V2
26
+ }
27
+
28
+ export enum PoolTokenType {
29
+ Collateral = 'collateral',
30
+ BorrowableA = 'borrowable0',
31
+ BorrowableB = 'borrowable1',
32
+ }
33
+
34
+ export enum Amms {
35
+ uniswap = 'uniswap',
36
+ quickswap = 'quickswap',
37
+ sushiswap = 'sushiswap',
38
+ swapr = 'swapr',
39
+ swapfish = 'swapfish',
40
+ zyberswap = 'zyberswap',
41
+ pangolin = 'pangolin',
42
+ traderJoe = 'traderJoe',
43
+ solarbeam = 'solarbeam',
44
+ thorus = 'thorus',
45
+ tetuswap = 'tetuswap',
46
+ solidly = 'solidly',
47
+ solidlyB = 'solidlyB',
48
+ solidlyUSDC = 'solidlyUSDC',
49
+ solidlyUSDCB = 'solidlyUSDCB',
50
+ solidly091 = 'solidly091',
51
+ solidlyOxd = 'solidlyOxd',
52
+ }
53
+
54
+ export type Address = string;
55
+
56
+ export type NetworkIndex<Type> = {
57
+ [key in Networks]?: Type
58
+ };
59
+
60
+ export type FactoryIndex<Type> = {
61
+ [key in Factory]?: Type
62
+ };
63
+
64
+ export type AddressIndex<Type> = {
65
+ [key in Address]: Type
66
+ };
67
+
68
+ export type NetworkFactoryIndex<Type> = NetworkIndex<FactoryIndex<Type>>;
69
+
70
+ export type LendingPoolIndex<Type> = FactoryIndex<AddressIndex<Type>>;
71
+
72
+ export type AmmIndex = {
73
+ [key in Networks]: {
74
+ [key in Amms]?: any
75
+ }
76
+ };
77
+
78
+ export type DistributorDetails = {
79
+ claimableAddress: Address,
80
+ name: string,
81
+ };
@@ -0,0 +1,123 @@
1
+ import { AirdropData, Contract, PendingRewardUI } from "../impermax-router/interfaces";
2
+
3
+ import ImpermaxRouter from ".";
4
+ import AccountLendingPool from "./AccountLendingPool";
5
+ import { BigNumber } from "ethers";
6
+ import { Address, Factory, FactoryIndex, LendingPoolIndex } from '../config/types';
7
+ import { MERKLE_URL_ETH, MERKLE_URL_IBEX, MERKLE_URL_IBEX_2 } from '../config/endpoints/merkle-distributors';
8
+
9
+ export default class Account {
10
+ router: ImpermaxRouter;
11
+ account: Address;
12
+ accountLLPs: LendingPoolIndex<Promise<AccountLendingPool>>;
13
+ cache: {
14
+ airdropData: {[key in string]?: Promise<AirdropData>},
15
+ claimableIBEX?: {
16
+ [key in Address]?: Promise<number>
17
+ },
18
+ } = { airdropData: {} };
19
+
20
+ constructor(router: ImpermaxRouter, account: Address) {
21
+ this.router = router;
22
+ this.account = account;
23
+ this.accountLLPs = {};
24
+ this.cache.claimableIBEX = {};
25
+ }
26
+
27
+ async cleanCache() {
28
+ this.cache = { airdropData: {} };
29
+ this.cache.claimableIBEX = {};
30
+ for (const factory of Object.keys(this.accountLLPs) as Factory[]) {
31
+ for (const pairAddress of Object.keys(this.accountLLPs[factory]) as Address[]) {
32
+ (await this.accountLLPs[factory][pairAddress]).cleanCache()
33
+ }
34
+ }
35
+ }
36
+
37
+ private async initializeAccountLendingPool(factory: Factory, pair: Address) : Promise<AccountLendingPool> {
38
+ const lendingPool = await this.router.getFactory(factory).getLendingPool(pair);
39
+ return new AccountLendingPool(this, lendingPool);
40
+ }
41
+ async getAccountLendingPool(factory: Factory, pair: Address) : Promise<AccountLendingPool> {
42
+ if (!this.accountLLPs[factory]) this.accountLLPs[factory] = {};
43
+ if (!this.accountLLPs[factory][pair]) this.accountLLPs[factory][pair] = this.initializeAccountLendingPool(factory, pair);
44
+ return this.accountLLPs[factory][pair];
45
+ }
46
+
47
+ // Mass Available Reward (only supports ImpermaxChef)
48
+ async getMassAvailableReward(pairs: FactoryIndex<Address[]>) : Promise<LendingPoolIndex<PendingRewardUI[]>> {
49
+ const result: LendingPoolIndex<PendingRewardUI[]> = {};
50
+ if (!pairs) return result;
51
+ const requests = [];
52
+ const factories = Object.keys(pairs) as Factory[];
53
+ for (const factory of factories) {
54
+ const requestsLocal = [];
55
+ for (const pairAddress of pairs[factory]) {
56
+ requestsLocal.push((await this.getAccountLendingPool(factory, pairAddress)).getAvailableReward());
57
+ }
58
+ requests.push(Promise.all(requestsLocal));
59
+ }
60
+ const data = await Promise.all(requests);
61
+ for (let i = 0; i < factories.length; i++) {
62
+ const factory = factories[i];
63
+ result[factory] = {};
64
+ for (let j = 0; j < pairs[factory].length; j++) {
65
+ result[factory][pairs[factory][j]] = data[i][j];
66
+ }
67
+ }
68
+ return result;
69
+ }
70
+
71
+ // Airdrop Data
72
+ private async initializeAirdropData(airdropURL: string, merkleDistributor: Contract) : Promise<AirdropData> {
73
+ if (airdropURL && airdropURL !== '') {
74
+ try {
75
+ const json = await fetch(airdropURL + '/' + this.account);
76
+ const data = await json.json();
77
+ if (data) {
78
+ data.amount = BigNumber.from(data.amount);
79
+ const isClaimed = await merkleDistributor.methods.isClaimed(data.index).call();
80
+ if (!isClaimed) return data;
81
+ }
82
+ } catch (e){}
83
+ }
84
+ return {
85
+ index: -1,
86
+ amount: null,
87
+ proof: [],
88
+ }
89
+ }
90
+ private async getAirdropData(airdropURL: string, merkleDistributor: Contract) : Promise<AirdropData> {
91
+ if (!this.cache.airdropData[airdropURL]) this.cache.airdropData[airdropURL] = this.initializeAirdropData(airdropURL, merkleDistributor);
92
+ return this.cache.airdropData[airdropURL];
93
+ }
94
+ async getIbexAirdropData() : Promise<AirdropData> {
95
+ return this.getAirdropData(MERKLE_URL_IBEX[this.router.network], this.router.contractsHelper.merkleDistributorIbex);
96
+ }
97
+ async getIbex2AirdropData() : Promise<AirdropData> {
98
+ return this.getAirdropData(MERKLE_URL_IBEX_2[this.router.network], this.router.contractsHelper.merkleDistributorIbex2);
99
+ }
100
+ async getEthAirdropData() : Promise<AirdropData> {
101
+ return this.getAirdropData(MERKLE_URL_ETH[this.router.network], this.router.contractsHelper.merkleDistributorEth);
102
+ }
103
+ async hasIbexClaimableAirdrop() : Promise<boolean> {
104
+ const airdropData = await this.getIbexAirdropData();
105
+ if (airdropData.amount) return true;
106
+ return false;
107
+ }
108
+ async hasEthClaimableAirdrop() : Promise<boolean> {
109
+ const airdropData = await this.getEthAirdropData();
110
+ if (airdropData.amount) return true;
111
+ return false;
112
+ }
113
+
114
+ // Claim Claimable IBEX
115
+ async initializeClaimableIBEX(claimableAddress: Address) : Promise<number> {
116
+ const claimable = this.router.contractsHelper.newClaimable(claimableAddress);
117
+ return await claimable.methods.claim().call({from: this.account}) / 1e18;
118
+ }
119
+ async getClaimableIBEX(claimableAddress: Address) : Promise<number> {
120
+ if (!this.cache.claimableIBEX[claimableAddress]) this.cache.claimableIBEX[claimableAddress] = this.initializeClaimableIBEX(claimableAddress);
121
+ return this.cache.claimableIBEX[claimableAddress];
122
+ }
123
+ }
@@ -0,0 +1,110 @@
1
+ import { NO_CHANGES } from "./interfaces";
2
+
3
+ import AccountPoolToken from "./AccountPoolToken";
4
+ import Borrowable from "./Borrowable";
5
+ import { PoolTokenType } from '../config/types';
6
+
7
+ export default class AccountBorrowable extends AccountPoolToken {
8
+ poolToken: Borrowable;
9
+ borrowableCache: {
10
+ borrowed?: Promise<number>,
11
+ farmingShares?: Promise<number>,
12
+ availableIMXReward?: Promise<number>,
13
+ availableRewarderReward?: Promise<number>,
14
+ } = {};
15
+
16
+ cleanCache() {
17
+ super.cleanCache();
18
+ this.borrowableCache = {};
19
+ }
20
+
21
+ // Borrowed
22
+ private async initializeBorrowed() : Promise<number> {
23
+ const borrowable = await this.poolToken.getPoolToken();
24
+ const balance = await borrowable.methods.borrowBalance(this.getAccount()).call();
25
+ const storedAmount = await await this.poolToken.normalize(balance);
26
+ const accrualTimestamp = await this.getAccrualTimestamp();
27
+ const borrowRate = await this.getBorrowRate();
28
+ return storedAmount * (1 + (Date.now() / 1000 - accrualTimestamp) * borrowRate);
29
+ }
30
+ async getBorrowed() : Promise<number> {
31
+ if (!this.borrowableCache.borrowed) this.borrowableCache.borrowed = this.initializeBorrowed();
32
+ return this.borrowableCache.borrowed;
33
+ }
34
+ async getBorrowedUSD() : Promise<number> {
35
+ const borrowed = await this.getBorrowed();
36
+ const tokenPrice = await this.getTokenPriceAccurate();
37
+ return borrowed * tokenPrice;
38
+ }
39
+
40
+ // Max Borrowable
41
+ async getMaxBorrowable() : Promise<number> {
42
+ const availableCash = await this.poolToken.getTotalBalance();
43
+ const { valueCollateral, valueA, valueB } = await this.accountLendingPool.getValues(NO_CHANGES);
44
+ const [valueBorrowed, valueOther] = this.poolToken.poolTokenType == PoolTokenType.BorrowableA ? [valueA, valueB] : [valueB, valueA];
45
+ const safetyMargin = (await this.accountLendingPool.getSafetyMargin()) * this.getUiMargin();
46
+ const liquidationPenalty = await this.accountLendingPool.getLiquidationPenalty();
47
+ const actualCollateral = valueCollateral / liquidationPenalty;
48
+ const totalValueBorrowable1 = (actualCollateral * Math.sqrt(safetyMargin) - valueOther) / safetyMargin;
49
+ const totalValueBorrowable2 = (actualCollateral / Math.sqrt(safetyMargin) - valueOther) * safetyMargin;
50
+ const maxValueBorrowable = Math.min(totalValueBorrowable1, totalValueBorrowable2) - valueBorrowed;
51
+ const price = await this.poolToken.getMarketPriceDenomLP();
52
+ return Math.max(0, Math.min(availableCash, maxValueBorrowable / price));
53
+ }
54
+
55
+ // Farming Shares
56
+ private async initializeFarmingShares() : Promise<number> {
57
+ if (!await this.poolToken.hasFarming()) return 0;
58
+ if (await this.poolToken.hasImpermaxChef()) {
59
+ const c = (await this.poolToken.getImpermaxChef().methods.userInfo(
60
+ await this.poolToken.getPoolTokenAddress(),
61
+ this.getAccount()
62
+ ).call()).shares;
63
+ return c
64
+ } else {
65
+ const farmingPool = await this.poolToken.getFarmingPool();
66
+ return (await farmingPool.methods.recipients(this.getAccount()).call()).shares;
67
+ }
68
+ }
69
+ async getFarmingShares() : Promise<number> {
70
+ if (!this.borrowableCache.farmingShares) this.borrowableCache.farmingShares = this.initializeFarmingShares();
71
+ return this.borrowableCache.farmingShares;
72
+ }
73
+
74
+ // Available Reward
75
+ private async initializeAvailableIMXReward() : Promise<number> {
76
+ if (!await this.poolToken.hasFarming()) return 0;
77
+ if (await this.poolToken.hasImpermaxChef()) {
78
+ const borrowableAddress = await this.poolToken.getPoolTokenAddress();
79
+ try {
80
+ return await this.poolToken.getImpermaxChef().methods.pendingReward(borrowableAddress, this.getAccount()).call() / 1e18;
81
+ } catch {
82
+ console.log("Warning: pending reward negative");
83
+ return 0;
84
+ }
85
+ } else {
86
+ const farmingPool = await this.poolToken.getFarmingPool();
87
+ return await farmingPool.methods.claim().call({from: this.getAccount()}) / 1e18;
88
+ }
89
+ }
90
+ async getAvailableIMXReward() {
91
+ if (!this.borrowableCache.availableIMXReward) this.borrowableCache.availableIMXReward = this.initializeAvailableIMXReward();
92
+ return this.borrowableCache.availableIMXReward;
93
+ }
94
+ private async initializeAvailableRewarderReward() : Promise<number> {
95
+ if (!await this.poolToken.hasRewarder()) return 0;
96
+ const borrowableAddress = await this.poolToken.getPoolTokenAddress();
97
+ const rewarder = await this.poolToken.getRewarder();
98
+ try {
99
+ return await rewarder.methods.pendingReward(borrowableAddress, this.getAccount()).call() / 1e18;
100
+ } catch {
101
+ console.log("Warning: pending reward negative");
102
+ return 0;
103
+ }
104
+ }
105
+ async getAvailableRewarderReward() {
106
+ if (!this.borrowableCache.availableRewarderReward) this.borrowableCache.availableRewarderReward = this.initializeAvailableRewarderReward();
107
+ return this.borrowableCache.availableRewarderReward;
108
+ }
109
+
110
+ }
@@ -0,0 +1,40 @@
1
+ import { NO_CHANGES } from "./interfaces";
2
+ import AccountPoolToken from "./AccountPoolToken";
3
+ import Collateral from "./Collateral";
4
+
5
+ export default class AccountCollateral extends AccountPoolToken {
6
+ poolToken: Collateral;
7
+ collateralCache: {
8
+ } = {};
9
+
10
+ cleanCache() {
11
+ super.cleanCache();
12
+ this.collateralCache = {};
13
+ }
14
+
15
+ async fixStakedLPExchangeRate(n: Promise<number>) : Promise<number> {
16
+ const stakedLPExchangeRate = await this.poolToken.lendingPool.getStakedLPExchangeRate();
17
+ return await n / stakedLPExchangeRate;
18
+ }
19
+
20
+ async getAvailableBalanceUSD() : Promise<number> {
21
+ return this.fixStakedLPExchangeRate(super.getAvailableBalanceUSD());
22
+ }
23
+
24
+ async getDepositedUSD() : Promise<number> {
25
+ return this.fixStakedLPExchangeRate(super.getDepositedUSD());
26
+ }
27
+
28
+ // Max Withdrawable
29
+ async getMaxWithdrawable() : Promise<number> {
30
+ const deposited = await this.getDeposited();
31
+ const availableCash = await this.poolToken.getTotalBalance();
32
+ const { valueCollateral, valueA, valueB } = await this.accountLendingPool.getValues(NO_CHANGES);
33
+ const safetyMargin = (await this.accountLendingPool.getSafetyMargin()) * this.getUiMargin();
34
+ const liquidationPenalty = await this.accountLendingPool.getLiquidationPenalty();
35
+ const actualCollateral = valueCollateral / liquidationPenalty;
36
+ const maxWithdrawable1 = (actualCollateral - (valueA + valueB * safetyMargin) / Math.sqrt(safetyMargin)) * liquidationPenalty;
37
+ const maxWithdrawable2 = (actualCollateral - (valueB + valueA * safetyMargin) / Math.sqrt(safetyMargin)) * liquidationPenalty;
38
+ return Math.max(0, Math.min(deposited, availableCash, maxWithdrawable1, maxWithdrawable2) / this.getDust());
39
+ }
40
+ }
@@ -0,0 +1,231 @@
1
+ import { Changes, NO_CHANGES, PendingRewardUI, Values } from "./interfaces";
2
+ import Account from "./Account";
3
+ import AccountBorrowable from "./AccountBorrowable";
4
+ import AccountCollateral from "./AccountCollateral";
5
+
6
+ import LendingPool from "./LendingPool";
7
+ import { PoolTokenType } from '../config/types';
8
+
9
+ export default class AccountLendingPool {
10
+ account: Account;
11
+ lendingPool: LendingPool;
12
+ accountPoolTokens: {
13
+ [PoolTokenType.Collateral]: AccountCollateral,
14
+ [PoolTokenType.BorrowableA]: AccountBorrowable,
15
+ [PoolTokenType.BorrowableB]: AccountBorrowable,
16
+ }
17
+ cache: {
18
+ availableReward?: Promise<PendingRewardUI[]>,
19
+ oldAvailableReward?: Promise<number>,
20
+ } = {};
21
+
22
+ constructor(account: Account, lendingPool: LendingPool) {
23
+ this.account = account;
24
+ this.lendingPool = lendingPool;
25
+ this.accountPoolTokens = {
26
+ [PoolTokenType.Collateral]: new AccountCollateral(this, PoolTokenType.Collateral),
27
+ [PoolTokenType.BorrowableA]: new AccountBorrowable(this, PoolTokenType.BorrowableA),
28
+ [PoolTokenType.BorrowableB]: new AccountBorrowable(this, PoolTokenType.BorrowableB),
29
+ };
30
+ }
31
+
32
+ cleanCache() {
33
+ this.cache = {};
34
+ this.pta().cleanCache();
35
+ this.ptb().cleanCache();
36
+ this.ptc().cleanCache();
37
+ }
38
+
39
+ // Shortcuts
40
+ pta = () => this.accountPoolTokens[PoolTokenType.BorrowableA];
41
+ ptb = () => this.accountPoolTokens[PoolTokenType.BorrowableB];
42
+ ptc = () => this.accountPoolTokens[PoolTokenType.Collateral];
43
+ getSafetyMargin = async () => (await this.lendingPool.getSubgraphLendingPool()).getSafetyMargin();
44
+ getLiquidationPenalty = async () => (await this.lendingPool.getSubgraphLendingPool()).getLiquidationPenalty();
45
+ getUiMargin = () => this.account.router.uiMargin;
46
+
47
+ // Balance
48
+ async getBalanceUSD() : Promise<number> {
49
+ const depositedAUSD = await this.pta().getDepositedUSD();
50
+ const depositedBUSD = await this.ptb().getDepositedUSD();
51
+ const depositedCUSD = await this.ptc().getDepositedUSD();
52
+ return depositedAUSD + depositedBUSD + depositedCUSD;
53
+ }
54
+
55
+ // Supplied
56
+ async getSuppliedUSD() : Promise<number> {
57
+ const depositedAUSD = await this.pta().getDepositedUSD();
58
+ const depositedBUSD = await this.ptb().getDepositedUSD();
59
+ return depositedAUSD + depositedBUSD;
60
+ }
61
+
62
+ // Debt
63
+ async getDebtUSD() : Promise<number> {
64
+ const borrowedAUSD = await this.pta().getBorrowedUSD();
65
+ const borrowedBUSD = await this.ptb().getBorrowedUSD();
66
+ return borrowedAUSD + borrowedBUSD;
67
+ }
68
+
69
+ // Equity
70
+ async getEquityUSD() : Promise<number> {
71
+ const balanceUSD = await this.getBalanceUSD();
72
+ const debtUSD = await this.getDebtUSD();
73
+ return balanceUSD - debtUSD;
74
+ }
75
+ async getLPEquityUSD() : Promise<number> {
76
+ const collateralUSD = await this.ptc().getDepositedUSD();
77
+ const debtUSD = await this.getDebtUSD();
78
+ return collateralUSD - debtUSD;
79
+ }
80
+ async getLPEquity() : Promise<number> {
81
+ const lpEquityUSD = await this.getLPEquityUSD();
82
+ const tokenPrice = await this.ptc().getTokenPriceAccurate();
83
+ const stakedLPExchangeRate = await this.lendingPool.getStakedLPExchangeRate();
84
+ return lpEquityUSD / tokenPrice * stakedLPExchangeRate;
85
+ }
86
+
87
+ // Debt
88
+ async getAccountAPR() : Promise<number> {
89
+ const depositedAUSD = await this.pta().getDepositedUSD();
90
+ const depositedBUSD = await this.ptb().getDepositedUSD();
91
+ const totalSupplied = depositedAUSD + depositedBUSD;
92
+ const supplyAPRA = await this.pta().getSupplyAPR();
93
+ const supplyAPRB = await this.ptb().getSupplyAPR();
94
+ return totalSupplied > 0 ? (depositedAUSD * supplyAPRA + depositedBUSD * supplyAPRB) / totalSupplied : 0;
95
+ }
96
+
97
+ // Values
98
+ async getValuesFromPrice(changes: Changes, priceA: number, priceB: number) {
99
+ const valueCollateral = await this.ptc().getDeposited() + changes.changeCollateral;
100
+ const amountA = await this.pta().getBorrowed() + changes.changeBorrowedA;
101
+ const amountB = await this.ptb().getBorrowed() + changes.changeBorrowedB;
102
+ const valueA = amountA * priceA;
103
+ const valueB = amountB * priceB;
104
+ return {
105
+ valueCollateral: valueCollateral > 0 ? valueCollateral : 0,
106
+ valueA: valueA > 0 ? valueA : 0,
107
+ valueB: valueB > 0 ? valueB : 0,
108
+ };
109
+ }
110
+ async getValues(changes: Changes) {
111
+ const [priceA, priceB] = await this.lendingPool.getPriceDenomLP();
112
+ return this.getValuesFromPrice(changes, priceA, priceB);
113
+ }
114
+ async getMarketValues(changes: Changes) {
115
+ const [priceA, priceB] = await this.lendingPool.getMarketPriceDenomLP();
116
+ return this.getValuesFromPrice(changes, priceA, priceB);
117
+ }
118
+
119
+ // Leverage
120
+ async getLeverage(changes?: Changes) : Promise<number> {
121
+ if (!changes) changes = NO_CHANGES;
122
+ const { valueCollateral, valueA, valueB } = await this.getValues(changes);
123
+ const valueDebt = valueA + valueB;
124
+ if (valueDebt == 0) return 1;
125
+ const equity = valueCollateral - valueDebt;
126
+ if (equity <= 0) return Infinity;
127
+ return valueDebt / equity + 1;
128
+ }
129
+
130
+ // Liquidation Threshold
131
+ async getLiquidationPriceSwingsGivenValues(values: Values) : Promise<[number, number]> {
132
+ if (!values) return [Infinity, Infinity];
133
+ const { valueCollateral, valueA, valueB } = values;
134
+ if (valueA + valueB == 0) return [Infinity, Infinity];
135
+ const safetyMargin = await this.getSafetyMargin();
136
+ const liquidationPenalty = await this.getLiquidationPenalty();
137
+ const actualCollateral = valueCollateral / liquidationPenalty;
138
+ const rad = Math.sqrt(actualCollateral ** 2 - 4 * valueA * valueB);
139
+ if (!rad) return [0, 0];
140
+ const t = (actualCollateral + rad) / (2 * Math.sqrt(safetyMargin));
141
+ let priceSwingA = (t / valueA) ** 2;
142
+ let priceSwingB = (t / valueB) ** 2;
143
+ return [priceSwingA, priceSwingB];
144
+ }
145
+ async getLiquidationPricesGivenValues(values: Values) : Promise<[number, number]> {
146
+ const currentPrice = await this.lendingPool.getTWAPPrice();
147
+ const [priceSwingA, priceSwingB] = await this.getLiquidationPriceSwingsGivenValues(values);
148
+ return !this.account.router.priceInverted ? [currentPrice / priceSwingB, currentPrice * priceSwingA] : [currentPrice / priceSwingA, currentPrice * priceSwingB];
149
+ }
150
+ async getLiquidationPriceSwings(changes?: Changes) {
151
+ const values = await this.getValues(changes);
152
+ return this.getLiquidationPriceSwingsGivenValues(values);
153
+ }
154
+ async getLiquidationPrices(changes?: Changes) {
155
+ if (!changes) changes = NO_CHANGES;
156
+ const values = await this.getValues(changes);
157
+ return this.getLiquidationPricesGivenValues(values);
158
+ }
159
+
160
+ // Max Leverage
161
+ async getMaxLeverage() : Promise<number> {
162
+ const availableCashA = await this.pta().poolToken.getTotalBalance();
163
+ const availableCashB = await this.ptb().poolToken.getTotalBalance();
164
+ const [priceA, priceB] = await this.lendingPool.getMarketPriceDenomLP();
165
+ const [priceATWAP,] = await this.lendingPool.getPriceDenomLP();
166
+ const diff = priceA > priceATWAP ? priceA / priceATWAP : priceATWAP / priceA;
167
+ const adjustFactor = 1 / diff;
168
+ const availableCashValue1 = availableCashA * priceA;
169
+ const availableCashValue2 = availableCashB * priceB;
170
+ const { valueCollateral, valueA, valueB } = await this.getValues(NO_CHANGES);
171
+ const safetyMargin = (await this.getSafetyMargin()) * this.getUiMargin();
172
+ const liquidationPenalty = await this.getLiquidationPenalty();
173
+ const actualCollateral = valueCollateral / liquidationPenalty;
174
+ const num1 = actualCollateral * Math.sqrt(safetyMargin) - valueA * safetyMargin - valueB;
175
+ const num2 = actualCollateral * Math.sqrt(safetyMargin) - valueB * safetyMargin - valueA;
176
+ const den = safetyMargin + 1 - 2 * Math.sqrt(safetyMargin) / liquidationPenalty;
177
+ const additionalValueBorrowablePerSide = Math.min(num1 / den, num2 / den, availableCashValue1, availableCashValue2) * adjustFactor;
178
+ const valueDebt = valueA + valueB;
179
+ const equity = valueCollateral - valueDebt;
180
+ if (equity == 0) return 1;
181
+ return (valueDebt + additionalValueBorrowablePerSide * 2) / equity + 1;
182
+ }
183
+
184
+ // Max Deleverage
185
+ async getMaxDeleverage(slippage: number) : Promise<number> {
186
+ const { valueCollateral, valueA, valueB } = await this.getMarketValues(NO_CHANGES);
187
+ const minRepayPerSide = valueCollateral / 2 / Math.sqrt(slippage);
188
+ if ( minRepayPerSide >= valueA && minRepayPerSide >= valueB ) {
189
+ return this.ptc().getDeposited();
190
+ }
191
+ if ( minRepayPerSide * 2 < valueA + valueB ) {
192
+ return 0;
193
+ }
194
+ return Math.min(valueA, valueB) * 2 * Math.sqrt(slippage);
195
+ }
196
+
197
+ // Available Reward
198
+ async getAvailableReward() : Promise<PendingRewardUI[]> {
199
+ const result = [];
200
+ const imxAmount = await this.pta().getAvailableIMXReward() + await this.ptb().getAvailableIMXReward();
201
+ if (imxAmount > 0) result.push({
202
+ symbol: "IMX",
203
+ amount: imxAmount
204
+ });
205
+ const symbols = [
206
+ await this.pta().poolToken.getRewarderTokenSymbol(),
207
+ await this.ptb().poolToken.getRewarderTokenSymbol()
208
+ ];
209
+ const rewarderAmounts = [
210
+ await this.pta().getAvailableRewarderReward(),
211
+ await this.ptb().getAvailableRewarderReward()
212
+ ];
213
+ if (symbols[0] === symbols[1]) {
214
+ const rewarderAmount = rewarderAmounts[0] + rewarderAmounts[1];
215
+ if (rewarderAmount === 0) return result;
216
+ result.push({
217
+ symbol: symbols[0],
218
+ amount: rewarderAmount
219
+ })
220
+ } else {
221
+ for (let i = 0; i < 2; i++) {
222
+ if (rewarderAmounts[i] === 0) continue;
223
+ result.push({
224
+ symbol: symbols[i],
225
+ amount: rewarderAmounts[i]
226
+ })
227
+ }
228
+ }
229
+ return result;
230
+ }
231
+ }
@@ -0,0 +1,76 @@
1
+ import AccountLendingPool from "./AccountLendingPool";
2
+ import PoolToken from "./PoolToken";
3
+ import { PoolTokenType } from '../config/types';
4
+ import { WETH } from '../config/contracts/weths';
5
+
6
+ export default class AccountPoolToken {
7
+ accountLendingPool: AccountLendingPool;
8
+ poolToken: PoolToken;
9
+ cache: {
10
+ availableBalance?: Promise<number>,
11
+ deposited?: Promise<number>,
12
+ } = {};
13
+
14
+ constructor(accountLendingPool: AccountLendingPool, poolTokenType: PoolTokenType) {
15
+ this.accountLendingPool = accountLendingPool;
16
+ this.poolToken = accountLendingPool.lendingPool.poolTokens[poolTokenType];
17
+ }
18
+
19
+ cleanCache() {
20
+ this.cache = {};
21
+ }
22
+
23
+ // Shortcuts
24
+ getAccount = () => this.accountLendingPool.account.account;
25
+ getNetwrok = () => this.accountLendingPool.account.router.network;
26
+ getEth = () => this.accountLendingPool.account.router.web3.eth;
27
+ getDust = () => this.accountLendingPool.account.router.dust;
28
+ getUiMargin = () => this.accountLendingPool.account.router.uiMargin;
29
+ getTokenPriceAccurate = async () => (await this.poolToken.getSubgraphPoolToken()).getTokenPriceAccurate();
30
+ getSupplyAPR = async () => (await this.poolToken.getSubgraphPoolToken()).getSupplyAPR();
31
+ getBorrowRate = async () => (await this.poolToken.getSubgraphPoolToken()).getBorrowRate();
32
+ getAccrualTimestamp = async () => (await this.poolToken.getSubgraphPoolToken()).getAccrualTimestamp();
33
+
34
+ // Available Balance
35
+ private async initializeAvailableBalance() : Promise<number> {
36
+ const token = await this.poolToken.getToken();
37
+ if (token._address.toLowerCase() == WETH[this.getNetwrok()].toLowerCase()) {
38
+ return (await this.getEth().getBalance(this.getAccount())) / 1e18 / this.getDust();
39
+ }
40
+ const balance = await token.methods.balanceOf(this.getAccount()).call();
41
+ return (await this.poolToken.normalize(balance)) / this.getDust();
42
+ }
43
+ async getAvailableBalance() : Promise<number> {
44
+ if (!this.cache.availableBalance) this.cache.availableBalance = this.initializeAvailableBalance();
45
+ return this.cache.availableBalance;
46
+ }
47
+ async getAvailableBalanceUSD() : Promise<number> {
48
+ const availableBalance = await this.getAvailableBalance();
49
+ const tokenPrice = await this.getTokenPriceAccurate();
50
+ return availableBalance * tokenPrice;
51
+ }
52
+
53
+ // Deposited
54
+ private async initializeDeposited() : Promise<number> {
55
+ const poolToken = await this.poolToken.getPoolToken();
56
+ const exchangeRate = await this.poolToken.getExchangeRate();
57
+ const balance = await poolToken.methods.balanceOf(this.getAccount()).call();
58
+ return (await this.poolToken.normalize(balance)) * exchangeRate;
59
+ }
60
+ async getDeposited() : Promise<number> {
61
+ if (!this.cache.deposited) this.cache.deposited = this.initializeDeposited();
62
+ return this.cache.deposited;
63
+ }
64
+ async getDepositedUSD() : Promise<number> {
65
+ const deposited = await this.getDeposited();
66
+ const tokenPrice = await this.getTokenPriceAccurate();
67
+ return deposited * tokenPrice;
68
+ }
69
+
70
+ // Max Withdrawable
71
+ async getMaxWithdrawable() : Promise<number> {
72
+ const deposited = await this.getDeposited();
73
+ const availableCash = await this.poolToken.getTotalBalance();
74
+ return Math.min(deposited, availableCash) / this.getDust();
75
+ }
76
+ }