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.
- package/.idea/impermax-sdk.iml +12 -0
- package/.idea/misc.xml +6 -0
- package/.idea/modules.xml +8 -0
- package/.idea/workspace.xml +642 -0
- package/abis/ImpermaxABI.ts +3 -0
- package/abis/contracts/BAllowance.json +4735 -0
- package/abis/contracts/BDeployer.json +1195 -0
- package/abis/contracts/BInterestRateModel.json +10796 -0
- package/abis/contracts/BSetter.json +6219 -0
- package/abis/contracts/BStorage.json +2613 -0
- package/abis/contracts/Borrowable.json +19937 -0
- package/abis/contracts/CDeployer.json +1104 -0
- package/abis/contracts/CSetter.json +5094 -0
- package/abis/contracts/CStorage.json +516 -0
- package/abis/contracts/ClaimAggregator.json +2015 -0
- package/abis/contracts/Collateral.json +21615 -0
- package/abis/contracts/ERC20.json +819 -0
- package/abis/contracts/Factory.json +21986 -0
- package/abis/contracts/FarmingPool.json +8601 -0
- package/abis/contracts/IBDeployer.json +351 -0
- package/abis/contracts/IBorrowTracker.json +346 -0
- package/abis/contracts/IBorrowable.json +13207 -0
- package/abis/contracts/ICDeployer.json +294 -0
- package/abis/contracts/IClaimable.json +406 -0
- package/abis/contracts/ICollateral.json +8952 -0
- package/abis/contracts/IERC20.json +2376 -0
- package/abis/contracts/IFactory.json +3660 -0
- package/abis/contracts/IFarmingPool.json +3584 -0
- package/abis/contracts/IImpermaxCallee.json +679 -0
- package/abis/contracts/IMerkleDistributor.json +1134 -0
- package/abis/contracts/IPoolToken.json +5343 -0
- package/abis/contracts/IRouter01.json +6891 -0
- package/abis/contracts/IRouter02.json +7283 -0
- package/abis/contracts/ISimpleUniswapOracle.json +1469 -0
- package/abis/contracts/IStakedLPToken.json +7309 -0
- package/abis/contracts/IStakingRewards.json +1036 -0
- package/abis/contracts/IUniswapV2Callee.json +403 -0
- package/abis/contracts/IUniswapV2ERC20.json +3155 -0
- package/abis/contracts/IUniswapV2Factory.json +1690 -0
- package/abis/contracts/IUniswapV2Pair.json +6761 -0
- package/abis/contracts/IWETH.json +561 -0
- package/abis/contracts/ImpermaxChef.json +20945 -0
- package/abis/contracts/ImpermaxERC20.json +12095 -0
- package/abis/contracts/Math.json +1966 -0
- package/abis/contracts/MockERC20.json +8884 -0
- package/abis/contracts/PoolToken.json +10784 -0
- package/abis/contracts/Router01.json +43963 -0
- package/abis/contracts/SafeMath.json +6828 -0
- package/abis/contracts/SimpleUniswapOracle.json +9640 -0
- package/abis/contracts/TransferHelper.json +4875 -0
- package/abis/contracts/UQ112x112.json +1201 -0
- package/abis/contracts/UniswapV2ERC20.json +10969 -0
- package/abis/contracts/UniswapV2Factory.json +5521 -0
- package/abis/contracts/UniswapV2Library.json +13789 -0
- package/abis/contracts/UniswapV2Pair.json +30782 -0
- package/abis/contracts/WETH9.json +6613 -0
- package/config/amms.ts +199 -0
- package/config/contracts/claim-aggregators.ts +16 -0
- package/config/contracts/impermax-chef.ts +16 -0
- package/config/contracts/imxes.ts +16 -0
- package/config/contracts/merkle-distributors.ts +13 -0
- package/config/contracts/routers.ts +36 -0
- package/config/contracts/simple-uniswap-oracles.ts +33 -0
- package/config/contracts/weths.ts +18 -0
- package/config/debank-ids.ts +15 -0
- package/config/endpoints/merkle-distributors.ts +13 -0
- package/config/eth.ts +32 -0
- package/config/factories.ts +26 -0
- package/config/farms.ts +119 -0
- package/config/general.ts +8 -0
- package/config/subgraphs.ts +69 -0
- package/config/types.ts +81 -0
- package/impermax-router/Account.ts +123 -0
- package/impermax-router/AccountBorrowable.ts +110 -0
- package/impermax-router/AccountCollateral.ts +40 -0
- package/impermax-router/AccountLendingPool.ts +231 -0
- package/impermax-router/AccountPoolToken.ts +76 -0
- package/impermax-router/Borrowable.ts +86 -0
- package/impermax-router/Collateral.ts +26 -0
- package/impermax-router/ContractsHelper.ts +64 -0
- package/impermax-router/ImpermaxFactory.ts +47 -0
- package/impermax-router/Interactions.ts +94 -0
- package/impermax-router/InteractionsLendingPool.ts +129 -0
- package/impermax-router/InteractionsPoolToken.ts +187 -0
- package/impermax-router/LendingPool.ts +256 -0
- package/impermax-router/PoolToken.ts +112 -0
- package/impermax-router/index.ts +49 -0
- package/impermax-router/interfaces.ts +233 -0
- package/index.ts +5 -0
- package/package.json +23 -0
- package/subgraph/Account.ts +93 -0
- package/subgraph/AccountLendingPool.ts +60 -0
- package/subgraph/AccountPoolToken.ts +60 -0
- package/subgraph/LendingPool.ts +179 -0
- package/subgraph/PoolToken.ts +381 -0
- package/subgraph/PriceHelper.ts +150 -0
- package/subgraph/SolidexHelper.ts +54 -0
- package/subgraph/index.ts +166 -0
- package/subgraph/initializer.ts +509 -0
- package/subgraph/query.ts +224 -0
- package/tsconfig.json +16 -0
- package/utils/ether-utils.ts +22 -0
- package/utils/index.ts +12 -0
package/config/types.ts
ADDED
|
@@ -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
|
+
}
|