Skip to content

Commit

Permalink
feat: add permissionaless
Browse files Browse the repository at this point in the history
  • Loading branch information
danielsimao committed Nov 8, 2023
1 parent 8112ce5 commit 97a8230
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 62 deletions.
81 changes: 46 additions & 35 deletions examples/account-abstraction/permissionless/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { createWeb3Modal } from '@web3modal/wagmi/react';
import { useAccount, useMutation, useQuery, useSignMessage } from 'wagmi';
import { useAccount, useMutation, usePublicClient, 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 { UserOperation, getSenderAddress, getUserOperationHash } from 'permissionless';
import { UserOperation, getAccountNonce, getSenderAddress, getUserOperationHash } from 'permissionless';
import { FormEventHandler, useEffect, useState } from 'react';
import Jazzicon, { jsNumberForAddress } from 'react-jazzicon';
import truncateEthAddress from 'truncate-eth-address';
import { Hex, encodeFunctionData } from 'viem';
import { goerli } from 'viem/chains';
import { optimismGoerli } from 'viem/chains';
import { Layout } from './components';
import { ContractType } from './constants';
import { ContractType, contracts } from './constants';
import { ENTRY_POINT_ADDRESS } from './constants/erc4337';
import { useContract } from './hooks/useContract';
import { bundlerClient, getInitCode, publicClient } from './sdk';
import { bundlerClient, getInitCode, paymasterClient, publicClient } from './sdk';

createWeb3Modal({
defaultChain: L2_CHAIN_CONFIG,
Expand All @@ -29,6 +29,8 @@ function App() {
const [transferAddress, setTransferAddress] = useState('');
const { signMessageAsync } = useSignMessage();

const { getBytecode } = usePublicClient();

const flagOwner = useQuery<string, Error, string>(['owner'], {
queryFn: () => read.flagHolder() as Promise<string>,
refetchInterval: 5000
Expand All @@ -41,32 +43,49 @@ function App() {

const initCode = getInitCode(address);

const senderAddress = await getSenderAddress(publicClient, {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const senderAddress = await getSenderAddress(publicClient as any, {
initCode,
entryPoint: ENTRY_POINT_ADDRESS
});

const call = encodeFunctionData({
abi: contracts[ContractType.CTF].abi,
functionName: 'captureFlag',
args: []
});

const callData = encodeFunctionData({
abi: [
{
inputs: [],
name: 'captureFlag',
outputs: [{ type: 'address', name: '', internalType: 'address' }],
inputs: [
{ name: 'dest', type: 'address' },
{ name: 'value', type: 'uint256' },
{ name: 'func', type: 'bytes' }
],
name: 'execute',
outputs: [],
stateMutability: 'nonpayable',
type: 'function'
}
]
],
args: [contracts[ContractType.CTF].address, 0n, call]
});

const gasPrice = await bundlerClient.getUserOperationGasPrice();

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const nonce = await getAccountNonce(publicClient as any, {
sender: senderAddress,
entryPoint: ENTRY_POINT_ADDRESS
});

const byteCode = await getBytecode({ address: senderAddress });

const userOperation = {
sender: senderAddress,
nonce: 2n,
// NOTE: should pass this if the account was not created yet
// initCode: initCode
// NOT putting "0x" because account was already created
initCode: '0x',
nonce,
initCode: byteCode?.toString() ? '0x' : initCode,
callData,
maxFeePerGas: gasPrice.fast.maxFeePerGas,
maxPriorityFeePerGas: gasPrice.fast.maxPriorityFeePerGas,
Expand All @@ -76,41 +95,33 @@ function App() {
};

// NOTE: code for sponsoring the userOP
// const sponsorUserOperationResult = await paymasterClient.sponsorUserOperation({
// userOperation,
// entryPoint: ENTRY_POINT_ADDRESS
// });

// const sponsoredUserOp: UserOperation = {
// ...userOperation,
// ...sponsorUserOperationResult
// };
const sponsorUserOperationResult = await paymasterClient.sponsorUserOperation({
userOperation,
entryPoint: ENTRY_POINT_ADDRESS
});

const finalUserOperation: UserOperation = {
const sponsoredUserOp: UserOperation = {
...userOperation,
callGasLimit: 60000n,
verificationGasLimit: 60000n,
preVerificationGas: 60000n,
paymasterAndData: '0x'
...sponsorUserOperationResult
};

const signature = await signMessageAsync({
message: {
/** Raw data representation of the message. */
raw: getUserOperationHash({
userOperation: finalUserOperation,
chainId: goerli.id,
userOperation: sponsoredUserOp,
chainId: optimismGoerli.id,
entryPoint: ENTRY_POINT_ADDRESS
})
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as unknown as any
});

finalUserOperation.signature = signature;
sponsoredUserOp.signature = signature;

// SUBMIT THE USER OPERATION TO BE BUNDLED
const userOperationHash = await bundlerClient.sendUserOperation({
userOperation: finalUserOperation,
userOperation: sponsoredUserOp,
entryPoint: ENTRY_POINT_ADDRESS
});

Expand All @@ -121,7 +132,7 @@ function App() {
const receipt = await bundlerClient.waitForUserOperationReceipt({ hash: userOperationHash });
const txHash = receipt.receipt.transactionHash;

console.log(`UserOperation included: https://goerli.lineascan.build/tx/${txHash}`);
console.log(`UserOperation included: https://goerli-optimism.etherscan.io/tx/${txHash}`);
},
onSuccess: () => flagOwner.refetch()
});
Expand Down Expand Up @@ -201,7 +212,7 @@ function App() {
)}
<CTA
loading={capturaFlagMutation.isLoading}
disabled={flagOwner.isLoading || isCurrentOwner || transferFlagMutation.isLoading}
// disabled={flagOwner.isLoading || isCurrentOwner || transferFlagMutation.isLoading}
size='large'
fullWidth
onPress={() => capturaFlagMutation.mutate()}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Web3Auth } from '@web3auth/modal';
import { EthereumPrivateKeyProvider } from '@web3auth/ethereum-provider';
import { OpenloginAdapter } from '@web3auth/openlogin-adapter';
import { Chain } from 'wagmi';
import { L2_BLOCK_EXPLORER, L2_RPC_URL } from '../config';
import { optimismGoerli } from 'viem/chains';

export default function Web3AuthConnectorInstance(chains: Chain[]) {
// Create Web3Auth Instance
Expand All @@ -21,12 +21,12 @@ export default function Web3AuthConnectorInstance(chains: Chain[]) {

const chainConfig = {
chainNamespace: 'eip155',
chainId: '0x5',
rpcTarget: 'https://goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161',
displayName: 'Görli',
blockExplorer: 'https://goerli.etherscan.io',
ticker: 'gETH',
tickerName: 'Ethereum'
chainId: '0x1a4',
rpcTarget: optimismGoerli.rpcUrls.default.http[0],
displayName: optimismGoerli.name,
blockExplorer: optimismGoerli.blockExplorers.default.url,
ticker: optimismGoerli.nativeCurrency.symbol,
tickerName: optimismGoerli.nativeCurrency.name
};

const web3AuthInstance = new Web3Auth({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { L2_BLOCK_EXPLORER, L2_CHAIN_ID, L2_MULTICALL3_ADDRESS, L2_RPC_URL, L2_W

import { InjectedConnector } from 'wagmi/connectors/injected';
import Web3AuthConnectorInstance from './Web3AuthConnectorInstance';
import { goerli } from 'viem/chains';
import { optimismGoerli } from 'viem/chains';
const L2_PROJECT_ID = import.meta.env.VITE_WALLET_CONNECT_PROJECT_ID as string;

const L2_METADATA = {
Expand Down Expand Up @@ -39,7 +39,7 @@ const L2_CHAIN_CONFIG = {
}
} as const satisfies Chain;

const chains = [goerli];
const chains = [optimismGoerli];
// const chains = [L2_CHAIN_CONFIG];

const { publicClient, webSocketPublicClient } = configureChains(chains, [publicProvider()]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ enum ContractType {

const contracts = {
[ContractType.CTF]: {
address: '0x7349289C7C4097D82d670Df784Fec290cB22CCaf',
address: '0x4478eE0Fd9F054ADBb422c55Beb21DF2bcCe71C8',
// address: '0x7349289C7C4097D82d670Df784Fec290cB22CCaf',
// address: '0x587255805b6bcb4Eb34a811AaFb2aa65ef8aA72b',
abi: CTFAbi
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
export const CTFAbi = [
{ type: 'constructor', stateMutability: 'nonpayable', inputs: [] },
{ inputs: [], stateMutability: 'nonpayable', type: 'constructor' },
{
type: 'event',
anonymous: false,
inputs: [{ indexed: true, internalType: 'address', name: 'flagHolder', type: 'address' }],
name: 'FlagCaptured',
inputs: [{ type: 'address', name: 'flagHolder', internalType: 'address', indexed: true }],
anonymous: false
type: 'event'
},
{ type: 'function', stateMutability: 'nonpayable', outputs: [], name: 'captureFlag', inputs: [] },
{ inputs: [], name: 'captureFlag', outputs: [], stateMutability: 'nonpayable', type: 'function' },
{
type: 'function',
stateMutability: 'view',
outputs: [{ type: 'address', name: '', internalType: 'address' }],
inputs: [],
name: 'flagHolder',
inputs: []
outputs: [{ internalType: 'address', name: '', type: 'address' }],
stateMutability: 'view',
type: 'function'
},
{
type: 'function',
stateMutability: 'nonpayable',
outputs: [],
inputs: [{ internalType: 'address', name: 'newFlagHolder', type: 'address' }],
name: 'transferOwnership',
inputs: [{ type: 'address', name: 'newFlagHolder', internalType: 'address' }]
outputs: [],
stateMutability: 'nonpayable',
type: 'function'
}
];
8 changes: 4 additions & 4 deletions examples/account-abstraction/permissionless/src/sdk.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
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 { optimismGoerli } from 'viem/chains';
import { SIMPLE_ACCOUNT_FACTORY_ADDRESS } from './constants/erc4337';

const chain = goerli;
const chain = optimismGoerli;
const apiKey = import.meta.env.VITE_PIMLICO_API_KEY; // REPLACE THIS

// CREATE THE CLIENTS
Expand All @@ -14,15 +14,15 @@ export const publicClient = createPublicClient({
});

export const bundlerClient = createClient({
transport: http(`https://api.pimlico.io/v1/${chain.name.toLowerCase()}/rpc?apikey=${apiKey}`),
transport: http(`https://api.pimlico.io/v1/${'optimism-goerli'}/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}`),
transport: http(`https://api.pimlico.io/v2/${'optimism-goerli'}/rpc?apikey=${apiKey}`),
chain
}).extend(pimlicoPaymasterActions);

Expand Down

0 comments on commit 97a8230

Please sign in to comment.