-
-
Save n00b21337/dd5a1d9ab3d26632f8e7dc89f89714c3 to your computer and use it in GitHub Desktop.
Etherdate gen 2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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