MỚI: Helius mua lại Light Protocol
cách gửi giao dịch Solana bằng nonce bền vững
Blog/Phát triển

Giao dịch Solana: Nonce bền vững (2023)

Trưởng bộ phận Quan hệ Nhà phát triểnHunter Davis trên LinkedIn
Đọc trong 6 phút

Giới thiệu

Khi gửi một giao dịch trên Solana với tư cách nhà phát triển, bạn cần lấy blockhash mới nhất qua Solana RPC. Bước quan trọng này giúp giảm thiểu các cuộc tấn công phát lại, đảm bảo rằng sau khi một giao dịch được ký và gửi bằng một blockhash cụ thể, không ai có thể dùng lại cùng hash đó để phát lại hoặc gửi lại giao dịch.

Hãy tưởng tượng bạn cần gửi một giao dịch yêu cầu chữ ký từ kho lạnh ngoại tuyến hoặc ví phần cứng như Ledger. Tuy nhiên, blockhash hết hạn rất nhanh và có thể khiến giao dịch không còn hợp lệ. Đây là lúc nonce bền vững phát huy tác dụng, cho phép thực hiện các giao dịch ngoại tuyến an toàn.

Sau khi hoàn thành hướng dẫn này, bạn sẽ hiểu:

  1. Nonce bền vững là gì.
  2. Mục đích của nonce bền vững.
  3. Cách sử dụng nonce bền vững trong giao dịch.

Giao dịch: Điều kiện tiên quyết

Trước khi bắt đầu, hãy đảm bảo bạn có:

  • Kiến thức JavaScript cơ bản.
  • Đã cài đặt NodeJS.
  • Đã cài đặt Solana CLI.
  • Đã cài đặt Git

Thiết lập môi trường

  1. Sao chép kho lưu trữ mẫu có sẵn các tiện ích của chúng tôi:
Mã
git clone
  1. Chuyển đến thư mục dự án và cài đặt npm:
Mã
cd durable-noncenpm install
  1. Chuyển đến thư mục wallets bên trong thư mục nonce. Đây sẽ là nơi lưu các khóa cục bộ để kiểm thử:
Mã
cd wallets
  1. Sau khi cài đặt Solana CLI, hãy tạo một ví cho cặp khóa thanh toán:
Mã
solana-keygen new -o ./wallet.json
  1. Bây giờ, hãy đặt ví này làm ví trên CLI để nhận Solana qua airdrop:
Mã
solana config set --keypair ./wallet.json
  1. Giờ đây, bạn có thể airdrop Solana vào địa chỉ này bằng cách chạy lệnh sau:
Mã
solana aidrop 2
  1. Chúng ta cũng cần tạo và nạp tiền vào một ví khác cho thẩm quyền nonce:
Mã
solana-keygen new -o ./nonceAuth.json
  1. Với khóa công khai vừa tạo, chúng ta có thể dùng trang faucet tại đây để airdrop 1 SOL vào khóa đó.

Môi trường đã được thiết lập. Giờ chúng ta có thể chuyển sang các bước tiếp theo.

Nonce bền vững là gì?

Một tài khoản nonce bền vững trên Solana có thể được xem như két an toàn. Khi bạn khởi tạo tài khoản này, Solana sẽ gán cho nó một mã ổn định và duy nhất gọi là "nonce bền vững". Không giống các nonce thông thường thay đổi theo từng giao dịch, nonce này giữ nguyên và đóng vai trò là tham chiếu nhất quán.

Điều này đặc biệt hữu ích cho các giao dịch "ngoại tuyến". Khi tạo giao dịch, bạn tham chiếu nonce này từ tài khoản của mình. Solana đối chiếu nonce đó với giá trị đã lưu và nếu khớp, giao dịch sẽ được chấp thuận. Do đó, tài khoản nonce bền vững vừa là cơ chế lưu trữ vừa là cơ chế xác thực, đảm bảo tính xác thực của giao dịch đồng thời đáp ứng tốc độ cao và các tình huống ngoại tuyến trên mạng Solana.

Nonce bền vững có thể được sử dụng trong nhiều trường hợp, chẳng hạn như:

  • Giao dịch theo lịch: Bạn có thể thiết lập giao dịch để diễn ra vào một thời điểm xác định trong tương lai. Nonce bền vững đảm bảo các giao dịch theo lịch này được thực thi an toàn.
  • Ví đa chữ ký: Trong ví đa chữ ký, nonce bền vững cung cấp thêm một lớp bảo mật và khả năng phối hợp giữa nhiều bên ký.
  • Chương trình cần tương tác trong tương lai: Một số chương trình trên Solana cần tương tác với các chương trình hoặc dịch vụ khác theo những khoảng thời gian cụ thể. Nonce bền vững giúp duy trì tính toàn vẹn của các tương tác này.
  • Tương tác với các blockchain khác: Khi Solana tương tác với các blockchain khác, nonce bền vững góp phần đảm bảo tính hợp lệ của các giao dịch xuyên chuỗi.

Bây giờ, chúng ta có thể bắt đầu xây dựng ví dụ.

Giao dịch Solana: Các bước xây dựng

Bước 1: Thiết lập các phần phụ thuộc và hằng số

Trong bước này, bạn sẽ nhập các mô-đun và tiện ích cần thiết, đồng thời xác định các hằng số và cặp khóa cho ví dụ. Những phần phụ thuộc và hằng số này sẽ được sử dụng xuyên suốt quá trình giao dịch.

Mã
import {
  Connection,
  Keypair,
  LAMPORTS_PER_SOL,
  NonceAccount,
  NONCE_ACCOUNT_LENGTH,
  SystemProgram,
  Transaction,
} from "@solana/web3.js";
import { encodeAndWriteTransaction, loadWallet, readAndDecodeTransaction } from "./utils";

const nonceAuthKeypair = loadWallet('./wallets/nonceAuth.json');
const nonceKeypair = Keypair.generate();
const senderKeypair = loadWallet('./wallets/wallet.json');
const connection = new Connection("https://devnet.helius-rpc.com/?api-key=");
const waitTime = 120000;
const TranferAmount = LAMPORTS_PER_SOL * 0.01;

Bước 2: Tạo hàm sendTransaction

Hàm sendTransaction điều phối quá trình gửi giao dịch bằng nonce bền vững. Hàm này xử lý việc tạo nonce, xác nhận và thực thi giao dịch.

Mã
async function sendTransaction() {
	console.log("Starting Nonce Transaction")
  try {
    const nonceCreationTxSig = await nonce();
    const confirmationStatus = await connection.confirmTransaction(nonceCreationTxSig);
    if (!confirmationStatus.value.err) {
      console.log("Nonce account creation confirmed.");

      const nonce = await getNonce();
      await createTx(nonce);
      await signOffline(waitTime);
      await executeTx();
    } else {
      console.error("Nonce account creation transaction failed:", confirmationStatus.value.err);
    }
  } catch (error) {
    console.error(error);
  }
}

Bước 3: Tạo hàm nonce

Hàm nonce chịu trách nhiệm tạo và khởi tạo tài khoản nonce bền vững. Quá trình này bao gồm tính toán tiền thuê cần thiết cho tài khoản, lấy blockhash mới nhất và tạo các giao dịch để vừa tạo vừa khởi tạo tài khoản nonce.

  1. Trước khi tạo tài khoản nonce, chúng ta cần tính toán tiền thuê cần thiết để lưu trữ dữ liệu tài khoản và lấy blockhash mới nhất.
Mã
async function nonce() {
  const rent = await connection.getMinimumBalanceForRentExemption(NONCE_ACCOUNT_LENGTH);
  const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
  1. Bây giờ, chúng ta sẽ tạo một giao dịch để tạo tài khoản nonce. Bước này sử dụng chỉ thị SystemProgram.createAccount để phân bổ không gian cho tài khoản nonce.
Mã
const createNonceTx = new Transaction().add(
    SystemProgram.createAccount({
      fromPubkey: nonceAuthKeypair.publicKey,
      newAccountPubkey: nonceKeypair.publicKey,
      lamports: rent,
      space: NONCE_ACCOUNT_LENGTH,
      programId: SystemProgram.programId,
    })
  );
  1. Chúng ta sẽ ký giao dịch bằng các cặp khóa có thẩm quyền rồi gửi giao dịch đến mạng Solana. Giao dịch này sẽ tạo tài khoản nonce bền vững.
Mã
createNonceTx.feePayer = nonceAuthKeypair.publicKey;
  createNonceTx.recentBlockhash = blockhash;
  createNonceTx.lastValidBlockHeight = lastValidBlockHeight;
  createNonceTx.sign(nonceAuthKeypair, nonceKeypair);

  try {
    const signature = await connection.sendRawTransaction(createNonceTx.serialize(), { skipPreflight: false, preflightCommitment: "single" });
  1. Sau khi gửi giao dịch, chúng ta sẽ xác nhận trạng thái để đảm bảo tài khoản nonce đã được tạo thành công.
Mã
const confirmationStatus = await connection.confirmTransaction(signature);
    if (confirmationStatus.value.err) {
      throw new Error("Nonce account creation transaction failed: " + confirmationStatus.value.err);
    }
    console.log("Nonce account created:", signature);
  1. Để sử dụng đầy đủ tài khoản nonce, chúng ta cần khởi tạo giá trị của tài khoản. Chúng ta sẽ tạo một giao dịch mới để thực thi chỉ thị SystemProgram.nonceInitialize.
Mã
// Initialize the nonce value within the account
    const initializeNonceTx = new Transaction().add(
      SystemProgram.nonceInitialize({
        noncePubkey: nonceKeypair.publicKey,
        authorizedPubkey: nonceAuthKeypair.publicKey,
      })
    );
  1. Tương tự bước trước, chúng ta sẽ ký giao dịch và gửi đến mạng để khởi tạo tài khoản nonce.
Mã
const { blockhash: initBlockhash, lastValidBlockHeight: initLastValidBlockHeight } = await connection.getLatestBlockhash();
    initializeNonceTx.feePayer = nonceAuthKeypair.publicKey;
    initializeNonceTx.recentBlockhash = initBlockhash;
    initializeNonceTx.lastValidBlockHeight = initLastValidBlockHeight;
    initializeNonceTx.sign(nonceAuthKeypair);

    const initSignature = await connection.sendRawTransaction(initializeNonceTx.serialize(), { skipPreflight: false, preflightCommitment: "single" });
  1. Cuối cùng, chúng ta sẽ xác nhận trạng thái của giao dịch khởi tạo để đảm bảo tài khoản nonce đã được khởi tạo đúng cách.
Mã
const initConfirmationStatus = await connection.confirmTransaction(initSignature);
    if (initConfirmationStatus.value.err) {
      throw new Error("Nonce initialization transaction failed: " + initConfirmationStatus.value.err);
    }
    console.log("Nonce initialized:", initSignature);
    return initSignature;
  } catch (error) {
    console.error("Failed in createNonce function: ", error);
    throw error;
  }
}

Toàn bộ hàm sẽ có dạng tương tự như sau:

Mã
async function nonce() {
  // For creating the nonce account
  const rent = await connection.getMinimumBalanceForRentExemption(
    NONCE_ACCOUNT_LENGTH
  );
  const { blockhash, lastValidBlockHeight } =
    await connection.getLatestBlockhash();
  const createNonceTx = new Transaction().add(
    SystemProgram.createAccount({
      fromPubkey: nonceAuthKeypair.publicKey,
      newAccountPubkey: nonceKeypair.publicKey,
      lamports: rent,
      space: NONCE_ACCOUNT_LENGTH,
      programId: SystemProgram.programId,
    })
  );

  createNonceTx.feePayer = nonceAuthKeypair.publicKey;
  createNonceTx.recentBlockhash = blockhash;
  createNonceTx.lastValidBlockHeight = lastValidBlockHeight;
  createNonceTx.sign(nonceAuthKeypair, nonceKeypair);

  try {
    // Create the nonce account
    const signature = await connection.sendRawTransaction(
      createNonceTx.serialize(),
      { skipPreflight: false, preflightCommitment: "single" }
    );
    const confirmationStatus = await connection.confirmTransaction(signature);
    if (confirmationStatus.value.err) {
      throw new Error(
        "Nonce account creation transaction failed: " +
          confirmationStatus.value.err
      );
    }
    console.log("Nonce account created:", signature);

    // Now, initialize the nonce
    const initializeNonceTx = new Transaction().add(
      SystemProgram.nonceInitialize({
        noncePubkey: nonceKeypair.publicKey,
        authorizedPubkey: nonceAuthKeypair.publicKey,
      })
    );

    const {
      blockhash: initBlockhash,
      lastValidBlockHeight: initLastValidBlockHeight,
    } = await connection.getLatestBlockhash();
    initializeNonceTx.feePayer = nonceAuthKeypair.publicKey;
    initializeNonceTx.recentBlockhash = initBlockhash;
    initializeNonceTx.lastValidBlockHeight = initLastValidBlockHeight;
    initializeNonceTx.sign(nonceAuthKeypair); // Only sign with nonceAuthKeypair

    const initSignature = await connection.sendRawTransaction(
      initializeNonceTx.serialize(),
      { skipPreflight: false, preflightCommitment: "single" }
    );
    const initConfirmationStatus = await connection.confirmTransaction(
      initSignature
    );
    if (initConfirmationStatus.value.err) {
      throw new Error(
        "Nonce initialization transaction failed: " +
          initConfirmationStatus.value.err
      );
    }
    console.log("Nonce initialized:", initSignature);
    return initSignature;
  } catch (error) {
    console.error("Failed in createNonce function: ", error);
    throw error;
  }
}

Bước 4: Tạo hàm getNonce

Định nghĩa hàm getNonce, chịu trách nhiệm lấy giá trị nonce từ tài khoản nonce đã tạo.

Mã
async function getNonce() {
  const nonceAccount = await fetchNonceInfo();
  return nonceAccount.nonce;
}

Bước 5: Tạo hàm createTx

Định nghĩa hàm createTx để tạo một giao dịch mẫu chứa cả chỉ thị tăng nonce và chỉ thị chuyển tiền. Hàm sử dụng nonce đã lấy trước đó để đảm bảo tính xác thực của giao dịch.

Mã
async function createTx(nonce) {
  const destination = Keypair.generate();

  const advanceNonceIx = SystemProgram.nonceAdvance({
    noncePubkey: nonceKeypair.publicKey,
    authorizedPubkey: nonceAuthKeypair.publicKey
  });

  const transferIx = SystemProgram.transfer({
    fromPubkey: senderKeypair.publicKey,
    toPubkey: destination.publicKey,
    lamports: TranferAmount,
  });

  const sampleTx = new Transaction();
  sampleTx.add(advanceNonceIx, transferIx);
  sampleTx.recentBlockhash = nonce; // Use the nonce fetched earlier
  sampleTx.feePayer = senderKeypair.publicKey;

  const serialisedTx = encodeAndWriteTransaction(sampleTx, "./unsignedTxn.json", false);
  return serialisedTx;
}

Bước 6: Tạo hàm signOffline

Định nghĩa hàm signOffline, chịu trách nhiệm ký giao dịch ngoại tuyến. Hàm mô phỏng độ trễ ngoại tuyến trước khi ký giao dịch bằng cả cặp khóa của người gửi và cặp khóa có thẩm quyền nonce.

Mã
async function createTx(nonce) {
  const destination = Keypair.generate();

  const advanceNonceIx = SystemProgram.nonceAdvance({
    noncePubkey: nonceKeypair.publicKey,
    authorizedPubkey: nonceAuthKeypair.publicKey
  });

  const transferIx = SystemProgram.transfer({
    fromPubkey: senderKeypair.publicKey,
    toPubkey: destination.publicKey,
    lamports: TranferAmount,
  });

  const sampleTx = new Transaction();
  sampleTx.add(advanceNonceIx, transferIx);
  sampleTx.recentBlockhash = nonce; // Use the nonce fetched earlier
  sampleTx.feePayer = senderKeypair.publicKey;

  const serialisedTx = encodeAndWriteTransaction(sampleTx, "./unsignedTxn.json", false);
  return serialisedTx;
}

Bước 7: Tạo hàm executeTx

Hàm executeTx chịu trách nhiệm gửi giao dịch đã ký đến mạng Solana để thực thi. Đây là bước cuối cùng của quy trình giao dịch, khi giao dịch được phát lên mạng.

Mã
async function executeTx() {
  const signedTx = await readAndDecodeTransaction("./signedTxn.json");
  const sig = await connection.sendRawTransaction(signedTx.serialize());
  console.log("Tx sent: ", sig);
}

Bước 8: Tạo hàm fetchNonceInfo

Hàm fetchNonceInfo lấy thông tin nonce từ tài khoản nonce đã tạo và thử lại tối đa ba lần nếu cần. Điều này giúp đảm bảo nonce được sử dụng trong giao dịch là mới nhất và hợp lệ.

Mã
async function fetchNonceInfo(retries = 3) {
  while (retries > 0) {
    const accountInfo = await connection.getAccountInfo(nonceKeypair.publicKey);
    if (accountInfo) {
      const nonceAccount = NonceAccount.fromAccountData(accountInfo.data);
      return nonceAccount;
    }
    retries--;
    if (retries > 0) {
      console.log(`Retry fetching nonce in 3 seconds. ${retries} retries left.`);
      await new Promise(res => setTimeout(res, 3000)); // wait for 3 seconds
    }
  }
  throw new Error("No account info found");
}

Bước 9: Gọi hàm sendTransaction

Cuối cùng, hãy gọi hàm sendTransaction để bắt đầu quy trình giao dịch. Hàm này kết hợp tất cả các bước đã xác định trước đó để tạo, ký và thực thi giao dịch bằng nonce bền vững.

Mã
ts-node main

Việc chạy sendTransaction sẽ tạo ra chữ ký giao dịch cho một giao dịch thành công. Chữ ký này là thông tin quan trọng để theo dõi và xác minh giao dịch trên mạng Solana.

Mã
Tx written to ./unsignedTxn.jsonTx written to ./signedTxn.jsonTx sent:  64vBuSbN8SJZo74r8KoRFF6GJD7iszdckER2NkmFfYzHCN1H9Q3iC2Z3CP7NsoAgrP2jdyQrVeSzVx6vsbxNEE5U

Bạn vừa sử dụng thành công nonce bền vững trong một giao dịch!

Mã nguồn đầy đủ

Mã
import {
  Connection,
  Keypair,
  LAMPORTS_PER_SOL,
  NonceAccount,
  NONCE_ACCOUNT_LENGTH,
  SystemProgram,
  Transaction,
} from "@solana/web3.js";
import {
  encodeAndWriteTransaction,
  loadWallet,
  readAndDecodeTransaction,
} from "./utils";

const TranferAmount = LAMPORTS_PER_SOL * 0.01;

const nonceAuthKeypair = loadWallet("./wallets/nonceAuth.json");
const nonceKeypair = Keypair.generate();
const senderKeypair = loadWallet("./wallets/wallet.json");
const connection = new Connection(
  "https://devnet.helius-rpc.com/?api-key="
);

const waitTime = 120000;

async function sendTransaction() {
  try {
    // Create nonce and get its creation transaction signature
    const nonceCreationTxSig = await nonce();

    // Ensure nonce account creation is confirmed before moving forward
    const confirmationStatus = await connection.confirmTransaction(
      nonceCreationTxSig
    );
    if (!confirmationStatus.value.err) {
      console.log("Nonce account creation confirmed.");

      const nonce = await getNonce();
      await createTx(nonce);
      await signOffline(waitTime);
      await executeTx();
    } else {
      console.error(
        "Nonce account creation transaction failed:",
        confirmationStatus.value.err
      );
    }
  } catch (error) {
    console.error(error);
  }
}

async function nonce() {
  // For creating the nonce account
  const rent = await connection.getMinimumBalanceForRentExemption(
    NONCE_ACCOUNT_LENGTH
  );
  const { blockhash, lastValidBlockHeight } =
    await connection.getLatestBlockhash();
  const createNonceTx = new Transaction().add(
    SystemProgram.createAccount({
      fromPubkey: nonceAuthKeypair.publicKey,
      newAccountPubkey: nonceKeypair.publicKey,
      lamports: rent,
      space: NONCE_ACCOUNT_LENGTH,
      programId: SystemProgram.programId,
    })
  );

  createNonceTx.feePayer = nonceAuthKeypair.publicKey;
  createNonceTx.recentBlockhash = blockhash;
  createNonceTx.lastValidBlockHeight = lastValidBlockHeight;
  createNonceTx.sign(nonceAuthKeypair, nonceKeypair);

  try {
    // Create the nonce account
    const signature = await connection.sendRawTransaction(
      createNonceTx.serialize(),
      { skipPreflight: false, preflightCommitment: "single" }
    );
    const confirmationStatus = await connection.confirmTransaction(signature);
    if (confirmationStatus.value.err) {
      throw new Error(
        "Nonce account creation transaction failed: " +
          confirmationStatus.value.err
      );
    }
    console.log("Nonce account created:", signature);

    // Now, initialize the nonce
    const initializeNonceTx = new Transaction().add(
      SystemProgram.nonceInitialize({
        noncePubkey: nonceKeypair.publicKey,
        authorizedPubkey: nonceAuthKeypair.publicKey,
      })
    );

    const {
      blockhash: initBlockhash,
      lastValidBlockHeight: initLastValidBlockHeight,
    } = await connection.getLatestBlockhash();
    initializeNonceTx.feePayer = nonceAuthKeypair.publicKey;
    initializeNonceTx.recentBlockhash = initBlockhash;
    initializeNonceTx.lastValidBlockHeight = initLastValidBlockHeight;
    initializeNonceTx.sign(nonceAuthKeypair); // Only sign with nonceAuthKeypair

    const initSignature = await connection.sendRawTransaction(
      initializeNonceTx.serialize(),
      { skipPreflight: false, preflightCommitment: "single" }
    );
    const initConfirmationStatus = await connection.confirmTransaction(
      initSignature
    );
    if (initConfirmationStatus.value.err) {
      throw new Error(
        "Nonce initialization transaction failed: " +
          initConfirmationStatus.value.err
      );
    }
    console.log("Nonce initialized:", initSignature);
    return initSignature;
  } catch (error) {
    console.error("Failed in createNonce function: ", error);
    throw error;
  }
}

async function getNonce() {
  const nonceAccount = await fetchNonceInfo();
  return nonceAccount.nonce;
}

async function createTx(nonce: string) {
  const destination = Keypair.generate();

  const advanceNonceIx = SystemProgram.nonceAdvance({
    noncePubkey: nonceKeypair.publicKey,
    authorizedPubkey: nonceAuthKeypair.publicKey,
  });

  const transferIx = SystemProgram.transfer({
    fromPubkey: senderKeypair.publicKey,
    toPubkey: destination.publicKey,
    lamports: TranferAmount,
  });

  const sampleTx = new Transaction();
  sampleTx.add(advanceNonceIx, transferIx);
  sampleTx.recentBlockhash = nonce; // Use the nonce fetched earlier
  sampleTx.feePayer = senderKeypair.publicKey;

  const serialisedTx = encodeAndWriteTransaction(
    sampleTx,
    "./unsigned.json",
    false
  );
  return serialisedTx;
}
async function signOffline(waitTime = 120000): Promise {
  await new Promise((resolve) => setTimeout(resolve, waitTime));
  const unsignedTx = readAndDecodeTransaction("./unsigned.json");
  unsignedTx.sign(senderKeypair, nonceAuthKeypair); // Sign with both keys
  const serialisedTx = encodeAndWriteTransaction(unsignedTx, "./signed.json");
  return serialisedTx;
}

async function executeTx() {
  const signedTx = readAndDecodeTransaction("./signed.json");
  const sig = await connection.sendRawTransaction(signedTx.serialize());
  console.log("      Tx sent: ", sig);
}

async function fetchNonceInfo(retries = 3): Promise {
  while (retries > 0) {
    const accountInfo = await connection.getAccountInfo(nonceKeypair.publicKey);
    if (accountInfo) {
      const nonceAccount = NonceAccount.fromAccountData(accountInfo.data);
      return nonceAccount;
    }
    retries--;
    if (retries > 0) {
      console.log(
        `Retry fetching nonce in 3 seconds. ${retries} retries left.`
      );
      await new Promise((res) => setTimeout(res, 3000)); // wait for 3 seconds
    }
  }
  throw new Error("No account info found");
}

sendTransaction();

Giao dịch Solana: Sử dụng Helius RPC

Helius có thể đóng vai trò là lớp trung gian mạnh mẽ để tương tác với các RPC của Solana, giúp đơn giản hóa quá trình lấy thông tin blockhash cần thiết cho nonce bền vững. Thông qua Helius, bạn có thể quản lý vòng đời của các giao dịch Solana đáng tin cậy hơn, đặc biệt trong các tình huống ngoại tuyến. Helius cung cấp quyền truy cập hợp lý hóa vào các blockhash, giúp nhà phát triển tăng khả năng chống chịu của ứng dụng trước việc giao dịch hết hạn.

Tóm lại, nonce bền vững trong giao dịch Solana cung cấp một phương thức an toàn và đáng tin cậy để xử lý các giao dịch ngoại tuyến và đảm bảo tính xác thực của giao dịch. Bằng cách làm theo các bước trong hướng dẫn này, nhà phát triển có thể triển khai nonce bền vững trong ứng dụng Solana, qua đó nâng cao tính bảo mật và linh hoạt.

‍

‍

‍

Đăng ký nhận tin từ Helius

Luôn cập nhật những thông tin mới nhất về phát triển Solana và nhận thông báo khi chúng tôi đăng bài