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

Feature/ Parent-controlled avatar resolver #244

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
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
63 changes: 63 additions & 0 deletions contracts/resolvers/ParentAvatarResolver.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.17 <0.9.0;

import {ENS} from "../registry/ENS.sol";
import {INameWrapper} from "../wrapper/INameWrapper.sol";
import {PublicResolver} from "./PublicResolver.sol";

error AvatarCannotBeSetByOwner();

/**
*
* This resolver allows the owner of a node to set the avatar of a child node.
*
*/
contract ParentAvatarResolver is PublicResolver {
constructor(
ENS _ens,
INameWrapper wrapperAddress,
address _trustedETHController,
address _trustedReverseRegistrar
)
PublicResolver(
_ens,
wrapperAddress,
_trustedETHController,
_trustedReverseRegistrar
)
{}

function setText(
bytes32 node,
string calldata key,
string calldata value
) external override authorised(node) {
// check if key is avatar
if (
keccak256(abi.encodePacked(key)) ==
keccak256(abi.encodePacked("avatar"))
) {
revert AvatarCannotBeSetByOwner();
}

_setText(node, key, value);
}

function setAvatar(
bytes32 parentNode,
bytes32 labelhash,
string calldata value
) external authorised(parentNode) {
bytes32 node = keccak256(abi.encodePacked(parentNode, labelhash));
_setText(node, "avatar", value);
}

function _setText(
bytes32 node,
string memory key,
string calldata value
) internal {
versionable_texts[recordVersions[node]][node][key] = value;
emit TextChanged(node, key, key, value);
}
}
6 changes: 5 additions & 1 deletion contracts/resolvers/ResolverBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@ pragma solidity >=0.8.4;
import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import "./profiles/IVersionableResolver.sol";

error NotAuthorised();

abstract contract ResolverBase is ERC165, IVersionableResolver {
mapping(bytes32 => uint64) public recordVersions;

function isAuthorised(bytes32 node) internal view virtual returns (bool);

modifier authorised(bytes32 node) {
require(isAuthorised(node));
if (!isAuthorised(node)) {
revert NotAuthorised();
}
_;
}

Expand Down
86 changes: 86 additions & 0 deletions test/resolvers/TestParentAvatarResolver.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
const ENS = artifacts.require('./registry/ENSRegistry.sol')
const NameWrapper = artifacts.require('DummyNameWrapper.sol')
const { deploy } = require('../test-utils/contracts')
const { labelhash } = require('../test-utils/ens')
const { EMPTY_BYTES32: ROOT_NODE } = require('../test-utils/constants')

const { expect } = require('chai')
const namehash = require('eth-ens-namehash')

contract('Parent Avatar Resolver', function (accounts) {
let node
let ens, resolver, resolver2, nameWrapper
let account
let signers

beforeEach(async () => {
signers = await ethers.getSigners()
account = await signers[0].getAddress()
node = namehash.hash('eth')
ens = await ENS.new()
nameWrapper = await NameWrapper.new()

//setup reverse registrar

const ReverseRegistrar = await deploy('ReverseRegistrar', ens.address)

await ens.setSubnodeOwner(ROOT_NODE, labelhash('reverse'), account)
await ens.setSubnodeOwner(
namehash.hash('reverse'),
labelhash('addr'),
ReverseRegistrar.address,
)

resolver = await deploy(
'ParentAvatarResolver',
ens.address,
nameWrapper.address,
accounts[9], // trusted contract
ReverseRegistrar.address, //ReverseRegistrar.address,
)

resolver2 = resolver.connect(signers[1])

await ReverseRegistrar.setDefaultResolver(resolver.address)

await ens.setSubnodeOwner('0x0', labelhash('eth'), accounts[0], {
from: accounts[0],
})
})

describe('setText()', () => {
it('should set text', async () => {
await resolver.setText(node, 'url', 'https://example.com', {
from: accounts[0],
})
const result = await resolver.text(node, 'url')
expect(result).to.equal('https://example.com')
})

it('should not be able to set avatar', async () => {
await expect(
resolver.setText(node, 'avatar', 'https://example.com', {
from: accounts[0],
}),
).to.be.revertedWith('AvatarCannotBeSetByOwner()')
})
})

describe('setAvatar()', () => {
it('should be able to set avatar as the parentOwner', async () => {
resolver.setAvatar(ROOT_NODE, labelhash('eth'), 'https://example.com', {
from: accounts[0],
})

const result = await resolver.text(node, 'avatar')
expect(result).to.equal('https://example.com')
})

it('should not able to set avatar if not the parent Owner', async () => {
ens.setSubnodeOwner('0x0', labelhash('eth'), accounts[1])
await expect(
resolver2.setAvatar(ROOT_NODE, labelhash('eth'), 'https://example.com'),
).to.be.revertedWith('NotAuthorised()')
})
})
})