Upgradeable proxy that asks a settings contract both where to delegate and how many bytes the answer will be, because it predates RETURNDATASIZE.
Historical Significance
Byzantium made this contract obsolete. After RETURNDATASIZE, a proxy just copies whatever came back and the size table disappears. Reading this code is a good way to see what upgradeable proxies cost before that opcode existed. Every function added to the implementation also had to be registered, by selector, with its exact return width, or calls through the proxy would silently truncate.
Key Facts
Description
One storage slot holds the settings contract. A call with empty calldata logs a Deposit and stops, so the address can be funded like an ordinary account. Anything else is forwarded.
Forwarding takes two reads from settings. The first, sizes, maps the incoming function selector to the number of bytes that function returns. The second, target, gives the current implementation. The proxy then copies its calldata to memory zero, delegatecalls the implementation with ten thousand gas held back, and returns exactly the number of bytes the first lookup promised.
That first lookup is the whole design. This is Homestead-era code, compiled before RETURNDATASIZE existed, so a proxy had no way to discover how much data a delegated call produced. The options were to guess a fixed size, or to keep a table. This one keeps a table, off-chain of itself, in a contract the operator can update alongside the implementation.
The selector is read straight out of calldata with inline assembly into a bytes32 and narrowed to bytes4 only at the point of the lookup.
Source Verified
Heuristic Analysis
The following characteristics were detected through bytecode analysis and may not be accurate.
Spurious Dragon Era
Continued DoS protection. State trie clearing.
Bytecode Overview
Verified Source Available
This contract has verified source code.
View Verification ProofShow source code (Solidity)
// Submitted by EthereumHistory (ethereumhistory.com)
contract Settings {
function sizes(bytes4 _sig) constant returns (uint32);
function target() constant returns (address);
}
contract Proxy {
address public settings;
event Deposit(address _from, uint256 _value);
function initialize(address _settings) {
}
function () payable {
bytes32 sig;
uint32 size;
address t;
uint256 result;
if (msg.data.length == 0) {
Deposit(msg.sender, msg.value);
return;
}
assembly { sig := calldataload(0) }
size = Settings(settings).sizes(bytes4(sig));
t = Settings(settings).target();
assembly {
calldatacopy(0x0, 0x0, calldatasize)
result := delegatecall(sub(gas, 10000), t, 0x0, calldatasize, 0x0, size)
}
if (result == 0) {
assembly { revert(0, 0) }
}
assembly { return(0, size) }
}
}