<?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 Leo Jacoby on Medium]]></title>
        <description><![CDATA[Stories by Leo Jacoby on Medium]]></description>
        <link>https://medium.com/@leojacoby?source=rss-e9336890ba8d------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*wZGl3NGNzMA0ma2pEe3zdg.jpeg</url>
            <title>Stories by Leo Jacoby on Medium</title>
            <link>https://medium.com/@leojacoby?source=rss-e9336890ba8d------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 12:18:48 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@leojacoby/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[Learning Solidity: The Clone Wars]]></title>
            <link>https://medium.com/@leojacoby/learning-solidity-the-clone-wars-824c503366a3?source=rss-e9336890ba8d------2</link>
            <guid isPermaLink="false">https://medium.com/p/824c503366a3</guid>
            <category><![CDATA[testing]]></category>
            <category><![CDATA[solidity]]></category>
            <category><![CDATA[nft]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[web3]]></category>
            <dc:creator><![CDATA[Leo Jacoby]]></dc:creator>
            <pubDate>Mon, 28 Feb 2022 05:20:42 GMT</pubDate>
            <atom:updated>2022-03-02T00:05:14.795Z</atom:updated>
            <content:encoded><![CDATA[<p>If you haven’t already check out the first post and origin of this project: <a href="https://medium.com/@leojacoby/ok-ethdenver-you-win-869cd58265b5">https://medium.com/@leojacoby/ok-ethdenver-you-win-869cd58265b5</a></p><p>Sam Sends, you rock. Gaby Goldberg connected us at the chance he could help me with the stuff I was struggling with and he came through big time. <a href="https://excellent-babcat-4d5.notion.site/Leo-2a3a95659c9e4af98a5af2711edd0981">Here is a page</a> he whipped up for me. Biggest changes since last week…</p><ol><li><strong>Foundry! </strong>Sam recommended I switch from truffle to foundry for my Solidity testing, compiling, and deploying. So far it’s been great. Foundry lets you write tests in Solidity and offers a smooth CLI. I actually wrote some tests and was blown away with how useful it is! Because you can’t log anything from a smart contract once it’s deployed, testing the various function calls let me find and fix bugs which saved me a TON of time and headaches. Foundry lets you do “fuzzing” for testing and — I’ll be honest — I have no clue what that is, but it seems useful I’m excited to figure it out soon.</li><li><strong>Ethers.js! </strong>Sam also advised me to use ethers.js over web3.js because less buggy. That transition went pretty smooth and most involved me switching some javascript syntax.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/475/1*IrPEX1S51OZihcuLjGafcw.jpeg" /><figcaption><a href="https://i.pinimg.com/originals/32/b8/d6/32b8d619fb9192cae8dc846982741624.jpg">https://i.pinimg.com/originals/32/b8/d6/32b8d619fb9192cae8dc846982741624.jpg</a></figcaption></figure><p>3. <strong>Contract cloning!? Whoa.</strong> This is a big one. Previously I had been using a single contract that minted NFTs from every collection, but that was getting messy and I struggled to keep track of important bits like the token IDs for each collection. Sam put me on to <a href="https://github.com/wighawag/clones-with-immutable-args">this library for cloning contracts</a>, which I didn’t even know was possible. Essentially I start with two contracts: a CollectionFactory and a Collection. When the “clone” function is called, the CollectionFactory creates an instance of the Collection with some data that makes the Collection unique. In my case this data included the athlete’s address, the address that gets the funds from the mint, the mint price, the size of the collection, and the metadata of the NFTs.</p><pre>bytes memory data = abi.encodePacked(athleteAddress, addressToPay, price, maxSize, metadataIpfsHashBytes);</pre><pre>Collection clone = Collection(address(implementation).clone(data));</pre><p>The Collection contract can then access the data, but this was NOT easy for me to figure out. There’s these functions that allow you to get chunks of data of different types, but the data is stored in a bytes array, and you have to know the size of each piece of data to access it. I eventually figured out that addresses were 20 bytes and uint256s are 32 bytes, but strings (for metadata hash) were more than 32 bytes which made things tricky.</p><pre>athleteAddress = _getArgAddress(0);</pre><pre>price = _getArgUint256(40);</pre><pre>maxSize = _getArgUint256(72);</pre><pre>addressToPay = _getArgAddress(20);</pre><pre>uint256[] memory uintArr = _getArgUint256Array(104, 2);</pre><pre>uint256 one = uintArr[0];</pre><pre>uint256 two = uintArr[1];</pre><pre>bytes32 metadataIpfsHashOne = bytes32(one);</pre><pre>bytes32 metadataIpfsHashTwo = bytes32(bytes14(bytes32(two)));</pre><pre>metadataIpfsHash = concat(metadataIpfsHashOne, metadataIpfsHashTwo);</pre><p>My IPFS hash for the metadata had 46 characters, so I split it up into two uint256 arrays which I then converted back to bytes and then joined them with<a href="https://ethereum.stackexchange.com/questions/63942/how-to-join-two-bytes32-values-in-solidity"> this function I found online</a>. This was really a trial and error process and showed me how much I have to learn about data types in Solidity. I can’t imagine this is gas efficient.</p><p>Where I’m at:</p><p>Like before, athletes can sign up and create a collection. Fans can pay ETH to mint NFTs from collections but the tokens don’t have their metadata. Really not sure why this is. I wanted to write this post once I figured that out but realized that was against the “building in public” spirit. When I ask the ERC721 contract for the metadata of token with ID=1, I get a <a href="https://gateway.pinata.cloud/ipfs/QmUChcUFLtnWvYgcypEAtegHGPsC6XGcN2eUqBJp5Mk8mG">fully functional JSON like this</a>. But the NFT still looks like this in my Rainbow wallet. No name, description, or image :(</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/750/1*2gTFBCsZDcURXScZBCXxPQ.jpeg" /></figure><p>Considering I got the metadata to work previously and the fact that it’s being successfully attached to the token, I have to imagine I’m just doing something silly. You’re more than welcome to check my github (linked above) and tell me if you see something dumb.</p><p>Next steps/struggles:</p><ol><li>Fix this metadata thing</li><li>Learn how bytes, data types, and conversions between types works in Solidity (lmk if you have resources!)</li><li>Add some frontend polish</li><li>I’m getting an error when I try to “prank” during my tests. Pranking is calling a contract using a specified “msg.sender” (see below). Not crucial but it feels like this is a good thing to know how to do.</li></ol><pre>assertEq(clone.getCurrentTokenId(), 0);</pre><pre>cheats.prank(0x9bEF1f52763852A339471f298c6780e158E43A68);</pre><pre>uint256 newTokenId = clone.mintNFT{value: 50000000000000000}();</pre><pre>assertEq(newTokenId, 1);</pre><p>5. Fuzzing??</p><p>Thanks for reading! Please DM me on Twitter (@leotheprocess21) if you wanna get in touch.</p><p><strong>Update! </strong>I figured out the metadata issue. I was calling _setTokenUri with “gateway.pinata.cloud/ipfs/…” and I needed to be calling it with “https://gateway.pinata.cloud/ipfs/”.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=824c503366a3" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Ok ETHDenver, you win]]></title>
            <link>https://medium.com/@leojacoby/ok-ethdenver-you-win-869cd58265b5?source=rss-e9336890ba8d------2</link>
            <guid isPermaLink="false">https://medium.com/p/869cd58265b5</guid>
            <category><![CDATA[react]]></category>
            <category><![CDATA[solidity]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[web3]]></category>
            <category><![CDATA[nft]]></category>
            <dc:creator><![CDATA[Leo Jacoby]]></dc:creator>
            <pubDate>Mon, 21 Feb 2022 08:51:04 GMT</pubDate>
            <atom:updated>2022-02-28T05:09:46.691Z</atom:updated>
            <content:encoded><![CDATA[<p><strong>Monday 2/21/22</strong></p><p>Ok ETHDenver, you win. I’m Solidity-pilled. A man can only hear how in-demand Solidity devs are and the power that smart contracts will wield in the next iteration of the internet so many times before he breaks down and starts the CryptoZombies.io tutorial. And that’s exactly what I did. After a few weeks of getting my hand held I finished the tutorial and was ready to leave the nest. I knew just enough to punch in the right questions into Google.</p><p>Knowing myself, starting a project was the only way I was going to progress towards my learning goals. But no single idea stuck out to me as a project I truly believed had high-growth potential. I settled on building NBA top-shot-esque platform for amateur athletes, teams, and athletic departments. I call it Contender. The vision is for athletes to design a collection — eventually being able to create sweet jpegs and gifs using templates from designers — and allow fans to mint them. Compensating amateur athletes is legally tricky, so I’m incorporating charity as a central component to the projects.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/764/1*z8MMSbijSYsdqf0amz4H7A.png" /><figcaption>A sweet jpeg from <a href="https://dot.la/ballertv-nft-2653283659/particle-2">https://dot.la/ballertv-nft-2653283659/particle-2</a></figcaption></figure><p><strong>Github: </strong><a href="https://github.com/leojacob1/contender-fe"><strong>Frontend</strong></a><strong>, </strong><a href="https://github.com/leojacob1/contender-truffle"><strong>Contracts</strong></a></p><p>The hurdles</p><ul><li>How do I make a basic NFT smart contract? Answer: OpenZeppelin. I copy and pasted <a href="https://docs.openzeppelin.com/contracts/4.x/api/token/erc721#ERC721URIStorage">their meat and potatoes ERC-721 contract</a> as a starting point.</li><li>How do I test? Answer: Rinkeby. I started with Ganache but because of MetaMask’s lack of NFT-support for their Chrome extension and my inability to connect to a local network from my phone, I settled on Rinkeby as my testnet.</li><li>How do I deploy a smart contract? Answer: <a href="https://trufflesuite.com/">Truffle</a>. I’ve used Truffle to handle compiling and deploying my contracts.</li><li>How do I interface with my contracts from a frontend? Answer: <a href="https://web3js.readthedocs.io/en/v1.7.0/">web3.js</a>. Their package has everything I need to connect to a contract, call methods, and listen for events.</li><li>How do I store metadata? Answer: After receiving a token name, description, and image on the frontend, I pin the content to IPFS using <a href="https://pinata.cloud/">Pinata</a>. They have a clean API that lets me host the data and just store a simple hash of the image and metadata JSON on chain. Here was a big milestone I reached after I implemented Pinata with my minting interface…minting a Blue Chip “Cousin Greg” NFT to my Rainbow wallet on the Rinkeby network</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/750/1*E1hhJwRn_cyZWYTDlKkaFA.png" /></figure><ul><li>How do I authenticate users? Answer: My Web2 instincts drove me to add Firebase Auth to my app, but I quickly realized that Ethereum sign accomplishes everything I need, given that Google’s user information is of little use to me. I store the user’s address using MobX so I can access it app-wide. I still need to do a better job of making that data persistent.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*gFRimlbqnUPyoAi1bftj0Q.png" /></figure><ul><li>How do I store data? Answer: Hybrid on/off chain. I have two schemas: Athlete and NFT Collection. The only reason I need to store Athlete information is so I can make it easy for them to create additional collections and make their collections linked within the interface. These seemed like UI issues and not functional concerns, so I decided to use Firestore to keep track of athletes. On the other hand, I care deeply about allowing anyone to interface with the smart contracts without using my app. To accomplish this, I needed to keep Collection data on chain. To minimize storage costs, I use events to track each time an athlete creates a collection, which I can then query from a frontend. Additionally, I use a mapping from collection ID to a counter to keep track of the current number of mints for each collection. I’m still struggling to fuse these two sources of information on the frontend when it’s time to display the data.</li></ul><p>Where I’m at:</p><p>Users can log in, create an athlete, log out, log back in, and reassume their identity as that athlete. They can then create a collection that appears on their athlete page for all of their fans to see. Fans can mint NFTs from specific collections, but I’m still working out the kinks of that flow when fans pay for NFTs. Mo is my little brother btw :)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/562/1*qDNGhBI9weFPfBlVmMRc9A.png" /></figure><p>What I’m struggling with:</p><ul><li>Adding current collection index (number of mints) to the collection data that comes from the event stream. This should be done on the frontend.</li><li>Allowing fans to send ETH to the contract and receive an NFT in return. It works without the payment part, so I’m not sure where it’s breaking.</li><li>Solidity doesn’t like decimals and javascript doesn’t like big numbers. The athlete inputs price in ETH, I convert it to WEI, and when I send it to the contract, it turns into an entirely different number. I’m using the “toWei” function from web3.js.</li><li>As you may have noticed, I’ve spent zero effort into design. Will definitely make that a priority before I show this to anyone important.</li></ul><p>I’m going to try to keep updating this post as I make progress, but I have a bad habit of not making writing a priority. Also, I decided to write more generally, so look out for some more pieces about tech stuff and beyond.</p><p><strong>Update: </strong>I’m making additional posts so check my profile for those.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=869cd58265b5" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>