Permalink
Fetching contributors…
Cannot retrieve contributors at this time
251 lines (217 sloc) 6.75 KB
pragma solidity 0.4.24;
import "./XRT.sol";
/// @title Dutch auction contract - distribution of XRT tokens using an auction.
/// @author Stefan George - <stefan.george@consensys.net>
/// @author Airalab - <research@aira.life>
contract DutchAuction {
/*
* Events
*/
event BidSubmission(address indexed sender, uint256 amount);
/*
* Constants
*/
uint constant public MAX_TOKENS_SOLD = 800 * 10**9; // 8M XRT = 10M - 1M (Foundation) - 1M (Early investors base)
uint constant public WAITING_PERIOD = 0; // 1 days;
/*
* Storage
*/
XRT public xrt;
address public ambix;
address public wallet;
address public owner;
uint public ceiling;
uint public priceFactor;
uint public startBlock;
uint public endTime;
uint public totalReceived;
uint public finalPrice;
mapping (address => uint) public bids;
Stages public stage;
/*
* Enums
*/
enum Stages {
AuctionDeployed,
AuctionSetUp,
AuctionStarted,
AuctionEnded,
TradingStarted
}
/*
* Modifiers
*/
modifier atStage(Stages _stage) {
// Contract on stage
require(stage == _stage);
_;
}
modifier isOwner() {
// Only owner is allowed to proceed
require(msg.sender == owner);
_;
}
modifier isWallet() {
// Only wallet is allowed to proceed
require(msg.sender == wallet);
_;
}
modifier isValidPayload() {
require(msg.data.length == 4 || msg.data.length == 36);
_;
}
modifier timedTransitions() {
if (stage == Stages.AuctionStarted && calcTokenPrice() <= calcStopPrice())
finalizeAuction();
if (stage == Stages.AuctionEnded && now > endTime + WAITING_PERIOD)
stage = Stages.TradingStarted;
_;
}
/*
* Public functions
*/
/// @dev Contract constructor function sets owner.
/// @param _wallet Multisig wallet.
/// @param _ceiling Auction ceiling.
/// @param _priceFactor Auction price factor.
constructor(address _wallet, uint _ceiling, uint _priceFactor)
public
{
require(_wallet != 0 && _ceiling > 0 && _priceFactor > 0);
owner = msg.sender;
wallet = _wallet;
ceiling = _ceiling;
priceFactor = _priceFactor;
stage = Stages.AuctionDeployed;
}
/// @dev Setup function sets external contracts' addresses.
/// @param _xrt Robonomics token address.
/// @param _ambix Distillation cube address.
function setup(address _xrt, address _ambix)
public
isOwner
atStage(Stages.AuctionDeployed)
{
// Validate argument
require(_xrt != 0 && _ambix != 0);
xrt = XRT(_xrt);
ambix = _ambix;
// Validate token balance
require(xrt.balanceOf(this) == MAX_TOKENS_SOLD);
stage = Stages.AuctionSetUp;
}
/// @dev Starts auction and sets startBlock.
function startAuction()
public
isWallet
atStage(Stages.AuctionSetUp)
{
stage = Stages.AuctionStarted;
startBlock = block.number;
}
/// @dev Calculates current token price.
/// @return Returns token price.
function calcCurrentTokenPrice()
public
timedTransitions
returns (uint)
{
if (stage == Stages.AuctionEnded || stage == Stages.TradingStarted)
return finalPrice;
return calcTokenPrice();
}
/// @dev Returns correct stage, even if a function with timedTransitions modifier has not yet been called yet.
/// @return Returns current auction stage.
function updateStage()
public
timedTransitions
returns (Stages)
{
return stage;
}
/// @dev Allows to send a bid to the auction.
/// @param receiver Bid will be assigned to this address if set.
function bid(address receiver)
public
payable
isValidPayload
timedTransitions
atStage(Stages.AuctionStarted)
returns (uint amount)
{
require(msg.value > 0);
amount = msg.value;
// If a bid is done on behalf of a user via ShapeShift, the receiver address is set.
if (receiver == 0)
receiver = msg.sender;
// Prevent that more than 90% of tokens are sold. Only relevant if cap not reached.
uint maxWei = MAX_TOKENS_SOLD * calcTokenPrice() / 10**9 - totalReceived;
uint maxWeiBasedOnTotalReceived = ceiling - totalReceived;
if (maxWeiBasedOnTotalReceived < maxWei)
maxWei = maxWeiBasedOnTotalReceived;
// Only invest maximum possible amount.
if (amount > maxWei) {
amount = maxWei;
// Send change back to receiver address. In case of a ShapeShift bid the user receives the change back directly.
receiver.transfer(msg.value - amount);
}
// Forward funding to ether wallet
wallet.transfer(amount);
bids[receiver] += amount;
totalReceived += amount;
emit BidSubmission(receiver, amount);
// Finalize auction when maxWei reached
if (amount == maxWei)
finalizeAuction();
}
/// @dev Claims tokens for bidder after auction.
/// @param receiver Tokens will be assigned to this address if set.
function claimTokens(address receiver)
public
isValidPayload
timedTransitions
atStage(Stages.TradingStarted)
{
if (receiver == 0)
receiver = msg.sender;
uint tokenCount = bids[receiver] * 10**9 / finalPrice;
bids[receiver] = 0;
require(xrt.transfer(receiver, tokenCount));
}
/// @dev Calculates stop price.
/// @return Returns stop price.
function calcStopPrice()
view
public
returns (uint)
{
return totalReceived * 10**9 / MAX_TOKENS_SOLD + 1;
}
/// @dev Calculates token price.
/// @return Returns token price.
function calcTokenPrice()
view
public
returns (uint)
{
return priceFactor * 10**18 / (block.number - startBlock + 7500) + 1;
}
/*
* Private functions
*/
function finalizeAuction()
private
{
stage = Stages.AuctionEnded;
finalPrice = totalReceived == ceiling ? calcTokenPrice() : calcStopPrice();
uint soldTokens = totalReceived * 10**9 / finalPrice;
if (totalReceived == ceiling) {
// Auction contract transfers all unsold tokens to Ambix contract
require(xrt.transfer(ambix, MAX_TOKENS_SOLD - soldTokens));
} else {
// Auction contract burn all unsold tokens
xrt.burn(MAX_TOKENS_SOLD - soldTokens);
}
endTime = now;
}
}