@ensdomains/ensjs 3.0.0-alpha.41 → 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.
Files changed (107) hide show
  1. package/dist/cjs/contracts/getContractAddress.js +4 -4
  2. package/dist/cjs/functions/commitName.js +2 -3
  3. package/dist/cjs/functions/createSubname.js +2 -8
  4. package/dist/cjs/functions/getHistory.js +14 -7
  5. package/dist/cjs/functions/getNames.js +10 -6
  6. package/dist/cjs/functions/getOwner.js +25 -12
  7. package/dist/cjs/functions/getWrapperData.js +3 -4
  8. package/dist/cjs/functions/renewNames.js +13 -20
  9. package/dist/cjs/functions/{burnFuses.js → setFuses.js} +24 -6
  10. package/dist/cjs/functions/wrapName.js +7 -23
  11. package/dist/cjs/generated/factories/ETHRegistrarController__factory.js +6 -55
  12. package/dist/cjs/generated/factories/NameWrapper__factory.js +90 -69
  13. package/dist/cjs/generated/factories/PublicResolver__factory.js +83 -0
  14. package/dist/cjs/generated/factories/UniversalResolver__factory.js +67 -2
  15. package/dist/cjs/index.js +5 -4
  16. package/dist/cjs/utils/consts.js +3 -1
  17. package/dist/cjs/utils/fuses.js +218 -53
  18. package/dist/cjs/utils/normalise.js +2 -2
  19. package/dist/cjs/utils/recordHelpers.js +4 -3
  20. package/dist/cjs/utils/registerHelpers.js +9 -23
  21. package/dist/cjs/utils/subgraph-types.js +16 -0
  22. package/dist/cjs/utils/wrapper.js +2 -2
  23. package/dist/esm/contracts/getContractAddress.mjs +4 -4
  24. package/dist/esm/functions/commitName.mjs +2 -3
  25. package/dist/esm/functions/createSubname.mjs +2 -2
  26. package/dist/esm/functions/getHistory.mjs +14 -7
  27. package/dist/esm/functions/getNames.mjs +10 -6
  28. package/dist/esm/functions/getOwner.mjs +25 -12
  29. package/dist/esm/functions/getWrapperData.mjs +3 -4
  30. package/dist/esm/functions/renewNames.mjs +13 -20
  31. package/dist/esm/functions/setFuses.mjs +30 -0
  32. package/dist/esm/functions/wrapName.mjs +8 -18
  33. package/dist/esm/generated/factories/ETHRegistrarController__factory.mjs +6 -55
  34. package/dist/esm/generated/factories/NameWrapper__factory.mjs +90 -69
  35. package/dist/esm/generated/factories/PublicResolver__factory.mjs +83 -0
  36. package/dist/esm/generated/factories/UniversalResolver__factory.mjs +67 -2
  37. package/dist/esm/index.mjs +5 -4
  38. package/dist/esm/utils/consts.mjs +3 -1
  39. package/dist/esm/utils/fuses.mjs +218 -53
  40. package/dist/esm/utils/normalise.mjs +1 -1
  41. package/dist/esm/utils/recordHelpers.mjs +2 -1
  42. package/dist/esm/utils/registerHelpers.mjs +9 -17
  43. package/dist/esm/utils/subgraph-types.mjs +0 -0
  44. package/dist/esm/utils/wrapper.mjs +1 -1
  45. package/dist/types/functions/commitName.d.ts +0 -1
  46. package/dist/types/functions/createSubname.d.ts +2 -2
  47. package/dist/types/functions/getHistory.d.ts +1 -1
  48. package/dist/types/functions/getNames.d.ts +3 -3
  49. package/dist/types/functions/getOwner.d.ts +1 -0
  50. package/dist/types/functions/getWrapperData.d.ts +44 -5
  51. package/dist/types/functions/renewNames.d.ts +4 -5
  52. package/dist/types/functions/setFuses.d.ts +7 -0
  53. package/dist/types/functions/types.d.ts +6 -4
  54. package/dist/types/functions/wrapName.d.ts +3 -3
  55. package/dist/types/generated/ETHRegistrarController.d.ts +13 -36
  56. package/dist/types/generated/NameWrapper.d.ts +97 -52
  57. package/dist/types/generated/PublicResolver.d.ts +50 -1
  58. package/dist/types/generated/UniversalResolver.d.ts +79 -32
  59. package/dist/types/generated/factories/UniversalResolver__factory.d.ts +15 -0
  60. package/dist/types/index.d.ts +51 -9
  61. package/dist/types/utils/consts.d.ts +1 -0
  62. package/dist/types/utils/fuses.d.ts +115 -45
  63. package/dist/types/utils/registerHelpers.d.ts +7 -25
  64. package/dist/types/utils/subgraph-types.d.ts +284 -0
  65. package/package.json +5 -5
  66. package/src/contracts/getContractAddress.ts +4 -4
  67. package/src/functions/commitName.test.ts +0 -3
  68. package/src/functions/commitName.ts +1 -2
  69. package/src/functions/createSubname.ts +4 -5
  70. package/src/functions/getHistory.ts +59 -25
  71. package/src/functions/getNames.test.ts +1 -1
  72. package/src/functions/getNames.ts +26 -13
  73. package/src/functions/getOwner.test.ts +62 -3
  74. package/src/functions/getOwner.ts +31 -11
  75. package/src/functions/getWrapperData.test.ts +11 -45
  76. package/src/functions/getWrapperData.ts +7 -5
  77. package/src/functions/registerName.test.ts +3 -3
  78. package/src/functions/renewNames.ts +17 -23
  79. package/src/functions/setFuses.test.ts +240 -0
  80. package/src/functions/setFuses.ts +44 -0
  81. package/src/functions/types.ts +6 -4
  82. package/src/functions/wrapName.test.ts +8 -7
  83. package/src/functions/wrapName.ts +10 -24
  84. package/src/generated/ETHRegistrarController.ts +12 -79
  85. package/src/generated/NameWrapper.ts +155 -99
  86. package/src/generated/PublicResolver.ts +123 -0
  87. package/src/generated/UniversalResolver.ts +157 -16
  88. package/src/generated/factories/ETHRegistrarController__factory.ts +6 -55
  89. package/src/generated/factories/NameWrapper__factory.ts +90 -69
  90. package/src/generated/factories/PublicResolver__factory.ts +83 -0
  91. package/src/generated/factories/UniversalResolver__factory.ts +67 -2
  92. package/src/index.ts +11 -14
  93. package/src/utils/consts.ts +1 -0
  94. package/src/utils/fuses.ts +299 -102
  95. package/src/utils/normalise.ts +1 -1
  96. package/src/utils/recordHelpers.ts +2 -1
  97. package/src/utils/registerHelpers.ts +13 -41
  98. package/src/utils/subgraph-types.ts +312 -0
  99. package/src/utils/wrapper.ts +1 -1
  100. package/dist/cjs/utils/generateFuseInput.js +0 -40
  101. package/dist/esm/functions/burnFuses.mjs +0 -12
  102. package/dist/esm/utils/generateFuseInput.mjs +0 -15
  103. package/dist/types/functions/burnFuses.d.ts +0 -3
  104. package/dist/types/utils/generateFuseInput.d.ts +0 -3
  105. package/src/functions/burnFuses.test.ts +0 -148
  106. package/src/functions/burnFuses.ts +0 -16
  107. package/src/utils/generateFuseInput.ts +0 -13
@@ -1,11 +1,15 @@
1
+ import { JsonRpcProvider } from '@ethersproject/providers'
1
2
  import { ENS } from '..'
2
3
  import setup from '../tests/setup'
3
4
 
4
5
  let ensInstance: ENS
5
6
  let revert: Awaited<ReturnType<typeof setup>>['revert']
7
+ let provider: JsonRpcProvider
8
+ let accounts: string[]
6
9
 
7
10
  beforeAll(async () => {
8
- ;({ ensInstance, revert } = await setup())
11
+ ;({ ensInstance, revert, provider } = await setup())
12
+ accounts = await provider.listAccounts()
9
13
  })
10
14
 
11
15
  afterAll(async () => {
@@ -13,8 +17,63 @@ afterAll(async () => {
13
17
  })
14
18
 
15
19
  describe('getOwner', () => {
16
- it('should return nameWrapper as the ownership level for a wrapped name', async () => {
20
+ it('should return correct ownership level and values for a wrapped .eth name', async () => {
17
21
  const result = await ensInstance.getOwner('wrapped.eth')
18
- expect(result?.ownershipLevel).toBe('nameWrapper')
22
+ expect(result).toEqual({
23
+ ownershipLevel: 'nameWrapper',
24
+ owner: accounts[1],
25
+ expired: false,
26
+ })
27
+ })
28
+ it('should return correct ownership level and values for an expired wrapped .eth name', async () => {
29
+ const result = await ensInstance.getOwner('expired-wrapped.eth')
30
+ expect(result).toEqual({
31
+ ownershipLevel: 'nameWrapper',
32
+ owner: '0x0000000000000000000000000000000000000000',
33
+ expired: true,
34
+ })
35
+ })
36
+ it('should return correct ownership level and values for an unwrapped .eth name', async () => {
37
+ const result = await ensInstance.getOwner('test123.eth')
38
+ expect(result).toEqual({
39
+ ownershipLevel: 'registrar',
40
+ owner: accounts[1],
41
+ registrant: accounts[1],
42
+ expired: false,
43
+ })
44
+ })
45
+ it('should return correct ownership level and values for an expired unwrapped .eth name', async () => {
46
+ const result = await ensInstance.getOwner('expired.eth')
47
+ expect(result).toEqual({
48
+ ownershipLevel: 'registrar',
49
+ owner: accounts[1],
50
+ registrant: accounts[1].toLowerCase(),
51
+ expired: true,
52
+ })
53
+ })
54
+ describe('subname', () => {
55
+ it('should return correct ownership level and values for a unwrapped name', async () => {
56
+ const result = await ensInstance.getOwner('test.with-subnames.eth')
57
+ expect(result).toEqual({
58
+ ownershipLevel: 'registry',
59
+ owner: accounts[2],
60
+ })
61
+ })
62
+ it('should return correct ownership level and values for a wrapped name', async () => {
63
+ const result = await ensInstance.getOwner(
64
+ 'test.wrapped-with-subnames.eth',
65
+ )
66
+ expect(result).toEqual({
67
+ ownershipLevel: 'nameWrapper',
68
+ owner: accounts[2],
69
+ })
70
+ })
71
+ it('should return correct ownership level and values for an expired wrapped name', async () => {
72
+ const result = await ensInstance.getOwner('test.expired-wrapped.eth')
73
+ expect(result).toEqual({
74
+ ownershipLevel: 'nameWrapper',
75
+ owner: accounts[2],
76
+ })
77
+ })
19
78
  })
20
79
  })
@@ -9,6 +9,7 @@ type Owner = {
9
9
  registrant?: string
10
10
  owner?: string
11
11
  ownershipLevel: 'nameWrapper' | 'registry' | 'registrar'
12
+ expired?: boolean
12
13
  }
13
14
 
14
15
  const singleContractOwnerRaw = async (
@@ -146,26 +147,43 @@ const decode = async (
146
147
 
147
148
  const registryOwner = (decodedData[0] as Result)[0]
148
149
  const nameWrapperOwner = (decodedData[1] as Result)[0]
149
- let registrarOwner = (decodedData[2] as Result | undefined)?.[0]
150
+ let registrarOwner: string | undefined = (
151
+ decodedData[2] as Result | undefined
152
+ )?.[0]
153
+ let baseReturnObject: {
154
+ expired?: boolean
155
+ } = {}
150
156
 
151
157
  // check for only .eth names
152
158
  if (labels[labels.length - 1] === 'eth') {
153
- if (!registrarOwner && labels.length === 2) {
154
- const graphRegistrantResult = await gqlInstance.client.request(
155
- registrantQuery,
156
- {
157
- namehash: makeNamehash(name),
158
- },
159
- )
160
- registrarOwner =
161
- graphRegistrantResult.domain?.registration?.registrant?.id
159
+ // if there is no registrar owner, the name is expired
160
+ // but we still want to get the registrar owner prior to expiry
161
+ if (labels.length === 2) {
162
+ if (!registrarOwner) {
163
+ const graphRegistrantResult = await gqlInstance.client.request(
164
+ registrantQuery,
165
+ {
166
+ namehash: makeNamehash(name),
167
+ },
168
+ )
169
+ registrarOwner =
170
+ graphRegistrantResult.domain?.registration?.registrant?.id
171
+ baseReturnObject = {
172
+ expired: true,
173
+ }
174
+ } else {
175
+ baseReturnObject = {
176
+ expired: false,
177
+ }
178
+ }
162
179
  }
163
180
  // if the owner on the registrar is the namewrapper, then the namewrapper owner is the owner
164
181
  // there is no "registrant" for wrapped names
165
- if (registrarOwner === nameWrapper.address) {
182
+ if (registrarOwner?.toLowerCase() === nameWrapper.address.toLowerCase()) {
166
183
  return {
167
184
  owner: nameWrapperOwner,
168
185
  ownershipLevel: 'nameWrapper',
186
+ ...baseReturnObject,
169
187
  }
170
188
  }
171
189
  // if there is a registrar owner, then it's not a subdomain but we have also passed the namewrapper clause
@@ -177,6 +195,7 @@ const decode = async (
177
195
  registrant: registrarOwner,
178
196
  owner: registryOwner,
179
197
  ownershipLevel: 'registrar',
198
+ ...baseReturnObject,
180
199
  }
181
200
  }
182
201
  if (hexStripZeros(registryOwner) !== '0x') {
@@ -187,6 +206,7 @@ const decode = async (
187
206
  registrant: undefined,
188
207
  owner: registryOwner,
189
208
  ownershipLevel: 'registrar',
209
+ expired: true,
190
210
  }
191
211
  }
192
212
  // this means that the subname is wrapped
@@ -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({ ...result, expiryDate: result?.expiryDate.toString() }).toEqual(
45
- unwrappedNameDefault,
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.fuseObj.CAN_DO_EVERYTHING).toBe(true)
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.burnFuses('wrapped.eth', {
66
- namedFusesToBurn: [
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.fuseObj).toMatchObject({
79
- CANNOT_UNWRAP: true,
80
- CANNOT_BURN_FUSES: false,
81
- CANNOT_TRANSFER: false,
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, _fuses, expiry] = nameWrapper.interface.decodeFunctionResult(
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 = new Date(expiry * 1000)
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, wrapperExpiry } = customData!
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 { FuseProps, validateFuses } from '../utils/fuses'
4
- import { MAX_INT_64 } from '../utils/registerHelpers'
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
- export async function renewNameWithData(
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
- if (labels.length !== 2 || labels[1] !== 'eth') {
26
- throw new Error('Currently only .eth TLD renewals are supported')
27
- }
23
+ const labelHash = labelhash(labels.shift()!)
24
+ const parentNode = namehash(labels.join('.'))
28
25
 
29
- const encodedFuses = fuses ? validateFuses(fuses) : 0
30
- const controller = await contracts!.getEthRegistrarController()
31
- return controller.populateTransaction.renewWithFuses(
32
- labels[0],
33
- duration,
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
+ }
@@ -4,7 +4,6 @@ import type {
4
4
  resolverMulticallWrapper,
5
5
  universalWrapper,
6
6
  } from './batchWrappers'
7
- import type burnFuses from './burnFuses'
8
7
  import type commitName from './commitName'
9
8
  import type createSubname from './createSubname'
10
9
  import type deleteSubname from './deleteSubname'
@@ -36,8 +35,10 @@ import type registerName from './registerName'
36
35
  import type {
37
36
  // eslint-disable-next-line import/no-named-default
38
37
  default as renewNames,
39
- renewNameWithData,
38
+ extendWrappedName,
40
39
  } from './renewNames'
40
+ import type setFuses from './setFuses'
41
+ import type { setChildFuses } from './setFuses'
41
42
  import type setName from './setName'
42
43
  import type setRecord from './setRecord'
43
44
  import type setRecords from './setRecords'
@@ -54,7 +55,8 @@ type Function = {
54
55
  multicallWrapper: typeof multicallWrapper
55
56
  resolverMulticallWrapper: typeof resolverMulticallWrapper
56
57
  universalWrapper: typeof universalWrapper
57
- burnFuses: typeof burnFuses
58
+ setFuses: typeof setFuses
59
+ setChildFuses: typeof setChildFuses
58
60
  commitName: typeof commitName
59
61
  createSubname: typeof createSubname
60
62
  deleteSubname: typeof deleteSubname
@@ -82,7 +84,7 @@ type Function = {
82
84
  importDNSSECName: typeof importDNSSECName
83
85
  registerName: typeof registerName
84
86
  renewNames: typeof renewNames
85
- renewNameWithData: typeof renewNameWithData
87
+ extendWrappedName: typeof extendWrappedName
86
88
  setName: typeof setName
87
89
  setRecord: typeof setRecord
88
90
  setRecords: typeof setRecords