feat: finsihed adding utility commands
ce843c47
10 file(s) · +216 −51
| 1 | + | { |
|
| 2 | + | "$schema": "https://biomejs.dev/schemas/2.2.0/schema.json", |
|
| 3 | + | "vcs": { |
|
| 4 | + | "enabled": false, |
|
| 5 | + | "clientKind": "git", |
|
| 6 | + | "useIgnoreFile": false |
|
| 7 | + | }, |
|
| 8 | + | "files": { |
|
| 9 | + | "ignoreUnknown": false |
|
| 10 | + | }, |
|
| 11 | + | "formatter": { |
|
| 12 | + | "enabled": true, |
|
| 13 | + | "indentStyle": "tab" |
|
| 14 | + | }, |
|
| 15 | + | "linter": { |
|
| 16 | + | "enabled": true, |
|
| 17 | + | "rules": { |
|
| 18 | + | "recommended": true |
|
| 19 | + | } |
|
| 20 | + | }, |
|
| 21 | + | "javascript": { |
|
| 22 | + | "formatter": { |
|
| 23 | + | "quoteStyle": "double" |
|
| 24 | + | } |
|
| 25 | + | }, |
|
| 26 | + | "assist": { |
|
| 27 | + | "enabled": true, |
|
| 28 | + | "actions": { |
|
| 29 | + | "source": { |
|
| 30 | + | "organizeImports": "on" |
|
| 31 | + | } |
|
| 32 | + | } |
|
| 33 | + | } |
|
| 34 | + | } |
| 7 | 7 | }, |
|
| 8 | 8 | "scripts": { |
|
| 9 | 9 | "build": "bun build src/index.ts --outdir dist --target node", |
|
| 10 | - | "dev": "bun run build && bun link" |
|
| 10 | + | "dev": "bun run build && bun link", |
|
| 11 | + | "lint": "bunx @biome-js/biome lint --write src", |
|
| 12 | + | "format": "bunx @biome-js/biome format --write src" |
|
| 11 | 13 | }, |
|
| 12 | 14 | "devDependencies": { |
|
| 13 | 15 | "@types/bun": "latest" |
| 1 | 1 | export * from "./profile"; |
|
| 2 | 2 | export * from "./resolve"; |
|
| 3 | + | export * from "./utils"; |
| 1 | 1 | import { getRecords } from "@ensdomains/ensjs/public"; |
|
| 2 | 2 | import { getSubgraphRecords } from "@ensdomains/ensjs/subgraph"; |
|
| 3 | - | import { type Address, isHex } from "viem"; |
|
| 4 | - | import { normalize } from "viem/ens"; |
|
| 3 | + | import type { Address } from "viem"; |
|
| 5 | 4 | import colors from "yoctocolors"; |
|
| 6 | - | import { spinner } from "../utils/spinner"; |
|
| 7 | - | import type { ResolveOptions } from "../utils/types"; |
|
| 8 | - | import { ensClient } from "../utils/viem"; |
|
| 5 | + | import { |
|
| 6 | + | ensClient, |
|
| 7 | + | getNameAndAddress, |
|
| 8 | + | type ResolveOptions, |
|
| 9 | + | spinner, |
|
| 10 | + | } from "../utils"; |
|
| 9 | 11 | ||
| 10 | 12 | function printProfile( |
|
| 11 | 13 | name: string | null, |
|
| 102 | 104 | } |
|
| 103 | 105 | ||
| 104 | 106 | export async function profile(options: ResolveOptions) { |
|
| 105 | - | let name: string | null; |
|
| 106 | - | let address: Address | null; |
|
| 107 | - | let input: "address" | "name"; |
|
| 108 | - | ||
| 109 | 107 | spinner.start(); |
|
| 110 | 108 | ||
| 111 | - | // Handle name or address |
|
| 112 | - | if (isHex(options.input)) { |
|
| 113 | - | address = options.input; |
|
| 114 | - | input = "address"; |
|
| 115 | - | name = await ensClient.getEnsName({ |
|
| 116 | - | address: input as Address, |
|
| 117 | - | }); |
|
| 118 | - | } else { |
|
| 119 | - | name = options.input; |
|
| 120 | - | input = "name"; |
|
| 121 | - | address = await ensClient.getEnsAddress({ |
|
| 122 | - | name: normalize(options.input as string), |
|
| 123 | - | }); |
|
| 109 | + | const { name, address } = await getNameAndAddress(options); |
|
| 110 | + | ||
| 111 | + | if (!name || !address) { |
|
| 112 | + | spinner.stop(); |
|
| 113 | + | console.log("404: Name not found"); |
|
| 114 | + | return; |
|
| 124 | 115 | } |
|
| 125 | 116 | ||
| 126 | 117 | try { |
|
| 1 | 1 | import { getContentHashRecord, getRecords } from "@ensdomains/ensjs/public"; |
|
| 2 | - | import { type Address, isHex } from "viem"; |
|
| 3 | 2 | import { normalize } from "viem/ens"; |
|
| 4 | - | import type { ResolveOptions } from "../utils/types"; |
|
| 5 | - | import { ensClient } from "../utils/viem"; |
|
| 6 | - | import { spinner } from "../utils/spinner"; |
|
| 3 | + | import { |
|
| 4 | + | ensClient, |
|
| 5 | + | getNameAndAddress, |
|
| 6 | + | type ResolveOptions, |
|
| 7 | + | spinner, |
|
| 8 | + | } from "../utils"; |
|
| 7 | 9 | ||
| 8 | 10 | export async function resolve(options: ResolveOptions) { |
|
| 9 | - | let name: string | null; |
|
| 10 | - | let address: Address | null; |
|
| 11 | - | let input: "address" | "name"; |
|
| 12 | 11 | spinner.start(); |
|
| 13 | 12 | ||
| 14 | - | // Handle name or address |
|
| 15 | - | if (isHex(options.input)) { |
|
| 16 | - | address = options.input; |
|
| 17 | - | input = "address"; |
|
| 18 | - | name = await ensClient.getEnsName({ |
|
| 19 | - | address: input as Address, |
|
| 20 | - | }); |
|
| 21 | - | } else { |
|
| 22 | - | name = options.input; |
|
| 23 | - | input = "name"; |
|
| 24 | - | address = await ensClient.getEnsAddress({ |
|
| 25 | - | name: normalize(options.input as string), |
|
| 26 | - | }); |
|
| 13 | + | const { name, address, input } = await getNameAndAddress(options); |
|
| 14 | + | ||
| 15 | + | if (!name || !address) { |
|
| 16 | + | spinner.stop(); |
|
| 17 | + | console.log("404: Name not found"); |
|
| 18 | + | return; |
|
| 27 | 19 | } |
|
| 28 | 20 | ||
| 29 | 21 | // Handle TXT |
| 1 | + | import { labelhash, namehash } from "viem"; |
|
| 2 | + | import { ensClient, spinner } from "../utils"; |
|
| 3 | + | import { normalize } from "viem/ens"; |
|
| 4 | + | import { addresses } from "@ensdomains/ensjs/contracts"; |
|
| 5 | + | ||
| 6 | + | export async function getLabelHash(options: { name: string }) { |
|
| 7 | + | spinner.start(); |
|
| 8 | + | const res = labelhash(options.name as string); |
|
| 9 | + | spinner.stop(); |
|
| 10 | + | console.log(res); |
|
| 11 | + | } |
|
| 12 | + | ||
| 13 | + | export async function getNamehash(options: { name: string }) { |
|
| 14 | + | spinner.start(); |
|
| 15 | + | const res = namehash(options.name as string); |
|
| 16 | + | spinner.stop(); |
|
| 17 | + | console.log(res); |
|
| 18 | + | } |
|
| 19 | + | ||
| 20 | + | export async function getResolver({ name }: { name: string }) { |
|
| 21 | + | spinner.start(); |
|
| 22 | + | const resolver = await ensClient.getEnsResolver({ |
|
| 23 | + | name: normalize(name), |
|
| 24 | + | }); |
|
| 25 | + | spinner.stop(); |
|
| 26 | + | console.log(resolver); |
|
| 27 | + | } |
|
| 28 | + | ||
| 29 | + | export function getDeployments() { |
|
| 30 | + | for (const [chainId, contracts] of Object.entries(addresses)) { |
|
| 31 | + | console.log(`Chain ID: ${chainId}`); |
|
| 32 | + | for (const [contractName, contractData] of Object.entries(contracts)) { |
|
| 33 | + | console.log(` ${contractName}: ${contractData.address}`); |
|
| 34 | + | } |
|
| 35 | + | } |
|
| 36 | + | } |
| 10 | 10 | string, |
|
| 11 | 11 | subcommands, |
|
| 12 | 12 | } from "cmd-ts"; |
|
| 13 | - | import { profile as profileCmd, resolve as resolveCmd } from "./commands"; |
|
| 13 | + | import { |
|
| 14 | + | getDeployments, |
|
| 15 | + | getLabelHash, |
|
| 16 | + | getNamehash, |
|
| 17 | + | getResolver, |
|
| 18 | + | profile as profileCmd, |
|
| 19 | + | resolve as resolveCmd, |
|
| 20 | + | } from "./commands"; |
|
| 14 | 21 | ||
| 15 | 22 | const resolve = command({ |
|
| 16 | 23 | name: "resolve", |
|
| 49 | 56 | }); |
|
| 50 | 57 | ||
| 51 | 58 | const profile = command({ |
|
| 52 | - | name: "resolve", |
|
| 59 | + | name: "profile", |
|
| 53 | 60 | description: "Resolve an ENS name to an address or vice versa", |
|
| 54 | 61 | args: { |
|
| 55 | 62 | input: positional({ |
|
| 60 | 67 | handler: async (args) => { |
|
| 61 | 68 | if (!args.input) { |
|
| 62 | 69 | console.log( |
|
| 63 | - | "Please provide an address or ENS Name `atlas resolve <ADDRESS | vitalik.eth>`", |
|
| 70 | + | "Please provide an address or ENS Name `atlas profile <ADDRESS | vitalik.eth>`", |
|
| 64 | 71 | ); |
|
| 65 | 72 | return; |
|
| 66 | 73 | } |
|
| 68 | 75 | }, |
|
| 69 | 76 | }); |
|
| 70 | 77 | ||
| 78 | + | const namehash = command({ |
|
| 79 | + | name: "namehash", |
|
| 80 | + | description: "Get a namehash for an ENS name", |
|
| 81 | + | args: { |
|
| 82 | + | name: positional({ |
|
| 83 | + | type: string, |
|
| 84 | + | description: "ENS name for the namehash", |
|
| 85 | + | }), |
|
| 86 | + | }, |
|
| 87 | + | handler: async (args) => { |
|
| 88 | + | if (!args.name) { |
|
| 89 | + | console.log("Please provide an ENS Name `atlas namehash <vitalik.eth>`"); |
|
| 90 | + | return; |
|
| 91 | + | } |
|
| 92 | + | await getNamehash(args); |
|
| 93 | + | }, |
|
| 94 | + | }); |
|
| 95 | + | ||
| 96 | + | const labelhash = command({ |
|
| 97 | + | name: "labelhash", |
|
| 98 | + | description: "Get a labelhash for an ENS name", |
|
| 99 | + | args: { |
|
| 100 | + | name: positional({ |
|
| 101 | + | type: string, |
|
| 102 | + | description: "ENS for the labelhash", |
|
| 103 | + | }), |
|
| 104 | + | }, |
|
| 105 | + | handler: async (args) => { |
|
| 106 | + | if (!args.name) { |
|
| 107 | + | console.log("Please provide an ENS Name `atlas labelhash <vitalik.eth>`"); |
|
| 108 | + | return; |
|
| 109 | + | } |
|
| 110 | + | await getLabelHash(args); |
|
| 111 | + | }, |
|
| 112 | + | }); |
|
| 113 | + | ||
| 114 | + | const resolver = command({ |
|
| 115 | + | name: "resolver", |
|
| 116 | + | description: "Get the current resolver for an ENS Name", |
|
| 117 | + | args: { |
|
| 118 | + | name: positional({ |
|
| 119 | + | type: string, |
|
| 120 | + | description: "Target ENS name for the resolver query", |
|
| 121 | + | }), |
|
| 122 | + | }, |
|
| 123 | + | handler: async (args) => { |
|
| 124 | + | if (!args.name) { |
|
| 125 | + | console.log("Please provide an ENS Name `atlas resolver <vitalik.eth>`"); |
|
| 126 | + | return; |
|
| 127 | + | } |
|
| 128 | + | await getResolver(args); |
|
| 129 | + | }, |
|
| 130 | + | }); |
|
| 131 | + | ||
| 132 | + | const deployments = command({ |
|
| 133 | + | name: "deployments", |
|
| 134 | + | description: "Print a list of currently deployed ENS contracts", |
|
| 135 | + | args: {}, |
|
| 136 | + | handler: () => { |
|
| 137 | + | getDeployments(); |
|
| 138 | + | }, |
|
| 139 | + | }); |
|
| 140 | + | ||
| 71 | 141 | const cli = subcommands({ |
|
| 72 | 142 | name: "atlas", |
|
| 73 | 143 | description: "Explore ENS with Atlas", |
|
| 75 | 145 | cmds: { |
|
| 76 | 146 | profile, |
|
| 77 | 147 | resolve, |
|
| 148 | + | namehash, |
|
| 149 | + | labelhash, |
|
| 150 | + | resolver, |
|
| 151 | + | deployments, |
|
| 78 | 152 | }, |
|
| 79 | 153 | }); |
|
| 80 | 154 | ||
| 1 | + | import { isHex, type Address } from "viem"; |
|
| 2 | + | import { normalize } from "viem/ens"; |
|
| 3 | + | import { ensClient, type ResolveOptions } from "./"; |
|
| 4 | + | ||
| 5 | + | export async function getNameAndAddress(options: ResolveOptions) { |
|
| 6 | + | let name: string | null = null; |
|
| 7 | + | let address: Address | null = null; |
|
| 8 | + | let input: "address" | "name"; |
|
| 9 | + | ||
| 10 | + | try { |
|
| 11 | + | // Handle name or address |
|
| 12 | + | if (isHex(options.input)) { |
|
| 13 | + | address = options.input; |
|
| 14 | + | input = "address"; |
|
| 15 | + | name = await ensClient.getEnsName({ |
|
| 16 | + | address: options.input as Address, |
|
| 17 | + | }); |
|
| 18 | + | } else { |
|
| 19 | + | name = options.input; |
|
| 20 | + | input = "name"; |
|
| 21 | + | address = await ensClient.getEnsAddress({ |
|
| 22 | + | name: normalize(options.input as string), |
|
| 23 | + | }); |
|
| 24 | + | } |
|
| 25 | + | ||
| 26 | + | return { name, address, input }; |
|
| 27 | + | } catch (_error) { |
|
| 28 | + | console.error(`Failed to resolve: ${options.input}`); |
|
| 29 | + | return { name: null, address: null, input: "name" }; |
|
| 30 | + | } |
|
| 31 | + | } |
| 1 | + | export * from "./general"; |
|
| 2 | + | export * from "./spinner"; |
|
| 3 | + | export * from "./types"; |
|
| 4 | + | export * from "./viem"; |
| 1 | 1 | import type { Address } from "viem"; |
|
| 2 | 2 | ||
| 3 | 3 | export type ResolveOptions = { |
|
| 4 | - | input: string | Address | null; |
|
| 5 | - | chain?: string; |
|
| 6 | - | contenthash?: boolean; |
|
| 7 | - | txt?: string; |
|
| 8 | - | } |
|
| 4 | + | input: string | Address | null; |
|
| 5 | + | chain?: string; |
|
| 6 | + | contenthash?: boolean; |
|
| 7 | + | txt?: string; |
|
| 8 | + | }; |