@ensdomains/ensjs 3.0.0-alpha.4 → 3.0.0-alpha.7

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 (86) hide show
  1. package/dist/cjs/GqlManager.d.ts +3 -0
  2. package/dist/cjs/GqlManager.js +62 -2
  3. package/dist/cjs/functions/burnFuses.d.ts +3 -1
  4. package/dist/cjs/functions/burnFuses.js +2 -6
  5. package/dist/cjs/functions/createSubname.d.ts +1 -5
  6. package/dist/cjs/functions/createSubname.js +3 -7
  7. package/dist/cjs/functions/deleteSubname.d.ts +3 -3
  8. package/dist/cjs/functions/deleteSubname.js +5 -2
  9. package/dist/cjs/functions/setName.d.ts +4 -3
  10. package/dist/cjs/functions/setName.js +5 -10
  11. package/dist/cjs/functions/setRecords.d.ts +4 -1
  12. package/dist/cjs/functions/setRecords.js +11 -9
  13. package/dist/cjs/functions/setResolver.d.ts +4 -3
  14. package/dist/cjs/functions/setResolver.js +5 -11
  15. package/dist/cjs/functions/transferName.d.ts +4 -3
  16. package/dist/cjs/functions/transferName.js +8 -13
  17. package/dist/cjs/functions/transferSubname.d.ts +4 -3
  18. package/dist/cjs/functions/transferSubname.js +3 -7
  19. package/dist/cjs/functions/unwrapName.d.ts +4 -3
  20. package/dist/cjs/functions/unwrapName.js +3 -8
  21. package/dist/cjs/functions/wrapName.d.ts +5 -3
  22. package/dist/cjs/functions/wrapName.js +8 -12
  23. package/dist/cjs/index.d.ts +40 -44
  24. package/dist/cjs/index.js +39 -22
  25. package/dist/cjs/utils/normalise.d.ts +1 -1
  26. package/dist/cjs/utils/normalise.js +10 -3
  27. package/dist/cjs/utils/writeTx.d.ts +4 -0
  28. package/dist/cjs/utils/writeTx.js +3 -0
  29. package/dist/esm/GqlManager.d.ts +3 -0
  30. package/dist/esm/GqlManager.js +58 -2
  31. package/dist/esm/functions/burnFuses.d.ts +3 -1
  32. package/dist/esm/functions/burnFuses.js +2 -6
  33. package/dist/esm/functions/createSubname.d.ts +1 -5
  34. package/dist/esm/functions/createSubname.js +3 -7
  35. package/dist/esm/functions/deleteSubname.d.ts +3 -3
  36. package/dist/esm/functions/deleteSubname.js +5 -2
  37. package/dist/esm/functions/setName.d.ts +4 -3
  38. package/dist/esm/functions/setName.js +5 -10
  39. package/dist/esm/functions/setRecords.d.ts +4 -1
  40. package/dist/esm/functions/setRecords.js +11 -9
  41. package/dist/esm/functions/setResolver.d.ts +4 -3
  42. package/dist/esm/functions/setResolver.js +5 -11
  43. package/dist/esm/functions/transferName.d.ts +4 -3
  44. package/dist/esm/functions/transferName.js +8 -13
  45. package/dist/esm/functions/transferSubname.d.ts +4 -3
  46. package/dist/esm/functions/transferSubname.js +3 -7
  47. package/dist/esm/functions/unwrapName.d.ts +4 -3
  48. package/dist/esm/functions/unwrapName.js +3 -8
  49. package/dist/esm/functions/wrapName.d.ts +5 -3
  50. package/dist/esm/functions/wrapName.js +8 -12
  51. package/dist/esm/index.d.ts +40 -44
  52. package/dist/esm/index.js +39 -22
  53. package/dist/esm/utils/normalise.d.ts +1 -1
  54. package/dist/esm/utils/normalise.js +10 -3
  55. package/dist/esm/utils/writeTx.d.ts +4 -0
  56. package/dist/esm/utils/writeTx.js +1 -0
  57. package/package.json +7 -5
  58. package/src/GqlManager.test.ts +170 -0
  59. package/src/GqlManager.ts +67 -2
  60. package/src/functions/burnFuses.test.ts +9 -8
  61. package/src/functions/burnFuses.ts +7 -9
  62. package/src/functions/createSubname.test.ts +8 -11
  63. package/src/functions/createSubname.ts +5 -12
  64. package/src/functions/deleteSubname.test.ts +28 -27
  65. package/src/functions/deleteSubname.ts +9 -12
  66. package/src/functions/getFuses.test.ts +11 -9
  67. package/src/functions/getOwner.test.ts +3 -1
  68. package/src/functions/setName.test.ts +3 -2
  69. package/src/functions/setName.ts +15 -17
  70. package/src/functions/setRecords.test.ts +7 -5
  71. package/src/functions/setRecords.ts +19 -13
  72. package/src/functions/setResolver.test.ts +12 -15
  73. package/src/functions/setResolver.ts +14 -19
  74. package/src/functions/transferName.test.ts +27 -29
  75. package/src/functions/transferName.ts +17 -21
  76. package/src/functions/transferSubname.test.ts +30 -29
  77. package/src/functions/transferSubname.ts +19 -12
  78. package/src/functions/unwrapName.test.ts +22 -22
  79. package/src/functions/unwrapName.ts +18 -14
  80. package/src/functions/wrapName.test.ts +15 -12
  81. package/src/functions/wrapName.ts +20 -22
  82. package/src/index.ts +105 -30
  83. package/src/tests/populateTransaction.test.ts +40 -0
  84. package/src/tests/signerInjection.test.ts +46 -0
  85. package/src/utils/normalise.ts +10 -4
  86. package/src/utils/writeTx.ts +6 -0
@@ -1,7 +1,18 @@
1
- import { ethers } from 'ethers';
1
+ import { JsonRpcSigner } from '@ethersproject/providers';
2
+ import { ContractTransaction, ethers, PopulatedTransaction } from 'ethers';
2
3
  import ContractManager from './contracts';
3
4
  import { getContractAddress as _getContractAddress } from './contracts/getContractAddress';
4
5
  import { SupportedNetworkId } from './contracts/types';
6
+ import type burnFuses from './functions/burnFuses';
7
+ import type createSubname from './functions/createSubname';
8
+ import type deleteSubname from './functions/deleteSubname';
9
+ import type setName from './functions/setName';
10
+ import type setRecords from './functions/setRecords';
11
+ import type setResolver from './functions/setResolver';
12
+ import type transferName from './functions/transferName';
13
+ import type transferSubname from './functions/transferSubname';
14
+ import type unwrapName from './functions/unwrapName';
15
+ import type wrapName from './functions/wrapName';
5
16
  import GqlManager from './GqlManager';
6
17
  declare type ENSOptions = {
7
18
  graphURI?: string | null;
@@ -10,6 +21,7 @@ declare type ENSOptions = {
10
21
  export declare type InternalENS = {
11
22
  options?: ENSOptions;
12
23
  provider?: ethers.providers.Provider;
24
+ signer: JsonRpcSigner;
13
25
  graphURI?: string | null;
14
26
  } & ENS;
15
27
  export declare type ENSArgs<K extends keyof InternalENS> = {
@@ -17,6 +29,15 @@ export declare type ENSArgs<K extends keyof InternalENS> = {
17
29
  };
18
30
  declare type OmitFirstArg<F> = F extends (x: any, ...args: infer P) => infer R ? (...args: P) => R : never;
19
31
  declare type OmitFirstTwoArgs<F> = F extends (x: any, y: any, ...args: infer P) => infer R ? (...args: P) => R : never;
32
+ declare type WriteOptions = {
33
+ addressOrIndex?: string | number;
34
+ signer?: JsonRpcSigner;
35
+ };
36
+ declare type OptionalWriteOptions<F> = F extends (x: any, arg_0: infer Z, options?: infer P) => infer R ? (name: Z, options?: P & WriteOptions) => R : F extends (x: any, arg_0: infer Z, options: infer P) => infer R ? (name: Z, options: P & WriteOptions) => R : never;
37
+ interface WriteFunction<F extends (...args: any) => any> extends Function {
38
+ (...args: Parameters<OptionalWriteOptions<F>>): Promise<ContractTransaction>;
39
+ populateTransaction: (...args: Parameters<OptionalWriteOptions<F>>) => Promise<PopulatedTransaction>;
40
+ }
20
41
  export declare type RawFunction = {
21
42
  raw: (...args: any[]) => Promise<{
22
43
  to: string;
@@ -77,6 +98,14 @@ export declare class ENS {
77
98
  * @returns {OmitFirstArg} - The generated wrapped function
78
99
  */
79
100
  private generateFunction;
101
+ /**
102
+ * Generates a write wrapped function
103
+ * @param {string} path - The path of the exported function
104
+ * @param {FunctionDeps} dependencies - An array of ENS properties
105
+ * @param {string} exportName - The export name of the target function
106
+ * @returns {OmitFirstArg} - The generated wrapped function
107
+ */
108
+ private generateWriteFunction;
80
109
  /**
81
110
  * Generates a wrapped function from raw and decode exports
82
111
  * @param {string} path - The path of the exported function
@@ -423,49 +452,16 @@ export declare class ENS {
423
452
  }>;
424
453
  decode: ({ contracts }: ENSArgs<"contracts">, data: string) => Promise<any>;
425
454
  }>;
426
- setName: (name: string, address?: string | undefined, resolver?: string | undefined, options?: {
427
- addressOrIndex?: string | number | undefined;
428
- } | undefined) => Promise<ethers.ContractTransaction | undefined>;
429
- setRecords: (name: string, records: import("./utils/recordHelpers").RecordOptions) => Promise<ethers.ContractTransaction | undefined>;
430
- setResolver: (name: string, contract: "nameWrapper" | "registry", resolver?: string | undefined, options?: {
431
- addressOrIndex?: string | number | undefined;
432
- } | undefined) => Promise<ethers.ContractTransaction>;
433
- transferName: (name: string, newOwner: string, contract: "nameWrapper" | "registry" | "baseRegistrar", options?: {
434
- addressOrIndex?: string | number | undefined;
435
- } | undefined) => Promise<ethers.ContractTransaction>;
436
- wrapName: (name: string, wrappedOwner: string, fuseOptions?: string | number | import("./@types/FuseOptions").FuseOptions | undefined, resolverAddress?: string | undefined, options?: {
437
- addressOrIndex?: string | number | undefined;
438
- } | undefined) => Promise<ethers.ContractTransaction>;
439
- unwrapName: (name: string, newController: string, newRegistrant?: string | undefined, options?: {
440
- addressOrIndex?: string | number | undefined;
441
- } | undefined) => Promise<ethers.ContractTransaction>;
442
- burnFuses: (name: string, fusesToBurn: import("./@types/FuseOptions").FuseOptions) => Promise<ethers.ContractTransaction>;
443
- createSubname: (args_0: {
444
- name: string;
445
- owner: string;
446
- resolverAddress?: string | undefined;
447
- contract: "nameWrapper" | "registry";
448
- options?: {
449
- addressOrIndex?: string | number | undefined;
450
- } | undefined;
451
- } | ({
452
- contract: "nameWrapper";
453
- fuses?: import("./@types/FuseOptions").FuseOptions | undefined;
454
- } & {
455
- name: string;
456
- owner: string;
457
- resolverAddress?: string | undefined;
458
- contract: "nameWrapper" | "registry";
459
- options?: {
460
- addressOrIndex?: string | number | undefined;
461
- } | undefined;
462
- })) => Promise<ethers.ContractTransaction>;
463
- deleteSubname: (name: string, contract: "nameWrapper" | "registry", options?: {
464
- addressOrIndex?: string | number | undefined;
465
- } | undefined) => Promise<ethers.ContractTransaction>;
466
- transferSubname: (name: string, contract: "nameWrapper" | "registry", address: string, options?: {
467
- addressOrIndex?: string | number | undefined;
468
- } | undefined) => Promise<ethers.ContractTransaction>;
455
+ setName: WriteFunction<typeof setName>;
456
+ setRecords: WriteFunction<typeof setRecords>;
457
+ setResolver: WriteFunction<typeof setResolver>;
458
+ transferName: WriteFunction<typeof transferName>;
459
+ wrapName: WriteFunction<typeof wrapName>;
460
+ unwrapName: WriteFunction<typeof unwrapName>;
461
+ burnFuses: WriteFunction<typeof burnFuses>;
462
+ createSubname: WriteFunction<typeof createSubname>;
463
+ deleteSubname: WriteFunction<typeof deleteSubname>;
464
+ transferSubname: WriteFunction<typeof transferSubname>;
469
465
  getDNSOwner: (dnsName: string) => Promise<any>;
470
466
  }
471
467
  export {};
package/dist/esm/index.js CHANGED
@@ -2,6 +2,7 @@ import ContractManager from './contracts';
2
2
  import { getContractAddress as _getContractAddress } from './contracts/getContractAddress';
3
3
  import GqlManager from './GqlManager';
4
4
  import singleCall from './utils/singleCall';
5
+ import writeTx from './utils/writeTx';
5
6
  const graphURIEndpoints = {
6
7
  1: 'https://api.thegraph.com/subgraphs/name/ensdomains/ens',
7
8
  3: 'https://api.thegraph.com/subgraphs/name/ensdomains/ensropsten',
@@ -57,10 +58,25 @@ export class ENS {
57
58
  // if combine isn't specified, run normally
58
59
  // otherwise, create a function from the raw and decode functions
59
60
  if (subFunc !== 'combine') {
61
+ const writeable = subFunc === 'write' || subFunc === 'populateTransaction';
60
62
  // get the function to call
61
- const func = subFunc ? mod[exportName][subFunc] : mod[exportName];
63
+ const func = subFunc && !writeable ? mod[exportName][subFunc] : mod[exportName];
62
64
  // get the dependencies to forward to the function as the first arg
63
- const dependenciesToForward = thisRef.forwardDependenciesFromArray(dependencies);
65
+ let dependenciesToForward = thisRef.forwardDependenciesFromArray(dependencies);
66
+ // if func is write func, inject signer into dependencies
67
+ if (writeable) {
68
+ const options = (args[1] || {});
69
+ const signer = options.signer ||
70
+ thisRef.provider?.getSigner(options.addressOrIndex);
71
+ const populate = subFunc === 'populateTransaction';
72
+ if (!signer) {
73
+ throw new Error('No signer specified');
74
+ }
75
+ delete options.addressOrIndex;
76
+ delete options.signer;
77
+ dependenciesToForward = { ...dependenciesToForward, signer };
78
+ return func(dependenciesToForward, args[0], options).then(writeTx(signer, populate));
79
+ }
64
80
  // return the function with the dependencies forwarded
65
81
  return func(dependenciesToForward, ...args);
66
82
  }
@@ -77,6 +93,9 @@ export class ENS {
77
93
  mainFunc.decode = this.importGenerator(path, dependencies, exportName, 'decode');
78
94
  mainFunc.batch = this.importGenerator(path, dependencies, exportName, 'batch', { raw: mainFunc.raw, decode: mainFunc.decode });
79
95
  }
96
+ else if (subFunc === 'write') {
97
+ mainFunc.populateTransaction = this.importGenerator(path, dependencies, exportName, 'populateTransaction');
98
+ }
80
99
  return mainFunc;
81
100
  };
82
101
  /**
@@ -87,6 +106,14 @@ export class ENS {
87
106
  * @returns {OmitFirstArg} - The generated wrapped function
88
107
  */
89
108
  this.generateFunction = (path, dependencies, exportName = 'default') => this.importGenerator(path, dependencies, exportName);
109
+ /**
110
+ * Generates a write wrapped function
111
+ * @param {string} path - The path of the exported function
112
+ * @param {FunctionDeps} dependencies - An array of ENS properties
113
+ * @param {string} exportName - The export name of the target function
114
+ * @returns {OmitFirstArg} - The generated wrapped function
115
+ */
116
+ this.generateWriteFunction = (path, dependencies, exportName = 'default') => this.importGenerator(path, dependencies, exportName, 'write');
90
117
  /**
91
118
  * Generates a wrapped function from raw and decode exports
92
119
  * @param {string} path - The path of the exported function
@@ -157,32 +184,22 @@ export class ENS {
157
184
  this.universalWrapper = this.generateRawFunction('initialGetters', ['contracts'], 'universalWrapper');
158
185
  this.resolverMulticallWrapper = this.generateRawFunction('initialGetters', ['contracts'], 'resolverMulticallWrapper');
159
186
  this.multicallWrapper = this.generateRawFunction('initialGetters', ['contracts'], 'multicallWrapper');
160
- this.setName = this.generateFunction('setName', [
161
- 'contracts',
162
- 'provider',
163
- ]);
164
- this.setRecords = this.generateFunction('setRecords', [
165
- 'contracts',
166
- 'provider',
167
- 'getResolver',
168
- ]);
169
- this.setResolver = this.generateFunction('setResolver', ['contracts', 'provider']);
170
- this.transferName = this.generateFunction('transferName', ['contracts', 'provider']);
171
- this.wrapName = this.generateFunction('wrapName', [
187
+ this.setName = this.generateWriteFunction('setName', [
172
188
  'contracts',
173
- 'provider',
174
189
  ]);
175
- this.unwrapName = this.generateFunction('unwrapName', [
190
+ this.setRecords = this.generateWriteFunction('setRecords', ['contracts', 'provider', 'getResolver']);
191
+ this.setResolver = this.generateWriteFunction('setResolver', ['contracts']);
192
+ this.transferName = this.generateWriteFunction('transferName', ['contracts']);
193
+ this.wrapName = this.generateWriteFunction('wrapName', [
176
194
  'contracts',
177
- 'provider',
178
195
  ]);
179
- this.burnFuses = this.generateFunction('burnFuses', [
196
+ this.unwrapName = this.generateWriteFunction('unwrapName', ['contracts']);
197
+ this.burnFuses = this.generateWriteFunction('burnFuses', [
180
198
  'contracts',
181
- 'provider',
182
199
  ]);
183
- this.createSubname = this.generateFunction('createSubname', ['contracts', 'provider']);
184
- this.deleteSubname = this.generateFunction('deleteSubname', ['contracts', 'provider', 'transferSubname']);
185
- this.transferSubname = this.generateFunction('transferSubname', ['contracts', 'provider']);
200
+ this.createSubname = this.generateWriteFunction('createSubname', ['contracts']);
201
+ this.deleteSubname = this.generateWriteFunction('deleteSubname', ['transferSubname']);
202
+ this.transferSubname = this.generateWriteFunction('transferSubname', ['contracts']);
186
203
  this.getDNSOwner = this.generateFunction('getDNSOwner', []);
187
204
  this.options = options;
188
205
  this.getContractAddress = options?.getContractAddress || _getContractAddress;
@@ -1,2 +1,2 @@
1
1
  export declare const normalise: (name: string) => any;
2
- export declare const namehash: (inputName: string) => string;
2
+ export declare const namehash: (name: string) => string;
@@ -1,15 +1,22 @@
1
1
  import { concat, hexlify, keccak256, toUtf8Bytes } from 'ethers/lib/utils';
2
2
  import uts46 from 'idna-uts46-hx/uts46bundle.js';
3
+ import { decodeLabelhash, isEncodedLabelhash } from './labels';
3
4
  const zeros = new Uint8Array(32);
4
5
  zeros.fill(0);
5
6
  export const normalise = (name) => name ? uts46.toUnicode(name, { useStd3ASCII: true }) : name;
6
- export const namehash = (inputName) => {
7
+ export const namehash = (name) => {
7
8
  let result = zeros;
8
- const name = normalise(inputName);
9
9
  if (name) {
10
10
  const labels = name.split('.');
11
11
  for (var i = labels.length - 1; i >= 0; i--) {
12
- const labelSha = keccak256(toUtf8Bytes(labels[i]));
12
+ let labelSha;
13
+ if (isEncodedLabelhash(labels[i])) {
14
+ labelSha = decodeLabelhash(labels[i]);
15
+ }
16
+ else {
17
+ const normalised = normalise(labels[i]);
18
+ labelSha = keccak256(toUtf8Bytes(normalised));
19
+ }
13
20
  result = keccak256(concat([result, labelSha]));
14
21
  }
15
22
  }
@@ -0,0 +1,4 @@
1
+ import { JsonRpcSigner } from '@ethersproject/providers';
2
+ import type { PopulatedTransaction } from 'ethers';
3
+ declare const _default: (signer: JsonRpcSigner, populate: boolean) => (tx: PopulatedTransaction) => PopulatedTransaction | Promise<import("@ethersproject/abstract-provider").TransactionResponse>;
4
+ export default _default;
@@ -0,0 +1 @@
1
+ export default (signer, populate) => (tx) => populate ? tx : signer.sendTransaction(tx);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ensdomains/ensjs",
3
- "version": "3.0.0-alpha.4",
3
+ "version": "3.0.0-alpha.7",
4
4
  "description": "ENS javascript library for contract interaction",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "types": "./dist/cjs/index.d.ts",
@@ -46,12 +46,13 @@
46
46
  "dns-packet": "^5.3.1",
47
47
  "ethers": "^5.6.1",
48
48
  "graphql": "^16.3.0",
49
- "graphql-request": "^4.1.0",
50
- "idna-uts46-hx": "3.4.0"
49
+ "graphql-request": "next",
50
+ "idna-uts46-hx": "3.4.0",
51
+ "traverse": "^0.6.6"
51
52
  },
52
53
  "devDependencies": {
53
54
  "@ensdomains/buffer": "^0.0.13",
54
- "@ensdomains/ens-test-env": "0.1.0",
55
+ "@ensdomains/ens-test-env": "0.1.4",
55
56
  "@ethersproject/abi": "^5.6.0",
56
57
  "@ethersproject/providers": "^5.6.2",
57
58
  "@nomiclabs/hardhat-ethers": "^2.0.5",
@@ -61,6 +62,7 @@
61
62
  "@typechain/ethers-v5": "^10.0.0",
62
63
  "@types/bn.js": "^5.1.0",
63
64
  "@types/jest": "^27.4.1",
65
+ "@types/traverse": "^0.6.32",
64
66
  "dotenv": "^16.0.0",
65
67
  "ens-contracts": "github:ensdomains/ens-contracts#head=master&commit=3ecc56b14beb4aae8296f8a94f7c0d095e62fd93",
66
68
  "hardhat": "^2.9.3",
@@ -81,5 +83,5 @@
81
83
  "peerDependencies": {
82
84
  "ethers": "*"
83
85
  },
84
- "stableVersion": "3.0.0-alpha.3"
86
+ "stableVersion": "3.0.0-alpha.6"
85
87
  }
@@ -0,0 +1,170 @@
1
+ import traverse from 'traverse'
2
+ import { visit, parse } from 'graphql'
3
+
4
+ import { requestMiddleware, responseMiddleware, enter } from './GqlManager'
5
+ import { namehash } from './utils/normalise'
6
+
7
+ describe('GqlManager', () => {
8
+ const queryWithoutId = `
9
+ query getNames($id: ID!, $expiryDate: Int) {
10
+ account(id: $id) {
11
+ registrations(first: 1000, where: { expiryDate_gt: $expiryDate }) {
12
+ registrationDate
13
+ expiryDate
14
+ domain {
15
+ labelName
16
+ labelhash
17
+ name
18
+ isMigrated
19
+ parent {
20
+ name
21
+ }
22
+ }
23
+ }
24
+ domains(first: 1000) {
25
+ labelName
26
+ labelhash
27
+ name
28
+ isMigrated
29
+ parent {
30
+ name
31
+ }
32
+ createdAt
33
+ }
34
+ }
35
+ }
36
+ `
37
+
38
+ const mockRequest = {
39
+ method: 'POST',
40
+ headers: {
41
+ 'Content-Type': 'application/json',
42
+ },
43
+ body: JSON.stringify({ query: queryWithoutId }),
44
+ }
45
+
46
+ const mockResponse = {
47
+ data: {
48
+ account: {
49
+ registrations: [
50
+ {
51
+ registrationDate: '1659245504',
52
+ expiryDate: '1690781504',
53
+ domain: {
54
+ id: '0xb1ccf7f6bb648f39ba48bf80f4a8a4522f474ee7d1edd6ea65f7fa51fe8b1612',
55
+ labelName: 'randydandy',
56
+ labelhash:
57
+ '0x63fd5bfdd0b1eb9f4c33c209b7c3e7dcb87a761d926a65e267f7cb99ee313ddd',
58
+ name: '0xde2551f43e950a2a4d4e6052f25edc450c48a5338faa27f09d1ee99ca9dc04fd',
59
+ isMigrated: true,
60
+ parent: {
61
+ name: '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae',
62
+ invalidName: true,
63
+ },
64
+ invalidName: true,
65
+ },
66
+ },
67
+ {
68
+ registrationDate: '1635284756',
69
+ expiryDate: '1666841708',
70
+ domain: {
71
+ id: '0xde2551f43e950a2a4d4e6052f25edc450c48a5338faa27f09d1ee99ca9dc04fd',
72
+ labelName: 'randydandy',
73
+ labelhash:
74
+ '0x6a877a8d92ad83c8d044d0c5b69aa0da3050f4d653dcc149fca952d6439e4105',
75
+ name: 'randydandy.eth',
76
+ isMigrated: true,
77
+ parent: {
78
+ name: '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae',
79
+ invalidName: true,
80
+ },
81
+ },
82
+ },
83
+ {
84
+ registrationDate: '1659250465',
85
+ expiryDate: '1690786465',
86
+ domain: {
87
+ id: '0xaf119e72f050e5acad6b2d97826a7afb45afe9407376a2ab8240b62173b2d7c2',
88
+ labelName: 'nullbyte',
89
+ labelhash:
90
+ '0xc8985f8323b3dc707003a6ef5b379d9e8a058b6d6bcc2cfb307c61ca36920e27',
91
+ name: '0xb54c7c79c89d571f1fbf4c67f524e336a04441eeee4d76f156e835da99a46ddb',
92
+ isMigrated: true,
93
+ parent: {
94
+ name: '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae',
95
+ invalidName: true,
96
+ },
97
+ invalidName: true,
98
+ },
99
+ },
100
+ ],
101
+ domains: [
102
+ {
103
+ id: '0xaf119e72f050e5acad6b2d97826a7afb45afe9407376a2ab8240b62173b2d7c2',
104
+ labelName: 'nullbyte',
105
+ labelhash:
106
+ '0xc8985f8323b3dc707003a6ef5b379d9e8a058b6d6bcc2cfb307c61ca36920e27',
107
+ name: '0xb54c7c79c89d571f1fbf4c67f524e336a04441eeee4d76f156e835da99a46ddb',
108
+ isMigrated: true,
109
+ parent: {
110
+ name: '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae',
111
+ invalidName: true,
112
+ },
113
+ createdAt: '1659250465',
114
+ invalidName: true,
115
+ },
116
+ {
117
+ id: '0xb1ccf7f6bb648f39ba48bf80f4a8a4522f474ee7d1edd6ea65f7fa51fe8b1612',
118
+ labelName: 'randydandy',
119
+ labelhash:
120
+ '0x63fd5bfdd0b1eb9f4c33c209b7c3e7dcb87a761d926a65e267f7cb99ee313ddd',
121
+ name: '0xde2551f43e950a2a4d4e6052f25edc450c48a5338faa27f09d1ee99ca9dc04fd',
122
+ isMigrated: true,
123
+ parent: {
124
+ name: '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae',
125
+ invalidName: true,
126
+ },
127
+ createdAt: '1659245504',
128
+ invalidName: true,
129
+ },
130
+ {
131
+ id: '0xde2551f43e950a2a4d4e6052f25edc450c48a5338faa27f09d1ee99ca9dc04fd',
132
+ labelName: 'randydandy',
133
+ labelhash:
134
+ '0x6a877a8d92ad83c8d044d0c5b69aa0da3050f4d653dcc149fca952d6439e4105',
135
+ name: 'randydandy.eth',
136
+ isMigrated: true,
137
+ parent: {
138
+ name: '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae',
139
+ invalidName: true,
140
+ },
141
+ createdAt: '1635284756',
142
+ },
143
+ ],
144
+ },
145
+ },
146
+ headers: {
147
+ map: {
148
+ 'content-type': 'application/json',
149
+ },
150
+ },
151
+ status: 200,
152
+ }
153
+
154
+ describe('requestMiddleware', () => {
155
+ it('should add id to a SelectionSet if name is present and id is not', () => {
156
+ const result = requestMiddleware(visit, parse)(mockRequest)
157
+ expect(result.body.includes('id')).toBe(true)
158
+ })
159
+ })
160
+ describe('responseMiddleware', () => {
161
+ it('should replace name with the namehash when there is an invalid name and id combo', () => {
162
+ const result = responseMiddleware(traverse)(mockResponse)
163
+ expect(result.data.account.domains[0].name).toBe(
164
+ namehash(
165
+ '0xb54c7c79c89d571f1fbf4c67f524e336a04441eeee4d76f156e835da99a46ddb',
166
+ ),
167
+ )
168
+ })
169
+ })
170
+ })
package/src/GqlManager.ts CHANGED
@@ -1,11 +1,76 @@
1
+ // @ts-nocheck
2
+ import { namehash } from './utils/normalise'
3
+
4
+ const generateSelection = (selection: any) => ({
5
+ kind: 'Field',
6
+ name: {
7
+ kind: 'Name',
8
+ value: selection,
9
+ },
10
+ arguments: [],
11
+ directives: [],
12
+ alias: undefined,
13
+ selectionSet: undefined,
14
+ })
15
+
16
+ export const enter = (node: any) => {
17
+ if (node.kind === 'SelectionSet') {
18
+ const id = node.selections.find((x) => x.name && x.name.value === 'id')
19
+ const name = node.selections.find((x) => x.name && x.name.value === 'name')
20
+
21
+ if (!id && name) {
22
+ node.selections = [...node.selections, generateSelection('id')]
23
+ return node
24
+ }
25
+ }
26
+ }
27
+
28
+ export const requestMiddleware = (visit, parse) => (request: any) => {
29
+ const requestBody = JSON.parse(request.body)
30
+ const rawQuery = requestBody.query
31
+ const parsedQuery = parse(rawQuery)
32
+ const updatedQuery = visit(parsedQuery, { enter })
33
+ const updatedBody = { ...requestBody, query: updatedQuery.loc.source.body }
34
+
35
+ return {
36
+ ...request,
37
+ body: JSON.stringify(updatedBody),
38
+ }
39
+ }
40
+
41
+ export const responseMiddleware = (traverse) => (response: any) => {
42
+ traverse(response).forEach(function (responseItem: any) {
43
+ if (responseItem instanceof Object && responseItem.name) {
44
+ //Name already in hashed form
45
+ if (responseItem.name && responseItem.name.includes('[')) {
46
+ return
47
+ }
48
+
49
+ const hashedName = namehash(responseItem.name)
50
+ if (responseItem.id !== hashedName) {
51
+ this.update({ ...responseItem, name: hashedName, invalidName: true })
52
+ }
53
+ }
54
+ })
55
+ return response
56
+ }
57
+
1
58
  export default class GqlManager {
2
59
  public gql: any = () => null
3
60
  public client?: any | null = null
4
61
 
5
62
  public setUrl = async (url: string | null) => {
6
63
  if (url) {
7
- const imported = await import('graphql-request')
8
- this.client = new imported.GraphQLClient(url)
64
+ const [imported, traverse, { visit, parse }] = await Promise.all([
65
+ import('graphql-request'),
66
+ import('traverse'),
67
+ import('graphql/language'),
68
+ ])
69
+
70
+ this.client = new imported.GraphQLClient(url, {
71
+ requestMiddleware: requestMiddleware(visit, parse),
72
+ responseMiddleware: responseMiddleware(traverse.default),
73
+ })
9
74
  this.gql = imported.gql
10
75
  } else {
11
76
  this.client = null
@@ -1,7 +1,7 @@
1
1
  import { BigNumber, ethers } from 'ethers'
2
2
  import { ENS } from '..'
3
- import { namehash } from '../utils/normalise'
4
3
  import setup from '../tests/setup'
4
+ import { namehash } from '../utils/normalise'
5
5
 
6
6
  let ENSInstance: ENS
7
7
  let revert: Awaited<ReturnType<typeof setup>>['revert']
@@ -22,16 +22,17 @@ describe('burnFuses', () => {
22
22
  await revert()
23
23
  })
24
24
  it('should return a burnFuses transaction and succeed', async () => {
25
- const wrapNameTx = await ENSInstance.wrapName(
26
- 'parthtejpal.eth',
27
- accounts[0],
28
- )
25
+ const wrapNameTx = await ENSInstance.wrapName('parthtejpal.eth', {
26
+ wrappedOwner: accounts[0],
27
+ })
29
28
  await wrapNameTx.wait()
30
29
 
31
30
  const tx = await ENSInstance.burnFuses('parthtejpal.eth', {
32
- cannotUnwrap: true,
33
- cannotCreateSubdomain: true,
34
- cannotSetTtl: true,
31
+ fusesToBurn: {
32
+ cannotUnwrap: true,
33
+ cannotCreateSubdomain: true,
34
+ cannotSetTtl: true,
35
+ },
35
36
  })
36
37
  expect(tx).toBeTruthy()
37
38
  await tx.wait()
@@ -4,20 +4,18 @@ import generateFuseInput from '../utils/generateFuseInput'
4
4
  import { namehash } from '../utils/normalise'
5
5
 
6
6
  export default async function (
7
- { contracts, provider }: ENSArgs<'contracts' | 'provider'>,
7
+ { contracts, signer }: ENSArgs<'contracts' | 'signer'>,
8
8
  name: string,
9
- fusesToBurn: FuseOptions,
9
+ {
10
+ fusesToBurn,
11
+ }: {
12
+ fusesToBurn: FuseOptions
13
+ },
10
14
  ) {
11
- const signer = provider?.getSigner()
12
-
13
- if (!signer) {
14
- throw new Error('No signer found')
15
- }
16
-
17
15
  const nameWrapper = (await contracts?.getNameWrapper()!).connect(signer)
18
16
  const hash = namehash(name)
19
17
 
20
18
  const encodedFuses = generateFuseInput(fusesToBurn)
21
19
 
22
- return nameWrapper.burnFuses(hash, encodedFuses)
20
+ return nameWrapper.populateTransaction.burnFuses(hash, encodedFuses)
23
21
  }
@@ -1,7 +1,7 @@
1
1
  import { ethers } from 'ethers'
2
2
  import { ENS } from '..'
3
- import { namehash } from '../utils/normalise'
4
3
  import setup from '../tests/setup'
4
+ import { namehash } from '../utils/normalise'
5
5
 
6
6
  let ENSInstance: ENS
7
7
  let revert: Awaited<ReturnType<typeof setup>>['revert']
@@ -23,11 +23,10 @@ describe('createSubname', () => {
23
23
  await revert()
24
24
  })
25
25
  it('should allow creating a subname on the registry', async () => {
26
- const tx = await ENSInstance.createSubname({
26
+ const tx = await ENSInstance.createSubname('test.parthtejpal.eth', {
27
27
  contract: 'registry',
28
- name: 'test.parthtejpal.eth',
29
28
  owner: accounts[0],
30
- options: { addressOrIndex: 0 },
29
+ addressOrIndex: 0,
31
30
  })
32
31
  expect(tx).toBeTruthy()
33
32
  await tx.wait()
@@ -37,16 +36,14 @@ describe('createSubname', () => {
37
36
  expect(result).toBe(accounts[0])
38
37
  })
39
38
  it('should allow creating a subname on the namewrapper', async () => {
40
- const wrapNameTx = await ENSInstance.wrapName(
41
- 'parthtejpal.eth',
42
- accounts[0],
43
- )
39
+ const wrapNameTx = await ENSInstance.wrapName('parthtejpal.eth', {
40
+ wrappedOwner: accounts[0],
41
+ })
44
42
  await wrapNameTx.wait()
45
- const tx = await ENSInstance.createSubname({
43
+ const tx = await ENSInstance.createSubname('test.parthtejpal.eth', {
46
44
  contract: 'nameWrapper',
47
- name: 'test.parthtejpal.eth',
48
45
  owner: accounts[0],
49
- options: { addressOrIndex: 0 },
46
+ addressOrIndex: 0,
50
47
  })
51
48
  expect(tx).toBeTruthy()
52
49
  await tx.wait()