+
+ +
+ +

Write to storage

+

In our previous tutorial, we've discussed how to initialize an account so that we could persis data in storage. Now let's discuss how to read and write to an account.

+

Try it out

+
anchor new day_17
+
+

let's add an extra function set() to the previous code, the rest remains unchanged:

+
use anchor_lang::prelude::*;
+use std::mem::size_of;
+
+declare_id!("AoiMpugaS2QZJP38Wvrcy3KVDFNayy4oPV3TZDnFB3ns");
+
+#[program]
+pub mod day_17 {
+    use super::*;
+
+    pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
+        Ok(())
+    }
+
+    // new code
+    pub fn set(ctx: Context<Set>, x: u64) -> Result<()> {
+        ctx.accounts.my_storage_set.x = x;
+        Ok(())
+    }
+}
+
+// new code
+#[derive(Accounts)]
+pub struct Set<'info> {
+    #[account(mut, seeds=[], bump)]
+    pub my_storage_set: Account<'info, MyStorage>,
+}
+
+#[derive(Accounts)]
+pub struct Initialize<'info> {
+    #[account(init, payer=signer, space=size_of::<MyStorage>()+8,seeds=[], bump)]
+    pub my_storage_account: Account<'info, MyStorage>,
+
+    #[account(mut)]
+    pub signer: Signer<'info>,
+
+    pub system_program: Program<'info, System>,
+}
+
+#[account]
+pub struct MyStorage {
+    x: u64,
+}
+
+

How function Set() works

+

Below, we have slightly reordered the code to show the set() function, the Set struct and the MyStorage struct close together.

+

image-20240824223515216

+

when function set() is called, the myStorage account will be passed to my_storage_set, and function set will load the storage, write the new value of x, serialize the struct, then store it back.

+
    +
  • mut: indicates this account can be modified.
  • +
  • seeds and bump: are used to derive the address of the account we will be modifying.
  • +
+

let's update the test case, call set after account initialization:

+
import * as anchor from "@coral-xyz/anchor";
+import { Program } from "@coral-xyz/anchor";
+import { Day17 } from '../target/types/day_17';
+
+describe("day_17", () => {
+  anchor.setProvider(anchor.AnchorProvider.env());
+  const program = anchor.workspace.Day17 as Program<Day17>;
+
+  it("should initialized!", async () => {
+    // same to previous
+    // account initialization
+
+    // call set()
+    const value = new anchor.BN(200)
+    const tx = await program.methods.set(value).accounts({
+      myStorageAccount: myStorage
+    }).rpc();
+    console.log('tx: ', tx);
+  })
+})
+
+

it works as expected, we will fetch x later on.

+

image-20240824223952932

+
myStorage account:  J2Xy2SHhchzvK7drEGcM2FnL3xTeSEKMQNf3NcAyE3p3
+
+

Viewing account outside program

+

We can view the account data with the following Solana command line instruction:

+
# solana account <MyStorage address>
+solana account J2Xy2SHhchzvK7drEGcM2FnL3xTeSEKMQNf3NcAyE3p3
+
+

image-20240824231636735

+

The first 8 bytes 1c f2 3b 85 43 19 31 before c8 are the discriminator, we can ignore as for now.

+

Viewing account inside program

+

Reading our own storage value inside the Rust program however, is straightforward.

+

We add the following function

+
    pub fn get(ctx: Context<Get>) -> Result<()> {
+        let x = ctx.accounts.my_storage_get.x;
+        msg!("Value of x: {}", x);
+        Ok(())
+    }
+
+

add new Get Account

+
#[derive(Accounts)]
+pub struct Get<'info> {
+    pub my_storage_get: Account<'info, MyStorage>,
+}
+
+

Note that we don't use mut in macro cos it's read only, let update the test case and test

+
  it.only("should get value", async () => {
+    const seeds = []
+    const [myStorage, _bump] = anchor.web3.PublicKey.findProgramAddressSync(seeds, program.programId);
+    await program.methods.get().accounts({
+      myStorageGet: myStorage
+    }).rpc();
+  })
+
+

get 200 as expected!

+

image-20240824232746102

+

cool!

+

Key takeaways

+
    +
  • don't forget to pass myStorage account during the call
  • +
+ + + + +
+ +
+