solanaのフルスタック開発
https://github.com/dabit3/complete-guide-to-full-stack-solana-development
Reacktって何?
UIを作ることに特化したJavaScriptライブラリ
サクサク動き、可読性も高いので人気
前提条件
-
Solana ToolSuite-インストール手順はこちらでご覧いただけます。ノート-私は非常に苦労主に、M1 Mac上のすべての作業を取得していた
solana-test-validator
としますcargo-build-bpf
。私はついにそれを理解し、ここに私の解決策を投稿しました。いつかこれは修正され、箱から出してすぐに機能すると確信しています。 -
アンカー-アンカーのインストールは私にとって非常に簡単でした。インストール手順はここにあります。
-
Solanaブラウザウォレット-私はこのアプリをテストしたものであるPhantomをお勧めします。
構築するには
- リポジトリのクローンを作成する
git clone git@github.com:dabit3 / complete-guide-to-full-stack-solana.git
https://blog.cloud-acct.com/posts/u-github-ssh/
出来なかった。ssh接続する必要があった。
-
実行するプロジェクトディレクトリに変更します
-
依存関係をインストールします
npmインストール
- ローカルSolanaノードを開始します
solana-test-validator
出来なかった。
homebrewで
brew install openssl@1.1
ををする必要ががあがあった
そもそもhomebrewがなかったのでインストールした パスは通さなかったけど動いた。大丈夫そう。 インストールも出来た 色々あって solanaをM1で動かす これで行けた。
React、Solana、Anchor、およびPhantomウォレットを使用してフルスタックdappを構築します。
でフルスタックイーサリアム開発への完全なガイド私もポリゴン、雪崩、およびイーサリアムレイヤArbitrumのような2つのような他のEVM互換性のあるチェーンに適用することができイーサリアム上の完全なスタックDAPPを構築する方法に深いダイビングをしました。
このガイドでは、フルスタックdappを構築する方法を示すためにSolanaに飛び込みたいと思います。また、エコシステムと開発者ツールを紹介して、今後の独自のアイデアやアプリケーションの構築を開始して実行できるようにしたいと思います。
Solana開発者の概要
約6か月前にSolidityとそのエコシステムを学び始めたばかりの私は、立ち上げて実行するのはそれほど難しいことではないと思いました。私は間違っていた。
開発者ツールの一部(Solana CLIとAnchor)は本当に素晴らしく洗練されていますが、残りのエコシステム、およびAnchorのドキュメント(公平に言えば非常に新しい)でさえ、かなりの量が望まれています。
そうは言っても、すべてのコツをつかむと、自分のアイデアの実装を開始して実験を開始する方法をすぐに理解しやすくなります。
答えを見つけるための鍵の1つは、Google、Githubのすべて、特にAnchorとSolanaのさまざまなDiscordサーバーを検索することに注意することです。これらのチャネルの開発者、特にアンカーフレームワークを作成したArmaniFerranteは非常に役に立ちました。検索機能に慣れてください。Discordでの過去のディスカッションで質問に対する回答を見つけることができます。
プロジェクトの概要
今日使用するツールには次のものがあります。
Solana Tool Suite-これには、Solanaネットワークと対話するための非常に洗練された十分に文書化されたCLIが含まれています。
アンカーフレームワーク-アンカーは実際には私にとって命の恩人であり、アンカーなしでは何かを構築するというこぶを乗り越えることができなかったとほぼ確信しています。それはソラナ開発のヘルメットなどであり、私はそれが大好きです。また、Rustの上にDSLを提供しているので、言語を深く理解する必要はありませんが、DSLを使用しても、重要なものを構築するのに役立つ可能性があるため、Rustを学習しようとしています。 。Rustを学ぶのに良い無料の場所は、The RustBookです。
DSLって何?
DSL
Rustそのもので書くと記述量が膨大になってしまうので、それを簡単にしようぜっていうものだと思う。ひとまとまりの関数的な?ライブラリと何が違うんだろう?
solana / web3.js-かなりうまく機能しているように見えるweb3.jsのSolanaバージョンですが、ドキュメントは私にはほとんど使用できませんでした
React-クライアント側のフレームワーク
他の人が私よりもこれをうまくカバーできるので、私はソラナ自体がどのように機能するかについての詳細な詳細をすべて省略します。代わりに、私は何かを構築し、これを達成するために知っておく必要のある詳細を、私が最も重要だと思うこととともに共有することに焦点を当てようとします。
ソラナとそれがどのように機能するかについてもっと知りたい場合は、ここにいくつかの良い部分があります:
このガイドでは、主にプロジェクトのセットアップ、テスト、およびいくつかのタイプのアプリケーションを構築するためのフロントエンドクライアントの統合に焦点を当てます。主にCRUD操作(もちろん削除なし)に焦点を当てますが、文書化されていないことがわかりました(クライアントアプリケーションとの統合)。
CRUDとは、永続的なデータを取り扱うソフトウェアに要求される4つの基本機能である、データの作成(Create)、読み出し(Read)、更新(Update)、削除(Delete)の頭文字を繋げた語。
また、Solana CLIを使用してトークンを独自の開発アカウントにエアドロップする方法と、ローカルネットワークとライブテストネットワークの両方にアプリをデプロイする方法についても学習します。
このガイドではNFTに焦点を当てませんが、将来のガイドでそれを行うことに焦点を当てる可能性があります。今のところ、SolanaでNFTマーケットプレイスを構築することに興味がある場合は、Metaplexをチェックすることをお勧めします。
前提条件
このチュートリアルでは、Solanaでフルスタックアプリを構築する方法について説明しますが、個々の依存関係をすべてインストールする方法については説明しません。
代わりに、依存関係をリストし、それらをインストールする方法についてのドキュメントへのリンクを示します。各プロジェクトは、これらのことをこれまで以上に説明および文書化し、最新の状態に保つことができるためです。
- Node.js- nvmまたはfnmのいずれかを使用してNodeをインストールすることをお勧めします
- Solana ToolSuite-インストール手順はこちらでご覧いただけます。ノート-私は非常に苦労主に、M1 Mac上のすべての作業を取得していた
solana-test-validator
としますcargo-build-bpf
。私はついにそれを理解し、ここに私の解決策を投稿しました。いつかこれは修正され、箱から出してすぐに機能すると確信しています。 - アンカー(モカのインストールを含む)-アンカーのインストールは私にとって非常に簡単でした。インストール手順はここにあります。
- Solanaブラウザウォレット-私はこのアプリをテストしたものであるPhantomをお勧めします。
入門
構築を開始する前に、SolanaCLIを見てみましょう。
Solana CLI
Solana CLIで行う主なことは、ネットワーク(localhostと開発者テストネットの間)の構成と、ウォレットへのトークンのエアドロップです。これは、AnchorCLIで行う他のほとんどすべてのことです。
たとえば、次のコマンドを使用して、現在のネットワーク(およびその他の)構成を確認できます。
solana config get
# output
Config File: /Users/user/.config/solana/cli/config.yml
RPC URL: https://api.devnet.solana.com
WebSocket URL: wss://api.devnet.solana.com/ (computed)
Keypair Path: /Users/user/.config/solana/id.json
Commitment: confirmed
次のようにネットワークを変更できます。
# set to localhost
solana config set --url localhost
# set to devnet
solana config set --url devnet
プログラムを構築、テスト、および展開するときに、使用しているネットワークを認識する必要があるため、これは重要です。また、ウォレットがテスト時にローカル環境で使用しているのと同じネットワークを使用していることを確認する必要があります。これについては後で説明します。
自分のパソコンでテストバリデーターを立てて、
solana config set --url localhost
で、
solana address
の
solana account <address from above>
をすると、accountが表示されたが、
solana config set --url devnet
とかだとaccountは表示されず。なぜ?
localhost
まず、ネットワーク上で開発を行い、次にネットワークに切り替えdevnet
ます。
CLIを使用して、現在のローカルウォレットアドレスを確認することもできます。
solana address
次に、アカウントに関する完全な詳細を取得します。
solana account <address from above>
次に、いくつかのトークンを空中投下しましょう。これを行うには、最初にローカルネットワークに切り替えます。これは、最初に作業を行う場所です。
solana config set --url localhost
次に、ローカルネットワークを起動します。これは、テスト用にデプロイできるローカルSolanaノードになります。
solana-test-validator
ローカルネットワークが実行されたら、アカウントにトークンをエアドロップできます。ネットワークが実行されている状態で、別のウィンドウを開き、次のコマンドを実行します。
solana airdrop 100
ウォレットの残高を確認できます。
solana balance
# or
solana balance <address>
これで、ウォレットにバランス100SOLが含まれるはずです。これで、構築を開始できます。
作り始めましょう
開始するには、新しいアンカープロジェクトを初期化し、新しいディレクトリに移動します。
anchor init mysolanaapp --javascript
Document>solanaapp>mysolanaapp
に作った。
cd mysolanaapp
必ずアンカーバージョン0.16.0以降を使用してください。
このプロジェクトでは、(node_modulesに加えて)4つのメインフォルダーが表示されます。
アプリ-フロントエンドコードの行き先
プログラム-これは、SolanaプログラムのRustコードが存在する場所です
テスト– JavaScriptがライブプログラムをテスト
移行-基本的なデプロイスクリプト
私たちのために作成されたプログラムを見てみましょう。
AnchorはeDSL(組み込みDSL)を使用し、それを記述できるようにします。これは、Solana&Rustを使用しない場合に通常実行する必要のある、より複雑な低レベルの操作の多くを抽象化し、私にとってより親しみやすいものにします。
// programs/src/lib.rs
use anchor_lang::prelude::*;
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
#[program]
pub mod mysolanaapp {
use super::*;
pub fn initialize(ctx: Context<Initialize>) -> ProgramResult {
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize {}
これはおそらくあなたが書くことができる最も基本的なプログラムです。ここで発生する唯一のことは、と呼ばれる関数を定義していることです。この関数はinitialize
、呼び出されると、プログラムを正常に終了します。データ操作はまったくありません。
Initialize
構造体は、任意の引数の空であるとしてコンテキストを定義します。関数コンテキストについては後で詳しく説明します。
このプログラムをコンパイルするには、Anchorbuild
コマンドを実行します。
コンパイル?人間の言葉で書かれたRust言語を、コンピューターがわかる機械語に翻訳する(バイナリコードにする)こと。
https://wa3.i-3-i.info/word186.html
anchor build
ビルドが完了すると、targetという名前の新しいフォルダーが表示されます。
作成されるアーティファクトの1つは、target / idl /mysolanaapp.jsonにあるIDLです。
つまり、JSでもRustでもMacでもLinuxでも全てが読めるような言語のことをIDLという。
イタフェース記述言語又はインターフェース定義言語(IDLは)、一つの言語で書かれたプログラムまたはオブジェクトが未知の言語で書かれた別のプログラムと通信することができ言語の総称です。IDLは、言語に依存しない方法でインターフェイスを記述し、1つの言語を共有しないソフトウェアコンポーネント間、たとえば、C ++で記述されたコンポーネントとJavaで記述されたコンポーネント間の通信を可能にします。インタフェース記述言語又はインターフェース定義言語(IDLは)、一つの言語で書かれたプログラムまたはオブジェクトが未知の言語で書かれた別のプログラムと通信することができ言語の総称です。IDLは、言語に依存しない方法でインターフェイスを記述し、1つの言語を共有しないソフトウェアコンポーネント間、たとえば、C ++で記述されたコンポーネントとJavaで記述されたコンポーネント間の通信を可能にします。
IDLはSolidityのABI(またはGraphQLのクエリ定義)と非常によく似ており、JavaScriptテストとフロントエンドで同様の方法で使用してRPCを介してSolanaプログラムと通信します。
IDLはすべての言語で読めるので、JS(フロントエンド)とsolanaとのやり取りでつかう
プログラムをテストすることもできます。tests / mysolanaapp.jsを開くと、プログラムをテストできるJavaScriptで記述されたテストがあることがわかります。
テストは次のようになります。
const anchor = require('@project-serum/anchor');
describe('mysolanaapp', () => {
// Configure the client to use the local cluster.
anchor.setProvider(anchor.Provider.env());
it('Is initialized!', async () => {
const program = anchor.workspace.Mysolanaapp;
const tx = await program.rpc.initialize();
console.log("Your transaction signature", tx);
});
});
このテストから学ぶべき重要なことがいくつかあり、テストとフロントエンドJavaScriptクライアントの両方で将来使用する予定です。
Anchorを使用してSolanaプログラムを呼び出すには、通常、次の2つの主要なものが必要です。
1Provider
–Provider
典型的にからなる、ソラナネットワークへの接続の抽象化でありConnection
、財布、およびプリフライトコミットメント。
テストでは、アンカーフレームワークが環境(anchor.Provider.env()
)に基づいてプロバイダーを作成しますが、クライアントでは、ユーザーのSolanaウォレットを使用してプロバイダーを自分で構築する必要があります。
2.program
–program
組み合わせた抽象化されProvider
、idl
、をし、programID
(プログラムが構築されたときに生成される)と、私たちが呼び出すことができますRPC
私たちのプログラムに対するメソッドを。
繰り返しになりProvider
ますが、アンカーは、にアクセスするための便利な方法を提供しますprogram
が、フロントエンドを構築するときは、これをprovider
自分で構築する必要があります。
これら2つが揃ったら、プログラムで関数の呼び出しを開始できます。たとえば、私たちのプログラムにはinitialize
関数があります。私たちのテストでは、次を使用してその関数を直接呼び出すことができることがわかりますprogram.rpc.functionName
。
const tx = await program.rpc.initialize();
これは、Anchorを操作するときによく使用する非常に一般的なパターンであり、Anchorがどのように機能するかを理解すると、Solanaプログラムへの接続と操作が非常に簡単になります。
これで、test
スクリプトを実行してプログラムをテストできます。
anchor test
HelloWorldの構築
プロジェクトの設定が完了したので、もう少し面白いものを作成しましょう。
フルスタック開発者として、ほとんどの場合、CRUDタイプの操作を実行する方法を考えているので、それを次に見ていきます。
最初に作成するプログラムでは、クライアントアプリケーションから呼び出すたびにインクリメントするカウンターを作成できます。
最初に行う必要があるのは、programs / mysolanaapp / src / lib.rsを開き、次のコードで更新することです。
use anchor_lang::prelude::*;
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
#[program]
mod mysolanaapp {
use super::*;
pub fn create(ctx: Context<Create>) -> ProgramResult {
let base_account = &mut ctx.accounts.base_account;
base_account.count = 0;
Ok(())
}
pub fn increment(ctx: Context<Increment>) -> ProgramResult {
let base_account = &mut ctx.accounts.base_account;
base_account.count += 1;
Ok(())
}
}
// Transaction instructions
#[derive(Accounts)]
pub struct Create<'info> {
#[account(init, payer = user, space = 16 + 16)]
pub base_account: Account<'info, BaseAccount>,
#[account(mut)]
pub user: Signer<'info>,
pub system_program: Program <'info, System>,
}
// Transaction instructions
#[derive(Accounts)]
pub struct Increment<'info> {
#[account(mut)]
pub base_account: Account<'info, BaseAccount>,
}
// An account that goes inside a transaction instruction
#[account]
pub struct BaseAccount {
pub count: u64,
}
このプログラムには、との2つの関数がcreate
ありincrement
ます。これらの2つの関数は、プログラムと対話するためにクライアントアプリケーションから呼び出すことができるRPC要求ハンドラーです。
サーバー側で実行する――サーバーサイド・アプリケーション
Webサーバー上でプログラムが実行され、その処理結果がクライアントに送られてブラウザに表示されます。そのため、ブラウザを実行しているクライアントの側には、ほとんど負荷がかかりません。
裏返せば、Webサーバーに負荷が集中するということです。CGIやJavaサーブレットなどが代表的です。
2. クライアント側で実行する――クライアントサイド・アプリケーション
Webサーバーに保存されたプログラムがクライアントのメモリに読み込まれ、Webブラウザがそれを実行します。処理結果も、クライアントのブラウザに表示されます。
Webサーバーからクライアントへ送られるのは、処理結果ではなくプログラムそのものとなるため、プログラムの読み込みに時間が取られます。また、プログラムの実行速度はクライアントの処理能力に左右されます。
“1.”の場合とは逆で、サーバー側は単にプログラムを送信するだけとなるため、負担は少なくて済みます。
JavaアプレットやJavaスクリプトが代表的です。
なるほど。java scriptの場合は、サーバーで実行されるのではなくて、クライアントのブラウザ上で実行されるんだね。
RPCハンドラーの最初のパラメーターはContext構造体です。これは、関数が呼び出されたときに渡されるコンテキストとその処理方法を記述します。以下の場合Create
:、我々は3つのパラメータを期待しているbase_account
、user
とsystem_program
。
#[account(...)]
属性は宣言進行アカウントに関連する制約と命令を定義します。これらの制約のいずれかが当てはまらない場合、命令は実行されません。
適切なbase_account
方法でこのプログラムを呼び出すクライアントは、これらのRPCメソッドを呼び出すことができます。
Solanaがデータを処理する方法は、私がこれまでに扱ったものとは大きく異なります。プログラム内には永続的な状態はなく、すべてがアカウントと呼ばれるものに関連付けられています。アカウントは基本的に、プログラムのすべての状態を保持します。このため、すべてのデータは外部からの参照によって渡されます。
読み取り操作もありません。これは、プログラムの内容を読み取るために必要なのはアカウントを要求することだけであり、そこからプログラムのすべての状態を表示できるためです。アカウントの仕組みについて詳しくは、この投稿をご覧ください。
プログラムをビルドするには:
anchor build
次に、このカウンタプログラムを使用するテストを作成しましょう。これを行うには、tests / mysolanaapp.jsを開き、次のコードで更新します。
const assert = require("assert");
const anchor = require("@project-serum/anchor");
const { SystemProgram } = anchor.web3;
describe("mysolanaapp", () => {
/* create and set a Provider */
const provider = anchor.Provider.env();
anchor.setProvider(provider);
const program = anchor.workspace.Mysolanaapp;
it("Creates a counter)", async () => {
/* Call the create function via RPC */
const baseAccount = anchor.web3.Keypair.generate();
await program.rpc.create({
accounts: {
baseAccount: baseAccount.publicKey,
user: provider.wallet.publicKey,
systemProgram: SystemProgram.programId,
},
signers: [baseAccount],
});
/* Fetch the account and check the value of count */
const account = await program.account.baseAccount.fetch(baseAccount.publicKey);
console.log('Count 0: ', account.count.toString())
assert.ok(account.count.toString() == 0);
_baseAccount = baseAccount;
});
it("Increments the counter", async () => {
const baseAccount = _baseAccount;
await program.rpc.increment({
accounts: {
baseAccount: baseAccount.publicKey,
},
});
const account = await program.account.baseAccount.fetch(baseAccount.publicKey);
console.log('Count 1: ', account.count.toString())
assert.ok(account.count.toString() == 1);
});
});
プログラムのテストと展開を続ける前に、ビルドによって生成された動的に生成されたプログラムIDを取得する必要があります。プロジェクトの作成時に設定したプレースホルダーIDを置き換えるために、RustプログラムでこのIDを使用する必要があります。このIDを取得するには、次のコマンドを実行します。
solana address -k target/deploy/mysolanaapp-keypair.json
何をやっているんだ?
アンカーのそもそもbuildによって新しく操作されたのは何だったんだ?
-kってなんだ?
これで、lib.rsのプログラムIDを更新できます。
プログラムIDってなんだ?
// mysolanaapp/src/lib.rs
declare_id!("your-program-id");
そしてAnchor.tomlで:
# Anchor.toml
[programs.localnet]
mysolanaapp = "your-program-id"
次に、テストを実行します。
anchor test
テストに合格すると、デプロイできるようになります。
これで、プログラムをデプロイできます。それsolana-test-validator
が実行されていることを確認してください:
anchor deploy
別のウィンドウを開いて実行することで、バリデーターのログを表示することもできます
solana logs
これで、フロントエンドを構築する準備が整いました。
バリデーター建てた状態で使えた。deployできた。
Reactアプリの構築
アンカープロジェクトのルートで、既存のアプリディレクトリを上書きする新しいreactアプリを作成します。
npx create-react-app app
create-react-appののインストールがひつようだったのでそのままインストールした
次に、AnchorとSolanaWeb3に必要な依存関係をインストールします。
cd app
npm install @project-serum/anchor @solana/web3.js
また、Solana Wallet Adapterを使用して、ユーザーのSolanaウォレットの接続を処理します。それらの依存関係もインストールしましょう:
npm install @solana/wallet-adapter-react \
@solana/wallet-adapter-react-ui @solana/wallet-adapter-wallets \
@solana/wallet-adapter-base
次に、srcディレクトリにidl.jsonという名前の新しいファイルを作成します。ここで、target / idl /mysolanaapp.jsonにあるメインプロジェクトフォルダーに作成されたIDLJSONをコピーします。
このidlファイルをクライアントアプリケーションのsrcフォルダーに自動的にコピーできればいいのですが、現時点では、これをネイティブに行う方法が見つかりません。もちろん、必要に応じてこれを行う独自のスクリプトを作成することもできます。そうでない場合は、メインプログラムに変更を加えるたびに、IDLをコピーして貼り付ける必要があります。
このようなスクリプトが必要な場合は、数行のコードで実行できます。
// copyIdl.js
const fs = require('fs');
const idl = require('./target/idl/mysolanaapp.json');
fs.writeFileSync('./app/src/idl.json', JSON.stringify(idl));
requireとは
このrequireコマンドはcomposer.json、現在のディレクトリからファイルに新しいパッケージを追加します。
$ php composer.phar require
要件を追加/変更した後、変更された要件がインストールまたは更新されます。
要件をインタラクティブに選択したくない場合は、それらをコマンドに渡すだけです。
$ php composer.phar require vendor/package:2.* vendor/package2:dev-master
次に、app / src / App.jsを開き、次のように更新します。
import './App.css';
import { useState } from 'react';
import { Connection, PublicKey } from '@solana/web3.js';
import {
Program, Provider, web3
} from '@project-serum/anchor';
import idl from './idl.json';
import { getPhantomWallet } from '@solana/wallet-adapter-wallets';
import { useWallet, WalletProvider, ConnectionProvider } from '@solana/wallet-adapter-react';
import { WalletModalProvider, WalletMultiButton } from '@solana/wallet-adapter-react-ui';
require('@solana/wallet-adapter-react-ui/styles.css');
const wallets = [
/* view list of available wallets at https://github.com/solana-labs/wallet-adapter#wallets */
getPhantomWallet()
]
const { SystemProgram, Keypair } = web3;
/* create an account */
const baseAccount = Keypair.generate();
const opts = {
preflightCommitment: "processed"
}
const programID = new PublicKey(idl.metadata.address);
function App() {
const [value, setValue] = useState(null);
const wallet = useWallet();
async function getProvider() {
/* create the provider and return it to the caller */
/* network set to local network for now */
const network = "http://127.0.0.1:8899";
const connection = new Connection(network, opts.preflightCommitment);
const provider = new Provider(
connection, wallet, opts.preflightCommitment,
);
return provider;
}
async function createCounter() {
const provider = await getProvider()
/* create the program interface combining the idl, program ID, and provider */
const program = new Program(idl, programID, provider);
try {
/* interact with the program via rpc */
await program.rpc.create({
accounts: {
baseAccount: baseAccount.publicKey,
user: provider.wallet.publicKey,
systemProgram: SystemProgram.programId,
},
signers: [baseAccount]
});
const account = await program.account.baseAccount.fetch(baseAccount.publicKey);
console.log('account: ', account);
setValue(account.count.toString());
} catch (err) {
console.log("Transaction error: ", err);
}
}
async function increment() {
const provider = await getProvider();
const program = new Program(idl, programID, provider);
await program.rpc.increment({
accounts: {
baseAccount: baseAccount.publicKey
}
});
const account = await program.account.baseAccount.fetch(baseAccount.publicKey);
console.log('account: ', account);
setValue(account.count.toString());
}
if (!wallet.connected) {
/* If the user's wallet is not connected, display connect wallet button. */
return (
<div style={{ display: 'flex', justifyContent: 'center', marginTop:'100px' }}>
<WalletMultiButton />
</div>
)
} else {
return (
<div className="App">
<div>
{
!value && (<button onClick={createCounter}>Create counter</button>)
}
{
value && <button onClick={increment}>Increment counter</button>
}
{
value && value >= Number(0) ? (
<h2>{value}</h2>
) : (
<h3>Please create the counter.</h3>
)
}
</div>
</div>
);
}
}
/* wallet configuration as specified here: https://github.com/solana-labs/wallet-adapter#setup */
const AppWithProvider = () => (
<ConnectionProvider endpoint="http://127.0.0.1:8899">
<WalletProvider wallets={wallets} autoConnect>
<WalletModalProvider>
<App />
</WalletModalProvider>
</WalletProvider>
</ConnectionProvider>
)
export default AppWithProvider;
ウォレットネットワークの切り替え
localhost
ネットワーク上のプログラムと対話する前に、Phantomウォレットを適切なネットワークに切り替える必要があります。
これを行うには、ファントムウォレットを開き、設定ボタンをクリックします。次に、[ネットワークの変更]まで下にスクロールします。
次に、Localhostを選択します。
次に、このウォレットにトークンをエアドロップする必要があります。ウォレットインターフェイスの上部で、アドレスをクリックしてクリップボードにコピーします。
次に、ターミナルを開き、次のコマンドを実行します(必ずsolana-test-validator
実行されていることを確認してください)。
solana airdrop 10 <address>
これで、ウォレットに10個のトークンが含まれているはずです。これで、アプリを実行してテストできます。
変更するアプリのディレクトリとは、次のコマンドを実行します。
npm start
ウォレットを接続し、カウンターを作成し、それをインクリメントできるはずです。
ブラウザ開くところまで入ったがそこでつながらないエラー。
最新バージョンのでエラーが出ているようなので、下記コマンド入力で解決した。
export NODE_OPTIONS=--openssl-legacy-provider
更新すると、プログラムの状態が失われることに気付くでしょう。これは、プログラムのロード時にベースアカウントを動的に生成しているためです。さまざまなクライアント間でプログラムデータを読み取って操作する場合は、プロジェクトのどこかにキーペアを作成して保存する必要があります。これがどのように見えるかについての素朴なアプローチの要点をまとめました。
わからない….
おそらくphantomWALLETがうまくつながっていないように思われる。
HelloWorldパート2
このプログラムのバリエーションを作成して、カウンターを処理する代わりに、メッセージを作成し、以前に作成されたすべてのメッセージを追跡できるようにします。
そのためには、Rustプログラムを次のように更新しましょう。
/* programs/mysolanaapp/src/lib.rs */
use anchor_lang::prelude::*;
declare_id!("your-program-id");
#[program]
mod mysolanaapp {
use super::*;
pub fn initialize(ctx: Context<Initialize>, data: String) -> ProgramResult {
let base_account = &mut ctx.accounts.base_account;
let copy = data.clone();
base_account.data = data;
base_account.data_list.push(copy);
Ok(())
}
pub fn update(ctx: Context<Update>, data: String) -> ProgramResult {
let base_account = &mut ctx.accounts.base_account;
let copy = data.clone();
base_account.data = data;
base_account.data_list.push(copy);
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(init, payer = user, space = 64 + 64)]
pub base_account: Account<'info, BaseAccount>,
#[account(mut)]
pub user: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct Update<'info> {
#[account(mut)]
pub base_account: Account<'info, BaseAccount>,
}
#[account]
pub struct BaseAccount {
pub data: String,
pub data_list: Vec<String>,
}
このプログラムには、追跡している2つの主要なデータがあります。名前付きの文字列data
と、という名前のプログラムにこれまでに追加されたすべてのデータのリストを保持するベクトルですdata_list
。
ここでのメモリ割り当ては128 + 128
、Vectorを説明するために、前のプログラムよりも高い()ことに気付くでしょう。このプログラムに何個の更新をそのまま保存できるかはわかりませんが、この例自体は実験的なものであり、その方法を理解するために、さらに調査したり実験したりする必要があるかもしれません。物事はうまくいきます。
次に、この新しいプログラムのテストを更新できます。
const assert = require("assert");
const anchor = require("@project-serum/anchor");
const { SystemProgram } = anchor.web3;
describe("Mysolanaapp", () => {
const provider = anchor.Provider.env();
anchor.setProvider(provider);
const program = anchor.workspace.Mysolanaapp;
it("It initializes the account", async () => {
const baseAccount = anchor.web3.Keypair.generate();
await program.rpc.initialize("Hello World", {
accounts: {
baseAccount: baseAccount.publicKey,
user: provider.wallet.publicKey,
systemProgram: SystemProgram.programId,
},
signers: [baseAccount],
});
const account = await program.account.baseAccount.fetch(baseAccount.publicKey);
console.log('Data: ', account.data);
assert.ok(account.data === "Hello World");
_baseAccount = baseAccount;
});
it("Updates a previously created account", async () => {
const baseAccount = _baseAccount;
await program.rpc.update("Some new data", {
accounts: {
baseAccount: baseAccount.publicKey,
},
});
const account = await program.account.baseAccount.fetch(baseAccount.publicKey);
console.log('Updated data: ', account.data)
assert.ok(account.data === "Some new data");
console.log('all account data:', account)
console.log('All data: ', account.dataList);
assert.ok(account.dataList.length === 2);
});
});
それをテストするには:
anchor test
テストが失敗した場合は、バリデーターをオフにしてから再度実行してみてください。
次に、クライアントを更新しましょう。
import './App.css';
import { useState } from 'react';
import { Connection, PublicKey } from '@solana/web3.js';
import { Program, Provider, web3 } from '@project-serum/anchor';
import idl from './idl.json';
import { getPhantomWallet } from '@solana/wallet-adapter-wallets';
import { useWallet, WalletProvider, ConnectionProvider } from '@solana/wallet-adapter-react';
import { WalletModalProvider, WalletMultiButton } from '@solana/wallet-adapter-react-ui';
require('@solana/wallet-adapter-react-ui/styles.css');
const wallets = [ getPhantomWallet() ]
const { SystemProgram, Keypair } = web3;
const baseAccount = Keypair.generate();
const opts = {
preflightCommitment: "processed"
}
const programID = new PublicKey(idl.metadata.address);
function App() {
const [value, setValue] = useState('');
const [dataList, setDataList] = useState([]);
const [input, setInput] = useState('');
const wallet = useWallet()
async function getProvider() {
/* create the provider and return it to the caller */
/* network set to local network for now */
const network = "http://127.0.0.1:8899";
const connection = new Connection(network, opts.preflightCommitment);
const provider = new Provider(
connection, wallet, opts.preflightCommitment,
);
return provider;
}
async function initialize() {
const provider = await getProvider();
/* create the program interface combining the idl, program ID, and provider */
const program = new Program(idl, programID, provider);
try {
/* interact with the program via rpc */
await program.rpc.initialize("Hello World", {
accounts: {
baseAccount: baseAccount.publicKey,
user: provider.wallet.publicKey,
systemProgram: SystemProgram.programId,
},
signers: [baseAccount]
});
const account = await program.account.baseAccount.fetch(baseAccount.publicKey);
console.log('account: ', account);
setValue(account.data.toString());
setDataList(account.dataList);
} catch (err) {
console.log("Transaction error: ", err);
}
}
async function update() {
if (!input) return
const provider = await getProvider();
const program = new Program(idl, programID, provider);
await program.rpc.update(input, {
accounts: {
baseAccount: baseAccount.publicKey
}
});
const account = await program.account.baseAccount.fetch(baseAccount.publicKey);
console.log('account: ', account);
setValue(account.data.toString());
setDataList(account.dataList);
setInput('');
}
if (!wallet.connected) {
return (
<div style={{ display: 'flex', justifyContent: 'center', marginTop:'100px' }}>
<WalletMultiButton />
</div>
)
} else {
return (
<div className="App">
<div>
{
!value && (<button onClick={initialize}>Initialize</button>)
}
{
value ? (
<div>
<h2>Current value: {value}</h2>
<input
placeholder="Add new data"
onChange={e => setInput(e.target.value)}
value={input}
/>
<button onClick={update}>Add data</button>
</div>
) : (
<h3>Please Inialize.</h3>
)
}
{
dataList.map((d, i) => <h4 key={i}>{d}</h4>)
}
</div>
</div>
);
}
}
const AppWithProvider = () => (
<ConnectionProvider endpoint="http://127.0.0.1:8899">
<WalletProvider wallets={wallets} autoConnect>
<WalletModalProvider>
<App />
</WalletModalProvider>
</WalletProvider>
</ConnectionProvider>
)
export default AppWithProvider;
次に、プログラムをビルドしてデプロイします(必ずsolana-test-validator is running
):
anchor build
anchor deploy
新しいビルドを使用すると、クライアント用に更新する必要がある新しいIDLが作成されます。新しいIDLをapp / src / idl.jsonにコピーするか、copyIdl.jsスクリプトを実行します。
それをテストする
新しいプログラムをテストするときは、ビルドによって作成されたidl.jsonファイルを必ず更新してください。
変更するアプリのディレクトリとは、実行するstart
コマンドを:
npm start
Devnetへのデプロイ
ライブネットワークへの展開は、ここから非常に簡単です。私たちがしなければならない主なことは次のとおりです。
- 使用するようにSolanaCLIを更新します
devnet
。
solana config set --url devnet
- 使用するファントムウォレットを更新する
devnet
- Anchor.tomlを開き、クラスターをから
localnet
に更新しますdevnet
。 - プログラムを再構築します。Anchor.tomlのプログラムIDが現在のプログラムIDと一致していることを確認してください。
- プログラムを再度デプロイします。今回は、
devnet
- ではアプリ/ SRC / App.js、我々はまた、使用して、今回のネットワークを更新する必要があります
clusterApiUrl
から@solana/web3
、このように、:
/* before */
<ConnectionProvider endpoint="http://127.0.0.1:8899">
/* after */
import {
...,
clusterApiUrl
} from '@solana/web3.js';
const network = clusterApiUrl('devnet');
<ConnectionProvider endpoint={network}>
ここから、前の手順と同じようにデプロイしてテストできるようになります。
このようなテクノロジーをフルタイムで使用することに興味がある場合は、私とEdge&Nodeの私のチームに参加してください。採用しています。