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

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 (40) hide show
  1. package/dist/cjs/functions/deleteSubname.js +17 -7
  2. package/dist/cjs/functions/getNames.js +25 -13
  3. package/dist/cjs/functions/getSubnames.js +32 -8
  4. package/dist/cjs/functions/transferSubname.js +4 -9
  5. package/dist/cjs/index.js +1 -1
  6. package/dist/cjs/utils/consts.js +1 -1
  7. package/dist/cjs/utils/fuses.js +2 -0
  8. package/dist/cjs/utils/registry.js +32 -0
  9. package/dist/cjs/utils/wrapper.js +14 -9
  10. package/dist/esm/functions/deleteSubname.mjs +17 -7
  11. package/dist/esm/functions/getNames.mjs +26 -14
  12. package/dist/esm/functions/getSubnames.mjs +32 -8
  13. package/dist/esm/functions/transferSubname.mjs +5 -10
  14. package/dist/esm/index.mjs +1 -1
  15. package/dist/esm/utils/consts.mjs +1 -1
  16. package/dist/esm/utils/fuses.mjs +2 -0
  17. package/dist/esm/utils/registry.mjs +13 -0
  18. package/dist/esm/utils/wrapper.mjs +14 -9
  19. package/dist/types/functions/deleteSubname.d.ts +8 -2
  20. package/dist/types/functions/getSubnames.d.ts +16 -4
  21. package/dist/types/functions/transferSubname.d.ts +1 -1
  22. package/dist/types/index.d.ts +62 -4
  23. package/dist/types/utils/fuses.d.ts +1 -0
  24. package/dist/types/utils/registry.d.ts +2 -0
  25. package/dist/types/utils/wrapper.d.ts +1 -0
  26. package/package.json +1 -1
  27. package/src/functions/deleteSubname.test.ts +139 -6
  28. package/src/functions/deleteSubname.ts +28 -11
  29. package/src/functions/getNames.test.ts +15 -2
  30. package/src/functions/getNames.ts +46 -22
  31. package/src/functions/getSubnames.test.ts +1227 -322
  32. package/src/functions/getSubnames.ts +62 -13
  33. package/src/functions/getWrapperData.test.ts +0 -1
  34. package/src/functions/transferSubname.test.ts +120 -18
  35. package/src/functions/transferSubname.ts +5 -10
  36. package/src/index.ts +1 -1
  37. package/src/utils/consts.ts +1 -1
  38. package/src/utils/fuses.ts +3 -0
  39. package/src/utils/registry.ts +14 -0
  40. package/src/utils/wrapper.ts +12 -9
@@ -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
  }
@@ -59,7 +59,6 @@ describe('getWrapperData', () => {
59
59
  const result = await ensInstance.getWrapperData('wrapped.eth')
60
60
  expect(result).toBeTruthy()
61
61
  if (result) {
62
- console.log(result.expiryDate)
63
62
  expect(result.expiryDate).toBeInstanceOf(Date)
64
63
  expect(Number.isNaN(result.expiryDate?.getTime())).toBe(false)
65
64
  }
@@ -17,10 +17,11 @@ afterAll(async () => {
17
17
  await revert()
18
18
  })
19
19
 
20
+ beforeEach(async () => {
21
+ await revert()
22
+ })
23
+
20
24
  describe('transferSubname', () => {
21
- beforeEach(async () => {
22
- await revert()
23
- })
24
25
  it('should allow transferring a subname on the registry', async () => {
25
26
  const tx = await ensInstance.transferSubname('test.with-subnames.eth', {
26
27
  contract: 'registry',
@@ -34,22 +35,123 @@ describe('transferSubname', () => {
34
35
  const result = await registry.owner(namehash('test.with-subnames.eth'))
35
36
  expect(result).toBe(accounts[1])
36
37
  })
37
- it('should allow transferring a subname on the nameWrapper', async () => {
38
- const tx = await ensInstance.transferSubname(
39
- 'test.wrapped-with-subnames.eth',
40
- {
41
- contract: 'nameWrapper',
42
- owner: accounts[1],
38
+
39
+ describe('wrapped name with PCC NOT burned', () => {
40
+ it('should NOT allow transferring a subname by name owner', async () => {
41
+ const nameWrapper = await ensInstance.contracts!.getNameWrapper()!
42
+ await expect(
43
+ ensInstance.transferSubname('test.wrapped-with-subnames.eth', {
44
+ contract: 'nameWrapper',
45
+ owner: accounts[1],
46
+ addressOrIndex: 2,
47
+ }),
48
+ ).rejects.toThrow()
49
+
50
+ const result = await nameWrapper.ownerOf(
51
+ namehash('test.wrapped-with-subnames.eth'),
52
+ )
53
+ expect(result).toBe(accounts[2])
54
+ })
55
+
56
+ it('should allow transferring a subname by parent owner', async () => {
57
+ const tx = await ensInstance.transferSubname(
58
+ 'test.wrapped-with-subnames.eth',
59
+ {
60
+ contract: 'nameWrapper',
61
+ owner: accounts[1],
62
+ addressOrIndex: 1,
63
+ },
64
+ )
65
+ expect(tx).toBeTruthy()
66
+ await tx.wait()
67
+
68
+ const nameWrapper = await ensInstance.contracts!.getNameWrapper()!
69
+ const result = await nameWrapper.ownerOf(
70
+ namehash('test.wrapped-with-subnames.eth'),
71
+ )
72
+ expect(result).toBe(accounts[1])
73
+ })
74
+ })
75
+
76
+ describe('wrapped name with PCC burned', () => {
77
+ beforeEach(async () => {
78
+ const tx0 = await ensInstance.setFuses('wrapped-with-subnames.eth', {
79
+ named: ['CANNOT_UNWRAP'],
43
80
  addressOrIndex: 1,
44
- },
45
- )
46
- expect(tx).toBeTruthy()
47
- await tx.wait()
81
+ })
82
+ expect(tx0).toBeTruthy()
83
+ await tx0.wait()
84
+ })
48
85
 
49
- const nameWrapper = await ensInstance.contracts!.getNameWrapper()!
50
- const result = await nameWrapper.ownerOf(
51
- namehash('test.wrapped-with-subnames.eth'),
52
- )
53
- expect(result).toBe(accounts[1])
86
+ it('should NOT allow transferring a subname by name owner', async () => {
87
+ const nameWrapper = await ensInstance.contracts!.getNameWrapper()!
88
+
89
+ const tx1 = await ensInstance.setChildFuses(
90
+ 'test.wrapped-with-subnames.eth',
91
+ {
92
+ fuses: {
93
+ parent: {
94
+ named: ['PARENT_CANNOT_CONTROL'],
95
+ },
96
+ },
97
+ addressOrIndex: 1,
98
+ },
99
+ )
100
+ expect(tx1).toBeTruthy()
101
+ await tx1.wait()
102
+
103
+ const checkOwner = await nameWrapper.ownerOf(
104
+ namehash('test.wrapped-with-subnames.eth'),
105
+ )
106
+ expect(checkOwner).toBe(accounts[2])
107
+
108
+ await expect(
109
+ ensInstance.transferSubname('test.wrapped-with-subnames.eth', {
110
+ contract: 'nameWrapper',
111
+ owner: accounts[1],
112
+ addressOrIndex: 2,
113
+ }),
114
+ ).rejects.toThrow()
115
+ const result = await nameWrapper.ownerOf(
116
+ namehash('test.wrapped-with-subnames.eth'),
117
+ )
118
+ expect(result).toBe(accounts[2])
119
+ })
120
+
121
+ it('should NOT allow transferring a subname by parent owner', async () => {
122
+ const nameWrapper = await ensInstance.contracts!.getNameWrapper()!
123
+
124
+ const tx1 = await ensInstance.setChildFuses(
125
+ 'test.wrapped-with-subnames.eth',
126
+ {
127
+ fuses: {
128
+ parent: {
129
+ named: ['PARENT_CANNOT_CONTROL'],
130
+ },
131
+ },
132
+ addressOrIndex: 1,
133
+ },
134
+ )
135
+ expect(tx1).toBeTruthy()
136
+ await tx1.wait()
137
+
138
+ const checkOwner = await nameWrapper.ownerOf(
139
+ namehash('test.wrapped-with-subnames.eth'),
140
+ )
141
+ expect(checkOwner).toBe(accounts[2])
142
+
143
+ await expect(
144
+ ensInstance.transferSubname('test.wrapped-with-subnames.eth', {
145
+ contract: 'nameWrapper',
146
+ owner: accounts[1],
147
+ addressOrIndex: 1,
148
+ }),
149
+ ).rejects.toThrow()
150
+
151
+ const result = await nameWrapper.ownerOf(
152
+ namehash('test.wrapped-with-subnames.eth'),
153
+ )
154
+ expect(result).toBe(accounts[2])
155
+ })
54
156
  })
55
157
  })
@@ -1,7 +1,7 @@
1
1
  import { keccak256 as solidityKeccak256 } from '@ethersproject/solidity'
2
2
  import { ENSArgs } from '..'
3
3
  import { namehash } from '../utils/normalise'
4
- import { Expiry, makeExpiry } from '../utils/wrapper'
4
+ import { expiryToBigNumber, Expiry } from '../utils/wrapper'
5
5
 
6
6
  type BaseArgs = {
7
7
  owner: string
@@ -17,11 +17,7 @@ type NameWrapperArgs = {
17
17
  type Args = BaseArgs | NameWrapperArgs
18
18
 
19
19
  export default async function (
20
- {
21
- contracts,
22
- signer,
23
- getExpiry,
24
- }: ENSArgs<'contracts' | 'signer' | 'getExpiry'>,
20
+ { contracts, signer }: ENSArgs<'contracts' | 'signer'>,
25
21
  name: string,
26
22
  { contract, owner, resolverAddress, ...wrapperArgs }: Args,
27
23
  ) {
@@ -42,10 +38,9 @@ export default async function (
42
38
  }
43
39
  case 'nameWrapper': {
44
40
  const nameWrapper = (await contracts!.getNameWrapper()!).connect(signer)
45
- const expiry = await makeExpiry(
46
- { getExpiry },
47
- labels.join('.'),
48
- 'expiry' in wrapperArgs ? wrapperArgs.expiry : undefined,
41
+ const expiry = expiryToBigNumber(
42
+ (wrapperArgs as NameWrapperArgs).expiry,
43
+ 0,
49
44
  )
50
45
 
51
46
  return nameWrapper.populateTransaction.setSubnodeOwner(
package/src/index.ts CHANGED
@@ -625,7 +625,7 @@ export class ENS {
625
625
 
626
626
  public transferSubname = this.generateWriteFunction<
627
627
  FunctionTypes['transferSubname']
628
- >('transferSubname', ['contracts', 'getExpiry'])
628
+ >('transferSubname', ['contracts'])
629
629
 
630
630
  public commitName = this.generateWriteFunction<FunctionTypes['commitName']>(
631
631
  'commitName',
@@ -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
@@ -0,0 +1,14 @@
1
+ import { ENSArgs } from '../index'
2
+ import { namehash } from './normalise'
3
+
4
+ export const makeResolver = async (
5
+ { contracts }: ENSArgs<'contracts'>,
6
+ name: string,
7
+ resolver?: string,
8
+ ) => {
9
+ if (resolver) return resolver
10
+ const registry = await contracts!.getRegistry()
11
+ const node = namehash(name)
12
+ const _resolver = await registry.resolver(node)
13
+ return _resolver
14
+ }
@@ -6,20 +6,23 @@ export type Expiry = string | number | Date | BigNumber
6
6
 
7
7
  export const MAX_EXPIRY = BigNumber.from(2).pow(64).sub(1)
8
8
 
9
+ export const expiryToBigNumber = (expiry?: Expiry, defaultValue = 0) => {
10
+ if (!expiry) return BigNumber.from(defaultValue)
11
+ if (expiry instanceof Date) {
12
+ return BigNumber.from(expiry.getTime() / 1000)
13
+ }
14
+ if (expiry instanceof BigNumber) {
15
+ return expiry
16
+ }
17
+ return BigNumber.from(expiry)
18
+ }
19
+
9
20
  export const makeExpiry = async (
10
21
  { getExpiry }: ENSArgs<'getExpiry'>,
11
22
  name: string,
12
23
  expiry?: Expiry,
13
24
  ) => {
14
- if (expiry) {
15
- if (expiry instanceof Date) {
16
- return BigNumber.from(expiry.getTime() / 1000)
17
- }
18
- if (expiry instanceof BigNumber) {
19
- return expiry
20
- }
21
- return BigNumber.from(expiry)
22
- }
25
+ if (expiry) return expiryToBigNumber(expiry)
23
26
  if (name.endsWith('.eth')) {
24
27
  const expResponse = await getExpiry(name)
25
28
  if (!expResponse?.expiry)