Skip to content

Commit

Permalink
Update the controller to support referrals and bulk renewals (ensdoma…
Browse files Browse the repository at this point in the history
…ins#50)

* Support bulk renewal

* Add referral support to the registrar controller

* Changes in response to review
  • Loading branch information
Arachnid authored Apr 3, 2020
1 parent b2ccd0e commit 7d2a684
Show file tree
Hide file tree
Showing 5 changed files with 278 additions and 142 deletions.
14 changes: 14 additions & 0 deletions contracts/ACL.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
pragma solidity ^0.5.0;

import "openzeppelin-solidity/contracts/ownership/Ownable.sol";

contract ACL is Ownable {
event ACLChanged(address indexed addr, bool access);

mapping(address=>bool) public entries;

function setAccess(address addr, bool access) public onlyOwner {
entries[addr] = access;
emit ACLChanged(addr, access);
}
}
95 changes: 79 additions & 16 deletions contracts/ETHRegistrarController.sol
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
pragma solidity ^0.5.0;
pragma experimental ABIEncoderV2;

import "./ACL.sol";
import "./PriceOracle.sol";
import "./BaseRegistrar.sol";
import "./StringUtils.sol";
import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
import "openzeppelin-solidity/contracts/math/SafeMath.sol";
import "@ensdomains/resolver/contracts/Resolver.sol";

/**
* @dev A registrar controller for registering and renewing names at fixed cost.
*/
contract ETHRegistrarController is Ownable {
using StringUtils for *;
using SafeMath for uint256;

uint constant public MIN_REGISTRATION_DURATION = 28 days;

Expand All @@ -23,30 +27,39 @@ contract ETHRegistrarController is Ownable {
keccak256("register(string,address,uint256,bytes32)") ^
keccak256("renew(string,uint256)")
);

bytes4 constant private COMMITMENT_WITH_CONFIG_CONTROLLER_ID = bytes4(
keccak256("registerWithConfig(string,address,uint256,bytes32,address,address)") ^
keccak256("makeCommitmentWithConfig(string,address,bytes32,address,address)")
);
bytes4 constant public BULK_RENEWAL_ID = bytes4(
keccak256("rentPrice(string[],uint)") ^
keccak256("renewAll(string[],uint")
);

BaseRegistrar base;
PriceOracle prices;
// Base Registrar contract
BaseRegistrar public base;
// Price oracle contract
PriceOracle public prices;
// Minimum and maximum commitment ages, in seconds.
uint public minCommitmentAge;
uint public maxCommitmentAge;

// Referral fee, in 1/1000ths
uint public referralFeeMillis = 0;
// A map of commitment values to when they were committed to
mapping(bytes32=>uint) public commitments;
// An access-control list for referrers
ACL public referrers;

event NameRegistered(string name, bytes32 indexed label, address indexed owner, uint cost, uint expires);
event NameRenewed(string name, bytes32 indexed label, uint cost, uint expires);
event ReferralFeeSent(address indexed referrer, uint amount);
event NewPriceOracle(address indexed oracle);

constructor(BaseRegistrar _base, PriceOracle _prices, uint _minCommitmentAge, uint _maxCommitmentAge) public {
require(_maxCommitmentAge > _minCommitmentAge);

constructor(BaseRegistrar _base, PriceOracle _prices, uint _minCommitmentAge, uint _maxCommitmentAge, ACL _referrers) public {
base = _base;
prices = _prices;
minCommitmentAge = _minCommitmentAge;
maxCommitmentAge = _maxCommitmentAge;
setCommitmentAges(_minCommitmentAge, _maxCommitmentAge);
setReferrersACL(_referrers);
}

function rentPrice(string memory name, uint duration) view public returns(uint) {
Expand Down Expand Up @@ -82,10 +95,14 @@ contract ETHRegistrarController is Ownable {
}

function register(string calldata name, address owner, uint duration, bytes32 secret) external payable {
registerWithConfig(name, owner, duration, secret, address(0), address(0));
registerWithReferrer(name, owner, duration, secret, address(0), address(0), address(0));
}

function registerWithConfig(string memory name, address owner, uint duration, bytes32 secret, address resolver, address addr) public payable {
registerWithReferrer(name, owner, duration, secret, address(0), resolver, addr);
}

function registerWithReferrer(string memory name, address owner, uint duration, bytes32 secret, address payable referrer, address resolver, address addr) public payable {
bytes32 commitment = makeCommitmentWithConfig(name, owner, secret, resolver, addr);
uint cost = _consumeCommitment(name, duration, commitment);

Expand Down Expand Up @@ -123,20 +140,38 @@ contract ETHRegistrarController is Ownable {
if(msg.value > cost) {
msg.sender.transfer(msg.value - cost);
}

_sendReferralFee(referrer, cost);
}

function renew(string calldata name, uint duration) external payable {
uint cost = rentPrice(name, duration);
require(msg.value >= cost);
renewWithReferrer(name, duration, address(0));
}

bytes32 label = keccak256(bytes(name));
uint expires = base.renew(uint256(label), duration);
function renewWithReferrer(string memory name, uint duration, address payable referrer) public payable {
uint cost = _doRenew(name, duration);
require(msg.value >= cost);

// Refund any extra
if(msg.value > cost) {
msg.sender.transfer(msg.value - cost);
}

emit NameRenewed(name, label, cost, expires);
_sendReferralFee(referrer, cost);
}
function renewAll(string[] calldata names, uint duration, address payable referrer) external payable {
uint totalCost = 0;
for(uint i = 0; i < names.length; i++) {
totalCost += _doRenew(names[i], duration);
}
require(totalCost <= msg.value);

// Refund any extra
if(totalCost < msg.value) {
msg.sender.transfer(msg.value - totalCost);
}

_sendReferralFee(referrer, totalCost);
}

function setPriceOracle(PriceOracle _prices) public onlyOwner {
Expand All @@ -145,18 +180,29 @@ contract ETHRegistrarController is Ownable {
}

function setCommitmentAges(uint _minCommitmentAge, uint _maxCommitmentAge) public onlyOwner {
require(_minCommitmentAge < _maxCommitmentAge);
minCommitmentAge = _minCommitmentAge;
maxCommitmentAge = _maxCommitmentAge;
}

function setReferralFee(uint _referralFeeMillis) public onlyOwner {
require(_referralFeeMillis < 1000);
referralFeeMillis = _referralFeeMillis;
}

function setReferrersACL(ACL _referrers) public onlyOwner {
referrers = _referrers;
}

function withdraw() public onlyOwner {
msg.sender.transfer(address(this).balance);
}

function supportsInterface(bytes4 interfaceID) external pure returns (bool) {
return interfaceID == INTERFACE_META_ID ||
interfaceID == COMMITMENT_CONTROLLER_ID ||
interfaceID == COMMITMENT_WITH_CONFIG_CONTROLLER_ID;
interfaceID == COMMITMENT_WITH_CONFIG_CONTROLLER_ID ||
interfaceID == BULK_RENEWAL_ID;
}

function _consumeCommitment(string memory name, uint duration, bytes32 commitment) internal returns (uint256) {
Expand All @@ -175,4 +221,21 @@ contract ETHRegistrarController is Ownable {

return cost;
}

function _doRenew(string memory name, uint duration) internal returns(uint cost) {
uint cost = rentPrice(name, duration);
bytes32 label = keccak256(bytes(name));
uint expires = base.renew(uint256(label), duration);
emit NameRenewed(name, label, cost, expires);
return cost;
}

function _sendReferralFee(address payable referrer, uint cost) internal {
if(referrer != address(0) && referralFeeMillis > 0 && address(referrers) != address(0)) {
require(referrers.entries(referrer));
uint referralFee = (cost * referralFeeMillis) / 1000;
referrer.transfer(referralFee);
emit ReferralFeeSent(referrer, referralFee);
}
}
}
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
"eth-ens-namehash": "^2.0.8",
"openzeppelin-solidity": "2.1.3",
"solium": "^1.2.3",
"truffle": "^5.0.5",
"truffle": "^5.1.19",
"truffle-flattener": "^1.4.2",
"web3-utils": "^1.0.0-beta.48",
"@ensdomains/test-utils": "^1.3.0",
"truffle-flattener": "^1.4.2"
"@ensdomains/test-utils": "^1.3.0"
}
}
Loading

0 comments on commit 7d2a684

Please sign in to comment.