<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by xtremetom on Medium]]></title>
        <description><![CDATA[Stories by xtremetom on Medium]]></description>
        <link>https://medium.com/@xtremetom?source=rss-f8fef5ff64a6------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*aHxG4CW3PPx3KNv3YzPedg.png</url>
            <title>Stories by xtremetom on Medium</title>
            <link>https://medium.com/@xtremetom?source=rss-f8fef5ff64a6------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Thu, 05 Nov 2026 04:07:05 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@xtremetom/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[How I put an NFT collection of 20k animated GIFs on-chain and saved 9 Eth on gas]]></title>
            <link>https://medium.com/coinmonks/how-i-put-an-nft-collection-of-20k-animated-gifs-on-chain-and-saved-9-eth-on-gas-512dac931ac3?source=rss-f8fef5ff64a6------2</link>
            <guid isPermaLink="false">https://medium.com/p/512dac931ac3</guid>
            <category><![CDATA[data-compression]]></category>
            <category><![CDATA[solidity]]></category>
            <category><![CDATA[animated]]></category>
            <category><![CDATA[on-chain-data]]></category>
            <dc:creator><![CDATA[xtremetom]]></dc:creator>
            <pubDate>Mon, 31 Jul 2023 22:05:00 GMT</pubDate>
            <atom:updated>2023-08-01T13:26:47.381Z</atom:updated>
            <content:encoded><![CDATA[<p>Baby Pepes move on-chain</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GR6HZ-dx3_YW9OxiKf_Syw.png" /></figure><p>The first thing that comes to mind when reading the title is, why bother?</p><p>The honest answer is that it seemed like a fun challenge and it was. In the end, I came up with a unique data storage method that saved me more than 9 Eth in transaction costs by reducing the total data by ~90%.</p><p>Before we jump into the how, let&#39;s explore what Baby Pepes are and why it was a challenge to put the whole collection on-chain at a reasonable cost.</p><blockquote>Also, fair warning, things are gonna get pretty geeky.</blockquote><h3>What are Baby Pepes?</h3><p>Baby Pepes are a generative NFT collection of 20k animated pixel art pieces, inspired by the infamous <a href="https://en.wikipedia.org/wiki/Pepe_the_Frog">Pepe created by Matt Furie</a>. We wanted to build a derivative to celebrate the randomness of Pepe meme culture and inject a little fun back into NFTs. You can view the collection on Opensea:</p><p>🐸 <a href="https://opensea.io/collection/babypepes">https://opensea.io/collection/babypepes</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/288/1*7u20lvDoDagRpipQ4j-MJQ.gif" /><figcaption>Just chillin’</figcaption></figure><h3>Why is it a challenge to put them on-chain?</h3><p>Each token is constructed of four animated layers:</p><ul><li>Background (yep, even the background)</li><li>Body</li><li>Hat</li><li>Face</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KBvyeNIII2MmTbEi2OjFMg.gif" /><figcaption>Animate layers of a Baby Pepe</figcaption></figure><p>Each layer has a trait and for each trait, there are multiple possible GIFs that could be used; the background layer has 6 possible animated traits, the body layer has 93, the hat layer has 99 and the face layer has 67. Making a total of 235 GIFs.</p><p>For Baby Pepes to be fully on-chain and work with zero centralized dependencies, I would need to store all those GIFs on-chain.</p><blockquote>So, just do that, right?</blockquote><p>Sadly, storing data on-chain is really expensive, like stupid expensive.</p><p>Every day NFT flippers and collectors are complaining about gas prices and they, for the most part, are initiating cheap transfers. We would be writing large blocks of data to the blockchain.</p><p>To give you some idea of just how much data, below is what the above orange animated background looks like in data form (base64 encoded):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4rPlfG-Lz1h_OE1iN3s5VA.png" /></figure><p>And that is just one of 6 backgrounds. We would also need to write all the other animated layers to the blockchain.</p><p>In that form, all that data writing would easily cost more than 10 Eth and that’s if gas is 15–17 gwei.</p><p>The next problem we face is storing token trait information for all 20k pieces. Each Baby Pepe has four traits that are described with a trait id. Think of it as the genetics describing what each Baby Pepe looks like:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/608/1*OmhXfPsx6JKmm9c3AEr-Ig.png" /></figure><p>It looks simple enough, get the trait ids and string them together to form the longer number. And honestly, it is that simple. The problem arises when you have 20k of them and all that data needs to be stored on-chain.</p><p>Finally, all the data needs to be structured in a way to help reduce the size and cost.</p><p><strong>To recap, the issues are:</strong></p><ol><li>Writing data on-chain is expensive</li><li>GIFs are large data structures</li><li>The gene pool is a lot of data</li><li>Finalizing data, optimizing and structuring for retrieval</li></ol><p>Let&#39;s explore solving those issues</p><h3>Problem 1: Writing data on-chain is expensive</h3><p>There is nothing I can do to change the cost of writing data to the blockchain short of joining the Ethereum team and somehow creating a cheaper method. And that aint gunna happen cos I suck at Go, Im not that good at Solidity and Vitalik is an uber GOAT.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1003/0*ISkPM-WwftyZa2RJ.jpg" /><figcaption>Sorry, distracting image :p</figcaption></figure><p>That leaves me with two options:</p><ol><li>Use a cheaper method of writing data on-chain</li><li>Write as little as possible to save on costs</li></ol><h4>Finding a Cheap way to write data on-chain</h4><p>Writing directly to storage for the amount of data I need to write is not only prohibitively expensive, but it also adds an additional layer of complexity in terms of utilizing it. Luckily SSTORE2 can be utilized to efficiently read and write data as byte code contracts, which solves half my problem as explained with the following nerdery;</p><h4>Reading with SSTORE2</h4><p>source: <a href="https://github.com/0xsequence/sstore2">https://github.com/0xsequence/sstore2</a></p><p>Reading data is a lot cheaper compared to native SLOAD operations (native solidity storage).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/643/1*hfLLFhW7ek__vi_NymisCQ.png" /></figure><h4>Writing with SSTORE2</h4><p>source: <a href="https://github.com/0xsequence/sstore2">https://github.com/0xsequence/sstore2</a></p><p>Writing data is also cheaper than native SSTORE operations (native solidity storage), but gains become apparent after higher data sizes.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/668/1*NyKre8LyOst3tr9hpKgu_Q.png" /></figure><p>So SSTORE2 seems like a great fit. However, I would ideally like to be able to read my data bundle as a single block of data and not have to pick at byte contracts — cos I&#39;m lazy.</p><h3>How to read and use my data bundles</h3><p>For large data sets like mine, I will have to break the data into 24kb chunks and write each separate chunk as a byte contract, and when I want to use the data I need to pull all the data from all the chunks and concatenate it back into a single data set.</p><p>That all sounds horribly complicated. However, whilst I was experimenting with on-chain storage for the creation of <a href="https://opensea.io/collection/crypto-coaster-3d">CryptoCoasters</a> I was inspired by dhof’s rose:</p><h3>dom on Twitter: &quot;ROSESprocedurally generated in 3d + served fully on-chainsupply 1024 (32 reserved)NOT CURRENTLY MINTABLE. minting - like rendering - is modular &amp; upgradable. will share details once determinedbrowse the first 32 https://t.co/cEmisHnJ3dtechnical details coming shortly pic.twitter.com/x83melUxAJ / Twitter&quot;</h3><p>ROSESprocedurally generated in 3d + served fully on-chainsupply 1024 (32 reserved)NOT CURRENTLY MINTABLE. minting - like rendering - is modular &amp; upgradable. will share details once determinedbrowse the first 32 https://t.co/cEmisHnJ3dtechnical details coming shortly pic.twitter.com/x83melUxAJ</p><p>It helped me to fully understand how to utilize SSTORE2 for writing and retrieving large amounts of data on the blockchain. After a lot of experimenting, I came up with this code to concatenate data chunks into their original form:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b8d49ae8716179ee762d372779406c97/href">https://medium.com/media/b8d49ae8716179ee762d372779406c97/href</a></iframe><p>That code has since been used in EthFS (source: <a href="https://github.com/holic/ethfs/blob/main/packages/contracts/src/File.sol">https://github.com/holic/ethfs/blob/main/packages/contracts/src/File.sol</a>) and Scripty of which I am a co-author. Both libraries aid in the writing and reading of large data sets.</p><p>For this project, I opted to use Scripty storage. Mainly because we just finished creating version 2 and I wanted to demonstrate just one of its many uses:</p><p><a href="https://github.com/intartnft/scripty.sol">GitHub - intartnft/scripty.sol: A gas-efficient HTML builder that can combine multiple JS and a data storage solution that allows on-chain composable generative art.</a></p><p>Ok, so all that geeky stuff solves my issues with how to write data and utilize it on-chain. Thats great! Now to figure out how to reduce the amount of data I needed to write.</p><h3>Problem 2: Handling GIFs on-chain</h3><p>The section header is a little misleading, but it does illustrate what I was thinking I would have to do. In reality, I decided that GIFs were just too cumbersome to put on-chain for this particular project. I needed to find a new way to build my animated tokens.</p><h3>Bastardizing GIFs</h3><p>HAHA, ok this title is more on the money.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/288/1*gJLJv-MQ3HaBzfrpQikCXg.gif" /><figcaption>ROCK ON!!!</figcaption></figure><p>I have an extensive background in building and optimizing websites for performance and SEO, as well as a background in game development. Both of which make use of sprite sheets.</p><p>In gaming, character sprite sheets are used to render animations by displaying each separate frame in sequence. The sprite sheet simply houses the images. GIFs hold the same information but also a lot of additional data like the duration of each frame and a bunch of other stuff we don’t really care about.</p><p>This means if I could find a way to use sprite sheets instead of GIFs I could reduce the amount of data I have to store — that’s a huge win. The only problem was figuring out a nice method.</p><p><strong>At this point, my goals were:</strong></p><ol><li>Find a way to display the final tokens that could be displayed via the “image” attribute in the metadata. This is crucial because I want the token to be animated in the Opensea previews as well as the larger views.</li><li>Use sprite sheets</li><li>Reduce data to be stored</li></ol><h4>Displaying as an image</h4><p>After a lot of playing around, I converted a few traits into sprite sheets and threw them into SVGs. You can see an early example here (<a href="https://codepen.io/xtremetom/pen/MWzZjWj">https://codepen.io/xtremetom/pen/MWzZjWj</a>):</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcodepen.io%2Fxtremetom%2Fembed%2Fpreview%2FMWzZjWj%3Fdefault-tabs%3Dhtml%252Cresult%26height%3D600%26host%3Dhttps%253A%252F%252Fcodepen.io%26slug-hash%3DMWzZjWj&amp;display_name=CodePen&amp;url=https%3A%2F%2Fcodepen.io%2Fxtremetom%2Fpen%2FMWzZjWj&amp;image=https%3A%2F%2Fshots.codepen.io%2Fusername%2Fpen%2FMWzZjWj-512.jpg%3Fversion%3D1690801819&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=codepen" width="800" height="600" frameborder="0" scrolling="no"><a href="https://medium.com/media/7146350a1654acd1e6c1aa528c797fcd/href">https://medium.com/media/7146350a1654acd1e6c1aa528c797fcd/href</a></iframe><p>That early example doesn&#39;t work on a lot of browsers or mobile. It took a lot of trial and error to perfect the structure for cross-browser and device displays, which resulted in this (<a href="https://codepen.io/xtremetom/pen/poQqEvy">https://codepen.io/xtremetom/pen/poQqEvy</a>):</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcodepen.io%2Fxtremetom%2Fembed%2Fpreview%2FpoQqEvy%3Fdefault-tabs%3Dhtml%252Cresult%26height%3D600%26host%3Dhttps%253A%252F%252Fcodepen.io%26slug-hash%3DpoQqEvy&amp;display_name=CodePen&amp;url=https%3A%2F%2Fcodepen.io%2Fxtremetom%2Fpen%2FpoQqEvy&amp;image=https%3A%2F%2Fshots.codepen.io%2Fusername%2Fpen%2FpoQqEvy-512.jpg%3Fversion%3D1690801936&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=codepen" width="800" height="600" frameborder="0" scrolling="no"><a href="https://medium.com/media/286c7e47e64e2b11d20420e05d31de3c/href">https://medium.com/media/286c7e47e64e2b11d20420e05d31de3c/href</a></iframe><p>The way it works is pretty simple. The token is made of four sprite sheets all layered on top of each other like this on the z-axis:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*o6hZIYFPVHMo2NQW" /><figcaption>example of a sprite sheet</figcaption></figure><p>The viewer can only see a small area (red) and everything outside of that viewing area is hidden:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*2-rj40NNv8IxahIX" /><figcaption>(expanded, stacked view)</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*ba_ySYK8a5zuYoIF" /><figcaption>(actual view)</figcaption></figure><p>Each frame is presented for a fixed period of time and then the whole sprite sheet is shifted to the left so the next frame of each layer comes into view.</p><p>Once the cycle is complete, after 24 frames over 2.4 seconds, the sprite sheets return to their starting position and the cycle repeats. This creates the animation loop cycle.</p><p>This was a great step in the right direction, but I felt the sprite sheets, although they were considerably smaller in data size, were still too big as you can see in this comparison between the GIF and the sprite sheet.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/288/1*9PMddvRk9ybqr7-iBwiD8g.gif" /><figcaption>Body Layer: Police Trait as GIF</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/608/1*qMwUh1sLA2BrV-j0k6_L9w.png" /><figcaption>Details of GIF</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jfkioHSckJrfZ9G3f0kGIQ.png" /><figcaption>Body Layer: Police Trait as Sprite Sheet</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/604/1*AWK60bcu5Tco0M_XWh4LjQ.png" /><figcaption>Details of the sprite sheet</figcaption></figure><p>After a little playing around I was able to find a nice way to optimize the sprite sheets using <a href="https://pngquant.org/">https://pngquant.org/</a> and this was the final result:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*oxVzU2lJ7a797XZp-OCd2g.png" /><figcaption>Body Layer: Police Trait as Sprite Sheet — optimized</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/611/1*psOIiljZdcsytS5GKXjGnQ.png" /></figure><p>For those that aren&#39;t keeping track, that&#39;s a 10000000% reduction in data size — almost, it is actually <strong>~98%</strong>, which is insane :P</p><p>With that adjustment, I created a simple Python script to iterate over all the GIFs and convert them into optimized sprite sheets.</p><h3>Problem 3: The gene pool is a lot of data</h3><p>Sadly, there is not much I can do to reduce the size of this data structure. Each Baby Pepe has four traits and I simply need to store that information for 20k tokens.</p><p>If each genome is 4 bytes long, that means I can store four numbers up to 255. I know the highest trait Id I currently have is 98, so 4 bytes is perfect and leaves potential room for growth.</p><p>All I have to do is cram all the genomes together into one very long data set and boom, we have the final gene pool. Additionally, because we know each genome is 4 bytes long it&#39;s very easy to extract data for a specific token Id. We simply slice the gene pool using the (token Id -1) x 4 as the offset.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/491/1*lN-s6eMKG8p26V44dD4_3A.png" /><figcaption>Slicing the gene pool</figcaption></figure><p>Perfect. At this point, I solved my first three issues and had a way to display the animations and methods for reducing the size of the image data. Next, I had to figure out what other data was needed and how I should structure it and minimize it.</p><h3>Problem 4: Finalizing data, optimizing and structuring for retrieval</h3><p>Ok, time to get geeky again — time to introduce Professor Pepe:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/288/1*p9DvGd_rmMu82CTu6r2Y5A.gif" /><figcaption>Professor Pepe, is here to help with his Gigabrain</figcaption></figure><p>Some of you may already see a big issue with my approach.</p><blockquote>How exactly are you storing the sprite sheets on-chain in a way that allows you to retrieve a specific trait?</blockquote><p>For anyone wondering why this is an issue, let&#39;s break down the problem.</p><p>Imagine I am trying to save two separate sprite sheets on-chain. We already know we aren&#39;t saving the actual image as you might when you drag and drop something with an upload system on a website. Instead, we have to manage all the data ourselves. For the sake of this explanation we are going base64 encode the sprite sheets. The result for the two images will look something like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2kmc_BlutXqcxjv4_Sm8iw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MQqBP106BLB7_3ZVD09Lvw.png" /></figure><p>It doesn&#39;t look so bad when separated like this, but I will be packing the data together and adding on a lot more data, and that ends up looking like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*aW__NWtFaThIh__f4TdTDg.png" /></figure><p>It starts to become very hard to distinguish image data and that is the problem. In order to use the image data I need to be able to extract data for a single image from a dataset of roughly 300 images.</p><h4>Structure data for slicing</h4><p>Slicing specific data from the huge data set is the obvious solution. However, I don&#39;t want to handle the massive data set for every trait for every layer. That would waste gas and I need to be mindful of gas consumption. The solution I landed on was to group trait data into layers. That way I could slice all the body data into one variable and handle that to extract the required body sprite sheet. Structurally it looks like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/942/0*HFQD35jk9DvrFopV" /></figure><p>Ok, that all looks great, but I bet some of you are wondering how I actually pinpoint the data I need and extract it.</p><p>I know the length of data representing each sprite sheet. I can use that to calculate and store the index of the last byte of data for each. As long as I store that information in the same order as the trait Ids, I don&#39;t have to store anything else and I can use that to slice up a layer of data to extract any trait data I need.</p><p>Wow, that&#39;s a mouth full and certainly hard to visualize, so hopefully this example helps:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/620/0*wUcOZ8JmU83i64ZK" /></figure><p>The same can also be done when packing the layer bundles into the final collection bundle:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/818/0*ud9JcG8dk-tAfCZT" /></figure><p>Now that I have the data extraction method sorted, I just need to make sure there is no more data I need to pack in. I probably should have thought of this first but I pretty much made all this up as I went.</p><h4>Adding in Trait Names</h4><p>Turns out, I also need to pack in the trait names as the metadata for Baby Pepes includes the names for the traits. I could store that data in its own data section at the end of my big trait data set, but then I would also have to store more data to handle that data.</p><p>That just feels too messy and inefficient.</p><p>Instead, I opted to create a <strong>Trait Bundle</strong>. The Trait Bundles consist of the sprite sheet data, the name of the trait, and finally the length of the name. It looks like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/326/1*M9C8rkfrEMhglne9G0i0Hg.png" /><figcaption>Example of trait bundle structure and data</figcaption></figure><p>This structure allows me to store minimal information and still easily unpack the data with these steps:</p><ol><li>Extract this Trait Bundle from the Layer Bundle as previously explained</li><li>Extract the count</li><li>Use the count to extract the name</li><li>Extract the sprite sheet data</li></ol><p>Perfect, well nearly. There is still excessive data being stored due to the data format. We can optimize this data by at least 33%.</p><h3>Optimizing Bundles</h3><p>Anyone familiar with sending data over sockets and Redis probably already knows the key optimization Im about to share.</p><p>I can save about 33% in data size by storing all the data as bytes. By using base64 encoded sprite sheets Im adding roughly 33% to the data size — not good!</p><p>So, we keep the images as bytes. In fact, we store everything as bytes.</p><p>The count becomes a single byte, easily capable of storing the length of any trait name up to 255 characters long. The name can be up to 255 bytes long. The image data is as long as it needs to be.</p><p>But wait we can still do better and we are about to get into a whole new level of geekiness.</p><h3>PNG Structure Manipulation</h3><p>PNG images, like the ones I&#39;m converting the animated GIFs into, have a defined structure. They have to, otherwise, the internet would fall apart and all your fav memes would catch fire.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*6PYkbBUezqqOvAug" /></figure><p>If you can be bothered to take a look, there is a nice Wikipedia page that describes the PNG structure among other things:</p><p><a href="https://en.wikipedia.org/wiki/PNG">PNG - Wikipedia</a></p><p>But for the lazy, let me explain how I used this structure to save on data storage.</p><p>Simple PNGs like the ones I&#39;m handling have the same basic structure. They all share the same PNG signature and most of the data in the image header. This repetition of data means I can trim off the first 24 bytes from every image. I store those 24 bytes directly in the contract and every time I use a sprite sheet, I simply bolt it back on:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/608/1*r-bWNXbaOLAa7Pez-8ATqg.png" /><figcaption>Bolting back on the removed 24 bytes of data during sprite sheet use</figcaption></figure><p>That little trick saved me an additional 6,360 bytes — don&#39;t laugh, it all adds up :)</p><p>Next, I have to figure out how to handle the custom data of 1/1s.</p><h3>Adding 1 of 1s to the data bundle</h3><p>We can&#39;t move all the Baby Pepes on-chain and forget about our awesome 1/1s, like Pepechu and Sexy Pepe:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/288/1*pCvs1i94hG8_IzEsJ_y18w.gif" /><figcaption>Pepechu (1/1)</figcaption></figure><p>However, these tokens pose a new problem. They aren&#39;t made up of multiple layers and traits. Each 1/1 is a single GIF and has no trait ids to build from.</p><p>Luckily the solution was pretty straightforward. I created a data bundle, especially for the 1/1s. Think of them as having a single trait, that&#39;s exactly how I treat them. All the 1/1s bundles create their own special layer bundle. I simply use it as per all the other bundles.</p><p>Quick and easy now that we have a data handling model all planned out.</p><p>Awesome, that was the final issue that needed solving. We now have a data model and plans for retrieving any data we need — it&#39;s Solidity time!</p><h3>The Solidity Contracts</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*C0zFHC3IFFUI9IFg0JB1sg.png" /></figure><p>By now, I&#39;m sure most of you that are interested in on-chain NFT contracts have studied enough to know how they generally work, so I&#39;m not going to pad this already long article out any anymore. Plus my code is heavily commented.</p><p><strong>OnchainRenderer.sol:<br></strong><a href="https://www.contractreader.io/contract/mainnet/0xc4fca7eb2087568829e4b493adda247a11f0c966">https://www.contractreader.io/contract/mainnet/0xc4fca7eb2087568829e4b493adda247a11f0c966</a></p><p><strong>BundleManager.sol:<br></strong><a href="https://www.contractreader.io/contract/mainnet/0x75d71b583de37f2be7b271a79954055656620e7a">https://www.contractreader.io/contract/mainnet/0x75d71b583de37f2be7b271a79954055656620e7a</a></p><p>However, we do we still need to get the data on-chain.</p><p>To achieve this, I created a ton of Python scripts to build the bundle structure as described above. To make life easier for me, I made sure to save all the final data in hex format. I wanted to retain some readability for testing and playing with the data.</p><p>The final outcome was two files</p><ol><li>gene_pool.txt (80,000 bytes)</li><li>collection_bundle.txt (201,423 bytes)</li></ol><p>Next, I had to create a nice way to get all the data bundles on-chain. Luckily <a href="https://twitter.com/0xthedude">0xthedude</a> and I had just completed <a href="https://github.com/intartnft/scripty.sol">Scripty V2</a>, which comes with a nice facility to store massive data bundles on-chain.</p><p>I jumped into Hardhat (I know, I know, Foundry FTW) and threw together this simple script to chop up my final files and store the data via the Scripty storage script:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/1cc436d219724513f7bb6a80f7b2a402/href">https://medium.com/media/1cc436d219724513f7bb6a80f7b2a402/href</a></iframe><p>Just like that Baby Pepes move on-chain — easy O.o<br>The final cost for all the transactions was <strong>~1.075 Eth</strong></p><p>You can find the collection on Opensea:<br><a href="https://opensea.io/collection/babypepes">https://opensea.io/collection/babypepes</a></p><p>Join us on Twitter (X):<br><a href="https://twitter.com/baby_pepes">https://twitter.com/baby_pepes</a></p><p>Check out our website:<br><a href="https://www.babypepes.com/">https://www.babypepes.com/</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*asZbIPbmbRaCokDbmmZKjQ.png" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=512dac931ac3" width="1" height="1" alt=""><hr><p><a href="https://medium.com/coinmonks/how-i-put-an-nft-collection-of-20k-animated-gifs-on-chain-and-saved-9-eth-on-gas-512dac931ac3">How I put an NFT collection of 20k animated GIFs on-chain and saved 9 Eth on gas</a> was originally published in <a href="https://medium.com/coinmonks">Coinmonks</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[10 Simple Ways to Save Gas with Solidity]]></title>
            <link>https://medium.com/coinmonks/10-simple-ways-to-save-gas-with-solidity-f74cd2a59089?source=rss-f8fef5ff64a6------2</link>
            <guid isPermaLink="false">https://medium.com/p/f74cd2a59089</guid>
            <category><![CDATA[solidity]]></category>
            <category><![CDATA[nft]]></category>
            <category><![CDATA[solidity-tutorial]]></category>
            <dc:creator><![CDATA[xtremetom]]></dc:creator>
            <pubDate>Tue, 06 Sep 2022 10:07:30 GMT</pubDate>
            <atom:updated>2022-09-08T10:25:13.718Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OXQt-wvivqLIwGxunNh5pg.jpeg" /></figure><p>Below is a list of just a few simple ways you can save gas in solidity contracts. The full list is seemingly endless, but all boils down to finding the most efficient way to perform an action with the minimum number of operations.</p><h4>Table of contents</h4><ol><li>State data vs bytecode data</li><li>The power of do-while vs for loops</li><li>i++ vs ++i</li><li>Ignore safe maths — unchecked{}</li><li>Sticking to preferred data types</li><li>Calldata vs memory</li><li>Use memory to cache read variables</li><li>Efficient variable management</li><li>Save gas on the first write</li><li>Calling struct data</li></ol><h4>1. State data vs bytecode data</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/7ec6b66335461f3725bbb67a58039665/href">https://medium.com/media/7ec6b66335461f3725bbb67a58039665/href</a></iframe><p>By storing _b as a constant it is stored as immutable data in the contract’s bytecode. The same goes for our magic number 1000. _a on the other hand is stored in the contract’s state and requires a SLOAD operation to read it .</p><h4>2. The power of do-while vs for loops</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4bbe7e3dadc154d3cccfeca856f25ae5/href">https://medium.com/media/4bbe7e3dadc154d3cccfeca856f25ae5/href</a></iframe><p>Loops are pretty standard in almost every coding language and that familiarity can make us complacent.</p><p>The standard for loop will check the condition before executing the statement. A do while will execute the statement at least once before and then check the conditions, allowing for gas savings.</p><blockquote><strong>Word of warning:</strong> Only use a do-while when you are certain the code should execute at least once and remember to handle your conditions to prevent an infinite loop and gas out.</blockquote><p>For information on how do-while loops differ from while loops, check out this article:</p><p><a href="https://www.guru99.com/while-vs-do-while.html">Difference between while and do-while: Explained with Examples</a></p><h4>3. i++ vs ++i</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/07e640e6050f2f30afdd0387a2c07d93/href">https://medium.com/media/07e640e6050f2f30afdd0387a2c07d93/href</a></iframe><p>I know, I know, its a little crazy looking but the numbers dont lie — but why is it cheaper?</p><p>i++ increments the value of i by storing the original value in memory, incrementing it, then storing the resulting value into temporary memory and returning the original value. Upon that return the new value of i is updated. All in all it takes 4 operations.</p><p>++i increments the value of i and returns that value, which takes 2 operations.</p><h4>4. Ignore safe maths — unchecked{}</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ce86e5dd7afc2766519ed19be70a3681/href">https://medium.com/media/ce86e5dd7afc2766519ed19be70a3681/href</a></iframe><p>From Solidity docs:</p><p><a href="https://docs.soliditylang.org/en/v0.8.0/control-structures.html#checked-or-unchecked-arithmetic">https://docs.soliditylang.org/en/v0.8.0/control-structures.html#checked-or-unchecked-arithmetic</a></p><blockquote>Prior to Solidity 0.8.0, arithmetic operations would always wrap in case of under- or overflow leading to widespread use of libraries that introduce additional checks.</blockquote><blockquote>Since Solidity 0.8.0, all arithmetic operations revert on over- and underflow by default, thus making the use of these libraries unnecessary.</blockquote><blockquote>To obtain the previous behaviour, an unchecked block can be used:</blockquote><p>Which means you can save gas by employing the unchecked block to avoid unnecessary checks on your ++i.</p><p>In the example code above it is safe to do this because we know ++i will not overflow 2²⁵⁶, both of our loops will stop processing when i == 200</p><h4>5. Sticking to preferred data types</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3c8a912a336d54e2c35939be475c6928/href">https://medium.com/media/3c8a912a336d54e2c35939be475c6928/href</a></iframe><p>EVM (Ethereum Virtual Machine) is driven by 256 bit (32 bytes) variables. This means that in our loop above, the uint16 variable is converted to a uint256 before it is used, this conversion costs gas.</p><p>As you can see, we save gas by using uint256 for the variable i.</p><h4>6. Calldata vs memory</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/edb265f25abf389035dff34812cbee22/href">https://medium.com/media/edb265f25abf389035dff34812cbee22/href</a></iframe><p>From Solidity docs:</p><p><a href="https://docs.soliditylang.org/en/v0.5.11/types.html?highlight=memory#data-location">https://docs.soliditylang.org/en/v0.5.11/types.html?highlight=memory#data-location</a></p><blockquote>Calldata is only valid for parameters of external contract functions and is required for this type of parameter. Calldata is a non-modifiable, non-persistent area where function arguments are stored, and behaves mostly like memory.</blockquote><p>Its is suggested to try and use calldata where possible. It avoids unnecessary copies, hence the lower gas price and ensures the data is immutable.</p><p>Worth noting that since solidity 0.6.9 usage of memory and calldata is not limited by the visibility of the function.</p><p>For more details about the differences between storage, memory and calldata checkout this article:</p><p><a href="https://medium.com/coinmonks/solidity-storage-vs-memory-vs-calldata-8c7e8c38bce">Solidity — Storage vs Memory vs Calldata</a></p><h4>7. Use memory to cache read variables</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ed43a2b921810621289ef1ac514e5168/href">https://medium.com/media/ed43a2b921810621289ef1ac514e5168/href</a></iframe><p>Accessing a cold storage variable (first read) costs 2100 gas and 100 gas there after — warm storage access</p><p>You can save gas by caching the storage variable and reading the cached data instead of the storage data. This also works when reading data from a storage array.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a0124b76dc74d51bb7da3f8ec5a83fb6/href">https://medium.com/media/a0124b76dc74d51bb7da3f8ec5a83fb6/href</a></iframe><p>In the above example we are storing _arr in memory of SaveGas2 &gt; loop(). All reads are then called from our new arr.</p><p>This is a great gas saving method as long as the storage array is not too big. The data has to be copied to memory, so big arrays will be inefficient.</p><h4>8. Efficient variable management</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e860153c7f58e623c0cf079703f64391/href">https://medium.com/media/e860153c7f58e623c0cf079703f64391/href</a></iframe><p>If you plan on reusing a variable like solution and you know it will not inherit value from the previous loop, you can save gas by defining the variable outside of the loop — compare loops in SaveGas and SaveGas2.</p><p>Additionally, if you know there is no risk of overflow you can save more gas by nesting your solution calculation within the unchecked block.</p><h4>9. Save gas on the first write</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/efe2337f0e7056e2b6d1cdde82a1a374/href">https://medium.com/media/efe2337f0e7056e2b6d1cdde82a1a374/href</a></iframe><p>Changing a storage value to non-zero from a zero value will cost 20000 gas and all non-zero nth writes will cost 5000 gas. We can save the first user to call the function some gas by defining a non-zero value for _data.</p><h4>10. Calling struct data</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/68bba73c5cd03a32a4043194dbbed799/href">https://medium.com/media/68bba73c5cd03a32a4043194dbbed799/href</a></iframe><p>EVM stores data sequentially, trying to pack everything into 256 bit slots (32 bytes). In the example above, the struct data is arranged so x and y will occupy a single 256 bit memory slot and z will occupy its own slot — nice and tidy.</p><p>When it comes to reading our values we can save gas by minimizing how many memory slots we have to read and or cache.</p><p>SaveGas &gt; getUserZ() is storing the relevant MyData struct in memory — making a copy of both memory slots and then reading the relevant data.</p><p>SaveGas2 &gt; getUserZ() is reading data from the single memory slot containing z.</p><h3>Where to find me</h3><p>I can normally be found in the Cool Cats discord channel<br><a href="https://t.co/PjlbnmaW3a?amp=1">https://discord.gg/WhBAAHnSz4</a></p><p>Or on Twitter<br><a href="https://twitter.com/xtremetom">https://twitter.com/xtremetom</a></p><p><a href="https://twitter.com/coolcatsnft">JavaScript is not available.</a></p><blockquote>New to trading? Try <a href="https://medium.com/coinmonks/crypto-trading-bot-c2ffce8acb2a">crypto trading bots</a> or <a href="https://medium.com/coinmonks/top-10-crypto-copy-trading-platforms-for-beginners-d0c37c7d698c">copy trading</a></blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f74cd2a59089" width="1" height="1" alt=""><hr><p><a href="https://medium.com/coinmonks/10-simple-ways-to-save-gas-with-solidity-f74cd2a59089">10 Simple Ways to Save Gas with Solidity</a> was originally published in <a href="https://medium.com/coinmonks">Coinmonks</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Save Gas with Data Packing]]></title>
            <link>https://medium.com/coinmonks/save-gas-with-data-packing-dd9a28d4df15?source=rss-f8fef5ff64a6------2</link>
            <guid isPermaLink="false">https://medium.com/p/dd9a28d4df15</guid>
            <category><![CDATA[solidity]]></category>
            <category><![CDATA[solidity-tutorial]]></category>
            <category><![CDATA[nft]]></category>
            <dc:creator><![CDATA[xtremetom]]></dc:creator>
            <pubDate>Fri, 02 Sep 2022 16:34:27 GMT</pubDate>
            <atom:updated>2022-09-02T16:49:01.406Z</atom:updated>
            <content:encoded><![CDATA[<p>Storage writes are expensive, write less</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/907/1*0RZDMmjlkEUJsHwlWLtWng.jpeg" /></figure><p>Chances are you have interacted with a smart contract and thought — geez that was expensive. I know I have.</p><p>The culprit is usually the number of writes to storage or just simply bad logic. I cant help too much with bad logic, that usually comes with experience and trial and error. I can help by showing you a cool way to potentially reduce how many writes to storage your contract might need.</p><h3>What is data packing?</h3><p>We are used to seeing a unit256 as type definition for a variable:</p><pre>uint256 number = 1</pre><p>However, that uint256 is simply a data type that houses 256 bits. Clearly our little <strong>1</strong> is not using up all 256 bits, so how do we use up the other bits?</p><p>Uint256 is not the only available uint, if you know a variable will not exceed certain values you can start using smaller types. If you know a value wont exceed 2⁶⁴<strong> </strong>you could use a uint64 and this it where things get interesting.</p><pre>256 / 64 = 4</pre><p>If uint64 uses 64 bits, that means you can pack 4 uint64s into a single unit256.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/16ccd65b92fdb591ceef09d9d7dd8b24/href">https://medium.com/media/16ccd65b92fdb591ceef09d9d7dd8b24/href</a></iframe><blockquote>Gwei is a Solidity constant:<br><a href="https://docs.soliditylang.org/en/v0.8.15/units-and-global-variables.html">https://docs.soliditylang.org/en/v0.8.15/units-and-global-variables.html</a></blockquote><p>That function outputs the following number:</p><pre>25108406941546723056364004793593481054836439088298861789185000000000000</pre><p>If we feed that number into this function, it will output our original gwei:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/85200a28378c742e9f336688a8fdcf8b/href">https://medium.com/media/85200a28378c742e9f336688a8fdcf8b/href</a></iframe><p>Pretty cool right? <br>With data packing we can store multiple variables of smaller value types in a bigger value type.</p><p>Lets look at what is actually happening.</p><h3>How does data packing work?</h3><h4>Packing the data</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/624393eaf2109a9609414c3386d2fb3b/href">https://medium.com/media/624393eaf2109a9609414c3386d2fb3b/href</a></iframe><p><strong>Lines 2–5: </strong>Defining our data as uint64. I use gwei cos I’m lazy and wanted some big values without typing a lot. Its safe to use these values as I know they do not exceed 2⁶⁴.</p><p><strong>Line 7:</strong> packed is already defined as a unit256 in the returns of the function. Here we are setting the value to eth1k<strong>. </strong>This occupies the first (right to left) bits in the unit256.</p><pre>192 bits | eth1k (64 bits)<br>// packed = 1000000000000</pre><p><strong>Line 8:</strong> We use the bit wise OR assignment operator (|=) to performs the bitwise OR operation on the binary representation of the operands and assign the result to the packed.</p><p>In our case we are casting eth2kto uint256 and shifting it left (&lt;&lt;) by 64 bits because we know the first 64 bits are occupied by eth1k<strong>.</strong> Which will result in packed looking like this:</p><pre>128 bits | eth2k (64 bits) | eth1k (64 bits)<br>// packed = 36893488147419103233000000000000</pre><p><strong>Line 9:</strong> Again we use the OR operator, and shift eth3kleft by 128 bits. We know eth1k occupies the first 64 bits and eth2k occupies the second 64 bits. Resulting in packed looking like this:</p><pre>64 bits | eth3k (64 bits) | eth2k (64 bits) | eth1k (64 bits)<br>// packed = 1020847100762815390427017310442723737601000000000000</pre><p><strong>Line 10:</strong> Same as line 9 but shifting eth4kinto the last available 64 bits:</p><pre>eth4k (64 bits) | eth3k (64 bits) | eth2k (64 bits) | eth1k (64 bits)<br>// packed = 25108406941546723056364004793593481054836439088298861789185000000000000</pre><h4>Unpacking the data</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/af2aa147ec5396213d3358b56928101b/href">https://medium.com/media/af2aa147ec5396213d3358b56928101b/href</a></iframe><p><strong>Line 2:</strong> We know that eth1k occupies the first 64 bits, simply casting packed (uint256) to uint64 isolates those bits and gives us eth1k.</p><p><strong>Line 3:</strong> eth2k sits in the next 64 bits. We can use the same trick as we did with eth1k, we just have to move eth1kout of the way. We can do this by shifting packed to the right (&gt;&gt;) by 64 bits:</p><pre>64 bits | eth4k (64 bits) | eth3k (64 bits) | eth2k (64 bits)<br>// eth1k has been pushed off the end :)</pre><p><strong>Line 4:</strong> eth3k sits 128 bits to the left, so if we shift packed 128 bits to the right (&gt;&gt;) we can use the trick from line 2:</p><pre>128 bits | eth4k (64 bits) | eth3k (64 bits)<br>// eth2k + eth1k have been pushed off the end</pre><p><strong>Line 5:</strong> eth4k is 192 bits to the left of packed, repeat the same trick from line 4 and we have our data:</p><pre>192 bits | eth4k (64 bits)<br>// eth3k + eth2k + eth1k have all been pushed off the end</pre><h3>How does data packing save gas?</h3><p>Amazing, we can pack and unpack data, but why bother?</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5dcfd684fca35059a1fd4fb04796dfba/href">https://medium.com/media/5dcfd684fca35059a1fd4fb04796dfba/href</a></iframe><p>Writing to storage less saves gas and in the above example you can see writing the packed data to the mapping instead of the four separate variables <strong>saves a massive 66,470 units of gas</strong>. This is because we are doing a single write instead of four separate writes.</p><h4>Structs or data packing?</h4><p>Data packing really shines when you use it in place of structs, like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3a7e23b8885ac16e930465058e0cee54/href">https://medium.com/media/3a7e23b8885ac16e930465058e0cee54/href</a></iframe><blockquote>wow, that is so much more code</blockquote><p>Yes, there is more code. You have to pack to store the data and unpack to use the data. However, if the data being packed is updated and stored regularly the gas saving really add up and your users will thank you for looking out for their wallets.</p><p><strong>Packing addresses:</strong> Ethereum addresses are 20 bytes long. In order to pack them in a unit256 we need to cast them to a unit160 — <em>see line 37</em></p><p><strong>Unpacking addresses:</strong> To get an address from a unit160 you simply use the address cast on your uint160 address —<em> see line 49</em></p><p><strong>Nested packed data:</strong> Yep, you can nest packed data. Above we have taken an array of 6 uint16s and packed it into a unit96. We can then pack that into our uint256 — <em>see lines 29–38</em>.</p><p><strong>Unpacking nested packed data:</strong> First you need to obtain your nested packed uint and then unpack as you would any other data — <em>see lines 52–59</em>.</p><h3>Cool, what else can I do?</h3><p>Anywhere you have data you can employ data packing and its not restricted to uint data types.</p><p>One really nice use case is storing data in the id of an NFT. This would allow you to serve up deterministic data without having to write to storage.</p><h3>More, I need more information</h3><p>Bit manipulation is fun and this is just the tip of the iceberg. If you would like to dive deeper I highly suggest checking these articles out:</p><ul><li><a href="https://jeancvllr.medium.com/solidity-tutorial-all-about-bytes-9d88fdb22676">Solidity Tutorial : all about Bytes</a></li><li><a href="https://medium.com/@imolfar/bitwise-operations-and-bit-manipulation-in-solidity-ethereum-1751f3d2e216">Bitwise Operations and Bit Manipulation in Solidity, Ethereum</a></li></ul><h3>Where to find me</h3><p>I can normally be found in the Cool Cats discord channel<br><a href="https://t.co/PjlbnmaW3a?amp=1">https://discord.gg/WhBAAHnSz4</a></p><p>Or on Twitter<br><a href="https://twitter.com/xtremetom">https://twitter.com/xtremetom</a></p><p><a href="https://twitter.com/coolcatsnft">https://twitter.com/coolcatsnft</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=dd9a28d4df15" width="1" height="1" alt=""><hr><p><a href="https://medium.com/coinmonks/save-gas-with-data-packing-dd9a28d4df15">Save Gas with Data Packing</a> was originally published in <a href="https://medium.com/coinmonks">Coinmonks</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Cooltopia Login and Permission System]]></title>
            <link>https://xtremetom.medium.com/cooltopia-login-and-permission-system-49c06f23b915?source=rss-f8fef5ff64a6------2</link>
            <guid isPermaLink="false">https://medium.com/p/49c06f23b915</guid>
            <category><![CDATA[nft]]></category>
            <category><![CDATA[cool-cat]]></category>
            <category><![CDATA[solidity]]></category>
            <dc:creator><![CDATA[xtremetom]]></dc:creator>
            <pubDate>Wed, 25 May 2022 14:34:54 GMT</pubDate>
            <atom:updated>2022-05-25T14:35:13.493Z</atom:updated>
            <content:encoded><![CDATA[<p>A Solidity authorization and permission system</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tO7flqX97d2ghIc0TSRNWQ.jpeg" /></figure><p>If you have used Cooltopia, you will know that you only need to authorize your account once via a signed message. After that, you will no longer see any wallet popups asking for permissions. You don&#39;t have to pay for any transactions and the whole system runs on Polygon, while you are connected to Ethereum.</p><p>It all feels a little counterintuitive and confusing, but the end result is a system that allows users to freely interact with Cooltopia without interruptions or worries about spending too much on transactions.</p><p>So how did we achieve this?</p><h3>Ethereum and Polygon</h3><p>In designing Cooltopia we wanted to make it really simple for new NFT/crypto users to interact without the need to buy MATIC and swap chains on Metamask. Explaining how to fund a wallet with Eth is hard enough and for first-time users, it&#39;s scary.</p><p>Luckily Polygon is EVM-compatible.</p><p>Among other things, this means we can write Solidity code designed for Ethereum and it will also run on Polygon. This opens a lot of doors</p><h4>Shared Addresses</h4><p>Your address on Ethereum is also your address on Polygon. Of course, the tokens held on Ethereum are not mirrored on Polygon.</p><p>Think of your address as a house and that house exists in two somewhat parallel universes. Same street and town address and both houses start empty. As you buy furniture in the Ethereum universe it gets added to your Ethereum house, but not your Polygon house and vice versa.</p><h4>Shared Signatures</h4><p>The beauty of having a shared address and EVM compatibility is that you can sign a data packet on Ethereum and pass that signature to Polygon and it is still valid.</p><p>When you authorize on Cooltopia you are connected to Ethereum and sign the following data:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/372/1*liRkjWoGyFGqL52DNsus1A.png" /></figure><blockquote><strong>Note:</strong> Always take note of anything you sign with your wallet. Signing unexpected data could result in a wallet hack. For this reason, we make all our signature requests human-readable and not hashed data</blockquote><p>Once the data is signed by you, the signature is passed to our Polygon contracts where we run a few security checks to ensure the data is unique and actually signed by you.</p><p>We never store any signatures :)</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/55e98c4924e91a8ddb49804f6d9e3487/href">https://medium.com/media/55e98c4924e91a8ddb49804f6d9e3487/href</a></iframe><p>There are a few points to note here:</p><ul><li><strong>msgHash</strong>: We rebuild the expected message to ensure the signature matches</li><li><strong>_isValidSignature()</strong>: The msgHash is checked against your address and the signature to verify that you have requested this connection</li></ul><p>If you would like to learn more about signature verifications have a look here:</p><p><a href="https://xtremetom.medium.com/verifying-solidity-signatures-4898d003846b">Verifying Solidity Signatures</a></p><p>Let’s look at why we require you to connect in this way.</p><h3>Access Control</h3><p>Access Control is exactly as its name suggests. A system to control who has access to certain features and functions. You can think of it as a system to grant users things like moderator and admin permissions.</p><p><a href="https://docs.openzeppelin.com/contracts/3.x/access-control">Access Control - OpenZeppelin Docs</a></p><h4>Cooltopia Roles</h4><p>Our contracts use three main roles:</p><ul><li><strong>ADMIN_ROLE</strong>: Used to make changes to game settings for the purposes of balancing and maintenance</li><li><strong>CONTRACT_ROLE</strong>: Refers to contracts within our system. Sometimes they need to fire off functions within other system contracts and this role allows for that</li><li><strong>GAME_ROLE</strong>: Used for requests coming directly from users playing the game</li></ul><p>Possibly a little confusing without an example so let&#39;s look at how the ItemFactory plugs into the system and what happens when a user buys a Pet Chest.</p><h4>Connecting Contracts</h4><p>Below is an ItemFactory.sol contract that connects to the SystemChecker.sol contract via the HSystemChecker.sol</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d9f650e610f9c4826e30932b82262e2f/href">https://medium.com/media/d9f650e610f9c4826e30932b82262e2f/href</a></iframe><p>Notice that ItemFactory.sol line #17 passes systemCheckerAddress as a parameter to HSystemChecker which it extends.</p><p>Looking at HSystemChecker.sol line #14 you can see we have connected to SystemChecker via an interface.</p><p>Note HSystemChecker.sol does not extend SystemChecker.sol. This is because we need to access the data that exists within the contract and not just the structure.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b6c41c737132a8a0b2fd0b7a8c4ea3bb/href">https://medium.com/media/b6c41c737132a8a0b2fd0b7a8c4ea3bb/href</a></iframe><p>After all this connecting, ItemFactory can use the modifiers onlyRole and isUser . With access to these modifiers and the ability to access the data stored in SystemChecker.sol, we are now ready to use the roles that were detailed above and let users buy Pet Chest.</p><h4>Buying a Pet Chest</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*PQEZz0qMsv35GWqIRo0EDw.png" /></figure><p>Before we follow the flow of buying a chest, a quick recap.</p><p><strong>User State:</strong></p><ul><li>Connected to Ethereum</li><li>Authorized on Cooltopia</li><li>Sat on the shop with MILK and ready to buy</li></ul><p><strong>Contract State:</strong></p><ul><li>ItemFactory.sol extends HSystemChecker.sol</li><li>HSystemChecker.sol has access to SystemChecker.sol</li><li>ItemFactory.sol can access SystemChecker.sol data via modifiers</li></ul><p><strong>Time to buy</strong></p><p>A user requests to buy a Pet Chest by interacting with the web UI. The request is passed to our system which in turn fires off the transaction on Polygon (did I mention we pay for all the gas? :))</p><p>The transaction hits the buyBox() function and the onlyRole(GAME_ROLE) checks that the transaction call originated from the Cool Cats’ system.</p><p>From there the function starts to run and then we hit the _mint() function. If you examine that function you will notice that it fires off the modifier isUser which checks that the user minting the Pet Chest is an authorized user.</p><p>If an unauthorized user tried to buy a Pet Chest, it would revert. Otherwise, all is well and the user mints a Pet Chest.</p><p>…</p><p>Wait, what about the burning of MILK?</p><h3>Avoiding Popups</h3><p>This is where the power of the Cooltopia authorization comes into play. When you authorized you hopefully took the time to read the notification:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/797/1*IRQDBqD12OLoR0UN1g6pSA.png" /></figure><p>You can think of authorizing as logging into a traditional game.</p><p>While you are logged into the game and playing, the game doesn&#39;t keep asking you to give permission for the action you just asked it to perform. Instead, it does as you requested (given that you have all the appropriate requirements — gold, items, etc). Thereby helping you to stay immersed.</p><p>Nice and streamlined for the user, but how do we achieve this?</p><blockquote><strong>Note:</strong> Cooltopia can only update Cooltopia related tokens. Granting authorization <strong>does not</strong> allow access to any other tokens in your wallet</blockquote><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/eee2d7708347e00dae733ea2141060fd/href">https://medium.com/media/eee2d7708347e00dae733ea2141060fd/href</a></iframe><p>When a user buys a Pet Chest the function treasury.burn() is called from within buyBox() on the ItemFactory.sol contract. As you can see on line #28 in the above code snippet, burn() uses the modifier onlyRole(CONTRACT_ROLE) .</p><p>When we deployed ItemFactory.sol we granted it the CONTRACT_ROLE which allows it to call treasury.burn() . If you were to call burn() from an address that didn&#39;t have the CONTRACT_ROLE , your transaction would revert.</p><p>treasury.burn() calls _milkChild.gameBurn() and this is where the users&#39; MILK is burned.</p><p>gameBurn() can only be called by a contract with the TREASURY_ROLE . Given to the Treasury.sol contract upon deployment.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/122d44a6d29c8c8832d40fae6a5838b0/href">https://medium.com/media/122d44a6d29c8c8832d40fae6a5838b0/href</a></iframe><p>gameBurn() is where the anti-popup magic happens.</p><p>Remember that logged-in users have already agreed that Cooltopia can update their tokens as part of the gaming experience. With this permission, we transact on the users&#39; behalf — simple yet powerful.</p><p>This prevents the user from being shown a permissions popup every time a token needs to be interacted with.</p><h3>Token Ownership</h3><p>Every token that a user earns in Cooltopia, goes straight into their wallets and if a user wishes to deauthorize (log out) of the game, they can:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/362/1*zSOOG7r2uftvnLOk-U4xgw.png" /></figure><p>In doing so, Cooltopia will have no ability to update their tokens any longer and the user still retains everything, free to sell and trade on the open market in places like Opensea.</p><h3>Extending the System</h3><p>Not only does this system allow for a nice user experience, it also makes contract upgrading extremely easy (with some caveats).</p><p>Contracts that produce tokens or store time-sensitive info can not be upgraded without the migration, things like the ItemFactory and MilkChild.</p><p>However, all other contracts can easily be decoupled from the system by revoking their CONTRACT_ROLE and ties to dependants. An upgraded contract can then be slotted in.</p><h4>Community Contracts</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*IXSOjpNcRgrvqpah" /></figure><p>The simple use of roles allows anyone to create a contract for Cooltopia and as long as it passes security and balancing audits (internal and public), it can be bolted on and ready to use.</p><h3>Important Notes</h3><p>The demo code snippets are taken from the live contracts and those are linked below each snippet.</p><p>I haven&#39;t explained all the features, there are a number of security features and structures that I won&#39;t detail.</p><p>As with all my dev blogs, use the code at your own risk.</p><h3>Resources</h3><ul><li><a href="https://cooltopia.coolcatsnft.com/">Cool Cats - Cool Hub</a></li><li><a href="https://docs.openzeppelin.com/contracts/4.x/api/token/erc1155">ERC 1155 - OpenZeppelin Docs</a></li><li><a href="https://docs.openzeppelin.com/contracts/4.x/api/token/erc20">ERC 20 - OpenZeppelin Docs</a></li><li><a href="https://docs.openzeppelin.com/contracts/4.x/api/token/erc721">ERC 721 - OpenZeppelin Docs</a></li></ul><h3>Where to find me</h3><p>I can normally be found in the Cool Cats discord channel<br><a href="https://t.co/PjlbnmaW3a?amp=1">https://discord.gg/WhBAAHnSz4</a></p><p>Or on Twitter<br><a href="https://twitter.com/xtremetom">https://twitter.com/xtremetom</a></p><p><a href="https://twitter.com/coolcatsnft">https://twitter.com/coolcatsnft</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=49c06f23b915" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Solidity — Random Numbers]]></title>
            <link>https://medium.com/coinmonks/solidity-random-numbers-f54e1272c7dd?source=rss-f8fef5ff64a6------2</link>
            <guid isPermaLink="false">https://medium.com/p/f54e1272c7dd</guid>
            <category><![CDATA[solidity]]></category>
            <category><![CDATA[nft]]></category>
            <dc:creator><![CDATA[xtremetom]]></dc:creator>
            <pubDate>Sun, 19 Dec 2021 19:35:39 GMT</pubDate>
            <atom:updated>2022-10-29T02:32:07.028Z</atom:updated>
            <content:encoded><![CDATA[<h3>Solidity — Random Numbers</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3dYdmpecHy1QoCyGLV-f4g.png" /></figure><p>Hopefully, you know that it is impossible to generate a random number purely on-chain. You need to use services like chainlink, but let&#39;s be completely honest, pseudo-random is usually good enough for most needs.</p><p>However, sometimes you need multiple random numbers and things start to get expensive in terms of gas.</p><h3>Bit Shifting</h3><p>Bit manipulation is really fun, or I have been diving way too deep into some very obscure articles. Either way, it&#39;s extremely useful and can be used to save you gas.</p><h4>What is bit shifting?</h4><p>This subject and other byte manipulation techniques have been covered by people that are far smarter and more experienced than me:</p><p><a href="https://medium.com/@imolfar/bitwise-operations-and-bit-manipulation-in-solidity-ethereum-1751f3d2e216">Bitwise Operations and Bit Manipulation in Solidity, Ethereum</a></p><h4>How do we use bit shifting with RNG?</h4><p>Ok, imagine you need to generate 10 random numbers for attributes on your RPG NFT. You could create something like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/31a4ec2b5a1d52e56b1b5620d1d5ad5f/href">https://medium.com/media/31a4ec2b5a1d52e56b1b5620d1d5ad5f/href</a></iframe><p>The problem is you have to call it ten times.</p><p>There is a nice solution:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/7a32d63b1e94c76ae8595ef06f8c5be7/href">https://medium.com/media/7a32d63b1e94c76ae8595ef06f8c5be7/href</a></iframe><p>Just like that we only need to create a single randomHash and then use bit manipulation to create the 10 required random numbers.</p><p>Its worth noting that bit shifting doesn&#39;t mean you can shift forever, you will run out of bits. If you have a uint256 variable and you are shifting &gt;&gt; 16, you can only shift 16 times, cos 16 x 16 = 256.</p><p>I personally find it easier if there is some visual explanation of concepts and thought others might find a visual of bit shifting helpful.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/644/1*QAys21B8mdldnB-r8fKcqA.png" /><figcaption>uint256 &gt;&gt; 8, notice the angle versus &gt;&gt; 16 below</figcaption></figure><p>And here is what happens if you shift beyond the available data. uint256 &gt;&gt; 16, 20 times:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/694/1*ORPIlad_dr8fuLWjuhrmPQ.png" /></figure><h3>Random Number with Tracking</h3><p>Sometimes you need to pick a random number from an existing list while tracking the numbers you have already picked.</p><p>In javascript this kind of task is trivial, but in solidity, you really have to put some thought into the methodology to save on gas.</p><h4>Random TokenId from Fixed Population</h4><p>Imagine you have an NFT project with 10k tokens and you want to mint them out in random order. Most methods require a lot of looping and storing of data, which can get expensive and run the risk of gassing out.</p><h4>Magical Arrays</h4><p>I have no idea what the proper name for this method is, so I just call it Magical Arrays :)</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/bc4857b6b8ef461e36399db07298014d/href">https://medium.com/media/bc4857b6b8ef461e36399db07298014d/href</a></iframe><p><strong>MAX_POPULATION:</strong> is the population count of the NFT we are creating.</p><p><strong>ids:</strong> Is an empty array equal in length to MAX_POPULATION. If MAX_POPULATION is 5 then our array will look like this:</p><pre>[0,0,0,0,0]</pre><p>When pickId() is called a <strong>randomIndex</strong> is generated between 0 and the length of the array. This is important because we will be reducing the length of the array and ergo the range of possible <strong>randomIndex</strong>.</p><p>The next few lines of code are where the magic stuff starts to happen. I&#39;ll try to explain it as best I can.</p><h4>First Pass</h4><p>Imagine our array [0, 0, 0, 0, 0], it is 5 elements long but every element is 0. Feels a little useless right? Cool thing is, this is extremely helpful.</p><p>Imagine our <strong>randomIndex</strong> value is 2. We check index 2 in the array and we can see the value is 0 so we set our return value of <strong>id</strong> to the index we just checked, <strong>2.</strong></p><pre>[0, 0, 0, 0, 0]<br>[2] = 0<br>id = 2</pre><p>Now that we have an id value we need to edit our array to reflect what just happened and to ensure we don&#39;t lose any ids. Sounds like a lot to expect from an array.</p><p>If we don&#39;t edit the array a <strong>randomIndex</strong> value of 2 would result in the exact same return value and we don&#39;t want two NFTs with the tokenId of 2. To solve this problem we check to see if the last element in the array is equal to 0</p><pre>if(ids[ids.length-1] == 0)</pre><p>If it is, we replace the value at ids[randomIndex] with the index value of the last array element. That looks like this</p><pre>randomIndex = 2<br>[0, 0, 4, 0, 0]</pre><p>Finally, we need to shrink the array by popping off the last element. This results in our array now looking like this.</p><pre>[0, 0, 4, 0]</pre><h4>Second Pass</h4><p>When we pick our next <strong>id</strong> the <strong>randomIndex</strong> value again comes out as 2. Feels like this could be a problem, right — nope we got this covered.</p><p>First, we check if the value in the array at index 2 is equal to 0. We can see that it is equal to 4</p><pre>[0, 0, 4, 0]</pre><p>In the first pass, our index value was equal to 0 and so we returned the <strong>randomIndex</strong> value of 2. This time we have a value of 4, so we return the 4.</p><p>Just like the first pass, we need to edit the array to reflect what just happened. Resulting in our array now looking like this:</p><pre>[0, 0, 3]</pre><h4>Third Pass</h4><p>Wouldnt you know it, <strong>randomIndex</strong> rolled another 2.</p><p>This time the value at index 2 is a 3, so we will return 3 and edit the array to reflect the event.</p><p>This time things are a little different, the last value in the array is not equal to 0 and so we have to do things a little differently and the resulting array ends up being</p><pre>[0, 0]</pre><h4>Magic Array</h4><p>I know this can be a little tricky to get your head around and the first time I played with it I was mapping arrays out on paper just to make sure I fully understood the concept.</p><h4>Christmas Teaser</h4><p>The magic array code sample above can be optimized, can you see how?</p><h3>Where to find me</h3><p>I can normally be found in the Cool Cats discord channel<br><a href="https://t.co/PjlbnmaW3a?amp=1">https://discord.gg/WhBAAHnSz4</a></p><p>Or on Twitter<br><a href="https://twitter.com/xtremetom">https://twitter.com/xtremetom</a></p><p><a href="https://twitter.com/coolcatsnft">https://twitter.com/coolcatsnft</a></p><h3>Last Word</h3><p>I feel silly saying this, but I have had so many messages I feel it might save you and me some time.</p><p>I work full time on Cool Cats I am swamped with work, so sadly I have to decline every job offer.</p><p>But I am always willing to help out by sharing resources and advice where I can.</p><blockquote>New to trading? Try <a href="https://medium.com/coinmonks/crypto-trading-bot-c2ffce8acb2a">crypto trading bots</a> or <a href="https://medium.com/coinmonks/top-10-crypto-copy-trading-platforms-for-beginners-d0c37c7d698c">copy trading</a></blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f54e1272c7dd" width="1" height="1" alt=""><hr><p><a href="https://medium.com/coinmonks/solidity-random-numbers-f54e1272c7dd">Solidity — Random Numbers</a> was originally published in <a href="https://medium.com/coinmonks">Coinmonks</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Verifying Solidity Signatures]]></title>
            <link>https://medium.com/coinmonks/verifying-solidity-signatures-4898d003846b?source=rss-f8fef5ff64a6------2</link>
            <guid isPermaLink="false">https://medium.com/p/4898d003846b</guid>
            <category><![CDATA[nft]]></category>
            <category><![CDATA[solidity]]></category>
            <dc:creator><![CDATA[xtremetom]]></dc:creator>
            <pubDate>Fri, 22 Oct 2021 13:01:51 GMT</pubDate>
            <atom:updated>2022-10-29T02:32:13.022Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*PVutC5Ve7MEZsqn54tp4Bw.png" /></figure><p>Processing everything on-chain is great, but not always practical and sometimes you might want to do something a little tricky in order to save users gas.</p><p>Sadly, simply passing the desired data to a contract is not secure and without security measures, people will certainly abuse your contract.</p><p>There is a nice solution to this issue.</p><h3>Solidity signatures and signing data</h3><p>You can bundle desired data into a hash and sign it with the desired wallet.</p><p>Below you can see me running a very simple signing on hardhat where I am treating the signer[0] as my backend.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/db83e6208faa87f580e16a054e2d1a65/href">https://medium.com/media/db83e6208faa87f580e16a054e2d1a65/href</a></iframe><p>On line #45 you can see I am connecting to the sigTest contract as userWallet and firing off the isDataValid function. Passing in the timestamp and signature. Let&#39;s jump over to the contract to see what happens next.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8673b96b48abea17c91af2b1af15321d/href">https://medium.com/media/8673b96b48abea17c91af2b1af15321d/href</a></iframe><p>Looking at isDataValid() we can see that the first thing we do is build the hash in the contract, using the same data as we used on the client. The resulting msgHash should be equal to the messageHash on the client. If there is any discrepancy in the data, the hashes won&#39;t match and the signature test will fail.</p><p>Still looking at isDataValid() you can see that it fires off a require and calls isValidSignature(). Here we have to first sign the hash. It might seem arbitrary to pack in <strong>\x19Ethereum Signed Message:\n32</strong>, have a good Google to understand the importance of it — its very interesting from a security point of view.</p><p>With the data signed we can utilize the ECDSA library to recover an address bashed on the has and signature.</p><p>If the recovered address matches the systemAddress, all is well and we consider that a success, else, fail.</p><p>I won&#39;t go into how the ECDSA library works as I believe it&#39;s important for developers to learn how to read other contracts and Google around to fill in knowledge gaps, plus I don&#39;t wanna ruin the fun :)</p><p><a href="https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol">openzeppelin-contracts/ECDSA.sol at master · OpenZeppelin/openzeppelin-contracts</a></p><h3>Important Notes</h3><p>The code I have provided is in no way production-ready. It was written extremely fast and with no testing.</p><p>Tracking nonces is a nice feature you should look into adding to this code. It helps avoid replay attacks.</p><h3>References</h3><ul><li><a href="https://solidity-by-example.org/signature/">https://solidity-by-example.org/signature/</a></li><li><a href="https://blog.ricmoo.com/verifying-messages-in-solidity-50a94f82b2ca">https://blog.ricmoo.com/verifying-messages-in-solidity-50a94f82b2ca</a></li><li><a href="https://yos.io/2018/11/16/ethereum-signatures/">https://yos.io/2018/11/16/ethereum-signatures/</a></li></ul><blockquote>New to trading? Try <a href="https://medium.com/coinmonks/crypto-trading-bot-c2ffce8acb2a">crypto trading bots</a> or <a href="https://medium.com/coinmonks/top-10-crypto-copy-trading-platforms-for-beginners-d0c37c7d698c">copy trading</a></blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4898d003846b" width="1" height="1" alt=""><hr><p><a href="https://medium.com/coinmonks/verifying-solidity-signatures-4898d003846b">Verifying Solidity Signatures</a> was originally published in <a href="https://medium.com/coinmonks">Coinmonks</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Cool Cats Air Dropper v2]]></title>
            <link>https://xtremetom.medium.com/cool-cats-air-dropper-v2-f02c32b162c8?source=rss-f8fef5ff64a6------2</link>
            <guid isPermaLink="false">https://medium.com/p/f02c32b162c8</guid>
            <category><![CDATA[nft]]></category>
            <category><![CDATA[solidity]]></category>
            <category><![CDATA[cool-cat]]></category>
            <dc:creator><![CDATA[xtremetom]]></dc:creator>
            <pubDate>Mon, 30 Aug 2021 13:35:48 GMT</pubDate>
            <atom:updated>2021-09-13T08:08:33.474Z</atom:updated>
            <content:encoded><![CDATA[<h4>Automated drops</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vkK-xwb85l3PWH9uzKAixw.png" /></figure><p>The first air dropper I created was a semi-auto dropper that required a fair amount of manual data entry. Not ideal, prone to human error and slow. At the time it made sense due to time constraints. For the TIME collaboration drop I wanted to create an air dropper that would be capable of firing off hundreds of tokens in seconds.</p><p>Before we dive into the code, I need to say that this article is not going to give you 100% of the Cool Cats’ air dropper source code. It will however cover the theory and point you in the right direction to start work on your own dropper. I do this for two reasons</p><ul><li>I believe an engaged developer that understands every line of their code is more capable of fixing any issues or making changes to fit new scenarios</li><li>Cool Cats needs to have some hidden secrets.</li></ul><p>As you read this article and code, please keep in mind that I am pretty new to solidity development and the solutions I come up with might not be the most efficient. Always take the time to do your code due diligence.</p><h3>Air dropper theory</h3><p>The idea behind the contract was to essentially act as a wallet. It needed to be capable of:</p><ul><li>receiving tokens</li><li>sending held tokens to addresses</li><li>resetting</li></ul><h4>Receiving tokens</h4><p>This might seem like an intuitive action — just send tokens to the contract, right? Nope. I tried that and all transactions failed.</p><p>After some head scratching and Googling, it hit me. The contract needs to explicitly have token holding capabilities defined:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/55ce16d2547e01781c59a692071b1ae4/href">https://medium.com/media/55ce16d2547e01781c59a692071b1ae4/href</a></iframe><p>As I intended to send ERC1155 tokens to the contract it needed to know how to hold them.</p><h4>Recognising tokens</h4><p>The contract is able to receive and hold tokens, awesome, but it needs to do stuff with those tokens, like transfer them.</p><p>I had no idea how to do this until I found this code, for an ERC20 token as below:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/aeedf87804e824daef1bee3c59244270/href">https://medium.com/media/aeedf87804e824daef1bee3c59244270/href</a></iframe><p>The key part of this code for me was this line:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/1aa784d7fda2c74a185d0f0b0ba273e7/href">https://medium.com/media/1aa784d7fda2c74a185d0f0b0ba273e7/href</a></iframe><p>Of course, I need to define the token that is being moved around. Duh!</p><p>With that epiphany, I created the following function that uses a contract address to define the token and stores the tokenId for later use.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5b7e2852029a3ed152aa55df231216d0/href">https://medium.com/media/5b7e2852029a3ed152aa55df231216d0/href</a></iframe><h4>Dropping to addresses</h4><p>This was the easy part. I simply send 100 addresses and the contract sends tokens to those addresses.</p><p>However, I have to stress again that the provided code is not 100% complete. There are situations in which this function will fail. Always make sure to run a full battery of unit tests and account for all edge cases.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/219caae309838dd1d20e50a19124e2d7/href">https://medium.com/media/219caae309838dd1d20e50a19124e2d7/href</a></iframe><h3>Where to find me</h3><p>I can normally be found in the Cool Cats discord channel<br><a href="https://t.co/PjlbnmaW3a?amp=1">https://discord.gg/WhBAAHnSz4</a></p><p>Or on Twitter<br><a href="https://twitter.com/xtremetom">https://twitter.com/xtremetom</a></p><p><a href="https://twitter.com/coolcatsnft">https://twitter.com/coolcatsnft</a></p><h3>Last Word</h3><p>I feel silly saying this, but I have had so many messages I feel it might save you and me some time.</p><p>I have a full-time job IRL and coupled with Cool Cats I am swamped with work, so sadly I have to decline every job offer.</p><p>But I am always willing to help out by sharing resources and advice where I can.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f02c32b162c8" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Reality of Running an NFT]]></title>
            <link>https://xtremetom.medium.com/the-reality-of-running-an-nft-28856216a95f?source=rss-f8fef5ff64a6------2</link>
            <guid isPermaLink="false">https://medium.com/p/28856216a95f</guid>
            <category><![CDATA[nft]]></category>
            <category><![CDATA[cool-cat]]></category>
            <dc:creator><![CDATA[xtremetom]]></dc:creator>
            <pubDate>Sun, 15 Aug 2021 14:41:12 GMT</pubDate>
            <atom:updated>2021-08-15T14:49:05.060Z</atom:updated>
            <content:encoded><![CDATA[<p>Community is key</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4J1TOcrRSNlPchneDImZUQ.jpeg" /></figure><p>Let me preface this article by saying, I speak to many NFTs owners and prospective owners. Nothing in this article is directed at any single person or NFT project. It is mostly a reflection of my personal experiences.</p><p>The Cool Cats team didn&#39;t expect things to grow as big or as fast as they did. It was a huge shock and in the first few days, I don&#39;t think any of us got much sleep. I hope this article helps another team to prepare for a similar situation.</p><h3>The NFT plan…</h3><p>I see a number of NFTs starting as a quaint idea. Cool Cats, for example, we wanted to create a family-friendly NFT that featured some cool and cute cats. Clon wanted to share his art with the world and Lynq and I wanted to learn more about NFTs, widen our code knowledge base, and perhaps draw more attention to Faticorns.</p><p>Building the actual NFT and all the separate pieces was more challenging than I first anticipated. I wrote about that here:</p><p><a href="https://medium.com/coinmonks/what-i-learned-from-building-cool-cats-nft-4057f279d400">https://medium.com/coinmonks/what-i-learned-from-building-cool-cats-nft-4057f279d400</a></p><p>However, as difficult as writing the code and learning solidity (I&#39;m not done, still lots to learn) was, nothing could have prepared us for what came next.</p><h3>The NFT reality…</h3><h4>Marketing is hard</h4><p>One of the hardest parts of running an NFT is getting eyes on it in the early days. Sure you can pay for ads and influencers to promote you but ask the wrong influencer and a percentage of the community will instantly hate you.</p><p>Try the slow and steady approach. By dropping progress reports as you go, you run the risk of never building up hype, and come launch day, everyone is numb to your awesome graphics and mechanics.</p><p>Do nothing till you are a week from launch and then blast Twitter. I have seen this work but it is very risky. Your hype train may never leave the station and on drop day, no one turns up.</p><p>To be completely honest, the landscape of NFT Twitter is changing so rapidly. A marketing mechanic that worked 1 month ago, might not work anymore.</p><p>I can only suggest that you take some time to research your favorite projects and the methods they used to gain traction.</p><h4>Launch day</h4><p>Stress, insane amounts of stress.</p><p>You will be surrounded by hundreds of people scrutinizing every decision you make.</p><p>If something goes wrong, you will be flooded with angry messages and your DMs will start pinging faster than you can respond.</p><p>If everything goes well, you will still be flooded with angry messages. Inevitably a number of people will not be able to buy due to gas or limited stock. As the NFT owner, you will be held responsible for everything.</p><h4>Post-launch: Hours 1 to 12</h4><p>Your new community should be happy with their new shiny NFTs, hopefully, most of them are. However, now new demands/questions start:</p><ul><li>when will you be listed on rarity.tools?</li><li>where is your sales bot?</li><li>why don&#39;t you have a Twitter bot yet?</li><li>why doesn&#39;t the discord have channel x / y / z?</li><li>why haven&#39;t you retweeted that celebrity x / y/ z has just bought your token?</li><li>do you have a legal team I can talk to?</li><li>what license does your IP fall under?</li><li>can I sell t-shirts with my NFT on them?</li><li>…</li></ul><h4>Post-launch: Day 1–3</h4><p>Hopefully, you have managed to get some sleep, but I doubt it. You are probably running on fumes. Luckily your community has been sleeping well and they have filled your DMs with endless questions, rage-filled messages, and hopefully a message or two of support.</p><p>The messages of support I received really helped me to stay positive and motivated.</p><h4>Laying the community foundations</h4><p>Once you have made it through the first few days, the whales and flippers have likely taken their profits and your holder base is starting to grow. Now is the time to really focus on what kind of community you want to nurture.</p><p>The choices you make now are the foundations for how the whole NFT space will view your project and will heavily influence the type of people that buy into your NFT — choose wisely.</p><h3>Managing your new community</h3><p>I cant prepare you for what you will experience, but I can offer some tips based on my experiences.</p><p>I have run a number of companies. Nothing huge, mostly small consumer-facing businesses. Being small, I inevitably had to be very hands-on and a lot of my job would entail dealing with upset and angry customers. In doing so I learned a few golden rules that have served Cool Cats very well:</p><ol><li><strong>Always be honest:</strong> If you lie and the community finds out, they will tear you apart.</li><li><strong>Explain your actions and reasoning as soon as possible</strong>: The sooner you can inform your community about changes and the reasons behind those, the better.</li><li><strong>Always be thorough and think things through</strong>: If you can show that you have fully considered all points of view during your decision-making, that will help people feel heard.</li><li><strong>Never ignore a problem</strong>: Most anger stems from people feeling that their desires are not being met or addressed.</li><li><strong>Plan ahead</strong>: A reactionary response by nature will be ill-prepared and could cause more damage than good.</li><li><strong>Plan for the best and worse case</strong>: Things won&#39;t always go as planned and the NFT space has a mind of its own. It is important to have plans and backups for when things go wrong</li><li><strong>Involve the community as much as possible</strong>: If you have a tough choice to make, involve the community. They might surprise you and offer up a solution you never considered.</li><li><strong>Stay calm</strong>: If the community can see you losing your cool, some bad actors will try to bait you. Take a step away from the situation (computer), take a few deep breathes, and re-enter calm and collect.</li></ol><h3>Parting thought</h3><p>Next time you are in the discord of a brand new project, spare some time to support the team. They are only human and I&#39;m sure it will be greatly appreciated. It certainly helped me and the whole Cool Cats team.</p><h3>Where to find me</h3><p>I can normally be found in the Cool Cats discord channel<br><a href="https://t.co/PjlbnmaW3a?amp=1">https://discord.gg/WhBAAHnSz4</a></p><p>Or on Twitter<br><a href="https://twitter.com/xtremetom">https://twitter.com/xtremetom</a></p><p><a href="https://twitter.com/coolcatsnft">https://twitter.com/coolcatsnft</a></p><h3>Last Word</h3><p>I feel silly saying this, but I have had so many messages I feel it might save you and me some time.</p><p>I have a full-time job IRL and coupled with Cool Cats I am swamped with work, so sadly I have to decline every job offer.</p><p>But I am always willing to help out by sharing resources and advice where I can.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=28856216a95f" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Cool Cats Contract — Improvements]]></title>
            <link>https://xtremetom.medium.com/cool-cats-contract-improvements-8643fcbc7fb9?source=rss-f8fef5ff64a6------2</link>
            <guid isPermaLink="false">https://medium.com/p/8643fcbc7fb9</guid>
            <category><![CDATA[nft]]></category>
            <category><![CDATA[cool-cat]]></category>
            <category><![CDATA[solidity]]></category>
            <dc:creator><![CDATA[xtremetom]]></dc:creator>
            <pubDate>Mon, 09 Aug 2021 13:47:59 GMT</pubDate>
            <atom:updated>2021-08-09T13:47:59.658Z</atom:updated>
            <content:encoded><![CDATA[<h3>Cool Cats Contract — Improvements</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Mh1GsE4Ma6o1BMKWLOntRg.jpeg" /></figure><p>Cool Cats launched a little over five weeks ago, although it feels like much longer. In that time I have spotted a few projects using the Cool Cats contract as a base for their NFT. As such, I feel that I should outline contract improvements that I would make.</p><h3>Trimming the fat</h3><p>In reviewing my own code, I spotted a silly mistake.</p><pre>using Strings for uint256;</pre><p>My code never uses <strong>Strings. </strong>In a previous version of my code, I used Strings to concatenate a tokenURI output, but my deployed contract doesn&#39;t do this.</p><p>This could safely be removed.</p><h3>Add IPFS provenance?</h3><p>A number of people have pointed out that Cool Cats doesn&#39;t include a provenance hash, and why didn&#39;t I add one?</p><p>The honest answer is — I forgot.</p><p>I should have added a variable to hold the final image directory hash. This would have been extremely simple to do. It would also give people peace of mind that we won&#39;t be changing the images. We never would, but people like the security.</p><h3>Owner only buying capability during a pause</h3><p>I have seen a number of new projects that required a minting pause during their drop.</p><p>During the pause, they needed to be able to mint some of their own tokens. The Cool Cats contract does not allow for this and this was by design.</p><p>I didn&#39;t want people to think that the owner could sneak in a few purchases during a pause. However, having witnessed a few projects falling fowl due to this limitation, I would now add that functionality.</p><h3>Send vs Transfer when withdrawing</h3><p>The current withdrawal function works but is not optimal. Rather than having to use a require coupled with send, I could have simply used transfer.</p><ul><li>.send() returns a boolean result for success/failure.</li><li>.transfer() reverts on failure</li></ul><p>There are other considerations when decided which function to use and under certain situations, one may pose a security risk — always do your own research</p><h3>Last words</h3><p>You will notice that I have not included a gist of the updated contract. Sorry, but you really should be putting some brainpower into creating your own contract or at least fully understand the code you are using.</p><p>The Cool Cats contract is very simple, but even a simple contract can carry too much fat, mistakes, and unoptimized functions.</p><p>Never assume a contract from an existing project is bulletproof or perfect.</p><p>Do your own due diligence, you own it to your potential holders/investors</p><h3>Where to find us:</h3><p>My Twitter: <a href="https://twitter.com/xtremetom">https://twitter.com/xtremetom</a></p><p>Cool Cats Team: <a href="https://twitter.com/coolcatsnft">https://twitter.com/coolcatsnft</a></p><p>Cool Cats Discord: <a href="http://discord.gg/coolcatsnft">discord.gg/coolcatsnft</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8643fcbc7fb9" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[What are Paper Cats?]]></title>
            <link>https://xtremetom.medium.com/what-are-paper-cats-5011b5d496b1?source=rss-f8fef5ff64a6------2</link>
            <guid isPermaLink="false">https://medium.com/p/5011b5d496b1</guid>
            <category><![CDATA[nft]]></category>
            <category><![CDATA[cool-cat]]></category>
            <category><![CDATA[cat-paper]]></category>
            <dc:creator><![CDATA[xtremetom]]></dc:creator>
            <pubDate>Tue, 03 Aug 2021 18:38:26 GMT</pubDate>
            <atom:updated>2021-08-03T18:48:43.625Z</atom:updated>
            <content:encoded><![CDATA[<p>Cats made of paper — right? Nope.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UisYGEyaRVPakqHrRlc0TQ.png" /></figure><p>Setting up your first crypto wallet is hard, it&#39;s confusing, and almost every tutorial is over complicated and full of jargon.</p><p>It doesn&#39;t have to be. This is where Paper Cats comes in.</p><h3>Friendly NFT/Crypto onboarding</h3><p>We are creating a series of comic-book-style tutorials that use simple language and graphics to help anyone understand and use NFTs with confidence.</p><p>In our minds we are designing the whole thing so you can share the tutorials with a regular internet user (some basic tech knowledge is required) and they will be able to:</p><ul><li>Create a new Metamask wallet</li><li>Add funds to their wallet</li><li>Understand the do’s and dont’s of wallet safety</li><li>Navigate OpenSea safely</li><li>Understand NFT drops and Gas</li><li>Understand NFT rarity and traits</li><li>Buy/Trade NFTs safely</li><li>Learn how to spot scams</li><li>and more…</li></ul><h3>Paper Cats token</h3><p>The easiest way for us to teach people about NFT drops and gas is with a live NFT. Obviously, this comes with many complications: wild price fluctuations, gas wars, potential minting errors, gas outs, and many many more.</p><p>The solution is pretty simple.</p><p>We are creating a new NFT with the following features:</p><ul><li>Very cheap — aiming for &lt;$10 per token</li><li>Infinite supply</li><li>Trait 1: background color</li><li>Tiered Trait 2: heart color</li></ul><h4>Anti-Whales and Pro-noob</h4><p>The infinite supply means that anyone wanting a Paper Cat can buy one directly from the contract at any time.</p><h4>Background Color</h4><p>The background color is picked at random and is designed to teach users about randomness and how that can affect rarity.</p><h4>Heart Color</h4><p>The heart color is based on the roll of a 100 sided virtual dice. There are 7 heart colors available.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kTmX2bnr0i6cXgj854ALrg.png" /></figure><p>This allows us to teach holders about tiers and will potentially result in some tokens being priced higher than others on the secondary market.</p><h3>NFT Developers</h3><p>Paper Cats will also be completely open-source and has been coded with simplicity in mind. Someone following the tutorial should be able to fully understand the process and produce their own functioning NFT.</p><p>Every step of the process has been documented and every bit of the code is explained.</p><p>To ensure we are creating tutorials that are easy to follow and offer real value to new NFT developers, we thought it would be best for a developer with very little NFT knowledge to be your guide. As such we tasked <a href="https://twitter.com/Grampa_Bacon">Adam</a>, one of the Cool Cats team members to work with <a href="https://twitter.com/xtremetom">Tom</a> to write a series of tutorials from his perspective.</p><p>It should be noted that Adam does have coding experience and has worked on many javascript and typescript projects.</p><h3>When will Paper Cats be ready?</h3><p>We have a few more weeks worth of work to do. Luckily the NFT community on Twitter has been super supportive and a whole army of people have offered to help. Nevertheless, it takes time to create simple tutorials for a complex topic.</p><p>We will make announcements and drop tweets as we hit key milestones.</p><h3>Where to find us</h3><p>I can normally be found in the Cool Cats discord channel<br><a href="https://t.co/PjlbnmaW3a?amp=1">https://discord.gg/WhBAAHnSz4</a></p><p>Or on Twitter<br><a href="https://twitter.com/xtremetom">https://twitter.com/xtremetom</a></p><p><a href="https://twitter.com/Grampa_Bacon">https://twitter.com/Grampa_Bacon</a></p><p><a href="https://twitter.com/coolcatsnft">https://twitter.com/coolcatsnft</a></p><h3>Last Word</h3><p>I feel silly saying this, but I have had so many messages I feel it might save you and me some time.</p><p>I have a full-time job IRL and coupled with Cool Cats I am swamped with work, so sadly I have to decline every job offer.</p><p>But I am always willing to help out by sharing resources and advice where I can.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5011b5d496b1" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>