Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

2024 Participants: Hannah Ackermans * Sara Alsherif * Leonardo Aranda * Brian Arechiga * Jonathan Armoza * Stephanie E. August * Martin Bartelmus * Patsy Baudoin * Liat Berdugo * David Berry * Jason Boyd * Kevin Brock * Evan Buswell * Claire Carroll * John Cayley * Slavica Ceperkovic * Edmond Chang * Sarah Ciston * Lyr Colin * Daniel Cox * Christina Cuneo * Orla Delaney * Pierre Depaz * Ranjodh Singh Dhaliwal * Koundinya Dhulipalla * Samuel DiBella * Craig Dietrich * Quinn Dombrowski * Kevin Driscoll * Lai-Tze Fan * Max Feinstein * Meredith Finkelstein * Leonardo Flores * Cyril Focht * Gwen Foo * Federica Frabetti * Jordan Freitas * Erika FülöP * Sam Goree * Gulsen Guler * Anthony Hay * SHAWNÉ MICHAELAIN HOLLOWAY * Brendan Howell * Minh Hua * Amira Jarmakani * Dennis Jerz * Joey Jones * Ted Kafala * Titaÿna Kauffmann-Will * Darius Kazemi * andrea kim * Joey King * Ryan Leach * cynthia li * Judy Malloy * Zachary Mann * Marian Mazzone * Chris McGuinness * Yasemin Melek * Pablo Miranda Carranza * Jarah Moesch * Matt Nish-Lapidus * Yoehan Oh * Steven Oscherwitz * Stefano Penge * Marta Pérez-Campos * Jan-Christian Petersen * gripp prime * Rita Raley * Nicholas Raphael * Arpita Rathod * Amit Ray * Thorsten Ries * Abby Rinaldi * Mark Sample * Valérie Schafer * Carly Schnitzler * Arthur Schwarz * Lyle Skains * Rory Solomon * Winnie Soon * Harlin/Hayley Steele * Marylyn Tan * Daniel Temkin * Murielle Sandra Tiako Djomatchoua * Anna Tito * Introna Tommie * Fereshteh Toosi * Paige Treebridge * Lee Tusman * Joris J.van Zundert * Annette Vee * Dan Verständig * Yohanna Waliya * Shu Wan * Peggy WEIL * Jacque Wernimont * Katherine Yang * Zach Whalen * Elea Zhong * TengChao Zhou
CCSWG 2024 is coordinated by Lyr Colin (USC), Andrea Kim (USC), Elea Zhong (USC), Zachary Mann (USC), Jeremy Douglass (UCSB), and Mark C. Marino (USC) . Sponsored by the Humanities and Critical Code Studies Lab (USC), and the Digital Arts and Humanities Commons (UCSB).

Code Critique: CryptoKitties

edited February 2020 in 2020 Code Critiques

Title: Cryptokitties
Author/s: Gerald Bauer, https://github.com/geraldb
Language/s: Solidity
Year/s of development: 2018
Software/hardware requirements (if applicable): Ethereum Network (can be a test network).

The last 10 years as seen many people question traditional monetary models. One of the results of this has been the rise, fall, and rise (and perhaps fall or statis), of various cryptocurrencies like bitcoin and ethereum. Cryptokitties is an example of a smart contract. It is a particular kind of smart contract, a token. And it is a particular kind of token, a non-fungible token. So if a regular token is like a $1 bill (all $1 bills are the same), a non-fungible token is like a jewel or collectable. Each jewel is slightly different from another one.

CryptoKitties are representations of virtual kitties that are non-fungible tokens. Cryptokitties would be 'born' at random intervals with different attributes, and then could breed with one another to create new cryptokitties. Cryptokitties became enormously popular with some being exchanged for 100,000s USD. It spawned the creation of a new Smart Contract Standard - ERC-721, and people working in this field. There are a number of Solidity smart contracts that allow cryptokitties to work. You can see them all on github. This code snippet is the base class of the CryptoKitty itself.

Some wide ranging questions about the code:
1. What makes this code? If we take a legal contract and translates it into a smart contract. What does it gain or lose in becoming code (or a smart contract)? What happens when it become a subject of software studies in addition to legal studies? How is this different from say the a header file and the implementation, or a configuration file? Do they both have the same status as code?

  1. The code includes very specific ideas about embodiment, biology, and genetics. What is the impact of that on the community of coders and collectors and the kitties themselves. For example, what would it look like if we had Cryptokitties based on Haraway's notion of Kin (or even cyborgs).

  2. This sort of program 'performs' in a different way than other programs in that it has (or generates) exchange value in the world. Is this relevant at all to our discussion software? If we consider software to be 'performative' or a 'Speech Act' in a JL Austin way, are there different categories of acts for software? Is this a function of the software or of the compiler or platform?

  3. Can we consider this program apart from the Ethereum platform? What is the relationship between this software and the network it runs on, can we consider it in isolation? Is this a similar question to ML and the training set of Data.

  4. What cultural ideas are embedded here: a love of cats memes on the internet, KittyAccessControl, Matron, Sire, auctioning kitties in general, cooldown period (is this like a nuclear reactor)?

  5. Money - but we have enough to talk about.

/// @title Base contract for CryptoKitties. Holds all common structs, events and base variables.
/// @author Axiom Zen (https://www.axiomzen.co)
/// @dev See the KittyCore contract documentation to understand how the various contract facets are arranged.

contract KittyBase is KittyAccessControl {
    /*** EVENTS ***/

    /// @dev The Birth event is fired whenever a new kitten comes into existence. This obviously
    ///  includes any time a cat is created through the giveBirth method, but it is also called
    ///  when a new gen0 cat is created.
    event Birth(address owner, uint256 kittyId, uint256 matronId, uint256 sireId, uint256 genes);

    /// @dev Transfer event as defined in current draft of ERC721. Emitted every time a kitten
    ///  ownership is assigned, including births.
    event Transfer(address from, address to, uint256 tokenId);

    /*** DATA TYPES ***/

    /// @dev The main Kitty struct. Every cat in CryptoKitties is represented by a copy
    ///  of this structure, so great care was taken to ensure that it fits neatly into
    ///  exactly two 256-bit words. Note that the order of the members in this structure
    ///  is important because of the byte-packing rules used by Ethereum.
    ///  Ref: http://solidity.readthedocs.io/en/develop/miscellaneous.html
    struct Kitty {
        // The Kitty's genetic code is packed into these 256-bits, the format is
        // sooper-sekret! A cat's genes never change.
        uint256 genes;

        // The timestamp from the block when this cat came into existence.
        uint64 birthTime;

        // The minimum timestamp after which this cat can engage in breeding
        // activities again. This same timestamp is used for the pregnancy
        // timer (for matrons) as well as the siring cooldown.
        uint64 cooldownEndBlock;

        // The ID of the parents of this kitty, set to 0 for gen0 cats.
        // Note that using 32-bit unsigned integers limits us to a "mere"
        // 4 billion cats. This number might seem small until you realize
        // that Ethereum currently has a limit of about 500 million
        // transactions per year! So, this definitely won't be a problem
        // for several years (even as Ethereum learns to scale).
        uint32 matronId;
        uint32 sireId;

        // Set to the ID of the sire cat for matrons that are pregnant,
        // zero otherwise. A non-zero value here is how we know a cat
        // is pregnant. Used to retrieve the genetic material for the new
        // kitten when the birth transpires.
        uint32 siringWithId;

        // Set to the index in the cooldown array (see below) that represents
        // the current cooldown duration for this Kitty. This starts at zero
        // for gen0 cats, and is initialized to floor(generation/2) for others.
        // Incremented by one for each successful breeding action, regardless
        // of whether this cat is acting as matron or sire.
        uint16 cooldownIndex;

        // The "generation number" of this cat. Cats minted by the CK contract
        // for sale are called "gen0" and have a generation number of 0. The
        // generation number of all other cats is the larger of the two generation
        // numbers of their parents, plus one.
        // (i.e. max(matron.generation, sire.generation) + 1)
        uint16 generation;
    }

    /*** CONSTANTS ***/

    /// @dev A lookup table indicating the cooldown duration after any successful
    ///  breeding action, called "pregnancy time" for matrons and "siring cooldown"
    ///  for sires. Designed such that the cooldown roughly doubles each time a cat
    ///  is bred, encouraging owners not to just keep breeding the same cat over
    ///  and over again. Caps out at one week (a cat can breed an unbounded number
    ///  of times, and the maximum cooldown is always seven days).
    uint32[14] public cooldowns = [
        uint32(1 minutes),
        uint32(2 minutes),
        uint32(5 minutes),
        uint32(10 minutes),
        uint32(30 minutes),
        uint32(1 hours),
        uint32(2 hours),
        uint32(4 hours),
        uint32(8 hours),
        uint32(16 hours),
        uint32(1 days),
        uint32(2 days),
        uint32(4 days),
        uint32(7 days)
    ];

    // An approximation of currently how many seconds are in between blocks.
    uint256 public secondsPerBlock = 15;

    /*** STORAGE ***/

    /// @dev An array containing the Kitty struct for all Kitties in existence. The ID
    ///  of each cat is actually an index into this array. Note that ID 0 is a negacat,
    ///  the unKitty, the mythical beast that is the parent of all gen0 cats. A bizarre
    ///  creature that is both matron and sire... to itself! Has an invalid genetic code.
    ///  In other words, cat ID 0 is invalid... ;-)
    Kitty[] kitties;

    /// @dev A mapping from cat IDs to the address that owns them. All cats have
    ///  some valid owner address, even gen0 cats are created with a non-zero owner.
    mapping (uint256 => address) public kittyIndexToOwner;

    // @dev A mapping from owner address to count of tokens that address owns.
    //  Used internally inside balanceOf() to resolve ownership count.
    mapping (address => uint256) ownershipTokenCount;

    /// @dev A mapping from KittyIDs to an address that has been approved to call
    ///  transferFrom(). Each Kitty can only have one approved address for transfer
    ///  at any time. A zero value means no approval is outstanding.
    mapping (uint256 => address) public kittyIndexToApproved;

    /// @dev A mapping from KittyIDs to an address that has been approved to use
    ///  this Kitty for siring via breedWith(). Each Kitty can only have one approved
    ///  address for siring at any time. A zero value means no approval is outstanding.
    mapping (uint256 => address) public sireAllowedToAddress;

    /// @dev The address of the ClockAuction contract that handles sales of Kitties. This
    ///  same contract handles both peer-to-peer sales as well as the gen0 sales which are
    ///  initiated every 15 minutes.
    SaleClockAuction public saleAuction;

    /// @dev The address of a custom ClockAuction subclassed contract that handles siring
    ///  auctions. Needs to be separate from saleAuction because the actions taken on success
    ///  after a sales and siring auction are quite different.
    SiringClockAuction public siringAuction;

    /// @dev Assigns ownership of a specific Kitty to an address.
    function _transfer(address _from, address _to, uint256 _tokenId) internal {
        // Since the number of kittens is capped to 2^32 we can't overflow this
        ownershipTokenCount[_to]++;
        // transfer ownership
        kittyIndexToOwner[_tokenId] = _to;
        // When creating new kittens _from is 0x0, but we can't account that address.
        if (_from != address(0)) {
            ownershipTokenCount[_from]--;
            // once the kitten is transferred also clear sire allowances
            delete sireAllowedToAddress[_tokenId];
            // clear any previously approved ownership exchange
            delete kittyIndexToApproved[_tokenId];
        }
        // Emit the transfer event.
        Transfer(_from, _to, _tokenId);
    }

    /// @dev An internal method that creates a new kitty and stores it. This
    ///  method doesn't do any checking and should only be called when the
    ///  input data is known to be valid. Will generate both a Birth event
    ///  and a Transfer event.
    /// @param _matronId The kitty ID of the matron of this cat (zero for gen0)
    /// @param _sireId The kitty ID of the sire of this cat (zero for gen0)
    /// @param _generation The generation number of this cat, must be computed by caller.
    /// @param _genes The kitty's genetic code.
    /// @param _owner The inital owner of this cat, must be non-zero (except for the unKitty, ID 0)
    function _createKitty(
        uint256 _matronId,
        uint256 _sireId,
        uint256 _generation,
        uint256 _genes,
        address _owner
    )
        internal
        returns (uint)
    {
        // These requires are not strictly necessary, our calling code should make
        // sure that these conditions are never broken. However! _createKitty() is already
        // an expensive call (for storage), and it doesn't hurt to be especially careful
        // to ensure our data structures are always valid.
        require(_matronId == uint256(uint32(_matronId)));
        require(_sireId == uint256(uint32(_sireId)));
        require(_generation == uint256(uint16(_generation)));

        // New kitty starts with the same cooldown as parent gen/2
        uint16 cooldownIndex = uint16(_generation / 2);
        if (cooldownIndex > 13) {
            cooldownIndex = 13;
        }

        Kitty memory _kitty = Kitty({
            genes: _genes,
            birthTime: uint64(now),
            cooldownEndBlock: 0,
            matronId: uint32(_matronId),
            sireId: uint32(_sireId),
            siringWithId: 0,
            cooldownIndex: cooldownIndex,
            generation: uint16(_generation)
        });
        uint256 newKittenId = kitties.push(_kitty) - 1;

        // It's probably never going to happen, 4 billion cats is A LOT, but
        // let's just be 100% sure we never let this happen.
        require(newKittenId == uint256(uint32(newKittenId)));

        // emit the birth event
        Birth(
            _owner,
            newKittenId,
            uint256(_kitty.matronId),
            uint256(_kitty.sireId),
            _kitty.genes
        );

        // This will assign ownership, and also emit the Transfer event as
        // per ERC721 draft
        _transfer(0, _owner, newKittenId);

        return newKittenId;
    }

    // Any C-level can fix how many seconds per blocks are currently observed.
    function setSecondsPerBlock(uint256 secs) external onlyCLevel {
        require(secs < cooldowns[0]);
        secondsPerBlock = secs;
    }
}

Code Critique - CryptoKitties

Comments

  • Fascinating. I wasn't familiar with ERC-721 and "cryptocollectables." I also found a few extra resources here:

    I had encountered collectible digital objects before in the form of NeonMob and its collectable digital card packs -- which was a closed-platform approach to creating scarcity and ownership enforced by a central authority, rather than a distributed blockchain approach... I believe started sometime ~2011-2015.


    I found these two comment from the code particularly interesting:

    Note that ID 0 is a negacat, the unKitty, the mythical beast that is the parent of all gen0 cats. A bizarre creature that is both matron and sire... to itself! Has an invalid genetic code. In other words, cat ID 0 is invalid... ;-)

    Note that using 32-bit unsigned integers limits us to a "mere" 4 billion cats. This number might seem small until you realize that Ethereum currently has a limit of about 500 million transactions per year! So, this definitely won't be a problem for several years (even as Ethereum learns to scale).

    I think it is interesting how the anthropomorphized transactions (the kitties) are articulated in terms of generational reproduction and sexual dimorphism, raising similar problems that you encounter in religious narrative (the problem of the origin of man or of species, the possibility and conditions of end times). So this implementation is partly about prototyping conditions for non-fungible collectible value on the blockchain. But it also articulates something about the conditions of that value -- its origins, and its limits. Linked data structures may have special handling required for the root node(s), with origins as a special case. Similarly, questions of depth (how many levels, how many generations) or identity (how many unique objects, what size namespace?) often come up when you create something that appears to be designed to extend or branch arbitrarily or indefinitely. Those conditions arise merely by deciding to define cats with a uint32 -- nothing fancy. But within the context of a use case, and wrapped in a narrative, even things as simple as the bit width of a variable or the underlying implementation of a linked list can enable stories, and stories in turn may drive financial speculation or obsessive collecting -- because (and not in spite of) their uneven awareness of how things begin and end.

    "[I]n 1981, making those decisions, I felt like I was providing enough freedom for 10 years. That is, a move from 64k to 640k felt like something that would last a great deal of time." -- Bill Gates, speech to the Computer Science Club at the University of Waterloo, 1989

  • @jeremydouglass said:
    I think it is interesting how the anthropomorphized transactions (the kitties) are articulated in terms of generational reproduction and sexual dimorphism

    Cryptokitties seems to be of two minds about sexual dimorphism. On the one hand, in the breeding tutorial, it only lets you pick one of the two cryptokitties (the pink and white one) as the mother. But looking at the code @meredith.noelle has posted above, while the kitty has a fixed relationship with its parents as matron and sire, there's nothing to suggest the cryptokitty itself has a gender property, even after it has itself been mated. In their Terms of Use, it appears cryptokitties can change gender:

    CryptoKitties have no fixed gender, which means that in any particular breeding transaction, the CryptoKitties’ owners can choose which cat will be the sire, and which will be the matron. When the new kitten is born, it will always be assigned to the owner of the matron cat. The breeding of two CryptoKitties will always produce a kitten.

    The KittyBreeing.sol file sets out rules about what is allowable for breeding. This also seems to treat gender only in terms of the act of breeding, with a cryptokitty serving as the role of sire or matron for that particular transaction. It also includes rules about incest, but only of the parent/child or sibling variety. Here is the function responsible

    /// @dev Internal check to see if a given sire and matron are a valid mating pair. DOES NOT
    ///  check ownership permissions (that is up to the caller).
    /// @param _matron A reference to the Kitty struct of the potential matron.
    /// @param _matronId The matron's ID.
    /// @param _sire A reference to the Kitty struct of the potential sire.
    /// @param _sireId The sire's ID
    function _isValidMatingPair(
        Kitty storage _matron,
        uint256 _matronId,
        Kitty storage _sire,
        uint256 _sireId
    )
        private
        view
        returns(bool)
    {
        // A Kitty can't breed with itself!
        if (_matronId == _sireId) {
            return false;
        }
    
        // Kitties can't breed with their parents.
        if (_matron.matronId == _sireId || _matron.sireId == _sireId) {
            return false;
        }
        if (_sire.matronId == _matronId || _sire.sireId == _matronId) {
            return false;
        }
    
        // We can short circuit the sibling check (below) if either cat is
        // gen zero (has a matron ID of zero).
        if (_sire.matronId == 0 || _matron.matronId == 0) {
            return true;
        }
    
        // Kitties can't breed with full or half siblings.
        if (_sire.matronId == _matron.matronId || _sire.matronId == _matron.sireId) {
            return false;
        }
        if (_sire.sireId == _matron.matronId || _sire.sireId == _matron.sireId) {
            return false;
        }
    
        // Everything seems cool! Let's get DTF.
        return true;
    }
    

    Apparently, this has led to a large number of other types of inbreeding, with, for instance, 17,779 of 1,000,000 cryptokitties resulting from grandparent/grandchild relations.

  • @Temkin said:
    Cryptokitties seems to be of two minds about sexual dimorphism.

    Interesting -- choosing-genders-dynamically-for-each-reproduction model recalls Ursula K. Le Guin's The Left Hand of Darkness: Estraven is a father, but could also be a mother.

  • Also interesting to think about how CrytoKitties is (Etherium) patform-specific. As an odd point of comparison on the Bitcoin blockchain is the Rare Pepe meme and Pepe Cash.

    As I understand it, first was the Rare Pepe meme on 4chan, parodying the possibility of "rare" or "collectable" digital art such as forum gifs. This led to a somewhat trolling interview with (digital) art world appraisers / curators about the potential value of an original rare digital image[1] (2015). The interviewees largely responded that there is nothing inconceivable or ridiculous about it per se. The meme community response (?) was participating in an implementation of non-fungible Pepe jpgs and gifs stored on the Bitcoin blockchain.[2][3]

    So a parody of the possibility of digital collectable art became a semi-trolling interview about the future of the same, followed by a parodic(?) but fully working implementation of secure value (complete with digital wallets) but centered around the Pepe parody content. In a 2018 thesis on blockchain and "provably rare" crypto art wiith one focus on Pepe Cash[4], Blake Finucane argues that "crypto art reveals and critiques the framework of the art market" and that this is anti-institutional, democratizing, and good for artists / for art. In another contemporaneous write-ups on financial speculation in Rare Pepes ("People are paying thousands of dollars to own pictures of Pepe the Frog")[5 one surprise is that in 2018 this had become ~$1.2 million in transactions with "a roughly $37 million market cap."

    This is all the more remarkable because Pepe memes have been popularly associated with white supremacy and hate speech,[6] despite the artist's legal efforts and even though the ADL hate symbol listing for Pepe the Frog is careful to point out that "because so many Pepe the Frog memes are not bigoted in nature, it is important to examine use of the meme only in context."

    These contexts make me wonder whether CrytpoKitties is Pepe-aware, and, whether or not it is, if it attempting to be memetic and yet apolitical through echoing one of the most established and enduringly popular of internet meme genres: cats memes.


    1. Notopoulos, Katie. "We asked the art world how much rare Pepes are going for." Buzzfeed News. October 24 (2015): 2015. ↩︎

    2. Rare Pepe Directory: Rare Pepes on the Bitcoin Blockchain. http://rarepepedirectory.com/ ;↩︎

    3. Rare Pepe Fomo And The Alt Pump & Dump Cycle. October 9, 2016. http://shitco.in/2016/10/09/rare-pepe-fomo-and-the-alt-pump-dump-cycle/ ;↩︎

    4. Finucane, Blake Patricia. Creating with blockchain technology: the" provably rare" possibilities of crypto art. Diss. University of British Columbia, 2018. https://open.library.ubc.ca/cIRcle/collections/ubctheses/24/items/1.0370991 ;↩︎

    5. Roeder, O. "People are paying thousands of dollars to own pictures of Pepe the Frog. FiveThirtyEight." (2018). https://fivethirtyeight.com/features/pepe-the-frog-symbolism-cryptoart-blockchain/ ;↩︎

    6. Nuzzi, Olivia. "How Pepe the frog became a Nazi Trump supporter and alt-right symbol." The Daily Beast 26.05 (2016): 2016. https://www.thedailybeast.com/how-pepe-the-frog-became-a-nazi-trump-supporter-and-alt-right-symbol ;↩︎

  • I love this - how the metaphor of (cat, pepe) reflects something about the community that creates it. The opposite of everything is contingent of SR - nothing is contingent.

  • Roundups on "CryptoGaming" are also in the news -- a recent example is "Crypto Games, a Market in the Making of Its Own Fortune."[1] One of the interesting things about this framing for CryptoKitties is that, rather than reading it primarily a currency, or as fine art, or as a speculative collectable (Beanie Babies, tulip mania) -- and instead focuses on it as infrastructure for gameplay experiences along the lines of collectable card games. That is certainly how the CryptoKitties homepage presents itself, as a digital ownership-backed play experience:

    It is also interesting to think about CryptoKitties in the context of conspicuous consumption in leisure -- for example, the design pattern in Free-to-Play game markets to focus on capturing and monetizing a few "whale" super-spending players. Perez points back to an article from last year about a non-fungible token (NFT) unique car for the racing game F1 Delta Time that sold for 415.9 ETH (then ~$100,000 USD)[2] -- complete with accusations of rigged bidding / price manipulation. Some aspects of this form of performative conspicuous consumption resemble art markets -- for example, roundups ranking the most expensive NFT sales of the year -- however some are very directly speculative in game terms. For example, ni the case of Gods Unchained, some of those purchased NFTs in the tens of thousands of dollars may confer advantages in a competition for hundreds of thousands of dollars of prize money.


    1. Perez, Elena. "Crypto Games, a Market in the Making of Its Own Fortune." Cointelegraph.com, Feb 17, 2020. https://cointelegraph.com/news/crypto-games-a-market-in-the-making-of-its-own-fortune ;↩︎

    2. Heal, Jordan. "F1 Delta Time’s first NFT sells for more than $100,000." Yahoo! Finance. May 28, 2019. https://finance.yahoo.com/news/f1-delta-time-first-nft-160028496.html ;↩︎

Sign In or Register to comment.