Skip to content

Commit

Permalink
Merge pull request #1270 from Web3Auth/feat/versionedTransaction
Browse files Browse the repository at this point in the history
feat: support versioned transaction
  • Loading branch information
chaitanyapotti authored Feb 8, 2023
2 parents 2516769 + f046e06 commit 899c5eb
Show file tree
Hide file tree
Showing 10 changed files with 110 additions and 72 deletions.
20 changes: 10 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/adapters/torus-solana-adapter/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"@babel/runtime": "^7.x"
},
"dependencies": {
"@toruslabs/solana-embed": "^0.3.2",
"@toruslabs/solana-embed": "^0.3.3",
"@web3auth/base": "^4.4.0",
"@web3auth/base-provider": "^4.4.0",
"@web3auth/base-solana-adapter": "^4.4.0",
Expand Down
28 changes: 15 additions & 13 deletions packages/providers/solana-provider/src/interface.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
import type { Transaction } from "@solana/web3.js";
import type { Transaction, VersionedTransaction } from "@solana/web3.js";
import type Solflare from "@solflare-wallet/sdk";
import type { SafeEventEmitter } from "@toruslabs/openlogin-jrpc";
import { RequestArguments } from "@web3auth/base";
import BN from "bn.js";

import { InjectedProvider } from "./providers";

export type TransactionOrVersionedTransaction = Transaction | VersionedTransaction;

export interface ISolanaWallet {
publicKey?: { toBytes(): Uint8Array };
signAndSendTransaction(transaction: Transaction): Promise<{ signature: string }>;
signTransaction?(transaction: Transaction): Promise<Transaction>;
signAllTransactions?(transactions: Transaction[]): Promise<Transaction[]>;
signAndSendTransaction<T extends TransactionOrVersionedTransaction>(transaction: T): Promise<{ signature: string }>;
signTransaction?<T extends TransactionOrVersionedTransaction>(transaction: T): Promise<T>;
signAllTransactions?<T extends TransactionOrVersionedTransaction>(transactions: T[]): Promise<T[]>;
signMessage(message: Uint8Array, display?: string): Promise<Uint8Array>;
request<T>(args: RequestArguments): Promise<T>;
}

export interface IPhantomWalletProvider extends SafeEventEmitter {
isConnected: boolean;
publicKey?: { toBytes(): Uint8Array };
signAndSendTransaction(transaction: Transaction): Promise<{ signature: string }>;
signTransaction?(transaction: Transaction): Promise<Transaction>;
signAllTransactions?(transactions: Transaction[]): Promise<Transaction[]>;
signAndSendTransaction<T extends TransactionOrVersionedTransaction>(transaction: T): Promise<{ signature: string }>;
signTransaction?<T extends TransactionOrVersionedTransaction>(transaction: T): Promise<T>;
signAllTransactions?<T extends TransactionOrVersionedTransaction>(transactions: T[]): Promise<T[]>;
signMessage(message: Uint8Array): Promise<{ signature: Uint8Array; publicKey: BN }>;
request<T>(args: RequestArguments): Promise<T>;
_handleDisconnect(...args: unknown[]): void;
Expand All @@ -31,9 +33,9 @@ export interface IPhantomWalletProvider extends SafeEventEmitter {
export interface IBaseWalletProvider {
publicKey?: { toBytes(): Uint8Array };
signMessage?(message: Uint8Array, display?: "hex" | "utf8"): Promise<{ signature: Uint8Array; publicKey: BN }>;
signTransaction?(transaction: Transaction): Promise<Transaction>;
signAllTransactions?(transactions: Transaction[]): Promise<Transaction[]>;
signAndSendTransaction?(transaction: Transaction): Promise<{ signature: string }>;
signTransaction?<T extends TransactionOrVersionedTransaction>(transaction: T): Promise<T>;
signAllTransactions?<T extends TransactionOrVersionedTransaction>(transactions: T[]): Promise<T[]>;
signAndSendTransaction?<T extends TransactionOrVersionedTransaction>(transaction: T): Promise<{ signature: string }>;
}

// NOTE: solflare types fo sign message is broken.
Expand Down Expand Up @@ -67,8 +69,8 @@ export interface ISlopeProvider extends SafeEventEmitter {
}

export interface ITorusWalletProvider extends InjectedProvider {
sendTransaction(transaction: Transaction): Promise<string>;
signTransaction(transaction: Transaction): Promise<Transaction>;
signAllTransactions(transactions: Transaction[]): Promise<Transaction[]>;
sendTransaction<T extends TransactionOrVersionedTransaction>(transaction: T): Promise<string>;
signTransaction<T extends TransactionOrVersionedTransaction>(transaction: T): Promise<T>;
signAllTransactions<T extends TransactionOrVersionedTransaction>(transactions: T[]): Promise<T[]>;
signMessage(data: Uint8Array): Promise<Uint8Array>;
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { Transaction } from "@solana/web3.js";
import { JRPCRequest } from "@toruslabs/openlogin-jrpc";
import bs58 from "bs58";
import { ethErrors } from "eth-rpc-errors";

import { IBaseWalletProvider } from "../../../interface";
import { IBaseWalletProvider, TransactionOrVersionedTransaction } from "../../../interface";
import { IProviderHandlers } from "../../../rpc/solanaRpcMiddlewares";

export const getBaseProviderHandlers = (injectedProvider: IBaseWalletProvider): IProviderHandlers => {
Expand All @@ -18,22 +17,22 @@ export const getBaseProviderHandlers = (injectedProvider: IBaseWalletProvider):
getSecretKey: async () => {
throw ethErrors.rpc.methodNotSupported();
},
signTransaction: async (req: JRPCRequest<{ message: Transaction }>): Promise<Transaction> => {
signTransaction: async (req: JRPCRequest<{ message: TransactionOrVersionedTransaction }>): Promise<TransactionOrVersionedTransaction> => {
const transaction = await injectedProvider.signTransaction(req.params.message);
return transaction;
},
signMessage: async (req: JRPCRequest<{ message: Uint8Array; display?: "utf8" | "hex" }>): Promise<Uint8Array> => {
const sigData = await injectedProvider.signMessage(req.params.message, req.params.display);
return sigData.signature;
},
signAllTransactions: async (req: JRPCRequest<{ message: Transaction[] }>): Promise<Transaction[]> => {
signAllTransactions: async (req: JRPCRequest<{ message: TransactionOrVersionedTransaction[] }>): Promise<TransactionOrVersionedTransaction[]> => {
if (!req.params?.message || !req.params?.message.length) {
throw ethErrors.rpc.invalidParams("message");
}
const transaction = await injectedProvider.signAllTransactions(req.params.message);
return transaction;
},
signAndSendTransaction: async (req: JRPCRequest<{ message: Transaction }>): Promise<{ signature: string }> => {
signAndSendTransaction: async (req: JRPCRequest<{ message: TransactionOrVersionedTransaction }>): Promise<{ signature: string }> => {
const txRes = await injectedProvider.signAndSendTransaction(req.params.message);
return { signature: txRes.signature };
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { Connection, PublicKey, Transaction } from "@solana/web3.js";
import { Connection, PublicKey, Transaction, VersionedTransaction } from "@solana/web3.js";
import { JRPCRequest } from "@toruslabs/openlogin-jrpc";
import { CustomChainConfig, SafeEventEmitterProvider } from "@web3auth/base";
import bs58 from "bs58";
import { ethErrors } from "eth-rpc-errors";

import { ISlopeProvider } from "../../../interface";
import { ISlopeProvider, TransactionOrVersionedTransaction } from "../../../interface";
import { IProviderHandlers } from "../../../rpc/solanaRpcMiddlewares";

const isVersionTransction = (transaction: TransactionOrVersionedTransaction) =>
(transaction as VersionedTransaction).version !== undefined || transaction instanceof VersionedTransaction;

export const getSlopeHandlers = (injectedProvider: ISlopeProvider, getProviderEngineProxy: () => SafeEventEmitterProvider): IProviderHandlers => {
const providerHandlers: IProviderHandlers = {
requestAccounts: async () => {
Expand All @@ -23,11 +26,16 @@ export const getSlopeHandlers = (injectedProvider: ISlopeProvider, getProviderEn
getSecretKey: async () => {
throw ethErrors.rpc.methodNotSupported();
},
signTransaction: async (req: JRPCRequest<{ message: Transaction }>): Promise<Transaction> => {
signTransaction: async (req: JRPCRequest<{ message: TransactionOrVersionedTransaction }>): Promise<TransactionOrVersionedTransaction> => {
const txMessage = req.params.message;
if (!txMessage) throw ethErrors.rpc.invalidRequest({ message: "Invalid transaction message" });
const { data } = await injectedProvider.signTransaction(bs58.encode(txMessage.serializeMessage()));

const message = isVersionTransction(txMessage)
? (txMessage as VersionedTransaction).message.serialize()
: (txMessage as Transaction).serializeMessage();
const { data } = await injectedProvider.signTransaction(bs58.encode(message));
if (!data.publicKey || !data.signature) throw new Error("Invalid signature from slope wallet");

const publicKey = new PublicKey(data.publicKey);
const signature = bs58.decode(data.signature);
txMessage.addSignature(publicKey, Buffer.from(signature));
Expand All @@ -37,12 +45,16 @@ export const getSlopeHandlers = (injectedProvider: ISlopeProvider, getProviderEn
const response = await injectedProvider.signMessage(req.params.message);
return bs58.decode(response.data.signature);
},
signAndSendTransaction: async (req: JRPCRequest<{ message: Transaction }>): Promise<{ signature: string }> => {
signAndSendTransaction: async (req: JRPCRequest<{ message: TransactionOrVersionedTransaction }>): Promise<{ signature: string }> => {
const provider = getProviderEngineProxy();
if (!provider) throw ethErrors.provider.custom({ message: "Provider is not initialized", code: 4902 });
const txMessage = req.params.message;
if (!txMessage) throw ethErrors.rpc.invalidRequest({ message: "Invalid transaction message" });
const { data } = await injectedProvider.signTransaction(bs58.encode(txMessage.serializeMessage()));

const message = isVersionTransction(txMessage)
? (txMessage as VersionedTransaction).message.serialize()
: (txMessage as Transaction).serializeMessage();
const { data } = await injectedProvider.signTransaction(bs58.encode(message));
if (!data.publicKey || !data.signature) throw new Error("Invalid signature from slope wallet");
const publicKey = new PublicKey(data.publicKey);
const signature = bs58.decode(data.signature);
Expand All @@ -52,7 +64,7 @@ export const getSlopeHandlers = (injectedProvider: ISlopeProvider, getProviderEn
const res = await conn.sendRawTransaction(txMessage.serialize());
return { signature: res };
},
signAllTransactions: async (req: JRPCRequest<{ message: Transaction[] }>): Promise<Transaction[]> => {
signAllTransactions: async (req: JRPCRequest<{ message: TransactionOrVersionedTransaction[] }>): Promise<TransactionOrVersionedTransaction[]> => {
if (!req.params?.message || !req.params?.message.length) {
throw ethErrors.rpc.invalidParams("message");
}
Expand All @@ -63,7 +75,9 @@ export const getSlopeHandlers = (injectedProvider: ISlopeProvider, getProviderEn
const unsignedTx = [];

for (let i = 0; i < length; i++) {
unsignedTx.push(bs58.encode(req.params.message[i].serializeMessage()));
const item = allTxns[i];
const message = isVersionTransction(item) ? (item as VersionedTransaction).message.serialize() : (item as Transaction).serializeMessage();
unsignedTx.push(bs58.encode(message));
}
const { msg, data } = await injectedProvider.signAllTransactions(unsignedTx);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import { Connection, Transaction } from "@solana/web3.js";
import { Connection } from "@solana/web3.js";
import { JRPCRequest } from "@toruslabs/openlogin-jrpc";
import { CustomChainConfig, SafeEventEmitterProvider } from "@web3auth/base";
import { ethErrors } from "eth-rpc-errors";

import { IBaseWalletProvider, SolflareWallet } from "../../../interface";
import { IBaseWalletProvider, SolflareWallet, TransactionOrVersionedTransaction } from "../../../interface";
import { IProviderHandlers } from "../../../rpc/solanaRpcMiddlewares";
import { getBaseProviderHandlers } from "../base/providerHandlers";

export const getSolflareHandlers = (injectedProvider: SolflareWallet, getProviderEngineProxy: () => SafeEventEmitterProvider): IProviderHandlers => {
const solflareProviderHandlers = getBaseProviderHandlers(injectedProvider as IBaseWalletProvider);
solflareProviderHandlers.signAndSendTransaction = async (req: JRPCRequest<{ message: Transaction }>): Promise<{ signature: string }> => {
solflareProviderHandlers.signAndSendTransaction = async (
req: JRPCRequest<{ message: TransactionOrVersionedTransaction }>
): Promise<{ signature: string }> => {
const provider = getProviderEngineProxy();
if (!provider) throw ethErrors.provider.custom({ message: "Provider is not initialized", code: 4902 });

const transaction = await injectedProvider.signTransaction(req.params.message);
const chainConfig = (await provider.request<CustomChainConfig>({ method: "solana_provider_config", params: [] })) as CustomChainConfig;
const conn = new Connection(chainConfig.rpcTarget);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { Transaction } from "@solana/web3.js";
import { JRPCRequest } from "@toruslabs/openlogin-jrpc";
import { ethErrors } from "eth-rpc-errors";

import { ITorusWalletProvider } from "../../../interface";
import { ITorusWalletProvider, TransactionOrVersionedTransaction } from "../../../interface";
import { IProviderHandlers } from "../../../rpc/solanaRpcMiddlewares";

export const getTorusHandlers = (injectedProvider: ITorusWalletProvider): IProviderHandlers => {
Expand Down Expand Up @@ -37,7 +36,7 @@ export const getTorusHandlers = (injectedProvider: ITorusWalletProvider): IProvi
return message;
},

signTransaction: async (req: JRPCRequest<{ message: Transaction }>): Promise<Transaction> => {
signTransaction: async (req: JRPCRequest<{ message: TransactionOrVersionedTransaction }>): Promise<TransactionOrVersionedTransaction> => {
if (!req.params?.message) {
throw ethErrors.rpc.invalidParams("message");
}
Expand All @@ -46,7 +45,7 @@ export const getTorusHandlers = (injectedProvider: ITorusWalletProvider): IProvi
return response;
},

signAndSendTransaction: async (req: JRPCRequest<{ message: Transaction }>): Promise<{ signature: string }> => {
signAndSendTransaction: async (req: JRPCRequest<{ message: TransactionOrVersionedTransaction }>): Promise<{ signature: string }> => {
if (!req.params?.message) {
throw ethErrors.rpc.invalidParams("message");
}
Expand All @@ -55,7 +54,7 @@ export const getTorusHandlers = (injectedProvider: ITorusWalletProvider): IProvi
return { signature: response };
},

signAllTransactions: async (req: JRPCRequest<{ message: Transaction[] }>): Promise<Transaction[]> => {
signAllTransactions: async (req: JRPCRequest<{ message: TransactionOrVersionedTransaction[] }>): Promise<TransactionOrVersionedTransaction[]> => {
if (!req.params?.message || !req.params?.message.length) {
throw ethErrors.rpc.invalidParams("message");
}
Expand Down
Loading

0 comments on commit 899c5eb

Please sign in to comment.