Connecting wallet
- EIP1193
- EthersJS
- Wagmi
Passport is a valid EIP-1193 provider with support for all major RPC methods.
To create an EIP-1193 Passport provider all you need to do is call passportInstance.connectEvm()
after initialising Passport.
// fetch the Passport provider from the Passport instance
const passportProvider = passportInstance.connectEvm();
Once a EIP-1193 provider is created, you can call various methods on it via the request
function (which is protocol-agnostic), for example:
// calling eth_requestAccounts triggers the Passport login flow
const accounts = await passportProvider.request({ method: 'eth_requestAccounts' });
Calling the eth_requestAccounts
request method will trigger the login flow for Passport if the user is not signed in.
Checkout the full working example here: Passport with NextJS Connect with EIP1193.
You can convert Passport's native EIP-1193 Provider into an EthersJS compatible provider such as a Web3Provider
.
To create the Web3Provider
first fetch the Passport provider by calling passportInstance.connectEvm()
after initialising Passport. Then create a new EtherJS Web3Provider
and pass the Passport provider into the constructor.
// fetch the Passport provider from the Passport instance
const passportProvider = passportInstance.connectEvm();
// create the Web3Provider using the Passport provider
const web3Provider = new ethers.providers.Web3Provider(passportProvider);
Once the Web3Provider
is created, you can call various methods on it via the request
function (which is protocol-agnostic), for example:
// calling eth_requestAccounts triggers the Passport login flow
const accounts = await web3Provider.provider.request({ method: 'eth_requestAccounts' });
Calling the eth_requestAccounts
request method will trigger the login flow for Passport if the user is not signed in and will connect the Web3Provider
to the Passport instance.
Checkout the full working example here: Passport with NextJS Connect with EtherJS.
To integrate Passport with wagmi
, thanks to the EIP-1193 standard, you can use the inject
plugin.
Below is the approach taken in our Code Example Passport with NextJS Connect with Wagmi.
Generally Wagmi implementation is well described in their guide Wagmi Connect Wallet but the main differences are pointed out below.
Create the Wagmi Config for Immutable zkEVM Testnet and set injected()
as the connector.
import { createConfig, http } from 'wagmi';
import { immutableZkEvmTestnet } from 'wagmi/chains';
import { injected } from 'wagmi/connectors';
// create the Wagmi config for Immutable zkEVM Testnet
export const config = createConfig({
chains: [immutableZkEvmTestnet],
connectors: [injected()],
transports: {
[immutableZkEvmTestnet.id]: http(),
},
});
Create a <WalletOptions>
component and filter the standard Wagmi connectors
to show only the Immutable Passport connector.
The component displays a button for the user to login to passport with and handles calling Wagmi's connect()
function on click, passing the connector object which in this case will be Passport.
import { useState, useEffect } from 'react';
import { Connector, useConnect } from 'wagmi';
import { Button } from '@biom3/react';
export function WalletOptions() {
// get the available connectors and the connect function from Wagmi
const { connectors, connect } = useConnect();
// setup the filtered connectors state
const [filteredConnectors, setFilteredConnectors] = useState<Connector[]>([]);
// setup the loading state to enable/disable buttons when loading
const [loading, setLoadingState] = useState<boolean>(true);
useEffect(() => {
if (!connectors) return;
// filter the available connectors to show only Passport
setFilteredConnectors(connectors.filter((connector) => connector.name.includes('Immutable Passport')));
// enable button when loading has finished
setLoadingState(false);
}, [connectors]);
function passportLogin(connector:Connector) {
// disable button while loading
setLoadingState(true);
// connect Wagmi to Passport
connect({ connector });
}
// render the view to show login
return (
<>
{filteredConnectors.map((connector) => (
<>
<p>Connect with:</p>
<Button
className="mb-1"
key={connector.uid}
type="button"
onClick={() => passportLogin(connector)}
disabled={loading}
>
{connector.name}
</Button>
</>
))}
{loading && (
<>
<p>Loading...</p>
</>
)}
</>
);
}
Create an <Account>
component that shows the connected users account address and provides a way for them to logout. It's important to call Wagami's disconnect()
function and also await passportInstance.logout()
when the user clicks the Passport Logout button to both disconenct Wagmi from Passport and also log the user out of Passport.
import {
useAccount, useDisconnect, useEnsName,
} from 'wagmi';
import { useState } from 'react';
import { passportInstance } from '../utils/passport';
import { Button, Table } from '@biom3/react';
export function Account() {
const { address } = useAccount();
const { disconnect } = useDisconnect();
const { data: ensName } = useEnsName({ address });
// setup the loading state to enable/disable buttons when loading
const [loading, setLoadingState] = useState<boolean>(false);
const passportLogout = async () => {
// disable button while loading
setLoadingState(true);
// disconnect Wagmi from Passport
disconnect();
// logout from Passport
await passportInstance.logout();
};
// render the view to show the connected accounts and logout
return (
<>
<Button
onClick={() => passportLogout()}
disabled={loading}
type="button"
>
Passport Logout
</Button>
<Table>
<Table.Head>
<Table.Row>
<Table.Cell>Item</Table.Cell>
<Table.Cell>Value</Table.Cell>
</Table.Row>
</Table.Head>
<Table.Body>
<Table.Row>
<Table.Cell><b>Connected Account</b></Table.Cell>
<Table.Cell>
{!address && (
<span>(not connected)</span>
)
}
{address && (
<span>{ensName ? `${ensName} (${address})` : address}</span>
)}
</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
<br />
</>
);
}
The ConnectWallet
component simply handles showing the <WalletOptions>
component if the user is not logged in, or the <Account>
component if they are logged in.
import { useAccount } from 'wagmi';
import { WalletOptions } from './wallet-options';
import { Account } from './account';
// show wallet options for login or the logged in account details
// depending on whether the account is connected or not
export function ConnectWallet() {
const { isConnected } = useAccount();
if (isConnected) return <Account />;
return <WalletOptions />;
}
This is the main component which pulls everything together. It imports the Wagmi config
and parses it to the <WagmiProvider>
. It initalises the queryClient
and parses it to the <QueryClientProvider>
.
Before the Providers are rendered it's required to run passportInstance.conectEVM()
to make the Passport provider available to Wagmi.
'use client';
import { WagmiProvider } from 'wagmi';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { passportInstance } from '../utils/passport';
import { config } from './config';
import { ConnectWallet } from './connect';
import { Heading, Link } from '@biom3/react';
import NextLink from 'next/link';
// initialise the QueryClient for the Provider
const queryClient = new QueryClient();
export default function ConnectWithWagmi() {
// calling connectEVM() makes Passport available as an option to Wagmi
passportInstance.connectEvm();
// render the ConnectWallet component
// wrapping it in the Wagami and QueryClient Providers
return (
<>
<Heading className="mb-1">Passport Connect with Wagmi</Heading>
<WagmiProvider config={config}>
<QueryClientProvider client={queryClient}>
<ConnectWallet />
</QueryClientProvider>
</WagmiProvider>
<Link rc={<NextLink href="/" />}>Return to Examples</Link>
</>
);
}
Checkout the full working example here: Passport with NextJS Connect with Wagmi.
Provider Events
As per the EIP-1193 specification, the zkEVM provider exposes an on
and removeListener
function,
which can be used to subscribe to and unsubscribe from events emitted by the zkEVM provider
Supported Events
Accounts Changed
The zkEVM provider will emit an accountsChanged event when
the accounts available to the provider changes (for example, when eth_requestAccounts
is called and the users wallet is initialised):
// listen to the ACCOUNTS_CHANGED event and update the accounts state when it changes
passportProvider.on(ProviderEvent.ACCOUNTS_CHANGED, (accounts: string[]) => {
setAccountsState(accounts);
});