Augur's REP token distribution contract, one of the earliest DeFi applications on Ethereum, compiled in Serpent.
Historical Significance
Augur was Ethereum's first major prediction market protocol, and one of the earliest DeFi applications to launch on mainnet. REP was the governance and reporting token at the heart of the system. This contract handled the initial distribution of all 11 million REP tokens to crowdsale participants. It is one of the most complex Serpent contracts deployed on Ethereum mainnet, and one of the last — Serpent was soon superseded by Solidity as the dominant smart contract language.
Context
Written in Serpent, a Python-like EVM language created by Vitalik Buterin that predated Solidity's dominance. The contract uses Serpent's send() builtin with a 5,000 gas stipend — a value changed to 0 in commit a33ef61 on June 20, 2016, just three days after the compiler commit used to build the deployed bytecode (bc4ce59, June 17, 2016). This confirms the contract was compiled months before its October 2016 deployment, using a cached Serpent install. The source is from augur-core commit d4c095c3b (October 5, 2016). Augur's main augur-core repo is at https://github.com/AugurProject/augur-core.
Token Information
Key Facts
Description
The Augur REP (Reputation) Crowdsale contract distributed 11,000,000 REP tokens to early Augur backers in October 2016. REP holders are responsible for reporting on prediction market outcomes in the Augur protocol. This contract implements a full ERC20 token in Serpent with batch distribution via setSaleDistribution(), a stillCreating() time-lock (15,000 second window from deployment), and a getRidOfDustForLaunch() function callable only from a hardcoded Augur DAO address (0x668Fc8D2004379275357c8D8e2502E19153AdEEe). The token is named "Reputation" (REP) with 18 decimals.
Source Verified
Exact 2,037-byte runtime bytecode match. Source: augur-core commit d4c095c3b. Compiler: ethereum/serpent develop branch at commit bc4ce59 (June 17, 2016). The key to finding the correct compiler version was the send() gas value: bc4ce59 uses 5,000 gas (PUSH2 0x1388), while commit a33ef61 three days later changed it to 0. Sourcify does not support Serpent contracts.
Heuristic Analysis
The following characteristics were detected through bytecode analysis and may not be accurate.
DAO Fork Era
The controversial fork to recover funds from The DAO hack.
Bytecode Overview
Verified Source Available
Source verified through compiler archaeology and exact bytecode matching.
View Verification ProofShow source code (Serpent)
# This software (Augur) allows buying && selling event outcomes in ethereum
# Copyright (C) 2015 Forecast Foundation OU
# This program is free software; you can redistribute it &&/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is free software: you can redistribute it &&/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Storage of all data associated with reporters
# takes reporterID as key, fxpValue is rep amount
data reporting[2**160]
# owner [account balance is coming from], then spender, e.g. approvedToSpend[owner][spender]
# returns amount spender can spend
data approvedToSpend[2**160][2**160]
# whether the contract has been finished seeding
data seeded
# keeps track of total rep in the system
data totalSupply
# token name for display
data name
# token symbol for display
data symbol
# amount of decimals per unit of rep
data decimals
data creator
data creation
event Transfer(from:address:indexed, to:address:indexed, value:uint256)
event Approval(owner:address:indexed, spender:address:indexed, fxpValue:uint256)
# 18 decimals
macro ONE:
10 ** 18
macro refund():
if(msg.value > 0):
if(!send(msg.sender, msg.value)):
throw()
def init():
self.name = "Reputation"
self.symbol = "REP"
self.decimals = 18
self.creator = msg.sender
self.creation = block.timestamp
def any():
refund()
# Transfer rep
# @return 1 if reputation sent
def transfer(receiver: address, fxpValue: uint256):
senderBalance = self.reporting[msg.sender]
if(senderBalance < fxpValue or !self.seeded or stillCreating()):
throw()
if(!safeToSubtract(self.reporting[msg.sender], fxpValue) or !safeToAdd(self.reporting[receiver], fxpValue)):
throw()
self.reporting[msg.sender] -= fxpValue
self.reporting[receiver] += fxpValue
log(type = Transfer, msg.sender, receiver, fxpValue)
return(1: uint256)
# TransferFrom per token api allowing another contract to withdraw on user's behalf
# fails unless from has authorized sender
# @return 1 if rep sent
def transferFrom(from: address, receiver: address, fxpValue: uint256):
senderBalance = self.reporting[from]
if(senderBalance < fxpValue or fxpValue > self.approvedToSpend[from][msg.sender] or !self.seeded or stillCreating()):
throw()
if(!safeToSubtract(self.reporting[from], fxpValue) or !safeToAdd(self.reporting[receiver], fxpValue)):
throw()
self.approvedToSpend[from][msg.sender] -= fxpValue
self.reporting[from] -= fxpValue
self.reporting[receiver] += fxpValue
log(type = Transfer, from, receiver, fxpValue)
return(1: uint256)
# Allows spender to withdraw from your rep account
def approve(spender: address, amount: uint256):
self.approvedToSpend[msg.sender][spender] = amount
log(type = Approval, msg.sender, spender, amount)
return(1: uint256)
# Sets the initial distribution of rep
def setSaleDistribution(addresses: address[], balances: uint256[]):
i = 0
numberOfAddresses = len(addresses)
if(numberOfAddresses != len(balances)):
throw()
if(self.seeded or !stillCreating()):
throw()
if(msg.sender != self.creator):
throw()
while(i < numberOfAddresses):
if(!self.reporting[addresses[i]] && !self.seeded):
self.reporting[addresses[i]] = balances[i]
self.totalSupply += balances[i]
log(type = Transfer, 0, addresses[i], balances[i])
i += 1
if(self.totalSupply == 11000000 * ONE):
self.seeded = 1
if(self.totalSupply > 11000000 * ONE):
throw()
return(1: uint256)
def getRidOfDustForLaunch():
if(msg.sender != 0x668Fc8D2004379275357c8D8e2502E19153AdEEe):
throw()
if(self.reporting[0x0000000000000000000000000000000000000000]):
self.totalSupply -= self.reporting[0x0000000000000000000000000000000000000000]
self.reporting[0x0000000000000000000000000000000000000000] = 0
return(1)
else:
throw()
# Returns amount spender can withdraw from owner
def allowance(owner: address, spender: address):
return(self.approvedToSpend[owner][spender]: uint256)
def totalSupply():
return(self.totalSupply: uint256)
# @return reputation fxpValue
def balanceOf(address: address):
return(self.reporting[address]: uint256)
def name():
return(self.name: uint256)
def decimals():
return(self.decimals: uint256)
def symbol():
return(self.symbol: uint256)
def getSeeded():
return(self.seeded: uint256)
macro stillCreating():
block.timestamp < (self.creation + 15000)
macro throw():
~jump(2**256)
# safe adders idea pulled from piper merriam's btcrelay audit
macro safeToAdd($a, $b):
(($a + $b) >= $a)
macro safeToSubtract($a, $b):
($b <= $a)