> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Sign EVM Transactions

> Learn how to sign Ethereum transactions using Dynamic's Node SDK

## Overview

This guide teaches you how to sign EVM transactions using Dynamic's Node SDK. You'll learn how to prepare transactions, sign them securely, and send them to the blockchain.

## Prerequisites

Before you begin, make sure you have:

* [Created an EVM wallet](/node/evm/create-wallet)
* [Set up your authenticated client](/node/quickstart)

## Step 1: Set Up Your Environment

First, install the required dependencies:

```bash theme={"system"}
bun add viem
```

## Step 2: Prepare Your Transaction

Create a transaction using viem's transaction preparation utilities:

```typescript theme={"system"}
import { authenticatedEvmClient } from './client';
import { http } from 'viem';
import { base } from 'viem/chains';
import { parseEther } from 'viem/utils';

const evmClient = await authenticatedEvmClient();

// Create a viem public client for transaction preparation
const publicClient = evmClient.createViemPublicClient({
  chain: base,
  rpcUrl: 'https://mainnet.base.org',
});

// Define your transaction
const transactionRequest = {
  to: '0xRecipientAddress' as `0x${string}`,
  value: parseEther('0.1'), // 0.1 ETH
  // Add other transaction parameters as needed
  // data: '0x...', // For contract interactions
  // gas: 21000n, // Optional gas limit
};

// Prepare the transaction (this will estimate gas, get nonce, etc.)
const preparedTx = await publicClient.prepareTransactionRequest({
  ...transactionRequest,
  chain,
  account: '0xYourWalletAddress' as `0x${string}`,
});

console.log('Transaction prepared:', preparedTx);
```

## Step 3: Key Share Management for Signing

The approach for signing transactions depends on how you created your wallet:

### With Automatic Backup (Recommended)

If you created your wallet with `backUpToDynamic: true`, you can sign directly without retrieving key shares:

```typescript theme={"system"}
// ✅ Simple signing - no externalServerKeyShares needed
const signedTx = await evmClient.signTransaction({
  walletMetadata,
  transaction: preparedTx,
  password: 'your-wallet-password', // Only if wallet was created with password
});

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

### With Manual Backup

If you created your wallet with `backUpToDynamic: false`, you must retrieve and provide external key shares:

```typescript theme={"system"}
// Retrieve your stored key shares from your secrets vault
const externalServerKeyShares = await vault.read(`wallet:${accountAddress}/shares`);

// Sign the transaction with external key shares
const signedTx = await evmClient.signTransaction({
  walletMetadata,
  externalServerKeyShares,
  transaction: preparedTx,
});

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

**Password Handling Notes:**

* If your wallet was created **without a password**, omit the `password` parameter
* If your wallet was created **with a password**, you must provide it for all operations
* The password parameter is always optional in the API, but required if the wallet is password-protected

## Step 4: Send the Transaction

Send the signed transaction to the network:

```typescript theme={"system"}
import { createWalletClient } from 'viem';

const walletClient = createWalletClient({
  chain: base,
  transport: http('https://mainnet.base.org'),
  account: '0xYourWalletAddress' as `0x${string}`,
});

const txHash = await walletClient.sendRawTransaction({
  serializedTransaction: signedTx,
});

console.log('Transaction hash:', txHash);
console.log('Block explorer URL:', `https://basescan.org/tx/${txHash}`);
```

## Complete Example: Send ETH

Here's a complete example that sends ETH from one address to another:

```typescript theme={"system"}
import { authenticatedEvmClient } from './client';
import { http, createWalletClient } from 'viem';
import { base } from 'viem/chains';
import { parseEther } from 'viem/utils';

export const sendEth = async ({
  fromAddress,
  toAddress,
  amount,
  password,
}: {
  fromAddress: string;
  toAddress: string;
  amount: string; // Amount in ETH (e.g., "0.1")
  password?: string;
}) => {
  const evmClient = await authenticatedEvmClient();

  // Load walletMetadata + shares from where you persisted them at creation time
  const walletMetadata = JSON.parse(await redis.get(`wallet:${fromAddress}`));
  const externalServerKeyShares = await vault.read(`wallet:${fromAddress}/shares`);

  // Create public client for transaction preparation
  const publicClient = evmClient.createViemPublicClient({
    chain: base,
    rpcUrl: 'https://mainnet.base.org',
  });

  // Prepare transaction
  const preparedTx = await publicClient.prepareTransactionRequest({
    to: toAddress as `0x${string}`,
    value: parseEther(amount),
    chain: base,
    account: fromAddress as `0x${string}`,
  });

  // Sign transaction
  const signedTx = await evmClient.signTransaction({
    walletMetadata,
    externalServerKeyShares,
    transaction: preparedTx,
    password,
  });

  // Send transaction
  const walletClient = createWalletClient({
    chain: base,
    transport: http('https://mainnet.base.org'),
    account: fromAddress as `0x${string}`,
  });

  const txHash = await walletClient.sendRawTransaction({
    serializedTransaction: signedTx,
  });

  return txHash;
};

// Usage
const txHash = await sendEth({
  fromAddress: '0xYourWalletAddress',
  toAddress: '0xRecipientAddress',
  amount: '0.1',
  password: 'your-password',
});

console.log('Transaction sent:', txHash);
```

## Step 5: Handle Transaction Errors

Implement proper error handling for transaction signing and sending:

```typescript theme={"system"}
try {
  const signedTx = await evmClient.signTransaction({
    walletMetadata,
    externalServerKeyShares,
    transaction: preparedTx,
  });

  const txHash = await walletClient.sendRawTransaction({
    serializedTransaction: signedTx,
  });

  console.log('Transaction successful:', txHash);
} catch (error) {
  if (error.message.includes('insufficient funds')) {
    console.error('Insufficient balance for transaction');
  } else if (error.message.includes('nonce')) {
    console.error('Nonce error - transaction may have been sent already');
  } else if (error.message.includes('gas')) {
    console.error('Gas estimation failed');
  } else {
    console.error('Transaction failed:', error.message);
  }
}
```

## Best Practices

1. **Gas Estimation**: Always let viem estimate gas automatically unless you have specific requirements
2. **Nonce Management**: Use viem's transaction preparation to handle nonces automatically
3. **Error Handling**: Implement comprehensive error handling for different failure scenarios
4. **Transaction Monitoring**: Monitor transaction status after sending
5. **Security**: Never expose key shares in client-side code

## Common Transaction Types

### Simple ETH Transfer

```typescript theme={"system"}
const transactionRequest = {
  to: '0xRecipientAddress',
  value: parseEther('0.1'),
};
```

### Contract Interaction

```typescript theme={"system"}
const transactionRequest = {
  to: '0xContractAddress',
  data: '0xFunctionSelector...', // Contract function call data
  value: parseEther('0'), // For payable functions
};
```

### Gas Optimization

```typescript theme={"system"}
const transactionRequest = {
  to: '0xRecipientAddress',
  value: parseEther('0.1'),
  gas: 21000n, // Explicit gas limit for simple transfers
};
```

## Next Steps

Now that you can sign transactions, you can:

* [Sign messages](/node/evm/sign-messages)
* [Verify message signatures](/node/evm/verify-messages)
* [Import private keys](/node/evm/import-private-keys)
* [Use imported wallets](/node/evm/use-imported-wallets)
