Skip to content

Commit ada3243

Browse files
committed
Add Accountable integration for loan vaults and yield calculations
1 parent 08709a1 commit ada3243

File tree

1 file changed

+125
-0
lines changed

1 file changed

+125
-0
lines changed

src/adaptors/accountable/index.js

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
const sdk = require('@defillama/sdk');
2+
const utils = require('../utils');
3+
4+
const API_URL = 'https://yield.accountable.capital/api/loan';
5+
const chainIdToName = { 143: 'monad' };
6+
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
7+
8+
const abis = {
9+
asset: 'function asset() view returns (address)',
10+
convertToAssets: 'function convertToAssets(uint256 shares) view returns (uint256)',
11+
};
12+
13+
const basisPointsToPercent = (value) => Number(value) / 1e4;
14+
const formatAmountWithDecimlas = (value, decimals) => value / 10 ** decimals;
15+
16+
const fetchVaultsByLoanIds = async(loanIds) => {
17+
const results = await Promise.allSettled(
18+
loanIds.map((id) => utils.getData(`${API_URL}/${id}`))
19+
);
20+
21+
return results.reduce((acc, res, idx) => {
22+
if (res.status !== 'fulfilled') return acc;
23+
const vault = res.value.on_chain_loan.loan.vault;
24+
if (vault && vault !== ZERO_ADDRESS)
25+
acc[loanIds[idx]] = vault.toLowerCase();
26+
return acc;
27+
}, {});
28+
};
29+
30+
const getVaultAddressesFromApi = async() => {
31+
const { items } = await utils.getData(API_URL);
32+
const vaults = items
33+
.map((item) => item.on_chain_loan.loan.vault)
34+
.filter((addr) => addr && addr !== ZERO_ADDRESS)
35+
.map((addr) => addr.toLowerCase());
36+
return Array.from(new Set(vaults));
37+
};
38+
39+
const getVaultStats = async(vaults, chain = 'monad') => {
40+
if (!vaults.length) return {};
41+
42+
const [suppliesRes, underlyingsRes] = await Promise.all([
43+
sdk.api.abi.multiCall({
44+
chain,
45+
abi: 'erc20:totalSupply',
46+
calls: vaults.map((vault) => ({ target: vault })),
47+
permitFailure: true,
48+
}),
49+
sdk.api.abi.multiCall({
50+
chain,
51+
abi: abis.asset,
52+
calls: vaults.map((vault) => ({ target: vault })),
53+
permitFailure: true,
54+
}),
55+
]);
56+
57+
const supplies = suppliesRes.output.map((o) => o.output);
58+
const underlyings = underlyingsRes.output.map((o) => o.output);
59+
60+
const [totalAssetsRes, liquidityRes] = await Promise.all([
61+
sdk.api.abi.multiCall({
62+
chain,
63+
abi: abis.convertToAssets,
64+
calls: vaults.map((vault, i) => ({
65+
target: vault,
66+
params: [supplies[i]],
67+
})),
68+
permitFailure: true,
69+
}),
70+
sdk.api.abi.multiCall({
71+
chain,
72+
abi: 'erc20:balanceOf',
73+
calls: vaults.map((vault, i) => ({
74+
target: underlyings[i],
75+
params: vault,
76+
})),
77+
permitFailure: true,
78+
}),
79+
]);
80+
81+
const totalAssets = totalAssetsRes.output.map((o) => o.output);
82+
const liquidity = liquidityRes.output.map((o) => o.output);
83+
console.log(totalAssets, liquidity);
84+
return vaults.reduce((acc, address, i) => {
85+
acc[address] = {
86+
totalSupplied: supplies[i],
87+
totalBorrowed: totalAssets[i] - liquidity[i],
88+
tvl: liquidity[i],
89+
};
90+
return acc;
91+
}, {});
92+
};
93+
94+
const apy = async() => {
95+
const { items } = await utils.getData(API_URL);
96+
const activeLoans = items.filter((item) => item.loan_state === 3);
97+
const loanIds = activeLoans.map((item) => item.id);
98+
99+
const loanVaultMap = await fetchVaultsByLoanIds(loanIds);
100+
const vaultAddresses = Object.values(loanVaultMap);
101+
const vaultStats = await getVaultStats(vaultAddresses);
102+
103+
return activeLoans.map((item) => {
104+
const chainName = chainIdToName[item.chain_id] || 'unknown';
105+
const vaultAddress = loanVaultMap[item.id];
106+
const stats = vaultAddress ? vaultStats[vaultAddress] || {} : {};
107+
108+
return {
109+
pool: `${item.loan_address}-${chainName}`.toLowerCase(),
110+
chain: chainName,
111+
project: 'accountable',
112+
symbol: utils.formatSymbol(item.asset_symbol),
113+
tvlUsd: formatAmountWithDecimlas(stats.tvl, 6),
114+
apyBase: basisPointsToPercent(item.net_apy),
115+
url: `https://yield.accountable.capital/vaults/${item.loan_address}`,
116+
totalSupplyUsd: formatAmountWithDecimlas(stats.totalSupplied, 6),
117+
totalBorrowUsd: formatAmountWithDecimlas(stats.totalBorrowed, 6),
118+
};
119+
});
120+
};
121+
122+
module.exports = {
123+
timetravel: false,
124+
apy,
125+
};

0 commit comments

Comments
 (0)