Last active
June 26, 2018 20:39
-
-
Save MidnightLightning/00bc71c4a78f7aa661ab1d2b97a8e90a to your computer and use it in GitHub Desktop.
CardboardUnicorns
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.11; | |
contract CardboardUnicorns { | |
address public owner; | |
function mint(address who, uint value); | |
function changeOwner(address _newOwner); | |
function withdraw(); | |
function withdrawForeignTokens(address _tokenContract); | |
} | |
contract RealUnicornCongress { | |
uint public priceOfAUnicornInFinney; | |
} | |
contract ForeignToken { | |
function balanceOf(address _owner) constant returns (uint256); | |
function transfer(address _to, uint256 _value) returns (bool); | |
} | |
contract CardboardUnicornAssembler { | |
address public cardboardUnicornTokenAddress; | |
address public realUnicornAddress = 0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359; | |
address public owner = msg.sender; | |
uint public pricePerUnicorn = 1 finney; | |
uint public lastPriceSetDate = 0; | |
event PriceUpdate(uint newPrice, address updater); | |
modifier onlyOwner { | |
require(msg.sender == owner); | |
_; | |
} | |
/** | |
* Change ownership of the assembler | |
*/ | |
function changeOwner(address _newOwner) onlyOwner { | |
owner = _newOwner; | |
} | |
function changeTokenOwner(address _newOwner) onlyOwner { | |
CardboardUnicorns cu = CardboardUnicorns(cardboardUnicornTokenAddress); | |
cu.changeOwner(_newOwner); | |
} | |
/** | |
* Change the CardboardUnicorns token contract managed by this contract | |
*/ | |
function changeCardboardUnicornTokenAddress(address _newTokenAddress) onlyOwner { | |
CardboardUnicorns cu = CardboardUnicorns(_newTokenAddress); | |
require(cu.owner() == address(this)); // We must be the owner of the token | |
cardboardUnicornTokenAddress = _newTokenAddress; | |
} | |
/** | |
* Change the real unicorn contract location. | |
* This contract is used as a price reference; should the Ethereum Foundation | |
* re-deploy their contract, this should be called to update the reference. | |
*/ | |
function changeRealUnicornAddress(address _newUnicornAddress) onlyOwner { | |
realUnicornAddress = _newUnicornAddress; | |
} | |
function withdraw(bool _includeToken) onlyOwner { | |
if (_includeToken) { | |
// First have the token contract send all its funds to its owner (which is us) | |
CardboardUnicorns cu = CardboardUnicorns(cardboardUnicornTokenAddress); | |
cu.withdraw(); | |
} | |
// Then send that whole total to our owner | |
owner.transfer(this.balance); | |
} | |
function withdrawForeignTokens(address _tokenContract, bool _includeToken) onlyOwner { | |
ForeignToken token = ForeignToken(_tokenContract); | |
if (_includeToken) { | |
// First have the token contract send its tokens to its owner (which is us) | |
CardboardUnicorns cu = CardboardUnicorns(cardboardUnicornTokenAddress); | |
cu.withdrawForeignTokens(_tokenContract); | |
} | |
// Then send that whole total to our owner | |
uint256 amount = token.balanceOf(address(this)); | |
token.transfer(owner, amount); | |
} | |
/** | |
* Update the price of a CardboardUnicorn to be 1/1000 a real Unicorn's price | |
*/ | |
function updatePriceFromRealUnicornPrice() { | |
require(block.timestamp > lastPriceSetDate + 7 days); // If owner set the price, cannot sync right after | |
RealUnicornCongress congress = RealUnicornCongress(realUnicornAddress); | |
pricePerUnicorn = (congress.priceOfAUnicornInFinney() * 1 finney) / 1000; | |
PriceUpdate(pricePerUnicorn, msg.sender); | |
} | |
/** | |
* Set a specific price for a CardboardUnicorn | |
*/ | |
function setPrice(uint _newPrice) onlyOwner { | |
pricePerUnicorn = _newPrice; | |
lastPriceSetDate = block.timestamp; | |
PriceUpdate(pricePerUnicorn, msg.sender); | |
} | |
/** | |
* Strap a horn to a horse! | |
*/ | |
function assembleUnicorn() payable { | |
if (msg.value >= pricePerUnicorn) { | |
CardboardUnicorns cu = CardboardUnicorns(cardboardUnicornTokenAddress); | |
cu.mint(msg.sender, msg.value / pricePerUnicorn); | |
owner.transfer(msg.value); | |
} | |
} | |
function() payable { | |
assembleUnicorn(); | |
} | |
} |
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.11; | |
contract ForeignToken { | |
function balanceOf(address _owner) constant returns (uint256); | |
function transfer(address _to, uint256 _value) returns (bool); | |
} | |
/** | |
* Math operations with safety checks | |
*/ | |
library SafeMath { | |
function mul(uint a, uint b) internal returns (uint) { | |
uint c = a * b; | |
assert(a == 0 || c / a == b); | |
return c; | |
} | |
function div(uint a, uint b) internal returns (uint) { | |
// assert(b > 0); // Solidity automatically throws when dividing by 0 | |
uint c = a / b; | |
// assert(a == b * c + a % b); // There is no case in which this doesn't hold | |
return c; | |
} | |
function sub(uint a, uint b) internal returns (uint) { | |
assert(b <= a); | |
return a - b; | |
} | |
function add(uint a, uint b) internal returns (uint) { | |
uint c = a + b; | |
assert(c >= a); | |
return c; | |
} | |
function max64(uint64 a, uint64 b) internal constant returns (uint64) { | |
return a >= b ? a : b; | |
} | |
function min64(uint64 a, uint64 b) internal constant returns (uint64) { | |
return a < b ? a : b; | |
} | |
function max256(uint256 a, uint256 b) internal constant returns (uint256) { | |
return a >= b ? a : b; | |
} | |
function min256(uint256 a, uint256 b) internal constant returns (uint256) { | |
return a < b ? a : b; | |
} | |
function assert(bool assertion) internal { | |
if (!assertion) { | |
throw; | |
} | |
} | |
} | |
contract CardboardUnicorns { | |
using SafeMath for uint; | |
string public name = "HorseWithACheapCardboardHorn"; | |
string public symbol = "HWACCH"; | |
uint public decimals = 0; | |
uint public totalSupply = 0; | |
mapping(address => uint) balances; | |
mapping (address => mapping (address => uint)) allowed; | |
address public owner = msg.sender; | |
event Transfer(address indexed from, address indexed to, uint value); | |
event Approval(address indexed owner, address indexed spender, uint value); | |
event Minted(address indexed owner, uint value); | |
/** | |
* Fix for the ERC20 short address attack. | |
*/ | |
modifier onlyPayloadSize(uint size) { | |
if(msg.data.length < size + 4) { | |
throw; | |
} | |
_; | |
} | |
modifier onlyOwner { | |
require(msg.sender == owner); | |
_; | |
} | |
/** | |
* Change ownership of the token | |
*/ | |
function changeOwner(address _newOwner) onlyOwner { | |
owner = _newOwner; | |
} | |
function withdraw() onlyOwner { | |
owner.transfer(this.balance); | |
} | |
function withdrawForeignTokens(address _tokenContract) onlyOwner { | |
ForeignToken token = ForeignToken(_tokenContract); | |
uint256 amount = token.balanceOf(address(this)); | |
token.transfer(owner, amount); | |
} | |
/** | |
* Generate new tokens. | |
* Can only be done by the owner of the contract | |
*/ | |
function mint(address _who, uint _value) onlyOwner { | |
balances[_who] = balances[_who].add(_value); | |
totalSupply = totalSupply.add(_value); | |
Minted(_who, _value); | |
} | |
/** | |
* Get the token balance of the specified address | |
*/ | |
function balanceOf(address _who) constant returns (uint balance) { | |
return balances[_who]; | |
} | |
/** | |
* Transfer token to another address | |
*/ | |
function transfer(address _to, uint _value) onlyPayloadSize(2 * 32) { | |
require(_to != address(this)); // Don't send tokens back to the contract! | |
balances[msg.sender] = balances[msg.sender].sub(_value); | |
balances[_to] = balances[_to].add(_value); | |
Transfer(msg.sender, _to, _value); | |
} | |
/** | |
* Transfer tokens from an different address to another address. | |
* Need to have been granted an allowance to do this before triggering. | |
*/ | |
function transferFrom(address _from, address _to, uint _value) onlyPayloadSize(3 * 32) { | |
var _allowance = allowed[_from][msg.sender]; | |
// Check is not needed because sub(_allowance, _value) will already throw if this condition is not met | |
// if (_value > _allowance) throw; | |
balances[_to] = balances[_to].add(_value); | |
balances[_from] = balances[_from].sub(_value); | |
allowed[_from][msg.sender] = _allowance.sub(_value); | |
Transfer(_from, _to, _value); | |
} | |
/** | |
* Approve the indicated address to spend the specified amount of tokens on the sender's behalf | |
*/ | |
function approve(address _spender, uint _value) { | |
// Ensure allowance is zero if attempting to set to a non-zero number | |
// This helps manage an edge-case race condition better: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 | |
if ((_value != 0) && (allowed[msg.sender][_spender] != 0)) throw; | |
allowed[msg.sender][_spender] = _value; | |
Approval(msg.sender, _spender, _value); | |
} | |
/** | |
* Check how many tokens the indicated address can spend on behalf of the owner | |
*/ | |
function allowance(address _owner, address _spender) constant returns (uint remaining) { | |
return allowed[_owner][_spender]; | |
} | |
} |
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.11; | |
contract ERC20Token { | |
function balanceOf(address _who) constant returns (uint balance); | |
function allowance(address _owner, address _spender) constant returns (uint remaining); | |
function transferFrom(address _from, address _to, uint _value); | |
function transfer(address _to, uint _value); | |
} | |
contract GroveAPI { | |
function insert(bytes32 indexName, bytes32 id, int value) public; | |
} | |
/** | |
* Math operations with safety checks | |
*/ | |
library SafeMath { | |
function mul(uint a, uint b) internal returns (uint) { | |
uint c = a * b; | |
assert(a == 0 || c / a == b); | |
return c; | |
} | |
function div(uint a, uint b) internal returns (uint) { | |
// assert(b > 0); // Solidity automatically throws when dividing by 0 | |
uint c = a / b; | |
// assert(a == b * c + a % b); // There is no case in which this doesn't hold | |
return c; | |
} | |
function sub(uint a, uint b) internal returns (uint) { | |
assert(b <= a); | |
return a - b; | |
} | |
function add(uint a, uint b) internal returns (uint) { | |
uint c = a + b; | |
assert(c >= a); | |
return c; | |
} | |
function max64(uint64 a, uint64 b) internal constant returns (uint64) { | |
return a >= b ? a : b; | |
} | |
function min64(uint64 a, uint64 b) internal constant returns (uint64) { | |
return a < b ? a : b; | |
} | |
function max256(uint256 a, uint256 b) internal constant returns (uint256) { | |
return a >= b ? a : b; | |
} | |
function min256(uint256 a, uint256 b) internal constant returns (uint256) { | |
return a < b ? a : b; | |
} | |
} | |
contract UnicornRanch { | |
using SafeMath for uint; | |
enum VisitType { Spa, Afternoon, Day, Overnight, Week, Extended } | |
enum VisitState { InProgress, Completed, Repossessed } | |
struct Visit { | |
uint unicornCount; | |
VisitType t; | |
uint startBlock; | |
uint expiresBlock; | |
VisitState state; | |
uint completedBlock; | |
uint completedCount; | |
} | |
struct VisitMeta { | |
address owner; | |
uint index; | |
} | |
address public cardboardUnicornTokenAddress; | |
address public groveAddress; | |
address public owner = msg.sender; | |
mapping (address => Visit[]) bookings; | |
mapping (bytes32 => VisitMeta) public bookingMetadataForKey; | |
mapping (uint8 => uint) public visitLength; | |
mapping (uint8 => uint) public visitCost; | |
uint public visitingUnicorns = 0; | |
uint public repossessionBlocks = 43200; | |
uint8 public repossessionBountyPerTen = 2; | |
uint8 public repossessionBountyPerHundred = 25; | |
uint public birthBlockThreshold = 43860; | |
uint8 public birthPerTen = 1; | |
uint8 public birthPerHundred = 15; | |
event NewBooking(address indexed _who, uint indexed _index, VisitType indexed _type, uint _unicornCount); | |
event BookingUpdate(address indexed _who, uint indexed _index, VisitState indexed _newState, uint _unicornCount); | |
event RepossessionBounty(address indexed _who, uint _unicornCount); | |
event DonationReceived(address indexed _who, uint _unicornCount); | |
modifier onlyOwner { | |
require(msg.sender == owner); | |
_; | |
} | |
function UnicornRanch() { | |
visitLength[uint8(VisitType.Spa)] = 720; | |
visitLength[uint8(VisitType.Afternoon)] = 1440; | |
visitLength[uint8(VisitType.Day)] = 2880; | |
visitLength[uint8(VisitType.Overnight)] = 8640; | |
visitLength[uint8(VisitType.Week)] = 60480; | |
visitLength[uint8(VisitType.Extended)] = 120960; | |
visitCost[uint8(VisitType.Spa)] = 0; | |
visitCost[uint8(VisitType.Afternoon)] = 0; | |
visitCost[uint8(VisitType.Day)] = 10 szabo; | |
visitCost[uint8(VisitType.Overnight)] = 30 szabo; | |
visitCost[uint8(VisitType.Week)] = 50 szabo; | |
visitCost[uint8(VisitType.Extended)] = 70 szabo; | |
} | |
function getBookingCount(address _who) constant returns (uint count) { | |
return bookings[_who].length; | |
} | |
function getBooking(address _who, uint _index) constant returns (uint _unicornCount, VisitType _type, uint _startBlock, uint _expiresBlock, VisitState _state, uint _completedBlock, uint _completedCount) { | |
Visit storage v = bookings[_who][_index]; | |
return (v.unicornCount, v.t, v.startBlock, v.expiresBlock, v.state, v.completedBlock, v.completedCount); | |
} | |
function bookSpaVisit(uint _unicornCount) payable { | |
return addBooking(VisitType.Spa, _unicornCount); | |
} | |
function bookAfternoonVisit(uint _unicornCount) payable { | |
return addBooking(VisitType.Afternoon, _unicornCount); | |
} | |
function bookDayVisit(uint _unicornCount) payable { | |
return addBooking(VisitType.Day, _unicornCount); | |
} | |
function bookOvernightVisit(uint _unicornCount) payable { | |
return addBooking(VisitType.Overnight, _unicornCount); | |
} | |
function bookWeekVisit(uint _unicornCount) payable { | |
return addBooking(VisitType.Week, _unicornCount); | |
} | |
function bookExtendedVisit(uint _unicornCount) payable { | |
return addBooking(VisitType.Extended, _unicornCount); | |
} | |
function addBooking(VisitType _type, uint _unicornCount) payable { | |
if (_type == VisitType.Afternoon) { | |
return donateUnicorns(availableBalance(msg.sender)); | |
} | |
require(msg.value >= visitCost[uint8(_type)].mul(_unicornCount)); // Must be paying proper amount | |
ERC20Token cardboardUnicorns = ERC20Token(cardboardUnicornTokenAddress); | |
cardboardUnicorns.transferFrom(msg.sender, address(this), _unicornCount); // Transfer the actual asset | |
visitingUnicorns = visitingUnicorns.add(_unicornCount); | |
uint expiresBlock = block.number.add(visitLength[uint8(_type)]); // Calculate when this booking will be done | |
// Add the booking to the ledger | |
bookings[msg.sender].push(Visit( | |
_unicornCount, | |
_type, | |
block.number, | |
expiresBlock, | |
VisitState.InProgress, | |
0, | |
0 | |
)); | |
uint newIndex = bookings[msg.sender].length - 1; | |
bytes32 uniqueKey = keccak256(msg.sender, newIndex); // Create a unique key for this booking | |
// Add a reference for that key, to find the metadata about it later | |
bookingMetadataForKey[uniqueKey] = VisitMeta( | |
msg.sender, | |
newIndex | |
); | |
if (groveAddress > 0) { | |
// Insert into Grove index for applications to query | |
GroveAPI g = GroveAPI(groveAddress); | |
g.insert("bookingExpiration", uniqueKey, int(expiresBlock)); | |
} | |
// Send event about this new booking | |
NewBooking(msg.sender, newIndex, _type, _unicornCount); | |
} | |
function completeBooking(uint _index) { | |
require(bookings[msg.sender].length > _index); // Sender must have at least this many bookings | |
Visit storage v = bookings[msg.sender][_index]; | |
require(block.number >= v.expiresBlock); // Expired time must be past | |
require(v.state == VisitState.InProgress); // Visit must not be complete or repossessed | |
uint unicornsToReturn = v.unicornCount; | |
ERC20Token cardboardUnicorns = ERC20Token(cardboardUnicornTokenAddress); | |
// Determine if any births occurred | |
uint birthCount = 0; | |
if (SafeMath.sub(block.number, v.startBlock) >= birthBlockThreshold) { | |
if (v.unicornCount >= 100) { | |
birthCount = uint(birthPerHundred).mul(v.unicornCount / 100); | |
} else if (v.unicornCount >= 10) { | |
birthCount = uint(birthPerTen).mul(v.unicornCount / 10); | |
} | |
} | |
if (birthCount > 0) { | |
uint availableUnicorns = cardboardUnicorns.balanceOf(address(this)) - visitingUnicorns; | |
if (availableUnicorns < birthCount) { | |
birthCount = availableUnicorns; | |
} | |
unicornsToReturn = unicornsToReturn.add(birthCount); | |
} | |
// Update the status of the Visit | |
v.state = VisitState.Completed; | |
v.completedBlock = block.number; | |
v.completedCount = unicornsToReturn; | |
bookings[msg.sender][_index] = v; | |
// Transfer the asset back to the owner | |
visitingUnicorns = visitingUnicorns.sub(v.unicornCount); | |
cardboardUnicorns.transfer(msg.sender, unicornsToReturn); | |
// Send event about this update | |
BookingUpdate(msg.sender, _index, VisitState.Completed, unicornsToReturn); | |
} | |
function repossessBooking(address _who, uint _index) { | |
require(bookings[_who].length > _index); // Address in question must have at least this many bookings | |
Visit storage v = bookings[_who][_index]; | |
require(block.number > v.expiresBlock.add(repossessionBlocks)); // Repossession time must be past | |
require(v.state == VisitState.InProgress); // Visit must not be complete or repossessed | |
visitingUnicorns = visitingUnicorns.sub(v.unicornCount); | |
// Send event about this update | |
BookingUpdate(_who, _index, VisitState.Repossessed, v.unicornCount); | |
// Calculate Bounty amount | |
uint bountyCount = 1; | |
if (v.unicornCount >= 100) { | |
bountyCount = uint(repossessionBountyPerHundred).mul(v.unicornCount / 100); | |
} else if (v.unicornCount >= 10) { | |
bountyCount = uint(repossessionBountyPerTen).mul(v.unicornCount / 10); | |
} | |
// Send bounty to bounty hunter | |
ERC20Token cardboardUnicorns = ERC20Token(cardboardUnicornTokenAddress); | |
cardboardUnicorns.transfer(msg.sender, bountyCount); | |
// Send event about the bounty payout | |
RepossessionBounty(msg.sender, bountyCount); | |
// Update the status of the Visit | |
v.state = VisitState.Repossessed; | |
v.completedBlock = block.number; | |
v.completedCount = v.unicornCount - bountyCount; | |
bookings[_who][_index] = v; | |
} | |
function availableBalance(address _who) internal returns (uint) { | |
ERC20Token cardboardUnicorns = ERC20Token(cardboardUnicornTokenAddress); | |
uint count = cardboardUnicorns.allowance(_who, address(this)); | |
if (count == 0) { | |
return 0; | |
} | |
uint balance = cardboardUnicorns.balanceOf(_who); | |
if (balance < count) { | |
return balance; | |
} | |
return count; | |
} | |
function() payable { | |
if (cardboardUnicornTokenAddress == 0) { | |
return; | |
} | |
return donateUnicorns(availableBalance(msg.sender)); | |
} | |
function donateUnicorns(uint _unicornCount) payable { | |
if (_unicornCount == 0) { | |
return; | |
} | |
ERC20Token cardboardUnicorns = ERC20Token(cardboardUnicornTokenAddress); | |
cardboardUnicorns.transferFrom(msg.sender, address(this), _unicornCount); | |
DonationReceived(msg.sender, _unicornCount); | |
} | |
/** | |
* Change ownership of the Ranch | |
*/ | |
function changeOwner(address _newOwner) onlyOwner { | |
owner = _newOwner; | |
} | |
/** | |
* Change the outside contracts used by this contract | |
*/ | |
function changeCardboardUnicornTokenAddress(address _newTokenAddress) onlyOwner { | |
cardboardUnicornTokenAddress = _newTokenAddress; | |
} | |
function changeGroveAddress(address _newAddress) onlyOwner { | |
groveAddress = _newAddress; | |
} | |
/** | |
* Update block durations for various types of visits | |
*/ | |
function changeVisitLengths(uint _spa, uint _afternoon, uint _day, uint _overnight, uint _week, uint _extended) onlyOwner { | |
visitLength[uint8(VisitType.Spa)] = _spa; | |
visitLength[uint8(VisitType.Afternoon)] = _afternoon; | |
visitLength[uint8(VisitType.Day)] = _day; | |
visitLength[uint8(VisitType.Overnight)] = _overnight; | |
visitLength[uint8(VisitType.Week)] = _week; | |
visitLength[uint8(VisitType.Extended)] = _extended; | |
} | |
/** | |
* Update ether costs for various types of visits | |
*/ | |
function changeVisitCosts(uint _spa, uint _afternoon, uint _day, uint _overnight, uint _week, uint _extended) onlyOwner { | |
visitCost[uint8(VisitType.Spa)] = _spa; | |
visitCost[uint8(VisitType.Afternoon)] = _afternoon; | |
visitCost[uint8(VisitType.Day)] = _day; | |
visitCost[uint8(VisitType.Overnight)] = _overnight; | |
visitCost[uint8(VisitType.Week)] = _week; | |
visitCost[uint8(VisitType.Extended)] = _extended; | |
} | |
/** | |
* Update bounty reward settings | |
*/ | |
function changeRepoSettings(uint _repoBlocks, uint8 _repoPerTen, uint8 _repoPerHundred) onlyOwner { | |
repossessionBlocks = _repoBlocks; | |
repossessionBountyPerTen = _repoPerTen; | |
repossessionBountyPerHundred = _repoPerHundred; | |
} | |
/** | |
* Update birth event settings | |
*/ | |
function changeBirthSettings(uint _birthBlocks, uint8 _birthPerTen, uint8 _birthPerHundred) onlyOwner { | |
birthBlockThreshold = _birthBlocks; | |
birthPerTen = _birthPerTen; | |
birthPerHundred = _birthPerHundred; | |
} | |
function withdraw() onlyOwner { | |
owner.transfer(this.balance); // Send all ether in this contract to this contract's owner | |
} | |
function withdrawForeignTokens(address _tokenContract) onlyOwner { | |
ERC20Token token = ERC20Token(_tokenContract); | |
token.transfer(owner, token.balanceOf(address(this))); // Send all owned tokens to this contract's owner | |
} | |
} |
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.11; | |
contract ERC20Token { | |
function balanceOf(address _who) constant returns (uint balance); | |
function transferFrom(address _from, address _to, uint _value); | |
function transfer(address _to, uint _value); | |
} | |
contract UnicornRanch { | |
enum VisitType { Spa, Afternoon, Day, Overnight, Week, Extended } | |
enum VisitState { InProgress, Completed, Repossessed } | |
function getBooking(address _who, uint _index) constant returns (uint _unicornCount, VisitType _type, uint _startBlock, uint _expiresBlock, VisitState _state, uint _completedBlock, uint _completedCount); | |
} | |
/** | |
* Math operations with safety checks | |
*/ | |
library SafeMath { | |
function mul(uint a, uint b) internal returns (uint) { | |
uint c = a * b; | |
assert(a == 0 || c / a == b); | |
return c; | |
} | |
function div(uint a, uint b) internal returns (uint) { | |
// assert(b > 0); // Solidity automatically throws when dividing by 0 | |
uint c = a / b; | |
// assert(a == b * c + a % b); // There is no case in which this doesn't hold | |
return c; | |
} | |
function sub(uint a, uint b) internal returns (uint) { | |
assert(b <= a); | |
return a - b; | |
} | |
function add(uint a, uint b) internal returns (uint) { | |
uint c = a + b; | |
assert(c >= a); | |
return c; | |
} | |
function max64(uint64 a, uint64 b) internal constant returns (uint64) { | |
return a >= b ? a : b; | |
} | |
function min64(uint64 a, uint64 b) internal constant returns (uint64) { | |
return a < b ? a : b; | |
} | |
function max256(uint256 a, uint256 b) internal constant returns (uint256) { | |
return a >= b ? a : b; | |
} | |
function min256(uint256 a, uint256 b) internal constant returns (uint256) { | |
return a < b ? a : b; | |
} | |
} | |
contract UnicornRefunds { | |
using SafeMath for uint; | |
address public cardboardUnicornTokenAddress; | |
address public unicornRanchAddress; | |
address public owner = msg.sender; | |
uint public pricePerUnicorn = 1 finney; | |
uint public rewardUnicornAmount = 100; | |
mapping(address => uint) allowedAmounts; | |
mapping(address => bool) rewardClaimed; | |
event RewardClaimed(address indexed _who, uint _bookingIndex); | |
event UnicornsSold(address indexed _who, uint _unicornCount, uint _unicornCost, uint _paymentTotal); | |
event DonationReceived(address indexed _who, uint _amount, uint _allowanceEarned); | |
modifier onlyOwner { | |
require(msg.sender == owner); | |
_; | |
} | |
function getAllowedAmount(address _who) constant returns (uint _amount) { | |
return allowedAmounts[_who]; | |
} | |
function claimReward(uint _bookingIndex) { | |
UnicornRanch ranch = UnicornRanch(unicornRanchAddress); | |
var (unicornCount, visitType, , , state, , completedCount) = ranch.getBooking(msg.sender, _bookingIndex); | |
require(state == UnicornRanch.VisitState.Completed); // Must be a visit that's completed (not in progress or repossessed) | |
require(visitType != UnicornRanch.VisitType.Spa); // Must be longer than a Spa visit | |
require(completedCount > unicornCount); // Must have triggered the "birth" conditions so the user went home with more than what they send in | |
require(rewardClaimed[msg.sender] == false); // Must not have already claimed the reward | |
rewardClaimed[msg.sender] = true; | |
allowedAmounts[msg.sender] = allowedAmounts[msg.sender].add(rewardUnicornAmount); | |
RewardClaimed(msg.sender, _bookingIndex); | |
} | |
/** | |
* Sell back a number of unicorn tokens, in exchange for ether. | |
*/ | |
function sell(uint _unicornCount) { | |
require(_unicornCount > 0); | |
allowedAmounts[msg.sender] = allowedAmounts[msg.sender].sub(_unicornCount); | |
ERC20Token cardboardUnicorns = ERC20Token(cardboardUnicornTokenAddress); | |
cardboardUnicorns.transferFrom(msg.sender, owner, _unicornCount); // Transfer the actual asset | |
uint total = pricePerUnicorn.mul(_unicornCount); | |
msg.sender.transfer(total); | |
UnicornsSold(msg.sender, _unicornCount, pricePerUnicorn, total); | |
} | |
function() payable { | |
uint count = (msg.value).div(pricePerUnicorn); | |
allowedAmounts[msg.sender] = allowedAmounts[msg.sender].add(count); | |
DonationReceived(msg.sender, msg.value, count); | |
} | |
/** | |
* Change ownership | |
*/ | |
function changeOwner(address _newOwner) onlyOwner { | |
owner = _newOwner; | |
} | |
/** | |
* Change the outside contracts used by this contract | |
*/ | |
function changeCardboardUnicornTokenAddress(address _newTokenAddress) onlyOwner { | |
cardboardUnicornTokenAddress = _newTokenAddress; | |
} | |
function changeUnicornRanchAddress(address _newAddress) onlyOwner { | |
unicornRanchAddress = _newAddress; | |
} | |
/** | |
* Update unicorn price | |
*/ | |
function changePricePerUnicorn(uint _newPrice) onlyOwner { | |
pricePerUnicorn = _newPrice; | |
} | |
/** | |
* Update reward amount | |
*/ | |
function changeRewardAmount(uint _newAmount) onlyOwner { | |
rewardUnicornAmount = _newAmount; | |
} | |
function setAllowance(address _who, uint _amount) onlyOwner { | |
allowedAmounts[_who] = _amount; | |
} | |
function withdraw() onlyOwner { | |
owner.transfer(this.balance); // Send all ether in this contract to this contract's owner | |
} | |
function withdrawForeignTokens(address _tokenContract) onlyOwner { | |
ERC20Token token = ERC20Token(_tokenContract); | |
token.transfer(owner, token.balanceOf(address(this))); // Send all owned tokens to this contract's owner | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Deployed on main blockchain at:
0x2DC588Af5eff094401b1F8016E74E7D46d9E7A31
-- CardboardUnicornAssembler (unicornsRus.eth)0x69b5357D893fcE82248E83CCd9B0871F5D5d9461
-- CardboardUnicorns (Token)0x97E7ef310499bE2f97C392B8B16Ddd494Af73E22
-- UnicornRanch (unicornRanch.eth)0x2F490751589DB68f3C406bF9c14C95Ec7fa26840
-- UnicornRefundsDeployed on Rinkeby at:
0x13f03daf3884d2924b31047723330599Aa59BC3A
-- CardboardUnicornAssembler0x9308ea90D7A92E0430F2B03e501119Bc7c3bD8fc
-- CardboardUnicorns (Token)