|
3 | 3 | import { |
4 | 4 | LegacySolTransfer, |
5 | 5 | ModernSolTransfer, |
| 6 | + ModernWalletTransfer, |
| 7 | + TitanSwap, |
| 8 | + titanSwapCode, |
6 | 9 | KitSignerDemo, |
7 | 10 | ChainUtilitiesDemo, |
8 | 11 | ConnectionAbstractionDemo, |
@@ -257,6 +260,148 @@ export function ModernSolTransfer() { |
257 | 260 | }`, |
258 | 261 | render: () => <ModernSolTransfer />, |
259 | 262 | }, |
| 263 | + { |
| 264 | + id: 'modern-wallet-transfer', |
| 265 | + name: 'Modern Wallet Transfer', |
| 266 | + description: 'Transfer 1 lamport to another wallet using @solana/kit with a kit-compatible signer.', |
| 267 | + fileName: 'components/transactions/modern-wallet-transfer.tsx', |
| 268 | + code: `'use client'; |
| 269 | +
|
| 270 | +import { useCallback, useMemo } from 'react'; |
| 271 | +import { |
| 272 | + createSolanaRpc, |
| 273 | + pipe, |
| 274 | + createTransactionMessage, |
| 275 | + setTransactionMessageFeePayerSigner, |
| 276 | + setTransactionMessageLifetimeUsingBlockhash, |
| 277 | + appendTransactionMessageInstructions, |
| 278 | + sendAndConfirmTransactionFactory, |
| 279 | + signTransactionMessageWithSigners, |
| 280 | + createSolanaRpcSubscriptions, |
| 281 | + lamports, |
| 282 | + assertIsTransactionWithBlockhashLifetime, |
| 283 | + signature as createSignature, |
| 284 | + address, |
| 285 | + type TransactionSigner, |
| 286 | +} from '@solana/kit'; |
| 287 | +import { getTransferSolInstruction } from '@solana-program/system'; |
| 288 | +import { useKitTransactionSigner, useCluster, useConnectorClient } from '@solana/connector'; |
| 289 | +import { PipelineHeaderButton, PipelineVisualization } from '@/components/pipeline'; |
| 290 | +import { VisualPipeline } from '@/lib/visual-pipeline'; |
| 291 | +import { useExampleCardHeaderActions } from '@/components/playground/example-card-actions'; |
| 292 | +import { |
| 293 | + getBase58SignatureFromSignedTransaction, |
| 294 | + getBase64EncodedWireTransaction, |
| 295 | + getWebSocketUrlForRpcUrl, |
| 296 | + isRpcProxyUrl, |
| 297 | + waitForSignatureConfirmation, |
| 298 | +} from './rpc-utils'; |
| 299 | +
|
| 300 | +// Destination wallet address |
| 301 | +const DESTINATION_ADDRESS = address('A7Xmq3qqt4uvw3GELHw9HHNFbwZzHDJNtmk6fe2p5b5s'); |
| 302 | +
|
| 303 | +export function ModernWalletTransfer() { |
| 304 | + const { signer, ready } = useKitTransactionSigner(); |
| 305 | + const { cluster } = useCluster(); |
| 306 | + const client = useConnectorClient(); |
| 307 | +
|
| 308 | + const visualPipeline = useMemo( |
| 309 | + () => |
| 310 | + new VisualPipeline('modern-wallet-transfer', [ |
| 311 | + { name: 'Build instruction', type: 'instruction' }, |
| 312 | + { name: 'Transfer SOL', type: 'transaction' }, |
| 313 | + ]), |
| 314 | + [], |
| 315 | + ); |
| 316 | +
|
| 317 | + const getExplorerUrl = useCallback( |
| 318 | + (sig: string) => { |
| 319 | + const clusterSlug = cluster?.id?.replace('solana:', ''); |
| 320 | + if (!clusterSlug || clusterSlug === 'mainnet' || clusterSlug === 'mainnet-beta') { |
| 321 | + return 'https://explorer.solana.com/tx/' + sig; |
| 322 | + } |
| 323 | + return 'https://explorer.solana.com/tx/' + sig + '?cluster=' + clusterSlug; |
| 324 | + }, |
| 325 | + [cluster?.id], |
| 326 | + ); |
| 327 | +
|
| 328 | + const executeWalletTransfer = useCallback(async () => { |
| 329 | + if (!signer || !client) return; |
| 330 | +
|
| 331 | + const rpcUrl = client.getRpcUrl(); |
| 332 | + if (!rpcUrl) throw new Error('No RPC endpoint configured'); |
| 333 | + const rpc = createSolanaRpc(rpcUrl); |
| 334 | +
|
| 335 | + let signatureBase58: string | null = null; |
| 336 | +
|
| 337 | + await visualPipeline.execute(async () => { |
| 338 | + visualPipeline.setStepState('Build instruction', { type: 'building' }); |
| 339 | + visualPipeline.setStepState('Transfer SOL', { type: 'building' }); |
| 340 | +
|
| 341 | + const { value: latestBlockhash } = await rpc.getLatestBlockhash().send(); |
| 342 | + |
| 343 | + // Transfer to another wallet instead of self |
| 344 | + const transferInstruction = getTransferSolInstruction({ |
| 345 | + source: signer as TransactionSigner, |
| 346 | + destination: DESTINATION_ADDRESS, |
| 347 | + amount: lamports(1n), |
| 348 | + }); |
| 349 | +
|
| 350 | + const transactionMessage = pipe( |
| 351 | + createTransactionMessage({ version: 0 }), |
| 352 | + tx => setTransactionMessageFeePayerSigner(signer, tx), |
| 353 | + tx => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx), |
| 354 | + tx => appendTransactionMessageInstructions([transferInstruction], tx), |
| 355 | + ); |
| 356 | +
|
| 357 | + visualPipeline.setStepState('Transfer SOL', { type: 'signing' }); |
| 358 | +
|
| 359 | + const signedTransaction = await signTransactionMessageWithSigners(transactionMessage); |
| 360 | + signatureBase58 = getBase58SignatureFromSignedTransaction(signedTransaction); |
| 361 | +
|
| 362 | + visualPipeline.setStepState('Build instruction', { type: 'confirmed', signature: signatureBase58, cost: 0 }); |
| 363 | + visualPipeline.setStepState('Transfer SOL', { type: 'sending' }); |
| 364 | +
|
| 365 | + assertIsTransactionWithBlockhashLifetime(signedTransaction); |
| 366 | +
|
| 367 | + if (isRpcProxyUrl(rpcUrl)) { |
| 368 | + const encodedTransaction = getBase64EncodedWireTransaction(signedTransaction); |
| 369 | + await rpc.sendTransaction(encodedTransaction, { encoding: 'base64' }).send(); |
| 370 | + await waitForSignatureConfirmation({ |
| 371 | + signature: signatureBase58, |
| 372 | + commitment: 'confirmed', |
| 373 | + getSignatureStatuses: async sig => |
| 374 | + await rpc.getSignatureStatuses([createSignature(sig)]).send(), |
| 375 | + }); |
| 376 | + } else { |
| 377 | + const rpcSubscriptions = createSolanaRpcSubscriptions(getWebSocketUrlForRpcUrl(rpcUrl)); |
| 378 | + await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signedTransaction, { |
| 379 | + commitment: 'confirmed', |
| 380 | + }); |
| 381 | + } |
| 382 | +
|
| 383 | + visualPipeline.setStepState('Transfer SOL', { type: 'confirmed', signature: signatureBase58, cost: 0.000005 }); |
| 384 | + }); |
| 385 | + }, [client, signer, visualPipeline]); |
| 386 | +
|
| 387 | + useExampleCardHeaderActions( |
| 388 | + <PipelineHeaderButton visualPipeline={visualPipeline} disabled={!ready || !client} onExecute={executeWalletTransfer} />, |
| 389 | + ); |
| 390 | +
|
| 391 | + return ( |
| 392 | + <PipelineVisualization visualPipeline={visualPipeline} strategy="sequential" getExplorerUrl={getExplorerUrl} /> |
| 393 | + ); |
| 394 | +}`, |
| 395 | + render: () => <ModernWalletTransfer />, |
| 396 | + }, |
| 397 | + { |
| 398 | + id: 'titan-swap', |
| 399 | + name: 'Titan Swap (SOL → USDC)', |
| 400 | + description: 'Swap 0.01 SOL for USDC using Titan InstructionPlans and track the transaction(s) in Connector Devtools.', |
| 401 | + fileName: 'components/transactions/titan-swap.tsx', |
| 402 | + code: titanSwapCode, |
| 403 | + render: () => <TitanSwap />, |
| 404 | + }, |
260 | 405 | { |
261 | 406 | id: 'kit-signer', |
262 | 407 | name: 'Kit Signers', |
|
0 commit comments