Solana主网转账遇TransactionExpiredBlockheightExceededError求助
解决方案
1. 修正计算预算指令的添加顺序
你当前在交易签名后添加ComputeBudgetProgram.setComputeUnitPrice指令,这会直接导致交易签名失效——因为签名是基于签名前的交易数据生成的,后续修改交易内容会让节点拒绝该签名。必须在签名前添加计算预算指令:
// 先创建交易并添加所有核心指令 const transaction = new Transaction(); if (!accountInfo) { const associatedTokenAddress = await getAssociatedTokenAddress( mintAddress, to, ); transaction.add( createAssociatedTokenAccountInstruction( from, associatedTokenAddress, to, mintAddress, TOKEN_PROGRAM_ID, ), ); } transaction.add( createTransferInstruction( fromTokenAccount, toTokenAccount, from, amount, [], TOKEN_PROGRAM_ID, ), ); // 先添加计算预算指令,再进行签名 transaction.add( ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 100_000, }), ); // 获取blockhash及有效截止块高 const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('finalized'); transaction.recentBlockhash = blockhash; transaction.lastValidBlockHeight = lastValidBlockHeight; // 关键:设置交易过期的块高阈值 // 最后执行签名操作 transaction.sign({ publicKey: from, secretKey: signer });
2. 显式设置交易的lastValidBlockHeight
getLatestBlockhash会返回lastValidBlockHeight,代表当前blockhash的有效截止块高。如果不设置这个值,交易可能在默认的过期窗口内无法被节点打包。将其赋值给交易的对应属性,让节点明确知晓交易的有效期。
3. 优化sendAndConfirmTransaction的确认参数
默认的确认参数可能不够严格,导致交易未及时确认就触发超时。你可以指定更高的commitment级别和更长的超时时间:
return sendAndConfirmTransaction(connection, transaction, [ { publicKey: from, secretKey: signer }, ], { commitment: 'finalized', // 等待交易最终确认,避免中间状态的不确定性 timeout: 30000, // 超时时间设为30秒,覆盖默认的较短超时窗口 skipPreflight: false, // 保留预检查,提前发现交易格式或余额问题 })
4. 添加失败自动重试逻辑
即使做了上述优化,网络波动仍可能导致交易超时。可以捕获TransactionExpiredBlockheightExceededError并自动重试,最多重试2-3次:
async function sendWithRetry(connection, transaction, signers, maxRetries = 2) { let retryCount = 0; while (retryCount <= maxRetries) { try { // 每次重试前更新blockhash和有效块高 const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('finalized'); transaction.recentBlockhash = blockhash; transaction.lastValidBlockHeight = lastValidBlockHeight; // 重新签名(blockhash变更后需重新签名) transaction.sign(...signers); return await sendAndConfirmTransaction(connection, transaction, signers, { commitment: 'finalized', timeout: 30000, }); } catch (error) { if (error instanceof TransactionExpiredBlockheightExceededError && retryCount < maxRetries) { retryCount++; console.log(`交易超时,正在重试第${retryCount}次`); await new Promise(resolve => setTimeout(resolve, 1000)); // 等待1秒后重试,避免高频请求 } else { throw error; } } } throw new Error('重试次数耗尽,交易失败'); } // 替换原有的sendAndConfirmTransaction调用 console.log('> TRANSFERRING TOKENS'); return sendWithRetry(connection, transaction, [{ publicKey: from, secretKey: signer }]);
5. 检查gasStation逻辑的延迟
如果gasStation操作耗时过长,即使后续重新创建交易,也可能因为网络延迟导致新交易的blockhash很快过期。确保gasStation是快速完成的操作,若涉及SOL转账,建议给gas交易也设置高小费和严格的确认参数,减少等待时间。
内容的提问来源于stack exchange,提问作者Jonah Elbaz
相关产品推荐
相关产品推荐

