@ensdomains/ensjs 3.0.0-alpha.40 → 3.0.0-alpha.42
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/contracts/getContractAddress.js +4 -4
- package/dist/cjs/functions/commitName.js +2 -3
- package/dist/cjs/functions/createSubname.js +2 -8
- package/dist/cjs/functions/getHistory.js +14 -7
- package/dist/cjs/functions/getNames.js +10 -6
- package/dist/cjs/functions/getOwner.js +25 -12
- package/dist/cjs/functions/getSpecificRecord.js +83 -0
- package/dist/cjs/functions/getWrapperData.js +3 -4
- package/dist/cjs/functions/renewNames.js +13 -20
- package/dist/cjs/functions/{burnFuses.js → setFuses.js} +24 -6
- package/dist/cjs/functions/wrapName.js +7 -23
- package/dist/cjs/generated/factories/ETHRegistrarController__factory.js +6 -55
- package/dist/cjs/generated/factories/NameWrapper__factory.js +90 -69
- package/dist/cjs/generated/factories/PublicResolver__factory.js +83 -0
- package/dist/cjs/generated/factories/UniversalResolver__factory.js +67 -2
- package/dist/cjs/index.js +15 -4
- package/dist/cjs/utils/consts.js +3 -1
- package/dist/cjs/utils/fuses.js +218 -53
- package/dist/cjs/utils/normalise.js +2 -2
- package/dist/cjs/utils/recordHelpers.js +55 -0
- package/dist/cjs/utils/registerHelpers.js +9 -23
- package/dist/cjs/utils/subgraph-types.js +16 -0
- package/dist/cjs/utils/wrapper.js +2 -2
- package/dist/esm/contracts/getContractAddress.mjs +4 -4
- package/dist/esm/functions/commitName.mjs +2 -3
- package/dist/esm/functions/createSubname.mjs +2 -2
- package/dist/esm/functions/getHistory.mjs +14 -7
- package/dist/esm/functions/getNames.mjs +10 -6
- package/dist/esm/functions/getOwner.mjs +25 -12
- package/dist/esm/functions/getSpecificRecord.mjs +78 -1
- package/dist/esm/functions/getWrapperData.mjs +3 -4
- package/dist/esm/functions/renewNames.mjs +13 -20
- package/dist/esm/functions/setFuses.mjs +30 -0
- package/dist/esm/functions/wrapName.mjs +8 -18
- package/dist/esm/generated/factories/ETHRegistrarController__factory.mjs +6 -55
- package/dist/esm/generated/factories/NameWrapper__factory.mjs +90 -69
- package/dist/esm/generated/factories/PublicResolver__factory.mjs +83 -0
- package/dist/esm/generated/factories/UniversalResolver__factory.mjs +67 -2
- package/dist/esm/index.mjs +15 -4
- package/dist/esm/utils/consts.mjs +3 -1
- package/dist/esm/utils/fuses.mjs +218 -53
- package/dist/esm/utils/normalise.mjs +1 -1
- package/dist/esm/utils/recordHelpers.mjs +49 -0
- package/dist/esm/utils/registerHelpers.mjs +9 -17
- package/dist/esm/utils/subgraph-types.mjs +0 -0
- package/dist/esm/utils/wrapper.mjs +1 -1
- package/dist/types/functions/commitName.d.ts +5 -6
- package/dist/types/functions/createSubname.d.ts +3 -3
- package/dist/types/functions/deleteSubname.d.ts +1 -1
- package/dist/types/functions/getHistory.d.ts +1 -1
- package/dist/types/functions/getNames.d.ts +3 -3
- package/dist/types/functions/getOwner.d.ts +1 -0
- package/dist/types/functions/getSpecificRecord.d.ts +22 -0
- package/dist/types/functions/getWrapperData.d.ts +44 -5
- package/dist/types/functions/importDNSSECName.d.ts +1 -1
- package/dist/types/functions/registerName.d.ts +1 -1
- package/dist/types/functions/renewNames.d.ts +5 -6
- package/dist/types/functions/setFuses.d.ts +7 -0
- package/dist/types/functions/setName.d.ts +1 -1
- package/dist/types/functions/setRecord.d.ts +5 -1
- package/dist/types/functions/setRecords.d.ts +1 -1
- package/dist/types/functions/setResolver.d.ts +1 -1
- package/dist/types/functions/transferController.d.ts +1 -1
- package/dist/types/functions/transferName.d.ts +1 -1
- package/dist/types/functions/transferSubname.d.ts +1 -1
- package/dist/types/functions/types.d.ts +9 -5
- package/dist/types/functions/unwrapName.d.ts +1 -1
- package/dist/types/functions/wrapName.d.ts +4 -4
- package/dist/types/generated/ETHRegistrarController.d.ts +13 -36
- package/dist/types/generated/NameWrapper.d.ts +97 -52
- package/dist/types/generated/PublicResolver.d.ts +50 -1
- package/dist/types/generated/UniversalResolver.d.ts +79 -32
- package/dist/types/generated/factories/UniversalResolver__factory.d.ts +15 -0
- package/dist/types/index.d.ts +75 -11
- package/dist/types/utils/consts.d.ts +1 -0
- package/dist/types/utils/fuses.d.ts +115 -45
- package/dist/types/utils/recordHelpers.d.ts +14 -2
- package/dist/types/utils/registerHelpers.d.ts +7 -25
- package/dist/types/utils/subgraph-types.d.ts +284 -0
- package/dist/types/utils/writeTx.d.ts +5 -5
- package/package.json +8 -5
- package/src/@types/pako.d.ts +188 -0
- package/src/contracts/bulkRenewal.ts +1 -1
- package/src/contracts/getContractAddress.ts +4 -4
- package/src/functions/commitName.test.ts +0 -3
- package/src/functions/commitName.ts +1 -2
- package/src/functions/createSubname.ts +4 -5
- package/src/functions/getHistory.ts +59 -25
- package/src/functions/getNames.test.ts +1 -1
- package/src/functions/getNames.ts +26 -13
- package/src/functions/getOwner.test.ts +62 -3
- package/src/functions/getOwner.ts +31 -11
- package/src/functions/getSpecificRecord.test.ts +162 -0
- package/src/functions/getSpecificRecord.ts +95 -1
- package/src/functions/getWrapperData.test.ts +11 -45
- package/src/functions/getWrapperData.ts +7 -5
- package/src/functions/registerName.test.ts +3 -3
- package/src/functions/renewNames.ts +17 -23
- package/src/functions/setFuses.test.ts +240 -0
- package/src/functions/setFuses.ts +44 -0
- package/src/functions/setRecord.test.ts +147 -1
- package/src/functions/setRecord.ts +6 -1
- package/src/functions/types.ts +10 -4
- package/src/functions/wrapName.test.ts +8 -7
- package/src/functions/wrapName.ts +10 -24
- package/src/generated/ETHRegistrarController.ts +12 -79
- package/src/generated/NameWrapper.ts +155 -99
- package/src/generated/PublicResolver.ts +123 -0
- package/src/generated/UniversalResolver.ts +157 -16
- package/src/generated/factories/ETHRegistrarController__factory.ts +6 -55
- package/src/generated/factories/NameWrapper__factory.ts +90 -69
- package/src/generated/factories/PublicResolver__factory.ts +83 -0
- package/src/generated/factories/UniversalResolver__factory.ts +67 -2
- package/src/index.ts +23 -14
- package/src/utils/consts.ts +1 -0
- package/src/utils/fuses.ts +299 -102
- package/src/utils/normalise.ts +1 -1
- package/src/utils/recordHelpers.ts +64 -1
- package/src/utils/registerHelpers.ts +13 -41
- package/src/utils/subgraph-types.ts +312 -0
- package/src/utils/wrapper.ts +1 -1
- package/dist/cjs/utils/generateFuseInput.js +0 -40
- package/dist/esm/functions/burnFuses.mjs +0 -12
- package/dist/esm/utils/generateFuseInput.mjs +0 -15
- package/dist/types/functions/burnFuses.d.ts +0 -3
- package/dist/types/utils/generateFuseInput.d.ts +0 -3
- package/src/functions/burnFuses.test.ts +0 -148
- package/src/functions/burnFuses.ts +0 -16
- package/src/utils/generateFuseInput.ts +0 -13
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { formatsByCoinType, formatsByName } from '@ensdomains/address-encoder'
|
|
2
|
-
import {
|
|
2
|
+
import { BigNumber } from '@ethersproject/bignumber'
|
|
3
|
+
import { arrayify, hexStripZeros, isBytesLike } from '@ethersproject/bytes'
|
|
4
|
+
import { toUtf8String } from '@ethersproject/strings'
|
|
3
5
|
import { ENSArgs } from '..'
|
|
4
6
|
import { decodeContenthash } from '../utils/contentHash'
|
|
5
7
|
import { namehash } from '../utils/normalise'
|
|
@@ -234,3 +236,95 @@ export const getAddr = {
|
|
|
234
236
|
return _getAddr.decode({ contracts }, urData.data, _name, coinType)
|
|
235
237
|
},
|
|
236
238
|
}
|
|
239
|
+
|
|
240
|
+
// Supported content types as bitwise OR
|
|
241
|
+
// ID 1: JSON
|
|
242
|
+
// ID 2: zlib compressed JSON
|
|
243
|
+
// ID 4: CBOR
|
|
244
|
+
// ID 8: URI
|
|
245
|
+
const supportedContentTypes = '0xf'
|
|
246
|
+
|
|
247
|
+
export const _getABI = {
|
|
248
|
+
raw: async ({ contracts }: ENSArgs<'contracts'>, name: string) => {
|
|
249
|
+
const publicResolver = await contracts?.getPublicResolver()!
|
|
250
|
+
return {
|
|
251
|
+
to: '0x0000000000000000000000000000000000000000',
|
|
252
|
+
data: publicResolver.interface.encodeFunctionData('ABI', [
|
|
253
|
+
namehash(name),
|
|
254
|
+
supportedContentTypes,
|
|
255
|
+
]),
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
decode: async ({ contracts }: ENSArgs<'contracts'>, data: string) => {
|
|
259
|
+
const publicResolver = await contracts?.getPublicResolver()!
|
|
260
|
+
const [bnContentType, encodedABIData] =
|
|
261
|
+
publicResolver.interface.decodeFunctionResult('ABI', data)
|
|
262
|
+
if (!bnContentType || !data) {
|
|
263
|
+
return
|
|
264
|
+
}
|
|
265
|
+
const contentType = (bnContentType as BigNumber).toNumber()
|
|
266
|
+
if (!contentType) {
|
|
267
|
+
return
|
|
268
|
+
}
|
|
269
|
+
let abiData: string | object
|
|
270
|
+
let decoded = false
|
|
271
|
+
switch (contentType) {
|
|
272
|
+
// JSON
|
|
273
|
+
case 1:
|
|
274
|
+
abiData = JSON.parse(toUtf8String(encodedABIData))
|
|
275
|
+
decoded = true
|
|
276
|
+
break
|
|
277
|
+
// zlib compressed JSON
|
|
278
|
+
case 2: {
|
|
279
|
+
const { inflate } = await import('pako/dist/pako_inflate.min.js')
|
|
280
|
+
abiData = JSON.parse(
|
|
281
|
+
inflate(arrayify(encodedABIData), { to: 'string' }),
|
|
282
|
+
)
|
|
283
|
+
decoded = true
|
|
284
|
+
break
|
|
285
|
+
}
|
|
286
|
+
// CBOR
|
|
287
|
+
case 4: {
|
|
288
|
+
const { decodeFirst } = await import('cbor')
|
|
289
|
+
abiData = await decodeFirst(arrayify(encodedABIData))
|
|
290
|
+
decoded = true
|
|
291
|
+
break
|
|
292
|
+
}
|
|
293
|
+
// URI
|
|
294
|
+
case 8:
|
|
295
|
+
abiData = toUtf8String(encodedABIData)
|
|
296
|
+
decoded = false
|
|
297
|
+
break
|
|
298
|
+
default:
|
|
299
|
+
try {
|
|
300
|
+
abiData = toUtf8String(encodedABIData)
|
|
301
|
+
} catch {
|
|
302
|
+
abiData = encodedABIData
|
|
303
|
+
}
|
|
304
|
+
decoded = false
|
|
305
|
+
}
|
|
306
|
+
return {
|
|
307
|
+
contentType,
|
|
308
|
+
decoded,
|
|
309
|
+
abi: abiData,
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export const getABI = {
|
|
315
|
+
raw: async (
|
|
316
|
+
{ contracts, universalWrapper }: ENSArgs<'contracts' | 'universalWrapper'>,
|
|
317
|
+
name: string,
|
|
318
|
+
) => {
|
|
319
|
+
const prData = await _getABI.raw({ contracts }, name)
|
|
320
|
+
return universalWrapper.raw(name, prData.data)
|
|
321
|
+
},
|
|
322
|
+
decode: async (
|
|
323
|
+
{ contracts, universalWrapper }: ENSArgs<'contracts' | 'universalWrapper'>,
|
|
324
|
+
data: string,
|
|
325
|
+
) => {
|
|
326
|
+
const urData = await universalWrapper.decode(data)
|
|
327
|
+
if (!urData) return
|
|
328
|
+
return _getABI.decode({ contracts }, urData.data)
|
|
329
|
+
},
|
|
330
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { BigNumber } from 'ethers'
|
|
2
1
|
import { ENS } from '../index'
|
|
3
2
|
import setup from '../tests/setup'
|
|
4
3
|
|
|
@@ -7,22 +6,6 @@ let revert: Awaited<ReturnType<typeof setup>>['revert']
|
|
|
7
6
|
let createSnapshot: Awaited<ReturnType<typeof setup>>['createSnapshot']
|
|
8
7
|
let withWrappedSnapshot: any
|
|
9
8
|
|
|
10
|
-
const unwrappedNameDefault = {
|
|
11
|
-
expiryDate: new Date(0).toString(),
|
|
12
|
-
fuseObj: {
|
|
13
|
-
CANNOT_BURN_FUSES: false,
|
|
14
|
-
CANNOT_CREATE_SUBDOMAIN: false,
|
|
15
|
-
CANNOT_SET_RESOLVER: false,
|
|
16
|
-
CANNOT_SET_TTL: false,
|
|
17
|
-
CANNOT_TRANSFER: false,
|
|
18
|
-
CANNOT_UNWRAP: false,
|
|
19
|
-
PARENT_CANNOT_CONTROL: false,
|
|
20
|
-
CAN_DO_EVERYTHING: true,
|
|
21
|
-
},
|
|
22
|
-
owner: '0x0000000000000000000000000000000000000000',
|
|
23
|
-
rawFuses: BigNumber.from(0),
|
|
24
|
-
}
|
|
25
|
-
|
|
26
9
|
beforeAll(async () => {
|
|
27
10
|
;({ ensInstance, revert, createSnapshot } = await setup())
|
|
28
11
|
|
|
@@ -41,9 +24,9 @@ afterAll(async () => {
|
|
|
41
24
|
describe('getWrapperData', () => {
|
|
42
25
|
it('should return default data for an unwrapped name', async () => {
|
|
43
26
|
const result = await ensInstance.getWrapperData('with-profile.eth')
|
|
44
|
-
expect(
|
|
45
|
-
|
|
46
|
-
)
|
|
27
|
+
expect(result?.expiryDate).toBeUndefined()
|
|
28
|
+
expect(result?.rawFuses).toEqual(0)
|
|
29
|
+
expect(result?.child.CAN_DO_EVERYTHING).toBeTruthy()
|
|
47
30
|
})
|
|
48
31
|
it('should return with CAN_DO_EVERYTHING set to true for a name with no fuses burned', async () => {
|
|
49
32
|
const result = await ensInstance.getWrapperData(
|
|
@@ -51,23 +34,13 @@ describe('getWrapperData', () => {
|
|
|
51
34
|
)
|
|
52
35
|
expect(result).toBeTruthy()
|
|
53
36
|
if (result) {
|
|
54
|
-
expect(result.
|
|
55
|
-
expect(
|
|
56
|
-
Object.values(result.fuseObj).reduce(
|
|
57
|
-
(prev, curr) => (curr ? prev + 1 : prev),
|
|
58
|
-
0,
|
|
59
|
-
),
|
|
60
|
-
).toBe(1)
|
|
61
|
-
expect(result.rawFuses.toHexString()).toBe('0x00')
|
|
37
|
+
expect(result.child.CAN_DO_EVERYTHING).toBe(true)
|
|
38
|
+
expect(result.rawFuses).toBe(0)
|
|
62
39
|
}
|
|
63
40
|
})
|
|
64
41
|
it('should return with other correct fuses', async () => {
|
|
65
|
-
const tx = await ensInstance.
|
|
66
|
-
|
|
67
|
-
'CANNOT_UNWRAP',
|
|
68
|
-
'CANNOT_CREATE_SUBDOMAIN',
|
|
69
|
-
'CANNOT_SET_TTL',
|
|
70
|
-
],
|
|
42
|
+
const tx = await ensInstance.setFuses('wrapped.eth', {
|
|
43
|
+
named: ['CANNOT_UNWRAP', 'CANNOT_CREATE_SUBDOMAIN', 'CANNOT_SET_TTL'],
|
|
71
44
|
addressOrIndex: 1,
|
|
72
45
|
})
|
|
73
46
|
await tx.wait()
|
|
@@ -75,17 +48,10 @@ describe('getWrapperData', () => {
|
|
|
75
48
|
const result = await ensInstance.getWrapperData('wrapped.eth')
|
|
76
49
|
expect(result).toBeTruthy()
|
|
77
50
|
if (result) {
|
|
78
|
-
expect(result.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
CANNOT_SET_RESOLVER: false,
|
|
83
|
-
CANNOT_SET_TTL: true,
|
|
84
|
-
CANNOT_CREATE_SUBDOMAIN: true,
|
|
85
|
-
PARENT_CANNOT_CONTROL: true,
|
|
86
|
-
CAN_DO_EVERYTHING: false,
|
|
87
|
-
})
|
|
88
|
-
expect(result.rawFuses.toHexString()).toBe('0x71')
|
|
51
|
+
expect(result.child.CAN_DO_EVERYTHING).toBe(false)
|
|
52
|
+
expect(result.child.CANNOT_UNWRAP).toBe(true)
|
|
53
|
+
expect(result.child.CANNOT_CREATE_SUBDOMAIN).toBe(true)
|
|
54
|
+
expect(result.child.CANNOT_SET_TTL).toBe(true)
|
|
89
55
|
}
|
|
90
56
|
})
|
|
91
57
|
it('should return correct expiry', async () => {
|
|
@@ -14,18 +14,20 @@ const raw = async ({ contracts }: ENSArgs<'contracts'>, name: string) => {
|
|
|
14
14
|
const decode = async ({ contracts }: ENSArgs<'contracts'>, data: string) => {
|
|
15
15
|
const nameWrapper = await contracts?.getNameWrapper()!
|
|
16
16
|
try {
|
|
17
|
-
const [owner,
|
|
17
|
+
const [owner, fuses, expiry] = nameWrapper.interface.decodeFunctionResult(
|
|
18
18
|
'getData',
|
|
19
19
|
data,
|
|
20
|
-
)
|
|
20
|
+
) as [string, number, BigNumber]
|
|
21
21
|
|
|
22
|
-
const fuses = BigNumber.from(_fuses)
|
|
23
22
|
const fuseObj = decodeFuses(fuses)
|
|
24
23
|
|
|
25
|
-
const expiryDate =
|
|
24
|
+
const expiryDate =
|
|
25
|
+
expiry.gt(0) && expiry.lt(BigNumber.from(2).pow(32))
|
|
26
|
+
? new Date(expiry.mul(1000).toString())
|
|
27
|
+
: undefined
|
|
26
28
|
|
|
27
29
|
return {
|
|
28
|
-
fuseObj,
|
|
30
|
+
...fuseObj,
|
|
29
31
|
expiryDate,
|
|
30
32
|
rawFuses: fuses,
|
|
31
33
|
owner,
|
|
@@ -23,6 +23,8 @@ describe('registerName', () => {
|
|
|
23
23
|
await revert()
|
|
24
24
|
})
|
|
25
25
|
it('should return a registration transaction and succeed', async () => {
|
|
26
|
+
const controller = await ensInstance.contracts!.getEthRegistrarController()!
|
|
27
|
+
|
|
26
28
|
const name = 'cool-swag.eth'
|
|
27
29
|
const duration = 31536000
|
|
28
30
|
const { customData, ...commitPopTx } =
|
|
@@ -37,14 +39,12 @@ describe('registerName', () => {
|
|
|
37
39
|
await provider.send('evm_increaseTime', [60])
|
|
38
40
|
await provider.send('evm_mine', [])
|
|
39
41
|
|
|
40
|
-
const { secret
|
|
42
|
+
const { secret } = customData!
|
|
41
43
|
|
|
42
|
-
const controller = await ensInstance.contracts!.getEthRegistrarController()!
|
|
43
44
|
const [price] = await controller.rentPrice(name, duration)
|
|
44
45
|
|
|
45
46
|
const tx = await ensInstance.registerName(name, {
|
|
46
47
|
secret,
|
|
47
|
-
wrapperExpiry,
|
|
48
48
|
duration,
|
|
49
49
|
owner: accounts[1],
|
|
50
50
|
addressOrIndex: accounts[1],
|
|
@@ -1,39 +1,33 @@
|
|
|
1
1
|
import { BigNumber, BigNumberish } from '@ethersproject/bignumber/lib/bignumber'
|
|
2
2
|
import { ENSArgs } from '..'
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { MAX_INT_64 } from '../utils/consts'
|
|
4
|
+
import { labelhash } from '../utils/labels'
|
|
5
|
+
import { namehash } from '../utils/normalise'
|
|
5
6
|
|
|
6
7
|
type BaseProps = {
|
|
7
8
|
duration: number
|
|
8
9
|
value: BigNumber
|
|
9
10
|
}
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
type WrappedProps = {
|
|
13
|
+
duration: BigNumberish
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function extendWrappedName(
|
|
12
17
|
{ contracts }: ENSArgs<'contracts'>,
|
|
13
18
|
name: string,
|
|
14
|
-
|
|
15
|
-
duration,
|
|
16
|
-
value,
|
|
17
|
-
fuses,
|
|
18
|
-
wrapperExpiry = MAX_INT_64,
|
|
19
|
-
}: BaseProps & {
|
|
20
|
-
fuses?: FuseProps
|
|
21
|
-
wrapperExpiry?: BigNumberish
|
|
22
|
-
},
|
|
19
|
+
options?: WrappedProps,
|
|
23
20
|
) {
|
|
21
|
+
const expiry = options?.duration || MAX_INT_64
|
|
24
22
|
const labels = name.split('.')
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
23
|
+
const labelHash = labelhash(labels.shift()!)
|
|
24
|
+
const parentNode = namehash(labels.join('.'))
|
|
28
25
|
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
encodedFuses,
|
|
35
|
-
wrapperExpiry,
|
|
36
|
-
{ value },
|
|
26
|
+
const nameWrapper = await contracts!.getNameWrapper()
|
|
27
|
+
return nameWrapper.populateTransaction.extendExpiry(
|
|
28
|
+
parentNode,
|
|
29
|
+
labelHash,
|
|
30
|
+
expiry,
|
|
37
31
|
)
|
|
38
32
|
}
|
|
39
33
|
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { ethers } from 'ethers'
|
|
2
|
+
import { ENS } from '../index'
|
|
3
|
+
import setup from '../tests/setup'
|
|
4
|
+
import { unnamedUserSettableFuses, userSettableFuseEnum } from '../utils/fuses'
|
|
5
|
+
import { namehash } from '../utils/normalise'
|
|
6
|
+
|
|
7
|
+
let ensInstance: ENS
|
|
8
|
+
let revert: Awaited<ReturnType<typeof setup>>['revert']
|
|
9
|
+
let provider: ethers.providers.JsonRpcProvider
|
|
10
|
+
let accounts: string[]
|
|
11
|
+
|
|
12
|
+
beforeAll(async () => {
|
|
13
|
+
;({ ensInstance, revert, provider } = await setup())
|
|
14
|
+
accounts = await provider.listAccounts()
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
afterAll(async () => {
|
|
18
|
+
await revert()
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
const checkFuses = (
|
|
22
|
+
fuses: number,
|
|
23
|
+
expected: (keyof typeof userSettableFuseEnum)[],
|
|
24
|
+
) => {
|
|
25
|
+
// eslint-disable-next-line guard-for-in
|
|
26
|
+
for (const fuse in userSettableFuseEnum) {
|
|
27
|
+
const active =
|
|
28
|
+
(fuses &
|
|
29
|
+
userSettableFuseEnum[fuse as keyof typeof userSettableFuseEnum]) >
|
|
30
|
+
0
|
|
31
|
+
if (expected.includes(fuse as keyof typeof userSettableFuseEnum)) {
|
|
32
|
+
try {
|
|
33
|
+
expect(active).toBeTruthy()
|
|
34
|
+
} catch {
|
|
35
|
+
throw new Error(`${fuse} should be true but is false`)
|
|
36
|
+
}
|
|
37
|
+
} else if (active) {
|
|
38
|
+
try {
|
|
39
|
+
expect(active).toBeFalsy()
|
|
40
|
+
} catch {
|
|
41
|
+
throw new Error(`${fuse} should be false but is true`)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const checkUnnamedFuses = (
|
|
48
|
+
fuses: number,
|
|
49
|
+
expected: (keyof typeof unnamedUserSettableFuses)[],
|
|
50
|
+
) => {
|
|
51
|
+
// eslint-disable-next-line guard-for-in
|
|
52
|
+
for (const fuse of unnamedUserSettableFuses) {
|
|
53
|
+
const active = (fuses & fuse) > 0
|
|
54
|
+
if (expected.includes(fuse as keyof typeof unnamedUserSettableFuses)) {
|
|
55
|
+
try {
|
|
56
|
+
expect(active).toBeTruthy()
|
|
57
|
+
} catch {
|
|
58
|
+
throw new Error(`${fuse} should be true but is false`)
|
|
59
|
+
}
|
|
60
|
+
} else if (active) {
|
|
61
|
+
try {
|
|
62
|
+
expect(active).toBeFalsy()
|
|
63
|
+
} catch {
|
|
64
|
+
throw new Error(`${fuse} should be false but is true`)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
describe('setFuses', () => {
|
|
71
|
+
beforeEach(async () => {
|
|
72
|
+
await revert()
|
|
73
|
+
})
|
|
74
|
+
describe('Array', () => {
|
|
75
|
+
it('should return a setFuses transaction from a named fuse array and succeed', async () => {
|
|
76
|
+
const tx = await ensInstance.setFuses('wrapped.eth', {
|
|
77
|
+
named: ['CANNOT_UNWRAP', 'CANNOT_CREATE_SUBDOMAIN', 'CANNOT_SET_TTL'],
|
|
78
|
+
addressOrIndex: accounts[1],
|
|
79
|
+
})
|
|
80
|
+
expect(tx).toBeTruthy()
|
|
81
|
+
await tx.wait()
|
|
82
|
+
|
|
83
|
+
const nameWrapper = await ensInstance.contracts!.getNameWrapper()!
|
|
84
|
+
const [, fuses] = await nameWrapper.getData(namehash('wrapped.eth'))
|
|
85
|
+
checkFuses(fuses, [
|
|
86
|
+
'CANNOT_UNWRAP',
|
|
87
|
+
'CANNOT_CREATE_SUBDOMAIN',
|
|
88
|
+
'CANNOT_SET_TTL',
|
|
89
|
+
'PARENT_CANNOT_CONTROL',
|
|
90
|
+
])
|
|
91
|
+
})
|
|
92
|
+
it('should return a setFuses transaction from an unnamed fuse array and succeed', async () => {
|
|
93
|
+
const tx0 = await ensInstance.setFuses('wrapped.eth', {
|
|
94
|
+
named: ['CANNOT_UNWRAP'],
|
|
95
|
+
addressOrIndex: accounts[1],
|
|
96
|
+
})
|
|
97
|
+
expect(tx0).toBeTruthy()
|
|
98
|
+
await tx0.wait()
|
|
99
|
+
|
|
100
|
+
const tx = await ensInstance.setFuses('wrapped.eth', {
|
|
101
|
+
unnamed: [128, 256, 512],
|
|
102
|
+
addressOrIndex: accounts[1],
|
|
103
|
+
})
|
|
104
|
+
expect(tx).toBeTruthy()
|
|
105
|
+
await tx.wait()
|
|
106
|
+
|
|
107
|
+
const nameWrapper = await ensInstance.contracts!.getNameWrapper()!
|
|
108
|
+
const [, fuses] = await nameWrapper.getData(namehash('wrapped.eth'))
|
|
109
|
+
checkUnnamedFuses(fuses, [128, 256, 512])
|
|
110
|
+
})
|
|
111
|
+
it('should return a setFuses transaction from both an unnamed and named fuse array and succeed', async () => {
|
|
112
|
+
const tx = await ensInstance.setFuses('wrapped.eth', {
|
|
113
|
+
named: ['CANNOT_UNWRAP', 'CANNOT_CREATE_SUBDOMAIN', 'CANNOT_SET_TTL'],
|
|
114
|
+
unnamed: [128, 256, 512],
|
|
115
|
+
addressOrIndex: accounts[1],
|
|
116
|
+
})
|
|
117
|
+
expect(tx).toBeTruthy()
|
|
118
|
+
await tx.wait()
|
|
119
|
+
|
|
120
|
+
const nameWrapper = await ensInstance.contracts!.getNameWrapper()!
|
|
121
|
+
const [, fuses] = await nameWrapper.getData(namehash('wrapped.eth'))
|
|
122
|
+
checkFuses(fuses, [
|
|
123
|
+
'CANNOT_UNWRAP',
|
|
124
|
+
'CANNOT_CREATE_SUBDOMAIN',
|
|
125
|
+
'CANNOT_SET_TTL',
|
|
126
|
+
'PARENT_CANNOT_CONTROL',
|
|
127
|
+
])
|
|
128
|
+
checkUnnamedFuses(fuses, [128, 256, 512])
|
|
129
|
+
})
|
|
130
|
+
it('should throw an error when trying to burn a named fuse in an unnamed fuse array', async () => {
|
|
131
|
+
try {
|
|
132
|
+
await ensInstance.setFuses('wrapped.eth', {
|
|
133
|
+
unnamed: [32] as any,
|
|
134
|
+
addressOrIndex: accounts[1],
|
|
135
|
+
})
|
|
136
|
+
expect(false).toBeTruthy()
|
|
137
|
+
} catch (e: any) {
|
|
138
|
+
expect(e.message).toBe(
|
|
139
|
+
'32 is not a valid unnamed fuse. If you are trying to set a named fuse, use the named property.',
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
})
|
|
143
|
+
it('should throw an error when trying to burn an unnamed fuse in a named fuse array', async () => {
|
|
144
|
+
try {
|
|
145
|
+
await ensInstance.setFuses('wrapped.eth', {
|
|
146
|
+
named: ['COOL_SWAG_FUSE'] as any,
|
|
147
|
+
})
|
|
148
|
+
expect(false).toBeTruthy()
|
|
149
|
+
} catch (e: any) {
|
|
150
|
+
expect(e.message).toBe('COOL_SWAG_FUSE is not a valid named fuse.')
|
|
151
|
+
}
|
|
152
|
+
})
|
|
153
|
+
})
|
|
154
|
+
describe('Number', () => {
|
|
155
|
+
it('should return a setFuses transaction from a number and succeed', async () => {
|
|
156
|
+
const tx = await ensInstance.setFuses('wrapped.eth', {
|
|
157
|
+
number: 49,
|
|
158
|
+
addressOrIndex: accounts[1],
|
|
159
|
+
})
|
|
160
|
+
expect(tx).toBeTruthy()
|
|
161
|
+
await tx.wait()
|
|
162
|
+
|
|
163
|
+
const nameWrapper = await ensInstance.contracts!.getNameWrapper()!
|
|
164
|
+
const [, fuses] = await nameWrapper.getData(namehash('wrapped.eth'))
|
|
165
|
+
checkFuses(fuses, [
|
|
166
|
+
'CANNOT_UNWRAP',
|
|
167
|
+
'CANNOT_CREATE_SUBDOMAIN',
|
|
168
|
+
'CANNOT_SET_TTL',
|
|
169
|
+
'PARENT_CANNOT_CONTROL',
|
|
170
|
+
])
|
|
171
|
+
})
|
|
172
|
+
it('should throw an error if the number is too high', async () => {
|
|
173
|
+
try {
|
|
174
|
+
await ensInstance.setFuses('wrapped.eth', {
|
|
175
|
+
number: 4294967297,
|
|
176
|
+
addressOrIndex: accounts[1],
|
|
177
|
+
})
|
|
178
|
+
expect(false).toBeTruthy()
|
|
179
|
+
} catch (e: any) {
|
|
180
|
+
expect(e.message).toBe(
|
|
181
|
+
'Fuse number must be limited to uint32, 4294967297 was too high.',
|
|
182
|
+
)
|
|
183
|
+
}
|
|
184
|
+
})
|
|
185
|
+
it('should throw an error if the number is too low', async () => {
|
|
186
|
+
try {
|
|
187
|
+
await ensInstance.setFuses('wrapped.eth', {
|
|
188
|
+
number: -1,
|
|
189
|
+
addressOrIndex: accounts[1],
|
|
190
|
+
})
|
|
191
|
+
expect(false).toBeTruthy()
|
|
192
|
+
} catch (e: any) {
|
|
193
|
+
expect(e.message).toBe(
|
|
194
|
+
'Fuse number must be limited to uint32, -1 was too low.',
|
|
195
|
+
)
|
|
196
|
+
}
|
|
197
|
+
})
|
|
198
|
+
it('should throw an error if the number is not an integer', async () => {
|
|
199
|
+
try {
|
|
200
|
+
await ensInstance.setFuses('wrapped.eth', {
|
|
201
|
+
number: 7.5,
|
|
202
|
+
})
|
|
203
|
+
expect(false).toBeTruthy()
|
|
204
|
+
} catch (e: any) {
|
|
205
|
+
expect(e.message).toBe('Fuse number must be an integer, 7.5 was not.')
|
|
206
|
+
}
|
|
207
|
+
})
|
|
208
|
+
})
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
describe('setChildFuses', () => {
|
|
212
|
+
it('should return a setChildFuses transaction and succeed', async () => {
|
|
213
|
+
const nameWrapper = await ensInstance.contracts!.getNameWrapper()!
|
|
214
|
+
const setParentTx = await ensInstance.setFuses(
|
|
215
|
+
'wrapped-with-subnames.eth',
|
|
216
|
+
{
|
|
217
|
+
named: ['CANNOT_UNWRAP'],
|
|
218
|
+
addressOrIndex: accounts[1],
|
|
219
|
+
},
|
|
220
|
+
)
|
|
221
|
+
expect(setParentTx).toBeTruthy()
|
|
222
|
+
await setParentTx.wait()
|
|
223
|
+
|
|
224
|
+
const tx = await ensInstance.setChildFuses(
|
|
225
|
+
'test.wrapped-with-subnames.eth',
|
|
226
|
+
{
|
|
227
|
+
fuses: 65537,
|
|
228
|
+
addressOrIndex: accounts[1],
|
|
229
|
+
},
|
|
230
|
+
)
|
|
231
|
+
expect(tx).toBeTruthy()
|
|
232
|
+
await tx.wait()
|
|
233
|
+
|
|
234
|
+
const [, fuses] = await nameWrapper.getData(
|
|
235
|
+
namehash('test.wrapped-with-subnames.eth'),
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
checkFuses(fuses, ['CANNOT_UNWRAP', 'PARENT_CANNOT_CONTROL'])
|
|
239
|
+
})
|
|
240
|
+
})
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { ENSArgs } from '../index'
|
|
2
|
+
import { CombinedFuseInput, encodeFuses } from '../utils/fuses'
|
|
3
|
+
import { labelhash } from '../utils/labels'
|
|
4
|
+
import { namehash } from '../utils/normalise'
|
|
5
|
+
|
|
6
|
+
export async function setChildFuses(
|
|
7
|
+
{ contracts, signer }: ENSArgs<'contracts' | 'signer'>,
|
|
8
|
+
name: string,
|
|
9
|
+
{
|
|
10
|
+
fuses,
|
|
11
|
+
expiry = 0,
|
|
12
|
+
}: {
|
|
13
|
+
fuses: Partial<CombinedFuseInput> | number
|
|
14
|
+
expiry?: number
|
|
15
|
+
},
|
|
16
|
+
) {
|
|
17
|
+
const encodedFuses = encodeFuses(fuses)
|
|
18
|
+
|
|
19
|
+
const labels = name.split('.')
|
|
20
|
+
const labelHash = labelhash(labels.shift()!)
|
|
21
|
+
const parentNode = namehash(labels.join('.'))
|
|
22
|
+
|
|
23
|
+
const nameWrapper = (await contracts!.getNameWrapper()!).connect(signer)
|
|
24
|
+
|
|
25
|
+
return nameWrapper.populateTransaction.setChildFuses(
|
|
26
|
+
parentNode,
|
|
27
|
+
labelHash,
|
|
28
|
+
encodedFuses,
|
|
29
|
+
expiry,
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export default async function (
|
|
34
|
+
{ contracts, signer }: ENSArgs<'contracts' | 'signer'>,
|
|
35
|
+
name: string,
|
|
36
|
+
props: CombinedFuseInput['child'],
|
|
37
|
+
) {
|
|
38
|
+
const encodedFuses = encodeFuses(props, 'child')
|
|
39
|
+
|
|
40
|
+
const nameWrapper = (await contracts!.getNameWrapper()!).connect(signer)
|
|
41
|
+
const hash = namehash(name)
|
|
42
|
+
|
|
43
|
+
return nameWrapper.populateTransaction.setFuses(hash, encodedFuses)
|
|
44
|
+
}
|