> ## 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 交易发送工作流。学习计算优化、优先费用和确认策略。

<Tip>
  这是基础交易发送路径——按次计费，适用于可靠性比速度更重要的场合（支付、钱包、应用）。如果您在交易并需要最低延迟，请改用[Helius Sender](/docs/zh/sending-transactions/sender)。
</Tip>

构建您自己的交易发送逻辑是确保应用程序获得最大性能、控制和可靠性的最佳方式。虽然[Helius SDK](/docs/zh/sdks)提供了方便的入门封装，但强烈建议在生产系统中理解和实施此手动工作流程。

本指南将引导您完成构建自己解决方案的必要步骤。

### 手动工作流程

手动发送交易涉及以下步骤：

<Steps>
  <Step title="构建初始交易">
    汇集您的指令并签署交易，以便进行模拟。
  </Step>

  <Step title="优化计算单元">
    模拟交易以确定所需的精确CU，并添加一个小缓冲。
  </Step>

  <Step title="添加优先费用">
    从Helius优先费用API获取费用估算并将其添加到您的交易中。
  </Step>

  <Step title="发送并重新广播">
    发送最终交易并实施稳健的轮询策略以处理确认。
  </Step>
</Steps>

<Info>
  Helius SDK是开源的。您可以查看我们[Node.js SDK](https://github.com/helius-labs/helius-sdk)和[Rust SDK](https://github.com/helius-labs/helius-rust-sdk)中`sendSmartTransaction`方法的底层代码，查看此工作流程的生产级实现。
</Info>

### 1. 构建初始交易

首先，收集您想在交易中包含的所有指令。然后，创建一个`Transaction`或`VersionedTransaction`对象。您还需要获取一个最近的区块哈希。

此示例准备一个版本化交易。在此阶段，您还必须对其进行签名，以便在下一步进行模拟。

```typescript theme={"system"}
import {
  Connection,
  Keypair,
  TransactionMessage,
  VersionedTransaction,
  SystemProgram,
  LAMPORTS_PER_SOL,
} from "@solana/web3.js";

const connection = new Connection("YOUR_RPC_URL");
const fromKeypair = Keypair.generate(); // Assume this is funded
const toPubkey = Keypair.generate().publicKey;

// 1. Build your instructions
const instructions = [
  SystemProgram.transfer({
    fromPubkey: fromKeypair.publicKey,
    toPubkey: toPubkey,
    lamports: 0.001 * LAMPORTS_PER_SOL,
  }),
];

// 2. Get a recent blockhash
const { blockhash } = await connection.getLatestBlockhash();

// 3. Compile the transaction message
const messageV0 = new TransactionMessage({
  payerKey: fromKeypair.publicKey,
  recentBlockhash: blockhash,
  instructions,
}).compileToV0Message();

// 4. Create and sign the transaction
const transaction = new VersionedTransaction(messageV0);
transaction.sign([fromKeypair]);
```

### 2. 优化计算单元 (CU) 使用

为了避免浪费费用或交易失败，您应尽可能准确地设定计算单元 (CU) 限制。您可以通过使用 [`simulateTransaction`](/docs/zh/api-reference/rpc/http/simulatetransaction) RPC 方法模拟交易来实现这一点。

最佳实践是首先用高 CU 限制定进行模拟，以确保模拟本身成功，然后使用响应中的 `unitsConsumed` 来设定实际限制。

```typescript theme={"system"}
import { ComputeBudgetProgram } from "@solana/web3.js";

// Create a test transaction with a high compute limit to ensure simulation succeeds
const testInstructions = [
    ComputeBudgetProgram.setComputeUnitLimit({ units: 1_400_000 }),
    ...instructions, // Your original instructions
];
const testMessage = new TransactionMessage({
    payerKey: fromKeypair.publicKey,
    recentBlockhash: blockhash,
    instructions: testInstructions,
}).compileToV0Message();
const testTransaction = new VersionedTransaction(testMessage);
testTransaction.sign([fromKeypair]);

// Simulate the transaction to get the exact CUs consumed
const { value: simulationResult } = await connection.simulateTransaction(testTransaction);

if (!simulationResult.unitsConsumed) {
  throw new Error("Simulation failed to return unitsConsumed");
}

// Add a 10% buffer to the CU estimate
const computeUnitLimit = Math.ceil(simulationResult.unitsConsumed * 1.1);

// Create the instruction to set the CU limit
const setCuLimitInstruction = ComputeBudgetProgram.setComputeUnitLimit({
    units: computeUnitLimit,
});
```

现在您已有一个精确设定计算限制的指令。您将把它添加到最终交易中。

### 3. 设置合适的优先费用

接下来，确定要添加到交易中的最佳优先费用。使用 Helius [Priority Fee API](/docs/zh/priority-fee-api) 是根据当前网络条件获取实时估算的最佳方式。

您需要调用 `getPriorityFeeEstimate` RPC 方法。为了通过 Helius 的抵押连接获得最大包含机会，请使用 `recommended: true` 选项。

```typescript theme={"system"}
// The transaction needs to be serialized and base58 encoded
const serializedTransaction = bs58.encode(transaction.serialize());

const response = await fetch("YOUR_RPC_URL", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
        jsonrpc: "2.0",
        id: "1",
        method: "getPriorityFeeEstimate",
        params: [
            {
                // Pass the serialized transaction
                transaction: serializedTransaction, 
                // Use 'recommended' for Helius's staked connections
                options: { recommended: true },
            },
        ],   
    }),
});
const data = await response.json();

if (!data.result || !data.result.priorityFeeEstimate) {
    throw new Error("Failed to get priority fee estimate");
}

const priorityFeeEstimate = data.result.priorityFeeEstimate;

// Create the instruction to set the priority fee
const setPriorityFeeInstruction = ComputeBudgetProgram.setComputeUnitPrice({
    microLamports: priorityFeeEstimate,
});
```

### 4. 构建、发送和确认

现在，组装带有新计算预算指令的最终交易，发送它，并实施一个强大的轮询机制以确认它已完成。

<Warning>
  不要依赖 RPC 提供商的默认重试逻辑（[`maxRetries`](/docs/zh/api-reference/rpc/http/sendtransaction) 中的 `sendTransaction`）。尽管 Helius 的抵押连接直接将您的交易转发给领导者，但它仍然可能被丢弃。您必须实施自己的重广播逻辑以确保可靠地确认。
</Warning>

一个常见的模式是周期性地重新发送相同的交易，直到区块哈希过期。**仅在您也获取新区块哈希时重新签名交易。** 使用相同区块哈希重新签名可能导致确认重复交易。

```typescript theme={"system"}
// 1. Add the new instructions to your original set
const finalInstructions = [
  setCuLimitInstruction,
  setPriorityFeeInstruction,
  ...instructions,
];

// 2. Re-build and re-sign the transaction with the final instructions
const { blockhash: latestBlockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();

const finalMessage = new TransactionMessage({
  payerKey: fromKeypair.publicKey,
  recentBlockhash: latestBlockhash,
  instructions: finalInstructions,
}).compileToV0Message();

const finalTransaction = new VersionedTransaction(finalMessage);
finalTransaction.sign([fromKeypair]);

// 3. Send the transaction
const signature = await connection.sendTransaction(finalTransaction, {
  skipPreflight: true, // Optional: useful for bypassing client-side checks
});

// 4. Implement a polling loop to confirm the transaction
let confirmed = false;
while (!confirmed) {
    const statuses = await connection.getSignatureStatuses([signature]);
    const status = statuses && statuses.value && statuses.value[0];

    if (status && (status.confirmationStatus === 'confirmed' || status.confirmationStatus === 'finalized')) {
        console.log('Transaction confirmed!');
        confirmed = true;
    }

    // Check if the blockhash has expired
    const currentBlockHeight = await connection.getBlockHeight();
    if (currentBlockHeight > lastValidBlockHeight) {
        console.log('Blockhash expired, transaction failed.');
        break;
    }
    
    // Wait for a short period before polling again
    await new Promise(resolve => setTimeout(resolve, 2000)); 
}
```

这个示例提供了一个基本的轮询循环。生产级应用程序需要更复杂的逻辑，包括处理不同的确认状态和可能的超时。

## 防止三明治攻击

为了避免您的交易被统计关联到三明治攻击的验证者，您可以在RPC URL中添加`mev-protect=true`查询参数——不需要更改您的交易逻辑：

```
https://mainnet.helius-rpc.com/?api-key=YOUR_API_KEY&mev-protect=true
```

<Card title="MEV保护" icon="shield-halved" href="/docs/zh/sending-transactions/mev-protect">
  查看MEV保护的工作原理、支持的方法及其权衡。
</Card>

## 赚取交易回扣

您可以选择赚取您的交易所创造的MEV的一部分，自动以SOL支付——不需要更改您的交易逻辑。

<Card title="交易回扣" icon="coins" href="/docs/zh/sending-transactions/backrun-rebates">
  在您的`sendTransaction`调用中添加一个参数即可开始赚取SOL回扣。
</Card>

## 相关方法

<CardGroup cols={2}>
  <Card title="sendTransaction" href="/docs/zh/api-reference/rpc/http/sendtransaction">
    发送签名交易到网络
  </Card>

  <Card title="simulateTransaction" href="/docs/zh/api-reference/rpc/http/simulatetransaction">
    模拟交易以估算计算单元
  </Card>

  <Card title="getSignatureStatuses" href="/docs/zh/api-reference/rpc/http/getsignaturestatuses">
    检查交易确认状态
  </Card>

  <Card title="getLatestBlockhash" href="/docs/zh/api-reference/rpc/http/getlatestblockhash">
    获取用于交易签名的最近区块哈希
  </Card>

  <Card title="getBlockHeight" href="/docs/zh/api-reference/rpc/http/getblockheight">
    获取当前区块高度以进行到期检查
  </Card>
</CardGroup>
