@@ -6,6 +6,7 @@ import type {
6
6
VersionedTransaction ,
7
7
PublicKey ,
8
8
AddressLookupTableAccount ,
9
+ RecentPrioritizationFees ,
9
10
} from '@solana/web3.js' ;
10
11
import {
11
12
ComputeBudgetProgram ,
@@ -21,7 +22,7 @@ import type {
21
22
Signer ,
22
23
UnsignedTransaction ,
23
24
} from '@wormhole-foundation/sdk-connect' ;
24
- import { encoding } from '@wormhole-foundation/sdk-connect' ;
25
+ import { bound , encoding , median } from '@wormhole-foundation/sdk-connect' ;
25
26
import { SolanaPlatform } from './platform.js' ;
26
27
import type { SolanaChains } from './types.js' ;
27
28
import {
@@ -380,6 +381,37 @@ export async function determineComputeBudget(
380
381
return computeBudget ;
381
382
}
382
383
384
+ // Helper function to get the writable accounts from a transaction
385
+ export async function getWritableAccounts (
386
+ connection : Connection ,
387
+ transaction : Transaction | VersionedTransaction ,
388
+ ) : Promise < PublicKey [ ] > {
389
+ if ( isVersionedTransaction ( transaction ) ) {
390
+ const luts = (
391
+ await Promise . all (
392
+ transaction . message . addressTableLookups . map ( ( acc ) =>
393
+ connection . getAddressLookupTable ( acc . accountKey ) ,
394
+ ) ,
395
+ )
396
+ )
397
+ . map ( ( lut ) => lut . value )
398
+ . filter ( ( val ) => val !== null ) as AddressLookupTableAccount [ ] ;
399
+ const msg = transaction . message ;
400
+ const keys = msg . getAccountKeys ( {
401
+ addressLookupTableAccounts : luts ?? undefined ,
402
+ } ) ;
403
+ return msg . compiledInstructions
404
+ . flatMap ( ( ix ) => ix . accountKeyIndexes )
405
+ . map ( ( k ) => ( msg . isAccountWritable ( k ) ? keys . get ( k ) : null ) )
406
+ . filter ( Boolean ) as PublicKey [ ] ;
407
+ } else {
408
+ return transaction . instructions
409
+ . flatMap ( ( ix ) => ix . keys )
410
+ . map ( ( k ) => ( k . isWritable ? k . pubkey : null ) )
411
+ . filter ( Boolean ) as PublicKey [ ] ;
412
+ }
413
+ }
414
+
383
415
/**
384
416
* A helper function to determine the priority fee to use for a transaction
385
417
*
@@ -405,31 +437,10 @@ export async function determinePriorityFee(
405
437
let fee = minPriorityFee ;
406
438
407
439
// Figure out which accounts need write lock
408
- let lockedWritableAccounts = [ ] ;
409
- if ( isVersionedTransaction ( transaction ) ) {
410
- const luts = (
411
- await Promise . all (
412
- transaction . message . addressTableLookups . map ( ( acc ) =>
413
- connection . getAddressLookupTable ( acc . accountKey ) ,
414
- ) ,
415
- )
416
- )
417
- . map ( ( lut ) => lut . value )
418
- . filter ( ( val ) => val !== null ) as AddressLookupTableAccount [ ] ;
419
- const msg = transaction . message ;
420
- const keys = msg . getAccountKeys ( {
421
- addressLookupTableAccounts : luts ?? undefined ,
422
- } ) ;
423
- lockedWritableAccounts = msg . compiledInstructions
424
- . flatMap ( ( ix ) => ix . accountKeyIndexes )
425
- . map ( ( k ) => ( msg . isAccountWritable ( k ) ? keys . get ( k ) : null ) )
426
- . filter ( ( k ) => k !== null ) as PublicKey [ ] ;
427
- } else {
428
- lockedWritableAccounts = transaction . instructions
429
- . flatMap ( ( ix ) => ix . keys )
430
- . map ( ( k ) => ( k . isWritable ? k . pubkey : null ) )
431
- . filter ( ( k ) => k !== null ) as PublicKey [ ] ;
432
- }
440
+ const lockedWritableAccounts = await getWritableAccounts (
441
+ connection ,
442
+ transaction ,
443
+ ) ;
433
444
434
445
try {
435
446
const recentFeesResponse = await connection . getRecentPrioritizationFees ( {
@@ -461,6 +472,64 @@ export async function determinePriorityFee(
461
472
return Math . min ( Math . max ( fee , minPriorityFee ) , maxPriorityFee ) ;
462
473
}
463
474
475
+ interface RpcResponse {
476
+ jsonrpc : String ;
477
+ id ?: String ;
478
+ result ?: [ ] ;
479
+ error ?: any ;
480
+ }
481
+
482
+ // Helper function to calculate the priority fee using the Triton One API
483
+ // See https://docs.triton.one/chains/solana/improved-priority-fees-api
484
+ // NOTE: this is currently an experimental feature
485
+ export async function determinePriorityFeeTritonOne (
486
+ connection : Connection ,
487
+ transaction : Transaction | VersionedTransaction ,
488
+ percentile : number = DEFAULT_PRIORITY_FEE_PERCENTILE ,
489
+ multiple : number = DEFAULT_PERCENTILE_MULTIPLE ,
490
+ minPriorityFee : number = DEFAULT_MIN_PRIORITY_FEE ,
491
+ maxPriorityFee : number = DEFAULT_MAX_PRIORITY_FEE ,
492
+ ) : Promise < number > {
493
+ const scaledPercentile = percentile * 10_000 ;
494
+
495
+ if ( scaledPercentile < 1 || scaledPercentile > 10_000 ) {
496
+ throw new Error ( 'percentile must be between 0.0001 and 1' ) ;
497
+ }
498
+
499
+ // @ts -ignore
500
+ const rpcRequest = connection . _rpcRequest ;
501
+
502
+ const accounts = await getWritableAccounts ( connection , transaction ) ;
503
+
504
+ const args = [
505
+ accounts ,
506
+ {
507
+ percentile : scaledPercentile ,
508
+ } ,
509
+ ] ;
510
+
511
+ const response = ( await rpcRequest (
512
+ 'getRecentPrioritizationFees' ,
513
+ args ,
514
+ ) ) as RpcResponse ;
515
+
516
+ if ( response . error ) {
517
+ throw new Error ( response . error ) ;
518
+ }
519
+
520
+ const recentPrioritizationFees = (
521
+ response . result as RecentPrioritizationFees [ ]
522
+ ) . map ( ( e ) => e . prioritizationFee ) ;
523
+
524
+ if ( recentPrioritizationFees . length === 0 ) return minPriorityFee ;
525
+
526
+ const unboundedFee = Math . floor (
527
+ median ( recentPrioritizationFees ) * ( multiple > 0 ? multiple : 1 ) ,
528
+ ) ;
529
+
530
+ return bound ( unboundedFee , minPriorityFee , maxPriorityFee ) ;
531
+ }
532
+
464
533
export class SolanaSigner < N extends Network , C extends SolanaChains = 'Solana' >
465
534
implements SignOnlySigner < N , C >
466
535
{
0 commit comments