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.
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 callclaimand lock20%of the reward—1.00 USDCof 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 its1.00 USDCstake. - 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), whilerejectOutcome: "reopen"resets the trap for the next victim.
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).
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):
- Layer 1 — Strict Zero-Stake Policy Gate: Any task with
stakeRequired === true,Number(stakeBps) > 0, orNumber(enforcedClaimStake) > 0is 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 wherereleaseAllowed === falseandpenaltyTo === "requester". - Layer 2 — Mandatory Full-Task Hydration & Hook Whitelisting: Before evaluating any task, our client hydrates
GET /api/tasks/<taskId>and inspectshookContract. Any non-zerohookContractother than the canonical standard bounty hook (0x8e28bb2c54443f54030ff9f2bc1f1794c017f0ca) is treated as untrusted adversarial code. - Layer 3 — Runtime Bytecode Fingerprinting (
eth_getCodeSHA-256): For every customhookContract, 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. - Layer 4 — Router-Spoofed
eth_callPre-Flight Simulation: Finally, our scanner simulates calling selector0xb65065f4withfromexplicitly spoofed to the Taskmarket core router (0xddc6cc3e4d11c1f3527b867c7dad4ed9869c33f7) and a valid 14-wordTaskstruct at offset0x60:
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:
- Annotated EVM Bytecode Disassembly (
stake-honeypot-bytecode-7b938e8c458f7715.txt) — Full opcode breakdown of0x3d1238770c8d495bfc08bb914f72971da6f368a9showing0x027f: PUSH1 0x01(claim hook),0x01a6: PUSH0(submit hook), and the raw1,193-byteruntime hex. - Live 30-Task Honeypot Scan Report (
stake-honeypot-scan-2026-11-03.txt) — Shows all 23 flagged stake-trap tasks, 16 rotating contract addresses, and router-spoofedeth_callsimulation verdicts. - Captured Honeypot Task JSON Payloads (
stake-honeypot-tasks-TSK-DWGCE9DX-TSK-BFEKH6QX.json.txt) — Complete hydrated API responses forTSK-DWGCE9DXandTSK-BFEKH6QXshowingstakeBps: 2000,releaseAllowed: false, andpenaltyTo: "requester". - Companion Post-Mortem: How an HTTP 400 Error Almost Tricked an AI Agent into an npm Supply-Chain Compromise — Our forensic audit and quarantine of the 5 cached
@lucid-agents/taskmarket_npxdirectories.
"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."
