> ## Documentation Index
> Fetch the complete documentation index at: https://litprotocol-vincent.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# EVM Transaction Signer

The EVM Transaction Signer Ability enables Vincent Apps to sign Ethereum Virtual Machine (EVM) transactions on behalf of Vincent Users using their Vincent Wallets. This enables Vincent Apps to interact with contracts even if there isn't an explicit Vincent Ability made for interacting with that contract.

This Vincent Ability also supports the [Contract Whitelist Policy](../policy/official-policies/contract-whitelist), which allows Vincent Users to restrict which contracts and functions can be called before this Ability signs transactions on their behalf.

## Key Features

* **Secure Transaction Signing**: Signs transactions using Vincent Wallets within a secure Trusted Execution Environment
* **Full Transaction Support**: Handles all EVM transaction types including legacy, EIP-2930, and EIP-1559
* **Policy Integration**: Supports the Contract Whitelist Policy for restricting what transactions can be signed

## How It Works

The EVM Transaction Signer Ability is built using the Vincent Ability SDK and operates in two phases:

<Steps>
  <Step title="Precheck Phase">
    Validates the transaction structure and runs policy checks:

    * Deserializes the provided serialized transaction using ethers.js
    * Validates all required fields are present (nonce, gasPrice, gasLimit, etc.)
    * Returns the deserialized unsigned transaction for confirmation
  </Step>

  <Step title="Execution Phase">
    If permitted by the evaluated Policies, signs the serialized transaction:

    * Signs the transaction using the Vincent App User's Vincent Wallet
    * Returns both the signed transaction hex string and the deserialized signed transaction object
  </Step>
</Steps>

## Getting Started

Depending on your role in the Vincent Ecosystem, you'll be interacting with this Ability in different ways. Click on the link below that matches your role to see how to get started:

* **Vincent App Developers**: If you're building a Vincent App that needs to sign transactions, go [here](#adding-the-ability-to-your-vincent-app).
* **Vincent App Delegatees**: If you're executing this ability on behalf of Vincent App Users, go [here](#executing-the-ability-as-a-vincent-app-delegatee).

## Adding the Ability to your Vincent App

If you want to enable your App Delegatees to sign transactions on behalf of your Vincent App Users, allowing them to interact with contracts that don't have an explicit Vincent Ability made for interacting with them, you can add this Ability to your App.

Adding Abilities to your Vincent App is done using the [Vincent App Dashboard](https://dashboard.heyvincent.ai/). Visit the [Create Vincent App](/app/quickstart) guide to learn more about how to add Abilities to your App during creation, or check out the [Updating Your App](/app/updating-apps) guide to learn how to add Abilities to an existing App.

## Executing the Ability as a Vincent App Delegatee

Before executing EVM transaction signing operations, the following conditions must be met. You can use the Ability's `precheck` function to check if these conditions are met, or you can check them manually.

* **Complete Transaction Object**: You must provide a complete EVM transaction object with all required fields including `to`, `value`, `data`, `chainId`, `nonce`, `gasLimit`, and appropriate gas pricing (`gasPrice` for legacy transactions, or `maxFeePerGas` and `maxPriorityFeePerGas` for EIP-1559 transactions).

* **Gas Balance**: The Vincent App User's Vincent Wallet must have sufficient native tokens (ETH, MATIC, etc.) to pay for the transaction gas fees specified in the transaction object.

* **Policy Compliance**: If the Vincent App User has enabled the [Contract Whitelist Policy](../policy/official-policies/contract-whitelist), the transaction must target a whitelisted contract and function. The transaction will be rejected if it attempts to interact with non-whitelisted contracts or functions.

<Info>
  To learn more about executing Vincent Abilities, see the [Executing Abilities](/ability/explaining-abilities) guide.
</Info>

<Note>
  Vincent App Users configure the Policies that govern Ability execution while consenting to the Vincent App. If the Vincent App you're a Delegatee for has enabled the Contract Whitelist Policy for this Ability, then what contracts and functions that can be called will be restricted to what the Vincent App User has whitelisted. To learn more about how the Policy works, and how it affects your execution of this Ability, see the [Contract Whitelist Policy](../policy/official-policies/contract-whitelist) documentation.
</Note>

### Executing the `precheck` Function

This Ability's `precheck` function is used to check if the provided unsigned serialized transaction is valid in structure and contains all the required fields to sign the transaction for submission to the blockchain network.

<Tabs>
  <Tab title="Parameters">
    Before executing the `precheck` function, you'll need to create the complete EVM transaction object (which must contain all required properties such as `to`, `value`, `data`, `chainId`, `nonce`, `gasLimit`, and gas pricing) you want the user's Vincent Wallet to sign, and serialize it into a hex string.

    The Ability expects this serialized transaction as the only parameter:

    ```typescript theme={null}
    {
      /**
       * The serialized transaction to be evaluated and signed.
       * This is the transaction object serialized into a hex string.
       * The transaction object must contain all required properties such as
       * `to`, `value`, `data`, `chainId`, `nonce`, `gasLimit`, and gas pricing.
       */
      serializedTransaction: string;
    }
    ```
  </Tab>

  <Tab title="Implementation">
    To execute `precheck`, you'll need to:

    * Create an instance of the `VincentAbilityClient` using the `getVincentAbilityClient` function (imported from `@lit-protocol/vincent-app-sdk/abilityClient`)
      * Pass in the Ability's `bundledVincentAbility` object (imported from `@lit-protocol/vincent-ability-evm-transaction-signer`)
      * Pass in the `ethersSigner` you'll be using to sign the request to Lit with your Delegatee private key
    * Create the transaction object you want the Vincent App User's Vincent Wallet to sign
    * Serialize the transaction object into a hex string using `ethers.utils.serializeTransaction`
    * Call the `precheck` function on the `VincentAbilityClient` instance, passing in the serialized transaction and the Vincent App User's Vincent Wallet address

    ```typescript theme={null}
    import { getVincentAbilityClient } from '@lit-protocol/vincent-app-sdk/abilityClient';
    import { bundledVincentAbility } from '@lit-protocol/vincent-ability-evm-transaction-signer';
    import { ethers } from 'ethers';

    // Create ability client
    const abilityClient = getVincentAbilityClient({
      bundledVincentAbility: bundledVincentAbility,
      ethersSigner: yourEthersSigner,
    });

    // Create a transaction - Example: ERC20 transfer on Base
    const transaction = {
      to: '0x4200000000000000000000000000000000000006', // Base WETH
      value: '0x00',
      data: '0xa9059cbb...', // ERC20 transfer function call
      chainId: 8453,
      nonce: 0,
      gasPrice: '0x...',
      gasLimit: '0x...',
    };

    // Serialize the transaction
    const serializedTx = ethers.utils.serializeTransaction(transaction);

    const precheckResult = await abilityClient.precheck(
      {
        serializedTransaction: serializedTx,
      },
      {
        delegatorPkpEthAddress: '0x...', // The Vincent App User's Vincent Wallet address that will sign the transaction
      },
    );

    if (precheckResult.success) {
      const { deserializedUnsignedTransaction } = precheckResult.result;
      console.log('Transaction validated:', deserializedUnsignedTransaction);
    } else {
      // Handle different types of failures
      if (precheckResult.runtimeError) {
        console.error('Runtime error:', precheckResult.runtimeError);
      }
      if (precheckResult.schemaValidationError) {
        console.error('Schema validation error:', precheckResult.schemaValidationError);
      }
      if (precheckResult.result) {
        console.error('Transaction validation failed:', precheckResult.result.error);
      }
    }
    ```
  </Tab>

  <Tab title="Response">
    **Success Response**

    A successful `precheck` response will contain the deserialized unsigned transaction object, which you can use to inspect the validated transaction details before signing:

    ```typescript theme={null}
    {
      deserializedUnsignedTransaction: {
        /**
         * The recipient address
         */
        to?: string;
        /**
         * The transaction nonce
         */
        nonce?: number;
        /**
         * The gas limit for the transaction
         */
        gasLimit: string;
        /**
         * Gas price for legacy transactions
         */
        gasPrice?: string;
        /**
         * Transaction data/input
         */
        data: string;
        /**
         * Transaction value in wei
         */
        value: string;
        /**
         * The chain ID
         */
        chainId: number;
        /**
         * Transaction type (0 for legacy, 2 for EIP-1559)
         */
        type?: number;
        /**
         * Access list for EIP-2930 transactions
         */
        accessList?: any[];
        /**
         * Priority fee for EIP-1559 transactions
         */
        maxPriorityFeePerGas?: string;
        /**
         * Max fee for EIP-1559 transactions
         */
        maxFeePerGas?: string;
      }
    }
    ```

    **Failure Response**

    A failure `precheck` response will contain:

    ```typescript theme={null}
    {
      /**
       * A string containing the error message if the precheck failed
       */
      error: string;
    }
    ```
  </Tab>
</Tabs>

### Executing the `execute` Function

This Ability's `execute` function signs the serialized transaction if permitted by the evaluated Policies.

<Tabs>
  <Tab title="Parameters">
    The `execute` function expects the same parameter as the `precheck` function:

    ```typescript theme={null}
    {
      /**
       * The serialized transaction to be evaluated and signed.
       * This is the transaction object serialized into a hex string.
       * The transaction object must contain all required properties such as
       * `to`, `value`, `data`, `chainId`, `nonce`, `gasLimit`, and gas pricing.
       */
      serializedTransaction: string;
    }
    ```
  </Tab>

  <Tab title="Implementation">
    The `execute` function can be called using the same `VincentAbilityClient` instance:

    ```typescript theme={null}
    const executeResult = await abilityClient.execute(
      {
        serializedTransaction: serializedTx,
      },
      {
        delegatorPkpEthAddress: '0x...', // The Vincent App User's Vincent Wallet address that will sign the transaction
      },
    );

    if (executeResult.success) {
      const { signedTransaction, deserializedSignedTransaction } = executeResult.result;
      console.log('Transaction signed successfully:', signedTransaction);
      console.log('Transaction details:', deserializedSignedTransaction);
    } else {
      // Handle different types of failures
      if (executeResult.runtimeError) {
        console.error('Runtime error:', executeResult.runtimeError);
      }
      if (executeResult.schemaValidationError) {
        console.error('Schema validation error:', executeResult.schemaValidationError);
      }
      if (executeResult.result) {
        console.error('Transaction signing failed:', executeResult.result.error);
      }
    }
    ```
  </Tab>

  <Tab title="Response">
    **Success Response**

    A successful `execute` response will contain the signed transaction hex and the deserialized signed transaction object, which you can use to inspect the signed transaction details before broadcasting:

    ```typescript theme={null}
    {
      /**
       * The signed transaction ready for broadcast
       */
      signedTransaction: string;
      /**
       * The deserialized signed transaction object
       */
      deserializedSignedTransaction: {
        /**
         * Transaction hash
         */
        hash?: string;
        /**
         * Recipient address
         */
        to: string;
        /**
         * Sender address
         */
        from: string;
        /**
         * Transaction nonce
         */
        nonce: number;
        /**
         * Gas limit
         */
        gasLimit: string;
        /**
         * Gas price for legacy transactions
         */
        gasPrice?: string;
        /**
         * Transaction data
         */
        data: string;
        /**
         * Transaction value in wei
         */
        value: string;
        /**
         * Chain ID
         */
        chainId: number;
        /**
         * Signature v component
         */
        v: number;
        /**
         * Signature r component
         */
        r: string;
        /**
         * Signature s component
         */
        s: string;
        /**
         * Transaction type
         */
        type?: number;
        /**
         * Access list for EIP-2930 transactions
         */
        accessList?: any[];
        /**
         * Priority fee for EIP-1559 transactions
         */
        maxPriorityFeePerGas?: string;
        /**
         * Max fee for EIP-1559 transactions
         */
        maxFeePerGas?: string;
      }
    }
    ```

    **Failure Response**

    A failure `execute` response will contain:

    ```typescript theme={null}
    {
      /**
       * A string containing the error message if the execution failed
       */
      error: string;
    }
    ```
  </Tab>
</Tabs>

## Important Considerations

<AccordionGroup>
  <Accordion title="Complete Transaction Object Required" icon="file">
    Both the `precheck` and `execute` functions require a complete unsigned serialized transaction to be provided. This Ability does not handle the `nonce` or gas related fields, so you'll need to provide these values in the transaction object you're serializing.
  </Accordion>

  <Accordion title="Policy Integration" icon="shield">
    This ability integrates with the Contract Whitelist Policy. If enabled, transactions will only be signed if they target whitelisted contracts and functions.
  </Accordion>

  <Accordion title="Transaction Types" icon="list">
    Supports all EVM transaction types including legacy transactions, EIP-2930 (access lists), and EIP-1559 (fee market) transactions.
  </Accordion>

  <Accordion title="Gas Management" icon="gas-pump">
    You must handle nonce management and gas estimation yourself. The Vincent Wallet must have sufficient native tokens to cover the gas fees specified in your transaction.
  </Accordion>
</AccordionGroup>

## Error Handling

<AccordionGroup>
  <Accordion title="Common Failure Scenarios" icon="bug">
    * **Invalid Serialized Transaction**: The provided serialized transaction is malformed or missing required fields
    * **Insufficient Gas Balance**: The Vincent App User's Vincent Wallet doesn't have enough native tokens for gas fees
    * **Policy Violations**: Transaction violates Contract Whitelist Policy (if enabled)
    * **Invalid Nonce**: Transaction nonce is incorrect or already used
    * **Gas Limit Too Low**: Specified gas limit is insufficient for transaction execution
    * **Invalid Chain ID**: Chain ID doesn't match the target network
  </Accordion>
</AccordionGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.