@enscribe/hardhat-enscribe 0.1.1

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.
Files changed (32) hide show
  1. package/README.md +211 -0
  2. package/dist/src/index.d.ts +4 -0
  3. package/dist/src/index.js +33 -0
  4. package/dist/src/internal/abi/ENSRegistry.d.ts +36 -0
  5. package/dist/src/internal/abi/ENSRegistry.js +377 -0
  6. package/dist/src/internal/abi/NameWrapper.d.ts +51 -0
  7. package/dist/src/internal/abi/NameWrapper.js +1456 -0
  8. package/dist/src/internal/abi/Ownable.d.ts +36 -0
  9. package/dist/src/internal/abi/Ownable.js +55 -0
  10. package/dist/src/internal/abi/PublicResolver.d.ts +40 -0
  11. package/dist/src/internal/abi/PublicResolver.js +1014 -0
  12. package/dist/src/internal/abi/ReverseRegistrar.d.ts +40 -0
  13. package/dist/src/internal/abi/ReverseRegistrar.js +337 -0
  14. package/dist/src/internal/config/contracts.d.ts +175 -0
  15. package/dist/src/internal/config/contracts.js +168 -0
  16. package/dist/src/internal/constants.d.ts +1 -0
  17. package/dist/src/internal/constants.js +1 -0
  18. package/dist/src/internal/tasks/name.d.ts +9 -0
  19. package/dist/src/internal/tasks/name.integration.test.d.ts +1 -0
  20. package/dist/src/internal/tasks/name.integration.test.js +180 -0
  21. package/dist/src/internal/tasks/name.js +265 -0
  22. package/dist/src/internal/tasks/name.test.d.ts +1 -0
  23. package/dist/src/internal/tasks/name.test.js +55 -0
  24. package/dist/src/internal/type-extensions.d.ts +1 -0
  25. package/dist/src/internal/type-extensions.js +16 -0
  26. package/dist/src/internal/utils.d.ts +8 -0
  27. package/dist/src/internal/utils.js +48 -0
  28. package/dist/test/enscribe.hardhat.test.d.ts +1 -0
  29. package/dist/test/enscribe.hardhat.test.js +290 -0
  30. package/dist/test/enscribe.integration.test.d.ts +1 -0
  31. package/dist/test/enscribe.integration.test.js +229 -0
  32. package/package.json +59 -0
@@ -0,0 +1,168 @@
1
+ // ENS Contract addresses for different networks
2
+ export const ENS_CONTRACTS = {
3
+ // Mainnet addresses
4
+ mainnet: {
5
+ ENS_REGISTRY: "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e",
6
+ PUBLIC_RESOLVER: "0xF29100983E058B709F3D539b0c765937B804AC15",
7
+ NAME_WRAPPER: "0xD4416b13d2b3a9aBae7AcD5D6C2BbDBE25686401",
8
+ REVERSE_REGISTRAR: "0xa58E81fe9b61B5c3fE2AFD33CF304c454AbFc7Cb",
9
+ L2_REVERSE_REGISTRAR: "",
10
+ },
11
+ // Sepolia testnet addresses
12
+ sepolia: {
13
+ ENS_REGISTRY: "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e",
14
+ PUBLIC_RESOLVER: "0xE99638b40E4Fff0129D56f03b55b6bbC4BBE49b5",
15
+ NAME_WRAPPER: "0x0635513f179D50A207757E05759CbD106d7dFcE8",
16
+ REVERSE_REGISTRAR: "0xA0a1AbcDAe1a2a4A2EF8e9113Ff0e02DD81DC0C6",
17
+ L2_REVERSE_REGISTRAR: "",
18
+ },
19
+ // Linea mainnet addresses
20
+ linea: {
21
+ ENS_REGISTRY: "0x50130b669B28C339991d8676FA73CF122a121267",
22
+ PUBLIC_RESOLVER: "0x86c5AED9F27837074612288610fB98ccC1733126",
23
+ NAME_WRAPPER: "0xA53cca02F98D590819141Aa85C891e2Af713C223",
24
+ REVERSE_REGISTRAR: "0x08D3fF6E65f680844fd2465393ff6f0d742b67D5",
25
+ L2_REVERSE_REGISTRAR: "0x0000000000D8e504002cC26E3Ec46D81971C1664",
26
+ },
27
+ // Linea Sepolia testnet addresses
28
+ "linea-sepolia": {
29
+ ENS_REGISTRY: "0x5B2636F0f2137B4aE722C01dd5122D7d3e9541f7",
30
+ PUBLIC_RESOLVER: "0xA2008916Ed2d7ED0Ecd747a8a5309267e42cf1f1",
31
+ NAME_WRAPPER: "0xF127De9E039a789806fEd4C6b1C0f3aFfeA9425e",
32
+ REVERSE_REGISTRAR: "0x4aAA964D8EB65508ca3DA3b0A3C060c16059E613",
33
+ L2_REVERSE_REGISTRAR: "0x00000BeEF055f7934784D6d81b6BC86665630dbA",
34
+ },
35
+ // Base mainnet addresses
36
+ base: {
37
+ ENS_REGISTRY: "0xB94704422c2a1E396835A571837Aa5AE53285a95",
38
+ PUBLIC_RESOLVER: "0xC6d566A56A1aFf6508b41f6c90ff131615583BCD",
39
+ NAME_WRAPPER: "",
40
+ REVERSE_REGISTRAR: "0x79EA96012eEa67A83431F1701B3dFf7e37F9E282",
41
+ L2_REVERSE_REGISTRAR: "0x0000000000D8e504002cC26E3Ec46D81971C1664",
42
+ },
43
+ // Base Sepolia testnet addresses
44
+ "base-sepolia": {
45
+ ENS_REGISTRY: "0x1493b2567056c2181630115660963E13A8E32735",
46
+ PUBLIC_RESOLVER: "0x6533C94869D28fAA8dF77cc63f9e2b2D6Cf77eBA",
47
+ NAME_WRAPPER: "",
48
+ REVERSE_REGISTRAR: "0xa0A8401ECF248a9375a0a71C4dedc263dA18dCd7",
49
+ L2_REVERSE_REGISTRAR: "0x00000BeEF055f7934784D6d81b6BC86665630dbA",
50
+ },
51
+ // Optimism mainnet addresses
52
+ optimism: {
53
+ ENS_REGISTRY: "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e",
54
+ PUBLIC_RESOLVER: "",
55
+ NAME_WRAPPER: "",
56
+ REVERSE_REGISTRAR: "",
57
+ L2_REVERSE_REGISTRAR: "0x0000000000D8e504002cC26E3Ec46D81971C1664",
58
+ },
59
+ // Optimism Sepolia testnet addresses
60
+ "optimism-sepolia": {
61
+ ENS_REGISTRY: "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e",
62
+ PUBLIC_RESOLVER: "",
63
+ NAME_WRAPPER: "",
64
+ REVERSE_REGISTRAR: "",
65
+ L2_REVERSE_REGISTRAR: "0x00000BeEF055f7934784D6d81b6BC86665630dbA",
66
+ },
67
+ // Arbitrum mainnet addresses
68
+ arbitrum: {
69
+ ENS_REGISTRY: "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e",
70
+ PUBLIC_RESOLVER: "",
71
+ NAME_WRAPPER: "",
72
+ REVERSE_REGISTRAR: "",
73
+ L2_REVERSE_REGISTRAR: "0x0000000000D8e504002cC26E3Ec46D81971C1664",
74
+ },
75
+ // Arbitrum Sepolia testnet addresses
76
+ "arbitrum-sepolia": {
77
+ ENS_REGISTRY: "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e",
78
+ PUBLIC_RESOLVER: "",
79
+ NAME_WRAPPER: "",
80
+ REVERSE_REGISTRAR: "",
81
+ L2_REVERSE_REGISTRAR: "0x00000BeEF055f7934784D6d81b6BC86665630dbA",
82
+ },
83
+ // Scroll mainnet addresses
84
+ scroll: {
85
+ ENS_REGISTRY: "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e",
86
+ PUBLIC_RESOLVER: "",
87
+ NAME_WRAPPER: "",
88
+ REVERSE_REGISTRAR: "",
89
+ L2_REVERSE_REGISTRAR: "0x0000000000D8e504002cC26E3Ec46D81971C1664",
90
+ },
91
+ // Scroll Sepolia testnet addresses
92
+ "scroll-sepolia": {
93
+ ENS_REGISTRY: "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e",
94
+ PUBLIC_RESOLVER: "",
95
+ NAME_WRAPPER: "",
96
+ REVERSE_REGISTRAR: "",
97
+ L2_REVERSE_REGISTRAR: "0x00000BeEF055f7934784D6d81b6BC86665630dbA",
98
+ },
99
+ // Local development addresses (will be set during deployment)
100
+ localhost: {
101
+ ENS_REGISTRY: "0x0000000000000000000000000000000000000000", // Placeholder for local development
102
+ PUBLIC_RESOLVER: "",
103
+ NAME_WRAPPER: "",
104
+ REVERSE_REGISTRAR: "",
105
+ L2_REVERSE_REGISTRAR: "",
106
+ },
107
+ };
108
+ // Helper function to get contract addresses for a given network
109
+ export function getContractAddresses(networkName) {
110
+ const addresses = ENS_CONTRACTS[networkName];
111
+ if (!addresses) {
112
+ throw new Error(`Unsupported network: ${networkName}`);
113
+ }
114
+ return addresses;
115
+ }
116
+ // Helper function to get network name from chain ID
117
+ export function getNetworkNameFromChainId(chainId) {
118
+ switch (chainId) {
119
+ case 1:
120
+ return "mainnet";
121
+ case 11155111:
122
+ return "sepolia";
123
+ case 59144:
124
+ return "linea";
125
+ case 59141:
126
+ return "linea-sepolia";
127
+ case 8453:
128
+ return "base";
129
+ case 84532:
130
+ return "base-sepolia";
131
+ case 10:
132
+ return "optimism";
133
+ case 11155420:
134
+ return "optimism-sepolia";
135
+ case 42161:
136
+ return "arbitrum";
137
+ case 421614:
138
+ return "arbitrum-sepolia";
139
+ case 534352:
140
+ return "scroll";
141
+ case 534351:
142
+ return "scroll-sepolia";
143
+ case 31337:
144
+ return "localhost";
145
+ default:
146
+ throw new Error(`Unsupported chain ID: ${chainId}`);
147
+ }
148
+ }
149
+ // Helper function to validate contract addresses
150
+ export function validateContractAddresses(addresses) {
151
+ const requiredContracts = ["ENS_REGISTRY"]; // Only ENS_REGISTRY is always required
152
+ const optionalContracts = ["PUBLIC_RESOLVER", "NAME_WRAPPER", "REVERSE_REGISTRAR", "L2_REVERSE_REGISTRAR"];
153
+ // Validate required contracts
154
+ for (const contract of requiredContracts) {
155
+ if (!addresses[contract]) {
156
+ throw new Error(`Missing contract address for ${contract}`);
157
+ }
158
+ if (!addresses[contract].match(/^0x[a-fA-F0-9]{40}$/)) {
159
+ throw new Error(`Invalid contract address format for ${contract}: ${addresses[contract]}`);
160
+ }
161
+ }
162
+ // Validate optional contracts (if provided, must be valid format)
163
+ for (const contract of optionalContracts) {
164
+ if (addresses[contract] && addresses[contract] !== "" && !addresses[contract].match(/^0x[a-fA-F0-9]{40}$/)) {
165
+ throw new Error(`Invalid contract address format for ${contract}: ${addresses[contract]}`);
166
+ }
167
+ }
168
+ }
@@ -0,0 +1 @@
1
+ export declare const PLUGIN_ID = "hardhat-enscribe";
@@ -0,0 +1 @@
1
+ export const PLUGIN_ID = "hardhat-enscribe";
@@ -0,0 +1,9 @@
1
+ import type { NewTaskActionFunction } from "hardhat/types/tasks";
2
+ import "@nomicfoundation/hardhat-viem";
3
+ interface TaskNameArguments {
4
+ name: string;
5
+ contract?: string;
6
+ chain?: string;
7
+ }
8
+ declare const taskName: NewTaskActionFunction<TaskNameArguments>;
9
+ export default taskName;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,180 @@
1
+ import { describe, it, expect, beforeEach, vi } from "vitest";
2
+ import { namehash } from "viem/ens";
3
+ import { keccak256, toBytes } from "viem";
4
+ import { parseNormalizedName } from "../utils.js";
5
+ // Mock Hardhat Runtime Environment for testing
6
+ const mockHRE = {
7
+ network: {
8
+ connect: vi.fn().mockResolvedValue({
9
+ viem: {
10
+ getWalletClients: vi.fn().mockResolvedValue([
11
+ {
12
+ account: {
13
+ address: "0x1234567890123456789012345678901234567890",
14
+ },
15
+ chain: {
16
+ id: 31337, // Hardhat local network ID
17
+ },
18
+ },
19
+ ]),
20
+ getContractAt: vi.fn(),
21
+ },
22
+ }),
23
+ },
24
+ userConfig: {
25
+ networks: {},
26
+ },
27
+ };
28
+ describe("ENS Primary Name Setting Integration Tests", () => {
29
+ let ensRegistry;
30
+ let publicResolver;
31
+ let nameWrapper;
32
+ let reverseRegistrar;
33
+ let testWallet;
34
+ beforeEach(async () => {
35
+ // This would be set up in a real Hardhat test environment
36
+ // For now, we'll create mock contracts
37
+ ensRegistry = {
38
+ recordExists: vi.fn(),
39
+ setSubnodeRecord: vi.fn(),
40
+ };
41
+ publicResolver = {
42
+ addr: vi.fn(),
43
+ setAddr: vi.fn(),
44
+ };
45
+ nameWrapper = {
46
+ isWrapped: vi.fn(),
47
+ setSubnodeRecord: vi.fn(),
48
+ };
49
+ reverseRegistrar = {
50
+ setNameForAddr: vi.fn(),
51
+ };
52
+ testWallet = {
53
+ account: {
54
+ address: "0x1234567890123456789012345678901234567890",
55
+ },
56
+ chain: {
57
+ id: 31337,
58
+ },
59
+ };
60
+ });
61
+ describe("parseNormalizedName utility", () => {
62
+ it("should correctly parse ENS names for contract naming", () => {
63
+ const testCases = [
64
+ {
65
+ input: "mynft.abhi.eth",
66
+ expected: { label: "mynft", parent: "abhi.eth" },
67
+ },
68
+ {
69
+ input: "contract.example.eth",
70
+ expected: { label: "contract", parent: "example.eth" },
71
+ },
72
+ {
73
+ input: "sub.domain.example.eth",
74
+ expected: { label: "sub", parent: "domain.example.eth" },
75
+ },
76
+ ];
77
+ testCases.forEach(({ input, expected }) => {
78
+ const result = parseNormalizedName(input);
79
+ expect(result).toEqual(expected);
80
+ });
81
+ });
82
+ });
83
+ describe("ENS name creation flow", () => {
84
+ it("should create subname when parent domain is wrapped", async () => {
85
+ // Mock that parent is wrapped
86
+ nameWrapper.isWrapped.mockResolvedValue(true);
87
+ // Mock that name doesn't exist yet
88
+ ensRegistry.recordExists.mockResolvedValue(false);
89
+ const normalizedName = "testcontract.abhi.eth";
90
+ const { label, parent } = parseNormalizedName(normalizedName);
91
+ const parentNode = namehash(parent);
92
+ const fullNameNode = namehash(normalizedName);
93
+ // Verify the flow would call correct functions
94
+ expect(label).toBe("testcontract");
95
+ expect(parent).toBe("abhi.eth");
96
+ expect(parentNode).toBeDefined();
97
+ expect(fullNameNode).toBeDefined();
98
+ });
99
+ it("should create subname when parent domain is not wrapped", async () => {
100
+ // Mock that parent is not wrapped
101
+ nameWrapper.isWrapped.mockResolvedValue(false);
102
+ // Mock that name doesn't exist yet
103
+ ensRegistry.recordExists.mockResolvedValue(false);
104
+ const normalizedName = "testcontract.example.eth";
105
+ const { label, parent } = parseNormalizedName(normalizedName);
106
+ const parentNode = namehash(parent);
107
+ const labelHash = keccak256(toBytes(label));
108
+ // Verify the flow would call correct functions
109
+ expect(label).toBe("testcontract");
110
+ expect(parent).toBe("example.eth");
111
+ expect(parentNode).toBeDefined();
112
+ expect(labelHash).toBeDefined();
113
+ });
114
+ it("should skip subname creation if name already exists", async () => {
115
+ // Mock that name already exists
116
+ ensRegistry.recordExists.mockResolvedValue(true);
117
+ const normalizedName = "existing.abhi.eth";
118
+ const fullNameNode = namehash(normalizedName);
119
+ // Should skip creation
120
+ expect(fullNameNode).toBeDefined();
121
+ });
122
+ });
123
+ describe("Forward resolution setting", () => {
124
+ it("should set forward resolution when current address differs", async () => {
125
+ const contractAddress = "0x9876543210987654321098765432109876543210";
126
+ const currentAddr = "0x1111111111111111111111111111111111111111";
127
+ const normalizedName = "testcontract.abhi.eth";
128
+ const fullNameNode = namehash(normalizedName);
129
+ // Mock current resolution
130
+ publicResolver.addr.mockResolvedValue(currentAddr);
131
+ // Should set new resolution
132
+ expect(currentAddr.toLowerCase()).not.toBe(contractAddress.toLowerCase());
133
+ });
134
+ it("should skip forward resolution when already set correctly", async () => {
135
+ const contractAddress = "0x9876543210987654321098765432109876543210";
136
+ const currentAddr = "0x9876543210987654321098765432109876543210";
137
+ const normalizedName = "testcontract.abhi.eth";
138
+ const fullNameNode = namehash(normalizedName);
139
+ // Mock current resolution matches
140
+ publicResolver.addr.mockResolvedValue(currentAddr);
141
+ // Should skip setting
142
+ expect(currentAddr.toLowerCase()).toBe(contractAddress.toLowerCase());
143
+ });
144
+ });
145
+ describe("Reverse resolution setting", () => {
146
+ it("should set reverse resolution for contract", async () => {
147
+ const contractAddress = "0x9876543210987654321098765432109876543210";
148
+ const normalizedName = "testcontract.abhi.eth";
149
+ const ownerAddress = testWallet.account.address;
150
+ // Verify reverse resolution would be set
151
+ expect(contractAddress).toBeDefined();
152
+ expect(normalizedName).toBeDefined();
153
+ expect(ownerAddress).toBeDefined();
154
+ });
155
+ });
156
+ describe("Complete naming flow integration", () => {
157
+ it("should execute complete flow for new contract naming", async () => {
158
+ const normalizedName = "newcontract.abhi.eth";
159
+ const contractAddress = "0x9876543210987654321098765432109876543210";
160
+ // Parse the name
161
+ const { label, parent } = parseNormalizedName(normalizedName);
162
+ // Generate required hashes
163
+ const parentNode = namehash(parent);
164
+ const fullNameNode = namehash(normalizedName);
165
+ const labelHash = keccak256(toBytes(label));
166
+ // Verify all components are ready
167
+ expect(label).toBe("newcontract");
168
+ expect(parent).toBe("abhi.eth");
169
+ expect(parentNode).toBeDefined();
170
+ expect(fullNameNode).toBeDefined();
171
+ expect(labelHash).toBeDefined();
172
+ // This test verifies the data preparation for the complete flow
173
+ // In a real integration test, we would:
174
+ // 1. Deploy ENS contracts to local Hardhat network
175
+ // 2. Set up parent domain ownership
176
+ // 3. Execute the actual contract calls
177
+ // 4. Verify the results on-chain
178
+ });
179
+ });
180
+ });
@@ -0,0 +1,265 @@
1
+ import "@nomicfoundation/hardhat-viem";
2
+ import { namehash, normalize } from "viem/ens";
3
+ import { readContract, writeContract, waitForTransactionReceipt } from "viem/actions";
4
+ import { keccak256, toBytes } from "viem";
5
+ import ensRegistryABI from "../abi/ENSRegistry.js";
6
+ import ownableContractABI from "../abi/Ownable.js";
7
+ import nameWrapperABI from "../abi/NameWrapper.js";
8
+ import publicResolverABI from "../abi/PublicResolver.js";
9
+ import reverseRegistrarABI from "../abi/ReverseRegistrar.js";
10
+ import { isAddressEmpty, isAddressValid, parseNormalizedName, logMetric, } from "../utils.js";
11
+ import { getContractAddresses } from "../config/contracts.js";
12
+ import { v4 as uuid } from 'uuid';
13
+ // Global contracts object - will be initialized based on chain
14
+ let contracts = {};
15
+ const opType = 'hh-enscribe-nameexisting';
16
+ const corelationId = uuid();
17
+ const taskName = async (args, hre) => {
18
+ // console.log(args);
19
+ if (args.contract == null) {
20
+ console.log("need to pass a contract address");
21
+ return;
22
+ }
23
+ // Determine network from chain parameter or default to sepolia
24
+ const networkName = args.chain || "sepolia";
25
+ // Initialize global contracts object based on network
26
+ contracts = getContractAddresses(networkName);
27
+ const { viem } = await hre.network.connect(networkName);
28
+ // console.log(hre.userConfig.networks);
29
+ const [senderClient, recvrClient] = await viem.getWalletClients();
30
+ // console.log(senderClient.account);
31
+ const nameNormalized = normalize(args.name);
32
+ console.log(`normalized name is ${nameNormalized}`);
33
+ await setPrimaryName(nameNormalized, args.contract, senderClient);
34
+ };
35
+ const isOwnable = async (address, walletClient) => {
36
+ if (isAddressEmpty(address) || !isAddressValid(address)) {
37
+ return false;
38
+ }
39
+ try {
40
+ const _ = (await readContract(walletClient, {
41
+ address: address,
42
+ abi: ownableContractABI,
43
+ functionName: "owner",
44
+ args: [],
45
+ }));
46
+ console.log("contract implements Ownable");
47
+ }
48
+ catch (err) {
49
+ return false;
50
+ }
51
+ return true;
52
+ };
53
+ const isReverseClaimable = async (address, walletClient) => {
54
+ if (isAddressEmpty(address) || !isAddressValid(address)) {
55
+ return;
56
+ }
57
+ try {
58
+ const addrLabel = address.slice(2).toLowerCase();
59
+ const reversedNode = namehash(addrLabel + '.' + 'addr.reverse');
60
+ const resolvedAddr = (await readContract(walletClient, {
61
+ address: contracts.ENS_REGISTRY,
62
+ abi: ensRegistryABI,
63
+ functionName: 'owner',
64
+ args: [reversedNode],
65
+ }));
66
+ console.log('resolvedaddr is ' + resolvedAddr);
67
+ if (resolvedAddr.toLowerCase() === walletClient.account?.address.toLowerCase()) {
68
+ console.log('contract implements Reverseclaimable');
69
+ return true;
70
+ }
71
+ else {
72
+ console.log('contract does not implement reverseclaimable');
73
+ return false;
74
+ }
75
+ }
76
+ catch (err) {
77
+ console.log('there was an error checking if the contract was reverse claimer');
78
+ return false;
79
+ }
80
+ };
81
+ const isContractOwner = async (address, walletClient) => {
82
+ if (isAddressEmpty(address) || !isAddressValid(address) || !walletClient) {
83
+ return false;
84
+ }
85
+ try {
86
+ const ownerAddress = (await readContract(walletClient, {
87
+ address: address,
88
+ abi: ownableContractABI,
89
+ functionName: 'owner',
90
+ args: [],
91
+ }));
92
+ return ownerAddress.toLowerCase() == walletClient.account?.address.toLowerCase();
93
+ }
94
+ catch (err) {
95
+ // console.log('err ' + err);
96
+ const addrLabel = address.slice(2).toLowerCase();
97
+ const reversedNode = namehash(addrLabel + '.' + 'addr.reverse');
98
+ const resolvedAddr = (await readContract(walletClient, {
99
+ address: contracts.ENS_REGISTRY,
100
+ abi: ensRegistryABI,
101
+ functionName: 'owner',
102
+ args: [reversedNode],
103
+ }));
104
+ return resolvedAddr.toLowerCase() == walletClient.account?.address.toLowerCase();
105
+ }
106
+ };
107
+ const setPrimaryName = async (normalizedName, contractAddress, walletClient) => {
108
+ const { label, parent } = parseNormalizedName(normalizedName);
109
+ const parentNode = namehash(parent);
110
+ const labelHash = keccak256(toBytes(label));
111
+ const fullNameNode = namehash(normalizedName);
112
+ const nameExists = (await readContract(walletClient, {
113
+ address: contracts.ENS_REGISTRY,
114
+ abi: ensRegistryABI,
115
+ functionName: "recordExists",
116
+ args: [fullNameNode],
117
+ }));
118
+ let contractType = 'Unknown';
119
+ if (await isReverseClaimable(contractAddress, walletClient)) {
120
+ contractType = 'ReverseClaimer';
121
+ }
122
+ if (await isOwnable(contractAddress, walletClient)) {
123
+ contractType = 'Ownable';
124
+ }
125
+ // create subname
126
+ if (!nameExists) {
127
+ process.stdout.write(`creating subname ... `);
128
+ let txn;
129
+ const isWrapped = await readContract(walletClient, {
130
+ address: contracts.NAME_WRAPPER,
131
+ abi: nameWrapperABI,
132
+ functionName: "isWrapped",
133
+ args: [parentNode],
134
+ });
135
+ if (isWrapped) {
136
+ // console.log(
137
+ // "create subname::writeContract calling setSubnodeRecord on NAME_WRAPPER",
138
+ // );
139
+ txn = await writeContract(walletClient, {
140
+ address: contracts.NAME_WRAPPER,
141
+ abi: nameWrapperABI,
142
+ functionName: "setSubnodeRecord",
143
+ args: [
144
+ parentNode,
145
+ label,
146
+ walletClient.account?.address,
147
+ contracts.PUBLIC_RESOLVER,
148
+ 0,
149
+ 0,
150
+ 0,
151
+ ],
152
+ account: walletClient.account,
153
+ chain: walletClient.chain,
154
+ });
155
+ // console.log(`create subname txn: ${txn}`);
156
+ await waitForTransactionReceipt(walletClient, { hash: txn });
157
+ process.stdout.write(`done with txn: ${txn}\n`);
158
+ await logMetric(corelationId, Date.now(), walletClient.chain?.id, contractAddress, walletClient.account?.address, normalizedName, 'subname::setSubnodeRecord', txn, contractType, opType);
159
+ }
160
+ else {
161
+ // console.log(
162
+ // "create subname::writeContract calling setSubnodeRecord on ENS_REGISTRY",
163
+ // );
164
+ txn = await writeContract(walletClient, {
165
+ address: contracts.ENS_REGISTRY,
166
+ abi: ensRegistryABI,
167
+ functionName: "setSubnodeRecord",
168
+ args: [
169
+ parentNode,
170
+ labelHash,
171
+ walletClient.account?.address,
172
+ contracts.PUBLIC_RESOLVER,
173
+ 0,
174
+ ],
175
+ account: walletClient.account,
176
+ chain: walletClient.chain,
177
+ });
178
+ // console.log(`create subname txn: ${txn}`);
179
+ await waitForTransactionReceipt(walletClient, { hash: txn });
180
+ process.stdout.write(`done with txn: ${txn}\n`);
181
+ await logMetric(corelationId, Date.now(), walletClient.chain?.id, contractAddress, walletClient.account?.address, normalizedName, 'subname::setSubnodeRecord', txn, contractType, opType);
182
+ }
183
+ }
184
+ else {
185
+ process.stdout.write(`${normalizedName} already exists. skipping subname creation.\n`);
186
+ }
187
+ // Wait for subname creation to be confirmed before proceeding
188
+ // set fwd res
189
+ process.stdout.write(`setting forward resolution ... `);
190
+ const currentAddr = (await readContract(walletClient, {
191
+ address: contracts.PUBLIC_RESOLVER,
192
+ abi: publicResolverABI,
193
+ functionName: "addr",
194
+ args: [fullNameNode],
195
+ }));
196
+ if (currentAddr.toLowerCase() !== contractAddress.toLowerCase()) {
197
+ // console.log("set fwdres::writeContract calling setAddr on PUBLIC_RESOLVER");
198
+ let txn = await writeContract(walletClient, {
199
+ address: contracts.PUBLIC_RESOLVER,
200
+ abi: publicResolverABI,
201
+ functionName: "setAddr",
202
+ args: [fullNameNode, contractAddress],
203
+ account: walletClient.account,
204
+ chain: walletClient.chain,
205
+ });
206
+ await waitForTransactionReceipt(walletClient, { hash: txn });
207
+ process.stdout.write(`done with txn: ${txn}\n`);
208
+ await logMetric(corelationId, Date.now(), walletClient.chain?.id, contractAddress, walletClient.account?.address, normalizedName, 'fwdres::setAddr', txn, contractType, opType);
209
+ }
210
+ else {
211
+ process.stdout.write("forward resolution already set.\n");
212
+ }
213
+ // set rev res
214
+ process.stdout.write(`setting reverse resolution ... `);
215
+ if (await isContractOwner(contractAddress, walletClient)) {
216
+ let txn;
217
+ if (contractType === 'ReverseClaimer') {
218
+ const addrLabel = contractAddress.slice(2).toLowerCase();
219
+ const reversedNode = namehash(addrLabel + '.' + 'addr.reverse');
220
+ // console.log('reversedNode is ' + reversedNode + '\n');
221
+ // console.log('pub res is ' + contracts.PUBLIC_RESOLVER + '\n');
222
+ txn = await writeContract(walletClient, {
223
+ address: contracts.PUBLIC_RESOLVER,
224
+ abi: publicResolverABI,
225
+ functionName: 'setName',
226
+ args: [
227
+ reversedNode,
228
+ normalizedName,
229
+ ],
230
+ account: walletClient.account,
231
+ chain: walletClient.chain,
232
+ });
233
+ await waitForTransactionReceipt(walletClient, { hash: txn });
234
+ process.stdout.write(`done with txn: ${txn}\n`);
235
+ await logMetric(corelationId, Date.now(), walletClient.chain?.id, contractAddress, walletClient.account?.address, normalizedName, 'revres::setName', txn, 'ReverseClaimer', opType);
236
+ }
237
+ else if (contractType === 'Ownable') {
238
+ txn = await writeContract(walletClient, {
239
+ address: contracts.REVERSE_REGISTRAR,
240
+ abi: reverseRegistrarABI,
241
+ functionName: "setNameForAddr",
242
+ args: [
243
+ contractAddress,
244
+ walletClient.account?.address,
245
+ contracts.PUBLIC_RESOLVER,
246
+ normalizedName,
247
+ ],
248
+ account: walletClient.account,
249
+ chain: walletClient.chain,
250
+ });
251
+ await waitForTransactionReceipt(walletClient, { hash: txn });
252
+ process.stdout.write(`done with txn: ${txn}\n`);
253
+ await logMetric(corelationId, Date.now(), walletClient.chain?.id, contractAddress, walletClient.account?.address, normalizedName, 'revres::setNameForAddr', txn, 'Ownable', opType);
254
+ }
255
+ else {
256
+ console.log(`Only Ownable, ERC173 and ReverseClaimer contracts can be named.`);
257
+ return;
258
+ }
259
+ }
260
+ else {
261
+ console.log(`You are not the owner of this contract. Skipping reverse resolution.\n`);
262
+ }
263
+ console.log(`✨ Contract named: https://app.enscribe.xyz/explore/${walletClient.chain?.id}/${normalizedName}`);
264
+ };
265
+ export default taskName;
@@ -0,0 +1 @@
1
+ export {};