UNPKG

@augustdigital/vaults

Version:

JS SDK for web3 interactions with the August Digital Lending Pools

414 lines 16.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.POOL_FUNCTIONS = exports.determineBlockSkipInternal = exports.determineBlockCutoff = exports.filterOutBySize = exports.MULTI_ASSET_VAULTS = exports.SUBACCOUNT_CALEB = exports.VAULT_AgoraAUSD = void 0; exports.promiseSettle = promiseSettle; exports.getVaultRewards = getVaultRewards; exports.getIdleAssets = getIdleAssets; exports.buildFormattedVault = buildFormattedVault; exports.fetchVaultWithFallback = fetchVaultWithFallback; exports.fetchVaultsBatch = fetchVaultsBatch; exports.fetchVaultsComprehensive = fetchVaultsComprehensive; exports.filterVaultsIntelligently = filterVaultsIntelligently; const abis_1 = require("@augustdigital/abis"); const utils_1 = require("@augustdigital/utils"); exports.VAULT_AgoraAUSD = '0x828BC5895b78b2fb591018Ca5bDC2064742D6D0f'; exports.SUBACCOUNT_CALEB = '0xFff71B0b66f076C60Fa2f176a34a6EA709ccF21B'; exports.MULTI_ASSET_VAULTS = [ '0x18EE038C114a07f4B08b420fb1E4149a4F357249', ]; async function promiseSettle(promises, maxRetries = 3, baseDelay = 1000) { const promisesWithRetry = promises.map((promise, index) => (0, utils_1.withRetry)(async () => { try { return await promise; } catch (error) { console.warn(`Promise at index ${index} failed:`, error); throw error; } }, maxRetries, baseDelay)); const results = await Promise.allSettled(promisesWithRetry); return results.map((result, index) => { if (result.status === 'fulfilled') { return result.value; } else { console.warn(`Promise at index ${index} failed after retries:`, result.reason); return null; } }); } const filterOutBySize = (usdAmount) => usdAmount > 10000; exports.filterOutBySize = filterOutBySize; const determineBlockCutoff = (chain) => { switch (chain) { case 56: return 120000; case 43114: return 120000; default: return 150000; } }; exports.determineBlockCutoff = determineBlockCutoff; const determineBlockSkipInternal = (chain) => { switch (chain) { case 43114: return 8000; case 56: return 8000; default: return 50000; } }; exports.determineBlockSkipInternal = determineBlockSkipInternal; exports.POOL_FUNCTIONS = [ 'decimals', 'asset', 'totalSupply', 'totalAssets', 'maxSupply', 'withdrawalFee', 'lagDuration', 'withdrawalsPaused', ]; function getVaultRewards(tokenizedVault) { const upshiftPointRewards = tokenizedVault.rewards?.filter((r) => r.text === 'Upshift Points'); const sortedPointsMultipliers = upshiftPointRewards?.sort((a, b) => new Date(b.start_datetime).getTime() - new Date(a.start_datetime).getTime()); const upshiftPointsMultipliers = sortedPointsMultipliers?.map((r) => ({ timestamp: new Date(r.start_datetime).getTime() / 1000, multiplier: r.multiplier, })); const latestUpshiftPointMultiplier = upshiftPointsMultipliers?.[0]?.multiplier; const rewards = { upshiftPoints: !!upshiftPointRewards.length ? `${latestUpshiftPointMultiplier}x Upshift Points` : '', latestUpshiftPointMultiplier, upshiftPointsMultipliers: upshiftPointsMultipliers, additionalPoints: tokenizedVault.rewards.map((r) => `${r.multiplier !== 1 ? `${r.multiplier}x` : ''} ${r.text}`), }; return rewards; } async function getIdleAssets(provider, vaultAddress, underlying, totalAssets) { let idleAssets; if (utils_1.OLD_LENDING_POOLS.includes(vaultAddress)) { const globalLoansAmount = await (0, utils_1.createContract)({ provider, address: vaultAddress, abi: abis_1.ABI_LENDING_POOLS, }).globalLoansAmount(); idleAssets = BigInt(totalAssets.raw) - BigInt(globalLoansAmount); } else { idleAssets = await (0, utils_1.createContract)({ provider, address: underlying, abi: abis_1.ABI_ERC20, }).balanceOf(vaultAddress); } return idleAssets; } async function buildFormattedVault(provider, tokenizedVault, contractCalls) { const underlying = { address: contractCalls.asset, symbol: await (0, utils_1.getSymbol)(provider, contractCalls.asset), decimals: contractCalls.decimals, }; if (!tokenizedVault.reported_apy) console.warn('#buildFormattedVault::reported_apy: no APY found for', tokenizedVault.receipt_token_symbol); const apy = { apy: tokenizedVault?.reported_apy?.apy * 100, explainer: tokenizedVault?.reported_apy?.explainer, liquidApy: tokenizedVault?.reported_apy?.liquid_apy * 100, rewardsClaimable: tokenizedVault?.reported_apy?.rewards_claimable, rewardsCompounded: tokenizedVault?.reported_apy?.rewards_compounded, underlyingApy: tokenizedVault?.reported_apy?.underlying_apy * 100, }; const strategists = tokenizedVault.subaccounts.map((s) => ({ address: s.address, logo: s.strategist?.strategist_logo, name: s.strategist?.strategist_name, })); const platformFee = { fee: tokenizedVault.platform_fee_override.management_fee, isWaived: tokenizedVault.platform_fee_override.is_fee_waived, }; const rewards = getVaultRewards(tokenizedVault); const idleAssets = await getIdleAssets(provider, tokenizedVault.address, contractCalls.asset, contractCalls.totalAssets); return { lagDuration: Number(contractCalls.lagDuration), address: tokenizedVault.address, name: tokenizedVault.vault_name, logoUrl: tokenizedVault.vault_logo_url, symbol: tokenizedVault.receipt_token_symbol, description: tokenizedVault.description, startDatetime: String(tokenizedVault.start_datetime), publicType: tokenizedVault.public_type, internalType: tokenizedVault.internal_type, status: tokenizedVault.status, tags: [tokenizedVault.public_type], isFeatured: tokenizedVault.is_featured, isVisible: tokenizedVault.is_visible, reserveTarget: tokenizedVault.reserve_target, reserveTolerance: tokenizedVault.reserve_tolerance, underlying, apy: apy.apy, apyBreakdown: apy, rewards, strategists, weeklyPerformanceFee: tokenizedVault.weekly_performance_fee_bps, platformFee, totalAssets: contractCalls.totalAssets, totalSupply: contractCalls.totalSupply, loansOperator: contractCalls.loansOperator, decimals: contractCalls.decimals, maxSupply: contractCalls.maxSupply, chainId: tokenizedVault.chain, maxDailyDrawdown: tokenizedVault.max_daily_drawdown || 0, risk: tokenizedVault.risk, isWithdrawalPaused: contractCalls.withdrawalsPaused, idleAssets: (0, utils_1.toNormalizedBn)(idleAssets, contractCalls.decimals), isDepositPaused: (0, utils_1.isBadVault)(tokenizedVault.address), }; } async function fetchVaultWithFallback(vaultAddress, fetchFn, options = {}) { const { maxRetries = 3, baseDelay = 1000 } = options; try { const data = await (0, utils_1.withRetry)(fetchFn, maxRetries * 2, baseDelay * 2); return { success: true, data, strategy: 'extended-retry' }; } catch (error) { console.warn(`Vault ${vaultAddress} failed with extended retry strategy:`, error); } return { success: false, error: new Error(`All strategies failed for vault ${vaultAddress}`), strategy: 'all-failed', }; } async function fetchVaultsBatch(vaults, options = {}) { const { maxRetries = 3, baseDelay = 1000, batchSize = 10, parallelLimit = 5, } = options; const successful = []; const failed = []; let totalAttempted = 0; for (let i = 0; i < vaults.length; i += batchSize) { const batch = vaults.slice(i, i + batchSize); const batchPromises = batch.map(async (vault, batchIndex) => { const globalIndex = i + batchIndex; totalAttempted++; try { const result = await fetchVaultWithFallback(vault.address, vault.fetchFn, { maxRetries, baseDelay, }); if (result.success && result.data) { successful.push({ index: globalIndex, data: result.data, strategy: result.strategy, }); } else { failed.push({ index: globalIndex, error: result.error || new Error('Unknown failure'), address: vault.address, }); } } catch (error) { failed.push({ index: globalIndex, error: error, address: vault.address, }); } }); await Promise.all(batchPromises); if (i + batchSize < vaults.length) { await new Promise((resolve) => setTimeout(resolve, 100)); } } const successRate = totalAttempted > 0 ? (successful.length / totalAttempted) * 100 : 0; return { successful, failed, totalAttempted, successRate, }; } async function fetchVaultsComprehensive(vaults, fetchFn, options = {}) { const { maxRetries = 5, baseDelay = 2000, batchSize = 10, parallelLimit = 5, includeClosed = false, includeInvisible = false, fallbackRpcUrls = {}, timeout = 60000, } = options; const successful = []; const failed = []; const rpcFailures = {}; let totalAttempted = 0; for (let i = 0; i < vaults.length; i += batchSize) { const batch = vaults.slice(i, i + batchSize); const batchPromises = batch.map(async (vault, batchIndex) => { const globalIndex = i + batchIndex; totalAttempted++; let attempts = 0; let lastError; for (attempts = 1; attempts <= maxRetries; attempts++) { try { const data = await Promise.race([ fetchFn(vault), new Promise((_, reject) => setTimeout(() => reject(new Error('Primary fetch timeout')), timeout)), ]); successful.push({ index: globalIndex, data, strategy: 'primary-retry', attempts, }); return; } catch (error) { lastError = error; if (attempts < maxRetries) { const delay = baseDelay * Math.pow(2, attempts - 1); console.warn(`Vault ${vault.address} attempt ${attempts} failed, retrying in ${delay}ms:`, error); await new Promise((resolve) => setTimeout(resolve, delay)); } } } if (fallbackRpcUrls[vault.chain] && fallbackRpcUrls[vault.chain].length > 0) { for (const fallbackRpc of fallbackRpcUrls[vault.chain]) { try { const data = await Promise.race([ fetchFn({ ...vault, fallbackRpc }), new Promise((_, reject) => setTimeout(() => reject(new Error('Fallback RPC timeout')), timeout)), ]); successful.push({ index: globalIndex, data, strategy: 'fallback-rpc', attempts: attempts + 1, }); return; } catch (error) { console.warn(`Vault ${vault.address} failed with fallback RPC ${fallbackRpc}:`, error); } } } try { const data = await Promise.race([ fetchFn({ ...vault, minimal: true }), new Promise((_, reject) => setTimeout(() => reject(new Error('Minimal fetch timeout')), timeout)), ]); successful.push({ index: globalIndex, data, strategy: 'minimal-data', attempts: attempts + 1, }); return; } catch (error) { console.warn(`Vault ${vault.address} failed with minimal data:`, error); } try { const extendedDelay = baseDelay * 3; await new Promise((resolve) => setTimeout(resolve, extendedDelay)); const data = await Promise.race([ fetchFn(vault), new Promise((_, reject) => setTimeout(() => reject(new Error('Extended retry timeout')), timeout)), ]); successful.push({ index: globalIndex, data, strategy: 'extended-retry', attempts: attempts + 2, }); return; } catch (error) { console.warn(`Vault ${vault.address} failed with extended retry:`, error); } failed.push({ index: globalIndex, error: lastError || new Error('All strategies failed'), address: vault.address, chain: vault.chain, attempts: attempts + 2, }); rpcFailures[vault.chain] = (rpcFailures[vault.chain] || 0) + 1; }); await Promise.all(batchPromises); if (i + batchSize < vaults.length) { await new Promise((resolve) => setTimeout(resolve, 200)); } } const successRate = totalAttempted > 0 ? (successful.length / totalAttempted) * 100 : 0; return { successful, failed, totalAttempted, successRate, coverageReport: { totalVaults: vaults.length, successfulVaults: successful.length, failedVaults: failed.length, closedVaults: 0, invisibleVaults: 0, rpcFailures, }, }; } function filterVaultsIntelligently(vaults, options = {}) { const { includeClosed = true, includeInvisible = true, includeNull = false, minStatus = ['active', 'closed'], } = options; const active = []; const closed = []; const invisible = []; const failed = []; vaults.forEach((vault, index) => { if (vault === null) { if (includeNull) { failed.push({ index, reason: 'null_response' }); } return; } const status = vault.status || 'unknown'; const isVisible = vault.isVisible ?? vault.is_visible ?? true; if (status === 'active' && isVisible) { active.push(vault); } else if (status === 'closed' && includeClosed) { closed.push(vault); } else if (!isVisible && includeInvisible) { invisible.push(vault); } else if (minStatus.includes(status)) { active.push(vault); } else { failed.push({ index, reason: `status_${status}_invisible_${!isVisible}`, }); } }); const total = vaults.length; const successCount = active.length + closed.length + invisible.length; const successRate = total > 0 ? (successCount / total) * 100 : 0; return { active, closed, invisible, failed, summary: { total, active: active.length, closed: closed.length, invisible: invisible.length, failed: failed.length, successRate, }, }; } //# sourceMappingURL=utils.js.map