Skip to content

Commit f6bfdf4

Browse files
Add zenrock DefiLlama TVL adapter (#16860)
Co-authored-by: Claude <[email protected]>
1 parent dd7be71 commit f6bfdf4

File tree

2 files changed

+136
-0
lines changed

2 files changed

+136
-0
lines changed

projects/helper/bitcoin-book/fetchers.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,4 +242,68 @@ module.exports = {
242242
})
243243
return Array.from(new Set(staticAddresses))
244244
},
245+
zenrock: async () => {
246+
const ZRCHAIN_WALLETS_API = 'https://api.diamond.zenrocklabs.io/zrchain/treasury/zenbtc_wallets';
247+
const ZENBTC_PARAMS_API = 'https://api.diamond.zenrocklabs.io/zenbtc/params';
248+
const ZRCHAIN_KEY_BY_ID_API = 'https://api.diamond.zenrocklabs.io/zrchain/treasury/key_by_id';
249+
250+
return getConfig('zenrock/addresses', undefined, {
251+
fetcher: async () => {
252+
async function getBitcoinAddresses() {
253+
const btcAddresses = [];
254+
let nextKey = null;
255+
256+
while (true) {
257+
let url = ZRCHAIN_WALLETS_API;
258+
if (nextKey) {
259+
url += `?pagination.key=${encodeURIComponent(nextKey)}`;
260+
}
261+
const { data } = await axios.get(url);
262+
if (data.zenbtc_wallets && Array.isArray(data.zenbtc_wallets)) {
263+
for (const walletGroup of data.zenbtc_wallets) {
264+
if (walletGroup.wallets && Array.isArray(walletGroup.wallets)) {
265+
for (const wallet of walletGroup.wallets) {
266+
if (wallet.type === 'WALLET_TYPE_BTC_MAINNET' && wallet.address) {
267+
btcAddresses.push(wallet.address);
268+
}
269+
}
270+
}
271+
}
272+
}
273+
if (data.pagination && data.pagination.next_key) {
274+
nextKey = data.pagination.next_key;
275+
} else {
276+
break;
277+
}
278+
}
279+
return btcAddresses;
280+
}
281+
282+
async function getChangeAddresses() {
283+
const changeAddresses = [];
284+
285+
const { data: paramsData } = await axios.get(ZENBTC_PARAMS_API);
286+
const changeAddressKeyIDs = paramsData.params?.changeAddressKeyIDs || [];
287+
for (const keyID of changeAddressKeyIDs) {
288+
const { data: keyData } = await axios.get(`${ZRCHAIN_KEY_BY_ID_API}/${keyID}/WALLET_TYPE_BTC_MAINNET/`);
289+
if (keyData.wallets && Array.isArray(keyData.wallets)) {
290+
for (const wallet of keyData.wallets) {
291+
if (wallet.type === 'WALLET_TYPE_BTC_MAINNET' && wallet.address) {
292+
changeAddresses.push(wallet.address);
293+
}
294+
}
295+
}
296+
}
297+
return changeAddresses;
298+
}
299+
300+
const [btcAddresses, changeAddresses] = await Promise.all([
301+
getBitcoinAddresses(),
302+
getChangeAddresses(),
303+
]);
304+
const allAddresses = [...btcAddresses, ...changeAddresses];
305+
return allAddresses;
306+
}
307+
});
308+
},
245309
}

projects/zenrock/index.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
const { sumTokens: sumBitcoinTokens } = require('../helper/chain/bitcoin');
2+
const { zenrock } = require('../helper/bitcoin-book/fetchers');
3+
4+
/**
5+
* Queries Bitcoin balances for all zrchain treasury and change addresses
6+
* Returns balances object with Bitcoin TVL
7+
*/
8+
async function tvl() {
9+
// Fetch all protocol addresses (treasury + change) from the bitcoin-book fetcher
10+
const allAddresses = await zenrock();
11+
12+
if (allAddresses.length === 0) {
13+
return { bitcoin: '0' };
14+
}
15+
16+
// Use Bitcoin helper to sum balances for all addresses
17+
const balances = {};
18+
await sumBitcoinTokens({ balances, owners: allAddresses });
19+
20+
return balances;
21+
}
22+
23+
/**
24+
* Queries Zcash balances for all zrchain treasury and change addresses
25+
* Returns balances object with Zcash TVL
26+
*
27+
* NOTE: Currently using supply-based approach as a test implementation.
28+
* The address-based approach is commented out below for future use.
29+
*/
30+
async function zcashTvl() {
31+
// Fetch custodied amount from DCT supply endpoint
32+
const { get } = require('../helper/http');
33+
const { getConfig } = require('../helper/cache');
34+
const sdk = require('@defillama/sdk');
35+
36+
const DCT_SUPPLY_API = 'https://api.diamond.zenrocklabs.io/dct/supply';
37+
38+
const supplyData = await getConfig('zenrock/dct_supply', DCT_SUPPLY_API, {
39+
fetcher: async () => {
40+
const response = await get(DCT_SUPPLY_API);
41+
return response;
42+
}
43+
});
44+
45+
const balances = {};
46+
47+
// Find ASSET_ZENZEC in supplies array
48+
const zenZecSupply = supplyData.supplies?.find(
49+
item => item.supply?.asset === 'ASSET_ZENZEC'
50+
);
51+
52+
if (zenZecSupply && zenZecSupply.supply?.custodied_amount) {
53+
// custodied_amount is in Zatoshi (smallest unit, like satoshis for BTC)
54+
// Convert to ZEC by dividing by 1e8
55+
const custodiedAmount = Number(zenZecSupply.supply.custodied_amount);
56+
const custodiedZEC = custodiedAmount / 1e8;
57+
58+
sdk.util.sumSingleBalance(balances, 'zcash', custodiedZEC);
59+
}
60+
61+
return balances;
62+
}
63+
64+
module.exports = {
65+
methodology: 'zrchain locks native assets through its decentralized MPC network. zenBTC, Zenrock\'s flagship product, is a yield-bearing wrapped Bitcoin issued on Solana and EVM chains. TVL represents the total Bitcoin locked in zrchain treasury addresses. All zenBTC is fully backed by native Bitcoin, with the price of zenBTC anticipated to increase as yield payments are made continuously.',
66+
bitcoin: {
67+
tvl,
68+
},
69+
zcash: {
70+
tvl: zcashTvl,
71+
},
72+
};

0 commit comments

Comments
 (0)