@ensdomains/ensjs 3.0.0-alpha.5 → 3.0.0-alpha.8
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/GqlManager.d.ts +3 -0
- package/dist/cjs/GqlManager.js +62 -2
- package/dist/cjs/functions/burnFuses.d.ts +1 -1
- package/dist/cjs/functions/burnFuses.js +1 -1
- package/dist/cjs/functions/createSubname.d.ts +1 -1
- package/dist/cjs/functions/createSubname.js +2 -2
- package/dist/cjs/functions/deleteSubname.d.ts +1 -1
- package/dist/cjs/functions/deleteSubname.js +1 -1
- package/dist/cjs/functions/getProfile.d.ts +4 -1
- package/dist/cjs/functions/getProfile.js +62 -18
- package/dist/cjs/functions/getRecords.d.ts +1 -0
- package/dist/cjs/functions/setName.d.ts +1 -1
- package/dist/cjs/functions/setName.js +2 -2
- package/dist/cjs/functions/setRecords.d.ts +1 -1
- package/dist/cjs/functions/setRecords.js +1 -1
- package/dist/cjs/functions/setResolver.d.ts +1 -1
- package/dist/cjs/functions/setResolver.js +2 -2
- package/dist/cjs/functions/transferName.d.ts +1 -1
- package/dist/cjs/functions/transferName.js +3 -3
- package/dist/cjs/functions/transferSubname.d.ts +1 -1
- package/dist/cjs/functions/transferSubname.js +2 -2
- package/dist/cjs/functions/unwrapName.d.ts +1 -1
- package/dist/cjs/functions/unwrapName.js +2 -2
- package/dist/cjs/functions/wrapName.d.ts +2 -2
- package/dist/cjs/functions/wrapName.js +3 -3
- package/dist/cjs/index.d.ts +34 -52
- package/dist/cjs/index.js +10 -6
- package/dist/cjs/utils/labels.js +4 -3
- package/dist/cjs/utils/normalise.d.ts +1 -1
- package/dist/cjs/utils/normalise.js +10 -3
- package/dist/cjs/utils/writeTx.d.ts +4 -0
- package/dist/cjs/utils/writeTx.js +3 -0
- package/dist/esm/GqlManager.d.ts +3 -0
- package/dist/esm/GqlManager.js +58 -2
- package/dist/esm/functions/burnFuses.d.ts +1 -1
- package/dist/esm/functions/burnFuses.js +1 -1
- package/dist/esm/functions/createSubname.d.ts +1 -1
- package/dist/esm/functions/createSubname.js +2 -2
- package/dist/esm/functions/deleteSubname.d.ts +1 -1
- package/dist/esm/functions/deleteSubname.js +1 -1
- package/dist/esm/functions/getProfile.d.ts +4 -1
- package/dist/esm/functions/getProfile.js +62 -18
- package/dist/esm/functions/getRecords.d.ts +1 -0
- package/dist/esm/functions/setName.d.ts +1 -1
- package/dist/esm/functions/setName.js +2 -2
- package/dist/esm/functions/setRecords.d.ts +1 -1
- package/dist/esm/functions/setRecords.js +1 -1
- package/dist/esm/functions/setResolver.d.ts +1 -1
- package/dist/esm/functions/setResolver.js +2 -2
- package/dist/esm/functions/transferName.d.ts +1 -1
- package/dist/esm/functions/transferName.js +3 -3
- package/dist/esm/functions/transferSubname.d.ts +1 -1
- package/dist/esm/functions/transferSubname.js +2 -2
- package/dist/esm/functions/unwrapName.d.ts +1 -1
- package/dist/esm/functions/unwrapName.js +2 -2
- package/dist/esm/functions/wrapName.d.ts +2 -2
- package/dist/esm/functions/wrapName.js +3 -3
- package/dist/esm/index.d.ts +34 -52
- package/dist/esm/index.js +10 -6
- package/dist/esm/utils/labels.js +4 -3
- package/dist/esm/utils/normalise.d.ts +1 -1
- package/dist/esm/utils/normalise.js +10 -3
- package/dist/esm/utils/writeTx.d.ts +4 -0
- package/dist/esm/utils/writeTx.js +1 -0
- package/package.json +7 -5
- package/src/GqlManager.test.ts +170 -0
- package/src/GqlManager.ts +67 -2
- package/src/functions/burnFuses.ts +1 -1
- package/src/functions/createSubname.ts +2 -2
- package/src/functions/deleteSubname.ts +1 -1
- package/src/functions/getProfile.test.ts +16 -1
- package/src/functions/getProfile.ts +82 -19
- package/src/functions/getRecords.ts +1 -0
- package/src/functions/setName.ts +5 -5
- package/src/functions/setRecords.ts +2 -2
- package/src/functions/setResolver.ts +5 -2
- package/src/functions/transferName.ts +5 -3
- package/src/functions/transferSubname.ts +11 -2
- package/src/functions/unwrapName.ts +10 -2
- package/src/functions/wrapName.ts +5 -8
- package/src/index.ts +37 -12
- package/src/tests/populateTransaction.test.ts +40 -0
- package/src/tests/signerInjection.test.ts +7 -3
- package/src/utils/labels.ts +5 -3
- package/src/utils/normalise.ts +10 -4
- package/src/utils/writeTx.ts +6 -0
|
@@ -3,6 +3,7 @@ import { ethers } from 'ethers'
|
|
|
3
3
|
import { ENSArgs } from '..'
|
|
4
4
|
import { decodeContenthash, DecodedContentHash } from '../utils/contentHash'
|
|
5
5
|
import { hexEncodeName } from '../utils/hexEncodedName'
|
|
6
|
+
import { namehash } from '../utils/normalise'
|
|
6
7
|
import { parseInputType } from '../utils/validation'
|
|
7
8
|
|
|
8
9
|
type InternalProfileOptions = {
|
|
@@ -136,7 +137,8 @@ const getDataForName = async (
|
|
|
136
137
|
>,
|
|
137
138
|
name: string,
|
|
138
139
|
options: InternalProfileOptions,
|
|
139
|
-
fallbackResolver
|
|
140
|
+
fallbackResolver?: string,
|
|
141
|
+
specificResolver?: string,
|
|
140
142
|
) => {
|
|
141
143
|
const universalResolver = await contracts?.getUniversalResolver()
|
|
142
144
|
|
|
@@ -149,7 +151,17 @@ const getDataForName = async (
|
|
|
149
151
|
let resolvedData: any
|
|
150
152
|
let useFallbackResolver = false
|
|
151
153
|
try {
|
|
152
|
-
|
|
154
|
+
if (specificResolver) {
|
|
155
|
+
const publicResolver = await contracts?.getPublicResolver(
|
|
156
|
+
undefined,
|
|
157
|
+
specificResolver,
|
|
158
|
+
)
|
|
159
|
+
resolvedData = await publicResolver?.callStatic.multicall(
|
|
160
|
+
calls.map((x) => x.data),
|
|
161
|
+
)
|
|
162
|
+
} else {
|
|
163
|
+
resolvedData = await universalResolver?.resolve(hexEncodeName(name), data)
|
|
164
|
+
}
|
|
153
165
|
} catch {
|
|
154
166
|
useFallbackResolver = true
|
|
155
167
|
}
|
|
@@ -158,15 +170,19 @@ const getDataForName = async (
|
|
|
158
170
|
let recordData: any
|
|
159
171
|
|
|
160
172
|
if (useFallbackResolver) {
|
|
161
|
-
resolverAddress = fallbackResolver
|
|
173
|
+
resolverAddress = specificResolver || fallbackResolver!
|
|
162
174
|
recordData = await fetchWithoutResolverMulticall(
|
|
163
175
|
{ multicallWrapper },
|
|
164
176
|
calls,
|
|
165
177
|
resolverAddress,
|
|
166
178
|
)
|
|
167
179
|
} else {
|
|
168
|
-
resolverAddress = resolvedData['1']
|
|
169
|
-
|
|
180
|
+
resolverAddress = specificResolver || resolvedData['1']
|
|
181
|
+
if (specificResolver) {
|
|
182
|
+
recordData = resolvedData
|
|
183
|
+
} else {
|
|
184
|
+
;[recordData] = await resolverMulticallWrapper.decode(resolvedData['0'])
|
|
185
|
+
}
|
|
170
186
|
}
|
|
171
187
|
|
|
172
188
|
const matchAddress = recordData[calls.findIndex((x) => x.key === '60')]
|
|
@@ -298,10 +314,11 @@ const graphFetch = async (
|
|
|
298
314
|
{ gqlInstance }: ENSArgs<'gqlInstance'>,
|
|
299
315
|
name: string,
|
|
300
316
|
wantedRecords?: ProfileOptions,
|
|
317
|
+
resolverAddress?: string,
|
|
301
318
|
) => {
|
|
302
319
|
const query = gqlInstance.gql`
|
|
303
|
-
query getRecords($
|
|
304
|
-
|
|
320
|
+
query getRecords($id: String!) {
|
|
321
|
+
domain(id: $id) {
|
|
305
322
|
isMigrated
|
|
306
323
|
createdAt
|
|
307
324
|
resolver {
|
|
@@ -317,13 +334,44 @@ const graphFetch = async (
|
|
|
317
334
|
}
|
|
318
335
|
`
|
|
319
336
|
|
|
337
|
+
const customResolverQuery = gqlInstance.gql`
|
|
338
|
+
query getRecordsWithCustomResolver($id: String!, $resolverId: String!) {
|
|
339
|
+
domain(id: $id) {
|
|
340
|
+
isMigrated
|
|
341
|
+
createdAt
|
|
342
|
+
}
|
|
343
|
+
resolver(id: $resolverId) {
|
|
344
|
+
texts
|
|
345
|
+
coinTypes
|
|
346
|
+
contentHash
|
|
347
|
+
addr {
|
|
348
|
+
id
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
`
|
|
353
|
+
|
|
320
354
|
const client = gqlInstance.client
|
|
321
355
|
|
|
322
|
-
const
|
|
356
|
+
const id = namehash(name)
|
|
323
357
|
|
|
324
|
-
|
|
358
|
+
let domain: any
|
|
359
|
+
let resolverResponse: any
|
|
325
360
|
|
|
326
|
-
|
|
361
|
+
if (!resolverAddress) {
|
|
362
|
+
;({ domain } = await client.request(query, { id }))
|
|
363
|
+
resolverResponse = domain?.resolver
|
|
364
|
+
} else {
|
|
365
|
+
const resolverId = `${resolverAddress}-${id}`
|
|
366
|
+
;({ resolver: resolverResponse, domain } = await client.request(
|
|
367
|
+
customResolverQuery,
|
|
368
|
+
{ id, resolverId },
|
|
369
|
+
))
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (!domain) return
|
|
373
|
+
|
|
374
|
+
const { isMigrated, createdAt } = domain
|
|
327
375
|
|
|
328
376
|
let returnedRecords: ProfileResponse = {}
|
|
329
377
|
|
|
@@ -333,7 +381,7 @@ const graphFetch = async (
|
|
|
333
381
|
return {
|
|
334
382
|
isMigrated,
|
|
335
383
|
createdAt,
|
|
336
|
-
graphResolverAddress: resolverResponse.address,
|
|
384
|
+
graphResolverAddress: resolverResponse.address || resolverAddress,
|
|
337
385
|
}
|
|
338
386
|
|
|
339
387
|
Object.keys(wantedRecords).forEach((key: string) => {
|
|
@@ -351,7 +399,7 @@ const graphFetch = async (
|
|
|
351
399
|
...returnedRecords,
|
|
352
400
|
isMigrated,
|
|
353
401
|
createdAt,
|
|
354
|
-
graphResolverAddress: resolverResponse.address,
|
|
402
|
+
graphResolverAddress: resolverResponse.address || resolverAddress,
|
|
355
403
|
}
|
|
356
404
|
}
|
|
357
405
|
|
|
@@ -361,6 +409,10 @@ type ProfileOptions = {
|
|
|
361
409
|
coinTypes?: boolean | string[]
|
|
362
410
|
}
|
|
363
411
|
|
|
412
|
+
type InputProfileOptions = ProfileOptions & {
|
|
413
|
+
resolverAddress?: string
|
|
414
|
+
}
|
|
415
|
+
|
|
364
416
|
const getProfileFromName = async (
|
|
365
417
|
{
|
|
366
418
|
contracts,
|
|
@@ -380,13 +432,23 @@ const getProfileFromName = async (
|
|
|
380
432
|
| 'multicallWrapper'
|
|
381
433
|
>,
|
|
382
434
|
name: string,
|
|
383
|
-
options?:
|
|
435
|
+
options?: InputProfileOptions,
|
|
384
436
|
) => {
|
|
437
|
+
const { resolverAddress, ..._options } = options || {}
|
|
438
|
+
const optsLength = Object.keys(_options).length
|
|
385
439
|
const usingOptions =
|
|
386
|
-
!
|
|
387
|
-
?
|
|
440
|
+
!optsLength || _options?.texts === true || _options?.coinTypes === true
|
|
441
|
+
? optsLength
|
|
442
|
+
? _options
|
|
443
|
+
: { contentHash: true, texts: true, coinTypes: true }
|
|
388
444
|
: undefined
|
|
389
|
-
|
|
445
|
+
|
|
446
|
+
const graphResult = await graphFetch(
|
|
447
|
+
{ gqlInstance },
|
|
448
|
+
name,
|
|
449
|
+
usingOptions,
|
|
450
|
+
resolverAddress,
|
|
451
|
+
)
|
|
390
452
|
if (!graphResult) return
|
|
391
453
|
const {
|
|
392
454
|
isMigrated,
|
|
@@ -398,7 +460,7 @@ const getProfileFromName = async (
|
|
|
398
460
|
createdAt: string
|
|
399
461
|
graphResolverAddress?: string
|
|
400
462
|
} & InternalProfileOptions = graphResult
|
|
401
|
-
if (!graphResolverAddress)
|
|
463
|
+
if (!graphResolverAddress && !options?.resolverAddress)
|
|
402
464
|
return { isMigrated, createdAt, message: "Name doesn't have a resolver" }
|
|
403
465
|
const result = await getDataForName(
|
|
404
466
|
{
|
|
@@ -412,6 +474,7 @@ const getProfileFromName = async (
|
|
|
412
474
|
name,
|
|
413
475
|
usingOptions ? wantedRecords : (options as InternalProfileOptions),
|
|
414
476
|
graphResolverAddress,
|
|
477
|
+
options?.resolverAddress!,
|
|
415
478
|
)
|
|
416
479
|
if (!result)
|
|
417
480
|
return { isMigrated, createdAt, message: "Records fetch didn't complete" }
|
|
@@ -439,7 +502,7 @@ const getProfileFromAddress = async (
|
|
|
439
502
|
| 'multicallWrapper'
|
|
440
503
|
>,
|
|
441
504
|
address: string,
|
|
442
|
-
options?:
|
|
505
|
+
options?: InputProfileOptions,
|
|
443
506
|
) => {
|
|
444
507
|
let name
|
|
445
508
|
try {
|
|
@@ -492,7 +555,7 @@ export default async function (
|
|
|
492
555
|
| 'multicallWrapper'
|
|
493
556
|
>,
|
|
494
557
|
nameOrAddress: string,
|
|
495
|
-
options?:
|
|
558
|
+
options?: InputProfileOptions,
|
|
496
559
|
): Promise<ResolvedProfile | undefined> {
|
|
497
560
|
if (options && options.coinTypes && typeof options.coinTypes !== 'boolean') {
|
|
498
561
|
options.coinTypes = options.coinTypes.map((coin: string) => {
|
package/src/functions/setName.ts
CHANGED
|
@@ -15,18 +15,18 @@ export default async function (
|
|
|
15
15
|
|
|
16
16
|
const reverseRegistrar = (await contracts?.getReverseRegistrar())?.connect(
|
|
17
17
|
signer,
|
|
18
|
-
)
|
|
18
|
+
)!
|
|
19
19
|
|
|
20
20
|
if (address) {
|
|
21
|
-
const publicResolver = await contracts?.getPublicResolver()
|
|
21
|
+
const publicResolver = await contracts?.getPublicResolver()!
|
|
22
22
|
|
|
23
|
-
return reverseRegistrar
|
|
23
|
+
return reverseRegistrar.populateTransaction.setNameForAddr(
|
|
24
24
|
address,
|
|
25
25
|
signerAddress,
|
|
26
|
-
resolver || publicResolver
|
|
26
|
+
resolver || publicResolver.address,
|
|
27
27
|
name,
|
|
28
28
|
)
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
return reverseRegistrar
|
|
31
|
+
return reverseRegistrar.populateTransaction.setName(name)
|
|
32
32
|
}
|
|
@@ -35,10 +35,10 @@ export default async function (
|
|
|
35
35
|
|
|
36
36
|
const resolver = (
|
|
37
37
|
await contracts?.getPublicResolver(provider, resolverToUse)
|
|
38
|
-
)?.connect(signer)
|
|
38
|
+
)?.connect(signer)!
|
|
39
39
|
const hash = namehash(name)
|
|
40
40
|
|
|
41
41
|
const calls: string[] = generateRecordCallArray(hash, records, resolver!)
|
|
42
42
|
|
|
43
|
-
return resolver
|
|
43
|
+
return resolver.populateTransaction.multicall(calls)
|
|
44
44
|
}
|
|
@@ -19,11 +19,14 @@ export default async function (
|
|
|
19
19
|
switch (contract) {
|
|
20
20
|
case 'registry': {
|
|
21
21
|
const registry = (await contracts?.getRegistry())!.connect(signer)
|
|
22
|
-
return registry.setResolver(namehash(name), resolver)
|
|
22
|
+
return registry.populateTransaction.setResolver(namehash(name), resolver)
|
|
23
23
|
}
|
|
24
24
|
case 'nameWrapper': {
|
|
25
25
|
const nameWrapper = (await contracts?.getNameWrapper())!.connect(signer)
|
|
26
|
-
return nameWrapper.setResolver(
|
|
26
|
+
return nameWrapper.populateTransaction.setResolver(
|
|
27
|
+
namehash(name),
|
|
28
|
+
resolver,
|
|
29
|
+
)
|
|
27
30
|
}
|
|
28
31
|
default: {
|
|
29
32
|
throw new Error(`Unknown contract: ${contract}`)
|
|
@@ -18,7 +18,7 @@ export default async function (
|
|
|
18
18
|
switch (contract) {
|
|
19
19
|
case 'registry': {
|
|
20
20
|
const registry = (await contracts?.getRegistry())!.connect(signer)
|
|
21
|
-
return registry.setOwner(namehash(name), newOwner)
|
|
21
|
+
return registry.populateTransaction.setOwner(namehash(name), newOwner)
|
|
22
22
|
}
|
|
23
23
|
case 'baseRegistrar': {
|
|
24
24
|
const baseRegistrar = (await contracts?.getBaseRegistrar())!.connect(
|
|
@@ -28,7 +28,9 @@ export default async function (
|
|
|
28
28
|
if (labels.length > 2 || labels[labels.length - 1] !== 'eth') {
|
|
29
29
|
throw new Error('Invalid name for baseRegistrar')
|
|
30
30
|
}
|
|
31
|
-
return baseRegistrar[
|
|
31
|
+
return baseRegistrar.populateTransaction[
|
|
32
|
+
'safeTransferFrom(address,address,uint256)'
|
|
33
|
+
](
|
|
32
34
|
address,
|
|
33
35
|
newOwner,
|
|
34
36
|
ethers.utils.solidityKeccak256(['string'], [labels[0]]),
|
|
@@ -36,7 +38,7 @@ export default async function (
|
|
|
36
38
|
}
|
|
37
39
|
case 'nameWrapper': {
|
|
38
40
|
const nameWrapper = (await contracts?.getNameWrapper())!.connect(signer)
|
|
39
|
-
return nameWrapper.safeTransferFrom(
|
|
41
|
+
return nameWrapper.populateTransaction.safeTransferFrom(
|
|
40
42
|
address,
|
|
41
43
|
newOwner,
|
|
42
44
|
namehash(name),
|
|
@@ -22,12 +22,21 @@ export default async function (
|
|
|
22
22
|
case 'registry': {
|
|
23
23
|
const registry = (await contracts?.getRegistry()!).connect(signer)
|
|
24
24
|
|
|
25
|
-
return registry.setSubnodeOwner(
|
|
25
|
+
return registry.populateTransaction.setSubnodeOwner(
|
|
26
|
+
parentNodehash,
|
|
27
|
+
labelhash,
|
|
28
|
+
address,
|
|
29
|
+
)
|
|
26
30
|
}
|
|
27
31
|
case 'nameWrapper': {
|
|
28
32
|
const nameWrapper = (await contracts?.getNameWrapper()!).connect(signer)
|
|
29
33
|
|
|
30
|
-
return nameWrapper.setSubnodeOwner(
|
|
34
|
+
return nameWrapper.populateTransaction.setSubnodeOwner(
|
|
35
|
+
parentNodehash,
|
|
36
|
+
label,
|
|
37
|
+
address,
|
|
38
|
+
'0',
|
|
39
|
+
)
|
|
31
40
|
}
|
|
32
41
|
default: {
|
|
33
42
|
throw new Error(`Unknown contract: ${contract}`)
|
|
@@ -24,12 +24,20 @@ export default async function (
|
|
|
24
24
|
throw new Error('newRegistrant must be specified for .eth names')
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
return nameWrapper.unwrapETH2LD(
|
|
27
|
+
return nameWrapper.populateTransaction.unwrapETH2LD(
|
|
28
|
+
labelhash,
|
|
29
|
+
newRegistrant,
|
|
30
|
+
newController,
|
|
31
|
+
)
|
|
28
32
|
} else {
|
|
29
33
|
if (newRegistrant) {
|
|
30
34
|
throw new Error('newRegistrant can only be specified for .eth names')
|
|
31
35
|
}
|
|
32
36
|
|
|
33
|
-
return nameWrapper.unwrap(
|
|
37
|
+
return nameWrapper.populateTransaction.unwrap(
|
|
38
|
+
parentNodehash,
|
|
39
|
+
labelhash,
|
|
40
|
+
newController,
|
|
41
|
+
)
|
|
34
42
|
}
|
|
35
43
|
}
|
|
@@ -23,12 +23,9 @@ async function wrapETH(
|
|
|
23
23
|
[labels[0], wrappedOwner, '0x0', decodedFuses, resolverAddress],
|
|
24
24
|
)
|
|
25
25
|
|
|
26
|
-
return baseRegistrar[
|
|
27
|
-
address,
|
|
28
|
-
|
|
29
|
-
labelhash,
|
|
30
|
-
data,
|
|
31
|
-
)
|
|
26
|
+
return baseRegistrar.populateTransaction[
|
|
27
|
+
'safeTransferFrom(address,address,uint256,bytes)'
|
|
28
|
+
](address, nameWrapper.address, labelhash, data)
|
|
32
29
|
}
|
|
33
30
|
|
|
34
31
|
async function wrapOther(
|
|
@@ -54,7 +51,7 @@ async function wrapOther(
|
|
|
54
51
|
)
|
|
55
52
|
}
|
|
56
53
|
|
|
57
|
-
return nameWrapper.wrap(
|
|
54
|
+
return nameWrapper.populateTransaction.wrap(
|
|
58
55
|
hexEncodeName(name),
|
|
59
56
|
wrappedOwner,
|
|
60
57
|
decodedFuses,
|
|
@@ -63,7 +60,7 @@ async function wrapOther(
|
|
|
63
60
|
}
|
|
64
61
|
|
|
65
62
|
export default async function (
|
|
66
|
-
{ contracts, signer }: ENSArgs<'contracts' | 'signer'>,
|
|
63
|
+
{ contracts, signer, populate }: ENSArgs<'contracts' | 'signer' | 'populate'>,
|
|
67
64
|
name: string,
|
|
68
65
|
{
|
|
69
66
|
wrappedOwner,
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { JsonRpcSigner } from '@ethersproject/providers'
|
|
2
|
+
import { ContractTransaction, ethers, PopulatedTransaction } from 'ethers'
|
|
2
3
|
import ContractManager from './contracts'
|
|
3
4
|
import { getContractAddress as _getContractAddress } from './contracts/getContractAddress'
|
|
4
5
|
import { SupportedNetworkId } from './contracts/types'
|
|
@@ -44,6 +45,7 @@ import type unwrapName from './functions/unwrapName'
|
|
|
44
45
|
import type wrapName from './functions/wrapName'
|
|
45
46
|
import GqlManager from './GqlManager'
|
|
46
47
|
import singleCall from './utils/singleCall'
|
|
48
|
+
import writeTx from './utils/writeTx'
|
|
47
49
|
|
|
48
50
|
type ENSOptions = {
|
|
49
51
|
graphURI?: string | null
|
|
@@ -53,7 +55,7 @@ type ENSOptions = {
|
|
|
53
55
|
export type InternalENS = {
|
|
54
56
|
options?: ENSOptions
|
|
55
57
|
provider?: ethers.providers.Provider
|
|
56
|
-
signer:
|
|
58
|
+
signer: JsonRpcSigner
|
|
57
59
|
graphURI?: string | null
|
|
58
60
|
} & ENS
|
|
59
61
|
|
|
@@ -79,10 +81,10 @@ type FunctionDeps<F> = Extract<keyof FirstArg<F>, string>[]
|
|
|
79
81
|
|
|
80
82
|
type WriteOptions = {
|
|
81
83
|
addressOrIndex?: string | number
|
|
82
|
-
signer?:
|
|
84
|
+
signer?: JsonRpcSigner
|
|
83
85
|
}
|
|
84
86
|
|
|
85
|
-
type
|
|
87
|
+
type OptionalWriteOptions<F> = F extends (
|
|
86
88
|
x: any,
|
|
87
89
|
arg_0: infer Z,
|
|
88
90
|
options?: infer P,
|
|
@@ -92,6 +94,13 @@ type WriteFunction<F> = F extends (
|
|
|
92
94
|
? (name: Z, options: P & WriteOptions) => R
|
|
93
95
|
: never
|
|
94
96
|
|
|
97
|
+
interface WriteFunction<F extends (...args: any) => any> extends Function {
|
|
98
|
+
(...args: Parameters<OptionalWriteOptions<F>>): Promise<ContractTransaction>
|
|
99
|
+
populateTransaction: (
|
|
100
|
+
...args: Parameters<OptionalWriteOptions<F>>
|
|
101
|
+
) => Promise<PopulatedTransaction>
|
|
102
|
+
}
|
|
103
|
+
|
|
95
104
|
const graphURIEndpoints: Record<string, string> = {
|
|
96
105
|
1: 'https://api.thegraph.com/subgraphs/name/ensdomains/ens',
|
|
97
106
|
3: 'https://api.thegraph.com/subgraphs/name/ensdomains/ensropsten',
|
|
@@ -188,7 +197,13 @@ export class ENS {
|
|
|
188
197
|
path: string,
|
|
189
198
|
dependencies: FunctionDeps<F>,
|
|
190
199
|
exportName: string = 'default',
|
|
191
|
-
subFunc?:
|
|
200
|
+
subFunc?:
|
|
201
|
+
| 'raw'
|
|
202
|
+
| 'decode'
|
|
203
|
+
| 'combine'
|
|
204
|
+
| 'batch'
|
|
205
|
+
| 'write'
|
|
206
|
+
| 'populateTransaction',
|
|
192
207
|
passthrough?: RawFunction,
|
|
193
208
|
): Function => {
|
|
194
209
|
// if batch is specified, create batch func
|
|
@@ -202,35 +217,38 @@ export class ENS {
|
|
|
202
217
|
await thisRef.checkInitialProvider()
|
|
203
218
|
// import the module dynamically
|
|
204
219
|
const mod = await import(
|
|
205
|
-
/* webpackMode: "lazy", webpackChunkName: "[request]", webpackPreload: true */
|
|
220
|
+
/* webpackMode: "lazy", webpackChunkName: "[request]", webpackPreload: true, webpackExclude: /.*\.ts$/ */
|
|
206
221
|
`./functions/${path}`
|
|
207
222
|
)
|
|
208
223
|
|
|
209
224
|
// if combine isn't specified, run normally
|
|
210
225
|
// otherwise, create a function from the raw and decode functions
|
|
211
226
|
if (subFunc !== 'combine') {
|
|
227
|
+
const writeable =
|
|
228
|
+
subFunc === 'write' || subFunc === 'populateTransaction'
|
|
212
229
|
// get the function to call
|
|
213
230
|
const func =
|
|
214
|
-
subFunc && subFunc
|
|
215
|
-
? mod[exportName][subFunc]
|
|
216
|
-
: mod[exportName]
|
|
231
|
+
subFunc && !writeable ? mod[exportName][subFunc] : mod[exportName]
|
|
217
232
|
// get the dependencies to forward to the function as the first arg
|
|
218
233
|
let dependenciesToForward =
|
|
219
234
|
thisRef.forwardDependenciesFromArray<F>(dependencies)
|
|
220
235
|
|
|
221
236
|
// if func is write func, inject signer into dependencies
|
|
222
|
-
if (
|
|
237
|
+
if (writeable) {
|
|
223
238
|
const options = (args[1] || {}) as WriteOptions
|
|
224
239
|
const signer =
|
|
225
240
|
options.signer ||
|
|
226
241
|
thisRef.provider?.getSigner(options.addressOrIndex)
|
|
242
|
+
const populate = subFunc === 'populateTransaction'
|
|
227
243
|
if (!signer) {
|
|
228
244
|
throw new Error('No signer specified')
|
|
229
245
|
}
|
|
230
246
|
delete options.addressOrIndex
|
|
231
247
|
delete options.signer
|
|
232
248
|
dependenciesToForward = { ...dependenciesToForward, signer }
|
|
233
|
-
return func(dependenciesToForward, args[0], options)
|
|
249
|
+
return func(dependenciesToForward, args[0], options).then(
|
|
250
|
+
writeTx(signer, populate),
|
|
251
|
+
)
|
|
234
252
|
}
|
|
235
253
|
|
|
236
254
|
// return the function with the dependencies forwarded
|
|
@@ -272,6 +290,13 @@ export class ENS {
|
|
|
272
290
|
'batch',
|
|
273
291
|
{ raw: mainFunc.raw as any, decode: mainFunc.decode as any },
|
|
274
292
|
)
|
|
293
|
+
} else if (subFunc === 'write') {
|
|
294
|
+
mainFunc.populateTransaction = this.importGenerator<F>(
|
|
295
|
+
path,
|
|
296
|
+
dependencies,
|
|
297
|
+
exportName,
|
|
298
|
+
'populateTransaction',
|
|
299
|
+
)
|
|
275
300
|
}
|
|
276
301
|
|
|
277
302
|
return mainFunc as Function
|
|
@@ -298,7 +323,7 @@ export class ENS {
|
|
|
298
323
|
* @param {string} exportName - The export name of the target function
|
|
299
324
|
* @returns {OmitFirstArg} - The generated wrapped function
|
|
300
325
|
*/
|
|
301
|
-
private generateWriteFunction = <F>(
|
|
326
|
+
private generateWriteFunction = <F extends (...args: any) => any>(
|
|
302
327
|
path: string,
|
|
303
328
|
dependencies: FunctionDeps<F>,
|
|
304
329
|
exportName: string = 'default',
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ethers } from 'ethers'
|
|
2
|
+
import { ENS } from '..'
|
|
3
|
+
import setup from '../tests/setup'
|
|
4
|
+
|
|
5
|
+
let ENSInstance: ENS
|
|
6
|
+
let revert: Awaited<ReturnType<typeof setup>>['revert']
|
|
7
|
+
let provider: ethers.providers.JsonRpcProvider
|
|
8
|
+
let accounts: string[]
|
|
9
|
+
|
|
10
|
+
beforeAll(async () => {
|
|
11
|
+
;({ ENSInstance, revert, provider } = await setup())
|
|
12
|
+
accounts = await provider.listAccounts()
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
afterAll(async () => {
|
|
16
|
+
await revert()
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
jest.setTimeout(20000)
|
|
20
|
+
|
|
21
|
+
describe('populateTransaction', () => {
|
|
22
|
+
beforeEach(async () => {
|
|
23
|
+
await revert()
|
|
24
|
+
})
|
|
25
|
+
it('should return a transaction successfully', async () => {
|
|
26
|
+
const tx = await ENSInstance.setName('fleek.eth')
|
|
27
|
+
expect(tx).toBeTruthy()
|
|
28
|
+
if (tx) {
|
|
29
|
+
await tx.wait()
|
|
30
|
+
expect(tx.hash).toBeTruthy()
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
it('should return a populated transaction successfully', async () => {
|
|
34
|
+
const tx = await ENSInstance.setName.populateTransaction('fleek.eth')
|
|
35
|
+
expect(tx).toBeTruthy()
|
|
36
|
+
if (tx) {
|
|
37
|
+
expect(tx).not.toHaveProperty('hash')
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
})
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ethers } from 'ethers'
|
|
1
|
+
import { ContractTransaction, ethers } from 'ethers'
|
|
2
2
|
import { ENS } from '..'
|
|
3
3
|
import setup from '../tests/setup'
|
|
4
4
|
|
|
@@ -24,7 +24,9 @@ describe('Signer Injection', () => {
|
|
|
24
24
|
})
|
|
25
25
|
it('should return a transaction successfully for a custom signer', async () => {
|
|
26
26
|
const signer = provider.getSigner(accounts[3])
|
|
27
|
-
const tx = await ENSInstance.setName('fleek.eth', {
|
|
27
|
+
const tx = await ENSInstance.setName('fleek.eth', {
|
|
28
|
+
signer,
|
|
29
|
+
})
|
|
28
30
|
expect(tx).toBeTruthy()
|
|
29
31
|
if (tx) {
|
|
30
32
|
await tx.wait()
|
|
@@ -32,7 +34,9 @@ describe('Signer Injection', () => {
|
|
|
32
34
|
}
|
|
33
35
|
})
|
|
34
36
|
it('should return a transaction succesfully for a custom signer index', async () => {
|
|
35
|
-
const tx = await ENSInstance.setName('fleek.eth', {
|
|
37
|
+
const tx = (await ENSInstance.setName('fleek.eth', {
|
|
38
|
+
addressOrIndex: 3,
|
|
39
|
+
})) as ContractTransaction
|
|
36
40
|
expect(tx).toBeTruthy()
|
|
37
41
|
if (tx) {
|
|
38
42
|
await tx.wait()
|
package/src/utils/labels.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { solidityKeccak256 } from 'ethers/lib/utils'
|
|
2
2
|
import { truncateFormat } from './format'
|
|
3
3
|
|
|
4
|
+
const hasLocalStorage = typeof localStorage !== 'undefined'
|
|
5
|
+
|
|
4
6
|
export const labelhash = (input: string) =>
|
|
5
7
|
solidityKeccak256(['string'], [input])
|
|
6
8
|
|
|
@@ -37,13 +39,13 @@ export function isEncodedLabelhash(hash: string) {
|
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
function getLabels() {
|
|
40
|
-
return
|
|
42
|
+
return hasLocalStorage
|
|
41
43
|
? JSON.parse(localStorage.getItem('ensjs:labels') as string) || {}
|
|
42
44
|
: {}
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
function _saveLabel(hash: string, label: any) {
|
|
46
|
-
if (!
|
|
48
|
+
if (!hasLocalStorage) return hash
|
|
47
49
|
const labels = getLabels()
|
|
48
50
|
localStorage.setItem(
|
|
49
51
|
'ensjs:labels',
|
|
@@ -106,7 +108,7 @@ export function decryptName(name: string) {
|
|
|
106
108
|
export const truncateUndecryptedName = (name: string) => truncateFormat(name)
|
|
107
109
|
|
|
108
110
|
export function checkLocalStorageSize() {
|
|
109
|
-
if (!
|
|
111
|
+
if (!hasLocalStorage) return 'Empty (0 KB)'
|
|
110
112
|
let allStrings = ''
|
|
111
113
|
for (const key in window.localStorage) {
|
|
112
114
|
if (Object.prototype.hasOwnProperty.call(window.localStorage, key)) {
|
package/src/utils/normalise.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { concat, hexlify, keccak256, toUtf8Bytes } from 'ethers/lib/utils'
|
|
2
2
|
import uts46 from 'idna-uts46-hx/uts46bundle.js'
|
|
3
|
+
import { decodeLabelhash, isEncodedLabelhash } from './labels'
|
|
3
4
|
|
|
4
5
|
const zeros = new Uint8Array(32)
|
|
5
6
|
zeros.fill(0)
|
|
@@ -7,16 +8,21 @@ zeros.fill(0)
|
|
|
7
8
|
export const normalise = (name: string) =>
|
|
8
9
|
name ? uts46.toUnicode(name, { useStd3ASCII: true }) : name
|
|
9
10
|
|
|
10
|
-
export const namehash = (
|
|
11
|
+
export const namehash = (name: string): string => {
|
|
11
12
|
let result: string | Uint8Array = zeros
|
|
12
13
|
|
|
13
|
-
const name = normalise(inputName)
|
|
14
|
-
|
|
15
14
|
if (name) {
|
|
16
15
|
const labels = name.split('.')
|
|
17
16
|
|
|
18
17
|
for (var i = labels.length - 1; i >= 0; i--) {
|
|
19
|
-
|
|
18
|
+
let labelSha: string
|
|
19
|
+
if (isEncodedLabelhash(labels[i])) {
|
|
20
|
+
labelSha = decodeLabelhash(labels[i])
|
|
21
|
+
} else {
|
|
22
|
+
const normalised = normalise(labels[i])
|
|
23
|
+
labelSha = keccak256(toUtf8Bytes(normalised))
|
|
24
|
+
}
|
|
25
|
+
|
|
20
26
|
result = keccak256(concat([result, labelSha]))
|
|
21
27
|
}
|
|
22
28
|
} else {
|