019ba9bc-ec77-7229-831b-371cc06748e3
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 |
|---|---|---|---|---|
| 37790 | artifact.fetch | complete | 1 / 3 | 3 months ago |
| 37791 | proxy.resolve | complete | 1 / 3 | 3 months ago |
| 37792 | tool.cast_disasm | complete | 1 / 3 | 3 months ago |
| 37793 | analysis.bundle | complete | 1 / 3 | 3 months ago |
| 37794 | capability.graph | complete | 1 / 3 | 3 months ago |
| 37795 | detector.run | complete | 1 / 3 | 3 months ago |
| 37796 | validation.fork | complete | 1 / 3 | 3 months ago |
| 37919 | report.consolidate | complete | 1 / 6 | 3 months ago |
{
"method": "etherscan+onchain",
"status": "confirmed",
"etherscan_hint": {
"source": "etherscan",
"etherscan": {
"proxy_flag": true,
"implementation": "0xfb3102759f2d57f547b9c519db49ce1ffde15db2"
}
},
"implementation": "0xfb3102759f2d57f547b9c519db49ce1ffde15db2",
"onchain_method": "eip1967"
}
No slither job recorded yet.
{"no_findings":false,"summary":"One medium issue: native token allowance accounting in rebalanceDexVault can be bypassed if the vault reports non-spending amounts, enabling repeated ETH outflows beyond the intended allowance.","findings":[{"severity":"medium","category":"access_control","title":"rebalanceDexVault can skip allowance reduction despite sending native value","exploit_scenario":"A rebalancer calls rebalanceDexVault with value_ > 0. If the target vault (buggy or malicious) accepts the ETH but returns colAmount_ <= 0 and debtAmount_ >= 0, the allowance reduction block is skipped. The allowance remains unchanged, so the rebalancer can repeat the call to drain native ETH beyond the configured allowance.","recommendation":"When value_ > 0, always decrement allowance based on actual native outflow (e.g., computed via balance delta) regardless of returned col/debt amounts. Additionally, validate that sending native value is only allowed when the vault actually expects native tokens, and revert when value_ is nonzero but no native-token usage is indicated.","evidence":"Allowance is reduced only inside `if (value_ > 0 && (colAmount_ > 0 || debtAmount_ < 0))`, so ETH can be sent without decrementing nativeTokenAllowances if the vault reports non-spending amounts.","locations":["contracts/reserve/main.sol:238","contracts/reserve/main.sol:250"]}]}
OpenAI Codex v0.77.0 (research preview)
--------
workdir: /tmp/codex-hb1udi63
model: gpt-5.2-codex
provider: openai
approval: never
sandbox: read-only
reasoning effort: xhigh
reasoning summaries: auto
session id: 019ba9f0-292e-7793-b263-7ef04ede8fd4
--------
user
Context: The user requested the proxy at 0x264786ef916af64a1db19f513f24a3681734ce92.
Audit the implementation at 0xfb3102759f2d57f547b9c519db49ce1ffde15db2.
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
thinking
**Preparing to audit by reading file**
exec
/bin/bash -lc ls in /tmp/codex-hb1udi63 succeeded in 8ms:
AUDIT_BUNDLE.txt
codex-output-schema.json
contracts
@openzeppelin
exec
/bin/bash -lc "sed -n '1,200p' AUDIT_BUNDLE.txt" in /tmp/codex-hb1udi63 succeeded in 30ms:
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: contracts/infiniteProxy/interfaces/iProxy.sol
================================================================================
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.21 <=0.8.29;
interface IProxy {
function setAdmin(address newAdmin_) external;
function setDummyImplementation(address newDummyImplementation_) external;
function addImplementation(address implementation_, bytes4[] calldata sigs_) external;
function removeImplementation(address implementation_) external;
function getAdmin() external view returns (address);
thinking
**Preparing to analyze large audit bundle**
exec
/bin/bash -lc "sed -n '200,400p' AUDIT_BUNDLE.txt" in /tmp/codex-hb1udi63 succeeded in 27ms:
///
/// @param rateAtUtilizationKink borrow rate when utilization is at kink. in 1e2: 100% = 10_000; 1% = 100
/// e.g. when rate should be 7% at kink then rateAtUtilizationKink would be 700
uint256 rateAtUtilizationKink;
///
/// @param rateAtUtilizationMax borrow rate when utilization is maximum at 100%. in 1e2: 100% = 10_000; 1% = 100
/// e.g. when rate should be 125% at 100% then rateAtUtilizationMax would be 12_500
uint256 rateAtUtilizationMax;
}
/// @notice struct to set borrow rate data for version 2
struct RateDataV2Params {
///
/// @param token for rate data
address token;
///
/// @param kink1 first kink in borrow rate. in 1e2: 100% = 10_000; 1% = 100
/// utilization below kink 1 usually means slow increase in rate, once utilization is above kink 1 borrow rate increases faster
uint256 kink1;
///
thinking
**Continuing thorough audit bundle scan**
exec
/bin/bash -lc "sed -n '400,800p' AUDIT_BUNDLE.txt" in /tmp/codex-hb1udi63 succeeded in 26ms:
function unpauseUser(address user_, address[] calldata supplyTokens_, address[] calldata borrowTokens_) external;
/// @notice collects revenue for tokens to configured revenueCollector address.
/// @param tokens_ array of tokens to collect revenue for
/// @dev Note that this can revert if token balance is < revenueAmount (utilization > 100%)
function collectRevenue(address[] calldata tokens_) external;
/// @notice gets the current updated exchange prices for n tokens and updates all prices, rates related data in storage.
/// @param tokens_ tokens to update exchange prices for
/// @return supplyExchangePrices_ new supply rates of overall system for each token
/// @return borrowExchangePrices_ new borrow rates of overall system for each token
function updateExchangePrices(
address[] calldata tokens_
) external returns (uint256[] memory supplyExchangePrices_, uint256[] memory borrowExchangePrices_);
}
interface IFluidLiquidityLogic is IFluidLiquidityAdmin {
/// @notice Single function which handles supply, withdraw, borrow & payback
/// @param token_ address of token (0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE for native)
/// @param supplyAmount_ if +ve then supply, if -ve then withdraw, if 0 then nothing
thinking
**Continuing interface review**
exec
/bin/bash -lc "sed -n '800,1200p' AUDIT_BUNDLE.txt" in /tmp/codex-hb1udi63 succeeded in 7ms:
address indexed token,
address indexed spender,
uint48 newNonce,
uint48 oldNonce
);
/// @notice Emits an event when the owner successfully sets permissions on a token for the spender.
event Approval(
address indexed owner,
address indexed token,
address indexed spender,
uint160 amount,
uint48 expiration
);
/// @notice Emits an event when the owner successfully sets permissions using a permit signature on a token for the spender.
event Permit(
address indexed owner,
address indexed token,
address indexed spender,
thinking
**Continuing analysis**
exec
/bin/bash -lc "sed -n '1200,1600p' AUDIT_BUNDLE.txt" in /tmp/codex-hb1udi63 succeeded in 12ms:
================================================================================
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
abstract contract Events {
/// @notice Emitted when an address is added or removed from the auths
event LogUpdateAuth(address indexed auth, bool isAuth);
/// @notice Emitted when an address is added or removed from the rebalancers
event LogUpdateRebalancer(address indexed rebalancer, bool isRebalancer);
/// @notice Emitted when a token is approved for use by a protocol
event LogAllow(address indexed protocol, address indexed token, uint256 newAllowance, uint existingAllowance);
/// @notice Emitted when a token is revoked for use by a protocol
event LogRevoke(address indexed protocol, address indexed token);
/// @notice Emitted when fToken is rebalanced
event LogRebalanceFToken(address indexed protocol, uint amount);
thinking
**Continuing process**
exec
/bin/bash -lc "sed -n '1600,2000p' AUDIT_BUNDLE.txt" in /tmp/codex-hb1udi63 succeeded in 5ms:
allowances_ = new TokenAllowance[](tokens_.length());
for (uint256 i = 0; i < tokens_.length(); i++) {
address token_ = tokens_.at(i);
(allowances_[i]).token = token_;
if (token_ == NATIVE_TOKEN_ADDRESS) {
(allowances_[i]).allowance = nativeTokenAllowances[protocol_];
} else {
(allowances_[i]).allowance = IERC20(token_).allowance(address(this), protocol_);
}
}
}
/// @notice Gets the allowances that are approved to a protocol
/// @return allowances_ The tokens that are approved for use by all the protocols
function getAllProtocolAllowances() public view returns (ProtocolTokenAllowance[] memory allowances_) {
allowances_ = new ProtocolTokenAllowance[](_protocols.length());
for (uint i = 0; i < _protocols.length(); i++) {
address protocol_ = _protocols.at(i);
(allowances_[i]).protocol = protocol_;
(allowances_[i]).tokenAllowances = getProtocolAllowances(protocol_);
thinking
**Continuing contract review**
exec
/bin/bash -lc "sed -n '2000,2400p' AUDIT_BUNDLE.txt" in /tmp/codex-hb1udi63 succeeded in 5ms:
thinking
**Checking file end with tail command**
exec
/bin/bash -lc 'tail -n 20 AUDIT_BUNDLE.txt' in /tmp/codex-hb1udi63 succeeded in 21ms:
// address private _owner; (at slot 51)
// uint256[49] private __gap; (slot 52 until slot 100)
// ----------------------- slot 101 ---------------------------
/// @notice Maps address to there status as an Auth
mapping(address => bool) public isAuth;
/// @notice Maps address to there status as a Rebalancer
mapping(address => bool) public isRebalancer;
/// @notice Mapping of protocol addresses to the tokens that are allowed to be used by that protocol
mapping(address => EnumerableSet.AddressSet) internal _protocolTokens;
/// @notice Mapping of protocol addresses to the amount of tokens that are allowed to be used by that protocol
mapping(address => uint256) public nativeTokenAllowances;
/// @notice Array of all the protocols that had tokens allowed to be used
EnumerableSet.AddressSet internal _protocols;
}
thinking
**Analyzing Reserve Contract Vulnerabilities**
thinking
**Examining Complex Allowance and Rebalance Logic**
I'm unpacking how the reserve contract handles native token allowances during rebalances, especially in rebalanceDexVault where usedBalance calculations interact with both msg.value and value_ parameters. There’s a nuanced interplay where protocols could manipulate reported deposit/withdraw amounts to skirt allowance reductions, possibly enabling repeated fund usage if assumptions about protocol trust hold. I’m also looking closely at access control, upgrade patterns, and SafeTransfer mechanics to see if they introduce risks in these scenarios.
thinking
**Reviewing Approval Logic and Upgrade Patterns**
I’m checking allowance handling for ERC20 tokens versus native tokens, noticing that ERC20 allowances are managed via standard approve calls while native tokens use an internal mapping. I see some minor quirks in how tokens with zero allowance remain listed and in upgrade-related patterns like missing storage gaps. I’m also looking over reentrancy possibilities around approve and rebalance functions, plus how the contract’s upgradeability and access control interrelate, aiming to spot any subtle gaps or inefficiencies.
exec
/bin/bash -lc 'rg -n "TODO|FIXME|TODO" -n contracts' in /tmp/codex-hb1udi63 exited 1 in 49ms:
thinking
**Analyzing potential allowance bypass and accounting risks**
I'm working through a detailed exploration of possible vulnerabilities in the reserve contract's handling of native token allowances during rebalance calls. The main concern is that under certain returned values from the protocol, allowances might not be decremented even though ETH is spent, potentially enabling a rebalancer to exceed allowance limits and drain funds. I'm also checking ERC20 approve mechanics, safe transfer gas assumptions, and edge cases around msg.value and balance accounting to weigh the overall risk and scope of possible issues. This is quite intricate, and I’m aiming to identify any subtle bugs without assuming too much about external protocol behavior.
thinking
**Examining allowance logic and protocol call validations**
I’m reviewing scenarios where the reserve’s allowance checks might be circumvented, especially in `rebalanceDexVault` where `msg.value` and `value_` handling is inconsistent and allowance decrements rely on return values from external protocol calls. I’m also verifying how calls to potentially non-contract addresses behave and assessing approval flows for edge cases around ERC20 tokens and native allowances, searching for any overlooked risks or denial-of-service concerns.
exec
/bin/bash -lc 'rg -n "nativeTokenAllowances" -n contracts/reserve/main.sol' in /tmp/codex-hb1udi63 succeeded in 20ms:
80: existingAllowance_ = nativeTokenAllowances[protocol_];
124: nativeTokenAllowances[protocol_] = amount_;
182: if (nativeTokenAllowances[protocol_] < value_) {
185: nativeTokenAllowances[protocol_] -= value_;
197: if (nativeTokenAllowances[protocol_] < value_) {
200: nativeTokenAllowances[protocol_] -= value_;
239: if (value_ > 0 && nativeTokenAllowances[protocol_] < value_) {
260: nativeTokenAllowances[protocol_] -= usedBalance_;
373: (allowances_[i]).allowance = nativeTokenAllowances[protocol_];
thinking
**Analyzing rebalanceDexVault allowance vulnerability**
exec
/bin/bash -lc "nl -ba contracts/reserve/main.sol | sed -n '200,320p'" in /tmp/codex-hb1udi63 succeeded in 10ms:
200 nativeTokenAllowances[protocol_] -= value_;
201 }
202
203 (int256 colAmount_, int256 debtAmount_) = IFluidVaultT1(protocol_).rebalance{ value: value_ }();
204
205 if (value_ > 0) {
206 IFluidVaultT1.ConstantViews memory constants_ = IFluidVaultT1(protocol_).constantsView();
207 if (constants_.supplyToken == NATIVE_TOKEN_ADDRESS && colAmount_ < 0) {
208 revert FluidReserveContractError(ErrorTypes.ReserveContract__WrongValueSent);
209 }
210
211 if (constants_.borrowToken == NATIVE_TOKEN_ADDRESS && debtAmount_ > 0) {
212 revert FluidReserveContractError(ErrorTypes.ReserveContract__WrongValueSent);
213 }
214
215 if (!(constants_.supplyToken == NATIVE_TOKEN_ADDRESS || constants_.borrowToken == NATIVE_TOKEN_ADDRESS)) {
216 revert FluidReserveContractError(ErrorTypes.ReserveContract__WrongValueSent);
217 }
218 }
219
thinking
**Summarizing potential allowance bypass vulnerability**
I'm reviewing the allowance decrement logic in `rebalanceDexVault` and finding that if the vault returns non-spending amounts, it can bypass allowance reduction on ETH outflows. This could let a trusted rebalancer drain funds beyond limits if the vault behaves maliciously or buggy, so I'm marking it as a medium-severity access control issue with recommendations to enforce allowance decrement regardless of returned values and to validate ETH usage by vaults.
codex
{"no_findings":false,"summary":"One medium issue: native token allowance accounting in rebalanceDexVault can be bypassed if the vault reports non-spending amounts, enabling repeated ETH outflows beyond the intended allowance.","findings":[{"severity":"medium","category":"access_control","title":"rebalanceDexVault can skip allowance reduction despite sending native value","exploit_scenario":"A rebalancer calls rebalanceDexVault with value_ > 0. If the target vault (buggy or malicious) accepts the ETH but returns colAmount_ <= 0 and debtAmount_ >= 0, the allowance reduction block is skipped. The allowance remains unchanged, so the rebalancer can repeat the call to drain native ETH beyond the configured allowance.","recommendation":"When value_ > 0, always decrement allowance based on actual native outflow (e.g., computed via balance delta) regardless of returned col/debt amounts. Additionally, validate that sending native value is only allowed when the vault actually expects native tokens, and revert when value_ is nonzero but no native-token usage is indicated.","evidence":"Allowance is reduced only inside `if (value_ > 0 && (colAmount_ > 0 || debtAmount_ < 0))`, so ETH can be sent without decrementing nativeTokenAllowances if the vault reports non-spending amounts.","locations":["contracts/reserve/main.sol:238","contracts/reserve/main.sol:250"]}]}
tokens used
54,207
{
"codex": {
"ran_at": "2026-01-10T22:10:08.103290+00:00",
"returncode": 0
},
"project": {
"mode": "source",
"file_count": 33,
"import_count": 35,
"first_party_contracts_count": 17
},
"summary": "One medium issue: native token allowance accounting in rebalanceDexVault can be bypassed if the vault reports non-spending amounts, enabling repeated ETH outflows beyond the intended allowance.",
"raw_final": {
"summary": "One medium issue: native token allowance accounting in rebalanceDexVault can be bypassed if the vault reports non-spending amounts, enabling repeated ETH outflows beyond the intended allowance.",
"findings": [
{
"title": "rebalanceDexVault can skip allowance reduction despite sending native value",
"category": "access_control",
"evidence": "Allowance is reduced only inside `if (value_ > 0 && (colAmount_ > 0 || debtAmount_ < 0))`, so ETH can be sent without decrementing nativeTokenAllowances if the vault reports non-spending amounts.",
"severity": "medium",
"locations": [
"contracts/reserve/main.sol:238",
"contracts/reserve/main.sol:250"
],
"recommendation": "When value_ > 0, always decrement allowance based on actual native outflow (e.g., computed via balance delta) regardless of returned col/debt amounts. Additionally, validate that sending native value is only allowed when the vault actually expects native tokens, and revert when value_ is nonzero but no native-token usage is indicated.",
"exploit_scenario": "A rebalancer calls rebalanceDexVault with value_ > 0. If the target vault (buggy or malicious) accepts the ETH but returns colAmount_ <= 0 and debtAmount_ >= 0, the allowance reduction block is skipped. The allowance remains unchanged, so the rebalancer can repeat the call to drain native ETH beyond the configured allowance."
}
],
"no_findings": false
},
"no_findings": false,
"proxy_context": {
"proxy_run_id": "019ba9bc-ec77-7229-831b-371cc06748e3",
"proxy_address": "0x264786ef916af64a1db19f513f24a3681734ce92",
"implementation_run_id": "019ba9bd-3bd2-7165-b27e-350fe4b4d11c",
"implementation_address": "0xfb3102759f2d57f547b9c519db49ce1ffde15db2"
},
"schema_version": 1
}
00000000: PUSH1 0x80 00000002: PUSH1 0x40 00000004: MSTORE 00000005: CALLDATASIZE 00000006: PUSH1 0x10 00000008: JUMPI 00000009: PUSH1 0x0e 0000000b: PUSH1 0x13 0000000d: JUMP 0000000e: JUMPDEST 0000000f: STOP 00000010: JUMPDEST 00000011: PUSH1 0x0e 00000013: JUMPDEST 00000014: PUSH1 0x1f 00000016: PUSH1 0x1b 00000018: PUSH1 0x21 0000001a: JUMP 0000001b: JUMPDEST 0000001c: PUSH1 0x65 0000001e: JUMP 0000001f: JUMPDEST 00000020: JUMP 00000021: JUMPDEST 00000022: PUSH1 0x00 00000024: PUSH1 0x60 00000026: PUSH32 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc 00000047: SLOAD 00000048: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 0000005d: AND 0000005e: SWAP1 0000005f: JUMP 00000060: JUMPDEST 00000061: SWAP1 00000062: POP 00000063: SWAP1 00000064: JUMP 00000065: JUMPDEST 00000066: CALLDATASIZE 00000067: PUSH1 0x00 00000069: DUP1 0000006a: CALLDATACOPY 0000006b: PUSH1 0x00 0000006d: DUP1 0000006e: CALLDATASIZE 0000006f: PUSH1 0x00 00000071: DUP5 00000072: GAS 00000073: DELEGATECALL 00000074: RETURNDATASIZE 00000075: PUSH1 0x00 00000077: DUP1 00000078: RETURNDATACOPY 00000079: DUP1 0000007a: DUP1 0000007b: ISZERO 0000007c: PUSH1 0x83 0000007e: JUMPI 0000007f: RETURNDATASIZE 00000080: PUSH1 0x00 00000082: RETURN 00000083: JUMPDEST 00000084: RETURNDATASIZE 00000085: PUSH1 0x00 00000087: REVERT 00000088: INVALID 00000089: LOG2 0000008a: PUSH5 0x6970667358 00000090: UNKNOWN(0x22) 00000091: SLT 00000092: KECCAK256 00000093: TSTORE 00000094: UNKNOWN(0xE7) 00000095: RETURNDATASIZE 00000096: SGT 00000097: UNKNOWN(0xF8) 00000098: UNKNOWN(0xE2) 00000099: CREATE 0000009a: UNKNOWN(0xF7) 0000009b: PUSH7 0x3d1a7c804ef540 000000a3: UNKNOWN(0xD4) 000000a4: RETURNDATASIZE 000000a5: SWAP7 000000a6: UNKNOWN(0xEE) 000000a7: UNKNOWN(0xDB) 000000a8: LOG0 000000a9: PUSH19 0xff7584b3e0fae926ef64736f6c634300081500 000000bd: CALLER
| Severity | Category | Tool | Title | Confidence | Validated | Confirmed | Details |
|---|---|---|---|---|---|---|---|
| critical | upgradeability | detector | Untrusted DELEGATECALL target reachable | 70 | no | no |
viewtrace_id: 0xc5f97c625e813027bf5969cd7b5405bdab08f94770ea5082401c31f69f4cd93e
call_targets:
0xfb3102759f2d57f547b9c519db49ce1ffde15db2
target_varies:
no
classification:
constant_target
validation_json{
"sink": "DELEGATECALL",
"errors": 0,
"status": "sink_reached",
"attempts": 1,
"trace_id": "0xc5f97c625e813027bf5969cd7b5405bdab08f94770ea5082401c31f69f4cd93e",
"confirmed": false,
"trace_mode": "callTracer",
"call_targets": [
"0xfb3102759f2d57f547b9c519db49ce1ffde15db2"
],
"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": 115,
"opcode": "DELEGATECALL",
"returndata_checked": null,
"value_expression_category": "n/a",
"target_expression_category": "computed"
}
],
"guards_detected": [],
"delegatecall_count": 1,
"untrusted_delegatecall_count": 1
}
|
| medium | upgradeability | cast | DELEGATECALL present | 80 | no | — |
viewevidence_json{
"notes": "delegatecall enables execution in caller context; common in proxies/upgradeable patterns",
"delegatecall_count": 1
}
|
| medium | other | detector | CREATE/CREATE2 reachable | 45 | no | no |
viewtrace_id: 0x24dd05c8da6548110a826654a93e9cf88187eb53a00cf1576a990b52d20d787f
validation_json{
"sink": "CREATE",
"errors": 0,
"status": "unknown",
"attempts": 1,
"trace_id": "0x24dd05c8da6548110a826654a93e9cf88187eb53a00cf1576a990b52d20d787f",
"confirmed": false,
"trace_mode": "structLogs",
"call_targets": null,
"matched_probe": null,
"target_varies": null,
"classification": "no_sink"
}
witness_json{
"notes": "heuristic witness; entrypoints are best-effort selectors with zeroed calldata",
"constraints": [],
"entrypoints": [
{
"calldata": "0x",
"selector": "fallback",
"calldata_variants": [
"0x"
]
}
]
}
evidence_json{
"create_count": 1,
"create2_count": 0
}
|
| medium | access_control | codex | rebalanceDexVault can skip allowance reduction despite sending native value | 75 | no | — |
viewevidence_json{
"evidence": "Allowance is reduced only inside `if (value_ > 0 && (colAmount_ > 0 || debtAmount_ < 0))`, so ETH can be sent without decrementing nativeTokenAllowances if the vault reports non-spending amounts.",
"locations": [
"contracts/reserve/main.sol:238",
"contracts/reserve/main.sol:250"
],
"recommendation": "When value_ > 0, always decrement allowance based on actual native outflow (e.g., computed via balance delta) regardless of returned col/debt amounts. Additionally, validate that sending native value is only allowed when the vault actually expects native tokens, and revert when value_ is nonzero but no native-token usage is indicated.",
"exploit_scenario": "A rebalancer calls rebalanceDexVault with value_ > 0. If the target vault (buggy or malicious) accepts the ETH but returns colAmount_ <= 0 and debtAmount_ >= 0, the allowance reduction block is skipped. The allowance remains unchanged, so the rebalancer can repeat the call to drain native ETH beyond the configured allowance."
}
|
| low | other | cast | Contract creation opcode present | 60 | no | — |
viewevidence_json{
"notes": "runtime can deploy contracts dynamically via CREATE/CREATE2",
"create_count": 1,
"create2_count": 0
}
|
| info | upgradeability | cast | Likely proxy-like runtime | 60 | no | — |
viewevidence_json{
"notes": "short runtime with SLOAD and DELEGATECALL resembles minimal proxy/upgradeable pattern",
"sload_count": 1,
"total_opcodes": 88,
"delegatecall_count": 1
}
|