Zenrock: prevent historical query errors due to missing DCT module (#16998)

This commit is contained in:
Peyton Spencer
2025-11-11 05:10:26 -05:00
committed by GitHub
parent c6e30df0f5
commit da0a8c4baa
2 changed files with 28 additions and 60 deletions

View File

@@ -242,38 +242,14 @@ module.exports = {
})
return Array.from(new Set(staticAddresses))
},
zenrock: async (blockHeight = null) => {
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';
// Helper function to make API requests with optional height header
async function apiRequest(url, height = null) {
const options = {};
if (height) {
options.headers = {
'x-cosmos-block-height': String(height)
};
}
try {
return await get(url, options);
} catch (error) {
// If historical query fails (code 13 - nil pointer), throw descriptive error
// This indicates either: (1) the module was not present on-chain at this block height,
// or (2) historical state has been pruned (varies by module)
const errorStr = error.message || error.toString() || '';
if (errorStr.includes('code 13') || errorStr.includes('nil pointer')) {
throw new Error(`Historical data unavailable for block height ${height}. The module may not have been present on-chain at this block height, or historical state may have been pruned (varies by module).`);
}
throw error;
}
}
// Use cache key that includes block height for historical queries
const cacheKey = blockHeight ? `zenrock/addresses/${blockHeight}` : 'zenrock/addresses';
return getConfig(cacheKey, undefined, {
// Always use latest addresses since wallets are only added, never removed.
// The balance checker will use the historical timestamp to get correct balances.
return getConfig('zenrock/addresses', undefined, {
fetcher: async () => {
async function getBitcoinAddresses() {
const btcAddresses = [];
@@ -284,7 +260,7 @@ module.exports = {
if (nextKey) {
url += `?pagination.key=${encodeURIComponent(nextKey)}`;
}
const data = await apiRequest(url, blockHeight);
const data = await 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)) {
@@ -306,12 +282,13 @@ module.exports = {
}
async function getChangeAddresses() {
const paramsData = await get(ZENBTC_PARAMS_API);
if (!paramsData?.params?.changeAddressKeyIDs) {
return [];
}
const changeAddresses = [];
const paramsData = await apiRequest(ZENBTC_PARAMS_API, blockHeight);
const changeAddressKeyIDs = paramsData.params?.changeAddressKeyIDs || [];
for (const keyID of changeAddressKeyIDs) {
const keyData = await apiRequest(`${ZRCHAIN_KEY_BY_ID_API}/${keyID}/WALLET_TYPE_BTC_MAINNET/`, blockHeight);
for (const keyID of paramsData.params.changeAddressKeyIDs) {
const keyData = await 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) {

View File

@@ -97,19 +97,7 @@ async function apiRequest(url, blockHeight = null) {
'x-cosmos-block-height': String(blockHeight)
};
}
try {
return await get(url, options);
} catch (error) {
// If historical query fails (code 13 - nil pointer), throw descriptive error
// This indicates either: (1) the module was not present on-chain at this block height,
// or (2) historical state has been pruned (varies by module - DCT was launched on 2025-10-31)
const errorStr = error.message || error.toString() || '';
if (errorStr.includes('code 13') || errorStr.includes('nil pointer')) {
throw new Error(`Historical data unavailable for block height ${blockHeight}. The module may not have been present on-chain at this block height, or historical state may have been pruned (varies by module).`);
}
throw error;
}
}
async function tvl(api) {
@@ -124,8 +112,7 @@ async function tvl(api) {
}
// Fetch all protocol addresses (treasury + change) from the bitcoin-book fetcher
// Pass blockHeight for historical queries
const allAddresses = await zenrock(blockHeight);
const allAddresses = await zenrock();
if (allAddresses.length === 0) {
return { bitcoin: '0' };
@@ -153,21 +140,25 @@ async function zcashTvl(api) {
}
// Fetch custodied amount from DCT supply endpoint with height header
const supplyData = await apiRequest(`${ZRCHAIN_API}/dct/supply`, blockHeight);
// Find ASSET_ZENZEC in supplies array
let supplyData;
try {
supplyData = await apiRequest(`${ZRCHAIN_API}/dct/supply`, blockHeight);
} catch (error) {
// If DCT supply API fails, return 0 supply instead of erroring
// This can happen for historical queries before the module was launched (2025-10-31)
return balances;
}
const zenZecSupply = supplyData.supplies?.find(
item => item.supply?.asset === 'ASSET_ZENZEC'
);
if (zenZecSupply && zenZecSupply.supply?.custodied_amount) {
if (!zenZecSupply?.supply?.custodied_amount) {
return balances;
}
// 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;
}