Instantly share code, notes, and snippets.

Embed
What would you like to do?
Etherdate gen 2
pragma solidity ^0.4.4;
contract Etherdate {
uint constant startingPrice = 20 finney;
string constant startingMessage = "Nothing to see here...";
uint constant dummyCoinID = 0;
// There are 366 coins (1-indexed so that 0 can be used as a non-assignment flag):
// day | id
// 1/1 | 1
// ...
// 12/31 | 365
// 2/29 | 366 (leap day)
mapping(uint => address) public coinToOwner;
mapping(uint => string) public coinToMessage;
mapping(uint => uint) public coinToPrice;
mapping(address => uint) _pendingWithdrawals;
// sorted (lowest-highest) array of top 10 coin IDs by price
uint[10] _top10Coins;
// May not need this
address public creator;
modifier onlyCreator() {
require(msg.sender == creator);
_;
}
function Etherdate() public {
creator = msg.sender;
}
function buy(uint id, string message) public payable returns (bool) {
require(id >= 1 && id <= 366);
var (owner, prevMessage, price) = _getCoinData(id);
if (msg.value >= price) {
var (fee, payment) = _extractFee(msg.value);
_pendingWithdrawals[creator] += fee;
_pendingWithdrawals[owner] += payment;
_assignCoin(id, msg.sender, message, _determineNewPrice(msg.value));
_updateTop10Coins(id);
return true;
} else {
return false;
}
}
function _assignCoin(uint id, address owner, string message, uint newPrice) private {
coinToOwner[id] = owner;
coinToMessage[id] = message;
coinToPrice[id] = newPrice;
}
// Extract fee to be paid to contract creator. Fee is defined entirely in this contract! Can be changed in
// future versions
function _extractFee(uint amountPaid) private pure returns (uint, uint) {
uint fee = amountPaid / 100;
return (fee, amountPaid - fee);
}
// 20 -> 320 f = 2x
// 320 -> 1620 f = 1.5x
// 1620 -> ... f = 1.1x
function _determineNewPrice(uint amountPaid) private pure returns (uint) {
if (amountPaid < 320 finney) {
return amountPaid * 2;
} else if (amountPaid < 1620 finney) {
return (amountPaid * 3) / 2;
} else {
return (amountPaid * 11) / 10;
}
}
// Do an insertion sort into the list and then unshift elements 'behind it'
function _updateTop10Coins(uint newCoinId) private {
_removeExistingFromTop10(newCoinId);
_insertNewCoinTop10(newCoinId);
}
function _insertNewCoinTop10(uint newCoinId) private {
uint newPrice = coinToPrice[newCoinId];
// get insertion index
uint8 i = 0;
while (i < 10 && (_top10Coins[i] == dummyCoinID || newPrice >= coinToPrice[_top10Coins[i]])) {
i++;
}
// don't need to insert if doesn't belong in top 10
if (i > 0) {
uint8 insertionIndex = i - 1;
uint idToInsert = newCoinId;
uint tmp;
while (idToInsert != dummyCoinID) {
tmp = _top10Coins[insertionIndex];
_top10Coins[insertionIndex] = idToInsert;
// Don't do shifting logic if we're at the beginning of the list!
if (insertionIndex == 0) {
break;
}
insertionIndex--;
idToInsert = tmp;
}
}
}
// Remove all existing instances of new coin in the top 10 and shift list accordingly
// i.e. if newCoinId = 1 and top10List = [0,...,3,1,2] -> [0,...,3,2]
function _removeExistingFromTop10(uint newCoinId) private {
uint newCoinIdx = 10; // only top 10, so this can never be an issue... May want to hardcode in top 10-ness
for (uint i = 0; i < 10; i++) {
if (_top10Coins[i] == newCoinId) {
newCoinIdx = i;
}
}
if (newCoinIdx < 10) {
_top10Coins[newCoinIdx] = dummyCoinID;
uint dummyIdx = newCoinIdx;
// Right shift dummy coins
while (dummyIdx > 0) {
_top10Coins[dummyIdx] = _top10Coins[dummyIdx - 1];
_top10Coins[dummyIdx - 1] = dummyCoinID;
dummyIdx--;
}
}
}
// NOTE: Maybe this should return a 'nil'-type value
function getCoinData(uint id) public view returns (address, string, uint) {
return _getCoinData(id);
}
function _getCoinData(uint id) private view returns (address, string, uint) {
address owner;
string memory message;
uint price;
if (coinToPrice[id] == dummyCoinID) {
owner = creator;
message = startingMessage;
price = startingPrice;
} else {
owner = coinToOwner[id];
message = coinToMessage[id];
price = coinToPrice[id];
}
return (owner, message, price);
}
function getTop10Coins() public view returns (uint[10]) {
return _top10Coins;
}
// Withdraw split out to avoid re-entrancey if buy fails on send
function withdraw() public {
uint amount = _pendingWithdrawals[msg.sender];
if (amount > 0) {
_pendingWithdrawals[msg.sender] = 0; // zero out withdrawal first to protect against re-entrancy
msg.sender.transfer(amount);
}
}
function getPendingWithdrawal() public view returns (uint) {
return _pendingWithdrawals[msg.sender];
}
// NOTE: For retrieving withdrawals in case a new generation is necessary
function getPendingWithdrawal(address user) public view onlyCreator returns (uint) {
return _pendingWithdrawals[user];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment