Estimated reading time: More than 10 minutes
Before you start
Types: Custom or Standard Library
To get started integrating a Ledger button into your DApp choose either:
-
Custom: Install @ledgerhq/connect-kit-loader using Yarn
or NPM
to use the DApp Connect Kit API
-
Library: Use a popular wrapper library such as, Web3Modal, wagmi, or (COMING SOON) Web3-react, Web3-Onboard, or RainbowKit to easily integrate multiple wallets.
UX Guidelines
- If you already have a “Connect your App” implementation or a WalletConnect Ledger button, remove them so there is only one Ledger button
- Label the connection as “Ledger” for clarity to the users
- In this folder you will find Ledger’s icon to display in your app
- Have Ledger appear above the fold for users to discover without clicking on “Show More”
Custom Integration
Setup
You just have to add the @ledgerhq/connect-kit-loader
package as a dependency using your preferred package manager.
Using yarn as an example
yarn add @ledgerhq/connect-kit-loader
and use it as below
import { loadConnectKit } from '@ledgerhq/connect-kit-loader'
// ...
const connectKit = await loadConnectKit()
If you are using classes you can get the promise on the class constructor and its value where you need it
import { loadConnectKit } from '@ledgerhq/connect-kit-loader'
export class LedgerConnector {
private provider: any
private connectKitPromise: Promise<LedgerConnectKit>
constructor() {
super()
this.connectKitPromise = loadConnectKit()
}
public async connect(): Promise<Connector> {
if (!this.provider) {
// load Connect Kit, check support and show a UI modal if not supported
const connectKit = await this.connectKitPromise
API functions
Connect Kit provides three functions, enableDebugLogs
, checkSupport
and getProvider
.
enableDebugLogs
Description
Enables debug messages on the console in case you need to diagnose a possible problem.
Once Connect Kit is loaded you can call it from the browser’s developer tools console with window.ledgerConnectKit.enableDebugLogs()
and call the other Connect Kit functions to see the messages. Just reload the DApp to disable them.
checkSupport
Description
Returns the level of support for Ledger Extension on the user’s platform, and shows a UI to guide the user.
- Case A - If the Ledger Extension is Compatible & Installed & Enabled (by the user, once and for all), you will just get the response and no UI will be shown.
Otherwise (when the Ledger Extension is not Compatible/Installed/Enabled), one of these modals will be shown on the user’s platform:
- Case B - if the user’s platform supports the Ledger Extension but it is not Installed or Enabled; a link will be shown to guide the user to Install and Enable the Ledger Extension;
- Cases C/D - Connect with Ledger Live option. When the user’s platform does not support Ledger Extension you will connect via Ledger Live Mobile or Desktop.
Parameters
type CheckSupportOptions = {
providerType: SupportedProviders;
chainId?: ConnectSupportedChains;
bridge?: string;
infuraId?: string;
rpc: { [chainId: number]: string; };
}
Returns
type ConnectSupport = type CheckSupportResult = {
isLedgerConnectSupported?: boolean;
isLedgerConnectEnabled?: boolean;
isChainIdSupported?: boolean;
providerImplementation: 'LedgerConnect' | 'WalletConnect';
}
getProvider
Description
Based on the options passed to checkSupport
it returns a promise to a Ledger Extension
Ethereum provider or a WalletConnect provider.
Returns
Promise<EthereumProvider>
Example
An example function using the DApp Connect Kit and ethers.js, called when pressing the Ledger button, written in React.
setProvider
, setLibrary
, setAccount
, setChainId
and setError
are just simple useState
functions to store the app state.
// JSX code
<Button onClick={() => connectWallet()}>Connect With Ledger</Button>
const connectWallet = async () => {
try {
const connectKit = await loadConnectKit();
const checkSupportResult = connectKit.checkSupport({
chainId: 1,
providerType: SupportedProviders.Ethereum,
rpc: {
1: "https://cloudflare-eth.com/", // Mainnet
137: "https://polygon-rpc.com/", // Polygon
}
});
console.log('checkSupportResult is', checkSupportResult);
const provider = await connectKit.getProvider();
setProvider(provider);
const accounts = await provider.request({ method: 'eth_requestAccounts' });
if (accounts) setAccount(accounts[0]);
const library = new ethers.providers.Web3Provider(provider);
setLibrary(library);
const network = await library.getNetwork();
setChainId(network.chainId);
} catch (error) {
setError(error);
}
}
Standard library Integration
Web3Modal
You just have to add the packages using your preferred package manager.
Using yarn as an example:
yarn add @ledgerhq/connect-kit-loader web3modal
and use it as below
import { loadConnectKit } from '@ledgerhq/connect-kit-loader';
import { ethers } from "ethers";
import Web3Modal from "web3modal";
const providerOptions = {
ledger: {
package: loadConnectKit, // required
chainId: 1, // defaults to 1
infuraId: "INFURA_ID" // required if no rpc
rpc: { // required if no infuraId
1: "INSERT_MAINNET_RPC_URL",
137: "INSERT_POLYGON_RPC_URL",
// ...
}
}
};
const web3Modal = new Web3Modal({
providerOptions // required
});
An example function using the DApp Connect Kit, Web3Modal and ethers.js, that would be called when pressing the Ledger button, written in React.
setProvider
, setLibrary
, setAccount
, setChainId
and setError
are just simple useState
functions to keep the app state.
const connectWallet = async () => {
try {
const provider = await web3Modal.connect();
const library = new ethers.providers.Web3Provider(provider);
const accounts = await library.listAccounts();
const network = await library.getNetwork();
setProvider(provider);
setLibrary(library);
if (accounts) setAccount(accounts[0]);
setChainId(network.chainId);
} catch (error) {
setError(error);
}
};
JSX code
<Button onClick={connectWallet}>Connect Wallet</Button>
Web3-Onboard
You just have to add the packages using your preferred package manager.
Using yarn as an example:
yarn add @web3-onboard/core @web3-onboard/ledger
Use it as shown below:
import { useState } from "react";
import Onboard from "@web3-onboard/core";
import ledgerModule from '@web3-onboard/ledger';
const ledger = ledgerModule({
chainId: 1,
rpc: {
1: `https://cloudflare-eth.com/`, // Mainnet
5: 'https://goerli.optimism.io', // Goerli
137: "https://polygon-rpc.com/", // Polygon
}
});
const onboard = Onboard({
Wallets: [ledger],
Chains: [ /*...*/ ],
});
export default function App() {
const [provider, setProvider] = useState();
const [account, setAccount] = useState();
const [error, setError] = useState();
const connectWallet = async () => {
try {
const wallets = await onboard.connectWallet();
if (wallets.length) {
const { accounts, provider } = wallets[0];
if (accounts.length) {
setAccount(accounts[0].address);
}
setProvider(provider);
};
} catch(err) {
setError(err?.message || err);
}
};
const switchAccount = (accounts) => {
setAccount(accounts[0]);
};
const disconnectWallet = async () => {
try {
const [primaryWallet] = onboard.state.get().wallets;
if (!primaryWallet) return;
await onboard.disconnectWallet({ label: primaryWallet.label });
setAccount();
setProvider();
} catch (err) {
setError(err?.message || err);
}
};
useEffect(() => {
if (provider?.on) {
provider.on("disconnect", disconnectWallet);
provider.on("accountsChanged", switchAccount);
return () => {
if (provider.removeListener) {
provider.removeListener("disconnect", disconnectWallet);
provider.removeListener("accountsChanged", switchAccount);
}
};
}
}, [provider]);
return (
<>
<div>Status: {account
? (<span>Connected</span>)
: (<span>Not connected</span>)
}</div>
{!account ? (
<Button onClick={connectWallet}>Connect Wallet</Button>
) : (
<Button onClick={disconnectWallet}>Disconnect</Button>
)}
<div>{error ? error.message : null}</div>
</>
);
}
For more information take a look at the web3-onboard documentation.
wagmi
To use the Ledger wagmi connector you need a recent version of the @wagmi/connectors
package. Using the yarn package manager as an example:
yarn add wagmi @wagmi/connectors
Setup the wagmi client and the Ledger connector with:
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { WagmiConfig, createClient, configureChains, mainnet } from 'wagmi';
import { LedgerConnector } from '@wagmi/connectors/ledger';
import App from './App';
const { chains, provider, webSocketProvider } = configureChains(
[mainnet],
[publicProvider()],
);
const client = createClient({
autoConnect: true,
provider,
webSocketProvider,
connectors: {
ledger: new LedgerConnector({
// chains supported by app
chains: [mainnet],
options: {
// enable debug messages, default is false
enableDebugLogs: false,
// chain id of the connecting chain, passed to WalletConnect
chainId: 1,
// a chainId / RPC URL map of the supported chains, passed to WalletConnect
rpc: {
1: 'https://cloudflare-eth.com/', // Mainnet
137: 'https://polygon-rpc.com/', // Polygon
}
}
}),
// other connectors
},
});
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
root.render(
<StrictMode>
<WagmiConfig client={client}>
<App />
</WagmiConfig>
</StrictMode>
);
Manage the DApp state using wagmi hooks and use it to decide what to show to the user:
import { useAccount, useConnect, useDisconnect, useNetwork } from 'wagmi'
export default function App() {
const { disconnect } = useDisconnect();
const { connector, address } = useAccount();
const { chain } = useNetwork();
const { connect, connectors } = useConnect();
return (
<>
<div>Status: {connector
? (<span>Connected</span>)
: (<span>Not connected</span>)
}</div>
{connector && (
<>
<div>Network Id: {chain ? `${chain.id (${chain.name})` : "none"}}
</div>
<div>Account: {address ? address : 'none'}</div>
</>
)}
<div>
{!connector ? (
<Button onClick={() => {
connect({ connector: connectors['ledger'] })
}}>Connect Ledger Wallet</Button>
) : (
<Button onClick={() => disconnect()}>Disconnect</Button>
)}
</div>
</>
);
}
For more information take a look at the wagmi documentation.
Web3-React
Coming soon
RainbowKit
Coming soon