最近(2024 年第一季度)对 Solana 区块空间的需求导致网络流量增加,随后交易量下降。主要原因是对 Solana 的优先费用系统和交易时间表进行了一些压力测试(预计 2024 年 4 月的计划升级将解决这些问题)。尽管面临这些挑战,但网络仍在继续生成区块和处理交易。不过,您可以在自己的应用程序中使用一些工具来提高性能并增加您的交易被纳入区块的可能性。在本指南中,我们将讨论在您的交易组装中需要考虑的两种方法,这将提高交易在链上结算的可能性:

实施优先收费
优化计算单元
优先费用
Solana 的费用优先级系统允许您在交易基本费用的基础上设置额外费用,这将使您的交易在领导者队列中具有更高的优先级。通过竞标更高的优先级,您的交易将更有可能被网络快速确认。更高的优先级费用并不能保证您的交易被纳入区块,但它确实使交易在同一个线程中处理的其他交易中具有优先级。如今大多数交易都使用优先级费用,因此忽略它们可能会导致您的交易被丢弃。
我们有一份指南,如何在 Solana 上使用优先费用。现在,我们将重点介绍如何根据您的业务需求确定适当的优先费用水平。
QuickNode 有一个优先费用 API,它将获取整个网络或特定程序帐户在过去(最多)100 个区块中支付的最近优先费用。该方法qn_estimatePriorityFees
以 5% 百分位数和方便范围(低、中、高、极端和推荐)返回优先费用。以下是如何在 TypeScript 应用程序中获取最新费用的示例:
import { Transaction, ComputeBudgetProgram } from "@solana/web3.js";
import { RequestPayload, ResponseData, EstimatePriorityFeesParams } from "./types";
async function fetchEstimatePriorityFees({
last_n_blocks,
account,
api_version,
endpoint
}: EstimatePriorityFeesParams): Promise<ResponseData> {
const params: any = {};
if (last_n_blocks !== undefined) {
params.last_n_blocks = last_n_blocks;
}
if (account !== undefined) {
params.account = account;
}
if (api_version !== undefined) {
params.api_version = api_version;
}
const payload: RequestPayload = {
method: 'qn_estimatePriorityFees',
params,
id: 1,
jsonrpc: '2.0',
};
const response = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data: ResponseData = await response.json();
return data;
}
示例响应:
{
"jsonrpc": "2.0",
"result": {
"context": {
"slot": 254387670
},
"per_compute_unit": {
"extreme": 1686993,
"high": 260140,
"low": 10714,
"medium": 50000,
"percentiles": {
"5": 100,
"10": 827,
"15": 2000,
// ...
"90": 510498,
"95": 1686993,
"100": 20000000000,
}
},
"per_transaction": {
"extreme": 4290114,
"high": 500000,
"low": 50000,
"medium": 149999,
"percentiles": {
"5": 700,
"10": 2000,
"15": 9868,
// ...
"90": 1250000,
"95": 4290114,
"100": 20000000000,
}
},
"recommended": 400251
},
"id": 1
}
现在,您可以选择适合您业务需求的优先费用级别。现在让我们看看计算单元优化,然后我们将讨论如何在您的交易组合中使用这两种方法。
计算单元优化
Solana 上的每笔交易都使用计算单元 (CU) 来处理。交易越复杂,它消耗的计算单元就越多。网络对单个区块中可处理的计算单元数量有限制。如果您的交易超出此限制,它将被丢弃。
自 2024 年 3 月起,实行以下限制/默认值:
每个区块的最大计算量:4800 万 CU(在有争议的时期,通常会达到此限制)
每个账户每个区块的最大计算量:1200 万 CU
每笔交易的最大计算量:140 万 CU
交易默认计算:200,000 CU
每笔交易的成本:每个签名 5000 个 lamport(可通过
getFeeForMessage
RPC 方法访问)每 CU 的增量成本:0(未来可能会发生变化)
由于没有对计算单元收费的历史,因此几乎没有动力或需要优化发送到网络的交易。这意味着许多应用程序使用默认的每笔交易 200,000 CU 或最大值(以避免交易错误)。这并不理想,特别是在网络流量大的时候,因为它可能导致交易丢失。
幸运的是,您可以在将交易发送到集群之前对其进行模拟,以确定其消耗的计算单元。这将允许您以尽可能少的计算单元将交易发送到集群,从而增加交易被纳入区块的可能性。
要计算交易的计算单位,请使用simulateTransaction
Solana Web3.js 库中的方法。以下是如何在 TypeScript 应用程序中使用此方法的示例:
async getSimulationUnits(
connection: Connection,
instructions: TransactionInstruction[],
payer: PublicKey
): Promise<number | undefined> {
const testInstructions = [
ComputeBudgetProgram.setComputeUnitLimit({ units: 1_400_000 }),
...instructions,
];
const testVersionedTxn = new VersionedTransaction(
new TransactionMessage({
instructions: testInstructions,
payerKey: payer,
recentBlockhash: PublicKey.default.toString(),
}).compileToV0Message()
);
const simulation = await connection.simulateTransaction(testVersionedTxn, {
replaceRecentBlockhash: true,
sigVerify: false,
});
if (simulation.value.err) {
return undefined;
}
return simulation.value.unitsConsumed;
}
关于我们的功能,有几点需要注意:
确保
setComputeUnitLimit
在测试指令中包含一条指令。这是因为我们的交易将需要此指令来更新计算单元限制。您可以使用最大值进行模拟,因为我们只对消耗的计算单元数量感兴趣。如果您使用优先费用,请确保在数组中包含优先费用指令
instructions
。我们将在下面的示例中执行此操作。
现在,我们有了一种方法,可以计算我们的交易将消耗的计算单元数量。让我们使用这个方法和我们的fetchEstimatePriorityFees
方法来创建一个交易组合,它将考虑最近的网络费用和我们的交易将消耗的计算单元数量。
优化交易组装
现在我们有了获取最近的优先费用和计算交易将消耗的计算单元数量的工具,我们可以使用这些工具来创建交易组件,这将增加我们的交易被包含在区块中的可能性。
以下是如何在 TypeScript 应用程序中使用这些工具的示例:
import { Connection, Keypair, Transaction, ComputeBudgetProgram } from "@solana/web3.js";
import { fetchEstimatePriorityFees, getSimulationUnits } from "./helpers"; // Update with your path to our methods
const endpoint = YOUR_QUICKNODE_ENDPOINT; // Replace with your QuickNode endpoint
const keyPair = Keypair.generate();// derive your keypair from your secret key
async function main(){
// 1. Establish a connection to the Solana cluster
const connection = new Connection(endpoint);
// 2. Create your transaction
const transaction = new Transaction();
// ... add instructions to the transaction
// 3. Fetch the recent priority fees
const { result } = await fetchEstimatePriorityFees({ endpoint });
const priorityFee = result.recommended; // Replace with your priority fee level based on your business requirements, e.g. result.per_compute_unit['high']
// 4. Create a PriorityFee instruction and add it to your transaction
const priorityFeeInstruction = ComputeBudgetProgram.setComputeUnitPrice({
microLamports: priorityFee,
});
transaction.add(priorityFeeInstruction);
// 5. Simulate the transaction and add the compute unit limit instruction to your transaction
let [units, recentBlockhash] =
await Promise.all([
getSimulationUnits(
connection,
transaction.instructions,
keyPair.publicKey
),
connection.getLatestBlockhash(),
]);
if (units) {
units = Math.ceil(units * 1.05); // margin of error
transaction.add(ComputeBudgetProgram.setComputeUnitLimit({ units }));
}
// 6. Sign and send your transaction
transaction.feePayer = keyPair.publicKey;
transaction.recentBlockhash = recentBlockhash.blockhash;
transaction.sign(keyPair);
const hash = await connection.sendRawTransaction(
transaction.serialize(),
{ skipPreflight: true, maxRetries: 0 }
);
return hash;
}
让我们分解一下示例中的步骤:
我们建立与 Solana 集群的连接。
我们创建交易。您可以在此处向交易添加指令。
我们使用我们的方法获取最近的优先费用
fetchEstimatePriorityFees
。我们创建优先费用指令并将其添加到我们的交易中。您可以根据您的业务需求自定义优先费用级别。
我们模拟交易并将计算单元限制指令添加到我们的交易中。我们使用此
getSimulationUnits
方法计算我们的交易将消耗的计算单元数量。我们还为我们的交易获取最近的区块哈希。我们签署并将交易发送到 Solana 集群。
这样,您现在就有了针对优先费用和计算单元进行优化的交易。这将增加您的交易被纳入区块的可能性!请随意修改函数以满足您的需求!
QuickNode SDK
如果您想稍微简化此流程,可以使用QuickNode SDK来创建和发送优化的交易!
要使用 SDK,您需要将其安装在您的项目中:
npm i @quicknode/sdk # or yarn add @quicknode/sdk
SDK 中有三种相关方法可用于创建和发送优化交易:
sendSmartTransaction
- 此方法将创建并发送具有优先费用和优化计算单元以及给定密钥对的交易。prepareSmartTransaction
- 此方法将准备具有优先费用和优化计算单元的交易。fetchEstimatePriorityFees
qn_estimatePriorityFees
- 此方法将使用附加方法获取最近的优先费用。
以下是如何使用 SDK 向 Solana 集群发送“智能”交易的示例:
import { solanaWeb3, Solana } from "@quicknode/sdk";
const { Transaction, SystemProgram, Keypair, PublicKey } = solanaWeb3;
const mainSecretKey = Uint8Array.from([
// Replace with your secret key
]);
const sender = Keypair.fromSecretKey(mainSecretKey);
const receiver = new PublicKey("YOUR_RECEIVER_ADDRESS");
const senderPublicKey = sender.publicKey;
const endpoint = new Solana({
endpointUrl:
"https://some-cool-name.solana-mainnet.quiknode.pro/redacted",
});
const transaction = new Transaction();
// Add instructions for each receiver
transaction.add(
SystemProgram.transfer({
fromPubkey: senderPublicKey,
toPubkey: receiver,
lamports: 10,
})
);
(async () => {
// Endpoint must added to Priority Fee API to do this
const signature = await endpoint.sendSmartTransaction({
transaction,
keyPair: sender,
feeLevel: "recommended"
});
console.log(signature);
})().catch(console.error);
在此示例中,我们使用该sendSmartTransaction
方法创建并发送具有优先费用和优化计算单元的交易。我们还使用SystemProgram.transfer
指令将 Lamport 从发送方转移到接收方。您可以feeLevel
根据您的业务需求自定义。就是这样!
总结
在 Solana 1.18 升级推出之前,交易落地可能会更具挑战性。但是,通过实施优先费用并优化交易的计算单元,您可以增加交易被纳入区块的可能性。