Skip to main content
The execute function contains your Ability’s core logic and runs in the Lit Action environment with access to signing capabilities. This is where the actual work happens.

Function Parameters

The execute function receives two parameters:
params
object
required
Object containing the ability parameters
abilityContext
object
required
Context object provided by the SDK with helpers and metadata

Response Schemas

Success Response

executeSuccessSchema
ZodSchema
required
A Zod schema that defines the structure of successful execution results. Include details about the completed operation, such as transaction hashes, amounts transferred, or other relevant outcomes.

Failure Response

executeFailSchema
ZodSchema
required
A Zod schema that defines the structure of failed execution results. Include error details to help the executor understand what went wrong, including error codes, reasons, and relevant context.
  • Success Schema
  • Failure Schema
import { createVincentAbility } from '@lit-protocol/vincent-ability-sdk';
import { z } from 'zod';

const vincentAbility = createVincentAbility({
  // ... other ability definitions

  executeSuccessSchema: z.object({
    transactionHash: z.string(),
    policyCommitResults: z.record(z.any()).optional(),
    amountTransferred: z.number().optional(),
    gasUsed: z.number().optional(),
  }),
});
If any unhandled error occurs during execution, the Vincent Ability SDK automatically returns a fail result with the error message.

Executing Vincent Policy Commit Functions

After your ability’s execute function successfully completes, the last step should be calling the commit functions for any supported Vincent Policies that have them. These commit functions allow policies to update their internal state based on what actions your ability performed.
Your ability’s execute function should always call the commit functions for any supported Vincent Policies that have a commit function defined. Failing to do so could cause the Vincent Policies to operate incorrectly.
For our token transfer ability example, after successfully executing the transfer, we call the Vincent spending limit policy’s commit function to update the amount spent:
import { createVincentAbility } from '@lit-protocol/vincent-ability-sdk';
import { createErc20TransferTransaction } from './my-ability-code';

const vincentAbility = createVincentAbility({
  // ... other ability definitions

  execute: async ({ abilityParams }, abilityContext) => {
    const { tokenAddress, amountToSend, recipientAddress } = abilityParams;
    const { policiesContext } = abilityContext;

    // previous code omitted for brevity
    const transferTransaction = createErc20TransferTransaction(
      tokenAddress,
      recipientAddress,
      amountToSend
    );

    const transferTransactionHash = await transferTransaction.send();

    const spendingLimitPolicyContext =
      policiesContext.allowedPolicies['@lit-protocol/vincent-policy-spending-limit'];

    let spendTransactionHash: string | undefined;

    if (spendingLimitPolicyContext !== undefined) {
      const commitResult = await spendingLimitPolicyContext.commit({
        spentAmount: amountToSend,
        tokenAddress,
      });

      if (commitResult.allow) {
        spendTransactionHash = commitResult.result.spendTransactionHash;
      } else {
        return abilityContext.fail({
          error: commitResult.error ?? 'Unknown error occurred while committing spending limit policy',
        });
      }
    }

    return abilityContext.succeed({
      transferTransactionHash,
      spendTransactionHash,
    });
  },
});

Example Implementation

import { createVincentAbility } from '@lit-protocol/vincent-ability-sdk';
import { z } from 'zod';

import { createErc20TransferTransaction } from './my-ability-code';

const vincentAbility = createVincentAbility({
  // ... other ability definitions

  executeSuccessSchema: z.object({
    transferTransactionHash: z.string(),
    spendTransactionHash: z.string().optional(),
  }),

  executeFailSchema: z.object({
    error: z.string(),
    errorCode: z.string().optional(),
    revertReason: z.string().optional(),
    transferTransaction: z.object({
      to: z.string(),
      value: z.string(),
      data: z.string(),
      gasLimit: z.string(),
      gasPrice: z.string(),
      maxFeePerGas: z.string(),
      maxPriorityFeePerGas: z.string(),
      nonce: z.number(),
      chainId: z.number(),
      type: z.number(),
    }).optional(),
  }),

  execute: async ({ abilityParams }, abilityContext) => {
    const { tokenAddress, amountToSend, recipientAddress } = abilityParams;

    const transferTransaction = createErc20TransferTransaction(
      tokenAddress,
      recipientAddress,
      amountToSend,
    );

    let estimatedGas;
    try {
      // Estimate gas to catch potential revert reasons early
      estimatedGas = await transferTransaction.estimateGas();
    } catch (error) {
      // Handle gas estimation failures (transaction would revert)
      if (error.code === 'UNPREDICTABLE_GAS_LIMIT') {
        return abilityContext.fail({
          error: 'Transaction reverted during gas estimation/transaction simulation',
          errorCode: error.code,
          revertReason: error.reason || 'Unknown revert reason',
          transferTransaction,
        });
      }

      // Let the Vincent Ability SDK handle the error
      throw error;
    }

    const transferTransactionHash = await transferTransaction.send();

    // Handle Policy commits
    const spendingLimitPolicyContext =
      abilityContext.policiesContext?.allowedPolicies?.['@lit-protocol/vincent-policy-spending-limit'];

    let spendTransactionHash: string | undefined;

    if (spendingLimitPolicyContext !== undefined) {
      const commitResult = await spendingLimitPolicyContext.commit({
        spentAmount: amountToSend,
        tokenAddress,
      });

      if (commitResult.allow) {
        spendTransactionHash = commitResult.result.spendTransactionHash;
      } else {
        return abilityContext.fail({
          error: commitResult.error ?? 'Unknown error occurred while committing spending limit policy',
        });
      }
    }

    return abilityContext.succeed({
      transferTransactionHash,
      spendTransactionHash,
    });
  },
});

Next Steps

I