@ensdomains/ensjs 3.0.0 → 3.1.0

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 (110) hide show
  1. package/dist/cjs/clients/decorators/public.js.map +1 -1
  2. package/dist/cjs/contracts/consts.js +3 -3
  3. package/dist/cjs/contracts/universalResolver.js +92 -59
  4. package/dist/cjs/contracts/universalResolver.js.map +1 -1
  5. package/dist/cjs/errors/version.js +1 -1
  6. package/dist/cjs/functions/public/_getAbi.js +52 -45
  7. package/dist/cjs/functions/public/_getAbi.js.map +1 -1
  8. package/dist/cjs/functions/public/_getAddr.js +30 -23
  9. package/dist/cjs/functions/public/_getAddr.js.map +1 -1
  10. package/dist/cjs/functions/public/_getContentHash.js +14 -7
  11. package/dist/cjs/functions/public/_getContentHash.js.map +1 -1
  12. package/dist/cjs/functions/public/_getText.js +15 -8
  13. package/dist/cjs/functions/public/_getText.js.map +1 -1
  14. package/dist/cjs/functions/public/getAbiRecord.js +13 -5
  15. package/dist/cjs/functions/public/getAbiRecord.js.map +1 -1
  16. package/dist/cjs/functions/public/getAddressRecord.js +13 -5
  17. package/dist/cjs/functions/public/getAddressRecord.js.map +1 -1
  18. package/dist/cjs/functions/public/getContentHashRecord.js +13 -5
  19. package/dist/cjs/functions/public/getContentHashRecord.js.map +1 -1
  20. package/dist/cjs/functions/public/getName.js +53 -35
  21. package/dist/cjs/functions/public/getName.js.map +1 -1
  22. package/dist/cjs/functions/public/getRecords.js +164 -161
  23. package/dist/cjs/functions/public/getRecords.js.map +1 -1
  24. package/dist/cjs/functions/public/getTextRecord.js +13 -5
  25. package/dist/cjs/functions/public/getTextRecord.js.map +1 -1
  26. package/dist/cjs/functions/public/universalWrapper.js +45 -37
  27. package/dist/cjs/functions/public/universalWrapper.js.map +1 -1
  28. package/dist/cjs/utils/checkSafeUniversalResolverData.js +35 -0
  29. package/dist/cjs/utils/checkSafeUniversalResolverData.js.map +1 -0
  30. package/dist/esm/clients/decorators/public.js.map +1 -1
  31. package/dist/esm/contracts/consts.js +3 -3
  32. package/dist/esm/contracts/universalResolver.js +92 -59
  33. package/dist/esm/contracts/universalResolver.js.map +1 -1
  34. package/dist/esm/errors/version.js +1 -1
  35. package/dist/esm/functions/public/_getAbi.js +56 -49
  36. package/dist/esm/functions/public/_getAbi.js.map +1 -1
  37. package/dist/esm/functions/public/_getAddr.js +30 -23
  38. package/dist/esm/functions/public/_getAddr.js.map +1 -1
  39. package/dist/esm/functions/public/_getContentHash.js +14 -7
  40. package/dist/esm/functions/public/_getContentHash.js.map +1 -1
  41. package/dist/esm/functions/public/_getText.js +15 -8
  42. package/dist/esm/functions/public/_getText.js.map +1 -1
  43. package/dist/esm/functions/public/getAbiRecord.js +13 -5
  44. package/dist/esm/functions/public/getAbiRecord.js.map +1 -1
  45. package/dist/esm/functions/public/getAddressRecord.js +13 -5
  46. package/dist/esm/functions/public/getAddressRecord.js.map +1 -1
  47. package/dist/esm/functions/public/getContentHashRecord.js +13 -5
  48. package/dist/esm/functions/public/getContentHashRecord.js.map +1 -1
  49. package/dist/esm/functions/public/getName.js +54 -36
  50. package/dist/esm/functions/public/getName.js.map +1 -1
  51. package/dist/esm/functions/public/getRecords.js +168 -165
  52. package/dist/esm/functions/public/getRecords.js.map +1 -1
  53. package/dist/esm/functions/public/getTextRecord.js +13 -5
  54. package/dist/esm/functions/public/getTextRecord.js.map +1 -1
  55. package/dist/esm/functions/public/universalWrapper.js +46 -38
  56. package/dist/esm/functions/public/universalWrapper.js.map +1 -1
  57. package/dist/esm/utils/checkSafeUniversalResolverData.js +39 -0
  58. package/dist/esm/utils/checkSafeUniversalResolverData.js.map +1 -0
  59. package/dist/types/clients/decorators/public.d.ts +6 -6
  60. package/dist/types/clients/decorators/public.d.ts.map +1 -1
  61. package/dist/types/clients/public.d.ts +34 -8
  62. package/dist/types/clients/public.d.ts.map +1 -1
  63. package/dist/types/contracts/consts.d.ts +3 -3
  64. package/dist/types/contracts/universalResolver.d.ts +73 -0
  65. package/dist/types/contracts/universalResolver.d.ts.map +1 -1
  66. package/dist/types/errors/version.d.ts +1 -1
  67. package/dist/types/functions/public/_getAbi.d.ts +3 -1
  68. package/dist/types/functions/public/_getAbi.d.ts.map +1 -1
  69. package/dist/types/functions/public/_getAddr.d.ts +3 -1
  70. package/dist/types/functions/public/_getAddr.d.ts.map +1 -1
  71. package/dist/types/functions/public/_getContentHash.d.ts +3 -1
  72. package/dist/types/functions/public/_getContentHash.d.ts.map +1 -1
  73. package/dist/types/functions/public/_getText.d.ts +3 -1
  74. package/dist/types/functions/public/_getText.d.ts.map +1 -1
  75. package/dist/types/functions/public/getAbiRecord.d.ts +8 -5
  76. package/dist/types/functions/public/getAbiRecord.d.ts.map +1 -1
  77. package/dist/types/functions/public/getAddressRecord.d.ts +8 -5
  78. package/dist/types/functions/public/getAddressRecord.d.ts.map +1 -1
  79. package/dist/types/functions/public/getContentHashRecord.d.ts +7 -4
  80. package/dist/types/functions/public/getContentHashRecord.d.ts.map +1 -1
  81. package/dist/types/functions/public/getName.d.ts +9 -3
  82. package/dist/types/functions/public/getName.d.ts.map +1 -1
  83. package/dist/types/functions/public/getRecords.d.ts +53 -26
  84. package/dist/types/functions/public/getRecords.d.ts.map +1 -1
  85. package/dist/types/functions/public/getTextRecord.d.ts +7 -4
  86. package/dist/types/functions/public/getTextRecord.d.ts.map +1 -1
  87. package/dist/types/functions/public/universalWrapper.d.ts +4 -2
  88. package/dist/types/functions/public/universalWrapper.d.ts.map +1 -1
  89. package/dist/types/types.d.ts +2 -2
  90. package/dist/types/types.d.ts.map +1 -1
  91. package/dist/types/utils/checkSafeUniversalResolverData.d.ts +14 -0
  92. package/dist/types/utils/checkSafeUniversalResolverData.d.ts.map +1 -0
  93. package/package.json +4 -4
  94. package/src/clients/decorators/public.ts +31 -4
  95. package/src/contracts/consts.ts +3 -3
  96. package/src/contracts/universalResolver.ts +95 -59
  97. package/src/errors/version.ts +1 -1
  98. package/src/functions/public/_getAbi.ts +61 -53
  99. package/src/functions/public/_getAddr.ts +31 -24
  100. package/src/functions/public/_getContentHash.ts +15 -7
  101. package/src/functions/public/_getText.ts +16 -8
  102. package/src/functions/public/getAbiRecord.ts +21 -8
  103. package/src/functions/public/getAddressRecord.ts +25 -9
  104. package/src/functions/public/getContentHashRecord.ts +19 -7
  105. package/src/functions/public/getName.ts +58 -40
  106. package/src/functions/public/getRecords.ts +336 -235
  107. package/src/functions/public/getTextRecord.ts +19 -6
  108. package/src/functions/public/universalWrapper.ts +49 -43
  109. package/src/types.ts +4 -3
  110. package/src/utils/checkSafeUniversalResolverData.ts +62 -0
@@ -1,10 +1,8 @@
1
1
  import {
2
2
  BaseError,
3
3
  decodeAbiParameters,
4
- decodeErrorResult,
5
4
  decodeFunctionResult,
6
5
  encodeFunctionData,
7
- getContractError,
8
6
  hexToBigInt,
9
7
  toHex,
10
8
  type Address,
@@ -16,12 +14,13 @@ import { universalResolverResolveArraySnippet } from '../../contracts/universalR
16
14
  import type {
17
15
  DecodedAddr,
18
16
  DecodedText,
17
+ Prettify,
19
18
  SimpleTransactionRequest,
20
19
  TransactionRequestWithPassthrough,
21
20
  } from '../../types.js'
21
+ import { checkSafeUniversalResolverData } from '../../utils/checkSafeUniversalResolverData.js'
22
22
  import { EMPTY_ADDRESS } from '../../utils/consts.js'
23
23
  import { generateFunction } from '../../utils/generateFunction.js'
24
- import { getRevertErrorData } from '../../utils/getRevertErrorData.js'
25
24
  import { packetToBytes } from '../../utils/hexEncodedName.js'
26
25
  import _getAbi, { type InternalGetAbiReturnType } from './_getAbi.js'
27
26
  import _getAddr from './_getAddr.js'
@@ -31,9 +30,25 @@ import _getContentHash, {
31
30
  import _getText from './_getText.js'
32
31
  import multicallWrapper from './multicallWrapper.js'
33
32
 
34
- export type GetRecordsParameters = {
33
+ export type GetRecordsParameters<
34
+ TTexts extends readonly string[] | undefined = readonly string[],
35
+ TCoins extends readonly (string | number)[] | undefined = readonly (
36
+ | string
37
+ | number
38
+ )[],
39
+ TContentHash extends boolean | undefined = true,
40
+ TAbi extends boolean | undefined = true,
41
+ > = {
35
42
  /** Name to get records for */
36
43
  name: string
44
+ /** Text record key array */
45
+ texts?: TTexts
46
+ /** Coin record id/symbol array */
47
+ coins?: TCoins
48
+ /** If true, will fetch content hash */
49
+ contentHash?: TContentHash
50
+ /** If true, will fetch ABI */
51
+ abi?: TAbi
37
52
  /** Optional specific resolver address, for fallback or for all results */
38
53
  resolver?: {
39
54
  /** Resolver address */
@@ -41,17 +56,8 @@ export type GetRecordsParameters = {
41
56
  /** If true, will only use resolver if main fetch fails */
42
57
  fallbackOnly?: boolean
43
58
  }
44
- /** Records to fetch */
45
- records: {
46
- /** Text record key array */
47
- texts?: string[]
48
- /** Coin record id/symbol array */
49
- coins?: (string | number)[]
50
- /** If true, will fetch content hash */
51
- contentHash?: boolean
52
- /** If true, will fetch ABI */
53
- abi?: boolean
54
- }
59
+ /** Batch gateway URLs to use for resolving CCIP-read requests. */
60
+ gatewayUrls?: string[]
55
61
  }
56
62
 
57
63
  type WithContentHashResult = {
@@ -74,182 +80,321 @@ type WithCoinsResult = {
74
80
  coins: DecodedAddr[]
75
81
  }
76
82
 
77
- type BaseGetRecordsReturnType = Partial<
78
- WithContentHashResult & WithAbiResult & WithTextsResult & WithCoinsResult
79
- > & {
80
- /** Resolver address used for fetch */
81
- resolverAddress: Address
82
- }
83
-
84
83
  export type GetRecordsReturnType<
85
- TParams extends GetRecordsParameters = GetRecordsParameters,
86
- > = TParams['records'] extends undefined
87
- ? BaseGetRecordsReturnType
88
- : (TParams['records']['contentHash'] extends true
89
- ? WithContentHashResult
90
- : {}) &
91
- (TParams['records']['abi'] extends true ? WithAbiResult : {}) &
92
- (TParams['records']['texts'] extends string[] ? WithTextsResult : {}) &
93
- (TParams['records']['coins'] extends (string | number)[]
94
- ? WithCoinsResult
95
- : {}) & {
96
- /** Resolver address used for fetch */
97
- resolverAddress: Address
98
- }
99
-
100
- type CallObj = {
101
- key: string | number
102
- call: SimpleTransactionRequest
103
- type: 'coin' | 'text' | 'contentHash' | 'abi'
104
- }
84
+ TTexts extends readonly string[] = readonly [string, ...string[]],
85
+ TCoins extends readonly (string | number)[] = readonly [
86
+ string | number,
87
+ ...(string | number)[],
88
+ ],
89
+ TContentHash extends boolean = true,
90
+ TAbi extends boolean = true,
91
+ > = Prettify<
92
+ (TContentHash extends true ? WithContentHashResult : {}) &
93
+ (TAbi extends true ? WithAbiResult : {}) &
94
+ (TTexts extends readonly [string, ...string[]] ? WithTextsResult : {}) &
95
+ (TCoins extends readonly [string | number, ...(string | number)[]]
96
+ ? WithCoinsResult
97
+ : {}) & {
98
+ /** Resolver address used for fetch */
99
+ resolverAddress: Address
100
+ }
101
+ >
105
102
 
106
- const encode = (
107
- client: ClientWithEns,
108
- { name, resolver, records }: GetRecordsParameters,
109
- ): TransactionRequestWithPassthrough => {
110
- const calls = Object.keys(records).reduce((prev, key) => {
111
- if (key === 'texts') {
112
- return [
113
- ...prev,
114
- ...records.texts!.map(
115
- (text) =>
116
- ({
117
- key: text,
118
- call: _getText.encode(client, { name, key: text }),
119
- type: 'text',
120
- } as const),
121
- ),
122
- ]
103
+ type CallObj =
104
+ | {
105
+ key: string
106
+ call: SimpleTransactionRequest
107
+ type: 'text'
108
+ }
109
+ | {
110
+ key: string | number
111
+ call: SimpleTransactionRequest
112
+ type: 'coin'
123
113
  }
124
- if (key === 'coins') {
125
- return [
126
- ...prev,
127
- ...records.coins!.map(
128
- (coin) =>
129
- ({
130
- key: coin,
131
- call: _getAddr.encode(client, { name, coin }),
132
- type: 'coin',
133
- } as const),
134
- ),
135
- ]
114
+ | {
115
+ key: 'contentHash'
116
+ call: SimpleTransactionRequest
117
+ type: 'contentHash'
136
118
  }
137
- if (key === 'contentHash') {
138
- return [
139
- ...prev,
119
+ | {
120
+ key: 'abi'
121
+ call: SimpleTransactionRequest
122
+ type: 'abi'
123
+ }
124
+
125
+ type EncodeReturnType = Required<
126
+ TransactionRequestWithPassthrough<{
127
+ calls: (CallObj | null)[]
128
+ address?: Address
129
+ args?: any
130
+ }>
131
+ >
132
+
133
+ const createCalls = (
134
+ client: ClientWithEns,
135
+ {
136
+ name,
137
+ texts,
138
+ coins,
139
+ abi,
140
+ contentHash,
141
+ }: Pick<
142
+ GetRecordsParameters,
143
+ 'name' | 'texts' | 'coins' | 'abi' | 'contentHash'
144
+ >,
145
+ ) => [
146
+ ...(texts ?? []).map(
147
+ (text) =>
148
+ ({
149
+ key: text,
150
+ call: _getText.encode(client, { name, key: text }),
151
+ type: 'text',
152
+ } as const),
153
+ ),
154
+ ...(coins ?? []).map(
155
+ (coin) =>
156
+ ({
157
+ key: coin,
158
+ call: _getAddr.encode(client, { name, coin }),
159
+ type: 'coin',
160
+ } as const),
161
+ ),
162
+ ...(contentHash
163
+ ? ([
140
164
  {
141
165
  key: 'contentHash',
142
166
  call: _getContentHash.encode(client, { name }),
143
167
  type: 'contentHash',
144
- } as const,
145
- ]
146
- }
147
- if (key === 'abi') {
148
- return [
149
- ...prev,
150
- {
151
- key: 'abi',
152
- call: _getAbi.encode(client, { name }),
153
- type: 'abi',
154
- } as const,
155
- ]
156
- }
157
- return prev
158
- }, [] as (CallObj | null)[])
168
+ },
169
+ ] as const)
170
+ : []),
171
+ ...(abi
172
+ ? ([
173
+ { key: 'abi', call: _getAbi.encode(client, { name }), type: 'abi' },
174
+ ] as const)
175
+ : []),
176
+ ]
159
177
 
160
- const passthrough = calls
178
+ const encode = (
179
+ client: ClientWithEns,
180
+ {
181
+ name,
182
+ resolver,
183
+ texts,
184
+ coins,
185
+ contentHash,
186
+ abi,
187
+ gatewayUrls,
188
+ }: GetRecordsParameters,
189
+ ): EncodeReturnType => {
190
+ const calls = createCalls(client, {
191
+ name,
192
+ texts,
193
+ coins,
194
+ contentHash,
195
+ abi,
196
+ })
161
197
 
162
198
  if (resolver?.address && !resolver.fallbackOnly) {
163
199
  const encoded = multicallWrapper.encode(client, {
164
- transactions: calls
165
- .filter((c) => c)
166
- .map((c) => ({ to: resolver.address, data: c!.call.data })),
200
+ transactions: calls.map((c) => ({
201
+ to: resolver.address,
202
+ data: c.call.data,
203
+ })),
167
204
  })
168
205
  return {
169
206
  ...encoded,
170
- passthrough,
207
+ passthrough: { calls },
171
208
  }
172
209
  }
173
210
 
174
- const encoded = encodeFunctionData({
175
- abi: universalResolverResolveArraySnippet,
176
- functionName: 'resolve',
177
- args: [
178
- toHex(packetToBytes(name)),
179
- calls.filter((c) => c).map((c) => c!.call.data),
180
- ],
211
+ const to = getChainContractAddress({
212
+ client,
213
+ contract: 'ensUniversalResolver',
181
214
  })
215
+ const args = [
216
+ toHex(packetToBytes(name)),
217
+ calls.map((c) => c.call.data),
218
+ ] as const
182
219
 
183
220
  return {
184
- to: getChainContractAddress({ client, contract: 'ensUniversalResolver' }),
185
- data: encoded,
186
- passthrough,
221
+ to,
222
+ ...(gatewayUrls?.length
223
+ ? {
224
+ data: encodeFunctionData({
225
+ abi: universalResolverResolveArraySnippet,
226
+ functionName: 'resolve',
227
+ args: [...args, gatewayUrls] as const,
228
+ }),
229
+ passthrough: {
230
+ calls,
231
+ args: [...args, gatewayUrls],
232
+ address: to,
233
+ },
234
+ }
235
+ : {
236
+ data: encodeFunctionData({
237
+ abi: universalResolverResolveArraySnippet,
238
+ functionName: 'resolve',
239
+ args,
240
+ }),
241
+ passthrough: {
242
+ calls,
243
+ args,
244
+ address: to,
245
+ },
246
+ }),
247
+ }
248
+ }
249
+
250
+ const createEmptyResult = <
251
+ TTexts extends readonly string[] | undefined,
252
+ TCoins extends readonly (string | number)[] | undefined,
253
+ TContentHash extends boolean | undefined,
254
+ TAbi extends boolean | undefined,
255
+ >({
256
+ texts,
257
+ coins,
258
+ abi,
259
+ contentHash,
260
+ }: Pick<
261
+ GetRecordsParameters<TTexts, TCoins, TContentHash, TAbi>,
262
+ 'texts' | 'coins' | 'abi' | 'contentHash'
263
+ >) => ({
264
+ ...(texts && texts.length > 0 ? { texts: [] as DecodedText[] } : {}),
265
+ ...(coins && coins.length > 0 ? { coins: [] as DecodedAddr[] } : {}),
266
+ ...(contentHash ? { contentHash: null } : {}),
267
+ ...(abi ? { abi: null } : {}),
268
+ })
269
+
270
+ const decodeRecord = async (
271
+ client: ClientWithEns,
272
+ { item, call }: { item: Hex; call: CallObj },
273
+ ) => {
274
+ const { key, type } = call
275
+ const baseItem = { key, type }
276
+ if (type === 'contentHash') {
277
+ try {
278
+ const decodedFromAbi = decodeAbiParameters(
279
+ [{ type: 'bytes' }] as const,
280
+ item,
281
+ )[0]
282
+ if (decodedFromAbi === '0x' || hexToBigInt(decodedFromAbi) === 0n) {
283
+ return { ...baseItem, value: null }
284
+ }
285
+ } catch {
286
+ // ignore
287
+ }
288
+ }
289
+ if (type === 'text') {
290
+ const decodedFromAbi = await _getText.decode(client, item, {
291
+ strict: false,
292
+ })
293
+ return { ...baseItem, value: decodedFromAbi }
294
+ }
295
+ if (type === 'coin') {
296
+ const decodedFromAbi = await _getAddr.decode(client, item, {
297
+ coin: key,
298
+ strict: false,
299
+ })
300
+ return { ...baseItem, value: decodedFromAbi }
301
+ }
302
+ if (type === 'contentHash') {
303
+ const decodedFromAbi = await _getContentHash.decode(client, item, {
304
+ strict: false,
305
+ })
306
+ return { ...baseItem, value: decodedFromAbi }
307
+ }
308
+ // abi
309
+ const decodedFromAbi = await _getAbi.decode(client, item, {
310
+ strict: false,
311
+ })
312
+ return { ...baseItem, value: decodedFromAbi }
313
+ }
314
+
315
+ const createRecordResult = (
316
+ prev: GetRecordsReturnType,
317
+ curr: Awaited<ReturnType<typeof decodeRecord>>,
318
+ ) => {
319
+ if (curr.type === 'text' || curr.type === 'coin') {
320
+ if (!curr.value) {
321
+ return prev
322
+ }
323
+ }
324
+ if (curr.type === 'text') {
325
+ return {
326
+ ...prev,
327
+ texts: [
328
+ ...(prev.texts || []),
329
+ { key: curr.key, value: curr.value } as DecodedText,
330
+ ],
331
+ }
187
332
  }
333
+ if (curr.type === 'coin') {
334
+ return {
335
+ ...prev,
336
+ coins: [...(prev.coins || []), curr.value as DecodedAddr],
337
+ }
338
+ }
339
+ if (curr.type === 'contentHash') {
340
+ return {
341
+ ...prev,
342
+ contentHash: curr.value as InternalGetContentHashReturnType,
343
+ }
344
+ }
345
+ // abi
346
+ return { ...prev, abi: curr.value as InternalGetAbiReturnType }
188
347
  }
189
348
 
190
- const decode = async <TParams extends GetRecordsParameters>(
349
+ const decode = async <
350
+ const TTexts extends readonly string[] = readonly string[],
351
+ const TCoins extends readonly (string | number)[] = readonly (
352
+ | string
353
+ | number
354
+ )[],
355
+ const TContentHash extends boolean = true,
356
+ const TAbi extends boolean = true,
357
+ >(
191
358
  client: ClientWithEns,
192
359
  data: Hex | BaseError,
193
- passthrough: (CallObj | null)[],
194
- { name, resolver, records: recordsParams }: TParams,
195
- ): Promise<GetRecordsReturnType<TParams>> => {
196
- const calls = passthrough
360
+ passthrough: EncodeReturnType['passthrough'],
361
+ {
362
+ resolver,
363
+ texts,
364
+ coins,
365
+ contentHash,
366
+ abi,
367
+ }: GetRecordsParameters<TTexts, TCoins, TContentHash, TAbi>,
368
+ ): Promise<GetRecordsReturnType<TTexts, TCoins, TContentHash, TAbi>> => {
369
+ const { calls } = passthrough
197
370
  let recordData: (Hex | null)[] = []
198
371
  let resolverAddress: Address
199
372
 
373
+ const emptyResult = createEmptyResult({ texts, coins, contentHash, abi })
374
+
200
375
  if (resolver?.address && !resolver.fallbackOnly) {
201
376
  const result = await multicallWrapper.decode(
202
377
  client,
203
378
  data,
204
- passthrough.filter((c) => c).map((c) => c!.call),
379
+ passthrough.calls.filter((c) => c).map((c) => c!.call),
205
380
  )
206
381
  resolverAddress = resolver.address
207
382
  recordData = result.map((r) => r.returnData)
208
383
  } else {
209
- if (typeof data === 'object') {
210
- const errorData = getRevertErrorData(data)
211
- if (errorData) {
212
- const decodedError = decodeErrorResult({
213
- abi: universalResolverResolveArraySnippet,
214
- data: errorData,
215
- })
216
- if (
217
- decodedError.errorName === 'ResolverNotFound' ||
218
- decodedError.errorName === 'ResolverWildcardNotSupported'
219
- )
220
- return passthrough.reduce(
221
- (prev, curr) => {
222
- if (!curr) return prev
223
- if (curr.type === 'coin' && !('coin' in prev)) {
224
- return { ...prev, coins: [] }
225
- }
226
- if (curr.type === 'text' && !('texts' in prev)) {
227
- return { ...prev, texts: [] }
228
- }
229
- if (curr.type === 'contentHash' && !('contentHash' in prev)) {
230
- return { ...prev, contentHash: null }
231
- }
232
- // abi
233
- return { ...prev, abi: null }
234
- },
235
- {
236
- resolverAddress: EMPTY_ADDRESS,
237
- } as unknown as GetRecordsReturnType<TParams>,
238
- )
239
- }
240
- throw getContractError(data, {
241
- abi: universalResolverResolveArraySnippet,
242
- functionName: 'resolve',
243
- args: [
244
- toHex(packetToBytes(name)),
245
- calls.filter((c) => c).map((c) => c!.call.data),
246
- ],
247
- address: getChainContractAddress({
248
- client,
249
- contract: 'ensUniversalResolver',
250
- }),
251
- }) as BaseError
252
- }
384
+ const isSafe = checkSafeUniversalResolverData(data, {
385
+ strict: false,
386
+ abi: universalResolverResolveArraySnippet,
387
+ args: passthrough.args,
388
+ functionName: 'resolve',
389
+ address: passthrough.address,
390
+ })
391
+
392
+ if (!isSafe)
393
+ return {
394
+ ...emptyResult,
395
+ resolverAddress: EMPTY_ADDRESS,
396
+ } as GetRecordsReturnType<TTexts, TCoins, TContentHash, TAbi>
397
+
253
398
  const result = decodeFunctionResult({
254
399
  abi: universalResolverResolveArraySnippet,
255
400
  functionName: 'resolve',
@@ -269,83 +414,17 @@ const decode = async <TParams extends GetRecordsParameters>(
269
414
  const filteredRecordData = recordData.filter((x) => x) as Hex[]
270
415
 
271
416
  const decodedRecords = await Promise.all(
272
- filteredRecordData.map(async (item, i) => {
273
- const { key, type } = filteredCalls[i]
274
- const baseItem = { key, type }
275
- if (type === 'contentHash') {
276
- const decodedFromAbi = decodeAbiParameters(
277
- [{ type: 'bytes' }] as const,
278
- item,
279
- )[0]
280
- if (decodedFromAbi === '0x' || hexToBigInt(decodedFromAbi) === 0n) {
281
- return { ...baseItem, value: null }
282
- }
283
- }
284
- if (type === 'text') {
285
- const decodedFromAbi = await _getText.decode(client, item)
286
- return { ...baseItem, value: decodedFromAbi }
287
- }
288
- if (type === 'coin') {
289
- const decodedFromAbi = await _getAddr.decode(client, item, {
290
- name,
291
- coin: key,
292
- })
293
- return { ...baseItem, value: decodedFromAbi }
294
- }
295
- if (type === 'contentHash') {
296
- const decodedFromAbi = await _getContentHash.decode(client, item)
297
- return { ...baseItem, value: decodedFromAbi }
298
- }
299
- // abi
300
- const decodedFromAbi = await _getAbi.decode(client, item)
301
- return { ...baseItem, value: decodedFromAbi }
302
- }),
417
+ filteredRecordData.map(async (item, i) =>
418
+ decodeRecord(client, { item, call: filteredCalls[i] }),
419
+ ),
303
420
  )
304
421
 
305
- const records = decodedRecords.reduce(
306
- (prev, curr) => {
307
- if (curr.type === 'text' || curr.type === 'coin') {
308
- if (!curr.value) {
309
- return prev
310
- }
311
- }
312
- if (curr.type === 'text') {
313
- return {
314
- ...prev,
315
- texts: [
316
- ...(prev.texts || []),
317
- { key: curr.key, value: curr.value } as DecodedText,
318
- ],
319
- }
320
- }
321
- if (curr.type === 'coin') {
322
- return {
323
- ...prev,
324
- coins: [...(prev.coins || []), curr.value as DecodedAddr],
325
- }
326
- }
327
- if (curr.type === 'contentHash') {
328
- return {
329
- ...prev,
330
- contentHash: curr.value as InternalGetContentHashReturnType,
331
- }
332
- }
333
- // abi
334
- return { ...prev, abi: curr.value as InternalGetAbiReturnType }
335
- },
336
- {
337
- resolverAddress,
338
- ...('texts' in recordsParams ? { texts: [] } : {}),
339
- ...('coins' in recordsParams ? { coins: [] } : {}),
340
- ...('contentHash' in recordsParams ? { contentHash: null } : {}),
341
- } as GetRecordsReturnType<
342
- GetRecordsParameters & {
343
- records: Required<GetRecordsParameters['records']>
344
- }
345
- >,
346
- )
422
+ const records = decodedRecords.reduce(createRecordResult, {
423
+ ...emptyResult,
424
+ resolverAddress,
425
+ } as GetRecordsReturnType)
347
426
 
348
- return records as GetRecordsReturnType<TParams>
427
+ return records as GetRecordsReturnType<TTexts, TCoins, TContentHash, TAbi>
349
428
  }
350
429
 
351
430
  type EncoderFunction = typeof encode
@@ -354,12 +433,20 @@ type DecoderFunction = typeof decode<any>
354
433
  type BatchableFunctionObject = {
355
434
  encode: EncoderFunction
356
435
  decode: DecoderFunction
357
- batch: <TParams extends GetRecordsParameters>(
358
- args: TParams,
436
+ batch: <
437
+ const TTexts extends readonly string[] = readonly string[],
438
+ const TCoins extends readonly (string | number)[] = readonly (
439
+ | string
440
+ | number
441
+ )[],
442
+ const TContentHash extends boolean = true,
443
+ const TAbi extends boolean = true,
444
+ >(
445
+ args: GetRecordsParameters<TTexts, TCoins, TContentHash, TAbi>,
359
446
  ) => {
360
- args: [TParams]
447
+ args: [GetRecordsParameters<TTexts, TCoins, TContentHash, TAbi>]
361
448
  encode: EncoderFunction
362
- decode: typeof decode<TParams>
449
+ decode: typeof decode<TTexts, TCoins, TContentHash, TAbi>
363
450
  }
364
451
  }
365
452
 
@@ -390,11 +477,25 @@ type BatchableFunctionObject = {
390
477
  * // { texts: [{ key: 'com.twitter', value: 'ensdomains' }, { key: 'com.github', value: 'ensdomains' }], coins: [{ id: 60, name: 'ETH', value: '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7' }], contentHash: { protocolType: 'ipns', decoded: 'k51qzi5uqu5djdczd6zw0grmo23j2vkj9uzvujencg15s5rlkq0ss4ivll8wqw' } }
391
478
  */
392
479
  const getRecords = generateFunction({ encode, decode }) as (<
393
- TParams extends GetRecordsParameters,
480
+ const TTexts extends readonly string[] = readonly string[],
481
+ const TCoins extends readonly (string | number)[] = readonly (
482
+ | string
483
+ | number
484
+ )[],
485
+ const TContentHash extends boolean = true,
486
+ const TAbi extends boolean = true,
394
487
  >(
395
488
  client: ClientWithEns,
396
- { name, records, resolver }: TParams,
397
- ) => Promise<GetRecordsReturnType<TParams>>) &
489
+ {
490
+ name,
491
+ texts,
492
+ coins,
493
+ contentHash,
494
+ abi,
495
+ resolver,
496
+ gatewayUrls,
497
+ }: GetRecordsParameters<TTexts, TCoins, TContentHash, TAbi>,
498
+ ) => Promise<GetRecordsReturnType<TTexts, TCoins, TContentHash, TAbi>>) &
398
499
  BatchableFunctionObject
399
500
 
400
501
  export default getRecords