@ensdomains/ensjs 3.0.0-alpha.40 → 3.0.0-alpha.41
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/functions/getSpecificRecord.js +83 -0
- package/dist/cjs/index.js +10 -0
- package/dist/cjs/utils/recordHelpers.js +54 -0
- package/dist/esm/functions/getSpecificRecord.mjs +78 -1
- package/dist/esm/index.mjs +10 -0
- package/dist/esm/utils/recordHelpers.mjs +48 -0
- package/dist/types/functions/burnFuses.d.ts +1 -1
- package/dist/types/functions/commitName.d.ts +6 -6
- package/dist/types/functions/createSubname.d.ts +1 -1
- package/dist/types/functions/deleteSubname.d.ts +1 -1
- package/dist/types/functions/getSpecificRecord.d.ts +22 -0
- package/dist/types/functions/importDNSSECName.d.ts +1 -1
- package/dist/types/functions/registerName.d.ts +1 -1
- package/dist/types/functions/renewNames.d.ts +2 -2
- package/dist/types/functions/setName.d.ts +1 -1
- package/dist/types/functions/setRecord.d.ts +5 -1
- package/dist/types/functions/setRecords.d.ts +1 -1
- package/dist/types/functions/setResolver.d.ts +1 -1
- package/dist/types/functions/transferController.d.ts +1 -1
- package/dist/types/functions/transferName.d.ts +1 -1
- package/dist/types/functions/transferSubname.d.ts +1 -1
- package/dist/types/functions/types.d.ts +3 -1
- package/dist/types/functions/unwrapName.d.ts +1 -1
- package/dist/types/functions/wrapName.d.ts +1 -1
- package/dist/types/index.d.ts +25 -3
- package/dist/types/utils/recordHelpers.d.ts +14 -2
- package/dist/types/utils/writeTx.d.ts +5 -5
- package/package.json +4 -1
- package/src/@types/pako.d.ts +188 -0
- package/src/contracts/bulkRenewal.ts +1 -1
- package/src/functions/getSpecificRecord.test.ts +162 -0
- package/src/functions/getSpecificRecord.ts +95 -1
- package/src/functions/setRecord.test.ts +147 -1
- package/src/functions/setRecord.ts +6 -1
- package/src/functions/types.ts +4 -0
- package/src/index.ts +12 -0
- package/src/utils/recordHelpers.ts +63 -1
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
import { decodeFirst } from 'cbor'
|
|
2
|
+
import { BigNumber } from 'ethers'
|
|
3
|
+
import { arrayify, toUtf8String } from 'ethers/lib/utils'
|
|
4
|
+
import { inflate } from 'pako'
|
|
1
5
|
import { ENS } from '..'
|
|
2
6
|
import setup from '../tests/setup'
|
|
3
7
|
import { decodeContenthash } from '../utils/contentHash'
|
|
4
8
|
import { hexEncodeName } from '../utils/hexEncodedName'
|
|
5
9
|
import { namehash } from '../utils/normalise'
|
|
10
|
+
import { generateABIInput } from '../utils/recordHelpers'
|
|
6
11
|
|
|
7
12
|
let ensInstance: ENS
|
|
8
13
|
let revert: Awaited<ReturnType<typeof setup>>['revert']
|
|
@@ -11,10 +16,30 @@ beforeAll(async () => {
|
|
|
11
16
|
;({ ensInstance, revert } = await setup())
|
|
12
17
|
})
|
|
13
18
|
|
|
14
|
-
|
|
19
|
+
afterEach(async () => {
|
|
15
20
|
await revert()
|
|
16
21
|
})
|
|
17
22
|
|
|
23
|
+
const dummyABI = [
|
|
24
|
+
{
|
|
25
|
+
type: 'function',
|
|
26
|
+
name: 'supportsInterface',
|
|
27
|
+
constant: true,
|
|
28
|
+
stateMutability: 'view',
|
|
29
|
+
payable: false,
|
|
30
|
+
inputs: [
|
|
31
|
+
{
|
|
32
|
+
type: 'bytes4',
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
outputs: [
|
|
36
|
+
{
|
|
37
|
+
type: 'bool',
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
]
|
|
42
|
+
|
|
18
43
|
describe('setRecord', () => {
|
|
19
44
|
it('should allow a text record set', async () => {
|
|
20
45
|
const tx = await ensInstance.setRecord('test123.eth', {
|
|
@@ -100,4 +125,125 @@ describe('setRecord', () => {
|
|
|
100
125
|
)
|
|
101
126
|
expect(content.protocolType).toBe('ipns')
|
|
102
127
|
})
|
|
128
|
+
it('should allow an abi record to be set from an object', async () => {
|
|
129
|
+
const tx = await ensInstance.setRecord('test123.eth', {
|
|
130
|
+
type: 'abi',
|
|
131
|
+
record: {
|
|
132
|
+
data: dummyABI,
|
|
133
|
+
},
|
|
134
|
+
addressOrIndex: 1,
|
|
135
|
+
})
|
|
136
|
+
expect(tx).toBeTruthy()
|
|
137
|
+
await tx.wait()
|
|
138
|
+
|
|
139
|
+
const universalResolver =
|
|
140
|
+
await ensInstance.contracts!.getUniversalResolver()!
|
|
141
|
+
const publicResolver = await ensInstance.contracts!.getPublicResolver()!
|
|
142
|
+
const encodedRes = await universalResolver['resolve(bytes,bytes)'](
|
|
143
|
+
hexEncodeName('test123.eth'),
|
|
144
|
+
publicResolver.interface.encodeFunctionData('ABI', [
|
|
145
|
+
namehash('test123.eth'),
|
|
146
|
+
'0x0f',
|
|
147
|
+
]),
|
|
148
|
+
)
|
|
149
|
+
const [contentType, encodedABI] =
|
|
150
|
+
publicResolver.interface.decodeFunctionResult('ABI', encodedRes[0]) as [
|
|
151
|
+
BigNumber,
|
|
152
|
+
string,
|
|
153
|
+
]
|
|
154
|
+
|
|
155
|
+
expect(contentType.toNumber()).toBe(1)
|
|
156
|
+
expect(toUtf8String(encodedABI)).toBe(JSON.stringify(dummyABI))
|
|
157
|
+
})
|
|
158
|
+
it('should allow a zlib encoded abi record to be set', async () => {
|
|
159
|
+
const encodedInput = await generateABIInput('zlib', dummyABI)
|
|
160
|
+
const tx = await ensInstance.setRecord('test123.eth', {
|
|
161
|
+
type: 'abi',
|
|
162
|
+
record: encodedInput,
|
|
163
|
+
addressOrIndex: 1,
|
|
164
|
+
})
|
|
165
|
+
expect(tx).toBeTruthy()
|
|
166
|
+
await tx.wait()
|
|
167
|
+
|
|
168
|
+
const universalResolver =
|
|
169
|
+
await ensInstance.contracts!.getUniversalResolver()!
|
|
170
|
+
const publicResolver = await ensInstance.contracts!.getPublicResolver()!
|
|
171
|
+
const encodedRes = await universalResolver['resolve(bytes,bytes)'](
|
|
172
|
+
hexEncodeName('test123.eth'),
|
|
173
|
+
publicResolver.interface.encodeFunctionData('ABI', [
|
|
174
|
+
namehash('test123.eth'),
|
|
175
|
+
'0x0f',
|
|
176
|
+
]),
|
|
177
|
+
)
|
|
178
|
+
const [contentType, encodedABI] =
|
|
179
|
+
publicResolver.interface.decodeFunctionResult('ABI', encodedRes[0]) as [
|
|
180
|
+
BigNumber,
|
|
181
|
+
string,
|
|
182
|
+
]
|
|
183
|
+
|
|
184
|
+
expect(contentType.toNumber()).toBe(2)
|
|
185
|
+
expect(inflate(arrayify(encodedABI), { to: 'string' })).toBe(
|
|
186
|
+
JSON.stringify(dummyABI),
|
|
187
|
+
)
|
|
188
|
+
})
|
|
189
|
+
it('should allow a cbor encoded abi record to be set', async () => {
|
|
190
|
+
const encodedInput = await generateABIInput('cbor', dummyABI)
|
|
191
|
+
const tx = await ensInstance.setRecord('test123.eth', {
|
|
192
|
+
type: 'abi',
|
|
193
|
+
record: encodedInput,
|
|
194
|
+
addressOrIndex: 1,
|
|
195
|
+
})
|
|
196
|
+
expect(tx).toBeTruthy()
|
|
197
|
+
await tx.wait()
|
|
198
|
+
|
|
199
|
+
const universalResolver =
|
|
200
|
+
await ensInstance.contracts!.getUniversalResolver()!
|
|
201
|
+
const publicResolver = await ensInstance.contracts!.getPublicResolver()!
|
|
202
|
+
const encodedRes = await universalResolver['resolve(bytes,bytes)'](
|
|
203
|
+
hexEncodeName('test123.eth'),
|
|
204
|
+
publicResolver.interface.encodeFunctionData('ABI', [
|
|
205
|
+
namehash('test123.eth'),
|
|
206
|
+
'0x0f',
|
|
207
|
+
]),
|
|
208
|
+
)
|
|
209
|
+
const [contentType, encodedABI] =
|
|
210
|
+
publicResolver.interface.decodeFunctionResult('ABI', encodedRes[0]) as [
|
|
211
|
+
BigNumber,
|
|
212
|
+
string,
|
|
213
|
+
]
|
|
214
|
+
|
|
215
|
+
expect(contentType.toNumber()).toBe(4)
|
|
216
|
+
expect(JSON.stringify(await decodeFirst(arrayify(encodedABI)))).toBe(
|
|
217
|
+
JSON.stringify(dummyABI),
|
|
218
|
+
)
|
|
219
|
+
})
|
|
220
|
+
it('should allow a uri abi record to be set', async () => {
|
|
221
|
+
const encodedInput = await generateABIInput('uri', 'https://example.com')
|
|
222
|
+
const tx = await ensInstance.setRecord('test123.eth', {
|
|
223
|
+
type: 'abi',
|
|
224
|
+
record: encodedInput,
|
|
225
|
+
addressOrIndex: 1,
|
|
226
|
+
})
|
|
227
|
+
expect(tx).toBeTruthy()
|
|
228
|
+
await tx.wait()
|
|
229
|
+
|
|
230
|
+
const universalResolver =
|
|
231
|
+
await ensInstance.contracts!.getUniversalResolver()!
|
|
232
|
+
const publicResolver = await ensInstance.contracts!.getPublicResolver()!
|
|
233
|
+
const encodedRes = await universalResolver['resolve(bytes,bytes)'](
|
|
234
|
+
hexEncodeName('test123.eth'),
|
|
235
|
+
publicResolver.interface.encodeFunctionData('ABI', [
|
|
236
|
+
namehash('test123.eth'),
|
|
237
|
+
'0x0f',
|
|
238
|
+
]),
|
|
239
|
+
)
|
|
240
|
+
const [contentType, encodedABI] =
|
|
241
|
+
publicResolver.interface.decodeFunctionResult('ABI', encodedRes[0]) as [
|
|
242
|
+
BigNumber,
|
|
243
|
+
string,
|
|
244
|
+
]
|
|
245
|
+
|
|
246
|
+
expect(contentType.toNumber()).toBe(8)
|
|
247
|
+
expect(toUtf8String(encodedABI)).toBe('https://example.com')
|
|
248
|
+
})
|
|
103
249
|
})
|
|
@@ -16,6 +16,11 @@ type AddrOrTextInput = {
|
|
|
16
16
|
type: 'addr' | 'text'
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
type ABIInput = {
|
|
20
|
+
record: RecordInput<'abi'>
|
|
21
|
+
type: 'abi'
|
|
22
|
+
}
|
|
23
|
+
|
|
19
24
|
export default async function (
|
|
20
25
|
{
|
|
21
26
|
contracts,
|
|
@@ -28,7 +33,7 @@ export default async function (
|
|
|
28
33
|
record,
|
|
29
34
|
type,
|
|
30
35
|
resolverAddress,
|
|
31
|
-
}: BaseInput & (ContentHashInput | AddrOrTextInput),
|
|
36
|
+
}: BaseInput & (ContentHashInput | AddrOrTextInput | ABIInput),
|
|
32
37
|
) {
|
|
33
38
|
if (!name.includes('.')) {
|
|
34
39
|
throw new Error('Input is not an ENS name')
|
package/src/functions/types.ts
CHANGED
|
@@ -20,9 +20,11 @@ import type getProfile from './getProfile'
|
|
|
20
20
|
import type getRecords from './getRecords'
|
|
21
21
|
import type getResolver from './getResolver'
|
|
22
22
|
import type {
|
|
23
|
+
getABI,
|
|
23
24
|
getAddr,
|
|
24
25
|
getContentHash,
|
|
25
26
|
getText,
|
|
27
|
+
_getABI,
|
|
26
28
|
_getAddr,
|
|
27
29
|
_getContentHash,
|
|
28
30
|
_getText,
|
|
@@ -70,9 +72,11 @@ type Function = {
|
|
|
70
72
|
getAddr: typeof getAddr
|
|
71
73
|
getContentHash: typeof getContentHash
|
|
72
74
|
getText: typeof getText
|
|
75
|
+
getABI: typeof getABI
|
|
73
76
|
_getAddr: typeof _getAddr
|
|
74
77
|
_getContentHash: typeof _getContentHash
|
|
75
78
|
_getText: typeof _getText
|
|
79
|
+
_getABI: typeof _getABI
|
|
76
80
|
getSubnames: typeof getSubnames
|
|
77
81
|
getWrapperData: typeof getWrapperData
|
|
78
82
|
importDNSSECName: typeof importDNSSECName
|
package/src/index.ts
CHANGED
|
@@ -497,6 +497,18 @@ export class ENS {
|
|
|
497
497
|
'_getText',
|
|
498
498
|
)
|
|
499
499
|
|
|
500
|
+
public getABI = this.generateRawFunction<FunctionTypes['getABI']>(
|
|
501
|
+
'initialGetters',
|
|
502
|
+
['contracts', 'universalWrapper'],
|
|
503
|
+
'getABI',
|
|
504
|
+
)
|
|
505
|
+
|
|
506
|
+
public _getABI = this.generateRawFunction<FunctionTypes['_getABI']>(
|
|
507
|
+
'initialGetters',
|
|
508
|
+
['contracts'],
|
|
509
|
+
'_getABI',
|
|
510
|
+
)
|
|
511
|
+
|
|
500
512
|
public getOwner = this.generateRawFunction<FunctionTypes['getOwner']>(
|
|
501
513
|
'initialGetters',
|
|
502
514
|
['contracts', 'multicallWrapper', 'gqlInstance'],
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { formatsByCoinType, formatsByName } from '@ensdomains/address-encoder'
|
|
2
|
+
import type { BigNumberish } from 'ethers'
|
|
3
|
+
import { isBytesLike, toUtf8Bytes } from 'ethers/lib/utils'
|
|
2
4
|
import type { PublicResolver } from '../generated'
|
|
3
5
|
import { encodeContenthash } from './contentHash'
|
|
4
6
|
|
|
@@ -7,11 +9,19 @@ type RecordItem = {
|
|
|
7
9
|
value: string
|
|
8
10
|
}
|
|
9
11
|
|
|
12
|
+
type ABIEncodeAs = 'json' | 'zlib' | 'cbor' | 'uri'
|
|
13
|
+
|
|
14
|
+
type ABIItem = {
|
|
15
|
+
contentType?: BigNumberish
|
|
16
|
+
data: object | string
|
|
17
|
+
}
|
|
18
|
+
|
|
10
19
|
export type RecordOptions = {
|
|
11
20
|
clearRecords?: boolean
|
|
12
21
|
contentHash?: string
|
|
13
22
|
texts?: RecordItem[]
|
|
14
23
|
coinTypes?: RecordItem[]
|
|
24
|
+
abi?: ABIItem
|
|
15
25
|
}
|
|
16
26
|
|
|
17
27
|
export const generateSetAddr = (
|
|
@@ -34,10 +44,44 @@ export const generateSetAddr = (
|
|
|
34
44
|
)
|
|
35
45
|
}
|
|
36
46
|
|
|
37
|
-
export
|
|
47
|
+
export const generateABIInput = async (
|
|
48
|
+
encodeAs: ABIEncodeAs,
|
|
49
|
+
data: object | string,
|
|
50
|
+
) => {
|
|
51
|
+
let contentType: number
|
|
52
|
+
let encodedData: string | Buffer | Uint8Array
|
|
53
|
+
switch (encodeAs) {
|
|
54
|
+
case 'json':
|
|
55
|
+
contentType = 1
|
|
56
|
+
encodedData = JSON.stringify(data)
|
|
57
|
+
break
|
|
58
|
+
case 'zlib': {
|
|
59
|
+
contentType = 2
|
|
60
|
+
const { deflate } = await import('pako/dist/pako_deflate.min.js')
|
|
61
|
+
encodedData = deflate(JSON.stringify(data))
|
|
62
|
+
break
|
|
63
|
+
}
|
|
64
|
+
case 'cbor': {
|
|
65
|
+
contentType = 4
|
|
66
|
+
const { encode } = await import('cbor')
|
|
67
|
+
encodedData = encode(data)
|
|
68
|
+
break
|
|
69
|
+
}
|
|
70
|
+
default: {
|
|
71
|
+
contentType = 8
|
|
72
|
+
encodedData = data as string
|
|
73
|
+
break
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return { contentType, data: encodedData }
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export type RecordTypes = 'contentHash' | 'text' | 'addr' | 'abi'
|
|
38
80
|
|
|
39
81
|
export type RecordInput<T extends RecordTypes> = T extends 'contentHash'
|
|
40
82
|
? string
|
|
83
|
+
: T extends 'abi'
|
|
84
|
+
? ABIItem
|
|
41
85
|
: RecordItem
|
|
42
86
|
|
|
43
87
|
export function generateSingleRecordCall<T extends RecordTypes>(
|
|
@@ -60,6 +104,24 @@ export function generateSingleRecordCall<T extends RecordTypes>(
|
|
|
60
104
|
])
|
|
61
105
|
}
|
|
62
106
|
}
|
|
107
|
+
if (type === 'abi') {
|
|
108
|
+
return (_r: RecordInput<T>) => {
|
|
109
|
+
const record = _r as ABIItem
|
|
110
|
+
const { contentType = 1, data } = record
|
|
111
|
+
let encodedData = data as string | Uint8Array | Buffer
|
|
112
|
+
if (!isBytesLike(encodedData)) {
|
|
113
|
+
if (typeof encodedData === 'object') {
|
|
114
|
+
encodedData = JSON.stringify(encodedData)
|
|
115
|
+
}
|
|
116
|
+
encodedData = toUtf8Bytes(encodedData)
|
|
117
|
+
}
|
|
118
|
+
return resolver.interface.encodeFunctionData('setABI', [
|
|
119
|
+
namehash,
|
|
120
|
+
contentType,
|
|
121
|
+
encodedData,
|
|
122
|
+
])
|
|
123
|
+
}
|
|
124
|
+
}
|
|
63
125
|
return (_r: RecordInput<T>) => {
|
|
64
126
|
const record = _r as RecordItem
|
|
65
127
|
if (type === 'text') {
|