基于 Solana Playground (pg模式)发行自定义参数的代币

要做的事

Solana 支持简单发币,这种情况无法自定义代币名称、图片,也没有拓展字段。
使用合约 TOKEN_2022_PROGRAM 可以直接实现这个功能。

1、水龙头(devnet 环境)

免费领取 solana
https://faucet.solana.com/

  • Mainnet - https://api.mainnet-beta.solana.com
  • Devnet - https://api.devnet.solana.com
  • Testnet - https://api.testnet.solana.com

2、Solana Playground

https://beta.solpg.io/

2.1、选择环境

选择 devnet
在这里插入图片描述

3、基于 Solana Playground (pg模式)发行自定义参数的代币

3.1、账户体系

需要注意的是,Solana 的账户体系在发币过程需要三个账号。
你的登录账号(用于付费)-代币账号-关联账号(用于承接发行的额度)
https://solana.com/docs/core/tokens
下面代码选择的是:
在这里插入图片描述

3.2、代码

直接粘贴到 https://beta.solpg.io/660cea45cffcf4b13384d012 覆盖即可运行

import * as web3 from "@solana/web3.js";
import * as token from "@solana/spl-token";

import {
  Connection,
  Keypair,
  SystemProgram,
  Transaction,
  clusterApiUrl,
  sendAndConfirmTransaction,
} from "@solana/web3.js";
import {
  ExtensionType,
  TOKEN_2022_PROGRAM_ID,
  createInitializeMintInstruction,
  getMintLen,
  createInitializeMetadataPointerInstruction,
  getMint,
  getMetadataPointerState,
  getTokenMetadata,
  TYPE_SIZE,
  LENGTH_SIZE,
  createMintToInstruction,
} from "@solana/spl-token";
import {
  createInitializeInstruction,
  createUpdateFieldInstruction,
  createRemoveKeyInstruction,
  pack,
  TokenMetadata,
} from "@solana/spl-token-metadata";

// Playground wallet
const payer = pg.wallet.keypair;

// Connection to devnet cluster
const connection = new Connection(clusterApiUrl("devnet"), "confirmed");

// Transaction to send
let transaction: Transaction;
// Transaction signature returned from sent transaction
let transactionSignature: string;

// Generate new keypair for Mint Account
const mintKeypair = Keypair.generate();
// Address for Mint Account
const mint = mintKeypair.publicKey;
// Decimals for Mint Account
const decimals = 2;
// Authority that can mint new tokens
const mintAuthority = pg.wallet.publicKey;
// Authority that can update token metadata
const updateAuthority = pg.wallet.publicKey;

// Metadata to store in Mint Account
const metaData: TokenMetadata = {
  updateAuthority: updateAuthority,
  mint: mint,
  name: "TestDemoJJJ",
  symbol: "TestDemo JJJ",
  //uri: "https://profile-avatar.csdnimg.cn/b521ddb316d54ee0811a45941c53b042_bestcxx.jpg!1",
  uri: "https://raw.githubusercontent.com/solana-developers/opos-asset/main/assets/DeveloperPortal/metadata.json",
  additionalMetadata: [
    ["namea", "aaa"],
    ["ccc", "ddd"],
  ],
};

// Size of MetadataExtension 2 bytes for type, 2 bytes for length
const metadataExtension = TYPE_SIZE + LENGTH_SIZE;
// Size of metadata
const metadataLen = pack(metaData).length;

// Size of Mint Account with extension
const mintLen = getMintLen([ExtensionType.MetadataPointer]);

// Minimum lamports required for Mint Account
const lamports = await connection.getMinimumBalanceForRentExemption(
  mintLen + metadataExtension + metadataLen
);

// Instruction to invoke System Program to create new account
const createAccountInstruction = SystemProgram.createAccount({
  fromPubkey: payer.publicKey, // Account that will transfer lamports to created account
  newAccountPubkey: mint, // Address of the account to create
  space: mintLen, // Amount of bytes to allocate to the created account
  lamports, // Amount of lamports transferred to created account
  programId: TOKEN_2022_PROGRAM_ID, // Program assigned as owner of created account
});

// Instruction to initialize the MetadataPointer Extension
const initializeMetadataPointerInstruction =
  createInitializeMetadataPointerInstruction(
    mint, // Mint Account address
    updateAuthority, // Authority that can set the metadata address
    mint, // Account address that holds the metadata
    TOKEN_2022_PROGRAM_ID
  );

// Instruction to initialize Mint Account data
const initializeMintInstruction = createInitializeMintInstruction(
  mint, // Mint Account Address
  decimals, // Decimals of Mint
  mintAuthority, // Designated Mint Authority
  null, // Optional Freeze Authority
  TOKEN_2022_PROGRAM_ID // Token Extension Program ID
);

// Instruction to initialize Metadata Account data
const initializeMetadataInstruction = createInitializeInstruction({
  programId: TOKEN_2022_PROGRAM_ID, // Token Extension Program as Metadata Program
  metadata: mint, // Account address that holds the metadata
  updateAuthority: updateAuthority, // Authority that can update the metadata
  mint: mint, // Mint Account address
  mintAuthority: mintAuthority, // Designated Mint Authority
  name: metaData.name,
  symbol: metaData.symbol,
  uri: metaData.uri,
});

// Instruction to update metadata, adding custom field
const updateFieldInstruction = createUpdateFieldInstruction({
  programId: TOKEN_2022_PROGRAM_ID, // Token Extension Program as Metadata Program
  metadata: mint, // Account address that holds the metadata
  updateAuthority: updateAuthority, // Authority that can update the metadata
  field: metaData.additionalMetadata[0][0], // key
  value: metaData.additionalMetadata[0][1], // value
});

const updateFieldInstruction1 = createUpdateFieldInstruction({
  programId: TOKEN_2022_PROGRAM_ID, // Token Extension Program as Metadata Program
  metadata: mint, // Account address that holds the metadata
  updateAuthority: updateAuthority, // Authority that can update the metadata
  field: metaData.additionalMetadata[1][0], // key
  value: metaData.additionalMetadata[1][1], // value
});

// Add instructions to new transaction
transaction = new Transaction().add(
  createAccountInstruction,
  initializeMetadataPointerInstruction,
  initializeMintInstruction,
  initializeMetadataInstruction,
  updateFieldInstruction,
  updateFieldInstruction1
);

// Send transaction
transactionSignature = await sendAndConfirmTransaction(
  connection,
  transaction,
  [payer, mintKeypair] // Signers
);

console.log(
  "\nCreate Mint Account:",
  `https://solana.fm/tx/${transactionSignature}?cluster=devnet-solana`
);

// Retrieve mint information
const mintInfo = await getMint(
  connection,
  mint,
  "confirmed",
  TOKEN_2022_PROGRAM_ID
);

// Retrieve and log the metadata pointer state
const metadataPointer = getMetadataPointerState(mintInfo);
console.log("\nMetadata Pointer:", JSON.stringify(metadataPointer, null, 2));

// Retrieve and log the metadata state
const metadata = await getTokenMetadata(
  connection,
  mint // Mint Account address
);
console.log("\nMetadata:", JSON.stringify(metadata, null, 2));

// // Instruction to remove a key from the metadata
// const removeKeyInstruction = createRemoveKeyInstruction({
//   programId: TOKEN_2022_PROGRAM_ID, // Token Extension Program as Metadata Program
//   metadata: mint, // Address of the metadata
//   updateAuthority: updateAuthority, // Authority that can update the metadata
//   key: metaData.additionalMetadata[0][0], // Key to remove from the metadata
//   idempotent: true, // If the idempotent flag is set to true, then the instruction will not error if the key does not exist
// });

// Add instruction to new transaction
//transaction = new Transaction().add(removeKeyInstruction);

// Send transaction
// transactionSignature = await sendAndConfirmTransaction(
//   connection,
//   transaction,
//   [payer]
// );

// console.log(
//   "\nRemove Additional Metadata Field:",
//   `https://solana.fm/tx/${transactionSignature}?cluster=devnet-solana`
// );

//Retrieve and log the metadata state
const updatedMetadata = await getTokenMetadata(
  connection,
  mint // Mint Account address
);
console.log("\nUpdated Metadata:", JSON.stringify(updatedMetadata, null, 2));

console.log(
  "\nMint Account:",
  `https://solana.fm/address/${mint}?cluster=devnet-solana`
);

//todo-begin
try {
  // const transaction = await buildCreateAssociatedTokenAccountTransaction(
  //   payer.publicKey,
  //   mint
  // );

  const associatedTokenAddress = await token.getAssociatedTokenAddress(
    mint,
    payer.publicKey,
    false,
    TOKEN_2022_PROGRAM_ID // 使用SPL Token 2022的程序ID
  );

  console.log(
    "build associatedTokenAddress:",
    associatedTokenAddress.toBase58()
  );

  const transaction = new web3.Transaction().add(
    token.createAssociatedTokenAccountInstruction(
      payer.publicKey,
      associatedTokenAddress,
      payer.publicKey,
      mint,
      TOKEN_2022_PROGRAM_ID // 使用SPL Token 2022的程序ID
    )
  );

  // 指定交易的最近区块哈希
  transaction.recentBlockhash = (
    await connection.getLatestBlockhash()
  ).blockhash;

  // 添加签署者
  transaction.feePayer = payer.publicKey;

  // 签名交易
  transaction.sign(payer);

  // 发送并确认交易
  const transactionSignature = await connection.sendRawTransaction(
    transaction.serialize()
  );

  await connection.confirmTransaction(transactionSignature);

  console.log(
    "buildCreateAssociatedTokenAccountTransaction Transaction successful with signature:",
    transactionSignature
  );
  console.log(
    "\nbuildCreateAssociatedTokenAccountTransaction:",
    `https://solana.fm/tx/${transactionSignature}?cluster=devnet-solana`
  );

  //todo-end

  //todo-begin
  const instruction = createMintToInstruction(
    mint,
    associatedTokenAddress, // 改为使用关联代币账户地址
    mintAuthority, // mint authority 要确保这已被正确设置为 `mint` 的授权者
    1000000000,
    [],
    TOKEN_2022_PROGRAM_ID
  );

  var transaction2 = new Transaction().add(instruction);

  // 指定交易的最近区块哈希
  transaction2.recentBlockhash = (
    await connection.getLatestBlockhash()
  ).blockhash;

  // 添加签署者
  transaction2.feePayer = payer.publicKey;

  // 签名交易
  transaction2.sign(payer);

  // 发送并确认交易
  var transactionSignature2 = await connection.sendRawTransaction(
    transaction2.serialize()
  );

  await connection.confirmTransaction(transactionSignature2);

  console.log(
    "mintTo Transaction successful with signature:",
    transactionSignature2
  );
  console.log(
    "\nmintTo:",
    `https://solana.fm/tx/${transactionSignature2}?cluster=devnet-solana`
  );
  //todo-end
} catch (error) {
  console.error("Error creating associated token account:", error);
}

// function
async function buildCreateAssociatedTokenAccountTransaction(
  payer: web3.PublicKey,
  mint: web3.PublicKey
): Promise<web3.Transaction> {
  const associatedTokenAddress = await token.getAssociatedTokenAddress(
    mint,
    payer,
    false,
    TOKEN_2022_PROGRAM_ID // 使用SPL Token 2022的程序ID
  );

  const transaction = new web3.Transaction().add(
    token.createAssociatedTokenAccountInstruction(
      payer,
      associatedTokenAddress,
      payer,
      mint,
      TOKEN_2022_PROGRAM_ID // 使用SPL Token 2022的程序ID
    )
  );

  return transaction;
}

4、浏览器& Phantom钱包 展示代币图片

4.1、 浏览器和钱包展示的图片来自特定 uri 返回的 json 中的特定字段。

  • Phantom 钱包(谷歌浏览器安装插件):
    [图片]

  • 浏览器
    https://explorer.solana.com/address/Fg6t4x38NuxN**********zdhs?cluster=devnet
    在这里插入图片描述

4.2、需要怎么做?

需要提供一个可以公开访问的 url,返回固定信息的 json
比如 :https://raw.githubusercontent.com/solana-developers/opos-asset/main/assets/DeveloperPortal/metadata.json

{
    "name": "OPOS",
    "symbol": "OPOS",
    "description": "Only Possible On Solana",
    "image": "https://raw.githubusercontent.com/solana-developers/opos-asset/main/assets/DeveloperPortal/image.png",
    "attributes": [
      {
        "trait_type": "Item",
        "value": "Developer Portal"
      }
    ]
  }

应用方法是,把这个 url 传入到创建代币的入参uri 中

// Metadata to store in Mint Account
const metaData: TokenMetadata = {
  updateAuthority: updateAuthority,
  mint: mint,
  name: "TestDemoJJJ",
  symbol: "TestDemo JJJ",
  //uri: "https://profile-avatar.csdnimg.cn/b521ddb316d54ee0811a45941c53b042_bestcxx.jpg!1",
  uri: "https://raw.githubusercontent.com/solana-developers/opos-asset/main/assets/DeveloperPortal/metadata.json",
  additionalMetadata: [
    ["namea", "aaa"],
    ["ccc", "ddd"],
  ],
};

5、更多 api 接口

https://solana.com/zh/docs/rpc/http/gettokenaccountsbyowner#code-sample
https://solana.com/zh/docs/rpc/http/getsupply

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值