Crypto payments

Table of contents

Overview

The most common way to integrate with Unstoppable Domains is through simple domain resolution. This process converts a human-readable name like brad.crypto to the cryptocurrency addresses that name stores. As long as the addresses are set, a user can send any of our 80+ supported cryptocurrencies to an Unstoppable Domain and it will end up in the right place. A user can send BTC to brad.crypto and it will go to Brad's BTC address. A user can send ETH to brad.crypto and will go to Brad's ETH address. Unstoppable Domains support 80+ cryptocurrencies and counting.

A successful domain resolution and payment

How does this work?

At a high-level, an application retrieves a domain's records through smart contracts deployed on the Ethereum and/or Zilliqa blockchains (.crypto and .zil, respectively).

In the example above, we're sending 1 ETH to ryan.crypto. The application sends those two parameters to the Resolver contract on the Ethereum blockchain and it returns the record stored under crypto.ETH.address for that domain. This address can be used to complete the ETH transfer to Ryan.

A domain can store many records and key formats. To learn about our supported record types, see Records reference guide.

Data movement for a successful payment

Enabling domain resolution

The easiest way to integrate domain resolution for crypto payments is by using the Unstoppable Domains resolution libraries. These libraries communicate with the Ethereum and Zilliqa blockchains directly so that you don't have to.

Resolving crypto records

Example: Resolving ryan.crypto into Ethereum address

To resolve ryan.crypto into its Ethereum address, the library searches for the crypto.ETH.address record attached to the domain.

resolution
resolution-java
resolution-swift
resolution
const {default: Resolution} = require('@unstoppabledomains/resolution');
const resolution = new Resolution();
resolution
.addr('ryan.crypto', 'ETH')
.then((receiverETHAddress) => {
// receiverETHAddress consists receiver ethereum address
// use this address as recipient of the payment
})
.catch(console.error);
resolution-java
import com.unstoppabledomains.resolution.Resolution
...
DomainResolution resolution = new Resolution();
String receiverETHAddress = resolution.getAddress("ryan.crypto", "ETH");
// receiverETHAddress consists receiver ethereum address
// use this address as recipient of the payment
resolution-swift
import UnstoppableDomainsResolution
guard let resolution = try? Resolution() else {
print ("Init of Resolution instance with default parameters failed...")
return
}
resolution.addr(domain: "ryan.crypto", ticker: "ETH") { result in
switch result {
case .success(let returnValue):
let receiverETHAddress = returnValue
// receiverETHAddress consists receiver ethereum address
// use this address as recipient of the payment
case .failure(let error):
print("Expected eth Address, but got \(error)")
}
}

Our libraries use Infura to interact with the Ethereum blockchain by default. To configure a custom Ethereum provider see our Library configuration guide.

Records involved

In the code above, the addr() and getAddr() methods convert the provided 3-letter ticker into the format crypto.<TICKER>.address. In this example, ETH ticker becomes crypto.ETH.address. The library uses crypto.ETH.address and the domain name to query the blockchain.

Example: Resolve udtestdev-usdt.crypto into USDT-ERC20 address

The USDT currency exists on multiple blockchains. Our libraries provide a dedicated method to look up cryptocurrency addresses for different blockchains.

resolution
resolution-java
resolution-swift
resolution
const {default: Resolution} = require('@unstoppabledomains/resolution');
const resolution = new Resolution();
resolution
.multiChainAddr('udtestdev-usdt.crypto', 'USDT', 'ERC20')
.then((receiverUSDTAddress) => {
// receiverUSDTAddress consists address for receiving USDT on Ethereum (ERC20 version)
// use this address as recipient of the payment
})
.catch(console.error);
resolution-java
import com.unstoppabledomains.resolution.Resolution
...
DomainResolution resolution = new Resolution();
String receiverUSDTAddress = resolution.getMultiChainAddress("udtestdev-usdt.crypto", "USDT", "ERC20");
// receiverUSDTAddress consists address for receiving USDT on Ethereum (ERC20 version)
// use this address as recipient of the payment
resolution-swift
import UnstoppableDomainsResolution
guard let resolution = try? Resolution() else {
print ("Init of Resolution instance with default parameters failed...")
return
}
resolution.multiChainAddress(domain: "udtestdev-usdt.crypto", ticker: "USDT", chain: "ERC20") { (result) in
switch result {
case .success(let returnValue):
receiverUSDTAddress = returnValue;
// receiverUSDTAddress consists address for receiving USDT on Ethereum (ERC20 version)
// use this address as recipient of the payment
case .failure(let error):
print("Expected USDT-ETC20 Address, but got \(error)")
}
}

Records involved

The multiChainAddress() and getMultiChainAddress() methods create a key from the provided USDT ticker and ERC20 version. The key format is crypto.USDT.version.<VERSION>.address. In the example above with the ERC-20 version of USDT, the created key would be crypto.USDT.version.ERC20.address.

Information about supported crypto payment tickers and USDT versions — Managing domain records

Error handling

Error example

Common error cases to handle:

  • Domain is not registered

  • Crypto record is not found (or empty)

  • Domain is not configured (empty resolver)

  • Domain is not supported

resolution
resolution-java
resolution-swift
resolution
const {default: Resolution} = require('@unstoppabledomains/resolution');
const resolution = new Resolution();
resolution
.addr('domain-with-error.crypto', 'ETH')
.then((ethAddress) => {
})
.catch((error) => {
if (error.code === 'UnregisteredDomain') {
console.log('Domain is not registered')
}
if (error.code === 'RecordNotFound') {
console.log('Crypto record is not found (or empty)')
}
if (error.code === 'UnspecifiedResolver') {
console.log('Domain is not configured (empty resolver)')
}
if (error.code === 'UnsupportedDomain') {
console.log('Domain is not supported')
}
});

To see all supported error codes please check resolution library api docs

resolution-java
import com.unstoppabledomains.resolution.Resolution
import com.unstoppabledomains.exceptions.ns.NamingServiceException
import com.unstoppabledomains.exceptions.ns.NSExceptionCode
...
DomainResolution resolution = new Resolution();
try {
String receiverETHAddress = resolution.getAddress("domain-with-error.crypto", "ETH");
} catch (NamingServiceException exception) {
if (exception.getCode() == NSExceptionCode.UnregisteredDomain) {
// Domain is not registered
}
if (exception.getCode() == NSExceptionCode.RecordNotFound) {
// Crypto record is not found (or empty)
}
if (exception.getCode() == NSExceptionCode.UnspecifiedResolver) {
// Domain is not configured (empty resolver)
}
if (exception.getCode() == NSExceptionCode.UnsupportedDomain) {
// Domain is not supported
}
}

To see all supported error codes please check resolution-java readme

resolution-swift
import UnstoppableDomainsResolution
guard let resolution = try? Resolution() else {
print ("Init of Resolution instance with default parameters failed...")
return
}
resolution.addr(domain: "domain-with-error.crypto", ticker: "ETH") { result in
switch result {
case .success(let returnValue):
// Success flow
case .failure(let error):
switch error {
case ResolutionError.unregisteredDomain:
// Domain is not registered
break;
case ResolutionError.recordNotFound:
// Crypto record is not found (or empty)
break;
case ResolutionError.unspecifiedResolver:
// Domain is not configured (empty resolver)
break;
case ResolutionError.unsupportedDomain:
// Domain is not supported
break;
}
}
}

To see all supported error codes please check resolution-swift readme

Always check address validity after receiving a result from the library. The user has full control over the domain and is able to set any value - even values which are invalid.

Integration best practices

  • Always display the resolved address near the domain name for additional security.

  • Don’t overwrite the input field with the cryptocurrency address.

  • Always try to resolve a domain with the provided currency code.

  • Always handle resolution errors according to error type.

Best practices

Resources