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

Analysis Run

019c0ea9-094d-71d5-afa7-e616ff0989ab

complete
Chain ID
1
Address
Block Number
Created
Fri, Jan 30, 2026 11:28 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
42040 artifact.fetch complete 1 / 3 2 months ago
42041 proxy.resolve complete 1 / 3 2 months ago
42042 tool.cast_disasm complete 1 / 3 2 months ago
42043 analysis.bundle complete 1 / 3 2 months ago
42044 capability.graph complete 1 / 3 2 months ago
42045 detector.run complete 1 / 3 2 months ago
42046 validation.fork complete 1 / 3 2 months ago
42429 report.consolidate complete 1 / 6 2 months ago
42430 tool.slither complete 1 / 2 2 months ago

Artifact

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

Slither

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

SimpleERC20Escrow.pay(address,uint256) (contracts/Contract.sol#34-38) ignores return value by token.transfer(recipient,amount) (contracts/Contract.sol#37-38)
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#unchecked-transfer

Version constraint ^0.8.13 contains known severe issues (https://solidity.readthedocs.io/en/latest/bugs.html)
	- VerbatimInvalidDeduplication
	- FullInlinerNonExpressionSplitArgumentEvaluationOrder
	- MissingSideEffectsOnSelectorAccess
	- StorageWriteRemovalBeforeConditionalTermination
	- AbiReencodingHeadOverflowWithStaticArrayCleanup
	- DirtyBytesArrayToStorage
	- InlineAssemblyMemorySideEffects
	- DataLocationChangeInInternalOverride
	- NestedCalldataArrayAbiReencodingSizeValidation.
It is used by:
	- ^0.8.13 (contracts/Contract.sol#1-2)
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-versions-of-solidity

Parameter SimpleERC20Escrow.initialize(IERC20,address)._token (contracts/Contract.sol#25) is not in mixedCase
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#conformance-to-solidity-naming-conventions
. analyzed (2 contracts with 100 detectors), 3 result(s) found
Metadata
{
    "solc": {
        "status": "ok",
        "use_rc": 0,
        "version": "0.8.13",
        "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.13",
        "install_stderr": null,
        "install_stdout": "Version '0.8.13' is already installed, skipping..."
    },
    "solc_args": "--base-path /tmp/slither-qf3k999l --allow-paths /tmp/slither-qf3k999l,/tmp/slither-qf3k999l/lib,/tmp/slither-qf3k999l/src",
    "remappings": [],
    "source_files": [
        "contracts/Contract.sol"
    ],
    "contract_count": 0,
    "contract_names": [],
    "detector_count": 3,
    "workspace_hash": "ac180ace9007f3d65608af4ec47c99752171c820406ab397107213d9551b5709",
    "duration_seconds": 0.3736457824707031,
    "solc_version_requested": "0.8.13"
}

Codex

report.consolidate
Job Status
complete
Attempts
1 / 6
Findings
2
No findings?
no
Summary
Found an unprotected initializer that can be seized if the proxy is not initialized immediately, plus an unchecked ERC20 transfer return that can silently fail on non-reverting tokens.
View stdout
{"no_findings":false,"summary":"Found an unprotected initializer that can be seized if the proxy is not initialized immediately, plus an unchecked ERC20 transfer return that can silently fail on non-reverting tokens.","findings":[{"severity":"high","category":"access_control","title":"Unprotected initialize allows hostile takeover of escrow market","exploit_scenario":"If the proxy is deployed and not initialized in the same transaction, any account can call initialize first and set themselves as market. They can then call pay to move any escrowed tokens to arbitrary recipients once funds arrive.","recommendation":"Restrict initialize to a trusted deployer/factory or pass the expected market address and require msg.sender to be that address; use an initializer guard (e.g., OpenZeppelin Initializable) and initialize atomically during proxy deployment.","evidence":"initialize is public and only guarded by market == address(0); it sets market = msg.sender.","locations":["contracts/Contract.sol:25"]},{"severity":"low","category":"other","title":"ERC20 transfer return value ignored","exploit_scenario":"For ERC20s that return false instead of reverting, pay will succeed without transferring funds, causing the caller to believe payment was made and potentially breaking accounting in the wider system.","recommendation":"Check the return value or use SafeERC20.safeTransfer to ensure failures revert.","evidence":"token.transfer(recipient, amount) is called without checking the returned bool.","locations":["contracts/Contract.sol:36"]}]}
View stderr
OpenAI Codex v0.77.0 (research preview)
--------
workdir: /tmp/codex-yjf2k5u3
model: gpt-5.2-codex
provider: openai
approval: never
sandbox: read-only
reasoning effort: high
reasoning summaries: auto
session id: 019c0f6e-fd50-7a73-bdcf-631d80113222
--------
user
Context: The user requested the proxy at 0xd01159af63c39ef4de125ac3baa940893e8009cd.
Audit the implementation at 0xc06053fcad0a0df7cc32289a135bbea9030c010f.
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
exec
/bin/bash -lc ls in /tmp/codex-yjf2k5u3 succeeded in 6ms:
AUDIT_BUNDLE.txt
codex-output-schema.json
contracts
exec
/bin/bash -lc 'cat AUDIT_BUNDLE.txt' in /tmp/codex-yjf2k5u3 succeeded in 18ms:
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/Contract.sol
================================================================================
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

/// @dev Caution: We assume all failed transfers cause reverts and ignore the returned bool.
interface IERC20 {
    function transfer(address,uint) external returns (bool);
    function transferFrom(address,address,uint) external returns (bool);
    function balanceOf(address) external view returns (uint);
}

/**
@title Simple ERC20 Escrow
@notice Collateral is stored in unique escrow contracts for every user and every market.
@dev Caution: This is a proxy implementation. Follow proxy pattern best practices
exec
/bin/bash -lc 'nl -ba contracts/Contract.sol' in /tmp/codex-yjf2k5u3 succeeded in 7ms:
     1	// SPDX-License-Identifier: UNLICENSED
     2	pragma solidity ^0.8.13;
     3	
     4	/// @dev Caution: We assume all failed transfers cause reverts and ignore the returned bool.
     5	interface IERC20 {
     6	    function transfer(address,uint) external returns (bool);
     7	    function transferFrom(address,address,uint) external returns (bool);
     8	    function balanceOf(address) external view returns (uint);
     9	}
    10	
    11	/**
    12	@title Simple ERC20 Escrow
    13	@notice Collateral is stored in unique escrow contracts for every user and every market.
    14	@dev Caution: This is a proxy implementation. Follow proxy pattern best practices
    15	*/
    16	contract SimpleERC20Escrow {
    17	    address public market;
    18	    IERC20 public token;
    19	    
    20	    /**
codex
{"no_findings":false,"summary":"Found an unprotected initializer that can be seized if the proxy is not initialized immediately, plus an unchecked ERC20 transfer return that can silently fail on non-reverting tokens.","findings":[{"severity":"high","category":"access_control","title":"Unprotected initialize allows hostile takeover of escrow market","exploit_scenario":"If the proxy is deployed and not initialized in the same transaction, any account can call initialize first and set themselves as market. They can then call pay to move any escrowed tokens to arbitrary recipients once funds arrive.","recommendation":"Restrict initialize to a trusted deployer/factory or pass the expected market address and require msg.sender to be that address; use an initializer guard (e.g., OpenZeppelin Initializable) and initialize atomically during proxy deployment.","evidence":"initialize is public and only guarded by market == address(0); it sets market = msg.sender.","locations":["contracts/Contract.sol:25"]},{"severity":"low","category":"other","title":"ERC20 transfer return value ignored","exploit_scenario":"For ERC20s that return false instead of reverting, pay will succeed without transferring funds, causing the caller to believe payment was made and potentially breaking accounting in the wider system.","recommendation":"Check the return value or use SafeERC20.safeTransfer to ensure failures revert.","evidence":"token.transfer(recipient, amount) is called without checking the returned bool.","locations":["contracts/Contract.sol:36"]}]}
tokens used
3,973
Metadata
{
    "codex": {
        "ran_at": "2026-01-30T15:04:45.397737+00:00",
        "returncode": 0
    },
    "project": {
        "mode": "source",
        "file_count": 1,
        "import_count": 0,
        "first_party_contracts_count": 1
    },
    "summary": "Found an unprotected initializer that can be seized if the proxy is not initialized immediately, plus an unchecked ERC20 transfer return that can silently fail on non-reverting tokens.",
    "raw_final": {
        "summary": "Found an unprotected initializer that can be seized if the proxy is not initialized immediately, plus an unchecked ERC20 transfer return that can silently fail on non-reverting tokens.",
        "findings": [
            {
                "title": "Unprotected initialize allows hostile takeover of escrow market",
                "category": "access_control",
                "evidence": "initialize is public and only guarded by market == address(0); it sets market = msg.sender.",
                "severity": "high",
                "locations": [
                    "contracts/Contract.sol:25"
                ],
                "recommendation": "Restrict initialize to a trusted deployer/factory or pass the expected market address and require msg.sender to be that address; use an initializer guard (e.g., OpenZeppelin Initializable) and initialize atomically during proxy deployment.",
                "exploit_scenario": "If the proxy is deployed and not initialized in the same transaction, any account can call initialize first and set themselves as market. They can then call pay to move any escrowed tokens to arbitrary recipients once funds arrive."
            },
            {
                "title": "ERC20 transfer return value ignored",
                "category": "other",
                "evidence": "token.transfer(recipient, amount) is called without checking the returned bool.",
                "severity": "low",
                "locations": [
                    "contracts/Contract.sol:36"
                ],
                "recommendation": "Check the return value or use SafeERC20.safeTransfer to ensure failures revert.",
                "exploit_scenario": "For ERC20s that return false instead of reverting, pay will succeed without transferring funds, causing the caller to believe payment was made and potentially breaking accounting in the wider system."
            }
        ],
        "no_findings": false
    },
    "no_findings": false,
    "proxy_context": {
        "proxy_run_id": "019c0ea9-094d-71d5-afa7-e616ff0989ab",
        "proxy_address": "0xd01159af63c39ef4de125ac3baa940893e8009cd",
        "implementation_run_id": "019c0eab-909f-728c-9614-1375a52b5654",
        "implementation_address": "0xc06053fcad0a0df7cc32289a135bbea9030c010f"
    },
    "schema_version": 1
}

Opcode Heuristics

tool.cast_disasm
Delegatecall
1
Selfdestruct
0
CREATE2
0
CALL-family (heavy)
0
EXT*/BALANCE
0
Total opcodes
24
Flags
delegatecall_present
View cast disassembly output
00000000: CALLDATASIZE
00000001: RETURNDATASIZE
00000002: RETURNDATASIZE
00000003: CALLDATACOPY
00000004: RETURNDATASIZE
00000005: RETURNDATASIZE
00000006: RETURNDATASIZE
00000007: CALLDATASIZE
00000008: RETURNDATASIZE
00000009: PUSH20 0xc06053fcad0a0df7cc32289a135bbea9030c010f
0000001e: GAS
0000001f: DELEGATECALL
00000020: RETURNDATASIZE
00000021: DUP3
00000022: DUP1
00000023: RETURNDATACOPY
00000024: SWAP1
00000025: RETURNDATASIZE
00000026: SWAP2
00000027: PUSH1 0x2b
00000029: JUMPI
0000002a: REVERT
0000002b: JUMPDEST
0000002c: RETURN

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: 0x4244f6e5ee4c476937fc87b2b83250d5853835cb7f149d4c2ac181f46c784323
call_targets: 0xc06053fcad0a0df7cc32289a135bbea9030c010f
target_varies: no
classification: constant_target
validation_json
{
    "sink": "DELEGATECALL",
    "errors": 0,
    "status": "sink_reached",
    "attempts": 1,
    "trace_id": "0x4244f6e5ee4c476937fc87b2b83250d5853835cb7f149d4c2ac181f46c784323",
    "confirmed": false,
    "trace_mode": "callTracer",
    "call_targets": [
        "0xc06053fcad0a0df7cc32289a135bbea9030c010f"
    ],
    "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": 31,
            "opcode": "DELEGATECALL",
            "returndata_checked": null,
            "value_expression_category": "n/a",
            "target_expression_category": "computed"
        }
    ],
    "guards_detected": [],
    "delegatecall_count": 1,
    "untrusted_delegatecall_count": 1
}
high access_control codex Unprotected initialize allows hostile takeover of escrow market 85 no
view
evidence_json
{
    "evidence": "initialize is public and only guarded by market == address(0); it sets market = msg.sender.",
    "locations": [
        "contracts/Contract.sol:25"
    ],
    "recommendation": "Restrict initialize to a trusted deployer/factory or pass the expected market address and require msg.sender to be that address; use an initializer guard (e.g., OpenZeppelin Initializable) and initialize atomically during proxy deployment.",
    "exploit_scenario": "If the proxy is deployed and not initialized in the same transaction, any account can call initialize first and set themselves as market. They can then call pay to move any escrowed tokens to arbitrary recipients once funds arrive."
}
high other slither SimpleERC20Escrow.pay(address,uint256) (contracts/Contract.sol#34-38) ignores return value by token.transfer(recipient,amount) (contracts/Contract.sol#37-38) 85 no
view
evidence_json
{
    "impact": "High",
    "detector": "unchecked-transfer",
    "elements": [
        {
            "name": "pay",
            "type": "function",
            "source_mapping": {
                "lines": [
                    34,
                    35,
                    36,
                    37,
                    38
                ],
                "start": 1248,
                "length": 156,
                "ending_column": 12,
                "is_dependency": false,
                "filename_short": "contracts/Contract.sol",
                "starting_column": 42,
                "filename_absolute": "/tmp/slither-qf3k999l/contracts/Contract.sol",
                "filename_relative": "contracts/Contract.sol"
            },
            "type_specific_fields": {
                "parent": {
                    "name": "SimpleERC20Escrow",
                    "type": "contract",
                    "source_mapping": {
                        "lines": [
                            14,
                            15,
                            16,
                            17,
                            18,
                            19,
                            20,
                            21,
                            22,
                            23,
                            24,
                            25,
                            26,
                            27,
                            28,
                            29,
                            30,
                            31,
                            32,
                            33,
                            34,
                            35,
                            36,
                            37,
                            38,
                            39,
                            40,
                            41,
                            42,
                            43,
                            44,
                            45,
                            46,
                            47,
                            48,
                            49,
                            50,
                            51
                        ],
                        "start": 581,
                        "length": 1290,
                        "ending_column": 92,
                        "is_dependency": false,
                        "filename_short": "contracts/Contract.sol",
                        "starting_column": 73,
                        "filename_absolute": "/tmp/slither-qf3k999l/contracts/Contract.sol",
                        "filename_relative": "contracts/Contract.sol"
                    }
                },
                "signature": "pay(address,uint256)"
            }
        },
        {
            "name": "token.transfer(recipient,amount)",
            "type": "node",
            "source_mapping": {
                "lines": [
                    37,
                    38
                ],
                "start": 1364,
                "length": 33,
                "ending_column": 5,
                "is_dependency": false,
                "filename_short": "contracts/Contract.sol",
                "starting_column": 27,
                "filename_absolute": "/tmp/slither-qf3k999l/contracts/Contract.sol",
                "filename_relative": "contracts/Contract.sol"
            },
            "type_specific_fields": {
                "parent": {
                    "name": "pay",
                    "type": "function",
                    "source_mapping": {
                        "lines": [
                            34,
                            35,
                            36,
                            37,
                            38
                        ],
                        "start": 1248,
                        "length": 156,
                        "ending_column": 12,
                        "is_dependency": false,
                        "filename_short": "contracts/Contract.sol",
                        "starting_column": 42,
                        "filename_absolute": "/tmp/slither-qf3k999l/contracts/Contract.sol",
                        "filename_relative": "contracts/Contract.sol"
                    },
                    "type_specific_fields": {
                        "parent": {
                            "name": "SimpleERC20Escrow",
                            "type": "contract",
                            "source_mapping": {
                                "lines": [
                                    14,
                                    15,
                                    16,
                                    17,
                                    18,
                                    19,
                                    20,
                                    21,
                                    22,
                                    23,
                                    24,
                                    25,
                                    26,
                                    27,
                                    28,
                                    29,
                                    30,
                                    31,
                                    32,
                                    33,
                                    34,
                                    35,
                                    36,
                                    37,
                                    38,
                                    39,
                                    40,
                                    41,
                                    42,
                                    43,
                                    44,
                                    45,
                                    46,
                                    47,
                                    48,
                                    49,
                                    50,
                                    51
                                ],
                                "start": 581,
                                "length": 1290,
                                "ending_column": 92,
                                "is_dependency": false,
                                "filename_short": "contracts/Contract.sol",
                                "starting_column": 73,
                                "filename_absolute": "/tmp/slither-qf3k999l/contracts/Contract.sol",
                                "filename_relative": "contracts/Contract.sol"
                            }
                        },
                        "signature": "pay(address,uint256)"
                    }
                }
            }
        }
    ],
    "locations": [
        {
            "file": "contracts/Contract.sol",
            "name": "pay",
            "type": "function",
            "lines": [
                34,
                35,
                36,
                37,
                38
            ]
        },
        {
            "file": "contracts/Contract.sol",
            "name": "token.transfer(recipient,amount)",
            "type": "node",
            "lines": [
                37,
                38
            ]
        }
    ],
    "description": "SimpleERC20Escrow.pay(address,uint256) (contracts/Contract.sol#34-38) ignores return value by token.transfer(recipient,amount) (contracts/Contract.sol#37-38)"
}
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
}
low other codex ERC20 transfer return value ignored 65 no
view
evidence_json
{
    "evidence": "token.transfer(recipient, amount) is called without checking the returned bool.",
    "locations": [
        "contracts/Contract.sol:36"
    ],
    "recommendation": "Check the return value or use SafeERC20.safeTransfer to ensure failures revert.",
    "exploit_scenario": "For ERC20s that return false instead of reverting, pay will succeed without transferring funds, causing the caller to believe payment was made and potentially breaking accounting in the wider system."
}