풀스택 솔라나 dApp (Rust, Anchor, Phantom, Vue3)

Jay Lee
15 min readOct 29, 2021

Solana, Vue3, Phantom을 통한 dApp을 구축하는 법

구축할 프로젝트는 카운터입니다.

이 튜토리얼은 맥 환경에 최적화되어 있습니다.

프로젝트 개요

사용할 도구 모음

Solana tool : Solana 네트워크와 상호작요하기 위한 문서화된 cli가 포함 되어 있습니다.

Anchor Framework: Anchor는 여러 편리한 개발자 도구를 제공하는 Solana dApp 개발 프레임워크입니다.

참조

The Complete Guide to Full Stack Solana Development

https://github.com/lorisleiva/solana-vue3-counter

Solana 문서

ok so what the fuck is the deal with solana anyway

개발환경

  1. Node.js (^16.11.0)
  2. Solana tool suite
  3. Anchor
  4. Phantom
  5. Vue3

시작

개발환경이 갖춰져 있다고 가정하고 시작합니다.

% solana config getConfig 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

Keypair Path가 없는 경우, https://docs.solana.com/wallet-guide/paper-wallet#seed-phrase-generation를 참조

다음과 같이 네트워크를 변경 가능합니다.

[mainnet-beta, testnet, devnet, localhost]# 로컬호스트
% solana config set --url localhost
Config File: /Users/user/.config/solana/cli/config.yml
RPC URL: http://localhost:8899
WebSocket URL: ws://localhost:8900/ (computed)
Keypair Path: /Users/user/.config/solana/id.json
Commitment: confirmed
# 개발 네트워크
% solana config set --url devnet
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
# 메인 네트워크
% solana config set --url mainnet-beta
Config File: /Users/user/.config/solana/cli/config.yml
RPC URL: https://api.mainnet-beta.solana.com
WebSocket URL: wss://api.mainnet-beta.solana.com/ (computed)
Keypair Path: /Users/user/.config/solana/id.json
Commitment: confirmed

현재 로컬 지갑을 확인해 봅니다.

# 현재 로컬 지갑 주소 확인
% solana address
C7XoqXNJgAXR1orY9YZ4F2k3V3B1FfBqDcX5hNztq3f
# 현재 지갑에 세부 정보 확인
% solana account C7XoqXNJgAXR1orY9YZ4F2k3V3B1FfBqDcX5hNztq3f
Public Key: C7XoqXNJgAXR1orY9YZ4F2k3V3B1FfBqDcX5hNztq3f
Balance: 0 SOL
Owner: 11111111111111111111111111111111
Executable: false
Rent Epoch: 204

먼저 localhost네트워크에서 개발한 다음 네트워크로 전환 devnet합니다.

% solana config set --url localhost
Config File: /Users/user/.config/solana/cli/config.yml
RPC URL: http://localhost:8899
WebSocket URL: ws://localhost:8900/ (computed)
Keypair Path: /Users/user/.config/solana/id.json
Commitment: confirmed

다른 터미널에서 solana-test-validator 실행

% solana-test-validator
--faucet-sol argument ignored, ledger already exists
Ledger location: test-ledger
Log: test-ledger/validator.log
Identity: 4eQjXA76HfYzzMCXez4BH54qEyeGF39x2WDpo7mLN5xU
Genesis Hash: EcfPLt2mvHnPgEHxRWZGR7o2a4VTivbuVSFnfXRVaygJ
Version: 1.8.0
Shred Version: 53958
Gossip Address: 127.0.0.1:1024
TPU Address: 127.0.0.1:1027
JSON RPC URL: http://127.0.0.1:8899
⠚ 00:02:40 | Processed Slot: 1196 | Confirmed Slot: 1196 | Finalized Slot: 1164

원래 터미널에서 에러드랍 100

% solana airdrop 100               
Requesting airdrop of 100 SOL
Signature: RY7TWUDy9YYLHK9mAtBbEzxB68jRu9jxbJu5qjgpd6aY3n8nwmQAHxyWRSYsSKV9z2BFxXawb5jFtZdxMNTj1n9100 SOL# 제대로 100솔 받아졌는지 확인
% solana balance
100 SOL
# 지갑 상세 확인
% solana account C7XoqXNJgAXR1orY9YZ4F2k3V3B1FfBqDcX5hNztq3f
Public Key: C7XoqXNJgAXR1orY9YZ4F2k3V3B1FfBqDcX5hNztq3f
Balance: 100 SOL
Owner: 11111111111111111111111111111111
Executable: false
Rent Epoch: 0

Anchor를 사용해서 프로젝트 생성

% anchor init mysolanaapp...% cd mysolanaappmysolanaapp % tree -L 1.├── Anchor.toml├── Cargo.toml├── app├── migrations├── node_modules├── package.json├── programs├── tests└── yarn.lock5 directories, 4 files

app : 프론트 엔드 코드 위치

programs: Rust 코드 위치

tests: 자바스크립트 테스트 코드 위치

migrations: 기본 배포 스크립트

이제 Rust 코드를 확인해 봅니다.

./programs/maysolanaapp/src/lib.rs

initialize호출될 때, 프로그램을 성공적으로 종료하는 함수를 정의하고 있습니다.

빌드해 봅니다.

mysolanaapp % anchor build
...
mysolanaapp % tree -L 1.├── Anchor.toml├── Cargo.lock├── Cargo.toml├── app├── migrations├── node_modules├── package.json├── programs├── target├── tests└── yarn.lock

빌드가 완료되면 target 이라는 새 폴더가 표시되어야 합니다 .

mysolanaapp % tree ./target/idl./target/idl└── mysolanaapp.json0 directories, 1 file

생성된 IDL도 확인할 수 있습니다.

mysolanaapp % tree ./tests./tests└── mysolanaapp.js0 directories, 1 file

또한 테스트 코드도 확인 가능합니다.

./tests/mysolanaapp.js

Anchor를 사용하여, Solana 프로그램을 호출하려면 일반적으로 두 가지 주요 사항이 필요합니다.

Provider: Provider은 일반적으로 Connection, Wallet 및 Commitment로 구성된 Solana 네트워크에 대한 연결의 추상화입니다 .

테스트에서 Anchor 프레임워크는 환경( anchor.Provider.env())을 기반으로 공급자를 생성 하지만 클라이언트에서는 사용자의 Solana 지갑을 사용하여 공급자를 직접 구성해야 합니다.

Program: programProvider, idl, 및 programID(프로그램이 빌드될 때 생성됨) 을 결합한 추상화이며 프로그램 RPC에 대해 메서드 를 호출할 수 있도록 합니다.

이 두 가지가 있으면 프로그램에서 함수 호출을 시작할 수 있습니다.

예를 들어, 우리 프로그램에는 initialize함수가 있습니다. 테스트에서 다음을 사용하여 해당 함수를 직접 호출할 수 있음을 알 수 있습니다

const tx = await program.rpc.initialize();

이것은 Anchor로 작업할 때 많이 사용할 매우 일반적인 패턴이며, 작동 방식을 이해하면 Solana 프로그램에 연결하고 상호 작용하기가 정말 쉽습니다.

이제 test스크립트 를 실행하여 프로그램을 테스트할 수 있습니다 .

mysolanaapp % anchor deploy
...
mysolanaapp % anchor test
...
✔ Is initialized! (460ms)
1 passing (463ms)

에러가 난다면, Anchor.toml에 program id와 배포된 program id가 같은지 확인하고, 다른 터미널에서 solana-test-validator가 실행 중인지 확인하세요.

카운터 프로그램 작성

우리가 만들 프로그램을 사용하면, 클라이언트 응용 프로그램에서 호출할 때마다 증가하는 카운터를 만들 수 있습니다.

./programs/mysolanaapp/src/lib.rs를 열고 다음 코드로 업데이트합니다.

./programs/mysolanaapp/src/lib.rs

이제 우리는 프론트에서 호출 할 수 있는 두개의 함수(create, increment)를 가졌습니다.

#[account(…)]은 제약 조건을 정의합니다. 이러한 조건을 유지하지 않으면, 명령이 실행되지 않습니다.

Solana 내에서는 상태저장이 없으며, 계정이 버퍼같이 사용되어, 모든 상태를 보유합니다.

프로그램을 빌드합니다.

mysolanaapp % anchor build

이제, 테스트를 작성합니다.

.tests/mysolanaapp.js
mysolanaapp % anchor test

어떤 이유인지 알 수 없어도, 테스트 진행 전에 solana-test-validator를 중지해야 테스트가 통과합니다.

이제, 네트워크를 devnet으로 바꾸고 다시 빌드/테스트합니다. 그리고 테스트 전에, 에어드랍으로 충분한 코인을 확보하세요. 전 3개 정도 받았습니다.

mysolanaapp % solana config set --url devnet
mysolanaapp % solana airdrop 1
mysolanaapp % solana airdrop 1
mysolanaapp % solana airdrop 1
mysolanaapp % anchor build
mysolanaapp % anchor test

배포하기 전에 빌드에 의해 생성된 동적으로 생성된 프로그램 ID를 얻고 싶습니다. 프로젝트를 생성할 때, 프로그램 ID를 대체하기 위해, Rust 프로그램에서 이 ID를 사용해야 합니다. 이 ID를 얻으려면 다음 명령을 실행할 수 있습니다.

mysolanaapp % solana address -k target/deploy/mysolanaapp-keypair.json9WSTCnLnpovgyoyCEbhpzAfSLezPXmkS3F67rSUWAeam

이제 ./programs/mysolanaapp/src/lib.rs에서 프로그램 ID를 업데이트할 수 있습니다 .

use anchor_lang::prelude::*;declare_id!("9WSTCnLnpovgyoyCEbhpzAfSLezPXmkS3F67rSUWAeam");

또한 ./Anchor.toml를 수정합니다.

[programs.devnet]mysolanaapp = "9WSTCnLnpovgyoyCEbhpzAfSLezPXmkS3F67rSUWAeam"[registry]url = "https://anchor.projectserum.com"[provider]cluster = "devnet"wallet = "/Users/user/.config/solana/id.json"[scripts]test = "mocha -t 1000000 tests/"

이제 배포합니다.

mysolanaapp % anchor deployDeploying workspace: https://api.devnet.solana.comUpgrade authority: /Users/user/.config/solana/id.jsonDeploying program "mysolanaapp"...Program path: /Users/user/Workspace/mysolanaapp/target/deploy/mysolanaapp.so...Program Id: 9WSTCnLnpovgyoyCEbhpzAfSLezPXmkS3F67rSUWAeamDeploy success

배포가 잘안되시면, 코인양을 확인하세요.

이제 프런트엔드를 구축해야 합니다.

Vuejs 3.x

기존 app 폴더에 덮어쓰도록, Vuejs 프로젝트를 만듭니다.

mysolanaapp % yarn global add @vue/cli
mysolanaapp % vue create app

프론트엔드 앱의 실행

mysolanaapp % cd app 
app % yarn
app % yarn serve

웹 브라우저에서 Vue3의 데모 프로젝트를 볼 수 있습니다.

@project-serum/anchor와 @solana/web3.js를 추가합니다.

app % yarn add @project-serum/anchor @solana/web3.js 

Solana 지갑 어댑터를 사용하기 위한 종속성을 추가합니다.

yarn add @solana/wallet-adapter-base \
@solana/wallet-adapter-wallets \
@solana/wallet-adapter-vue \
@solana/wallet-adapter-vue-ui

./assets 폴더에, ../target/idl/mysolanaapp.json를 복사해서 새 파일(idl.json)을 만듭니다.

app % cp ../target/idl/mysolanaapp.json ./src/assets/idl.json

./src/Counter.vue 파일을 아래와 같이 만듭니다.

./src/App.vue를 아래와 같이 수정합니다.

이제 실행해봅니다.

app % yarn serve
Phantom Wallet

Phantom 지갑을 연결 하기전에, 지갑의 네트워크를 Devnet으로 바꿉니다.

지갑 주소로 충분한 코인을 에어드랍하는 것도 잊지마세요.

설정>Change Network>Devnet

참고 : https://github.com/kjaylee/mysolanaapp

저의 삽질이 도움이 되셨으면 합니다.

--

--