Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update wagmi starter with Wagmi v2 #16

Open
RIP21 opened this issue Feb 18, 2024 · 1 comment
Open

Update wagmi starter with Wagmi v2 #16

RIP21 opened this issue Feb 18, 2024 · 1 comment

Comments

@RIP21
Copy link

RIP21 commented Feb 18, 2024

The current example uses v1 and it's not compatible with Wagmi v2. Quickly looking at the migration guides on Wagmi website didn't help much to be honest.

Thanks!

@RIP21
Copy link
Author

RIP21 commented Feb 20, 2024

For anyone looking for a compatible one, here is my attempt.

It seems to be working. Unsure tho, how getWalletClient is even used TBH and whether it was used in the original version TBH. So I still need a bit of clarification/help with that if it's important.

import { createWalletClient, custom, getAddress, UserRejectedRequestError } from 'viem'
import {
  type AuthType,
  type Config,
  type LoginOptions,
  type ParticleNetwork,
} from '@particle-network/auth'
import type { ParticleProvider } from '@particle-network/provider'
import { createConnector } from '@wagmi/core'

export type ParticleOptions = Config

const ID_NAME_MAPPING = {
  apple: 'Apple',
  google: 'Google',
  phone: 'Phone',
  facebook: 'Facebook',
  discord: 'Discord',
  github: 'GitHub',
  twitch: 'Twitch',
  twitter: 'Twitter',
  microsoft: 'Microsoft',
  linkedin: 'LinkedIn',
  jwt: 'Jwt',
} satisfies Record<Exclude<AuthType, 'email'>, string>

export function particle({
  id,
  options,
  loginOptions = {},
}: {
  readonly id?: AuthType
  readonly name?: string
  options: ParticleOptions
  loginOptions?: Omit<LoginOptions, 'preferredAuthType'>
}) {
  return createConnector<
    ParticleProvider,
    {
      client: ParticleNetwork | null
      provider: ParticleProvider | null
    }
  >((config) => {
    const name = id ? ID_NAME_MAPPING[id as Exclude<AuthType, 'email'>] : 'Particle'
    if (id) {
      ;(loginOptions as LoginOptions).preferredAuthType = id
    }
    return {
      id: id ?? 'particle',
      name: name,
      client: null,
      provider: null,
      type: 'Particle',
      async connect({ chainId }: { chainId?: number } = {}) {
        try {
          const provider = await this.getProvider()
          provider.on('accountsChanged', this.onAccountsChanged)
          provider.on('chainChanged', this.onChainChanged)
          provider.on('disconnect', this.onDisconnect)

          config.emitter.emit('message', { type: 'connecting' })

          // Switch to chain if provided
          let id = await this.getChainId()
          if (chainId && id !== chainId) {
            const chain = await this.switchChain!({ chainId })
            id = chain.id
          }

          if (!this.client?.auth.isLogin()) {
            await this.client?.auth.login(loginOptions)
          }
          const accounts = await this.getAccounts()

          return {
            accounts,
            chainId: id,
          }
        } catch (error) {
          // eslint-disable-next-line @typescript-eslint/no-explicit-any
          if ((error as any).code === 4011) {
            throw new UserRejectedRequestError(error as Error)
          }
          throw error
        }
      },
      async disconnect() {
        const provider = await this.getProvider()
        provider.removeListener('accountsChanged', this.onAccountsChanged)
        provider.removeListener('chainChanged', this.onChainChanged)
        provider.removeListener('disconnect', this.onDisconnect)
        await provider.disconnect()
      },
      async getAccounts() {
        const provider = await this.getProvider()
        const accounts = await provider.request({
          method: 'eth_accounts',
        })
        // return checksum address
        return [getAddress(accounts[0] as string)]
      },
      async getChainId() {
        const provider = await this.getProvider()
        const chainId = await provider.request({ method: 'eth_chainId' })
        return Number(chainId)
      },
      async getProvider() {
        if (!this.provider) {
          const [{ ParticleNetwork }, { ParticleProvider }] = await Promise.all([
            import('@particle-network/auth'),
            import('@particle-network/provider'),
          ])
          this.client = new ParticleNetwork(options)
          this.provider = new ParticleProvider(this.client.auth)
        }
        return this.provider
      },
      async getWalletClient({ chainId }: { chainId?: number } = {}) {
        const [provider, account] = await Promise.all([
          this.getProvider(),
          this.getAccounts(),
        ])
        const chain = config.chains.find((x) => x.id === chainId)!
        if (!provider) {
          throw new Error('provider is required.')
        }
        return createWalletClient({
          account: account[0],
          chain,
          transport: custom(provider),
        })
      },
      async isAuthorized() {
        try {
          await this.getProvider()
          return this.client!.auth.isLogin() && this.client!.auth.walletExist()
        } catch {
          return false
        }
      },
      async switchChain({ chainId }) {
        const provider = await this.getProvider()
        const id = `0x${chainId.toString(16)}`
        await provider.request({
          method: 'wallet_switchEthereumChain',
          params: [{ chainId: id }],
        })
        return (
          config.chains.find((x) => x.id === chainId) ?? {
            id: chainId,
            name: `Chain ${id}`,
            network: `${id}`,
            nativeCurrency: { name: 'Ether', decimals: 18, symbol: 'ETH' },
            rpcUrls: { default: { http: [''] }, public: { http: [''] } },
          }
        )
      },
      onAccountsChanged(accounts) {
        if (accounts.length === 0) {
          config.emitter.emit('disconnect')
        } else {
          config.emitter.emit('change', {
            accounts: accounts.map((it) => getAddress(it)),
          })
        }
      },
      onChainChanged(chainId) {
        const id = Number(chainId)
        config.emitter.emit('change', { chainId: id })
      },
      async onDisconnect() {
        config.emitter.emit('disconnect')
      },
    }
  })
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant