Skip to main content

Function Signature

delegatedSignTransaction(
  client: DelegatedSvmWalletClient,
  params: {
    walletId: string;
    walletApiKey: string;
    keyShare: ServerKeyShare;
    transaction: VersionedTransaction | Transaction;
  }
): Promise<VersionedTransaction | Transaction>

Description

Signs a Solana transaction on behalf of a user who has granted delegation permission. This function supports both legacy Transaction and VersionedTransaction types from @solana/web3.js. The function returns the signed transaction object with the signature added, ready to be sent to the Solana network.

Parameters

Required Parameters

  • client (DelegatedSvmWalletClient) - The delegated client instance created with createDelegatedSvmWalletClient()
  • walletId (string) - The wallet ID from the delegation webhook
  • walletApiKey (string) - The wallet-specific API key from the delegation webhook
  • keyShare (ServerKeyShare) - The server key share from the delegation webhook
  • transaction (VersionedTransaction | Transaction) - The Solana transaction to sign

Returns

Promise<VersionedTransaction | Transaction> - The signed transaction with the signature added

Example

Basic Transaction Signing

import { 
  createDelegatedSvmWalletClient,
  delegatedSignTransaction 
} from '@dynamic-labs-wallet/node-svm';
import { Transaction, SystemProgram, PublicKey, Connection } from '@solana/web3.js';

const delegatedClient = createDelegatedSvmWalletClient({
  environmentId: 'your-environment-id',
  apiKey: 'your-server-api-key',
});

const connection = new Connection('https://api.mainnet-beta.solana.com');

const transaction = new Transaction().add(
  SystemProgram.transfer({
    fromPubkey: new PublicKey(senderAddress),
    toPubkey: new PublicKey(recipientAddress),
    lamports: 1000000,
  })
);

transaction.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
transaction.feePayer = new PublicKey(senderAddress);

const signedTx = await delegatedSignTransaction(delegatedClient, {
  walletId: 'wallet-id-from-webhook',
  walletApiKey: 'wallet-api-key-from-webhook',
  keyShare: keyShareFromWebhook,
  transaction,
});

console.log('Transaction signed:', signedTx);

Complete Transaction Flow

import { 
  createDelegatedSvmWalletClient,
  delegatedSignTransaction 
} from '@dynamic-labs-wallet/node-svm';
import { 
  Transaction, 
  SystemProgram, 
  PublicKey, 
  Connection,
  sendAndConfirmTransaction 
} from '@solana/web3.js';

const delegatedClient = createDelegatedSvmWalletClient({
  environmentId: process.env.DYNAMIC_ENVIRONMENT_ID!,
  apiKey: process.env.DYNAMIC_API_KEY!,
});

const connection = new Connection('https://api.mainnet-beta.solana.com');

async function sendPayment(
  userId: string,
  recipientAddress: string,
  amount: number
) {
  const credentials = await getDelegationCredentials(userId);
  const senderAddress = credentials.publicKey;

  const transaction = new Transaction().add(
    SystemProgram.transfer({
      fromPubkey: new PublicKey(senderAddress),
      toPubkey: new PublicKey(recipientAddress),
      lamports: amount,
    })
  );

  const { blockhash } = await connection.getLatestBlockhash();
  transaction.recentBlockhash = blockhash;
  transaction.feePayer = new PublicKey(senderAddress);

  const signedTx = await delegatedSignTransaction(delegatedClient, {
    walletId: credentials.walletId,
    walletApiKey: credentials.walletApiKey,
    keyShare: credentials.keyShare,
    transaction,
  });

  const signature = await connection.sendRawTransaction(
    signedTx.serialize()
  );

  await connection.confirmTransaction(signature);

  console.log('Transaction confirmed:', signature);
  return signature;
}

Versioned Transaction

import { VersionedTransaction, TransactionMessage } from '@solana/web3.js';

const message = new TransactionMessage({
  payerKey: new PublicKey(senderAddress),
  recentBlockhash: (await connection.getLatestBlockhash()).blockhash,
  instructions: [
    SystemProgram.transfer({
      fromPubkey: new PublicKey(senderAddress),
      toPubkey: new PublicKey(recipientAddress),
      lamports: 1000000,
    })
  ],
}).compileToV0Message();

const versionedTx = new VersionedTransaction(message);

const signedTx = await delegatedSignTransaction(delegatedClient, {
  walletId: 'wallet-id-from-webhook',
  walletApiKey: 'wallet-api-key-from-webhook',
  keyShare: keyShareFromWebhook,
  transaction: versionedTx,
});

const signature = await connection.sendTransaction(signedTx);

Type Definitions

type ServerKeyShare = {
  pubkey: {
    pubkey: Uint8Array;
  };
  secretShare: string;
};

Error Handling

The function throws an error if:
  • Any required parameter is missing or invalid
  • The transaction is malformed
  • The delegation credentials are expired or revoked
  • The MPC signing operation fails
  • Network communication with Dynamic services fails
try {
  const signedTx = await delegatedSignTransaction(delegatedClient, {
    walletId: credentials.walletId,
    walletApiKey: credentials.walletApiKey,
    keyShare: credentials.keyShare,
    transaction,
  });
  
  const signature = await connection.sendTransaction(signedTx);
  console.log('Transaction sent:', signature);
} catch (error) {
  if (error.message.includes('key share')) {
    console.error('Invalid or expired delegation credentials');
  } else if (error.message.includes('transaction')) {
    console.error('Invalid transaction format');
  } else {
    console.error('Transaction signing failed:', error);
  }
}

Security Considerations

  • Transaction Validation: Always validate transaction contents before signing
  • Recipient Verification: Verify recipient addresses to prevent sending to wrong addresses
  • Amount Validation: Implement checks for transaction amounts to prevent accidental large transfers
  • Audit Logging: Log all transaction signing operations for security auditing
  • Credential Storage: Never log or expose wallet API keys or key shares
  • Rate Limiting: Implement rate limiting to prevent abuse

Common Use Cases

Automated Payments

async function processAutomatedPayment(
  userId: string,
  recipient: string,
  amount: number
) {
  const credentials = await getDelegationCredentials(userId);
  
  const transaction = new Transaction().add(
    SystemProgram.transfer({
      fromPubkey: new PublicKey(credentials.publicKey),
      toPubkey: new PublicKey(recipient),
      lamports: amount,
    })
  );

  const { blockhash } = await connection.getLatestBlockhash();
  transaction.recentBlockhash = blockhash;
  transaction.feePayer = new PublicKey(credentials.publicKey);

  const signedTx = await delegatedSignTransaction(delegatedClient, {
    walletId: credentials.walletId,
    walletApiKey: credentials.walletApiKey,
    keyShare: credentials.keyShare,
    transaction,
  });

  return await connection.sendTransaction(signedTx);
}

Batch Transactions

async function signMultipleTransactions(
  userId: string,
  transactions: Transaction[]
): Promise<Transaction[]> {
  const credentials = await getDelegationCredentials(userId);
  
  const signedTransactions = await Promise.all(
    transactions.map(transaction =>
      delegatedSignTransaction(delegatedClient, {
        walletId: credentials.walletId,
        walletApiKey: credentials.walletApiKey,
        keyShare: credentials.keyShare,
        transaction,
      })
    )
  );

  return signedTransactions;
}
I