hawk-names 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,6 @@
1
+ export { base, baseSepolia, withHawk, } from "./chains.js";
2
+ export { HAWK_ADDRESSES, getHawkAddresses, hawkAddressesFrom, type HawkAddresses, } from "./addresses.js";
3
+ export { getHawkName, getHawkAddress, getHawkText, getHawkAvatar, } from "./actions.js";
4
+ export { HAWK_NODE, REVERSE_RECORD_NONE, REVERSE_RECORD_CHAIN, REVERSE_RECORD_DEFAULT, SECONDS_PER_YEAR, MIN_REGISTRATION_DURATION_MAINNET, MAX_REGISTRATION_DURATION, type Registration, makeRegistration, makeCommitment, randomSecret, hawkNode, hawkTokenId, validateLabel, } from "./registration.js";
5
+ export { normalize, namehash, labelhash } from "viem/ens";
6
+ export * from "./generated/abis.js";
package/dist/index.js ADDED
@@ -0,0 +1,13 @@
1
+ // hawk-names — resolve and register .hawk names on Base.
2
+ //
3
+ // One-line dapp integration:
4
+ // import { createPublicClient, http } from "viem";
5
+ // import { base } from "hawk-names";
6
+ // const client = createPublicClient({ chain: base, transport: http() });
7
+ // await client.getEnsName({ address: "0x..." }); // → "trader.hawk"
8
+ export { base, baseSepolia, withHawk, } from "./chains.js";
9
+ export { HAWK_ADDRESSES, getHawkAddresses, hawkAddressesFrom, } from "./addresses.js";
10
+ export { getHawkName, getHawkAddress, getHawkText, getHawkAvatar, } from "./actions.js";
11
+ export { HAWK_NODE, REVERSE_RECORD_NONE, REVERSE_RECORD_CHAIN, REVERSE_RECORD_DEFAULT, SECONDS_PER_YEAR, MIN_REGISTRATION_DURATION_MAINNET, MAX_REGISTRATION_DURATION, makeRegistration, makeCommitment, randomSecret, hawkNode, hawkTokenId, validateLabel, } from "./registration.js";
12
+ export { normalize, namehash, labelhash } from "viem/ens";
13
+ export * from "./generated/abis.js";
@@ -0,0 +1,49 @@
1
+ import { type Address, type Hex } from "viem";
2
+ /** namehash('hawk') */
3
+ export declare const HAWK_NODE: "0xf431efc1fb1b854a38191ac67de7e1ee70205b40f71cb927c938685e82e05403";
4
+ /** Reverse-record bits for Registration.reverseRecord. */
5
+ export declare const REVERSE_RECORD_NONE = 0;
6
+ export declare const REVERSE_RECORD_CHAIN = 1;
7
+ export declare const REVERSE_RECORD_DEFAULT = 2;
8
+ /** Mirror of HawkRegistrarController's Registration struct. */
9
+ export type Registration = {
10
+ label: string;
11
+ owner: Address;
12
+ duration: bigint;
13
+ secret: Hex;
14
+ resolver: Address;
15
+ data: readonly Hex[];
16
+ reverseRecord: number;
17
+ referrer: Hex;
18
+ };
19
+ export declare function makeRegistration(params: {
20
+ label: string;
21
+ owner: Address;
22
+ duration: bigint;
23
+ secret: Hex;
24
+ resolver?: Address;
25
+ data?: readonly Hex[];
26
+ reverseRecord?: number;
27
+ referrer?: Hex;
28
+ }): Registration;
29
+ /**
30
+ * Computes the commit-reveal commitment for a registration —
31
+ * `keccak256(abi.encode(registration))`, byte-identical to the
32
+ * controller's `makeCommitment`.
33
+ */
34
+ export declare function makeCommitment(registration: Registration): Hex;
35
+ /** Random 32-byte commit-reveal secret (browser + node). */
36
+ export declare function randomSecret(): Hex;
37
+ /** The namehash of `<label>.hawk`. */
38
+ export declare function hawkNode(label: string): Hex;
39
+ /** The ERC-721 token id (labelhash) for a .hawk second-level name. */
40
+ export declare function hawkTokenId(label: string): bigint;
41
+ /**
42
+ * Client-side registrability check, mirroring the controller's `valid` plus
43
+ * ENSIP-15 normalization (the on-chain check is length-only; apps must
44
+ * normalize). Returns the normalized label or throws with the reason.
45
+ */
46
+ export declare function validateLabel(rawLabel: string): string;
47
+ export declare const SECONDS_PER_YEAR = 31536000n;
48
+ export declare const MIN_REGISTRATION_DURATION_MAINNET = 2419200n;
49
+ export declare const MAX_REGISTRATION_DURATION = 315360000n;
@@ -0,0 +1,73 @@
1
+ import { encodeAbiParameters, keccak256, toHex, zeroAddress, zeroHash, } from "viem";
2
+ import { namehash, normalize } from "viem/ens";
3
+ /** namehash('hawk') */
4
+ export const HAWK_NODE = "0xf431efc1fb1b854a38191ac67de7e1ee70205b40f71cb927c938685e82e05403";
5
+ /** Reverse-record bits for Registration.reverseRecord. */
6
+ export const REVERSE_RECORD_NONE = 0;
7
+ export const REVERSE_RECORD_CHAIN = 1; // addr.reverse on Base
8
+ export const REVERSE_RECORD_DEFAULT = 2; // default.reverse (EVM-wide fallback)
9
+ export function makeRegistration(params) {
10
+ return {
11
+ label: params.label,
12
+ owner: params.owner,
13
+ duration: params.duration,
14
+ secret: params.secret,
15
+ resolver: params.resolver ?? zeroAddress,
16
+ data: params.data ?? [],
17
+ reverseRecord: params.reverseRecord ?? REVERSE_RECORD_NONE,
18
+ referrer: params.referrer ?? zeroHash,
19
+ };
20
+ }
21
+ const REGISTRATION_TUPLE = {
22
+ type: "tuple",
23
+ components: [
24
+ { name: "label", type: "string" },
25
+ { name: "owner", type: "address" },
26
+ { name: "duration", type: "uint256" },
27
+ { name: "secret", type: "bytes32" },
28
+ { name: "resolver", type: "address" },
29
+ { name: "data", type: "bytes[]" },
30
+ { name: "reverseRecord", type: "uint8" },
31
+ { name: "referrer", type: "bytes32" },
32
+ ],
33
+ };
34
+ /**
35
+ * Computes the commit-reveal commitment for a registration —
36
+ * `keccak256(abi.encode(registration))`, byte-identical to the
37
+ * controller's `makeCommitment`.
38
+ */
39
+ export function makeCommitment(registration) {
40
+ return keccak256(encodeAbiParameters([REGISTRATION_TUPLE], [registration]));
41
+ }
42
+ /** Random 32-byte commit-reveal secret (browser + node). */
43
+ export function randomSecret() {
44
+ const bytes = new Uint8Array(32);
45
+ globalThis.crypto.getRandomValues(bytes);
46
+ return toHex(bytes);
47
+ }
48
+ /** The namehash of `<label>.hawk`. */
49
+ export function hawkNode(label) {
50
+ return namehash(`${normalize(label)}.hawk`);
51
+ }
52
+ /** The ERC-721 token id (labelhash) for a .hawk second-level name. */
53
+ export function hawkTokenId(label) {
54
+ return BigInt(keccak256(toHex(normalize(label))));
55
+ }
56
+ /**
57
+ * Client-side registrability check, mirroring the controller's `valid` plus
58
+ * ENSIP-15 normalization (the on-chain check is length-only; apps must
59
+ * normalize). Returns the normalized label or throws with the reason.
60
+ */
61
+ export function validateLabel(rawLabel) {
62
+ const label = normalize(rawLabel);
63
+ if (label.includes(".")) {
64
+ throw new Error("A label cannot contain dots.");
65
+ }
66
+ if ([...label].length < 3) {
67
+ throw new Error("Names must be at least 3 characters.");
68
+ }
69
+ return label;
70
+ }
71
+ export const SECONDS_PER_YEAR = 31536000n; // 365 days, the pricing year
72
+ export const MIN_REGISTRATION_DURATION_MAINNET = 2419200n; // 28 days
73
+ export const MAX_REGISTRATION_DURATION = 315360000n; // 3650 days
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "hawk-names",
3
+ "version": "0.1.0",
4
+ "description": "Resolve and register .hawk names on Base — ENS-standard resolution with a one-line viem config.",
5
+ "license": "MIT",
6
+ "author": "dothawkxyz",
7
+ "homepage": "https://docs.dothawk.xyz",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/dothawkxyz/hawk.git",
11
+ "directory": "packages/sdk"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/dothawkxyz/hawk/issues"
15
+ },
16
+ "type": "module",
17
+ "main": "./dist/index.js",
18
+ "types": "./dist/index.d.ts",
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "default": "./dist/index.js"
23
+ }
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "src"
28
+ ],
29
+ "sideEffects": false,
30
+ "scripts": {
31
+ "build": "tsc -p tsconfig.json",
32
+ "sync-artifacts": "node scripts/sync-artifacts.mjs",
33
+ "test": "node --test test/resolution.test.mjs"
34
+ },
35
+ "peerDependencies": {
36
+ "viem": ">=2.30.0"
37
+ },
38
+ "devDependencies": {
39
+ "typescript": "^5.6.0",
40
+ "viem": "^2.33.3"
41
+ },
42
+ "keywords": [
43
+ "hawk",
44
+ "base-chain",
45
+ "ens",
46
+ "naming",
47
+ "viem"
48
+ ]
49
+ }
package/src/actions.ts ADDED
@@ -0,0 +1,50 @@
1
+ import type { Address, Client } from "viem";
2
+ import {
3
+ getEnsAddress,
4
+ getEnsAvatar,
5
+ getEnsName,
6
+ getEnsText,
7
+ normalize,
8
+ } from "viem/ens";
9
+
10
+ /**
11
+ * Thin, explicit wrappers over viem's ENS actions. If your client's chain
12
+ * comes from this SDK (`base` / `withHawk`), viem's own
13
+ * `getEnsName`/`getEnsAddress`/... already resolve .hawk — use whichever
14
+ * reads better in your codebase.
15
+ */
16
+
17
+ /** Primary name for an address (reverse resolution), or null. */
18
+ export async function getHawkName(
19
+ client: Client,
20
+ params: { address: Address },
21
+ ): Promise<string | null> {
22
+ return getEnsName(client as never, { address: params.address });
23
+ }
24
+
25
+ /** Forward-resolves a .hawk name to its address, or null. */
26
+ export async function getHawkAddress(
27
+ client: Client,
28
+ params: { name: string },
29
+ ): Promise<Address | null> {
30
+ return getEnsAddress(client as never, { name: normalize(params.name) });
31
+ }
32
+
33
+ /** Reads a text record (e.g. "com.twitter", "url", "description"). */
34
+ export async function getHawkText(
35
+ client: Client,
36
+ params: { name: string; key: string },
37
+ ): Promise<string | null> {
38
+ return getEnsText(client as never, {
39
+ name: normalize(params.name),
40
+ key: params.key,
41
+ });
42
+ }
43
+
44
+ /** Resolves the avatar record to a usable URI, or null. */
45
+ export async function getHawkAvatar(
46
+ client: Client,
47
+ params: { name: string },
48
+ ): Promise<string | null> {
49
+ return getEnsAvatar(client as never, { name: normalize(params.name) });
50
+ }
@@ -0,0 +1,101 @@
1
+ import type { Address } from "viem";
2
+
3
+ /** Every Hawk contract for one deployment. */
4
+ export type HawkAddresses = {
5
+ registry: Address;
6
+ baseRegistrar: Address;
7
+ controller: Address;
8
+ priceOracle: Address;
9
+ wrapper: Address;
10
+ metadata: Address;
11
+ reservedList: Address;
12
+ publicResolver: Address;
13
+ reverseRegistrar: Address;
14
+ defaultReverseRegistrar: Address;
15
+ universalResolver: Address;
16
+ usdc: Address;
17
+ };
18
+
19
+ const ZERO = "0x0000000000000000000000000000000000000000" as const;
20
+
21
+ /**
22
+ * Canonical deployments by chain id.
23
+ *
24
+ * - 8453 — Base mainnet (populated at mainnet deploy)
25
+ * - 84532 — Base Sepolia testnet (populated at testnet deploy)
26
+ *
27
+ * For a local/anvil deployment, build your own with `hawkAddressesFrom`
28
+ * using the deploy script's deployments/hawk-local.json.
29
+ */
30
+ export const HAWK_ADDRESSES: Record<number, HawkAddresses> = {
31
+ 8453: {
32
+ registry: "0x5DAF4DF48b022Bf0Fb454DBe9CB2592d3A32b0b2",
33
+ baseRegistrar: "0x83dcABD50E531325C76b9CB07F4C04Aca187722E",
34
+ controller: "0x65Acb254B2EF5af1FDFf7B8C77427Ca051Ff4F71",
35
+ priceOracle: "0x697b2C75652b6895D507b9C8E2Cb3b3C8EfccA29",
36
+ wrapper: "0x0B922e8B56c778667AbDbBBa522880F98362c6C8",
37
+ metadata: "0xA972f32580C2DD8eEf379e27B4B91b205BF3437F",
38
+ reservedList: "0x351c51AA3079e7f7EbeFb5079A92279b22a0AB6c",
39
+ publicResolver: "0xC7cAB8Af20fF52346F4e93c143Cc8e9d6384e26b",
40
+ reverseRegistrar: "0x4221F2953294ec98Ddf54964DB293E281db71daB",
41
+ defaultReverseRegistrar: "0x34acC481E26Ee9566d868F455BecD05353Bae257",
42
+ universalResolver: "0x1Ebbe68AfA011ADBE561De6215B1AA1Fe0e4bc6C",
43
+ usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
44
+ },
45
+ 84532: {
46
+ registry: ZERO,
47
+ baseRegistrar: ZERO,
48
+ controller: ZERO,
49
+ priceOracle: ZERO,
50
+ wrapper: ZERO,
51
+ metadata: ZERO,
52
+ reservedList: ZERO,
53
+ publicResolver: ZERO,
54
+ reverseRegistrar: ZERO,
55
+ defaultReverseRegistrar: ZERO,
56
+ universalResolver: ZERO,
57
+ usdc: ZERO,
58
+ },
59
+ };
60
+
61
+ /** Maps a deployments/hawk-<network>.json record to HawkAddresses. */
62
+ export function hawkAddressesFrom(deployment: {
63
+ HawkRegistry: Address;
64
+ HawkBaseRegistrar: Address;
65
+ HawkRegistrarController: Address;
66
+ HawkPriceOracle: Address;
67
+ HawkWrapper: Address;
68
+ HawkMetadata: Address;
69
+ HawkReservedList: Address;
70
+ PublicResolver: Address;
71
+ ReverseRegistrar: Address;
72
+ DefaultReverseRegistrar: Address;
73
+ UniversalResolver: Address;
74
+ usdc: Address;
75
+ }): HawkAddresses {
76
+ return {
77
+ registry: deployment.HawkRegistry,
78
+ baseRegistrar: deployment.HawkBaseRegistrar,
79
+ controller: deployment.HawkRegistrarController,
80
+ priceOracle: deployment.HawkPriceOracle,
81
+ wrapper: deployment.HawkWrapper,
82
+ metadata: deployment.HawkMetadata,
83
+ reservedList: deployment.HawkReservedList,
84
+ publicResolver: deployment.PublicResolver,
85
+ reverseRegistrar: deployment.ReverseRegistrar,
86
+ defaultReverseRegistrar: deployment.DefaultReverseRegistrar,
87
+ universalResolver: deployment.UniversalResolver,
88
+ usdc: deployment.usdc,
89
+ };
90
+ }
91
+
92
+ export function getHawkAddresses(chainId: number): HawkAddresses {
93
+ const addresses = HAWK_ADDRESSES[chainId];
94
+ if (!addresses || addresses.registry === ZERO) {
95
+ throw new Error(
96
+ `Hawk is not deployed on chain ${chainId} in this SDK version — ` +
97
+ `pass addresses explicitly (hawkAddressesFrom) or upgrade hawk-names.`,
98
+ );
99
+ }
100
+ return addresses;
101
+ }
package/src/chains.ts ADDED
@@ -0,0 +1,91 @@
1
+ import { defineChain, type Chain } from "viem";
2
+ import { HAWK_ADDRESSES, type HawkAddresses } from "./addresses.js";
3
+
4
+ const ZERO = "0x0000000000000000000000000000000000000000";
5
+
6
+ function ensContracts(addresses: HawkAddresses | undefined) {
7
+ if (!addresses || addresses.registry === ZERO) return {};
8
+ return {
9
+ ensRegistry: { address: addresses.registry },
10
+ ensUniversalResolver: { address: addresses.universalResolver },
11
+ };
12
+ }
13
+
14
+ /**
15
+ * Base mainnet (8453), with Hawk wired in as the chain's ENS —
16
+ * `getEnsName`, `getEnsAddress`, `getEnsText`, `getEnsAvatar` and every
17
+ * other viem/wagmi ENS action work out of the box against .hawk names.
18
+ */
19
+ export const base: Chain = defineChain({
20
+ id: 8453,
21
+ name: "Base",
22
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
23
+ rpcUrls: {
24
+ default: { http: ["https://mainnet.base.org"] },
25
+ },
26
+ blockExplorers: {
27
+ default: {
28
+ name: "Basescan",
29
+ url: "https://basescan.org",
30
+ },
31
+ },
32
+ contracts: {
33
+ multicall3: {
34
+ address: "0xcA11bde05977b3631167028862bE2a173976CA11",
35
+ },
36
+ ...ensContracts(HAWK_ADDRESSES[8453]),
37
+ },
38
+ });
39
+
40
+ /** Base Sepolia testnet (84532), Hawk wired in. */
41
+ export const baseSepolia: Chain = defineChain({
42
+ id: 84532,
43
+ name: "Base Sepolia",
44
+ testnet: true,
45
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
46
+ rpcUrls: {
47
+ default: { http: ["https://sepolia.base.org"] },
48
+ },
49
+ blockExplorers: {
50
+ default: {
51
+ name: "Basescan",
52
+ url: "https://sepolia.basescan.org",
53
+ },
54
+ },
55
+ contracts: {
56
+ multicall3: {
57
+ address: "0xcA11bde05977b3631167028862bE2a173976CA11",
58
+ },
59
+ ...ensContracts(HAWK_ADDRESSES[84532]),
60
+ },
61
+ });
62
+
63
+ /**
64
+ * Wires Hawk resolution into any viem Chain object — the one-line
65
+ * integration for dapps that already have their own chain config:
66
+ *
67
+ * ```ts
68
+ * import { withHawk } from "hawk-names";
69
+ * const chain = withHawk(myBaseChainConfig);
70
+ * // viem's getEnsName / getEnsAddress now resolve .hawk
71
+ * ```
72
+ */
73
+ export function withHawk(
74
+ chain: Chain,
75
+ addresses?: HawkAddresses,
76
+ ): Chain {
77
+ const hawk = addresses ?? HAWK_ADDRESSES[chain.id];
78
+ if (!hawk || hawk.registry === ZERO) {
79
+ throw new Error(
80
+ `No Hawk deployment known for chain ${chain.id} — pass addresses explicitly.`,
81
+ );
82
+ }
83
+ return {
84
+ ...chain,
85
+ contracts: {
86
+ ...chain.contracts,
87
+ ensRegistry: { address: hawk.registry },
88
+ ensUniversalResolver: { address: hawk.universalResolver },
89
+ },
90
+ };
91
+ }