- Your core Mesh integration is working end-to-end (see Prepare for go-live)
- You’ve determined which addresses or wallet types require verification in your app
Background and Overview
Travel Rule regulations have long existed in traditional financial markets as a way for regulators to enforce laws pertaining to the movement of money (eg. anti-money laundering, terrorist financing, sanctions, etc.). In short, regulated financial institutions are responsible for knowing who they are receiving assets from, and where they’re sending assets to. The crypto world has long-awaited clear Travel Rule guidelines for crypto, and on July 4th, 2024 the European Banking Authority (EBA) released its final guidelines. Other global regulatory regimes are likely not far behind. This guide dives into detail around Mesh’s support for verifying ownership of a self-hosted wallet.How does wallet verification work?
The nature of self-hosted wallets is that they don’t know who the user is. In other words, a user doesn’t KYC with MetaMask like they would with Coinbase or another centralized exchange. So verifying ownership of a self-hosted wallet is not about receiving user information from that wallet. Instead, it is about confirming that the user you know is in control of the wallet in question. Verification pertains to an address (ie. 0x31…cF98), not a wallet app (ie. MetaMask). Keep in mind that a user can interact with the same wallet (ie. address) from multiple wallet apps, and can also interact with multiple wallets from within the same wallet app. The EBA specifies that one acceptable method of verifying ownership of a self-hosted wallet is having the user sign a self-attestation of ownership (ie. a message) in that wallet. The message doesn’t have to be anything specific, but the message the user signs must be the exact message requested by you (or Mesh). A message signature is an off-chain event (ie. it’s completely gasless), but is also fully verifiable with the combination of thesignedMessageHash, the address, and message.
In Mesh’s wallet verification flow, the user is prompted to sign a message immediately after connecting their wallet. Similar to connection requests and transfer requests, the user won’t need to configure anything — all they have to do is review and sign. Wallet verification can be completely independent of a transfer (ie. the user only connects their wallet and signs the message), or can be used in combination with a transfer (ie. the user connects their wallet, signs a message, and then continues to the transfer).
How to use wallet verification in Mesh Link
1. Use the verifyWalletOptions object in the Link Token request
Link Token request structure
Link Token request structure
Parameter reference
Parameter reference
X-Client-Id requiredX-Client-Secret requireduserId requiredrestrictMultipleAccounts optionalDefaults to true, which is standard used for any transfer flow.
On some non-transfer flows (ie. “read” use cases), a user could connect multiple accounts in a row if this value were false. Sometimes valuable for withdrawal use cases to let the user create an external “address book”.
integrationId optionalA unique Mesh identifier representing a specific integration.
Use the Get integrations endpoint (/api/v1/transfers/managed/integrations) to pull a list of integrations and the corresponding Mesh integrationId. These values won’t change, so you do not have to hit this endpoint before each Link Token request.
To be used if the user selects the integration in your UX before launching Mesh (most commonly in an onramp flow).
verifyWalletOptions.verificationMethods optional required for verificationsMethod by which the user must verify their self-hosted wallet.
Enum to support future verification methods, but only one option for now.
verifyWalletOptions.message optionalThe message the user should sign in their wallet.
The exact language isn’t important. Mesh has standard language if this isn’t provided.
verifyWalletOptions.networkId optionalA unique Mesh identifier for the network on which the user must verify their self-hosted address.
Use the Get networks endpoint (/api/v1/transfers/managed/networks) to pull a list of all supported networks and the corresponding Mesh networkId. These values won’t change, so you do not have to hit this endpoint before each Link Token request.
To be used if you need your user to verify an address on a specific network. Should not be used in combination with networkType.
verifyWalletOptions.networkType optionalThe network type on which the user must verify their self-hosted address.
To be used if you need your user to verify an address on a particular network type (eg. EVM). Should not be used in combination with networkId.
verifyWalletOptions.address optionalA list of address from which the user can verify ownership. User is not allowed to verify an address outside this list.
Used if you need your user to verify ownership of a specific address. If not provided, user can verify ownership of any self-hosted address they connect.
- Wallet verification is fully compatible with the deposit & payment use cases. If you require users to verify that they own a self-custody wallet before initiating a deposit or payment from that wallet, you can add these configurations into the same Link Token request.
- This will only impact the user experience for self-hosted wallets (ie. MetaMask, Phantom, etc.). This will not change anything about the experience for centralized exchanges (ie. Binance, Coinbase, etc.).
2. Store the walletMessageSigned payload
After the user signs the message in their wallet, you will receive the walletMessageSigned SDK event with the following payload:
signedMessageHashmessageaddresstimeStampisVerified(boolean)
signedMessageHash will be provided — Mesh does not retain this data.
Test your implementation
Test using MetaMask (Sepolia testnet) or Phantom (Solana Devnet). See Sandbox & Testnets for wallet setup instructions. What to test:- Full flow: Launch a sandbox Link session with
verifyWalletOptionsconfigured. Connect a test wallet — the user should be prompted to sign immediately after connecting, with no additional configuration needed. - Payload capture: Confirm your
onEventhandler receiveswalletMessageSignedwith all five fields present:address,isVerified,message,signedMessageHash, andtimeStamp. - Message match: Verify that
messagein the payload exactly matches what you passed inverifyWalletOptions.message. If you didn’t provide a custom message, confirm Mesh’s default language appears. - Rejection path: Reject the signature request in your test wallet (click “Reject” in MetaMask). Confirm you receive the
verifyWalletRejectedevent and that your app handles it gracefully. - Payload storage: Confirm you’re capturing and storing
signedMessageHashat the time of the event — it won’t be retrievable afterward.
What’s next
Explore the other Extend guides:- Add Mesh onramp integrations to your “Buy” lineup — add exchange-based onramps to your “Buy” flow.
- Add Mesh to your withdrawal flow — enable automated address retrieval for user withdrawals.
AI coding reference (llms.txt)
AI coding reference (llms.txt)
verificationMethods: ["signedMessage"] (required) | message (optional — Mesh provides default if omitted) | networkId (optional, specific network; do not use with networkType) | networkType (optional, e.g. “solana” or “evm”) | addresses[] (optional — restrict to specific addresses)Get networkIds: Use GET /api/v1/transfers/managed/networks to fetch the full list of supported networks and their networkId values. These values don’t change — safe to cache permanently, no need to call this endpoint before every Link Token request.Compatible with transfer flows: Add verifyWalletOptions to any deposit or payment Link Token request. User verifies wallet then proceeds to transfer. Does not affect CEX flows.SDK event: walletMessageSigned
Payload: signedMessageHash | message | address | timeStamp | isVerified (boolean)
Critical: Store signedMessageHash at event time — Mesh does not retain it.Rejection event: verifyWalletRejected fires if user declines to sign.Test: MetaMask (Sepolia testnet) or Phantom (Solana Devnet). See Sandbox & Testnets guide for setup.Verify use case — Link Token request body:walletMessageSigned event payload (capture immediately — Mesh does not store it):