Skip to content

Commit 000d7de

Browse files
authored
Merge pull request #2575 from balancer/v3-canary
publish to prod
2 parents e9299a3 + 69c9e0d commit 000d7de

10 files changed

Lines changed: 212 additions & 28 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# backend
22

3+
## 2.1.2
4+
5+
### Patch Changes
6+
7+
- 88cb046: add hypurrfi prime vault apr
8+
39
## 2.1.1
410

511
### Patch Changes

apps/api/sentry.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ export const initApiSentry = () => {
2121

2222
// Add Tracing by setting tracesSampleRate
2323
// Send 1% of transactions to Sentry
24-
tracesSampleRate: Number(env.SENTRY_TRACES_SAMPLE_RATE || 0.01),
24+
tracesSampleRate: Number(env.SENTRY_TRACES_SAMPLE_RATE || 0.001),
2525

2626
// Set sampling rate for profiling
2727
// This is relative to tracesSampleRate
28-
profilesSampleRate: Number(env.SENTRY_PROFILES_SAMPLE_RATE || 0.01),
28+
profilesSampleRate: Number(env.SENTRY_PROFILES_SAMPLE_RATE || 0.001),
2929

3030
beforeSend(event, hint) {
3131
const error = hint.originalException;

config/hyperevm.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,12 +184,15 @@ export default <NetworkData>{
184184
'0x2c910f67dbf81099e6f8e126e7265d7595dc20ad', // hyUSD₮0-hwHLP
185185
],
186186
},
187+
hypurrfiPrime: {
188+
vaults: [
189+
'0xc200aab602cd7046389b5c8fb088884323f8dd0f', // EVK Vault eUSDC-3
190+
],
191+
},
187192
morphoVaultHyperevm: {
188193
vaults: [
189194
'0xfc5126377f0efc0041c0969ef9ba903ce67d151e', // feUSDT0
190195
'0x9c59a9389d8f72de2cdaf1126f36ea4790e2275e', // feUSDhl
191-
'0x5eec795d919fa97688fb9844eeb0072e6b846f9d', // gtUSDe
192-
'0xd3a9cb7312b9c29113290758f5adfe12304cd16a', // mcUSR
193196
'0x3bcc0a5a66bb5bdceef5dd8a659a4ec75f3834d8', // mcUSDT
194197
'0xd19e3d00f8547f7d108abfd4bbb015486437b487', // mcHYPE
195198
'0x53a333e51e96fe288bc9add7cdc4b1ead2cd2ffa', // gtUSDT0

modules/token-yields/handlers/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const sourceToHandler = {
99
teth: sources.treehouseYieldHandler,
1010
sts: sources.stsYieldHandler,
1111
hypurrfi: sources.hypurrFiYieldhandler,
12+
hypurrfiPrime: sources.hypurrFiPrimeYieldHandler,
1213
morphoVaultHyperevm: sources.morphoHyperevmYieldHandler,
1314
http: sources.httpTokenYieldHandler,
1415
contract: sources.contractTokenYieldHandler,
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// ABI for the Euler EVK Vault Lens at 0x0eaDDE9EfCf1540dcA8f94e813E12db55f8405a8
2+
// Used to fetch lend vault info including supply/borrow APY from the IRM.
3+
// The full struct definition is required for correct ABI decoding.
4+
5+
const assetPriceInfoComponents = [
6+
{ name: 'queryFailure', type: 'bool' },
7+
{ name: 'queryFailureReason', type: 'bytes' },
8+
{ name: 'timestamp', type: 'uint256' },
9+
{ name: 'oracle', type: 'address' },
10+
{ name: 'asset', type: 'address' },
11+
{ name: 'unitOfAccount', type: 'address' },
12+
{ name: 'amountIn', type: 'uint256' },
13+
{ name: 'amountOutMid', type: 'uint256' },
14+
{ name: 'amountOutBid', type: 'uint256' },
15+
{ name: 'amountOutAsk', type: 'uint256' },
16+
] as const;
17+
18+
const oracleDetailedInfoComponents = [
19+
{ name: 'oracle', type: 'address' },
20+
{ name: 'name', type: 'string' },
21+
{ name: 'oracleInfo', type: 'bytes' },
22+
] as const;
23+
24+
export const hypurrfiVaultLensAbi = [
25+
{
26+
type: 'function',
27+
name: 'getVaultInfoFull',
28+
inputs: [{ name: 'vault', type: 'address' }],
29+
outputs: [
30+
{
31+
name: '',
32+
type: 'tuple',
33+
components: [
34+
{ name: 'timestamp', type: 'uint256' },
35+
{ name: 'vault', type: 'address' },
36+
{ name: 'vaultName', type: 'string' },
37+
{ name: 'vaultSymbol', type: 'string' },
38+
{ name: 'vaultDecimals', type: 'uint256' },
39+
{ name: 'asset', type: 'address' },
40+
{ name: 'assetName', type: 'string' },
41+
{ name: 'assetSymbol', type: 'string' },
42+
{ name: 'assetDecimals', type: 'uint256' },
43+
{ name: 'unitOfAccount', type: 'address' },
44+
{ name: 'unitOfAccountName', type: 'string' },
45+
{ name: 'unitOfAccountSymbol', type: 'string' },
46+
{ name: 'unitOfAccountDecimals', type: 'uint256' },
47+
{ name: 'totalShares', type: 'uint256' },
48+
{ name: 'totalCash', type: 'uint256' },
49+
{ name: 'totalBorrowed', type: 'uint256' },
50+
{ name: 'totalAssets', type: 'uint256' },
51+
{ name: 'accumulatedFeesShares', type: 'uint256' },
52+
{ name: 'accumulatedFeesAssets', type: 'uint256' },
53+
{ name: 'governorFeeReceiver', type: 'address' },
54+
{ name: 'protocolFeeReceiver', type: 'address' },
55+
{ name: 'protocolFeeShare', type: 'uint256' },
56+
{ name: 'interestFee', type: 'uint256' },
57+
{ name: 'hookedOperations', type: 'uint256' },
58+
{ name: 'configFlags', type: 'uint256' },
59+
{ name: 'supplyCap', type: 'uint256' },
60+
{ name: 'borrowCap', type: 'uint256' },
61+
{ name: 'maxLiquidationDiscount', type: 'uint256' },
62+
{ name: 'liquidationCoolOffTime', type: 'uint256' },
63+
{ name: 'dToken', type: 'address' },
64+
{ name: 'oracle', type: 'address' },
65+
{ name: 'interestRateModel', type: 'address' },
66+
{ name: 'hookTarget', type: 'address' },
67+
{ name: 'evc', type: 'address' },
68+
{ name: 'protocolConfig', type: 'address' },
69+
{ name: 'balanceTracker', type: 'address' },
70+
{ name: 'permit2', type: 'address' },
71+
{ name: 'creator', type: 'address' },
72+
{ name: 'governorAdmin', type: 'address' },
73+
{
74+
name: 'irmInfo',
75+
type: 'tuple',
76+
components: [
77+
{ name: 'queryFailure', type: 'bool' },
78+
{ name: 'queryFailureReason', type: 'bytes' },
79+
{ name: 'vault', type: 'address' },
80+
{ name: 'interestRateModel', type: 'address' },
81+
{
82+
name: 'interestRateInfo',
83+
type: 'tuple[]',
84+
components: [
85+
{ name: 'cash', type: 'uint256' },
86+
{ name: 'borrows', type: 'uint256' },
87+
{ name: 'borrowSPY', type: 'uint256' },
88+
{ name: 'borrowAPY', type: 'uint256' },
89+
{ name: 'supplyAPY', type: 'uint256' },
90+
],
91+
},
92+
{
93+
name: 'interestRateModelInfo',
94+
type: 'tuple',
95+
components: [
96+
{ name: 'interestRateModel', type: 'address' },
97+
{ name: 'interestRateModelType', type: 'uint8' },
98+
{ name: 'interestRateModelParams', type: 'bytes' },
99+
],
100+
},
101+
],
102+
},
103+
{
104+
name: 'collateralLTVInfo',
105+
type: 'tuple[]',
106+
components: [
107+
{ name: 'collateral', type: 'address' },
108+
{ name: 'borrowLTV', type: 'uint256' },
109+
{ name: 'liquidationLTV', type: 'uint256' },
110+
{ name: 'initialLiquidationLTV', type: 'uint256' },
111+
{ name: 'targetTimestamp', type: 'uint256' },
112+
{ name: 'rampDuration', type: 'uint256' },
113+
],
114+
},
115+
{ name: 'liabilityPriceInfo', type: 'tuple', components: assetPriceInfoComponents },
116+
{ name: 'collateralPriceInfo', type: 'tuple[]', components: assetPriceInfoComponents },
117+
{ name: 'oracleInfo', type: 'tuple', components: oracleDetailedInfoComponents },
118+
{ name: 'backupAssetPriceInfo', type: 'tuple', components: assetPriceInfoComponents },
119+
{ name: 'backupAssetOracleInfo', type: 'tuple', components: oracleDetailedInfoComponents },
120+
],
121+
},
122+
],
123+
stateMutability: 'view',
124+
},
125+
] as const;
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { formatUnits } from 'viem';
2+
import { TokenApr, TokenYieldConfig, TokenYieldHandler } from '../../types';
3+
import { getViemClient } from '../../../sources/viem-client';
4+
import { hypurrfiVaultLensAbi } from './abis/hypurrfi-vault-lens';
5+
6+
const VAULT_LENS = '0x0eaDDE9EfCf1540dcA8f94e813E12db55f8405a8';
7+
8+
export const hypurrFiPrimeYieldHandler: TokenYieldHandler = async (config: TokenYieldConfig['hypurrfiPrime']) => {
9+
try {
10+
const client = getViemClient('HYPEREVM');
11+
12+
const results = await client.multicall({
13+
contracts: config!.vaults.map((vault) => ({
14+
address: VAULT_LENS as `0x${string}`,
15+
abi: hypurrfiVaultLensAbi,
16+
functionName: 'getVaultInfoFull' as const,
17+
args: [vault as `0x${string}`],
18+
})),
19+
allowFailure: true,
20+
});
21+
22+
const aprs: TokenApr[] = [];
23+
24+
for (let i = 0; i < config!.vaults.length; i++) {
25+
const result = results[i];
26+
if (result.status !== 'success') continue;
27+
const { irmInfo } = result.result;
28+
if (irmInfo.queryFailure) continue;
29+
const rateInfo = irmInfo.interestRateInfo[0];
30+
if (!rateInfo) continue;
31+
// supplyAPY is 27-decimal ray format; formatUnits(value, 27) gives the float (e.g. 0.015 = 1.5%)
32+
aprs.push({ address: config!.vaults[i].toLowerCase(), apr: Number(formatUnits(rateInfo.supplyAPY, 27)) });
33+
}
34+
35+
return aprs;
36+
} catch (error) {
37+
throw Error(`HypurrFi prime vault APR handler failed: ${(error as Error).message}`);
38+
}
39+
};

modules/token-yields/handlers/sources/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export * from './euler-yield-handler';
66
export * from './teth-yield-handler';
77
export * from './sts-yield-handler';
88
export * from './hypurrfi-yield-handler';
9+
export * from './hypurrfi-prime-yield-handler';
910
export * from './morpho-hyperevm-yield-handler';
1011
export * from './rate-provider-handler';
1112
export * from './loops-yield-handler';

modules/token-yields/handlers/sources/morpho-hyperevm-yield-handler.ts

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -59,21 +59,25 @@ export const morphoHyperevmYieldHandler: TokenYieldHandler = async (
5959
const morphoBlueCaller = new Multicaller3Viem('HYPEREVM', MorphoBlueAbi);
6060
// for each vault, get the supply data for each market in the supply queue
6161
config!.vaults.forEach((vaultAddress) => {
62-
for (const marketId of vaultResult[vaultAddress].supplyQueue) {
63-
if (marketId === undefined) {
64-
throw new Error(`Market data for vault ${vaultAddress} is undefined`);
62+
if (vaultResult[vaultAddress].supplyQueue) {
63+
for (const marketId of vaultResult[vaultAddress].supplyQueue) {
64+
if (marketId === undefined) {
65+
throw new Error(`Market data for vault ${vaultAddress} is undefined`);
66+
}
67+
morphoBlueCaller.call(`${vaultAddress}.position.${marketId}`, MORPHO_BLUE_ADDRESS, 'position', [
68+
marketId,
69+
vaultAddress as `0x${string}`,
70+
]);
71+
morphoBlueCaller.call(`${vaultAddress}.market.${marketId}`, MORPHO_BLUE_ADDRESS, 'market', [
72+
marketId,
73+
]);
74+
morphoBlueCaller.call(
75+
`${vaultAddress}.idToMarketParams.${marketId}`,
76+
MORPHO_BLUE_ADDRESS,
77+
'idToMarketParams',
78+
[marketId],
79+
);
6580
}
66-
morphoBlueCaller.call(`${vaultAddress}.position.${marketId}`, MORPHO_BLUE_ADDRESS, 'position', [
67-
marketId,
68-
vaultAddress as `0x${string}`,
69-
]);
70-
morphoBlueCaller.call(`${vaultAddress}.market.${marketId}`, MORPHO_BLUE_ADDRESS, 'market', [marketId]);
71-
morphoBlueCaller.call(
72-
`${vaultAddress}.idToMarketParams.${marketId}`,
73-
MORPHO_BLUE_ADDRESS,
74-
'idToMarketParams',
75-
[marketId],
76-
);
7781
}
7882
});
7983

@@ -100,15 +104,17 @@ export const morphoHyperevmYieldHandler: TokenYieldHandler = async (
100104
const morphoIrmCaller = new Multicaller3Viem('HYPEREVM', MorphoIrmAbi);
101105
//for each vault, get the borrow rate for each market in the supply queue
102106
config!.vaults.forEach((vaultAddress) => {
103-
for (const marketId of vaultResult[vaultAddress].supplyQueue) {
104-
const market = positionResult[vaultAddress].market[marketId];
105-
const [loanToken, collateralToken, oracle, irm, lltv] =
106-
positionResult[vaultAddress].idToMarketParams[marketId];
107-
if (irm !== ZERO_ADDRESS) {
108-
morphoIrmCaller.call(`${vaultAddress}.${marketId}`, irm, 'borrowRateView', [
109-
[loanToken, collateralToken, oracle, irm, lltv],
110-
market,
111-
]);
107+
if (vaultResult[vaultAddress].supplyQueue) {
108+
for (const marketId of vaultResult[vaultAddress].supplyQueue) {
109+
const market = positionResult[vaultAddress].market[marketId];
110+
const [loanToken, collateralToken, oracle, irm, lltv] =
111+
positionResult[vaultAddress].idToMarketParams[marketId];
112+
if (irm !== ZERO_ADDRESS) {
113+
morphoIrmCaller.call(`${vaultAddress}.${marketId}`, irm, 'borrowRateView', [
114+
[loanToken, collateralToken, oracle, irm, lltv],
115+
market,
116+
]);
117+
}
112118
}
113119
}
114120
});

modules/token-yields/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ export interface TokenYieldConfig {
8989
hypurrfi?: {
9090
markets: string[];
9191
};
92+
hypurrfiPrime?: {
93+
vaults: string[];
94+
};
9295
morphoVaultHyperevm?: {
9396
vaults: string[];
9497
};

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "backend",
3-
"version": "2.1.1",
3+
"version": "2.1.2",
44
"description": "Backend service for Beethoven X and Balancer",
55
"repository": "https://github.com/balancer/backend",
66
"author": "Beethoven X",

0 commit comments

Comments
 (0)