Quick Start
Let's integrate Human Wallet in less than 5 minutes. You will learn how to embed Human Wallet login options like this:
The login button can be styled in any way and need not even say login. All that matters is that you call the correct method on window.silk
.
Getting Started​
Get started by creating a new dApp.
Or clone the Human Wallet Demo App.
To see how the demo app looks, you can visit the published demo app.
Check out our Human Passport Verification Demo - a complete Next.js application showcasing Human Wallet + Human Passport integration with modern UI/UX, multi-chain support, and production-ready features.
What you'll need​
- Node.js version 18.0 or above.
- Silk SDK:
npm install @silk-wallet/silk-wallet-sdk
- A web3 provider like ethers.js:
npm install ethers@^5.7.2
Basic Integration​
You can easily embed Human Wallet into your site using a single, provider-agnostic approach. Human Wallet exposes an EIP-1193-compliant interface via window.silk
, so you can interact with it just as you would with MetaMask or other wallets.
import { initSilk } from "@silk-wallet/silk-wallet-sdk"
const initConfig = {
config: {
allowedSocials: ['google', 'twitter', 'discord', 'linkedin', 'apple', 'coinbase'],
authenticationMethods: ['email', 'phone', 'social'],
styles: { darkMode: true }
},
useStaging: true
}
useEffect(() => {
// This will automatically set window.silk with all necessary methods.
initSilk(initConfig)
}, [])
try {
// Open the Human Wallet login modal
const loginType = await window.silk.login();
// loginType: 'human' | 'walletconnect' | 'injected' | null
// Get the user's wallet addresses
const accounts = await window.silk.request({ method: 'eth_requestAccounts' })
} catch (error) {
console.error(error)
}
Auto-Connect on Page Refresh​
Human Wallet automatically reconnects users when they return to your app. Simply call eth_requestAccounts
and it will restore their previous connection seamlessly.
// This will auto-connect if user was previously logged in
const accounts = await window.silk.request({ method: 'eth_requestAccounts' })
For more details on auto-connect functionality, see Auto-Connect in Methods.
Logout​
For simple logout (Human Wallet only):
await window.silk.logout();
When external wallets are enabled, the logout behavior depends on how the user logged in. See Logout and Session Management for complete details.
Events and Advanced Usage​
You can listen for wallet events like account changes and chain changes:
window.silk.on("accountsChanged", (accounts) => {
console.log("Active account changed:", accounts[0]);
});
For complete method documentation, see the Methods page.
Human Wallet + Next.js + Wagmi​
If your dApp uses wagmi, you can integrate Human Wallet as a custom connector. This allows you to use all wagmi hooks and utilities with Human Wallet.
A great example is the Axé DAO dApp, Quilombo.
Note: When using wagmi, don't include
'wallet'
inauthenticationMethods
. Instead, add separate connectors for each wallet type in your wagmi config.
- wagmiConfig.ts
- silk.connector.ts
import { createConfig, http } from 'wagmi';
import { injected, walletConnect } from 'wagmi/connectors';
import silk from '@/utils/silk.connector';
import { mainnet, sepolia, optimism } from 'wagmi/chains';
const wagmiConfig = createConfig({
chains: [mainnet, optimism, sepolia],
connectors: [
silk(initSilkOptions),
injected(), // For MetaMask and other injected wallets
walletConnect({ projectId: '<YOUR_WALLETCONNECT_PROJECT_ID>' }),
],
transports: {
[mainnet.id]: http('<mainnet_rpc_url>'),
[optimism.id]: http('<optimism_rpc_url>'),
[sepolia.id]: http('<sepolia_rpc_url>'),
},
});
import { ChainNotConfiguredError, createConnector } from 'wagmi';
import { getAddress, SwitchChainError, UserRejectedRequestError } from 'viem';
import { type CredentialType, SILK_METHOD } from '@silk-wallet/silk-interface-core';
import { type SilkEthereumProviderInterface, initSilk } from '@silk-wallet/silk-wallet-sdk';
import type { InitSilkOptions } from '@silk-wallet/silk-wallet-sdk/dist/lib/provider/types';
/**
* Creates a WAGMI connector for the Silk Wallet SDK
* @param options the initialization options passed to the Silk Wallet SDK
* @returns
*/
export default function silk(options?: InitSilkOptions) {
let silkProvider: SilkEthereumProviderInterface | null = null;
return createConnector<SilkEthereumProviderInterface>((config) => {
console.log('Silk Connector Config:', config);
return {
id: 'silk',
name: 'Silk Connector',
type: 'Silk',
chains: config.chains,
supportsSimulation: false,
async connect({ chainId } = {}) {
try {
config.emitter.emit('message', {
type: 'connecting',
});
const provider = await this.getProvider();
provider.on('accountsChanged', this.onAccountsChanged);
provider.on('chainChanged', this.onChainChanged);
provider.on('disconnect', this.onDisconnect);
if (!provider.connected) {
try {
await provider.login();
} catch (error) {
console.warn('Unable to login', error);
throw new UserRejectedRequestError('User rejected login or login failed' as unknown as Error);
}
}
let currentChainId = await this.getChainId();
if (chainId && currentChainId !== chainId) {
console.info(`Switching chain from ${currentChainId} to ${chainId}`);
// biome-ignore lint/style/noNonNullAssertion: the switchChain method is defined in the connector
const chain = await this.switchChain!({ chainId }).catch((error) => {
if (error.code === UserRejectedRequestError.code) throw error;
return { id: currentChainId };
});
currentChainId = chain?.id ?? currentChainId;
}
const accounts = await this.getAccounts();
return { accounts, chainId: currentChainId };
} catch (error) {
console.error('Error while connecting', error);
this.onDisconnect();
throw error;
}
},
async getAccounts() {
const provider = await this.getProvider();
const accounts = await provider.request({
method: SILK_METHOD.eth_accounts,
});
if (accounts && Array.isArray(accounts)) return accounts.map((x: string) => getAddress(x));
return [];
},
async getChainId() {
const provider = await this.getProvider();
const chainId = await provider.request({ method: SILK_METHOD.eth_chainId });
return Number(chainId);
},
async getProvider(): Promise<SilkEthereumProviderInterface> {
if (!silkProvider) {
console.log('Initializing Silk Provider with options:', options);
silkProvider = initSilk(options ?? {});
}
return silkProvider;
},
async isAuthorized() {
try {
const accounts = await this.getAccounts();
return !!accounts.length;
} catch {
return false;
}
},
async switchChain({ chainId }) {
console.info('Switching chain to ID', chainId);
try {
const chain = config.chains.find((x) => x.id === chainId);
if (!chain) throw new ChainNotConfiguredError();
const provider = await this.getProvider();
await provider.request({
method: SILK_METHOD.wallet_switchEthereumChain,
params: [{ chainId: `0x${chain.id.toString(16)}` }],
});
config.emitter.emit('change', { chainId });
return chain;
} catch (error: unknown) {
console.error('Error: Unable to switch chain', error);
throw new SwitchChainError(error as Error);
}
},
async disconnect(): Promise<void> {
const provider = await this.getProvider();
provider.removeListener('accountsChanged', this.onAccountsChanged);
provider.removeListener('chainChanged', this.onChainChanged);
provider.removeListener('disconnect', this.onDisconnect);
},
async requestEmail(): Promise<unknown> {
const provider = await this.getProvider();
return provider.requestEmail();
},
async requestSBT(type: CredentialType): Promise<unknown> {
const provider = await this.getProvider();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
// biome-ignore lint/suspicious/noExplicitAny: the requestSBT method is not declared in the SilkEthereumProviderInterface, but is implemented in the EthereumProvider class
return (provider as any).requestSBT(type);
},
onAccountsChanged(accounts) {
if (accounts.length === 0) config.emitter.emit('disconnect');
else
config.emitter.emit('change', {
accounts: accounts.map((x) => getAddress(x)),
});
},
onChainChanged(chain) {
const chainId = Number(chain);
config.emitter.emit('change', { chainId });
},
onDisconnect(): void {
config.emitter.emit('disconnect');
},
};
});
}
Example: ConnectButton with wagmi's useConnect
hook​
"use client";
import { useAccount, useConnect } from "wagmi";
import { sepolia } from "wagmi/chains";
import { UserRejectedRequestError } from "viem";
import silk from "@/utils/silk.connector";
export default function ConnectPanel() {
const { connect, error, isError, connectors } = useConnect();
const account = useAccount();
const handleConnect = async () => {
const silkConnector = connectors.find((connector) => connector.id === "silk");
try {
// There should already be a Human Wallet connector in the wagmi config which also
// enables automatic reconnect on page refresh, but just in case, we can also create
// the connector here.
if (!silkConnector) {
connect({
chainId: sepolia.id,
connector: silk({params: initSilkOptions}),
});
} else {
connect({ chainId: sepolia.id, connector: silkConnector });
}
} catch (error) {
console.error("Error connecting to Human Wallet:", error);
if (error instanceof UserRejectedRequestError) console.log("User aborted the transaction");
}
};
return (
<div className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
{!account.address ? (
<button onClick={handleConnect}>Connect</button>
) : (
<div className="flex items-center justify-center">Connected: {account.address}</div>
)}
{isError && error.message && <div className="text-red-500">{error.message}</div>}
</div>
);
}
Examples and Templates​
- Human Wallet Demo App - Full-featured demo showcasing all functionality (source)
- Next.js + Viem Template - Template app using Human Wallet and Viem