Augur v1 share token shell: a delegator that looks up its logic contract by name in the Augur Controller on every call and delegatecalls into it.
Historical Significance
Augur was the first large prediction market on Ethereum, and the Delegator is how it made per-market contracts affordable in 2018. Every outcome of every market needed its own share token, and deploying a full ERC20 for each would have cost far more gas than a shell pointing at shared code.
The design is an early registry-resolved proxy, predating the ERC-1967 storage slot convention and the ERC-1167 minimal proxy. The upgrade path runs through a name lookup in a central Controller rather than through anything stored in the proxy itself, which made the Controller's owner the single point that could change the code behind every market.
Token Information
Key Facts
Description
Augur v1 did not deploy full contracts for every market object. It deployed Delegators, small shells that hold their own storage but no business logic of their own. Each one stores two things: the address of the Augur Controller and a bytes32 lookup key. This one was created by Augur's ShareTokenFactory in July 2018, days after the v1 mainnet launch, and its key reads "ShareToken".
The fallback function is the whole mechanism. If the lookup key is empty it returns without doing anything. Otherwise it asks the Controller which address is currently registered under that key, copies the calldata into memory, and delegatecalls that address, returning or reverting with whatever comes back. Because the logic address is resolved on every call rather than fixed at deployment, Augur could repoint every share token at once by registering a new implementation in the Controller.
The source is Augur's own Delegator contract with its Controlled and DelegationTarget parents, compiled with solc 0.4.20 and the optimizer on. The runtime code matches the deployed bytecode; only the trailing swarm metadata hash differs, because the EthereumHistory attribution comment changes the source text. Sourcify records this as a partial match and Etherscan shows it as verified.
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)
}
}
}
}