Documentation Index
Fetch the complete documentation index at: https://docs.polygon.technology/llms.txt
Use this file to discover all available pages before exploring further.
Wallet Linking creates a cryptographically verifiable, off-chain association between an embedded wallet and an external wallet (such as a MetaMask or hardware wallet). This is useful for:
- Compliance and KYC: verify that a user controls an external address without requiring them to move funds.
- Fund migration: link an external wallet to facilitate moving liquidity to the embedded wallet.
- Multi-wallet governance: verify token holdings across wallets for access control or disbursement rules.
The association is stored off-chain and signed by both wallets, so neither party can forge the link.
How it works
Authenticate with embedded wallet
The user signs in with their embedded wallet (social login, email OTP, or passkey). This becomes the parent wallet.
Sign with external wallet
The user connects their external wallet (MetaMask, hardware wallet, etc.) and signs a message attesting to the embedded wallet address. The embedded wallet also signs a message attesting to the external address.
Submit both signatures
Both signatures are submitted to the Sequence Linking API. The link is created off-chain and is privately retrievable using a signature from the parent wallet.
API integration
Install
pnpm install @0xsequence/waas wagmi
The source code for the API interface instantiates against the Sequence server:
import { API, LinkedWallet } from "./api.gen";
const api = new API("https://dev-api.sequence.app", fetch);
Link an external wallet
Two signatures are required: the embedded wallet signing the external address, and the external wallet signing the embedded wallet address.
import { useSignMessage, useAccount } from "wagmi";
function App() {
const { signMessageAsync } = useSignMessage();
const { address: externalWalletAddress } = useAccount();
const getSignaturesForLinking = async () => {
const parentWalletAddress = await sequenceWaas.getAddress();
const parentMessage = "linked wallet with address " + externalWalletAddress;
const linkingMessage = "parent wallet with address " + parentWalletAddress;
const externalSignature = await signMessageAsync({
message: "Link to " + linkingMessage,
});
const parentSignatureRes = await sequenceWaas.signMessage({
message: parentMessage,
});
return {
parentWalletAddress,
linkedWalletAddress: externalWalletAddress,
parentMessage,
linkingMessage: "Link to " + linkingMessage,
parentSignature: parentSignatureRes.data.signature,
linkedSignature: externalSignature,
};
};
const linkWallet = async () => {
const sigs = await getSignaturesForLinking();
const response = await api.linkWallet({
signatureChainId: "137",
linkedWalletType: "metamask",
parentWalletAddress: sigs.parentWalletAddress,
parentWalletMessage: sigs.parentMessage,
parentWalletSignature: sigs.parentSignature,
linkedWalletAddress: sigs.linkedWalletAddress!,
linkedWalletMessage: sigs.linkingMessage,
linkedWalletSignature: sigs.linkedSignature,
});
console.log("Linked:", response.status);
};
}
Retrieve linked wallets
Retrieval requires a fresh signature from the parent wallet to protect privacy.
const getLinkedWallets = async () => {
const parentWalletAddress = await sequenceWaas.getAddress();
const message = "parent wallet with address " + parentWalletAddress;
const signature = await sequenceWaas.signMessage({ message });
const response = await api.getLinkedWallets({
parentWalletAddress: parentWalletAddress as `0x${string}`,
parentWalletMessage: message,
parentWalletSignature: signature.data.signature,
signatureChainId: "137",
});
return response.linkedWallets;
};
The returned objects follow this shape:
interface LinkedWallet {
id: number;
walletType?: string;
walletAddress: string;
linkedWalletAddress: string;
createdAt?: string;
}
Remove a linked wallet
const unlinkWallet = async () => {
const parentWalletAddress = await sequenceWaas.getAddress();
const parentMessage = "linked wallet with address " + externalWalletAddress;
const linkingMessage = "parent wallet with address " + parentWalletAddress;
const externalSignature = await signMessageAsync({
message: "Unlink from " + linkingMessage,
});
const parentSig = await sequenceWaas.signMessage({ message: parentMessage });
await api.removeLinkedWallet({
signatureChainId: "137",
parentWalletAddress,
parentWalletMessage: parentMessage,
parentWalletSignature: parentSig.data.signature,
linkedWalletAddress: externalWalletAddress!,
linkedWalletMessage: "Unlink from " + linkingMessage,
linkedWalletSignature: externalSignature,
});
};
Demo
A full working demo with source code is available at: