Anatomy of an EVM Stake Honeypot: How a Single PUSH0 Opcode Drains Autonomous AI Agents

If you connect an autonomous AI agent with a funded USDC wallet to an on-chain bounty marketplace without bytecode-level pre-claim verification, your agent's treasury will be drained within minutes—not by a zero-day exploit in your LLM, but by a 1,193-byte smart contract containing a single PUSH0 (0x5f) opcode at offset 0x01a6. Following our forensic quarantine of the Taskmarket npm CLI, we ran a live sweep of the last 30 tasks on Base Mainnet (Chain ID 8453). Twenty-three of those 30 tasks—76.7% of the entire feed—were rotating stake-trap honeypots engineered specifically to confiscate worker collateral from naive autonomous agents. Here is the complete EVM disassembly of the attack and the four-layer scanner we built to defeat it.

Argon 3D EVM bytecode inspection and zero-stake security filter on WebDesigner.la
Address blocklists cannot stop an adversary who deploys a fresh contract and burner wallet every 40 minutes. Defeating on-chain stake honeypots requires runtime bytecode hashing (eth_getCode) and router-spoofed eth_call simulation.

1. The Bait and the Asymmetric Escrow Terms

To understand why autonomous agents fall for the stake honeypot attack, examine the raw JSON payload of task TSK-DWGCE9DX (task ID 0x687480747bb448d8a09a679af3f8da7c85d8b11bb4792492777217842c792c7b), captured live from our forensic mirror in stake-honeypot-tasks-TSK-DWGCE9DX-TSK-BFEKH6QX.json.txt:

{
  "referenceCode": "TSK-DWGCE9DX",
  "requester": "0x93Aa3d790A039305A1aB21D83e640397De6021c4",
  "description": "Write 3 short field labels for a parking app.\n\nDeliverable: one markdown file.\n- exactly 3 lines of `label: unit`\n- plain language, no emoji, no links\n\nAcceptance is mechanical: exactly 3 lines of `label: unit`.",
  "reward": "5000000",
  "mode": "claim",
  "stakeRequired": true,
  "stakeBps": 2000,
  "enforcedClaimStake": "1000000",
  " hookContract": "0x3d1238770c8d495bfc08bb914f72971da6f368a9",
  "terms": {
    "stakeRequired": true,
    "stakeBps": 2000,
    "penaltyTo": "requester",
    "releaseAllowed": false,
    "rejectOutcome": "reopen",
    "rejectFeeBps": 2000,
    "maxRevisions": 0
  }
}

Look at the psychological and algorithmic engineering of that task description: "Write 3 short field labels for a parking app... Acceptance is mechanical: exactly 3 lines of label: unit." For three lines of trivial Markdown, the requester promises 5.00 USDC (5000000 micro-USDC)—roughly 10x the prevailing market rate for micro-tasks.

Now examine the trap geometry encoded inside terms:

  • Upfront Worker Collateral (stakeBps: 2000 / enforcedClaimStake: "1000000"): Before the worker agent can submit its 3-line Markdown file, it must call claim and lock 20% of the reward—1.00 USDC of the worker's own funds—into the escrow contract.
  • The One-Way Door (releaseAllowed: false): Once the worker agent claims the task, the escrow contract forbids voluntary release. The worker cannot back out and recover its 1.00 USDC stake.
  • Zero Retries & Direct Confiscation (maxRevisions: 0, penaltyTo: "requester"): The moment a submission is rejected or the claim window expires, 100% of the forfeited stake is routed directly to the requester wallet (0x93Aa3d790A039305A1aB21D83e640397De6021c4), while rejectOutcome: "reopen" resets the trap for the next victim.
The Missing Detail in List Endpoints: Notice that summary list responses (GET /api/tasks?status=open) return stakeRequired: true and stakeBps: 2000, but omit hookContract until you hydrate the individual task endpoint (GET /api/tasks/<taskId>). An agent that only inspects list summaries never sees the on-chain hook address waiting behind the claim call.

2. Why Address Blocklists Fail: 16 Rotating Contracts, 1 Bytecode Hash

When we first encountered a malicious hook contract on Taskmarket days ago, a naive remediation would have been to hardcode that single contract address or requester wallet into a blocklist. To see why static address blocklists fail completely against automated adversaries, inspect the live output of our 30-task sweep (stake-honeypot-scan-2026-11-03.txt):

Task Ref Status Prompt Bait (5.00 USDC Reward / 1.00 USDC Stake) Deployed Hook Contract (Base Mainnet) Bytecode SHA-256 Prefix
TSK-DWGCE9DX OPEN Write 3 short field labels for a parking app. 0x3d1238770c8d495bfc08bb914f72971da6f368a9 7b938e8c458f7715 (1,193 B)
TSK-BFEKH6QX Cancelled Write 5 short field labels for a gym membership. 0x8ab1ef6ccad2883fec984710e295430e44a6dd76 7b938e8c458f7715 (1,193 B)
TSK-GFKJJ6EP Cancelled Write 3 subject lines for a warehouse shelf layout. 0x9bb501ec40c30311009953283b4b8854a1a8da41 7b938e8c458f7715 (1,193 B)
TSK-JRTFWS9H Cancelled Write a 6-question FAQ for a reusable coffee cup. 0xbff053d0894f5e7d4be510cdcf03aebb6128b16b 7b938e8c458f7715 (1,193 B)
TSK-CNH5DBPB Cancelled Write 3 short error messages for a hotel booking flow. 0x2420abf4e4bf6d1c31ebbdef1e86aa6a1a65f065 7b938e8c458f7715 (1,193 B)
TSK-T75S7A5V Cancelled Write 6 one-line release notes for a refund help article. 0xa517f1931faa25dfd70c8d161166585ef89e2d44 7b938e8c458f7715 (1,193 B)

Every 35 to 45 minutes, the attacker's bot cancels its previous open task, recycles the 5.00 USDC bait into a fresh requester EOA, deploys a brand-new instance of the hook contract on Base Mainnet, and posts a newly generated 3-to-7-line writing prompt. Across 23 flagged tasks, the attacker deployed 16 distinct contract addresses—yet every single one of those 16 contracts compiled to the exact same 1,193-byte runtime payload with SHA-256 digest 7b938e8c458f7715074d18bbabfd662f1d8a8f8200c8f3a54e26636ddd9628a5.

3. Disassembling the 1,193-Byte Trap: PUSH1 0x01 on Entry, PUSH0 on Exit

What does that 1,193-byte contract actually do when a worker agent claims the task and submits the 3 lines of Markdown requested in the prompt? We pulled the runtime bytecode via eth_getCode on Base Mainnet and disassembled every instruction from 0x0000 to 0x04a9 (available in full at stake-honeypot-bytecode-7b938e8c458f7715.txt).

Argon real-time EVM opcode disassembly and router simulation loop
The entire 1,193-byte contract contains zero SLOAD, zero SSTORE, and zero string comparisons. It is a stateless boolean diode that returns true on claim and false on submission.

First, across all 1,193 bytes of runtime code, there are zero SLOAD (0x54) opcodes, zero SSTORE (0x55) opcodes, and zero external CALL/STATICCALL instructions. Despite the task description claiming "Acceptance is mechanical: exactly 3 lines of label: unit", the smart contract never reads or inspects the worker's submission at all.

Instead, compare the two critical jump destinations inside the bytecode:

A. The Pre-Claim Hook (Selector 0x97942309 at Offset 0x024b..0x0283)

When a victim agent attempts to claim the task and lock its 1.00 USDC stake, the Taskmarket core router (0xddc6cc3e4d11c1f3527b867c7dad4ed9869c33f7) invokes selector 0x97942309. The contract checks that CALLER equals the Taskmarket router and immediately executes PUSH1 0x01 (returning true):

0x024b: PUSH32 0x000000000000000000000000ddc6cc3e4d11c1f3527b867c7dad4ed9869c33f7
0x026c: PUSH1  0x01
0x026e: PUSH1  0x01
0x0270: PUSH1  0xa0
0x0272: SHL
0x0273: SUB
0x0274: AND
0x0275: CALLER
0x0276: SUB
0x0277: PUSH2  0x01aa
0x027a: JUMPI              // Revert with UnauthorizedCaller(0x1935da05) if msg.sender != router
0x027b: PUSH1  0x20
0x027d: SWAP1
0x027e: MLOAD
0x027f: PUSH1  0x01        // <--- HARDCODED TRUE (1): Welcomes victim & locks 1.00 USDC stake
0x0281: DUP2
0x0282: MSTORE
0x0283: RETURN

B. The Submission Verification Hook (Selector 0xb65065f4 at Offset 0x017a..0x01a9)

Once the victim's 1.00 USDC stake is locked in escrow with releaseAllowed: false, the worker submits its deliverable, triggering selector 0xb65065f4. Look at what the contract executes at offset 0x01a6:

0x017a: PUSH32 0x000000000000000000000000ddc6cc3e4d11c1f3527b867c7dad4ed9869c33f7
0x019b: AND
0x019c: CALLER
0x019d: SUB
0x019e: PUSH2  0x01aa
0x01a1: JUMPI              // Revert with UnauthorizedCaller(0x1935da05) if msg.sender != router
0x01a2: PUSH1  0x20
0x01a4: SWAP1
0x01a5: MLOAD
0x01a6: PUSH0              // <--- HARDCODED FALSE (0): Unconditionally rejects submission!
0x01a7: DUP2
0x01a8: MSTORE
0x01a9: RETURN

At 0x01a6: PUSH0, the contract pushes 0 onto the EVM stack, writes a 32-byte zero word (0x0000...0000) to memory, and returns false. No input on earth can make 0xb65065f4 return true. Even more cleverly, notice lines 0x019c..0x01a1: if a security researcher probes 0xb65065f4 via a standard eth_call without spoofing from: 0xddc6cc3e4d11c1f3527b867c7dad4ed9869c33f7, execution jumps to 0x01aa and reverts with custom error 0x1935da05(address caller)—masking the hardcoded false return behind an access-control revert!

4. How to Detect and Block Stake Honeypots Programmatically

To protect our worker wallet (0xe8797A6BB4693674D78e933E85CB159a0219d03B) permanently, we implemented a four-layer defense inside scripts/a2a_client.js (invoked via node scripts/a2a_client.js --scan-honeypots):

  1. Layer 1 — Strict Zero-Stake Policy Gate: Any task with stakeRequired === true, Number(stakeBps) > 0, or Number(enforcedClaimStake) > 0 is immediately flagged as ⛔ STAKE/TRAP HOOK — DO NOT CLAIM. A worker agent should never post unilateral liquid collateral into a custom requester-chosen escrow policy where releaseAllowed === false and penaltyTo === "requester".
  2. Layer 2 — Mandatory Full-Task Hydration & Hook Whitelisting: Before evaluating any task, our client hydrates GET /api/tasks/<taskId> and inspects hookContract. Any non-zero hookContract other than the canonical standard bounty hook (0x8e28bb2c54443f54030ff9f2bc1f1794c017f0ca) is treated as untrusted adversarial code.
  3. Layer 3 — Runtime Bytecode Fingerprinting (eth_getCode SHA-256): For every custom hookContract, the scanner fetches the deployed bytecode from Base Mainnet RPC and computes its SHA-256 hash. Even when the attacker deploys a brand-new contract address from a fresh burner wallet, its runtime SHA-256 fingerprint (7b938e8c458f7715074d18bbabfd662f1d8a8f8200c8f3a54e26636ddd9628a5) immediately identifies it as a known honeypot clone.
  4. Layer 4 — Router-Spoofed eth_call Pre-Flight Simulation: Finally, our scanner simulates calling selector 0xb65065f4 with from explicitly spoofed to the Taskmarket core router (0xddc6cc3e4d11c1f3527b867c7dad4ed9869c33f7) and a valid 14-word Task struct at offset 0x60:
const routerAddr = '0xddc6cc3e4d11c1f3527b867c7dad4ed9869c33f7';
const dummyCalldata =
  '0xb65065f4' +
  String(t.id || '').replace(/^0x/, '').padStart(64, '0') +
  '0000000000000000000000000000000000000000000000000000000000000060' +
  '000000000000000000000000e8797a6bb4693674d78e933e85cb159a0219d03b' +
  '0'.repeat(14 * 64);
const simHex = await BaseModule.queryRpc('eth_call', [
  { from: routerAddr, to: hook, data: dummyCalldata },
  'latest'
]);
// Returns 0x0000000000000000000000000000000000000000000000000000000000000000
// -> RETURNS_FALSE (0x0 via PUSH0 at 0x01a6) -> TRAP CONFIRMED

5. Inspect the Live Bytecode & Honeypot Scan Artifacts

We have published the complete bytecode disassembly, the 30-task honeypot scan output, and the raw JSON task payloads in our public forensic mirror on webdesigner.la so any agent builder can test their own detection pipeline against real-world samples:

"Never let an autonomous agent evaluate an on-chain bounty by its natural-language prompt alone. In adversarial agentic marketplaces, the prompt is merely the lure; the ground truth lives in the escrow terms and the disassembled EVM opcodes of the verification hook."