1010import { createLogger } from '@solana/example-utils/createLogger.js' ;
1111import pressAnyKeyPrompt from '@solana/example-utils/pressAnyKeyPrompt.js' ;
1212import {
13+ address ,
1314 Address ,
1415 airdropFactory ,
1516 appendTransactionMessageInstruction ,
1617 appendTransactionMessageInstructions ,
1718 assertIsTransactionWithBlockhashLifetime ,
1819 BlockhashLifetimeConstraint ,
20+ ClusterUrl ,
1921 createSolanaRpc ,
2022 createSolanaRpcSubscriptions ,
2123 createTransactionMessage ,
2224 createTransactionPlanExecutor ,
2325 createTransactionPlanner ,
26+ devnet ,
2427 generateKeyPairSigner ,
2528 getSignatureFromTransaction ,
2629 Instruction ,
2730 InstructionPlan ,
2831 lamports ,
2932 Lamports ,
33+ mainnet ,
34+ MainnetUrl ,
3035 parallelInstructionPlan ,
3136 pipe ,
37+ Rpc ,
38+ RpcSubscriptions ,
3239 sendAndConfirmTransactionFactory ,
3340 sequentialInstructionPlan ,
3441 setTransactionMessageFeePayer ,
@@ -38,6 +45,7 @@ import {
3845 singleTransactionPlan ,
3946 SolanaError ,
4047 SolanaRpcApi ,
48+ SolanaRpcApiFromClusterUrl ,
4149 SolanaRpcSubscriptionsApi ,
4250 TransactionMessage ,
4351 TransactionMessageWithBlockhashLifetime ,
@@ -118,20 +126,28 @@ function summarizeTransactionPlanResult(result: TransactionPlanResult): CompactS
118126 return transactionResults ;
119127}
120128
121- type Client < TRpcApi = SolanaRpcApi , TRpcSubscriptionsApi = SolanaRpcSubscriptionsApi > = {
122- rpc : TRpcApi ;
123- rpcSubscriptions : TRpcSubscriptionsApi ;
124- airdrop ( recipientAddress : Address , amount : number | bigint | Lamports ) : Promise < Signature > ;
129+ type HideFromMainnet < TClusterUrl extends ClusterUrl , T > = TClusterUrl extends MainnetUrl ? never : T ;
130+
131+ type SolanaClient <
132+ TClusterUrl extends ClusterUrl ,
133+ > = {
134+ rpc : Rpc < SolanaRpcApiFromClusterUrl < TClusterUrl > > ;
135+ rpcSubscriptions : RpcSubscriptions < SolanaRpcSubscriptionsApi > ;
136+ airdrop : HideFromMainnet < TClusterUrl , ( recipientAddress : Address , amount : number | bigint | Lamports ) => Promise < Signature > > ;
125137 transaction ( config : TransactionConfig ) : TransactionBuilder ;
126138 transactionPlan ( config : TransactionConfig ) : TransactionPlanBuilder ;
127139 sendAndConfirm ( transaction : SendableTransactionMessage | TransactionPlan , abortSignal ?: AbortSignal ) : Promise < CompactSingleTransactionSummary [ ] > ;
128140}
129141
130- function createSolanaClient ( endpoint : string ) : Client < ReturnType < typeof createSolanaRpc > , ReturnType < typeof createSolanaRpcSubscriptions > > {
142+ function createSolanaClient < TClusterUrl extends ClusterUrl > ( endpoint : TClusterUrl ) : SolanaClient < TClusterUrl > {
131143 const rpc = createSolanaRpc ( endpoint ) ;
144+ // Typescript doesn't know the cluster URL, so internally we cast to the base SolanaRpcApi
145+ // We return `rpc` which is cluster-aware though
146+ const internalRpc = rpc as Rpc < SolanaRpcApi > ;
132147 const rpcSubscriptions = createSolanaRpcSubscriptions ( endpoint . replace ( 'http' , 'ws' ) . replace ( '8899' , '8900' ) ) ;
133148
134- const airdrop = airdropFactory ( { rpc, rpcSubscriptions } ) ;
149+ // We hide this from mainnet in the `SolanaClient` type
150+ const airdrop = airdropFactory ( { rpc : internalRpc , rpcSubscriptions } ) ;
135151
136152 async function createBaseTransactionMessage ( config : TransactionConfig , abortSignal ?: AbortSignal ) : Promise < SendableTransactionMessage > {
137153 return pipe (
@@ -148,7 +164,7 @@ function createSolanaClient(endpoint: string): Client<ReturnType<typeof createSo
148164 if ( config . blockhash ) {
149165 return setTransactionMessageLifetimeUsingBlockhash ( config . blockhash , tx ) ;
150166 } else {
151- const { value : latestBlockhash } = await rpc . getLatestBlockhash ( { commitment : 'confirmed' } ) . send ( { abortSignal } ) ;
167+ const { value : latestBlockhash } = await internalRpc . getLatestBlockhash ( { commitment : 'confirmed' } ) . send ( { abortSignal } ) ;
152168 return setTransactionMessageLifetimeUsingBlockhash ( latestBlockhash , tx ) ;
153169 }
154170 }
@@ -189,8 +205,8 @@ function createSolanaClient(endpoint: string): Client<ReturnType<typeof createSo
189205 }
190206 }
191207
192- const sendAndConfirm = sendAndConfirmTransactionFactory ( { rpc, rpcSubscriptions } ) ;
193- const estimateCULimit = estimateComputeUnitLimitFactory ( { rpc } ) ;
208+ const sendAndConfirm = sendAndConfirmTransactionFactory ( { rpc : internalRpc , rpcSubscriptions } ) ;
209+ const estimateCULimit = estimateComputeUnitLimitFactory ( { rpc : internalRpc } ) ;
194210 async function estimateWithMultiplier ( ...args : Parameters < typeof estimateCULimit > ) : Promise < number > {
195211 const estimate = await estimateCULimit ( ...args ) ;
196212 return Math . ceil ( estimate * 1.1 ) ;
@@ -208,17 +224,19 @@ function createSolanaClient(endpoint: string): Client<ReturnType<typeof createSo
208224 }
209225 } )
210226
227+ async function airdropHelper ( recipientAddress : Address , amount : number | bigint | Lamports ) : Promise < Signature > {
228+ const lamportsAmount = typeof amount === 'number' ? lamports ( BigInt ( amount ) ) : typeof amount === 'bigint' ? lamports ( amount ) : amount ;
229+ return await airdrop ( {
230+ commitment : 'confirmed' ,
231+ recipientAddress,
232+ lamports : lamportsAmount ,
233+ } ) ;
234+ }
235+
211236 return {
212237 rpc,
213238 rpcSubscriptions,
214- async airdrop ( recipientAddress : Address , amount : number | bigint | Lamports ) : Promise < Signature > {
215- const lamportsAmount = typeof amount === 'number' ? lamports ( BigInt ( amount ) ) : typeof amount === 'bigint' ? lamports ( amount ) : amount ;
216- return await airdrop ( {
217- commitment : 'confirmed' ,
218- recipientAddress,
219- lamports : lamportsAmount ,
220- } ) ;
221- } ,
239+ airdrop : airdropHelper as HideFromMainnet < TClusterUrl , typeof airdropHelper > ,
222240 transaction ( config : TransactionConfig ) : TransactionBuilder {
223241 return transactionBuilder ( config ) ;
224242 } ,
@@ -253,7 +271,6 @@ function displayAmount(amount: bigint, decimals: number): string {
253271const client = createSolanaClient ( 'http://127.0.0.1:8899' ) ;
254272
255273// Example: Airdrop SOL to a new signer
256-
257274const signer = await generateKeyPairSigner ( ) ;
258275await client . airdrop ( signer . address , sol ( 1.5 ) ) ;
259276log . info ( 'Airdropped 1.5 SOL to the new signer address' ) ;
@@ -315,6 +332,7 @@ async function createMintExample() {
315332}
316333await createMintExample ( ) ;
317334
335+ // Example: Airdrop tokens to multiple recipients
318336async function tokenAirdropExample ( ) {
319337 const tokenMint = await generateKeyPairSigner ( ) ;
320338
@@ -383,3 +401,24 @@ async function tokenAirdropExample() {
383401 await pressAnyKeyPrompt ( 'Press any key to quit' ) ;
384402}
385403await tokenAirdropExample ( ) ;
404+
405+ // Additional typetests
406+ async ( ) => {
407+ const client = createSolanaClient ( '' ) ;
408+ await client . rpc . requestAirdrop ( address ( 'a' ) , lamports ( 1n ) ) . send ( ) ;
409+ await client . airdrop ( address ( 'a' ) , 1 ) ;
410+ const customSendConfirm = sendAndConfirmTransactionFactory ( { rpc : client . rpc , rpcSubscriptions : client . rpcSubscriptions } ) ;
411+
412+ const devnetClient = createSolanaClient ( devnet ( '' ) ) ;
413+ await devnetClient . rpc . requestAirdrop ( address ( 'a' ) , lamports ( 1n ) ) . send ( ) ;
414+ await devnetClient . airdrop ( address ( 'a' ) , 1 ) ;
415+ const devnetCustomSendConfirm = sendAndConfirmTransactionFactory ( { rpc : devnetClient . rpc , rpcSubscriptions : devnetClient . rpcSubscriptions } ) ;
416+
417+ const mainnetClient = createSolanaClient ( mainnet ( '' ) ) ;
418+ await mainnetClient . rpc . getBalance ( address ( 'a' ) ) . send ( ) ;
419+ // @ts -expect-error requestAirdrop should be unavailable on mainnet rpc
420+ await mainnetClient . rpc . requestAirdrop ( address ( 'a' ) , lamports ( 1n ) ) . send ( ) ;
421+ // @ts -expect-error airdrop should be unavailable on mainnet client
422+ await mainnetClient . airdrop ( address ( 'a' ) , 1 ) ; // should error
423+ const mainnetCustomSendConfirm = sendAndConfirmTransactionFactory ( { rpc : mainnetClient . rpc , rpcSubscriptions : mainnetClient . rpcSubscriptions } ) ;
424+ }
0 commit comments