@ensdomains/ensjs 3.0.0-alpha.8 → 3.0.0-alpha.9
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/functions/setRecord.d.ts +18 -0
- package/dist/cjs/functions/setRecord.js +27 -0
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.js +5 -0
- package/dist/cjs/utils/recordHelpers.d.ts +3 -0
- package/dist/cjs/utils/recordHelpers.js +42 -15
- package/dist/esm/functions/setRecord.d.ts +18 -0
- package/dist/esm/functions/setRecord.js +24 -0
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/utils/recordHelpers.d.ts +3 -0
- package/dist/esm/utils/recordHelpers.js +40 -14
- package/package.json +2 -2
- package/src/functions/setRecord.test.ts +100 -0
- package/src/functions/setRecord.ts +63 -0
- package/src/functions/setRecords.test.ts +2 -2
- package/src/index.ts +7 -0
- package/src/utils/recordHelpers.ts +52 -18
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ENSArgs } from '..';
|
|
2
|
+
import { RecordInput, RecordTypes } from '../utils/recordHelpers';
|
|
3
|
+
declare type BaseInput = {
|
|
4
|
+
resolverAddress?: string;
|
|
5
|
+
};
|
|
6
|
+
declare type ContentHashInput = {
|
|
7
|
+
record: RecordInput<'contentHash'>;
|
|
8
|
+
type: 'contentHash';
|
|
9
|
+
};
|
|
10
|
+
declare type AddrOrTextInput = {
|
|
11
|
+
record: RecordInput<'addr' | 'text'>;
|
|
12
|
+
type: 'addr' | 'text';
|
|
13
|
+
};
|
|
14
|
+
export default function <T extends RecordTypes>({ contracts, provider, getResolver, signer, }: ENSArgs<'contracts' | 'provider' | 'getResolver' | 'signer'>, name: string, { record, type, resolverAddress, }: BaseInput & (ContentHashInput | AddrOrTextInput)): Promise<{
|
|
15
|
+
to: string;
|
|
16
|
+
data: string;
|
|
17
|
+
}>;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const normalise_1 = require("../utils/normalise");
|
|
4
|
+
const recordHelpers_1 = require("../utils/recordHelpers");
|
|
5
|
+
async function default_1({ contracts, provider, getResolver, signer, }, name, { record, type, resolverAddress, }) {
|
|
6
|
+
if (!name.includes('.')) {
|
|
7
|
+
throw new Error('Input is not an ENS name');
|
|
8
|
+
}
|
|
9
|
+
let resolverToUse;
|
|
10
|
+
if (resolverAddress) {
|
|
11
|
+
resolverToUse = resolverAddress;
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
resolverToUse = await getResolver(name);
|
|
15
|
+
}
|
|
16
|
+
if (!resolverToUse) {
|
|
17
|
+
throw new Error('No resolver found for input address');
|
|
18
|
+
}
|
|
19
|
+
const resolver = (await contracts?.getPublicResolver(provider, resolverToUse))?.connect(signer);
|
|
20
|
+
const hash = (0, normalise_1.namehash)(name);
|
|
21
|
+
const call = (0, recordHelpers_1.generateSingleRecordCall)(hash, resolver, type)(record);
|
|
22
|
+
return {
|
|
23
|
+
to: resolver.address,
|
|
24
|
+
data: call,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
exports.default = default_1;
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type burnFuses from './functions/burnFuses';
|
|
|
7
7
|
import type createSubname from './functions/createSubname';
|
|
8
8
|
import type deleteSubname from './functions/deleteSubname';
|
|
9
9
|
import type setName from './functions/setName';
|
|
10
|
+
import type setRecord from './functions/setRecord';
|
|
10
11
|
import type setRecords from './functions/setRecords';
|
|
11
12
|
import type setResolver from './functions/setResolver';
|
|
12
13
|
import type transferName from './functions/transferName';
|
|
@@ -457,6 +458,7 @@ export declare class ENS {
|
|
|
457
458
|
}>;
|
|
458
459
|
setName: WriteFunction<typeof setName>;
|
|
459
460
|
setRecords: WriteFunction<typeof setRecords>;
|
|
461
|
+
setRecord: WriteFunction<typeof setRecord>;
|
|
460
462
|
setResolver: WriteFunction<typeof setResolver>;
|
|
461
463
|
transferName: WriteFunction<typeof transferName>;
|
|
462
464
|
wrapName: WriteFunction<typeof wrapName>;
|
package/dist/cjs/index.js
CHANGED
|
@@ -225,6 +225,11 @@ class ENS {
|
|
|
225
225
|
'contracts',
|
|
226
226
|
]);
|
|
227
227
|
setRecords = this.generateWriteFunction('setRecords', ['contracts', 'provider', 'getResolver']);
|
|
228
|
+
setRecord = this.generateWriteFunction('setRecord', [
|
|
229
|
+
'contracts',
|
|
230
|
+
'provider',
|
|
231
|
+
'getResolver',
|
|
232
|
+
]);
|
|
228
233
|
setResolver = this.generateWriteFunction('setResolver', ['contracts']);
|
|
229
234
|
transferName = this.generateWriteFunction('transferName', ['contracts']);
|
|
230
235
|
wrapName = this.generateWriteFunction('wrapName', [
|
|
@@ -9,5 +9,8 @@ export declare type RecordOptions = {
|
|
|
9
9
|
coinTypes?: RecordItem[];
|
|
10
10
|
};
|
|
11
11
|
export declare const generateSetAddr: (namehash: string, coinType: string, address: string, resolver: PublicResolver) => string;
|
|
12
|
+
export declare type RecordTypes = 'contentHash' | 'text' | 'addr';
|
|
13
|
+
export declare type RecordInput<T extends RecordTypes> = T extends 'contentHash' ? string : RecordItem;
|
|
14
|
+
export declare function generateSingleRecordCall<T extends RecordTypes>(namehash: string, resolver: PublicResolver, type: T): (record: RecordInput<T>) => string;
|
|
12
15
|
export declare const generateRecordCallArray: (namehash: string, records: RecordOptions, resolver: PublicResolver) => string[];
|
|
13
16
|
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.generateRecordCallArray = exports.generateSetAddr = void 0;
|
|
3
|
+
exports.generateRecordCallArray = exports.generateSingleRecordCall = exports.generateSetAddr = void 0;
|
|
4
4
|
const address_encoder_1 = require("@ensdomains/address-encoder");
|
|
5
5
|
const contentHash_1 = require("./contentHash");
|
|
6
6
|
const generateSetAddr = (namehash, coinType, address, resolver) => {
|
|
@@ -16,28 +16,55 @@ const generateSetAddr = (namehash, coinType, address, resolver) => {
|
|
|
16
16
|
return resolver?.interface.encodeFunctionData('setAddr(bytes32,uint256,bytes)', [namehash, inputCoinType, encodedAddress]);
|
|
17
17
|
};
|
|
18
18
|
exports.generateSetAddr = generateSetAddr;
|
|
19
|
+
function generateSingleRecordCall(namehash, resolver, type) {
|
|
20
|
+
if (type === 'contentHash') {
|
|
21
|
+
return (_r) => {
|
|
22
|
+
const record = _r;
|
|
23
|
+
let _contentHash = '';
|
|
24
|
+
if (record !== _contentHash) {
|
|
25
|
+
const encoded = (0, contentHash_1.encodeContenthash)(record);
|
|
26
|
+
if (encoded.error)
|
|
27
|
+
throw new Error(encoded.error);
|
|
28
|
+
_contentHash = encoded.encoded;
|
|
29
|
+
}
|
|
30
|
+
return resolver.interface.encodeFunctionData('setContenthash', [
|
|
31
|
+
namehash,
|
|
32
|
+
_contentHash,
|
|
33
|
+
]);
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
return (_r) => {
|
|
38
|
+
const record = _r;
|
|
39
|
+
if (type === 'text') {
|
|
40
|
+
return resolver.interface.encodeFunctionData('setText', [
|
|
41
|
+
namehash,
|
|
42
|
+
record.key,
|
|
43
|
+
record.value,
|
|
44
|
+
]);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
return (0, exports.generateSetAddr)(namehash, record.key, record.value, resolver);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
exports.generateSingleRecordCall = generateSingleRecordCall;
|
|
19
53
|
const generateRecordCallArray = (namehash, records, resolver) => {
|
|
20
54
|
const calls = [];
|
|
21
55
|
if (records.contentHash) {
|
|
22
|
-
const
|
|
23
|
-
const data = resolver?.interface.encodeFunctionData('setContenthash', [namehash, contentHash]);
|
|
56
|
+
const data = generateSingleRecordCall(namehash, resolver, 'contentHash')(records.contentHash);
|
|
24
57
|
data && calls.push(data);
|
|
25
58
|
}
|
|
26
59
|
if (records.texts && records.texts.length > 0) {
|
|
27
|
-
records.texts
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
key,
|
|
31
|
-
value,
|
|
32
|
-
]);
|
|
33
|
-
data && calls.push(data);
|
|
34
|
-
});
|
|
60
|
+
records.texts
|
|
61
|
+
.map(generateSingleRecordCall(namehash, resolver, 'text'))
|
|
62
|
+
.forEach((call) => calls.push(call));
|
|
35
63
|
}
|
|
36
64
|
if (records.coinTypes && records.coinTypes.length > 0) {
|
|
37
|
-
records.coinTypes
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
});
|
|
65
|
+
records.coinTypes
|
|
66
|
+
.map(generateSingleRecordCall(namehash, resolver, 'addr'))
|
|
67
|
+
.forEach((call) => calls.push(call));
|
|
41
68
|
}
|
|
42
69
|
return calls;
|
|
43
70
|
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ENSArgs } from '..';
|
|
2
|
+
import { RecordInput, RecordTypes } from '../utils/recordHelpers';
|
|
3
|
+
declare type BaseInput = {
|
|
4
|
+
resolverAddress?: string;
|
|
5
|
+
};
|
|
6
|
+
declare type ContentHashInput = {
|
|
7
|
+
record: RecordInput<'contentHash'>;
|
|
8
|
+
type: 'contentHash';
|
|
9
|
+
};
|
|
10
|
+
declare type AddrOrTextInput = {
|
|
11
|
+
record: RecordInput<'addr' | 'text'>;
|
|
12
|
+
type: 'addr' | 'text';
|
|
13
|
+
};
|
|
14
|
+
export default function <T extends RecordTypes>({ contracts, provider, getResolver, signer, }: ENSArgs<'contracts' | 'provider' | 'getResolver' | 'signer'>, name: string, { record, type, resolverAddress, }: BaseInput & (ContentHashInput | AddrOrTextInput)): Promise<{
|
|
15
|
+
to: string;
|
|
16
|
+
data: string;
|
|
17
|
+
}>;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { namehash } from '../utils/normalise';
|
|
2
|
+
import { generateSingleRecordCall, } from '../utils/recordHelpers';
|
|
3
|
+
export default async function ({ contracts, provider, getResolver, signer, }, name, { record, type, resolverAddress, }) {
|
|
4
|
+
if (!name.includes('.')) {
|
|
5
|
+
throw new Error('Input is not an ENS name');
|
|
6
|
+
}
|
|
7
|
+
let resolverToUse;
|
|
8
|
+
if (resolverAddress) {
|
|
9
|
+
resolverToUse = resolverAddress;
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
resolverToUse = await getResolver(name);
|
|
13
|
+
}
|
|
14
|
+
if (!resolverToUse) {
|
|
15
|
+
throw new Error('No resolver found for input address');
|
|
16
|
+
}
|
|
17
|
+
const resolver = (await contracts?.getPublicResolver(provider, resolverToUse))?.connect(signer);
|
|
18
|
+
const hash = namehash(name);
|
|
19
|
+
const call = generateSingleRecordCall(hash, resolver, type)(record);
|
|
20
|
+
return {
|
|
21
|
+
to: resolver.address,
|
|
22
|
+
data: call,
|
|
23
|
+
};
|
|
24
|
+
}
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type burnFuses from './functions/burnFuses';
|
|
|
7
7
|
import type createSubname from './functions/createSubname';
|
|
8
8
|
import type deleteSubname from './functions/deleteSubname';
|
|
9
9
|
import type setName from './functions/setName';
|
|
10
|
+
import type setRecord from './functions/setRecord';
|
|
10
11
|
import type setRecords from './functions/setRecords';
|
|
11
12
|
import type setResolver from './functions/setResolver';
|
|
12
13
|
import type transferName from './functions/transferName';
|
|
@@ -457,6 +458,7 @@ export declare class ENS {
|
|
|
457
458
|
}>;
|
|
458
459
|
setName: WriteFunction<typeof setName>;
|
|
459
460
|
setRecords: WriteFunction<typeof setRecords>;
|
|
461
|
+
setRecord: WriteFunction<typeof setRecord>;
|
|
460
462
|
setResolver: WriteFunction<typeof setResolver>;
|
|
461
463
|
transferName: WriteFunction<typeof transferName>;
|
|
462
464
|
wrapName: WriteFunction<typeof wrapName>;
|
package/dist/esm/index.js
CHANGED
|
@@ -188,6 +188,11 @@ export class ENS {
|
|
|
188
188
|
'contracts',
|
|
189
189
|
]);
|
|
190
190
|
this.setRecords = this.generateWriteFunction('setRecords', ['contracts', 'provider', 'getResolver']);
|
|
191
|
+
this.setRecord = this.generateWriteFunction('setRecord', [
|
|
192
|
+
'contracts',
|
|
193
|
+
'provider',
|
|
194
|
+
'getResolver',
|
|
195
|
+
]);
|
|
191
196
|
this.setResolver = this.generateWriteFunction('setResolver', ['contracts']);
|
|
192
197
|
this.transferName = this.generateWriteFunction('transferName', ['contracts']);
|
|
193
198
|
this.wrapName = this.generateWriteFunction('wrapName', [
|
|
@@ -9,5 +9,8 @@ export declare type RecordOptions = {
|
|
|
9
9
|
coinTypes?: RecordItem[];
|
|
10
10
|
};
|
|
11
11
|
export declare const generateSetAddr: (namehash: string, coinType: string, address: string, resolver: PublicResolver) => string;
|
|
12
|
+
export declare type RecordTypes = 'contentHash' | 'text' | 'addr';
|
|
13
|
+
export declare type RecordInput<T extends RecordTypes> = T extends 'contentHash' ? string : RecordItem;
|
|
14
|
+
export declare function generateSingleRecordCall<T extends RecordTypes>(namehash: string, resolver: PublicResolver, type: T): (record: RecordInput<T>) => string;
|
|
12
15
|
export declare const generateRecordCallArray: (namehash: string, records: RecordOptions, resolver: PublicResolver) => string[];
|
|
13
16
|
export {};
|
|
@@ -12,28 +12,54 @@ export const generateSetAddr = (namehash, coinType, address, resolver) => {
|
|
|
12
12
|
const encodedAddress = coinTypeInstance.decoder(address);
|
|
13
13
|
return resolver?.interface.encodeFunctionData('setAddr(bytes32,uint256,bytes)', [namehash, inputCoinType, encodedAddress]);
|
|
14
14
|
};
|
|
15
|
+
export function generateSingleRecordCall(namehash, resolver, type) {
|
|
16
|
+
if (type === 'contentHash') {
|
|
17
|
+
return (_r) => {
|
|
18
|
+
const record = _r;
|
|
19
|
+
let _contentHash = '';
|
|
20
|
+
if (record !== _contentHash) {
|
|
21
|
+
const encoded = encodeContenthash(record);
|
|
22
|
+
if (encoded.error)
|
|
23
|
+
throw new Error(encoded.error);
|
|
24
|
+
_contentHash = encoded.encoded;
|
|
25
|
+
}
|
|
26
|
+
return resolver.interface.encodeFunctionData('setContenthash', [
|
|
27
|
+
namehash,
|
|
28
|
+
_contentHash,
|
|
29
|
+
]);
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
return (_r) => {
|
|
34
|
+
const record = _r;
|
|
35
|
+
if (type === 'text') {
|
|
36
|
+
return resolver.interface.encodeFunctionData('setText', [
|
|
37
|
+
namehash,
|
|
38
|
+
record.key,
|
|
39
|
+
record.value,
|
|
40
|
+
]);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
return generateSetAddr(namehash, record.key, record.value, resolver);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
15
48
|
export const generateRecordCallArray = (namehash, records, resolver) => {
|
|
16
49
|
const calls = [];
|
|
17
50
|
if (records.contentHash) {
|
|
18
|
-
const
|
|
19
|
-
const data = resolver?.interface.encodeFunctionData('setContenthash', [namehash, contentHash]);
|
|
51
|
+
const data = generateSingleRecordCall(namehash, resolver, 'contentHash')(records.contentHash);
|
|
20
52
|
data && calls.push(data);
|
|
21
53
|
}
|
|
22
54
|
if (records.texts && records.texts.length > 0) {
|
|
23
|
-
records.texts
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
key,
|
|
27
|
-
value,
|
|
28
|
-
]);
|
|
29
|
-
data && calls.push(data);
|
|
30
|
-
});
|
|
55
|
+
records.texts
|
|
56
|
+
.map(generateSingleRecordCall(namehash, resolver, 'text'))
|
|
57
|
+
.forEach((call) => calls.push(call));
|
|
31
58
|
}
|
|
32
59
|
if (records.coinTypes && records.coinTypes.length > 0) {
|
|
33
|
-
records.coinTypes
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
});
|
|
60
|
+
records.coinTypes
|
|
61
|
+
.map(generateSingleRecordCall(namehash, resolver, 'addr'))
|
|
62
|
+
.forEach((call) => calls.push(call));
|
|
37
63
|
}
|
|
38
64
|
return calls;
|
|
39
65
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ensdomains/ensjs",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.9",
|
|
4
4
|
"description": "ENS javascript library for contract interaction",
|
|
5
5
|
"main": "./dist/cjs/index.js",
|
|
6
6
|
"types": "./dist/cjs/index.d.ts",
|
|
@@ -83,5 +83,5 @@
|
|
|
83
83
|
"peerDependencies": {
|
|
84
84
|
"ethers": "*"
|
|
85
85
|
},
|
|
86
|
-
"stableVersion": "3.0.0-alpha.
|
|
86
|
+
"stableVersion": "3.0.0-alpha.8"
|
|
87
87
|
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { ENS } from '..'
|
|
2
|
+
import setup from '../tests/setup'
|
|
3
|
+
import { decodeContenthash } from '../utils/contentHash'
|
|
4
|
+
import { hexEncodeName } from '../utils/hexEncodedName'
|
|
5
|
+
import { namehash } from '../utils/normalise'
|
|
6
|
+
|
|
7
|
+
let ENSInstance: ENS
|
|
8
|
+
let revert: Awaited<ReturnType<typeof setup>>['revert']
|
|
9
|
+
|
|
10
|
+
beforeAll(async () => {
|
|
11
|
+
;({ ENSInstance, revert } = await setup())
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
afterAll(async () => {
|
|
15
|
+
await revert()
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
describe('setRecord', () => {
|
|
19
|
+
it('should allow a text record set', async () => {
|
|
20
|
+
const tx = await ENSInstance.setRecord('parthtejpal.eth', {
|
|
21
|
+
type: 'text',
|
|
22
|
+
record: { key: 'foo', value: 'bar' },
|
|
23
|
+
})
|
|
24
|
+
expect(tx).toBeTruthy()
|
|
25
|
+
await tx.wait()
|
|
26
|
+
|
|
27
|
+
const universalResolver =
|
|
28
|
+
await ENSInstance.contracts!.getUniversalResolver()!
|
|
29
|
+
const publicResolver = await ENSInstance.contracts!.getPublicResolver()!
|
|
30
|
+
const encodedText = await universalResolver.resolve(
|
|
31
|
+
hexEncodeName('parthtejpal.eth'),
|
|
32
|
+
publicResolver.interface.encodeFunctionData('text', [
|
|
33
|
+
namehash('parthtejpal.eth'),
|
|
34
|
+
'foo',
|
|
35
|
+
]),
|
|
36
|
+
)
|
|
37
|
+
const [resultText] = publicResolver.interface.decodeFunctionResult(
|
|
38
|
+
'text',
|
|
39
|
+
encodedText[0],
|
|
40
|
+
)
|
|
41
|
+
expect(resultText).toBe('bar')
|
|
42
|
+
})
|
|
43
|
+
it('should allow an address record set', async () => {
|
|
44
|
+
const tx = await ENSInstance.setRecord('parthtejpal.eth', {
|
|
45
|
+
type: 'addr',
|
|
46
|
+
record: {
|
|
47
|
+
key: 'ETC',
|
|
48
|
+
value: '0x42D63ae25990889E35F215bC95884039Ba354115',
|
|
49
|
+
},
|
|
50
|
+
})
|
|
51
|
+
expect(tx).toBeTruthy()
|
|
52
|
+
await tx.wait()
|
|
53
|
+
|
|
54
|
+
const universalResolver =
|
|
55
|
+
await ENSInstance.contracts!.getUniversalResolver()!
|
|
56
|
+
const publicResolver = await ENSInstance.contracts!.getPublicResolver()!
|
|
57
|
+
const encodedAddr = await universalResolver.resolve(
|
|
58
|
+
hexEncodeName('parthtejpal.eth'),
|
|
59
|
+
publicResolver.interface.encodeFunctionData('addr(bytes32,uint256)', [
|
|
60
|
+
namehash('parthtejpal.eth'),
|
|
61
|
+
'61',
|
|
62
|
+
]),
|
|
63
|
+
)
|
|
64
|
+
const [resultAddr] = publicResolver.interface.decodeFunctionResult(
|
|
65
|
+
'addr(bytes32,uint256)',
|
|
66
|
+
encodedAddr[0],
|
|
67
|
+
)
|
|
68
|
+
expect(resultAddr).toBe(
|
|
69
|
+
'0x42D63ae25990889E35F215bC95884039Ba354115'.toLowerCase(),
|
|
70
|
+
)
|
|
71
|
+
})
|
|
72
|
+
it('should allow a contenthash record set', async () => {
|
|
73
|
+
const tx = await ENSInstance.setRecord('parthtejpal.eth', {
|
|
74
|
+
type: 'contentHash',
|
|
75
|
+
record:
|
|
76
|
+
'ipns://k51qzi5uqu5dgox2z23r6e99oqency055a6xt92xzmyvpz8mwz5ycjavm0u150',
|
|
77
|
+
})
|
|
78
|
+
expect(tx).toBeTruthy()
|
|
79
|
+
await tx.wait()
|
|
80
|
+
|
|
81
|
+
const universalResolver =
|
|
82
|
+
await ENSInstance.contracts!.getUniversalResolver()!
|
|
83
|
+
const publicResolver = await ENSInstance.contracts!.getPublicResolver()!
|
|
84
|
+
const encodedContent = await universalResolver.resolve(
|
|
85
|
+
hexEncodeName('parthtejpal.eth'),
|
|
86
|
+
publicResolver.interface.encodeFunctionData('contenthash', [
|
|
87
|
+
namehash('parthtejpal.eth'),
|
|
88
|
+
]),
|
|
89
|
+
)
|
|
90
|
+
const [resultContent] = publicResolver.interface.decodeFunctionResult(
|
|
91
|
+
'contenthash',
|
|
92
|
+
encodedContent[0],
|
|
93
|
+
)
|
|
94
|
+
const content = decodeContenthash(resultContent)
|
|
95
|
+
expect(content.decoded).toBe(
|
|
96
|
+
'k51qzi5uqu5dgox2z23r6e99oqency055a6xt92xzmyvpz8mwz5ycjavm0u150',
|
|
97
|
+
)
|
|
98
|
+
expect(content.protocolType).toBe('ipns')
|
|
99
|
+
})
|
|
100
|
+
})
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { ENSArgs } from '..'
|
|
2
|
+
import { namehash } from '../utils/normalise'
|
|
3
|
+
import {
|
|
4
|
+
generateSingleRecordCall,
|
|
5
|
+
RecordInput,
|
|
6
|
+
RecordTypes,
|
|
7
|
+
} from '../utils/recordHelpers'
|
|
8
|
+
|
|
9
|
+
type BaseInput = {
|
|
10
|
+
resolverAddress?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type ContentHashInput = {
|
|
14
|
+
record: RecordInput<'contentHash'>
|
|
15
|
+
type: 'contentHash'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type AddrOrTextInput = {
|
|
19
|
+
record: RecordInput<'addr' | 'text'>
|
|
20
|
+
type: 'addr' | 'text'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default async function <T extends RecordTypes>(
|
|
24
|
+
{
|
|
25
|
+
contracts,
|
|
26
|
+
provider,
|
|
27
|
+
getResolver,
|
|
28
|
+
signer,
|
|
29
|
+
}: ENSArgs<'contracts' | 'provider' | 'getResolver' | 'signer'>,
|
|
30
|
+
name: string,
|
|
31
|
+
{
|
|
32
|
+
record,
|
|
33
|
+
type,
|
|
34
|
+
resolverAddress,
|
|
35
|
+
}: BaseInput & (ContentHashInput | AddrOrTextInput),
|
|
36
|
+
) {
|
|
37
|
+
if (!name.includes('.')) {
|
|
38
|
+
throw new Error('Input is not an ENS name')
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let resolverToUse: string
|
|
42
|
+
if (resolverAddress) {
|
|
43
|
+
resolverToUse = resolverAddress
|
|
44
|
+
} else {
|
|
45
|
+
resolverToUse = await getResolver(name)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!resolverToUse) {
|
|
49
|
+
throw new Error('No resolver found for input address')
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const resolver = (
|
|
53
|
+
await contracts?.getPublicResolver(provider, resolverToUse)
|
|
54
|
+
)?.connect(signer)!
|
|
55
|
+
const hash = namehash(name)
|
|
56
|
+
|
|
57
|
+
const call = generateSingleRecordCall(hash, resolver, type)(record)
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
to: resolver.address,
|
|
61
|
+
data: call,
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -32,7 +32,7 @@ describe('setRecords', () => {
|
|
|
32
32
|
const publicResolver = await ENSInstance.contracts!.getPublicResolver()!
|
|
33
33
|
const encodedText = await universalResolver.resolve(
|
|
34
34
|
hexEncodeName('parthtejpal.eth'),
|
|
35
|
-
publicResolver.interface.encodeFunctionData('text
|
|
35
|
+
publicResolver.interface.encodeFunctionData('text', [
|
|
36
36
|
namehash('parthtejpal.eth'),
|
|
37
37
|
'foo',
|
|
38
38
|
]),
|
|
@@ -45,7 +45,7 @@ describe('setRecords', () => {
|
|
|
45
45
|
]),
|
|
46
46
|
)
|
|
47
47
|
const [resultText] = publicResolver.interface.decodeFunctionResult(
|
|
48
|
-
'text
|
|
48
|
+
'text',
|
|
49
49
|
encodedText[0],
|
|
50
50
|
)
|
|
51
51
|
const [resultAddr] = publicResolver.interface.decodeFunctionResult(
|
package/src/index.ts
CHANGED
|
@@ -37,6 +37,7 @@ import type {
|
|
|
37
37
|
} from './functions/getSpecificRecord'
|
|
38
38
|
import type getSubnames from './functions/getSubnames'
|
|
39
39
|
import type setName from './functions/setName'
|
|
40
|
+
import type setRecord from './functions/setRecord'
|
|
40
41
|
import type setRecords from './functions/setRecords'
|
|
41
42
|
import type setResolver from './functions/setResolver'
|
|
42
43
|
import type transferName from './functions/transferName'
|
|
@@ -543,6 +544,12 @@ export class ENS {
|
|
|
543
544
|
['contracts', 'provider', 'getResolver'],
|
|
544
545
|
)
|
|
545
546
|
|
|
547
|
+
public setRecord = this.generateWriteFunction<typeof setRecord>('setRecord', [
|
|
548
|
+
'contracts',
|
|
549
|
+
'provider',
|
|
550
|
+
'getResolver',
|
|
551
|
+
])
|
|
552
|
+
|
|
546
553
|
public setResolver = this.generateWriteFunction<typeof setResolver>(
|
|
547
554
|
'setResolver',
|
|
548
555
|
['contracts'],
|
|
@@ -33,6 +33,47 @@ export const generateSetAddr = (
|
|
|
33
33
|
)
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
export type RecordTypes = 'contentHash' | 'text' | 'addr'
|
|
37
|
+
|
|
38
|
+
export type RecordInput<T extends RecordTypes> = T extends 'contentHash'
|
|
39
|
+
? string
|
|
40
|
+
: RecordItem
|
|
41
|
+
|
|
42
|
+
export function generateSingleRecordCall<T extends RecordTypes>(
|
|
43
|
+
namehash: string,
|
|
44
|
+
resolver: PublicResolver,
|
|
45
|
+
type: T,
|
|
46
|
+
): (record: RecordInput<T>) => string {
|
|
47
|
+
if (type === 'contentHash') {
|
|
48
|
+
return (_r: RecordInput<T>) => {
|
|
49
|
+
const record = _r as string
|
|
50
|
+
let _contentHash = ''
|
|
51
|
+
if (record !== _contentHash) {
|
|
52
|
+
const encoded = encodeContenthash(record)
|
|
53
|
+
if (encoded.error) throw new Error(encoded.error)
|
|
54
|
+
_contentHash = encoded.encoded as string
|
|
55
|
+
}
|
|
56
|
+
return resolver.interface.encodeFunctionData('setContenthash', [
|
|
57
|
+
namehash,
|
|
58
|
+
_contentHash,
|
|
59
|
+
])
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
return (_r: RecordInput<T>) => {
|
|
63
|
+
const record = _r as RecordItem
|
|
64
|
+
if (type === 'text') {
|
|
65
|
+
return resolver.interface.encodeFunctionData('setText', [
|
|
66
|
+
namehash,
|
|
67
|
+
record.key,
|
|
68
|
+
record.value,
|
|
69
|
+
])
|
|
70
|
+
} else {
|
|
71
|
+
return generateSetAddr(namehash, record.key, record.value, resolver)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
36
77
|
export const generateRecordCallArray = (
|
|
37
78
|
namehash: string,
|
|
38
79
|
records: RecordOptions,
|
|
@@ -41,31 +82,24 @@ export const generateRecordCallArray = (
|
|
|
41
82
|
const calls: string[] = []
|
|
42
83
|
|
|
43
84
|
if (records.contentHash) {
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
'
|
|
48
|
-
|
|
49
|
-
)
|
|
85
|
+
const data = generateSingleRecordCall(
|
|
86
|
+
namehash,
|
|
87
|
+
resolver,
|
|
88
|
+
'contentHash',
|
|
89
|
+
)(records.contentHash)
|
|
50
90
|
data && calls.push(data)
|
|
51
91
|
}
|
|
52
92
|
|
|
53
93
|
if (records.texts && records.texts.length > 0) {
|
|
54
|
-
records.texts
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
key,
|
|
58
|
-
value,
|
|
59
|
-
])
|
|
60
|
-
data && calls.push(data)
|
|
61
|
-
})
|
|
94
|
+
records.texts
|
|
95
|
+
.map(generateSingleRecordCall(namehash, resolver, 'text'))
|
|
96
|
+
.forEach((call) => calls.push(call))
|
|
62
97
|
}
|
|
63
98
|
|
|
64
99
|
if (records.coinTypes && records.coinTypes.length > 0) {
|
|
65
|
-
records.coinTypes
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
})
|
|
100
|
+
records.coinTypes
|
|
101
|
+
.map(generateSingleRecordCall(namehash, resolver, 'addr'))
|
|
102
|
+
.forEach((call) => calls.push(call))
|
|
69
103
|
}
|
|
70
104
|
|
|
71
105
|
return calls
|