TW
Tripwire
Find contracts. Test them. Review real vulns.

Analysis Run

019bb377-6981-7258-b853-2ed456496df7

failed
Chain ID
1
Address
Block Number
Created
Mon, Jan 12, 2026 6:28 PM
Review Summary

What survived the funnel

Open this run to answer one question: is this real attacker-relevant behavior with real impact, or just normal contract plumbing?

Confirmed
0
Validated
0
Source
Verified
Review Backing
Present
Best current signal
Untrusted DELEGATECALL target reachable
crit 0 high 0 proxy context implementation known
Review Checklist
1. Read the findings table first. Ignore raw jobs until the result looks real.
2. Confirm attacker control, not just the existence of a sink.
3. Confirm money or privilege impact.
4. Keep it if the behavior is real. Kill it if this is normal proxy, admin, or upgrade plumbing.

Processing Jobs

Status & attempts
ID Type Status Attempts Heartbeat
39783 artifact.fetch complete 1 / 3 3 months ago
39784 proxy.resolve complete 1 / 3 3 months ago
39785 tool.cast_disasm complete 1 / 3 3 months ago
39786 analysis.bundle complete 1 / 3 3 months ago
39787 capability.graph complete 1 / 3 3 months ago
39788 detector.run complete 1 / 3 3 months ago
39789 validation.fork complete 1 / 3 3 months ago
39949 report.consolidate complete 1 / 6 3 months ago
39950 tool.slither failed 2 / 2 3 months ago

Artifact

Runtime bytecode: available
Creation TX:
Deployer:
Proxy: yes onchain_only
Verified Source: yes
Proxy evidence
{
    "method": "eip1967",
    "status": "onchain_only",
    "implementation": "0x7d3e713f1160a21d8251749e30e62ff12d27d6f2",
    "onchain_method": "eip1967"
}

Slither

tool.slither
Status
failed slither failed (exit 1): 'forge clean' running (wd: /tmp/slither-abgbxu_d) 'forge config --json' running 'forge build --build-info --skip ./test/** ./script/** --force' running (wd: /tmp/slither-abgbxu_d) 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-abgbxu_d/out/build-info is not a directory.
Attempts
2 / 2
Findings
0
Solc
View stderr
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-abgbxu_d)
'forge config --json' running
'forge build --build-info --skip ./test/** ./script/** --force' running (wd: /tmp/slither-abgbxu_d)
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-abgbxu_d/out/build-info is not a directory.

Codex

report.consolidate
Job Status
complete
Attempts
1 / 6
Findings
1
No findings?
no
Summary
Found a high-severity authorization gap in the claim flow: the merkle-leaf user data is not bound to ClaimData.entityUUID, allowing cross-entity claims if a platform signature is produced for mismatched data.
View stdout
{"no_findings":false,"summary":"Found a high-severity authorization gap in the claim flow: the merkle-leaf user data is not bound to ClaimData.entityUUID, allowing cross-entity claims if a platform signature is produced for mismatched data.","findings":[{"severity":"high","category":"auth","title":"ClaimData entityUUID not bound to merkle leaf allows cross-entity claims and carry miscalculation","exploit_scenario":"A signer for entity A obtains (or colludes to obtain) a platform SIGNER_ROLE signature over ClaimData that sets entityUUID = B and receiver = attacker. They submit a claim using A’s merkle proof and signature. The contract validates A’s leaf/signature but applies accounting to B (state/claimable/carry) while using A’s distributionShares/notChargedCarry, letting the attacker drain B’s allocation or avoid carry.","recommendation":"Require `claimData.entityUUID == user.entityUUID` and consider including the merkle-leaf fields (or their hash) in the signed ClaimData so the signed claim is cryptographically bound to the merkle leaf.","evidence":"`_release` verifies the merkle proof for `user`, but then uses `claimData.entityUUID` together with `user.distributionShares` and `user.amountInvestedUSDC` without checking that the entity UUIDs match.","locations":["src/unlock/v1/Distributor.sol:296","src/unlock/v1/Distributor.sol:321","src/unlock/v1/Distributor.sol:325","src/unlock/v1/Distributor.sol:332"]}]}
View stderr
OpenAI Codex v0.77.0 (research preview)
--------
workdir: /tmp/codex-yecvf0v4
model: gpt-5.2-codex
provider: openai
approval: never
sandbox: read-only
reasoning effort: high
reasoning summaries: auto
session id: 019bb37f-63b1-7190-a5ce-7c1a91a44640
--------
user
Context: The user requested the proxy at 0x1a170e3bbc7d930677bb7a77f9979032fefebb25.
Audit the implementation at 0x7d3e713f1160a21d8251749e30e62ff12d27d6f2.
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-yecvf0v4 succeeded in 6ms:
AUDIT_BUNDLE.txt
codex-output-schema.json
lib
src
exec
/bin/bash -lc "sed -n '1,200p' AUDIT_BUNDLE.txt" in /tmp/codex-yecvf0v4 succeeded in 7ms:
This file concatenates the full contents of first-party Solidity sources for auditing.
Do not treat dependency code under lib/ or @openzeppelin/ as first-party.

================================================================================
FILE: src/Merkle.sol
================================================================================
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.23;

import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";

/// @notice A library for computing merkle tree roots and proofs.
/// @dev This library produces complete trees. If a node does not have a sibling, it is combined with itself.
library Merkle {
    /// @notice A merkle tree.
    /// @dev Contains the leaf hashes at the 0th level, pairwise hashes in the following, and the root in the last.
    struct Tree {
        bytes32[][] hashes;
    }


thinking
**Continuing exploration**
exec
/bin/bash -lc "sed -n '200,400p' AUDIT_BUNDLE.txt" in /tmp/codex-yecvf0v4 succeeded in 6ms:
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.23;

interface IGenericRegistry {
    function setBytes32(bytes32 key, bytes32 value) external;
    function setAddress(bytes32 key, address value) external;
    function setUint256(bytes32 key, uint256 value) external;
    function setInt256(bytes32 key, int256 value) external;
    function setBool(bytes32 key, bool value) external;

    function readBytes32(bytes32 key) external view returns (bytes32);
    function readAddress(bytes32 key) external view returns (address);
    function readUint256(bytes32 key) external view returns (uint256);
    function readInt256(bytes32 key) external view returns (int256);
    function readBool(bytes32 key) external view returns (bool);
}

================================================================================
FILE: src/interfaces/PlatformEvents.sol
================================================================================

thinking
**Needing more content to proceed**
exec
/bin/bash -lc "sed -n '400,800p' AUDIT_BUNDLE.txt" in /tmp/codex-yecvf0v4 succeeded in 6ms:
    bytes16[] private _carryWithdrawerUUIDs;

    struct Init {
        IERC20 token;
        bytes16 tokenDistributionUUID;
        UnlockerLib.Unlocker unlocker;
        IGenericRegistry genericRegistry;
        Claimer[] claimers;
        CarryWithdrawer[] carryWithdrawers;
        address adminIM;
        address managerIM;
        address adminENG;
        address managerENG;
        address platformSigner;
        address platformSender;
        bytes32 expectedClaimersRoot;
        uint16 platformCarryBPS;
    }

    constructor() {
exec
/bin/bash -lc "sed -n '800,1200p' AUDIT_BUNDLE.txt" in /tmp/codex-yecvf0v4 succeeded in 5ms:
        for (uint256 i = 0; i < claimParams.length; i++) {
            carryAmounts[i] = _calculateCarryAmount(
                claimParams[i].entityUUID,
                claimParams[i].amountInvestedUSDC,
                claimParams[i].notChargedCarry,
                claimParams[i].amountToClaim,
                price
            );
        }

        return carryAmounts;
    }

    /// @notice Setter for the generic registry.
    function setGenericRegistry(IGenericRegistry registry) external onlyRole(MANAGER_ROLE) {
        genericRegistry = registry;
    }

    /// @notice Setter for the platform carry BPS
    /// @dev We use a separate setter for the platform carry over setting it in the setCarryWithdrawerSettings
exec
/bin/bash -lc "sed -n '1200,1600p' AUDIT_BUNDLE.txt" in /tmp/codex-yecvf0v4 succeeded in 5ms:
/// @notice Interface for Superfluid Unlocker contracts (SuperfluidToken).
/// @dev The unlocker address for Superfluid should be the SuperToken address.
interface ISuperfluidUnlocker {
    function balanceOf(address account) external view returns (uint256);
    function downgrade(uint256 amount) external;
}

/// @notice Library for managing calls to unlocker contracts.
/// @dev The distributor contracts will need to interface with a number of different unlocker contracts.
/// This library provides a dynamic function dispatch to make interacting with Unlocker contracts easier.
library UnlockerLib {
    /// @notice Struct for holding the unlocker contract address and the function types it implements.
    /// @dev This is used to dynamically dispatch calls to the correct function on the unlocker contract as specified by the releasableType and releaseType.
    /// @dev The unlocker address may be 0, in which case no function calls will be made.
    struct Unlocker {
        address unlocker;
        bytes6 releasableType;
        bytes6 releaseType;
    }




thinking
**Analyzing potential reentrancy and signature issues**
thinking
**Identifying mismatched entityUUID authorization risks**

I’m unpacking how claimData.entityUUID not being matched to the user's merkle leaf entityUUID could let one signer claim tokens allocated to another entity, if they get a platform signature. This creates a cross-entity claim issue where distribution shares mix incorrectly, potentially allowing token theft if the platform signer cooperates or is compromised. It’s a significant authorization gap that the contract should prevent to ensure claims are securely tied to the correct entity.
thinking
**Detailing signature, carry, and config edge cases**

I’m outlining issues with signature handling for contract signers, lack of zero-address checks on receivers, potential overflow in carry basis points leading to claim failures, and configuration risks like unchecked total carry BPS sums. I’ll also note how reentrancy and merkle proof usages impact the contract’s integrity, emphasizing the missing binding between claimData and user entityUUID as the main high-severity finding.
exec
/bin/bash -lc "nl -ba src/unlock/v1/Distributor.sol | sed -n '120,260p'" in /tmp/codex-yecvf0v4 succeeded in 7ms:
   120	    /// @notice  The token that will be unlocked/released.
   121	    IERC20 public token;
   122	
   123	    /// @notice Flag to enable/disable token claims and carry withdrawals.
   124	    bool public isEnabled;
   125	
   126	    /// @notice Flag to enable/disable the forced distribution mode.
   127	    /// @dev When enabled, the token claim and carry withdrawal functions do not require signatures from the user signer.
   128	    bool public isForcedDistributionModeEnabled;
   129	
   130	    /// @notice The UUID of the token distribution.
   131	    bytes16 public tokenDistributionUUID;
   132	
   133	    /// @notice  The total amount of distribution shares for the SPV across all users.
   134	    /// @dev This is the sum of all `distributionShares` in `usersSettings`.
   135	    /// @dev It is used to compute the relative share of the total unlocked tokens for each state. It is derived from
   136	    /// the users's ownership of the SPV.
   137	    uint64 public totalDistributionShares;
   138	
   139	    /// @notice The total carry in basis points.

thinking
**Searching for _release references**
exec
/bin/bash -lc "nl -ba src/unlock/v1/Distributor.sol | sed -n '300,420p'" in /tmp/codex-yecvf0v4 succeeded in 7ms:
   300	                revert InvalidClaimerMerkleProof(leaf, computedRoot, claimersRoot);
   301	            }
   302	        }
   303	
   304	        {
   305	            bytes32 digest = ClaimDataLib.digestTypedData(claimData, _domainSeparatorV4());
   306	
   307	            if (
   308	                !isForcedDistributionModeEnabled
   309	                    && !SignatureChecker.isValidSignatureNow(user.signer, digest, claimSignatureUser)
   310	            ) {
   311	                address signer = ECDSA.recover(digest, claimSignatureUser);
   312	                revert UnauthorizedUserSigner(signer, user.signer);
   313	            }
   314	
   315	            address signerPlatform = ECDSA.recover(digest, claimSignaturePlatform);
   316	            if (!hasRole(SIGNER_ROLE, signerPlatform)) {
   317	                revert UnauthorizedPlatformSigner(signerPlatform);
   318	            }
   319	        }

thinking
**Analyzing claimData entityUUID mismatch and overflow risks**
exec
/bin/bash -lc "nl -ba src/unlock/v1/Distributor.sol | sed -n '260,340p'" in /tmp/codex-yecvf0v4 succeeded in 6ms:
   260	    /// @param claimData The claim data that is signed by the user and the platform.
   261	    /// @param claimSignatureUser The signature of the claim data by the user (EIP-712).
   262	    /// @param claimSignaturePlatform The signature of the claim data by the platform (EIP-712).
   263	    /// @param user The user settings that are used in the merkle proof.
   264	    /// @param merkleProof The merkle proof that is used to verify the user's settings in the merkle tree.
   265	    /// @param pullUnlocked Whether to pull the unlocked tokens from the unlocker contract.
   266	    function claim(
   267	        ClaimData calldata claimData,
   268	        bytes calldata claimSignatureUser,
   269	        bytes calldata claimSignaturePlatform,
   270	        Claimer calldata user,
   271	        bytes32[] calldata merkleProof,
   272	        bool pullUnlocked
   273	    ) external onlyIf(isEnabled) {
   274	        if (pullUnlocked) {
   275	            pullUnlockedTokens();
   276	        }
   277	
   278	        _release(claimData, claimSignatureUser, claimSignaturePlatform, user, merkleProof);
   279	    }

thinking
**Preparing JSON report for authorization issue**
codex
{"no_findings":false,"summary":"Found a high-severity authorization gap in the claim flow: the merkle-leaf user data is not bound to ClaimData.entityUUID, allowing cross-entity claims if a platform signature is produced for mismatched data.","findings":[{"severity":"high","category":"auth","title":"ClaimData entityUUID not bound to merkle leaf allows cross-entity claims and carry miscalculation","exploit_scenario":"A signer for entity A obtains (or colludes to obtain) a platform SIGNER_ROLE signature over ClaimData that sets entityUUID = B and receiver = attacker. They submit a claim using A’s merkle proof and signature. The contract validates A’s leaf/signature but applies accounting to B (state/claimable/carry) while using A’s distributionShares/notChargedCarry, letting the attacker drain B’s allocation or avoid carry.","recommendation":"Require `claimData.entityUUID == user.entityUUID` and consider including the merkle-leaf fields (or their hash) in the signed ClaimData so the signed claim is cryptographically bound to the merkle leaf.","evidence":"`_release` verifies the merkle proof for `user`, but then uses `claimData.entityUUID` together with `user.distributionShares` and `user.amountInvestedUSDC` without checking that the entity UUIDs match.","locations":["src/unlock/v1/Distributor.sol:296","src/unlock/v1/Distributor.sol:321","src/unlock/v1/Distributor.sol:325","src/unlock/v1/Distributor.sol:332"]}]}
tokens used
99,071
Metadata
{
    "codex": {
        "ran_at": "2026-01-12T18:41:31.084429+00:00",
        "returncode": 0
    },
    "project": {
        "mode": "source",
        "file_count": 31,
        "import_count": 35,
        "first_party_contracts_count": 8
    },
    "summary": "Found a high-severity authorization gap in the claim flow: the merkle-leaf user data is not bound to ClaimData.entityUUID, allowing cross-entity claims if a platform signature is produced for mismatched data.",
    "raw_final": {
        "summary": "Found a high-severity authorization gap in the claim flow: the merkle-leaf user data is not bound to ClaimData.entityUUID, allowing cross-entity claims if a platform signature is produced for mismatched data.",
        "findings": [
            {
                "title": "ClaimData entityUUID not bound to merkle leaf allows cross-entity claims and carry miscalculation",
                "category": "auth",
                "evidence": "`_release` verifies the merkle proof for `user`, but then uses `claimData.entityUUID` together with `user.distributionShares` and `user.amountInvestedUSDC` without checking that the entity UUIDs match.",
                "severity": "high",
                "locations": [
                    "src/unlock/v1/Distributor.sol:296",
                    "src/unlock/v1/Distributor.sol:321",
                    "src/unlock/v1/Distributor.sol:325",
                    "src/unlock/v1/Distributor.sol:332"
                ],
                "recommendation": "Require `claimData.entityUUID == user.entityUUID` and consider including the merkle-leaf fields (or their hash) in the signed ClaimData so the signed claim is cryptographically bound to the merkle leaf.",
                "exploit_scenario": "A signer for entity A obtains (or colludes to obtain) a platform SIGNER_ROLE signature over ClaimData that sets entityUUID = B and receiver = attacker. They submit a claim using A\u2019s merkle proof and signature. The contract validates A\u2019s leaf/signature but applies accounting to B (state/claimable/carry) while using A\u2019s distributionShares/notChargedCarry, letting the attacker drain B\u2019s allocation or avoid carry."
            }
        ],
        "no_findings": false
    },
    "no_findings": false,
    "proxy_context": {
        "proxy_run_id": "019bb377-6981-7258-b853-2ed456496df7",
        "proxy_address": "0x1a170e3bbc7d930677bb7a77f9979032fefebb25",
        "implementation_run_id": "019bb377-84c2-701a-9244-2bb4af000d2a",
        "implementation_address": "0x7d3e713f1160a21d8251749e30e62ff12d27d6f2"
    },
    "schema_version": 1
}

Opcode Heuristics

tool.cast_disasm
Delegatecall
2
Selfdestruct
0
CREATE2
0
CALL-family (heavy)
0
EXT*/BALANCE
3
Total opcodes
728
Flags
delegatecall_present
View cast disassembly output
00000000: PUSH1 0x80
00000002: PUSH1 0x40
00000004: MSTORE
00000005: PUSH2 0x000c
00000008: PUSH2 0x000e
0000000b: JUMP
0000000c: JUMPDEST
0000000d: STOP
0000000e: JUMPDEST
0000000f: PUSH32 0x0000000000000000000000003a63da3371ca19ad20a69e3e9fbb296660832f5f
00000030: PUSH1 0x01
00000032: PUSH1 0x01
00000034: PUSH1 0xa0
00000036: SHL
00000037: SUB
00000038: AND
00000039: CALLER
0000003a: SUB
0000003b: PUSH2 0x007a
0000003e: JUMPI
0000003f: PUSH0
00000040: CALLDATALOAD
00000041: PUSH1 0x01
00000043: PUSH1 0x01
00000045: PUSH1 0xe0
00000047: SHL
00000048: SUB
00000049: NOT
0000004a: AND
0000004b: PUSH4 0x278f7943
00000050: PUSH1 0xe1
00000052: SHL
00000053: EQ
00000054: PUSH2 0x0070
00000057: JUMPI
00000058: PUSH1 0x40
0000005a: MLOAD
0000005b: PUSH4 0x34ad5dbb
00000060: PUSH1 0xe2
00000062: SHL
00000063: DUP2
00000064: MSTORE
00000065: PUSH1 0x04
00000067: ADD
00000068: PUSH1 0x40
0000006a: MLOAD
0000006b: DUP1
0000006c: SWAP2
0000006d: SUB
0000006e: SWAP1
0000006f: REVERT
00000070: JUMPDEST
00000071: PUSH2 0x0078
00000074: PUSH2 0x0082
00000077: JUMP
00000078: JUMPDEST
00000079: JUMP
0000007a: JUMPDEST
0000007b: PUSH2 0x0078
0000007e: PUSH2 0x00b0
00000081: JUMP
00000082: JUMPDEST
00000083: PUSH0
00000084: DUP1
00000085: PUSH2 0x0091
00000088: CALLDATASIZE
00000089: PUSH1 0x04
0000008b: DUP2
0000008c: DUP5
0000008d: PUSH2 0x0303
00000090: JUMP
00000091: JUMPDEST
00000092: DUP2
00000093: ADD
00000094: SWAP1
00000095: PUSH2 0x009e
00000098: SWAP2
00000099: SWAP1
0000009a: PUSH2 0x033e
0000009d: JUMP
0000009e: JUMPDEST
0000009f: SWAP2
000000a0: POP
000000a1: SWAP2
000000a2: POP
000000a3: PUSH2 0x00ac
000000a6: DUP3
000000a7: DUP3
000000a8: PUSH2 0x00c0
000000ab: JUMP
000000ac: JUMPDEST
000000ad: POP
000000ae: POP
000000af: JUMP
000000b0: JUMPDEST
000000b1: PUSH2 0x0078
000000b4: PUSH2 0x00bb
000000b7: PUSH2 0x011a
000000ba: JUMP
000000bb: JUMPDEST
000000bc: PUSH2 0x0151
000000bf: JUMP
000000c0: JUMPDEST
000000c1: PUSH2 0x00c9
000000c4: DUP3
000000c5: PUSH2 0x016f
000000c8: JUMP
000000c9: JUMPDEST
000000ca: PUSH1 0x40
000000cc: MLOAD
000000cd: PUSH1 0x01
000000cf: PUSH1 0x01
000000d1: PUSH1 0xa0
000000d3: SHL
000000d4: SUB
000000d5: DUP4
000000d6: AND
000000d7: SWAP1
000000d8: PUSH32 0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b
000000f9: SWAP1
000000fa: PUSH0
000000fb: SWAP1
000000fc: LOG2
000000fd: DUP1
000000fe: MLOAD
000000ff: ISZERO
00000100: PUSH2 0x0112
00000103: JUMPI
00000104: PUSH2 0x010d
00000107: DUP3
00000108: DUP3
00000109: PUSH2 0x01ea
0000010c: JUMP
0000010d: JUMPDEST
0000010e: POP
0000010f: POP
00000110: POP
00000111: JUMP
00000112: JUMPDEST
00000113: PUSH2 0x00ac
00000116: PUSH2 0x025c
00000119: JUMP
0000011a: JUMPDEST
0000011b: PUSH0
0000011c: PUSH2 0x014c
0000011f: PUSH32 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc
00000140: SLOAD
00000141: PUSH1 0x01
00000143: PUSH1 0x01
00000145: PUSH1 0xa0
00000147: SHL
00000148: SUB
00000149: AND
0000014a: SWAP1
0000014b: JUMP
0000014c: JUMPDEST
0000014d: SWAP1
0000014e: POP
0000014f: SWAP1
00000150: JUMP
00000151: JUMPDEST
00000152: CALLDATASIZE
00000153: PUSH0
00000154: DUP1
00000155: CALLDATACOPY
00000156: PUSH0
00000157: DUP1
00000158: CALLDATASIZE
00000159: PUSH0
0000015a: DUP5
0000015b: GAS
0000015c: DELEGATECALL
0000015d: RETURNDATASIZE
0000015e: PUSH0
0000015f: DUP1
00000160: RETURNDATACOPY
00000161: DUP1
00000162: DUP1
00000163: ISZERO
00000164: PUSH2 0x016b
00000167: JUMPI
00000168: RETURNDATASIZE
00000169: PUSH0
0000016a: RETURN
0000016b: JUMPDEST
0000016c: RETURNDATASIZE
0000016d: PUSH0
0000016e: REVERT
0000016f: JUMPDEST
00000170: DUP1
00000171: PUSH1 0x01
00000173: PUSH1 0x01
00000175: PUSH1 0xa0
00000177: SHL
00000178: SUB
00000179: AND
0000017a: EXTCODESIZE
0000017b: PUSH0
0000017c: SUB
0000017d: PUSH2 0x01a9
00000180: JUMPI
00000181: PUSH1 0x40
00000183: MLOAD
00000184: PUSH4 0x4c9c8ce3
00000189: PUSH1 0xe0
0000018b: SHL
0000018c: DUP2
0000018d: MSTORE
0000018e: PUSH1 0x01
00000190: PUSH1 0x01
00000192: PUSH1 0xa0
00000194: SHL
00000195: SUB
00000196: DUP3
00000197: AND
00000198: PUSH1 0x04
0000019a: DUP3
0000019b: ADD
0000019c: MSTORE
0000019d: PUSH1 0x24
0000019f: ADD
000001a0: JUMPDEST
000001a1: PUSH1 0x40
000001a3: MLOAD
000001a4: DUP1
000001a5: SWAP2
000001a6: SUB
000001a7: SWAP1
000001a8: REVERT
000001a9: JUMPDEST
000001aa: PUSH32 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc
000001cb: DUP1
000001cc: SLOAD
000001cd: PUSH1 0x01
000001cf: PUSH1 0x01
000001d1: PUSH1 0xa0
000001d3: SHL
000001d4: SUB
000001d5: NOT
000001d6: AND
000001d7: PUSH1 0x01
000001d9: PUSH1 0x01
000001db: PUSH1 0xa0
000001dd: SHL
000001de: SUB
000001df: SWAP3
000001e0: SWAP1
000001e1: SWAP3
000001e2: AND
000001e3: SWAP2
000001e4: SWAP1
000001e5: SWAP2
000001e6: OR
000001e7: SWAP1
000001e8: SSTORE
000001e9: JUMP
000001ea: JUMPDEST
000001eb: PUSH1 0x60
000001ed: PUSH0
000001ee: DUP1
000001ef: DUP5
000001f0: PUSH1 0x01
000001f2: PUSH1 0x01
000001f4: PUSH1 0xa0
000001f6: SHL
000001f7: SUB
000001f8: AND
000001f9: DUP5
000001fa: PUSH1 0x40
000001fc: MLOAD
000001fd: PUSH2 0x0206
00000200: SWAP2
00000201: SWAP1
00000202: PUSH2 0x0407
00000205: JUMP
00000206: JUMPDEST
00000207: PUSH0
00000208: PUSH1 0x40
0000020a: MLOAD
0000020b: DUP1
0000020c: DUP4
0000020d: SUB
0000020e: DUP2
0000020f: DUP6
00000210: GAS
00000211: DELEGATECALL
00000212: SWAP2
00000213: POP
00000214: POP
00000215: RETURNDATASIZE
00000216: DUP1
00000217: PUSH0
00000218: DUP2
00000219: EQ
0000021a: PUSH2 0x023e
0000021d: JUMPI
0000021e: PUSH1 0x40
00000220: MLOAD
00000221: SWAP2
00000222: POP
00000223: PUSH1 0x1f
00000225: NOT
00000226: PUSH1 0x3f
00000228: RETURNDATASIZE
00000229: ADD
0000022a: AND
0000022b: DUP3
0000022c: ADD
0000022d: PUSH1 0x40
0000022f: MSTORE
00000230: RETURNDATASIZE
00000231: DUP3
00000232: MSTORE
00000233: RETURNDATASIZE
00000234: PUSH0
00000235: PUSH1 0x20
00000237: DUP5
00000238: ADD
00000239: RETURNDATACOPY
0000023a: PUSH2 0x0243
0000023d: JUMP
0000023e: JUMPDEST
0000023f: PUSH1 0x60
00000241: SWAP2
00000242: POP
00000243: JUMPDEST
00000244: POP
00000245: SWAP2
00000246: POP
00000247: SWAP2
00000248: POP
00000249: PUSH2 0x0253
0000024c: DUP6
0000024d: DUP4
0000024e: DUP4
0000024f: PUSH2 0x027b
00000252: JUMP
00000253: JUMPDEST
00000254: SWAP6
00000255: SWAP5
00000256: POP
00000257: POP
00000258: POP
00000259: POP
0000025a: POP
0000025b: JUMP
0000025c: JUMPDEST
0000025d: CALLVALUE
0000025e: ISZERO
0000025f: PUSH2 0x0078
00000262: JUMPI
00000263: PUSH1 0x40
00000265: MLOAD
00000266: PUSH4 0xb398979f
0000026b: PUSH1 0xe0
0000026d: SHL
0000026e: DUP2
0000026f: MSTORE
00000270: PUSH1 0x04
00000272: ADD
00000273: PUSH1 0x40
00000275: MLOAD
00000276: DUP1
00000277: SWAP2
00000278: SUB
00000279: SWAP1
0000027a: REVERT
0000027b: JUMPDEST
0000027c: PUSH1 0x60
0000027e: DUP3
0000027f: PUSH2 0x0290
00000282: JUMPI
00000283: PUSH2 0x028b
00000286: DUP3
00000287: PUSH2 0x02da
0000028a: JUMP
0000028b: JUMPDEST
0000028c: PUSH2 0x02d3
0000028f: JUMP
00000290: JUMPDEST
00000291: DUP2
00000292: MLOAD
00000293: ISZERO
00000294: DUP1
00000295: ISZERO
00000296: PUSH2 0x02a7
00000299: JUMPI
0000029a: POP
0000029b: PUSH1 0x01
0000029d: PUSH1 0x01
0000029f: PUSH1 0xa0
000002a1: SHL
000002a2: SUB
000002a3: DUP5
000002a4: AND
000002a5: EXTCODESIZE
000002a6: ISZERO
000002a7: JUMPDEST
000002a8: ISZERO
000002a9: PUSH2 0x02d0
000002ac: JUMPI
000002ad: PUSH1 0x40
000002af: MLOAD
000002b0: PUSH4 0x9996b315
000002b5: PUSH1 0xe0
000002b7: SHL
000002b8: DUP2
000002b9: MSTORE
000002ba: PUSH1 0x01
000002bc: PUSH1 0x01
000002be: PUSH1 0xa0
000002c0: SHL
000002c1: SUB
000002c2: DUP6
000002c3: AND
000002c4: PUSH1 0x04
000002c6: DUP3
000002c7: ADD
000002c8: MSTORE
000002c9: PUSH1 0x24
000002cb: ADD
000002cc: PUSH2 0x01a0
000002cf: JUMP
000002d0: JUMPDEST
000002d1: POP
000002d2: DUP1
000002d3: JUMPDEST
000002d4: SWAP4
000002d5: SWAP3
000002d6: POP
000002d7: POP
000002d8: POP
000002d9: JUMP
000002da: JUMPDEST
000002db: DUP1
000002dc: MLOAD
000002dd: ISZERO
000002de: PUSH2 0x02ea
000002e1: JUMPI
000002e2: DUP1
000002e3: MLOAD
000002e4: DUP1
000002e5: DUP3
000002e6: PUSH1 0x20
000002e8: ADD
000002e9: REVERT
000002ea: JUMPDEST
000002eb: PUSH1 0x40
000002ed: MLOAD
000002ee: PUSH4 0x0a12f521
000002f3: PUSH1 0xe1
000002f5: SHL
000002f6: DUP2
000002f7: MSTORE
000002f8: PUSH1 0x04
000002fa: ADD
000002fb: PUSH1 0x40
000002fd: MLOAD
000002fe: DUP1
000002ff: SWAP2
00000300: SUB
00000301: SWAP1
00000302: REVERT
00000303: JUMPDEST
00000304: PUSH0
00000305: DUP1
00000306: DUP6
00000307: DUP6
00000308: GT
00000309: ISZERO
0000030a: PUSH2 0x0311
0000030d: JUMPI
0000030e: PUSH0
0000030f: DUP1
00000310: REVERT
00000311: JUMPDEST
00000312: DUP4
00000313: DUP7
00000314: GT
00000315: ISZERO
00000316: PUSH2 0x031d
00000319: JUMPI
0000031a: PUSH0
0000031b: DUP1
0000031c: REVERT
0000031d: JUMPDEST
0000031e: POP
0000031f: POP
00000320: DUP3
00000321: ADD
00000322: SWAP4
00000323: SWAP2
00000324: SWAP1
00000325: SWAP3
00000326: SUB
00000327: SWAP2
00000328: POP
00000329: JUMP
0000032a: JUMPDEST
0000032b: PUSH4 0x4e487b71
00000330: PUSH1 0xe0
00000332: SHL
00000333: PUSH0
00000334: MSTORE
00000335: PUSH1 0x41
00000337: PUSH1 0x04
00000339: MSTORE
0000033a: PUSH1 0x24
0000033c: PUSH0
0000033d: REVERT
0000033e: JUMPDEST
0000033f: PUSH0
00000340: DUP1
00000341: PUSH1 0x40
00000343: DUP4
00000344: DUP6
00000345: SUB
00000346: SLT
00000347: ISZERO
00000348: PUSH2 0x034f
0000034b: JUMPI
0000034c: PUSH0
0000034d: DUP1
0000034e: REVERT
0000034f: JUMPDEST
00000350: DUP3
00000351: CALLDATALOAD
00000352: PUSH1 0x01
00000354: PUSH1 0x01
00000356: PUSH1 0xa0
00000358: SHL
00000359: SUB
0000035a: DUP2
0000035b: AND
0000035c: DUP2
0000035d: EQ
0000035e: PUSH2 0x0365
00000361: JUMPI
00000362: PUSH0
00000363: DUP1
00000364: REVERT
00000365: JUMPDEST
00000366: SWAP2
00000367: POP
00000368: PUSH1 0x20
0000036a: DUP4
0000036b: ADD
0000036c: CALLDATALOAD
0000036d: PUSH8 0xffffffffffffffff
00000376: DUP1
00000377: DUP3
00000378: GT
00000379: ISZERO
0000037a: PUSH2 0x0381
0000037d: JUMPI
0000037e: PUSH0
0000037f: DUP1
00000380: REVERT
00000381: JUMPDEST
00000382: DUP2
00000383: DUP6
00000384: ADD
00000385: SWAP2
00000386: POP
00000387: DUP6
00000388: PUSH1 0x1f
0000038a: DUP4
0000038b: ADD
0000038c: SLT
0000038d: PUSH2 0x0394
00000390: JUMPI
00000391: PUSH0
00000392: DUP1
00000393: REVERT
00000394: JUMPDEST
00000395: DUP2
00000396: CALLDATALOAD
00000397: DUP2
00000398: DUP2
00000399: GT
0000039a: ISZERO
0000039b: PUSH2 0x03a6
0000039e: JUMPI
0000039f: PUSH2 0x03a6
000003a2: PUSH2 0x032a
000003a5: JUMP
000003a6: JUMPDEST
000003a7: PUSH1 0x40
000003a9: MLOAD
000003aa: PUSH1 0x1f
000003ac: DUP3
000003ad: ADD
000003ae: PUSH1 0x1f
000003b0: NOT
000003b1: SWAP1
000003b2: DUP2
000003b3: AND
000003b4: PUSH1 0x3f
000003b6: ADD
000003b7: AND
000003b8: DUP2
000003b9: ADD
000003ba: SWAP1
000003bb: DUP4
000003bc: DUP3
000003bd: GT
000003be: DUP2
000003bf: DUP4
000003c0: LT
000003c1: OR
000003c2: ISZERO
000003c3: PUSH2 0x03ce
000003c6: JUMPI
000003c7: PUSH2 0x03ce
000003ca: PUSH2 0x032a
000003cd: JUMP
000003ce: JUMPDEST
000003cf: DUP2
000003d0: PUSH1 0x40
000003d2: MSTORE
000003d3: DUP3
000003d4: DUP2
000003d5: MSTORE
000003d6: DUP9
000003d7: PUSH1 0x20
000003d9: DUP5
000003da: DUP8
000003db: ADD
000003dc: ADD
000003dd: GT
000003de: ISZERO
000003df: PUSH2 0x03e6
000003e2: JUMPI
000003e3: PUSH0
000003e4: DUP1
000003e5: REVERT
000003e6: JUMPDEST
000003e7: DUP3
000003e8: PUSH1 0x20
000003ea: DUP7
000003eb: ADD
000003ec: PUSH1 0x20
000003ee: DUP4
000003ef: ADD
000003f0: CALLDATACOPY
000003f1: PUSH0
000003f2: PUSH1 0x20
000003f4: DUP5
000003f5: DUP4
000003f6: ADD
000003f7: ADD
000003f8: MSTORE
000003f9: DUP1
000003fa: SWAP6
000003fb: POP
000003fc: POP
000003fd: POP
000003fe: POP
000003ff: POP
00000400: POP
00000401: SWAP3
00000402: POP
00000403: SWAP3
00000404: SWAP1
00000405: POP
00000406: JUMP
00000407: JUMPDEST
00000408: PUSH0
00000409: DUP3
0000040a: MLOAD
0000040b: PUSH0
0000040c: JUMPDEST
0000040d: DUP2
0000040e: DUP2
0000040f: LT
00000410: ISZERO
00000411: PUSH2 0x0426
00000414: JUMPI
00000415: PUSH1 0x20
00000417: DUP2
00000418: DUP7
00000419: ADD
0000041a: DUP2
0000041b: ADD
0000041c: MLOAD
0000041d: DUP6
0000041e: DUP4
0000041f: ADD
00000420: MSTORE
00000421: ADD
00000422: PUSH2 0x040c
00000425: JUMP
00000426: JUMPDEST
00000427: POP
00000428: PUSH0
00000429: SWAP3
0000042a: ADD
0000042b: SWAP2
0000042c: DUP3
0000042d: MSTORE
0000042e: POP
0000042f: SWAP2
00000430: SWAP1
00000431: POP
00000432: JUMP
00000433: INVALID
00000434: LOG2
00000435: PUSH5 0x6970667358
0000043b: UNKNOWN(0x22)
0000043c: SLT
0000043d: KECCAK256
0000043e: PUSH21 0x0d83b0f0314c5a4b4f3771cc54601255c45b2e55a7
00000454: UNKNOWN(0xDF)
00000455: EXTCODECOPY
00000456: UNKNOWN(0xDF)
00000457: UNKNOWN(0xD8)
00000458: EQ
00000459: SWAP16
0000045a: PC
0000045b: COINBASE
0000045c: MUL
0000045d: UNKNOWN(0xC8)
0000045e: PUSH5 0x736f6c6343
00000464: STOP
00000465: ADDMOD
00000466: OR
00000467: STOP
00000468: CALLER

Findings

validated = confirmed for call sinks; sink observed for others
Severity Category Tool Title Confidence Validated Confirmed Details
critical upgradeability detector Untrusted DELEGATECALL target reachable 70 no no
view
trace_id: 0xccd6bcb299050b870654ad809eda3eb3648fe4824473ea30d5d8202875e2044b
call_targets: 0x7d3e713f1160a21d8251749e30e62ff12d27d6f2
target_varies: no
classification: constant_target
validation_json
{
    "sink": "DELEGATECALL",
    "errors": 0,
    "status": "sink_reached",
    "attempts": 1,
    "trace_id": "0xccd6bcb299050b870654ad809eda3eb3648fe4824473ea30d5d8202875e2044b",
    "confirmed": false,
    "trace_mode": "callTracer",
    "call_targets": [
        "0x7d3e713f1160a21d8251749e30e62ff12d27d6f2"
    ],
    "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": "0x278f7943",
            "selector": "0x278f7943",
            "calldata_variants": [
                "0x278f7943",
                "0x278f79430000000000000000000000000000000000000000000000000000000000000000"
            ]
        }
    ]
}
evidence_json
{
    "examples": [
        {
            "pc": 348,
            "opcode": "DELEGATECALL",
            "returndata_checked": null,
            "value_expression_category": "n/a",
            "target_expression_category": "computed"
        },
        {
            "pc": 529,
            "opcode": "DELEGATECALL",
            "returndata_checked": null,
            "value_expression_category": "n/a",
            "target_expression_category": "computed"
        }
    ],
    "guards_detected": [],
    "delegatecall_count": 2,
    "untrusted_delegatecall_count": 2
}
high auth codex ClaimData entityUUID not bound to merkle leaf allows cross-entity claims and carry miscalculation 85 no
view
evidence_json
{
    "evidence": "`_release` verifies the merkle proof for `user`, but then uses `claimData.entityUUID` together with `user.distributionShares` and `user.amountInvestedUSDC` without checking that the entity UUIDs match.",
    "locations": [
        "src/unlock/v1/Distributor.sol:296",
        "src/unlock/v1/Distributor.sol:321",
        "src/unlock/v1/Distributor.sol:325",
        "src/unlock/v1/Distributor.sol:332"
    ],
    "recommendation": "Require `claimData.entityUUID == user.entityUUID` and consider including the merkle-leaf fields (or their hash) in the signed ClaimData so the signed claim is cryptographically bound to the merkle leaf.",
    "exploit_scenario": "A signer for entity A obtains (or colludes to obtain) a platform SIGNER_ROLE signature over ClaimData that sets entityUUID = B and receiver = attacker. They submit a claim using A\u2019s merkle proof and signature. The contract validates A\u2019s leaf/signature but applies accounting to B (state/claimable/carry) while using A\u2019s distributionShares/notChargedCarry, letting the attacker drain B\u2019s allocation or avoid carry."
}
medium upgradeability cast DELEGATECALL present 80 no
view
evidence_json
{
    "notes": "delegatecall enables execution in caller context; common in proxies/upgradeable patterns",
    "delegatecall_count": 2
}