SolanaプログラムにGIFカウンターを実装しよう!
今のところ、作成したプログラムでは何も起こりません。
データを保存するための変更を加えていきましょう!
今回作成するWebアプリケーションは、誰でもGIFを投稿することができます。
そのため、total_gifs
でGIFの投稿数の管理ができるとかなり便利です。
🥞 GIF カウントを保存する
GIFの投稿数を保存するための変数total_gifs
を定義し、誰かが新しくGIFを投稿した際に、total_gifs
の数値を+1
すれば良いだけです。
しかし、SolanaはEthereumとは異なり、「ステートレス」であるため、データを永久に保持することはできません。
ただし、Solanaは「アカウント」とやり取りができます。
Solanaの「アカウント」は「プログラムが読み書きできるファイル」のことを指します。
「ウォレットでアカウントを作成する」といった際に利用される「アカウント」とは異なるので、紛らわしいですが注意しましょう。
アカウントについての詳細はこちらを参照してください。
それでは、lib.rs
を以下のとおり修正していきましょう。
use anchor_lang::prelude::*;
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
#[program]
pub mod myepicproject {
use super::*;
pub fn start_stuff_off(ctx: Context<StartStuffOff>) -> Result <()> {
// アカウントへのリファレンスを取得します。
let base_account = &mut ctx.accounts.base_account;
// total_gifsを初期化します。
base_account.total_gifs = 0;
Ok(())
}
}
// StartStuffOffコンテキストに特定の変数をアタッチします。
#[derive(Accounts)]
pub struct StartStuffOff<'info> {
#[account(init, payer = user, space = 9000)]
pub base_account: Account<'info, BaseAccount>,
#[account(mut)]
pub user: Signer<'info>,
pub system_program: Program <'info, System>,
}
// 指定のアカウントに何を保存したいかをSolanaに伝えます。
#[account]
pub struct BaseAccount {
pub total_gifs: u64,
}
以下でコードの中身を詳しく説明していきます。
🤠 アカウントを初期化する
#[account]
pub struct BaseAccount {
pub total_gifs: u64,
}
ここでは、どんな種類のアカウントを作成し、その中に何を保持するかを定義しています。
今回はBaseAccount
がtotal_gifs
という名前の整数を保持することになります。
#[derive(Accounts)]
pub struct StartStuffOff<'info> {
#[account(init, payer = user, space = 9000)]
pub base_account: Account<'info, BaseAccount>,
#[account(mut)]
pub user: Signer<'info>,
pub system_program: Program <'info, System>,
}
ここでは、StartStuffOff
コンテキストで初期化する方法と保持する変数を実際に実際に指定しています。
[account(init, payer = user, space = 9000)]
では、BaseAccount
の初期化方法をSolanaに指示しています。
詳細は以下のとおりです。
1. init
では、Solanaにプログラムの新しいアカウントを作成するよう指示しています。
2. payer = user
では、アカウントの利用料金を誰が支払うかをプログラム側に指示しています(今回はこの関数を呼び出したユーザーです)。
3. space = 9000
では、9000バイトのスペースがアカウントに割り当てています(今回は9000バイトで十分です)。
なぜアカウントにお金を支払う必要があるのでしょうか?
実は、データの保存は無料ではないのです。
Solanaの仕組み上、ユーザーがアカウントの「レンタル料」を支払う必要があります。
ちなみに、「レンタル料」を支払わない場合は、バリデータのよってアカウントが削除されます。
これらの詳細についてはこちらを参照してください。
pub user:Signer <'info>
はプログラムに渡されるデータで、プログラムを呼び出したユーザーが、ウォレットアカウントを所有していることを証明するものです。
pub system_program:Program
はSolanaを実行するプログラムSystemProgramへの参照です。
SystemProgram
では多くのことができますが、主な役割はSolanaアカウントを作成することです。
pub fn start_stuff_off(ctx: Context<StartStuffOff>) -> Result <()> {
// アカウントへのリファレンスを取得します。
let base_account = &mut ctx.accounts.base_account;
// total_gifsを初期化します。
base_account.total_gifs = 0;
Ok(())
}
最後に、start_stuff_off
関数内でContext<StartStuffOff>
を実行して、StartStuffOff
コンテキストからbase_account
を取得する処理を実行します。
👋 アカウントデータを取得する
JavaScriptの世界でもアカウントデータを取得できるようになったので、myepicproject.js
を以下のとおり更新しましょう。
const anchor = require("@coral-xyz/anchor");
// 以下の処理に必要なSystemProgramモジュールを用意します。
const { SystemProgram } = anchor.web3;
const main = async () => {
console.log("🚀 Starting test...");
// フロントエンドと通信するためにプロバイダを再度作成して設定します。
const provider = anchor.AnchorProvider.env();
anchor.setProvider(provider);
const program = anchor.workspace.Myepicproject;
// プログラムが使用するアカウントのキーペアを作成します。
const baseAccount = anchor.web3.Keypair.generate();
// start_stuff_off を呼び出し、必要なパラメータを渡します。
let tx = await program.rpc.startStuffOff({
accounts: {
baseAccount: baseAccount.publicKey,
user: provider.wallet.publicKey,
systemProgram: SystemProgram.programId,
},
signers: [baseAccount],
});
console.log("📝 Your transaction signature", tx);
// アカウントからデータを取得します。
let account = await program.account.baseAccount.fetch(baseAccount.publicKey);
console.log("👀 GIF Count", account.totalGifs.toString());
};
const runMain = async () => {
try {
await main();
process.exit(0);
} catch (error) {
console.error(error);
process.exit(1);
}
};
runMain();
anchor.web3.Keypair.generate()
では、BaseAccount
用の認証情報を作成しています。
program.rpc.startStuffOff
では、lib.rs
で指定したpub struct StartStuffOff
のパラメータを渡しています。
await program.account.baseAccount.fetch(baseAccount.publicKey);
console.log("👀 GIF Count", account.totalGifs.toString());
ここでは、作成したアカウントを取得して、totalGifs
にアクセスしています。
anchor test
コマンドを実行すると以下のように表示されます。
🚀 Starting test...
📝 Your transaction signature 44Ufkfyq56kHkYeahViFrJPwBV5w99kMLLRY9NbRfRWA7PjBcLVfC9GLvsceW9YhSc39QwrHcWaBMmoEHhdkcaCx
👀 GIF Count 0
GIF Count
が0になっていますね。
これで、実際にプログラムを呼び出し、Solanaチェーン上にデータを保存することができました。