指令构建方式
使用 Anchor构建
// 使用 Anchor 构建交易
const buyTokenExactInArgs = {
payAmount: new BN(100000000), // 0.01 SOL (10,000,000 lamports)
minReceive: new BN(0), // 最少接收1个代币
donateRate: 0 // 捐赠率为0
};
const tx= await program.methods
.buyTokenExactIn(buyTokenExactInArgs) //交易参数
.accounts(accounts) //指令账户
.instruction(); //创建交易指令
原生构建
// 原生方式构建交易
const accounts = [
{ pubkey: account1, isSigner: true, isWritable: false },
{ pubkey: account2, isSigner: false, isWritable: true }
];
const tx= new TransactionInstruction({
programId: programId,
keys: accounts,
data: instructionData
});
const transaction = new Transaction().add(instruction);
⚙️ 费用配置
计算单元限制,
const computeUnitLimit = 500000; // 根据指令复杂度调整
const computeLimitInstruction = ComputeBudgetProgram.setComputeUnitLimit({
units: computeUnitLimit
});
Priority Fee 设置(优先费)
const priorityFeeInMicroLamports = 100000; // 网络拥堵情况调整
const priorityFeeInstruction = ComputeBudgetProgram.setComputeUnitPrice({
microLamports: priorityFeeInMicroLamports
});
// 计算实际费用,计算单元 * PriorityFee / 1000000
const estimatedPriorityFee = (computeUnitLimit * priorityFeeInMicroLamports) / 1000000;
Jito Fee (可选)
const jitoTipAccounts = [
"96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5",
// ... 其他账户
];
const jitoTipAccount = new PublicKey(jitoTipAccounts[Math.floor(Math.random() * jitoTipAccounts.length)]);
const jitoFeeInLamports = 20000;
const jitoTipInstruction = SystemProgram.transfer({
fromPubkey: playerKeyPair.publicKey,
toPubkey: jitoTipAccount,
lamports: jitoFeeInLamports
});
🔧 交易最终设置
基本设置
// 设置手续费的支付者
tx.feePayer = playerKeyPair.publicKey;
// 获取最新区块哈希
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
tx.recentBlockhash = blockhash;
tx.lastValidBlockHeight = lastValidBlockHeight;
添加费用指令(按顺序)
const finalTransaction = new Transaction();
// 指令顺序很重要!
finalTransaction.add(computeLimitInstruction); // 1. 计算单元限制
finalTransaction.add(priorityFeeInstruction); // 2. Priority Fee
finalTransaction.add(jitoTipInstruction); // 3. Jito Fee (可选)
finalTransaction.add(mainInstruction); // 4. 主要业务指令
// 设置交易属性
finalTransaction.feePayer = tx.feePayer;
finalTransaction.recentBlockhash = tx.recentBlockhash;
finalTransaction.lastValidBlockHeight = tx.lastValidBlockHeight;
⚠️ 关键计算和检查
账户余额检查
const balance = await connection.getBalance(playerKeyPair.publicKey);
const requiredBalance = maxPay + estimatedPriorityFee + jitoFeeInLamports + 5000; // 基础费用
if (balance < requiredBalance) {
throw new Error(`余额不足: 需要 ${requiredBalance} lamports, 当前 ${balance} lamports`);
}
滑点保护
// ⚠️ 重要:不要设置 minReceive: 0,可能会遇到夹子
// 需要修改指令参数中的最小接收数量
// 计算合理的最小接收量
const slippageTolerance = 0.05; // 5% 滑点
const expectedTokens = await calculateExpectedOutput(maxPay); // 需要实现
const minReceive = Math.floor(expectedTokens * (1 - slippageTolerance));
交易大小检查
const serializedTransaction = finalTransaction.serialize();
console.log('交易大小:', serializedTransaction.length, 'bytes');
if (serializedTransaction.length > 1232) {
throw new Error('交易过大,超过 1232 bytes 限制');
}
费用计算总结
const totalFees = {
baseFee: 5000, // 系统自动收取
priorityFee: estimatedPriorityFee, // 手动设置
jitoFee: jitoFeeInLamports, // 可选
total: 5000 + estimatedPriorityFee + jitoFeeInLamports
};
console.log('费用明细:', totalFees);
📤 交易发送和确认
签名和发送
// 签名交易
finalTransaction.sign(playerKeyPair);
// 发送交易
const signature = await connection.sendRawTransaction(
finalTransaction.serialize(),
{
skipPreflight: false,
preflightCommitment: 'finalized',
maxRetries: 5
}
);
等待确认
// 等待交易确认
const confirmation = await connection.confirmTransaction({
signature: signature,
blockhash: blockhash,
lastValidBlockHeight: lastValidBlockHeight
});
if (confirmation.value.err) {
throw new Error(`交易失败: ${confirmation.value.err}`);
}
📊 完整示例总结
// 1. 环境准备 ✅
// 2. 账户计算 ✅
// 3. 参数验证 ⚠️ (需要完善滑点保护)
// 4. 费用配置 ✅
// 5. 交易构建 ✅
// 6. 大小检查 ⚠️ (建议添加)
// 7. 余额检查 ⚠️ (建议添加)
// 8. 签名发送 ✅
// 9. 确认等待 ⚠️ (建议添加)
🚨 常见风险和注意事项
高风险
minReceive: 0
→ 可能遭受三明治攻击缺少余额检查 → 交易可能因余额不足失败
区块哈希过期 → 交易可能被拒绝
中风险
固定 Priority Fee → 网络拥堵时执行慢
缺少重试机制 → 临时网络问题导致失败
交易大小超限 → 交易被拒绝