From 65c5723e2c28dee37a4081a64f3abef0b87091d2 Mon Sep 17 00:00:00 2001 From: Karandeep Singh <90941366+KannuSingh@users.noreply.github.com> Date: Tue, 17 Sep 2024 22:44:22 -0400 Subject: [PATCH] Transition dca dapp to base sepolia (#708) * change chain to baseSepolia * refactor userOpBuilder and cosigner service * update DonutContract address * chores:run prettier * fix functionName --- .../src/app/api/dca/execute/route.ts | 7 +- .../dca-dapp-demo/src/app/api/signer/route.ts | 3 +- .../dapps/dca-dapp-demo/src/hooks/useDCA.ts | 5 +- .../dca-dapp-demo/src/utils/ChainsUtil.ts | 128 ------------------ .../dapps/dca-dapp-demo/src/utils/DCAUtils.ts | 2 +- .../dca-dapp-demo/src/utils/DonutContract.ts | 2 +- .../src/utils/ERC7715PermissionsAsyncUtils.ts | 95 +++---------- .../src/utils/UserOpBuilderServiceUtils.ts | 83 ++++++------ .../dca-dapp-demo/src/utils/WagmiConstants.ts | 4 +- .../src/utils/WalletConnectCosignerUtils.ts | 4 +- 10 files changed, 78 insertions(+), 255 deletions(-) diff --git a/advanced/dapps/dca-dapp-demo/src/app/api/dca/execute/route.ts b/advanced/dapps/dca-dapp-demo/src/app/api/dca/execute/route.ts index 328f8a706..664a37f3d 100644 --- a/advanced/dapps/dca-dapp-demo/src/app/api/dca/execute/route.ts +++ b/advanced/dapps/dca-dapp-demo/src/app/api/dca/execute/route.ts @@ -7,7 +7,7 @@ import { CoSignerApiError } from "@/utils/WalletConnectCosignerUtils"; import { NextResponse } from "next/server"; import { encodeFunctionData, parseEther } from "viem"; import { GrantPermissionsReturnType } from "viem/experimental"; -import { sepolia } from "viem/chains"; +import { baseSepolia } from "viem/chains"; import { DCAFormSchemaType } from "@/schema/DCAFormSchema"; export async function POST(request: Request) { @@ -20,7 +20,8 @@ export async function POST(request: Request) { permissions: GrantPermissionsReturnType; pci: string; } = await request.json(); - const APPLICATION_PRIVATE_KEY = process.env.APPLICATION_PRIVATE_KEY as `0x${string}`; + const APPLICATION_PRIVATE_KEY = process.env + .APPLICATION_PRIVATE_KEY as `0x${string}`; try { if (!APPLICATION_PRIVATE_KEY) { @@ -61,7 +62,7 @@ export async function POST(request: Request) { ecdsaPrivateKey: APPLICATION_PRIVATE_KEY, pci, permissions, - chain: sepolia, + chain: baseSepolia, actions: purchaseDonutCallDataExecution, }); diff --git a/advanced/dapps/dca-dapp-demo/src/app/api/signer/route.ts b/advanced/dapps/dca-dapp-demo/src/app/api/signer/route.ts index 83f7adbc2..5ede0219d 100644 --- a/advanced/dapps/dca-dapp-demo/src/app/api/signer/route.ts +++ b/advanced/dapps/dca-dapp-demo/src/app/api/signer/route.ts @@ -3,7 +3,8 @@ import { privateKeyToAccount } from "viem/accounts"; export function GET() { try { - const APPLICATION_PRIVATE_KEY = process.env.APPLICATION_PRIVATE_KEY as `0x${string}`; + const APPLICATION_PRIVATE_KEY = process.env + .APPLICATION_PRIVATE_KEY as `0x${string}`; const account = privateKeyToAccount(APPLICATION_PRIVATE_KEY); return NextResponse.json({ key: account.publicKey }); diff --git a/advanced/dapps/dca-dapp-demo/src/hooks/useDCA.ts b/advanced/dapps/dca-dapp-demo/src/hooks/useDCA.ts index 6a6765f50..5bc2cfda9 100644 --- a/advanced/dapps/dca-dapp-demo/src/hooks/useDCA.ts +++ b/advanced/dapps/dca-dapp-demo/src/hooks/useDCA.ts @@ -4,7 +4,7 @@ import { hexStringToBase64, } from "../utils/EncodingUtils"; import { walletActionsErc7715 } from "viem/experimental"; -import { createPublicClient, custom } from "viem"; +import { createPublicClient, custom, zeroAddress } from "viem"; import { WalletConnectCosigner } from "../utils/WalletConnectCosignerUtils"; import { useDcaApplicationContext } from "../context/DcaApplicationContextProvider"; import { DCAFormSchemaType } from "@/schema/DCAFormSchema"; @@ -83,8 +83,7 @@ export function useDCA() { }, }, signerData: { - // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain - userOpBuilder: approvedPermissions.signerData?.userOpBuilder!, + userOpBuilder: zeroAddress, }, permissionsContext: approvedPermissions.permissionsContext, factory: approvedPermissions.factory || "", diff --git a/advanced/dapps/dca-dapp-demo/src/utils/ChainsUtil.ts b/advanced/dapps/dca-dapp-demo/src/utils/ChainsUtil.ts index 1106ac9f6..c8b7c8aec 100644 --- a/advanced/dapps/dca-dapp-demo/src/utils/ChainsUtil.ts +++ b/advanced/dapps/dca-dapp-demo/src/utils/ChainsUtil.ts @@ -8,110 +8,6 @@ function getBlockchainApiRpcUrl(chainId: number) { return `https://rpc.walletconnect.org/v1/?chainId=eip155:${chainId}&projectId=${process.env["NEXT_PUBLIC_PROJECT_ID"]}`; } -export const mainnet = { - chainId: 1, - name: "Ethereum", - currency: "ETH", - explorerUrl: "https://etherscan.io", - rpcUrl: getBlockchainApiRpcUrl(1), -}; - -export const arbitrum = { - chainId: 42161, - name: "Arbitrum", - currency: "ETH", - explorerUrl: "https://arbiscan.io", - rpcUrl: getBlockchainApiRpcUrl(42161), -}; - -export const avalanche = { - chainId: 43114, - name: "Avalanche", - currency: "AVAX", - explorerUrl: "https://snowtrace.io", - rpcUrl: getBlockchainApiRpcUrl(43114), -}; - -export const binanceSmartChain = { - chainId: 56, - name: "Binance Smart Chain", - currency: "BNB", - explorerUrl: "https://bscscan.com", - rpcUrl: getBlockchainApiRpcUrl(56), -}; - -export const optimism = { - chainId: 10, - name: "Optimism", - currency: "ETH", - explorerUrl: "https://optimistic.etherscan.io", - rpcUrl: getBlockchainApiRpcUrl(10), -}; - -export const polygon = { - chainId: 137, - name: "Polygon", - currency: "MATIC", - explorerUrl: "https://polygonscan.com", - rpcUrl: getBlockchainApiRpcUrl(137), -}; - -export const gnosis = { - chainId: 100, - name: "Gnosis", - currency: "xDAI", - explorerUrl: "https://gnosis.blockscout.com", - rpcUrl: getBlockchainApiRpcUrl(100), -}; - -export const zkSync = { - chainId: 324, - name: "ZkSync", - currency: "ETH", - explorerUrl: "https://explorer.zksync.io", - rpcUrl: getBlockchainApiRpcUrl(324), -}; - -export const zora = { - chainId: 7777777, - name: "Zora", - currency: "ETH", - explorerUrl: "https://explorer.zora.energy", - rpcUrl: getBlockchainApiRpcUrl(7777777), -}; - -export const celo = { - chainId: 42220, - name: "Celo", - currency: "CELO", - explorerUrl: "https://explorer.celo.org/mainnet", - rpcUrl: getBlockchainApiRpcUrl(42220), -}; - -export const base = { - chainId: 8453, - name: "Base", - currency: "BASE", - explorerUrl: "https://basescan.org", - rpcUrl: getBlockchainApiRpcUrl(8453), -}; - -export const aurora = { - chainId: 1313161554, - name: "Aurora", - currency: "ETH", - explorerUrl: "https://explorer.aurora.dev", - rpcUrl: getBlockchainApiRpcUrl(1313161554), -}; - -export const sepolia = { - chainId: 11155111, - name: "Sepolia", - currency: "ETH", - explorerUrl: "https://sepolia.etherscan.io", - rpcUrl: getBlockchainApiRpcUrl(11155111), -}; - export const baseSepolia = { chainId: 84532, name: "Base Sepolia", @@ -120,30 +16,6 @@ export const baseSepolia = { rpcUrl: getBlockchainApiRpcUrl(84532), }; -export const solana = { - chainId: "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp", - name: "Solana", - currency: "SOL", - explorerUrl: "https://solscan.io", - rpcUrl: "https://rpc.walletconnect.org/v1", -}; - -export const solanaTestnet = { - chainId: "4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z", - name: "Solana Testnet", - currency: "SOL", - explorerUrl: "https://explorer.solana.com/?cluster=testnet", - rpcUrl: "https://rpc.walletconnect.org/v1", -}; - -export const solanaDevnet = { - chainId: "EtWTRABZaYq6iMfeYKouRu166VU2xqa1", - name: "Solana Devnet", - currency: "SOL", - explorerUrl: "https://explorer.solana.com/?cluster=devnet", - rpcUrl: "https://rpc.walletconnect.org/v1", -}; - export function getChain(id: number) { const chains = Object.values(viemChains) as viemChains.Chain[]; diff --git a/advanced/dapps/dca-dapp-demo/src/utils/DCAUtils.ts b/advanced/dapps/dca-dapp-demo/src/utils/DCAUtils.ts index aba298d64..f095f338e 100644 --- a/advanced/dapps/dca-dapp-demo/src/utils/DCAUtils.ts +++ b/advanced/dapps/dca-dapp-demo/src/utils/DCAUtils.ts @@ -37,7 +37,7 @@ export function getSampleAsyncDCAPermissions( target: donutContractAddress, abi: donutContractAbi, valueLimit: parseEther("10").toString(), - functionName: "function purchase()", + functionName: "purchase(uint256)", }, policies: [], }, diff --git a/advanced/dapps/dca-dapp-demo/src/utils/DonutContract.ts b/advanced/dapps/dca-dapp-demo/src/utils/DonutContract.ts index ba8221df2..2a2b57c9b 100644 --- a/advanced/dapps/dca-dapp-demo/src/utils/DonutContract.ts +++ b/advanced/dapps/dca-dapp-demo/src/utils/DonutContract.ts @@ -70,4 +70,4 @@ export const abi = [ }, ]; -export const address = "0xfcfCFD8D9f4A23D8DD11b03b212B69262A3ba1b8"; +export const address = "0x2E65BAfA07238666c3b239E94F32DaD3cDD6498D"; diff --git a/advanced/dapps/dca-dapp-demo/src/utils/ERC7715PermissionsAsyncUtils.ts b/advanced/dapps/dca-dapp-demo/src/utils/ERC7715PermissionsAsyncUtils.ts index c48c32b39..36a70d08f 100644 --- a/advanced/dapps/dca-dapp-demo/src/utils/ERC7715PermissionsAsyncUtils.ts +++ b/advanced/dapps/dca-dapp-demo/src/utils/ERC7715PermissionsAsyncUtils.ts @@ -2,11 +2,10 @@ import { type GrantPermissionsReturnType } from "viem/experimental"; import { bigIntReplacer } from "./CommonUtils"; import { signMessage } from "viem/accounts"; import { type Chain } from "viem"; -import { WalletConnectCosigner } from "./WalletConnectCosignerUtils"; import { buildUserOp, + sendUserOp, type Call, - type FillUserOpResponse, } from "./UserOpBuilderServiceUtils"; export type MultikeySigner = { @@ -16,29 +15,31 @@ export type MultikeySigner = { }; }; -async function prepareUserOperationWithPermissions(args: { +export async function executeActionsWithECDSAAndCosignerPermissions(args: { actions: Call[]; + ecdsaPrivateKey: `0x${string}`; chain: Chain; permissions: GrantPermissionsReturnType; -}): Promise { - const { actions, chain, permissions } = args; + pci: string; +}): Promise<`0x${string}`> { + const { ecdsaPrivateKey, actions, chain, permissions, pci } = args; + if (!pci) { + throw new Error("No WC_COSIGNER PCI data available"); + } if (!permissions) { throw new Error("No permissions available"); } const { signerData, permissionsContext } = permissions; - if ( - !signerData?.userOpBuilder || - !signerData.submitToAddress || - !permissionsContext - ) { + if (!signerData?.submitToAddress || !permissionsContext) { throw new Error( `Invalid permissions ${JSON.stringify(permissions, bigIntReplacer)}`, ); } + const accountAddress = signerData.submitToAddress; const filledUserOp = await buildUserOp({ - account: signerData.submitToAddress, + account: accountAddress, chainId: chain.id, calls: actions, capabilities: { @@ -46,72 +47,20 @@ async function prepareUserOperationWithPermissions(args: { }, }); - return filledUserOp; -} - -async function signUserOperationWithECDSAKey(args: { - ecdsaPrivateKey: `0x${string}`; - userOpHash: `0x${string}`; -}): Promise<`0x${string}`> { - const { ecdsaPrivateKey, userOpHash } = args; + const userOp = filledUserOp.userOp; - const dappSignatureOnUserOp = await signMessage({ + const dappSignature = await signMessage({ privateKey: ecdsaPrivateKey, - message: { raw: userOpHash }, + message: { raw: filledUserOp.hash }, }); + userOp.signature = dappSignature; - return dappSignatureOnUserOp; -} - -export async function executeActionsWithECDSAAndCosignerPermissions(args: { - actions: Call[]; - ecdsaPrivateKey: `0x${string}`; - chain: Chain; - permissions: GrantPermissionsReturnType; - pci: string; -}): Promise<`0x${string}`> { - const { ecdsaPrivateKey, actions, chain, permissions, pci } = args; - const accountAddress = permissions?.signerData?.submitToAddress; - if (!accountAddress) { - throw new Error(`Unable to get account details from granted permission`); - } - - if (!pci) { - throw new Error("No WC_COSIGNER PCI data available"); - } - const caip10Address = `eip155:${chain?.id}:${accountAddress}`; - const filledUserOp = await prepareUserOperationWithPermissions({ - actions, - chain, - permissions, - }); - const userOp = filledUserOp.userOp; - - const dappSignature = await signUserOperationWithECDSAKey({ - ecdsaPrivateKey, - userOpHash: filledUserOp.hash, + const sendUserOpResponse = await sendUserOp({ + userOp, + pci, + chainId: chain.id, + permissionsContext: permissionsContext as `0x${string}`, }); - userOp.signature = dappSignature; - const walletConnectCosigner = new WalletConnectCosigner(); - const cosignResponse = await walletConnectCosigner.coSignUserOperation( - caip10Address, - { - pci, - userOp: { - ...userOp, - callData: userOp.callData, - 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), - }, - }, - ); - console.log("Cosign response:", cosignResponse); - return cosignResponse.receipt as `0x${string}`; + return sendUserOpResponse.userOpId; } diff --git a/advanced/dapps/dca-dapp-demo/src/utils/UserOpBuilderServiceUtils.ts b/advanced/dapps/dca-dapp-demo/src/utils/UserOpBuilderServiceUtils.ts index 317bf8e2e..c8ecd097a 100644 --- a/advanced/dapps/dca-dapp-demo/src/utils/UserOpBuilderServiceUtils.ts +++ b/advanced/dapps/dca-dapp-demo/src/utils/UserOpBuilderServiceUtils.ts @@ -1,44 +1,41 @@ import axios, { AxiosError } from "axios"; -import { bigIntReplacer } from "./CommonUtils"; +import { bigIntReplacer } from "../utils/CommonUtils"; import type { Address, Hex } from "viem"; import { USEROP_BUILDER_SERVICE_BASE_URL } from "./ConstantsUtil"; export type Call = { to: Address; value: bigint; data: Hex }; -export type BuildUserOpRequestArguments = { - account: Address; - chainId: number; - calls: Call[]; - capabilities: { - paymasterService?: { url: string }; - permissions?: { context: Hex }; - }; -}; -/** - * UserOperation v0.7 - */ -export type UserOperation = { +export type UserOperationWithBigIntAsHex = { sender: Address; - nonce: bigint; - factory?: Address; - factoryData?: Hex; + nonce: Hex; + factory: Address | undefined; + factoryData: Hex | undefined; callData: Hex; - callGasLimit: bigint; - verificationGasLimit: bigint; - preVerificationGas: bigint; - maxFeePerGas: bigint; - maxPriorityFeePerGas: bigint; - paymaster?: Address; - paymasterVerificationGasLimit?: bigint; - paymasterPostOpGasLimit?: bigint; - paymasterData?: 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 FillUserOpResponse = { - userOp: UserOperation; +export type BuildUserOpRequestParams = { + chainId: number; + account: Address; + calls: Call[]; + capabilities: { + paymasterService?: { url: string }; + permissions?: { context: Hex }; + }; +}; +export type BuildUserOpResponseReturn = { + userOp: UserOperationWithBigIntAsHex; hash: Hex; }; @@ -47,14 +44,14 @@ export type ErrorResponse = { error: string; }; -export type SendUserOpWithSignatureParams = { +export type SendUserOpRequestParams = { chainId: number; - userOp: UserOperation; - signature: Hex; + userOp: UserOperationWithBigIntAsHex; + pci?: string; permissionsContext?: Hex; }; -export type SendUserOpWithSignatureResponse = { - receipt: Hex; +export type SendUserOpResponseReturn = { + userOpId: Hex; }; // Define a custom error type @@ -116,11 +113,11 @@ async function sendUserOpBuilderRequest< } export async function buildUserOp( - args: BuildUserOpRequestArguments, -): Promise { + args: BuildUserOpRequestParams, +): Promise { const response = await sendUserOpBuilderRequest< - BuildUserOpRequestArguments, - FillUserOpResponse + BuildUserOpRequestParams, + BuildUserOpResponseReturn >({ url: `${USEROP_BUILDER_SERVICE_BASE_URL}/build`, data: args, @@ -133,12 +130,16 @@ export async function buildUserOp( return response; } -export async function sendUserOp(args: SendUserOpWithSignatureParams) { +export async function sendUserOp(args: SendUserOpRequestParams) { + const projectId = process.env["NEXT_PUBLIC_PROJECT_ID"]; + if (!projectId) { + throw new Error("NEXT_PUBLIC_PROJECT_ID is not set"); + } const response = await sendUserOpBuilderRequest< - SendUserOpWithSignatureParams, - SendUserOpWithSignatureResponse + SendUserOpRequestParams, + SendUserOpResponseReturn >({ - url: `${USEROP_BUILDER_SERVICE_BASE_URL}/sendUserOp`, + url: `${USEROP_BUILDER_SERVICE_BASE_URL}/sendUserOp?projectId=${projectId}`, data: args, headers: { "Content-Type": "application/json", diff --git a/advanced/dapps/dca-dapp-demo/src/utils/WagmiConstants.ts b/advanced/dapps/dca-dapp-demo/src/utils/WagmiConstants.ts index bbcad79d9..edd3c5609 100644 --- a/advanced/dapps/dca-dapp-demo/src/utils/WagmiConstants.ts +++ b/advanced/dapps/dca-dapp-demo/src/utils/WagmiConstants.ts @@ -1,10 +1,10 @@ import { defaultWagmiConfig } from "@web3modal/wagmi/react/config"; -import { sepolia, type Chain } from "wagmi/chains"; +import { baseSepolia, type Chain } from "wagmi/chains"; import { ConstantsUtil } from "./ConstantsUtil"; import type { CreateConnectorFn } from "wagmi"; export const WagmiConstantsUtil = { - chains: [sepolia] as [Chain, ...Chain[]], + chains: [baseSepolia] as [Chain, ...Chain[]], }; export function getWagmiConfig( diff --git a/advanced/dapps/dca-dapp-demo/src/utils/WalletConnectCosignerUtils.ts b/advanced/dapps/dca-dapp-demo/src/utils/WalletConnectCosignerUtils.ts index 1159f6e74..db1d206a3 100644 --- a/advanced/dapps/dca-dapp-demo/src/utils/WalletConnectCosignerUtils.ts +++ b/advanced/dapps/dca-dapp-demo/src/utils/WalletConnectCosignerUtils.ts @@ -1,7 +1,7 @@ import axios, { AxiosError } from "axios"; import { bigIntReplacer } from "./CommonUtils"; -import type { UserOperation } from "./UserOpBuilderServiceUtils"; import { WC_COSIGNER_BASE_URL } from "./ConstantsUtil"; +import { UserOperationWithBigIntAsHex } from "./UserOpBuilderServiceUtils"; // Define types for the request and response type AddPermission = { @@ -53,7 +53,7 @@ type RevokePermissionRequest = { type CoSignRequest = { pci: string; - userOp: UserOperation; + userOp: UserOperationWithBigIntAsHex; }; type CoSignResponse = {