0xtrails
Version:
SDK for Trails
407 lines (375 loc) • 14.6 kB
text/typescript
import {
getSequenceProjectAccessKey,
getWalletConnectProjectId,
} from "./config.js"
import WalletConnectLogo from "./widget/assets/WalletConnect-logo-blue-bg.svg"
import { Wallet as WalletIcon } from "lucide-react"
import {
injected,
walletConnect,
safe,
baseAccount,
gemini,
} from "wagmi/connectors"
import { useConnect } from "wagmi"
import React from "react"
import { sequenceWallet } from "@0xsequence/connect"
type Connector = any
export interface WalletConfig {
id: string
name: string
icon: string
connector?: Connector
}
// WalletConnect connector singleton to avoid multiple Core initializations
let wcConnectorSingleton: ReturnType<typeof walletConnect> | null = null
function getWalletConnectConnectorSingleton() {
if (!wcConnectorSingleton) {
wcConnectorSingleton = walletConnect({
projectId: getWalletConnectProjectId(),
showQrModal: false,
})
}
return wcConnectorSingleton
}
export const walletConnectConnector: Connector =
getWalletConnectConnectorSingleton()
export const injectedConnector = injected()
export const baseAccountConnector: Connector = baseAccount()
export const geminiConnector: Connector = gemini()
function isInIframe() {
// check if we're in an iframe
if (window?.parent === window) {
return false
}
return true
}
function safeProviderOrWalletConnect() {
if (isInIframe()) {
return safe()
}
return walletConnectConnector
}
export function windowProviderOrWalletConnect(
walletId: string,
windowObjectIdentifier: string | string[],
name: string,
) {
if (typeof window !== "undefined") {
let provider: any
if (Array.isArray(windowObjectIdentifier)) {
// Handle array case: ["phantom", "ethereum"] -> window.phantom.ethereum
provider = windowObjectIdentifier.reduce(
(obj, key) => obj?.[key],
window as any,
)
} else {
// Handle string case: "phantom" -> window.phantom
provider = (window as any)[windowObjectIdentifier]
}
if (provider) {
return injected({
target() {
return {
id: walletId,
name: name || walletId,
provider: provider,
}
},
})
}
}
return walletConnectConnector
}
export const metamaskConnector = injected({
target() {
return {
id: "metamask",
name: "metamask",
provider:
typeof window !== "undefined" ? (window as any).ethereum : undefined,
}
},
})
export const rainbowConnector = windowProviderOrWalletConnect(
"rainbow",
"rainbow",
"Rainbow",
)
export const trustwalletConnector = windowProviderOrWalletConnect(
"trustwallet",
"trustwallet",
"Trust",
)
export const phantomConnector = windowProviderOrWalletConnect(
"phantom",
["phantom", "ethereum"],
"Phantom",
)
export const okxwalletConnector = windowProviderOrWalletConnect(
"okxwallet",
"okxwallet",
"OKX Wallet",
)
export const zerionConnector = windowProviderOrWalletConnect(
"zerion",
"zerionWallet",
"Zerion",
)
export const ambireConnector = windowProviderOrWalletConnect(
"ambire",
"ambire",
"Ambire",
)
export const rabbyConnector = windowProviderOrWalletConnect(
"rabby",
"rabby",
"Rabby",
)
export const coinbaseWalletConnector = windowProviderOrWalletConnect(
"coinbase",
"coinbaseWalletExtension",
"Coinbase Wallet",
)
export const sequenceConnector: any = sequenceWallet({
connect: {
app: "Trails",
projectAccessKey: getSequenceProjectAccessKey(),
},
defaultNetwork: 1,
walletAppURL: "https://sequence.app",
})
export const safeConnector: Connector = safeProviderOrWalletConnect()
export const connectors = [
injectedConnector,
walletConnectConnector,
safeConnector,
baseAccountConnector,
geminiConnector,
coinbaseWalletConnector,
metamaskConnector,
rainbowConnector,
trustwalletConnector,
phantomConnector,
okxwalletConnector,
zerionConnector,
ambireConnector,
rabbyConnector,
]
export const WALLET_CONFIGS: Record<string, WalletConfig> = {
injected: {
id: "injected",
name: "Browser Wallet",
connector: injectedConnector,
icon: WalletIcon as unknown as string,
},
walletconnect: {
id: "walletconnect",
name: "WalletConnect",
connector: walletConnectConnector,
icon: WalletConnectLogo as string,
},
metamask: {
id: "metamask",
name: "MetaMask",
connector: metamaskConnector,
icon: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAiIGhlaWdodD0iMzAiIHZpZXdCb3g9IjAgMCAzMCAzMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjIzIiBoZWlnaHQ9IjIzIiB4PSIzLjUiIHk9IjMuNSIgdmlld0JveD0iMCAwIDE0MS41MSAxMzYuNDIiPjxwYXRoIGZpbGw9IiNGRjVDMTYiIGQ9Im0xMzIuMjQgMTMxLjc1LTMwLjQ4LTkuMDctMjIuOTkgMTMuNzQtMTYuMDMtLjAxLTIzLTEzLjc0LTMwLjQ3IDkuMDhMMCAxMDAuNDdsOS4yNy0zNC43M0wwIDM2LjQgOS4yNyAwbDQ3LjYgMjguNDRoMjcuNzZMMTMyLjI0IDBsOS4yNyAzNi4zOC05LjI3IDI5LjM2IDkuMjcgMzQuNzItOS4yNyAzMS4zWiIvPjxwYXRoIGZpbGw9IiNGRjVDMTYiIGQ9Im05LjI3IDAgNDcuNjEgMjguNDZMNTQuOTggNDggOS4yOSAwWm0zMC40NyAxMDAuNDggMjAuOTUgMTUuOTUtMjAuOTUgNi4yNHYtMjIuMlpNNTkuMDEgNzQuMSA1NSA0OCAyOS4yMiA2NS43NWgtLjAybC4wOCAxOC4yNyAxMC40NS05LjkyaDE5LjI5Wk0xMzIuMjUgMGwtNDcuNiAyOC40Nkw4Ni41MSA0OGw0NS43Mi00OFptLTMwLjQ3IDEwMC40OC0yMC45NCAxNS45NSAyMC45NCA2LjI0di0yMi4yWm0xMC41My0zNC43M0w4Ni41MyA0OCA4Mi41IDc0LjFoMTkuMjdsMTAuNDYgOS45LjA3LTE4LjI2WiIvPjxwYXRoIGZpbGw9IiNFMzQ4MDciIGQ9Im0zOS43MyAxMjIuNjctMzAuNDYgOS4wOEwwIDEwMC40OGgzOS43M3YyMi4yWk01OS4wMiA3NC4xbDUuODIgMzcuNzEtOC4wNy0yMC45Ny0yNy40OS02LjgyIDEwLjQ2LTkuOTJINTlabTQyLjc2IDQ4LjU5IDMwLjQ3IDkuMDcgOS4yNy0zMS4yN2gtMzkuNzR6TTgyLjUgNzQuMDlsLTUuODIgMzcuNzEgOC4wNi0yMC45NyAyNy41LTYuODItMTAuNDctOS45MnoiLz48cGF0aCBmaWxsPSIjRkY4RDVEIiBkPSJtMCAxMDAuNDcgOS4yNy0zNC43M0gyOS4ybC4wNyAxOC4yNyAyNy41IDYuODIgOC4wNiAyMC45Ny00LjE1IDQuNjItMjAuOTQtMTUuOTZIMFptMTQxLjUgMC05LjI2LTM0LjczaC0xOS45M2wtLjA3IDE4LjI3LTI3LjUgNi44Mi04LjA2IDIwLjk3IDQuMTUgNC42MiAyMC45NC0xNS45NmgzOS43NFpNODQuNjQgMjguNDRINTYuODhsLTEuODkgMTkuNTQgOS44NCA2My44aDExLjg1bDkuODUtNjMuOC0xLjktMTkuNTRaIi8+PHBhdGggZmlsbD0iIzY2MTgwMCIgZD0iTTkuMjcgMCAwIDM2LjM4bDkuMjcgMjkuMzZIMjkuMkw1NC45OCA0OHptNDMuOTggODEuNjdoLTkuMDNsLTQuOTIgNC44MSAxNy40NyA0LjMzLTMuNTItOS4xNVpNMTMyLjI0IDBsOS4yNyAzNi4zOC05LjI3IDI5LjM2aC0xOS45M0w4Ni41MyA0OHpNODguMjcgODEuNjdoOS4wNGw0LjkyIDQuODItMTcuNDkgNC4zNCAzLjUzLTkuMTdabS05LjUgNDIuMyAyLjA2LTcuNTQtNC4xNS00LjYySDY0LjgybC00LjE0IDQuNjIgMi4wNSA3LjU0Ii8+PHBhdGggZmlsbD0iI0MwQzRDRCIgZD0iTTc4Ljc3IDEyMy45N3YxMi40NUg2Mi43NHYtMTIuNDVoMTYuMDJaIi8+PHBhdGggZmlsbD0iI0U3RUJGNiIgZD0ibTM5Ljc0IDEyMi42NiAyMyAxMy43NnYtMTIuNDZsLTIuMDUtNy41NHptNjIuMDMgMC0yMyAxMy43NnYtMTIuNDZsMi4wNi03LjU0eiIvPjwvc3ZnPjwvc3ZnPg==",
},
baseAccount: {
id: "baseAccount",
name: "Base",
connector: baseAccountConnector,
icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAALZJREFUaEPtmjEOhDAMBNc/O14GvOzys3CAKK6eAlmaVGl2Zc+kTOU685vkc9/bnD2prZK5/TZY24z9P+g4F5hNh7/GdoG37WlAA5CATwgCxHENYISwQAMQII5rACOEBRqAAHFcAxghLNAABIjjGsAIYYEGIEAc1wBGCAs0AAHiuAYwQligAQgQxzWAEcICDUCAOK4BjBAWaAACxHENYISwQAMQII6fBjr+VHkW3+u+tfyxMpJaDgYzYxb/ALZVAAAAAElFTkSuQmCC",
},
safe: {
id: "safe",
name: "Safe",
connector: safeConnector,
icon: "https://explorer-api.walletconnect.com/v3/logo/sm/3913df81-63c2-4413-d60b-8ff83cbed500?projectId=34357d3c125c2bcf2ce2bc3309d98715",
},
rainbow: {
id: "rainbow",
name: "Rainbow",
connector: rainbowConnector,
icon: "https://explorer-api.walletconnect.com/v3/logo/sm/7a33d7f1-3d12-4b5c-f3ee-5cd83cb1b500?projectId=34357d3c125c2bcf2ce2bc3309d98715",
},
trust: {
id: "trust",
name: "Trust",
connector: trustwalletConnector,
icon: "https://explorer-api.walletconnect.com/v3/logo/sm/7677b54f-3486-46e2-4e37-bf8747814f00?projectId=34357d3c125c2bcf2ce2bc3309d98715",
},
ambire: {
id: "ambire",
name: "Ambire Wallet",
connector: ambireConnector,
icon: "https://explorer-api.walletconnect.com/v3/logo/sm/c39b3a16-1a38-4588-f089-cb7aeb584700?projectId=34357d3c125c2bcf2ce2bc3309d98715",
},
argent: {
id: "argent",
name: "Argent",
connector: walletConnectConnector,
icon: "https://explorer-api.walletconnect.com/v3/logo/sm/215158d2-614b-49c9-410f-77aa661c3900?projectId=34357d3c125c2bcf2ce2bc3309d98715",
},
binance: {
id: "binance",
name: "Binance Wallet",
connector: walletConnectConnector,
icon: "https://explorer-api.walletconnect.com/v3/logo/sm/ebac7b39-688c-41e3-7912-a4fefba74600?projectId=34357d3c125c2bcf2ce2bc3309d98715",
},
okx: {
id: "okx",
name: "OKX Wallet",
connector: okxwalletConnector,
icon: "https://explorer-api.walletconnect.com/v3/logo/sm/c55df831-3c52-49fc-d1d1-97a926dc0c00?projectId=34357d3c125c2bcf2ce2bc3309d98715",
},
zerion: {
id: "zerion",
name: "Zerion",
connector: zerionConnector,
icon: "https://explorer-api.walletconnect.com/v3/logo/sm/73f6f52f-7862-49e7-bb85-ba93ab72cc00?projectId=34357d3c125c2bcf2ce2bc3309d98715",
},
phantom: {
id: "phantom",
name: "Phantom",
connector: phantomConnector,
icon: "https://explorer-api.walletconnect.com/v3/logo/sm/b6ec7b81-bb4f-427d-e290-7631e6e50d00?projectId=34357d3c125c2bcf2ce2bc3309d98715",
},
coinbase: {
id: "coinbase",
name: "Coinbase Wallet",
connector: coinbaseWalletConnector,
icon: "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PScwIDAgMTAyNCAxMDI0JyBmaWxsPSdub25lJyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHN0eWxlPSdoZWlnaHQ6MjhweDt3aWR0aDoyOHB4Jz48cmVjdCB3aWR0aD0nMTAyNCcgaGVpZ2h0PScxMDI0JyBmaWxsPScjMDA1MkZGJyByeD0nMTAwJyByeT0nMTAwJz48L3JlY3Q+PHBhdGggZmlsbC1ydWxlPSdldmVub2RkJyBjbGlwLXJ1bGU9J2V2ZW5vZGQnIGQ9J00xNTIgNTEyQzE1MiA3MTAuODIzIDMxMy4xNzcgODcyIDUxMiA4NzJDNzEwLjgyMyA4NzIgODcyIDcxMC44MjMgODcyIDUxMkM4NzIgMzEzLjE3NyA3MTAuODIzIDE1MiA1MTIgMTUyQzMxMy4xNzcgMTUyIDE1MiAzMTMuMTc3IDE1MiA1MTJaTTQyMCAzOTZDNDA2Ljc0NSAzOTYgMzk2IDQwNi43NDUgMzk2IDQyMFY2MDRDMzk2IDYxNy4yNTUgNDA2Ljc0NSA2MjggNDIwIDYyOEg2MDRDNjE3LjI1NSA2MjggNjI4IDYxNy4yNTUgNjI4IDYwNFY0MjBDNjI4IDQwNi43NDUgNjE3LjI1NSAzOTYgNjA0IDM5Nkg0MjBaJyBmaWxsPSd3aGl0ZSc+PC9wYXRoPjwvc3ZnPg==",
},
rabby: {
id: "rabby",
name: "Rabby",
connector: rabbyConnector,
icon: "https://explorer-api.walletconnect.com/v3/logo/sm/255e6ba2-8dfd-43ad-e88e-57cbb98f6800?projectId=34357d3c125c2bcf2ce2bc3309d98715",
},
privy: {
id: "privy",
name: "Privy",
icon: "https://avatars.githubusercontent.com/u/81824329?s=200&v=4",
},
gemini: {
id: "gemini",
name: "Gemini",
connector: geminiConnector,
icon: "https://explorer-api.walletconnect.com/v3/logo/sm/56a3fd87-2627-4903-fddd-205224dac500?projectId=34357d3c125c2bcf2ce2bc3309d98715",
},
sequence: {
id: "sequence",
name: "Sequence",
connector: sequenceConnector,
icon: "https://sequence.app/icon.svg",
},
}
export const topShownWallets = [
"metamask",
"walletconnect",
"baseAccount",
"rainbow",
"phantom",
]
export const getWalletIcon = (walletId?: string): string => {
if (!walletId) return ""
return (WALLET_CONFIGS[walletId]?.icon || "") as string
}
export const getWalletName = (walletId?: string): string => {
if (!walletId) return ""
return (WALLET_CONFIGS[walletId]?.name || "") as string
}
export const wagmiConnectorToWalletId = (wagmiConnector: any): string => {
const wagmiConnectorName = (
wagmiConnector?.name ||
wagmiConnector?.id ||
""
).toLowerCase()
if (wagmiConnectorName.includes("rabby")) return "rabby"
if (wagmiConnectorName.includes("metamask")) return "metamask"
if (wagmiConnectorName.includes("walletconnect")) return "walletconnect"
if (wagmiConnectorName.includes("coinbase")) return "coinbase"
if (wagmiConnectorName.includes("base")) return "baseAccount"
if (wagmiConnectorName.includes("rainbow")) return "rainbow"
if (wagmiConnectorName.includes("trust")) return "trust"
if (wagmiConnectorName.includes("ambire")) return "ambire"
if (wagmiConnectorName.includes("argent")) return "argent"
if (wagmiConnectorName.includes("okx")) return "okxwallet"
if (wagmiConnectorName.includes("privy")) return "privy"
if (wagmiConnectorName.includes("gemini")) return "gemini"
if (wagmiConnectorName.includes("sequence")) return "sequence"
return wagmiConnector.id || "injected"
}
// Hook to get combined wallets from WALLET_CONFIGS and wagmi connectors
export const useWallets = () => {
const { connectors } = useConnect()
const allWallets = React.useMemo(() => {
// Get config wallets that have connectors
const configWallets = Object.values(WALLET_CONFIGS)
.filter((config) => config.connector)
.map((config) => ({
id: config.id,
name: config.name,
icon: config.icon,
connector: config.connector,
source: "config" as const,
}))
// Get wagmi wallets
const wagmiWallets =
connectors?.map((connector: any) => ({
id: wagmiConnectorToWalletId(connector),
name: connector.name || "",
icon:
getWalletIcon(wagmiConnectorToWalletId(connector)) ||
connector.icon ||
"",
connector: connector,
source: "wagmi" as const,
})) || []
// Combine and deduplicate, prioritizing wagmi over config
const allWallets: any[] = []
for (const wallet of configWallets) {
if (!wallet.connector) {
wallet.connector = connectors?.find(
(connector: any) => wagmiConnectorToWalletId(connector) === wallet.id,
)
if (!wallet.connector) {
continue
}
}
allWallets.push({
id: wallet.id,
name: wallet.name,
icon: wallet.icon,
connector: wallet.connector,
source: "config" as const,
})
}
for (const wallet of wagmiWallets) {
if (
!allWallets.find((w: any) => w.id === wagmiConnectorToWalletId(wallet))
) {
const configWallet = configWallets.find(
(config: any) => config.id === wallet.id,
)
allWallets.push({
id: wallet.id,
name: configWallet?.name || wallet.name,
icon: configWallet?.icon || wallet.icon,
connector: wallet.connector,
source: "wagmi" as const,
})
}
}
return allWallets
}, [connectors])
return {
wallets: allWallets,
}
}
// Helper method to find wallet by ID using the hook (for use in components)
export const useFindWalletById = (walletId: string) => {
const { wallets } = useWallets()
return wallets.find((wallet: any) => wallet.id === walletId) || null
}