Integrating D3 Infrastructure
Building With D3
Why Use D3's SDK
D3’s SDK offers a host of benefits including:
- Seamless transition between existing web (Internet) and Web3 infrastructure
- Futureproof design with backward and forward compatibility at its foundation
- Single integration without the need for endless SDKs, APIs and more
- Accessible, compliant and secure infrastructure
D3 Connect SDK
Overview
The D3 Connect SDK aims to bring different Web3 Name Resolution services under one umbrella to simplify integration efforts for developers. It supports the following resolution methods, which can be applied in order of priority:
- DNS Resolution
- (Optional) ENS resolution
- (Optional) Other resolution services - either provided by D3 or community.
SDK Documentation and Sample Code
D3 Connect is a unified JavaScript SDK to resolve Web3 names, built to be modular in nature and consisting of several resolution modules, which are applied in order of priority:
- DNS resolution module. Resolves Web3 names using DNS TXT records. This is a core module, which cannot be disabled and always applied first.
- ENS resolution module. Resolves Web3 names using ENS. Distributed as a separate
@d3-inc/d3connect-enspackage. - Other modules...
D3 Connect SDK can be used in both NodeJS and browser environments:
- Node 18+ (
fetchapi support is needed, this might be relaxed in future). - Browsers with ES2022 features support (basically all modern browsers).
@d3-inc/d3connect
This package provides a modular SDK itself and a DNS resolution module. It is a lightweight zero-dependency library, which can be used to perform basic Web3 Wallet address resolution using standard DNS protocol.
{% tabs %} {% tab title="NPM" %}
npm install @d3-inc/d3connect
{% endtab %}
{% tab title="Yarn" %}
yarn add @d3-inc/d3connect
{% endtab %} {% endtabs %}
For basic usage, no configuration is required since it comes with reasonable defaults:
import { D3Connect } from '@d3-inc/d3connect';
const d3Connect = new D3Connect();
// Resolves `example.core` name on `CORE` blockchain
const walletAddress = await d3Connect.resolve('example.core', 'CORE');
console.log(walletAddress);
// Reverse resolves wallet address `0xaaaa` on `CORE` blockchain
const domainName = await d3Connect.reverseResolve('0xaaaa', 'CORE');
console.log(domainName);
Our SDK is highly configurable, and can be customized for a variety of use cases:
const d3Connect = new D3Connect({
// DNS resolution module options (with defaults):
dns: {
forwarderDomain: 'forwarder.d3.app',
// Whether or not DNSSEC verification must be performed by a resolver
dnssecVerification: true,
// DNS-over-HTTPS resolver is provided by default. It uses dns-json format, which is supported by CloudFlare & Google resolvers.
// This could be substituted with different implementations by SDK consumers.
resolver: new DNSOverHTTPSResolver({
dnsServer: 'https://cloudflare-dns.com/dns-query',
}),
},
// Log level for the SDK log messages
logLevel: 'info', // "trace" | "info" | "warn" | "error" | "silent"
// If needed, custom logger implementation can be provided.
// By default, standard console logger is used.
logger: <ConsoleLogger>,
// In-memory cache is used by default to cache resolution result (and intermediate resolution data)
// To avoid memory leaks (in server scenarios), or use persistent cache (in browser scenarios), custom caching implementation can be provided.
caching: {
enabled: true,
cacheProvider: <InMemoryCache>,
},
});
Current version limitations:
- IDNA (Unicode) names resolution is not supported, names should be normalized and converted to Punycode before passing to SDK.
- Retries are not implemented for DNS queries.
- Need to provide a common interface to pass cross-cutting services (caching, logging) to the modules.
logLevelis not respected for external logger. Need to use an internal logging abstraction.networkargument is not properly validated. SLIP44 validation can be added.
@d3-inc/d3connect-ens
This is an optional module which adds support for ENS names resolution.
{% tabs %} {% tab title="NPM" %}
npm install @d3-inc/d3connect-ens
{% endtab %}
{% tab title="Yarn" %}
yarn add @d3-inc/d3connect-ens
{% endtab %} {% endtabs %}
Basic usage:
import { D3Connect } from '@d3-inc/d3connect';
import { ENSModule } from '@d3-inc/d3connect-ens';
const d3Connect = new D3Connect({ modules: [new ENSModule()] });
const walletAddress = await d3Connect.resolve('test.eth', 'ETH');
console.log(walletAddress);
Under the hood, it uses the @ensdomains/ensjs package, which in turn uses viem for blockchain integration. Viem chain and transport can be provided to further customize this module:
import { D3Connect } from '@d3-inc/d3connect';
import { ENSModule } from '@d3-inc/d3connect-ens';
import { http } from 'viem';
import { mainnet } from 'viem/chains';
const d3Connect = new D3Connect({
modules: [
new ENSModule({
// Be default, `viem` Ethereum Mainnet network is used.
// Custom network can be provided for testnet or private deployments.
chain: mainnet,
// By default, HTTP transport used
transport: http(),
}),
],
});
Current version limitations:
- ENS TTL is currently not used, so resolution results are not cached. Internal
@ensdomains/ensjsis used instead. - Provided
networkis not validated for support by@ensdomains/ensjs.
Custom resolution modules
Custom resolution modules can be written by implementing the D3ConnectModule interface:
export interface CustomModuleOptions {
// Module options here
}
export class CustomModule implements D3ConnectModule {
// Name is used for logging purposes
name = 'MyModuleName';
constructor(options?: CustomModuleOptions) {
// Initialize your module
}
async resolve(name: string, network: string): Promise<ResolutionResult | undefined> {
// Perform resolution of the provided name using custom logic.
// If name cannot be resolved by this module, return `undefined`.
return {
// Return resolved wallet address (MUST not be empty)
address: resolvedAddress,
// Return TTL for resolved address (in seconds)
// 0 can be returned to disable caching for this name
ttl: 30,
};
}
async reverseResolve(address: string, network: string): Promise<ReverseResolutionResult | undefined> {
// Perform reverse resolution of the provided address using custom logic.
// If address cannot be reverse resolved by this module, return `undefined`.
return {
// Return reverse resolved name (MUST not be empty)
name: resolvedName,
// Return TTL for resolved address (in seconds)
// 0 can be returned to disable caching for this name
ttl: 30,
};
}
To use it, pass it as a module in D3Connect options:
import { D3Connect } from '@d3-inc/d3connect';
const d3Connect = new D3Connect({ modules: [new CustomModule()] });
const walletAddress = await d3Connect.resolve('test.custom', 'ETH');
console.log(walletAddress);
DNS Wallet Address Resolution
D3 Connect provides wallet address resolution using existing DNS infrastructure. This is achieved using standard DNS TXT records on a special subdomain.
A DNS record with predefined format on a specific host must be set:
- Type:
TXT - Host:
_w3addr- specific name mandated by this standard. - Value:
KEY:VALUE,KEY:VALUE- comma-separated key-value list of web3 records. Format is intentionally generic to allow easy extensions with new record types in future (e.g. for DID).
{% hint style="info" %} Separate host for TXT records is used. This is needed for several purposes:
- To avoid using TXT record on parent name, which might be needed for other purposes.
- To bypass limits on TXT record value length (CloudFlare has 2048 characters limit), multiple TXT records can be specified. During resolution, key-value pairs from different TXT records are joined together. Order is not guaranteed, so records shouldn’t have duplicate keys, otherwise resolution will become non-deterministic. {% endhint %}
For wallet address resolution the following records are used: <SYMBOL>:<ADDRESS>:
<SYMBOL>is a chain symbol to resolve. It should be taken from the SLIP44 spec.<ADDRESS>is an address in a chain-specific format.
Example:
_w3addr IN TXT "CORE:0xaaa,MATIC:0xbbbb,SOL:0xcccc"
{% hint style="warning" %} DNSSEC must be enabled on domain names. Without this, the resolution process MUST be aborted, since this opens the possibility for security vulnerabilities (like DNS Cache Poisoning). {% endhint %}
For EOIs, we can’t create DNS records since the TLD is not yet available, so we use a dedicated DNS name as a parent until the TLD is registered. For example, instead of using example.core we can put records under a forwarder DNS name, e.g. example.core.vana.
With the above format in mind, the wallet address resolution process using DNS consists of the following steps:
- Try to resolve
_w3addrTXT records under DNS name (_w3addr.example.core). - If the TLD is not resolved, fallback to resolution using the forwarder address (
_w3addr.example.core.vana). - If the record is not found, abort.
- If DNSSEC is not enabled or validation fails, abort.
- Parse web3 records from a response.
- If needed, join the parsed records together from multiple TXT records.
- Return the resolved web3 records to a consumer.
{% hint style="info" %} Ideally, caching SHOULD be employed by a resolver to make sure infrequently changed data (like DNSSEC keys or TLD registration status) are not requested unnecessarily. {% endhint %}
Reverse Wallet Address Resolution
D3 Connect provides reverse wallet address resolution using existing DNS infrastructure. This is achieved using standard DNS CNAME records on a special subdomain.
A DNS record with predefined format on a specific host must be set:
- Type:
CNAME - Host:
<WALLET_ADDRESS>.wallet.vana- specific name mandated by this standard - Value:
<NAME>- The resolved name for the specified wallet address
For chain-specific reverse mappings, a CNAME record in the following format must be set:\
<WALLET_ADDRESS>.<SYMBOL>.wallet.vana
<SYMBOL>is a chain symbol to resolve. It should be taken from the SLIP44 spec.
When attempting to reverse resolve a wallet address, D3 Connect SDK will first attempt to lookup the chain-specific CNAME record <WALLET_ADDRESS>.<SYMBOL>.wallet.vana and if it does not exist, it will fallback to the default <WALLET_ADDRESS>.wallet.vana CNAME record.
Examples:
0xaaa.wallet.vana IN CNAME example.core
0xbbb.core.wallet.vana IN CNAME example.core