solana 開発勉強日誌6　solana-twitterを作る＊前編

SOLANAの開発勉強シリーズ6
solana-twitterを作る＊前編
<br>

正式な名称（アカウントやアドレス）の表記揺れあります
自分なりにわかりやすく、開発勉強日誌6の中で統一させてるつもりなので注意してください
<br>


めちゃくちゃ簡単なDApps（webとブロックチェーンが合体したやつ）を作っていく

１回自分でやってみればそこまで難しいものではないが、依存関係の衝突やOSの違いで、チュートリアルでもコピペで簡単に動くものがなかった。バージョンが違うとコマンドが対応してなかったり普通にする
<br>
ユウキさんがNOTEを書いてくれているの本当にありがたい！


https://note.com/standenglish/n/n89d4d6d91f7a


<br>
<br>
<br>
<br>
<br>



下記のチュートリアルをやっていく


https://lorisleiva.com/create-a-solana-dapp-from-scratch/what-are-we-building


 <br>
<br>
yarnをインストールしてない人はインストール

https://classic.yarnpkg.com/lang/en/docs/install/#mac-stable



<br>
まず各種version確認（ターミナルで実行

```
anchor --version
solana --version
rustup --version
rustc --version
cargo --version
yarn --version
```
今回の実行環境（macOS

コピペで動くはずだけど、依存関係が大変だった
 Windowsを使っている人は動かないかも
 
```
anchor-cli 0.26.0
solana-cli 1.18.24 
rustup 1.27.1
rustc 1.81.0
cargo 1.81.0 
yarn　1.22.22
```





  <br>

 各種インストールはsolana公式からどうぞ


 https://solana.com/docs/intro/installation



 <br>
 <br>
 <br>
 <br>
 
```インストールしたsolanaのCLI（solana --version）をローカル環境に設定する為のコマンド.
solana config set --url localhost
```

 ```初めてsolanaのCLIを実行した人はエラーが出る、すでにキーペアがある人は自分のウォレットアドレスが表示される.
solana address
```


 ```初めてsolanaのCLIを実行した人はキーペアを作成。すでにある人はスルー.
solana-keygen new
```


```ターミナル等で適当なフォルダに移動（今回はデスクトップを想定.
cd desktop
```
```anchorのコマンドを使ってプロジェクトを作成、プロジェクト名solana-twitter.
anchor init solana-twitter
```
```solana-twitterフォルダへ移動.
cd solana-twitter
```


```solana-twitterのプログラムをCompilする.
anchor build
```
anchor buildを実行すると、deployフォルダにsolana_twitter.so、deployするプログラムのキーペアパス（秘密鍵）が作成される。このへんはsolanaは全てがアカウントだからと覚えてたら良い。そして、その辺をうまくやってくれるのがanchor




```solanaのブロックチェーンをローカル環境で起動する（別のターミナル等で実行.
solana-test-validator
```

```anchor deploy（さきほど作成したsolana_twitter.soをローカル環境solanaにデプロイする.
anchor deploy
```
  <br>
  <br>
  <br>
  <br>


![スクリーンショット 2024-09-29 19.56.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573208/34950e0d-605b-c94e-ad3a-28bb86123015.png)


anchor deployが完了した。
  <br>


anchor build、anchor deployで作成した
<br>
solana_twitter.so（プログラムファイル）とsolana_twitter-keypair.json（秘密鍵）は
<br>
自分のウォレットを設定し作成した為、自分のウォレットが親、
<br>
solana_twitter.soが子供のような関係。
<br>
solanaは全てがアカウントなので、solana_twitter.soにもアドレスと秘密鍵がある。
<br>
権限は親である自分のウォレット（owner）にある。
<br>
solana_twitter.soと対話する（プログラムを実行する）には2箇所ある
<br>
アドレス欄をsolana_twitter.soのアドレスに変更する必要がある

<br>
<br>
anchor deployで実行した時表示されたProgram Id:がsolana_twitter.soのアドレス
<br>
<br>


/Users/###/Desktop/solana-twitter/programs/solana-twitter/src/lib.rs
/Users/###/Desktop/solana-twitter/Anchor.toml
<br>

上記2箇所をsolana_twitter.soのアドレスに変更する
変更したら再度build.deployを実行する

<br>


```
anchor build
anchor deploy
```
上記が完了したら、solana_twitter.soのテストをする

```
anchor run test
```

```実行結果.
  solana-twitter
Your transaction signature 5xXr4Y4s2x4mKPxuXaZrET4DvQi8kFZA4gFfY6U5GwaUC3zW8BeRNBJyfgJM2TC333333333333
    ✔ Is initialized! (609ms)


  1 passing (612ms)

✨  Done in 2.51s.
```

上記まで完了したら、


```ローカル環境のvalidatorをresetして再起動させる.
solana-test-validator --reset
```

/Users/###/Desktop/solana-twitter/programs/solana-twitter/src/lib.rs
/Users/###/Desktop/solana-twitter/tests/solana-twitter.ts

２つのコードを書き換える

```lib.rs
use anchor_lang::prelude::*;

declare_id!("#####solana_twitter.soのアドレスに書き換える######");

#[program]
pub mod solana_twitter {
    use super::*;
    pub fn send_tweet(ctx: Context<SendTweet>, topic: String, content: String) -> Result<()> {
        let tweet: &mut Account<Tweet> = &mut ctx.accounts.tweet;
        let author: &Signer = &ctx.accounts.author;
        let clock: Clock = Clock::get().unwrap();

        if topic.chars().count() > 50 {
            return Err(ErrorCode::TopicTooLong.into())
        }

        if content.chars().count() > 280 {
            return Err(ErrorCode::ContentTooLong.into())
        }

        tweet.author = *author.key;
        tweet.timestamp = clock.unix_timestamp;
        tweet.topic = topic;
        tweet.content = content;

        Ok(())
    }
}

#[derive(Accounts)]
pub struct SendTweet<'info> {
    #[account(init, payer = author, space = Tweet::LEN)]
    pub tweet: Account<'info, Tweet>,
    #[account(mut)]
    pub author: Signer<'info>,
    pub system_program: Program<'info, System>,
}

#[account]
pub struct Tweet {
    pub author: Pubkey,
    pub timestamp: i64,
    pub topic: String,
    pub content: String,
}

const DISCRIMINATOR_LENGTH: usize = 8;
const PUBLIC_KEY_LENGTH: usize = 32;
const TIMESTAMP_LENGTH: usize = 8;
const STRING_LENGTH_PREFIX: usize = 4; // Stores the size of the string.
const MAX_TOPIC_LENGTH: usize = 50; // 50 chars max.
const MAX_CONTENT_LENGTH: usize = 280; // 280 chars max.

impl Tweet {
    const LEN: usize = DISCRIMINATOR_LENGTH
        + PUBLIC_KEY_LENGTH // Author.
        + TIMESTAMP_LENGTH // Timestamp.
        + STRING_LENGTH_PREFIX + MAX_TOPIC_LENGTH // Topic.
        + STRING_LENGTH_PREFIX + MAX_CONTENT_LENGTH // Content.
        + 6; // パディングを追加して392バイトに調整
}

// // ここで、MAX_TOPIC_LENGTHとMAX_CONTENT_LENGTHを確認してください。
// const MAX_TOPIC_LENGTH: usize = 50; // 50 chars max.
// const MAX_CONTENT_LENGTH: usize = 280; // 280 chars max.

#[error_code]
pub enum ErrorCode {
    #[msg("The provided topic should be 50 characters long maximum.")]
    TopicTooLong,
    #[msg("The provided content should be 280 characters long maximum.")]
    ContentTooLong,
}

```


```solana-twitter.ts
import * as anchor from '@project-serum/anchor';
import { Program } from '@project-serum/anchor';
import { SolanaTwitter } from '../target/types/solana_twitter';
import * as assert from "assert";

describe('solana-twitter', () => {
    // Configure the client to use the local cluster.
    // anchor.setProvider(anchor.Provider.env());
    const program = anchor.workspace.SolanaTwitter as Program<SolanaTwitter>;

    it('can send a new tweet', async () => {
        // Call the "SendTweet" instruction.
        const tweet = anchor.web3.Keypair.generate();
        await program.rpc.sendTweet('veganism', 'Hummus, am I right?', {
            accounts: {
                tweet: tweet.publicKey,
                author: program.provider.wallet.publicKey,
                systemProgram: anchor.web3.SystemProgram.programId,
            },
            signers: [tweet],
        });

        // Fetch the account details of the created tweet.
        const tweetAccount = await program.account.tweet.fetch(tweet.publicKey);
        console.log(tweetAccount);
    });
});


```
コードの書き換えが完了したら


```実行する.
anchor build
anchor deploy
anchor run test
```



```実行結果.

yarn run v1.22.22
warning package.json: No license field
$ /Users/#####/Desktop/solana-twitter/node_modules/.bin/ts-mocha -p ./tsconfig.json -t 1000000 'tests/**/*.ts'
(node:60745) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
(Use `node --trace-deprecation ...` to show where the warning was created)


  solana-twitter
{
  author: PublicKey [PublicKey(自分のウォレットアドレスが表示される)] {
    _bn: <BN: 5879add5713eb8d0442024908e261bacff4f58ac0defdeea5752db4ee9b39aa2>
  },
  timestamp: <BN: 66f940cb>,
  topic: 'veganism',
  content: 'Hummus, am I right?'
}
    ✔ can send a new tweet (467ms)


  1 passing (470ms)

✨  Done in 1.70s.

```


ローカル環境での実行ですが、solscanのRPCを
Deploying workspace: h ttp://localhost:8899
上記のh ttp://localhost:8899を入力すると
mainnetの様にsolscanで確認できます


ここまでで前編は終了