From a9ee967a4a628a1b01c2a9de782c815ccbd1fb6e Mon Sep 17 00:00:00 2001 From: Karandeep Singh Date: Wed, 4 Sep 2024 01:18:34 -0400 Subject: [PATCH 01/18] add getDummySignature impl --- .../builders/SmartSessionUserOpBuilder.ts | 85 ++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SmartSessionUserOpBuilder.ts b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SmartSessionUserOpBuilder.ts index 59be819e7..b6019f962 100644 --- a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SmartSessionUserOpBuilder.ts +++ b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SmartSessionUserOpBuilder.ts @@ -1,4 +1,4 @@ -import { Address, decodeAbiParameters, getAddress, Hex, PublicClient } from 'viem' +import { Address, decodeAbiParameters, encodeAbiParameters, getAddress, Hex, PublicClient } from 'viem' import { encodeEnable, encodeUse } from './EncodeLib' import { smartSessionAddress } from '@biconomy/permission-context-builder' import { readContract } from 'viem/actions' @@ -230,3 +230,86 @@ export async function formatSignature( return encodeEnable(signerId, signature, enableSession[0]) } } + +export async function getDummySignature( + publicClient: PublicClient, + params: { + permissionsContext: Hex + accountAddress: Address + } +) { + const { permissionsContext, accountAddress } = params + if (!permissionsContext || permissionsContext.length < 178) { + throw new Error('Permissions context too short') + } + + const validatorAddress = permissionsContext.slice(0, 42) as Address + + if (getAddress(validatorAddress) !== getAddress(smartSessionAddress)) { + throw new Error( + `Validator ${validatorAddress} is not the smart session validator ${smartSessionAddress}` + ) + } + + const enableData = `0x${permissionsContext.slice(178)}` as Hex + const enableSession = decodeAbiParameters(enableSessionsStructAbi, enableData) + const signerInitData = enableSession[0].isignerInitData + console.log('signerInitData', signerInitData) + const signers = decodeSigners(signerInitData) + console.log('signers', signers) + const dummySignatures: `0x${string}`[] = [] + const dummyECDSASignature: `0x${string}` = + '0xe8b94748580ca0b4993c9a1b86b5be851bfc076ff5ce3a1ff65bf16392acfcb800f9b4f1aef1555c7fce5599fffb17e7c635502154a0333ba21f3ae491839af51c' + const dummyPasskeySignature: `0x${string}` = '0x' + for (let i = 0; i < signers.length; i++) { + const signer = signers[i] + if (signer.type === 0) { + dummySignatures.push(dummyECDSASignature) + } else if (signer.type === 1) { + dummySignatures.push(dummyPasskeySignature) + } + } + const concatenatedDummySignature = encodeAbiParameters([{ type: 'bytes[]' }], [dummySignatures]) + + return formatSignature(publicClient, { + signature: concatenatedDummySignature, + permissionsContext, + accountAddress + }) +} + + +export function decodeSigners( + encodedData: `0x${string}` +): Array<{ type: number; data: `0x${string}` }> { + let offset = 2 // Start after '0x' + const signers: Array<{ type: number; data: `0x${string}` }> = [] + + // Decode the number of signers + const signersCount = parseInt(encodedData.slice(offset, offset + 2), 16) + offset += 2 + + for (let i = 0; i < signersCount; i++) { + // Decode signer type + const signerType = parseInt(encodedData.slice(offset, offset + 2), 16) + offset += 2 + + // Determine data length based on signer type + let dataLength: number + if (signerType === 0) { + dataLength = 40 // 20 bytes + } else if (signerType === 1) { + dataLength = 128 // 64 bytes + } else { + throw new Error(`Unknown signer type: ${signerType}`) + } + + // Decode signer data + const signerData = `0x${encodedData.slice(offset, offset + dataLength)}` as `0x${string}` + offset += dataLength + + signers.push({ type: signerType, data: signerData }) + } + + return signers +} From a9ac2383518f7493255e7268e5b607dcb43136c4 Mon Sep 17 00:00:00 2001 From: Karandeep Singh Date: Wed, 4 Sep 2024 02:35:35 -0400 Subject: [PATCH 02/18] chore:run prettier --- .../builders/SmartSessionUserOpBuilder.ts | 10 ++++++++-- .../wallets/react-wallet-v2/src/pages/_middleware.ts | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SmartSessionUserOpBuilder.ts b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SmartSessionUserOpBuilder.ts index b6019f962..973b41ab4 100644 --- a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SmartSessionUserOpBuilder.ts +++ b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SmartSessionUserOpBuilder.ts @@ -1,4 +1,11 @@ -import { Address, decodeAbiParameters, encodeAbiParameters, getAddress, Hex, PublicClient } from 'viem' +import { + Address, + decodeAbiParameters, + encodeAbiParameters, + getAddress, + Hex, + PublicClient +} from 'viem' import { encodeEnable, encodeUse } from './EncodeLib' import { smartSessionAddress } from '@biconomy/permission-context-builder' import { readContract } from 'viem/actions' @@ -278,7 +285,6 @@ export async function getDummySignature( }) } - export function decodeSigners( encodedData: `0x${string}` ): Array<{ type: number; data: `0x${string}` }> { diff --git a/advanced/wallets/react-wallet-v2/src/pages/_middleware.ts b/advanced/wallets/react-wallet-v2/src/pages/_middleware.ts index 56f759e80..a2f774c14 100644 --- a/advanced/wallets/react-wallet-v2/src/pages/_middleware.ts +++ b/advanced/wallets/react-wallet-v2/src/pages/_middleware.ts @@ -25,7 +25,7 @@ export function middleware(req: NextRequest) { return new Response(null, { status: 204, headers: { - 'Access-Control-Allow-Origin': process.env.NODE_ENV === 'production' ? '*' : (origin || ''), + 'Access-Control-Allow-Origin': process.env.NODE_ENV === 'production' ? '*' : origin || '', 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type, Authorization' } @@ -33,4 +33,4 @@ export function middleware(req: NextRequest) { } return res -} \ No newline at end of file +} From fc719b2c9ab3772d91b5c649200c95488fec3aa2 Mon Sep 17 00:00:00 2001 From: Karandeep Singh Date: Tue, 10 Sep 2024 01:10:52 -0400 Subject: [PATCH 03/18] update @biconomy/permission-context-builder deps --- advanced/wallets/react-wallet-v2/package.json | 4 ++-- advanced/wallets/react-wallet-v2/yarn.lock | 18 ++++++++++++------ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/advanced/wallets/react-wallet-v2/package.json b/advanced/wallets/react-wallet-v2/package.json index de51b96b6..d6a2a54a6 100644 --- a/advanced/wallets/react-wallet-v2/package.json +++ b/advanced/wallets/react-wallet-v2/package.json @@ -10,7 +10,7 @@ "prettier:write": "prettier --write '**/*.{js,ts,jsx,tsx}'" }, "dependencies": { - "@biconomy/permission-context-builder": "^1.0.9", + "@biconomy/permission-context-builder": "^1.1.2", "@cosmjs/amino": "0.32.3", "@cosmjs/encoding": "0.32.3", "@cosmjs/proto-signing": "0.32.3", @@ -71,4 +71,4 @@ "prettier": "2.6.2", "typescript": "5.2.2" } -} +} \ No newline at end of file diff --git a/advanced/wallets/react-wallet-v2/yarn.lock b/advanced/wallets/react-wallet-v2/yarn.lock index d716d8085..5627113b8 100644 --- a/advanced/wallets/react-wallet-v2/yarn.lock +++ b/advanced/wallets/react-wallet-v2/yarn.lock @@ -118,12 +118,13 @@ "@babel/helper-validator-identifier" "^7.22.20" to-fast-properties "^2.0.0" -"@biconomy/permission-context-builder@^1.0.9": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@biconomy/permission-context-builder/-/permission-context-builder-1.0.9.tgz#6d1c9717b80b94576c99dad10e928baa0a850f0d" - integrity sha512-gEKRixmJDJH1eQciYYuJWjfMLaUqpXXJ2eWoTZ5oqufENAgv2srHJVeXCTZjiLoEhI2rPYOv4uWGtZsGEwScMA== +"@biconomy/permission-context-builder@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@biconomy/permission-context-builder/-/permission-context-builder-1.1.2.tgz#caa7a96583784cf632e5450a3d9cfd1b2bfb4a90" + integrity sha512-VmKsX4NmLS+Y9vViQFJoSlpl9XXJ7MpTIOzLNfRAeBNqGnR0UcU+K/wzer9jRmp4MC4XoK0qfkHUjj+D8NVquw== dependencies: - typescript "^5.5.3" + tslib "^2.7.0" + typescript "^5.5.4" webauthn-p256 "^0.0.8" "@cosmjs/amino@0.32.3", "@cosmjs/amino@^0.32.3": @@ -6947,6 +6948,11 @@ tslib@^2.3.0, tslib@^2.6.2: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== +tslib@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01" + integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA== + tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" @@ -7020,7 +7026,7 @@ typescript@5.2.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78" integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w== -typescript@^5.5.3: +typescript@^5.5.4: version "5.5.4" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.4.tgz#d9852d6c82bad2d2eda4fd74a5762a8f5909e9ba" integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q== From 0e35aba7c9881bf3e78323ca887c72b3bc720357 Mon Sep 17 00:00:00 2001 From: Karandeep Singh Date: Tue, 10 Sep 2024 01:12:47 -0400 Subject: [PATCH 04/18] use chain specific module address --- .../lib/smart-accounts/SafeSmartAccountLib.ts | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SafeSmartAccountLib.ts b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SafeSmartAccountLib.ts index 57a902b04..eaa5a13b5 100644 --- a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SafeSmartAccountLib.ts +++ b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SafeSmartAccountLib.ts @@ -20,7 +20,7 @@ import { mockValidator, Permission, smartSessionAddress, - userOperationBuilderAddress + userOperationBuilderAddress, } from '@biconomy/permission-context-builder' import { ModuleType } from 'permissionless/actions/erc7579' @@ -72,20 +72,27 @@ export class SafeSmartAccountLib extends SmartAccountLib { account: this.client.account, transport: http() }) - - const permissionContext = await getContext(walletClient, { - permissions: [...grantPermissionsRequestParameters.permissions] as unknown as Permission[], - expiry: grantPermissionsRequestParameters.expiry, - signer: grantPermissionsRequestParameters.signer as MultiKeySigner, - smartAccountAddress: this.client.account.address - }) + console.log("walletClient chainId:",walletClient.chain.id) + let permissionContext = '0x' + try{ + permissionContext = await getContext(walletClient, { + permissions: [...grantPermissionsRequestParameters.permissions] as unknown as Permission[], + expiry: grantPermissionsRequestParameters.expiry, + signer: grantPermissionsRequestParameters.signer as MultiKeySigner, + smartAccountAddress: this.client.account.address + }) + } catch (error) { + console.error(`Error getting permission context: ${error}`) + throw error + } + console.log(`Returning the permissions request`) return { permissionsContext: permissionContext, grantedPermissions: grantPermissionsRequestParameters.permissions, expiry: grantPermissionsRequestParameters.expiry, signerData: { - userOpBuilder: userOperationBuilderAddress, + userOpBuilder: userOperationBuilderAddress[this.chain.id] as Address, submitToAddress: this.client.account.address } } as WalletGrantPermissionsReturnType @@ -110,11 +117,12 @@ export class SafeSmartAccountLib extends SmartAccountLib { let smartSessionValidatorInstalled = false let mockValidatorInstalled = false - + console.log(`SmartSession Address: ${smartSessionAddress[this.chain.id]}`) + console.log(`mockValidator Address: ${mockValidator[this.chain.id]}`) if (isAccountDeployed) { ;[smartSessionValidatorInstalled, mockValidatorInstalled] = await Promise.all([ - this.isValidatorModuleInstalled(smartSessionAddress), - this.isValidatorModuleInstalled(mockValidator) + this.isValidatorModuleInstalled(smartSessionAddress[this.chain.id] as Address), + this.isValidatorModuleInstalled(mockValidator[this.chain.id] as Address) ]) } console.log({ smartSessionValidatorInstalled, mockValidatorInstalled }) @@ -134,7 +142,7 @@ export class SafeSmartAccountLib extends SmartAccountLib { if (!isAccountDeployed || !smartSessionValidatorInstalled) { installModules.push({ - address: smartSessionAddress, + address: smartSessionAddress[this.chain.id], type: 'validator', context: '0x' }) @@ -142,7 +150,7 @@ export class SafeSmartAccountLib extends SmartAccountLib { if (!isAccountDeployed || !mockValidatorInstalled) { installModules.push({ - address: mockValidator, + address: mockValidator[this.chain.id], type: 'validator', context: '0x' }) From e4dbc8105549427bded01cadb76850410e7a1a8e Mon Sep 17 00:00:00 2001 From: Karandeep Singh Date: Tue, 10 Sep 2024 01:16:06 -0400 Subject: [PATCH 05/18] add base-sepolia to supported chains data --- .../src/consts/smartAccounts.ts | 4 ++-- .../react-wallet-v2/src/data/EIP155Data.ts | 9 +++++++++ .../src/utils/SmartAccountUtil.ts | 19 ++++++++++++------- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/advanced/wallets/react-wallet-v2/src/consts/smartAccounts.ts b/advanced/wallets/react-wallet-v2/src/consts/smartAccounts.ts index 13da3514d..2072a691d 100644 --- a/advanced/wallets/react-wallet-v2/src/consts/smartAccounts.ts +++ b/advanced/wallets/react-wallet-v2/src/consts/smartAccounts.ts @@ -1,10 +1,10 @@ import { KernelSmartAccountLib } from '@/lib/smart-accounts/KernelSmartAccountLib' import { SafeSmartAccountLib } from '@/lib/smart-accounts/SafeSmartAccountLib' import { getAddress } from 'viem' -import { goerli, polygonMumbai, sepolia } from 'viem/chains' +import { baseSepolia, goerli, polygonMumbai, sepolia } from 'viem/chains' // Types -export const allowedChains = [sepolia, polygonMumbai, goerli] +export const allowedChains = [sepolia, polygonMumbai, goerli, baseSepolia] // build chains so I can access them by id export const chains = allowedChains.reduce((acc, chain) => { acc[chain.id] = chain diff --git a/advanced/wallets/react-wallet-v2/src/data/EIP155Data.ts b/advanced/wallets/react-wallet-v2/src/data/EIP155Data.ts index 3055e9d49..8aefbb651 100644 --- a/advanced/wallets/react-wallet-v2/src/data/EIP155Data.ts +++ b/advanced/wallets/react-wallet-v2/src/data/EIP155Data.ts @@ -83,6 +83,15 @@ export const EIP155_TEST_CHAINS: Record = { namespace: 'eip155', smartAccountEnabled: true }, + 'eip155:84532': { + chainId: 84532, + name: 'Base Sepolia', + logo: '/chain-logos/eip155-1.png', + rgb: '99, 125, 234', + rpc: 'https://sepolia.base.org', + namespace: 'eip155', + smartAccountEnabled: true + }, 'eip155:43113': { chainId: 43113, name: 'Avalanche Fuji', diff --git a/advanced/wallets/react-wallet-v2/src/utils/SmartAccountUtil.ts b/advanced/wallets/react-wallet-v2/src/utils/SmartAccountUtil.ts index a5a6aa14c..89f3b0bae 100644 --- a/advanced/wallets/react-wallet-v2/src/utils/SmartAccountUtil.ts +++ b/advanced/wallets/react-wallet-v2/src/utils/SmartAccountUtil.ts @@ -3,7 +3,7 @@ import { Hex, Chain as ViemChain } from 'viem' import { SessionTypes } from '@walletconnect/types' import { Chain, allowedChains } from '@/consts/smartAccounts' import { KernelSmartAccountLib } from '@/lib/smart-accounts/KernelSmartAccountLib' -import { sepolia } from 'viem/chains' +import { baseSepolia, sepolia } from 'viem/chains' import { SafeSmartAccountLib } from '@/lib/smart-accounts/SafeSmartAccountLib' import { SmartAccountLib } from '@/lib/smart-accounts/SmartAccountLib' @@ -11,7 +11,8 @@ import { SmartAccountLib } from '@/lib/smart-accounts/SmartAccountLib' export const ENTRYPOINT_ADDRESSES: Record = { Sepolia: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789', 'Polygon Mumbai': '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789', - Goerli: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789' + Goerli: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789', + 'Base Sepolia': '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789', } // Paymasters @@ -19,28 +20,32 @@ export const ENTRYPOINT_ADDRESSES: Record = { export const PAYMASTER_ADDRESSES: Record = { Sepolia: '0x0000000000325602a77416A16136FDafd04b299f', 'Polygon Mumbai': '0x000000000009B901DeC1aaB9389285965F49D387', - Goerli: '0xEc43912D8C772A0Eba5a27ea5804Ba14ab502009' + Goerli: '0xEc43912D8C772A0Eba5a27ea5804Ba14ab502009', + 'Base Sepolia': '0xEc43912D8C772A0Eba5a27ea5804Ba14ab502009', //Dummy } // USDC export const USDC_ADDRESSES: Record = { Sepolia: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238', 'Polygon Mumbai': '0x9999f7fea5938fd3b1e26a12c3f2fb024e194f97', - Goerli: '0x07865c6e87b9f70255377e024ace6630c1eaa37f' + Goerli: '0x07865c6e87b9f70255377e024ace6630c1eaa37f', + 'Base Sepolia': '0x07865c6e87b9f70255377e024ace6630c1eaa37f' //Dummy } // RPC URLs export const RPC_URLS: Record = { Sepolia: 'https://rpc.ankr.com/eth_sepolia', 'Polygon Mumbai': 'https://mumbai.rpc.thirdweb.com', - Goerli: 'https://ethereum-goerli.publicnode.com' + Goerli: 'https://ethereum-goerli.publicnode.com', + 'Base Sepolia': 'https://sepolia.base.org', } // Pimlico RPC names export const PIMLICO_NETWORK_NAMES: Record = { Sepolia: 'sepolia', 'Polygon Mumbai': 'mumbai', - Goerli: 'goerli' + Goerli: 'goerli', + 'Base Sepolia': 'base-sepolia' } export const publicRPCUrl = ({ chain }: UrlConfig) => { @@ -73,7 +78,7 @@ export function supportedAddressPriority( } export const kernelAllowedChains = [sepolia] -export const safeAllowedChains = [sepolia] +export const safeAllowedChains = [sepolia, baseSepolia] export const biconomyAllowedChains = [sepolia] export let smartAccountWallets: Record = {} From c10af9b770b05f3b47fc320070489556dcca095a Mon Sep 17 00:00:00 2001 From: Karandeep Singh Date: Tue, 10 Sep 2024 01:18:04 -0400 Subject: [PATCH 06/18] chores: run prettier --- .../src/lib/smart-accounts/SafeSmartAccountLib.ts | 8 ++++---- .../wallets/react-wallet-v2/src/utils/SmartAccountUtil.ts | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SafeSmartAccountLib.ts b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SafeSmartAccountLib.ts index eaa5a13b5..a640fb2eb 100644 --- a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SafeSmartAccountLib.ts +++ b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SafeSmartAccountLib.ts @@ -20,7 +20,7 @@ import { mockValidator, Permission, smartSessionAddress, - userOperationBuilderAddress, + userOperationBuilderAddress } from '@biconomy/permission-context-builder' import { ModuleType } from 'permissionless/actions/erc7579' @@ -72,9 +72,9 @@ export class SafeSmartAccountLib extends SmartAccountLib { account: this.client.account, transport: http() }) - console.log("walletClient chainId:",walletClient.chain.id) + console.log('walletClient chainId:', walletClient.chain.id) let permissionContext = '0x' - try{ + try { permissionContext = await getContext(walletClient, { permissions: [...grantPermissionsRequestParameters.permissions] as unknown as Permission[], expiry: grantPermissionsRequestParameters.expiry, @@ -85,7 +85,7 @@ export class SafeSmartAccountLib extends SmartAccountLib { console.error(`Error getting permission context: ${error}`) throw error } - + console.log(`Returning the permissions request`) return { permissionsContext: permissionContext, diff --git a/advanced/wallets/react-wallet-v2/src/utils/SmartAccountUtil.ts b/advanced/wallets/react-wallet-v2/src/utils/SmartAccountUtil.ts index 89f3b0bae..38794eba7 100644 --- a/advanced/wallets/react-wallet-v2/src/utils/SmartAccountUtil.ts +++ b/advanced/wallets/react-wallet-v2/src/utils/SmartAccountUtil.ts @@ -12,7 +12,7 @@ export const ENTRYPOINT_ADDRESSES: Record = { Sepolia: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789', 'Polygon Mumbai': '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789', Goerli: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789', - 'Base Sepolia': '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789', + 'Base Sepolia': '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789' } // Paymasters @@ -21,7 +21,7 @@ export const PAYMASTER_ADDRESSES: Record = { Sepolia: '0x0000000000325602a77416A16136FDafd04b299f', 'Polygon Mumbai': '0x000000000009B901DeC1aaB9389285965F49D387', Goerli: '0xEc43912D8C772A0Eba5a27ea5804Ba14ab502009', - 'Base Sepolia': '0xEc43912D8C772A0Eba5a27ea5804Ba14ab502009', //Dummy + 'Base Sepolia': '0xEc43912D8C772A0Eba5a27ea5804Ba14ab502009' //Dummy } // USDC @@ -37,7 +37,7 @@ export const RPC_URLS: Record = { Sepolia: 'https://rpc.ankr.com/eth_sepolia', 'Polygon Mumbai': 'https://mumbai.rpc.thirdweb.com', Goerli: 'https://ethereum-goerli.publicnode.com', - 'Base Sepolia': 'https://sepolia.base.org', + 'Base Sepolia': 'https://sepolia.base.org' } // Pimlico RPC names From 4cf4ef024f0fc600cc328b552a6898e593e09178 Mon Sep 17 00:00:00 2001 From: Karandeep Singh Date: Wed, 11 Sep 2024 09:13:41 -0400 Subject: [PATCH 07/18] update @rhinestone/module-kit deps --- advanced/wallets/react-wallet-v2/package.json | 2 +- .../src/utils/ERC7579AccountUtils.ts | 2 +- .../views/OwnableValidatorAddOwnerAction.tsx | 21 ++- .../views/OwnableValidatorInstallAction.tsx | 4 +- advanced/wallets/react-wallet-v2/yarn.lock | 132 ++---------------- 5 files changed, 30 insertions(+), 131 deletions(-) diff --git a/advanced/wallets/react-wallet-v2/package.json b/advanced/wallets/react-wallet-v2/package.json index d6a2a54a6..8dc2f3223 100644 --- a/advanced/wallets/react-wallet-v2/package.json +++ b/advanced/wallets/react-wallet-v2/package.json @@ -29,7 +29,7 @@ "@nextui-org/react": "1.0.8-beta.5", "@polkadot/keyring": "^10.1.2", "@polkadot/types": "^9.3.3", - "@rhinestone/module-sdk": "0.1.2", + "@rhinestone/module-sdk": "0.1.13", "@solana/web3.js": "1.89.2", "@taquito/signer": "^15.1.0", "@taquito/taquito": "^15.1.0", diff --git a/advanced/wallets/react-wallet-v2/src/utils/ERC7579AccountUtils.ts b/advanced/wallets/react-wallet-v2/src/utils/ERC7579AccountUtils.ts index 36eb91429..5afece052 100644 --- a/advanced/wallets/react-wallet-v2/src/utils/ERC7579AccountUtils.ts +++ b/advanced/wallets/react-wallet-v2/src/utils/ERC7579AccountUtils.ts @@ -109,7 +109,7 @@ export async function getERC7579OwnableValidatorOwners({ } } -export async function getPublicClient(chain: Chain) { +export function getPublicClient(chain: Chain) { return createPublicClient({ transport: http(), chain: chain diff --git a/advanced/wallets/react-wallet-v2/src/views/OwnableValidatorAddOwnerAction.tsx b/advanced/wallets/react-wallet-v2/src/views/OwnableValidatorAddOwnerAction.tsx index 318866d7e..477691a25 100644 --- a/advanced/wallets/react-wallet-v2/src/views/OwnableValidatorAddOwnerAction.tsx +++ b/advanced/wallets/react-wallet-v2/src/views/OwnableValidatorAddOwnerAction.tsx @@ -1,9 +1,10 @@ -import { manageERC7579Module } from '@/utils/ERC7579AccountUtils' +import { getChainById } from '@/utils/ChainUtil' +import { getPublicClient, manageERC7579Module } from '@/utils/ERC7579AccountUtils' import { styledToast } from '@/utils/HelperUtil' import { Button, Col, Collapse, Input, Loading, Row, Text } from '@nextui-org/react' import { useState } from 'react' -import { isAddress } from 'viem' -const { getAddOwnableValidatorOwnerAction } = +import { Address, isAddress } from 'viem' +const { getAccount, getAddOwnableValidatorOwnerAction } = require('@rhinestone/module-sdk') as typeof import('@rhinestone/module-sdk') export default function OwnableValidatorAddOwnerAction({ @@ -29,9 +30,19 @@ export default function OwnableValidatorAddOwnerAction({ return } setAddingOwner(true) - const addOwnableValidatorOwnerAction = getAddOwnableValidatorOwnerAction({ - owner: newOwner as `0x${string}` + const account = getAccount({ + address: accountAddress as Address, + type: 'erc7579-implementation' }) + const addOwnableValidatorOwnerAction = await getAddOwnableValidatorOwnerAction({ + owner: newOwner as `0x${string}`, + account, + client: getPublicClient(getChainById(parseInt(chainId))) + }) + + if (addOwnableValidatorOwnerAction instanceof Error) { + throw new Error(addOwnableValidatorOwnerAction.message) + } const txReceipt = await manageERC7579Module({ accountAddress, chainId: chainId, diff --git a/advanced/wallets/react-wallet-v2/src/views/OwnableValidatorInstallAction.tsx b/advanced/wallets/react-wallet-v2/src/views/OwnableValidatorInstallAction.tsx index 5042eb71b..2820f2962 100644 --- a/advanced/wallets/react-wallet-v2/src/views/OwnableValidatorInstallAction.tsx +++ b/advanced/wallets/react-wallet-v2/src/views/OwnableValidatorInstallAction.tsx @@ -3,7 +3,7 @@ import { styledToast } from '@/utils/HelperUtil' import { Button, Col, Collapse, Input, Loading, Row, Text, Textarea } from '@nextui-org/react' import { useState } from 'react' import { getAddress, isAddress } from 'viem' -const { getInstallOwnableValidator } = +const { getOwnableValidator } = require('@rhinestone/module-sdk') as typeof import('@rhinestone/module-sdk') export default function OwnableValidatorInstallAction({ @@ -48,7 +48,7 @@ export default function OwnableValidatorInstallAction({ setInstalling(false) return } - const installOwnableValidator = getInstallOwnableValidator({ + const installOwnableValidator = getOwnableValidator({ threshold: threshold, owners: addresses.split(',').map(address => getAddress(address)) }) diff --git a/advanced/wallets/react-wallet-v2/yarn.lock b/advanced/wallets/react-wallet-v2/yarn.lock index 5627113b8..ad1f38d51 100644 --- a/advanced/wallets/react-wallet-v2/yarn.lock +++ b/advanced/wallets/react-wallet-v2/yarn.lock @@ -12,26 +12,6 @@ resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz#d2a39395c587e092d77cbbc80acf956a54f38bf7" integrity sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q== -"@apollo/client@^3.10.1": - version "3.10.4" - resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.10.4.tgz#1abc488c79cf37dc63edf041aee6f9dc5aabc692" - integrity sha512-51gk0xOwN6Ls1EbTG5svFva1kdm2APHYTzmFhaAdvUQoJFDxfc0UwQgDxGptzH84vkPlo1qunY1FuboyF9LI3Q== - dependencies: - "@graphql-typed-document-node/core" "^3.1.1" - "@wry/caches" "^1.0.0" - "@wry/equality" "^0.5.6" - "@wry/trie" "^0.5.0" - graphql-tag "^2.12.6" - hoist-non-react-statics "^3.3.2" - optimism "^0.18.0" - prop-types "^15.7.2" - rehackt "^0.1.0" - response-iterator "^0.2.6" - symbol-observable "^4.0.0" - ts-invariant "^0.10.3" - tslib "^2.3.0" - zen-observable-ts "^1.2.5" - "@babel/code-frame@^7.0.0": version "7.24.2" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.2.tgz#718b4b19841809a58b29b68cde80bc5e1aa6d9ae" @@ -805,11 +785,6 @@ dependencies: tslib "^2.4.0" -"@graphql-typed-document-node/core@^3.1.1": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861" - integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== - "@humanwhocodes/config-array@^0.9.2": version "0.9.5" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.5.tgz#2cbaf9a89460da24b5ca6531b8bbfc23e1df50c7" @@ -2111,13 +2086,12 @@ "@react-types/grid" "^3.2.6" "@react-types/shared" "^3.23.1" -"@rhinestone/module-sdk@0.1.2": - version "0.1.2" - resolved "https://registry.yarnpkg.com/@rhinestone/module-sdk/-/module-sdk-0.1.2.tgz#bc9e2257ff295ed7253fff535405874f70c8ed3a" - integrity sha512-CTh+sJGt4x2vAQE4Rs3AeTRO7UZGgyzcBh0uzQKkHIiOUPUngcQBZ3KWeJY+sdklo8+NeNYOftYGSd5XzKdzrQ== +"@rhinestone/module-sdk@0.1.13": + version "0.1.13" + resolved "https://registry.yarnpkg.com/@rhinestone/module-sdk/-/module-sdk-0.1.13.tgz#5df0a07d65a4fd3c0d5838714fc4468feaad0a55" + integrity sha512-Tb78W6BYOmBnjtqIHFmq43SBfd9vucZ4pxgN8h43zto1fKSwrBYLf4Pzrm/NwaAfFCw0BHuYo5YNQtPJj3oqUA== dependencies: - "@apollo/client" "^3.10.1" - tslib "^2.6.2" + solady "^0.0.235" "@rushstack/eslint-patch@^1.1.3": version "1.6.0" @@ -3000,41 +2974,6 @@ "@walletconnect/window-getters" "^1.0.1" tslib "1.14.1" -"@wry/caches@^1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@wry/caches/-/caches-1.0.1.tgz#8641fd3b6e09230b86ce8b93558d44cf1ece7e52" - integrity sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA== - dependencies: - tslib "^2.3.0" - -"@wry/context@^0.7.0": - version "0.7.4" - resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.7.4.tgz#e32d750fa075955c4ab2cfb8c48095e1d42d5990" - integrity sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ== - dependencies: - tslib "^2.3.0" - -"@wry/equality@^0.5.6": - version "0.5.7" - resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.5.7.tgz#72ec1a73760943d439d56b7b1e9985aec5d497bb" - integrity sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw== - dependencies: - tslib "^2.3.0" - -"@wry/trie@^0.4.3": - version "0.4.3" - resolved "https://registry.yarnpkg.com/@wry/trie/-/trie-0.4.3.tgz#077d52c22365871bf3ffcbab8e95cb8bc5689af4" - integrity sha512-I6bHwH0fSf6RqQcnnXLJKhkSXG45MFral3GxPaY4uAl0LYDZM+YDVDAiU9bYwjTuysy1S0IeecWtmq1SZA3M1w== - dependencies: - tslib "^2.3.0" - -"@wry/trie@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@wry/trie/-/trie-0.5.0.tgz#11e783f3a53f6e4cd1d42d2d1323f5bc3fa99c94" - integrity sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA== - dependencies: - tslib "^2.3.0" - "@zerodev/ecdsa-validator@5.3.0": version "5.3.0" resolved "https://registry.yarnpkg.com/@zerodev/ecdsa-validator/-/ecdsa-validator-5.3.0.tgz#4b837411026cb77c4e350ff3aab391f7d9402025" @@ -4703,13 +4642,6 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" -graphql-tag@^2.12.6: - version "2.12.6" - resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.12.6.tgz#d441a569c1d2537ef10ca3d1633b48725329b5f1" - integrity sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg== - dependencies: - tslib "^2.1.0" - graphql@^16.8.2: version "16.8.2" resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.8.2.tgz#54771c7ff195da913f5e70af8044a026d32eca2a" @@ -5906,16 +5838,6 @@ onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" -optimism@^0.18.0: - version "0.18.0" - resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.18.0.tgz#e7bb38b24715f3fdad8a9a7fc18e999144bbfa63" - integrity sha512-tGn8+REwLRNFnb9WmcY5IfpOqeX2kpaYJ1s6Ae3mn12AeydLkR3j+jSCmVQFoXqU8D41PAJ1RG1rCRNWmNZVmQ== - dependencies: - "@wry/caches" "^1.0.0" - "@wry/context" "^0.7.0" - "@wry/trie" "^0.4.3" - tslib "^2.3.0" - optimist@~0.6.0: version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" @@ -6381,11 +6303,6 @@ regexpp@^3.2.0: resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== -rehackt@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/rehackt/-/rehackt-0.1.0.tgz#a7c5e289c87345f70da8728a7eb878e5d03c696b" - integrity sha512-7kRDOuLHB87D/JESKxQoRwv4DzbIdwkAGQ7p6QKGdVlY1IZheUnVhlk/4UZlNUVxdAXpyxikE3URsG067ybVzw== - resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" @@ -6409,11 +6326,6 @@ resolve@^2.0.0-next.4: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -response-iterator@^0.2.6: - version "0.2.6" - resolved "https://registry.yarnpkg.com/response-iterator/-/response-iterator-0.2.6.tgz#249005fb14d2e4eeb478a3f735a28fd8b4c9f3da" - integrity sha512-pVzEEzrsg23Sh053rmDUvLSkGXluZio0qu8VT6ukrYuvtjVfCbDZH9d6PGXb8HZfzdNZt8feXv/jvUzlhRgLnw== - reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" @@ -6617,6 +6529,11 @@ solady@^0.0.234: resolved "https://registry.yarnpkg.com/solady/-/solady-0.0.234.tgz#99ddd59e38f1987683b465e5f97d6808c2c50a32" integrity sha512-twY/0NtBbOZ7fGFIqp7Ebuvfgw2yD6A44HKbEpS05RK03VH+bv81/Mg6kYI/7EK/NuATlvrACDdLYfp/c1+d4A== +solady@^0.0.235: + version "0.0.235" + resolved "https://registry.yarnpkg.com/solady/-/solady-0.0.235.tgz#50ab6e403ed6935012df2c16803fcd58045f9a0e" + integrity sha512-JUEXLDG7ag3HmqUnrDG7ilhafH6R9bFPpwV63O2kH4UbnS2+gRGEOqqy4k01O7tHjo3MWkDD0cpG+UY9pjy/fQ== + sonic-boom@^2.2.1: version "2.8.0" resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-2.8.0.tgz#c1def62a77425090e6ad7516aad8eb402e047611" @@ -6836,11 +6753,6 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -symbol-observable@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-4.0.0.tgz#5b425f192279e87f2f9b937ac8540d1984b39205" - integrity sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ== - text-encoding-utf-8@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz#585b62197b0ae437e3c7b5d0af27ac1021e10d13" @@ -6911,13 +6823,6 @@ tronweb@^4.4.0: semver "^5.6.0" validator "^13.7.0" -ts-invariant@^0.10.3: - version "0.10.3" - resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.10.3.tgz#3e048ff96e91459ffca01304dbc7f61c1f642f6c" - integrity sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ== - dependencies: - tslib "^2.1.0" - tsconfig-paths@^3.14.1, tsconfig-paths@^3.14.2: version "3.15.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" @@ -6943,11 +6848,6 @@ tslib@^2.1.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.6.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== -tslib@^2.3.0, tslib@^2.6.2: - version "2.6.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" - integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== - tslib@^2.7.0: version "2.7.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01" @@ -7338,15 +7238,3 @@ yaml@^1.10.0: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== - -zen-observable-ts@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz#6c6d9ea3d3a842812c6e9519209365a122ba8b58" - integrity sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg== - dependencies: - zen-observable "0.8.15" - -zen-observable@0.8.15: - version "0.8.15" - resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15" - integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ== From 4c091c02283fb6bc2118acc8f693d4c0ea778821 Mon Sep 17 00:00:00 2001 From: Karandeep Singh Date: Wed, 11 Sep 2024 09:17:17 -0400 Subject: [PATCH 08/18] use SMART_SESSION_ADDRESS from module-sdk --- .../react-wallet-v2/src/data/ERC7579ModuleData.ts | 4 ++-- .../builders/SmartSessionUserOpBuilder.ts | 13 +++++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/advanced/wallets/react-wallet-v2/src/data/ERC7579ModuleData.ts b/advanced/wallets/react-wallet-v2/src/data/ERC7579ModuleData.ts index 97c7453b2..b9cce6212 100644 --- a/advanced/wallets/react-wallet-v2/src/data/ERC7579ModuleData.ts +++ b/advanced/wallets/react-wallet-v2/src/data/ERC7579ModuleData.ts @@ -1,7 +1,7 @@ -import { smartSessionAddress } from '@biconomy/permission-context-builder' import { ModuleType } from '@rhinestone/module-sdk' //Note: ES6 syntax dont work for this package const { + SMART_SESSIONS_ADDRESS, MULTI_FACTOR_VALIDATOR_ADDRESS, OWNABLE_VALIDATOR_ADDRESS, WEBAUTHN_VALIDATOR_ADDRESS, @@ -31,7 +31,7 @@ export const supportedModules: Module[] = [ name: 'Permission Validator', type: 'validator', url: '/permission-validator', - moduleAddress: smartSessionAddress, + moduleAddress: SMART_SESSIONS_ADDRESS, description: `The Permission Validator module is a module that allows DApp to request permissions from a wallet in order to execute transactions on users's behalf that is scoped with permissions`, moduleData: '0x' }, diff --git a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SmartSessionUserOpBuilder.ts b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SmartSessionUserOpBuilder.ts index 973b41ab4..1eaeb0e22 100644 --- a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SmartSessionUserOpBuilder.ts +++ b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SmartSessionUserOpBuilder.ts @@ -7,7 +7,8 @@ import { PublicClient } from 'viem' import { encodeEnable, encodeUse } from './EncodeLib' -import { smartSessionAddress } from '@biconomy/permission-context-builder' +const { SMART_SESSIONS_ADDRESS } = + require('@rhinestone/module-sdk') as typeof import('@rhinestone/module-sdk') import { readContract } from 'viem/actions' export const enableSessionsStructAbi = [ @@ -215,9 +216,9 @@ export async function formatSignature( const validatorAddress = permissionsContext.slice(0, 42) as Address - if (getAddress(validatorAddress) !== getAddress(smartSessionAddress)) { + if (getAddress(validatorAddress) !== getAddress(SMART_SESSIONS_ADDRESS)) { throw new Error( - `Validator ${validatorAddress} is not the smart session validator ${smartSessionAddress}` + `Validator ${validatorAddress} is not the smart session validator ${SMART_SESSIONS_ADDRESS}` ) } @@ -225,7 +226,7 @@ export async function formatSignature( const enableSession = Array.from(decodeAbiParameters(enableSessionsStructAbi, enableData)) const isPermissionsEnabled = await readContract(publicClient, { - address: smartSessionAddress, + address: SMART_SESSIONS_ADDRESS, abi: isPermissionsEnabledAbi, functionName: 'isPermissionEnabled', args: [signerId, accountAddress, enableSession[0]] @@ -252,9 +253,9 @@ export async function getDummySignature( const validatorAddress = permissionsContext.slice(0, 42) as Address - if (getAddress(validatorAddress) !== getAddress(smartSessionAddress)) { + if (getAddress(validatorAddress) !== getAddress(SMART_SESSIONS_ADDRESS)) { throw new Error( - `Validator ${validatorAddress} is not the smart session validator ${smartSessionAddress}` + `Validator ${validatorAddress} is not the smart session validator ${SMART_SESSIONS_ADDRESS}` ) } From 6a4ce0e0e562e4ae31baa4c004649dfcbc7cf81e Mon Sep 17 00:00:00 2001 From: Karandeep Singh Date: Wed, 11 Sep 2024 09:18:08 -0400 Subject: [PATCH 09/18] impl ContextBuilderUtil using module-sdk --- .../lib/smart-accounts/SafeSmartAccountLib.ts | 12 +- .../builders/ContextBuilderUtil.ts | 589 ++++++++++++++++++ .../builders/SafeUserOpBuilder.ts | 28 +- 3 files changed, 614 insertions(+), 15 deletions(-) create mode 100644 advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/ContextBuilderUtil.ts diff --git a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SafeSmartAccountLib.ts b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SafeSmartAccountLib.ts index a640fb2eb..cae9866d1 100644 --- a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SafeSmartAccountLib.ts +++ b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SafeSmartAccountLib.ts @@ -16,14 +16,14 @@ import { } from 'viem' import { MultiKeySigner } from 'viem/_types/experimental/erc7715/types/signer' import { - getContext, mockValidator, Permission, - smartSessionAddress, userOperationBuilderAddress } from '@biconomy/permission-context-builder' import { ModuleType } from 'permissionless/actions/erc7579' - +import { getContext } from './builders/ContextBuilderUtil' +const { SMART_SESSIONS_ADDRESS } = + require('@rhinestone/module-sdk') as typeof import('@rhinestone/module-sdk') export class SafeSmartAccountLib extends SmartAccountLib { protected ERC_7579_LAUNCHPAD_ADDRESS: Address = '0xEBe001b3D534B9B6E2500FB78E67a1A137f561CE' protected SAFE_4337_MODULE_ADDRESS: Address = '0x3Fdb5BC686e861480ef99A6E3FaAe03c0b9F32e2' @@ -117,11 +117,11 @@ export class SafeSmartAccountLib extends SmartAccountLib { let smartSessionValidatorInstalled = false let mockValidatorInstalled = false - console.log(`SmartSession Address: ${smartSessionAddress[this.chain.id]}`) + console.log(`SmartSession Address: ${SMART_SESSIONS_ADDRESS}`) console.log(`mockValidator Address: ${mockValidator[this.chain.id]}`) if (isAccountDeployed) { ;[smartSessionValidatorInstalled, mockValidatorInstalled] = await Promise.all([ - this.isValidatorModuleInstalled(smartSessionAddress[this.chain.id] as Address), + this.isValidatorModuleInstalled(SMART_SESSIONS_ADDRESS as Address), this.isValidatorModuleInstalled(mockValidator[this.chain.id] as Address) ]) } @@ -142,7 +142,7 @@ export class SafeSmartAccountLib extends SmartAccountLib { if (!isAccountDeployed || !smartSessionValidatorInstalled) { installModules.push({ - address: smartSessionAddress[this.chain.id], + address: SMART_SESSIONS_ADDRESS, type: 'validator', context: '0x' }) diff --git a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/ContextBuilderUtil.ts b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/ContextBuilderUtil.ts new file mode 100644 index 000000000..b2d442a94 --- /dev/null +++ b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/ContextBuilderUtil.ts @@ -0,0 +1,589 @@ +import { decodeDIDToPublicKey, KEY_TYPES } from '@/utils/HelperUtil' +import { mockValidator, multiKeySignerAddress, timeFramePolicyAddress } from '@biconomy/permission-context-builder' +import { + type AccountType, + type Session, + type ChainSession, + type EnableSessionData, + +} from '@rhinestone/module-sdk' +const { + getAccount, + getPermissionId, + getSessionDigest, + getSessionNonce, + SMART_SESSIONS_ADDRESS, + // OWNABLE_VALIDATOR_ADDRESS, + encodeSmartSessionSignature, + SmartSessionMode, + hashChainSessions, + encodeUseOrEnableSmartSessionSignature, + encodeValidationData +} = require('@rhinestone/module-sdk') as typeof import('@rhinestone/module-sdk') + +import { LibZip } from 'solady' +import { + Address, + createPublicClient, + decodeAbiParameters, + encodeAbiParameters, + encodePacked, + Hex, + http, + parseAbiParameters, + PublicClient, + toBytes, + toFunctionSelector, + toHex, + WalletClient +} from 'viem' +import { publicKeyToAddress } from 'viem/accounts' +import { sepolia } from 'viem/chains' +import { parsePublicKey } from 'webauthn-p256' + +const MULTIKEY_SIGNER_ADDRESS = multiKeySignerAddress[sepolia.id] as Address +export enum SignerType { + EOA, + PASSKEY +} + +export type Signer = { + type: SignerType + data: string +} +export type MultiKeySigner = { + type: 'keys' + data: { + ids: string[] + address?: Address + } +} +export type Permission = { + type: PermissionType + policies: Policy[] + required: boolean + data: any +} +export type Policy = { + type: PolicyType + data: any +} +export type PermissionType = + | 'native-token-transfer' + | 'erc20-token-transfer' + | 'erc721-token-transfer' + | 'erc1155-token-transfer' + | { + custom: any + } +export type PolicyType = + | 'gas-limit' + | 'call-limit' + | 'rate-limit' + | 'spent-limit' + | 'value-limit' + | 'time-frame' + | 'uni-action' + | 'simpler-signer' + | { + custom: any + } +export type GrantPermissionsRequestParams = { + smartAccountAddress: `0x${string}` + signer: MultiKeySigner + permissions: Permission[] + expiry: number +} + +export async function getContext( + walletClient: WalletClient, + { smartAccountAddress, permissions, expiry, signer }: GrantPermissionsRequestParams +): Promise<`0x${string}`> { + if (walletClient.chain === undefined) { + throw new Error('Chain is undefined') + } + if (walletClient.account === undefined) { + throw new Error('wallet account is undefined') + } + const account = getAccount({ + address: smartAccountAddress, + type: 'safe' + }) + const chainId = walletClient.chain.id + + let signers: Signer[] = [] + // if singer type if multiKeySigner + if (signer.type === 'keys') { + const publicKeys = signer.data.ids.map(id => decodeDIDToPublicKey(id)) + publicKeys.forEach(key => { + if (key.keyType === KEY_TYPES.secp256k1) { + const eoaAddress = publicKeyToAddress(key.key) + const signer = { type: SignerType.EOA, data: eoaAddress } + signers.push(signer) + } + if (key.keyType === KEY_TYPES.secp256r1) { + const passkeyPublicKey = parsePublicKey(key.key as `0x${string}`) + const signer = { + type: SignerType.PASSKEY, + data: encodeAbiParameters(parseAbiParameters('uint256, uint256'), [ + passkeyPublicKey.x, + passkeyPublicKey.y + ]) + } + signers.push(signer) + } + }) + } + const samplePermissionsSession: Session = getSamplePermissions(signers, chainId, { + permissions, + expiry + }) + const session: Session = { + ...samplePermissionsSession, + salt: toHex(toBytes(55, { size: 32 })) + } + const publicClient = createPublicClient({ + chain: walletClient.chain, + transport: http() + }) + const permissionId = (await getPermissionId({ + client: publicClient, + session + })) as Hex + + const sessionNonce = await getSessionNonce({ + client: publicClient, + account, + permissionId + }) + + const sessionDigest = await getSessionDigest({ + client: publicClient, + account, + session, + mode: SmartSessionMode.ENABLE, + permissionId + }) + + const chainDigests = [ + { + chainId: BigInt(chainId), + sessionDigest + } + ] + + const chainSessions: ChainSession[] = [ + { + chainId: BigInt(chainId), + session: { + ...session, + account: account.address, + smartSession: SMART_SESSIONS_ADDRESS, + mode: 1, + nonce: sessionNonce + } + } + ] + const permissionEnableHash = hashChainSessions(chainSessions) + const permissionEnableSig = await walletClient.signMessage({ + account: walletClient.account, + message: { raw: permissionEnableHash } + }) + + return encodeSmartSessionSignature({ + mode: SmartSessionMode.ENABLE, + permissionId, + signature: '0xe8b94748580ca0b4993c9a1b86b5be851bfc076ff5ce3a1ff65bf16392acfcb800f9b4f1aef1555c7fce5599fffb17e7c635502154a0333ba21f3ae491839af51c', + enableSessionData: { + enableSession: { + chainDigestIndex: 0, + hashesAndChainIds: chainDigests, + sessionToEnable: session, + permissionEnableSig + }, + validator: mockValidator[chainId], + accountType: 'safe' + } + }) +} + +export async function getDummySignature( + publicClient: PublicClient, + params: { + permissionsContext: Hex + accountAddress: Address + } +) { + const { permissionsContext, accountAddress } = params + const { permissionId,signature, enableSessionData } = decodeSmartSessionSignature(permissionsContext) + const account = getAccount({ + address: accountAddress, + type: 'safe' + }) + if (!enableSessionData) { + throw new Error('EnableSessionData is undefined, invalid permissionsContext') + } + console.log("encodedSignature",signature) + const signerValidatorInitData = + enableSessionData?.enableSession.sessionToEnable.sessionValidatorInitData + console.log('multiKeySignerValidatorInitData', signerValidatorInitData) + const signers = decodeSigners(signerValidatorInitData) + console.log('signers', signers) + const dummySignatures: `0x${string}`[] = [] + const dummyECDSASignature: `0x${string}` = + '0xe8b94748580ca0b4993c9a1b86b5be851bfc076ff5ce3a1ff65bf16392acfcb800f9b4f1aef1555c7fce5599fffb17e7c635502154a0333ba21f3ae491839af51c' + const dummyPasskeySignature: `0x${string}` = '0x' + for (let i = 0; i < signers.length; i++) { + const signer = signers[i] + if (signer.type === 0) { + dummySignatures.push(dummyECDSASignature) + } else if (signer.type === 1) { + dummySignatures.push(dummyPasskeySignature) + } + } + const concatenatedDummySignature = encodeAbiParameters([{ type: 'bytes[]' }], [dummySignatures]) + console.log('concatenatedDummySignature', concatenatedDummySignature) + return encodeUseOrEnableSmartSessionSignature({ + account: account, + client: publicClient, + enableSessionData: enableSessionData, + permissionId: permissionId, + signature: concatenatedDummySignature + }) +} + +export async function formatSignature( + publicClient: PublicClient, + params: { + signature: Hex + permissionsContext: Hex + accountAddress: Address + } +) { + const { permissionsContext, signature, accountAddress } = params + const { permissionId, enableSessionData } = decodeSmartSessionSignature(permissionsContext) + const account = getAccount({ + address: accountAddress, + type: 'safe' + }) + if (!enableSessionData) { + throw new Error('EnableSessionData is undefined, invalid permissionsContext') + } + return encodeUseOrEnableSmartSessionSignature({ + account: account, + client: publicClient, + enableSessionData: enableSessionData, + permissionId: permissionId, + signature: signature + }) +} + +function getSamplePermissions( + signers: Signer[], + chainId: number, + { permissions, expiry }: Omit +): Session { + return { + sessionValidator: MULTIKEY_SIGNER_ADDRESS, + sessionValidatorInitData: encodeMultiKeySignerInitData(signers), + salt: toHex(toBytes('1', { size: 32 })), + userOpPolicies: [], + actions: permissions.map(permission => ({ + actionTarget: permission.data.target, + actionTargetSelector: toFunctionSelector(permission.data.functionName), + actionPolicies: [ + { + policy: timeFramePolicyAddress[chainId], + initData: encodePacked(['uint128', 'uint128'], [BigInt(expiry), BigInt(0)]) // hardcoded for demo + } + ] + })), + erc7739Policies: { + allowedERC7739Content: [], + erc1271Policies: [] + } + } +} + +function encodeMultiKeySignerInitData(signers: Signer[]): Hex { + let encoded: Hex = encodePacked(['uint8'], [signers.length]) + // signer.data = decoded public key from DID + for (const signer of signers) { + encoded = encodePacked(['bytes', 'uint8', 'bytes'], [encoded, signer.type, signer.data as Hex]) + } + + return encoded +} + +export function decodeSigners( + encodedData: `0x${string}` +): Array<{ type: number; data: `0x${string}` }> { + let offset = 2 // Start after '0x' + const signers: Array<{ type: number; data: `0x${string}` }> = [] + + // Decode the number of signers + const signersCount = parseInt(encodedData.slice(offset, offset + 2), 16) + offset += 2 + + for (let i = 0; i < signersCount; i++) { + // Decode signer type + const signerType = parseInt(encodedData.slice(offset, offset + 2), 16) + offset += 2 + + // Determine data length based on signer type + let dataLength: number + if (signerType === 0) { + dataLength = 40 // 20 bytes + } else if (signerType === 1) { + dataLength = 128 // 64 bytes + } else { + throw new Error(`Unknown signer type: ${signerType}`) + } + + // Decode signer data + const signerData = `0x${encodedData.slice(offset, offset + dataLength)}` as `0x${string}` + offset += dataLength + + signers.push({ type: signerType, data: signerData }) + } + + return signers +} + +export const decodeSmartSessionSignature = ( + encodedData: Hex +): { + mode: Hex + permissionId: Hex + signature: Hex + enableSessionData?: EnableSessionData +} => { + const mode = `0x${encodedData.slice(2, 4)}` as Hex + const permissionId = `0x${encodedData.slice(4, 68)}` as Hex + const compressedData = `0x${encodedData.slice(68)}` as Hex + // const [mode, permissionId, compressedData] = decodeAbiParameters( + // [{ type: 'bytes1' }, { type: 'bytes32' }, { type: 'bytes' }], + // encodedData + // ) as [Hex, Hex, Hex] + + const decompressedData = LibZip.flzDecompress(compressedData) as Hex + + switch (mode) { + case SmartSessionMode.USE: + const [signature] = decodeAbiParameters([{ type: 'bytes' }], decompressedData) as [Hex] + return { mode, permissionId, signature } + + case SmartSessionMode.ENABLE: + case SmartSessionMode.UNSAFE_ENABLE: + const { enableSessionData, signature: enableSignature } = + decodeEnableSessionSignature(decompressedData) + return { mode, permissionId, signature: enableSignature, enableSessionData } + + default: + throw new Error(`Unknown mode ${mode}`) + } +} + +const decodeEnableSessionSignature = ( + encodedData: Hex +): { enableSessionData: EnableSessionData; signature: Hex } => { + const [enableSessionDataEncoded, signature] = decodeAbiParameters( + encodeEnableSessionSignatureAbi, + encodedData + ) as [any, Hex] + + const { chainDigestIndex, hashesAndChainIds, sessionToEnable, permissionEnableSig } = + enableSessionDataEncoded + + const { validator, accountType, decodedSignature } = + decodePermissionEnableSig(permissionEnableSig) + + const enableSessionData: EnableSessionData = { + enableSession: { + chainDigestIndex, + hashesAndChainIds: hashesAndChainIds.map((digest: any) => ({ + chainId: BigInt(digest.chainId), + sessionDigest: digest.sessionDigest + })), + sessionToEnable: { + sessionValidator: sessionToEnable.sessionValidator, + sessionValidatorInitData: sessionToEnable.sessionValidatorInitData, + salt: sessionToEnable.salt, + userOpPolicies: sessionToEnable.userOpPolicies.map((policy: any) => ({ + policy: policy.policy, + initData: policy.initData + })), + erc7739Policies: { + allowedERC7739Content: sessionToEnable.erc7739Policies.allowedERC7739Content, + erc1271Policies: sessionToEnable.erc7739Policies.erc1271Policies.map((policy: any) => ({ + policy: policy.policy, + initData: policy.initData + })) + }, + actions: sessionToEnable.actions.map((action: any) => ({ + actionTargetSelector: action.actionTargetSelector, + actionTarget: action.actionTarget, + actionPolicies: action.actionPolicies.map((policy: any) => ({ + policy: policy.policy, + initData: policy.initData + })) + })) + }, + permissionEnableSig: decodedSignature + }, + validator, + accountType + } + + return { enableSessionData, signature } +} + +const decodePermissionEnableSig = ( + encodedSig: Hex +): { validator: Address; accountType: AccountType; decodedSignature: Hex } => { + if (encodedSig.startsWith('0x01')) { + // Kernel type + const validator = `0x${encodedSig.slice(4, 44)}` as Address + const signature = `0x${encodedSig.slice(44)}` as Hex + // const [, validator, signature] = decodeAbiParameters( + // [{ type: 'bytes1' }, { type: 'address' }, { type: 'bytes' }], + // encodedSig + // ) as [Hex, Address, Hex] + return { validator, accountType: 'kernel', decodedSignature: signature } + } else { + // Other types (erc7579-implementation, nexus, safe) + const validator = `0x${encodedSig.slice(2, 42)}` as Address + const signature = `0x${encodedSig.slice(42)}` as Hex + // const [validator, signature] = decodeAbiParameters( + // [{ type: 'address' }, { type: 'bytes' }], + // encodedSig + // ) as [Address, Hex] + // Note: We can't determine the exact account type here, so we'll default to 'erc7579-implementation' + return { validator, accountType: 'erc7579-implementation', decodedSignature: signature } + } +} + +export const encodeEnableSessionSignatureAbi = [ + { + components: [ + { + type: 'uint8', + name: 'chainDigestIndex' + }, + { + type: 'tuple[]', + components: [ + { + internalType: 'uint64', + name: 'chainId', + type: 'uint64' + }, + { + internalType: 'bytes32', + name: 'sessionDigest', + type: 'bytes32' + } + ], + name: 'hashesAndChainIds' + }, + { + components: [ + { + internalType: 'contract ISessionValidator', + name: 'sessionValidator', + type: 'address' + }, + { + internalType: 'bytes', + name: 'sessionValidatorInitData', + type: 'bytes' + }, + { internalType: 'bytes32', name: 'salt', type: 'bytes32' }, + { + components: [ + { internalType: 'address', name: 'policy', type: 'address' }, + { internalType: 'bytes', name: 'initData', type: 'bytes' } + ], + internalType: 'struct PolicyData[]', + name: 'userOpPolicies', + type: 'tuple[]' + }, + { + components: [ + { + internalType: 'string[]', + name: 'allowedERC7739Content', + type: 'string[]' + }, + { + components: [ + { + internalType: 'address', + name: 'policy', + type: 'address' + }, + { + internalType: 'bytes', + name: 'initData', + type: 'bytes' + } + ], + internalType: 'struct PolicyData[]', + name: 'erc1271Policies', + type: 'tuple[]' + } + ], + internalType: 'struct ERC7739Data', + name: 'erc7739Policies', + type: 'tuple' + }, + { + components: [ + { + internalType: 'bytes4', + name: 'actionTargetSelector', + type: 'bytes4' + }, + { + internalType: 'address', + name: 'actionTarget', + type: 'address' + }, + { + components: [ + { + internalType: 'address', + name: 'policy', + type: 'address' + }, + { + internalType: 'bytes', + name: 'initData', + type: 'bytes' + } + ], + internalType: 'struct PolicyData[]', + name: 'actionPolicies', + type: 'tuple[]' + } + ], + internalType: 'struct ActionData[]', + name: 'actions', + type: 'tuple[]' + } + ], + internalType: 'struct Session', + name: 'sessionToEnable', + type: 'tuple' + }, + { + type: 'bytes', + name: 'permissionEnableSig' + } + ], + internalType: 'struct EnableSession', + name: 'enableSession', + type: 'tuple' + }, + { type: 'bytes' } +] as const diff --git a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SafeUserOpBuilder.ts b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SafeUserOpBuilder.ts index 214c59217..01a29d82c 100644 --- a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SafeUserOpBuilder.ts +++ b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SafeUserOpBuilder.ts @@ -33,7 +33,10 @@ import { bundlerUrl, paymasterUrl, publicClientUrl } from '@/utils/SmartAccountU import { getChainById } from '@/utils/ChainUtil' import { SAFE_FALLBACK_HANDLER_STORAGE_SLOT } from '@/consts/smartAccounts' -import { formatSignature } from './SmartSessionUserOpBuilder' +import { formatSignature, getDummySignature } from './ContextBuilderUtil' +const { + SMART_SESSIONS_ADDRESS, +} = require('@rhinestone/module-sdk') as typeof import('@rhinestone/module-sdk') const ERC_7579_LAUNCHPAD_ADDRESS: Address = '0xEBe001b3D534B9B6E2500FB78E67a1A137f561CE' @@ -96,15 +99,16 @@ export class SafeUserOpBuilder implements UserOpBuilder { chain: this.chain, bundlerTransport, middleware: { - sponsorUserOperation: - params.capabilities.paymasterService && paymasterClient.sponsorUserOperation, // optional + sponsorUserOperation:paymasterClient.sponsorUserOperation, + // params.capabilities.paymasterService && paymasterClient.sponsorUserOperation, // optional gasPrice: async () => (await pimlicoBundlerClient.getUserOperationGasPrice()).fast // if using pimlico bundler } }) const account = smartAccountClient.account - - const validatorAddress = (params.capabilities.permissions?.context.slice(0, 42) || - zeroAddress) as Address + const nonceKey = SMART_SESSIONS_ADDRESS + // const validatorAddress = (params.capabilities.permissions?.context.slice(0, 42) || + // zeroAddress) as Address + const validatorAddress = SMART_SESSIONS_ADDRESS as Address let nonce: bigint = await getAccountNonce(this.publicClient, { sender: this.accountAddress, entryPoint: ENTRYPOINT_ADDRESS_V07, @@ -116,13 +120,19 @@ export class SafeUserOpBuilder implements UserOpBuilder { ) }) + const signature = await getDummySignature(this.publicClient, { + permissionsContext: params.capabilities.permissions?.context!, + accountAddress: this.accountAddress + }) + console.log('dummySignature', signature) const userOp = await smartAccountClient.prepareUserOperationRequest({ userOperation: { nonce: nonce, callData: await account.encodeCallData(params.calls), - callGasLimit: BigInt('0x1E8480'), - verificationGasLimit: BigInt('0x1E8480'), - preVerificationGas: BigInt('0x1E8480') + // callGasLimit: BigInt('0x1E8480'), + // verificationGasLimit: BigInt('0x1E8480'), + // preVerificationGas: BigInt('0x1E8480'), + signature: signature }, account: account }) From d90370a5d5c1f6f2c6d5896594dd8338eb481df6 Mon Sep 17 00:00:00 2001 From: Karandeep Singh Date: Wed, 11 Sep 2024 09:19:04 -0400 Subject: [PATCH 10/18] enable safeSmartAccount on sepolia and baseSepolia --- .../src/utils/SmartAccountUtil.ts | 56 +++++++++++++++---- 1 file changed, 44 insertions(+), 12 deletions(-) diff --git a/advanced/wallets/react-wallet-v2/src/utils/SmartAccountUtil.ts b/advanced/wallets/react-wallet-v2/src/utils/SmartAccountUtil.ts index 38794eba7..c952cf6bc 100644 --- a/advanced/wallets/react-wallet-v2/src/utils/SmartAccountUtil.ts +++ b/advanced/wallets/react-wallet-v2/src/utils/SmartAccountUtil.ts @@ -109,21 +109,46 @@ export function isAllowedSafeChain(chainId: number): boolean { return safeAllowedChains.some(chain => chain.id == chainId) } +const initializeSafeSmartAccountLib = async (chain: Chain, privateKey: string) => { + try { + const lib = new SafeSmartAccountLib({ + privateKey, + chain, + sponsored: true, + entryPointVersion: 7 + }) + await lib.init() + return lib + } catch (error) { + console.error(`Error initializing SafeSmartAccountLib for chain ${chain}:`, error) + return null // or throw error if you want to stop the entire process + } +} export async function createOrRestoreSafeSmartAccount(privateKey: string) { - const lib = new SafeSmartAccountLib({ - privateKey, - chain: sepolia, - sponsored: true, - entryPointVersion: 7 - }) - await lib.init() - const address = lib.getAddress() - const key = `${sepolia.id}:${address}` - if (!smartAccountWallets[key]) { - smartAccountWallets[key] = lib + if (safeAllowedChains.length === 0) { + throw new Error('No allowed chains for SafeSmartAccount') } + + const libs = await Promise.all( + safeAllowedChains.map(chain => initializeSafeSmartAccountLib(chain, privateKey)) + ).then(results => results.filter((lib): lib is NonNullable => lib !== null)) + + if (libs.length === 0) { + throw new Error('No safe smart account initialized') + } + + libs.forEach(lib => { + const address = lib.getAddress() + const key = `${lib.chain.id}:${address}` + if (!smartAccountWallets[key]) { + smartAccountWallets[key] = lib + } + }) + + const safeSmartAccountAddress: string = libs[0].getAddress() + return { - safeSmartAccountAddress: address + safeSmartAccountAddress } } export function removeSmartAccount(address: string) { @@ -132,6 +157,13 @@ export function removeSmartAccount(address: string) { delete smartAccountWallets[key] } } +export function removeSmartAccounts(addresses: string[]) { + addresses.forEach(address => { + if (smartAccountWallets[address]) { + delete smartAccountWallets[address] + } + }) +} export async function createOrRestoreBiconomySmartAccount(privateKey: string) { const lib = new BiconomySmartAccountLib({ privateKey, chain: sepolia, sponsored: true }) From 45d4694d69cde8b2405b3d4698a6ef4ddd736ee3 Mon Sep 17 00:00:00 2001 From: Karandeep Singh Date: Fri, 13 Sep 2024 19:27:05 -0400 Subject: [PATCH 11/18] added smart-session support on base-sepolia --- advanced/wallets/react-wallet-v2/package.json | 3 +- .../react-wallet-v2/src/data/EIP7715Data.ts | 76 +++ .../lib/smart-accounts/SafeSmartAccountLib.ts | 62 ++- .../builders/ContextBuilderUtil.ts | 453 ++++-------------- .../lib/smart-accounts/builders/EncodeLib.ts | 40 -- .../builders/SafeUserOpBuilder.ts | 143 +++--- .../builders/SmartSessionUserOpBuilder.ts | 322 ------------- .../builders/SmartSessionUtil.ts | 22 + .../smart-accounts/builders/UserOpBuilder.ts | 48 +- .../builders/WalletConnectCosignerUtils.ts | 169 +++++++ .../react-wallet-v2/src/pages/api/build.ts | 7 +- .../src/pages/api/sendUserOp.ts | 35 +- .../src/utils/ConstantsUtil.ts | 2 + advanced/wallets/react-wallet-v2/yarn.lock | 35 +- 14 files changed, 583 insertions(+), 834 deletions(-) delete mode 100644 advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/EncodeLib.ts delete mode 100644 advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SmartSessionUserOpBuilder.ts create mode 100644 advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SmartSessionUtil.ts create mode 100644 advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/WalletConnectCosignerUtils.ts create mode 100644 advanced/wallets/react-wallet-v2/src/utils/ConstantsUtil.ts diff --git a/advanced/wallets/react-wallet-v2/package.json b/advanced/wallets/react-wallet-v2/package.json index 8dc2f3223..4193c608d 100644 --- a/advanced/wallets/react-wallet-v2/package.json +++ b/advanced/wallets/react-wallet-v2/package.json @@ -10,7 +10,6 @@ "prettier:write": "prettier --write '**/*.{js,ts,jsx,tsx}'" }, "dependencies": { - "@biconomy/permission-context-builder": "^1.1.2", "@cosmjs/amino": "0.32.3", "@cosmjs/encoding": "0.32.3", "@cosmjs/proto-signing": "0.32.3", @@ -29,7 +28,7 @@ "@nextui-org/react": "1.0.8-beta.5", "@polkadot/keyring": "^10.1.2", "@polkadot/types": "^9.3.3", - "@rhinestone/module-sdk": "0.1.13", + "@rhinestone/module-sdk": "0.1.15", "@solana/web3.js": "1.89.2", "@taquito/signer": "^15.1.0", "@taquito/taquito": "^15.1.0", diff --git a/advanced/wallets/react-wallet-v2/src/data/EIP7715Data.ts b/advanced/wallets/react-wallet-v2/src/data/EIP7715Data.ts index bda174d30..61652e732 100644 --- a/advanced/wallets/react-wallet-v2/src/data/EIP7715Data.ts +++ b/advanced/wallets/react-wallet-v2/src/data/EIP7715Data.ts @@ -1,6 +1,82 @@ +import { Address } from 'viem' + /** * EIP7715Method */ export const EIP7715_METHOD = { WALLET_GRANT_PERMISSIONS: 'wallet_grantPermissions' } + +// `data` is not necessary for this signer type as the wallet is both the signer and grantor of these permissions +export type WalletSigner = { + type: 'wallet' + data: {} +} + +// A signer representing a single key. +// `id` is a did:key identifier and can therefore represent both Secp256k1 or Secp256r1 keys, among other key types. +export type KeySigner = { + type: 'key' + data: { + id: string + } +} + +// A signer representing a multisig signer. +// Each element of `ids` is a did:key identifier just like the `key` signer. +export type MultiKeySigner = { + type: 'keys' + data: { + ids: string[] + address?: Address + } +} + +// An account that can be granted with permissions as in ERC-7710. +export type AccountSigner = { + type: 'account' + data: { + id: `0x${string}` + } +} + +export enum SignerType { + EOA, + PASSKEY +} + +export type Signer = { + type: SignerType + data: string +} + +export type Permission = { + type: PermissionType + policies: Policy[] + required: boolean + data: any +} +export type Policy = { + type: PolicyType + data: any +} +export type PermissionType = + | 'native-token-transfer' + | 'erc20-token-transfer' + | 'erc721-token-transfer' + | 'erc1155-token-transfer' + | { + custom: any + } +export type PolicyType = + | 'gas-limit' + | 'call-limit' + | 'rate-limit' + | 'spent-limit' + | 'value-limit' + | 'time-frame' + | 'uni-action' + | 'simpler-signer' + | { + custom: any + } diff --git a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SafeSmartAccountLib.ts b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SafeSmartAccountLib.ts index cae9866d1..48b0d3bd9 100644 --- a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SafeSmartAccountLib.ts +++ b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SafeSmartAccountLib.ts @@ -11,18 +11,16 @@ import { Hex, WalletGrantPermissionsParameters, createWalletClient, + encodeFunctionData, http, type WalletGrantPermissionsReturnType } from 'viem' import { MultiKeySigner } from 'viem/_types/experimental/erc7715/types/signer' -import { - mockValidator, - Permission, - userOperationBuilderAddress -} from '@biconomy/permission-context-builder' import { ModuleType } from 'permissionless/actions/erc7579' -import { getContext } from './builders/ContextBuilderUtil' -const { SMART_SESSIONS_ADDRESS } = +import { MOCK_VALIDATOR_ADDRESSES } from './builders/SmartSessionUtil' +import { Permission } from '@/data/EIP7715Data' +import { getSmartSessionContext } from './builders/ContextBuilderUtil' +const { SMART_SESSIONS_ADDRESS, getAccount } = require('@rhinestone/module-sdk') as typeof import('@rhinestone/module-sdk') export class SafeSmartAccountLib extends SmartAccountLib { protected ERC_7579_LAUNCHPAD_ADDRESS: Address = '0xEBe001b3D534B9B6E2500FB78E67a1A137f561CE' @@ -75,11 +73,15 @@ export class SafeSmartAccountLib extends SmartAccountLib { console.log('walletClient chainId:', walletClient.chain.id) let permissionContext = '0x' try { - permissionContext = await getContext(walletClient, { + permissionContext = await getSmartSessionContext({ + walletClient, + account: getAccount({ + address: this.client.account.address, + type: 'safe' + }), permissions: [...grantPermissionsRequestParameters.permissions] as unknown as Permission[], expiry: grantPermissionsRequestParameters.expiry, - signer: grantPermissionsRequestParameters.signer as MultiKeySigner, - smartAccountAddress: this.client.account.address + signer: grantPermissionsRequestParameters.signer as MultiKeySigner }) } catch (error) { console.error(`Error getting permission context: ${error}`) @@ -92,7 +94,6 @@ export class SafeSmartAccountLib extends SmartAccountLib { grantedPermissions: grantPermissionsRequestParameters.permissions, expiry: grantPermissionsRequestParameters.expiry, signerData: { - userOpBuilder: userOperationBuilderAddress[this.chain.id] as Address, submitToAddress: this.client.account.address } } as WalletGrantPermissionsReturnType @@ -114,15 +115,17 @@ export class SafeSmartAccountLib extends SmartAccountLib { this.publicClient, this.client.account.address ) + // TODO: check if account trust the attesters of the module + await this.trustAttesters() let smartSessionValidatorInstalled = false let mockValidatorInstalled = false console.log(`SmartSession Address: ${SMART_SESSIONS_ADDRESS}`) - console.log(`mockValidator Address: ${mockValidator[this.chain.id]}`) + console.log(`mockValidator Address: ${MOCK_VALIDATOR_ADDRESSES[this.chain.id]}`) if (isAccountDeployed) { ;[smartSessionValidatorInstalled, mockValidatorInstalled] = await Promise.all([ this.isValidatorModuleInstalled(SMART_SESSIONS_ADDRESS as Address), - this.isValidatorModuleInstalled(mockValidator[this.chain.id] as Address) + this.isValidatorModuleInstalled(MOCK_VALIDATOR_ADDRESSES[this.chain.id] as Address) ]) } console.log({ smartSessionValidatorInstalled, mockValidatorInstalled }) @@ -150,7 +153,7 @@ export class SafeSmartAccountLib extends SmartAccountLib { if (!isAccountDeployed || !mockValidatorInstalled) { installModules.push({ - address: mockValidator[this.chain.id], + address: MOCK_VALIDATOR_ADDRESSES[this.chain.id], type: 'validator', context: '0x' }) @@ -193,4 +196,35 @@ export class SafeSmartAccountLib extends SmartAccountLib { const receipt = await this.bundlerClient.waitForUserOperationReceipt({ hash: userOpHash }) console.log(`Module installation receipt:`, receipt) } + + private async trustAttesters(): Promise { + if (!this.client?.account) { + throw new Error('Client not initialized') + } + const trustAttestersAction = { + to: '0x000000000069E2a187AEFFb852bF3cCdC95151B2' as Address, // mock-registry + value: BigInt(0), + data: encodeFunctionData({ + abi: [ + { + inputs: [ + { type: 'uint8', name: 'threshold' }, + { type: 'address[]', name: 'attesters' } + ], + name: 'trustAttesters', + type: 'function', + stateMutability: 'nonpayable', + outputs: [] + } + ], + functionName: 'trustAttesters', + args: [1, ['0xA4C777199658a41688E9488c4EcbD7a2925Cc23A']] + }) + } + + const userOpHash = await this.sendTransaction(trustAttestersAction) + console.log(`Trust Attesters userOpHash:`, userOpHash) + // const receipt = await this.bundlerClient.waitForUserOperationReceipt({ hash: userOpHash }) + // console.log(`Trust Attesters receipt:`, receipt) + } } diff --git a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/ContextBuilderUtil.ts b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/ContextBuilderUtil.ts index b2d442a94..09c6ae6f2 100644 --- a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/ContextBuilderUtil.ts +++ b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/ContextBuilderUtil.ts @@ -1,114 +1,79 @@ import { decodeDIDToPublicKey, KEY_TYPES } from '@/utils/HelperUtil' -import { mockValidator, multiKeySignerAddress, timeFramePolicyAddress } from '@biconomy/permission-context-builder' import { - type AccountType, - type Session, - type ChainSession, - type EnableSessionData, - -} from '@rhinestone/module-sdk' + MOCK_VALIDATOR_ADDRESSES, + MULTIKEY_SIGNER_ADDRESSES, + TIME_FRAME_POLICY_ADDRESSES +} from './SmartSessionUtil' +import type { Session, ChainSession, Account } from '@rhinestone/module-sdk' const { - getAccount, getPermissionId, getSessionDigest, getSessionNonce, SMART_SESSIONS_ADDRESS, - // OWNABLE_VALIDATOR_ADDRESS, encodeSmartSessionSignature, SmartSessionMode, hashChainSessions, encodeUseOrEnableSmartSessionSignature, - encodeValidationData + decodeSmartSessionSignature } = require('@rhinestone/module-sdk') as typeof import('@rhinestone/module-sdk') - -import { LibZip } from 'solady' import { - Address, createPublicClient, - decodeAbiParameters, encodeAbiParameters, encodePacked, Hex, http, + pad, parseAbiParameters, PublicClient, + slice, toBytes, toFunctionSelector, toHex, WalletClient } from 'viem' import { publicKeyToAddress } from 'viem/accounts' -import { sepolia } from 'viem/chains' +import { ENTRYPOINT_ADDRESS_V07, getAccountNonce } from 'permissionless' import { parsePublicKey } from 'webauthn-p256' +import { MultiKeySigner, Permission, Signer, SignerType } from '@/data/EIP7715Data' -const MULTIKEY_SIGNER_ADDRESS = multiKeySignerAddress[sepolia.id] as Address -export enum SignerType { - EOA, - PASSKEY -} - -export type Signer = { - type: SignerType - data: string +type GetNonceWithContextParams = { + publicClient: PublicClient + account: Account + permissionsContext: Hex } -export type MultiKeySigner = { - type: 'keys' - data: { - ids: string[] - address?: Address - } -} -export type Permission = { - type: PermissionType - policies: Policy[] - required: boolean - data: any +type GetDummySignatureParams = { + publicClient: PublicClient + permissionsContext: Hex + account: Account } -export type Policy = { - type: PolicyType - data: any +type FormatSignatureParams = { + publicClient: PublicClient + modifiedSignature: Hex + permissionsContext: Hex + account: Account } -export type PermissionType = - | 'native-token-transfer' - | 'erc20-token-transfer' - | 'erc721-token-transfer' - | 'erc1155-token-transfer' - | { - custom: any - } -export type PolicyType = - | 'gas-limit' - | 'call-limit' - | 'rate-limit' - | 'spent-limit' - | 'value-limit' - | 'time-frame' - | 'uni-action' - | 'simpler-signer' - | { - custom: any - } -export type GrantPermissionsRequestParams = { - smartAccountAddress: `0x${string}` - signer: MultiKeySigner +type GetSmartSessionContextParams = { + walletClient: WalletClient + account: Account permissions: Permission[] expiry: number + signer: MultiKeySigner } -export async function getContext( - walletClient: WalletClient, - { smartAccountAddress, permissions, expiry, signer }: GrantPermissionsRequestParams -): Promise<`0x${string}`> { +export async function getSmartSessionContext({ + walletClient, + account, + permissions, + expiry, + signer +}: GetSmartSessionContextParams): Promise<`0x${string}`> { if (walletClient.chain === undefined) { - throw new Error('Chain is undefined') + throw new Error('GetSmartSessionContextParams:Chain is undefined') } if (walletClient.account === undefined) { throw new Error('wallet account is undefined') } - const account = getAccount({ - address: smartAccountAddress, - type: 'safe' - }) + const chainId = walletClient.chain.id let signers: Signer[] = [] @@ -190,7 +155,7 @@ export async function getContext( message: { raw: permissionEnableHash } }) - return encodeSmartSessionSignature({ + const encodedSmartSessionSignature = encodeSmartSessionSignature({ mode: SmartSessionMode.ENABLE, permissionId, signature: '0xe8b94748580ca0b4993c9a1b86b5be851bfc076ff5ce3a1ff65bf16392acfcb800f9b4f1aef1555c7fce5599fffb17e7c635502154a0333ba21f3ae491839af51c', @@ -201,32 +166,38 @@ export async function getContext( sessionToEnable: session, permissionEnableSig }, - validator: mockValidator[chainId], + validator: MOCK_VALIDATOR_ADDRESSES[chainId], //smartAccountAddress, accountType: 'safe' } }) -} -export async function getDummySignature( - publicClient: PublicClient, - params: { - permissionsContext: Hex - accountAddress: Address + const smartSessionContext = encodePacked( + ['address', 'bytes'], + [SMART_SESSIONS_ADDRESS, encodedSmartSessionSignature] + ) + return smartSessionContext +} +export async function getDummySignature({ + publicClient, + permissionsContext, + account +}: GetDummySignatureParams) { + const validatorAddress = slice(permissionsContext, 0, 20) + if (validatorAddress.toLowerCase() !== SMART_SESSIONS_ADDRESS.toLowerCase()) { + throw new Error('getDummySignature:Invalid permission context') } -) { - const { permissionsContext, accountAddress } = params - const { permissionId,signature, enableSessionData } = decodeSmartSessionSignature(permissionsContext) - const account = getAccount({ - address: accountAddress, - type: 'safe' + + const smartSessionSignature = slice(permissionsContext, 20) + const { permissionId, enableSessionData } = decodeSmartSessionSignature({ + signature: smartSessionSignature, + account: account }) + if (!enableSessionData) { - throw new Error('EnableSessionData is undefined, invalid permissionsContext') + throw new Error('EnableSessionData is undefined, invalid smartSessionSignature') } - console.log("encodedSignature",signature) const signerValidatorInitData = enableSessionData?.enableSession.sessionToEnable.sessionValidatorInitData - console.log('multiKeySignerValidatorInitData', signerValidatorInitData) const signers = decodeSigners(signerValidatorInitData) console.log('signers', signers) const dummySignatures: `0x${string}`[] = [] @@ -242,7 +213,7 @@ export async function getDummySignature( } } const concatenatedDummySignature = encodeAbiParameters([{ type: 'bytes[]' }], [dummySignatures]) - console.log('concatenatedDummySignature', concatenatedDummySignature) + return encodeUseOrEnableSmartSessionSignature({ account: account, client: publicClient, @@ -251,40 +222,65 @@ export async function getDummySignature( signature: concatenatedDummySignature }) } - -export async function formatSignature( - publicClient: PublicClient, - params: { - signature: Hex - permissionsContext: Hex - accountAddress: Address +export async function formatSignature({ + publicClient, + account, + modifiedSignature, + permissionsContext +}: FormatSignatureParams) { + const validatorAddress = slice(permissionsContext, 0, 20) + if (validatorAddress.toLowerCase() !== SMART_SESSIONS_ADDRESS.toLowerCase()) { + throw new Error('formatSignature:Invalid permission context') } -) { - const { permissionsContext, signature, accountAddress } = params - const { permissionId, enableSessionData } = decodeSmartSessionSignature(permissionsContext) - const account = getAccount({ - address: accountAddress, - type: 'safe' + + const smartSessionSignature = slice(permissionsContext, 20) + const { permissionId, enableSessionData } = decodeSmartSessionSignature({ + signature: smartSessionSignature, + account: account }) + if (!enableSessionData) { - throw new Error('EnableSessionData is undefined, invalid permissionsContext') + throw new Error('EnableSessionData is undefined, invalid smartSessionSignature') } + return encodeUseOrEnableSmartSessionSignature({ account: account, client: publicClient, enableSessionData: enableSessionData, permissionId: permissionId, - signature: signature + signature: modifiedSignature + }) +} +export async function getNonce({ + publicClient, + account, + permissionsContext +}: GetNonceWithContextParams): Promise { + const chainId = await publicClient.getChainId() + const validatorAddress = slice(permissionsContext, 0, 20) + if (validatorAddress.toLowerCase() !== SMART_SESSIONS_ADDRESS.toLowerCase()) { + throw new Error('getNonce:Invalid permission context') + } + + return await getAccountNonce(publicClient, { + sender: account.address, + entryPoint: ENTRYPOINT_ADDRESS_V07, + key: BigInt( + pad(validatorAddress, { + dir: 'right', + size: 24 + }) || 0 + ) }) } function getSamplePermissions( signers: Signer[], chainId: number, - { permissions, expiry }: Omit + { permissions, expiry }: { permissions: Permission[]; expiry: number } ): Session { return { - sessionValidator: MULTIKEY_SIGNER_ADDRESS, + sessionValidator: MULTIKEY_SIGNER_ADDRESSES[chainId], sessionValidatorInitData: encodeMultiKeySignerInitData(signers), salt: toHex(toBytes('1', { size: 32 })), userOpPolicies: [], @@ -293,7 +289,7 @@ function getSamplePermissions( actionTargetSelector: toFunctionSelector(permission.data.functionName), actionPolicies: [ { - policy: timeFramePolicyAddress[chainId], + policy: TIME_FRAME_POLICY_ADDRESSES[chainId], initData: encodePacked(['uint128', 'uint128'], [BigInt(expiry), BigInt(0)]) // hardcoded for demo } ] @@ -307,7 +303,6 @@ function getSamplePermissions( function encodeMultiKeySignerInitData(signers: Signer[]): Hex { let encoded: Hex = encodePacked(['uint8'], [signers.length]) - // signer.data = decoded public key from DID for (const signer of signers) { encoded = encodePacked(['bytes', 'uint8', 'bytes'], [encoded, signer.type, signer.data as Hex]) } @@ -315,9 +310,7 @@ function encodeMultiKeySignerInitData(signers: Signer[]): Hex { return encoded } -export function decodeSigners( - encodedData: `0x${string}` -): Array<{ type: number; data: `0x${string}` }> { +function decodeSigners(encodedData: `0x${string}`): Array<{ type: number; data: `0x${string}` }> { let offset = 2 // Start after '0x' const signers: Array<{ type: number; data: `0x${string}` }> = [] @@ -349,241 +342,3 @@ export function decodeSigners( return signers } - -export const decodeSmartSessionSignature = ( - encodedData: Hex -): { - mode: Hex - permissionId: Hex - signature: Hex - enableSessionData?: EnableSessionData -} => { - const mode = `0x${encodedData.slice(2, 4)}` as Hex - const permissionId = `0x${encodedData.slice(4, 68)}` as Hex - const compressedData = `0x${encodedData.slice(68)}` as Hex - // const [mode, permissionId, compressedData] = decodeAbiParameters( - // [{ type: 'bytes1' }, { type: 'bytes32' }, { type: 'bytes' }], - // encodedData - // ) as [Hex, Hex, Hex] - - const decompressedData = LibZip.flzDecompress(compressedData) as Hex - - switch (mode) { - case SmartSessionMode.USE: - const [signature] = decodeAbiParameters([{ type: 'bytes' }], decompressedData) as [Hex] - return { mode, permissionId, signature } - - case SmartSessionMode.ENABLE: - case SmartSessionMode.UNSAFE_ENABLE: - const { enableSessionData, signature: enableSignature } = - decodeEnableSessionSignature(decompressedData) - return { mode, permissionId, signature: enableSignature, enableSessionData } - - default: - throw new Error(`Unknown mode ${mode}`) - } -} - -const decodeEnableSessionSignature = ( - encodedData: Hex -): { enableSessionData: EnableSessionData; signature: Hex } => { - const [enableSessionDataEncoded, signature] = decodeAbiParameters( - encodeEnableSessionSignatureAbi, - encodedData - ) as [any, Hex] - - const { chainDigestIndex, hashesAndChainIds, sessionToEnable, permissionEnableSig } = - enableSessionDataEncoded - - const { validator, accountType, decodedSignature } = - decodePermissionEnableSig(permissionEnableSig) - - const enableSessionData: EnableSessionData = { - enableSession: { - chainDigestIndex, - hashesAndChainIds: hashesAndChainIds.map((digest: any) => ({ - chainId: BigInt(digest.chainId), - sessionDigest: digest.sessionDigest - })), - sessionToEnable: { - sessionValidator: sessionToEnable.sessionValidator, - sessionValidatorInitData: sessionToEnable.sessionValidatorInitData, - salt: sessionToEnable.salt, - userOpPolicies: sessionToEnable.userOpPolicies.map((policy: any) => ({ - policy: policy.policy, - initData: policy.initData - })), - erc7739Policies: { - allowedERC7739Content: sessionToEnable.erc7739Policies.allowedERC7739Content, - erc1271Policies: sessionToEnable.erc7739Policies.erc1271Policies.map((policy: any) => ({ - policy: policy.policy, - initData: policy.initData - })) - }, - actions: sessionToEnable.actions.map((action: any) => ({ - actionTargetSelector: action.actionTargetSelector, - actionTarget: action.actionTarget, - actionPolicies: action.actionPolicies.map((policy: any) => ({ - policy: policy.policy, - initData: policy.initData - })) - })) - }, - permissionEnableSig: decodedSignature - }, - validator, - accountType - } - - return { enableSessionData, signature } -} - -const decodePermissionEnableSig = ( - encodedSig: Hex -): { validator: Address; accountType: AccountType; decodedSignature: Hex } => { - if (encodedSig.startsWith('0x01')) { - // Kernel type - const validator = `0x${encodedSig.slice(4, 44)}` as Address - const signature = `0x${encodedSig.slice(44)}` as Hex - // const [, validator, signature] = decodeAbiParameters( - // [{ type: 'bytes1' }, { type: 'address' }, { type: 'bytes' }], - // encodedSig - // ) as [Hex, Address, Hex] - return { validator, accountType: 'kernel', decodedSignature: signature } - } else { - // Other types (erc7579-implementation, nexus, safe) - const validator = `0x${encodedSig.slice(2, 42)}` as Address - const signature = `0x${encodedSig.slice(42)}` as Hex - // const [validator, signature] = decodeAbiParameters( - // [{ type: 'address' }, { type: 'bytes' }], - // encodedSig - // ) as [Address, Hex] - // Note: We can't determine the exact account type here, so we'll default to 'erc7579-implementation' - return { validator, accountType: 'erc7579-implementation', decodedSignature: signature } - } -} - -export const encodeEnableSessionSignatureAbi = [ - { - components: [ - { - type: 'uint8', - name: 'chainDigestIndex' - }, - { - type: 'tuple[]', - components: [ - { - internalType: 'uint64', - name: 'chainId', - type: 'uint64' - }, - { - internalType: 'bytes32', - name: 'sessionDigest', - type: 'bytes32' - } - ], - name: 'hashesAndChainIds' - }, - { - components: [ - { - internalType: 'contract ISessionValidator', - name: 'sessionValidator', - type: 'address' - }, - { - internalType: 'bytes', - name: 'sessionValidatorInitData', - type: 'bytes' - }, - { internalType: 'bytes32', name: 'salt', type: 'bytes32' }, - { - components: [ - { internalType: 'address', name: 'policy', type: 'address' }, - { internalType: 'bytes', name: 'initData', type: 'bytes' } - ], - internalType: 'struct PolicyData[]', - name: 'userOpPolicies', - type: 'tuple[]' - }, - { - components: [ - { - internalType: 'string[]', - name: 'allowedERC7739Content', - type: 'string[]' - }, - { - components: [ - { - internalType: 'address', - name: 'policy', - type: 'address' - }, - { - internalType: 'bytes', - name: 'initData', - type: 'bytes' - } - ], - internalType: 'struct PolicyData[]', - name: 'erc1271Policies', - type: 'tuple[]' - } - ], - internalType: 'struct ERC7739Data', - name: 'erc7739Policies', - type: 'tuple' - }, - { - components: [ - { - internalType: 'bytes4', - name: 'actionTargetSelector', - type: 'bytes4' - }, - { - internalType: 'address', - name: 'actionTarget', - type: 'address' - }, - { - components: [ - { - internalType: 'address', - name: 'policy', - type: 'address' - }, - { - internalType: 'bytes', - name: 'initData', - type: 'bytes' - } - ], - internalType: 'struct PolicyData[]', - name: 'actionPolicies', - type: 'tuple[]' - } - ], - internalType: 'struct ActionData[]', - name: 'actions', - type: 'tuple[]' - } - ], - internalType: 'struct Session', - name: 'sessionToEnable', - type: 'tuple' - }, - { - type: 'bytes', - name: 'permissionEnableSig' - } - ], - internalType: 'struct EnableSession', - name: 'enableSession', - type: 'tuple' - }, - { type: 'bytes' } -] as const diff --git a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/EncodeLib.ts b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/EncodeLib.ts deleted file mode 100644 index 52aab3751..000000000 --- a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/EncodeLib.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { SmartSessionMode } from '@biconomy/permission-context-builder' -import { LibZip } from 'solady' -import { Address, encodeAbiParameters, encodePacked, Hex } from 'viem' -import { enableSessionsStructAbi } from './SmartSessionUserOpBuilder' - -type EnableSessions = { - isigner: Address - isignerInitData: Hex - userOpPolicies: readonly { policy: Address; initData: Hex }[] - erc1271Policies: readonly { policy: Address; initData: Hex }[] - actions: readonly { - actionId: Hex - actionPolicies: readonly { policy: Address; initData: Hex }[] - }[] - permissionEnableSig: Hex -} - -export function packMode(data: Hex, mode: SmartSessionMode, signerId: Hex): Hex { - return encodePacked(['uint8', 'bytes32', 'bytes'], [mode, signerId, data]) -} - -export function encodeUse(signerId: Hex, sig: Hex) { - const data = encodeAbiParameters([{ type: 'bytes' }], [sig]) - const compressedData = LibZip.flzCompress(data) as Hex - return packMode(compressedData, SmartSessionMode.USE, signerId) -} - -export function encodeEnable(signerId: Hex, sig: Hex, enableData: EnableSessions) { - const data = encodeAbiParameters( - [enableSessionsStructAbi[0], { type: 'bytes' }], - [ - { - ...enableData - }, - sig - ] - ) - const compressedData = LibZip.flzCompress(data) as Hex - return packMode(compressedData, SmartSessionMode.UNSAFE_ENABLE, signerId) -} diff --git a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SafeUserOpBuilder.ts b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SafeUserOpBuilder.ts index 01a29d82c..daf568705 100644 --- a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SafeUserOpBuilder.ts +++ b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SafeUserOpBuilder.ts @@ -1,28 +1,27 @@ -import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts' +import { generatePrivateKey, privateKeyToAccount, signMessage } from 'viem/accounts' import { - FillUserOpParams, - FillUserOpResponse, - SendUserOpWithSignatureParams, - SendUserOpWithSignatureResponse, + BuildUserOpRequestParams, + BuildUserOpResponseReturn, + SendUserOpRequestParams, + SendUserOpResponseReturn, UserOpBuilder } from './UserOpBuilder' import { Address, Chain, createPublicClient, + encodeAbiParameters, Hex, http, - pad, parseAbi, PublicClient, - trim, - zeroAddress + toHex, + trim } from 'viem' import { signerToSafeSmartAccount } from 'permissionless/accounts' import { createSmartAccountClient, ENTRYPOINT_ADDRESS_V07, - getAccountNonce, getUserOperationHash } from 'permissionless' import { @@ -30,13 +29,11 @@ import { createPimlicoPaymasterClient } from 'permissionless/clients/pimlico' import { bundlerUrl, paymasterUrl, publicClientUrl } from '@/utils/SmartAccountUtil' - import { getChainById } from '@/utils/ChainUtil' import { SAFE_FALLBACK_HANDLER_STORAGE_SLOT } from '@/consts/smartAccounts' -import { formatSignature, getDummySignature } from './ContextBuilderUtil' -const { - SMART_SESSIONS_ADDRESS, -} = require('@rhinestone/module-sdk') as typeof import('@rhinestone/module-sdk') +import { formatSignature, getDummySignature, getNonce } from './ContextBuilderUtil' +import { WalletConnectCosigner } from './WalletConnectCosignerUtils' +const { getAccount } = require('@rhinestone/module-sdk') as typeof import('@rhinestone/module-sdk') const ERC_7579_LAUNCHPAD_ADDRESS: Address = '0xEBe001b3D534B9B6E2500FB78E67a1A137f561CE' @@ -53,7 +50,7 @@ export class SafeUserOpBuilder implements UserOpBuilder { this.accountAddress = accountAddress } - async fillUserOp(params: FillUserOpParams): Promise { + async fillUserOp(params: BuildUserOpRequestParams): Promise { const privateKey = generatePrivateKey() const signer = privateKeyToAccount(privateKey) @@ -99,62 +96,96 @@ export class SafeUserOpBuilder implements UserOpBuilder { chain: this.chain, bundlerTransport, middleware: { - sponsorUserOperation:paymasterClient.sponsorUserOperation, - // params.capabilities.paymasterService && paymasterClient.sponsorUserOperation, // optional + sponsorUserOperation: paymasterClient.sponsorUserOperation, + // params.capabilities.paymasterService && paymasterClient.sponsorUserOperation, // optional gasPrice: async () => (await pimlicoBundlerClient.getUserOperationGasPrice()).fast // if using pimlico bundler } }) - const account = smartAccountClient.account - const nonceKey = SMART_SESSIONS_ADDRESS - // const validatorAddress = (params.capabilities.permissions?.context.slice(0, 42) || - // zeroAddress) as Address - const validatorAddress = SMART_SESSIONS_ADDRESS as Address - let nonce: bigint = await getAccountNonce(this.publicClient, { - sender: this.accountAddress, - entryPoint: ENTRYPOINT_ADDRESS_V07, - key: BigInt( - pad(validatorAddress, { - dir: 'right', - size: 24 - }) || 0 - ) + + const account = getAccount({ + address: smartAccountClient.account.address, + type: 'safe' + }) + + let nonce: bigint = await getNonce({ + publicClient: this.publicClient, + account, + permissionsContext: params.capabilities.permissions?.context! }) - const signature = await getDummySignature(this.publicClient, { - permissionsContext: params.capabilities.permissions?.context!, - accountAddress: this.accountAddress + const callData = await smartAccountClient.account.encodeCallData(params.calls) + + const dummySignature = await getDummySignature({ + publicClient: this.publicClient, + account, + permissionsContext: params.capabilities.permissions?.context! }) - console.log('dummySignature', signature) + const userOp = await smartAccountClient.prepareUserOperationRequest({ userOperation: { nonce: nonce, - callData: await account.encodeCallData(params.calls), - // callGasLimit: BigInt('0x1E8480'), - // verificationGasLimit: BigInt('0x1E8480'), - // preVerificationGas: BigInt('0x1E8480'), - signature: signature + callData: callData, + signature: dummySignature }, - account: account + account: smartAccountClient.account }) + const hash = getUserOperationHash({ userOperation: userOp, chainId: this.chain.id, entryPoint: ENTRYPOINT_ADDRESS_V07 }) + return { - userOp, + userOp: { + ...userOp, + nonce: toHex(userOp.nonce), + callGasLimit: toHex(userOp.callGasLimit), + verificationGasLimit: toHex(userOp.verificationGasLimit), + preVerificationGas: toHex(userOp.preVerificationGas), + maxFeePerGas: toHex(userOp.maxFeePerGas), + maxPriorityFeePerGas: toHex(userOp.maxPriorityFeePerGas), + paymasterPostOpGasLimit: userOp.paymasterPostOpGasLimit + ? toHex(userOp.paymasterPostOpGasLimit) + : undefined, + paymasterVerificationGasLimit: userOp.paymasterVerificationGasLimit + ? toHex(userOp.paymasterVerificationGasLimit) + : undefined, + factory: userOp.factory, + factoryData: userOp.factoryData, + paymaster: userOp.paymaster, + paymasterData: userOp.paymasterData + }, hash } } + async sendUserOpWithSignature( - params: SendUserOpWithSignatureParams - ): Promise { - const { userOp, permissionsContext } = params + projectId: string, + params: SendUserOpRequestParams + ): Promise { + const { chainId, userOp, permissionsContext, pci } = params + if (pci && projectId) { + const walletConnectCosigner = new WalletConnectCosigner(projectId) + const caip10AccountAddress = `eip155:${chainId}:${userOp.sender}` + const cosignResponse = await walletConnectCosigner.coSignUserOperation(caip10AccountAddress, { + pci, + userOp + }) + console.log('cosignResponse:', cosignResponse) + userOp.signature = cosignResponse.signature + } + const account = getAccount({ + address: userOp.sender, + type: 'safe' + }) + if (permissionsContext) { - const formattedSignature = await formatSignature(this.publicClient, { - signature: userOp.signature, - permissionsContext, - accountAddress: userOp.sender + const formattedSignature = await formatSignature({ + publicClient: this.publicClient, + account, + modifiedSignature: userOp.signature, + permissionsContext }) userOp.signature = formattedSignature } @@ -167,23 +198,25 @@ export class SafeUserOpBuilder implements UserOpBuilder { entryPoint: ENTRYPOINT_ADDRESS_V07 }) - const userOpHash = await pimlicoBundlerClient.sendUserOperation({ + const userOpId = await pimlicoBundlerClient.sendUserOperation({ userOperation: { ...userOp, - callData: userOp.callData, + signature: userOp.signature, callGasLimit: BigInt(userOp.callGasLimit), nonce: BigInt(userOp.nonce), preVerificationGas: BigInt(userOp.preVerificationGas), verificationGasLimit: BigInt(userOp.verificationGasLimit), - sender: userOp.sender, - signature: userOp.signature, maxFeePerGas: BigInt(userOp.maxFeePerGas), - maxPriorityFeePerGas: BigInt(userOp.maxPriorityFeePerGas) + maxPriorityFeePerGas: BigInt(userOp.maxPriorityFeePerGas), + paymasterVerificationGasLimit: + userOp.paymasterVerificationGasLimit && BigInt(userOp.paymasterVerificationGasLimit), + paymasterPostOpGasLimit: + userOp.paymasterPostOpGasLimit && BigInt(userOp.paymasterPostOpGasLimit) } }) return { - receipt: userOpHash + userOpId } } diff --git a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SmartSessionUserOpBuilder.ts b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SmartSessionUserOpBuilder.ts deleted file mode 100644 index 1eaeb0e22..000000000 --- a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SmartSessionUserOpBuilder.ts +++ /dev/null @@ -1,322 +0,0 @@ -import { - Address, - decodeAbiParameters, - encodeAbiParameters, - getAddress, - Hex, - PublicClient -} from 'viem' -import { encodeEnable, encodeUse } from './EncodeLib' -const { SMART_SESSIONS_ADDRESS } = - require('@rhinestone/module-sdk') as typeof import('@rhinestone/module-sdk') -import { readContract } from 'viem/actions' - -export const enableSessionsStructAbi = [ - { - components: [ - { - name: 'isigner', - type: 'address' - }, - { - name: 'isignerInitData', - type: 'bytes' - }, - { - name: 'userOpPolicies', - type: 'tuple[]', - components: [ - { - name: 'policy', - type: 'address' - }, - { - name: 'initData', - type: 'bytes' - } - ] - }, - { - name: 'erc1271Policies', - type: 'tuple[]', - components: [ - { - name: 'policy', - type: 'address' - }, - { - name: 'initData', - type: 'bytes' - } - ] - }, - { - name: 'actions', - type: 'tuple[]', - components: [ - { - name: 'actionId', - type: 'bytes32' - }, - { - name: 'actionPolicies', - type: 'tuple[]', - components: [ - { - name: 'policy', - type: 'address' - }, - { - name: 'initData', - type: 'bytes' - } - ] - } - ] - }, - { - name: 'permissionEnableSig', - type: 'bytes' - } - ], - name: 'EnableSessions', - type: 'tuple' - } -] as const - -export const isPermissionsEnabledAbi = [ - { - inputs: [ - { - internalType: 'SignerId', - name: 'signerId', - type: 'bytes32' - }, - { - internalType: 'address', - name: 'account', - type: 'address' - }, - { - components: [ - { - internalType: 'contract ISigner', - name: 'isigner', - type: 'address' - }, - { - internalType: 'bytes', - name: 'isignerInitData', - type: 'bytes' - }, - { - components: [ - { - internalType: 'address', - name: 'policy', - type: 'address' - }, - { - internalType: 'bytes', - name: 'initData', - type: 'bytes' - } - ], - internalType: 'struct PolicyData[]', - name: 'userOpPolicies', - type: 'tuple[]' - }, - { - components: [ - { - internalType: 'address', - name: 'policy', - type: 'address' - }, - { - internalType: 'bytes', - name: 'initData', - type: 'bytes' - } - ], - internalType: 'struct PolicyData[]', - name: 'erc1271Policies', - type: 'tuple[]' - }, - { - components: [ - { - internalType: 'ActionId', - name: 'actionId', - type: 'bytes32' - }, - { - components: [ - { - internalType: 'address', - name: 'policy', - type: 'address' - }, - { - internalType: 'bytes', - name: 'initData', - type: 'bytes' - } - ], - internalType: 'struct PolicyData[]', - name: 'actionPolicies', - type: 'tuple[]' - } - ], - internalType: 'struct ActionData[]', - name: 'actions', - type: 'tuple[]' - }, - { - internalType: 'bytes', - name: 'permissionEnableSig', - type: 'bytes' - } - ], - internalType: 'struct EnableSessions', - name: 'enableData', - type: 'tuple' - } - ], - name: 'isPermissionEnabled', - outputs: [ - { - internalType: 'bool', - name: 'isEnabled', - type: 'bool' - } - ], - stateMutability: 'view', - type: 'function' - } -] as const - -export async function formatSignature( - publicClient: PublicClient, - params: { - signature: Hex - permissionsContext: Hex - accountAddress: Address - } -) { - const { signature, permissionsContext, accountAddress } = params - if (!permissionsContext || permissionsContext.length < 178) { - throw new Error('Permissions context too short') - } - const signerId = `0x${permissionsContext.slice(114, 178)}` as Hex - - if (permissionsContext.length == 178) { - return encodeUse(signerId, signature) - } - - const validatorAddress = permissionsContext.slice(0, 42) as Address - - if (getAddress(validatorAddress) !== getAddress(SMART_SESSIONS_ADDRESS)) { - throw new Error( - `Validator ${validatorAddress} is not the smart session validator ${SMART_SESSIONS_ADDRESS}` - ) - } - - const enableData = `0x${permissionsContext.slice(178)}` as Hex - const enableSession = Array.from(decodeAbiParameters(enableSessionsStructAbi, enableData)) - - const isPermissionsEnabled = await readContract(publicClient, { - address: SMART_SESSIONS_ADDRESS, - abi: isPermissionsEnabledAbi, - functionName: 'isPermissionEnabled', - args: [signerId, accountAddress, enableSession[0]] - }) - - if (isPermissionsEnabled) { - return encodeUse(signerId, signature) - } else { - return encodeEnable(signerId, signature, enableSession[0]) - } -} - -export async function getDummySignature( - publicClient: PublicClient, - params: { - permissionsContext: Hex - accountAddress: Address - } -) { - const { permissionsContext, accountAddress } = params - if (!permissionsContext || permissionsContext.length < 178) { - throw new Error('Permissions context too short') - } - - const validatorAddress = permissionsContext.slice(0, 42) as Address - - if (getAddress(validatorAddress) !== getAddress(SMART_SESSIONS_ADDRESS)) { - throw new Error( - `Validator ${validatorAddress} is not the smart session validator ${SMART_SESSIONS_ADDRESS}` - ) - } - - const enableData = `0x${permissionsContext.slice(178)}` as Hex - const enableSession = decodeAbiParameters(enableSessionsStructAbi, enableData) - const signerInitData = enableSession[0].isignerInitData - console.log('signerInitData', signerInitData) - const signers = decodeSigners(signerInitData) - console.log('signers', signers) - const dummySignatures: `0x${string}`[] = [] - const dummyECDSASignature: `0x${string}` = - '0xe8b94748580ca0b4993c9a1b86b5be851bfc076ff5ce3a1ff65bf16392acfcb800f9b4f1aef1555c7fce5599fffb17e7c635502154a0333ba21f3ae491839af51c' - const dummyPasskeySignature: `0x${string}` = '0x' - for (let i = 0; i < signers.length; i++) { - const signer = signers[i] - if (signer.type === 0) { - dummySignatures.push(dummyECDSASignature) - } else if (signer.type === 1) { - dummySignatures.push(dummyPasskeySignature) - } - } - const concatenatedDummySignature = encodeAbiParameters([{ type: 'bytes[]' }], [dummySignatures]) - - return formatSignature(publicClient, { - signature: concatenatedDummySignature, - permissionsContext, - accountAddress - }) -} - -export function decodeSigners( - encodedData: `0x${string}` -): Array<{ type: number; data: `0x${string}` }> { - let offset = 2 // Start after '0x' - const signers: Array<{ type: number; data: `0x${string}` }> = [] - - // Decode the number of signers - const signersCount = parseInt(encodedData.slice(offset, offset + 2), 16) - offset += 2 - - for (let i = 0; i < signersCount; i++) { - // Decode signer type - const signerType = parseInt(encodedData.slice(offset, offset + 2), 16) - offset += 2 - - // Determine data length based on signer type - let dataLength: number - if (signerType === 0) { - dataLength = 40 // 20 bytes - } else if (signerType === 1) { - dataLength = 128 // 64 bytes - } else { - throw new Error(`Unknown signer type: ${signerType}`) - } - - // Decode signer data - const signerData = `0x${encodedData.slice(offset, offset + dataLength)}` as `0x${string}` - offset += dataLength - - signers.push({ type: signerType, data: signerData }) - } - - return signers -} diff --git a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SmartSessionUtil.ts b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SmartSessionUtil.ts new file mode 100644 index 000000000..b925850c8 --- /dev/null +++ b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SmartSessionUtil.ts @@ -0,0 +1,22 @@ +import { Address } from 'viem' + +export const TIME_FRAME_POLICY_ADDRESSES: Record = { + 84532: '0x9A6c4974dcE237E01Ff35c602CA9555a3c0Fa5EF', + 11155111: '0x6E1FCe0ec6feaD8dBD2D36a5b9eCf8e33A538479' +} + +export const MULTIKEY_SIGNER_ADDRESSES: Record = { + 84532: '0x207b90941d9cff79A750C1E5c05dDaA17eA01B9F', + 11155111: '0x3cA2D7D588FA66248a49c1C885997e5017aF9Dc7' +} + +export const MOCK_VALIDATOR_ADDRESSES: Record = { + 84532: '0x8F8842B9b7346529484F282902Af173217411076', + 11155111: '0xaE15a31afb2770cE4c5C6131925564B03b597Fe3' +} + +// All on sepolia +export const SIMPLE_SIGNER = '0x6ff7E9992160bB25f5c67b0Ce389c28d8faD3Bfb' as Address +export const UNI_ACTION_POLICY = '0x237C7567Ac09D4DB7Dd48852a438F77a6bd65fc4' as Address +export const USAGE_LIMIT_POLICY = '0x1f265E3beDc6ce93e1A36Dc80E1B1c65844F9861' as Address +export const VALUE_LIMIT_POLICY = '0x6F0eC0c77cCAF4c25ff8FF7113D329caAA769688' as Address diff --git a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/UserOpBuilder.ts b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/UserOpBuilder.ts index 8fccfae7d..b15f4b789 100644 --- a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/UserOpBuilder.ts +++ b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/UserOpBuilder.ts @@ -1,11 +1,27 @@ -import { UserOperation } from 'permissionless' import { Address, Hex } from 'viem' type Call = { to: Address; value: bigint; data: Hex } -type UserOp = UserOperation<'v0.7'> - -export type FillUserOpParams = { +export type UserOperationWithBigIntAsHex = { + sender: Address + nonce: Hex + factory: Address | undefined + factoryData: Hex | undefined + callData: Hex + callGasLimit: Hex + verificationGasLimit: Hex + preVerificationGas: Hex + maxFeePerGas: Hex + maxPriorityFeePerGas: Hex + paymaster: Address | undefined + paymasterVerificationGasLimit: Hex | undefined + paymasterPostOpGasLimit: Hex | undefined + paymasterData: Hex | undefined + signature: Hex + initCode?: never + paymasterAndData?: never +} +export type BuildUserOpRequestParams = { chainId: number account: Address calls: Call[] @@ -14,28 +30,28 @@ export type FillUserOpParams = { permissions?: { context: Hex } } } -export type FillUserOpResponse = { - userOp: UserOp +export type BuildUserOpResponseReturn = { + userOp: UserOperationWithBigIntAsHex hash: Hex } - export type ErrorResponse = { message: string error: string } - -export type SendUserOpWithSignatureParams = { - chainId: Hex - userOp: UserOp +export type SendUserOpRequestParams = { + chainId: number + userOp: UserOperationWithBigIntAsHex + pci?: string permissionsContext?: Hex } -export type SendUserOpWithSignatureResponse = { - receipt: Hex +export type SendUserOpResponseReturn = { + userOpId: Hex } export interface UserOpBuilder { - fillUserOp(params: FillUserOpParams): Promise + fillUserOp(params: BuildUserOpRequestParams): Promise sendUserOpWithSignature( - params: SendUserOpWithSignatureParams - ): Promise + projectId: string, + params: SendUserOpRequestParams + ): Promise } diff --git a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/WalletConnectCosignerUtils.ts b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/WalletConnectCosignerUtils.ts new file mode 100644 index 000000000..6b658973c --- /dev/null +++ b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/WalletConnectCosignerUtils.ts @@ -0,0 +1,169 @@ +import axios, { AxiosError } from 'axios' +import { UserOperationWithBigIntAsHex } from './UserOpBuilder' +import { bigIntReplacer } from '@/utils/HelperUtil' +import { WC_COSIGNER_BASE_URL } from '@/utils/ConstantsUtil' + +// Define types for the request and response +type AddPermission = { + permissionType: string + data: string + required: boolean + onChainValidated: boolean +} + +type AddPermissionRequest = { + permission: AddPermission +} + +export type AddPermissionResponse = { + pci: string + key: string +} + +type Signer = { + type: string + data: { + ids: string[] + } +} + +type SignerData = { + userOpBuilder: string +} + +type PermissionsContext = { + signer: Signer + expiry: number + signerData: SignerData + factory?: string + factoryData?: string + permissionsContext: string +} + +type UpdatePermissionsContextRequest = { + pci: string + signature?: string + context: PermissionsContext +} + +type RevokePermissionRequest = { + pci: string + signature: string +} + +type CoSignRequest = { + pci: string + userOp: UserOperationWithBigIntAsHex +} + +type CoSignResponse = { + signature: `0x${string}` +} + +// Define a custom error type +export class CoSignerApiError extends Error { + constructor(public status: number, message: string) { + super(message) + this.name = 'CoSignerApiError' + } +} + +// Function to send requests to the CoSigner API +async function sendCoSignerRequest< + TRequest, + TResponse, + TQueryParams extends Record = Record +>(args: { + url: string + data: TRequest + queryParams?: TQueryParams + headers: Record + transformRequest?: (data: TRequest) => unknown +}): Promise { + const { url, data, queryParams = {}, headers, transformRequest } = args + const transformedData = transformRequest ? transformRequest(data) : data + + try { + const response = await axios.post(url, transformedData, { + params: queryParams, + headers + }) + + return response.data + } catch (error) { + if (axios.isAxiosError(error)) { + const axiosError = error as AxiosError + if (axiosError.response) { + throw new CoSignerApiError( + axiosError.response.status, + JSON.stringify(axiosError.response.data) + ) + } else { + throw new CoSignerApiError(500, 'Network error') + } + } + // Re-throw if it's not an Axios error + throw error + } +} + +// Class to interact with the WalletConnect CoSigner API +export class WalletConnectCosigner { + private baseUrl: string + private projectId: string + + constructor(projectId: string) { + this.baseUrl = WC_COSIGNER_BASE_URL + this.projectId = projectId + } + + async addPermission(address: string, permission: AddPermission): Promise { + const url = `${this.baseUrl}/${encodeURIComponent(address)}` + + return await sendCoSignerRequest< + AddPermissionRequest, + AddPermissionResponse, + { projectId: string } + >({ + url, + data: { permission }, + queryParams: { projectId: this.projectId }, + headers: { 'Content-Type': 'application/json' } + }) + } + + async updatePermissionsContext( + address: string, + updateData: UpdatePermissionsContextRequest + ): Promise { + const url = `${this.baseUrl}/${encodeURIComponent(address)}/context` + await sendCoSignerRequest({ + url, + data: updateData, + queryParams: { projectId: this.projectId }, + headers: { 'Content-Type': 'application/json' } + }) + } + + async revokePermission(address: string, revokeData: RevokePermissionRequest): Promise { + const url = `${this.baseUrl}/${encodeURIComponent(address)}/revoke` + await sendCoSignerRequest({ + url, + data: revokeData, + queryParams: { projectId: this.projectId }, + headers: { 'Content-Type': 'application/json' } + }) + } + + async coSignUserOperation(address: string, coSignData: CoSignRequest): Promise { + const url = `${this.baseUrl}/${encodeURIComponent(address)}/sign` + + return await sendCoSignerRequest({ + url, + data: coSignData, + queryParams: { projectId: this.projectId }, + headers: { 'Content-Type': 'application/json' }, + transformRequest: (value: CoSignRequest) => JSON.stringify(value, bigIntReplacer) + }) + } +} diff --git a/advanced/wallets/react-wallet-v2/src/pages/api/build.ts b/advanced/wallets/react-wallet-v2/src/pages/api/build.ts index 9456b5fc3..878e910fe 100644 --- a/advanced/wallets/react-wallet-v2/src/pages/api/build.ts +++ b/advanced/wallets/react-wallet-v2/src/pages/api/build.ts @@ -1,11 +1,14 @@ -import { ErrorResponse, FillUserOpResponse } from '@/lib/smart-accounts/builders/UserOpBuilder' +import { + ErrorResponse, + BuildUserOpResponseReturn +} from '@/lib/smart-accounts/builders/UserOpBuilder' import { getChainById } from '@/utils/ChainUtil' import { getUserOpBuilder } from '@/utils/UserOpBuilderUtil' import { NextApiRequest, NextApiResponse } from 'next' export default async function handler( req: NextApiRequest, - res: NextApiResponse + res: NextApiResponse ) { const chainId = req.body.chainId const account = req.body.account diff --git a/advanced/wallets/react-wallet-v2/src/pages/api/sendUserOp.ts b/advanced/wallets/react-wallet-v2/src/pages/api/sendUserOp.ts index 3a61cee0f..8aaf90c1c 100644 --- a/advanced/wallets/react-wallet-v2/src/pages/api/sendUserOp.ts +++ b/advanced/wallets/react-wallet-v2/src/pages/api/sendUserOp.ts @@ -1,6 +1,6 @@ import { ErrorResponse, - SendUserOpWithSignatureResponse + SendUserOpResponseReturn } from '@/lib/smart-accounts/builders/UserOpBuilder' import { getChainById } from '@/utils/ChainUtil' import { getUserOpBuilder } from '@/utils/UserOpBuilderUtil' @@ -8,8 +8,37 @@ import { NextApiRequest, NextApiResponse } from 'next' export default async function handler( req: NextApiRequest, - res: NextApiResponse + res: NextApiResponse ) { + if (req.method !== 'POST') { + return res.status(405).json({ + message: 'Method not allowed', + error: 'Method not allowed' + }) + } + if (!req.body.chainId) { + return res.status(400).json({ + message: 'chainId is required', + error: 'chainId is required' + }) + } + if (!req.body.userOp) { + return res.status(400).json({ + message: 'userOp is required', + error: 'userOp is required' + }) + } + if ( + req.query.projectId === undefined || + req.query.projectId === '' || + typeof req.query.projectId !== 'string' + ) { + return res.status(400).json({ + message: 'invalid projectId', + error: 'invalid projectId' + }) + } + const projectId = req.query.projectId const chainId = req.body.chainId const account = req.body.userOp.sender const chain = getChainById(chainId) @@ -18,7 +47,7 @@ export default async function handler( account, chain }) - const response = await builder.sendUserOpWithSignature(req.body) + const response = await builder.sendUserOpWithSignature(projectId, req.body) res.status(200).json(response) } catch (error: any) { diff --git a/advanced/wallets/react-wallet-v2/src/utils/ConstantsUtil.ts b/advanced/wallets/react-wallet-v2/src/utils/ConstantsUtil.ts new file mode 100644 index 000000000..e24109177 --- /dev/null +++ b/advanced/wallets/react-wallet-v2/src/utils/ConstantsUtil.ts @@ -0,0 +1,2 @@ +export const WC_COSIGNER_BASE_URL = 'https://maksy.ngrok.dev/v1/sessions' +// export const WC_COSIGNER_BASE_URL = 'https://rpc.walletconnect.org/v1/sessions' diff --git a/advanced/wallets/react-wallet-v2/yarn.lock b/advanced/wallets/react-wallet-v2/yarn.lock index ad1f38d51..7e8c1a43a 100644 --- a/advanced/wallets/react-wallet-v2/yarn.lock +++ b/advanced/wallets/react-wallet-v2/yarn.lock @@ -98,15 +98,6 @@ "@babel/helper-validator-identifier" "^7.22.20" to-fast-properties "^2.0.0" -"@biconomy/permission-context-builder@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@biconomy/permission-context-builder/-/permission-context-builder-1.1.2.tgz#caa7a96583784cf632e5450a3d9cfd1b2bfb4a90" - integrity sha512-VmKsX4NmLS+Y9vViQFJoSlpl9XXJ7MpTIOzLNfRAeBNqGnR0UcU+K/wzer9jRmp4MC4XoK0qfkHUjj+D8NVquw== - dependencies: - tslib "^2.7.0" - typescript "^5.5.4" - webauthn-p256 "^0.0.8" - "@cosmjs/amino@0.32.3", "@cosmjs/amino@^0.32.3": version "0.32.3" resolved "https://registry.yarnpkg.com/@cosmjs/amino/-/amino-0.32.3.tgz#b81d4a2b8d61568431a1afcd871e1344a19d97ff" @@ -2086,10 +2077,10 @@ "@react-types/grid" "^3.2.6" "@react-types/shared" "^3.23.1" -"@rhinestone/module-sdk@0.1.13": - version "0.1.13" - resolved "https://registry.yarnpkg.com/@rhinestone/module-sdk/-/module-sdk-0.1.13.tgz#5df0a07d65a4fd3c0d5838714fc4468feaad0a55" - integrity sha512-Tb78W6BYOmBnjtqIHFmq43SBfd9vucZ4pxgN8h43zto1fKSwrBYLf4Pzrm/NwaAfFCw0BHuYo5YNQtPJj3oqUA== +"@rhinestone/module-sdk@0.1.15": + version "0.1.15" + resolved "https://registry.yarnpkg.com/@rhinestone/module-sdk/-/module-sdk-0.1.15.tgz#8d49af671ed39881caa28e0a97351a8bc9261580" + integrity sha512-9EHglokR1+NCj7CE37lFbzrsCgI7Anr6KLIwNdJ9Rs7cNAsyDNvbzDaY6FBlPbK9dk3bEZHfyphSVMJVvH+Siw== dependencies: solady "^0.0.235" @@ -6848,11 +6839,6 @@ tslib@^2.1.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.6.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== -tslib@^2.7.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01" - integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA== - tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" @@ -6926,11 +6912,6 @@ typescript@5.2.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78" integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w== -typescript@^5.5.4: - version "5.5.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.4.tgz#d9852d6c82bad2d2eda4fd74a5762a8f5909e9ba" - integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q== - u3@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/u3/-/u3-0.1.1.tgz#5f52044f42ee76cd8de33148829e14528494b73b" @@ -7116,14 +7097,6 @@ webauthn-p256@0.0.2: "@noble/curves" "^1.4.0" "@noble/hashes" "^1.4.0" -webauthn-p256@^0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/webauthn-p256/-/webauthn-p256-0.0.8.tgz#eefcaa1eab9929c69f7b2d03572c03472efc9805" - integrity sha512-nswXgJdl1N2CDSRA4Py6iIhPdY0mPIhy0ityyxzcZ4MRFFnyW05/79dyvWlDn03XGh+eCinOrqjTYzmu9rRD3w== - dependencies: - "@noble/curves" "^1.4.0" - "@noble/hashes" "^1.4.0" - webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" From e2b215c42c9f1fa3fbe4a617d3e9f08adf96cac8 Mon Sep 17 00:00:00 2001 From: Karandeep Singh Date: Mon, 16 Sep 2024 17:12:54 -0400 Subject: [PATCH 12/18] attest and install modules in single userOp --- advanced/wallets/react-wallet-v2/package.json | 2 +- .../lib/smart-accounts/SafeSmartAccountLib.ts | 193 ++++++++++++------ .../builders/ContextBuilderUtil.ts | 23 ++- .../src/utils/EIP7715RequestHandlerUtils.ts | 13 +- advanced/wallets/react-wallet-v2/yarn.lock | 14 +- 5 files changed, 166 insertions(+), 79 deletions(-) diff --git a/advanced/wallets/react-wallet-v2/package.json b/advanced/wallets/react-wallet-v2/package.json index 4193c608d..dc7c4c814 100644 --- a/advanced/wallets/react-wallet-v2/package.json +++ b/advanced/wallets/react-wallet-v2/package.json @@ -28,7 +28,7 @@ "@nextui-org/react": "1.0.8-beta.5", "@polkadot/keyring": "^10.1.2", "@polkadot/types": "^9.3.3", - "@rhinestone/module-sdk": "0.1.15", + "@rhinestone/module-sdk": "0.1.16", "@solana/web3.js": "1.89.2", "@taquito/signer": "^15.1.0", "@taquito/taquito": "^15.1.0", diff --git a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SafeSmartAccountLib.ts b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SafeSmartAccountLib.ts index 48b0d3bd9..efb6a6c12 100644 --- a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SafeSmartAccountLib.ts +++ b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SafeSmartAccountLib.ts @@ -12,7 +12,9 @@ import { WalletGrantPermissionsParameters, createWalletClient, encodeFunctionData, + getAddress, http, + parseAbi, type WalletGrantPermissionsReturnType } from 'viem' import { MultiKeySigner } from 'viem/_types/experimental/erc7715/types/signer' @@ -20,8 +22,16 @@ import { ModuleType } from 'permissionless/actions/erc7579' import { MOCK_VALIDATOR_ADDRESSES } from './builders/SmartSessionUtil' import { Permission } from '@/data/EIP7715Data' import { getSmartSessionContext } from './builders/ContextBuilderUtil' -const { SMART_SESSIONS_ADDRESS, getAccount } = - require('@rhinestone/module-sdk') as typeof import('@rhinestone/module-sdk') +import { readContract } from 'viem/actions' +import { Execution, Module } from '@rhinestone/module-sdk' + +const { + SMART_SESSIONS_ADDRESS, + REGISTRY_ADDRESS, + getTrustAttestersAction, + getAccount, + getSmartSessionsValidator +} = require('@rhinestone/module-sdk') as typeof import('@rhinestone/module-sdk') export class SafeSmartAccountLib extends SmartAccountLib { protected ERC_7579_LAUNCHPAD_ADDRESS: Address = '0xEBe001b3D534B9B6E2500FB78E67a1A137f561CE' protected SAFE_4337_MODULE_ADDRESS: Address = '0x3Fdb5BC686e861480ef99A6E3FaAe03c0b9F32e2' @@ -102,8 +112,9 @@ export class SafeSmartAccountLib extends SmartAccountLib { /** * Check Safe7579 Account is ready for processing this RPC request * - Check Account is deployed + * - Check SmartSession Attesters are trusted * - Check Permission Validator & Mock Validator modules are installed - * If not, Deploy and installed all necessary module for processing this RPC request + * If not, Deploy and installed all necessary module and enable trusted attester if not trusted for processing this RPC request * @returns */ private async ensureAccountReadyForGrantPermissions(): Promise { @@ -111,12 +122,12 @@ export class SafeSmartAccountLib extends SmartAccountLib { throw new Error('Client not initialized') } try { - const isAccountDeployed = await isSmartAccountDeployed( - this.publicClient, - this.client.account.address - ) - // TODO: check if account trust the attesters of the module - await this.trustAttesters() + const setUpSmartAccountForSmartSession: Execution[] = [] + + const [isAccountDeployed, doesSmartAccountTrustSmartSessionAttesters] = await Promise.all([ + isSmartAccountDeployed(this.publicClient, this.client.account.address), + this.isSmartAccountTrustSmartSessionAttesters() + ]) let smartSessionValidatorInstalled = false let mockValidatorInstalled = false @@ -130,36 +141,64 @@ export class SafeSmartAccountLib extends SmartAccountLib { } console.log({ smartSessionValidatorInstalled, mockValidatorInstalled }) - if (isAccountDeployed && smartSessionValidatorInstalled && mockValidatorInstalled) { + if ( + isAccountDeployed && + smartSessionValidatorInstalled && + mockValidatorInstalled && + doesSmartAccountTrustSmartSessionAttesters + ) { console.log('Account is already set up with required modules') return } console.log('Setting up the Account with required modules') - const installModules: { - address: Address - type: ModuleType - context: Hex - }[] = [] - if (!isAccountDeployed || !smartSessionValidatorInstalled) { - installModules.push({ - address: SMART_SESSIONS_ADDRESS, - type: 'validator', - context: '0x' - }) + const smartSessionValidator: Module = { + module: SMART_SESSIONS_ADDRESS, + type: 'validator' + } + const installSmartSessionValidatorAction = this.getInstallModuleAction( + this.client.account.address, + smartSessionValidator + ) + setUpSmartAccountForSmartSession.push(installSmartSessionValidatorAction) } if (!isAccountDeployed || !mockValidatorInstalled) { - installModules.push({ - address: MOCK_VALIDATOR_ADDRESSES[this.chain.id], - type: 'validator', - context: '0x' + const mockSignatureValidator: Module = { + module: MOCK_VALIDATOR_ADDRESSES[this.chain.id], + type: 'validator' + } + const installMockSignatureValidatorAction = this.getInstallModuleAction( + this.client.account.address, + mockSignatureValidator + ) + setUpSmartAccountForSmartSession.push(installMockSignatureValidatorAction) + } + + if (!doesSmartAccountTrustSmartSessionAttesters) { + console.log('Smart Account do not trusted the attesters of the smartsessions module') + console.log('Enable trusting the attesters of the smartsessions module') + const trustAttestersAction = getTrustAttestersAction({ + attesters: ['0xA4C777199658a41688E9488c4EcbD7a2925Cc23A'], + threshold: 1 }) + setUpSmartAccountForSmartSession.push(trustAttestersAction) } - await this.installModules(installModules) + console.log('Setting up the Account with Executions', { setUpSmartAccountForSmartSession }) + const userOpHash = await this.sendBatchTransaction( + setUpSmartAccountForSmartSession.map(action => { + return { + to: action.target, + value: action.value.valueOf(), + data: action.callData + } + }) + ) + const receipt = await this.bundlerClient.waitForUserOperationReceipt({ hash: userOpHash }) + console.log(`Account setup receipt:`, receipt) console.log('Account setup completed') } catch (error) { console.error(`Error ensuring account is ready for grant permissions: ${error}`) @@ -179,52 +218,82 @@ export class SafeSmartAccountLib extends SmartAccountLib { }) } - private async installModules( - modules: { - address: Address - type: ModuleType - context: Hex - }[] - ): Promise { - if (!this.client?.account) { - throw new Error('Client not initialized') - } - const userOpHash = await this.client.installModules({ - account: this.client.account, - modules: modules - }) - const receipt = await this.bundlerClient.waitForUserOperationReceipt({ hash: userOpHash }) - console.log(`Module installation receipt:`, receipt) - } - - private async trustAttesters(): Promise { - if (!this.client?.account) { - throw new Error('Client not initialized') - } - const trustAttestersAction = { - to: '0x000000000069E2a187AEFFb852bF3cCdC95151B2' as Address, // mock-registry + private getInstallModuleAction(accountAddress: Address, module: Module): Execution { + return { + target: accountAddress, value: BigInt(0), - data: encodeFunctionData({ + callData: encodeFunctionData({ abi: [ { - inputs: [ - { type: 'uint8', name: 'threshold' }, - { type: 'address[]', name: 'attesters' } - ], - name: 'trustAttesters', + name: 'installModule', type: 'function', stateMutability: 'nonpayable', + inputs: [ + { + type: 'uint256', + name: 'moduleTypeId' + }, + { + type: 'address', + name: 'module' + }, + { + type: 'bytes', + name: 'initData' + } + ], outputs: [] } ], - functionName: 'trustAttesters', - args: [1, ['0xA4C777199658a41688E9488c4EcbD7a2925Cc23A']] + functionName: 'installModule', + args: [ + this.parseModuleTypeId(module.type), + getAddress(module.module), + module.initData || '0x' + ] }) } + } - const userOpHash = await this.sendTransaction(trustAttestersAction) - console.log(`Trust Attesters userOpHash:`, userOpHash) - // const receipt = await this.bundlerClient.waitForUserOperationReceipt({ hash: userOpHash }) - // console.log(`Trust Attesters receipt:`, receipt) + private async isSmartAccountTrustSmartSessionAttesters(): Promise { + if (!this.client?.account) { + throw new Error('Client not initialized') + } + + const TRUSTED_SMART_SESSIONS_ATTERSTER_ADDRESS = '0xA4C777199658a41688E9488c4EcbD7a2925Cc23A' + const attesters = await readContract(this.publicClient, { + address: REGISTRY_ADDRESS, + abi: parseAbi([ + 'function findTrustedAttesters(address smartAccount) view returns (address[])' + ]), + functionName: 'findTrustedAttesters', + args: [this.client.account.address] + }) + + if (attesters.length > 0) { + return Boolean( + attesters.find( + (attester: Address) => + attester.toLowerCase() === TRUSTED_SMART_SESSIONS_ATTERSTER_ADDRESS.toLowerCase() + ) + ) + } + + return false + } + + private parseModuleTypeId(type: ModuleType): bigint { + switch (type) { + case 'validator': + return BigInt(1) + case 'executor': + return BigInt(2) + case 'fallback': + return BigInt(3) + case 'hook': + return BigInt(4) + default: + throw new Error('Invalid module type') + } } } diff --git a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/ContextBuilderUtil.ts b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/ContextBuilderUtil.ts index 09c6ae6f2..a18663ba8 100644 --- a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/ContextBuilderUtil.ts +++ b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/ContextBuilderUtil.ts @@ -6,12 +6,13 @@ import { } from './SmartSessionUtil' import type { Session, ChainSession, Account } from '@rhinestone/module-sdk' const { + SMART_SESSIONS_ADDRESS, + SmartSessionMode, getPermissionId, getSessionDigest, getSessionNonce, - SMART_SESSIONS_ADDRESS, + encode1271Hash, encodeSmartSessionSignature, - SmartSessionMode, hashChainSessions, encodeUseOrEnableSmartSessionSignature, decodeSmartSessionSignature @@ -115,7 +116,7 @@ export async function getSmartSessionContext({ client: publicClient, session })) as Hex - + console.log('permissionId', permissionId) const sessionNonce = await getSessionNonce({ client: publicClient, account, @@ -150,15 +151,24 @@ export async function getSmartSessionContext({ } ] const permissionEnableHash = hashChainSessions(chainSessions) + + // const formattedHash = encode1271Hash({ + // account, + // chainId: chainId, + // validator: account.address, + // hash: permissionEnableHash + // }) + const permissionEnableSig = await walletClient.signMessage({ account: walletClient.account, + // message: { raw: formattedHash } message: { raw: permissionEnableHash } }) - + const encodedSmartSessionSignature = encodeSmartSessionSignature({ mode: SmartSessionMode.ENABLE, permissionId, - signature: '0xe8b94748580ca0b4993c9a1b86b5be851bfc076ff5ce3a1ff65bf16392acfcb800f9b4f1aef1555c7fce5599fffb17e7c635502154a0333ba21f3ae491839af51c', + signature: '0x', enableSessionData: { enableSession: { chainDigestIndex: 0, @@ -166,7 +176,7 @@ export async function getSmartSessionContext({ sessionToEnable: session, permissionEnableSig }, - validator: MOCK_VALIDATOR_ADDRESSES[chainId], //smartAccountAddress, + validator: MOCK_VALIDATOR_ADDRESSES[chainId], //account.address, accountType: 'safe' } }) @@ -279,6 +289,7 @@ function getSamplePermissions( chainId: number, { permissions, expiry }: { permissions: Permission[]; expiry: number } ): Session { + console.log({expiry}) return { sessionValidator: MULTIKEY_SIGNER_ADDRESSES[chainId], sessionValidatorInitData: encodeMultiKeySignerInitData(signers), diff --git a/advanced/wallets/react-wallet-v2/src/utils/EIP7715RequestHandlerUtils.ts b/advanced/wallets/react-wallet-v2/src/utils/EIP7715RequestHandlerUtils.ts index 8b1c71f8f..aa290dd47 100644 --- a/advanced/wallets/react-wallet-v2/src/utils/EIP7715RequestHandlerUtils.ts +++ b/advanced/wallets/react-wallet-v2/src/utils/EIP7715RequestHandlerUtils.ts @@ -6,17 +6,18 @@ import { EIP7715_METHOD } from '@/data/EIP7715Data' import { SafeSmartAccountLib } from '@/lib/smart-accounts/SafeSmartAccountLib' import { web3wallet } from './WalletConnectUtil' import { smartAccountWallets } from './SmartAccountUtil' -import { GrantPermissionsParameters, GrantPermissionsReturnType } from 'viem/experimental' import { KernelSmartAccountLib } from '@/lib/smart-accounts/KernelSmartAccountLib' import { WalletGrantPermissionsParameters, WalletGrantPermissionsReturnType } from 'viem' type RequestEventArgs = Omit -function getSmartWalletAddressFromSession(requestSession: SessionTypes.Struct) { - const sessionAccounts = requestSession.namespaces['eip155'].accounts - const sessionAccountsAddress = sessionAccounts.map(value => value.split(':').slice(1).join(':')) +function getSmartWalletAddressFromSession(requestSession: SessionTypes.Struct, chainId: string) { + const sessionAccounts = requestSession.namespaces['eip155'].accounts.filter(value => + value.startsWith(chainId) + ) + const sessionAccountsAddresses = sessionAccounts.map(value => value.split(':').slice(1).join(':')) const smartAccounts = Object.keys(smartAccountWallets) const smartWalletAddress = smartAccounts.find(smartAccount => - sessionAccountsAddress.some(address => address.toLowerCase() === smartAccount.toLowerCase()) + sessionAccountsAddresses.some(address => address.toLowerCase() === smartAccount.toLowerCase()) ) if (!smartWalletAddress) { @@ -44,7 +45,7 @@ export async function approveEIP7715Request(requestEvent: RequestEventArgs) { SettingsStore.setActiveChainId(chainId) switch (request.method) { case EIP7715_METHOD.WALLET_GRANT_PERMISSIONS: { - const wallet = getSmartWalletAddressFromSession(requestSession) + const wallet = getSmartWalletAddressFromSession(requestSession, chainId) let grantPermissionsRequestParams: WalletGrantPermissionsParameters = request.params[0] if (wallet instanceof SafeSmartAccountLib || wallet instanceof KernelSmartAccountLib) { const grantPermissionsResponse: WalletGrantPermissionsReturnType = diff --git a/advanced/wallets/react-wallet-v2/yarn.lock b/advanced/wallets/react-wallet-v2/yarn.lock index 7e8c1a43a..99d18514a 100644 --- a/advanced/wallets/react-wallet-v2/yarn.lock +++ b/advanced/wallets/react-wallet-v2/yarn.lock @@ -2077,12 +2077,13 @@ "@react-types/grid" "^3.2.6" "@react-types/shared" "^3.23.1" -"@rhinestone/module-sdk@0.1.15": - version "0.1.15" - resolved "https://registry.yarnpkg.com/@rhinestone/module-sdk/-/module-sdk-0.1.15.tgz#8d49af671ed39881caa28e0a97351a8bc9261580" - integrity sha512-9EHglokR1+NCj7CE37lFbzrsCgI7Anr6KLIwNdJ9Rs7cNAsyDNvbzDaY6FBlPbK9dk3bEZHfyphSVMJVvH+Siw== +"@rhinestone/module-sdk@0.1.16": + version "0.1.16" + resolved "https://registry.yarnpkg.com/@rhinestone/module-sdk/-/module-sdk-0.1.16.tgz#8bbc5ed193ad4f3656a0ac1aa1f438771734d182" + integrity sha512-Cn+JJ/8J6f5rk9ihagCoQ815bqve0YOpPCzkxEIqFzsyVYOuEUrEbX5ED9KJhoyyZSuAk2Ar8yxNGJBlS56RvA== dependencies: solady "^0.0.235" + tslib "^2.7.0" "@rushstack/eslint-patch@^1.1.3": version "1.6.0" @@ -6839,6 +6840,11 @@ tslib@^2.1.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.6.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== +tslib@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01" + integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA== + tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" From 66d977b339d41f5f49406d94d62f17c5440cd420 Mon Sep 17 00:00:00 2001 From: Karandeep Singh Date: Mon, 16 Sep 2024 17:13:37 -0400 Subject: [PATCH 13/18] chores: run prettier --- .../src/lib/smart-accounts/builders/ContextBuilderUtil.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/ContextBuilderUtil.ts b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/ContextBuilderUtil.ts index a18663ba8..c53034f03 100644 --- a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/ContextBuilderUtil.ts +++ b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/ContextBuilderUtil.ts @@ -164,7 +164,7 @@ export async function getSmartSessionContext({ // message: { raw: formattedHash } message: { raw: permissionEnableHash } }) - + const encodedSmartSessionSignature = encodeSmartSessionSignature({ mode: SmartSessionMode.ENABLE, permissionId, @@ -289,7 +289,7 @@ function getSamplePermissions( chainId: number, { permissions, expiry }: { permissions: Permission[]; expiry: number } ): Session { - console.log({expiry}) + console.log({ expiry }) return { sessionValidator: MULTIKEY_SIGNER_ADDRESSES[chainId], sessionValidatorInitData: encodeMultiKeySignerInitData(signers), From 759c8eb9c79f8da55cba3a539f18b8b797cac6bd Mon Sep 17 00:00:00 2001 From: Karandeep Singh Date: Tue, 17 Sep 2024 11:16:20 -0400 Subject: [PATCH 14/18] update co-signer handling and url --- .../builders/SafeUserOpBuilder.ts | 104 ++++++++++-------- .../builders/WalletConnectCosignerUtils.ts | 2 +- .../src/utils/ConstantsUtil.ts | 3 +- 3 files changed, 58 insertions(+), 51 deletions(-) diff --git a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SafeUserOpBuilder.ts b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SafeUserOpBuilder.ts index daf568705..307450c85 100644 --- a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SafeUserOpBuilder.ts +++ b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SafeUserOpBuilder.ts @@ -164,59 +164,67 @@ export class SafeUserOpBuilder implements UserOpBuilder { projectId: string, params: SendUserOpRequestParams ): Promise { - const { chainId, userOp, permissionsContext, pci } = params - if (pci && projectId) { - const walletConnectCosigner = new WalletConnectCosigner(projectId) - const caip10AccountAddress = `eip155:${chainId}:${userOp.sender}` - const cosignResponse = await walletConnectCosigner.coSignUserOperation(caip10AccountAddress, { - pci, - userOp + try { + const { chainId, userOp, permissionsContext, pci } = params + if (pci && projectId) { + const walletConnectCosigner = new WalletConnectCosigner(projectId) + const caip10AccountAddress = `eip155:${chainId}:${userOp.sender}` + const cosignResponse = await walletConnectCosigner.coSignUserOperation( + caip10AccountAddress, + { + pci, + userOp + } + ) + console.log('cosignResponse:', cosignResponse) + userOp.signature = cosignResponse.signature + } + const account = getAccount({ + address: userOp.sender, + type: 'safe' }) - console.log('cosignResponse:', cosignResponse) - userOp.signature = cosignResponse.signature - } - const account = getAccount({ - address: userOp.sender, - type: 'safe' - }) - if (permissionsContext) { - const formattedSignature = await formatSignature({ - publicClient: this.publicClient, - account, - modifiedSignature: userOp.signature, - permissionsContext + if (permissionsContext) { + const formattedSignature = await formatSignature({ + publicClient: this.publicClient, + account, + modifiedSignature: userOp.signature, + permissionsContext + }) + userOp.signature = formattedSignature + } + const bundlerTransport = http(bundlerUrl({ chain: this.chain }), { + timeout: 30000 + }) + const pimlicoBundlerClient = createPimlicoBundlerClient({ + chain: this.chain, + transport: bundlerTransport, + entryPoint: ENTRYPOINT_ADDRESS_V07 }) - userOp.signature = formattedSignature - } - const bundlerTransport = http(bundlerUrl({ chain: this.chain }), { - timeout: 30000 - }) - const pimlicoBundlerClient = createPimlicoBundlerClient({ - chain: this.chain, - transport: bundlerTransport, - entryPoint: ENTRYPOINT_ADDRESS_V07 - }) - const userOpId = await pimlicoBundlerClient.sendUserOperation({ - userOperation: { - ...userOp, - signature: userOp.signature, - callGasLimit: BigInt(userOp.callGasLimit), - nonce: BigInt(userOp.nonce), - preVerificationGas: BigInt(userOp.preVerificationGas), - verificationGasLimit: BigInt(userOp.verificationGasLimit), - maxFeePerGas: BigInt(userOp.maxFeePerGas), - maxPriorityFeePerGas: BigInt(userOp.maxPriorityFeePerGas), - paymasterVerificationGasLimit: - userOp.paymasterVerificationGasLimit && BigInt(userOp.paymasterVerificationGasLimit), - paymasterPostOpGasLimit: - userOp.paymasterPostOpGasLimit && BigInt(userOp.paymasterPostOpGasLimit) - } - }) + const userOpId = await pimlicoBundlerClient.sendUserOperation({ + userOperation: { + ...userOp, + signature: userOp.signature, + callGasLimit: BigInt(userOp.callGasLimit), + nonce: BigInt(userOp.nonce), + preVerificationGas: BigInt(userOp.preVerificationGas), + verificationGasLimit: BigInt(userOp.verificationGasLimit), + maxFeePerGas: BigInt(userOp.maxFeePerGas), + maxPriorityFeePerGas: BigInt(userOp.maxPriorityFeePerGas), + paymasterVerificationGasLimit: + userOp.paymasterVerificationGasLimit && BigInt(userOp.paymasterVerificationGasLimit), + paymasterPostOpGasLimit: + userOp.paymasterPostOpGasLimit && BigInt(userOp.paymasterPostOpGasLimit) + } + }) - return { - userOpId + return { + userOpId + } + } catch (e) { + console.log(e) + throw new Error('Failed to sign user operation with cosigner') } } diff --git a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/WalletConnectCosignerUtils.ts b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/WalletConnectCosignerUtils.ts index 6b658973c..fd529bc8c 100644 --- a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/WalletConnectCosignerUtils.ts +++ b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/WalletConnectCosignerUtils.ts @@ -156,7 +156,7 @@ export class WalletConnectCosigner { } async coSignUserOperation(address: string, coSignData: CoSignRequest): Promise { - const url = `${this.baseUrl}/${encodeURIComponent(address)}/sign` + const url = `${this.baseUrl}/${encodeURIComponent(address)}/sign?version=1` return await sendCoSignerRequest({ url, diff --git a/advanced/wallets/react-wallet-v2/src/utils/ConstantsUtil.ts b/advanced/wallets/react-wallet-v2/src/utils/ConstantsUtil.ts index e24109177..0f462d5b6 100644 --- a/advanced/wallets/react-wallet-v2/src/utils/ConstantsUtil.ts +++ b/advanced/wallets/react-wallet-v2/src/utils/ConstantsUtil.ts @@ -1,2 +1 @@ -export const WC_COSIGNER_BASE_URL = 'https://maksy.ngrok.dev/v1/sessions' -// export const WC_COSIGNER_BASE_URL = 'https://rpc.walletconnect.org/v1/sessions' +export const WC_COSIGNER_BASE_URL = 'https://rpc.walletconnect.org/v1/sessions' From 87baac3cd7d12717c607e758849b13c21aed91c3 Mon Sep 17 00:00:00 2001 From: Karandeep Singh Date: Tue, 17 Sep 2024 11:21:48 -0400 Subject: [PATCH 15/18] chores: move constants to single source --- .../src/lib/smart-accounts/SafeSmartAccountLib.ts | 8 +++++--- .../src/lib/smart-accounts/builders/SmartSessionUtil.ts | 3 +++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SafeSmartAccountLib.ts b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SafeSmartAccountLib.ts index efb6a6c12..162b280ff 100644 --- a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SafeSmartAccountLib.ts +++ b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SafeSmartAccountLib.ts @@ -19,7 +19,10 @@ import { } from 'viem' import { MultiKeySigner } from 'viem/_types/experimental/erc7715/types/signer' import { ModuleType } from 'permissionless/actions/erc7579' -import { MOCK_VALIDATOR_ADDRESSES } from './builders/SmartSessionUtil' +import { + MOCK_VALIDATOR_ADDRESSES, + TRUSTED_SMART_SESSIONS_ATTERSTER_ADDRESS +} from './builders/SmartSessionUtil' import { Permission } from '@/data/EIP7715Data' import { getSmartSessionContext } from './builders/ContextBuilderUtil' import { readContract } from 'viem/actions' @@ -181,7 +184,7 @@ export class SafeSmartAccountLib extends SmartAccountLib { console.log('Smart Account do not trusted the attesters of the smartsessions module') console.log('Enable trusting the attesters of the smartsessions module') const trustAttestersAction = getTrustAttestersAction({ - attesters: ['0xA4C777199658a41688E9488c4EcbD7a2925Cc23A'], + attesters: [TRUSTED_SMART_SESSIONS_ATTERSTER_ADDRESS], threshold: 1 }) setUpSmartAccountForSmartSession.push(trustAttestersAction) @@ -260,7 +263,6 @@ export class SafeSmartAccountLib extends SmartAccountLib { throw new Error('Client not initialized') } - const TRUSTED_SMART_SESSIONS_ATTERSTER_ADDRESS = '0xA4C777199658a41688E9488c4EcbD7a2925Cc23A' const attesters = await readContract(this.publicClient, { address: REGISTRY_ADDRESS, abi: parseAbi([ diff --git a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SmartSessionUtil.ts b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SmartSessionUtil.ts index b925850c8..732f70831 100644 --- a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SmartSessionUtil.ts +++ b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/builders/SmartSessionUtil.ts @@ -20,3 +20,6 @@ export const SIMPLE_SIGNER = '0x6ff7E9992160bB25f5c67b0Ce389c28d8faD3Bfb' as Add export const UNI_ACTION_POLICY = '0x237C7567Ac09D4DB7Dd48852a438F77a6bd65fc4' as Address export const USAGE_LIMIT_POLICY = '0x1f265E3beDc6ce93e1A36Dc80E1B1c65844F9861' as Address export const VALUE_LIMIT_POLICY = '0x6F0eC0c77cCAF4c25ff8FF7113D329caAA769688' as Address + +export const TRUSTED_SMART_SESSIONS_ATTERSTER_ADDRESS = + '0xA4C777199658a41688E9488c4EcbD7a2925Cc23A' as Address From abe5e253626f1138ab39905033b2b92720b47e78 Mon Sep 17 00:00:00 2001 From: Karandeep Singh Date: Tue, 17 Sep 2024 14:46:41 -0400 Subject: [PATCH 16/18] return smartAccount on allowed chains --- .../react-wallet-v2/src/utils/SmartAccountUtil.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/advanced/wallets/react-wallet-v2/src/utils/SmartAccountUtil.ts b/advanced/wallets/react-wallet-v2/src/utils/SmartAccountUtil.ts index c952cf6bc..8a196a80c 100644 --- a/advanced/wallets/react-wallet-v2/src/utils/SmartAccountUtil.ts +++ b/advanced/wallets/react-wallet-v2/src/utils/SmartAccountUtil.ts @@ -69,10 +69,12 @@ export function supportedAddressPriority( return providedAllowedChains.map(chain => chain?.id.toString()).includes(chainId) }) if (allowedChainIds.length === 0) return [] - const chainIdParsed = allowedChainIds[0].replace(`${nameSpaceKey}:`, '') - const chain = providedAllowedChains.find(chain => chain?.id.toString() === chainIdParsed)! - if (allowedChainIds.length > 0 && smartAccountAddress) { - return [`${nameSpaceKey}:${chain.id}:${smartAccountAddress}`] + const parsedAllowedChainIds = allowedChainIds.map(chain => chain.replace(`${nameSpaceKey}:`, '')) + if (parsedAllowedChainIds.length > 0 && smartAccountAddress) { + const returnVal = parsedAllowedChainIds.map( + chainId => `${nameSpaceKey}:${chainId}:${smartAccountAddress}` + ) + return returnVal } return [] } From c37d4eefca30ab93c8b36f8bd1d8f6514150f87f Mon Sep 17 00:00:00 2001 From: Karandeep Singh Date: Tue, 17 Sep 2024 22:24:41 -0400 Subject: [PATCH 17/18] display smartAccount on requested chains only --- .../src/hooks/useSmartAccounts.ts | 21 ++++++++++++++++++- .../src/views/SessionProposalModal.tsx | 10 ++++----- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/advanced/wallets/react-wallet-v2/src/hooks/useSmartAccounts.ts b/advanced/wallets/react-wallet-v2/src/hooks/useSmartAccounts.ts index 538e58237..af79eb6a4 100644 --- a/advanced/wallets/react-wallet-v2/src/hooks/useSmartAccounts.ts +++ b/advanced/wallets/react-wallet-v2/src/hooks/useSmartAccounts.ts @@ -1,3 +1,4 @@ +import { EIP155Chain } from '@/data/EIP155Data' import SettingsStore from '@/store/SettingsStore' import { createOrRestoreBiconomySmartAccount, @@ -51,8 +52,26 @@ export default function useSmartAccounts() { return accounts } + const getAvailableSmartAccountsOnNamespaceChains = (chains:string[]|undefined) => { + if (!smartAccountEnabled || chains === undefined) { + return [] + } + const accounts = [] + for (const [key, lib] of Object.entries(smartAccountWallets)) { + accounts.push({ + address: key.split(':')[1], + type: lib.type, + chain: lib.chain + }) + } + + const filteredAccounts = accounts.filter(account => chains.some(chain => chain && parseInt(chain.split(':')[1]) === account.chain.id)) + return filteredAccounts + } + return { initializeSmartAccounts, - getAvailableSmartAccounts + getAvailableSmartAccounts, + getAvailableSmartAccountsOnNamespaceChains } } diff --git a/advanced/wallets/react-wallet-v2/src/views/SessionProposalModal.tsx b/advanced/wallets/react-wallet-v2/src/views/SessionProposalModal.tsx index 8096cad8e..2a49eaa24 100644 --- a/advanced/wallets/react-wallet-v2/src/views/SessionProposalModal.tsx +++ b/advanced/wallets/react-wallet-v2/src/views/SessionProposalModal.tsx @@ -53,7 +53,7 @@ export default function SessionProposalModal() { const proposal = data?.data?.proposal as SignClientTypes.EventArguments['session_proposal'] const [isLoadingApprove, setIsLoadingApprove] = useState(false) const [isLoadingReject, setIsLoadingReject] = useState(false) - const { getAvailableSmartAccounts } = useSmartAccounts() + const { getAvailableSmartAccountsOnNamespaceChains } = useSmartAccounts() const supportedNamespaces = useMemo(() => { // eip155 @@ -362,8 +362,8 @@ export default function SessionProposalModal() { })) || Non available} Smart Accounts - {smartAccountEnabled && - getAvailableSmartAccounts().map((account, i) => { + {smartAccountEnabled && namespaces && + getAvailableSmartAccountsOnNamespaceChains(namespaces.eip155.chains).map((account, i) => { if (!account) { return <> } @@ -393,8 +393,8 @@ export default function SessionProposalModal() { Chains - {smartAccountEnabled && - getAvailableSmartAccounts().map(({ chain }, i) => { + {smartAccountEnabled && namespaces && + getAvailableSmartAccountsOnNamespaceChains(namespaces.eip155.chains).map(({ chain }, i) => { if (!chain) { return <> } From 3be57db7457ba4742af14e7424f547c6636d0a6f Mon Sep 17 00:00:00 2001 From: Karandeep Singh Date: Tue, 17 Sep 2024 22:25:14 -0400 Subject: [PATCH 18/18] chores:run prettier --- .../src/hooks/useSmartAccounts.ts | 6 ++- .../src/views/SessionProposalModal.tsx | 46 +++++++++++-------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/advanced/wallets/react-wallet-v2/src/hooks/useSmartAccounts.ts b/advanced/wallets/react-wallet-v2/src/hooks/useSmartAccounts.ts index af79eb6a4..70cedc49f 100644 --- a/advanced/wallets/react-wallet-v2/src/hooks/useSmartAccounts.ts +++ b/advanced/wallets/react-wallet-v2/src/hooks/useSmartAccounts.ts @@ -52,7 +52,7 @@ export default function useSmartAccounts() { return accounts } - const getAvailableSmartAccountsOnNamespaceChains = (chains:string[]|undefined) => { + const getAvailableSmartAccountsOnNamespaceChains = (chains: string[] | undefined) => { if (!smartAccountEnabled || chains === undefined) { return [] } @@ -65,7 +65,9 @@ export default function useSmartAccounts() { }) } - const filteredAccounts = accounts.filter(account => chains.some(chain => chain && parseInt(chain.split(':')[1]) === account.chain.id)) + const filteredAccounts = accounts.filter(account => + chains.some(chain => chain && parseInt(chain.split(':')[1]) === account.chain.id) + ) return filteredAccounts } diff --git a/advanced/wallets/react-wallet-v2/src/views/SessionProposalModal.tsx b/advanced/wallets/react-wallet-v2/src/views/SessionProposalModal.tsx index 2a49eaa24..8a7b3cae7 100644 --- a/advanced/wallets/react-wallet-v2/src/views/SessionProposalModal.tsx +++ b/advanced/wallets/react-wallet-v2/src/views/SessionProposalModal.tsx @@ -362,17 +362,20 @@ export default function SessionProposalModal() { })) || Non available} Smart Accounts - {smartAccountEnabled && namespaces && - getAvailableSmartAccountsOnNamespaceChains(namespaces.eip155.chains).map((account, i) => { - if (!account) { - return <> + {smartAccountEnabled && + namespaces && + getAvailableSmartAccountsOnNamespaceChains(namespaces.eip155.chains).map( + (account, i) => { + if (!account) { + return <> + } + return ( + + + + ) } - return ( - - - - ) - })} + )} @@ -393,17 +396,20 @@ export default function SessionProposalModal() { Chains - {smartAccountEnabled && namespaces && - getAvailableSmartAccountsOnNamespaceChains(namespaces.eip155.chains).map(({ chain }, i) => { - if (!chain) { - return <> + {smartAccountEnabled && + namespaces && + getAvailableSmartAccountsOnNamespaceChains(namespaces.eip155.chains).map( + ({ chain }, i) => { + if (!chain) { + return <> + } + return ( + + + + ) } - return ( - - - - ) - })} + )}