@intuweb3/sdk
Version:
INTU SDK - Modern blockchain interaction toolkit
293 lines • 12.3 kB
JavaScript
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