Web3新玩法:SolanaNonceAccount让你交易无忧想在Web3世界玩出新花样?Solana的交易速度快到飞起,但你有没有遇到过这样的烦恼:交易刚签名,转眼就因区块哈希过期失效?别急,SolanaNonceAccount来了!它就像一个“交易时间胶囊”,让你随时签名
想在 Web3 世界玩出新花样?Solana 的交易速度快到飞起,但你有没有遇到过这样的烦恼:交易刚签名,转眼就因区块哈希过期失效?别急,Solana Nonce Account 来了!它就像一个“交易时间胶囊”,让你随时签名、随时提交,再也不用担心时间限制。本文将带你揭开这个 Web3 新玩法的面纱,手把手教你如何用它让交易无忧,轻松迈入 Solana 开发前沿!
在 Solana 区块链中,Nonce Account 是一个隐藏的“交易神器”。Nonce Account(nonce 账户) 是一种特殊的账户类型,用于支持 Durable Nonce(持久化 nonce) 机制。它用一个持久化的 nonce 值替代短命的区块哈希,让你的交易签名可以“长寿”无忧,随时提交不失效。本文不仅深入讲解了 Nonce Account 的原理和特性,还通过代码实战演示了如何创建、签名和发送持久化交易。对比传统方式,你会发现它的妙处:离线签名、定时转账,统统不在话下。无论你是 Web3 新手还是 Solana 开发者,这篇指南都能让你快速上手,玩转交易新姿势!
Nonce Account 是 Solana 系统程序(System Program)拥有的一种账户,用于存储一个 nonce 值(一个唯一的标识符,通常是一个 32 字节的哈希值)。这个 nonce 值可以替代常规交易中的 recent blockhash(最近区块哈希),从而实现 持久化交易(Durable Transactions),也就是允许交易在签名后延迟提交而不失效。
存储内容:
Nonce 值: 一个唯一的哈希值,替代交易中的 recent blockhash。
Authority(授权者): 一个公钥,负责管理该 Nonce Account(例如推进 nonce 或提取资金)。
状态: 表示账户是否已初始化。
租免(Rent-Exempt):
prepareAccount
代码export function prepareAccount(params: any) {
const {
authorAddress,
nonceAccountAddress,
recentBlockhash,
minBalanceForRentExemption,
privs,
} = params;
const authorPrivateKey = privs?.find(
(ele: { address: any }) => ele.address === authorAddress
)?.key;
if (!authorPrivateKey) throw new Error("authorPrivateKey 为空");
const nonceAcctPrivateKey = privs?.find(
(ele: { address: any }) => ele.address === nonceAccountAddress
)?.key;
if (!nonceAcctPrivateKey) throw new Error("nonceAcctPrivateKey 为空");
const author = Keypair.fromSecretKey(
new Uint8Array(Buffer.from(authorPrivateKey, "hex"))
);
const nonceAccount = Keypair.fromSecretKey(
new Uint8Array(Buffer.from(nonceAcctPrivateKey, "hex"))
);
const tx = new Transaction();
tx.add(
SystemProgram.createAccount({
fromPubkey: author.publicKey,
newAccountPubkey: nonceAccount.publicKey,
lamports: minBalanceForRentExemption,
space: NONCE_ACCOUNT_LENGTH,
programId: SystemProgram.programId,
}),
SystemProgram.nonceInitialize({
noncePubkey: nonceAccount.publicKey,
authorizedPubkey: author.publicKey,
})
);
tx.recentBlockhash = recentBlockhash;
tx.sign(author, nonceAccount);
return tx.serialize().toString("base64");
}
{
"jsonrpc": "2.0",
"id": 1,
"method": "getLatestBlockhash",
"params": [
{
"commitment": "processed"
}
]
}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"context": {
"apiVersion": "2.1.11",
"slot": 361716547
},
"value": {
"blockhash": "7kcpQ8Hfpi35gTKtmkVW5XBdYH5Zrw4e9Pg27YyCxkcP",
"lastValidBlockHeight": 349703424
}
}
}
test('prepareAccount test', async () => {
const params = {
authorAddress: from,
nonceAccountAddress: nonce_account_addr,
recentBlockhash: "7kcpQ8Hfpi35gTKtmkVW5XBdYH5Zrw4e9Pg27YyCxkcP",
minBalanceForRentExemption: 1647680,
privs: [
{
address: from,
key: fromPrivateKey
},
{
address: nonce_account_addr,
key: noncePrivateKey
}
]
}
let tx_msg = await prepareAccount(params)
console.log("prepareAccount-tx_msg===", tx_msg)
});
console.log
prepareAccount-tx_msg=== AovVQR0MBuqhv9g/Z13s7ZxLGWUJJT3Oo93lbpmuv9f/liETgjOJVoJyRGRnwp3lo+rb45SCPzcoubMn4we/mwNNRDoya6WDYE9FkcZOGZAgLHSJ8+e0+H5D8pDxHAEnHgDCqWPguVrIyTulfaDxplezPdql6xQHFT9ggyzc9EgPAgADBVDS9aJBo99CnaEp9zsUQTrBn951FTSzXyb0W7DUWub2xEp9ziCqAhF18Ho7eXUKwhKbLjja+jESCZydfVfC/UsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAan1RcZLFaO4IqEX3PSl4jPA1wxRbIas0TYBi6pQAAABqfVFxksXFEhjMlMPUrxf1ja7gibof1E49vZigAAAABkUphSZcxpdk94aUysDsPylqN3s1PWrP/RYWsLIxSGGAICAgABNAAAAABAJBkAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAwEDBCQGAAAAUNL1okGj30KdoSn3OxRBOsGf3nUVNLNfJvRbsNRa5vY=
{
"method": "sendTransaction",
"params": [
"AovVQR0MBuqhv9g/Z13s7ZxLGWUJJT3Oo93lbpmuv9f/liETgjOJVoJyRGRnwp3lo+rb45SCPzcoubMn4we/mwNNRDoya6WDYE9FkcZOGZAgLHSJ8+e0+H5D8pDxHAEnHgDCqWPguVrIyTulfaDxplezPdql6xQHFT9ggyzc9EgPAgADBVDS9aJBo99CnaEp9zsUQTrBn951FTSzXyb0W7DUWub2xEp9ziCqAhF18Ho7eXUKwhKbLjja+jESCZydfVfC/UsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAan1RcZLFaO4IqEX3PSl4jPA1wxRbIas0TYBi6pQAAABqfVFxksXFEhjMlMPUrxf1ja7gibof1E49vZigAAAABkUphSZcxpdk94aUysDsPylqN3s1PWrP/RYWsLIxSGGAICAgABNAAAAABAJBkAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAwEDBCQGAAAAUNL1okGj30KdoSn3OxRBOsGf3nUVNLNfJvRbsNRa5vY=",
{
"encoding": "base64"
}
],
"id": 1,
"jsonrpc": "2.0"
}
{
"jsonrpc": "2.0",
"result": "3o9maYQjBDV3vSignLouQwpzZTpKckTPo72cyW5PmtRRHs2JP8osVq9BscGeX3LdE2WjY6ydU52v2DqCnXH3SzNv",
"id": 1
}
Solana Nonce Account 是一个存储持久化 nonce 的账户,用于支持延迟提交的交易。它通过替代 recent blockhash,解决了常规交易的短寿命问题(约 1-2 分钟),非常适合需要离线签名或定时提交的场景。简单来说,它就像一个“私人区块哈希队列”,让你的交易签名可以长期有效。
test('get nonce test', async () => {
var connection = new Connection(sol_dev_rpc_url, "confirmed");
const nonce = await connection.getNonce(new PublicKey("EDEnnPuvJMsVij4QWza2mrujsqyytAAK3qcRAvFuY84A"));
console.log("nonce===", nonce)
});
console.log
nonce=== NonceAccount {
authorizedPubkey: PublicKey [PublicKey(6SWBzQWZndeaCKg3AzbY3zkvapCu9bHFZv12iiRoGvCD)] {
_bn: <BN: 50d2f5a241a3df429da129f73b14413ac19fde751534b35f26f45bb0d45ae6f6>
},
nonce: '3rK6Qgj2hVhp1pnzxTNea7D3hUjLwgqr6r3Q12KmCks8',
feeCalculator: { lamportsPerSignature: 5000 }
}
test('signTransactionNonceAdvance', async () => {
const authPrivateKey = fromPrivateKey
let nonceAccountAddress = nonce_account_addr
let authorAddress = from
let mintAddress = "0x00";
const params = {
txObj: {
from: from,
amount: "0.001",
to: to,
nonce: "3rK6Qgj2hVhp1pnzxTNea7D3hUjLwgqr6r3Q12KmCks8",
nonceAccountAddress: nonceAccountAddress,
authorAddress: authorAddress,
decimal: 9,
mintAddress: mintAddress,
// txType: "TRANSFER_TOKEN",
hasCreatedTokenAddr: true
},
privs: [
{ address: from, key: fromPrivateKey },
{ address: authorAddress, key: authPrivateKey }
]
}
let tx_msg = await signTransactionNonceAdvance(params)
console.log("signTransactionNonceAdvance-tx_msg===", tx_msg)
});
测试输出
console.log
signTransactionNonceAdvance-tx_msg=== AQ94ysKq0/FKQZxAlGDnFjd2rwTdqD10/e681Zd6qhGm+kp0i+0tEd0KJEf+0jVjBi7cDHA/Xb5swhcj6K1KQgwBAAIFUNL1okGj30KdoSn3OxRBOsGf3nUVNLNfJvRbsNRa5vbESn3OIKoCEXXwejt5dQrCEpsuONr6MRIJnJ19V8L9S+8gLasmP57V9/83zS23swyxzRmuamhmcL6vfgXvnLqfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGp9UXGSxWjuCKhF9z0peIzwNcMUWyGrNE2AYuqUAAACpZSqYk5dPPmahjsCk9PKmCzJirdegCTuuVZIL77hFfAgMDAQQABAQAAAADAgACDAIAAABAQg8AAAAAAA==
Request:
{
"method": "sendTransaction",
"params": [
"AQ94ysKq0/FKQZxAlGDnFjd2rwTdqD10/e681Zd6qhGm+kp0i+0tEd0KJEf+0jVjBi7cDHA/Xb5swhcj6K1KQgwBAAIFUNL1okGj30KdoSn3OxRBOsGf3nUVNLNfJvRbsNRa5vbESn3OIKoCEXXwejt5dQrCEpsuONr6MRIJnJ19V8L9S+8gLasmP57V9/83zS23swyxzRmuamhmcL6vfgXvnLqfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGp9UXGSxWjuCKhF9z0peIzwNcMUWyGrNE2AYuqUAAACpZSqYk5dPPmahjsCk9PKmCzJirdegCTuuVZIL77hFfAgMDAQQABAQAAAADAgACDAIAAABAQg8AAAAAAA==",
{
"encoding": "base64"
}
],
"id": 1,
"jsonrpc": "2.0"
}
Response
{
"jsonrpc": "2.0",
"result": "JwbL7ZHkzGADLHiT7yNr5Tp17bKW3EwupaS3deP8jkLMvcrDnej8RSFboWH1FxQGoWEWjuFt7EZT4XeMh3LDSFD",
"id": 1
}
Request:
{
"jsonrpc": "2.0",
"id": 1,
"method": "getLatestBlockhash",
"params": [
{
"commitment": "processed"
}
]
}
Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"context": {
"apiVersion": "2.1.11",
"slot": 361853137
},
"value": {
"blockhash": "2Et5fCcmfKLBLj8JFKNNP5HrBtid1LGDcJ2TjvhKTJ85",
"lastValidBlockHeight": 349839702
}
}
}
test('signSolTransaction2', async () => {
const params = {
txObj: {
from: from,
amount: "0.001",
to: to,
nonce: "2Et5fCcmfKLBLj8JFKNNP5HrBtid1LGDcJ2TjvhKTJ85",
decimal: 9,
hasCreatedTokenAddr: true,
mintAddress: "0x00"
},
privateKey: fromPrivateKey
}
let tx_msg = await signSolTransaction(params)
console.log("signSolTransaction mintAddress-tx_msg===: ", tx_msg)
});
测试输出
console.log
signSolTransaction mintAddress-tx_msg===: AXGC+/AByHznnU8A7RRRZ05S0HCy1IyLrDl2eupbJ4ZxI/oRFZn16gK1Dd3IrsIP0xrQGnIrUzBp42YacoDsEQ0BAAEDUNL1okGj30KdoSn3OxRBOsGf3nUVNLNfJvRbsNRa5vbvIC2rJj+e1ff/N80tt7MMsc0ZrmpoZnC+r34F75y6nwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEmn+gHRn9+6fhkPtGiR7satZ98KcS8o9Qdgxhnh3yW4BAgIAAQwCAAAAQEIPAAAAAAA=
{
"method": "sendTransaction",
"params": [
"AXGC+/AByHznnU8A7RRRZ05S0HCy1IyLrDl2eupbJ4ZxI/oRFZn16gK1Dd3IrsIP0xrQGnIrUzBp42YacoDsEQ0BAAEDUNL1okGj30KdoSn3OxRBOsGf3nUVNLNfJvRbsNRa5vbvIC2rJj+e1ff/N80tt7MMsc0ZrmpoZnC+r34F75y6nwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEmn+gHRn9+6fhkPtGiR7satZ98KcS8o9Qdgxhnh3yW4BAgIAAQwCAAAAQEIPAAAAAAA=",
{
"encoding": "base64"
}
],
"id": 1,
"jsonrpc": "2.0"
}
{
"jsonrpc": "2.0",
"result": "3GdUC2Q5RzTW8NaooqR5vT9CbCJwC59AL6edFg8fzuSGVwXPWnHUxt6r4Mv6vL3qn5knQrxYSWHsSP9UtBpLh492",
"id": 1
}
# 转账之前
solana balance
69.56672102 SOL
solana balance H6Su7YsGK5mMASrZvJ51nt7oBzD88V8FKSBPNnRG1u3k
26.55100002 SOL
# 转账之后
solana balance
69.56571602 SOL
solana balance H6Su7YsGK5mMASrZvJ51nt7oBzD88V8FKSBPNnRG1u3k
26.55200002 SOL
Solana Nonce Account 就像 Web3 交易的“万能钥匙”,解决了传统交易只能“短命”的痛点,让你签名无忧、提交随心。从离线操作到定时任务,它为 Solana 生态带来了无限可能。跟着本文的步骤,你不仅能理解它的奥秘,还能动手实践,轻松打造属于自己的 Web3 应用。在区块链的世界里,Nonce Account 是一个简单却强大的新玩法,准备好了吗?快用它解锁 Solana 的交易自由吧!
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!