Staking

Truy vấn danh sách Validators hiện tại

Chúng ta có thể stake SOL và kiếm phần thưởng cho việc nâng cao an toàn mạng lưới. Để stake, bạn phải uỷ quyền SOL có các validators người sẽ thực hiện việc xử lý các giao dịch.

import { clusterApiUrl, Connection } from "@solana/web3.js";

(async () => {
  const connection = new Connection(clusterApiUrl("devnet"), "confirmed");

  // Get all validators, categorized by current (i.e. active) and deliquent (i.e. inactive)
  const { current, delinquent } = await connection.getVoteAccounts();
  console.log("current validators: ", current);
  console.log("all validators: ", current.concat(delinquent));
})();
solana validators

Tạo một Stake Account

Tất cả các chỉ thị staking sẽ được đảm nhiệm bởi Stake Programopen in new window. Để bắt đầu, chúng ta tạo ra một Stake Accountopen in new window. Stake Account được tạo ra và quản lý rất khác so với một System Account chuẩn. Thực tế, chúng ta phải đặt Stake AuthorityWithdrawal Authority của account đó.

Press </> button to view full source
import {
  clusterApiUrl,
  Connection,
  Keypair,
  LAMPORTS_PER_SOL,
  StakeProgram,
  Authorized,
  sendAndConfirmTransaction,
  Lockup,
} from "@solana/web3.js";

(async () => {
  // Setup our connection and wallet
  const connection = new Connection(clusterApiUrl("devnet"), "confirmed");
  const wallet = Keypair.generate();

  // Fund our wallet with 1 SOL
  const airdropSignature = await connection.requestAirdrop(
    wallet.publicKey,
    LAMPORTS_PER_SOL
  );
  await connection.confirmTransaction(airdropSignature);

  // Create a keypair for our stake account
  const stakeAccount = Keypair.generate();

  // Calculate how much we want to stake
  const minimumRent = await connection.getMinimumBalanceForRentExemption(
    StakeProgram.space
  );
  const amountUserWantsToStake = LAMPORTS_PER_SOL / 2; // This is can be user input. For now, we'll hardcode to 0.5 SOL
  const amountToStake = minimumRent + amountUserWantsToStake;

  // Setup a transaction to create our stake account
  // Note: `StakeProgram.createAccount` returns a `Transaction` preconfigured with the necessary `TransactionInstruction`s
  const createStakeAccountTx = StakeProgram.createAccount({
    authorized: new Authorized(wallet.publicKey, wallet.publicKey), // Here we set two authorities: Stake Authority and Withdrawal Authority. Both are set to our wallet.
    fromPubkey: wallet.publicKey,
    lamports: amountToStake,
    lockup: new Lockup(0, 0, wallet.publicKey), // Optional. We'll set this to 0 for demonstration purposes.
    stakePubkey: stakeAccount.publicKey,
  });

  const createStakeAccountTxId = await sendAndConfirmTransaction(
    connection,
    createStakeAccountTx,
    [
      wallet,
      stakeAccount, // Since we're creating a new stake account, we have that account sign as well
    ]
  );
  console.log(`Stake account created. Tx Id: ${createStakeAccountTxId}`);

  // Check our newly created stake account balance. This should be 0.5 SOL.
  let stakeBalance = await connection.getBalance(stakeAccount.publicKey);
  console.log(`Stake account balance: ${stakeBalance / LAMPORTS_PER_SOL} SOL`);

  // Verify the status of our stake account. This will start as inactive and will take some time to activate.
  let stakeStatus = await connection.getStakeActivation(stakeAccount.publicKey);
  console.log(`Stake account status: ${stakeStatus.state}`);
})();

Uỷ quyền Stake

Một khi stake account đã được cọc, Stake Authority có thể uỷ quyền của nó cho một validator. Mỗi stake account có thể chỉ uỷ quyền cho một validator tại một thời điểm. Thêm nữa, tất cả token trong account hoặc là phải để được uỷ quyền, hoặc là phải không uỷ quyền cho bất kỳ ai. Khi đã uỷ quyền, sẽ phải tốn một và epoch để stake account có thể hoạt động.

Press </> button to view full source
import {
  clusterApiUrl,
  Connection,
  Keypair,
  LAMPORTS_PER_SOL,
  StakeProgram,
  Authorized,
  sendAndConfirmTransaction,
  Lockup,
  PublicKey,
} from "@solana/web3.js";

(async () => {
  // Setup our connection and wallet
  const connection = new Connection(clusterApiUrl("devnet"), "confirmed");
  const wallet = Keypair.generate();

  // Fund our wallet with 1 SOL
  const airdropSignature = await connection.requestAirdrop(
    wallet.publicKey,
    LAMPORTS_PER_SOL
  );
  await connection.confirmTransaction(airdropSignature);

  // Create a keypair for our stake account
  const stakeAccount = Keypair.generate();

  // Calculate how much we want to stake
  const minimumRent = await connection.getMinimumBalanceForRentExemption(
    StakeProgram.space
  );
  const amountUserWantsToStake = LAMPORTS_PER_SOL / 2; // This is can be user input. For now, we'll hardcode to 0.5 SOL
  const amountToStake = minimumRent + amountUserWantsToStake;

  // Setup a transaction to create our stake account
  // Note: `StakeProgram.createAccount` returns a `Transaction` preconfigured with the necessary `TransactionInstruction`s
  const createStakeAccountTx = StakeProgram.createAccount({
    authorized: new Authorized(wallet.publicKey, wallet.publicKey), // Here we set two authorities: Stake Authority and Withdrawal Authority. Both are set to our wallet.
    fromPubkey: wallet.publicKey,
    lamports: amountToStake,
    lockup: new Lockup(0, 0, wallet.publicKey), // Optional. We'll set this to 0 for demonstration purposes.
    stakePubkey: stakeAccount.publicKey,
  });

  const createStakeAccountTxId = await sendAndConfirmTransaction(
    connection,
    createStakeAccountTx,
    [
      wallet,
      stakeAccount, // Since we're creating a new stake account, we have that account sign as well
    ]
  );
  console.log(`Stake account created. Tx Id: ${createStakeAccountTxId}`);

  // Check our newly created stake account balance. This should be 0.5 SOL.
  let stakeBalance = await connection.getBalance(stakeAccount.publicKey);
  console.log(`Stake account balance: ${stakeBalance / LAMPORTS_PER_SOL} SOL`);

  // Verify the status of our stake account. This will start as inactive and will take some time to activate.
  let stakeStatus = await connection.getStakeActivation(stakeAccount.publicKey);
  console.log(`Stake account status: ${stakeStatus.state}`);

  // To delegate our stake, we first have to select a validator. Here we get all validators and select the first active one.
  const validators = await connection.getVoteAccounts();
  const selectedValidator = validators.current[0];
  const selectedValidatorPubkey = new PublicKey(selectedValidator.votePubkey);

  // With a validator selected, we can now setup a transaction that delegates our stake to their vote account.
  const delegateTx = StakeProgram.delegate({
    stakePubkey: stakeAccount.publicKey,
    authorizedPubkey: wallet.publicKey,
    votePubkey: selectedValidatorPubkey,
  });

  const delegateTxId = await sendAndConfirmTransaction(connection, delegateTx, [
    wallet,
  ]);
  console.log(
    `Stake account delegated to ${selectedValidatorPubkey}. Tx Id: ${delegateTxId}`
  );

  // Check in on our stake account. It should now be activating.
  stakeStatus = await connection.getStakeActivation(stakeAccount.publicKey);
  console.log(`Stake account status: ${stakeStatus.state}`);
})();

Truy vấn các nhà uỷ quyền bằng validator

Nhiều account có thể stake và một validator cụ thể. Để truy vấn tất cả các staker, bạn sẽ sử dụng getProgramAccounts hoặc getParsedProgramAccounts API. Tham khảo Phần hướng dẫn cho nhiều thông tin hơn. Stake account có độ dài là 200 bytes với Voter Public Key bắt đầu ở bytes thứ 124. Tham khảoopen in new window.

Press </> button to view full source
import { clusterApiUrl, Connection, PublicKey } from "@solana/web3.js";

(async () => {
  const STAKE_PROGRAM_ID = new PublicKey(
    "Stake11111111111111111111111111111111111111"
  );
  const VOTE_PUB_KEY = "27MtjMSAQ2BGkXNuJEJkxFyCJT8dugGAaHJ9T7Gc6x4x";

  const connection = new Connection(clusterApiUrl("mainnet-beta"), "confirmed");
  const accounts = await connection.getParsedProgramAccounts(STAKE_PROGRAM_ID, {
    filters: [
      {
        dataSize: 200, // number of bytes
      },
      {
        memcmp: {
          offset: 124, // number of bytes
          bytes: VOTE_PUB_KEY, // base58 encoded string
        },
      },
    ],
  });

  console.log(`Accounts for program ${STAKE_PROGRAM_ID}: `);
  console.log(
    `Total number of delegators found for ${VOTE_PUB_KEY} is: ${accounts.length}`
  );
  if (accounts.length)
    console.log(`Sample delegator:`, JSON.stringify(accounts[0]));

  /*
  // Output

    Accounts for program Stake11111111111111111111111111111111111111: 
    Total number of delegators found for 27MtjMSAQ2BGkXNuJEJkxFyCJT8dugGAaHJ9T7Gc6x4x is: 184
    Sample delegator: 
    {
      "account": {
        "data": {
          "parsed": {
            "info": {
              "meta": {
                "authorized": {
                  "staker": "3VDVh3rHTLkNJp6FVYbuFcaihYBFCQX5VSBZk23ckDGV",
                  "withdrawer": "EhYXq3ANp5nAerUpbSgd7VK2RRcxK1zNuSQ755G5Mtxx"
                },
                "lockup": {
                  "custodian": "3XdBZcURF5nKg3oTZAcfQZg8XEc5eKsx6vK8r3BdGGxg",
                  "epoch": 0,
                  "unixTimestamp": 1822867200
                },
                "rentExemptReserve": "2282880"
              },
              "stake": {
                "creditsObserved": 58685367,
                "delegation": {
                  "activationEpoch": "208",
                  "deactivationEpoch": "18446744073709551615",
                  "stake": "433005300621",
                  "voter": "27MtjMSAQ2BGkXNuJEJkxFyCJT8dugGAaHJ9T7Gc6x4x",
                  "warmupCooldownRate": 0.25
                }
              }
            },
            "type": "delegated"
          },
          "program": "stake",
          "space": 200
        },
        "executable": false,
        "lamports": 433012149261,
        "owner": {
          "_bn": "06a1d8179137542a983437bdfe2a7ab2557f535c8a78722b68a49dc000000000"
        },
        "rentEpoch": 264
      },
      "pubkey": {
        "_bn": "0dc8b506f95e52c9ac725e714c7078799dd3268df562161411fe0916a4dc0a43"
      }
    }

  */
})();

Dừng stake

Bất kỳ lúc nào sau khi một stake account được uỷ quyền, Stake Authority có thể được chọn để dừng account. Dừng stake có thể mất một khoảng thời gian để hoàn thành, và phải được hoàn thành trước có thể rút SOL.

Press </> button to view full source
import {
  clusterApiUrl,
  Connection,
  Keypair,
  LAMPORTS_PER_SOL,
  StakeProgram,
  Authorized,
  sendAndConfirmTransaction,
  Lockup,
  PublicKey,
} from "@solana/web3.js";

(async () => {
  // Setup our connection and wallet
  const connection = new Connection(clusterApiUrl("devnet"), "confirmed");
  const wallet = Keypair.generate();

  // Fund our wallet with 1 SOL
  const airdropSignature = await connection.requestAirdrop(
    wallet.publicKey,
    LAMPORTS_PER_SOL
  );
  await connection.confirmTransaction(airdropSignature);

  // Create a keypair for our stake account
  const stakeAccount = Keypair.generate();

  // Calculate how much we want to stake
  const minimumRent = await connection.getMinimumBalanceForRentExemption(
    StakeProgram.space
  );
  const amountUserWantsToStake = LAMPORTS_PER_SOL / 2; // This is can be user input. For now, we'll hardcode to 0.5 SOL
  const amountToStake = minimumRent + amountUserWantsToStake;

  // Setup a transaction to create our stake account
  // Note: `StakeProgram.createAccount` returns a `Transaction` preconfigured with the necessary `TransactionInstruction`s
  const createStakeAccountTx = StakeProgram.createAccount({
    authorized: new Authorized(wallet.publicKey, wallet.publicKey), // Here we set two authorities: Stake Authority and Withdrawal Authority. Both are set to our wallet.
    fromPubkey: wallet.publicKey,
    lamports: amountToStake,
    lockup: new Lockup(0, 0, wallet.publicKey), // Optional. We'll set this to 0 for demonstration purposes.
    stakePubkey: stakeAccount.publicKey,
  });

  const createStakeAccountTxId = await sendAndConfirmTransaction(
    connection,
    createStakeAccountTx,
    [
      wallet,
      stakeAccount, // Since we're creating a new stake account, we have that account sign as well
    ]
  );
  console.log(`Stake account created. Tx Id: ${createStakeAccountTxId}`);

  // Check our newly created stake account balance. This should be 0.5 SOL.
  let stakeBalance = await connection.getBalance(stakeAccount.publicKey);
  console.log(`Stake account balance: ${stakeBalance / LAMPORTS_PER_SOL} SOL`);

  // Verify the status of our stake account. This will start as inactive and will take some time to activate.
  let stakeStatus = await connection.getStakeActivation(stakeAccount.publicKey);
  console.log(`Stake account status: ${stakeStatus.state}`);

  // To delegate our stake, we first have to select a validator. Here we get all validators and select the first active one.
  const validators = await connection.getVoteAccounts();
  const selectedValidator = validators.current[0];
  const selectedValidatorPubkey = new PublicKey(selectedValidator.votePubkey);

  // With a validator selected, we can now setup a transaction that delegates our stake to their vote account.
  const delegateTx = StakeProgram.delegate({
    stakePubkey: stakeAccount.publicKey,
    authorizedPubkey: wallet.publicKey,
    votePubkey: selectedValidatorPubkey,
  });

  const delegateTxId = await sendAndConfirmTransaction(connection, delegateTx, [
    wallet,
  ]);
  console.log(
    `Stake account delegated to ${selectedValidatorPubkey}. Tx Id: ${delegateTxId}`
  );

  // Check in on our stake account. It should now be activating.
  stakeStatus = await connection.getStakeActivation(stakeAccount.publicKey);
  console.log(`Stake account status: ${stakeStatus.state}`);

  // At anytime we can choose to deactivate our stake. Our stake account must be inactive before we can withdraw funds.
  const deactivateTx = StakeProgram.deactivate({
    stakePubkey: stakeAccount.publicKey,
    authorizedPubkey: wallet.publicKey,
  });
  const deactivateTxId = await sendAndConfirmTransaction(
    connection,
    deactivateTx,
    [wallet]
  );
  console.log(`Stake account deactivated. Tx Id: ${deactivateTxId}`);

  // Check in on our stake account. It should now be inactive.
  stakeStatus = await connection.getStakeActivation(stakeAccount.publicKey);
  console.log(`Stake account status: ${stakeStatus.state}`);
})();

Rút Stake

Một khi đã dừng stake, Withdrawal Authority có thể rút SOL về lại một system account. Và khi một stake account không còn được uỷ quyền cũng như có số dư là 0 SOL, nó sẽ bị huỷ ngay lập tức.

Press </> button to view full source
import {
  clusterApiUrl,
  Connection,
  Keypair,
  LAMPORTS_PER_SOL,
  StakeProgram,
  Authorized,
  sendAndConfirmTransaction,
  Lockup,
  PublicKey,
} from "@solana/web3.js";

(async () => {
  // Setup our connection and wallet
  const connection = new Connection(clusterApiUrl("devnet"), "confirmed");
  const wallet = Keypair.generate();

  // Fund our wallet with 1 SOL
  const airdropSignature = await connection.requestAirdrop(
    wallet.publicKey,
    LAMPORTS_PER_SOL
  );
  await connection.confirmTransaction(airdropSignature);

  // Create a keypair for our stake account
  const stakeAccount = Keypair.generate();

  // Calculate how much we want to stake
  const minimumRent = await connection.getMinimumBalanceForRentExemption(
    StakeProgram.space
  );
  const amountUserWantsToStake = LAMPORTS_PER_SOL / 2; // This is can be user input. For now, we'll hardcode to 0.5 SOL
  const amountToStake = minimumRent + amountUserWantsToStake;

  // Setup a transaction to create our stake account
  // Note: `StakeProgram.createAccount` returns a `Transaction` preconfigured with the necessary `TransactionInstruction`s
  const createStakeAccountTx = StakeProgram.createAccount({
    authorized: new Authorized(wallet.publicKey, wallet.publicKey), // Here we set two authorities: Stake Authority and Withdrawal Authority. Both are set to our wallet.
    fromPubkey: wallet.publicKey,
    lamports: amountToStake,
    lockup: new Lockup(0, 0, wallet.publicKey), // Optional. We'll set this to 0 for demonstration purposes.
    stakePubkey: stakeAccount.publicKey,
  });

  const createStakeAccountTxId = await sendAndConfirmTransaction(
    connection,
    createStakeAccountTx,
    [
      wallet,
      stakeAccount, // Since we're creating a new stake account, we have that account sign as well
    ]
  );
  console.log(`Stake account created. Tx Id: ${createStakeAccountTxId}`);

  // Check our newly created stake account balance. This should be 0.5 SOL.
  let stakeBalance = await connection.getBalance(stakeAccount.publicKey);
  console.log(`Stake account balance: ${stakeBalance / LAMPORTS_PER_SOL} SOL`);

  // Verify the status of our stake account. This will start as inactive and will take some time to activate.
  let stakeStatus = await connection.getStakeActivation(stakeAccount.publicKey);
  console.log(`Stake account status: ${stakeStatus.state}`);

  // To delegate our stake, we first have to select a validator. Here we get all validators and select the first active one.
  const validators = await connection.getVoteAccounts();
  const selectedValidator = validators.current[0];
  const selectedValidatorPubkey = new PublicKey(selectedValidator.votePubkey);

  // With a validator selected, we can now setup a transaction that delegates our stake to their vote account.
  const delegateTx = StakeProgram.delegate({
    stakePubkey: stakeAccount.publicKey,
    authorizedPubkey: wallet.publicKey,
    votePubkey: selectedValidatorPubkey,
  });

  const delegateTxId = await sendAndConfirmTransaction(connection, delegateTx, [
    wallet,
  ]);
  console.log(
    `Stake account delegated to ${selectedValidatorPubkey}. Tx Id: ${delegateTxId}`
  );

  // Check in on our stake account. It should now be activating.
  stakeStatus = await connection.getStakeActivation(stakeAccount.publicKey);
  console.log(`Stake account status: ${stakeStatus.state}`);

  // At anytime we can choose to deactivate our stake. Our stake account must be inactive before we can withdraw funds.
  const deactivateTx = StakeProgram.deactivate({
    stakePubkey: stakeAccount.publicKey,
    authorizedPubkey: wallet.publicKey,
  });
  const deactivateTxId = await sendAndConfirmTransaction(
    connection,
    deactivateTx,
    [wallet]
  );
  console.log(`Stake account deactivated. Tx Id: ${deactivateTxId}`);

  // Check in on our stake account. It should now be inactive.
  stakeStatus = await connection.getStakeActivation(stakeAccount.publicKey);
  console.log(`Stake account status: ${stakeStatus.state}`);

  // Once deactivated, we can withdraw our SOL back to our main wallet
  const withdrawTx = StakeProgram.withdraw({
    stakePubkey: stakeAccount.publicKey,
    authorizedPubkey: wallet.publicKey,
    toPubkey: wallet.publicKey,
    lamports: stakeBalance, // Withdraw the full balance at the time of the transaction
  });

  const withdrawTxId = await sendAndConfirmTransaction(connection, withdrawTx, [
    wallet,
  ]);
  console.log(`Stake account withdrawn. Tx Id: ${withdrawTxId}`);

  // Confirm that our stake account balance is now 0
  stakeBalance = await connection.getBalance(stakeAccount.publicKey);
  console.log(`Stake account balance: ${stakeBalance / LAMPORTS_PER_SOL} SOL`);
})();
Last Updated:
Contributors: tuphan-dn