1
1
import {
2
+ ComputeBudgetProgram ,
2
3
Connection ,
3
4
Keypair ,
4
5
SendOptions ,
@@ -15,6 +16,9 @@ import { SolanaChains } from '../types';
15
16
import { SolanaUnsignedTransaction } from '../unsignedTransaction' ;
16
17
import { logTxDetails } from './debug' ;
17
18
19
+ // Number of blocks to wait before considering a transaction expired
20
+ const SOLANA_EXPIRED_BLOCKHEIGHT = 150 ;
21
+
18
22
export class SolanaSendSigner <
19
23
N extends Network ,
20
24
C extends SolanaChains = 'Solana' ,
@@ -26,6 +30,7 @@ export class SolanaSendSigner<
26
30
private _keypair : Keypair ,
27
31
private _debug : boolean = false ,
28
32
private _sendOpts ?: SendOptions ,
33
+ private _priotifyFeeAmount ?: bigint ,
29
34
) {
30
35
this . _sendOpts = this . _sendOpts ?? {
31
36
preflightCommitment : this . _rpc . commitment ,
@@ -42,6 +47,7 @@ export class SolanaSendSigner<
42
47
43
48
// Handles retrying a Transaction if the error is deemed to be
44
49
// recoverable. Currently handles:
50
+ // - Transaction expired
45
51
// - Blockhash not found
46
52
// - Not enough bytes (storage account not seen yet)
47
53
private retryable ( e : any ) : boolean {
@@ -75,7 +81,6 @@ export class SolanaSendSigner<
75
81
async signAndSend ( tx : UnsignedTransaction [ ] ) : Promise < any [ ] > {
76
82
let { blockhash, lastValidBlockHeight } = await SolanaPlatform . latestBlock (
77
83
this . _rpc ,
78
- 'finalized' ,
79
84
) ;
80
85
81
86
const txids : string [ ] = [ ] ;
@@ -86,10 +91,18 @@ export class SolanaSendSigner<
86
91
} = txn as SolanaUnsignedTransaction < N , C > ;
87
92
console . log ( `Signing: ${ description } for ${ this . address ( ) } ` ) ;
88
93
94
+ if ( this . _priotifyFeeAmount )
95
+ transaction . add (
96
+ ComputeBudgetProgram . setComputeUnitPrice ( {
97
+ microLamports : this . _priotifyFeeAmount ,
98
+ } ) ,
99
+ ) ;
100
+
89
101
if ( this . _debug ) logTxDetails ( transaction ) ;
90
102
91
103
// Try to send the transaction up to 5 times
92
- for ( let i = 0 ; i < 5 ; i ++ ) {
104
+ const maxRetries = 5 ;
105
+ for ( let i = 0 ; i < maxRetries ; i ++ ) {
93
106
try {
94
107
transaction . recentBlockhash = blockhash ;
95
108
transaction . partialSign ( this . _keypair , ...( extraSigners ?? [ ] ) ) ;
@@ -101,11 +114,28 @@ export class SolanaSendSigner<
101
114
txids . push ( txid ) ;
102
115
break ;
103
116
} catch ( e ) {
117
+ // No point checking if retryable if we're on the last retry
118
+ if ( i === maxRetries - 1 ) throw e ;
119
+
120
+ // If it's not retryable, throw
104
121
if ( ! this . retryable ( e ) ) throw e ;
105
122
106
- // If it is retryable, we should grab a new block hash
107
- ( { blockhash, lastValidBlockHeight } =
108
- await SolanaPlatform . latestBlock ( this . _rpc , 'finalized' ) ) ;
123
+ // If it is retryable, we need to grab a new block hash
124
+ const {
125
+ blockhash : newBlockhash ,
126
+ lastValidBlockHeight : newBlockHeight ,
127
+ } = await SolanaPlatform . latestBlock ( this . _rpc ) ;
128
+
129
+ // But we should _not_ submit if the blockhash hasnt expired
130
+ if (
131
+ newBlockHeight - lastValidBlockHeight <
132
+ SOLANA_EXPIRED_BLOCKHEIGHT
133
+ ) {
134
+ throw e ;
135
+ }
136
+
137
+ lastValidBlockHeight = newBlockHeight ;
138
+ blockhash = newBlockhash ;
109
139
}
110
140
}
111
141
}
0 commit comments