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

migrate ethers v6, add sepolia support #168

Merged
merged 13 commits into from
May 6, 2024
2 changes: 1 addition & 1 deletion docgen.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ const doc = {
tokenId: '4221908525551133525058944220830153...',
networkName: {
description: 'Name of the chain to query for.',
'@enum': ['mainnet', 'rinkeby', 'ropsten', 'goerli'],
'@enum': ['mainnet', 'rinkeby', 'ropsten', 'goerli', 'sepolia'],
},
},
components: {
Expand Down
11 changes: 9 additions & 2 deletions mock/entry.mock.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { namehash } from '@ensdomains/ensjs/utils/normalise';
import { utils } from 'ethers';
import {
keccak256,
toUtf8Bytes
} from 'ethers';
import nock from 'nock';
import { Version } from '../src/base';
import { ADDRESS_NAME_WRAPPER } from '../src/config';
Expand Down Expand Up @@ -93,7 +96,7 @@ export class MockEntry {
const registrationDate = +new Date() - 157680000000;
const expiryDate = +new Date() + 31536000000;
const labelName = name.split('.')[0];
const labelhash = utils.keccak256(utils.toUtf8Bytes(labelName));
const labelhash = keccak256(toUtf8Bytes(labelName));
const _metadata = new Metadata({
name,
created_date: +randomDate,
Expand Down Expand Up @@ -187,6 +190,10 @@ export class MockEntry {
value: getWrapperState(decodedFuses),
});

_metadata.description += _metadata.generateRuggableWarning(
_metadata.name, version, getWrapperState(decodedFuses)
)

nock(SUBGRAPH_URL.origin)
.post(SUBGRAPH_PATH, {
query: GET_WRAPPED_DOMAIN,
Expand Down
5 changes: 3 additions & 2 deletions mock/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ export function nockProvider(
WEB3_URL: URL,
method: string,
params: any[],
response: EthCallResponse | EthChainIdResponse | NetVersionResponse
response: EthCallResponse | EthChainIdResponse | NetVersionResponse,
id?: number
) {
nock(WEB3_URL.origin)
.persist()
.post(WEB3_URL.pathname, {
method,
params,
id: /[0-9]/,
id: id || /[0-9]/,
jsonrpc: '2.0',
})
.reply(200, response);
Expand Down
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@
},
"dependencies": {
"@adraffy/ens-normalize": "^1.10.0",
"@ensdomains/ens-avatar": "0.3.6",
"@ensdomains/ensjs": "^3.0.0-alpha.67",
"@ensdomains/ens-avatar": "^1.0.0-alpha.1.ethers.6",
"@ensdomains/ensjs": "3.0.0-alpha.67",
"@types/lodash": "^4.14.170",
"btoa": "^1.2.1",
"canvas": "^2.11.2",
"compression": "^1.7.4",
"cors": "^2.8.5",
"dompurify": "^3.0.2",
"dompurify": "^3.0.5",
"dotenv": "^10.0.0",
"emoji-regex": "^10.1.0",
"ethers": "^5.7.2",
"ethers": "6.12.0",
"express": "^4.18.1",
"google-auth-library": "^8.1.0",
"graphql": "^16.5.0",
Expand All @@ -42,15 +42,15 @@
"start": "node dist/index.js",
"deploy": "ENV=prod yarn build && gcloud app deploy --project=ens-metadata-service",
"deploy:test": "ENV=prod yarn build && gcloud app deploy --no-promote --project=ens-metadata-service",
"test": "ENV=local ava -v --serial --timeout=1m",
"test": "ENV=local ava -v --serial --timeout=2m",
"test:cov": "nyc --reporter=json --reporter=text ava --serial",
"docgen": "node docgen.js",
"appconfgen": "node appconfgen.js"
},
"devDependencies": {
"@types/compression": "^1.7.2",
"@types/cors": "^2.8.10",
"@types/dompurify": "^2.3.1",
"@types/dompurify": "^3.0.2",
"@types/express": "^4.17.12",
"@types/jsdom": "^16.2.13",
"@types/node": "^16.11.0",
Expand Down
7 changes: 7 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const NODE_PROVIDER_URL = process.env.NODE_PROVIDER_URL || 'http://localhost:854
// undocumented, temporary keys
const NODE_PROVIDER_URL_CF = process.env.NODE_PROVIDER_URL_CF || '';
const NODE_PROVIDER_URL_GOERLI = process.env.NODE_PROVIDER_URL_GOERLI || '';
const NODE_PROVIDER_URL_SEPOLIA = process.env.NODE_PROVIDER_URL_SEPOLIA || '';

const ADDRESS_ETH_REGISTRAR = process.env.ADDRESS_ETH_REGISTRAR || '0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85';
const ADDRESS_ETH_REGISTRY = process.env.ADDRESS_ETH_REGISTRY || '0x00000000000c2e074ec69a0dfb2997ba6c7d2e1e'
Expand All @@ -32,6 +33,10 @@ const ETH_REGISTRY_ABI = [
'function recordExists(bytes32 node) external view returns (bool)'
];

const NAMEWRAPPER_ABI = [
'function isWrapped(bytes32 node) public view returns (bool)'
];

// response timeout: 1 min
const RESPONSE_TIMEOUT = 15 * 1000;

Expand All @@ -42,6 +47,7 @@ export {
CANVAS_FONT_PATH,
CANVAS_EMOJI_FONT_PATH,
ETH_REGISTRY_ABI,
NAMEWRAPPER_ABI,
INAMEWRAPPER,
IPFS_GATEWAY,
INFURA_API_KEY,
Expand All @@ -51,6 +57,7 @@ export {
NODE_PROVIDER_URL,
NODE_PROVIDER_URL_CF,
NODE_PROVIDER_URL_GOERLI,
NODE_PROVIDER_URL_SEPOLIA,
RESPONSE_TIMEOUT,
SERVER_URL,
};
75 changes: 44 additions & 31 deletions src/controller/ensMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import {
ContractMismatchError,
ExpiredNameError,
NamehashMismatchError,
SubgraphRecordNotFound,
UnsupportedNetwork,
Version,
} from '../base';
import {
ADDRESS_ETH_REGISTRY,
ETH_REGISTRY_ABI,
NAMEWRAPPER_ABI,
RESPONSE_TIMEOUT,
} from '../config';
import { checkContract } from '../service/contract';
Expand Down Expand Up @@ -67,7 +69,6 @@ export async function ensMetadata(req: Request, res: Response) {
description: 'Unsupported network'
} */
if (
error instanceof FetchError ||
error instanceof ContractMismatchError ||
error instanceof ExpiredNameError ||
error instanceof NamehashMismatchError ||
Expand All @@ -81,37 +82,49 @@ export async function ensMetadata(req: Request, res: Response) {
}
}

try {
// Here is the case; if subgraph did not index fresh ENS name but registry has the record,
// instead of 'not found' send positive unknown metadata information
const registry = new Contract(
ADDRESS_ETH_REGISTRY,
ETH_REGISTRY_ABI,
provider
);
if (!tokenId || !version) {
throw 'Missing parameters to construct namehash';
}
const _namehash = constructEthNameHash(tokenId, version as Version);
const isRecordExist = await registry.recordExists(_namehash);
assert(isRecordExist, 'ENS name does not exist');
if (error instanceof SubgraphRecordNotFound) {
try {
// Here is the case; if subgraph did not index fresh ENS name but registry has the record,
// instead of 'not found' send positive unknown metadata information
const registry = new Contract(
ADDRESS_ETH_REGISTRY,
ETH_REGISTRY_ABI,
provider
);
if (!tokenId || !version) {
throw 'Missing parameters to construct namehash';
}
const _namehash = constructEthNameHash(tokenId, version as Version);
const isRecordExist = await registry.recordExists(_namehash);
assert(isRecordExist, 'ENS name does not exist');

// When entry is not available on subgraph yet,
// return unknown name metadata with 200 status code
const { url, ...unknownMetadata } = new Metadata({
name: 'unknown.name',
description: 'Unknown ENS name',
created_date: 1580346653000,
tokenId: '',
version: Version.v1,
// add timestamp of the request date
last_request_date
});
res.status(200).json({
message: unknownMetadata,
});
return;
} catch (error) {}
if (version == Version.v2) {
const contract = new Contract(
contractAddress,
NAMEWRAPPER_ABI,
provider
);
const isNameWrapped = await contract.isWrapped(_namehash);
assert(isNameWrapped, 'Name is not wrapped');
}

// When entry is not available on subgraph yet,
// return unknown name metadata with 200 status code
const { url, ...unknownMetadata } = new Metadata({
name: 'unknown.name',
description: 'Unknown ENS name',
created_date: 1580346653000,
tokenId: '',
version: Version.v1,
// add timestamp of the request date
last_request_date,
});
res.status(200).json({
message: unknownMetadata,
});
return;
} catch (error) {}
}

/* #swagger.responses[404] = {
description: 'No results found'
Expand Down
58 changes: 53 additions & 5 deletions src/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import avaTest, { ExecutionContext, TestFn } from 'ava';
import { ethers } from 'ethers';
import * as http from 'http';
import got, {
HTTPError,
Expand Down Expand Up @@ -107,11 +106,12 @@ test.before(async (t: ExecutionContext<TestContext>) => {
[
{
to: ADDRESS_NAME_WRAPPER.toLowerCase(),
data: /^0xfd0cd0.*$/,
data: '0xfd0cd0d97857c9824139b8a8c3cb04712b41558b4878c55fa9c1e5390e910ee3220c3cce',
},
'latest',
],
{
id: 2,
result:
'0x0000000000000000000000000000000000000000000000000000000000000001',
}
Expand All @@ -132,6 +132,42 @@ test.before(async (t: ExecutionContext<TestContext>) => {
}
);

nockProvider(
WEB3_URL,
'eth_call',
[
{
to: ADDRESS_ETH_REGISTRY.toLowerCase(),
data: '0xf79fe538b71788e9ec63be108fba9c0b01c927e4d8f1887d53787ff84752be3d8db1dd9a',
},
'latest'
],
{
id: 1,
result:
'0x0000000000000000000000000000000000000000000000000000000000000001',
},
1
);

nockProvider(
WEB3_URL,
'eth_call',
[
{
to: '0x114d4603199df73e7d157787f8778e21fcd13066',
data: '0xfd0cd0d9b71788e9ec63be108fba9c0b01c927e4d8f1887d53787ff84752be3d8db1dd9a',
},
'latest'
],
{
id: 2,
result:
'0x0000000000000000000000000000000000000000000000000000000000000001',
},
2
);

// something.eth recordExist true
nockProvider(
WEB3_URL,
Expand All @@ -144,6 +180,7 @@ test.before(async (t: ExecutionContext<TestContext>) => {
'latest',
],
{
id: 1,
result:
'0x0000000000000000000000000000000000000000000000000000000000000001',
}
Expand Down Expand Up @@ -194,8 +231,11 @@ test('get /:contractAddress/:tokenId for domain (wrappertest3.eth)', async (t: E
});

test('get /:contractAddress/:tokenId by decimal id', async (t: ExecutionContext<TestContext>) => {
const intId = ethers.BigNumber.from(wrappertest3.namehash).toString();
const result: Metadata = await got(`${METADATA_PATH}/${intId}`, options).json();
const intId = BigInt(wrappertest3.namehash).toString();
const result: Metadata = await got(
`${METADATA_PATH}/${intId}`,
options
).json();
delete result.last_request_date;
t.deepEqual(result, wrappertest3.expect);
});
Expand Down Expand Up @@ -288,6 +328,7 @@ test('raise 404 status from subgraph connection', async (t: ExecutionContext<Tes
variables: {
tokenId: sub1Wrappertest.namehash,
},
operationName: 'getDomains',
})
.replyWithError(fetchError);
const {
Expand All @@ -297,6 +338,7 @@ test('raise 404 status from subgraph connection', async (t: ExecutionContext<Tes
got(`${METADATA_PATH}/${sub1Wrappertest.namehash}`, {
...options,
retry: 0,
throwHttpErrors: true
}),
{
instanceOf: HTTPError,
Expand All @@ -321,6 +363,7 @@ test('raise ECONNREFUSED from subgraph connection', async (t: ExecutionContext<T
variables: {
tokenId: sub1Wrappertest.namehash,
},
operationName: 'getDomains',
})
.replyWithError(fetchError);
const {
Expand All @@ -330,6 +373,7 @@ test('raise ECONNREFUSED from subgraph connection', async (t: ExecutionContext<T
got(`${METADATA_PATH}/${sub1Wrappertest.namehash}`, {
...options,
retry: 0,
throwHttpErrors: true
}),
{
instanceOf: HTTPError,
Expand All @@ -354,6 +398,7 @@ test('raise Internal Server Error from subgraph', async (t: ExecutionContext<Tes
variables: {
tokenId: sub1Wrappertest.namehash,
},
operationName: 'getDomains',
})
.replyWithError(fetchError);
const {
Expand All @@ -363,6 +408,7 @@ test('raise Internal Server Error from subgraph', async (t: ExecutionContext<Tes
got(`${METADATA_PATH}/${sub1Wrappertest.namehash}`, {
...options,
retry: 0,
throwHttpErrors: true
}),
{
instanceOf: HTTPError,
Expand All @@ -380,8 +426,9 @@ test('raise timeout from subgraph', async (t: ExecutionContext<TestContext>) =>
variables: {
tokenId: sub1Wrappertest.namehash,
},
operationName: 'getDomains',
})
.delayConnection(2000) // 2 seconds
.delayConnection(3000) // 3 seconds
.replyWithError({ code: 'ETIMEDOUT' })
.persist(false);
const {
Expand All @@ -391,6 +438,7 @@ test('raise timeout from subgraph', async (t: ExecutionContext<TestContext>) =>
got(`${METADATA_PATH}/${sub1Wrappertest.namehash}`, {
...options,
retry: 0,
throwHttpErrors: true
}),
{
instanceOf: HTTPError,
Expand Down
Loading
Loading