@ensdomains/ensjs 3.0.0-alpha.6 → 3.0.0-alpha.9
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/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/setRecord.d.ts +18 -0
- package/dist/cjs/functions/setRecord.js +27 -0
- package/dist/cjs/index.d.ts +7 -2
- package/dist/cjs/index.js +6 -1
- 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/recordHelpers.d.ts +3 -0
- package/dist/cjs/utils/recordHelpers.js +42 -15
- package/dist/esm/GqlManager.d.ts +3 -0
- package/dist/esm/GqlManager.js +58 -2
- 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/setRecord.d.ts +18 -0
- package/dist/esm/functions/setRecord.js +24 -0
- package/dist/esm/index.d.ts +7 -2
- package/dist/esm/index.js +6 -1
- 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/recordHelpers.d.ts +3 -0
- package/dist/esm/utils/recordHelpers.js +40 -14
- package/package.json +7 -5
- package/src/GqlManager.test.ts +170 -0
- package/src/GqlManager.ts +67 -2
- 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/setRecord.test.ts +100 -0
- package/src/functions/setRecord.ts +63 -0
- package/src/functions/setRecords.test.ts +2 -2
- package/src/index.ts +8 -1
- package/src/utils/labels.ts +5 -3
- package/src/utils/normalise.ts +10 -4
- package/src/utils/recordHelpers.ts +52 -18
|
@@ -2,6 +2,7 @@ import { formatsByName } from '@ensdomains/address-encoder';
|
|
|
2
2
|
import { ethers } from 'ethers';
|
|
3
3
|
import { decodeContenthash } from '../utils/contentHash';
|
|
4
4
|
import { hexEncodeName } from '../utils/hexEncodedName';
|
|
5
|
+
import { namehash } from '../utils/normalise';
|
|
5
6
|
import { parseInputType } from '../utils/validation';
|
|
6
7
|
const makeMulticallData = async ({ _getAddr, _getContentHash, _getText, resolverMulticallWrapper, }, name, options) => {
|
|
7
8
|
let calls = [];
|
|
@@ -47,13 +48,19 @@ const fetchWithoutResolverMulticall = async ({ multicallWrapper }, calls, resolv
|
|
|
47
48
|
}));
|
|
48
49
|
return (await multicallWrapper(callsWithResolver)).map((x) => x[1]);
|
|
49
50
|
};
|
|
50
|
-
const getDataForName = async ({ contracts, _getAddr, _getContentHash, _getText, resolverMulticallWrapper, multicallWrapper, }, name, options, fallbackResolver) => {
|
|
51
|
+
const getDataForName = async ({ contracts, _getAddr, _getContentHash, _getText, resolverMulticallWrapper, multicallWrapper, }, name, options, fallbackResolver, specificResolver) => {
|
|
51
52
|
const universalResolver = await contracts?.getUniversalResolver();
|
|
52
53
|
const { data, calls } = await makeMulticallData({ _getAddr, _getContentHash, _getText, resolverMulticallWrapper }, name, options);
|
|
53
54
|
let resolvedData;
|
|
54
55
|
let useFallbackResolver = false;
|
|
55
56
|
try {
|
|
56
|
-
|
|
57
|
+
if (specificResolver) {
|
|
58
|
+
const publicResolver = await contracts?.getPublicResolver(undefined, specificResolver);
|
|
59
|
+
resolvedData = await publicResolver?.callStatic.multicall(calls.map((x) => x.data));
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
resolvedData = await universalResolver?.resolve(hexEncodeName(name), data);
|
|
63
|
+
}
|
|
57
64
|
}
|
|
58
65
|
catch {
|
|
59
66
|
useFallbackResolver = true;
|
|
@@ -61,12 +68,18 @@ const getDataForName = async ({ contracts, _getAddr, _getContentHash, _getText,
|
|
|
61
68
|
let resolverAddress;
|
|
62
69
|
let recordData;
|
|
63
70
|
if (useFallbackResolver) {
|
|
64
|
-
resolverAddress = fallbackResolver;
|
|
71
|
+
resolverAddress = specificResolver || fallbackResolver;
|
|
65
72
|
recordData = await fetchWithoutResolverMulticall({ multicallWrapper }, calls, resolverAddress);
|
|
66
73
|
}
|
|
67
74
|
else {
|
|
68
|
-
resolverAddress = resolvedData['1'];
|
|
69
|
-
|
|
75
|
+
resolverAddress = specificResolver || resolvedData['1'];
|
|
76
|
+
if (specificResolver) {
|
|
77
|
+
recordData = resolvedData;
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
;
|
|
81
|
+
[recordData] = await resolverMulticallWrapper.decode(resolvedData['0']);
|
|
82
|
+
}
|
|
70
83
|
}
|
|
71
84
|
const matchAddress = recordData[calls.findIndex((x) => x.key === '60')];
|
|
72
85
|
return {
|
|
@@ -158,10 +171,10 @@ const formatRecords = async ({ _getText, _getAddr, _getContentHash, }, data, cal
|
|
|
158
171
|
}
|
|
159
172
|
return returnedResponse;
|
|
160
173
|
};
|
|
161
|
-
const graphFetch = async ({ gqlInstance }, name, wantedRecords) => {
|
|
174
|
+
const graphFetch = async ({ gqlInstance }, name, wantedRecords, resolverAddress) => {
|
|
162
175
|
const query = gqlInstance.gql `
|
|
163
|
-
query getRecords($
|
|
164
|
-
|
|
176
|
+
query getRecords($id: String!) {
|
|
177
|
+
domain(id: $id) {
|
|
165
178
|
isMigrated
|
|
166
179
|
createdAt
|
|
167
180
|
resolver {
|
|
@@ -175,12 +188,39 @@ const graphFetch = async ({ gqlInstance }, name, wantedRecords) => {
|
|
|
175
188
|
}
|
|
176
189
|
}
|
|
177
190
|
}
|
|
191
|
+
`;
|
|
192
|
+
const customResolverQuery = gqlInstance.gql `
|
|
193
|
+
query getRecordsWithCustomResolver($id: String!, $resolverId: String!) {
|
|
194
|
+
domain(id: $id) {
|
|
195
|
+
isMigrated
|
|
196
|
+
createdAt
|
|
197
|
+
}
|
|
198
|
+
resolver(id: $resolverId) {
|
|
199
|
+
texts
|
|
200
|
+
coinTypes
|
|
201
|
+
contentHash
|
|
202
|
+
addr {
|
|
203
|
+
id
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
178
207
|
`;
|
|
179
208
|
const client = gqlInstance.client;
|
|
180
|
-
const
|
|
181
|
-
|
|
209
|
+
const id = namehash(name);
|
|
210
|
+
let domain;
|
|
211
|
+
let resolverResponse;
|
|
212
|
+
if (!resolverAddress) {
|
|
213
|
+
;
|
|
214
|
+
({ domain } = await client.request(query, { id }));
|
|
215
|
+
resolverResponse = domain?.resolver;
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
const resolverId = `${resolverAddress}-${id}`;
|
|
219
|
+
({ resolver: resolverResponse, domain } = await client.request(customResolverQuery, { id, resolverId }));
|
|
220
|
+
}
|
|
221
|
+
if (!domain)
|
|
182
222
|
return;
|
|
183
|
-
const
|
|
223
|
+
const { isMigrated, createdAt } = domain;
|
|
184
224
|
let returnedRecords = {};
|
|
185
225
|
if (!resolverResponse)
|
|
186
226
|
return { isMigrated, createdAt };
|
|
@@ -188,7 +228,7 @@ const graphFetch = async ({ gqlInstance }, name, wantedRecords) => {
|
|
|
188
228
|
return {
|
|
189
229
|
isMigrated,
|
|
190
230
|
createdAt,
|
|
191
|
-
graphResolverAddress: resolverResponse.address,
|
|
231
|
+
graphResolverAddress: resolverResponse.address || resolverAddress,
|
|
192
232
|
};
|
|
193
233
|
Object.keys(wantedRecords).forEach((key) => {
|
|
194
234
|
const data = wantedRecords[key];
|
|
@@ -205,18 +245,22 @@ const graphFetch = async ({ gqlInstance }, name, wantedRecords) => {
|
|
|
205
245
|
...returnedRecords,
|
|
206
246
|
isMigrated,
|
|
207
247
|
createdAt,
|
|
208
|
-
graphResolverAddress: resolverResponse.address,
|
|
248
|
+
graphResolverAddress: resolverResponse.address || resolverAddress,
|
|
209
249
|
};
|
|
210
250
|
};
|
|
211
251
|
const getProfileFromName = async ({ contracts, gqlInstance, _getAddr, _getContentHash, _getText, resolverMulticallWrapper, multicallWrapper, }, name, options) => {
|
|
212
|
-
const
|
|
213
|
-
|
|
252
|
+
const { resolverAddress, ..._options } = options || {};
|
|
253
|
+
const optsLength = Object.keys(_options).length;
|
|
254
|
+
const usingOptions = !optsLength || _options?.texts === true || _options?.coinTypes === true
|
|
255
|
+
? optsLength
|
|
256
|
+
? _options
|
|
257
|
+
: { contentHash: true, texts: true, coinTypes: true }
|
|
214
258
|
: undefined;
|
|
215
|
-
const graphResult = await graphFetch({ gqlInstance }, name, usingOptions);
|
|
259
|
+
const graphResult = await graphFetch({ gqlInstance }, name, usingOptions, resolverAddress);
|
|
216
260
|
if (!graphResult)
|
|
217
261
|
return;
|
|
218
262
|
const { isMigrated, createdAt, graphResolverAddress, ...wantedRecords } = graphResult;
|
|
219
|
-
if (!graphResolverAddress)
|
|
263
|
+
if (!graphResolverAddress && !options?.resolverAddress)
|
|
220
264
|
return { isMigrated, createdAt, message: "Name doesn't have a resolver" };
|
|
221
265
|
const result = await getDataForName({
|
|
222
266
|
contracts,
|
|
@@ -225,7 +269,7 @@ const getProfileFromName = async ({ contracts, gqlInstance, _getAddr, _getConten
|
|
|
225
269
|
_getText,
|
|
226
270
|
resolverMulticallWrapper,
|
|
227
271
|
multicallWrapper,
|
|
228
|
-
}, name, usingOptions ? wantedRecords : options, graphResolverAddress);
|
|
272
|
+
}, name, usingOptions ? wantedRecords : options, graphResolverAddress, options?.resolverAddress);
|
|
229
273
|
if (!result)
|
|
230
274
|
return { isMigrated, createdAt, message: "Records fetch didn't complete" };
|
|
231
275
|
return { ...result, isMigrated, createdAt, message: undefined };
|
|
@@ -3,6 +3,7 @@ declare type ProfileOptions = {
|
|
|
3
3
|
contentHash?: boolean;
|
|
4
4
|
texts?: boolean | string[];
|
|
5
5
|
coinTypes?: boolean | string[];
|
|
6
|
+
resolverAddress?: string;
|
|
6
7
|
};
|
|
7
8
|
export default function ({ getProfile }: ENSArgs<'getProfile'>, name: string, options?: ProfileOptions): Promise<{
|
|
8
9
|
isMigrated: boolean | null;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ENSArgs } from '..';
|
|
2
|
+
import { RecordInput, RecordTypes } from '../utils/recordHelpers';
|
|
3
|
+
declare type BaseInput = {
|
|
4
|
+
resolverAddress?: string;
|
|
5
|
+
};
|
|
6
|
+
declare type ContentHashInput = {
|
|
7
|
+
record: RecordInput<'contentHash'>;
|
|
8
|
+
type: 'contentHash';
|
|
9
|
+
};
|
|
10
|
+
declare type AddrOrTextInput = {
|
|
11
|
+
record: RecordInput<'addr' | 'text'>;
|
|
12
|
+
type: 'addr' | 'text';
|
|
13
|
+
};
|
|
14
|
+
export default function <T extends RecordTypes>({ contracts, provider, getResolver, signer, }: ENSArgs<'contracts' | 'provider' | 'getResolver' | 'signer'>, name: string, { record, type, resolverAddress, }: BaseInput & (ContentHashInput | AddrOrTextInput)): Promise<{
|
|
15
|
+
to: string;
|
|
16
|
+
data: string;
|
|
17
|
+
}>;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { namehash } from '../utils/normalise';
|
|
2
|
+
import { generateSingleRecordCall, } from '../utils/recordHelpers';
|
|
3
|
+
export default async function ({ contracts, provider, getResolver, signer, }, name, { record, type, resolverAddress, }) {
|
|
4
|
+
if (!name.includes('.')) {
|
|
5
|
+
throw new Error('Input is not an ENS name');
|
|
6
|
+
}
|
|
7
|
+
let resolverToUse;
|
|
8
|
+
if (resolverAddress) {
|
|
9
|
+
resolverToUse = resolverAddress;
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
resolverToUse = await getResolver(name);
|
|
13
|
+
}
|
|
14
|
+
if (!resolverToUse) {
|
|
15
|
+
throw new Error('No resolver found for input address');
|
|
16
|
+
}
|
|
17
|
+
const resolver = (await contracts?.getPublicResolver(provider, resolverToUse))?.connect(signer);
|
|
18
|
+
const hash = namehash(name);
|
|
19
|
+
const call = generateSingleRecordCall(hash, resolver, type)(record);
|
|
20
|
+
return {
|
|
21
|
+
to: resolver.address,
|
|
22
|
+
data: call,
|
|
23
|
+
};
|
|
24
|
+
}
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type burnFuses from './functions/burnFuses';
|
|
|
7
7
|
import type createSubname from './functions/createSubname';
|
|
8
8
|
import type deleteSubname from './functions/deleteSubname';
|
|
9
9
|
import type setName from './functions/setName';
|
|
10
|
+
import type setRecord from './functions/setRecord';
|
|
10
11
|
import type setRecords from './functions/setRecords';
|
|
11
12
|
import type setResolver from './functions/setResolver';
|
|
12
13
|
import type transferName from './functions/transferName';
|
|
@@ -133,11 +134,13 @@ export declare class ENS {
|
|
|
133
134
|
}>;
|
|
134
135
|
decode: ({ multicallWrapper }: ENSArgs<"multicallWrapper">, data: string, ...items: BatchFunctionResult<RawFunction>[]) => Promise<any[] | undefined>;
|
|
135
136
|
}>;
|
|
136
|
-
getProfile: (nameOrAddress: string, options?: {
|
|
137
|
+
getProfile: (nameOrAddress: string, options?: ({
|
|
137
138
|
contentHash?: boolean | undefined;
|
|
138
139
|
texts?: boolean | string[] | undefined;
|
|
139
140
|
coinTypes?: boolean | string[] | undefined;
|
|
140
|
-
}
|
|
141
|
+
} & {
|
|
142
|
+
resolverAddress?: string | undefined;
|
|
143
|
+
}) | undefined) => Promise<{
|
|
141
144
|
isMigrated: boolean | null;
|
|
142
145
|
createdAt: string | null;
|
|
143
146
|
address?: string | undefined;
|
|
@@ -166,6 +169,7 @@ export declare class ENS {
|
|
|
166
169
|
contentHash?: boolean | undefined;
|
|
167
170
|
texts?: boolean | string[] | undefined;
|
|
168
171
|
coinTypes?: boolean | string[] | undefined;
|
|
172
|
+
resolverAddress?: string | undefined;
|
|
169
173
|
} | undefined) => Promise<{
|
|
170
174
|
isMigrated: boolean | null;
|
|
171
175
|
createdAt: string | null;
|
|
@@ -454,6 +458,7 @@ export declare class ENS {
|
|
|
454
458
|
}>;
|
|
455
459
|
setName: WriteFunction<typeof setName>;
|
|
456
460
|
setRecords: WriteFunction<typeof setRecords>;
|
|
461
|
+
setRecord: WriteFunction<typeof setRecord>;
|
|
457
462
|
setResolver: WriteFunction<typeof setResolver>;
|
|
458
463
|
transferName: WriteFunction<typeof transferName>;
|
|
459
464
|
wrapName: WriteFunction<typeof wrapName>;
|
package/dist/esm/index.js
CHANGED
|
@@ -53,7 +53,7 @@ export class ENS {
|
|
|
53
53
|
await thisRef.checkInitialProvider();
|
|
54
54
|
// import the module dynamically
|
|
55
55
|
const mod = await import(
|
|
56
|
-
/* webpackMode: "lazy", webpackChunkName: "[request]", webpackPreload: true */
|
|
56
|
+
/* webpackMode: "lazy", webpackChunkName: "[request]", webpackPreload: true, webpackExclude: /.*\.ts$/ */
|
|
57
57
|
`./functions/${path}`);
|
|
58
58
|
// if combine isn't specified, run normally
|
|
59
59
|
// otherwise, create a function from the raw and decode functions
|
|
@@ -188,6 +188,11 @@ export class ENS {
|
|
|
188
188
|
'contracts',
|
|
189
189
|
]);
|
|
190
190
|
this.setRecords = this.generateWriteFunction('setRecords', ['contracts', 'provider', 'getResolver']);
|
|
191
|
+
this.setRecord = this.generateWriteFunction('setRecord', [
|
|
192
|
+
'contracts',
|
|
193
|
+
'provider',
|
|
194
|
+
'getResolver',
|
|
195
|
+
]);
|
|
191
196
|
this.setResolver = this.generateWriteFunction('setResolver', ['contracts']);
|
|
192
197
|
this.transferName = this.generateWriteFunction('transferName', ['contracts']);
|
|
193
198
|
this.wrapName = this.generateWriteFunction('wrapName', [
|
package/dist/esm/utils/labels.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { solidityKeccak256 } from 'ethers/lib/utils';
|
|
2
2
|
import { truncateFormat } from './format';
|
|
3
|
+
const hasLocalStorage = typeof localStorage !== 'undefined';
|
|
3
4
|
export const labelhash = (input) => solidityKeccak256(['string'], [input]);
|
|
4
5
|
export const keccakFromString = (input) => labelhash(input);
|
|
5
6
|
export function decodeLabelhash(hash) {
|
|
@@ -24,12 +25,12 @@ export function isEncodedLabelhash(hash) {
|
|
|
24
25
|
return hash.startsWith('[') && hash.endsWith(']') && hash.length === 66;
|
|
25
26
|
}
|
|
26
27
|
function getLabels() {
|
|
27
|
-
return
|
|
28
|
+
return hasLocalStorage
|
|
28
29
|
? JSON.parse(localStorage.getItem('ensjs:labels')) || {}
|
|
29
30
|
: {};
|
|
30
31
|
}
|
|
31
32
|
function _saveLabel(hash, label) {
|
|
32
|
-
if (!
|
|
33
|
+
if (!hasLocalStorage)
|
|
33
34
|
return hash;
|
|
34
35
|
const labels = getLabels();
|
|
35
36
|
localStorage.setItem('ensjs:labels', JSON.stringify({
|
|
@@ -81,7 +82,7 @@ export function decryptName(name) {
|
|
|
81
82
|
}
|
|
82
83
|
export const truncateUndecryptedName = (name) => truncateFormat(name);
|
|
83
84
|
export function checkLocalStorageSize() {
|
|
84
|
-
if (!
|
|
85
|
+
if (!hasLocalStorage)
|
|
85
86
|
return 'Empty (0 KB)';
|
|
86
87
|
let allStrings = '';
|
|
87
88
|
for (const key in window.localStorage) {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export declare const normalise: (name: string) => any;
|
|
2
|
-
export declare const namehash: (
|
|
2
|
+
export declare const namehash: (name: string) => string;
|
|
@@ -1,15 +1,22 @@
|
|
|
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
|
const zeros = new Uint8Array(32);
|
|
4
5
|
zeros.fill(0);
|
|
5
6
|
export const normalise = (name) => name ? uts46.toUnicode(name, { useStd3ASCII: true }) : name;
|
|
6
|
-
export const namehash = (
|
|
7
|
+
export const namehash = (name) => {
|
|
7
8
|
let result = zeros;
|
|
8
|
-
const name = normalise(inputName);
|
|
9
9
|
if (name) {
|
|
10
10
|
const labels = name.split('.');
|
|
11
11
|
for (var i = labels.length - 1; i >= 0; i--) {
|
|
12
|
-
|
|
12
|
+
let labelSha;
|
|
13
|
+
if (isEncodedLabelhash(labels[i])) {
|
|
14
|
+
labelSha = decodeLabelhash(labels[i]);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
const normalised = normalise(labels[i]);
|
|
18
|
+
labelSha = keccak256(toUtf8Bytes(normalised));
|
|
19
|
+
}
|
|
13
20
|
result = keccak256(concat([result, labelSha]));
|
|
14
21
|
}
|
|
15
22
|
}
|
|
@@ -9,5 +9,8 @@ export declare type RecordOptions = {
|
|
|
9
9
|
coinTypes?: RecordItem[];
|
|
10
10
|
};
|
|
11
11
|
export declare const generateSetAddr: (namehash: string, coinType: string, address: string, resolver: PublicResolver) => string;
|
|
12
|
+
export declare type RecordTypes = 'contentHash' | 'text' | 'addr';
|
|
13
|
+
export declare type RecordInput<T extends RecordTypes> = T extends 'contentHash' ? string : RecordItem;
|
|
14
|
+
export declare function generateSingleRecordCall<T extends RecordTypes>(namehash: string, resolver: PublicResolver, type: T): (record: RecordInput<T>) => string;
|
|
12
15
|
export declare const generateRecordCallArray: (namehash: string, records: RecordOptions, resolver: PublicResolver) => string[];
|
|
13
16
|
export {};
|
|
@@ -12,28 +12,54 @@ export const generateSetAddr = (namehash, coinType, address, resolver) => {
|
|
|
12
12
|
const encodedAddress = coinTypeInstance.decoder(address);
|
|
13
13
|
return resolver?.interface.encodeFunctionData('setAddr(bytes32,uint256,bytes)', [namehash, inputCoinType, encodedAddress]);
|
|
14
14
|
};
|
|
15
|
+
export function generateSingleRecordCall(namehash, resolver, type) {
|
|
16
|
+
if (type === 'contentHash') {
|
|
17
|
+
return (_r) => {
|
|
18
|
+
const record = _r;
|
|
19
|
+
let _contentHash = '';
|
|
20
|
+
if (record !== _contentHash) {
|
|
21
|
+
const encoded = encodeContenthash(record);
|
|
22
|
+
if (encoded.error)
|
|
23
|
+
throw new Error(encoded.error);
|
|
24
|
+
_contentHash = encoded.encoded;
|
|
25
|
+
}
|
|
26
|
+
return resolver.interface.encodeFunctionData('setContenthash', [
|
|
27
|
+
namehash,
|
|
28
|
+
_contentHash,
|
|
29
|
+
]);
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
return (_r) => {
|
|
34
|
+
const record = _r;
|
|
35
|
+
if (type === 'text') {
|
|
36
|
+
return resolver.interface.encodeFunctionData('setText', [
|
|
37
|
+
namehash,
|
|
38
|
+
record.key,
|
|
39
|
+
record.value,
|
|
40
|
+
]);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
return generateSetAddr(namehash, record.key, record.value, resolver);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
15
48
|
export const generateRecordCallArray = (namehash, records, resolver) => {
|
|
16
49
|
const calls = [];
|
|
17
50
|
if (records.contentHash) {
|
|
18
|
-
const
|
|
19
|
-
const data = resolver?.interface.encodeFunctionData('setContenthash', [namehash, contentHash]);
|
|
51
|
+
const data = generateSingleRecordCall(namehash, resolver, 'contentHash')(records.contentHash);
|
|
20
52
|
data && calls.push(data);
|
|
21
53
|
}
|
|
22
54
|
if (records.texts && records.texts.length > 0) {
|
|
23
|
-
records.texts
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
key,
|
|
27
|
-
value,
|
|
28
|
-
]);
|
|
29
|
-
data && calls.push(data);
|
|
30
|
-
});
|
|
55
|
+
records.texts
|
|
56
|
+
.map(generateSingleRecordCall(namehash, resolver, 'text'))
|
|
57
|
+
.forEach((call) => calls.push(call));
|
|
31
58
|
}
|
|
32
59
|
if (records.coinTypes && records.coinTypes.length > 0) {
|
|
33
|
-
records.coinTypes
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
});
|
|
60
|
+
records.coinTypes
|
|
61
|
+
.map(generateSingleRecordCall(namehash, resolver, 'addr'))
|
|
62
|
+
.forEach((call) => calls.push(call));
|
|
37
63
|
}
|
|
38
64
|
return calls;
|
|
39
65
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ensdomains/ensjs",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.9",
|
|
4
4
|
"description": "ENS javascript library for contract interaction",
|
|
5
5
|
"main": "./dist/cjs/index.js",
|
|
6
6
|
"types": "./dist/cjs/index.d.ts",
|
|
@@ -46,12 +46,13 @@
|
|
|
46
46
|
"dns-packet": "^5.3.1",
|
|
47
47
|
"ethers": "^5.6.1",
|
|
48
48
|
"graphql": "^16.3.0",
|
|
49
|
-
"graphql-request": "
|
|
50
|
-
"idna-uts46-hx": "3.4.0"
|
|
49
|
+
"graphql-request": "next",
|
|
50
|
+
"idna-uts46-hx": "3.4.0",
|
|
51
|
+
"traverse": "^0.6.6"
|
|
51
52
|
},
|
|
52
53
|
"devDependencies": {
|
|
53
54
|
"@ensdomains/buffer": "^0.0.13",
|
|
54
|
-
"@ensdomains/ens-test-env": "0.1.
|
|
55
|
+
"@ensdomains/ens-test-env": "0.1.5",
|
|
55
56
|
"@ethersproject/abi": "^5.6.0",
|
|
56
57
|
"@ethersproject/providers": "^5.6.2",
|
|
57
58
|
"@nomiclabs/hardhat-ethers": "^2.0.5",
|
|
@@ -61,6 +62,7 @@
|
|
|
61
62
|
"@typechain/ethers-v5": "^10.0.0",
|
|
62
63
|
"@types/bn.js": "^5.1.0",
|
|
63
64
|
"@types/jest": "^27.4.1",
|
|
65
|
+
"@types/traverse": "^0.6.32",
|
|
64
66
|
"dotenv": "^16.0.0",
|
|
65
67
|
"ens-contracts": "github:ensdomains/ens-contracts#head=master&commit=3ecc56b14beb4aae8296f8a94f7c0d095e62fd93",
|
|
66
68
|
"hardhat": "^2.9.3",
|
|
@@ -81,5 +83,5 @@
|
|
|
81
83
|
"peerDependencies": {
|
|
82
84
|
"ethers": "*"
|
|
83
85
|
},
|
|
84
|
-
"stableVersion": "3.0.0-alpha.
|
|
86
|
+
"stableVersion": "3.0.0-alpha.8"
|
|
85
87
|
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import traverse from 'traverse'
|
|
2
|
+
import { visit, parse } from 'graphql'
|
|
3
|
+
|
|
4
|
+
import { requestMiddleware, responseMiddleware, enter } from './GqlManager'
|
|
5
|
+
import { namehash } from './utils/normalise'
|
|
6
|
+
|
|
7
|
+
describe('GqlManager', () => {
|
|
8
|
+
const queryWithoutId = `
|
|
9
|
+
query getNames($id: ID!, $expiryDate: Int) {
|
|
10
|
+
account(id: $id) {
|
|
11
|
+
registrations(first: 1000, where: { expiryDate_gt: $expiryDate }) {
|
|
12
|
+
registrationDate
|
|
13
|
+
expiryDate
|
|
14
|
+
domain {
|
|
15
|
+
labelName
|
|
16
|
+
labelhash
|
|
17
|
+
name
|
|
18
|
+
isMigrated
|
|
19
|
+
parent {
|
|
20
|
+
name
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
domains(first: 1000) {
|
|
25
|
+
labelName
|
|
26
|
+
labelhash
|
|
27
|
+
name
|
|
28
|
+
isMigrated
|
|
29
|
+
parent {
|
|
30
|
+
name
|
|
31
|
+
}
|
|
32
|
+
createdAt
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
`
|
|
37
|
+
|
|
38
|
+
const mockRequest = {
|
|
39
|
+
method: 'POST',
|
|
40
|
+
headers: {
|
|
41
|
+
'Content-Type': 'application/json',
|
|
42
|
+
},
|
|
43
|
+
body: JSON.stringify({ query: queryWithoutId }),
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const mockResponse = {
|
|
47
|
+
data: {
|
|
48
|
+
account: {
|
|
49
|
+
registrations: [
|
|
50
|
+
{
|
|
51
|
+
registrationDate: '1659245504',
|
|
52
|
+
expiryDate: '1690781504',
|
|
53
|
+
domain: {
|
|
54
|
+
id: '0xb1ccf7f6bb648f39ba48bf80f4a8a4522f474ee7d1edd6ea65f7fa51fe8b1612',
|
|
55
|
+
labelName: 'randydandy',
|
|
56
|
+
labelhash:
|
|
57
|
+
'0x63fd5bfdd0b1eb9f4c33c209b7c3e7dcb87a761d926a65e267f7cb99ee313ddd',
|
|
58
|
+
name: '0xde2551f43e950a2a4d4e6052f25edc450c48a5338faa27f09d1ee99ca9dc04fd',
|
|
59
|
+
isMigrated: true,
|
|
60
|
+
parent: {
|
|
61
|
+
name: '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae',
|
|
62
|
+
invalidName: true,
|
|
63
|
+
},
|
|
64
|
+
invalidName: true,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
registrationDate: '1635284756',
|
|
69
|
+
expiryDate: '1666841708',
|
|
70
|
+
domain: {
|
|
71
|
+
id: '0xde2551f43e950a2a4d4e6052f25edc450c48a5338faa27f09d1ee99ca9dc04fd',
|
|
72
|
+
labelName: 'randydandy',
|
|
73
|
+
labelhash:
|
|
74
|
+
'0x6a877a8d92ad83c8d044d0c5b69aa0da3050f4d653dcc149fca952d6439e4105',
|
|
75
|
+
name: 'randydandy.eth',
|
|
76
|
+
isMigrated: true,
|
|
77
|
+
parent: {
|
|
78
|
+
name: '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae',
|
|
79
|
+
invalidName: true,
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
registrationDate: '1659250465',
|
|
85
|
+
expiryDate: '1690786465',
|
|
86
|
+
domain: {
|
|
87
|
+
id: '0xaf119e72f050e5acad6b2d97826a7afb45afe9407376a2ab8240b62173b2d7c2',
|
|
88
|
+
labelName: 'nullbyte',
|
|
89
|
+
labelhash:
|
|
90
|
+
'0xc8985f8323b3dc707003a6ef5b379d9e8a058b6d6bcc2cfb307c61ca36920e27',
|
|
91
|
+
name: '0xb54c7c79c89d571f1fbf4c67f524e336a04441eeee4d76f156e835da99a46ddb',
|
|
92
|
+
isMigrated: true,
|
|
93
|
+
parent: {
|
|
94
|
+
name: '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae',
|
|
95
|
+
invalidName: true,
|
|
96
|
+
},
|
|
97
|
+
invalidName: true,
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
domains: [
|
|
102
|
+
{
|
|
103
|
+
id: '0xaf119e72f050e5acad6b2d97826a7afb45afe9407376a2ab8240b62173b2d7c2',
|
|
104
|
+
labelName: 'nullbyte',
|
|
105
|
+
labelhash:
|
|
106
|
+
'0xc8985f8323b3dc707003a6ef5b379d9e8a058b6d6bcc2cfb307c61ca36920e27',
|
|
107
|
+
name: '0xb54c7c79c89d571f1fbf4c67f524e336a04441eeee4d76f156e835da99a46ddb',
|
|
108
|
+
isMigrated: true,
|
|
109
|
+
parent: {
|
|
110
|
+
name: '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae',
|
|
111
|
+
invalidName: true,
|
|
112
|
+
},
|
|
113
|
+
createdAt: '1659250465',
|
|
114
|
+
invalidName: true,
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
id: '0xb1ccf7f6bb648f39ba48bf80f4a8a4522f474ee7d1edd6ea65f7fa51fe8b1612',
|
|
118
|
+
labelName: 'randydandy',
|
|
119
|
+
labelhash:
|
|
120
|
+
'0x63fd5bfdd0b1eb9f4c33c209b7c3e7dcb87a761d926a65e267f7cb99ee313ddd',
|
|
121
|
+
name: '0xde2551f43e950a2a4d4e6052f25edc450c48a5338faa27f09d1ee99ca9dc04fd',
|
|
122
|
+
isMigrated: true,
|
|
123
|
+
parent: {
|
|
124
|
+
name: '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae',
|
|
125
|
+
invalidName: true,
|
|
126
|
+
},
|
|
127
|
+
createdAt: '1659245504',
|
|
128
|
+
invalidName: true,
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
id: '0xde2551f43e950a2a4d4e6052f25edc450c48a5338faa27f09d1ee99ca9dc04fd',
|
|
132
|
+
labelName: 'randydandy',
|
|
133
|
+
labelhash:
|
|
134
|
+
'0x6a877a8d92ad83c8d044d0c5b69aa0da3050f4d653dcc149fca952d6439e4105',
|
|
135
|
+
name: 'randydandy.eth',
|
|
136
|
+
isMigrated: true,
|
|
137
|
+
parent: {
|
|
138
|
+
name: '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae',
|
|
139
|
+
invalidName: true,
|
|
140
|
+
},
|
|
141
|
+
createdAt: '1635284756',
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
headers: {
|
|
147
|
+
map: {
|
|
148
|
+
'content-type': 'application/json',
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
status: 200,
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
describe('requestMiddleware', () => {
|
|
155
|
+
it('should add id to a SelectionSet if name is present and id is not', () => {
|
|
156
|
+
const result = requestMiddleware(visit, parse)(mockRequest)
|
|
157
|
+
expect(result.body.includes('id')).toBe(true)
|
|
158
|
+
})
|
|
159
|
+
})
|
|
160
|
+
describe('responseMiddleware', () => {
|
|
161
|
+
it('should replace name with the namehash when there is an invalid name and id combo', () => {
|
|
162
|
+
const result = responseMiddleware(traverse)(mockResponse)
|
|
163
|
+
expect(result.data.account.domains[0].name).toBe(
|
|
164
|
+
namehash(
|
|
165
|
+
'0xb54c7c79c89d571f1fbf4c67f524e336a04441eeee4d76f156e835da99a46ddb',
|
|
166
|
+
),
|
|
167
|
+
)
|
|
168
|
+
})
|
|
169
|
+
})
|
|
170
|
+
})
|