Skip to content

Commit

Permalink
cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
jribbink committed Feb 20, 2025
1 parent 676a756 commit 25205eb
Show file tree
Hide file tree
Showing 4 changed files with 258 additions and 227 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {formatChainId} from "./util/eth"
import {getAccountsFromNamespaces} from "@walletconnect/utils"
import {FLOW_METHODS} from "@onflow/fcl-wc"
import * as fcl from "@onflow/fcl"
import {CurrentUser, Service} from "@onflow/typedefs"
import {Service} from "@onflow/typedefs"

const BASE_WC_SERVICE = (
externalProvider: InstanceType<typeof UniversalProvider>
Expand All @@ -43,11 +43,11 @@ const BASE_WC_SERVICE = (
},
}) as unknown as Service

export class ExtendedEthereumProvider extends EthereumProvider {
export class WalletConnectEthereumProvider extends EthereumProvider {
static async init(
opts: EthereumProviderOptions
): Promise<ExtendedEthereumProvider> {
const provider = new ExtendedEthereumProvider()
): Promise<WalletConnectEthereumProvider> {
const provider = new WalletConnectEthereumProvider()
await provider.initialize(opts)

// Refresh the FCL user to align with the WalletConnect session
Expand All @@ -63,7 +63,10 @@ export class ExtendedEthereumProvider extends EthereumProvider {

// If there’s no auth service or the auth service
if (authnService && authnService.uid !== wcService.uid) {
// TODO: need to handle... maybe wait for condition to reauthenticate
// Another FCL user is already authenticated, we need to unauthenticate it
if (provider.signer.session) {
await fclUser.authenticate({service: wcService})
}
} else {
// Determine the external provider's topic from the auth service params
const externalProvider = authnService?.params?.externalProvider as
Expand Down Expand Up @@ -108,10 +111,7 @@ export class ExtendedEthereumProvider extends EthereumProvider {

return provider
}
// TODO: remove
protected async initialize(opts: EthereumProviderOptions): Promise<void> {
await super.initialize(opts)
}

async connect(
opts?: Parameters<InstanceType<typeof EthereumProvider>["connect"]>[0]
) {
Expand Down
215 changes: 215 additions & 0 deletions packages/fcl-wagmi-adapter/src/fcl-connector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
import {
ChainNotConfiguredError,
type Connector,
createConnector,
} from "@wagmi/core"
import {
type Address,
type ProviderConnectInfo,
ProviderDisconnectedError,
SwitchChainError,
getAddress,
numberToHex,
} from "viem"
import {createProvider} from "@onflow/fcl-ethereum-provider"

type FclWagmiAdapterParams = Parameters<typeof createProvider>[0]

export function fclWagmiAdapter(params: FclWagmiAdapterParams) {
type Provider = ReturnType<typeof createProvider>
type Properties = {
onConnect(connectInfo: ProviderConnectInfo): void
onDisplayUri(uri: string): void
}
let provider: Provider | undefined

let accountsChanged: Connector["onAccountsChanged"] | undefined
let chainChanged: Connector["onChainChanged"] | undefined
let connect: Connector["onConnect"] | undefined
let disconnect: ((error: Error) => void) | undefined

// Parse and validate service parameters
const id = params.service?.uid || "fcl"
const name = params.service?.provider?.name || "Cadence Wallet"

// TODO: we need to surface this through FCL service configuration
const rdns = (params.service?.provider as any)?.rdns

return createConnector<Provider, Properties>(config => ({
id: id,
name: name,
type: "fcl-wagmi-adapter",
rdns: rdns,
async setup() {
const provider = await this.getProvider()

if (connect) provider.removeListener("connect", connect)
connect = this.onConnect.bind(this)
provider.on("connect", connect)

// We shouldn't need to listen for `'accountsChanged'` here since the `'connect'` event should suffice (and wallet shouldn't be connected yet).
// Some wallets, like MetaMask, do not implement the `'connect'` event and overload `'accountsChanged'` instead.
if (!accountsChanged) {
accountsChanged = this.onAccountsChanged.bind(this)
provider.on("accountsChanged", accountsChanged)
}
},
async connect({isReconnecting}: any = {}) {
const provider = await this.getProvider()

let accounts: readonly Address[]
if (isReconnecting) {
accounts = await this.getAccounts()
} else {
accounts = (
(await provider.request({
method: "eth_requestAccounts",
})) as string[]
).map(x => getAddress(x))
}

// Manage EIP-1193 event listeners
// https://eips.ethereum.org/EIPS/eip-1193#events
if (connect) provider.removeListener("connect", connect)
connect = this.onConnect.bind(this)
provider.on("connect", connect)

if (accountsChanged)
provider.removeListener("accountsChanged", accountsChanged)
accountsChanged = this.onAccountsChanged.bind(this)
provider.on("accountsChanged", accountsChanged)

if (chainChanged) provider.removeListener("chainChanged", chainChanged)
chainChanged = this.onChainChanged.bind(this)
provider.on("chainChanged", chainChanged)

if (disconnect) provider.removeListener("disconnect", disconnect)
disconnect = (error: Error) => {
throw new ProviderDisconnectedError(error)
}
provider.on("disconnect", disconnect)

return {accounts, chainId: await this.getChainId()}
},
async disconnect() {
const provider = await this.getProvider()

// Manage EIP-1193 event listeners
if (chainChanged) provider.removeListener("chainChanged", chainChanged)
chainChanged = undefined

if (disconnect) provider.removeListener("disconnect", disconnect)
disconnect = undefined

if (connect) provider.removeListener("connect", connect)
connect = this.onConnect.bind(this)
provider.on("connect", connect)

await provider.disconnect()
},
async getAccounts() {
const provider = await this.getProvider()
const accounts = (await provider.request({
method: "eth_accounts",
})) as string[]
return accounts.map(x => getAddress(x))
},
async getChainId() {
const provider = await this.getProvider()
const chainId = await provider.request({method: "eth_chainId"})
console.log("CHAIN ID", chainId)
return Number(chainId)
},
async getProvider() {
return provider ?? (provider = createProvider(params))
},
async isAuthorized() {
// TODO: There may be an issue here if a user without a COA refreshes the page
// We should instead be checking whether FCL itself is authorized
const accounts = await this.getAccounts()
return accounts.length > 0
},
async switchChain({addEthereumChainParameter, chainId}: any) {
console.log("HEY")
const provider = await this.getProvider()

const chain = config.chains.find(x => x.id === chainId)
if (!chain) throw new SwitchChainError(new ChainNotConfiguredError())

try {
await provider.request({
method: "wallet_switchEthereumChain",
params: [{chainId: numberToHex(chainId)}],
})

return chain
} catch (err) {
// TODO: Error handling
throw new SwitchChainError(err as Error)
}
},
onAccountsChanged(accounts) {
if (accounts.length === 0) this.onDisconnect()
else
config.emitter.emit("change", {
accounts: accounts.map((x: any) => getAddress(x)),
})
},
onChainChanged(chain) {
const chainId = Number(chain)
config.emitter.emit("change", {chainId})
},
async onConnect(connectInfo) {
const accounts = await this.getAccounts()

// TODO: What to do if accounts is empty? not sure this is accurate
if (accounts.length === 0) return

const chainId = Number(connectInfo.chainId)
config.emitter.emit("connect", {accounts, chainId})

const provider = await this.getProvider()

if (connect) provider.removeListener("connect", connect)
connect = undefined

if (accountsChanged)
provider.removeListener("accountsChanged", accountsChanged)
accountsChanged = this.onAccountsChanged.bind(this)
provider.on("accountsChanged", accountsChanged)

if (chainChanged) provider.removeListener("chainChanged", chainChanged)
chainChanged = this.onChainChanged.bind(this)
provider.on("chainChanged", chainChanged)

if (disconnect) provider.removeListener("disconnect", disconnect)
disconnect = (error: Error) => {
throw new ProviderDisconnectedError(error)
}
provider.on("disconnect", disconnect)
},
// TODO: waht to do with error?
async onDisconnect(error) {
const provider = await this.getProvider()

config.emitter.emit("disconnect")

// Manage EIP-1193 event listeners
if (chainChanged) {
provider.removeListener("chainChanged", chainChanged)
chainChanged = undefined
}
if (disconnect) {
provider.removeListener("disconnect", disconnect)
disconnect = undefined
}
if (!connect) {
connect = this.onConnect.bind(this)
provider.on("connect", connect)
}
},
onDisplayUri(uri: string) {
config.emitter.emit("message", {type: "display_uri", data: uri})
},
}))
}
Loading

0 comments on commit 25205eb

Please sign in to comment.