Skip to content

Commit f98ffa8

Browse files
authored
add veda adapter (#2315)
* add veda adapter * try / catch
1 parent 9442141 commit f98ffa8

File tree

1 file changed

+246
-0
lines changed

1 file changed

+246
-0
lines changed

src/adaptors/veda/index.js

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
const sdk = require('@defillama/sdk');
2+
const axios = require('axios');
3+
const utils = require('../utils');
4+
5+
const vaults = [
6+
{
7+
name: 'Lido Golden Goose Vault',
8+
symbol: 'GGV',
9+
vault: '0xef417FCE1883c6653E7dC6AF7c6F85CCDE84Aa09',
10+
chain: 'ethereum',
11+
decimals: 18,
12+
underlyingTokens: [
13+
'0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84', // stETH
14+
'0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0', // wstETH
15+
],
16+
url: 'https://app.veda.tech/vaults/lido-golden-goose-vault',
17+
},
18+
{
19+
name: 'Lombard DeFi Vault',
20+
symbol: 'LBTCv',
21+
vault: '0x5401b8620E5FB570064CA9114fd1e135fd77D57c',
22+
chain: 'ethereum',
23+
decimals: 8,
24+
apiUrl:
25+
'https://bff.prod.lombard-fi.com/sevenseas-api/daily-data/all/0x5401b8620E5FB570064CA9114fd1e135fd77D57c/0/latest',
26+
underlyingTokens: [
27+
'0x8236a87084f8B84306f72007F36F2618A5634494', // LBTC
28+
'0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', // WBTC
29+
],
30+
url: 'https://app.veda.tech/vaults/lombard-defi-vault',
31+
},
32+
{
33+
name: 'PlasmaUSD',
34+
symbol: 'PlasmaUSD',
35+
vault: '0xd1074E0AE85610dDBA0147e29eBe0D8E5873a000',
36+
chain: 'ethereum',
37+
displayChain: 'plasma',
38+
chains: ['ethereum', 'plasma'],
39+
decimals: 6,
40+
underlyingTokens: [
41+
'0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
42+
'0xdAC17F958D2ee523a2206206994597C13D831ec7', // USDT
43+
'0x6B175474E89094C44Da98b954EedeAC495271d0F', // DAI
44+
],
45+
url: 'https://app.veda.tech/vaults/plasmausd',
46+
},
47+
{
48+
name: 'Veda USD Vault',
49+
symbol: 'vedaUSD',
50+
vault: '0x71b9601d96B7e43C434d07D4AE1Aa26650920aA7',
51+
chain: 'ethereum',
52+
decimals: 6,
53+
underlyingTokens: [
54+
'0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
55+
],
56+
url: 'https://app.veda.tech/vaults/usd-vault',
57+
},
58+
];
59+
60+
const SECONDS_PER_DAY = 86400;
61+
62+
const calculateApr = (currentRate, pastRate, days) => {
63+
if (!pastRate || pastRate <= 0 || currentRate === pastRate) return null;
64+
return ((currentRate - pastRate) / pastRate / days) * 365 * 100;
65+
};
66+
67+
const getBlock = async (timestamp, chain = 'ethereum') => {
68+
try {
69+
const response = await axios.get(
70+
`https://coins.llama.fi/block/${chain}/${timestamp}`
71+
);
72+
return response.data.height;
73+
} catch (e) {
74+
console.error(`Error fetching block for ${chain} at ${timestamp}:`, e.message);
75+
return null;
76+
}
77+
};
78+
79+
const getAccountant = async (vaultAddress, chain) => {
80+
try {
81+
const hookResult = await sdk.api.abi.call({
82+
target: vaultAddress,
83+
abi: 'function hook() view returns (address)',
84+
chain,
85+
});
86+
87+
if (hookResult.output === '0x0000000000000000000000000000000000000000') {
88+
return null;
89+
}
90+
91+
const accountantResult = await sdk.api.abi.call({
92+
target: hookResult.output,
93+
abi: 'function accountant() view returns (address)',
94+
chain,
95+
});
96+
return accountantResult.output;
97+
} catch (e) {
98+
return null;
99+
}
100+
};
101+
102+
const getRate = async (accountant, chain, block) => {
103+
try {
104+
const result = await sdk.api.abi.call({
105+
target: accountant,
106+
abi: 'function getRate() view returns (uint256)',
107+
chain,
108+
block: block || undefined,
109+
});
110+
return Number(result.output);
111+
} catch (e) {
112+
return null;
113+
}
114+
};
115+
116+
const getTotalSupply = async (vault) => {
117+
if (vault.chains) {
118+
const supplies = await Promise.all(
119+
vault.chains.map(async (chain) => {
120+
try {
121+
const result = await sdk.api.abi.call({
122+
target: vault.vault,
123+
abi: 'erc20:totalSupply',
124+
chain,
125+
});
126+
return Number(result.output) / Math.pow(10, vault.decimals);
127+
} catch (e) {
128+
return 0;
129+
}
130+
})
131+
);
132+
return supplies.reduce((sum, s) => sum + s, 0);
133+
}
134+
135+
const result = await sdk.api.abi.call({
136+
target: vault.vault,
137+
abi: 'erc20:totalSupply',
138+
chain: vault.chain,
139+
});
140+
return Number(result.output) / Math.pow(10, vault.decimals);
141+
};
142+
143+
const getTvl = async (vault, totalSupply, currentRate) => {
144+
const vaultKey = `${vault.chain}:${vault.vault}`;
145+
const underlyingKey = `${vault.chain}:${vault.underlyingTokens[0]}`;
146+
const priceRes = await axios.get(
147+
`https://coins.llama.fi/prices/current/${vaultKey},${underlyingKey}`
148+
);
149+
150+
if (priceRes.data.coins[vaultKey]?.price) {
151+
return totalSupply * priceRes.data.coins[vaultKey].price;
152+
}
153+
if (priceRes.data.coins[underlyingKey]?.price) {
154+
const rate = currentRate / Math.pow(10, vault.decimals);
155+
return totalSupply * rate * priceRes.data.coins[underlyingKey].price;
156+
}
157+
return 0;
158+
};
159+
160+
const getApyFromApi = async (apiUrl) => {
161+
try {
162+
const response = await axios.get(apiUrl);
163+
const data = response.data;
164+
if (!data || data.length === 0) return null;
165+
166+
const latest = data[0];
167+
let apyBase7d = null;
168+
if (data.length >= 7) {
169+
const recent7d = data.slice(0, 7);
170+
apyBase7d =
171+
recent7d.reduce((sum, d) => sum + (d.daily_apy || 0), 0) /
172+
recent7d.length;
173+
}
174+
175+
return {
176+
apyBase: latest.daily_apy || 0,
177+
apyBase7d,
178+
};
179+
} catch (e) {
180+
return null;
181+
}
182+
};
183+
184+
const apy = async () => {
185+
const now = Math.floor(Date.now() / 1000);
186+
const [block1d, block7d] = await Promise.all([
187+
getBlock(now - SECONDS_PER_DAY),
188+
getBlock(now - 7 * SECONDS_PER_DAY),
189+
]);
190+
191+
const pools = [];
192+
193+
for (const vault of vaults) {
194+
try {
195+
const accountant = await getAccountant(vault.vault, vault.chain);
196+
if (!accountant) continue;
197+
198+
const currentRate = await getRate(accountant, vault.chain, null);
199+
if (!currentRate) continue;
200+
201+
const totalSupply = await getTotalSupply(vault);
202+
const tvlUsd = await getTvl(vault, totalSupply, currentRate);
203+
204+
let apyBase, apyBase7d;
205+
206+
if (vault.apiUrl) {
207+
const apiApy = await getApyFromApi(vault.apiUrl);
208+
apyBase = apiApy?.apyBase || 0;
209+
apyBase7d = apiApy?.apyBase7d || 0;
210+
} else {
211+
// Calculate APY from on-chain rate changes (skip if blocks unavailable)
212+
const [rate1d, rate7d] = await Promise.all([
213+
block1d ? getRate(accountant, vault.chain, block1d) : null,
214+
block7d ? getRate(accountant, vault.chain, block7d) : null,
215+
]);
216+
const apr1d = calculateApr(currentRate, rate1d, 1);
217+
const apr7d = calculateApr(currentRate, rate7d, 7);
218+
apyBase = apr1d || apr7d || 0;
219+
apyBase7d = apr7d || 0;
220+
}
221+
222+
const poolChain = vault.displayChain || vault.chain;
223+
pools.push({
224+
pool: `${vault.vault}-${poolChain}`.toLowerCase(),
225+
chain: utils.formatChain(poolChain),
226+
project: 'veda',
227+
symbol: vault.symbol,
228+
tvlUsd,
229+
apyBase,
230+
apyBase7d,
231+
underlyingTokens: vault.underlyingTokens,
232+
url: vault.url,
233+
});
234+
} catch (e) {
235+
console.error(`Error processing ${vault.name}:`, e.message);
236+
}
237+
}
238+
239+
return pools;
240+
};
241+
242+
module.exports = {
243+
timetravel: false,
244+
apy,
245+
url: 'https://veda.tech/',
246+
};

0 commit comments

Comments
 (0)