From bdb32a09cc126fbd0071028230cba5c297e4d987 Mon Sep 17 00:00:00 2001 From: joepegler Date: Thu, 15 Aug 2024 10:28:18 +0100 Subject: [PATCH 1/3] fix: taiko (#559) * chore: fix taiko testnet --- .github/workflows/test-playground.yml | 25 +++ CHANGELOG.md | 7 + package.json | 2 +- src/account/BaseSmartContractAccount.ts | 6 +- src/account/BiconomySmartAccountV2.ts | 99 +++++---- src/account/utils/Constants.ts | 7 +- src/modules/ECDSAOwnershipValidationModule.ts | 5 +- tests/account/write.test.ts | 90 ++++++++- tests/modules/write.test.ts | 2 - tests/playground/write.test.ts | 189 +++++++----------- tests/utils.ts | 14 +- 11 files changed, 275 insertions(+), 171 deletions(-) create mode 100644 .github/workflows/test-playground.yml diff --git a/.github/workflows/test-playground.yml b/.github/workflows/test-playground.yml new file mode 100644 index 000000000..e7fdf8777 --- /dev/null +++ b/.github/workflows/test-playground.yml @@ -0,0 +1,25 @@ +name: test-playground +on: + workflow_dispatch: + push: + branches: + - test/* +jobs: + test-playground: + name: test-playground + permissions: write-all + runs-on: ubuntu-latest + concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v3 + + - name: Install dependencies + uses: ./.github/actions/install-dependencies + + - name: Run the tests + run: bun run test:ci -t=Playground + env: + E2E_PRIVATE_KEY_ONE: ${{ secrets.E2E_PRIVATE_KEY_ONE }} + TESTING: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 2229bbcc4..b99a35797 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # @biconomy/account +## 4.6.1 + +### Patch Changes + +- Taiko testnet fix + ## 4.5.5 ### Patch Changes @@ -13,6 +19,7 @@ - Distributed Session Keys ## 4.5.3 + ## 4.6.0 ### Minor Changes diff --git a/package.json b/package.json index 303543907..a74b346cb 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "sideEffects": false, "name": "@biconomy/account", "author": "Biconomy", - "version": "4.6.0", + "version": "4.6.1", "description": "SDK for Biconomy integration with support for account abstraction, smart accounts, ERC-4337.", "keywords": [ "erc-7579", diff --git a/src/account/BaseSmartContractAccount.ts b/src/account/BaseSmartContractAccount.ts index 7d3c24cbb..799ef04bd 100644 --- a/src/account/BaseSmartContractAccount.ts +++ b/src/account/BaseSmartContractAccount.ts @@ -28,8 +28,7 @@ export enum DeploymentState { export abstract class BaseSmartContractAccount< TSigner extends SmartAccountSigner = SmartAccountSigner -> implements ISmartContractAccount -{ +> implements ISmartContractAccount { protected factoryAddress: Address protected deploymentState: DeploymentState = DeploymentState.UNDEFINED @@ -209,6 +208,7 @@ export abstract class BaseSmartContractAccount< } async getInitCode(): Promise { + if (this.deploymentState === DeploymentState.DEPLOYED) { return "0x" } @@ -228,12 +228,14 @@ export abstract class BaseSmartContractAccount< } async getAddress(): Promise
{ + if (!this.accountAddress) { const initCode = await this._getAccountInitCode() Logger.log("[BaseSmartContractAccount](getAddress) initCode: ", initCode) try { await this.entryPoint.simulate.getSenderAddress([initCode]) } catch (err: any) { + Logger.log( "[BaseSmartContractAccount](getAddress) getSenderAddress err: ", err diff --git a/src/account/BiconomySmartAccountV2.ts b/src/account/BiconomySmartAccountV2.ts index 059878b77..9d2363497 100644 --- a/src/account/BiconomySmartAccountV2.ts +++ b/src/account/BiconomySmartAccountV2.ts @@ -5,6 +5,7 @@ import { type GetContractReturnType, type Hex, type PublicClient, + type WalletClient, concat, concatHex, createPublicClient, @@ -21,6 +22,7 @@ import { toBytes, toHex, } from "viem"; +import { taikoHekla } from "viem/chains"; import type { IBundler } from "../bundler/IBundler.js"; import { Bundler, @@ -75,6 +77,7 @@ import { MAGIC_BYTES, NATIVE_TOKEN_ALIAS, PROXY_CREATION_CODE, + TAIKO_FACTORY_ADDRESS, } from "./utils/Constants.js"; import type { BalancePayload, @@ -146,23 +149,31 @@ export class BiconomySmartAccountV2 extends BaseSmartContractAccount { private constructor( readonly biconomySmartAccountConfig: BiconomySmartAccountV2ConfigConstructorProps, ) { + + const chain = biconomySmartAccountConfig.viemChain ?? + biconomySmartAccountConfig.customChain ?? + getChain(biconomySmartAccountConfig.chainId) + + const rpcClient = biconomySmartAccountConfig.rpcUrl || + getChain(biconomySmartAccountConfig.chainId).rpcUrls.default.http[0] + + const defaultedFactoryAddress = chain?.id === taikoHekla.id ? TAIKO_FACTORY_ADDRESS : + biconomySmartAccountConfig.factoryAddress ?? + DEFAULT_BICONOMY_FACTORY_ADDRESS; + + const isDefaultFactory = [DEFAULT_BICONOMY_FACTORY_ADDRESS, TAIKO_FACTORY_ADDRESS].some(address => addressEquals(defaultedFactoryAddress, address)); + super({ ...biconomySmartAccountConfig, - chain: - biconomySmartAccountConfig.viemChain ?? - biconomySmartAccountConfig.customChain ?? - getChain(biconomySmartAccountConfig.chainId), - rpcClient: - biconomySmartAccountConfig.rpcUrl || - getChain(biconomySmartAccountConfig.chainId).rpcUrls.default.http[0], + chain, + rpcClient, entryPointAddress: (biconomySmartAccountConfig.entryPointAddress as Hex) ?? DEFAULT_ENTRYPOINT_ADDRESS, accountAddress: (biconomySmartAccountConfig.accountAddress as Hex) ?? undefined, factoryAddress: - biconomySmartAccountConfig.factoryAddress ?? - DEFAULT_BICONOMY_FACTORY_ADDRESS, + defaultedFactoryAddress, }); this.sessionData = biconomySmartAccountConfig.sessionData @@ -194,10 +205,12 @@ export class BiconomySmartAccountV2 extends BaseSmartContractAccount { this.bundler = biconomySmartAccountConfig.bundler; + const defaultFallbackHandlerAddress = - this.factoryAddress === DEFAULT_BICONOMY_FACTORY_ADDRESS + isDefaultFactory ? DEFAULT_FALLBACK_HANDLER_ADDRESS : biconomySmartAccountConfig.defaultFallbackHandler; + if (!defaultFallbackHandlerAddress) { throw new Error("Default Fallback Handler address is not provided"); } @@ -212,14 +225,8 @@ export class BiconomySmartAccountV2 extends BaseSmartContractAccount { biconomySmartAccountConfig.activeValidationModule!; this.provider = createPublicClient({ - chain: - biconomySmartAccountConfig.viemChain ?? - biconomySmartAccountConfig.customChain ?? - getChain(biconomySmartAccountConfig.chainId), - transport: http( - biconomySmartAccountConfig.rpcUrl || - getChain(biconomySmartAccountConfig.chainId).rpcUrls.default.http[0] - ) + chain, + transport: http(rpcClient) }) this.scanForUpgradedAccountsFromV1 = @@ -705,10 +712,7 @@ export class BiconomySmartAccountV2 extends BaseSmartContractAccount { } } - const counterFactualAddressV2 = await this.getCounterFactualAddressV2({ - validationModule, - index, - }); + const counterFactualAddressV2 = await this.getCounterFactualAddressV2({ validationModule, index }); return counterFactualAddressV2; } @@ -720,13 +724,31 @@ export class BiconomySmartAccountV2 extends BaseSmartContractAccount { const index = params?.index ?? this.index; try { + + const owner = validationModule.getAddress() + const moduleSetupData = (await validationModule.getInitData()) as Hex + + if (this.chainId === taikoHekla.id) { + + const factoryContract = getContract({ + address: TAIKO_FACTORY_ADDRESS, + abi: BiconomyFactoryAbi, + client: { public: this.provider, wallet: this.getSigner() as unknown as WalletClient } + }) + + const smartAccountAddressFromContracts = + await factoryContract.read.getAddressForCounterFactualAccount([owner, moduleSetupData, BigInt(index)]) + + return smartAccountAddressFromContracts + } + const initCalldata = encodeFunctionData({ abi: BiconomyAccountAbi, functionName: "init", args: [ this.defaultFallbackHandlerAddress, - validationModule.getAddress() as Hex, - (await validationModule.getInitData()) as Hex, + owner, + moduleSetupData, ], }); @@ -845,15 +867,15 @@ export class BiconomySmartAccountV2 extends BaseSmartContractAccount { * Return the value to put into the "initCode" field, if the account is not yet deployed. * This value holds the "factory" address, followed by this account's information */ - async getAccountInitCode(): Promise { + public override async getAccountInitCode(): Promise { + this.isDefaultValidationModuleDefined(); if (await this.isAccountDeployed()) return "0x"; - return concatHex([ - this.factoryAddress as Hex, - (await this.getFactoryData()) ?? "0x", - ]); + const factoryData = await this.getFactoryData() as Hex; + + return concatHex([this.factoryAddress, factoryData]); } /** @@ -1317,6 +1339,7 @@ export class BiconomySmartAccountV2 extends BaseSmartContractAccount { userOp: Partial, stateOverrideSet?: StateOverrideSet, ): Promise> { + if (!this.bundler) throw new Error("Bundler is not provided"); const requiredFields: UserOperationKey[] = [ "sender", @@ -1336,12 +1359,17 @@ export class BiconomySmartAccountV2 extends BaseSmartContractAccount { maxFeePerGas, maxPriorityFeePerGas, } = await this.bundler.estimateUserOpGas(userOp, stateOverrideSet); + + + // if neither user sent gas fee nor the bundler, estimate gas from provider if ( !userOp.maxFeePerGas && !userOp.maxPriorityFeePerGas && (!maxFeePerGas || !maxPriorityFeePerGas) ) { + + const feeData = await this.provider.estimateFeesPerGas(); if (feeData.maxFeePerGas?.toString()) { finalUserOp.maxFeePerGas = `0x${feeData.maxFeePerGas.toString( @@ -1694,6 +1722,8 @@ export class BiconomySmartAccountV2 extends BaseSmartContractAccount { dummySignatureFetchPromise, ]); + const sender = await this.getAccountAddress() as Hex + if (transactions.length === 0) { throw new Error("Transactions array cannot be empty"); } @@ -1708,7 +1738,7 @@ export class BiconomySmartAccountV2 extends BaseSmartContractAccount { } let userOp: Partial = { - sender: (await this.getAccountAddress()) as Hex, + sender, nonce: toHex(nonceFromFetch), initCode, callData, @@ -2115,14 +2145,13 @@ export class BiconomySmartAccountV2 extends BaseSmartContractAccount { this.isDefaultValidationModuleDefined(); + const defaultValidationModuleAddress = this.defaultValidationModule.getAddress(); + const defaultValidationInitData = await this.defaultValidationModule.getInitData(); + return encodeFunctionData({ abi: BiconomyFactoryAbi, functionName: "deployCounterFactualAccount", - args: [ - this.defaultValidationModule.getAddress() as Hex, - (await this.defaultValidationModule.getInitData()) as Hex, - BigInt(this.index), - ], + args: [defaultValidationModuleAddress, defaultValidationInitData, BigInt(this.index)], }); } diff --git a/src/account/utils/Constants.ts b/src/account/utils/Constants.ts index df309580e..06d2f0421 100644 --- a/src/account/utils/Constants.ts +++ b/src/account/utils/Constants.ts @@ -1,7 +1,6 @@ import type { Hex } from "viem" import type { BiconomyFactories, - BiconomyFactoriesByVersion, BiconomyImplementations, BiconomyImplementationsByVersion, EntryPointAddresses, @@ -22,6 +21,7 @@ export const ENTRYPOINT_ADDRESSES: EntryPointAddresses = { } // will always be latest factory address +export const TAIKO_FACTORY_ADDRESS = "0x000008B3078bA5ed444FFf7658F76385F6004e7A"; //https://biconomyworkspace.slack.com/archives/C061BSA9279/p1721234773541039 export const DEFAULT_BICONOMY_FACTORY_ADDRESS = "0x000000a56Aaca3e9a4C479ea6b6CD0DbcB6634F5" export const DEFAULT_FALLBACK_HANDLER_ADDRESS = @@ -47,11 +47,6 @@ export const ENTRYPOINT_ADDRESSES_BY_VERSION: EntryPointAddressesByVersion = { V0_0_6: "0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789" } -export const BICONOMY_FACTORY_ADDRESSES_BY_VERSION: BiconomyFactoriesByVersion = - Object.fromEntries( - Object.entries(BICONOMY_FACTORY_ADDRESSES).map(([k, v]) => [v, k]) - ) - export const BICONOMY_IMPLEMENTATION_ADDRESSES_BY_VERSION: BiconomyImplementationsByVersion = Object.fromEntries( Object.entries(BICONOMY_IMPLEMENTATION_ADDRESSES).map(([k, v]) => [v, k]) diff --git a/src/modules/ECDSAOwnershipValidationModule.ts b/src/modules/ECDSAOwnershipValidationModule.ts index 0d472c3e6..1e6b4b860 100644 --- a/src/modules/ECDSAOwnershipValidationModule.ts +++ b/src/modules/ECDSAOwnershipValidationModule.ts @@ -77,14 +77,13 @@ export class ECDSAOwnershipValidationModule extends BaseValidationModule { // Note: other modules may need additional attributes to build init data async getInitData(): Promise { const ecdsaOwnerAddress = await this.signer.getAddress() - const moduleRegistryParsedAbi = parseAbi([ - "function initForSmartAccount(address owner)" - ]) + const moduleRegistryParsedAbi = parseAbi(["function initForSmartAccount(address owner)"]) const ecdsaOwnershipInitData = encodeFunctionData({ abi: moduleRegistryParsedAbi, functionName: "initForSmartAccount", args: [ecdsaOwnerAddress] }) + return ecdsaOwnershipInitData } diff --git a/tests/account/write.test.ts b/tests/account/write.test.ts index d771f1610..61f10f23b 100644 --- a/tests/account/write.test.ts +++ b/tests/account/write.test.ts @@ -8,7 +8,7 @@ import { parseAbi } from "viem" import { generatePrivateKey, privateKeyToAccount } from "viem/accounts" -import { arbitrumSepolia, polygonAmoy } from "viem/chains" +import { arbitrumSepolia, polygonAmoy, taikoHekla } from "viem/chains" import { beforeAll, describe, expect, test } from "vitest" import { type BiconomySmartAccountV2, @@ -668,4 +668,92 @@ describe("Account:Write", async () => { const response = await wait() expect(response.success).toBe("true") }, 50000) + + test.concurrent.skip( + "should check taiko network", + async () => { + + const { privateKey, privateKeyTwo } = getConfig(); + + const customChain = taikoHekla + const chainId = customChain.id; + const bundlerUrl = getBundlerUrl(chainId); + + const account = privateKeyToAccount(`0x${privateKey}`); + const recipientAccount = privateKeyToAccount(`0x${privateKeyTwo}`); + + const walletClientWithCustomChain = createWalletClient({ + account, + chain: customChain, + transport: http(), + }) + + const publicClient = createPublicClient({ + chain: customChain, + transport: http(), + }) + + const smartAccount = await createSmartAccountClient({ + signer: walletClientWithCustomChain, + bundlerUrl, + customChain, + }) + + const smartAccountAddress: Hex = await smartAccount.getAddress(); + const [balance] = await smartAccount.getBalances(); + if (balance.amount <= 1n) console.warn("Insufficient balance in smart account") + + const recipientBalanceBefore = await checkBalance(recipientAccount.address, undefined, customChain); + + const userOp = await smartAccount.buildUserOp([ + { + to: recipientAccount.address, + value: 1n + } + ]) + + userOp.signature = undefined + + const signedUserOp = await smartAccount.signUserOp(userOp) + + const entrypointContract = getContract({ + address: DEFAULT_ENTRYPOINT_ADDRESS, + abi: EntryPointAbi, + client: { public: publicClient, wallet: walletClientWithCustomChain } + }) + + const hash = await entrypointContract.write.handleOps([ + [ + { + sender: signedUserOp.sender as Hex, + nonce: BigInt(signedUserOp.nonce ?? 0), + callGasLimit: BigInt(signedUserOp.callGasLimit ?? 0), + verificationGasLimit: BigInt(signedUserOp.verificationGasLimit ?? 0), + preVerificationGas: BigInt(signedUserOp.preVerificationGas ?? 0), + maxFeePerGas: BigInt(signedUserOp.maxFeePerGas ?? 0), + maxPriorityFeePerGas: BigInt(signedUserOp.maxPriorityFeePerGas ?? 0), + initCode: signedUserOp.initCode as Hex, + callData: signedUserOp.callData as Hex, + paymasterAndData: signedUserOp.paymasterAndData as Hex, + signature: signedUserOp.signature as Hex + } + ], + account.address + ]) + + const { status, transactionHash } = + await publicClient.waitForTransactionReceipt({ hash }) + + const recipientBalanceAfter = await checkBalance(recipientAccount.address, undefined, customChain); + + expect(status).toBe("success") + expect(transactionHash).toBeTruthy() + + expect(recipientBalanceAfter).toBe(recipientBalanceBefore + 1n) + + }, + 70000 + ) + + }) diff --git a/tests/modules/write.test.ts b/tests/modules/write.test.ts index 4369a9067..85798b79a 100644 --- a/tests/modules/write.test.ts +++ b/tests/modules/write.test.ts @@ -24,13 +24,11 @@ import { } from "../../src/account"; import { Logger, getChain } from "../../src/account"; import { - BrowserWallet, type CreateSessionDataParams, DEFAULT_BATCHED_SESSION_ROUTER_MODULE, DEFAULT_ECDSA_OWNERSHIP_MODULE, DEFAULT_MULTICHAIN_MODULE, DEFAULT_SESSION_KEY_MANAGER_MODULE, - DanModuleInfo, ECDSA_OWNERSHIP_MODULE_ADDRESSES_BY_VERSION, NodeWallet, type PolicyLeaf, diff --git a/tests/playground/write.test.ts b/tests/playground/write.test.ts index 4d2941f01..0d4001b39 100644 --- a/tests/playground/write.test.ts +++ b/tests/playground/write.test.ts @@ -1,114 +1,77 @@ -import { http, type Hex, createWalletClient, encodeFunctionData, parseAbi } from "viem" -import { generatePrivateKey, privateKeyToAccount } from "viem/accounts" -import { polygonAmoy } from "viem/chains" +import { http, type Hex, createPublicClient, createWalletClient } from "viem"; +import { privateKeyToAccount } from "viem/accounts"; +import { polygonAmoy } from "viem/chains"; import { beforeAll, describe, expect, test } from "vitest" -import { PaymasterMode, type PolicyLeaf } from "../../src" -import { - type BiconomySmartAccountV2, - createSmartAccountClient, - getChain, - getCustomChain -} from "../../src/account" -import { createSession } from "../../src/modules/sessions/abi" -import { createSessionSmartAccountClient } from "../../src/modules/sessions/sessionSmartAccountClient" -import { getBundlerUrl, getConfig, getPaymasterUrl } from "../utils" - -const withSponsorship = { - paymasterServiceData: { mode: PaymasterMode.SPONSORED }, -}; - -describe("Playground:Write", () => { - - test.concurrent( - "should quickly run a write test in the playground ", - async () => { - - const { privateKey } = getConfig(); - const incrementCountContractAdd = "0xcf29227477393728935BdBB86770f8F81b698F1A"; - - // const customChain = getCustomChain( - // "Bera", - // 80084, - // "https://bartio.rpc.b-harvest.io", - // "https://bartio.beratrail.io/tx" - // ) - - // Switch to this line to test against Amoy - const customChain = polygonAmoy; - const chainId = customChain.id; - const bundlerUrl = getBundlerUrl(chainId); - - const paymasterUrls = { - 80002: getPaymasterUrl(chainId, "_sTfkyAEp.552504b5-9093-4d4b-94dd-701f85a267ea"), - 80084: getPaymasterUrl(chainId, "9ooHeMdTl.aa829ad6-e07b-4fcb-afc2-584e3400b4f5") - } - - const paymasterUrl = paymasterUrls[chainId]; - const account = privateKeyToAccount(`0x${privateKey}`); - - const walletClientWithCustomChain = createWalletClient({ - account, - chain: customChain, - transport: http() - }) - - const smartAccount = await createSmartAccountClient({ - signer: walletClientWithCustomChain, - bundlerUrl, - paymasterUrl, - customChain - }) - - const smartAccountAddress: Hex = await smartAccount.getAddress(); - - const [balance] = await smartAccount.getBalances(); - if (balance.amount <= 0) console.warn("Smart account balance is zero"); - - const policy: PolicyLeaf[] = [ - { - contractAddress: incrementCountContractAdd, - functionSelector: "increment()", - rules: [], - interval: { - validUntil: 0, - validAfter: 0, - }, - valueLimit: BigInt(0), - }, - ]; - - const { wait } = await createSession(smartAccount, policy, null, withSponsorship); - const { success } = await wait(); - - expect(success).toBe("true"); - - const smartAccountWithSession = await createSessionSmartAccountClient( - { - accountAddress: smartAccountAddress, // Set the account address on behalf of the user - bundlerUrl, - paymasterUrl, - chainId, - }, - "DEFAULT_STORE" // Storage client, full Session or smartAccount address if using default storage - ); - - const { wait: mintWait } = await smartAccountWithSession.sendTransaction( - { - to: incrementCountContractAdd, - data: encodeFunctionData({ - abi: parseAbi(["function increment()"]), - functionName: "increment", - args: [], - }), - }, - { paymasterServiceData: { mode: PaymasterMode.SPONSORED } }, - { leafIndex: "LAST_LEAF" }, - ); - - const { success: mintSuccess, receipt } = await mintWait(); - expect(mintSuccess).toBe("true"); - - }, - 30000 - ) -}) +import { type BiconomySmartAccountV2, createSmartAccountClient } from "../../src" +import { getBundlerUrl, getConfig } from "../utils"; + + +describe("Playground", async () => { + const { privateKey, chain } = getConfig() + const customChain = chain ?? polygonAmoy; + + /* + // Alternatively, you can use the following custom chain configuration... + + const customChain = getCustomChain( + "Bera", + 80084, + "https://bartio.rpc.b-harvest.io", + "https://bartio.beratrail.io/tx" + ) + + */ + + const chainId = customChain.id; + const bundlerUrl = getBundlerUrl(chainId); + const account = privateKeyToAccount(`0x${privateKey}`); + + const publicClient = createPublicClient({ + chain: customChain, + transport: http() + }) + + const walletClient = createWalletClient({ + account, + chain: customChain, + transport: http() + }) + + let smartAccount: BiconomySmartAccountV2; + let smartAccountAddress: Hex; + + beforeAll(async () => { + smartAccount = await createSmartAccountClient({ + signer: walletClient, + bundlerUrl, + customChain + }) + + smartAccountAddress = await smartAccount.getAddress(); + + const [balance] = await smartAccount.getBalances(); + const walletClientBalance = await publicClient.getBalance({ address: account.address }); + + console.log("account.address: ", account.address); + console.log("smartAccountAddress: ", smartAccountAddress); + + if (balance.amount <= 0) console.warn("Smart account balance is zero"); + if (walletClientBalance <= 0) console.warn("Wallet client balance is zero"); + }) + + + test("should send some native token for the configured chain", async () => { + + const { wait } = await smartAccount.sendTransaction( + { + to: "0x3079B249DFDE4692D7844aA261f8cf7D927A0DA5", + value: BigInt(1) + }, + ); + + const { success } = await wait(); + expect(success).toBe("true"); + + }, 30000) + +}) \ No newline at end of file diff --git a/tests/utils.ts b/tests/utils.ts index ce682624a..ba0e14ef9 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -13,16 +13,10 @@ import { extractChainIdFromBundlerUrl, extractChainIdFromPaymasterUrl } from "../src/bundler" +import { PaymasterMode } from "../src/paymaster" export const getEnvVars = () => { - const fields = [ - "BUNDLER_URL", - "E2E_PRIVATE_KEY_ONE", - "E2E_PRIVATE_KEY_TWO", - "E2E_BICO_PAYMASTER_KEY_AMOY", - "E2E_BICO_PAYMASTER_KEY_BASE", - "CHAIN_ID" - ] + const fields = ["E2E_PRIVATE_KEY_ONE"] const errorFields = fields.filter((field) => !process?.env?.[field]) if (errorFields.length) { @@ -191,3 +185,7 @@ export const getBundlerUrl = (chainId: number, apiKey?: string) => `https://bundler.biconomy.io/api/v2/${chainId}/${apiKey ?? "nJPK7B3ru.dd7f7861-190d-41bd-af80-6877f74b8f14"}` export const getPaymasterUrl = (chainId: number, apiKey: string) => `https://paymaster.biconomy.io/api/v1/${chainId}/${apiKey}` + +export const withSponsorship = { + paymasterServiceData: { mode: PaymasterMode.SPONSORED }, +}; From 88e05594944563fd570881a7eb43311d04625f87 Mon Sep 17 00:00:00 2001 From: joepegler Date: Tue, 17 Sep 2024 10:27:14 +0100 Subject: [PATCH 2/3] chore: remove dan continued (#577) --- bun.lockb | Bin 213930 -> 213434 bytes package.json | 1 - src/account/BiconomySmartAccountV2.ts | 11 +- src/modules/DANSessionKeyManagerModule.ts | 375 ---------------- src/modules/index.ts | 4 - src/modules/sessions/dan.ts | 410 ------------------ .../sessions/sessionSmartAccountClient.ts | 9 +- src/modules/utils/Helper.ts | 39 -- tests/modules/write.test.ts | 210 +-------- 9 files changed, 8 insertions(+), 1051 deletions(-) delete mode 100644 src/modules/DANSessionKeyManagerModule.ts delete mode 100644 src/modules/sessions/dan.ts diff --git a/bun.lockb b/bun.lockb index 0e8882e575de288f6efbff7277111470c0459f7c..4307e05cf7a4e2fb9fa2d8fc52dab67d8eee40f5 100755 GIT binary patch delta 40738 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 diff --git a/package.json b/package.json index a74b346cb..2f1d8f4cb 100644 --- a/package.json +++ b/package.json @@ -120,7 +120,6 @@ "commit-msg": "npx --no -- commitlint --edit ${1}" }, "dependencies": { - "@silencelaboratories/walletprovider-sdk": "^0.1.0", "merkletreejs": "^0.4.0" } } diff --git a/src/account/BiconomySmartAccountV2.ts b/src/account/BiconomySmartAccountV2.ts index 9d2363497..5816d63f1 100644 --- a/src/account/BiconomySmartAccountV2.ts +++ b/src/account/BiconomySmartAccountV2.ts @@ -37,7 +37,6 @@ import { type SessionType, createECDSAOwnershipValidationModule, getBatchSessionTxParams, - getDanSessionTxParams, getSingleSessionTxParams } from "../modules" import type { ISessionStorage } from "../modules/interfaces/ISessionStorage.js" @@ -1637,11 +1636,11 @@ export class BiconomySmartAccountV2 extends BaseSmartContractAccount { if (!defaultedChain) throw new Error("Chain is not provided") if (this.sessionType === "DISTRIBUTED_KEY") { - return getDanSessionTxParams( - defaultedConditionalSession, - defaultedChain, - correspondingIndex - ) + // return getDanSessionTxParams( + // defaultedConditionalSession, + // defaultedChain, + // correspondingIndex + // ) } if (this.sessionType === "BATCHED") { return getBatchSessionTxParams( diff --git a/src/modules/DANSessionKeyManagerModule.ts b/src/modules/DANSessionKeyManagerModule.ts deleted file mode 100644 index b9de92339..000000000 --- a/src/modules/DANSessionKeyManagerModule.ts +++ /dev/null @@ -1,375 +0,0 @@ -import { MerkleTree } from "merkletreejs" -import { - type Hex, - concat, - encodeAbiParameters, - encodeFunctionData, - keccak256, - pad, - parseAbi, - parseAbiParameters, - // toBytes, - toHex -} from "viem" -import { DEFAULT_ENTRYPOINT_ADDRESS, type SmartAccountSigner, type UserOperationStruct } from "../account" -import { BaseValidationModule } from "./BaseValidationModule.js" -import { danSDK } from "./index.js" -import type { - ISessionStorage, - SessionLeafNode, - SessionSearchParam, - SessionStatus -} from "./interfaces/ISessionStorage.js" -import { SessionLocalStorage } from "./session-storage/SessionLocalStorage.js" -import { SessionMemoryStorage } from "./session-storage/SessionMemoryStorage.js" -import { - DEFAULT_SESSION_KEY_MANAGER_MODULE, - SESSION_MANAGER_MODULE_ADDRESSES_BY_VERSION -} from "./utils/Constants.js" -import { - type CreateSessionDataParams, - type CreateSessionDataResponse, - type DanSignatureObject, - type ModuleInfo, - type ModuleVersion, - type SessionKeyManagerModuleConfig, - type SessionParams, - StorageType -} from "./utils/Types.js" -import { generateRandomHex } from "./utils/Uid.js" - -export type WalletProviderDefs = { - walletProviderId: string - walletProviderUrl: string -} - -export type Config = { - walletProvider: WalletProviderDefs -} - -export type SendUserOpArgs = SessionParams & { rawUserOperation: Partial } - -export class DANSessionKeyManagerModule extends BaseValidationModule { - version: ModuleVersion = "V1_0_0" - - moduleAddress!: Hex - - merkleTree!: MerkleTree - - sessionStorageClient!: ISessionStorage - - readonly mockEcdsaSessionKeySig: Hex = - "0x73c3ac716c487ca34bb858247b5ccf1dc354fbaabdd089af3b2ac8e78ba85a4959a2d76250325bd67c11771c31fccda87c33ceec17cc0de912690521bb95ffcb1b" - - /** - * This constructor is private. Use the static create method to instantiate SessionKeyManagerModule - * @param moduleConfig The configuration for the module - * @returns An instance of SessionKeyManagerModule - */ - private constructor(moduleConfig: SessionKeyManagerModuleConfig) { - super(moduleConfig) - } - - /** - * Asynchronously creates and initializes an instance of SessionKeyManagerModule - * @param moduleConfig The configuration for the module - * @returns A Promise that resolves to an instance of SessionKeyManagerModule - */ - public static async create( - moduleConfig: SessionKeyManagerModuleConfig - ): Promise { - // TODO: (Joe) stop doing things in a 'create' call after the instance has been created - const instance = new DANSessionKeyManagerModule(moduleConfig) - - if (moduleConfig.moduleAddress) { - instance.moduleAddress = moduleConfig.moduleAddress - } else if (moduleConfig.version) { - const moduleAddr = SESSION_MANAGER_MODULE_ADDRESSES_BY_VERSION[ - moduleConfig.version - ] as Hex - if (!moduleAddr) { - throw new Error(`Invalid version ${moduleConfig.version}`) - } - instance.moduleAddress = moduleAddr - instance.version = moduleConfig.version as ModuleVersion - } else { - instance.moduleAddress = DEFAULT_SESSION_KEY_MANAGER_MODULE - // Note: in this case Version remains the default one - } - - if (moduleConfig.sessionStorageClient) { - instance.sessionStorageClient = moduleConfig.sessionStorageClient - } else { - switch (moduleConfig.storageType) { - case StorageType.MEMORY_STORAGE: - instance.sessionStorageClient = new SessionMemoryStorage( - moduleConfig.smartAccountAddress - ) - break - case StorageType.LOCAL_STORAGE: - instance.sessionStorageClient = new SessionLocalStorage( - moduleConfig.smartAccountAddress - ) - break - default: - instance.sessionStorageClient = new SessionLocalStorage( - moduleConfig.smartAccountAddress - ) - } - } - - const existingSessionData = - await instance.sessionStorageClient.getAllSessionData() - const existingSessionDataLeafs = existingSessionData.map((sessionData) => { - const leafDataHex = concat([ - pad(toHex(sessionData.validUntil), { size: 6 }), - pad(toHex(sessionData.validAfter), { size: 6 }), - pad(sessionData.sessionValidationModule, { size: 20 }), - sessionData.sessionKeyData - ]) - return keccak256(leafDataHex) - }) - - instance.merkleTree = new MerkleTree(existingSessionDataLeafs, keccak256, { - sortPairs: true, - hashLeaves: false - }) - - return instance - } - - /** - * Method to create session data for any module. The session data is used to create a leaf in the merkle tree - * @param leavesData The data of one or more leaves to be used to create session data - * @returns The session data - */ - createSessionData = async ( - leavesData: CreateSessionDataParams[] - ): Promise => { - const sessionKeyManagerModuleABI = parseAbi([ - "function setMerkleRoot(bytes32 _merkleRoot)" - ]) - - const leavesToAdd: Buffer[] = [] - const sessionIDInfo: string[] = [] - - for (const leafData of leavesData) { - const leafDataHex = concat([ - pad(toHex(leafData.validUntil), { size: 6 }), - pad(toHex(leafData.validAfter), { size: 6 }), - pad(leafData.sessionValidationModule, { size: 20 }), - leafData.sessionKeyData - ]) - - const generatedSessionId = - leafData.preferredSessionId ?? generateRandomHex() - - // TODO: verify this, might not be buffer - leavesToAdd.push(keccak256(leafDataHex) as unknown as Buffer) - sessionIDInfo.push(generatedSessionId) - - const sessionLeafNode = { - ...leafData, - sessionID: generatedSessionId, - status: "PENDING" as SessionStatus - } - - await this.sessionStorageClient.addSessionData(sessionLeafNode) - } - - this.merkleTree.addLeaves(leavesToAdd) - - const leaves = this.merkleTree.getLeaves() - - const newMerkleTree = new MerkleTree(leaves, keccak256, { - sortPairs: true, - hashLeaves: false - }) - - this.merkleTree = newMerkleTree - - const setMerkleRootData = encodeFunctionData({ - abi: sessionKeyManagerModuleABI, - functionName: "setMerkleRoot", - args: [this.merkleTree.getHexRoot() as Hex] - }) - - await this.sessionStorageClient.setMerkleRoot(this.merkleTree.getHexRoot()) - return { - data: setMerkleRootData, - sessionIDInfo: sessionIDInfo - } - } - - /** - * This method is used to sign the user operation using the session signer - * @param userOp The user operation to be signed - * @param sessionSigner The signer to be used to sign the user operation - * @returns The signature of the user operation - */ - async signUserOpHash(_: string, { sessionID, rawUserOperation, additionalSessionData }: SendUserOpArgs): Promise { - const sessionSignerData = await this.getLeafInfo({ sessionID }) - - if (!rawUserOperation) throw new Error("Missing userOperation") - if (!sessionID) throw new Error("Missing sessionID") - if (!sessionSignerData.danModuleInfo) throw new Error("Missing danModuleInfo") - - if ( - !rawUserOperation.verificationGasLimit || - !rawUserOperation.callGasLimit || - !rawUserOperation.callData || - !rawUserOperation.paymasterAndData || - !rawUserOperation.initCode - ) { - throw new Error("Missing params from User operation") - } - - const userOpTemp = { - ...rawUserOperation, - verificationGasLimit: rawUserOperation.verificationGasLimit.toString(), - callGasLimit: rawUserOperation.callGasLimit.toString(), - callData: rawUserOperation.callData.slice(2), - paymasterAndData: rawUserOperation.paymasterAndData.slice(2), - initCode: String(rawUserOperation.initCode).slice(2) - } - - const objectToSign: DanSignatureObject = { - // @ts-ignore - userOperation: userOpTemp, - entryPointVersion: "v0.6.0", - entryPointAddress: DEFAULT_ENTRYPOINT_ADDRESS, - chainId: sessionSignerData.danModuleInfo.chainId - } - const messageToSign = JSON.stringify(objectToSign) - - const signature = await danSDK.signMessage(messageToSign, sessionSignerData.danModuleInfo) - - const leafDataHex = concat([ - pad(toHex(sessionSignerData.validUntil), { size: 6 }), - pad(toHex(sessionSignerData.validAfter), { size: 6 }), - pad(sessionSignerData.sessionValidationModule, { size: 20 }), - sessionSignerData.sessionKeyData - ]) - - // Generate the padded signature with (validUntil,validAfter,sessionVerificationModuleAddress,validationData,merkleProof,signature) - let paddedSignature: Hex = encodeAbiParameters( - parseAbiParameters("uint48, uint48, address, bytes, bytes32[], bytes"), - [ - sessionSignerData.validUntil, - sessionSignerData.validAfter, - sessionSignerData.sessionValidationModule, - sessionSignerData.sessionKeyData, - this.merkleTree.getHexProof(keccak256(leafDataHex)) as Hex[], - signature as Hex - ] - ) - - if (additionalSessionData) { - paddedSignature += additionalSessionData - } - - return paddedSignature as Hex - } - - private async getLeafInfo(params: ModuleInfo): Promise { - if (params?.sessionID) { - const matchedDatum = await this.sessionStorageClient.getSessionData({ - sessionID: params.sessionID - }) - if (matchedDatum) { - return matchedDatum - } - } - throw new Error("Session data not found") - } - - /** - * Update the session data pending state to active - * @param param The search param to find the session data - * @param status The status to be updated - * @returns - */ - async updateSessionStatus( - param: SessionSearchParam, - status: SessionStatus - ): Promise { - this.sessionStorageClient.updateSessionStatus(param, status) - } - - /** - * @remarks This method is used to clear all the pending sessions - * @returns - */ - async clearPendingSessions(): Promise { - this.sessionStorageClient.clearPendingSessions() - } - - /** - * @returns SessionKeyManagerModule address - */ - getAddress(): Hex { - return this.moduleAddress - } - - /** - * @remarks This is the version of the module contract - */ - async getSigner(): Promise { - throw new Error("Method not implemented.") - } - - /** - * @remarks This is the dummy signature for the module, used in buildUserOp for bundler estimation - * @returns Dummy signature - */ - async getDummySignature(params?: ModuleInfo): Promise { - if (!params) { - throw new Error("Params must be provided.") - } - - const sessionSignerData = await this.getLeafInfo(params) - const leafDataHex = concat([ - pad(toHex(sessionSignerData.validUntil), { size: 6 }), - pad(toHex(sessionSignerData.validAfter), { size: 6 }), - pad(sessionSignerData.sessionValidationModule, { size: 20 }), - sessionSignerData.sessionKeyData - ]) - - // Generate the padded signature with (validUntil,validAfter,sessionVerificationModuleAddress,validationData,merkleProof,signature) - let paddedSignature: Hex = encodeAbiParameters( - parseAbiParameters("uint48, uint48, address, bytes, bytes32[], bytes"), - [ - sessionSignerData.validUntil, - sessionSignerData.validAfter, - sessionSignerData.sessionValidationModule, - sessionSignerData.sessionKeyData, - this.merkleTree.getHexProof(keccak256(leafDataHex)) as Hex[], - this.mockEcdsaSessionKeySig - ] - ) - if (params?.additionalSessionData) { - paddedSignature += params.additionalSessionData - } - - const dummySig = encodeAbiParameters( - parseAbiParameters(["bytes, address"]), - [paddedSignature as Hex, this.getAddress()] - ) - - return dummySig - } - - /** - * @remarks Other modules may need additional attributes to build init data - */ - async getInitData(): Promise { - throw new Error("Method not implemented.") - } - - /** - * @remarks This Module dont have knowledge of signer. So, this method is not implemented - */ - async signMessage(_message: Uint8Array | string): Promise { - throw new Error("Method not implemented.") - } -} diff --git a/src/modules/index.ts b/src/modules/index.ts index 50877d085..57582607f 100644 --- a/src/modules/index.ts +++ b/src/modules/index.ts @@ -12,10 +12,8 @@ export * from "./session-validation-modules/ERC20SessionValidationModule.js" export * from "./sessions/abi.js" export * from "./sessions/erc20.js" export * from "./sessions/batch.js" -export * from "./sessions/dan.js" export * from "./sessions/sessionSmartAccountClient.js" export * from "./session-storage/index.js" -import { DANSessionKeyManagerModule } from "./DANSessionKeyManagerModule.js" import { BatchedSessionRouterModule, ECDSAOwnershipValidationModule, @@ -30,8 +28,6 @@ export const createMultiChainValidationModule = export const createECDSAOwnershipValidationModule = ECDSAOwnershipValidationModule.create export const createSessionKeyManagerModule = SessionKeyManagerModule.create -export const createDANSessionKeyManagerModule = - DANSessionKeyManagerModule.create export const createERC20SessionValidationModule = ERC20SessionValidationModule.create diff --git a/src/modules/sessions/dan.ts b/src/modules/sessions/dan.ts deleted file mode 100644 index d303a3fcb..000000000 --- a/src/modules/sessions/dan.ts +++ /dev/null @@ -1,410 +0,0 @@ -import { EOAAuth, EphAuth, type IBrowserWallet, NetworkSigner, WalletProviderServiceClient, computeAddress } from "@silencelaboratories/walletprovider-sdk" -import type { Chain, Hex } from "viem" -import { generatePrivateKey, privateKeyToAccount } from "viem/accounts" -import { type Session, createDANSessionKeyManagerModule } from "../" -import { - type BiconomySmartAccountV2, - type BuildUserOpOptions, - ERROR_MESSAGES, - Logger, - type Transaction, - isWalletClient -} from "../../account" -import { extractChainIdFromBundlerUrl } from "../../bundler" -import type { ISessionStorage } from "../interfaces/ISessionStorage" -import { getDefaultStorageClient } from "../session-storage/utils" -import { - DAN_BACKEND_URL, - DEFAULT_SESSION_KEY_MANAGER_MODULE -} from "../utils/Constants" -import { - NodeWallet, - type SessionSearchParam, - didProvideFullSession, - hexToUint8Array, - resumeSession -} from "../utils/Helper" -import type { DanModuleInfo } from "../utils/Types" -import { - type Policy, - type SessionGrantedPayload, - createABISessionDatum -} from "./abi" - -export type PolicyLeaf = Omit -export const DEFAULT_SESSION_DURATION = 60 * 60 * 24 * 365 // 1 year -export const QUORUM_PARTIES = 5 -export const QUORUM_THRESHOLD = 3 - -export type CreateSessionWithDistributedKeyParams = { - /** The user's smart account instance */ - smartAccountClient: BiconomySmartAccountV2, - /** An array of session configurations */ - policy: PolicyLeaf[], - /** The storage client to store the session keys */ - sessionStorageClient?: ISessionStorage | null, - /** The build userop dto */ - buildUseropDto?: BuildUserOpOptions, - /** The chain ID */ - chainId?: number, - /** Optional. The user's {@link IBrowserWallet} instance. Default will be the signer associated with the smart account. */ - browserWallet?: IBrowserWallet -} - -/** - * - * createSessionWithDistributedKey - * - * Creates a session for a user's smart account. - * This grants a dapp permission to execute a specific function on a specific contract on behalf of a user. - * Permissions can be specified by the dapp in the form of rules{@link Rule}, and then submitted to the user for approval via signing. - * The session keys granted with the imparted policy are stored in a StorageClient {@link ISessionStorage}. They can later be retrieved and used to validate userops. - * - * @param smartAccount - The user's {@link BiconomySmartAccountV2} smartAccount instance. - * @param policy - An array of session configurations {@link Policy}. - * @param sessionStorageClient - The storage client to store the session keys. {@link ISessionStorage} - * @param buildUseropDto - Optional. {@link BuildUserOpOptions} - * @param chainId - Optional. Will be inferred if left unset. - * @param browserWallet - Optional. The user's {@link IBrowserWallet} instance. Default will be the signer associated with the smart account. - * @returns Promise<{@link SessionGrantedPayload}> - An object containing the status of the transaction and the sessionID. - * - * @example - * - * import { type PolicyLeaf, type Session, createSessionWithDistributedKey } from "@biconomy/account" - * - * const policy: PolicyLeaf[] = [{ - * contractAddress: nftAddress, - * functionSelector: "safeMint(address)", - * rules: [ - * { - * offset: 0, - * condition: 0, - * referenceValue: smartAccountAddress - * } - * ], - * interval: { - * validUntil: 0, - * validAfter: 0 - * }, - * valueLimit: 0n - * }] - * - * const { wait, session } = await createSessionWithDistributedKey({ - * smartAccountClient, - * policy - * }) - * - * const { success } = await wait() -*/ -export const createSessionWithDistributedKey = async ({ - smartAccountClient, - policy, - sessionStorageClient, - buildUseropDto, - chainId, - browserWallet -}: CreateSessionWithDistributedKeyParams): Promise => { - const defaultedChainId = - chainId ?? - extractChainIdFromBundlerUrl(smartAccountClient?.bundler?.getBundlerUrl() ?? ""); - - if (!defaultedChainId) { - throw new Error(ERROR_MESSAGES.CHAIN_NOT_FOUND) - } - const smartAccountAddress = await smartAccountClient.getAddress() - const defaultedSessionStorageClient = - sessionStorageClient || getDefaultStorageClient(smartAccountAddress) - - const sessionsModule = await createDANSessionKeyManagerModule({ - smartAccountAddress, - sessionStorageClient: defaultedSessionStorageClient - }) - - let duration = DEFAULT_SESSION_DURATION - if (policy?.[0].interval?.validUntil) { - duration = Math.round(policy?.[0].interval?.validUntil - Date.now() / 1000) - } - - const { sessionKeyEOA: sessionKeyAddress, ...other } = await danSDK.generateSessionKey({ - smartAccountClient, - browserWallet, - duration, - chainId - }) - - const danModuleInfo: DanModuleInfo = { ...other } - const defaultedPolicy: Policy[] = policy.map((p) => ({ ...p, sessionKeyAddress })) - - const humanReadablePolicyArray = defaultedPolicy.map((p) => - createABISessionDatum({ ...p, danModuleInfo }) - ) - - const { data: policyData, sessionIDInfo } = - await sessionsModule.createSessionData(humanReadablePolicyArray) - - const permitTx = { - to: DEFAULT_SESSION_KEY_MANAGER_MODULE, - data: policyData - } - - const txs: Transaction[] = [] - - const isDeployed = await smartAccountClient.isAccountDeployed() - const enableSessionTx = await smartAccountClient.getEnableModuleData( - DEFAULT_SESSION_KEY_MANAGER_MODULE - ) - - if (isDeployed) { - const enabled = await smartAccountClient.isModuleEnabled( - DEFAULT_SESSION_KEY_MANAGER_MODULE - ) - if (!enabled) { - txs.push(enableSessionTx) - } - } else { - Logger.log(ERROR_MESSAGES.ACCOUNT_NOT_DEPLOYED) - txs.push(enableSessionTx) - } - - txs.push(permitTx) - - const userOpResponse = await smartAccountClient.sendTransaction(txs, buildUseropDto) - - smartAccountClient.setActiveValidationModule(sessionsModule) - - return { - session: { - sessionStorageClient: defaultedSessionStorageClient, - sessionIDInfo - }, - ...userOpResponse - } -} - -export type DanSessionKeyPayload = { - /** Dan Session ephemeral key*/ - sessionKeyEOA: Hex; - /** Dan Session MPC key ID*/ - mpcKeyId: string; - /** Dan Session ephemeral private key without 0x prefix */ - jwt: string; - /** Number of nodes that participate in keygen operation. Also known as n. */ - partiesNumber: number; - /** Number of nodes that needs to participate in protocol in order to generate valid signature. Also known as t. */ - threshold: number; - /** The eoa that was used to create the session */ - eoaAddress: Hex; - /** the chainId is relevant only to the */ - chainId: number -} - -export type DanSessionKeyRequestParams = { - /** Relevant smart account */ - smartAccountClient: BiconomySmartAccountV2; - /** Optional browser wallet. If using wagmi can be set to connector.getProvider() from useAccount hook */ - browserWallet?: IBrowserWallet; - /** Optional hardcoded values if required */ - hardcodedValues?: Partial; - /** Optional duration of the session key in seconds. Default is 3600 seconds. */ - duration?: number; - /** Optional chainId. Will be inferred if left unset. */ - chainId?: number; -} - -/** - * - * generateSessionKey - * - * @description This function is used to generate a new session key for a Distributed Account Network (DAN) session. This information is kept in the session storage and can be used to validate userops without the user's direct involvement. - * - * Generates a new session key for a Distributed Account Network (DAN) session. - * @param smartAccount - The user's {@link BiconomySmartAccountV2} smartAccount instance. - * @param browserWallet - Optional. The user's {@link IBrowserWallet} instance. - * @param hardcodedValues - Optional. {@link DanModuleInfo} - Additional information for the DAN module configuration to override the default values. - * @param duration - Optional. The duration of the session key in seconds. Default is 3600 seconds. - * @param chainId - Optional. The chain ID. Will be inferred if left unset. - * @returns Promise<{@link DanModuleInfo}> - An object containing the session key, the MPC key ID, the number of parties, the threshold, and the EOA address. - * -*/ -export const generateSessionKey = async ({ - smartAccountClient, - browserWallet, - hardcodedValues = {}, - duration = DEFAULT_SESSION_DURATION, - chainId -}: DanSessionKeyRequestParams): Promise => { - - const eoaAddress = hardcodedValues?.eoaAddress ?? (await smartAccountClient.getSigner().getAddress()) as Hex // Smart account owner - const innerSigner = smartAccountClient.getSigner().inner - - const defaultedChainId = chainId ?? extractChainIdFromBundlerUrl( - smartAccountClient?.bundler?.getBundlerUrl() ?? "" - ) - - if (!defaultedChainId) { - throw new Error(ERROR_MESSAGES.CHAIN_NOT_FOUND) - } - - if (!browserWallet && !isWalletClient(innerSigner)) - throw new Error(ERROR_MESSAGES.INVALID_BROWSER_WALLET) - const wallet = browserWallet ?? new NodeWallet(innerSigner) - - const hexEphSK = generatePrivateKey() - const account = privateKeyToAccount(hexEphSK) - const jwt = hardcodedValues?.jwt ?? hexEphSK.slice(2); - - const ephPK: Uint8Array = hexToUint8Array(account.address.slice(2)) - - const wpClient = new WalletProviderServiceClient({ - walletProviderId: "WalletProvider", - walletProviderUrl: DAN_BACKEND_URL - }) - - const eoaAuth = new EOAAuth(eoaAddress, wallet, ephPK, duration); - - const partiesNumber = hardcodedValues?.partiesNumber ?? QUORUM_PARTIES - const threshold = hardcodedValues?.threshold ?? QUORUM_THRESHOLD - - const sdk = new NetworkSigner(wpClient, threshold, partiesNumber, eoaAuth) - - // @ts-ignore - const resp = await sdk.authenticateAndCreateKey(ephPK) - - const pubKey = resp.publicKey - const mpcKeyId = resp.keyId as Hex - - const sessionKeyEOA = computeAddress(pubKey) - - return { - sessionKeyEOA, - mpcKeyId, - jwt, - partiesNumber, - threshold, - eoaAddress, - chainId: defaultedChainId - } -} - -export type DanSessionParamsPayload = { - params: { - sessionID: string - } -} -/** - * getDanSessionTxParams - * - * Retrieves the transaction parameters for a batched session. - * - * @param correspondingIndex - An index for the transaction corresponding to the relevant session. If not provided, the last session index is used. - * @param conditionalSession - {@link SessionSearchParam} The session data that contains the sessionID and sessionSigner. If not provided, The default session storage (localStorage in browser, fileStorage in node backend) is used to fetch the sessionIDInfo - * @returns Promise<{@link DanSessionParamsPayload}> - session parameters. - * - */ -export const getDanSessionTxParams = async ( - conditionalSession: SessionSearchParam, - chain: Chain, - correspondingIndex?: number | null | undefined -): Promise => { - const defaultedCorrespondingIndex = Array.isArray(correspondingIndex) - ? correspondingIndex[0] - : correspondingIndex - const resumedSession = await resumeSession(conditionalSession) - // if correspondingIndex is null then use the last session. - const allSessions = - await resumedSession.sessionStorageClient.getAllSessionData() - - const sessionID = didProvideFullSession(conditionalSession) - ? (conditionalSession as Session).sessionIDInfo[ - defaultedCorrespondingIndex ?? 0 - ] - : allSessions[defaultedCorrespondingIndex ?? allSessions.length - 1] - .sessionID - - const matchingLeafDatum = allSessions.find((s) => s.sessionID === sessionID) - - if (!sessionID) throw new Error(ERROR_MESSAGES.MISSING_SESSION_ID) - if (!matchingLeafDatum) throw new Error(ERROR_MESSAGES.NO_LEAF_FOUND) - if (!matchingLeafDatum.danModuleInfo) - throw new Error(ERROR_MESSAGES.NO_DAN_MODULE_INFO) - const chainIdsMatch = chain.id === matchingLeafDatum?.danModuleInfo?.chainId - if (!chainIdsMatch) throw new Error(ERROR_MESSAGES.CHAIN_ID_MISMATCH) - - return { params: { sessionID } } - -} - -/** - * - * signMessage - * - * @description This function is used to sign a message using the Distributed Account Network (DAN) module. - * - * @param message - The message to sign - * @param danParams {@link DanModuleInfo} - The DAN module information required to sign the message - * @returns signedResponse - Hex - * - * @example - * - * ```ts - * import { signMessage } from "@biconomy/account"; - * const objectToSign: DanSignatureObject = { - * userOperation: UserOperationStruct, - * entryPointVersion: "v0.6.0", - * entryPointAddress: "0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789", - * chainId - * } - * - * const messageToSign = JSON.stringify(objectToSign) - * const signature: Hex = await signMessage(messageToSign, sessionSignerData.danModuleInfo); // From the generateSessionKey helper - * ``` - * - */ -export const signMessage = async (message: string, danParams: DanModuleInfo): Promise => { - - const { jwt, eoaAddress, threshold, partiesNumber, chainId, mpcKeyId } = danParams; - - if (!message) throw new Error("Missing message") - if ( - !jwt || - !eoaAddress || - !threshold || - !partiesNumber || - !chainId || - !mpcKeyId - ) { - throw new Error("Missing params from danModuleInfo") - } - - const wpClient = new WalletProviderServiceClient({ - walletProviderId: "WalletProvider", - walletProviderUrl: DAN_BACKEND_URL - }) - - const ephSK = hexToUint8Array(jwt) - - const authModule = new EphAuth(eoaAddress, ephSK) - - const sdk = new NetworkSigner( - wpClient, - threshold, - partiesNumber, - authModule - ) - - const reponse: Awaited> = await sdk.authenticateAndSign(mpcKeyId, message); - - const v = reponse.recid - const sigV = v === 0 ? "1b" : "1c" - - const signature: Hex = `0x${reponse.sign}${sigV}` - - return signature -}; - -export const danSDK = { - generateSessionKey, - signMessage -} - -export default danSDK; \ No newline at end of file diff --git a/src/modules/sessions/sessionSmartAccountClient.ts b/src/modules/sessions/sessionSmartAccountClient.ts index 0f7817b03..97d3f660c 100644 --- a/src/modules/sessions/sessionSmartAccountClient.ts +++ b/src/modules/sessions/sessionSmartAccountClient.ts @@ -13,7 +13,6 @@ import type { UserOpResponse } from "../../bundler/index.js"; import { type SessionSearchParam, createBatchedSessionRouterModule, - createDANSessionKeyManagerModule, createSessionKeyManagerModule, type getSingleSessionTxParams, resumeSession, @@ -145,17 +144,11 @@ export const createSessionSmartAccountClient = async ( sessionKeyManagerModule: sessionModule, }, ); - const danSessionValidationModule = await createDANSessionKeyManagerModule({ - smartAccountAddress: biconomySmartAccountConfig.accountAddress, - sessionStorageClient, - }); const activeValidationModule = defaultedSessionType === "BATCHED" ? batchedSessionValidationModule - : defaultedSessionType === "STANDARD" - ? sessionModule - : danSessionValidationModule; + : sessionModule; return await createSmartAccountClient({ ...biconomySmartAccountConfig, diff --git a/src/modules/utils/Helper.ts b/src/modules/utils/Helper.ts index 4904d7f5e..fdbbcdf69 100644 --- a/src/modules/utils/Helper.ts +++ b/src/modules/utils/Helper.ts @@ -1,11 +1,8 @@ -import type { IBrowserWallet, TypedData } from "@silencelaboratories/walletprovider-sdk" import { type Address, type ByteArray, type Chain, - type EIP1193Provider, type Hex, - type WalletClient, encodeAbiParameters, isAddress, keccak256, @@ -254,40 +251,4 @@ export const hexToUint8Array = (hex: string) => { return array } -// Sign data using the secret key stored on Browser Wallet -// It creates a popup window, presenting the human readable form of `request` -// Throws an error if User rejected signature -export class BrowserWallet implements IBrowserWallet { - provider: EIP1193Provider - constructor(provider: EIP1193Provider) { - this.provider = provider - } - - async signTypedData( - from: string, - request: TypedData - ): Promise { - return await this.provider.request({ - method: "eth_signTypedData_v4", - // @ts-ignore - params: [from, JSON.stringify(request)] - }) - } -} - -// Sign data using the secret key stored on Browser Wallet -// It creates a popup window, presenting the human readable form of `request` -// Throws an error if User rejected signature -export class NodeWallet implements IBrowserWallet { - walletClient: WalletClient - - constructor(walletClient: WalletClient) { - this.walletClient = walletClient - } - - async signTypedData(_: string, request: TypedData): Promise { - // @ts-ignore - return await this.walletClient.signTypedData(request) - } -} diff --git a/tests/modules/write.test.ts b/tests/modules/write.test.ts index 85798b79a..0a77f160a 100644 --- a/tests/modules/write.test.ts +++ b/tests/modules/write.test.ts @@ -12,7 +12,7 @@ import { slice, toFunctionSelector } from "viem" -import { generatePrivateKey, privateKeyToAccount } from "viem/accounts" +import { privateKeyToAccount } from "viem/accounts" import { beforeAll, describe, expect, test } from "vitest" import { type BiconomySmartAccountV2, @@ -30,19 +30,13 @@ import { DEFAULT_MULTICHAIN_MODULE, DEFAULT_SESSION_KEY_MANAGER_MODULE, ECDSA_OWNERSHIP_MODULE_ADDRESSES_BY_VERSION, - NodeWallet, - type PolicyLeaf, - createECDSAOwnershipValidationModule, createMultiChainValidationModule, createSessionKeyManagerModule, - createSessionWithDistributedKey, - danSDK, getABISVMSessionKeyData, resumeSession, } from "../../src/modules"; import { ECDSAModuleAbi } from "../../src/account/abi/ECDSAModule" -import { DANSessionKeyManagerModule } from "../../src/modules/DANSessionKeyManagerModule" import { SessionMemoryStorage } from "../../src/modules/session-storage/SessionMemoryStorage" import { createSessionKeyEOA } from "../../src/modules/session-storage/utils" import { @@ -1466,206 +1460,6 @@ describe("Modules:Write", () => { balanceOfPreferredTokenBefore - balanceOfPreferredTokenAfter ).toBeGreaterThan(0) }, 80000) +}); - test("should create and use an DAN session on behalf of a user (abstracted)", async () => { - const policy: PolicyLeaf[] = [ - { - contractAddress: nftAddress, - functionSelector: "safeMint(address)", - rules: [ - { - offset: RuleHelpers.OffsetByIndex(0), - condition: RuleHelpers.Condition("EQUAL"), - referenceValue: smartAccountAddress - } - ], - interval: PolicyHelpers.Indefinitely, - valueLimit: PolicyHelpers.NoValueLimit - } - ] - - const { wait } = await createSessionWithDistributedKey({ smartAccountClient: smartAccount, policy }) - - const { success } = await wait() - expect(success).toBe("true") - - const nftMintTx: Transaction = { - to: nftAddress, - data: encodeFunctionData({ - abi: parseAbi(["function safeMint(address _to)"]), - functionName: "safeMint", - args: [smartAccountAddress] - }) - } - - const nftBalanceBefore = await checkBalance(smartAccountAddress, nftAddress) - - const smartAccountWithSession = await createSessionSmartAccountClient( - { - accountAddress: smartAccountAddress, // Set the account address on behalf of the user - bundlerUrl, - paymasterUrl, - chainId - }, - "DEFAULT_STORE", - "DISTRIBUTED_KEY" - ) - - const { wait: waitForMint } = await smartAccountWithSession.sendTransaction( - nftMintTx, - withSponsorship, - { leafIndex: "LAST_LEAF" } - ) - - const { - success: mintSuccess, - receipt: { transactionHash } - } = await waitForMint() - - expect(mintSuccess).toBe("true") - expect(transactionHash).toBeTruthy() - - const nftBalanceAfter = await checkBalance(smartAccountAddress, nftAddress) - expect(nftBalanceAfter - nftBalanceBefore).toBe(1n) - - }, 50000) - - - test("should create and use a DAN session on behalf of a user (deconstructed)", async () => { - - // To begin with, ensure that the regular validation module is set - smartAccount = smartAccount.setActiveValidationModule( - await createECDSAOwnershipValidationModule({ signer: walletClient }) - ) - - // Create a new storage client - const memoryStore = new SessionMemoryStorage(smartAccountAddress); - - // Get the module for activation later - const sessionsModule = await DANSessionKeyManagerModule.create({ - smartAccountAddress, - sessionStorageClient: memoryStore - }) - - // Set the ttl for the session - const duration = 60 * 60 - - // Get the session key from the dan network - const danModuleInfo = await danSDK.generateSessionKey({ - smartAccountClient: smartAccount, - browserWallet: new NodeWallet(walletClient), - duration - }) - - // create the policy to be signed over by the user - const policy: Policy[] = [{ - contractAddress: nftAddress, - functionSelector: "safeMint(address)", - sessionKeyAddress: danModuleInfo.sessionKeyEOA, // Add the session key address from DAN - rules: [ - { - offset: RuleHelpers.OffsetByIndex(0), - condition: RuleHelpers.Condition("EQUAL"), - referenceValue: smartAccountAddress - } - ], - interval: { - validAfter: 0, - validUntil: Math.round(Date.now() / 1000) + duration // The duration is set to 1 hour - }, - valueLimit: PolicyHelpers.NoValueLimit - }]; - - // Create the session data using the information retrieved from DAN. Keep the danModuleInfo for later use in a session leaf - const { data: policyData, sessionIDInfo: sessionIDs } = - await sessionsModule.createSessionData(policy.map(p => createABISessionDatum({ ...p, danModuleInfo }))) - - // Cconstruct the session transaction - const permitTx = { - to: DEFAULT_SESSION_KEY_MANAGER_MODULE, - data: policyData - } - - const txs: Transaction[] = [] - - // Ensure the module is enabled - const isDeployed = await smartAccount.isAccountDeployed() - const enableSessionTx = await smartAccount.getEnableModuleData( - DEFAULT_SESSION_KEY_MANAGER_MODULE - ) - - // Ensure the smart account is deployed - if (isDeployed) { - // Add the enable module transaction if it is not enabled - const enabled = await smartAccount.isModuleEnabled( - DEFAULT_SESSION_KEY_MANAGER_MODULE - ) - if (!enabled) { - txs.push(enableSessionTx) - } - } else { - txs.push(enableSessionTx) - } - - // Add the permit transaction - txs.push(permitTx) - - // User must sign over the policy to grant the relevant permissions - const { wait } = await smartAccount.sendTransaction(txs, { ...withSponsorship, nonceOptions }); - const { success } = await wait(); - - expect(success).toBe("true"); - - // Now let's use the session, assuming we have no user-connected smartAccountClient. - const randomWalletClient = createWalletClient({ - account: privateKeyToAccount(generatePrivateKey()), - chain, - transport: http() - }); - - // Now assume that the users smart account address and the storage client are the only known values - let unconnectedSmartAccount = await createSmartAccountClient({ - accountAddress: smartAccountAddress, // Set the account address on behalf of the user - signer: randomWalletClient, // This signer is irrelevant and will not be used - bundlerUrl, - paymasterUrl, - chainId - }); - - // Set the active validation module to the DAN session module - unconnectedSmartAccount = unconnectedSmartAccount.setActiveValidationModule(sessionsModule); - - // Use the session to submit a tx relevant to the policy - const nftMintTx = { - to: nftAddress, - data: encodeFunctionData({ - abi: parseAbi(["function safeMint(address _to)"]), - functionName: "safeMint", - args: [smartAccountAddress] - }) - } - - // Assume we know that the relevant session leaf to the transaction is the last one... - const allLeaves = await memoryStore.getAllSessionData(); - const relevantLeaf = allLeaves[allLeaves.length - 1]; - - const sessionID = relevantLeaf.sessionID; - // OR - const sameSessionID = sessionIDs[0]; // Usually only available when the session is created - - const nftBalanceBefore = await checkBalance(smartAccountAddress, nftAddress); - - // Now use the sessionID to send the transaction - const { wait: waitForMint } = await unconnectedSmartAccount.sendTransaction(nftMintTx, { ...withSponsorship, params: { sessionID } }); - - // Check for success - const { success: mintSuccess } = await waitForMint(); - const nftBalanceAfter = await checkBalance(smartAccountAddress, nftAddress); - - expect(nftBalanceAfter - nftBalanceBefore).toBe(1n); - expect(mintSuccess).toBe("true"); - - }, 50000) - -}) From 4124d7eb6975bba8a44fd2fb554a7749de67bbd4 Mon Sep 17 00:00:00 2001 From: joepegler Date: Tue, 17 Sep 2024 10:31:13 +0100 Subject: [PATCH 3/3] chore: release v4.6.2 (#579) --- CHANGELOG.md | 6 ++++++ package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b99a35797..d8f95d7a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # @biconomy/account +## 4.6.2 + +### Patch Changes + +- Temporary removal of DAN + ## 4.6.1 ### Patch Changes diff --git a/package.json b/package.json index 2f1d8f4cb..3f618bbdf 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "sideEffects": false, "name": "@biconomy/account", "author": "Biconomy", - "version": "4.6.1", + "version": "4.6.2", "description": "SDK for Biconomy integration with support for account abstraction, smart accounts, ERC-4337.", "keywords": [ "erc-7579",