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
- The token hasn’t been claimed already
- 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.

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.