@ensdomains/ensjs 3.0.0-alpha.1 → 3.0.0-alpha.2

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 (71) hide show
  1. package/README.md +2 -0
  2. package/dist/cjs/functions/burnFuses.d.ts +1 -2
  3. package/dist/cjs/functions/burnFuses.js +3 -3
  4. package/dist/cjs/functions/createSubname.js +2 -1
  5. package/dist/cjs/functions/getFuses.js +3 -2
  6. package/dist/cjs/functions/getOwner.js +7 -6
  7. package/dist/cjs/functions/getProfile.d.ts +1 -1
  8. package/dist/cjs/functions/getProfile.js +49 -15
  9. package/dist/cjs/functions/getSpecificRecord.js +22 -5
  10. package/dist/cjs/functions/getSubnames.js +6 -6
  11. package/dist/cjs/functions/setRecords.d.ts +1 -2
  12. package/dist/cjs/functions/setRecords.js +3 -3
  13. package/dist/cjs/functions/setResolver.d.ts +1 -2
  14. package/dist/cjs/functions/setResolver.js +3 -3
  15. package/dist/cjs/functions/transferName.js +3 -2
  16. package/dist/cjs/functions/transferSubname.js +2 -1
  17. package/dist/cjs/functions/unwrapName.js +2 -1
  18. package/dist/cjs/index.js +1 -0
  19. package/dist/cjs/utils/makeHashIndexes.js +2 -2
  20. package/dist/cjs/utils/normalise.d.ts +1 -0
  21. package/dist/cjs/utils/normalise.js +20 -1
  22. package/dist/cjs/utils/registerHelpers.js +3 -4
  23. package/dist/esm/functions/burnFuses.d.ts +1 -2
  24. package/dist/esm/functions/burnFuses.js +3 -3
  25. package/dist/esm/functions/createSubname.js +2 -1
  26. package/dist/esm/functions/getFuses.js +4 -3
  27. package/dist/esm/functions/getOwner.js +7 -6
  28. package/dist/esm/functions/getProfile.d.ts +1 -1
  29. package/dist/esm/functions/getProfile.js +49 -15
  30. package/dist/esm/functions/getSpecificRecord.js +22 -5
  31. package/dist/esm/functions/getSubnames.js +3 -3
  32. package/dist/esm/functions/setRecords.d.ts +1 -2
  33. package/dist/esm/functions/setRecords.js +3 -3
  34. package/dist/esm/functions/setResolver.d.ts +1 -2
  35. package/dist/esm/functions/setResolver.js +3 -3
  36. package/dist/esm/functions/transferName.js +3 -2
  37. package/dist/esm/functions/transferSubname.js +2 -1
  38. package/dist/esm/functions/unwrapName.js +2 -1
  39. package/dist/esm/index.js +1 -0
  40. package/dist/esm/utils/makeHashIndexes.js +2 -2
  41. package/dist/esm/utils/normalise.d.ts +1 -0
  42. package/dist/esm/utils/normalise.js +18 -0
  43. package/dist/esm/utils/registerHelpers.js +3 -4
  44. package/package.json +3 -2
  45. package/src/functions/burnFuses.ts +3 -3
  46. package/src/functions/createSubname.ts +2 -1
  47. package/src/functions/getFuses.ts +4 -3
  48. package/src/functions/getOwner.ts +7 -7
  49. package/src/functions/getProfile.ts +77 -13
  50. package/src/functions/getSpecificRecord.ts +28 -8
  51. package/src/functions/getSubnames.ts +68 -66
  52. package/src/functions/setRecords.ts +3 -3
  53. package/src/functions/setResolver.ts +3 -3
  54. package/src/functions/transferName.ts +3 -2
  55. package/src/functions/transferSubname.ts +2 -1
  56. package/src/functions/unwrapName.ts +2 -1
  57. package/src/index.ts +1 -0
  58. package/src/tests/burnFuses.test.ts +3 -2
  59. package/src/tests/createSubname.test.ts +4 -5
  60. package/src/tests/deleteSubname.test.ts +4 -5
  61. package/src/tests/getOwner.test.ts +10 -0
  62. package/src/tests/getProfile.test.ts +28 -10
  63. package/src/tests/normalise.test.ts +22 -0
  64. package/src/tests/setRecords.test.ts +3 -3
  65. package/src/tests/transferName.test.ts +3 -2
  66. package/src/tests/transferSubname.test.ts +4 -5
  67. package/src/tests/unwrapName.test.ts +2 -1
  68. package/src/tests/wrapName.test.ts +4 -7
  69. package/src/utils/makeHashIndexes.ts +4 -4
  70. package/src/utils/normalise.ts +23 -0
  71. package/src/utils/registerHelpers.ts +3 -4
@@ -42,6 +42,16 @@ describe('getOwner', () => {
42
42
  expect(result?.ownershipLevel).toBe('registrar')
43
43
  expect(result?.registrant).toBe(nameWrapperAddress)
44
44
  })
45
+ it('should return the owner of a TLD on the registry', async () => {
46
+ const result = await ENSInstance.getOwner('eth')
47
+ const baseRegistrarAddress = (
48
+ await ENSInstance.contracts!.getBaseRegistrar()
49
+ ).address
50
+ expect(result?.ownershipLevel).toBe('registry')
51
+ expect(result?.owner?.toLowerCase()).toBe(
52
+ baseRegistrarAddress.toLowerCase(),
53
+ )
54
+ })
45
55
  // it('should return registry as the ownership level for an unwrapped subname', async () => {
46
56
  // const result0 = await ENSInstance.getOwner('fleek.eth')
47
57
  // console.log(result0?.owner, accounts[2])
@@ -8,11 +8,11 @@ beforeAll(async () => {
8
8
  })
9
9
 
10
10
  const checkRecords = (
11
- result: Record<string, any> | null,
11
+ result: Record<string, any> | undefined,
12
12
  textLength = 3,
13
13
  coinTypeLength = 5,
14
14
  ) => {
15
- expect(result).toBeTruthy()
15
+ expect(result).toBeDefined()
16
16
  if (result) {
17
17
  expect(result.records?.texts).toHaveLength(textLength)
18
18
  expect(result.records?.coinTypes).toHaveLength(coinTypeLength)
@@ -22,24 +22,32 @@ const checkRecords = (
22
22
  }
23
23
  }
24
24
 
25
+ jest.setTimeout(20000)
26
+
25
27
  describe('getProfile', () => {
26
28
  describe('with an address', () => {
27
29
  it('should return a profile object with no other args', async () => {
28
30
  const result = await ENSInstance.getProfile(
29
31
  '0x866B3c4994e1416B7C738B9818b31dC246b95eEE',
30
32
  )
31
- expect((result as any).name).toBe('jefflau.eth')
32
- expect((result as any).address).toBeUndefined()
33
- checkRecords(result)
33
+ expect(result).toBeDefined()
34
+ if (result) {
35
+ expect((result as any).name).toBe('jefflau.eth')
36
+ expect((result as any).address).toBeUndefined()
37
+ checkRecords(result)
38
+ }
34
39
  })
35
40
  it('should return a profile object with specified records', async () => {
36
41
  const result = await ENSInstance.getProfile(
37
42
  '0x866B3c4994e1416B7C738B9818b31dC246b95eEE',
38
43
  { texts: ['description', 'url'], coinTypes: ['ETC', '0'] },
39
44
  )
40
- expect((result as any).name).toBe('jefflau.eth')
41
- expect((result as any).address).toBeUndefined()
42
- checkRecords(result, 2, 3)
45
+ expect(result).toBeDefined()
46
+ if (result) {
47
+ expect((result as any).name).toBe('jefflau.eth')
48
+ expect((result as any).address).toBeUndefined()
49
+ checkRecords(result, 2, 3)
50
+ }
43
51
  })
44
52
  it('should return a profile object with all of each specified record type', async () => {
45
53
  const result = await ENSInstance.getProfile(
@@ -80,11 +88,22 @@ describe('getProfile', () => {
80
88
  })
81
89
  checkRecords(result)
82
90
  })
83
- it('should return null for an unregistered name', async () => {
91
+ it('should return undefined for an unregistered name', async () => {
84
92
  const result = await ENSInstance.getProfile('test123123123cool.eth')
85
93
  expect(result).toBeUndefined()
86
94
  })
87
95
  })
96
+ describe('with an old resolver', () => {
97
+ it('should use fallback methods for a name with an older resolver (no multicall)', async () => {
98
+ const result = await ENSInstance.getProfile('pepeq6.eth')
99
+ expect(result).toBeDefined()
100
+ if (result) {
101
+ expect(result.address).toBe(
102
+ '0x6308F1c6f283583C8bf8E31Da793B87718b051eD',
103
+ )
104
+ }
105
+ })
106
+ })
88
107
  describe('with an unmigrated name', () => {
89
108
  beforeAll(async () => {
90
109
  ;({ ENSInstance } = await setup(true))
@@ -95,7 +114,6 @@ describe('getProfile', () => {
95
114
  expect(result).toBeTruthy()
96
115
  if (result) {
97
116
  expect(result.isMigrated).toBe(false)
98
- expect(result.message).toBe("Records fetch didn't complete")
99
117
  }
100
118
  })
101
119
  })
@@ -0,0 +1,22 @@
1
+ import { namehash } from '../utils/normalise'
2
+
3
+ describe('normalise', () => {
4
+ it('should namehash an empty string', () => {
5
+ const hash = namehash('')
6
+ expect(hash).toEqual(
7
+ '0x0000000000000000000000000000000000000000000000000000000000000000',
8
+ )
9
+ })
10
+ it('should namehash a TLD', () => {
11
+ const hash = namehash('eth')
12
+ expect(hash).toEqual(
13
+ '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae',
14
+ )
15
+ })
16
+ it('should namehash a 2LD', () => {
17
+ const hash = namehash('foo.eth')
18
+ expect(hash).toEqual(
19
+ '0xde9b09fd7c5f901e23a3f19fecc54828e9c848539801e86591bd9801b019f84f',
20
+ )
21
+ })
22
+ })
@@ -1,6 +1,6 @@
1
- import { utils } from 'ethers'
2
1
  import { ENS } from '..'
3
2
  import { hexEncodeName } from '../utils/hexEncodedName'
3
+ import { namehash } from '../utils/normalise'
4
4
  import setup from './setup'
5
5
 
6
6
  let ENSInstance: ENS
@@ -31,14 +31,14 @@ describe('setRecords', () => {
31
31
  const encodedText = await universalResolver.resolve(
32
32
  hexEncodeName('parthtejpal.eth'),
33
33
  publicResolver.interface.encodeFunctionData('text(bytes32,string)', [
34
- utils.namehash('parthtejpal.eth'),
34
+ namehash('parthtejpal.eth'),
35
35
  'foo',
36
36
  ]),
37
37
  )
38
38
  const encodedAddr = await universalResolver.resolve(
39
39
  hexEncodeName('parthtejpal.eth'),
40
40
  publicResolver.interface.encodeFunctionData('addr(bytes32,uint256)', [
41
- utils.namehash('parthtejpal.eth'),
41
+ namehash('parthtejpal.eth'),
42
42
  '61',
43
43
  ]),
44
44
  )
@@ -1,5 +1,6 @@
1
1
  import { ethers, utils } from 'ethers'
2
2
  import { ENS } from '..'
3
+ import { namehash } from '../utils/normalise'
3
4
  import setup from './setup'
4
5
 
5
6
  let ENSInstance: ENS
@@ -52,7 +53,7 @@ describe('transferName', () => {
52
53
  await tx.wait()
53
54
 
54
55
  const nameWrapper = await ENSInstance.contracts!.getNameWrapper()!
55
- const result = await nameWrapper.ownerOf(utils.namehash('parthtejpal.eth'))
56
+ const result = await nameWrapper.ownerOf(namehash('parthtejpal.eth'))
56
57
  expect(result).toBe(accounts[1])
57
58
  })
58
59
  it('should allow a transfer on the registry', async () => {
@@ -74,7 +75,7 @@ describe('transferName', () => {
74
75
  await tx.wait()
75
76
 
76
77
  const registry = await ENSInstance.contracts!.getRegistry()!
77
- const result = await registry.owner(utils.namehash('test.parthtejpal.eth'))
78
+ const result = await registry.owner(namehash('test.parthtejpal.eth'))
78
79
  expect(result).toBe(accounts[1])
79
80
  })
80
81
  })
@@ -1,5 +1,6 @@
1
- import { ethers, utils } from 'ethers'
1
+ import { ethers } from 'ethers'
2
2
  import { ENS } from '..'
3
+ import { namehash } from '../utils/normalise'
3
4
  import setup from './setup'
4
5
 
5
6
  let ENSInstance: ENS
@@ -39,7 +40,7 @@ describe('transferSubname', () => {
39
40
  await tx.wait()
40
41
 
41
42
  const registry = await ENSInstance.contracts!.getRegistry()!
42
- const result = await registry.owner(utils.namehash('test.parthtejpal.eth'))
43
+ const result = await registry.owner(namehash('test.parthtejpal.eth'))
43
44
  expect(result).toBe(accounts[1])
44
45
  })
45
46
  it('should allow transferring a subname on the nameWrapper', async () => {
@@ -66,9 +67,7 @@ describe('transferSubname', () => {
66
67
  await tx.wait()
67
68
 
68
69
  const nameWrapper = await ENSInstance.contracts!.getNameWrapper()!
69
- const result = await nameWrapper.ownerOf(
70
- utils.namehash('test.parthtejpal.eth'),
71
- )
70
+ const result = await nameWrapper.ownerOf(namehash('test.parthtejpal.eth'))
72
71
  expect(result).toBe(accounts[1])
73
72
  })
74
73
  })
@@ -1,5 +1,6 @@
1
1
  import { ethers, utils } from 'ethers'
2
2
  import { ENS } from '..'
3
+ import { namehash } from '../utils/normalise'
3
4
  import setup from './setup'
4
5
 
5
6
  let ENSInstance: ENS
@@ -61,7 +62,7 @@ describe('unwrapName', () => {
61
62
  await tx.wait()
62
63
 
63
64
  const registry = await ENSInstance.contracts!.getRegistry()!
64
- const result = await registry.owner(utils.namehash('test.parthtejpal.eth'))
65
+ const result = await registry.owner(namehash('test.parthtejpal.eth'))
65
66
  expect(result).toBe(accounts[0])
66
67
  })
67
68
  })
@@ -1,6 +1,7 @@
1
- import { BigNumber, ethers, utils } from 'ethers'
1
+ import { BigNumber, ethers } from 'ethers'
2
2
  import { ENS } from '..'
3
3
  import { hexEncodeName } from '../utils/hexEncodedName'
4
+ import { namehash } from '../utils/normalise'
4
5
  import setup from './setup'
5
6
 
6
7
  let ENSInstance: ENS
@@ -28,9 +29,7 @@ describe('wrapName', () => {
28
29
  await tx.wait()
29
30
 
30
31
  const nameWrapper = await ENSInstance.contracts!.getNameWrapper()!
31
- const [result] = await nameWrapper.getFuses(
32
- utils.namehash('parthtejpal.eth'),
33
- )
32
+ const [result] = await nameWrapper.getFuses(namehash('parthtejpal.eth'))
34
33
  expect((result as BigNumber).toHexString()).toBe('0x40')
35
34
  })
36
35
  it('should allow initial fuses', async () => {
@@ -42,9 +41,7 @@ describe('wrapName', () => {
42
41
  await tx.wait()
43
42
 
44
43
  const nameWrapper = await ENSInstance.contracts!.getNameWrapper()!
45
- const [result] = await nameWrapper.getFuses(
46
- utils.namehash('parthtejpal.eth'),
47
- )
44
+ const [result] = await nameWrapper.getFuses(namehash('parthtejpal.eth'))
48
45
  expect((result as BigNumber).toHexString()).toBe('0x51')
49
46
  })
50
47
  it('should allow an initial resolver address', async () => {
@@ -1,9 +1,9 @@
1
- import { ethers } from 'ethers'
1
+ import { namehash } from './normalise'
2
2
 
3
3
  export const makeOtherIndexes = (data: string, findStr: string) =>
4
4
  Array.from(data.matchAll(findStr as any)).map((x: any) => x.index / 2 - 1)
5
5
 
6
6
  export const makeNamehashIndexes = (data: string, name: string) =>
7
- Array.from(
8
- data.matchAll(ethers.utils.namehash(name).substring(2) as any),
9
- ).map((x: any) => x.index / 2 - 1)
7
+ Array.from(data.matchAll(namehash(name).substring(2) as any)).map(
8
+ (x: any) => x.index / 2 - 1,
9
+ )
@@ -1,4 +1,27 @@
1
+ import { concat, hexlify, keccak256, toUtf8Bytes } from 'ethers/lib/utils'
1
2
  import uts46 from 'idna-uts46-hx/uts46bundle.js'
2
3
 
4
+ const zeros = new Uint8Array(32)
5
+ zeros.fill(0)
6
+
3
7
  export const normalise = (name: string) =>
4
8
  name ? uts46.toUnicode(name, { useStd3ASCII: true }) : name
9
+
10
+ export const namehash = (inputName: string): string => {
11
+ let result: string | Uint8Array = zeros
12
+
13
+ const name = normalise(inputName)
14
+
15
+ if (name) {
16
+ const labels = name.split('.')
17
+
18
+ for (var i = labels.length - 1; i >= 0; i--) {
19
+ const labelSha = keccak256(toUtf8Bytes(labels[i]))
20
+ result = keccak256(concat([result, labelSha]))
21
+ }
22
+ } else {
23
+ result = hexlify(zeros)
24
+ }
25
+
26
+ return result as string
27
+ }
@@ -3,6 +3,7 @@ import type { FuseOptions } from '../@types/FuseOptions'
3
3
  import type { PublicResolver } from '../generated'
4
4
  import generateFuseInput from './generateFuseInput'
5
5
  import { labelhash } from './labels'
6
+ import { namehash } from './normalise'
6
7
  import { generateRecordCallArray, RecordOptions } from './recordHelpers'
7
8
 
8
9
  export const randomSecret = () => {
@@ -28,11 +29,9 @@ export const makeCommitment = ({
28
29
  fuses?: FuseOptions
29
30
  }) => {
30
31
  const label = labelhash(name.split('.')[0])
31
- const namehash = utils.namehash(name)
32
+ const hash = namehash(name)
32
33
  const resolverAddress = resolver.address
33
- const data = records
34
- ? generateRecordCallArray(namehash, records, resolver)
35
- : []
34
+ const data = records ? generateRecordCallArray(hash, records, resolver) : []
36
35
  const secret = randomSecret()
37
36
  const fuseData = fuses ? generateFuseInput(fuses) : '0'
38
37