> ## Documentation Index
> Fetch the complete documentation index at: https://www.helius.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Solana 交易优化指南

> 优化 Solana 交易以最小化确认延迟并最大化交付率。了解优先费用、计算单元和最佳实践。

在 Solana 上发送交易有两种主要方法：

1. 使用[抵押连接](https://www.helius.dev/staked-connections) （默认）
2. 使用专业着陆服务如[Sender](https://www.helius.dev/sender) （推荐）

本文涵盖使用 staked connections 的交易优化最佳实践，这是所有 Helius 付费计划的默认方法。

Staked connections 最适合于延迟对您的业务不关键的用例（例如，支付、钱包、社交应用等）。

如果您是高级交易者（例如，propAMM、狙击手、跟单交易者、清算机器人、套利）并正在寻找专门的超低延迟交易着陆服务，请阅读我们的[发送者教程](/docs/zh/sending-transactions/sender)。

## 总结

Helius 的 staked connections 保证 100% 的交易交付，确认时间最短。为了优化使用 staked connections 的交易着陆率，我们建议以下最佳实践：

* 使用提交 "confirmed" 来获取[最新区块哈希](/docs/zh/rpc/guides/getlatestblockhash)
* 添加[优先费用](/docs/zh/sending-transactions/optimizing-transactions#设置正确的优先级费用)并动态计算
* 优化计算单元（CU）使用
* 设置 `maxRetries` 为0并实施强大的重试逻辑
* 将 `skipPreflight` 设置为 `true` (可选)

<Tip>
  想深入了解吗？我们在这篇 [博客文章](https://www.helius.dev/blog/how-to-land-transactions-on-solana) 中涵盖了所有基础知识。
</Tip>

## 为交易员推荐的优化

对于对延迟敏感的交易用例，我们建议 [使用 Sender](/docs/zh/sending-transactions/sender)。

但是，如果您使用 staked connections 并希望优化您的设置以获得尽可能低的延迟，我们建议以下优化（除了应用上述最佳实践）：

* 你的客户端服务器（用于发送交易的机器）应该位于美国东部或西欧。
* 如果想与Helius交易发送服务器共同定位，请选择FRA或PIT。
* 避免从离验证器网络较远的地区（如 LATAM，南非）发送。
* 预热Helius区域缓存以最小化尾部延迟。
* 每个区域只需一个预热线程——多余的没有任何好处。
* 每秒使用相同的端点和API密钥发送一个[`getHealth`](/docs/zh/rpc/guides/gethealth) RPC 调用。

这些优势只有经验丰富的交易员才能察觉。对于普通应用开发人员，我们建议遵循下面智能交易发送部分的指南。

<Tip>
  通过[Raw Shreds (UDP)](/docs/zh/shred-delivery/raw-shreds)尽可能快速地获取链上交易数据。 [在您的 Helius Dashboard 订阅](https://dashboard.helius.dev/shred-delivery-seats)。
</Tip>

## 发送智能交易

Helius [Node.js](/docs/zh/sending-transactions/optimizing-transactions#nodejs-sdk) 和 [Rust](/docs/zh/sending-transactions/optimizing-transactions#rust-sdk) SDK 都可以发送智能交易。这个新方法在处理确认状态的同时构建并发送优化交易。

用户可以配置交易的发送选项，例如交易是否应跳过预检。

在最基本的层面上，用户必须提供他们的密钥对和他们希望执行的指令，我们处理剩下的。

我们：

* 获取最新的 blockhash
* 构建初始交易
* 模拟初始交易以获取消耗的计算单元（CU）
* 将 CU 限制设置为上一步中消耗的 CU，并留出一些余量
* 通过我们的 [Priority Fee API](/docs/zh/priority-fee-api) 获取 Helius 推荐的优先费率
* 将优先费（每 CU 的微 lamports）设置为 Helius 推荐的费率
* 添加一个小的缓冲费，以防推荐费在接下来的几秒钟内发生变化
* 构建并发送优化交易
* 如果成功，返回交易签名

<Note>
  对于我们的质押连接，要求推荐值（或更高）可以确保 Helius 发送高质量交易，并且我们不会被验证者限速。
</Note>

这种方法是 Solana 上构建、发送和落地交易的最简单方法。

通过使用 Helius 推荐的费率，Helius 用户在我们的[标准付费计划](/docs/zh/billing/plans)之一上发送的交易将通过我们的质押连接路由，保证交易几乎 100% 传送并且延迟最小。

### Node.js SDK

在我们的[Helius Node.js SDK](/docs/zh/sdks)中，`sendSmartTransaction` 方法可用于[版本 >= 1.3.2](https://www.npmjs.com/package/helius-sdk)。要更新到更高版本的SDK，请运行 `npm update helius-sdk`。

此示例将 SOL 转移到您选择的账户。它使用 `sendSmartTransaction` 发送不跳过预检检查的优化交易：

```typescript theme={"system"}
import { Helius } from "helius-sdk";
import {
  Keypair,
  SystemProgram,
  LAMPORTS_PER_SOL,
  TransactionInstruction,
} from "@solana/web3.js";

const helius = new Helius("YOUR_API_KEY");
const fromKeypair = /* Your keypair goes here */;
const fromPubkey = fromKeypair.publicKey;
const toPubkey = /* The person we're sending 0.5 SOL to */;

const instructions: TransactionInstruction[] = [
  SystemProgram.transfer({
    fromPubkey: fromPubkey,
    toPubkey: toPubkey,
    lamports: 0.5 * LAMPORTS_PER_SOL, 
  }),
];

const transactionSignature = await helius.rpc.sendSmartTransaction(instructions, [fromKeypair]);
console.log(`Successful transfer: ${transactionSignature}`);
```

### Rust SDK

`send_smart_transaction` 方法在我们的 [Rust SDK](/docs/zh/sdks) 中可用，适用于 [版本 >= 0.1.5](https://docs.rs/helius/latest/helius/)。要更新到更高版本的 SDK，运行 `cargo update helius`。

以下示例将0.01 SOL 转移到您选择的账户。

它利用 `send_smart_transaction` 发送跳过预检检查的优化交易，并在必要时重试两次：

```rust theme={"system"}
use helius::types::*;
use helius::Helius;
use solana_sdk::{
    pubkey::Pubkey,
    signature::Keypair,
    system_instruction
};

#[tokio::main]
async fn main() {
    let api_key: &str = "YOUR_API_KEY";
    let cluster: Cluster = Cluster::MainnetBeta;
    let helius: Helius = Helius::new(api_key, cluster).unwrap();
    
    let from_keypair: Keypair = /* Your keypair goes here */;
    let from_pubkey: Pubkey = from_keypair.pubkey();
    let to_pubkey: Pubkey = /* The person we're sending 0.01 SOL to */;

    // Create a simple instruction (transfer 0.01 SOL from from_pubkey to to_pubkey)
    let transfer_amount = 100_000; // 0.01 SOL in lamports
    let instruction = system_instruction::transfer(&from_pubkey, &to_pubkey, transfer_amount);

    // Create the SmartTransactionConfig
    let config = SmartTransactionConfig {
        instructions,
        signers: vec![&from_keypair],
        send_options: RpcSendTransactionConfig {
            skip_preflight: true,
            preflight_commitment: None,
            encoding: None,
            max_retries: Some(2),
            min_context_slot: None,
        },
        lookup_tables: None,
    };

    // Send the optimized transaction
    match helius.send_smart_transaction(config).await {
        Ok(signature) => {
            println!("Transaction sent successfully: {}", signature);
        }
        Err(e) => {
            eprintln!("Failed to send transaction: {:?}", e);
        }
    }
}
```

## 发送不使用SDK的交易

我们建议使用我们的 SDK 发送智能交易，但也可以不使用它实现相同的功能。

Node.js SDK 和 Rust SDK 都是开源的，可以随时查看发送智能交易功能的底层代码。

### 准备和构建初始交易

首先，准备和构建初始交易。这包括创建一个包含指令集的新交易，添加最近的区块哈希，并指定费用支付者。

对于版本化的交易，创建一个 `TransactionMessage` 并在有查找表的情况下编译它。

然后，创建一个新的版本化交易并签署它——在我们模拟交易的下一步中需要进行签名，因为交易必须被签名。

例如，如果我们想准备一个版本化交易：

```typescript theme={"system"}
// Prepare your instructions and set them to an instructions variable
// The payerKey is the public key that will be paying for this transaction
// Prepare your lookup tables and set them to a lookupTables variable
let recentBlockhash = (await this.connection.getLatestBlockhash()).blockhash;
const v0Message = new TransactionMessage({
    instructions: instructions,
    payerKey: pubKey,
    recentBlockhash: recentBlockhash,
}).compileToV0Message(lookupTables);
versionedTransaction = new VersionedTransaction(v0Message);
versionedTransaction.sign([fromKeypair]);
```

### 优化交易的计算单元（CU）使用

为了[优化交易的计算单元（CU）使用](https://www.helius.dev/blog/optimizing-solana-programs)，我们可以使用`simulateTransaction` RPC方法来模拟交易。

[模拟交易](/docs/zh/api-reference/rpc/http/simulatetransaction)将返回使用的CU数量，因此我们可以使用该值来相应地设置我们的计算限制。

建议首先使用具有所需指令的测试交易，加上一个将计算限制设置为1.4m CU的指令。

这样做是为了确保交易模拟成功。

例如：

```typescript theme={"system"}
const testInstructions = [
    ComputeBudgetProgram.setComputeUnitLimit({ units: 1_400_000 }),
    ...instructions,
];

const testTransaction = new VersionedTransaction(
    new TransactionMessage({
        instructions: testInstructions,
        payerKey: payer,
        recentBlockhash: (await this.connection.getLatestBlockhash()).blockhash,
    }).compileToV0Message(lookupTables)
);

const rpcResponse = await this.connection.simulateTransaction(testTransaction, {
    replaceRecentBlockhash: true,
    sigVerify: false,
});

const unitsConsumed = rpcResponse.value.unitsConsumed;
```

还建议添加一点余量，以确保交易顺利执行。我们可以通过设置以下内容来实现：

```typescript theme={"system"}
let customersCU = Math.ceil(unitsConsumed * 1.1);
```

然后，创建一个将该值设置为计算单元限制的指令，并将其添加到您的指令数组中：

```typescript theme={"system"}
const computeUnitIx = ComputeBudgetProgram.setComputeUnitLimit({
    units: customersCU
});
instructions.push(computeUnitIx);
```

### 序列化和编码交易

这相对简单。

首先，为了序列化交易，Transaction和VersionedTransaction类型都有一个`.serialize()`方法。然后使用[bs58包](https://www.npmjs.com/package/bs58)对交易进行编码。

您的代码应该类似于`bs58.encode(txt.serialize());`

### 设置正确的优先级费用

首先，使用[优先级费用API](/docs/zh/priority-fee-api)获取优先级费用估算。我们希望传入我们的交易，并通过推荐参数获取Helius推荐的费用：

```typescript theme={"system"}
const response = await fetch(HeliusURL, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
        jsonrpc: "2.0",
        id: "1",
        method: "getPriorityFeeEstimate",
        params: [
            {
                transaction: bs58.encode(versionedTransaction), // Pass the serialized transaction in
                options: { recommended: true },
            },
        ],   
    }),
});

const data = await response.json();
const priorityFeeRecommendation = data.result.priorityFeeEstimate;
```

然后，创建一个指令，将该值设置为计算单元价格，并将该指令添加到您的先前指令中：

```typescript theme={"system"}
const computeBudgetIx = ComputeBudgetProgram.setComputeUnitPrice({
    microLamports: priorityFeeRecommendation,
});

instructions.push(computeBudgetIx);
```

### 构建并发送优化后的交易

这一步几乎是第一步的重复。然而，初始指令数组已经被更改，以优化地添加两个指令来设置计算单元限制和价格。

现在，发送交易。

无论是否进行预检或更改其他发送选项，交易都会通过我们投注的连接路由，适用于所有付费计划。

### 轮询交易状态和重新广播

<警告> 尽管投注连接会将交易直接转发给领袖，但交易在[银行阶段](https://www.helius.dev/blog/solana-virtual-machine#the-banking-stage)仍可能被丢弃。建议用户使用自己重新广播的逻辑，而不是依赖RPC来为他们重试交易。 </警告>

[`sendTransaction` RPC方法](/docs/zh/api-reference/rpc/http/sendtransaction) 有一个`maxRetries` 参数，可以通过设置来覆盖RPC的默认重试逻辑，使开发者对重试过程有更多控制。

一个常见的模式是通过[`getLatestBlockhash`](/docs/zh/rpc/guides/getlatestblockhash)获取当前的区块哈希，存储`lastValidBlockHeight`，并在区块哈希过期前重试交易。

至关重要的是，只在区块哈希失效时重新签署交易，否则两笔交易可能都会被网络接受。

一旦交易被发送，务必轮询其确认状态，以查看网络是否已经处理并确认交易，然后再重试。使用[`getSignatureStatuses` RPC方法](/docs/zh/rpc/guides/getsignaturestatuses)检查交易列表的确认状态。

@solana/web3.js SDK 也在其`Connection`类上有一个`getSignatureStatuses`方法，用于获取多个签名的当前状态。

### sendSmartTransaction 如何处理轮询和重新广播

`sendSmartTransaction`方法的超时时间为60秒。由于区块哈希有效150个槽位，假设400ms的理想槽位，我们可以合理地假设交易的区块哈希在一分钟后失效。

该方法发送交易并使用此超时时间轮询其签名：

```typescript theme={"system"}
try {
   // Create a smart transaction
   const transaction = await this.createSmartTransaction(instructions, signers, lookupTables, sendOptions);
  
   const timeout = 60000;
   const startTime = Date.now();
   let txtSig;
  
   while (Date.now() - startTime < timeout) {
     try {
       txtSig = await this.connection.sendRawTransaction(transaction.serialize(), {
         skipPreflight: sendOptions.skipPreflight,
         ...sendOptions,
       });
  
       return await this.pollTransactionConfirmation(txtSig);
     } catch (error) {
       continue;
     }
   }
} catch (error) {
   throw new Error(`Error sending smart transaction: ${error}`);
}
```

`txtSig` 被设置为刚刚发送的交易的签名。

然后该方法使用 `pollTransactionConfirmation()` 方法轮询交易的确认状态。此方法每五秒检查一次交易的状态，最多三次。

如果在此期间交易未确认，则返回错误：

```typescript theme={"system"}
async pollTransactionConfirmation(txtSig: TransactionSignature): Promise<TransactionSignature> {
    // 15 second timeout
    const timeout = 15000;
    // 5 second retry interval
    const interval = 5000;
    let elapsed = 0;

    return new Promise<TransactionSignature>((resolve, reject) => {
      const intervalId = setInterval(async () => {
        elapsed += interval;

        if (elapsed >= timeout) {
          clearInterval(intervalId);
          reject(new Error(`Transaction ${txtSig}'s confirmation timed out`));
        }

        const status = await this.connection.getSignatureStatuses([txtSig]);

        if (status?.value[0]?.confirmationStatus === "confirmed") {
          clearInterval(intervalId);
          resolve(txtSig);
        }
      }, interval);
   });
}
```
