diff --git a/packages/wrapped-keys-lit-actions/src/lib/internal/solana/signTransaction.ts b/packages/wrapped-keys-lit-actions/src/lib/internal/solana/signTransaction.ts index 1586f5ea0..af24371ff 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/internal/solana/signTransaction.ts +++ b/packages/wrapped-keys-lit-actions/src/lib/internal/solana/signTransaction.ts @@ -4,6 +4,7 @@ import { Connection, Keypair, Transaction, + VersionedTransaction, } from '@solana/web3.js'; // Solana transactions are pre-serialized; much simpler API than ethereum transactions @@ -31,7 +32,7 @@ export function validateUnsignedTransaction( } } -function signTransaction({ +function signLegacyTransaction({ solanaKeyPair, transaction, }: { @@ -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'); @@ -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; diff --git a/packages/wrapped-keys-lit-actions/src/lib/raw-action-functions/solana/signTransactionWithEncryptedSolanaKey.ts b/packages/wrapped-keys-lit-actions/src/lib/raw-action-functions/solana/signTransactionWithEncryptedSolanaKey.ts index b3efdcb4e..48feb9fb3 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/raw-action-functions/solana/signTransactionWithEncryptedSolanaKey.ts +++ b/packages/wrapped-keys-lit-actions/src/lib/raw-action-functions/solana/signTransactionWithEncryptedSolanaKey.ts @@ -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 } /** @@ -28,6 +29,7 @@ export async function signTransactionWithEncryptedSolanaKey({ dataToEncryptHash, unsignedTransaction, broadcast, + versionedTransaction, }: SignTransactionWithEncryptedSolanaKeyParams): Promise { validateUnsignedTransaction(unsignedTransaction); @@ -41,5 +43,6 @@ export async function signTransactionWithEncryptedSolanaKey({ broadcast, privateKey, unsignedTransaction, + versionedTransaction, }); } diff --git a/packages/wrapped-keys-lit-actions/src/lib/self-executing-actions/solana/signTransactionWithEncryptedSolanaKey.ts b/packages/wrapped-keys-lit-actions/src/lib/self-executing-actions/solana/signTransactionWithEncryptedSolanaKey.ts index 164c8e117..9725ae534 100644 --- a/packages/wrapped-keys-lit-actions/src/lib/self-executing-actions/solana/signTransactionWithEncryptedSolanaKey.ts +++ b/packages/wrapped-keys-lit-actions/src/lib/self-executing-actions/solana/signTransactionWithEncryptedSolanaKey.ts @@ -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 () => @@ -20,5 +21,6 @@ declare const broadcast: SignTransactionWithEncryptedSolanaKeyParams['broadcast' dataToEncryptHash, unsignedTransaction, broadcast, + versionedTransaction, }) ))(); diff --git a/packages/wrapped-keys/src/lib/api/sign-transaction-with-encrypted-key.ts b/packages/wrapped-keys/src/lib/api/sign-transaction-with-encrypted-key.ts index 53cfa8d4e..7df1daf13 100644 --- a/packages/wrapped-keys/src/lib/api/sign-transaction-with-encrypted-key.ts +++ b/packages/wrapped-keys/src/lib/api/sign-transaction-with-encrypted-key.ts @@ -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 diff --git a/packages/wrapped-keys/src/lib/lit-actions-client/sign-transaction.ts b/packages/wrapped-keys/src/lib/lit-actions-client/sign-transaction.ts index 5dde9e0b3..9e28ddc5c 100644 --- a/packages/wrapped-keys/src/lib/lit-actions-client/sign-transaction.ts +++ b/packages/wrapped-keys/src/lib/lit-actions-client/sign-transaction.ts @@ -21,6 +21,7 @@ interface SignTransactionWithLitActionParams { storedKeyMetadata: StoredKeyData; accessControlConditions: AccessControlConditions; broadcast: boolean; + versionedTransaction?: boolean; } export async function signTransactionWithLitAction({ @@ -32,6 +33,7 @@ export async function signTransactionWithLitAction({ pkpSessionSigs, storedKeyMetadata: { ciphertext, dataToEncryptHash, pkpAddress }, unsignedTransaction, + versionedTransaction, }: SignTransactionWithLitActionParams): Promise { const result = await litNodeClient.executeJs({ sessionSigs: pkpSessionSigs, @@ -44,6 +46,7 @@ export async function signTransactionWithLitAction({ unsignedTransaction, broadcast, accessControlConditions, + versionedTransaction, }, ipfsOptions: { overwriteCode: diff --git a/packages/wrapped-keys/src/lib/types.ts b/packages/wrapped-keys/src/lib/types.ts index b3742ff69..b76f2cff0 100644 --- a/packages/wrapped-keys/src/lib/types.ts +++ b/packages/wrapped-keys/src/lib/types.ts @@ -291,6 +291,7 @@ export interface SignTransactionParamsSupportedSolana extends SignTransactionParams { unsignedTransaction: SerializedTransaction; network: Extract; + versionedTransaction?: boolean; } /** @typedef SignTransactionWithEncryptedKeyParams