<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Crypto Coven</title>
        <link>https://paragraph.com/@crypto-coven-2</link>
        <description>an immersive, narrative-driven experiment in world-building... the weird wilds await. https://cryptocoven.xyz/</description>
        <lastBuildDate>Thu, 13 Aug 2026 04:48:14 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>Crypto Coven</title>
            <url>https://storage.googleapis.com/papyrus_images/805a4aeed6cffd33815191047ca9bef1400f9646658757cba015d63a9ae867f8.png</url>
            <link>https://paragraph.com/@crypto-coven-2</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[Building the Narrator's Hut]]></title>
            <link>https://paragraph.com/@crypto-coven-2/building-the-narrator-s-hut</link>
            <guid>toHaiV2NxQ9OJlieDph5</guid>
            <pubDate>Mon, 19 Dec 2022 20:50:36 GMT</pubDate>
            <description><![CDATA[This All Hallow’s Eve, we cast open the doors to THE NARRATOR’S HUT—by far the most ambitious work we’ve ever crafted at Crypto Coven and, arguably, one of the most ambitious works in the NFT space. After the genesis collection of WITCHES, we ruminated on which part of the weird wilds (the world that Crypto Coven inhabits) we wanted to build first. Since our project has always been grounded in rich storytelling, we decided to launch a introductory project as the very first location: the home ...]]></description>
            <content:encoded><![CDATA[<p>This All Hallow’s Eve, we cast open the doors to <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://cryptocoven.xyz/map/narrators-hut">THE NARRATOR’S HUT</a>—by far the most ambitious work we’ve ever crafted at Crypto Coven and, arguably, one of the most ambitious works in the NFT space.</p><p>After the genesis collection of WITCHES, we ruminated on which part of the weird wilds (the world that Crypto Coven inhabits) we wanted to build first. Since our project has always been grounded in rich storytelling, we decided to launch a introductory project as the very first location: the home of our elusive NARRATOR. With it, a series of short stories about witches of olde, told by said narrator.</p><p>The brilliant <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/keridwen_eth">Keridwen</a> wrote five achingly poignant stories (about which you can read more in <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://cryptocoven.mirror.xyz/-etGqYLcwc919Rd94huoEEG8e-8v-J7z2U8IoRbLEvU">her own post</a>), and quickly, as our ideas often do under the wing of visionary <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/nyx_eth">Nyx</a>, the project grew into one much larger and grander.</p><p>What if these stories could be minted as magical objects (what we’ve called ARTIFACTS) imbued with the memories of their erstwhile owners?</p><p>What if they weren’t just static images, but richer multimedia assets?</p><p>What if people could interact with their metadata on chain?</p><p>And, the cherry on top, what if they could be tailored to each WITCH?</p><p>Back in February, I set out to pen a series of design docs for everything we needed to build:</p><ul><li><p>A series of audiovisual NFTs to hold animated art and voice-narrated stories</p></li><li><p>A point-and-click interface for interacting with and minting these NFTs</p></li><li><p>A smart contract with minting gated through a server</p></li></ul><p>We spent the better part of a year creating five (!!!) generative collections of multimedia NFTs, the immersive experience to bring the lore to life, and a set of contracts to stitch it all together. So here, witches, is a glimpse into it all.</p><h2 id="h-the-multimedia-storytelling-nfts" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Multimedia Storytelling NFTs</h2><p>Static images have largely been the dominant medium for NFTs since they’ve existed. To break out of that mold and showcase a new paradigm for what NFTs could be, we chose to explore a multimedia format.</p><p>With five short stories chronicling WITCHES of note and the desire to form them into a collection of art, we realized we could weave the two together—by turning them each into a magical object (ARTIFACT) that the subject of the story had once owned, with the narrated story as the echo of that WITCH. As Nyx put it, each ARTIFACT was like a seashell you could press against your ear to hear a faint impression of a memory.</p><p>So, then, what exactly is an ARTIFACT?</p><ol><li><p>A work of generative art, either “attuned to” a WITCH (if you have one) or random (if you don’t). An attuned ARTIFACT reads from the metadata of the WITCH who minted it and generates its own appearance to match.</p></li><li><p>An animated HTML page. Some of the ARTIFACTS, like the CROOK below, are even interactive, with physics interactions to give the object heft and life.</p></li><li><p>A gorgeously narrated audiobook, replete with music and sound effects to augment the storytelling experience.</p></li></ol><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://opensea.io/assets/ethereum/0x270488657c6724172372615ea8eb2802b233d41c/1">https://opensea.io/assets/ethereum/0x270488657c6724172372615ea8eb2802b233d41c/1</a></p><h3 id="h-the-medium-of-choice" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">The Medium of Choice</h3><p>Why did we choose to go with HTML? When presented with the dilemma of <em>how</em> to make this collection multimedia, video and HTML were the immediate two options.</p><p>The upside of video was better support across platforms—they were handled in most places. But the downside was file size and flexibility. How would we generate tens of thousands of code-customized videos in high-res?</p><p>Writing code for web, on the other hand, was our strength. It made it easier for us to create animations, add combination logic, etc., and it added the element of interactivity. On top of that, it meant a smaller file size. The only problem was that the support across platforms was patchy. Some marketplaces like OpenSea and Zora supported HTML pages, and a few tools like Gallery did as well, but many other platforms, especially wallets, didn’t support it at all.</p><p>To choose, we focused on what these multimedia storytelling NFTs could be—they were a means of injecting the weird wilds with a new kind of dimensionality and physicality. Without the limitations of static imagery, the ARTIFACTS could be living, breathing objects that could (quite literally) tell a story. HTML was the ideal medium for that.</p><p>The exploration led us to the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://opensea.io/collection/sirens-shell">SIREN’S SHELL</a>, our first multimedia NFT that we released as a NFT NYC party favor. It emerged out of the research we did for the HUT, allowing us to test out the concept and iterate on it.</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://opensea.io/assets/ethereum/0xa2177f14aad9b44c2fe04ee6731629b8d4e7270c/1">https://opensea.io/assets/ethereum/0xa2177f14aad9b44c2fe04ee6731629b8d4e7270c/1</a></p><h3 id="h-the-generative-logic-for-the-artifacts" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">The Generative Logic for the Artifacts</h3><p>But just explaining what an ARTIFACT is doesn’t begin to cover the sheer complexity that this work entailed.</p><p>To call THE NARRATOR&apos;S HUT one generative collection isn&apos;t quite accurate—it&apos;s more like five generative collections, or even thirty-five generative collections. This grid just shows the spectrum of possibilities, with thousands to millions of combinations represented by <em>every single square</em> in this matrix.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/beb00bbae98889a2ebbc8d9c944758d2bb10afaf851a3bed2060ca79c05f7e88.png" alt="The &quot;big boy&quot; grid, as Keridwen dubbed it." blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">The &quot;big boy&quot; grid, as Keridwen dubbed it.</figcaption></figure><p>There were five ARTIFACTS (so five objects conjured from the five short stories), and each needed to be able to take seven distinctly different manifestations: one for each of the six archetypes of WITCHES and one for “faceless ones,” or anyone without a WITCH.</p><p>Then, within each artifact-archetype permutation, the real magic unfolded. We wanted each ARTIFACT to be visually and/or conceptually customized to the individual WITCH who conjured it. In the lore, we framed the minting process as placing your hands onto an object to make an impression, or copy, of it, thus imbuing it with your essence.</p><p>Each ARTIFACT could be affected by a WITCH’s archetype, attunements, astrological signs, and appearance. Some were direct one-to-one references, while others were more abstracted. Some were guaranteed, and others existed on a spectrum of random probability.</p><p>Here’s one simple example of WITCH metadata affecting ARTIFACT metadata:</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/e6b8449c03afec6f000cdf81a0a3c4c736b661db5f706430cae6f9a8a5ef383b.png" alt="A side-by-side comparison of the SEER Prunella the Untethered and the AXE they conjured." blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">A side-by-side comparison of the SEER Prunella the Untethered and the AXE they conjured.</figcaption></figure><p>The art for THE NARRATOR’S HUT encompassed a staggering 2,169 individual layers, each of which needed to be filtered through often complex rules and constraints that had to be codified into logic.</p><p>But the final outcome? An ARTIFACT that visibly bore the mark of a particular WITCH.</p><h3 id="h-conjuring-with-cohesion" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Conjuring with Cohesion</h3><p>We’d been pushing on the project for many months, and the ARTIFACTS had finally started to take form in all their wonder (and woe). But as I started generating sets, I realized that they didn’t <em>look</em> like they were sets. One crucial ingredient was missing: color coordination, with the WITCH and with the other ARTIFACTS conjured by that WITCH.</p><p>We tossed the question back and forth: how should we balance matching aesthetic across six generative art pieces against the magic of randomness?</p><p>I put forth two ideas. First, background was an easy win. Each WITCH had one of eight possible background colors, and we could quickly coordinate a set of ARTIFACTS to the WITCH who minted it by matching their backgrounds.</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/aletheia_eth">Aletheia</a> created these gorgeous textured gradient editions of each of our original backgrounds that added new depth and dimension but also immediately tied together the color palettes, reinforcing the relationship between a WITCH and their ARTIFACTS.</p><p>Second was creating the concept of a primary colorway across an entire set of ARTIFACTS. That meant that each set would have one dominant color across the different objects, ideally matched to some visual aspect of the WITCH.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/72d74becb529ff8039620b35b1db1107b18c49fc28520c3fcecb892e0267402d.png" alt="A full set of ARTIFACTS fit for a MAGE, with the &quot;moss&quot; background and the &quot;glo&quot; primary colorway, for the WITCH Equuleus, the Keen Quadrant." blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">A full set of ARTIFACTS fit for a MAGE, with the &quot;moss&quot; background and the &quot;glo&quot; primary colorway, for the WITCH Equuleus, the Keen Quadrant.</figcaption></figure><p>The logic was different across archetypes—for instance, for MAGES, to select a primary colorway, we checked for script tattoos, then script circles, then hair color. For ENCHANTRESSES and OCCULTISTS, on the other hand, the colorway was based on the WITCH’s attunements: lighter for WONDER, darker for WOE.</p><p>Nyx and Aletheia worked overtime to iron out all these rules and then to create the art for them—a daunting task that meant reinterpreting some layers in as many as seven different colorways for each archetype but ultimately shaped cohesion from chaos.</p><h1 id="h-the-hut" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Hut</h1><p>Ahhh, our titular HUT. This project was our first chance to build out the point-and-click narrative experience of the weird wilds—how it looked, how it sounded, how it felt. From a technical perspective, it needed to be dynamic and flexible for future use cases. From a creative perspective, it needed to feel immersive and expansive, seamlessly matching the aesthetic of the WITCHES.</p><p>After some exploration, we decided to go with PixiJS, a 2D rendering engine that enabled us to support more kinds of mechanics for interactivity. Oizys built component wrappers for PixiJS so that we could declaratively create “scenes”—of which this one would be the first.</p><p>Our jack of all trades <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/0xstarmanx0">Starman</a> set to work on the design. We had a difficult task ahead of us: to create a rich interface for a complex set of mechanics conveyed largely through ui cues, all while keeping the environment immersed in the lore. Slowly but surely, we plucked bits and pieces from game design and product design and wove them into an experience somewhere between the two, a fresh take on building a world and telling its stories.</p><p>The intention of THE NARRATOR’S HUT was to evoke a sense of a rich and layered world, ignite the imagination with what might be lurking right outside the frame. That meant that we wanted the experience to be experiential and to feel immersive.</p><p>Cue this scene: a distinctly magical home, abundant with trinkets and treasures alike.</p><p>The interaction starts with a conversation with the NARRATOR to give context to the HUT. After telling you about her home and the ARTIFACTS she keeps within it, she invites you inside. You can immediately hear the ambient sounds of the HUT—a crackling fireplace, the scratching nib of a pen on rough paper, the soft creak of a rocking chair.</p><p>The five mintable objects are highlighted with a permanent glow, but in fact many of the objects are interactable, triggering anything from exposition of the world to nods to community creations to witty commentary from the NARRATOR.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/4cd31fea2990dd3927ae57f650e0006e5f36d039ed33de3581fc466eb7fa9df5.png" alt="Step into the not-so-humble home of our NARRATOR..." blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Step into the not-so-humble home of our NARRATOR...</figcaption></figure><p>When you <em>do</em> decide to take a closer look at one of the glowing ARTIFACTS, you are presented with the option to have the NARRATOR tell you the story bound to it—her memory of the WITCH who once owned it.</p><p>To create this effect, we used Google’s Transcription API (with a heavy layering of manual editing) to timestamp each narrated word and sync the story reader’s text to the audio. Starman created a Snapchat Stories-inspired interface for separating out each story into many pages, with the ability to move back and forth between them.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/08539438d9513e5752cce4df09142f8b4f9fcfc5a2d8a89f82f327378b85645a.png" alt="An excerpt from the story reader for &quot;Dove and the Asylum,&quot; written and narrated by Keridwen." blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">An excerpt from the story reader for &quot;Dove and the Asylum,&quot; written and narrated by Keridwen.</figcaption></figure><p>Once you’ve sufficiently explored the nooks and crannies of the HUT, you can choose to mint some ARTIFACTS of your own. The minting screen takes the form of a summoning—a pentagram pointing at the five ARTIFACTS that lights up when each one is selected.</p><p>Each ARTIFACT could be minted by any number of WITCHES in a wallet, and the pricing logic differed between wallets based on a few different factors, which made this interaction particularly complex to communicate. But with some tricky maneuvering between design and engineering work, we wove together a simple yet clear interface for minting.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/ecb220d395c25fc1f999cad442f004ef8f7dd34e83ea098ba5a14dd89b50bc3c.png" alt="Can&apos;t say no to a summoning circle." blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Can&apos;t say no to a summoning circle.</figcaption></figure><p>And finally, a finishing touch we added to build out the experience was a brand-new <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://cryptocoven.xyz/">homepage</a>, with the layout of the beginnings of a map of the weird wilds. To enter the HUT, you first click on its exterior—and fall into a portal to another world.</p><h1 id="h-the-contracts" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Contracts</h1><p>We embarked on this project with two aims markedly different from our first contract:</p><ul><li><p>We wanted to require people to mint through our web experience, rather than directly through the contract.</p></li><li><p>We wanted people to be able to access metadata about the ARTIFACTS on chain.</p></li></ul><p>Why require people to mint through our website? That was explicitly the opposite of what we wanted for our first mint. Then, as a new project by an anonymous team in a time when swindles and tricks were commonplace, we wanted anyone to be able to mint directly through Etherscan.</p><p>But for this project, centered on world-building, we wanted these ARTIFACTS to be interwoven with the lore that created them, to situate them in a rich imaginative context—not just offer them as isolated objects. That meant we needed to gate the minting by signature, a task that code whisperer <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/0xoizys">Oizys</a> quickly untangled.</p><p>The other focus, on on-chain metadata, came out of a wish for higher composability in our work. Our vision for the weird wilds is a world co-created, anchored by decentralized technologies, and so many of the interesting experiments I’ve seen in this space exist on chain.</p><p>Because our rich art style and lore are cornerstones of this project, there were many parts that could never be stored on chain. But this time, since we were starting out with the concept of generative objects with (initially) a much lower degree of complexity than the WITCHES, maybe that metadata could live on chain.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/7514a6a3e995aa67da8765b3ab01b7d5cf8b4287c7babb13dea5dd243e783779.png" alt="It&apos;s a hut!" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">It&apos;s a hut!</figcaption></figure><p>I sketched out our contract—really, two contracts: a metadata contract and a minting contract.</p><p>For each token, we would need to store a <code>tokenId</code>, an <code>artifactId</code> (to identify which of the five ARTIFACTS it referenced), and a <code>witchId</code> (since each WITCH could only mint each ARTIFACT once)—already triple the data most off-chain art NFTs need to store.</p><p>With the default ERC-721 contract, we couldn’t store that data efficiently, and so Oizys forked ERC-721 to write our very own version, packing the data we needed into fewer storage slots for significant gas savings.</p><p>For most of the project, we had planned for entirely on-chain metadata. But as the ARTIFACTS expanded in complexity, the constraints of storing all of our metadata on chain grew and grew—until it became clear that the ARTIFACTS would have millions of possibilities and be much more customized to each WITCH than we had ever anticipated.</p><p>And so in the last few weeks leading up to launch, we decided to take a risk and pivot to a hybrid approach. Oizys rewrote our contracts to store only specific metadata (about each ARTIFACT and its attunement modifiers, but excluding all visual attributes) and surface that through on-chain getters accessible to any contract.</p><p>Our <code>tokenURI()</code> would instead point to our own servers, to allow us to dynamically and (still) deterministically generate metadata for a given token, including all the visual attributes that had become impossible to surface on chain.</p><p>In the end, that left us with three contracts:</p><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://etherscan.io/address/0x270488657c6724172372615ea8eb2802b233d41c#code#F10#L1">The token contract</a>, a fork of ERC-721 with customized storage for our data</p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://etherscan.io/address/0x270488657c6724172372615ea8eb2802b233d41c#code#F1#L1">The minting contract</a>, which handled minting tokens and storing the specific data associated with each one</p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://etherscan.io/address/0x5b25C96AB32eA30504494c0b812F93D6d2d5ce00#code#F1#L1">The metadata contract</a>, which stored all of the possibilities for each ARTIFACT and deterministically returned the right metadata given token data</p></li></ul><p>Once we were just about finished with the code, we reached out to <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.solidityguild.com/">Solidity Guild</a> to audit the contracts. We weren’t eager to repeat <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://cryptocoven.mirror.xyz/A622VSRm8-9oLzc8l3oFGmfnFUZQmDQ3Wx3ObhSlhsc">our error from the first project</a>, and so we worked with <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/w1nt3r_eth">w1nt3r.eth</a> and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/zachobront">obront.eth</a> to refine and hone our code to a sharply pointed tip. To date (knock on wood), we haven’t encountered any bugs in this contract after deploying to mainnet. A fortuitous first!</p><h1 id="h-on-second-acts" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">On Second Acts</h1><p>Second acts are hard. And they are really, really hard when everyone is burnt out and the market is collapsing. There were many times this past year I wasn’t confident we’d pull it off because of timing, because of luck, because the stars weren’t aligned. But after working on this project for the better part of a year, I think it’s a fitting one for us. It pushes the entire NFT space into new and interesting places, technically and creatively—and, well, we never go by halves.</p><p>THE NARRATOR’S HUT is a project crafted with heart and hope, the seedling of the world we’re building. We hope this world beckons to you—as we like to say, the weird wilds await.</p><hr><p><em>If you haven’t yet conjured your very own ARTIFACTS: </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://cryptocoven.xyz/map/narrators-hut"><em>now’s the time</em></a><em>. This phase of minting ends on the winter solstice (December 21).</em></p><p><em>If you don’t have a WITCH: minting closes forever on the solstice.</em></p><p><em>If you do have a WITCH, you have until the solstice to claim a gifted ARTIFACT for every WITCH in your wallet and mint additional ARTIFACTS at 0.07 ETH. After that, gifting closes, and minting goes up to 0.2 ETH.</em></p>]]></content:encoded>
            <author>crypto-coven-2@newsletter.paragraph.com (Crypto Coven)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/f7ec4e3620ae7be479f63fb94a0b79e428f8a4059bb64a7ba68259a26d11a082.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Stories of the Crypto Coven NARRATOR'S HUT]]></title>
            <link>https://paragraph.com/@crypto-coven-2/stories-of-the-crypto-coven-narrator-s-hut</link>
            <guid>apnxedQRrNAyh5vyaGGf</guid>
            <pubDate>Thu, 01 Dec 2022 20:33:22 GMT</pubDate>
            <description><![CDATA[Hail and well met, WITCH! If it’s our first time making a virtual acquaintance, I’m Keridwen, the high witch of wit and wordsmithery at Crypto Coven. We are “an immersive, narrative-driven experiment in world-building, architected on top of decentralized technologies.” If that sounds magical, intriguing, and yet deeply confusing to you, our latest drop is a tangible example of what that work can look like. Our second collection invites you into the home of our world’s NARRATOR. Inside her HUT...]]></description>
            <content:encoded><![CDATA[<p>Hail and well met, WITCH! If it’s our first time making a virtual acquaintance, I’m <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mobile.twitter.com/keridwen_eth">Keridwen</a>, the high witch of wit and wordsmithery at <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://mobile.twitter.com/crypto_coven">Crypto Coven</a>. We are “an immersive, narrative-driven experiment in world-building, architected on top of decentralized technologies.” If that sounds magical, intriguing, and yet deeply confusing to you, our latest drop is a tangible example of what that work can look like.</p><p>Our second collection invites you into the home of our world’s NARRATOR. Inside her HUT are five magical objects, or ARTIFACTS, previously owned by powerful WITCHES. Each is accompanied by a story of their past.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/b8363802dd4a2c4e46d48c942e6939eabf23b440e3f4b10df0bd37998a043f81.png" alt="THE NARRATOR&apos;S HUT at the heart of the wilds" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">THE NARRATOR&apos;S HUT at the heart of the wilds</figcaption></figure><p>The five stories are fully narrated, voice acted, and sound designed (an absolute dream come true for me as a writer, but I digress). They’re also completely free to experience. You don’t need to pay or hold a WITCH NFT to visit the NARRATOR and learn a bit more about our world, the weird wilds.</p><p>One of our goals as a project is to serve as a peculiar yet warm welcome to the crypto space for the uninitiated. This world, woven by words, art, code, and magic, is not exclusive or gated. We want you to be a part of it. We’re laying out a welcome mat and hot cup of virtual tea.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/1a8d3ac21117edca474ef73cb4cd270070f916cfe6070c01404cbf34ad0585c9.png" alt="The story reader inside the HUT" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">The story reader inside the HUT</figcaption></figure><p>When I wrote these stories, I knew I wanted to show how witchcraft in our world looks and feels. What are the exhilarations and limitations of power? What does it mean to forge or sunder bonds with the powerful? What kind of genius might a friendly neighborhood omniscient narrative device find crawling on her stoop?</p><p>I also wanted to demonstrate how characters, both WITCH and unattuned, exist in the wilds in a concrete way. Our goal is to collaborate with you to build these people and places. Part of that is establishing locations like THE HALLOWED HALLS or THE DEMON MARKET and telling you more about the WITCHES who founded them.</p><p>Before I dive into some of my inspirations behind each story, you can experience them in the HUT on <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://cryptocoven.xyz/">our website</a>.</p><p>If you don’t have an Ethereum wallet, you can experience just the audio version of the stories <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://anchor.fm/crypto-coven">on Spotify</a>, or anywhere you get your podcasts. If you’re interested in getting one to experience the HUT in all its audiovisual glory, we have a handy guide for beginners <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://cryptocoven.xyz/guide">here</a>.</p><h2 id="h-the-stories" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Stories</h2><h3 id="h-avisa-and-cypress" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">AVISA AND CYPRESS</h3><p>AVISA AND CYPRESS was the first story to find shape, and in many ways the most akin to a traditional tragedy or epic. I knew I wanted to tell the story of THE ORACLE, a near-deity at the center of a vast desert, and the dark secrets of its power.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/b244411cd8f784a9ad99e44eb85a4852a71d7eb4f1d7cac4db78f178396fbe29.png" alt="Collages I made representing AVISA the SEER (left) and CYPRESS the NECROMANCER (right) " blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Collages I made representing AVISA the SEER (left) and CYPRESS the NECROMANCER (right)</figcaption></figure><p>The idea of SEERS as pirates came from these sick eyepatches <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/nyx_eth">Nyx</a> and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/aletheia_eth">Aletheia</a> drew into the first collection. Of course WITCHES with the ability to see the future would make the best seafarers and maruaders! Who better to predict the movements of the ocean and the treasures of its guests?</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/212a2995f01669d4659e492c30c38566d71d845c5682eb19ee80c7d3a9f9c576.png" alt="Two SEERS with the eyepatch trait" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Two SEERS with the eyepatch trait</figcaption></figure><p>SEERS’ power over time itself makes them both terrifyingly powerful and terrifyingly vulnerable. Several of these stories explore that exchange and its dangers. THE ORACLE, ultimate knower and dispenser of wisdom, is not exempt from laws demanding unbound sacrifice in exchange for power. In contrast, the work of a NECROMANCER can be inherently grounding. The entrance of CYPRESS helped me explore that.</p><p>While writing, I spoke often with the NECROMANCERS in our Discord about how the archetype could manifest as patient, kind, steady, and balancing. To face spirits, to calm them, to bring them peace, is the work of someone generous with those who need it most. I was inspired by these conversations and the community entries to our <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://cryptocoven.xyz/tree-of-echoes">ECHO TREE</a> to represent that kind of death magic practitioner.</p><p>I’ve also always been excited by stories of disability and magic, or fantastical worlds that incorporate a wide range of experiences. It was fun to think about how a NECROMANCER so used to speaking to spirits might communicate differently with the living, and how languages like sign might find new contexts among the fantastical.</p><h3 id="h-null-and-rijha" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">NULL AND RIJHA</h3><p>The next story, NULL AND RIJHA, is just the beginning of what we have to share about the relationship between OCCULTISTS and DEMONS. Their pactmaking was a recurring idea in the articulations of the WITCHES and community discussion of THE DEMON MARKET map location.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/d895f3217ad2aa06cfba88c935f82f583ff39e9b82f53f3bd86b1a57b514226f.png" alt="Two WITCHES whose descriptions include their relationships to DEMONS" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Two WITCHES whose descriptions include their relationships to DEMONS</figcaption></figure><p>Where do DEMONS come from? What kind of WITCH was the first to make such a deal and how? What kind of rad extra limbs and organs did they have because of it? Asked and answered. We know OCCULTISTS are the kind of WITCH to seek out sacrifice and deception… but what about ENCHANTRESSES?</p><p>ENCHANTRESSES are beguiling, power hungry, principled, driven by ideals and execution. How far might one go to achieve her goals? What might she sacrifice? I’ll admit this story only hints at these answers as a way to set up our future work in THE CITY OF MIRRORS.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/2841eca1885f9e06664cce09ea581afa524249006be2bd60a04125e83ed0390c.png" alt="Collages for NULL the OCCULTIST (left) and RIJHA the ENCHANTRESS (right)" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Collages for NULL the OCCULTIST (left) and RIJHA the ENCHANTRESS (right)</figcaption></figure><p>Aside from the plot itself, this story also exemplifies our interest in representing a diverse set of experiences in a fictional universe. Null (in addition to Cypress) is nonbinary and uses they/them pronouns while Rijha’s name evokes south Asia. As I was drafting, high witch <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/xuannu_eth">Xuannu</a> expressed a desire for cultural touchpoints in the wilds that would build on the choices we’d made in the first collection, for example, her own WITCH name derived from a Chinese goddess rather than conforming to a more Western approach to witchcraft and fantasy. The goal of these stories was not only to show WITCHES in action and conversation, but also to introduce the range of people and places that populate the wilds.</p><h3 id="h-iona-and-the-unattuned" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">IONA AND THE UNATTUNED</h3><p>One of the core principles the team wanted to communicate was that our kind of magic is not exclusionary. Our stories are not of gifted vs ungifted, but rather of spirit, building, and becoming. While no one may own a WITCH, anyone can become one.</p><p>While this is most intuitive with MAGES, it’s not limited to only their archetype. IONA was the first HAG to shape herself through stubbornness and WILL. She protected the earth not because it was her birthright but because it was her lived experience.</p><p>As an archetype, HAGS are in tune with nature. That extends beyond an immediate relationship with plants or animals to one of balance on a large scale. HAGS are protectors, architects, and guardians. They correct the natural order of things. IONA points out the unsustainability and disrespect of the BEAST’s actions rather than best it in combat. While we have emphasized the soft, floral cottagecore aesthetic of the archetype in the past, this story expands on HAGS as active arbiters of equilibrium.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/4b2a317ce9ed888087f90ad77d6b5a0fc4fb0d250397495abe550b9170d091ba.png" alt="IONA the HAG and her flock" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">IONA the HAG and her flock</figcaption></figure><p>We also meet our first BEAST, an amorphous kind of creature oft the enemy of WITCHES. Unlike DEMONS whose powers and behaviors have been loosely mapped, BEASTS are wide-ranging and highly unpredictable. Their origin and motives are yet to be known, if such knowledge is even up for the knowing…</p><h3 id="h-dove-and-the-asylum" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">DOVE AND THE ASYLUM</h3><p>DOVE’s story is a fable, one that made its way to the NARRATOR through lineages of storytelling. DOVE is the only WITCH the NARRATOR herself has not beheld in some form. She is lost to time the way her wards, WITCHES who ruptured the limitations of magic, are LOST.</p><p>Approaching the story this way evokes some of our aesthetic inspirations—oral histories and folktales. The wilds’ history finds texture in the contrast between stories immediately recollected and those that have been smoothed by many tellers. THE ASYLUM is something of legend, a relic of the past whose true nature is unknowable to all except the most in need. It is as much haven as it is mystery, a call to the adventurous and determined.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/5e558973f0573c37fcde1bfd3680da8792e49cdfd7b239461d3d224d6aaacae2.png" alt="DOVE, a WITCH of unknown archetype" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">DOVE, a WITCH of unknown archetype</figcaption></figure><p>THE ASYLUM is a location <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/aradia_eth">Aradia</a> introduced when naming the WITCHES, an origin that appears only for OCCULTISTS and SEERS. It’s fun to see the roots of our ideas for the world planted in each of our contributions to the original collection as we expand on them.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/f0c47d5664f30ff9a3911b94334716eb8a1c7620d681ae7bed8b562ec676f6d0.png" alt="An OCCULTIST and SEER with &quot;of the ASYLUM&quot; in their name" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">An OCCULTIST and SEER with &quot;of the ASYLUM&quot; in their name</figcaption></figure><p>We wanted to create a location at odds with the modern understanding of the word and closer to its original one: a place of solace and safety for those most in need. DOVE’s story follows her as she negotiates with the keepers of such a place: the snow monkeys of THE ASYLUM. Its setting and keepers are inspired by the onsen monkeys of Japan’s Nagano Prefecture. It ends with perhaps the most direct call to action to the reader, or “player” of the narrative experience. Will you be the WITCH to cure the LOST?</p><h3 id="h-spinor-and-the-design" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">SPINOR AND THE DESIGN</h3><p>Lastly, SPINOR! MAGES are a particular favorite of mine and the archetype I personally identify with. Xuannu and I have racked up many a Discord log chatting about magic that is learned, precise, and innovatively threatening to existing systems. This story is the most “recent” to the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://cryptocoven.xyz/lore#timeline">narrative timeline</a> of the wilds, as MAGES are the newest archetype of WITCH. Their work, mechanical and strange, has a momentum that thrusts the world into an unknown new era.</p><p>Here we see the most of the NARRATOR herself, and experience the liveliest story, one that is present and punctuated by snappy dialogue. I wanted the form of each story to mirror its age. While AVISA AND CYPRESS has the rolling prose and epithets of epic poetry, SPINOR AND THE DESIGN is grounded and immediate. After all, it happened only a few dozen years ago.</p><p>Part of what makes SPINOR fascinating and dear to me is how feral she is. Crypto Coven is deeply interested in varied interpretations of femininity and feminine power. A WITCH can be an austere goth, a nonbinary conartist, a scientist who crawls around on all fours observing lizards in nature and attempting to trap them in the claws of her manicure. MAGES have a sharpness to them because of how they experience the world—through tearing it apart.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a8ebf94f945205dba56ed7140244e634156a0ece626fc54efa0035b4ec5d0568.png" alt="SPINOR the MAGE and her iridescent lizards" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">SPINOR the MAGE and her iridescent lizards</figcaption></figure><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/ccd32901ff7d7c578dfda9ebc9c35df65a7d4a24f66184a600cdef563240d8d2.png" alt="A MAGE and their attuned version of SPINOR&apos;S AXE" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">A MAGE and their attuned version of SPINOR&apos;S AXE</figcaption></figure><p>One of the most distinct visual cues for MAGES are their glowing tattoos and rings, shown here on both a WITCH and their ARTIFACT. SPINOR’s story follows the moment of their invention—the feral scientist who redrew her own limits unlike any before.</p><p>SPINOR declares that she will teach her new art at a school of her invention: THE HALLOWED HALLS. The HALLS are a map location home to professors and students, dissertations and expertise on even the most niche subjects. You can find much more information on these studies in the articulations, names, and visages of MAGES. The story behind each WITCH’s tattoos and MAGE rings are yours to tell.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/c24f6ee0f5ba6099e3154bb50f02d68aa7037af19b16e2d909e5372ec28da2ee.png" alt="WITCHES from our first collection described as a student and professor at THE HALLOWED HALLS" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">WITCHES from our first collection described as a student and professor at THE HALLOWED HALLS</figcaption></figure><h2 id="h-the-future-of-the-wilds" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Future of the Wilds</h2><p>These stories are the beginning of our expansion, built on the foundation of all the love and detail poured into our original WITCH collection. Through the winter solstice, you can mint ARTIFACTS that contain the audio recordings of each story to take with you on your travels.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/f09a1843524c59684bf08734973534495d09047ba9afadfbd26c165c534cd8b2.png" alt="One of your first interactions with the NARRATOR inside the HUT" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">One of your first interactions with the NARRATOR inside the HUT</figcaption></figure><p>There are both attuned and unattuned varieties, either adorably canon or customized to your WITCH if you have one. I am not at all biased towards the spectacular MONKEY CHARMS because they have my voice in them…</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/83b05028cc59ba215e34e80943a8566dcca4beec404a3f1af3fafde3e19ac495.png" alt="An unattuned MONKEY CHARM vs one attuned to a HAG" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">An unattuned MONKEY CHARM vs one attuned to a HAG</figcaption></figure><p>It’s been an absolute honor to have these stories out in the world swaddled in such gorgeous art and seamless, tireless code. Thank you for reading and being a part of this adventure.</p><p>Narrative is at the heart of what we do, and this is far from the last time you’ll hear from me. In fact, I have <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://cryptocoven.xyz/tree-of-echoes">three new stories</a> on the ECHO TREE as part of our latest writing contest. If you’ve made it this far, you’re exactly the kind of magic-maker—dare I say, WITCH—we want to build and experience this world with us. We want to live inside your stories, gush over your art, be challenged by your game design, find delight and strangeness in the recesses of your mind you may have to brush the dust off of.</p><p>We would love to welcome you to THE NARRATOR’S HUT as well as <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://discord.gg/cryptocoven">our Discord</a> and fluttering sphere of socials. Reach out, hang out, we’re waiting by the crow’s nest for your transmission.</p>]]></content:encoded>
            <author>crypto-coven-2@newsletter.paragraph.com (Crypto Coven)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/28c909ffac5f1443b23b050c21b16303d6ba8ebe1624006fdae9592e871ee7f6.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Inking a Smart Contract]]></title>
            <link>https://paragraph.com/@crypto-coven-2/inking-a-smart-contract</link>
            <guid>ESFX17SuGr2cfHwUaKV6</guid>
            <pubDate>Sun, 13 Mar 2022 14:57:38 GMT</pubDate>
            <description><![CDATA[I’m Xuannü, the WITCH of WILL and keeper of the coven at Crypto Coven. As some of you know, I worked on crafting the ERC-721 contract for the initial series of WITCHES for the project. It was the first smart contract I had ever written—about a week or two into knowing what a smart contract was at all. An experience better not repeated, decidedly, but an edifying one nonetheless. The cryptic art of spellcasting in Solidity can be inscrutable at the surface, but we were able to draw from a coll...]]></description>
            <content:encoded><![CDATA[<p>I’m Xuannü, the WITCH of WILL and keeper of the coven at Crypto Coven.</p><p>As some of you know, I worked on crafting the ERC-721 contract for the initial series of WITCHES for the project. It was the first smart contract I had ever written—about a week or two into knowing what a smart contract was at all. An experience better not repeated, decidedly, but an edifying one nonetheless.</p><p>The cryptic art of spellcasting in Solidity can be inscrutable at the surface, but we were able to draw from a collective pool of wisdom that deepens daily… and so we bring our own to you, young arcanists. Here is the story of a contract cast into the ether to bring to life a coven, as well as the trials encountered along the way—the curse of the void witch, and their untimely return.</p><h1 id="h-unraveling-the-meaning-of-a-smart-contract" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Unraveling the Meaning of a Smart Contract</h1><p>It was a few weeks before the Halloween launch of Crypto Coven and, thus, the date at which we were meant to deploy the smart contract for our collection of WITCHES.</p><p>The catch? There was no smart contract; and, frankly, we weren’t yet sure what exactly a smart contract was.</p><p>We had begun working on the project in mid-September, and there was a seemingly infinite amount to do—drawing art, sculpting a generator, weaving lore, creating a website, and so on. As arcanists with keen competencies in web development, the generator and the website were easy (and, granted, crucial) places to focus our energies. They were already enough work on their own that we had been pulling four a.m. nights for weeks. The smart contract, by contrast, was uncharted territory. We regarded it with no slight amount of trepidation, and so we procrastinated on it until we had simply no other recourse but to dust off the sacred tomes and, well, learn what it meant to write code on the blockchain.</p><p>Of course, we could have just used a platform like OpenSea or Zora rather than develop a contract from scratch—but we <em>wanted</em> to write our own. For one, a major part of why we had undertaken this project at all was to explore and experiment with the blockchain as a medium. Intrigued by the possibilities that emerged out of permissionless code and decentralized data, we sought to understand the technologies underpinning them more deeply for ourselves. More than that, we weren’t just dropping a collection of WITCHES—we were crafting a world around them. Having our own contract would enable us to design the entire experience around the summoning of a WITCH, setting the tone for the community we hoped to manifest by offering an early glimpse into the weird wilds.</p><p>With that aim, I first set out to get a technical base of the problem space, starting with just one Saturday spent perusing the entirety of the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://ethereum.org/en/developers/docs/">Ethereum Foundation docs</a> on the subject.</p><p>What is a smart contract? It’s a misnomer, really. 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.</p><p>What, then, is a non-fungible token (NFT)? It’s a record of ownership created and stored within a smart contract that follows a particular standard (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-721">ERC-721</a>), and it can point to anything—an image, a song, an HTML page, and so on. Unlike cryptocurrencies, where tokens are interchangeable (fungible), NFTs reference unique objects.</p><p>Even as competent engineers, we initially struggled with the specifics of how NFTs work—in part because it’s so simple that it’s counterintuitive. It was hard to believe that that was <em>it</em>. Most NFTs are just pointers: a blockchain-based database entry that points to content somewhere else on the internet. On-chain NFTs are the exception; the content itself lives on the blockchain, not just a pointer to it, but that also means that the content is limited to formats that can feasibly be stored, given the size limitations of the blockchain (i.e., vector or pixel art rather than high-res raster art).</p><p>But for our project, for instance, 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.</p><h1 id="h-poring-over-past-spellwork" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Poring over Past Spellwork</h1><p>After developing a high-level understanding of smart contracts and NFTs, I started looking through existing contracts, from older, well-known projects like Cryptopunks, Bored Ape Yacht Club, and Blitmaps to projects that were just launching at the time, like Doodles. Seeing real code bridged a lot of the gaps in my mental model. Nyx and I had worked to sketch out the functionality we wanted in our smart contract; browsing other projects’ contracts on Etherscan helped to see countless examples of different implementations and feature sets.</p><p>As much as we gleaned from all this prior work, perhaps one takeaway was even more salient: not every contract is well-written, and in fact even common practices can be anti-patterns. This space is still early, and many of the people deploying to mainnet are, like us, new to developing on Ethereum—which means that it’s important to never assume that even a well-known project necessarily has good code. Understanding the fundamentals rather than replicating code can be essential to ensure that you aren’t copying inefficient, incorrect, or (worst of all) insecure code.</p><h1 id="h-the-desired-functionality" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Desired Functionality</h1><ul><li><p>Minting</p></li><li><p>Gifting</p></li><li><p>On-chain royalties</p></li><li><p>Gasless listings on OpenSea</p></li><li><p>Batch reveals</p></li><li><p>Low gas</p></li><li><p>Testability</p></li><li><p>Transparency</p></li></ul><h3 id="h-minting" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Minting</h3><p>We had a few aims with minting: we wanted to support both a community sale (limited to a list of addresses) and a public sale, with a three-WITCH-per-wallet limit across both.</p><p>Figuring out the public sale and the per-wallet limit were trivial. Figuring out how to limit minting for the community sale to a list of addresses, though, posed a more complicated question. Few of the contracts I read included logic for limiting minting to a list of addresses.</p><p>But when I tweeted a different question, asking how people usually implemented airdrops, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/strollinghome">indreams.eth</a> shared Mirror’s <code>DroppableEditionsLogic</code> <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://etherscan.io/address/0x86bea424e00ed6d742e2c81c2f9eda9567eeab5c#code">contract</a>. I scrutinized it with acolytes <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/0xoizys">Oizys</a> and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/enthea_the_hag">Enthea</a>, breaking down each function to understand how it worked. We focused on <code>purchaseWithProof()</code>, learning what a <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://decentralizedthoughts.github.io/2020-12-22-what-is-a-merkle-tree/">Merkle tree</a> was. But even after we understood <em>what</em> it was, we still didn’t understand why this seemingly convoluted logic was necessary. Without any reference points, we kept wondering: why not just write this code, the obvious solution?</p><pre data-type="codeBlock" text="function setCommunityList(address[] calldata addresses) external onlyOwner {
    _communityList = addresses;
}

function mintCommunitySale(...) external payable {
    require(_communityList.includes(msg.sender)); // this `includes()` method does not exist!
    ...
}
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setCommunityList</span>(<span class="hljs-params"><span class="hljs-keyword">address</span>[] <span class="hljs-keyword">calldata</span> addresses</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">onlyOwner</span> </span>{
    _communityList <span class="hljs-operator">=</span> addresses;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mintCommunitySale</span>(<span class="hljs-params">...</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span> </span>{
    <span class="hljs-built_in">require</span>(_communityList.includes(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>)); <span class="hljs-comment">// this `includes()` method does not exist!</span>
    ...
}
</code></pre><p>After some experimenting with code, the reason quickly became clear: gas. The cost of storing so much data on-chain was astronomical. It was perhaps the first revelation we had about how writing Solidity differed from most of the code we’d written in the past. 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.</p><h3 id="h-gifting" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Gifting</h3><p>With gifting, we wanted to be able to support airdrops and claim drops. 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.</p><p>We wanted the flexibility of these different options because, around the time the project was launching, gas was <em>particularly</em> high. On one hand, we were super low on funds and weren’t sure if we could feasibly take on the cost of airdropping gifts to people. On the other hand, many of the people we wanted to gift were new to NFTs, and, in an environment where a few hundred dollars to mint an NFT wasn’t out of the question, making the experience as frictionless as possible was important to us.</p><p>In retrospect, we probably spent too much time optimizing for this question—it was a relatively minor feature in the grand scheme of everything, and we overcomplicated the contract by deploying it with multiple gifting implementations. With smart contracts, simplicity can be key, especially when it comes to functionality as core as minting, but more on that later.</p><h3 id="h-on-chain-royalties" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">On-Chain Royalties</h3><p>One recurring question I had as I browsed examples of ERC-721 contracts was: where are all the royalties? None of the contracts I reviewed had any mention of royalties written into them. Because of that, it was difficult to even learn what percentage royalties a given project received.</p><p>We realized that the state of royalties at the time that we were writing our contract wasn’t what we had expected—marketplaces like OpenSea had off-chain implementations, and the one on-chain royalty standard (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-2981">EIP-2891</a>) 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.</p><p>Still, a standard for on-chain royalties was certainly preferable to the alternative, so we decided to implement it.</p><h3 id="h-gasless-listings-on-opensea" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Gasless Listings on OpenSea</h3><p>Because we were writing and deploying our own contract, by default, people wouldn’t be able to list their WITCHES on OpenSea without paying gas. But we had noticed in <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.opensea.io/docs/1-structuring-your-smart-contract#opensea-whitelisting-optional">their docs</a>, sparse as they were about the specifics and why any of it was necessary in the first place, that we had the option to lower this friction by allowing gasless listings. Oizys was able to piece together what we needed to do to enable the functionality: override the <code>isApprovedForAll()</code> function to allowlist the OpenSea proxy registry address.</p><h3 id="h-batch-reveals" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Batch Reveals</h3><p>We were never expecting an instant sellout for the project, nor was that our aim, which meant that 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).</p><p>That would be simple enough if we were hosting our metadata on our own servers. But we had decided to use <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://ipfs.io/">IPFS</a>, a distributed, peer-to-peer file storage system. That complicated matters to some extent. IPFS 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.</p><p>In our case, each time we wanted to make changes to the metadata (i.e., by replacing placeholder WITCHES with revealed ones), we would need to upload a new directory containing <em>all</em> of our metadata files—which meant that the IPFS address would change, which <em>then</em> meant that the contract would need to point to the new one. Because of that, we had to be able to update the base URI rather than have it hardcoded into the contract.</p><p>Our acolyte Enthea wrote a script to programmatically reveal batches of WITCHES, reading the number of WITCHES already minted to the contract and then populating the directory of minted witches with the correct metadata, creating placeholders for all the others. We then uploaded a new directory to IPFS each time and updated the base URI in the contract to reflect the new address for the metadata.</p><h3 id="h-low-gas" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Low Gas</h3><p>Lowering gas usage was a high priority for us, given that gas fees had become so onerous around the time that we were launching. At the time, gas hovered solidly at hundreds of gwei, often even spiking to the thousands.</p><p>There were a few different ways we attempted to tackle the issue—compiling the contract with Hardhat using gas optimization for 100,000 runs, minimizing data storage with the Merkle tree for the community sale, and looking into best practices for airdrops (specifically, where we covered gas) to make them more financially viable.</p><p>For this last case, we spent a huge amount of time implementing different methods for airdrops and comparing gas usage between them, to see if there was a way to get the best of both worlds. We spent days experimenting with different options, ranging from exploring Cargo’s batch transfer EIP to... just minting on a <code>for</code> loop. Underwhelmingly, sequentially minting with a <code>for</code> loop turned out to be the most gas-efficient option.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/1d6c3c8e4a5d1c97462e61403db135ef89f20a6682e117c697d709f815dccdd5.png" alt="All of Oizys’ loving work... only for us to realize that our very first and most obvious idea was still the least expensive." blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">All of Oizys’ loving work... only for us to realize that our very first and most obvious idea was still the least expensive.</figcaption></figure><h3 id="h-testability" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Testability</h3><p>Oizys, meticulous arcanist that they are, brought up testing. We needed to be able to easily write tests for the contract to ensure that we didn’t break anything as we made changes.</p><p>What that meant for the contract was moving some of the variables we had hardcoded (<code>maxWitches</code>, <code>maxCommunitySaleWitches</code>, and <code>maxGiftedWitches</code>) into the constructor. The tradeoff was that it made it harder to read these values at a glance, but it became easier and faster to write tests checking key logic, like what should happen when we hit each of these limits. And, in fact, we <em>did</em> catch several off-by-one bugs this way before we shipped the contract. Tests! They work.</p><h3 id="h-transparency" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Transparency</h3><p>And, finally, transparency. We wanted the limits we’d set for ourselves to be clearly encoded in the contract (i.e., the total number of WITCHES we could ever gift). We hadn’t seen that in any of the contracts we read; the owners of contracts could typically reserve an arbitrary number of tokens for themselves—in BAYC’s case, an infinite number of tokens because the owner-only function wasn’t subject to the max total tokens check.</p><p>Similarly, 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.</p><h1 id="h-a-first-iteration-the-curse-of-the-void-witch" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">A First Iteration: The Curse of the Void Witch</h1><p>Late on the night before Halloween, we finally had a <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://etherscan.io/address/0xe6ddda1c3f1cb01aa5c86a21e8636deabfd1f013#code">contract</a> that had been heavily tested locally and on testnet. It was time to deploy.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/b4b43957a52c9afee5ecf9565ffb9e85e925ce486506b41d9b8f0dcf0a3b39e6.png" alt="The adrenaline-fueled moment of truth on the eve of the community sale." blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">The adrenaline-fueled moment of truth on the eve of the community sale.</figcaption></figure><p>We opened the community sale, to no small amount of chaos—there were a few bugs in the minting UI (notably, that the addresses for the community sale were case-sensitive), but people were finally able to begin summoning their WITCHES.</p><p>Oizys and I stayed awake until five a.m. that morning, debugging issues and pushing fixes, but at that level of fatigue, we were breaking more than we were fixing. At some point, everyone started experiencing issues with minting. We decided to figure it out in the morning.</p><p>The next day, after a few hours of sleep, I quickly realized what had been wrong—we had reverted to an incorrect Merkle tree at some point the previous night, which is why all minting was failing rather than just some of it. I recreated the tree and added the correct root to the contract, and after we redeployed to the website, we announced that minting was fixed. All was well.</p><p>We had decided to do a few batch reveals that same day (rather than have people wait) to commemorate the occasion. The first went smoothly on our end—I updated the metadata and set a new base URI pointing to the new IPFS directory without any issues. But it was taking an unsettlingly long amount of time to update on OpenSea.</p><p>We kept pressing the refresh metadata button, to no avail, and we were searching for what might be wrong when we came across a (spoiler: incorrect) writeup about how IPFS files couldn’t include file extensions. Then, when we tested the token URI with OpenSea’s validator, it threw an exception, unable to pull the metadata. The <code>.json</code> extension was written directly into our <code>tokenURI()</code> function. Did we make an error that would prevent the WITCHES from ever rendering?</p><p>Luckily, it was just OpenSea’s slow queueing to process metadata refreshes, and within the hour, the unveiled WITCHES began to appear.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/d7ebdf804a53abce6d00b7a5f31fe3eeeb1046d386734da5344c338135145803.png" alt="Imagine having a bug so serious that you had to deploy a new smart contract and re-mint all existing tokens. Imagine." blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Imagine having a bug so serious that you had to deploy a new smart contract and re-mint all existing tokens. Imagine.</figcaption></figure><p>In retrospect, this panic didn’t make any sense—the metadata for the placeholder was working perfectly, for one. But we were exhausted and paranoid, and our first WITCH unveiling had stalled. It was our first post-deployment panic over a bug in immutable code; it wouldn’t be the last.</p><p>As people unveiled their WITCHES, the tension and stress of the day dissipated. The reception was glowing, and I was floating on the high, dizzy from the whiplash of emotions. The remainder of the day passed in a blur, with each subsequent reveal unfolding smoothly. I decided to go to bed early that evening but first wanted to send out the WITCHES we had been planning to gift to the friends of the coven on launch day.</p><p>I went to Etherscan to call the <code>giftWitches()</code> function in our contract, but when I attempted to pass it the full list of addresses (eighteen), I was hit with an impossibly high gas fee and an ambiguous error. Tired, I opted to deal with it in the morning.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/216733f47691f7e4b2b162131e7e3747355097f4b7afa386beb03ad2c2ce16e1.png" alt="This emoji was my spiritual embodiment for much of this period." blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">This emoji was my spiritual embodiment for much of this period.</figcaption></figure><p>The next morning, I woke, ready to figure out the gifting at last. I still had no idea what could possibly be wrong; I wondered vaguely if there might be an issue with one of the addresses or even the number of addresses in the list. I started testing the function with different combinations of addresses, with differing outcomes. With just the first half of the addresses, everything looked fine. Swapping some of the addresses worked as well. Without a definitive answer, I decided to mint to the addresses that looked fine, the first half. All good.</p><p>I checked the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://etherscan.io/tx/0xdd161285d7c56e16efb1e4123a5dc32d353911ed09b7290bcf2488965d847fb9">transaction</a> to see if it had been successful. It had, but upon looking more closely, I noticed that two of the WITCHES hadn’t been gifted after all. We had skipped over two IDs entirely.</p><p>What happened? Skimming the function, I remembered that we’d implemented a per-wallet limit for gifting as well as for minting in the community and public sales; two of the people we had intended to gift had already minted three WITCHES in the community sale in the short time that it had been open. Disappointing, but not the end of the world.</p><p>I turned back to the second half of the addresses. Except, this time, none of them worked—not a single one could be minted without triggering the astronomical gas fee.</p><p>With a growing sense of doom, I decided to call <code>giftWitches()</code> from the command line to see if <em>that</em> would work. Immediately, there, I saw the real error that using Etherscan and Metamask had buried:</p><p><code>ProviderError: execution reverted: ERC721: token already minted</code></p><p>That wasn’t what I had been expecting to see, <em>at all.</em> Suddenly I had a flash of understanding and rushed back to the contract code to reread the function, line by line. How could we be minting a token that had already been minted?</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/b7fe731a382823cfd6875e5ab3d023535cd9b872aab66c98112b22cd8b10c96f.png" alt="Because of time zones, I ran into this bug several hours before the other high witches would wake... spiraling into doom alone in perhaps some of the darkest hours of my life." blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Because of time zones, I ran into this bug several hours before the other high witches would wake... spiraling into doom alone in perhaps some of the darkest hours of my life.</figcaption></figure><p>As I read, the situation became clearer, but only to our detriment.</p><p>Here was the function I had called:</p><pre data-type="codeBlock" text="function giftWitches(address[] calldata addresses) ... {
    uint256 ts = totalSupply();
    uint256 numToGift = addresses.length;
    
    ...

    for (uint256 i = 0; i &lt; numToGift; i++) {
        if (balanceOf(addresses[i]) &lt; MAX_WITCHES_PER_WALLET) {
            _safeMint(addresses[i], ts + i + 1);
        }
    }

    numGiftedWitches += numToGift;
}
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">giftWitches</span>(<span class="hljs-params"><span class="hljs-keyword">address</span>[] <span class="hljs-keyword">calldata</span> addresses</span>) ... </span>{
    <span class="hljs-keyword">uint256</span> ts <span class="hljs-operator">=</span> totalSupply();
    <span class="hljs-keyword">uint256</span> numToGift <span class="hljs-operator">=</span> addresses.<span class="hljs-built_in">length</span>;
    
    ...

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&#x3C;</span> numToGift; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
        <span class="hljs-keyword">if</span> (balanceOf(addresses[i]) <span class="hljs-operator">&#x3C;</span> MAX_WITCHES_PER_WALLET) {
            _safeMint(addresses[i], ts <span class="hljs-operator">+</span> i <span class="hljs-operator">+</span> <span class="hljs-number">1</span>);
        }
    }

    numGiftedWitches <span class="hljs-operator">+</span><span class="hljs-operator">=</span> numToGift;
}
</code></pre><p>There was one crucial error in this code and then one design choice that made it irreparable.</p><p>First, we included a check for the per-wallet limit in a <code>for</code> loop and relied on the iterator to assign IDs—which meant that IDs could be skipped, not a desirable behavior under any circumstance.</p><p>Second, we were using <code>totalSupply()</code> to assign token IDs because we had assumed that the total supply would always be equal to the last token ID we had minted.</p><p>But now we were stuck in a situation where we had a total supply of 135 WITCHES, but the last token ID that had been minted was 137. We had skipped 130 and 135, and there was already a token with the ID 136, which the contract considered the “next” token ID. It was an infuriatingly simple bug, one that could have been easily fixed if we had thought to test that edge case.</p><p>But we hadn’t, and the contract was trapped in this state, unable to ever mint again—the crisis we had been so relieved to avert just the prior day had come to pass.</p><h1 id="h-facing-the-fire" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Facing the Fire</h1><p>So it had happened—our contract had hit a snag that would force us to redeploy after the community had already summoned WITCHES. What now?</p><p>The high witches flew into action. Nyx posted a pronouncement notifying the community that minting was on pause as we investigated a flaw in our gifting logic, and we started reaching out to all the people we knew who might be able to help us figure out a fix.</p><p>Oizys and I analyzed our options to see if there was any way at all that we could circumvent the bug within the contract. 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.</p><p>After we announced that minting was paused, 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.</p><p>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 <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/matthewdif">Matthew Di Ferrante</a>, an experienced Solidity auditor and developer who had been contributing to the Ethereum ecosystem for years. He offered to help us figure out what to do in exchange for a <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://cryptocoven.xyz/witches/137">custom WITCH</a>; we agreed, gladly.</p><p>With someone with, well, more than a few weeks of experience with writing code on Ethereum, the necessary course of action quickly became clear. We <em>would</em> need to redeploy and roll over all the WITCHES that had already been minted. Outside of the contract, then, we would need to replace the art for the tokens on the v1 contract and (ideally) integrate it into the lore for the world we were building.</p><p>Nyx went to work on writing a post explaining the situation to the community. Aletheia started developing the art for what would become the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://opensea.io/collection/crypto-coven-curse-of-the-void-witch">soul vessels</a>—decadent bejeweled vials containing the souls of the WITCHES lost to the ether. Aradia delved into the world-building around the soul vessels, while Keridwen put her pen to weaving the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://cryptocoven.xyz/curse-of-the-void-witch">lore</a> behind the so-called VOID WITCH who had snatched these lost WITCHES.</p><p>Once we had solidified a plan, Nyx shared out the update to everyone affected, along with what they should expect: that they would be granted new copies of their WITCHES as tokens on another contract, and the WITCHES they had would be turned into soul vessels to commemorate minting at launch. We were terrified of how people might respond to the news.</p><p>But perhaps we didn’t need to be. The replies were overwhelmingly kind; not a single person expressed so much as frustration, and, in fact, the community told us to take our time and not rush ourselves. The relief I felt upon seeing that was—indescribable.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/00ee078bdba667d47c06ce86dd6fc073063aa5dee6c916a38984dfdff29aea9b.png" alt="The first witches, absolute gems." blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">The first witches, absolute gems.</figcaption></figure><p>With the pressure of immediately redeploying a fixed contract alleviated, 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.</p><h1 id="h-a-second-iteration-inscribing-a-new-contract" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">A Second Iteration: Inscribing a New Contract</h1><p>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.</p><h3 id="h-patching-the-bugs" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Patching the Bugs</h3><p>The first problem we needed to fix was the <code>giftWitches()</code> function. Here was the original glitchy code:</p><pre data-type="codeBlock" text="function giftWitches(address[] calldata addresses) ... {
    uint256 ts = totalSupply();
    uint256 numToGift = addresses.length;

        ...

    for (uint256 i = 0; i &lt; numToGift; i++) {
        if (balanceOf(addresses[i]) &lt; MAX_WITCHES_PER_WALLET) {
            _safeMint(addresses[i], ts + i + 1);
        }
    }

    numGiftedWitches += numToGift;
}
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">giftWitches</span>(<span class="hljs-params"><span class="hljs-keyword">address</span>[] <span class="hljs-keyword">calldata</span> addresses</span>) ... </span>{
    <span class="hljs-keyword">uint256</span> ts <span class="hljs-operator">=</span> totalSupply();
    <span class="hljs-keyword">uint256</span> numToGift <span class="hljs-operator">=</span> addresses.<span class="hljs-built_in">length</span>;

        ...

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&#x3C;</span> numToGift; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
        <span class="hljs-keyword">if</span> (balanceOf(addresses[i]) <span class="hljs-operator">&#x3C;</span> MAX_WITCHES_PER_WALLET) {
            _safeMint(addresses[i], ts <span class="hljs-operator">+</span> i <span class="hljs-operator">+</span> <span class="hljs-number">1</span>);
        }
    }

    numGiftedWitches <span class="hljs-operator">+</span><span class="hljs-operator">=</span> numToGift;
}
</code></pre><p>We addressed the issue in two ways. First, 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.</p><pre data-type="codeBlock" text="function giftWitches(address[] calldata addresses) ... {
    uint256 numToGift = addresses.length;
    numGiftedWitches += numToGift;

    for (uint256 i = 0; i &lt; numToGift; i++) {
        _safeMint(addresses[i], nextTokenId());
    }
}
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">giftWitches</span>(<span class="hljs-params"><span class="hljs-keyword">address</span>[] <span class="hljs-keyword">calldata</span> addresses</span>) ... </span>{
    <span class="hljs-keyword">uint256</span> numToGift <span class="hljs-operator">=</span> addresses.<span class="hljs-built_in">length</span>;
    numGiftedWitches <span class="hljs-operator">+</span><span class="hljs-operator">=</span> numToGift;

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&#x3C;</span> numToGift; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
        _safeMint(addresses[i], nextTokenId());
    }
}
</code></pre><p>Just removing the logic around the three-token limit eliminated this specific bug.</p><p>But the higher-level problem was how we were assigning token IDs—using <code>totalSupply()</code> made it unnecessarily fragile. Beyond that, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/dmccartney">Daniel McCartney</a> (a skilled arcanist and friend upon whom we had called to review the code) observed that we were calling <code>_safeMint()</code> in five different places, calculating the correct token ID in each place. He suggested extracting the logic into a <code>nextTokenId()</code> method we could call instead.</p><p>We decided to switch to using OpenZeppelin’s <code>Counters</code> library and wrote a new <code>nextTokenId()</code> function (as used above) that would ensure that we would never be able to get stuck in a state where the next token ID pointed to an existing token again.</p><pre data-type="codeBlock" text="Counters.Counter private tokenCounter;

function nextTokenId() private returns (uint256) {
    tokenCounter.increment();
    return tokenCounter.current();
}
"><code>Counters.Counter <span class="hljs-keyword">private</span> tokenCounter;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">nextTokenId</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">private</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
    tokenCounter.increment();
    <span class="hljs-keyword">return</span> tokenCounter.current();
}
</code></pre><p>The next problem was the potential community sale exploit that Mersenne had flagged. The tweak for that was simple; instead of just requiring that <code>msg.sender</code> 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.</p><pre data-type="codeBlock" text="mapping(address =&gt; uint256) public communityMintCounts;

function mintCommunitySale(
    uint8 numberOfTokens,
    bytes32[] calldata merkleProof
) ... {
    uint256 numAlreadyMinted = communityMintCounts[msg.sender];

    require(
        numAlreadyMinted + numberOfTokens &lt;= MAX_WITCHES_PER_WALLET,
        &quot;Max witches to mint in community sale is three&quot;
    );

    ...

    communityMintCounts[msg.sender] = numAlreadyMinted + numberOfTokens;

    for (uint256 i = 0; i &lt; numberOfTokens; i++) {
        _safeMint(msg.sender, nextTokenId());
    }
}
"><code><span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">address</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">uint256</span>) <span class="hljs-keyword">public</span> communityMintCounts;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mintCommunitySale</span>(<span class="hljs-params">
    <span class="hljs-keyword">uint8</span> numberOfTokens,
    <span class="hljs-keyword">bytes32</span>[] <span class="hljs-keyword">calldata</span> merkleProof
</span>) ... </span>{
    <span class="hljs-keyword">uint256</span> numAlreadyMinted <span class="hljs-operator">=</span> communityMintCounts[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>];

    <span class="hljs-built_in">require</span>(
        numAlreadyMinted <span class="hljs-operator">+</span> numberOfTokens <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span> MAX_WITCHES_PER_WALLET,
        <span class="hljs-string">"Max witches to mint in community sale is three"</span>
    );

    ...

    communityMintCounts[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">=</span> numAlreadyMinted <span class="hljs-operator">+</span> numberOfTokens;

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&#x3C;</span> numberOfTokens; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
        _safeMint(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, nextTokenId());
    }
}
</code></pre><h3 id="h-rolling-over-witches" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Rolling over WITCHES</h3><p>We needed a way to replicate the mints from the previous contract, which was straightforward enough. I wrote a function that took an array of addresses and minted them in order, along with a script that created and formatted the list by talking to the v1 contract.</p><pre data-type="codeBlock" text="function rollOverWitches(address[] calldata addresses) ... {
    require(
        tokenCounter.current() + addresses.length &lt;= 128,
        &quot;All witches are already rolled over&quot;
    );

    for (uint256 i = 0; i &lt; addresses.length; i++) {
        communityMintCounts[addresses[i]] += 1;
        _mint(addresses[i], nextTokenId());
    }
}
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">rollOverWitches</span>(<span class="hljs-params"><span class="hljs-keyword">address</span>[] <span class="hljs-keyword">calldata</span> addresses</span>) ... </span>{
    <span class="hljs-built_in">require</span>(
        tokenCounter.current() <span class="hljs-operator">+</span> addresses.<span class="hljs-built_in">length</span> <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span> <span class="hljs-number">128</span>,
        <span class="hljs-string">"All witches are already rolled over"</span>
    );

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&#x3C;</span> addresses.<span class="hljs-built_in">length</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
        communityMintCounts[addresses[i]] <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-number">1</span>;
        _mint(addresses[i], nextTokenId());
    }
}
</code></pre><p>All of these mints would count toward the community mint counts. It wasn’t a perfect mapping because a handful of these WITCHES had been gifts, but we didn’t want to further complicate the logic by attempting to differentiate between the two.</p><h2 id="h-making-v2-better" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Making v2 Better</h2><p>Now, the interesting bit. What could we do to improve the contract? With Mersenne, we suddenly had access to a depth of experience with Solidity. As we made changes to the contract, he reviewed them and suggested others. In the end, the v2 contract included five key improvements over v1.</p><h3 id="h-much-lower-gas" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Much Lower Gas</h3><p>We had made an effort to optimize for gas in v1 of the contract—but we didn’t realize that one choice we had made increased gas usage by ~30,000 per mint and per transfer. Nearly every single ERC-721 contract we read used <code>ERC721Enumerable</code>, the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.openzeppelin.com/contracts/4.x/api/token/erc721#ERC721Enumerable">extension</a> that supplied the <code>totalSupply()</code> function we had been using and, more importantly, maintained a mapping of which tokens belonged to a particular address.</p><p>We had added it without thinking too deeply about it; the value-add was ambiguous to us, but everyone else was using it, so we assumed it wouldn’t hurt to have the functionality. Without a clear mental model of what kinds of computations cost more or less gas, it didn’t occur to us that the data structures that the extension added could make the two key features of our contract (mints and transfers) as much as 50% more expensive.</p><p>With that knowledge, we decided to cut <code>ERC721Enumerable</code> in favor of off-chain implementations that didn’t require witches to pay a premium on every mint or transfer forever.</p><h3 id="h-reentrancy-guards" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Reentrancy Guards</h3><p>In writing the original contract, we considered reentrancy as an attack vector but incorrectly believed that our contract wasn’t susceptible because we weren’t calling arbitrary contracts. It turned out that we had missed a call inside OpenZeppelin’s implementation of <code>_safeMint()</code>—there was a <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol#L263">function</a> that <em>did</em> call a function on the receiver address if it was a contract. That meant that it was possible for contracts to call back into ours, sidestepping the checks we had put in place.</p><p>Because of that, we had to restructure how a few of our functions were written to update the state we were tracking in the contract before minting to ensure that they couldn’t be exploited with reentrancy.</p><p>For example, here was the original <code>claim()</code> function that allowed people to claim a WITCH for the cost of gas only:</p><pre data-type="codeBlock" text="function claim(bytes32[] calldata merkleProof) ... {
    uint256 ts = totalSupply();

    ...
    require(!claimed[msg.sender], &quot;Witch already claimed by this wallet&quot;);
    ...

    _safeMint(msg.sender, ts + 1);

    claimed[msg.sender] = true;
    numGiftedWitches += 1;
}
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">claim</span>(<span class="hljs-params"><span class="hljs-keyword">bytes32</span>[] <span class="hljs-keyword">calldata</span> merkleProof</span>) ... </span>{
    <span class="hljs-keyword">uint256</span> ts <span class="hljs-operator">=</span> totalSupply();

    ...
    <span class="hljs-built_in">require</span>(<span class="hljs-operator">!</span>claimed[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>], <span class="hljs-string">"Witch already claimed by this wallet"</span>);
    ...

    _safeMint(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, ts <span class="hljs-operator">+</span> <span class="hljs-number">1</span>);

    claimed[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
    numGiftedWitches <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-number">1</span>;
}
</code></pre><p>Logically, this code made sense—only once the mint was successful did we consider it claimed by the address and added to the total count of gifted WITCHES.</p><p>But because <code>_safeMint()</code> allowed reentrant calls back to the function, a malicious contract with a valid Merkle proof could keep calling back into <code>claim()</code> to mint more WITCHES, before we had been able to update our state.</p><p>Updating the state before performing actions like minting handled the issue.</p><pre data-type="codeBlock" text="function claim(bytes32[] calldata merkleProof) ... {
    require(!claimed[msg.sender], &quot;Witch already claimed by this wallet&quot;);

    claimed[msg.sender] = true;
    numGiftedWitches += 1;

    _safeMint(msg.sender, nextTokenId());
}
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">claim</span>(<span class="hljs-params"><span class="hljs-keyword">bytes32</span>[] <span class="hljs-keyword">calldata</span> merkleProof</span>) ... </span>{
    <span class="hljs-built_in">require</span>(<span class="hljs-operator">!</span>claimed[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>], <span class="hljs-string">"Witch already claimed by this wallet"</span>);

    claimed[<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>] <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
    numGiftedWitches <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-number">1</span>;

    _safeMint(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, nextTokenId());
}
</code></pre><p>On top of that, we opted to use <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.openzeppelin.com/contracts/4.x/api/security#ReentrancyGuard">OpenZeppelin’s </a><code>ReentrancyGuard</code> module, which offers a <code>nonReentrant</code> modifier that prevents reentrant calls to functions. We took care to add guards even to <code>onlyOwner</code> functions that only we could potentially exploit, as a measure to increase trust.</p><h3 id="h-modifiers" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Modifiers</h3><p>And, speaking of modifiers, they were the next improvement that Mersenne offered. We hadn’t been familiar with the concept in Solidity, but I had felt the pain of our oft-duplicated <code>require</code> calls across our functions.</p><p>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.</p><p>Here’s one example of how our <code>mint()</code> function changed. We started out with this code:</p><pre data-type="codeBlock" text="function mint(uint256 numberOfTokens) external payable {
    uint256 ts = totalSupply();
    require(isPublicSaleActive, &quot;Public sale is not open&quot;);
    require(
        balanceOf(msg.sender) + numberOfTokens &lt;= MAX_WITCHES_PER_WALLET,
        &quot;Max witches already minted to this wallet&quot;
    );
    require(
        ts + numberOfTokens &lt;= maxWitches - maxGiftedWitches,
        &quot;Not enough witches remaining to mint&quot;
    );
    require(
        PUBLIC_SALE_PRICE * numberOfTokens &lt;= msg.value,
        &quot;Incorrect ETH value sent&quot;
    );

    for (uint256 i = 0; i &lt; numberOfTokens; i++) {
        _safeMint(msg.sender, ts + i + 1);
    }
}
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mint</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> numberOfTokens</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span> </span>{
    <span class="hljs-keyword">uint256</span> ts <span class="hljs-operator">=</span> totalSupply();
    <span class="hljs-built_in">require</span>(isPublicSaleActive, <span class="hljs-string">"Public sale is not open"</span>);
    <span class="hljs-built_in">require</span>(
        balanceOf(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>) <span class="hljs-operator">+</span> numberOfTokens <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span> MAX_WITCHES_PER_WALLET,
        <span class="hljs-string">"Max witches already minted to this wallet"</span>
    );
    <span class="hljs-built_in">require</span>(
        ts <span class="hljs-operator">+</span> numberOfTokens <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span> maxWitches <span class="hljs-operator">-</span> maxGiftedWitches,
        <span class="hljs-string">"Not enough witches remaining to mint"</span>
    );
    <span class="hljs-built_in">require</span>(
        PUBLIC_SALE_PRICE <span class="hljs-operator">*</span> numberOfTokens <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">value</span>,
        <span class="hljs-string">"Incorrect ETH value sent"</span>
    );

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&#x3C;</span> numberOfTokens; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
        _safeMint(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, ts <span class="hljs-operator">+</span> i <span class="hljs-operator">+</span> <span class="hljs-number">1</span>);
    }
}
</code></pre><p>Not the worst, but also not easy to parse at a glance. Especially since several of those <code>require</code> calls were the same ones we needed in other functions like <code>mintCommunitySale()</code> or <code>giftWitches()</code>, pulling them out into modifiers vastly improved the reading experience and made the code less error-prone.</p><p>With modifiers, the content of the <code>mint()</code> function became trivial—three lines of code, with an easy-to-understand list of modifiers that applied to it.</p><pre data-type="codeBlock" text="modifier publicSaleActive() {
    require(isPublicSaleActive, &quot;Public sale is not open&quot;);
    _;
}

modifier maxWitchesPerWallet(uint256 numberOfTokens) {
    require(
        balanceOf(msg.sender) + numberOfTokens &lt;= MAX_WITCHES_PER_WALLET,
        &quot;Max witches to mint is three&quot;
    );
    _;
}

modifier canMintWitches(uint256 numberOfTokens) {
    require(
        tokenCounter.current() + numberOfTokens &lt;=
            maxWitches - maxGiftedWitches,
        &quot;Not enough witches remaining to mint&quot;
    );
    _;
}

modifier isCorrectPayment(uint256 price, uint256 numberOfTokens) {
    require(
        price * numberOfTokens == msg.value,
        &quot;Incorrect ETH value sent&quot;
    );
    _;
}

function mint(uint256 numberOfTokens)
    external
    payable
    nonReentrant
    isCorrectPayment(PUBLIC_SALE_PRICE, numberOfTokens)
    publicSaleActive
    canMintWitches(numberOfTokens)
    maxWitchesPerWallet(numberOfTokens)
{
    for (uint256 i = 0; i &lt; numberOfTokens; i++) {
        _safeMint(msg.sender, nextTokenId());
    }
}
"><code><span class="hljs-function"><span class="hljs-keyword">modifier</span> <span class="hljs-title">publicSaleActive</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-built_in">require</span>(isPublicSaleActive, <span class="hljs-string">"Public sale is not open"</span>);
    <span class="hljs-keyword">_</span>;
}

<span class="hljs-function"><span class="hljs-keyword">modifier</span> <span class="hljs-title">maxWitchesPerWallet</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> numberOfTokens</span>) </span>{
    <span class="hljs-built_in">require</span>(
        balanceOf(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>) <span class="hljs-operator">+</span> numberOfTokens <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span> MAX_WITCHES_PER_WALLET,
        <span class="hljs-string">"Max witches to mint is three"</span>
    );
    <span class="hljs-keyword">_</span>;
}

<span class="hljs-function"><span class="hljs-keyword">modifier</span> <span class="hljs-title">canMintWitches</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> numberOfTokens</span>) </span>{
    <span class="hljs-built_in">require</span>(
        tokenCounter.current() <span class="hljs-operator">+</span> numberOfTokens <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span>
            maxWitches <span class="hljs-operator">-</span> maxGiftedWitches,
        <span class="hljs-string">"Not enough witches remaining to mint"</span>
    );
    <span class="hljs-keyword">_</span>;
}

<span class="hljs-function"><span class="hljs-keyword">modifier</span> <span class="hljs-title">isCorrectPayment</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> price, <span class="hljs-keyword">uint256</span> numberOfTokens</span>) </span>{
    <span class="hljs-built_in">require</span>(
        price <span class="hljs-operator">*</span> numberOfTokens <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">value</span>,
        <span class="hljs-string">"Incorrect ETH value sent"</span>
    );
    <span class="hljs-keyword">_</span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mint</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> numberOfTokens</span>)
    <span class="hljs-title"><span class="hljs-keyword">external</span></span>
    <span class="hljs-title"><span class="hljs-keyword">payable</span></span>
    <span class="hljs-title">nonReentrant</span>
    <span class="hljs-title">isCorrectPayment</span>(<span class="hljs-params">PUBLIC_SALE_PRICE, numberOfTokens</span>)
    <span class="hljs-title">publicSaleActive</span>
    <span class="hljs-title">canMintWitches</span>(<span class="hljs-params">numberOfTokens</span>)
    <span class="hljs-title">maxWitchesPerWallet</span>(<span class="hljs-params">numberOfTokens</span>)
</span>{
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&#x3C;</span> numberOfTokens; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
        _safeMint(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, nextTokenId());
    }
}
</code></pre><h3 id="h-erc-20-withdrawals" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">ERC-20 Withdrawals</h3><p>We had a <code>withdraw()</code> function in v1 of the contract to withdraw the ETH from the mint, but Mersenne added a function to withdraw ERC-20 tokens as well, so that we were able to pull out any accidentally sent to our contract address.</p><p>Initially I thought that it was nice to have this function, just in case, but also the odds weren’t super high that we’d need it. I was wrong! EIP-2981, the standard for on-chain royalties, <em>explicitly</em> <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-2981#unit-less-royalty-payment-across-all-marketplaces-both-on-chain-and-off-chain">does not specify a currency or token</a>, and, in fact, some major marketplaces like LooksRare that have implemented on-chain royalties pay them in WETH, not ETH. Without this function, we wouldn’t have been able to withdraw these royalties; they would be trapped in the contract forever.</p><h3 id="h-escape-hatch-for-opensea" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Escape Hatch for OpenSea</h3><p>Finally, as we wrapped work on the contract and readied for redeployment, Mersenne noted that 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.</p><pre data-type="codeBlock" text="bool private isOpenSeaProxyActive = true;

function setIsOpenSeaProxyActive(bool _isOpenSeaProxyActive) ... {
    isOpenSeaProxyActive = _isOpenSeaProxyActive;
}

function isApprovedForAll(address owner, address operator) ... {
    ...

    if (
        isOpenSeaProxyActive &amp;&amp;
        address(proxyRegistry.proxies(owner)) == operator
    ) {
        return true;
    }

    ...
}
"><code><span class="hljs-keyword">bool</span> <span class="hljs-keyword">private</span> isOpenSeaProxyActive <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setIsOpenSeaProxyActive</span>(<span class="hljs-params"><span class="hljs-keyword">bool</span> _isOpenSeaProxyActive</span>) ... </span>{
    isOpenSeaProxyActive <span class="hljs-operator">=</span> _isOpenSeaProxyActive;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">isApprovedForAll</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> owner, <span class="hljs-keyword">address</span> operator</span>) ... </span>{
    ...

    <span class="hljs-keyword">if</span> (
        isOpenSeaProxyActive <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span>
        <span class="hljs-keyword">address</span>(proxyRegistry.proxies(owner)) <span class="hljs-operator">=</span><span class="hljs-operator">=</span> operator
    ) {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
    }

    ...
}
</code></pre><p>It was a helpful reminder that, 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.</p><h2 id="h-sealing-the-contract" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Sealing the Contract</h2><p>At last, on November 3, we were ready to redeploy. The whole process, as long and arduous as it had felt, had taken just two days.</p><p>I deployed the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://etherscan.io/address/0xe6ddda1c3f1cb01aa5c86a21e8636deabfd1f013#code">v2 contract</a>, 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.</p><p>In the coming weeks, the state of the project would transform utterly. The coven would start to grow—slowly at first, and then all at once. Soon, countless people we admired, in and out of web3, would start to assume the visage of a WITCH. The first sprouts of conceptual expansions of our world would start to emerge out of the community. And, underneath it all, the contract (its own subject of <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://emamo.com/event/web3con/s/an-analysis-of-a-good-nft-contract-crypto-coven-NP8PzN">no</a> <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/cygaar_dev/status/1486154256218353665">little</a> <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/mannynotfound/status/1470535464922845187">praise</a>) hummed, surely and steadily, holding the coven fast.</p><h1 id="h-coda" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Coda</h1><p>I close this imparting of learnings with perhaps the most important one of all: no smart contract is perfect. The same is true for ours. The Crypto Coven contract that now lives on mainnet contains two known bugs, one of which spelled the return of the void witch. Both are detailed in <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://cryptocoven.mirror.xyz/0eZ0tjudMU0ByeXLlRtPzDqxGzMMZw6ldzf-HfYETW0">this earlier post</a>, and fortunately neither was severe enough that we had to take more drastic action again.</p><p>Even outside of these flaws, with the knowledge we’ve accrued since this first attempt at conjuring artifacts out of the ether, 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.</p><p>And, finally: writing immutable code that deals in large sums of coin is emotionally taxing. The sob emoji became the most frequently used on my keyboard; I’ve cursed the very existence of smart contracts more times than I can remember.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/28668dd6d1b5e2e49b7dc6ee2403086ab3d5f1459dfc669ab3cd25d2c8da5293.png" alt="abolish-smart-contracts.png" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">abolish-smart-contracts.png</figcaption></figure><p>But, more earnestly, it is <em>good</em> 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.</p><p>There will be errors, because code is code, and constructing an incantation to perfectly mirror your wishes requires more than just caution or care. But, as arcanists, the most we can endeavor to do is our finest work—and no less.</p>]]></content:encoded>
            <author>crypto-coven-2@newsletter.paragraph.com (Crypto Coven)</author>
        </item>
        <item>
            <title><![CDATA[forging a witch: crypto coven art generator overview]]></title>
            <link>https://paragraph.com/@crypto-coven-2/forging-a-witch-crypto-coven-art-generator-overview</link>
            <guid>5cXOj0vhyn10Gczcztt9</guid>
            <pubDate>Mon, 07 Mar 2022 07:03:05 GMT</pubDate>
            <description><![CDATA[Hello! ✨ I am Nyx, Witch of Woe for Crypto Coven. I conjure visions for the weird wilds through tone and concept, am one of our dual artists with Aletheia, and delicately weave layers of logic in code for the coven. Today I’d like to speak to our generator and how a WITCH is forged.generative witches - crypto coven characters are created through a mix of art + codeOur generator is responsible for two parts of your witch’s creation:Defining a .json file of text that contains the metadata for y...]]></description>
            <content:encoded><![CDATA[<p>Hello! ✨ I am Nyx, Witch of Woe for Crypto Coven. I conjure visions for the weird wilds through tone and concept, am one of our dual artists with Aletheia, and delicately weave layers of logic in code for the coven.</p><p>Today I’d like to speak to our generator and how a WITCH is forged.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/6e28ef9707f254fadde6ca69eb96c069cbcdd52feac0963dc5bfdd674fb92590.png" alt="generative witches - crypto coven characters are created through a mix of art + code" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">generative witches - crypto coven characters are created through a mix of art + code</figcaption></figure><p>Our generator is responsible for two parts of your witch’s creation:</p><ol><li><p>Defining a <code>.json</code> file of text that contains the metadata for your witch, so their definitions are in a parseable format for anyone to read or display (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://ipfs.io/ipfs/QmYWAa2seZpyWnMnmufDMb9DsSYq2y2zSWRBtVnFP9iTTn/9755.json">like so</a>)</p></li><li><p>Choosing the traits that comprise their visage and painting the final image</p></li></ol><p>To begin, some background context on ERC-721s and what that inscrutable <code>.json</code> file contains:</p><h2 id="h-the-technical-shape-of-a-witch" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The technical shape of a witch</h2><p>(If you would like to skip to the more creative code, feel free to hop below!)</p><p>The final form of a witch is an ERC-721 token, stored on the Ethereum mainnet. As a construct, they are quite simple—a token with an owner and a pointer to an amorphous blob of metadata. ERC-721s are called “non-fungible,” since the abstraction is used to attest that each witch is unique and may not be equivalently exchanged for any other.</p><p>A US dollar or a bottle of perfume still in its package is, by contrast, fungible and can be swapped for another of the same kind interchangeably.</p><p>Witches associated with a non-fungible tokens (NFT), allow their owner to take them to any web3 destination that may welcome a WITCH. It is stored in your wallet where you alone have custody, rather than a centralized database that may only be accessible through a single platform (like your Fallout 3 character, or an instagram post).</p><p>A witch’s metadata takes the following shape, complying to a loose standard for <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://nftschool.dev/reference/metadata-schemas/">NFTs in general</a>, augmented by recommendations <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.opensea.io/docs/metadata-standards">from OpenSea</a> to help standardize their marketplace.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/26662e2582e727b2b2e56207aa984e9ac2b2d41c7ed0adef22a96d8770f0f7f0.jpg" alt="pseudocode for crypto coven&apos;s metadata" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">pseudocode for crypto coven&apos;s metadata</figcaption></figure><p>Inside the <code>attributes</code> array, we define aspects that are custom to the project and not shared by the overall NFT standard (physical traits, attunements, horoscopes, etc.). The shape of these objects are arbitrary—we followed a standard that lets marketplaces create filters for the collection, so you could find a <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://opensea.io/collection/cryptocoven?search%5BsortAscending%5D=true&amp;search%5BsortBy%5D=PRICE&amp;search%5BstringTraits%5D%5B0%5D%5Bname%5D=Sun%20Sign&amp;search%5BstringTraits%5D%5B0%5D%5Bvalues%5D%5B0%5D=Taurus&amp;search%5BstringTraits%5D%5B1%5D%5Bname%5D=Archetype%20of%20Power&amp;search%5BstringTraits%5D%5B1%5D%5Bvalues%5D%5B0%5D=Enchantress&amp;search%5BstringTraits%5D%5B2%5D%5Bname%5D=Hair%20Color&amp;search%5BstringTraits%5D%5B2%5D%5Bvalues%5D%5B0%5D=Pink">Taurus enchantress with pink hair</a> if you like.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/271d2902a7669930c7ebf86b0dafc6c9fa923d31424600ed8b39d881b6ad782d.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>Once a witch is constructed—name, archetype, articulation, astrological chart, physical aspects, and final image—our generator uploads all of the above to <code>ipfs</code>, a decentralized file storage system similar to a peer-to-peer torrent network.</p><p>We pay <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.pinata.cloud/">Pinata</a> to “pin” our metadata <code>.json</code> files there so that they are distributed across the network and are not subject to garbage collection if they are not accessed for a while. Our smart contract points to the individual metadata files on <code>ipfs</code>, and thus a witch is stored and rendered persistently.</p><p>The final images of the witches themselves are hosted on <code>s3</code>—we wanted them to load quickly when a witch desired them, even at large sizes. The caching and infrastructure of a more mature platform lends itself well to that. The images are also easily backed up in case the centralized entity disappears and can be restored using the metadata if all else fails.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/993f32adcf75176cfdbbb051cc6324727166e1b59fb8023891fc479bbe8935be.png" alt="A witch and their archetype, astrological chart, articulation, and attunements" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">A witch and their archetype, astrological chart, articulation, and attunements</figcaption></figure><h2 id="h-painting-a-witchs-visage" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Painting a witch’s visage</h2><p>Constructing a witch, however, has little to do with how it is stored. It’s similar to coding a doll-maker, the ancient internet girl art form perfected by <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://www.styledollz.com/">Dollz</a> and <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://picrew.me/">Picrew</a>.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/7744c1b962aafbdbcc61bf91490c86a5e7e93216b7bcbd168683cc22b7f06b17.png" alt="“Generative artwork” is a broad term and can take many forms. At its core, it is art that can manifest in infinite ways depending on external forces, whether the taste of the viewer, input from a sensor, or the whims of a random number generator." blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">“Generative artwork” is a broad term and can take many forms. At its core, it is art that can manifest in infinite ways depending on external forces, whether the taste of the viewer, input from a sensor, or the whims of a random number generator.</figcaption></figure><p>What was most important to us is that we had the flexibility to create a set of witches that were truly diverse—a range of body sizes, skin tones, and hair textures, where as many people as possible could find themselves in a witch. A lofty goal, made difficult to achieve by traits that by default are randomly shuffled together.</p><p>We also wanted the witches to be visually unique, not merely computationally. Especially at a large scale (such as 9,999 witches) with hand-drawn layers, it’s easy for each of the characters to blend together or feel extremely similar if there are not enough combinations. We needed to optimize for <strong>composability</strong> so we had enough variety each witch could stand on their own.</p><p>The first task of constructing the witches was to define what <code>attributes</code> we would need and their relationships to each other.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/f7fa0ee300a9819b4057968b602d076b09822c559f7199986938c2ad5b539dc1.png" alt="Early explorations of witch body types, accessories, hair" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Early explorations of witch body types, accessories, hair</figcaption></figure><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/317e7b8fa1d7a3d0f2da686ae584f9d5bddccf7f70324c3bc8c8e6b877810edb.png" alt="Experimenting with shoulder and face placement, so hair styles could be used consistently across body types by keeping the chin and the eyes roughly in the same place" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Experimenting with shoulder and face placement, so hair styles could be used consistently across body types by keeping the chin and the eyes roughly in the same place</figcaption></figure><p>Our very first iteration of the generator was based on hashlip’s <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/HashLips/generative-art-node">generative-art-node</a>, which buckets layers in the file system by attribute (eyes, mouth, hair, etc.), randomizes them, and has a simple scaffold to paint onto a javascript <code>canvas</code> using <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.npmjs.com/package/canvas">Node.js</a>. It’s a great place to start for simple 2D character projects.</p><p>The setup feels similar to <code>processing.js</code>, which has a deep archive of excellent <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://processing.org/tutorials/">tutorials</a> if you’re looking to dip your toe in generative art and would like more structure getting started. It’s helpful to have a mental model that your code paints layers on a canvas, where each canvas is a single frame in time.</p><p>For the witches, I began by creating some quick sketch layers to deliberately trigger edge cases and collisions.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/70e2c06f2fcd450aa385178a181f3319a658844d89e7413fbc512a7e81db71e6.png" alt="Early witch prototypes, which focused on unique silhouettes, assets we wanted that would cause collisions, and exploring how we could compose hairstyles. I wanted to make sure we accounted for natural hair textures up front." blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Early witch prototypes, which focused on unique silhouettes, assets we wanted that would cause collisions, and exploring how we could compose hairstyles. I wanted to make sure we accounted for natural hair textures up front.</figcaption></figure><p>I think an easy trap is to create layers that look cohesively good on a single character but are not composable, limited the range of your collection. Or to avoid tricky edge cases like the purple starry skies hair that do not work well with hats until the very end, coding yourself into a corner where your abstractions are not flexible enough to accommodate them.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/5ae86f8836353c8f9eaa2444c50bfdc54775d0316b3685f8f90dcca5aeaf28c9.png" alt="Our first witch ever rolled—strong “I was randomly generated” vibes" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Our first witch ever rolled—strong “I was randomly generated” vibes</figcaption></figure><p>Quickly we refined some basic rules—different parts of a hairstyle should be the same color, and witches should not roll conflicting items to wear on their head (forehead jewelry would not fit with hats).</p><p>A witch with every attribute at once often looked cluttered, so we decided to flip a weighted coin to determine which layers were present and which were not.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/756b362e4cd7ddcb9f36353322bf4ba195e77384132d6938b3ebc56b18914e36.png" alt="MVP randomizers in the builder code are truly the bedrock of a generative PFP project. Semantically naming them  helped us discuss and share a mental model of how a witch is constructed and gave the codebase a similar vibe to a pirate casino, which I deeply enjoyed." blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">MVP randomizers in the builder code are truly the bedrock of a generative PFP project. Semantically naming them helped us discuss and share a mental model of how a witch is constructed and gave the codebase a similar vibe to a pirate casino, which I deeply enjoyed.</figcaption></figure><p>While isolating layers by style or parts of the body worked for most accessories, our most complicated guardrail logic revolved around hair.</p><p>A simple color match often resulted in chaotic styles and limited the range we could achieve. We defined textures that were a requirement for the matching logic (straight, wavy, curly, braids, cloud) and broke hair up into even more granular layers we could compose.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/dc7a647b40a1aadd2bf36f90e1d0af7d899a9a8223c0cc322a569b6b61fce568.png" alt="Early combination logic before we added an additional requirement for hair texture. Some bangs work with any texture, while others like the straight blunt nyx bob require textures to match." blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Early combination logic before we added an additional requirement for hair texture. Some bangs work with any texture, while others like the straight blunt nyx bob require textures to match.</figcaption></figure><p>Composing hair became a series of array intersections. Roll for bangs, flip a coin to determine optional back, front, middle—then choose from a set of styles for each category that matched restraints for hair texture, color, body shape, and archetype.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/5a428eeab86623cfa0efaf486693f7438fd84af057db635548295338deeaf203.png" alt="Working out hats with hair texture. We originally had every asset that sat on a witch’s head check to see if it could fit with the styles in that texture. Once the number and variety of hair styles scaled, we layered on the ability for individual styles to exclude categories of assets, like hats." blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Working out hats with hair texture. We originally had every asset that sat on a witch’s head check to see if it could fit with the styles in that texture. Once the number and variety of hair styles scaled, we layered on the ability for individual styles to exclude categories of assets, like hats.</figcaption></figure><p>Some styles (like the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://zora.co/collections/0x5180db8F5c931aaE63c74266b211F580155ecac8">dramatic swoop</a> or <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://zora.co/collections/0x5180db8F5c931aaE63c74266b211F580155ecac8">cloudy skies</a>) were intentionally drawn with a matching bang and back, while others were made to <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://zora.co/collections/0x5180db8F5c931aaE63c74266b211F580155ecac8">mix and match</a> to diversify the silhouettes in the overall collection.</p><p>We defined requirements and exclusions for certain styles and the large set of possible combinations helped the coven feel visually unique, not just computationally.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/310197a8882170c719a0d9542bd046b77673d0732a24bc779d424012f3daa6fe.png" alt="The styles on the left were drawn to be cohesive (and matched with nothing else), while the Keridwen bangs on the middle and right can be composed with any hair back of the same color." blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">The styles on the left were drawn to be cohesive (and matched with nothing else), while the Keridwen bangs on the middle and right can be composed with any hair back of the same color.</figcaption></figure><p>For more complex styles, we experimented with how we wanted to solve visual collisions by adding generic rulesets in the code, versus changing how the layers were structured in Photoshop.</p><p>For example, the pearl hood on the top left below chained multiple requirements when one of the pieces were rolled (the hood front required the matching <code>hat-back</code>, <code>body-over</code>, and pearl <code>facewear</code>), but the pearls could be rolled alone as headwear requiring nothing else.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/bead6199e40ebe9c26a514646b810bff620485023133f934148e04446a4fb4eb.png" alt="More complicated styles (like the pearled hood on the top left) were created by requiring multiple traits at roll: hat-back, hat-front, face-wear, body-over. Simpler assets like the sheer wrapped scarf could be done as a front/back." blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">More complicated styles (like the pearled hood on the top left) were created by requiring multiple traits at roll: hat-back, hat-front, face-wear, body-over. Simpler assets like the sheer wrapped scarf could be done as a front/back.</figcaption></figure><p>To achieve all ends, we defined 21 attributes (or layer types) to express the witches. We settled on a system where some attributes would deliberately exclude each other (horns could not be worn with hats), and others would always be required (<code>hat-backs</code> require a <code>hat-front</code>). The strict layer order is the one rule it is easiest not to circumvent in the painting code, since edge cases and bugs are already complex enough to reason about.</p><p>Tagging each hand-drawn style with extra metadata like hair texture, color, body size, archetype, requirements and exclusions, we soon had a witch roller that produced a ~cute~ witch for most rolls.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/29539d83d5dda1633a0a15b5136dee375d957e56cf61f079f37f23f372d1aa4a.png" alt="Some baby prototype witches" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Some baby prototype witches</figcaption></figure><h2 id="h-assembling-an-entire-covenscaling-to-9999" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Assembling an entire coven—scaling to 9,999</h2><p>As Aletheia + I defined styles for particular archetypes, individual styles needed to maintain a relationship with each other and not just the broad categories of attributes.</p><p>We ended up tossing out most of the original <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/HashLips/generative-art-node">generative-art-node</a> structure that relied on filenames for style metadata and moved to a proper database. Xuannü devised a visually beautiful and easily modifiable table for us to upload and manage the individual styles, which allowed us to break out the metadata and have all the high witches help with tagging and refinement for each individual layer.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/b6485c1bbed43fe72d9e9b984509390d4e1227409e0f3f1f9a7f352e50e27416.png" alt="Styles the frame the face or sit on the shoulders needed to be adjusted per body type and tagged accordingly to be matched correctly. Here&apos;s the three body types wearing the chiseled vogue hat. " blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Styles the frame the face or sit on the shoulders needed to be adjusted per body type and tagged accordingly to be matched correctly. Here&apos;s the three body types wearing the chiseled vogue hat.</figcaption></figure><p>As the system evolved, our internal “witch roller” let us perceive the sum of these parts as the high witches refined separate aspects, spot-checking for edge cases as we added new assets. Articulations from Keridwen inspired visual styles, Xuannü charted the stars at each WITCH&apos;s summoning, and Aradia bequeathed each a name.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/cba6ffd74ef89c181b9ea3f1834cd1aafd8fee0dfa5e2c5f3cf26a8adfff408d.png" alt="A classic witch and accompanying metadata" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">A classic witch and accompanying metadata</figcaption></figure><p>the overall structure of the generator also boiled down to something quite simple, thanks to the abstractions in our data model. (though hair continued to be quite complex)</p><ol><li><p>roll to pick a witch’s archetype</p></li><li><p>roll to pick their base model (skin color + body shape)</p></li><li><p>roll for bangs, and choose a hairstyle.</p></li><li><p>check the remainder of the attributes in their drawing order, flip a coin if the attribute is optional, and roll a weighed dice to choose from the valid styles not excluded by any previous attribute. Add any dependencies for the chosen style.</p></li><li><p>check that the resulting witch is unique, and not duplicated in the overall collection.</p></li></ol><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/c42c0096ea79380c055348928512f4551ba07e63d2c0cd61edd3d1a0deba83a9.png" alt="Another early witch whose magic spawns from a long-forgotten kiss and who looks like someone who would pad their bed with eiderdown feathers and dreams" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Another early witch whose magic spawns from a long-forgotten kiss and who looks like someone who would pad their bed with eiderdown feathers and dreams</figcaption></figure><p>When we needed to make a one-off exception for a certain art asset, Xuannü’s system of <code>dependencies</code>, <code>exclusions</code>, and <code>requirements</code> extended past attributes to individual styles, giving us the abstractions to create specific styles and tweak what assets they did not work with dynamically in our database.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/d8f32e41c83ade6b9ac81242562789c9685a56b1dbf4e5478217186144e44bc9.png" alt="Defining where we wanted to lean on pure abstraction, and where that introduced too much complexity for specific edge cases like hair or hats, was a delicate line. Here’s the final rule set I ended up writing for combining all of the layer restrictions." blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Defining where we wanted to lean on pure abstraction, and where that introduced too much complexity for specific edge cases like hair or hats, was a delicate line. Here’s the final rule set I ended up writing for combining all of the layer restrictions.</figcaption></figure><p>Working with Aletheia to draw new styles and layering on rules and logic to accommodate them in the generator became a satisfying mix of art and science.</p><p>To define new rulesets, I’d roll massive test sets scanning for clipping errors and pulling out patterns where assets would clash.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/c4b658fa07189c24bcff1259f10746f874376c31a8ec715d3311dd0babc03d9f.png" alt="Bulk test rolls scanning for clipping errors and tweaking our matching algorithm" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Bulk test rolls scanning for clipping errors and tweaking our matching algorithm</figcaption></figure><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/d05e8de01165ed5a59e0feac9d9196d270a3777adddd17017216d1c7c3fc07f9.png" alt="Early witches that were too busy. Test rolls helped catch layer collisions and refine aesthetics for each of the archetypes. This set resulted in a rule that limited the number of items you could have render on a face. The demon bloom eyes in the top left also invalidated the layer order for face markings by drawing on a lower area of the face, which were then adjusted." blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Early witches that were too busy. Test rolls helped catch layer collisions and refine aesthetics for each of the archetypes. This set resulted in a rule that limited the number of items you could have render on a face. The demon bloom eyes in the top left also invalidated the layer order for face markings by drawing on a lower area of the face, which were then adjusted.</figcaption></figure><p>Some edge cases were easier to solve in the art files, like accommodating bangs with transparency. Styles like the buzz cut and baby hairs needed to conditionally render on top of other assets that sat on the head, like horns or crowns causing clipping errors.</p><p>For the buzz cut, we ended up baking it into the base model, since there was no way to render the style that worked across skin tones and have it cover the base of horns properly. For baby hairs, it was easier to lower the hairline to not conflict with the horn assets.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/7b3ab5bfcf9794dfef4a7cc989259bad91c0d9dc8cde86b35c22606b4eb337a7.png" alt="Bang hairstyles where we wanted to include transparency was an early edge case we ended up solving in the art files themselves." blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Bang hairstyles where we wanted to include transparency was an early edge case we ended up solving in the art files themselves.</figcaption></figure><p>By the end, we were able to accommodate an expressive range of fantastical and realistic styles combined with random-ish generation, like this rather generous interpretation of “hat” across the different archetypes I’m quite proud of.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/2e4f257288289029b8877457d12493a0f031d1f761642027720c2ae8e857b699.png" alt="Whe wide range of “hats” a  hat-front with an optional  hat-back could express for the coven. Some edge cases we addressed with code (e.g., the Nyx veil required Nyx bangs and would be excluded from the option of hats if that hairstyle was not rolled)—and some with the underlying art (turbans were sorted into a hair-bang since requiring a bald head made them extremely rare)." blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Whe wide range of “hats” a hat-front with an optional hat-back could express for the coven. Some edge cases we addressed with code (e.g., the Nyx veil required Nyx bangs and would be excluded from the option of hats if that hairstyle was not rolled)—and some with the underlying art (turbans were sorted into a hair-bang since requiring a bald head made them extremely rare).</figcaption></figure><p>With millions of potential witches, the last step of generation was a hand of human curation, and choosing which witches would be added to the collection. While most glaring visual issues were gone 🤞, aestheic preference that could not be captured by broad rules of thumb were imprinted in this final phase. (Aletheia still caught some subtle edge cases I definitely missed.) We rolled ~15k witches to narrow down to our final set of 9999.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/24683cf0add817457402f8e5749f7008a70265ab9e02672afdd939eac472e02a.png" alt="an uncurated set of witches generated during a test roll. Initially when curating it was hard to get rid of any of them, but we soon became ruthless in our preferences, looking for witches we&apos;d want in our own wallets. " blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">an uncurated set of witches generated during a test roll. Initially when curating it was hard to get rid of any of them, but we soon became ruthless in our preferences, looking for witches we&apos;d want in our own wallets.</figcaption></figure><p>Writing the generator was like taking a deep stretch after a long time spent stiff hunched over a chair. It rekindled my love of generative art (and coding), and was a fantastic introduction to the web3 space.</p><p>I hope your curiosity was sated, and that we may cross paths in the wilds ✨</p><p>-Nyx</p>]]></content:encoded>
            <author>crypto-coven-2@newsletter.paragraph.com (Crypto Coven)</author>
        </item>
        <item>
            <title><![CDATA[Crypto Coven Contract Bugs: An Arcanist’s Addendum]]></title>
            <link>https://paragraph.com/@crypto-coven-2/crypto-coven-contract-bugs-an-arcanist-s-addendum</link>
            <guid>3Z4C4N4TlmsHELMBAeSS</guid>
            <pubDate>Thu, 10 Feb 2022 22:29:37 GMT</pubDate>
            <description><![CDATA[A warm welcome, arcanists! I’m Xuannü, the WITCH of WILL and keeper of the coven at Crypto Coven. As our anointed blockchain whisperer, I wrote the ERC-721 contract for the genesis series of 9,999 WITCHES, aided by our acolytes Oizys and Enthea and audited by our Solidity adeptus Mersenne. We’ve been deeply gratified and (honestly) shocked to receive a lot of praise for our first foray into web3 development, and we’re quite proud of what we’ve been able to do with so little experience—we have...]]></description>
            <content:encoded><![CDATA[<p>A warm welcome, arcanists! I’m Xuannü, the WITCH of WILL and keeper of the coven at Crypto Coven.</p><p>As our anointed blockchain whisperer, I wrote <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://etherscan.io/address/0x5180db8F5c931aaE63c74266b211F580155ecac8#contracts">the ERC-721 contract</a> for the genesis series of 9,999 WITCHES, aided by our acolytes <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/0xoizys">Oizys</a> and Enthea and audited by our Solidity adeptus <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/matthewdif">Mersenne</a>.</p><p>We’ve been deeply gratified and (honestly) shocked to receive a lot of praise for our first foray into web3 development, and we’re quite proud of what we’ve been able to do with so little experience—we have a deeper dive into the contract as a whole in the works, coming soon.</p><p>However, for newer Solidity developers using the Crypto Coven contract as a reference, we wanted to pay it forward by sharing two bugs we’ve discovered in our contract and providing examples of correct implementations.</p><h1 id="h-the-return-of-the-void-witch-total-mintable-witches" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Return of the Void Witch: Total Mintable WITCHES</h1><p>Oh, gifting logic. A painfully simple but subtle <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://cryptocoven.xyz/curse-of-the-void-witch">bug</a> in the gifting logic had <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://etherscan.io/address/0xe6ddda1c3f1cb01aa5c86a21e8636deabfd1f013#code">our first smart contract</a> grinding to a halt, and in our redeployed contract, we found ourselves plagued once again by a different gifting bug.</p><p>We wrote this modifier <code>canMintWitches()</code> to check if addresses should be able to mint more WITCHES in the public sale:</p><pre data-type="codeBlock" text="uint256 public maxWitches; // initialized to 9,999
uint256 public maxGiftedWitches; // initialized to 250

modifier canMintWitches(uint256 numberOfTokens) {
    require(
        tokenCounter.current() + numberOfTokens &lt;=
            maxWitches - maxGiftedWitches,
        &quot;Not enough witches remaining to mint&quot;
    );
    _;
}
"><code><span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> maxWitches; <span class="hljs-comment">// initialized to 9,999</span>
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> maxGiftedWitches; <span class="hljs-comment">// initialized to 250</span>

<span class="hljs-function"><span class="hljs-keyword">modifier</span> <span class="hljs-title">canMintWitches</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> numberOfTokens</span>) </span>{
    <span class="hljs-built_in">require</span>(
        tokenCounter.current() <span class="hljs-operator">+</span> numberOfTokens <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span>
            maxWitches <span class="hljs-operator">-</span> maxGiftedWitches,
        <span class="hljs-string">"Not enough witches remaining to mint"</span>
    );
    <span class="hljs-keyword">_</span>;
}
</code></pre><p>What’s the bug? It’s a difficult one to spot because it’s only a bug given certain conditions.</p><p>The problem is that there should be 9,749 WITCHES available to mint in the public function and 250 reserved to mint by the owner-only function, for a total of 9,999. This logic works perfectly if no WITCHES are gifted until the public sale is sold out.</p><p>However, we had always intended to gift WITCHES throughout the mint, which means that the right side of that check should have been dynamic. As the number of gifted WITCHES increased, the allowable highest token ID for witches in the public sale should have increased as well.</p><p>At the time that the public sale closed, we had gifted 93 WITCHES. That meant that <code>tokenCounter.current()</code> had reached 9,749, ending the open mint period—but only 9,656 of them were WITCHES minted in the community and public sales.</p><p>The <code>canGiftWitches()</code> function, however, intentionally limited the number of WITCHES we could gift to 250, which meant that we couldn’t circumvent the bug that way, either:</p><pre data-type="codeBlock" text="uint256 public maxWitches; // initialized to 9,999
uint256 public maxGiftedWitches; // initialized to 250
uint256 private numGiftedWitches;

modifier canGiftWitches(uint256 num) {
    require(
        numGiftedWitches + num &lt;= maxGiftedWitches,
        &quot;Not enough witches remaining to gift&quot;
    );
    require(
        tokenCounter.current() + num &lt;= maxWitches,
        &quot;Not enough witches remaining to mint&quot;
    );
    _;
}
"><code><span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> maxWitches; <span class="hljs-comment">// initialized to 9,999</span>
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> maxGiftedWitches; <span class="hljs-comment">// initialized to 250</span>
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">private</span> numGiftedWitches;

<span class="hljs-function"><span class="hljs-keyword">modifier</span> <span class="hljs-title">canGiftWitches</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> num</span>) </span>{
    <span class="hljs-built_in">require</span>(
        numGiftedWitches <span class="hljs-operator">+</span> num <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span> maxGiftedWitches,
        <span class="hljs-string">"Not enough witches remaining to gift"</span>
    );
    <span class="hljs-built_in">require</span>(
        tokenCounter.current() <span class="hljs-operator">+</span> num <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span> maxWitches,
        <span class="hljs-string">"Not enough witches remaining to mint"</span>
    );
    <span class="hljs-keyword">_</span>;
}
</code></pre><p>As a result, 93 WITCHES are lost to the void—this contract will only ever mint 9,906 in total.</p><p>(For anyone curious about the fate of the WITCHES trapped somewhere in the ether: stay attuned, for these WITCHES may yet find their way back to the weird wilds...)</p><h2 id="h-fixing-the-implementation" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Fixing the Implementation</h2><p>This bug is easily remedied in our contract by adding <code>numGiftedWitches</code>, a variable we already store, to the check:</p><pre data-type="codeBlock" text="uint256 public maxWitches; // initialized to 9,999
uint256 public maxGiftedWitches; // initialized to 250
uint256 private numGiftedWitches;

modifier canMintWitches(uint256 numberOfTokens) {
    require(
        tokenCounter.current() + numberOfTokens &lt;=
            maxWitches - maxGiftedWitches + numGiftedWitches,
        &quot;Not enough witches remaining to mint&quot;
    );
    _;
}
"><code><span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> maxWitches; <span class="hljs-comment">// initialized to 9,999</span>
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">public</span> maxGiftedWitches; <span class="hljs-comment">// initialized to 250</span>
<span class="hljs-keyword">uint256</span> <span class="hljs-keyword">private</span> numGiftedWitches;

<span class="hljs-function"><span class="hljs-keyword">modifier</span> <span class="hljs-title">canMintWitches</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> numberOfTokens</span>) </span>{
    <span class="hljs-built_in">require</span>(
        tokenCounter.current() <span class="hljs-operator">+</span> numberOfTokens <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">=</span>
            maxWitches <span class="hljs-operator">-</span> maxGiftedWitches <span class="hljs-operator">+</span> numGiftedWitches,
        <span class="hljs-string">"Not enough witches remaining to mint"</span>
    );
    <span class="hljs-keyword">_</span>;
}
</code></pre><h1 id="h-a-sealed-vault-royalties" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">A Sealed Vault: Royalties</h1><p><em>A big thanks to </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/zachspaded"><em>Zach Burks</em></a><em> (founder of Mintable and one of the authors of EIP-2981) for reporting this bug.</em></p><p>It was important to us to have on-chain royalties, not just use platform-specific, off-chain implementations, which is what led us to <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://eips.ethereum.org/EIPS/eip-2981">EIP-2981</a>. The code to support the standard was simple:</p><pre data-type="codeBlock" text="function royaltyInfo(uint256 tokenId, uint256 salePrice)
    external
    view
    override
    returns (address receiver, uint256 royaltyAmount)
{
    require(_exists(tokenId), &quot;Nonexistent token&quot;);

    return (address(this), SafeMath.div(SafeMath.mul(salePrice, 5), 100));
}
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">royaltyInfo</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> tokenId, <span class="hljs-keyword">uint256</span> salePrice</span>)
    <span class="hljs-title"><span class="hljs-keyword">external</span></span>
    <span class="hljs-title"><span class="hljs-keyword">view</span></span>
    <span class="hljs-title"><span class="hljs-keyword">override</span></span>
    <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">address</span> receiver, <span class="hljs-keyword">uint256</span> royaltyAmount</span>)
</span>{
    <span class="hljs-built_in">require</span>(_exists(tokenId), <span class="hljs-string">"Nonexistent token"</span>);

    <span class="hljs-keyword">return</span> (<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), SafeMath.div(SafeMath.mul(salePrice, <span class="hljs-number">5</span>), <span class="hljs-number">100</span>));
}
</code></pre><p>How does it work? Marketplaces call the function to read data for a <code>receiver</code> address and a <code>royaltyAmount</code>, then send the royalties accordingly. In our case, our receiver is the contract address, and the royalty amount is 5%. Easy enough.</p><p>Except, crucially, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.soliditylang.org/en/latest/contracts.html#receive-ether-function">as of Solidity 0.6.x</a>, smart contracts cannot receive Ether directly without implementing a <code>receive()</code> function, which we didn’t have.</p><p>While our tests checked that the <code>royaltyInfo()</code> function returned the correct values, we never tested <em>receiving</em> royalties to the contract, so transactions from marketplaces that attempted to send us royalties would trigger a <code>revert</code>.</p><p>The remedy for us, in this case, was luckily pretty simple, thanks to the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://royaltyregistry.xyz/">Royalty Registry</a>. We configured an override pointing to a different <code>receiver</code> address (in this case, our multi-sig wallet), so now marketplaces that read from the Royalty Registry will use the override.</p><h2 id="h-fixing-the-implementation" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Fixing the Implementation</h2><p>The easiest way to fix this bug to support EIP-2981 is to simply return the same owner address that receives withdrawals, rather than the contract address. Another option would be to add a <code>royaltyReceiverAddress</code> variable and a setter to make this value configurable.</p><p>If you <em>do</em> want to receive Ether to the contract address, all you need to do is add a <code>receive()</code> function to the contract:</p><pre data-type="codeBlock" text="receive() external payable {}
"><code><span class="hljs-function"><span class="hljs-keyword">receive</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span> </span>{}
</code></pre><h1 id="h-coda" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Coda</h1><p>Learning to develop in Solidity can be a trial by fire—mistakes, minor and major, live eternally ensconced on the blockchain, often at great cost. But the rigid, unforgiving nature of the space can have its own kind of magic, in the creativity born out of constraint and the solidarity forged through shared sleepless nights. To any budding arcanists making their own way through the wilds: I hope that this offering of our learnings further illuminates the path.</p><p>If you stumble upon any other bugs in our contract, please <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/xuannu_eth">DM me</a> on Twitter—we intend to keep this post up-to-date!</p>]]></content:encoded>
            <author>crypto-coven-2@newsletter.paragraph.com (Crypto Coven)</author>
        </item>
        <item>
            <title><![CDATA[The Crypto Coven Gift Guide 🎁]]></title>
            <link>https://paragraph.com/@crypto-coven-2/the-crypto-coven-gift-guide-2</link>
            <guid>B8MHBCAxTVeSPv7WqMP4</guid>
            <pubDate>Mon, 06 Dec 2021 00:48:17 GMT</pubDate>
            <description><![CDATA[Season’s greetings, witches! As the days shorten and winter descends, we celebrate the splendor of the sun’s rebirth and the people, creatures, and dark entities we hold dearest to our hearts. We tip our hats at whatever form your celebration may take—foliage decoration, foraged nuts over ceremonial pyres, a festive photoshoot with your cat—but we laud most of all the exchange of fine gifts. Whether it be a show of devotion or an offering to an angered spirit, we have recommendations for all ...]]></description>
            <content:encoded><![CDATA[<p>Season’s greetings, witches! As the days shorten and winter descends, we celebrate the splendor of the sun’s rebirth and the people, creatures, and dark entities we hold dearest to our hearts. We tip our hats at whatever form your celebration may take—foliage decoration, foraged nuts over ceremonial pyres, a festive photoshoot with your cat—but we laud most of all the exchange of fine gifts. Whether it be a show of devotion or an offering to an angered spirit, we have recommendations for all of the wondrous and woeful magic-makers in your life.</p><h1 id="h-for-enchantresses" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">For Enchantresses</h1><p>ENCHANTRESSES are beings of luxury and glamour—indulge in those ostrich feathers! Those fine faux furs, shimmers, and jewels! Turn to <em>Circe</em> or the Sisters Wyrd to put your ENCHANTRESS in touch with their rich lineage of desire and destruction.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/1b64119a7cb6313293b5de6c795edaf73c0c2983b891aab1d5d5143f50840ca1.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><ol><li><p><strong>Cassandra Dressing Gown in Dark Olive</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://boudoirbydlish.bigcartel.com/product/dark-olive-cassandra-dressing-gown">Boudoir by D’Lish, $500</a>)</p></li><li><p><strong>Serpent Ring</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.childofwild.com/collections/editors-picks/products/serpent-ring">Child of Wild, $170</a>)</p></li><li><p><strong><em>Wyrd Sisters</em> by Terry Prachett</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://bookshop.org/books/wyrd-sisters/9780062225733">Bookshop, $9</a>)</p></li><li><p><strong>Chacha Duskita Turban</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.wolfandbadger.com/uk/chacha-duskita-turban/">Wolf &amp; Badger, £357</a>)</p></li><li><p><strong>Dauphine Eau de Toilette</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.goest.us/dauphine-edt/">Goest, $140</a>)</p></li><li><p><strong>Legendary Bestsellers Kit</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.patmcgrath.com/products/legendary-bestsellers-kit?variant=39599892496453">Pat McGrath, $168</a>)</p></li><li><p><strong>Gold Halo Crown</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.etsy.com/listing/776824844/gold-halo-crown-halo-headpiece-festival">CarbickovaCrowns, $89</a>)</p></li><li><p><strong>Circe by Madeline Miller</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://bookshop.org/books/circe-9781549117640/9780316556323">Bookshop, $16</a>)</p></li><li><p><strong>Pointed Toe Pompom Slippers</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.larosastyle.com/products/pointed-toe-pompom-mule">LarosaStyle, $100</a>)</p></li></ol><h1 id="h-for-hags" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">For Hags</h1><p>The winter is a hard time for your cottagecore cutie. As nature lays down to rest, lift your HAG’s spirits with candlelight, the illustrious mushroom hat, or virtual farming set to the most calming soundtrack the internet can muster.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/8f2ffc5dfbf60bdd569886ffebf01626ec253db970b91bda41c89272c87a5500.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><ol><li><p><strong>Mushroom Hat Patterns</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.etsy.com/listing/881248887/mushroom-hat-all-five-patterns-and">OfTheFforest, $20</a>)</p></li><li><p><strong>Monstera Adansonii “Archipelago” Keychain</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://hemleva.com/collections/keychains/products/monstera-adansonii-archipelago-keychain">Hemleva, $18</a>)</p></li><li><p><strong>Queen of the Underworld Art Nouveau Crown</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.etsy.com/listing/604304163/queen-of-the-underworld-art-nouveau?ref=shop_home_feat_1">idolatre, $393</a>)</p></li><li><p><strong>Plant Magic E-Book</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.thewondersmith.com/ebooks/plantmagic">The Wondersmith, $9</a>)</p></li><li><p><strong>Steel Watering Can</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://store.moma.org/gifts/under-25/burgon-ball-steel-watering-can/14015-153965.html?cgid=gifts-under-25#start=1">MoMA Design Store, $28</a>)</p></li><li><p><strong>Fox Druid Enamel Pin</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.gallerynucleus.com/detail/30507/">Gallery Nucleus, $12</a>)</p></li><li><p><strong>The Green Gables Cottage Puff Dress</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://selkiecollection.com/collections/dresses-jumpsuits/products/the-green-gables-cottage-puff-dress">Selkie, $295</a>)</p></li><li><p><strong>Take Off Temporary Tattoo</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://inkbox.com/products/take-off">Inkbox, $12</a>)</p></li><li><p><strong>Stardew Valley</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://store.steampowered.com/app/413150/Stardew_Valley/">Steam, $15</a>)</p></li><li><p><strong>Avocado Vase</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://huckberry.com/store/ilex-studios/category/p/62177-avocado-vase">Huckberry, $48</a>)</p></li><li><p><strong>Winnie Floral Taper Candle Set</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.urbanoutfitters.com/shop/winnie-floral-taper-candle-set?inventoryCountry=US&amp;color=095&amp;size=ONE%20SIZE&amp;type=REGULAR&amp;quantity=1">Urban Outfitters, $18</a>)</p></li><li><p><strong>Teak Bookends</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.cb2.com/teak-bookends-set-of-2/s338498">CB2, $40</a>)</p></li></ol><h1 id="h-for-mages" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">For Mages</h1><p>If you’ve noticed your MAGE lusting after their WITCH’s octagonal glasses, now is your time. Gift your favorite bookworm a new read, or light up their room with the arcane summoning circle of their dreams and dissertations.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/4842f15c783dd34a19aa8426ec89b69ab035739ac1eb657c4d8db697b6a78ae1.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><ol><li><p><strong>Baba Is You</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://store.steampowered.com/app/736260/Baba_Is_You/">Steam, $15</a>)</p></li><li><p><strong>Agate Stone Bookends</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.westelm.com/products/agate-bookend-d1229/?pkey=s~bookends~119">West Elm, $52</a>)</p></li><li><p><strong>Duncan Octagonal Glasses</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://tijneyewear.com/product-duncan-gold-colour-00?nopregsrc=YM-S-TIJN&amp;nopcampaign=TIJN-006&amp;gclid=Cj0KCQiA-qGNBhD3ARIsAO_o7ylN_4qYzMnJi0q4rrbcBvs8QHjtmTU15SbRvyHx69zi5mX2dYDQNI8aAo5eEALw_wcB">Tijn, $28</a>)</p></li><li><p><strong>Zodiac Ring</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.catbirdnyc.com/zodiac-ring.html">Catbird, $124</a>)</p></li><li><p><strong>AdaBox</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.adafruit.com/adabox/">Adafruit, $60</a>)</p></li><li><p><strong>HAY Neon LED Tube Light</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://store.moma.org/for-the-home/featured/hay/hay-neon-led-tube-light/12992-152730.html?flow_enabled=false&amp;gclid=Cj0KCQiAtJeNBhCVARIsANJUJ2GNl44qEH7VO1NSYroDB1rbEHZFgXndgjDzmOI7LlTZHkS3mITpUu0aAn1aEALw_wcB">MoMA Design Store, $75</a>)</p></li><li><p><strong><em>Equal Rites</em> by Terry Pratchett</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://bookshop.org/books/equal-rites/9780062225696">Bookshop, $9</a>)</p></li><li><p><strong><em>The Three-Body Problem</em> by Liu Cixin</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://bookshop.org/books/the-three-body-problem/9780765382030">Bookshop, $17</a>)</p></li><li><p><strong><em>House of Leaves</em> by Mark Z. Danielewski</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://bookshop.org/books/house-of-leaves/9780375703768">Bookshop, $22</a>)</p></li><li><p><strong><em>The Hitchhiker’s Guide to the Galaxy</em> by Douglas Adams</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://bookshop.org/books/the-hitchhiker-s-guide-to-the-galaxy-9780345391803/9780345391803">Bookshop, $7</a>)</p></li><li><p><strong>Prysm Halo Desk Lamp</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.etsy.com/listing/954843731/prysm-halo-color-changing-desk-lamp-led?ga_order=most_relevant&amp;ga_search_type=all&amp;ga_view_type=gallery&amp;ga_search_query=neon+halo&amp;ref=sr_gallery-1-27&amp;frs=1">WiseHomeProducts, $54</a>)</p></li></ol><h1 id="h-for-necromancers" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">For Necromancers</h1><p>Necromancy requires polish, patience, and a fantastic hat. Check out some of the IRL inspirations for the necrotic headgear and jewelry of the wilds. Garth Nix’s <em>Sabriel</em> is a team favorite, and who could say no to a bewitching bat from Jellycat…</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/8418ac5666d52e21850e989d3349b2d9e4392a40509a7799f7972de6a4a6e13e.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><ol><li><p><strong>Wing Earrings</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://stonehartjewelry.com/collections/best-selling/products/wing-earrings?variant=32872272396372">Stonehart, $174</a>)</p></li><li><p><strong><em>Sabriel</em> by Garth Nix</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://bookshop.org/books/sabriel/9780062315557">Bookshop, $12</a>)</p></li><li><p><strong>Theda Headband</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.hoodlondon.com/collections/moon/products/theda-br">Hood, £395</a>)</p></li><li><p><strong>Drucilla Choker</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://creepyyeha.storenvy.com/collections/1849264-pearls/products/31041679-drucilla-choker">Creepyyeha, $165</a>)</p></li><li><p><strong>Scarab Amulet with Pendant</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.etsy.com/listing/898740422/scarab-amulet-the-eternal-return-vintage">ModEvil, $136</a>)</p></li><li><p><strong>Leather Gloves with Nail Embroidery</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://shop.vivetta.com/en/scarves-gloves/120-572-leather-glove-with-embroidered-nails.html#/11-color-black/74-taglia_guanti-6">Vivetta, €220</a>)</p></li><li><p><strong>Bewitching Bat Plushie</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.jellycat.com/us/bewitching-bat-be3bat/">Jellycat, $18</a>)</p></li><li><p><strong>Stargaze Headband</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.hoodlondon.com/collections/moon/products/stargaze-br">Hood, £190</a>)</p></li><li><p><strong>Alexander McQueen Pave Skull Earrings</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.farfetch.com/shopping/women/alexander-mcqueen-pave-skull-earrings-item-17297050.aspx">FARFETCH, $380</a>)</p></li><li><p><strong>Framed Sunset Moth</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://paxtongate.com/collections/entomology/products/urania-ripheus">Paxton Gate, $45</a>)</p></li></ol><h1 id="h-for-occultists" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">For Occultists</h1><p>We understand your OCCULTIST is a wildcard. Hard to shop for, even harder to track down as they stalk prey in the moonlight. Gift your favorite monstrosity some temporary ink, chibi totems, fire kits, or maybe even a signed-by-the-artist <em>Animorphs</em> poster. Chaos asked and delivered.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/f06a8c5987b581fd8cf342d92bf828732896e4adac214a2b8a2df1740a5bf158.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><ol><li><p><strong>Worry Dolls</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://sofiazakia.com/collections/worry-dolls">Sofia Zakia, starting at $168</a>)</p></li><li><p><strong>Demon Horns</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.etsy.com/listing/1042041072/demon-horns-black-headband-20cm-queen-of">HornMaskShop, $49</a>)</p></li><li><p><strong>Cthulhu Crocheted Plush</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.etsy.com/listing/876753997/cthulhu-crocheted-plush-lovecraft-crypid">SweetPeachesInk, $23</a>)</p></li><li><p><strong>Ranger</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.solostove.com/solo-stove-ranger/">Solo Stove, $180</a>)</p></li><li><p><strong>Silver Claw Ring</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.etsy.com/listing/628194173/silver-claw-rings-sterling-silver-gothic?ga_order=most_relevant&amp;ga_search_type=all&amp;ga_view_type=gallery&amp;ga_search_query=silver+claws&amp;ref=sr_gallery-1-47&amp;organic_search_click=1&amp;frs=1">SterlingMalee, $39</a>)</p></li><li><p><strong><em>Animorphs</em> Poster</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.etsy.com/shop/AnimorphsArtStore">AnimorphsArtStore, $50</a>)</p></li><li><p><strong>Talon Bone/Horn Spike Ear Cuff</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://houseofmalakai.com/collections/all-products/products/talon-spike-ear-cuffs">House of Malakai, $602</a>)</p></li><li><p><strong>Divine Thorns Claw Rings</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.etsy.com/listing/663416278/finger-armor-divine-thorns-claw-rings?ga_order=most_relevant&amp;ga_search_type=all&amp;ga_view_type=gallery&amp;ga_search_query=silver+claws&amp;ref=sr_gallery-1-24&amp;organic_search_click=1&amp;frs=1">MyrilJewels, $81</a>)</p></li><li><p><strong>Hand-Shaped Nylon Padded Stole</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://shop.vivetta.com/en/scarves-gloves/55-130-stola-nylon-imbottita-a-forma-di-mano.html#/10-color-red/57-size_letters-one_size">Vivetta, €290</a>)</p></li><li><p><strong>Damascus Hunting Knife</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.etsy.com/listing/941269369/damascus-hunting-knife-custom-damascus">SksenterprisesArt, $95</a>)</p></li><li><p><strong>Rainbow Flames Pine Cone Firestarters</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.etsy.com/listing/947918452/rainbow-flames-fire-color-changing-pine">WeMadeGardenCrafts, $20</a>)</p></li><li><p><strong>Dark Pathways Temporary Tattoo</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://inkbox.com/products/dark-pathways">Inkbox, $12</a>)</p></li></ol><h1 id="h-for-seers" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">For Seers</h1><p>Having so many all-seeing eyes really tires a witch out, we’ll have you know. Gift your SEER a silk eye mask or calming incense to help their multiple consciousnesses finally catch some rest. If their mind is in the future, we’ve never known a SEER to turn away a new tarot deck or set of oracle cards.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/072978a773bc2bbe65664896301371b6dfdb27c9913f1c3726aa1041b9e78a2e.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><ol><li><p><strong>Tarot Card Necklace</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://sofiazakia.com/collections/tarot-cards">Sofia Zakia, starting at $595</a>)</p></li><li><p><strong>MTHRSHP MEGA: Celestial Odyssey</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.patmcgrath.com/products/mthrshp-mega-celestial-odyssey?variant=39522029207621">Pat McGrath, $78</a>)</p></li><li><p><strong>Cat Tarot</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.urbanoutfitters.com/shop/cat-tarot-78-cards-and-guidebook-by-megan-lynn-kott">Urban Outfitters, $20</a>)</p></li><li><p><strong><em>Lirael</em> by Garth Nix</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://bookshop.org/books/lirael-daughter-of-the-clayr/9780060005429">Bookshop, $8</a>)</p></li><li><p><strong>Sleeping Beauty Opal Stud</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.catbirdnyc.com/sleeping-beauty-opal-stud.html">Catbird, $112</a>)</p></li><li><p><strong>Elucidate Temporary Tattoo</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://inkbox.com/products/elucidate">Inkbox, $9</a>)</p></li><li><p><strong>Black Sleep Mask</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.slip.com/collections/25-off-sale/products/sleep-mask-black">Slip, $50</a>)</p></li><li><p><strong>Moonology Oracle Cards</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.hayhouse.com/moonology-oracle-cards-deck">Hay House, $12</a>)</p></li><li><p><strong>Le Patch Incense Papers</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.ponsont.com/shop/le-patch">Ponsont, $24</a>)</p></li></ol><h1 id="h-for-every-witch" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">For Every Witch</h1><p>Any witch could benefit from a good read, so here are some of our favorites. These narratives have helped shape the wilds, even as they grow ever stranger. We would also be amiss to not recommend a solid hardware wallet—we don’t want some ruffian absconding with your WITCHES!</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/d7d8e9143ace3324bc6dd778a8f0cdb92318aab0b36779d9a7d15cf5b6136bfd.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><ol><li><p><strong><em>The Night Circus</em> by Erin Morgenstern</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://bookshop.org/books/the-night-circus-9781616577889/9780307744432">Bookshop, $16</a>)</p></li><li><p><strong><em>The Poppy War</em> by R.F. Kuang</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://bookshop.org/books/the-poppy-war/9780062662583">Bookshop, $15</a>)</p></li><li><p><strong><em>Foundryside</em> by Robert Jackson Bennett</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://bookshop.org/books/foundryside/9780525573845">Bookshop, $16</a>)</p></li><li><p><strong><em>The Fifth Season</em> by N.K. Jemisin</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://bookshop.org/books/the-fifth-season/9780316229296">Bookshop, $17</a>)</p></li><li><p><strong><em>Exhalation</em> by Ted Chiang</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://bookshop.org/books/exhalation/9781101972083">Bookshop, $16</a>)</p></li><li><p><strong><em>Invisible Cities</em> by Italo Calvino</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://bookshop.org/books/invisible-cities-9798200060641/9780156453806">Bookshop, $15</a>)</p></li><li><p><strong><em>The Grace of Kings</em> by Ken Liu</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://bookshop.org/books/the-grace-of-kings-1/9781481424288">Bookshop, $17</a>)</p></li><li><p><strong><em>Queen of Camelot</em> by Nancy McKenzie</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://bookshop.org/books/queen-of-camelot/9780345445872">Bookshop, $18)</a></p></li><li><p><strong>Ledger Nano S</strong> (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://shop.ledger.com/products/ledger-nano-s/flamingo-pink">Ledger, $59</a>)</p></li><li><p><strong>A WITCH</strong> ✨ (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://cryptocoven.xyz/">Crypto Coven, 0.07 ETH</a>)</p></li></ol>]]></content:encoded>
            <author>crypto-coven-2@newsletter.paragraph.com (Crypto Coven)</author>
        </item>
        <item>
            <title><![CDATA[AI-Aided Articulations: What Makes a WITCH?]]></title>
            <link>https://paragraph.com/@crypto-coven-2/ai-aided-articulations-what-makes-a-witch</link>
            <guid>2HuXDWU0SrF0o49YSzPk</guid>
            <pubDate>Thu, 18 Nov 2021 19:39:32 GMT</pubDate>
            <description><![CDATA[If we haven&apos;t crossed paths in the weird wilds before, I&apos;m Keridwen, the WITCH of WIT and resident wordsmith at Crypto Coven. 🖋️✨ As many of you know, our WITCH NFTs come with not only gorgeously rendered art, but also a name, an archetype, astrological signs, stats within our magic system, and an ARTICULATION. Here&apos;s me:https://www.cryptocoven.xyz/witches/5And a few lovely examples:https://www.cryptocoven.xyz/witches/64https://www.cryptocoven.xyz/witches/214https://www.crypto...]]></description>
            <content:encoded><![CDATA[<p>If we haven&apos;t crossed paths in the weird wilds before, I&apos;m Keridwen, the WITCH of WIT and resident wordsmith at Crypto Coven. 🖋️✨</p><p>As many of you know, our WITCH NFTs come with not only gorgeously rendered art, but also a name, an archetype, astrological signs, stats within our magic system, and an ARTICULATION.</p><p>Here&apos;s me:</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/7a56f22eed7f660adbd994b1f0c1f67a377088d7ce4ba821684705c0be811a26.png" alt="https://www.cryptocoven.xyz/witches/5" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">https://www.cryptocoven.xyz/witches/5</figcaption></figure><p>And a few lovely examples:</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/254c9faeec12d0400c2fa493dd30e5a6e804daa8b8bcc91747c203c75df20613.png" alt="https://www.cryptocoven.xyz/witches/64" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">https://www.cryptocoven.xyz/witches/64</figcaption></figure><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/fb63c297773b4cf9b392200c39e22b832a2567e6e7743297bc541c660c90dfa7.png" alt="https://www.cryptocoven.xyz/witches/214" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">https://www.cryptocoven.xyz/witches/214</figcaption></figure><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/393427b344d7b44c6141bae93d16a2159ad6ed168681b51fbe8fec10ef2ce1df.png" alt="https://www.cryptocoven.xyz/witches/425" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">https://www.cryptocoven.xyz/witches/425</figcaption></figure><p>When we first imagined this project, we knew we wanted the WITCHES to feel folded into the world we were building from the moment of their inception. Not just empty tokens who would one day populate the universe we create, but individuals with a firm place in it: hags with pumpkin cottages, mages with dissertations on star alignment, necromancers with brooches haunted by lost souls. We wanted the WITCHES to feel alive and bolstered by narrative—something new and exciting in this space.</p><p>I started drafting ARTICULATIONS, or WITCH descriptions, with scalability in mind. Our goal was 9,999 WITCHES, more than could be hand-composed without some sort of magic-making. I decided to break down the descriptions into five sentences each: an intro, a hobby, a magic origin, an archetype-specific trait, and an exclamation. I wanted a structure that would give each WITCH an identity made particular, idiosyncratic, and strange, that still unified the archetypes, created an overall tone for the voice of the project, and delivered our ethos of individuality.</p><p>To generate the sheer number of sentences required for that level of uniqueness, I used OpenAI&apos;s GPT-3 Playground as both a jumping off point for new ideas and a straight-up generator to churn outputs using my writing as a seed when it was up to the task.</p><p>Our initial acquaintance was... underwhelming. Here, my input is in bold and the AI output is in standard text:</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/8d08d87d183f6fa389a45e2aa4d384967d112c3a98800f2568a65132f5adc2ba.png" alt="A first attempt at generating hobby sentences." blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">A first attempt at generating hobby sentences.</figcaption></figure><p>Although the rhythms were excellent (I believe I used an adaptation of &quot;You let your hair grow out. You let it fall.&quot;), the specificity wasn&apos;t quite where I wanted it.</p><p>More rigid structures, as shown below, yielded better results!</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/97ec0ccf4dee58638c57cb01deb85382895941ee1284a8f085d8d90c780a67a2.png" alt="GPT-3 got creative with sources of magic." blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">GPT-3 got creative with sources of magic.</figcaption></figure><p>And there were occasional gems like, &quot;Your magic spawns from... a good book with ten very attractive people on the cover,&quot; which 100% made the final cut. Polyamorous harem romance is, after all, the PREMIER source of magic.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/611cfbb1809707ceb193c16b5851db9332c461c789a0e3f733e0890053737c63.png" alt="I didn&apos;t end up using &quot;a balloon animal made out of fire and an old movie theater ticket,&quot; but, God, I love that one." blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">I didn&apos;t end up using &quot;a balloon animal made out of fire and an old movie theater ticket,&quot; but, God, I love that one.</figcaption></figure><p>Here&apos;s another AI replication of a firm structure that yielded fantastic results:</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a4c18ea4410986b120ca9bca6aa3cbfa23cf3fe26bef3f90d5c9e533b14b2162.png" alt="The WITCHES just love to dance, okay?" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">The WITCHES just love to dance, okay?</figcaption></figure><p>For something shorter and more abstract like the exclamations, I let it freestyle much more:</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/16ff3db1cdb19977bb3f8cf2e3b1b36ac35a4eeaa92b623d6a59e52c24873330.png" alt="What it was going to say with &quot;MAKE YOURSELF BE F-&quot; haunts me to this day." blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">What it was going to say with &quot;MAKE YOURSELF BE F-&quot; haunts me to this day.</figcaption></figure><p>Sometimes the AI just wanted to write straight-up fiction with dialogue, and I hated to crush its dreams, but that was not our intended vibe.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/e85c1f257d6dbed3aaf7d552bb89562a167d19a80cdf6ebf8fc17a16cc1b67fe.png" alt="GPT-3 had a lot more to say about &quot;the device.&quot;" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">GPT-3 had a lot more to say about &quot;the device.&quot;</figcaption></figure><p>I also found that our energy often struck fear into the heart of this machine.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/c217150f897b81ca9bba425da39708dc73a3cc3c2bacdc32e72780adf2b58307.png" alt="An OCCULTIST simply can&apos;t be tamed." blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">An OCCULTIST simply can&apos;t be tamed.</figcaption></figure><p>A large majority of my initial completions were warned to be &quot;unsafe,&quot; though the outputs themselves were fantastic and perfect for the OCCULTIST archetype with whom they made their home.</p><p>At the end of our labors, we produced hundreds of sentences to be mixed and matched across the metaverse, to combine and collide into thousands of consciousnesses. Aletheia, our WITCH of WISDOM, was the first to say that OpenAI had become something of a digital familiar—a friend and confidante that lent a helping artificial consciousness to the effort of bringing WITCHES to life.</p><p>But we were not yet done! One of our goals for the project was to incorporate the creativity and enthusiasm of our early supporters. We collected community moodboards and ARTICULATIONS with the goal of selecting those we loved most to fold into the makings of the weird wilds. The results were, frankly, fantastic:</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/0a7a70f6fc27dde9832bfadb1bac7b05c4d4655c43aa0e4d735e848ac056ea55.png" alt="If you spot one of these ARTICULATIONS in a WITCH, it came from the community!" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">If you spot one of these ARTICULATIONS in a WITCH, it came from the community!</figcaption></figure><p>So now, as our community sale churns on, the WITCHES are articulated not only by my initial writing, but also the strange magick of artificial intelligence and the imagination of some of the project&apos;s dearest supporters. Paired with the delightful names generated by Aradia, we&apos;ve summoned some truly wondrous and woeful WITCHES. 🖤</p><p>Admire them all:</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.cryptocoven.xyz/witches">https://www.cryptocoven.xyz/witches</a></p><hr><h3 id="h-be-well-be-wild-or-else-begone" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">BE WELL; BE WILD; OR ELSE, BEGONE!</h3>]]></content:encoded>
            <author>crypto-coven-2@newsletter.paragraph.com (Crypto Coven)</author>
        </item>
    </channel>
</rss>