From 94cb465ef5aaa98763ae5088976c93604d48db96 Mon Sep 17 00:00:00 2001 From: Anvit Mangal Date: Mon, 22 Jan 2024 14:17:18 +0530 Subject: [PATCH 1/7] signAndSendAllTransactions for phantom adapter --- packages/core/base/package.json | 2 +- packages/core/base/src/adapter.ts | 28 +++--- packages/core/react/package.json | 2 +- packages/starter/example/package.json | 4 +- packages/wallets/phantom/src/adapter.ts | 41 ++++++++ packages/wallets/unsafe-burner/package.json | 4 +- pnpm-lock.yaml | 102 ++++++++++---------- 7 files changed, 110 insertions(+), 73 deletions(-) diff --git a/packages/core/base/package.json b/packages/core/base/package.json index 3e8b8667d..c0a6d7678 100644 --- a/packages/core/base/package.json +++ b/packages/core/base/package.json @@ -35,7 +35,7 @@ "@solana/web3.js": "^1.77.3" }, "dependencies": { - "@solana/wallet-standard-features": "^1.1.0", + "@solana/wallet-standard-features": "^1.2.0", "@wallet-standard/base": "^1.0.1", "@wallet-standard/features": "^1.0.3", "eventemitter3": "^4.0.7" diff --git a/packages/core/base/src/adapter.ts b/packages/core/base/src/adapter.ts index 37ae98308..501502936 100644 --- a/packages/core/base/src/adapter.ts +++ b/packages/core/base/src/adapter.ts @@ -18,23 +18,22 @@ export interface SignAndSendTransactionOptions extends SendOptions { export interface SendOptions { minContextSlot?: number; - /** @deprecated Wallets are not expected to support this option. */ + /** @deprecated Wallets are not expected to support this option. */ skipPreflight?: boolean; - /** @deprecated Wallets are not expected to support this option. */ + /** @deprecated Wallets are not expected to support this option. */ preflightCommitment?: Commitment; - /** @deprecated Wallets are not expected to support this option. */ + /** @deprecated Wallets are not expected to support this option. */ maxRetries?: number; + /** Mode for signing and sending transactions. */ + mode?: SolanaSignAndSendTransactionMode; } +/** Mode for signing and sending transactions. */ +export type SolanaSignAndSendTransactionMode = 'parallel' | 'serial'; + /** @deprecated Use `SignAndSendTransactionOptions` instead. */ export type SendTransactionOptions = SignAndSendTransactionOptions; -export interface SignAndSendAllTransactionsError { - type: string; - code: number; - message: string; -} - // WalletName is a nominal type that wallet adapters should use, e.g. `'MyCryptoWallet' as WalletName<'MyCryptoWallet'>` // https://medium.com/@KevinBGreene/surviving-the-typescript-ecosystem-branding-and-type-tagging-6cf6e516523d export type WalletName = T & { __brand__: 'WalletName' }; @@ -61,7 +60,7 @@ export interface WalletAdapterProps { transactions: TransactionOrVersionedTransaction[], connection: Connection, options?: SignAndSendTransactionOptions - ): Promise<(TransactionSignature | SignAndSendAllTransactionsError)[]>; + ): Promise<(TransactionSignature | undefined)[]>; /** @deprecated Use `signAndSendTransaction` instead. */ sendTransaction( transaction: TransactionOrVersionedTransaction, @@ -134,17 +133,12 @@ export abstract class BaseWalletAdapter transactions: TransactionOrVersionedTransaction[], connection: Connection, options?: SignAndSendTransactionOptions | undefined - ): Promise<(TransactionSignature | SignAndSendAllTransactionsError)[]> { + ): Promise<(TransactionSignature | undefined)[]> { const results = await Promise.allSettled( transactions.map((transaction) => this.signAndSendTransaction(transaction, connection, options)) ); return results.map((result) => { - if (result.status === 'fulfilled') return result.value; - return { - type: result.reason.type || result.reason.name, - code: result.reason.code, - message: result.reason.message, - }; + return result.status === 'fulfilled' ? result.value : undefined; }); } diff --git a/packages/core/react/package.json b/packages/core/react/package.json index 2f5d118e9..e3de069a4 100644 --- a/packages/core/react/package.json +++ b/packages/core/react/package.json @@ -39,7 +39,7 @@ "dependencies": { "@solana-mobile/wallet-adapter-mobile": "^2.0.0", "@solana/wallet-adapter-base": "workspace:^", - "@solana/wallet-standard-wallet-adapter-react": "^1.1.0" + "@solana/wallet-standard-wallet-adapter-react": "^1.1.2" }, "devDependencies": { "@solana/web3.js": "^1.77.3", diff --git a/packages/starter/example/package.json b/packages/starter/example/package.json index adeb9275d..0c0aa314b 100644 --- a/packages/starter/example/package.json +++ b/packages/starter/example/package.json @@ -46,8 +46,8 @@ "@solana/wallet-adapter-react": "workspace:^", "@solana/wallet-adapter-react-ui": "workspace:^", "@solana/wallet-adapter-wallets": "workspace:^", - "@solana/wallet-standard-features": "^1.1.0", - "@solana/wallet-standard-util": "^1.1.0", + "@solana/wallet-standard-features": "^1.2.0", + "@solana/wallet-standard-util": "^1.1.1", "@solana/web3.js": "^1.77.3", "antd": "^4.24.10", "bs58": "^4.0.1", diff --git a/packages/wallets/phantom/src/adapter.ts b/packages/wallets/phantom/src/adapter.ts index a973ba5ca..6dedc2626 100644 --- a/packages/wallets/phantom/src/adapter.ts +++ b/packages/wallets/phantom/src/adapter.ts @@ -43,6 +43,10 @@ interface PhantomWallet extends EventEmitter { transaction: T, options?: SendOptions ): Promise<{ signature: TransactionSignature }>; + signAndSendAllTransactions( + transactions: T[], + options?: SendOptions + ): Promise<{ signatures: TransactionSignature[] }>; signMessage(message: Uint8Array): Promise<{ signature: Uint8Array }>; connect(): Promise; disconnect(): Promise; @@ -224,6 +228,43 @@ export class PhantomWalletAdapter extends BaseMessageSignerWalletAdapter { } } + async signAndSendAllTransactions( + transactions: T[], + connection: Connection, + options: SignAndSendTransactionOptions = {} + ): Promise { + try { + const wallet = this._wallet; + if (!wallet) throw new WalletNotConnectedError(); + + try { + const { signers, ...sendOptions } = options; + + transactions = await Promise.all( + transactions.map(async (transaction) => { + if (isVersionedTransaction(transaction)) { + signers?.length && transaction.sign(signers); + } else { + transaction = (await this.prepareTransaction(transaction, connection, sendOptions)) as T; + signers?.length && (transaction as Transaction).partialSign(...signers); + } + return transaction; + }) + ); + sendOptions.preflightCommitment = sendOptions.preflightCommitment || connection.commitment; + + const { signatures } = await wallet.signAndSendAllTransactions(transactions, sendOptions); + return signatures; + } catch (error: any) { + if (error instanceof WalletError) throw error; + throw new WalletSendTransactionError(error?.message, error); + } + } catch (error: any) { + this.emit('error', error); + throw error; + } + } + async signTransaction(transaction: T): Promise { try { const wallet = this._wallet; diff --git a/packages/wallets/unsafe-burner/package.json b/packages/wallets/unsafe-burner/package.json index 1792e3ec1..2c4e5c1f1 100644 --- a/packages/wallets/unsafe-burner/package.json +++ b/packages/wallets/unsafe-burner/package.json @@ -37,8 +37,8 @@ "dependencies": { "@noble/curves": "^1.1.0", "@solana/wallet-adapter-base": "workspace:^", - "@solana/wallet-standard-features": "^1.1.0", - "@solana/wallet-standard-util": "^1.1.0" + "@solana/wallet-standard-features": "^1.2.0", + "@solana/wallet-standard-util": "^1.1.1" }, "devDependencies": { "@solana/web3.js": "^1.77.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index df6494fbc..c88670787 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -70,8 +70,8 @@ importers: packages/core/base: dependencies: '@solana/wallet-standard-features': - specifier: ^1.1.0 - version: 1.1.0 + specifier: ^1.2.0 + version: 1.2.0 '@wallet-standard/base': specifier: ^1.0.1 version: 1.0.1 @@ -101,8 +101,8 @@ importers: specifier: workspace:^ version: link:../base '@solana/wallet-standard-wallet-adapter-react': - specifier: ^1.1.0 - version: 1.1.0(@solana/wallet-adapter-base@packages+core+base)(@solana/web3.js@1.78.0)(bs58@4.0.1)(react@18.2.0) + specifier: ^1.1.2 + version: 1.1.2(@solana/wallet-adapter-base@packages+core+base)(@solana/web3.js@1.78.0)(bs58@4.0.1)(react@18.2.0) devDependencies: '@solana/web3.js': specifier: ^1.77.3 @@ -265,11 +265,11 @@ importers: specifier: workspace:^ version: link:../../wallets/wallets '@solana/wallet-standard-features': - specifier: ^1.1.0 - version: 1.1.0 + specifier: ^1.2.0 + version: 1.2.0 '@solana/wallet-standard-util': - specifier: ^1.1.0 - version: 1.1.0 + specifier: ^1.1.1 + version: 1.1.1 '@solana/web3.js': specifier: ^1.77.3 version: 1.78.0 @@ -1122,11 +1122,11 @@ importers: specifier: workspace:^ version: link:../../core/base '@solana/wallet-standard-features': - specifier: ^1.1.0 - version: 1.1.0 + specifier: ^1.2.0 + version: 1.2.0 '@solana/wallet-standard-util': - specifier: ^1.1.0 - version: 1.1.0 + specifier: ^1.1.1 + version: 1.1.1 devDependencies: '@solana/web3.js': specifier: ^1.77.3 @@ -1398,7 +1398,7 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0} peerDependencies: '@babel/core': '>=7.11.0' - eslint: ^7.5.0 || ^8.0.0 + eslint: 8.22.0 dependencies: '@babel/core': 7.22.9 '@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1 @@ -3342,7 +3342,7 @@ packages: resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + eslint: 8.22.0 dependencies: eslint: 8.22.0 eslint-visitor-keys: 3.4.1 @@ -5841,25 +5841,25 @@ packages: '@wallet-standard/base': 1.0.1 dev: false - /@solana/wallet-standard-features@1.1.0: - resolution: {integrity: sha512-oVyygxfYkkF5INYL0GuD8GFmNO/wd45zNesIqGCFE6X66BYxmI6HmyzQJCcZTZ0BNsezlVg4t+3MCL5AhfFoGA==} + /@solana/wallet-standard-features@1.2.0: + resolution: {integrity: sha512-tUd9srDLkRpe1BYg7we+c4UhRQkq+XQWswsr/L1xfGmoRDF47BPSXf4zE7ZU2GRBGvxtGt7lwJVAufQyQYhxTQ==} engines: {node: '>=16'} dependencies: '@wallet-standard/base': 1.0.1 '@wallet-standard/features': 1.0.3 dev: false - /@solana/wallet-standard-util@1.1.0: - resolution: {integrity: sha512-vssoCIx43sY5EMrT1pVltsZZKPAQfKpPG3ib2fuqRqpTRGkeRFCPDf4lrVFAYYp238tFr3Xrr/3JLcGvPP7uYw==} + /@solana/wallet-standard-util@1.1.1: + resolution: {integrity: sha512-dPObl4ntmfOc0VAGGyyFvrqhL8UkHXmVsgbj0K9RcznKV4KB3MgjGwzo8CTSX5El5lkb0rDeEzFqvToJXRz3dw==} engines: {node: '>=16'} dependencies: '@noble/curves': 1.1.0 '@solana/wallet-standard-chains': 1.1.0 - '@solana/wallet-standard-features': 1.1.0 + '@solana/wallet-standard-features': 1.2.0 dev: false - /@solana/wallet-standard-wallet-adapter-base@1.1.0(@solana/web3.js@1.78.0)(bs58@4.0.1): - resolution: {integrity: sha512-wCPiQChIsTB3SlQ18QiPiAcmnqXcX1FRf3ylxpo9LNJ+cOD6vBcrAC4UK/P7sYww1RJM+bHTxvUTweeNtQ/7Pg==} + /@solana/wallet-standard-wallet-adapter-base@1.1.2(@solana/web3.js@1.78.0)(bs58@4.0.1): + resolution: {integrity: sha512-DqhzYbgh3disHMgcz6Du7fmpG29BYVapNEEiL+JoVMa+bU9d4P1wfwXUNyJyRpGGNXtwhyZjIk2umWbe5ZBNaQ==} engines: {node: '>=16'} peerDependencies: '@solana/web3.js': ^1.58.0 @@ -5867,8 +5867,8 @@ packages: dependencies: '@solana/wallet-adapter-base': link:packages/core/base '@solana/wallet-standard-chains': 1.1.0 - '@solana/wallet-standard-features': 1.1.0 - '@solana/wallet-standard-util': 1.1.0 + '@solana/wallet-standard-features': 1.2.0 + '@solana/wallet-standard-util': 1.1.1 '@solana/web3.js': 1.78.0 '@wallet-standard/app': 1.0.1 '@wallet-standard/base': 1.0.1 @@ -5877,15 +5877,15 @@ packages: bs58: 4.0.1 dev: false - /@solana/wallet-standard-wallet-adapter-react@1.1.0(@solana/wallet-adapter-base@packages+core+base)(@solana/web3.js@1.78.0)(bs58@4.0.1)(react@18.2.0): - resolution: {integrity: sha512-bkCPtOHWMXsCpqudoOQ2BSMAJQAcPrgAApDROGI4zpPIM1GI8WA7QslS9MJgSvkWKIRIUdf1r6YnpVSwT6c8sw==} + /@solana/wallet-standard-wallet-adapter-react@1.1.2(@solana/wallet-adapter-base@packages+core+base)(@solana/web3.js@1.78.0)(bs58@4.0.1)(react@18.2.0): + resolution: {integrity: sha512-bN6W4QkzenyjUoUz3sC5PAed+z29icGtPh9VSmLl1ZrRO7NbFB49a8uwUUVXNxhL/ZbMsyVKhb9bNj47/p8uhQ==} engines: {node: '>=16'} peerDependencies: - '@solana/wallet-adapter-base': '*' + '@solana/wallet-adapter-base': workspace:^ react: '*' dependencies: '@solana/wallet-adapter-base': link:packages/core/base - '@solana/wallet-standard-wallet-adapter-base': 1.1.0(@solana/web3.js@1.78.0)(bs58@4.0.1) + '@solana/wallet-standard-wallet-adapter-base': 1.1.2(@solana/web3.js@1.78.0)(bs58@4.0.1) '@wallet-standard/app': 1.0.1 '@wallet-standard/base': 1.0.1 react: 18.2.0 @@ -5923,7 +5923,7 @@ packages: peerDependencies: '@solana/web3.js': '*' dependencies: - '@solana/wallet-standard-features': 1.1.0 + '@solana/wallet-standard-features': 1.2.0 '@solana/web3.js': 1.78.0 '@wallet-standard/base': 1.0.1 bs58: 5.0.0 @@ -6919,7 +6919,7 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: '@typescript-eslint/parser': ^5.0.0 - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + eslint: 8.22.0 typescript: '*' peerDependenciesMeta: typescript: @@ -6945,7 +6945,7 @@ packages: resolution: {integrity: sha512-RTXpeB3eMkpoclG3ZHft6vG/Z30azNHuqY6wKPBHlVMZFuEvrtlEDe8gMqDb+SO+9hjC/pLekeSCryf9vMZlCw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + eslint: 8.22.0 dependencies: '@typescript-eslint/utils': 5.62.0(eslint@8.22.0)(typescript@4.7.4) eslint: 8.22.0 @@ -6958,7 +6958,7 @@ packages: resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + eslint: 8.22.0 typescript: '*' peerDependenciesMeta: typescript: @@ -6984,7 +6984,7 @@ packages: resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: - eslint: '*' + eslint: 8.22.0 typescript: '*' peerDependenciesMeta: typescript: @@ -7027,7 +7027,7 @@ packages: resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + eslint: 8.22.0 dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.22.0) '@types/json-schema': 7.0.12 @@ -9923,7 +9923,7 @@ packages: /eslint-config-next@12.3.4(eslint@8.22.0)(typescript@4.7.4): resolution: {integrity: sha512-WuT3gvgi7Bwz00AOmKGhOeqnyA5P29Cdyr0iVjLyfDbk+FANQKcOjFUTZIdyYfe5Tq1x4TGcmoe4CwctGvFjHQ==} peerDependencies: - eslint: ^7.23.0 || ^8.0.0 + eslint: 8.22.0 typescript: '>=3.3.1' peerDependenciesMeta: typescript: @@ -9949,7 +9949,7 @@ packages: resolution: {integrity: sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==} hasBin: true peerDependencies: - eslint: '>=7.0.0' + eslint: 8.22.0 dependencies: eslint: 8.22.0 dev: true @@ -9958,7 +9958,7 @@ packages: resolution: {integrity: sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==} engines: {node: '>=14.0.0'} peerDependencies: - eslint: ^8.0.0 + eslint: 8.22.0 typescript: '*' peerDependenciesMeta: typescript: @@ -10002,7 +10002,7 @@ packages: resolution: {integrity: sha512-00UbgGwV8bSgUv34igBDbTOtKhqoRMy9bFjNehT40bXg6585PNIct8HhXZ0SybqB9rWtXj9crcku8ndDn/gIqQ==} engines: {node: '>=4'} peerDependencies: - eslint: '*' + eslint: 8.22.0 eslint-plugin-import: '*' dependencies: debug: 4.3.4 @@ -10050,7 +10050,7 @@ packages: peerDependencies: '@babel/plugin-syntax-flow': ^7.14.5 '@babel/plugin-transform-react-jsx': ^7.14.9 - eslint: ^8.1.0 + eslint: 8.22.0 dependencies: '@babel/plugin-syntax-flow': 7.22.5(@babel/core@7.22.9) '@babel/plugin-transform-react-jsx': 7.22.5(@babel/core@7.22.9) @@ -10064,7 +10064,7 @@ packages: engines: {node: '>=4'} peerDependencies: '@typescript-eslint/parser': '*' - eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + eslint: 8.22.0 peerDependenciesMeta: '@typescript-eslint/parser': optional: true @@ -10096,7 +10096,7 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} peerDependencies: '@typescript-eslint/eslint-plugin': ^4.0.0 || ^5.0.0 - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + eslint: 8.22.0 jest: '*' peerDependenciesMeta: '@typescript-eslint/eslint-plugin': @@ -10117,7 +10117,7 @@ packages: resolution: {integrity: sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==} engines: {node: '>=4.0'} peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + eslint: 8.22.0 dependencies: '@babel/runtime': 7.22.6 aria-query: 5.3.0 @@ -10141,7 +10141,7 @@ packages: resolution: {integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==} engines: {node: '>=12.0.0'} peerDependencies: - eslint: '>=7.28.0' + eslint: 8.22.0 eslint-config-prettier: '*' prettier: '>=2.0.0' peerDependenciesMeta: @@ -10158,7 +10158,7 @@ packages: resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} engines: {node: '>=10'} peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + eslint: 8.22.0 dependencies: eslint: 8.22.0 @@ -10166,7 +10166,7 @@ packages: resolution: {integrity: sha512-qewL/8P34WkY8jAqdQxsiL82pDUeT7nhs8IsuXgfgnsEloKCT4miAV9N9kGtx7/KM9NH/NCGUE7Edt9iGxLXFw==} engines: {node: '>=4'} peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + eslint: 8.22.0 dependencies: array-includes: 3.1.6 array.prototype.flatmap: 1.3.1 @@ -10189,7 +10189,7 @@ packages: resolution: {integrity: sha512-T3c1PZ9PIdI3hjV8LdunfYI8gj017UQjzAnCrxuo3wAjneDbTPHdE3oNWInOjMA+z/aBkUtlW5vC0YepYMZIug==} engines: {node: '>=16'} peerDependencies: - eslint: '*' + eslint: 8.22.0 dependencies: eslint: 8.22.0 dev: true @@ -10198,7 +10198,7 @@ packages: resolution: {integrity: sha512-ELY7Gefo+61OfXKlQeXNIDVVLPcvKTeiQOoMZG9TeuWa7Ln4dUNRv8JdRWBQI9Mbb427XGlVB1aa1QPZxBJM8Q==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0, npm: '>=6'} peerDependencies: - eslint: ^7.5.0 || ^8.0.0 + eslint: 8.22.0 dependencies: '@typescript-eslint/utils': 5.62.0(eslint@8.22.0)(typescript@4.7.4) eslint: 8.22.0 @@ -10225,7 +10225,7 @@ packages: resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} peerDependencies: - eslint: '>=5' + eslint: 8.22.0 dependencies: eslint: 8.22.0 eslint-visitor-keys: 2.1.0 @@ -10242,7 +10242,7 @@ packages: resolution: {integrity: sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w==} engines: {node: '>= 12.13.0'} peerDependencies: - eslint: ^7.0.0 || ^8.0.0 + eslint: 8.22.0 webpack: ^5.0.0 dependencies: '@types/eslint': 8.44.0 @@ -10715,7 +10715,7 @@ packages: resolution: {integrity: sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==} engines: {node: '>=10', yarn: '>=1.0.0'} peerDependencies: - eslint: '>= 6' + eslint: 8.22.0 typescript: '>= 2.7' vue-template-compiler: '*' webpack: '>= 4' @@ -14210,6 +14210,7 @@ packages: /node-gyp-build-optional-packages@5.0.7: resolution: {integrity: sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w==} hasBin: true + requiresBuild: true dev: true optional: true @@ -15727,6 +15728,7 @@ packages: /prr@1.0.1: resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} + requiresBuild: true dev: true optional: true @@ -16580,7 +16582,7 @@ packages: engines: {node: '>=14.0.0'} hasBin: true peerDependencies: - eslint: '*' + eslint: 8.22.0 react: '>= 16' typescript: ^3.2.1 || ^4 peerDependenciesMeta: From 0f1d065cff9e413dc94dfa39cd8c87323a5f9e5f Mon Sep 17 00:00:00 2001 From: Anvit Mangal Date: Fri, 2 Feb 2024 14:31:39 +0530 Subject: [PATCH 2/7] unsafe burner implementation --- FAQ.md | 2 +- packages/core/base/src/adapter.ts | 24 +++--- packages/core/base/src/errors.ts | 20 +++++ packages/core/base/src/signer.ts | 16 ++++ .../core/react/src/WalletProviderBase.tsx | 15 ++-- packages/core/react/src/useWallet.ts | 2 +- .../components/SignAndSendAllTransactions.tsx | 61 ++++++++++++++ ...n.tsx => SignAndSendLegacyTransaction.tsx} | 4 +- ...saction.tsx => SignAndSendTransaction.tsx} | 4 +- ...ction.tsx => SignAndSendV0Transaction.tsx} | 4 +- packages/starter/example/src/pages/index.tsx | 29 ++++--- packages/wallets/phantom/src/adapter.ts | 41 ---------- packages/wallets/unsafe-burner/src/adapter.ts | 79 ++++++++++++++++++- 13 files changed, 221 insertions(+), 80 deletions(-) create mode 100644 packages/starter/example/src/components/SignAndSendAllTransactions.tsx rename packages/starter/example/src/components/{SendLegacyTransaction.tsx => SignAndSendLegacyTransaction.tsx} (95%) rename packages/starter/example/src/components/{SendTransaction.tsx => SignAndSendTransaction.tsx} (95%) rename packages/starter/example/src/components/{SendV0Transaction.tsx => SignAndSendV0Transaction.tsx} (96%) diff --git a/FAQ.md b/FAQ.md index 4756581d1..231e69023 100644 --- a/FAQ.md +++ b/FAQ.md @@ -108,7 +108,7 @@ This shouldn't happen if you're using one of the starter projects, since they se ### `[...] is not a function` -This can happen if you try to use `signTransaction`, `signAllTransactions`, or `signMessage` without checking if they are defined first. +This can happen if you try to use `signTransaction`, `signAllTransactions`, `signAndSendAllTransactions`, `signMessage`, or `signIn` without checking if they are defined first. `sendTransaction` is the primary method that all wallets support, and it signs transactions. The other methods are optional APIs, so you have to feature-detect them before using them. diff --git a/packages/core/base/src/adapter.ts b/packages/core/base/src/adapter.ts index 501502936..907aa985d 100644 --- a/packages/core/base/src/adapter.ts +++ b/packages/core/base/src/adapter.ts @@ -1,7 +1,9 @@ import type { Commitment, Connection, PublicKey, Signer, Transaction, TransactionSignature } from '@solana/web3.js'; import EventEmitter from 'eventemitter3'; +import type { WalletSignAndSendAllTransactionsError } from './errors.js'; import { WalletNotConnectedError, type WalletError } from './errors.js'; import type { SupportedTransactionVersions, TransactionOrVersionedTransaction } from './transaction.js'; +import type { SolanaSignAndSendTransactionMode } from '@solana/wallet-standard-features'; export { EventEmitter }; @@ -28,12 +30,15 @@ export interface SendOptions { mode?: SolanaSignAndSendTransactionMode; } -/** Mode for signing and sending transactions. */ -export type SolanaSignAndSendTransactionMode = 'parallel' | 'serial'; - /** @deprecated Use `SignAndSendTransactionOptions` instead. */ export type SendTransactionOptions = SignAndSendTransactionOptions; +export interface SignAndSendAllTransactionsError { + type: string; + code: number; + message: string; +} + // WalletName is a nominal type that wallet adapters should use, e.g. `'MyCryptoWallet' as WalletName<'MyCryptoWallet'>` // https://medium.com/@KevinBGreene/surviving-the-typescript-ecosystem-branding-and-type-tagging-6cf6e516523d export type WalletName = T & { __brand__: 'WalletName' }; @@ -60,7 +65,7 @@ export interface WalletAdapterProps { transactions: TransactionOrVersionedTransaction[], connection: Connection, options?: SignAndSendTransactionOptions - ): Promise<(TransactionSignature | undefined)[]>; + ): Promise<(TransactionSignature | WalletSignAndSendAllTransactionsError)[]>; /** @deprecated Use `signAndSendTransaction` instead. */ sendTransaction( transaction: TransactionOrVersionedTransaction, @@ -129,18 +134,11 @@ export abstract class BaseWalletAdapter options?: SignAndSendTransactionOptions ): Promise; - async signAndSendAllTransactions( + abstract signAndSendAllTransactions( transactions: TransactionOrVersionedTransaction[], connection: Connection, options?: SignAndSendTransactionOptions | undefined - ): Promise<(TransactionSignature | undefined)[]> { - const results = await Promise.allSettled( - transactions.map((transaction) => this.signAndSendTransaction(transaction, connection, options)) - ); - return results.map((result) => { - return result.status === 'fulfilled' ? result.value : undefined; - }); - } + ): Promise<(TransactionSignature | WalletSignAndSendAllTransactionsError)[]>; /** @deprecated Use `signAndSendTransaction` instead. */ sendTransaction( diff --git a/packages/core/base/src/errors.ts b/packages/core/base/src/errors.ts index d6c3e4c70..e0e831704 100644 --- a/packages/core/base/src/errors.ts +++ b/packages/core/base/src/errors.ts @@ -75,3 +75,23 @@ export class WalletWindowBlockedError extends WalletError { export class WalletWindowClosedError extends WalletError { name = 'WalletWindowClosedError'; } + +export class WalletSignAndSendAllTransactionsError extends WalletError { + name = 'WalletSignAndSendAllTransactionsError'; + + // Holds the original error object or details + error: any; + + // Type of the error (if available) + type?: string; + + // Error code (if available) + code?: number; + + constructor(error: any) { + super(error?.reason?.message, error); + this.error = error; + this.type = error.type || error.name; + this.code = error.code; + } +} diff --git a/packages/core/base/src/signer.ts b/packages/core/base/src/signer.ts index 9c526af3a..a9da988db 100644 --- a/packages/core/base/src/signer.ts +++ b/packages/core/base/src/signer.ts @@ -1,11 +1,13 @@ import type { SolanaSignInInput, SolanaSignInOutput } from '@solana/wallet-standard-features'; import type { Connection, TransactionSignature } from '@solana/web3.js'; +import type { SignAndSendAllTransactionsError } from './adapter.js'; import { BaseWalletAdapter, type SignAndSendTransactionOptions, type WalletAdapter, type WalletAdapterProps, } from './adapter.js'; +import { WalletSignAndSendAllTransactionsError } from './errors.js'; import { WalletSendTransactionError, WalletSignTransactionError } from './errors.js'; import { isVersionedTransaction, type TransactionOrVersionedTransaction } from './transaction.js'; @@ -24,6 +26,20 @@ export abstract class BaseSignerWalletAdapter extends BaseWalletAdapter implements SignerWalletAdapter { + async signAndSendAllTransactions( + transactions: TransactionOrVersionedTransaction[], + connection: Connection, + options: SignAndSendTransactionOptions = {} + ): Promise<(TransactionSignature | WalletSignAndSendAllTransactionsError)[]> { + const results = await Promise.allSettled( + transactions.map((transaction) => this.signAndSendTransaction(transaction, connection, options)) + ); + return results.map((result) => { + if (result.status === 'fulfilled') return result.value; + return new WalletSignAndSendAllTransactionsError(result.reason); + }); + } + async signAndSendTransaction( transaction: TransactionOrVersionedTransaction, connection: Connection, diff --git a/packages/core/react/src/WalletProviderBase.tsx b/packages/core/react/src/WalletProviderBase.tsx index 243172ba7..5f4a79e65 100644 --- a/packages/core/react/src/WalletProviderBase.tsx +++ b/packages/core/react/src/WalletProviderBase.tsx @@ -205,12 +205,15 @@ export function WalletProviderBase({ ); // Sign and send multiple transactions using the provided connection - const signAndSendAllTransactions: WalletAdapterProps['signAndSendAllTransactions'] = useCallback( - async (transactions, connection, options) => { - if (!adapter) throw handleErrorRef.current(new WalletNotSelectedError()); - if (!connected) throw handleErrorRef.current(new WalletNotConnectedError(), adapter); - return await adapter.signAndSendAllTransactions(transactions, connection, options); - }, + const signAndSendAllTransactions: WalletAdapterProps['signAndSendAllTransactions'] | undefined = useMemo( + () => + adapter && 'signAndSendTransactions' in adapter + ? async (transactions, connection, options) => { + if (!adapter) throw handleErrorRef.current(new WalletNotSelectedError()); + if (!connected) throw handleErrorRef.current(new WalletNotConnectedError(), adapter); + return await adapter.signAndSendAllTransactions(transactions, connection, options); + } + : undefined, [adapter, connected] ); diff --git a/packages/core/react/src/useWallet.ts b/packages/core/react/src/useWallet.ts index 86a6cfb29..d80fa724d 100644 --- a/packages/core/react/src/useWallet.ts +++ b/packages/core/react/src/useWallet.ts @@ -29,7 +29,7 @@ export interface WalletContextState { disconnect(): Promise; signAndSendTransaction: WalletAdapterProps['signAndSendTransaction']; - signAndSendAllTransactions: WalletAdapterProps['signAndSendAllTransactions']; + signAndSendAllTransactions: WalletAdapterProps['signAndSendAllTransactions'] | undefined; signTransaction: SignerWalletAdapterProps['signTransaction'] | undefined; signAllTransactions: SignerWalletAdapterProps['signAllTransactions'] | undefined; signMessage: MessageSignerWalletAdapterProps['signMessage'] | undefined; diff --git a/packages/starter/example/src/components/SignAndSendAllTransactions.tsx b/packages/starter/example/src/components/SignAndSendAllTransactions.tsx new file mode 100644 index 000000000..36d2d94bf --- /dev/null +++ b/packages/starter/example/src/components/SignAndSendAllTransactions.tsx @@ -0,0 +1,61 @@ +import { Button } from '@mui/material'; +import { useConnection, useWallet } from '@solana/wallet-adapter-react'; +import type { TransactionSignature } 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'; +import { WalletSignAndSendAllTransactionsError } from '@solana/wallet-adapter-base'; + +export const SignAndSendAllTransactions: FC = () => { + const { connection } = useConnection(); + const { publicKey, signAndSendAllTransactions } = useWallet(); + const notify = useNotify(); + + const onClick = useCallback(async () => { + try { + if (!publicKey) throw new Error('Wallet not connected!'); + + const { + context: { slot: minContextSlot }, + value: { blockhash, lastValidBlockHeight }, + } = await connection.getLatestBlockhashAndContext(); + + const transactions = []; + for (let i = 0; i < 5; i++) { + const transaction = new Transaction({ + feePayer: publicKey, + recentBlockhash: blockhash, + }).add( + new TransactionInstruction({ + data: Buffer.from('Hello, from the Solana Wallet Adapter example app!'), + keys: [], + programId: new PublicKey('MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'), + }) + ); + transactions.push(transaction); + } + + const results = await signAndSendAllTransactions(transactions, connection, { minContextSlot }); + + // Iterate through the results + results.forEach((result, index) => { + if (result instanceof WalletSignAndSendAllTransactionsError) { + // If the result is an instance of WalletSignAndSendAllTransactionsError, notify about the error + notify('error', `Transaction failed: ${result.message}`); + } else { + // If the result is a signature, notify about the successful transaction + notify('info', 'Transaction sent:', result); + } + }); + } catch (error: any) { + notify('error', `Transactions failed! ${error?.message}`); + } + }, [publicKey, connection, signAndSendAllTransactions, notify]); + + return ( + + ); +}; diff --git a/packages/starter/example/src/components/SendLegacyTransaction.tsx b/packages/starter/example/src/components/SignAndSendLegacyTransaction.tsx similarity index 95% rename from packages/starter/example/src/components/SendLegacyTransaction.tsx rename to packages/starter/example/src/components/SignAndSendLegacyTransaction.tsx index b860509d0..e97d33637 100644 --- a/packages/starter/example/src/components/SendLegacyTransaction.tsx +++ b/packages/starter/example/src/components/SignAndSendLegacyTransaction.tsx @@ -6,7 +6,7 @@ import type { FC } from 'react'; import React, { useCallback } from 'react'; import { useNotify } from './notify'; -export const SendLegacyTransaction: FC = () => { +export const SignAndSendLegacyTransaction: FC = () => { const { connection } = useConnection(); const { publicKey, signAndSendTransaction, wallet } = useWallet(); const notify = useNotify(); @@ -55,7 +55,7 @@ export const SendLegacyTransaction: FC = () => { onClick={onClick} disabled={!publicKey || !supportedTransactionVersions?.has('legacy')} > - Send Legacy Transaction (devnet) + Sign and Send Legacy Transaction (devnet) ); }; diff --git a/packages/starter/example/src/components/SendTransaction.tsx b/packages/starter/example/src/components/SignAndSendTransaction.tsx similarity index 95% rename from packages/starter/example/src/components/SendTransaction.tsx rename to packages/starter/example/src/components/SignAndSendTransaction.tsx index bc7557a92..fd4e5a571 100644 --- a/packages/starter/example/src/components/SendTransaction.tsx +++ b/packages/starter/example/src/components/SignAndSendTransaction.tsx @@ -6,7 +6,7 @@ import type { FC } from 'react'; import React, { useCallback } from 'react'; import { useNotify } from './notify'; -export const SendTransaction: FC = () => { +export const SignAndSendTransaction: FC = () => { const { connection } = useConnection(); const { publicKey, signAndSendTransaction } = useWallet(); const notify = useNotify(); @@ -44,7 +44,7 @@ export const SendTransaction: FC = () => { return ( ); }; diff --git a/packages/starter/example/src/components/SendV0Transaction.tsx b/packages/starter/example/src/components/SignAndSendV0Transaction.tsx similarity index 96% rename from packages/starter/example/src/components/SendV0Transaction.tsx rename to packages/starter/example/src/components/SignAndSendV0Transaction.tsx index 7c1451f31..5e2195191 100644 --- a/packages/starter/example/src/components/SendV0Transaction.tsx +++ b/packages/starter/example/src/components/SignAndSendV0Transaction.tsx @@ -6,7 +6,7 @@ import type { FC } from 'react'; import React, { useCallback } from 'react'; import { useNotify } from './notify'; -export const SendV0Transaction: FC = () => { +export const SignAndSendV0Transaction: FC = () => { const { connection } = useConnection(); const { publicKey, signAndSendTransaction, wallet } = useWallet(); const notify = useNotify(); @@ -68,7 +68,7 @@ export const SendV0Transaction: FC = () => { onClick={onClick} disabled={!publicKey || !supportedTransactionVersions?.has(0)} > - Send V0 Transaction using Address Lookup Table (devnet) + Sign and Send V0 Transaction using Address Lookup Table (devnet) ); }; diff --git a/packages/starter/example/src/pages/index.tsx b/packages/starter/example/src/pages/index.tsx index d5f7d8ff5..e9fbc970c 100644 --- a/packages/starter/example/src/pages/index.tsx +++ b/packages/starter/example/src/pages/index.tsx @@ -59,15 +59,24 @@ const ReactUIWalletModalButtonDynamic = dynamic( const RequestAirdropDynamic = dynamic(async () => (await import('../components/RequestAirdrop')).RequestAirdrop, { ssr: false, }); -const SendLegacyTransactionDynamic = dynamic( - async () => (await import('../components/SendLegacyTransaction')).SendLegacyTransaction, +const SignAndSendLegacyTransactionDynamic = dynamic( + async () => (await import('../components/SignAndSendLegacyTransaction')).SignAndSendLegacyTransaction, { ssr: false } ); -const SendTransactionDynamic = dynamic(async () => (await import('../components/SendTransaction')).SendTransaction, { - ssr: false, -}); -const SendV0TransactionDynamic = dynamic( - async () => (await import('../components/SendV0Transaction')).SendV0Transaction, +const SignAndSendTransactionDynamic = dynamic( + async () => (await import('../components/SignAndSendTransaction')).SignAndSendTransaction, + { + ssr: false, + } +); +const SignAndSendAllTransactionsDynamic = dynamic( + async () => (await import('../components/SignAndSendAllTransactions')).SignAndSendAllTransactions, + { + ssr: false, + } +); +const SignAndSendV0TransactionDynamic = dynamic( + async () => (await import('../components/SignAndSendV0Transaction')).SignAndSendV0Transaction, { ssr: false } ); const SignInDynamic = dynamic(async () => (await import('../components/SignIn')).SignIn, { ssr: false }); @@ -191,13 +200,13 @@ const Index: NextPage = () => { - + - + - + diff --git a/packages/wallets/phantom/src/adapter.ts b/packages/wallets/phantom/src/adapter.ts index 6dedc2626..a973ba5ca 100644 --- a/packages/wallets/phantom/src/adapter.ts +++ b/packages/wallets/phantom/src/adapter.ts @@ -43,10 +43,6 @@ interface PhantomWallet extends EventEmitter { transaction: T, options?: SendOptions ): Promise<{ signature: TransactionSignature }>; - signAndSendAllTransactions( - transactions: T[], - options?: SendOptions - ): Promise<{ signatures: TransactionSignature[] }>; signMessage(message: Uint8Array): Promise<{ signature: Uint8Array }>; connect(): Promise; disconnect(): Promise; @@ -228,43 +224,6 @@ export class PhantomWalletAdapter extends BaseMessageSignerWalletAdapter { } } - async signAndSendAllTransactions( - transactions: T[], - connection: Connection, - options: SignAndSendTransactionOptions = {} - ): Promise { - try { - const wallet = this._wallet; - if (!wallet) throw new WalletNotConnectedError(); - - try { - const { signers, ...sendOptions } = options; - - transactions = await Promise.all( - transactions.map(async (transaction) => { - if (isVersionedTransaction(transaction)) { - signers?.length && transaction.sign(signers); - } else { - transaction = (await this.prepareTransaction(transaction, connection, sendOptions)) as T; - signers?.length && (transaction as Transaction).partialSign(...signers); - } - return transaction; - }) - ); - sendOptions.preflightCommitment = sendOptions.preflightCommitment || connection.commitment; - - const { signatures } = await wallet.signAndSendAllTransactions(transactions, sendOptions); - return signatures; - } catch (error: any) { - if (error instanceof WalletError) throw error; - throw new WalletSendTransactionError(error?.message, error); - } - } catch (error: any) { - this.emit('error', error); - throw error; - } - } - async signTransaction(transaction: T): Promise { try { const wallet = this._wallet; diff --git a/packages/wallets/unsafe-burner/src/adapter.ts b/packages/wallets/unsafe-burner/src/adapter.ts index 7159431e7..176b27e7d 100644 --- a/packages/wallets/unsafe-burner/src/adapter.ts +++ b/packages/wallets/unsafe-burner/src/adapter.ts @@ -1,14 +1,24 @@ import { ed25519 } from '@noble/curves/ed25519'; -import type { WalletName } from '@solana/wallet-adapter-base'; +import type { SignAndSendTransactionOptions, WalletName } from '@solana/wallet-adapter-base'; import { BaseSignInMessageSignerWalletAdapter, isVersionedTransaction, + WalletError, WalletNotConnectedError, WalletReadyState, + WalletSendTransactionError, + WalletSignTransactionError, + WalletSignAndSendAllTransactionsError, } from '@solana/wallet-adapter-base'; import { type SolanaSignInInput, type SolanaSignInOutput } from '@solana/wallet-standard-features'; import { createSignInMessage } from '@solana/wallet-standard-util'; -import type { Transaction, TransactionVersion, VersionedTransaction } from '@solana/web3.js'; +import type { + Connection, + Transaction, + TransactionSignature, + TransactionVersion, + VersionedTransaction, +} from '@solana/web3.js'; import { Keypair } from '@solana/web3.js'; export const UnsafeBurnerWalletName = 'Burner Wallet' as WalletName<'Burner Wallet'>; @@ -105,4 +115,69 @@ export class UnsafeBurnerWalletAdapter extends BaseSignInMessageSignerWalletAdap signature, }; } + + async signAndSendAllTransactions( + transactions: T[], + connection: Connection, + options: SignAndSendTransactionOptions = {} + ): Promise<(TransactionSignature | WalletSignAndSendAllTransactionsError)[]> { + try { + if (!this._keypair) throw new WalletNotConnectedError(); + + try { + const { signers, ...sendOptions } = options; + sendOptions.preflightCommitment = sendOptions.preflightCommitment || connection.commitment; + + transactions = await Promise.all( + transactions.map(async (transaction) => { + if (isVersionedTransaction(transaction)) { + signers?.length && transaction.sign(signers); + } else { + transaction = (await this.prepareTransaction(transaction, connection, sendOptions)) as T; + signers?.length && (transaction as Transaction).partialSign(...signers); + } + return transaction; + }) + ); + + for (const transaction of transactions) { + if (isVersionedTransaction(transaction)) { + if (!this.supportedTransactionVersions) + throw new WalletSignTransactionError( + `Signing versioned transactions isn't supported by this wallet` + ); + + if (!this.supportedTransactionVersions.has(transaction.version)) + throw new WalletSignTransactionError( + `Signing transaction version ${transaction.version} isn't supported by this wallet` + ); + } + } + + // Sign and send transactions in parallel + const sendPromises = transactions.map(async (transaction) => { + const signedTransaction = await this.signTransaction(transaction); + const rawTransaction = signedTransaction.serialize(); + return connection.sendRawTransaction(rawTransaction, options); + }); + + // Use Promise.allSettled to wait for all sendPromises to settle + const sendResults = await Promise.allSettled(sendPromises); + + // Process results + const result = sendResults.map((result) => { + if (result.status === 'fulfilled') return result.value; + return new WalletSignAndSendAllTransactionsError(result.reason); + }); + + return result; + } catch (error: any) { + if (error instanceof WalletError) throw error; + throw new WalletSendTransactionError(error?.message, error); + } + } catch (error: any) { + this.emit('error', error); + throw error; + } + } } From d3c633717c77bfcee635af1d46a9ecda58dd3420 Mon Sep 17 00:00:00 2001 From: Anvit Mangal Date: Fri, 2 Feb 2024 14:52:31 +0530 Subject: [PATCH 3/7] bug fixes --- packages/core/base/src/adapter.ts | 11 ----------- packages/core/base/src/signer.ts | 5 +++++ packages/core/react/src/WalletProviderBase.tsx | 6 +++--- packages/core/react/src/useWallet.ts | 2 +- .../src/components/SignAndSendAllTransactions.tsx | 1 + 5 files changed, 10 insertions(+), 15 deletions(-) diff --git a/packages/core/base/src/adapter.ts b/packages/core/base/src/adapter.ts index 907aa985d..2cfcd5b67 100644 --- a/packages/core/base/src/adapter.ts +++ b/packages/core/base/src/adapter.ts @@ -61,11 +61,6 @@ export interface WalletAdapterProps { connection: Connection, options?: SignAndSendTransactionOptions ): Promise; - signAndSendAllTransactions( - transactions: TransactionOrVersionedTransaction[], - connection: Connection, - options?: SignAndSendTransactionOptions - ): Promise<(TransactionSignature | WalletSignAndSendAllTransactionsError)[]>; /** @deprecated Use `signAndSendTransaction` instead. */ sendTransaction( transaction: TransactionOrVersionedTransaction, @@ -134,12 +129,6 @@ export abstract class BaseWalletAdapter options?: SignAndSendTransactionOptions ): Promise; - abstract signAndSendAllTransactions( - transactions: TransactionOrVersionedTransaction[], - connection: Connection, - options?: SignAndSendTransactionOptions | undefined - ): Promise<(TransactionSignature | WalletSignAndSendAllTransactionsError)[]>; - /** @deprecated Use `signAndSendTransaction` instead. */ sendTransaction( transaction: TransactionOrVersionedTransaction, diff --git a/packages/core/base/src/signer.ts b/packages/core/base/src/signer.ts index a9da988db..ee7dea781 100644 --- a/packages/core/base/src/signer.ts +++ b/packages/core/base/src/signer.ts @@ -18,6 +18,11 @@ export interface SignerWalletAdapterProps extends signAllTransactions>( transactions: T[] ): Promise; + signAndSendAllTransactions( + transactions: TransactionOrVersionedTransaction[], + connection: Connection, + options?: SignAndSendTransactionOptions + ): Promise<(TransactionSignature | WalletSignAndSendAllTransactionsError)[]>; } export type SignerWalletAdapter = WalletAdapter & SignerWalletAdapterProps; diff --git a/packages/core/react/src/WalletProviderBase.tsx b/packages/core/react/src/WalletProviderBase.tsx index 5f4a79e65..df650ed0a 100644 --- a/packages/core/react/src/WalletProviderBase.tsx +++ b/packages/core/react/src/WalletProviderBase.tsx @@ -204,10 +204,10 @@ export function WalletProviderBase({ [adapter, connected] ); - // Sign and send multiple transactions using the provided connection - const signAndSendAllTransactions: WalletAdapterProps['signAndSendAllTransactions'] | undefined = useMemo( + // Sign and send multiple transactions using the provided connection if the wallet supports it + const signAndSendAllTransactions: SignerWalletAdapterProps['signAndSendAllTransactions'] | undefined = useMemo( () => - adapter && 'signAndSendTransactions' in adapter + adapter && 'signAndSendAllTransactions' in adapter ? async (transactions, connection, options) => { if (!adapter) throw handleErrorRef.current(new WalletNotSelectedError()); if (!connected) throw handleErrorRef.current(new WalletNotConnectedError(), adapter); diff --git a/packages/core/react/src/useWallet.ts b/packages/core/react/src/useWallet.ts index d80fa724d..5d5d6e3ff 100644 --- a/packages/core/react/src/useWallet.ts +++ b/packages/core/react/src/useWallet.ts @@ -29,7 +29,7 @@ export interface WalletContextState { disconnect(): Promise; signAndSendTransaction: WalletAdapterProps['signAndSendTransaction']; - signAndSendAllTransactions: WalletAdapterProps['signAndSendAllTransactions'] | undefined; + signAndSendAllTransactions: SignerWalletAdapterProps['signAndSendAllTransactions'] | undefined; signTransaction: SignerWalletAdapterProps['signTransaction'] | undefined; signAllTransactions: SignerWalletAdapterProps['signAllTransactions'] | undefined; signMessage: MessageSignerWalletAdapterProps['signMessage'] | undefined; diff --git a/packages/starter/example/src/components/SignAndSendAllTransactions.tsx b/packages/starter/example/src/components/SignAndSendAllTransactions.tsx index 36d2d94bf..afdf85f0f 100644 --- a/packages/starter/example/src/components/SignAndSendAllTransactions.tsx +++ b/packages/starter/example/src/components/SignAndSendAllTransactions.tsx @@ -15,6 +15,7 @@ export const SignAndSendAllTransactions: FC = () => { const onClick = useCallback(async () => { try { if (!publicKey) throw new Error('Wallet not connected!'); + if (!signAndSendAllTransactions) throw new Error('Wallet does not supoport signAndSendAllTransactions!'); const { context: { slot: minContextSlot }, From 566088f4ccdca6d7411400f5c4674ac9b2e0ec2e Mon Sep 17 00:00:00 2001 From: Anvit Mangal Date: Fri, 2 Feb 2024 16:09:29 +0530 Subject: [PATCH 4/7] fix example --- .../components/SignAndSendAllTransactions.tsx | 34 +++++++++++++------ packages/starter/example/src/pages/index.tsx | 3 ++ 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/packages/starter/example/src/components/SignAndSendAllTransactions.tsx b/packages/starter/example/src/components/SignAndSendAllTransactions.tsx index afdf85f0f..4d4f490b7 100644 --- a/packages/starter/example/src/components/SignAndSendAllTransactions.tsx +++ b/packages/starter/example/src/components/SignAndSendAllTransactions.tsx @@ -29,7 +29,7 @@ export const SignAndSendAllTransactions: FC = () => { recentBlockhash: blockhash, }).add( new TransactionInstruction({ - data: Buffer.from('Hello, from the Solana Wallet Adapter example app!'), + data: Buffer.from(`transaction number ${i}`), keys: [], programId: new PublicKey('MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr'), }) @@ -38,17 +38,29 @@ export const SignAndSendAllTransactions: FC = () => { } const results = await signAndSendAllTransactions(transactions, connection, { minContextSlot }); - + console.log(results); // Iterate through the results - results.forEach((result, index) => { - if (result instanceof WalletSignAndSendAllTransactionsError) { - // If the result is an instance of WalletSignAndSendAllTransactionsError, notify about the error - notify('error', `Transaction failed: ${result.message}`); - } else { - // If the result is a signature, notify about the successful transaction - notify('info', 'Transaction sent:', result); - } - }); + (async () => { + await Promise.all( + results.map(async (result) => { + if (result instanceof WalletSignAndSendAllTransactionsError) { + // If the result is an instance of WalletSignAndSendAllTransactionsError, notify about the error + notify('error', `Transaction failed: ${result.message}`); + } else { + try { + await connection.confirmTransaction({ + blockhash, + lastValidBlockHeight, + signature: result, + }); + notify('success', 'Transaction successful!', result); + } catch (error: any) { + notify('error', `Transaction failed! ${error?.message}`, result); + } + } + }) + ); + })(); } catch (error: any) { notify('error', `Transactions failed! ${error?.message}`); } diff --git a/packages/starter/example/src/pages/index.tsx b/packages/starter/example/src/pages/index.tsx index e9fbc970c..572f151a4 100644 --- a/packages/starter/example/src/pages/index.tsx +++ b/packages/starter/example/src/pages/index.tsx @@ -202,6 +202,9 @@ const Index: NextPage = () => { + + + From 157f43157f3dee9117f698836ecef7f2bee77634 Mon Sep 17 00:00:00 2001 From: Anvit Mangal Date: Fri, 2 Feb 2024 16:10:03 +0530 Subject: [PATCH 5/7] comment --- .../example/src/components/SignAndSendAllTransactions.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/starter/example/src/components/SignAndSendAllTransactions.tsx b/packages/starter/example/src/components/SignAndSendAllTransactions.tsx index 4d4f490b7..9d3dc8656 100644 --- a/packages/starter/example/src/components/SignAndSendAllTransactions.tsx +++ b/packages/starter/example/src/components/SignAndSendAllTransactions.tsx @@ -39,7 +39,8 @@ export const SignAndSendAllTransactions: FC = () => { const results = await signAndSendAllTransactions(transactions, connection, { minContextSlot }); console.log(results); - // Iterate through the results + + // Confirm all transactions (async () => { await Promise.all( results.map(async (result) => { From 65755db4069ab65876a2ee028008c646e84c934c Mon Sep 17 00:00:00 2001 From: Anvit Mangal Date: Fri, 2 Feb 2024 17:32:32 +0530 Subject: [PATCH 6/7] reordering --- packages/core/base/src/signer.ts | 28 +++++++++---------- .../components/SignAndSendAllTransactions.tsx | 3 +- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/packages/core/base/src/signer.ts b/packages/core/base/src/signer.ts index ee7dea781..34fa01706 100644 --- a/packages/core/base/src/signer.ts +++ b/packages/core/base/src/signer.ts @@ -31,20 +31,6 @@ export abstract class BaseSignerWalletAdapter extends BaseWalletAdapter implements SignerWalletAdapter { - async signAndSendAllTransactions( - transactions: TransactionOrVersionedTransaction[], - connection: Connection, - options: SignAndSendTransactionOptions = {} - ): Promise<(TransactionSignature | WalletSignAndSendAllTransactionsError)[]> { - const results = await Promise.allSettled( - transactions.map((transaction) => this.signAndSendTransaction(transaction, connection, options)) - ); - return results.map((result) => { - if (result.status === 'fulfilled') return result.value; - return new WalletSignAndSendAllTransactionsError(result.reason); - }); - } - async signAndSendTransaction( transaction: TransactionOrVersionedTransaction, connection: Connection, @@ -107,6 +93,20 @@ export abstract class BaseSignerWalletAdapter } } + async signAndSendAllTransactions( + transactions: TransactionOrVersionedTransaction[], + connection: Connection, + options: SignAndSendTransactionOptions = {} + ): Promise<(TransactionSignature | WalletSignAndSendAllTransactionsError)[]> { + const results = await Promise.allSettled( + transactions.map((transaction) => this.signAndSendTransaction(transaction, connection, options)) + ); + return results.map((result) => { + if (result.status === 'fulfilled') return result.value; + return new WalletSignAndSendAllTransactionsError(result.reason); + }); + } + abstract signTransaction>( transaction: T ): Promise; diff --git a/packages/starter/example/src/components/SignAndSendAllTransactions.tsx b/packages/starter/example/src/components/SignAndSendAllTransactions.tsx index 9d3dc8656..c6568aa5d 100644 --- a/packages/starter/example/src/components/SignAndSendAllTransactions.tsx +++ b/packages/starter/example/src/components/SignAndSendAllTransactions.tsx @@ -1,6 +1,5 @@ import { Button } from '@mui/material'; import { useConnection, useWallet } from '@solana/wallet-adapter-react'; -import type { TransactionSignature } from '@solana/web3.js'; import { PublicKey, Transaction, TransactionInstruction } from '@solana/web3.js'; import type { FC } from 'react'; import React, { useCallback } from 'react'; @@ -15,7 +14,7 @@ export const SignAndSendAllTransactions: FC = () => { const onClick = useCallback(async () => { try { if (!publicKey) throw new Error('Wallet not connected!'); - if (!signAndSendAllTransactions) throw new Error('Wallet does not supoport signAndSendAllTransactions!'); + if (!signAndSendAllTransactions) throw new Error('Wallet does not support signAndSendAllTransactions!'); const { context: { slot: minContextSlot }, From f99e91816ac0c1cbef09d49954e77b21a27bdc15 Mon Sep 17 00:00:00 2001 From: Anvit Mangal Date: Fri, 2 Feb 2024 20:53:28 +0530 Subject: [PATCH 7/7] remove extra imports --- packages/core/base/src/adapter.ts | 7 ------- packages/core/base/src/errors.ts | 4 ++-- packages/core/base/src/signer.ts | 1 - .../example/src/components/SignAndSendAllTransactions.tsx | 1 - 4 files changed, 2 insertions(+), 11 deletions(-) diff --git a/packages/core/base/src/adapter.ts b/packages/core/base/src/adapter.ts index 2cfcd5b67..979b3e560 100644 --- a/packages/core/base/src/adapter.ts +++ b/packages/core/base/src/adapter.ts @@ -1,6 +1,5 @@ import type { Commitment, Connection, PublicKey, Signer, Transaction, TransactionSignature } from '@solana/web3.js'; import EventEmitter from 'eventemitter3'; -import type { WalletSignAndSendAllTransactionsError } from './errors.js'; import { WalletNotConnectedError, type WalletError } from './errors.js'; import type { SupportedTransactionVersions, TransactionOrVersionedTransaction } from './transaction.js'; import type { SolanaSignAndSendTransactionMode } from '@solana/wallet-standard-features'; @@ -33,12 +32,6 @@ export interface SendOptions { /** @deprecated Use `SignAndSendTransactionOptions` instead. */ export type SendTransactionOptions = SignAndSendTransactionOptions; -export interface SignAndSendAllTransactionsError { - type: string; - code: number; - message: string; -} - // WalletName is a nominal type that wallet adapters should use, e.g. `'MyCryptoWallet' as WalletName<'MyCryptoWallet'>` // https://medium.com/@KevinBGreene/surviving-the-typescript-ecosystem-branding-and-type-tagging-6cf6e516523d export type WalletName = T & { __brand__: 'WalletName' }; diff --git a/packages/core/base/src/errors.ts b/packages/core/base/src/errors.ts index e0e831704..2618ea694 100644 --- a/packages/core/base/src/errors.ts +++ b/packages/core/base/src/errors.ts @@ -91,7 +91,7 @@ export class WalletSignAndSendAllTransactionsError extends WalletError { constructor(error: any) { super(error?.reason?.message, error); this.error = error; - this.type = error.type || error.name; - this.code = error.code; + this.type = error?.type || error?.name; + this.code = error?.code; } } diff --git a/packages/core/base/src/signer.ts b/packages/core/base/src/signer.ts index 34fa01706..194865f90 100644 --- a/packages/core/base/src/signer.ts +++ b/packages/core/base/src/signer.ts @@ -1,6 +1,5 @@ import type { SolanaSignInInput, SolanaSignInOutput } from '@solana/wallet-standard-features'; import type { Connection, TransactionSignature } from '@solana/web3.js'; -import type { SignAndSendAllTransactionsError } from './adapter.js'; import { BaseWalletAdapter, type SignAndSendTransactionOptions, diff --git a/packages/starter/example/src/components/SignAndSendAllTransactions.tsx b/packages/starter/example/src/components/SignAndSendAllTransactions.tsx index c6568aa5d..a6a8d0430 100644 --- a/packages/starter/example/src/components/SignAndSendAllTransactions.tsx +++ b/packages/starter/example/src/components/SignAndSendAllTransactions.tsx @@ -37,7 +37,6 @@ export const SignAndSendAllTransactions: FC = () => { } const results = await signAndSendAllTransactions(transactions, connection, { minContextSlot }); - console.log(results); // Confirm all transactions (async () => {