Skip to content

Commit 515dd03

Browse files
authored
Usual: separate TVL by product (ETH0, EUR0) and improve treasury adapter` (#18097)
1 parent 7d9db8f commit 515dd03

4 files changed

Lines changed: 187 additions & 32 deletions

File tree

projects/treasury/usual.js

Lines changed: 147 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,172 @@
11
const ADDRESSES = require('../helper/coreAssets.json')
22
const { treasuryExports } = require("../helper/treasury");
33

4+
const bUSD0 = "0x35d8949372d46b7a3d5a56006ae77b215fc69bc0";
45
const tokens = [
56
'0x259338656198ec7a76c729514d3cb45dfbf768a1', // Resolv
67
'0xd001f0a15d272542687b2677ba627f48a4333b5d', // e-USD0
7-
'0xF8094570485B124b4f2aBE98909A87511489C162', // PENDLE-LPT
8-
'0x67ec31a47a4126A66C7bb2fE017308cf5832A4Db', // usUSDS++
9-
'0xb4cd5c5440eb69edb6db878ca2acd7c6b97b2ba5', // YT-USD0++-27NOV2025
10-
'0xD2245ee5C3099d65a3d0fdCecA0f71Cc4aA8f0FF', // fSL23
11-
'0x8092cA384D44260ea4feaf7457B629B8DC6f88F0', // ustUSR++
12-
'0x8245FD9Ae99A482dFe76576dd4298f799c041D61', // uUSCC++
13-
'0x28d24D4380B26A1Ef305Ad8D8DB258159E472F33', // Usual_MV
14-
'0x35D8949372D46B7a3D5A56006AE77B215fc69bC0', // USD0++
15-
'0x2e7fC02bE94BC7f0cD69DcAB572F64bcC173cd81', // USD0a
8+
'0xf8094570485b124b4f2abe98909a87511489c162', // PENDLE-LPT
9+
'0xb4cd5c5440eb69edb6db878ca2acd7c6b97b2ba5', // YT-USD0++-27NOV2025
10+
'0xd2245ee5c3099d65a3d0fdceca0f71cc4aa8f0ff', // fSL23
11+
'0x8092ca384d44260ea4feaf7457b629b8dc6f88f0', // ustUSR++
12+
'0x28d24d4380b26a1ef305ad8d8db258159e472f33', // Usual_MV
13+
bUSD0, // formerlyUSD0++
14+
'0x2e7fc02be94bc7f0cd69dcab572f64bcc173cd81', // USD0a
1615
ADDRESSES.ethereum.USD0, // USD0
1716
ADDRESSES.ethereum.USDC, // USDC
1817
ADDRESSES.ethereum.WETH, // WETH
1918
'0x58d97b57bb95320f9a05dc918aef65434969c2b2', // morpho
20-
'0x734eec7930bc84eC5732022B9EB949A81fB89AbE', // ETH0
19+
'0x734eec7930bc84ec5732022b9eb949a81fb89abe', // ETH0
2120
'0x3c89cd1884e7bef73ca3ef08d2ef6ec338fd8e49', // EUR0
22-
'0xC4441c2BE5d8fA8126822B9929CA0b81Ea0DE38E', // USUAL
23-
'0x437cc33344a0B27A429f795ff6B469C72698B291', // wM
24-
'0xC139190F447e929f090Edeb554D95AbB8b18aC1C', // USDtb
25-
'0xAF87B90E8a3035905697E07Bb813d2d59D2b0951', // uTac
26-
'0x77633305032b6bDf3eBdB373b3bADcCb2786ce2f', // EVK Vault eUSD0-6
27-
'0x5Cd97d97D311F9dE388C1C110b10BF81AAD7CAf0', // Bunni USD0 - USD0++
28-
'0x749794E985Af5a9A384B9cEe6D88DaB4CE1576A1', // MEVCapital USD0
29-
'0xa0769f7A8fC65e47dE93797b4e21C073c117Fc80', // euTBL
30-
'0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c', // EURC
21+
'0xc4441c2be5d8fa8126822b9929ca0b81ea0de38e', // USUAL
22+
'0x437cc33344a0b27a429f795ff6b469c72698b291', // wM
23+
'0xc139190f447e929f090edeb554d95abb8b18ac1c', // USDtb
24+
'0xaf87b90e8a3035905697e07bb813d2d59d2b0951', // uTac
25+
'0x77633305032b6bdf3ebdb373b3badccb2786ce2f', // EVK Vault eUSD0-6
26+
'0x5cd97d97d311f9de388c1c110b10bf81aad7caf0', // Bunni USD0 - USD0++
27+
'0x749794e985af5a9a384b9cee6d88dab4ce1576a1', // MEVCapital USD0
28+
'0xa0769f7a8fc65e47de93797b4e21c073c117fc80', // euTBL
29+
'0x1abaea1f7c830bd89acc67ec4af516284b1bc33c', // EURC
3130
ADDRESSES.ethereum.WSTETH // wstEth
3231
]
3332

3433
const owners = [
35-
'0x81ad394C0Fa87e99Ca46E1aca093BEe020f203f4', // Yield Treasury
3634
'0xF3D913De4B23ddB9CfdFAF955BAC5634CbAE95F4', // longterm Treasury
3735
'0xc32e2a2F03d41768095e67b62C9c739f2C2Bc4aA', // Treasury 1
36+
'0x81ad394C0Fa87e99Ca46E1aca093BEe020f203f4', // Yield Treasury
3837
'0xe3FD5A2cA538904A9e967CBd9e64518369e5a03f', // Treasury 2
39-
'0xcbf85D44178c01765Ab32Af72D5E291dcd39A06B', // Treasury 3
38+
'0xcbf85D44178c01765Ab32Af72D5E291dcd39A06B', // Treasury 3
39+
// '0x28E52d338Aa22Ab3e2331b65291C871B6fd6e517', // LP Holdings
4040
]
41+
const lendingMarketGetUserPositionAbi = "function getUserPosition(tuple(address, address,address, address,uint256,uint256,address), address) view returns (uint256 supplyAssets, uint256 supplyShares, uint256 borrowAssets, uint256 borrowShares, uint256 collateralAssets)";
42+
const firaLendingMarket= "0xa428723ee8ffd87088c36121d72100b43f11fb6a";
43+
const UZRMarketParams = [
44+
"0x73A15FeD60Bf67631dC6cd7Bc5B6e8da8190aCF5", // loanToken
45+
"0x35D8949372D46B7a3D5A56006AE77B215fc69bC0", // collateralToken (bUSD0)
46+
"0x30Da78355FcEA04D1fa34AF3c318BE203C6F2145", // oracle
47+
"0xdfCF197B0B65066183b04B88d50ACDC0C4b01385", // irm
48+
"880000000000000000", // lltv
49+
"999900000000000000", // param6
50+
"0xFE7C47895eDb12a990b311Df33B90Cfea1D44c24", // Fira UZR vault
51+
];
52+
53+
// Vaults that need to be priced via convertToAssets
54+
const VAULTS = [
55+
"0xBEEf050ecd6a16c4e7bfFbB52Ebba7846C4b8cD4", // Steakhouse ETH
56+
"0x67ec31a47a4126A66C7bb2fE017308cf5832A4Db", // usUSDS++
57+
"0x8245FD9Ae99A482dFe76576dd4298f799c041D61", // uUSCC++
58+
"0xFE7C47895eDb12a990b311Df33B90Cfea1D44c24", // Fira UZR
59+
"0xd001f0a15d272542687b2677ba627f48a4333b5d", // e-USD0
60+
]
61+
62+
63+
// Aave HORIZON V3 addresses (Ethereum mainnet)
64+
const AAVE_HORIZON_POOL = "0xAe05Cd22df81871bc7cC2a04BeCfb516bFe332C8"; // Aave Horizon Pool
65+
const aaveUserReserveDataAbi = "function getUserAccountData(address user) view returns (uint256 totalCollateralBase, uint256 totalDebtBase, uint256 availableBorrowsBase, uint256 currentLiquidationThreshold, uint256 ltv, uint256 healthFactor)";
66+
67+
4168

42-
module.exports = treasuryExports({
69+
const base = treasuryExports({
4370
ethereum: {
4471
tokens,
4572
owners,
4673
ownTokens: ["0x06B964d96f5dCF7Eae9d7C559B09EDCe244d4B8E", "0xC4441c2BE5d8fA8126822B9929CA0b81Ea0DE38E" ], // USUALX and USUAL
74+
blacklistedTokens: [
75+
"0x98c23e9d8f34fefb1b7bd6a91b7ff122f4e16f5c", // aEthUSDC
76+
"0x4d5f47fa6a74757f35c14fd3a6ef8e3c9bc514e8", // aEthWETH
77+
"0x23878914efe38d27c4d67ab83ed1b93a74d4086a" // aEthUSDT
78+
],
79+
resolveUniV3: true,
80+
resolveLP: true,
81+
fetchCoValentTokens: true,
4782
}
48-
})
83+
});
84+
const baseTvl = base.ethereum.tvl
85+
86+
87+
base.ethereum.tvl = async (api) => {
88+
// 1. Run existing treasury logic (standard ERC20 + LPs + UniV3)
89+
await baseTvl(api)
90+
91+
// 2. Add Fira position
92+
let calls = []
93+
owners.forEach(owner => {
94+
calls.push({ target: firaLendingMarket, params: [UZRMarketParams, owner] })
95+
})
96+
// add collateral and subtract borrowed amount
97+
const positions = await api.multiCall({ abi: lendingMarketGetUserPositionAbi, calls: calls })
98+
positions.forEach(position => {
99+
api.add(bUSD0, position.collateralAssets)
100+
api.add(ADDRESSES.ethereum.USD0,-BigInt(position.borrowAssets) )
101+
})
102+
103+
104+
// 3. Handle unpriced vaults (Morpho/ERC4626)
105+
// Step A: Get balances of vault tokens across all owners
106+
calls = []
107+
owners.forEach(owner => {
108+
VAULTS.forEach(vault => {
109+
calls.push({ target: vault, params: owner })
110+
})
111+
})
112+
const vaultBalances = await api.multiCall({ abi: 'erc20:balanceOf', calls, permitFailure: true })
113+
const vaultAssets = await api.multiCall({ abi: 'function asset() view returns (address)', calls: VAULTS.map(vault => ({ target: vault })), permitFailure: true })
114+
// it needs to be an object with the vault as the key and the asset as the value
115+
const vaultToAsset= Object.fromEntries(vaultAssets.map((asset, i) => [VAULTS[i], asset]))
116+
// add vaultAssets to calls
117+
calls.forEach((call, i) => {
118+
call.asset = vaultToAsset[call.target]
119+
})
120+
121+
// Step B: Filter for non-zero balances to query convertToAssets
122+
const activeVaultCalls = []
123+
vaultBalances.forEach((bal, i) => {
124+
if (bal > 0) {
125+
activeVaultCalls.push({
126+
target: calls[i].target,
127+
params: bal, // convertToAssets takes the share amount as input
128+
asset: calls[i].asset
129+
})
130+
}
131+
})
132+
// Step C: Convert shares to assets (ETH)
133+
if (activeVaultCalls.length > 0) {
134+
const underlyingAssets = await api.multiCall({
135+
abi: 'function convertToAssets(uint256) view returns (uint256)',
136+
calls: activeVaultCalls,
137+
permitFailure: true
138+
})
139+
underlyingAssets.forEach((amount, i) => {
140+
const asset = activeVaultCalls[i].asset
141+
if (amount != null && asset != null && amount > 0) api.add(asset, amount)
142+
})
143+
}
144+
145+
// 4. Add Aave position
146+
calls = []
147+
owners.forEach(owner => {
148+
calls.push({ target: AAVE_HORIZON_POOL, params: owner })
149+
})
150+
151+
// Get all Aave positions
152+
const aavePositions = await api.multiCall({ abi: aaveUserReserveDataAbi, calls: calls, permitFailure: true })
153+
154+
// Process positions: add supplied assets, subtract borrowed assets
155+
// NOTE: Aave returns values in USD base currency with 8 decimals
156+
// Convert from 8 decimals to actual USD value and add directly
157+
aavePositions.forEach((position) => {
158+
if (!position) return
159+
const supplied = BigInt(position.totalCollateralBase)
160+
const borrowed = BigInt(position.totalDebtBase)
161+
162+
if (supplied > 0) {
163+
// Convert from 8 decimals to USD value
164+
api.addUSDValue(Number(supplied )/ 1e8)
165+
}
166+
if (borrowed > 0) {
167+
api.addUSDValue(-Number(borrowed )/ 1e8)
168+
}
169+
})
170+
}
171+
172+
module.exports = base;

projects/usual-eth0/index.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
const ADDRESSES = require('../helper/coreAssets.json')
2+
const { sumTokensExport } = require("../helper/unwrapLPs");
3+
4+
const owners = [
5+
"0xc912B5684a1dF198294D8b931B3926a14d700F64", // treasury ETH0
6+
7+
];
8+
const tokens = [
9+
ADDRESSES.ethereum.WSTETH, // wstEth,
10+
];
11+
12+
module.exports = {
13+
methodology: "ETH0 is a synthetic Ethereum-based asset fully collateralized by Lido’s wrapped staked ETH (wstETH).",
14+
ethereum: {
15+
tvl: sumTokensExport({ owners , tokens, resolveUniV3: true }),
16+
}
17+
}

projects/usual-eur0/index.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
const { sumTokensExport } = require("../helper/unwrapLPs");
2+
3+
const owners = [
4+
"0x11D75bC93aE69350231D8fF0F5832A697678183E", // Treasury EUR0
5+
];
6+
const tokens = [
7+
"0xa0769f7A8fC65e47dE93797b4e21C073c117Fc80", // euTBL
8+
"0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c", // EURC
9+
];
10+
11+
module.exports = {
12+
methodology: "EUR0 is a euro-pegged stablecoin. It is fully backed by EU issued T-Bills.",
13+
ethereum: {
14+
tvl: sumTokensExport({ owners , tokens, resolveUniV3: true }),
15+
}
16+
}

projects/usual/index.js

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,21 @@ const tokens = [
77
"0x136471a34f6ef19fe571effc1ca711fdb8e49f2b", // USYC
88
"0x437cc33344a0B27A429f795ff6B469C72698B291", // wM
99
"0xC139190F447e929f090Edeb554D95AbB8b18aC1C", // USDtb
10-
ADDRESSES.ethereum.WSTETH, // wstEth,
11-
"0xa0769f7A8fC65e47dE93797b4e21C073c117Fc80", // euTBL
12-
"0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c", // EURC
10+
"0xe4880249745eAc5F1eD9d8F7DF844792D560e750", //ustbl
1311
];
1412

1513
const owners = [
16-
// '0xc32e2a2F03d41768095e67b62C9c739f2C2Bc4aA', // Treasury 1
14+
"0xc32e2a2F03d41768095e67b62C9c739f2C2Bc4aA", // Treasury 1
15+
"0xF3D913De4B23ddB9CfdFAF955BAC5634CbAE95F4", //DAO LT Treasury
1716
"0xdd82875f0840AAD58a455A70B88eEd9F59ceC7c7", // treasury
1817
"0x4Cbc25559DbBD1272EC5B64c7b5F48a2405e6470", // USUALM
1918
"0x58073531a2809744D1bF311D30FD76B27D662abB", // USUALUSDtb
20-
"0xc912B5684a1dF198294D8b931B3926a14d700F64", // treasury ETH0
21-
"0x11D75bC93aE69350231D8fF0F5832A697678183E", // Treasury EUR0
22-
"0x81ad394C0Fa87e99Ca46E1aca093BEe020f203f4", // Yield Treasury EUR0
2319
];
2420

21+
22+
2523
module.exports = {
26-
methodology: "TVL represents the value in RWA held by the protocol",
24+
methodology: "TVL represents the value held by the protocol",
2725
ethereum: {
2826
staking: staking(
2927
"0x06B964d96f5dCF7Eae9d7C559B09EDCe244d4B8E",
@@ -33,7 +31,7 @@ module.exports = {
3331
tvl: sumTokensExport({
3432
tokens,
3533
owners,
36-
resolveUniV3: true,
34+
resolveUniV3: true,
3735
}),
3836
},
3937
doublecounted: true,

0 commit comments

Comments
 (0)