Solana 私有链与Token
# macOS
brew install solana
solana-keygen new
# 生成私钥文件 ~/.config/solana/id.json
# 如果要指定私钥文件生成的位置,使用 solana-keygen new -o key.json
查看账户地址:
solana address
输出:
2LJPdSc2GAsQrkbn8mN1DMPwAi5FEFqnesNLBH9rK41L
Docker 启动私有链docker-compose.yaml
:
version: '3.8'
services:
solana-test-validator:
image: tchambard/solana-test-validator:latest
container_name: solana-test-validator
command: solana-test-validator --ledger ledger
volumes:
- /root/docker/solana-test-validator/ledger:/working-dir/ledger:rw
ports:
- "8899:8899"
- "8900:8900"
restart: unless-stopped
客户端配置:
# Main Net
solana config set --url https://api.mainnet-beta.solana.com
# Dev Net
solana config set --url https://api.devnet.solana.com
# Local Net
solana config set --url http://127.0.0.1:8899
# Private Net
solana config set --url https://dev.flxdu.cn/solana
对账户~/.config/solana/id.json
发放空投:
solana airdrop 20
查询sol余额:
solana balance
测试是否可用:
solana ping
# 此操作会向链上发送交易,会产生Sol开销(但是损失很小)。
创建 Token
装个rust:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
安装 spl-token
CLI
solana
包含了 spl-token
工具,因此只要你安装了 Solana CLI,你就可以直接使用 spl-token
工具。如果你没有安装 Solana CLI,可以通过以下步骤单独安装 spl-token
。
cargo install spl-token-cli
cargo install
会自动从 Rust 的包管理系统(crates.io
)下载并构建 spl-token-cli
工具。
验证安装
安装完成后,可以使用以下命令验证是否安装成功:
spl-token --version
如果安装成功,会显示当前安装的 spl-token-cli
版本。
创建Token
接下来执行命令,都是以~/.config/solana/id.json
私钥的身份执行的,如果有花费Sol的操作,均由~/.config/solana/id.json
发出交易并支付Gas。
spl-token create-token --enable-freeze
输出类似如下:
Creating token 7ojF7rnLawUCKMXVTMmU6aPLfDKKvw9Lzfj24oLB1vXq under program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
Address: 7ojF7rnLawUCKMXVTMmU6aPLfDKKvw9Lzfj24oLB1vXq
Decimals: 9
Signature: 2Pazt9uhHrbVv9bBdBuw5p6ypiVNtMPJZWh1CZZq6C1axRJszPSP7cZg8nJhdpRLRWSczfbxWM1KERwuYAwqyTPa
收到了代币 ID 和签名。然后我们可以利用代币 ID 来检查代币的发行量:
spl-token supply 7ojF7rnLawUCKMXVTMmU6aPLfDKKvw9Lzfj24oLB1vXq
创建一个代币账户:
# ~/.config/solana/id.json 针对代币 7ojF7rnLawUCKMXVTMmU6aPLfDKKvw9Lzfj24oLB1vXq 的账户地址
spl-token create-account 7ojF7rnLawUCKMXVTMmU6aPLfDKKvw9Lzfj24oLB1vXq
输出:
Creating account EMhH7nif2cGtCbRFzfW5zL7M5gskLZEPEAp98R6QSQCC
Signature: 5YSvadDyYFpLetkkGh8aWibWe46Kf4WeFCZ95wsj12YHPRcjavHAssNBQKsdZ3p6vMV5RYmbZYD8fnJ1YECGmVtR
发行代币:
spl-token mint 7ojF7rnLawUCKMXVTMmU6aPLfDKKvw9Lzfj24oLB1vXq 1000000000 EMhH7nif2cGtCbRFzfW5zL7M5gskLZEPEAp98R6QSQCC
输出如下:
Minting 10000000 tokens
Token: 7ojF7rnLawUCKMXVTMmU6aPLfDKKvw9Lzfj24oLB1vXq
Recipient: EMhH7nif2cGtCbRFzfW5zL7M5gskLZEPEAp98R6QSQCC
Signature: 3pedDzGyHH5FshZKxnyjDTfhKE5cU6CTJqypiQgJPgcBsGhMcxrF4dRCCge7aSze3CCNBW58Z8PN1Gv1hZwbn7w7
检查账户余额: 一旦 mint 完成,你可以检查新代币账户的余额:
# 此命令查询 ~/.config/solana/id.json 拥有代币 7ojF7rnLawUCKMXVTMmU6aPLfDKKvw9Lzfj24oLB1vXq 的数量
spl-token balance 7ojF7rnLawUCKMXVTMmU6aPLfDKKvw9Lzfj24oLB1vXq
创建一个账户2,用于接收转账:
solana-keygen new -o solana2.key
账户2要创建针对此地址的代币账户:
spl-token create-account 7ojF7rnLawUCKMXVTMmU6aPLfDKKvw9Lzfj24oLB1vXq
转账:
# 从 ~/.config/solana/id.json 向 AX7iQ6senZ5NxDf6P63NGX2G7KTdywNuCtsSjDG9fuww 转出代币 7ojF7rnLawUCKMXVTMmU6aPLfDKKvw9Lzfj24oLB1vXq,数量100
spl-token transfer --fund-recipient 7ojF7rnLawUCKMXVTMmU6aPLfDKKvw9Lzfj24oLB1vXq 100 AX7iQ6senZ5NxDf6P63NGX2G7KTdywNuCtsSjDG9fuww
输出:
Transfer 100 tokens
Sender: EMhH7nif2cGtCbRFzfW5zL7M5gskLZEPEAp98R6QSQCC
Recipient: AX7iQ6senZ5NxDf6P63NGX2G7KTdywNuCtsSjDG9fuww
Recipient associated token account: AzHb1tc4dbKjYhYr2krkKjjedwXGFuWzYz4vzFPwQ96i
Funding recipient: AzHb1tc4dbKjYhYr2krkKjjedwXGFuWzYz4vzFPwQ96i
Signature: bKVqSEtB6miMAgDuJikJcr4STgmuPHksM1Zj8sbeWVAvBhJqPSNkYd3NxUJa2aX9miuKv2jtcZDtdz4uwNNmGvX
查询默认账户余额:
spl-token balance 7ojF7rnLawUCKMXVTMmU6aPLfDKKvw9Lzfj24oLB1vXq
# 输出 9999900
查询账户2余额:
spl-token balance 7ojF7rnLawUCKMXVTMmU6aPLfDKKvw9Lzfj24oLB1vXq --owner AX7iQ6senZ5NxDf6P63NGX2G7KTdywNuCtsSjDG9fuww
# 输出 100
冻结账户:
# 需要使用账户地址
spl-token freeze AzHb1tc4dbKjYhYr2krkKjjedwXGFuWzYz4vzFPwQ96i
获取一个地址<OWNER_ADDRESS>
针对代币 <TOKEN_MINT_ADDRESS>
的账户地址:
spl-token address --verbose --token <TOKEN_MINT_ADDRESS> --owner <OWNER_ADDRESS>
放弃一些权力:
spl-token authorize <mint_address> mint --disable # 放弃铸币权
spl-token authorize <mint_address> freeze --disable # 放弃冻结权
对 Token 添加名称和Logo
在 Pinata 将Logo图片上传至IPFS。
将Logo链接写到此文件中:
{
"name": "White Dog Token",
"symbol": "WDT",
"description": "A white dog token",
"image": "https://scarlet-faithful-marten-470.mypinata.cloud/ipfs/bafkreig7mn5t6galamlqesnmq54h32wikr4s2qmplew4kh6dufgycejati",
"attributes": []
}
把此文件也上传至 IPFS,得到此文件的链接:https://scarlet-faithful-marten-470.mypinata.cloud/ipfs/bafkreiajb5eihbx5xhbqiblhqeo7345woynsqm4i7gj5yb7qytb2zcu52q
执行代码即可,注意修改其中的参数:
const {
createMetadataAccountV3
} = require("@metaplex-foundation/mpl-token-metadata");
const web3 = require("@solana/web3.js");
const {createSignerFromKeypair, none, signerIdentity} = require("@metaplex-foundation/umi");
const {createUmi} = require('@metaplex-foundation/umi-bundle-defaults');
const {fromWeb3JsKeypair, fromWeb3JsPublicKey} = require('@metaplex-foundation/umi-web3js-adapters');
// 从文件加载钱包密钥
function loadWalletKey(keypairFile) {
const fs = require("fs");
return web3.Keypair.fromSecretKey(
new Uint8Array(JSON.parse(fs.readFileSync(keypairFile).toString()))
);
}
async function main() {
console.log("RUN...");
// 加载钱包密钥对并设置铸币地址
const myKeypair = loadWalletKey("~/.config/solana/id_devnet.json");
const mint = new web3.PublicKey("3k5asAfFbvz7jZ3sTFqGCxyRPvNTNSD1kLouXUWe9QP2");
// 使用自定义 RPC 连接到 Solana 开发网络
// const umi = createUmi("https://dev.flxdu.cn/solana");
const umi = createUmi("https://devnet.helius-rpc.com/?api-key=YourAPIKey");
// const umi = createUmi("https://api.devnet.solana.com");
// const umi = createUmi("https://api.mainnet-beta.solana.com");
// 设置签名者身份
const signer = createSignerFromKeypair(umi, fromWeb3JsKeypair(myKeypair));
umi.use(signerIdentity(signer, true));
// 定义代币的元数据
const onChainData = {
name: "White Dog Token", // 代币名称
symbol: "WDT", // 代币符号
uri: "https://scarlet-faithful-marten-470.mypinata.cloud/ipfs/bafkreiajb5eihbx5xhbqiblhqeo7345woynsqm4i7gj5yb7qytb2zcu52q", // 元数据 JSON 文件链接
sellerFeeBasisPoints: 0, // 设置销售费用为0
creators: none(),
collection: none(),
uses: none(),
};
// 设置铸币授权和铸币地址
const accounts = {
mint: fromWeb3JsPublicKey(mint),
mintAuthority: signer,
};
// 创建元数据账户
const txid = await createMetadataAccountV3(umi, {
...accounts,
isMutable: true, // 设置元数据可修改
collectionDetails: null,
data: onChainData,
}).sendAndConfirm(umi);
console.log(txid);
}
main();
随后在浏览器上就可以看到:https://explorer.solana.com/address/3k5asAfFbvz7jZ3sTFqGCxyRPvNTNSD1kLouXUWe9QP2?cluster=devnet
添加流动性
通过Raydium来添加流动性,raydium在主网和开发网上都部署了相关程序,地址在这里:https://docs.raydium.io/raydium/protocol/developers/addresses。
Raydium 的网站虽然允许设置自定义RPC连接,但是他仅支持主网,也就是说自定义RPC连接也必须是指向主网的RPC链接。

solana 私有链 Nginx 反向代理
Docker 启动私有链之后,可以使用 Nginx 做反向代理:
location /solana {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_read_timeout 600s;
proxy_send_timeout 600s;
if ($http_upgrade = "websocket") {
proxy_pass http://host.docker.internal:8900;
break;
}
proxy_pass http://host.docker.internal:8899;
}
Base58 私钥转 JSON
import base58
import json
from solders.keypair import Keypair
def base58_to_json(private_key_base58: str) -> list:
# 将 Base58 编码的私钥转换为字节
private_key_bytes = base58.b58decode(private_key_base58)
# 确保私钥长度是 64 字节
if len(private_key_bytes) != 64:
raise ValueError("私钥长度不正确,应为 64 字节")
# 创建一个 Solana Keypair 对象
keypair = Keypair.from_bytes(private_key_bytes)
pubkey = json.loads(keypair.pubkey().to_json())
secretkey = list(keypair.secret())
return secretkey + pubkey
# 将包含私钥和公钥的 JSON 数据转换回 Base58 编码的私钥字符串。
def json_to_base58(key: list) -> str:
# 提取密钥信息
if not isinstance(key, list) or len(key) != 64:
raise ValueError("输入的 JSON 数据格式不正确,应为包含 64 个字节的列表")
# 将密钥列表转换为字节数组
private_key_bytes = bytes(key)
# 确保长度为 64 字节
if len(private_key_bytes) != 64:
raise ValueError("密钥字节数组长度不正确,应为 64 字节")
# 编码为 Base58
private_key_base58 = base58.b58encode(private_key_bytes).decode("utf-8")
return private_key_base58
if __name__ == "__main__":
key_base58 = "" # 私钥 Base58 字符串
assert (len(key_base58) != 0)
res = base58_to_json(key_base58)
outputFile = "keypair_devnet.json"
with open(outputFile, "w") as f:
json.dump(res, f)
with open(outputFile, "r") as f:
assert json_to_base58(json.load(f)) == key_base58
修改私钥配置:
solana config set --keypair keypair_devnet.json