<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Smile</title>
    <description>The latest articles on Forem by Smile (@smile0307).</description>
    <link>https://forem.com/smile0307</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F843162%2Fc7cd9912-d161-4f72-bceb-3e22780be604.jpg</url>
      <title>Forem: Smile</title>
      <link>https://forem.com/smile0307</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/smile0307"/>
    <language>en</language>
    <item>
      <title>Programming on Solana</title>
      <dc:creator>Smile</dc:creator>
      <pubDate>Thu, 02 Jun 2022 09:37:55 +0000</pubDate>
      <link>https://forem.com/smile0307/programming-on-solana-1in4</link>
      <guid>https://forem.com/smile0307/programming-on-solana-1in4</guid>
      <description>&lt;h2&gt;
  
  
  Intro &amp;amp; Motivation
&lt;/h2&gt;

&lt;p&gt;This guide is meant to serve as an intro to coding on the Solana Blockchain, using an escrow program as an example. We'll go through the code together, building the escrow program step by step. I've also created a UI you will be able to use to try out your program. Additionally, you'll get to play with the (shameless plug) spl-token-ui.&lt;/p&gt;

&lt;p&gt;Most of the info in this blog post can be found somewhere in the docs or in example programs. Having said this, I have not found a guide that both walks through most of the coding theory step by step and applies it in practice. I hope this post achieves this, interweaving the theory and practice of solana programs. It requires no previous knowledge of Solana. While this is not a Rust tutorial, I will link to the Rust docs (opens new window)whenever I introduce a new concept. I will also link to the relevant Solana docs although you don't have to read them to follow along.&lt;/p&gt;

&lt;p&gt;Important theory will be sprinkled into the post like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;On Solana, smart contracts are called programs&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and at the end of each section summarized like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;theory recap 📚

- On Solana, smart contracts are called programs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I do not claim to explain all topics but hope this will be a solid starting point from which the reader can explore Solana further. If you're new to Solana and Rust and want to finish this post without breaks and leave with a solid understanding of all concepts discussed and links mentioned I recommend allocating an entire day to the post.&lt;/p&gt;

&lt;p&gt;If something is not working and you just cannot figure out why, have a look at the final code &lt;a href="https://github.com/smile930307/solana-escrow"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Final Product
&lt;/h2&gt;

&lt;p&gt;Before we start coding, let's look at the final product to understand what we are building: an escrow program.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is an escrow?&lt;/strong&gt;&lt;br&gt;
An escrow smart contract is a good example to look at and build because it highlights well what a blockchain makes possible while still being easy to understand, allowing us to focus on the code itself. For those unfamiliar with the concept, here is a brief explainer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ATj6ULYV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/luug9f6n8vdfgdtpjt18.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ATj6ULYV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/luug9f6n8vdfgdtpjt18.gif" alt="Image description" width="347" height="241"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Imagine Alice has an asset A and Bob has an asset B. They would like to trade their assets but neither wants to send their asset first. After all, what if the other party does not hold up their end of the trade and runs away with both assets? A deadlock will be reached where no party wants to send their asset first.&lt;/p&gt;

&lt;p&gt;The traditional way to solve this problem is to introduce a third party C which both A and B trust. A or B can now go first and send their asset to C. C then waits for the other party to send their asset and only then does C release both assets.&lt;/p&gt;

&lt;p&gt;The blockchain way is to replace the trusted third party C with code on a blockchain, specifically a smart contract that verifiably acts the same way a trusted third party would. A smart contract is superior to a trusted third party because of a number of reasons e.g. can you be sure that the trusted third party isn't colluding with the person on the other side of the trade? You can be sure with a smart contract because you can look at the code before running it.&lt;/p&gt;

&lt;p&gt;I'll end this background section here. The internet already has a lot of material on escrows on blockchains. Let's now look at how to build such an escrow on Solana.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the escrow program - Alice's Transaction
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Setting up the project&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Head over to the template repo, click Use this template, and set up a repo. The Solana ecosystem is still young so this is what we've got for now. Vscode with the Rust extension is what I use. You'll also need Rust. Additionally, go here (opens new window)to install the Solana dev tools. (If you're on mac and there are no binaries for the version you want, follow the "build from source" section and add installed the bins to path. If it doesn't build because a command cannot be found, try installing coreutils and binutils with homebrew).&lt;/p&gt;

&lt;p&gt;If you don't know how to test solana programs yet, remove all the testing code. Testing programs is a topic for another blog post. Remove the testing code in lib.rs as well as the tests folder next to src. Lastly, remove the testing dependencies from Cargo.toml. It should now look like this:&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Explanation of Solana’s Token Program</title>
      <dc:creator>Smile</dc:creator>
      <pubDate>Thu, 02 Jun 2022 01:33:09 +0000</pubDate>
      <link>https://forem.com/smile0307/explanation-of-solanas-token-program-19im</link>
      <guid>https://forem.com/smile0307/explanation-of-solanas-token-program-19im</guid>
      <description>&lt;p&gt;&lt;strong&gt;Breaking down how fungible and non-fungible tokens work in Solana.&lt;/strong&gt;&lt;br&gt;
Understanding Solana tokens can be quite difficult if you come from an Ethereum background. In Ethereum, fungible tokens use the ERC20 standard, and non-fungible tokens use the ERC721 standard. Each ERC20 token has its own smart contract, and each NFT collection has its own ERC721 (or ERC1155) contract. Pretty simple stuff.&lt;br&gt;
Things work quite differently in Solana land. Let’s get into it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Solana’s Account Model
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;An account either holds data (e.g. how many tokens you have) or is an executable program (i.e. a smart contract). I refer to the former as “data accounts” and the latter as “program accounts.” Importantly, unlike Ethereum, program accounts do not store state. All state is stored in data accounts.&lt;/li&gt;
&lt;li&gt;Each account contains the following fields 👇&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--r-hOqwaZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c03g4jhygqh98jq0gzrg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--r-hOqwaZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c03g4jhygqh98jq0gzrg.png" alt="Image description" width="880" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each account has a unique address (similar to Ethereum). Most addresses are the public key of a keypair.&lt;/li&gt;
&lt;li&gt;Each account is owned by a program. By default, a newly created account is owned by a built-in program called the “System Program.” Only the owner of an account can modify it.
For more details, you can consult the &lt;a href="https://solana.wiki/zh-cn/docs/account-model/#account-storage"&gt;wiki&lt;/a&gt; I mentioned above, or read a short &lt;a href="https://pencilflip.medium.com/learning-solana-2-what-is-an-account-fc26314609c2"&gt;primer on accounts&lt;/a&gt; that I wrote.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  What is Solana’s Token Program?
&lt;/h2&gt;

&lt;p&gt;Solana’s token program enables the following (for fungible and non-fungible tokens):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Token minting&lt;/li&gt;
&lt;li&gt;Token transferring&lt;/li&gt;
&lt;li&gt;Token burning&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Basically, instead of deploying a new ERC20 smart contract for each new token, all you need to do is send an instruction to the token program. Depending on what instruction you send, the token program will mint/transfer/burn tokens.&lt;/p&gt;

&lt;p&gt;If you don’t completely understand yet, don’t worry! Going through an example should make things much clearer, which is what we’ll do next.&lt;/p&gt;

&lt;p&gt;Note: sometimes you’ll see Solana tokens referred to as “SPL tokens.” SPL stands for Solana Program Library, which is a set of Solana programs that the Solana team has deployed on-chain. SPL tokens are similar to ERC20 tokens, since every SPL token has a standard set of functionality.&lt;/p&gt;
&lt;h2&gt;
  
  
  How Does Solana’s Token Program Work?
&lt;/h2&gt;

&lt;p&gt;The easiest way to understand this is by going through a few examples. Our examples will cover fungible tokens, and we’ll use the spl-token command-line tool to interact with the token program (you can install it by running cargo install spl-token-cli).&lt;/p&gt;

&lt;p&gt;To reiterate, all spl-token is doing is sending instructions to the token program. You can mimic the following behavior by using the &lt;a href="https://www.npmjs.com/package/@solana/spl-token"&gt;JavaScript client&lt;/a&gt;, or by interacting with the token program via &lt;a href="https://docs.solana.com/developing/programming-model/calling-between-programs"&gt;CPI&lt;/a&gt; in Rust.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First, make sure you’ve installed the &lt;a href="https://docs.solana.com/cli/install-solana-cli-tools"&gt;Solana CLI&lt;/a&gt; and spl-token.&lt;/p&gt;

&lt;p&gt;Next, run solana-keygen new -o ~/my_solana_wallet1.json, and create a new environment variable called $SOLADDR1 that stores the resulting public key. Repeat this process, but with a second environment variable called $SOLADDR2 (and name the keypair file my_solana_wallet2.json). We’ll use these addresses and keypair files later on to test out token minting and transferring.&lt;/p&gt;

&lt;p&gt;Here’s what these values look like for me:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ echo $SOLADDR1
3sdsSwWWjjGA7HpPBQfGaXRE2HqmdKicMXHRapqLAu4L
$ echo $SOLADDR2
ES2C1YPzNh5JjQu7DdxrveaPUHj9CnrRWSdrFo4ku5Zh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: many of the commands in this tutorial can be made shorter by running solana config set — keypair ~/my_solana_wallet1.json, which sets the default client keypair. For example, this lets you leave off the --owner flag for many of the following commands. I use the longer version of commands to make things clearer and more explicit.&lt;/p&gt;

&lt;p&gt;Token Creation&lt;/p&gt;

&lt;p&gt;Now that we’re all set up, we can use spl-token. First, let’s create a new type of token.&lt;/p&gt;

&lt;p&gt;$ spl-token create-token --mint-authority ~/my_solana_wallet1.json&lt;br&gt;&lt;br&gt;
Creating token &lt;code&gt;6ifRGEkJ6XmEjuqfTFNqAjjomUiDJTjRv4GHqBH6usWr&lt;/code&gt;&lt;br&gt;
Creating a new type of token creates a new data account, which from now on we’ll refer to as a “mint account.” Each type of token is associated with exactly one mint account. The address of the mint account is &lt;code&gt;6ifRGEkJ6XmEjuqfTFNqAjjomUiDJTjRv4GHqBH6usWr&lt;/code&gt;, but we’ll use &lt;code&gt;$TOKEN1&lt;/code&gt; instead to make things more readable. We can query for the account’s information as follows (don’t worry about all the gobbledygook at the end):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ solana account $TOKEN1
Public Key: Aqf1rBKNQYgX1mjE64STwV3miEwXEe2ioZzD7n4vkpXk
Balance: 0.0014616 SOL
Owner: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
Executable: false
Rent Epoch: 207
Length: 82 (0x52) bytes
0000:   01 00 00 00  2a b0 1a 64  bb c5 f0 df  bf 57 d5 61   ....*..d.....W.a
0010:   56 a8 b8 85  8f a8 0b 09  f1 f1 a2 dc  4d 51 b3 63   V...........MQ.c
0020:   8f 72 bd e9  00 00 00 00  00 00 00 00  09 01 00 00   .r..............
0030:   00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00   ................
0040:   00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00   ................
0050:   00 00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can check out the owner (TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA) using &lt;a href="https://explorer.solana.com/address/6ifRGEkJ6XmEjuqfTFNqAjjomUiDJTjRv4GHqBH6usWr?cluster=devnet"&gt;Solana Explorer&lt;/a&gt; 👇. The owner is the token program (a program account), which is responsible for creating new tokens, minting tokens, and transferring tokens.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6y1eWd74--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ij16kxar5hvub9rn3tum.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6y1eWd74--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ij16kxar5hvub9rn3tum.png" alt="Image description" width="880" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now let’s look at the mint account’s data, which is the stuff that comes after “Length: 82 (0x52) bytes” as shown above. It’s kind of hard to read plain hex data, so we can instead use the Solana Explorer:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9TrKUd4S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qjoiz3tygyq4t177hk65.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9TrKUd4S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qjoiz3tygyq4t177hk65.png" alt="Image description" width="880" height="344"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Solana Explorer decodes the data and displays it in a human-readable format. Here’s what each of the fields means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Address—I hope this is self-explanatory 😛 This is just the mint account’s address.&lt;/li&gt;
&lt;li&gt;Current Supply—The number of tokens that have been minted. Since we just created the token, it is 0.&lt;/li&gt;
&lt;li&gt;Mint Authority—The public key of the keypair that is allowed to mint tokens (we specified this with the --mint-authority flag. If anyone else tries to mint tokens, it will fail.&lt;/li&gt;
&lt;li&gt;Decimals—This dictates the smallest denomination of the token. For NFTs, it should be zero. Nine is the default.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before we move on, here’s a simple diagram showing the accounts that are in play and how they’re related. The legend is stolen from paulx’s great blog:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Internal Solana relations” refers to the owner field that is set on every account, e.g. the owner that’s displayed when you run solana account $TOKEN1.&lt;/li&gt;
&lt;li&gt;“User-space relations” refers to when a relation between two accounts is encoded in an account’s data, e.g. the “mint authority” field we saw above.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1vliNjZC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v47h0djdx128vluqaxyz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1vliNjZC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v47h0djdx128vluqaxyz.png" alt="The Token Program’s owner is the BPF Loader. I don’t picture it here because it’s not that important, and it clutters up the diagram.&amp;lt;br&amp;gt;
" width="880" height="1160"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Token Account Creation
&lt;/h2&gt;

&lt;p&gt;Before we can mint some tokens, we must first create a token account.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ spl-token create-account $TOKEN1 --owner $SOLADDR1                              
Creating account 9EYnoqiBQmJPR55db44cF4wkN1PD5D6vjxEz61r2Ujak
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From now on, we’ll use &lt;code&gt;$TOKENACCT1&lt;/code&gt; instead of &lt;code&gt;9EYnoqiBQmJPR55db44cF4wkN1PD5D6vjxEz61r2Ujak&lt;/code&gt;, and we’ll refer to this account as a “token account.” You might be wondering—what’s a token account? Basically, it just stores how many tokens a particular user has, for a particular type of token. For example, if you own 10 of token1 and 5 of token2, you will have two token accounts.&lt;/p&gt;

&lt;p&gt;Here’s what the token account looks like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ solana account $TOKENACCT1
Public Key: 9EYnoqiBQmJPR55db44cF4wkN1PD5D6vjxEz61r2Ujak
Balance: 0.00203928 SOL
Owner: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
Executable: false
Rent Epoch: 207
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The token account has relations with three other accounts. Its internal owner is the token program, since the token program must be able to modify the account (e.g. add more tokens to it). Then, in user-space, its owner is &lt;code&gt;$SOLADDR1&lt;/code&gt; and its “mint,” or associated mint account, is &lt;code&gt;$TOKEN1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here’s how all the accounts are related now:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gpFKYRzz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s2wy6z8g673iydqb0q8p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gpFKYRzz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s2wy6z8g673iydqb0q8p.png" alt="Image description" width="880" height="673"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Token Minting&lt;/p&gt;

&lt;p&gt;Finally, we can mint some tokens! This part is simpler—we just specify the mint account (which determines the “type” of token to mint), the amount of tokens to mint, and the account to give them to.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ spl-token balance $TOKEN1 --owner $SOLADDR1
0
$ spl-token mint $TOKEN1 10 $TOKENACCT1 --mint-authority ~/my_solana_wallet1.json
Minting 10 tokens
  Token: Aqf1rBKNQYgX1mjE64STwV3miEwXEe2ioZzD7n4vkpXk
  Recipient: 9EYnoqiBQmJPR55db44cF4wkN1PD5D6vjxEz61r2Ujak
$ spl-token balance $TOKEN1 --owner $SOLADDR1
10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In addition to using spl-token balance, you can also use spl-token accounts to view how many of each token a certain account owns.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ spl-token accounts  --owner $SOLADDR1                                                                                                      
Token                                         Balance
---------------------------------------------------------------
Aqf1rBKNQYgX1mjE64STwV3miEwXEe2ioZzD7n4vkpXk  10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happens if you try to mint tokens with a different value for --mint-authority?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ spl-token mint $TOKEN1 10 $TOKENACCT1 --mint-authority ~/my_solana_wallet2.json
Minting 10 tokens
  Token: Aqf1rBKNQYgX1mjE64STwV3miEwXEe2ioZzD7n4vkpXk
  Recipient: 9EYnoqiBQmJPR55db44cF4wkN1PD5D6vjxEz61r2Ujak
RPC response error -32002: Transaction simulation failed: Error processing Instruction 0: custom program error: 0x4 [5 log messages]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The answer? It doesn’t work! This is because the “mint authority” field of the token account is set to a different account, and only that account is allowed to mint new tokens.&lt;/p&gt;

&lt;h2&gt;
  
  
  Token Transferring
&lt;/h2&gt;

&lt;p&gt;Let’s try to transfer some tokens to $SOLADDR2 (which should have been created in the “Prerequisites” section).&lt;/p&gt;

&lt;p&gt;$ spl-token transfer $TOKEN1 1 $SOLADDR2 --owner ~/my_solana_wallet1.json&lt;br&gt;
Transfer 5 tokens&lt;br&gt;
  Sender: 9EYnoqiBQmJPR55db44cF4wkN1PD5D6vjxEz61r2Ujak&lt;br&gt;
  Recipient: ES2C1YPzNh5JjQu7DdxrveaPUHj9CnrRWSdrFo4ku5Zh&lt;br&gt;
  Recipient associated token account: FFednTgQRKDbGYjXrXk8SWPfzSJW3Q8ApN5mGCpGdAtE&lt;br&gt;
Error: Recipient's associated token account does not exist. Add &lt;code&gt;--fund-recipient&lt;/code&gt; to fund their account&lt;br&gt;
Uh oh, it failed 😨 The error message tells us why—we’re not allowed to transfer tokens unless the recipient has a token account.&lt;/p&gt;

&lt;p&gt;There are two ways to fix this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a token account for the recipient and then transfer the tokens.&lt;/li&gt;
&lt;li&gt;Use the --fund-recipient flag. This makes the sender pay for creating the recipient’s token account.
We’ll go with approach #1.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ spl-token create-account $TOKEN1 --owner $SOLADDR2
Creating account FFednTgQRKDbGYjXrXk8SWPfzSJW3Q8ApN5mGCpGdAtE
$ spl-token transfer $TOKEN1 1 $SOLADDR2 --owner ~/my_solana_wallet1.json
Transfer 1 tokens
  Sender: 9EYnoqiBQmJPR55db44cF4wkN1PD5D6vjxEz61r2Ujak
  Recipient: ES2C1YPzNh5JjQu7DdxrveaPUHj9CnrRWSdrFo4ku5Zh
  Recipient associated token account: FFednTgQRKDbGYjXrXk8SWPfzSJW3Q8ApN5mGCpGdAtE
$ spl-token balance $TOKEN1 --owner $SOLADDR2
1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Nice, it worked! Let’s take a look at all the different accounts in play.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WOQSJuP1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/va4rzc8a5puo9z9hy0l1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WOQSJuP1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/va4rzc8a5puo9z9hy0l1.png" alt="Image description" width="880" height="385"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Wrapped SOL
&lt;/h2&gt;

&lt;p&gt;Just like how ETH can be wrapped in an ERC20 token to form wETH, SOL can be wrapped in an SPL token.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ spl-token wrap 1 ~/my_solana_wallet1.json
Wrapping 1 SOL into DXWzvX3RtZbito65G8ZgqmEzJM6Z6Fqy7NHkbpEmCUZD
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, you can unwrap the resulting SPL token to get back your SOL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ spl-token unwrap DXWzvX3RtZbito65G8ZgqmEzJM6Z6Fqy7NHkbpEmCUZD ~/my_solana_wallet1.json
Unwrapping DXWzvX3RtZbito65G8ZgqmEzJM6Z6Fqy7NHkbpEmCUZD
  Amount: 1 SOL
  Recipient: 3sdsSwWWjjGA7HpPBQfGaXRE2HqmdKicMXHRapqLAu4L
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wrapping SOL like this allows SOL to be easily exchanged for other SPL tokens.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wait, What About NFTS?
&lt;/h2&gt;

&lt;p&gt;Now that we’ve walked through how fungible tokens work, understanding non-fungible tokens should be easy. I’ll just let you read the docs, since I wouldn’t add much anyways. The main important points are:&lt;/p&gt;

&lt;p&gt;Use --decimals 0 when creating the token, since there should only be one of them.&lt;br&gt;
After minting one token, disable future minting. This ensures there will only ever be one token.&lt;br&gt;
In practice, no one creates NFTs this way. Instead, most people use Candy Machine, which is a tool for uploading images and metadata and minting them as NFTs. I’ll explain how that works in a future post!&lt;/p&gt;

&lt;p&gt;That’s it! By now, you should understand how SPL tokens work and how they differ from ERC20 tokens. To recap:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A single program called the Token Program is able to create new token types, mint new tokens, and transfer tokens between accounts.&lt;/li&gt;
&lt;li&gt;Unlike ERC20 tokens, where each new type of token is associated with a new ERC20 smart contract, there is only one Token Program. However, each new type of token is associated with a new mint account.&lt;/li&gt;
&lt;li&gt;If a user wants to own some tokens of a particular type, they need to have a token account. In other words, if someone owns five different types of tokens, they will have five different token accounts.&lt;/li&gt;
&lt;li&gt;You can wrap SOL in an SPL token, which allows it to be easily exchanged for other SPL tokens.&lt;/li&gt;
&lt;li&gt;A non-fungible SPL token is a token with zero decimals that has minted one copy and disabled future minting.
If you want to see more of this kind of content, feel free to follow me on &lt;a href="https://twitter.com/Smile930307"&gt;Twitter&lt;/a&gt;!&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>blockchain</category>
      <category>solana</category>
    </item>
    <item>
      <title>What is staking in Blockchain?</title>
      <dc:creator>Smile</dc:creator>
      <pubDate>Fri, 06 May 2022 02:16:45 +0000</pubDate>
      <link>https://forem.com/smile0307/what-is-staking-in-blockchain-11g0</link>
      <guid>https://forem.com/smile0307/what-is-staking-in-blockchain-11g0</guid>
      <description>&lt;h2&gt;
  
  
  Introduction to staking
&lt;/h2&gt;

&lt;p&gt;Someone is said to have a stake in a venture when they contribute some assets in exchange of exercising some level of control, influence, or participation in its activities.&lt;/p&gt;

&lt;p&gt;In the cryptocurrency world this is understood as giving users some kind of right or reward for as long as they don’t transfer some tokens in their possession. A staking mechanism usually encourages token holding against token trading, which in turn is expected to drive up the token valuation.&lt;/p&gt;

&lt;p&gt;The whole project including development environment and tests is available from &lt;a href="https://github.com/smile930307/StakingToken"&gt;my github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To build this staking mechanism we will need:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A staking token.&lt;/li&gt;
&lt;li&gt;Data structures to keep track of stakes, stakeholders and rewards.&lt;/li&gt;
&lt;li&gt;Methods to create and remove stakes.&lt;/li&gt;
&lt;li&gt;A rewards system.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Staking Token
&lt;/h2&gt;

&lt;p&gt;A staking token can be created as an ERC20 token. I’m going to need SafeMath and Ownable later on, so let’s import and use those as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pragma solidity ^0.5.0;

import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";
import "openzeppelin-solidity/contracts/math/SafeMath.sol";
import "openzeppelin-solidity/contracts/ownership/Ownable.sol";

/**
* @title Staking Token (STK)
* @author Smile
* @notice Implements a basic ERC20 staking token with incentive distribution.
*/
contract StakingToken is ERC20, Ownable {
   using SafeMath for uint256;

   /**
    * @notice The constructor for the Staking Token.
    * @param _owner The address to receive all tokens on construction.
    * @param _supply The amount of tokens to mint on construction.
    */
   constructor(address _owner, uint256 _supply)
       public
   {
       _mint(_owner, _supply);
   }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stakeholders&lt;br&gt;
In this implementation we are going to keep track of the stakeholders to facilitate a robust distribution of incentives later on. In theory it would be possible to not keep track of them as a normal ERC20 token would do, but in practice it is difficult to ensure that stakeholders don’t game the distribution system if you don’t track them.&lt;/p&gt;

&lt;p&gt;For implementation we will just use a dynamic array of stakeholder addresses.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; /**
     * @notice We usually require to know who are all the stakeholders.
     */
    address[] internal stakeholders;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following methods add a stakeholder, remove a stakeholder, and verify whether an address belongs to a stakeholder. Other more efficient implementations are surely possible but I like this one for readability.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; /**
    * @notice A method to check if an address is a stakeholder.
    * @param _address The address to verify.
    * @return bool, uint256 Whether the address is a stakeholder,
    * and if so its position in the stakeholders array.
    */
   function isStakeholder(address _address)
       public
       view
       returns(bool, uint256)
   {
       for (uint256 s = 0; s &amp;lt; stakeholders.length; s += 1){
           if (_address == stakeholders[s]) return (true, s);
       }
       return (false, 0);
   }

   /**
    * @notice A method to add a stakeholder.
    * @param _stakeholder The stakeholder to add.
    */
   function addStakeholder(address _stakeholder)
       public
   {
       (bool _isStakeholder, ) = isStakeholder(_stakeholder);
       if(!_isStakeholder) stakeholders.push(_stakeholder);
   }

   /**
    * @notice A method to remove a stakeholder.
    * @param _stakeholder The stakeholder to remove.
    */
   function removeStakeholder(address _stakeholder)
       public
   {
       (bool _isStakeholder, uint256 s) = isStakeholder(_stakeholder);
       if(_isStakeholder){
           stakeholders[s] = stakeholders[stakeholders.length - 1];
           stakeholders.pop();
       }
   }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Stakes
&lt;/h2&gt;

&lt;p&gt;A stake at its simplest form will need to record the stake size and the stake holder. A really simple implementation of this could be just a mapping from the address of the stakeholder to the stake size.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   /**
    * @notice The stakes for each stakeholder.
    */
   mapping(address =&amp;gt; uint256) internal stakes;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I’m going to follow the function names from ERC20 and create equivalents to get the data from the stakes mapping.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   /**
    * @notice A method to retrieve the stake for a stakeholder.
    * @param _stakeholder The stakeholder to retrieve the stake for.
    * @return uint256 The amount of wei staked.
    */
   function stakeOf(address _stakeholder)
       public
       view
       returns(uint256)
   {
       return stakes[_stakeholder];
   }

   /**
    * @notice A method to the aggregated stakes from all stakeholders.
    * @return uint256 The aggregated stakes from all stakeholders.
    */
   function totalStakes()
       public
       view
       returns(uint256)
   {
       uint256 _totalStakes = 0;
       for (uint256 s = 0; s &amp;lt; stakeholders.length; s += 1){
           _totalStakes = _totalStakes.add(stakes[stakeholders[s]]);
       }
       return _totalStakes;
   }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are now going to give STK holders the capability to create and remove stakes. We will burn the tokens as they are staked to stop users from transferring them until the stake is removed.&lt;/p&gt;

&lt;p&gt;Please note that on stake creation _burn will revert if the user tries to stake more tokens than he owns, and on stake removal the update of the stakes mapping will revert if there is an attempt to remove more tokens that were staked.&lt;/p&gt;

&lt;p&gt;Finally, we use addStakeholder and removeStakeholder to have a record of who has stakes, to be used later in the rewards system.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; /**
    * @notice A method for a stakeholder to create a stake.
    * @param _stake The size of the stake to be created.
    */
   function createStake(uint256 _stake)
       public
   {
       _burn(msg.sender, _stake);
       if(stakes[msg.sender] == 0) addStakeholder(msg.sender);
       stakes[msg.sender] = stakes[msg.sender].add(_stake);
   }

   /**
    * @notice A method for a stakeholder to remove a stake.
    * @param _stake The size of the stake to be removed.
    */
   function removeStake(uint256 _stake)
       public
   {
       stakes[msg.sender] = stakes[msg.sender].sub(_stake);
       if(stakes[msg.sender] == 0) removeStakeholder(msg.sender);
       _mint(msg.sender, _stake);
   }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Rewards
&lt;/h2&gt;

&lt;p&gt;Rewards mechanisms can have many different implementations and be quite heavy to run. For this contract we will implement a very simple version where the stakeholders periodically receive a reward in STK tokens equivalent to a 1% of their individual stakes.&lt;/p&gt;

&lt;p&gt;In more sophisticated contracts the distribution of rewards would be automatically triggered when certain conditions are met, but in this case we will let the owner trigger it manually. Following best practice we will also keep track of the rewards and implement a method to withdraw them.&lt;/p&gt;

&lt;p&gt;As before, to make the code readable we have followed the naming conventions from the ERC20.sol contract, first the data structure and data management methods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; /**
    * @notice The accumulated rewards for each stakeholder.
    */
   mapping(address =&amp;gt; uint256) internal rewards;

   /**
    * @notice A method to allow a stakeholder to check his rewards.
    * @param _stakeholder The stakeholder to check rewards for.
    */
   function rewardOf(address _stakeholder)
       public
       view
       returns(uint256)
   {
       return rewards[_stakeholder];
   }

   /**
    * @notice A method to the aggregated rewards from all stakeholders.
    * @return uint256 The aggregated rewards from all stakeholders.
    */
   function totalRewards()
       public
       view
       returns(uint256)
   {
       uint256 _totalRewards = 0;
       for (uint256 s = 0; s &amp;lt; stakeholders.length; s += 1){
           _totalRewards = _totalRewards.add(rewards[stakeholders[s]]);
       }
       return _totalRewards;
   }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next are the methods to calculate, distribute and withdraw rewards:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; /**
    * @notice A simple method that calculates the rewards for each stakeholder.
    * @param _stakeholder The stakeholder to calculate rewards for.
    */
   function calculateReward(address _stakeholder)
       public
       view
       returns(uint256)
   {
       return stakes[_stakeholder] / 100;
   }

   /**
    * @notice A method to distribute rewards to all stakeholders.
    */
   function distributeRewards()
       public
       onlyOwner
   {
       for (uint256 s = 0; s &amp;lt; stakeholders.length; s += 1){
           address stakeholder = stakeholders[s];
           uint256 reward = calculateReward(stakeholder);
           rewards[stakeholder] = rewards[stakeholder].add(reward);
       }
   }

   /**
    * @notice A method to allow a stakeholder to withdraw his rewards.
    */
   function withdrawReward()
       public
   {
       uint256 reward = rewards[msg.sender];
       rewards[msg.sender] = 0;
       _mint(msg.sender, reward);
   }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;No contract can be complete without a comprehensive set of tests. I tend to produce a bug per function at least, and often things don’t work the way I think they do. You could say I get things wrong most of the time, and surely I’m not alone in this.&lt;/p&gt;

&lt;p&gt;Apart from allowing you to produce code that works, tests also are quite useful in developing a process to set up and use contracts. I always write my Getting Started documentation from the code that sets up the environment for the tests.&lt;/p&gt;

&lt;p&gt;Follows an extract of how the test environment is set up and used. We will mint 1000 STK tokens and give them to a user to play with the system. We use truffle for testing which gives us the accounts to use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;contract('StakingToken', (accounts) =&amp;gt; {
   let stakingToken;
   const manyTokens = BigNumber(10).pow(18).multipliedBy(1000);
   const owner = accounts[0];
   const user = accounts[1];

   before(async () =&amp;gt; {
       stakingToken = await StakingToken.deployed();
   });

   describe('Staking', () =&amp;gt; {
       beforeEach(async () =&amp;gt; {
           stakingToken = await StakingToken.new(
               owner,
               manyTokens.toString(10)
           );
       });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When creating tests I always write the tests that make the code revert, but those are not very interesting to see. The test for createStake shows what needs to be done to create a stake, and what should change afterwards.&lt;/p&gt;

&lt;p&gt;It is important to notice how in this staking contract we have two parallel data structures, one for STK balances and one for stakes and how their sum remains constant through stake creation and removal. In this example we give 3 STK wei to the user, and the sum of balance plus stakes for that user will always be 3.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;     it('createStake creates a stake.', async () =&amp;gt; {
           await stakingToken.transfer(user, 3, { from: owner });
           await stakingToken.createStake(1, { from: user });

           assert.equal(await stakingToken.balanceOf(user), 2);
           assert.equal(await stakingToken.stakeOf(user), 1);
           assert.equal(
               await stakingToken.totalSupply(), 
               manyTokens.minus(1).toString(10),
           );
           assert.equal(await stakingToken.totalStakes(), 1);
       });

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For rewards, the test below shows how the owner fires up the distribution of fees, with the user getting a reward of a 1% of his stake.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    it('rewards are distributed.', async () =&amp;gt; {
           await stakingToken.transfer(user, 100, { from: owner });
           await stakingToken.createStake(100, { from: user });
           await stakingToken.distributeRewards({ from: owner });

           assert.equal(await stakingToken.rewardOf(user), 1);
           assert.equal(await stakingToken.totalRewards(), 1);
       });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The total supply for STK is increased when rewards are distributed, and this test shows how the three data structures (balances, stakes and rewards) relate to each other. The amount of existing and promised STK will always be the amount minted on creation plus the amount distributed in rewards, which might or might not be minted. The amount of STK minted on creation will be equal to the sum of balances and stakes until a distribution is done.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  it('rewards can be withdrawn.', async () =&amp;gt; {
           await stakingToken.transfer(user, 100, { from: owner });
           await stakingToken.createStake(100, { from: user });
           await stakingToken.distributeRewards({ from: owner });
           await stakingToken.withdrawReward({ from: user });

           const initialSupply = manyTokens;
           const existingStakes = 100;
           const mintedAndWithdrawn = 1;

           assert.equal(await stakingToken.balanceOf(user), 1);
           assert.equal(await stakingToken.stakeOf(user), 100);
           assert.equal(await stakingToken.rewardOf(user), 0);
           assert.equal(
               await stakingToken.totalSupply(),
               initialSupply
                   .minus(existingStakes)
                   .plus(mintedAndWithdrawn)
                   .toString(10)
               );
           assert.equal(await stakingToken.totalStakes(), 100);
           assert.equal(await stakingToken.totalRewards(), 0);
       });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;A staking and rewards mechanism is a powerful incentive tool that only needs to be as complex as we want to make it. The methods provided in the ERC20 standard and SafeMath allows us to code it in about 200 lines of sparse code.&lt;/p&gt;

&lt;p&gt;Thanks for reading my post.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Build a Solana Smart Contract using Anchor(Part 2)</title>
      <dc:creator>Smile</dc:creator>
      <pubDate>Sun, 24 Apr 2022 05:33:08 +0000</pubDate>
      <link>https://forem.com/smile0307/build-a-solana-smart-contract-using-anchorpart-2-2ed9</link>
      <guid>https://forem.com/smile0307/build-a-solana-smart-contract-using-anchorpart-2-2ed9</guid>
      <description>&lt;h2&gt;
  
  
  Starting a Project with Anchor Framework
&lt;/h2&gt;

&lt;p&gt;Use Anchor’s CLI to start a new project. The following command is the syntax to initialize a new project:&lt;br&gt;
&lt;code&gt;anchor init &amp;lt;new-project-name&amp;gt;&lt;/code&gt;&lt;br&gt;
We are going to give the name of “mySolanaProgram”.&lt;br&gt;
&lt;code&gt;anchor init mySolanaProgram&lt;/code&gt;&lt;br&gt;
This will create a new folder with the name of the project. If you open the folder, you will see a project with the following folder structure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffwss417gbr6xyok5x7tz.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffwss417gbr6xyok5x7tz.PNG" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Working on the Solana Program
&lt;/h2&gt;

&lt;p&gt;Notice there is a lib.rs file that lives in the programs/mySolanaProgram/src folder. In there, it lives the Solana program, or smart contract. Notice the file extension finishes in rs which means it is a Rust file.&lt;/p&gt;

&lt;p&gt;Open the lib.rs file using your favorite IDE. You should see a code similar to the following snippet.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsvmnksdilbg62owm9nj3.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsvmnksdilbg62owm9nj3.PNG" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fspe0169jk4hib1enjljd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fspe0169jk4hib1enjljd.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you don’t have previous knowledge of Rust, this won’t make sense at all. The first line of code is a way to import dependencies or libraries in Rust. In this case, it is importing the anchor library.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use anchor_lang::prelude::*;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The declareId is where Solana stores the address or program id of your program. By default, Anchor generates a program id for us.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, you will see a #[program] section. This is the program module and is where the logic of the program lives.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#[program]
pub mod my_solana_program {
    use super::*;
    pub fn initialize(ctx: Context&amp;lt;Initialize&amp;gt;) -&amp;gt; ProgramResult {
        Ok(())
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, there is a #[derive(Acccounts)] section. This is where the Accounts struct lives which is where accounts are validated.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#[derive(Accounts)]
pub struct Initialize {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might be wondering what a struct is. A struct defines a group of properties. In other words, structs define the structure of the data. It’s like defining an interface in TypeScript if you have a background in TypeScript.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;interface Initialize {
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While it is tempting to relate structs as classes or objects, these concepts do not exist in Rust. For example, it is not possible to define methods in structs like you would using another programming language. However, it is possible to define functions to access structs.&lt;/p&gt;

&lt;p&gt;You will see the methods manipulating the data are available in the module pub mod my_solana_program. This means, our program will have to pass data by reference from the outside in order to modify it.&lt;/p&gt;

&lt;p&gt;This is one of the main differences between Solana and Ethereum, as Solana’s goal is to separate code and data. This means the logic could be applied to other pieces of data. That’s why Solana programs are considered stateless contracts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Updating the Solana Program
&lt;/h2&gt;

&lt;p&gt;Update the logic of the program using the following code. I recommend giving it a look and trying to understand what is going on, even if you don’t have previous knowledge of Rust.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#[program]
pub mod my_solana_program {
    use super::*;
    pub fn setup_platform(ctx: Context&amp;lt;TweetPlatform&amp;gt;) -&amp;gt; ProgramResult {
        let tweet = &amp;amp;mut ctx.accounts.tweet;
        tweet.likes = 0;
        tweet.message = ("").to_string();
        Ok(())
    }

    pub fn write_tweet(
        ctx: Context&amp;lt;WriteTweet&amp;gt;,
        message: String,
        user_public_key: Pubkey
    ) -&amp;gt; ProgramResult {
        let tweet = &amp;amp;mut ctx.accounts.tweet;

        if !tweet.message.trim().is_empty() {
            return Err(Errors::CannotUpdateTweet.into());
        }

        if message.trim().is_empty() {
            return Err(Errors::EmtpyMessage.into());
        }

        tweet.message = message;
        tweet.likes = 0;
        tweet.creator = user_public_key;

        Ok(())
    }

    pub fn like_tweet(ctx: Context&amp;lt;LikeTweet&amp;gt;, user_liking_tweet: Pubkey) -&amp;gt; ProgramResult {
        let tweet = &amp;amp;mut ctx.accounts.tweet;

        if tweet.message.trim().is_empty() {
            return Err(Errors::NotValidTweet.into());
        }

        if tweet.likes == 5 {
            return Err(Errors::ReachedMaxLikes.into());
        }

        let mut iter = tweet.people_who_liked.iter();
        if iter.any(|&amp;amp;v| v == user_liking_tweet) {
            return Err(Errors::UserLikedTweet.into());
        }

        tweet.likes += 1;
        tweet.people_who_liked.push(user_liking_tweet);

        Ok(())
    }
}

#[derive(Accounts)]
pub struct TweetPlatform&amp;lt;'info&amp;gt; {
    #[account(init, payer = user, space = 9000 )]
    pub tweet: Account&amp;lt;'info, Tweet&amp;gt;,
    #[account(mut)]
    pub user: Signer&amp;lt;'info&amp;gt;,
    pub system_program: Program&amp;lt;'info, System&amp;gt;,
}

#[derive(Accounts)]
pub struct WriteTweet&amp;lt;'info&amp;gt; {
    #[account(mut)]
    pub tweet: Account&amp;lt;'info, Tweet&amp;gt;,
}

#[derive(Accounts)]
pub struct LikeTweet&amp;lt;'info&amp;gt; {
    #[account(mut)]
    pub tweet: Account&amp;lt;'info, Tweet&amp;gt;
}

#[account] //An attribute for a data structure representing a Solana account.
#[derive(Default)]
pub struct Tweet {
    message: String,
    likes: u8,
    creator: Pubkey,
    people_who_liked: Vec&amp;lt;Pubkey&amp;gt;, // with  #[derive(Default)] we can assign default values
}


#[error]
pub enum Errors {
    #[msg("Tweet message cannot be updated")]
    CannotUpdateTweet,

    #[msg("Message cannot be empty")]
    EmtpyMessage,

    #[msg("Cannot receive more than 5 likes")]
    ReachedMaxLikes,

    #[msg("Cannot like a tweet without a valid message")]
    NotValidTweet,

    #[msg("User has already liked the tweet")]
    UserLikedTweet,
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What does the program do?
&lt;/h2&gt;

&lt;p&gt;The main purpose of this Solana program is for a user to write a tweet. The requirements for this program are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only one person can write a tweet&lt;/li&gt;
&lt;li&gt;The tweet’s message cannot be empty&lt;/li&gt;
&lt;li&gt;The tweet can be liked by up to 5 different users&lt;/li&gt;
&lt;li&gt;A user cannot like a tweet more than once&lt;/li&gt;
&lt;li&gt;These requirements are pretty standard.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Defining the program errors
&lt;/h2&gt;

&lt;p&gt;Let’s start with the bottom of the code. It is an enum of Errors, and it is used to define each of the errors.&lt;/p&gt;

&lt;p&gt;Notice the attribute #[error] above the enum Errors. This attribute is provided by anchor_lang library, which is imported at the top of the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#[error]
pub enum Errors {
    #[msg("Tweet message cannot be updated")]
    CannotUpdateTweet
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how there is a #[msg()] attribute right above each enum error. This allows us to define a user-friendly error message.&lt;/p&gt;

&lt;p&gt;In the following snippet of code, we show you how to throw one of the enum Errors by using the Err which represents a result type containing a value, value we provide using into(). For instance, we return the error CannotUpdateTweet using the following syntax Err(Errors::CannotUpdateTweet.into()) if the message of the tweet is not empty.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pub fn write_tweet(
        ctx: Context&amp;lt;WriteTweet&amp;gt;,
        message: String,
        user_public_key: Pubkey
    ) -&amp;gt; ProgramResult {
        // some logic

        if !tweet.message.trim().is_empty() {
            return Err(Errors::CannotUpdateTweet.into());
        }

        // more logic
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Defining the Structures
&lt;/h2&gt;

&lt;p&gt;Let’s move on to defining the structs, or the structures of data.&lt;/p&gt;

&lt;p&gt;Let’s start with the struct where the main logic of the program is based on, the Tweet struct.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#[account]
#[derive(Default)]
pub struct Tweet {
    message: String,
    likes: u8,
    creator: Pubkey,
    people_who_liked: Vec&amp;lt;Pubkey&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is incorrect to call message, likes, creator, and people_who_liked “properties”. However, you can think of them in that way to relate if you are coming from object-oriented programming. In theory, we are only defining the names and types of the pieces of data.&lt;/p&gt;

&lt;p&gt;The String type is itself a string, but the other types you might not be familiar with.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;u8 is an unsigned integer type.&lt;/li&gt;
&lt;li&gt;Pubkey is a public key type of a solana account.&lt;/li&gt;
&lt;li&gt;Vec&amp;lt;&amp;gt; is a growable array type. Hence, Vec is a growable array type of public key types.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is the #[account] attribute and it defines the data structure of a Solana account. One of the main characteristics of this attribute is to generate implementations to serialize and deserialize an account. According to the docs, when implementing account serialization traits the first 8 bytes are reserved for a unique account discriminator, self-described by the first 8 bytes of the SHA256 of the account’s Rust ident.AccountSerialize.&lt;/p&gt;

&lt;p&gt;Finally, there is #[derive(Default)], and it allows defining default values for the pieces of data whenever the struc is generated. Currently, we are not setting default data.&lt;/p&gt;

&lt;p&gt;Let’s move on to the TweetPlatform structure, which contains the tweet, user and system_program pieces of data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#[derive(Accounts)]
pub struct TweetPlatform&amp;lt;'info&amp;gt; {
    #[account(init, payer = user, space = 9000 )]
    pub tweet: Account&amp;lt;'info, Tweet&amp;gt;,
    #[account(mut)]
    pub user: Signer&amp;lt;'info&amp;gt;,
    pub system_program: Program&amp;lt;'info, System&amp;gt;,
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pay attention to the use of the derive attribute #[derive(Accounts)]. This allows deserializing the Solana accounts so they can be validated and prevent account substitution attacks. A derive attribute allows new items to be automatically generated for data structures.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#[account(init, payer = user, space = 9000 )]
 pub tweet: Account&amp;lt;'info, Tweet&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the use of init attribute for the tweet field. In other words, this will create a new account owned by the current program. Using init requires someone to pay for creating the account.&lt;/p&gt;

&lt;p&gt;In this case, the user field is defined as the account that will provide the funds to create the tweet account. Finally, there is the space attribute. This defines how large the tweet account should be. For the purposes of this tutorial, we assigned 9000, but this should be calculated beforehand to know how much space it will occupy the program.&lt;/p&gt;

&lt;p&gt;When using the init attribute, we must provide the system_program . This is required by the runtime to create the account.&lt;/p&gt;

&lt;p&gt;Finally, we have the WriteTweet and LikeTweet structs.&lt;/p&gt;

&lt;p&gt;These structs have the tweet account defined with the attribute #[account(mut)]. The mut flag allows keeping changes made to the account even after exiting the program.&lt;/p&gt;

&lt;p&gt;Hence, there are two flags you must take into consideration. The init and the mut flags. The first is used when the account (tweet) is used for the first time and the mut is used to persist any changes, for instance, writing a value to message or updating the likes field.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining instructions in the module
&lt;/h2&gt;

&lt;p&gt;It’s time to talk about the methods defined in the module. These are called instructions. Based on Solana documentation, an instruction specifies a single program, a subset of the transaction’s accounts that should be passed to the program, and a data byte array that is passed to the program. The program interprets the data array and operates on the accounts specified by the instructions.&lt;/p&gt;

&lt;p&gt;Let’s start with the setup_platform method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pub fn setup_platform(ctx: Context&amp;lt;TweetPlatform&amp;gt;) -&amp;gt; ProgramResult {
        let tweet = &amp;amp;mut ctx.accounts.tweet;
        tweet.likes = 0;
        tweet.message = ("").to_string();
        Ok(())
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first thing to notice is the usage of a ctx parameter via Context&amp;lt;&amp;gt;. The Context&amp;lt;&amp;gt; deserializes the accounts found in the struct type passed, in this case, the TweetPlatform. That’s why you can access the field tweet when using ctx.accounts.&lt;/p&gt;

&lt;p&gt;There are other data fields you can access via ctx parameter&lt;/p&gt;

&lt;p&gt;program_id: The current executing program id.&lt;br&gt;
remaining_accounts: Other accounts given but not deserialized or validated.&lt;br&gt;
Which we are not using, but it is important to know about in case we need access to other data fields of a struct.&lt;/p&gt;

&lt;p&gt;Also, we use the keyword &amp;amp;mut to get the mutable reference of the tweet data field. Remember, Solana programs are considered stateless contracts. This means there are no variables and data must be passed from the outside if we want to modify it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let tweet = &amp;amp;mut ctx.accounts.tweet;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While the setup_platform instruction doesn’t seem to do much besides setting the likes data to 0 and the message to an empty string, it is important to remember this process must happen as the TweetPlatform struc uses the init attribute to create tweet account.&lt;/p&gt;

&lt;p&gt;Now, let’s look a the write_tweet instruction.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pub fn write_tweet(
        ctx: Context&amp;lt;WriteTweet&amp;gt;,
        message: String,
        user_public_key: Pubkey
    ) -&amp;gt; ProgramResult {
        let tweet = &amp;amp;mut ctx.accounts.tweet;

        if !tweet.message.trim().is_empty() {
            return Err(Errors::CannotUpdateTweet.into());
        }

        if message.trim().is_empty() {
            return Err(Errors::EmtpyMessage.into());
        }

        tweet.message = message;
        tweet.likes = 0;
        tweet.creator = user_public_key;

        Ok(())
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are two new things happening in this instruction.&lt;/p&gt;

&lt;p&gt;One of them is for the Solana program to accept incoming data used to modify an account (tweet). Besides accepting the context ctx: Context, which we used to deserialize and access tweet account,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A user can pass a message.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A user can pass their user_public_key to “write” a new tweet and identify the creator or owner of the tweet.&lt;br&gt;
The second new part is to apply restrictions to the tweet account.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cannot overwrite a tweet.message if the tweet.message has already data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cannot write an empty tweet.message if the message provided by the external user is empty.&lt;br&gt;
This allows us to use our custom program errors in case any of these fails.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you paid close attention, we used the WriteTweet struct which uses the mut attribute on the tweet account. Remember, this attribute marks the account as mutable and persists changes. That’s why, if we access the tweet account in a different instruction such as like_tweet, we can add additional check to verify the tweet.message is not empty, which infers the tweet.message should have been previously updated in the write_tweet instruction.&lt;/p&gt;

&lt;p&gt;Finally, we have the like_tweet instruction.&lt;/p&gt;

&lt;p&gt;Hopefully, this won’t require much explanation as most of the concepts were explained when we covered the other two instructions, setup_platform and write_tweet. Probably, the only part it might be new for those without much Rust experience is the way we are checking if a new user has liked a tweet before or not.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let mut iter = tweet.people_who_liked.iter();
if iter.any(|&amp;amp;v| v == user_liking_tweet) {
    return Err(Errors::UserLikedTweet.into());
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;tweet.people_who_liked.push(user_liking_tweet);&lt;br&gt;
In short, we need to iterate through all of the values from the people_who_liked data field to verify if the user liking the tweet (user_liking_tweet) has liked it before. Otherwise, add the user_liking_tweet to the array.&lt;/p&gt;

&lt;p&gt;Build the program&lt;br&gt;
After you added all the program logic in the lib.rs file, go ahead and build the program using the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;anchor build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing the program
&lt;/h2&gt;

&lt;p&gt;If you haven’t written many programmatic tests in the past, you will find writing tests more often than what you have ever done in your career when working in web3.&lt;/p&gt;

&lt;p&gt;When we created this project using the Anchor framework, it generated a tests/mySolanaProgram.ts file.&lt;/p&gt;

&lt;p&gt;Update the mySolanaProgram.ts file with the following tests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import * as anchor from '@project-serum/anchor';
import { Program } from '@project-serum/anchor';
import { MySolanaProgram } from '../target/types/my_solana_program';
import { expect, assert } from 'chai';

describe('mySolanaProgram', () =&amp;gt; {

  // Configure the client to use the local cluster.
  anchor.setProvider(anchor.Provider.env());

  const program = anchor.workspace.MySolanaProgram as Program&amp;lt;MySolanaProgram&amp;gt;;
  it('setup tweet platform!', async () =&amp;gt; {
    const tweetKeypair = anchor.web3.Keypair.generate();
    const user = program.provider.wallet;
    await program.rpc.setupPlatform({
      accounts: {
        tweet: tweetKeypair.publicKey,
        user: user.publicKey,
        systemProgram: anchor.web3.SystemProgram.programId
      },
      signers: [tweetKeypair]
    });

    let tweet = await program.account.tweet.fetch(tweetKeypair.publicKey);
    expect(tweet.likes).to.equal(0);
    expect(tweet.message).to.equal('');
  });

  it('Write a tweet', async () =&amp;gt; {
    const tweetKeypair = anchor.web3.Keypair.generate();
    const user = program.provider.wallet;
    await program.rpc.setupPlatform({
      accounts: {
        tweet: tweetKeypair.publicKey,
        user: user.publicKey,
        systemProgram: anchor.web3.SystemProgram.programId
      },
      signers: [tweetKeypair]
    });

    let tweet = await program.account.tweet.fetch(tweetKeypair.publicKey);
    expect(tweet.likes).to.equal(0);
    expect(tweet.message).to.equal('');

    await program.rpc.writeTweet('Hello World!', user.publicKey, {
      accounts: {
        tweet: tweetKeypair.publicKey,
      },
      signers: []
    });

    tweet = await program.account.tweet.fetch(tweetKeypair.publicKey);

    expect(tweet.likes).to.equal(0);
    expect(tweet.message).to.equal('Hello World!');
    expect(tweet.creator.toString()).to.equal(user.publicKey.toString());
  });

  it('should like tweet up no more than 5 times', async () =&amp;gt; {
    const tweetKeypair = anchor.web3.Keypair.generate();
    const user = program.provider.wallet;
    await program.rpc.setupPlatform({
      accounts: {
        tweet: tweetKeypair.publicKey,
        user: user.publicKey,
        systemProgram: anchor.web3.SystemProgram.programId
      },
      signers: [tweetKeypair]
    });

    let tweet = await program.account.tweet.fetch(tweetKeypair.publicKey);
    expect(tweet.likes).to.equal(0);
    expect(tweet.message).to.equal('');

    await program.rpc.writeTweet('Hello World!', user.publicKey, {
      accounts: {
        tweet: tweetKeypair.publicKey,
      },
      signers: []
    });

    tweet = await program.account.tweet.fetch(tweetKeypair.publicKey);
    expect(tweet.likes).to.equal(0);
    expect(tweet.message).to.equal('Hello World!');
    expect(tweet.creator.toString()).to.equal(user.publicKey.toString());

    await program.rpc.likeTweet(user.publicKey, {
      accounts: {
        tweet: tweetKeypair.publicKey,
      },
      signers: []
    });

    tweet = await program.account.tweet.fetch(tweetKeypair.publicKey);
    expect(tweet.likes).to.equal(1);
    expect(tweet.peopleWhoLiked[0].toString()).to.equal(user.publicKey.toString());

    try {
      await program.rpc.likeTweet(user.publicKey, {
        accounts: {
          tweet: tweetKeypair.publicKey,
        },
        signers: []
      });

      assert.ok(false);
    } catch (error) {
      console.log('error ', error.toString());
      const expectedError = 'User has already liked the tweet';
      assert.equal(error.toString().toString(), expectedError);
    }


    const secondUser = anchor.web3.Keypair.generate();
    await program.rpc.likeTweet(secondUser.publicKey, {
      accounts: {
        tweet: tweetKeypair.publicKey,
      },
      signers: []
    });

    tweet = await program.account.tweet.fetch(tweetKeypair.publicKey);
    expect(tweet.likes).to.equal(2);
    expect(tweet.peopleWhoLiked[1].toString()).to.equal(secondUser.publicKey.toString());



    const thirdUser = anchor.web3.Keypair.generate();
    await program.rpc.likeTweet(thirdUser.publicKey, {
      accounts: {
        tweet: tweetKeypair.publicKey,
      },
      signers: []
    });

    tweet = await program.account.tweet.fetch(tweetKeypair.publicKey);
    expect(tweet.likes).to.equal(3);
    expect(tweet.peopleWhoLiked[2].toString()).to.equal(thirdUser.publicKey.toString());



    const fourthUser = anchor.web3.Keypair.generate();
    await program.rpc.likeTweet(fourthUser.publicKey, {
      accounts: {
        tweet: tweetKeypair.publicKey,
      },
      signers: []
    });

    tweet = await program.account.tweet.fetch(tweetKeypair.publicKey);
    expect(tweet.likes).to.equal(4);
    expect(tweet.peopleWhoLiked[3].toString()).to.equal(fourthUser.publicKey.toString());



    const fifthUser = anchor.web3.Keypair.generate();
    await program.rpc.likeTweet(fifthUser.publicKey, {
      accounts: {
        tweet: tweetKeypair.publicKey,
      },
      signers: []
    });

    tweet = await program.account.tweet.fetch(tweetKeypair.publicKey);
    expect(tweet.likes).to.equal(5);
    expect(tweet.peopleWhoLiked[4].toString()).to.equal(fifthUser.publicKey.toString());


    const sixthUser = anchor.web3.Keypair.generate();
    try {


      await program.rpc.likeTweet(sixthUser.publicKey, {
        accounts: {
          tweet: tweetKeypair.publicKey,
        },
        signers: []
      });

      assert.ok(false);
    } catch (error) {
      console.log('error ', error.toString());
      assert.equal(error.toString().toString(), 'Cannot receive more than 5 likes');
    }
  });

  it('should not allow writting an empty message', async () =&amp;gt; {
    const tweetKeypair = anchor.web3.Keypair.generate();
    const user = program.provider.wallet;
    await program.rpc.setupPlatform({
      accounts: {
        tweet: tweetKeypair.publicKey,
        user: user.publicKey,
        systemProgram: anchor.web3.SystemProgram.programId
      },
      signers: [tweetKeypair]
    });

    let tweet = await program.account.tweet.fetch(tweetKeypair.publicKey);
    expect(tweet.likes).to.equal(0);
    expect(tweet.message).to.equal('');


    try {
      await program.rpc.writeTweet('', user.publicKey, {
        accounts: {
          tweet: tweetKeypair.publicKey,
        },
        signers: []
      });
      assert.ok(false);
    } catch (error) {
      assert.equal(error.toString().toString(), 'Message cannot be empty');
    }
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What you will find interesting is how programmatic tests are written in a different programming language (Typescript, but you could configure it also to use JavaScript), and the Solana program uses Rust.&lt;/p&gt;

&lt;p&gt;When you built the Solana program in the previous step, it generates an IDL located in the target/idl folder. The IDL is an interface definition language that lets a program written in one language communicate with another program written in an unknown language.&lt;/p&gt;

&lt;p&gt;Hence, we can access the instructions we defined in my_solana_program in TypeScript so we can validate they are properly working.&lt;/p&gt;

&lt;p&gt;By default, anchor added mocha and chai, JavaScript testing libraries which you can find in the devDependencies of the package.json.&lt;/p&gt;

&lt;p&gt;We won’t go into details explaining how these libraries work. However, there are a few things to take into account which we will explain below.&lt;/p&gt;

&lt;p&gt;Configuring to use a specific cluster&lt;br&gt;
Solana programs are run in clusters. As previously mentioned, the clusters available are devnet, testnet, mainnet, and localnet which is the local cluster. The following line of code is Anchor helping us configure the client to use a specific cluster.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Configure the client to use the local cluster.
  anchor.setProvider(anchor.Provider.env());
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Currently, the comments said that // Configure the client to use the local cluster. What is truly getting the local cluster is this line of code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;anchor.Provider.env()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In other words, that checks the Anchor.toml file and checks the cluster set up in [provider] section. By default, it should have been set to “localnet”. Hence, if you decide to update it to another cluster, it will configure that cluster at the moment of executing the tests.&lt;/p&gt;

&lt;p&gt;Accessing the Solana program and executing instructions&lt;br&gt;
Anchor does a lot of the heavy lifting for us to access the Solana program by using the program variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const program = anchor.workspace.MySolanaProgram as Program&amp;lt;MySolanaProgram&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Anchor provides workspace as a way to access all Solana programs in the local project.&lt;/p&gt;

&lt;p&gt;Anchor also has a helper function to generate key pairs to execute the tests. Remember, these key pairs represent a wallet which are accounts we validate in the Solana program.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const tweetKeypair = anchor.web3.Keypair.generate();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, notice how we are triggering the instructions or methods of the Solana program via the rpc property&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;await program.rpc.setupPlatform({
      accounts: {
        tweet: tweetKeypair.publicKey,
        user: user.publicKey,
        systemProgram: anchor.web3.SystemProgram.programId
      },
      signers: [tweetKeypair]
    });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how Anchor updated the naming convention of the methods from one programming language to another. In Rust, it is called setup_platform and in JavaScript it is called setupPlatform.&lt;/p&gt;

&lt;p&gt;Finally, notice how we access the data from the program via program.account.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;await program.account.tweet.fetch(tweetKeypair.publicKey);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, we access the tweet account, and trigger the fetch method using the public key for the tweet account to access deserialized data, which we use to run tests validations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let tweet = await program.account.tweet.fetch(tweetKeypair.publicKey);
expect(tweet.likes).to.equal(0);
expect(tweet.message).to.equal('');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running the tests&lt;br&gt;
To run the tests, execute the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;anchor test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will build, deploy, and test programs against a specific cluster.&lt;/p&gt;

&lt;p&gt;I encourage you to make changes to the project and further develop this Solana program if you wish to. By no means do I consider myself an expert. However, I did my best effort to explain the concepts after a lot of research to break them into more digestible content.&lt;/p&gt;

&lt;p&gt;Thanks for reading my post.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Build a Solana Smart Contract using Anchor(Part 1)</title>
      <dc:creator>Smile</dc:creator>
      <pubDate>Sun, 24 Apr 2022 01:35:07 +0000</pubDate>
      <link>https://forem.com/smile0307/build-a-solana-smart-contract-using-anchorpart-1-2ga6</link>
      <guid>https://forem.com/smile0307/build-a-solana-smart-contract-using-anchorpart-1-2ga6</guid>
      <description>&lt;p&gt;Solana programs are written using low-level programming languages. More specifically, you can write Solana programs using Rust or C and C++ programming language. For this tutorial, we are going to use Rust.&lt;/p&gt;

&lt;p&gt;Solana has emerged as an attractive alternative to Ethereum due to the cost per transaction being cheaper and faster. While the focus of this article is not to mention the differences between using Solana and Ethereum, it is a good idea to have a general understanding of why Solana is gaining popularity quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  What kind of Solana program are we building?
&lt;/h2&gt;

&lt;p&gt;We are going to build Solana program that writes a tweet, and users are able to like the tweet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do I need to know Rust?
&lt;/h2&gt;

&lt;p&gt;While having previous knowledge in Rust will help you to focus on learning more about the concepts of a Solana program, it is not necessary to know Rust. However, having previous knowledge of a programming language is strongly recommended. We will do our best to break down step-by-step what some of the Rust syntaxes mean.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup Solana Development Environment
&lt;/h2&gt;

&lt;p&gt;We are going to use Anchor, a framework for Solana which is in active development at the moment of this writing. The main purpose of Anchor is to provide convenient tools for developers to write Solana programs instead of spending time working on the tedious parts of the raw Solana programs.&lt;/p&gt;

&lt;p&gt;Important Note: Currently, only x86_64 Linux is supported currently in Anchor. Therefore, your OS needs to run on x86_64. For Windows users, you can install WSL (Windows Subsystem for Linux) and follow the instructions to set up the Solana development environment.&lt;/p&gt;

&lt;p&gt;I am going to skip this step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generate Paper Wallet
&lt;/h2&gt;

&lt;p&gt;For us to test our Solana program, we need to have a crypto wallet. A crypto wallet stores collections of keys used to send and receive cryptocurrencies. For this tutorial, we are going to use a paper wallet generated using the command line.&lt;/p&gt;

&lt;p&gt;To generate a paper wallet, we will use solana-keygen , which should have been installed when we installed solana. However, to verify it was correctly installed, run the following command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;solana-keygen --version&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If you see the version of solana-keygen, that means we can start using it. Run the following command to generate a keypair.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;solana-keygen new&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This will generate a random seed phrase and prompt you to add an optional passphrase. Feel free to add a passphrase if you want, it is ok not to for the purposes of this tutorial.&lt;/p&gt;

&lt;p&gt;Once you fill out the information, the terminal should display the public key and a generated seed phrase. Copy and save the seed phrase in a safe place.&lt;/p&gt;

&lt;p&gt;The key pair will be generated in the following location:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;/home/&amp;lt;your user&amp;gt;/.config/solana/id.json&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the localnet Cluster
&lt;/h2&gt;

&lt;p&gt;To run our Solana program, we need to be in a cluster. According to the Solana documentation, a Solana cluster is a set of validators working together to serve client transactions and maintain the integrity of the ledger.&lt;/p&gt;

&lt;p&gt;In other words, the Solana program cannot run if it is not in a cluster. Currently, Solana has the following clusters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;devnet&lt;/li&gt;
&lt;li&gt;testnet&lt;/li&gt;
&lt;li&gt;mainnet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is possible to connect to localnet, which is the cluster run in our local machine in localhost. To connect to localnet, we will use Solana CLI.&lt;/p&gt;

&lt;p&gt;It is possible to connect to localnet, which is the cluster run in our local machine in localhost. To connect to localnet, we will use Solana CLI.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;solana config set --url localhost&lt;/code&gt;&lt;br&gt;
Now, verify you connected to localnet.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;solana config get&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Tmh7uhev--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dr1kg4xx2d9jd89v4h1o.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Tmh7uhev--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dr1kg4xx2d9jd89v4h1o.PNG" alt="Image description" width="421" height="86"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By default, it will connect to port 8899 for RPC and port 8900 for Websocket.&lt;/p&gt;

&lt;p&gt;Up to this point, we have completed the installation of the development environment.&lt;br&gt;
I am going to deal about how start project in next post.&lt;br&gt;
Thanks.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>NFT Staking and How It Works</title>
      <dc:creator>Smile</dc:creator>
      <pubDate>Fri, 08 Apr 2022 15:20:31 +0000</pubDate>
      <link>https://forem.com/smile0307/nft-staking-and-how-it-works-19pm</link>
      <guid>https://forem.com/smile0307/nft-staking-and-how-it-works-19pm</guid>
      <description>&lt;p&gt;Whenever conversations about non-fungible tokens (NFTs) come up, people almost always think of them as digital representations of art and collectibles that could potentially appreciate in value over time. &lt;/p&gt;

&lt;p&gt;That’s true for a majority of NFT projects, but as the market continues to grow, artists, developers and collectors are exploring new use cases for NFTs. One up-and-coming use case for NFTs is “staking” — locking up a collection in a pool and earning rewards from the staking platform. &lt;/p&gt;

&lt;p&gt;In this guide, we’re going to take a look at what NFT staking is, how it works, what type of rewards holders can receive, and the best platforms for NFT staking. &lt;/p&gt;

&lt;h2&gt;
  
  
  What Is NFT Staking?
&lt;/h2&gt;

&lt;p&gt;*&lt;em&gt;NFT staking refers to locking up non-fungible tokens on a platform or protocol in exchange for staking rewards and other benefits. Staking NFTs allows holders to earn an income from their collection while maintaining ownership. *&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In the crypto world, NFTs are in vogue. They are indivisible smart contracts, typically based on the Ethereum network, that use the ERC721 token standard, meaning every token is unique. These cryptographic tokens — much like cryptocurrencies — are recorded on the blockchain and can be used to prove the ownership, authenticity and provenance of pretty much anything physical or digital, including artwork, avatars, video files, GIFs, collectible cards, video game assets, and more. &lt;/p&gt;

&lt;p&gt;A lot of the buzz and hype around NFTs concerns their potential to revolutionize art collecting. Many of the NFTs that have made headlines generally involve art. For example, digital artist Beeple made history in March 2021 after selling his NFT artwork “Everydays: the First 5000 Days” for a whopping $69 million at Christie’s. This event was one of the first milestones to accelerate the meteoric rise of NFTs. &lt;/p&gt;

&lt;p&gt;NFTs have also found a home in blockchain-based play-to-earn games and GameFi projects. Play-to-earn crypto games use NFTs to give players verifiable ownership of virtual items they collect in games such as Axie Infinity, Gods — and Illuvium.&lt;/p&gt;

&lt;p&gt;The uniqueness of NFTs makes them ideal for wait-and-HODL strategies, though it can take a while before such long-term investments comes to fruition. NFTs are not without drawbacks: the process of minting, buying and selling NFTs can be resource-intensive, sometimes requiring high transaction fees, especially on Ethereum. There’s also the uncertainty of whether or not an NFT will actually appreciate in value over time.&lt;/p&gt;

&lt;p&gt;NFT staking opens up a new opportunity for collectors to monetize their NFT collections. It has become the new way to earn a passive income in the crypto world. HODLers who stake NFTs lock their assets in decentralized finance (DeFi) platforms in order to receive rewards without having to sell or lose ownership of their collection. You basically get to have your cookie — and eat it, too. &lt;/p&gt;

&lt;p&gt;It is similar in concept to DeFi yield farming, an investment strategy that involves lending or staking cryptocurrencies to liquidity providers to earn rewards in the form of transaction fees or interest. It’s similar to earning interest from a bank account, but without a middleman facilitating transactions and taking a cut. &lt;/p&gt;

&lt;p&gt;Staking involves “locking” tokens in a digital wallet to support a blockchain network’s operations and security in exchange for rewards. Platforms that support staking typically use a proof of stake (PoS) mechanism for this purpose. &lt;/p&gt;

&lt;p&gt;Blockchains rely on a global network of transaction validators to secure the network by authenticating transactions before the data is added to a new block on the chain. These validators (also called miners) are rewarded in the native cryptocurrency of a particular blockchain for devoting their resources to the network. &lt;/p&gt;

&lt;p&gt;For energy-intensive blockchains that use a proof of work (PoW) mechanism, such as Bitcoin, the resource validators must devote their computing power, which requires a lot of electricity and expensive specialized hardware. &lt;/p&gt;

&lt;h2&gt;
  
  
  How NFT Staking Works
&lt;/h2&gt;

&lt;p&gt;The blockchain protocol locks up the funds in a staking pool and then randomly chooses validators, who are tasked with “mining” or confirming blocks of transactions. The more a participant pledges, the more likely they are to be chosen. &lt;/p&gt;

&lt;p&gt;Every time a new block is added to the chain, new tokens are minted and distributed to the validators as staking rewards. There are a number of factors which determine how much a validator receives as a staking reward, including how many coins the validator is staking, how long the validator has been actively staking, how many coins are staked on the network, the token’s inflation rate, and more. &lt;/p&gt;

&lt;p&gt;By staking their coins and becoming validators, coin holders are able to make their idle assets work for them in exchange for rewards and generate passive income. The cryptocurrency protocol is also secured and user transactions are confirmed. It’s a win for everybody. Users who stake their coins are still in possession of their assets and have the freedom to remove them from the staking pool at any time, depending on the terms and conditions of the cryptocurrency protocol. &lt;/p&gt;

&lt;p&gt;NFT staking works using the same system, since NFTs are essentially tokenized assets. Users can lock up their NFTs on specific platforms for safekeeping and receive rewards based on the established annual percentage yield (APY) and the number of NFTs staked. &lt;/p&gt;

&lt;p&gt;It’s important to note that, like cryptocurrencies, not every NFT can be staked for rewards. Different projects have different requirements, so check the conditions of your chosen project first before you acquire any NFTs. &lt;/p&gt;

&lt;h2&gt;
  
  
  NFT Staking Rewards
&lt;/h2&gt;

&lt;p&gt;The type of reward NFT holders can get for staking their collection depends on the platform used and the type of NFT staked. The majority of platforms which allow users to stake NFTs offer daily or weekly rewards. The staking rewards are typically issued in a platform’s native utility token, which is often listed on exchanges and can be traded for other cryptocurrencies or fiat money. &lt;/p&gt;

&lt;p&gt;Some staking platforms feature a decentralized autonomous organization (DAO), in which NFT holders can lock up their assets in the DAO pool to participate in the platform’s governance and vote on future proposals.&lt;/p&gt;

&lt;p&gt;Since a majority of the NFT market is attributed to in-game NFTs, most staking opportunities are on play-to-earn gaming platforms such as Axie Infinity, The Sandbox, Polychain Monsters, Splinterlands, and others. &lt;/p&gt;

&lt;h2&gt;
  
  
  Is NFT Staking a Good Investment?
&lt;/h2&gt;

&lt;p&gt;The concept of NFT staking is still in its infancy. Understandably, liquidity is a big issue for NFTs — partly because the ecosystem is underdeveloped, and also because the majority of NFTs are purchased for the purpose of HODLing as long-term investments. Nevertheless, the hype around NFTs has piqued the interest of investors entering the crypto market for the first time who want to explore and potentially earn rewards on NFT platforms. &lt;/p&gt;

&lt;p&gt;NFT staking may not yet be as popular as cryptocurrency staking, but it has a lot of potential for growth in the near future, particularly when Eth2 successfully upgrades to a PoS mechanism, with staking replacing mining. &lt;/p&gt;

&lt;p&gt;Staking NFTs already has a promising foundation which has produced results. Perhaps the biggest advantage of NFT staking is that you don’t need to transfer ownership or sell your NFT collection. All you really need to do is lock up your assets in a staking pool and earn rewards. &lt;/p&gt;

&lt;p&gt;It’s that simple!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bottom Line on NFT Staking
&lt;/h2&gt;

&lt;p&gt;NFT staking is a great way to make extra passive income from your idle NFT collections. It has created new use cases for NFTs that have never been explored before. While the concept is still nascent, it will likely encourage more NFT staking opportunities. If you’re keen to start your own NFT collection, check out our list of best NFT marketplaces to get you going.&lt;/p&gt;

&lt;p&gt;The play-to-earn gaming industry in particular also has a lot to gain from NFT staking.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Publishing a large NFT collection on OpenSea:Part 3</title>
      <dc:creator>Smile</dc:creator>
      <pubDate>Fri, 08 Apr 2022 08:41:14 +0000</pubDate>
      <link>https://forem.com/smile0307/publishing-a-large-nft-collection-on-openseapart-3-3ja5</link>
      <guid>https://forem.com/smile0307/publishing-a-large-nft-collection-on-openseapart-3-3ja5</guid>
      <description>&lt;p&gt;Welcome to Part 3 of “How to publish a large NFT collection on OpenSea” — the story of how we published the Krypto Pandaz collection. In case you missed the previous parts, Part 1 covers selecting the blockchain for our NFT and creating a smart contract. Part 2 describes how to generate a large collection of different images and how to create a metadata server that describes the features of images using a format understood by OpenSea.&lt;br&gt;
By now, you should have a smart contract drafted. You also have a collection of images and a metadata server. It’s time to start the publishing process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup Infura.
&lt;/h2&gt;

&lt;p&gt;What the heck is Infura and why do you need it? Blockchains such as Ethereum or Polygon are run by peer-to-peer networks. They promise decentralization. Anyone can join their networks. The problem is that operating an Ethereum or a Polygon node means a lot of overhead if all we want to do is to create or contract or programmatically invoke a contract method. This is where providers like Infura step in. Infura provides a convenient API interface to blockchains. Best of all, Infura has a generous free tier. It’s more than enough for occasional contract deployments.&lt;br&gt;
Go to &lt;a href="https://infura.io/"&gt;https://infura.io/&lt;/a&gt; and create an account. If you decided to use Polygon, you will need to activate an add-on called “Polygon PoS” at &lt;a href="https://infura.io/upgrade"&gt;https://infura.io/upgrade&lt;/a&gt;. Once you are logged in, create a project and copy its ID. You will need it to deploy your contract.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add crypto to the wallet you will use to deploy the contract.
&lt;/h2&gt;

&lt;p&gt;Each time you run a transaction on Ethereum or Polygon you have to pay a fee called a gas fee. In our case, we will have to pay for the following operations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Deploy the contract — this is a one-time fee. Polygon is &lt;br&gt;
significantly cheaper than Ethereum. In our case, we paid $0.34 / 0.203 MATIC for contract creation. You can inspect the transaction on Polygonscan. Deploying the same contract on Ethereum could easily cost close to $1000. For example, see this ERC721 contract deployed on 2/14/2022 at 0.235 ETH ~= $860. It’s 2500 more than on Polygon!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Mint NFT’s — in our case, we have a contract method that mints &lt;br&gt;
NFT’s in batches of 100. Each batch cost us about $1.50 / 0.85 MATIC in gas fees. See this initial mint. I was eager to see it work so I cranked up the fee to reduce the wait time. That’s why the transaction price for the first mint is higher than the subsequent mints. If we stick with our 2500 multiplier for Ethereum, that would be thousands of dollars per batch.&lt;br&gt;
The total amount of money for Polygon to list a collection of 10000 NFT’s is:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Polygon — $150&lt;/li&gt;
&lt;li&gt;Ethereum — tens of thousands, depending on the time of day 
(network congestion) and the price of ETH.
Getting money to your Ethereum wallet is trivial. Buy it from a trustworthy source that supports transferring to wallets, e.g. Coinbase or the providers integrated with MetaMask.
Funding your Polygon wallet is a lot more work. First, you need Ethereum. Then you need to bridge Ethereum to Polygon (where it becomes WETH or Wrapped Ethereum). Finally, you convert your Polygon ETH to MATIC. Bridging from Ethereum to Polygon is very expensive. Initially, I made a mistake and bridged only $100. I paid $40 in fees so I had to repeat the process and paid the fees again. Don’t be like Adam and bridge $300 to give you some breathing room. You may also experiment with other bridging solutions that take a lot longer (days) but are also a lot cheaper as they batch many requests into larger transactions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Deploy the contract.
&lt;/h2&gt;

&lt;p&gt;We will deploy the contract using Truffle in a Javascript project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Install Truffle:&lt;br&gt;
&lt;code&gt;npm install -g truffle&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a sample project. Here we are using polygon:&lt;br&gt;
truffle unbox polygon&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add your smart contract .sol file to ./contracts/polygon directory.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Adjust the migrations script in .contracts/migrations directory to point to your contract.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Go to the terminal and configure Infura:&lt;br&gt;
&lt;code&gt;export INFURA_PROJECT_ID=......&lt;/code&gt;&lt;br&gt;
Export the mnemonic for the account that will be used to deploy the contract:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export MNEMONIC=.....
export TESTNET_MNEMONIC=.....
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Deploy the contract to the testnet:&lt;br&gt;
&lt;code&gt;npm run migrate:polygon --network=polygon_infura_testnet&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If everything looks good, deploy to the mainnet:&lt;br&gt;
&lt;code&gt;npm run migrate:polygon — network=polygon_infura_mainnet&lt;/code&gt;&lt;br&gt;
Make sure you record the address of the contract. You will need it in subsequent steps.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Mint tokens.
&lt;/h2&gt;

&lt;p&gt;You may think that publishing a contract to mainnet should be enough for OpenSea to notice it. That is not the case. OpenSea will not pick it up unless there is at least one NFT minted in the contract.&lt;br&gt;
In order to mint NFT’s, you have to send transactions to your blockchain of choice. There are several ways to do it. You could automate the process with e.g. Truffle. We didn’t feel comfortable automating so many transactions that cost us real $$$. Instead, I invoked methods on our contract using Remix. For some reason, Remix was very slow and unstable for me in Chrome. I switched to Brave browser and it resolved my issues.&lt;br&gt;
Here is what you need to do to invoke a contract method in Remix:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new contract file in the workspace. Simply copy/paste the contents of your .sol file.&lt;/li&gt;
&lt;li&gt;Compile the contract.&lt;/li&gt;
&lt;li&gt;Make sure you are logged into Metamask as the contract owner.&lt;/li&gt;
&lt;li&gt;Configure your Metamask wallet to use Polygon mainnet.&lt;/li&gt;
&lt;li&gt;Configure Remix to use Injected web3 provider. This will wire up Remix to the Polygon config in Metamask.&lt;/li&gt;
&lt;li&gt;Use load contract from address option to load your contract.&lt;/li&gt;
&lt;li&gt;Remix will display methods exposed by the contract. Find the mint method and invoke it. You will need to approve it in Metamask. Repeat till you have achieved the desired number of NFT’s.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Setup your collection on OpenSea.
&lt;/h2&gt;

&lt;p&gt;Now that your contract is live on Polygon it’s time to publish it as a collection on OpenSea.&lt;br&gt;
Make sure you are signed in to Metamask as the contract owner.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For Ethereum, go to &lt;a href="https://opensea.io/assets/"&gt;https://opensea.io/assets/&lt;/a&gt;&amp;lt;contract_address/0, e.g. &lt;a href="https://opensea.io/assets/0xe04fe81304e4c1f7edbcb46f239199876400c712/1"&gt;https://opensea.io/assets/0xe04fe81304e4c1f7edbcb46f239199876400c712/1&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;For Polygon, go to 
&lt;a href="https://opensea.io/assets/matic/"&gt;https://opensea.io/assets/matic/&lt;/a&gt;&amp;lt;contract_address/0, e.g. &lt;a href="https://opensea.io/assets/matic/0xb20c24050fa9000a3824c58dd3efda300eab2bac/0"&gt;https://opensea.io/assets/matic/0xb20c24050fa9000a3824c58dd3efda300eab2bac/0&lt;/a&gt;
OpenSea will allow you to edit the collection. Most options control how the collection is visible and are self-explanatory. You can change them to improve how your collection is presented over time. There are a couple of settings that you want to get right from the get-go:&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Creator Earnings — this is % from all subsequent transactions that will keep flowing to your wallet as royalty fees.&lt;/li&gt;
&lt;li&gt;Your payout wallet address — the contract that will receive royalties.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this series, we have gone through publishing a large NFT collection on OpenSea. The process involves multiple steps and requires a diverse skillset. We talked about how to generate image combinations from layers, how to expose a metadata server, how much it costs to mint, how to deploy a contract and register a collection on OpenSea.&lt;br&gt;
Good luck with your creations!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/topsoftwareengineer/publishing-a-large-nft-collection-on-openseapart-2-3e3m"&gt;Back to Part2&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Publishing a large NFT collection on OpenSea:Part 2</title>
      <dc:creator>Smile</dc:creator>
      <pubDate>Fri, 08 Apr 2022 08:32:47 +0000</pubDate>
      <link>https://forem.com/smile0307/publishing-a-large-nft-collection-on-openseapart-2-3e3m</link>
      <guid>https://forem.com/smile0307/publishing-a-large-nft-collection-on-openseapart-2-3e3m</guid>
      <description>&lt;p&gt;Welcome to Part 2 of “Publishing a large NFT collection on OpenSea” — the story of how we published the Krypto Pandaz collection. Part 1 covered selecting the blockchain and defining your smart contract. In Part 2 we will focus on generating images and creating a metadata server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create images and a metadata server
&lt;/h2&gt;

&lt;p&gt;Most large NFT collections consist of similar images, each having a unique set of variations. The most popular collections have anywhere from 1,000 to 10,000 NFTs which would be almost impossible to create by hand. We assume that many of these large collections are programmatically generated; which is what we decided to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up an image generator project
&lt;/h2&gt;

&lt;p&gt;We decided to use NodeJS to generate the pieces for our collection. We started by installing a few libraries to our project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add color jimp sharp path fs-extra lodash async
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We used the color library for parsing and manipulating hex color codes, jimp for manipulating images, sharp for compositing our image layers, path for working with file and directory paths, fs-extra for reading and writing files, lodash for utility methods, and async for managing asynchronous operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a configuration for image assets
&lt;/h2&gt;

&lt;p&gt;Before you enter this step, you will need to have some image assets that will eventually be layered together to create a single NFT.&lt;br&gt;
For this example, I’ll show you how we created the configuration required to generate a panda with various color schemes and clothing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ArB7HX-L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mpme449iziwmspkqtmtk.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ArB7HX-L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mpme449iziwmspkqtmtk.PNG" alt="" width="880" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s start by setting up a JSON object that contains a list of sections that will come together to create an individual NFT.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import * as path from 'path';

const pandaAssetsDir = path.join(__dirname, '../../assets/panda');
const config = {
    sections: [
        {
            name: 'Panda',
            chanceToBeEmpty: 0,
            pieces: [{
                    name: 'Panda',
                    baseImage: path.join(pandaAssetsDir, 'panda.png')
            }]
        },
        {
            name: 'Body',
            chanceToBeEmpty: 0.2,
            pieces: [
                {
                    name: 'Lab Coat',
                    baseImage: path.join(pandaAssetsDir, 'lab-coat.png')
                },
                {
                    name: 'Scrubs',
                    baseImage: path.join(pandaAssetsDir, 'scrubs.png')
                }
            ]
        }
    ]
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see here that we added two sections; one for the panda and another for the clothing it’ll have on its body. This alone does not give us many variations so let’s add some configuration that’ll help us generate new themes for the panda.&lt;br&gt;
We can add a new property called scheme to our pieces, we want to generate themes for. In this object, we’ll define the colors in the base image that we want to modify as well as a list of themes containing colors that will replace the corresponding base colors.&lt;br&gt;
Tip: The color library is very helpful for creating different colors from a single color code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import * as path from 'path';
import Color from 'color';

const pandaAssetsDir = path.join(__dirname, '../../assets/panda');

const orange = Color('#d35400');
const pink = Color('#e84393');
const blue = Color('#0984e3');
const green = Color('#00b894');
const red = Color('#d63031');
const gray = Color('#dfe6e9');

const config = {
    sections: [
        {
            name: 'Panda',
            chanceToBeEmpty: 0,
            pieces: [{
                name: 'Panda',
                keepBaseImage: true,
                baseImage: path.join(pandaAssetsDir, 'panda.png'),
                scheme: {
                    baseColors: ['#75388B', '#453451', '#2F243B'],
                    intensity: 0.6,
                    themes: [
                        { name: 'Pink', colors: [pink, pink.darken(0.15), pink.darken(0.3)] },
                        { name: 'Blue', colors: [blue, blue.darken(0.15), blue.darken(0.3)] },
                        { name: 'Green', colors: [green, green.darken(0.15), green.darken(0.3)] },
                        { name: 'Red', colors: [red, red.darken(0.15), red.darken(0.3)] },
                        { name: 'Gray', colors: [gray, gray.darken(0.15), gray.darken(0.3)] },
                    ],
                }
            }]
        }
    ]
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have some configuration to work with, we can move forward with writing the code that will generate our images!&lt;/p&gt;

&lt;p&gt;Theme individual assets&lt;br&gt;
The last thing we added to our configuration was the scheme property to create different color variations of our panda. I wrote a function that relies on jimp to apply a color to an image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function getDistanceBetweenColors(color1: number[], color2: number[]) {
    const r = Math.abs(color1[0] - color2[0]);
    const g = Math.abs(color1[1] - color2[1]);
    const b = Math.abs(color1[2] - color2[2]);

    return r + g + b;
}

function isColorIsNearTo(color: number[], colorToCompare: number[], distance: number) {
    return getDistanceBetweenColors(color, colorToCompare) &amp;lt; distance;
}

function applyColorToImage(image: jimp, color: Color, intensity: number, shouldColorBeChanged: any = () =&amp;gt; true) {
    image.scan(0, 0, image.bitmap.width, image.bitmap.height, function (x, y, idx) {
        const currentRed = this.bitmap.data[idx + 0];
        const currentGreen = this.bitmap.data[idx + 1];
        const currentBlue = this.bitmap.data[idx + 2];

        if (shouldColorBeChanged([currentRed, currentGreen, currentBlue])) {
            const redDiff = currentRed - color.red();
            const greenDiff = currentGreen - color.green();
            const blueDiff = currentBlue - color.blue();

            const red = redDiff &amp;gt; 0 ? currentRed - redDiff * intensity : currentRed + Math.abs(redDiff * intensity);
            const green = greenDiff &amp;gt; 0 ? currentGreen - greenDiff * intensity : currentGreen + Math.abs(greenDiff * intensity);
            const blue = blueDiff &amp;gt; 0 ? currentBlue - blueDiff * intensity : currentBlue + Math.abs(blueDiff * intensity);

            this.bitmap.data[idx + 0] = red;
            this.bitmap.data[idx + 1] = green;
            this.bitmap.data[idx + 2] = blue;
        }
    });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using the above functions we are now able to go through every theme and apply our new colors onto the base colors of the image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const parsedImagePath = path.parse(baseImage);
fs.mkdirpSync(path.join(parsedImagePath.dir, '/variants/'));

async.map(scheme.themes, (theme, callback) =&amp;gt; {
    const newImagePath = path.join(parsedImagePath.dir, '/variants/', `${parsedImagePath.name}--${theme.name}${parsedImagePath.ext}`);

    jimp.read(baseImage, (err, image) =&amp;gt; {
        if (err) throw err;

        theme.colors.forEach((color, i) =&amp;gt; {
            applyColorToImage(image, color, opts.scheme.intensity, (c) =&amp;gt; isColorIsNearTo(c, scheme.baseColors[i].rgb().array(), 1));
        });

        image.write(newImagePath);
    });
}, (err, results) =&amp;gt; {
    if (err) return reject(err);
    resolve();
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this code, we are now able to generate variations for each of our assets by simply adding some more configuration!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BFKWwG4Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h8svih2mysylvk2hry9i.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BFKWwG4Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h8svih2mysylvk2hry9i.PNG" alt="" width="880" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Generate images&lt;br&gt;
Now that we have different sections in our configuration we need to write some code to put the different pieces together. This is where the sharp library comes in handy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const firstImage = path.join(__dirname, '../assets/panda.png');
const comboImages = [
    path.join(__dirname, '../assets/variants/scrubs--red.png'),
    path.join(__dirname, '../assets/nurse-hat.png')
];

await sharp(firstImage)
    .composite(comboImages.map(image =&amp;gt; ({
        input: image
    })))
    .toFile(path.join(__dirname, 'panda-1.png'));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This small bit of code takes our base panda image and adds red scrubs and a nurse hat.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gjkWTzQB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cxnqmdrc2mvoq6q33721.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gjkWTzQB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cxnqmdrc2mvoq6q33721.PNG" alt="" width="305" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Generate metadata
&lt;/h2&gt;

&lt;p&gt;Once you have the code needed to generate your NFT images, you’ll want to enhance it so that it also generates metadata for each NFT. OpenSea has documentation for its metadata standards here.&lt;br&gt;
We created one large JSON array which we put into a file that contains metadata for each NFT.&lt;br&gt;
Each entry has the following properties:&lt;br&gt;
description : A description of the NFT, can be the same for each one but we decided to generate a description based on the properties of the Panda.&lt;br&gt;
image : The name of the image file for the NFT.&lt;br&gt;
name : The name for the NFT.&lt;br&gt;
attributes : A list of attributes for the NFT.&lt;br&gt;
This is the first metadata object we generated for our collection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "description": "Green Panda with Lab Coat, Nurse Hat on a Pink background",
  "image": "panda-1.png",
  "name": "Panda #1",
  "attributes": [
    {
      "trait_type": "Panda",
      "value": "Green Panda"
    },
    {
      "trait_type": "Body",
      "value": "Lab Coat"
    },
    {
      "trait_type": "Head",
      "value": "Nurse Hat"
    },
    {
      "trait_type": "Background",
      "value": "Pink"
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is what the above NFT will look like on OpenSea:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Zl09mNIF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ik17nwaxtxhvg00s3ed9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Zl09mNIF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ik17nwaxtxhvg00s3ed9.png" alt="" width="412" height="798"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Upload images to a publicly accessible location
&lt;/h2&gt;

&lt;p&gt;There are many options you can use for storing your NFT images. You can use IPFS, a cloud storage provider like Google Cloud Storage, or some other solution that provides public access to your images.&lt;br&gt;
We went with Google Cloud Storage as it’s very easy to use. After we used our script to generate all our pandas, we simply ran a command using gsutil to copy them all to a Cloud Storage bucket.&lt;br&gt;
&lt;code&gt;gsutil -m cp *.png gs://nft-pandas&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Create a metadata server
&lt;/h2&gt;

&lt;p&gt;In order for people and services to know about the assets that are available in our collection, we need to create a server to serve the JSON metadata we created above. We decided to use Google App Engine.&lt;br&gt;
First, we installed ExpressJS: npm install express --save&lt;br&gt;
Then we created a NodeJS script to start an ExpressJS server that serves a JSON object with some details about our collection and an endpoint for each individual NFT:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const express = require('express')
const db = require('./src/panda-manifest.json')

const PORT = process.env.PORT || 5000

const app = express().set('port', PORT)

app.get('/', function(req, res) {
  res.send({
    "name": "Pandas",
    "description": "Pandas are adorable pandas. Adopt one today to make us reach. Jump on the NFT bandwagon and get your own Panda!",
    "image": "https://storage.googleapis.com/nft-pandas/panda-0.png",
    "seller_fee_basis_points": 100,
    "fee_recipient": "0xA97F337c39cccE66adfeCB2BF99C1DdC54C2D721"
  });
})

app.get('/api/token/:token_id', function(req, res) {
  const tokenId = parseInt(req.params.token_id);
  const panda = db[tokenId];
  res.send({
    description: panda.description,
    image: 'https://storage.googleapis.com/nft-pandas/' + panda.image,
    name: panda.name,
    attributes: panda.attributes,
  })
})

app.listen(app.get('port'), function() {
  console.log('Node app is running on port', app.get('port'));
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have the script for our metadata server, we just need to create an app.yamlthat tells App Engine what environment to run our server with and a .gcloudignore that tells it what files to ignore when deploying:&lt;/p&gt;

&lt;p&gt;The final step is to deploy our app to Google App Engine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gcloud config set project nfts-326700
gcloud app deploy --quiet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the deployment completes your metadata server will be accessible via your App Engine project!&lt;br&gt;
This is our metadata server: &lt;a href="https://nfts-326700.uc.r.appspot.com/"&gt;https://nfts-326700.uc.r.appspot.com/&lt;/a&gt;&lt;br&gt;
Now that you have your metadata server exposed on the Internet, go &lt;br&gt;
back to your contract and insert the metadata server URL in lines 22, 26 of the contract that we covered in Part 1.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In Part 2 we learned how to generate a large collection of different images. We also covered how to create a metadata server that describes the properties of images in a format that is understood by OpenSea.&lt;br&gt;
In Part 3, we will finally publish our contract, mint NFT’s and list the collection on OpenSea.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/topsoftwareengineer/publishing-a-large-nft-collection-on-opensea-50m2"&gt;Back to Part1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/topsoftwareengineer/publishing-a-large-nft-collection-on-openseapart-3-3ja5"&gt;Go to Part3&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Publishing a large NFT collection on OpenSea:Part 1</title>
      <dc:creator>Smile</dc:creator>
      <pubDate>Fri, 08 Apr 2022 08:31:20 +0000</pubDate>
      <link>https://forem.com/smile0307/publishing-a-large-nft-collection-on-opensea-50m2</link>
      <guid>https://forem.com/smile0307/publishing-a-large-nft-collection-on-opensea-50m2</guid>
      <description>&lt;p&gt;You have been following the exponential growth of NFT transactions. You keep hearing about 12-year-olds who have made millions selling NFT’s. Perhaps you have even bought a few NFTs or even created a small collection yourself. You know that large NFT collections are where all the action is. I’m writing this in early April 2022. This is what the top collections chart by transaction volume looks like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WNZLzry6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ee570sz86hc3mnaikrcb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WNZLzry6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ee570sz86hc3mnaikrcb.png" alt="" width="880" height="795"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You need thousands of items to become a top-tier collection. How does one create such a large collection? It’s very different from building a small collection manually. In this article, we will describe how we published the Krypto Pandaz collection.&lt;br&gt;
We will focus on the technical steps. We are not going to cover marketing, the initial sales process, and other activities that you need to get right to have a relevant NFT collection.&lt;/p&gt;

&lt;p&gt;The technical process involves multiple steps. We have divided the content into 3 parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Part 1 (this story)&lt;br&gt;
Decide which blockchain you want to use&lt;br&gt;
Define a smart contract&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Part 2&lt;br&gt;
Create images and a metadata server&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Part 3&lt;br&gt;
Setup Infura&lt;br&gt;
Add crypto to the wallet you will use to deploy the contract&lt;br&gt;
Deploy the contract&lt;br&gt;
Mint tokens&lt;br&gt;
Setup your collection on OpenSea&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;What skills do you need to have or hire? :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understanding of how blockchains work&lt;/li&gt;
&lt;li&gt;Solidity — smart contract definition language&lt;/li&gt;
&lt;li&gt;A modern development language that can manipulate images and has libraries for web 3 — we will use TypeScript&lt;/li&gt;
&lt;li&gt;Art skills to create image layers&lt;/li&gt;
&lt;li&gt;Armed with these skills, we can start the process of generating a large NFT collection.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Decide which blockchain you want to use
&lt;/h2&gt;

&lt;p&gt;OpenSea currently supports the following blockchains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Ethereum —the largest of all 3 in terms of NFT items published on &lt;br&gt;
OpenSea. It’s also the biggest in terms of spending. People spend 100x more on NFT’s on Ethereum than on NFT’s on Polygon. Ethereum’s problem is high gas fees. When you publish a contract, mint an NFT, or approve a transaction, it costs money. Especially minting NFT’s is expensive if you have thousands of tokens to create. At the time of writing, the costs of minting 10k tokens through a standard ERC-721 contract on Ethereum go in thousands of dollars.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Polygon — Ethereum’s L2 network. It has much lower gas fees. It &lt;br&gt;
will cost you less than $100 to publish an ERC-721 contract and mint 10k NFT’s. The issue here is that people are spending 100x less on NFT’s on Polygon than on Ethereum.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Klatyn — recently added by OpenSea. It’s compatible with Ethereum
&lt;/h2&gt;

&lt;p&gt;smart contracts. It has the smallest footprint on OpenSea. I’ve not explored it much.&lt;br&gt;
For our first collection, we decided to go with Polygon. We were not comfortable with the upfront cost that Ethereum required. We wanted to learn the process and be OK with making mistakes with smaller amounts of money.&lt;/p&gt;

&lt;h2&gt;
  
  
  Define a smart contract
&lt;/h2&gt;

&lt;p&gt;It’s time to define a Solidity contract. OpenSea supports so-called ERC 721 and ERC 1155 contracts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;ERC 721 — this standard describes how to store information about &lt;br&gt;
NFT ownership on Ethereum. It also defines what operations can be performed and how they happen. In most basic terms, an NFT is a place in a smart contract that can hold ownership information. There is no NFT metadata in the contract. When you buy an NFT, you instruct OpenSea to insert your wallet address in a map in the contract that manages the NFT.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ERC 1155 — this is an extension to ERC 721 and currency token &lt;br&gt;
contracts. It defines how to store many tokens and many NFT collections in a single contract. If you intend to deploy multiple NFT collections or want to mix an NFT collection with a token, ERC 1155 is the way to go.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://dev.to/topsoftwareengineer/publishing-a-large-nft-collection-on-openseapart-2-3e3m"&gt;Go to Part2&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How To Create NFT on Solana With IPFS</title>
      <dc:creator>Smile</dc:creator>
      <pubDate>Fri, 08 Apr 2022 08:09:37 +0000</pubDate>
      <link>https://forem.com/smile0307/how-to-create-nft-on-solana-with-ipfs-3kd0</link>
      <guid>https://forem.com/smile0307/how-to-create-nft-on-solana-with-ipfs-3kd0</guid>
      <description>&lt;p&gt;In case you slept through all of 2021, NFTs (non-fungible tokens) are hot. They have evolved from “random jpegs” to self-contained applications, access tokens, communities, and more. With increased creativity and expanding support across multiple blockchains, NFTs don’t look to be going away any time soon.&lt;br&gt;
While Ethereum might be the first blockchain one thinks of when talking about NFTs, there are plenty of other chains out there to consider. I previously wrote about creating NFTs on the Flow blockchain, but an increasingly popular choice is Solana. Solana recently overtook Ethereum in total transaction volume in November of 2021, and Rarity Sniper (a tool for creating rarity rankings on NFT collections) moved to support Solana in 2021 after initially supporting Ethereum only.&lt;br&gt;
With that as our background, let’s take a look at how to create an NFT collection on Solana using Pinata and IPFS. This is an especially interesting tutorial because Solana has been closely linked to Arweave, but many projects prefer IPFS for its rapid content access and reliability. Metaplex, a project built to make creating NFT projects on Solana easier is has IPFS support baked in, and this includes the ability to use Pinata to store content and serve that content through a dedicated IPFS gateway.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting Up The Development Environment
&lt;/h2&gt;

&lt;p&gt;To begin, make sure you sign up for a Pinata account. You can test with a free account, but for a mainnet NFT launch, you may want to consider the Professional Plan with a Dedicated IPFS Gateway.&lt;br&gt;
Once you’ve signed up for an account, you really just need to make sure you have the following installed (each is linked to the installed instructions of you need to install them):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js version 16.13.0 or newer is recommended&lt;/li&gt;
&lt;li&gt;Git version 2.32.0 or newer&lt;/li&gt;
&lt;li&gt;Yarn version 1.22.17 or newer&lt;/li&gt;
&lt;li&gt;ts-node version 10.4.0 or newer&lt;/li&gt;
&lt;li&gt;solana cli&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For what it’s worth, we will be following much of the instructions on Metaplex’s website with some modifications that allow us to upload content to IPFS through Pinata.&lt;/p&gt;
&lt;h2&gt;
  
  
  Preparing The Assets
&lt;/h2&gt;

&lt;p&gt;This tutorial is not going to go through the process of generating assets for your NFT project. That’s a totally separate endeavor that has to happen before the contract can be deployed to Solana. So, assuming you have the art created, we’ll walk through how to prep this for upload to IPFS through Metaplex.&lt;br&gt;
The first step is to create a folder for your NFT project assets. From the command line, that will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir nft-project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside that folder, create another folder called assets. In the assets folder, you will add all of your images. It’s important that each of your images be labeled in a base 0 index format. That means the first image would be 0.png and the second would be 1.png and so on.&lt;br&gt;
Once your images are in the folder, you will need to create the metadata for these images. If you have any experience with Ethereum NFT metadata, you’ll feel right at home with Solana NFT metadata. The structure is nearly identical. Let’s took a look at a basic JSON metadata file structure for NFTs on Solana:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "name": "Number #0001",
    "symbol": "NB",
    "description": "Collection of 10 numbers on the blockchain. This is the number 1/10.",
    "image": "0.png",
    "attributes": [
        {"trait_type": "Layer-1", "value": "0"},
        {"trait_type": "Layer-2", "value": "0"}, 
        {"trait_type": "Layer-3", "value": "0"},
        {"trait_type": "Layer-4", "value": "1"}
    ],
    "properties": {
        "creators": [{"address": "N4f6zftYsuu4yT7icsjLwh4i6pB1zvvKbseHj2NmSQw", "share": 100}],
        "files": [{"uri": "0.png", "type": "image/png"}]
    },
    "collection": {"name": "numbers", "family": "numbers"}
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just like with the Ethereum metadata standard, the Solana metadata standard has a name, image, and description. Additionally, you can include attributes (just like with ETH), a symbol, and collection details. In Ethereum projects, the token symbol is generally assigned at contract deployment and not in the metadata. Another difference is the properties portion of the Solana metadata. This is required and it allows you to include an array of files for your NFTs. You have to have at least one file in that array which points to the same asset as the image property, but you can include other files that make up your entire NFT. This is a really interesting concept that should be explored further, but for the sake of this post, we will only be operating with single asset NFTs.&lt;/p&gt;

&lt;p&gt;Ok, so we know we have images in the folder. We know those images need to be named in a specific way. And we know that we need JSON metadata files. How are we now going to get those JSON files created and added to the assets folder. You could do this manually, but with a 10,000 NFT project, that would be nearly impossible.&lt;/p&gt;

&lt;p&gt;Let’s write a script to create the metadata files.&lt;br&gt;
From your command line, make sure you are in the nft-project folder. We will create a new file called &lt;code&gt;metadata-generator.js&lt;/code&gt; by running this command: &lt;code&gt;touch metadata-generator.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In your code editor, open that new file. It’s empty, but we’ll fill it in now. We need to loop through all of the images in our assets folder and create a unique JSON file for each. We need to name and save that JSON file in the same assets folder. To do this, we will make use of the Node.js fs which is built into Node.js.&lt;/p&gt;

&lt;p&gt;In your &lt;code&gt;metadata-generator.js&lt;/code&gt; file add this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const fs = require('fs');
const imageDir = fs.readdirSync("./assets");
imageDir.forEach(img =&amp;gt; {
  const metadata = {
    name: `Image ${img.split(".")[0]}`,
    description: "An image in the NFT collection",
    symbol: "YOUR NFT COLLECTION SHORT SYMBOL",
    image: img,
    seller_fee_basis_points: ROYALTIES_PERCENTAGE_BASIS_POINTS,
    properties: {
      files: [{ uri: img, "type": "image/png" }],
      category: "image",
      creators: [{
        address: "YOUR_SOL_WALLET_ADDRESS",
        share: 100
      }]
    }
  }
  fs.writeFileSync(`./assets/${img.split(".")[0].json}`, JSON.stringify(metadata))
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To run your script and generate the metadata, you should run this command from the root your project folder: &lt;code&gt;node metadata-generator.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When the script is finished, you’ll have an assets folder that has images and JSON files together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Metaplex
&lt;/h2&gt;

&lt;p&gt;Metaplex is a tool that makes launching an NFT project on Solana much easier. Rather than having to write your own smart contract like you would have to do with Ethereum, Metaplex has pre-written contracts that projects can plug into. To make this work, Metaplex has to be able to access the files associated with the NFTs, then it needs to be able to upload those files and associated them with each token to be minted.&lt;/p&gt;

&lt;p&gt;Metaplex has support for IPFS through a few services, but we’ll be focusing on using Pinata.&lt;br&gt;
We will be following the standard Metaplex Candy Machine guide found here. The first step is going to be to clone Metaplex. Run the following command in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/metaplex-foundation/metaplex.git ~/metaplex
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are cloning the directory into the home directory so that we don’t have to remember where the project was cloned. Inside the new metaplex repo is code to support the JavaScript CLI we will be making use of. So, we need to install dependencies for that CLI code.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn install --cwd ~/metaplex/js/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now, let’s make sure the install worked. Remember, we installed ts-node? We’re going to use that now to run a command for the metaplex cli.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ts-node ~/metaplex/js/packages/cli/src/candy-machine-v2-cli.ts --version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should print out a version if everything was installed properly. Now, we need to make sure the Solana CLI is working. Run this command to get the version number:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;solana --version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If that works, you’re good to go. If not, double check that you have installed the Solana CLI Toolkit. Next, we’re going to use that CLI to create devnet wallet. The Solana devnet is where we can experiment without fear of incurring any real financial impact.&lt;br&gt;
Run this command to create your new devnet wallet and store the keys:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;solana-keygen new --outfile ~/.config/solana/devnet.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we can set the default keypair for our Solana CLI interactions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;solana config set --keypair ~/.config/solana/devnet.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, let’s let the CLI know that we intent to interact with the devnet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;solana config set --url https://api.devnet.solana.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To confirm this all worked as expected, we can fetch the configuration for Solana like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;solana config get
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should print out the configuration information. If so, you’re ready to get airdropped some SOL!&lt;br&gt;
This is, of course, devnet SOL, but it’s still necessary to interact with the devnet. Run this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;solana airdrop 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When that is complete, you can run this command to verify your devnet wallet has the SOL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;solana balance
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see that your devnet wallet has 2 SOL.&lt;br&gt;
With all of the initial Solana and Metaplex installation and configuration done, we will move on to configuring out NFT drop.&lt;/p&gt;
&lt;h2&gt;
  
  
  Drop Configuration
&lt;/h2&gt;

&lt;p&gt;This is the most important part of the entire drop, so you should read about it in full here. Metaplex uses a tool called Candy Machine for NFT drops like this, and it’s important to make sure your project is configured properly.&lt;br&gt;
In the root of your project, create a JSON file called &lt;code&gt;config.json&lt;/code&gt;. Then, enter a configuration like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
"price": 0.05,
"number": 100,
"gatekeeper": null,
"solTreasuryAccount": "YOUR DEVNET WALLET ADDRESS",
"splTokenAccount": null,
"splToken": null,
"goLiveDate": "01 Jan 2022 00:00:00 GMT",
"endSettings": null,
"whitelistMintSettings": null,
"hiddenSettings": null,
"storage": "pinata",
"ipfsInfuraProjectId": null,
"ipfsInfuraSecret": null,
"pinataJwt": "YOUR PINATA JWT TOKEN",
"pinataGateway": "YOUR PINATA GATEWAY URL",
"awsS3Bucket": null,
"noRetainAuthority": false,
"noMutable": false
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This may feel like a lot, but this is actually just the minimum configuration required. There’s a lot more you can add. Don’t worry, though. We’re keeping it simple. You only need to change five things in this file. The first change is to set the storage property to point to pinata.&lt;br&gt;
Then, you need to decide if you are going to use a Pinata Dedicated Gateway or not. Doing so will give you and anyone who loads your NFTs the best possible performance. But, while recommended it’s not required. If you do decide to get a Dedicated Gateway, you’ll need to upgrade to the Professional Plan in Pinata.&lt;/p&gt;

&lt;p&gt;Next, you need to get a JWT for Pinata API access. To do so, click the dropdown menu in the top-right, click API Keys, then generate a new API Key. You can either select specific endpoints the key should have access to (pinFileToIPFS) or you can make it an admin key which has full access. This is up to your security preferences, but the key must have access to pinFileToIPFS. You can read more about scoped API keys here.&lt;/p&gt;

&lt;p&gt;When you’ve created your key a modal will display your API Key, API Secret, and your JWT. We only need the JWT, so copy that and paste it into the pinataJwt configuration file property’s value.&lt;br&gt;
If you created a Dedicated Gateway, you can paste in your Dedicated Gateway’s URL in the pinataGateway section of the config file. If you did not upgrade and do not have a Dedicated Gateway, you should update that property to look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pinataGateway: null
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, you just need to set the number of items in your NFT collection for the number property and the price in SOL for the price property.&lt;br&gt;
That’s all you need. Now, there’s a lot more you can do, but I want to point out something that a lot of projects probably want to do, and that’s hide the NFT assets until after a reveal date. This is possible with Metaplex and Candy Machine with hidden settings. You can read about that here. We won’t be doing that in this tutorial. The NFT project in this tutorial will represent a mint with immediate reveal.&lt;br&gt;
The final thing you’ll want to change in the config file is the solTreasuryAccount. This should probably point to your devnet wallet address, but it can be any Solana wallet address. To get your devnet wallet address, you can run this in the command line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;solana address
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’re about ready to upload some assets and create our NFT contract!&lt;/p&gt;

&lt;h2&gt;
  
  
  Uploading To Pinata and Solana
&lt;/h2&gt;

&lt;p&gt;We’ve finished all the prep work. It’s time to upload. Fortunately, this is really simple.&lt;br&gt;
In the root of the project directory, run this command in the command line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ts-node ~/metaplex/js/packages/cli/src/candy-machine-v2-cli.ts upload \
    -e devnet \
    -k ~/.config/solana/devnet.json \
    -cp config.json \
    -c nft-project \
    ./assets
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will take your config file parse it so that Metaplex knows how to upload your files and how to get the NFT mint info onto Solana. You’ll see in your command line each upload of your asset folder’s metadata and your asset folders images. Depending on how many files you are uploading this process could take a long time.&lt;/p&gt;

&lt;p&gt;When the process is done, you’ll need to do one more thing. You’ll need to verify the upload. This helps you know if everything is ready to go and if your NFTs can be minted. You just need to run this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ts-node ~/metaplex/js/packages/cli/src/candy-machine-v2-cli.ts verify_upload \
    -e devnet \
    -k ~/.config/solana/devnet.json \
    -c nft-project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If all goes well, you should see a message that includes Ready to deploy!. This means your project is ready to go.&lt;/p&gt;

&lt;p&gt;Your NFT drop is ready for minting! But what do we do now? You probably want to create a website to allow for minting. Fortunately, the Metaplex code base has us covered.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a Minting App
&lt;/h2&gt;

&lt;p&gt;Remember, we cloned the the full repository for Metaplex? We cloned that into our home directory. So, from your command line, let’s change to our home directory and into the metaplex project, directly into the sample React app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd ~/metaplex/js/packages/candy-machine-ui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll need to install the dependencies like this:&lt;br&gt;
yarn install&lt;br&gt;
Then, open the project in your code editor. You’ll see a .env file in that folder. You should update it to look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REACT_APP_CANDY_MACHINE_ID=YOUR CANDY MACHINE ID
REACT_APP_SOLANA_NETWORK=devnet
REACT_APP_SOLANA_RPC_HOST=https://api.devnet.solana.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember, we used the Candy Machine ID to look up our project on the Solana Explorer? We are going to paste that same ID where is says YOUR CANDY MACHINE ID.&lt;br&gt;
Now, you can run the app with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the app load, you should see:&lt;/p&gt;

&lt;p&gt;!(&lt;a href="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rz4ksvhvusoqa0p716kg.PNG"&gt;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rz4ksvhvusoqa0p716kg.PNG&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;You will be able to choose a Solana wallet to connect with. Regardless of which wallet you choose, you’ll need to make sure it has some devnet SOL in it. Remember when we airdropped ourselves some devnet SOL? We can also do that with another address. Connect your wallet, make sure you’re on devnet, then copy the wallet address. From the command line run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;solana airdrop 2 YOUR_ADDRESS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once this is complete, you should have SOL in your browser-based wallet and can now mint one of your new NFTs. To do so, just click the Mint button:&lt;/p&gt;

&lt;p&gt;!(&lt;a href="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g8c51uq8cmk7z8sj7l6p.PNG"&gt;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g8c51uq8cmk7z8sj7l6p.PNG&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;You’ll need to confirm the transaction. When you do so, the minting process should only take a few seconds. Once it’s complete, your new NFT should show up in your wallet’s NFT collection.&lt;/p&gt;

&lt;p&gt;The image you see of your NFT is being loaded from Pinata. Depending on if you provided a Dedicated Gateway URL in your config file, the NFT is loading through your custom gateway or a public IPFS gateway. Note: If you provided a Dedicated Gateway URL in the config, your NFT asset will not only load quicker but will be cached so that all subsequent loads are instant. You can see this in the example below using Phantom Wallet. The two NFTs that are stuck loading are using a public IPFS gateway. The two that load almost instantly are loaded through a Dedicated Gateway.&lt;/p&gt;

&lt;p&gt;!(&lt;a href="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/73vilc2t12si99e4qdoo.gif"&gt;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/73vilc2t12si99e4qdoo.gif&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Wrapping Up&lt;br&gt;
The benefits of using IPFS for the storage of NFT assets are well-documented. IPFS offers quick retrieval (especially when using a Pinata Dedicated Gateway), IPFS offers content addressability, and IPFS is portable. Ownership of NFTs can transfer in both the token sense and the media sense.&lt;br&gt;
However, it has not always been easy to use IPFS with Solana NFT projects. Hopefully, this tutorial helps change that. Hopefully, it can lead to more creative projects.&lt;br&gt;
Happy pinning, and happy drop days!&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
