Skip to content

Instantly share code, notes, and snippets.

@yanxi-me

yanxi-me/Cox.col

Created Sep 16, 2018
Embed
What would you like to do?
pragma solidity ^0.4.17;
import './SafeMath.sol';
import './Holder.sol';
import './Round.sol';
import './Dealer.sol';
import './Ownable.sol';
contract Cox is Ownable {
using SafeMath for uint256;
using HolderLib for HolderLib.Holder;
using RoundLib for RoundLib.Round;
using DealerLib for DealerLib.Dealers;
event RoundIn(address addr, uint256 amt, uint256 currentRoundRaised, uint256 round, uint256 bigRound, string refCode);
event Log(string str, uint256 value);
event PoolAdd(uint256 value);
event PoolSub(uint256 value);
// 可配置参数
uint256 private roundDuration = 1 days;
uint256 private initSize = 10 ether; // 首轮募集数量
uint256 private minRecharge = 0.01 ether; // 最小充值数量
bool private mIsActive = false; // 游戏是否接受充值和提现
bool private isAutoRestart = true; // 最后一轮结束后,是否马上重新开始第一轮
uint256 private rate = 300; // 万分之,300 即 3%
string private defaultRefCode = "owner";
DealerLib.Dealers private dealers; // 渠道商信息
HolderLib.Holder private coinHolders; // 投资人信息
RoundLib.Round[] private roundList;
uint256 private fundPoolSize; // 奖池金额
uint256 private roundStartTime;
uint256 private roundEndTime;
uint256 private bigRound = 1;
uint256 private totalAmountInvested = 0;
constructor() public {
roundList.push(RoundLib.getInitRound(initSize));
dealers.insert(defaultRefCode, msg.sender, 100);
}
function transferOwnership(address _newOwner) public onlyOwner {
Ownable.transferOwnership(_newOwner);
dealers.update(defaultRefCode, _newOwner, 100);
}
function poolAdd(uint256 value) private {
fundPoolSize = fundPoolSize.add(value);
emit PoolAdd(value);
}
function poolSub(uint256 value) private {
fundPoolSize = fundPoolSize.sub(value);
emit PoolSub(value);
}
modifier isActive() {
require(mIsActive == true, "it's not ready yet.");
require(now >= roundStartTime, "it's not started yet.");
_;
}
modifier callFromHuman(address addr) {
uint size;
assembly {size := extcodesize(addr)}
require(size == 0, "not human");
_;
}
// 充值
function recharge(string code) public isActive callFromHuman(msg.sender) payable {
require(msg.value >= minRecharge, "not enough fund");
string memory _code = coinHolders.getRefCode(msg.sender);
if (bytes(_code).length > 0) {
code = _code;
} else {
if (!dealers.dealerExisted(code)) {
code = defaultRefCode;
}
coinHolders.setRefCode(msg.sender, code);
}
coinHolders.add(msg.sender, msg.value);
roundIn(msg.value, code);
}
function moveRoundsToHolder(address adrs) internal {
HolderLib.HolderValue storage holderValue = coinHolders.holderMap[adrs];
uint256[] memory roundIds = holderValue.relatedRoundIds;
uint256 roundId;
for (uint256 i = holderValue.fromIndex; i < roundIds.length; i++) {
roundId = roundIds[i];
roundList[roundId - 1].moveToHolder(adrs, coinHolders);
}
}
// 提现
function withdraw() public callFromHuman(msg.sender) {
moveRoundsToHolder(msg.sender);
uint256 amount = coinHolders.getNum(msg.sender);
if (amount > 0) {
coinHolders.sub(msg.sender, amount);
//transfer
msg.sender.transfer(amount);
}
}
// 余额再投资
function roundIn() public isActive {
string memory code = coinHolders.getRefCode(msg.sender);
require(bytes(code).length > 0, "code must not be empty");
require(dealers.dealerExisted(code), "dealer not exist");
moveRoundsToHolder(msg.sender);
uint256 amount = coinHolders.getNum(msg.sender);
require(amount > 0, "your balance is 0");
roundIn(amount, code);
}
// 结束本局
function endRound() public isActive {
RoundLib.Round storage curRound = roundList[roundList.length - 1];
endRoundWhenTimeout(curRound);
}
function endRoundWhenTimeout(RoundLib.Round storage curRound) private isActive {
if (now >= roundEndTime) {
uint256 preRoundMax = 0;
if (curRound.roundNum > 1) {
RoundLib.Round storage preRound = roundList[roundList.length - 2];
preRoundMax = preRound.max;
}
uint256 last2RoundsRaised = preRoundMax + curRound.raised;
if (last2RoundsRaised > 0) {
curRound.pot = curRound.raised * fundPoolSize / last2RoundsRaised;
if (curRound.roundNum > 1) {
preRound.pot = preRound.raised * fundPoolSize / last2RoundsRaised;
poolSub(preRound.pot);
}
poolSub(curRound.pot);
}
mIsActive = isAutoRestart;
startNextRound(curRound);
bigRound++;
}
}
function startNextRound(RoundLib.Round storage curRound) private {
roundList.push(curRound.getNextRound(initSize));
roundStartTime = now;
roundEndTime = now + roundDuration;
}
function roundIn(uint256 amt, string code) private isActive {
require(coinHolders.getNum(msg.sender) >= amt, "not enough coin");
RoundLib.Round storage curRound = roundList[roundList.length - 1];
if (now >= roundEndTime) {
endRoundWhenTimeout(curRound);
return;
}
(bool isFinished, uint256 amountUsed) = curRound.add(msg.sender, amt);
totalAmountInvested = totalAmountInvested.add(amountUsed);
require(amountUsed > 0, 'amountUsed must greater than 0');
emit RoundIn(msg.sender, amountUsed, curRound.raised, curRound.roundNum, bigRound, code);
// 添加关联 roundId
coinHolders.addRelatedRoundId(msg.sender, curRound.roundId);
coinHolders.sub(msg.sender, amountUsed);
// 分配维护资金和代理商
uint256 amountToDealer = dealers.addAmount(code, amountUsed);
uint256 amountToOwner = (amountUsed * rate / 10000).sub(amountToDealer);
coinHolders.add(owner, amountToOwner);
coinHolders.add(dealers.query(code).addr, amountToDealer);
poolAdd(amountUsed.sub(amountToDealer).sub(amountToOwner));
if (isFinished) {
if (curRound.roundNum > 1) {
RoundLib.Round storage preRound2 = roundList[roundList.length - 2];
preRound2.pot = preRound2.max * 11 / 10;
poolSub(preRound2.pot);
}
startNextRound(curRound);
}
}
function verifyCodeLength(string code) public pure returns (bool) {
return bytes(code).length >= 4 && bytes(code).length <= 20;
}
function addDealer(string code, address addr, uint256 _rate) public onlyOwner {
require(verifyCodeLength(code), "code length should between 4 and 20");
dealers.insert(code, addr, _rate);
}
function addDealerForSender(string code) public {
require(verifyCodeLength(code), "code length should between 4 and 20");
dealers.insert(code, msg.sender, 100);
}
function getDealerInfo(string code) public view returns (string _code, address adrs, uint256 amount, uint256 _rate) {
DealerLib.DealerInfo storage dealer = dealers.query(code);
return (code, dealer.addr, dealer.amount, dealer.rate);
}
function updateDealer(string code, address addr, uint256 _rate) public onlyOwner {
dealers.update(code, addr, _rate);
}
function setIsAutoRestart(bool isAuto) public onlyOwner {
isAutoRestart = isAuto;
}
function setMinRecharge(uint256 a) public onlyOwner {
minRecharge = a;
}
function setRoundDuration(uint256 a) public onlyOwner {
roundDuration = a;
}
function setInitSize(uint256 size) public onlyOwner {
initSize = size;
RoundLib.Round storage curRound = roundList[roundList.length - 1];
if (curRound.roundNum == 1 && curRound.raised < size) {
curRound.max = size;
}
}
function activate() public onlyOwner {
// can only be ran once
require(mIsActive == false, "already activated");
// activate the contract
mIsActive = true;
roundStartTime = now;
roundEndTime = now + roundDuration;
}
function setStartTime(uint256 startTime) public onlyOwner {
roundStartTime = startTime;
roundEndTime = roundStartTime + roundDuration;
}
function deactivate() public onlyOwner {
require(mIsActive == true, "already deactivated");
mIsActive = false;
}
function getGlobalInfo() public view returns
(bool _isActive, bool _isAutoRestart, uint256 _round, uint256 _bigRound,
uint256 _curRoundSize, uint256 _curRoundRaised, uint256 _fundPoolSize,
uint256 _roundStartTime, uint256 _roundEndTime, uint256 _totalAmountInvested) {
RoundLib.Round storage curRound = roundList[roundList.length - 1];
return (mIsActive, isAutoRestart, curRound.roundNum, bigRound,
curRound.max, curRound.raised, fundPoolSize,
roundStartTime, roundEndTime, totalAmountInvested);
}
function getMyInfo() public view
returns (address ethAddress, uint256 balance, uint256 preRoundAmount, uint256 curRoundAmount,
string dealerCode, uint256 dealerAmount, uint256 dealerRate) {
return getAddressInfo(msg.sender);
}
function getAddressInfo(address _address) public view
returns (address ethAddress, uint256 balance, uint256 preRoundAmount, uint256 curRoundAmount,
string dealerCode, uint256 dealerAmount, uint256 dealerRate) {
RoundLib.Round storage curRound = roundList[roundList.length - 1];
preRoundAmount = 0;
if (curRound.roundNum > 1) {
RoundLib.Round storage preRound = roundList[roundList.length - 2];
preRoundAmount = preRound.getNum(_address);
}
(dealerCode, , dealerAmount, dealerRate) = getDealerInfo(dealers.queryCodeByAddress(_address));
return (_address, coinHolders.getNum(_address) + getBalanceFromRound(_address),
preRoundAmount, curRound.getNum(_address), dealerCode, dealerAmount, dealerRate);
}
function getBalanceFromRound(address adrs) internal view returns (uint256) {
HolderLib.HolderValue storage holderValue = coinHolders.holderMap[adrs];
uint256[] storage roundIds = holderValue.relatedRoundIds;
uint256 roundId;
uint256 balance = 0;
for (uint256 i = holderValue.fromIndex; i < roundIds.length; i++) {
roundId = roundIds[i];
balance += roundList[roundId - 1].getBalance(adrs);
}
return balance;
}
function getRoundInfo(uint256 roundId, uint256 page) public view
returns (uint256 _roundId, uint256 roundNum, uint256 max, uint256 raised, uint256 pot,
uint256 count, address[] addressList, uint256[] numList) {
RoundLib.Round storage round = roundList[roundId - 1];
_roundId = round.roundId;
roundNum = round.roundNum;
max = round.max;
raised = round.raised;
pot = round.pot;
(count, addressList, numList) = round.getInvestList(page);
}
}
pragma solidity ^0.4.17;
import './SafeMath.sol';
library DealerLib {
using SafeMath for uint256;
struct DealerInfo {
address addr;
uint256 amount;
uint256 rate; // 万分之,必须不大于 200,即 2%
}
struct Dealers {
mapping (string => DealerInfo) dealerMap;
mapping (address => string) addressToCodeMap;
}
function query(Dealers storage dealers, string code) internal view returns (DealerInfo storage) {
return dealers.dealerMap[code];
}
function queryCodeByAddress(Dealers storage dealers, address adrs) internal view returns (string code) {
return dealers.addressToCodeMap[adrs];
}
function dealerExisted(Dealers storage dealers, string code) internal view returns (bool value) {
return dealers.dealerMap[code].addr != 0x0;
}
function insert(Dealers storage dealers, string code, address addr, uint256 rate) internal {
require(!dealerExisted(dealers, code), "code existed");
require(bytes(queryCodeByAddress(dealers, addr)).length == 0, "address existed in dealers");
setDealer(dealers, code, addr, rate);
}
function update(Dealers storage dealers, string code, address addr, uint256 rate) internal {
address oldAddr = dealers.dealerMap[code].addr;
require(oldAddr != 0x0, "code not exist");
require(bytes(queryCodeByAddress(dealers, addr)).length == 0, "address existed in dealers");
delete dealers.addressToCodeMap[oldAddr];
setDealer(dealers, code, addr, rate);
}
function setDealer(Dealers storage dealers, string code, address addr, uint256 rate) private {
require(addr != 0x0, "invalid address");
require(rate <= 300, "invalid rate");
dealers.addressToCodeMap[addr] = code;
dealers.dealerMap[code].addr = addr;
dealers.dealerMap[code].rate = rate;
}
function addAmount(Dealers storage dealers, string code, uint256 amountUsed) internal
returns (uint256 amountToDealer) {
require(amountUsed > 0, "amount must be greater than 0");
require(dealerExisted(dealers, code), "code not exist");
amountToDealer = amountUsed * dealers.dealerMap[code].rate / 10000;
dealers.dealerMap[code].amount = dealers.dealerMap[code].amount.add(amountToDealer);
}
}
pragma solidity ^0.4.17;
import './SafeMath.sol';
library HolderLib {
using SafeMath for uint256;
struct HolderValue {
uint256 value;
uint256[] relatedRoundIds;
uint256 fromIndex;
string refCode;
}
struct Holder {
mapping (address => HolderValue) holderMap;
}
function getNum(Holder storage holder, address adrs) internal view returns (uint256 num) {
return holder.holderMap[adrs].value;
}
function setRefCode(Holder storage holder, address adrs, string refCode) internal {
holder.holderMap[adrs].refCode = refCode;
}
function getRefCode(Holder storage holder, address adrs) internal view returns (string refCode) {
return holder.holderMap[adrs].refCode;
}
function add(Holder storage holder, address adrs, uint256 num) internal {
holder.holderMap[adrs].value = holder.holderMap[adrs].value.add(num);
}
function sub(Holder storage holder, address adrs, uint256 num) internal {
holder.holderMap[adrs].value = holder.holderMap[adrs].value.sub(num);
}
function setNum(Holder storage holder, address adrs, uint256 num) internal {
holder.holderMap[adrs].value = num;
}
function addRelatedRoundId(Holder storage holder, address adrs, uint256 roundId) internal {
uint256[] storage ids = holder.holderMap[adrs].relatedRoundIds;
if (ids.length > 0 && ids[ids.length - 1] == roundId) {
return;
}
ids.push(roundId);
}
function removeRelatedRoundId(Holder storage holder, address adrs, uint256 roundId) internal {
HolderValue storage value = holder.holderMap[adrs];
require(value.relatedRoundIds[value.fromIndex] == roundId, 'only the fromIndex element can be removed');
value.fromIndex++;
}
}
pragma solidity ^0.4.17;
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address public owner;
event OwnershipRenounced(address indexed previousOwner);
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
constructor() public {
owner = msg.sender;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param _newOwner The address to transfer ownership to.
*/
function transferOwnership(address _newOwner) public onlyOwner {
_transferOwnership(_newOwner);
}
/**
* @dev Transfers control of the contract to a newOwner.
* @param _newOwner The address to transfer ownership to.
*/
function _transferOwnership(address _newOwner) internal {
require(_newOwner != address(0));
emit OwnershipTransferred(owner, _newOwner);
owner = _newOwner;
}
}
pragma solidity ^0.4.17;
import './SafeMath.sol';
import './Holder.sol';
import './Table.sol';
library RoundLib {
using SafeMath for uint256;
using HolderLib for HolderLib.Holder;
using TableLib for TableLib.Table;
event Log(string str, uint256 v1, uint256 v2, uint256 v3);
uint256 constant private roundSizeIncreasePercent = 160;
struct Round {
uint256 roundId; // 一直累加,永不重复
uint256 roundNum; // 轮次,数字会重复
uint256 max; // 本轮应募集数量
TableLib.Table investers; // 投资者数据
uint256 raised; // 本轮已募集
uint256 pot; // 本轮需要分配的金额
}
function getInitRound(uint256 initSize) internal pure returns (Round) {
TableLib.Table memory investers;
return Round({
roundId: 1,
roundNum: 1,
max: initSize,
investers: investers,
raised: 0,
pot: 0
});
}
function getNextRound(Round storage round, uint256 initSize) internal view returns (Round) {
TableLib.Table memory investers;
bool isFinished = round.max == round.raised;
return Round({
roundId: round.roundId + 1,
roundNum: isFinished ? round.roundNum + 1 : 1,
max: isFinished ? round.max * roundSizeIncreasePercent / 100 : initSize,
investers: investers,
raised: 0,
pot: 0
});
}
function add (Round storage round, address adrs, uint256 amount) internal
returns (bool isFinished, uint256 amountUsed) {
if (round.raised + amount >= round.max) {
isFinished = true;
amountUsed = round.max - round.raised;
} else {
isFinished = false;
amountUsed = amount;
}
round.investers.add(adrs, amountUsed);
round.raised = round.raised.add(amountUsed);
}
function getNum(Round storage round, address adrs) internal view returns (uint256) {
return round.investers.getNum(adrs);
}
function getBalance(Round storage round, address adrs)
internal view returns (uint256) {
uint256 balance = round.investers.getNum(adrs);
if (balance == 0) {
return balance;
}
return balance * round.pot / round.raised;
}
function moveToHolder(Round storage round, address adrs, HolderLib.Holder storage coinHolders) internal {
if (round.pot == 0) {
return;
}
uint256 amount = getBalance(round, adrs);
if (amount > 0) {
coinHolders.add(adrs, amount);
coinHolders.removeRelatedRoundId(adrs, round.roundId);
}
}
function getInvestList(Round storage round, uint256 page) internal view
returns (uint256 count, address[] addressList, uint256[] numList) {
return round.investers.getValues(page);
}
}
pragma solidity ^0.4.17;
library SafeMath {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint256 a, uint256 b)
internal
pure
returns (uint256 c)
{
if (a == 0) {
return 0;
}
c = a * b;
require(c / a == b, "SafeMath mul failed");
return c;
}
/**
* @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b)
internal
pure
returns (uint256)
{
require(b <= a, "SafeMath sub failed");
return a - b;
}
/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint256 a, uint256 b)
internal
pure
returns (uint256 c)
{
c = a + b;
require(c >= a, "SafeMath add failed");
return c;
}
}
pragma solidity ^0.4.17;
import './SafeMath.sol';
library TableLib {
using SafeMath for uint256;
struct TableValue {
bool exists;
uint256 value;
}
struct Table {
mapping (address => TableValue) tableMapping;
address[] addressList;
}
function getNum(Table storage tbl, address adrs) internal view returns (uint256 num) {
return tbl.tableMapping[adrs].value;
}
function add(Table storage tbl, address adrs, uint256 num) internal {
if (!tbl.tableMapping[adrs].exists) {
tbl.addressList.push(adrs);
tbl.tableMapping[adrs].exists = true;
}
tbl.tableMapping[adrs].value = tbl.tableMapping[adrs].value.add(num);
}
function getValues(Table storage tbl, uint256 page) internal view
returns (uint256 count, address[] addressList, uint256[] numList) {
count = tbl.addressList.length;
uint256 maxPageSize = 50;
uint256 index = 0;
uint256 pageSize = maxPageSize;
if ( page*maxPageSize > count ) {
pageSize = count - (page-1)*maxPageSize;
}
addressList = new address[](pageSize);
numList = new uint256[](pageSize);
for (uint256 i = (page - 1) * maxPageSize; i < count && index < pageSize; i++) {
address adrs = tbl.addressList[i];
addressList[index] = adrs;
numList[index] = tbl.tableMapping[adrs].value;
index++;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment