@@ -39,19 +39,86 @@ const IX_ROUTER_QUOTE_EXECUTION = 1;
3939const CHAIN_ID_SOLANA = 1 ;
4040const CHAIN_ID_ETHEREUM = 2 ;
4141
42+ // Testnet chain IDs
43+ const CHAIN_ID_ETH_SEPOLIA = 10002 ;
44+ const CHAIN_ID_ARBITRUM_SEPOLIA = 10003 ;
45+ const CHAIN_ID_BASE_SEPOLIA = 10004 ;
46+ const CHAIN_ID_OPTIMISM_SEPOLIA = 10005 ;
47+
4248// Quote calculation constants (must match Rust)
4349const QUOTE_DECIMALS = 10n ;
4450const SVM_DECIMAL_RESOLUTION = 9n ;
4551const EVM_DECIMAL_RESOLUTION = 18n ;
4652
47- // Test quote parameters
53+ // Test quote parameters for mainnet Ethereum
4854const TEST_DST_PRICE = 3000n * 10n ** QUOTE_DECIMALS ; // ETH $3000
4955const TEST_SRC_PRICE = 200n * 10n ** QUOTE_DECIMALS ; // SOL $200
5056const TEST_DST_GAS_PRICE = 20n ; // 20 gwei (decimals=9)
5157const TEST_BASE_FEE = 1_000_000n ; // 0.001 SOL in lamports
5258const TEST_GAS_PRICE_DECIMALS = 9 ; // gwei decimals
5359const TEST_NATIVE_DECIMALS = 18 ; // ETH decimals
5460
61+ // Testnet chain configurations with realistic gas prices
62+ // All testnets use ETH as native token (18 decimals)
63+ // Gas prices vary by L2 characteristics
64+ interface ChainConfig {
65+ chainId : number ;
66+ name : string ;
67+ dstPrice : bigint ; // Native token price in QUOTE_DECIMALS
68+ gasPriceDecimals : number ;
69+ nativeDecimals : number ;
70+ dstGasPrice : bigint ; // Gas price in gasPriceDecimals
71+ baseFee : bigint ; // Base fee in lamports
72+ }
73+
74+ const TESTNET_CHAINS : ChainConfig [ ] = [
75+ {
76+ // Ethereum Sepolia - standard L1 gas prices
77+ // All EVM chains store gas price in wei (gasPriceDecimals=18)
78+ // Reference: w7-executor/src/env/testnet.ts
79+ chainId : CHAIN_ID_ETH_SEPOLIA ,
80+ name : "Ethereum Sepolia" ,
81+ dstPrice : 3000n * 10n ** QUOTE_DECIMALS , // ETH ~$3000
82+ gasPriceDecimals : 18 , // gas price stored in wei
83+ nativeDecimals : 18 ,
84+ dstGasPrice : 25_000_000_000n , // 25 gwei in wei
85+ baseFee : 1_000_000n , // 0.001 SOL
86+ } ,
87+ {
88+ // Arbitrum Sepolia - L2 with lower gas prices
89+ // Arbitrum min gas price floor is 0.01 gwei
90+ chainId : CHAIN_ID_ARBITRUM_SEPOLIA ,
91+ name : "Arbitrum Sepolia" ,
92+ dstPrice : 3000n * 10n ** QUOTE_DECIMALS , // ETH ~$3000
93+ gasPriceDecimals : 18 , // gas price stored in wei
94+ nativeDecimals : 18 ,
95+ dstGasPrice : 100_000_000n , // 0.1 gwei in wei
96+ baseFee : 500_000n , // 0.0005 SOL (lower for L2)
97+ } ,
98+ {
99+ // Base Sepolia - L2 with very low gas prices
100+ // Base typically has gas prices around 0.001-0.01 gwei
101+ chainId : CHAIN_ID_BASE_SEPOLIA ,
102+ name : "Base Sepolia" ,
103+ dstPrice : 3000n * 10n ** QUOTE_DECIMALS , // ETH ~$3000
104+ gasPriceDecimals : 18 , // gas price stored in wei
105+ nativeDecimals : 18 ,
106+ dstGasPrice : 10_000_000n , // 0.01 gwei in wei
107+ baseFee : 500_000n , // 0.0005 SOL
108+ } ,
109+ {
110+ // Optimism Sepolia - L2 with low gas prices
111+ // OP typically has gas prices around 0.001-0.05 gwei
112+ chainId : CHAIN_ID_OPTIMISM_SEPOLIA ,
113+ name : "Optimism Sepolia" ,
114+ dstPrice : 3000n * 10n ** QUOTE_DECIMALS , // ETH ~$3000
115+ gasPriceDecimals : 18 , // gas price stored in wei
116+ nativeDecimals : 18 ,
117+ dstGasPrice : 50_000_000n , // 0.05 gwei in wei
118+ baseFee : 500_000n , // 0.0005 SOL
119+ } ,
120+ ] ;
121+
55122// Test request parameters
56123const TEST_GAS_LIMIT = 100_000n ;
57124const TEST_MSG_VALUE = 10n ** 18n ; // 1 ETH in wei
@@ -578,6 +645,116 @@ describe("executor-quoter", () => {
578645 } ) ;
579646} ) ;
580647
648+ describe ( "executor-quoter testnet chains" , ( ) => {
649+ beforeAll ( async ( ) => {
650+ connection = new Connection ( "https://api.devnet.solana.com" , "confirmed" ) ;
651+ wallet = loadWallet ( ) ;
652+ [ quoterConfigPda ] = deriveQuoterConfigPda ( ) ;
653+ } ) ;
654+
655+ for ( const chain of TESTNET_CHAINS ) {
656+ test ( `updates chain info for ${ chain . name } (${ chain . chainId } )` , async ( ) => {
657+ const [ chainInfoPda ] = deriveQuoterChainInfoPda ( chain . chainId ) ;
658+
659+ const ix = new TransactionInstruction ( {
660+ programId : QUOTER_PROGRAM_ID ,
661+ keys : [
662+ { pubkey : wallet . publicKey , isSigner : true , isWritable : true } ,
663+ { pubkey : wallet . publicKey , isSigner : true , isWritable : false } ,
664+ { pubkey : chainInfoPda , isSigner : false , isWritable : true } ,
665+ { pubkey : SystemProgram . programId , isSigner : false , isWritable : false } ,
666+ ] ,
667+ data : buildUpdateChainInfoData (
668+ chain . chainId ,
669+ 1 , // enabled
670+ chain . gasPriceDecimals ,
671+ chain . nativeDecimals ,
672+ ) ,
673+ } ) ;
674+
675+ const tx = new Transaction ( ) . add ( ix ) ;
676+ await sendAndConfirmTransaction ( connection , tx , [ wallet ] ) ;
677+
678+ const accountInfo = await connection . getAccountInfo ( chainInfoPda ) ;
679+ expect ( accountInfo ) . not . toBeNull ( ) ;
680+ console . log ( ` Chain info PDA for ${ chain . name } : ${ chainInfoPda . toBase58 ( ) } ` ) ;
681+ } ) ;
682+
683+ test ( `updates quote for ${ chain . name } (${ chain . chainId } )` , async ( ) => {
684+ const [ quoteBodyPda ] = deriveQuoterQuoteBodyPda ( chain . chainId ) ;
685+
686+ const ix = new TransactionInstruction ( {
687+ programId : QUOTER_PROGRAM_ID ,
688+ keys : [
689+ { pubkey : wallet . publicKey , isSigner : true , isWritable : true } ,
690+ { pubkey : wallet . publicKey , isSigner : true , isWritable : false } ,
691+ { pubkey : quoteBodyPda , isSigner : false , isWritable : true } ,
692+ { pubkey : SystemProgram . programId , isSigner : false , isWritable : false } ,
693+ ] ,
694+ data : buildUpdateQuoteData (
695+ chain . chainId ,
696+ chain . dstPrice ,
697+ TEST_SRC_PRICE ,
698+ chain . dstGasPrice ,
699+ chain . baseFee
700+ ) ,
701+ } ) ;
702+
703+ const tx = new Transaction ( ) . add ( ix ) ;
704+ await sendAndConfirmTransaction ( connection , tx , [ wallet ] ) ;
705+
706+ const accountInfo = await connection . getAccountInfo ( quoteBodyPda ) ;
707+ expect ( accountInfo ) . not . toBeNull ( ) ;
708+ console . log ( ` Quote body PDA for ${ chain . name } : ${ quoteBodyPda . toBase58 ( ) } ` ) ;
709+ } ) ;
710+
711+ test ( `returns correct quote for ${ chain . name } (${ chain . chainId } )` , async ( ) => {
712+ const [ chainInfoPda ] = deriveQuoterChainInfoPda ( chain . chainId ) ;
713+ const [ quoteBodyPda ] = deriveQuoterQuoteBodyPda ( chain . chainId ) ;
714+
715+ const dstAddr = new Uint8Array ( 32 ) . fill ( 0xAB ) ;
716+ const refundAddr = new Uint8Array ( 32 ) ;
717+ wallet . publicKey . toBuffer ( ) . copy ( Buffer . from ( refundAddr ) ) ;
718+
719+ const ix = new TransactionInstruction ( {
720+ programId : QUOTER_PROGRAM_ID ,
721+ keys : [
722+ { pubkey : quoterConfigPda , isSigner : false , isWritable : false } ,
723+ { pubkey : chainInfoPda , isSigner : false , isWritable : false } ,
724+ { pubkey : quoteBodyPda , isSigner : false , isWritable : false } ,
725+ ] ,
726+ data : buildRequestQuoteData (
727+ chain . chainId ,
728+ dstAddr ,
729+ refundAddr ,
730+ new Uint8Array ( 0 ) ,
731+ buildGasRelayInstruction ( TEST_GAS_LIMIT , TEST_MSG_VALUE )
732+ ) ,
733+ } ) ;
734+
735+ const { returnData } = await simulateInstruction ( connection , wallet , ix ) ;
736+
737+ expect ( returnData . length ) . toBe ( 8 ) ;
738+ const payment = returnData . readBigUInt64BE ( 0 ) ;
739+
740+ // Calculate expected quote
741+ const expectedQuote = calculateExpectedQuote (
742+ chain . baseFee ,
743+ TEST_SRC_PRICE ,
744+ chain . dstPrice ,
745+ chain . dstGasPrice ,
746+ chain . gasPriceDecimals ,
747+ chain . nativeDecimals ,
748+ TEST_GAS_LIMIT ,
749+ TEST_MSG_VALUE
750+ ) ;
751+
752+ expect ( payment ) . toBe ( expectedQuote ) ;
753+ console . log ( ` Quote for ${ chain . name } : ${ payment } lamports (${ Number ( payment ) / 1e9 } SOL)` ) ;
754+ } ) ;
755+ }
756+ } ) ;
757+
581758describe ( "executor-quoter-router" , ( ) => {
582759 beforeAll ( async ( ) => {
583760 connection = new Connection ( "https://api.devnet.solana.com" , "confirmed" ) ;
0 commit comments