UNPKG

@intuweb3/sdk

Version:

INTU SDK - Modern blockchain interaction toolkit

293 lines 12.3 kB
import { ethers } from "ethers"; import { loadJson, JSON_PATHS } from "../../utils/json-imports.js"; const getVaultJson = (() => { let vaultJson = null; return async () => { if (!vaultJson) { vaultJson = await loadJson(JSON_PATHS.VAULT); } return vaultJson; }; })(); export async function getBlockHeightFromRPC(provider) { try { const blockNumber = await provider.getBlockNumber(); return blockNumber; } catch (error) { console.error("Failed to get block height from RPC:", error); throw error; } } export async function getBlockHeightFromIndexer(indexerUrl, chainId) { try { const query = chainId ? `{ chain_metadata(where: { chain_id: { _eq: ${chainId} } }) { block_height chain_id } }` : `{ chain_metadata { block_height chain_id } }`; const response = await fetch(indexerUrl, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ query }), }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); let blockHeight; if (Array.isArray(data?.data?.chain_metadata)) { if (chainId) { const matchingEntry = data.data.chain_metadata.find((entry) => entry.chain_id === chainId); blockHeight = matchingEntry?.block_height; console.log(`🔍 Found block height for chainId ${chainId}:`, blockHeight); } else { blockHeight = data?.data?.chain_metadata?.[0]?.block_height; console.log(`⚠️ No chainId filter provided, using first entry:`, blockHeight); } } else { blockHeight = data?.data?.chain_metadata?.block_height; } if (blockHeight === undefined || blockHeight === null) { console.warn("Block height not available from indexer response"); throw new Error("Block height not available from indexer"); } console.log("✅ Block height retrieved:", blockHeight); return blockHeight; } catch (error) { console.error("Failed to get block height from indexer:", error); throw error; } } export async function isIndexerBehind(provider, indexerUrl, threshold = 60) { try { const rpcHeight = await getBlockHeightFromRPC(provider); const chainId = (await provider.getNetwork()).chainId; try { const indexerHeight = await getBlockHeightFromIndexer(indexerUrl, chainId); const difference = rpcHeight - indexerHeight; console.log(`Block height difference: ${difference} (RPC: ${rpcHeight}, Indexer: ${indexerHeight})`); return difference > threshold; } catch (indexerError) { console.log(`Block height comparison skipped - indexer doesn't support block height queries (RPC: ${rpcHeight}, Indexer: unavailable)`); return false; } } catch (error) { console.error("Error checking block heights:", error); return true; } } export async function getUserPreRegisterInfosDirect(vaultAddress, userAddress, provider) { const VaultJson = await getVaultJson(); const vaultContract = new ethers.Contract(vaultAddress, VaultJson.abi, provider); try { const filter = vaultContract.filters.VaultUserPreRegister(userAddress); const vaultInfo = await vaultContract.vaultInfos(); const createdBlock = vaultInfo.createdBlock.toNumber(); const currentBlock = await provider.getBlockNumber(); const events = await vaultContract.queryFilter(filter, createdBlock, currentBlock); if (events.length === 0) { return { user: userAddress, registered: false, parisEncKey: "", megaPublicKey: "", encMegaSecretKey: "", dbKey: "", }; } const event = events[events.length - 1]; if (!event || !event.args) { throw new Error(`VaultUserPreRegister event found but args are missing`); } const parisEncKey = event.args[1] || ""; const megaPublicKey = event.args[2] || ""; const encSharedKey = event.args[3] || ""; const dbKey = event.args[4] || ""; return { user: userAddress, registered: true, parisEncKey: parisEncKey || "", megaPublicKey: megaPublicKey || "", encMegaSecretKey: encSharedKey || "", dbKey: dbKey || "", }; } catch (error) { console.warn(`Failed to get pre-registration data for user ${userAddress}:`, error); return { user: userAddress, registered: false, parisEncKey: "", megaPublicKey: "", encMegaSecretKey: "", dbKey: "", }; } } export async function getUserRegistrationAllInfosDirect(vaultAddress, provider) { const VaultJson = await getVaultJson(); const vaultContract = new ethers.Contract(vaultAddress, VaultJson.abi, provider); try { const vaultInfo = await vaultContract.vaultInfos(); const users = vaultInfo.users; const filter = vaultContract.filters.VaultUserRegisteredAll(); const createdBlock = vaultInfo.createdBlock.toNumber(); const currentBlock = await provider.getBlockNumber(); const events = await vaultContract.queryFilter(filter, createdBlock, currentBlock); const userRegistrationMap = new Map(); events.forEach((event) => { if (event.args) { const userAddress = event.args[0]; const step1Dealings = event.args[1] || ""; const openingKey = event.args[2] || ""; const openingKappa = event.args[3] || ""; const openingLambda = event.args[4] || ""; const simpleDealingKey = event.args[5] || ""; const simpleDealingKappa = event.args[6] || ""; const transcriptKey = event.args[7] || ""; const transcriptKappa = event.args[8] || ""; const transcriptLambda = event.args[9] || ""; const step3Crypto = event.args[10] || ""; userRegistrationMap.set(userAddress.toLowerCase(), { user: userAddress, registered: true, step1Dealings: step1Dealings, pedersenOpeningKey: openingKey, pedersenOpeningKappa: openingKappa, pedersenOpeningLambda: openingLambda, simpleDealingKey: simpleDealingKey, simpleDealingKappa: simpleDealingKappa, pedersenTranscriptKey: transcriptKey, pedersenTranscriptKappa: transcriptKappa, pedersenTranscriptLambda: transcriptLambda, step3Crypto: step3Crypto, }); } }); const userRegistrations = new Array(users.length).fill(null); users.forEach((userAddress, index) => { const registration = userRegistrationMap.get(userAddress.toLowerCase()); if (registration) { userRegistrations[index] = registration; } }); return userRegistrations; } catch (error) { console.warn(`Failed to get registration data from RPC:`, error); try { const vaultInfo = await vaultContract.vaultInfos(); const users = vaultInfo.users; return new Array(users.length).fill(null); } catch { return []; } } } export async function getTransactionLeanDirect(vaultAddress, txId, provider) { const VaultJson = await getVaultJson(); const vaultContract = new ethers.Contract(vaultAddress, VaultJson.abi, provider); try { const txInfo = await vaultContract.transactionInfos(txId); const filter = vaultContract.filters.TransactionProposed(txId); const vaultInfo = await vaultContract.vaultInfos(); const createdBlock = vaultInfo.createdBlock.toNumber(); const currentBlock = await provider.getBlockNumber(); const events = await vaultContract.queryFilter(filter, createdBlock, currentBlock); if (events.length === 0) { throw new Error(`Transaction ${txId} event not found in blockchain`); } const event = events.find((e) => { const args = e.args; if (!args) return false; const eventTxId = args[0]; return eventTxId && eventTxId.toNumber() === txId; }) || events[0]; if (!event || !event.args) { throw new Error(`Transaction ${txId} event found but args are missing`); } const transactionData = event.args[1] || ""; const notes = event.args[2] || ""; return { id: txId, transactionData: transactionData || "", transactionNotes: notes || "", signedTransactionsNeeded: Number(txInfo.votesNeeded), userSignedTransactions: [], }; } catch (error) { console.warn(`Failed to get transaction ${txId} from RPC:`, error); throw error; } } export async function retryWithIndexerFallback(indexerFn, directFn, provider, indexerUrl, retries = 5, delay = 1000) { try { const indexerBehind = await isIndexerBehind(provider, indexerUrl); if (indexerBehind) { return await directFn(); } return await indexerFn(); } catch (error) { console.log(`Attempt failed, retries left: ${retries}. Error:`, error?.message || error); if (retries === 0) { throw error; } await new Promise((resolve) => setTimeout(resolve, delay)); return retryWithIndexerFallback(indexerFn, directFn, provider, indexerUrl, retries - 1, delay); } } export async function getDataWithFallback(indexerFn, provider, indexerUrl, blockThreshold = 50, rpcFallbackFn) { try { const rpcHeight = await getBlockHeightFromRPC(provider); const chainId = (await provider.getNetwork()).chainId; try { const indexerHeight = await getBlockHeightFromIndexer(indexerUrl, chainId); const blockDifference = rpcHeight - indexerHeight; console.log(`Block height difference: ${blockDifference} (RPC: ${rpcHeight}, Indexer: ${indexerHeight})`); if (blockDifference > blockThreshold) { console.warn(`⚠️ [INDEXER_FALLBACK] Indexer is ${blockDifference} blocks behind RPC (threshold: ${blockThreshold}). Falling back to RPC.`); if (rpcFallbackFn) { try { console.log("🔄 [INDEXER_FALLBACK] Attempting RPC fallback..."); const rpcResult = await rpcFallbackFn(); console.log("✅ [INDEXER_FALLBACK] RPC fallback successful"); return rpcResult; } catch (rpcError) { console.warn("❌ [INDEXER_FALLBACK] RPC fallback failed, trying indexer anyway:", rpcError); } } else { console.warn("⚠️ [INDEXER_FALLBACK] No RPC fallback function provided, proceeding with indexer"); } } } catch (indexerError) { console.log(`Block height comparison skipped - indexer doesn't support block height queries (RPC: ${rpcHeight}, Indexer: unavailable)`); } return await indexerFn(); } catch (error) { console.warn("Block height check failed or indexer fetch threw an error, retrying indexer function:", error.message || error); return await indexerFn(); } } //# sourceMappingURL=index.js.map