Source file ../contracts/FxxxLandRush.sol.
// BK Ok
pragma solidity ^0.4.25;
// ----------------------------------------------------------------------------
// Fxxx Land Rush Contract - Purchase land parcels with GZE and ETH
//
// Enjoy.
//
// (c) BokkyPooBah / Bok Consulting Pty Ltd for GazeCoin 2018. The MIT Licence.
// ----------------------------------------------------------------------------
// BK Next 5 Ok
import "Owned.sol";
import "SafeMath.sol";
import "BTTSTokenInterface110.sol";
import "PriceFeedInterface.sol";
import "BonusListInterface.sol";
// ----------------------------------------------------------------------------
// FxxxLandRush Contract
// ----------------------------------------------------------------------------
// BK Ok
contract FxxxLandRush is Owned, ApproveAndCallFallBack {
// BK Ok
using SafeMath for uint;
// BK Ok
uint private constant TENPOW18 = 10 ** 18;
// BK Next 5 Ok
BTTSTokenInterface public parcelToken;
BTTSTokenInterface public gzeToken;
PriceFeedInterface public ethUsdPriceFeed;
PriceFeedInterface public gzeEthPriceFeed;
BonusListInterface public bonusList;
// BK Next 8 Ok
address public wallet;
uint public startDate;
uint public endDate;
uint public maxParcels;
uint public parcelUsd; // USD per parcel, e.g., USD 1,500 * 10^18
uint public usdLockAccountThreshold; // e.g., USD 7,000 * 10^18
uint public gzeBonusOffList; // e.g., 20 = 20% bonus
uint public gzeBonusOnList; // e.g., 30 = 30% bonus
// BK Next 4 Ok
uint public parcelsSold;
uint public contributedGze;
uint public contributedEth;
bool public finalised;
// BK Next 9 Ok - Events
event WalletUpdated(address indexed oldWallet, address indexed newWallet);
event StartDateUpdated(uint oldStartDate, uint newStartDate);
event EndDateUpdated(uint oldEndDate, uint newEndDate);
event MaxParcelsUpdated(uint oldMaxParcels, uint newMaxParcels);
event ParcelUsdUpdated(uint oldParcelUsd, uint newParcelUsd);
event UsdLockAccountThresholdUpdated(uint oldUsdLockAccountThreshold, uint newUsdLockAccountThreshold);
event GzeBonusOffListUpdated(uint oldGzeBonusOffList, uint newGzeBonusOffList);
event GzeBonusOnListUpdated(uint oldGzeBonusOnList, uint newGzeBonusOnList);
event Purchased(address indexed addr, uint parcels, uint gzeToTransfer, uint ethToTransfer, uint parcelsSold, uint contributedGze, uint contributedEth, bool lockAccount);
// BK Ok - Constructor
constructor(address _parcelToken, address _gzeToken, address _ethUsdPriceFeed, address _gzeEthPriceFeed, address _bonusList, address _wallet, uint _startDate, uint _endDate, uint _maxParcels, uint _parcelUsd, uint _usdLockAccountThreshold, uint _gzeBonusOffList, uint _gzeBonusOnList) public {
// BK Next 5 Ok. _gzeBonus*List don't need to be `require(...)`-d as they can be 0
require(_parcelToken != address(0) && _gzeToken != address(0));
require(_ethUsdPriceFeed != address(0) && _gzeEthPriceFeed != address(0) && _bonusList != address(0));
require(_wallet != address(0));
require(_startDate >= now && _endDate > _startDate);
require(_maxParcels > 0 && _parcelUsd > 0);
// BK Ok - Initialisation executed
initOwned(msg.sender);
// BK Next 5 Ok
parcelToken = BTTSTokenInterface(_parcelToken);
gzeToken = BTTSTokenInterface(_gzeToken);
ethUsdPriceFeed = PriceFeedInterface(_ethUsdPriceFeed);
gzeEthPriceFeed = PriceFeedInterface(_gzeEthPriceFeed);
bonusList = BonusListInterface(_bonusList);
// BK Next 7 Ok
wallet = _wallet;
startDate = _startDate;
endDate = _endDate;
maxParcels = _maxParcels;
parcelUsd = _parcelUsd;
usdLockAccountThreshold = _usdLockAccountThreshold;
gzeBonusOffList = _gzeBonusOffList;
gzeBonusOnList = _gzeBonusOnList;
}
// BK Ok - Only owner can execute
function setWallet(address _wallet) public onlyOwner {
// BK Ok
require(!finalised);
// BK Ok
require(_wallet != address(0));
// BK Ok - Log event
emit WalletUpdated(wallet, _wallet);
// BK Ok
wallet = _wallet;
}
// BK Ok - Only owner can execute
function setStartDate(uint _startDate) public onlyOwner {
// BK Ok
require(!finalised);
// BK Ok
require(_startDate >= now);
// BK Ok - Log event
emit StartDateUpdated(startDate, _startDate);
// BK Ok
startDate = _startDate;
}
// BK Ok - Only owner can execute
function setEndDate(uint _endDate) public onlyOwner {
// BK Ok
require(!finalised);
// BK Ok
require(_endDate > startDate);
// BK Ok - Log event
emit EndDateUpdated(endDate, _endDate);
// BK Ok
endDate = _endDate;
}
// BK Ok - Only owner can execute
function setMaxParcels(uint _maxParcels) public onlyOwner {
// BK Ok
require(!finalised);
// BK Ok
require(_maxParcels >= parcelsSold);
// BK Ok - Log event
emit MaxParcelsUpdated(maxParcels, _maxParcels);
// BK Ok
maxParcels = _maxParcels;
}
// BK Ok - Only owner can execute
function setParcelUsd(uint _parcelUsd) public onlyOwner {
// BK Ok
require(!finalised);
// BK Ok
require(_parcelUsd > 0);
// BK Ok - Log event
emit ParcelUsdUpdated(parcelUsd, _parcelUsd);
// BK Ok
parcelUsd = _parcelUsd;
}
// BK Ok - Only owner can execute
function setUsdLockAccountThreshold(uint _usdLockAccountThreshold) public onlyOwner {
// BK Ok
require(!finalised);
// BK Ok - Log event
emit UsdLockAccountThresholdUpdated(usdLockAccountThreshold, _usdLockAccountThreshold);
// BK Ok
usdLockAccountThreshold = _usdLockAccountThreshold;
}
// BK Ok - Only owner can execute
function setGzeBonusOffList(uint _gzeBonusOffList) public onlyOwner {
// BK Ok
require(!finalised);
// BK Ok - Log event
emit GzeBonusOffListUpdated(gzeBonusOffList, _gzeBonusOffList);
// BK Ok
gzeBonusOffList = _gzeBonusOffList;
}
// BK Ok - Only owner can execute
function setGzeBonusOnList(uint _gzeBonusOnList) public onlyOwner {
// BK Ok
require(!finalised);
// BK Ok - Log event
emit GzeBonusOnListUpdated(gzeBonusOnList, _gzeBonusOnList);
// BK Ok
gzeBonusOnList = _gzeBonusOnList;
}
// BK Ok - View function
function symbol() public view returns (string _symbol) {
// BK Ok
_symbol = parcelToken.symbol();
}
// BK Ok - View function
function name() public view returns (string _name) {
// BK Ok
_name = parcelToken.name();
}
// USD per ETH, e.g., 221.99 * 10^18
// BK Ok - View function
function ethUsd() public view returns (uint _rate, bool _live) {
// BK Ok
return ethUsdPriceFeed.getRate();
}
// ETH per GZE, e.g., 0.00004366 * 10^18
// BK Ok - View function
function gzeEth() public view returns (uint _rate, bool _live) {
// BK Ok
return gzeEthPriceFeed.getRate();
}
// USD per GZE, e.g., 0.0096920834 * 10^18
// BK Ok - View function
function gzeUsd() public view returns (uint _rate, bool _live) {
// BK Next 2 Ok
uint _ethUsd;
bool _ethUsdLive;
// BK Ok
(_ethUsd, _ethUsdLive) = ethUsdPriceFeed.getRate();
// BK Next 2 Ok
uint _gzeEth;
bool _gzeEthLive;
// BK Ok
(_gzeEth, _gzeEthLive) = gzeEthPriceFeed.getRate();
// BK Ok
if (_ethUsdLive && _gzeEthLive) {
// BK Ok
_live = true;
// BK Ok
_rate = _ethUsd.mul(_gzeEth).div(TENPOW18);
}
}
// ETH per parcel, e.g., 6.757061128879679264 * 10^18
// BK Ok - View function
function parcelEth() public view returns (uint _rate, bool _live) {
// BK Ok
uint _ethUsd;
// BK Ok
(_ethUsd, _live) = ethUsd();
// BK Ok
if (_live) {
// BK Ok
_rate = parcelUsd.mul(TENPOW18).div(_ethUsd);
}
}
// GZE per parcel, without bonus, e.g., 154765.486231783766945298 * 10^18
// BK Ok - View function
function parcelGzeWithoutBonus() public view returns (uint _rate, bool _live) {
// BK Ok
uint _gzeUsd;
// BK Ok
(_gzeUsd, _live) = gzeUsd();
// BK Ok
if (_live) {
// BK Ok
_rate = parcelUsd.mul(TENPOW18).div(_gzeUsd);
}
}
// GZE per parcel, with bonus but not on bonus list, e.g., 128971.238526486472454415 * 10^18
// BK Ok - View function
function parcelGzeWithBonusOffList() public view returns (uint _rate, bool _live) {
// BK Ok
uint _parcelGzeWithoutBonus;
// BK Ok
(_parcelGzeWithoutBonus, _live) = parcelGzeWithoutBonus();
// BK Ok
if (_live) {
// BK Ok
_rate = _parcelGzeWithoutBonus.mul(100).div(gzeBonusOffList.add(100));
}
}
// GZE per parcel, with bonus and on bonus list, e.g., 119050.374024449051496383 * 10^18
// BK Ok - View function
function parcelGzeWithBonusOnList() public view returns (uint _rate, bool _live) {
// BK Ok
uint _parcelGzeWithoutBonus;
// BK Ok
(_parcelGzeWithoutBonus, _live) = parcelGzeWithoutBonus();
// BK Ok
if (_live) {
// BK Ok
_rate = _parcelGzeWithoutBonus.mul(100).div(gzeBonusOnList.add(100));
}
}
// Account contributes by:
// 1. calling GZE.approve(landRushAddress, tokens)
// 2. calling this.purchaseWithGze(tokens)
// BK Ok - Any account can purchase parcels, but must have approve(...)-d the right amount of GZE tokens
function purchaseWithGze(uint256 tokens) public {
// BK Ok
require(gzeToken.allowance(msg.sender, this) >= tokens);
// BK Ok
receiveApproval(msg.sender, tokens, gzeToken, "");
}
// Account contributes by calling GZE.approveAndCall(landRushAddress, tokens, "")
// BK Ok - Any account can purchase parcels with GZE.approveAndCall(landRushAddress, tokens, "")
function receiveApproval(address from, uint256 tokens, address token, bytes /* data */) public {
// BK Ok
require(now >= startDate && now <= endDate);
// BK Ok
require(token == address(gzeToken));
// BK Next 2 Ok
uint _parcelGze;
bool _live;
// BK Ok
if (bonusList.isInBonusList(from)) {
// BK Ok
(_parcelGze, _live) = parcelGzeWithBonusOnList();
// BK Ok
} else {
// BK Ok
(_parcelGze, _live) = parcelGzeWithBonusOffList();
}
// BK Ok
require(_live);
// BK Ok
uint parcels = tokens.div(_parcelGze);
// BK Ok
if (parcelsSold.add(parcels) >= maxParcels) {
// BK Ok
parcels = maxParcels.sub(parcelsSold);
}
uint gzeToTransfer = parcels.mul(_parcelGze);
// BK Ok
contributedGze = contributedGze.add(gzeToTransfer);
// BK Ok
require(ERC20Interface(token).transferFrom(from, wallet, gzeToTransfer));
// BK Ok
bool lock = mintParcelTokens(from, parcels);
// BK Ok - Log event
emit Purchased(from, parcels, gzeToTransfer, 0, parcelsSold, contributedGze, contributedEth, lock);
}
// Account contributes by sending ETH
// BK Ok - Any account can purchase with ETH
function () public payable {
// BK Ok
require(now >= startDate && now <= endDate);
// BK Next 2 Ok
uint _parcelEth;
bool _live;
// BK Ok
(_parcelEth, _live) = parcelEth();
// BK Ok
require(_live);
// BK Ok
uint parcels = msg.value.div(_parcelEth);
// BK Ok
if (parcelsSold.add(parcels) >= maxParcels) {
// BK Ok
parcels = maxParcels.sub(parcelsSold);
}
uint ethToTransfer = parcels.mul(_parcelEth);
// BK Ok
contributedEth = contributedEth.add(ethToTransfer);
// BK Ok
uint ethToRefund = msg.value.sub(ethToTransfer);
// BK Ok
if (ethToRefund > 0) {
// BK Ok
msg.sender.transfer(ethToRefund);
}
// BK Ok
bool lock = mintParcelTokens(msg.sender, parcels);
// BK Ok - Log event
emit Purchased(msg.sender, parcels, 0, ethToTransfer, parcelsSold, contributedGze, contributedEth, lock);
}
// Contract owner allocates parcels to tokenOwner for offline purchase
// BK Ok - Owner can mint for offline purchases
function offlinePurchase(address tokenOwner, uint parcels) public onlyOwner {
// BK Ok
require(!finalised);
// BK Ok
if (parcelsSold.add(parcels) >= maxParcels) {
// BK Ok
parcels = maxParcels.sub(parcelsSold);
}
// BK Ok
bool lock = mintParcelTokens(tokenOwner, parcels);
// BK Ok - Log event
emit Purchased(tokenOwner, parcels, 0, 0, parcelsSold, contributedGze, contributedEth, lock);
}
// Internal function to mint tokens and disable minting if maxParcels sold
// BK Ok - Internal function
function mintParcelTokens(address account, uint parcels) internal returns (bool _lock) {
// BK Ok
require(parcels > 0);
// BK Ok
parcelsSold = parcelsSold.add(parcels);
// BK Ok
_lock = parcelToken.balanceOf(account).add(parcelUsd.mul(parcels)) >= usdLockAccountThreshold;
// BK Ok
require(parcelToken.mint(account, parcelUsd.mul(parcels), _lock));
// BK Ok
if (parcelsSold >= maxParcels) {
// BK Ok
parcelToken.disableMinting();
// BK Ok
finalised = true;
}
}
// Contract owner finalises to disable parcel minting
// BK Ok - Only owner can execute
function finalise() public onlyOwner {
// BK Ok
require(!finalised);
// BK Ok
require(now > endDate || parcelsSold >= maxParcels);
// BK Ok
parcelToken.disableMinting();
// BK Ok
finalised = true;
}
}