A simple blog platform, powered by Solana with a SvelteKit front end.
- Solana (Rust)
- Anchor (Rust macros + IDL generation)
YouTube video on building the Rust portion:
Video: SolBlog - Solana blog platform built with Anchor
In this tutotial, we will create a simple short form yet fully functional blog with the data saved on Solana. We are going to do this using the Anchor framework to illustrate how to use Anchor.
We're building a blog publishing platform because it is a simple web format that most people are familiar with. Blogs have a simple data structure: A blogger and their blog posts. This example of storing data on the Solana network is an easy way to learn.
Building Solana programs in Rust language can be tough. Anytime you want to save or retrieve data from an account, you need to think about packing/unpacking the data, serializing/unserializing the data and formats, which are all a real pain.
Anchor abstracts away from the low level construction of accounts, the packing and unpacking, and modifying the interfaces to your Solana program. It does this by bundling the boilerplate into rust macros! This gives users of Anchor a lot of shortcuts and speed to building Solana programs.
- Complete Solana 101 Pathway
- Basic understanding of Rust & Solana will be a plus.
- Basic Command Line Interface usage
We will use these installed tools throughout the tutorial:
- A Linux command line interface
- Anchor and Rust installed in accordance with the Setup section below
- Phantom.app Solana Wallet, with an address you are comfortable using on DevNet (can make a new address in Phantom if you like)
It's important to understand that Anchor is new, and the API may change as development proceeds. These directions are valid now, but might differ in the future if/when there are breaking changes. But the general overall concepts remain unchanged.
I used v 0.16.1 for this tutorial.
Getting started with Anchor is fairly straightforward, and you can follow the setup instructions on the Anchor website.
For test environments, you have the choice of installing a local validator or using the Devnet online. We will be deploying to the Solana Devnet to practice deploying in real life. As the saying goes, "deploy early, deploy often"! The last thing you want is for your code to work in a development environment, and not in production.
Once you have installed all the Anchor dependencies, it's time to initiate a project!
Initiating a project is pretty straightforward. In the command line -- use Windows Subsystem for Linux 2 (WSL2) if you are on Windows, as the Berkeley Packet Filter (BPF) compiler portion of the Solana toolchain doesn't work in regular Windows.
Don't make a new folder! Anchor will create a new one for you :)
This creates a folder and puts the anchor starter in that directory. From here, we can build our app.
The folders we are interested in the most to start are:
By default, Anchor has put some basic starting code in there for us.
Let's break down what we see.
After including the anchor library, the program public key has this placeholder:
That default key is NOT our public key for your build. You need to generate that for your program. We generate it once, and then include it, and can make as many changes as we need to before deploying.
Since that's not our key, let's fix that now and generate our key.
As that builds (it'll take a minute), watch your target folder as it is pretty empty right now.
Watch how the target folder changes once build completes it is a bit beefier:
Our newly generated code public key is in that new
./target/deployfolder, go ahead and check it out!
To show our program public key which we will use as our id, simply run:
Which shows us our unique key:
Your key will look different, that's ok. Anchor will generate a unique keypair for everyone, which is how we can uniquely identify our programs from one another.
If you're following along in this tutorial repo, I've placed a shortcut to this script in the
package.jsonfile, so you can simply run
npm run show-keyin the terminal (as long as it's WSL2/Linux -- the rust toolchain doen't work in Windows).
Copy-and-paste your key and replace that default
We will also need to include this same Program ID in the client side, in our
The next code block under
#[program]is our program's functions, how we make the program do anything. The function names here are lowercase snake_case.
#[derive(Accounts)]struct is the same name as in the
program, but in
snake_caseis used in program above).
The next code block under
#[derive(Accounts)]is a going to be struct that describes the account itself and enables us to access fields from the account struct (which is non-existant at this point). Let's create it:
We created the third style code block, which is an Account struct which is a Solana account that holds our data. We will save 2 pieces of data to this account:
authority: you need to have this keypair in order to make posts,
latest_post: the, well, the lastest blog post.
Now we have the three Anchor blocks we need to make our blog:
But right now our program doesn't do anything, because our program methods are empty.
initializewe want to set our blog account
authority. We will set
authorityto the same public key as the keys that signed the transaction.
BUT, in order for us to have access to
- BlogAccount must be a created account
- BlogAccount must paid for by someone
- BlogAccount must have enough space allocated to store our data
initializemust have access to the
authorityfield on BlogAccount
authoritymust sign the
Anchor makes this easy for us using their macros:
#[derive(Accounts)]wire up all the connections we need in order to use
initilizefunction. So now we can use
authorityin our function:
Once Anchor has helped us
initilizeour account and set the blog
authoritynow we want to actually save some data to our BlogAccount. We follow similar steps:
First we create our Anchor glue by writing an additional
#[derive(Accounts)]struct. Again, we want access to
blog_accountso we need to include that. We are changing
blog_accountneeds to be mutable, hence the
#[account(mut)]but we also need for the transaction to be signed by the blogger, so it also needs to include
Signer. The result looks like this:
Now that this Anchor struct has given us access to these fields, we can use them in the first code block under
make_postfunction gets broken down in a few steps here:
First we take our
new_postas an argument to the function:
Our blog posts are going to be Strings, but we don't know how long these strings are going to be. Yes, we could pad them to a certain size, but a String is really just an array of bytes. In Rust we can describe our array of bytes as a Rust Vector of u8 bytes (
Next we take the
Vec<u8>and convert it to a String slice (
&str) with a bit of error handling included, in case we don't get valid UTF8:
Lastly, we print the blog post to the Program Log:
The reason we print to the program log is: our BlogAccount only saves the latest post... so what if we want to see previous posts? We can simply pull up prevously saved logs and we'll have it. Alternatively we could create an Account for every post, but that's a lot of "costly" overhead for very little benefit, whereas asaving to the Program Log is "Transaction priced" ($0.00025) and we only need to pay for one account (which is super cheap, but why pay more?).
Lastly, we grab the
accountsfrom the context (
ctx) and pick
blog_accountas the one we're going to use (we only have one, but you could have had more) so we can also save the most recent post to the Account (
Our Rust Solana Program is complete, written in Rust! Now that we're done, we need to build again so that the Solana build uses our most recent code:
In project root directory, run:
Make sure you run anchor build in your project's root folder, anchor will take care of the rest.
Now that our Rust Program has been written with the help of Anchor, it's time to deploy to the Devnet.
To deploy the anchor program on devnet, a small helper script to setup some keys, fund via airdrop, then use anchor deploy to deploy to the devnet would sure be great. That's included with this tutorial!
The deploy code is saved at
./deploy.js. Let's walk through the necessary parts to deploy to Solana using anchor.
We need 2 keypairs in order to deploy:
- Our Program Authority keypair, and
- Our Program Keypair
We can generate a Program Authority keypair by using the solana-web3.js library:
To deploy our code, we need cryptocurrency SOL in our newly created account. On Devnet, we can call an airdrop to fund our account.
I like to use the library's constant
LAMPORTS_PER_SOLbecause that way you can ensure you get a full SOL or 2, just multiply by however much you need. For this deployment, we should need between 2 - 3 SOL, so let's drop 5 SOL in there just to make sure we aren't hassled with airdropping more.
Now that we have our Program Authority Account keypair created and funded, let's save it as a keyfile (json file) so that Anchor can access it from the command line:
The second keypair that we need is our program keypair. This was generated for you when you ran
anchor buildand is saved in
target/deploy/solblog-keypair.json. We need to get the programId from that keypair. We could just copy and paste, or we can retrieve the keys programmatically liek this:
Now we have both keys that we need to deploy!
The command in Anchor to deploy is
anchor deployand we want to call this from our deploy script. Let's use Nodejs to spawn a sub process to call it programatically:
If we want to update our program after the initial deployment, we can use
anchor upgradeinstead. We would need to specify what program we are upgrading, and specify the built code we want to replace it with. So we get:
A full functioning version of the complete code is included with this tutorial.
Now that we've built our
deploy.jsscript, we can run the script using
nodeor use the shortcut script in
package.jsonwhich is convenienlty run by:
The deploy script creates a new keypair to pay for the deployment for you, funds it with some SOL, and deploys it to the Devnet.
Since this tutorial is about Anchor, I'll gloss over some of the finer details in that
deploy.jsscript, and jump straight to the juicy Anchor points, which are:
When choosing to deploy, you can use Solana's Devnet or choose Figment's DataHub as well for deploying the program.
To use Figment's Datahub, Go to https://datahub.figment.io -> Make an account (It's free) -> Select Solana from Protocols -> Get the API Key and endpoint on your dashboard. Now you can use the same here in this tutorial by setting the cluster to Figment's datahub!
You may want to consider Figment's endpoint, as Public endpoints, for example native Devnet, have some limitations like rate limits, latency, per day req. limit/quota etc. However, DataHub resolves all these problems and breaks barriers that devs come across while scaling their projects. I myself ran into a couple of these issues working with Devnet, so it's a valid considering for you too.
The first run through, the deploy script uses
anchor deploy, whereas in subsequent deploys with the same program, it will use
anchor upgradewith all the required flags included for convenience:
That is because on subsequent deploys, we want Anchor to upgrade our program using the same
programIdand program authority so everything except the code stays the same. This is important because we want our program address to stay the same so users don't have to change to a new address every time we upgrade our software.
At the end, the
deploy.jsscript will also save your keys to the dapp-starter style .json file for easy reference. But you'll see that I have also saved the keyfiles as
.jsonbytes so they can be used by Anchor commands in the command line, since we use Anchor via CLI and not programmatically.
When you run this
deploy.jscode, you can see the result is that Anchor has filled in our basic starting point with all the necessary glue to make a Solana program work, and now it's compiled ready for the blockchain as a 149kb in size file, and real life deployment would costs about 2 SOL to deploy.
Now that the program is deployed to Solana Devnet, we can access it from the client code inside our Svelte App, and start blogging.
Let's build the client front end to interface with our program!
Anchor is every bit about the client side as it is about the program side. Our program is complete and our IDL is built, so all that is left is for us to build a front end to use it all.
Although I have chosen to use Svelte for this, any front end can be used. We will put our anchor client code in a file named
anchorClient.jswhich can be used by any framework or pure vanilla JS.
So I'll get straight to the
anchorClient.jssetup and then cover the SvelteKit integration for those who wish to stick around for that part.
We have our IDL.json which describes how we use our app, but we need to build some handlers to call the remote procedure calls (RPCs).
Let's take a look at the
initialize()function, now from the client side.
There are a few references to assets that we haven't coded in yet, like
this.programbut we'll make that in our setup constructor.
The RPC nature of Anchor means that all our functions are exposed through
program.rpc.<method name>. So our make_post call looks very similar:
In order for these calls to work, we need this
this.programthat you see used everywhere, so let's take care of that.
programusing a call to the class constructor:
Let's dissect what's going on here.
In order to start up
anchor.program, we need three things:
- ProgramID, and
- Wallet Provider
jsonfile) is saved alongside our rust program at:
In SvelteKit, which uses Vite, we can import the
jsoninto our code by simply doing:
If you're using a different framework for front end, you may need to change this. But for this tutorial, it works.
publicKeyof our program keypair that we generated when we ran
anchor build, remember that?
Similar to the previous
jsonfile, we can bring this
programIdinto our code by importing it:
When you want to use a
programin production, instead of calling
getDevPgmId()you would simply pass in the
programIdto the constructor.
Lastly, we need a Wallet
Provider. Anchor gives us the option of making a provider using:
Connection is straightforward enough, we just use the Solana Web3 library (which is re-exported by Anchor) and pass in one of the Solana network endpoints, such as devnet:
Wallet, Anchor only provides a
Nodejswallet. But since we want our code to run in the browser, we either need to provide a
keypairor a mapping to a wallet provider, such as Phantom wallet. For ease of simplicity, I chose Phantom Wallet for the wallet provider.
WalletAdaptorPhantomsimply maps the Phantom functions to what Anchor loosk for:
Which means making a wallet essentially becomes:
...with some fallback in case Phantom isn't the wallet of choice. The user has the option of passing in a keypair, or the code will generate a random keypair for use as a backup. In production, users will want to use a wallet such as Phantom, but in Dev mode we can use made up keys, because we can call
airDrop(publicKey)to fund our accounts.
So now that we have a connection, and a wallet we get a provider, and with the provier we get a
programrpc client, and we can make our calls. Phew!
The rest of the app integrates both
makePostas well as
solana-web3.jscalls to interact with the program.
You can use Anchor and these tutorial libraries with any framework, such as React, Vue, or Svelte. I chose to demonstrated this in Svelte, below are some front-end tips for working with the libraries, through a Svelte lens. But they can be applied to any other framework too.
After setup and focusing on the Anchor RPC and Solana-Web3.js portions of the client side code, we can see a bit of the Svelte Setup, which is pretty standard and easy from the Svelte website:
The Svelte setup is simply:
Choose the full app defaults for the install step.
There are a few gotchyas that you might run into while trying to use the Solana or Anchor libraries in client side browser code.
Since Anchor uses borsh, there may be a small hack you need to add in order to get the Global varibale to work. In Svelte, if we paste something in our page layouts, it'll apply to all layouts, so we'll add our hack here to make things work with the imported Anchor library:
The other issue is the solana / anchor code is NOT isomorphic, which means it doesn't play equally nicely in Nodejs and the browser. The way to force frontend frameworks like Svelte to use the Browser version only (and skip the whole Server Side Rendering, or SSR) is to
importin the browser side code, in Svelte's case, in onMount():
loadAnchorClient()is defined in
Embedding loading of our anchor Client into the Browser side ensires that the browser version of any non-isomorphic libraries gets loaded, and we don't get any nasty errors.
There are a number of keypairs that are used throughout this whole process, and it can get a bit confusing:
- ProgramId (saved in ./target/deploy/solblog-keypair.json)
- Program's authority: Pays to deploy and upgrade the program. it's the wallet provider (program.wallet.publicKey)
- Transaction fee payers (can be the same as the program authority, though it could be different too)
- The blog posting authority (often the same as payer & authority)
- The Blog Account (key is used as address only, the programId actually OWNS this account. Once initialized, the private key is useless because the account is owned by the programId and only the programId can edit the account)
Recall that ProgramId is created during the first
Program upgrade authority keys are creates and funded during the first deploy call
npm run deployin
The BlogAccount key is created on the fly during intilization, but if there was a reason you wanted to pick your key, it could be passed in, but that's pretty extra.
In the end, navigating to
npm run devwill start up the Svelte App and get you to the home screen.
You can use
npm run dev3654if using the exact tutorial code repository.
If you are using Visual Studio Code as your IDE and you want a shortcut, you can also just click in the "npm" tab to "run dev".
Open up your browser to
http://localhost:3000and play around!
Please remember that this code is not intended for production! There are still a few other things to consider if you wanted to deploy this to mainnet, such as more robust wallet integration and unit testing. However this tutorial has given you the basics to build on top of!
The blog program is a very basic smart contract. In what ways could you make it do more? You can now keep experimenting by adding other features like a bio or about section. Or you could try next to integrate Solana's "Cross Program Invocations" (CPI) to get the Blog program to call other programs, perhaps triggering an event in someone else's program based on a post (perhaps a token event, such as minting or burning a token?). The only limit is your imagination. What would you choose as next steps?
This tutorial was written by Doug Anderson. Doug is a computer engineer and retired Naval Engineering Officer in the Royal Canadian Navy. Get in touch with Doug on twitter and elsewhere under @DougAnderson444
- Web3.js Library https://solana-labs.github.io/solana-web3.js/
- Anchor Website https://project-serum.github.io/anchor/
- Anchor Rust Docs https://docs.rs/anchor-lang/0.16.1/anchor_lang/
- Credit to Decentology DappStarter for ideas on how to programmatically deploy a Solana program https://dappstarter.decentology.com/
- Figment Solana RPC API https://docs.figment.io/network-documentation/solana/rpc-and-rest-api
- Figment DataHub https://datahub.figment.io/