Creates Waffle 🧇
-
Rust (rustup 1.26.0)
-
Solana CLI (solana-cli 1.16.7)
-
Anchor CLI (anchor-cli 0.28.0)
-
yarn
# Using npm global dependencies. npm install -g yarn # Using homebrew on Mac. brew install yarn # Using apt on Linux apt install yarn
-
Anchor Program Setup
-
Initialize
anchor init waffle-maker
-
Change Cluster to Localnet
solana config set --url localhost
-
Build and Deploy (Don’t forget to
cdinto the directory)anchor build
Run
solana-test-validatorin another terminal, before running below command, only if you are on localhostanchor deploy
-
Replace program id in lib.rs and Anchor.toml
-
Anchor Test
anchor test
-
-
Program
In
waffle-maker/programs/waffle-maker/src/lib.rs, Removestruct Initializeand the functioninitialize-
Waffle Account : We define a
Wafflestruct and declare it as anaccountusing#[account]provided by Anchor. We will store author’s Public Key, timestamp and the topic of the waffle in this Account.#[account] pub struct Waffle { pub author: Pubkey, pub name: String, }
-
Rent and Size of Waffle: We have to calculate the size of the
WaffleAccount as we will be paying Rent to store our account on chain.
-
Discriminator is always added before every Account, to define which Account it is.
-
Rest others are the sizes of the respective fields of the
WaffleAccount. -
In an Account, there should be a 4 byte extra space for any String, if present. This extra space defines the length of the String.
-
Right now, we have not defined the space for
nameas it will be dynamic based on user inputs.const DISCRIMINATOR_LENGTH: usize = 8; const PUBLIC_KEY_LENGTH: usize = 32; const STRING_LENGTH_PREFIX: usize = 4;
-
Implementation Block for Waffle:
implblocks of astructstores all the functions and constants related to that struct. We have calculated the length of theWaffleaccount. There is also a functionnewwhich creates an instance of theWaffleaccount. These will be handy when we write theCreateWaffleinstruction.impl Waffle { pub const LEN: usize = DISCRIMINATOR_LENGTH + PUBLIC_KEY_LENGTH + STRING_LENGTH_PREFIX; pub fn new(author: Pubkey, name: String) -> Self { Waffle { author, name } } }
-
Create Waffle instruction: In Anchor, we need to define a
structfor every instruction, it contains all accounts, programs associated with the instruction and the signer. Here:
-
waffleis an Account of typeWaffle -
authoris theSigner(An Anchor Type) -
system_programisProgram, it has instructions to create an Account, as we will be creating theWaffleaccount, we have to includesystem_program -
<'info>is a lifetime, it means that all the contents will have the same lifetime asCreateWaffle#[derive(Accounts)] pub struct CreateWaffle<'info> { pub waffle: Account<'info, Waffle>, pub author: Signer<'info>, pub system_program: Program<'info, System>, }
- Account Constraints: We need to still add some constraints inside the
CreateWafflefunction.
-
waffleaccount getsinit_if_neededas a constraint as it will be initialized the first time and also if we re-intialize it again, it wont do. We also define who will be paying to create this account i.e.author. And finally we allot the space required for the account usingWaffle::LEN() + name.len().Waffle::Len()is from theimplblock which we wrote before.namecomes from the user and to add that in the instruction struct we use#[instruction(name: String)].seedsconstraint is used for Program Derived Address, it takes “waffle” and thenameof the waffle. To make an account a PDA, you need seeds. -
authoris givenmutas constraint as when it pays for the account creation, some Sol will be deducted and hence this account has to be mutable.#[derive(Accounts)] #[instruction(name: String)] pub struct CreateWaffle<'info> { #[account( init_if_needed, payer = author, space = Waffle::LEN + name.len(), seeds = [b"waffle", name.as_bytes()], )] pub waffle: Account<'info, Waffle>, #[account(mut)] pub author: Signer<'info>, pub system_program: Program<'info, System>, }
-
To add
init_if_neededas a feature, inwaffle-maker/programs/waffle-maker/Cargo.toml[dependencies] anchor-lang = {version = "0.28.0", features = ["init-if-needed"]}
-
Implementing
CreateWafflefunction (Should be added insidemod waffle_maker) : This is the function which gets called when we create a waffle, it takesCreateWafflestruct asContext.pub fn create_waffle (ctx: Context<CreateWaffle>, name: String) -> Result<()> { Ok(()) }
-
Define Errors (Outside
mod waffle_maker): This enum will be handy to check errors.#[error_code] pub enum WaffleError { #[msg("Waffle name can be 30 characters long.")] NameTooLong, #[msg("You need to name the Waffle.")] NameEmpty, }
-
Handle Errors (Inside
create_wafflefunction) : We check for errors and accordingly take actions.require!(name.chars().count() > 30, WaffleError::NameTooLong); require!(name.chars().count() < 1, WaffleError::NameEmpty);
-
Assign value (Inside
create_wafflefunction) : After error checking, we assign value inside the waffle account.ctx.accounts.waffle.author = ctx.accounts.author.key(); ctx.accounts.waffle.name = name;
-
Adding a log message
msg!("Waffle {} created", &ctx.accounts.waffle.name);
-
Switch to Devent
solana config set --url devnet
In
waffle-maker/Anchor.tomlreplace cluster to Devnet[provider] cluster = "Devnet"
Also add
[programs.devnet] waffle_maker = <program-id-in-string>
-
Anchor Build and Deploy
anchor build
anchor deploy
-
Copy the IDL file
waffle-maker/target/idl/waffle_maker.json
-
-
Whole Code
waffle-maker/programs/waffle-maker/src/lib.rsuse anchor_lang::prelude::*; declare_id!("B6cnkQKZeNT4VEmkjfNfisL4UzobUfL5wU93xi16DSTU"); #[program] pub mod waffle_maker { use super::*; pub fn create_waffle (ctx: Context<CreateWaffle>, name: String) -> Result<()> { require!(name.chars().count() < 30, WaffleError::NameTooLong); require!(name.chars().count() > 1, WaffleError::NameEmpty); ctx.accounts.waffle.author = ctx.accounts.author.key(); ctx.accounts.waffle.name = name; msg!("Waffle {} created", &ctx.accounts.waffle.name); Ok(()) } } #[account] pub struct Waffle { pub author: Pubkey, pub name: String, } const DISCRIMINATOR_LENGTH: usize = 8; const PUBLIC_KEY_LENGTH: usize = 32; const STRING_LENGTH_PREFIX: usize = 4; impl Waffle { pub const LEN: usize = DISCRIMINATOR_LENGTH + PUBLIC_KEY_LENGTH + STRING_LENGTH_PREFIX; pub fn new(author: Pubkey, name: String) -> Self { Waffle { author, name } } } #[derive(Accounts)] #[instruction(name: String)] pub struct CreateWaffle<'info> { #[account( init_if_needed, payer = author, space = Waffle::LEN + name.len(), seeds = [b"waffle", name.as_bytes()], bump )] pub waffle: Account<'info, Waffle>, #[account(mut)] pub author: Signer<'info>, pub system_program: Program<'info, System>, } #[error_code] pub enum WaffleError { #[msg("Waffle name can be 30 characters long.")] NameTooLong, #[msg("You need to name the Waffle.")] NameEmpty, }