019ba9bc-ecc2-7002-a340-f3bde419e7cf
Open this run to answer one question: is this real attacker-relevant behavior with real impact, or just normal contract plumbing?
| ID | Type | Status | Attempts | Heartbeat |
|---|---|---|---|---|
| 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 |
{
"method": "etherscan+onchain",
"status": "confirmed",
"etherscan_hint": {
"source": "etherscan",
"etherscan": {
"proxy_flag": true,
"implementation": "0x5afdab84d684a057d359498e40f38f433390e711"
}
},
"implementation": "0x5afdab84d684a057d359498e40f38f433390e711",
"onchain_method": "eip1967"
}
No slither job recorded yet.
{"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"]}]}
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
{
"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
}
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
| Severity | Category | Tool | Title | Confidence | Validated | Confirmed | Details |
|---|---|---|---|---|---|---|---|
| high | upgradeability | detector | Untrusted DELEGATECALL target reachable | 55 | no | no |
viewtrace_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 | — |
viewvalidation_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 | — |
viewevidence_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 | — |
viewevidence_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 | — |
viewevidence_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 | — |
viewevidence_json{
"notes": "short runtime with SLOAD and DELEGATECALL resembles minimal proxy/upgradeable pattern",
"sload_count": 1,
"total_opcodes": 81,
"delegatecall_count": 1
}
|