Finding Ouroboros

close button

Finding Ouroboros

Download quests in Questplay

View the contracts and any other additional content from your IDE.

So you think you have mastered the EVM, and all its intricacies. Well, this challenge might prove you wrong.

Can you hunt the legendary Ouroboros?

Quine in EVM

A quine is a computer program that takes no input and returns its own source code as its only output. Quines are proven to be possible in any Turing-complete programming language, and that includes EVM bytecode.

That is, it is possible to write an EVM contract that when called, returns its own bytecode.


Since a quine should strictly take no input, it should not rely on reading from calldata, storage, or block variables. It should also not rely on any external contracts.

Ouroboros in EVM

Extending from quines, let us try to build a quine-like relay: two distinct programs that, when called, return the bytecode of the other.


Ouroboros Quines

Here is the catch! To make this challenge truly difficult, neither contract can rely on CODECOPY or EXTCODECOPY. Is this construction still possible?

Your Task

Look for an Ouroboros, and set success in SerpentHunt to true.

To recap, your solution should not rely on:

  • Reading storage (SLOAD)


  • Copying bytecode (CODECOPY and EXTCODECOPY)

  • Mutating bytecode (SELFDESTRUCT)

  • External contracts (CALL, STATICCALL, ...)

Contract Code

// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; interface IQuine { function code() external returns (bytes memory); } contract SerpentHunt { bool public success; function hunt(address _head, address _tail) external { bytes memory headCode = IQuine(_tail).code(); bytes memory tailCode = IQuine(_head).code(); // 1. Contracts must be distinct require(_head.codehash != _tail.codehash, "Head == Tail"); // 2. Contracts must produce each other's code require(keccak256(headCode) == _head.codehash, "Not Ourosboros 1"); require(keccak256(tailCode) == _tail.codehash, "Not Ourosboros 2"); // 3. Contracts must not have illegal opcodes require(_isGood(headCode), "Bad Head"); require(_isGood(tailCode), "Bad Tail"); success = true; } /// @notice Checks that the given `_bytecode` does not have illegal opcodes. function _isGood(bytes memory _bytecode) private pure returns (bool) { uint i = 0; while (i < _bytecode.length) { bytes1 b = _bytecode[i]; if ( b == 0x31 // no BALANCE || b == 0x34 // no CALLVALUE || b == 0x39 // no CODECOPY || b == 0x3c // no EXTCODECOPY || b == 0x47 // no SELFBALANCE || b == 0x54 // no SLOAD || b == 0xf0 // no CREATE || b == 0xf1 // no CALL || b == 0xf2 // no CALLCODE || b == 0xf4 // no DELEGATECALL || b == 0xf5 // no CREATE2 || b == 0xfa // no STATICCALL || b == 0xff // no SELFDESTRUCT ) return false; if (b >= 0x60 && b < 0x80) { i += (uint8(b) - 0x60) + 1; } i++; } return true; } }

Hunt down the elusive Ouroboros, and bring back its skin…