@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.
- package/dist/cjs/functions/deleteSubname.js +17 -7
- package/dist/cjs/functions/getNames.js +25 -13
- package/dist/cjs/functions/getSubnames.js +32 -8
- package/dist/cjs/functions/transferSubname.js +4 -9
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/utils/consts.js +1 -1
- package/dist/cjs/utils/fuses.js +2 -0
- package/dist/cjs/utils/registry.js +32 -0
- package/dist/cjs/utils/wrapper.js +14 -9
- package/dist/esm/functions/deleteSubname.mjs +17 -7
- package/dist/esm/functions/getNames.mjs +26 -14
- package/dist/esm/functions/getSubnames.mjs +32 -8
- package/dist/esm/functions/transferSubname.mjs +5 -10
- package/dist/esm/index.mjs +1 -1
- package/dist/esm/utils/consts.mjs +1 -1
- package/dist/esm/utils/fuses.mjs +2 -0
- package/dist/esm/utils/registry.mjs +13 -0
- package/dist/esm/utils/wrapper.mjs +14 -9
- package/dist/types/functions/deleteSubname.d.ts +8 -2
- package/dist/types/functions/getSubnames.d.ts +16 -4
- package/dist/types/functions/transferSubname.d.ts +1 -1
- package/dist/types/index.d.ts +62 -4
- package/dist/types/utils/fuses.d.ts +1 -0
- package/dist/types/utils/registry.d.ts +2 -0
- package/dist/types/utils/wrapper.d.ts +1 -0
- package/package.json +1 -1
- package/src/functions/deleteSubname.test.ts +139 -6
- package/src/functions/deleteSubname.ts +28 -11
- package/src/functions/getNames.test.ts +15 -2
- package/src/functions/getNames.ts +46 -22
- package/src/functions/getSubnames.test.ts +1227 -322
- package/src/functions/getSubnames.ts +62 -13
- package/src/functions/getWrapperData.test.ts +0 -1
- package/src/functions/transferSubname.test.ts +120 -18
- package/src/functions/transferSubname.ts +5 -10
- package/src/index.ts +1 -1
- package/src/utils/consts.ts +1 -1
- package/src/utils/fuses.ts +3 -0
- package/src/utils/registry.ts +14 -0
- 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
|
|
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
|
-
|
|
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(
|
|
110
|
-
|
|
132
|
+
const subdomains = domain.subdomains.map(
|
|
133
|
+
({ wrappedDomain, ...subname }: Domain) => {
|
|
134
|
+
const decrypted = decryptName(subname.name!)
|
|
111
135
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
47
|
-
|
|
81
|
+
})
|
|
82
|
+
expect(tx0).toBeTruthy()
|
|
83
|
+
await tx0.wait()
|
|
84
|
+
})
|
|
48
85
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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 {
|
|
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 =
|
|
46
|
-
|
|
47
|
-
|
|
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'
|
|
628
|
+
>('transferSubname', ['contracts'])
|
|
629
629
|
|
|
630
630
|
public commitName = this.generateWriteFunction<FunctionTypes['commitName']>(
|
|
631
631
|
'commitName',
|
package/src/utils/consts.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export const EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'
|
|
2
|
-
export const MAX_INT_64 =
|
|
2
|
+
export const MAX_INT_64 = BigInt('18446744073709551615')
|
package/src/utils/fuses.ts
CHANGED
|
@@ -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
|
+
}
|
package/src/utils/wrapper.ts
CHANGED
|
@@ -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)
|