Tutorial -- Transaction Automation
Refer to this code example while you follow along the tutorial. You can run the example by following instructions of the examples repo.
Installation
The examples repo already installed this, but normally you would install permissions with:
npm i @zerodev/permissionsOwner-Agent Architecture
There are multiple ways to use session keys. In this tutorial, we will use the popular "owner-agent" pattern:
- The "owner" is the owner of the master key.
- The "agent" is the entity that wants to use the session key.
In a typical setup, you might be wishing to automate transactions for your users from your server.
your user's master key might be connected to your DApp frontend, in which case your frontend is the "owner." You might be wishing
API
With a permissions validator, you are putting together:
- One signer
- Any number of policies
- (optionally) one action
Creating a signer
Start by creating a ECDSA signer:
const sessionPrivateKey = generatePrivateKey()
const sessionKeyAccount = privateKeyToAccount(sessionPrivateKey)
const sessionKeySigner = await toECDSASigner({
signer: sessionKeyAccount,
})Creating a number of policies
Now, let's create two policies:
- A "call policy" that checks that the user is minting an NFT.
- A "rate limit policy" that checks that the user executes this action once per month.
const sessionPrivateKey = generatePrivateKey()
const sessionKeyAccount = privateKeyToAccount(sessionPrivateKey)
const callPolicy = toCallPolicy({
policyVersion: CallPolicyVersion.V0_0_2,
permissions: [
{
target: contractAddress,
valueLimit: BigInt(0),
abi: contractABI,
functionName: "mint",
},
],
})
const rateLimitPolicy = toRateLimitPolicy({
count: 1,
interval: 60 * 60 * 24 * 30, // month in seconds
}),Composing signer and policies
Here comes the fun part -- we "compose" the signer and policies together into a single validator:
const sessionKeyValidator = await toPermissionValidator(publicClient, {
entryPoint,
kernelVersion,
signer: sessionKeySigner,
policies: [
callPolicy,
rateLimitPolicy,
],
}),Now, we have created a ECDSA session key that's subject to a call policy and a rate limit policy. Just like that!
Using the session key
Finally, we can set up the account with this session key as the signer. Note that if this is the first time that the session key is used, we need to enable the plugin.
const account = await createKernelAccount(publicClient, {
plugins: {
sudo: ecdsaValidator,
regular: sessionKeyValidator,
},
entryPoint,
kernelVersion,
})Now you can set up a Kernel client using this account, and start minting NFTs with this session key -- but only up to once a month!
Try running this script and see for yourself.
Storing Session Keys
Session keys (and permission validators in general) can be stored, by serializing them and then deserializing them later.
Code examples
There are two general patterns with storing session keys.
- The owner creates a session key for another agent to store & use. Check out this example.
- The agent creates a session key and asks the owner to "approve" it as a session key. Check out this example.
- In this flow, the owner never sees the private part of the session key, so it may be better for security.
Serializing a session key
const serializedSessionKey = await serializePermissionAccount(sessionKeyAccount, sessionPrivateKey)Note that sessionPrivateKey is optional. If the private key is not included, then you must provide the private key when you deserialize the session key.
De-serializing a session key
const sessionKeySigner = await toECDSASigner({
signer: privateKeyToAccount(sessionPrivateKey),
})
const sessionKeyAccount = await deserializePermissionAccount(
publicClient,
entryPoint,
kernelVersion,
serializedSessionKey,
sessionKeySigner,
)Note that sessionKeySigner is only needed if you did not include the private key in the serialized session key.