pimlicolabs / permissionless.js

TypeScript utilities built on viem for ERC-4337: Account Abstraction
https://docs.pimlico.io/permissionless
MIT License
167 stars 46 forks source link

Support Coinbase Smart Wallet #224

Open jacobgoren-sb opened 3 months ago

jacobgoren-sb commented 3 months ago

Hi,

I want to deploy a Safe from a Smart Wallet using keys.coinbase.com

plusminushalf commented 3 months ago

Hey can you share the full error and code on how you are trying to deploy from safe?

jacobgoren-sb commented 3 months ago

I apologize I want to do it the other way:

From Coinbase Smart Wallet deploy a safe:

Here is the component:

//SmartWalletToSafe.tsx

import React, { useState } from "react";
import { useAccount } from "wagmi";
import { usePublicClient } from "wagmi";
import { useWalletClient } from "wagmi";
import { useEthersSigner } from "~~/hooks/useEthersSigner";
import { createSafe } from "~~/utils/createSafe";

export const SmartWalletToSafe: React.FC = () => {
  const { address } = useAccount();
  const [saltNonce] = useState<string>("ghvhgvh");
  const [smartAccountAddress, setSmartAccountAddress] = useState<`0x${string}`>();
  const { isConnected } = useAccount();
  const publicClient = usePublicClient();
  const ethersSigner = useEthersSigner();
  const { data: walletClient } = useWalletClient();

  async function createSafeAction() {
    if (isConnected && walletClient && publicClient && ethersSigner && address) {
      createSafe(isConnected, walletClient, publicClient, ethersSigner, saltNonce, setSmartAccountAddress);
    }
  }

  return (
    <div className="p-4 bg-base-200 rounded-lg shadow-md">
      <h2 className="text-2xl font-bold mb-4">Create Safe Account From a Smart Wallet</h2>
      <button type="button" className="btn btn-primary" onClick={createSafeAction}>
        Create Safe
      </button>
      {smartAccountAddress}
    </div>
  );
};

// Filename: createSafe.ts
import { bundlerUrl, paymasterClient, safeVersionString } from "./pimlicoClients";
import { ethers } from "ethers";
import { walletClientToSmartAccountSigner } from "permissionless";
import { ENTRYPOINT_ADDRESS_V06, createSmartAccountClient } from "permissionless";
import { signerToSafeSmartAccount } from "permissionless/accounts";
import { http, keccak256, toBytes } from "viem";
import { Account, Chain, Client, Transport, WalletClient } from "viem";
import { base } from "wagmi/chains";

export async function getSmartAccountClients(
  walletClient: WalletClient<Transport, Chain, Account>,
  publicClient: Client<Transport, Chain>,
  saltNonce: string,
): Promise<{ safeSmartAccountClient: any; smartAccountClient: any }> {
  const customSigner = walletClientToSmartAccountSigner(walletClient);
  const safeSmartAccountClient = await signerToSafeSmartAccount(publicClient, {
    entryPoint: ENTRYPOINT_ADDRESS_V06,
    signer: customSigner,
    safeVersion: safeVersionString,
    saltNonce: BigInt(keccak256(toBytes(saltNonce))),
  });

  const smartAccountClient = createSmartAccountClient({
    account: safeSmartAccountClient,
    chain: base,
    bundlerTransport: http(bundlerUrl),
    middleware: {
      sponsorUserOperation: paymasterClient.sponsorUserOperation,
    },
  });

  return { safeSmartAccountClient, smartAccountClient };
}

export async function createSafe(
  isConnected: boolean,
  walletClient: WalletClient<Transport, Chain, Account>,
  publicClient: Client<Transport, Chain>,
  ethersSigner: ethers.Signer,
  saltNonce: string,
  setSmartAccountAddress?: (address: `0x${string}`) => void,
): Promise<void> {
  if (isConnected && walletClient && publicClient && ethersSigner) {
    const { safeSmartAccountClient, smartAccountClient } = await getSmartAccountClients(
      walletClient,
      publicClient,
      saltNonce,
    );

    const txHash = await smartAccountClient.sendTransaction({
      to: safeSmartAccountClient.address,
      value: 0n,
      data: "0x",
    });
    if (setSmartAccountAddress) {
      setSmartAccountAddress(safeSmartAccountClient.address as `0x${string}`);
    }
    console.log("Transaction Hash: ", txHash);
  }
}
Screenshot 2024-06-14 at 10 57 12 AM Screenshot 2024-06-14 at 10 56 19 AM