Skip to content

Commit 11f5112

Browse files
authored
Merge pull request #1900 from RickGriff/liquity_v2_yield_adaptor
Liquity v2 yield adaptor
2 parents 3d5432b + 320bef4 commit 11f5112

File tree

1 file changed

+324
-0
lines changed

1 file changed

+324
-0
lines changed

src/adaptors/liquity-v2/index.js

Lines changed: 324 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,324 @@
1+
const sdk = require('@defillama/sdk');
2+
const superagent = require('superagent');
3+
4+
const BOLD_TOKEN = '0x6440f144b7e50d6a8439336510312d2f54beb01d';
5+
6+
const WETH_BRANCH = {
7+
activePool: '0xeb5a8c825582965f1d84606e078620a84ab16afe',
8+
defaultPool: '0xd4558240d50c2e219a21c9d25afd513bb6e5b1a0',
9+
stabilityPool: '0x5721cbbd64fc7ae3ef44a0a3f9a790a9264cf9bf',
10+
borrowerOperations: '0x0b995602b5a797823f92027e8b40c0f2d97aff1c',
11+
collToken: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'.toLowerCase(),
12+
symbol: 'ETH'
13+
}
14+
15+
const WSTETH_BRANCH = {
16+
activePool: '0x531a8f99c70d6a56a7cee02d6b4281650d7919a0',
17+
defaultPool: '0xd796e1648526400386cc4d12fa05e5f11e6a22a1',
18+
stabilityPool: '0x9502b7c397e9aa22fe9db7ef7daf21cd2aebe56b',
19+
borrowerOperations: '0x94c1610a7373919bd9cfb09ded19894601f4a1be',
20+
collToken: '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0'.toLowerCase(),
21+
symbol: 'WSTETH'
22+
}
23+
24+
const RETH_BRANCH = {
25+
activePool: '0x9074d72cc82dad1e13e454755aa8f144c479532f',
26+
defaultPool: '0x5cc5cefd034fdc4728d487a72ca58a410cddcd6b',
27+
stabilityPool: '0xd442e41019b7f5c4dd78f50dc03726c446148695',
28+
borrowerOperations: '0xa351d5b9cda9eb518727c3ceff02208915fda60d',
29+
collToken: '0xae78736cd615f374d3085123a210448e74fc6393'.toLowerCase(),
30+
symbol: 'RETH'
31+
}
32+
33+
const branches = [WETH_BRANCH, WSTETH_BRANCH, RETH_BRANCH];
34+
35+
const SP_YIELD_SPLIT = 0.75;
36+
37+
const ABIS = {
38+
getTotalBoldDeposits: {
39+
inputs: [],
40+
name: 'getTotalBoldDeposits',
41+
outputs: [
42+
{
43+
internalType: 'uint256',
44+
name: 'totalBoldDeposits',
45+
type: 'uint256',
46+
},
47+
],
48+
stateMutability: 'view',
49+
type: 'function',
50+
},
51+
getCollBalance: {
52+
inputs: [],
53+
name: 'getCollBalance',
54+
outputs: [
55+
{
56+
internalType: 'uint256',
57+
name: 'collBalance',
58+
type: 'uint256',
59+
},
60+
],
61+
stateMutability: 'view',
62+
type: 'function',
63+
},
64+
getBoldDebt: {
65+
inputs: [],
66+
name: 'getBoldDebt',
67+
outputs: [
68+
{
69+
internalType: 'uint256',
70+
name: 'boldDebt',
71+
type: 'uint256',
72+
},
73+
],
74+
stateMutability: 'view',
75+
type: 'function',
76+
},
77+
getMCR: {
78+
inputs: [],
79+
name: 'MCR',
80+
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
81+
stateMutability: 'view',
82+
type: 'function',
83+
},
84+
getNewApproxAvgInterestRateFromTroveChange: {
85+
inputs: [
86+
{
87+
components: [
88+
{
89+
internalType: 'uint256',
90+
name: 'appliedRedistBoldDebtGain',
91+
type: 'uint256',
92+
},
93+
{
94+
internalType: 'uint256',
95+
name: 'appliedRedistCollGain',
96+
type: 'uint256',
97+
},
98+
{
99+
internalType: 'uint256',
100+
name: 'collIncrease',
101+
type: 'uint256',
102+
},
103+
{
104+
internalType: 'uint256',
105+
name: 'collDecrease',
106+
type: 'uint256',
107+
},
108+
{
109+
internalType: 'uint256',
110+
name: 'debtIncrease',
111+
type: 'uint256',
112+
},
113+
{
114+
internalType: 'uint256',
115+
name: 'debtDecrease',
116+
type: 'uint256',
117+
},
118+
{
119+
internalType: 'uint256',
120+
name: 'newWeightedRecordedDebt',
121+
type: 'uint256',
122+
},
123+
{
124+
internalType: 'uint256',
125+
name: 'oldWeightedRecordedDebt',
126+
type: 'uint256',
127+
},
128+
{
129+
internalType: 'uint256',
130+
name: 'upfrontFee',
131+
type: 'uint256',
132+
},
133+
{
134+
internalType: 'uint256',
135+
name: 'batchAccruedManagementFee',
136+
type: 'uint256',
137+
},
138+
{
139+
internalType: 'uint256',
140+
name: 'newWeightedRecordedBatchManagementFee',
141+
type: 'uint256',
142+
},
143+
{
144+
internalType: 'uint256',
145+
name: 'oldWeightedRecordedBatchManagementFee',
146+
type: 'uint256',
147+
},
148+
],
149+
internalType: 'struct TroveChange',
150+
name: '_troveChange',
151+
type: 'tuple',
152+
},
153+
],
154+
name: 'getNewApproxAvgInterestRateFromTroveChange',
155+
outputs: [
156+
{
157+
internalType: 'uint256',
158+
name: '',
159+
type: 'uint256',
160+
},
161+
],
162+
stateMutability: 'view',
163+
type: 'function',
164+
}
165+
};
166+
167+
const getSPSupplyAndApy = async (spAddr, avgBranchInterestRate, branchBoldSupply) => {
168+
const spSupply = (await sdk.api.abi.call({
169+
target: spAddr,
170+
abi: ABIS.getTotalBoldDeposits,
171+
chain: 'ethereum',
172+
})).output / 1e18;
173+
174+
// Yield is the branch interest rate amplifyed by ratio of branch supply to the BOLD in the SP
175+
const spApy = avgBranchInterestRate * SP_YIELD_SPLIT * branchBoldSupply / spSupply;
176+
177+
return [spSupply, spApy];
178+
}
179+
180+
const getPrices = async (addresses) => {
181+
const req = addresses.map((address) => `ethereum:${address}`).join(',').toLowerCase();
182+
const prices = (await superagent.get(`https://coins.llama.fi/prices/current/${req}`)).body.coins;
183+
184+
const pricesObj = Object.fromEntries(
185+
Object.entries(prices).map(([address, priceData]) => [address.split(':')[1].toLowerCase(), priceData.price])
186+
);
187+
188+
return pricesObj;
189+
}
190+
191+
const getBranchColl = async (collPools) => {
192+
const results = await sdk.api.abi.multiCall({
193+
calls: collPools.map((poolAddr) => ({
194+
target: poolAddr,
195+
params: [],
196+
})),
197+
abi: ABIS.getCollBalance,
198+
chain: 'ethereum',
199+
});
200+
201+
const totalColl = results.output.map(x => Number(x.output)).reduce((a, b) => a + b);
202+
return totalColl / 1e18;
203+
}
204+
205+
const getBranchDebt = async (debtPools) => {
206+
const results = await sdk.api.abi.multiCall({
207+
calls: debtPools.map((poolAddr) => ({
208+
target: poolAddr,
209+
params: [],
210+
})),
211+
abi: ABIS.getBoldDebt,
212+
chain: 'ethereum',
213+
});
214+
215+
const totalDebt = results.output.map(x => Number(x.output)).reduce((a, b) => a + b);
216+
return totalDebt / 1e18;
217+
}
218+
219+
const getLTV = async (borrowerOpsAddr) =>{
220+
const res = (await sdk.api.abi.call({
221+
target: borrowerOpsAddr,
222+
abi: ABIS.getMCR,
223+
chain: 'ethereum',
224+
})
225+
);
226+
227+
return 1 / (res.output / 1e18);
228+
}
229+
230+
const getNewApproxAvgInterestRateFromTroveChange = async(activePoolAddr) => {
231+
const res = await sdk.api.abi.call({
232+
target: activePoolAddr,
233+
abi: ABIS.getNewApproxAvgInterestRateFromTroveChange,
234+
params: [
235+
[0, // appliedRedistBoldDebtGain
236+
0, // appliedRedistCollGain
237+
0, // collIncrease
238+
0, // collDecrease
239+
0, // debtIncrease
240+
0, // debtDecrease
241+
0, // newWeightedRecordedDebt
242+
0, // oldWeightedRecordedDebt
243+
0, // upfrontFee
244+
0, // batchAccruedManagementFee
245+
0, // newWeightedRecordedBatchManagementFee
246+
0] // oldWeightedRecordedBatchManagementFee
247+
],
248+
chain: 'ethereum',
249+
});
250+
251+
// convert from 18 decimals and make percentage
252+
return res.output / 1e16;
253+
}
254+
255+
const main = async () => {
256+
const prices = await getPrices(
257+
[WETH_BRANCH.collToken,
258+
WSTETH_BRANCH.collToken,
259+
RETH_BRANCH.collToken,
260+
BOLD_TOKEN
261+
]
262+
);
263+
264+
WETH_BRANCH.price = prices[WETH_BRANCH.collToken];
265+
WSTETH_BRANCH.price = prices[WSTETH_BRANCH.collToken];
266+
RETH_BRANCH.price = prices[RETH_BRANCH.collToken];
267+
268+
const pools = [];
269+
270+
for (const branch of branches) {
271+
const collPools = [branch.activePool, branch.defaultPool];
272+
273+
const totalColl = await getBranchColl(collPools);
274+
const totalCollUsd = totalColl * branch.price
275+
276+
const ltv = await getLTV(branch.borrowerOperations);
277+
const borrowApy = await getNewApproxAvgInterestRateFromTroveChange(branch.activePool);
278+
279+
const totalDebt = await getBranchDebt(collPools);
280+
const totalDebtUsd = totalDebt * prices[BOLD_TOKEN];
281+
282+
const [spSupply, spApy] = await getSPSupplyAndApy(branch.stabilityPool, borrowApy, totalDebt);
283+
const spSupplyUsd = spSupply * prices[BOLD_TOKEN];
284+
285+
const spPool =
286+
{
287+
pool: branch.stabilityPool,
288+
project: 'liquity-v2',
289+
symbol: 'BOLD',
290+
chain: 'ethereum',
291+
apy: spApy,
292+
tvlUsd: spSupplyUsd,
293+
underlyingTokens: [BOLD_TOKEN],
294+
rewardTokens: [BOLD_TOKEN, branch.collToken],
295+
poolMeta: `BOLD deposited in the ${branch.symbol} Stability Pool earns continuous BOLD yield and periodic ${branch.symbol} rewards from Trove liquidations`
296+
}
297+
298+
const borrowPool =
299+
{
300+
pool: branch.activePool,
301+
project: 'liquity-v2',
302+
symbol: branch.symbol,
303+
chain: 'ethereum',
304+
apy: 0,
305+
tvlUsd: totalCollUsd,
306+
apyBaseBorrow: borrowApy,
307+
totalSupplyUsd: totalCollUsd,
308+
totalBorrowUsd: totalDebtUsd,
309+
ltv: ltv,
310+
mintedCoin: 'BOLD',
311+
underlyingTokens: [branch.collToken],
312+
}
313+
314+
pools.push(spPool, borrowPool);
315+
};
316+
317+
return pools;
318+
}
319+
320+
module.exports = {
321+
timetravel: false,
322+
apy: main,
323+
url: 'https://www.liquity.org/',
324+
};

0 commit comments

Comments
 (0)