@ensdomains/ensjs 3.0.0-alpha.6 → 3.0.0-alpha.7

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.
@@ -1,3 +1,6 @@
1
+ export declare const enter: (node: any) => any;
2
+ export declare const requestMiddleware: (visit: any, parse: any) => (request: any) => any;
3
+ export declare const responseMiddleware: (traverse: any) => (response: any) => any;
1
4
  export default class GqlManager {
2
5
  gql: any;
3
6
  client?: any | null;
@@ -23,13 +23,73 @@ var __importStar = (this && this.__importStar) || function (mod) {
23
23
  return result;
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.responseMiddleware = exports.requestMiddleware = exports.enter = void 0;
27
+ // @ts-nocheck
28
+ const normalise_1 = require("./utils/normalise");
29
+ const generateSelection = (selection) => ({
30
+ kind: 'Field',
31
+ name: {
32
+ kind: 'Name',
33
+ value: selection,
34
+ },
35
+ arguments: [],
36
+ directives: [],
37
+ alias: undefined,
38
+ selectionSet: undefined,
39
+ });
40
+ const enter = (node) => {
41
+ if (node.kind === 'SelectionSet') {
42
+ const id = node.selections.find((x) => x.name && x.name.value === 'id');
43
+ const name = node.selections.find((x) => x.name && x.name.value === 'name');
44
+ if (!id && name) {
45
+ node.selections = [...node.selections, generateSelection('id')];
46
+ return node;
47
+ }
48
+ }
49
+ };
50
+ exports.enter = enter;
51
+ const requestMiddleware = (visit, parse) => (request) => {
52
+ const requestBody = JSON.parse(request.body);
53
+ const rawQuery = requestBody.query;
54
+ const parsedQuery = parse(rawQuery);
55
+ const updatedQuery = visit(parsedQuery, { enter: exports.enter });
56
+ const updatedBody = { ...requestBody, query: updatedQuery.loc.source.body };
57
+ return {
58
+ ...request,
59
+ body: JSON.stringify(updatedBody),
60
+ };
61
+ };
62
+ exports.requestMiddleware = requestMiddleware;
63
+ const responseMiddleware = (traverse) => (response) => {
64
+ traverse(response).forEach(function (responseItem) {
65
+ if (responseItem instanceof Object && responseItem.name) {
66
+ //Name already in hashed form
67
+ if (responseItem.name && responseItem.name.includes('[')) {
68
+ return;
69
+ }
70
+ const hashedName = (0, normalise_1.namehash)(responseItem.name);
71
+ if (responseItem.id !== hashedName) {
72
+ this.update({ ...responseItem, name: hashedName, invalidName: true });
73
+ }
74
+ }
75
+ });
76
+ return response;
77
+ };
78
+ exports.responseMiddleware = responseMiddleware;
26
79
  class GqlManager {
27
80
  gql = () => null;
28
81
  client = null;
29
82
  setUrl = async (url) => {
30
83
  if (url) {
31
- const imported = await Promise.resolve().then(() => __importStar(require('graphql-request')));
32
- this.client = new imported.GraphQLClient(url);
84
+ const [imported, traverse, { visit, parse }] = await Promise.all([
85
+ Promise.resolve().then(() => __importStar(require('graphql-request'))),
86
+ Promise.resolve().then(() => __importStar(require('traverse'))),
87
+ Promise.resolve().then(() => __importStar(require('graphql/language'))),
88
+ ]);
89
+ this.client = new imported.GraphQLClient(url, {
90
+ requestMiddleware: (0, exports.requestMiddleware)(visit, parse),
91
+ responseMiddleware: (0, exports.responseMiddleware)(traverse.default),
92
+ });
33
93
  this.gql = imported.gql;
34
94
  }
35
95
  else {
@@ -1,2 +1,2 @@
1
1
  export declare const normalise: (name: string) => any;
2
- export declare const namehash: (inputName: string) => string;
2
+ export declare const namehash: (name: string) => string;
@@ -6,17 +6,24 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.namehash = exports.normalise = void 0;
7
7
  const utils_1 = require("ethers/lib/utils");
8
8
  const uts46bundle_js_1 = __importDefault(require("idna-uts46-hx/uts46bundle.js"));
9
+ const labels_1 = require("./labels");
9
10
  const zeros = new Uint8Array(32);
10
11
  zeros.fill(0);
11
12
  const normalise = (name) => name ? uts46bundle_js_1.default.toUnicode(name, { useStd3ASCII: true }) : name;
12
13
  exports.normalise = normalise;
13
- const namehash = (inputName) => {
14
+ const namehash = (name) => {
14
15
  let result = zeros;
15
- const name = (0, exports.normalise)(inputName);
16
16
  if (name) {
17
17
  const labels = name.split('.');
18
18
  for (var i = labels.length - 1; i >= 0; i--) {
19
- const labelSha = (0, utils_1.keccak256)((0, utils_1.toUtf8Bytes)(labels[i]));
19
+ let labelSha;
20
+ if ((0, labels_1.isEncodedLabelhash)(labels[i])) {
21
+ labelSha = (0, labels_1.decodeLabelhash)(labels[i]);
22
+ }
23
+ else {
24
+ const normalised = (0, exports.normalise)(labels[i]);
25
+ labelSha = (0, utils_1.keccak256)((0, utils_1.toUtf8Bytes)(normalised));
26
+ }
20
27
  result = (0, utils_1.keccak256)((0, utils_1.concat)([result, labelSha]));
21
28
  }
22
29
  }
@@ -1,3 +1,6 @@
1
+ export declare const enter: (node: any) => any;
2
+ export declare const requestMiddleware: (visit: any, parse: any) => (request: any) => any;
3
+ export declare const responseMiddleware: (traverse: any) => (response: any) => any;
1
4
  export default class GqlManager {
2
5
  gql: any;
3
6
  client?: any | null;
@@ -1,11 +1,67 @@
1
+ // @ts-nocheck
2
+ import { namehash } from './utils/normalise';
3
+ const generateSelection = (selection) => ({
4
+ kind: 'Field',
5
+ name: {
6
+ kind: 'Name',
7
+ value: selection,
8
+ },
9
+ arguments: [],
10
+ directives: [],
11
+ alias: undefined,
12
+ selectionSet: undefined,
13
+ });
14
+ export const enter = (node) => {
15
+ if (node.kind === 'SelectionSet') {
16
+ const id = node.selections.find((x) => x.name && x.name.value === 'id');
17
+ const name = node.selections.find((x) => x.name && x.name.value === 'name');
18
+ if (!id && name) {
19
+ node.selections = [...node.selections, generateSelection('id')];
20
+ return node;
21
+ }
22
+ }
23
+ };
24
+ export const requestMiddleware = (visit, parse) => (request) => {
25
+ const requestBody = JSON.parse(request.body);
26
+ const rawQuery = requestBody.query;
27
+ const parsedQuery = parse(rawQuery);
28
+ const updatedQuery = visit(parsedQuery, { enter });
29
+ const updatedBody = { ...requestBody, query: updatedQuery.loc.source.body };
30
+ return {
31
+ ...request,
32
+ body: JSON.stringify(updatedBody),
33
+ };
34
+ };
35
+ export const responseMiddleware = (traverse) => (response) => {
36
+ traverse(response).forEach(function (responseItem) {
37
+ if (responseItem instanceof Object && responseItem.name) {
38
+ //Name already in hashed form
39
+ if (responseItem.name && responseItem.name.includes('[')) {
40
+ return;
41
+ }
42
+ const hashedName = namehash(responseItem.name);
43
+ if (responseItem.id !== hashedName) {
44
+ this.update({ ...responseItem, name: hashedName, invalidName: true });
45
+ }
46
+ }
47
+ });
48
+ return response;
49
+ };
1
50
  export default class GqlManager {
2
51
  constructor() {
3
52
  this.gql = () => null;
4
53
  this.client = null;
5
54
  this.setUrl = async (url) => {
6
55
  if (url) {
7
- const imported = await import('graphql-request');
8
- this.client = new imported.GraphQLClient(url);
56
+ const [imported, traverse, { visit, parse }] = await Promise.all([
57
+ import('graphql-request'),
58
+ import('traverse'),
59
+ import('graphql/language'),
60
+ ]);
61
+ this.client = new imported.GraphQLClient(url, {
62
+ requestMiddleware: requestMiddleware(visit, parse),
63
+ responseMiddleware: responseMiddleware(traverse.default),
64
+ });
9
65
  this.gql = imported.gql;
10
66
  }
11
67
  else {
@@ -1,2 +1,2 @@
1
1
  export declare const normalise: (name: string) => any;
2
- export declare const namehash: (inputName: string) => string;
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 = (inputName) => {
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
- const labelSha = keccak256(toUtf8Bytes(labels[i]));
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ensdomains/ensjs",
3
- "version": "3.0.0-alpha.6",
3
+ "version": "3.0.0-alpha.7",
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,8 +46,9 @@
46
46
  "dns-packet": "^5.3.1",
47
47
  "ethers": "^5.6.1",
48
48
  "graphql": "^16.3.0",
49
- "graphql-request": "^4.1.0",
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",
@@ -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.5"
86
+ "stableVersion": "3.0.0-alpha.6"
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
+ })
package/src/GqlManager.ts CHANGED
@@ -1,11 +1,76 @@
1
+ // @ts-nocheck
2
+ import { namehash } from './utils/normalise'
3
+
4
+ const generateSelection = (selection: any) => ({
5
+ kind: 'Field',
6
+ name: {
7
+ kind: 'Name',
8
+ value: selection,
9
+ },
10
+ arguments: [],
11
+ directives: [],
12
+ alias: undefined,
13
+ selectionSet: undefined,
14
+ })
15
+
16
+ export const enter = (node: any) => {
17
+ if (node.kind === 'SelectionSet') {
18
+ const id = node.selections.find((x) => x.name && x.name.value === 'id')
19
+ const name = node.selections.find((x) => x.name && x.name.value === 'name')
20
+
21
+ if (!id && name) {
22
+ node.selections = [...node.selections, generateSelection('id')]
23
+ return node
24
+ }
25
+ }
26
+ }
27
+
28
+ export const requestMiddleware = (visit, parse) => (request: any) => {
29
+ const requestBody = JSON.parse(request.body)
30
+ const rawQuery = requestBody.query
31
+ const parsedQuery = parse(rawQuery)
32
+ const updatedQuery = visit(parsedQuery, { enter })
33
+ const updatedBody = { ...requestBody, query: updatedQuery.loc.source.body }
34
+
35
+ return {
36
+ ...request,
37
+ body: JSON.stringify(updatedBody),
38
+ }
39
+ }
40
+
41
+ export const responseMiddleware = (traverse) => (response: any) => {
42
+ traverse(response).forEach(function (responseItem: any) {
43
+ if (responseItem instanceof Object && responseItem.name) {
44
+ //Name already in hashed form
45
+ if (responseItem.name && responseItem.name.includes('[')) {
46
+ return
47
+ }
48
+
49
+ const hashedName = namehash(responseItem.name)
50
+ if (responseItem.id !== hashedName) {
51
+ this.update({ ...responseItem, name: hashedName, invalidName: true })
52
+ }
53
+ }
54
+ })
55
+ return response
56
+ }
57
+
1
58
  export default class GqlManager {
2
59
  public gql: any = () => null
3
60
  public client?: any | null = null
4
61
 
5
62
  public setUrl = async (url: string | null) => {
6
63
  if (url) {
7
- const imported = await import('graphql-request')
8
- this.client = new imported.GraphQLClient(url)
64
+ const [imported, traverse, { visit, parse }] = await Promise.all([
65
+ import('graphql-request'),
66
+ import('traverse'),
67
+ import('graphql/language'),
68
+ ])
69
+
70
+ this.client = new imported.GraphQLClient(url, {
71
+ requestMiddleware: requestMiddleware(visit, parse),
72
+ responseMiddleware: responseMiddleware(traverse.default),
73
+ })
9
74
  this.gql = imported.gql
10
75
  } else {
11
76
  this.client = null
@@ -1,5 +1,6 @@
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
 
4
5
  const zeros = new Uint8Array(32)
5
6
  zeros.fill(0)
@@ -7,16 +8,21 @@ zeros.fill(0)
7
8
  export const normalise = (name: string) =>
8
9
  name ? uts46.toUnicode(name, { useStd3ASCII: true }) : name
9
10
 
10
- export const namehash = (inputName: string): string => {
11
+ export const namehash = (name: string): string => {
11
12
  let result: string | Uint8Array = zeros
12
13
 
13
- const name = normalise(inputName)
14
-
15
14
  if (name) {
16
15
  const labels = name.split('.')
17
16
 
18
17
  for (var i = labels.length - 1; i >= 0; i--) {
19
- const labelSha = keccak256(toUtf8Bytes(labels[i]))
18
+ let labelSha: string
19
+ if (isEncodedLabelhash(labels[i])) {
20
+ labelSha = decodeLabelhash(labels[i])
21
+ } else {
22
+ const normalised = normalise(labels[i])
23
+ labelSha = keccak256(toUtf8Bytes(normalised))
24
+ }
25
+
20
26
  result = keccak256(concat([result, labelSha]))
21
27
  }
22
28
  } else {