@edgeandnode/graph-auth-kit v7.3.0
@edgeandnode/graph-auth-kit
Authentication Kit for connecting The Graph apps/dapps to a wallet.
Relies on/requires wagmi + viem for allowing users to connect their wallets and interact with the connected auth state context.
Exposes a simple useGraphAuthKit hook that lets the app open the connect modal, letting users connect to their wallet of choice (one of: injected/MetaMask, coinbase, WalletConnect, Safe). See integration example below.
Install
Currently, graph-auth-kit has a lot of peer deps needed to interact with the library. But this may change after testing. At the very least, wagmi, viem, @tanstack/react-query will be needed.
# with bun
bun add @edgeandnode/graph-auth-kit \
@edgeandnode/common \
@edgeandnode/gds \
@edgeandnode/go \
@emotion/react \
@tanstack/react-query \
cookies-next \
ethers@5.7.2 \
theme-ui \
viem@2.x \
wagmi
# with pnpm
pnpm add @edgeandnode/graph-auth-kit \
@edgeandnode/common \
@edgeandnode/gds \
@edgeandnode/go \
@emotion/react \
@tanstack/react-query \
cookies-next \
ethers@5.7.2 \
theme-ui \
viem@2.x \
wagmiUsage
Properties:
config-> REQUIRED. The instantiated wagmi config instance.queryClient-> OPTIONAL. TheGraphAuthKitProviderrenders aQueryClientProviderinstance. Pass in an instantiatedQueryClientto use, otherwise,GraphAuthKitwill instantiate its own.infuraKey-> REQUIREDgatewayApiKey-> OPTIONAL. A The Graph Gateway API key used to query Subgraphs published on the Graph Network. Used for ENS name resolution
Example
- Setup the
GraphAuthKitProvider.- Note you do not need to add the
ConnectModalcomponent, theGraphAuthKitProviderrenders it for us - Note you do not need to add the
QueryClientProviderasGraphAuthKitProviderinstantiates and renders it- you can pass a
QueryClientinstance to theGraphAuthKitProvider, otherwise, it will instantiate its own
- you can pass a
- Note you do not need to add the
// _app.tsx
import { QueryClient } from '@tanstack/react-query'
import { AppProps } from 'next'
import { useRef } from 'react'
import { createClient } from 'viem'
import { cookieStorage, createConfig, createStorage } from 'wagmi'
import { coinbaseWallet, injected, walletConnect } from 'wagmi/connectors'
import { AnalyticsProvider, GDSProvider } from '@edgeandnode/gds'
import {
AUTH_STORAGE_KEY,
buildInfuraHttpTransport,
chainIsSupportedChain,
DefChain,
GraphAuthKitProvider,
L1Chain,
L1ChainTestnet,
L2Chain,
L2ChainTestnet,
} from '@edgeandnode/graph-auth-kit'
import { FutureNextLink } from '../components/FutureNextLink'
import { Layout } from '../components/Layout'
const infuraKey = process.env.INFURA_KEY!
const walletConnectProjectID = process.env.WALLETCONNECT_PROJECT_ID!
const gatewayApiKey = process.env.GATEWAY_API_KEY
import '@edgeandnode/gds/style.css'
import '../app.css'
const config = createConfig({
chains: [L2Chain, L2ChainTestnet, L1Chain, L1ChainTestnet] as const,
ssr: typeof window !== 'undefined',
client(params) {
const chain = chainIsSupportedChain(params.chain) ? params.chain : DefChain
const transport = buildInfuraHttpTransport({
chain: chain.id,
infuraKey,
})
return createClient({
chain,
transport,
})
},
connectors: [
injected(),
coinbaseWallet({
appName: 'Boom',
}),
walletConnect({
projectId: env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID,
qrModalOptions: {
themeMode: 'dark',
},
}),
],
storage: createStorage({
storage: cookieStorage,
key: AUTH_STORAGE_KEY,
}),
})
declare module 'wagmi' {
interface Register {
config: typeof config
}
}
export default function App({ Component, router, pageProps }: AppProps) {
const queryClient = useRef<QueryClient>()
if (!queryClient.current) {
queryClient.current = new QueryClient({
defaultOptions: {
queries: {
// With SSR, we usually want to set some default staleTime
// above 0 to avoid refetching immediately on the client
staleTime: 60 * 1000,
},
},
})
}
return (
<GDSProvider clientRouter={router} clientLink={FutureNextLink} useLegacyTheme>
<AnalyticsProvider
app="EXPLORER"
clientRouter={router}
mixpanel={{
sdk: mixpanel,
token: process.env.MIXPANEL_TOKEN ?? null,
}}
googleAnalytics={{
sdk: googleAnalytics,
measurementId: process.env.GOOGLE_ANALYTICS_MEASUREMENT_ID ?? null,
}}
>
<GraphAuthKitProvider
config={config}
queryClient={queryClient.current}
infuraKey={infuraKey}
gatewayApiKey={gatewayApiKey}
>
<Layout>
<DefaultSeo {...defaultSEO} />
<Component {...pageProps} />
</Layout>
<ReactQueryDevtools initialIsOpen={false} />
</GraphAuthKitProvider>
</AnalyticsProvider>
</GDSProvider>
)
}- Usage in a component
// components/Layout.tsx
import { ReactNode } from 'react'
import { useAccountEffect, useEnsName } from 'wagmi'
import { Layout as GDSLayout, UserProfile } from '@edgeandnode/gds'
import { GlobalFooter, GlobalHeader, NPSForm } from '@edgeandnode/go'
import { L1Chain, useGraphAuthKitConnector, useGraphAuthKit, useGraphAuthKitAccount } from '@edgeandnode/graph-auth-kit/hooks'
import { NavDropDownMenu } from './NavDropDownMenu'
export function Layout({ children }: Readonly<{ children: ReactNode }>) {
const authkit = useGraphAuthKit()
const { address } = useGraphAuthKitAccount()
const { data: ens } = useEnsName({ address, blockTag: 'latest', chainId: L1Chain.id })
const connector = useGraphAuthKitConnector()
const walletConnected = Boolean(address)
useAccountEffect({
onConnect(data) {
console.log('user connected wallet', {data})
},
onDisconnect() {
console.log('user disconnected wallet')
},
})
return (
<GDSLayout
header={
<GlobalHeader
activeProduct="EXPLORER"
basePath="/explorer"
showSupportButton={walletConnected}
rigtContent={(defaultContent) => (
<>
{!walletConnected ?
<GlobalHeader.ConnectButton onClick={() => authkit.openConnectModal()} />
) : (
<UserProfile
ethereumAccount={address}
graphAccount={...}
ens={ens}
profileUrl={`/profile/${address}?view=Overview`}
profileUrlAs={`/profile/${address}?view=Overview`}
onConnect={() => authkit.openConnectModal()}
onDisconnect={() => authkit.disconnect()}
userDropDownMenu={<NavDropDownMenu />}
accountTag={connector === 'safe' ? 'multisig' : undefined}
/>
)}
</>
)}
/>
}
footer={
<>
<NPSForm sx={{ mb: Spacing['64px'] }} />
<GlobalFooter />
</>
}
>
{children}
</GDSLayout>
)
}Hooks
useGraphAuthKitAccount-> This is an override of the wagmi useAccount hook. The reason is, if the user connects with a multisig, the wagmi context is connected with the user-selected EoA, so all of the wagmi context hooks reference this EoA and not the Safe. This returns the wagmiUseAccountReturnTypebut theaddressandaddressesvalues include the entered Safe address. It also adds aneoaproperty that is the connected EoA address.useGraphAuthKitConnector-> The user selected wallet/connector option.useClientToEthersSigner-> Returns the ethers@5.x.x JsonRpcSigner or SafeEthersSigner if the user is connected via a multisiguseAuthAccount-> similar to theuseGraphAuthKitAccountbut requires the user to be authenticated and throws an error if notuseGraphAuthKitAccountEffect-> Override of the wagmi useAccountEffect hook that listens to account changes and the_enteredMultisigInfoon the inner context instance. If the account data changes, emits aonConnectoronDisconnectevent, but in theonConnectalso checks theuseGraphAuthKitInnerContext()._enteredMultisigInfovalue and if the user has connected via a multisig, returns the mutlsig as theaddressvalue and the connected EoA as theeoavalue.useGraphAuthKitWalletClient-> Override of the wagmi useWalletClient hook that returns the extended Safe viem actions if the user is connected to a multisig.useGraphAuthKitWriteContract-> Override of the wagmi useWriteContract hook that creates the transaction on the Safe if the user is connected via multisig; otherwise, returns theuseWriteContractmutation hook.
Components
Connected-> renders the passed inchildrenonly if the user is connected.
import { Connected } from '@edgeandnode/graph-auth-kit'
export function ShowMeConnected() {
return <Connected>{(account) => <div>Connected Wallet: {account.address}</div>}</Connected>
}Disconnected-> renders the passed inchildrenonly if the user id disconnected. Useful for rendering components like a "Connect Wallet" CTA that should only render if the user is not authenticated.
import { ExperimentalButton as Button } from '@edgeandnode/gds'
import { Disconnected, useGraphAuthKit } from '@edgeandnode/graph-auth-kit'
export function ShowMeDisconnected() {
const authkit = useGraphAuthKit()
return (
<Disconnected>
<Button variant="primary" onClick={() => authkit.openConnectModal()}>
Connect Wallet
</Button>
</Disconnected>
)
}SwitchChain-> Similar to theSwitchNetworkcomponent from GDS, but uses the wagmi useSwitchChain hook.
import { ExperimentalButton as Button } from '@edgeandnode/gds'
import { L2Chain, SwitchChain } from '@edgeandnode/graph-auth-kit'
export function LetMeSwitchChain() {
return <SwitchChain requestedChain={L2Chain} />
}References
8 months ago
9 months ago
10 months ago
10 months ago
9 months ago
9 months ago
11 months ago
10 months ago
9 months ago
9 months ago
9 months ago
11 months ago
11 months ago
10 months ago
10 months ago
9 months ago
11 months ago
11 months ago
11 months ago
11 months ago
10 months ago
11 months ago
9 months ago
11 months ago
6 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
10 months ago
11 months ago
11 months ago
5 months ago
11 months ago
9 months ago
11 months ago
11 months ago
11 months ago
11 months ago
11 months ago
10 months ago
10 months ago
9 months ago
12 months ago
12 months ago
12 months ago
12 months ago
11 months ago
11 months ago
10 months ago
11 months ago
10 months ago
10 months ago
9 months ago
8 months ago
11 months ago
8 months ago
10 months ago
8 months ago
9 months ago
9 months ago
9 months ago
9 months ago
10 months ago
10 months ago
8 months ago
8 months ago
8 months ago
7 months ago
10 months ago
5 months ago
6 months ago
6 months ago
6 months ago
10 months ago
9 months ago
8 months ago
5 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
9 months ago
9 months ago
7 months ago
6 months ago
7 months ago
5 months ago
5 months ago
11 months ago
10 months ago
9 months ago
12 months ago
12 months ago
12 months ago
12 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago