@ensdomains/ensjs 3.0.0-alpha.13 → 3.0.0-alpha.14
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.
- package/dist/cjs/contracts/index.d.ts +8 -8
- package/dist/cjs/functions/burnFuses.d.ts +31 -4
- package/dist/cjs/functions/burnFuses.js +36 -6
- package/dist/cjs/functions/getFuses.d.ts +2 -1
- package/dist/cjs/functions/getFuses.js +9 -18
- package/dist/cjs/functions/getSpecificRecord.d.ts +4 -4
- package/dist/cjs/index.d.ts +12 -1
- package/dist/cjs/index.js +2 -0
- package/dist/cjs/utils/fuses.d.ts +18 -17
- package/dist/cjs/utils/fuses.js +10 -4
- package/dist/cjs/utils/wrapperExpiry.d.ts +1 -1
- package/dist/esm/contracts/index.d.ts +8 -8
- package/dist/esm/functions/burnFuses.d.ts +31 -4
- package/dist/esm/functions/burnFuses.js +36 -3
- package/dist/esm/functions/getFuses.d.ts +2 -1
- package/dist/esm/functions/getFuses.js +10 -19
- package/dist/esm/functions/getSpecificRecord.d.ts +4 -4
- package/dist/esm/index.d.ts +12 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/utils/fuses.d.ts +18 -17
- package/dist/esm/utils/fuses.js +9 -3
- package/dist/esm/utils/wrapperExpiry.d.ts +1 -1
- package/package.json +1 -1
- package/src/functions/burnFuses.test.ts +123 -15
- package/src/functions/burnFuses.ts +101 -10
- package/src/functions/getFuses.test.ts +33 -15
- package/src/functions/getFuses.ts +13 -25
- package/src/index.ts +2 -0
- package/src/utils/fuses.ts +12 -4
|
@@ -5,12 +5,12 @@ export default class ContractManager {
|
|
|
5
5
|
private fetchAddress;
|
|
6
6
|
constructor(provider: ethers.providers.Provider, fetchAddress: ContractAddressFetch);
|
|
7
7
|
private generateContractGetter;
|
|
8
|
-
getPublicResolver: (passedProvider?: any, address?: string
|
|
9
|
-
getUniversalResolver: (passedProvider?: any, address?: string
|
|
10
|
-
getRegistry: (passedProvider?: any, address?: string
|
|
11
|
-
getReverseRegistrar: (passedProvider?: any, address?: string
|
|
12
|
-
getNameWrapper: (passedProvider?: any, address?: string
|
|
13
|
-
getBaseRegistrar: (passedProvider?: any, address?: string
|
|
14
|
-
getEthRegistrarController: (passedProvider?: any, address?: string
|
|
15
|
-
getMulticall: (passedProvider?: any, address?: string
|
|
8
|
+
getPublicResolver: (passedProvider?: any, address?: string) => Promise<import("../generated").PublicResolver>;
|
|
9
|
+
getUniversalResolver: (passedProvider?: any, address?: string) => Promise<import("../generated").UniversalResolver>;
|
|
10
|
+
getRegistry: (passedProvider?: any, address?: string) => Promise<import("../generated").ENSRegistry>;
|
|
11
|
+
getReverseRegistrar: (passedProvider?: any, address?: string) => Promise<import("../generated").ReverseRegistrar>;
|
|
12
|
+
getNameWrapper: (passedProvider?: any, address?: string) => Promise<import("../generated").NameWrapper>;
|
|
13
|
+
getBaseRegistrar: (passedProvider?: any, address?: string) => Promise<import("../generated").BaseRegistrarImplementation>;
|
|
14
|
+
getEthRegistrarController: (passedProvider?: any, address?: string) => Promise<import("../generated").ETHRegistrarController>;
|
|
15
|
+
getMulticall: (passedProvider?: any, address?: string) => Promise<import("../generated").Multicall>;
|
|
16
16
|
}
|
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
import { ENSArgs } from '..';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import { fuseEnum, unnamedFuses } from '../utils/fuses';
|
|
3
|
+
declare type FuseObj = typeof fuseEnum;
|
|
4
|
+
declare type UnnamedFuseType = typeof unnamedFuses;
|
|
5
|
+
declare type Fuse = keyof FuseObj;
|
|
6
|
+
declare type UnnamedFuseValues = UnnamedFuseType[number];
|
|
7
|
+
declare type FuseArrayPossibilities = [Fuse] | [Fuse, Fuse] | [Fuse, Fuse, Fuse] | [Fuse, Fuse, Fuse, Fuse] | [Fuse, Fuse, Fuse, Fuse, Fuse] | [Fuse, Fuse, Fuse, Fuse, Fuse, Fuse] | [Fuse, Fuse, Fuse, Fuse, Fuse, Fuse, Fuse];
|
|
8
|
+
/**
|
|
9
|
+
* This type creates a type error if there are any duplicate fuses.
|
|
10
|
+
* It effectively works like a reduce function, starting with 0 included types, adding a type each time, and then checking for duplicates.
|
|
11
|
+
*
|
|
12
|
+
* @template A The array to check for duplicates.
|
|
13
|
+
* @template B The union of all checked existing types.
|
|
14
|
+
*/
|
|
15
|
+
declare type FusesWithoutDuplicates<A, B = never> = A extends FuseArrayPossibilities ? A extends [infer Head, ...infer Tail] ? Head extends B ? [
|
|
16
|
+
] : [
|
|
17
|
+
Head,
|
|
18
|
+
...FusesWithoutDuplicates<Tail, Head | B>
|
|
19
|
+
] : A : [
|
|
20
|
+
];
|
|
21
|
+
declare type FusePropsNamedArray<A extends FuseArrayPossibilities> = {
|
|
22
|
+
namedFusesToBurn: FusesWithoutDuplicates<A>;
|
|
23
|
+
};
|
|
24
|
+
declare type FusePropsUnnamedArray = {
|
|
25
|
+
unnamedFusesToBurn: UnnamedFuseValues[];
|
|
26
|
+
};
|
|
27
|
+
declare type FusePropsNumber = {
|
|
28
|
+
fuseNumberToBurn: number;
|
|
29
|
+
};
|
|
30
|
+
declare type FuseProps<A extends FuseArrayPossibilities> = (Partial<FusePropsNamedArray<A>> & FusePropsUnnamedArray) | (FusePropsNamedArray<A> & Partial<FusePropsUnnamedArray>) | FusePropsNumber;
|
|
31
|
+
export default function <A extends FuseArrayPossibilities>({ contracts, signer }: ENSArgs<'contracts' | 'signer'>, name: string, props: FuseProps<A>): Promise<import("ethers").PopulatedTransaction>;
|
|
32
|
+
export {};
|
|
@@ -1,14 +1,44 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const
|
|
3
|
+
const fuses_1 = require("../utils/fuses");
|
|
7
4
|
const normalise_1 = require("../utils/normalise");
|
|
8
|
-
async function default_1({ contracts, signer }, name,
|
|
5
|
+
async function default_1({ contracts, signer }, name, props) {
|
|
6
|
+
const isNumber = 'fuseNumberToBurn' in props;
|
|
7
|
+
const hasNamedArray = 'namedFusesToBurn' in props;
|
|
8
|
+
const hasUnnamedArray = 'unnamedFusesToBurn' in props;
|
|
9
|
+
let encodedFuses = 0;
|
|
10
|
+
if (isNumber) {
|
|
11
|
+
if (props.fuseNumberToBurn > 2 ** 32 || props.fuseNumberToBurn < 1) {
|
|
12
|
+
throw new Error(`Fuse number must be limited to uint32, ${props.fuseNumberToBurn} was too ${props.fuseNumberToBurn < 1 ? 'low' : 'high'}.`);
|
|
13
|
+
}
|
|
14
|
+
else if (props.fuseNumberToBurn % 1 !== 0) {
|
|
15
|
+
throw new Error(`Fuse number must be an integer, ${props.fuseNumberToBurn} was not.`);
|
|
16
|
+
}
|
|
17
|
+
encodedFuses = props.fuseNumberToBurn;
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
if (!hasNamedArray && !hasUnnamedArray) {
|
|
21
|
+
throw new Error('Please provide fuses to burn');
|
|
22
|
+
}
|
|
23
|
+
if (hasNamedArray) {
|
|
24
|
+
for (const fuse of props.namedFusesToBurn) {
|
|
25
|
+
if (!(fuse in fuses_1.fuseEnum)) {
|
|
26
|
+
throw new Error(`${fuse} is not a valid named fuse.`);
|
|
27
|
+
}
|
|
28
|
+
encodedFuses |= fuses_1.fuseEnum[fuse];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (hasUnnamedArray) {
|
|
32
|
+
for (const fuse of props.unnamedFusesToBurn) {
|
|
33
|
+
if (!fuses_1.unnamedFuses.includes(fuse)) {
|
|
34
|
+
throw new Error(`${fuse} is not a valid unnamed fuse. If you are trying to burn a named fuse, use the namedFusesToBurn property.`);
|
|
35
|
+
}
|
|
36
|
+
encodedFuses |= fuse;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
9
40
|
const nameWrapper = (await contracts?.getNameWrapper()).connect(signer);
|
|
10
41
|
const hash = (0, normalise_1.namehash)(name);
|
|
11
|
-
const encodedFuses = (0, generateFuseInput_1.default)(fusesToBurn);
|
|
12
42
|
return nameWrapper.populateTransaction.setFuses(hash, encodedFuses);
|
|
13
43
|
}
|
|
14
44
|
exports.default = default_1;
|
|
@@ -5,12 +5,13 @@ declare const _default: {
|
|
|
5
5
|
to: string;
|
|
6
6
|
data: string;
|
|
7
7
|
}>;
|
|
8
|
-
decode: ({ contracts }: ENSArgs<"contracts">, data: string
|
|
8
|
+
decode: ({ contracts }: ENSArgs<"contracts">, data: string) => Promise<{
|
|
9
9
|
fuseObj: {
|
|
10
10
|
[k: string]: boolean;
|
|
11
11
|
};
|
|
12
12
|
expiryDate: Date;
|
|
13
13
|
rawFuses: BigNumber;
|
|
14
|
+
owner: any;
|
|
14
15
|
} | undefined>;
|
|
15
16
|
};
|
|
16
17
|
export default _default;
|
|
@@ -3,32 +3,21 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const ethers_1 = require("ethers");
|
|
4
4
|
const fuses_1 = require("../utils/fuses");
|
|
5
5
|
const normalise_1 = require("../utils/normalise");
|
|
6
|
-
const NameSafety = [
|
|
7
|
-
'Safe',
|
|
8
|
-
'RegistrantNotWrapped',
|
|
9
|
-
'ControllerNotWrapped',
|
|
10
|
-
'SubdomainReplacementAllowed',
|
|
11
|
-
'Expired',
|
|
12
|
-
];
|
|
13
6
|
const raw = async ({ contracts }, name) => {
|
|
14
7
|
const nameWrapper = await contracts?.getNameWrapper();
|
|
15
8
|
return {
|
|
16
9
|
to: nameWrapper.address,
|
|
17
|
-
data: nameWrapper.interface.encodeFunctionData('
|
|
18
|
-
(0, normalise_1.namehash)(name),
|
|
19
|
-
]),
|
|
10
|
+
data: nameWrapper.interface.encodeFunctionData('getData', [(0, normalise_1.namehash)(name)]),
|
|
20
11
|
};
|
|
21
12
|
};
|
|
22
|
-
const decode = async ({ contracts }, data
|
|
13
|
+
const decode = async ({ contracts }, data) => {
|
|
23
14
|
const nameWrapper = await contracts?.getNameWrapper();
|
|
24
15
|
try {
|
|
25
|
-
const
|
|
16
|
+
const { owner, fuses: _fuses, expiry, } = nameWrapper.interface.decodeFunctionResult('getData', data);
|
|
26
17
|
const fuses = ethers_1.BigNumber.from(_fuses);
|
|
27
|
-
const fuseObj = Object.fromEntries(Object.keys(fuses_1.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
.replace(/([-_][a-z])/g, (group) => group.toUpperCase().replace('-', '').replace('_', '')),
|
|
31
|
-
fuses.and(fuses_1.testable[fuseEnum]).gt(0),
|
|
18
|
+
const fuseObj = Object.fromEntries(Object.keys(fuses_1.fuseEnum).map((fuse) => [
|
|
19
|
+
fuse,
|
|
20
|
+
fuses.and(fuses_1.fuseEnum[fuse]).gt(0),
|
|
32
21
|
]));
|
|
33
22
|
if (fuses.eq(0)) {
|
|
34
23
|
fuseObj.canDoEverything = true;
|
|
@@ -41,9 +30,11 @@ const decode = async ({ contracts }, data, name) => {
|
|
|
41
30
|
fuseObj,
|
|
42
31
|
expiryDate,
|
|
43
32
|
rawFuses: fuses,
|
|
33
|
+
owner,
|
|
44
34
|
};
|
|
45
35
|
}
|
|
46
|
-
catch {
|
|
36
|
+
catch (e) {
|
|
37
|
+
console.error('Error decoding fuses data: ', e);
|
|
47
38
|
return;
|
|
48
39
|
}
|
|
49
40
|
};
|
|
@@ -52,21 +52,21 @@ export declare const getText: {
|
|
|
52
52
|
decode: ({ contracts, universalWrapper }: ENSArgs<'contracts' | 'universalWrapper'>, data: string) => Promise<any>;
|
|
53
53
|
};
|
|
54
54
|
export declare const _getAddr: {
|
|
55
|
-
raw: ({ contracts }: ENSArgs<'contracts'>, name: string, coinType?: string | number
|
|
55
|
+
raw: ({ contracts }: ENSArgs<'contracts'>, name: string, coinType?: string | number, bypassFormat?: boolean) => Promise<{
|
|
56
56
|
to: string;
|
|
57
57
|
data: string;
|
|
58
58
|
}>;
|
|
59
|
-
decode: ({ contracts }: ENSArgs<'contracts'>, data: string, _name: string, coinType?: string | number
|
|
59
|
+
decode: ({ contracts }: ENSArgs<'contracts'>, data: string, _name: string, coinType?: string | number) => Promise<string | {
|
|
60
60
|
coin: string;
|
|
61
61
|
addr: string;
|
|
62
62
|
} | undefined>;
|
|
63
63
|
};
|
|
64
64
|
export declare const getAddr: {
|
|
65
|
-
raw: ({ contracts, universalWrapper }: ENSArgs<'contracts' | 'universalWrapper'>, name: string, coinType?: string | number
|
|
65
|
+
raw: ({ contracts, universalWrapper }: ENSArgs<'contracts' | 'universalWrapper'>, name: string, coinType?: string | number) => Promise<{
|
|
66
66
|
to: string;
|
|
67
67
|
data: string;
|
|
68
68
|
}>;
|
|
69
|
-
decode: ({ contracts, universalWrapper }: ENSArgs<'contracts' | 'universalWrapper'>, data: string, _name: string, coinType?: string | number
|
|
69
|
+
decode: ({ contracts, universalWrapper }: ENSArgs<'contracts' | 'universalWrapper'>, data: string, _name: string, coinType?: string | number) => Promise<string | {
|
|
70
70
|
coin: string;
|
|
71
71
|
addr: string;
|
|
72
72
|
} | undefined>;
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -75,6 +75,16 @@ export declare class ENS {
|
|
|
75
75
|
contracts?: ContractManager;
|
|
76
76
|
getContractAddress: (networkId: SupportedNetworkId) => import("./contracts/getContractAddress").ContractAddressFetch;
|
|
77
77
|
gqlInstance: GqlManager;
|
|
78
|
+
fuses: {
|
|
79
|
+
CAN_DO_EVERYTHING: number;
|
|
80
|
+
CANNOT_UNWRAP: 1;
|
|
81
|
+
CANNOT_BURN_FUSES: 2;
|
|
82
|
+
CANNOT_TRANSFER: 4;
|
|
83
|
+
CANNOT_SET_RESOLVER: 8;
|
|
84
|
+
CANNOT_SET_TTL: 16;
|
|
85
|
+
CANNOT_CREATE_SUBDOMAIN: 32;
|
|
86
|
+
PARENT_CANNOT_CONTROL: 64;
|
|
87
|
+
};
|
|
78
88
|
constructor(options?: ENSOptions);
|
|
79
89
|
/**
|
|
80
90
|
* Checks for an initial provider and if it exists, sets it as the provider
|
|
@@ -229,12 +239,13 @@ export declare class ENS {
|
|
|
229
239
|
to: string;
|
|
230
240
|
data: string;
|
|
231
241
|
}>;
|
|
232
|
-
decode: ({ contracts }: ENSArgs<"contracts">, data: string
|
|
242
|
+
decode: ({ contracts }: ENSArgs<"contracts">, data: string) => Promise<{
|
|
233
243
|
fuseObj: {
|
|
234
244
|
[k: string]: boolean;
|
|
235
245
|
};
|
|
236
246
|
expiryDate: Date;
|
|
237
247
|
rawFuses: ethers.BigNumber;
|
|
248
|
+
owner: any;
|
|
238
249
|
} | undefined>;
|
|
239
250
|
}>;
|
|
240
251
|
getHistory: (name: string) => Promise<{
|
package/dist/cjs/index.js
CHANGED
|
@@ -32,6 +32,7 @@ const getContractAddress_1 = require("./contracts/getContractAddress");
|
|
|
32
32
|
const GqlManager_1 = __importDefault(require("./GqlManager"));
|
|
33
33
|
const singleCall_1 = __importDefault(require("./utils/singleCall"));
|
|
34
34
|
const writeTx_1 = __importDefault(require("./utils/writeTx"));
|
|
35
|
+
const fuses_1 = __importDefault(require("./utils/fuses"));
|
|
35
36
|
const graphURIEndpoints = {
|
|
36
37
|
1: 'https://api.thegraph.com/subgraphs/name/ensdomains/ens',
|
|
37
38
|
3: 'https://api.thegraph.com/subgraphs/name/ensdomains/ensropsten',
|
|
@@ -46,6 +47,7 @@ class ENS {
|
|
|
46
47
|
contracts;
|
|
47
48
|
getContractAddress = getContractAddress_1.getContractAddress;
|
|
48
49
|
gqlInstance = new GqlManager_1.default();
|
|
50
|
+
fuses = fuses_1.default;
|
|
49
51
|
constructor(options) {
|
|
50
52
|
this.options = options;
|
|
51
53
|
this.getContractAddress = options?.getContractAddress || getContractAddress_1.getContractAddress;
|
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
export declare const
|
|
2
|
-
CANNOT_UNWRAP:
|
|
3
|
-
CANNOT_BURN_FUSES:
|
|
4
|
-
CANNOT_TRANSFER:
|
|
5
|
-
CANNOT_SET_RESOLVER:
|
|
6
|
-
CANNOT_SET_TTL:
|
|
7
|
-
CANNOT_CREATE_SUBDOMAIN:
|
|
8
|
-
PARENT_CANNOT_CONTROL:
|
|
1
|
+
export declare const fuseEnum: {
|
|
2
|
+
readonly CANNOT_UNWRAP: 1;
|
|
3
|
+
readonly CANNOT_BURN_FUSES: 2;
|
|
4
|
+
readonly CANNOT_TRANSFER: 4;
|
|
5
|
+
readonly CANNOT_SET_RESOLVER: 8;
|
|
6
|
+
readonly CANNOT_SET_TTL: 16;
|
|
7
|
+
readonly CANNOT_CREATE_SUBDOMAIN: 32;
|
|
8
|
+
readonly PARENT_CANNOT_CONTROL: 64;
|
|
9
9
|
};
|
|
10
|
-
declare const
|
|
10
|
+
export declare const unnamedFuses: readonly [128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824, 2147483648, 4294967296];
|
|
11
|
+
declare const fullFuseEnum: {
|
|
11
12
|
CAN_DO_EVERYTHING: number;
|
|
12
|
-
CANNOT_UNWRAP:
|
|
13
|
-
CANNOT_BURN_FUSES:
|
|
14
|
-
CANNOT_TRANSFER:
|
|
15
|
-
CANNOT_SET_RESOLVER:
|
|
16
|
-
CANNOT_SET_TTL:
|
|
17
|
-
CANNOT_CREATE_SUBDOMAIN:
|
|
18
|
-
PARENT_CANNOT_CONTROL:
|
|
13
|
+
CANNOT_UNWRAP: 1;
|
|
14
|
+
CANNOT_BURN_FUSES: 2;
|
|
15
|
+
CANNOT_TRANSFER: 4;
|
|
16
|
+
CANNOT_SET_RESOLVER: 8;
|
|
17
|
+
CANNOT_SET_TTL: 16;
|
|
18
|
+
CANNOT_CREATE_SUBDOMAIN: 32;
|
|
19
|
+
PARENT_CANNOT_CONTROL: 64;
|
|
19
20
|
};
|
|
20
|
-
export default
|
|
21
|
+
export default fullFuseEnum;
|
package/dist/cjs/utils/fuses.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.unnamedFuses = exports.fuseEnum = void 0;
|
|
4
4
|
const CANNOT_UNWRAP = 1;
|
|
5
5
|
const CANNOT_BURN_FUSES = 2;
|
|
6
6
|
const CANNOT_TRANSFER = 4;
|
|
@@ -9,7 +9,7 @@ const CANNOT_SET_TTL = 16;
|
|
|
9
9
|
const CANNOT_CREATE_SUBDOMAIN = 32;
|
|
10
10
|
const PARENT_CANNOT_CONTROL = 64;
|
|
11
11
|
const CAN_DO_EVERYTHING = 0;
|
|
12
|
-
exports.
|
|
12
|
+
exports.fuseEnum = {
|
|
13
13
|
CANNOT_UNWRAP,
|
|
14
14
|
CANNOT_BURN_FUSES,
|
|
15
15
|
CANNOT_TRANSFER,
|
|
@@ -18,7 +18,13 @@ exports.testable = {
|
|
|
18
18
|
CANNOT_CREATE_SUBDOMAIN,
|
|
19
19
|
PARENT_CANNOT_CONTROL,
|
|
20
20
|
};
|
|
21
|
-
exports.
|
|
22
|
-
|
|
21
|
+
exports.unnamedFuses = [
|
|
22
|
+
128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144,
|
|
23
|
+
524288, 1048576, 2097152, 4194304, 8388608, 16777216, 33554432, 67108864,
|
|
24
|
+
134217728, 268435456, 536870912, 1073741824, 2147483648, 4294967296,
|
|
25
|
+
];
|
|
26
|
+
const fullFuseEnum = {
|
|
27
|
+
...exports.fuseEnum,
|
|
23
28
|
CAN_DO_EVERYTHING,
|
|
24
29
|
};
|
|
30
|
+
exports.default = fullFuseEnum;
|
|
@@ -2,4 +2,4 @@ import { BigNumber } from 'ethers';
|
|
|
2
2
|
import { ENSArgs } from '..';
|
|
3
3
|
export declare type Expiry = string | number | Date | BigNumber;
|
|
4
4
|
export declare const MAX_EXPIRY: BigNumber;
|
|
5
|
-
export declare const makeExpiry: ({ getExpiry }: ENSArgs<'getExpiry'>, name: string, expiry?: Expiry
|
|
5
|
+
export declare const makeExpiry: ({ getExpiry }: ENSArgs<'getExpiry'>, name: string, expiry?: Expiry) => Promise<BigNumber>;
|
|
@@ -5,12 +5,12 @@ export default class ContractManager {
|
|
|
5
5
|
private fetchAddress;
|
|
6
6
|
constructor(provider: ethers.providers.Provider, fetchAddress: ContractAddressFetch);
|
|
7
7
|
private generateContractGetter;
|
|
8
|
-
getPublicResolver: (passedProvider?: any, address?: string
|
|
9
|
-
getUniversalResolver: (passedProvider?: any, address?: string
|
|
10
|
-
getRegistry: (passedProvider?: any, address?: string
|
|
11
|
-
getReverseRegistrar: (passedProvider?: any, address?: string
|
|
12
|
-
getNameWrapper: (passedProvider?: any, address?: string
|
|
13
|
-
getBaseRegistrar: (passedProvider?: any, address?: string
|
|
14
|
-
getEthRegistrarController: (passedProvider?: any, address?: string
|
|
15
|
-
getMulticall: (passedProvider?: any, address?: string
|
|
8
|
+
getPublicResolver: (passedProvider?: any, address?: string) => Promise<import("../generated").PublicResolver>;
|
|
9
|
+
getUniversalResolver: (passedProvider?: any, address?: string) => Promise<import("../generated").UniversalResolver>;
|
|
10
|
+
getRegistry: (passedProvider?: any, address?: string) => Promise<import("../generated").ENSRegistry>;
|
|
11
|
+
getReverseRegistrar: (passedProvider?: any, address?: string) => Promise<import("../generated").ReverseRegistrar>;
|
|
12
|
+
getNameWrapper: (passedProvider?: any, address?: string) => Promise<import("../generated").NameWrapper>;
|
|
13
|
+
getBaseRegistrar: (passedProvider?: any, address?: string) => Promise<import("../generated").BaseRegistrarImplementation>;
|
|
14
|
+
getEthRegistrarController: (passedProvider?: any, address?: string) => Promise<import("../generated").ETHRegistrarController>;
|
|
15
|
+
getMulticall: (passedProvider?: any, address?: string) => Promise<import("../generated").Multicall>;
|
|
16
16
|
}
|
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
import { ENSArgs } from '..';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import { fuseEnum, unnamedFuses } from '../utils/fuses';
|
|
3
|
+
declare type FuseObj = typeof fuseEnum;
|
|
4
|
+
declare type UnnamedFuseType = typeof unnamedFuses;
|
|
5
|
+
declare type Fuse = keyof FuseObj;
|
|
6
|
+
declare type UnnamedFuseValues = UnnamedFuseType[number];
|
|
7
|
+
declare type FuseArrayPossibilities = [Fuse] | [Fuse, Fuse] | [Fuse, Fuse, Fuse] | [Fuse, Fuse, Fuse, Fuse] | [Fuse, Fuse, Fuse, Fuse, Fuse] | [Fuse, Fuse, Fuse, Fuse, Fuse, Fuse] | [Fuse, Fuse, Fuse, Fuse, Fuse, Fuse, Fuse];
|
|
8
|
+
/**
|
|
9
|
+
* This type creates a type error if there are any duplicate fuses.
|
|
10
|
+
* It effectively works like a reduce function, starting with 0 included types, adding a type each time, and then checking for duplicates.
|
|
11
|
+
*
|
|
12
|
+
* @template A The array to check for duplicates.
|
|
13
|
+
* @template B The union of all checked existing types.
|
|
14
|
+
*/
|
|
15
|
+
declare type FusesWithoutDuplicates<A, B = never> = A extends FuseArrayPossibilities ? A extends [infer Head, ...infer Tail] ? Head extends B ? [
|
|
16
|
+
] : [
|
|
17
|
+
Head,
|
|
18
|
+
...FusesWithoutDuplicates<Tail, Head | B>
|
|
19
|
+
] : A : [
|
|
20
|
+
];
|
|
21
|
+
declare type FusePropsNamedArray<A extends FuseArrayPossibilities> = {
|
|
22
|
+
namedFusesToBurn: FusesWithoutDuplicates<A>;
|
|
23
|
+
};
|
|
24
|
+
declare type FusePropsUnnamedArray = {
|
|
25
|
+
unnamedFusesToBurn: UnnamedFuseValues[];
|
|
26
|
+
};
|
|
27
|
+
declare type FusePropsNumber = {
|
|
28
|
+
fuseNumberToBurn: number;
|
|
29
|
+
};
|
|
30
|
+
declare type FuseProps<A extends FuseArrayPossibilities> = (Partial<FusePropsNamedArray<A>> & FusePropsUnnamedArray) | (FusePropsNamedArray<A> & Partial<FusePropsUnnamedArray>) | FusePropsNumber;
|
|
31
|
+
export default function <A extends FuseArrayPossibilities>({ contracts, signer }: ENSArgs<'contracts' | 'signer'>, name: string, props: FuseProps<A>): Promise<import("ethers").PopulatedTransaction>;
|
|
32
|
+
export {};
|
|
@@ -1,8 +1,41 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { fuseEnum, unnamedFuses } from '../utils/fuses';
|
|
2
2
|
import { namehash } from '../utils/normalise';
|
|
3
|
-
export default async function ({ contracts, signer }, name,
|
|
3
|
+
export default async function ({ contracts, signer }, name, props) {
|
|
4
|
+
const isNumber = 'fuseNumberToBurn' in props;
|
|
5
|
+
const hasNamedArray = 'namedFusesToBurn' in props;
|
|
6
|
+
const hasUnnamedArray = 'unnamedFusesToBurn' in props;
|
|
7
|
+
let encodedFuses = 0;
|
|
8
|
+
if (isNumber) {
|
|
9
|
+
if (props.fuseNumberToBurn > 2 ** 32 || props.fuseNumberToBurn < 1) {
|
|
10
|
+
throw new Error(`Fuse number must be limited to uint32, ${props.fuseNumberToBurn} was too ${props.fuseNumberToBurn < 1 ? 'low' : 'high'}.`);
|
|
11
|
+
}
|
|
12
|
+
else if (props.fuseNumberToBurn % 1 !== 0) {
|
|
13
|
+
throw new Error(`Fuse number must be an integer, ${props.fuseNumberToBurn} was not.`);
|
|
14
|
+
}
|
|
15
|
+
encodedFuses = props.fuseNumberToBurn;
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
if (!hasNamedArray && !hasUnnamedArray) {
|
|
19
|
+
throw new Error('Please provide fuses to burn');
|
|
20
|
+
}
|
|
21
|
+
if (hasNamedArray) {
|
|
22
|
+
for (const fuse of props.namedFusesToBurn) {
|
|
23
|
+
if (!(fuse in fuseEnum)) {
|
|
24
|
+
throw new Error(`${fuse} is not a valid named fuse.`);
|
|
25
|
+
}
|
|
26
|
+
encodedFuses |= fuseEnum[fuse];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (hasUnnamedArray) {
|
|
30
|
+
for (const fuse of props.unnamedFusesToBurn) {
|
|
31
|
+
if (!unnamedFuses.includes(fuse)) {
|
|
32
|
+
throw new Error(`${fuse} is not a valid unnamed fuse. If you are trying to burn a named fuse, use the namedFusesToBurn property.`);
|
|
33
|
+
}
|
|
34
|
+
encodedFuses |= fuse;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
4
38
|
const nameWrapper = (await contracts?.getNameWrapper()).connect(signer);
|
|
5
39
|
const hash = namehash(name);
|
|
6
|
-
const encodedFuses = generateFuseInput(fusesToBurn);
|
|
7
40
|
return nameWrapper.populateTransaction.setFuses(hash, encodedFuses);
|
|
8
41
|
}
|
|
@@ -5,12 +5,13 @@ declare const _default: {
|
|
|
5
5
|
to: string;
|
|
6
6
|
data: string;
|
|
7
7
|
}>;
|
|
8
|
-
decode: ({ contracts }: ENSArgs<"contracts">, data: string
|
|
8
|
+
decode: ({ contracts }: ENSArgs<"contracts">, data: string) => Promise<{
|
|
9
9
|
fuseObj: {
|
|
10
10
|
[k: string]: boolean;
|
|
11
11
|
};
|
|
12
12
|
expiryDate: Date;
|
|
13
13
|
rawFuses: BigNumber;
|
|
14
|
+
owner: any;
|
|
14
15
|
} | undefined>;
|
|
15
16
|
};
|
|
16
17
|
export default _default;
|
|
@@ -1,32 +1,21 @@
|
|
|
1
1
|
import { BigNumber } from 'ethers';
|
|
2
|
-
import {
|
|
2
|
+
import { fuseEnum } from '../utils/fuses';
|
|
3
3
|
import { namehash } from '../utils/normalise';
|
|
4
|
-
const NameSafety = [
|
|
5
|
-
'Safe',
|
|
6
|
-
'RegistrantNotWrapped',
|
|
7
|
-
'ControllerNotWrapped',
|
|
8
|
-
'SubdomainReplacementAllowed',
|
|
9
|
-
'Expired',
|
|
10
|
-
];
|
|
11
4
|
const raw = async ({ contracts }, name) => {
|
|
12
5
|
const nameWrapper = await contracts?.getNameWrapper();
|
|
13
6
|
return {
|
|
14
7
|
to: nameWrapper.address,
|
|
15
|
-
data: nameWrapper.interface.encodeFunctionData('
|
|
16
|
-
namehash(name),
|
|
17
|
-
]),
|
|
8
|
+
data: nameWrapper.interface.encodeFunctionData('getData', [namehash(name)]),
|
|
18
9
|
};
|
|
19
10
|
};
|
|
20
|
-
const decode = async ({ contracts }, data
|
|
11
|
+
const decode = async ({ contracts }, data) => {
|
|
21
12
|
const nameWrapper = await contracts?.getNameWrapper();
|
|
22
13
|
try {
|
|
23
|
-
const
|
|
14
|
+
const { owner, fuses: _fuses, expiry, } = nameWrapper.interface.decodeFunctionResult('getData', data);
|
|
24
15
|
const fuses = BigNumber.from(_fuses);
|
|
25
|
-
const fuseObj = Object.fromEntries(Object.keys(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
.replace(/([-_][a-z])/g, (group) => group.toUpperCase().replace('-', '').replace('_', '')),
|
|
29
|
-
fuses.and(fuseEnums[fuseEnum]).gt(0),
|
|
16
|
+
const fuseObj = Object.fromEntries(Object.keys(fuseEnum).map((fuse) => [
|
|
17
|
+
fuse,
|
|
18
|
+
fuses.and(fuseEnum[fuse]).gt(0),
|
|
30
19
|
]));
|
|
31
20
|
if (fuses.eq(0)) {
|
|
32
21
|
fuseObj.canDoEverything = true;
|
|
@@ -39,9 +28,11 @@ const decode = async ({ contracts }, data, name) => {
|
|
|
39
28
|
fuseObj,
|
|
40
29
|
expiryDate,
|
|
41
30
|
rawFuses: fuses,
|
|
31
|
+
owner,
|
|
42
32
|
};
|
|
43
33
|
}
|
|
44
|
-
catch {
|
|
34
|
+
catch (e) {
|
|
35
|
+
console.error('Error decoding fuses data: ', e);
|
|
45
36
|
return;
|
|
46
37
|
}
|
|
47
38
|
};
|
|
@@ -52,21 +52,21 @@ export declare const getText: {
|
|
|
52
52
|
decode: ({ contracts, universalWrapper }: ENSArgs<'contracts' | 'universalWrapper'>, data: string) => Promise<any>;
|
|
53
53
|
};
|
|
54
54
|
export declare const _getAddr: {
|
|
55
|
-
raw: ({ contracts }: ENSArgs<'contracts'>, name: string, coinType?: string | number
|
|
55
|
+
raw: ({ contracts }: ENSArgs<'contracts'>, name: string, coinType?: string | number, bypassFormat?: boolean) => Promise<{
|
|
56
56
|
to: string;
|
|
57
57
|
data: string;
|
|
58
58
|
}>;
|
|
59
|
-
decode: ({ contracts }: ENSArgs<'contracts'>, data: string, _name: string, coinType?: string | number
|
|
59
|
+
decode: ({ contracts }: ENSArgs<'contracts'>, data: string, _name: string, coinType?: string | number) => Promise<string | {
|
|
60
60
|
coin: string;
|
|
61
61
|
addr: string;
|
|
62
62
|
} | undefined>;
|
|
63
63
|
};
|
|
64
64
|
export declare const getAddr: {
|
|
65
|
-
raw: ({ contracts, universalWrapper }: ENSArgs<'contracts' | 'universalWrapper'>, name: string, coinType?: string | number
|
|
65
|
+
raw: ({ contracts, universalWrapper }: ENSArgs<'contracts' | 'universalWrapper'>, name: string, coinType?: string | number) => Promise<{
|
|
66
66
|
to: string;
|
|
67
67
|
data: string;
|
|
68
68
|
}>;
|
|
69
|
-
decode: ({ contracts, universalWrapper }: ENSArgs<'contracts' | 'universalWrapper'>, data: string, _name: string, coinType?: string | number
|
|
69
|
+
decode: ({ contracts, universalWrapper }: ENSArgs<'contracts' | 'universalWrapper'>, data: string, _name: string, coinType?: string | number) => Promise<string | {
|
|
70
70
|
coin: string;
|
|
71
71
|
addr: string;
|
|
72
72
|
} | undefined>;
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -75,6 +75,16 @@ export declare class ENS {
|
|
|
75
75
|
contracts?: ContractManager;
|
|
76
76
|
getContractAddress: (networkId: SupportedNetworkId) => import("./contracts/getContractAddress").ContractAddressFetch;
|
|
77
77
|
gqlInstance: GqlManager;
|
|
78
|
+
fuses: {
|
|
79
|
+
CAN_DO_EVERYTHING: number;
|
|
80
|
+
CANNOT_UNWRAP: 1;
|
|
81
|
+
CANNOT_BURN_FUSES: 2;
|
|
82
|
+
CANNOT_TRANSFER: 4;
|
|
83
|
+
CANNOT_SET_RESOLVER: 8;
|
|
84
|
+
CANNOT_SET_TTL: 16;
|
|
85
|
+
CANNOT_CREATE_SUBDOMAIN: 32;
|
|
86
|
+
PARENT_CANNOT_CONTROL: 64;
|
|
87
|
+
};
|
|
78
88
|
constructor(options?: ENSOptions);
|
|
79
89
|
/**
|
|
80
90
|
* Checks for an initial provider and if it exists, sets it as the provider
|
|
@@ -229,12 +239,13 @@ export declare class ENS {
|
|
|
229
239
|
to: string;
|
|
230
240
|
data: string;
|
|
231
241
|
}>;
|
|
232
|
-
decode: ({ contracts }: ENSArgs<"contracts">, data: string
|
|
242
|
+
decode: ({ contracts }: ENSArgs<"contracts">, data: string) => Promise<{
|
|
233
243
|
fuseObj: {
|
|
234
244
|
[k: string]: boolean;
|
|
235
245
|
};
|
|
236
246
|
expiryDate: Date;
|
|
237
247
|
rawFuses: ethers.BigNumber;
|
|
248
|
+
owner: any;
|
|
238
249
|
} | undefined>;
|
|
239
250
|
}>;
|
|
240
251
|
getHistory: (name: string) => Promise<{
|
package/dist/esm/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { getContractAddress as _getContractAddress } from './contracts/getContra
|
|
|
3
3
|
import GqlManager from './GqlManager';
|
|
4
4
|
import singleCall from './utils/singleCall';
|
|
5
5
|
import writeTx from './utils/writeTx';
|
|
6
|
+
import fuseEnum from './utils/fuses';
|
|
6
7
|
const graphURIEndpoints = {
|
|
7
8
|
1: 'https://api.thegraph.com/subgraphs/name/ensdomains/ens',
|
|
8
9
|
3: 'https://api.thegraph.com/subgraphs/name/ensdomains/ensropsten',
|
|
@@ -13,6 +14,7 @@ export class ENS {
|
|
|
13
14
|
constructor(options) {
|
|
14
15
|
this.getContractAddress = _getContractAddress;
|
|
15
16
|
this.gqlInstance = new GqlManager();
|
|
17
|
+
this.fuses = fuseEnum;
|
|
16
18
|
/**
|
|
17
19
|
* Checks for an initial provider and if it exists, sets it as the provider
|
|
18
20
|
* @returns {Promise<void>} - A promise that resolves when the provider is checked, and set if needed
|
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
export declare const
|
|
2
|
-
CANNOT_UNWRAP:
|
|
3
|
-
CANNOT_BURN_FUSES:
|
|
4
|
-
CANNOT_TRANSFER:
|
|
5
|
-
CANNOT_SET_RESOLVER:
|
|
6
|
-
CANNOT_SET_TTL:
|
|
7
|
-
CANNOT_CREATE_SUBDOMAIN:
|
|
8
|
-
PARENT_CANNOT_CONTROL:
|
|
1
|
+
export declare const fuseEnum: {
|
|
2
|
+
readonly CANNOT_UNWRAP: 1;
|
|
3
|
+
readonly CANNOT_BURN_FUSES: 2;
|
|
4
|
+
readonly CANNOT_TRANSFER: 4;
|
|
5
|
+
readonly CANNOT_SET_RESOLVER: 8;
|
|
6
|
+
readonly CANNOT_SET_TTL: 16;
|
|
7
|
+
readonly CANNOT_CREATE_SUBDOMAIN: 32;
|
|
8
|
+
readonly PARENT_CANNOT_CONTROL: 64;
|
|
9
9
|
};
|
|
10
|
-
declare const
|
|
10
|
+
export declare const unnamedFuses: readonly [128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824, 2147483648, 4294967296];
|
|
11
|
+
declare const fullFuseEnum: {
|
|
11
12
|
CAN_DO_EVERYTHING: number;
|
|
12
|
-
CANNOT_UNWRAP:
|
|
13
|
-
CANNOT_BURN_FUSES:
|
|
14
|
-
CANNOT_TRANSFER:
|
|
15
|
-
CANNOT_SET_RESOLVER:
|
|
16
|
-
CANNOT_SET_TTL:
|
|
17
|
-
CANNOT_CREATE_SUBDOMAIN:
|
|
18
|
-
PARENT_CANNOT_CONTROL:
|
|
13
|
+
CANNOT_UNWRAP: 1;
|
|
14
|
+
CANNOT_BURN_FUSES: 2;
|
|
15
|
+
CANNOT_TRANSFER: 4;
|
|
16
|
+
CANNOT_SET_RESOLVER: 8;
|
|
17
|
+
CANNOT_SET_TTL: 16;
|
|
18
|
+
CANNOT_CREATE_SUBDOMAIN: 32;
|
|
19
|
+
PARENT_CANNOT_CONTROL: 64;
|
|
19
20
|
};
|
|
20
|
-
export default
|
|
21
|
+
export default fullFuseEnum;
|
package/dist/esm/utils/fuses.js
CHANGED
|
@@ -6,7 +6,7 @@ const CANNOT_SET_TTL = 16;
|
|
|
6
6
|
const CANNOT_CREATE_SUBDOMAIN = 32;
|
|
7
7
|
const PARENT_CANNOT_CONTROL = 64;
|
|
8
8
|
const CAN_DO_EVERYTHING = 0;
|
|
9
|
-
export const
|
|
9
|
+
export const fuseEnum = {
|
|
10
10
|
CANNOT_UNWRAP,
|
|
11
11
|
CANNOT_BURN_FUSES,
|
|
12
12
|
CANNOT_TRANSFER,
|
|
@@ -15,7 +15,13 @@ export const testable = {
|
|
|
15
15
|
CANNOT_CREATE_SUBDOMAIN,
|
|
16
16
|
PARENT_CANNOT_CONTROL,
|
|
17
17
|
};
|
|
18
|
-
export
|
|
19
|
-
|
|
18
|
+
export const unnamedFuses = [
|
|
19
|
+
128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144,
|
|
20
|
+
524288, 1048576, 2097152, 4194304, 8388608, 16777216, 33554432, 67108864,
|
|
21
|
+
134217728, 268435456, 536870912, 1073741824, 2147483648, 4294967296,
|
|
22
|
+
];
|
|
23
|
+
const fullFuseEnum = {
|
|
24
|
+
...fuseEnum,
|
|
20
25
|
CAN_DO_EVERYTHING,
|
|
21
26
|
};
|
|
27
|
+
export default fullFuseEnum;
|
|
@@ -2,4 +2,4 @@ import { BigNumber } from 'ethers';
|
|
|
2
2
|
import { ENSArgs } from '..';
|
|
3
3
|
export declare type Expiry = string | number | Date | BigNumber;
|
|
4
4
|
export declare const MAX_EXPIRY: BigNumber;
|
|
5
|
-
export declare const makeExpiry: ({ getExpiry }: ENSArgs<'getExpiry'>, name: string, expiry?: Expiry
|
|
5
|
+
export declare const makeExpiry: ({ getExpiry }: ENSArgs<'getExpiry'>, name: string, expiry?: Expiry) => Promise<BigNumber>;
|
package/package.json
CHANGED
|
@@ -21,20 +21,128 @@ describe('burnFuses', () => {
|
|
|
21
21
|
beforeEach(async () => {
|
|
22
22
|
await revert()
|
|
23
23
|
})
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
24
|
+
describe('Array', () => {
|
|
25
|
+
it('should return a burnFuses transaction from a named fuse array and succeed', async () => {
|
|
26
|
+
const tx = await ENSInstance.burnFuses('wrapped.eth', {
|
|
27
|
+
namedFusesToBurn: [
|
|
28
|
+
'CANNOT_UNWRAP',
|
|
29
|
+
'CANNOT_CREATE_SUBDOMAIN',
|
|
30
|
+
'CANNOT_SET_TTL',
|
|
31
|
+
],
|
|
32
|
+
addressOrIndex: accounts[1],
|
|
33
|
+
})
|
|
34
|
+
expect(tx).toBeTruthy()
|
|
35
|
+
await tx.wait()
|
|
36
|
+
|
|
37
|
+
const nameWrapper = await ENSInstance.contracts!.getNameWrapper()!
|
|
38
|
+
const [fuses] = await nameWrapper.getFuses(namehash('wrapped.eth'))
|
|
39
|
+
expect(fuses).toBe(113)
|
|
40
|
+
})
|
|
41
|
+
it('should return a burnFuses transaction from an unnamed fuse array and succeed', async () => {
|
|
42
|
+
const tx0 = await ENSInstance.burnFuses('wrapped.eth', {
|
|
43
|
+
namedFusesToBurn: ['CANNOT_UNWRAP'],
|
|
44
|
+
addressOrIndex: accounts[1],
|
|
45
|
+
})
|
|
46
|
+
expect(tx0).toBeTruthy()
|
|
47
|
+
await tx0.wait()
|
|
48
|
+
|
|
49
|
+
const tx = await ENSInstance.burnFuses('wrapped.eth', {
|
|
50
|
+
unnamedFusesToBurn: [128, 256, 512],
|
|
51
|
+
addressOrIndex: accounts[1],
|
|
52
|
+
})
|
|
53
|
+
expect(tx).toBeTruthy()
|
|
54
|
+
await tx.wait()
|
|
55
|
+
|
|
56
|
+
const nameWrapper = await ENSInstance.contracts!.getNameWrapper()!
|
|
57
|
+
const [fuses] = await nameWrapper.getFuses(namehash('wrapped.eth'))
|
|
58
|
+
expect(fuses).toBe(961)
|
|
59
|
+
})
|
|
60
|
+
it('should return a burnFuses transaction from both an unnamed and named fuse array and succeed', async () => {
|
|
61
|
+
const tx = await ENSInstance.burnFuses('wrapped.eth', {
|
|
62
|
+
namedFusesToBurn: [
|
|
63
|
+
'CANNOT_UNWRAP',
|
|
64
|
+
'CANNOT_CREATE_SUBDOMAIN',
|
|
65
|
+
'CANNOT_SET_TTL',
|
|
66
|
+
],
|
|
67
|
+
unnamedFusesToBurn: [128, 256, 512],
|
|
68
|
+
addressOrIndex: accounts[1],
|
|
69
|
+
})
|
|
70
|
+
expect(tx).toBeTruthy()
|
|
71
|
+
await tx.wait()
|
|
72
|
+
|
|
73
|
+
const nameWrapper = await ENSInstance.contracts!.getNameWrapper()!
|
|
74
|
+
const [fuses] = await nameWrapper.getFuses(namehash('wrapped.eth'))
|
|
75
|
+
expect(fuses).toBe(1009)
|
|
76
|
+
})
|
|
77
|
+
it('should throw an error when trying to burn a named fuse in an unnamed fuse array', async () => {
|
|
78
|
+
try {
|
|
79
|
+
await ENSInstance.burnFuses('wrapped.eth', {
|
|
80
|
+
unnamedFusesToBurn: [64] as any,
|
|
81
|
+
})
|
|
82
|
+
expect(false).toBeTruthy()
|
|
83
|
+
} catch (e: any) {
|
|
84
|
+
expect(e.message).toBe(
|
|
85
|
+
'64 is not a valid unnamed fuse. If you are trying to burn a named fuse, use the namedFusesToBurn property.',
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
it('should throw an error when trying to burn an unnamed fuse in a named fuse array', async () => {
|
|
90
|
+
try {
|
|
91
|
+
await ENSInstance.burnFuses('wrapped.eth', {
|
|
92
|
+
namedFusesToBurn: ['COOL_SWAG_FUSE'] as any,
|
|
93
|
+
})
|
|
94
|
+
expect(false).toBeTruthy()
|
|
95
|
+
} catch (e: any) {
|
|
96
|
+
expect(e.message).toBe('COOL_SWAG_FUSE is not a valid named fuse.')
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
})
|
|
100
|
+
describe('Number', () => {
|
|
101
|
+
it('should return a burnFuses transaction from a number and succeed', async () => {
|
|
102
|
+
const tx = await ENSInstance.burnFuses('wrapped.eth', {
|
|
103
|
+
fuseNumberToBurn: 49,
|
|
104
|
+
addressOrIndex: accounts[1],
|
|
105
|
+
})
|
|
106
|
+
expect(tx).toBeTruthy()
|
|
107
|
+
await tx.wait()
|
|
108
|
+
|
|
109
|
+
const nameWrapper = await ENSInstance.contracts!.getNameWrapper()!
|
|
110
|
+
const [fuses] = await nameWrapper.getFuses(namehash('wrapped.eth'))
|
|
111
|
+
expect(fuses).toBe(113)
|
|
112
|
+
})
|
|
113
|
+
it('should throw an error if the number is too high', async () => {
|
|
114
|
+
try {
|
|
115
|
+
await ENSInstance.burnFuses('wrapped.eth', {
|
|
116
|
+
fuseNumberToBurn: 4294967297,
|
|
117
|
+
})
|
|
118
|
+
expect(false).toBeTruthy()
|
|
119
|
+
} catch (e: any) {
|
|
120
|
+
expect(e.message).toBe(
|
|
121
|
+
'Fuse number must be limited to uint32, 4294967297 was too high.',
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
it('should throw an error if the number is too low', async () => {
|
|
126
|
+
try {
|
|
127
|
+
await ENSInstance.burnFuses('wrapped.eth', {
|
|
128
|
+
fuseNumberToBurn: -1,
|
|
129
|
+
})
|
|
130
|
+
expect(false).toBeTruthy()
|
|
131
|
+
} catch (e: any) {
|
|
132
|
+
expect(e.message).toBe(
|
|
133
|
+
'Fuse number must be limited to uint32, -1 was too low.',
|
|
134
|
+
)
|
|
135
|
+
}
|
|
136
|
+
})
|
|
137
|
+
it('should throw an error if the number is not an integer', async () => {
|
|
138
|
+
try {
|
|
139
|
+
await ENSInstance.burnFuses('wrapped.eth', {
|
|
140
|
+
fuseNumberToBurn: 7.5,
|
|
141
|
+
})
|
|
142
|
+
expect(false).toBeTruthy()
|
|
143
|
+
} catch (e: any) {
|
|
144
|
+
expect(e.message).toBe('Fuse number must be an integer, 7.5 was not.')
|
|
145
|
+
}
|
|
146
|
+
})
|
|
39
147
|
})
|
|
40
148
|
})
|
|
@@ -1,21 +1,112 @@
|
|
|
1
1
|
import { ENSArgs } from '..'
|
|
2
|
-
import {
|
|
3
|
-
import generateFuseInput from '../utils/generateFuseInput'
|
|
2
|
+
import { fuseEnum, unnamedFuses } from '../utils/fuses'
|
|
4
3
|
import { namehash } from '../utils/normalise'
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
type FuseObj = typeof fuseEnum
|
|
6
|
+
type UnnamedFuseType = typeof unnamedFuses
|
|
7
|
+
type Fuse = keyof FuseObj
|
|
8
|
+
type NamedFuseValues = FuseObj[Fuse]
|
|
9
|
+
type UnnamedFuseValues = UnnamedFuseType[number]
|
|
10
|
+
|
|
11
|
+
// We need this type so that the following type isn't infinite. This type limits the max length of the fuse array to 7.
|
|
12
|
+
type FuseArrayPossibilities =
|
|
13
|
+
| [Fuse]
|
|
14
|
+
| [Fuse, Fuse]
|
|
15
|
+
| [Fuse, Fuse, Fuse]
|
|
16
|
+
| [Fuse, Fuse, Fuse, Fuse]
|
|
17
|
+
| [Fuse, Fuse, Fuse, Fuse, Fuse]
|
|
18
|
+
| [Fuse, Fuse, Fuse, Fuse, Fuse, Fuse]
|
|
19
|
+
| [Fuse, Fuse, Fuse, Fuse, Fuse, Fuse, Fuse]
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* This type creates a type error if there are any duplicate fuses.
|
|
23
|
+
* It effectively works like a reduce function, starting with 0 included types, adding a type each time, and then checking for duplicates.
|
|
24
|
+
*
|
|
25
|
+
* @template A The array to check for duplicates.
|
|
26
|
+
* @template B The union of all checked existing types.
|
|
27
|
+
*/
|
|
28
|
+
// CLAUSE A: This extension unwraps the type as a fuse tuple.
|
|
29
|
+
type FusesWithoutDuplicates<A, B = never> = A extends FuseArrayPossibilities
|
|
30
|
+
? // CLAUSE A > TRUE: CLAUSE B: Pick out the first item in the current array, separating the current item from the rest.
|
|
31
|
+
A extends [infer Head, ...infer Tail]
|
|
32
|
+
? // CLAUSE B > TRUE: CLAUSE C: Check if the current item is a duplicate based on the input union.
|
|
33
|
+
Head extends B
|
|
34
|
+
? // CLAUSE C > TRUE: Duplicate found, return an empty array to throw a type error.
|
|
35
|
+
[]
|
|
36
|
+
: // CLAUSE C > FALSE: Return a new array to continue the recursion, adds the current item type to the union.
|
|
37
|
+
[Head, ...FusesWithoutDuplicates<Tail, Head | B>]
|
|
38
|
+
: // CLAUSE B > FALSE: Return the input array as there is no more array elements to check.
|
|
39
|
+
A
|
|
40
|
+
: // CLAUSE A > FALSE: Return an empty array as it isn't a fuse tuple.
|
|
41
|
+
[]
|
|
42
|
+
|
|
43
|
+
type FusePropsNamedArray<A extends FuseArrayPossibilities> = {
|
|
44
|
+
namedFusesToBurn: FusesWithoutDuplicates<A>
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
type FusePropsUnnamedArray = {
|
|
48
|
+
unnamedFusesToBurn: UnnamedFuseValues[]
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
type FusePropsNumber = {
|
|
52
|
+
fuseNumberToBurn: number
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
type FuseProps<A extends FuseArrayPossibilities> =
|
|
56
|
+
| (Partial<FusePropsNamedArray<A>> & FusePropsUnnamedArray)
|
|
57
|
+
| (FusePropsNamedArray<A> & Partial<FusePropsUnnamedArray>)
|
|
58
|
+
| FusePropsNumber
|
|
59
|
+
|
|
60
|
+
export default async function <A extends FuseArrayPossibilities>(
|
|
7
61
|
{ contracts, signer }: ENSArgs<'contracts' | 'signer'>,
|
|
8
62
|
name: string,
|
|
9
|
-
|
|
10
|
-
fusesToBurn,
|
|
11
|
-
}: {
|
|
12
|
-
fusesToBurn: FuseOptions
|
|
13
|
-
},
|
|
63
|
+
props: FuseProps<A>,
|
|
14
64
|
) {
|
|
65
|
+
const isNumber = 'fuseNumberToBurn' in props
|
|
66
|
+
const hasNamedArray = 'namedFusesToBurn' in props
|
|
67
|
+
const hasUnnamedArray = 'unnamedFusesToBurn' in props
|
|
68
|
+
|
|
69
|
+
let encodedFuses: number = 0
|
|
70
|
+
|
|
71
|
+
if (isNumber) {
|
|
72
|
+
if (props.fuseNumberToBurn > 2 ** 32 || props.fuseNumberToBurn < 1) {
|
|
73
|
+
throw new Error(
|
|
74
|
+
`Fuse number must be limited to uint32, ${
|
|
75
|
+
props.fuseNumberToBurn
|
|
76
|
+
} was too ${props.fuseNumberToBurn < 1 ? 'low' : 'high'}.`,
|
|
77
|
+
)
|
|
78
|
+
} else if (props.fuseNumberToBurn % 1 !== 0) {
|
|
79
|
+
throw new Error(
|
|
80
|
+
`Fuse number must be an integer, ${props.fuseNumberToBurn} was not.`,
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
encodedFuses = props.fuseNumberToBurn
|
|
84
|
+
} else {
|
|
85
|
+
if (!hasNamedArray && !hasUnnamedArray) {
|
|
86
|
+
throw new Error('Please provide fuses to burn')
|
|
87
|
+
}
|
|
88
|
+
if (hasNamedArray) {
|
|
89
|
+
for (const fuse of props.namedFusesToBurn!) {
|
|
90
|
+
if (!(fuse in fuseEnum)) {
|
|
91
|
+
throw new Error(`${fuse} is not a valid named fuse.`)
|
|
92
|
+
}
|
|
93
|
+
encodedFuses |= fuseEnum[fuse]
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (hasUnnamedArray) {
|
|
97
|
+
for (const fuse of props.unnamedFusesToBurn!) {
|
|
98
|
+
if (!unnamedFuses.includes(fuse)) {
|
|
99
|
+
throw new Error(
|
|
100
|
+
`${fuse} is not a valid unnamed fuse. If you are trying to burn a named fuse, use the namedFusesToBurn property.`,
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
encodedFuses |= fuse
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
15
108
|
const nameWrapper = (await contracts?.getNameWrapper()!).connect(signer)
|
|
16
109
|
const hash = namehash(name)
|
|
17
110
|
|
|
18
|
-
const encodedFuses = generateFuseInput(fusesToBurn)
|
|
19
|
-
|
|
20
111
|
return nameWrapper.populateTransaction.setFuses(hash, encodedFuses)
|
|
21
112
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ethers } from 'ethers'
|
|
1
|
+
import { BigNumber, ethers } from 'ethers'
|
|
2
2
|
import { ENS } from '..'
|
|
3
3
|
import setup from '../tests/setup'
|
|
4
4
|
|
|
@@ -9,6 +9,22 @@ let provider: ethers.providers.JsonRpcProvider
|
|
|
9
9
|
let accounts: string[]
|
|
10
10
|
let withWrappedSnapshot: any
|
|
11
11
|
|
|
12
|
+
const unwrappedNameDefault = {
|
|
13
|
+
expiryDate: new Date(0).toString(),
|
|
14
|
+
fuseObj: {
|
|
15
|
+
CANNOT_BURN_FUSES: false,
|
|
16
|
+
CANNOT_CREATE_SUBDOMAIN: false,
|
|
17
|
+
CANNOT_SET_RESOLVER: false,
|
|
18
|
+
CANNOT_SET_TTL: false,
|
|
19
|
+
CANNOT_TRANSFER: false,
|
|
20
|
+
CANNOT_UNWRAP: false,
|
|
21
|
+
PARENT_CANNOT_CONTROL: false,
|
|
22
|
+
canDoEverything: true,
|
|
23
|
+
},
|
|
24
|
+
owner: '0x0000000000000000000000000000000000000000',
|
|
25
|
+
rawFuses: BigNumber.from(0),
|
|
26
|
+
}
|
|
27
|
+
|
|
12
28
|
beforeAll(async () => {
|
|
13
29
|
;({ ENSInstance, revert, provider, createSnapshot } = await setup())
|
|
14
30
|
accounts = await provider.listAccounts()
|
|
@@ -26,9 +42,11 @@ afterAll(async () => {
|
|
|
26
42
|
})
|
|
27
43
|
|
|
28
44
|
describe('getFuses', () => {
|
|
29
|
-
it('should return
|
|
45
|
+
it('should return default data for an unwrapped name', async () => {
|
|
30
46
|
const result = await ENSInstance.getFuses('with-profile.eth')
|
|
31
|
-
expect(result).
|
|
47
|
+
expect({ ...result, expiryDate: result?.expiryDate.toString() }).toEqual(
|
|
48
|
+
unwrappedNameDefault,
|
|
49
|
+
)
|
|
32
50
|
})
|
|
33
51
|
it('should return with canDoEverything set to true for a name with no fuses burned', async () => {
|
|
34
52
|
const nameWrapper = await ENSInstance.contracts!.getNameWrapper()!
|
|
@@ -47,11 +65,11 @@ describe('getFuses', () => {
|
|
|
47
65
|
})
|
|
48
66
|
it('should return with other correct fuses', async () => {
|
|
49
67
|
const tx = await ENSInstance.burnFuses('wrapped.eth', {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
68
|
+
namedFusesToBurn: [
|
|
69
|
+
'CANNOT_UNWRAP',
|
|
70
|
+
'CANNOT_CREATE_SUBDOMAIN',
|
|
71
|
+
'CANNOT_SET_TTL',
|
|
72
|
+
],
|
|
55
73
|
addressOrIndex: 1,
|
|
56
74
|
})
|
|
57
75
|
await tx.wait()
|
|
@@ -60,13 +78,13 @@ describe('getFuses', () => {
|
|
|
60
78
|
expect(result).toBeTruthy()
|
|
61
79
|
if (result) {
|
|
62
80
|
expect(result.fuseObj).toMatchObject({
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
81
|
+
CANNOT_UNWRAP: true,
|
|
82
|
+
CANNOT_BURN_FUSES: false,
|
|
83
|
+
CANNOT_TRANSFER: false,
|
|
84
|
+
CANNOT_SET_RESOLVER: false,
|
|
85
|
+
CANNOT_SET_TTL: true,
|
|
86
|
+
CANNOT_CREATE_SUBDOMAIN: true,
|
|
87
|
+
PARENT_CANNOT_CONTROL: true,
|
|
70
88
|
canDoEverything: false,
|
|
71
89
|
})
|
|
72
90
|
expect(result.rawFuses.toHexString()).toBe('0x71')
|
|
@@ -1,48 +1,34 @@
|
|
|
1
1
|
import { BigNumber } from 'ethers'
|
|
2
2
|
import { ENSArgs } from '..'
|
|
3
|
-
import {
|
|
3
|
+
import { fuseEnum } from '../utils/fuses'
|
|
4
4
|
import { namehash } from '../utils/normalise'
|
|
5
5
|
|
|
6
|
-
const NameSafety = [
|
|
7
|
-
'Safe',
|
|
8
|
-
'RegistrantNotWrapped',
|
|
9
|
-
'ControllerNotWrapped',
|
|
10
|
-
'SubdomainReplacementAllowed',
|
|
11
|
-
'Expired',
|
|
12
|
-
]
|
|
13
|
-
|
|
14
6
|
const raw = async ({ contracts }: ENSArgs<'contracts'>, name: string) => {
|
|
15
7
|
const nameWrapper = await contracts?.getNameWrapper()!
|
|
16
8
|
return {
|
|
17
9
|
to: nameWrapper.address,
|
|
18
|
-
data: nameWrapper.interface.encodeFunctionData('
|
|
19
|
-
namehash(name),
|
|
20
|
-
]),
|
|
10
|
+
data: nameWrapper.interface.encodeFunctionData('getData', [namehash(name)]),
|
|
21
11
|
}
|
|
22
12
|
}
|
|
23
13
|
|
|
24
14
|
const decode = async (
|
|
25
15
|
{ contracts }: ENSArgs<'contracts'>,
|
|
26
16
|
data: string,
|
|
27
|
-
name: string,
|
|
28
17
|
) => {
|
|
29
18
|
const nameWrapper = await contracts?.getNameWrapper()!
|
|
30
19
|
try {
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
20
|
+
const {
|
|
21
|
+
owner,
|
|
22
|
+
fuses: _fuses,
|
|
23
|
+
expiry,
|
|
24
|
+
} = nameWrapper.interface.decodeFunctionResult('getData', data)
|
|
35
25
|
|
|
36
26
|
const fuses = BigNumber.from(_fuses)
|
|
37
27
|
|
|
38
28
|
const fuseObj = Object.fromEntries(
|
|
39
|
-
Object.keys(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
.replace(/([-_][a-z])/g, (group: string) =>
|
|
43
|
-
group.toUpperCase().replace('-', '').replace('_', ''),
|
|
44
|
-
),
|
|
45
|
-
fuses.and(fuseEnums[fuseEnum as keyof typeof fuseEnums]).gt(0),
|
|
29
|
+
Object.keys(fuseEnum).map((fuse) => [
|
|
30
|
+
fuse,
|
|
31
|
+
fuses.and(fuseEnum[fuse as keyof typeof fuseEnum]).gt(0),
|
|
46
32
|
]),
|
|
47
33
|
)
|
|
48
34
|
|
|
@@ -58,8 +44,10 @@ const decode = async (
|
|
|
58
44
|
fuseObj,
|
|
59
45
|
expiryDate,
|
|
60
46
|
rawFuses: fuses,
|
|
47
|
+
owner,
|
|
61
48
|
}
|
|
62
|
-
} catch {
|
|
49
|
+
} catch (e) {
|
|
50
|
+
console.error('Error decoding fuses data: ', e)
|
|
63
51
|
return
|
|
64
52
|
}
|
|
65
53
|
}
|
package/src/index.ts
CHANGED
|
@@ -46,6 +46,7 @@ import type wrapName from './functions/wrapName'
|
|
|
46
46
|
import GqlManager from './GqlManager'
|
|
47
47
|
import singleCall from './utils/singleCall'
|
|
48
48
|
import writeTx from './utils/writeTx'
|
|
49
|
+
import fuseEnum from './utils/fuses'
|
|
49
50
|
|
|
50
51
|
type ENSOptions = {
|
|
51
52
|
graphURI?: string | null
|
|
@@ -157,6 +158,7 @@ export class ENS {
|
|
|
157
158
|
contracts?: ContractManager
|
|
158
159
|
getContractAddress = _getContractAddress
|
|
159
160
|
gqlInstance = new GqlManager()
|
|
161
|
+
fuses = fuseEnum
|
|
160
162
|
|
|
161
163
|
constructor(options?: ENSOptions) {
|
|
162
164
|
this.options = options
|
package/src/utils/fuses.ts
CHANGED
|
@@ -7,7 +7,7 @@ const CANNOT_CREATE_SUBDOMAIN = 32
|
|
|
7
7
|
const PARENT_CANNOT_CONTROL = 64
|
|
8
8
|
const CAN_DO_EVERYTHING = 0
|
|
9
9
|
|
|
10
|
-
export const
|
|
10
|
+
export const fuseEnum = {
|
|
11
11
|
CANNOT_UNWRAP,
|
|
12
12
|
CANNOT_BURN_FUSES,
|
|
13
13
|
CANNOT_TRANSFER,
|
|
@@ -15,9 +15,17 @@ export const testable = {
|
|
|
15
15
|
CANNOT_SET_TTL,
|
|
16
16
|
CANNOT_CREATE_SUBDOMAIN,
|
|
17
17
|
PARENT_CANNOT_CONTROL,
|
|
18
|
-
}
|
|
18
|
+
} as const
|
|
19
|
+
|
|
20
|
+
export const unnamedFuses = [
|
|
21
|
+
128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144,
|
|
22
|
+
524288, 1048576, 2097152, 4194304, 8388608, 16777216, 33554432, 67108864,
|
|
23
|
+
134217728, 268435456, 536870912, 1073741824, 2147483648, 4294967296,
|
|
24
|
+
] as const
|
|
19
25
|
|
|
20
|
-
|
|
21
|
-
...
|
|
26
|
+
const fullFuseEnum = {
|
|
27
|
+
...fuseEnum,
|
|
22
28
|
CAN_DO_EVERYTHING,
|
|
23
29
|
}
|
|
30
|
+
|
|
31
|
+
export default fullFuseEnum
|