From 8c2906ac2acee05b88b50feda1458d6c5166ba48 Mon Sep 17 00:00:00 2001 From: Justin Starry Date: Thu, 15 Sep 2022 15:56:53 -0400 Subject: [PATCH 1/4] Add versioned tx sending components to example app --- .../src/components/SendLegacyTransaction.tsx | 54 +++++++++++++++ .../src/components/SendV0Transaction.tsx | 66 +++++++++++++++++++ packages/starter/example/src/pages/index.tsx | 13 ++++ 3 files changed, 133 insertions(+) create mode 100644 packages/starter/example/src/components/SendLegacyTransaction.tsx create mode 100644 packages/starter/example/src/components/SendV0Transaction.tsx diff --git a/packages/starter/example/src/components/SendLegacyTransaction.tsx b/packages/starter/example/src/components/SendLegacyTransaction.tsx new file mode 100644 index 000000000..b60b3c6d6 --- /dev/null +++ b/packages/starter/example/src/components/SendLegacyTransaction.tsx @@ -0,0 +1,54 @@ +import { Button } from '@mui/material'; +import { useConnection, useWallet } from '@solana/wallet-adapter-react'; +import { AddressLookupTableAccount, MessageV0, TransactionMessage, TransactionSignature, VersionedTransaction } from '@solana/web3.js'; +import { PublicKey, Transaction, TransactionInstruction } from '@solana/web3.js'; +import type { FC } from 'react'; +import React, { useCallback } from 'react'; +import { useNotify } from './notify'; + +export const SendLegacyTransaction: FC = () => { + const { connection } = useConnection(); + const { publicKey, sendTransaction } = useWallet(); + const notify = useNotify(); + + const onClick = useCallback(async () => { + if (!publicKey) { + notify('error', 'Wallet not connected!'); + return; + } + + let signature: TransactionSignature = ''; + try { + const { + context: { slot: minContextSlot }, + value: { blockhash, lastValidBlockHeight }, + } = await connection.getLatestBlockhashAndContext(); + + const message = new TransactionMessage({ + payerKey: publicKey, + instructions: [{ + data: Buffer.from('Hello, from the Solana Wallet Adapter example app!'), + keys: [], + programId: new PublicKey('MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'), + }], + recentBlockhash: blockhash, + }); + + const transaction = new VersionedTransaction(message.compileToLegacyMessage()); + signature = await sendTransaction(transaction, connection, { minContextSlot }); + notify('info', 'Transaction sent:', signature); + + await connection.confirmTransaction({ blockhash, lastValidBlockHeight, signature }); + notify('success', 'Transaction successful!', signature); + } catch (error: any) { + notify('error', `Transaction failed! ${error?.message}`, signature); + return; + } + }, [publicKey, notify, connection, sendTransaction]); + + return ( + + ); +}; diff --git a/packages/starter/example/src/components/SendV0Transaction.tsx b/packages/starter/example/src/components/SendV0Transaction.tsx new file mode 100644 index 000000000..7627a3d56 --- /dev/null +++ b/packages/starter/example/src/components/SendV0Transaction.tsx @@ -0,0 +1,66 @@ +import { Button } from '@mui/material'; +import { useConnection, useWallet } from '@solana/wallet-adapter-react'; +import { AddressLookupTableAccount, AddressLookupTableInstruction, AddressLookupTableProgram, MessageV0, TransactionMessage, TransactionSignature, VersionedTransaction } from '@solana/web3.js'; +import { PublicKey, Transaction, TransactionInstruction } from '@solana/web3.js'; +import type { FC } from 'react'; +import React, { useCallback } from 'react'; +import { useNotify } from './notify'; + +export const SendV0Transaction: FC = () => { + const { connection } = useConnection(); + const { publicKey, sendTransaction } = useWallet(); + const notify = useNotify(); + + const onClick = useCallback(async () => { + if (!publicKey) { + notify('error', 'Wallet not connected!'); + return; + } + + let signature: TransactionSignature = ''; + try { + const lookupTable = (await connection.getAddressLookupTable(new PublicKey("F3MfgEJe1TApJiA14nN2m4uAH4EBVrqdBnHeGeSXvQ7B"))).value; + if (lookupTable === null) { + notify('error', 'Address lookup table wasn\'t found!'); + return; + } + + const { + context: { slot: minContextSlot }, + value: { blockhash, lastValidBlockHeight }, + } = await connection.getLatestBlockhashAndContext(); + + const message = new TransactionMessage({ + payerKey: publicKey, + instructions: [{ + data: Buffer.from('Hello, from the Solana Wallet Adapter example app!'), + keys: lookupTable.state.addresses.map((pubkey, index) => ({ + pubkey, + isWritable: index % 2 == 0, + isSigner: false, + })), + programId: new PublicKey('MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'), + }], + recentBlockhash: blockhash, + }); + + const lookupTables: AddressLookupTableAccount[] = [lookupTable]; + const transaction = new VersionedTransaction(message.compileToV0Message(lookupTables)); + + signature = await sendTransaction(transaction, connection, { minContextSlot }); + notify('info', 'Transaction sent:', signature); + + await connection.confirmTransaction({ blockhash, lastValidBlockHeight, signature }); + notify('success', 'Transaction successful!', signature); + } catch (error: any) { + notify('error', `Transaction failed! ${error?.message}`, signature); + return; + } + }, [publicKey, notify, connection, sendTransaction]); + + return ( + + ); +}; diff --git a/packages/starter/example/src/pages/index.tsx b/packages/starter/example/src/pages/index.tsx index 78bb33d3f..89d8207a6 100644 --- a/packages/starter/example/src/pages/index.tsx +++ b/packages/starter/example/src/pages/index.tsx @@ -23,6 +23,8 @@ import pkg from '../../package.json'; import { useAutoConnect } from '../components/AutoConnectProvider'; import { RequestAirdrop } from '../components/RequestAirdrop'; import { SendTransaction } from '../components/SendTransaction'; +import { SendLegacyTransaction } from '../components/SendLegacyTransaction'; +import { SendV0Transaction } from '../components/SendV0Transaction'; import { SignMessage } from '../components/SignMessage'; const Index: NextPage = () => { @@ -119,6 +121,17 @@ const Index: NextPage = () => { + + + + + + + + + + + ); From 21728d2b9c911f9330c5f8e403ccf2799c29fcaf Mon Sep 17 00:00:00 2001 From: Justin Starry Date: Thu, 15 Sep 2022 17:05:44 -0400 Subject: [PATCH 2/4] Disable buttons if adapter doesn't support versioned tx --- .../src/components/SendLegacyTransaction.tsx | 16 ++++++++++----- .../src/components/SendV0Transaction.tsx | 20 ++++++++++++++----- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/packages/starter/example/src/components/SendLegacyTransaction.tsx b/packages/starter/example/src/components/SendLegacyTransaction.tsx index b60b3c6d6..d660c065c 100644 --- a/packages/starter/example/src/components/SendLegacyTransaction.tsx +++ b/packages/starter/example/src/components/SendLegacyTransaction.tsx @@ -1,15 +1,16 @@ import { Button } from '@mui/material'; import { useConnection, useWallet } from '@solana/wallet-adapter-react'; -import { AddressLookupTableAccount, MessageV0, TransactionMessage, TransactionSignature, VersionedTransaction } from '@solana/web3.js'; -import { PublicKey, Transaction, TransactionInstruction } from '@solana/web3.js'; +import { TransactionMessage, TransactionSignature, VersionedTransaction } from '@solana/web3.js'; +import { PublicKey } from '@solana/web3.js'; import type { FC } from 'react'; import React, { useCallback } from 'react'; import { useNotify } from './notify'; export const SendLegacyTransaction: FC = () => { const { connection } = useConnection(); - const { publicKey, sendTransaction } = useWallet(); + const { publicKey, sendTransaction, wallet } = useWallet(); const notify = useNotify(); + const supportedTransactionVersions = wallet?.adapter.supportedTransactionVersions; const onClick = useCallback(async () => { if (!publicKey) { @@ -17,6 +18,11 @@ export const SendLegacyTransaction: FC = () => { return; } + if (!supportedTransactionVersions) { + notify('error', 'Wallet doesn\'t support versioned transactions!'); + return; + } + let signature: TransactionSignature = ''; try { const { @@ -44,10 +50,10 @@ export const SendLegacyTransaction: FC = () => { notify('error', `Transaction failed! ${error?.message}`, signature); return; } - }, [publicKey, notify, connection, sendTransaction]); + }, [publicKey, notify, connection, sendTransaction, supportedTransactionVersions]); return ( - ); diff --git a/packages/starter/example/src/components/SendV0Transaction.tsx b/packages/starter/example/src/components/SendV0Transaction.tsx index 7627a3d56..1070dc19a 100644 --- a/packages/starter/example/src/components/SendV0Transaction.tsx +++ b/packages/starter/example/src/components/SendV0Transaction.tsx @@ -1,15 +1,16 @@ import { Button } from '@mui/material'; import { useConnection, useWallet } from '@solana/wallet-adapter-react'; -import { AddressLookupTableAccount, AddressLookupTableInstruction, AddressLookupTableProgram, MessageV0, TransactionMessage, TransactionSignature, VersionedTransaction } from '@solana/web3.js'; -import { PublicKey, Transaction, TransactionInstruction } from '@solana/web3.js'; +import { AddressLookupTableAccount, TransactionMessage, TransactionSignature, VersionedTransaction } from '@solana/web3.js'; +import { PublicKey } from '@solana/web3.js'; import type { FC } from 'react'; import React, { useCallback } from 'react'; import { useNotify } from './notify'; export const SendV0Transaction: FC = () => { const { connection } = useConnection(); - const { publicKey, sendTransaction } = useWallet(); + const { publicKey, sendTransaction, wallet } = useWallet(); const notify = useNotify(); + const supportedTransactionVersions = wallet?.adapter.supportedTransactionVersions; const onClick = useCallback(async () => { if (!publicKey) { @@ -17,6 +18,14 @@ export const SendV0Transaction: FC = () => { return; } + if (!supportedTransactionVersions) { + notify('error', 'Wallet doesn\'t support versioned transactions!'); + return; + } else if (!supportedTransactionVersions.has(0)) { + notify('error', 'Wallet doesn\'t support v0 transactions!'); + return; + } + let signature: TransactionSignature = ''; try { const lookupTable = (await connection.getAddressLookupTable(new PublicKey("F3MfgEJe1TApJiA14nN2m4uAH4EBVrqdBnHeGeSXvQ7B"))).value; @@ -56,10 +65,11 @@ export const SendV0Transaction: FC = () => { notify('error', `Transaction failed! ${error?.message}`, signature); return; } - }, [publicKey, notify, connection, sendTransaction]); + }, [publicKey, notify, connection, sendTransaction, supportedTransactionVersions]); + const disabled = !publicKey || !(supportedTransactionVersions && supportedTransactionVersions.has(0)); return ( - ); From 88af6ac01596db411cd739cd054f6629bac48d79 Mon Sep 17 00:00:00 2001 From: Justin Starry Date: Thu, 15 Sep 2022 17:08:41 -0400 Subject: [PATCH 3/4] Add comment on how to create custom lookup tables --- .../starter/example/src/components/SendV0Transaction.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/starter/example/src/components/SendV0Transaction.tsx b/packages/starter/example/src/components/SendV0Transaction.tsx index 1070dc19a..a950a3867 100644 --- a/packages/starter/example/src/components/SendV0Transaction.tsx +++ b/packages/starter/example/src/components/SendV0Transaction.tsx @@ -28,6 +28,11 @@ export const SendV0Transaction: FC = () => { let signature: TransactionSignature = ''; try { + /** + * This lookup table only exists on devnet and can be replaced as + * needed. To create and manage a lookup table, use the `solana + * address-lookup-table` commands. + */ const lookupTable = (await connection.getAddressLookupTable(new PublicKey("F3MfgEJe1TApJiA14nN2m4uAH4EBVrqdBnHeGeSXvQ7B"))).value; if (lookupTable === null) { notify('error', 'Address lookup table wasn\'t found!'); From 719c447d7ac5d1e789c5f9e08639131b4a577227 Mon Sep 17 00:00:00 2001 From: Jordan Sexton Date: Thu, 15 Sep 2022 17:38:00 -0500 Subject: [PATCH 4/4] lint / minor fixes --- .../src/components/SendLegacyTransaction.tsx | 28 +++++++---- .../src/components/SendV0Transaction.tsx | 46 +++++++++++-------- packages/starter/example/src/pages/index.tsx | 2 +- 3 files changed, 48 insertions(+), 28 deletions(-) diff --git a/packages/starter/example/src/components/SendLegacyTransaction.tsx b/packages/starter/example/src/components/SendLegacyTransaction.tsx index d660c065c..bb9fe915d 100644 --- a/packages/starter/example/src/components/SendLegacyTransaction.tsx +++ b/packages/starter/example/src/components/SendLegacyTransaction.tsx @@ -1,6 +1,7 @@ import { Button } from '@mui/material'; import { useConnection, useWallet } from '@solana/wallet-adapter-react'; -import { TransactionMessage, TransactionSignature, VersionedTransaction } from '@solana/web3.js'; +import type { TransactionSignature } from '@solana/web3.js'; +import { TransactionMessage, VersionedTransaction } from '@solana/web3.js'; import { PublicKey } from '@solana/web3.js'; import type { FC } from 'react'; import React, { useCallback } from 'react'; @@ -19,7 +20,10 @@ export const SendLegacyTransaction: FC = () => { } if (!supportedTransactionVersions) { - notify('error', 'Wallet doesn\'t support versioned transactions!'); + notify('error', "Wallet doesn't support versioned transactions!"); + return; + } else if (!supportedTransactionVersions.has('legacy')) { + notify('error', "Wallet doesn't support legacy transactions!"); return; } @@ -32,15 +36,18 @@ export const SendLegacyTransaction: FC = () => { const message = new TransactionMessage({ payerKey: publicKey, - instructions: [{ - data: Buffer.from('Hello, from the Solana Wallet Adapter example app!'), - keys: [], - programId: new PublicKey('MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'), - }], + instructions: [ + { + data: Buffer.from('Hello, from the Solana Wallet Adapter example app!'), + keys: [], + programId: new PublicKey('MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'), + }, + ], recentBlockhash: blockhash, }); const transaction = new VersionedTransaction(message.compileToLegacyMessage()); + signature = await sendTransaction(transaction, connection, { minContextSlot }); notify('info', 'Transaction sent:', signature); @@ -53,7 +60,12 @@ export const SendLegacyTransaction: FC = () => { }, [publicKey, notify, connection, sendTransaction, supportedTransactionVersions]); return ( - ); diff --git a/packages/starter/example/src/components/SendV0Transaction.tsx b/packages/starter/example/src/components/SendV0Transaction.tsx index a950a3867..1cb33ef10 100644 --- a/packages/starter/example/src/components/SendV0Transaction.tsx +++ b/packages/starter/example/src/components/SendV0Transaction.tsx @@ -1,7 +1,7 @@ import { Button } from '@mui/material'; import { useConnection, useWallet } from '@solana/wallet-adapter-react'; -import { AddressLookupTableAccount, TransactionMessage, TransactionSignature, VersionedTransaction } from '@solana/web3.js'; -import { PublicKey } from '@solana/web3.js'; +import type { TransactionSignature } from '@solana/web3.js'; +import { PublicKey, TransactionMessage, VersionedTransaction } from '@solana/web3.js'; import type { FC } from 'react'; import React, { useCallback } from 'react'; import { useNotify } from './notify'; @@ -19,10 +19,10 @@ export const SendV0Transaction: FC = () => { } if (!supportedTransactionVersions) { - notify('error', 'Wallet doesn\'t support versioned transactions!'); + notify('error', "Wallet doesn't support versioned transactions!"); return; } else if (!supportedTransactionVersions.has(0)) { - notify('error', 'Wallet doesn\'t support v0 transactions!'); + notify('error', "Wallet doesn't support v0 transactions!"); return; } @@ -33,9 +33,11 @@ export const SendV0Transaction: FC = () => { * needed. To create and manage a lookup table, use the `solana * address-lookup-table` commands. */ - const lookupTable = (await connection.getAddressLookupTable(new PublicKey("F3MfgEJe1TApJiA14nN2m4uAH4EBVrqdBnHeGeSXvQ7B"))).value; - if (lookupTable === null) { - notify('error', 'Address lookup table wasn\'t found!'); + const { value: lookupTable } = await connection.getAddressLookupTable( + new PublicKey('F3MfgEJe1TApJiA14nN2m4uAH4EBVrqdBnHeGeSXvQ7B') + ); + if (!lookupTable) { + notify('error', "Address lookup table wasn't found!"); return; } @@ -46,19 +48,21 @@ export const SendV0Transaction: FC = () => { const message = new TransactionMessage({ payerKey: publicKey, - instructions: [{ - data: Buffer.from('Hello, from the Solana Wallet Adapter example app!'), - keys: lookupTable.state.addresses.map((pubkey, index) => ({ - pubkey, - isWritable: index % 2 == 0, - isSigner: false, - })), - programId: new PublicKey('MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'), - }], + instructions: [ + { + data: Buffer.from('Hello, from the Solana Wallet Adapter example app!'), + keys: lookupTable.state.addresses.map((pubkey, index) => ({ + pubkey, + isWritable: index % 2 == 0, + isSigner: false, + })), + programId: new PublicKey('MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'), + }, + ], recentBlockhash: blockhash, }); - const lookupTables: AddressLookupTableAccount[] = [lookupTable]; + const lookupTables = [lookupTable]; const transaction = new VersionedTransaction(message.compileToV0Message(lookupTables)); signature = await sendTransaction(transaction, connection, { minContextSlot }); @@ -72,9 +76,13 @@ export const SendV0Transaction: FC = () => { } }, [publicKey, notify, connection, sendTransaction, supportedTransactionVersions]); - const disabled = !publicKey || !(supportedTransactionVersions && supportedTransactionVersions.has(0)); return ( - ); diff --git a/packages/starter/example/src/pages/index.tsx b/packages/starter/example/src/pages/index.tsx index 89d8207a6..249b5ae2e 100644 --- a/packages/starter/example/src/pages/index.tsx +++ b/packages/starter/example/src/pages/index.tsx @@ -122,6 +122,7 @@ const Index: NextPage = () => { + @@ -130,7 +131,6 @@ const Index: NextPage = () => { -