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

Analysis Run

019ba9bc-ecc2-7002-a340-f3bde419e7cf

complete
Chain ID
1
Address
Block Number
Created
Sat, Jan 10, 2026 9:08 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
37881 artifact.fetch complete 1 / 3 3 months ago
37882 proxy.resolve complete 1 / 3 3 months ago
37883 tool.cast_disasm complete 1 / 3 3 months ago
37884 analysis.bundle complete 1 / 3 3 months ago
37885 capability.graph complete 1 / 3 3 months ago
37886 detector.run complete 1 / 3 3 months ago
37887 validation.fork complete 1 / 3 3 months ago
37967 report.consolidate complete 1 / 6 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": "0x5afdab84d684a057d359498e40f38f433390e711"
        }
    },
    "implementation": "0x5afdab84d684a057d359498e40f38f433390e711",
    "onchain_method": "eip1967"
}

Slither

tool.slither

No slither job recorded yet.

Codex

report.consolidate
Job Status
complete
Attempts
1 / 6
Findings
2
No findings?
no
Summary
Two low-severity issues: permit-based order submission can be griefed due to the global nonce coupling, and GoFastCaller leaves lingering token approvals that can expose tokens held in GoFastCaller.
View stdout
{"no_findings":false,"summary":"Two low-severity issues: permit-based order submission can be griefed due to the global nonce coupling, and GoFastCaller leaves lingering token approvals that can expose tokens held in GoFastCaller.","findings":[{"severity":"low","category":"auth","title":"Global order nonce reused for Permit2 enables signature-griefing DoS","exploit_scenario":"A user signs a Permit2 transfer using the current gateway nonce. An attacker front-runs by submitting any order to bump the global nonce. The user’s `submitOrderWithPermit` then uses the new nonce and the signature fails, forcing re-signing. Repeated front‑runs can indefinitely delay permit-based submissions.","recommendation":"Decouple order IDs from Permit2 nonces. Track per-user permit nonces or require the caller to supply the Permit2 nonce used in the signature, and avoid deriving it from the global order nonce.","evidence":"`submitOrderWithPermit` builds the order with the global `nonce` and passes `order.nonce` into `_permitTransferFrom`, which is then used as the Permit2 nonce.","locations":["src/FastTransferGateway.sol:216","src/FastTransferGateway.sol:226","src/FastTransferGateway.sol:465"]},{"severity":"low","category":"other","title":"GoFastCaller leaves stale allowances to recipients","exploit_scenario":"A recipient contract called via `goFastCaller.execute` can intentionally not spend all (or any) of the allowance. The approval remains, so if GoFastCaller later holds tokens (e.g., for another order where the recipient fails to pull during its call), the previously approved recipient can call `transferFrom` to drain up to the remaining allowance.","recommendation":"Clear approvals after the external call (set allowance back to 0), or avoid approvals by transferring tokens directly to the recipient or using a dedicated pull pattern with explicit post-call cleanup.","evidence":"`execute` sets allowance via `forceApprove` but never revokes it after `_target.call` completes.","locations":["src/GoFastCaller.sol:30","src/GoFastCaller.sol:32"]}]}
View stderr
OpenAI Codex v0.77.0 (research preview)
--------
workdir: /tmp/codex-af0ajtpn
model: gpt-5.2-codex
provider: openai
approval: never
sandbox: read-only
reasoning effort: high
reasoning summaries: auto
session id: 019baa24-86ea-7ce1-8e13-3ac5ef6c12e7
--------
user
Context: The user requested the proxy at 0xe7935104c9670015b21c6300e5b95d2f75474cda.
Audit the implementation at 0x5afdab84d684a057d359498e40f38f433390e711.
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-af0ajtpn succeeded in 8ms:
AUDIT_BUNDLE.txt
codex-output-schema.json
lib
src
exec
/bin/bash -lc 'cat AUDIT_BUNDLE.txt' in /tmp/codex-af0ajtpn succeeded in 9ms:
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/FastTransferGateway.sol
================================================================================
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";

import {console} from "forge-std/console.sol";

import {TypeCasts} from "./libraries/TypeCasts.sol";




thinking
**Analyzing smart contract vulnerabilities in detail**

I'm auditing the FastTransferGateway contract focusing on its logic and potential security issues. I’m evaluating initializer omissions, order sender validations, nonce management, permissions in fill and settlement flows, allowance handling in GoFastCaller, reentrancy risks, and access control. There’s subtlety around tokens stuck in GoFastCaller due to leftover allowances and crafted orders causing failed settlement messages, but no outright critical exploits seem apparent so far. This deep dive is helping uncover nuanced edge cases and grasp how different components interact.
thinking
**Examining order flow and cross-chain message handling**

I’m exploring how fillOrder’s filler parameter and settlement process can be manipulated, looking closely at authorization, token flows, and replay risks. There are subtle trust assumptions across chains, like trusting remote contract messages and filler assignment, plus quirks around nonce usage and calldata parsing. I’m also scrutinizing token allowance handling in GoFastCaller and edge cases in upgradeability and external calls that might affect token custody or reentrancy. It’s interesting how economic incentives limit exploits, yet design choices leave certain behaviors possible.
thinking
**Analyzing reentrancy, nonce, and filler assignment issues**

I'm assessing reentrancy protections limited to fillOrder and potential reentrancy via token callbacks, the global nonce usage causing a DoS risk by invalidating permits, and how filler can be arbitrarily assigned, enabling griefing by locking sender funds if settlement can't be initiated. Also, the use of GoFastCaller and token allowances alongside upgrade patterns reveals subtle traps around token custody and fallback behaviors. There’s a lot to consider around trust boundaries and side effects in cross-chain message flows.
thinking
**Evaluating filler flexibility and token approval edge cases**

I'm considering how allowing arbitrary filler assignment can lead to griefing by locking sender funds, the implications of token approvals in GoFastCaller possibly enabling token retention or theft, and subtle interactions in nonce management and order encoding that limit clear vulnerabilities but highlight some low-to-medium concerns. This helps me pinpoint where the design might invite inefficiencies or user friction without outright exploits.
exec
/bin/bash -lc 'nl -ba src/FastTransferGateway.sol' in /tmp/codex-af0ajtpn succeeded in 6ms:
     1	// SPDX-License-Identifier: UNLICENSED
     2	pragma solidity ^0.8.13;
     3	
     4	import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
     5	import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
     6	import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
     7	import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
     8	import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
     9	import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
    10	import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
    11	
    12	import {console} from "forge-std/console.sol";
    13	
    14	import {TypeCasts} from "./libraries/TypeCasts.sol";
    15	import {OrderEncoder} from "./libraries/OrderEncoder.sol";
    16	
    17	import {IPermit2} from "./interfaces/IPermit2.sol";
    18	import {IMailbox} from "./interfaces/hyperlane/IMailbox.sol";
    19	import {GoFastCaller} from "./GoFastCaller.sol";
    20	

thinking
**Searching for GoFastCaller usage**
exec
/bin/bash -lc 'nl -ba src/GoFastCaller.sol' in /tmp/codex-af0ajtpn succeeded in 6ms:
     1	// SPDX-License-Identifier: UNLICENSED
     2	pragma solidity ^0.8.13;
     3	
     4	import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
     5	import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
     6	import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
     7	
     8	contract GoFastCaller is Ownable {
     9	    using SafeERC20 for IERC20;
    10	
    11	    address public gateway;
    12	
    13	    constructor(address _owner) Ownable(_owner) {}
    14	
    15	    function setGateway(address _gateway) external onlyOwner {
    16	        gateway = _gateway;
    17	    }
    18	
    19	    modifier onlyGateway() {
    20	        require(gateway != address(0), "GoFastCaller: gateway not set");


thinking
**Summarizing potential vulnerabilities and findings**

I'll gather and organize key low-to-medium severity issues including global nonce reuse causing DoS in Permit2, lingering token allowances in GoFastCaller enabling fund draining, and the risk of locked funds due to filler misuse—with careful notes on exploit scenarios, impact, and recommendations for each.
thinking
**Describing reentrancy and nonce risks with JSON output**

I'll explain possible reentrancy via token transferFrom calls potentially creating extra orders, though low severity due to trusted token assumption, and carefully organize two low-severity findings with detailed JSON including severity, category, exploit scenarios, recommendations, evidence, and source code locations for the user.
codex
{"no_findings":false,"summary":"Two low-severity issues: permit-based order submission can be griefed due to the global nonce coupling, and GoFastCaller leaves lingering token approvals that can expose tokens held in GoFastCaller.","findings":[{"severity":"low","category":"auth","title":"Global order nonce reused for Permit2 enables signature-griefing DoS","exploit_scenario":"A user signs a Permit2 transfer using the current gateway nonce. An attacker front-runs by submitting any order to bump the global nonce. The user’s `submitOrderWithPermit` then uses the new nonce and the signature fails, forcing re-signing. Repeated front‑runs can indefinitely delay permit-based submissions.","recommendation":"Decouple order IDs from Permit2 nonces. Track per-user permit nonces or require the caller to supply the Permit2 nonce used in the signature, and avoid deriving it from the global order nonce.","evidence":"`submitOrderWithPermit` builds the order with the global `nonce` and passes `order.nonce` into `_permitTransferFrom`, which is then used as the Permit2 nonce.","locations":["src/FastTransferGateway.sol:216","src/FastTransferGateway.sol:226","src/FastTransferGateway.sol:465"]},{"severity":"low","category":"other","title":"GoFastCaller leaves stale allowances to recipients","exploit_scenario":"A recipient contract called via `goFastCaller.execute` can intentionally not spend all (or any) of the allowance. The approval remains, so if GoFastCaller later holds tokens (e.g., for another order where the recipient fails to pull during its call), the previously approved recipient can call `transferFrom` to drain up to the remaining allowance.","recommendation":"Clear approvals after the external call (set allowance back to 0), or avoid approvals by transferring tokens directly to the recipient or using a dedicated pull pattern with explicit post-call cleanup.","evidence":"`execute` sets allowance via `forceApprove` but never revokes it after `_target.call` completes.","locations":["src/GoFastCaller.sol:30","src/GoFastCaller.sol:32"]}]}
tokens used
36,714
Metadata
{
    "codex": {
        "ran_at": "2026-01-10T23:06:48.270809+00:00",
        "returncode": 0
    },
    "project": {
        "mode": "source",
        "file_count": 22,
        "import_count": 26,
        "first_party_contracts_count": 6
    },
    "summary": "Two low-severity issues: permit-based order submission can be griefed due to the global nonce coupling, and GoFastCaller leaves lingering token approvals that can expose tokens held in GoFastCaller.",
    "raw_final": {
        "summary": "Two low-severity issues: permit-based order submission can be griefed due to the global nonce coupling, and GoFastCaller leaves lingering token approvals that can expose tokens held in GoFastCaller.",
        "findings": [
            {
                "title": "Global order nonce reused for Permit2 enables signature-griefing DoS",
                "category": "auth",
                "evidence": "`submitOrderWithPermit` builds the order with the global `nonce` and passes `order.nonce` into `_permitTransferFrom`, which is then used as the Permit2 nonce.",
                "severity": "low",
                "locations": [
                    "src/FastTransferGateway.sol:216",
                    "src/FastTransferGateway.sol:226",
                    "src/FastTransferGateway.sol:465"
                ],
                "recommendation": "Decouple order IDs from Permit2 nonces. Track per-user permit nonces or require the caller to supply the Permit2 nonce used in the signature, and avoid deriving it from the global order nonce.",
                "exploit_scenario": "A user signs a Permit2 transfer using the current gateway nonce. An attacker front-runs by submitting any order to bump the global nonce. The user\u2019s `submitOrderWithPermit` then uses the new nonce and the signature fails, forcing re-signing. Repeated front\u2011runs can indefinitely delay permit-based submissions."
            },
            {
                "title": "GoFastCaller leaves stale allowances to recipients",
                "category": "other",
                "evidence": "`execute` sets allowance via `forceApprove` but never revokes it after `_target.call` completes.",
                "severity": "low",
                "locations": [
                    "src/GoFastCaller.sol:30",
                    "src/GoFastCaller.sol:32"
                ],
                "recommendation": "Clear approvals after the external call (set allowance back to 0), or avoid approvals by transferring tokens directly to the recipient or using a dedicated pull pattern with explicit post-call cleanup.",
                "exploit_scenario": "A recipient contract called via `goFastCaller.execute` can intentionally not spend all (or any) of the allowance. The approval remains, so if GoFastCaller later holds tokens (e.g., for another order where the recipient fails to pull during its call), the previously approved recipient can call `transferFrom` to drain up to the remaining allowance."
            }
        ],
        "no_findings": false
    },
    "no_findings": false,
    "proxy_context": {
        "proxy_run_id": "019ba9bc-ecc2-7002-a340-f3bde419e7cf",
        "proxy_address": "0xe7935104c9670015b21c6300e5b95d2f75474cda",
        "implementation_run_id": "019ba9bd-975f-73b1-8c6a-5aab9a2fbec6",
        "implementation_address": "0x5afdab84d684a057d359498e40f38f433390e711"
    },
    "schema_version": 1
}

Opcode Heuristics

tool.cast_disasm
Delegatecall
1
Selfdestruct
0
CREATE2
0
CALL-family (heavy)
0
EXT*/BALANCE
0
Total opcodes
81
Flags
delegatecall_present proxy_like_runtime
View cast disassembly output
00000000: PUSH1 0x80
00000002: PUSH1 0x40
00000004: MSTORE
00000005: PUSH1 0x0a
00000007: PUSH1 0x0c
00000009: JUMP
0000000a: JUMPDEST
0000000b: STOP
0000000c: JUMPDEST
0000000d: PUSH1 0x18
0000000f: PUSH1 0x14
00000011: PUSH1 0x1a
00000013: JUMP
00000014: JUMPDEST
00000015: PUSH1 0x51
00000017: JUMP
00000018: JUMPDEST
00000019: JUMP
0000001a: JUMPDEST
0000001b: PUSH1 0x00
0000001d: PUSH1 0x4c
0000001f: PUSH32 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc
00000040: SLOAD
00000041: PUSH1 0x01
00000043: PUSH1 0x01
00000045: PUSH1 0xa0
00000047: SHL
00000048: SUB
00000049: AND
0000004a: SWAP1
0000004b: JUMP
0000004c: JUMPDEST
0000004d: SWAP1
0000004e: POP
0000004f: SWAP1
00000050: JUMP
00000051: JUMPDEST
00000052: CALLDATASIZE
00000053: PUSH1 0x00
00000055: DUP1
00000056: CALLDATACOPY
00000057: PUSH1 0x00
00000059: DUP1
0000005a: CALLDATASIZE
0000005b: PUSH1 0x00
0000005d: DUP5
0000005e: GAS
0000005f: DELEGATECALL
00000060: RETURNDATASIZE
00000061: PUSH1 0x00
00000063: DUP1
00000064: RETURNDATACOPY
00000065: DUP1
00000066: DUP1
00000067: ISZERO
00000068: PUSH1 0x6f
0000006a: JUMPI
0000006b: RETURNDATASIZE
0000006c: PUSH1 0x00
0000006e: RETURN
0000006f: JUMPDEST
00000070: RETURNDATASIZE
00000071: PUSH1 0x00
00000073: REVERT
00000074: INVALID
00000075: LOG2
00000076: PUSH5 0x6970667358
0000007c: UNKNOWN(0x22)
0000007d: SLT
0000007e: KECCAK256
0000007f: UNKNOWN(0x29)
00000080: PUSH10 0x736c580c73d705f1e276
0000008b: EXP
0000008c: JUMP
0000008d: CALLVALUE
0000008e: UNKNOWN(0x0F)
0000008f: STOP
00000090: ORIGIN
00000091: UNKNOWN(0xB7)
00000092: UNKNOWN(0xB6)
00000093: PUSH27

Findings

validated = confirmed for call sinks; sink observed for others
Severity Category Tool Title Confidence Validated Confirmed Details
high upgradeability detector Untrusted DELEGATECALL target reachable 55 no no
view
trace_id: 0x810bc911ee713ded69763bc4d6327c7fbd9c8bd41c894be2281c90d3ecdc4106
call_targets: 0x5afdab84d684a057d359498e40f38f433390e711
target_varies: no
classification: constant_target
validation_json
{
    "sink": "DELEGATECALL",
    "errors": 0,
    "status": "sink_reached",
    "attempts": 1,
    "trace_id": "0x810bc911ee713ded69763bc4d6327c7fbd9c8bd41c894be2281c90d3ecdc4106",
    "confirmed": false,
    "trace_mode": "callTracer",
    "call_targets": [
        "0x5afdab84d684a057d359498e40f38f433390e711"
    ],
    "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": 95,
            "opcode": "DELEGATECALL",
            "returndata_checked": null,
            "value_expression_category": "n/a",
            "target_expression_category": "computed"
        }
    ],
    "guards_detected": [
        {
            "pc": 144,
            "type": "tx_origin_used"
        }
    ],
    "delegatecall_count": 1,
    "untrusted_delegatecall_count": 1
}
high auth detector Authorization based on tx.origin 70 no
view
validation_json
{
    "reason": "missing sink mapping",
    "status": "no_sink"
}
witness_json
{
    "notes": "heuristic witness; entrypoints are best-effort selectors with zeroed calldata",
    "constraints": [],
    "entrypoints": [
        {
            "calldata": "0x",
            "selector": "fallback",
            "calldata_variants": [
                "0x"
            ]
        }
    ]
}
evidence_json
{
    "guards_detected": [
        {
            "pc": 144,
            "type": "tx_origin_used"
        }
    ]
}
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 auth codex Global order nonce reused for Permit2 enables signature-griefing DoS 65 no
view
evidence_json
{
    "evidence": "`submitOrderWithPermit` builds the order with the global `nonce` and passes `order.nonce` into `_permitTransferFrom`, which is then used as the Permit2 nonce.",
    "locations": [
        "src/FastTransferGateway.sol:216",
        "src/FastTransferGateway.sol:226",
        "src/FastTransferGateway.sol:465"
    ],
    "recommendation": "Decouple order IDs from Permit2 nonces. Track per-user permit nonces or require the caller to supply the Permit2 nonce used in the signature, and avoid deriving it from the global order nonce.",
    "exploit_scenario": "A user signs a Permit2 transfer using the current gateway nonce. An attacker front-runs by submitting any order to bump the global nonce. The user\u2019s `submitOrderWithPermit` then uses the new nonce and the signature fails, forcing re-signing. Repeated front\u2011runs can indefinitely delay permit-based submissions."
}
low other codex GoFastCaller leaves stale allowances to recipients 65 no
view
evidence_json
{
    "evidence": "`execute` sets allowance via `forceApprove` but never revokes it after `_target.call` completes.",
    "locations": [
        "src/GoFastCaller.sol:30",
        "src/GoFastCaller.sol:32"
    ],
    "recommendation": "Clear approvals after the external call (set allowance back to 0), or avoid approvals by transferring tokens directly to the recipient or using a dedicated pull pattern with explicit post-call cleanup.",
    "exploit_scenario": "A recipient contract called via `goFastCaller.execute` can intentionally not spend all (or any) of the allowance. The approval remains, so if GoFastCaller later holds tokens (e.g., for another order where the recipient fails to pull during its call), the previously approved recipient can call `transferFrom` to drain up to the remaining allowance."
}
info upgradeability cast Likely proxy-like runtime 60 no
view
evidence_json
{
    "notes": "short runtime with SLOAD and DELEGATECALL resembles minimal proxy/upgradeable pattern",
    "sload_count": 1,
    "total_opcodes": 81,
    "delegatecall_count": 1
}