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

Analysis Run

019bb004-7ab1-705e-a818-96d48a80f311

complete
Chain ID
1
Address
Block Number
Created
Mon, Jan 12, 2026 2:24 AM
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
39283 artifact.fetch complete 1 / 3 3 months ago
39284 proxy.resolve complete 1 / 3 3 months ago
39285 tool.cast_disasm complete 1 / 3 3 months ago
39286 analysis.bundle complete 1 / 3 3 months ago
39287 capability.graph complete 1 / 3 3 months ago
39288 detector.run complete 1 / 3 3 months ago
39289 validation.fork complete 1 / 3 3 months ago
39448 report.consolidate complete 1 / 6 3 months ago
39449 tool.slither complete 1 / 2 3 months ago

Artifact

Runtime bytecode: available
Creation TX:
Deployer:
Proxy: yes confirmed
Verified Source: yes
Proxy evidence
{
    "method": "etherscan+onchain",
    "status": "confirmed",
    "etherscan_hint": {
        "source": "etherscan",
        "etherscan": {
            "proxy_flag": true,
            "implementation": "0x0e41862deeca185f784b96a0bc44de21f0436306"
        }
    },
    "implementation": "0x0e41862deeca185f784b96a0bc44de21f0436306",
    "onchain_method": "eip1967"
}

Slither

tool.slither
Status
complete
Attempts
1 / 2
Findings
0
Solc
0.8.6 (solc-select)
View stderr
'forge clean' running (wd: /tmp/slither-ojh0l_09)
'forge config --json' running
'forge build --build-info --skip ./test/** ./script/** --force' running (wd: /tmp/slither-ojh0l_09)

CrowdfundWithEditionsProxy.constructor(address,address,string,string).logic (contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsProxy.sol#51) shadows:
	- CrowdfundWithEditionsProxy.logic() (contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsProxy.sol#75-79) (function)
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#local-variable-shadowing

CrowdfundWithEditionsProxy.constructor(address,address,string,string).operator_ (contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsProxy.sol#47) lacks a zero-check on :
		- operator = operator_ (contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsProxy.sol#68)
CrowdfundWithEditionsProxy.constructor(address,address,string,string).treasuryConfig_ (contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsProxy.sol#46) lacks a zero-check on :
		- treasuryConfig = treasuryConfig_ (contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsProxy.sol#69)
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#missing-zero-address-validation

CrowdfundWithEditionsProxy.constructor(address,address,string,string) (contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsProxy.sol#45-72) uses assembly
	- INLINE ASM (contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsProxy.sol#53-55)
CrowdfundWithEditionsProxy.logic() (contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsProxy.sol#75-79) uses assembly
	- INLINE ASM (contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsProxy.sol#76-78)
CrowdfundWithEditionsProxy.fallback() (contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsProxy.sol#81-104) uses assembly
	- INLINE ASM (contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsProxy.sol#82-103)
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#assembly-usage

Version constraint 0.8.6 contains known severe issues (https://solidity.readthedocs.io/en/latest/bugs.html)
	- VerbatimInvalidDeduplication
	- FullInlinerNonExpressionSplitArgumentEvaluationOrder
	- MissingSideEffectsOnSelectorAccess
	- AbiReencodingHeadOverflowWithStaticArrayCleanup
	- DirtyBytesArrayToStorage
	- DataLocationChangeInInternalOverride
	- NestedCalldataArrayAbiReencodingSizeValidation
	- SignedImmutables.
It is used by:
	- 0.8.6 (contracts/external/ERC20Storage.sol#2)
	- 0.8.6 (contracts/external/interface/IERC20.sol#2)
	- 0.8.6 (contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsProxy.sol#2)
	- 0.8.6 (contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsStorage.sol#2)
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-versions-of-solidity

Variable CrowdfundWithEditionsStorage.reentrancy_status (contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsStorage.sol#30) is not in mixedCase
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#conformance-to-solidity-naming-conventions

CrowdfundWithEditionsStorage.TOKEN_SCALE (contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsStorage.sol#21) is never used in CrowdfundWithEditionsProxy (contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsProxy.sol#30-107)
CrowdfundWithEditionsStorage.REENTRANCY_NOT_ENTERED (contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsStorage.sol#26) is never used in CrowdfundWithEditionsProxy (contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsProxy.sol#30-107)
CrowdfundWithEditionsStorage.REENTRANCY_ENTERED (contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsStorage.sol#27) is never used in CrowdfundWithEditionsProxy (contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsProxy.sol#30-107)
CrowdfundWithEditionsStorage.reentrancy_status (contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsStorage.sol#30) is never used in CrowdfundWithEditionsProxy (contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsProxy.sol#30-107)
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#unused-state-variable

CrowdfundWithEditionsStorage.reentrancy_status (contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsStorage.sol#30) should be constant 
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#state-variables-that-could-be-declared-constant

CrowdfundWithEditionsStorage.editions (contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsStorage.sol#58) should be immutable 
CrowdfundWithEditionsStorage.feePercentage (contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsStorage.sol#45) should be immutable 
CrowdfundWithEditionsStorage.fundingCap (contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsStorage.sol#42) should be immutable 
CrowdfundWithEditionsStorage.fundingRecipient (contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsStorage.sol#36) should be immutable 
CrowdfundWithEditionsStorage.operator (contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsStorage.sol#33) should be immutable 
CrowdfundWithEditionsStorage.operatorPercent (contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsStorage.sol#48) should be immutable 
CrowdfundWithEditionsStorage.treasuryConfig (contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsStorage.sol#39) should be immutable 
ERC20Storage.totalSupply (contracts/external/ERC20Storage.sol#16) should be immutable 
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#state-variables-that-could-be-declared-immutable
. analyzed (6 contracts with 100 detectors), 21 result(s) found
Metadata
{
    "solc": {
        "status": "ok",
        "use_rc": 0,
        "version": "0.8.6",
        "strategy": "solc-select",
        "solc_path": "/var/www/tripwire/.venv/bin/solc",
        "install_rc": 0,
        "use_stderr": null,
        "use_stdout": "Switched global version to 0.8.6",
        "install_stderr": null,
        "install_stdout": "Version '0.8.6' is already installed, skipping..."
    },
    "solc_args": "--base-path /tmp/slither-ojh0l_09 --allow-paths /tmp/slither-ojh0l_09,/tmp/slither-ojh0l_09/lib,/tmp/slither-ojh0l_09/src",
    "remappings": [],
    "source_files": [
        "contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsProxy.sol",
        "contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsStorage.sol",
        "contracts/external/ERC20Storage.sol",
        "contracts/external/interface/IERC20.sol"
    ],
    "contract_count": 0,
    "contract_names": [],
    "detector_count": 21,
    "workspace_hash": "2dd1dbd6dc7eea5b54f45718e5c4804e028f1ab3acdbbfc14123a839a4eed9be",
    "duration_seconds": 0.5152864456176758,
    "solc_version_requested": "0.8.6"
}

Codex

report.consolidate
Job Status
complete
Attempts
1 / 6
Findings
2
No findings?
no
Summary
Found two issues in the implementation: an unprotected withdraw path that lets anyone drain funds during funding, and a proxy-unsafe initializer that reverts when called via delegatecall.
View stdout
{"no_findings":false,"summary":"Found two issues in the implementation: an unprotected withdraw path that lets anyone drain funds during funding, and a proxy-unsafe initializer that reverts when called via delegatecall.","findings":[{"severity":"medium","category":"access_control","title":"Unrestricted withdraw allows premature drain during FUNDING","exploit_scenario":"Any address can call withdraw() at any time, including while status is FUNDING. This immediately transfers the entire contract balance to the treasury and fundingRecipient, leaving contributors with minted tokens but no backing ETH and bypassing the closeFunding flow (operator token minting and status progression). A griefer can force early payout and effectively break redemption guarantees.","recommendation":"Restrict withdraw to onlyOperator and/or require status == TRADING, and consider calling it only from closeFunding. Adding nonReentrant to withdraw is also prudent if external recipients are untrusted.","evidence":"withdraw() is public and unguarded, and _withdraw() transfers full balance externally without checking status.","locations":["contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsLogic.sol:151","contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsLogic.sol:254"]},{"severity":"medium","category":"upgradeability","title":"ERC20.initialize reverts when called via proxy, preventing proxy initialization","exploit_scenario":"When used behind a proxy, calling initialize() via delegatecall will make extcodesize(address()) non-zero (the proxy already has code), so initialize reverts. This prevents setting totalSupply/balances through the proxy, leaving the system uninitialized or bricked (e.g., totalSupply remains 0 and critical state may never be set).","recommendation":"Replace the extcodesize construction check with a proper initializer guard (e.g., OpenZeppelin Initializable) that is proxy-safe, or move initialization into the proxy deployment constructor/initializer logic.","evidence":"initialize() uses extcodesize(address()) to allow only constructor-time calls, which fails for proxy delegatecalls.","locations":["contracts/external/ERC20.sol:38"]}]}
View stderr
OpenAI Codex v0.77.0 (research preview)
--------
workdir: /tmp/codex-d9owvwck
model: gpt-5.2-codex
provider: openai
approval: never
sandbox: read-only
reasoning effort: high
reasoning summaries: auto
session id: 019bb00e-a4b7-7960-9508-41b7b497ec53
--------
user
Context: The user requested the proxy at 0x1b7d237406f51978d48bfcec2211c5eb97a344aa.
Audit the implementation at 0x0e41862deeca185f784b96a0bc44de21f0436306.
Findings should refer to the implementation unless explicitly about proxy mechanics.

You are a security auditor. Audit this Solidity codebase for exploitable vulnerabilities.
Focus on: reentrancy, access control, upgradeability/proxy issues, signature validation, oracle manipulation,
accounting/invariant breaks, external call patterns, unchecked return values, and dangerous delegatecall.

Scope rules:
- You MUST fully read `AUDIT_BUNDLE.txt` before concluding. It contains the complete contents of all first-party Solidity sources in this workspace.
- Treat `lib/` and `@openzeppelin/` as third-party dependencies; skip them unless there is a concrete misuse angle in first-party code.
- If there are only dependency files and no first-party sources, set no_findings=true and explain in summary.

Output MUST be valid JSON matching the provided schema.
- If you find nothing meaningful: set no_findings=true and findings=[].
- Otherwise: set no_findings=false and include findings with severity/category/title and file/line locations when possible.

Output constraints (IMPORTANT):
- severity MUST be exactly one of: info, low, medium, high, critical (lowercase).
- category MUST be exactly one of: access_control, reentrancy, math, oracle, upgradeability, auth, other.
Keep summary short and concrete.
mcp startup: no servers

thinking
**Preparing to audit bundle read-only**
exec
/bin/bash -lc ls in /tmp/codex-d9owvwck succeeded in 8ms:
AUDIT_BUNDLE.txt
codex-output-schema.json
contracts
exec
/bin/bash -lc 'cat AUDIT_BUNDLE.txt' in /tmp/codex-d9owvwck succeeded in 5ms:
This file concatenates the full contents of first-party Solidity sources for auditing.
Do not treat dependency code under lib/ or @openzeppelin/ as first-party.

================================================================================
FILE: contracts/external/ERC20.sol
================================================================================
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.6;

// import {ERC20Storage} from "./ERC20Storage.sol";
import {IERC20, IERC20Events} from "./interface/IERC20.sol";

/**
 * @title ERC20 Implementation.
 * @author MirrorXYZ
 */
contract ERC20 is IERC20, IERC20Events {
    // ============ ERC20 Attributes ============
    /// @notice EIP-20 token name for this token
    string public override name;

thinking
**Auditing fund withdrawal and reentrancy risks**

I'm carefully reviewing the contract's withdraw function, which is publicly callable without access control and can prematurely release funds during the FUNDING phase—potentially griefing contributors by draining escrowed ETH early. I'm also analyzing reentrancy concerns since withdraw lacks a reentrancy guard and makes external calls, which could allow nested calls and unintended transfers. Additionally, I'm considering initialization and upgradeability issues with the proxy pattern and how token redemption logic aligns with the funding and withdrawal flow. Next, I'll locate relevant file lines to support clear issue reporting.
exec
/bin/bash -lc 'nl -ba contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsLogic.sol' in /tmp/codex-d9owvwck succeeded in 24ms:
     1	// SPDX-License-Identifier: GPL-3.0-or-later
     2	pragma solidity 0.8.6;
     3	
     4	import {CrowdfundWithEditionsStorage} from "./CrowdfundWithEditionsStorage.sol";
     5	import {ERC20} from "../../../external/ERC20.sol";
     6	import {ICrowdfundWithEditions} from "./interface/ICrowdfundWithEditions.sol";
     7	import {ITreasuryConfig} from "../../../interface/ITreasuryConfig.sol";
     8	
     9	/**
    10	 * @title CrowdfundWithEditionsLogic
    11	 * @author MirrorXYZ
    12	 *
    13	 * Crowdfund the creation of NFTs by issuing ERC20 tokens that
    14	 * can be redeemed for the underlying value of the NFT once sold.
    15	 */
    16	contract CrowdfundWithEditionsLogic is CrowdfundWithEditionsStorage, ERC20 {
    17	    // ============ Events ============
    18	
    19	    event ReceivedERC721(uint256 tokenId, address sender);
    20	    event Contribution(address contributor, uint256 amount);

thinking
**Adding initialization lines to ERC20**
exec
/bin/bash -lc 'nl -ba contracts/external/ERC20.sol' in /tmp/codex-d9owvwck succeeded in 5ms:
     1	// SPDX-License-Identifier: GPL-3.0-or-later
     2	pragma solidity 0.8.6;
     3	
     4	// import {ERC20Storage} from "./ERC20Storage.sol";
     5	import {IERC20, IERC20Events} from "./interface/IERC20.sol";
     6	
     7	/**
     8	 * @title ERC20 Implementation.
     9	 * @author MirrorXYZ
    10	 */
    11	contract ERC20 is IERC20, IERC20Events {
    12	    // ============ ERC20 Attributes ============
    13	    /// @notice EIP-20 token name for this token
    14	    string public override name;
    15	
    16	    /// @notice EIP-20 token symbol for this token
    17	    string public override symbol;
    18	
    19	    /// @notice EIP-20 token decimals for this token
    20	    uint8 public constant override decimals = 18;

thinking
**Analyzing reentrancy and initialization vulnerabilities**

I'm mapping out subtle reentrancy risks around the withdraw function, especially how its lack of a guard can let a malicious treasury contract repeatedly claim fees, inflating what it receives. I'm also flagging a critical upgradeability trap where the ERC20.initialize guard blocks proxy initialization, potentially leaving the proxy uninitialized and bricked. Both deserve clear findings with severity and remediation suggestions.
codex
{"no_findings":false,"summary":"Found two issues in the implementation: an unprotected withdraw path that lets anyone drain funds during funding, and a proxy-unsafe initializer that reverts when called via delegatecall.","findings":[{"severity":"medium","category":"access_control","title":"Unrestricted withdraw allows premature drain during FUNDING","exploit_scenario":"Any address can call withdraw() at any time, including while status is FUNDING. This immediately transfers the entire contract balance to the treasury and fundingRecipient, leaving contributors with minted tokens but no backing ETH and bypassing the closeFunding flow (operator token minting and status progression). A griefer can force early payout and effectively break redemption guarantees.","recommendation":"Restrict withdraw to onlyOperator and/or require status == TRADING, and consider calling it only from closeFunding. Adding nonReentrant to withdraw is also prudent if external recipients are untrusted.","evidence":"withdraw() is public and unguarded, and _withdraw() transfers full balance externally without checking status.","locations":["contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsLogic.sol:151","contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsLogic.sol:254"]},{"severity":"medium","category":"upgradeability","title":"ERC20.initialize reverts when called via proxy, preventing proxy initialization","exploit_scenario":"When used behind a proxy, calling initialize() via delegatecall will make extcodesize(address()) non-zero (the proxy already has code), so initialize reverts. This prevents setting totalSupply/balances through the proxy, leaving the system uninitialized or bricked (e.g., totalSupply remains 0 and critical state may never be set).","recommendation":"Replace the extcodesize construction check with a proper initializer guard (e.g., OpenZeppelin Initializable) that is proxy-safe, or move initialization into the proxy deployment constructor/initializer logic.","evidence":"initialize() uses extcodesize(address()) to allow only constructor-time calls, which fails for proxy delegatecalls.","locations":["contracts/external/ERC20.sol:38"]}]}
tokens used
20,081
Metadata
{
    "codex": {
        "ran_at": "2026-01-12T02:37:27.034800+00:00",
        "returncode": 0
    },
    "project": {
        "mode": "source",
        "file_count": 6,
        "import_count": 6,
        "first_party_contracts_count": 6
    },
    "summary": "Found two issues in the implementation: an unprotected withdraw path that lets anyone drain funds during funding, and a proxy-unsafe initializer that reverts when called via delegatecall.",
    "raw_final": {
        "summary": "Found two issues in the implementation: an unprotected withdraw path that lets anyone drain funds during funding, and a proxy-unsafe initializer that reverts when called via delegatecall.",
        "findings": [
            {
                "title": "Unrestricted withdraw allows premature drain during FUNDING",
                "category": "access_control",
                "evidence": "withdraw() is public and unguarded, and _withdraw() transfers full balance externally without checking status.",
                "severity": "medium",
                "locations": [
                    "contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsLogic.sol:151",
                    "contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsLogic.sol:254"
                ],
                "recommendation": "Restrict withdraw to onlyOperator and/or require status == TRADING, and consider calling it only from closeFunding. Adding nonReentrant to withdraw is also prudent if external recipients are untrusted.",
                "exploit_scenario": "Any address can call withdraw() at any time, including while status is FUNDING. This immediately transfers the entire contract balance to the treasury and fundingRecipient, leaving contributors with minted tokens but no backing ETH and bypassing the closeFunding flow (operator token minting and status progression). A griefer can force early payout and effectively break redemption guarantees."
            },
            {
                "title": "ERC20.initialize reverts when called via proxy, preventing proxy initialization",
                "category": "upgradeability",
                "evidence": "initialize() uses extcodesize(address()) to allow only constructor-time calls, which fails for proxy delegatecalls.",
                "severity": "medium",
                "locations": [
                    "contracts/external/ERC20.sol:38"
                ],
                "recommendation": "Replace the extcodesize construction check with a proper initializer guard (e.g., OpenZeppelin Initializable) that is proxy-safe, or move initialization into the proxy deployment constructor/initializer logic.",
                "exploit_scenario": "When used behind a proxy, calling initialize() via delegatecall will make extcodesize(address()) non-zero (the proxy already has code), so initialize reverts. This prevents setting totalSupply/balances through the proxy, leaving the system uninitialized or bricked (e.g., totalSupply remains 0 and critical state may never be set)."
            }
        ],
        "no_findings": false
    },
    "no_findings": false,
    "proxy_context": {
        "proxy_run_id": "019bb004-7ab1-705e-a818-96d48a80f311",
        "proxy_address": "0x1b7d237406f51978d48bfcec2211c5eb97a344aa",
        "implementation_run_id": "019bb004-9839-71fb-bf53-cdb271c42b17",
        "implementation_address": "0x0e41862deeca185f784b96a0bc44de21f0436306"
    },
    "schema_version": 1
}

Opcode Heuristics

tool.cast_disasm
Delegatecall
1
Selfdestruct
0
CREATE2
0
CALL-family (heavy)
0
EXT*/BALANCE
0
Total opcodes
646
Flags
delegatecall_present
View cast disassembly output
00000000: PUSH1 0x80
00000002: PUSH1 0x40
00000004: MSTORE
00000005: PUSH1 0x04
00000007: CALLDATASIZE
00000008: LT
00000009: PUSH2 0x00cb
0000000c: JUMPI
0000000d: PUSH1 0x00
0000000f: CALLDATALOAD
00000010: PUSH1 0xe0
00000012: SHR
00000013: DUP1
00000014: PUSH4 0x95d89b41
00000019: GT
0000001a: PUSH2 0x0074
0000001d: JUMPI
0000001e: DUP1
0000001f: PUSH4 0xcfd7b0df
00000024: GT
00000025: PUSH2 0x004e
00000028: JUMPI
00000029: DUP1
0000002a: PUSH4 0xcfd7b0df
0000002f: EQ
00000030: PUSH2 0x0280
00000033: JUMPI
00000034: DUP1
00000035: PUSH4 0xd7dfa0dd
0000003a: EQ
0000003b: PUSH2 0x02ad
0000003e: JUMPI
0000003f: DUP1
00000040: PUSH4 0xe3b2594f
00000045: EQ
00000046: PUSH2 0x02e1
00000049: JUMPI
0000004a: PUSH2 0x00d2
0000004d: JUMP
0000004e: JUMPDEST
0000004f: DUP1
00000050: PUSH4 0x95d89b41
00000055: EQ
00000056: PUSH2 0x0223
00000059: JUMPI
0000005a: DUP1
0000005b: PUSH4 0xa001ecdd
00000060: EQ
00000061: PUSH2 0x0238
00000064: JUMPI
00000065: DUP1
00000066: PUSH4 0xb8ddbcb3
0000006b: EQ
0000006c: PUSH2 0x024e
0000006f: JUMPI
00000070: PUSH2 0x00d2
00000073: JUMP
00000074: JUMPDEST
00000075: DUP1
00000076: PUSH4 0x200d2ed2
0000007b: GT
0000007c: PUSH2 0x00a5
0000007f: JUMPI
00000080: DUP1
00000081: PUSH4 0x200d2ed2
00000086: EQ
00000087: PUSH2 0x01b9
0000008a: JUMPI
0000008b: DUP1
0000008c: PUSH4 0x570ca735
00000091: EQ
00000092: PUSH2 0x01e0
00000095: JUMPI
00000096: DUP1
00000097: PUSH4 0x7b4044a0
0000009c: EQ
0000009d: PUSH2 0x020d
000000a0: JUMPI
000000a1: PUSH2 0x00d2
000000a4: JUMP
000000a5: JUMPDEST
000000a6: DUP1
000000a7: PUSH4 0x06fdde03
000000ac: EQ
000000ad: PUSH2 0x0118
000000b0: JUMPI
000000b1: DUP1
000000b2: PUSH4 0x18160ddd
000000b7: EQ
000000b8: PUSH2 0x0143
000000bb: JUMPI
000000bc: DUP1
000000bd: PUSH4 0x1bb534ba
000000c2: EQ
000000c3: PUSH2 0x0167
000000c6: JUMPI
000000c7: PUSH2 0x00d2
000000ca: JUMP
000000cb: JUMPDEST
000000cc: CALLDATASIZE
000000cd: PUSH2 0x00d2
000000d0: JUMPI
000000d1: STOP
000000d2: JUMPDEST
000000d3: PUSH1 0x40
000000d5: MLOAD
000000d6: CALLDATASIZE
000000d7: PUSH1 0x00
000000d9: DUP3
000000da: CALLDATACOPY
000000db: PUSH1 0x00
000000dd: DUP1
000000de: CALLDATASIZE
000000df: DUP4
000000e0: PUSH32 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc
00000101: SLOAD
00000102: GAS
00000103: DELEGATECALL
00000104: RETURNDATASIZE
00000105: DUP1
00000106: PUSH1 0x00
00000108: DUP5
00000109: RETURNDATACOPY
0000010a: DUP2
0000010b: DUP1
0000010c: ISZERO
0000010d: PUSH2 0x0114
00000110: JUMPI
00000111: DUP2
00000112: DUP5
00000113: RETURN
00000114: JUMPDEST
00000115: DUP2
00000116: DUP5
00000117: REVERT
00000118: JUMPDEST
00000119: CALLVALUE
0000011a: DUP1
0000011b: ISZERO
0000011c: PUSH2 0x0124
0000011f: JUMPI
00000120: PUSH1 0x00
00000122: DUP1
00000123: REVERT
00000124: JUMPDEST
00000125: POP
00000126: PUSH2 0x012d
00000129: PUSH2 0x02f7
0000012c: JUMP
0000012d: JUMPDEST
0000012e: PUSH1 0x40
00000130: MLOAD
00000131: PUSH2 0x013a
00000134: SWAP2
00000135: SWAP1
00000136: PUSH2 0x03d3
00000139: JUMP
0000013a: JUMPDEST
0000013b: PUSH1 0x40
0000013d: MLOAD
0000013e: DUP1
0000013f: SWAP2
00000140: SUB
00000141: SWAP1
00000142: RETURN
00000143: JUMPDEST
00000144: CALLVALUE
00000145: DUP1
00000146: ISZERO
00000147: PUSH2 0x014f
0000014a: JUMPI
0000014b: PUSH1 0x00
0000014d: DUP1
0000014e: REVERT
0000014f: JUMPDEST
00000150: POP
00000151: PUSH2 0x0159
00000154: PUSH1 0x0a
00000156: SLOAD
00000157: DUP2
00000158: JUMP
00000159: JUMPDEST
0000015a: PUSH1 0x40
0000015c: MLOAD
0000015d: SWAP1
0000015e: DUP2
0000015f: MSTORE
00000160: PUSH1 0x20
00000162: ADD
00000163: PUSH2 0x013a
00000166: JUMP
00000167: JUMPDEST
00000168: CALLVALUE
00000169: DUP1
0000016a: ISZERO
0000016b: PUSH2 0x0173
0000016e: JUMPI
0000016f: PUSH1 0x00
00000171: DUP1
00000172: REVERT
00000173: JUMPDEST
00000174: POP
00000175: PUSH1 0x02
00000177: SLOAD
00000178: PUSH2 0x0194
0000017b: SWAP1
0000017c: PUSH20 0xffffffffffffffffffffffffffffffffffffffff
00000191: AND
00000192: DUP2
00000193: JUMP
00000194: JUMPDEST
00000195: PUSH1 0x40
00000197: MLOAD
00000198: PUSH20 0xffffffffffffffffffffffffffffffffffffffff
000001ad: SWAP1
000001ae: SWAP2
000001af: AND
000001b0: DUP2
000001b1: MSTORE
000001b2: PUSH1 0x20
000001b4: ADD
000001b5: PUSH2 0x013a
000001b8: JUMP
000001b9: JUMPDEST
000001ba: CALLVALUE
000001bb: DUP1
000001bc: ISZERO
000001bd: PUSH2 0x01c5
000001c0: JUMPI
000001c1: PUSH1 0x00
000001c3: DUP1
000001c4: REVERT
000001c5: JUMPDEST
000001c6: POP
000001c7: PUSH1 0x07
000001c9: SLOAD
000001ca: PUSH2 0x01d3
000001cd: SWAP1
000001ce: PUSH1 0xff
000001d0: AND
000001d1: DUP2
000001d2: JUMP
000001d3: JUMPDEST
000001d4: PUSH1 0x40
000001d6: MLOAD
000001d7: PUSH2 0x013a
000001da: SWAP2
000001db: SWAP1
000001dc: PUSH2 0x0392
000001df: JUMP
000001e0: JUMPDEST
000001e1: CALLVALUE
000001e2: DUP1
000001e3: ISZERO
000001e4: PUSH2 0x01ec
000001e7: JUMPI
000001e8: PUSH1 0x00
000001ea: DUP1
000001eb: REVERT
000001ec: JUMPDEST
000001ed: POP
000001ee: PUSH1 0x01
000001f0: SLOAD
000001f1: PUSH2 0x0194
000001f4: SWAP1
000001f5: PUSH20 0xffffffffffffffffffffffffffffffffffffffff
0000020a: AND
0000020b: DUP2
0000020c: JUMP
0000020d: JUMPDEST
0000020e: CALLVALUE
0000020f: DUP1
00000210: ISZERO
00000211: PUSH2 0x0219
00000214: JUMPI
00000215: PUSH1 0x00
00000217: DUP1
00000218: REVERT
00000219: JUMPDEST
0000021a: POP
0000021b: PUSH2 0x0159
0000021e: PUSH1 0x06
00000220: SLOAD
00000221: DUP2
00000222: JUMP
00000223: JUMPDEST
00000224: CALLVALUE
00000225: DUP1
00000226: ISZERO
00000227: PUSH2 0x022f
0000022a: JUMPI
0000022b: PUSH1 0x00
0000022d: DUP1
0000022e: REVERT
0000022f: JUMPDEST
00000230: POP
00000231: PUSH2 0x012d
00000234: PUSH2 0x0385
00000237: JUMP
00000238: JUMPDEST
00000239: CALLVALUE
0000023a: DUP1
0000023b: ISZERO
0000023c: PUSH2 0x0244
0000023f: JUMPI
00000240: PUSH1 0x00
00000242: DUP1
00000243: REVERT
00000244: JUMPDEST
00000245: POP
00000246: PUSH2 0x0159
00000249: PUSH1 0x05
0000024b: SLOAD
0000024c: DUP2
0000024d: JUMP
0000024e: JUMPDEST
0000024f: CALLVALUE
00000250: DUP1
00000251: ISZERO
00000252: PUSH2 0x025a
00000255: JUMPI
00000256: PUSH1 0x00
00000258: DUP1
00000259: REVERT
0000025a: JUMPDEST
0000025b: POP
0000025c: PUSH1 0x07
0000025e: SLOAD
0000025f: PUSH2 0x0194
00000262: SWAP1
00000263: PUSH2 0x0100
00000266: SWAP1
00000267: DIV
00000268: PUSH20 0xffffffffffffffffffffffffffffffffffffffff
0000027d: AND
0000027e: DUP2
0000027f: JUMP
00000280: JUMPDEST
00000281: CALLVALUE
00000282: DUP1
00000283: ISZERO
00000284: PUSH2 0x028c
00000287: JUMPI
00000288: PUSH1 0x00
0000028a: DUP1
0000028b: REVERT
0000028c: JUMPDEST
0000028d: POP
0000028e: PUSH1 0x03
00000290: SLOAD
00000291: PUSH2 0x0194
00000294: SWAP1
00000295: PUSH20 0xffffffffffffffffffffffffffffffffffffffff
000002aa: AND
000002ab: DUP2
000002ac: JUMP
000002ad: JUMPDEST
000002ae: CALLVALUE
000002af: DUP1
000002b0: ISZERO
000002b1: PUSH2 0x02b9
000002b4: JUMPI
000002b5: PUSH1 0x00
000002b7: DUP1
000002b8: REVERT
000002b9: JUMPDEST
000002ba: POP
000002bb: PUSH32 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc
000002dc: SLOAD
000002dd: PUSH2 0x0194
000002e0: JUMP
000002e1: JUMPDEST
000002e2: CALLVALUE
000002e3: DUP1
000002e4: ISZERO
000002e5: PUSH2 0x02ed
000002e8: JUMPI
000002e9: PUSH1 0x00
000002eb: DUP1
000002ec: REVERT
000002ed: JUMPDEST
000002ee: POP
000002ef: PUSH2 0x0159
000002f2: PUSH1 0x04
000002f4: SLOAD
000002f5: DUP2
000002f6: JUMP
000002f7: JUMPDEST
000002f8: PUSH1 0x08
000002fa: DUP1
000002fb: SLOAD
000002fc: PUSH2 0x0304
000002ff: SWAP1
00000300: PUSH2 0x0446
00000303: JUMP
00000304: JUMPDEST
00000305: DUP1
00000306: PUSH1 0x1f
00000308: ADD
00000309: PUSH1 0x20
0000030b: DUP1
0000030c: SWAP2
0000030d: DIV
0000030e: MUL
0000030f: PUSH1 0x20
00000311: ADD
00000312: PUSH1 0x40
00000314: MLOAD
00000315: SWAP1
00000316: DUP2
00000317: ADD
00000318: PUSH1 0x40
0000031a: MSTORE
0000031b: DUP1
0000031c: SWAP3
0000031d: SWAP2
0000031e: SWAP1
0000031f: DUP2
00000320: DUP2
00000321: MSTORE
00000322: PUSH1 0x20
00000324: ADD
00000325: DUP3
00000326: DUP1
00000327: SLOAD
00000328: PUSH2 0x0330
0000032b: SWAP1
0000032c: PUSH2 0x0446
0000032f: JUMP
00000330: JUMPDEST
00000331: DUP1
00000332: ISZERO
00000333: PUSH2 0x037d
00000336: JUMPI
00000337: DUP1
00000338: PUSH1 0x1f
0000033a: LT
0000033b: PUSH2 0x0352
0000033e: JUMPI
0000033f: PUSH2 0x0100
00000342: DUP1
00000343: DUP4
00000344: SLOAD
00000345: DIV
00000346: MUL
00000347: DUP4
00000348: MSTORE
00000349: SWAP2
0000034a: PUSH1 0x20
0000034c: ADD
0000034d: SWAP2
0000034e: PUSH2 0x037d
00000351: JUMP
00000352: JUMPDEST
00000353: DUP3
00000354: ADD
00000355: SWAP2
00000356: SWAP1
00000357: PUSH1 0x00
00000359: MSTORE
0000035a: PUSH1 0x20
0000035c: PUSH1 0x00
0000035e: KECCAK256
0000035f: SWAP1
00000360: JUMPDEST
00000361: DUP2
00000362: SLOAD
00000363: DUP2
00000364: MSTORE
00000365: SWAP1
00000366: PUSH1 0x01
00000368: ADD
00000369: SWAP1
0000036a: PUSH1 0x20
0000036c: ADD
0000036d: DUP1
0000036e: DUP4
0000036f: GT
00000370: PUSH2 0x0360
00000373: JUMPI
00000374: DUP3
00000375: SWAP1
00000376: SUB
00000377: PUSH1 0x1f
00000379: AND
0000037a: DUP3
0000037b: ADD
0000037c: SWAP2
0000037d: JUMPDEST
0000037e: POP
0000037f: POP
00000380: POP
00000381: POP
00000382: POP
00000383: DUP2
00000384: JUMP
00000385: JUMPDEST
00000386: PUSH1 0x09
00000388: DUP1
00000389: SLOAD
0000038a: PUSH2 0x0304
0000038d: SWAP1
0000038e: PUSH2 0x0446
00000391: JUMP
00000392: JUMPDEST
00000393: PUSH1 0x20
00000395: DUP2
00000396: ADD
00000397: PUSH1 0x02
00000399: DUP4
0000039a: LT
0000039b: PUSH2 0x03cd
0000039e: JUMPI
0000039f: PUSH32 0x4e487b7100000000000000000000000000000000000000000000000000000000
000003c0: PUSH1 0x00
000003c2: MSTORE
000003c3: PUSH1 0x21
000003c5: PUSH1 0x04
000003c7: MSTORE
000003c8: PUSH1 0x24
000003ca: PUSH1 0x00
000003cc: REVERT
000003cd: JUMPDEST
000003ce: SWAP2
000003cf: SWAP1
000003d0: MSTORE
000003d1: SWAP1
000003d2: JUMP
000003d3: JUMPDEST
000003d4: PUSH1 0x00
000003d6: PUSH1 0x20
000003d8: DUP1
000003d9: DUP4
000003da: MSTORE
000003db: DUP4
000003dc: MLOAD
000003dd: DUP1
000003de: DUP3
000003df: DUP6
000003e0: ADD
000003e1: MSTORE
000003e2: PUSH1 0x00
000003e4: JUMPDEST
000003e5: DUP2
000003e6: DUP2
000003e7: LT
000003e8: ISZERO
000003e9: PUSH2 0x0400
000003ec: JUMPI
000003ed: DUP6
000003ee: DUP2
000003ef: ADD
000003f0: DUP4
000003f1: ADD
000003f2: MLOAD
000003f3: DUP6
000003f4: DUP3
000003f5: ADD
000003f6: PUSH1 0x40
000003f8: ADD
000003f9: MSTORE
000003fa: DUP3
000003fb: ADD
000003fc: PUSH2 0x03e4
000003ff: JUMP
00000400: JUMPDEST
00000401: DUP2
00000402: DUP2
00000403: GT
00000404: ISZERO
00000405: PUSH2 0x0412
00000408: JUMPI
00000409: PUSH1 0x00
0000040b: PUSH1 0x40
0000040d: DUP4
0000040e: DUP8
0000040f: ADD
00000410: ADD
00000411: MSTORE
00000412: JUMPDEST
00000413: POP
00000414: PUSH1 0x1f
00000416: ADD
00000417: PUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0
00000438: AND
00000439: SWAP3
0000043a: SWAP1
0000043b: SWAP3
0000043c: ADD
0000043d: PUSH1 0x40
0000043f: ADD
00000440: SWAP4
00000441: SWAP3
00000442: POP
00000443: POP
00000444: POP
00000445: JUMP
00000446: JUMPDEST
00000447: PUSH1 0x01
00000449: DUP2
0000044a: DUP2
0000044b: SHR
0000044c: SWAP1
0000044d: DUP3
0000044e: AND
0000044f: DUP1
00000450: PUSH2 0x045a
00000453: JUMPI
00000454: PUSH1 0x7f
00000456: DUP3
00000457: AND
00000458: SWAP2
00000459: POP
0000045a: JUMPDEST
0000045b: PUSH1 0x20
0000045d: DUP3
0000045e: LT
0000045f: DUP2
00000460: EQ
00000461: ISZERO
00000462: PUSH2 0x0494
00000465: JUMPI
00000466: PUSH32 0x4e487b7100000000000000000000000000000000000000000000000000000000
00000487: PUSH1 0x00
00000489: MSTORE
0000048a: PUSH1 0x22
0000048c: PUSH1 0x04
0000048e: MSTORE
0000048f: PUSH1 0x24
00000491: PUSH1 0x00
00000493: REVERT
00000494: JUMPDEST
00000495: POP
00000496: SWAP2
00000497: SWAP1
00000498: POP
00000499: JUMP
0000049a: INVALID
0000049b: LOG2
0000049c: PUSH5 0x6970667358
000004a2: UNKNOWN(0x22)
000004a3: SLT
000004a4: KECCAK256
000004a5: PUSH22 0xe2c11b94fd8bff7467563877afbbd5b54be285e95731
000004bc: SDIV
000004bd: PUSH22

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: 0xcc1d8f4300ffa6091ecfb4f1030b7bf6df2f8217ebb56f212503ec8444bec76a
validation_json
{
    "sink": "DELEGATECALL",
    "errors": 0,
    "status": "unknown",
    "attempts": 144,
    "trace_id": "0xcc1d8f4300ffa6091ecfb4f1030b7bf6df2f8217ebb56f212503ec8444bec76a",
    "confirmed": false,
    "trace_mode": "callTracer",
    "call_targets": null,
    "matched_probe": null,
    "target_varies": null,
    "classification": "no_sink"
}
witness_json
{
    "notes": "heuristic witness; entrypoints are best-effort selectors with zeroed calldata",
    "constraints": [],
    "entrypoints": [
        {
            "calldata": "0xcfd7b0df",
            "selector": "0xcfd7b0df",
            "calldata_variants": [
                "0xcfd7b0df",
                "0xcfd7b0df0000000000000000000000000000000000000000000000000000000000000000"
            ]
        },
        {
            "calldata": "0xd7dfa0dd",
            "selector": "0xd7dfa0dd",
            "calldata_variants": [
                "0xd7dfa0dd",
                "0xd7dfa0dd0000000000000000000000000000000000000000000000000000000000000000"
            ]
        },
        {
            "calldata": "0xe3b2594f",
            "selector": "0xe3b2594f",
            "calldata_variants": [
                "0xe3b2594f",
                "0xe3b2594f0000000000000000000000000000000000000000000000000000000000000000"
            ]
        },
        {
            "calldata": "0x95d89b41",
            "selector": "0x95d89b41",
            "calldata_variants": [
                "0x95d89b41",
                "0x95d89b410000000000000000000000000000000000000000000000000000000000000000"
            ]
        },
        {
            "calldata": "0xa001ecdd",
            "selector": "0xa001ecdd",
            "calldata_variants": [
                "0xa001ecdd",
                "0xa001ecdd0000000000000000000000000000000000000000000000000000000000000000"
            ]
        },
        {
            "calldata": "0xb8ddbcb3",
            "selector": "0xb8ddbcb3",
            "calldata_variants": [
                "0xb8ddbcb3",
                "0xb8ddbcb30000000000000000000000000000000000000000000000000000000000000000"
            ]
        },
        {
            "calldata": "0x200d2ed2",
            "selector": "0x200d2ed2",
            "calldata_variants": [
                "0x200d2ed2",
                "0x200d2ed20000000000000000000000000000000000000000000000000000000000000000"
            ]
        },
        {
            "calldata": "0x570ca735",
            "selector": "0x570ca735",
            "calldata_variants": [
                "0x570ca735",
                "0x570ca7350000000000000000000000000000000000000000000000000000000000000000"
            ]
        }
    ]
}
evidence_json
{
    "examples": [
        {
            "pc": 259,
            "opcode": "DELEGATECALL",
            "returndata_checked": null,
            "value_expression_category": "n/a",
            "target_expression_category": "computed"
        }
    ],
    "guards_detected": [],
    "delegatecall_count": 1,
    "untrusted_delegatecall_count": 1
}
medium access_control codex Unrestricted withdraw allows premature drain during FUNDING 75 no
view
evidence_json
{
    "evidence": "withdraw() is public and unguarded, and _withdraw() transfers full balance externally without checking status.",
    "locations": [
        "contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsLogic.sol:151",
        "contracts/producers/crowdfunds/crowdfund-with-editions/CrowdfundWithEditionsLogic.sol:254"
    ],
    "recommendation": "Restrict withdraw to onlyOperator and/or require status == TRADING, and consider calling it only from closeFunding. Adding nonReentrant to withdraw is also prudent if external recipients are untrusted.",
    "exploit_scenario": "Any address can call withdraw() at any time, including while status is FUNDING. This immediately transfers the entire contract balance to the treasury and fundingRecipient, leaving contributors with minted tokens but no backing ETH and bypassing the closeFunding flow (operator token minting and status progression). A griefer can force early payout and effectively break redemption guarantees."
}
medium upgradeability codex ERC20.initialize reverts when called via proxy, preventing proxy initialization 75 no
view
evidence_json
{
    "evidence": "initialize() uses extcodesize(address()) to allow only constructor-time calls, which fails for proxy delegatecalls.",
    "locations": [
        "contracts/external/ERC20.sol:38"
    ],
    "recommendation": "Replace the extcodesize construction check with a proper initializer guard (e.g., OpenZeppelin Initializable) that is proxy-safe, or move initialization into the proxy deployment constructor/initializer logic.",
    "exploit_scenario": "When used behind a proxy, calling initialize() via delegatecall will make extcodesize(address()) non-zero (the proxy already has code), so initialize reverts. This prevents setting totalSupply/balances through the proxy, leaving the system uninitialized or bricked (e.g., totalSupply remains 0 and critical state may never be set)."
}
medium upgradeability cast DELEGATECALL present 80 no
view
evidence_json
{
    "notes": "delegatecall enables execution in caller context; common in proxies/upgradeable patterns",
    "delegatecall_count": 1
}