Vaults
All Lucidly Pro vaults
Lucidly Pro ยท no active vault
// (or, for per-tenant builds, inline-substitute at build time) const INDEXER_URL = (() => { if (typeof window !== "undefined" && typeof window.LUCIDLY_INDEXER_URL === "string") { return window.LUCIDLY_INDEXER_URL; } const h = (typeof window !== "undefined" && window.location && window.location.hostname) || ""; if (h === "localhost" || h === "127.0.0.1" || h === "0.0.0.0" || h.endsWith(".local")) { return "http://localhost:42069"; } return ""; // production with no override โ†’ indexer disabled; UI degrades gracefully })(); // Expose for debugging + so the Research rail can prefill its "indexer URL" field. if (typeof window !== "undefined") window.INDEXER_URL = INDEXER_URL; function indexerEndpoint(){ const b=(INDEXER_URL||"").replace(/\/+$/,""); return b ? (b.endsWith("/graphql")?b:b+"/graphql") : ""; } async function indexerQuery(query, variables){ const url=indexerEndpoint(); if(!url) return null; try{ const r=await fetch(url,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({query,variables})}); if(!r.ok) return null; const j=await r.json(); return j.data ?? null; }catch(e){ console.warn("indexer query failed:",e); return null; } } // NOTE: Ponder pluralises table names by appending "s" (vaultโ†’vaults, policyRequestโ†’policyRequests, // rateUpdateโ†’rateUpdates). If your Ponder version differs, run `ponder codegen` and adjust here. async function idxVaults(){ const d=await indexerQuery(`query{ vaults(limit:200, orderBy:"createdAt", orderDirection:"desc"){ items{ chainId nonce vault strategist baseAsset policyManager rate policyCount depositCount } } }`); return d?.vaults?.items ?? null; } async function idxRateHistory(vaultAddr){ const d=await indexerQuery(`query($v:String!){ rateUpdates(where:{vault:$v}, orderBy:"at", orderDirection:"asc", limit:1000){ items{ at newRate } } }`,{v:String(vaultAddr).toLowerCase()}); return d?.rateUpdates?.items ?? null; } async function idxDeposits(vaultAddr){ const d=await indexerQuery(`query($v:String!){ deposits(where:{vault:$v}, orderBy:"nonce", orderDirection:"desc", limit:200){ items{ nonce receiver depositAsset depositAmount shareAmount at txHash } } }`,{v:String(vaultAddr).toLowerCase()}); return d?.deposits?.items ?? null; } async function idxShareTransfers(vaultAddr){ const d=await indexerQuery(`query($v:String!){ shareTransfers(where:{vault:$v}, orderBy:"blockNumber", orderDirection:"desc", limit:200){ items{ from to amount at txHash } } }`,{v:String(vaultAddr).toLowerCase()}); return d?.shareTransfers?.items ?? null; } async function idxPolicyRequests(){ const d=await indexerQuery(`query{ policyRequests(limit:200, orderBy:"requestedAt", orderDirection:"desc"){ items{ chainId protocolId target selector note requestedBy requestedAt txHash } } }`); return d?.policyRequests?.items ?? null; } const CHAIN_BY_ID = () => Object.fromEntries(ALL_CHAINS().map(c=>[String(c.chainId),c])); function explorerFor(chainId){ return CHAIN_BY_ID()[String(chainId)]?.explorer || ""; } /* ---- Review queue (indexer-powered) ---- */ function vReview(){ setTimeout(loadReviewQueue,0); return `

Policy review queue

Strategist requests for policies not yet whitelisted in the Registry. The owner reviews + audits, adds the template, and the strategist then enables it in one click.

${indexerEndpoint() ? `
Loading from indexerโ€ฆ
` : ``}`; } async function loadReviewQueue(){ const el=document.getElementById("reviewQueue"); if(!el) return; const items=await idxPolicyRequests(); if(items===null){ el.innerHTML=`Indexer unreachable.`; return; } if(items.length===0){ el.innerHTML=`No open policy requests.`; return; } el.innerHTML=`${ items.map(it=>{const exp=explorerFor(it.chainId);return ``;}).join("")}
ChainProtocolTargetSelectorNoteRequested by
${CHAIN_BY_ID()[String(it.chainId)]?.name||it.chainId} ${short(it.protocolId)} ${exp?`${short(it.target)} โ†—`:short(it.target)} ${it.selector} ${escapeHtml(it.note||"")} ${short(it.requestedBy)} ${exp?`tx โ†—`:""}
`; } /* ---- Indexed vault discovery (Vaults page) ---- */ async function loadIndexedVaults(){ const el=document.getElementById("indexedVaults"); if(!el) return; el.innerHTML=`
Discovering deployed vaults from indexerโ€ฆ
`; const items=await idxVaults(); if(items===null){ el.innerHTML=""; return; } // indexer down โ†’ silently keep the RPC list only if(items.length===0){ el.innerHTML=`
No vaults deployed via the factory yet.
`; return; } el.innerHTML=`
All deployed vaults (indexed ยท ${items.length})
${ items.map(it=>{const exp=explorerFor(it.chainId);const cc=CHAIN_BY_ID()[String(it.chainId)];return ``;}).join("")}
#VaultStrategistBasePoliciesDepositsNAVChain
${it.nonce ?? "โ€”"} ${exp?`${short(it.vault)} โ†—`:short(it.vault)} ${short(it.strategist)} ${short(it.baseAsset)} ${it.policyCount ?? 0} ${it.depositCount ?? 0} ${it.rate?fmtUnits(BigInt(it.rate),6,4):"โ€”"} ${cc?cc.name:it.chainId}
`; } /* ---- NAV history chart (Monitor page) ---- */ async function loadNavHistory(){ const ctx=document.getElementById("navHistChart"); if(!ctx||!window.Chart) return; const v=V(); if(!v) return; const items=await idxRateHistory(v.contracts.vault); if(!items || items.length===0){ const w=document.getElementById("navHistWrap"); if(w) w.innerHTML=`
No NAV history indexed yet${indexerEndpoint()?"":" (set INDEXER_URL to enable)"}.
`; return; } if(window.__navHistChart) window.__navHistChart.destroy(); window.__navHistChart=new Chart(ctx,{type:"line", data:{labels:items.map(p=>new Date(Number(p.at)*1000).toLocaleDateString()), datasets:[{label:"NAV / share",data:items.map(p=>Number(p.newRate)/1e6),borderColor:"#5b9dff",backgroundColor:"rgba(91,157,255,.12)",fill:true,tension:.25,pointRadius:0}]}, options:{plugins:{legend:{display:false}},scales:{y:{ticks:{color:"#8b98a9"},grid:{color:"#1f2937"}},x:{ticks:{color:"#8b98a9",maxTicksLimit:6},grid:{display:false}}}}}); } /* ---- Deposit ledger (Monitor page, indexed by nonce) ---- */ async function loadDepositLedger(){ const el=document.getElementById("depositLedger"); if(!el) return; const v=V(); if(!v) return; const body=el.querySelector("div:last-child"); if(!indexerEndpoint()){ if(body) body.innerHTML=`Set INDEXER_URL to enable the indexed deposit ledger.`; return; } const items=await idxDeposits(v.contracts.vault); if(items===null){ if(body) body.innerHTML=`Indexer unreachable.`; return; } if(items.length===0){ if(body) body.innerHTML=`No deposits indexed for this vault yet.`; return; } const exp=v.explorer || explorerFor(v.chainId); const dec=state.data?.decimals ?? 6, sym=state.data?.symbol ?? "shares"; el.innerHTML=`
Deposit ledger (indexed ยท ${items.length} ยท newest first)
${ items.map(it=>``).join("")}
#ReceiverAsset inAmountSharesWhen
${it.nonce} ${exp?`${short(it.receiver)} โ†—`:short(it.receiver)} ${short(it.depositAsset)} ${fmtUnits(BigInt(it.depositAmount),dec,4)} ${fmtUnits(BigInt(it.shareAmount),dec,4)} ${sym} ${new Date(Number(it.at)*1000).toLocaleDateString()} ${exp?`tx โ†—`:""}
`; } /* ---- Share-transfer (holder) ledger (Monitor page, indexed) ---- */ async function loadShareTransfers(){ const el=document.getElementById("shareLedger"); if(!el) return; const v=V(); if(!v) return; const body=el.querySelector("div:last-child"); if(!indexerEndpoint()){ if(body) body.innerHTML=`Set INDEXER_URL to enable the indexed holder ledger.`; return; } const items=await idxShareTransfers(v.contracts.vault); if(items===null){ if(body) body.innerHTML=`Indexer unreachable.`; return; } if(items.length===0){ if(body) body.innerHTML=`No share transfers indexed for this vault yet.`; return; } const exp=v.explorer || explorerFor(v.chainId); const dec=state.data?.decimals ?? 6, sym=state.data?.symbol ?? "shares"; const ZERO="0x0000000000000000000000000000000000000000"; const kind=(it)=>{const f=String(it.from).toLowerCase()===ZERO, t=String(it.to).toLowerCase()===ZERO; return f?`mint`:t?`burn`:`transfer`;}; const acct=(a)=>String(a).toLowerCase()===ZERO?`โ€”`:(exp?`${short(a)} โ†—`:short(a)); el.innerHTML=`
Share transfers (indexed ยท ${items.length} ยท newest first)
${ items.map(it=>``).join("")}
TypeFromToSharesWhen
${kind(it)} ${acct(it.from)} ${acct(it.to)} ${fmtUnits(BigInt(it.amount),dec,4)} ${sym} ${new Date(Number(it.at)*1000).toLocaleDateString()} ${exp?`tx โ†—`:""}
`; } const VIEWS={vaults:vVaults,library:vLibrary,review:vReview,overview:vOverview,strategies:vStrategies,queue:vQueue,monitor:vMonitor,settings:vSettings,deploy:vDeploy}; const TITLES={vaults:["Vaults","All Lucidly Pro vaults"],library:["Strategy Library","Whitelisted strategies โ€” pick or request"],review:["Review queue","Strategist policy requests awaiting whitelist"],overview:["Overview","Live portfolio health"],strategies:["Strategies","The Merkle-gated execution whitelist"],queue:["Queue","Asynchronous, illiquid-safe redemptions"],monitor:["Monitor","Live NAV, AUM, flows & alerts"],settings:["Vault settings","Roles, accountant & contracts"],deploy:["Self-deploy","CLI runbook for advanced users"]}; function go(r){ state.route=r; document.querySelectorAll("#nav button").forEach(b=>b.classList.toggle("active",b.dataset.route===r)); $("#bcTitle").textContent=TITLES[r][0]; $("#bcSub").textContent=TITLES[r][1]; $("#view").innerHTML=VIEWS[r](); bindView(); } function render(){ if($("#view")) go(state.route); } function bindView(){ if(state.route==="review") loadReviewQueue(); if(state.route==="vaults" && indexerEndpoint()) loadIndexedVaults(); $("#qLookup")?.addEventListener("click",async()=>{ const u=$("#qUser").value.trim(); const out=$("#qResult"); if(!/^0x[0-9a-fA-F]{40}$/.test(u)){out.innerHTML=`Enter a valid address.`;return;} out.innerHTML=``; try{ const v=V(); const data="0x433a8534"+padAddr(u)+padAddr(v.contracts.vault)+padAddr(v.usdc); const r=await call(v.rpc,v.contracts.queue,data); const deadline=Number(uint(word(r,0))), price=uint(word(r,1)), amount=uint(word(r,2)), inSolve=uint(word(r,3))!==0n; if(amount===0n){ out.innerHTML=`
No open request for this holder (offerAmount = 0).
`; return; } const live=deadline>Math.floor(Date.now()/1000); out.innerHTML=`
${fmtUnits(amount,state.data.decimals,2)} ${state.data.symbol} โ†’ USDC @ ${fmtUnits(price,6,6)}/share
deadline ${new Date(deadline*1000).toLocaleString()} ยท ${live?'live':'expired'}${inSolve?' ยท in solve':''}
`; }catch(e){ out.innerHTML=`Lookup failed: ${escapeHtml(e.message)}`; } }); } /* ============================ wiring ============================ */ function setRpc(){ const val=$("#rpcInput")?.value?.trim(); if(val){state.rpc=val;V().rpc=val;loadVault();} } document.querySelectorAll("#nav button").forEach(b=>b.addEventListener("click",()=>go(b.dataset.route))); $("#openConsole").addEventListener("click",()=>openConsole()); $("#refreshBtn").addEventListener("click",()=>{ state.vaultsLoaded=false; for(const v of VAULTS) v.metrics=null; if(state.route==="overview"||state.route==="strategies"||state.route==="queue"||state.route==="monitor"||state.route==="settings"){ loadVault(); } else { loadAllVaults(); render(); } }); $("#consoleBg").addEventListener("click",e=>{if(e.target.id==="consoleBg")closeConsole();}); $("#createBg").addEventListener("click",e=>{if(e.target.id==="createBg")closeCreate();}); $("#reqBg").addEventListener("click",e=>{if(e.target.id==="reqBg")closeReq();}); $("#addChainBg").addEventListener("click",e=>{if(e.target.id==="addChainBg")closeAddChain();}); $("#consoleInput").addEventListener("input",e=>renderConsole(e.target.value)); $("#cvNext").addEventListener("click",cvNext); $("#cvBack").addEventListener("click",cvBack); $("#connectBtn").addEventListener("click",async()=>{ if(!window.ethereum){ state.wallet="0x" + Math.random().toString(16).slice(2,42).padEnd(40,"0"); state.walletInjected=false; $("#connectBtn").textContent=short(state.wallet); toast("Connected mock wallet (no injected provider). Real deploys need an injected wallet (MetaMask/Rabbit)."); state.vaultsLoaded=false; for(const v of VAULTS) v.metrics=null; render(); return; } try{const acc=await window.ethereum.request({method:"eth_requestAccounts"});state.wallet=acc[0];state.walletInjected=true;$("#connectBtn").textContent=short(acc[0]);toast("Connected "+short(acc[0])); state.vaultsLoaded=false; for(const v of VAULTS) v.metrics=null; render();} catch(e){toast("Wallet connection rejected.");} }); document.addEventListener("keydown",e=>{ if((e.metaKey||e.ctrlKey)&&e.key.toLowerCase()==="k"){e.preventDefault();$("#consoleBg").classList.contains("open")?closeConsole():openConsole();} if(e.key==="Escape"){closeConsole();closeCreate();closeReq();} }); go("vaults");