---
title: "TeikhosBounty"
description: "In 2018 Johan Nygren put ETH behind five contracts that pay whoever submits the right public key. Four of them can pay. One did in 2026 with a key a failed attempt had left on chain four years earlier."
canonical_url: "https://puzzles.agntn.dev/collections/teikhos"
---
# TeikhosBounty

> In 2018 Johan Nygren put ETH behind five contracts that pay whoever submits the right public key. Four of them can pay. One did in 2026 with a key a failed attempt had left on chain four years earlier.

In February and March 2018 [Johan Nygren](https://puzzles.agntn.dev/authors/johan-nygren) deployed five contracts from one address, all verified on Etherscan as `TeikhosBounty`, and funded each with ETH. The idea behind them: hide a signature behind a hash so nobody can use it until they know the public key. Five days after the last deploy the author [proposed it on the EIPs tracker](https://github.com/ethereum/EIPs/issues/935) as `proof_of_public_key = keccak256(nextPublicKey)` and named it Teikhos, Greek for "wall" or "fortification". The contracts are the wall with money behind it.

<collection-facts collection="teikhos">



</collection-facts>

## How a TeikhosBounty works

Normally a public key leaks the moment an address signs something. Here the address never signs. The contract stores a signature that key made over itself, XORed with the key, so the stored words look like noise. Send the right 64 bytes to `authenticate()` and the XOR gives back `r` and `s`. `ecrecover` checks them against the address the key hashes to. Wrong key, garbage signature, nothing happens.

So what do you search for? Not a private key. You never sign anything with it, and you don't need it. You need the public key, and there is no route from the contract to it: no transaction from its address, no signature, nothing on chain to recover it from. It's a 64-byte secret someone has to hand you.

### Three variants

- **teikhos/0** and **teikhos/1**, 1 ETH each. The simple one: stored words XOR public key, `ecrecover`, `suicide(msg.sender)`. Except teikhos/0 stops at the check, more on that below.
- **teikhos/2**, 0.5 ETH. One more layer: the public key first unlocks a symmetric key, and that key unlocks the signature. The same code went up [as a gist](https://gist.github.com/resilience-me/be11a0ed3575dddca10df8263b53cc1d) 48 minutes before the deploy, stored proofs included.
- **teikhos/3** and **teikhos/4**, 0.5 ETH each. The mask is the Keccak-512 of the key, computed by a separate `SHA3_512` contract. And there's commit and reveal on top, because teikhos/1 and teikhos/2 pay whoever's transaction lands first. Anyone watching the mempool can copy a correct key and outbid you on gas.

The two commit and reveal contracts aren't twins. teikhos/4 went up two hours after teikhos/3 as a revision. In teikhos/3 you commit, `authenticate()` sets the solution, you call `reveal()` later, and if nobody reveals a valid commit in seven days, `reward()` sends the ETH back to the author. In teikhos/4 `authenticate()` reveals your commit in the same call and reverts unless it holds. No way back to the author.

## How teikhos/4 fell

On April 3, 2022 an address nobody has tied to a name committed to teikhos/4 and, twenty minutes later, called `authenticate()` with a public key. The transaction [reverted](https://etherscan.io/tx/0x7e2596f9f202aad8a90e84cf93e32e7b3fb0ba244491d84542cedaf089a553c4). The key was right. In teikhos/4 a correct key only reverts at the last line, `require(reveal() == true)`, so it was their commit that didn't hold. A reverted transaction still keeps its calldata, though. The answer sat on chain for four years.

[floflo777](https://puzzles.agntn.dev/solvers/floflo777) went through the calldata of every authenticate call to the five contracts, checked each key offline and found this one. In June 2026 they committed and authenticated through Flashbots, so nobody could copy the commit, waited the seven days and called `reward()`. 0.5 ETH, eight years and three months after the funding.

The day they [posted about it](https://x.com/0xFlorent_/status/2068735759889145906), a crowd showed up. More than a dozen addresses sent `authenticate()` calls to teikhos/0, teikhos/1 and teikhos/2. Nothing paid, and teikhos/0 couldn't have.

## What the teikhos records carry

```ts [src/collections/teikhos/4.ts]
export const teikhos4 = puzzle({
  id: "teikhos/4",
  chain: "ethereum",
  address: "0x735ba26f91e1275fa4b504649b19ef74739fe7e7",
  sourceUrl: "https://etherscan.io/address/0x735ba26f91e1275fa4b504649b19ef74739fe7e7#code",
  startedAt: "2018-03-14 03:11:51",
  status: Status.Solved,
  prize: 0.5,
  stages: [
    stage("commit", "Store a signature by your own address …", [
      artifact("verified contract source", source),
    ]),
    stage(
      "authenticate",
      "Submit the public key whose Keccak-512 unmasks the stored proof …",
      [artifact("verified contract source", source)],
      answer("ca6a98ce…c36940c6", writeup, { date: "2026-08-16" }),
    ),
    stage("reward", "After the seven days anyone can call it …", [
      artifact("verified contract source", source),
    ]),
  ],
  solvedAt: "2026-06-21 09:51:23",
  solveTime: 261_038_372,
  transactions: [funding(/* 2018 */), claim(/* 2026, reward() */)],
  solver: party("floflo777", { key: "floflo777", addresses: ["0x83e4…5156"], facts: [/* … */] }),
});
```

### An answer that isn't a key

The winning input is a public key, and `pubkey` is the wrong place for it. That field means the key of the puzzle's own address, and a contract has none. So the key lives on the `authenticate` stage as its published `answer`, with floflo777's write-up as the source. The stages are the contract's own calls, one transaction each: three for teikhos/4, four for teikhos/3 with its separate `reveal()`, and one `authenticate` for the older three. The record has no `key` and no `pubkey`, and `verify` has nothing to check. The chain already did: `reward()` only pays after the right key went through `authenticate()`.

### Contracts don't sign

A claim here is a `selfdestruct`, not a signed spend, so a solved record has no public key to show. The validation that asks every claimed puzzle for one skips this collection, the same way it skips the Arweave weaves.

### Numbered in deployment order

`teikhos/4` resolves as `4`, `"4"` or `teikhos/4`. The numbers are our own, the order the contracts went on chain, from 0. Etherscan calls all five `TeikhosBounty`, so the author's name doesn't tell them apart. Why 0 and not 1? The first contract is the odd one out, and the rest keep the numbers they'd have without it.

### teikhos/0 can't pay

The first contract, [`0xaec7e8c2…`](https://etherscan.io/address/0xaec7e8c221c3fd24e75c996e32289235fd899ebf#code), went up 49 minutes before teikhos/1 and holds a bit over 1 ETH. Its `authenticate()` returns `true` on the right key and that's all. No `suicide`, no transfer, nothing else in the code moves ETH. So the record is there because the author funded it like the others, and it stays `unsolved` forever, key or no key. Its 1 ETH counts toward the open prizes, and nobody can collect it.

## The TeikhosBounty records

<collection-puzzles collection="teikhos">



</collection-puzzles>


## Sitemap

See the full [sitemap](https://puzzles.agntn.dev/sitemap.md) for all pages.
