The second ether_ad store, redeployed two days later with a revised two phase auction
Historical Significance
The later of the two ether_ad deployments. Its OnePhaseAuction is unchanged from the first, so the redeployment was driven entirely by the sealed bid auction, which had its phase accounting rewritten.
Context
Deployed during the Frontier era, when Solidity had been public for two months and dapp-bin was the reference collection of worked examples.
Key Facts
Description
adStorer is the contract at the centre of ether_ad, the advertising dapp in ethereum/dapp-bin. It creates eight auction contracts of its own, four OnePhaseAuction and four TwoPhaseAuction, and keeps the winning bidder and the winning URL for each of the eight ad slots. This deployment carries a revised TwoPhaseAuction in which the two phase deadlines are stored as absolute end times rather than a single rolling next phase marker, and it exposes getHashSubmissionEnd and getHashRevealEnd in place of getPhaseExpiry. Deployed on 22 October 2015 by 0x1db3439a222c519ab44bb1144fc28167b4fa6ee6, the account of Vitalik Buterin. Recovered from ethereum/dapp-bin and reproduced byte for byte with solc v0.1.1 and the optimizer on.
Source Verified
Source: ethereum/dapp-bin@cab4374:ether_ad/ (one_phase_auction.sol + two_phase_auction.sol + adStorer.sol). Compiled with soljson-v0.1.1+commit.6ff4cd6 optimizer ON. The output is a byte-for-byte match against the on-chain creation bytecode (8752 bytes, tx input of 0x2c09efb1) and against the runtime returned by eth_getCode. v0.1.1 was the first tagged solc release (August 2015), still in production use when Vitalik deployed this in October 2015. Every later compiler from v0.1.4 through v0.2.0 produces a different output for the same source. Sourcify cannot verify this contract: the oldest compiler version it supports is v0.1.7. Submission to /server/v2/verify with compilerVersion=0.1.1+commit.6ff4cd6f was rejected with customCode unsupported_compiler_version (verificationId 969ec1a0-e6f6-455a-8feb-ad9237721d5e on 2026-05-13). Submitting the same standard-json with compilerVersion=0.1.7+commit.b4e666cc was accepted but returned no_match because v0.1.7 emits different bytecode. Independent reproduction proof lives at https://github.com/cartoonitunes/adstorer-verification: running verify.js compiles the bundled source with soljson-v0.1.1+commit.6ff4cd6.js and asserts the 8752-byte exact match.
Heuristic Analysis
The following characteristics were detected through bytecode analysis and may not be accurate.
Frontier Era
The initial release of Ethereum. A bare-bones implementation for technical users.
Bytecode Overview
Verified Source Available
Source verified through compiler archaeology and exact bytecode matching.
View Verification ProofShow source code (Solidity)
// Submitted by EthereumHistory (ethereumhistory.com)
//
// Source: ethereum/dapp-bin@cab4374:ether_ad/ (one_phase_auction.sol + two_phase_auction.sol + adStorer.sol)
// Compiled with solc v0.1.1+commit.6ff4cd6, optimizer enabled.
// Exact bytecode match against on-chain creation code at block 411110.
contract OnePhaseAuction {
adStorer target;
address owner;
uint256 phase;
uint256 auctionEnd;
uint256 durationBumpTo;
uint256 minIncrementMillis;
uint256 mostRecentAuctionStart;
struct Bid {
uint256 bidValue;
string metadata;
address bidder;
}
Bid[999999999999999999999] bids;
uint256 bestBidIndex;
uint256 bestBidValue;
uint256 secondBestBidValue;
uint256 nextBidIndex;
uint256 totalRevenue;
// Bitmask: +1 if bids cumulative +0 if independent, +2 if all-pay +0 if lead pays
uint256 auctionType;
event BidSubmitted(uint256 index, uint256 bidValue, string metadata, address indexed bidder);
event BidIncreased(uint256 index, uint256 bidValue, uint256 cumValue, string metadata, address indexed bidder);
event AuctionWinner(uint256 index, uint256 bidValue, string metadata, address indexed bidder);
event AuctionFinalized(uint256 revenue);
event AuctionInitialized();
function OnePhaseAuction() {
owner = msg.sender;
}
// Initialize the auction
function initialize(address _t, uint256 _baseDuration, uint256 _durationBumpTo, uint256 _minIncrementMillis, uint256 _tp) returns (bool) {
if (msg.sender != owner) return false;
if (phase == 1 || phase == 2) return false;
phase = 1;
target = adStorer(_t);
auctionEnd = block.timestamp + _baseDuration;
durationBumpTo = _durationBumpTo;
minIncrementMillis = _minIncrementMillis;
nextBidIndex = 0;
bestBidValue = 0;
bestBidIndex = 0;
auctionType = _tp;
mostRecentAuctionStart = block.number;
AuctionInitialized();
return true;
}
// Place one's bid
function bid(string metadata) returns (int256) {
if (phase != 1) {
msg.sender.send(msg.value);
return (-1);
}
if (phase == 1 && block.timestamp >= auctionEnd) {
phase = 2;
msg.sender.send(msg.value);
return (-1);
}
if (msg.value * 1000 < bestBidValue * (1000 + minIncrementMillis)) {
msg.sender.send(msg.value);
return (-1);
}
if (msg.value > bestBidValue) {
bestBidValue = msg.value;
bestBidIndex = nextBidIndex;
}
bids[nextBidIndex].bidValue = msg.value;
bids[nextBidIndex].metadata = metadata;
bids[nextBidIndex].bidder = msg.sender;
BidSubmitted(nextBidIndex, msg.value, metadata, msg.sender);
nextBidIndex = nextBidIndex + 1;
if (auctionEnd - block.timestamp < durationBumpTo)
auctionEnd = block.timestamp + durationBumpTo;
if ((auctionType & 2) == 2)
totalRevenue += msg.value;
return int256(nextBidIndex) - 1;
}
// Increase one's bid
function increaseBid(uint256 index) returns (bool) {
if ((auctionType & 1) == 0) {
msg.sender.send(msg.value);
return (false);
}
if (phase != 1) {
msg.sender.send(msg.value);
return (false);
}
if (phase == 1 && block.timestamp >= auctionEnd) {
msg.sender.send(msg.value);
phase = 2;
return (false);
}
if ((bids[index].bidValue + msg.value) * 1000 < bestBidValue * (1000 + minIncrementMillis)) {
msg.sender.send(msg.value);
return(false);
}
if (index >= nextBidIndex) {
msg.sender.send(msg.value);
return false;
}
bids[index].bidValue += msg.value;
if (bids[index].bidValue > bestBidValue) {
bestBidValue = bids[index].bidValue;
bestBidIndex = index;
}
if ((auctionType & 2) == 2)
totalRevenue += msg.value;
BidIncreased(index, msg.value, bids[index].bidValue, bids[index].metadata, bids[index].bidder);
if (auctionEnd - block.timestamp < durationBumpTo)
auctionEnd = block.timestamp + durationBumpTo;
return(true);
}
// Clean up during phase 3
function ping() returns(bool) {
if (phase == 1 && block.timestamp >= auctionEnd)
phase = 2;
if (phase != 2) return(false);
uint _nbi = nextBidIndex;
while (msg.gas > 100000 && _nbi > 0) {
_nbi -= 1;
if (_nbi == bestBidIndex) {
}
else {
if ((auctionType & 2) == 0)
bids[_nbi].bidder.send(bids[_nbi].bidValue);
bids[_nbi].bidValue = 0;
}
}
nextBidIndex = _nbi;
if (_nbi == 0) {
phase = 0;
bool success;
if (bestBidValue > 0) {
AuctionWinner(bestBidIndex, bestBidValue, bids[bestBidIndex].metadata, bids[bestBidIndex].bidder);
success = target.acceptAuctionResult(bids[bestBidIndex].bidder, bestBidValue, bids[bestBidIndex].metadata);
}
else {
success = target.acceptAuctionResult(0, 0, "");
}
if ((auctionType & 2) == 2)
AuctionFinalized(totalRevenue);
else
AuctionFinalized(bestBidValue);
if (!success) { while (1 == 1) { _nbi = _nbi; } }
return(true);
}
return(false);
}
function setOwner(address newOwner) {
if (owner == msg.sender) owner = newOwner;
}
function withdraw() {
if (msg.sender == owner) msg.sender.send(this.balance);
}
function getPhase() constant returns (uint256) {
return phase;
}
function getMostRecentAuctionStart() constant returns (uint256) {
return mostRecentAuctionStart;
}
function getPhaseExpiry() constant returns (uint256) {
return auctionEnd;
}
}
contract AuctionResultAcceptor {
function acceptAuctionResult(address winner, uint256 value, string metadata) { }
}
contract TwoPhaseAuction {
adStorer target;
address owner;
uint256 phase;
uint256 hashSubmissionEnd;
uint256 hashRevealEnd;
uint256 mostRecentAuctionStart;
uint256 valueSubmissionSubsidyMillis;
struct Bid {
bytes32 bidValueHash;
uint256 bidValue;
uint256 valueSubmitted;
string metadata;
address bidder;
}
Bid[999999999999999999999] bids;
uint256 bestBidIndex;
uint256 bestBidValue;
uint256 secondBestBidValue;
uint256 nextBidIndex;
uint256 totalValueSubmitted;
uint256 auctionRevenue;
// 1 = first price, 2 == second price, 3 == all pay, 4 == all pay + second price
uint256 auctionType;
event BidCommitted(uint256 index, bytes32 bidValueHash, string metadata, address indexed bidder);
event BidRevealed(uint256 index, uint256 bidValue, string metadata, address indexed bidder);
event AuctionWinner(uint256 index, uint256 bidValue, string metadata, address indexed bidder);
event AuctionFinalized(uint256 revenue);
event AuctionInitialized();
function TwoPhaseAuction() {
owner = msg.sender;
}
// Initialize the auction
function initialize(address _t, uint256 _hsp, uint256 _hrp, uint256 _vssm, uint256 _tp) returns (bool) {
if (msg.sender != owner) return false;
if (phase == 1 || phase == 2) return false;
phase = 1;
target = adStorer(_t);
hashSubmissionEnd = block.timestamp + _hsp;
hashRevealEnd = block.timestamp + _hsp + _hrp;
valueSubmissionSubsidyMillis = _vssm;
nextBidIndex = 0;
bestBidValue = 0;
secondBestBidValue = 0;
totalValueSubmitted = 0;
auctionRevenue = 0;
auctionType = _tp;
mostRecentAuctionStart = block.number;
AuctionInitialized();
return true;
}
// Commit one's bid. This also entails sending an amount of ether at least
// equal to, but potentially more than, one's bid; if you send a greater
// amount than the difference between the submission and your actual bid
// will be refunded to you (even in all-pay auctions). This protects bid
// privacy.
function commitBid(bytes32 bidValueHash, string metadata) returns (int256) {
if (phase != 1) {
msg.sender.send(msg.value);
return (-1);
}
if (phase == 1 && block.timestamp >= hashSubmissionEnd) {
msg.sender.send(msg.value);
return (-1);
}
bids[nextBidIndex].bidValueHash = bidValueHash;
bids[nextBidIndex].valueSubmitted = msg.value;
bids[nextBidIndex].metadata = metadata;
bids[nextBidIndex].bidder = msg.sender;
BidCommitted(nextBidIndex, bidValueHash, metadata, msg.sender);
nextBidIndex = nextBidIndex + 1;
totalValueSubmitted += msg.value;
return int256(nextBidIndex) - 1;
}
// Reveal one's bid
function revealBid(uint256 index, uint256 bidValue, bytes32 nonce) returns (bool) {
if (phase == 1 && block.timestamp >= hashRevealEnd) {
phase = 2;
}
if (phase != 1) {
return (false);
}
if (phase == 1 && block.timestamp < hashSubmissionEnd) {
return (false);
}
if (index >= nextBidIndex)
return false;
if (bidValue > bids[index].valueSubmitted)
return false;
if (sha3(bidValue, nonce) != bids[index].bidValueHash)
return false;
if (bidValue > bestBidValue) {
secondBestBidValue = bestBidValue;
bestBidValue = bidValue;
bestBidIndex = index;
}
else if (bidValue > secondBestBidValue) {
secondBestBidValue = bidValue;
}
// Only need to keep track of bid values for all-pay auctions
if (auctionType == 3 || auctionType == 4) {
bids[index].bidValue = bidValue;
auctionRevenue += bidValue;
}
BidRevealed(index, bidValue, bids[index].metadata, bids[index].bidder);
return true;
}
// Clean up during phase 2
function ping() returns(bool) {
if (phase == 1 && block.timestamp >= hashRevealEnd)
phase = 2;
if (phase != 2) return(false);
uint _nbi = nextBidIndex;
uint _ar;
if (auctionType == 1) _ar = bestBidValue;
else if (auctionType == 2) _ar = secondBestBidValue;
else if (auctionType == 3) _ar = auctionRevenue;
else if (auctionType == 4) _ar = auctionRevenue + secondBestBidValue - bestBidValue;
while (msg.gas > 500000 && _nbi > 0) {
_nbi -= 1;
uint256 subsidy = bids[_nbi].valueSubmitted * _ar * valueSubmissionSubsidyMillis / totalValueSubmitted / 1000;
if (_nbi == bestBidIndex) {
// First price auction or all-pay auction: take winner's bid at its own value
if (auctionType == 1 || auctionType == 3)
bids[_nbi].bidder.send(bids[_nbi].valueSubmitted - bestBidValue + subsidy);
// Second price auction: take winner's bid at second highest value
else if (auctionType == 2 || auctionType == 4)
bids[_nbi].bidder.send(bids[_nbi].valueSubmitted - secondBestBidValue + subsidy);
}
else {
// First price or second price auction: refund everyone else's bids
if (auctionType == 1 || auctionType == 2)
bids[_nbi].bidder.send(bids[_nbi].valueSubmitted + subsidy);
// All-pay auction: don't refund everyone else's bids
else
bids[_nbi].bidder.send(bids[_nbi].valueSubmitted - bids[_nbi].bidValue + subsidy);
bids[_nbi].bidValueHash = 0;
}
}
nextBidIndex = _nbi;
if (_nbi == 0) {
phase = 0;
bool success;
if (bestBidValue > 0) {
AuctionWinner(bestBidIndex, bestBidValue, bids[bestBidIndex].metadata, bids[bestBidIndex].bidder);
success = target.acceptAuctionResult(bids[bestBidIndex].bidder, bestBidValue, bids[bestBidIndex].metadata);
}
else {
success = target.acceptAuctionResult(0, 0, "");
}
AuctionFinalized(_ar);
if (!success) { while (1 == 1) { _nbi = _nbi; } }
return(true);
}
return(false);
}
function setOwner(address newOwner) {
if (owner == msg.sender) owner = newOwner;
}
function withdraw() {
if (msg.sender == owner) msg.sender.send(this.balance);
}
function getPhase() constant returns (uint256) {
return phase;
}
function getMostRecentAuctionStart() constant returns (uint256) {
return mostRecentAuctionStart;
}
function getHashSubmissionEnd() constant returns (uint256) {
return hashSubmissionEnd;
}
function getHashRevealEnd() constant returns (uint256) {
return hashRevealEnd;
}
}
contract adStorer {
string[8] urls;
address[8] winners;
address owner;
address[8] auctions;
uint256 hashSubmissionPeriod;
uint256 hashRevealPeriod;
uint256 baseDuration;
uint256 durationBumpTo;
uint256 minIncrementMillis;
uint256 initializedTo;
uint256 valueSubmissionSubsidyMillis;
event GasRemaining(uint256 g, uint256 i);
// Recommend: 86400 86400 86400 3600 50 10 live
// Recommend: 240 240 240 120 50 10 test
function initialize(uint256 _hsp, uint256 _hrp, uint256 _bdur, uint256 _dbt, uint256 _mim, uint256 _vssm) returns (bool) {
if (initializedTo < 8) {
if (owner == 0) owner = msg.sender;
hashSubmissionPeriod = _hsp;
hashRevealPeriod = _hrp;
baseDuration = _bdur;
durationBumpTo = _dbt;
minIncrementMillis = _mim;
valueSubmissionSubsidyMillis = _vssm;
for (uint256 i = initializedTo; i < 8 && msg.gas > 1100000; i++) {
GasRemaining(msg.gas, i);
if (i < 4) {
auctions[i] = new OnePhaseAuction();
OnePhaseAuction(auctions[i]).initialize(this, baseDuration, durationBumpTo, minIncrementMillis, i);
}
else {
auctions[i] = new TwoPhaseAuction();
TwoPhaseAuction(auctions[i]).initialize(this, hashSubmissionPeriod, hashRevealPeriod, valueSubmissionSubsidyMillis, i - 3);
}
}
initializedTo = i;
if (initializedTo == 8) return true;
else return false;
}
}
function acceptAuctionResult(address winner, uint256 value, string metadata) returns (bool) {
for (uint256 i = 0; i < 8; i++) {
if (msg.sender == auctions[i]) {
if (winner != 0) {
urls[i] = metadata;
winners[i] = winner;
}
if (i < 4) OnePhaseAuction(msg.sender).initialize(this, baseDuration, durationBumpTo, minIncrementMillis, i);
else TwoPhaseAuction(msg.sender).initialize(this, hashSubmissionPeriod, hashRevealPeriod, valueSubmissionSubsidyMillis, i - 3);
return true;
}
}
return false;
}
function getAuctionAddress(uint256 id) constant returns (address) {
return auctions[id];
}
function getWinnerUrl(uint256 id) constant returns (string) {
return urls[id];
}
function getWinnerAddress(uint256 id) constant returns (address) {
return winners[id];
}
}External Links
Related contracts
SerpentGamble
Same deployerProvably-fair betting game where players choose their own odds. The owner settles bets with a commit-reveal random seed. Includes a 2-day emergency refund.
0x034df8...200e2fSeptember 20, 2015SerpentGamble
Same deployerSibling of 0x034dF870 (SerpentGamble). Provably-fair Serpent betting game; players choose their own odds, owner settles via commit-reveal seed, 2-day refund.
0x9ca7e9...fa8bb4September 21, 2015SerpentGamble
Same deployerSibling of 0x034dF870 (SerpentGamble). Provably-fair Serpent betting game; players choose their own odds, owner settles via commit-reveal seed, 2-day refund.
0x75649a...176028September 21, 2015SerpentGamble
Same deployerIntermediate build of serpent_gamble, a provably-fair betting dapp (Bet/Win/Loss/NewSeed), related to verified sibling 0x59375871. Source not yet reconstructed.
0xe1a99f...0d2457September 21, 2015SerpentGamble
Same deployerIntermediate build of serpent_gamble, a provably-fair betting dapp (Bet/Win/Loss/NewSeed), related to verified sibling 0x59375871. Source not yet reconstructed.
0xdc00a9...15861fSeptember 21, 2015SerpentGamble
Same deployerIntermediate build of serpent_gamble, a provably-fair betting dapp (Bet/Win/Loss/NewSeed), related to verified sibling 0x59375871. Source not yet reconstructed.
0xd0ed09...300b48September 21, 2015