-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
647 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,4 +3,4 @@ | |
zkeys/ | ||
proofs/ | ||
tally.json | ||
|
||
session-keys.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
/** | ||
* Generate the RPCUrl for Pimlico based on the chain we need to interact with | ||
* @param network - the network we want to interact with | ||
* @returns the RPCUrl for the network | ||
*/ | ||
export const genPimlicoRPCUrl = (network: string): string => { | ||
const pimlicoAPIKey = process.env.PIMLICO_API_KEY; | ||
|
||
if (!pimlicoAPIKey) { | ||
throw new Error("PIMLICO_API_KEY is not set"); | ||
} | ||
|
||
return `https://api.pimlico.io/v2/${network}/rpc?apikey=${pimlicoAPIKey}`; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
57 changes: 57 additions & 0 deletions
57
packages/coordinator/ts/sessionKeys/__tests__/sessionKeys.controller.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import { Test } from "@nestjs/testing"; | ||
import { zeroAddress } from "viem"; | ||
|
||
import type { IGenerateSessionKeyReturn } from "../types"; | ||
|
||
import { SessionKeysController } from "../sessionKeys.controller"; | ||
import { SessionKeysService } from "../sessionKeys.service"; | ||
|
||
describe("SessionKeysController", () => { | ||
let sessionKeysController: SessionKeysController; | ||
|
||
const mockSessionKeysService = { | ||
generateSessionKey: jest.fn(), | ||
deactivateSessionKey: jest.fn(), | ||
}; | ||
|
||
const defaultGenerateSessionKeyReturn: IGenerateSessionKeyReturn = { | ||
sessionKeyAddress: zeroAddress, | ||
}; | ||
|
||
beforeEach(async () => { | ||
const app = await Test.createTestingModule({ | ||
controllers: [SessionKeysController], | ||
}) | ||
.useMocker((token) => { | ||
if (token === SessionKeysService) { | ||
mockSessionKeysService.generateSessionKey.mockResolvedValue(defaultGenerateSessionKeyReturn); | ||
return mockSessionKeysService; | ||
} | ||
|
||
return jest.fn(); | ||
}) | ||
.compile(); | ||
|
||
sessionKeysController = app.get<SessionKeysController>(SessionKeysController); | ||
}); | ||
|
||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
describe("v1/session-keys/generate", () => { | ||
test("should return a session key address", async () => { | ||
// have to use await otherwise the mock will return a Promise | ||
// eslint-disable-next-line @typescript-eslint/await-thenable | ||
const data = await sessionKeysController.generateSessionKey(); | ||
expect(data).toStrictEqual(defaultGenerateSessionKeyReturn); | ||
}); | ||
}); | ||
|
||
describe("v1/session-keys/:sessionKeyAddress", () => { | ||
test("should delete a session key", () => { | ||
sessionKeysController.deactivateSessionKey(zeroAddress); | ||
expect(mockSessionKeysService.deactivateSessionKey).toHaveBeenCalledWith(zeroAddress); | ||
}); | ||
}); | ||
}); |
40 changes: 40 additions & 0 deletions
40
packages/coordinator/ts/sessionKeys/__tests__/sessionKeys.service.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import dotenv from "dotenv"; | ||
import { ZeroAddress } from "ethers"; | ||
|
||
import { CryptoService } from "../../crypto/crypto.service"; | ||
import { FileService } from "../../file/file.service"; | ||
import { SessionKeysService } from "../sessionKeys.service"; | ||
|
||
dotenv.config(); | ||
|
||
describe("SessionKeysService", () => { | ||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
const cryptoService = new CryptoService(); | ||
const fileService = new FileService(); | ||
const sessionKeysService = new SessionKeysService(cryptoService, fileService); | ||
|
||
test("should generate and store a session key", () => { | ||
const sessionKeyAddress = sessionKeysService.generateSessionKey(); | ||
expect(sessionKeyAddress).toBeDefined(); | ||
expect(sessionKeyAddress).not.toEqual(ZeroAddress); | ||
|
||
const sessionKey = fileService.getSessionKey(sessionKeyAddress.sessionKeyAddress); | ||
expect(sessionKey).toBeDefined(); | ||
}); | ||
|
||
test("should delete a session key", () => { | ||
const sessionKeyAddress = sessionKeysService.generateSessionKey(); | ||
expect(sessionKeyAddress).toBeDefined(); | ||
expect(sessionKeyAddress).not.toEqual(ZeroAddress); | ||
|
||
const sessionKey = fileService.getSessionKey(sessionKeyAddress.sessionKeyAddress); | ||
expect(sessionKey).toBeDefined(); | ||
|
||
sessionKeysService.deactivateSessionKey(sessionKeyAddress.sessionKeyAddress); | ||
const sessionKeyDeleted = fileService.getSessionKey(sessionKeyAddress.sessionKeyAddress); | ||
expect(sessionKeyDeleted).toBeUndefined(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import { signerToEcdsaValidator } from "@zerodev/ecdsa-validator"; | ||
import { type Policy, serializePermissionAccount, toPermissionValidator } from "@zerodev/permissions"; | ||
import { toTimestampPolicy } from "@zerodev/permissions/policies"; | ||
import { toECDSASigner } from "@zerodev/permissions/signers"; | ||
import { addressToEmptyAccount, createKernelAccount } from "@zerodev/sdk"; | ||
import { KERNEL_V3_1 } from "@zerodev/sdk/constants"; | ||
import { ENTRYPOINT_ADDRESS_V07 } from "permissionless"; | ||
import { createPublicClient, type Hex, http } from "viem"; | ||
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; | ||
import { localhost } from "viem/chains"; | ||
|
||
// use the latest kernel version | ||
const kernelVersion = KERNEL_V3_1; | ||
// we use the most recent entrypoint | ||
const entryPoint = ENTRYPOINT_ADDRESS_V07; | ||
|
||
/** | ||
* Generate a timestamp policy | ||
* @param endTime - The end time of the policy | ||
* @param start - The start time of the policy | ||
* @returns The timestamp policy | ||
*/ | ||
export const generateTimestampPolicy = (endTime: number, start?: number): Policy => | ||
toTimestampPolicy({ | ||
validAfter: start, | ||
validUntil: endTime, | ||
}); | ||
|
||
/** | ||
* Mock a session key approval | ||
* @dev This will fail in hardhat with: | ||
* "InvalidEntryPointError: The entry point address | ||
* (`entryPoint` = 0x0000000071727De22E5E9d8BAf0edAc6f37da032) | ||
* is not a valid entry point. getSenderAddress did not revert with | ||
* a SenderAddressResult error." | ||
* | ||
* @param sessionKeyAddress - The address of the session key | ||
* @returns The approval string | ||
*/ | ||
export const mockSessionKeyApproval = async (sessionKeyAddress: Hex): Promise<string> => { | ||
const policies = [generateTimestampPolicy(Math.floor(Date.now() / 1000) + 1000)]; | ||
|
||
const publicClient = createPublicClient({ | ||
chain: localhost, | ||
transport: http(), | ||
}); | ||
|
||
const sessionPrivateKey = generatePrivateKey(); | ||
|
||
const signer = privateKeyToAccount(sessionPrivateKey); | ||
|
||
const ecdsaValidator = await signerToEcdsaValidator(publicClient, { | ||
signer, | ||
entryPoint, | ||
kernelVersion, | ||
}); | ||
|
||
// Create an "empty account" as the signer -- you only need the public | ||
// key (address) to do this. | ||
// disabling rule even though Hex is 0x${string} | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument | ||
const emptyAccount = addressToEmptyAccount(sessionKeyAddress); | ||
const emptySessionKeySigner = toECDSASigner({ signer: emptyAccount }); | ||
|
||
const permissionPlugin = await toPermissionValidator(publicClient, { | ||
entryPoint, | ||
kernelVersion, | ||
signer: emptySessionKeySigner, | ||
policies, | ||
}); | ||
|
||
const sessionKeyAccount = await createKernelAccount(publicClient, { | ||
entryPoint, | ||
kernelVersion, | ||
plugins: { | ||
sudo: ecdsaValidator, | ||
regular: permissionPlugin, | ||
}, | ||
}); | ||
|
||
const approval = await serializePermissionAccount(sessionKeyAccount); | ||
|
||
return approval; | ||
}; |
Oops, something went wrong.