ethereum-attestation-service / eas-sdk

Ethereum Attestation Service - TypeScript/JavaScript SDK
MIT License
83 stars 38 forks source link

Wagmi compatibility hooks #86

Open borgbob opened 4 months ago

borgbob commented 4 months ago

Hi

I started working on an alternative frontend which uses this SDK with wagmi/viem. The frontend is part of Avalanche deployment of EAS which is being led by @cryptofish7: https://github.com/avax-attestations/aas-frontend

In the docs I found a link to a gist with some hooks which wrapped wagmi/viem clients in ethers compatible signer/provider, but the code was outdated (it was targeting ethers v5) so I had to adapt it. Here's the updated code which works with ethers v6:

// useProvider.ts
import { JsonRpcProvider, FallbackProvider } from "ethers";
import { type PublicClient } from "viem";
import { useState, useEffect } from "react";
import { usePublicClient } from "wagmi";

function publicClientToProvider(publicClient: PublicClient) {
  const { chain, transport } = publicClient;
  if (!chain) {
    return;
  }

  const network = {
    chainId: chain.id,
    name: chain.name,
    ensAddress: chain.contracts?.ensRegistry?.address
  };

  if (transport.type === "fallback") {
    return new FallbackProvider(
      (transport.transports).map(
        ({ value }: any) => new JsonRpcProvider(value?.url, network)
      )
    );
  }

  return new JsonRpcProvider(transport.url, network);
}

type Provider = ReturnType<typeof publicClientToProvider>

export function useProvider() {
  const publicClient = usePublicClient();
  const [provider, setProvider] = useState<Provider | undefined>(undefined);

  useEffect(() => {
    if (publicClient) {
      setProvider(publicClientToProvider(publicClient));
    }
  }, [publicClient]);

  return provider;
}
// useSigner.ts
import { useEffect, useState } from "react";
import { useWalletClient } from "wagmi";
import { type WalletClient } from "viem";
import { BrowserProvider, JsonRpcSigner } from "ethers";

export async function walletClientToSigner(walletClient: WalletClient) {
  const { account, chain, transport } = walletClient;
  if (!chain || !account) {
    return;
  }

  const network = {
    chainId: chain.id,
    name: chain.name,
    ensAddress: chain.contracts?.ensRegistry?.address
  };

  const provider = new BrowserProvider(transport, network);
  const signer = await provider.getSigner(account.address);

  return signer;
}

export function useSigner() {
  const { data: walletClient } = useWalletClient();

  const [signer, setSigner] = useState<JsonRpcSigner | undefined>(undefined);

  useEffect(() => {
    if (walletClient) {
      walletClientToSigner(walletClient).then((signer) => {
        setSigner(signer);
      })
    }
  }, [walletClient]);

  return signer;
}

If you want, I can create a PR that adds these hooks to a new module (wagmi-compat.ts) so that this functionality is more easily available to SDK users. The module would not be referenced by other modules in this package and users would have to import it directly with something like:

import { useSigner, useProvider } from "@ethereum-attestation-service/eas-sdk/wagmi-compat"

I would also add viem, wagmi and react as peer dependencies.

lbeder commented 4 months ago

Hey @borgbob, this is great. It'd be amazing if you can indeed go ahead an open this as a PR (+ a very simple test) and I'll be more than happy to review and merge 🙏

mubarakone commented 4 months ago

@borgbob I followed your steps, however signer is not the same type as the argument for the eas.connect() function. What did you do to get that to work?

image

So far this is what I came up with, not sure if this works so please don't quote me:

import { SignerOrProvider } from '@ethereum-attestation-service/eas-sdk/dist/transaction';
const eas = new EAS(EASContractAddress);
    if (!signer) {
      return
    } else {
      eas.connect(signer as unknown as SignerOrProvider);
    }

Someone let me know what is the correct way to fix this.

borgbob commented 4 months ago

@mubarakone the return value of useSigner can be undefined when the component first mounts (and also when the wallet is not connected)

What I did in aas-frontend was create EAS in the submit handler after checking the signer was not undefined, here's how it looks like: https://github.com/avax-attestations/aas-frontend/blob/main/pages/schema/create.tsx#L77-L87

mubarakone commented 4 months ago

I get this error in your useSigner.ts file:

image

Not sure what this means. But I am also using Account Abstraction, so it could be related to that.

borgbob commented 4 months ago

@mubarakone if you are using an account abstraction, can you check if it correctly implements account.address? Also please check if the wagmi/viem versions are compatible with the ones used in aas-frontend