Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ens setAddress support #6834

Merged
merged 9 commits into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/web3-eth-ens/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,7 @@ Documentation:
- Dependencies updated

## [Unreleased]

### Added

- Added function `setAddress` in ENS and Resolver classes (#5956)
18 changes: 18 additions & 0 deletions packages/web3-eth-ens/src/abi/ens/PublicResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -590,4 +590,22 @@ export const PublicResolverAbi = [
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{
internalType: 'bytes32',
name: 'node',
type: 'bytes32',
},
{
internalType: 'address',
name: 'a',
type: 'address',
},
],
name: 'setAddr',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
] as const;
23 changes: 22 additions & 1 deletion packages/web3-eth-ens/src/ens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,18 @@ along with web3.js. If not, see <http://www.gnu.org/licenses/>.
*/

import { Web3Context, Web3ContextObject } from 'web3-core';
import { ENSNetworkNotSyncedError, ENSUnsupportedNetworkError } from 'web3-errors';
import { ENSNetworkNotSyncedError, ENSUnsupportedNetworkError, RevertInstructionError } from 'web3-errors';
import { isSyncing } from 'web3-eth';
import { Contract } from 'web3-eth-contract';
import { getId } from 'web3-net';
import {
Address,
DEFAULT_RETURN_FORMAT,
EthExecutionAPI,
FMT_NUMBER,
PayableCallOptions,
SupportedProviders,
TransactionReceipt,
Web3NetAPI,
} from 'web3-types';
import { PublicResolverAbi } from './abi/ens/PublicResolver.js';
Expand Down Expand Up @@ -258,4 +261,22 @@ export class ENS extends Web3Context<EthExecutionAPI & Web3NetAPI> {
public get events() {
return this._registry.events;
}

/**
* Sets the address of an ENS name in his resolver.
* @param name - The ENS name
* @param address - The address to set
* @param txConfig - (Optional) The transaction config
* @returns - The transaction receipt
* ```ts
* const receipt = await ens.setAddress('web3js.eth','0xe2597eb05cf9a87eb1309e86750c903ec38e527e');
*```
*/
public async setAddress(
name: string,
address: Address,
txConfig: PayableCallOptions
): Promise<TransactionReceipt | RevertInstructionError> {
return this._resolver.setAddress(name, address, txConfig);
}
}
15 changes: 15 additions & 0 deletions packages/web3-eth-ens/src/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ import { ResolverMethodMissingError } from 'web3-errors';
import { Contract } from 'web3-eth-contract';
import { isNullish, sha3 } from 'web3-utils';
import { isHexStrict } from 'web3-validator';
import { Address, PayableCallOptions } from 'web3-types';
import { PublicResolverAbi } from './abi/ens/PublicResolver.js';
import { interfaceIds, methodsInInterface } from './config.js';
import { Registry } from './registry.js';
import { namehash } from './utils.js';


// Default public resolver
// https://github.com/ensdomains/resolvers/blob/master/contracts/PublicResolver.sol

Expand Down Expand Up @@ -102,4 +104,17 @@ export class Resolver {

return resolverContract.methods.contenthash(namehash(ENSName)).call();
}

public async setAddress(
ENSName: string,
address: Address,
txConfig: PayableCallOptions,
) {
const resolverContract = await this.getResolverContractAdapter(ENSName);
await this.checkInterfaceSupport(resolverContract, methodsInInterface.setAddr);

return resolverContract.methods
.setAddr(namehash(ENSName), address)
.send(txConfig);
}
}
2 changes: 2 additions & 0 deletions packages/web3-eth-ens/test/unit/constructor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ describe('ens', () => {
const registry = new Registry(object);
const resolver = new Resolver(registry);

expect(resolver.setAddress).toBeDefined();
expect(resolver.getAddress).toBeDefined();
expect(resolver.checkInterfaceSupport).toBeDefined();
expect(resolver.supportsInterface).toBeDefined();
Expand All @@ -52,6 +53,7 @@ describe('ens', () => {
const ens = new ENS(registryAddresses.main, 'http://127.0.0.1:8545');

expect(ens.getResolver).toBeDefined();
expect(ens.setAddress).toBeDefined();
expect(ens.recordExists).toBeDefined();
expect(ens.getTTL).toBeDefined();
expect(ens.getOwner).toBeDefined();
Expand Down
16 changes: 16 additions & 0 deletions packages/web3-eth-ens/test/unit/ens.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,22 @@ describe('ens', () => {
});

describe('addr', () => {
it('setAddr valid', async () => {
// eslint-disable-next-line @typescript-eslint/no-empty-function
const send = jest.spyOn({ send: () => {} }, 'send');

const setAddressMock = jest.spyOn(ens['_resolver'], 'setAddress').mockReturnValue({
send,
} as unknown as Web3PromiEvent<any, any>);

const sendOptions = { from: mockAddress };
await ens.setAddress(ENS_NAME, mockAddress, sendOptions);
expect(setAddressMock).toHaveBeenCalledWith(
ENS_NAME,
mockAddress,
sendOptions,
);
});
it('getAddress', async () => {
// eslint-disable-next-line @typescript-eslint/no-empty-function
const call = jest.spyOn({ call: () => {} }, 'call');
Expand Down
23 changes: 23 additions & 0 deletions packages/web3-eth-ens/test/unit/resolver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,29 @@ describe('resolver', () => {
);
});
describe('addr', () => {
it('setAddr valid', async () => {
const checkInteraface = jest.spyOn(resolver, 'checkInterfaceSupport');

const setAddrMock = jest.spyOn(contract.methods, 'setAddr').mockReturnValue({
send: jest.fn(),
} as unknown as NonPayableMethodObject<any, any>);

jest.spyOn(contract.methods, 'supportsInterface').mockReturnValue({
call: jest.fn().mockReturnValue(true),
} as unknown as NonPayableMethodObject<any, any>);

// todo when moving this mock in beforeAll, jest calls the actual implementation, how to fix that
// I use this in many places
jest.spyOn(registry, 'getResolver').mockImplementation(async () => {
return new Promise(resolve => {
resolve(contract);
});
});

await resolver.setAddress(ENS_NAME, mockAddress, { from: mockAddress });
expect(checkInteraface).toHaveBeenCalled();
expect(setAddrMock).toHaveBeenCalledWith(namehash(ENS_NAME), mockAddress);
});
it('getAddress', async () => {
const supportsInterfaceMock = jest
.spyOn(contract.methods, 'supportsInterface')
Expand Down
Loading