A CryptoKitties birth bot that earns midwife rewards while deploying self-destructing child contracts for gas refunds.
Key Facts
Description
CKBirthBot is a CryptoKitties birth bot deployed in November 2018 at an address starting with seven zero bytes (0x00000000). It calls the CryptoKitties core contract giveBirth() function to earn midwife rewards for birthing pregnant kitties. Simultaneously, it batch-deploys self-destructing child contracts via inline CREATE opcodes to harvest gas refunds, following a pattern similar to GST2 but with a custom, non-ERC20 implementation. The contract encodes its entire operational logic in a single fallback function that parses raw calldata to determine which action to take: birthing kitties, deploying gas token children, or managing storage slots. The contract includes owner-restricted withdraw and gas threshold functions protected by inline assembly origin checks.
Source Verified
Compiled with solc v0.4.17+commit.bdeb9e52, optimizer disabled. Exact runtime bytecode match against on-chain code at block 6720524.
Heuristic Analysis
The following characteristics were detected through bytecode analysis and may not be accurate.
Byzantium Era
First Metropolis hard fork. Added zk-SNARK precompiles, REVERT opcode, and staticcall.
Bytecode Overview
Verified Source Available
Source verified through compiler archaeology and exact bytecode matching.
View Verification ProofShow source code (Solidity)
pragma solidity ^0.4.13;
contract CKBirthBot {
uint256 counter;
uint256 gasThreshold;
function() payable {
bytes32 data;
bytes32 v1;
uint256 v2;
uint256 v3;
uint256 v4;
uint256 v5;
uint256 v6;
uint256 v7;
uint256 v8;
uint256 v9;
if (msg.data.length > msg.value) {
assembly { data := calldataload(callvalue()) }
if (uint256(byte(data)) == msg.value) {
data = bytes32(uint256(data) * 0x100);
v1 = data;
data = bytes32(uint256(data) * 0x100);
v9 = gasThreshold;
if (byte(v1) == 0x01 && tx.gasprice > v2) {
v3 = uint256(bytes2(data));
assembly { sstore(v3, callvalue()) }
}
data = bytes32(uint256(data) * 0x10000);
v4 = uint256(bytes4(data));
data = bytes32(uint256(data) * 0x100000000);
if (block.blockhash(v4 - 1) != 0) {
v5 = uint256(bytes3(data));
while (v5 > msg.value) {
data = bytes32(uint256(data) * 0x1000000);
if (address(0x06012c8cf97BEaD5deAe237070F9587f8E7A266d).call(0x88c2a0bf, v5) && byte(v1) == 0x01) {
v3 = uint256(bytes3(data));
v7 = v3 + 6;
while (v3 < v7) {
if (v3 > 0xffff) {
assembly {
mstore(callvalue, add(v3, 0xd99400000000a8f806c754549943b6550a2594c9a12683000000))
pop(call(gas, sha3(6, 0x1a), callvalue, callvalue, callvalue, callvalue, callvalue))
}
} else if (v3 > 0xff) {
assembly {
mstore(callvalue, add(v3, 0xd89400000000a8f806c754549943b6550a2594c9a126820000))
pop(call(gas, sha3(7, 0x19), callvalue, callvalue, callvalue, callvalue, callvalue))
}
} else if (v3 > 0x7f) {
assembly {
mstore(callvalue, add(v3, 0xd79400000000a8f806c754549943b6550a2594c9a1268100))
pop(call(gas, sha3(8, 0x18), callvalue, callvalue, callvalue, callvalue, callvalue))
}
} else {
assembly {
mstore(callvalue, add(v3, 0xd69400000000a8f806c754549943b6550a2594c9a12600))
pop(call(gas, sha3(9, 0x17), callvalue, callvalue, callvalue, callvalue, callvalue))
}
}
v3++;
}
}
data = bytes32(uint256(data) * 0x1000000);
v6++;
if (v6 == 4) {
assembly { data := calldataload(0x20) }
} else if (v6 == 9) {
assembly { data := calldataload(0x3e) }
}
v5 = uint256(bytes3(data));
}
}
} else if (byte(data) == 0x01) {
data = bytes32(uint256(data) * 0x100);
v8 = uint256(bytes3(data));
v9 = v8 + 0x64;
if (v8 > 1) {
while (v8 < v9) {
assembly { sstore(v8, 1) }
v8++;
}
}
} else if (byte(data) == 0x02) {
assembly {
mstore(0x64, 0x3318585733ff600052601b6005f3)
mstore(0x56, address)
mstore8(0x60, 0x7a)
mstore8(0x61, 0x73)
for { let i := 0 } lt(i, 0x3c) { i := add(i, 1) } {
pop(create(0, 0x60, 0x24))
}
sstore(0, add(0x3c, sload(0)))
}
}
}
}
function withdraw(uint256 amount) public {
checkOwner();
checkGas();
if (amount > 0) {
tx.origin.transfer(amount);
}
}
function setGasThreshold(uint256 val) public {
checkOwner();
checkGas();
assembly { sstore(1, val) }
}
function checkOwner() internal {
assembly {
jumpi(ok, eq(origin(), 0x06aba80df0bb055e707a2c0337910c1438dc9d17))
revert(0, 0)
ok:
}
}
function checkGas() internal {
assembly {
jumpi(ok, lt(gaslimit(), 0x01312d00))
revert(0, 0)
ok:
}
}
}External Links
Related contracts
BankWallet
Same eraProxy wallet that forwards received ETH to a configured bank address. Owner can sweep ETH and ERC-20 token balances to that same destination.
0x449ad8...17a931November 14, 2017UserWallet
Same eraExchange deposit wallet with ETH and ERC20 sweep functions. One of the most widely deployed bytecodes on Ethereum.
0x24eb88...a068a7January 4, 2018Contract 0x3d40c2...1ec557
Same eraToken relay contract. Forwards ERC20 token transfers via transferToken(token, recipient, amount) on behalf of a configured owner.
0x3d40c2...1ec557January 4, 2018IMG
Same eraOn-chain JPEG of a tabby cat: a 128x128 JFIF baseline image returned as a hex-text string by a single read() function. January 2018.
0x2fabe6...86c7a1January 21, 2018Contract 0xda0a31...93b07c
Same eraToken distributor contract. Transfers ERC20 tokens from the contract to recipients via sendCoin(recipient, amount, tokenAddress).
0xda0a31...93b07cJanuary 27, 2018CryptoColors
Same eraFebruary 2018 hot-potato collectible game on Ethereum. Five tokens: Red, Blue, Lime, Yellow, Orange — each storing R/G/B values on-chain.
0x3116c4...ef9b96February 3, 2018