pragma solidity ^0.4.0; | |
import "SafeMath.sol"; | |
import "IERC20Token.sol"; | |
/** | |
* @dev Implements a capped token sale using a second-price auction. | |
* | |
* @author Nick Johnson <arachnid@notdot.net> | |
* | |
* WARNING WARNING WARNING | |
* This contract code is intended as a proof-of-concept demonstration. No | |
* testing has been performed other than verifying that it compiles. DO NOT under | |
* any circumstances use this in its current state for any reason. | |
* WARNING WARNING WARNING | |
* | |
* Users can deposits funds for the auction any time before endTimestamp. After | |
* doing so, they send a signed message to the party running the auction with a bid. | |
* Bids are `(price, quantity)` tuples, where `price` is the maximum amount of | |
* ether a user is willing to pay per token, and `quantity` is the number of | |
* tokens the user wants to buy. | |
* | |
* At the end of the bidding period, the seller sets a 'strike price'. All bids | |
* with a price at least as high as the strike price are filled, and all bids | |
* under this strike price are returned. The strike price is calculated | |
* offchain by the seller. Once set, anyone - bidder, seller, or third party - | |
* can submit the signed bid to the contract, which will issue tokens to winning | |
* bidders. | |
* | |
* A hard cap on the amount of ether raised is specified at the time the | |
* contract is deployed, with extra funds being returned to users. | |
* | |
* A week after the end of the contract, users can unilaterally withdraw any | |
* remaining funds. | |
*/ | |
contract TokenSale is SafeMath { | |
event Deposit(address indexed bidder, uint price); | |
event StrikePriceSet(uint strikePrice); | |
mapping(address=>uint) public deposits; | |
uint public depositCount; // Count of bids | |
address public owner; | |
IERC20Token token; | |
uint public salesTarget; // Total number of ether to be raised | |
uint public strikePrice; // Price at which tokens are sold, set after sale completes | |
uint public strikePricePct; // Percentage of total qty to sell to bidders who bid exactly the strike price | |
uint public endTimestamp; // Time at which sale ends | |
modifier beforeSale { | |
require(now <= endTimestamp); | |
_; | |
} | |
modifier afterSale { | |
require(endTimestamp <= now && strikePrice != 0); | |
_; | |
} | |
modifier afterTimeout { | |
require(endTimestamp + 1 weeks <= now); | |
_; | |
} | |
modifier ownerOnly { | |
require(msg.sender == owner); | |
_; | |
} | |
function TokenSale(IERC20Token _token, uint _salesTarget, uint _startTimestamp, uint _endTimestamp) { | |
token = _token; | |
owner = msg.sender; | |
salesTarget = _salesTarget; | |
endTimestamp = _endTimestamp; | |
} | |
function deposit() beforeSale payable { | |
deposits[msg.sender] += msg.value; | |
depositCount += 1; | |
Deposit(msg.sender, msg.value); | |
} | |
function() payable { | |
deposit(); | |
} | |
function setStrikePrice(uint128 price, uint pct) ownerOnly { | |
require(endTimestamp <= now && strikePrice == 0 && price != 0); | |
strikePrice = price; | |
strikePricePct == pct; | |
StrikePriceSet(price); | |
} | |
function acceptBid(uint price, uint quantity, uint8 v, bytes32 r, bytes32 s) afterSale { | |
var tokensLeft = token.balanceOf(this); | |
var bidHash = sha3(address(this), price, quantity); | |
var bidder = ecrecover(bidHash, v, r, s); | |
var total = safeMul(price, quantity); | |
require(total <= deposits[bidder]); | |
uint filledQuantity = quantity; | |
// If the user bid under the strike price, they get no tokens | |
if(price < strikePrice) { | |
filledQuantity = 0; | |
} else if(price == strikePrice) { | |
// For bidders at exactly the strike price, part-fill their bids | |
filledQuantity = (filledQuantity * strikePricePct) / 100; | |
} | |
// If there's not enough tokens left, they get the remaining tokens | |
if(tokensLeft < filledQuantity) { | |
filledQuantity = tokensLeft; | |
} | |
// If there's not enough funds left under the cap, sell them the remainder | |
var filledTotal = safeMul(strikePrice, filledQuantity); | |
if(filledTotal > salesTarget) { | |
filledQuantity = salesTarget / strikePrice; | |
filledTotal = safeMul(strikePrice, filledQuantity); | |
} | |
if(filledTotal > 0) { | |
// Sell the user the tokens | |
owner.transfer(filledTotal); | |
salesTarget -= filledTotal; | |
tokensLeft -= filledQuantity; | |
} | |
if(deposits[bidder] > filledTotal) { | |
// Send back any extra funds | |
bidder.transfer(deposits[bidder] - filledTotal); | |
deposits[bidder] = 0; | |
} | |
depositCount -= 1; | |
token.transfer(bidder, filledQuantity); | |
} | |
function withdrawTokens() afterTimeout { | |
var total = deposits[msg.sender]; | |
deposits[msg.sender] = 0; | |
msg.sender.transfer(total); | |
} | |
} |
This comment has been minimized.
This comment has been minimized.
Wouldn't it make sense to check that pct is !=0 in function setStrikePrice? Assuming that bidders who bid exactly the strike price get something? |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
At L90, I believe you want to change == to =