019bab3b-ac5a-722f-b716-e6968b11ca70
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 |
|---|---|---|---|---|
| 38744 | artifact.fetch | complete | 1 / 3 | 3 months ago |
| 38745 | proxy.resolve | complete | 1 / 3 | 3 months ago |
| 38746 | tool.cast_disasm | complete | 1 / 3 | 3 months ago |
| 38747 | analysis.bundle | complete | 1 / 3 | 3 months ago |
| 38748 | capability.graph | complete | 1 / 3 | 3 months ago |
| 38749 | detector.run | complete | 1 / 3 | 3 months ago |
| 38750 | validation.fork | complete | 1 / 3 | 3 months ago |
| 39166 | report.consolidate | complete | 1 / 6 | 3 months ago |
| 39167 | tool.slither | failed | 2 / 2 | 3 months ago |
{
"method": "etherscan+onchain",
"status": "confirmed",
"etherscan_hint": {
"source": "etherscan",
"etherscan": {
"proxy_flag": true,
"implementation": "0x49643fc85fb1f25b6775ebbbdc69295d45105abc"
}
},
"implementation": "0x49643fc85fb1f25b6775ebbbdc69295d45105abc",
"onchain_method": "eip1967"
}
Traceback (most recent call last):
File "/var/www/tripwire/worker/worker.py", line 198, in main
payload = dispatch_job(job_type, job_id, job_meta, run_data, config, laravel_client)
File "/var/www/tripwire/worker/worker.py", line 114, in dispatch_job
return slither_run.run(job_id, job_meta, run_data, config, log, laravel_client)
~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/var/www/tripwire/worker/jobs/slither_run.py", line 1084, in run
stdout_text, stderr_text, result_json = _run_slither(
~~~~~~~~~~~~^
workspace,
^^^^^^^^^^
...<4 lines>...
target_path=target_path,
^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/var/www/tripwire/worker/jobs/slither_run.py", line 958, in _run_slither
raise RuntimeError(f"slither failed (exit {result.returncode}): {stderr_text or stdout_text}")
RuntimeError: slither failed (exit 1): 'forge clean' running (wd: /tmp/slither-z9z2vbz6)
'forge config --json' running
'forge build --build-info --skip ./test/** ./script/** --force' running (wd: /tmp/slither-z9z2vbz6)
Traceback (most recent call last):
File "/var/www/tripwire/.venv/bin/slither", line 10, in <module>
sys.exit(main())
~~~~^^
File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 776, in main
main_impl(all_detector_classes=detectors, all_printer_classes=printers)
~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 882, in main_impl
) = process_all(filename, args, detector_classes, printer_classes)
~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 96, in process_all
compilations = compile_all(target, **vars(args))
File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 722, in compile_all
compilations.append(CryticCompile(target, **kwargs))
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 211, in __init__
self._compile(**kwargs)
~~~~~~~~~~~~~^^^^^^^^^^
File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 633, in _compile
self._platform.compile(self, **kwargs)
~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/platform/foundry.py", line 102, in compile
hardhat_like_parsing(
~~~~~~~~~~~~~~~~~~~~^
crytic_compile, str(self._target), build_directory, str(self._project_root)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/platform/hardhat.py", line 52, in hardhat_like_parsing
raise InvalidCompilation(txt)
crytic_compile.platform.exceptions.InvalidCompilation: Compilation failed. Can you run build command?
/tmp/slither-z9z2vbz6/out/build-info is not a directory.
{"no_findings":false,"summary":"Identified three issues: retired committees retain authority to process requests; transfer limit logic can ignore USD minima; and oracle anomalies can bypass limits due to unchecked prices combined with fail-open handling.","findings":[{"severity":"high","category":"access_control","title":"Retired committee members remain authorized to process requests","exploit_scenario":"After a committee rotation, members of the old committee can still call `receiveRequest` with their historical `_committeeId` and reach the previous `signatureThreshold`. They can mint or release arbitrary amounts by submitting any parameters and signing among themselves, even if governance intended to revoke them.","recommendation":"Enforce that `receiveRequest` only accepts the current `committeeId`, or add an explicit `activeCommitteeId`/revocation mechanism so old committees are disabled once a new committee is set. If you need overlap for in-flight requests, add a bounded grace period.","evidence":"`setCommittee` only increments `committeeId` and adds new members; it never disables old committees. `receiveRequest` accepts any `_committeeId` provided by the caller and only checks membership for that id.","locations":["contracts/AbstractMost.sol:295","contracts/AbstractMost.sol:367"]},{"severity":"medium","category":"other","title":"USD transfer minimum is ignored when default minimum is smaller (often zero)","exploit_scenario":"If `defaultMinima[_token]` is left at the default `0` (or set lower than the USD-based minimum), `minimumTransferAmount` returns the smaller of the two values. This makes the USD oracle limit ineffective, allowing any amount to pass `isRequestAllowed`.","recommendation":"When a USD oracle is configured, use the USD-derived minimum as the floor (e.g., `Math.max(defaultMinima, usdBasedMinimum)`), or if `defaultMinima` is zero, treat it as unset and use the USD value directly.","evidence":"`minimumTransferAmount` computes `usdBasedMinimum` and then uses `Math.min(minimum, usdBasedMinimum)`, which selects the smaller threshold.","locations":["contracts/TransferLimit.sol:62"]},{"severity":"medium","category":"oracle","title":"Oracle anomalies can bypass transfer limits due to unchecked price and fail-open handling","exploit_scenario":"If the oracle returns zero, negative, or stale data, `minimumTransferAmount` can revert (division by zero) or underflow to an extremely large uint cast. `Most.checkTransferAllowed` catches all errors and proceeds as if no limit exists, so an attacker who can manipulate oracle responses can bypass transfer limits entirely.","recommendation":"Validate `usdPrice > 0`, ensure `answeredInRound >= roundId`, and check `updatedAt` freshness before using prices. Consider failing closed (revert) in `Most` when the transfer limit contract errors instead of ignoring failures.","evidence":"`minimumTransferAmount` casts `usdPrice` to `uint256` with no checks and divides by it; `Most.checkTransferAllowed` catches and ignores failures.","locations":["contracts/TransferLimit.sol:68","contracts/Most.sol:41"]}]}
OpenAI Codex v0.77.0 (research preview)
--------
workdir: /tmp/codex-1ve8blzm
model: gpt-5.2-codex
provider: openai
approval: never
sandbox: read-only
reasoning effort: high
reasoning summaries: auto
session id: 019babde-a604-7513-b216-56422c002384
--------
user
Context: The user requested the proxy at 0xf2139f5c8afb8a4d64084efc5532830774742830.
Audit the implementation at 0x49643fc85fb1f25b6775ebbbdc69295d45105abc.
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
**Reading AUDIT_BUNDLE file**
exec
/bin/bash -lc ls in /tmp/codex-1ve8blzm succeeded in 8ms:
AUDIT_BUNDLE.txt
@chainlink
codex-output-schema.json
contracts
@openzeppelin
@openzeppelin-4.5.0
exec
/bin/bash -lc 'cat AUDIT_BUNDLE.txt' in /tmp/codex-1ve8blzm succeeded in 6ms:
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: @chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol
================================================================================
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// solhint-disable-next-line interface-starts-with-i
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
function getRoundData(
uint80 _roundId
) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
thinking
**Searching for relevant contracts**
exec
/bin/bash -lc 'rg "''^FILE:" -n AUDIT_BUNDLE.txt' in /tmp/codex-1ve8blzm succeeded in 16ms:
5:FILE: @chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol
29:FILE: @openzeppelin-4.5.0/contracts/access/Ownable.sol
109:FILE: @openzeppelin-4.5.0/contracts/security/ReentrancyGuard.sol
176:FILE: @openzeppelin-4.5.0/contracts/token/ERC20/ERC20.sol
563:FILE: @openzeppelin-4.5.0/contracts/token/ERC20/IERC20.sol
649:FILE: @openzeppelin-4.5.0/contracts/token/ERC20/extensions/IERC20Metadata.sol
681:FILE: @openzeppelin-4.5.0/contracts/token/ERC20/utils/SafeERC20.sol
784:FILE: @openzeppelin-4.5.0/contracts/utils/Address.sol
1010:FILE: @openzeppelin-4.5.0/contracts/utils/Context.sol
1038:FILE: contracts/AbstractMost.sol
1507:FILE: contracts/ITransferLimit.sol
1523:FILE: contracts/IWETH9.sol
1538:FILE: contracts/IWrappedToken.sol
1556:FILE: contracts/MockOracle.sol
1586:FILE: contracts/Most.sol
1643:FILE: contracts/MostL2.sol
1983:FILE: contracts/StableSwap/IStableSwapLP.sol
2002:FILE: contracts/StableSwap/StableSwapLP.sol
2038:FILE: contracts/StableSwap/StableSwapTwoPool.sol
2998:FILE: contracts/Token.sol
thinking
**Reviewing main contract files**
exec
/bin/bash -lc 'rg --files contracts' in /tmp/codex-1ve8blzm succeeded in 37ms:
contracts/IWETH9.sol
contracts/WrappedToken.sol
contracts/MostL2.sol
contracts/Token.sol
contracts/MockOracle.sol
contracts/IWrappedToken.sol
contracts/TransferLimit.sol
contracts/AbstractMost.sol
contracts/ITransferLimit.sol
contracts/Most.sol
contracts/StableSwap/IStableSwapLP.sol
contracts/StableSwap/StableSwapTwoPool.sol
contracts/StableSwap/StableSwapLP.sol
thinking
**Inspecting AbstractMost class**
exec
/bin/bash -lc "sed -n '1,240p' contracts/AbstractMost.sol" in /tmp/codex-1ve8blzm succeeded in 5ms:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
import {IWETH9} from "./IWETH9.sol";
import {IWrappedToken} from "./IWrappedToken.sol";
/// @title Most
/// @author Cardinal Cryptography
abstract contract AbstractMost is
Initializable,
UUPSUpgradeable,
Ownable2StepUpgradeable,
PausableUpgradeable
exec
/bin/bash -lc "sed -n '240,520p' contracts/AbstractMost.sol" in /tmp/codex-1ve8blzm succeeded in 26ms:
amount,
destReceiverAddress,
requestNonce
);
++requestNonce;
}
function onReceiveRequestThresholdMet(
bytes32 requestHash,
bytes32 destTokenAddress,
uint256 amount,
bytes32 destReceiverAddress
) internal virtual {
processedRequests[requestHash] = true;
delete pendingRequests[requestHash];
address _destTokenAddress = bytes32ToAddress(destTokenAddress);
address _destReceiverAddress = bytes32ToAddress(destReceiverAddress);
thinking
**Identifying required components**
exec
/bin/bash -lc "sed -n '1,220p' contracts/Most.sol" in /tmp/codex-1ve8blzm succeeded in 7ms:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {AbstractMost} from "./AbstractMost.sol";
import {ITransferLimit} from "./ITransferLimit.sol";
/// @title Most
/// @author Cardinal Cryptography
contract Most is AbstractMost {
ITransferLimit public transferLimit;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
function setTransferLimitContract(
ITransferLimit _transferLimit
) external onlyOwner {
exec
/bin/bash -lc "sed -n '1,220p' contracts/MostL2.sol" in /tmp/codex-1ve8blzm succeeded in 19ms:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {AbstractMost} from "./AbstractMost.sol";
import {StableSwapTwoPool} from "./StableSwap/StableSwapTwoPool.sol";
import {IWrappedToken} from "./IWrappedToken.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
/// @title MostL2.sol
/// @author Cardinal Cryptography
contract MostL2 is AbstractMost {
using SafeERC20 for IERC20;
/// Ratio between bridged azero (12 decimals) and native token on L2 (18 decimals)
uint256 public constant BAZERO_TO_NATIVE_RATIO = 1e6;
address payable public stableSwapAddress;
address public bAzeroAddress;
exec
/bin/bash -lc "sed -n '220,520p' contracts/MostL2.sol" in /tmp/codex-1ve8blzm succeeded in 13ms:
}
/// @notice Invoke this tx to transfer funds to the destination chain.
/// Account needs to send native Azero which are swapped for bazero
/// tokens. Since the Bazero have 12 decimals and Azero have 18,
/// user need to send at leas 10e6 tokens with this call.
///
/// @dev Tx emits a CrosschainTransferRequest event that the relayers listen to
/// & forward to the destination chain.
function sendRequestNative(
bytes32 destReceiverAddress,
uint256 amount_to_bridge
) external payable whenNotPaused {
require(
amount_to_bridge >= BAZERO_TO_NATIVE_RATIO,
"Value must be at least 10e6"
);
if (destReceiverAddress == bytes32(0)) {
revert ZeroAddress();
}
thinking
**Identifying required interfaces**
exec
/bin/bash -lc "sed -n '1,160p' contracts/ITransferLimit.sol" in /tmp/codex-1ve8blzm succeeded in 28ms:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface ITransferLimit {
function isRequestAllowed(
address _token,
uint256 _amount
) external view returns (bool);
}
exec
/bin/bash -lc "sed -n '1,160p' contracts/IWrappedToken.sol" in /tmp/codex-1ve8blzm succeeded in 5ms:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
/// @title Interface for WrappedToken
interface IWrappedToken {
/// @notice Mint a given amount of remote PSP22 token representation to a given address
function mint(address, uint256) external;
/// @notice Burn a given amount of remote PSP22 token representation
function burn(uint256) external;
/// @notice Approve spending a given amount of token to the spender
function approve(address spender, uint256 value) external returns (bool);
}
exec
/bin/bash -lc "sed -n '1,200p' contracts/MockOracle.sol" in /tmp/codex-1ve8blzm succeeded in 6ms:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/// @title MockOracle
/// @author Cardinal Cryptography
/// @notice Mock Chainlink oracle for testing purposes
contract MockOracle {
int public price;
function setPrice(int _price) public {
price = _price;
}
function latestRoundData()
public
view
returns (uint80, int, uint, uint, uint80)
{
return (0, price, 0, 0, 0);
thinking
**Inspecting StableSwapTwoPool components**
exec
/bin/bash -lc "sed -n '1,260p' contracts/StableSwap/StableSwapTwoPool.sol" in /tmp/codex-1ve8blzm succeeded in 27ms:
// MIT License
//
// Copyright (c) 2024 PancakeSwap
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
exec
/bin/bash -lc "sed -n '260,620p' contracts/StableSwap/StableSwapTwoPool.sol" in /tmp/codex-1ve8blzm succeeded in 8ms:
}
} else {
if (Dprev - D <= 1) {
break;
}
}
}
return D;
}
function get_D_mem(
uint256[N_COINS] memory _balances,
uint256 amp
) internal view returns (uint256) {
return get_D(_xp_mem(_balances), amp);
}
function get_virtual_price() external view returns (uint256) {
/**
Returns portfolio virtual price (for calculating profit)
exec
/bin/bash -lc "sed -n '620,980p' contracts/StableSwap/StableSwapTwoPool.sol" in /tmp/codex-1ve8blzm succeeded in 27ms:
uint256[N_COINS] memory fees;
for (uint256 i = 0; i < N_COINS; i++) {
uint256 ideal_balance = (D1 * old_balances[i]) / D0;
uint256 difference;
if (ideal_balance > new_balances[i]) {
difference = ideal_balance - new_balances[i];
} else {
difference = new_balances[i] - ideal_balance;
}
fees[i] = (_fee * difference) / FEE_DENOMINATOR;
balances[i] =
new_balances[i] -
((fees[i] * _admin_fee) / FEE_DENOMINATOR);
new_balances[i] -= fees[i];
}
uint256 D2 = get_D_mem(new_balances, amp);
uint256 token_amount = ((D0 - D2) * token_supply) / D0;
require(token_amount > 0, "token_amount must be greater than 0");
token_amount += 1; // In case of rounding errors - make it unfavorable for the "attacker"
thinking
**Considering StableSwap LP concept**
exec
/bin/bash -lc "sed -n '1,220p' contracts/StableSwap/StableSwapLP.sol" in /tmp/codex-1ve8blzm succeeded in 5ms:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import "@openzeppelin-4.5.0/contracts/token/ERC20/ERC20.sol";
contract StableSwapLP is ERC20 {
address public minter;
constructor() ERC20("StableSwap LPs", "Stable-LP") {
minter = msg.sender;
}
/**
* @notice Checks if the msg.sender is the minter address.
*/
modifier onlyMinter() {
require(msg.sender == minter, "Not minter");
_;
}
exec
/bin/bash -lc "sed -n '1,200p' contracts/StableSwap/IStableSwapLP.sol" in /tmp/codex-1ve8blzm succeeded in 12ms:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
interface IStableSwapLP {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
function mint(address _to, uint256 _amount) external;
function burnFrom(address _to, uint256 _amount) external;
function setMinter(address _newMinter) external;
}
thinking
**Identifying dependency IWETH9**
exec
/bin/bash -lc "sed -n '1,120p' contracts/IWETH9.sol" in /tmp/codex-1ve8blzm succeeded in 8ms:
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.2;
/// @title Interface for WETH9
interface IWETH9 {
/// @notice Deposit ether to get wrapped ether
function deposit() external payable;
/// @notice Withdraw wrapped ether to get ether
function withdraw(uint256) external;
}
thinking
**Analyzing smart contract trust and reentrancy risks**
I'm auditing a UUPS upgradeable bridge contract with guardian committees controlling minting. There’s no cryptographic signature validation—just guardian approvals via msg.sender—and older committees remain authorized after rotation, posing a potential access control vulnerability. Reentrancy issues may arise during token transfers especially with malicious ERC777 tokens and native fee handling, though some state updates precede external calls reducing risk. Oracle price feeds lack stale or negative checks, enabling bypass or DoS of transfer limits. Also, minimum transfer amount logic uses `Math.min` incorrectly, effectively disabling limits under some configs. Low-level stable swap call handling risks reverts if unexpected data returned, potentially halting bridging processing. Overall, governance trust assumptions and careful contract call patterns require scrutiny.
thinking
**Evaluating upgradeability, reentrancy, and token swap intricacies**
I'm scrutinizing how receiveRequest handles swap failures and state rollback, confirming internal call reverts revert all changes properly. I notice reentrancy risks in MostL2’s fee handling where raw calls to owner and msg.sender could reenter sendTokenRequest before nonce increments, but increment timing avoids duplicate nonces. Initialization misses __UUPSUpgradeable_init calls but likely not critical. The bridge doesn’t revoke old committees on rotation, so previous members can still act under old IDs, posing governance risks. Token swaps rely on external stablePool calls that must return data correctly or revert. Some token approval patterns don’t reset allowances, though it’s internal use. Handling of native token balances in swap pools may allow accidental ETH trapping. StableSwap contract functions carefully update balances with reentrancy guards. Overall, a mix of upgradeable contract considerations, cross-contract call safety, and state correctness in bridging flows.
thinking
**Analyzing token transfer handling and limit logic**
I'm checking how token transfers use safeTransferFrom after balance updates with reentrancy guards, and how native token transfers rely on adjustable gas limits. I notice the minimumTransferAmount calculation misuses Math.min, likely disabling the USD limit which could let transfers bypass limits. Oracle price validations are lax: zero or negative prices cause unexpected behaviors. Token burn logic assumes wrapped tokens behave as expected, and committee rotations don’t revoke old committees, so prior members retain permissions. There are subtle concerns about low-level calls and approvals without full return checks. Overall, it’s a mix of transfer mechanics, oracle dependency logic, and committee key management that I’m piecing together.
exec
/bin/bash -lc "nl -ba contracts/TransferLimit.sol | sed -n '35,140p'" in /tmp/codex-1ve8blzm succeeded in 8ms:
35
36 /// @notice Set the default minimum transfer amount for a token - this is used if no USD oracle is set
37 /// @param _token The token address
38 /// @param _limit The minimum transfer amount
39 function setDefaultLimit(address _token, uint256 _limit) public onlyOwner {
40 defaultMinima[_token] = _limit;
41 }
42
43 /// @notice Set the USD oracle params for a token
44 /// @param _token The token address
45 /// @param _tokenDecimals The number of decimals the token has
46 /// @param _oracle The Chainlink oracle address
47 /// @param _limit The minimum transfer amount in USD with no decimals
48 function setUSDOracle(
49 address _token,
50 uint256 _tokenDecimals,
51 AggregatorV3Interface _oracle,
52 uint256 _limit
53 ) public onlyOwner {
54 usdMinima[_token] = USDMinimum({
thinking
**Adding line numbers for debugging**
exec
/bin/bash -lc "nl -ba contracts/AbstractMost.sol | sed -n '40,220p'" in /tmp/codex-1ve8blzm succeeded in 28ms:
40
41 address public wrappedAzeroAddress;
42
43 struct Request {
44 uint256 signatureCount;
45 mapping(address => bool) signatures;
46 }
47
48 event CrosschainTransferRequest(
49 uint256 indexed committeeId,
50 bytes32 indexed destTokenAddress,
51 uint256 amount,
52 bytes32 indexed destReceiverAddress,
53 uint256 requestNonce
54 );
55
56 event RequestSigned(bytes32 requestHash, address signer);
57
58 event RequestProcessed(bytes32 requestHash);
59
thinking
**Noting missing lines for functions**
exec
/bin/bash -lc "nl -ba contracts/AbstractMost.sol | sed -n '220,420p'" in /tmp/codex-1ve8blzm succeeded in 28ms:
220 }
221
222 function sendRequestAzeroToNative(
223 uint256 amount,
224 bytes32 destReceiverAddress
225 ) external virtual whenNotPaused {
226 if (amount == 0) revert ZeroAmount();
227 if (destReceiverAddress == bytes32(0)) revert ZeroAddress();
228 if (wrappedAzeroAddress == address(0)) revert AzeroAddressNotSet();
229
230 checkTransferAllowed(wrappedAzeroAddress, amount);
231
232 IERC20 azeroToken = IERC20(wrappedAzeroAddress);
233 azeroToken.safeTransferFrom(msg.sender, address(this), amount);
234 IWrappedToken burnableToken = IWrappedToken(wrappedAzeroAddress);
235 burnableToken.burn(amount);
236
237 emit CrosschainTransferRequest(
238 committeeId,
239 0x0,
thinking
**Reviewing lines for try-catch blocks**
exec
/bin/bash -lc "nl -ba contracts/Most.sol | sed -n '1,140p'" in /tmp/codex-1ve8blzm succeeded in 25ms:
1 // SPDX-License-Identifier: MIT
2
3 pragma solidity ^0.8.20;
4
5 import {AbstractMost} from "./AbstractMost.sol";
6 import {ITransferLimit} from "./ITransferLimit.sol";
7
8 /// @title Most
9 /// @author Cardinal Cryptography
10 contract Most is AbstractMost {
11 ITransferLimit public transferLimit;
12
13 /// @custom:oz-upgrades-unsafe-allow constructor
14 constructor() {
15 _disableInitializers();
16 }
17
18 function setTransferLimitContract(
19 ITransferLimit _transferLimit
20 ) external onlyOwner {
thinking
**Composing detailed security findings and JSON report**
I’m putting together a clear JSON report with three findings: a high-severity access control issue where old committees remain authorized, a medium-severity logic bug allowing USD transfer minima bypass via Math.min, and a medium-severity oracle validation flaw combined with Most’s fail-open behavior enabling transfer limit bypass; I’m including concise summaries, scenarios, recommendations, evidences, and exact code locations.
codex
{"no_findings":false,"summary":"Identified three issues: retired committees retain authority to process requests; transfer limit logic can ignore USD minima; and oracle anomalies can bypass limits due to unchecked prices combined with fail-open handling.","findings":[{"severity":"high","category":"access_control","title":"Retired committee members remain authorized to process requests","exploit_scenario":"After a committee rotation, members of the old committee can still call `receiveRequest` with their historical `_committeeId` and reach the previous `signatureThreshold`. They can mint or release arbitrary amounts by submitting any parameters and signing among themselves, even if governance intended to revoke them.","recommendation":"Enforce that `receiveRequest` only accepts the current `committeeId`, or add an explicit `activeCommitteeId`/revocation mechanism so old committees are disabled once a new committee is set. If you need overlap for in-flight requests, add a bounded grace period.","evidence":"`setCommittee` only increments `committeeId` and adds new members; it never disables old committees. `receiveRequest` accepts any `_committeeId` provided by the caller and only checks membership for that id.","locations":["contracts/AbstractMost.sol:295","contracts/AbstractMost.sol:367"]},{"severity":"medium","category":"other","title":"USD transfer minimum is ignored when default minimum is smaller (often zero)","exploit_scenario":"If `defaultMinima[_token]` is left at the default `0` (or set lower than the USD-based minimum), `minimumTransferAmount` returns the smaller of the two values. This makes the USD oracle limit ineffective, allowing any amount to pass `isRequestAllowed`.","recommendation":"When a USD oracle is configured, use the USD-derived minimum as the floor (e.g., `Math.max(defaultMinima, usdBasedMinimum)`), or if `defaultMinima` is zero, treat it as unset and use the USD value directly.","evidence":"`minimumTransferAmount` computes `usdBasedMinimum` and then uses `Math.min(minimum, usdBasedMinimum)`, which selects the smaller threshold.","locations":["contracts/TransferLimit.sol:62"]},{"severity":"medium","category":"oracle","title":"Oracle anomalies can bypass transfer limits due to unchecked price and fail-open handling","exploit_scenario":"If the oracle returns zero, negative, or stale data, `minimumTransferAmount` can revert (division by zero) or underflow to an extremely large uint cast. `Most.checkTransferAllowed` catches all errors and proceeds as if no limit exists, so an attacker who can manipulate oracle responses can bypass transfer limits entirely.","recommendation":"Validate `usdPrice > 0`, ensure `answeredInRound >= roundId`, and check `updatedAt` freshness before using prices. Consider failing closed (revert) in `Most` when the transfer limit contract errors instead of ignoring failures.","evidence":"`minimumTransferAmount` casts `usdPrice` to `uint256` with no checks and divides by it; `Most.checkTransferAllowed` catches and ignores failures.","locations":["contracts/TransferLimit.sol:68","contracts/Most.sol:41"]}]}
tokens used
54,595
{
"codex": {
"ran_at": "2026-01-11T07:09:21.909120+00:00",
"returncode": 0
},
"project": {
"mode": "source",
"file_count": 43,
"import_count": 40,
"first_party_contracts_count": 22
},
"summary": "Identified three issues: retired committees retain authority to process requests; transfer limit logic can ignore USD minima; and oracle anomalies can bypass limits due to unchecked prices combined with fail-open handling.",
"raw_final": {
"summary": "Identified three issues: retired committees retain authority to process requests; transfer limit logic can ignore USD minima; and oracle anomalies can bypass limits due to unchecked prices combined with fail-open handling.",
"findings": [
{
"title": "Retired committee members remain authorized to process requests",
"category": "access_control",
"evidence": "`setCommittee` only increments `committeeId` and adds new members; it never disables old committees. `receiveRequest` accepts any `_committeeId` provided by the caller and only checks membership for that id.",
"severity": "high",
"locations": [
"contracts/AbstractMost.sol:295",
"contracts/AbstractMost.sol:367"
],
"recommendation": "Enforce that `receiveRequest` only accepts the current `committeeId`, or add an explicit `activeCommitteeId`/revocation mechanism so old committees are disabled once a new committee is set. If you need overlap for in-flight requests, add a bounded grace period.",
"exploit_scenario": "After a committee rotation, members of the old committee can still call `receiveRequest` with their historical `_committeeId` and reach the previous `signatureThreshold`. They can mint or release arbitrary amounts by submitting any parameters and signing among themselves, even if governance intended to revoke them."
},
{
"title": "USD transfer minimum is ignored when default minimum is smaller (often zero)",
"category": "other",
"evidence": "`minimumTransferAmount` computes `usdBasedMinimum` and then uses `Math.min(minimum, usdBasedMinimum)`, which selects the smaller threshold.",
"severity": "medium",
"locations": [
"contracts/TransferLimit.sol:62"
],
"recommendation": "When a USD oracle is configured, use the USD-derived minimum as the floor (e.g., `Math.max(defaultMinima, usdBasedMinimum)`), or if `defaultMinima` is zero, treat it as unset and use the USD value directly.",
"exploit_scenario": "If `defaultMinima[_token]` is left at the default `0` (or set lower than the USD-based minimum), `minimumTransferAmount` returns the smaller of the two values. This makes the USD oracle limit ineffective, allowing any amount to pass `isRequestAllowed`."
},
{
"title": "Oracle anomalies can bypass transfer limits due to unchecked price and fail-open handling",
"category": "oracle",
"evidence": "`minimumTransferAmount` casts `usdPrice` to `uint256` with no checks and divides by it; `Most.checkTransferAllowed` catches and ignores failures.",
"severity": "medium",
"locations": [
"contracts/TransferLimit.sol:68",
"contracts/Most.sol:41"
],
"recommendation": "Validate `usdPrice > 0`, ensure `answeredInRound >= roundId`, and check `updatedAt` freshness before using prices. Consider failing closed (revert) in `Most` when the transfer limit contract errors instead of ignoring failures.",
"exploit_scenario": "If the oracle returns zero, negative, or stale data, `minimumTransferAmount` can revert (division by zero) or underflow to an extremely large uint cast. `Most.checkTransferAllowed` catches all errors and proceeds as if no limit exists, so an attacker who can manipulate oracle responses can bypass transfer limits entirely."
}
],
"no_findings": false
},
"no_findings": false,
"proxy_context": {
"proxy_run_id": "019bab3b-ac5a-722f-b716-e6968b11ca70",
"proxy_address": "0xf2139f5c8afb8a4d64084efc5532830774742830",
"implementation_run_id": "019bab3d-f2be-72f8-a06f-15a39da6a9c7",
"implementation_address": "0x49643fc85fb1f25b6775ebbbdc69295d45105abc"
},
"schema_version": 1
}
00000000: PUSH1 0x80 00000002: PUSH1 0x40 00000004: MSTORE 00000005: CALLDATASIZE 00000006: PUSH2 0x0013 00000009: JUMPI 0000000a: PUSH2 0x0011 0000000d: PUSH2 0x0017 00000010: JUMP 00000011: JUMPDEST 00000012: STOP 00000013: JUMPDEST 00000014: PUSH2 0x0011 00000017: JUMPDEST 00000018: PUSH2 0x0027 0000001b: PUSH2 0x0022 0000001e: PUSH2 0x0067 00000021: JUMP 00000022: JUMPDEST 00000023: PUSH2 0x009f 00000026: JUMP 00000027: JUMPDEST 00000028: JUMP 00000029: JUMPDEST 0000002a: PUSH1 0x60 0000002c: PUSH2 0x004e 0000002f: DUP4 00000030: DUP4 00000031: PUSH1 0x40 00000033: MLOAD 00000034: DUP1 00000035: PUSH1 0x60 00000037: ADD 00000038: PUSH1 0x40 0000003a: MSTORE 0000003b: DUP1 0000003c: PUSH1 0x27 0000003e: DUP2 0000003f: MSTORE 00000040: PUSH1 0x20 00000042: ADD 00000043: PUSH2 0x0268 00000046: PUSH1 0x27 00000048: SWAP2 00000049: CODECOPY 0000004a: PUSH2 0x00c3 0000004d: JUMP 0000004e: JUMPDEST 0000004f: SWAP4 00000050: SWAP3 00000051: POP 00000052: POP 00000053: POP 00000054: JUMP 00000055: JUMPDEST 00000056: PUSH1 0x01 00000058: PUSH1 0x01 0000005a: PUSH1 0xa0 0000005c: SHL 0000005d: SUB 0000005e: AND 0000005f: EXTCODESIZE 00000060: ISZERO 00000061: ISZERO 00000062: SWAP1 00000063: JUMP 00000064: JUMPDEST 00000065: SWAP1 00000066: JUMP 00000067: JUMPDEST 00000068: PUSH1 0x00 0000006a: PUSH2 0x009a 0000006d: PUSH32 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc 0000008e: SLOAD 0000008f: PUSH1 0x01 00000091: PUSH1 0x01 00000093: PUSH1 0xa0 00000095: SHL 00000096: SUB 00000097: AND 00000098: SWAP1 00000099: JUMP 0000009a: JUMPDEST 0000009b: SWAP1 0000009c: POP 0000009d: SWAP1 0000009e: JUMP 0000009f: JUMPDEST 000000a0: CALLDATASIZE 000000a1: PUSH1 0x00 000000a3: DUP1 000000a4: CALLDATACOPY 000000a5: PUSH1 0x00 000000a7: DUP1 000000a8: CALLDATASIZE 000000a9: PUSH1 0x00 000000ab: DUP5 000000ac: GAS 000000ad: DELEGATECALL 000000ae: RETURNDATASIZE 000000af: PUSH1 0x00 000000b1: DUP1 000000b2: RETURNDATACOPY 000000b3: DUP1 000000b4: DUP1 000000b5: ISZERO 000000b6: PUSH2 0x00be 000000b9: JUMPI 000000ba: RETURNDATASIZE 000000bb: PUSH1 0x00 000000bd: RETURN 000000be: JUMPDEST 000000bf: RETURNDATASIZE 000000c0: PUSH1 0x00 000000c2: REVERT 000000c3: JUMPDEST 000000c4: PUSH1 0x60 000000c6: PUSH1 0x00 000000c8: DUP1 000000c9: DUP6 000000ca: PUSH1 0x01 000000cc: PUSH1 0x01 000000ce: PUSH1 0xa0 000000d0: SHL 000000d1: SUB 000000d2: AND 000000d3: DUP6 000000d4: PUSH1 0x40 000000d6: MLOAD 000000d7: PUSH2 0x00e0 000000da: SWAP2 000000db: SWAP1 000000dc: PUSH2 0x0218 000000df: JUMP 000000e0: JUMPDEST 000000e1: PUSH1 0x00 000000e3: PUSH1 0x40 000000e5: MLOAD 000000e6: DUP1 000000e7: DUP4 000000e8: SUB 000000e9: DUP2 000000ea: DUP6 000000eb: GAS 000000ec: DELEGATECALL 000000ed: SWAP2 000000ee: POP 000000ef: POP 000000f0: RETURNDATASIZE 000000f1: DUP1 000000f2: PUSH1 0x00 000000f4: DUP2 000000f5: EQ 000000f6: PUSH2 0x011b 000000f9: JUMPI 000000fa: PUSH1 0x40 000000fc: MLOAD 000000fd: SWAP2 000000fe: POP 000000ff: PUSH1 0x1f 00000101: NOT 00000102: PUSH1 0x3f 00000104: RETURNDATASIZE 00000105: ADD 00000106: AND 00000107: DUP3 00000108: ADD 00000109: PUSH1 0x40 0000010b: MSTORE 0000010c: RETURNDATASIZE 0000010d: DUP3 0000010e: MSTORE 0000010f: RETURNDATASIZE 00000110: PUSH1 0x00 00000112: PUSH1 0x20 00000114: DUP5 00000115: ADD 00000116: RETURNDATACOPY 00000117: PUSH2 0x0120 0000011a: JUMP 0000011b: JUMPDEST 0000011c: PUSH1 0x60 0000011e: SWAP2 0000011f: POP 00000120: JUMPDEST 00000121: POP 00000122: SWAP2 00000123: POP 00000124: SWAP2 00000125: POP 00000126: PUSH2 0x0131 00000129: DUP7 0000012a: DUP4 0000012b: DUP4 0000012c: DUP8 0000012d: PUSH2 0x013b 00000130: JUMP 00000131: JUMPDEST 00000132: SWAP7 00000133: SWAP6 00000134: POP 00000135: POP 00000136: POP 00000137: POP 00000138: POP 00000139: POP 0000013a: JUMP 0000013b: JUMPDEST 0000013c: PUSH1 0x60 0000013e: DUP4 0000013f: ISZERO 00000140: PUSH2 0x01ac 00000143: JUMPI 00000144: DUP3 00000145: MLOAD 00000146: PUSH2 0x01a5 00000149: JUMPI 0000014a: PUSH1 0x01 0000014c: PUSH1 0x01 0000014e: PUSH1 0xa0 00000150: SHL 00000151: SUB 00000152: DUP6 00000153: AND 00000154: EXTCODESIZE 00000155: PUSH2 0x01a5 00000158: JUMPI 00000159: PUSH1 0x40 0000015b: MLOAD 0000015c: PUSH3 0x461bcd 00000160: PUSH1 0xe5 00000162: SHL 00000163: DUP2 00000164: MSTORE 00000165: PUSH1 0x20 00000167: PUSH1 0x04 00000169: DUP3 0000016a: ADD 0000016b: MSTORE 0000016c: PUSH1 0x1d 0000016e: PUSH1 0x24 00000170: DUP3 00000171: ADD 00000172: MSTORE 00000173: PUSH32 0x416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000 00000194: PUSH1 0x44 00000196: DUP3 00000197: ADD 00000198: MSTORE 00000199: PUSH1 0x64 0000019b: ADD 0000019c: JUMPDEST 0000019d: PUSH1 0x40 0000019f: MLOAD 000001a0: DUP1 000001a1: SWAP2 000001a2: SUB 000001a3: SWAP1 000001a4: REVERT 000001a5: JUMPDEST 000001a6: POP 000001a7: DUP2 000001a8: PUSH2 0x01b6 000001ab: JUMP 000001ac: JUMPDEST 000001ad: PUSH2 0x01b6 000001b0: DUP4 000001b1: DUP4 000001b2: PUSH2 0x01be 000001b5: JUMP 000001b6: JUMPDEST 000001b7: SWAP5 000001b8: SWAP4 000001b9: POP 000001ba: POP 000001bb: POP 000001bc: POP 000001bd: JUMP 000001be: JUMPDEST 000001bf: DUP2 000001c0: MLOAD 000001c1: ISZERO 000001c2: PUSH2 0x01ce 000001c5: JUMPI 000001c6: DUP2 000001c7: MLOAD 000001c8: DUP1 000001c9: DUP4 000001ca: PUSH1 0x20 000001cc: ADD 000001cd: REVERT 000001ce: JUMPDEST 000001cf: DUP1 000001d0: PUSH1 0x40 000001d2: MLOAD 000001d3: PUSH3 0x461bcd 000001d7: PUSH1 0xe5 000001d9: SHL 000001da: DUP2 000001db: MSTORE 000001dc: PUSH1 0x04 000001de: ADD 000001df: PUSH2 0x019c 000001e2: SWAP2 000001e3: SWAP1 000001e4: PUSH2 0x0234 000001e7: JUMP 000001e8: JUMPDEST 000001e9: PUSH1 0x00 000001eb: JUMPDEST 000001ec: DUP4 000001ed: DUP2 000001ee: LT 000001ef: ISZERO 000001f0: PUSH2 0x0203 000001f3: JUMPI 000001f4: DUP2 000001f5: DUP2 000001f6: ADD 000001f7: MLOAD 000001f8: DUP4 000001f9: DUP3 000001fa: ADD 000001fb: MSTORE 000001fc: PUSH1 0x20 000001fe: ADD 000001ff: PUSH2 0x01eb 00000202: JUMP 00000203: JUMPDEST 00000204: DUP4 00000205: DUP2 00000206: GT 00000207: ISZERO 00000208: PUSH2 0x0212 0000020b: JUMPI 0000020c: PUSH1 0x00 0000020e: DUP5 0000020f: DUP5 00000210: ADD 00000211: MSTORE 00000212: JUMPDEST 00000213: POP 00000214: POP 00000215: POP 00000216: POP 00000217: JUMP 00000218: JUMPDEST 00000219: PUSH1 0x00 0000021b: DUP3 0000021c: MLOAD 0000021d: PUSH2 0x022a 00000220: DUP2 00000221: DUP5 00000222: PUSH1 0x20 00000224: DUP8 00000225: ADD 00000226: PUSH2 0x01e8 00000229: JUMP 0000022a: JUMPDEST 0000022b: SWAP2 0000022c: SWAP1 0000022d: SWAP2 0000022e: ADD 0000022f: SWAP3 00000230: SWAP2 00000231: POP 00000232: POP 00000233: JUMP 00000234: JUMPDEST 00000235: PUSH1 0x20 00000237: DUP2 00000238: MSTORE 00000239: PUSH1 0x00 0000023b: DUP3 0000023c: MLOAD 0000023d: DUP1 0000023e: PUSH1 0x20 00000240: DUP5 00000241: ADD 00000242: MSTORE 00000243: PUSH2 0x0253 00000246: DUP2 00000247: PUSH1 0x40 00000249: DUP6 0000024a: ADD 0000024b: PUSH1 0x20 0000024d: DUP8 0000024e: ADD 0000024f: PUSH2 0x01e8 00000252: JUMP 00000253: JUMPDEST 00000254: PUSH1 0x1f 00000256: ADD 00000257: PUSH1 0x1f 00000259: NOT 0000025a: AND 0000025b: SWAP2 0000025c: SWAP1 0000025d: SWAP2 0000025e: ADD 0000025f: PUSH1 0x40 00000261: ADD 00000262: SWAP3 00000263: SWAP2 00000264: POP 00000265: POP 00000266: JUMP 00000267: INVALID 00000268: COINBASE 00000269: PUSH5 0x6472657373 0000026f: GASPRICE 00000270: KECCAK256 00000271: PUSH13 0x6f772d6c6576656c2064656c65 0000027f: PUSH8 0x6174652063616c6c 00000288: KECCAK256 00000289: PUSH7 0x61696c6564a264 00000291: PUSH10 0x70667358221220ff8e6f 0000029c: UNKNOWN(0x2D) 0000029d: PUSH23 0x1d58b3bd984933269e01a7ff1f70a460b808056daa4cff 000002b5: CLZ 000002b6: UNKNOWN(0xE8) 000002b7: UNKNOWN(0xAB) 000002b8: PUSH10 0x64736f6c634300080900 000002c3: CALLER
| Severity | Category | Tool | Title | Confidence | Validated | Confirmed | Details |
|---|---|---|---|---|---|---|---|
| critical | upgradeability | detector | Untrusted DELEGATECALL target reachable | 70 | no | no |
viewtrace_id: 0x4c6d8834092af7bfd2342c0596a1769d1e9ef75df35fe324a06498c0617bbec3
call_targets:
0x49643fc85fb1f25b6775ebbbdc69295d45105abc
target_varies:
no
classification:
constant_target
validation_json{
"sink": "DELEGATECALL",
"errors": 0,
"status": "sink_reached",
"attempts": 1,
"trace_id": "0x4c6d8834092af7bfd2342c0596a1769d1e9ef75df35fe324a06498c0617bbec3",
"confirmed": false,
"trace_mode": "callTracer",
"call_targets": [
"0x49643fc85fb1f25b6775ebbbdc69295d45105abc"
],
"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": 173,
"opcode": "DELEGATECALL",
"returndata_checked": null,
"value_expression_category": "n/a",
"target_expression_category": "computed"
},
{
"pc": 236,
"opcode": "DELEGATECALL",
"returndata_checked": null,
"value_expression_category": "n/a",
"target_expression_category": "computed"
}
],
"guards_detected": [],
"delegatecall_count": 2,
"untrusted_delegatecall_count": 2
}
|
| high | access_control | codex | Retired committee members remain authorized to process requests | 85 | no | — |
viewevidence_json{
"evidence": "`setCommittee` only increments `committeeId` and adds new members; it never disables old committees. `receiveRequest` accepts any `_committeeId` provided by the caller and only checks membership for that id.",
"locations": [
"contracts/AbstractMost.sol:295",
"contracts/AbstractMost.sol:367"
],
"recommendation": "Enforce that `receiveRequest` only accepts the current `committeeId`, or add an explicit `activeCommitteeId`/revocation mechanism so old committees are disabled once a new committee is set. If you need overlap for in-flight requests, add a bounded grace period.",
"exploit_scenario": "After a committee rotation, members of the old committee can still call `receiveRequest` with their historical `_committeeId` and reach the previous `signatureThreshold`. They can mint or release arbitrary amounts by submitting any parameters and signing among themselves, even if governance intended to revoke them."
}
|
| medium | other | codex | USD transfer minimum is ignored when default minimum is smaller (often zero) | 75 | no | — |
viewevidence_json{
"evidence": "`minimumTransferAmount` computes `usdBasedMinimum` and then uses `Math.min(minimum, usdBasedMinimum)`, which selects the smaller threshold.",
"locations": [
"contracts/TransferLimit.sol:62"
],
"recommendation": "When a USD oracle is configured, use the USD-derived minimum as the floor (e.g., `Math.max(defaultMinima, usdBasedMinimum)`), or if `defaultMinima` is zero, treat it as unset and use the USD value directly.",
"exploit_scenario": "If `defaultMinima[_token]` is left at the default `0` (or set lower than the USD-based minimum), `minimumTransferAmount` returns the smaller of the two values. This makes the USD oracle limit ineffective, allowing any amount to pass `isRequestAllowed`."
}
|
| medium | oracle | codex | Oracle anomalies can bypass transfer limits due to unchecked price and fail-open handling | 75 | no | — |
viewevidence_json{
"evidence": "`minimumTransferAmount` casts `usdPrice` to `uint256` with no checks and divides by it; `Most.checkTransferAllowed` catches and ignores failures.",
"locations": [
"contracts/TransferLimit.sol:68",
"contracts/Most.sol:41"
],
"recommendation": "Validate `usdPrice > 0`, ensure `answeredInRound >= roundId`, and check `updatedAt` freshness before using prices. Consider failing closed (revert) in `Most` when the transfer limit contract errors instead of ignoring failures.",
"exploit_scenario": "If the oracle returns zero, negative, or stale data, `minimumTransferAmount` can revert (division by zero) or underflow to an extremely large uint cast. `Most.checkTransferAllowed` catches all errors and proceeds as if no limit exists, so an attacker who can manipulate oracle responses can bypass transfer limits entirely."
}
|
| medium | upgradeability | cast | DELEGATECALL present | 80 | no | — |
viewevidence_json{
"notes": "delegatecall enables execution in caller context; common in proxies/upgradeable patterns",
"delegatecall_count": 2
}
|