|
| 1 | +--- |
| 2 | +title: Execute Function |
| 3 | +--- |
| 4 | + |
| 5 | +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. |
| 6 | + |
| 7 | +## Function Parameters |
| 8 | + |
| 9 | +The execute function receives two parameters: |
| 10 | + |
| 11 | +<ParamField path="params" type="object" required> |
| 12 | + Object containing the ability parameters |
| 13 | + <Expandable title="properties"> |
| 14 | + <ParamField path="abilityParams" type="object"> |
| 15 | + Parameters matching your `abilityParamsSchema` definition |
| 16 | + </ParamField> |
| 17 | + </Expandable> |
| 18 | +</ParamField> |
| 19 | + |
| 20 | +<ParamField path="abilityContext" type="object" required> |
| 21 | + Context object provided by the SDK with helpers and metadata |
| 22 | + <Expandable title="properties"> |
| 23 | + <ParamField path="succeed" type="function"> |
| 24 | + Helper function to return a success result matching your `executeSuccessSchema` |
| 25 | + </ParamField> |
| 26 | + <ParamField path="fail" type="function"> |
| 27 | + Helper function to return a failure result matching your `executeFailSchema` |
| 28 | + </ParamField> |
| 29 | + <ParamField path="delegation" type="object"> |
| 30 | + Contains `delegatorPkpInfo` with the Vincent Wallet address executing the ability |
| 31 | + </ParamField> |
| 32 | + <ParamField path="policiesContext" type="object"> |
| 33 | + Policy evaluation results and commit functions |
| 34 | + </ParamField> |
| 35 | + <ParamField path="appId" type="number"> |
| 36 | + ID of the Vincent App executing this Ability |
| 37 | + </ParamField> |
| 38 | + <ParamField path="appVersion" type="number"> |
| 39 | + Version of the Vincent App |
| 40 | + </ParamField> |
| 41 | + </Expandable> |
| 42 | +</ParamField> |
| 43 | + |
| 44 | +## Response Schemas |
| 45 | + |
| 46 | +### Success Response |
| 47 | + |
| 48 | +<ParamField path="executeSuccessSchema" type="ZodSchema" required> |
| 49 | + 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. |
| 50 | +</ParamField> |
| 51 | + |
| 52 | +### Failure Response |
| 53 | + |
| 54 | +<ParamField path="executeFailSchema" type="ZodSchema" required> |
| 55 | + 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. |
| 56 | +</ParamField> |
| 57 | + |
| 58 | +<Tabs> |
| 59 | + <Tab title="Success Schema"> |
| 60 | + ```typescript |
| 61 | + import { createVincentAbility } from '@lit-protocol/vincent-ability-sdk'; |
| 62 | + import { z } from 'zod'; |
| 63 | + |
| 64 | + const vincentAbility = createVincentAbility({ |
| 65 | + // ... other ability definitions |
| 66 | + |
| 67 | + executeSuccessSchema: z.object({ |
| 68 | + transactionHash: z.string(), |
| 69 | + policyCommitResults: z.record(z.any()).optional(), |
| 70 | + amountTransferred: z.number().optional(), |
| 71 | + gasUsed: z.number().optional(), |
| 72 | + }), |
| 73 | + }); |
| 74 | + ``` |
| 75 | + </Tab> |
| 76 | + <Tab title="Failure Schema"> |
| 77 | + ```typescript |
| 78 | + import { createVincentAbility } from '@lit-protocol/vincent-ability-sdk'; |
| 79 | + import { z } from 'zod'; |
| 80 | + |
| 81 | + const vincentAbility = createVincentAbility({ |
| 82 | + // ... other ability definitions |
| 83 | + |
| 84 | + executeFailSchema: z.object({ |
| 85 | + error: z.string(), |
| 86 | + errorCode: z.string().optional(), |
| 87 | + reason: z.string().optional(), |
| 88 | + revertReason: z.string().optional(), |
| 89 | + }), |
| 90 | + }); |
| 91 | + ``` |
| 92 | + </Tab> |
| 93 | +</Tabs> |
| 94 | + |
| 95 | +<Note> |
| 96 | +If any unhandled error occurs during execution, the Vincent Ability SDK automatically returns a fail result with the error message. |
| 97 | +</Note> |
| 98 | + |
| 99 | +## Executing Vincent Policy Commit Functions |
| 100 | + |
| 101 | +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. |
| 102 | + |
| 103 | +<Warning> |
| 104 | +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. |
| 105 | +</Warning> |
| 106 | + |
| 107 | +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: |
| 108 | + |
| 109 | +```typescript |
| 110 | +import { createVincentAbility } from '@lit-protocol/vincent-ability-sdk'; |
| 111 | +import { createErc20TransferTransaction } from './my-ability-code'; |
| 112 | + |
| 113 | +const vincentAbility = createVincentAbility({ |
| 114 | + // ... other ability definitions |
| 115 | + |
| 116 | + execute: async ({ abilityParams }, abilityContext) => { |
| 117 | + const { tokenAddress, amountToSend, recipientAddress } = abilityParams; |
| 118 | + const { policiesContext } = abilityContext; |
| 119 | + |
| 120 | + // previous code omitted for brevity |
| 121 | + const transferTransaction = createErc20TransferTransaction( |
| 122 | + tokenAddress, |
| 123 | + recipientAddress, |
| 124 | + amountToSend |
| 125 | + ); |
| 126 | + |
| 127 | + const transferTransactionHash = await transferTransaction.send(); |
| 128 | + |
| 129 | + const spendingLimitPolicyContext = |
| 130 | + policiesContext.allowedPolicies['@lit-protocol/vincent-policy-spending-limit']; |
| 131 | + |
| 132 | + let spendTransactionHash: string | undefined; |
| 133 | + |
| 134 | + if (spendingLimitPolicyContext !== undefined) { |
| 135 | + const commitResult = await spendingLimitPolicyContext.commit({ |
| 136 | + spentAmount: amountToSend, |
| 137 | + tokenAddress, |
| 138 | + }); |
| 139 | + |
| 140 | + if (commitResult.allow) { |
| 141 | + spendTransactionHash = commitResult.result.spendTransactionHash; |
| 142 | + } else { |
| 143 | + return abilityContext.fail({ |
| 144 | + error: commitResult.error ?? 'Unknown error occurred while committing spending limit policy', |
| 145 | + }); |
| 146 | + } |
| 147 | + } |
| 148 | + |
| 149 | + return abilityContext.succeed({ |
| 150 | + transferTransactionHash, |
| 151 | + spendTransactionHash, |
| 152 | + }); |
| 153 | + }, |
| 154 | +}); |
| 155 | +``` |
| 156 | + |
| 157 | +## Example Implementation |
| 158 | + |
| 159 | +<Accordion title="Full token transfer ability implementation"> |
| 160 | + ```typescript |
| 161 | + import { createVincentAbility } from '@lit-protocol/vincent-ability-sdk'; |
| 162 | + import { z } from 'zod'; |
| 163 | + |
| 164 | + import { createErc20TransferTransaction } from './my-ability-code'; |
| 165 | + |
| 166 | + const vincentAbility = createVincentAbility({ |
| 167 | + // ... other ability definitions |
| 168 | + |
| 169 | + executeSuccessSchema: z.object({ |
| 170 | + transferTransactionHash: z.string(), |
| 171 | + spendTransactionHash: z.string().optional(), |
| 172 | + }), |
| 173 | + |
| 174 | + executeFailSchema: z.object({ |
| 175 | + error: z.string(), |
| 176 | + errorCode: z.string().optional(), |
| 177 | + revertReason: z.string().optional(), |
| 178 | + transferTransaction: z.object({ |
| 179 | + to: z.string(), |
| 180 | + value: z.string(), |
| 181 | + data: z.string(), |
| 182 | + gasLimit: z.string(), |
| 183 | + gasPrice: z.string(), |
| 184 | + maxFeePerGas: z.string(), |
| 185 | + maxPriorityFeePerGas: z.string(), |
| 186 | + nonce: z.number(), |
| 187 | + chainId: z.number(), |
| 188 | + type: z.number(), |
| 189 | + }).optional(), |
| 190 | + }), |
| 191 | + |
| 192 | + execute: async ({ abilityParams }, abilityContext) => { |
| 193 | + const { tokenAddress, amountToSend, recipientAddress } = abilityParams; |
| 194 | + |
| 195 | + const transferTransaction = createErc20TransferTransaction( |
| 196 | + tokenAddress, |
| 197 | + recipientAddress, |
| 198 | + amountToSend, |
| 199 | + ); |
| 200 | + |
| 201 | + let estimatedGas; |
| 202 | + try { |
| 203 | + // Estimate gas to catch potential revert reasons early |
| 204 | + estimatedGas = await transferTransaction.estimateGas(); |
| 205 | + } catch (error) { |
| 206 | + // Handle gas estimation failures (transaction would revert) |
| 207 | + if (error.code === 'UNPREDICTABLE_GAS_LIMIT') { |
| 208 | + return abilityContext.fail({ |
| 209 | + error: 'Transaction reverted during gas estimation/transaction simulation', |
| 210 | + errorCode: error.code, |
| 211 | + revertReason: error.reason || 'Unknown revert reason', |
| 212 | + transferTransaction, |
| 213 | + }); |
| 214 | + } |
| 215 | + |
| 216 | + // Let the Vincent Ability SDK handle the error |
| 217 | + throw error; |
| 218 | + } |
| 219 | + |
| 220 | + const transferTransactionHash = await transferTransaction.send(); |
| 221 | + |
| 222 | + // Handle Policy commits |
| 223 | + const spendingLimitPolicyContext = |
| 224 | + abilityContext.policiesContext?.allowedPolicies?.['@lit-protocol/vincent-policy-spending-limit']; |
| 225 | + |
| 226 | + let spendTransactionHash: string | undefined; |
| 227 | + |
| 228 | + if (spendingLimitPolicyContext !== undefined) { |
| 229 | + const commitResult = await spendingLimitPolicyContext.commit({ |
| 230 | + spentAmount: amountToSend, |
| 231 | + tokenAddress, |
| 232 | + }); |
| 233 | + |
| 234 | + if (commitResult.allow) { |
| 235 | + spendTransactionHash = commitResult.result.spendTransactionHash; |
| 236 | + } else { |
| 237 | + return abilityContext.fail({ |
| 238 | + error: commitResult.error ?? 'Unknown error occurred while committing spending limit policy', |
| 239 | + }); |
| 240 | + } |
| 241 | + } |
| 242 | + |
| 243 | + return abilityContext.succeed({ |
| 244 | + transferTransactionHash, |
| 245 | + spendTransactionHash, |
| 246 | + }); |
| 247 | + }, |
| 248 | + }); |
| 249 | + ``` |
| 250 | +</Accordion> |
| 251 | + |
| 252 | +## Next Steps |
| 253 | + |
| 254 | +<CardGroup cols={2}> |
| 255 | + <Card title="Supporting Policies" icon="shield" href="/ability/supporting-policies"> |
| 256 | + Learn how to integrate Vincent Policies with your Ability |
| 257 | + </Card> |
| 258 | + <Card title="Publishing" icon="upload" href="/ability/publishing"> |
| 259 | + Deploy and publish your Ability to the Vincent Registry |
| 260 | + </Card> |
| 261 | +</CardGroup> |
0 commit comments