Hello all. I often “live tweet” my readings of articles by pulling quotes I find interesting or thoughtful and occasionally adding commentary. One recent example is here:
The issue with using twitter for these undertakings is that I delete tweets on a schedule and with it this actually useful content. So, I’ve decided to change things up a bit.
I have been attempting to learn more about the web3 ecosystem because I’m a technology nerd and dislike basing my opinions on what others tell me to think. While I’ve read short form articles, a few longer introductory posts and bookmarked many more resources, I always like to base my technological inquiry and understanding in fundamental documents. Inside web3, one such type of document is what’s known as a “smart contract”.
In my current understanding, smart contracts are actually named relatively aptly. They govern the technological underpinnings of transactions that take place on “the blockchain” (different contracts are used for different blockchains). In essence, they state the terms and perform the exchange of value – I will pay $10 for this funny cat picture, the photographer gets $1 and the website that sends me the picture to my email will get $.50.
Of course, I intend to learn more about smart contracts in general by reading one specifically. So I’ve decided this will be my live-blog of my initial read through of the Crypto Coven smart contract.
Crypto Coven – https://cryptocoven.xyz/ – is, in my opinion, an incredible project for many reasons, primary among them the thoughtfulness and technical expertise of the deployment, to say nothing of the stunningly beautiful artwork and robust community. It’s inspired an idea for a side project which I am not yet technically proficient enough to undertake, hence the reading of the contract. So, let’s get into it!
Article link: https://cryptocoven.mirror.xyz/A622VSRm8-9oLzc8l3oFGmfnFUZQmDQ3Wx3ObhSlhsc
Xuannü begins with stating some of what I’ll call the “tech stack” of this project a)the type of smart contract: ERC-721 and her (their? double check pronouns before publish) scripting tool: Solidity. I have seen Solidity mentioned before in other articles and to my understanding it is both a host of smart contracts (like a website) as well as a way to write them (like a programming language e.g. CSS/html). I will follow up about ERC-721 contracts specifically after reading this to see what other resources might be helpful.
“write code on the blockchain” – this is the most concise explanation of what a smart contract is that I’ve ever seen.
“we could have just used a platform like OpenSea or Zora rather than develop a contract from scratch—but we wanted to write our own” – this helps me contextualize those services, which I’ve seen mentioned before.
“Having our own contract would enable us to design the entire experience around the summoning of a WITCH” – which is why I want to read this explainer before I try to pseudo code my side project.
Link to ethereum development docs – https://ethereum.org/en/developers/docs/ – bookmarked for myself for later
“A smart contract is a program that lives and stores its data on the blockchain, typically with APIs to interact with its contents. Poking around contracts on Etherscan makes that much clearer; you can see the entirety of the code for a verified contract, as well as call read and write functions on them.” emphasis my own – smart contract execute functions inside the blockchain; I wonder what functions exist natively (e.g. what about sort? getting dates/times? etc).
“Most NFTs are just pointers: a blockchain-based database entry that points to content somewhere else on the internet.” – difference between NFTs and crypto currencies – NFTs are unique, crypto is not
“there is absolutely nothing in the smart contract that defines what makes a WITCH—there are just arbitrary IDs (1 through 9,999). Everything about a particular witch exists at the URI at which the smart contract points. That meant that the surrounding infrastructure we needed to write was more fragile and more complex, but the work of writing the contract was relatively straightforward.” — had to google URI, from Wikipedia “unique sequence of characters that identifies a logical or physical resource used by web technologies. URIs may be used to identify anything, including real-world objects, such as people and places, concepts, or information resources such as web pages and books”
“browsing other projects’ contracts on Etherscan helped to see countless examples of different implementations and feature sets.” — necessary research for successfully doing side project
“This space is still early, and [many of people are new] —which means that it’s important to never assume that even a well-known project necessarily has good code.” – I’ve never thought ANY code was good and I certainly don’t intend to start now.
List of desired functions an important cornerstone of any project
can limit sales-per-wallet; a cool feature
“Storing a whole list of addresses just to check whether a given address should be able to mint was a really inefficient use of on-chain storage. Instead, we could use a Merkle tree and only store the root of the tree.” – Merkle trees are thus far over my head but the idea of gas/chain storage being the limitation you are always coding against makes sense
“In a “true” airdrop, the project takes on the cost of gas and directly sends tokens to recipients. In a claim drop, the recipient covers gas.” – good to know!
“we overcomplicated the contract by deploying it with multiple gifting implementations.” – the programmer’s honeypot
[while browsing ERC-721 contracts “where are the royalties?] ; “marketplaces like OpenSea had off-chain implementations, and the one on-chain royalty standard (EIP-2891) that existed was strictly opt-in.”
“In other words, royalties (an oft-touted benefit of NFTs for artists!) only existed at the discretion of third-party implementations that decided to respect them.”
“allowing gasless listings. Oizys was able to piece together what we needed to do to enable the functionality: override the isApprovedForAll()
function to allowlist the OpenSea proxy registry address.” — shout out to this tech WITCH for the find & grateful to the team for sharing the trick ❤
“we needed some way to slowly unveil new WITCHES and ensure that minters received a random WITCH (rather than competing to snipe the most desirable WITCHES).” – my side project would also respect this as I think it is a super neat part of the Crypto Coven project
“IPFS (a distributed, peer-to-peer file storage) works by using content-addressed storage—in other words, all files that live on IPFS are hashed, producing a unique identifier based on the specific content of that file, and that determines its “address” on IPFS.” – oh fuck, how will I host side project files? Probably need to discuss with Michael
“we had to be able to update the base URI rather than have it hardcoded into the contract.” — this is the crux of the issue, the contract has a pointer to the URI rather than the data (which also respects the size limitations)
TIL “gwei” : a denomination of the cryptocurrency ether (ETH), the digital coin used on the Ethereum network.
“Underwhelmingly, sequentially minting with a for
loop turned out to be the most gas-efficient option.” – we love when other scientists do the boring trials and the rest of us get to use their data; thank you again to the community for this knowledge
“We needed to be able to easily write tests for the contract to ensure that we didn’t break anything as we made changes.” – the quality assurance nerds have logged on, which is great for us, because they are always correct
“once all the WITCHES were revealed, we wanted to add a verification hash for all the images (which are currently hosted on S3) so that anyone could confirm in the future that they hadn’t been modified.” – I have no idea what this means and I’m leaving that rabbit hole for another day
“there were a few bugs in the minting UI (notably, that the addresses for the community sale were case-sensitive)” — fuck are computers bad
“It was our first post-deployment panic over a bug in immutable code; it wouldn’t be the last.” the idea of ‘immutable code’ makes me shudder BUT it’s not like physical objects aren’t more or less immutable sans what can easily be edited; idk maybe making software more like hardware will eventually make it suck less?? random thoughts here
“As I read, the situation became clearer, but only to our detriment.” — oh man we’ve all been there
“There was one crucial error in this code and then one design choice that made it irreparable.” – ahh the snowball of “one bad decision begets another” and the realization of tech debt you yourself created, we hate to see it!!!
“First, we included a check for the per-wallet limit in a for
loop and relied on the iterator to assign IDs—which meant that IDs could be skipped, not a desirable behavior under any circumstance.” – this to me feels like a not-good sign and is a great warning for an issue to be aware of in any other projects. also if it’s purposeful architecture it could also be a great way to do malfeasance and say “it wasn’t us!! it was the COMPUTER!!” — I do NOT think Crypto Coven did that but something I wonder if I could spot in the many scam projects currently out there ?
“Second, we were using totalSupply()
to assign token IDs because we had assumed that the total supply would always be equal to the last token ID we had minted.” oh man this logic error, again computers are so bad and case sensitive and just the fussiest of amalgamations of metals. i would have totally made this assumption myself also, it feels so ~right~
“It was an infuriatingly simple bug, one that could have been easily fixed if we had thought to test that edge case.” – the one perspective about software bugs is at least no one blew up on a space shuttle because someone didn’t do QA
“the contract was trapped in this state, unable to ever mint again” – again, at least the worst case in a software failure isn’t USUALLY death. but I feel so bad for the folks who worked so hard on this. I bet this was a verrrryyyyy shit day
“Could we burn all the tokens after the first gap in IDs to reset it to a pre-broken state? Could we deploy a new contract but have it delegate back to the v1 contract for tokens that had already been minted? Redeploying the contract and re-minting all the tokens that had already been minted felt prohibitively expensive—it could exhaust all the funds we had earned from the mint thus far.” — ahh, technology, you cruel mistress
“one Discord user named mersenne inquired about the nature of the bug. They had noticed another potential issue in the contract upon reading it on Etherscan: namely, that the community sale could be drained if a malicious user who had added their address to our community list minted three WITCHES, transferred them to another wallet, and then repeated the process.
In a frankly implausible stroke of luck, this random person who had only just wandered into the Discord an hour earlier turned out to be Matthew Di Ferrante, an experienced Solidity auditor and developer who had been contributing to the Ethereum ecosystem for years.” – I have been the “oh, shit, I need [specific help]” person and I have been the “oh, shit, I know how to do [specific help]” person and it never gets less magical. The internet baby!! The world of information and ideas that connects us all!! We honestly love to see it.
“Oizys and I turned our attention to the new contract. We now had an opportunity to improve it, not just push out the fixes for the two issues we had discovered.” — classic technology ‘knowing what I know now, this is gonna suck less! that’s improvement, baby!!’
“So we had our order of business: patch the bugs, roll over the mints from the v1 contract, do a proper audit, make any additional improvements we wanted, and then redeploy.” – love a game plan
“we chose to remove the per-wallet check—it didn’t make sense to us that we would no longer be able to gift a WITCH to someone if they had already minted three on their own.” totally makes sense and also makes that code way more readable, deleting code is a truly great technology high
“the higher-level problem was how we were assigning token IDs—using totalSupply()
made it unnecessarily fragile. Beyond that, Daniel McCartney (a skilled arcanist and friend upon whom we had called to review the code) observed that we were calling _safeMint()
in five different places, calculating the correct token ID in each place. He suggested extracting the logic into a nextTokenId()
method we could call instead.” – _safeMint seems like too powerful to be used to name something, as you want to separate out the logic for MINTING and NUMBERING. Abstracting away the numbering into different logic totally makes sense.
“OpenZeppelin’s Counters
library” – in this house we love open source libraries
“the potential community sale exploit [tweak was simple]; instead of just requiring that msg.sender
had fewer than three WITCHES in their wallet at the time, I added a mapping that tracked community sale mints by address and checked against that count.” – this is not trivial logic or code, I’m in a little over my head here and very impressed!
rolling over the void witches seems super easy, that’s cool that’s a well supported use case
“the v2 contract included five key improvements over v1” – cannot wait to be in over my head again; hold please
“clear mental model of what kinds of computations cost more or less gas, …. we decided to cut ERC721Enumerable
in favor of off-chain implementations”
ok I understand this section about gas in a theoretical way but don’t get the details. Using ERC721Enumerable was standard in many contracts but did Expensive Math. That’s my summary. Need to do more research but that’s a C-tier rabbit hole for now.
“we considered reentrancy as an attack vector […] it was possible for contracts to call back into ours, sidestepping the checks we had put in place.” – man fuck me, I’m gonna have to learn about smart contract attack vectors. why do I do this to myself? because I love computer and I also love to suffer
“_safeMint()
allowed reentrant calls back to the function” – I’m going to need to read the _safeMint() function documents because that seems to be the meat of many contracts and it also apparently quite complicated. gonna need a red bull for that one
“Updating the state before performing actions like minting handled the issue.” – handling state is one of my least favorite parts of writing or thinking about code, not going to lie that the idea of this is Not Exciting To Me
“use OpenZeppelin’s ReentrancyGuard
module, which offers a nonReentrant
modifier that prevents reentrant calls to functions. We took care to add guards even to onlyOwner
functions that only we could potentially exploit, as a measure to increase trust.” – neat!! and another link bookmarked
“Function modifiers are a declarative way to “modify” functions, with code that runs before or after a function call. They’re typically used to wrap checks, both for readability and reusability.” – search for this inside the Solidity docs and read the source material and see examples to understand. I get all these words in this order, but I get to the end of the sentence and my brain is twisted up
“With modifiers, the content of the mint()
function became trivial—three lines of code, with an easy-to-understand list of modifiers that applied to it.” — ohhhhhhhhhhhh nested functions are nested.
the entire section about Withdrawals doesn’t yet make sense to me, I think that I need to read some contracts. but “Without this function, we wouldn’t have been able to withdraw these royalties; they would be trapped in the contract forever.” – seems important!
“the OpenSea proxy registry approval we had enabled for gasless listings could be a vulnerability for the contract, in the event that OpenSea were ever compromised. If we thought there was a chance that Crypto Coven could become a truly long-term project, it would be wise to add a toggle that allowed us to disable access.” — threat modeling in web3 is blowing my mind
“in a world of code as long-lived (and an environment as adversarial) as Ethereum, it can be important to take the long view. Companies come and go; the blockchain is forever.” — so true, bestie!!
“I deployed the v2 contract, ran the script to generate the most up-to-date list of current owners of tokens from the v1 contract, and called the rollover function. Just like that, minting could resume. The next day, we updated the base URI for the original contract, replacing the WITCHES with the soul vessels. The transition was complete.” the massage you need after doing this is I assume a full eight hours
ooooh a video to watch, bookmarked! i love tech talks because as mentioned above i love to suffer
“no smart contract is perfect. The same is true for ours. The Crypto Coven contract that now lives on mainnet contains two known bugs,” – all code contains bugs or flaws in logic and the blockchain is an unforgiving medium to code on. just two is like, super duper impressive IMHO
“there are further refinements I’d make—for better on-chain composability, for even lower gas usage, for less fragile batch reveals, and so on. This domain is still just emerging; day by day, week by week, the collective wisdom evolves in novel and intriguing directions.” – maybe one day I will make a mistake that teaches other people how to do things better!! that’s tech work!!
“it is good to face shipping Solidity with apprehension. That’s how it should be. It’s not a responsibility to take lightly. Doing high-stakes work has high-stakes consequences, and once the die is cast, it cannot be undone. The choices you make, knowingly or unknowingly, have gravity.” – I bitch to Michael about this all the time. Maybe if regular ass software devs knew their shitty code has real consequences, they would test it literally at all. Prob not, but maybe!!!
“as arcanists, the most we can endeavor to do is our finest work—and no less.” — as an alchemist, it is my goal to keep getting lead closer and closer to gold. so excited to keep learning about this project ❤