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

Use wallet for transaction broadcasting #601

Merged
merged 4 commits into from
May 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
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
38 changes: 36 additions & 2 deletions packages/solana-contrib/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type {
Transaction,
} from "@solana/web3.js";

import type { PendingTransaction } from ".";
import type { BroadcastOptions, PendingTransaction } from ".";

/**
* Wallet interface for objects that can be used to sign provider transactions.
Expand Down Expand Up @@ -103,7 +103,7 @@ export interface Broadcaster {
*/
broadcast: (
tx: Transaction,
opts?: ConfirmOptions
opts?: BroadcastOptions
) => Promise<PendingTransaction>;

/**
Expand All @@ -121,12 +121,34 @@ export interface Broadcaster {
): Promise<RpcResponseAndContext<SimulatedTransactionResponse>>;
}

/**
* Sign and broadcast options.
*/
export type SignAndBroadcastOptions = BroadcastOptions & {
/**
* Additional signers
*/
signers?: Signer[];
};

/**
* An interface that can sign transactions.
*/
export interface TransactionSigner {
publicKey: PublicKey;

/**
* Signs and broadcasts a transaction.
*
* @param transaction
* @param broadcaster
* @param options
*/
signAndBroadcastTransaction(
transaction: Transaction,
opts?: SignAndBroadcastOptions
): Promise<PendingTransaction>;

/**
* Signs the given transaction, paid for and signed by the provider's wallet.
*
Expand Down Expand Up @@ -204,6 +226,18 @@ export interface Provider extends ReadonlyProvider {
opts?: ConfirmOptions
) => Promise<PendingTransaction[]>;

/**
* Signs and broadcasts a transaction.
*
* @param transaction
* @param broadcaster
* @param options
*/
signAndBroadcastTransaction(
transaction: Transaction,
opts?: SignAndBroadcastOptions
): Promise<PendingTransaction>;

/**
* Simulates the given transaction, returning emitted logs from execution.
*
Expand Down
52 changes: 45 additions & 7 deletions packages/solana-contrib/src/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { SingleConnectionBroadcaster } from "./broadcaster";
import type {
Provider,
SendTxRequest,
SignAndBroadcastOptions,
TransactionSigner,
Wallet,
} from "./interfaces";
Expand Down Expand Up @@ -77,6 +78,19 @@ export class SolanaReadonlyProvider implements ReadonlyProvider {
}
}

export const doSignAndBroadcastTransaction = async (
wallet: Pick<Wallet, "signTransaction">,
transaction: Transaction,
broadcaster: Broadcaster,
opts?: SignAndBroadcastOptions
): Promise<PendingTransaction> => {
const tx = await wallet.signTransaction(transaction);
if (opts?.signers && opts.signers.length > 0) {
tx.sign(...opts.signers);
}
return await broadcaster.broadcast(tx, opts);
};

/**
* Signs Solana transactions.
*/
Expand All @@ -91,6 +105,18 @@ export class SolanaTransactionSigner implements TransactionSigner {
return this.wallet.publicKey;
}

async signAndBroadcastTransaction(
transaction: Transaction,
opts?: SignAndBroadcastOptions | undefined
): Promise<PendingTransaction> {
return await doSignAndBroadcastTransaction(
this.wallet,
transaction,
this.broadcaster,
opts
);
}

/**
* Sends the given transaction, paid for and signed by the provider's wallet.
*
Expand Down Expand Up @@ -160,8 +186,6 @@ export class SolanaTransactionSigner implements TransactionSigner {
* This implementation was taken from Anchor.
*/
export class SolanaProvider extends SolanaReadonlyProvider implements Provider {
readonly signer: TransactionSigner;

/**
* @param connection The cluster connection where the program is deployed.
* @param sendConnection The connection where transactions are sent to.
Expand All @@ -172,14 +196,21 @@ export class SolanaProvider extends SolanaReadonlyProvider implements Provider {
override readonly connection: Connection,
readonly broadcaster: Broadcaster,
override readonly wallet: Wallet,
override readonly opts: ConfirmOptions = DEFAULT_PROVIDER_OPTIONS
) {
super(connection, opts);
this.signer = new SolanaTransactionSigner(
override readonly opts: ConfirmOptions = DEFAULT_PROVIDER_OPTIONS,
readonly signer: TransactionSigner = new SolanaTransactionSigner(
wallet,
broadcaster,
opts.preflightCommitment
);
)
) {
super(connection, opts);
}

async signAndBroadcastTransaction(
transaction: Transaction,
opts?: SignAndBroadcastOptions
): Promise<PendingTransaction> {
return await this.signer.signAndBroadcastTransaction(transaction, opts);
}

/**
Expand Down Expand Up @@ -384,6 +415,13 @@ export class SolanaAugmentedProvider implements AugmentedProvider {
return this.provider.wallet;
}

signAndBroadcastTransaction(
transaction: Transaction,
opts?: SignAndBroadcastOptions
): Promise<PendingTransaction> {
return this.provider.signAndBroadcastTransaction(transaction, opts);
}

send(
tx: Transaction,
signers?: (Signer | undefined)[] | undefined,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import type Transport from "@ledgerhq/hw-transport";
import TransportWebUSB from "@ledgerhq/hw-transport-webusb";
import type { PublicKey, Transaction } from "@solana/web3.js";
import type {
Broadcaster,
PendingTransaction,
SignAndBroadcastOptions,
} from "@saberhq/solana-contrib";
import { doSignAndBroadcastTransaction } from "@saberhq/solana-contrib";
import type { Connection, PublicKey, Transaction } from "@solana/web3.js";
import EventEmitter from "eventemitter3";

import type { WalletAdapter } from "../types";
import type { ConnectedWallet, WalletAdapter } from "../types";
import { getPublicKey, getSolanaDerivationPath, signTransaction } from "./core";

const DEFAULT_DERIVATION_PATH = getSolanaDerivationPath();
Expand Down Expand Up @@ -42,6 +48,20 @@ export class LedgerWalletAdapter extends EventEmitter implements WalletAdapter {
return false;
}

async signAndBroadcastTransaction(
transaction: Transaction,
_connection: Connection,
broadcaster: Broadcaster,
opts?: SignAndBroadcastOptions
): Promise<PendingTransaction> {
return await doSignAndBroadcastTransaction(
this as ConnectedWallet,
transaction,
broadcaster,
opts
);
}

async signAllTransactions(
transactions: Transaction[]
): Promise<Transaction[]> {
Expand Down
19 changes: 18 additions & 1 deletion packages/use-solana/src/adapters/readonly/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import type { Transaction } from "@solana/web3.js";
import type {
Broadcaster,
BroadcastOptions,
PendingTransaction,
} from "@saberhq/solana-contrib";
import type { Connection, Transaction } from "@solana/web3.js";
import { PublicKey } from "@solana/web3.js";
import { EventEmitter } from "eventemitter3";

Expand All @@ -21,6 +26,9 @@ export const setReadonlySolanaPubkey = (pubkey: PublicKey): void => {
window.USE_SOLANA_PUBKEY_OVERRIDE = pubkey.toString();
};

/**
* Adapter that cannot sign transactions. Dummy for testing.
*/
export class ReadonlyAdapter extends EventEmitter implements WalletAdapter {
private _publicKey: PublicKey | null = null;

Expand Down Expand Up @@ -49,6 +57,15 @@ export class ReadonlyAdapter extends EventEmitter implements WalletAdapter {
return this._publicKey;
}

signAndBroadcastTransaction(
_transaction: Transaction,
_connection: Connection,
_broadcaster: Broadcaster,
_opts?: BroadcastOptions
): Promise<PendingTransaction> {
throw new Error("readonly adapter cannot sign transactions");
}

signAllTransactions(_transactions: Transaction[]): Promise<Transaction[]> {
throw new Error("readonly adapter cannot sign transactions");
}
Expand Down
31 changes: 28 additions & 3 deletions packages/use-solana/src/adapters/secret-key/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
import { SignerWallet } from "@saberhq/solana-contrib";
import type { PublicKey, Transaction } from "@solana/web3.js";
import type {
Broadcaster,
PendingTransaction,
SignAndBroadcastOptions,
} from "@saberhq/solana-contrib";
import {
doSignAndBroadcastTransaction,
SignerWallet,
} from "@saberhq/solana-contrib";
import type { Connection, PublicKey, Transaction } from "@solana/web3.js";
import { Keypair } from "@solana/web3.js";
import EventEmitter from "eventemitter3";

import type { WalletAdapter } from "../types";
import type { ConnectedWallet, WalletAdapter } from "../types";

/**
* Adapter backed by a secret key.
*/
export class SecretKeyAdapter extends EventEmitter implements WalletAdapter {
_wallet?: SignerWallet;
_publicKey?: PublicKey;
Expand All @@ -24,6 +35,20 @@ export class SecretKeyAdapter extends EventEmitter implements WalletAdapter {
return false;
}

async signAndBroadcastTransaction(
transaction: Transaction,
_connection: Connection,
broadcaster: Broadcaster,
opts?: SignAndBroadcastOptions
): Promise<PendingTransaction> {
return await doSignAndBroadcastTransaction(
this as ConnectedWallet,
transaction,
broadcaster,
opts
);
}

signAllTransactions(transactions: Transaction[]): Promise<Transaction[]> {
const wallet = this._wallet;
if (!wallet) {
Expand Down
Loading