0b182317-6fc8-49be-9b05-708e9dfa9460
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 |
|---|---|---|---|---|
| 30813 | report.consolidate | complete | 1 / 1 | 3 months ago |
| 104537 | artifact.fetch | complete | 1 / 3 | 1 week ago |
| 104538 | tool.cast_disasm | complete | 1 / 3 | 1 week ago |
| 104539 | analysis.bundle | complete | 1 / 3 | 1 week ago |
| 104540 | capability.graph | complete | 1 / 3 | 1 week ago |
| 104541 | detector.run | complete | 1 / 3 | 1 week ago |
| 104542 | validation.fork | complete | 1 / 3 | 1 week ago |
No slither job recorded yet.
[codex:913858] OpenAI Codex v0.77.0 (research preview)
[codex:913858] --------
[codex:913858] workdir: /tmp/codex-multi-e_tm5cnr
[codex:913858] model: gpt-5.2-codex
[codex:913858] provider: openai
[codex:913858] approval: never
[codex:913858] sandbox: read-only
[codex:913858] reasoning effort: xhigh
[codex:913858] reasoning summaries: auto
[codex:913858] session id: 019b7180-542b-7583-9a6a-42531212ed26
[codex:913858] --------
[codex:913858] user
[codex:913858] You are a security auditor. Audit this Solidity codebase for exploitable vulnerabilities.
[codex:913858] Focus on: reentrancy, access control, upgradeability/proxy issues, signature validation, oracle manipulation,
[codex:913858] accounting/invariant breaks, external call patterns, unchecked return values, and dangerous delegatecall.
[codex:913858]
[codex:913858] Scope rules:
[codex:913858] - You MUST fully read `AUDIT_BUNDLE.txt` before concluding. It contains the complete contents of all first-party `contracts/**/*.sol` files.
[codex:913858] - Treat `lib/` and `@openzeppelin/` as third-party dependencies; skip them unless there is a concrete misuse angle in first-party code.
[codex:913858]
[codex:913858] Output MUST be valid JSON matching the provided schema.
[codex:913858] - If you find nothing meaningful: set no_findings=true and findings=[].
[codex:913858] - Otherwise: set no_findings=false and include findings with severity/category/title and file/line locations when possible.
[codex:913858]
[codex:913858] Output constraints (IMPORTANT):
[codex:913858] - severity MUST be exactly one of: info, low, medium, high, critical (lowercase).
[codex:913858] - category MUST be exactly one of: access_control, reentrancy, math, oracle, upgradeability, auth, other.
[codex:913858] mcp startup: no servers
[codex:913858]
[codex:913858] thinking
[codex:913858] **Preparing to review AGENTS instructions**
[codex:913858] exec
[codex:913858] /bin/bash -lc ls in /tmp/codex-multi-e_tm5cnr succeeded in 5ms:
[codex:913858] AUDIT_BUNDLE.txt
[codex:913858] codex-output-schema.json
[codex:913858] contracts
[codex:913858]
[codex:913858] thinking
[codex:913858] **Reading AUDIT_BUNDLE.txt**
[codex:913858] exec
[codex:913858] /bin/bash -lc 'cat AUDIT_BUNDLE.txt' in /tmp/codex-multi-e_tm5cnr succeeded in 6ms:
[codex:913858] This file concatenates the full contents of first-party Solidity sources for auditing.
[codex:913858]
[codex:913858] ================================================================================
[codex:913858] FILE: contracts/lib/clones-with-immutable-args/src/Clone.sol
[codex:913858] ================================================================================
[codex:913858] // SPDX-License-Identifier: BSD
[codex:913858] pragma solidity ^0.8.4;
[codex:913858]
[codex:913858] /// @title Clone
[codex:913858] /// @author zefram.eth
[codex:913858] /// @notice Provides helper functions for reading immutable args from calldata
[codex:913858] contract Clone {
[codex:913858] /// @notice Reads an immutable arg with type address
[codex:913858] /// @param argOffset The offset of the arg in the packed data
[codex:913858] /// @return arg The arg value
[codex:913858] function _getArgAddress(uint256 argOffset)
[codex:913858] internal
[codex:913858] pure
[codex:913858] returns (address arg)
[codex:913858] {
[codex:913858]
[codex:913858] thinking
[codex:913858] **Preparing to analyze audit bundle contents**
[codex:913858] exec
[codex:913858] /bin/bash -lc 'rg --files contracts/src' in /tmp/codex-multi-e_tm5cnr succeeded in 17ms:
[codex:913858] contracts/src/interfaces/pool/IERC3156FlashBorrower.sol
[codex:913858] contracts/src/interfaces/pool/commons/IPoolImmutables.sol
[codex:913858] contracts/src/interfaces/pool/commons/IPoolEvents.sol
[codex:913858] contracts/src/interfaces/pool/commons/IPoolLenderActions.sol
[codex:913858] contracts/src/interfaces/pool/commons/IPoolSettlerActions.sol
[codex:913858] contracts/src/interfaces/pool/commons/IPoolLPActions.sol
[codex:913858] contracts/src/interfaces/pool/commons/IPoolDerivedState.sol
[codex:913858] contracts/src/interfaces/pool/commons/IPoolState.sol
[codex:913858] contracts/src/interfaces/pool/commons/IPoolBorrowerActions.sol
[codex:913858] contracts/src/interfaces/pool/commons/IPoolTakerActions.sol
[codex:913858] contracts/src/interfaces/pool/commons/IPoolInternals.sol
[codex:913858] contracts/src/interfaces/pool/commons/IPoolKickerActions.sol
[codex:913858] contracts/src/interfaces/pool/commons/IPoolErrors.sol
[codex:913858] contracts/src/interfaces/pool/IPool.sol
[codex:913858] contracts/src/interfaces/pool/erc20/IERC20PoolEvents.sol
[codex:913858] contracts/src/interfaces/pool/erc20/IERC20Taker.sol
[codex:913858] contracts/src/interfaces/pool/erc20/IERC20PoolLenderActions.sol
[codex:913858] contracts/src/interfaces/pool/erc20/IERC20PoolBorrowerActions.sol
[codex:913858] contracts/src/interfaces/pool/erc20/IERC20Pool.sol
[codex:913858] contracts/src/interfaces/pool/erc20/IERC20PoolImmutables.sol
[codex:913858]
[codex:913858] thinking
[codex:913858] **Investigating key contract usage**
[codex:913858] exec
[codex:913858] /bin/bash -lc "rg \"delegatecall|call\\{|transfer\\(|safeTransfer|safeTransferFrom|permit\\(|nonReentrant|reentr\" contracts/src" in /tmp/codex-multi-e_tm5cnr succeeded in 10ms:
[codex:913858] contracts/src/ERC20Pool.sol: ) external nonReentrant {
[codex:913858] contracts/src/ERC20Pool.sol: ) external nonReentrant {
[codex:913858] contracts/src/ERC20Pool.sol: ) external override nonReentrant returns (uint256 bucketLP_) {
[codex:913858] contracts/src/ERC20Pool.sol: ) external override nonReentrant returns (uint256 removedAmount_, uint256 redeemedLP_) {
[codex:913858] contracts/src/ERC20Pool.sol: ) external override nonReentrant {
[codex:913858] contracts/src/ERC20Pool.sol: ) external override nonReentrant {
[codex:913858] contracts/src/ERC20Pool.sol: ) external override nonReentrant {
[codex:913858] contracts/src/ERC20Pool.sol: IERC20(_getArgAddress(COLLATERAL_ADDRESS)).safeTransferFrom(from_, address(this), transferAmount);
[codex:913858] contracts/src/ERC20Pool.sol: IERC20(_getArgAddress(COLLATERAL_ADDRESS)).safeTransfer(to_, amount_ / _getArgUint256(COLLATERAL_SCALE));
[codex:913858] contracts/src/interfaces/pool/IPool.sol: function transfer(address to, uint256 amount) external returns (bool);
[codex:913858] contracts/src/base/FlashloanablePool.sol: ) external virtual override nonReentrant returns (bool success_) {
[codex:913858] contracts/src/base/FlashloanablePool.sol: tokenContract.safeTransfer(
[codex:913858] contracts/src/base/FlashloanablePool.sol: tokenContract.safeTransferFrom(
[codex:913858] contracts/src/base/Pool.sol: ) external override nonReentrant returns (uint256 bucketLP_) {
[codex:913858] contracts/src/base/Pool.sol: ) external override nonReentrant returns (uint256 fromBucketLP_, uint256 toBucketLP_, uint256 movedAmount_) {
[codex:913858] contracts/src/base/Pool.sol: ) external override nonReentrant returns (uint256 removedAmount_, uint256 redeemedLP_) {
[codex:913858] contracts/src/base/Pool.sol: function updateInterest() external override nonReentrant {
[codex:913858] contracts/src/base/Pool.sol: function stampLoan() external override nonReentrant {
[codex:913858] contracts/src/base/Pool.sol: ) external override nonReentrant {
[codex:913858] contracts/src/base/Pool.sol: ) external override nonReentrant {
[codex:913858]
[codex:913858] thinking
[codex:913858] **Reviewing main contract files**
[codex:913858] exec
[codex:913858] /bin/bash -lc "sed -n '1,240p' contracts/src/ERC20Pool.sol" in /tmp/codex-multi-e_tm5cnr succeeded in 7ms:
[codex:913858] // SPDX-License-Identifier: BUSL-1.1
[codex:913858]
[codex:913858] pragma solidity 0.8.18;
[codex:913858]
[codex:913858] import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
[codex:913858] import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
[codex:913858]
[codex:913858] import {
[codex:913858] IERC20Pool,
[codex:913858] IERC20PoolBorrowerActions,
[codex:913858] IERC20PoolImmutables,
[codex:913858] IERC20PoolLenderActions
[codex:913858] } from './interfaces/pool/erc20/IERC20Pool.sol';
[codex:913858] import { IERC20Taker } from './interfaces/pool/erc20/IERC20Taker.sol';
[codex:913858]
[codex:913858] import {
[codex:913858] IPoolLenderActions,
[codex:913858] IPoolKickerActions,
[codex:913858] IPoolTakerActions,
[codex:913858] IPoolSettlerActions
[codex:913858]
[codex:913858] thinking
[codex:913858] **Analyzing contract for vulnerabilities**
[codex:913858]
[codex:913858] I’m focusing on external functions in the pool contract, especially around access control and reentrancy risks. Since user operations seem open, I want to spot any state changes before external calls that could lead to exploits like flashloan reentrancy or price manipulation. I’ll dig into FlashloanablePool.sol next to see how these protections hold up.
[codex:913858] exec
[codex:913858] /bin/bash -lc "sed -n '1,220p' contracts/src/base/FlashloanablePool.sol" in /tmp/codex-multi-e_tm5cnr succeeded in 5ms:
[codex:913858] // SPDX-License-Identifier: BUSL-1.1
[codex:913858]
[codex:913858] pragma solidity 0.8.18;
[codex:913858]
[codex:913858] import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
[codex:913858] import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
[codex:913858]
[codex:913858] import { Pool } from './Pool.sol';
[codex:913858] import { IERC3156FlashBorrower } from '../interfaces/pool/IERC3156FlashBorrower.sol';
[codex:913858]
[codex:913858] /**
[codex:913858] * @title Flashloanable Pool Contract
[codex:913858] * @notice Pool contract with `IERC3156` flashloans capabilities.
[codex:913858] * @notice No fee is charged for taking flashloans from pool.
[codex:913858] * @notice Flashloans can be taking in `ERC20` quote and `ERC20` collateral tokens.
[codex:913858] */
[codex:913858] abstract contract FlashloanablePool is Pool {
[codex:913858] using SafeERC20 for IERC20;
[codex:913858]
[codex:913858] /**
[codex:913858]
[codex:913858] thinking
[codex:913858] **Investigating reentrancy protections**
[codex:913858] exec
[codex:913858] /bin/bash -lc "sed -n '1,200p' contracts/src/base/Pool.sol" in /tmp/codex-multi-e_tm5cnr succeeded in 5ms:
[codex:913858] // SPDX-License-Identifier: BUSL-1.1
[codex:913858]
[codex:913858] pragma solidity 0.8.18;
[codex:913858]
[codex:913858] import { Clone } from '@clones/Clone.sol';
[codex:913858] import { ReentrancyGuard } from '@openzeppelin/contracts/security/ReentrancyGuard.sol';
[codex:913858] import { Multicall } from '@openzeppelin/contracts/utils/Multicall.sol';
[codex:913858] import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
[codex:913858] import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
[codex:913858]
[codex:913858] import {
[codex:913858] IPool,
[codex:913858] IPoolImmutables,
[codex:913858] IPoolBorrowerActions,
[codex:913858] IPoolLPActions,
[codex:913858] IPoolLenderActions,
[codex:913858] IPoolKickerActions,
[codex:913858] IPoolTakerActions,
[codex:913858] IPoolSettlerActions,
[codex:913858] IPoolState,
[codex:913858]
[codex:913858] thinking
[codex:913858] **Inspecting smart contract vulnerabilities**
[codex:913858] exec
[codex:913858] /bin/bash -lc 'rg "delegatecall" contracts/src' in /tmp/codex-multi-e_tm5cnr exited 1 in 9ms:
[codex:913858]
[codex:913858]
[codex:913858] thinking
[codex:913858] **Searching for permit usage**
[codex:913858] exec
[codex:913858] /bin/bash -lc 'rg "permit|signature|sig" contracts/src' in /tmp/codex-multi-e_tm5cnr succeeded in 9ms:
[codex:913858] contracts/src/libraries/helpers/PoolHelper.sol: int256 sign;
[codex:913858] contracts/src/libraries/helpers/PoolHelper.sol: sign = Maths.minInt(
[codex:913858] contracts/src/libraries/helpers/PoolHelper.sol: if (val < 0 ) sign = -1e18;
[codex:913858] contracts/src/libraries/helpers/PoolHelper.sol: else if (val != 0) sign = 1e18;
[codex:913858] contracts/src/libraries/helpers/PoolHelper.sol: return PRBMathSD59x18.mul(int256(bondFactor_), sign);
[codex:913858] contracts/src/libraries/internal/Deposits.sol: // Shift i to next less significant bit
[codex:913858] contracts/src/libraries/internal/Deposits.sol: * @notice Get least significant bit (`LSB`) of integer `i_`.
[codex:913858] contracts/src/libraries/internal/Deposits.sol: // consider next most significant bit
[codex:913858] contracts/src/libraries/internal/Deposits.sol: // shift j to consider next less signficant bit
[codex:913858]
[codex:913858] thinking
[codex:913858] **Analyzing external calls and transfer safety**
[codex:913858] exec
[codex:913858] /bin/bash -lc 'rg "_transferQuoteToken|_transferCollateral" -n contracts/src/base/Pool.sol contracts/src/ERC20Pool.sol' in /tmp/codex-multi-e_tm5cnr succeeded in 9ms:
[codex:913858] contracts/src/base/Pool.sol:181: _transferQuoteTokenFrom(msg.sender, amount_);
[codex:913858] contracts/src/base/Pool.sol:255: _transferQuoteToken(msg.sender, removedAmount_);
[codex:913858] contracts/src/base/Pool.sol:327: if (result.amountToCoverBond != 0) _transferQuoteTokenFrom(msg.sender, result.amountToCoverBond);
[codex:913858] contracts/src/base/Pool.sol:374: if (result.amountToCoverBond != 0) _transferQuoteTokenFrom(msg.sender, result.amountToCoverBond);
[codex:913858] contracts/src/base/Pool.sol:402: _transferQuoteToken(recipient_, maxAmount_);
[codex:913858] contracts/src/base/Pool.sol:433: _transferQuoteToken(msg.sender, kickerAward);
[codex:913858] contracts/src/base/Pool.sol:457: _transferQuoteToken(msg.sender, amount_);
[codex:913858] contracts/src/base/Pool.sol:714: function _transferQuoteTokenFrom(address from_, uint256 amount_) internal {
[codex:913858] contracts/src/base/Pool.sol:725: function _transferQuoteToken(address to_, uint256 amount_) internal {
[codex:913858] contracts/src/ERC20Pool.sol:188: _transferCollateralFrom(msg.sender, collateralToPledge_);
[codex:913858] contracts/src/ERC20Pool.sol:196: _transferQuoteToken(msg.sender, amountToBorrow_);
[codex:913858] contracts/src/ERC20Pool.sol:267: _transferQuoteTokenFrom(msg.sender, result.quoteTokenToRepay);
[codex:913858] contracts/src/ERC20Pool.sol:274: _transferCollateral(collateralReceiver_, collateralAmountToPull_);
[codex:913858] contracts/src/ERC20Pool.sol:314: _transferCollateralFrom(msg.sender, amountToAdd_);
[codex:913858] contracts/src/ERC20Pool.sol:347: _transferCollateral(msg.sender, removedAmount_);
[codex:913858] contracts/src/ERC20Pool.sol:422: _transferCollateral(callee_, result.collateralAmount);
[codex:913858] contracts/src/ERC20Pool.sol:432: _transferQuoteTokenFrom(msg.sender, result.quoteTokenAmount);
[codex:913858] contracts/src/ERC20Pool.sol:489: function _transferCollateralFrom(address from_, uint256 amount_) internal {
[codex:913858] contracts/src/ERC20Pool.sol:500: function _transferCollateral(address to_, uint256 amount_) internal {
[codex:913858]
[codex:913858] thinking
[codex:913858] **Inspecting internal transfer functions**
[codex:913858] exec
[codex:913858] /bin/bash -lc "sed -n '680,760p' contracts/src/base/Pool.sol" in /tmp/codex-multi-e_tm5cnr succeeded in 5ms:
[codex:913858] * @dev === Write state ===
[codex:913858] * @dev - `PoolCommons.updateInterestState`
[codex:913858] * @dev `EMA`s accumulators
[codex:913858] * @dev interest rate accumulator and `interestRateUpdate` state
[codex:913858] * @dev pool inflator and `inflatorUpdate` state
[codex:913858] * @dev === Emit events ===
[codex:913858] * @dev `PoolCommons.updateInterestState`: `UpdateInterestRate`
[codex:913858] * @param poolState_ Struct containing pool details.
[codex:913858] * @param lup_ Current `LUP` in pool.
[codex:913858] */
[codex:913858] function _updateInterestState(
[codex:913858] PoolState memory poolState_,
[codex:913858] uint256 lup_
[codex:913858] ) internal {
[codex:913858]
[codex:913858] PoolCommons.updateInterestState(interestState, emaState, deposits, poolState_, lup_);
[codex:913858]
[codex:913858] // update pool inflator
[codex:913858] if (poolState_.isNewInterestAccrued) {
[codex:913858] inflatorState.inflator = uint208(poolState_.inflator);
[codex:913858]
[codex:913858] thinking
[codex:913858] **Analyzing rounding asymmetry in token transfers**
[codex:913858] exec
[codex:913858] /bin/bash -lc "sed -n '470,540p' contracts/src/ERC20Pool.sol" in /tmp/codex-multi-e_tm5cnr succeeded in 7ms:
[codex:913858] /**
[codex:913858] * @inheritdoc FlashloanablePool
[codex:913858] * @dev Override default implementation and allows flashloans for both quote and collateral token.
[codex:913858] */
[codex:913858] function _isFlashloanSupported(
[codex:913858] address token_
[codex:913858] ) internal virtual view override returns (bool) {
[codex:913858] return token_ == _getArgAddress(QUOTE_ADDRESS) || token_ == _getArgAddress(COLLATERAL_ADDRESS);
[codex:913858] }
[codex:913858]
[codex:913858] /************************/
[codex:913858] /*** Helper Functions ***/
[codex:913858] /************************/
[codex:913858]
[codex:913858] /**
[codex:913858] * @notice Helper function to transfer amount of collateral tokens from sender to pool contract.
[codex:913858] * @param from_ Sender address.
[codex:913858] * @param amount_ Amount to transfer from sender (`WAD` precision). Scaled to collateral precision before transfer.
[codex:913858] */
[codex:913858] function _transferCollateralFrom(address from_, uint256 amount_) internal {
[codex:913858]
[codex:913858] thinking
[codex:913858] **Analyzing external calls and reentrancy risks**
[codex:913858] exec
[codex:913858] /bin/bash -lc 'rg "external" -n contracts/src/base/Pool.sol contracts/src/ERC20Pool.sol contracts/src/base/FlashloanablePool.sol' in /tmp/codex-multi-e_tm5cnr succeeded in 8ms:
[codex:913858] contracts/src/base/Pool.sol:67:import { BorrowerActions } from '../libraries/external/BorrowerActions.sol';
[codex:913858] contracts/src/base/Pool.sol:68:import { LenderActions } from '../libraries/external/LenderActions.sol';
[codex:913858] contracts/src/base/Pool.sol:69:import { LPActions } from '../libraries/external/LPActions.sol';
[codex:913858] contracts/src/base/Pool.sol:70:import { KickerActions } from '../libraries/external/KickerActions.sol';
[codex:913858] contracts/src/base/Pool.sol:71:import { TakerActions } from '../libraries/external/TakerActions.sol';
[codex:913858] contracts/src/base/Pool.sol:72:import { PoolCommons } from '../libraries/external/PoolCommons.sol';
[codex:913858] contracts/src/base/Pool.sol:125: function poolType() external pure override returns (uint8) {
[codex:913858] contracts/src/base/Pool.sol:130: function collateralAddress() external pure override returns (address) {
[codex:913858] contracts/src/base/Pool.sol:135: function quoteTokenAddress() external pure override returns (address) {
[codex:913858] contracts/src/base/Pool.sol:140: function quoteTokenScale() external pure override returns (uint256) {
[codex:913858] contracts/src/base/Pool.sol:155: ) external override nonReentrant returns (uint256 bucketLP_) {
[codex:913858] contracts/src/base/Pool.sol:191: ) external override nonReentrant returns (uint256 fromBucketLP_, uint256 toBucketLP_, uint256 movedAmount_) {
[codex:913858] contracts/src/base/Pool.sol:228: ) external override nonReentrant returns (uint256 removedAmount_, uint256 redeemedLP_) {
[codex:913858] contracts/src/base/Pool.sol:259: function updateInterest() external override nonReentrant {
[codex:913858] contracts/src/base/Pool.sol:269: function stampLoan() external override nonReentrant {
[codex:913858] contracts/src/base/Pool.sol:295: ) external override nonReentrant {
[codex:913858] contracts/src/base/Pool.sol:339: ) external override nonReentrant {
[codex:913858] contracts/src/base/Pool.sol:386: ) external override nonReentrant {
[codex:913858] contracts/src/base/Pool.sol:419: function kickReserveAuction() external override nonReentrant {
[codex:913858] contracts/src/base/Pool.sol:444: ) external override nonReentrant returns (uint256 amount_) {
[codex:913858]
[codex:913858] thinking
[codex:913858] **Checking external Pool functions for nonReentrant**
[codex:913858] exec
[codex:913858] /bin/bash -lc "sed -n '480,560p' contracts/src/base/Pool.sol" in /tmp/codex-multi-e_tm5cnr succeeded in 5ms:
[codex:913858] address spender_,
[codex:913858] uint256[] calldata indexes_,
[codex:913858] uint256[] calldata amounts_
[codex:913858] ) external override nonReentrant {
[codex:913858] LPActions.decreaseLPAllowance(
[codex:913858] _lpAllowances[msg.sender][spender_],
[codex:913858] spender_,
[codex:913858] indexes_,
[codex:913858] amounts_
[codex:913858] );
[codex:913858] }
[codex:913858]
[codex:913858] /// @inheritdoc IPoolLPActions
[codex:913858] function revokeLPAllowance(
[codex:913858] address spender_,
[codex:913858] uint256[] calldata indexes_
[codex:913858] ) external override nonReentrant {
[codex:913858] LPActions.revokeLPAllowance(
[codex:913858] _lpAllowances[msg.sender][spender_],
[codex:913858] spender_,
[codex:913858]
[codex:913858] thinking
[codex:913858] **Evaluating reentrancy risk**
[codex:913858] exec
[codex:913858] /bin/bash -lc "sed -n '1,240p' contracts/src/libraries/external/LPActions.sol" in /tmp/codex-multi-e_tm5cnr succeeded in 5ms:
[codex:913858] // SPDX-License-Identifier: BUSL-1.1
[codex:913858]
[codex:913858] pragma solidity 0.8.18;
[codex:913858]
[codex:913858] import { Bucket, Lender } from '../../interfaces/pool/commons/IPoolState.sol';
[codex:913858]
[codex:913858] import { MAX_FENWICK_INDEX } from '../helpers/PoolHelper.sol';
[codex:913858]
[codex:913858] import { Maths } from '../internal/Maths.sol';
[codex:913858]
[codex:913858] /**
[codex:913858] @title LPActions library
[codex:913858] @notice External library containing logic for `LP` owners to:
[codex:913858] - `increase`/`decrease`/`revoke` `LP` allowance; `approve`/`revoke` `LP` transferors; `transfer` `LP`
[codex:913858] */
[codex:913858] library LPActions {
[codex:913858]
[codex:913858] /**************/
[codex:913858] /*** Events ***/
[codex:913858] /**************/
[codex:913858]
[codex:913858] thinking
[codex:913858] **Extending transferLP logic**
[codex:913858] exec
[codex:913858] /bin/bash -lc "sed -n '240,480p' contracts/src/libraries/external/LPActions.sol" in /tmp/codex-multi-e_tm5cnr succeeded in 5ms:
[codex:913858] uint256 allowedAmount = allowances_[ownerAddress_][newOwnerAddress_][index];
[codex:913858] if (allowedAmount == 0) revert NoAllowance();
[codex:913858]
[codex:913858] // transfer allowed amount or entire LP balance
[codex:913858] allowedAmount = Maths.min(allowedAmount, ownerLpBalance);
[codex:913858]
[codex:913858] // move owner LP (if any) to the new owner
[codex:913858] if (allowedAmount != 0) {
[codex:913858] Lender storage newOwner = bucket.lenders[newOwnerAddress_];
[codex:913858]
[codex:913858] uint256 newOwnerDepositTime = newOwner.depositTime;
[codex:913858]
[codex:913858] if (newOwnerDepositTime > bankruptcyTime) {
[codex:913858] // deposit happened in a healthy bucket, add amount of LP to new owner
[codex:913858] newOwner.lps += allowedAmount;
[codex:913858] } else {
[codex:913858] // bucket bankruptcy happened after deposit, reset balance and add amount of LP to new owner
[codex:913858] newOwner.lps = allowedAmount;
[codex:913858] }
[codex:913858]
[codex:913858]
[codex:913858] thinking
[codex:913858] **Reviewing potential vulnerabilities in actions and initialization**
[codex:913858] exec
[codex:913858] /bin/bash -lc "sed -n '1,240p' contracts/src/libraries/external/BorrowerActions.sol" in /tmp/codex-multi-e_tm5cnr succeeded in 5ms:
[codex:913858] // SPDX-License-Identifier: BUSL-1.1
[codex:913858]
[codex:913858] pragma solidity 0.8.18;
[codex:913858]
[codex:913858] import {
[codex:913858] AuctionsState,
[codex:913858] Borrower,
[codex:913858] Bucket,
[codex:913858] DepositsState,
[codex:913858] LoansState,
[codex:913858] PoolState
[codex:913858] } from '../../interfaces/pool/commons/IPoolState.sol';
[codex:913858] import {
[codex:913858] DrawDebtResult,
[codex:913858] RepayDebtResult
[codex:913858] } from '../../interfaces/pool/commons/IPoolInternals.sol';
[codex:913858]
[codex:913858] import {
[codex:913858] _borrowFeeRate,
[codex:913858] _priceAt,
[codex:913858]
[codex:913858] thinking
[codex:913858] **Analyzing potential vulnerabilities in token interactions**
[codex:913858] exec
[codex:913858] /bin/bash -lc "sed -n '1,260p' contracts/src/libraries/external/TakerActions.sol" in /tmp/codex-multi-e_tm5cnr succeeded in 5ms:
[codex:913858] // SPDX-License-Identifier: BUSL-1.1
[codex:913858]
[codex:913858] pragma solidity 0.8.18;
[codex:913858]
[codex:913858] import { PRBMathSD59x18 } from "@prb-math/contracts/PRBMathSD59x18.sol";
[codex:913858] import { Math } from '@openzeppelin/contracts/utils/math/Math.sol';
[codex:913858]
[codex:913858] import { PoolType } from '../../interfaces/pool/IPool.sol';
[codex:913858]
[codex:913858] import {
[codex:913858] AuctionsState,
[codex:913858] Borrower,
[codex:913858] Bucket,
[codex:913858] BurnEvent,
[codex:913858] DepositsState,
[codex:913858] Liquidation,
[codex:913858] LoansState,
[codex:913858] PoolState,
[codex:913858] ReserveAuctionState
[codex:913858] } from '../../interfaces/pool/commons/IPoolState.sol';
[codex:913858]
[codex:913858] thinking
[codex:913858] **Investigating TakerActions and IERC20Taker**
[codex:913858] exec
[codex:913858] /bin/bash -lc 'rg "IERC20Taker|onFlashLoan|callback" -n contracts/src/libraries/external/TakerActions.sol' in /tmp/codex-multi-e_tm5cnr exited 1 in 7ms:
[codex:913858]
[codex:913858]
[codex:913858] thinking
[codex:913858] **Searching ERC20Pool for IERC20Taker usage**
[codex:913858] exec
[codex:913858] /bin/bash -lc 'rg "IERC20Taker|taker" -n contracts/src/ERC20Pool.sol' in /tmp/codex-multi-e_tm5cnr succeeded in 6ms:
[codex:913858] 14:import { IERC20Taker } from './interfaces/pool/erc20/IERC20Taker.sol';
[codex:913858] 425: IERC20Taker(callee_).atomicSwapCallback(
[codex:913858]
[codex:913858] thinking
[codex:913858] **Inspecting ERC20Pool external calls**
[codex:913858] exec
[codex:913858] /bin/bash -lc "sed -n '380,460p' contracts/src/ERC20Pool.sol" in /tmp/codex-multi-e_tm5cnr succeeded in 7ms:
[codex:913858] })
[codex:913858] );
[codex:913858]
[codex:913858] _updatePostSettleState(result, poolState);
[codex:913858] }
[codex:913858]
[codex:913858] /**
[codex:913858] * @inheritdoc IPoolTakerActions
[codex:913858] * @dev === Write state ===
[codex:913858] * @dev - decrement `poolBalances.t0Debt` accumulator
[codex:913858] * @dev - decrement `poolBalances.t0DebtInAuction` accumulator
[codex:913858] * @dev - decrement `poolBalances.pledgedCollateral` accumulator
[codex:913858] * @dev - update `t0Debt2ToCollateral` ratio only if auction settled, debt and collateral pre action are considered 0
[codex:913858] */
[codex:913858] function take(
[codex:913858] address borrowerAddress_,
[codex:913858] uint256 maxAmount_,
[codex:913858] address callee_,
[codex:913858] bytes calldata data_
[codex:913858] ) external override nonReentrant {
[codex:913858]
[codex:913858] thinking
[codex:913858] **Analyzing reentrancy risks in take()**
[codex:913858]
[codex:913858] I'm examining the take() function's flow to see if transferring collateral before pulling quote tokens and invoking callbacks could cause reentrancy vulnerabilities, considering nonReentrant guards and token behaviors like ERC777 hooks. I want to confirm that state updates happen before external calls and identify any unguarded functions that might be exploited. Next, I'll look into _updatePostTakeState to verify state consistency after take actions.
[codex:913858] exec
[codex:913858] /bin/bash -lc 'rg "_updatePostTakeState" -n contracts/src/ERC20Pool.sol contracts/src/base/Pool.sol' in /tmp/codex-multi-e_tm5cnr succeeded in 8ms:
[codex:913858] contracts/src/ERC20Pool.sol:420: _updatePostTakeState(result, poolState);
[codex:913858] contracts/src/ERC20Pool.sol:463: _updatePostTakeState(result, poolState);
[codex:913858] contracts/src/base/Pool.sol:597: function _updatePostTakeState(
[codex:913858] exec
[codex:913858] /bin/bash -lc "sed -n '560,640p' contracts/src/base/Pool.sol" in /tmp/codex-multi-e_tm5cnr succeeded in 6ms:
[codex:913858] poolState_.quoteTokenScale = _getArgUint256(QUOTE_SCALE);
[codex:913858]
[codex:913858] // check if t0Debt is not equal to 0, indicating that there is debt to be tracked for the pool
[codex:913858] if (poolState_.t0Debt != 0) {
[codex:913858] // Calculate prior pool debt
[codex:913858] poolState_.debt = Maths.wmul(poolState_.t0Debt, poolState_.inflator);
[codex:913858]
[codex:913858] // calculate elapsed time since inflator was last updated
[codex:913858] uint256 elapsed = block.timestamp - inflatorState.inflatorUpdate;
[codex:913858]
[codex:913858] // set isNewInterestAccrued field to true if elapsed time is not 0, indicating that new interest may have accrued
[codex:913858] poolState_.isNewInterestAccrued = elapsed != 0;
[codex:913858]
[codex:913858] // if new interest may have accrued, call accrueInterest function and update inflator and debt fields of poolState_ struct
[codex:913858] if (poolState_.isNewInterestAccrued) {
[codex:913858] (uint256 newInflator, uint256 newInterest) = PoolCommons.accrueInterest(
[codex:913858] emaState,
[codex:913858] deposits,
[codex:913858] poolState_,
[codex:913858] Loans.getMax(loans).thresholdPrice,
[codex:913858]
[codex:913858] thinking
[codex:913858] **Analyzing potential contract vulnerabilities**
[codex:913858]
[codex:913858] I'm carefully reviewing the contract's functions for risks like reentrancy, access control gaps, flashloan abuse, and rounding errors in token accounting. There's a lot to unpack regarding callback behaviors, allowance handling, initialization protections, and deposit-withdraw timing that might impact loan pricing or pool reserves. I'll keep digging into key areas like auctions, LP allowances, and token transfers, making sure state changes and external calls align safely. This looks like an in-depth audit of logic, external calls, and upgrade/security patterns!
[codex:913858] exec
[codex:913858] /bin/bash -lc "sed -n '200,360p' contracts/src/base/Pool.sol" in /tmp/codex-multi-e_tm5cnr succeeded in 5ms:
[codex:913858] MoveQuoteParams memory moveParams;
[codex:913858] moveParams.maxAmountToMove = maxAmount_;
[codex:913858] moveParams.fromIndex = fromIndex_;
[codex:913858] moveParams.toIndex = toIndex_;
[codex:913858] moveParams.thresholdPrice = Loans.getMax(loans).thresholdPrice;
[codex:913858] moveParams.revertIfBelowLup = revertIfBelowLup_;
[codex:913858]
[codex:913858] uint256 newLup;
[codex:913858] (
[codex:913858] fromBucketLP_,
[codex:913858] toBucketLP_,
[codex:913858] movedAmount_,
[codex:913858] newLup
[codex:913858] ) = LenderActions.moveQuoteToken(
[codex:913858] buckets,
[codex:913858] deposits,
[codex:913858] poolState,
[codex:913858] moveParams
[codex:913858] );
[codex:913858]
[codex:913858]
[codex:913858] thinking
[codex:913858] **Verifying LenderActions value scaling**
[codex:913858] exec
[codex:913858] /bin/bash -lc 'rg "removeQuoteToken" -n contracts/src/libraries/external/LenderActions.sol' in /tmp/codex-multi-e_tm5cnr succeeded in 6ms:
[codex:913858] 55: /// @dev Struct used for `removeQuoteToken` function local vars.
[codex:913858] 399: function removeQuoteToken(
[codex:913858] exec
[codex:913858] /bin/bash -lc "sed -n '360,460p' contracts/src/libraries/external/LenderActions.sol" in /tmp/codex-multi-e_tm5cnr succeeded in 5ms:
[codex:913858] toBucketLender.lps = toBucketLP_;
[codex:913858]
[codex:913858] // set deposit time of the lender's to bucket as bucket's last bankruptcy timestamp + 1 so deposit won't get invalidated
[codex:913858] vars.toBucketDepositTime = vars.toBucketBankruptcyTime + 1;
[codex:913858] } else {
[codex:913858] toBucketLender.lps += toBucketLP_;
[codex:913858] }
[codex:913858]
[codex:913858] // set deposit time to the greater of the lender's from bucket and the target bucket
[codex:913858] toBucketLender.depositTime = Maths.max(vars.fromBucketDepositTime, vars.toBucketDepositTime);
[codex:913858]
[codex:913858] // update bucket LP balance
[codex:913858] toBucket.lps += toBucketLP_;
[codex:913858]
[codex:913858] emit MoveQuoteToken(
[codex:913858] msg.sender,
[codex:913858] params_.fromIndex,
[codex:913858] params_.toIndex,
[codex:913858] movedAmount_,
[codex:913858] fromBucketRedeemedLP_,
[codex:913858]
[codex:913858] thinking
[codex:913858] **Analyzing token transfer rounding and access control risks**
[codex:913858] exec
[codex:913858] /bin/bash -lc "sed -n '240,360p' contracts/src/ERC20Pool.sol" in /tmp/codex-multi-e_tm5cnr succeeded in 6ms:
[codex:913858] poolState.t0Debt = result.t0PoolDebt;
[codex:913858] poolState.collateral = result.poolCollateral;
[codex:913858]
[codex:913858] // update t0 debt in auction in memory pool state struct and pool balances state
[codex:913858] if (result.t0DebtInAuctionChange != 0) {
[codex:913858] poolState.t0DebtInAuction -= result.t0DebtInAuctionChange;
[codex:913858] poolBalances.t0DebtInAuction = poolState.t0DebtInAuction;
[codex:913858] }
[codex:913858]
[codex:913858] // adjust t0Debt2ToCollateral ratio if loan not in auction
[codex:913858] if (!result.inAuction) {
[codex:913858] _updateT0Debt2ToCollateral(
[codex:913858] result.settledAuction ? 0 : result.debtPreAction, // debt pre settle (for loan in auction) not taken into account
[codex:913858] result.debtPostAction,
[codex:913858] result.settledAuction ? 0 : result.collateralPreAction, // collateral pre settle (for loan in auction) not taken into account
[codex:913858] result.collateralPostAction
[codex:913858] );
[codex:913858] }
[codex:913858]
[codex:913858] // update pool interest rate state
[codex:913858]
[codex:913858] thinking
[codex:913858] **Investigating potential rounding and dust logic issues**
[codex:913858] exec
[codex:913858] /bin/bash -lc "sed -n '1,200p' contracts/src/libraries/external/PoolCommons.sol" in /tmp/codex-multi-e_tm5cnr succeeded in 6ms:
[codex:913858] // SPDX-License-Identifier: BUSL-1.1
[codex:913858]
[codex:913858] pragma solidity 0.8.18;
[codex:913858]
[codex:913858] import { PRBMathSD59x18 } from "@prb-math/contracts/PRBMathSD59x18.sol";
[codex:913858] import { PRBMathUD60x18 } from "@prb-math/contracts/PRBMathUD60x18.sol";
[codex:913858]
[codex:913858] import { InterestState, EmaState, PoolState, DepositsState } from '../../interfaces/pool/commons/IPoolState.sol';
[codex:913858]
[codex:913858] import { _dwatp, _indexOf, MAX_FENWICK_INDEX, MIN_PRICE, MAX_PRICE } from '../helpers/PoolHelper.sol';
[codex:913858]
[codex:913858] import { Deposits } from '../internal/Deposits.sol';
[codex:913858] import { Buckets } from '../internal/Buckets.sol';
[codex:913858] import { Loans } from '../internal/Loans.sol';
[codex:913858] import { Maths } from '../internal/Maths.sol';
[codex:913858]
[codex:913858] /**
[codex:913858] @title PoolCommons library
[codex:913858] @notice External library containing logic for common pool functionality:
[codex:913858] - interest rate accrual and interest rate params update
[codex:913858]
[codex:913858] thinking
[codex:913858] **Analyzing potential smart contract vulnerabilities**
[codex:913858]
[codex:913858] I'm closely examining various functions related to allowances, transfers, flash loans, multicall, and token scaling to find possible security issues. I'm testing assumptions about ownership checks, approval flows, reentrancy protections, and rounding errors to see if any of these might lead to exploitable behavior. There's a lot to piece together here!
[codex:913858] exec
[codex:913858] /bin/bash -lc 'rg "_roundToScale|_roundUpToScale" -n contracts/src/libraries/helpers/PoolHelper.sol' in /tmp/codex-multi-e_tm5cnr succeeded in 6ms:
[codex:913858] 260: function _roundToScale(
[codex:913858] 273: function _roundUpToScale(
[codex:913858] 280: scaledAmount_ = _roundToScale(amount_, tokenScale_) + tokenScale_;
[codex:913858] exec
[codex:913858] /bin/bash -lc "sed -n '240,300p' contracts/src/libraries/helpers/PoolHelper.sol" in /tmp/codex-multi-e_tm5cnr succeeded in 5ms:
[codex:913858] ) pure returns (uint256 quoteTokenAmount_) {
[codex:913858] quoteTokenAmount_ = Buckets.lpToQuoteTokens(
[codex:913858] bucketCollateral_,
[codex:913858] bucketLP_,
[codex:913858] deposit_,
[codex:913858] lenderLPBalance_,
[codex:913858] bucketPrice_,
[codex:913858] Math.Rounding.Down
[codex:913858] );
[codex:913858]
[codex:913858] if (quoteTokenAmount_ > deposit_) quoteTokenAmount_ = deposit_;
[codex:913858] if (quoteTokenAmount_ > maxQuoteToken_) quoteTokenAmount_ = maxQuoteToken_;
[codex:913858] }
[codex:913858]
[codex:913858] /**
[codex:913858] * @notice Rounds a token amount down to the minimum amount permissible by the token scale.
[codex:913858] * @param amount_ Value to be rounded.
[codex:913858] * @param tokenScale_ Scale of the token, presented as a power of `10`.
[codex:913858] * @return scaledAmount_ Rounded value.
[codex:913858] */
[codex:913858]
[codex:913858] thinking
[codex:913858] **Analyzing rounding and transfer logic**
[codex:913858] exec
[codex:913858] /bin/bash -lc 'rg "repayDebt" -n contracts/src/libraries/external/BorrowerActions.sol' in /tmp/codex-multi-e_tm5cnr succeeded in 6ms:
[codex:913858] 57: /// @dev Struct used for `repayDebt` function local vars.
[codex:913858] 270: function repayDebt(
[codex:913858] exec
[codex:913858] /bin/bash -lc "sed -n '260,420p' contracts/src/libraries/external/BorrowerActions.sol" in /tmp/codex-multi-e_tm5cnr succeeded in 5ms:
[codex:913858] * @dev update borrower in `address => borrower` mapping
[codex:913858] * @dev === Reverts on ===
[codex:913858] * @dev no debt to repay `NoDebt()`
[codex:913858] * @dev borrower debt less than pool min debt `AmountLTMinDebt()`
[codex:913858] * @dev borrower not sender `BorrowerNotSender()`
[codex:913858] * @dev not enough collateral to pull `InsufficientCollateral()`
[codex:913858] * @dev limit price reached `LimitIndexExceeded()`
[codex:913858] * @dev === Emit events ===
[codex:913858] * @dev - `SettlerActions._settleAuction`: `AuctionNFTSettle` or `AuctionSettle`
[codex:913858] */
[codex:913858] function repayDebt(
[codex:913858] AuctionsState storage auctions_,
[codex:913858] mapping(uint256 => Bucket) storage buckets_,
[codex:913858] DepositsState storage deposits_,
[codex:913858] LoansState storage loans_,
[codex:913858] PoolState calldata poolState_,
[codex:913858] address borrowerAddress_,
[codex:913858] uint256 maxQuoteTokenAmountToRepay_,
[codex:913858] uint256 collateralAmountToPull_,
[codex:913858] uint256 limitIndex_
[codex:913858]
[codex:913858] thinking
[codex:913858] **Reviewing repayDebt restrictions**
[codex:913858] exec
[codex:913858] /bin/bash -lc "sed -n '1,240p' contracts/src/libraries/external/KickerActions.sol" in /tmp/codex-multi-e_tm5cnr succeeded in 5ms:
[codex:913858] // SPDX-License-Identifier: BUSL-1.1
[codex:913858]
[codex:913858] pragma solidity 0.8.18;
[codex:913858]
[codex:913858] import { Math } from '@openzeppelin/contracts/utils/math/Math.sol';
[codex:913858] import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol";
[codex:913858]
[codex:913858] import { PoolType } from '../../interfaces/pool/IPool.sol';
[codex:913858]
[codex:913858] import {
[codex:913858] AuctionsState,
[codex:913858] Borrower,
[codex:913858] Bucket,
[codex:913858] DepositsState,
[codex:913858] Kicker,
[codex:913858] Lender,
[codex:913858] Liquidation,
[codex:913858] LoansState,
[codex:913858] PoolState,
[codex:913858] ReserveAuctionState
[codex:913858]
[codex:913858] thinking
[codex:913858] **Analyzing potential contract vulnerabilities**
[codex:913858] exec
[codex:913858] /bin/bash -lc "sed -n '440,520p' contracts/src/ERC20Pool.sol" in /tmp/codex-multi-e_tm5cnr succeeded in 5ms:
[codex:913858] * @dev - decrement `poolBalances.pledgedCollateral` accumulator
[codex:913858] * @dev - update `t0Debt2ToCollateral` ratio only if auction settled, debt and collateral pre action are considered 0
[codex:913858] */
[codex:913858] function bucketTake(
[codex:913858] address borrowerAddress_,
[codex:913858] bool depositTake_,
[codex:913858] uint256 index_
[codex:913858] ) external override nonReentrant {
[codex:913858]
[codex:913858] PoolState memory poolState = _accruePoolInterest();
[codex:913858]
[codex:913858] TakeResult memory result = TakerActions.bucketTake(
[codex:913858] auctions,
[codex:913858] buckets,
[codex:913858] deposits,
[codex:913858] loans,
[codex:913858] poolState,
[codex:913858] borrowerAddress_,
[codex:913858] depositTake_,
[codex:913858] index_,
[codex:913858]
[codex:913858] thinking
[codex:913858] **Verifying bucketTake token transfers**
[codex:913858] exec
[codex:913858] /bin/bash -lc 'rg "depositTake" -n contracts/src/libraries/external/TakerActions.sol' in /tmp/codex-multi-e_tm5cnr succeeded in 6ms:
[codex:913858] 56: bool depositTake; // deposit or arb take, used by bucket take
[codex:913858] 140: bool depositTake_,
[codex:913858] 160: depositTake: depositTake_,
[codex:913858] 442: if (params_.depositTake) vars_.auctionPrice = vars_.bucketPrice;
[codex:913858] 462: params_.depositTake,
[codex:913858] 603: * @param depositTake_ If `true` then the take will happen at an auction price equal with bucket price. Auction price is used otherwise.
[codex:913858] 612: bool depositTake_,
[codex:913858] 624: if (!depositTake_) {
[codex:913858] exec
[codex:913858] /bin/bash -lc "sed -n '420,520p' contracts/src/libraries/external/TakerActions.sol" in /tmp/codex-multi-e_tm5cnr succeeded in 5ms:
[codex:913858] BucketTakeParams memory params_
[codex:913858] ) internal returns (TakeLocalVars memory vars_) {
[codex:913858] Liquidation storage liquidation = auctions_.liquidations[params_.borrower];
[codex:913858]
[codex:913858] vars_= _prepareTake(
[codex:913858] liquidation,
[codex:913858] borrower_.t0Debt,
[codex:913858] borrower_.collateral,
[codex:913858] params_.inflator
[codex:913858] );
[codex:913858]
[codex:913858] vars_.unscaledDeposit = Deposits.unscaledValueAt(deposits_, params_.index);
[codex:913858]
[codex:913858] // revert if no quote tokens in arbed bucket
[codex:913858] if (vars_.unscaledDeposit == 0) revert InsufficientLiquidity();
[codex:913858]
[codex:913858] vars_.bucketPrice = _priceAt(params_.index);
[codex:913858]
[codex:913858] // cannot arb with a price lower than the auction price
[codex:913858] if (vars_.auctionPrice > vars_.bucketPrice) revert AuctionPriceGtBucketPrice();
[codex:913858]
[codex:913858] thinking
[codex:913858] **Inspecting BucketTake collateral handling**
[codex:913858] exec
[codex:913858] /bin/bash -lc 'rg "_rewardBucketTake" -n contracts/src/libraries/external/TakerActions.sol' in /tmp/codex-multi-e_tm5cnr succeeded in 6ms:
[codex:913858] 456: _rewardBucketTake(
[codex:913858] 606: function _rewardBucketTake(
[codex:913858] exec
[codex:913858] /bin/bash -lc "sed -n '600,720p' contracts/src/libraries/external/TakerActions.sol" in /tmp/codex-multi-e_tm5cnr succeeded in 5ms:
[codex:913858] * @param buckets_ Struct for pool buckets state.
[codex:913858] * @param liquidation_ Struct containing details of auction to be taken from.
[codex:913858] * @param bucketIndex_ Index of a bucket, likely the `HPB`, in which collateral will be deposited.
[codex:913858] * @param depositTake_ If `true` then the take will happen at an auction price equal with bucket price. Auction price is used otherwise.
[codex:913858] * @param vars Struct containing bucket take action result details.
[codex:913858] */
[codex:913858] function _rewardBucketTake(
[codex:913858] AuctionsState storage auctions_,
[codex:913858] DepositsState storage deposits_,
[codex:913858] mapping(uint256 => Bucket) storage buckets_,
[codex:913858] Liquidation storage liquidation_,
[codex:913858] uint256 bucketIndex_,
[codex:913858] bool depositTake_,
[codex:913858] TakeLocalVars memory vars
[codex:913858] ) internal {
[codex:913858] Bucket storage bucket = buckets_[bucketIndex_];
[codex:913858]
[codex:913858] uint256 bankruptcyTime = bucket.bankruptcyTime;
[codex:913858] uint256 scaledDeposit = Maths.wmul(vars.unscaledDeposit, vars.bucketScale);
[codex:913858] uint256 totalLPReward;
[codex:913858]
[codex:913858] thinking
[codex:913858] **Analyzing collateral transfer and external calls**
[codex:913858] exec
[codex:913858] /bin/bash -lc 'rg "accrueInterest" -n contracts/src/libraries/external/PoolCommons.sol' in /tmp/codex-multi-e_tm5cnr succeeded in 6ms:
[codex:913858] 220: function accrueInterest(
[codex:913858] exec
[codex:913858] /bin/bash -lc "sed -n '200,320p' contracts/src/libraries/external/PoolCommons.sol" in /tmp/codex-multi-e_tm5cnr succeeded in 6ms:
[codex:913858] // save new interest rate params to storage
[codex:913858] interestParams_.debt = vars.newDebt;
[codex:913858] interestParams_.meaningfulDeposit = vars.newMeaningfulDeposit;
[codex:913858] interestParams_.debtCol = vars.newDebtCol;
[codex:913858] interestParams_.lupt0Debt = vars.newLupt0Debt;
[codex:913858] }
[codex:913858]
[codex:913858] /**
[codex:913858] * @notice Calculates new pool interest and scale the fenwick tree to update amount of debt owed to lenders (saved in storage).
[codex:913858] * @dev === Write state ===
[codex:913858] * @dev - `Deposits.mult` (scale `Fenwick` tree with new interest accrued):
[codex:913858] * @dev update `scaling` array state
[codex:913858] * @param emaParams_ Struct for pool `EMA`s state.
[codex:913858] * @param deposits_ Struct for pool deposits state.
[codex:913858] * @param poolState_ Current state of the pool.
[codex:913858] * @param thresholdPrice_ Current Pool Threshold Price.
[codex:913858] * @param elapsed_ Time elapsed since last inflator update.
[codex:913858] * @return newInflator_ The new value of pool inflator.
[codex:913858] * @return newInterest_ The new interest accrued.
[codex:913858] */
[codex:913858]
[codex:913858] thinking
[codex:913858] **Analyzing Clone and Multicall Interaction Risks**
[codex:913858] exec
[codex:913858] /bin/bash -lc "sed -n '1,160p' contracts/lib/openzeppelin-contracts/contracts/utils/Multicall.sol" in /tmp/codex-multi-e_tm5cnr succeeded in 6ms:
[codex:913858] // SPDX-License-Identifier: MIT
[codex:913858] // OpenZeppelin Contracts (last updated v4.5.0) (utils/Multicall.sol)
[codex:913858]
[codex:913858] pragma solidity ^0.8.0;
[codex:913858]
[codex:913858] import "./Address.sol";
[codex:913858]
[codex:913858] /**
[codex:913858] * @dev Provides a function to batch together multiple calls in a single external call.
[codex:913858] *
[codex:913858] * _Available since v4.1._
[codex:913858] */
[codex:913858] abstract contract Multicall {
[codex:913858] /**
[codex:913858] * @dev Receives and executes a batch of function calls on this contract.
[codex:913858] */
[codex:913858] function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) {
[codex:913858] results = new bytes[](data.length);
[codex:913858] for (uint256 i = 0; i < data.length; i++) {
[codex:913858] results[i] = Address.functionDelegateCall(address(this), data[i]);
[codex:913858]
[codex:913858]
[codex:913858] thinking
[codex:913858] **Analyzing multicall delegatecall vulnerability**
[codex:913858] thinking
[codex:913858] **Reviewing flashloan and reserve functions**
[codex:913858] exec
[codex:913858] /bin/bash -lc 'rg "takeReserves" -n contracts/src/base/Pool.sol contracts/src/ERC20Pool.sol' in /tmp/codex-multi-e_tm5cnr succeeded in 9ms:
[codex:913858] contracts/src/base/Pool.sol:442: function takeReserves(
[codex:913858] contracts/src/base/Pool.sol:446: (amount_, ajnaRequired) = TakerActions.takeReserves(
[codex:913858] exec
[codex:913858] /bin/bash -lc "sed -n '420,480p' contracts/src/base/Pool.sol" in /tmp/codex-multi-e_tm5cnr succeeded in 5ms:
[codex:913858] // start a new claimable reserve auction, passing in relevant parameters such as the current pool size, debt, balance, and inflator value
[codex:913858] uint256 kickerAward = KickerActions.kickReserveAuction(
[codex:913858] auctions,
[codex:913858] reserveAuction,
[codex:913858] KickReserveAuctionParams({
[codex:913858] poolSize: Deposits.treeSum(deposits),
[codex:913858] t0PoolDebt: poolBalances.t0Debt,
[codex:913858] poolBalance: _getNormalizedPoolQuoteTokenBalance(),
[codex:913858] inflator: inflatorState.inflator
[codex:913858] })
[codex:913858] );
[codex:913858]
[codex:913858] // transfer kicker award to msg.sender
[codex:913858] _transferQuoteToken(msg.sender, kickerAward);
[codex:913858] }
[codex:913858]
[codex:913858] /**
[codex:913858] * @inheritdoc IPoolTakerActions
[codex:913858] * @dev === Write state ===
[codex:913858] * @dev increment `reserveAuction.totalAjnaBurned` accumulator
[codex:913858]
[codex:913858] thinking
[codex:913858] **Analyzing multicall exploit and access control risks**
[codex:913858] exec
[codex:913858] /bin/bash -lc "nl -ba contracts/src/base/Pool.sol | sed -n '30,80p'" in /tmp/codex-multi-e_tm5cnr succeeded in 6ms:
[codex:913858] 30 InflatorState,
[codex:913858] 31 EmaState,
[codex:913858] 32 InterestState,
[codex:913858] 33 PoolBalancesState,
[codex:913858] 34 ReserveAuctionState,
[codex:913858] 35 Bucket,
[codex:913858] 36 Lender,
[codex:913858] 37 Borrower,
[codex:913858] 38 Kicker,
[codex:913858] 39 BurnEvent,
[codex:913858] 40 Liquidation
[codex:913858] 41 } from '../interfaces/pool/commons/IPoolState.sol';
[codex:913858] 42 import {
[codex:913858] 43 KickResult,
[codex:913858] 44 SettleResult,
[codex:913858] 45 TakeResult,
[codex:913858] 46 RemoveQuoteParams,
[codex:913858] 47 MoveQuoteParams,
[codex:913858] 48 AddQuoteParams,
[codex:913858] 49 KickReserveAuctionParams
[codex:913858]
[codex:913858] thinking
[codex:913858] **Identifying inheritance and usage examples**
[codex:913858] exec
[codex:913858] /bin/bash -lc "nl -ba contracts/src/base/Pool.sol | sed -n '700,740p'" in /tmp/codex-multi-e_tm5cnr succeeded in 6ms:
[codex:913858] 700 inflatorState.inflatorUpdate = uint48(block.timestamp);
[codex:913858] 701 // if the debt in the current pool state is 0, also update the inflator and inflatorUpdate fields in inflatorState
[codex:913858] 702 // slither-disable-next-line incorrect-equality
[codex:913858] 703 } else if (poolState_.debt == 0) {
[codex:913858] 704 inflatorState.inflator = uint208(Maths.WAD);
[codex:913858] 705 inflatorState.inflatorUpdate = uint48(block.timestamp);
[codex:913858] 706 }
[codex:913858] 707 }
[codex:913858] 708
[codex:913858] 709 /**
[codex:913858] 710 * @notice Helper function to transfer amount of quote tokens from sender to pool contract.
[codex:913858] 711 * @param from_ Sender address.
[codex:913858] 712 * @param amount_ Amount to transfer from sender (`WAD` precision). Scaled to quote token precision before transfer.
[codex:913858] 713 */
[codex:913858] 714 function _transferQuoteTokenFrom(address from_, uint256 amount_) internal {
[codex:913858] 715 // Transfer amount in favour of the pool
[codex:913858] 716 uint256 transferAmount = Maths.ceilDiv(amount_, _getArgUint256(QUOTE_SCALE));
[codex:913858] 717 IERC20(_getArgAddress(QUOTE_ADDRESS)).safeTransferFrom(from_, address(this), transferAmount);
[codex:913858] 718 }
[codex:913858] 719
[codex:913858]
[codex:913858] thinking
[codex:913858] **Noting key code references**
[codex:913858] exec
[codex:913858] /bin/bash -lc "nl -ba contracts/src/ERC20Pool.sol | sed -n '480,520p'" in /tmp/codex-multi-e_tm5cnr succeeded in 6ms:
[codex:913858] 480 /************************/
[codex:913858] 481 /*** Helper Functions ***/
[codex:913858] 482 /************************/
[codex:913858] 483
[codex:913858] 484 /**
[codex:913858] 485 * @notice Helper function to transfer amount of collateral tokens from sender to pool contract.
[codex:913858] 486 * @param from_ Sender address.
[codex:913858] 487 * @param amount_ Amount to transfer from sender (`WAD` precision). Scaled to collateral precision before transfer.
[codex:913858] 488 */
[codex:913858] 489 function _transferCollateralFrom(address from_, uint256 amount_) internal {
[codex:913858] 490 // Transfer amount in favour of the pool
[codex:913858] 491 uint256 transferAmount = Maths.ceilDiv(amount_, _getArgUint256(COLLATERAL_SCALE));
[codex:913858] 492 IERC20(_getArgAddress(COLLATERAL_ADDRESS)).safeTransferFrom(from_, address(this), transferAmount);
[codex:913858] 493 }
[codex:913858] 494
[codex:913858] 495 /**
[codex:913858] 496 * @notice Helper function to transfer amount of collateral tokens from pool contract.
[codex:913858] 497 * @param to_ Receiver address.
[codex:913858] 498 * @param amount_ Amount to transfer to receiver (`WAD` precision). Scaled to collateral precision before transfer.
[codex:913858] 499 */
[codex:913858]
[codex:913858] thinking
[codex:913858] **Checking ERC20Pool.initialize for access control**
[codex:913858] exec
[codex:913858] /bin/bash -lc "nl -ba contracts/src/ERC20Pool.sol | sed -n '70,120p'" in /tmp/codex-multi-e_tm5cnr succeeded in 6ms:
[codex:913858] 70 */
[codex:913858] 71 contract ERC20Pool is FlashloanablePool, IERC20Pool {
[codex:913858] 72 using SafeERC20 for IERC20;
[codex:913858] 73
[codex:913858] 74 /*****************/
[codex:913858] 75 /*** Constants ***/
[codex:913858] 76 /*****************/
[codex:913858] 77
[codex:913858] 78 /// @dev Immutable collateral scale arg offset.
[codex:913858] 79 uint256 internal constant COLLATERAL_SCALE = 93;
[codex:913858] 80
[codex:913858] 81 /****************************/
[codex:913858] 82 /*** Initialize Functions ***/
[codex:913858] 83 /****************************/
[codex:913858] 84
[codex:913858] 85 /// @inheritdoc IERC20Pool
[codex:913858] 86 function initialize(
[codex:913858] 87 uint256 rate_
[codex:913858] 88 ) external override {
[codex:913858] 89 if (isPoolInitialized) revert AlreadyInitialized();
[codex:913858]
[codex:913858] thinking
[codex:913858] **Summarizing audit findings**
[codex:913858] codex
[codex:913858] {"no_findings":false,"summary":"Identified a critical multicall/immutable‑args spoofing vector that can lead to asset loss, plus a low‑severity initialization access‑control risk.","findings":[{"severity":"critical","category":"other","title":"Multicall delegatecalls let callers spoof immutable args (token addresses/scales), enabling asset drainage","exploit_scenario":"An attacker calls `multicall` with crafted calldata that appends fake “immutable args” and sets the last two bytes so `_getImmutableArgsOffset()` points at attacker‑controlled bytes. This makes `_getArgAddress` / `_getArgUint256` return arbitrary token addresses and scales during the delegatecall. Example: use `multicall` to call `addQuoteToken` with QUOTE_ADDRESS set to a malicious ERC20 that returns true without transferring, minting LP without depositing real quote; then call `removeQuoteToken` (or `takeReserves`) with immutables set to the real quote/collateral/AJNA addresses to withdraw real assets or bypass burning. Because Multicall uses delegatecall, the pool reads these spoofed immutables from calldata.","recommendation":"Remove `Multicall` inheritance or override `multicall` to append the real immutable args to each delegatecall. Alternatively, store immutables in storage (set once at deployment/initialization) and read them from storage instead of calldata, or add a guard that reverts when immutable‑args offsets are not the expected appended length.","evidence":"`Pool` inherits `Multicall` while relying on `_getArgAddress/_getArgUint256` for critical token transfers. In a delegatecall context, immutable args are read from user‑controlled calldata rather than the proxy‑appended bytes, so addresses/scales become attacker‑chosen.","locations":["contracts/src/base/Pool.sol:78","contracts/src/base/Pool.sol:714","contracts/src/ERC20Pool.sol:489"]},{"severity":"low","category":"access_control","title":"`initialize` is permissionless and can be front‑run","exploit_scenario":"If a clone is deployed and `initialize` is not executed atomically by the factory, any address can call `initialize(rate_)` first, locking in an attacker‑chosen interest rate and preventing intended initialization.","recommendation":"Restrict `initialize` to a trusted factory/deployer address (immutable or stored), or ensure initialization happens in the same transaction as deployment. Consider using an initializer guard with access control.","evidence":"`initialize` only checks `isPoolInitialized` and lacks caller validation.","locations":["contracts/src/ERC20Pool.sol:86"]}]}
[codex:913858] tokens used
[codex:913858] 86,449
[codex:913858] {"no_findings":false,"summary":"Identified a critical multicall/immutable‑args spoofing vector that can lead to asset loss, plus a low‑severity initialization access‑control risk.","findings":[{"severity":"critical","category":"other","title":"Multicall delegatecalls let callers spoof immutable args (token addresses/scales), enabling asset drainage","exploit_scenario":"An attacker calls `multicall` with crafted calldata that appends fake “immutable args” and sets the last two bytes so `_getImmutableArgsOffset()` points at attacker‑controlled bytes. This makes `_getArgAddress` / `_getArgUint256` return arbitrary token addresses and scales during the delegatecall. Example: use `multicall` to call `addQuoteToken` with QUOTE_ADDRESS set to a malicious ERC20 that returns true without transferring, minting LP without depositing real quote; then call `removeQuoteToken` (or `takeReserves`) with immutables set to the real quote/collateral/AJNA addresses to withdraw real assets or bypass burning. Because Multicall uses delegatecall, the pool reads these spoofed immutables from calldata.","recommendation":"Remove `Multicall` inheritance or override `multicall` to append the real immutable args to each delegatecall. Alternatively, store immutables in storage (set once at deployment/initialization) and read them from storage instead of calldata, or add a guard that reverts when immutable‑args offsets are not the expected appended length.","evidence":"`Pool` inherits `Multicall` while relying on `_getArgAddress/_getArgUint256` for critical token transfers. In a delegatecall context, immutable args are read from user‑controlled calldata rather than the proxy‑appended bytes, so addresses/scales become attacker‑chosen.","locations":["contracts/src/base/Pool.sol:78","contracts/src/base/Pool.sol:714","contracts/src/ERC20Pool.sol:489"]},{"severity":"low","category":"access_control","title":"`initialize` is permissionless and can be front‑run","exploit_scenario":"If a clone is deployed and `initialize` is not executed atomically by the factory, any address can call `initialize(rate_)` first, locking in an attacker‑chosen interest rate and preventing intended initialization.","recommendation":"Restrict `initialize` to a trusted factory/deployer address (immutable or stored), or ensure initialization happens in the same transaction as deployment. Consider using an initializer guard with access control.","evidence":"`initialize` only checks `isPoolInitialized` and lacks caller validation.","locations":["contracts/src/ERC20Pool.sol:86"]}]}
{
"codex": {
"ran_at": "2025-12-30T23:11:29.816664+00:00",
"returncode": 0
},
"summary": "Identified a critical multicall/immutable\u2011args spoofing vector that can lead to asset loss, plus a low\u2011severity initialization access\u2011control risk.",
"raw_final": {
"summary": "Identified a critical multicall/immutable\u2011args spoofing vector that can lead to asset loss, plus a low\u2011severity initialization access\u2011control risk.",
"findings": [
{
"title": "Multicall delegatecalls let callers spoof immutable args (token addresses/scales), enabling asset drainage",
"category": "other",
"evidence": "`Pool` inherits `Multicall` while relying on `_getArgAddress/_getArgUint256` for critical token transfers. In a delegatecall context, immutable args are read from user\u2011controlled calldata rather than the proxy\u2011appended bytes, so addresses/scales become attacker\u2011chosen.",
"severity": "critical",
"locations": [
"contracts/src/base/Pool.sol:78",
"contracts/src/base/Pool.sol:714",
"contracts/src/ERC20Pool.sol:489"
],
"recommendation": "Remove `Multicall` inheritance or override `multicall` to append the real immutable args to each delegatecall. Alternatively, store immutables in storage (set once at deployment/initialization) and read them from storage instead of calldata, or add a guard that reverts when immutable\u2011args offsets are not the expected appended length.",
"exploit_scenario": "An attacker calls `multicall` with crafted calldata that appends fake \u201cimmutable args\u201d and sets the last two bytes so `_getImmutableArgsOffset()` points at attacker\u2011controlled bytes. This makes `_getArgAddress` / `_getArgUint256` return arbitrary token addresses and scales during the delegatecall. Example: use `multicall` to call `addQuoteToken` with QUOTE_ADDRESS set to a malicious ERC20 that returns true without transferring, minting LP without depositing real quote; then call `removeQuoteToken` (or `takeReserves`) with immutables set to the real quote/collateral/AJNA addresses to withdraw real assets or bypass burning. Because Multicall uses delegatecall, the pool reads these spoofed immutables from calldata."
},
{
"title": "`initialize` is permissionless and can be front\u2011run",
"category": "access_control",
"evidence": "`initialize` only checks `isPoolInitialized` and lacks caller validation.",
"severity": "low",
"locations": [
"contracts/src/ERC20Pool.sol:86"
],
"recommendation": "Restrict `initialize` to a trusted factory/deployer address (immutable or stored), or ensure initialization happens in the same transaction as deployment. Consider using an initializer guard with access control.",
"exploit_scenario": "If a clone is deployed and `initialize` is not executed atomically by the factory, any address can call `initialize(rate_)` first, locking in an attacker\u2011chosen interest rate and preventing intended initialization."
}
],
"no_findings": false
},
"no_findings": false,
"schema_version": 1
}
00000000: RETURNDATASIZE 00000001: RETURNDATASIZE 00000002: RETURNDATASIZE 00000003: RETURNDATASIZE 00000004: CALLDATASIZE 00000005: RETURNDATASIZE 00000006: RETURNDATASIZE 00000007: CALLDATACOPY 00000008: PUSH2 0x007f 0000000b: PUSH1 0x37 0000000d: CALLDATASIZE 0000000e: CODECOPY 0000000f: CALLDATASIZE 00000010: PUSH2 0x007f 00000013: ADD 00000014: RETURNDATASIZE 00000015: PUSH20 0x05bb4f6362b02f17c1a3f2b047a8b23368269a21 0000002a: GAS 0000002b: DELEGATECALL 0000002c: RETURNDATASIZE 0000002d: RETURNDATASIZE 0000002e: SWAP4 0000002f: DUP1 00000030: RETURNDATACOPY 00000031: PUSH1 0x35 00000033: JUMPI 00000034: REVERT 00000035: JUMPDEST 00000036: RETURN 00000037: STOP 00000038: SWAP11 00000039: SWAP7 0000003a: UNKNOWN(0xEC) 0000003b: SWAP12 0000003c: JUMPI 0000003d: UNKNOWN(0xFB) 0000003e: PUSH5 0xfbc60b423d 00000044: UNKNOWN(0x1F) 00000045: UNKNOWN(0x4D) 00000046: UNKNOWN(0xA7) 00000047: PUSH10 0x1bd35079be9895146f7a 00000052: DELEGATECALL 00000053: ADDRESS 00000054: BLOBHASH 00000055: UNKNOWN(0xCA) 00000056: SHR 00000057: BYTE 00000058: UNKNOWN(0xE3) 00000059: PC 0000005a: UNKNOWN(0xB0) 0000005b: SLOAD 0000005c: CLZ 0000005d: LOG4 0000005e: SWAP8 0000005f: DIV 00000060: UNKNOWN(0xC0) 00000061: UNKNOWN(0x2A) 00000062: UNKNOWN(0xAA) 00000063: CODECOPY 00000064: UNKNOWN(0xB2) 00000065: UNKNOWN(0x23) 00000066: INVALID 00000067: DUP14 00000068: EXP 00000069: UNKNOWN(0x0E) 0000006a: TLOAD 0000006b: UNKNOWN(0x4F) 0000006c: UNKNOWN(0x27) 0000006d: UNKNOWN(0xEA) 0000006e: UNKNOWN(0xD9) 0000006f: ADDMOD 00000070: EXTCODECOPY 00000071: PUSH22 0x6cc20000000000000000000000000000000000000000 00000088: STOP 00000089: STOP 0000008a: STOP 0000008b: STOP 0000008c: STOP 0000008d: STOP 0000008e: STOP 0000008f: STOP 00000090: STOP 00000091: STOP 00000092: STOP 00000093: ADD 00000094: STOP 00000095: STOP 00000096: STOP 00000097: STOP 00000098: STOP 00000099: STOP 0000009a: STOP 0000009b: STOP 0000009c: STOP 0000009d: STOP 0000009e: STOP 0000009f: STOP 000000a0: STOP 000000a1: STOP 000000a2: STOP 000000a3: STOP 000000a4: STOP 000000a5: STOP 000000a6: STOP 000000a7: STOP 000000a8: STOP 000000a9: STOP 000000aa: STOP 000000ab: STOP 000000ac: STOP 000000ad: STOP 000000ae: STOP 000000af: STOP 000000b0: STOP 000000b1: STOP 000000b2: STOP 000000b3: ADD 000000b4: STOP 000000b5: PUSH30
| Severity | Category | Tool | Title | Confidence | Validated | Confirmed | Details |
|---|---|---|---|---|---|---|---|
| critical | other | codex | Multicall delegatecalls let callers spoof immutable args (token addresses/scales), enabling asset drainage | 90 | no | — |
viewevidence_json{
"evidence": "`Pool` inherits `Multicall` while relying on `_getArgAddress/_getArgUint256` for critical token transfers. In a delegatecall context, immutable args are read from user\u2011controlled calldata rather than the proxy\u2011appended bytes, so addresses/scales become attacker\u2011chosen.",
"locations": [
"contracts/src/base/Pool.sol:78",
"contracts/src/base/Pool.sol:714",
"contracts/src/ERC20Pool.sol:489"
],
"recommendation": "Remove `Multicall` inheritance or override `multicall` to append the real immutable args to each delegatecall. Alternatively, store immutables in storage (set once at deployment/initialization) and read them from storage instead of calldata, or add a guard that reverts when immutable\u2011args offsets are not the expected appended length.",
"exploit_scenario": "An attacker calls `multicall` with crafted calldata that appends fake \u201cimmutable args\u201d and sets the last two bytes so `_getImmutableArgsOffset()` points at attacker\u2011controlled bytes. This makes `_getArgAddress` / `_getArgUint256` return arbitrary token addresses and scales during the delegatecall. Example: use `multicall` to call `addQuoteToken` with QUOTE_ADDRESS set to a malicious ERC20 that returns true without transferring, minting LP without depositing real quote; then call `removeQuoteToken` (or `takeReserves`) with immutables set to the real quote/collateral/AJNA addresses to withdraw real assets or bypass burning. Because Multicall uses delegatecall, the pool reads these spoofed immutables from calldata."
}
|
| critical | upgradeability | detector | Untrusted DELEGATECALL target reachable | 70 | no | no |
viewtrace_id: 0x9c3cea70fe50f18f10d6de32cc3be2f70593af8ff0450bc3596c1c634d0720aa
call_targets:
0x05bb4f6362b02f17c1a3f2b047a8b23368269a21
target_varies:
no
classification:
constant_target
validation_json{
"sink": "DELEGATECALL",
"errors": 0,
"status": "sink_reached",
"attempts": 1,
"trace_id": "0x9c3cea70fe50f18f10d6de32cc3be2f70593af8ff0450bc3596c1c634d0720aa",
"confirmed": false,
"trace_mode": "callTracer",
"call_targets": [
"0x05bb4f6362b02f17c1a3f2b047a8b23368269a21"
],
"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": 82,
"opcode": "DELEGATECALL",
"returndata_checked": null,
"value_expression_category": "n/a",
"target_expression_category": "computed"
}
],
"guards_detected": [],
"delegatecall_count": 2,
"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": 2
}
|
| low | access_control | codex | `initialize` is permissionless and can be front‑run | 65 | no | — |
viewevidence_json{
"evidence": "`initialize` only checks `isPoolInitialized` and lacks caller validation.",
"locations": [
"contracts/src/ERC20Pool.sol:86"
],
"recommendation": "Restrict `initialize` to a trusted factory/deployer address (immutable or stored), or ensure initialization happens in the same transaction as deployment. Consider using an initializer guard with access control.",
"exploit_scenario": "If a clone is deployed and `initialize` is not executed atomically by the factory, any address can call `initialize(rate_)` first, locking in an attacker\u2011chosen interest rate and preventing intended initialization."
}
|
| 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": 119,
"delegatecall_count": 2
}
|