Augur's Delegator, resolving its implementation by name through the Augur controller on every call.
Historical Significance
Reading the three families together shows the same compiled artifact being deployed under different names within one system. Because the code is byte for byte identical, all of them can be attributed from the bytecode alone, and the only thing distinguishing a given delegator is the constructor arguments recorded in its creation transaction.
Key Facts
Description
Identical bytecode to its sibling families, deployed with different constructor arguments. The fallback returns quietly when the lookup key is unset, and otherwise asks the controller for the address registered under that key, delegatecalls it with the original calldata, and returns whatever comes back.
setController is restricted to the current controller, and both getController and controllerLookupName are readable so the wiring can be inspected from outside.
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)
}
}
}
}