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: retain action data in session payload #141

Merged
merged 2 commits into from
Dec 6, 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
44 changes: 32 additions & 12 deletions src/sdk/clients/createNexusSessionClient.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { SmartSessionMode } from "@rhinestone/module-sdk"
import { http, type Address, type Chain, type Hex, toBytes, toHex } from "viem"
import type { LocalAccount, PublicClient } from "viem"
import { encodeFunctionData } from "viem"
Expand All @@ -13,8 +14,15 @@ import {
} from "../../test/testUtils"
import type { MasterClient, NetworkConfig } from "../../test/testUtils"
import { SMART_SESSIONS_ADDRESS } from "../constants"
import { isPermissionEnabled } from "../modules/smartSessionsValidator/Helpers"
import type { CreateSessionDataParams } from "../modules/smartSessionsValidator/Types"
import {
isPermissionEnabled,
parse,
stringify
} from "../modules/smartSessionsValidator/Helpers"
import type {
CreateSessionDataParams,
SessionData
} from "../modules/smartSessionsValidator/Types"
import {
smartSessionCreateActions,
smartSessionUseActions
Expand All @@ -36,7 +44,7 @@ describe("nexus.session.client", async () => {
let nexusAccountAddress: Address
let sessionKeyAccount: LocalAccount
let sessionPublicKey: Address
let cachedPermissionId: Hex
let cachedSessionData: string

let sessionsModule: Module

Expand Down Expand Up @@ -135,7 +143,19 @@ describe("nexus.session.client", async () => {

expect(createSessionsResponse.userOpHash).toBeDefined()
expect(createSessionsResponse.permissionIds).toBeDefined()
;[cachedPermissionId] = createSessionsResponse.permissionIds

// Prepare the session data to be stored by the dApp. This could be saved in a Database by the dApp, or client side in local storage.
const sessionData: SessionData = {
granter: nexusSessionClient?.account?.address as Hex,
sessionPublicKey,
description: `Session to increment a counter for ${testAddresses.Counter}`,
moduleData: {
...createSessionsResponse,
mode: SmartSessionMode.USE
}
}

cachedSessionData = stringify(sessionData)

const receipt = await nexusClient.waitForUserOperationReceipt({
hash: createSessionsResponse.userOpHash
Expand All @@ -146,12 +166,14 @@ describe("nexus.session.client", async () => {
const isEnabled = await isPermissionEnabled({
client: nexusClient.account.client as PublicClient,
accountAddress: nexusClient.account.address,
permissionId: cachedPermissionId
permissionId: createSessionsResponse.permissionIds[0]
})
expect(isEnabled).toBe(true)
}, 60000)

test("session signer should use session to increment a counter for a user (USE MODE)", async () => {
const sessionData = parse(cachedSessionData) as SessionData

const counterBefore = await testClient.readContract({
address: testAddresses.Counter,
abi: CounterAbi,
Expand All @@ -169,9 +191,7 @@ describe("nexus.session.client", async () => {
const usePermissionsModule = toSmartSessionsValidator({
account: smartSessionNexusClient.account,
signer: sessionKeyAccount,
moduleData: {
permissionIds: [cachedPermissionId]
}
moduleData: sessionData.moduleData
})

const useSmartSessionNexusClient = smartSessionNexusClient.extend(
Expand Down Expand Up @@ -209,12 +229,12 @@ describe("nexus.session.client", async () => {
}, 60000)

test("session signer is not allowed to send unauthorised action", async () => {
const sessionData = parse(cachedSessionData) as SessionData

const usePermissionsModule = toSmartSessionsValidator({
account: nexusClient.account,
signer: sessionKeyAccount,
moduleData: {
permissionIds: [cachedPermissionId]
}
moduleData: sessionData.moduleData
})

const smartSessionNexusClient = await createNexusSessionClient({
Expand All @@ -232,7 +252,7 @@ describe("nexus.session.client", async () => {
const isEnabled = await isPermissionEnabled({
client: testClient as unknown as PublicClient,
accountAddress: nexusClient.account.address,
permissionId: cachedPermissionId
permissionId: sessionData.moduleData.permissionIds[0]
})
expect(isEnabled).toBe(true)

Expand Down
11 changes: 5 additions & 6 deletions src/sdk/modules/smartSessionsValidator/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ export type SessionData = {

/** Module-specific data containing session configuration and permissions. */
moduleData: UsePermissionModuleData

/** Description of the session. Useful for keeping humancontext about the session. */
description?: string
}

export type GrantPermissionActionReturnParams = {
Expand All @@ -39,9 +42,7 @@ export type GrantPermissionActionReturnParams = {
export type GrantPermissionResponse = {
/** The hash of the user operation. */
userOpHash: Hex
/** Array of permission IDs for the created sessions. */
permissionIds: Hex[]
}
} & GrantPermissionActionReturnParams

/**
* Represents the possible modes for a smart session.
Expand All @@ -53,15 +54,13 @@ export type SmartSessionModeType =
* Represents the data structure for using a session module.
*/
export type UsePermissionModuleData = {
/** The permission ID for the session. */
permissionIds: Hex[]
/** The mode of the smart session. */
mode?: SmartSessionModeType
/** Data for enabling the session. */
enableSessionData?: EnableSessionData
/** The index of the permission ID to use for the session. Defaults to 0. */
permissionIdIndex?: number
}
} & GrantPermissionActionReturnParams

type OptionalSessionKeyData = OneOf<
| {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ export async function grantPermission<

return {
userOpHash: userOpHash,
permissionIds: actionResponse.permissionIds
...actionResponse
}
}
throw new Error("Error getting enable sessions action")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ describe("modules.smartSessions.dx", async () => {
let sessionKeyAccount: LocalAccount
let sessionPublicKey: Address

let zippedSessionDatum: string
let stringifiedSessionDatum: string
let sessionsModule: Module

beforeAll(async () => {
Expand Down Expand Up @@ -147,22 +147,23 @@ describe("modules.smartSessions.dx", async () => {
const sessionData: SessionData = {
granter: usersNexusClient.account.address,
sessionPublicKey,
description: `Session to increment a counter for ${testAddresses.Counter}`,
moduleData: {
permissionIds: createSessionsResponse.permissionIds,
...createSessionsResponse,
mode: SmartSessionMode.USE
}
}

// Zip the session data, and store it for later use by a dapp
zippedSessionDatum = stringify(sessionData)
stringifiedSessionDatum = stringify(sessionData)
}, 200000)

test("should demonstrate using a smart session from dapp's perspective", async () => {
// Now assume the user has left the dapp and the usersNexusClient signer is no longer available
// The following code demonstrates how a dapp can use the session to act on behalf of the user

// Unzip the session data
const usersSessionData = parse(zippedSessionDatum)
const usersSessionData = parse(stringifiedSessionDatum)

// Create a new Nexus client for the session
// This client will be used to interact with the smart contract account using the session key
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe("modules.smartSessions.sudo.policy", async () => {
let sessionKeyAccount: LocalAccount
let sessionPublicKey: Address

let zippedSessionDatum: string // Session data to be stored by the dApp
let stringifiedSessionDatum: string // Session data to be stored by the dApp

let sessionsModule: Module

Expand Down Expand Up @@ -130,22 +130,23 @@ describe("modules.smartSessions.sudo.policy", async () => {
const sessionData: SessionData = {
granter: usersNexusClient?.account?.address as Hex,
sessionPublicKey,
description: `Session to increment a counter for ${testAddresses.Counter}`,
moduleData: {
permissionIds: createSessionsResponse.permissionIds,
...createSessionsResponse,
mode: SmartSessionMode.USE
}
}

// Zip the session data, and store it for later use by a dapp
zippedSessionDatum = stringify(sessionData)
stringifiedSessionDatum = stringify(sessionData)
})

test("should demonstrate using a smart session from dapp's perspective", async () => {
// Now assume the user has left the dapp and the usersNexusClient signer is no longer available
// The following code demonstrates how a dapp can use the session to act on behalf of the user

// Unzip the session data
const usersSessionData = parse(zippedSessionDatum)
const usersSessionData = parse(stringifiedSessionDatum)

// Create a new Nexus client for the session
// This client will be used to interact with the smart contract account using the session key
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { SmartSessionMode } from "@rhinestone/module-sdk"
import {
http,
type Address,
Expand Down Expand Up @@ -27,8 +28,13 @@ import {
import { createNexusSessionClient } from "../../clients/createNexusSessionClient"
import { parseReferenceValue } from "../utils/Helpers"
import type { Module } from "../utils/Types"
import { abiToPoliciesInfo, toUniversalActionPolicy } from "./Helpers"
import type { CreateSessionDataParams } from "./Types"
import {
abiToPoliciesInfo,
parse,
stringify,
toUniversalActionPolicy
} from "./Helpers"
import type { CreateSessionDataParams, SessionData } from "./Types"
import { ParamCondition } from "./Types"
import { smartSessionCreateActions, smartSessionUseActions } from "./decorators"
import { toSmartSessionsValidator } from "./toSmartSessionsValidator"
Expand All @@ -42,7 +48,7 @@ describe("modules.smartSessions", async () => {
let testClient: MasterClient
let eoaAccount: LocalAccount
let nexusClient: NexusClient
let cachedPermissionId: Hex
let cachedSessionData: string // Session data to be stored by the dApp
let sessionKeyAccount: LocalAccount
let sessionPublicKey: Address

Expand Down Expand Up @@ -244,7 +250,18 @@ describe("modules.smartSessions", async () => {

expect(createSessionsResponse.userOpHash).toBeDefined()
expect(createSessionsResponse.permissionIds).toBeDefined()
;[cachedPermissionId] = createSessionsResponse.permissionIds

const sessionData: SessionData = {
granter: nexusClient.account.address,
description: `Session to increment a counter for ${testAddresses.Counter}`,
sessionPublicKey,
moduleData: {
...createSessionsResponse,
mode: SmartSessionMode.USE
}
}

cachedSessionData = stringify(sessionData)

const receipt = await nexusClient.waitForUserOperationReceipt({
hash: createSessionsResponse.userOpHash
Expand All @@ -260,6 +277,8 @@ describe("modules.smartSessions", async () => {
functionName: "getNumber"
})

const parsedSessionData = parse(cachedSessionData) as SessionData

const smartSessionNexusClient = await createNexusSessionClient({
chain,
accountAddress: nexusClient.account.address,
Expand All @@ -271,9 +290,7 @@ describe("modules.smartSessions", async () => {
const usePermissionsModule = toSmartSessionsValidator({
account: smartSessionNexusClient.account,
signer: sessionKeyAccount,
moduleData: {
permissionIds: [cachedPermissionId]
}
moduleData: parsedSessionData.moduleData
})

const useSmartSessionNexusClient = smartSessionNexusClient.extend(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
} from "viem"
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"
import { afterAll, beforeAll, describe, expect, test } from "vitest"
import { CounterAbi, MockRegistryAbi } from "../../../test/__contracts/abi"
import { MockCalleeAbi } from "../../../test/__contracts/abi/MockCalleeAbi"
import { testAddresses } from "../../../test/callDatas"
import { toNetwork } from "../../../test/testSetup"
Expand All @@ -33,7 +32,7 @@ import {
import { createNexusSessionClient } from "../../clients/createNexusSessionClient"
import { SMART_SESSIONS_ADDRESS } from "../../constants"
import type { Module } from "../utils/Types"
import { abiToPoliciesInfo, isPermissionEnabled } from "./Helpers"
import { isPermissionEnabled, parse, stringify } from "./Helpers"
import type { CreateSessionDataParams, Rule, SessionData } from "./Types"
import { ParamCondition } from "./Types"
import { smartSessionCreateActions, smartSessionUseActions } from "./decorators"
Expand All @@ -51,7 +50,7 @@ describe("modules.smartSessions.uni.policy", async () => {
let nexusAccountAddress: Address
let sessionKeyAccount: LocalAccount
let sessionPublicKey: Address
let cachedPermissionId: Hex
let cachedSessionData: string

let sessionsModule: Module

Expand Down Expand Up @@ -227,7 +226,18 @@ describe("modules.smartSessions.uni.policy", async () => {

expect(createSessionsResponse.userOpHash).toBeDefined()
expect(createSessionsResponse.permissionIds).toBeDefined()
;[cachedPermissionId] = createSessionsResponse.permissionIds

const sessionData: SessionData = {
granter: nexusClient.account.address,
description: `Session to add balance to MockCallee for ${testAddresses.MockCallee}`,
sessionPublicKey,
moduleData: {
...createSessionsResponse,
mode: SmartSessionMode.USE
}
}

cachedSessionData = stringify(sessionData)

const receipt = await nexusClient.waitForUserOperationReceipt({
hash: createSessionsResponse.userOpHash
Expand All @@ -238,16 +248,18 @@ describe("modules.smartSessions.uni.policy", async () => {
const isEnabled = await isPermissionEnabled({
client: nexusClient.account.client as PublicClient,
accountAddress: nexusClient.account.address,
permissionId: cachedPermissionId
permissionId: sessionData.moduleData.permissionIds[0]
})
expect(isEnabled).toBe(true)
}, 200000)

test("should make use of already enabled session (USE mode) to add balance to MockCallee using a session key", async () => {
const parsedSessionData = parse(cachedSessionData) as SessionData

const isEnabled = await isPermissionEnabled({
client: nexusClient.account.client as PublicClient,
accountAddress: nexusClient.account.address,
permissionId: cachedPermissionId
permissionId: parsedSessionData.moduleData.permissionIds[0]
})
expect(isEnabled).toBe(true)

Expand Down Expand Up @@ -285,9 +297,7 @@ describe("modules.smartSessions.uni.policy", async () => {
const usePermissionsModule = toSmartSessionsValidator({
account: smartSessionNexusClient.account,
signer: sessionKeyAccount,
moduleData: {
permissionIds: [cachedPermissionId]
}
moduleData: parsedSessionData.moduleData
})

const useSmartSessionNexusClient = smartSessionNexusClient.extend(
Expand Down
Loading