Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEAT] ENS Wildcard and offchain resolution #5002

Merged
merged 4 commits into from
Sep 21, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -92,7 +92,7 @@ jobs:
node-version: ${{ steps.nvm.outputs.NODE_VERSION }}
- run: yarn test:tgz-check
test:
runs-on: ubuntu-20.04
runs-on: macos-12
needs: setup
steps:
- uses: actions/cache@v2
1 change: 1 addition & 0 deletions app/constants/network.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export const MAINNET = 'mainnet';
export const HOMESTEAD = 'homestead';
export const ROPSTEN = 'ropsten';
export const KOVAN = 'kovan';
export const RINKEBY = 'rinkeby';
50 changes: 31 additions & 19 deletions app/util/ENSUtils.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import 'react-native-get-random-values';
// eslint-disable-next-line import/no-unresolved
import '@ethersproject/shims';

import Engine from '../core/Engine';
import networkMap from 'ethjs-ens/lib/network-map.json';
import ENS from 'ethjs-ens';
import ensNetworkMap from 'ethereum-ens-network-map';
import { ethers } from 'ethers';
import { toLowerCaseEquals } from '../util/general';
import { getEthersNetworkTypeById } from './networks';
import Logger from './Logger';

/**
* Utility class with the single responsibility
@@ -11,21 +17,29 @@ class ENSCache {
static cache = {};
}

export function getEnsProvider(network, provider) {
const ensAddress = ensNetworkMap[network];
if (ensAddress) {
const networkType = getEthersNetworkTypeById(network);
return new ethers.providers.Web3Provider(provider, {
chainId: parseInt(network, 10),
name: networkType,
ensAddress,
});
}
}

export async function doENSReverseLookup(address, network) {
const cache = ENSCache.cache[network + address];
const { provider } = Engine.context.NetworkController;
if (cache) {
return Promise.resolve(cache);
}

const { provider } = Engine.context.NetworkController;

const networkHasEnsSupport = Boolean(networkMap[network]);

if (networkHasEnsSupport) {
this.ens = new ENS({ provider, network });
const ensProvider = await getEnsProvider(network, provider);
if (ensProvider) {
try {
const name = await this.ens.reverse(address);
const resolvedAddress = await this.ens.lookup(name);
const name = await ensProvider.lookupAddress(address);
const resolvedAddress = await ensProvider.resolveName(name);
if (toLowerCaseEquals(address, resolvedAddress)) {
ENSCache.cache[network + address] = name;
return name;
@@ -37,16 +51,14 @@ export async function doENSReverseLookup(address, network) {

export async function doENSLookup(ensName, network) {
const { provider } = Engine.context.NetworkController;

const networkHasEnsSupport = Boolean(networkMap[network]);

if (networkHasEnsSupport) {
this.ens = new ENS({ provider, network });
const ensProvider = await getEnsProvider(network, provider);
if (ensProvider) {
try {
const resolvedAddress = await this.ens.lookup(ensName);
const resolvedAddress = await ensProvider.resolveName(ensName);
return resolvedAddress;
// eslint-disable-next-line no-empty
} catch (e) {}
} catch (e) {
Logger.error(e);
}
}
}

64 changes: 44 additions & 20 deletions app/util/ENSUtils.test.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,48 @@
import { isDefaultAccountName } from './ENSUtils';
import { isDefaultAccountName, getEnsProvider } from './ENSUtils';

describe('isDefaultAccountName', () => {
const accountNameDefaultOne = 'Account 1';
it('should match RegEx if name "Account 1" has default pattern', () => {
expect(isDefaultAccountName(accountNameDefaultOne)).toEqual(true);
describe('ENSUtils', () => {
describe('isDefaultAccountName', () => {
const accountNameDefaultOne = 'Account 1';
it('should match RegEx if name "Account 1" has default pattern', () => {
expect(isDefaultAccountName(accountNameDefaultOne)).toEqual(true);
});
const accountNameDefaultTwo = 'Account 99999';
it('should match RegEx if name "Account 99999" has default pattern', () => {
expect(isDefaultAccountName(accountNameDefaultTwo)).toEqual(true);
});
const accountNameEmpty = '';
it('should not match RegEx if name is empty', () => {
expect(isDefaultAccountName(accountNameEmpty)).toEqual(false);
});
const accountNameUndefined = undefined;
it('should not match RegEx if name is undefined', () => {
expect(isDefaultAccountName(accountNameUndefined)).toEqual(false);
});
const accountNameNotDefault = 'Johns Wallet';
it('should not match RegEx if name does not has default pattern', () => {
expect(isDefaultAccountName(accountNameNotDefault)).toEqual(false);
});
});
const accountNameDefaultTwo = 'Account 99999';
it('should match RegEx if name "Account 99999" has default pattern', () => {
expect(isDefaultAccountName(accountNameDefaultTwo)).toEqual(true);
});
const accountNameEmpty = '';
it('should not match RegEx if name is empty', () => {
expect(isDefaultAccountName(accountNameEmpty)).toEqual(false);
});
const accountNameUndefined = undefined;
it('should not match RegEx if name is undefined', () => {
expect(isDefaultAccountName(accountNameUndefined)).toEqual(false);
});
const accountNameNotDefault = 'Johns Wallet';
it('should not match RegEx if name does not has default pattern', () => {
expect(isDefaultAccountName(accountNameNotDefault)).toEqual(false);

describe('getEnsProvider', () => {
let provider: any;

beforeEach(async () => {
provider = jest.fn();
});

it('should return ensProvider if exists', () => {
const network = '1'; // mainnet
const ensProvider = getEnsProvider(network, provider);
const ENS_ADDRESS = '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e';
expect(ensProvider?._network.name).toEqual('homestead');
expect(ensProvider?._network.chainId).toEqual(1);
expect(ensProvider?._network.ensAddress).toEqual(ENS_ADDRESS);
});

it('should not return ensProvider if not exist', () => {
const network = '10'; // does not exist
expect(!!getEnsProvider(network, provider)).toEqual(false);
});
});
});
6 changes: 6 additions & 0 deletions app/util/networks/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import URL from 'url-parse';
import AppConstants from '../../core/AppConstants';
import {
HOMESTEAD,
MAINNET,
ROPSTEN,
KOVAN,
@@ -126,6 +127,11 @@ export function getNetworkTypeById(id) {
throw new Error(`${NETWORK_ERROR_UNKNOWN_NETWORK_ID} ${id}`);
}

export function getEthersNetworkTypeById(id) {
const networkType = getNetworkTypeById(id);
return networkType === MAINNET ? HOMESTEAD : networkType;
}

export function getDefaultNetworkByChainId(chainId) {
if (!chainId) {
throw new Error(NETWORK_ERROR_MISSING_CHAIN_ID);
6 changes: 6 additions & 0 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
@@ -310,6 +310,8 @@ PODS:
- React-Core
- react-native-cookies (6.2.1):
- React-Core
- react-native-get-random-values (1.8.0):
- React-Core
- react-native-in-app-review (3.2.3):
- React
- react-native-minimizer (1.3.0):
@@ -535,6 +537,7 @@ DEPENDENCIES:
- react-native-branch (from `../node_modules/react-native-branch`)
- react-native-camera (from `../node_modules/react-native-camera`)
- "react-native-cookies (from `../node_modules/@react-native-cookies/cookies`)"
- react-native-get-random-values (from `../node_modules/react-native-get-random-values`)
- react-native-in-app-review (from `../node_modules/react-native-in-app-review`)
- react-native-minimizer (from `../node_modules/react-native-minimizer`)
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
@@ -655,6 +658,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-camera"
react-native-cookies:
:path: "../node_modules/@react-native-cookies/cookies"
react-native-get-random-values:
:path: "../node_modules/react-native-get-random-values"
react-native-in-app-review:
:path: "../node_modules/react-native-in-app-review"
react-native-minimizer:
@@ -790,6 +795,7 @@ SPEC CHECKSUMS:
react-native-branch: 5b20bf032b3a1a20d2b76e57215bd3254a137dd5
react-native-camera: b8cc03e2feec0c04403d0998e37cf519d8fd4c6f
react-native-cookies: f54fcded06bb0cda05c11d86788020b43528a26c
react-native-get-random-values: a6ea6a8a65dc93e96e24a11105b1a9c8cfe1d72a
react-native-in-app-review: 23f4f5b9fcd94339dd5d93c6230557f9c67c7dda
react-native-minimizer: bd07450123d8c685acb2dff64f0213b0c7b5dbd6
react-native-netinfo: e849fc21ca2f4128a5726c801a82fc6f4a6db50d
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -153,11 +153,11 @@
"eth-json-rpc-infura": "5.1.0",
"eth-json-rpc-middleware": "4.3.0",
"eth-url-parser": "1.0.4",
"ethereum-ens-network-map": "^1.0.2",
"ethereumjs-abi": "0.6.6",
"ethereumjs-util": "6.1.0",
"ethers": "^5.0.14",
"ethers": "^5.6.5",
"ethjs-contract": "0.2.3",
"ethjs-ens": "2.0.1",
"ethjs-query": "0.3.8",
"ethjs-unit": "0.1.6",
"events": "3.0.0",
@@ -208,6 +208,7 @@
"react-native-flash-message": "0.1.11",
"react-native-fs": "^2.16.6",
"react-native-gesture-handler": "^1.10.3",
"react-native-get-random-values": "1.8.0",
"react-native-i18n": "2.0.15",
"react-native-in-app-review": "^3.2.3",
"react-native-jazzicon": "^0.1.2",
646 changes: 457 additions & 189 deletions yarn.lock

Large diffs are not rendered by default.