/*
This file is part of the PROOF Contract.
The PROOF Contract is free software: you can redistribute it and/or
modify it under the terms of the GNU lesser General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The PROOF Contract is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU lesser General Public License for more details.
You should have received a copy of the GNU lesser General Public License
along with the PROOF Contract. If not, see .
@author Ilya Svirin
*/
pragma solidity ^0.4.0;
contract owned {
address public owner;
address public newOwner;
function owned() payable {
owner = msg.sender;
}
modifier onlyOwner {
require(owner == msg.sender);
_;
}
function changeOwner(address _owner) onlyOwner public {
require(_owner != 0);
newOwner = _owner;
}
function confirmOwner() public {
require(newOwner == msg.sender);
owner = newOwner;
delete newOwner;
}
}
/**
* @title ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
contract ERC20 {
uint public totalSupply;
function balanceOf(address who) constant returns (uint);
function transfer(address to, uint value);
function allowance(address owner, address spender) constant returns (uint);
function transferFrom(address from, address to, uint value);
function approve(address spender, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint value);
}
contract ManualMigration is owned, ERC20 {
mapping (address => uint) internal balances;
address public migrationHost;
function ManualMigration(address _migrationHost) payable owned() {
migrationHost = _migrationHost;
//balances[this] = ERC20(migrationHost).balanceOf(migrationHost);
}
function migrateManual(address _tokensHolder) onlyOwner {
require(migrationHost != 0);
uint tokens = ERC20(migrationHost).balanceOf(_tokensHolder);
tokens = tokens * 125 / 100;
balances[_tokensHolder] = tokens;
totalSupply += tokens;
Transfer(migrationHost, _tokensHolder, tokens);
}
function sealManualMigration() onlyOwner {
delete migrationHost;
}
}
/**
* @title Crowdsale implementation
*/
contract Crowdsale is ManualMigration {
uint public etherPrice;
address public crowdsaleOwner;
uint public totalLimitUSD;
uint public minimalSuccessUSD;
uint public collectedUSD;
enum State { Disabled, PreICO, CompletePreICO, Crowdsale, Enabled, Migration }
event NewState(State state);
State public state = State.Disabled;
uint public crowdsaleStartTime;
uint public crowdsaleFinishTime;
modifier enabledState {
require(state == State.Enabled);
_;
}
modifier enabledOrMigrationState {
require(state == State.Enabled || state == State.Migration);
_;
}
struct Investor {
uint amountTokens;
uint amountWei;
}
mapping (address => Investor) public investors;
mapping (uint => address) public investorsIter;
uint public numberOfInvestors;
function Crowdsale(address _migrationHost)
payable ManualMigration(_migrationHost) {
}
function () payable {
require(state == State.PreICO || state == State.Crowdsale);
require(now < crowdsaleFinishTime);
uint valueWei = msg.value;
uint valueUSD = valueWei * etherPrice / 1000000000000000000;
if (collectedUSD + valueUSD > totalLimitUSD) { // don't need so much ether
valueUSD = totalLimitUSD - collectedUSD;
valueWei = valueUSD * 1000000000000000000 / etherPrice;
require(msg.sender.call.gas(3000000).value(msg.value - valueWei)());
collectedUSD = totalLimitUSD; // to be sure!
} else {
collectedUSD += valueUSD;
}
mintTokens(msg.sender, valueUSD, valueWei);
}
function depositUSD(address _who, uint _valueUSD) public onlyOwner {
require(state == State.PreICO || state == State.Crowdsale);
require(now < crowdsaleFinishTime);
require(collectedUSD + _valueUSD <= totalLimitUSD);
collectedUSD += _valueUSD;
mintTokens(_who, _valueUSD, 0);
}
function mintTokens(address _who, uint _valueUSD, uint _valueWei) internal {
uint tokensPerUSD = 100;
if (state == State.PreICO) {
if (now < crowdsaleStartTime + 1 days && _valueUSD >= 50000) {
tokensPerUSD = 150;
} else {
tokensPerUSD = 125;
}
} else if (state == State.Crowdsale) {
if (now < crowdsaleStartTime + 1 days) {
tokensPerUSD = 115;
} else if (now < crowdsaleStartTime + 1 weeks) {
tokensPerUSD = 110;
}
}
uint tokens = tokensPerUSD * _valueUSD;
require(balances[_who] + tokens > balances[_who]); // overflow
require(tokens > 0);
Investor storage inv = investors[_who];
if (inv.amountTokens == 0) { // new investor
investorsIter[numberOfInvestors++] = _who;
}
inv.amountTokens += tokens;
inv.amountWei += _valueWei;
balances[_who] += tokens;
Transfer(this, _who, tokens);
totalSupply += tokens;
}
function startTokensSale(
address _crowdsaleOwner,
uint _crowdsaleDurationDays,
uint _totalLimitUSD,
uint _minimalSuccessUSD,
uint _etherPrice) public onlyOwner {
require(state == State.Disabled || state == State.CompletePreICO);
crowdsaleStartTime = now;
crowdsaleOwner = _crowdsaleOwner;
etherPrice = _etherPrice;
delete numberOfInvestors;
delete collectedUSD;
crowdsaleFinishTime = now + _crowdsaleDurationDays * 1 days;
totalLimitUSD = _totalLimitUSD;
minimalSuccessUSD = _minimalSuccessUSD;
if (state == State.Disabled) {
state = State.PreICO;
} else {
state = State.Crowdsale;
}
NewState(state);
}
function timeToFinishTokensSale() public constant returns(uint t) {
require(state == State.PreICO || state == State.Crowdsale);
if (now > crowdsaleFinishTime) {
t = 0;
} else {
t = crowdsaleFinishTime - now;
}
}
function finishTokensSale(uint _investorsToProcess) public {
require(state == State.PreICO || state == State.Crowdsale);
require(now >= crowdsaleFinishTime || collectedUSD == totalLimitUSD ||
(collectedUSD >= minimalSuccessUSD && msg.sender == owner));
if (collectedUSD < minimalSuccessUSD) {
// Investors can get their ether calling withdrawBack() function
while (_investorsToProcess > 0 && numberOfInvestors > 0) {
address addr = investorsIter[--numberOfInvestors];
Investor memory inv = investors[addr];
balances[addr] -= inv.amountTokens;
totalSupply -= inv.amountTokens;
Transfer(addr, this, inv.amountTokens);
--_investorsToProcess;
delete investorsIter[numberOfInvestors];
}
if (numberOfInvestors > 0) {
return;
}
if (state == State.PreICO) {
state = State.Disabled;
} else {
state = State.CompletePreICO;
}
} else {
while (_investorsToProcess > 0 && numberOfInvestors > 0) {
--numberOfInvestors;
--_investorsToProcess;
delete investors[investorsIter[numberOfInvestors]];
delete investorsIter[numberOfInvestors];
}
if (numberOfInvestors > 0) {
return;
}
if (state == State.PreICO) {
require(crowdsaleOwner.call.gas(3000000).value(this.balance)());
state = State.CompletePreICO;
} else {
require(crowdsaleOwner.call.gas(3000000).value(minimalSuccessUSD * 1000000000000000000 / etherPrice)());
// Create additional tokens for owner (30% of complete totalSupply)
uint tokens = 3 * totalSupply / 7;
balances[owner] = tokens;
totalSupply += tokens;
Transfer(this, owner, tokens);
state = State.Enabled;
}
}
NewState(state);
}
// This function must be called by token holder in case of crowdsale failed
function withdrawBack() public {
require(state == State.Disabled || state == State.CompletePreICO);
uint value = investors[msg.sender].amountWei;
if (value > 0) {
delete investors[msg.sender];
require(msg.sender.call.gas(3000000).value(value)());
}
}
}
/**
* @title Abstract interface for PROOF operating from registered external controllers
*/
contract Fund {
function transferFund(address _to, uint _value);
}
/**
* @title Token PROOF implementation
*/
contract Token is Crowdsale, Fund {
string public standard = 'Token 0.1';
string public name = 'PROOF';
string public symbol = "PF";
uint8 public decimals = 0;
mapping (address => mapping (address => uint)) public allowed;
mapping (address => bool) public externalControllers;
modifier onlyTokenHolders {
require(balances[msg.sender] != 0);
_;
}
// Fix for the ERC20 short address attack
modifier onlyPayloadSize(uint size) {
require(msg.data.length >= size + 4);
_;
}
modifier externalController {
require(externalControllers[msg.sender]);
_;
}
function Token(address _migrationHost)
payable Crowdsale(_migrationHost) {}
function balanceOf(address who) constant returns (uint) {
return balances[who];
}
function transfer(address _to, uint _value)
public enabledState onlyPayloadSize(2 * 32) {
require(balances[msg.sender] >= _value);
require(balances[_to] + _value >= balances[_to]); // overflow
balances[msg.sender] -= _value;
balances[_to] += _value;
Transfer(msg.sender, _to, _value);
}
function transferFrom(address _from, address _to, uint _value)
public enabledState onlyPayloadSize(3 * 32) {
require(balances[_from] >= _value);
require(balances[_to] + _value >= balances[_to]); // overflow
require(allowed[_from][msg.sender] >= _value);
balances[_from] -= _value;
balances[_to] += _value;
allowed[_from][msg.sender] -= _value;
Transfer(_from, _to, _value);
}
function approve(address _spender, uint _value) public enabledState {
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
}
function allowance(address _owner, address _spender) public constant enabledState
returns (uint remaining) {
return allowed[_owner][_spender];
}
function transferFund(address _to, uint _value) public externalController {
require(balances[this] >= _value);
require(balances[_to] + _value >= balances[_to]); // overflow
balances[this] -= _value;
balances[_to] += _value;
Transfer(this, _to, _value);
}
}
contract ProofVote is Token {
function ProofVote(address _migrationHost)
payable Token(_migrationHost) {}
event VotingStarted(uint weiReqFund, VoteReason voteReason);
event Voted(address indexed voter, bool inSupport);
event VotingFinished(bool inSupport);
enum Vote { NoVote, VoteYea, VoteNay }
enum VoteReason { Nothing, ReqFund, Migration, UpdateContract }
uint public weiReqFund;
uint public votingDeadline;
uint public numberOfVotes;
uint public yea;
uint public nay;
VoteReason voteReason;
mapping (address => Vote) public votes;
mapping (uint => address) public votesIter;
address public migrationAgent;
address public migrationAgentCandidate;
address public externalControllerCandidate;
function startVoting(uint _weiReqFund) public enabledOrMigrationState onlyOwner {
require(_weiReqFund > 0);
internalStartVoting(_weiReqFund, VoteReason.ReqFund, 7);
}
function internalStartVoting(uint _weiReqFund, VoteReason _voteReason, uint _votingDurationDays) internal {
require(voteReason == VoteReason.Nothing && _weiReqFund <= this.balance);
weiReqFund = _weiReqFund;
votingDeadline = now + _votingDurationDays * 1 days;
voteReason = _voteReason;
delete yea;
delete nay;
VotingStarted(_weiReqFund, _voteReason);
}
function votingInfo() public constant
returns(uint _weiReqFund, uint _timeToFinish, VoteReason _voteReason) {
_weiReqFund = weiReqFund;
_voteReason = voteReason;
if (votingDeadline <= now) {
_timeToFinish = 0;
} else {
_timeToFinish = votingDeadline - now;
}
}
function vote(bool _inSupport) public onlyTokenHolders returns (uint voteId) {
require(voteReason != VoteReason.Nothing);
require(votes[msg.sender] == Vote.NoVote);
require(votingDeadline > now);
voteId = numberOfVotes++;
votesIter[voteId] = msg.sender;
if (_inSupport) {
votes[msg.sender] = Vote.VoteYea;
} else {
votes[msg.sender] = Vote.VoteNay;
}
Voted(msg.sender, _inSupport);
return voteId;
}
function finishVoting(uint _votesToProcess) public returns (bool _inSupport) {
require(voteReason != VoteReason.Nothing);
require(now >= votingDeadline);
while (_votesToProcess > 0 && numberOfVotes > 0) {
address voter = votesIter[--numberOfVotes];
Vote v = votes[voter];
uint voteWeight = balances[voter];
if (v == Vote.VoteYea) {
yea += voteWeight;
} else if (v == Vote.VoteNay) {
nay += voteWeight;
}
delete votes[voter];
delete votesIter[numberOfVotes];
--_votesToProcess;
}
if (numberOfVotes > 0) {
_inSupport = false;
return;
}
_inSupport = (yea > nay);
uint weiForSend = weiReqFund;
delete weiReqFund;
delete votingDeadline;
delete numberOfVotes;
if (_inSupport) {
if (voteReason == VoteReason.ReqFund) {
require(owner.call.gas(3000000).value(weiForSend)());
} else if (voteReason == VoteReason.Migration) {
migrationAgent = migrationAgentCandidate;
require(migrationAgent.call.gas(3000000).value(this.balance)());
delete migrationAgentCandidate;
state = State.Migration;
} else if (voteReason == VoteReason.UpdateContract) {
externalControllers[externalControllerCandidate] = true;
delete externalControllerCandidate;
}
}
delete voteReason;
VotingFinished(_inSupport);
}
}
/**
* @title Migration agent intefrace for possibility of moving tokens
* to another contract
*/
contract MigrationAgent {
function migrateFrom(address _from, uint _value);
}
/**
* @title Migration functionality for possibility of moving tokens
* to another contract
*/
contract TokenMigration is ProofVote {
uint public totalMigrated;
event Migrate(address indexed from, address indexed to, uint value);
function TokenMigration(address _migrationHost) payable ProofVote(_migrationHost) {}
// Migrate _value of tokens to the new token contract
function migrate() external {
require(state == State.Migration);
uint value = balances[msg.sender];
balances[msg.sender] -= value;
Transfer(msg.sender, this, value);
totalSupply -= value;
totalMigrated += value;
MigrationAgent(migrationAgent).migrateFrom(msg.sender, value);
Migrate(msg.sender, migrationAgent, value);
}
function setMigrationAgent(address _agent) external onlyOwner {
require(migrationAgent == 0 && _agent != 0);
migrationAgentCandidate = _agent;
internalStartVoting(0, VoteReason.Migration, 2);
}
}
contract ProofFund is TokenMigration {
function ProofFund(address _migrationHost)
payable TokenMigration(_migrationHost) {}
function addExternalController(address _externalControllerCandidate) public onlyOwner {
require(_externalControllerCandidate != 0);
externalControllerCandidate = _externalControllerCandidate;
internalStartVoting(0, VoteReason.UpdateContract, 2);
}
function removeExternalController(address _externalController) public onlyOwner {
delete externalControllers[_externalController];
}
}
/**
* @title Proof interface
*/
contract ProofAbstract {
function swypeCode(address _who) returns (uint16 _swype);
function setHash(address _who, uint16 _swype, bytes32 _hash);
}
contract Proof is ProofFund {
uint public priceInTokens;
uint public teamFee;
address public proofImpl;
function Proof(address _migrationHost)
payable ProofFund(_migrationHost) {}
function setPrice(uint _priceInTokens) public onlyOwner {
require(_priceInTokens >= 2);
teamFee = _priceInTokens / 10;
if (teamFee == 0) {
teamFee = 1;
}
priceInTokens = _priceInTokens - teamFee;
}
function setProofImpl(address _proofImpl) public onlyOwner {
proofImpl = _proofImpl;
}
function swypeCode() public returns (uint16 _swype) {
require(proofImpl != 0);
_swype = ProofAbstract(proofImpl).swypeCode(msg.sender);
}
function setHash(uint16 _swype, bytes32 _hash) public {
require(proofImpl != 0);
transfer(owner, teamFee);
transfer(this, priceInTokens);
ProofAbstract(proofImpl).setHash(msg.sender, _swype, _hash);
}
}
/**
* @title Public vote for Proof tokens contract
*/
contract ProofPublicVote is owned {
address proofFund;
function ProofPublicVote(address _proofFund) payable owned() {
proofFund = _proofFund;
}
event Deployed(address indexed projectOwner, uint proofReqFund, bytes32 urlInfo);
event Voted(address indexed projectOwner, address indexed voter, bool inSupport);
event VotingFinished(address indexed projectOwner, bool inSupport);
enum Vote { NoVote, VoteYea, VoteNay }
struct Project {
uint proofReqFund;
bytes32 urlInfo;
uint votingDeadline;
uint numberOfVotes;
uint yea;
uint nay;
mapping (address => Vote) votes;
mapping (uint => address) votesIter;
}
mapping (address => Project) public projects;
function deployProject(uint _proofReqFund, bytes32 _urlInfo) public {
require(_proofReqFund > 0 && _proofReqFund <= ERC20(proofFund).balanceOf(proofFund));
require(_proofReqFund <= ERC20(proofFund).balanceOf(msg.sender) * 1000);
require(projects[msg.sender].proofReqFund == 0);
projects[msg.sender].proofReqFund = _proofReqFund;
projects[msg.sender].urlInfo = _urlInfo;
projects[msg.sender].votingDeadline = now + 7 days;
Deployed(msg.sender, _proofReqFund, _urlInfo);
}
function projectInfoPublic(address _projectOwner) constant public
returns(uint _proofReqFund, bytes32 _urlInfo, uint _timeToFinish) {
_proofReqFund = projects[_projectOwner].proofReqFund;
_urlInfo = projects[_projectOwner].urlInfo;
if (projects[_projectOwner].votingDeadline <= now) {
_timeToFinish = 0;
} else {
_timeToFinish = projects[_projectOwner].votingDeadline - now;
}
}
function votePublic(address _projectOwner, bool _inSupport) public
returns (uint voteId) {
Project storage p = projects[_projectOwner];
require(p.proofReqFund > 0);
require(p.votes[msg.sender] == Vote.NoVote);
require(p.votingDeadline > now);
voteId = p.numberOfVotes++;
p.votesIter[voteId] = msg.sender;
if (_inSupport) {
p.votes[msg.sender] = Vote.VoteYea;
} else {
p.votes[msg.sender] = Vote.VoteNay;
}
Voted(_projectOwner, msg.sender, _inSupport);
return voteId;
}
function finishVotingPublic(address _projectOwner, uint _votesToProcess) public
returns (bool _inSupport) {
Project storage p = projects[_projectOwner];
require(p.proofReqFund > 0);
require(now >= p.votingDeadline && p.proofReqFund <= ERC20(proofFund).balanceOf(proofFund));
while (_votesToProcess > 0 && p.numberOfVotes > 0) {
address voter = p.votesIter[--p.numberOfVotes];
Vote v = p.votes[voter];
uint voteWeight = ERC20(proofFund).balanceOf(voter);
if (v == Vote.VoteYea) {
p.yea += voteWeight;
} else if (v == Vote.VoteNay) {
p.nay += voteWeight;
}
delete p.votesIter[p.numberOfVotes];
delete p.votes[voter];
--_votesToProcess;
}
if (p.numberOfVotes > 0) {
_inSupport = false;
return;
}
_inSupport = (p.yea > p.nay);
uint proofReqFund = p.proofReqFund;
delete projects[_projectOwner];
if (_inSupport) {
Fund(proofFund).transferFund(_projectOwner, proofReqFund);
}
VotingFinished(_projectOwner, _inSupport);
}
}
/**
* @title Prover business-logic contract
*/
contract ProofImpl is owned, ProofAbstract {
struct Swype {
uint16 swype;
uint timestampSwype;
}
struct Video {
uint16 swype;
uint timestampSwype;
uint timestampHash;
address owner;
}
mapping (address => Swype) public swypes;
mapping (bytes32 => Video) public videos;
address public proofFund;
modifier onlyProofFund {
require(proofFund == msg.sender);
_;
}
function ProofImpl(address _proofFund) payable owned() {
proofFund = _proofFund;
}
function swypeCode(address _who) public onlyProofFund returns (uint16 _swype) {
bytes32 blockHash = block.blockhash(block.number - 1);
bytes32 shaTemp = sha3(msg.sender, blockHash);
_swype = uint16(uint256(shaTemp) % 65536);
swypes[_who] = Swype({swype: _swype, timestampSwype: now});
}
function setHash(address _who, uint16 _swype, bytes32 _hash) onlyProofFund public {
require(swypes[_who].timestampSwype != 0);
require(swypes[_who].swype == _swype);
videos[_hash] = Video({swype: _swype, timestampSwype:swypes[_who].timestampSwype,
timestampHash: now, owner: _who});
delete swypes[_who];
}
}