Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

signAndSendTransaction + signAndSendAllTransactions API #889

Open
wants to merge 7 commits into
base: sign-and-send-all-transactions
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/core/base/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
28 changes: 11 additions & 17 deletions packages/core/base/src/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to re-export this from the @solana/wallet-standard-features package.


/** @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 extends string = string> = T & { __brand__: 'WalletName' };
Expand All @@ -61,7 +60,7 @@ export interface WalletAdapterProps<Name extends string = string> {
transactions: TransactionOrVersionedTransaction<this['supportedTransactionVersions']>[],
connection: Connection,
options?: SignAndSendTransactionOptions
): Promise<(TransactionSignature | SignAndSendAllTransactionsError)[]>;
): Promise<(TransactionSignature | undefined)[]>;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning undefined is generally not ideal because the code may early return or simply return implicitly. Returning null is better because it's explicit.

Unfortunately returning either undefined or null isn't great, because it gives the caller no information about the failure.

What is an app supposed to do with this result? I don't think we can kick the can on having an error of some kind.

/** @deprecated Use `signAndSendTransaction` instead. */
sendTransaction(
transaction: TransactionOrVersionedTransaction<this['supportedTransactionVersions']>,
Expand Down Expand Up @@ -134,17 +133,12 @@ export abstract class BaseWalletAdapter<Name extends string = string>
transactions: TransactionOrVersionedTransaction<this['supportedTransactionVersions']>[],
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;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

});
}

Expand Down
2 changes: 1 addition & 1 deletion packages/core/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 2 additions & 2 deletions packages/starter/example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
41 changes: 41 additions & 0 deletions packages/wallets/phantom/src/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ interface PhantomWallet extends EventEmitter<PhantomWalletEvents> {
transaction: T,
options?: SendOptions
): Promise<{ signature: TransactionSignature }>;
signAndSendAllTransactions<T extends Transaction | VersionedTransaction>(
transactions: T[],
options?: SendOptions
): Promise<{ signatures: TransactionSignature[] }>;
signMessage(message: Uint8Array): Promise<{ signature: Uint8Array }>;
connect(): Promise<void>;
disconnect(): Promise<void>;
Expand Down Expand Up @@ -224,6 +228,43 @@ export class PhantomWalletAdapter extends BaseMessageSignerWalletAdapter {
}
}

async signAndSendAllTransactions<T extends Transaction | VersionedTransaction>(
transactions: T[],
connection: Connection,
options: SignAndSendTransactionOptions = {}
): Promise<TransactionSignature[]> {
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);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we can call this without feature detection because there's no guarantee of what version of the wallet the user has that supports this method. If it's older, it'll just null reference with nothing for the app to do.

We should consider having/calling a super method for this that will do signAllTransactions and send them after, in the same way we have sendTransaction in the base class if only signTransaction is supported.

Copy link
Contributor Author

@0xproflupin 0xproflupin Feb 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose we don't even need to add an implementation for signAndSendAll in individual wallet adapters (other than the unsafe burner, where we won't have to do feature detection)?

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<T extends Transaction | VersionedTransaction>(transaction: T): Promise<T> {
try {
const wallet = this._wallet;
Expand Down
4 changes: 2 additions & 2 deletions packages/wallets/unsafe-burner/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Loading