Augur's Delegator, resolving its implementation by name through the Augur controller on every call.
Historical Significance
Augur addressed its components by name rather than by address, and every deployed piece sat behind one of these. Three separate families share this exact bytecode, differing only in the constructor arguments that set the controller and key, which is why they appear as distinct deployments of a single design.
Key Facts
Description
The contract holds a controller and a lookup key. Its fallback returns without doing anything when the key is unset, then otherwise calls lookup on the controller with that key, delegatecalls the returned address with the original calldata, and passes the return data straight back.
getController and setController come from Augur's Controlled base, with setController callable only by the current controller. controllerLookupName exposes the key, so the component a delegator stands in for can be read directly from the chain.
Source Verified
Byzantium Era
First Metropolis hard fork. Added zk-SNARK precompiles, REVERT opcode, and staticcall.
Bytecode Overview
Verified Source Available
This contract has verified source code.
View Verification ProofShow source code (Solidity)
// Submitted by EthereumHistory (ethereumhistory.com)
pragma solidity 0.4.20;
contract IController {
function assertIsWhitelisted(address _target) public view returns(bool);
function lookup(bytes32 _key) public view returns(address);
function stopInEmergency() public view returns(bool);
function onlyInEmergency() public view returns(bool);
function getTimestamp() public view returns (uint256);
function emergencyStop() public returns (bool);
}
contract IControlled {
function getController() public view returns (IController);
function setController(IController _controller) public returns(bool);
}
contract Controlled is IControlled {
IController internal controller;
modifier onlyWhitelistedCallers {
require(controller.assertIsWhitelisted(msg.sender));
_;
}
modifier onlyCaller(bytes32 _key) {
require(msg.sender == controller.lookup(_key));
_;
}
modifier onlyControllerCaller {
require(IController(msg.sender) == controller);
_;
}
modifier onlyInGoodTimes {
require(controller.stopInEmergency());
_;
}
modifier onlyInBadTimes {
require(controller.onlyInEmergency());
_;
}
function Controlled() public {
controller = IController(msg.sender);
}
function getController() public view returns(IController) {
return controller;
}
function setController(IController _controller) public onlyControllerCaller returns(bool) {
controller = _controller;
return true;
}
}
contract DelegationTarget is Controlled {
bytes32 public controllerLookupName;
}
contract Delegator is DelegationTarget {
function Delegator(IController _controller, bytes32 _controllerLookupName) public {
controller = _controller;
controllerLookupName = _controllerLookupName;
}
function() external payable {
// Do nothing if we haven't properly set up the delegator to delegate calls
if (controllerLookupName == 0) {
return;
}
// Get the delegation target contract
address _target = controller.lookup(controllerLookupName);
assembly {
//0x40 is the address where the next free memory slot is stored in Solidity
let _calldataMemoryOffset := mload(0x40)
// new "memory end" including padding. The bitwise operations here ensure we get rounded up to the nearest 32 byte boundary
let _size := and(add(calldatasize, 0x1f), not(0x1f))
// Update the pointer at 0x40 to point at new free memory location so any theoretical allocation doesn't stomp our memory in this call
mstore(0x40, add(_calldataMemoryOffset, _size))
// Copy method signature and parameters of this call into memory
calldatacopy(_calldataMemoryOffset, 0x0, calldatasize)
// Call the actual method via delegation
let _retval := delegatecall(gas, _target, _calldataMemoryOffset, calldatasize, 0, 0)
switch _retval
case 0 {
// 0 == it threw, so we revert
revert(0,0)
} default {
// If the call succeeded return the return data from the delegate call
let _returndataMemoryOffset := mload(0x40)
// Update the pointer at 0x40 again to point at new free memory location so any theoretical allocation doesn't stomp our memory in this call
mstore(0x40, add(_returndataMemoryOffset, returndatasize))
returndatacopy(_returndataMemoryOffset, 0x0, returndatasize)
return(_returndataMemoryOffset, returndatasize)
}
}
}
}