Midwives for CryptoKitties: Little Kitten is Yours, but Birthing Fee is Mine, Part 1

Zhongqiang Chen
17 min readOct 12, 2019

--

Overview of the CryptoKitties game

The CryptoKitties is a game that consists of collecting virtual cats. Cats in the game can be created by Axiom Zen, the company behind the game, or created by the players of the game who can breed two cats to generate a new one. The physical attributes of each is determined by its own genetic sequence, which is mainly inherited from its parents’ genes with some randomness.

The main functions of the CryptoKitties game are implemented in smart contract called KittyCore. All operations related to genomes are stored in smart contract GeneScience. There is a market for buying and selling cats, and it is deployed in smart contract SaleClockAuction. Another market is for “renting” cats for breeding purposes, which is in smart contract SiringClockAuction.

Here is a summary on the smart contracts related to the CryptoKitties game.

KittyCore: 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d
creator: 0xba52c75764d6F594735dc735Be7F1830CDf58dDf
time: Nov-23-2017 05:41:19 AM +UTC
GeneScience: 0xf97e0A5b616dfFC913e72455Fde9eA8bBe946a2B
creator: 0xba52c75764d6F594735dc735Be7F1830CDf58dDf
time: Nov-23-2017 05:40:25 AM +UTC
SiringClockAuction: 0xC7af99Fe5513eB6710e6D5f44F9989dA40F27F26
creator: 0xba52c75764d6F594735dc735Be7F1830CDf58dDf
time: Nov-23-2017 05:41:47 AM +UTC
SaleClockAuction: 0xb1690C08E213a35Ed9bAb7B318DE14420FB57d8C
creator: 0xba52c75764d6F594735dc735Be7F1830CDf58dDf
time: Nov-23-2017 05:41:27 AM +UTC

The address of the KittyCore contract is 0x0601, and this smart contract provides the main entry points for players to interact with the game.

The most important function in the game is the giveBirth() function, which will be called to give birth of a new cat. When two cats breed, information on what block the pregnancy will be completed will be stored on the pregnant cat. In Ethereum blockchain, however, there is no inherent timer that allows a piece of code to be automatically executed in a certain block. This means that someone must call the function giveBirth() in the smart contract to deliver the baby.

contract KittyBreeding is KittyOwnership {
/// @notice Have a pregnant Kitty give birth!
/// @param _matronId A Kitty ready to give birth.
/// @return The Kitty ID of the new kitten.
/// @dev Looks at a given Kitty and, if pregnant and if the gestation period has passed,
/// combines the genes of the two parents to create a new kitten. The new Kitty is assigned
/// to the current owner of the matron. Upon successful completion, both the matron and the
/// new kitten will be ready to breed again. Note that anyone can call this function (if they
/// are willing to pay the gas!), but the new kitten always goes to the mother's owner.
function giveBirth(uint256 _matronId)
external
whenNotPaused
returns(uint256)
{
// Grab a reference to the matron in storage.
Kitty storage matron = kitties[_matronId];
// Check that the matron is a valid cat.
require(matron.birthTime != 0);
// Check that the matron is pregnant, and that its time has come!
require(_isReadyToGiveBirth(matron));
// Grab a reference to the sire in storage.
uint256 sireId = matron.siringWithId;
Kitty storage sire = kitties[sireId];
// Determine the higher generation number of the two parents
uint16 parentGen = matron.generation;
if (sire.generation > matron.generation) {
parentGen = sire.generation;
}
// Call the sooper-sekret gene mixing operation.
uint256 childGenes = geneScience.mixGenes(matron.genes, sire.genes, matron.cooldownEndBlock - 1);
// Make the new kitten!
address owner = kittyIndexToOwner[_matronId];
uint256 kittenId = _createKitty(_matronId, matron.siringWithId, parentGen + 1, childGenes, owner);
// Clear the reference to sire from the matron (REQUIRED! Having siringWithId
// set is what marks a matron as being pregnant.)
delete matron.siringWithId;
// Every time a kitty gives birth counter is decremented.
pregnantKitties--;
// Send the balance fee to the person who made birth happen.
msg.sender.send(autoBirthFee);
// return the new kitten's ID
return kittenId;
}
}

When the function giveBirth() is called, it first checks if the pregnancy has reached its course, which is done in function _isReadyToGiveBirth(). It then determines the genes of the offspring are determined by calling function mixGenes() in the GeneScience smart contract. Finally, the new kitten is created with the generated genes and the matron is the owner of the newly born baby.

It can be seen that there is no restriction on the invocation of the function giveBirth(), so everyone can call the function to deliver the baby even though the expected baby belongs to someone else. This is an incentive mechanism by the developers of the game to encourage the delivery of the child as soon as the pregnancy has been completed in order to reduce the chances of players gaming the system.

When computing the genome of a cat in the mixGenes() function, the block hash of the target block to deliver the cat is used as the source of randomness. In Ethereum blockchain, only the block hashes of the past 256 blocks are available to the EVM and therefore to the contracts. Thus, it is desirable that the new kitten should be delivered within 256 blocks of the target block.

If a cat is delivered after 256 blocks of the target block, the block hash is unavailable and thus it is zero. In that case, a faked target block is generated based on the current block and the target block as shown in the following piece of code for the mixGenes() function.

GeneScience {
/// @dev the function as defined in the breeding contract - as defined in CK bible
function mixGenes(uint256 _genes1, uint256 _genes2, uint256 _targetBlock) public returns (uint256) {
require(block.number > _targetBlock);
// Try to grab the hash of the "target block". This should be available the vast
// majority of the time (it will only fail if no-one calls giveBirth() within 256
// blocks of the target block, which is about 40 minutes. Since anyone can call
// giveBirth() and they are rewarded with ether if it succeeds, this is quite unlikely.)
uint256 randomN = uint256(block.blockhash(_targetBlock));
if (randomN == 0) {
// We don't want to completely bail if the target block is no-longer available,
// nor do we want to just use the current block's hash (since it could allow a
// caller to game the random result). Compute the most recent block that has the
// the same value modulo 256 as the target block. The hash for this block will
// still be available, and – while it can still change as time passes – it will
// only change every 40 minutes. Again, someone is very likely to jump in with
// the giveBirth() call before it can cycle too many times.
_targetBlock = (block.number & maskFirst248Bits) + (_targetBlock & maskLast8Bits);// The computation above could result in a block LARGER than the current block,
// if so, subtract 256.
if (_targetBlock >= block.number) _targetBlock -= 256;
randomN = uint256(block.blockhash(_targetBlock));
}
// generate 256 bits of random, using as much entropy as we can from
// sources that can't change between calls.
randomN = uint256(keccak256(randomN, _genes1, _genes2, _targetBlock));
uint256 randomIndex = 0;
// rest of code is omitted
}
}

It is clear that if the baby cat is given birth after 256 blocks of its target block, the faked target block is completely deterministic, and the genome of the new cat can be predicted in advance. In that case, the owner of the cat would have the opportunity to manipulate the genetic combination of its kittens and gain an unfair advantage against other breeders.

To keep this from happening, the game collects a fee from every breeding (called birthing fee), and allows anyone to claim the birthing fee by calling the giveBirth() function on the pregnant cat at (or after) the target block. The new cat still goes to the owner of the pregnant cat. But anyone can give birth to a cat whose pregnancy period has ended and be compensated for it.

The birthing fee on the delivery of a new kitten is transferred to the caller of the giveBirth() function by the statement msg.sender.send(autoBirthFee) in the function. Currently, the birthing fee is 0.008 Ethers.

This incentive mechanism in the CryptoKitties game has led to the emergence of the CryptoMidwives, which are agents who, even though they are not the owners of the to-be-born kittens or even though they are not themselves players of the game at all, fulfill the critical function of baby delivery in the CryptoKitties economy.

The crypto-midwife service is typically designed as a complex system that monitors the Ethereum blockchain to identify pregnant cats, collects information on the target blocks of pregnant cats, and invokes giveBirth() function on the right times to grab birthing fee. Part of this complex crypto-midwife service is implemented in smart contracts.

Nowadays, crypto-midwife service has become an economy in the CryptoKitties game and the competition in this market is fierce. Many attempts to call giveBirth() function arrive too late, meaning that when the transaction is processed by the miners the cat is no longer pregnant because another transaction was processed first.

Let us take a deep look at this crypto-midwife market in this article.

A Crypto-Midwife

The first crypto-midwife to be studied is the one deployed on the Ethereum blockchain by the following transaction.

Transaction Hash: 0xd2ea80192c6f00d76867817000110239535f48984ee90aee29311bdc55da3ab6
Status: Success
Block: 7175488 1464789 Block Confirmations
Timestamp: 236 days 39 mins ago (Feb-04-2019 10:58:43 PM +UTC)
From: 0x06aba80df0bb055e707a2c0337910c1438dc9d17
To: [Contract 0x4a2b76d5cb87bab48a14759649bb43ec8a35a628 Created]
Value: 0 Ether ($0.00)
Transaction Fee: 0.00015351672763 Ether ($0.03)
Gas Limit: 600,000
Gas Used by Transaction: 75,624 (12.6%)
Gas Price: 0.000000002030000101 Ether (2.030000101 Gwei)
Nonce Position 11023 25
Input Data: (omitted)

The smart contract was deployed on Feb-04–2019 10:58:43 PM +UTC. Its deployer is 0x06ab. The address of the smart contract is 0x4a2b.

As the creator of this smart contract did not publish its source code, we resort to reverse engineering techniques to restore it.

The source code of the smart contract for the crypto-midwife service, named contract_4a2b, is presented as follows.

pragma solidity ^0.5.1;
contract contract_4a2b {
address constant master = 0x00000000a8F806c754549943B6550A2594c9a126;

constructor () public {
}

function () external payable {
(bool success,) = master.call.value(msg.value)(msg.data);
}
}

This smart contract is amazingly simple. The only function with real code is its fallback function, which directly calls a function in another smart contract.

It is evident that there is no access control on the fallback function. So, no only the creator of the smart contract but also everyone in the world can call this function. However, this will not be a problem for the creator of the smart contract, which will become clear later.

In fact, this smart contract just acts as a wrapper to the smart contract whose address is 0x00000000a8F806c754549943B6550A2594c9a126, let us name it as contract_a8f8.

The contract_a8f8 is deployed on the Ethereum blockchain by the following transaction.

Transaction Hash: 0xa4540bb6a28be9af2b779e2027e491b22bb8638585bb296c4642b625e3ff68d2
Status: Success
Block: 6720524 1919588 Block Confirmations
Timestamp: 315 days 13 hrs ago (Nov-17-2018 09:35:14 AM +UTC)
From: 0x6a05fc6615c4f2bc872901876744b81f2414f44f
To: [Contract 0x00000000a8f806c754549943b6550a2594c9a126 Created]
Value: 0 Ether ($0.00)
Transaction Fee: 0.0019226203577 Ether ($0.33)
Gas Limit: 1,300,000
Gas Used by Transaction: 477,077 (36.7%)
Gas Price: 0.0000000040300001 Ether (4.0300001 Gwei)
Nonce Position 18 76
Input Data: (omitted)

The creation time of this smart contract, contract_a8f8, is Nov-17–2018 09:35:14 AM +UTC. Clearly, it is deployed much earlier than the wrapper contract contract_4a2b, which is created on Feb-04–2019 10:58:43 PM +UTC. It can be inferred that before the wrapper smart contract is created, the contract contract_a8f8 is triggered directly to utilize its midwife’s service. The creation of the wrapper is only an afterthought.

The creator of contract_a8f8 is 0x6a05, who is not the same as the one deploying the wrapper smart contract. It is quite natural to guess that the creator of contract_4a2b and the creator of contract_a8f8 belong to the same group.

Again, the source code the the contract_a8f8 is recovered via reverse engineering methods due to the unavailability of its source code.

The source code of contract_a8f8 is given below.

pragma solidity ^0.5.1;
contract contract_a8f8 {
address constant target = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
address constant admin = 0x06ABA80DF0Bb055E707A2c0337910C1438dc9d17;

uint256 constant MASK_1 = uint256(~((2 ** 248) - 1));
uint256 constant BYTE_1 = (2 ** 248);
uint256 constant BYTE_2 = (2 ** 240);
uint256 constant BYTE_3 = (2 ** 232);
uint256 constant BYTE_4 = (2 ** 224);
uint256 constant SHIFT_1 = 0x0100;
uint256 constant SHIFT_2 = 0x010000;
uint256 constant SHIFT_3 = 0x01000000;
uint256 constant SHIFT_4 = 0x0100000000;

// slot 0x00
uint256 numAgents;
// slot 0x01
uint256 unused;

constructor () public {
}

// function selector: 0x2e1a7d4d
// code entrance: 0x0579
function withdraw(uint256 _val) public {
checkOrigin();
checkGasLimit();
if (_val > 0) {
tx.origin.transfer(_val);
}
}

// function selector: 0xcb3b3ab3
// code entrance: 0x059c
function setUnused(uint256 _val) public {
checkOrigin();
checkGasLimit();
unused = _val;
}

function () external payable {
if (msg.data.length <= msg.value) {
return;
}
uint256 start = msg.value;
uint256 input;
assembly {
input := calldataload(start)
}
if ((input / BYTE_1) == msg.value) {
input = input * SHIFT_1;
uint256 secondByte = input;
input = input * SHIFT_1; // slot 01: uint256 unused; if (((secondByte & MASK_1) == BYTE_1) && (tx.gasprice > 0)) {
// get storage refund
uint256 slot = input / BYTE_2;
assembly {
sstore(slot, start)
}
}
input = input * SHIFT_2; uint256 cooldownEndBlock = input / BYTE_4; input = input * SHIFT_4; if (blockhash(cooldownEndBlock - 1) != 0) {
uint256 matronId = input / BYTE_3;
uint256 numBirth = 0;
while (matronId > msg.value) {
input = input * SHIFT_3;
// function giveBirth() selector: 0x88c2a0bf
(bool success,) = target.call(
abi.encodeWithSelector(0x88c2a0bf, matronId));
if ((success) && ((secondByte & MASK_1) == BYTE_1)) {
// suicide agent contracts to get refund
uint256 nonce = input / BYTE_3;
uint256 nonceEnd = nonce + 6;
while (nonce < nonceEnd) {
if (nonce > 0xffff) {
address addr = address(uint160(bytes20(
keccak256(abi.encodePacked(
bytes1(0xd9),
bytes1(0x94),
address(this),
bytes1(0x83),
uint24(nonce)
)))));
addr.call.value(msg.value)("");
}
else if (nonce > 0xff) {
address addr = address(uint160(bytes20(
keccak256(abi.encodePacked(
bytes1(0xd8),
bytes1(0x94),
address(this),
bytes1(0x82),
uint16(nonce)
)))));
addr.call.value(msg.value)("");
}
else if (nonce > 0x7f) {
address addr = address(uint160(bytes20(
keccak256(abi.encodePacked(
bytes1(0xd7),
bytes1(0x94),
address(this),
bytes1(0x81),
uint8(nonce)
)))));
addr.call.value(msg.value)("");
}
else {
address addr = address(uint160(bytes20(
keccak256(abi.encodePacked(
bytes1(0xd6),
bytes1(0x94),
address(this),
uint8(nonce)
)))));
addr.call.value(msg.value)("");
}
nonce = nonce + 1;
}
}
input = input * SHIFT_3;
numBirth = numBirth + 1;
if (numBirth == 4) {
assembly {
input := calldataload(0x20)
}
}
else if (numBirth == 9) {
assembly {
input := calldataload(0x3e)
}
}
matronId = input / BYTE_3;
}
}
return;
}
if ((input & MASK_1) == BYTE_1) {
// create bank of storage
input = input * SHIFT_1;
// 0x64 = 100
uint256 slot = input / BYTE_3;
uint256 slotEnd = slot + 100;
if (slot > 1) {
while (slot < slotEnd) {
assembly {
sstore(slot, 1)
}
slot = slot + 1;
}
}
return;
}
if ((input & MASK_1) == 2 * BYTE_1) {
// create pool of agents
uint112 tail = 0x3318585733ff600052601b6005f3;
bytes memory code = abi.encodePacked(
bytes1(0x7a),
bytes1(0x73),
address(this),
tail
);
uint256 len = code.length;
address deployed;
// 0x3c = 60
for (uint256 idx = 0; idx < 60; idx++) {
assembly {
deployed := create(0, add(code, 0x20), len)
}
}
numAgents = numAgents + 60;
}
}

// internal functions

// code entrance: 0x0633
function checkOrigin() private view {
require (tx.origin == admin);
}

// code entrance: 0x0655
function checkGasLimit() private view {
// 0x01312d00 = 20000000
require (block.gaslimit < 20000000);
}
}

The most obvious thing about the source code is that the entire functionality of the midwife service is implemented within the fallback function. The other function named withdraw() is used by the administrator of the smart contract to withdraw funds from the contract. The administrator of the smart contract is hard coded in the code, which is 0x06ab, who is also the creator of the wrapper smart contract contract_4a2b.

It is interesting that the deployer of the contract_18f8 is 0x6a05, however, the administrator is set to be 0x06ab, who later creator the wrapper contract_4a2b for contract_18f8. Clearly, addresses 0x6a05 and 0x06ab know each other, they may be even controlled by the same person.

The address of the smart contract for KittyCore, the core service for the CryptoKitties game, is also hard coded in the source code in variable “target”.

The fallback function takes a byte sequence as its input and provides different services accordingly. The first byte of the input sequence determines the type of service the caller intends to use.

There are mainly three different types of services:

  1. the first byte is 0: birth delivery service
  2. the first byte is 1: create bank of storage
  3. the first byte is 2: create pool of agent contracts

In the birth delivery service, the height of the block in which the births of crypto-kitties are expected is specified in the input sequence. As multiple births can be due on the same block, the input can include information on many births. For each birth, the matronId and a nonce are provided in the input sequence.

For each birth, the birth delivery service calls the function giveBirth() in the smart contract for the KittyCore using the provided matronId as parameter.

The invocation of the giveBirth() function costs gas. If the code for birth delivery service is not designed and implemented in an efficient manner, the profit by delivering a birth will be eaten up by the miner (as transaction fee).

Therefore, this smart contract takes advantage of both storage refund and contract refund provided by Ethereum blockchain.

In Ethereum blockchain, storages consumed by a contract are stored by all nodes of the blockchain forever. That is obviously very expensive. To encourage contract writers to delete storage variables when they are no longer useful, Ethereum provides a refund when a storage element is deleted. This refund can pay for up to half of the gas used by a contract transaction.

Similarly, Ethereum also provides gas refund when deleting a whole contract.

This is why this smart contract also provides services to create a bank of storage and to create a pool of agent contracts.

In the service for creating a bank of storage, the start key of the storage is specified in the input sequence, and the service will create 100 storage slots and assign non-zero (in this case 1) value to each slot. As the content of these storage slots are not zero, so they are occupied and acts as a gas deposit. These storage slots can be released later by setting them to zero, so that storage refunds can be obtained.

In the same manner, in the service for creating a pool of agent contracts, 64 agent contracts will be created whenever this service is invoked. These agent contracts are very simple, they simply self destruct themselves when they are triggered if the callers are their creators, or crash otherwise.

With the bank of storage and the pool of agent contracts at hand, the birth delivery service and compensate the cost of invoking giveBirth() function by releasing one storage slot to get the storage refund and instructing 6 agent contracts to destruct themselves to get the contract refund.

For details, please refer to the pieces of code starting at comment “get storage refund” and comment “suicide agent contracts to get refund” in the source code.

It can be seen that there is no access control to the fallback function, implying that any one can invokes the services provided by this function. However, the birth fee from a successful delivery of birth is stored in the smart contract, not the caller of the function. In order to extract money from this smart contract, the withdraw() function should be called. As expected, the withdraw() function first checks whether or not the caller is the administrator and it simply reverts the transaction if the caller is not the administrator.

The CryptoMidwife in action

As discussed above, the user of this crypto-midwife service can interact with either contract_4a2b (the wrapper) or contract_a8f8 (the core).

Before the crypto-midwife service can be used to deliver kittens, a bank of storage and a pool of agent contracts should be first created.

Here is an example transaction that triggers the function to create part of the storage bank.

Transaction Hash: 0x0ac4c974bfa7dd782027eb1a5378cc9e86c6966f070854ddaf130edc56b93d0d
Status: Success
Block: 6720538 1919582 Block Confirmations
Timestamp: 315 days 13 hrs ago (Nov-17-2018 09:38:04 AM +UTC)
From: 0x29ae0154e2a95ad4366abbe14b8b253fbbb44add
To: Contract 0x00000000a8f806c754549943b6550a2594c9a126
Value: 0 Ether ($0.00)
Transaction Fee: 0.00612400452278 Ether ($1.06)
Gas Limit: 2,050,000
Gas Used by Transaction: 2,027,816 (98.92%)
Gas Price: 0.0000000030200001 Ether (3.0200001 Gwei)
Nonce Position 5353 10
Input Data: 0x01002e7c

In this transaction, the input sequence to the fallback function is “0x01002e7c”. Because the first byte is “01”, it will trigger the service for creating a bank of storage. The remaining three bytes, “002e7c”, acts as the starting key for a bank of 100 storage slots. Thus, the key for the very last storage slot is (0x002e7c + 0x64–1) = 0x002edf.

On the other hand, the following transaction is used to create a pool of agent contracts.

Transaction Hash: 0x23e0c9c6c957dcb73adce9537ff3cb32de4447d194f1e2bec739e0c587248080
Status: Success
Block: 6720542 1919579 Block Confirmations
Timestamp: 315 days 13 hrs ago (Nov-17-2018 09:39:28 AM +UTC)
From: 0x06aba80df0bb055e707a2c0337910c1438dc9d17
To: Contract 0x00000000a8f806c754549943b6550a2594c9a126
Value: 0 Ether ($0.00)
Transaction Fee: 0.00691722868904 Ether ($1.20)
Gas Limit: 2,300,000
Gas Used by Transaction: 2,290,473 (99.59%)
Gas Price: 0.0000000030200001 Ether (3.0200001 Gwei)
Nonce Position 6558 72
Input Data: 0x02

The input sequence to the fallback function is a single byte “0x02”, and it will trigger the service for creating a pool of 100 agent contracts. The sender of this transaction is 0x06ab, while the sender of the previous transaction is 0x29ae. The status of both transactions are “success” as everyone can use this smart contract by invoking its functions.

Because for each successful invocation of giveBirth() function, the smart contract will release 1 storage slot and 6 agent contracts, it is expected that more transactions for calling agent creation service than for calling storage creation service. By scanning the transactions sent to contract_a8f8, we can verify that it is indeed so.

When invoking the child delivery service to give birth a new kitten, the transaction may arrive late because there is another transaction that delivers the same new kitten and has already processed in previous blocks or even in the same block but is positioned ahead of the current transaction.

Here is a transaction that attempts to deliver a child but arrives late.

Transaction Hash: 0x95fc8abca4116b7d10a8da8b10798fb7cddc08fc370c0bbf5d8dfed4cb2ce7ea
Status: Success
Block: 6720662 1919468 Block Confirmations
Timestamp: 315 days 12 hrs ago (Nov-17-2018 10:08:16 AM +UTC)
From: 0x2a9847093ad514639e8cdec960b5e51686960291
To: Contract 0x00000000a8f806c754549943b6550a2594c9a126
Although one or more Error Occurred [Reverted] Contract Execution Completed
Value: 0 Ether ($0.00)
Transaction Fee: 0.000136560262 Ether ($0.02)
Gas Limit: 340,000
Gas Used by Transaction: 16,653 (4.9%)
Gas Price: 0.000000008200339999 Ether (8.200339999 Gwei)
Nonce Position 92424 42
Input Data: 0x00012edf00668c961219e9000001

The input sequence to the fallback function is “0x00012edf00668c961219e9000001”. Thus, it will trigger the service to deliver one new kitten.

The target block number for the child delivery is 0x00668c96, that is, 6720662. The height of the current block is also 6720662. So, the transaction tries to deliver the kitten at the right block. However, the transaction is reverted because the giveBirth() function fails.

From the input sequence, we know that the key for storage slot is 0x2edf, which is one of the slots in the storage bank created by the transaction described above (in block 6720538).

The matronId for the to-be-born kitten is 0x1219e9, and the nonce for agent contract is 0x000001.

Therefore, this transaction tries to deliver a single child.

The transaction fee of this transaction is 0.000136560262 Ethers, which is much less than that of the transaction creating storage bank, 0.00691722868904 Ethers, or that of the transaction creating contract pool, 0.00691722868904 Ethers.

Let us look at a transaction that successfully deliver a kitten.

Transaction Hash: 0x19192c10c80e3d1992544c13955d8d133b01462ad0ba3c5f11acb630101be2aa
Status: Success
Block: 6720668 1919464 Block Confirmations
Timestamp: 315 days 12 hrs ago (Nov-17-2018 10:09:34 AM +UTC)
From: 0x2a9847093ad514639e8cdec960b5e51686960291
To: Contract 0x00000000a8f806c754549943b6550a2594c9a126
SELF DESTRUCT Contract 0xf4fcd6188acd111d8964c1f42e7a624a57112462
SELF DESTRUCT Contract 0xe3fa49d1ee4f46383405ab2b7800df5b2a0e87d0
SELF DESTRUCT Contract 0x6c369b3223567dedadbc9f71aede48b35aa80674
SELF DESTRUCT Contract 0xc4622874a15cdbe2986aa60cd8e1668da7ef7b0b
SELF DESTRUCT Contract 0x52cfb61968d59a1900aa64ff93a027f799f4609d
SELF DESTRUCT Contract 0x7861331e3675ac1a91eb56c2fc1763b2963b0caa
Tokens Transferred:From 0x0000000000000000000000000000000000000000To 0x75771dedde9707fbb78d9f0dbdc8a4d4e7784794 For ERC-721 TokenID [1191526] CryptoKittie... (CK)Value: 0 Ether ($0.00)
Transaction Fee: 0.0012375051089 Ether ($0.21)
Gas Limit: 340,000
Gas Used by Transaction: 150,909 (44.39%)
Gas Price: 0.000000008200339999 Ether (8.200339999 Gwei)
Nonce Position 92425 18
Input Data: 0x00012ede00668c9c122e42000007

The transaction indeed successfully delivers a new kitten, whose Id is 1191526. The matronId of the new baby is 0x122e42.

The information also shows that 6 agent contracts destruct themselves.

The transaction fee of this transaction is 0.0012375051089 Ethers. Compared to the birthing fee earned by this transaction (i.e., 0.008 Ethers), the transaction fee is about 1/7. So, the storage refund and contract refund indeed help reduce the cost of transaction execution and the transaction fee. Therefore, the crypto-midwife service is still quite profitable.

The internal transactions generated by this transaction are:

The contract call From 0x2a9847093ad514639e8cdec960b5e51686960291 To 0x00000000a8f806c754549943b6550a2594c9a126 produced 7 contract Internal Transactions :Type Trace Address      From            To      Value   Gas Limitcall_0_1       0x06012c8cf97bead5deae237070f9587f8e7a266d              0x00000000a8f806c754549943b6550a2594c9a126      0.008 Ether     2,300suicide_1_0    0xf4fcd6188acd111d8964c1f42e7a624a57112462              0x00000000a8f806c754549943b6550a2594c9a126      0 Ether 0suicide_2_0    0xe3fa49d1ee4f46383405ab2b7800df5b2a0e87d0              0x00000000a8f806c754549943b6550a2594c9a126      0 Ether 0suicide_3_0    0x6c369b3223567dedadbc9f71aede48b35aa80674              0x00000000a8f806c754549943b6550a2594c9a126      0 Ether 0suicide_4_0    0xc4622874a15cdbe2986aa60cd8e1668da7ef7b0b              0x00000000a8f806c754549943b6550a2594c9a126      0 Ether 0suicide_5_0    0x52cfb61968d59a1900aa64ff93a027f799f4609d              0x00000000a8f806c754549943b6550a2594c9a126      0 Ether 0suicide_6_0    0x7861331e3675ac1a91eb56c2fc1763b2963b0caa              0x00000000a8f806c754549943b6550a2594c9a126      0 Ether 0

It clearly indicates that 0.008 Ethers are transferred from the KittyCore contract (i.e., 0x0612) to the contract_a8f8. It also shows that 6 agent contracts self destroy themselves.

During the execution of the transaction, the KittyCore contract also emits some events shown below.

Transaction Receipt Event LogsAddress 0x06012c8cf97bead5deae237070f9587f8e7a266d
Name Birth (address owner, uint256 kittyId, uint256 matronId, uint256 sireId, uint256 genes)
Topics 0 0x0a5311bd2a6608f08a180df2ee7c5946819a649b204b554bb8e39825b2c50ad5
Data
00000000000000000000000075771dedde9707fbb78d9f0dbdc8a4d4e7784794
0000000000000000000000000000000000000000000000000000000000122e66
0000000000000000000000000000000000000000000000000000000000122e42
00000000000000000000000000000000000000000000000000000000000a35b7
0000421252198a30c2e22109600a008cc462d861a860314d3194a81aeaf0b16c
Address 0x06012c8cf97bead5deae237070f9587f8e7a266d
Name Transfer (address from, address to, uint256 tokenId)
Topics 0 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
Data
0000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000075771dedde9707fbb78d9f0dbdc8a4d4e7784794
0000000000000000000000000000000000000000000000000000000000122e66

Two events are generated: one is Birth() for giving birth to a new kitten and the other Transfer() for transferring birthing fee to the caller.

The Birth() event shows that the owner of the newly born cat is 0x75771dedde9707fbb78d9f0dbdc8a4d4e7784794, the matronId is 0x122e42, and the sireId is 0x0a35b7. The Id of the new kitten is 0x122e66 (i.e., 1191526).

References

--

--