019c0ea9-0919-70a6-b4c7-3e776897cc66
Open this run to answer one question: is this real attacker-relevant behavior with real impact, or just normal contract plumbing?
| ID | Type | Status | Attempts | Heartbeat |
|---|---|---|---|---|
| 41991 | artifact.fetch | complete | 1 / 3 | 2 months ago |
| 41992 | proxy.resolve | complete | 1 / 3 | 2 months ago |
| 41993 | tool.cast_disasm | complete | 1 / 3 | 2 months ago |
| 41994 | analysis.bundle | complete | 1 / 3 | 2 months ago |
| 41995 | capability.graph | complete | 1 / 3 | 2 months ago |
| 41996 | detector.run | complete | 1 / 3 | 2 months ago |
| 41997 | validation.fork | complete | 1 / 3 | 2 months ago |
| 42397 | report.consolidate | complete | 1 / 6 | 2 months ago |
| 42398 | tool.slither | failed | 2 / 2 | 2 months ago |
{
"method": "etherscan+onchain",
"status": "confirmed",
"etherscan_hint": {
"source": "etherscan",
"etherscan": {
"proxy_flag": true,
"implementation": "0xf59c19eb032ff3301b6695b58303470a79098771"
}
},
"implementation": "0xf59c19eb032ff3301b6695b58303470a79098771",
"onchain_method": "eip1967"
}
Traceback (most recent call last):
File "/var/www/tripwire/worker/worker.py", line 198, in main
payload = dispatch_job(job_type, job_id, job_meta, run_data, config, laravel_client)
File "/var/www/tripwire/worker/worker.py", line 114, in dispatch_job
return slither_run.run(job_id, job_meta, run_data, config, log, laravel_client)
~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/var/www/tripwire/worker/jobs/slither_run.py", line 1084, in run
stdout_text, stderr_text, result_json = _run_slither(
~~~~~~~~~~~~^
workspace,
^^^^^^^^^^
...<4 lines>...
target_path=target_path,
^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/var/www/tripwire/worker/jobs/slither_run.py", line 958, in _run_slither
raise RuntimeError(f"slither failed (exit {result.returncode}): {stderr_text or stdout_text}")
RuntimeError: slither failed (exit 1): 'forge clean' running (wd: /tmp/slither-1tjk16al)
'forge config --json' running
'forge build --build-info --skip ./test/** ./script/** --force' running (wd: /tmp/slither-1tjk16al)
Traceback (most recent call last):
File "/var/www/tripwire/.venv/bin/slither", line 10, in <module>
sys.exit(main())
~~~~^^
File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 776, in main
main_impl(all_detector_classes=detectors, all_printer_classes=printers)
~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 882, in main_impl
) = process_all(filename, args, detector_classes, printer_classes)
~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 96, in process_all
compilations = compile_all(target, **vars(args))
File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 722, in compile_all
compilations.append(CryticCompile(target, **kwargs))
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 211, in __init__
self._compile(**kwargs)
~~~~~~~~~~~~~^^^^^^^^^^
File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 633, in _compile
self._platform.compile(self, **kwargs)
~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/platform/foundry.py", line 102, in compile
hardhat_like_parsing(
~~~~~~~~~~~~~~~~~~~~^
crytic_compile, str(self._target), build_directory, str(self._project_root)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/platform/hardhat.py", line 52, in hardhat_like_parsing
raise InvalidCompilation(txt)
crytic_compile.platform.exceptions.InvalidCompilation: Compilation failed. Can you run build command?
/tmp/slither-1tjk16al/out/build-info is not a directory.
{"no_findings":false,"summary":"Two issues found: oracle price is trusted without freshness/validity checks, and redeem cancellations return shares to the receiver instead of the original owner.","findings":[{"severity":"high","category":"oracle","title":"Oracle price used without freshness/validity checks enables share mispricing","exploit_scenario":"If the oracle can be manipulated or becomes stale, an attacker can mint shares at an artificially low price and later redeem at a higher price to drain assets, or lock in inflated redemption amounts via requestRedeem. Because conversions and totalAssets are driven solely by getLatestPrice and only require price > 0, mispriced oracle data directly affects deposits and redemptions.","recommendation":"Add oracle validity checks in the vault (timestamp freshness, max deviation from anchor/TWAP), or enforce pausability on stale/abnormal updates. Consider integrating oracle safeguards locally instead of relying on implicit oracle correctness.","evidence":"totalAssets, _convertToShares, and _convertToAssets all use IYoOracle.getLatestPrice(address(this)) and only require price > 0, with no timestamp or deviation checks.","locations":["src/YoVault_V2.sol:239","src/YoVault_V2.sol:283","src/YoVault_V2.sol:291"]},{"severity":"low","category":"other","title":"Canceled redeems return shares to receiver, not the original owner","exploit_scenario":"A user can request redeem to a third-party receiver. If the operator later cancels, the shares are transferred to the receiver (not the owner), potentially locking or gifting shares away from the original owner. The comment says “transfer the shares back to the owner,” but the implementation uses receiver, which can lead to unintended loss if receiver != owner.","recommendation":"Record the original owner in PendingRedeem and return shares to that owner on cancel, or enforce receiver == owner when requesting redeem if cancellations should restore ownership.","evidence":"cancelRedeem reduces pending and then transfers shares to receiver instead of the original owner; no owner is tracked in PendingRedeem.","locations":["src/YoVault_V2.sol:196","src/YoVault_V2.sol:210"]}]}
OpenAI Codex v0.77.0 (research preview)
--------
workdir: /tmp/codex-7ey0vym0
model: gpt-5.2-codex
provider: openai
approval: never
sandbox: read-only
reasoning effort: high
reasoning summaries: auto
session id: 019c0f5b-3701-7123-aef1-d252ab14a05c
--------
user
Context: The user requested the proxy at 0x3a43aec53490cb9fa922847385d82fe25d0e9de7.
Audit the implementation at 0xf59c19eb032ff3301b6695b58303470a79098771.
Findings should refer to the implementation unless explicitly about proxy mechanics.
You are a security auditor. Audit this Solidity codebase for exploitable vulnerabilities.
Focus on: reentrancy, access control, upgradeability/proxy issues, signature validation, oracle manipulation,
accounting/invariant breaks, external call patterns, unchecked return values, and dangerous delegatecall.
Scope rules:
- You MUST fully read `AUDIT_BUNDLE.txt` before concluding. It contains the complete contents of all first-party Solidity sources in this workspace.
- Treat `lib/` and `@openzeppelin/` as third-party dependencies; skip them unless there is a concrete misuse angle in first-party code.
- If there are only dependency files and no first-party sources, set no_findings=true and explain in summary.
Output MUST be valid JSON matching the provided schema.
- If you find nothing meaningful: set no_findings=true and findings=[].
- Otherwise: set no_findings=false and include findings with severity/category/title and file/line locations when possible.
Output constraints (IMPORTANT):
- severity MUST be exactly one of: info, low, medium, high, critical (lowercase).
- category MUST be exactly one of: access_control, reentrancy, math, oracle, upgradeability, auth, other.
Keep summary short and concrete.
mcp startup: no servers
exec
/bin/bash -lc ls in /tmp/codex-7ey0vym0 succeeded in 7ms:
AUDIT_BUNDLE.txt
codex-output-schema.json
node_modules
src
exec
/bin/bash -lc 'cat AUDIT_BUNDLE.txt' in /tmp/codex-7ey0vym0 succeeded in 7ms:
This file concatenates the full contents of first-party Solidity sources for auditing.
Do not treat dependency code under lib/ or @openzeppelin/ as first-party.
================================================================================
FILE: src/YoVault_V2.sol
================================================================================
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import {Errors} from "./libraries/Errors.sol";
import {IYoVault} from "./interfaces/IYoVault.sol";
import {IYoOracle} from "./interfaces/IYoOracle.sol";
import {Compatible} from "./base/Compatible.sol";
import {AuthUpgradeable, Authority} from "./base/AuthUpgradeable.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
exec
/bin/bash -lc "nl -ba src/YoVault_V2.sol | sed -n '90,260p'" in /tmp/codex-7ey0vym0 succeeded in 8ms:
90
91 // ========================================= PUBLIC FUNCTIONS =========================================
92
93 /// @notice Allows the vault operator to manage the vault.
94 /// @param target The target contract to make a call to.
95 /// @param data The data to send to the target contract.
96 /// @param value The amount of native assets to send with the call.
97 function manage(
98 address target,
99 bytes calldata data,
100 uint256 value
101 ) external requiresAuth returns (bytes memory result) {
102 bytes4 functionSig = bytes4(data);
103 require(
104 authority().canCall(msg.sender, target, functionSig),
105 Errors.TargetMethodNotAuthorized(target, functionSig)
106 );
107
108 result = target.functionCallWithValue(data, value);
109 }
exec
/bin/bash -lc "nl -ba src/YoVault_V2.sol | sed -n '300,420p'" in /tmp/codex-7ey0vym0 succeeded in 6ms:
300 function getImplementation() external view returns (address) {
301 AddressSlot storage r;
302 assembly ("memory-safe") {
303 r.slot := _IMPLEMENTATION_SLOT
304 }
305 return r.value;
306 }
307
308 /// @dev Preview taking an entry fee on deposit. See {IERC4626-previewDeposit}.
309 function previewDeposit(uint256 assets) public view virtual override returns (uint256) {
310 uint256 fee = _feeOnTotal(assets, feeOnDeposit);
311 return super.previewDeposit(assets - fee);
312 }
313
314 /// @dev Preview adding an entry fee on mint. See {IERC4626-previewMint}.
315 function previewMint(uint256 shares) public view virtual override returns (uint256) {
316 uint256 assets = super.previewMint(shares);
317 return assets + _feeOnRaw(assets, feeOnDeposit);
318 }
319
exec
/bin/bash -lc "nl -ba src/YoVault_V2.sol | sed -n '250,320p'" in /tmp/codex-7ey0vym0 succeeded in 6ms:
250 /// @notice Get the amount of assets and shares that are pending redemption.
251 /// @param user The address of the user.
252 function pendingRedeemRequest(address user) public view returns (uint256 assets, uint256 pendingShares) {
253 return (_pendingRedeem[user].assets, _pendingRedeem[user].shares);
254 }
255
256 //============================== OVERRIDES ===============================
257
258 /// @dev Override the default `deposit` function to add the `whenNotPaused` modifier.
259 function deposit(uint256 assets, address receiver) public override whenNotPaused returns (uint256) {
260 return super.deposit(assets, receiver);
261 }
262
263 /// @dev Override the default `mint` function to add the `whenNotPaused` modifier.
264 function mint(uint256 shares, address receiver) public override whenNotPaused returns (uint256) {
265 return super.mint(shares, receiver);
266 }
267
268 /// @notice This method is disabled. Use `requestRedeem` or `redeem`instead.
269 function withdraw(uint256, address, address) public override whenNotPaused returns (uint256) {
codex
{"no_findings":false,"summary":"Two issues found: oracle price is trusted without freshness/validity checks, and redeem cancellations return shares to the receiver instead of the original owner.","findings":[{"severity":"high","category":"oracle","title":"Oracle price used without freshness/validity checks enables share mispricing","exploit_scenario":"If the oracle can be manipulated or becomes stale, an attacker can mint shares at an artificially low price and later redeem at a higher price to drain assets, or lock in inflated redemption amounts via requestRedeem. Because conversions and totalAssets are driven solely by getLatestPrice and only require price > 0, mispriced oracle data directly affects deposits and redemptions.","recommendation":"Add oracle validity checks in the vault (timestamp freshness, max deviation from anchor/TWAP), or enforce pausability on stale/abnormal updates. Consider integrating oracle safeguards locally instead of relying on implicit oracle correctness.","evidence":"totalAssets, _convertToShares, and _convertToAssets all use IYoOracle.getLatestPrice(address(this)) and only require price > 0, with no timestamp or deviation checks.","locations":["src/YoVault_V2.sol:239","src/YoVault_V2.sol:283","src/YoVault_V2.sol:291"]},{"severity":"low","category":"other","title":"Canceled redeems return shares to receiver, not the original owner","exploit_scenario":"A user can request redeem to a third-party receiver. If the operator later cancels, the shares are transferred to the receiver (not the owner), potentially locking or gifting shares away from the original owner. The comment says “transfer the shares back to the owner,” but the implementation uses receiver, which can lead to unintended loss if receiver != owner.","recommendation":"Record the original owner in PendingRedeem and return shares to that owner on cancel, or enforce receiver == owner when requesting redeem if cancellations should restore ownership.","evidence":"cancelRedeem reduces pending and then transfers shares to receiver instead of the original owner; no owner is tracked in PendingRedeem.","locations":["src/YoVault_V2.sol:196","src/YoVault_V2.sol:210"]}]}
tokens used
26,728
{
"codex": {
"ran_at": "2026-01-30T14:46:46.018479+00:00",
"returncode": 0
},
"project": {
"mode": "source",
"file_count": 28,
"import_count": 37,
"first_party_contracts_count": 6
},
"summary": "Two issues found: oracle price is trusted without freshness/validity checks, and redeem cancellations return shares to the receiver instead of the original owner.",
"raw_final": {
"summary": "Two issues found: oracle price is trusted without freshness/validity checks, and redeem cancellations return shares to the receiver instead of the original owner.",
"findings": [
{
"title": "Oracle price used without freshness/validity checks enables share mispricing",
"category": "oracle",
"evidence": "totalAssets, _convertToShares, and _convertToAssets all use IYoOracle.getLatestPrice(address(this)) and only require price > 0, with no timestamp or deviation checks.",
"severity": "high",
"locations": [
"src/YoVault_V2.sol:239",
"src/YoVault_V2.sol:283",
"src/YoVault_V2.sol:291"
],
"recommendation": "Add oracle validity checks in the vault (timestamp freshness, max deviation from anchor/TWAP), or enforce pausability on stale/abnormal updates. Consider integrating oracle safeguards locally instead of relying on implicit oracle correctness.",
"exploit_scenario": "If the oracle can be manipulated or becomes stale, an attacker can mint shares at an artificially low price and later redeem at a higher price to drain assets, or lock in inflated redemption amounts via requestRedeem. Because conversions and totalAssets are driven solely by getLatestPrice and only require price > 0, mispriced oracle data directly affects deposits and redemptions."
},
{
"title": "Canceled redeems return shares to receiver, not the original owner",
"category": "other",
"evidence": "cancelRedeem reduces pending and then transfers shares to receiver instead of the original owner; no owner is tracked in PendingRedeem.",
"severity": "low",
"locations": [
"src/YoVault_V2.sol:196",
"src/YoVault_V2.sol:210"
],
"recommendation": "Record the original owner in PendingRedeem and return shares to that owner on cancel, or enforce receiver == owner when requesting redeem if cancellations should restore ownership.",
"exploit_scenario": "A user can request redeem to a third-party receiver. If the operator later cancels, the shares are transferred to the receiver (not the owner), potentially locking or gifting shares away from the original owner. The comment says \u201ctransfer the shares back to the owner,\u201d but the implementation uses receiver, which can lead to unintended loss if receiver != owner."
}
],
"no_findings": false
},
"no_findings": false,
"proxy_context": {
"proxy_run_id": "019c0ea9-0919-70a6-b4c7-3e776897cc66",
"proxy_address": "0x3a43aec53490cb9fa922847385d82fe25d0e9de7",
"implementation_run_id": "019c0eab-5d2a-7148-a859-77ddcfaad2fb",
"implementation_address": "0xf59c19eb032ff3301b6695b58303470a79098771"
},
"schema_version": 1
}
00000000: PUSH1 0x80 00000002: PUSH1 0x40 00000004: MSTORE 00000005: PUSH2 0x000c 00000008: PUSH2 0x000e 0000000b: JUMP 0000000c: JUMPDEST 0000000d: STOP 0000000e: JUMPDEST 0000000f: PUSH32 0x000000000000000000000000d460cf1f5d764acad1c3276c549ee5f1bb671473 00000030: PUSH1 0x01 00000032: PUSH1 0x01 00000034: PUSH1 0xa0 00000036: SHL 00000037: SUB 00000038: AND 00000039: CALLER 0000003a: SUB 0000003b: PUSH2 0x00c4 0000003e: JUMPI 0000003f: PUSH0 00000040: CALLDATALOAD 00000041: PUSH32 0xffffffff00000000000000000000000000000000000000000000000000000000 00000062: AND 00000063: PUSH32 0x4f1ef28600000000000000000000000000000000000000000000000000000000 00000084: EQ 00000085: PUSH2 0x00ba 00000088: JUMPI 00000089: PUSH1 0x40 0000008b: MLOAD 0000008c: PUSH32 0xd2b576ec00000000000000000000000000000000000000000000000000000000 000000ad: DUP2 000000ae: MSTORE 000000af: PUSH1 0x04 000000b1: ADD 000000b2: PUSH1 0x40 000000b4: MLOAD 000000b5: DUP1 000000b6: SWAP2 000000b7: SUB 000000b8: SWAP1 000000b9: REVERT 000000ba: JUMPDEST 000000bb: PUSH2 0x00c2 000000be: PUSH2 0x00cc 000000c1: JUMP 000000c2: JUMPDEST 000000c3: JUMP 000000c4: JUMPDEST 000000c5: PUSH2 0x00c2 000000c8: PUSH2 0x00fa 000000cb: JUMP 000000cc: JUMPDEST 000000cd: PUSH0 000000ce: DUP1 000000cf: PUSH2 0x00db 000000d2: CALLDATASIZE 000000d3: PUSH1 0x04 000000d5: DUP2 000000d6: DUP5 000000d7: PUSH2 0x03c9 000000da: JUMP 000000db: JUMPDEST 000000dc: DUP2 000000dd: ADD 000000de: SWAP1 000000df: PUSH2 0x00e8 000000e2: SWAP2 000000e3: SWAP1 000000e4: PUSH2 0x041d 000000e7: JUMP 000000e8: JUMPDEST 000000e9: SWAP2 000000ea: POP 000000eb: SWAP2 000000ec: POP 000000ed: PUSH2 0x00f6 000000f0: DUP3 000000f1: DUP3 000000f2: PUSH2 0x010a 000000f5: JUMP 000000f6: JUMPDEST 000000f7: POP 000000f8: POP 000000f9: JUMP 000000fa: JUMPDEST 000000fb: PUSH2 0x00c2 000000fe: PUSH2 0x0105 00000101: PUSH2 0x0164 00000104: JUMP 00000105: JUMPDEST 00000106: PUSH2 0x019b 00000109: JUMP 0000010a: JUMPDEST 0000010b: PUSH2 0x0113 0000010e: DUP3 0000010f: PUSH2 0x01b9 00000112: JUMP 00000113: JUMPDEST 00000114: PUSH1 0x40 00000116: MLOAD 00000117: PUSH1 0x01 00000119: PUSH1 0x01 0000011b: PUSH1 0xa0 0000011d: SHL 0000011e: SUB 0000011f: DUP4 00000120: AND 00000121: SWAP1 00000122: PUSH32 0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b 00000143: SWAP1 00000144: PUSH0 00000145: SWAP1 00000146: LOG2 00000147: DUP1 00000148: MLOAD 00000149: ISZERO 0000014a: PUSH2 0x015c 0000014d: JUMPI 0000014e: PUSH2 0x0157 00000151: DUP3 00000152: DUP3 00000153: PUSH2 0x0265 00000156: JUMP 00000157: JUMPDEST 00000158: POP 00000159: POP 0000015a: POP 0000015b: JUMP 0000015c: JUMPDEST 0000015d: PUSH2 0x00f6 00000160: PUSH2 0x02d7 00000163: JUMP 00000164: JUMPDEST 00000165: PUSH0 00000166: PUSH2 0x0196 00000169: PUSH32 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc 0000018a: SLOAD 0000018b: PUSH1 0x01 0000018d: PUSH1 0x01 0000018f: PUSH1 0xa0 00000191: SHL 00000192: SUB 00000193: AND 00000194: SWAP1 00000195: JUMP 00000196: JUMPDEST 00000197: SWAP1 00000198: POP 00000199: SWAP1 0000019a: JUMP 0000019b: JUMPDEST 0000019c: CALLDATASIZE 0000019d: PUSH0 0000019e: PUSH0 0000019f: CALLDATACOPY 000001a0: PUSH0 000001a1: PUSH0 000001a2: CALLDATASIZE 000001a3: PUSH0 000001a4: DUP5 000001a5: GAS 000001a6: DELEGATECALL 000001a7: RETURNDATASIZE 000001a8: PUSH0 000001a9: PUSH0 000001aa: RETURNDATACOPY 000001ab: DUP1 000001ac: DUP1 000001ad: ISZERO 000001ae: PUSH2 0x01b5 000001b1: JUMPI 000001b2: RETURNDATASIZE 000001b3: PUSH0 000001b4: RETURN 000001b5: JUMPDEST 000001b6: RETURNDATASIZE 000001b7: PUSH0 000001b8: REVERT 000001b9: JUMPDEST 000001ba: DUP1 000001bb: PUSH1 0x01 000001bd: PUSH1 0x01 000001bf: PUSH1 0xa0 000001c1: SHL 000001c2: SUB 000001c3: AND 000001c4: EXTCODESIZE 000001c5: PUSH0 000001c6: SUB 000001c7: PUSH2 0x020c 000001ca: JUMPI 000001cb: PUSH1 0x40 000001cd: MLOAD 000001ce: PUSH32 0x4c9c8ce300000000000000000000000000000000000000000000000000000000 000001ef: DUP2 000001f0: MSTORE 000001f1: PUSH1 0x01 000001f3: PUSH1 0x01 000001f5: PUSH1 0xa0 000001f7: SHL 000001f8: SUB 000001f9: DUP3 000001fa: AND 000001fb: PUSH1 0x04 000001fd: DUP3 000001fe: ADD 000001ff: MSTORE 00000200: PUSH1 0x24 00000202: ADD 00000203: JUMPDEST 00000204: PUSH1 0x40 00000206: MLOAD 00000207: DUP1 00000208: SWAP2 00000209: SUB 0000020a: SWAP1 0000020b: REVERT 0000020c: JUMPDEST 0000020d: PUSH32 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc 0000022e: DUP1 0000022f: SLOAD 00000230: PUSH32 0xffffffffffffffffffffffff0000000000000000000000000000000000000000 00000251: AND 00000252: PUSH1 0x01 00000254: PUSH1 0x01 00000256: PUSH1 0xa0 00000258: SHL 00000259: SUB 0000025a: SWAP3 0000025b: SWAP1 0000025c: SWAP3 0000025d: AND 0000025e: SWAP2 0000025f: SWAP1 00000260: SWAP2 00000261: OR 00000262: SWAP1 00000263: SSTORE 00000264: JUMP 00000265: JUMPDEST 00000266: PUSH1 0x60 00000268: PUSH0 00000269: PUSH0 0000026a: DUP5 0000026b: PUSH1 0x01 0000026d: PUSH1 0x01 0000026f: PUSH1 0xa0 00000271: SHL 00000272: SUB 00000273: AND 00000274: DUP5 00000275: PUSH1 0x40 00000277: MLOAD 00000278: PUSH2 0x0281 0000027b: SWAP2 0000027c: SWAP1 0000027d: PUSH2 0x04ee 00000280: JUMP 00000281: JUMPDEST 00000282: PUSH0 00000283: PUSH1 0x40 00000285: MLOAD 00000286: DUP1 00000287: DUP4 00000288: SUB 00000289: DUP2 0000028a: DUP6 0000028b: GAS 0000028c: DELEGATECALL 0000028d: SWAP2 0000028e: POP 0000028f: POP 00000290: RETURNDATASIZE 00000291: DUP1 00000292: PUSH0 00000293: DUP2 00000294: EQ 00000295: PUSH2 0x02b9 00000298: JUMPI 00000299: PUSH1 0x40 0000029b: MLOAD 0000029c: SWAP2 0000029d: POP 0000029e: PUSH1 0x1f 000002a0: NOT 000002a1: PUSH1 0x3f 000002a3: RETURNDATASIZE 000002a4: ADD 000002a5: AND 000002a6: DUP3 000002a7: ADD 000002a8: PUSH1 0x40 000002aa: MSTORE 000002ab: RETURNDATASIZE 000002ac: DUP3 000002ad: MSTORE 000002ae: RETURNDATASIZE 000002af: PUSH0 000002b0: PUSH1 0x20 000002b2: DUP5 000002b3: ADD 000002b4: RETURNDATACOPY 000002b5: PUSH2 0x02be 000002b8: JUMP 000002b9: JUMPDEST 000002ba: PUSH1 0x60 000002bc: SWAP2 000002bd: POP 000002be: JUMPDEST 000002bf: POP 000002c0: SWAP2 000002c1: POP 000002c2: SWAP2 000002c3: POP 000002c4: PUSH2 0x02ce 000002c7: DUP6 000002c8: DUP4 000002c9: DUP4 000002ca: PUSH2 0x030f 000002cd: JUMP 000002ce: JUMPDEST 000002cf: SWAP6 000002d0: SWAP5 000002d1: POP 000002d2: POP 000002d3: POP 000002d4: POP 000002d5: POP 000002d6: JUMP 000002d7: JUMPDEST 000002d8: CALLVALUE 000002d9: ISZERO 000002da: PUSH2 0x00c2 000002dd: JUMPI 000002de: PUSH1 0x40 000002e0: MLOAD 000002e1: PUSH32 0xb398979f00000000000000000000000000000000000000000000000000000000 00000302: DUP2 00000303: MSTORE 00000304: PUSH1 0x04 00000306: ADD 00000307: PUSH1 0x40 00000309: MLOAD 0000030a: DUP1 0000030b: SWAP2 0000030c: SUB 0000030d: SWAP1 0000030e: REVERT 0000030f: JUMPDEST 00000310: PUSH1 0x60 00000312: DUP3 00000313: PUSH2 0x0324 00000316: JUMPI 00000317: PUSH2 0x031f 0000031a: DUP3 0000031b: PUSH2 0x0387 0000031e: JUMP 0000031f: JUMPDEST 00000320: PUSH2 0x0380 00000323: JUMP 00000324: JUMPDEST 00000325: DUP2 00000326: MLOAD 00000327: ISZERO 00000328: DUP1 00000329: ISZERO 0000032a: PUSH2 0x033b 0000032d: JUMPI 0000032e: POP 0000032f: PUSH1 0x01 00000331: PUSH1 0x01 00000333: PUSH1 0xa0 00000335: SHL 00000336: SUB 00000337: DUP5 00000338: AND 00000339: EXTCODESIZE 0000033a: ISZERO 0000033b: JUMPDEST 0000033c: ISZERO 0000033d: PUSH2 0x037d 00000340: JUMPI 00000341: PUSH1 0x40 00000343: MLOAD 00000344: PUSH32 0x9996b31500000000000000000000000000000000000000000000000000000000 00000365: DUP2 00000366: MSTORE 00000367: PUSH1 0x01 00000369: PUSH1 0x01 0000036b: PUSH1 0xa0 0000036d: SHL 0000036e: SUB 0000036f: DUP6 00000370: AND 00000371: PUSH1 0x04 00000373: DUP3 00000374: ADD 00000375: MSTORE 00000376: PUSH1 0x24 00000378: ADD 00000379: PUSH2 0x0203 0000037c: JUMP 0000037d: JUMPDEST 0000037e: POP 0000037f: DUP1 00000380: JUMPDEST 00000381: SWAP4 00000382: SWAP3 00000383: POP 00000384: POP 00000385: POP 00000386: JUMP 00000387: JUMPDEST 00000388: DUP1 00000389: MLOAD 0000038a: ISZERO 0000038b: PUSH2 0x0397 0000038e: JUMPI 0000038f: DUP1 00000390: MLOAD 00000391: DUP1 00000392: DUP3 00000393: PUSH1 0x20 00000395: ADD 00000396: REVERT 00000397: JUMPDEST 00000398: PUSH1 0x40 0000039a: MLOAD 0000039b: PUSH32 0xd6bda27500000000000000000000000000000000000000000000000000000000 000003bc: DUP2 000003bd: MSTORE 000003be: PUSH1 0x04 000003c0: ADD 000003c1: PUSH1 0x40 000003c3: MLOAD 000003c4: DUP1 000003c5: SWAP2 000003c6: SUB 000003c7: SWAP1 000003c8: REVERT 000003c9: JUMPDEST 000003ca: PUSH0 000003cb: PUSH0 000003cc: DUP6 000003cd: DUP6 000003ce: GT 000003cf: ISZERO 000003d0: PUSH2 0x03d7 000003d3: JUMPI 000003d4: PUSH0 000003d5: PUSH0 000003d6: REVERT 000003d7: JUMPDEST 000003d8: DUP4 000003d9: DUP7 000003da: GT 000003db: ISZERO 000003dc: PUSH2 0x03e3 000003df: JUMPI 000003e0: PUSH0 000003e1: PUSH0 000003e2: REVERT 000003e3: JUMPDEST 000003e4: POP 000003e5: POP 000003e6: DUP3 000003e7: ADD 000003e8: SWAP4 000003e9: SWAP2 000003ea: SWAP1 000003eb: SWAP3 000003ec: SUB 000003ed: SWAP2 000003ee: POP 000003ef: JUMP 000003f0: JUMPDEST 000003f1: PUSH32 0x4e487b7100000000000000000000000000000000000000000000000000000000 00000412: PUSH0 00000413: MSTORE 00000414: PUSH1 0x41 00000416: PUSH1 0x04 00000418: MSTORE 00000419: PUSH1 0x24 0000041b: PUSH0 0000041c: REVERT 0000041d: JUMPDEST 0000041e: PUSH0 0000041f: PUSH0 00000420: PUSH1 0x40 00000422: DUP4 00000423: DUP6 00000424: SUB 00000425: SLT 00000426: ISZERO 00000427: PUSH2 0x042e 0000042a: JUMPI 0000042b: PUSH0 0000042c: PUSH0 0000042d: REVERT 0000042e: JUMPDEST 0000042f: DUP3 00000430: CALLDATALOAD 00000431: PUSH1 0x01 00000433: PUSH1 0x01 00000435: PUSH1 0xa0 00000437: SHL 00000438: SUB 00000439: DUP2 0000043a: AND 0000043b: DUP2 0000043c: EQ 0000043d: PUSH2 0x0444 00000440: JUMPI 00000441: PUSH0 00000442: PUSH0 00000443: REVERT 00000444: JUMPDEST 00000445: SWAP2 00000446: POP 00000447: PUSH1 0x20 00000449: DUP4 0000044a: ADD 0000044b: CALLDATALOAD 0000044c: PUSH8 0xffffffffffffffff 00000455: DUP2 00000456: GT 00000457: ISZERO 00000458: PUSH2 0x045f 0000045b: JUMPI 0000045c: PUSH0 0000045d: PUSH0 0000045e: REVERT 0000045f: JUMPDEST 00000460: DUP4 00000461: ADD 00000462: PUSH1 0x1f 00000464: DUP2 00000465: ADD 00000466: DUP6 00000467: SGT 00000468: PUSH2 0x046f 0000046b: JUMPI 0000046c: PUSH0 0000046d: PUSH0 0000046e: REVERT 0000046f: JUMPDEST 00000470: DUP1 00000471: CALLDATALOAD 00000472: PUSH8 0xffffffffffffffff 0000047b: DUP2 0000047c: GT 0000047d: ISZERO 0000047e: PUSH2 0x0489 00000481: JUMPI 00000482: PUSH2 0x0489 00000485: PUSH2 0x03f0 00000488: JUMP 00000489: JUMPDEST 0000048a: PUSH1 0x40 0000048c: MLOAD 0000048d: PUSH1 0x1f 0000048f: DUP3 00000490: ADD 00000491: PUSH1 0x1f 00000493: NOT 00000494: SWAP1 00000495: DUP2 00000496: AND 00000497: PUSH1 0x3f 00000499: ADD 0000049a: AND 0000049b: DUP2 0000049c: ADD 0000049d: PUSH8 0xffffffffffffffff 000004a6: DUP2 000004a7: GT 000004a8: DUP3 000004a9: DUP3 000004aa: LT 000004ab: OR 000004ac: ISZERO 000004ad: PUSH2 0x04b8 000004b0: JUMPI 000004b1: PUSH2 0x04b8 000004b4: PUSH2 0x03f0 000004b7: JUMP 000004b8: JUMPDEST 000004b9: PUSH1 0x40 000004bb: MSTORE 000004bc: DUP2 000004bd: DUP2 000004be: MSTORE 000004bf: DUP3 000004c0: DUP3 000004c1: ADD 000004c2: PUSH1 0x20 000004c4: ADD 000004c5: DUP8 000004c6: LT 000004c7: ISZERO 000004c8: PUSH2 0x04cf 000004cb: JUMPI 000004cc: PUSH0 000004cd: PUSH0 000004ce: REVERT 000004cf: JUMPDEST 000004d0: DUP2 000004d1: PUSH1 0x20 000004d3: DUP5 000004d4: ADD 000004d5: PUSH1 0x20 000004d7: DUP4 000004d8: ADD 000004d9: CALLDATACOPY 000004da: PUSH0 000004db: PUSH1 0x20 000004dd: DUP4 000004de: DUP4 000004df: ADD 000004e0: ADD 000004e1: MSTORE 000004e2: DUP1 000004e3: SWAP4 000004e4: POP 000004e5: POP 000004e6: POP 000004e7: POP 000004e8: SWAP3 000004e9: POP 000004ea: SWAP3 000004eb: SWAP1 000004ec: POP 000004ed: JUMP 000004ee: JUMPDEST 000004ef: PUSH0 000004f0: DUP3 000004f1: MLOAD 000004f2: PUSH0 000004f3: JUMPDEST 000004f4: DUP2 000004f5: DUP2 000004f6: LT 000004f7: ISZERO 000004f8: PUSH2 0x050d 000004fb: JUMPI 000004fc: PUSH1 0x20 000004fe: DUP2 000004ff: DUP7 00000500: ADD 00000501: DUP2 00000502: ADD 00000503: MLOAD 00000504: DUP6 00000505: DUP4 00000506: ADD 00000507: MSTORE 00000508: ADD 00000509: PUSH2 0x04f3 0000050c: JUMP 0000050d: JUMPDEST 0000050e: POP 0000050f: PUSH0 00000510: SWAP3 00000511: ADD 00000512: SWAP2 00000513: DUP3 00000514: MSTORE 00000515: POP 00000516: SWAP2 00000517: SWAP1 00000518: POP 00000519: JUMP 0000051a: INVALID 0000051b: LOG1 0000051c: PUSH5 0x736f6c6343 00000522: STOP 00000523: ADDMOD 00000524: SHR 00000525: STOP 00000526: EXP
| Severity | Category | Tool | Title | Confidence | Validated | Confirmed | Details |
|---|---|---|---|---|---|---|---|
| critical | upgradeability | detector | Untrusted DELEGATECALL target reachable | 70 | no | no |
viewtrace_id: 0x93f8a5c28e0c905a946bcd39f7118ef25c114ca28cae672e2edb0239898b48f2
call_targets:
0xf59c19eb032ff3301b6695b58303470a79098771
target_varies:
no
classification:
constant_target
validation_json{
"sink": "DELEGATECALL",
"errors": 0,
"status": "sink_reached",
"attempts": 1,
"trace_id": "0x93f8a5c28e0c905a946bcd39f7118ef25c114ca28cae672e2edb0239898b48f2",
"confirmed": false,
"trace_mode": "callTracer",
"call_targets": [
"0xf59c19eb032ff3301b6695b58303470a79098771"
],
"matched_probe": null,
"target_varies": false,
"classification": "constant_target"
}
witness_json{
"notes": "heuristic witness; entrypoints are best-effort selectors with zeroed calldata",
"constraints": [],
"entrypoints": [
{
"calldata": "0x",
"selector": "fallback",
"calldata_variants": [
"0x"
]
}
]
}
evidence_json{
"examples": [
{
"pc": 422,
"opcode": "DELEGATECALL",
"returndata_checked": null,
"value_expression_category": "n/a",
"target_expression_category": "computed"
},
{
"pc": 652,
"opcode": "DELEGATECALL",
"returndata_checked": null,
"value_expression_category": "n/a",
"target_expression_category": "computed"
}
],
"guards_detected": [],
"delegatecall_count": 2,
"untrusted_delegatecall_count": 2
}
|
| high | oracle | codex | Oracle price used without freshness/validity checks enables share mispricing | 85 | no | — |
viewevidence_json{
"evidence": "totalAssets, _convertToShares, and _convertToAssets all use IYoOracle.getLatestPrice(address(this)) and only require price > 0, with no timestamp or deviation checks.",
"locations": [
"src/YoVault_V2.sol:239",
"src/YoVault_V2.sol:283",
"src/YoVault_V2.sol:291"
],
"recommendation": "Add oracle validity checks in the vault (timestamp freshness, max deviation from anchor/TWAP), or enforce pausability on stale/abnormal updates. Consider integrating oracle safeguards locally instead of relying on implicit oracle correctness.",
"exploit_scenario": "If the oracle can be manipulated or becomes stale, an attacker can mint shares at an artificially low price and later redeem at a higher price to drain assets, or lock in inflated redemption amounts via requestRedeem. Because conversions and totalAssets are driven solely by getLatestPrice and only require price > 0, mispriced oracle data directly affects deposits and redemptions."
}
|
| medium | upgradeability | cast | DELEGATECALL present | 80 | no | — |
viewevidence_json{
"notes": "delegatecall enables execution in caller context; common in proxies/upgradeable patterns",
"delegatecall_count": 2
}
|
| low | other | codex | Canceled redeems return shares to receiver, not the original owner | 65 | no | — |
viewevidence_json{
"evidence": "cancelRedeem reduces pending and then transfers shares to receiver instead of the original owner; no owner is tracked in PendingRedeem.",
"locations": [
"src/YoVault_V2.sol:196",
"src/YoVault_V2.sol:210"
],
"recommendation": "Record the original owner in PendingRedeem and return shares to that owner on cancel, or enforce receiver == owner when requesting redeem if cancellations should restore ownership.",
"exploit_scenario": "A user can request redeem to a third-party receiver. If the operator later cancels, the shares are transferred to the receiver (not the owner), potentially locking or gifting shares away from the original owner. The comment says \u201ctransfer the shares back to the owner,\u201d but the implementation uses receiver, which can lead to unintended loss if receiver != owner."
}
|