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

Feature: added versioned transaction solana signature #739

Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
Connection,
Keypair,
Transaction,
VersionedTransaction,
} from '@solana/web3.js';

// Solana transactions are pre-serialized; much simpler API than ethereum transactions
Expand Down Expand Up @@ -31,7 +32,7 @@ export function validateUnsignedTransaction(
}
}

function signTransaction({
function signLegacyTransaction({
solanaKeyPair,
transaction,
}: {
Expand All @@ -45,18 +46,36 @@ function signTransaction({
throw new Error('Transaction signature is null');
}

return { signature: ethers.utils.base58.encode(transaction.signature) };
return ethers.utils.base58.encode(transaction.signature);
} catch (err: unknown) {
throw new Error(`When signing transaction - ${(err as Error).message}`);
}
}
function signVersionedTransaction({
solanaKeyPair,
transaction,
}: {
solanaKeyPair: Keypair;
transaction: VersionedTransaction;
}) {
try {
transaction.sign([solanaKeyPair]);

if (!transaction.signatures.length) {
throw new Error('Transaction signature is null');
}

return ethers.utils.base58.encode(transaction.signatures[0]);
} catch (err: unknown) {
throw new Error(`When signing transaction - ${(err as Error).message}`);
}
}
async function sendTransaction({
chain,
transaction,
}: {
chain: Cluster;
transaction: Transaction;
transaction: Transaction | VersionedTransaction;
}) {
try {
const solanaConnection = new Connection(clusterApiUrl(chain), 'confirmed');
Expand All @@ -70,20 +89,32 @@ export async function signTransactionSolanaKey({
broadcast,
privateKey,
unsignedTransaction,
versionedTransaction,
}: {
broadcast: boolean;
privateKey: string;
unsignedTransaction: UnsignedTransaction;
versionedTransaction?: boolean;
}) {
// Be sure you call validateUnsignedTransaction(unsignedTransaction); before calling this method!

const solanaKeyPair = Keypair.fromSecretKey(Buffer.from(privateKey, 'hex'));

const transaction = Transaction.from(
Buffer.from(unsignedTransaction.serializedTransaction, 'base64')
);

const { signature } = signTransaction({ transaction, solanaKeyPair });
let transaction;
let signature;
if (versionedTransaction) {
const swapTransactionBuf = Buffer.from(
unsignedTransaction.serializedTransaction,
'base64'
);
transaction = VersionedTransaction.deserialize(swapTransactionBuf);
signature = signVersionedTransaction({ transaction, solanaKeyPair });
} else {
transaction = Transaction.from(
Buffer.from(unsignedTransaction.serializedTransaction, 'base64')
);
signature = signLegacyTransaction({ transaction, solanaKeyPair });
}

if (!broadcast) {
return signature;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export interface SignTransactionWithEncryptedSolanaKeyParams {
dataToEncryptHash: string; // The hash of the data to encrypt
unsignedTransaction: UnsignedTransaction;
broadcast: boolean; // Flag to determine if the transaction should be broadcasted
versionedTransaction?: boolean; // Flag to determine if the transaction is a versioned one or a legacy one
}

/**
Expand All @@ -28,6 +29,7 @@ export async function signTransactionWithEncryptedSolanaKey({
dataToEncryptHash,
unsignedTransaction,
broadcast,
versionedTransaction,
}: SignTransactionWithEncryptedSolanaKeyParams): Promise<string> {
validateUnsignedTransaction(unsignedTransaction);

Expand All @@ -41,5 +43,6 @@ export async function signTransactionWithEncryptedSolanaKey({
broadcast,
privateKey,
unsignedTransaction,
versionedTransaction,
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ declare const ciphertext: SignTransactionWithEncryptedSolanaKeyParams['ciphertex
declare const dataToEncryptHash: SignTransactionWithEncryptedSolanaKeyParams['dataToEncryptHash'];
declare const unsignedTransaction: SignTransactionWithEncryptedSolanaKeyParams['unsignedTransaction'];
declare const broadcast: SignTransactionWithEncryptedSolanaKeyParams['broadcast'];
declare const versionedTransaction: SignTransactionWithEncryptedSolanaKeyParams['versionedTransaction'];

(async () =>
litActionHandler(async () =>
Expand All @@ -20,5 +21,6 @@ declare const broadcast: SignTransactionWithEncryptedSolanaKeyParams['broadcast'
dataToEncryptHash,
unsignedTransaction,
broadcast,
versionedTransaction,
})
))();
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { SignTransactionWithEncryptedKeyParams } from '../types';
* Signs a transaction inside the Lit Action using the previously persisted wrapped key associated with the current LIT PK.
* This method fetches the encrypted key from the wrapped keys service, then executes a Lit Action that decrypts the key inside the LIT action and uses
* the decrypted key to sign the provided transaction
* use `versionedTransaction: true` to sign a versioned transaction and `false` for a legacy one
* Optionally, if you pass `broadcast: true`, the LIT action will also submit the signed transaction to the associated RPC endpoint on your behalf
*
* @param { SignTransactionWithEncryptedKeyParams } params Parameters required to sign the requested transaction
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ interface SignTransactionWithLitActionParams {
storedKeyMetadata: StoredKeyData;
accessControlConditions: AccessControlConditions;
broadcast: boolean;
versionedTransaction?: boolean;
}

export async function signTransactionWithLitAction({
Expand All @@ -32,6 +33,7 @@ export async function signTransactionWithLitAction({
pkpSessionSigs,
storedKeyMetadata: { ciphertext, dataToEncryptHash, pkpAddress },
unsignedTransaction,
versionedTransaction,
}: SignTransactionWithLitActionParams): Promise<string> {
const result = await litNodeClient.executeJs({
sessionSigs: pkpSessionSigs,
Expand All @@ -44,6 +46,7 @@ export async function signTransactionWithLitAction({
unsignedTransaction,
broadcast,
accessControlConditions,
versionedTransaction,
},
ipfsOptions: {
overwriteCode:
Expand Down
1 change: 1 addition & 0 deletions packages/wrapped-keys/src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ export interface SignTransactionParamsSupportedSolana
extends SignTransactionParams {
unsignedTransaction: SerializedTransaction;
network: Extract<Network, 'solana'>;
versionedTransaction?: boolean;
}

/** @typedef SignTransactionWithEncryptedKeyParams
Expand Down