Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions projects/helper/bitcoin-book/fetchers.js
Original file line number Diff line number Diff line change
Expand Up @@ -242,4 +242,68 @@ module.exports = {
})
return Array.from(new Set(staticAddresses))
},
zenrock: async () => {
const ZRCHAIN_WALLETS_API = 'https://api.diamond.zenrocklabs.io/zrchain/treasury/zenbtc_wallets';
const ZENBTC_PARAMS_API = 'https://api.diamond.zenrocklabs.io/zenbtc/params';
const ZRCHAIN_KEY_BY_ID_API = 'https://api.diamond.zenrocklabs.io/zrchain/treasury/key_by_id';

return getConfig('zenrock/addresses', undefined, {
fetcher: async () => {
async function getBitcoinAddresses() {
const btcAddresses = [];
let nextKey = null;

while (true) {
let url = ZRCHAIN_WALLETS_API;
if (nextKey) {
url += `?pagination.key=${encodeURIComponent(nextKey)}`;
}
const { data } = await axios.get(url);
if (data.zenbtc_wallets && Array.isArray(data.zenbtc_wallets)) {
for (const walletGroup of data.zenbtc_wallets) {
if (walletGroup.wallets && Array.isArray(walletGroup.wallets)) {
for (const wallet of walletGroup.wallets) {
if (wallet.type === 'WALLET_TYPE_BTC_MAINNET' && wallet.address) {
btcAddresses.push(wallet.address);
}
}
}
}
}
if (data.pagination && data.pagination.next_key) {
nextKey = data.pagination.next_key;
} else {
break;
}
}
return btcAddresses;
}

async function getChangeAddresses() {
const changeAddresses = [];

const { data: paramsData } = await axios.get(ZENBTC_PARAMS_API);
const changeAddressKeyIDs = paramsData.params?.changeAddressKeyIDs || [];
for (const keyID of changeAddressKeyIDs) {
const { data: keyData } = await axios.get(`${ZRCHAIN_KEY_BY_ID_API}/${keyID}/WALLET_TYPE_BTC_MAINNET/`);
if (keyData.wallets && Array.isArray(keyData.wallets)) {
for (const wallet of keyData.wallets) {
if (wallet.type === 'WALLET_TYPE_BTC_MAINNET' && wallet.address) {
changeAddresses.push(wallet.address);
}
}
}
}
return changeAddresses;
}

const [btcAddresses, changeAddresses] = await Promise.all([
getBitcoinAddresses(),
getChangeAddresses(),
]);
const allAddresses = [...btcAddresses, ...changeAddresses];
return allAddresses;
}
});
},
}
72 changes: 72 additions & 0 deletions projects/zenrock/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
const { sumTokens: sumBitcoinTokens } = require('../helper/chain/bitcoin');
const { zenrock } = require('../helper/bitcoin-book/fetchers');

/**
* Queries Bitcoin balances for all zrchain treasury and change addresses
* Returns balances object with Bitcoin TVL
*/
async function tvl() {
// Fetch all protocol addresses (treasury + change) from the bitcoin-book fetcher
const allAddresses = await zenrock();

if (allAddresses.length === 0) {
return { bitcoin: '0' };
}

// Use Bitcoin helper to sum balances for all addresses
const balances = {};
await sumBitcoinTokens({ balances, owners: allAddresses });

return balances;
}

/**
* Queries Zcash balances for all zrchain treasury and change addresses
* Returns balances object with Zcash TVL
*
* NOTE: Currently using supply-based approach as a test implementation.
* The address-based approach is commented out below for future use.
*/
async function zcashTvl() {
// Fetch custodied amount from DCT supply endpoint
const { get } = require('../helper/http');
const { getConfig } = require('../helper/cache');
const sdk = require('@defillama/sdk');

const DCT_SUPPLY_API = 'https://api.diamond.zenrocklabs.io/dct/supply';

const supplyData = await getConfig('zenrock/dct_supply', DCT_SUPPLY_API, {
fetcher: async () => {
const response = await get(DCT_SUPPLY_API);
return response;
}
});

const balances = {};

// Find ASSET_ZENZEC in supplies array
const zenZecSupply = supplyData.supplies?.find(
item => item.supply?.asset === 'ASSET_ZENZEC'
);

if (zenZecSupply && zenZecSupply.supply?.custodied_amount) {
// custodied_amount is in Zatoshi (smallest unit, like satoshis for BTC)
// Convert to ZEC by dividing by 1e8
const custodiedAmount = Number(zenZecSupply.supply.custodied_amount);
const custodiedZEC = custodiedAmount / 1e8;

sdk.util.sumSingleBalance(balances, 'zcash', custodiedZEC);
}

return balances;
}

module.exports = {
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.',
bitcoin: {
tvl,
},
zcash: {
tvl: zcashTvl,
},
};
Loading