@raac/rpc 2.0.0-beta.5 → 2.0.0-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/RPCLibrary.js +46 -8
- package/dist/configs/chains/11155111.json +32 -2
- package/dist/configs/chains/8453.json +23 -2
- package/dist/contracts/curve/common/_helpers.js +39 -0
- package/dist/contracts/curve/common/getPools.js +36 -0
- package/dist/contracts/curve/common/getVirtualPrice.js +26 -0
- package/dist/contracts/curve/common/index.js +18 -0
- package/dist/contracts/findContract.js +3 -3
- package/dist/contracts/getContractAddress.js +1 -0
- package/dist/contracts/governance/gaugeSystems/_helpers.js +2 -2
- package/dist/contracts/zeno/_helpers.js +43 -0
- package/dist/contracts/zeno/buy.js +27 -0
- package/dist/contracts/zeno/getAuctionState.js +45 -0
- package/dist/contracts/zeno/getAuctions.js +7 -4
- package/dist/contracts/zeno/getBondState.js +47 -0
- package/dist/contracts/zeno/getPrice.js +23 -0
- package/dist/contracts/zeno/getZenos.js +6 -6
- package/dist/contracts/zeno/grantRole.js +46 -0
- package/dist/contracts/zeno/pause.js +42 -0
- package/dist/contracts/zeno/priceAt.js +20 -0
- package/dist/contracts/zeno/quoteBuy.js +33 -0
- package/dist/contracts/zeno/redeem.js +44 -0
- package/dist/contracts/zeno/setAuction.js +25 -0
- package/dist/contracts/zeno/sweepSurplus.js +26 -0
- package/dist/pools/lendingPool/adapters/erc20/getERC20AdapterBalance.js +4 -4
- package/dist/types/RPCLibrary.d.ts +33 -1
- package/dist/types/contracts/curve/common/_helpers.d.ts +16 -0
- package/dist/types/contracts/curve/common/getPools.d.ts +30 -0
- package/dist/types/contracts/curve/common/getVirtualPrice.d.ts +13 -0
- package/dist/types/contracts/curve/common/index.d.ts +2 -0
- package/dist/types/contracts/zeno/_helpers.d.ts +17 -0
- package/dist/types/contracts/zeno/buy.d.ts +15 -0
- package/dist/types/contracts/zeno/getAuctionState.d.ts +45 -0
- package/dist/types/contracts/zeno/getAuctions.d.ts +3 -2
- package/dist/types/contracts/zeno/getBondState.d.ts +42 -0
- package/dist/types/contracts/zeno/getPrice.d.ts +12 -0
- package/dist/types/contracts/zeno/getZenos.d.ts +2 -2
- package/dist/types/contracts/zeno/grantRole.d.ts +22 -0
- package/dist/types/contracts/zeno/pause.d.ts +18 -0
- package/dist/types/contracts/zeno/priceAt.d.ts +20 -0
- package/dist/types/contracts/zeno/quoteBuy.d.ts +34 -0
- package/dist/types/contracts/zeno/redeem.d.ts +20 -0
- package/dist/types/contracts/zeno/setAuction.d.ts +13 -0
- package/dist/types/contracts/zeno/sweepSurplus.d.ts +14 -0
- package/dist/types/pools/lendingPool/adapters/erc20/getERC20AdapterBalance.d.ts +3 -3
- package/dist/types/user/_helpers.d.ts +23 -0
- package/dist/types/user/computeEffectiveReturn.d.ts +67 -0
- package/dist/types/user/getUserClaimables.d.ts +55 -0
- package/dist/types/user/getUserCollateral.d.ts +12 -0
- package/dist/types/user/getUserDebt.d.ts +18 -0
- package/dist/types/user/getUserFarming.d.ts +64 -0
- package/dist/types/user/getUserGovernance.d.ts +20 -0
- package/dist/types/user/getUserPortfolio.d.ts +61 -0
- package/dist/types/user/getUserWalletBalances.d.ts +20 -0
- package/dist/types/user/index.d.ts +9 -0
- package/dist/types/utils/amount.d.ts +7 -0
- package/dist/types/utils/contracts.d.ts +2 -0
- package/dist/user/_helpers.js +22 -0
- package/dist/user/computeEffectiveReturn.js +33 -0
- package/dist/user/getUserClaimables.js +97 -0
- package/dist/user/getUserCollateral.js +29 -0
- package/dist/user/getUserDebt.js +32 -0
- package/dist/user/getUserFarming.js +100 -0
- package/dist/user/getUserGovernance.js +35 -0
- package/dist/user/getUserPortfolio.js +109 -0
- package/dist/user/getUserWalletBalances.js +30 -0
- package/dist/user/index.js +24 -0
- package/dist/utils/amount.js +10 -0
- package/dist/utils/contracts.js +5 -2
- package/package.json +1 -1
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getUserClaimables = void 0;
|
|
7
|
+
const ethers_1 = require("ethers");
|
|
8
|
+
const chains_1 = __importDefault(require("../configs/chains"));
|
|
9
|
+
const contracts_1 = require("../utils/contracts");
|
|
10
|
+
const getContractAddress_1 = require("../contracts/getContractAddress");
|
|
11
|
+
const getGauges_1 = require("../contracts/governance/gaugeSystems/getGauges");
|
|
12
|
+
const getUserClaimableRewards_1 = require("../contracts/governance/gauge/getUserClaimableRewards");
|
|
13
|
+
const getRewardTokens_1 = require("../contracts/tokens/veRAAC/getRewardTokens");
|
|
14
|
+
const getMaxUnclaimedDistributions_1 = require("../contracts/tokens/veRAAC/getMaxUnclaimedDistributions");
|
|
15
|
+
const getClaimable_1 = require("../contracts/tokens/veRAAC/getClaimable");
|
|
16
|
+
const getUserInfo_1 = require("../contracts/lockers/maturityVault/getUserInfo");
|
|
17
|
+
const getClaimables_1 = require("../contracts/lockers/botLocker/getClaimables");
|
|
18
|
+
const _helpers_1 = require("./_helpers");
|
|
19
|
+
const toSource = (tokens) => ({ available: true, tokens: tokens.filter(({ amount }) => amount.value > 0n), error: null });
|
|
20
|
+
const failed = (error, available = true) => ({ available, tokens: [], error });
|
|
21
|
+
/** A locker's address when the chain config has it deployed; null when the chain has none. */
|
|
22
|
+
function deployedLocker(chainId, name) {
|
|
23
|
+
const lockers = chains_1.default[chainId].lockers;
|
|
24
|
+
const address = lockers?.[name]?.contract;
|
|
25
|
+
return (0, contracts_1.isDeployed)(address) ? address : null;
|
|
26
|
+
}
|
|
27
|
+
async function readVeRAAC(chainId, user, provider) {
|
|
28
|
+
const rewardTokens = await (0, getRewardTokens_1.getRewardTokens)(chainId, provider);
|
|
29
|
+
return Promise.all(rewardTokens.map(async (token) => {
|
|
30
|
+
const maxDistributions = await (0, getMaxUnclaimedDistributions_1.getMaxUnclaimedDistributions)(chainId, user, token, provider);
|
|
31
|
+
const amount = await (0, getClaimable_1.getClaimable)(chainId, user, token, maxDistributions, provider);
|
|
32
|
+
return { token, amount };
|
|
33
|
+
}));
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Everything a user can claim right now, across gauges, veRAAC fees, the maturity vault and the bot locker. Amounts
|
|
37
|
+
* only: the caller values them in USD with its own prices. Each source (and each gauge) is read independently, so one
|
|
38
|
+
* failing read shows as an error on that source and every other source still returns.
|
|
39
|
+
*
|
|
40
|
+
* Every configured gauge is scanned, not only those the user is staked in: a user can still have rewards after fully
|
|
41
|
+
* unstaking.
|
|
42
|
+
*
|
|
43
|
+
* @param chainId - The chain/network to use.
|
|
44
|
+
* @param user - The user's address.
|
|
45
|
+
* @param provider - Optional ethers.js Provider instance.
|
|
46
|
+
* @returns Per source: the non-zero claimable tokens, and an `error` when it failed.
|
|
47
|
+
*/
|
|
48
|
+
const getUserClaimables = async (chainId, user, provider) => {
|
|
49
|
+
if (!ethers_1.ethers.isAddress(user)) {
|
|
50
|
+
throw new Error(`Invalid user address: ${user}`);
|
|
51
|
+
}
|
|
52
|
+
if (!provider) {
|
|
53
|
+
const rpc = chains_1.default[chainId].rpcs[0];
|
|
54
|
+
provider = new ethers_1.ethers.JsonRpcProvider(rpc);
|
|
55
|
+
}
|
|
56
|
+
const gauges = (0, getGauges_1.getGauges)(chainId).filter((gauge) => (0, contracts_1.isDeployed)(gauge.contract));
|
|
57
|
+
const maturityVault = deployedLocker(chainId, "raacmaturityvault");
|
|
58
|
+
const botLocker = deployedLocker(chainId, "raacbotlocker");
|
|
59
|
+
const [gaugeResults, veRAACResult, maturityResult, botResult] = await Promise.all([
|
|
60
|
+
Promise.all(gauges.map((gauge) => (0, _helpers_1.settled)((0, getUserClaimableRewards_1.getUserClaimableRewards)(chainId, gauge.contract, user, provider)))),
|
|
61
|
+
(0, _helpers_1.settled)(readVeRAAC(chainId, user, provider)),
|
|
62
|
+
maturityVault ? (0, _helpers_1.settled)((0, getUserInfo_1.getUserInfo)(chainId, user, provider)) : null,
|
|
63
|
+
botLocker ? (0, _helpers_1.settled)((0, getClaimables_1.getClaimables)(chainId, user, provider)) : null,
|
|
64
|
+
]);
|
|
65
|
+
const items = gauges.map((gauge, i) => {
|
|
66
|
+
const result = gaugeResults[i];
|
|
67
|
+
const base = { gauge: gauge.contract, id: gauge.id, name: gauge.name, system: gauge.system };
|
|
68
|
+
if (result.error)
|
|
69
|
+
return { ...base, ...failed(result.error) };
|
|
70
|
+
const { rewardToken, inGauge, extras } = result.data;
|
|
71
|
+
return { ...base, ...toSource([{ token: rewardToken, amount: inGauge }, ...extras.map(({ token, amount }) => ({ token, amount }))]) };
|
|
72
|
+
});
|
|
73
|
+
const gaugeErrors = items.filter((item) => item.error).map((item) => item.error);
|
|
74
|
+
const gaugesSource = {
|
|
75
|
+
available: items.length > 0,
|
|
76
|
+
tokens: items.flatMap((item) => item.tokens),
|
|
77
|
+
error: gaugeErrors.length > 1 ? new Error(`${gaugeErrors.length} gauges could not be read`) : (gaugeErrors[0] ?? null),
|
|
78
|
+
items,
|
|
79
|
+
claimableCount: items.filter((item) => item.tokens.length > 0).length,
|
|
80
|
+
};
|
|
81
|
+
const veRAAC = veRAACResult.error ? failed(veRAACResult.error) : toSource(veRAACResult.data);
|
|
82
|
+
const maturity = !maturityResult
|
|
83
|
+
? failed(null, false)
|
|
84
|
+
: maturityResult.error
|
|
85
|
+
? failed(maturityResult.error)
|
|
86
|
+
: toSource([{ token: (0, getContractAddress_1.getContractAddress)(chainId, "raactoken"), amount: maturityResult.data.realised }]);
|
|
87
|
+
const bot = !botResult ? failed(null, false) : botResult.error ? failed(botResult.error) : toSource(botResult.data);
|
|
88
|
+
return {
|
|
89
|
+
chainId,
|
|
90
|
+
user: ethers_1.ethers.getAddress(user),
|
|
91
|
+
gauges: gaugesSource,
|
|
92
|
+
veRAAC,
|
|
93
|
+
maturityVault: maturity,
|
|
94
|
+
botLocker: bot,
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
exports.getUserClaimables = getUserClaimables;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getUserCollateral = void 0;
|
|
7
|
+
const ethers_1 = require("ethers");
|
|
8
|
+
const chains_1 = __importDefault(require("../configs/chains"));
|
|
9
|
+
const getAdapterAddress_1 = require("../pools/lendingPool/getAdapterAddress");
|
|
10
|
+
const getERC20AdapterBalance_1 = __importDefault(require("../pools/lendingPool/adapters/erc20/getERC20AdapterBalance"));
|
|
11
|
+
/**
|
|
12
|
+
* The iREET a user has deposited as collateral in the lending pool's iREET adapter. NFT collateral is not included.
|
|
13
|
+
*
|
|
14
|
+
* @param chainId - The chain/network to use.
|
|
15
|
+
* @param user - The user's address.
|
|
16
|
+
* @param provider - Optional ethers.js Provider instance.
|
|
17
|
+
* @returns The deposited iREET, with its registry decimals and symbol.
|
|
18
|
+
*/
|
|
19
|
+
const getUserCollateral = async (chainId, user, provider) => {
|
|
20
|
+
if (!provider) {
|
|
21
|
+
const rpc = chains_1.default[chainId].rpcs[0];
|
|
22
|
+
provider = new ethers_1.ethers.JsonRpcProvider(rpc);
|
|
23
|
+
}
|
|
24
|
+
const adapter = (0, getAdapterAddress_1.getAdapterAddressOnly)(chainId, "rwaindextokenadapter");
|
|
25
|
+
if (!adapter)
|
|
26
|
+
throw new Error("The iREET adapter is not configured on this chain");
|
|
27
|
+
return (0, getERC20AdapterBalance_1.default)(chainId, adapter, user, provider);
|
|
28
|
+
};
|
|
29
|
+
exports.getUserCollateral = getUserCollateral;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getUserDebt = void 0;
|
|
7
|
+
const ethers_1 = require("ethers");
|
|
8
|
+
const chains_1 = __importDefault(require("../configs/chains"));
|
|
9
|
+
const getPositionView_1 = __importDefault(require("../pools/lendingPool/getPositionView"));
|
|
10
|
+
/** Position data of the single iREET position: the ERC20 adapter keys it by `abi.encode(uint256(0))`. */
|
|
11
|
+
const IREET_POSITION_DATA = ethers_1.AbiCoder.defaultAbiCoder().encode(["uint256"], [0]);
|
|
12
|
+
/** The lending pool's health factor for a position with no debt (1e27). Scaled as WAD otherwise. */
|
|
13
|
+
const NO_DEBT_HEALTH_FACTOR = 10n ** 27n;
|
|
14
|
+
/**
|
|
15
|
+
* The debt and health factor of a user's iREET position in the lending pool.
|
|
16
|
+
*
|
|
17
|
+
* @param chainId - The chain/network to use.
|
|
18
|
+
* @param user - The user's address.
|
|
19
|
+
* @param provider - Optional ethers.js Provider instance.
|
|
20
|
+
* @returns The debt, and the health factor when there is debt.
|
|
21
|
+
*/
|
|
22
|
+
const getUserDebt = async (chainId, user, provider) => {
|
|
23
|
+
if (!provider) {
|
|
24
|
+
const rpc = chains_1.default[chainId].rpcs[0];
|
|
25
|
+
provider = new ethers_1.ethers.JsonRpcProvider(rpc);
|
|
26
|
+
}
|
|
27
|
+
const view = await (0, getPositionView_1.default)(chainId, "rwaindextokenadapter", user, IREET_POSITION_DATA, provider);
|
|
28
|
+
const amount = view.scaledDebtBalance;
|
|
29
|
+
const hasDebt = amount.value > 0n && view.healthFactor.value < NO_DEBT_HEALTH_FACTOR;
|
|
30
|
+
return { amount, healthFactor: hasDebt ? view.healthFactor : null };
|
|
31
|
+
};
|
|
32
|
+
exports.getUserDebt = getUserDebt;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getUserFarming = void 0;
|
|
7
|
+
const ethers_1 = require("ethers");
|
|
8
|
+
const chains_1 = __importDefault(require("../configs/chains"));
|
|
9
|
+
const amount_1 = require("../utils/amount");
|
|
10
|
+
const contracts_1 = require("../utils/contracts");
|
|
11
|
+
const getRawBalanceOf_1 = require("../contracts/governance/gauge/getRawBalanceOf");
|
|
12
|
+
const getUserApy_1 = require("../contracts/governance/gauge/getUserApy");
|
|
13
|
+
const getGauges_1 = require("../contracts/governance/gaugeSystems/getGauges");
|
|
14
|
+
const _helpers_1 = require("../contracts/curve/common/_helpers");
|
|
15
|
+
const _helpers_2 = require("./_helpers");
|
|
16
|
+
/** The lending pool's receipts earn the supply rate wherever they sit; no other coin earns on its own. */
|
|
17
|
+
const coinYield = (asset) => (asset === "rtoken" || asset === "detoken" ? "supply" : "none");
|
|
18
|
+
/** Every registered asset's price keyed by address, as `getUserApy` takes reward token prices. */
|
|
19
|
+
function pricesByAddress(chainId, prices) {
|
|
20
|
+
const assets = (chains_1.default[chainId]?.assets ?? {});
|
|
21
|
+
const byAddress = {};
|
|
22
|
+
for (const { contract, symbol } of Object.values(assets)) {
|
|
23
|
+
const price = symbol === undefined ? undefined : prices[symbol.toLowerCase()];
|
|
24
|
+
if (contract && typeof price === "number" && Number.isFinite(price))
|
|
25
|
+
byAddress[contract.toLowerCase()] = price;
|
|
26
|
+
}
|
|
27
|
+
return byAddress;
|
|
28
|
+
}
|
|
29
|
+
async function readPool(chainId, user, entry, gauges, prices, rewardTokenPricesUSD, provider) {
|
|
30
|
+
const contract = new ethers_1.ethers.Contract(entry.contract, _helpers_1.CURVE_POOL_READ_ABI, provider);
|
|
31
|
+
const [walletLp, staked] = await Promise.all([
|
|
32
|
+
contract.balanceOf(user),
|
|
33
|
+
Promise.all(gauges.map((gauge) => (0, getRawBalanceOf_1.getRawBalanceOf)(chainId, gauge.contract, user, provider))),
|
|
34
|
+
]);
|
|
35
|
+
const stakedLp = staked.reduce((sum, amount) => sum + amount.value, 0n);
|
|
36
|
+
const userLp = walletLp + stakedLp;
|
|
37
|
+
// Pool reserves and prices are only needed when the user holds some of the pool's LP.
|
|
38
|
+
let coins = [];
|
|
39
|
+
let lpPriceUsd = 0;
|
|
40
|
+
if (userLp > 0n) {
|
|
41
|
+
const [totalSupply, ...balances] = (await Promise.all([
|
|
42
|
+
contract.totalSupply(),
|
|
43
|
+
...entry.coins.map((_, i) => contract.balances(i)),
|
|
44
|
+
]));
|
|
45
|
+
coins = entry.coins.map((asset, i) => {
|
|
46
|
+
const amount = (0, amount_1.toTokenAmount)(chainId, asset, (balances[i] * userLp) / totalSupply);
|
|
47
|
+
return { asset, amount, valueUsd: (0, _helpers_2.requireUsd)(prices, amount, asset), yield: coinYield(asset) };
|
|
48
|
+
});
|
|
49
|
+
lpPriceUsd = coins.reduce((sum, coin) => sum + coin.valueUsd, 0) / (0, amount_1.toNumber)((0, amount_1.toAmount)(userLp, amount_1.WAD_DECIMALS));
|
|
50
|
+
}
|
|
51
|
+
const farmed = await Promise.all(gauges.map(async (gauge, i) => {
|
|
52
|
+
let apr = null;
|
|
53
|
+
if (staked[i].value > 0n) {
|
|
54
|
+
const apy = await (0, getUserApy_1.getUserApy)(chainId, gauge.contract, user, { stakingTokenPriceUSD: lpPriceUsd, rewardTokenPricesUSD }, {}, provider);
|
|
55
|
+
// getUserApy reports simple annualized percent, not compounded: an APR.
|
|
56
|
+
apr = apy.current.apy === null ? null : apy.current.apy / 100;
|
|
57
|
+
}
|
|
58
|
+
return { id: gauge.id, gauge: gauge.contract, name: gauge.name, pool: entry.id, staked: staked[i], valueUsd: (0, amount_1.toNumber)(staked[i]) * lpPriceUsd, apr };
|
|
59
|
+
}));
|
|
60
|
+
return {
|
|
61
|
+
pool: { pool: entry.id, address: entry.contract, walletLp: (0, amount_1.toAmount)(walletLp, amount_1.WAD_DECIMALS), stakedLp: (0, amount_1.toAmount)(stakedLp, amount_1.WAD_DECIMALS), coins },
|
|
62
|
+
gauges: farmed,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* A user's Curve LP and gauge positions: every configured Curve pool that a configured gauge stakes, the user's share
|
|
67
|
+
* of each pool's coins, and their stake and boosted APR in each gauge. Each pool is read independently, so one failing
|
|
68
|
+
* pool shows in `errors` and the rest still return. Reserves, prices and APRs are only read for pools the user holds.
|
|
69
|
+
*
|
|
70
|
+
* @param chainId - The chain/network to use.
|
|
71
|
+
* @param user - The user's address.
|
|
72
|
+
* @param prices - USD per whole token, keyed by lowercase symbol. Needs every coin of a pool the user holds, and the
|
|
73
|
+
* reward tokens of a gauge the user is staked in.
|
|
74
|
+
* @param provider - Optional ethers.js Provider instance.
|
|
75
|
+
* @returns The pools and gauges read, and the pools that failed.
|
|
76
|
+
*/
|
|
77
|
+
const getUserFarming = async (chainId, user, prices, provider) => {
|
|
78
|
+
if (!provider) {
|
|
79
|
+
const rpc = chains_1.default[chainId].rpcs[0];
|
|
80
|
+
provider = new ethers_1.ethers.JsonRpcProvider(rpc);
|
|
81
|
+
}
|
|
82
|
+
const gauges = (0, getGauges_1.getGauges)(chainId).filter((gauge) => (0, contracts_1.isDeployed)(gauge.contract) && gauge.stakingToken);
|
|
83
|
+
const farmed = Object.values((0, _helpers_1.getCurvePools)(chainId))
|
|
84
|
+
.map((pool) => ({ pool, gauges: gauges.filter((gauge) => gauge.stakingToken.toLowerCase() === pool.contract.toLowerCase()) }))
|
|
85
|
+
.filter((entry) => entry.gauges.length > 0);
|
|
86
|
+
const rewardTokenPricesUSD = pricesByAddress(chainId, prices);
|
|
87
|
+
const results = await Promise.all(farmed.map((entry) => (0, _helpers_2.settled)(readPool(chainId, user, entry.pool, entry.gauges, prices, rewardTokenPricesUSD, provider))));
|
|
88
|
+
const farming = { pools: [], gauges: [], errors: [] };
|
|
89
|
+
results.forEach((result, i) => {
|
|
90
|
+
if (result.error) {
|
|
91
|
+
farming.errors.push({ pool: farmed[i].pool.id, error: result.error });
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
farming.pools.push(result.data.pool);
|
|
95
|
+
farming.gauges.push(...result.data.gauges);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
return farming;
|
|
99
|
+
};
|
|
100
|
+
exports.getUserFarming = getUserFarming;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getUserGovernance = void 0;
|
|
7
|
+
const ethers_1 = require("ethers");
|
|
8
|
+
const chains_1 = __importDefault(require("../configs/chains"));
|
|
9
|
+
const amount_1 = require("../utils/amount");
|
|
10
|
+
const getContractAddress_1 = require("../contracts/getContractAddress");
|
|
11
|
+
const getBalance_1 = require("../contracts/tokens/veRAAC/getBalance");
|
|
12
|
+
const getTotalSupply_1 = require("../contracts/tokens/veRAAC/getTotalSupply");
|
|
13
|
+
const getLockedBalance_1 = require("../contracts/tokens/veRAAC/getLockedBalance");
|
|
14
|
+
/**
|
|
15
|
+
* A user's veRAAC governance position: voting power, its share of the total, and the RAAC locked.
|
|
16
|
+
*
|
|
17
|
+
* @param chainId - The chain/network to use.
|
|
18
|
+
* @param user - The user's address.
|
|
19
|
+
* @param provider - Optional ethers.js Provider instance.
|
|
20
|
+
* @returns The voting power, total voting power, share and locked RAAC.
|
|
21
|
+
*/
|
|
22
|
+
const getUserGovernance = async (chainId, user, provider) => {
|
|
23
|
+
if (!provider) {
|
|
24
|
+
const rpc = chains_1.default[chainId].rpcs[0];
|
|
25
|
+
provider = new ethers_1.ethers.JsonRpcProvider(rpc);
|
|
26
|
+
}
|
|
27
|
+
const [votingPower, totalVotingPower, locked] = await Promise.all([
|
|
28
|
+
(0, getBalance_1.getBalance)(chainId, user, provider),
|
|
29
|
+
(0, getTotalSupply_1.getTotalSupply)(chainId, (0, getContractAddress_1.getContractAddress)(chainId, "veraac"), provider),
|
|
30
|
+
(0, getLockedBalance_1.getLockedBalance)(chainId, user, provider),
|
|
31
|
+
]);
|
|
32
|
+
const total = (0, amount_1.toNumber)(totalVotingPower);
|
|
33
|
+
return { votingPower, totalVotingPower, share: total > 0 ? (0, amount_1.toNumber)(votingPower) / total : null, locked };
|
|
34
|
+
};
|
|
35
|
+
exports.getUserGovernance = getUserGovernance;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getUserPortfolio = void 0;
|
|
7
|
+
const ethers_1 = require("ethers");
|
|
8
|
+
const chains_1 = __importDefault(require("../configs/chains"));
|
|
9
|
+
const amount_1 = require("../utils/amount");
|
|
10
|
+
const getLendingPoolInfo_1 = __importDefault(require("../pools/lendingPool/getLendingPoolInfo"));
|
|
11
|
+
const computeEffectiveReturn_1 = require("./computeEffectiveReturn");
|
|
12
|
+
const getUserCollateral_1 = require("./getUserCollateral");
|
|
13
|
+
const getUserDebt_1 = require("./getUserDebt");
|
|
14
|
+
const getUserWalletBalances_1 = require("./getUserWalletBalances");
|
|
15
|
+
const getUserFarming_1 = require("./getUserFarming");
|
|
16
|
+
const getUserGovernance_1 = require("./getUserGovernance");
|
|
17
|
+
const _helpers_1 = require("./_helpers");
|
|
18
|
+
const unavailable = (what, sections) => {
|
|
19
|
+
const failed = sections.filter((section) => section.error);
|
|
20
|
+
return failed.length ? new Error(`${what} needs data that could not be read: ${failed.map((section) => section.error.message).join("; ")}`) : null;
|
|
21
|
+
};
|
|
22
|
+
/** Builds the effective return legs from the reads. Each unit of capital earns each yield once. */
|
|
23
|
+
function buildLegs(debt, rates, wallet, farming) {
|
|
24
|
+
const legs = [];
|
|
25
|
+
if (debt.valueUsd > 0) {
|
|
26
|
+
legs.push({ id: "borrow", source: "Borrow pmUSD", kind: "cost", yieldType: "borrow", countsAsCapital: false, valueUsd: debt.valueUsd, rate: (0, amount_1.toNumber)(rates.borrowApy), rateKind: "apy" });
|
|
27
|
+
}
|
|
28
|
+
// Every RpmUSD and DEpmUSD the user owns earns the supply rate wherever it sits: the wallet, inside an LP, or LP staked in a gauge.
|
|
29
|
+
const coins = farming.pools.flatMap((pool) => pool.coins);
|
|
30
|
+
const suppliedUsd = wallet.suppliedUsd + coins.filter((coin) => coin.yield === "supply").reduce((sum, coin) => sum + coin.valueUsd, 0);
|
|
31
|
+
if (suppliedUsd > 0) {
|
|
32
|
+
legs.push({ id: "supply", source: "Supplied (RpmUSD + DEpmUSD)", kind: "earning", yieldType: "supply", countsAsCapital: true, valueUsd: suppliedUsd, rate: (0, amount_1.toNumber)(rates.supplyApy), rateKind: "apr" });
|
|
33
|
+
}
|
|
34
|
+
// A gauge is an overlay: its staked value is already counted through its coins, so it earns rewards but adds no capital.
|
|
35
|
+
for (const gauge of farming.gauges) {
|
|
36
|
+
if (gauge.staked.value === 0n)
|
|
37
|
+
continue;
|
|
38
|
+
legs.push({ id: `gauge:${gauge.id}`, source: gauge.name, kind: "earning", yieldType: "gauge", countsAsCapital: false, valueUsd: gauge.valueUsd, rate: gauge.apr ?? 0, rateKind: "apr" });
|
|
39
|
+
}
|
|
40
|
+
const idleUsd = wallet.idleUsd + coins.filter((coin) => coin.yield === "none").reduce((sum, coin) => sum + coin.valueUsd, 0);
|
|
41
|
+
return { legs, idleUsd };
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The user's position across the app: iREET collateral, debt and health factor, wallet RpmUSD/DEpmUSD/pmUSD, LP and
|
|
45
|
+
* gauge positions, veRAAC governance power, and the projected effective return built from them. Each part is read
|
|
46
|
+
* independently, so one failing read shows as an `error` on that part and the rest still return.
|
|
47
|
+
*
|
|
48
|
+
* Only the user's position data is returned; pool parameters such as the liquidation threshold stay with the callers
|
|
49
|
+
* that need them. iREET is the only collateral read (NFT positions are out of scope).
|
|
50
|
+
*
|
|
51
|
+
* @param chainId - The chain/network to use.
|
|
52
|
+
* @param user - The user's address.
|
|
53
|
+
* @param prices - USD per whole token, keyed by lowercase symbol. iREET, pmUSD, RpmUSD, DEpmUSD and RAAC are needed,
|
|
54
|
+
* plus every coin of a Curve pool the user holds.
|
|
55
|
+
* @param provider - Optional ethers.js Provider instance. Read-only is enough.
|
|
56
|
+
* @returns Each part as `{ data, error }`, the effective return legs, and the computed effective return.
|
|
57
|
+
*/
|
|
58
|
+
const getUserPortfolio = async (chainId, user, prices, provider) => {
|
|
59
|
+
if (!ethers_1.ethers.isAddress(user)) {
|
|
60
|
+
throw new Error(`Invalid user address: ${user}`);
|
|
61
|
+
}
|
|
62
|
+
if (!provider) {
|
|
63
|
+
const rpc = chains_1.default[chainId].rpcs[0];
|
|
64
|
+
provider = new ethers_1.ethers.JsonRpcProvider(rpc);
|
|
65
|
+
}
|
|
66
|
+
const [collateral, debt, rates, wallet, farming, governance] = await Promise.all([
|
|
67
|
+
(0, _helpers_1.settled)((0, getUserCollateral_1.getUserCollateral)(chainId, user, provider).then((amount) => ({ amount, valueUsd: (0, _helpers_1.requireUsd)(prices, amount, "rwaindextoken") }))),
|
|
68
|
+
(0, _helpers_1.settled)((0, getUserDebt_1.getUserDebt)(chainId, user, provider).then((debt) => ({ ...debt, valueUsd: (0, _helpers_1.requireUsd)(prices, debt.amount, chains_1.default[chainId].pools.lendingpool.asset ?? "debt") }))),
|
|
69
|
+
(0, _helpers_1.settled)((0, getLendingPoolInfo_1.default)(chainId, undefined, provider).then(({ borrowRate, borrowApy, supplyApy }) => ({ borrowRate, borrowApy, supplyApy }))),
|
|
70
|
+
(0, _helpers_1.settled)((0, getUserWalletBalances_1.getUserWalletBalances)(chainId, user, provider).then((balances) => ({
|
|
71
|
+
...balances,
|
|
72
|
+
suppliedUsd: (0, _helpers_1.requireUsd)(prices, balances.rtoken, "rtoken") + (0, _helpers_1.requireUsd)(prices, balances.detoken, "detoken"),
|
|
73
|
+
idleUsd: (0, _helpers_1.requireUsd)(prices, balances.pmusd, "pmusd"),
|
|
74
|
+
}))),
|
|
75
|
+
(0, _helpers_1.settled)((0, getUserFarming_1.getUserFarming)(chainId, user, prices, provider)),
|
|
76
|
+
(0, _helpers_1.settled)((0, getUserGovernance_1.getUserGovernance)(chainId, user, provider)),
|
|
77
|
+
]);
|
|
78
|
+
const netPositionError = unavailable("Net RAACLend position", [collateral, debt, wallet]);
|
|
79
|
+
const netPositionUsd = netPositionError
|
|
80
|
+
? { data: null, error: netPositionError }
|
|
81
|
+
: { data: collateral.data.valueUsd + wallet.data.suppliedUsd - debt.data.valueUsd, error: null };
|
|
82
|
+
// A pool that failed may hold part of the user's capital, so the return cannot be computed without it.
|
|
83
|
+
const farmingErrors = (farming.data?.errors ?? []).map(({ pool, error }) => ({ data: null, error: new Error(`Curve pool ${pool}: ${error.message}`) }));
|
|
84
|
+
let legs = [];
|
|
85
|
+
let effectiveReturn;
|
|
86
|
+
const returnError = unavailable("Effective return", [collateral, debt, rates, wallet, farming, ...farmingErrors]);
|
|
87
|
+
if (returnError) {
|
|
88
|
+
effectiveReturn = { data: null, error: returnError };
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
const built = buildLegs(debt.data, rates.data, wallet.data, farming.data);
|
|
92
|
+
legs = built.legs;
|
|
93
|
+
effectiveReturn = { data: (0, computeEffectiveReturn_1.computeEffectiveReturn)({ legs, collateralUsd: collateral.data.valueUsd, idleUsd: built.idleUsd }), error: null };
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
chainId,
|
|
97
|
+
user: ethers_1.ethers.getAddress(user),
|
|
98
|
+
collateral,
|
|
99
|
+
debt,
|
|
100
|
+
rates,
|
|
101
|
+
wallet,
|
|
102
|
+
farming,
|
|
103
|
+
governance,
|
|
104
|
+
netPositionUsd,
|
|
105
|
+
legs,
|
|
106
|
+
effectiveReturn,
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
exports.getUserPortfolio = getUserPortfolio;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getUserWalletBalances = void 0;
|
|
7
|
+
const ethers_1 = require("ethers");
|
|
8
|
+
const chains_1 = __importDefault(require("../configs/chains"));
|
|
9
|
+
const getBalance_1 = __importDefault(require("../wallet/assets/getBalance"));
|
|
10
|
+
/**
|
|
11
|
+
* The lending tokens a user holds in their wallet: RpmUSD, DEpmUSD and idle pmUSD.
|
|
12
|
+
*
|
|
13
|
+
* @param chainId - The chain/network to use.
|
|
14
|
+
* @param user - The user's address.
|
|
15
|
+
* @param provider - Optional ethers.js Provider instance.
|
|
16
|
+
* @returns Each balance, with its registry decimals and symbol.
|
|
17
|
+
*/
|
|
18
|
+
const getUserWalletBalances = async (chainId, user, provider) => {
|
|
19
|
+
if (!provider) {
|
|
20
|
+
const rpc = chains_1.default[chainId].rpcs[0];
|
|
21
|
+
provider = new ethers_1.ethers.JsonRpcProvider(rpc);
|
|
22
|
+
}
|
|
23
|
+
const [rtoken, detoken, pmusd] = await Promise.all([
|
|
24
|
+
(0, getBalance_1.default)(chainId, "rtoken", user, provider),
|
|
25
|
+
(0, getBalance_1.default)(chainId, "detoken", user, provider),
|
|
26
|
+
(0, getBalance_1.default)(chainId, "pmusd", user, provider),
|
|
27
|
+
]);
|
|
28
|
+
return { rtoken, detoken, pmusd };
|
|
29
|
+
};
|
|
30
|
+
exports.getUserWalletBalances = getUserWalletBalances;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./computeEffectiveReturn"), exports);
|
|
18
|
+
__exportStar(require("./getUserClaimables"), exports);
|
|
19
|
+
__exportStar(require("./getUserCollateral"), exports);
|
|
20
|
+
__exportStar(require("./getUserDebt"), exports);
|
|
21
|
+
__exportStar(require("./getUserFarming"), exports);
|
|
22
|
+
__exportStar(require("./getUserGovernance"), exports);
|
|
23
|
+
__exportStar(require("./getUserPortfolio"), exports);
|
|
24
|
+
__exportStar(require("./getUserWalletBalances"), exports);
|
package/dist/utils/amount.js
CHANGED
|
@@ -5,6 +5,7 @@ exports.toAmount = toAmount;
|
|
|
5
5
|
exports.toScaled = toScaled;
|
|
6
6
|
exports.parseAmount = parseAmount;
|
|
7
7
|
exports.toDecimalString = toDecimalString;
|
|
8
|
+
exports.toNumber = toNumber;
|
|
8
9
|
exports.serializeAmount = serializeAmount;
|
|
9
10
|
exports.deserializeAmount = deserializeAmount;
|
|
10
11
|
exports.getAssetDecimals = getAssetDecimals;
|
|
@@ -65,6 +66,15 @@ function parseAmount(text, decimals) {
|
|
|
65
66
|
function toDecimalString(amount) {
|
|
66
67
|
return ethers_1.ethers.formatUnits(amount.value, amount.decimals);
|
|
67
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* An amount or scaled value as a JavaScript number, for USD and rate maths. Lossy past about 15 significant digits,
|
|
71
|
+
* so keep on-chain values as `bigint` and use this only for computed figures.
|
|
72
|
+
* @param amount The amount or scaled value, e.g. a ray rate of 3.5e25 becomes 0.035.
|
|
73
|
+
* @returns The value in whole units.
|
|
74
|
+
*/
|
|
75
|
+
function toNumber(amount) {
|
|
76
|
+
return Number(toDecimalString(amount));
|
|
77
|
+
}
|
|
68
78
|
/**
|
|
69
79
|
* Converts an amount to a JSON-safe form, since `bigint` does not survive `JSON.stringify`.
|
|
70
80
|
* @param amount The amount.
|
package/dist/utils/contracts.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getContractAddress = exports.getContract = exports.getConfig = exports.setOverrideConfig = exports.getConfigs = void 0;
|
|
3
|
+
exports.isDeployed = exports.getContractAddress = exports.getContract = exports.getConfig = exports.setOverrideConfig = exports.getConfigs = void 0;
|
|
4
4
|
const configs_1 = require("../configs");
|
|
5
5
|
const chains_1 = require("../configs/chains");
|
|
6
6
|
const artifacts_1 = require("./artifacts");
|
|
@@ -56,7 +56,7 @@ const getContractAddress = (chainId, contractName) => {
|
|
|
56
56
|
if (!config) {
|
|
57
57
|
throw new Error(`Unsupported chain ID: ${chainId}`);
|
|
58
58
|
}
|
|
59
|
-
const chainKeys = ["contracts", "governance", "assets", "pools", "nfts", "lockers", "zap", "psm"];
|
|
59
|
+
const chainKeys = ["contracts", "governance", "assets", "pools", "nfts", "lockers", "zap", "psm", "curve"];
|
|
60
60
|
const keys = Object.keys(config).filter((key) => chainKeys.includes(key));
|
|
61
61
|
let address = null;
|
|
62
62
|
for (const key of keys) {
|
|
@@ -84,3 +84,6 @@ exports.default = {
|
|
|
84
84
|
getConfig: exports.getConfig,
|
|
85
85
|
getConfigs: exports.getConfigs,
|
|
86
86
|
};
|
|
87
|
+
/** Whether a config address is set and deployed: a valid, non-zero address. */
|
|
88
|
+
const isDeployed = (address) => !!address && ethers_1.ethers.isAddress(address) && address !== ethers_1.ethers.ZeroAddress;
|
|
89
|
+
exports.isDeployed = isDeployed;
|