Developer Guide
Connex

Connex

Connex is the standard framework for interacting with vechain.

Regular requests with connex require:

  1. A valid connex instance
  2. ABI object for all calls
  3. ethers for hashing when interacting with the registry directly

Resolve Utility

Resolve Names to Addresses

const utilityAddress = '0xA11413086e163e41901bb81fdc5617c975Fa5a1A' // MainNet Deployment
 
const {
  decoded: { addresses },
} = await connex.thor
  .account(utilityAddress)
  .method({
    inputs: [
      {
        internalType: "string[]",
        name: "names",
        type: "string[]",
      },
    ],
    name: "getAddresses",
    outputs: [
      {
        internalType: "address[]",
        name: "addresses",
        type: "address[]",
      },
    ],
    stateMutability: "view",
    type: "function",
  })
  .call(["hello.vet", "test.vet"]);

Get Primary Names for Addresses

const utilityAddress = '0xA11413086e163e41901bb81fdc5617c975Fa5a1A' // MainNet Deployment
 
const {
  decoded: { names },
} = await connex.thor
  .account(utilityAddress)
  .method({
    inputs: [
      {
        internalType: "address[]",
        name: "addresses",
        type: "address[]",
      },
    ],
    name: "getNames",
    outputs: [
      {
        internalType: "string[]",
        name: "names",
        type: "string[]",
      },
    ],
    stateMutability: "view",
    type: "function",
  })
  .call([
    "0x981ebf8f1f98465f93fd0208a0b5e531ddc37815",
    "0x105199a26b10e55300cb71b46c5b5e867b7df427",
  ]);

Pure Registry

Get Address for a Name

import { namehash } from 'ethers
import { Connex } from '@vechain/connex'
 
// create connex instance
const connex = new Connex({
  node: 'https://node-mainnet.vechain.energy/',
  network: 'main'
})
 
// what and where to resolve
const registryAddress = '0xa9231da8BF8D10e2df3f6E03Dd5449caD600129b' // MainNet Registry
const name = 'hello.vet'
 
// convert name into bytes32 hash
const node = namehash(name)
 
// Step 1: get a resolver that is responsible for the entry
const { decoded: { '0': resolverAddress } } = await connex.thor.account(registryAddress)
    .method({
        "inputs": [
            {
                "internalType": "bytes32",
                "name": "node",
                "type": "bytes32"
            }
        ],
        "name": "resolver",
        "outputs": [
            {
                "internalType": "address",
                "name": "",
                "type": "address"
            }
        ],
        "stateMutability": "view",
        "type": "function"
    })
    .call(node)
 
// Step 2: ask the resolver about the current address setting
// it can be a zero address, if the user has not set one
const { decoded: { '0': address } } = await connex.thor.account(resolverAddress)
    .method({
        "inputs": [
            {
                "internalType": "bytes32",
                "name": "node",
                "type": "bytes32"
            }
        ],
        "name": "addr",
        "outputs": [
            {
                "internalType": "address payable",
                "name": "",
                "type": "address"
            }
        ],
        "stateMutability": "view",
        "type": "function"
    })
    .call(node)
 
console.log(name, 'resolves to', address, 'on resolver', resolverAddress)

Get Primary Name for Address

Get the primary name set for a given address.

import { namehash } from 'ethers
import { Connex } from '@vechain/connex'
 
// create connex instance
const connex = new Connex({
  node: 'https://node-mainnet.vechain.energy/',
  network: 'main'
})
 
// what and where to resolve
const registryAddress = '0xa9231da8BF8D10e2df3f6E03Dd5449caD600129b' // MainNet Registry
const address = '0x981ebf8F1F98465F93fd0208a0b5e531DdC37815' // the address to lookup
 
// the reverse lookup is handled at a different suffix `.addr.reverse` in lower case without the leading 0x
const reverseLookup = `${address.slice(2).toLowerCase()}.addr.reverse`;
const node = namehash(reverseLookup);
 
// Step 1: get a resolver that is responsible for the entry
const { decoded: { '0': resolverAddress } } = await connex.thor.account(registryAddress)
    .method({
        "inputs": [
            {
                "internalType": "bytes32",
                "name": "node",
                "type": "bytes32"
            }
        ],
        "name": "resolver",
        "outputs": [
            {
                "internalType": "address",
                "name": "",
                "type": "address"
            }
        ],
        "stateMutability": "view",
        "type": "function"
    })
    .call(node)
 
// Step 2: ask the resolver the name of that id
const { decoded: { 0: name }   } = await connex.thor.account(resolverAddress)
    .method({
      inputs: [
        {
          internalType: "bytes32",
          name: "node",
          type: "bytes32",
        },
      ],
      name: "name",
      outputs: [
        {
          internalType: "string",
          name: "",
          type: "string",
        },
      ],
      stateMutability: "view",
      type: "function",
    })
    .call(node);
 
console.log(address, 'primary name is', name, 'on resolver', resolverAddress)

Tools

Helper Functions

import { namehash, isAddress } from "ethers";
import { Connex } from "@vechain/connex";
 
const VET_REGISTRY_ADDRESS = "0xa9231da8BF8D10e2df3f6E03Dd5449caD600129b";
 
const ABI = {
  resolver: {
    inputs: [{ internalType: "bytes32", name: "node", type: "bytes32" }],
    name: "resolver",
    outputs: [{ internalType: "address", name: "resolverAddress", type: "address" }],
    stateMutability: "view",
    type: "function",
  },
 
  addr: {
    inputs: [{ internalType: "bytes32", name: "node", type: "bytes32" }],
    name: "addr",
    outputs: [{ internalType: "address payable", name: "address", type: "address" }],
    stateMutability: "view",
    type: "function",
  },
 
  name: {
    inputs: [{ internalType: "bytes32", name: "node", type: "bytes32" }],
    name: "name",
    outputs: [{ internalType: "string", name: "name", type: "string" }],
    stateMutability: "view",
    type: "function",
  },
};
 
export async function getRecord(name: string, connex: Connex): Promise<string> {
  const node = namehash(name);
 
  const { decoded: { resolverAddress } } = await connex.thor.account(VET_REGISTRY_ADDRESS).method(ABI.resolver).call(node);
  const { decoded: { address } } = await connex.thor.account(resolverAddress).method(ABI.addr).call(node);
 
  return address;
}
 
export async function getName(address: string, connex: Connex): Promise<string> {
  const reverseLookup = `${address.slice(2).toLowerCase()}.addr.reverse`;
  const node = namehash(reverseLookup);
 
  const { decoded: { resolverAddress } } = await connex.thor.account(VET_REGISTRY_ADDRESS).method(ABI.resolver).call(node);
  const { decoded: { name } } = await connex.thor.account(resolverAddress).method(ABI.name).call(node);
 
  return name;
}
 
export async function getAddress(addressOrName: string, connex: Connex): Promise<string> {
  return isAddress(addressOrName) ? addressOrName : getRecord(addressOrName, connex)
}

The function can be used like this:

import { getRecord, getName, getAddress } from "./vet-domains/utils.ts";
 
const connex = new Connex({
    node: "https://node-mainnet.vechain.energy",
    network: "main",
});
 
const address = await getRecord("hello.vet", connex);
const primaryName = await getName(address, connex);
 
await getAddress("hello.vet", connex) // will resolve to the address behind the name
await getAddress(address, connex)     // will return the address

@vechain.energy/connex-utils

With @vechain.energy/connex-utils (opens in a new tab) basic name lookup functionality is provided.

Example using Node.js with Connex:

const { getConnex, getAddress, getRecord, getName } = require('@vechain.energy/connex-utils')
 
async function main() {
    const connex = await getConnex({
        node: [
            'https://mainnet.veblocks.net',
            'https://node.vechain.energy'
        ]
    })
 
    const testName = 'hello.vet'
    const address = await getRecord(testName, connex)
    console.log(testName, 'resolves to', address)
 
    const name = await getName(address, connex)
    console.log('primary name of', address, 'is', name)
 
    const addressTest1 = await getAddress(address, connex)
    const addressTest2 = await getAddress(name, connex)
 
    console.log('getAddress(address) replies with', addressTest1)
    console.log('getAddress(name) replies with', addressTest2)
}
 
main()
    .then(() => process.exit(0))
    .catch(err => console.error(err))