{"assessments":[],"deployments":[],"fuzz":[],"identity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"interpretation":"Records acceptance and evidence. Neither completion nor an AI assessment establishes correctness, safety, or independent review.","jobId":"db05e39a-2ed0-487e-a6d7-a855a1fe8468","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"f93e4c1e5178e715c68884cce1022ff062ab1f94494b3252935ed94d96be7a9f","dependsOn":[],"execution":{"mustProduce":["dist/index.html"],"network":true,"profile":"none","requires":["network"],"skillHash":"d85feeeba61710fcde95b6484d4ed21af1a49b2b609a1b0ea33f16d5a47ec9ca","skillId":"import-site","tools":[]},"key":"import_site","kind":"code","role":"implement","skillHash":"d85feeeba61710fcde95b6484d4ed21af1a49b2b609a1b0ea33f16d5a47ec9ca","skillId":"import-site","state":"accepted"},{"acceptedSubmissionHash":"dd8e6fcc80ae22ac5bb22599871005147042f9f5582b838a0dc2297a7a6f18f0","dependsOn":["import_site"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"8189a3059fc9bb774ad7dcee66b87f32e25d781025f0e9f82a4c75182fe0ef19","skillId":"site-content-check","tools":[]},"key":"site_content_check","kind":"code","role":"review","skillHash":"8189a3059fc9bb774ad7dcee66b87f32e25d781025f0e9f82a4c75182fe0ef19","skillId":"site-content-check","state":"accepted"}],"objective":"PepesFamily is a static website (HTML/JS, no backend) for a fair-launch token launchpad on Robinhood Chain and Ethereum: it lists tokens, launches and trades them on Uniswap v4, and lets holders claim IMD rewards, all through the visitor’s own browser wallet.","parentJobId":null,"planHash":"fda706df52c4e6e088799df23103a0b7e417b1ae79b3a702622888b635568b40","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"db05e39a-2ed0-487e-a6d7-a855a1fe8468","publication":{"commit":null,"deliveredAt":null,"repoUrl":null},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"52230","feedbackHash":"0659d7630f1cffce193a3e17fc5caf64e029cc1aed6e2f1bbe82e50a55a34210","nodeKey":"import_site","submissionHash":"f93e4c1e5178e715c68884cce1022ff062ab1f94494b3252935ed94d96be7a9f","tag1":"verification:structural","tag2":"acceptance-v2","value":1},{"agentId":"51031","feedbackHash":"5dc656612487383aee3bc79f21fdd10fa7be6241e10f98bed0efbaf448f79510","nodeKey":"site_content_check","submissionHash":"dd8e6fcc80ae22ac5bb22599871005147042f9f5582b838a0dc2297a7a6f18f0","tag1":"review:submission","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"ae3d213c1bcbc4c9b5f49b84a049f0fcf8ef0f30605075f9ab74c39ce24dedc9","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"5fb4f77b3d119b1d","findings":[{"citation":"resolved","description":"When an already connected wallet is on Ethereum and does not know Robinhood Chain, connect() handles a failed wallet_switchEthereumChain by calling wallet_addEthereumChain (lines 799–819). Adding a chain does not guarantee selecting it (EIP-3085: https://eips.ethereum.org/EIPS/eip-3085). requireWallet() never checks the rebuilt signer after this fallback. Trading therefore proceeds on Ethereum using a router address checked for code only on Robinhood. I reproduced the Buy flow issuing an eth_sendTransaction for 0.01 ETH to 0x79eeE0C12C1284bc046e4494Eea6180695F5028A while eth_chainId remained 0x1; an Ethereum RPC getCode of this destination returned 0x. If approved, this is an ETH transfer to an address without the router, with no purchased tokens or refund. This is a hosting blocker because the page can request an irreversible payment on a different network from the advertised trade. The same fallback exists in dist/world/index.html:740–751 and both web/ source copies. Explicitly switch after adding and verify the active chain immediately before permitting a wallet write.","line":832,"path":"dist/index.html","reproduction":"Use an EIP-1193 test wallet initially connected to Ethereum (eth_chainId=0x1), with Robinhood absent. Connect it to an Ethereum action first so signer/account exist. Open /#/t/0xE2C46c7068566740A33A4C93f5445B07BCfE5644, keep Pay with ETH, enter 0.01 and click Buy. Have wallet_switchEthereumChain({chainId:\"0x1237\"}) reject with code 4902, and wallet_addEthereumChain return null while leaving eth_chainId=0x1 (allowed by EIP-3085). Expected: another switch and verification, or abort before any payment request. Actual: the next transaction targets 0x79eee0c12c1284bc046e4494eea6180695f5028a with value 0x2386f26fc10000 on Ethereum. Captured in Chromium using a mock wallet that answered account/chain/estimate requests and rejected eth_sendTransaction after recording it; no payment was broadcast.","severity":"high","snippet":"  if (Number(net.chainId) !== ch.chainId) await connect(ch, true);","title":"A successful add-chain request can send the purchase ETH on the wrong network"},{"citation":"resolved","description":"The launch submit handler calls requireWallet(ch) before reading the form (lines 2589–2607). For a visitor who has not connected yet, requireWallet calls connect with quiet=false; connect then calls route(), which recreates the launch form and clears its fields. The original submit handler resumes against the new empty inputs, losing the name, ticker, metadata and initial-buy amount. In a browser reproduction, the resulting launchWithSplit arguments were empty name, empty symbol, metadata {}, and initialBuy=0. The same code is in web/index.html. Preserve the entered values or suppress rerendering during an action.","line":825,"path":"dist/index.html","reproduction":"Start with no connected wallet and open /#/create. Enter name Review Frog, ticker RFROG and description Review description, leave the optional image and initial buy empty, then press Launch token and approve the wallet connection. Expected: the launch transaction contains those entered fields. Actual: all form fields clear; the pending launch is built with name=\"\", symbol=\"\", metadata=\"{}\" (confirmed by decoding the eth_sendTransaction request from a mock EIP-1193 wallet; no transaction was broadcast). A real wallet/contract simulation rejects this invalid launch, and the user must re-enter the form.","severity":"medium","snippet":"  if (!quiet) route();","title":"Connecting during launch clears the form before the transaction is constructed"},{"citation":"resolved","description":"The Rewards page labels withdrawnDividends(addr) as claimed, but this accounting counter includes recycled rewards: PadToken._recycle increments it before sending IMD to feeRecipient (contracts/src/PadToken.sol:334–341), and PepesEarnToken.recycle increments it when moving IMD to the buyback reserve (contracts/src/earn/PepesEarnToken.sol:298–306). Consequently the Claimed total and per-token column report payments the holder never received. The token-page Claimed so far display (lines 2756–2762), NFT display (2214, 2235), and identical web/index.html are affected too. Use actual DividendClaimed amounts or subtract per-holder RewardsRecycled amounts rather than treating the entire accounting counter as a payout. This is an advisory reporting defect, not evidence of deliberately deceptive claims.","line":1662,"path":"dist/index.html","reproduction":"For a v4/v5 token, let a holder accrue 10 IMD, make no further distributions or holder activity for more than 7 days, then call recycle(holder) without any claim by that holder. The token now reports withdrawableDividendOf(holder)=0 and withdrawnDividends(holder)=10e18, while the 10 IMD went to feeRecipient. Open /#/rewards/<holder>. Expected: Claimed is 0 IMD and forfeited rewards are separately identified. Actual: Claimed is 10 IMD. Reproduced in Chromium by supplying exactly that reachable contract-read state for the V4 token: the page rendered Claimed 10 IMD and Ready to claim 0.","severity":"medium","snippet":"      const [bal, owed, claimed] = await Promise.all([tok.balanceOf(addr), tok.withdrawableDividendOf(addr), tok.withdrawnDividends(addr)]);","title":"Rewards sent away by expiry are displayed as claimed by the holder"},{"citation":"resolved","description":"Metadata is supplied by arbitrary token creators, and the launch contract accepts any metadata string up to 2048 bytes. meta() accepts arbitrary JSON objects, but the token-card renderer passes a truthy description directly to esc(), whose String(value) conversion can throw. A description object with an own toString property set to null is valid JSON and causes Cannot convert object to primitive value. Because every card is generated in one map before assignment to #list, a single such token prevents all cards from rendering, and the home-page catch replaces the whole list with an error. The affected token detail page also fails at its description rendering (line 2670). Validate metadata field types before converting/rendering; the source copy web/index.html has the same defect.","line":1214,"path":"dist/index.html","reproduction":"Launch an IMD-paired token through launchWithSplit (or its router) with name Broken Description, symbol BRK, metadata exactly {\"description\":{\"toString\":null}}, and a valid split such as (0,300,0). No initial buy is needed. Open the Tokens page sorted Newest while this launch is among the newest 60. Expected: the malformed description is ignored or only its card is rejected; other tokens remain visible. Actual: #list becomes \"Couldn't load tokens: Cannot convert object to primitive value\". Confirmed by feeding this exact metadata with otherwise valid on-chain token info into the unmodified Chromium renderHome() path; no on-chain launch was broadcast.","severity":"medium","snippet":"          ${m.description ? `<div class=\"stat\" style=\"overflow:hidden;text-overflow:ellipsis;white-space:nowrap\">${esc(m.description)}</div>` : \"\"}","title":"One launch with object-valued description breaks the entire token list"}],"hash":"dd8e6fcc80ae22ac5bb22599871005147042f9f5582b838a0dc2297a7a6f18f0","nodeId":"fa71b4b5-9c99-488d-aa05-1f45d2d86758","outcome":"completed","summary":"Saved four findings to [.imd-findings.json](/root/.identitymd/work/db05e39a-2ed0-487e-a6d7-a855a1fe8468/fa71b4b5-9c99-488d-aa05-1f45d2d86758/.imd-findings.json): one high hosting blocker and three medium advisories.\n\nFix before hosting: the wallet fallback can request ETH payments on the wrong network.\n\nReviewed all exported pages, scripts, styles, and assets. All 31 targeted Foundry tests passed. No source files changed.","treeHash":null,"usage":{"cachedInputTokens":5029120,"inputTokens":232070,"model":"gpt-6-astra","outputTokens":17152,"runtime":"codex","turns":7,"wallClockMs":607597}},{"artifacts":[],"attempt":1,"bundleHash":"c4c58e910a01d2f80f3e4946c90dd276fcaf8b9189df7706aac7a59989aaaaca","device":"04f946173ab09bec","findings":[],"hash":"f93e4c1e5178e715c68884cce1022ff062ab1f94494b3252935ed94d96be7a9f","nodeId":"767a3405-03eb-404b-b4a5-7bad97abdbd1","outcome":"completed","summary":"I built the site with npm. There's nothing to install: there's no lockfile and `package.json` has no dependencies. One thing to know before you host it: some images and links won't load when it's served under the IPFS path prefix.\n\n- **Package manager:** npm (11.19.0, running on Node v24.21.0)\n- **Build command:** `npm run build`. This is the repository's own script: it deletes `dist/` and copies `web/` into it, leaving out `web/api` and `web/vercel.json`.\n- **Where the build wrote:** `dist/` at the repository root, which contains `dist/index.html`. Nothing needed copying from anywhere else.\n\n**Checks:**\n- `diff -r web dist` shows only the two files the script is meant to leave out.\n- The output is 8 files and about 776 KB, well under the 8 MiB limit.\n- `.gitignore` doesn't ignore `dist/`, so I left it alone. Nothing outside `dist/` was changed.\n\n**Root-relative paths:** `index.html` and `world/index.html` hard-code paths that start with `/`: the favicons and icons, `/earn-preview.jpg`, and the `/world/` and `/` links. Behind a prefix, those images won't load and those two links point to the wrong place. The script, styles and in-app `#/` routes are inline or hash-based, so the app itself should still work. The build has no base-path setting, and fixing this means editing the site's source, which I'm not allowed to do. I've left it as built.\n\n**Not committed:** `dist/` is staged but has no commit yet, because git has no author identity configured here and I didn't set one. Running `git config user.name …` / `user.email …` and then `git commit` will record it.","treeHash":"2904239fb7ceb8d34f81c57d9e1951b01f488b26","usage":{"cachedInputTokens":98404,"inputTokens":8,"model":"claude-opus-5-5","outputTokens":1442,"runtime":"claude","turns":4,"wallClockMs":32869}}],"verification":[{"checks":[],"detail":"paths and tree verified; no suite was run for this kind of work","evaluation":"structural","profile":"none","status":"accepted","submissionHash":"f93e4c1e5178e715c68884cce1022ff062ab1f94494b3252935ed94d96be7a9f","verifiedTreeHash":"2904239fb7ceb8d34f81c57d9e1951b01f488b26","verifierVersion":"0.1.0+fdeb4d4a"}]}