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

Delegatable resolver registrar #295

Closed
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
41 changes: 41 additions & 0 deletions contracts/resolvers/DelegatableResolverRegistrar.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;
import "./IDelegatableResolverRegistrar.sol";
import "./DelegatableResolver.sol";
import "@openzeppelin/contracts/utils/introspection/ERC165.sol";

/**
* A delegated resolver registrar that allows anyone to register subname
*/
contract DelegatableResolverRegistrar is IDelegatableResolverRegistrar, ERC165 {
DelegatableResolver public resolver;

constructor(DelegatableResolver _resolver) {
resolver = _resolver;
}

/**
* @dev Approve an operator to be able to updated records on a node.
* @param name The encoded subname
* @param operator The address to approve
*/

function register(bytes memory name, address operator) external {
(bytes32 node, bool authorized) = resolver.getAuthorisedNode(
name,
0,
operator
);
if (authorized == false) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this does what you want. It won't prevent duplicate subdomains, because it only prevents the same person being issued the same name twice. I think instead you need to keep a mapping of issued names, or modify the DelegatableResolver to keep track of issued names.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's my intention. I only added it to avoiding existing operator to waste the gas. Not sure restricting multiple subname operators at registrar level makes sense because the owner of the resolver contract can always approve multiple operators. If we want to change the delegatable resolver to only have single owner per subname, I think I need to add registrar feature directly in the resolver as @jefflau initially suggested.

resolver.approve(name, operator, true);
}
}

function supportsInterface(
bytes4 interfaceId
) public view override returns (bool) {
return
interfaceId == type(IDelegatableResolverRegistrar).interfaceId ||
super.supportsInterface(interfaceId);
}
}
6 changes: 6 additions & 0 deletions contracts/resolvers/IDelegatableResolverRegistrar.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

interface IDelegatableResolverRegistrar {
function register(bytes memory name, address operator) external;
}
50 changes: 50 additions & 0 deletions test/resolvers/TestDelegatableResolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ const DelegatableResolverFactory = artifacts.require(
'DelegatableResolverFactory.sol',
)
const DelegatableResolver = artifacts.require('DelegatableResolver.sol')
const DelegatableResolverRegistrar = artifacts.require(
'DelegatableResolverRegistrar.sol',
)
const { encodeName, namehash } = require('../test-utils/ens')
const { exceptions } = require('../test-utils')
const { expect } = require('chai')
Expand Down Expand Up @@ -204,4 +207,51 @@ contract('DelegatableResolver', function (accounts) {
assert.equal(args.approved, true)
})
})

describe('DelegatableResolverRegistrar', async () => {
let registrar
beforeEach(async () => {
registrar = await DelegatableResolverRegistrar.new(resolver.address)
})
it('approves users to update record for the given subname', async () => {
const basename = encodeName('')
const name = `foo.bar.eth`
const encodedsubname = encodeName(name)
const encodedsubnode = namehash(name)

const operator2Resolver = await (
await ethers.getContractFactory('DelegatableResolver')
)
.attach(resolver.address)
.connect(operator2Signer)

await exceptions.expectFailure(
operator2Resolver['setAddr(bytes32,address)'](
encodedsubnode,
operator2,
),
)

await resolver.approve(basename, registrar.address, true)
await registrar.register(encodedsubname, operator2)
assert.equal(
(await resolver.getAuthorisedNode(encodedsubname, 0, operator2))[1],
true,
)

await operator2Resolver['setAddr(bytes32,address)'](
encodedsubnode,
operator2,
)
assert.equal(
await operator2Resolver['addr(bytes32)'](encodedsubnode),
operator2,
)
})

it('should support interface', async () => {
expect(await registrar.supportsInterface('0x01ffc9a7')).to.equal(true) // ERC-165 support
expect(await registrar.supportsInterface('0xa99e7e29')).to.equal(true) // IDelegatableResolverRegistrar
})
})
})
Loading