@ensdomains/ensjs 3.0.0-alpha.5 → 3.0.0-alpha.8

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.
Files changed (86) hide show
  1. package/dist/cjs/GqlManager.d.ts +3 -0
  2. package/dist/cjs/GqlManager.js +62 -2
  3. package/dist/cjs/functions/burnFuses.d.ts +1 -1
  4. package/dist/cjs/functions/burnFuses.js +1 -1
  5. package/dist/cjs/functions/createSubname.d.ts +1 -1
  6. package/dist/cjs/functions/createSubname.js +2 -2
  7. package/dist/cjs/functions/deleteSubname.d.ts +1 -1
  8. package/dist/cjs/functions/deleteSubname.js +1 -1
  9. package/dist/cjs/functions/getProfile.d.ts +4 -1
  10. package/dist/cjs/functions/getProfile.js +62 -18
  11. package/dist/cjs/functions/getRecords.d.ts +1 -0
  12. package/dist/cjs/functions/setName.d.ts +1 -1
  13. package/dist/cjs/functions/setName.js +2 -2
  14. package/dist/cjs/functions/setRecords.d.ts +1 -1
  15. package/dist/cjs/functions/setRecords.js +1 -1
  16. package/dist/cjs/functions/setResolver.d.ts +1 -1
  17. package/dist/cjs/functions/setResolver.js +2 -2
  18. package/dist/cjs/functions/transferName.d.ts +1 -1
  19. package/dist/cjs/functions/transferName.js +3 -3
  20. package/dist/cjs/functions/transferSubname.d.ts +1 -1
  21. package/dist/cjs/functions/transferSubname.js +2 -2
  22. package/dist/cjs/functions/unwrapName.d.ts +1 -1
  23. package/dist/cjs/functions/unwrapName.js +2 -2
  24. package/dist/cjs/functions/wrapName.d.ts +2 -2
  25. package/dist/cjs/functions/wrapName.js +3 -3
  26. package/dist/cjs/index.d.ts +34 -52
  27. package/dist/cjs/index.js +10 -6
  28. package/dist/cjs/utils/labels.js +4 -3
  29. package/dist/cjs/utils/normalise.d.ts +1 -1
  30. package/dist/cjs/utils/normalise.js +10 -3
  31. package/dist/cjs/utils/writeTx.d.ts +4 -0
  32. package/dist/cjs/utils/writeTx.js +3 -0
  33. package/dist/esm/GqlManager.d.ts +3 -0
  34. package/dist/esm/GqlManager.js +58 -2
  35. package/dist/esm/functions/burnFuses.d.ts +1 -1
  36. package/dist/esm/functions/burnFuses.js +1 -1
  37. package/dist/esm/functions/createSubname.d.ts +1 -1
  38. package/dist/esm/functions/createSubname.js +2 -2
  39. package/dist/esm/functions/deleteSubname.d.ts +1 -1
  40. package/dist/esm/functions/deleteSubname.js +1 -1
  41. package/dist/esm/functions/getProfile.d.ts +4 -1
  42. package/dist/esm/functions/getProfile.js +62 -18
  43. package/dist/esm/functions/getRecords.d.ts +1 -0
  44. package/dist/esm/functions/setName.d.ts +1 -1
  45. package/dist/esm/functions/setName.js +2 -2
  46. package/dist/esm/functions/setRecords.d.ts +1 -1
  47. package/dist/esm/functions/setRecords.js +1 -1
  48. package/dist/esm/functions/setResolver.d.ts +1 -1
  49. package/dist/esm/functions/setResolver.js +2 -2
  50. package/dist/esm/functions/transferName.d.ts +1 -1
  51. package/dist/esm/functions/transferName.js +3 -3
  52. package/dist/esm/functions/transferSubname.d.ts +1 -1
  53. package/dist/esm/functions/transferSubname.js +2 -2
  54. package/dist/esm/functions/unwrapName.d.ts +1 -1
  55. package/dist/esm/functions/unwrapName.js +2 -2
  56. package/dist/esm/functions/wrapName.d.ts +2 -2
  57. package/dist/esm/functions/wrapName.js +3 -3
  58. package/dist/esm/index.d.ts +34 -52
  59. package/dist/esm/index.js +10 -6
  60. package/dist/esm/utils/labels.js +4 -3
  61. package/dist/esm/utils/normalise.d.ts +1 -1
  62. package/dist/esm/utils/normalise.js +10 -3
  63. package/dist/esm/utils/writeTx.d.ts +4 -0
  64. package/dist/esm/utils/writeTx.js +1 -0
  65. package/package.json +7 -5
  66. package/src/GqlManager.test.ts +170 -0
  67. package/src/GqlManager.ts +67 -2
  68. package/src/functions/burnFuses.ts +1 -1
  69. package/src/functions/createSubname.ts +2 -2
  70. package/src/functions/deleteSubname.ts +1 -1
  71. package/src/functions/getProfile.test.ts +16 -1
  72. package/src/functions/getProfile.ts +82 -19
  73. package/src/functions/getRecords.ts +1 -0
  74. package/src/functions/setName.ts +5 -5
  75. package/src/functions/setRecords.ts +2 -2
  76. package/src/functions/setResolver.ts +5 -2
  77. package/src/functions/transferName.ts +5 -3
  78. package/src/functions/transferSubname.ts +11 -2
  79. package/src/functions/unwrapName.ts +10 -2
  80. package/src/functions/wrapName.ts +5 -8
  81. package/src/index.ts +37 -12
  82. package/src/tests/populateTransaction.test.ts +40 -0
  83. package/src/tests/signerInjection.test.ts +7 -3
  84. package/src/utils/labels.ts +5 -3
  85. package/src/utils/normalise.ts +10 -4
  86. package/src/utils/writeTx.ts +6 -0
package/dist/cjs/index.js CHANGED
@@ -31,6 +31,7 @@ const contracts_1 = __importDefault(require("./contracts"));
31
31
  const getContractAddress_1 = require("./contracts/getContractAddress");
32
32
  const GqlManager_1 = __importDefault(require("./GqlManager"));
33
33
  const singleCall_1 = __importDefault(require("./utils/singleCall"));
34
+ const writeTx_1 = __importDefault(require("./utils/writeTx"));
34
35
  const graphURIEndpoints = {
35
36
  1: 'https://api.thegraph.com/subgraphs/name/ensdomains/ens',
36
37
  3: 'https://api.thegraph.com/subgraphs/name/ensdomains/ensropsten',
@@ -89,29 +90,29 @@ class ENS {
89
90
  await thisRef.checkInitialProvider();
90
91
  // import the module dynamically
91
92
  const mod = await Promise.resolve().then(() => __importStar(require(
92
- /* webpackMode: "lazy", webpackChunkName: "[request]", webpackPreload: true */
93
+ /* webpackMode: "lazy", webpackChunkName: "[request]", webpackPreload: true, webpackExclude: /.*\.ts$/ */
93
94
  `./functions/${path}`)));
94
95
  // if combine isn't specified, run normally
95
96
  // otherwise, create a function from the raw and decode functions
96
97
  if (subFunc !== 'combine') {
98
+ const writeable = subFunc === 'write' || subFunc === 'populateTransaction';
97
99
  // get the function to call
98
- const func = subFunc && subFunc !== 'write'
99
- ? mod[exportName][subFunc]
100
- : mod[exportName];
100
+ const func = subFunc && !writeable ? mod[exportName][subFunc] : mod[exportName];
101
101
  // get the dependencies to forward to the function as the first arg
102
102
  let dependenciesToForward = thisRef.forwardDependenciesFromArray(dependencies);
103
103
  // if func is write func, inject signer into dependencies
104
- if (subFunc === 'write') {
104
+ if (writeable) {
105
105
  const options = (args[1] || {});
106
106
  const signer = options.signer ||
107
107
  thisRef.provider?.getSigner(options.addressOrIndex);
108
+ const populate = subFunc === 'populateTransaction';
108
109
  if (!signer) {
109
110
  throw new Error('No signer specified');
110
111
  }
111
112
  delete options.addressOrIndex;
112
113
  delete options.signer;
113
114
  dependenciesToForward = { ...dependenciesToForward, signer };
114
- return func(dependenciesToForward, args[0], options);
115
+ return func(dependenciesToForward, args[0], options).then((0, writeTx_1.default)(signer, populate));
115
116
  }
116
117
  // return the function with the dependencies forwarded
117
118
  return func(dependenciesToForward, ...args);
@@ -129,6 +130,9 @@ class ENS {
129
130
  mainFunc.decode = this.importGenerator(path, dependencies, exportName, 'decode');
130
131
  mainFunc.batch = this.importGenerator(path, dependencies, exportName, 'batch', { raw: mainFunc.raw, decode: mainFunc.decode });
131
132
  }
133
+ else if (subFunc === 'write') {
134
+ mainFunc.populateTransaction = this.importGenerator(path, dependencies, exportName, 'populateTransaction');
135
+ }
132
136
  return mainFunc;
133
137
  };
134
138
  /**
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.checkLocalStorageSize = exports.truncateUndecryptedName = exports.decryptName = exports.checkIsDecrypted = exports.parseName = exports.encodeLabel = exports.checkLabel = exports.saveName = exports.saveLabel = exports.isEncodedLabelhash = exports.encodeLabelhash = exports.decodeLabelhash = exports.keccakFromString = exports.labelhash = void 0;
4
4
  const utils_1 = require("ethers/lib/utils");
5
5
  const format_1 = require("./format");
6
+ const hasLocalStorage = typeof localStorage !== 'undefined';
6
7
  const labelhash = (input) => (0, utils_1.solidityKeccak256)(['string'], [input]);
7
8
  exports.labelhash = labelhash;
8
9
  const keccakFromString = (input) => (0, exports.labelhash)(input);
@@ -32,12 +33,12 @@ function isEncodedLabelhash(hash) {
32
33
  }
33
34
  exports.isEncodedLabelhash = isEncodedLabelhash;
34
35
  function getLabels() {
35
- return localStorage
36
+ return hasLocalStorage
36
37
  ? JSON.parse(localStorage.getItem('ensjs:labels')) || {}
37
38
  : {};
38
39
  }
39
40
  function _saveLabel(hash, label) {
40
- if (!localStorage)
41
+ if (!hasLocalStorage)
41
42
  return hash;
42
43
  const labels = getLabels();
43
44
  localStorage.setItem('ensjs:labels', JSON.stringify({
@@ -97,7 +98,7 @@ exports.decryptName = decryptName;
97
98
  const truncateUndecryptedName = (name) => (0, format_1.truncateFormat)(name);
98
99
  exports.truncateUndecryptedName = truncateUndecryptedName;
99
100
  function checkLocalStorageSize() {
100
- if (!localStorage)
101
+ if (!hasLocalStorage)
101
102
  return 'Empty (0 KB)';
102
103
  let allStrings = '';
103
104
  for (const key in window.localStorage) {
@@ -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
  }
@@ -0,0 +1,4 @@
1
+ import { JsonRpcSigner } from '@ethersproject/providers';
2
+ import type { PopulatedTransaction } from 'ethers';
3
+ declare const _default: (signer: JsonRpcSigner, populate: boolean) => (tx: PopulatedTransaction) => PopulatedTransaction | Promise<import("@ethersproject/abstract-provider").TransactionResponse>;
4
+ export default _default;
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = (signer, populate) => (tx) => populate ? tx : signer.sendTransaction(tx);
@@ -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 {
@@ -2,4 +2,4 @@ import { ENSArgs } from '..';
2
2
  import { FuseOptions } from '../@types/FuseOptions';
3
3
  export default function ({ contracts, signer }: ENSArgs<'contracts' | 'signer'>, name: string, { fusesToBurn, }: {
4
4
  fusesToBurn: FuseOptions;
5
- }): Promise<import("ethers").ContractTransaction>;
5
+ }): Promise<import("ethers").PopulatedTransaction>;
@@ -4,5 +4,5 @@ export default async function ({ contracts, signer }, name, { fusesToBurn, }) {
4
4
  const nameWrapper = (await contracts?.getNameWrapper()).connect(signer);
5
5
  const hash = namehash(name);
6
6
  const encodedFuses = generateFuseInput(fusesToBurn);
7
- return nameWrapper.burnFuses(hash, encodedFuses);
7
+ return nameWrapper.populateTransaction.burnFuses(hash, encodedFuses);
8
8
  }
@@ -11,5 +11,5 @@ declare type NameWrapperArgs = {
11
11
  fuses?: FuseOptions;
12
12
  } & BaseArgs;
13
13
  declare type Args = BaseArgs | NameWrapperArgs;
14
- export default function ({ contracts, signer }: ENSArgs<'contracts' | 'signer'>, name: string, { owner, resolverAddress, contract, ...wrapperArgs }: Args): Promise<ethers.ContractTransaction>;
14
+ export default function ({ contracts, signer }: ENSArgs<'contracts' | 'signer'>, name: string, { owner, resolverAddress, contract, ...wrapperArgs }: Args): Promise<ethers.PopulatedTransaction>;
15
15
  export {};
@@ -18,14 +18,14 @@ export default async function ({ contracts, signer }, name, { owner, resolverAdd
18
18
  switch (contract) {
19
19
  case 'registry': {
20
20
  const registry = (await contracts?.getRegistry()).connect(signer);
21
- return registry.setSubnodeRecord(parentNodehash, labelhash, owner, resolverAddress, 0);
21
+ return registry.populateTransaction.setSubnodeRecord(parentNodehash, labelhash, owner, resolverAddress, 0);
22
22
  }
23
23
  case 'nameWrapper': {
24
24
  const nameWrapper = (await contracts?.getNameWrapper()).connect(signer);
25
25
  const generatedFuses = 'fuses' in wrapperArgs && wrapperArgs.fuses
26
26
  ? generateFuseInput(wrapperArgs.fuses)
27
27
  : '0';
28
- return nameWrapper.setSubnodeRecord(parentNodehash, label, owner, resolverAddress, 0, generatedFuses);
28
+ return nameWrapper.populateTransaction.setSubnodeRecord(parentNodehash, label, owner, resolverAddress, 0, generatedFuses);
29
29
  }
30
30
  default: {
31
31
  throw new Error(`Unknown contract: ${contract}`);
@@ -1,4 +1,4 @@
1
1
  import { ENSArgs } from '..';
2
2
  export default function ({ transferSubname }: ENSArgs<'transferSubname'>, name: string, { contract, }: {
3
3
  contract: 'registry' | 'nameWrapper';
4
- }): Promise<import("ethers").ContractTransaction>;
4
+ }): Promise<import("ethers").PopulatedTransaction>;
@@ -1,5 +1,5 @@
1
1
  export default async function ({ transferSubname }, name, { contract, }) {
2
- return transferSubname(name, {
2
+ return transferSubname.populateTransaction(name, {
3
3
  contract,
4
4
  address: '0x0000000000000000000000000000000000000000',
5
5
  });
@@ -26,5 +26,8 @@ declare type ProfileOptions = {
26
26
  texts?: boolean | string[];
27
27
  coinTypes?: boolean | string[];
28
28
  };
29
- export default function ({ contracts, gqlInstance, getName, _getAddr, _getContentHash, _getText, resolverMulticallWrapper, multicallWrapper, }: ENSArgs<'contracts' | 'gqlInstance' | 'getName' | '_getText' | '_getAddr' | '_getContentHash' | 'resolverMulticallWrapper' | 'multicallWrapper'>, nameOrAddress: string, options?: ProfileOptions): Promise<ResolvedProfile | undefined>;
29
+ declare type InputProfileOptions = ProfileOptions & {
30
+ resolverAddress?: string;
31
+ };
32
+ export default function ({ contracts, gqlInstance, getName, _getAddr, _getContentHash, _getText, resolverMulticallWrapper, multicallWrapper, }: ENSArgs<'contracts' | 'gqlInstance' | 'getName' | '_getText' | '_getAddr' | '_getContentHash' | 'resolverMulticallWrapper' | 'multicallWrapper'>, nameOrAddress: string, options?: InputProfileOptions): Promise<ResolvedProfile | undefined>;
30
33
  export {};
@@ -2,6 +2,7 @@ import { formatsByName } from '@ensdomains/address-encoder';
2
2
  import { ethers } from 'ethers';
3
3
  import { decodeContenthash } from '../utils/contentHash';
4
4
  import { hexEncodeName } from '../utils/hexEncodedName';
5
+ import { namehash } from '../utils/normalise';
5
6
  import { parseInputType } from '../utils/validation';
6
7
  const makeMulticallData = async ({ _getAddr, _getContentHash, _getText, resolverMulticallWrapper, }, name, options) => {
7
8
  let calls = [];
@@ -47,13 +48,19 @@ const fetchWithoutResolverMulticall = async ({ multicallWrapper }, calls, resolv
47
48
  }));
48
49
  return (await multicallWrapper(callsWithResolver)).map((x) => x[1]);
49
50
  };
50
- const getDataForName = async ({ contracts, _getAddr, _getContentHash, _getText, resolverMulticallWrapper, multicallWrapper, }, name, options, fallbackResolver) => {
51
+ const getDataForName = async ({ contracts, _getAddr, _getContentHash, _getText, resolverMulticallWrapper, multicallWrapper, }, name, options, fallbackResolver, specificResolver) => {
51
52
  const universalResolver = await contracts?.getUniversalResolver();
52
53
  const { data, calls } = await makeMulticallData({ _getAddr, _getContentHash, _getText, resolverMulticallWrapper }, name, options);
53
54
  let resolvedData;
54
55
  let useFallbackResolver = false;
55
56
  try {
56
- resolvedData = await universalResolver?.resolve(hexEncodeName(name), data);
57
+ if (specificResolver) {
58
+ const publicResolver = await contracts?.getPublicResolver(undefined, specificResolver);
59
+ resolvedData = await publicResolver?.callStatic.multicall(calls.map((x) => x.data));
60
+ }
61
+ else {
62
+ resolvedData = await universalResolver?.resolve(hexEncodeName(name), data);
63
+ }
57
64
  }
58
65
  catch {
59
66
  useFallbackResolver = true;
@@ -61,12 +68,18 @@ const getDataForName = async ({ contracts, _getAddr, _getContentHash, _getText,
61
68
  let resolverAddress;
62
69
  let recordData;
63
70
  if (useFallbackResolver) {
64
- resolverAddress = fallbackResolver;
71
+ resolverAddress = specificResolver || fallbackResolver;
65
72
  recordData = await fetchWithoutResolverMulticall({ multicallWrapper }, calls, resolverAddress);
66
73
  }
67
74
  else {
68
- resolverAddress = resolvedData['1'];
69
- [recordData] = await resolverMulticallWrapper.decode(resolvedData['0']);
75
+ resolverAddress = specificResolver || resolvedData['1'];
76
+ if (specificResolver) {
77
+ recordData = resolvedData;
78
+ }
79
+ else {
80
+ ;
81
+ [recordData] = await resolverMulticallWrapper.decode(resolvedData['0']);
82
+ }
70
83
  }
71
84
  const matchAddress = recordData[calls.findIndex((x) => x.key === '60')];
72
85
  return {
@@ -158,10 +171,10 @@ const formatRecords = async ({ _getText, _getAddr, _getContentHash, }, data, cal
158
171
  }
159
172
  return returnedResponse;
160
173
  };
161
- const graphFetch = async ({ gqlInstance }, name, wantedRecords) => {
174
+ const graphFetch = async ({ gqlInstance }, name, wantedRecords, resolverAddress) => {
162
175
  const query = gqlInstance.gql `
163
- query getRecords($name: String!) {
164
- domains(where: { name: $name }) {
176
+ query getRecords($id: String!) {
177
+ domain(id: $id) {
165
178
  isMigrated
166
179
  createdAt
167
180
  resolver {
@@ -175,12 +188,39 @@ const graphFetch = async ({ gqlInstance }, name, wantedRecords) => {
175
188
  }
176
189
  }
177
190
  }
191
+ `;
192
+ const customResolverQuery = gqlInstance.gql `
193
+ query getRecordsWithCustomResolver($id: String!, $resolverId: String!) {
194
+ domain(id: $id) {
195
+ isMigrated
196
+ createdAt
197
+ }
198
+ resolver(id: $resolverId) {
199
+ texts
200
+ coinTypes
201
+ contentHash
202
+ addr {
203
+ id
204
+ }
205
+ }
206
+ }
178
207
  `;
179
208
  const client = gqlInstance.client;
180
- const { domains } = await client.request(query, { name });
181
- if (!domains || domains.length === 0)
209
+ const id = namehash(name);
210
+ let domain;
211
+ let resolverResponse;
212
+ if (!resolverAddress) {
213
+ ;
214
+ ({ domain } = await client.request(query, { id }));
215
+ resolverResponse = domain?.resolver;
216
+ }
217
+ else {
218
+ const resolverId = `${resolverAddress}-${id}`;
219
+ ({ resolver: resolverResponse, domain } = await client.request(customResolverQuery, { id, resolverId }));
220
+ }
221
+ if (!domain)
182
222
  return;
183
- const [{ resolver: resolverResponse, isMigrated, createdAt }] = domains;
223
+ const { isMigrated, createdAt } = domain;
184
224
  let returnedRecords = {};
185
225
  if (!resolverResponse)
186
226
  return { isMigrated, createdAt };
@@ -188,7 +228,7 @@ const graphFetch = async ({ gqlInstance }, name, wantedRecords) => {
188
228
  return {
189
229
  isMigrated,
190
230
  createdAt,
191
- graphResolverAddress: resolverResponse.address,
231
+ graphResolverAddress: resolverResponse.address || resolverAddress,
192
232
  };
193
233
  Object.keys(wantedRecords).forEach((key) => {
194
234
  const data = wantedRecords[key];
@@ -205,18 +245,22 @@ const graphFetch = async ({ gqlInstance }, name, wantedRecords) => {
205
245
  ...returnedRecords,
206
246
  isMigrated,
207
247
  createdAt,
208
- graphResolverAddress: resolverResponse.address,
248
+ graphResolverAddress: resolverResponse.address || resolverAddress,
209
249
  };
210
250
  };
211
251
  const getProfileFromName = async ({ contracts, gqlInstance, _getAddr, _getContentHash, _getText, resolverMulticallWrapper, multicallWrapper, }, name, options) => {
212
- const usingOptions = !options || options?.texts === true || options?.coinTypes === true
213
- ? options || { contentHash: true, texts: true, coinTypes: true }
252
+ const { resolverAddress, ..._options } = options || {};
253
+ const optsLength = Object.keys(_options).length;
254
+ const usingOptions = !optsLength || _options?.texts === true || _options?.coinTypes === true
255
+ ? optsLength
256
+ ? _options
257
+ : { contentHash: true, texts: true, coinTypes: true }
214
258
  : undefined;
215
- const graphResult = await graphFetch({ gqlInstance }, name, usingOptions);
259
+ const graphResult = await graphFetch({ gqlInstance }, name, usingOptions, resolverAddress);
216
260
  if (!graphResult)
217
261
  return;
218
262
  const { isMigrated, createdAt, graphResolverAddress, ...wantedRecords } = graphResult;
219
- if (!graphResolverAddress)
263
+ if (!graphResolverAddress && !options?.resolverAddress)
220
264
  return { isMigrated, createdAt, message: "Name doesn't have a resolver" };
221
265
  const result = await getDataForName({
222
266
  contracts,
@@ -225,7 +269,7 @@ const getProfileFromName = async ({ contracts, gqlInstance, _getAddr, _getConten
225
269
  _getText,
226
270
  resolverMulticallWrapper,
227
271
  multicallWrapper,
228
- }, name, usingOptions ? wantedRecords : options, graphResolverAddress);
272
+ }, name, usingOptions ? wantedRecords : options, graphResolverAddress, options?.resolverAddress);
229
273
  if (!result)
230
274
  return { isMigrated, createdAt, message: "Records fetch didn't complete" };
231
275
  return { ...result, isMigrated, createdAt, message: undefined };
@@ -3,6 +3,7 @@ declare type ProfileOptions = {
3
3
  contentHash?: boolean;
4
4
  texts?: boolean | string[];
5
5
  coinTypes?: boolean | string[];
6
+ resolverAddress?: string;
6
7
  };
7
8
  export default function ({ getProfile }: ENSArgs<'getProfile'>, name: string, options?: ProfileOptions): Promise<{
8
9
  isMigrated: boolean | null;
@@ -2,4 +2,4 @@ import { ENSArgs } from '..';
2
2
  export default function ({ contracts, signer }: ENSArgs<'contracts' | 'signer'>, name: string, { address, resolver, }?: {
3
3
  address?: string;
4
4
  resolver?: string;
5
- }): Promise<import("ethers").ContractTransaction | undefined>;
5
+ }): Promise<import("ethers").PopulatedTransaction>;
@@ -3,7 +3,7 @@ export default async function ({ contracts, signer }, name, { address, resolver,
3
3
  const reverseRegistrar = (await contracts?.getReverseRegistrar())?.connect(signer);
4
4
  if (address) {
5
5
  const publicResolver = await contracts?.getPublicResolver();
6
- return reverseRegistrar?.setNameForAddr(address, signerAddress, resolver || publicResolver.address, name);
6
+ return reverseRegistrar.populateTransaction.setNameForAddr(address, signerAddress, resolver || publicResolver.address, name);
7
7
  }
8
- return reverseRegistrar?.setName(name);
8
+ return reverseRegistrar.populateTransaction.setName(name);
9
9
  }
@@ -3,4 +3,4 @@ import { RecordOptions } from '../utils/recordHelpers';
3
3
  export default function ({ contracts, provider, getResolver, signer, }: ENSArgs<'contracts' | 'provider' | 'getResolver' | 'signer'>, name: string, { records, resolverAddress, }: {
4
4
  records: RecordOptions;
5
5
  resolverAddress?: string;
6
- }): Promise<import("ethers").ContractTransaction | undefined>;
6
+ }): Promise<import("ethers").PopulatedTransaction>;
@@ -17,5 +17,5 @@ export default async function ({ contracts, provider, getResolver, signer, }, na
17
17
  const resolver = (await contracts?.getPublicResolver(provider, resolverToUse))?.connect(signer);
18
18
  const hash = namehash(name);
19
19
  const calls = generateRecordCallArray(hash, records, resolver);
20
- return resolver?.multicall(calls);
20
+ return resolver.populateTransaction.multicall(calls);
21
21
  }
@@ -2,4 +2,4 @@ import { ENSArgs } from '..';
2
2
  export default function ({ contracts, signer }: ENSArgs<'contracts' | 'signer'>, name: string, { contract, resolver, }: {
3
3
  contract: 'registry' | 'nameWrapper';
4
4
  resolver?: string;
5
- }): Promise<import("ethers").ContractTransaction>;
5
+ }): Promise<import("ethers").PopulatedTransaction>;
@@ -6,11 +6,11 @@ export default async function ({ contracts, signer }, name, { contract, resolver
6
6
  switch (contract) {
7
7
  case 'registry': {
8
8
  const registry = (await contracts?.getRegistry()).connect(signer);
9
- return registry.setResolver(namehash(name), resolver);
9
+ return registry.populateTransaction.setResolver(namehash(name), resolver);
10
10
  }
11
11
  case 'nameWrapper': {
12
12
  const nameWrapper = (await contracts?.getNameWrapper()).connect(signer);
13
- return nameWrapper.setResolver(namehash(name), resolver);
13
+ return nameWrapper.populateTransaction.setResolver(namehash(name), resolver);
14
14
  }
15
15
  default: {
16
16
  throw new Error(`Unknown contract: ${contract}`);
@@ -3,4 +3,4 @@ import { ENSArgs } from '..';
3
3
  export default function ({ contracts, signer }: ENSArgs<'contracts' | 'signer'>, name: string, { newOwner, contract, }: {
4
4
  newOwner: string;
5
5
  contract: 'registry' | 'nameWrapper' | 'baseRegistrar';
6
- }): Promise<ethers.ContractTransaction>;
6
+ }): Promise<ethers.PopulatedTransaction>;
@@ -5,7 +5,7 @@ export default async function ({ contracts, signer }, name, { newOwner, contract
5
5
  switch (contract) {
6
6
  case 'registry': {
7
7
  const registry = (await contracts?.getRegistry()).connect(signer);
8
- return registry.setOwner(namehash(name), newOwner);
8
+ return registry.populateTransaction.setOwner(namehash(name), newOwner);
9
9
  }
10
10
  case 'baseRegistrar': {
11
11
  const baseRegistrar = (await contracts?.getBaseRegistrar()).connect(signer);
@@ -13,11 +13,11 @@ export default async function ({ contracts, signer }, name, { newOwner, contract
13
13
  if (labels.length > 2 || labels[labels.length - 1] !== 'eth') {
14
14
  throw new Error('Invalid name for baseRegistrar');
15
15
  }
16
- return baseRegistrar['safeTransferFrom(address,address,uint256)'](address, newOwner, ethers.utils.solidityKeccak256(['string'], [labels[0]]));
16
+ return baseRegistrar.populateTransaction['safeTransferFrom(address,address,uint256)'](address, newOwner, ethers.utils.solidityKeccak256(['string'], [labels[0]]));
17
17
  }
18
18
  case 'nameWrapper': {
19
19
  const nameWrapper = (await contracts?.getNameWrapper()).connect(signer);
20
- return nameWrapper.safeTransferFrom(address, newOwner, namehash(name), 1, '0x');
20
+ return nameWrapper.populateTransaction.safeTransferFrom(address, newOwner, namehash(name), 1, '0x');
21
21
  }
22
22
  default: {
23
23
  throw new Error(`Unknown contract: ${contract}`);
@@ -3,4 +3,4 @@ import { ENSArgs } from '..';
3
3
  export default function ({ contracts, signer }: ENSArgs<'contracts' | 'signer'>, name: string, { contract, address, }: {
4
4
  contract: 'registry' | 'nameWrapper';
5
5
  address: string;
6
- }): Promise<ethers.ContractTransaction>;
6
+ }): Promise<ethers.PopulatedTransaction>;
@@ -8,11 +8,11 @@ export default async function ({ contracts, signer }, name, { contract, address,
8
8
  switch (contract) {
9
9
  case 'registry': {
10
10
  const registry = (await contracts?.getRegistry()).connect(signer);
11
- return registry.setSubnodeOwner(parentNodehash, labelhash, address);
11
+ return registry.populateTransaction.setSubnodeOwner(parentNodehash, labelhash, address);
12
12
  }
13
13
  case 'nameWrapper': {
14
14
  const nameWrapper = (await contracts?.getNameWrapper()).connect(signer);
15
- return nameWrapper.setSubnodeOwner(parentNodehash, label, address, '0');
15
+ return nameWrapper.populateTransaction.setSubnodeOwner(parentNodehash, label, address, '0');
16
16
  }
17
17
  default: {
18
18
  throw new Error(`Unknown contract: ${contract}`);
@@ -2,4 +2,4 @@ import { ENSArgs } from '..';
2
2
  export default function ({ contracts, signer }: ENSArgs<'contracts' | 'signer'>, name: string, { newController, newRegistrant, }: {
3
3
  newController: string;
4
4
  newRegistrant?: string;
5
- }): Promise<import("ethers").ContractTransaction>;
5
+ }): Promise<import("ethers").PopulatedTransaction>;
@@ -9,12 +9,12 @@ export default async function ({ contracts, signer }, name, { newController, new
9
9
  if (!newRegistrant) {
10
10
  throw new Error('newRegistrant must be specified for .eth names');
11
11
  }
12
- return nameWrapper.unwrapETH2LD(labelhash, newRegistrant, newController);
12
+ return nameWrapper.populateTransaction.unwrapETH2LD(labelhash, newRegistrant, newController);
13
13
  }
14
14
  else {
15
15
  if (newRegistrant) {
16
16
  throw new Error('newRegistrant can only be specified for .eth names');
17
17
  }
18
- return nameWrapper.unwrap(parentNodehash, labelhash, newController);
18
+ return nameWrapper.populateTransaction.unwrap(parentNodehash, labelhash, newController);
19
19
  }
20
20
  }
@@ -1,8 +1,8 @@
1
1
  import { ethers } from 'ethers';
2
2
  import { ENSArgs } from '..';
3
3
  import type { FuseOptions } from '../@types/FuseOptions';
4
- export default function ({ contracts, signer }: ENSArgs<'contracts' | 'signer'>, name: string, { wrappedOwner, fuseOptions, resolverAddress, }: {
4
+ export default function ({ contracts, signer, populate }: ENSArgs<'contracts' | 'signer' | 'populate'>, name: string, { wrappedOwner, fuseOptions, resolverAddress, }: {
5
5
  wrappedOwner: string;
6
6
  fuseOptions?: FuseOptions | string | number;
7
7
  resolverAddress?: string;
8
- }): Promise<ethers.ContractTransaction>;
8
+ }): Promise<ethers.PopulatedTransaction>;
@@ -6,7 +6,7 @@ async function wrapETH({ contracts }, labels, wrappedOwner, decodedFuses, resolv
6
6
  const baseRegistrar = (await contracts?.getBaseRegistrar()).connect(signer);
7
7
  const labelhash = ethers.utils.solidityKeccak256(['string'], [labels[0]]);
8
8
  const data = ethers.utils.defaultAbiCoder.encode(['string', 'address', 'uint32', 'uint64', 'address'], [labels[0], wrappedOwner, '0x0', decodedFuses, resolverAddress]);
9
- return baseRegistrar['safeTransferFrom(address,address,uint256,bytes)'](address, nameWrapper.address, labelhash, data);
9
+ return baseRegistrar.populateTransaction['safeTransferFrom(address,address,uint256,bytes)'](address, nameWrapper.address, labelhash, data);
10
10
  }
11
11
  async function wrapOther({ contracts }, name, wrappedOwner, decodedFuses, resolverAddress, address, signer) {
12
12
  const nameWrapper = (await contracts?.getNameWrapper()).connect(signer);
@@ -15,9 +15,9 @@ async function wrapOther({ contracts }, name, wrappedOwner, decodedFuses, resolv
15
15
  if (!hasApproval) {
16
16
  throw new Error('NameWrapper must have approval to wrap a name from this address.');
17
17
  }
18
- return nameWrapper.wrap(hexEncodeName(name), wrappedOwner, decodedFuses, resolverAddress);
18
+ return nameWrapper.populateTransaction.wrap(hexEncodeName(name), wrappedOwner, decodedFuses, resolverAddress);
19
19
  }
20
- export default async function ({ contracts, signer }, name, { wrappedOwner, fuseOptions, resolverAddress, }) {
20
+ export default async function ({ contracts, signer, populate }, name, { wrappedOwner, fuseOptions, resolverAddress, }) {
21
21
  const address = await signer.getAddress();
22
22
  let decodedFuses;
23
23
  switch (typeof fuseOptions) {