diff --git a/examples/account-abstraction/web3auth/.env.example b/examples/account-abstraction/permissionless/.env.example similarity index 100% rename from examples/account-abstraction/web3auth/.env.example rename to examples/account-abstraction/permissionless/.env.example diff --git a/examples/account-abstraction/web3auth/.eslintrc.cjs b/examples/account-abstraction/permissionless/.eslintrc.cjs similarity index 100% rename from examples/account-abstraction/web3auth/.eslintrc.cjs rename to examples/account-abstraction/permissionless/.eslintrc.cjs diff --git a/examples/account-abstraction/web3auth/.gitignore b/examples/account-abstraction/permissionless/.gitignore similarity index 100% rename from examples/account-abstraction/web3auth/.gitignore rename to examples/account-abstraction/permissionless/.gitignore diff --git a/examples/account-abstraction/web3auth/.prettierignore b/examples/account-abstraction/permissionless/.prettierignore similarity index 100% rename from examples/account-abstraction/web3auth/.prettierignore rename to examples/account-abstraction/permissionless/.prettierignore diff --git a/examples/account-abstraction/web3auth/.prettierrc.json b/examples/account-abstraction/permissionless/.prettierrc.json similarity index 100% rename from examples/account-abstraction/web3auth/.prettierrc.json rename to examples/account-abstraction/permissionless/.prettierrc.json diff --git a/examples/account-abstraction/web3auth/README.md b/examples/account-abstraction/permissionless/README.md similarity index 100% rename from examples/account-abstraction/web3auth/README.md rename to examples/account-abstraction/permissionless/README.md diff --git a/examples/account-abstraction/web3auth/index.html b/examples/account-abstraction/permissionless/index.html similarity index 100% rename from examples/account-abstraction/web3auth/index.html rename to examples/account-abstraction/permissionless/index.html diff --git a/examples/account-abstraction/web3auth/package.json b/examples/account-abstraction/permissionless/package.json similarity index 96% rename from examples/account-abstraction/web3auth/package.json rename to examples/account-abstraction/permissionless/package.json index 7599c495..ee3176fc 100644 --- a/examples/account-abstraction/web3auth/package.json +++ b/examples/account-abstraction/permissionless/package.json @@ -27,6 +27,7 @@ "@web3auth/openlogin-adapter": "^7.0.4", "@web3modal/wagmi": "^3.1.0", "big.js": "^6.2.1", + "permissionless": "^0.0.9", "react": "^18.2.0", "react-dom": "^18.2.0", "react-jazzicon": "^1.0.4", @@ -34,7 +35,7 @@ "truncate-eth-address": "^1.0.2", "viem": "^1.10.9", "vite-plugin-node-polyfills": "^0.15.0", - "wagmi": "^1.4.1" + "wagmi": "^1.4.5" }, "devDependencies": { "@typechain/ethers-v6": "^0.5.0", diff --git a/examples/account-abstraction/web3auth/pnpm-lock.yaml b/examples/account-abstraction/permissionless/pnpm-lock.yaml similarity index 99% rename from examples/account-abstraction/web3auth/pnpm-lock.yaml rename to examples/account-abstraction/permissionless/pnpm-lock.yaml index 8adadc37..e89302bd 100644 --- a/examples/account-abstraction/web3auth/pnpm-lock.yaml +++ b/examples/account-abstraction/permissionless/pnpm-lock.yaml @@ -50,6 +50,9 @@ dependencies: big.js: specifier: ^6.2.1 version: 6.2.1 + permissionless: + specifier: ^0.0.9 + version: 0.0.9(viem@1.17.2) react: specifier: ^18.2.0 version: 18.2.0 @@ -72,7 +75,7 @@ dependencies: specifier: ^0.15.0 version: 0.15.0(vite@4.5.0) wagmi: - specifier: ^1.4.1 + specifier: ^1.4.5 version: 1.4.5(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)(viem@1.17.2) devDependencies: @@ -6058,6 +6061,14 @@ packages: sha.js: 2.4.11 dev: false + /permissionless@0.0.9(viem@1.17.2): + resolution: {integrity: sha512-KtNggmRw3YvvsEZPPMJ26Gxk0AO9bAnZPvXYbDmAdIDXqqLElZnfcEmmeDMZe/K1aPHmwZmmG+EGG39g74dGrQ==} + peerDependencies: + viem: ^1.14.0 + dependencies: + viem: 1.17.2(typescript@5.2.2) + dev: false + /picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} diff --git a/examples/account-abstraction/web3auth/public/vite.svg b/examples/account-abstraction/permissionless/public/vite.svg similarity index 100% rename from examples/account-abstraction/web3auth/public/vite.svg rename to examples/account-abstraction/permissionless/public/vite.svg diff --git a/examples/account-abstraction/web3auth/src/App.tsx b/examples/account-abstraction/permissionless/src/App.tsx similarity index 54% rename from examples/account-abstraction/web3auth/src/App.tsx rename to examples/account-abstraction/permissionless/src/App.tsx index aacc61d2..b69a2294 100644 --- a/examples/account-abstraction/web3auth/src/App.tsx +++ b/examples/account-abstraction/permissionless/src/App.tsx @@ -1,14 +1,20 @@ import { createWeb3Modal } from '@web3modal/wagmi/react'; -import { useAccount, useMutation, useQuery } from 'wagmi'; +import { useAccount, useMutation, useQuery, useSignMessage } from 'wagmi'; import { L2_CHAIN_CONFIG, L2_PROJECT_ID, config } from './connectors/wagmi-connectors'; import { CTA, Card, Flex, H1, Input, P, Span } from '@interlay/ui'; +import { FormEventHandler, useEffect, useState } from 'react'; +import Jazzicon, { jsNumberForAddress } from 'react-jazzicon'; +import truncateEthAddress from 'truncate-eth-address'; import { Layout } from './components'; import { ContractType } from './constants'; import { useContract } from './hooks/useContract'; -import Jazzicon, { jsNumberForAddress } from 'react-jazzicon'; -import truncateEthAddress from 'truncate-eth-address'; -import { FormEventHandler, useEffect, useState } from 'react'; +import { UserOperation, getSenderAddress, getUserOperationHash } from 'permissionless'; +import { bundlerClient, getInitCode, paymasterClient, publicClient } from './sdk'; +import { ENTRY_POINT_ADDRESS } from './constants/erc4337'; +import { Hex, encodeFunctionData } from 'viem'; +import { goerli } from 'viem/chains'; +import { signMessage } from '@wagmi/core'; createWeb3Modal({ defaultChain: L2_CHAIN_CONFIG, @@ -22,14 +28,92 @@ function App() { const { address } = useAccount(); const [isTransfering, setTransfering] = useState(false); const [transferAddress, setTransferAddress] = useState(''); + const { data: signMessageData, error, isLoading, signMessageAsync, variables } = useSignMessage(); const flagOwner = useQuery(['owner'], { queryFn: () => read.flagHolder() as Promise, - refetchInterval: 10000 + refetchInterval: 5000 }); const capturaFlagMutation = useMutation({ - mutationFn: () => write.captureFlag(), + // mutationFn: () => write.captureFlag(), + mutationFn: async () => { + if (!address) return; + + const initCode = getInitCode(address); + + const senderAddress = await getSenderAddress(publicClient, { + initCode, + entryPoint: ENTRY_POINT_ADDRESS + }); + + const callData = encodeFunctionData({ + abi: [ + { + inputs: [], + name: 'captureFlag', + outputs: [{ type: 'address', name: '', internalType: 'address' }], + stateMutability: 'nonpayable', + type: 'function' + } + ] + }); + + const gasPrice = await bundlerClient.getUserOperationGasPrice(); + + const userOperation: UserOperation = { + sender: senderAddress, + nonce: 2n, + initCode: '0x', + callData, + maxFeePerGas: gasPrice.fast.maxFeePerGas, + maxPriorityFeePerGas: gasPrice.fast.maxPriorityFeePerGas, + // dummy signature + signature: + '0xa15569dd8f8324dbeabf8073fdec36d4b754f53ce5901e283c6de79af177dc94557fa3c9922cd7af2a96ca94402d35c39f266925ee6407aeb32b31d76978d4ba1c' as Hex + }; + + const sponsorUserOperationResult = await paymasterClient.sponsorUserOperation({ + userOperation, + entryPoint: ENTRY_POINT_ADDRESS + }); + + const finalUserOperation: UserOperation = { + ...userOperation, + callGasLimit: 60000n, + verificationGasLimit: 60000n, + preVerificationGas: 60000n, + paymasterAndData: '0x' + }; + + const signature = await signMessageAsync({ + message: { + /** Raw data representation of the message. */ + raw: getUserOperationHash({ + userOperation: finalUserOperation, + chainId: goerli.id, + entryPoint: ENTRY_POINT_ADDRESS + }) + } + }); + + finalUserOperation.signature = signature; + + // SUBMIT THE USER OPERATION TO BE BUNDLED + const userOperationHash = await bundlerClient.sendUserOperation({ + userOperation: finalUserOperation, + entryPoint: ENTRY_POINT_ADDRESS + }); + + console.log('Received User Operation hash:', userOperationHash); + + // let's also wait for the userOperation to be included, by continually querying for the receipts + console.log('Querying for receipts...'); + const receipt = await bundlerClient.waitForUserOperationReceipt({ hash: userOperationHash }); + const txHash = receipt.receipt.transactionHash; + + console.log(`UserOperation included: https://goerli.lineascan.build/tx/${txHash}`); + }, onSuccess: () => flagOwner.refetch() }); @@ -69,7 +153,7 @@ function App() {

Current Holder

- + {isCurrentOwner ? 'YOU' : truncateEthAddress(flagOwner.data || '0x0')} diff --git a/examples/account-abstraction/web3auth/src/components/AuthCTA/AuthCTA.tsx b/examples/account-abstraction/permissionless/src/components/AuthCTA/AuthCTA.tsx similarity index 100% rename from examples/account-abstraction/web3auth/src/components/AuthCTA/AuthCTA.tsx rename to examples/account-abstraction/permissionless/src/components/AuthCTA/AuthCTA.tsx diff --git a/examples/account-abstraction/web3auth/src/components/AuthCTA/index.tsx b/examples/account-abstraction/permissionless/src/components/AuthCTA/index.tsx similarity index 100% rename from examples/account-abstraction/web3auth/src/components/AuthCTA/index.tsx rename to examples/account-abstraction/permissionless/src/components/AuthCTA/index.tsx diff --git a/examples/account-abstraction/web3auth/src/components/EthFaucet/EthFaucet.tsx b/examples/account-abstraction/permissionless/src/components/EthFaucet/EthFaucet.tsx similarity index 100% rename from examples/account-abstraction/web3auth/src/components/EthFaucet/EthFaucet.tsx rename to examples/account-abstraction/permissionless/src/components/EthFaucet/EthFaucet.tsx diff --git a/examples/account-abstraction/web3auth/src/components/EthFaucet/index.tsx b/examples/account-abstraction/permissionless/src/components/EthFaucet/index.tsx similarity index 100% rename from examples/account-abstraction/web3auth/src/components/EthFaucet/index.tsx rename to examples/account-abstraction/permissionless/src/components/EthFaucet/index.tsx diff --git a/examples/account-abstraction/permissionless/src/components/Layout/Header.tsx b/examples/account-abstraction/permissionless/src/components/Layout/Header.tsx new file mode 100644 index 00000000..8e28afa8 --- /dev/null +++ b/examples/account-abstraction/permissionless/src/components/Layout/Header.tsx @@ -0,0 +1,81 @@ +import { CTA, Flex, Span } from '@interlay/ui'; +import { useWeb3Modal } from '@web3modal/wagmi/react'; +import { getSenderAddress } from 'permissionless'; +import Jazzicon, { jsNumberForAddress } from 'react-jazzicon'; +import truncateEthAddress from 'truncate-eth-address'; +import { concat, encodeFunctionData } from 'viem'; +import { useAccount, useBalance } from 'wagmi'; +import { ENTRY_POINT_ADDRESS, SIMPLE_ACCOUNT_FACTORY_ADDRESS } from '../../constants/erc4337'; +import { CTAWrapper, StyledHeader } from './Layout.styles'; +import { publicClient } from '../../sdk'; +import { useState } from 'react'; + +const getInitCode = (ownerAddress: `0x${string}`) => { + return concat([ + SIMPLE_ACCOUNT_FACTORY_ADDRESS, + encodeFunctionData({ + abi: [ + { + inputs: [ + { name: 'owner', type: 'address' }, + { name: 'salt', type: 'uint256' } + ], + name: 'createAccount', + outputs: [{ name: 'ret', type: 'address' }], + stateMutability: 'nonpayable', + type: 'function' + } + ], + args: [ownerAddress, 0n] + }) + ]); +}; + +const Header = () => { + const { open } = useWeb3Modal(); + + const [smartContractAddress, setSmartContractAddress] = useState(''); + + const { address, isConnecting } = useAccount({ + onConnect: async ({ address }) => { + if (!address) return; + const senderAddress = await getSenderAddress(publicClient, { + initCode: getInitCode(address), + entryPoint: ENTRY_POINT_ADDRESS + }); + + setSmartContractAddress(senderAddress); + } + }); + const { data } = useBalance({ address }); + + return ( + + + logo + + + {smartContractAddress} + Balance: {data?.value.toString()} + open()}> + {address ? ( + + + + {truncateEthAddress(address)} + + + ) : ( + 'Connect Wallet' + )} + + + + ); +}; + +export { Header }; diff --git a/examples/account-abstraction/web3auth/src/components/Layout/Layout.styles.tsx b/examples/account-abstraction/permissionless/src/components/Layout/Layout.styles.tsx similarity index 100% rename from examples/account-abstraction/web3auth/src/components/Layout/Layout.styles.tsx rename to examples/account-abstraction/permissionless/src/components/Layout/Layout.styles.tsx diff --git a/examples/account-abstraction/web3auth/src/components/Layout/Layout.tsx b/examples/account-abstraction/permissionless/src/components/Layout/Layout.tsx similarity index 100% rename from examples/account-abstraction/web3auth/src/components/Layout/Layout.tsx rename to examples/account-abstraction/permissionless/src/components/Layout/Layout.tsx diff --git a/examples/account-abstraction/web3auth/src/components/Layout/index.tsx b/examples/account-abstraction/permissionless/src/components/Layout/index.tsx similarity index 100% rename from examples/account-abstraction/web3auth/src/components/Layout/index.tsx rename to examples/account-abstraction/permissionless/src/components/Layout/index.tsx diff --git a/examples/account-abstraction/web3auth/src/components/index.tsx b/examples/account-abstraction/permissionless/src/components/index.tsx similarity index 100% rename from examples/account-abstraction/web3auth/src/components/index.tsx rename to examples/account-abstraction/permissionless/src/components/index.tsx diff --git a/examples/account-abstraction/web3auth/src/config.ts b/examples/account-abstraction/permissionless/src/config.ts similarity index 100% rename from examples/account-abstraction/web3auth/src/config.ts rename to examples/account-abstraction/permissionless/src/config.ts diff --git a/examples/account-abstraction/web3auth/src/connectors/Web3AuthConnectorInstance.tsx b/examples/account-abstraction/permissionless/src/connectors/Web3AuthConnectorInstance.tsx similarity index 99% rename from examples/account-abstraction/web3auth/src/connectors/Web3AuthConnectorInstance.tsx rename to examples/account-abstraction/permissionless/src/connectors/Web3AuthConnectorInstance.tsx index e844762d..9b4e504a 100644 --- a/examples/account-abstraction/web3auth/src/connectors/Web3AuthConnectorInstance.tsx +++ b/examples/account-abstraction/permissionless/src/connectors/Web3AuthConnectorInstance.tsx @@ -47,6 +47,7 @@ export default function Web3AuthConnectorInstance(chains: Chain[]) { const privateKeyProvider = new EthereumPrivateKeyProvider({ config: { chainConfig } }); + const openloginAdapterInstance = new OpenloginAdapter({ privateKeyProvider, adapterSettings: { diff --git a/examples/account-abstraction/web3auth/src/connectors/interfaces.ts b/examples/account-abstraction/permissionless/src/connectors/interfaces.ts similarity index 100% rename from examples/account-abstraction/web3auth/src/connectors/interfaces.ts rename to examples/account-abstraction/permissionless/src/connectors/interfaces.ts diff --git a/examples/account-abstraction/web3auth/src/connectors/wagmi-connectors.ts b/examples/account-abstraction/permissionless/src/connectors/wagmi-connectors.ts similarity index 100% rename from examples/account-abstraction/web3auth/src/connectors/wagmi-connectors.ts rename to examples/account-abstraction/permissionless/src/connectors/wagmi-connectors.ts diff --git a/examples/account-abstraction/web3auth/src/connectors/web3-auth-connector.ts b/examples/account-abstraction/permissionless/src/connectors/web3-auth-connector.ts similarity index 100% rename from examples/account-abstraction/web3auth/src/connectors/web3-auth-connector.ts rename to examples/account-abstraction/permissionless/src/connectors/web3-auth-connector.ts diff --git a/examples/account-abstraction/web3auth/src/constants/contracts.ts b/examples/account-abstraction/permissionless/src/constants/contracts.ts similarity index 100% rename from examples/account-abstraction/web3auth/src/constants/contracts.ts rename to examples/account-abstraction/permissionless/src/constants/contracts.ts diff --git a/examples/account-abstraction/permissionless/src/constants/erc4337.ts b/examples/account-abstraction/permissionless/src/constants/erc4337.ts new file mode 100644 index 00000000..4d1151bd --- /dev/null +++ b/examples/account-abstraction/permissionless/src/constants/erc4337.ts @@ -0,0 +1,2 @@ +export const ENTRY_POINT_ADDRESS = '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789'; +export const SIMPLE_ACCOUNT_FACTORY_ADDRESS = '0x9406Cc6185a346906296840746125a0E44976454'; diff --git a/examples/account-abstraction/web3auth/src/constants/index.ts b/examples/account-abstraction/permissionless/src/constants/index.ts similarity index 100% rename from examples/account-abstraction/web3auth/src/constants/index.ts rename to examples/account-abstraction/permissionless/src/constants/index.ts diff --git a/examples/account-abstraction/web3auth/src/contracts/abi/CTF.abi.ts b/examples/account-abstraction/permissionless/src/contracts/abi/CTF.abi.ts similarity index 100% rename from examples/account-abstraction/web3auth/src/contracts/abi/CTF.abi.ts rename to examples/account-abstraction/permissionless/src/contracts/abi/CTF.abi.ts diff --git a/examples/account-abstraction/web3auth/src/hooks/useContract.ts b/examples/account-abstraction/permissionless/src/hooks/useContract.ts similarity index 100% rename from examples/account-abstraction/web3auth/src/hooks/useContract.ts rename to examples/account-abstraction/permissionless/src/hooks/useContract.ts diff --git a/examples/account-abstraction/web3auth/src/index.css b/examples/account-abstraction/permissionless/src/index.css similarity index 100% rename from examples/account-abstraction/web3auth/src/index.css rename to examples/account-abstraction/permissionless/src/index.css diff --git a/examples/account-abstraction/web3auth/src/main.tsx b/examples/account-abstraction/permissionless/src/main.tsx similarity index 100% rename from examples/account-abstraction/web3auth/src/main.tsx rename to examples/account-abstraction/permissionless/src/main.tsx diff --git a/examples/account-abstraction/permissionless/src/sdk.ts b/examples/account-abstraction/permissionless/src/sdk.ts new file mode 100644 index 00000000..575ef266 --- /dev/null +++ b/examples/account-abstraction/permissionless/src/sdk.ts @@ -0,0 +1,48 @@ +import { bundlerActions } from 'permissionless'; +import { pimlicoBundlerActions, pimlicoPaymasterActions } from 'permissionless/actions/pimlico'; +import { concat, createClient, createPublicClient, encodeFunctionData, http } from 'viem'; +import { goerli } from 'viem/chains'; +import { SIMPLE_ACCOUNT_FACTORY_ADDRESS } from './constants/erc4337'; + +const chain = goerli; +const apiKey = import.meta.env.VITE_PIMLICO_API_KEY; // REPLACE THIS + +// CREATE THE CLIENTS +export const publicClient = createPublicClient({ + transport: http(chain.rpcUrls.default.http[0]), + chain +}); + +export const bundlerClient = createClient({ + transport: http(`https://api.pimlico.io/v1/${chain.name.toLowerCase()}/rpc?apikey=${apiKey}`), + chain +}) + .extend(bundlerActions) + .extend(pimlicoBundlerActions); + +export const paymasterClient = createClient({ + // ⚠️ using v2 of the API ⚠️ + transport: http(`https://api.pimlico.io/v2/${chain.name.toLowerCase()}/rpc?apikey=${apiKey}`), + chain +}).extend(pimlicoPaymasterActions); + +export const getInitCode = (ownerAddress: `0x${string}`) => { + return concat([ + SIMPLE_ACCOUNT_FACTORY_ADDRESS, + encodeFunctionData({ + abi: [ + { + inputs: [ + { name: 'owner', type: 'address' }, + { name: 'salt', type: 'uint256' } + ], + name: 'createAccount', + outputs: [{ name: 'ret', type: 'address' }], + stateMutability: 'nonpayable', + type: 'function' + } + ], + args: [ownerAddress, 0n] + }) + ]); +}; diff --git a/examples/account-abstraction/web3auth/src/vite-env.d.ts b/examples/account-abstraction/permissionless/src/vite-env.d.ts similarity index 100% rename from examples/account-abstraction/web3auth/src/vite-env.d.ts rename to examples/account-abstraction/permissionless/src/vite-env.d.ts diff --git a/examples/account-abstraction/web3auth/tsconfig.json b/examples/account-abstraction/permissionless/tsconfig.json similarity index 100% rename from examples/account-abstraction/web3auth/tsconfig.json rename to examples/account-abstraction/permissionless/tsconfig.json diff --git a/examples/account-abstraction/web3auth/tsconfig.node.json b/examples/account-abstraction/permissionless/tsconfig.node.json similarity index 100% rename from examples/account-abstraction/web3auth/tsconfig.node.json rename to examples/account-abstraction/permissionless/tsconfig.node.json diff --git a/examples/account-abstraction/web3auth/vite.config.ts b/examples/account-abstraction/permissionless/vite.config.ts similarity index 100% rename from examples/account-abstraction/web3auth/vite.config.ts rename to examples/account-abstraction/permissionless/vite.config.ts diff --git a/examples/account-abstraction/web3auth/src/components/Layout/Header.tsx b/examples/account-abstraction/web3auth/src/components/Layout/Header.tsx deleted file mode 100644 index 3509d966..00000000 --- a/examples/account-abstraction/web3auth/src/components/Layout/Header.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { CTA, Flex, Span } from '@interlay/ui'; -import { useWeb3Modal } from '@web3modal/wagmi/react'; -import Jazzicon, { jsNumberForAddress } from 'react-jazzicon'; -import truncateEthAddress from 'truncate-eth-address'; -import { useAccount } from 'wagmi'; -import { CTAWrapper, StyledHeader } from './Layout.styles'; -import { EthFaucet } from '../EthFaucet'; - -const Header = () => { - const { open } = useWeb3Modal(); - const { address, isConnecting } = useAccount(); - - return ( - - - logo - - - {address && ( - <> - - - )} - open()}> - {address ? ( - - - - {truncateEthAddress(address)} - - - ) : ( - 'Connect Wallet' - )} - - - - ); -}; - -export { Header };