@ensdomains/ensjs 3.0.0-alpha.45 → 3.0.0-alpha.46

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.
@@ -28,6 +28,16 @@ const mapDomain = ({ name, ...domain }) => {
28
28
  const decrypted = name ? (0, import_labels.decryptName)(name) : void 0;
29
29
  return {
30
30
  ...domain,
31
+ ...domain.registration ? {
32
+ registration: {
33
+ expiryDate: new Date(
34
+ parseInt(domain.registration.expiryDate) * 1e3
35
+ ),
36
+ registrationDate: new Date(
37
+ parseInt(domain.registration.registrationDate) * 1e3
38
+ )
39
+ }
40
+ } : {},
31
41
  name: decrypted,
32
42
  truncatedName: decrypted ? (0, import_format.truncateFormat)(decrypted) : void 0,
33
43
  createdAt: new Date(parseInt(domain.createdAt) * 1e3),
@@ -35,19 +45,13 @@ const mapDomain = ({ name, ...domain }) => {
35
45
  };
36
46
  };
37
47
  const mapWrappedDomain = (wrappedDomain) => {
38
- const domain = mapDomain(wrappedDomain.domain);
39
- if (domain.registration) {
40
- domain.registration = {
41
- expiryDate: new Date(
42
- parseInt(domain.registration.expiryDate) * 1e3
43
- ),
44
- registrationDate: new Date(
45
- parseInt(domain.registration.registrationDate) * 1e3
46
- )
47
- };
48
+ const expiryDate = wrappedDomain.expiryDate && wrappedDomain.expiryDate !== "0" ? new Date(parseInt(wrappedDomain.expiryDate) * 1e3) : void 0;
49
+ if (expiryDate && expiryDate < new Date() && (0, import_fuses.checkPCCBurned)(wrappedDomain.fuses)) {
50
+ return null;
48
51
  }
52
+ const domain = mapDomain(wrappedDomain.domain);
49
53
  return {
50
- expiryDate: new Date(parseInt(wrappedDomain.expiryDate) * 1e3),
54
+ expiryDate,
51
55
  fuses: (0, import_fuses.decodeFuses)(wrappedDomain.fuses),
52
56
  ...domain,
53
57
  type: "wrappedDomain"
@@ -106,6 +110,10 @@ const getNames = async ({ gqlInstance }, {
106
110
  domains(first: 1000) {
107
111
  ${domainQueryData}
108
112
  createdAt
113
+ registration {
114
+ registrationDate
115
+ expiryDate
116
+ }
109
117
  }
110
118
  wrappedDomains(first: 1000) {
111
119
  expiryDate
@@ -137,6 +145,10 @@ const getNames = async ({ gqlInstance }, {
137
145
  domains(orderBy: $orderBy, orderDirection: $orderDirection) {
138
146
  ${domainQueryData}
139
147
  createdAt
148
+ registration {
149
+ registrationDate
150
+ expiryDate
151
+ }
140
152
  }
141
153
  }
142
154
  }
@@ -310,7 +322,7 @@ const getNames = async ({ gqlInstance }, {
310
322
  return [
311
323
  ...(account == null ? void 0 : account.domains.map(mapDomain)) || [],
312
324
  ...(account == null ? void 0 : account.registrations.map(mapRegistration)) || [],
313
- ...(account == null ? void 0 : account.wrappedDomains.map(mapWrappedDomain)) || []
325
+ ...(account == null ? void 0 : account.wrappedDomains.map(mapWrappedDomain).filter((d) => d)) || []
314
326
  ].sort((a, b) => {
315
327
  if (orderDirection === "desc") {
316
328
  if (orderBy === "labelName") {
@@ -328,7 +340,7 @@ const getNames = async ({ gqlInstance }, {
328
340
  return (account == null ? void 0 : account.domains.map(mapDomain)) || [];
329
341
  }
330
342
  if (type === "wrappedOwner") {
331
- return (account == null ? void 0 : account.wrappedDomains.map(mapWrappedDomain)) || [];
343
+ return (account == null ? void 0 : account.wrappedDomains.map(mapWrappedDomain).filter((d) => d)) || [];
332
344
  }
333
345
  return (account == null ? void 0 : account.registrations.map(mapRegistration)) || [];
334
346
  };
@@ -22,6 +22,7 @@ __export(getSubnames_exports, {
22
22
  });
23
23
  module.exports = __toCommonJS(getSubnames_exports);
24
24
  var import_format = require("../utils/format");
25
+ var import_fuses = require("../utils/fuses");
25
26
  var import_labels = require("../utils/labels");
26
27
  var import_normalise = require("../utils/normalise");
27
28
  const largeQuery = async ({ gqlInstance }, {
@@ -77,6 +78,13 @@ const largeQuery = async ({ gqlInstance }, {
77
78
  owner {
78
79
  id
79
80
  }
81
+ wrappedDomain {
82
+ fuses
83
+ expiryDate
84
+ owner {
85
+ id
86
+ }
87
+ }
80
88
  }
81
89
  }
82
90
  }
@@ -92,14 +100,30 @@ const largeQuery = async ({ gqlInstance }, {
92
100
  };
93
101
  const response = await client.request(finalQuery, queryVars);
94
102
  const domain = response == null ? void 0 : response.domain;
95
- const subdomains = domain.subdomains.map((subname) => {
96
- const decrypted = (0, import_labels.decryptName)(subname.name);
97
- return {
98
- ...subname,
99
- name: decrypted,
100
- truncatedName: (0, import_format.truncateFormat)(decrypted)
101
- };
102
- });
103
+ const subdomains = domain.subdomains.map(
104
+ ({ wrappedDomain, ...subname }) => {
105
+ const decrypted = (0, import_labels.decryptName)(subname.name);
106
+ const obj = {
107
+ ...subname,
108
+ labelName: subname.labelName || null,
109
+ labelhash: subname.labelhash || "",
110
+ name: decrypted,
111
+ truncatedName: (0, import_format.truncateFormat)(decrypted),
112
+ owner: subname.owner.id,
113
+ type: "domain"
114
+ };
115
+ if (wrappedDomain) {
116
+ obj.type = "wrappedDomain";
117
+ const expiryDateAsDate = wrappedDomain.expiryDate && wrappedDomain.expiryDate !== "0" ? new Date(parseInt(wrappedDomain.expiryDate) * 1e3) : void 0;
118
+ const hasExpired = expiryDateAsDate && expiryDateAsDate < new Date();
119
+ obj.expiryDate = expiryDateAsDate;
120
+ obj.fuses = (0, import_fuses.decodeFuses)(hasExpired ? 0 : wrappedDomain.fuses);
121
+ obj.pccExpired = hasExpired ? (0, import_fuses.checkPCCBurned)(wrappedDomain.fuses) : false;
122
+ obj.owner = obj.pccExpired ? void 0 : wrappedDomain.owner.id;
123
+ }
124
+ return obj;
125
+ }
126
+ );
103
127
  return {
104
128
  subnames: subdomains,
105
129
  subnameCount: domain.subdomainCount
@@ -23,4 +23,4 @@ __export(consts_exports, {
23
23
  });
24
24
  module.exports = __toCommonJS(consts_exports);
25
25
  const EMPTY_ADDRESS = "0x0000000000000000000000000000000000000000";
26
- const MAX_INT_64 = 2n ** 64n - 1n;
26
+ const MAX_INT_64 = BigInt("18446744073709551615");
@@ -21,6 +21,7 @@ __export(fuses_exports, {
21
21
  CHILD_CONTROLLED_FUSES: () => CHILD_CONTROLLED_FUSES,
22
22
  PARENT_CONTROLLED_FUSES: () => PARENT_CONTROLLED_FUSES,
23
23
  USER_SETTABLE_FUSES: () => USER_SETTABLE_FUSES,
24
+ checkPCCBurned: () => checkPCCBurned,
24
25
  childFuseEnum: () => childFuseEnum,
25
26
  childFuseKeys: () => childFuseKeys,
26
27
  decodeFuses: () => decodeFuses,
@@ -297,4 +298,5 @@ const decodeFuses = (fuses) => {
297
298
  }
298
299
  };
299
300
  };
301
+ const checkPCCBurned = (fuses) => (fuses & PARENT_CANNOT_CONTROL) === PARENT_CANNOT_CONTROL;
300
302
  var fuses_default = fullFuseEnum;
@@ -1,11 +1,21 @@
1
1
  // src/functions/getNames.ts
2
2
  import { truncateFormat } from "../utils/format.mjs";
3
- import { decodeFuses } from "../utils/fuses.mjs";
3
+ import { checkPCCBurned, decodeFuses } from "../utils/fuses.mjs";
4
4
  import { decryptName } from "../utils/labels.mjs";
5
5
  var mapDomain = ({ name, ...domain }) => {
6
6
  const decrypted = name ? decryptName(name) : void 0;
7
7
  return {
8
8
  ...domain,
9
+ ...domain.registration ? {
10
+ registration: {
11
+ expiryDate: new Date(
12
+ parseInt(domain.registration.expiryDate) * 1e3
13
+ ),
14
+ registrationDate: new Date(
15
+ parseInt(domain.registration.registrationDate) * 1e3
16
+ )
17
+ }
18
+ } : {},
9
19
  name: decrypted,
10
20
  truncatedName: decrypted ? truncateFormat(decrypted) : void 0,
11
21
  createdAt: new Date(parseInt(domain.createdAt) * 1e3),
@@ -13,19 +23,13 @@ var mapDomain = ({ name, ...domain }) => {
13
23
  };
14
24
  };
15
25
  var mapWrappedDomain = (wrappedDomain) => {
16
- const domain = mapDomain(wrappedDomain.domain);
17
- if (domain.registration) {
18
- domain.registration = {
19
- expiryDate: new Date(
20
- parseInt(domain.registration.expiryDate) * 1e3
21
- ),
22
- registrationDate: new Date(
23
- parseInt(domain.registration.registrationDate) * 1e3
24
- )
25
- };
26
+ const expiryDate = wrappedDomain.expiryDate && wrappedDomain.expiryDate !== "0" ? new Date(parseInt(wrappedDomain.expiryDate) * 1e3) : void 0;
27
+ if (expiryDate && expiryDate < new Date() && checkPCCBurned(wrappedDomain.fuses)) {
28
+ return null;
26
29
  }
30
+ const domain = mapDomain(wrappedDomain.domain);
27
31
  return {
28
- expiryDate: new Date(parseInt(wrappedDomain.expiryDate) * 1e3),
32
+ expiryDate,
29
33
  fuses: decodeFuses(wrappedDomain.fuses),
30
34
  ...domain,
31
35
  type: "wrappedDomain"
@@ -84,6 +88,10 @@ var getNames = async ({ gqlInstance }, {
84
88
  domains(first: 1000) {
85
89
  ${domainQueryData}
86
90
  createdAt
91
+ registration {
92
+ registrationDate
93
+ expiryDate
94
+ }
87
95
  }
88
96
  wrappedDomains(first: 1000) {
89
97
  expiryDate
@@ -115,6 +123,10 @@ var getNames = async ({ gqlInstance }, {
115
123
  domains(orderBy: $orderBy, orderDirection: $orderDirection) {
116
124
  ${domainQueryData}
117
125
  createdAt
126
+ registration {
127
+ registrationDate
128
+ expiryDate
129
+ }
118
130
  }
119
131
  }
120
132
  }
@@ -288,7 +300,7 @@ var getNames = async ({ gqlInstance }, {
288
300
  return [
289
301
  ...account?.domains.map(mapDomain) || [],
290
302
  ...account?.registrations.map(mapRegistration) || [],
291
- ...account?.wrappedDomains.map(mapWrappedDomain) || []
303
+ ...account?.wrappedDomains.map(mapWrappedDomain).filter((d) => d) || []
292
304
  ].sort((a, b) => {
293
305
  if (orderDirection === "desc") {
294
306
  if (orderBy === "labelName") {
@@ -306,7 +318,7 @@ var getNames = async ({ gqlInstance }, {
306
318
  return account?.domains.map(mapDomain) || [];
307
319
  }
308
320
  if (type === "wrappedOwner") {
309
- return account?.wrappedDomains.map(mapWrappedDomain) || [];
321
+ return account?.wrappedDomains.map(mapWrappedDomain).filter((d) => d) || [];
310
322
  }
311
323
  return account?.registrations.map(mapRegistration) || [];
312
324
  };
@@ -1,5 +1,6 @@
1
1
  // src/functions/getSubnames.ts
2
2
  import { truncateFormat } from "../utils/format.mjs";
3
+ import { checkPCCBurned, decodeFuses } from "../utils/fuses.mjs";
3
4
  import { decryptName } from "../utils/labels.mjs";
4
5
  import { namehash } from "../utils/normalise.mjs";
5
6
  var largeQuery = async ({ gqlInstance }, {
@@ -55,6 +56,13 @@ var largeQuery = async ({ gqlInstance }, {
55
56
  owner {
56
57
  id
57
58
  }
59
+ wrappedDomain {
60
+ fuses
61
+ expiryDate
62
+ owner {
63
+ id
64
+ }
65
+ }
58
66
  }
59
67
  }
60
68
  }
@@ -70,14 +78,30 @@ var largeQuery = async ({ gqlInstance }, {
70
78
  };
71
79
  const response = await client.request(finalQuery, queryVars);
72
80
  const domain = response?.domain;
73
- const subdomains = domain.subdomains.map((subname) => {
74
- const decrypted = decryptName(subname.name);
75
- return {
76
- ...subname,
77
- name: decrypted,
78
- truncatedName: truncateFormat(decrypted)
79
- };
80
- });
81
+ const subdomains = domain.subdomains.map(
82
+ ({ wrappedDomain, ...subname }) => {
83
+ const decrypted = decryptName(subname.name);
84
+ const obj = {
85
+ ...subname,
86
+ labelName: subname.labelName || null,
87
+ labelhash: subname.labelhash || "",
88
+ name: decrypted,
89
+ truncatedName: truncateFormat(decrypted),
90
+ owner: subname.owner.id,
91
+ type: "domain"
92
+ };
93
+ if (wrappedDomain) {
94
+ obj.type = "wrappedDomain";
95
+ const expiryDateAsDate = wrappedDomain.expiryDate && wrappedDomain.expiryDate !== "0" ? new Date(parseInt(wrappedDomain.expiryDate) * 1e3) : void 0;
96
+ const hasExpired = expiryDateAsDate && expiryDateAsDate < new Date();
97
+ obj.expiryDate = expiryDateAsDate;
98
+ obj.fuses = decodeFuses(hasExpired ? 0 : wrappedDomain.fuses);
99
+ obj.pccExpired = hasExpired ? checkPCCBurned(wrappedDomain.fuses) : false;
100
+ obj.owner = obj.pccExpired ? void 0 : wrappedDomain.owner.id;
101
+ }
102
+ return obj;
103
+ }
104
+ );
81
105
  return {
82
106
  subnames: subdomains,
83
107
  subnameCount: domain.subdomainCount
@@ -1,6 +1,6 @@
1
1
  // src/utils/consts.ts
2
2
  var EMPTY_ADDRESS = "0x0000000000000000000000000000000000000000";
3
- var MAX_INT_64 = 2n ** 64n - 1n;
3
+ var MAX_INT_64 = BigInt("18446744073709551615");
4
4
  export {
5
5
  EMPTY_ADDRESS,
6
6
  MAX_INT_64
@@ -257,11 +257,13 @@ var decodeFuses = (fuses) => {
257
257
  }
258
258
  };
259
259
  };
260
+ var checkPCCBurned = (fuses) => (fuses & PARENT_CANNOT_CONTROL) === PARENT_CANNOT_CONTROL;
260
261
  var fuses_default = fullFuseEnum;
261
262
  export {
262
263
  CHILD_CONTROLLED_FUSES,
263
264
  PARENT_CONTROLLED_FUSES,
264
265
  USER_SETTABLE_FUSES,
266
+ checkPCCBurned,
265
267
  childFuseEnum,
266
268
  childFuseKeys,
267
269
  decodeFuses,
@@ -1,15 +1,27 @@
1
1
  import { ENSArgs } from '..';
2
- declare type Subname = {
2
+ import { AllCurrentFuses } from '../utils/fuses';
3
+ declare type BaseSubname = {
3
4
  id: string;
4
5
  labelName: string | null;
5
6
  truncatedName?: string;
6
7
  labelhash: string;
7
8
  isMigrated: boolean;
8
9
  name: string;
9
- owner: {
10
- id: string;
11
- };
10
+ owner: string | undefined;
12
11
  };
12
+ declare type UnwrappedSubname = BaseSubname & {
13
+ fuses?: never;
14
+ expiryDate?: never;
15
+ pccExpired?: never;
16
+ type: 'domain';
17
+ };
18
+ declare type WrappedSubname = BaseSubname & {
19
+ fuses: AllCurrentFuses;
20
+ expiryDate: Date;
21
+ pccExpired: boolean;
22
+ type: 'wrappedDomain';
23
+ };
24
+ declare type Subname = WrappedSubname | UnwrappedSubname;
13
25
  declare type Params = {
14
26
  name: string;
15
27
  page?: number;
@@ -461,17 +461,75 @@ export declare class ENS {
461
461
  lastSubnames?: any[] | undefined;
462
462
  search?: string | undefined;
463
463
  }) => Promise<{
464
- subnames: {
464
+ subnames: (({
465
465
  id: string;
466
466
  labelName: string | null;
467
467
  truncatedName?: string | undefined;
468
468
  labelhash: string;
469
469
  isMigrated: boolean;
470
470
  name: string;
471
- owner: {
472
- id: string;
471
+ owner: string | undefined;
472
+ } & {
473
+ fuses?: undefined;
474
+ expiryDate?: undefined;
475
+ pccExpired?: undefined;
476
+ type: "domain";
477
+ }) | ({
478
+ id: string;
479
+ labelName: string | null;
480
+ truncatedName?: string | undefined;
481
+ labelhash: string;
482
+ isMigrated: boolean;
483
+ name: string;
484
+ owner: string | undefined;
485
+ } & {
486
+ fuses: {
487
+ parent: {
488
+ unnamed: {
489
+ 524288: boolean;
490
+ 1048576: boolean;
491
+ 2097152: boolean;
492
+ 4194304: boolean;
493
+ 8388608: boolean;
494
+ 16777216: boolean;
495
+ 33554432: boolean;
496
+ 67108864: boolean;
497
+ 134217728: boolean;
498
+ 268435456: boolean;
499
+ 536870912: boolean;
500
+ 1073741824: boolean;
501
+ 2147483648: boolean;
502
+ };
503
+ IS_DOT_ETH: boolean;
504
+ PARENT_CANNOT_CONTROL: boolean;
505
+ CAN_EXTEND_EXPIRY: boolean;
506
+ };
507
+ child: {
508
+ CAN_DO_EVERYTHING: boolean;
509
+ unnamed: {
510
+ 1024: boolean;
511
+ 64: boolean;
512
+ 128: boolean;
513
+ 256: boolean;
514
+ 512: boolean;
515
+ 2048: boolean;
516
+ 4096: boolean;
517
+ 8192: boolean;
518
+ 16384: boolean;
519
+ 32768: boolean;
520
+ };
521
+ CANNOT_UNWRAP: boolean;
522
+ CANNOT_BURN_FUSES: boolean;
523
+ CANNOT_TRANSFER: boolean;
524
+ CANNOT_SET_RESOLVER: boolean;
525
+ CANNOT_SET_TTL: boolean;
526
+ CANNOT_CREATE_SUBDOMAIN: boolean;
527
+ };
473
528
  };
474
- }[];
529
+ expiryDate: Date;
530
+ pccExpired: boolean;
531
+ type: "wrappedDomain";
532
+ }))[];
475
533
  subnameCount: number;
476
534
  }>;
477
535
  getNames: (args_0: {
@@ -128,5 +128,6 @@ export declare const decodeFuses: (fuses: number) => {
128
128
  CANNOT_CREATE_SUBDOMAIN: boolean;
129
129
  };
130
130
  };
131
+ export declare const checkPCCBurned: (fuses: number) => boolean;
131
132
  export declare type AllCurrentFuses = ReturnType<typeof decodeFuses>;
132
133
  export default fullFuseEnum;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ensdomains/ensjs",
3
- "version": "3.0.0-alpha.45",
3
+ "version": "3.0.0-alpha.46",
4
4
  "description": "ENS javascript library for contract interaction",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "module": "./dist/esm/index.mjs",
@@ -166,13 +166,16 @@ describe('getNames', () => {
166
166
  })
167
167
  expect(pageFive).toHaveLength(totalOwnedNames % 10)
168
168
  })
169
- it('should get wrapped domains for an address with pagination', async () => {
169
+ it('should get wrapped domains for an address with pagination, and filter out pcc expired names', async () => {
170
170
  const pageOne = await ensInstance.getNames({
171
171
  address: '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC',
172
172
  type: 'wrappedOwner',
173
173
  page: 0,
174
174
  })
175
- expect(pageOne).toHaveLength(2)
175
+ // length of page one should be all the names on 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC
176
+ // minus 1 for the PCC expired name.
177
+ // the result here implies that the PCC expired name is not returned
178
+ expect(pageOne).toHaveLength(4)
176
179
  })
177
180
  describe('orderBy', () => {
178
181
  describe('registrations', () => {
@@ -1,6 +1,6 @@
1
1
  import { ENSArgs } from '..'
2
2
  import { truncateFormat } from '../utils/format'
3
- import { AllCurrentFuses, decodeFuses } from '../utils/fuses'
3
+ import { AllCurrentFuses, checkPCCBurned, decodeFuses } from '../utils/fuses'
4
4
  import { decryptName } from '../utils/labels'
5
5
  import { Domain, Registration, WrappedDomain } from '../utils/subgraph-types'
6
6
 
@@ -60,8 +60,21 @@ type Params = BaseParams &
60
60
 
61
61
  const mapDomain = ({ name, ...domain }: Domain) => {
62
62
  const decrypted = name ? decryptName(name) : undefined
63
+
63
64
  return {
64
65
  ...domain,
66
+ ...(domain.registration
67
+ ? {
68
+ registration: {
69
+ expiryDate: new Date(
70
+ parseInt(domain.registration.expiryDate) * 1000,
71
+ ),
72
+ registrationDate: new Date(
73
+ parseInt(domain.registration.registrationDate) * 1000,
74
+ ),
75
+ },
76
+ }
77
+ : {}),
65
78
  name: decrypted,
66
79
  truncatedName: decrypted ? truncateFormat(decrypted) : undefined,
67
80
  createdAt: new Date(parseInt(domain.createdAt) * 1000),
@@ -70,27 +83,27 @@ const mapDomain = ({ name, ...domain }: Domain) => {
70
83
  }
71
84
 
72
85
  const mapWrappedDomain = (wrappedDomain: WrappedDomain) => {
73
- const domain = mapDomain(wrappedDomain.domain) as Omit<
74
- ReturnType<typeof mapDomain>,
75
- 'registration'
76
- > & {
77
- registration?: {
78
- expiryDate: string | Date
79
- registrationDate: string | Date
80
- }
81
- }
82
- if (domain.registration) {
83
- domain.registration = {
84
- expiryDate: new Date(
85
- parseInt(domain.registration.expiryDate as string) * 1000,
86
- ),
87
- registrationDate: new Date(
88
- parseInt(domain.registration.registrationDate as string) * 1000,
89
- ),
90
- }
86
+ const expiryDate =
87
+ wrappedDomain.expiryDate && wrappedDomain.expiryDate !== '0'
88
+ ? new Date(parseInt(wrappedDomain.expiryDate) * 1000)
89
+ : undefined
90
+ if (
91
+ expiryDate &&
92
+ expiryDate < new Date() &&
93
+ checkPCCBurned(wrappedDomain.fuses)
94
+ ) {
95
+ // PCC was burned previously and now the fuses are expired meaning that the
96
+ // owner is now 0x0 so we need to filter this out
97
+ // if a user's local time is out of sync with the blockchain, this could potentially
98
+ // be incorrect. the likelihood of that happening though is very low, and devs
99
+ // shouldn't be relying on this value for anything critical anyway.
100
+ return null
91
101
  }
102
+
103
+ const domain = mapDomain(wrappedDomain.domain)
104
+
92
105
  return {
93
- expiryDate: new Date(parseInt(wrappedDomain.expiryDate) * 1000),
106
+ expiryDate,
94
107
  fuses: decodeFuses(wrappedDomain.fuses),
95
108
  ...domain,
96
109
  type: 'wrappedDomain',
@@ -156,6 +169,10 @@ const getNames = async (
156
169
  domains(first: 1000) {
157
170
  ${domainQueryData}
158
171
  createdAt
172
+ registration {
173
+ registrationDate
174
+ expiryDate
175
+ }
159
176
  }
160
177
  wrappedDomains(first: 1000) {
161
178
  expiryDate
@@ -187,6 +204,10 @@ const getNames = async (
187
204
  domains(orderBy: $orderBy, orderDirection: $orderDirection) {
188
205
  ${domainQueryData}
189
206
  createdAt
207
+ registration {
208
+ registrationDate
209
+ expiryDate
210
+ }
190
211
  }
191
212
  }
192
213
  }
@@ -366,7 +387,8 @@ const getNames = async (
366
387
  return [
367
388
  ...(account?.domains.map(mapDomain) || []),
368
389
  ...(account?.registrations.map(mapRegistration) || []),
369
- ...(account?.wrappedDomains.map(mapWrappedDomain) || []),
390
+ ...(account?.wrappedDomains.map(mapWrappedDomain).filter((d: any) => d) ||
391
+ []),
370
392
  ].sort((a, b) => {
371
393
  if (orderDirection === 'desc') {
372
394
  if (orderBy === 'labelName') {
@@ -384,7 +406,9 @@ const getNames = async (
384
406
  return (account?.domains.map(mapDomain) || []) as Name[]
385
407
  }
386
408
  if (type === 'wrappedOwner') {
387
- return (account?.wrappedDomains.map(mapWrappedDomain) || []) as Name[]
409
+ return (account?.wrappedDomains
410
+ .map(mapWrappedDomain)
411
+ .filter((d: any) => d) || []) as Name[]
388
412
  }
389
413
  return (account?.registrations.map(mapRegistration) || []) as Name[]
390
414
  }
@@ -1,5 +1,6 @@
1
1
  import { ENS } from '..'
2
2
  import setup from '../tests/setup'
3
+ import { decodeFuses } from '../utils/fuses'
3
4
 
4
5
  let ensInstance: ENS
5
6
 
@@ -130,6 +131,56 @@ describe('getSubnames', () => {
130
131
  'truncatedName',
131
132
  )
132
133
  })
134
+ describe('wrapped subnames', () => {
135
+ it('should return fuses', async () => {
136
+ const result = await ensInstance.getSubnames({
137
+ name: 'wrapped-with-subnames.eth',
138
+ pageSize: 10,
139
+ orderBy: 'createdAt',
140
+ orderDirection: 'desc',
141
+ })
142
+
143
+ expect(result).toBeTruthy()
144
+ expect(result.subnames.length).toBe(1)
145
+ expect(result.subnameCount).toBe(1)
146
+ expect(result.subnames[0].fuses).toBeDefined()
147
+ })
148
+ it('should return expiry as undefined if 0', async () => {
149
+ const result = await ensInstance.getSubnames({
150
+ name: 'wrapped-with-expiring-subnames.eth',
151
+ pageSize: 10,
152
+ orderBy: 'createdAt',
153
+ orderDirection: 'desc',
154
+ })
155
+
156
+ expect(result).toBeTruthy()
157
+ expect(result.subnames[1].expiryDate).toBeUndefined()
158
+ })
159
+ it('should return expiry', async () => {
160
+ const result = await ensInstance.getSubnames({
161
+ name: 'wrapped-with-expiring-subnames.eth',
162
+ pageSize: 10,
163
+ orderBy: 'createdAt',
164
+ orderDirection: 'desc',
165
+ })
166
+
167
+ expect(result).toBeTruthy()
168
+ expect(result.subnames[2].expiryDate).toBeInstanceOf(Date)
169
+ })
170
+ it('should return owner as undefined, fuses as 0, and pccExpired as true if pcc expired', async () => {
171
+ const result = await ensInstance.getSubnames({
172
+ name: 'wrapped-with-expiring-subnames.eth',
173
+ pageSize: 10,
174
+ orderBy: 'createdAt',
175
+ orderDirection: 'desc',
176
+ })
177
+
178
+ expect(result).toBeTruthy()
179
+ expect(result.subnames[0].owner).toBeUndefined()
180
+ expect(result.subnames[0].fuses).toStrictEqual(decodeFuses(0))
181
+ expect(result.subnames[0].pccExpired).toBe(true)
182
+ })
183
+ })
133
184
 
134
185
  describe('with pagination', () => {
135
186
  it('should get paginated subnames for a name ordered by createdAt in desc order', async () => {
@@ -1,20 +1,36 @@
1
1
  import { ENSArgs } from '..'
2
2
  import { truncateFormat } from '../utils/format'
3
+ import { AllCurrentFuses, checkPCCBurned, decodeFuses } from '../utils/fuses'
3
4
  import { decryptName } from '../utils/labels'
4
5
  import { namehash } from '../utils/normalise'
6
+ import { Domain } from '../utils/subgraph-types'
5
7
 
6
- type Subname = {
8
+ type BaseSubname = {
7
9
  id: string
8
10
  labelName: string | null
9
11
  truncatedName?: string
10
12
  labelhash: string
11
13
  isMigrated: boolean
12
14
  name: string
13
- owner: {
14
- id: string
15
- }
15
+ owner: string | undefined
16
+ }
17
+
18
+ type UnwrappedSubname = BaseSubname & {
19
+ fuses?: never
20
+ expiryDate?: never
21
+ pccExpired?: never
22
+ type: 'domain'
16
23
  }
17
24
 
25
+ type WrappedSubname = BaseSubname & {
26
+ fuses: AllCurrentFuses
27
+ expiryDate: Date
28
+ pccExpired: boolean
29
+ type: 'wrappedDomain'
30
+ }
31
+
32
+ type Subname = WrappedSubname | UnwrappedSubname
33
+
18
34
  type Params = {
19
35
  name: string
20
36
  page?: number
@@ -90,6 +106,13 @@ const largeQuery = async (
90
106
  owner {
91
107
  id
92
108
  }
109
+ wrappedDomain {
110
+ fuses
111
+ expiryDate
112
+ owner {
113
+ id
114
+ }
115
+ }
93
116
  }
94
117
  }
95
118
  }
@@ -106,18 +129,44 @@ const largeQuery = async (
106
129
  }
107
130
  const response = await client.request(finalQuery, queryVars)
108
131
  const domain = response?.domain
109
- const subdomains = domain.subdomains.map((subname: any) => {
110
- const decrypted = decryptName(subname.name)
132
+ const subdomains = domain.subdomains.map(
133
+ ({ wrappedDomain, ...subname }: Domain) => {
134
+ const decrypted = decryptName(subname.name!)
111
135
 
112
- return {
113
- ...subname,
114
- name: decrypted,
115
- truncatedName: truncateFormat(decrypted),
116
- }
117
- })
136
+ const obj = {
137
+ ...subname,
138
+ labelName: subname.labelName || null,
139
+ labelhash: subname.labelhash || '',
140
+ name: decrypted,
141
+ truncatedName: truncateFormat(decrypted),
142
+ owner: subname.owner.id,
143
+ type: 'domain',
144
+ } as Subname
145
+
146
+ if (wrappedDomain) {
147
+ obj.type = 'wrappedDomain'
148
+ const expiryDateAsDate =
149
+ wrappedDomain.expiryDate && wrappedDomain.expiryDate !== '0'
150
+ ? new Date(parseInt(wrappedDomain.expiryDate) * 1000)
151
+ : undefined
152
+ // if a user's local time is out of sync with the blockchain, this could potentially
153
+ // be incorrect. the likelihood of that happening though is very low, and devs
154
+ // shouldn't be relying on this value for anything critical anyway.
155
+ const hasExpired = expiryDateAsDate && expiryDateAsDate < new Date()
156
+ obj.expiryDate = expiryDateAsDate
157
+ obj.fuses = decodeFuses(hasExpired ? 0 : wrappedDomain.fuses)
158
+ obj.pccExpired = hasExpired
159
+ ? checkPCCBurned(wrappedDomain.fuses)
160
+ : false
161
+ obj.owner = obj.pccExpired ? undefined : wrappedDomain.owner.id
162
+ }
163
+
164
+ return obj
165
+ },
166
+ )
118
167
 
119
168
  return {
120
- subnames: subdomains,
169
+ subnames: subdomains as Subname[],
121
170
  subnameCount: domain.subdomainCount,
122
171
  }
123
172
  }
@@ -1,2 +1,2 @@
1
1
  export const EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'
2
- export const MAX_INT_64 = 2n ** 64n - 1n
2
+ export const MAX_INT_64 = BigInt('18446744073709551615')
@@ -352,6 +352,9 @@ export const decodeFuses = (fuses: number) => {
352
352
  }
353
353
  }
354
354
 
355
+ export const checkPCCBurned = (fuses: number) =>
356
+ (fuses & PARENT_CANNOT_CONTROL) === PARENT_CANNOT_CONTROL
357
+
355
358
  export type AllCurrentFuses = ReturnType<typeof decodeFuses>
356
359
 
357
360
  export default fullFuseEnum