Unhosted wallet verification

Use wallet ownership determination to comply with FATF requirements.

The Sumsub Travel Rule solution allows unhosted wallet controllers to securely prove wallet ownership using a cryptographic signature.

What is an unhosted wallet

An unhosted wallet—also known as self-hosted or non-custodial—refers to a type of digital wallet that is hosted and controlled by the user, as opposed to being hosted by any exchanges, markets, or other VASPs, which means that:

  • The applicant cryptocurrency balances are off any third parties.
  • The applicant did not pass any KYC or customer due diligence processes.

Such wallets offer their owners direct control over their private keys and, consequently, the security and management of their digital assets.

For example, a MetaMask wallet is considered unhosted, and a Centralized Crypto Exchange (CEX) account represents a hosted wallet, as you rely on a third party (custodian) to control your funds.

Why do you need Unhosted Wallet Verification

Countries that follow the Travel Rule requirements may oblige VASPs to verify the ownership of self-hosted wallets before transacting with them.

Such verification includes, but is not limited to, the following:

  • Collecting relevant Travel Rule information related to unhosted wallets from their customers.
  • Introducing additional mitigation measures, such as verifying the identity of the unhosted wallet owner or performing enhanced due diligence.
  • Limiting or restricting transactions with unhosted wallets.

Apart from compliance with regulations such as the Travel Rule, unhosted wallets verification is crucial for businesses for the following reasons:

  • Preventing money laundering and terrorist financing.
  • Mitigating the risks associated with anonymous transactions.
  • Establishing trust with their customers and reducing the potential for fraud.

How Unhosted Wallet Verification works

Let’s say Alice, based in Switzerland, wants to withdraw some funds from her VASP to her private wallet on Metamask. As her VASP is based in Switzerland, it has to follow local regulatory requirements and verify who controls the wallet before approving the Travel Rule message:

  1. The VASP gets notified about the blockchain transaction and creates a Travel Rule message on Sumsub.
  2. Sumsub identifies the wallet type as Unhosted and generates a unique link that will be sent to Alice.
  3. The VASP sends Alice a message with the link asking her to confirm wallet ownership by signing the message with her private key.
  4. After Alice confirms wallet ownership by following the link and signing in to her wallet, the wallet is verified, the Travel Rule message is sent for further processing, and the wallet is stored in the system so that Alice will not have to verify it again.

Sumsub applies signature proof for Unhosted Wallet Verification. With the WebSDK, you can generate a signature for supported wallets.

Data processing

Here is how the data is processed:

  1. Your VASP sends Sumsub a Travel Rule message with all its details.
  2. The Travel Rule: New TR data exchange request rule is applied. In accordance with this rule, Sumsub performs target VASP attribution and checks if:
    • The recipient VASP is identified.
    • The recipient VASP is unidentified and assumed to be an unhosted wallet. In this case, the Travel Rule: Unhosted wallet verification rule is applied.
  3. The unhosted wallet ownership is verified through the unique link generated by Sumsub that is relevant only to the specific wallet address.
  4. The message is approved, rejected, or put on hold based on the rules you applied and the verification results.

How to enable Unhosted Wallet Verification

If you have never used Sumsub, visit our website and click Get started to begin your journey or contact our sales department.

If you are already a Sumsub customer, in the Dashboard, open the Rules Library and enable the following rules:

Verify unhosted wallets

The wallet verification flow via Sumsub is:

  • Seamless — all done via a link.
  • Integrated with the most popular wallets — Metamask, Ledger, Trezor, and any WalletConnect solution.

In the table below you can find a list of cryptocurrency networks with which Sumsub currently supports integrations.

ChainsChain symbol
BitcoinBTC
EthereumETH
SolanaSOL
POL (formerly Matic)MATIC/SOL
ArbitrumARB
Binance Smart ChainBNB
Base NetworkBASE
MantleMNT
AvalancheAVAX
CoreCORE
Sonic (formerly Fantom)S
OptimismOP
BerachainBERA

The following diagram shows a typical wallet verification flow:

Step 1: Create Travel Rule message

Travel Rule messages are sent with the following conditions:

  • Messages have no attribution, i.e. we did not manage to identify whom the key/wallet belongs to.
  • Unhosted Wallet Verification is enabled as described here.

Submit a Travel Rule message using any of the following methods:

curl -X POST \ 'https://api.sumsub.com/resources/applicants/66e056c7776e5a46bf63da0a/kyt/txns/-/data'\
-H 'Accept: application/json'\
-H 'Content-Type: application/json'\
-d '{
      "txnId": "z527pptec8492lrq9pa4b",
      "type": "travelRule",
      "applicant": {
        "address": {
          "country": "CHE"
        },
        "institutionInfo": {
          "internalId": "AliceVaspId"
        },
        "paymentMethod": {
          "type": "crypto",
          "accountId": "bc1qwxdppz8623cewq46tg4wnrp9nu7jj4dx3nr70n",
          "issuingCountry": "CHE"
        },
        "device": {
          "ipInfo": {
            "ip": "130.60.28.120"
          }
        },
        "type": "individual",
        "fullName": "Alice Doe"
      },
      "counterparty": {
        "paymentMethod": {
          "type": "crypto",
          "accountId": "bc1qtspgw38syqvyullsac4lnymm6qj38s6d35pchn"
        },
        "fullName": "Joe Doe",
        "externalUserId": "JoeDoeID",
        "type": "individual"
      },
      "info": {
        "direction": "out",
        "amount": 0.02,
        "currencyType": "crypto",
        "currencyCode": "BTC"
      },
      "txnDate": "2024-08-24 23:37:02+0000"
    }'
curl -X POST \ 'https://api.sumsub.com/resources/applicants/-/kyt/txns/-/data?levelName=basic-kyc-level'\
-H 'Accept: application/json'\
-H 'Content-Type: application/json'\
-d '{
      "txnId": "z527pktec34922lrq9pa4b",
      "type": "travelRule",
      "applicant": {
        "address": {
          "country": "CHE"
        },
        "institutionInfo": {
          "internalId": "AliceVaspId"
        },
        "paymentMethod": {
          "type": "crypto",
          "accountId": "bc1qwxdzpz8653cewq46tg4wnrp9nu7jj4dx3nr70n",
          "issuingCountry": "CHE"
        },
        "device": {
          "ipInfo": {
            "ip": "130.60.28.120"
          }
        },
        "type": "individual",
        "fullName": "Alice Doe",
        "externalUserId": "AliceId"
      },
      "counterparty": {
        "paymentMethod": {
          "type": "crypto",
          "accountId": "bc1qtspgw38syqvynllsac4lnypm6qj38s6d35pchn"
        },
        "fullName": "Joe Doe",
        "externalUserId": "JoeDoeID",
        "type": "individual"
      },
      "info": {
        "direction": "out",
        "amount": 0.02,
        "currencyType": "crypto",
        "currencyCode": "BTC"
      },
      "txnDate": "2024-08-24 23:37:02+0000"
    }'
curl -X POST \ 'https://api.sumsub.com/resources/kyt/misc/txns/import' \
-H 'Content-Type: application/x-ndjson' \ 
-d $'{
        "applicantId": "636cee6b17d6c000144673b",
        "data": {
          "txnId": "631f268442d8290071e1eee8_newTxn",
          "type": "travelRule",
          "applicant": {
            "externalUserId": "AliceDoeId",
            "address": {
              "country": "CHE"
            },
            "device": {
              "ipInfo": {
                "ip": "130.60.28.120"
              }
            },
            "institutionInfo": {
              "internalId": "AliceVaspId"
            },
            "paymentMethod": {
              "type": "crypto",
              "accountId": "bc1qwxdzpv8623cewq46tg4wnrp9nu7jj4dx3nr70n",
              "issuingCountry": "CHE"
            }
          },
          "counterparty": {
            "paymentMethod": {
              "type": "crypto",
              "accountId": "bc1qtspgw38syqvyullsac8lnypm6qj38s6d35pchn"
            },
            "fullName": "Joe Doe",
            "externalUserId": "JoeDoeID",
            "type": "individual"
          },
          "info": {
            "direction": "out",
            "amount": 0.02,
            "currencyType": "crypto",
            "currencyCode": "BTC"
          }
        }
      }\n{
        "applicantId": "66e066c3756e5a46bf63da0a",
        "data": {
          "txnId": "631f268442h8290001e1eee9_newTxn",
          "type": "travelRule",
          "applicant": {
            "externalUserId": "JoeDoeId",
            "address": {
              "country": "CHE"
            },
            "device": {
              "ipInfo": {
                "ip": "130.60.38.120"
              }
            },
            "institutionInfo": {
              "internalId": "JoeDoeId"
            },
            "paymentMethod": {
              "type": "crypto",
              "accountId": "0x4d1f36b2vc2d7b42f71f0b381b6d9d4ce1282721"
            }
          },
          "counterparty": {
            "paymentMethod": {
              "type": "crypto",
              "accountId": "0xf9b12293340a1153601bc8d8a858f0a24a54e655"
            },
            "fullName": "James Doe",
            "externalUserId": "JamesDoeID",
            "type": "individual"
          },
          "info": {
            "direction": "out",
            "amount": 0.01,
            "currencyType": "crypto",
            "currencyCode": "ETH"
          }
        }
      }'

If the message is approved, you will get the applicantKytTxnApproved webhook. For more information about the webhooks, see this article.

{
  "applicantId": "634829375766b80001a40152",
  "applicantType": "individual",
  "correlationId": "f24f6616020245053139a6537303a251",
  "sandboxMode": false,
  "externalUserId": "AliceId",
  "type": "applicantKytTxnApproved",
  "reviewResult": {
    "reviewAnswer": "GREEN"
  },
  "reviewStatus": "completed",
  "createdAt": "2024-09-10 17:46:24+0000",
  "createdAtMs": "2024-09-10 17:46:24.183",
  "clientId": "sumsub_new_22",
  "kytTxnId": "64a7dc05fbf57c624afcb72d",
  "kytDataTxnId": "uauu08x44xexbohyh4lkp9",
  "kytTxnType": "travelRule"
}

If the message was put on hold, you will get the applicantKytOnHold webhook. At this stage, the applicant must confirm wallet ownership via a link.

{
  "applicantId": "66e085ef8c8b010a0d318e47",
  "applicantType": "individual",
  "correlationId": "e38c2eb49fde9291c6a028de2c760104",
  "sandboxMode": false,
  "externalUserId": "AliceId",
  "type": "applicantKytOnHold",
  "reviewStatus": "onHold",
  "createdAt": "2024-09-10 17:46:24+0000",
  "createdAtMs": "2024-09-10 17:46:24.183",
  "clientId": "sumsub_new_22",
  "kytTxnId": "66e075ef8c8b910a0d318e4a",
  "kytDataTxnId": "z527mptec34922lrq9pa4b",
  "kytTxnType": "travelRule"
}

Step 2: Retrieve verification link and send it to the applicant

When the applicantKytOnHold webhook is received, you must obtain the wallet confirmation link that you must send to the applicant. By following the link, the applicant is asked to sign/encrypt the initial message with their private key.

🚧

Important

When an applicant has previously confirmed wallet address ownership, we store this information and use it for future Travel Rule messages. The applicant will only need to verify the wallet address once.

To get the link, use the WebSDK preview link that is provided in the Wallet ownership verification section of the message. To fetch this link, do the following:

  1. Load the message using this API method.
  2. Check the scoringResult.applicantActions field. If the data is valid, proceed to the next step.
  3. Load applicant action using this API method.
  4. Check the ownershipChecks.type field. It should display unhostedWallet.
  5. Generate external link to the WebSDK using data from the aforementioned fields via this API method. You can also integrate with the WebSDK by following the instructions given in this article.

Example of such an API call:

{
"levelName": "paymentSource", // we are creating this level automatically for you
"externalActionId": "86893ec9-d48c-496a-9f72-69e272ac84e9", // use the value from applicant actions (point 2)
"ttlInSecs" : 3600,
"userId" : "{externalId}" // use the value included in the message (point 1) txn.data.applicant.externalUserId
}

Step 3: Wallet ownership confirmation

Wait until the applicant confirms wallet ownership using signature proof via the WebSDK.

User flow is as follows:

  1. Applicant reads the sensitive data disclaimer.
  2. Applicant selects a wallet type.
  3. Applicant is given a choice of confirming wallet ownership on the current device or on a mobile phone.
  4. Applicant verifies wallet ownership by signing the message.
  5. We verify the signed message.
  6. We display verification results.

Step 4: Results verification

At this step, we receive the encrypted message, decrypt it, and check with the initial message. If the messages are the same, the applicant has passed verification, in which case, we calculate the transaction risk score and send you the applicantKytTxnApproved webhook to validate successful wallet confirmation.