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

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 (63) hide show
  1. package/dist/cjs/functions/burnFuses.d.ts +1 -1
  2. package/dist/cjs/functions/burnFuses.js +1 -1
  3. package/dist/cjs/functions/createSubname.d.ts +1 -1
  4. package/dist/cjs/functions/createSubname.js +2 -2
  5. package/dist/cjs/functions/deleteSubname.d.ts +1 -1
  6. package/dist/cjs/functions/deleteSubname.js +1 -1
  7. package/dist/cjs/functions/setName.d.ts +1 -1
  8. package/dist/cjs/functions/setName.js +2 -2
  9. package/dist/cjs/functions/setRecords.d.ts +1 -1
  10. package/dist/cjs/functions/setRecords.js +1 -1
  11. package/dist/cjs/functions/setResolver.d.ts +1 -1
  12. package/dist/cjs/functions/setResolver.js +2 -2
  13. package/dist/cjs/functions/transferName.d.ts +1 -1
  14. package/dist/cjs/functions/transferName.js +3 -3
  15. package/dist/cjs/functions/transferSubname.d.ts +1 -1
  16. package/dist/cjs/functions/transferSubname.js +2 -2
  17. package/dist/cjs/functions/unwrapName.d.ts +1 -1
  18. package/dist/cjs/functions/unwrapName.js +2 -2
  19. package/dist/cjs/functions/wrapName.d.ts +2 -2
  20. package/dist/cjs/functions/wrapName.js +3 -3
  21. package/dist/cjs/index.d.ts +29 -50
  22. package/dist/cjs/index.js +9 -5
  23. package/dist/cjs/utils/writeTx.d.ts +4 -0
  24. package/dist/cjs/utils/writeTx.js +3 -0
  25. package/dist/esm/functions/burnFuses.d.ts +1 -1
  26. package/dist/esm/functions/burnFuses.js +1 -1
  27. package/dist/esm/functions/createSubname.d.ts +1 -1
  28. package/dist/esm/functions/createSubname.js +2 -2
  29. package/dist/esm/functions/deleteSubname.d.ts +1 -1
  30. package/dist/esm/functions/deleteSubname.js +1 -1
  31. package/dist/esm/functions/setName.d.ts +1 -1
  32. package/dist/esm/functions/setName.js +2 -2
  33. package/dist/esm/functions/setRecords.d.ts +1 -1
  34. package/dist/esm/functions/setRecords.js +1 -1
  35. package/dist/esm/functions/setResolver.d.ts +1 -1
  36. package/dist/esm/functions/setResolver.js +2 -2
  37. package/dist/esm/functions/transferName.d.ts +1 -1
  38. package/dist/esm/functions/transferName.js +3 -3
  39. package/dist/esm/functions/transferSubname.d.ts +1 -1
  40. package/dist/esm/functions/transferSubname.js +2 -2
  41. package/dist/esm/functions/unwrapName.d.ts +1 -1
  42. package/dist/esm/functions/unwrapName.js +2 -2
  43. package/dist/esm/functions/wrapName.d.ts +2 -2
  44. package/dist/esm/functions/wrapName.js +3 -3
  45. package/dist/esm/index.d.ts +29 -50
  46. package/dist/esm/index.js +9 -5
  47. package/dist/esm/utils/writeTx.d.ts +4 -0
  48. package/dist/esm/utils/writeTx.js +1 -0
  49. package/package.json +3 -3
  50. package/src/functions/burnFuses.ts +1 -1
  51. package/src/functions/createSubname.ts +2 -2
  52. package/src/functions/deleteSubname.ts +1 -1
  53. package/src/functions/setName.ts +5 -5
  54. package/src/functions/setRecords.ts +2 -2
  55. package/src/functions/setResolver.ts +5 -2
  56. package/src/functions/transferName.ts +5 -3
  57. package/src/functions/transferSubname.ts +11 -2
  58. package/src/functions/unwrapName.ts +10 -2
  59. package/src/functions/wrapName.ts +5 -8
  60. package/src/index.ts +36 -11
  61. package/src/tests/populateTransaction.test.ts +40 -0
  62. package/src/tests/signerInjection.test.ts +7 -3
  63. package/src/utils/writeTx.ts +6 -0
@@ -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) {
@@ -1,7 +1,18 @@
1
- import { ethers, Signer } from 'ethers';
1
+ import { JsonRpcSigner } from '@ethersproject/providers';
2
+ import { ContractTransaction, ethers, PopulatedTransaction } from 'ethers';
2
3
  import ContractManager from './contracts';
3
4
  import { getContractAddress as _getContractAddress } from './contracts/getContractAddress';
4
5
  import { SupportedNetworkId } from './contracts/types';
6
+ import type burnFuses from './functions/burnFuses';
7
+ import type createSubname from './functions/createSubname';
8
+ import type deleteSubname from './functions/deleteSubname';
9
+ import type setName from './functions/setName';
10
+ import type setRecords from './functions/setRecords';
11
+ import type setResolver from './functions/setResolver';
12
+ import type transferName from './functions/transferName';
13
+ import type transferSubname from './functions/transferSubname';
14
+ import type unwrapName from './functions/unwrapName';
15
+ import type wrapName from './functions/wrapName';
5
16
  import GqlManager from './GqlManager';
6
17
  declare type ENSOptions = {
7
18
  graphURI?: string | null;
@@ -10,7 +21,7 @@ declare type ENSOptions = {
10
21
  export declare type InternalENS = {
11
22
  options?: ENSOptions;
12
23
  provider?: ethers.providers.Provider;
13
- signer: Signer;
24
+ signer: JsonRpcSigner;
14
25
  graphURI?: string | null;
15
26
  } & ENS;
16
27
  export declare type ENSArgs<K extends keyof InternalENS> = {
@@ -20,8 +31,13 @@ declare type OmitFirstArg<F> = F extends (x: any, ...args: infer P) => infer R ?
20
31
  declare type OmitFirstTwoArgs<F> = F extends (x: any, y: any, ...args: infer P) => infer R ? (...args: P) => R : never;
21
32
  declare type WriteOptions = {
22
33
  addressOrIndex?: string | number;
23
- signer?: Signer;
34
+ signer?: JsonRpcSigner;
24
35
  };
36
+ declare type OptionalWriteOptions<F> = F extends (x: any, arg_0: infer Z, options?: infer P) => infer R ? (name: Z, options?: P & WriteOptions) => R : F extends (x: any, arg_0: infer Z, options: infer P) => infer R ? (name: Z, options: P & WriteOptions) => R : never;
37
+ interface WriteFunction<F extends (...args: any) => any> extends Function {
38
+ (...args: Parameters<OptionalWriteOptions<F>>): Promise<ContractTransaction>;
39
+ populateTransaction: (...args: Parameters<OptionalWriteOptions<F>>) => Promise<PopulatedTransaction>;
40
+ }
25
41
  export declare type RawFunction = {
26
42
  raw: (...args: any[]) => Promise<{
27
43
  to: string;
@@ -436,53 +452,16 @@ export declare class ENS {
436
452
  }>;
437
453
  decode: ({ contracts }: ENSArgs<"contracts">, data: string) => Promise<any>;
438
454
  }>;
439
- setName: (name: string, options?: ({
440
- address?: string | undefined;
441
- resolver?: string | undefined;
442
- } & WriteOptions) | undefined) => Promise<ethers.ContractTransaction | undefined>;
443
- setRecords: (name: string, options: {
444
- records: import("./utils/recordHelpers").RecordOptions;
445
- resolverAddress?: string | undefined;
446
- } & WriteOptions) => Promise<ethers.ContractTransaction | undefined>;
447
- setResolver: (name: string, options: {
448
- contract: "nameWrapper" | "registry";
449
- resolver?: string | undefined;
450
- } & WriteOptions) => Promise<ethers.ContractTransaction>;
451
- transferName: (name: string, options: {
452
- newOwner: string;
453
- contract: "nameWrapper" | "registry" | "baseRegistrar";
454
- } & WriteOptions) => Promise<ethers.ContractTransaction>;
455
- wrapName: (name: string, options: {
456
- wrappedOwner: string;
457
- fuseOptions?: string | number | import("./@types/FuseOptions").FuseOptions | undefined;
458
- resolverAddress?: string | undefined;
459
- } & WriteOptions) => Promise<ethers.ContractTransaction>;
460
- unwrapName: (name: string, options: {
461
- newController: string;
462
- newRegistrant?: string | undefined;
463
- } & WriteOptions) => Promise<ethers.ContractTransaction>;
464
- burnFuses: (name: string, options: {
465
- fusesToBurn: import("./@types/FuseOptions").FuseOptions;
466
- } & WriteOptions) => Promise<ethers.ContractTransaction>;
467
- createSubname: (name: string, options: ({
468
- owner: string;
469
- resolverAddress?: string | undefined;
470
- contract: "nameWrapper" | "registry";
471
- } | ({
472
- contract: "nameWrapper";
473
- fuses?: import("./@types/FuseOptions").FuseOptions | undefined;
474
- } & {
475
- owner: string;
476
- resolverAddress?: string | undefined;
477
- contract: "nameWrapper" | "registry";
478
- })) & WriteOptions) => Promise<ethers.ContractTransaction>;
479
- deleteSubname: (name: string, options: {
480
- contract: "nameWrapper" | "registry";
481
- } & WriteOptions) => Promise<ethers.ContractTransaction>;
482
- transferSubname: (name: string, options: {
483
- contract: "nameWrapper" | "registry";
484
- address: string;
485
- } & WriteOptions) => Promise<ethers.ContractTransaction>;
455
+ setName: WriteFunction<typeof setName>;
456
+ setRecords: WriteFunction<typeof setRecords>;
457
+ setResolver: WriteFunction<typeof setResolver>;
458
+ transferName: WriteFunction<typeof transferName>;
459
+ wrapName: WriteFunction<typeof wrapName>;
460
+ unwrapName: WriteFunction<typeof unwrapName>;
461
+ burnFuses: WriteFunction<typeof burnFuses>;
462
+ createSubname: WriteFunction<typeof createSubname>;
463
+ deleteSubname: WriteFunction<typeof deleteSubname>;
464
+ transferSubname: WriteFunction<typeof transferSubname>;
486
465
  getDNSOwner: (dnsName: string) => Promise<any>;
487
466
  }
488
467
  export {};
package/dist/esm/index.js CHANGED
@@ -2,6 +2,7 @@ import ContractManager from './contracts';
2
2
  import { getContractAddress as _getContractAddress } from './contracts/getContractAddress';
3
3
  import GqlManager from './GqlManager';
4
4
  import singleCall from './utils/singleCall';
5
+ import writeTx from './utils/writeTx';
5
6
  const graphURIEndpoints = {
6
7
  1: 'https://api.thegraph.com/subgraphs/name/ensdomains/ens',
7
8
  3: 'https://api.thegraph.com/subgraphs/name/ensdomains/ensropsten',
@@ -57,24 +58,24 @@ export class ENS {
57
58
  // if combine isn't specified, run normally
58
59
  // otherwise, create a function from the raw and decode functions
59
60
  if (subFunc !== 'combine') {
61
+ const writeable = subFunc === 'write' || subFunc === 'populateTransaction';
60
62
  // get the function to call
61
- const func = subFunc && subFunc !== 'write'
62
- ? mod[exportName][subFunc]
63
- : mod[exportName];
63
+ const func = subFunc && !writeable ? mod[exportName][subFunc] : mod[exportName];
64
64
  // get the dependencies to forward to the function as the first arg
65
65
  let dependenciesToForward = thisRef.forwardDependenciesFromArray(dependencies);
66
66
  // if func is write func, inject signer into dependencies
67
- if (subFunc === 'write') {
67
+ if (writeable) {
68
68
  const options = (args[1] || {});
69
69
  const signer = options.signer ||
70
70
  thisRef.provider?.getSigner(options.addressOrIndex);
71
+ const populate = subFunc === 'populateTransaction';
71
72
  if (!signer) {
72
73
  throw new Error('No signer specified');
73
74
  }
74
75
  delete options.addressOrIndex;
75
76
  delete options.signer;
76
77
  dependenciesToForward = { ...dependenciesToForward, signer };
77
- return func(dependenciesToForward, args[0], options);
78
+ return func(dependenciesToForward, args[0], options).then(writeTx(signer, populate));
78
79
  }
79
80
  // return the function with the dependencies forwarded
80
81
  return func(dependenciesToForward, ...args);
@@ -92,6 +93,9 @@ export class ENS {
92
93
  mainFunc.decode = this.importGenerator(path, dependencies, exportName, 'decode');
93
94
  mainFunc.batch = this.importGenerator(path, dependencies, exportName, 'batch', { raw: mainFunc.raw, decode: mainFunc.decode });
94
95
  }
96
+ else if (subFunc === 'write') {
97
+ mainFunc.populateTransaction = this.importGenerator(path, dependencies, exportName, 'populateTransaction');
98
+ }
95
99
  return mainFunc;
96
100
  };
97
101
  /**
@@ -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 @@
1
+ export default (signer, populate) => (tx) => populate ? tx : signer.sendTransaction(tx);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ensdomains/ensjs",
3
- "version": "3.0.0-alpha.5",
3
+ "version": "3.0.0-alpha.6",
4
4
  "description": "ENS javascript library for contract interaction",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "types": "./dist/cjs/index.d.ts",
@@ -51,7 +51,7 @@
51
51
  },
52
52
  "devDependencies": {
53
53
  "@ensdomains/buffer": "^0.0.13",
54
- "@ensdomains/ens-test-env": "0.1.2",
54
+ "@ensdomains/ens-test-env": "0.1.4",
55
55
  "@ethersproject/abi": "^5.6.0",
56
56
  "@ethersproject/providers": "^5.6.2",
57
57
  "@nomiclabs/hardhat-ethers": "^2.0.5",
@@ -81,5 +81,5 @@
81
81
  "peerDependencies": {
82
82
  "ethers": "*"
83
83
  },
84
- "stableVersion": "3.0.0-alpha.4"
84
+ "stableVersion": "3.0.0-alpha.5"
85
85
  }
@@ -17,5 +17,5 @@ export default async function (
17
17
 
18
18
  const encodedFuses = generateFuseInput(fusesToBurn)
19
19
 
20
- return nameWrapper.burnFuses(hash, encodedFuses)
20
+ return nameWrapper.populateTransaction.burnFuses(hash, encodedFuses)
21
21
  }
@@ -44,7 +44,7 @@ export default async function (
44
44
  case 'registry': {
45
45
  const registry = (await contracts?.getRegistry()!).connect(signer)
46
46
 
47
- return registry.setSubnodeRecord(
47
+ return registry.populateTransaction.setSubnodeRecord(
48
48
  parentNodehash,
49
49
  labelhash,
50
50
  owner,
@@ -60,7 +60,7 @@ export default async function (
60
60
  ? generateFuseInput(wrapperArgs.fuses)
61
61
  : '0'
62
62
 
63
- return nameWrapper.setSubnodeRecord(
63
+ return nameWrapper.populateTransaction.setSubnodeRecord(
64
64
  parentNodehash,
65
65
  label,
66
66
  owner,
@@ -9,7 +9,7 @@ export default async function (
9
9
  contract: 'registry' | 'nameWrapper'
10
10
  },
11
11
  ) {
12
- return transferSubname(name, {
12
+ return transferSubname.populateTransaction(name, {
13
13
  contract,
14
14
  address: '0x0000000000000000000000000000000000000000',
15
15
  })
@@ -15,18 +15,18 @@ export default async function (
15
15
 
16
16
  const reverseRegistrar = (await contracts?.getReverseRegistrar())?.connect(
17
17
  signer,
18
- )
18
+ )!
19
19
 
20
20
  if (address) {
21
- const publicResolver = await contracts?.getPublicResolver()
21
+ const publicResolver = await contracts?.getPublicResolver()!
22
22
 
23
- return reverseRegistrar?.setNameForAddr(
23
+ return reverseRegistrar.populateTransaction.setNameForAddr(
24
24
  address,
25
25
  signerAddress,
26
- resolver || publicResolver!.address,
26
+ resolver || publicResolver.address,
27
27
  name,
28
28
  )
29
29
  }
30
30
 
31
- return reverseRegistrar?.setName(name)
31
+ return reverseRegistrar.populateTransaction.setName(name)
32
32
  }
@@ -35,10 +35,10 @@ export default async function (
35
35
 
36
36
  const resolver = (
37
37
  await contracts?.getPublicResolver(provider, resolverToUse)
38
- )?.connect(signer)
38
+ )?.connect(signer)!
39
39
  const hash = namehash(name)
40
40
 
41
41
  const calls: string[] = generateRecordCallArray(hash, records, resolver!)
42
42
 
43
- return resolver?.multicall(calls)
43
+ return resolver.populateTransaction.multicall(calls)
44
44
  }
@@ -19,11 +19,14 @@ export default async function (
19
19
  switch (contract) {
20
20
  case 'registry': {
21
21
  const registry = (await contracts?.getRegistry())!.connect(signer)
22
- return registry.setResolver(namehash(name), resolver)
22
+ return registry.populateTransaction.setResolver(namehash(name), resolver)
23
23
  }
24
24
  case 'nameWrapper': {
25
25
  const nameWrapper = (await contracts?.getNameWrapper())!.connect(signer)
26
- return nameWrapper.setResolver(namehash(name), resolver)
26
+ return nameWrapper.populateTransaction.setResolver(
27
+ namehash(name),
28
+ resolver,
29
+ )
27
30
  }
28
31
  default: {
29
32
  throw new Error(`Unknown contract: ${contract}`)
@@ -18,7 +18,7 @@ export default async function (
18
18
  switch (contract) {
19
19
  case 'registry': {
20
20
  const registry = (await contracts?.getRegistry())!.connect(signer)
21
- return registry.setOwner(namehash(name), newOwner)
21
+ return registry.populateTransaction.setOwner(namehash(name), newOwner)
22
22
  }
23
23
  case 'baseRegistrar': {
24
24
  const baseRegistrar = (await contracts?.getBaseRegistrar())!.connect(
@@ -28,7 +28,9 @@ export default async function (
28
28
  if (labels.length > 2 || labels[labels.length - 1] !== 'eth') {
29
29
  throw new Error('Invalid name for baseRegistrar')
30
30
  }
31
- return baseRegistrar['safeTransferFrom(address,address,uint256)'](
31
+ return baseRegistrar.populateTransaction[
32
+ 'safeTransferFrom(address,address,uint256)'
33
+ ](
32
34
  address,
33
35
  newOwner,
34
36
  ethers.utils.solidityKeccak256(['string'], [labels[0]]),
@@ -36,7 +38,7 @@ export default async function (
36
38
  }
37
39
  case 'nameWrapper': {
38
40
  const nameWrapper = (await contracts?.getNameWrapper())!.connect(signer)
39
- return nameWrapper.safeTransferFrom(
41
+ return nameWrapper.populateTransaction.safeTransferFrom(
40
42
  address,
41
43
  newOwner,
42
44
  namehash(name),
@@ -22,12 +22,21 @@ export default async function (
22
22
  case 'registry': {
23
23
  const registry = (await contracts?.getRegistry()!).connect(signer)
24
24
 
25
- return registry.setSubnodeOwner(parentNodehash, labelhash, address)
25
+ return registry.populateTransaction.setSubnodeOwner(
26
+ parentNodehash,
27
+ labelhash,
28
+ address,
29
+ )
26
30
  }
27
31
  case 'nameWrapper': {
28
32
  const nameWrapper = (await contracts?.getNameWrapper()!).connect(signer)
29
33
 
30
- return nameWrapper.setSubnodeOwner(parentNodehash, label, address, '0')
34
+ return nameWrapper.populateTransaction.setSubnodeOwner(
35
+ parentNodehash,
36
+ label,
37
+ address,
38
+ '0',
39
+ )
31
40
  }
32
41
  default: {
33
42
  throw new Error(`Unknown contract: ${contract}`)
@@ -24,12 +24,20 @@ export default async function (
24
24
  throw new Error('newRegistrant must be specified for .eth names')
25
25
  }
26
26
 
27
- return nameWrapper.unwrapETH2LD(labelhash, newRegistrant, newController)
27
+ return nameWrapper.populateTransaction.unwrapETH2LD(
28
+ labelhash,
29
+ newRegistrant,
30
+ newController,
31
+ )
28
32
  } else {
29
33
  if (newRegistrant) {
30
34
  throw new Error('newRegistrant can only be specified for .eth names')
31
35
  }
32
36
 
33
- return nameWrapper.unwrap(parentNodehash, labelhash, newController)
37
+ return nameWrapper.populateTransaction.unwrap(
38
+ parentNodehash,
39
+ labelhash,
40
+ newController,
41
+ )
34
42
  }
35
43
  }
@@ -23,12 +23,9 @@ async function wrapETH(
23
23
  [labels[0], wrappedOwner, '0x0', decodedFuses, resolverAddress],
24
24
  )
25
25
 
26
- return baseRegistrar['safeTransferFrom(address,address,uint256,bytes)'](
27
- address,
28
- nameWrapper.address,
29
- labelhash,
30
- data,
31
- )
26
+ return baseRegistrar.populateTransaction[
27
+ 'safeTransferFrom(address,address,uint256,bytes)'
28
+ ](address, nameWrapper.address, labelhash, data)
32
29
  }
33
30
 
34
31
  async function wrapOther(
@@ -54,7 +51,7 @@ async function wrapOther(
54
51
  )
55
52
  }
56
53
 
57
- return nameWrapper.wrap(
54
+ return nameWrapper.populateTransaction.wrap(
58
55
  hexEncodeName(name),
59
56
  wrappedOwner,
60
57
  decodedFuses,
@@ -63,7 +60,7 @@ async function wrapOther(
63
60
  }
64
61
 
65
62
  export default async function (
66
- { contracts, signer }: ENSArgs<'contracts' | 'signer'>,
63
+ { contracts, signer, populate }: ENSArgs<'contracts' | 'signer' | 'populate'>,
67
64
  name: string,
68
65
  {
69
66
  wrappedOwner,
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { ethers, Signer } from 'ethers'
1
+ import { JsonRpcSigner } from '@ethersproject/providers'
2
+ import { ContractTransaction, ethers, PopulatedTransaction } from 'ethers'
2
3
  import ContractManager from './contracts'
3
4
  import { getContractAddress as _getContractAddress } from './contracts/getContractAddress'
4
5
  import { SupportedNetworkId } from './contracts/types'
@@ -44,6 +45,7 @@ import type unwrapName from './functions/unwrapName'
44
45
  import type wrapName from './functions/wrapName'
45
46
  import GqlManager from './GqlManager'
46
47
  import singleCall from './utils/singleCall'
48
+ import writeTx from './utils/writeTx'
47
49
 
48
50
  type ENSOptions = {
49
51
  graphURI?: string | null
@@ -53,7 +55,7 @@ type ENSOptions = {
53
55
  export type InternalENS = {
54
56
  options?: ENSOptions
55
57
  provider?: ethers.providers.Provider
56
- signer: Signer
58
+ signer: JsonRpcSigner
57
59
  graphURI?: string | null
58
60
  } & ENS
59
61
 
@@ -79,10 +81,10 @@ type FunctionDeps<F> = Extract<keyof FirstArg<F>, string>[]
79
81
 
80
82
  type WriteOptions = {
81
83
  addressOrIndex?: string | number
82
- signer?: Signer
84
+ signer?: JsonRpcSigner
83
85
  }
84
86
 
85
- type WriteFunction<F> = F extends (
87
+ type OptionalWriteOptions<F> = F extends (
86
88
  x: any,
87
89
  arg_0: infer Z,
88
90
  options?: infer P,
@@ -92,6 +94,13 @@ type WriteFunction<F> = F extends (
92
94
  ? (name: Z, options: P & WriteOptions) => R
93
95
  : never
94
96
 
97
+ interface WriteFunction<F extends (...args: any) => any> extends Function {
98
+ (...args: Parameters<OptionalWriteOptions<F>>): Promise<ContractTransaction>
99
+ populateTransaction: (
100
+ ...args: Parameters<OptionalWriteOptions<F>>
101
+ ) => Promise<PopulatedTransaction>
102
+ }
103
+
95
104
  const graphURIEndpoints: Record<string, string> = {
96
105
  1: 'https://api.thegraph.com/subgraphs/name/ensdomains/ens',
97
106
  3: 'https://api.thegraph.com/subgraphs/name/ensdomains/ensropsten',
@@ -188,7 +197,13 @@ export class ENS {
188
197
  path: string,
189
198
  dependencies: FunctionDeps<F>,
190
199
  exportName: string = 'default',
191
- subFunc?: 'raw' | 'decode' | 'combine' | 'batch' | 'write',
200
+ subFunc?:
201
+ | 'raw'
202
+ | 'decode'
203
+ | 'combine'
204
+ | 'batch'
205
+ | 'write'
206
+ | 'populateTransaction',
192
207
  passthrough?: RawFunction,
193
208
  ): Function => {
194
209
  // if batch is specified, create batch func
@@ -209,28 +224,31 @@ export class ENS {
209
224
  // if combine isn't specified, run normally
210
225
  // otherwise, create a function from the raw and decode functions
211
226
  if (subFunc !== 'combine') {
227
+ const writeable =
228
+ subFunc === 'write' || subFunc === 'populateTransaction'
212
229
  // get the function to call
213
230
  const func =
214
- subFunc && subFunc !== 'write'
215
- ? mod[exportName][subFunc]
216
- : mod[exportName]
231
+ subFunc && !writeable ? mod[exportName][subFunc] : mod[exportName]
217
232
  // get the dependencies to forward to the function as the first arg
218
233
  let dependenciesToForward =
219
234
  thisRef.forwardDependenciesFromArray<F>(dependencies)
220
235
 
221
236
  // if func is write func, inject signer into dependencies
222
- if (subFunc === 'write') {
237
+ if (writeable) {
223
238
  const options = (args[1] || {}) as WriteOptions
224
239
  const signer =
225
240
  options.signer ||
226
241
  thisRef.provider?.getSigner(options.addressOrIndex)
242
+ const populate = subFunc === 'populateTransaction'
227
243
  if (!signer) {
228
244
  throw new Error('No signer specified')
229
245
  }
230
246
  delete options.addressOrIndex
231
247
  delete options.signer
232
248
  dependenciesToForward = { ...dependenciesToForward, signer }
233
- return func(dependenciesToForward, args[0], options)
249
+ return func(dependenciesToForward, args[0], options).then(
250
+ writeTx(signer, populate),
251
+ )
234
252
  }
235
253
 
236
254
  // return the function with the dependencies forwarded
@@ -272,6 +290,13 @@ export class ENS {
272
290
  'batch',
273
291
  { raw: mainFunc.raw as any, decode: mainFunc.decode as any },
274
292
  )
293
+ } else if (subFunc === 'write') {
294
+ mainFunc.populateTransaction = this.importGenerator<F>(
295
+ path,
296
+ dependencies,
297
+ exportName,
298
+ 'populateTransaction',
299
+ )
275
300
  }
276
301
 
277
302
  return mainFunc as Function
@@ -298,7 +323,7 @@ export class ENS {
298
323
  * @param {string} exportName - The export name of the target function
299
324
  * @returns {OmitFirstArg} - The generated wrapped function
300
325
  */
301
- private generateWriteFunction = <F>(
326
+ private generateWriteFunction = <F extends (...args: any) => any>(
302
327
  path: string,
303
328
  dependencies: FunctionDeps<F>,
304
329
  exportName: string = 'default',
@@ -0,0 +1,40 @@
1
+ import { ethers } from 'ethers'
2
+ import { ENS } from '..'
3
+ import setup from '../tests/setup'
4
+
5
+ let ENSInstance: ENS
6
+ let revert: Awaited<ReturnType<typeof setup>>['revert']
7
+ let provider: ethers.providers.JsonRpcProvider
8
+ let accounts: string[]
9
+
10
+ beforeAll(async () => {
11
+ ;({ ENSInstance, revert, provider } = await setup())
12
+ accounts = await provider.listAccounts()
13
+ })
14
+
15
+ afterAll(async () => {
16
+ await revert()
17
+ })
18
+
19
+ jest.setTimeout(20000)
20
+
21
+ describe('populateTransaction', () => {
22
+ beforeEach(async () => {
23
+ await revert()
24
+ })
25
+ it('should return a transaction successfully', async () => {
26
+ const tx = await ENSInstance.setName('fleek.eth')
27
+ expect(tx).toBeTruthy()
28
+ if (tx) {
29
+ await tx.wait()
30
+ expect(tx.hash).toBeTruthy()
31
+ }
32
+ })
33
+ it('should return a populated transaction successfully', async () => {
34
+ const tx = await ENSInstance.setName.populateTransaction('fleek.eth')
35
+ expect(tx).toBeTruthy()
36
+ if (tx) {
37
+ expect(tx).not.toHaveProperty('hash')
38
+ }
39
+ })
40
+ })
@@ -1,4 +1,4 @@
1
- import { ethers } from 'ethers'
1
+ import { ContractTransaction, ethers } from 'ethers'
2
2
  import { ENS } from '..'
3
3
  import setup from '../tests/setup'
4
4
 
@@ -24,7 +24,9 @@ describe('Signer Injection', () => {
24
24
  })
25
25
  it('should return a transaction successfully for a custom signer', async () => {
26
26
  const signer = provider.getSigner(accounts[3])
27
- const tx = await ENSInstance.setName('fleek.eth', { signer })
27
+ const tx = await ENSInstance.setName('fleek.eth', {
28
+ signer,
29
+ })
28
30
  expect(tx).toBeTruthy()
29
31
  if (tx) {
30
32
  await tx.wait()
@@ -32,7 +34,9 @@ describe('Signer Injection', () => {
32
34
  }
33
35
  })
34
36
  it('should return a transaction succesfully for a custom signer index', async () => {
35
- const tx = await ENSInstance.setName('fleek.eth', { addressOrIndex: 3 })
37
+ const tx = (await ENSInstance.setName('fleek.eth', {
38
+ addressOrIndex: 3,
39
+ })) as ContractTransaction
36
40
  expect(tx).toBeTruthy()
37
41
  if (tx) {
38
42
  await tx.wait()
@@ -0,0 +1,6 @@
1
+ import { JsonRpcSigner } from '@ethersproject/providers'
2
+ import type { PopulatedTransaction } from 'ethers'
3
+
4
+ export default (signer: JsonRpcSigner, populate: boolean) =>
5
+ (tx: PopulatedTransaction) =>
6
+ populate ? tx : signer.sendTransaction(tx)