Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3518223
D13699.id45182.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
43 KB
Referenced Files
None
Subscribers
None
D13699.id45182.diff
View Options
diff --git a/keyserver/package.json b/keyserver/package.json
--- a/keyserver/package.json
+++ b/keyserver/package.json
@@ -89,6 +89,7 @@
"tcomb": "^3.2.29",
"twin-bcrypt": "^2.1.1",
"uuid": "^3.4.0",
+ "viem": "^2.9.5",
"web": "0.0.1",
"web-push": "^3.5.0",
"ws": "^8.13.0"
diff --git a/keyserver/src/utils/ens-cache.js b/keyserver/src/utils/ens-cache.js
--- a/keyserver/src/utils/ens-cache.js
+++ b/keyserver/src/utils/ens-cache.js
@@ -1,6 +1,10 @@
// @flow
+import { addEnsContracts } from '@ensdomains/ensjs';
import { AlchemyProvider } from 'ethers';
+import { createClient } from 'viem';
+// eslint-disable-next-line import/extensions
+import { mainnet } from 'viem/chains';
import { getCommConfig } from 'lib/utils/comm-config.js';
import { ENSCache } from 'lib/utils/ens-cache.js';
@@ -8,6 +12,8 @@
getENSNames as baseGetENSNames,
type GetENSNames,
} from 'lib/utils/ens-helpers.js';
+import { ENSWrapper } from 'lib/utils/ens-wrapper.js';
+import { getAlchemyMainnetViemTransport } from 'lib/utils/viem-utils.js';
type AlchemyConfig = { +key: string };
type BaseUserInfo = { +username?: ?string, ... };
@@ -22,8 +28,14 @@
if (!alchemyKey) {
return;
}
- const provider = new AlchemyProvider('mainnet', alchemyKey);
- const ensCache = new ENSCache(provider);
+ const viemTransport = getAlchemyMainnetViemTransport(alchemyKey);
+ const viemClient = createClient({
+ chain: addEnsContracts(mainnet),
+ transport: viemTransport,
+ });
+ const ethersProvider = new AlchemyProvider('mainnet', alchemyKey);
+ const ensWrapper = new ENSWrapper(viemClient, ethersProvider);
+ const ensCache = new ENSCache(ensWrapper);
getENSNames = <T: ?BaseUserInfo>(users: $ReadOnlyArray<T>): Promise<T[]> =>
baseGetENSNames(ensCache, users);
}
diff --git a/lib/components/ens-cache-provider.react.js b/lib/components/ens-cache-provider.react.js
--- a/lib/components/ens-cache-provider.react.js
+++ b/lib/components/ens-cache-provider.react.js
@@ -1,6 +1,10 @@
// @flow
+import { addEnsContracts } from '@ensdomains/ensjs';
import * as React from 'react';
+import { createClient } from 'viem';
+// eslint-disable-next-line import/extensions
+import { mainnet } from 'viem/chains';
import type { EthersProvider } from '../types/ethers-types.js';
import { ENSCache } from '../utils/ens-cache.js';
@@ -8,6 +12,8 @@
getENSNames as baseGetENSNames,
type GetENSNames,
} from '../utils/ens-helpers.js';
+import { ENSWrapper } from '../utils/ens-wrapper.js';
+import { getAlchemyMainnetViemTransport } from '../utils/viem-utils.js';
type BaseUserInfo = { +username?: ?string, ... };
@@ -23,21 +29,28 @@
React.createContext<ENSCacheContextType>(defaultContext);
type Props = {
- +provider: ?EthersProvider,
+ +ethersProvider: ?EthersProvider,
+ +alchemyKey: ?string,
+children: React.Node,
};
function ENSCacheProvider(props: Props): React.Node {
- const { provider, children } = props;
+ const { ethersProvider, alchemyKey, children } = props;
const context = React.useMemo(() => {
- if (!provider) {
+ if (!ethersProvider) {
return defaultContext;
}
- const ensCache = new ENSCache(provider);
+ const viemTransport = getAlchemyMainnetViemTransport(alchemyKey);
+ const viemClient = createClient({
+ chain: addEnsContracts(mainnet),
+ transport: viemTransport,
+ });
+ const ensWrapper = new ENSWrapper(viemClient, ethersProvider);
+ const ensCache = new ENSCache(ensWrapper);
const getENSNames: GetENSNames = <T: ?BaseUserInfo>(
users: $ReadOnlyArray<T>,
): Promise<T[]> => baseGetENSNames(ensCache, users);
return { ensCache, getENSNames };
- }, [provider]);
+ }, [ethersProvider, alchemyKey]);
return (
<ENSCacheContext.Provider value={context}>
{children}
diff --git a/lib/flow-typed/npm/@ensdomains/ensjs_vx.x.x.js b/lib/flow-typed/npm/@ensdomains/ensjs_vx.x.x.js
new file mode 100644
--- /dev/null
+++ b/lib/flow-typed/npm/@ensdomains/ensjs_vx.x.x.js
@@ -0,0 +1,62 @@
+// flow-typed signature: 3c7fa5a7cb8e52f2d81c14972c6d689f
+// flow-typed version: <<STUB>>/@ensdomains/ensjs_v4.0.1/flow_v0.202.1
+
+declare module '@ensdomains/ensjs' {
+ import type { ViemChain } from 'viem';
+
+ declare export function addEnsContracts(chain: ViemChain): ViemChain;
+}
+
+declare module '@ensdomains/ensjs/public' {
+ import type { ViemClient } from 'viem';
+
+ declare export type BatchHandle<T> = {
+ ...
+ };
+
+ declare export type GetNameResult = {
+ +match: boolean,
+ +name: ?string,
+ +reverseResolverAddress: ?string,
+ +resolverAddress: ?string,
+ ...
+ };
+ declare export var getName:
+ & {
+ +batch: ({ +address: string, ... }) => BatchHandle<GetNameResult>,
+ ...
+ }
+ & (ViemClient, { +address: string, ... }) => Promise<GetNameResult>;
+
+ declare export type GetOwnerResult = {
+ +owner: ?string,
+ +registrant: ?string,
+ +ownershipLevel: ?string,
+ ...
+ };
+ declare export var getOwner:
+ & {
+ +batch: ({ +name: string, ... }) => BatchHandle<GetOwnerResult>,
+ ...
+ }
+ & (ViemClient, { +name: string, ... }) => Promise<GetOwnerResult>;
+
+ declare export type GetRecordsResult = {
+ +texts: $ReadOnlyArray<{
+ +key: string,
+ +value: string,
+ }>,
+ ...
+ };
+ declare export var getRecords:
+ & {
+ +batch: ({ +name: string, +texts: $ReadOnlyArray<string>, ... }) => BatchHandle<GetRecordsResult>,
+ ...
+ }
+ & (ViemClient, { +name: string, +texts: $ReadOnlyArray<string>, ... }) => Promise<GetRecordsResult>;
+
+ declare export function batch<T>(
+ client: ViemClient,
+ ...batchCalls: $ReadOnlyArray<BatchHandle<T>>
+ ): Promise<Array<T>>;
+}
diff --git a/lib/flow-typed/npm/viem_vx.x.x.js b/lib/flow-typed/npm/viem_vx.x.x.js
new file mode 100644
--- /dev/null
+++ b/lib/flow-typed/npm/viem_vx.x.x.js
@@ -0,0 +1,32 @@
+// flow-typed signature: 585e2883477434dc7cfd534337e85dce
+// flow-typed version: <<STUB>>/viem_v2.9.5/flow_v0.202.1
+
+declare module 'viem' {
+
+ declare export type ViemTransport = { ... };
+
+ declare export function http(url?: string): ViemTransport;
+
+ declare export type ViemChain = { ... };
+
+ declare export type ViemCreateClientParams = {
+ +chain: ViemChain,
+ +transport: ViemTransport,
+ ...
+ };
+
+ declare export type ViemClient = {
+ ...
+ };
+
+ declare export function createClient(
+ params: ViemCreateClientParams,
+ ): ViemClient;
+}
+
+declare module 'view/chains' {
+ import type { ViemChain } from 'viem';
+
+ declare export var mainnet: ViemChain;
+ declare export var sepolia: ViemChain;
+}
diff --git a/lib/package.json b/lib/package.json
--- a/lib/package.json
+++ b/lib/package.json
@@ -36,6 +36,7 @@
},
"dependencies": {
"@commapp/olm": "0.2.1",
+ "@ensdomains/ensjs": "^4.0.1",
"@rainbow-me/rainbowkit": "^2.0.7",
"base-64": "^0.1.0",
"dateformat": "^3.0.3",
diff --git a/lib/utils/ens-cache.js b/lib/utils/ens-cache.js
--- a/lib/utils/ens-cache.js
+++ b/lib/utils/ens-cache.js
@@ -1,20 +1,13 @@
// @flow
import namehash from 'eth-ens-namehash';
-import { Contract } from 'ethers';
-import invariant from 'invariant';
-
-import {
- resolverABI,
- resolverAddresses,
- type ReverseRecordsEthersSmartContract,
-} from './reverse-records.js';
+
+import { ENSWrapper } from './ens-wrapper.js';
import sleep from './sleep.js';
-import type { EthersProvider } from '../types/ethers-types.js';
const cacheTimeout = 24 * 60 * 60 * 1000; // one day
const failedQueryCacheTimeout = 5 * 60 * 1000; // five minutes
-const queryTimeout = 10 * 1000; // ten seconds
+const queryTimeout = 30 * 1000; // ten seconds
async function throwOnTimeout(identifier: string) {
await sleep(queryTimeout);
@@ -56,9 +49,7 @@
// caching behavior consistent across platforms, we instead introduce this
// vanilla JS class that handles querying and caching ENS for all cases.
class ENSCache {
- provider: EthersProvider;
- batchReverseResolverSmartContract: ?ReverseRecordsEthersSmartContract;
- batchReverseResolverSmartContractPromise: Promise<?ReverseRecordsEthersSmartContract>;
+ ensWrapper: ENSWrapper;
// Maps from normalized ETH address to a cache entry for its name
nameQueryCache: Map<string, ENSNameQueryCacheEntry> = new Map();
@@ -67,22 +58,8 @@
// Maps from normalized ETH address to a cache entry for its avatar
avatarQueryCache: Map<string, ENSAvatarQueryCacheEntry> = new Map();
- constructor(provider: EthersProvider) {
- this.provider = provider;
- this.batchReverseResolverSmartContractPromise = (async () => {
- const { chainId } = await provider.getNetwork();
- const reverseRecordsAddress = resolverAddresses[chainId];
- if (reverseRecordsAddress) {
- this.batchReverseResolverSmartContract = new Contract(
- reverseRecordsAddress,
- resolverABI,
- provider,
- );
- } else {
- this.batchReverseResolverSmartContract = null;
- }
- return this.batchReverseResolverSmartContract;
- })();
+ constructor(ensWrapper: ENSWrapper) {
+ this.ensWrapper = ensWrapper;
}
// Getting a name for an ETH address is referred to as "reverse resolution".
@@ -109,7 +86,7 @@
let ensName;
try {
ensName = await Promise.race([
- this.provider.lookupAddress(normalizedETHAddress),
+ this.ensWrapper.getNameForAddress(normalizedETHAddress),
throwOnTimeout(`${normalizedETHAddress}'s name`),
]);
} catch (e) {
@@ -174,39 +151,16 @@
}
const fetchENSNamesPromise = (async () => {
- const {
- batchReverseResolverSmartContract,
- batchReverseResolverSmartContractPromise,
- } = this;
-
- let smartContract;
- if (batchReverseResolverSmartContract) {
- smartContract = batchReverseResolverSmartContract;
- } else if (batchReverseResolverSmartContract !== null) {
- smartContract = await batchReverseResolverSmartContractPromise;
- }
-
- // ReverseRecords smart contract handles checking forward resolution
let ensNames: $ReadOnlyArray<?string>;
- if (smartContract) {
- try {
- const raceResult = await Promise.race([
- smartContract['getNames(address[])'](needFetch),
- throwOnTimeout(`names for ${JSON.stringify(needFetch)}`),
- ]);
- invariant(
- Array.isArray(raceResult),
- 'ReverseRecords smart contract should return array',
- );
- ensNames = raceResult;
- } catch (e) {
- console.log(e);
- ensNames = new Array<?string>(needFetch.length).fill(null);
- }
- } else {
- ensNames = await Promise.all(
- needFetch.map(ethAddress => this.getNameForAddress(ethAddress)),
- );
+ try {
+ const raceResult = await Promise.race([
+ this.ensWrapper.getNamesForAddresses(needFetch),
+ throwOnTimeout(`names for ${JSON.stringify(needFetch)}`),
+ ]);
+ ensNames = raceResult;
+ } catch (e) {
+ console.log(e);
+ ensNames = new Array<?string>(needFetch.length).fill(null);
}
const resultMap = new Map<string, ?string>();
@@ -315,7 +269,7 @@
let ethAddress;
try {
ethAddress = await Promise.race([
- this.provider.resolveName(normalizedENSName),
+ this.ensWrapper.getAddressForName(normalizedENSName),
throwOnTimeout(`${normalizedENSName}'s address`),
]);
} catch (e) {
@@ -398,7 +352,7 @@
let ensAvatar;
try {
ensAvatar = await Promise.race([
- this.provider.getAvatar(ensName),
+ this.ensWrapper.getAvatarURIForName(ensName),
throwOnTimeout(`${normalizedETHAddress}'s avatar`),
]);
} catch (e) {
diff --git a/lib/utils/ens-cache.test.js b/lib/utils/ens-cache.test.js
--- a/lib/utils/ens-cache.test.js
+++ b/lib/utils/ens-cache.test.js
@@ -1,32 +1,74 @@
// @flow
+import { addEnsContracts } from '@ensdomains/ensjs';
import { AlchemyProvider } from 'ethers';
+import { createClient } from 'viem';
+// eslint-disable-next-line import/extensions
+import { mainnet, sepolia } from 'viem/chains';
import { ENSCache } from './ens-cache.js';
+import { ENSWrapper } from './ens-wrapper.js';
+import {
+ getAlchemyMainnetViemTransport,
+ getAlchemySepoliaViemTransport,
+} from './viem-utils.js';
-const provider = new AlchemyProvider('sepolia', process.env.ALCHEMY_API_KEY);
-const ensCache = new ENSCache(provider);
-
-const baseLookupAddress = provider.lookupAddress.bind(provider);
-let timesLookupAddressCalled = 0;
-provider.lookupAddress = (ethAddress: string) => {
- timesLookupAddressCalled++;
- return baseLookupAddress(ethAddress);
-};
-
-const baseResolveName = provider.resolveName.bind(provider);
-let timesResolveNameCalled = 0;
-provider.resolveName = (ensName: string) => {
- timesResolveNameCalled++;
- return baseResolveName(ensName);
-};
-
-const baseGetAvatar = provider.getAvatar.bind(provider);
-let timesGetAvatarCalled = 0;
-provider.getAvatar = (ethAddress: string) => {
- timesGetAvatarCalled++;
- return baseGetAvatar(ethAddress);
-};
+const sepoliaEthersProvider = new AlchemyProvider(
+ 'sepolia',
+ process.env.ALCHEMY_API_KEY,
+);
+
+const sepoliaViemTransport = getAlchemySepoliaViemTransport(
+ process.env.ALCHEMY_API_KEY,
+);
+const sepoliaViemClient = createClient({
+ chain: addEnsContracts(sepolia),
+ transport: sepoliaViemTransport,
+});
+
+const baseSepoliaENSWrapper = new ENSWrapper(
+ sepoliaViemClient,
+ sepoliaEthersProvider,
+);
+
+let timesGetAddressForNameCalled = 0;
+let timesGetNameForAddressCalled = 0;
+let timesGetAvatarURIForNameCalled = 0;
+const sepoliaENSWrapper: ENSWrapper = ({
+ ...baseSepoliaENSWrapper,
+ getAddressForName: (ethAddress: string) => {
+ timesGetAddressForNameCalled++;
+ return baseSepoliaENSWrapper.getAddressForName(ethAddress);
+ },
+ getNameForAddress: (ensName: string) => {
+ timesGetNameForAddressCalled++;
+ return baseSepoliaENSWrapper.getNameForAddress(ensName);
+ },
+ getAvatarURIForName: (ensName: string) => {
+ timesGetAvatarURIForNameCalled++;
+ return baseSepoliaENSWrapper.getAvatarURIForName(ensName);
+ },
+}: any);
+const ensCache = new ENSCache(sepoliaENSWrapper);
+
+const mainnetEthersProvider = new AlchemyProvider(
+ 'mainnet',
+ process.env.ALCHEMY_API_KEY,
+);
+
+const mainnetViemTransport = getAlchemyMainnetViemTransport(
+ process.env.ALCHEMY_API_KEY,
+);
+const mainnetViemClient = createClient({
+ chain: addEnsContracts(mainnet),
+ transport: mainnetViemTransport,
+});
+
+const mainnetENSWrapper = new ENSWrapper(
+ mainnetViemClient,
+ mainnetEthersProvider,
+);
+const mainnetENSCache = new ENSCache(mainnetENSWrapper);
if (!process.env.ALCHEMY_API_KEY) {
// Test only works if we can query blockchain
@@ -52,9 +94,13 @@
const noENSNameAddr = '0xcF986104d869967381dFfAb3A4127bCe6a404362';
+const nfthreatBaseName = 'nfthreat.base.eth';
+const nfthreatBaseAddr = '0x598C91d70e16177defB34CbA95C2f80F551A4ccB';
+
describe('getNameForAddress', () => {
beforeAll(() => {
ensCache.clearCache();
+ mainnetENSCache.clearCache();
});
it('should fail to return ashoat.eth if not in cache', async () => {
if (!process.env.ALCHEMY_API_KEY) {
@@ -70,6 +116,14 @@
const ashoatEthResult = await ensCache.getNameForAddress(ashoatAddr);
expect(ashoatEthResult).toBe(ashoatDotEth);
});
+ it('should return nfthreat.base.eth', async () => {
+ if (!process.env.ALCHEMY_API_KEY) {
+ return;
+ }
+ const nfthreatBaseResult =
+ await mainnetENSCache.getNameForAddress(nfthreatBaseAddr);
+ expect(nfthreatBaseResult).toBe(nfthreatBaseName);
+ }, 10000);
it('should return ashoat.eth if in cache', async () => {
if (!process.env.ALCHEMY_API_KEY) {
return;
@@ -81,12 +135,12 @@
if (!process.env.ALCHEMY_API_KEY) {
return;
}
- const timesLookupAddressCalledBefore = timesLookupAddressCalled;
+ const timesLookupAddressCalledBefore = timesGetAddressForNameCalled;
const ashoatEthResult = await ensCache.getNameForAddress(
ashoatAddr.toUpperCase(),
);
expect(ashoatEthResult).toBe(ashoatDotEth);
- expect(timesLookupAddressCalled).toBe(timesLookupAddressCalledBefore);
+ expect(timesGetAddressForNameCalled).toBe(timesLookupAddressCalledBefore);
});
it('should dedup simultaneous fetches', async () => {
if (!process.env.ALCHEMY_API_KEY) {
@@ -94,14 +148,16 @@
}
ensCache.clearCache();
- const timesLookupAddressCalledBeforeSingleFetch = timesLookupAddressCalled;
+ const timesLookupAddressCalledBeforeSingleFetch =
+ timesGetAddressForNameCalled;
const ashoatEthResult1 = await ensCache.getNameForAddress(ashoatAddr);
expect(ashoatEthResult1).toBe(ashoatDotEth);
const timesLookupAddressCalledForSingleFetch =
- timesLookupAddressCalled - timesLookupAddressCalledBeforeSingleFetch;
+ timesGetAddressForNameCalled - timesLookupAddressCalledBeforeSingleFetch;
ensCache.clearCache();
- const timesLookupAddressCalledBeforeDoubleFetch = timesLookupAddressCalled;
+ const timesLookupAddressCalledBeforeDoubleFetch =
+ timesGetAddressForNameCalled;
const [ashoatEthResult2, ashoatEthResult3] = await Promise.all([
ensCache.getNameForAddress(ashoatAddr),
ensCache.getNameForAddress(ashoatAddr.toUpperCase()),
@@ -109,7 +165,7 @@
expect(ashoatEthResult2).toBe(ashoatDotEth);
expect(ashoatEthResult3).toBe(ashoatDotEth);
const timesLookupAddressCalledForDoubleFetch =
- timesLookupAddressCalled - timesLookupAddressCalledBeforeDoubleFetch;
+ timesGetAddressForNameCalled - timesLookupAddressCalledBeforeDoubleFetch;
expect(timesLookupAddressCalledForDoubleFetch).toBe(
timesLookupAddressCalledForSingleFetch,
@@ -120,6 +176,7 @@
describe('getNamesForAddresses', () => {
beforeAll(() => {
ensCache.clearCache();
+ mainnetENSCache.clearCache();
});
it('should fail to return ashoat.eth if not in cache', async () => {
if (!process.env.ALCHEMY_API_KEY) {
@@ -135,6 +192,15 @@
const [ashoatEthResult] = await ensCache.getNamesForAddresses([ashoatAddr]);
expect(ashoatEthResult).toBe(ashoatDotEth);
});
+ it('should return nfthreat.base.eth', async () => {
+ if (!process.env.ALCHEMY_API_KEY) {
+ return;
+ }
+ const [nfthreatBaseResult] = await mainnetENSCache.getNamesForAddresses([
+ nfthreatBaseAddr,
+ ]);
+ expect(nfthreatBaseResult).toBe(nfthreatBaseName);
+ }, 10000);
it('should return ashoat.eth if in cache', async () => {
if (!process.env.ALCHEMY_API_KEY) {
return;
@@ -162,7 +228,7 @@
}
ensCache.clearCache();
- const timesLookupAddressCalledBefore = timesLookupAddressCalled;
+ const timesLookupAddressCalledBefore = timesGetAddressForNameCalled;
const [
[ashoatEthResult1, commalphaEthResult1, commbetaEthResult1],
@@ -180,17 +246,11 @@
ensCache.getNameForAddress(commbetaEthAddr),
]);
- const timesLookupAddressCalledAfter = timesLookupAddressCalled;
+ const timesLookupAddressCalledAfter = timesGetAddressForNameCalled;
const timesLookupAddressCalledDuringTest =
timesLookupAddressCalledAfter - timesLookupAddressCalledBefore;
- // These tests are run on the Sepolia testnet, where the ReverseRecords
- // smart contract is not deployed. As a result, we end up needing to call
- // the lookupAddress method (single lookup) once for each address. On
- // mainnet (outside of these tests) this is 0, since the ReverseRecords
- // smart contract lets us batch up our requests, and avoid calling
- // lookupAddress entirely.
- expect(timesLookupAddressCalledDuringTest).toBe(3);
+ expect(timesLookupAddressCalledDuringTest).toBe(0);
expect(ashoatEthResult1).toBe(ashoatDotEth);
expect(commalphaEthResult1).toBe(commalphaDotEth);
@@ -211,6 +271,7 @@
describe('getAddressForName', () => {
beforeAll(() => {
ensCache.clearCache();
+ mainnetENSCache.clearCache();
});
it("should fail to return ashoat.eth's address if not in cache", async () => {
if (!process.env.ALCHEMY_API_KEY) {
@@ -237,10 +298,10 @@
if (!process.env.ALCHEMY_API_KEY) {
return;
}
- const timesResolveNameCalledBefore = timesResolveNameCalled;
+ const timesResolveNameCalledBefore = timesGetNameForAddressCalled;
const ashoatAddrResult = await ensCache.getAddressForName(ashoatDotEth);
expect(ashoatAddrResult).toBe(ashoatAddr);
- expect(timesResolveNameCalled).toBe(timesResolveNameCalledBefore);
+ expect(timesGetNameForAddressCalled).toBe(timesResolveNameCalledBefore);
});
it('should dedup simultaneous fetches', async () => {
if (!process.env.ALCHEMY_API_KEY) {
@@ -248,14 +309,16 @@
}
ensCache.clearCache();
- const timesResolveNameCalledBeforeSingleFetch = timesResolveNameCalled;
+ const timesResolveNameCalledBeforeSingleFetch =
+ timesGetNameForAddressCalled;
const ashoatAddrResult1 = await ensCache.getAddressForName(ashoatDotEth);
expect(ashoatAddrResult1).toBe(ashoatAddr);
const timesResolveNameCalledForSingleFetch =
- timesResolveNameCalled - timesResolveNameCalledBeforeSingleFetch;
+ timesGetNameForAddressCalled - timesResolveNameCalledBeforeSingleFetch;
ensCache.clearCache();
- const timesResolveNameCalledBeforeDoubleFetch = timesResolveNameCalled;
+ const timesResolveNameCalledBeforeDoubleFetch =
+ timesGetNameForAddressCalled;
const [ashoatAddrResult2, ashoatAddrResult3] = await Promise.all([
ensCache.getAddressForName(ashoatDotEth),
ensCache.getAddressForName(ashoatDotEth),
@@ -263,7 +326,7 @@
expect(ashoatAddrResult2).toBe(ashoatAddr);
expect(ashoatAddrResult3).toBe(ashoatAddr);
const timesResolveNamesCalledForDoubleFetch =
- timesResolveNameCalled - timesResolveNameCalledBeforeDoubleFetch;
+ timesGetNameForAddressCalled - timesResolveNameCalledBeforeDoubleFetch;
expect(timesResolveNamesCalledForDoubleFetch).toBe(
timesResolveNameCalledForSingleFetch,
@@ -303,11 +366,11 @@
if (!process.env.ALCHEMY_API_KEY) {
return;
}
- const timesGetAvatarCalledBefore = timesGetAvatarCalled;
+ const timesGetAvatarCalledBefore = timesGetAvatarURIForNameCalled;
const ashoatAvatarResult =
await ensCache.getAvatarURIForAddress(ashoatAddr);
expect(ashoatAvatarResult).toBe(ashoatAvatar);
- expect(timesGetAvatarCalled).toBe(timesGetAvatarCalledBefore);
+ expect(timesGetAvatarURIForNameCalled).toBe(timesGetAvatarCalledBefore);
});
it('should dedup simultaneous fetches', async () => {
if (!process.env.ALCHEMY_API_KEY) {
@@ -315,15 +378,17 @@
}
ensCache.clearCache();
- const timesGetAvatarCalledBeforeSingleFetch = timesGetAvatarCalled;
+ const timesGetAvatarCalledBeforeSingleFetch =
+ timesGetAvatarURIForNameCalled;
const ashoatAvatarResult1 =
await ensCache.getAvatarURIForAddress(ashoatAddr);
expect(ashoatAvatarResult1).toBe(ashoatAvatar);
const timesGetAvatarCalledForSingleFetch =
- timesGetAvatarCalled - timesGetAvatarCalledBeforeSingleFetch;
+ timesGetAvatarURIForNameCalled - timesGetAvatarCalledBeforeSingleFetch;
ensCache.clearCache();
- const timesGetAvatarCalledBeforeDoubleFetch = timesGetAvatarCalled;
+ const timesGetAvatarCalledBeforeDoubleFetch =
+ timesGetAvatarURIForNameCalled;
const [ashoatAvatarResult2, ashoatAvatarResult3] = await Promise.all([
ensCache.getAvatarURIForAddress(ashoatAddr),
ensCache.getAvatarURIForAddress(ashoatAddr),
@@ -331,7 +396,7 @@
expect(ashoatAvatarResult2).toBe(ashoatAvatar);
expect(ashoatAvatarResult3).toBe(ashoatAvatar);
const timesGetAvatarCalledForDoubleFetch =
- timesGetAvatarCalled - timesGetAvatarCalledBeforeDoubleFetch;
+ timesGetAvatarURIForNameCalled - timesGetAvatarCalledBeforeDoubleFetch;
expect(timesGetAvatarCalledForDoubleFetch).toBe(
timesGetAvatarCalledForSingleFetch,
diff --git a/lib/utils/ens-wrapper.js b/lib/utils/ens-wrapper.js
new file mode 100644
--- /dev/null
+++ b/lib/utils/ens-wrapper.js
@@ -0,0 +1,49 @@
+// @flow
+
+import {
+ batch,
+ getName,
+ type GetNameResult,
+ getOwner,
+ // eslint-disable-next-line import/extensions
+} from '@ensdomains/ensjs/public';
+import type { ViemClient } from 'viem';
+
+import type { EthersProvider } from '../types/ethers-types.js';
+
+class ENSWrapper {
+ viemClient: ViemClient;
+ ethersProvider: EthersProvider;
+
+ constructor(viemClient: ViemClient, ethersProvider: EthersProvider) {
+ this.viemClient = viemClient;
+ this.ethersProvider = ethersProvider;
+ }
+
+ getNameForAddress: string => Promise<?string> = async ethAddress => {
+ const result = await getName(this.viemClient, { address: ethAddress });
+ return result ? result.name : undefined;
+ };
+
+ getNamesForAddresses: ($ReadOnlyArray<string>) => Promise<Array<?string>> =
+ async ethAddresses => {
+ const results = await batch<GetNameResult>(
+ this.viemClient,
+ ...ethAddresses.map(address => getName.batch({ address })),
+ );
+ return results.map(result => (result ? result.name : undefined));
+ };
+
+ getAddressForName: string => Promise<?string> = async ensName => {
+ const result = await getOwner(this.viemClient, { name: ensName });
+ return result ? result.owner : undefined;
+ };
+
+ // @ensdomains/ensjs doesn't handle resolving ipfs and eip155 URIs to HTTP
+ // URIs, so we use Ethers.js instead
+ getAvatarURIForName: string => Promise<?string> = async ensName => {
+ return await this.ethersProvider.getAvatar(ensName);
+ };
+}
+
+export { ENSWrapper };
diff --git a/lib/utils/reverse-records.js b/lib/utils/reverse-records.js
deleted file mode 100644
--- a/lib/utils/reverse-records.js
+++ /dev/null
@@ -1,45 +0,0 @@
-// @flow
-
-type ABIParam = {
- +internalType: string,
- +name: string,
- +type: string,
-};
-type EthereumSmartContractABI = $ReadOnlyArray<{
- +inputs: $ReadOnlyArray<ABIParam>,
- +stateMutability: string,
- +type: string,
- +name?: ?string,
- +outputs?: ?$ReadOnlyArray<ABIParam>,
-}>;
-
-const resolverABI: EthereumSmartContractABI = [
- {
- inputs: [{ internalType: 'contract ENS', name: '_ens', type: 'address' }],
- stateMutability: 'nonpayable',
- type: 'constructor',
- },
- {
- inputs: [
- { internalType: 'address[]', name: 'addresses', type: 'address[]' },
- ],
- name: 'getNames',
- outputs: [{ internalType: 'string[]', name: 'r', type: 'string[]' }],
- stateMutability: 'view',
- type: 'function',
- },
-];
-
-const mainnetChainID = 1;
-const goerliChainID = 5;
-const resolverAddresses: { +[chainID: number]: string } = {
- [mainnetChainID]: '0x3671aE578E63FdF66ad4F3E12CC0c0d71Ac7510C',
- [goerliChainID]: '0x333Fc8f550043f239a2CF79aEd5e9cF4A20Eb41e',
-};
-
-export type ReverseRecordsEthersSmartContract = {
- +'getNames(address[])': ($ReadOnlyArray<string>) => Promise<string[]>,
- ...
-};
-
-export { resolverABI, resolverAddresses };
diff --git a/lib/utils/viem-utils.js b/lib/utils/viem-utils.js
new file mode 100644
--- /dev/null
+++ b/lib/utils/viem-utils.js
@@ -0,0 +1,15 @@
+// @flow
+
+import { http, type ViemTransport } from 'viem';
+
+function getAlchemyMainnetViemTransport(alchemyKey: ?string): ViemTransport {
+ const key = alchemyKey ?? 'demo';
+ return http(`https://eth-mainnet.g.alchemy.com/v2/${key}`);
+}
+
+function getAlchemySepoliaViemTransport(alchemyKey: ?string): ViemTransport {
+ const key = alchemyKey ?? 'demo';
+ return http(`https://eth-sepolia.g.alchemy.com/v2/${key}`);
+}
+
+export { getAlchemyMainnetViemTransport, getAlchemySepoliaViemTransport };
diff --git a/lib/utils/wagmi-utils.js b/lib/utils/wagmi-utils.js
--- a/lib/utils/wagmi-utils.js
+++ b/lib/utils/wagmi-utils.js
@@ -67,7 +67,10 @@
function AlchemyENSCacheProvider(props: Props): React.Node {
const { children } = props;
return (
- <ENSCacheProvider provider={ethersAlchemyProvider}>
+ <ENSCacheProvider
+ ethersProvider={ethersAlchemyProvider}
+ alchemyKey={alchemyKey}
+ >
{children}
</ENSCacheProvider>
);
diff --git a/native/package.json b/native/package.json
--- a/native/package.json
+++ b/native/package.json
@@ -87,6 +87,7 @@
"expo-media-library": "~15.0.0",
"expo-secure-store": "~12.0.0",
"expo-splash-screen": "~0.17.4",
+ "fastestsmallesttextencoderdecoder": "^1.0.22",
"ffmpeg-kit-react-native": "^6.0.2",
"find-root": "^1.1.0",
"invariant": "^2.2.4",
diff --git a/native/root.react.js b/native/root.react.js
--- a/native/root.react.js
+++ b/native/root.react.js
@@ -103,7 +103,7 @@
import { useLoadCommFonts } from './themes/fonts.js';
import { DarkTheme, LightTheme } from './themes/navigation.js';
import ThemeHandler from './themes/theme-handler.react.js';
-import { provider } from './utils/ethers-utils.js';
+import { alchemyKey, ethersProvider } from './utils/ethers-utils.js';
import { neynarKey } from './utils/neynar-utils.js';
// Add custom items to expo-dev-menu
@@ -353,7 +353,10 @@
initialMetrics={initialWindowMetrics}
>
<ActionSheetProvider>
- <ENSCacheProvider provider={provider}>
+ <ENSCacheProvider
+ ethersProvider={ethersProvider}
+ alchemyKey={alchemyKey}
+ >
<NeynarClientProvider apiKey={neynarKey}>
<MediaCacheProvider
persistence={filesystemMediaCache}
diff --git a/native/utils/ethers-utils.js b/native/utils/ethers-utils.js
--- a/native/utils/ethers-utils.js
+++ b/native/utils/ethers-utils.js
@@ -1,21 +1,22 @@
// @flow
import '@ethersproject/shims';
+import 'fastestsmallesttextencoderdecoder';
import { AlchemyProvider } from 'ethers';
import type { EthersProvider } from 'lib/types/ethers-types.js';
-let alchemyKey;
+let alchemyKey: ?string;
try {
// $FlowExpectedError: file might not exist
const { key } = require('../facts/alchemy.json');
alchemyKey = key;
} catch {}
-let provider: ?EthersProvider;
+let ethersProvider: ?EthersProvider;
if (alchemyKey) {
- provider = new AlchemyProvider('mainnet', alchemyKey);
+ ethersProvider = new AlchemyProvider('mainnet', alchemyKey);
}
-export { provider };
+export { alchemyKey, ethersProvider };
diff --git a/yarn.lock b/yarn.lock
--- a/yarn.lock
+++ b/yarn.lock
@@ -2090,6 +2090,57 @@
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz#d0fce5d07b0620caa282b5131c297bb60f9d87e6"
integrity sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==
+"@ensdomains/address-encoder@1.0.0-rc.3":
+ version "1.0.0-rc.3"
+ resolved "https://registry.yarnpkg.com/@ensdomains/address-encoder/-/address-encoder-1.0.0-rc.3.tgz#78a8081bed834661e7fd21a9c9f67f927100fce5"
+ integrity sha512-8o6zH69rObIqDY4PusEWuN9jvVOct+9jj9AOPO7ifc3ev8nmsly0e8TE1sHkhk0iKFbd3DlSsUnJ+yuRWmdLCQ==
+ dependencies:
+ "@noble/curves" "^1.2.0"
+ "@noble/hashes" "^1.3.2"
+ "@scure/base" "^1.1.5"
+
+"@ensdomains/address-encoder@1.1.1":
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/@ensdomains/address-encoder/-/address-encoder-1.1.1.tgz#5cbec1fc6e2435b109c058426d7c222cb5a5d8de"
+ integrity sha512-yg7s+suCuKRhaGsgLu57W/jxIs/Lnqs/SU7jT7UwS4ATSnW94jbUCbmyyZ82CQwKsmwaUE8uYvvVb4N6lfz29A==
+ dependencies:
+ "@noble/curves" "^1.2.0"
+ "@noble/hashes" "^1.3.2"
+ "@scure/base" "^1.1.5"
+
+"@ensdomains/content-hash@3.1.0-rc.1":
+ version "3.1.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@ensdomains/content-hash/-/content-hash-3.1.0-rc.1.tgz#f22220df19be2f60683070a683f5760a9a7134d8"
+ integrity sha512-OzdkXgdFmduzcJKU4KRkkJkQHnm5p7m7FkX8k+bHOEoOIzc0ueGT/Jay4nnb60wNk1wSHRmzY+hHBMpFDiGReg==
+ dependencies:
+ "@ensdomains/address-encoder" "1.0.0-rc.3"
+ "@noble/curves" "^1.2.0"
+ "@scure/base" "^1.1.5"
+
+"@ensdomains/dnsprovejs@^0.5.1":
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/@ensdomains/dnsprovejs/-/dnsprovejs-0.5.1.tgz#7b09121580d3224736567e680697fe179f0288af"
+ integrity sha512-nfm4ggpK5YBVwVwLZKF9WPjRGRTL9aUxX2O4pqv/AnQCz3WeGHsW7VhVFLj2s4EoWSzCXwR1E6nuqgUwnH692w==
+ dependencies:
+ "@noble/hashes" "^1.3.2"
+ dns-packet "^5.6.1"
+ typescript-logging "^1.0.1"
+
+"@ensdomains/ensjs@^4.0.1":
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/@ensdomains/ensjs/-/ensjs-4.0.1.tgz#49a8930c1fe0f8fbd4c7fbb9aea517e71d41652f"
+ integrity sha512-s7/cM+7h0tZrDHLVHhmcg9qpXMkReWTU98EUGAZiJAx4xP+5C+GJhmv2jplAzHESXCIEzOt4K0QOwLfMUJl4Bg==
+ dependencies:
+ "@adraffy/ens-normalize" "1.10.1"
+ "@ensdomains/address-encoder" "1.1.1"
+ "@ensdomains/content-hash" "3.1.0-rc.1"
+ "@ensdomains/dnsprovejs" "^0.5.1"
+ abitype "^1.0.0"
+ dns-packet "^5.3.1"
+ graphql "^16.3.0"
+ graphql-request "6.1.0"
+ pako "^2.1.0"
+
"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
version "4.4.0"
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59"
@@ -2735,7 +2786,7 @@
"@graphql-typed-document-node/core" "^3.1.1"
tslib "^2.4.0"
-"@graphql-typed-document-node/core@^3.1.0", "@graphql-typed-document-node/core@^3.1.1":
+"@graphql-typed-document-node/core@^3.1.0", "@graphql-typed-document-node/core@^3.1.1", "@graphql-typed-document-node/core@^3.2.0":
version "3.2.0"
resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861"
integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==
@@ -3588,6 +3639,13 @@
dependencies:
"@noble/hashes" "1.3.3"
+"@noble/curves@^1.2.0":
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.6.0.tgz#be5296ebcd5a1730fccea4786d420f87abfeb40b"
+ integrity sha512-TlaHRXDehJuRNR9TfZDNQ45mMEd5dwUwmicsafcIX4SsNiqnCHKjE/1alYPd/lDRVhxdhUAlv8uEhMCI5zjIJQ==
+ dependencies:
+ "@noble/hashes" "1.5.0"
+
"@noble/hashes@1.3.2", "@noble/hashes@^1.1.2", "@noble/hashes@~1.3.0", "@noble/hashes@~1.3.2":
version "1.3.2"
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39"
@@ -3598,6 +3656,11 @@
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699"
integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==
+"@noble/hashes@1.5.0", "@noble/hashes@^1.3.2":
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.5.0.tgz#abadc5ca20332db2b1b2aa3e496e9af1213570b0"
+ integrity sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==
+
"@noble/hashes@^1.3.1":
version "1.4.0"
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426"
@@ -4534,6 +4597,11 @@
resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.6.tgz#8ce5d304b436e4c84f896e0550c83e4d88cb917d"
integrity sha512-ok9AWwhcgYuGG3Zfhyqg+zwl+Wn5uE+dwC0NV/2qQkx4dABbb/bx96vWu8NSj+BNjjSjno+JRYRjle1jV08k3g==
+"@scure/base@^1.1.5":
+ version "1.1.9"
+ resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.9.tgz#e5e142fbbfe251091f9c5f1dd4c834ac04c3dbd1"
+ integrity sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==
+
"@scure/base@~1.1.0", "@scure/base@~1.1.2":
version "1.1.3"
resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.3.tgz#8584115565228290a6c6c4961973e0903bb3df2f"
@@ -6164,6 +6232,11 @@
resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.0.0.tgz#237176dace81d90d018bebf3a45cb42f2a2d9e97"
integrity sha512-NMeMah//6bJ56H5XRj8QCV4AwuW6hB6zqz2LnhhLdcWVQOsXki6/Pn3APeqxCma62nXIcmZWdu1DlHWS74umVQ==
+abitype@^1.0.0:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.0.6.tgz#76410903e1d88e34f1362746e2d407513c38565b"
+ integrity sha512-MMSqYh4+C/aVqI2RQaWqbvI4Kxo5cQV40WQ4QFtDnNzCkqChm8MuENhElmynZlO0qUy/ObkEUaXtKqYnx1Kp3A==
+
abort-controller@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392"
@@ -10172,6 +10245,13 @@
dependencies:
"@leichtgewicht/ip-codec" "^2.0.1"
+dns-packet@^5.3.1, dns-packet@^5.6.1:
+ version "5.6.1"
+ resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.6.1.tgz#ae888ad425a9d1478a0674256ab866de1012cf2f"
+ integrity sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==
+ dependencies:
+ "@leichtgewicht/ip-codec" "^2.0.1"
+
doctrine@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d"
@@ -10626,6 +10706,13 @@
dependencies:
is-arrayish "^0.2.1"
+error-stack-parser@^1.3.6:
+ version "1.3.6"
+ resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-1.3.6.tgz#e0e73b93e417138d1cd7c0b746b1a4a14854c292"
+ integrity sha512-xhuSYd8wLgOXwNgjcPeXMPL/IiiA1Huck+OPvClpJViVNNlJVtM41o+1emp7bPvlCJwCatFX2DWc05/DgfbWzA==
+ dependencies:
+ stackframe "^0.3.1"
+
error-stack-parser@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.0.6.tgz#5a99a707bd7a4c58a797902d48d82803ede6aad8"
@@ -11873,6 +11960,11 @@
resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5"
integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==
+fastestsmallesttextencoderdecoder@^1.0.22:
+ version "1.0.22"
+ resolved "https://registry.yarnpkg.com/fastestsmallesttextencoderdecoder/-/fastestsmallesttextencoderdecoder-1.0.22.tgz#59b47e7b965f45258629cc6c127bf783281c5e93"
+ integrity sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==
+
fastq@^1.6.0:
version "1.11.1"
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.1.tgz#5d8175aae17db61947f8b162cfc7f63264d22807"
@@ -13166,6 +13258,14 @@
resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6"
integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==
+graphql-request@6.1.0:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-6.1.0.tgz#f4eb2107967af3c7a5907eb3131c671eac89be4f"
+ integrity sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw==
+ dependencies:
+ "@graphql-typed-document-node/core" "^3.2.0"
+ cross-fetch "^3.1.5"
+
graphql-tag@^2.10.1:
version "2.12.6"
resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.12.6.tgz#d441a569c1d2537ef10ca3d1633b48725329b5f1"
@@ -13178,6 +13278,11 @@
resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.8.0.tgz#33410e96b012fa3bdb1091cc99a94769db212b38"
integrity sha512-5gghUc24tP9HRznNpV2+FIoq3xKkj5dTQqf4v0CpdPbFVwFkWoxOM+o+2OC9ZSvjEMTjfmG9QT+gcvggTwW1zw==
+graphql@^16.3.0:
+ version "16.9.0"
+ resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.9.0.tgz#1c310e63f16a49ce1fbb230bd0a000e99f6f115f"
+ integrity sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==
+
graphql@^16.6.0:
version "16.8.1"
resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.8.1.tgz#1930a965bef1170603702acdb68aedd3f3cf6f07"
@@ -19012,7 +19117,7 @@
registry-url "^5.0.0"
semver "^6.2.0"
-pako@^2.0.4:
+pako@^2.0.4, pako@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86"
integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==
@@ -22418,6 +22523,11 @@
resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=
+source-map@0.5.6:
+ version "0.5.6"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
+ integrity sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==
+
source-map@^0.4.4:
version "0.4.4"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"
@@ -22594,6 +22704,13 @@
resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==
+stack-generator@^1.0.7:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/stack-generator/-/stack-generator-1.1.0.tgz#36f6a920751a6c10f499a13c32cbb5f51a0b8b25"
+ integrity sha512-sZDVjwC56vZoo+a5t0LH/1sMQLWYLi/r+Z2ztyCAOhOX3QBP34GWxK0FWf2eU1TIU2CJKCKBAtDZycUh/ZKMlw==
+ dependencies:
+ stackframe "^1.0.2"
+
stack-utils@^1.0.1:
version "1.0.5"
resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.5.tgz#a19b0b01947e0029c8e451d5d61a498f5bb1471b"
@@ -22608,11 +22725,38 @@
dependencies:
escape-string-regexp "^2.0.0"
+stackframe@^0.3.1, stackframe@~0.3:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-0.3.1.tgz#33aa84f1177a5548c8935533cbfeb3420975f5a4"
+ integrity sha512-XmoiF4T5nuWEp2x2w92WdGjdHGY/cZa6LIbRsDRQR/Xlk4uW0PAUlH1zJYVffocwKpCdwyuypIp25xsSXEtZHw==
+
+stackframe@^1.0.2:
+ version "1.3.4"
+ resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310"
+ integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==
+
stackframe@^1.1.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.2.0.tgz#52429492d63c62eb989804c11552e3d22e779303"
integrity sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA==
+stacktrace-gps@^2.4.3:
+ version "2.4.4"
+ resolved "https://registry.yarnpkg.com/stacktrace-gps/-/stacktrace-gps-2.4.4.tgz#69c827e9d6d6f41cf438d7f195e2e3cbfcf28c44"
+ integrity sha512-msFhuMEEklQLUtaJ+GeCDjzUN+PamfHWQiK3C1LnbHjoxSeF5dAxiE+aJkptNMmMNOropGFJ7G3ZT7dPZHgDaQ==
+ dependencies:
+ source-map "0.5.6"
+ stackframe "~0.3"
+
+stacktrace-js@1.3.1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/stacktrace-js/-/stacktrace-js-1.3.1.tgz#67cab2589af5c417b962f7369940277bb3b6a18b"
+ integrity sha512-b+5voFnXqg9TWdOE50soXL+WuOreYUm1Ukg9U7rzEWGL4+gcVxIcFasNBtOffVX0I1lYqVZj0PZXZvTt5e3YRQ==
+ dependencies:
+ error-stack-parser "^1.3.6"
+ stack-generator "^1.0.7"
+ stacktrace-gps "^2.4.3"
+
stacktrace-parser@^0.1.10, stacktrace-parser@^0.1.3:
version "0.1.10"
resolved "https://registry.yarnpkg.com/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz#29fb0cae4e0d0b85155879402857a1639eb6051a"
@@ -23911,6 +24055,13 @@
resolved "https://registry.yarnpkg.com/typescript-compiler/-/typescript-compiler-1.4.1-2.tgz#ba4f7db22d91534a1929d90009dce161eb72fd3f"
integrity sha1-uk99si2RU0oZKdkACdzhYety/T8=
+typescript-logging@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/typescript-logging/-/typescript-logging-1.0.1.tgz#e0f8157943780cf5943aacd53b04cb73d108a0f9"
+ integrity sha512-zp28ABme0m5q/nXabBaY9Hv/35N8lMH4FsvhpUO0zVi4vFs3uKlb5br2it61HAZF5k+U0aP6E67j0VD0IzXGpQ==
+ dependencies:
+ stacktrace-js "1.3.1"
+
typescript@^3.4:
version "3.6.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.3.tgz#fea942fabb20f7e1ca7164ff626f1a9f3f70b4da"
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Dec 23, 7:11 PM (16 h, 52 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2696014
Default Alt Text
D13699.id45182.diff (43 KB)
Attached To
Mode
D13699: [lib][native] Use ensjs for ENSCache name/address resolution
Attached
Detach File
Event Timeline
Log In to Comment