分类 Solana-新手教程 下的文章

Solana 开发入门


Solana 的开发可以分为两个主要部分:

  1. 链上程序开发:在这里您可以创建自定义程序并将其直接部署到区块链。部署后,任何知道如何与它们通信的人都可以使用它们。您可以用 Rust、C 或 C++ 编写这些程序。Rust 目前对链上程序开发的支持最多。
  2. 客户端开发:在这里,您可以编写与链上程序通信的软件(称为去中心化应用程序或 dApp)。您的应用程序可以提交交易以在链上执行操作。客户端开发可以用任何编程语言编写。

客户端和链上端之间的“粘合剂”是 Solana JSON RPC API。客户端向 Solana 网络发送 RPC 请求以与链上程序交互。这与前端和后端之间的正常开发非常相似。使用 Solana 的主要区别在于后端是一条全球无权限区块链。这意味着任何人都可以与您的链上程序交互,而无需颁发 API 密钥或任何其他形式的权限。

客户端SDK

Language SDK
RUST solana_sdk
Typescript @solana/web3.js
Python solders
Java solanaj or solana4j
C++ solcpp
Go solana-go
Kotlin solanaKT or sol4k
Dart solana
C# solnet
GdScript godot

您还需要与 RPC 建立连接才能与网络交互。您可以与RPC 基础设施提供商合作,也 可以运行自己的 RPC 节点

脚手架

为了快速开始使用应用程序的前端,您可以通过在 CLI 中输入以下内容来生成可定制的 Solana 脚手架:

npx create-solana-dapp <project-name>

这将创建一个新项目,其中包含开始在 Solana 上构建所需的所有文件和基本配置。脚手架将包含一个示例前端和一个链上程序模板(如果您选择了一个)。您可以阅读文档 create-solana-dapp以 了解更多信息。

测试框架

测试实例

当你开始在 Solana 上进行构建时,还有一些资源可帮助你加速你的旅程:

  • Solana Cookbook:参考资料和代码片段的集合,可帮助您在 Solana 上进行构建。
  • Solana 程序示例:示例程序库,为程序上的不同操作提供构建块。
  • 指南:教程和指南引导您在 Solana 上进行构建。

获取支持

https://solana.stackexchange.com/


Solana Playground (Solpg) - 类似以太坊Remix的web开发环境


网址

Solana Playground (Solpg) https://beta.solpg.io

使用步骤

  1. 连接到 Playground
    点击屏幕左下方的“未连接”按钮

  2. 创建你的钱包
    您将看到一个保存钱包密钥对的选项。(可选)保存钱包密钥对以进行备份,然后单击“继续”。

    您现在应该在窗口底部看到您的钱包地址、SOL 余额和连接的集群(默认为 devnet)。

    您的 Playground 钱包将保存在浏览器的本地存储中。清除浏览器缓存将删除您保存的钱包。

  3. 获取 Devnet SOL
    从开发人员的角度来看,SOL 主要有两个用途:

    1. 创建可以存储数据或部署程序的账户
    2. 与网络交互时支付交易费用

以下是使用 devnet SOL 为你的钱包提供资金的两种方法
选项 1:使用 Playground 终端
要使用 devnet SOL 为您的 Playground 钱包提供资金,请在 Playground 终端中运行:

solana airdrop 5

选项 2:使用 Devnet Faucet
如果空投命令不起作用(由于速率限制或错误),您可以使用Web Faucet

  • 输入你的钱包地址(位于 Playground 屏幕底部)并选择金额
  • 单击“确认空投”以接收您的 devnet SOL


Solana 禁用原有初始化账户


需求

Solana原版代码会在初始化时,预先会创建一系列账户地址和余额
跟进代码,确定初始化地址用途,是否必须的,是否可禁掉

跟进代码

genesis/src/genesis_accounts.rs

fn add_stakes(
    genesis_config: &mut GenesisConfig,
    staker_infos: &[StakerInfo],
    unlock_info: &UnlockInfo,
) -> u64 {
    staker_infos
        .iter()
        .map(|staker_info| create_and_add_stakes(genesis_config, staker_info, unlock_info, None))
        .sum::<u64>()
}

pub fn add_genesis_accounts(genesis_config: &mut GenesisConfig, mut issued_lamports: u64) {
    // add_stakes() and add_validators() award tokens for rent exemption and
    //  to cover an initial transfer-free period of the network

    issued_lamports += add_stakes(
        genesis_config,
        CREATOR_STAKER_INFOS,
        &UNLOCKS_HALF_AT_9_MONTHS,
    ) + add_stakes(
        genesis_config,
        SERVICE_STAKER_INFOS,
        &UNLOCKS_ALL_AT_9_MONTHS,
    ) + add_stakes(
        genesis_config,
        FOUNDATION_STAKER_INFOS,
        &UNLOCKS_ALL_DAY_ZERO,
    ) + add_stakes(genesis_config, GRANTS_STAKER_INFOS, &UNLOCKS_ALL_DAY_ZERO)
        + add_stakes(
            genesis_config,
            COMMUNITY_STAKER_INFOS,
            &UNLOCKS_ALL_DAY_ZERO,
        );

    // "one thanks" (community pool) gets 500_000_000SOL (total) - above distributions
    create_and_add_stakes(
        genesis_config,
        &StakerInfo {
            name: "one thanks",
            staker: "7vEAL3nS9CWmy1q6njUUyHE7Cf5RmyQpND6CsoHjzPiR",
            lamports: (500_000_000 * LAMPORTS_PER_SOL).saturating_sub(issued_lamports),
            withdrawer: Some("3FFaheyqtyAXZSYxDzsr5CVKvJuvZD1WE1VEsBtDbRqB"),
        },
        &UNLOCKS_ALL_DAY_ZERO,
        None,
    );
}

分析

  1. 创建多个质押锁仓地址,并初始化对应量余额
  2. 创建community pool,余额为 500_000_000 - (issued_lamports - faucet_lamports)

从功能来说不是私链初始化链所必须的

Diff

 genesis/src/main.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/genesis/src/main.rs b/genesis/src/main.rs
index 6b7efd5e66..45154443ab 100644
--- a/genesis/src/main.rs
+++ b/genesis/src/main.rs
@@ -15,7 +15,7 @@ use {
         },
     },
     solana_entry::poh::compute_hashes_per_tick,
-    solana_genesis::{genesis_accounts::add_genesis_accounts, Base64Account},
+    solana_genesis::{Base64Account},
     solana_ledger::{blockstore::create_new_ledger, blockstore_options::LedgerColumnOptions},
     solana_sdk::{
         account::{Account, AccountSharedData, ReadableAccount, WritableAccount},
@@ -597,7 +597,7 @@ fn main() -> Result<(), Box<dyn error::Error>> {
         .map(|account| account.lamports)
         .sum::<u64>();

-    add_genesis_accounts(&mut genesis_config, issued_lamports - faucet_lamports);
+    // add_genesis_accounts(&mut genesis_config, issued_lamports - faucet_lamports);

     let parse_address = |address: &str, input_type: &str| {
         address.parse::<Pubkey>().unwrap_or_else(|err| {

测试

  1. 重置链网络
  2. 查看初始化地址,是否有非预期地址
  3. 查看bootstrap-validator 出块,手续费接收等是否正常

Solana 区块浏览器定制网络配置


github: https://github.com/solana-labs/explorer

Diff

From 88f5c222a19c983fae1c950cc3612d6bbc53a31c Mon Sep 17 00:00:00 2001
From: Hendrik Hofstadt <hendrik@bool.capital>
Date: Sun, 18 Feb 2024 10:19:46 +0100
Subject: [PATCH] reduce cluster options to SPE

---
 app/providers/cluster.tsx                  |  5 ++++-
 app/providers/stats/SolanaPingProvider.tsx |  2 +-
 app/utils/cluster.ts                       | 11 +++++++++--
 3 files changed, 14 insertions(+), 4 deletions(-)

diff --git a/app/providers/cluster.tsx b/app/providers/cluster.tsx
index deef8a2..04bcc6b 100644
--- a/app/providers/cluster.tsx
+++ b/app/providers/cluster.tsx
@@ -59,9 +59,12 @@ function parseQuery(searchParams: ReadonlyURLSearchParams | null): Cluster {
             return Cluster.Devnet;
         case 'testnet':
             return Cluster.Testnet;
+        case 'localspe':
+            return Cluster.SPE;
         case 'mainnet-beta':
-        default:
             return Cluster.MainnetBeta;
+        default:
+            return Cluster.SPE;
     }
 }

diff --git a/app/providers/stats/SolanaPingProvider.tsx b/app/providers/stats/SolanaPingProvider.tsx
index 76b2682..6d5df04 100644
--- a/app/providers/stats/SolanaPingProvider.tsx
+++ b/app/providers/stats/SolanaPingProvider.tsx
@@ -12,7 +12,7 @@ const FETCH_PING_INTERVAL = 60 * 1000;
 function getPingUrl(cluster: Cluster) {
     const slug = clusterSlug(cluster);

-    if (slug === 'custom') {
+    if (slug === 'custom' || slug === 'localspe') {
         return undefined;
     }

diff --git a/app/utils/cluster.ts b/app/utils/cluster.ts
index f30963f..734f121 100644
--- a/app/utils/cluster.ts
+++ b/app/utils/cluster.ts
@@ -8,10 +8,11 @@ export enum Cluster {
     MainnetBeta,
     Testnet,
     Devnet,
+    SPE,
     Custom,
 }

-export const CLUSTERS = [Cluster.MainnetBeta, Cluster.Testnet, Cluster.Devnet, Cluster.Custom];
+export const CLUSTERS = [Cluster.SPE, Cluster.Custom];

 export function clusterSlug(cluster: Cluster): string {
     switch (cluster) {
@@ -21,6 +22,8 @@ export function clusterSlug(cluster: Cluster): string {
             return 'testnet';
         case Cluster.Devnet:
             return 'devnet';
+        case Cluster.SPE:
+            return 'localspe';
         case Cluster.Custom:
             return 'custom';
     }
@@ -34,6 +37,8 @@ export function clusterName(cluster: Cluster): string {
             return 'Testnet';
         case Cluster.Devnet:
             return 'Devnet';
+        case Cluster.SPE:
+            return 'Local SPE';
         case Cluster.Custom:
             return 'Custom';
     }
@@ -59,9 +64,11 @@ export function clusterUrl(cluster: Cluster, customUrl: string): string {
             return process.env.NEXT_PUBLIC_MAINNET_RPC_URL ?? modifyUrl(MAINNET_BETA_URL);
         case Cluster.Testnet:
             return process.env.NEXT_PUBLIC_TESTNET_RPC_URL ?? modifyUrl(TESTNET_URL);
+        case Cluster.SPE:
+            return "http://localhost:8899";
         case Cluster.Custom:
             return customUrl;
     }
 }

-export const DEFAULT_CLUSTER = Cluster.MainnetBeta;
+export const DEFAULT_CLUSTER = Cluster.SPE;
--
2.42.0