@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.
Files changed (129) 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/getSpecificRecord.js +83 -0
  8. package/dist/cjs/functions/getWrapperData.js +3 -4
  9. package/dist/cjs/functions/renewNames.js +13 -20
  10. package/dist/cjs/functions/{burnFuses.js → setFuses.js} +24 -6
  11. package/dist/cjs/functions/wrapName.js +7 -23
  12. package/dist/cjs/generated/factories/ETHRegistrarController__factory.js +6 -55
  13. package/dist/cjs/generated/factories/NameWrapper__factory.js +90 -69
  14. package/dist/cjs/generated/factories/PublicResolver__factory.js +83 -0
  15. package/dist/cjs/generated/factories/UniversalResolver__factory.js +67 -2
  16. package/dist/cjs/index.js +15 -4
  17. package/dist/cjs/utils/consts.js +3 -1
  18. package/dist/cjs/utils/fuses.js +218 -53
  19. package/dist/cjs/utils/normalise.js +2 -2
  20. package/dist/cjs/utils/recordHelpers.js +55 -0
  21. package/dist/cjs/utils/registerHelpers.js +9 -23
  22. package/dist/cjs/utils/subgraph-types.js +16 -0
  23. package/dist/cjs/utils/wrapper.js +2 -2
  24. package/dist/esm/contracts/getContractAddress.mjs +4 -4
  25. package/dist/esm/functions/commitName.mjs +2 -3
  26. package/dist/esm/functions/createSubname.mjs +2 -2
  27. package/dist/esm/functions/getHistory.mjs +14 -7
  28. package/dist/esm/functions/getNames.mjs +10 -6
  29. package/dist/esm/functions/getOwner.mjs +25 -12
  30. package/dist/esm/functions/getSpecificRecord.mjs +78 -1
  31. package/dist/esm/functions/getWrapperData.mjs +3 -4
  32. package/dist/esm/functions/renewNames.mjs +13 -20
  33. package/dist/esm/functions/setFuses.mjs +30 -0
  34. package/dist/esm/functions/wrapName.mjs +8 -18
  35. package/dist/esm/generated/factories/ETHRegistrarController__factory.mjs +6 -55
  36. package/dist/esm/generated/factories/NameWrapper__factory.mjs +90 -69
  37. package/dist/esm/generated/factories/PublicResolver__factory.mjs +83 -0
  38. package/dist/esm/generated/factories/UniversalResolver__factory.mjs +67 -2
  39. package/dist/esm/index.mjs +15 -4
  40. package/dist/esm/utils/consts.mjs +3 -1
  41. package/dist/esm/utils/fuses.mjs +218 -53
  42. package/dist/esm/utils/normalise.mjs +1 -1
  43. package/dist/esm/utils/recordHelpers.mjs +49 -0
  44. package/dist/esm/utils/registerHelpers.mjs +9 -17
  45. package/dist/esm/utils/subgraph-types.mjs +0 -0
  46. package/dist/esm/utils/wrapper.mjs +1 -1
  47. package/dist/types/functions/commitName.d.ts +5 -6
  48. package/dist/types/functions/createSubname.d.ts +3 -3
  49. package/dist/types/functions/deleteSubname.d.ts +1 -1
  50. package/dist/types/functions/getHistory.d.ts +1 -1
  51. package/dist/types/functions/getNames.d.ts +3 -3
  52. package/dist/types/functions/getOwner.d.ts +1 -0
  53. package/dist/types/functions/getSpecificRecord.d.ts +22 -0
  54. package/dist/types/functions/getWrapperData.d.ts +44 -5
  55. package/dist/types/functions/importDNSSECName.d.ts +1 -1
  56. package/dist/types/functions/registerName.d.ts +1 -1
  57. package/dist/types/functions/renewNames.d.ts +5 -6
  58. package/dist/types/functions/setFuses.d.ts +7 -0
  59. package/dist/types/functions/setName.d.ts +1 -1
  60. package/dist/types/functions/setRecord.d.ts +5 -1
  61. package/dist/types/functions/setRecords.d.ts +1 -1
  62. package/dist/types/functions/setResolver.d.ts +1 -1
  63. package/dist/types/functions/transferController.d.ts +1 -1
  64. package/dist/types/functions/transferName.d.ts +1 -1
  65. package/dist/types/functions/transferSubname.d.ts +1 -1
  66. package/dist/types/functions/types.d.ts +9 -5
  67. package/dist/types/functions/unwrapName.d.ts +1 -1
  68. package/dist/types/functions/wrapName.d.ts +4 -4
  69. package/dist/types/generated/ETHRegistrarController.d.ts +13 -36
  70. package/dist/types/generated/NameWrapper.d.ts +97 -52
  71. package/dist/types/generated/PublicResolver.d.ts +50 -1
  72. package/dist/types/generated/UniversalResolver.d.ts +79 -32
  73. package/dist/types/generated/factories/UniversalResolver__factory.d.ts +15 -0
  74. package/dist/types/index.d.ts +75 -11
  75. package/dist/types/utils/consts.d.ts +1 -0
  76. package/dist/types/utils/fuses.d.ts +115 -45
  77. package/dist/types/utils/recordHelpers.d.ts +14 -2
  78. package/dist/types/utils/registerHelpers.d.ts +7 -25
  79. package/dist/types/utils/subgraph-types.d.ts +284 -0
  80. package/dist/types/utils/writeTx.d.ts +5 -5
  81. package/package.json +8 -5
  82. package/src/@types/pako.d.ts +188 -0
  83. package/src/contracts/bulkRenewal.ts +1 -1
  84. package/src/contracts/getContractAddress.ts +4 -4
  85. package/src/functions/commitName.test.ts +0 -3
  86. package/src/functions/commitName.ts +1 -2
  87. package/src/functions/createSubname.ts +4 -5
  88. package/src/functions/getHistory.ts +59 -25
  89. package/src/functions/getNames.test.ts +1 -1
  90. package/src/functions/getNames.ts +26 -13
  91. package/src/functions/getOwner.test.ts +62 -3
  92. package/src/functions/getOwner.ts +31 -11
  93. package/src/functions/getSpecificRecord.test.ts +162 -0
  94. package/src/functions/getSpecificRecord.ts +95 -1
  95. package/src/functions/getWrapperData.test.ts +11 -45
  96. package/src/functions/getWrapperData.ts +7 -5
  97. package/src/functions/registerName.test.ts +3 -3
  98. package/src/functions/renewNames.ts +17 -23
  99. package/src/functions/setFuses.test.ts +240 -0
  100. package/src/functions/setFuses.ts +44 -0
  101. package/src/functions/setRecord.test.ts +147 -1
  102. package/src/functions/setRecord.ts +6 -1
  103. package/src/functions/types.ts +10 -4
  104. package/src/functions/wrapName.test.ts +8 -7
  105. package/src/functions/wrapName.ts +10 -24
  106. package/src/generated/ETHRegistrarController.ts +12 -79
  107. package/src/generated/NameWrapper.ts +155 -99
  108. package/src/generated/PublicResolver.ts +123 -0
  109. package/src/generated/UniversalResolver.ts +157 -16
  110. package/src/generated/factories/ETHRegistrarController__factory.ts +6 -55
  111. package/src/generated/factories/NameWrapper__factory.ts +90 -69
  112. package/src/generated/factories/PublicResolver__factory.ts +83 -0
  113. package/src/generated/factories/UniversalResolver__factory.ts +67 -2
  114. package/src/index.ts +23 -14
  115. package/src/utils/consts.ts +1 -0
  116. package/src/utils/fuses.ts +299 -102
  117. package/src/utils/normalise.ts +1 -1
  118. package/src/utils/recordHelpers.ts +64 -1
  119. package/src/utils/registerHelpers.ts +13 -41
  120. package/src/utils/subgraph-types.ts +312 -0
  121. package/src/utils/wrapper.ts +1 -1
  122. package/dist/cjs/utils/generateFuseInput.js +0 -40
  123. package/dist/esm/functions/burnFuses.mjs +0 -12
  124. package/dist/esm/utils/generateFuseInput.mjs +0 -15
  125. package/dist/types/functions/burnFuses.d.ts +0 -3
  126. package/dist/types/utils/generateFuseInput.d.ts +0 -3
  127. package/src/functions/burnFuses.test.ts +0 -148
  128. package/src/functions/burnFuses.ts +0 -16
  129. package/src/utils/generateFuseInput.ts +0 -13
@@ -1,8 +1,7 @@
1
1
  import { BigNumber } from '@ethersproject/bignumber/lib/bignumber'
2
2
  import { keccak256 as solidityKeccak256 } from '@ethersproject/solidity'
3
3
  import { ENSArgs } from '..'
4
- import { FuseOptions } from '../utils/fuses'
5
- import generateFuseInput from '../utils/generateFuseInput'
4
+ import { CombinedFuseInput, encodeFuses } from '../utils/fuses'
6
5
  import { namehash } from '../utils/normalise'
7
6
  import { Expiry, makeExpiry, wrappedLabelLengthCheck } from '../utils/wrapper'
8
7
 
@@ -14,7 +13,7 @@ type BaseArgs = {
14
13
 
15
14
  type NameWrapperArgs = {
16
15
  contract: 'nameWrapper'
17
- fuses?: FuseOptions
16
+ fuses?: CombinedFuseInput
18
17
  expiry?: Expiry
19
18
  } & BaseArgs
20
19
 
@@ -70,8 +69,8 @@ export default async function (
70
69
 
71
70
  const generatedFuses =
72
71
  'fuses' in wrapperArgs && wrapperArgs.fuses
73
- ? generateFuseInput(wrapperArgs.fuses)
74
- : '0'
72
+ ? encodeFuses(wrapperArgs.fuses)
73
+ : 0
75
74
 
76
75
  return nameWrapper.populateTransaction.setSubnodeRecord(
77
76
  parentNodehash,
@@ -4,6 +4,30 @@ import { ENSArgs } from '..'
4
4
  import { decodeContenthash } from '../utils/contentHash'
5
5
  import { labelhash } from '../utils/labels'
6
6
  import { namehash } from '../utils/normalise'
7
+ import {
8
+ AbiChanged,
9
+ AddrChanged,
10
+ AuthorisationChanged,
11
+ ContenthashChanged,
12
+ ExpiryExtended,
13
+ FusesSet,
14
+ InterfaceChanged,
15
+ MulticoinAddrChanged,
16
+ NameChanged,
17
+ NameRegistered,
18
+ NameRenewed,
19
+ NameTransferred,
20
+ NameUnwrapped,
21
+ NameWrapped,
22
+ NewOwner,
23
+ NewResolver,
24
+ NewTtl,
25
+ PubkeyChanged,
26
+ TextChanged,
27
+ Transfer,
28
+ VersionChanged,
29
+ WrappedTransfer,
30
+ } from '../utils/subgraph-types'
7
31
 
8
32
  type DomainEvent =
9
33
  | 'NewOwner'
@@ -14,6 +38,7 @@ type DomainEvent =
14
38
  | 'NameWrapped'
15
39
  | 'NameUnwrapped'
16
40
  | 'FusesSet'
41
+ | 'ExpiryExtended'
17
42
  type RegistrationEvent = 'NameRegistered' | 'NameRenewed' | 'NameTransferred'
18
43
  type ResolverEvent =
19
44
  | 'AddrChanged'
@@ -38,30 +63,35 @@ const eventFormat: {
38
63
  [key in EventTypes]: { [n in EventFormat[key]]: (args: any) => object }
39
64
  } = {
40
65
  Domain: {
41
- NewOwner: (args: any) => ({ owner: args.owner.id }),
42
- NewResolver: (args: any) => ({ resolver: args.resolver.id.split('-')[0] }),
43
- Transfer: (args: any) => ({ owner: args.owner.id }),
44
- NewTTL: (args: any) => ({ ttl: args.ttl }),
45
- WrappedTransfer: (args: any) => ({ owner: args.owner.id }),
46
- NameWrapped: (args: any) => ({
66
+ NewOwner: (args: NewOwner) => ({ owner: args.owner.id }),
67
+ NewResolver: (args: NewResolver) => ({
68
+ resolver: args.resolver.id.split('-')[0],
69
+ }),
70
+ Transfer: (args: Transfer) => ({ owner: args.owner.id }),
71
+ NewTTL: (args: NewTtl) => ({ ttl: args.ttl }),
72
+ WrappedTransfer: (args: WrappedTransfer) => ({ owner: args.owner.id }),
73
+ NameWrapped: (args: NameWrapped) => ({
47
74
  fuses: args.fuses,
48
75
  owner: args.owner.id,
49
- expiry: args.expiry,
76
+ expiryDate: args.expiryDate,
50
77
  }),
51
- NameUnwrapped: (args: any) => ({ owner: args.owner.id }),
52
- FusesSet: (args: any) => ({ fuses: args.fuses, expiry: args.expiry }),
78
+ NameUnwrapped: (args: NameUnwrapped) => ({ owner: args.owner.id }),
79
+ FusesSet: (args: FusesSet) => ({ fuses: args.fuses }),
80
+ ExpiryExtended: (args: ExpiryExtended) => ({ expiryDate: args.expiryDate }),
53
81
  },
54
82
  Registration: {
55
- NameRegistered: (args: any) => ({
83
+ NameRegistered: (args: NameRegistered) => ({
56
84
  registrant: args.registrant.id,
57
85
  expiryDate: args.expiryDate,
58
86
  }),
59
- NameRenewed: (args: any) => ({ expiryDate: args.expiryDate }),
60
- NameTransferred: (args: any) => ({ owner: args.newOwner.id }),
87
+ NameRenewed: (args: NameRenewed) => ({ expiryDate: args.expiryDate }),
88
+ NameTransferred: (args: NameTransferred) => ({ owner: args.newOwner.id }),
61
89
  },
62
90
  Resolver: {
63
- AddrChanged: (args: any) => ({ addr: args.addr.id }),
64
- MulticoinAddrChanged: (args: any) => {
91
+ AddrChanged: (args: AddrChanged) => ({ addr: args.addr.id }),
92
+ MulticoinAddrChanged: (
93
+ args: Omit<MulticoinAddrChanged, 'addr'> & { multiaddr: string },
94
+ ) => {
65
95
  const format = formatsByCoinType[parseInt(args.coinType)]
66
96
  if (!format) {
67
97
  return {
@@ -82,21 +112,23 @@ const eventFormat: {
82
112
  addr: format.encoder(Buffer.from(args.multiaddr.slice(2), 'hex')),
83
113
  }
84
114
  },
85
- NameChanged: (args: any) => ({ name: args.name }),
86
- AbiChanged: (args: any) => ({ contentType: args.contentType }),
87
- PubkeyChanged: (args: any) => ({ x: args.x, y: args.y }),
88
- TextChanged: (args: any) => ({ key: args.key, value: args.value }),
89
- ContenthashChanged: (args: any) => ({ hash: decodeContenthash(args.hash) }),
90
- InterfaceChanged: (args: any) => ({
91
- interfaceId: args.interfaceId,
115
+ NameChanged: (args: NameChanged) => ({ name: args.name }),
116
+ AbiChanged: (args: AbiChanged) => ({ contentType: args.contentType }),
117
+ PubkeyChanged: (args: PubkeyChanged) => ({ x: args.x, y: args.y }),
118
+ TextChanged: (args: TextChanged) => ({ key: args.key, value: args.value }),
119
+ ContenthashChanged: (args: ContenthashChanged) => ({
120
+ hash: decodeContenthash(args.hash),
121
+ }),
122
+ InterfaceChanged: (args: InterfaceChanged) => ({
123
+ interfaceId: args.interfaceID,
92
124
  implementer: args.implementer,
93
125
  }),
94
- AuthorisationChanged: (args: any) => ({
126
+ AuthorisationChanged: (args: AuthorisationChanged) => ({
95
127
  owner: args.owner,
96
128
  target: args.target,
97
129
  isAuthorized: args.isAuthorized,
98
130
  }),
99
- VersionChanged: (args: any) => ({ version: args.version }),
131
+ VersionChanged: (args: VersionChanged) => ({ version: args.version }),
100
132
  },
101
133
  }
102
134
 
@@ -154,7 +186,7 @@ export async function getHistory(
154
186
  }
155
187
  ...on NameWrapped {
156
188
  fuses
157
- expiry
189
+ expiryDate
158
190
  owner {
159
191
  id
160
192
  }
@@ -166,7 +198,9 @@ export async function getHistory(
166
198
  }
167
199
  ...on FusesSet {
168
200
  fuses
169
- expiry
201
+ }
202
+ ...on ExpiryExtended {
203
+ expiryDate
170
204
  }
171
205
  }
172
206
  registration {
@@ -172,7 +172,7 @@ describe('getNames', () => {
172
172
  type: 'wrappedOwner',
173
173
  page: 0,
174
174
  })
175
- expect(pageOne).toHaveLength(1)
175
+ expect(pageOne).toHaveLength(2)
176
176
  })
177
177
  describe('orderBy', () => {
178
178
  describe('registrations', () => {
@@ -1,7 +1,8 @@
1
1
  import { ENSArgs } from '..'
2
2
  import { truncateFormat } from '../utils/format'
3
- import { CurrentFuses, decodeFuses } from '../utils/fuses'
3
+ import { AllCurrentFuses, decodeFuses } from '../utils/fuses'
4
4
  import { decryptName } from '../utils/labels'
5
+ import { Domain, Registration, WrappedDomain } from '../utils/subgraph-types'
5
6
 
6
7
  export type Name = {
7
8
  id: string
@@ -16,7 +17,7 @@ export type Name = {
16
17
  createdAt?: Date
17
18
  registrationDate?: Date
18
19
  expiryDate?: Date
19
- fuses?: CurrentFuses
20
+ fuses?: AllCurrentFuses
20
21
  registration?: {
21
22
  expiryDate: Date
22
23
  registrationDate: Date
@@ -44,7 +45,7 @@ type OwnerParams = {
44
45
 
45
46
  type WrappedOwnerParams = {
46
47
  type: 'wrappedOwner'
47
- orderBy?: 'expiryDate' | 'labelName'
48
+ orderBy?: 'expiryDate' | 'name'
48
49
  }
49
50
 
50
51
  type AllParams = {
@@ -57,23 +58,35 @@ type AllParams = {
57
58
  type Params = BaseParams &
58
59
  (RegistrantParams | OwnerParams | WrappedOwnerParams | AllParams)
59
60
 
60
- const mapDomain = (domain: any) => {
61
- const decrypted = decryptName(domain.name)
61
+ const mapDomain = ({ name, ...domain }: Domain) => {
62
+ const decrypted = name ? decryptName(name) : undefined
62
63
  return {
63
64
  ...domain,
64
65
  name: decrypted,
65
- truncatedName: truncateFormat(decrypted),
66
+ truncatedName: decrypted ? truncateFormat(decrypted) : undefined,
66
67
  createdAt: new Date(parseInt(domain.createdAt) * 1000),
67
68
  type: 'domain',
68
69
  }
69
70
  }
70
71
 
71
- const mapWrappedDomain = (wrappedDomain: any) => {
72
- const domain = mapDomain(wrappedDomain.domain)
72
+ const mapWrappedDomain = (wrappedDomain: WrappedDomain) => {
73
+ const domain = mapDomain(wrappedDomain.domain) as Omit<
74
+ ReturnType<typeof mapDomain>,
75
+ 'registration'
76
+ > & {
77
+ registration?: {
78
+ expiryDate: string | Date
79
+ registrationDate: string | Date
80
+ }
81
+ }
73
82
  if (domain.registration) {
74
83
  domain.registration = {
75
- expiryDate: new Date(parseInt(domain.registration.expiryDate) * 1000),
76
- registrationDate: new Date(domain.registration.registrationDate * 1000),
84
+ expiryDate: new Date(
85
+ parseInt(domain.registration.expiryDate as string) * 1000,
86
+ ),
87
+ registrationDate: new Date(
88
+ parseInt(domain.registration.registrationDate as string) * 1000,
89
+ ),
77
90
  }
78
91
  }
79
92
  return {
@@ -84,8 +97,8 @@ const mapWrappedDomain = (wrappedDomain: any) => {
84
97
  }
85
98
  }
86
99
 
87
- const mapRegistration = (registration: any) => {
88
- const decrypted = decryptName(registration.domain.name)
100
+ const mapRegistration = (registration: Registration) => {
101
+ const decrypted = decryptName(registration.domain.name!)
89
102
  return {
90
103
  expiryDate: new Date(parseInt(registration.expiryDate) * 1000),
91
104
  registrationDate: new Date(parseInt(registration.registrationDate) * 1000),
@@ -275,7 +288,7 @@ const getNames = async (
275
288
  id: address,
276
289
  first: pageSize,
277
290
  skip: (page || 0) * pageSize,
278
- orderBy,
291
+ orderBy: orderBy === 'labelName' ? 'name' : orderBy,
279
292
  orderDirection,
280
293
  }
281
294
  }
@@ -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
@@ -7,6 +7,125 @@ beforeAll(async () => {
7
7
  ;({ ensInstance } = await setup())
8
8
  })
9
9
 
10
+ const dummyABI = [
11
+ {
12
+ type: 'event',
13
+ anonymous: false,
14
+ name: 'ABIChanged',
15
+ inputs: [
16
+ {
17
+ type: 'bytes32',
18
+ indexed: true,
19
+ },
20
+ {
21
+ type: 'uint256',
22
+ indexed: true,
23
+ },
24
+ ],
25
+ },
26
+ {
27
+ type: 'event',
28
+ anonymous: false,
29
+ name: 'VersionChanged',
30
+ inputs: [
31
+ {
32
+ type: 'bytes32',
33
+ indexed: true,
34
+ },
35
+ {
36
+ type: 'uint64',
37
+ },
38
+ ],
39
+ },
40
+ {
41
+ type: 'function',
42
+ name: 'ABI',
43
+ constant: true,
44
+ stateMutability: 'view',
45
+ payable: false,
46
+ inputs: [
47
+ {
48
+ type: 'bytes32',
49
+ },
50
+ {
51
+ type: 'uint256',
52
+ },
53
+ ],
54
+ outputs: [
55
+ {
56
+ type: 'uint256',
57
+ },
58
+ {
59
+ type: 'bytes',
60
+ },
61
+ ],
62
+ },
63
+ {
64
+ type: 'function',
65
+ name: 'clearRecords',
66
+ constant: false,
67
+ payable: false,
68
+ inputs: [
69
+ {
70
+ type: 'bytes32',
71
+ },
72
+ ],
73
+ outputs: [],
74
+ },
75
+ {
76
+ type: 'function',
77
+ name: 'recordVersions',
78
+ constant: true,
79
+ stateMutability: 'view',
80
+ payable: false,
81
+ inputs: [
82
+ {
83
+ type: 'bytes32',
84
+ },
85
+ ],
86
+ outputs: [
87
+ {
88
+ type: 'uint64',
89
+ },
90
+ ],
91
+ },
92
+ {
93
+ type: 'function',
94
+ name: 'setABI',
95
+ constant: false,
96
+ payable: false,
97
+ inputs: [
98
+ {
99
+ type: 'bytes32',
100
+ },
101
+ {
102
+ type: 'uint256',
103
+ },
104
+ {
105
+ type: 'bytes',
106
+ },
107
+ ],
108
+ outputs: [],
109
+ },
110
+ {
111
+ type: 'function',
112
+ name: 'supportsInterface',
113
+ constant: true,
114
+ stateMutability: 'view',
115
+ payable: false,
116
+ inputs: [
117
+ {
118
+ type: 'bytes4',
119
+ },
120
+ ],
121
+ outputs: [
122
+ {
123
+ type: 'bool',
124
+ },
125
+ ],
126
+ },
127
+ ]
128
+
10
129
  describe('getSpecificRecord', () => {
11
130
  describe('getContentHash', () => {
12
131
  it('should return null for a non-existent name', async () => {
@@ -76,4 +195,47 @@ describe('getSpecificRecord', () => {
76
195
  expect(result).toBeUndefined()
77
196
  })
78
197
  })
198
+
199
+ describe('getABI', () => {
200
+ it('should return object for type 1 ABI', async () => {
201
+ const result = await ensInstance.getABI('with-type-1-abi.eth')
202
+ expect(result).toBeTruthy()
203
+ if (result) {
204
+ expect(result.contentType).toBe(1)
205
+ expect(result.decoded).toBe(true)
206
+ expect(result.abi).toMatchObject(dummyABI)
207
+ }
208
+ })
209
+ it('should return object for type 2 ABI', async () => {
210
+ const result = await ensInstance.getABI('with-type-2-abi.eth')
211
+ expect(result).toBeTruthy()
212
+ if (result) {
213
+ expect(result.contentType).toBe(2)
214
+ expect(result.decoded).toBe(true)
215
+ expect(result.abi).toMatchObject(dummyABI)
216
+ }
217
+ })
218
+ it('should return object for type 4 ABI', async () => {
219
+ const result = await ensInstance.getABI('with-type-4-abi.eth')
220
+ expect(result).toBeTruthy()
221
+ if (result) {
222
+ expect(result.contentType).toBe(4)
223
+ expect(result.decoded).toBe(true)
224
+ expect(result.abi).toMatchObject(dummyABI)
225
+ }
226
+ })
227
+ it('should return unresolved URI for type 8 ABI', async () => {
228
+ const result = await ensInstance.getABI('with-type-8-abi.eth')
229
+ expect(result).toBeTruthy()
230
+ if (result) {
231
+ expect(result.contentType).toBe(8)
232
+ expect(result.decoded).toBe(false)
233
+ expect(result.abi).toBe('https://example.com')
234
+ }
235
+ })
236
+ it('should return undefined if unsupported contentType', async () => {
237
+ const result = await ensInstance.getABI('with-type-256-abi.eth')
238
+ expect(result).toBeUndefined()
239
+ })
240
+ })
79
241
  })