StreamHacker

Search
Skip to content
  • About
  • NLTK Cookbook
  • NLTK Demos
contracts

Creyzies Contract Review

April 23, 2022 Jacob Leave a comment

Creyzies is the official companion collect to mfers, by Reylarsdam. Every mfers holder received an equivalent amount of Creyzies in a surprise airdrop on 4/20/2022, with all expenses paid by Sartoshi, the creator of mfers.

The contract was written by West Coast NFT, who also wrote the mfers contract. Let’s take a look at the code, which begins with a giant ascii creyzie.

Airdrop

    struct SendData {
        address receiver;
        uint256 amount;
    }

 ...

    function airdrop(SendData[] calldata sendData) external onlyRole(SUPPORT_ROLE) nonReentrant {
        uint256 ts = baseContractAddress.totalSupply();

        // loop through all addresses
        for (uint256 index = 0; index < sendData.length; index++) {
            require(totalSupply() + sendData[index].amount <= ts, 'Exceeds original supply');
            _safeMint(sendData[index].receiver, sendData[index].amount);
        }
    }

The airdrop function receives an array of SendData structs, each of which specifies a receiver address and an amount of tokens to airdrop. Before this function was called, the devs did a snapshot of all mfers owner addresses, and counted how many mfers were owned by each address, to create this SendData array. Then they called this function 14 times to perform the airdrop, for a total gas cost of ~15 ETH. Sartoshi deposited 20 ETH to fund the airdrop, then withdrew 5 ETH when the airdrop was complete.

The baseContractAddress is the mfers contract address, which you can see in the Constructor Arguments below the code. So the first line of the function gets the total number of mfers from the mfers contract. Then, looping through the array of SendData, there’s a requirement that the current supply + the amount to be minted is less than or equal to the total mfers supply. The amounts should have been calculated correctly during the snapshot, but this is a good way to enforce the supply limit. Finally, the amount of tokens is minted to the receiver address. The Creyzies contract uses ERC721A, so minting multiple tokens at a time is very efficient.

There’s no token ownership checks at mint time, so this does all assume that the SendData was calculated correctly from a snapshot. Some addresses were explicitly left out of the airdrop – these were known smart contract addresses such as a Gnosis Safe or NiftyGateway. The devs knew the airdrop may not work for them, so they setup a claim site and implemented a redeem function.

Redeem

    function setUnclaimedTokenIds(uint256[] calldata tokenIds) external onlyRole(SUPPORT_ROLE) {
        for (uint256 index = 0; index < tokenIds.length; index++) {
            unclaimedTokenIds[tokenIds[index]] = true;
        }
    }

    function redeem(uint256[] calldata tokenIds) external nonReentrant {
        uint256 numberOfTokens = tokenIds.length;

        for (uint256 index = 0; index < numberOfTokens; index++) {
            require(unclaimedTokenIds[tokenIds[index]], 'Token has already been claimed');

            try baseContractAddress.ownerOf(tokenIds[index]) returns (address ownerOfAddress) {
                require(ownerOfAddress == msg.sender, 'Caller must own NFTs');
            } catch (bytes memory) {
                revert('Bad token contract');
            }

            unclaimedTokenIds[tokenIds[index]] = false;
        }

        uint256 ts = baseContractAddress.totalSupply();
        require(totalSupply() + numberOfTokens <= ts, 'Exceeds original supply');

        _safeMint(msg.sender, numberOfTokens);
    }

Sartoshi posted claim instructions to redeem unclaimed tokens that were not airdropped. setUnclaimedTokenIds was called immediately after the airdrop, and sets a flag to true in unclaimedTokenIds. Then, when an owner goes to creyzies.art to claim their token(s), the redeem function is called. For each token attempting to be redeem, the function checks that

  1. The token hasn’t been claimed already
  2. The redeemer (msg.sender) owns the corresponding mfers token.

If those checks pass, the token is marked as claimed. But before it is minted, there is one final check that minting these tokens won’t exceed the total mfers supply. The devs definitely put some extra effort into the contract to double check their work, to ensure that Creyzies tokens went to the right addresses and never exceeded mfers supply.

Royalties

Often, royalties from sales are handled by secondary marketplaces, using info from royaltyregistry.xyz. However, this contract implements ERC2981, which defines a royalty standard specified in the contract. Like with mfers, the default royalties are set fairly low, to 2.5%. The royalties are sent to 0x750314177EF0a319DCdC959599C76D63964729f1, which appears to be another contract that splits the royalties automatically.

Provenance

Like with The Picaroons, a provenance hash was set before minting, to 3307bc0bd06029bba4a826856f2e19db62d6df5b7f70d447fa5262117256c46c. They also published a page proving the fairness of the drop.

Token URI

While some NFTs exist fully on chain, most are token numbers in a contract that point to a URI. This URI points to metadata, which is where to find the NFT image and properties. For mfers, the token URI is on ipfs, a distributed filesystem, where the JSON metadata for each mfers token is stored. Creyzies, though, currently points to a web app on heroku.

The token URI for Creyzies #1 is https://minting-pipeline-10.herokuapp.com/1

This is not great, because it means that the Creyzies metadata depends on a Heroku app running correctly and being available. ipfs, being distributed, is a more decentralized and resilient location, and any files stored there can’t be changed without changing the URI. Metadata being served from a web app can be changed very easily. Hopefully there’s a plan to put all the metadata into ipfs as well; the images are there already. If that is done correctly, then the devs can call setBaseURI with the new ipfs URI, just like they did to set the heroku base URI. However, if there is some other surprise planned that requires changing the metadata, then keeping the token URIs pointing to a web app make sense.

    function setBaseURI(string memory baseURI_) external onlyRole(SUPPORT_ROLE) {
        _baseURIextended = baseURI_;
    }

    /**
     * @dev See {ERC721-_baseURI}.
     */
    function _baseURI() internal view virtual override returns (string memory) {
        return _baseURIextended;
    }

Slither Analysis

In order to make slither work this time, I had to tweak the code for one of the dependencies, crytic-compile. Using my branch, you can run slither to analyze the contract.

$ slither 0x19bb64b80cbf61e61965b0e5c2560cc7364c6546 --print human-summary

The issues can all be seen by running slither 0x19bb64b80cbf61e61965b0e5c2560cc7364c6546. I think the way this contract calls baseContractAddress.ownerOf within a try-catch block might be a little unusual, since slither says the return value is unused, when that’s not really the case. The other medium issues are within ERC721A._mint, and look ignorable. However there is an issue about costly operations inside a loop within ERC721A._mint, for _currentIndex = updatedIndex. This is within an unchecked block, which is the same thing that the Counters library does. The Azuki gas optimization analysis was pretty extensive, so there may not be any more that can be done to optimize gas in this case.

Conclusion

This is a very nice & unexpected gift to all mfers holders, and it looks like the devs did excellent work to ensure accuracy and minimize airdrop costs. The Crezies art is cc0 just like mfers, and it’s definitely worth checking out more of Rey’s work.

Share this:

  • Twitter
  • Reddit
  • Facebook
airdropazukierc2981erc721erc721aethipfsjsonmetadatamfersnftprovenanceroyaltiesslithersoliditytoken

Post navigation

Previous PostPicaroons Contract ReviewNext PostDelegate.cash Contract Review

Subscribe Here

Subscribe via RSSSubscribe via EmailSubscribe via Twitter

Popular Posts

  • Text Classification for Sentiment Analysis - Naive Bayes Classifier
  • Text Classification for Sentiment Analysis - Eliminate Low Information Features
  • Fuzzy String Matching in Python
  • Text Classification for Sentiment Analysis - Stopwords and Collocations
  • Python Point-in-Polygon with Shapely
  • Using word2vec with NLTK
  • Text Classification for Sentiment Analysis - Precision and Recall
  • Chunk Extraction with NLTK

Recent Posts

  • Delegate.cash Contract Review
  • Creyzies Contract Review
  • Picaroons Contract Review
  • Dead mfers Contract Review
  • 3D Mutant Mfers Contract Review

Weotta be Hacking

Python NLTK Cookbook

Python 3 Text Processing with NLTK 3 Cookbook

Bad Data Handbook

Bad Data Handbook

Code

  • NLTK-Trainer
  • Presentations

Sites

  • Fletch
  • Text Processing

Post Categories

  • blog (2)
  • books (10)
  • contracts (6)
  • design (6)
  • erlang (11)
  • insight-engines (2)
  • javascript (6)
  • links (23)
  • papers (2)
  • programming (13)
  • python (62)
  • security (1)
  • talks (3)
  • Uncategorized (2)
  • weotta (4)

Tag cloud

authentication bayes chunking classification database design django doctest erc721 erlang eth fab feature extraction forms javascript jquery json machinelearning make mercurial mfers mint mnesia mongodb nft nginx nlp nltk nose otp parsing performance python redis security sentiment slither solidity statistics tagging templates testing token ui unittest
Proudly powered by WordPress