@snapshot-labs/snapshot.js 0.12.55 → 0.12.56
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/snapshot.cjs.js +67 -2
- package/dist/snapshot.esm.js +67 -2
- package/dist/snapshot.min.js +1 -1
- package/dist/src/utils.d.ts +2 -2
- package/package.json +1 -1
- package/src/utils.spec.js +9 -1
- package/src/utils.ts +78 -4
package/dist/src/utils.d.ts
CHANGED
|
@@ -30,8 +30,8 @@ interface validateSchemaOptions {
|
|
|
30
30
|
export declare function validateSchema(schema: any, data: any, options?: validateSchemaOptions): true | import("ajv").ErrorObject<string, Record<string, any>, unknown>[] | null | undefined;
|
|
31
31
|
export declare function getEnsTextRecord(ens: string, record: string, network?: string, options?: any): Promise<string | null>;
|
|
32
32
|
export declare function getSpaceUri(id: string, network?: string, options?: any): Promise<string | null>;
|
|
33
|
-
export declare function getEnsOwner(ens: string, network?: string, options?: any): Promise<string
|
|
34
|
-
export declare function getSpaceController(id: string, network?: string, options?: any): Promise<string
|
|
33
|
+
export declare function getEnsOwner(ens: string, network?: string, options?: any): Promise<string>;
|
|
34
|
+
export declare function getSpaceController(id: string, network?: string, options?: any): Promise<string>;
|
|
35
35
|
export declare function clone(item: any): any;
|
|
36
36
|
export declare function sleep(time: any): Promise<unknown>;
|
|
37
37
|
export declare function getNumberWithOrdinal(n: any): string;
|
package/package.json
CHANGED
package/src/utils.spec.js
CHANGED
|
@@ -10,6 +10,8 @@ import {
|
|
|
10
10
|
getEnsTextRecord
|
|
11
11
|
} from './utils';
|
|
12
12
|
|
|
13
|
+
const EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
14
|
+
|
|
13
15
|
vi.mock('cross-fetch', async () => {
|
|
14
16
|
const actual = await vi.importActual('cross-fetch');
|
|
15
17
|
|
|
@@ -617,7 +619,13 @@ describe('utils', () => {
|
|
|
617
619
|
describe('getEnsOwner', () => {
|
|
618
620
|
test('should return null when the ENS is not valid', () => {
|
|
619
621
|
// special hidden characters after the k
|
|
620
|
-
expect(getEnsOwner('elonmusk.eth')).resolves.toBe(
|
|
622
|
+
expect(getEnsOwner('elonmusk.eth')).resolves.toBe(EMPTY_ADDRESS);
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
test('throw an error when the network is not supported', () => {
|
|
626
|
+
expect(getEnsOwner('shot.eth', '100')).rejects.toThrow(
|
|
627
|
+
'Network not supported'
|
|
628
|
+
);
|
|
621
629
|
});
|
|
622
630
|
});
|
|
623
631
|
|
package/src/utils.ts
CHANGED
|
@@ -30,6 +30,15 @@ interface Strategy {
|
|
|
30
30
|
params: any;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
type DomainType = 'ens' | 'tld' | 'other-tld' | 'subdomain';
|
|
34
|
+
|
|
35
|
+
const MUTED_ERRORS = [
|
|
36
|
+
// mute error from coinbase, when the subdomain is not found
|
|
37
|
+
// most other resolvers just return an empty address
|
|
38
|
+
'response not found during CCIP fetch',
|
|
39
|
+
// mute error from missing offchain resolver (mostly for sepolia)
|
|
40
|
+
'UNSUPPORTED_OPERATION'
|
|
41
|
+
];
|
|
33
42
|
const ENS_REGISTRY = '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e';
|
|
34
43
|
const ENS_ABI = [
|
|
35
44
|
'function text(bytes32 node, string calldata key) external view returns (string memory)',
|
|
@@ -219,6 +228,45 @@ ajv.addFormat('domain', {
|
|
|
219
228
|
}
|
|
220
229
|
});
|
|
221
230
|
|
|
231
|
+
function getDomainType(domain: string): DomainType {
|
|
232
|
+
const isEns = domain.endsWith('.eth');
|
|
233
|
+
|
|
234
|
+
const tokens = domain.split('.');
|
|
235
|
+
|
|
236
|
+
if (tokens.length === 1) return 'tld';
|
|
237
|
+
else if (tokens.length === 2 && !isEns) return 'other-tld';
|
|
238
|
+
else if (tokens.length > 2) return 'subdomain';
|
|
239
|
+
else if (isEns) return 'ens';
|
|
240
|
+
else throw new Error('Invalid domain');
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// see https://docs.ens.domains/registry/dns#gasless-import
|
|
244
|
+
async function getDNSOwner(domain: string): Promise<string> {
|
|
245
|
+
const response = await fetch(
|
|
246
|
+
`https://cloudflare-dns.com/dns-query?name=${domain}&type=TXT`,
|
|
247
|
+
{
|
|
248
|
+
headers: {
|
|
249
|
+
accept: 'application/dns-json'
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
const data = await response.json();
|
|
255
|
+
// Error list: https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6
|
|
256
|
+
if (data.Status === 3) return EMPTY_ADDRESS;
|
|
257
|
+
if (data.Status !== 0) throw new Error('Failed to fetch DNS Owner');
|
|
258
|
+
|
|
259
|
+
const ownerRecord = data.Answer?.find((record: any) =>
|
|
260
|
+
record.data.includes('ENS1')
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
if (!ownerRecord) return EMPTY_ADDRESS;
|
|
264
|
+
|
|
265
|
+
return getAddress(
|
|
266
|
+
ownerRecord.data.replace(new RegExp('"', 'g'), '').split(' ').pop()
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
|
|
222
270
|
export async function call(provider, abi: any[], call: any[], options?) {
|
|
223
271
|
const contract = new Contract(call[0], abi, provider);
|
|
224
272
|
try {
|
|
@@ -611,7 +659,12 @@ export async function getEnsOwner(
|
|
|
611
659
|
ens: string,
|
|
612
660
|
network = '1',
|
|
613
661
|
options: any = {}
|
|
614
|
-
): Promise<string
|
|
662
|
+
): Promise<string> {
|
|
663
|
+
if (!networks[network]?.ensResolvers?.length) {
|
|
664
|
+
throw new Error('Network not supported');
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
const domainType = getDomainType(ens);
|
|
615
668
|
const provider = getProvider(network, options);
|
|
616
669
|
const ensRegistry = new Contract(
|
|
617
670
|
ENS_REGISTRY,
|
|
@@ -624,7 +677,7 @@ export async function getEnsOwner(
|
|
|
624
677
|
try {
|
|
625
678
|
ensHash = namehash(ensNormalize(ens));
|
|
626
679
|
} catch (e: any) {
|
|
627
|
-
return
|
|
680
|
+
return EMPTY_ADDRESS;
|
|
628
681
|
}
|
|
629
682
|
|
|
630
683
|
const ensNameWrapper =
|
|
@@ -639,14 +692,35 @@ export async function getEnsOwner(
|
|
|
639
692
|
);
|
|
640
693
|
owner = await ensNameWrapperContract.ownerOf(ensHash);
|
|
641
694
|
}
|
|
642
|
-
|
|
695
|
+
|
|
696
|
+
if (owner === EMPTY_ADDRESS && domainType === 'other-tld') {
|
|
697
|
+
const resolvedAddress = await provider.resolveName(ens);
|
|
698
|
+
|
|
699
|
+
// Filter out domains with valid TXT records, but not imported
|
|
700
|
+
if (resolvedAddress) {
|
|
701
|
+
owner = await getDNSOwner(ens);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
if (owner === EMPTY_ADDRESS && domainType === 'subdomain') {
|
|
706
|
+
try {
|
|
707
|
+
owner = await provider.resolveName(ens);
|
|
708
|
+
} catch (e: any) {
|
|
709
|
+
if (MUTED_ERRORS.every((error) => !e.message.includes(error))) {
|
|
710
|
+
throw e;
|
|
711
|
+
}
|
|
712
|
+
owner = EMPTY_ADDRESS;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
return owner || EMPTY_ADDRESS;
|
|
643
717
|
}
|
|
644
718
|
|
|
645
719
|
export async function getSpaceController(
|
|
646
720
|
id: string,
|
|
647
721
|
network = '1',
|
|
648
722
|
options: any = {}
|
|
649
|
-
): Promise<string
|
|
723
|
+
): Promise<string> {
|
|
650
724
|
const spaceUri = await getSpaceUri(id, network, options);
|
|
651
725
|
if (spaceUri) {
|
|
652
726
|
let isUriAddress = isAddress(spaceUri);
|