{"assessments":[],"deployments":[{"attestationHash":"1763450ea7fcba889c46476720ae9fe2e19377ca5ea7807a1195c17d11366f36","chainId":4663,"contracts":[{"address":"0x739fd5b653aa092a434534fa1ade67c1770b5a5b","blockNumber":83752458,"name":"BaskVault","role":"other","txHash":"0xb5d76fd12eda0829ffc4c73b68a390e44945a176e21b8325b436e374b87c8fca"}],"id":"4715417b-5084-4050-8fb7-1d23050581ce","manifestHash":"7f48bf07b695c0f1478a13eba92dc032e9cf603a016f40926fa76cc63c3eee5a","status":"live"}],"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":"4ff210c0-6868-42fb-b90e-23a06e676b0f","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"29b5d32fea3f20cc6eaedced7c52f1b9a14d5b6bd782f67af840199ba660014f","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_economics","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"855973b9e7a4f22c4033d06a87bb0343a4b63a9a38b7ea9f81510f70f95ff766","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_flow","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"08cf290e5f80da59d5f27797432f789f04ea9d4ff3cfd5b6d569c633c67e2a11","dependsOn":["build_contract_project","write_foundry_tests","manifest","audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"3014f1ea5961918ca059453a484bf4c8bcbbfc2248dbe31d94ac7c5cdf8f50bd","skillId":"audit-judge","tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"3014f1ea5961918ca059453a484bf4c8bcbbfc2248dbe31d94ac7c5cdf8f50bd","skillId":"audit-judge","state":"accepted"},{"acceptedSubmissionHash":"0cdf3a6fd138e36c9a35f6b2f2ba7411d10fe437ba46babd5f75ec268338a35c","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_math","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"4f768da41a564baf803f7e12ffbcba788cb53c3c3c018f63822a65811b4651b0","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_permissions","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"cf0a8de87a9e36c7e22eefdda75bb01337ccfb7ce1e7d7699908a99cdaa3cd10","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"587567a5ef71546b5973ceaf6ef1dbbd590f49503b5ac9a6973ed86b89584857","skillId":"build-contract-project","tools":[]},"key":"build_contract_project","kind":"code","role":"implement","skillHash":"587567a5ef71546b5973ceaf6ef1dbbd590f49503b5ac9a6973ed86b89584857","skillId":"build-contract-project","state":"accepted"},{"acceptedSubmissionHash":"2ffaf4ee904c3ec8cf0be1c978b00e4a21c3696c46530a865fd86138eaf7d02f","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"3cab3336f4e2fca9fb171834fd3f8f2a7a532327e2ba29d766c0f901a205e8d2","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","tools":[]},"key":"write_foundry_tests","kind":"code","role":"tests","skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","state":"accepted"}],"objective":"Basket Protocol: an index vault for Stock Tokens on Robinhood Chain. Contracts only: no launch token, pool or website. BASK is the vault's own 18-decimal ERC-20 share. Say \"Stock Tokens\", never \"tokenized\".\nToken name: Basket\nToken symbol: BASK\nTotal supply: 0 at deployment, no cap: deposit mints, redeem burns\n\nBUILD RULES\n- Simplest code that satisfies this text: add no feature, role, setting or safeguard.\n- solc 0.8.26, optimizer on, 200 runs, via_ir, evm cancun, bytecode_hash none; custom errors.\n- If BaskVault exceeds 24,576 bytes of runtime, move views into BaskLens(address vault = $contract:BaskVault); never drop a check.\n- No proxy, delegatecall, selfdestruct, rescue or sweep. User-facing state changes are nonReentrant and emit events.\n\nMANIFEST\n1. BaskVault(address owner_, address guardian_): owner_ = 0x30B57ECf51D19ABcED7F6f70974e6fBb6f3b9Da3, guardian_ = 0x5ed39AF86f2C00ad99913B5d727bD68f2A904B68, written as these literals. Reverts if either is zero or they are equal.\n\nOutside contracts (mocked under test/; no fork tests or vm.env):\n- Token: ERC-20; may have oraclePaused() returns bool.\n- Feed: decimals() <= 18, latestRoundData(); answer = USD per whole token.\n- Pool: Uniswap v3 style token0(), token1(), observe(uint32[]).\n\nASSETS: (token, feed, pool, quoteFeed, minLiquidity, open, retired, hasPause, centre), at most maxAssets. managed[token] is the accounting balance; value never uses balanceOf.\nListing checks (proposal and execution): token not listed; token and feed decimals() <= 18; feed not used by another unretired asset; answer > 0 and under maxAge. At listing: open, centre = answer, hasPause = oraclePaused() returns a bool.\nGenesis: until finalizeGenesis() (once, needs 3+ assets), the owner lists assets with their pools at once; deposits open at finalize.\nAnyone may remove a retired asset with managed and totalOwed 0; its token may then be relisted.\n\nPROPOSALS (owner): list an asset with its pool; new feed (centre = new answer); re-centre on the answer at execution, under maxAge; reopen (cancelled by any later close); retire (closed both times); set an asset's pool, quoteFeed and minLiquidity, or none; resync (managed += balanceOf - managed - totalOwed, if above 0); new guardian; raise NAV_CAP; set feeRecipient (not zero or the vault); change a setting within its bounds. A proposal waits 2 days, then only the owner executes it; it lapses 7 days later; the owner or guardian may cancel it, the guardian not its own replacement. A retired asset is closed for good, voids its pending proposals, then takes only resync, is 0 in NAV and skipped by every deposit check even with managed > 0.\n\nSETTINGS (start; bounds): band 4 (2-100); maxAge 80 hours, noPoolAge 26 hours (1 hour-30 days); freshCount 1 (0-10), freshHours 1 (1-48); hours 72000-504000 (seconds since Sunday 00:00 New York time; from < to <= 604800, or 0-0 = always open); dst 0 (daylight: 0 US rule, 1 never, 2 always; test 2026-2040); poolWindow 1800 s (300-86400); poolDeviation 300 bps (50-2000); feedGas, pauseGas 100,000, poolGas 150,000, balanceGas 50,000, payGas 250,000 (each 20,000-500,000); maxAssets 250; directLimit 50. A change must keep maxAssets >= asset count, maxAssets * (balanceGas + 60,000) <= 28,000,000 and directLimit * (balanceGas + payGas + 70,000) <= 28,000,000.\n\nPRICE is valid only if the feed read succeeds (feedGas), answer > 0, centre / band <= answer <= centre * band, updatedAt <= now, now - updatedAt <= maxAge, oraclePaused() returns false if hasPause (pauseGas), and: with a pool, its observe over poolWindow (poolGas; Uniswap OracleLibrary.consult) succeeds with mean liquidity >= minLiquidity and its mean-tick price per whole token in whole quote tokens, times the quoteFeed price (> 0, under maxAge), is within poolDeviation of the feed price in USD; with no pool, now - updatedAt <= noPoolAge. value(amount) = amount * answer * 1e18 / 10^(token decimals + feed decimals), rounded down. USD amounts below are dollars times 1e18.\n\ndeposit(tokens[], amounts[], receiver, minSharesOut, deadline) requires:\n- deposits open, not paused, inside hours; each token listed, open, once, amount > 0, vault balance >= totalOwed[token]; receiver not the vault;\n- freshCount or more unretired assets updated within freshHours;\n- a valid price for each token and every unretired asset with managed > 0; no unretired asset short or unreadable.\nPull the tokens; each vault balance must rise by exactly its amount. NAV = sum of value(managed) before; v = sum of value(amounts).\ngross = v if totalSupply is 0, else v * totalSupply / NAV rounded down (revert if NAV is 0). fee = gross * 50 / 10000, rounded up, minted to feeRecipient; no fee while unset. The receiver gets gross - fee; on the first deposit 1e15 of that goes to address(0xdEaD) instead. It must be > 0 and >= minSharesOut. NAV + v <= NAV_CAP (starts 1,000,000; lowering cancels pending raises; never above 10,000,000,000).\n\nredeem(shares, receiver, minAmountsOut[], deadline), receiver not zero, reads no price, ignores every pause and never reverts because of an asset. fee = shares * 50 / 10000 rounded up, transferred to feeRecipient; no fee while unset. net = shares - fee is burned. Per asset with managed > 0: available = balanceOf(vault) - totalOwed[token], floor 0 (balanceGas static call, 32 bytes copied; else unreadable: available = managed); leg = min(managed, available) * net / totalSupplyBeforeBurn, rounded down; require leg >= minAmountsOut[i] (missing entry = 0); managed -= leg. If at most directLimit assets have managed > 0, each leg is paid to receiver by an external function only the vault may call, given payGas, reverting unless the transfer succeeds, returns nothing or true, and the vault balance falls by exactly leg; if that fails, or above directLimit, owed[receiver][token] and totalOwed[token] grow by leg. claim(tokens[], to), to not zero, pays each min(caller's owed, vault balance) by the same function with no gas limit.\n\nLOSSES. An asset is short when available < managed. Nothing lowers managed automatically. flagDeficit(token), by anyone, records shortfall and time if larger than recorded, or clears the record if not short. recognizeLoss(token), by anyone 7 days or more later, lowers managed by min(recorded, current shortfall) and clears the record. A deposit clears unretired records.\n\nRoles:\n- Owner (two-step transfer, never to the guardian, no renounce): genesis, proposals, cancel; at once: close an asset to deposits, pause and unpause deposits, lower NAV_CAP.\n- Guardian: pause deposits, close an asset, cancel as stated.\n- Nobody can move assets, block redeem or claim, mint outside deposit, change a fee or upgrade.\n\nVIEWS: all assets (config, answer, updatedAt, pool price, managed, short, totalOwed); settings; previewDeposit; previewRedeem; depositStatus(tokens[]): reason and asset at fault; pending proposals.\n\nREVIEW. Accepted, add no mechanism: (1) profit from feed lag under poolDeviation; (2) the owner pairs each token with its true feed and pool; (3) no per-asset limit; (4) moving a thin pool can stop deposits; (5) depositors after a retire share its tokens. MUST ATTACK: redeem with paused, blocked or upgraded tokens (maxAssets assets in any state: under 28,000,000 gas); any way a role or a setting blocks redeem.","parentJobId":null,"planHash":"cbc093bec13cdef9922c8c9aeac9f61e6b3091f936eefb52933be205b113a1c3","previousHash":"e33370664c39bda6ff2e3468b1dbd63ad6731d60f61a04185df293c1d508a3f7","projectId":"4ff210c0-6868-42fb-b90e-23a06e676b0f","publication":{"commit":"50acd7248c2ce59907a963a648115900d629f352","deliveredAt":"2026-10-09T00:55:47.167Z","repoUrl":"https://github.com/identity-md-launches/launch-1110-basket"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[],"site":null,"snapshotHash":"58846308d8b3db27dd399c15d2a188515213397f4864966b0fdf6be7ee073e2d","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"87804e27e9c9f85a","findings":[{"citation":"resolved","description":"deposit rejects receiver == address(this) (line 625) but redeem only rejects address(0). With receiver == address(this) each direct leg goes through pay(token, vault, leg): the token performs a self-transfer, the vault balance does not fall, the exact-debit check at line 741 reverts, _tryPay returns false and lines 724-725 credit owed[address(this)][token] and totalOwed[token]. claim is keyed on msg.sender and the vault never calls claim, so that debt is unclearable. Consequences: the redeemer's tokens are stranded inside the vault (self-harm), those tokens are excluded from available for every later redeem, deposit health check and Resync (line 794), and removeRetired at line 368 requires totalOwed[token] == 0, so any share holder can, with a dust redemption, make a Stock Token permanently unremovable and therefore never relistable. Any holder of dust shares can do this; no role is needed. Merged from three specialist reports (flow, permissions, math) that describe the same root cause. Minimal fix within the brief's wording for deposit: treat receiver == address(this) like address(0) in redeem.","line":688,"path":"src/BaskVault.sol","reproduction":"Default fixture (3 genesis assets, $100 feeds, hours set to always open). alice deposits 10e18 stock0, then calls redeem(1000, address(vault), [], now). Expected: either revert like deposit does for the vault receiver, or a delivered leg. Actual (test/scratch/Probe.t.sol::testRedeemToVault): legs[0] == 10 wei, owed(vault, stock0) == 10, totalOwed(stock0) == 10, no Paid event. alice then redeems the rest, the issuer confiscates the whole vault balance, flagDeficit + 7 days + recognizeLoss bring managed[stock0] to 0, owner closes and retires stock0: removeRetired(stock0) reverts InvalidAsset because totalOwed[stock0] == 10 and no call can lower it.","severity":"low","snippet":"        if (receiver == address(0)) revert InvalidAddress();","title":"redeem accepts the vault itself as receiver; the queued leg becomes owed[vault][token] that nothing can claim, so totalOwed[token] stays nonzero forever and removeRetired(token) can never succeed"},{"citation":"resolved","description":"Every redemption removes leg = min(managed, available) * net / totalSupplyBeforeBurn (line 713) with net < totalSupply, because the first deposit mints 1e15 shares to address(0xdEaD) (line 659) that nobody can burn. After every holder exits, managed[token] is left at ceil(managed * 1e15 / supply) > 0. recognizeLoss only lowers managed by an actual shortfall (lines 832-834) and a healthy token is never short because its balance equals managed; Resync only raises managed; nothing else writes managed. So for a normal Stock Token the condition managed == 0 at line 368 can only be met by an asset that never received a deposit or whose custody was confiscated and recognised. The retire -> remove -> relist lifecycle the brief promises is unreachable in practice, every retired asset keeps its slot against maxAssets, keeps counting in managedAssetCount against directLimit, and keeps costing a balance read in every redeem. The implementation matches the brief's literal condition; the brief's lifecycle is what does not hold, so this is a scope decision for the author.","line":368,"path":"src/BaskVault.sol","reproduction":"Default fixture, hours always open. alice deposits 10e18 stock0 (totalSupply 1000e18, 1e15 at 0xdEaD). alice redeems her whole balance (1000e18 - 1e15) to herself. Expected per the brief: once every holder has exited the owner can close and retire stock0 and anyone can removeRetired(stock0). Actual (test/scratch/Probe.t.sol::testDeadShareDustBlocksRemoval): managed[stock0] == 1e13, vault balance == 1e13, flagDeficit records nothing (amount 0), close + Retire + execute succeed, removeRetired(stock0) reverts InvalidAsset. No owner or public call lowers managed for a healthy token.","severity":"low","snippet":"        if (!a.retired || managed[token] != 0 || totalOwed[token] != 0) revert InvalidAsset();","title":"removeRetired is unreachable for any asset that ever received a deposit: the permanent 1e15 dead shares leave managed dust that no path can zero"},{"citation":"resolved","description":"redeem compares each leg to minAmountsOut[i] by position in tokens. removeRetired, callable by anyone the moment a retired asset has managed == 0 and totalOwed == 0, swaps the last asset into the removed slot (line 375) and pops. A redeem built from previewRedeem or assetTokens before that call executes with its minimum for the moved asset applied to the removed slot's old index (where the caller put 0 for the empty retired asset) and its real minimum beyond the new length and ignored. The only slippage guard redeem offers is defeated by a public call; harm needs the moved asset's leg to actually fall before the redeem lands (a shortfall or a recognizeLoss on it, which anyone may trigger in the same block once a record is 7 days old). In the opposite direction the shift makes the redeem revert, a one-transaction DoS. Merged from the permissions and economics reports. Its precondition is narrow because of finding 2 (removeRetired needs managed == 0). A fix that keeps the brief's positional interface could reject a nonzero-length minAmountsOut whose length differs from tokens.length; the brief's 'missing entry = 0' rule would then need a scope decision.","line":715,"path":"src/BaskVault.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {BaskVault} from \"src/BaskVault.sol\";\n\ncontract ShiftToken {\n    uint8 public constant decimals = 18;\n    mapping(address => uint256) public balanceOf;\n    mapping(address => mapping(address => uint256)) public allowance;\n\n    function mint(address to, uint256 amount) external {\n        balanceOf[to] += amount;\n    }\n\n    function confiscate(address from, uint256 amount) external {\n        balanceOf[from] -= amount;\n    }\n\n    function approve(address spender, uint256 amount) external returns (bool) {\n        allowance[msg.sender][spender] = amount;\n        return true;\n    }\n\n    function transfer(address to, uint256 amount) external returns (bool) {\n        balanceOf[msg.sender] -= amount;\n        balanceOf[to] += amount;\n        return true;\n    }\n\n    function transferFrom(address from, address to, uint256 amount) external returns (bool) {\n        allowance[from][msg.sender] -= amount;\n        balanceOf[from] -= amount;\n        balanceOf[to] += amount;\n        return true;\n    }\n}\n\ncontract ShiftFeed {\n    uint8 public constant decimals = 8;\n    int256 public answer = 100e8;\n    uint256 public updatedAt;\n\n    constructor() {\n        updatedAt = block.timestamp;\n    }\n\n    function refresh() external {\n        updatedAt = block.timestamp;\n    }\n\n    function latestRoundData() external view returns (uint80, int256, uint256, uint256, uint80) {\n        return (1, answer, updatedAt, updatedAt, 1);\n    }\n}\n\n/// A redeem's minAmountsOut is positional. removeRetired is permissionless and\n/// moves the last asset into the removed slot, so a minimum built for the old\n/// order is applied to the wrong asset and the real minimum is dropped.\ncontract MinOutShiftProof is Test {\n    address internal constant OWNER = 0x30B57ECf51D19ABcED7F6f70974e6fBb6f3b9Da3;\n    address internal constant GUARDIAN = 0x5ed39AF86f2C00ad99913B5d727bD68f2A904B68;\n    BaskVault internal vault;\n    ShiftToken[3] internal stock;\n    ShiftFeed[3] internal feed;\n    address internal alice = address(0xA11CE);\n    address internal mallory = address(0xBAD);\n\n    function setUp() public {\n        vm.warp(1_789_992_000); // Monday 2026-09-21 12:00 UTC, inside trading hours.\n        vault = new BaskVault(OWNER, GUARDIAN);\n        for (uint256 i; i < 3; ++i) {\n            stock[i] = new ShiftToken();\n            feed[i] = new ShiftFeed();\n            vm.prank(OWNER);\n            vault.listGenesis(address(stock[i]), address(feed[i]), address(0), address(0), 0);\n            stock[i].mint(alice, 100e18);\n            vm.prank(alice);\n            stock[i].approve(address(vault), type(uint256).max);\n        }\n        vm.prank(OWNER);\n        vault.finalizeGenesis();\n    }\n\n    function _deposit(uint256 i, uint256 amount) internal {\n        address[] memory ts = new address[](1);\n        ts[0] = address(stock[i]);\n        uint256[] memory amounts = new uint256[](1);\n        amounts[0] = amount;\n        vm.prank(alice);\n        vault.deposit(ts, amounts, alice, 0, block.timestamp);\n    }\n\n    function _refresh() internal {\n        for (uint256 i; i < 3; ++i) {\n            feed[i].refresh();\n        }\n    }\n\n    function testMinimumForMovedAssetIsNotEnforcedAfterRemoveRetired() public {\n        _deposit(0, 10e18);\n        _deposit(2, 10e18);\n        // Retire the empty middle asset.\n        vm.prank(OWNER);\n        vault.close(address(stock[1]));\n        BaskVault.Action memory a;\n        a.kind = BaskVault.Kind.Retire;\n        a.token = address(stock[1]);\n        vm.prank(OWNER);\n        uint256 id = vault.propose(a);\n        vm.warp(block.timestamp + 2 days);\n        _refresh();\n        vm.prank(OWNER);\n        vault.execute(id);\n\n        // Alice builds minima from the preview in the current order: [2.5, 0, 2.5].\n        (uint256[] memory minima,,) = vault.previewRedeem(500e18);\n        assertEq(minima.length, 3);\n        assertEq(minima[0], 25e17);\n        assertEq(minima[2], 25e17);\n\n        // Anyone reorders the list before Alice's redeem lands; stock2 is now index 1.\n        vm.prank(mallory);\n        vault.removeRetired(address(stock[1]));\n        assertEq(vault.assetTokens()[1], address(stock[2]));\n        // stock2 becomes short, so its leg is 0.75e18, below Alice's 2.5e18 minimum.\n        stock[2].confiscate(address(vault), 7e18);\n\n        // Expected: the redeem is rejected because stock2's leg is under its minimum.\n        vm.prank(alice);\n        vm.expectRevert();\n        vault.redeem(500e18, alice, minima, block.timestamp);\n    }\n}","reproduction":"tokens = [stock0, stock1, stock2]; alice deposited 10e18 stock0 and 10e18 stock2 ($100 each, supply 2000e18). stock1 is closed and retired with managed 0 and totalOwed 0. alice calls previewRedeem(500e18) -> [2.5e18, 0, 2.5e18] and submits redeem(500e18, alice, [2.5e18, 0, 2.5e18], now). Before it lands anyone calls removeRetired(stock1) (tokens = [stock0, stock2]) and stock2 loses 7e18 of the vault's 10e18, so its leg is min(3e18,10e18)*500e18/2000e18 = 0.75e18. Expected: revert Slippage because stock2's leg 0.75e18 < her 2.5e18 minimum. Actual: the call succeeds with amounts = [2.5e18, 0.75e18]; index 1 compares stock2 against 0 and index 2 is never read. The attached proof (the economics specialist's, re-run here) fails on this tree with 'next call did not revert as expected'.","severity":"low","snippet":"            if (i < minAmountsOut.length && leg < minAmountsOut[i]) revert Slippage();","title":"Permissionless removeRetired reorders the asset list, so a redeem built with positional minAmountsOut applies its minima to the wrong assets and the moved asset's minimum is dropped"},{"citation":"resolved","description":"NAV for deposits is the sum of value(managed), so tokens held beyond managed (custody returned after a recognizeLoss, an airdrop, a split, a donation) are worth 0 to NAV until the owner executes a Resync, which adds the whole surplus in one step. The proposal is public (Proposed event, pendingProposals) and executable only 2 to 9 days after it is proposed, so the step-up is announced in advance. Any depositor who enters at the pre-resync NAV and redeems after execution takes a pro-rata slice of the surplus, and redeem pays pro rata of managed with no price so the gain is realised at once; existing holders are diluted by that amount. The 0.5% deposit and redeem fees (only if a fee recipient is set) are the whole cost. This is the ordinary cost of a timelocked, publicly announced accounting step and the brief specifies Resync exactly this way, so it is reported as a documented economic property rather than a guard defect: the owner or guardian should pauseDeposits() from proposal until the Resync has executed. Making execution of Kind.Resync require depositsPaused would be a new safeguard and is a scope decision. Merged from the economics (medium) and permissions (low) reports; severity set to low because the mitigation exists inside the agreed design and no guard is bypassed.","line":442,"path":"src/BaskVault.sol","reproduction":"Hours always open. alice deposits 5000e18 stock0 at $100 (NAV $500k, supply 500000e18). 5000e18 stock0 arrives in the vault outside deposit (mint to the vault in the mock). Owner proposes Kind.Resync for stock0. bob deposits 5000e18 stock1 at $100 and receives 500000e18 shares (half the supply) because NAV still ignores the surplus. After 2 days the owner executes: managed[stock0] = 10000e18, NAV $1.5M. bob redeems all his shares. Expected under non-racing accounting: bob can take out $500k plus price moves; the surplus belongs to alice. Actual (test/scratch/Probe.t.sol::testResyncFrontRun): bob receives 5000e18 stock0 + 2500e18 stock1 = $750,000; alice's claim falls from $1,000,000 to $750,000.","severity":"low","snippet":"                uint256 extra = available > managed[a.token] ? available - managed[a.token] : 0;\n                _setManaged(a.token, managed[a.token] + extra);","title":"Resync is a public, timelocked NAV step-up; a deposit made between proposal and execution captures a pro-rata share of the surplus from existing holders"},{"citation":"resolved","description":"When totalSupply is 0 the share formula uses gross = value regardless of nav. totalSupply is 0 only before the first deposit, but managed can already be nonzero then: after listing, tokens sent to the vault can be absorbed by an owner Kind.Resync. The first depositor then receives shares equal only to their own value yet, through redeem's pro rata of managed, owns the whole pre-existing balance. The brief's text ('gross = v if totalSupply is 0') describes this formula, so the defect is only the combination with Resync being executable at supply 0; whoever sent the tokens (an owner seed, a mistaken transfer) loses them to the first depositor. Owner action is required to reach the state, so severity is low. A minimal fix is to refuse Resync while totalSupply == 0; that is an added check and a scope decision.","line":668,"path":"src/BaskVault.sol","reproduction":"Genesis finalised, no deposits, hours always open. 1000e18 stock0 is transferred to the vault; the owner proposes and after 2 days executes Kind.Resync for stock0, so managed[stock0] == 1000e18 and totalSupply == 0. bob deposits 1e18 stock1 ($100): _depositShares(100e18, nav=100000e18) with supply 0 returns gross 100e18, shares 100e18 - 1e15. Expected: a $100 deposit into a vault holding $100,000 should not entitle the depositor to the holdings. Actual (test/scratch/Probe.t.sol::testFirstDepositAfterResync): previewRedeem(bob's shares) returns 999.99e18 stock0 for bob.","severity":"low","snippet":"        uint256 gross = supply == 0 ? value : FullMath.mulDiv(value, supply, nav);","title":"First deposit mints shares equal to its own value and ignores NAV already held through a pre-deposit Resync, handing the existing custody to the first depositor"},{"citation":"resolved","description":"Every deposit reads the balance of every unretired asset, prices every asset with managed > 0 (feed call, pause call, pool observe and quote feed when a pool is set) in _depositState, then runs a second loop over all tokens that reads assets[token].retired and deletes two deficit slots per asset: about 8.6k gas per asset cold, 2.2M at 250 assets, 3M at the 350 maximum. With MockFeed/MockPool a cold single-token deposit costs 12,337,284 gas at 250 pool-less assets and 19,281,131 with pools. Real Chainlink-style proxy + aggregator reads and Uniswap v3 observe calls cost several times the mocks, so at the configured maximum a deposit is expected to approach or exceed a 28-32M block limit; deposits would then stop for everyone while redeem keeps working. The per-deposit validation of every held price is what the brief requires, so the operational conclusion is that the practical asset count must stay well below maxAssets; the second loop can be made constant-gas without changing semantics (a deficit epoch bumped on deposit and compared in flagDeficit/recognizeLoss), which recovers about 2.2M at 250 assets.","line":656,"path":"src/BaskVault.sol","reproduction":"test/scratch/Probe.t.sol::DepositGasProbe: list 250 assets (MockToken 18 dec, MockFeed 8 dec; optionally a MockPool against a 6-decimal quote token and quote feed), deposit 1e18 of every token once, cool every address, then measure vault.deposit([token0],[1e18],alice,0,now). Logged: 'single deposit gas at 250 assets: 12337284' without pools and '19281131' with pools. Expected: a deposit should stay comfortably inside the chain's block gas limit at any allowed asset count. Actual: already 12-19M with free mocks; substituting real oracle costs puts the same call above 28M.","severity":"low","snippet":"        for (uint256 i; i < tokens.length; ++i) {\n            if (!assets[tokens[i]].retired) delete deficits[tokens[i]];\n        }","title":"Deposit gas grows with every unretired asset; a single-token deposit measures 12.3M gas (19.3M with pools) at the default 250 assets even with free mocks, and the deficit-clearing loop alone costs abo"},{"citation":"resolved","description":"NAV excludes retired assets and deposits price shares as value * supply / NAV with 'revert if NAV is 0', both as the brief specifies. Once the owner retires the last asset with managed > 0 (an issuer migration where the old token is retired before the new one receives a deposit, for example) supply stays > 0 forever because of the address(0xdEaD) lock and nav == 0 for every unretired asset, so deposit reverts ZeroNAV for good. The only way out is a donated balance plus a timelocked Resync into an unretired asset, after which the next depositor receives value * supply / tinyNAV shares. This is the behaviour the brief specifies (accepted item 5 covers the sharing of retired tokens), so it is recorded for the author's awareness only; the operational rule is to list and fund a replacement asset before retiring the last funded one.","line":667,"path":"src/BaskVault.sol","reproduction":"Default fixture, hours always open. alice deposits 100e18 stock0. Guardian close(stock0); owner proposes Retire(stock0), 2 days pass, execute. bob calls deposit([stock1],[1e18], bob, 0, now). Expected per the brief: revert ZeroNAV. Actual (test/scratch/Probe2.t.sol::testZeroNavLock): revert ZeroNAV; no sequence of calls other than an owner Resync of donated tokens makes deposit succeed again.","severity":"info","snippet":"        if (supply != 0 && nav == 0) revert ZeroNAV();","title":"Retiring the last asset that holds a managed balance locks deposits permanently (ZeroNAV), because the 1e15 dead shares keep totalSupply above zero"}],"hash":"08cf290e5f80da59d5f27797432f789f04ea9d4ff3cfd5b6d569c633c67e2a11","nodeId":"aeb4aa37-df26-4ca6-b279-ea76ef8aef41","outcome":"completed","summary":"The review is complete. The findings file is written at `.imd-findings.json` with 7 findings and 24 coverage rows (all 21 entry points plus three invariants).\n\n**Outcome.** No critical, high or medium defects. The contract implements the brief faithfully: redeem reads no price, ignores every pause, survives 350 assets in any failure state under 28M gas, and no role or setting can block redeem or claim. Every specialist lead reproduced against the code, and I merged the three duplicate reports of the vault-receiver issue into one.\n\n**Findings kept (all low, one info):**\n1. `redeem` accepts the vault as receiver. The leg lands in `owed[vault][token]`, which nothing can claim, so `totalOwed` stays nonzero and `removeRetired` is blocked forever. Reproduced with a 1000-share redeem leaving a 10 wei stranded leg.\n2. `removeRetired` is unreachable for any asset that ever received a deposit. The 1e15 dead shares leave managed dust after a full exit, and no path lowers managed on a healthy token.\n3. Public `removeRetired` reorders the asset list, so positional `minAmountsOut` apply to the wrong assets. The economics specialist's proof fails on this tree for the stated reason, and it is attached.\n4. Resync is a publicly announced NAV step-up; a deposit in the two-day window captures surplus from existing holders. Reproduced at $250k transferred. Rated low because pausing deposits around execution is available inside the design and any code fix is a scope decision.\n5. A Resync executed before the first deposit hands the pre-existing custody to the first depositor.\n6. Deposit gas scales with every asset: 12.3M at 250 assets without pools, 19.3M with pools, even with free mocks. The deficit-clearing loop alone costs about 2.2M per deposit.\n7. Info: retiring the last funded asset locks deposits in ZeroNAV, as the brief specifies.\n\n**Verified clean on my own pass:** Transfer topic assembly, opcode scan and 23,913-byte runtime, two-step ownership against guardian swaps, zero-address `transferFrom` cannot mint, pool quote arithmetic matches Uniswap's OracleLibrary, New York DST arithmetic, settings bounds, and proposal epoch voiding. Scratch tests are under `test/scratch/` and nothing else in the tree was changed.","treeHash":null,"usage":{"cachedInputTokens":1303662,"inputTokens":354,"model":"claude-fable-5-1","outputTokens":56946,"runtime":"claude","turns":25,"wallClockMs":930298}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"1731fbfe0c4574fb","findings":[{"citation":"resolved","description":"Boundary (sentinel receiver). deposit() rejects receiver == address(this) (line 625) but redeem() only rejects address(0). With receiver == address(this) every direct leg goes through pay(token, vault, leg): the token moves vault->vault, the balance does not change, so `beforeBalance - afterBalance != amount` (line 741) reverts and the leg is written to owed[address(this)][token] and totalOwed[token] (lines 724-725). The vault cannot call claim() on itself, so that debt is never cleared: totalOwed[token] stays nonzero for good, the tokens are excluded from `available` in every later redeem, deposit health check and resync, and removeRetired(token) (line 368, requires totalOwed == 0) reverts forever, so the token can never be relisted and its slot is consumed against maxAssets. The redeemer burns shares for nothing (self-harm); no other holder loses because managed is reduced by the same leg. Any share holder can do this with a dust redemption, so it is a cheap, irreversible griefing of the remove/relist path for every held asset (a holder who redeems while a token is blocked and never claims achieves the same, so the design already tolerates unclaimed debt; this variant is merely deterministic and unrecoverable). Minimal fix within the brief: treat receiver == address(this) like address(0) in redeem (the brief's 'receiver not the vault' already exists for deposit).","line":688,"path":"src/BaskVault.sol","reproduction":"Default fixture (3 assets, hours always open). alice deposits 1000e18 of stock0 at $100 (feed 8 dec). alice calls redeem(1000, address(vault), [], now). Expected: either revert, or the 1000-share leg is delivered. Actual: with fee recipient set, supply = 1e23, fee = 5, net = 995, legs[0] = floor(1000e18 * 995 / 1e23) = 9 wei; owed(vault, stock0) == 9, totalOwed(stock0) == legs[0], stock0.balanceOf(vault) still 1000e18, managed(stock0) == 1000e18 - legs[0]. Afterwards close+retire stock0, redeem everything else: removeRetired(stock0) reverts InvalidAsset for ever because totalOwed != 0. Verified in test/scratch/MathProbe.t.sol::testRedeemToVaultLocksLegInTotalOwed on this tree.","severity":"low","snippet":"        if (receiver == address(0)) revert InvalidAddress();","title":"redeem accepts the vault itself as receiver; the leg is queued to owed[vault][token] which nothing can claim, so totalOwed[token] is inflated permanently and removeRetired(token) can never succeed"},{"citation":"resolved","description":"Numerical seam (boundary x invariant): NAV excludes retired assets (spec) and deposits price shares as value * supply / NAV (spec), so the moment the owner retires the last asset with managed > 0 (for example after an issuer migration, listing the new token and retiring the old) the vault can never accept a deposit again: supply > 0 forever because of the address(0xdEaD) lock, and nav == 0 for every unretired asset. The only way out is an owner-donated balance plus a timelocked Resync into a new asset, after which the first depositor receives value * supply / tinyNAV shares, i.e. nearly all of the retired custody. This is exactly the behaviour the brief specifies ('revert if NAV is 0', accepted tradeoff 5), so it is recorded for the author's awareness only and no change is proposed.","line":667,"path":"src/BaskVault.sol","reproduction":"Default fixture. alice deposits 1000e18 stock0. Guardian close(stock0); owner propose(Retire stock0), warp 2 days, execute. Now bob deposit([stock1],[1e6], bob, 0, now). Expected per spec: revert ZeroNAV. Actual: revert ZeroNAV (verified in test/scratch/MathProbe.t.sol::testAllRetiredLocksDeposits). No sequence of calls makes deposit succeed again without an owner Resync of donated tokens.","severity":"info","snippet":"        if (supply != 0 && nav == 0) revert ZeroNAV();","title":"Once every asset holding a managed balance is retired (or written off), NAV == 0 with supply > 0 makes deposit() revert ZeroNAV permanently; the 1e15 dead shares guarantee supply never returns to zero"}],"hash":"0cdf3a6fd138e36c9a35f6b2f2ba7411d10fe437ba46babd5f75ec268338a35c","nodeId":"0cdc40f8-4814-479d-a7ad-bf1a92256bdc","outcome":"completed","summary":"The review is complete. The findings file is at the repository root and validated; no source or config files were touched (only `.imd-findings.json` and my scratch test under `test/scratch/`).\n\n**Outcome.** The vault's arithmetic is sound in the three assigned areas. I traced every formula against the brief and found no value-leaking rounding, overflow, decimal or boundary defect. Two items are recorded:\n\n- **Low, `redeem` with the vault as receiver.** Deposit rejects the vault as receiver but redeem does not. The self-payment deterministically fails the exact-debit check, so the leg is queued to the vault's own owed entry, which nothing can ever claim. That leaves `totalOwed[token]` nonzero for good, so `removeRetired` can never succeed for that token and the tokens are excluded from `available` permanently. Only the redeemer loses value, but any dust holder can trigger it for every asset. Reproduced in a scratch test: leg of exactly 9 wei queued, balance unchanged, managed reduced.\n- **Info, permanent ZeroNAV lockout.** Once every asset holding managed balance is retired, NAV is zero while supply can never return to zero because of the dead shares, so deposits revert forever. This is exactly what the brief specifies, so it is noted without a proposed change.\n\n**What I verified in depth.** Value scaling for both exponent branches; gross, fee-ceiling, dead-share and NAV-cap math; the overflow-free band test; the Uniswap port in `PoolOracle` including offset layout, negative-tick floor and harmonic liquidity; the New York calendar arithmetic; every settings bound and both gas-constraint inequalities against measured per-asset costs; the bitmap and swap-and-pop index arithmetic. A 3000-run fuzz confirmed the share price never drops for remaining holders across deposits, partial redeems and fee minting with mixed 6/18-decimal tokens and 8/18-decimal feeds.\n\n**Static-analysis leads** were all false positives: the fee modulo is a ceiling, the assembly shift order is correct (the emitted Transfer topic is standard and tested), the FullMath caret is the standard inverse seed, and the strict equalities are sentinel checks.\n\n**Coverage.** All 21 entry points have rows, plus six invariant rows. Nothing was left unreached. The remaining limit is that gas figures were only reproduced against the repository's mock tokens, not real issuer implementations.","treeHash":null,"usage":{"cachedInputTokens":3859093,"inputTokens":1026,"model":"claude-fable-5-1","outputTokens":63316,"runtime":"claude","turns":33,"wallClockMs":959850}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"27168b1ff68f84d2","findings":[{"citation":"resolved","description":"NAV for deposits is the sum of value(managed). Tokens the vault holds beyond managed (custody returned after a recognizeLoss, a split/rebase crediting the vault, an airdrop, a donation) are worth 0 to NAV until the owner executes a Resync, which adds the whole surplus to managed in one step. The Resync proposal is public and executable only 2 to 9 days after it is proposed, so the step-up is announced in advance. Any depositor who enters at the pre-resync NAV and redeems after execution receives a pro rata slice of the surplus; redeem pays pro rata of managed with no price, so the gain is realised immediately. Existing holders, who owned the surplus, are diluted by exactly that amount. The 0.5% deposit and redeem fees (only if a fee recipient is set) are the attacker's whole cost. This is the mirror of accepted item 5 but is not listed among the accepted tradeoffs. The same class applies to a Feed/Recentre execution whose new answer is higher than the one deposits are currently priced at. Mitigation inside the existing design: the owner or guardian should pauseDeposits() from proposal until the Resync has executed, or execute the Resync only while deposits are paused; alternatively require depositsPaused for Kind.Resync execution. Concrete economics are in the reproduction (attacker in $500k, out $750k, honest holder loses $250k).","line":439,"path":"src/BaskVault.sol","reproduction":"State: hours set to always open; alice deposits 5000 stock0 at $100 (NAV $500k, supply 500000e18). The vault then receives 5000 more stock0 outside deposit (stock0.mint(vault, 5000e18) in the mock). Owner proposes Kind.Resync for stock0. Mallory deposits 5000 stock1 at $100 ($500k) and receives 500000e18 shares (NAV still $500k, so 1:1). After 2 days the owner executes the Resync: managed[stock0] becomes 10000e18 and NAV becomes $1.5M. Mallory redeems 500000e18 shares (50% of supply): legs are 5000 stock0 + 2500 stock1 = $750,000. Expected: Mallory can take out at most what she put in ($500,000) plus any price move; the $500k surplus belonged to alice. Actual: Mallory extracts $750,000 and alice's remaining claim is $749,999,998.5e12 wei of value instead of $1,000,000. Reproduced in test/scratch/Econ.t.sol::testResyncSurplusCapturedByFrontRunningDepositor (passes on the current code, printing the values).","severity":"medium","snippet":"            } else if (a.kind == Kind.Resync) {\n                (bool ok, uint256 available) = _available(a.token);\n                if (!ok) revert BalanceUnreadable(a.token);\n                uint256 extra = available > managed[a.token] ? available - managed[a.token] : 0;\n                _setManaged(a.token, managed[a.token] + extra);","title":"Resync is a public, timelocked NAV step-up that a depositor can front-run to take existing holders' surplus"},{"citation":"resolved","description":"redeem compares each leg to minAmountsOut[i] by position in `tokens`. removeRetired, callable by anyone, swaps the last asset into the removed slot and pops. A redeem built from previewRedeem (or allAssets) before the removal lands has its minimum for the moved asset applied to the removed slot's old index (where the caller put 0 for the empty retired asset) and its real minimum is beyond the new length and ignored. The slippage guard the spec requires (\"require leg >= minAmountsOut[i]\") is silently dropped for the moved asset. Harm needs the moved asset's leg to actually fall before the redeem lands (a shortfall or a recognizeLoss on it), so it is a narrow but concrete loss of a stated guarantee. Fix options that keep the spec: reject a minAmountsOut array whose length is nonzero and differs from tokens.length, or avoid reordering by filling the removed slot with the last element only when the caller-supplied minima cannot be stale (e.g. keep order and compact lazily). A redeem built with a full-length array would then revert instead of running with misaligned minima.","line":375,"path":"src/BaskVault.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {BaskVault} from \"src/BaskVault.sol\";\n\ncontract ShiftToken {\n    uint8 public constant decimals = 18;\n    mapping(address => uint256) public balanceOf;\n    mapping(address => mapping(address => uint256)) public allowance;\n\n    function mint(address to, uint256 amount) external {\n        balanceOf[to] += amount;\n    }\n\n    function confiscate(address from, uint256 amount) external {\n        balanceOf[from] -= amount;\n    }\n\n    function approve(address spender, uint256 amount) external returns (bool) {\n        allowance[msg.sender][spender] = amount;\n        return true;\n    }\n\n    function transfer(address to, uint256 amount) external returns (bool) {\n        balanceOf[msg.sender] -= amount;\n        balanceOf[to] += amount;\n        return true;\n    }\n\n    function transferFrom(address from, address to, uint256 amount) external returns (bool) {\n        allowance[from][msg.sender] -= amount;\n        balanceOf[from] -= amount;\n        balanceOf[to] += amount;\n        return true;\n    }\n}\n\ncontract ShiftFeed {\n    uint8 public constant decimals = 8;\n    int256 public answer = 100e8;\n    uint256 public updatedAt;\n\n    constructor() {\n        updatedAt = block.timestamp;\n    }\n\n    function refresh() external {\n        updatedAt = block.timestamp;\n    }\n\n    function latestRoundData() external view returns (uint80, int256, uint256, uint256, uint80) {\n        return (1, answer, updatedAt, updatedAt, 1);\n    }\n}\n\n/// A redeem's minAmountsOut is positional. removeRetired is permissionless and\n/// moves the last asset into the removed slot, so a minimum built for the old\n/// order is applied to the wrong asset and the real minimum is dropped.\ncontract MinOutShiftProof is Test {\n    address internal constant OWNER = 0x30B57ECf51D19ABcED7F6f70974e6fBb6f3b9Da3;\n    address internal constant GUARDIAN = 0x5ed39AF86f2C00ad99913B5d727bD68f2A904B68;\n    BaskVault internal vault;\n    ShiftToken[3] internal stock;\n    ShiftFeed[3] internal feed;\n    address internal alice = address(0xA11CE);\n    address internal mallory = address(0xBAD);\n\n    function setUp() public {\n        vm.warp(1_789_992_000); // Monday 2026-09-21 12:00 UTC, inside trading hours.\n        vault = new BaskVault(OWNER, GUARDIAN);\n        for (uint256 i; i < 3; ++i) {\n            stock[i] = new ShiftToken();\n            feed[i] = new ShiftFeed();\n            vm.prank(OWNER);\n            vault.listGenesis(address(stock[i]), address(feed[i]), address(0), address(0), 0);\n            stock[i].mint(alice, 100e18);\n            vm.prank(alice);\n            stock[i].approve(address(vault), type(uint256).max);\n        }\n        vm.prank(OWNER);\n        vault.finalizeGenesis();\n    }\n\n    function _deposit(uint256 i, uint256 amount) internal {\n        address[] memory ts = new address[](1);\n        ts[0] = address(stock[i]);\n        uint256[] memory amounts = new uint256[](1);\n        amounts[0] = amount;\n        vm.prank(alice);\n        vault.deposit(ts, amounts, alice, 0, block.timestamp);\n    }\n\n    function _refresh() internal {\n        for (uint256 i; i < 3; ++i) {\n            feed[i].refresh();\n        }\n    }\n\n    function testMinimumForMovedAssetIsNotEnforcedAfterRemoveRetired() public {\n        _deposit(0, 10e18);\n        _deposit(2, 10e18);\n        // Retire the empty middle asset.\n        vm.prank(OWNER);\n        vault.close(address(stock[1]));\n        BaskVault.Action memory a;\n        a.kind = BaskVault.Kind.Retire;\n        a.token = address(stock[1]);\n        vm.prank(OWNER);\n        uint256 id = vault.propose(a);\n        vm.warp(block.timestamp + 2 days);\n        _refresh();\n        vm.prank(OWNER);\n        vault.execute(id);\n\n        // Alice builds minima from the preview in the current order: [2.5, 0, 2.5].\n        (uint256[] memory minima,,) = vault.previewRedeem(500e18);\n        assertEq(minima.length, 3);\n        assertEq(minima[0], 25e17);\n        assertEq(minima[2], 25e17);\n\n        // Anyone reorders the list before Alice's redeem lands; stock2 is now index 1.\n        vm.prank(mallory);\n        vault.removeRetired(address(stock[1]));\n        assertEq(vault.assetTokens()[1], address(stock[2]));\n        // stock2 becomes short, so its leg is 0.75e18, below Alice's 2.5e18 minimum.\n        stock[2].confiscate(address(vault), 7e18);\n\n        // Expected: the redeem is rejected because stock2's leg is under its minimum.\n        vm.prank(alice);\n        vm.expectRevert();\n        vault.redeem(500e18, alice, minima, block.timestamp);\n    }\n}","reproduction":"State: tokens = [stock0, stock1, stock2]; alice deposited 10 stock0 and 10 stock2 ($100 each; supply 2000e18). stock1 is closed, retired, managed 0, totalOwed 0. alice calls previewRedeem(500e18) and gets minima [2.5e18, 0, 2.5e18]. Before her redeem lands, anyone calls removeRetired(stock1): tokens = [stock0, stock2]. stock2 then loses 7e18 of the vault's 10e18 (confiscate in the mock), so its leg is min(3e18,10e18)*500e18/2000e18 = 0.75e18. alice.redeem(500e18, alice, [2.5e18, 0, 2.5e18], now). Expected: revert Slippage because stock2's leg 0.75e18 < her 2.5e18 minimum for stock2. Actual: the call succeeds; amounts = [2.5e18, 0.75e18]; the 2.5e18 minimum at index 2 is ignored and the 0 at index 1 is applied to stock2. Proof test test/scratch/MinOutShift.t.sol fails on this code with \"next call did not revert as expected\".","severity":"low","snippet":"        tokens[i] = last;\n        indexPlusOne[last] = i + 1;\n        tokens.pop();","title":"Permissionless removeRetired reorders the asset list, so a pending redeem's positional minAmountsOut is applied to the wrong asset"},{"citation":"resolved","description":"When totalSupply is 0 the share formula uses gross = value regardless of nav. totalSupply is 0 only before the first deposit, but managed can already be nonzero then: after listing (genesis or later) tokens can be sent to the vault and the owner can execute a Kind.Resync, which sets managed from balanceOf. The first depositor then receives shares equal only to their own value yet, through redeem's pro rata of managed, owns essentially the whole pre-existing balance. The spec text (\"gross = v if totalSupply is 0\") describes this formula, so the defect is the combination with Resync being executable before any deposit. Whoever sent those tokens (a seed from the owner, a transfer by mistake) loses them to the first depositor. Minimal fix preserving the spec: revert Resync execution while totalSupply is 0 (or treat nav > 0 with supply 0 as ZeroNAV-style invalid), so the first deposit always starts from an empty basket.","line":668,"path":"src/BaskVault.sol","reproduction":"State: genesis finalised, no deposits, hours always open. 1000 stock0 ($100,000) are transferred to the vault; the owner proposes and after 2 days executes Kind.Resync for stock0, so managed[stock0] = 1000e18 and totalSupply = 0. Mallory deposits 1 stock1 ($100): _depositShares(100e18, nav=100000e18) with supply 0 returns gross = 100e18, shares = 100e18 - 1e15. previewRedeem(shares) shows 999.99 stock0 for Mallory. Expected: a $100 deposit into a vault already holding $100,000 should not entitle the depositor to the existing holdings (or the deposit should be refused until the basket is empty). Actual: the $100 depositor can redeem 999.99 of the 1000 stock0. Reproduced in test/scratch/Econ.t.sol::testFirstDepositIgnoresPreexistingManagedNAV (passes on the current code).","severity":"low","snippet":"        uint256 gross = supply == 0 ? value : FullMath.mulDiv(value, supply, nav);","title":"First deposit mints shares equal to value and ignores NAV already held through a pre-deposit Resync"},{"citation":"resolved","description":"Every deposit, even a single-token one, reads the balance of every unretired asset, prices every asset with managed > 0 (feed call, pause call, pool observe and quote feed when a pool is set) in _depositState, and then runs a second full loop over tokens that reads assets[token].retired and deletes two deficit slots per asset (about 8.6k gas per asset cold, 2.2M at 250 assets). With MockFeed/MockPool, which cost a few thousand gas each, a cold single-token deposit measures 12,332,173 gas at 250 assets without pools and 19,065,770 with pools. A Chainlink proxy+aggregator read costs roughly 20-30k and a Uniswap v3 observe roughly 30-60k, so at 250 pooled assets a deposit will be in the 28-40M range and at the allowed maximum of 350 assets higher still, above a 28-32M block limit. Deposits then stop for everyone while redeem keeps working. This follows from the spec's per-deposit requirement to validate every held price, so the owner must treat the practical asset count as well below maxAssets. The second loop can be made constant-gas without changing semantics (a deficit epoch bumped on deposit and compared in flagDeficit/recognizeLoss), which recovers about 2.2M at 250 assets and 3M at 350.","line":656,"path":"src/BaskVault.sol","reproduction":"test/scratch/DepositGas.t.sol: list 250 assets (MockToken, MockFeed; optionally a MockPool with a quote feed each), deposit 1e18 of every token once, cool all addresses, then measure vault.deposit([token0],[1e18],alice,0,now). Logged: 'single-token deposit gas (cold): 12332173' without pools and '19065770' with pools. Expected: a deposit's cost should stay comfortably inside the chain's block gas limit at any allowed asset count. Actual: with real oracle costs substituted for the mocks the same call exceeds 28M, so deposits become impossible at the configured maximum.","severity":"low","snippet":"        for (uint256 i; i < tokens.length; ++i) {\n            if (!assets[tokens[i]].retired) delete deficits[tokens[i]];\n        }","title":"Deposit gas grows with every unretired asset; at the default 250 assets it is already 12-19M with free mocks and will exceed the block gas limit with real feeds and pools"}],"hash":"29b5d32fea3f20cc6eaedced7c52f1b9a14d5b6bd782f67af840199ba660014f","nodeId":"8fd116d6-41c4-49b1-a6d3-d254c2ca6ec8","outcome":"completed","summary":"The findings file is in place and validated (all four snippets match the tree exactly, 27 coverage rows, none unreached). The repository's own files are untouched; only `.imd-findings.json` and `test/scratch/` were created, both of which the harness ignores in git.\n\n## Review recap\n\n**Scope covered.** I read the Senior Auditor's Mindset and the Economic Security, Invariant and Flow Gap guides, then traced every state-changing entry point of `BaskVault` against them, re-ran the existing 62 tests (all pass), and wrote scratch tests to confirm each claim. The two mandatory attacks hold: redeem at 250 and 350 paused, blocked or upgraded tokens stays under 28M gas in every settings corner, and no role or setting can block redeem or claim. Settings can only force legs into the queue, which claim pays with caller gas. The conservation invariants hold: vault balance equals managed plus totalOwed absent issuer action, queued legs move managed to totalOwed one for one, and dead shares keep every redeem leg strictly below managed.\n\n**Findings written to `.imd-findings.json`:**\n\n1. **Medium, Resync front-running.** A Resync proposal is public for at least 2 days and adds the whole surplus to managed in one step. A depositor entering at the pre-resync NAV and redeeming after execution takes a pro rata slice of the surplus. Scratch test: attacker deposits $500k and withdraws $750k, the honest holder loses $250k. Mitigation within the current design is to pause deposits from proposal through execution.\n2. **Low, positional minima after reorder.** `removeRetired` is permissionless and swaps the last asset into the removed slot. A redeem built from `previewRedeem` before that lands has its minimum for the moved asset ignored. Self-contained proof at `test/scratch/MinOutShift.t.sol` fails on the current code.\n3. **Low, first deposit ignores pre-existing NAV.** A Resync executed before any deposit gives the vault managed balance, and the first depositor's shares equal only their value. A $100 deposit can redeem 999.99 of 1000 pre-held tokens.\n4. **Low, deposit liveness at scale.** Every deposit prices every held asset and then runs a second loop clearing deficit records. With free mocks a cold single-token deposit costs 12.3M gas at 250 assets without pools and 19.1M with pools, so real Chainlink and Uniswap costs will push it past the block limit at the configured maximum.\n\n**Not reached or not reported.** The New York calendar library and pool tick math are outside my area and were only sanity checked. Several behaviours I traced are spec-mandated rather than defects and were left out: claim paying from the balance that backs managed holders during a shortfall, deposits stalling on a 1-wei shortfall until retire, and a ZeroNAV deadlock after retiring every asset.","treeHash":null,"usage":{"cachedInputTokens":1740364,"inputTokens":418,"model":"claude-fable-5-1","outputTokens":72083,"runtime":"claude","turns":35,"wallClockMs":1107724}},{"artifacts":[],"attempt":1,"bundleHash":"5303ecbb00393d05930d4bddbff9513d38b7913ba57f26eb0354ce45d2dc9e0e","device":"720122d0ca9f60ca","findings":[],"hash":"2ffaf4ee904c3ec8cf0be1c978b00e4a21c3696c46530a865fd86138eaf7d02f","nodeId":"252479cd-2a7e-4c5e-8150-97252d0209db","outcome":"completed","summary":"Updated [launch.json](/Users/hhyy/.lobster_wallets/identitymd/work/4ff210c0-6868-42fb-b90e-23a06e676b0f/252479cd-2a7e-4c5e-8150-97252d0209db/launch.json) for `BaskVault`, preserving the exact owner and guardian literals and clarifying BASK supply and genesis behavior.\n\nValidation passed: manifest constraints, constructor ABI, `forge build`, and all 62 tests. Runtime is 23,913 bytes, within the limit. Only `launch.json` changed.","treeHash":"655de318cb3bc0c9c53cb8096383b225b654b148","usage":{"cachedInputTokens":161536,"inputTokens":31188,"model":"gpt-6-astra","outputTokens":3432,"runtime":"codex","turns":3,"wallClockMs":165725}},{"artifacts":[],"attempt":1,"bundleHash":"a78468a1b7588b86c95493cf59e790399a1e5c06a3b23371d4428508f61e56a2","device":"004eae350f695d24","findings":[],"hash":"3cab3336f4e2fca9fb171834fd3f8f2a7a532327e2ba29d766c0f901a205e8d2","nodeId":"63363e97-eb3b-4eaf-abc0-0d056c5264e1","outcome":"completed","summary":"Added tests under `test/` covering adversarial failures, withdrawal gas limits, fuzz properties, and accounting invariants.\n\nVerification passed:\n- `forge build`\n- `forge test`: 80 passed\n- Invariants: 24,576 random calls, zero unexpected reverts\n\nNo implementation defects were reproduced. Only `test/` files were added.","treeHash":"40c4655b2ce44778e251370d9517bf7f2a9ef47d","usage":{"cachedInputTokens":2201728,"inputTokens":132129,"model":"gpt-6-astra","outputTokens":21549,"runtime":"codex","turns":6,"wallClockMs":632455}},{"artifacts":[],"attempt":2,"bundleHash":null,"device":"2c968e88904ec22b","findings":[{"citation":"resolved","description":"Asymmetry between deposit and redeem (pair: deposit/redeem receiver validation). deposit rejects receiver == address(this) at src/BaskVault.sol:625, but redeem only rejects the zero address. With receiver = address(this), every direct leg goes through pay(token, vault, leg): the token transfer is a self-transfer, the vault balance does not fall, the exact-debit check at line 741 fails, _tryPay returns false, and line 724-725 credit owed[vault][token] and totalOwed[token]. claim reads owed[msg.sender], and the vault never calls claim, so this debt is unclearable forever. Consequences: (1) the redeemer's tokens are locked in the vault for good (self-harm), and (2) removeRetired at line 368 requires totalOwed[token] == 0, so any unprivileged share holder can, with a dust redemption, make a token permanently unremovable and therefore never relistable, contradicting the brief's 'its token may then be relisted'. Unprivileged amplifier: any holder of dust shares; no role required. Minimal fix preserving design: mirror deposit and reject receiver == address(this) in redeem (and to == address(this) in claim). Reproduced in test/scratch/Seams.t.sol::testRedeemToVaultLocksTotalOwedAndBlocksRemoval.","line":688,"path":"src/BaskVault.sol","reproduction":"Setup: three genesis assets, deposits open. alice deposits 100e18 stock0, bob 100e18 stock1, griefer 1e18 stock0 (griefer gets ~100e18 shares). griefer calls redeem(sharesOfGriefer, address(vault), [], now). Expected: either revert (as deposit does for the vault receiver) or a payment. Actual: leg for stock0 is paid to the vault itself, the exact-debit check fails, owed[vault][stock0] = leg and totalOwed[stock0] = leg (~1e18). Then alice and bob redeem everything, the remaining dead-share dust in managed[stock0] is written down via flagDeficit + 7 days + recognizeLoss so managed[stock0] == 0, the owner closes and retires stock0. removeRetired(stock0) reverts InvalidAsset because totalOwed[stock0] is still ~1e18 and no address can ever claim it; the token can never be relisted. Replacing the receiver with any EOA makes removeRetired succeed.","severity":"low","snippet":"        if (receiver == address(0)) revert InvalidAddress();","title":"redeem accepts the vault itself as receiver, creating debt nobody can claim and permanently blocking removeRetired for that token"},{"citation":"resolved","description":"Trust-gap seam (access x asymmetry): minAmountsOut is bound to the asset's position in tokens[], but removeRetired (callable by anyone the moment a retired asset has managed == 0 and totalOwed == 0) swaps the last token into the removed slot at src/BaskVault.sol:375 and pops. A redeem transaction built from previewRedeem/assetTokens before that call executes with its minimums shifted: the moved token is compared against the minimum the caller set for a different slot (often 0), and the caller's intended minimum for it falls past the array end and is ignored. Combined with recognizeLoss (also anyone, once a record is 7 days old) in the same block, an unprivileged actor can make a redeemer accept a write-down their minimum was meant to reject. In the opposite direction the shift makes the redeem revert, a one-transaction DoS. Impact is bounded: the redeemer receives the honest post-loss pro-rata amount, but the only slippage control redeem offers is defeated by a public call. Reproduced in test/scratch/Seams.t.sol::testRemoveRetiredFrontRunDefeatsMinAmountsOut. A fix keeping the index-based interface could key minimums to token addresses or require minAmountsOut.length == tokens.length; the brief's 'missing entry = 0' rule would need to be revisited for that, so this is a scope decision for the author.","line":715,"path":"src/BaskVault.sol","reproduction":"Setup: tokens = [stock0, stock1, stock2]; alice deposits 100e18 stock0 and 100e18 stock2. Owner closes and retires stock1 (managed 0, totalOwed 0, so anyone may remove it). stock2 issuer confiscates 50e18 from the vault; anyone calls flagDeficit(stock2); 7 days pass. alice reads assetTokens() == [stock0, stock1, stock2] and submits redeem(allShares, alice, [0, 0, 99e18], now) expecting stock2's leg (~100e18 before the write-down) to meet 99e18 or revert. Griefer front-runs with removeRetired(stock1) then recognizeLoss(stock2). Expected: alice's redeem reverts Slippage because stock2's leg (~50e18) < 99e18. Actual: tokens is now [stock0, stock2]; i=1 compares stock2's ~50e18 leg against minAmountsOut[1] == 0 and passes; minAmountsOut[2] == 99e18 is never read; redeem succeeds returning 2 legs with legs[1] < 99e18.","severity":"low","snippet":"            if (i < minAmountsOut.length && leg < minAmountsOut[i]) revert Slippage();","title":"Unprivileged removeRetired can reorder tokens between preview and redeem, so position-indexed minAmountsOut are matched to the wrong asset and bypassed"},{"citation":"resolved","description":"Trust-gap seam (access x economics, race amplifier). Resync raises managed[token] by balanceOf - managed - totalOwed, instantly raising NAV for every share. The proposal and its executable time are public (Proposed event, pendingProposals), and until execution deposits are priced on a NAV that excludes the surplus. An unprivileged depositor who enters in the block before execute(id) is minted shares at the pre-resync NAV and immediately owns a pro-rata claim on the surplus, diluting the holders who were exposed when the surplus arrived. With no fee recipient set the capture is pure; with the 0.5% deposit and redeem fees it is profitable whenever surplus / NAV exceeds about 1%. This is the ordinary MEV cost of a timelocked, publicly announced accounting step and the brief requires resync as specified, so this is reported as a documented trust assumption rather than a defect in the guards: the owner should expect resync to be sandwiched and could, within the existing design, pause deposits (owner or guardian, at once) in the blocks around execution. Reproduced in test/scratch/Seams.t.sol::testResyncFrontRunCapturesSurplus.","line":443,"path":"src/BaskVault.sol","reproduction":"Setup: alice deposits 100e18 stock0 at $100 (NAV $10,000). 50e18 stock0 arrives in the vault as a surplus. Owner proposes Resync(stock0); two days pass. In the block before execute, bob deposits 100e18 stock0 and receives ~10,000e18 shares (half the supply), priced on NAV $10,000 that ignores the surplus. Owner executes: managed[stock0] becomes 250e18. bob redeems all his shares and receives ~125e18 stock0 for 100e18 deposited (test asserts 124e18 < leg < 126e18). Expected under a non-racing accounting: the 50e18 surplus accrues only to alice. Actual: 25e18 of it is transferred to bob.","severity":"low","snippet":"                _setManaged(a.token, managed[a.token] + extra);","title":"Resync execution is public two days ahead and can be front-run by a deposit that captures a share of the pre-existing surplus"},{"citation":"resolved","description":"Documenting a role power, not a guard bypass. DirectLimit has no lower bound (only the gas product bound at line 609), and PayGas may be lowered to 20,000, which is below what any ERC-20 transfer plus the exact-debit balance reads in pay() need. Either setting makes direct = false (line 697) or every _tryPay fail (line 723), so all legs are queued into owed and users must call claim, which uses caller gas and no setting. redeem itself never reverts because of these settings, claim is unaffected, and the brief's 'any way a role or a setting blocks redeem' was checked against each cfg read in redeem (directLimit, payGas, balanceGas) and found not to block. The owner (via two-day proposal, guardian may cancel) can therefore degrade exits to two transactions but cannot stop them. No change is recommended; this records the actor and precondition as the brief's role documentation asks.","line":597,"path":"src/BaskVault.sol","reproduction":"Owner proposes Setting(DirectLimit, 0); after 2 days executes. alice redeem(1000e18, alice, [], now) with one funded asset: active (1) <= directLimit (0) is false, the leg is credited to owed[alice][token] and totalOwed; no tokens move until alice calls claim([token], alice), which pays in full. Same outcome with Setting(PayGas, 20000): pay() runs out of gas, _tryPay returns false, the leg is queued, claim pays with caller gas.","severity":"info","snippet":"            c.directLimit = v;","title":"Trust assumption: DirectLimit may be set to 0 and PayGas to 20,000, converting every redemption into a two-step owed/claim exit (not a block)"}],"hash":"4f768da41a564baf803f7e12ffbcba788cb53c3c3c018f63822a65811b4651b0","nodeId":"e886abc2-f757-4895-be22-18a6e0a39204","outcome":"completed","summary":"Both files exist and are deliberately excluded from git by the harness's exclude file, so the tree is otherwise untouched and the review is complete.\n\n**Outcome.** The permission model holds. No missing guard, escalation chain, or role-driven way to revert `redeem` or `claim` exists. I wrote four entries to `.imd-findings.json`, three low and one informational, each with a reproduction that I ran in a scratch test.\n\n**Findings**\n- **Redeem accepts the vault as receiver.** `deposit` rejects it, `redeem` does not. The self-transfer fails the exact-debit check, so the leg becomes debt owed to the vault that no one can claim. Any dust holder can thereby make a token permanently unremovable and unrelistable.\n- **Public `removeRetired` reorders tokens.** Position-indexed `minAmountsOut` then compare against the wrong slot. Bundled with a matured `recognizeLoss`, an unprivileged actor can push a redeemer past the minimum they set.\n- **Resync is front-runnable.** The execution time is public, so a deposit in the prior block captures part of the surplus from existing holders. Reported as a documented trust assumption with a race amplifier.\n- **DirectLimit 0 or PayGas 20,000 forces two-step exits.** Recorded as a role power, not a defect. Redeem still never reverts and claim ignores every setting.\n\n**Coverage.** All 21 entry points have rows: 17 hold, 3 carry findings, plus 4 invariant rows and one honest `unreached` for deposit gas under maximal pooled-asset budgets, which is outside my area. The static-analysis leads were all false positives, which the coverage record explains.\n\n**What is next.** The judge decides whether the vault-receiver check and the minimum-alignment fix are in scope, since both would add a small safeguard the brief did not name. The scratch test under `test/scratch/` is disposable and demonstrates all three seams.","treeHash":null,"usage":{"cachedInputTokens":1954179,"inputTokens":610,"model":"claude-fable-5-1","outputTokens":52857,"runtime":"claude","turns":20,"wallClockMs":826625}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"61b4050710026370","findings":[{"citation":"resolved","description":"The brief says 'Anyone may remove a retired asset with managed and totalOwed 0; its token may then be relisted.' The code implements that condition literally, but the condition can never be met for a normal Stock Token once it has received a single deposit. Every redemption computes leg = min(managed, available) * net / totalSupplyBeforeBurn (line 713) with net < totalSupply, because the first deposit mints 1e15 shares to address(0xdEaD) (line 659) that nobody can ever burn. So each redemption removes strictly less than the whole managed balance and managed converges on a dust residual (managed * 1e15 / totalSupply, rounded) that no path can zero: redeem cannot (net < supply), recognizeLoss only lowers managed by an actual shortfall (line 832-834) and a healthy token is never short because its balance equals managed, and Resync only raises managed. The result is that after retirement the asset keeps its slot in `tokens` forever, the retire -> remove -> relist lifecycle promised by the brief is dead code in practice, and maxAssets capacity is permanently consumed by every retired asset. Only an asset that never received a deposit, or one whose entire custody was confiscated and recognised as a loss, can be removed. The scratch test test/scratch/RemoveRetired.t.sol::testRemoveRetiredUnreachableAfterFullExit reproduces this.","line":368,"path":"src/BaskVault.sol","reproduction":"Genesis with 3 assets at $100 each (default settings, feed decimals 8). alice deposits 10e18 of stock0 -> totalSupply 1000e18, 1e15 of it at 0xdEaD, managed[stock0] = 10e18. alice redeems her entire balance (1000e18 - 1e15) to herself. Expected per the brief's lifecycle: once every holder has exited, owner closes and retires stock0 and anyone can removeRetired(stock0). Actual: managed[stock0] = 1e13 (10e18 * 1e15 / 1e21) and the vault still holds exactly 1e13 of stock0, so flagDeficit records nothing, recognizeLoss reverts Timelock, and after close + Retire proposal + execute, removeRetired(stock0) reverts InvalidAsset. No sequence of unprivileged or owner calls can bring managed to 0 for a healthy token, so the asset can never be removed or relisted.","severity":"low","snippet":"        if (!a.retired || managed[token] != 0 || totalOwed[token] != 0) revert InvalidAsset();","title":"removeRetired is unreachable for any asset that ever held a deposit: the permanent 1e15 dead shares leave managed > 0 after every holder exits"},{"citation":"resolved","description":"deposit rejects the vault as receiver (line 625) but redeem only rejects address(0). With receiver = address(this), the direct path calls pay(token, vault, leg): the token transfers to itself, the vault balance does not fall, and the exact-debit check at line 741 reverts, so _tryPay returns false and line 724-725 credit owed[vault][token] and totalOwed[token]. The vault never calls claim on itself (claim is keyed on msg.sender and pay is the only self-call), so that debt can never be paid and totalOwed[token] can never return to 0. Those tokens are also excluded from available forever (line 794) and from Resync (line 442), so they are stranded inside the contract. The caller only loses a dust leg: with managed 10e18 and supply 1000e18, 1000 wei of BASK is enough to produce a nonzero leg for every asset in one call. Combined with finding 1 this closes the last remaining route to removeRetired (an asset whose custody was fully lost and recognised), and it strands a nonzero amount of every listed Stock Token with no owner. Reproduced by test/scratch/RemoveRetired.t.sol::testRedeemToVaultStrandsOwedAndBlocksRemoval.","line":688,"path":"src/BaskVault.sol","reproduction":"Same genesis as finding 1. alice deposits 10e18 of stock0 and then calls redeem(1000, address(vault), [], now). Expected: either the call is rejected like deposit does for the vault receiver, or the leg is deliverable. Actual: legs[0] = 10 wei, owed[vault][stock0] = 10, totalOwed[stock0] = 10, Paid is not emitted. alice then redeems the rest of her shares, the issuer confiscates the whole remaining vault balance, flagDeficit + 7 days + recognizeLoss bring managed[stock0] to 0, owner closes and retires stock0. removeRetired(stock0) reverts InvalidAsset because totalOwed[stock0] == 10 and no call can ever reduce it (claim requires msg.sender == vault).","severity":"low","snippet":"        if (receiver == address(0)) revert InvalidAddress();","title":"redeem accepts receiver == address(vault); the queued leg becomes owed[vault][token] that nobody can claim, raising totalOwed permanently and blocking removeRetired even when managed reaches 0"}],"hash":"855973b9e7a4f22c4033d06a87bb0343a4b63a9a38b7ea9f81510f70f95ff766","nodeId":"04684acf-558f-4461-bbe3-5ec8fb233823","outcome":"completed","summary":"The review is complete. The findings file is written and validated, both cited snippets match the source lines exactly, and only the permitted paths were touched.\n\n**Outcome:** the control flow of BaskVault holds against the Execution Trace, Periphery and First Principles checklists. No funds-at-risk defect was found. Two low findings are reported, both about the retire-and-relist lifecycle being unreachable.\n\n**Finding 1 (low):** `removeRetired` can never succeed for a Stock Token that ever held a deposit. The permanent dead shares mean every redemption burns strictly less than the full supply, so managed converges on a dust residual (10e18 deposited, full exit, residual 1e13) that no path can zero: redeem cannot, recognizeLoss needs a real shortfall, resync only raises. After retirement the asset occupies its slot forever and can never be relisted.\n\n**Finding 2 (low):** `redeem` accepts the vault itself as receiver. The self-transfer fails the exact-debit check, so the leg is queued as owed to the vault, which can never claim it. That permanently raises totalOwed, strands the tokens, and closes the last remaining route to removal. Cost to trigger is a dust redemption.\n\n**Mandatory attack verified clean.** Beyond the author's eight gas scenarios, I measured six additional setting corners for redeem with paused, blocked and upgraded tokens, including both call budgets at their maximum with the direct path and maximum balances at 250 and 350 assets:\n\n| Corner | Gas incl. intrinsic |\n| --- | ---: |\n| balanceGas 500k + payGas 500k, 26 direct legs, max balances | 27,629,246 |\n| 250 assets, defaults, max balances, upgraded tokens | 27,016,629 |\n| 350 assets, min balanceGas, max balances | 26,872,832 |\n\nAll stayed under 28,000,000. No role or setting can block redeem or claim. Every one of the 21 entry points has a coverage row, plus four invariant rows. Scratch tests under test/scratch reproduce both findings and the gas corners.","treeHash":null,"usage":{"cachedInputTokens":1447542,"inputTokens":354,"model":"claude-fable-5-1","outputTokens":67348,"runtime":"claude","turns":34,"wallClockMs":960206}},{"artifacts":[],"attempt":1,"bundleHash":"096a01ea7e2a89cff153a43389bb8e5162103d1edbea813ddc0e13005629b87f","device":"6960837f8e61d811","findings":[],"hash":"cf0a8de87a9e36c7e22eefdda75bb01337ccfb7ce1e7d7699908a99cdaa3cd10","nodeId":"f62606df-e7ca-4177-a806-c9d37637a48e","outcome":"completed","summary":"Implemented the vault, deployment manifest, vendored dependencies, tests, and operational documentation.\n\n- `forge build`, `forge test`, and `forge fmt --check` pass.\n- All 62 tests pass.\n- Runtime: 23,913 bytes.\n- Worst tested redemption: 27,990,581 gas, including calldata.\n\nGenesis assets must be configured before deposits open. Setup is documented in `README.md`; adversarial results and review limitations are in `REVIEW.md`.","treeHash":"46a9c313a77b97252d4c61e076a4a1bfdf6480b0","usage":{"cachedInputTokens":3633792,"inputTokens":248346,"model":"gpt-6-astra","outputTokens":73724,"runtime":"codex","turns":11,"wallClockMs":1959247}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"3f6a9bdd601cb99f","findings":[],"hash":"e59eaed9b128ea1f424a3b933ea82c93eacf55efd95d0cbb9814ee5ca3764495","nodeId":"252479cd-2a7e-4c5e-8150-97252d0209db","outcome":"failed","summary":"Selected model is at capacity. Please try a different model.","treeHash":null,"usage":{"cachedInputTokens":0,"inputTokens":0,"model":"gpt-6-astra","outputTokens":0,"runtime":"codex","turns":1,"wallClockMs":31282}}],"verification":[{"checks":[{"durationMs":20835,"exitCode":0,"name":"build","output":"Compiling 33 files with Solc 0.8.26\nSolc 0.8.26 finished in 20.54s\nCompiler run successful!\nwarning[boolean-cst]: misuse of a boolean constant\n   ╭▸ src/libraries/PoolOracle.sol:20:26\n   │\n20 │         if (!ok) return (false, 0, 0);\n   │                          ━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/BaskVault.sol:532:27\n    │\n532 │             return (0, 0, false);\n    │                           ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[boolean-cst]: misuse of a boolean constant\n   ╭▸ src/libraries/PoolOracle.sol:43:21\n   │\n43 │             return (false, 0, 0);\n   │                     ━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[boolean-cst]: misuse of a boolean constant\n   ╭▸ src/libraries/PoolOracle.sol:51:30\n   │\n51 │         if (ds == 0) return (false, 0, 0);\n   │                              ━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/TickMath.sol:25:38\n   │\n25 │         uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick));\n   │                                      ━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/BaskVault.sol:792:26\n    │\n792 │         if (!ok) return (false, 0);\n    │                          ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/TickMath.sol:25:63\n   │\n25 │         uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick));\n   │                                                               ━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/TickMath.sol:26:23\n   │\n26 │         if (absTick > uint256(int256(MAX_TICK))) revert InvalidTick();\n   │                       ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[boolean-cst]: misuse of a boolean constant\n   ╭▸ src/libraries/PoolOracle.sol:54:54\n   │\n54 │         if (tick < -887272 || tick > 887272) return (false, 0, 0);\n   │                                                      ━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[boolean-cst]: misuse of a boolean constant\n   ╭▸ src/libraries/PoolOracle.sol:56:46\n   │\n56 │         if (liq > type(uint128).max) return (false, 0, 0);\n   │                                              ━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[boolean-cst]: misuse of a boolean constant\n   ╭▸ src/libraries/PoolOracle.sol:57:17\n   │\n57 │         return (true, int24(tick), uint128(liq));\n   │                 ━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/TickMath.sol:54:24\n   │\n54 │         sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1));\n   │                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint160' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/PoolOracle.sol:40:74\n   │\n40 │             off0 != 64 || off1 != 160 || len0 != 2 || len1 != 2 || t0 != int56(t0) || t1 != int56(t1)\n   │                                                                          ━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int56' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/PoolOracle.sol:40:93\n   │\n40 │             off0 != 64 || off1 != 160 || len0 != 2 || len1 != 2 || t0 != int56(t0) || t1 != int56(t1)\n   │                                                                                             ━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int56' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/PoolOracle.sol:48:18\n   │\n48 │             dt = int56(t1) - int56(t0);\n   │                  ━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int56' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/PoolOracle.sol:48:30\n   │\n48 │             dt = int56(t1) - int56(t0);\n   │                              ━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int56' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/PoolOracle.sol:49:18\n   │\n49 │             ds = uint160(s1) - uint160(s0);\n   │                  ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint160' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/PoolOracle.sol:49:32\n   │\n49 │             ds = uint160(s1) - uint160(s0);\n   │                                ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint160' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/PoolOracle.sol:57:23\n   │\n57 │         return (true, int24(tick), uint128(liq));\n   │                       ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int24' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/PoolOracle.sol:57:36\n   │\n57 │         return (true, int24(tick), uint128(liq));\n   │                                    ━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint128' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/BaskVault.sol:313:65\n    │\n313 │     function transferOwnership(address next) external onlyOwner nonReentrant {\n    │                                                                 ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/BaskVault.sol:327:48\n    │\n327 │     function pauseDeposits() external onlyRole nonReentrant {\n    │                                                ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/BaskVault.sol:332:51\n    │\n332 │     function unpauseDeposits() external onlyOwner nonReentrant {\n    │                                                   ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/BaskVault.sol:337:53\n    │\n337 │     function close(address token) external onlyRole nonReentrant {\n    │                                                     ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/BaskVault.sol:344:58\n    │\n344 │     function lowerNAVCap(uint256 cap) external onlyOwner nonReentrant {\n    │                                                          ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/BaskVault.sol:354:9\n    │\n354 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/BaskVault.sol:360:51\n    │\n360 │     function finalizeGenesis() external onlyOwner nonReentrant {\n    │                                                   ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/BaskVault.sol:385:65\n    │\n385 │     function propose(Action calldata action) external onlyOwner nonReentrant returns (uint256 id) {\n    │                                                                 ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:390:9\n    │\n390 │         emit Proposed(id, action, block.timestamp + 2 days, block.timestamp + 9 days);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/BaskVault.sol:393:51\n    │\n393 │     function cancel(uint256 id) external onlyRole nonReentrant {\n    │                                                   ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/BaskVault.sol:400:53\n    │\n400 │     function execute(uint256 id) external onlyOwner nonReentrant {\n    │                                                     ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/BaskVault.sol:403:13\n    │\n403 │         if (block.timestamp < p.createdAt + 2 days) revert Timelock();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:522:9\n    │\n522 │         emit AssetListed(token, feed);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:411:13\n    │\n411 │             emit GuardianChanged(a.target);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:414:13\n    │\n414 │             emit CapChanged(a.value);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:417:13\n    │\n417 │             emit FeeRecipientChanged(a.target);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:420:13\n    │\n420 │             emit SettingChanged(a.setting, a.value, a.value2);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:444:17\n    │\n444 │                 emit Resynced(a.token, extra);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:446:13\n    │\n446 │             emit AssetChanged(a.token, a.kind);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:448:9\n    │\n448 │         emit ProposalExecuted(id);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/BaskVault.sol:481:23\n    │\n481 │         if (p.done || block.timestamp >= p.createdAt + 9 days) return false;\n    │                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/BaskVault.sol:538:29\n    │\n538 │                 || (address(uint160(t0)) != token && address(uint160(t1)) != token)\n    │                             ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/BaskVault.sol:538:62\n    │\n538 │                 || (address(uint160(t0)) != token && address(uint160(t1)) != token)\n    │                                                              ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/BaskVault.sol:540:32\n    │\n540 │         baseIsToken0 = address(uint160(t0)) == token;\n    │                                ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/BaskVault.sol:541:32\n    │\n541 │         qd = _decimals(address(uint160(baseIsToken0 ? t1 : t0)));\n    │                                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/BaskVault.sol:555:16\n    │\n555 │         return uint8(d);\n    │                ━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/BaskVault.sol:633:13\n    │\n633 │             value += _value(amounts[i], answers[indexPlusOne[a.token] - 1], a.tokenDecimals, a.feedDecimals);\n    │             ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/BaskVault.sol:624:13\n    │\n624 │         if (block.timestamp > deadline) revert Deadline();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:662:9\n    │\n662 │         emit Deposit(msg.sender, receiver, value, shares, fee);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/BaskVault.sol:631:34\n    │\n631 │             if (amounts[i] == 0) revert InvalidInput();\n    │                                  ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/libraries/FullMath.sol:33:39\n   │\n33 │                 if (denominator == 0) revert MathOverflow();\n   │                                       ━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/libraries/FullMath.sol:42:39\n   │\n42 │             if (denominator <= prod1) revert MathOverflow();\n   │                                       ━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/BaskVault.sol:787:18\n    │\n787 │         if (!ok) revert BalanceUnreadable(token);\n    │                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/BaskVault.sol:648:17\n    │\n648 │                 revert PaymentFailed(token);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/BaskVault.sol:652:17\n    │\n652 │                 revert PaymentFailed(token);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/BaskVault.sol:715:45\n    │\n715 │             if (i < minAmountsOut.length && leg < minAmountsOut[i]) revert Slippage();\n    │                                             ━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/BaskVault.sol:687:13\n    │\n687 │         if (block.timestamp > deadline) revert Deadline();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:729:9\n    │\n729 │         emit Redeem(msg.sender, receiver, shares, net, fee, amounts);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/BaskVault.sol:704:72\n    │\n704 │                 if (i < minAmountsOut.length && minAmountsOut[i] != 0) revert Slippage();\n    │                                                                        ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/BaskVault.sol:715:69\n    │\n715 │             if (i < minAmountsOut.length && leg < minAmountsOut[i]) revert Slippage();\n    │                                                                     ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:742:9\n    │\n742 │         emit Paid(token, to, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:765:13\n    │\n765 │             emit Claimed(msg.sender, to, token, amount);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/BaskVault.sol:762:65\n    │\n762 │                     if (!_tryPay(token, to, amount, gasleft())) revert PaymentFailed(token);\n    │                                                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:825:9\n    │\n825 │         emit DeficitFlagged(token, deficits[token].amount, deficits[token].since);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/BaskVault.sol:831:30\n    │\n831 │         if (d.amount == 0 || block.timestamp < d.since + 7 days) revert Timelock();\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:836:9\n    │\n836 │         emit LossRecognized(token, loss);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/BaskVault.sol:855:40\n    │\n855 │         ok = ok && signedAnswer > 0 && updatedAt <= block.timestamp && block.timestamp - updatedAt <= cfg.maxAge;\n    │                                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/BaskVault.sol:855:72\n    │\n855 │         ok = ok && signedAnswer > 0 && updatedAt <= block.timestamp && block.timestamp - updatedAt <= cfg.maxAge;\n    │                                                                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/BaskVault.sol:854:40\n    │\n854 │         if (signedAnswer > 0) answer = uint256(signedAnswer);\n    │                                        ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/BaskVault.sol:876:17\n    │\n876 │             if (block.timestamp - updatedAt > cfg.noPoolAge) reason = Reason.NoPoolAge;\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/BaskVault.sol:880:58\n    │\n880 │             (ok, tick, liq) = PoolOracle.consult(a.pool, uint32(cfg.poolWindow), cfg.poolGas);\n    │                                                          ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/BaskVault.sol:944:82\n    │\n944 │             if (ok && block.timestamp - updatedAt <= cfg.freshHours * 1 hours) ++fresh;\n    │                                                                                  ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/BaskVault.sol:944:23\n    │\n944 │             if (ok && block.timestamp - updatedAt <= cfg.freshHours * 1 hours) ++fresh;\n    │                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/libraries/TickMath.sol:26:50\n   │\n26 │         if (absTick > uint256(int256(MAX_TICK))) revert InvalidTick();\n   │                                                  ━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/BaskVault.sol:998:34\n    │\n998 │             if (amounts[i] == 0) revert InvalidInput();\n    │                                  ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[uninitialized-local]: local variable is read before being initialized\n     ╭▸ src/BaskVault.sol:1017:15\n     │\n1017 │             ++active;\n     │               ━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n     ╭▸ src/BaskVault.sol:1038:34\n     │\n1038 │             if (_pending(i)) ids[count++] = i;\n     │                                  ━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\n","passed":true},{"durationMs":724,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 2 tests for test/NewYorkTime.t.sol:NewYorkTimeTest\n[PASS] testDefaultWeeklyHoursAndAlwaysOpen() (gas: 505084)\n[PASS] testUSDaylightBoundaries2026Through2040() (gas: 374013)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 2.43ms (1.07ms CPU time)\n\nRan 15 tests for test/Governance.t.sol:GovernanceTest\n[PASS] testCapLoweringInvalidatesAllPendingRaises() (gas: 715882)\n[PASS] testExpiryAndCancellation() (gas: 522336)\n[PASS] testFeedAndRecentreUseExecutionAnswer() (gas: 1222463)\n[PASS] testGenesisNeedsThreeAndOnlyOnce() (gas: 959710)\n[PASS] testGuardianCannotCancelReplacementAndRolesRemainDistinct() (gas: 411720)\n[PASS] testLaterCloseVoidsReopenEvenIfAlreadyClosed() (gas: 712023)\n[PASS] testListingChecksAtProposalAndExecution() (gas: 1868804)\n[PASS] testLossClearsManagedBitAndResyncRestoresIt() (gas: 1166698)\n[PASS] testOnlyOwnerProposesExecutesAndTwoDayWindow() (gas: 423942)\n[PASS] testRemoveRelistAndReuseRetiredFeed() (gas: 1388135)\n[PASS] testRemovingEmptyAssetPreservesMovedManagedAssetAndMinimumOrder() (gas: 1005875)\n[PASS] testRetireVoidsProposalsSkipsAllDepositChecksAndSharesTokens() (gas: 3170738)\n[PASS] testRetirementRequiresClosedAtProposalAndExecution() (gas: 698500)\n[PASS] testSettingBoundsAndBothGasConstraintsRechecked() (gas: 1128463)\n[PASS] testTwoStepOwnershipAndGuardianCannotUnpause() (gas: 318585)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 18.46ms (7.47ms CPU time)\n\nRan 12 tests for test/Redemption.t.sol:RedemptionTest\n[PASS] testClaimHasNoPayGasLimitAndFailedClaimKeepsDebt() (gas: 763699)\n[PASS] testEveryPauseAndRoleRestrictionLeavesRedemptionAndClaimOpen() (gas: 915733)\n[PASS] testFuzzBrokenTransferRollsBackAndBecomesOwed(uint8) (runs: 256, μ: 654072, ~: 613710)\nLogs:\n  Bound result 1\n\n[PASS] testNoReturnAndLargeReturnTransfersPayDirectly() (gas: 1006080)\n[PASS] testPartialClaimUsesBalanceAndCanRedirectReceiver() (gas: 1170577)\n[PASS] testQueuedDebtExcludedFromRedemptionResyncAndDeposits() (gas: 1927946)\n[PASS] testRedemptionCallerInputsAndMinimumsIncludingZeroManagedLeg() (gas: 655931)\n[PASS] testReentrancyOnPullPaymentAndClaimAndPayOnlySelf() (gas: 1097338)\n[PASS] testRoleCannotUseBalanceGasOrPayGasToBlockClaim() (gas: 2212489)\n[PASS] testShortRedemptionNeverWritesDownResidualManaged() (gas: 519427)\n[PASS] testTokenImplementationDisappearsAfterDeposit() (gas: 502236)\n[PASS] testUnreadableUpgradedBalanceUsesManagedAndDoesNotTouchOracle() (gas: 1020219)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 24.55ms (25.46ms CPU time)\n\nRan 15 tests for test/BaskVault.t.sol:BaskVaultTest\n[PASS] testCapNoShareSupplyCap() (gas: 1011782)\n[PASS] testConstructorRoles() (gas: 4024)\n[PASS] testDeploymentDefaultsAndRuntime() (gas: 10024245)\n[PASS] testDepositAfterPriceChangeRoundsGrossDown() (gas: 706358)\n[PASS] testDepositMintPreviewDonationAndRedeem() (gas: 1128390)\n[PASS] testDepositSlippageDeadlinesAndExactPull() (gas: 931608)\n[PASS] testFeeRoundingMintTransferAndBurn() (gas: 1619864)\n[PASS] testFuzzConservation(uint96,uint96) (runs: 256, μ: 552314, ~: 555108)\nLogs:\n  Bound result 970904337593528104668\n  Bound result 51596299236967648902896\n\n[PASS] testLargerDeficitRestartsAndDepositsClearRecords() (gas: 1635310)\n[PASS] testLossDelayPartialRecoveryAndNoAutomaticWriteDown() (gas: 752461)\n[PASS] testMultiplePullsAreAtomic() (gas: 419027)\n[PASS] testResyncOnlyCountsSurplusAndDoesNotRecognizeLoss() (gas: 1147334)\n[PASS] testShareERC20() (gas: 726722)\n[PASS] testStandardTransferEventTopic() (gas: 431950)\n[PASS] testZeroNAVCannotMintAgainstExistingShares() (gas: 1208126)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 27.07ms (48.91ms CPU time)\n\nRan 10 tests for test/Oracle.t.sol:OracleTest\n[PASS] testConsultNegativeRoundingAndCumulativeWrap() (gas: 1513332)\n[PASS] testEveryHeldPriceAndEveryUnretiredBalanceRequired() (gas: 890383)\n[PASS] testFreshnessAndDuplicateUnlistedClosedReasons() (gas: 585592)\n[PASS] testFuzzValueAcrossTokenAndFeedDecimals(uint8,uint8) (runs: 256, μ: 2115694, ~: 2123139)\nLogs:\n  Bound result 5\n  Bound result 7\n\n[PASS] testInvalidFeedAnswersFutureStaleAndBoundedFailures() (gas: 1077350)\n[PASS] testNoPauseInterfaceAtListingDoesNotRequireItLater() (gas: 1855038)\n[PASS] testPoolAllowsMaxAgeAndRemovingPoolRestoresNoPoolAge() (gas: 3024110)\n[PASS] testPoolLiquidityQuotePriceDeviationAndStaleness() (gas: 4012046)\n[PASS] testPoolOrientationAndDifferentDecimals() (gas: 4431251)\n[PASS] testPriceBandBoundariesAndPauseProbe() (gas: 1165199)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 32.68ms (37.19ms CPU time)\n\nRan 8 tests for test/RedemptionGas.t.sol:RedemptionGasTest\n[PASS] testGas250BlockedTokensWithExpensiveReadableBalances() (gas: 463761331)\nLogs:\n  cold redemption gas: 26402204\n  including transaction calldata: 26477316\n\n[PASS] testGas250UnreadableUpgradedTokens() (gas: 463855905)\nLogs:\n  cold redemption gas: 26699204\n  including transaction calldata: 26774316\n\n[PASS] testGas50DirectPaymentsConsumeAllPayGasAmong250Assets() (gas: 410717246)\nLogs:\n  cold redemption gas: 18084342\n  including transaction calldata: 18142654\n\n[PASS] testGasMaximumAssetCount350AtMinimumBalanceGas() (gas: 628897158)\nLogs:\n  cold redemption gas: 26859576\n  including transaction calldata: 26960100\n\n[PASS] testGasMaximumBalanceGasAtMaximumAllowedAssetCount50() (gas: 139523161)\nLogs:\n  cold redemption gas: 27915169\n  including transaction calldata: 27947881\n\n[PASS] testGasMaximumBalancesUseFullPrecisionMath() (gas: 153096546)\nLogs:\n  cold redemption gas: 27942869\n  including transaction calldata: 27990581\n\n[PASS] testGasMaximumDirectCountAtMinimumCallBudgets() (gas: 608833028)\nLogs:\n  cold redemption gas: 24578492\n  including transaction calldata: 24669800\n\n[PASS] testGasMaximumPayGasWithManyEmptyAssets() (gas: 571673800)\nLogs:\n  cold redemption gas: 27467667\n  including transaction calldata: 27539103\n\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 651.56ms (2.94s CPU time)\n\nRan 6 test suites in 652.44ms (756.75ms CPU time): 62 tests passed, 0 failed, 0 skipped (62 total tests)\n","passed":true},{"durationMs":37,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"BaskVault.acceptOwnership()\",\"BaskVault.approve(address,uint256)\",\"BaskVault.cancel(uint256)\",\"BaskVault.claim(address[],address)\",\"BaskVault.close(address)\",\"BaskVault.deposit(address[],uint256[],address,uint256,uint256)\",\"BaskVault.execute(uint256)\",\"BaskVault.finalizeGenesis()\",\"BaskVault.flagDeficit(address)\",\"BaskVault.listGenesis(address,address,address,address,uint256)\",\"BaskVault.lowerNAVCap(uint256)\",\"BaskVault.pauseDeposits()\",\"BaskVault.pay(address,address,uint256)\",\"BaskVault.propose((uint8,address,address,address,address,uint256,uint256,uint8))\",\"BaskVault.recognizeLoss(address)\",\"BaskVault.redeem(uint256,address,uint256[],uint256)\",\"BaskVault.removeRetired(address)\",\"BaskVault.transfer(address,uint256)\",\"BaskVault.transferFrom(address,address,uint256)\",\"BaskVault.transferOwnership(address)\",\"BaskVault.unpauseDeposits()\"],\"files\":{\".gitignore\":3,\"LICENSE\":338,\"README.md\":134,\"REVIEW.md\":47,\"THIRD_PARTY.md\":10,\"foundry.toml\":13,\"launch.json\":13,\"remappings.txt\":1,\"src/BaskVault.sol\":1042,\"src/libraries/BoundedCall.sol\":34,\"src/libraries/FullMath.sol\":107,\"src/libraries/NewYorkTime.sol\":34,\"src/libraries/PoolOracle.sol\":69,\"src/libraries/TickMath.sol\":56,\"test/BaskVault.t.sol\":265,\"test/Governance.t.sol\":325,\"test/NewYorkTime.t.sol\":78,\"test/Oracle.t.sol\":221,\"test/Redemption.t.sol\":200,\"test/RedemptionGas.t.sol\":166,\"test/VaultTestBase.sol\":101,\"test/mocks/Mocks.sol\":238},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"2ffaf4ee904c3ec8cf0be1c978b00e4a21c3696c46530a865fd86138eaf7d02f","verifiedTreeHash":"655de318cb3bc0c9c53cb8096383b225b654b148","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":33477,"exitCode":0,"name":"build","output":"Compiling 36 files with Solc 0.8.26\nSolc 0.8.26 finished in 33.13s\nCompiler run successful!\nwarning[boolean-cst]: misuse of a boolean constant\n   ╭▸ src/libraries/PoolOracle.sol:20:26\n   │\n20 │         if (!ok) return (false, 0, 0);\n   │                          ━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[boolean-cst]: misuse of a boolean constant\n   ╭▸ src/libraries/PoolOracle.sol:43:21\n   │\n43 │             return (false, 0, 0);\n   │                     ━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[boolean-cst]: misuse of a boolean constant\n   ╭▸ src/libraries/PoolOracle.sol:51:30\n   │\n51 │         if (ds == 0) return (false, 0, 0);\n   │                              ━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[boolean-cst]: misuse of a boolean constant\n   ╭▸ src/libraries/PoolOracle.sol:54:54\n   │\n54 │         if (tick < -887272 || tick > 887272) return (false, 0, 0);\n   │                                                      ━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[boolean-cst]: misuse of a boolean constant\n   ╭▸ src/libraries/PoolOracle.sol:56:46\n   │\n56 │         if (liq > type(uint128).max) return (false, 0, 0);\n   │                                              ━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[boolean-cst]: misuse of a boolean constant\n   ╭▸ src/libraries/PoolOracle.sol:57:17\n   │\n57 │         return (true, int24(tick), uint128(liq));\n   │                 ━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/BaskVault.sol:532:27\n    │\n532 │             return (0, 0, false);\n    │                           ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/BaskVault.sol:792:26\n    │\n792 │         if (!ok) return (false, 0);\n    │                          ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/TickMath.sol:25:38\n   │\n25 │         uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick));\n   │                                      ━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/TickMath.sol:25:63\n   │\n25 │         uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick));\n   │                                                               ━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/TickMath.sol:26:23\n   │\n26 │         if (absTick > uint256(int256(MAX_TICK))) revert InvalidTick();\n   │                       ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/PoolOracle.sol:40:74\n   │\n40 │             off0 != 64 || off1 != 160 || len0 != 2 || len1 != 2 || t0 != int56(t0) || t1 != int56(t1)\n   │                                                                          ━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int56' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/PoolOracle.sol:40:93\n   │\n40 │             off0 != 64 || off1 != 160 || len0 != 2 || len1 != 2 || t0 != int56(t0) || t1 != int56(t1)\n   │                                                                                             ━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int56' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/TickMath.sol:54:24\n   │\n54 │         sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1));\n   │                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint160' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/PoolOracle.sol:48:18\n   │\n48 │             dt = int56(t1) - int56(t0);\n   │                  ━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int56' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/PoolOracle.sol:48:30\n   │\n48 │             dt = int56(t1) - int56(t0);\n   │                              ━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int56' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/PoolOracle.sol:49:18\n   │\n49 │             ds = uint160(s1) - uint160(s0);\n   │                  ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint160' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/PoolOracle.sol:49:32\n   │\n49 │             ds = uint160(s1) - uint160(s0);\n   │                                ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint160' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/PoolOracle.sol:57:23\n   │\n57 │         return (true, int24(tick), uint128(liq));\n   │                       ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int24' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/PoolOracle.sol:57:36\n   │\n57 │         return (true, int24(tick), uint128(liq));\n   │                                    ━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint128' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/BaskVault.sol:313:65\n    │\n313 │     function transferOwnership(address next) external onlyOwner nonReentrant {\n    │                                                                 ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/BaskVault.sol:327:48\n    │\n327 │     function pauseDeposits() external onlyRole nonReentrant {\n    │                                                ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/BaskVault.sol:332:51\n    │\n332 │     function unpauseDeposits() external onlyOwner nonReentrant {\n    │                                                   ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/BaskVault.sol:337:53\n    │\n337 │     function close(address token) external onlyRole nonReentrant {\n    │                                                     ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/BaskVault.sol:344:58\n    │\n344 │     function lowerNAVCap(uint256 cap) external onlyOwner nonReentrant {\n    │                                                          ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/BaskVault.sol:354:9\n    │\n354 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/BaskVault.sol:360:51\n    │\n360 │     function finalizeGenesis() external onlyOwner nonReentrant {\n    │                                                   ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/BaskVault.sol:385:65\n    │\n385 │     function propose(Action calldata action) external onlyOwner nonReentrant returns (uint256 id) {\n    │                                                                 ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:390:9\n    │\n390 │         emit Proposed(id, action, block.timestamp + 2 days, block.timestamp + 9 days);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/BaskVault.sol:393:51\n    │\n393 │     function cancel(uint256 id) external onlyRole nonReentrant {\n    │                                                   ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/BaskVault.sol:400:53\n    │\n400 │     function execute(uint256 id) external onlyOwner nonReentrant {\n    │                                                     ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/BaskVault.sol:403:13\n    │\n403 │         if (block.timestamp < p.createdAt + 2 days) revert Timelock();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:522:9\n    │\n522 │         emit AssetListed(token, feed);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:411:13\n    │\n411 │             emit GuardianChanged(a.target);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:414:13\n    │\n414 │             emit CapChanged(a.value);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:417:13\n    │\n417 │             emit FeeRecipientChanged(a.target);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:420:13\n    │\n420 │             emit SettingChanged(a.setting, a.value, a.value2);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:444:17\n    │\n444 │                 emit Resynced(a.token, extra);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:446:13\n    │\n446 │             emit AssetChanged(a.token, a.kind);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:448:9\n    │\n448 │         emit ProposalExecuted(id);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/BaskVault.sol:481:23\n    │\n481 │         if (p.done || block.timestamp >= p.createdAt + 9 days) return false;\n    │                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/BaskVault.sol:538:29\n    │\n538 │                 || (address(uint160(t0)) != token && address(uint160(t1)) != token)\n    │                             ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/BaskVault.sol:538:62\n    │\n538 │                 || (address(uint160(t0)) != token && address(uint160(t1)) != token)\n    │                                                              ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/BaskVault.sol:540:32\n    │\n540 │         baseIsToken0 = address(uint160(t0)) == token;\n    │                                ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/BaskVault.sol:541:32\n    │\n541 │         qd = _decimals(address(uint160(baseIsToken0 ? t1 : t0)));\n    │                                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/BaskVault.sol:555:16\n    │\n555 │         return uint8(d);\n    │                ━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/BaskVault.sol:633:13\n    │\n633 │             value += _value(amounts[i], answers[indexPlusOne[a.token] - 1], a.tokenDecimals, a.feedDecimals);\n    │             ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/BaskVault.sol:624:13\n    │\n624 │         if (block.timestamp > deadline) revert Deadline();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:662:9\n    │\n662 │         emit Deposit(msg.sender, receiver, value, shares, fee);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/BaskVault.sol:631:34\n    │\n631 │             if (amounts[i] == 0) revert InvalidInput();\n    │                                  ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/libraries/FullMath.sol:33:39\n   │\n33 │                 if (denominator == 0) revert MathOverflow();\n   │                                       ━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/libraries/FullMath.sol:42:39\n   │\n42 │             if (denominator <= prod1) revert MathOverflow();\n   │                                       ━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/BaskVault.sol:787:18\n    │\n787 │         if (!ok) revert BalanceUnreadable(token);\n    │                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/BaskVault.sol:648:17\n    │\n648 │                 revert PaymentFailed(token);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/BaskVault.sol:652:17\n    │\n652 │                 revert PaymentFailed(token);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/BaskVault.sol:715:45\n    │\n715 │             if (i < minAmountsOut.length && leg < minAmountsOut[i]) revert Slippage();\n    │                                             ━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/BaskVault.sol:687:13\n    │\n687 │         if (block.timestamp > deadline) revert Deadline();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:729:9\n    │\n729 │         emit Redeem(msg.sender, receiver, shares, net, fee, amounts);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/BaskVault.sol:704:72\n    │\n704 │                 if (i < minAmountsOut.length && minAmountsOut[i] != 0) revert Slippage();\n    │                                                                        ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/BaskVault.sol:715:69\n    │\n715 │             if (i < minAmountsOut.length && leg < minAmountsOut[i]) revert Slippage();\n    │                                                                     ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:742:9\n    │\n742 │         emit Paid(token, to, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:765:13\n    │\n765 │             emit Claimed(msg.sender, to, token, amount);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/BaskVault.sol:762:65\n    │\n762 │                     if (!_tryPay(token, to, amount, gasleft())) revert PaymentFailed(token);\n    │                                                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:825:9\n    │\n825 │         emit DeficitFlagged(token, deficits[token].amount, deficits[token].since);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/BaskVault.sol:831:30\n    │\n831 │         if (d.amount == 0 || block.timestamp < d.since + 7 days) revert Timelock();\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:836:9\n    │\n836 │         emit LossRecognized(token, loss);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/BaskVault.sol:855:40\n    │\n855 │         ok = ok && signedAnswer > 0 && updatedAt <= block.timestamp && block.timestamp - updatedAt <= cfg.maxAge;\n    │                                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/BaskVault.sol:855:72\n    │\n855 │         ok = ok && signedAnswer > 0 && updatedAt <= block.timestamp && block.timestamp - updatedAt <= cfg.maxAge;\n    │                                                                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/BaskVault.sol:854:40\n    │\n854 │         if (signedAnswer > 0) answer = uint256(signedAnswer);\n    │                                        ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/BaskVault.sol:876:17\n    │\n876 │             if (block.timestamp - updatedAt > cfg.noPoolAge) reason = Reason.NoPoolAge;\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/BaskVault.sol:880:58\n    │\n880 │             (ok, tick, liq) = PoolOracle.consult(a.pool, uint32(cfg.poolWindow), cfg.poolGas);\n    │                                                          ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/BaskVault.sol:944:82\n    │\n944 │             if (ok && block.timestamp - updatedAt <= cfg.freshHours * 1 hours) ++fresh;\n    │                                                                                  ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/BaskVault.sol:944:23\n    │\n944 │             if (ok && block.timestamp - updatedAt <= cfg.freshHours * 1 hours) ++fresh;\n    │                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/libraries/TickMath.sol:26:50\n   │\n26 │         if (absTick > uint256(int256(MAX_TICK))) revert InvalidTick();\n   │                                                  ━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/BaskVault.sol:998:34\n    │\n998 │             if (amounts[i] == 0) revert InvalidInput();\n    │                                  ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[uninitialized-local]: local variable is read before being initialized\n     ╭▸ src/BaskVault.sol:1017:15\n     │\n1017 │             ++active;\n     │               ━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n     ╭▸ src/BaskVault.sol:1038:34\n     │\n1038 │             if (_pending(i)) ids[count++] = i;\n     │                                  ━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\n","passed":true},{"durationMs":31741,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 2 tests for test/BasketAssetBoundary.t.sol:BasketAssetBoundaryTest\n[PASS] test250MixedTokenFailuresAndRetirementStayBelowGasCeiling() (gas: 405974705)\nLogs:\n  mixed asset redemption including intrinsic gas: 18249457\n\n[PASS] testSparseAssetsAcrossBothBitmapWordsSurviveRemovalAndResync() (gas: 478467704)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 238.44ms (426.91ms CPU time)\n\nRan 12 tests for test/Redemption.t.sol:RedemptionTest\n[PASS] testClaimHasNoPayGasLimitAndFailedClaimKeepsDebt() (gas: 763699)\n[PASS] testEveryPauseAndRoleRestrictionLeavesRedemptionAndClaimOpen() (gas: 915733)\n[PASS] testFuzzBrokenTransferRollsBackAndBecomesOwed(uint8) (runs: 256, μ: 650255, ~: 613713)\nLogs:\n  Bound result 1\n\n[PASS] testNoReturnAndLargeReturnTransfersPayDirectly() (gas: 1006080)\n[PASS] testPartialClaimUsesBalanceAndCanRedirectReceiver() (gas: 1170577)\n[PASS] testQueuedDebtExcludedFromRedemptionResyncAndDeposits() (gas: 1927946)\n[PASS] testRedemptionCallerInputsAndMinimumsIncludingZeroManagedLeg() (gas: 655931)\n[PASS] testReentrancyOnPullPaymentAndClaimAndPayOnlySelf() (gas: 1097338)\n[PASS] testRoleCannotUseBalanceGasOrPayGasToBlockClaim() (gas: 2212489)\n[PASS] testShortRedemptionNeverWritesDownResidualManaged() (gas: 519427)\n[PASS] testTokenImplementationDisappearsAfterDeposit() (gas: 502236)\n[PASS] testUnreadableUpgradedBalanceUsesManagedAndDoesNotTouchOracle() (gas: 1020219)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 333.76ms (146.91ms CPU time)\n\nRan 15 tests for test/Governance.t.sol:GovernanceTest\n[PASS] testCapLoweringInvalidatesAllPendingRaises() (gas: 715882)\n[PASS] testExpiryAndCancellation() (gas: 522336)\n[PASS] testFeedAndRecentreUseExecutionAnswer() (gas: 1222463)\n[PASS] testGenesisNeedsThreeAndOnlyOnce() (gas: 959710)\n[PASS] testGuardianCannotCancelReplacementAndRolesRemainDistinct() (gas: 411720)\n[PASS] testLaterCloseVoidsReopenEvenIfAlreadyClosed() (gas: 712023)\n[PASS] testListingChecksAtProposalAndExecution() (gas: 1868804)\n[PASS] testLossClearsManagedBitAndResyncRestoresIt() (gas: 1166698)\n[PASS] testOnlyOwnerProposesExecutesAndTwoDayWindow() (gas: 423942)\n[PASS] testRemoveRelistAndReuseRetiredFeed() (gas: 1388135)\n[PASS] testRemovingEmptyAssetPreservesMovedManagedAssetAndMinimumOrder() (gas: 1005875)\n[PASS] testRetireVoidsProposalsSkipsAllDepositChecksAndSharesTokens() (gas: 3170738)\n[PASS] testRetirementRequiresClosedAtProposalAndExecution() (gas: 698500)\n[PASS] testSettingBoundsAndBothGasConstraintsRechecked() (gas: 1128463)\n[PASS] testTwoStepOwnershipAndGuardianCannotUnpause() (gas: 318585)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 345.85ms (8.62ms CPU time)\n\nRan 2 tests for test/NewYorkTime.t.sol:NewYorkTimeTest\n[PASS] testDefaultWeeklyHoursAndAlwaysOpen() (gas: 505084)\n[PASS] testUSDaylightBoundaries2026Through2040() (gas: 374013)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 345.87ms (1.82ms CPU time)\n\nRan 14 tests for test/BasketAdversarial.t.sol:BasketAdversarialTest\n[PASS] testClaimBatchFailureRevertsEarlierPaymentsAndDuplicateClaimsPayOnce() (gas: 1986390)\n[PASS] testEveryBoundedSettingRejectsValuesOutsideItsRange() (gas: 2680166)\n[PASS] testEveryPrivilegedEntryRejectsAnUnrelatedCaller() (gas: 536337)\n[PASS] testFeeRecipientRedeemsItsOwnSharesWithoutSupplyOrValueCreation() (gas: 914100)\n[PASS] testFirstDepositAtLockBoundaryAndOneUnitAbove() (gas: 586248)\n[PASS] testFuzzAnyLegalSettingPreservesRedemptionAndClaim(uint8,uint256) (runs: 1000, μ: 2251970, ~: 2227460)\nLogs:\n  Bound result 1\n\n[PASS] testFuzzRepeatedCyclesCannotExtractRoundingDust(uint96,uint8,bool) (runs: 1000, μ: 4207591, ~: 3591850)\nLogs:\n  Bound result 134370582156522497\n  Bound result 3\n\n[PASS] testFuzzUnaccountedDonationCannotInflateDepositPrice(uint128) (runs: 1000, μ: 957555, ~: 957566)\n[PASS] testGuardianCannotGainOwnerOnlyPowers() (gas: 115793)\n[PASS] testInvalidPoolPairAndNoPoolResidualConfigAreRejected() (gas: 1167685)\n[PASS] testLateRedemptionSlippageRestoresEarlierPaymentFeeAndBurn() (gas: 1249219)\n[PASS] testListingExecutionRechecksDecimalsAndFeedUniqueness() (gas: 2746009)\n[PASS] testProposalLastExecutableSecondAndFirstExpiredSecond() (gas: 435266)\n[PASS] testZeroAndEmptyOperationsCannotCreateSharesOrDebt() (gas: 286567)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 345.87ms (927.46ms CPU time)\n\nRan 10 tests for test/Oracle.t.sol:OracleTest\n[PASS] testConsultNegativeRoundingAndCumulativeWrap() (gas: 1513332)\n[PASS] testEveryHeldPriceAndEveryUnretiredBalanceRequired() (gas: 890383)\n[PASS] testFreshnessAndDuplicateUnlistedClosedReasons() (gas: 585592)\n[PASS] testFuzzValueAcrossTokenAndFeedDecimals(uint8,uint8) (runs: 256, μ: 2116634, ~: 2123151)\nLogs:\n  Bound result 0\n  Bound result 3\n\n[PASS] testInvalidFeedAnswersFutureStaleAndBoundedFailures() (gas: 1077350)\n[PASS] testNoPauseInterfaceAtListingDoesNotRequireItLater() (gas: 1855038)\n[PASS] testPoolAllowsMaxAgeAndRemovingPoolRestoresNoPoolAge() (gas: 3024110)\n[PASS] testPoolLiquidityQuotePriceDeviationAndStaleness() (gas: 4012046)\n[PASS] testPoolOrientationAndDifferentDecimals() (gas: 4431251)\n[PASS] testPriceBandBoundariesAndPauseProbe() (gas: 1165199)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 345.91ms (283.52ms CPU time)\n\nRan 15 tests for test/BaskVault.t.sol:BaskVaultTest\n[PASS] testCapNoShareSupplyCap() (gas: 1011782)\n[PASS] testConstructorRoles() (gas: 4024)\n[PASS] testDeploymentDefaultsAndRuntime() (gas: 10024245)\n[PASS] testDepositAfterPriceChangeRoundsGrossDown() (gas: 706358)\n[PASS] testDepositMintPreviewDonationAndRedeem() (gas: 1128390)\n[PASS] testDepositSlippageDeadlinesAndExactPull() (gas: 931608)\n[PASS] testFeeRoundingMintTransferAndBurn() (gas: 1619864)\n[PASS] testFuzzConservation(uint96,uint96) (runs: 256, μ: 552983, ~: 555108)\nLogs:\n  Bound result 4999990000000000000217\n  Bound result 19333143015816092266129\n\n[PASS] testLargerDeficitRestartsAndDepositsClearRecords() (gas: 1635310)\n[PASS] testLossDelayPartialRecoveryAndNoAutomaticWriteDown() (gas: 752461)\n[PASS] testMultiplePullsAreAtomic() (gas: 419027)\n[PASS] testResyncOnlyCountsSurplusAndDoesNotRecognizeLoss() (gas: 1147334)\n[PASS] testShareERC20() (gas: 726722)\n[PASS] testStandardTransferEventTopic() (gas: 431950)\n[PASS] testZeroNAVCannotMintAgainstExistingShares() (gas: 1208126)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 346.00ms (349.33ms CPU time)\n\nRan 8 tests for test/RedemptionGas.t.sol:RedemptionGasTest\n[PASS] testGas250BlockedTokensWithExpensiveReadableBalances() (gas: 463761331)\nLogs:\n  cold redemption gas: 26402204\n  including transaction calldata: 26477316\n\n[PASS] testGas250UnreadableUpgradedTokens() (gas: 463855905)\nLogs:\n  cold redemption gas: 26699204\n  including transaction calldata: 26774316\n\n[PASS] testGas50DirectPaymentsConsumeAllPayGasAmong250Assets() (gas: 410717246)\nLogs:\n  cold redemption gas: 18084342\n  including transaction calldata: 18142654\n\n[PASS] testGasMaximumAssetCount350AtMinimumBalanceGas() (gas: 628897158)\nLogs:\n  cold redemption gas: 26859576\n  including transaction calldata: 26960100\n\n[PASS] testGasMaximumBalanceGasAtMaximumAllowedAssetCount50() (gas: 139523161)\nLogs:\n  cold redemption gas: 27915169\n  including transaction calldata: 27947881\n\n[PASS] testGasMaximumBalancesUseFullPrecisionMath() (gas: 153096546)\nLogs:\n  cold redemption gas: 27942869\n  including transaction calldata: 27990581\n\n[PASS] testGasMaximumDirectCountAtMinimumCallBudgets() (gas: 608833028)\nLogs:\n  cold redemption gas: 24578492\n  including transaction calldata: 24669800\n\n[PASS] testGasMaximumPayGasWithManyEmptyAssets() (gas: 571673800)\nLogs:\n  cold redemption gas: 27467667\n  including transaction calldata: 27539103\n\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 801.79ms (3.74s CPU time)\n\nRan 2 tests for test/BasketInvariant.t.sol:BasketInvariantTest\n[PASS]\nBasketInvariantTest invariants:\n[PASS] invariant_custodyAndManagedMatchIndependentFlows\n[PASS] invariant_everyActorCanRedeemDespiteCurrentFailures\n[PASS] invariant_shareAndDebtTotalsEqualTheirOwners\n BasketInvariantTest invariants (runs: 256, calls: 24576, reverts: 0)\n\n╭---------------+----------------+-------+---------+----------╮\n| Contract      | Selector       | Calls | Reverts | Discards |\n+=============================================================+\n| BasketHandler | claim          | 2237  | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| BasketHandler | confiscate     | 2338  | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| BasketHandler | deposit        | 2250  | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| BasketHandler | donate         | 2231  | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| BasketHandler | governance     | 2182  | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| BasketHandler | loss           | 2185  | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| BasketHandler | redeem         | 4439  | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| BasketHandler | resync         | 2276  | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| BasketHandler | tokenFailure   | 2194  | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| BasketHandler | transferShares | 2244  | 0       | 0        |\n╰---------------+----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 10000000000000000000\n  Bound result 10000000000000000000\n  Bound result 10000000000000000000\n  Bound result 100000000000000000000\n  Bound result 49999900000000013347\n  Bound result 365\n  Bound result 49999900000000011869\n  Bound result 769024447680673579884\n  Bound result 249\n  Bound result 0\n  Bound result 404098526\n  Bound result 5451\n  Bound result 5787\n  Bound result 29464437282727236328\n  Bound result 39414508288436726216\n  Bound result 3559652013724397720\n  Bound result 4160028693900969862\n  Bound result 1000010000000000000\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 25200\n  Bound result 0\n  Bound result 0\n  Bound result 9140\n  Bound result 1994310000\n  Bound result 12733\n  Bound result 49999900000000345601\n  Bound result 748388536689959849769\n  Bound result 609269858327034413\n  Bound result 15814\n  Bound result 0\n  Bound result 2500000000\n  Bound result 100000000000000\n  Bound result 117517\n  Bound result 280375465082879\n  Bound result 596953025\n  Bound result 43767618634104251\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 1\n  Bound result 10884\n  Bound result 476\n  Bound result 426\n  Bound result 537406\n  Bound result 0\n  Bound result 0\n  Bound result 308799\n  Bound result 0\n  Bound result 2088658800\n  Bound result 0\n  Bound result 0\n  Bound result 20573\n  Bound result 1368\n  Bound result 604799\n  Bound result 4595\n  Bound result 0\n  Bound result 604800\n  Bound result 0\n  Bound result 16149\n  Bound result 1772953200\n  Bound result 16282274070067730\n  Bound result 457304\n\n[PASS] testHandlerExercisesCustodyDebtLossAndRecovery() (gas: 1707693)\nLogs:\n  Bound result 10000000000000000000\n  Bound result 10000000000000000000\n  Bound result 10000000000000000000\n  Bound result 100000000000000000000\n  Bound result 2000000000000000000\n  Bound result 1000000000000000000\n  Bound result 604800\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 31.65s (31.64s CPU time)\n\nRan 9 test suites in 31.65s (34.75s CPU time): 80 tests passed, 0 failed, 0 skipped (80 total tests)\n","passed":true},{"durationMs":38,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"BaskVault.acceptOwnership()\",\"BaskVault.approve(address,uint256)\",\"BaskVault.cancel(uint256)\",\"BaskVault.claim(address[],address)\",\"BaskVault.close(address)\",\"BaskVault.deposit(address[],uint256[],address,uint256,uint256)\",\"BaskVault.execute(uint256)\",\"BaskVault.finalizeGenesis()\",\"BaskVault.flagDeficit(address)\",\"BaskVault.listGenesis(address,address,address,address,uint256)\",\"BaskVault.lowerNAVCap(uint256)\",\"BaskVault.pauseDeposits()\",\"BaskVault.pay(address,address,uint256)\",\"BaskVault.propose((uint8,address,address,address,address,uint256,uint256,uint8))\",\"BaskVault.recognizeLoss(address)\",\"BaskVault.redeem(uint256,address,uint256[],uint256)\",\"BaskVault.removeRetired(address)\",\"BaskVault.transfer(address,uint256)\",\"BaskVault.transferFrom(address,address,uint256)\",\"BaskVault.transferOwnership(address)\",\"BaskVault.unpauseDeposits()\"],\"files\":{\".gitignore\":3,\"LICENSE\":338,\"README.md\":134,\"REVIEW.md\":47,\"THIRD_PARTY.md\":10,\"foundry.toml\":13,\"launch.json\":13,\"remappings.txt\":1,\"src/BaskVault.sol\":1042,\"src/libraries/BoundedCall.sol\":34,\"src/libraries/FullMath.sol\":107,\"src/libraries/NewYorkTime.sol\":34,\"src/libraries/PoolOracle.sol\":69,\"src/libraries/TickMath.sol\":56,\"test/BASKET_TESTS.md\":51,\"test/BaskVault.t.sol\":265,\"test/BasketAdversarial.t.sol\":328,\"test/BasketAssetBoundary.t.sol\":163,\"test/BasketInvariant.t.sol\":353,\"test/Governance.t.sol\":325,\"test/NewYorkTime.t.sol\":78,\"test/Oracle.t.sol\":221,\"test/Redemption.t.sol\":200,\"test/RedemptionGas.t.sol\":166,\"test/VaultTestBase.sol\":101,\"test/mocks/Mocks.sol\":238},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"3cab3336f4e2fca9fb171834fd3f8f2a7a532327e2ba29d766c0f901a205e8d2","verifiedTreeHash":"40c4655b2ce44778e251370d9517bf7f2a9ef47d","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":20429,"exitCode":0,"name":"build","output":"Compiling 33 files with Solc 0.8.26\nSolc 0.8.26 finished in 20.13s\nCompiler run successful!\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/BaskVault.sol:532:27\n    │\n532 │             return (0, 0, false);\n    │                           ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[boolean-cst]: misuse of a boolean constant\n   ╭▸ src/libraries/PoolOracle.sol:20:26\n   │\n20 │         if (!ok) return (false, 0, 0);\n   │                          ━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[boolean-cst]: misuse of a boolean constant\n   ╭▸ src/libraries/PoolOracle.sol:43:21\n   │\n43 │             return (false, 0, 0);\n   │                     ━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/TickMath.sol:25:38\n   │\n25 │         uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick));\n   │                                      ━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[boolean-cst]: misuse of a boolean constant\n   ╭▸ src/libraries/PoolOracle.sol:51:30\n   │\n51 │         if (ds == 0) return (false, 0, 0);\n   │                              ━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/TickMath.sol:25:63\n   │\n25 │         uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick));\n   │                                                               ━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[boolean-cst]: misuse of a boolean constant\n   ╭▸ src/libraries/PoolOracle.sol:54:54\n   │\n54 │         if (tick < -887272 || tick > 887272) return (false, 0, 0);\n   │                                                      ━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[boolean-cst]: misuse of a boolean constant\n   ╭▸ src/libraries/PoolOracle.sol:56:46\n   │\n56 │         if (liq > type(uint128).max) return (false, 0, 0);\n   │                                              ━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[boolean-cst]: misuse of a boolean constant\n   ╭▸ src/libraries/PoolOracle.sol:57:17\n   │\n57 │         return (true, int24(tick), uint128(liq));\n   │                 ━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/TickMath.sol:26:23\n   │\n26 │         if (absTick > uint256(int256(MAX_TICK))) revert InvalidTick();\n   │                       ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/BaskVault.sol:792:26\n    │\n792 │         if (!ok) return (false, 0);\n    │                          ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/TickMath.sol:54:24\n   │\n54 │         sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1));\n   │                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint160' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/PoolOracle.sol:40:74\n   │\n40 │             off0 != 64 || off1 != 160 || len0 != 2 || len1 != 2 || t0 != int56(t0) || t1 != int56(t1)\n   │                                                                          ━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int56' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/PoolOracle.sol:40:93\n   │\n40 │             off0 != 64 || off1 != 160 || len0 != 2 || len1 != 2 || t0 != int56(t0) || t1 != int56(t1)\n   │                                                                                             ━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int56' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/PoolOracle.sol:48:18\n   │\n48 │             dt = int56(t1) - int56(t0);\n   │                  ━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int56' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/PoolOracle.sol:48:30\n   │\n48 │             dt = int56(t1) - int56(t0);\n   │                              ━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int56' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/PoolOracle.sol:49:18\n   │\n49 │             ds = uint160(s1) - uint160(s0);\n   │                  ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint160' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/PoolOracle.sol:49:32\n   │\n49 │             ds = uint160(s1) - uint160(s0);\n   │                                ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint160' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/PoolOracle.sol:57:23\n   │\n57 │         return (true, int24(tick), uint128(liq));\n   │                       ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int24' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/PoolOracle.sol:57:36\n   │\n57 │         return (true, int24(tick), uint128(liq));\n   │                                    ━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint128' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/BaskVault.sol:313:65\n    │\n313 │     function transferOwnership(address next) external onlyOwner nonReentrant {\n    │                                                                 ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/BaskVault.sol:327:48\n    │\n327 │     function pauseDeposits() external onlyRole nonReentrant {\n    │                                                ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/BaskVault.sol:332:51\n    │\n332 │     function unpauseDeposits() external onlyOwner nonReentrant {\n    │                                                   ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/BaskVault.sol:337:53\n    │\n337 │     function close(address token) external onlyRole nonReentrant {\n    │                                                     ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/BaskVault.sol:344:58\n    │\n344 │     function lowerNAVCap(uint256 cap) external onlyOwner nonReentrant {\n    │                                                          ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/BaskVault.sol:354:9\n    │\n354 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/BaskVault.sol:360:51\n    │\n360 │     function finalizeGenesis() external onlyOwner nonReentrant {\n    │                                                   ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/BaskVault.sol:385:65\n    │\n385 │     function propose(Action calldata action) external onlyOwner nonReentrant returns (uint256 id) {\n    │                                                                 ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:390:9\n    │\n390 │         emit Proposed(id, action, block.timestamp + 2 days, block.timestamp + 9 days);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/BaskVault.sol:393:51\n    │\n393 │     function cancel(uint256 id) external onlyRole nonReentrant {\n    │                                                   ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/BaskVault.sol:400:53\n    │\n400 │     function execute(uint256 id) external onlyOwner nonReentrant {\n    │                                                     ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/BaskVault.sol:403:13\n    │\n403 │         if (block.timestamp < p.createdAt + 2 days) revert Timelock();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:522:9\n    │\n522 │         emit AssetListed(token, feed);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:411:13\n    │\n411 │             emit GuardianChanged(a.target);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:414:13\n    │\n414 │             emit CapChanged(a.value);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:417:13\n    │\n417 │             emit FeeRecipientChanged(a.target);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:420:13\n    │\n420 │             emit SettingChanged(a.setting, a.value, a.value2);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:444:17\n    │\n444 │                 emit Resynced(a.token, extra);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:446:13\n    │\n446 │             emit AssetChanged(a.token, a.kind);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:448:9\n    │\n448 │         emit ProposalExecuted(id);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/BaskVault.sol:481:23\n    │\n481 │         if (p.done || block.timestamp >= p.createdAt + 9 days) return false;\n    │                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/BaskVault.sol:538:29\n    │\n538 │                 || (address(uint160(t0)) != token && address(uint160(t1)) != token)\n    │                             ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/BaskVault.sol:538:62\n    │\n538 │                 || (address(uint160(t0)) != token && address(uint160(t1)) != token)\n    │                                                              ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/BaskVault.sol:540:32\n    │\n540 │         baseIsToken0 = address(uint160(t0)) == token;\n    │                                ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/BaskVault.sol:541:32\n    │\n541 │         qd = _decimals(address(uint160(baseIsToken0 ? t1 : t0)));\n    │                                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/BaskVault.sol:555:16\n    │\n555 │         return uint8(d);\n    │                ━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/BaskVault.sol:633:13\n    │\n633 │             value += _value(amounts[i], answers[indexPlusOne[a.token] - 1], a.tokenDecimals, a.feedDecimals);\n    │             ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/BaskVault.sol:624:13\n    │\n624 │         if (block.timestamp > deadline) revert Deadline();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:662:9\n    │\n662 │         emit Deposit(msg.sender, receiver, value, shares, fee);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/BaskVault.sol:631:34\n    │\n631 │             if (amounts[i] == 0) revert InvalidInput();\n    │                                  ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/libraries/FullMath.sol:33:39\n   │\n33 │                 if (denominator == 0) revert MathOverflow();\n   │                                       ━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/libraries/FullMath.sol:42:39\n   │\n42 │             if (denominator <= prod1) revert MathOverflow();\n   │                                       ━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/BaskVault.sol:787:18\n    │\n787 │         if (!ok) revert BalanceUnreadable(token);\n    │                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/BaskVault.sol:648:17\n    │\n648 │                 revert PaymentFailed(token);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/BaskVault.sol:652:17\n    │\n652 │                 revert PaymentFailed(token);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/BaskVault.sol:715:45\n    │\n715 │             if (i < minAmountsOut.length && leg < minAmountsOut[i]) revert Slippage();\n    │                                             ━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/BaskVault.sol:687:13\n    │\n687 │         if (block.timestamp > deadline) revert Deadline();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:729:9\n    │\n729 │         emit Redeem(msg.sender, receiver, shares, net, fee, amounts);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/BaskVault.sol:704:72\n    │\n704 │                 if (i < minAmountsOut.length && minAmountsOut[i] != 0) revert Slippage();\n    │                                                                        ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/BaskVault.sol:715:69\n    │\n715 │             if (i < minAmountsOut.length && leg < minAmountsOut[i]) revert Slippage();\n    │                                                                     ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:742:9\n    │\n742 │         emit Paid(token, to, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:765:13\n    │\n765 │             emit Claimed(msg.sender, to, token, amount);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/BaskVault.sol:762:65\n    │\n762 │                     if (!_tryPay(token, to, amount, gasleft())) revert PaymentFailed(token);\n    │                                                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:825:9\n    │\n825 │         emit DeficitFlagged(token, deficits[token].amount, deficits[token].since);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/BaskVault.sol:831:30\n    │\n831 │         if (d.amount == 0 || block.timestamp < d.since + 7 days) revert Timelock();\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/BaskVault.sol:836:9\n    │\n836 │         emit LossRecognized(token, loss);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/BaskVault.sol:855:40\n    │\n855 │         ok = ok && signedAnswer > 0 && updatedAt <= block.timestamp && block.timestamp - updatedAt <= cfg.maxAge;\n    │                                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/BaskVault.sol:855:72\n    │\n855 │         ok = ok && signedAnswer > 0 && updatedAt <= block.timestamp && block.timestamp - updatedAt <= cfg.maxAge;\n    │                                                                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/BaskVault.sol:854:40\n    │\n854 │         if (signedAnswer > 0) answer = uint256(signedAnswer);\n    │                                        ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/BaskVault.sol:876:17\n    │\n876 │             if (block.timestamp - updatedAt > cfg.noPoolAge) reason = Reason.NoPoolAge;\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/BaskVault.sol:880:58\n    │\n880 │             (ok, tick, liq) = PoolOracle.consult(a.pool, uint32(cfg.poolWindow), cfg.poolGas);\n    │                                                          ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/BaskVault.sol:944:82\n    │\n944 │             if (ok && block.timestamp - updatedAt <= cfg.freshHours * 1 hours) ++fresh;\n    │                                                                                  ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/BaskVault.sol:944:23\n    │\n944 │             if (ok && block.timestamp - updatedAt <= cfg.freshHours * 1 hours) ++fresh;\n    │                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/libraries/TickMath.sol:26:50\n   │\n26 │         if (absTick > uint256(int256(MAX_TICK))) revert InvalidTick();\n   │                                                  ━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/BaskVault.sol:998:34\n    │\n998 │             if (amounts[i] == 0) revert InvalidInput();\n    │                                  ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[uninitialized-local]: local variable is read before being initialized\n     ╭▸ src/BaskVault.sol:1017:15\n     │\n1017 │             ++active;\n     │               ━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n     ╭▸ src/BaskVault.sol:1038:34\n     │\n1038 │             if (_pending(i)) ids[count++] = i;\n     │                                  ━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\n","passed":true},{"durationMs":748,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 2 tests for test/NewYorkTime.t.sol:NewYorkTimeTest\n[PASS] testDefaultWeeklyHoursAndAlwaysOpen() (gas: 505084)\n[PASS] testUSDaylightBoundaries2026Through2040() (gas: 374013)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 1.96ms (853.60µs CPU time)\n\nRan 15 tests for test/Governance.t.sol:GovernanceTest\n[PASS] testCapLoweringInvalidatesAllPendingRaises() (gas: 715882)\n[PASS] testExpiryAndCancellation() (gas: 522336)\n[PASS] testFeedAndRecentreUseExecutionAnswer() (gas: 1222463)\n[PASS] testGenesisNeedsThreeAndOnlyOnce() (gas: 959710)\n[PASS] testGuardianCannotCancelReplacementAndRolesRemainDistinct() (gas: 411720)\n[PASS] testLaterCloseVoidsReopenEvenIfAlreadyClosed() (gas: 712023)\n[PASS] testListingChecksAtProposalAndExecution() (gas: 1868804)\n[PASS] testLossClearsManagedBitAndResyncRestoresIt() (gas: 1166698)\n[PASS] testOnlyOwnerProposesExecutesAndTwoDayWindow() (gas: 423942)\n[PASS] testRemoveRelistAndReuseRetiredFeed() (gas: 1388135)\n[PASS] testRemovingEmptyAssetPreservesMovedManagedAssetAndMinimumOrder() (gas: 1005875)\n[PASS] testRetireVoidsProposalsSkipsAllDepositChecksAndSharesTokens() (gas: 3170738)\n[PASS] testRetirementRequiresClosedAtProposalAndExecution() (gas: 698500)\n[PASS] testSettingBoundsAndBothGasConstraintsRechecked() (gas: 1128463)\n[PASS] testTwoStepOwnershipAndGuardianCannotUnpause() (gas: 318585)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 4.03ms (8.00ms CPU time)\n\nRan 12 tests for test/Redemption.t.sol:RedemptionTest\n[PASS] testClaimHasNoPayGasLimitAndFailedClaimKeepsDebt() (gas: 763699)\n[PASS] testEveryPauseAndRoleRestrictionLeavesRedemptionAndClaimOpen() (gas: 915733)\n[PASS] testFuzzBrokenTransferRollsBackAndBecomesOwed(uint8) (runs: 256, μ: 641746, ~: 613710)\nLogs:\n  Bound result 6\n\n[PASS] testNoReturnAndLargeReturnTransfersPayDirectly() (gas: 1006080)\n[PASS] testPartialClaimUsesBalanceAndCanRedirectReceiver() (gas: 1170577)\n[PASS] testQueuedDebtExcludedFromRedemptionResyncAndDeposits() (gas: 1927946)\n[PASS] testRedemptionCallerInputsAndMinimumsIncludingZeroManagedLeg() (gas: 655931)\n[PASS] testReentrancyOnPullPaymentAndClaimAndPayOnlySelf() (gas: 1097338)\n[PASS] testRoleCannotUseBalanceGasOrPayGasToBlockClaim() (gas: 2212489)\n[PASS] testShortRedemptionNeverWritesDownResidualManaged() (gas: 519427)\n[PASS] testTokenImplementationDisappearsAfterDeposit() (gas: 502236)\n[PASS] testUnreadableUpgradedBalanceUsesManagedAndDoesNotTouchOracle() (gas: 1020219)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 36.34ms (26.89ms CPU time)\n\nRan 10 tests for test/Oracle.t.sol:OracleTest\n[PASS] testConsultNegativeRoundingAndCumulativeWrap() (gas: 1513332)\n[PASS] testEveryHeldPriceAndEveryUnretiredBalanceRequired() (gas: 890383)\n[PASS] testFreshnessAndDuplicateUnlistedClosedReasons() (gas: 585592)\n[PASS] testFuzzValueAcrossTokenAndFeedDecimals(uint8,uint8) (runs: 256, μ: 2117726, ~: 2123204)\nLogs:\n  Bound result 6\n  Bound result 1\n\n[PASS] testInvalidFeedAnswersFutureStaleAndBoundedFailures() (gas: 1077350)\n[PASS] testNoPauseInterfaceAtListingDoesNotRequireItLater() (gas: 1855038)\n[PASS] testPoolAllowsMaxAgeAndRemovingPoolRestoresNoPoolAge() (gas: 3024110)\n[PASS] testPoolLiquidityQuotePriceDeviationAndStaleness() (gas: 4012046)\n[PASS] testPoolOrientationAndDifferentDecimals() (gas: 4431251)\n[PASS] testPriceBandBoundariesAndPauseProbe() (gas: 1165199)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 37.95ms (42.75ms CPU time)\n\nRan 15 tests for test/BaskVault.t.sol:BaskVaultTest\n[PASS] testCapNoShareSupplyCap() (gas: 1011782)\n[PASS] testConstructorRoles() (gas: 4024)\n[PASS] testDeploymentDefaultsAndRuntime() (gas: 10024245)\n[PASS] testDepositAfterPriceChangeRoundsGrossDown() (gas: 706358)\n[PASS] testDepositMintPreviewDonationAndRedeem() (gas: 1128390)\n[PASS] testDepositSlippageDeadlinesAndExactPull() (gas: 931608)\n[PASS] testFeeRoundingMintTransferAndBurn() (gas: 1619864)\n[PASS] testFuzzConservation(uint96,uint96) (runs: 256, μ: 553089, ~: 555108)\nLogs:\n  Bound result 4999990000000000020579\n  Bound result 3787\n\n[PASS] testLargerDeficitRestartsAndDepositsClearRecords() (gas: 1635310)\n[PASS] testLossDelayPartialRecoveryAndNoAutomaticWriteDown() (gas: 752461)\n[PASS] testMultiplePullsAreAtomic() (gas: 419027)\n[PASS] testResyncOnlyCountsSurplusAndDoesNotRecognizeLoss() (gas: 1147334)\n[PASS] testShareERC20() (gas: 726722)\n[PASS] testStandardTransferEventTopic() (gas: 431950)\n[PASS] testZeroNAVCannotMintAgainstExistingShares() (gas: 1208126)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 37.98ms (45.15ms CPU time)\n\nRan 8 tests for test/RedemptionGas.t.sol:RedemptionGasTest\n[PASS] testGas250BlockedTokensWithExpensiveReadableBalances() (gas: 463761331)\nLogs:\n  cold redemption gas: 26402204\n  including transaction calldata: 26477316\n\n[PASS] testGas250UnreadableUpgradedTokens() (gas: 463855905)\nLogs:\n  cold redemption gas: 26699204\n  including transaction calldata: 26774316\n\n[PASS] testGas50DirectPaymentsConsumeAllPayGasAmong250Assets() (gas: 410717246)\nLogs:\n  cold redemption gas: 18084342\n  including transaction calldata: 18142654\n\n[PASS] testGasMaximumAssetCount350AtMinimumBalanceGas() (gas: 628897158)\nLogs:\n  cold redemption gas: 26859576\n  including transaction calldata: 26960100\n\n[PASS] testGasMaximumBalanceGasAtMaximumAllowedAssetCount50() (gas: 139523161)\nLogs:\n  cold redemption gas: 27915169\n  including transaction calldata: 27947881\n\n[PASS] testGasMaximumBalancesUseFullPrecisionMath() (gas: 153096546)\nLogs:\n  cold redemption gas: 27942869\n  including transaction calldata: 27990581\n\n[PASS] testGasMaximumDirectCountAtMinimumCallBudgets() (gas: 608833028)\nLogs:\n  cold redemption gas: 24578492\n  including transaction calldata: 24669800\n\n[PASS] testGasMaximumPayGasWithManyEmptyAssets() (gas: 571673800)\nLogs:\n  cold redemption gas: 27467667\n  including transaction calldata: 27539103\n\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 677.49ms (3.03s CPU time)\n\nRan 6 test suites in 678.45ms (795.75ms CPU time): 62 tests passed, 0 failed, 0 skipped (62 total tests)\n","passed":true},{"durationMs":32,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"BaskVault.acceptOwnership()\",\"BaskVault.approve(address,uint256)\",\"BaskVault.cancel(uint256)\",\"BaskVault.claim(address[],address)\",\"BaskVault.close(address)\",\"BaskVault.deposit(address[],uint256[],address,uint256,uint256)\",\"BaskVault.execute(uint256)\",\"BaskVault.finalizeGenesis()\",\"BaskVault.flagDeficit(address)\",\"BaskVault.listGenesis(address,address,address,address,uint256)\",\"BaskVault.lowerNAVCap(uint256)\",\"BaskVault.pauseDeposits()\",\"BaskVault.pay(address,address,uint256)\",\"BaskVault.propose((uint8,address,address,address,address,uint256,uint256,uint8))\",\"BaskVault.recognizeLoss(address)\",\"BaskVault.redeem(uint256,address,uint256[],uint256)\",\"BaskVault.removeRetired(address)\",\"BaskVault.transfer(address,uint256)\",\"BaskVault.transferFrom(address,address,uint256)\",\"BaskVault.transferOwnership(address)\",\"BaskVault.unpauseDeposits()\"],\"files\":{\".gitignore\":3,\"LICENSE\":338,\"README.md\":134,\"REVIEW.md\":47,\"THIRD_PARTY.md\":10,\"foundry.toml\":13,\"launch.json\":13,\"remappings.txt\":1,\"src/BaskVault.sol\":1042,\"src/libraries/BoundedCall.sol\":34,\"src/libraries/FullMath.sol\":107,\"src/libraries/NewYorkTime.sol\":34,\"src/libraries/PoolOracle.sol\":69,\"src/libraries/TickMath.sol\":56,\"test/BaskVault.t.sol\":265,\"test/Governance.t.sol\":325,\"test/NewYorkTime.t.sol\":78,\"test/Oracle.t.sol\":221,\"test/Redemption.t.sol\":200,\"test/RedemptionGas.t.sol\":166,\"test/VaultTestBase.sol\":101,\"test/mocks/Mocks.sol\":238},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":4003,"exitCode":0,"name":"slither","output":"[high/medium] weak-prng at src/BaskVault.sol:677: BaskVault._fee(uint256) (src/BaskVault.sol#677-680) uses a weak PRNG: \"amount % 200 == 0 (src/BaskVault.sol#679)\"\n[medium/high] incorrect-equality at src/BaskVault.sol:665: BaskVault._depositShares(uint256,uint256) (src/BaskVault.sol#665-675) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/BaskVault.sol:682: BaskVault.redeem(uint256,address,uint256[],uint256) (src/BaskVault.sol#682-730) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/BaskVault.sol:617: BaskVault.deposit(address[],uint256[],address,uint256,uint256) (src/BaskVault.sol#617-663) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/BaskVault.sol:665: BaskVault._depositShares(uint256,uint256) (src/BaskVault.sol#665-675) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/BaskVault.sol:682: BaskVault.redeem(uint256,address,uint256[],uint256) (src/BaskVault.sol#682-730) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/BaskVault.sol:986: BaskVault.previewDeposit(address[],uint256[]) (src/BaskVault.sol#986-1005) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/BaskVault.sol:677: BaskVault._fee(uint256) (src/BaskVault.sol#677-680) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/BaskVault.sol:665: BaskVault._depositShares(uint256,uint256) (src/BaskVault.sol#665-675) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/BaskVault.sol:1007: BaskVault.previewRedeem(uint256) (src/BaskVault.sol#1007-1024) uses a dangerous strict equality:\n[medium/medium] uninitialized-local at src/BaskVault.sol:970: BaskVault.allAssets().v (src/BaskVault.sol#970) is a local variable never initialized\n[medium/medium] uninitialized-local at src/BaskVault.sol:699: BaskVault.redeem(uint256,address,uint256[],uint256).bits (src/BaskVault.sol#699) is a local variable never initialized\n[medium/medium] uninitialized-local at src/BaskVault.sol:1012: BaskVault.previewRedeem(uint256).active (src/BaskVault.sol#1012) is a local variable never initialized\n[medium/medium] uninitialized-local at src/BaskVault.sol:923: BaskVault._depositState(address[]).fresh (src/BaskVault.sol#923) is a local variable never initialized\n[medium/medium] uninitialized-local at src/BaskVault.sol:629: BaskVault.deposit(address[],uint256[],address,uint256,uint256).value (src/BaskVault.sol#629) is a local variable never initialized\n[medium/medium] uninitialized-local at src/BaskVault.sol:709: BaskVault.redeem(uint256,address,uint256[],uint256).leg (src/BaskVault.sol#709) is a local variable never initialized\n[low/medium] timestamp at src/BaskVault.sol:478: BaskVault._pending(uint256) (src/BaskVault.sol#478-487) uses timestamp for comparisons\n[low/medium] timestamp at src/BaskVault.sol:828: BaskVault.recognizeLoss(address) (src/BaskVault.sol#828-837) uses timestamp for comparisons\n[low/medium] timestamp at src/BaskVault.sol:682: BaskVault.redeem(uint256,address,uint256[],uint256) (src/BaskVault.sol#682-730) uses timestamp for comparisons\n[low/medium] timestamp at src/BaskVault.sol:617: BaskVault.deposit(address[],uint256[],address,uint256,uint256) (src/BaskVault.sol#617-663) uses timestamp for comparisons\n[low/medium] timestamp at src/BaskVault.sol:905: BaskVault._depositState(address[]) (src/BaskVault.sol#905-948) uses timestamp for comparisons\n[low/medium] timestamp at src/BaskVault.sol:858: BaskVault._price(BaskVault.Asset) (src/BaskVault.sol#858-891) uses timestamp for comparisons\n[low/medium] timestamp at src/BaskVault.sol:966: BaskVault.allAssets() (src/BaskVault.sol#966-980) uses timestamp for comparisons\n[low/medium] timestamp at src/BaskVault.sol:400: BaskVault.execute(uint256) (src/BaskVault.sol#400-449) uses timestamp for comparisons\n[low/medium] timestamp at src/BaskVault.sol:665: BaskVault._depositShares(uint256,uint256) (src/BaskVault.sol#665-675) uses timestamp for comparisons\n[low/medium] timestamp at src/BaskVault.sol:677: BaskVault._fee(uint256) (src/BaskVault.sol#677-680) uses timestamp for comparisons\n[low/medium] timestamp at src/BaskVault.sol:292: BaskVault._update(address,address,uint256) (src/BaskVault.sol#292-311) uses timestamp for comparisons\n[low/medium] timestamp at src/BaskVault.sol:1007: BaskVault.previewRedeem(uint256) (src/BaskVault.sol#1007-1024) uses timestamp for comparisons\n[low/medium] timestamp at src/BaskVault.sol:986: BaskVault.previewDeposit(address[],uint256[]) (src/BaskVault.sol#986-1005) uses timestamp for comparisons\n[low/medium] timestamp at src/BaskVault.sol:846: BaskVault._feed(address) (src/BaskVault.sol#846-856) uses timestamp for comparisons\n[low/medium] timestamp at src/BaskVault.sol:899: BaskVault.insideHours() (src/BaskVault.sol#899-903) uses timestamp for comparisons","passed":true},{"durationMs":377,"exitCode":0,"name":"aderyn","output":"[high] incorrect-caret-operator at src/libraries/FullMath.sol:86: Incorrect use of caret operator\n[high] incorrect-shift-order at src/BaskVault.sol:306: Incorrect Assembly Shift Parameter Order (2 places)\n[low] centralization-risk at src/BaskVault.sol:313: Centralization Risk (10 places)\n[low] costly-loop at src/BaskVault.sol:639: Costly operations inside loop (4 places)\n[low] internal-function-used-once at src/libraries/NewYorkTime.sol:14: Internal Function Used Only Once\n[low] large-numeric-literal at src/BaskVault.sol:233: Large Numeric Literal (12 places)\n[low] literal-instead-of-constant at src/BaskVault.sol:232: Literal Instead of Constant (82 places)\n[low] local-variable-shadowing at src/BaskVault.sol:130: Local Variable Shadows State Variable (3 places)\n[low] non-reentrant-not-first at src/BaskVault.sol:313: `nonReentrant` is Not the First Modifier (10 places)\n[low] push-zero-opcode at src/libraries/TickMath.sol:2: PUSH0 Opcode\n[low] require-revert-in-loop at src/BaskVault.sol:630: Loop Contains `require`/`revert` (8 places)\n[low] state-change-without-event at src/BaskVault.sol:266: State Change Without Event\n[low] storage-array-length-not-cached at src/BaskVault.sol:656: Storage Array Length not Cached (4 places)\n[low] unchecked-return at src/BaskVault.sol:338: Unchecked Return (10 places)\n[low] uninitialized-local-variable at src/BaskVault.sol:630: Uninitialized Local Variable (8 places)\n[low] unsafe-erc20-operation at src/BaskVault.sol:642: Unsafe ERC20 Operation (2 places)","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"cf0a8de87a9e36c7e22eefdda75bb01337ccfb7ce1e7d7699908a99cdaa3cd10","verifiedTreeHash":"46a9c313a77b97252d4c61e076a4a1bfdf6480b0","verifierVersion":"0.1.0+ad90ce4c"}]}