<?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 Rubydusa on Medium]]></title>
        <description><![CDATA[Stories by Rubydusa on Medium]]></description>
        <link>https://medium.com/@rubydusa?source=rss-ba8923eeaab------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*1r77L_rUNhw7daGqkET2CQ.png</url>
            <title>Stories by Rubydusa on Medium</title>
            <link>https://medium.com/@rubydusa?source=rss-ba8923eeaab------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Wed, 04 Nov 2026 23:30:37 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@rubydusa/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[Symmetric Encryption In Circom]]></title>
            <link>https://rubydusa.medium.com/symmetric-encryption-in-circom-53137de2a011?source=rss-ba8923eeaab------2</link>
            <guid isPermaLink="false">https://medium.com/p/53137de2a011</guid>
            <category><![CDATA[encryption]]></category>
            <category><![CDATA[circom]]></category>
            <category><![CDATA[zero-knowledge-proofs]]></category>
            <category><![CDATA[symmetric-encryption]]></category>
            <category><![CDATA[zksnark]]></category>
            <dc:creator><![CDATA[Rubydusa]]></dc:creator>
            <pubDate>Tue, 23 Jan 2024 20:02:22 GMT</pubDate>
            <atom:updated>2024-03-13T11:56:47.805Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Hv9L-Bg6xodrFns9jsS5Pw.png" /></figure><p>In this article, I will explain how to implement symmetric encryption in Circom using simple to understand algorithms that are zkSNARK friendly. In particular, we’ll use the Poseidon hash function to implement encryption and decryption.</p><p>Many traditional symmetric encryption schemes aren’t suited for prime field arithmetic because they rely on heavy bit manipulation, which is considerably more expensive and complicated to perform within Circom.</p><p>I will not cover the basics of Circom in this article, it’s expected the reader has a basic understanding of how Circom works.</p><p><strong>This article DOES NOT cover on how to ensure authenticity and non-malleability of messages.</strong></p><p><strong>Code snippets used in this article ARE NOT secure and should not be used in production.</strong></p><h3>The General Idea</h3><p>Let’s assume the message we want to encrypt is contained within a single signal. Each party has access to the same key (thus — symmetric key) and uses it to perform encryption and decryption on transmitted messages.</p><p>The main idea behind this encryption algorithm is masking the message with a secret number that is derived from the key.</p><p>Aside from a key to encrypt with, every new encryption would need to use a one-time random value (an ephemeral key) which is referred to by the name of <strong>nonce</strong> in all following code sections. The nonce is always public information.</p><p>To encrypt, you sum the hash of the key and the nonce with the message. When decrypting, you reverse the process by subtracting the hash from the encrypted message.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/335/1*5H8-lakHGmNrP-lQYoVs8Q.png" /></figure><p>The following code illustrates the main idea clearly, but it should be noted it’s not secure and lacks protection against various types of attacks.</p><h4>Encrypting A Single Signal</h4><pre>include &quot;circomlib/circuits/poseidon.circom&quot;;<br><br>template SymmetricEncrypt() {<br>    signal input message;  // private<br>    signal input key;  // private<br>    signal input nonce;  // public<br><br>    signal output encrypteMessage;<br><br>    component hasher = Poseidon(2);<br><br>    hasher.inputs[0] &lt;== key;<br>    hasher.inputs[1] &lt;== nonce;<br><br>    encrypteMessage &lt;== hasher.out + message;<br>}</pre><h4>Decrypting A Single Signal</h4><pre>include &quot;circomlib/circuits/poseidon.circom&quot;;<br><br>template SymmetricDecrypt() {<br>    signal input encryptedMessage; // public<br>    signal input key;  // private<br>    signal input nonce; // public <br><br>    signal output message;<br><br>    component hasher = Poseidon(2);<br><br>    hasher.inputs[0] &lt;== key;<br>    hasher.inputs[1] &lt;== nonce;<br><br>    message &lt;== encryptedMessage - hasher.out;<br>}</pre><h3>How Does It Work?</h3><p>It may sound too simple to work, but if you think about it, the sum of two numbers in a prime field bears no information on the two individual numbers that were summed. The hash is uniformly random and there is no practical way to guess it.</p><p>However, messages aren’t uniformly random. The contents of a message rely heavily on the context in which they are sent. For example, if an encrypted message represents a vote for 1 of 8 candidates, there are only 8 possible values the message can be.</p><p>If an attacker were to guess correctly the contents of an encrypted message, it would enable them to retrieve the underlying hash and as a result to decrypt every other encrypted message that shared the same hash.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/573/1*b7c75ufHPXVkrZbz72meaA.png" /><figcaption>Diagram illustrating same-nonce attack</figcaption></figure><p>That’s why it’s crucial each encryption that is done with the same key, uses a unique nonce. A unique nonce ensures that no duplicate hashes are derived from a single key.</p><h3>Encrypting And Decrypting Longer Messages</h3><p>By default, Circom uses the BN254 curve. Under this curve, a single signal can consistently carry up to 253-bits of information. But what if we want to send messages that have more than 253-bits of information?</p><p>Representing longer messages requires using a combination of multiple signals, and encrypting each signal independently with the method above is not ideal. That would require generating a unique nonce for each signal and because nonces are public it adds overhead to the verification process.</p><p>Instead, we’ll use Poseidon in <em>sponge mode</em>. We begin by setting the initial input of the Poseidon function. Then, the Poseidon function is applied numerous times, each iteration using the last’s iteration output as its input. In a process known as “absorption”, we also integrate parts of the message into the input of each iteration. Iterations are performed until no parts of the message are left to be absorbed.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*K-Bwuzudf1XxKk3NVCx2Ng.png" /><figcaption>Diagram illustrating the usage of Poseidon in sponge mode</figcaption></figure><p>Absorption is done for security reasons, which are out of scope for this article.</p><p>Using Poseidon in sponge mode, we are able to generate hashes for each signal that comprises our message and by that solve the need for multiple nonces.</p><p>Additionally, you’ll notice that the encrypted message is one signal longer than the original one. This is because the last signal serves as validation that the message wasn’t altered.</p><p>Another change you’ll notice is that each Poseidon iteration produces a hash 2 signals in length ( out[0] and out[1]). This is because it’s important that some parts of the hash aren’t used for masking in the encrypted message but do affect the hashes of subsequent iterations.</p><pre>include &quot;circomlib/circuits/poseidon.circom&quot;;<br><br>template SymmetricEncrypt(length) {<br>    signal input message[length];  // private<br>    signal input key;  // private<br>    signal input nonce;  // public<br><br>    signal output encrypteMessage[length + 1];<br><br>    component hashers[length + 1];<br>    hashers[0] = PoseidonEx(2, 2);<br>    hashers[0].initialState &lt;== 0;<br>    hashers[0].inputs[0] &lt;== key;<br>    hashers[0].inputs[1] &lt;== nonce;<br><br>    for (var i = 0; i &lt; length; i++) {<br>        encryptedMessage[i] &lt;== hashers[i].out[1] + message[i];<br>        hashers[i + 1] = PoseidonEx(1, 2);<br>        hashers[i + 1].initialState &lt;== hashers[i].out[0];<br>        hashers[i + 1].inputs[0] &lt;== encryptedMessage[i];<br>    }<br><br>    encryptedMessage[length] &lt;== hashers[length].out[1];<br>}</pre><p>The decryption process is very similar — using the key and the nonce, the receiver starts by generating the same initial hash, and as they unmask the contents of the message they feed it into the sponge and generate the rest of the appropriate hashes needed to unmask the contents of the message:</p><pre>include &quot;circomlib/circuits/poseidon.circom&quot;;<br><br>template SymmetricEncrypt(length) {<br>    signal input encryptedMessage[length + 1];  // public<br>    signal input key;  // private<br>    signal input nonce;  // public<br><br>    signal output message[length];<br><br>    component hashers[length + 1];<br>    hashers[0] = PoseidonEx(2, 2);<br>    hashers[0].initialState &lt;== 0;<br>    hashers[0].inputs[0] &lt;== key;<br>    hashers[0].inputs[1] &lt;== nonce;<br><br>    for (var i = 0; i &lt; length; i++) {<br>        message[i] &lt;== encryptedMessage[i] - hashers[i].out[1];<br>        hashers[i + 1] = PoseidonEx(1, 2);<br>        hashers[i + 1].initialState &lt;== hashers[i].out[0];<br>        hashers[i + 1].inputs[0] &lt;== encryptedMessage[i];<br>    }<br><br>    encryptedMessage[length] === hashers[length].out[1];<br>}</pre><h3>How Can Two Parties Generate A Shared Key?</h3><p>I won’t go over this myself because there are many good resources already online, but one way to achieve a shared key between two parties is using an Elliptic Curve Diffie-Hellman (ECDH) key exchange. Here is a page explaining how it works:</p><p><a href="https://cryptobook.nakov.com/asymmetric-key-ciphers/ecdh-key-exchange">https://cryptobook.nakov.com/asymmetric-key-ciphers/ecdh-key-exchange</a></p><h3>Implementations</h3><p>Sadly, I found only one Circom implementation of this scheme:</p><p><a href="https://github.com/Shigoto-dev19/poseidon-encryption-circom2/tree/master-circom2">GitHub - Shigoto-dev19/poseidon-encryption-circom2: Poseidon encryption in Typescript and circom2 (decryption only) on the BN254 elliptic curve</a></p><p>This is a fork of a different implementation that was made for Circom 1.0, re-modified to work in Circom 2.0.</p><p>The paper the implementation is based upon:</p><p><a href="https://web.archive.org/web/20221114185153/https://dusk.network/uploads/Encryption-with-Poseidon.pdf">https://web.archive.org/web/20221114185153/https://dusk.network/uploads/Encryption-with-Poseidon.pdf</a></p><p>It should be noted that there are a couple of differences between my example implementation and this implementation:</p><ul><li>Keys are 2 signals long, not 1 (They are used to represent a point on an elliptic curve)</li><li>The length of the message is incorporated into the initial hash</li><li>A single Poseidon iteration generates a hash 4 signals long, not 2 — which allows to mask 3 signals of information in a single iteration</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=53137de2a011" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Graceful Tree Labeling In Circom: A Look Into A Circom Circuit]]></title>
            <link>https://rubydusa.medium.com/graceful-tree-labeling-in-circom-a-look-into-a-circom-circuit-89eccec31f61?source=rss-ba8923eeaab------2</link>
            <guid isPermaLink="false">https://medium.com/p/89eccec31f61</guid>
            <category><![CDATA[ethereum]]></category>
            <category><![CDATA[blockchain]]></category>
            <category><![CDATA[circom]]></category>
            <category><![CDATA[zero-knowledge-proofs]]></category>
            <category><![CDATA[zksnark]]></category>
            <dc:creator><![CDATA[Rubydusa]]></dc:creator>
            <pubDate>Thu, 15 Jun 2023 13:58:59 GMT</pubDate>
            <atom:updated>2023-06-15T13:58:59.927Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Rqygf8jsFG5BmjbE_2VHGA.jpeg" /></figure><p>Github: <a href="https://github.com/rubydusa/circom-graceful-labeling">https://github.com/rubydusa/circom-graceful-labeling</a></p><p>When I first heard about zkSNARKs I was fascinated. I couldn’t stop reading about them and trying to understand how they work. There is something so poetic about the idea of proving a statement while withholding evidence.</p><p>A lot of projects written in Circom are hard to understand for beginners, especially for people with no academic background in cryptography. As a part of a bigger project I’m working on, I wrote a circuit to create proofs of solutions to the Graceful Tree Labeling problem — and I believe I can share a couple of interesting techniques and insights from it.</p><p>Even though the name of the Graceful Tree Labeling problem sounds intimidating, I promise it’s really easy to understand. Contrary to many other Circom projects, this problem has <strong>nothing to do with cryptography.</strong></p><p>I will not cover the basics of Circom in this article, it’s expected the reader has a basic understanding of how Circom works.</p><h3>What Is The Graceful Tree Labeling Problem?</h3><p>A quick overview of graph theory terminology:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/914/1*_fB8VIV8bX1loNNyyAoqrg.png" /></figure><p>The Graceful Labeling Problem is a math problem in graph theory, where each node is labeled with a unique number such that the difference between every two nodes is unique. The labels of the nodes range between 0 and the number of edges.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/804/1*QjWY9qI_WAHFEIrQcrbIBA.png" /></figure><p>As you can see above, the range of the nodes’ labels is from 0 to 5 (the number of edges), each node is unique and so is each edge.</p><p>Not every graph can be labeled gracefully. A graph that can, is said to be graceful.</p><p>There exists a conjecture in graph theory that states that every tree graph is graceful. A ‘tree’ just means a graph that has no cycles. You can visualize any such graph as a ‘tree’ by choosing an arbitrary node to be the ‘root’, and display its connections below it in a recursive manner.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*N5mh4cx4M41RhShYHJzqMg.png" /></figure><p>At last, here is an example of a graceful labeling for a tree:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/444/1*hNzA3arQ_AUHQniqeKFvVg.png" /></figure><p>In short, the Graceful Tree Labeling problem is solving the Graceful Labeling problem for tree graphs.</p><h3>Approaching The Problem In Circom</h3><p>We will not write a Circom circuit to solve the problem for a given graph, but rather we will write a circuit to verify that a given solution is correct. Then, we will be able to create Zero-Knowledge-Proofs that we know a labeling solution to a certain tree.</p><p>In general, it’s a good rule of thumb that Circom code should <em>verify</em>, not <em>compute</em>.</p><p>To verify a given solution we need to check in an input tree that:</p><ol><li>Each label is unique</li><li>Each label is between 0 and the number of edges</li><li>Each edge is unique</li></ol><p>If we can show that all these requirements are met, we have proved a tree to be graceful.</p><p>There remains one obvious problem: <strong>How do we represent trees in Circom?</strong></p><p>Circom is very primitive and low-level. There are no data types, only numbers (field elements). Because of this, we need to get creative when representing more complex data types.</p><h4>How To Represent A Tree</h4><p>This was a really fun exercise for me personally. I’d recommend you give it a try to come up yourself with a way to represent trees.</p><p>If you didn’t succeed / can’t bother, I present you with my solution:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/577/1*UjAtODeGlgnwhA09YvxaqA.png" /></figure><p>We imagine the tree (with N nodes) consists of indexed nodes from 0 to N — 1. We represent the tree using an array of length N — 1, where the array describes the structure of the tree by describing for each node who’s their parent.</p><p>The node indexed 0 is always the root node, which means it has no parents. The value at index 0 of the array is the parent of the node indexed 1, the value at index 1 of the array is the parent of the node indexed 2, and so on.</p><p>Example: If at index 5 is the number 4, it means the parent of the node indexed with the number 6 (5 + 1) is the node indexed 4.</p><p>In order to avoid cycles (two nodes or more having each other as their parents) the index of a parent node must be lower than the index of the child node.</p><p>That way, no two nodes can point at each other because at least one of them has a smaller index and can’t point to a parent with a higher index than theirs.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/963/1*zv_qLkweVYdpd6NoOfRMpA.png" /></figure><p>Finally, in order to label the nodes we simply have an additional array of length N such that the i’th value is the labeling of the i’th node.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/579/1*YpGipmaa7AjPh6Huh2Padg.png" /></figure><p><em>Note: This way of representing trees is not one-to-one — there are multiple ways to represent the same tree with different arrays.</em></p><h4>How To Index An Array Using A Signal</h4><p>In order to compute the value of an edge, you need to access the labels of the two nodes at the ends of the edge.</p><p>The indices of the nodes depend on the structure of the tree, and this raises a problem: You can’t index an array with dynamic input. Well, at least not directly.</p><p>This is a really common problem and it has a really simple solution: in order to index using a signal you sum up the elements of the array, but before adding them together you multiply each of them by whether their index is equal to the index signal.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/943/1*4B0E_CdVwVPpKJDUSDKd3A.png" /></figure><pre>// sum in[N]<br>template CalculateTotal(N) {<br>    signal input in[N];<br>    signal output out;<br><br>    signal outs[N];<br>    outs[0] &lt;== in[0];<br><br>    for (var i=1; i &lt; N; i++) {<br>        outs[i] &lt;== outs[i - 1] + in[i];<br>    }<br><br>    out &lt;== outs[N - 1];<br>}<br><br>// array[index] when index is a signal<br>// O(n) because of how circuits work<br>template AtIndex(N) {<br>    signal input array[N];<br>    signal input index;<br><br>    signal output out;<br><br>    component result = CalculateTotal(N);<br>    for (var i = 0; i &lt; N; i++) {<br>        var isEqual = IsEqual()([i, index]);  // IsEqual() taken from circomlib<br>        result.in[i] &lt;== isEqual * array[i];<br>    }<br><br>    out &lt;== result.out;<br>}</pre><h4>We’re (Almost) Done!</h4><p>Once we know how to represent our data and access it, all that’s left to do is putting it all together.</p><p>I won’t go over all of the code because that would be boring, but if you’re curious you may head over to the Github repository and take a look for yourself: <a href="https://github.com/rubydusa/circom-graceful-labeling">https://github.com/rubydusa/circom-graceful-labeling</a></p><h4>Optimizing Using Bit Manipulation</h4><p>One of the benefits of zkSNARKs is that their verification is constant-time*.</p><p>Well, not exactly.</p><p>There is overhead (especially with on-chain verification) caused by the amount of public inputs a circuit has. The more public inputs it has, the more gas will be needed to verify it on-chain.</p><p>In my use-case, the public input of the circuit is the parents array, so for a tree of size N there will be N — 1 public inputs. This gets quite expensive to verify on-chain even for moderately sized trees.</p><p>One solution would be to make the parents array private, and instead create a new public signal which will be the hash of the array. This way, the verification of the structure of the tree is done elsewhere.</p><p>The above solution works depends on how you intend to use on-chain verifications and what for, and in my case it’s not really relevant.</p><p>A different way to optimize would be to use bit manipulation, and store multiple numbers within a single number. In our code, we’ll unpack the packed input into an array that consists of the numbers we actually care about:</p><pre>// amount of bits needed to represent n<br>function bits(n) {<br>    var result = 1;<br>    while (n &gt; 1) {<br>        n \= 2;<br>        result++;<br>    }<br>    return result;<br>}<br><br>// amount of bits needed to represent N numbers with max value of MAX_VALUE<br>function bit_usage(MAX_VALUE, AMOUNT) {<br>    var b = bits(MAX_VALUE);<br><br>    return b * AMOUNT;<br>}<br><br>// amount of signals needed to represent N numbers with max value of MAX_VALUE<br>function bit_usage_signals(MAX_VALUE, AMOUNT) {<br>    var b = bits(MAX_VALUE);<br><br>    return ((b * AMOUNT) \ 254) + 1;<br>}<br><br>// take in the minimal amount <br>template FromCompressed(MAX_VALUE, AMOUNT) {<br>    var bitsOfV = bits(MAX_VALUE);<br>    var bitsNeeded = bit_usage(MAX_VALUE, AMOUNT);<br>    var signalsNeeded = bit_usage_signals(MAX_VALUE, AMOUNT);<br><br>    signal input in[signalsNeeded];<br>    signal output out[AMOUNT];<br><br>    component asBits[signalsNeeded];<br><br>    for (var i = 0; i &lt; signalsNeeded; i++) {<br>        var curBits = i == signalsNeeded - 1 ? bitsNeeded % 254 : 254;<br><br>        asBits[i] = Num2Bits(curBits);  // Num2Bits() taken from circomlib<br>        asBits[i].in &lt;== in[i];<br>    }<br><br>    var values[AMOUNT];<br>    for (var i = 0; i &lt; AMOUNT; i++) {<br>        values[i] = 0;<br>    }<br><br>    for (var i = 0; i &lt; bitsNeeded; i++) {<br>        var valuesIndex = i \ bitsOfV;<br>        var asBitsIndex = i \ 254;<br><br>        var currentPower = 2 ** (i % bitsOfV);<br><br>        values[valuesIndex] += currentPower * asBits[asBitsIndex].out[i % 254];<br>    }<br><br>    for (var i = 0; i &lt; AMOUNT; i++) {<br>        out[i] &lt;== values[i];<br>    }<br>}</pre><p>This adds additional constraints to our circuit, but it decreases significantly the amount of public inputs.</p><h3>Conclusions</h3><p>This was a fun project for me, and I hope you learned something and/or get inspired.</p><p>The source code is available at the top of the article.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=89eccec31f61" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Paper.js: How To Add ECMAScript 6 Support For Local Projects]]></title>
            <link>https://rubydusa.medium.com/paper-js-how-to-add-ecmascript-6-support-for-local-projects-7ddcfd057cd8?source=rss-ba8923eeaab------2</link>
            <guid isPermaLink="false">https://medium.com/p/7ddcfd057cd8</guid>
            <category><![CDATA[es6]]></category>
            <category><![CDATA[svg]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[paperjs]]></category>
            <category><![CDATA[generative-art]]></category>
            <dc:creator><![CDATA[Rubydusa]]></dc:creator>
            <pubDate>Sun, 11 Jun 2023 21:25:54 GMT</pubDate>
            <atom:updated>2023-06-11T21:25:54.265Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mfoi4TTDvUxU9q2rBI6dNA.jpeg" /></figure><p>Depending on how you install paper.js locally, ES6 syntax and features may not work properly. You may experience Uncaught SyntaxError: Unexpected token if you use the keywords const and let, or use arrow functions and so on.</p><h3>How To Fix This</h3><p>Assuming you already have an HTML file, add this script tag:</p><pre>&lt;script src=&#39;https://cdnjs.cloudflare.com/ajax/libs/acorn/8.8.2/acorn.js&#39;&gt;&lt;/script&gt;</pre><p>Full example of usage:</p><pre>&lt;!DOCTYPE html&gt;<br>&lt;html&gt;<br>    &lt;head&gt;<br>        &lt;&lt;script type=&quot;text/javascript&quot; src=&quot;js/paper.js&quot;&gt;&lt;/script&gt;<br>        &lt;script src=&#39;https://cdnjs.cloudflare.com/ajax/libs/acorn/8.8.2/acorn.js&#39;&gt;&lt;/script&gt;<br>        &lt;script type=&quot;text/paperscript&quot; canvas=&quot;canvas&quot;&gt;<br>            const path = new Path.Circle({<br>                center: new Point(0, 0),<br>                radius: 100,<br>                fillColor: &#39;black&#39;<br>            });<br>        &lt;/script&gt;<br>    &lt;/head&gt;<br>    &lt;body&gt;<br>        &lt;canvas id=&quot;canvas&quot; resize&gt;&lt;/canvas&gt;<br>    &lt;/body&gt;<br>&lt;/html&gt;</pre><p>Also, if you don’t want to depend on cloudflare you can also install acorn locally and import it that way. At the time of writing the latest version is 8.8.2 but that might change by the time you read this.</p><h3>Why Does This Happen</h3><p>paper.js is quite an old library, and it was created before ES6 existed. It depends on a library called acron to parse JavaScript within JavaScript, and the version of acorn used in paper.js releases does not support ES6.</p><p>If paper.js detects there is a different version of acorn loaded, it will use it instead.</p><h3>Related Issues On Github</h3><ul><li><a href="https://github.com/paperjs/paper.js/issues/1183">Support ES 2015 in Paperscript · Issue #1183 · paperjs/paper.js</a></li><li><a href="https://github.com/paperjs/paper.js/issues/1357">Seems like generator functions are not supported in paperscript · Issue #1357 · paperjs/paper.js</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7ddcfd057cd8" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Advice on Creating On-Chain Generated NFT Art]]></title>
            <link>https://rubydusa.medium.com/advice-on-creating-on-chain-generated-nft-art-6ea2ac79d7cf?source=rss-ba8923eeaab------2</link>
            <guid isPermaLink="false">https://medium.com/p/6ea2ac79d7cf</guid>
            <category><![CDATA[blockchain]]></category>
            <category><![CDATA[generative-art]]></category>
            <category><![CDATA[cryptocurrency]]></category>
            <category><![CDATA[onchain]]></category>
            <category><![CDATA[nft]]></category>
            <dc:creator><![CDATA[Rubydusa]]></dc:creator>
            <pubDate>Mon, 24 Apr 2023 20:33:28 GMT</pubDate>
            <atom:updated>2023-04-24T20:33:28.496Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/880/1*c_zQ7EngqIHikEuUgN3GFg.png" /><figcaption>(not full snippet, obviously. check out my project: <a href="https://github.com/rubydusa/proof-of-life">https://github.com/rubydusa/proof-of-life</a>)</figcaption></figure><p>When I tried to dive in On-Chain Generated Art with no prior experience I made plentiful crucial mistakes, and as a result I’ve wasted precious time I could have saved if only I’ve known better from the start</p><p>Hopefully this will serve as a helpful guide for anyone wanting to experiment with on-chain generated art. This article focuses specifically on <strong>EVM-based chains</strong>, however, some ideas are more general and may apply to other blockchains as well</p><h3>Introduction</h3><p>Why generate the art on-chain at all? There are three main reasons:</p><ol><li>Dynamic Content — Unlike static images and videos, generated art can read data from the blockchain and shape itself accordingly</li><li>Censorship Resilience — Centralized hosting of media is unreliable in the light of censorship and goes against the spirit of decentralization. IPFS hosting and other decentralized hosting methods are also partially prone to censorship, and also are unreliable if there are only a few nodes storing the media</li><li>Composability — If done correctly, on-chain generated art can be composed with one another</li></ol><p>However, blockchains are usually not designed in mind to handle generative art. One of the biggest factors is the gas cost of deploying on-chain art, so if you are planning on deploying on a chain like Ethereum, expect to pay hundreds of dollars in gas costs.</p><p>The main concept of how to do generative <em>NFT</em> art lies in <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs"><strong>Data URLs</strong></a><strong> — </strong>Data URLs allow you to pass images and other forms of media in the URL itself. NFT Metadata Standards support the usage of data URLs in token metadata so your NFT tokens will render on OpenSea and other major NFT marketplaces.</p><h4>Helpful Sources</h4><p><a href="https://docs.opensea.io/docs/metadata-standards">Opensea Metadata Standards </a>— Opensea’s Metadata Standard. You should also refer to the <a href="https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md">official EIP-721 metadata specification</a></p><p><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs">Data URLs</a> — The official Mozilla docs on data URLs and how they work</p><p><a href="https://medium.com/@relicprotocol/fully-on-chain-nfts-c8ee6a358236">Relic Protocol — Fully On-Chain NFTs </a>(<a href="https://medium.com/u/d73b62156f13">Relic Protocol</a>)— This is a great article by Relic Protocol that includes optimization tips for storing images on-chain and insight to the benefits of on-chain art</p><p><a href="https://twitter.com/xtremetom/status/1600542212735090711">On-Chain generative 3D roller coaster</a> (<a href="https://medium.com/u/f8fef5ff64a6">xtremetom</a>) — Cool twitter thread with great tips and tools for on-chain generative art</p><p><a href="https://github.com/intartnft/scripty.sol">Scripty.sol</a> (<a href="https://twitter.com/0xthedude">@0xthedude</a> and <a href="https://medium.com/u/f8fef5ff64a6">xtremetom</a>) — Gas-efficient HTML builder that can combine multiple JS and a data storage solution that allows on-chain composable generative art (description copied from repo)</p><p><a href="https://www.npmjs.com/package/fully-on-chain.sol">fully-on-chain.sol</a> (<a href="https://twitter.com/nounsfes">@nounsfes</a>) — A collection of solidity libraries to generate SVGs on-chain</p><h3>The Formats You Can Use</h3><p>The two main formats of media you can use are HTML and SVG. Both are tag languages which makes it easy to create HTML and SVG files programatically.</p><p>These two formats are not exclusive — you can embed HTML into SVG files using foreignObject (always make sure to check support for intended platforms) and SVG elements work directly inside an HTML file.</p><p>SVGs are slightly easier to compose with themselves than HTMLs since laying out elements is more straightforward and HTML based art may not rely on elements at all and use exclusively the canvas element.</p><p>HTML is by far more expressive and gives you far greater artistic freedom:</p><ul><li>Interactivity is easy to achieve with Javascript compared to CSS</li><li>You can use existing Javascript graphics libraries</li><li>Custom art generation logic by utilizing the Canvas API</li></ul><p>However, if your content is generative but not animated or interactive it may be preferable to use SVGs for the sake of simplicity, and also because rendering batches of SVGs is more efficient than of HTMLs</p><h3>Gas Usage In View Functions Still Matters!</h3><p>You may think that because on-chain art relies on view functions it doesn’t matter how much gas do you use to generate your art. Well, it does.</p><p>Many RPC providers will block view calls that exceed the gas limit and for a good reason: High gas usage in a view call may indicate a high amount of reads to the state of blockchain — which is expensive. Even if the executed bytecode is mostly arithmetic, an RPC server has no way to know.</p><p>With that said, <strong>don’t optimize prematurely</strong>. Start by writing your code as you normally would, but beware the possibility of getting blocked.</p><p>The main gas pitfalls in on-chain generative art have to do with string manipulation, and it’s really easy to accidentally write inefficient code because Solidity was not designed in mind for cheap string manipulation.</p><p>Here is a comparison between three functions whose purpose is to produce a string that represents a rectangle element with data inside of it:</p><pre>// SPDX-License-Identifier: MIT<br>pragma solidity ^0.8.0;<br><br>import &quot;@openzeppelin/contracts/utils/Strings.sol&quot;;<br><br>contract Test {<br>  using Strings for uint256;<br><br>  bytes constant START_STRING = &quot;&lt;rect&gt;&quot;;<br>  bytes constant END_STRING = &quot;&lt;/rect&gt;&quot;;<br><br>  // 3744 gas<br>  function svg0(uint256 data) external pure returns (bytes memory) {<br>    return bytes.concat(<br>      START_STRING,<br>      bytes(data.toString()),<br>      END_STRING<br>    );<br>  }<br><br>  // 3748 gas<br>  function svg1(uint256 data) external pure returns (bytes memory) {<br>    bytes memory start = &quot;&lt;rect&gt;&quot;;<br>    bytes memory end = &quot;&lt;/rect&gt;&quot;;<br><br>    return bytes.concat(<br>      start,<br>      bytes(data.toString()),<br>      end<br>    );<br>  }<br><br>  bytes6 constant START = &quot;&lt;rect&gt;&quot;;<br>  bytes7 constant END = &quot;&lt;/rect&gt;&quot;;<br><br>  // 3220 gas<br>  function svg2(uint256 data) external pure returns (bytes memory) {<br>    return bytes.concat(<br>      START,<br>      bytes(data.toString()),<br>      END<br>    );<br>  }<br>}</pre><p>Slightly more than 500 gas units saved and all these functions do is wrap a number around a rect tag.</p><p>The difference is that the first two functions load the strings into memory and then create a copy of each. Because in the last function a string variable is never declared, the string data is put in its destination directly, without making a copy.</p><p>Another tip is that for large constant strings, you should look into saving the string on-chain as the bytecode of a contract and use EXTCODECOPY to retrieve it (Make sure the first byte in your contract’s source code is 0 to ensure it cannot be ran and accidentally be self-destructed)</p><p>In general, if you end up having issues with gas you should research more in depth how to optimize around string manipulation. There’s also a chance you’ll never have to deal with optimization.</p><h3>Use URL character encoding!</h3><p>Some characters that are often used in HTML and SVG files (such as &lt;, &gt;, “) are invalid URL characters.</p><p>A way to deal with this is to encode the files in base64, but the problem is that for large files they significantly bring up the gas cost.</p><p>More so, in certain cases you might need to do base64 conversion multiple times (e.g. if you want the function to return JSON metadata, you’ll need the convert once the SVG/HTML itself to base64 encoding, and then the whole JSON to base64 encoding)</p><p>The solution is to use <a href="https://developer.mozilla.org/en-US/docs/Glossary/percent-encoding">URL encoding</a>, which is a way to encode special characters inside a URL. That way, all strings are valid and composable with each other easily.</p><h3>Rendering The Art In A Website</h3><p>This is probably obvious to people familiar with front-end development, but the ideal way to render the art in a website is to use an iframe tag.</p><p>The reason I’m mentioning this at all is because in my personal project I used plain SVG generation, and I tried various ways to render the art on the front-end and all of them caused issues.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6ea2ac79d7cf" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How To Build A React App With SnarkJS]]></title>
            <link>https://rubydusa.medium.com/how-to-build-a-react-app-with-snarkjs-fe3a3aa03da8?source=rss-ba8923eeaab------2</link>
            <guid isPermaLink="false">https://medium.com/p/fe3a3aa03da8</guid>
            <category><![CDATA[cryptocurrency]]></category>
            <category><![CDATA[zero-knowledge-proofs]]></category>
            <category><![CDATA[ethereum]]></category>
            <category><![CDATA[circom]]></category>
            <dc:creator><![CDATA[Rubydusa]]></dc:creator>
            <pubDate>Sun, 30 Oct 2022 22:16:07 GMT</pubDate>
            <atom:updated>2022-10-30T22:16:07.461Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*udnuI6Bud_LmcwJPu4Txhg.png" /><figcaption>Background Attribution: <a href="https://www.vecteezy.com/members/via-faisal460653">Faisal Ical</a></figcaption></figure><p>In this short tutorial I will demonstrate how to import and use SnarkJS in a react project, prior knowledge of circom and react are expected.</p><p>Make sure to check the official docs on the matter first: <a href="https://github.com/iden3/snarkjs#in-the-browser">https://github.com/iden3/snarkjs#in-the-browser</a></p><p>Developers with experience with react could probably put the pieces together from the docs, but personally it took me quite a while to figure it out so I hope this tutorial reaches other newbies like me.</p><h3>Importing SnarkJS</h3><ol><li>Install SnarkJS through npm:</li></ol><pre>npm i snarkjs</pre><p>2. Copy snarkjs.min.js to the public folder</p><pre>// in the root directory</pre><pre>cp node_modules/snarkjs/build/snarkjs.min.js public/</pre><p>3. Add this script tag to public/index.html</p><pre>&lt;head&gt;<br>  ...<br>  &lt;script src=&quot;/snarkjs.min.js&quot;&gt;&lt;/script&gt;<br>  ...<br>&lt;/head&gt;</pre><h3>Using SnarkJS</h3><ol><li>Load SnarkJS in App.js (or any other react component)</li></ol><pre>const snarkjs = window.snarkjs;</pre><p>2. You’re done! You may now use SnarkJS as you would in node.</p><pre>function App() {<br>  return (<br>    &lt;div&gt;<br>      &lt;button onClick={async () =&gt; <br>        const {proof, publicSignals} = await snarkjs.groth16.fullProve({ a: 11, b: 3 }, &quot;circuit.wasm&quot;, &quot;circuit.zkey&quot;)<br>        console.log({ proof, publicSignals });}&gt;<br>      &lt;/button&gt;<br>    &lt;/div&gt;<br>  )<br>}</pre><p>Notice that wasm and zkey files should be placed under the public directory (e.g. public/circuit.wasmand public/circuit.zkey)</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fe3a3aa03da8" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Proof of Life: Zero-Knowledge-Proof Implementation of Conway’s Game of Life with Circom and…]]></title>
            <link>https://medium.com/coinmonks/proof-of-life-zero-knowledge-proof-implementation-of-conways-game-of-life-with-circom-and-6438521fb2b1?source=rss-ba8923eeaab------2</link>
            <guid isPermaLink="false">https://medium.com/p/6438521fb2b1</guid>
            <category><![CDATA[zero-knowledge-proofs]]></category>
            <category><![CDATA[zksnark]]></category>
            <category><![CDATA[solidity]]></category>
            <category><![CDATA[blockchain]]></category>
            <dc:creator><![CDATA[Rubydusa]]></dc:creator>
            <pubDate>Wed, 12 Oct 2022 20:15:09 GMT</pubDate>
            <atom:updated>2022-10-20T01:50:39.882Z</atom:updated>
            <content:encoded><![CDATA[<h3>Proof of Life: Zero-Knowledge-Proof Implementation of Conway’s Game of Life with Circom and Solidity</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/681/1*3IMHgZVO1oTRZrBlRJPxXQ.png" /></figure><p>Recently there is a rise in discussion about the potentials of ZKP — Zero Knowledge Proofs in crypto. Jumping on the bandwagon, I decided to learn circom and try to implement Conway’s Game of Life as a zkSNARK circuit, as well as use the circuit in an NFT smart contract.</p><p>In this post I will write briefly about the technologies in use, present my circuit, and explain how it works.</p><p>You can view the code here: <a href="https://github.com/rubydusa/proof-of-life">https://github.com/rubydusa/proof-of-life</a></p><h3>Brief Overview of ZKP, zkSNARK and Circom</h3><p>For the purposes of this post, a Zero-Knowledge-Proof means a mathematical proof that a piece of data upholds a set of constraints, without revealing what that piece of data is, and without revealing anything else about it.</p><p>a zkSNARK — <em>Zero Knowledge Succinct Non-Interactive Argument of Knowledge </em>is a type of a Zero Knowledge Proof that requires a single interaction between the prover and verifier, and is short.</p><p>Circom is a programming language to write programs (called circuits) that take a set of inputs, and describe <em>constraints</em> about them, as well as having one, many or no outputs.</p><p>Using these constraints, you can generate <em>proofs</em> that you know a set of inputs that uphold the constraints as well as verify proofs.</p><h3>What is Being Built?</h3><p>In this post I will show an NFT contract that mints a token anytime someone provides a proof that they have a solution to the following problem:</p><p>Find a Game of Life grid state such that after N iterations, the grid state matches some predefined state:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/442/1*94zBJJMI2uJICPPQgC2cMA.png" /></figure><p>For a finite grid state of life, I’ll be using the wrapped variant:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/720/1*qwlr4Q3OJ8JYUQysn1zI2A.png" /></figure><p>To clarify, whenever I say “solution” I refer to grid states that apply to the problem aforementioned .</p><p>Duplicate Solutions are not allowed.</p><p>ZKPs allow us to keep the solutions private after submission, prevent frontrunning, and create large puzzles of this sort.</p><h3>The Details</h3><p>This section will cover implementation details and explain the architecture of the project:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/720/1*lRrQXKoTiCEFbupQ32jgbA.png" /></figure><h4>The Circuit</h4><p>The circuit is parameterized over N — number of iterations, W — grid width, H — grid height.</p><p>It has three outputs:</p><ul><li>out — the state of the grid after N iterations</li><li>solutionHash — the hash of “out”, required so the contract can verify it is not a duplicate solution</li><li>hash — the hash of “address” combined with “solutionHash”, required in order to prevent frontrunning (more on that later)</li></ul><p>And two inputs:</p><ul><li>address (public) — the address of the prover</li><li>data (private) — the solution to “out”</li></ul><p>The grid state is represented as a single* 254 bit-number, and broken down to a W×H bit matrix. Then, the life algorithm is applied N times and the output is constrained to be whatever happens to be the result.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/421/1*GVrT36UpmTY3wSz8u93daA.png" /></figure><p>*In the actual circuit it is extended to support grids that have more than 254 cells by patching together the bits of a sequence of 254 bit-numbers</p><h4>The Verifier Contract</h4><p>In order to verify proofs on-chain, we need to compile our circuits into solidity contract templates. These contracts have a single public function that take proof data as an input, and return a boolean value indicating whether the proof is valid or not.</p><p>In the case of the groth16 protocol, the function signature looks like this:</p><pre>function verifyProof( <br>    uint[2] memory a,</pre><pre>    uint[2][2] memory b,</pre><pre>    uint[2] memory c,</pre><pre>    uint[4] memory input</pre><pre>) public view returns (bool r)</pre><p>Since I’m not a cryptographer I’m not able to explain the details of a, b, and c — think of them as “the private part” of the proof, and the input parameter consists of the public inputs and outputs of the circuit.</p><p>Note that outputs are also part of the verification input, as the contract is only responsible for validating a proof, not computing it.</p><h4>The NFT Contract</h4><p>The NFT contract inherits from ERC721Enumerable, and has these additional storage variables:</p><pre>uint256 public prizenum;</pre><pre>mapping(uint256 =&gt; bool) public proofs;</pre><pre>IGroth16Verifier public verifier;</pre><ul><li>prizenum — The sought-after grid state</li><li>proofs — A mapping to keep track of already submitted solutions</li><li>verifier — The address of the verifier contract</li></ul><p>And it adds an external function “mint” that mints a token when provided with a unique proof:</p><pre>function mint(</pre><pre>uint256 solutionHash,</pre><pre>uint256 hash,</pre><pre>uint256[2] memory a,</pre><pre>uint256[2][2] memory b,</pre><pre>uint256[2] memory c</pre><pre>) external {</pre><pre>require(!proofs[solutionHash], &quot;GOLNFT: Solution already exists!&quot;);</pre><pre>require(verifier.verifyProof(a, b, c, [</pre><pre>solutionHash,</pre><pre>hash,</pre><pre>prizenum,</pre><pre>uint256(uint160(msg.sender))</pre><pre>]), &quot;GOLNFT: Invalid proof&quot;);</pre><pre>_safeMint(msg.sender, totalSupply());</pre><pre>proofs[solutionHash] = true;</pre><pre>}</pre><h3>How do Zero-Knowledge-Proofs Prevent Frontrunning?</h3><p>If we were to not care about keeping the solutions private, we could have simply simulated the execution of life inside a solidity smart contract and it’d be much less burden.</p><p>So why use ZKPs in the first place?</p><p>The problem with the aforementioned approach is that malicious actors could steal solutions to the puzzle by listening to broadcast transactions. A malicious actor would simply copy the calldata, submit a transaction with a higher gas fee and reap the rewards:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/391/1*vF4p2ZVg4vFEAL8hnFJWFw.png" /></figure><p>A hash commitment scheme where a user commits to a hash of a solution before submitting it, and gets punished if he fails to submit the solution within some time-frame would be better, but has major flaws:</p><ol><li>If the cost of commitment is too high it would prevent users with insufficient capital from participating</li><li>If the cost of commitment is too low, malicious actors can front-run commitments and intentionally lose money if it’s beneficial for them to gatekeep tokens</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/821/1*NiSaNMjg7gaiqXBMx2lWbA.png" /></figure><p>We are able to prevent this by requiring the transaction caller to provide the hash of his address combined with his solution. The verfier contract is able to verify that the public address and the private solution match the provided hash.</p><p>Now, if a frontrunner tries to steal a solution, him calling the mint function with the same inputs would fail since his address won’t match the hash.</p><h3>How do Zero-Knowledge-Proofs Achieve Scalability?</h3><p>If we even disregard frontrunning, there is another major benefit of using ZKPs: scalability. Verification is constant time and as a result the gas used in the verifier contract is bounded. Alternatively, implementing life in solidity for large grids with a large number of iterations would probably consume the gas limit many times over.</p><p>The downside is that groth16, the zkSNARK protocol in use requires a circuit-specific trusted setup — which means that in order to deploy the application into some chain you’ll need to conduct a trusted setup ceremony beforehand.</p><p>A trusted setup is an essential source of randomness used in the proof generation and verification processes and if it’s created by a single party, that party is able to forge false proofs, unless they discard the “secret key” that enables them to do so (hence trusted).</p><p>A trusted setup ceremony is a method for multiple parties to create a trusted setup such that as long as a single party is honest (discards their part of the “secret key”) the trusted setup is secure.</p><h3>Stay Tuned!</h3><p>I’m planning on creating a react site for demonstration purposes. If you have any questions about the project feel free to ask and contact me at <a href="https://twitter.com/rubydusav">https://twitter.com/rubydusav</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=6438521fb2b1" width="1" height="1" alt=""><hr><p><a href="https://medium.com/coinmonks/proof-of-life-zero-knowledge-proof-implementation-of-conways-game-of-life-with-circom-and-6438521fb2b1">Proof of Life: Zero-Knowledge-Proof Implementation of Conway’s Game of Life with Circom and…</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[ParserError: Multiple SPDX license identifiers found in source file.]]></title>
            <link>https://rubydusa.medium.com/parsererror-multiple-spdx-license-identifiers-found-in-source-file-de4d61ffab64?source=rss-ba8923eeaab------2</link>
            <guid isPermaLink="false">https://medium.com/p/de4d61ffab64</guid>
            <category><![CDATA[ethereum]]></category>
            <category><![CDATA[blockchain]]></category>
            <category><![CDATA[cryptocurrency]]></category>
            <category><![CDATA[solidity]]></category>
            <dc:creator><![CDATA[Rubydusa]]></dc:creator>
            <pubDate>Mon, 17 Jan 2022 13:56:54 GMT</pubDate>
            <atom:updated>2022-01-17T19:47:19.744Z</atom:updated>
            <content:encoded><![CDATA[<h3>ParserError: Multiple SPDX license identifiers found in source file. Use “AND” or “OR” to combine multiple licenses — And How To Fix It (Hardhat)</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/894/1*EkCe9jDgaUyo89_8YNGB9g.png" /></figure><p>By default, The flatten feature in hardhat doesn’t solve SPDX license identifiers conflicts. In this article I’ll provide two solutions:</p><ul><li>Modifying hardhat.config.js</li><li>Script that fixes broken flattened contracts</li></ul><p>These solutions are simplified and cleaner versions of <a href="https://medium.com/u/6f3174f15a63">BoringCrypto</a>’s solution to this problem, found on <a href="https://github.com/nomiclabs/hardhat/issues/1050#issuecomment-840444798">#1050 at the hardhat github</a></p><p>These also deal with multiple experimental ABI encoders deceleration in a file</p><h3>Hardhat</h3><p>For hardhat users, this can be fixed by adding the following task to your hardhat.config.js file:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcarbon.now.sh%2Fembed%3Fbg%3Drgba%2528171%252C%2B184%252C%2B195%252C%2B1%2529%26t%3Dvscode%26wt%3Dnone%26l%3Djavascript%26ds%3Dtrue%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D56px%26ph%3D56px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D133%2525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3Dtask%2528%252522flat%252522%25252C%252520%252522Flattens%252520and%252520prints%252520contracts%252520and%252520their%252520dependencies%252520%2528Resolves%252520licenses%2529%252522%2529%25250A%252520%252520.addOptionalVariadicPositionalParam%2528%252522files%252522%25252C%252520%252522The%252520files%252520to%252520flatten%252522%25252C%252520undefined%25252C%252520types.inputFile%2529%25250A%252520%252520.setAction%2528async%252520%2528%25257B%252520files%252520%25257D%25252C%252520hre%2529%252520%25253D%25253E%252520%25257B%25250A%252520%252520%252520%252520let%252520flattened%252520%25253D%252520await%252520hre.run%2528%252522flatten%25253Aget-flattened-sources%252522%25252C%252520%25257B%252520files%252520%25257D%2529%25253B%25250A%252520%252520%252520%252520%25250A%252520%252520%252520%252520%25252F%25252F%252520Remove%252520every%252520line%252520started%252520with%252520%252522%25252F%25252F%252520SPDX-License-Identifier%25253A%252522%25250A%252520%252520%252520%252520flattened%252520%25253D%252520flattened.replace%2528%25252FSPDX-License-Identifier%25253A%25252Fgm%25252C%252520%252522License-Identifier%25253A%252522%2529%25253B%25250A%252520%252520%252520%252520flattened%252520%25253D%252520%252560%25252F%25252F%252520SPDX-License-Identifier%25253A%252520MIXED%25255Cn%25255Cn%252524%25257Bflattened%25257D%252560%25253B%25250A%25250A%252520%252520%252520%252520%25252F%25252F%252520Remove%252520every%252520line%252520started%252520with%252520%252522pragma%252520experimental%252520ABIEncoderV2%25253B%252522%252520except%252520the%252520first%252520one%25250A%252520%252520%252520%252520flattened%252520%25253D%252520flattened.replace%2528%25252Fpragma%252520experimental%252520ABIEncoderV2%25253B%25255Cn%25252Fgm%25252C%252520%2528%2528i%2529%252520%25253D%25253E%252520%2528m%2529%252520%25253D%25253E%252520%2528%2521i%25252B%25252B%252520%25253F%252520m%252520%25253A%252520%252522%252522%2529%2529%25280%2529%2529%25253B%25250A%252520%252520%252520%252520console.log%2528flattened%2529%25253B%25250A%252520%252520%25257D%2529%25253B&amp;display_name=Carbon&amp;url=https%3A%2F%2Fcarbon.now.sh%2F%3Fbg%3Drgba%252528171%25252C%2B184%25252C%2B195%25252C%2B1%252529%26t%3Dvscode%26wt%3Dnone%26l%3Djavascript%26ds%3Dtrue%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D56px%26ph%3D56px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D133%252525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3Dtask%252528%25252522flat%25252522%2525252C%25252520%25252522Flattens%25252520and%25252520prints%25252520contracts%25252520and%25252520their%25252520dependencies%25252520%252528Resolves%25252520licenses%252529%25252522%252529%2525250A%25252520%25252520.addOptionalVariadicPositionalParam%252528%25252522files%25252522%2525252C%25252520%25252522The%25252520files%25252520to%25252520flatten%25252522%2525252C%25252520undefined%2525252C%25252520types.inputFile%252529%2525250A%25252520%25252520.setAction%252528async%25252520%252528%2525257B%25252520files%25252520%2525257D%2525252C%25252520hre%252529%25252520%2525253D%2525253E%25252520%2525257B%2525250A%25252520%25252520%25252520%25252520let%25252520flattened%25252520%2525253D%25252520await%25252520hre.run%252528%25252522flatten%2525253Aget-flattened-sources%25252522%2525252C%25252520%2525257B%25252520files%25252520%2525257D%252529%2525253B%2525250A%25252520%25252520%25252520%25252520%2525250A%25252520%25252520%25252520%25252520%2525252F%2525252F%25252520Remove%25252520every%25252520line%25252520started%25252520with%25252520%25252522%2525252F%2525252F%25252520SPDX-License-Identifier%2525253A%25252522%2525250A%25252520%25252520%25252520%25252520flattened%25252520%2525253D%25252520flattened.replace%252528%2525252FSPDX-License-Identifier%2525253A%2525252Fgm%2525252C%25252520%25252522License-Identifier%2525253A%25252522%252529%2525253B%2525250A%25252520%25252520%25252520%25252520flattened%25252520%2525253D%25252520%25252560%2525252F%2525252F%25252520SPDX-License-Identifier%2525253A%25252520MIXED%2525255Cn%2525255Cn%25252524%2525257Bflattened%2525257D%25252560%2525253B%2525250A%2525250A%25252520%25252520%25252520%25252520%2525252F%2525252F%25252520Remove%25252520every%25252520line%25252520started%25252520with%25252520%25252522pragma%25252520experimental%25252520ABIEncoderV2%2525253B%25252522%25252520except%25252520the%25252520first%25252520one%2525250A%25252520%25252520%25252520%25252520flattened%25252520%2525253D%25252520flattened.replace%252528%2525252Fpragma%25252520experimental%25252520ABIEncoderV2%2525253B%2525255Cn%2525252Fgm%2525252C%25252520%252528%252528i%252529%25252520%2525253D%2525253E%25252520%252528m%252529%25252520%2525253D%2525253E%25252520%252528%252521i%2525252B%2525252B%25252520%2525253F%25252520m%25252520%2525253A%25252520%25252522%25252522%252529%252529%2525280%252529%252529%2525253B%2525250A%25252520%25252520%25252520%25252520console.log%252528flattened%252529%2525253B%2525250A%25252520%25252520%2525257D%252529%2525253B&amp;image=https%3A%2F%2Fcarbon.now.sh%2Fstatic%2Fbrand%2Fbanner.png&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;scroll=auto&amp;schema=carbon" width="1024" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/809a3cb124e8b6742cb4c82cb0378c39/href">https://medium.com/media/809a3cb124e8b6742cb4c82cb0378c39/href</a></iframe><p>This creates a task flat that has the exact same syntax and function as flatten, but changes the syntax of every SPDX-License-Identifier comment to match the MIXED SPDX-License-Identifier</p><h4>How To Use</h4><p>The newly created flat task operates identically to the flatten task. Example:</p><pre>&gt; npx hardhat flat contracts/ContractToFlatten.sol &gt; path/to/Output.sol</pre><h3>Script</h3><p>If you’re not using hardhat, the solution is the same but instead of the fix being a hardhat task, it is a script:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcarbon.now.sh%2Fembed%3Fbg%3Drgba%2528171%252C%2B184%252C%2B195%252C%2B1%2529%26t%3Dvscode%26wt%3Dnone%26l%3Djavascript%26ds%3Dtrue%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D56px%26ph%3D56px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D133%2525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3Dconst%252520fs%252520%25253D%252520require%2528%252522fs%252522%2529%25253B%25250Aconst%252520PATH%252520%25253D%252520%252522contracts%25252FAlreadyFlattenedButBroken.sol%252522%25253B%25250A%25250Alet%252520content%25253B%25250Acontent%252520%25253D%252520fs.readFileSync%2528PATH%25252C%252520%252522utf-8%252522%2529%25253B%25250A%25252F%25252F%252520Remove%252520every%252520line%252520started%252520with%252520%252522%25252F%25252F%252520SPDX-License-Identifier%25253A%252522%25250Acontent%252520%25253D%252520flattened.replace%2528%25252FSPDX-License-Identifier%25253A%25252Fgm%25252C%252520%252522License-Identifier%25253A%252522%2529%25253B%25250Acontent%252520%25253D%252520%252560%25252F%25252F%252520SPDX-License-Identifier%25253A%252520MIXED%25255Cn%25255Cn%252524%25257Bcontent%25257D%252560%25253B%25250A%25252F%25252F%252520Remove%252520every%252520line%252520started%252520with%252520%252522pragma%252520experimental%252520ABIEncoderV2%25253B%252522%252520except%252520the%252520first%252520one%25250Acontent%252520%25253D%252520content.replace%2528%25252Fpragma%252520experimental%252520ABIEncoderV2%25253B%25255Cn%25252Fgm%25252C%252520%2528%2528i%2529%252520%25253D%25253E%252520%2528m%2529%252520%25253D%25253E%252520%2528%2521i%25252B%25252B%252520%25253F%252520m%252520%25253A%252520%252522%252522%2529%2529%25280%2529%2529%25253B%25250A%25250Aconsole.log%2528content%2529%25253B&amp;display_name=Carbon&amp;url=https%3A%2F%2Fcarbon.now.sh%2F%3Fbg%3Drgba%252528171%25252C%2B184%25252C%2B195%25252C%2B1%252529%26t%3Dvscode%26wt%3Dnone%26l%3Djavascript%26ds%3Dtrue%26dsyoff%3D20px%26dsblur%3D68px%26wc%3Dtrue%26wa%3Dtrue%26pv%3D56px%26ph%3D56px%26ln%3Dfalse%26fl%3D1%26fm%3DHack%26fs%3D14px%26lh%3D133%252525%26si%3Dfalse%26es%3D2x%26wm%3Dfalse%26code%3Dconst%25252520fs%25252520%2525253D%25252520require%252528%25252522fs%25252522%252529%2525253B%2525250Aconst%25252520PATH%25252520%2525253D%25252520%25252522contracts%2525252FAlreadyFlattenedButBroken.sol%25252522%2525253B%2525250A%2525250Alet%25252520content%2525253B%2525250Acontent%25252520%2525253D%25252520fs.readFileSync%252528PATH%2525252C%25252520%25252522utf-8%25252522%252529%2525253B%2525250A%2525252F%2525252F%25252520Remove%25252520every%25252520line%25252520started%25252520with%25252520%25252522%2525252F%2525252F%25252520SPDX-License-Identifier%2525253A%25252522%2525250Acontent%25252520%2525253D%25252520flattened.replace%252528%2525252FSPDX-License-Identifier%2525253A%2525252Fgm%2525252C%25252520%25252522License-Identifier%2525253A%25252522%252529%2525253B%2525250Acontent%25252520%2525253D%25252520%25252560%2525252F%2525252F%25252520SPDX-License-Identifier%2525253A%25252520MIXED%2525255Cn%2525255Cn%25252524%2525257Bcontent%2525257D%25252560%2525253B%2525250A%2525252F%2525252F%25252520Remove%25252520every%25252520line%25252520started%25252520with%25252520%25252522pragma%25252520experimental%25252520ABIEncoderV2%2525253B%25252522%25252520except%25252520the%25252520first%25252520one%2525250Acontent%25252520%2525253D%25252520content.replace%252528%2525252Fpragma%25252520experimental%25252520ABIEncoderV2%2525253B%2525255Cn%2525252Fgm%2525252C%25252520%252528%252528i%252529%25252520%2525253D%2525253E%25252520%252528m%252529%25252520%2525253D%2525253E%25252520%252528%252521i%2525252B%2525252B%25252520%2525253F%25252520m%25252520%2525253A%25252520%25252522%25252522%252529%252529%2525280%252529%252529%2525253B%2525250A%2525250Aconsole.log%252528content%252529%2525253B&amp;image=https%3A%2F%2Fcarbon.now.sh%2Fstatic%2Fbrand%2Fbanner.png&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;scroll=auto&amp;schema=carbon" width="1024" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/07cc74a7e188509754476350bd81c653/href">https://medium.com/media/07cc74a7e188509754476350bd81c653/href</a></iframe><p>In this script the path to the flattened broken contract is set as a constant, but depending on your use case you may want to modify the script to accept input from the command line / return the fixed version as a value from a function</p><h3>Incompatible Licenses</h3><p>In the two solutions I provided, a program modifies a flattened contract with multiple SPDX license identifiers to a flattened contract with the SPDX License Identifier: MIXED and multiple license identifiers that define the mixed license.</p><p>Unfortunately, detecting incompatible SPDX licenses is not a trivial task, so solidity compilers won’t pick up on all incompatibility issues.</p><p>This shouldn’t be an issue most of the time since most of the popular smart contract libraries have very permissive licenses. For example, the Openzeppelin Contracts library is licensed under the MIT license</p><p>However, for a large serious project that uses code from a couple of different sources — extra caution may be needed</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=de4d61ffab64" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>