Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: distributed session keys #114

Merged
merged 3 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# @biconomy/sdk

## 0.0.10

### Patch Changes

- Added Distributed Session Keys w/ Ownable & Session examples

## 0.0.9

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@biconomy/sdk",
"version": "0.0.9",
"version": "0.0.10",
"author": "Biconomy",
"repository": "github:bcnmy/sdk",
"main": "./dist/_cjs/index.js",
Expand Down
2 changes: 2 additions & 0 deletions src/sdk/account/utils/Constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export const DefaultGasLimit = {
}

export const ERROR_MESSAGES = {
KEY_GEN_DATA_NOT_FOUND: "Key generation data is not available",
SIGNATURE_NOT_FOUND: "Signature not found from Dan",
FAILED_COMPUTE_ACCOUNT_ADDRESS:
"Failed to compute account address. Possible reasons:\n" +
"- The factory contract does not have the function 'computeAccountAddress'\n" +
Expand Down
18 changes: 8 additions & 10 deletions src/sdk/clients/createNexusSessionClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ describe("nexus.session.client", async () => {
account: smartSessionNexusClient.account,
signer: sessionKeyAccount,
moduleData: {
permissionId: cachedPermissionId
permissionIds: [cachedPermissionId]
}
})

Expand All @@ -177,11 +177,10 @@ describe("nexus.session.client", async () => {
)

const userOpHash = await useSmartSessionNexusClient.usePermission({
actions: [
calls: [
{
target: testAddresses.Counter,
value: 0n,
callData: encodeFunctionData({
to: testAddresses.Counter,
data: encodeFunctionData({
abi: CounterAbi,
functionName: "incrementNumber",
args: []
Expand Down Expand Up @@ -212,7 +211,7 @@ describe("nexus.session.client", async () => {
account: nexusClient.account,
signer: sessionKeyAccount,
moduleData: {
permissionId: cachedPermissionId
permissionIds: [cachedPermissionId]
}
})

Expand Down Expand Up @@ -246,11 +245,10 @@ describe("nexus.session.client", async () => {

expect(
useSmartSessionNexusClient.usePermission({
actions: [
calls: [
{
target: testAddresses.Counter,
value: 0n,
callData: encodeFunctionData({
to: testAddresses.Counter,
data: encodeFunctionData({
abi: CounterAbi,
functionName: "decrementNumber"
})
Expand Down
4 changes: 1 addition & 3 deletions src/sdk/clients/createNexusSessionClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,4 @@ import { type NexusClientConfig, createNexusClient } from "./createNexusClient"
export type NexusSessionClientConfig = NexusClientConfig & {
accountAddress: Address
}
export const createNexusSessionClient = async (
parameters: NexusSessionClientConfig
) => await createNexusClient({ ...parameters })
export const createNexusSessionClient = createNexusClient
10 changes: 4 additions & 6 deletions src/sdk/clients/decorators/dan/decorators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,8 @@ export function danActions() {
chain extends Chain | undefined
>(
client: Client<Transport, chain, TModularSmartAccount>
): DanActions<TModularSmartAccount> => {
return {
keyGen: (args) => keyGen(client, args),
sigGen: (parameters) => sigGen(client, parameters)
}
}
): DanActions<TModularSmartAccount> => ({
keyGen: (args) => keyGen(client, args),
sigGen: (parameters) => sigGen(client, parameters)
})
}
4 changes: 3 additions & 1 deletion src/sdk/modules/smartSessionsValidator/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,15 @@ export type SmartSessionModeType =
*/
export type UsePermissionModuleData = {
/** The permission ID for the session. */
permissionId: Hex
permissionIds: Hex[]
/** The mode of the smart session. */
mode?: SmartSessionModeType
/** Data for enabling the session. */
enableSessionData?: EnableSessionData
/** Key generation data for the session. */
keyGenData?: KeyGenData
/** The index of the permission ID to use for the session. Defaults to 0. */
permissionIdIndex?: number
}

type OptionalSessionKeyData = OneOf<
Expand Down
28 changes: 25 additions & 3 deletions src/sdk/modules/smartSessionsValidator/decorators/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import type { Chain, Client, Hash, Transport } from "viem"
import { danActions } from "../../../clients/decorators/dan/decorators"
import type { ModularSmartAccount, Module } from "../../utils/Types"
import type { GrantPermissionResponse } from "../Types"
import type { SmartSessionModule } from "../toSmartSessionsValidator"
import {
type GrantPermissionParameters,
grantPermission
} from "./grantPermission"
import { type TrustAttestersParameters, trustAttesters } from "./trustAttesters"
import {
type DanClient,
type UseDistributedPermissionParameters,
useDistributedPermission
} from "./useDistributedPermission"
import { type UsePermissionParameters, usePermission } from "./usePermission"

/**
* Defines the shape of actions available for creating smart sessions.
*
Expand Down Expand Up @@ -54,6 +60,15 @@ export type SmartSessionUseActions<
usePermission: (
args: UsePermissionParameters<TModularSmartAccount>
) => Promise<Hash>
/**
* Uses a session to perform multiple actions.
*
* @param args - Parameters for using a session.
* @returns A promise that resolves to the transaction hash.
*/
useDistributedPermission: (
args: UseDistributedPermissionParameters<TModularSmartAccount>
) => Promise<Hash>
}

/**
Expand All @@ -79,17 +94,24 @@ export function smartSessionCreateActions(_: Module) {
* @param smartSessionsModule - The smart sessions module to be set on the client's account.
* @returns A function that takes a client and returns SmartSessionUseActions.
*/
export function smartSessionUseActions(smartSessionsModule: Module) {
export function smartSessionUseActions(
smartSessionsModule: SmartSessionModule
) {
return <TModularSmartAccount extends ModularSmartAccount | undefined>(
client: Client<Transport, Chain | undefined, TModularSmartAccount>
): SmartSessionUseActions<TModularSmartAccount> => {
client?.account?.setModule(smartSessionsModule)
return {
usePermission: (args) => usePermission(client, args)
usePermission: (args) => usePermission(client, args),
useDistributedPermission: (args) => {
const danClient = client.extend(danActions()) as unknown as DanClient
return useDistributedPermission(danClient, args)
}
}
}
}

export * from "./grantPermission"
export * from "./trustAttesters"
export * from "./usePermission"
export * from "./useDistributedPermission"
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ describe("modules.smartSessions.decorators", async () => {
account: nexusClient.account,
signer: sessionKeyAccount,
moduleData: {
permissionId: "0x"
permissionIds: []
}
})

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import type { Chain, Client, Hex, Transport } from "viem"
import { type BundlerClient, sendUserOperation } from "viem/account-abstraction"
import { getAction } from "viem/utils"
import { ERROR_MESSAGES } from "../../../account"
import { AccountNotFoundError } from "../../../account/utils/AccountNotFound"
import type { Call } from "../../../account/utils/Types"
import type { Signer } from "../../../account/utils/toSigner"
import type { DanActions } from "../../../clients/decorators/dan/decorators"
import { parseModule } from "../../utils/Helpers"
import type { ModularSmartAccount } from "../../utils/Types"
import type { SmartSessionModule } from "../toSmartSessionsValidator"

/**
* Parameters for using a smart session to execute actions.
*
* @template TModularSmartAccount - Type of the modular smart account, extending ModularSmartAccount or undefined.
*/
export type UseDistributedPermissionParameters<
TModularSmartAccount extends ModularSmartAccount | undefined
> = {
/** Array of executions to perform in the session. Allows for batch transactions if the session is enabled for multiple actions. */
calls: Call[]
/** The maximum fee per gas unit the transaction is willing to pay. */
maxFeePerGas?: bigint
/** The maximum priority fee per gas unit the transaction is willing to pay. */
maxPriorityFeePerGas?: bigint
/** The nonce of the transaction. If not provided, it will be determined automatically. */
nonce?: bigint
/** The modular smart account to use for the session. If not provided, the client's account will be used. */
account?: TModularSmartAccount
/** The signer to use for the session. Defaults to the signer of the client. */
signer?: Signer
}

export type DanClient = Client<
Transport,
Chain | undefined,
ModularSmartAccount
> &
DanActions<ModularSmartAccount> &
BundlerClient

/**
* Executes actions using a smart session.
*
* This function allows for the execution of one or more actions within an enabled smart session.
* It can handle batch transactions if the session is configured for multiple actions.
*
* @template TModularSmartAccount - Type of the modular smart account, extending ModularSmartAccount or undefined.
* @param client - The client used to interact with the blockchain.
* @param parameters - Parameters for using the session, including actions to execute and optional gas settings.
* @returns A promise that resolves to the hash of the sent user operation.
*
* @throws {AccountNotFoundError} If no account is provided and the client doesn't have an associated account.
*
* @example
* ```typescript
* const result = await useDistributedPermission(nexusClient, {
* calls: [
* {
* to: '0x1234...',
* data: '0xabcdef...'
* }
* ],
* maxFeePerGas: 1000000000n
* });
* console.log(`Transaction hash: ${result}`);
* ```
*
* @remarks
* - Ensure that the session is enabled and has the necessary permissions for the actions being executed.
* - For batch transactions, all actions must be permitted within the same session.
* - The function uses the `sendUserOperation` method, which is specific to account abstraction implementations.
*/
export async function useDistributedPermission<
TModularSmartAccount extends ModularSmartAccount | undefined
>(
client: DanClient,
parameters: UseDistributedPermissionParameters<TModularSmartAccount>
): Promise<Hex> {
const { account: account_ = client.account } = parameters

if (!account_) {
throw new AccountNotFoundError({
docsPath: "/nexus-client/methods#sendtransaction"
})
}

const params = { ...parameters, account: account_ }

const preppedUserOp = await client.prepareUserOperation(params)
const sessionsModule = parseModule(client) as SmartSessionModule
const keyGenData = sessionsModule?.moduleData?.keyGenData

if (!keyGenData) {
throw new Error(ERROR_MESSAGES.KEY_GEN_DATA_NOT_FOUND)
}

const { signature } = await client.sigGen({ ...preppedUserOp, keyGenData })

if (!signature) {
throw new Error(ERROR_MESSAGES.SIGNATURE_NOT_FOUND)
}

const extendedSignature = sessionsModule.sigGen(signature)

return await getAction(
client,
sendUserOperation,
"sendUserOperation"
// @ts-ignore
)({ ...preppedUserOp, account: account_, signature: extendedSignature })
}
24 changes: 9 additions & 15 deletions src/sdk/modules/smartSessionsValidator/decorators/usePermission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import type { Chain, Client, Hex, Transport } from "viem"
import { sendUserOperation } from "viem/account-abstraction"
import { getAction } from "viem/utils"
import { AccountNotFoundError } from "../../../account/utils/AccountNotFound"
import type { Call } from "../../../account/utils/Types"
import type { Signer } from "../../../account/utils/toSigner"
import type { Execution, ModularSmartAccount } from "../../utils/Types"
import type { ModularSmartAccount } from "../../utils/Types"

/**
* Parameters for using a smart session to execute actions.
Expand All @@ -14,7 +15,7 @@ export type UsePermissionParameters<
TModularSmartAccount extends ModularSmartAccount | undefined
> = {
/** Array of executions to perform in the session. Allows for batch transactions if the session is enabled for multiple actions. */
actions: Execution[]
calls: Call[]
/** The maximum fee per gas unit the transaction is willing to pay. */
maxFeePerGas?: bigint
/** The maximum priority fee per gas unit the transaction is willing to pay. */
Expand Down Expand Up @@ -43,11 +44,10 @@ export type UsePermissionParameters<
* @example
* ```typescript
* const result = await usePermission(nexusClient, {
* actions: [
* calls: [
* {
* target: '0x1234...',
* value: 0n,
* callData: '0xabcdef...'
* to: '0x1234...',
* data: '0xabcdef...'
* }
* ],
* maxFeePerGas: 1000000000n
Expand All @@ -66,7 +66,7 @@ export async function usePermission<
client: Client<Transport, Chain | undefined, TModularSmartAccount>,
parameters: UsePermissionParameters<TModularSmartAccount>
): Promise<Hex> {
const { account: account_ = client.account, actions, ...rest } = parameters
const { account: account_ = client.account } = parameters

if (!account_) {
throw new AccountNotFoundError({
Expand All @@ -78,12 +78,6 @@ export async function usePermission<
client,
sendUserOperation,
"sendUserOperation"
)({
...rest,
calls: actions.map((action) => ({
to: action.target,
value: BigInt(action.value.toString()),
data: action.callData
}))
})
// @ts-ignore
)({ ...parameters, account: account_ })
}
Loading
Loading