From 13cb0ffabae838b577d1b8dc457b359a68d0764e Mon Sep 17 00:00:00 2001 From: Karandeep Singh <90941366+KannuSingh@users.noreply.github.com> Date: Tue, 3 Sep 2024 18:28:03 -0400 Subject: [PATCH] Implement actions using userOpBuilder service instead of permissionless.js (#2758) --- apps/laboratory/next.config.mjs | 2 +- apps/laboratory/package.json | 1 - .../Wagmi/WagmiCreatePasskeySignerTest.tsx | 8 +- ...WagmiPurchaseDonutAsyncPermissionsTest.tsx | 36 +- .../WagmiPurchaseDonutSyncPermissionsTest.tsx | 29 +- .../WagmiRequestPermissionsAsyncTest.tsx | 160 +- .../Wagmi/WagmiRequestPermissionsSyncTest.tsx | 183 +- .../src/context/ERC7715PermissionsContext.tsx | 69 + .../src/context/LocalEcdsaKeyContext.tsx | 72 + .../laboratory/src/context/PasskeyContext.tsx | 73 + .../context/WagmiPermissionsAsyncContext.tsx | 112 - .../context/WagmiPermissionsSyncContext.tsx | 112 - .../src/hooks/useERC7715Permissions.ts | 108 + .../src/hooks/useERC7715PermissionsAsync.ts | 137 - .../src/hooks/useERC7715PermissionsSync.ts | 158 - .../src/hooks/useLocalStorageState.ts | 6 +- apps/laboratory/src/hooks/useUserOpBuilder.ts | 312 - .../src/hooks/useWagmiActiveCapabilities.ts | 12 +- .../src/hooks/useWalletConnectCosigner.ts | 197 - apps/laboratory/src/layout/CustomWallet.tsx | 3 +- .../pages/library/wagmi-permissions-async.tsx | 13 +- .../pages/library/wagmi-permissions-sync.tsx | 13 +- apps/laboratory/src/utils/ConstantsUtil.ts | 7 +- apps/laboratory/src/utils/ERC7715Utils.ts | 220 +- apps/laboratory/src/utils/EncodingUtils.ts | 43 + apps/laboratory/src/utils/LocalStorage.ts | 30 +- apps/laboratory/src/utils/PermissionsUtils.ts | 67 - .../src/utils/UserOpBuilderServiceUtils.ts | 141 + .../src/utils/WalletConnectCosignerUtils.ts | 177 + pnpm-lock.yaml | 28272 +++++++--------- 30 files changed, 13237 insertions(+), 17536 deletions(-) create mode 100644 apps/laboratory/src/context/ERC7715PermissionsContext.tsx create mode 100644 apps/laboratory/src/context/LocalEcdsaKeyContext.tsx create mode 100644 apps/laboratory/src/context/PasskeyContext.tsx delete mode 100644 apps/laboratory/src/context/WagmiPermissionsAsyncContext.tsx delete mode 100644 apps/laboratory/src/context/WagmiPermissionsSyncContext.tsx create mode 100644 apps/laboratory/src/hooks/useERC7715Permissions.ts delete mode 100644 apps/laboratory/src/hooks/useERC7715PermissionsAsync.ts delete mode 100644 apps/laboratory/src/hooks/useERC7715PermissionsSync.ts delete mode 100644 apps/laboratory/src/hooks/useUserOpBuilder.ts delete mode 100644 apps/laboratory/src/hooks/useWalletConnectCosigner.ts delete mode 100644 apps/laboratory/src/utils/PermissionsUtils.ts create mode 100644 apps/laboratory/src/utils/UserOpBuilderServiceUtils.ts create mode 100644 apps/laboratory/src/utils/WalletConnectCosignerUtils.ts diff --git a/apps/laboratory/next.config.mjs b/apps/laboratory/next.config.mjs index 04353ea4ea..aef6cc3eb9 100644 --- a/apps/laboratory/next.config.mjs +++ b/apps/laboratory/next.config.mjs @@ -11,7 +11,7 @@ const cspHeader = ` style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src * 'self' data: blob: https://walletconnect.org https://walletconnect.com https://secure.walletconnect.com https://secure.walletconnect.org https://tokens-data.1inch.io https://tokens.1inch.io https://ipfs.io https://lab.web3modal.com; font-src 'self' https://fonts.gstatic.com; - connect-src 'self' https://rpc.walletconnect.com https://rpc.walletconnect.org https://relay.walletconnect.com https://relay.walletconnect.org wss://relay.walletconnect.com wss://relay.walletconnect.org https://pulse.walletconnect.com https://pulse.walletconnect.org https://api.web3modal.com https://api.web3modal.org wss://www.walletlink.org https://o1095249.ingest.sentry.io; + connect-src 'self' https://react-wallet.walletconnect.com https://rpc.walletconnect.com https://rpc.walletconnect.org https://relay.walletconnect.com https://relay.walletconnect.org wss://relay.walletconnect.com wss://relay.walletconnect.org https://pulse.walletconnect.com https://pulse.walletconnect.org https://api.web3modal.com https://api.web3modal.org wss://www.walletlink.org https://o1095249.ingest.sentry.io; frame-src 'self' https://verify.walletconnect.com https://verify.walletconnect.org https://secure.walletconnect.com https://secure.walletconnect.org; object-src 'none'; base-uri 'self'; diff --git a/apps/laboratory/package.json b/apps/laboratory/package.json index f3b253a383..255934ad6e 100644 --- a/apps/laboratory/package.json +++ b/apps/laboratory/package.json @@ -66,7 +66,6 @@ "framer-motion": "10.17.9", "next": "14.2.3", "next-auth": "4.24.5", - "permissionless": "0.1.31", "react": "18.2.0", "react-dom": "18.2.0", "react-icons": "4.12.0", diff --git a/apps/laboratory/src/components/Wagmi/WagmiCreatePasskeySignerTest.tsx b/apps/laboratory/src/components/Wagmi/WagmiCreatePasskeySignerTest.tsx index 2927920823..9f6e64ec6d 100644 --- a/apps/laboratory/src/components/Wagmi/WagmiCreatePasskeySignerTest.tsx +++ b/apps/laboratory/src/components/Wagmi/WagmiCreatePasskeySignerTest.tsx @@ -2,12 +2,12 @@ import { useEffect } from 'react' import { Button, Stack } from '@chakra-ui/react' import { privateKeyToAccount } from 'viem/accounts' import { useChakraToast } from '../Toast' -import { LOCAL_SIGNER_KEY, getItem } from '../../utils/LocalStorage' +import { LOCAL_SIGNER_KEY, getLocalStorageItem } from '../../utils/LocalStorage' import { createCredential } from 'webauthn-p256' -import { useWagmiPermissionsSync } from '../../context/WagmiPermissionsSyncContext' +import { usePasskey } from '../../context/PasskeyContext' export function WagmiCreatePasskeySignerTest() { - const { isPasskeyAvailable, setPasskey, passkeyId } = useWagmiPermissionsSync() + const { isPasskeyAvailable, setPasskey, passkeyId } = usePasskey() const toast = useChakraToast() async function handleCreatePasskey() { @@ -31,7 +31,7 @@ export function WagmiCreatePasskeySignerTest() { } useEffect(() => { - const storedLocalSignerPrivateKey = getItem(LOCAL_SIGNER_KEY) + const storedLocalSignerPrivateKey = getLocalStorageItem(LOCAL_SIGNER_KEY) if (storedLocalSignerPrivateKey) { privateKeyToAccount(storedLocalSignerPrivateKey as `0x${string}`) } diff --git a/apps/laboratory/src/components/Wagmi/WagmiPurchaseDonutAsyncPermissionsTest.tsx b/apps/laboratory/src/components/Wagmi/WagmiPurchaseDonutAsyncPermissionsTest.tsx index 3b7ff7dd20..fb26dc97f0 100644 --- a/apps/laboratory/src/components/Wagmi/WagmiPurchaseDonutAsyncPermissionsTest.tsx +++ b/apps/laboratory/src/components/Wagmi/WagmiPurchaseDonutAsyncPermissionsTest.tsx @@ -5,17 +5,14 @@ import { useChakraToast } from '../Toast' import { encodeFunctionData, parseEther } from 'viem' import { abi as donutContractAbi, address as donutContractaddress } from '../../utils/DonutContract' import { sepolia } from 'viem/chains' -import { useWagmiPermissionsAsync } from '../../context/WagmiPermissionsAsyncContext' -import { useERC7715PermissionsAsync } from '../../hooks/useERC7715PermissionsAsync' +import { useLocalEcdsaKey } from '../../context/LocalEcdsaKeyContext' +import { useERC7715Permissions } from '../../hooks/useERC7715Permissions' +import { executeActionsWithECDSAAndCosignerPermissions } from '../../utils/ERC7715Utils' export function WagmiPurchaseDonutAsyncPermissionsTest() { - const { grantedPermissions, wcCosignerData, privateKey, projectId } = useWagmiPermissionsAsync() + const { privateKey } = useLocalEcdsaKey() - const { executeActionsWithECDSAAndCosignerPermissions } = useERC7715PermissionsAsync({ - chain: sepolia, - permissions: grantedPermissions, - projectId - }) + const { grantedPermissions, pci } = useERC7715Permissions() const { data: donutsOwned, @@ -35,13 +32,15 @@ export function WagmiPurchaseDonutAsyncPermissionsTest() { async function onPurchaseDonutWithPermissions() { setTransactionPending(true) try { - if (!wcCosignerData) { - throw Error('No wc-cosigner data available') - } - if (!privateKey) { throw new Error(`Unable to get dApp private key`) } + if (!grantedPermissions) { + throw Error('No permissions available') + } + if (!pci) { + throw Error('No WC cosigner data(PCI) available') + } const purchaseDonutCallData = encodeFunctionData({ abi: donutContractAbi, functionName: 'purchase', @@ -49,26 +48,27 @@ export function WagmiPurchaseDonutAsyncPermissionsTest() { }) const purchaseDonutCallDataExecution = [ { - target: donutContractaddress as `0x${string}`, + to: donutContractaddress as `0x${string}`, value: parseEther('0.0001'), - callData: purchaseDonutCallData + data: purchaseDonutCallData } ] const txHash = await executeActionsWithECDSAAndCosignerPermissions({ actions: purchaseDonutCallDataExecution, chain: sepolia, - ecdsaPrivateKey: privateKey as `0x${string}` + ecdsaPrivateKey: privateKey as `0x${string}`, + permissions: grantedPermissions, + pci }) if (txHash) { toast({ - title: 'Transaction success', - description: txHash, + title: 'UserOp submitted successfully', + description: `UserOp Hash: ${txHash}`, type: 'success' }) await fetchDonutsOwned() } } catch (error) { - // Console.log(error) toast({ title: 'Transaction Failed', description: `${error}`, diff --git a/apps/laboratory/src/components/Wagmi/WagmiPurchaseDonutSyncPermissionsTest.tsx b/apps/laboratory/src/components/Wagmi/WagmiPurchaseDonutSyncPermissionsTest.tsx index d97f8f6d60..5ec66a05e0 100644 --- a/apps/laboratory/src/components/Wagmi/WagmiPurchaseDonutSyncPermissionsTest.tsx +++ b/apps/laboratory/src/components/Wagmi/WagmiPurchaseDonutSyncPermissionsTest.tsx @@ -4,17 +4,14 @@ import { useState } from 'react' import { useChakraToast } from '../Toast' import { encodeFunctionData, parseEther } from 'viem' import { abi as donutContractAbi, address as donutContractaddress } from '../../utils/DonutContract' -import { useERC7715PermissionsSync } from '../../hooks/useERC7715PermissionsSync' -import { useWagmiPermissionsSync } from '../../context/WagmiPermissionsSyncContext' +import { useERC7715Permissions } from '../../hooks/useERC7715Permissions' +import { usePasskey } from '../../context/PasskeyContext' import { sepolia } from 'viem/chains' +import { executeActionsWithPasskeyAndCosignerPermissions } from '../../utils/ERC7715Utils' export function WagmiPurchaseDonutSyncPermissionsTest() { - const { grantedPermissions, wcCosignerData, passkeyId, projectId } = useWagmiPermissionsSync() - const { executeActionsWithPasskeyAndCosignerPermissions } = useERC7715PermissionsSync({ - chain: sepolia, - permissions: grantedPermissions, - projectId - }) + const { passkeyId } = usePasskey() + const { grantedPermissions, pci } = useERC7715Permissions() const { data: donutsOwned, @@ -37,8 +34,8 @@ export function WagmiPurchaseDonutSyncPermissionsTest() { if (!grantedPermissions) { throw Error('No permissions available') } - if (!wcCosignerData) { - throw Error('No wc-cosigner data available') + if (!pci) { + throw Error('No WC cosigner data(PCI) available') } const purchaseDonutCallData = encodeFunctionData({ @@ -48,20 +45,22 @@ export function WagmiPurchaseDonutSyncPermissionsTest() { }) const purchaseDonutCallDataExecution = [ { - target: donutContractaddress as `0x${string}`, + to: donutContractaddress as `0x${string}`, value: parseEther('0.0001'), - callData: purchaseDonutCallData + data: purchaseDonutCallData } ] const txHash = await executeActionsWithPasskeyAndCosignerPermissions({ actions: purchaseDonutCallDataExecution, + chain: sepolia, passkeyId, - wcCosignerData + permissions: grantedPermissions, + pci }) if (txHash) { toast({ - title: 'Transaction success', - description: txHash, + title: 'UserOp submitted successfully', + description: `UserOp Hash: ${txHash}`, type: 'success' }) await fetchDonutsOwned() diff --git a/apps/laboratory/src/components/Wagmi/WagmiRequestPermissionsAsyncTest.tsx b/apps/laboratory/src/components/Wagmi/WagmiRequestPermissionsAsyncTest.tsx index 2eb7919111..12db6bf0cb 100644 --- a/apps/laboratory/src/components/Wagmi/WagmiRequestPermissionsAsyncTest.tsx +++ b/apps/laboratory/src/components/Wagmi/WagmiRequestPermissionsAsyncTest.tsx @@ -3,134 +3,102 @@ import { useAccount } from 'wagmi' import { walletActionsErc7715 } from 'viem/experimental' import { useCallback, useState } from 'react' import { useChakraToast } from '../Toast' -import { createPublicClient, custom } from 'viem' +import { createWalletClient, custom, type Address, type Chain } from 'viem' import { EIP_7715_RPC_METHODS } from '../../utils/EIP5792Utils' -import { useWalletConnectCosigner } from '../../hooks/useWalletConnectCosigner' -import { useWagmiAvailableCapabilities } from '../../hooks/useWagmiActiveCapabilities' -import { useWagmiPermissionsAsync } from '../../context/WagmiPermissionsAsyncContext' import { - decodeUncompressedPublicKey, - encodePublicKeyToDID, - hexStringToBase64 -} from '../../utils/EncodingUtils' + useWagmiAvailableCapabilities, + type Provider +} from '../../hooks/useWagmiActiveCapabilities' +import { useLocalEcdsaKey } from '../../context/LocalEcdsaKeyContext' import { bigIntReplacer } from '../../utils/CommonUtils' -import { getSampleAsyncPermissions } from '../../utils/ERC7715Utils' +import { useERC7715Permissions } from '../../hooks/useERC7715Permissions' +import { getPurchaseDonutPermissions } from '../../utils/ERC7715Utils' +import { KeyTypes } from '../../utils/EncodingUtils' export function WagmiRequestPermissionsAsyncTest() { const { provider, supported } = useWagmiAvailableCapabilities({ method: EIP_7715_RPC_METHODS.WALLET_GRANT_PERMISSIONS }) const { chain, address, isConnected } = useAccount() - const caip10Address = `eip155:${chain?.id}:${address}` - const { - projectId, - signer, - grantedPermissions, - clearGrantedPermissions, - setGrantedPermissions, - setWCCosignerData - } = useWagmiPermissionsAsync() + if (!isConnected || !provider || !address || !chain) { + return ( + + Wallet not connected + + ) + } + if (!supported) { + return ( + + Wallet does not support wallet_grantPermissions rpc method + + ) + } + + return +} + +function ConnectedTestContent({ + chain, + provider, + address +}: { + chain: Chain + provider: Provider + address: Address +}) { + const { grantedPermissions, clearGrantedPermissions, grantPermissions } = useERC7715Permissions() + const { signer } = useLocalEcdsaKey() const [isRequestPermissionLoading, setRequestPermissionLoading] = useState(false) - const { addPermission, updatePermissionsContext } = useWalletConnectCosigner( - caip10Address, - projectId - ) const toast = useChakraToast() const onRequestPermissions = useCallback(async () => { setRequestPermissionLoading(true) - - if (!signer) { - throw new Error('PrivateKey signer not available') - } - if (!provider) { - throw new Error('No Provider available, Please connect your wallet.') - } try { - const addPermissionResponse = await addPermission({ - permissionType: 'donut-purchase', - data: '', - onChainValidated: false, - required: true - }) - setWCCosignerData(addPermissionResponse) - const cosignerPublicKey = decodeUncompressedPublicKey(addPermissionResponse.key) - - const dAppECDSAPublicKey = signer.publicKey - const dAppSecp256k1DID = encodePublicKeyToDID(dAppECDSAPublicKey, 'secp256k1') - const coSignerSecp256k1DID = encodePublicKeyToDID(cosignerPublicKey, 'secp256k1') + if (!signer) { + throw new Error('PrivateKey signer not available') + } + if (!provider) { + throw new Error('No Provider available, Please connect your wallet.') + } - const publicClient = createPublicClient({ + const walletClient = createWalletClient({ + account: address, chain, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion transport: custom(provider) }).extend(walletActionsErc7715()) - const samplePermissions = getSampleAsyncPermissions([coSignerSecp256k1DID, dAppSecp256k1DID]) - const approvedPermissions = await publicClient.grantPermissions(samplePermissions) - if (approvedPermissions) { - await updatePermissionsContext({ - pci: addPermissionResponse.pci, - context: { - expiry: approvedPermissions.expiry, - signer: { - type: 'donut-purchase', - data: { - ids: [addPermissionResponse.key, hexStringToBase64(dAppECDSAPublicKey)] - } - }, - signerData: { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-non-null-asserted-optional-chain - userOpBuilder: approvedPermissions.signerData?.userOpBuilder! - }, - permissionsContext: approvedPermissions.permissionsContext, - factory: approvedPermissions.factory || '', - factoryData: approvedPermissions.factoryData || '' - } - }) - setGrantedPermissions(approvedPermissions) - setRequestPermissionLoading(false) - toast({ - type: 'success', - title: 'Permissions Granted', - description: JSON.stringify(approvedPermissions, bigIntReplacer) - }) - return - } - toast({ title: 'Error', description: 'Failed to obtain permissions' }) + const purchaseDonutPermissions = getPurchaseDonutPermissions() + const response = await grantPermissions(walletClient, { + permissions: purchaseDonutPermissions, + signerKey: { + key: signer.publicKey, + type: KeyTypes.secp256k1 + } + }) + toast({ + type: 'success', + title: 'Permissions Granted', + description: JSON.stringify(response.approvedPermissions, bigIntReplacer) + }) } catch (error) { toast({ type: 'error', - title: 'Permissions Erros', - description: error instanceof Error ? error.message : 'Some error occurred' + title: 'Request Permissions Errors', + description: error instanceof Error ? error.message : 'Unknown Error' }) + } finally { + setRequestPermissionLoading(false) } - setRequestPermissionLoading(false) - }, [signer, provider]) - if (!isConnected || !provider || !address) { - return ( - - Wallet not connected - - ) - } - if (!supported) { - return ( - - Wallet does not support wallet_grantPermissions rpc method - - ) - } + }, [signer, provider, address, chain, grantPermissions, toast]) return (