BUIDL on Solana - Explain me Like I am web2 - Frontend Edition

BUIDL on Solana - Explain me Like I am web2 - Frontend Edition

Feb 10, 2022Β·

21 min read

πŸ—Ί What are we Building?

We will build Joke To Earn! This is a walkthrough of building Solana dApps. This tutorial can be used independently or as a sequel to part1: Build Your Solana Program.

This is a walkthrough where I will explain every single concept. If you want to copy-paste the code, you can grab the full-stack code here.

At the end of this guide, you will know how to:

  1. πŸš€ Build your first dApp on Solana,
  2. πŸ„β€β™‚οΈ Understand what the workflow of a web3 developer is.
  3. πŸŽ™ Learn the language of Blockchains; IDLs, Instructions, Transactions.
  4. ✍️ Learn why wallets and accounts matter.
  5. 🌝 Go TO THE MOON (no).

The Stack:

  • You.
  • Solana Wallet-Adapter.
  • ReactJS.
  • Anchor.
  • A Solana Wallet.

🌈 Show it to me!

intro-gif.gif

TLDR: Try it live! Fullstack code here


πŸ›  Install

Install Node and NPM with NVM

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
nvm install node
npm install -g yarn

We will be using a wallet adapter, a library made by the Solana team, to facilitate wallets integration. I do not recommend using create-react-app as you need to tweak the config a lot. So we will be using the react-ui-starter to make our life easier:

git clone git@github.com:solana-labs/wallet-adapter.git
cp -r wallet-adapter/packages/starter/react-ui-starter .
cd react-ui-starter

Let's also install Anchor JS:

yarn add  @project-serum/anchor

We won't be using typescript; replace all the .ts files with .js.

mv *.ts *.js

Now delete everything from App.js and replace it with this:

// React Stuff
import React, { useMemo } from 'react';

// Wallet-Adapter Stuff
import { WalletAdapterNetwork } from '@solana/wallet-adapter-base';
import { ConnectionProvider, WalletProvider } from '@solana/wallet-adapter-react';
import { WalletModalProvider, WalletMultiButton } from '@solana/wallet-adapter-react-ui';
import {
  LedgerWalletAdapter,
  PhantomWalletAdapter,
  SlopeWalletAdapter,
  SolflareWalletAdapter,
  SolletExtensionWalletAdapter,
  SolletWalletAdapter,
  TorusWalletAdapter,
} from '@solana/wallet-adapter-wallets';
import '@solana/wallet-adapter-react-ui/styles.css'

// Solana Stuff
import { clusterApiUrl } from '@solana/web3.js';
// import JokeArena from './components/JokeArena';

const App = () => {
  // The network can be set to 'devnet', 'testnet', or 'mainnet-beta'.
  const network = WalletAdapterNetwork.Devnet;

  // You can also provide a custom RPC endpoint.
  const endpoint = useMemo(() => clusterApiUrl(network), [network]);

  // @solana/wallet-adapter-wallets includes all the adapters but supports tree shaking and lazy loading --
  // Only the wallets you configure here will be compiled into your application, and only the dependencies
  // of wallets that your users connect to will be loaded.
  const wallets = useMemo(
      () => [
        new PhantomWalletAdapter(),
        new SlopeWalletAdapter(),
        new SolflareWalletAdapter(),
        new TorusWalletAdapter(),
        new LedgerWalletAdapter(),
        new SolletWalletAdapter({ network }),
        new SolletExtensionWalletAdapter({ network }),
      ],
      [network]
  );

  return (
      <ConnectionProvider endpoint={endpoint}>
        <WalletProvider wallets={wallets} autoConnect>
          <WalletModalProvider>
              <WalletMultiButton />
            {/*<JokeArena network={endpoint}/>*/}
          </WalletModalProvider>
        </WalletProvider>
      </ConnectionProvider>
  )
}

export default App;

I will explain what the code is doing very soon. So don't worry about it, but before, I would like you to play with it to visualize and understand what is happening.

So run it

yarn start

If all is well, we should all be on the starter line with this:

starter-select-wallet.png

So why do we need a Wallet? 🧐

🌏 A world behind Wallets

🧐 What are Wallets?

Like the traditional apps where we need to log in, you need to be authenticated in the Blockchain world every time you do something. But, in Blockchain, we don't use logins and passwords; we use a signature ✍️. And a wallet provides a way to stamp/sign.

Instead of having a dozen user accounts with Google, Twitter, etc.. you have one identity, that identity, that pen, that signature, that stamp sits with you and be used everywhere. You show the gatekeepers your pen, your signature, the chain acknowledges your identity, and boom, welcome to web3 land!

By the way, you can also create multiple wallets if you want various identities.

Screen Shot 2022-02-03 at 7.54.28 PM.png

Source: @haltakov.eth

πŸ€·β€β™‚οΈ Why Wallets?

Now you are thinking; we were just fine with user accounts. Who had the bizarre idea to create wallets, and why do you even care? If you want to know more about wallets, please check out this post: blog.mwrites.xyz/why-do-we-need-wallets-in-...

πŸ‘» So let's grab that Wallet

So go ahead and install a wallet for your browser. For Solana, we use Phantom

Once you get your wallet, switch to the Devnet, there are multiple environments of Blockchain:

  1. πŸ’» Localhost: a Local Node Running on your machine.
  2. πŸ›  Devnet : stable development environment.
  3. πŸ†• Testnet: where the Solana team releases new beta features.
  4. πŸ’° Mainnet: the real Solana Chain where people deploy Live application and where real sols are burned.

Once you have installed your Phantom Wallet, let's switch to devnet, so we don't have to burn real sol πŸ˜‰ (more on why we need sol later)

Screenshot 2021-11-23 at 21.15.17.png

Now that you have your Phantom App installed, go ahead and try to connect; you should see your wallet address:

starter-wallet-connected.png

So what happened?

  1. We created a simple App component, wrapped it with WalletProvider, which manages the "connect to wallet" button, and added a modal to choose which Wallet app they are using in src/App.js:
    <WalletProvider wallets={wallets} onError={onError} autoConnect>
        <WalletDialogProvider>
            <WalletMultiButton />
        </WalletDialogProvider>
    </WalletProvider>
  1. Then we defined adapters we wanted to handle so that the user can choose multiple Wallet Apps in src/App.js:
const wallets = useMemo(
        () => [
            new PhantomWalletAdapter(),
            new SlopeWalletAdapter(),
            new SolflareWalletAdapter(),
            new TorusWalletAdapter(),
            new LedgerWalletAdapter(),
            new SolletWalletAdapter({ network }),
            new SolletExtensionWalletAdapter({ network }),
        ],
        [network]
    );
  1. FInall we created a Connection to the Devnet Blockchain in src/App.js:
// The network can be set to 'devnet', 'testnet', or 'mainnet-beta'.
const network = WalletAdapterNetwork.Devnet;

// You can also provide a custom RPC endpoint.
const endpoint = useMemo(() => clusterApiUrl(network), [network]);

<ConnectionProvider endpoint={endpoint}>
...
</ConnectionProvider>

Big Boy Option: Can all of this be handled simply, without installing someone's library. YES!

Basically what wallet-adapter is doing for us is this:

const connectWallet = async () => {
  try {
    const { solana } = window;

    if (!solana || !solana.isPhantom) {
      alert('Phantom Wallet not found! πŸ‘»');
      return;
    }
    console.log('Phantom wallet found!');

    console.log('Connecting to Wallet..');
    const response = await solana.connect();

    const publicKey = response.publicKey.toString();
    console.log(`Wallet connected!, address:, ${publicKey}`);

    return { response, publicKey };
  } catch (error) {
    console.error(error);
  }
};

if you want something minimal, you can take a look at how it's done here: github.com/mwrites/w3-solana-thumbs-up/blob..

But I discourage doing so because:

  1. This someone's library is not just someone, well... it comes from Solana Labs, I believe they know what they are doing since this is their tech πŸ˜…
  2. It handles much more edge cases, what if the user disconnected, what if he wants to disconnect, what if she switches Wallet, etc.
  3. For an ordinary thing such as connecting a Wallet, you shouldn't have to rewrite this from scratch every time. The goal is to make it the most standard possible in terms of experience for the user.
  4. Additional Plus: If a new Awesome Wallet arrives, you probably just have to yarn update
  5. Angular, Vue, Svelte are also supported: github.com/solana-labs/wallet-adapter.
  6. Of course, if you know what you are doing, ping me with your new implementation! 🀟

Pheww.. so much setup, don't worry, you won't have to do that again, plus, now that you have installed a web3 wallet, you can start connecting to all the web3 enabled websites, NFT, Defi, etc. Welcome to the new world, friend.

πŸ† ACHIEVEMENT - Wallets

  • βœ… Phantom Wallet is installed,
  • βœ… Know the basics of wallets
  • βœ… User can connect to our dApp using their Wallet App.

πŸŽ™ Let's Talk?

πŸ™Š A language called IDL

To exchange with our Solana Program on the Blockchain side, we first need to know how to talk to it; this is the role of the IDL (interface description language).

Every Program can upload its IDL to the Blockchain. It is a file that describes how to talk to the corresponding Program.

This is the IDL of the Solana Program I have deployed:

{
  "version": "0.1.0",
  "name": "joketoearn",
  "instructions": [
    {
      "name": "createJoke",
      "accounts": [
        {
          "name": "jokeAccount",
          "isMut": true,
          "isSigner": true
        },
        {
          "name": "authority",
          "isMut": true,
          "isSigner": true
        },
        {
          "name": "systemProgram",
          "isMut": false,
          "isSigner": false
        }
      ],
      "args": [
        {
          "name": "jokeContent",
          "type": "string"
        }
      ]
    }
  ],
  "accounts": [
    {
      "name": "Joke",
      "type": {
        "kind": "struct",
        "fields": [
          {
            "name": "author",
            "type": "publicKey"
          },
          {
            "name": "content",
            "type": "string"
          }
        ]
      }
    }
  ]
}

βœ‰οΈ Instructions

  "instructions": [
    {
      "name": "createJoke"
      ...

In the Blockchain world, we don't call APIs or functions; we send instructions.

In a traditional web2 setup, your backend is exposed as an endpoint URL that you can call, something like

https://my-pretty-aws-server/createjoke

However, with Blockchains, we can't do that. Why?

Reason #1: A Blockchain is not one single thing that you can talk to; it is a distributed database; it is more like a beehive, where there are many nodes or bees 🐝 that all together constitute the Blockchain.

image-20220126183042291

Reason #2: a Solana Program (or Smart Contract in Ethereum) is not accessible on the Blockchain node directly; it is contained and controlled by the Solana System Program. If the Blockchain is a beehive, each node 🐝 includes a hive brain πŸ‘ called the Solana System Program. It's like every node runs Linux, and your Program runs inside Linux.

create_joke_transactions_1.png

So the recipe goes like this:

  1. We craft an Instruction βœ‰οΈ
  2. We send that to one of the bees 🐝
  3. The bees will do their magic to agree on who will earn the right to collect that instruction.
  4. The winning bee will use his Solana System Program to read your instruction.
  5. ...Which in turn will forward that instruction to the corresponding Solana Program (the one I deployed for you, or yours if you did part1)

How do bees coordinate to decide who picks up the instruction? That is one of the fundamental concepts of blockchain; the consensus mechanism, each blockchain have their way to do this: (Proof of Work, Proof of State, Proof of History, etc..), in the case of Solana, this is Proof of History.

πŸ† ACHIEVEMENT - Deployment & IDLs

  • βœ… Understand what IDLs are
  • βœ… Understand why Instruction exist
  • βœ… Understand how Instructions are passed to a Solana Program

πŸͺ Deploy Your Solana Program to DevNet

We are doing the front-end part in this tutorial, so If you haven't followed [part1: Build Your Solana Program]:(blog.mwrites.xyz/your-first-solana-program) you can just use the Solana Program I have already deployed for you.

Otherwise, cd into the joketoearn project and follow the steps here: blog.mwrites.xyz/anchor-commands-cheatsheet...

And take note of the Program Address. We will need it later to call your Program.

🌈 Let's BUIDL - Setup and Base

Anyway congrats! You can now call your app a dApp, and this DApp does... nothing! πŸ˜† (for now)

Remember, our goal is this:

intro-gif.gif

Let's organize the pieces and configs into a base.

Create a new file src/utils/config.js:

import { PublicKey } from "@solana/web3.js";

export const connectionsOptions = {
    preflightCommitment: 'processed',
};

export const programAddress = new PublicKey(
    'HZy4kyk53Zsrzgv84fuRmuXFNar9VAyJmqwVZtK1iEVy'
);

(Optional): If you did part1, replace programAddress with yours, forget what it was? Run this:

solana address -k target/deploy/joketoearn-keypair.json

UpdateApp.js:

...
// Solana Stuff
import { clusterApiUrl } from '@solana/web3.js';
import JokeArena from './components/JokeArena';
...

const App = () => {
  ...
    return (
      <ConnectionProvider endpoint={endpoint}>
        <WalletProvider wallets={wallets} autoConnect>
          <WalletModalProvider>
            <JokeArena network={endpoint}
          </WalletModalProvider>
        </WalletProvider>
      </ConnectionProvider>
    );
}
  • We create a new Component where we will do most of our work components/JokeArena.js
  • We pass the Devnet endpoint to our Component.

From here on, I will use code to illustrate the logic instead of including everything because as this is not a react tutorial, I will skip all the basic HTML/react stuff to focus on the exciting part. No worries, you can always refer to the complete code here later if needed github.com/mwrites/joketoearn.

components/JokeArena.js is our main file and will hold all the pieces together.

// My Deps
import Intro from './Intro';
import { fetchJokes } from "../utils/anchorClient"



const JokeArena = ({ network }) => {
    const wallet = useWallet();
    const [jokes, setJokes] = useState([]);


    const getAllJokes = async () => {
        const jokes = await fetchJokes(wallet, network);
        setJokes(jokes.flatMap(joke => joke.account));
    }

    useEffect(() => {
        getAllJokes();
    }, [network, wallet]);

    return (        
        jokes.map((item, idx) => (
          ... html stuff to display our jokes
        ))
    )
}

export default JokeArena;

First, the Dependencies:

  • import Intro is just a simple component to hold some welcome text and the <WalletMultiButton that we removed from the code that was generated inApp.js
  • import { fetchJokes } from "../utils/anchorClient is where the magic happens

The logic:

  • const wallet = useWallet() is how we get a reference to the user's wallet,
  • const jokes = awaitFetchJokes(wallet, network) is where the magic happens, we pass the wallet, and the network as this will be necessary during the communication with the blockchain

Intro is just here to hold our WalletMultiButton and some HTML stuff.

import { WalletMultiButton } from '@solana/wallet-adapter-react-ui';

const Intro = ({ wallet }) => {
    return (
      ...
      <WalletMultiButton className={"btn-wallet"} />
      ...
    )
}

export default Intro;

πŸ€“ Reading the Blockchain - Fetching Jokes

jokelist.png

Since we are making a frontend, we still need to create a client; blockchains are not black magic, it is still something that lives on the Internet, and we need a client to communicate with it.

Previously in JokeArena Component, we had this:

import { fetchJokes } from "../utils/anchorClient"

...
    const getAllJokes = async () => {
        const jokes = await fetchJokes(wallet, network);
        setJokes(jokes.flatMap(joke => joke.account));
    }

So time to implement the anchorClient from here on; we will focus exclusively on utils/anchorClient.js:

import { Connection, PublicKey } from '@solana/web3.js';
import { Program, Provider, web3, BN } from '@project-serum/anchor';
import { programAddress, connectionsOptions } from './config';



const fetchJokes = async (wallet, network) => {
    ... How?
}

export  {
    fetchJokes,
};

So how do we implement this fetchJokes? Back to this diagram:

create_joke_transactions_1.png

  1. We need to connect to the Blockchain.
  2. We need to talk to our program.
  3. We need to craft an instruction for our program.

1. First, we need something to Provide us a connection to the Blockchain somehow, add this function:

const getConnectionProvider = async (wallet, network) => {
    const connection = new Connection(
        network,
        connectionsOptions.preflightCommitment
    );
    const provider = new Provider(
        connection,
        wallet,
        connectionsOptions.preflightCommitment
    );
    return provider;
};
  • We configure a Connection to the `network. In this case devnet
  • We feed that connection configuration to a Provider.
  • Note the wallet when we create the Provider.

2. Now that we have a way to connect to the Blockchain, we need to talk to our Program, add this function:

const getProgram = async (wallet, network) => {
    // Get a connection
    const provider = await getConnectionProvider(wallet, network);
    // Get metadata about your solana program
    const idl = await Program.fetchIdl(programAddress, provider);
    // Create a program that you can call
    return new Program(idl, programAddress, provider);
};
  • We use the previous getConnectionProvider to connect.
  • Then fetchIdl using the programAddress of our Program, remember? In "A language called IDL," we explained that an IDL describes a program.
  • Finally, with that idl, we can create a javascript object representing our Program that we can normally use as if we were calling a function.

I said as if, but we are not calling our program directly as we saw in the above diagram. The reality is that we are crafting an instruction, but thanks to the anchor library, it feels like we are just calling some functions on a regular javascript object.

3. Finally, we can finish implementing our fetchJokes:

const fetchJokes = async (wallet, network) => {
    const program = await getProgram(wallet, network);
    const jokes = await program.account.joke.all();
    return jokes;
}
  • We get a hand on our program; our previous function has solved the connection between the wallet and our app.
  • Then we fetch all the accounts of type joke.

Account

For our purpose, we can assume that accounts behave like files. This is how Solana works with data; it stores everything into what it calls "accounts," an account is just a way to encode data into a buffer of bytes: more on this here blog.mwrites.xyz/your-first-solana-program#...

And voila, you can display jokes that are stored on a Solana Program!

npm start

(if you are using your own Solana Program Address from Part 1 - build your Solana Program, you probably have no data yet, that's fine; we will create jokes in the next section)

πŸ† ACHIEVEMENT - Reading Instructions

  • βœ… Understand how to read a Solana Program IDL.
  • βœ… Understand Accounts.

✍️ Mutating the Blockchain - Creating Jokes

Cool, we can display jokes. How about creating some? Fetching data is pretty simple with Solana, but creating or modifying data with Solana or any Blockchain is another story.

Why? One word: Permission Because modifying data in the blockchain requires signing. I explain in much more detail the why and how in Why do we need Wallet In Blockchain, but the reasoning goes like this:

  1. Blockchain is a distributed DB; it is a swarm of 🐝.
  2. Each node or 🐝 holds a copy of the DB.
  3. Since anyone can be a node and has a copy of the DB...
  4. ...We need a way to ensure permission, which is done by signing ✍️ transactions/instructions.

So reading Blockchain data does not require signing; that is why you can go to sol scan and look up someone's data. But when writing data, you must prove to the Blockchain that that piece of data belongs to you and thus have permission to modify it.

Let's see what the UI look like:

transaction-sign-gif.gif

πŸ’₯ Attack Plan:

  1. First, we will introduce another component called JokeEditor to do the boring HTML stuff.
  2. Secondly, We will add it inside our glue component: JokeArena.
  3. Then, we will call the appropriate function from anchorClient.js.
  4. Finally, we will implement the sendJoke function in `anchorClient.js.

Gluing Stuff Together

In src/components/JokeArena.js:

// My Deps
import Intro from './Intro';
import JokeEditor from './JokeEditor';
import { fetchJokes, sendJoke } from "../utils/anchorClient"
  1. We already had Intro.
  2. We are adding a new JokeEditor there is nothing special inside. It is just a react component that holds a text input and a submit button.
  3. We import sendJoke like for fetchJoke this is where all the exciting stuff will happen.

Still in JokeArena.js, add the new JokeEditor component (just a text area and a button) and give it the submitJoke function:

    return (
        <div className="jokearena-container">
            <Intro wallet={wallet}/>

             ... HTML Stuff to display our joke cards

            <JokeEditor wallet={wallet} submitJoke={submitJoke} />
        </div>
    )

Finally, the submitJoke function will be called when users tap the button and send their jokes:

const JokeArena = ({ network }) => {
    ...
    const submitJoke = async (joke) => {
        await sendJoke(wallet, network, joke);
        await getAllJokes()
    }
    ...

AnchorClient

Now that all the gluing is done, let's focus on utils/anchorClient.js:

const sendJoke = async (wallet, network, joke) => {
// how?
}

export  {
    fetchJokes,
    sendJoke,
};

First, as we did before with fetchJoke, we need to get a hand on our Solana Program:

const sendJoke = async (wallet, network, joke) => {
    const program = await getProgram(wallet, network);
}

The Revenge of IDLs

Then, we need to craft an instruction to call the function of my or your Solana Program. To do this, we need to look again at the idl of the Solana Program, like we did in the fetchJoke par. There is this fantastic tool called solaneyes which puts a UI on top of this command:

anchor idl fetch YOUR_PROGRAM_ADDRESS_HERE

Try it for yourself:

https://www.solaneyes.com/address/YOUR_PROGRAM_ADDRESS_HERE

Again if you have not followed [part1: Build Your Solana Program]:(blog.mwrites.xyz/your-first-solana-program), just use this solaneye link.

This is the IDL: image.png

And here, a better-looking version of the IDL:

image.png

A Story of Accounts

So there is an instruction called createJoke, which requires:

  1. A string for the text of the joke.
  2. 3 Accounts.

Let's start by the joke string, easy:

const sendJoke = async (wallet, network, joke) => {
    const program = await getProgram(wallet, network);
    await program.rpc.createJoke(joke, {

    });
}

Now, let's talk about the accounts:

What did we say accounts were again? Accounts are like Linux files; this is how Solana stores data. Remember, remember, remember: it has nothing to do with login or password, accounts = files (at least for our interest here).

Like your Linux system, Solana manages how data are organized into accounts (buffer of bytes). So, of course, Solana knows how to create space in the Blockchain; it does not need your help for this. But remember what we said earlier about how Blockchain is a distributed DB? Every node gets a copy of the data, and thus, Solana needs to figure out a way to restrict permission to some data or accounts.

So here, Solana does not require you to provide accounts but instead a signature/stamp that it will use to identify who owns which accounts for the soon-to-be-created or existing accounts.

So first, we create a signature that will be used for the soon-to-be-created account storing the joke's data:

const sendJoke = async (wallet, network, joke) => {
    const program = await getProgram(wallet, network);

    // Create an address for the soon-to-be created Account
  const jokeAccountKeypair = anchor.web3.Keypair.generate() // 1

    await program.rpc.createJoke(joke, {
        accounts: {
        jokeAccount: jokeAccountKeypair.publicKey, // 1
    },
    signers: [jokeAccountKeypair] // 1
    });
}
  1. We create a keypair
  2. We provide the public key associated with the account holding the joke data.
  3. We give the keypair to sign and prove that we own the public key for it.

Secondly, the createJoke instruction requires to know who will be the authority; the people or wallet controlling this newly created jokeAccount:

const sendJoke = async (wallet, network, joke) => {
    const program = await getProgram(wallet, network);

    // Create an address for the soon-to-be created Account
  const jokeAccountKeypair = anchor.web3.Keypair.generate()

    await program.rpc.createJoke(joke, {
        accounts: {
        jokeAccount: jokeAccountKeypair.publicKey,
        authority: program.provider.wallet.publicKey, // 2
    },
    signers: [jokeAccountKeypair]
    });
}

Have you noticed that we did not need to include our wallet in the signers? Your wallet is also part of the signers by default: it makes sense since the wallet is signing the whole transaction/instruction.

Finally, we add the systemProgram which is Solana itself!

const sendJoke = async (wallet, network, joke) => {
    const program = await getProgram(wallet, network);

    // Create an address for the soon-to-be created Account
  const jokeAccountKeypair = anchor.web3.Keypair.generate()

    await program.rpc.createJoke(joke, {
        accounts: {
        jokeAccount: jokeAccountKeypair.publicKey,
        authority: program.provider.wallet.publicKey,
        systemProgram: anchor.web3.SystemProgram.programId, // 3
    },
    signers: [jokeAccountKeypair]
    });
}

The Solana System Program, like all Solana Programs, are also accounts; they are just read-only accounts. And how can a Solana Program maintain its state if it is read-only? Well, with non-read-only accounts just for the jokeAccount.

By the way, why do we even need to pass Solana to itself? The logic is that Solana Programs are stateless, and you need to provide everything dependencies or data to them. But still... not sure why Solana wouldn't be able to find itself πŸ˜†? Maybe we could use a different programId or publicKey to identify a different version of the Solana Master Program? If you got insights about this, please drop a comment!

If you want to learn more about how the signing works and what it looks like on the Solana Program side, take a look at this part.

πŸ† ACHIEVEMENT - Mutating Instructions

  • βœ… Understand how permissions are handled using public / private key pairs.
  • βœ… Understand how Instructions are passed to a Solana Program

At this stage, we are done with the code you can now run:

yarn start

🏁 Finish Line - Paying Transactions

I tricked you, sorry. A wallet is not only for signing, but it is also a wallet, duh.. to interact with the blockchain, you also need to pay!! (Solana tokens)

So send yourself some good free money πŸ€‘; we call this airdropping, yes it's raining tokens, baby.

Screenshot 2021-11-23 at 21.24.13.png

Option A: Please tap on the wallet to copy the wallet address to your clipboard and paste it into that command (can be installed here)

solana airdrop 1 MY_WALLET_ADDRESS

If it fails, you probably went too far and tried to rob the chain; this is not a bug; this is to prevent spamming since tokens give you the power to interact with the chain. Someone with too many tokens at once might make annoying attacks on the chain.

Option B: If you have not installed Solana cli, you can use a faucet. Make sure to switch to the devnet first.

You got one token, don't go trying to sell your fake Solanas 🀣

Screenshot 2021-11-23 at 21.30.14.png

Now that you have some sol, you can finally submit that joke!

πŸ‘ RECAP:

  • πŸ† You deployed your first Blockchain Program on Solana.
  • πŸ† You know how to read any Solana's Program IDL.
  • πŸ† Understand what the f accounts are and how to use public/private keys to permission-lock them.
  • πŸ† Understand how Instructions are passed to a Solana Program.
  • πŸ„β€β™‚οΈ Understand what the workflow of a web3 developer is.
  • πŸ† Understand how Instructions are passed to a Solana Program.

You still didn't go to the 🌝, but you learned the difference between working with a Blockchain compared to traditional web services, spoke in Transactions instead of REST APIs, and grasped the idea of signing. Ethereum or any other Blockchain will be a piece of cake for you.

πŸ¦Έβ€β™‚οΈ If you want to see how to build a Solana Program, the one that we used in this article: checkout part1!

These, my friend, are skills that you can re-apply to any blockchain. If you want to dive deeper into Solana, please follow the Solana Series on my blog. There are also other excellent tutorials here:

TLDR: Try it live! Fullstack code here

Please subscribe for more web3 tutorials or drop a comment πŸ‘‡πŸ‘‡πŸΎπŸ‘‡πŸ» if you find this helpful or want to say hi!


Going Further

Go to the other side and build the On-Chain Program

Implement Tipping, here's an example in another project

Explore a different way of storing data using Program Derived Address

Going Further on Solana


References


About The Author

Let's Build on Solana!

Let's connect!

Did you find this article valuable?

Support mwrites by becoming a sponsor. Any amount is appreciated!

Β