Connex
Connex is the standard framework for interacting with vechain.
Regular requests with connex require:
- A valid connex instance
- ABI object for all calls
- 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))