This library was generated with Nx.
Run nx build ability-sdk
to build the library.
This library provides a type-safe lifecycle system for defining and composing policies and abilities, with strong TypeScript inference support throughout.
precheck
and execute
lifecycle methods.allow()
/ deny()
and succeed()
/ fail()
methods, with schema-safe return typing.createVincentPolicy
, createVincentAbilityPolicy
, and createVincentAbility
.commit()
on a Policy from within a AbilityPolicies can define a commit()
lifecycle method to finalize changes once an ability executes successfully. These commit()
functions are injected automatically into the allowedPolicies
object of the AbilityContext
.
import { z } from 'zod';
import { createVincentPolicy } from '@lit-protocol/vincent-ability-sdk';
import { getAmountSpentToday, adjustDailySpendAmount } from '@my-org/spending-limit-client';
export const dailySpendPolicy = createVincentPolicy({
ipfsCid: 'policy-committable',
packageName: '@lit-protocol/max-spend-policy',
abilityParamsSchema: z.object({
buySomething: z.boolean(),
buyAmount: z.number(),
}),
userParamsSchema: z.object({
perBuyLimit: z.number(),
maxAmountPerDay: z.number(),
}),
evalAllowResultSchema: z.object({
ok: z.boolean(),
amountRemaining: z.number(),
amountToSpend: z.number(),
}),
evalDenyResultSchema: z.union([
z.object({
reason: z.literal('Buy amount request exceeds per-buy limit'),
buyAmount: z.number(),
}),
z.object({
reason: z.enum(['Buy amount request exceeds max amount per day']),
buyAmount: z.number(),
amountSpentToday: z.number(),
amountRemaining: z.number(),
}),
]),
commitParamsSchema: z.object({ amountSpent: z.number() }),
commitAllowResultSchema: z.object({
transactionId: z.string(),
timestamp: z.number(),
}),
commitDenyResultSchema: z.object({
errorCode: z.number().optional(),
message: z.string(),
}),
evaluate: async ({ abilityParams, userParams }, context) => {
const { maxAmountPerDay, perBuyLimit } = userParams;
const { buyAmount } = abilityParams;
if (buyAmount > perBuyLimit) {
return context.deny({
reason: 'Buy amount request exceeds per-buy limit',
buyAmount,
});
}
const amountSpentToday = await getAmountSpentToday(context.delegation.delegator);
const amountRemaining = maxAmountPerDay - amountSpentToday;
if (buyAmount > amountRemaining) {
return context.deny({
reason: 'Buy amount request exceeds max amount per day',
amountSpentToday,
buyAmount,
amountRemaining,
});
}
return context.allow({
ok: true,
amountRemaining,
amountToSpend: buyAmount,
});
},
commit: async ({ amountSpent }, context) => {
try {
const spendCommitResult: { transactionId: string; timestamp: number } =
await adjustDailySpendAmount(context.delegation.delegator, amountSpent);
return context.allow(spendCommitResult);
} catch (e: unknown) {
if (e instanceof Error) {
if ('errorCode' in e) {
return context.deny({
errorCode: e.errorCode as number,
message: e.message,
});
} else {
return context.deny({ message: e.message });
}
}
return context.deny({ message: String(e) });
}
},
});
import { z } from 'zod';
import {
createVincentAbilityPolicy,
createVincentAbility,
} from '@lit-protocol/vincent-ability-sdk';
import { dailySpendPolicy } from '@lit-protocol/max-spend-policy';
import uniswapV3Client from '@uniswap/v3-sdk';
const abilityParamsSchema = z.object({
buy: z.boolean(),
buyAmount: z.number(),
});
export const myTokenSwapAbility = createVincentAbility({
packageName: 'tokenswapability',
abilityDescription: 'Token Swap Ability',
abilityParamsSchema,
supportedPolicies: [
createVincentAbilityPolicy({
abilityParamsSchema,
PolicyConfig: dailySpendPolicy,
abilityParameterMappings: { buy: 'buyAmount' },
}),
],
executeSuccessSchema: z.object({
message: z.string(),
amountSpent: z.number().optional(),
spendCommitResult: z
.object({
transactionId: z.string(),
timestamp: z.number(),
})
.optional(),
}),
executeFailSchema: z.object({ error: z.string(), message: z.string() }),
async execute(abilityParams, { succeed, fail, policiesContext }) {
const spendPolicyContext = policiesContext.allowedPolicies['@lit-protocol/max-spend-policy'];
const amountSpent: number = await uniswapV3Client.performSwap({});
if (spendPolicyContext) {
const spendCommitResult = await spendPolicyContext.commit({
amountSpent,
});
if (!spendCommitResult.allow) {
return fail({
error: `Policy commit denied with code ${spendCommitResult.result.errorCode}`,
message: 'Ability executed but policy commit denied',
});
}
if (spendCommitResult.allow) {
return succeed({
amountSpent,
spendCommitResult: spendCommitResult.result,
message: 'Ability executed and spending limit policy commit completed',
});
}
}
return succeed({
message: 'Ability executed for user without enabled spending limit',
});
},
});
Ability and policy authors should export the result of createVincentPolicy()
/ createVincentAbility()