This guide covers how to implement the wallet aspect of the Dynamic Widget UI Component but using only our SDK hooks and your own UI implementation.
Prerequisites
Like with all this series of headless guides, “headless” is defined a way to use the Dynamic SDK without the need for Dynamic UI components (i.e. DynamicWidget, DynamicUserProfile).
You still need to add the SDK and set up the Dynamic Context Provider (complete the quickstart if you haven’t done so already, or refer to an example app)
Signup/Login
Fetch available wallets
You can find the list of available wallets in the walletOptions
prop returned by the useWalletOptions hook.
When browsing wallets in the Dynamic Widget, you might see labels beside them like “Last Used”, “Multichain” or “Recommended”.
Last used comes from the “dynamic_last_used_wallet” value in localstorage.
“Multichain” comes from the chainGroup
node in each wallet (Remember to also add the WalletConnectors for each chain).
“Recommendeded” from the Recommended Wallets feature.
Display a wallet icon
Use the @dynamic-labs/wallet-book
library to display a wallet icon using the exported WalletIcon
component. This component takes a walletKey
prop, which is the key of the wallet you want to display.
import { WalletIcon } from '@dynamic-labs/wallet-book';
const WalletIconComponent = () => {
return <WalletIcon walletKey="metamask" />;
};
Connect to a wallet
useWalletOptions allows you to prompt the user to connect using a specific wallet (by passing in the key).
import { useWalletOptions } from "@dynamic-labs/sdk-react-core";
// component setup etc.
const { selectWalletOption } = useWalletOptions();
const connectWithWallet = async (walletKey) => {
return await selectWalletOption(walletKey)
}
Onramp
How: Regular button attached to useFunding hook
Hook/Component: useFunding
Notes: Ensure Banxa is enabled in the Dynamic dashboard
import { useFunding } from "@dynamic-labs/sdk-react-core";
const Onramp = () => {
const { enabled, openFunding } = useFunding();
return (
<div>
{enabled && (
<button className="onramp-button" onClick={openFunding}>
Onramp
</button>
)}
</div>
);
};
Networks
Display current and available networks
How: Use evmNetworks and getNetwork to fetch, use supportsNetworkSwitching and switchNetwork to switch
Hook/Component: useDynamicContext & getNetwork
Notes: This example is for Evm networks only
import { useDynamicContext, getNetwork } from "@dynamic-labs/sdk-react-core";
const CustomNetworkPicker = () => {
const [currentNetwork, setCurrentNetwork] = useState(null);
const { primaryWallet } = useDynamicContext();
const handleNetworkChange = async (event) => {
const chainId = parseInt(event.target.value);
if (primaryWallet?.connector?.supportsNetworkSwitching()) {
try {
return await primaryWallet?.connector?.switchNetwork({
networkChainId: chainId,
});
} catch (error) {
console.error("Error switching network", error);
}
}
};
useEffect(() => {
if (!currentNetwork)
getNetwork(primaryWallet?.connector).then((network) => {
setCurrentNetwork(network);
});
}, [primaryWallet]);
return (
<>
{currentNetwork && (
<Select defaultValue={currentNetwork} onChange={handleNetworkChange}>
{primaryWallet?.connector?.evmNetworks?.map((network) => (
<option key={network.chainId} value={network.chainId}>
{network.name}
</option>
))}
</Select>
)}
</>
);
};
Primary Wallet
Primary wallet is the wallet that is defined as the active one of all your connected wallets. All interactions, such as signing or creating transactions, will be made with this wallet. In addition, when leveraging our multiasset functionality, this is the wallet that will be display all available tokens.
Display primary wallet address
How: Fetch the primary wallet info from context
Hook/Component: useDynamicContext
Notes: There can be a moment after logging in when it’s not populated yet
import { useDynamicContext } from "@dynamic-labs/sdk-react-core";
const { primaryWallet } = useDynamicContext();
const copyWalletAddress = () => {
navigator.clipboard.writeText(primaryWallet?.address);
};
return (
<button onCLick={() => copyWalletAddress()}>
<p>...{primaryWallet?.address?.slice(-6)}</p>
</button>
);
Display primary wallet icon
How: Utilize the Walletbook library
Hook/Component: WalletIcon (not publicly documented yet)
Notes: Requires the primaryWallet.connector object.
import { primaryWallet } from "@dynamic-labs/sdk-react-core";
import { WalletIcon } from "@dynamic-labs/wallet-book";
const WalletIconWrapped = ({ connector }) => {
return (
<div className="wallet-icon-container">
<WalletIcon walletKey={primaryWallet?.connector?.key} />
</div>
);
};
Show balance of primary wallet
How: getBalance method from useDynamicContext wallet connector
Hook/Component: https://docs.dynamic.xyz/react-sdk/hooks/usedynamiccontext
Notes: None
import { useDynamicContext } from "@dynamic-labs/sdk-react-core";
const { primaryWallet } = useDynamicContext();
const [balance, setBalance] = useState(null);
useEffect(() => {
const fetchBalance = async () => {
if (primaryWallet) {
const value = await primaryWallet.getBalance();
setBalance(value);
}
};
fetchBalance();
}, [primaryWallet]);
return <p>{balance}</p>;
Show all tokens and balances
How: Mapping return from usetokenbalances hook
Hook/Component: https://docs.dynamic.xyz/react-sdk/hooks/usetokenbalances
Notes: Multi-asset is only supported on the following networks - Ethereum, Optimism, Polygon, Arbitrum, and Base.
import { useTokenBalances } from "@dynamic-labs/sdk-react-core";
const TokenBalances = () => {
const tokenBalances = useTokenBalances();
return (
<div>
{tokenBalances.map((token) => (
<div key={token.address}>
<p>{token.symbol}</p>
<p>{token.balance}</p>
</div>
))}
</div>
);
};
Detect Wallet Locking
The wallet object has a field called connected
which indicates whether the wallet is locked or not, and you can use this to trigger your own workflow to warn the user.
Multi-Wallet
Show all linked wallets and allow unlinking
How: Mapping return from useUserWallets hook and handleUnlinkWallet from useDynamicContext
Hook/Component: https://docs.dynamic.xyz/react-sdk/hooks/useuserwallets & https://docs.dynamic.xyz/react-sdk/hooks/usedynamiccontext
Notes: Doesn’t yet show if a wallet is the primary wallet. Unlinking is also handled here.
import {
useDynamicContext,
useUserWallets,
} from "@dynamic-labs/sdk-react-core";
import { Tooltip } from "react-tooltip";
const LinkedWallets = () => {
const { handleUnlinkWallet } = useDynamicContext();
const userWallets = useUserWallets();
return (
<>
{userWallets.length > 0 && (
<div className="profile-wallets-container">
<h2>Connected Wallets</h2>
{userWallets.length < 2 && <Tooltip id="unlink-tooltip" />}
<div className="profile-wallets-inner-container">
{userWallets.map((wallet) => (
<Flex key={wallet.address}>
<p>{wallet.address}</p>
<Spacer />
<a
data-tooltip-id="unlink-tooltip"
data-tooltip-content="Can't unlink your only wallet!"
>
<Button
size="xs"
onClick={() => handleUnlinkWallet(wallet.id)}
disabled={userWallets.length < 2}
>
Unlink
</Button>
</a>
</Flex>
))}
</div>
</div>
)}
</>
);
};
Detect which wallet is primary
How: You can grab it directly from useDynamicContext
Hook/Component: useDynamicContext
import { useDynamicContext, useUserWallets } from "@dynamic-labs/sdk-react-core";
const { primaryWallet } = useDynamicContext();
console.log(primaryWallet);
Allow user to link a new wallet
How: Pop up our linking modal using a hook (setShowLinkNewWalletModal)
Hook/Component: useDynamicModals
Notes:
- You will need to also use the DynamicMultiWalletPromptsWidget component
import {
useDynamicModals,
DynamicMultiWalletPromptsWidget,
} from "@dynamic-labs/sdk-react-core";
const LinkWallet = ({ text }) => {
const { setShowLinkNewWalletModal } = useDynamicModals();
return (
<>
<div className="link-wallet-container">
<Button
className="profile-button"
onClick={() => setShowLinkNewWalletModal(true)}
>
{text}
</Button>
</div>
<DynamicMultiWalletPromptsWidget />
</>
);
};
Switch the primary wallet to another
How: useSwitchWallets hooks
Hook/Component: https://docs.dynamic.xyz/react-sdk/hooks/useswitchwallet#useswitchwallet
Notes: None
import React from "react";
import { useSwitchWallet, useUserWallets } from "@dynamic-labs/sdk-react-core";
const WalletSwitcher = () => {
const switchWallet = useSwitchWallet();
const userWallets = useUserWallets();
return (
<div>
{userWallets.map((wallet) => (
<button key={wallet.id} onClick={() => switchWallet(wallet.id)}>
{wallet.address}
</button>
))}
</div>
);
};
Wallet Interactions
To learn how to send balance, sign messages etc., please check out the Wallet Interaction guides where we split into Read actions and Write actions, and we provide per chain examples for common functionality.