diff --git a/advanced/wallets/react-wallet-v2/src/data/SolanaData.ts b/advanced/wallets/react-wallet-v2/src/data/SolanaData.ts index 0f3718d41..e83e0801c 100644 --- a/advanced/wallets/react-wallet-v2/src/data/SolanaData.ts +++ b/advanced/wallets/react-wallet-v2/src/data/SolanaData.ts @@ -21,7 +21,7 @@ export const SOLANA_MAINNET_CHAINS = { name: 'Solana', logo: '/chain-logos/solana-5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp.png', rgb: '30, 240, 166', - rpc: '', + rpc: 'https://api.mainnet-beta.solana.com', namespace: 'solana' } } @@ -41,7 +41,7 @@ export const SOLANA_TEST_CHAINS = { name: 'Solana Devnet', logo: '/chain-logos/solana-5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp.png', rgb: '30, 240, 166', - rpc: '', + rpc: 'https://api.devnet.solana.com', namespace: 'solana' }, 'solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z': { @@ -49,7 +49,7 @@ export const SOLANA_TEST_CHAINS = { name: 'Solana Testnet', logo: '/chain-logos/solana-5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp.png', rgb: '30, 240, 166', - rpc: '', + rpc: 'https://api.testnet.solana.com', namespace: 'solana' } } @@ -61,5 +61,6 @@ export const SOLANA_CHAINS = { ...SOLANA_MAINNET_CHAINS, ...SOLANA_TEST_CHAINS } */ export const SOLANA_SIGNING_METHODS = { SOLANA_SIGN_TRANSACTION: 'solana_signTransaction', - SOLANA_SIGN_MESSAGE: 'solana_signMessage' + SOLANA_SIGN_MESSAGE: 'solana_signMessage', + SOLANA_SIGN_AND_SEND_TRANSACTION: 'solana_signAndSendTransaction' } diff --git a/advanced/wallets/react-wallet-v2/src/hooks/useWalletConnectEventsManager.ts b/advanced/wallets/react-wallet-v2/src/hooks/useWalletConnectEventsManager.ts index dd939fed5..e961b39bc 100644 --- a/advanced/wallets/react-wallet-v2/src/hooks/useWalletConnectEventsManager.ts +++ b/advanced/wallets/react-wallet-v2/src/hooks/useWalletConnectEventsManager.ts @@ -106,6 +106,7 @@ export default function useWalletConnectEventsManager(initialized: boolean) { case SOLANA_SIGNING_METHODS.SOLANA_SIGN_MESSAGE: case SOLANA_SIGNING_METHODS.SOLANA_SIGN_TRANSACTION: + case SOLANA_SIGNING_METHODS.SOLANA_SIGN_AND_SEND_TRANSACTION: return ModalStore.open('SessionSignSolanaModal', { requestEvent, requestSession }) case POLKADOT_SIGNING_METHODS.POLKADOT_SIGN_MESSAGE: diff --git a/advanced/wallets/react-wallet-v2/src/lib/SolanaLib.ts b/advanced/wallets/react-wallet-v2/src/lib/SolanaLib.ts index d06ff5add..8abc3fd7a 100644 --- a/advanced/wallets/react-wallet-v2/src/lib/SolanaLib.ts +++ b/advanced/wallets/react-wallet-v2/src/lib/SolanaLib.ts @@ -1,7 +1,15 @@ -import { Keypair } from '@solana/web3.js' +import { + Keypair, + Connection, + Transaction, + TransactionInstruction, + PublicKey, + SendOptions +} from '@solana/web3.js' import bs58 from 'bs58' import nacl from 'tweetnacl' import SolanaWallet, { SolanaSignTransaction } from 'solana-wallet' +import { SOLANA_MAINNET_CHAINS, SOLANA_TEST_CHAINS } from '@/data/SolanaData' /** * Types @@ -58,4 +66,52 @@ export default class SolanaLib { return { signature } } + + public async signAndSendTransaction( + feePayer: SolanaSignTransaction['feePayer'], + instructions: SolanaSignTransaction['instructions'], + chainId: string, + options: SendOptions = {} + ) { + const rpc = { ...SOLANA_TEST_CHAINS, ...SOLANA_MAINNET_CHAINS }[chainId]?.rpc + + if (!rpc) { + throw new Error('There is no RPC URL for the provided chain') + } + + const connection = new Connection(rpc) + + const parsedInstructions = instructions.map(instruction => { + const keys = instruction.keys.map(key => ({ + pubkey: new PublicKey(key.pubkey), + isSigner: key.isSigner, + isWritable: key.isWritable + })) + const programId = new PublicKey(instruction.programId) + const data = + typeof instruction.data === 'string' + ? Buffer.from(bs58.decode(instruction.data).buffer) + : instruction.data + + return new TransactionInstruction({ + keys, + programId, + data + }) + }) + + const transaction = new Transaction().add(...parsedInstructions) + transaction.feePayer = new PublicKey(feePayer) + transaction.recentBlockhash = (await connection.getLatestBlockhash()).blockhash + transaction.sign(this.keypair) + + const signature = await connection.sendRawTransaction(transaction.serialize()) + const confirmation = await connection.confirmTransaction(signature, options.preflightCommitment) + + if (confirmation.value.err) { + throw new Error(confirmation.value.err.toString()) + } + + return { signature } + } } diff --git a/advanced/wallets/react-wallet-v2/src/utils/SolanaRequestHandlerUtil.ts b/advanced/wallets/react-wallet-v2/src/utils/SolanaRequestHandlerUtil.ts index 1a74eb2b4..152a3b6f7 100644 --- a/advanced/wallets/react-wallet-v2/src/utils/SolanaRequestHandlerUtil.ts +++ b/advanced/wallets/react-wallet-v2/src/utils/SolanaRequestHandlerUtil.ts @@ -9,7 +9,7 @@ export async function approveSolanaRequest( requestEvent: SignClientTypes.EventArguments['session_request'] ) { const { params, id } = requestEvent - const { request } = params + const { request, chainId } = params const wallet = solanaWallets[getWalletAddressFromParams(solanaAddresses, params)] switch (request.method) { @@ -26,6 +26,16 @@ export async function approveSolanaRequest( return formatJsonRpcResult(id, signedTransaction) + case SOLANA_SIGNING_METHODS.SOLANA_SIGN_AND_SEND_TRANSACTION: + const signedAndSentTransaction = await wallet.signAndSendTransaction( + request.params.feePayer, + request.params.instructions, + chainId, + request.params.options + ) + + return formatJsonRpcResult(id, signedAndSentTransaction) + default: throw new Error(getSdkError('INVALID_METHOD').message) }