Verify API
Verify API is a security-focused feature that allows wallets to notify end-users when they may be connecting to a suspicious or malicious domain, helping to prevent phishing attacks across the industry. Once a wallet knows whether an end-user is on uniswap.com or eviluniswap.com, it can help them to detect potentially harmful connections through Verify's combined offering of WalletConnect's domain registry.
When a user initiates a connection with an application, Verify API enables wallets to present their users with four key states that can help them determine whether the domain they’re about to connect to might be malicious.
These are:

Disclaimer
Verify API helps wallets detect suspicious or malicious domains, but it may not catch every threat. Use it as one more layer of protection alongside your wallet's other security measures.
Domain risk detection
The Verify security system will discriminate session proposals & session requests with distinct validations that can be either VALID, INVALID or UNKNOWN.
- Domain match: The domain linked to this request has been verified as this application's domain.
- This interface appears when the domain a user is attempting to connect to has been ‘verified’ in our domain registry as the registered domain of the application the user is trying to connect to, and the domain has not returned as suspicious from either of the security tools we work with. The
verifyContextincluded in the request will have a validation ofVALID.
- This interface appears when the domain a user is attempting to connect to has been ‘verified’ in our domain registry as the registered domain of the application the user is trying to connect to, and the domain has not returned as suspicious from either of the security tools we work with. The
- Unverified: The domain sending the request cannot be verified.
- This interface appears when the domain a user is attempting to connect to has not been verified in our domain registry, but the domain has not returned as suspicious from either of the security tools we work with. The
verifyContextincluded in the request will have a validation ofUNKNOWN.
- This interface appears when the domain a user is attempting to connect to has not been verified in our domain registry, but the domain has not returned as suspicious from either of the security tools we work with. The
- Mismatch: The application's domain doesn't match the sender of this request.
- This interface appears when the domain a user is attempting to connect to has been flagged as a different domain to the one this application has verified in our domain registry, but the domain has not returned as suspicious from either of the security tools we work with. The
verifyContextincluded in the request will have a validation ofINVALID
- This interface appears when the domain a user is attempting to connect to has been flagged as a different domain to the one this application has verified in our domain registry, but the domain has not returned as suspicious from either of the security tools we work with. The
- Threat: This domain is flagged as malicious and potentially harmful.
- This interface appears when the domain a user is attempting to connect to has been flagged as malicious on one or more of the security tools we work with. The
verifyContextincluded in the request will contain parameterisScamwith valuetrue.
- This interface appears when the domain a user is attempting to connect to has been flagged as malicious on one or more of the security tools we work with. The
Reading verifyContext by connection type
How your wallet should read verifyContext depends on how the dApp connects:
- Web dApp over QR code or deep link (relay): This is the full Verify API, including scam checks. Show all four states:
VALIDas Domain match,UNKNOWNas Cannot verify,INVALIDas Mismatch, andisScamas Security risk (ideally blocking or requiring the user to confirm). - Native dApp over relay: Native apps can't create a Verify attestation, so
UNKNOWNis expected here. We suggest a neutral "Cannot verify" label rather than an alarming warning. - Link Mode (native to native): The domain check above applies, with no scam check.
INVALIDmeans the dApp's universal link domain doesn't match itsmetadata.url. The wallet's own Link Mode setup also needs to be complete, or the result falls back toINVALID.
Implementation
sessionProposalPublisher, authenticateRequestPublisher and sessionRequestPublisher each emit a VerifyContext? alongside the request:
public struct VerifyContext: Equatable, Hashable, Codable {
public enum ValidationStatus: Codable {
case unknown
case valid
case invalid
case scam
}
public let origin: String?
public let validation: ValidationStatus
}Map it to one of the four states once, then use that state in each subscriber.
enum VerifyState { case threat, mismatch, unverified, domainMatch }
func verifyState(for context: VerifyContext?) -> VerifyState {
switch context?.validation {
case .scam:
// Threat: only set over relay, since Link Mode has no scam check
return .threat
case .invalid:
return .mismatch
case .valid:
return .domainMatch
case .unknown, .none:
// UNKNOWN: expected for native dApps over relay
return .unverified
}
}
// Session proposals from a web or native dApp
WalletKit.instance.sessionProposalPublisher
.receive(on: DispatchQueue.main)
.sink { proposal, context in
let state = verifyState(for: context)
// show the state on the approval screen
}
.store(in: &publishers)
// One-click auth requests can arrive over relay or Link Mode
WalletKit.instance.authenticateRequestPublisher
.receive(on: DispatchQueue.main)
.sink { request, context in
let state = verifyState(for: context)
// in Link Mode, MISMATCH means the universal link domain doesn't match metadata.url
}
.store(in: &publishers)
WalletKit.instance.sessionRequestPublisher
.receive(on: DispatchQueue.main)
.sink { request, context in
let state = verifyState(for: context)
// show the state on the signing screen
}
.store(in: &publishers)Keep in mind:
- WalletKit doesn't expose the transport a request arrived on. To detect Link Mode, check for
wc_evin the incoming universal link before passing it toWalletKit.instance.dispatchEnvelope(_:), and keep track of that request's topic. - Session requests sent over Link Mode arrive with a
nilcontext, so they map toUNVERIFIED.
Present each state like this:
THREAT: Block the request, or require the user to explicitly accept the risk. Check before anything else, so the user doesn't also see a Mismatch warning.MISMATCH: Show a warning and proceed only if the user accepts the risk.UNVERIFIED: Show a neutral "Cannot verify" label. Don't block the request, since native dApps over relay always land here.DOMAIN_MATCH: Show a verified badge and proceed as normal.