Contract Source Code:
File 1 of 1 : AICoin
pragma solidity ^0.4.11;
/*******************************************************************************
* ERC Token Standard #20 Interface
* https://github.com/ethereum/EIPs/issues/20
*******************************************************************************/
contract ERC20Interface {
// Get the total token supply
function totalSupply() constant returns (uint256 totalSupply);
// Get the account balance of another account with address _owner
function balanceOf(address _owner) constant returns (uint256 balance);
// Send _value amount of tokens to address _to
function transfer(address _to, uint256 _value) returns (bool success);
// Send _value amount of tokens from address _from to address _to
function transferFrom(address _from, address _to, uint256 _value) returns (bool success);
// Allow _spender to withdraw from your account, multiple times, up to the _value amount.
// If this function is called again it overwrites the current allowance with _value.
// this function is required for some DEX functionality.
function approve(address _spender, uint256 _value) returns (bool success);
// Returns the amount which _spender is still allowed to withdraw from _owner
function allowance(address _owner, address _spender) constant returns (uint256 remaining);
// Triggered when tokens are transferred.
event Transfer(address indexed _from, address indexed _to, uint256 _value);
// Triggered whenever approve(address _spender, uint256 _value) is called.
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}
/*******************************************************************************
* AICoin - Smart Contract with token and ballot handling
*******************************************************************************/
contract AICoin is ERC20Interface {
/* ******************************
* COIN data / functions
* ******************************/
/* Token constants */
string public constant name = 'AICoin';
string public constant symbol = 'XAI';
uint8 public constant decimals = 8;
string public constant smallestUnit = 'Hofstadter';
/* Token internal data */
address m_administrator;
uint256 m_totalSupply;
/* Current balances for each account */
mapping(address => uint256) balances;
/* Account holder approves the transfer of an amount to another account */
mapping(address => mapping (address => uint256)) allowed;
/* One-time create function: initialize the supply and set the admin address */
function AICoin (uint256 _initialSupply) {
m_administrator = msg.sender;
m_totalSupply = _initialSupply;
balances[msg.sender] = _initialSupply;
}
/* Get the admin address */
function administrator() constant returns (address adminAddress) {
return m_administrator;
}
/* Get the total coin supply */
function totalSupply() constant returns (uint256 totalSupply) {
return m_totalSupply;
}
/* Get the balance of a specific account by its address */
function balanceOf(address _owner) constant returns (uint256 balance) {
return balances[_owner];
}
/* Transfer an amount from the owner's account to an indicated account */
function transfer(address _to, uint256 _amount) returns (bool success) {
if (balances[msg.sender] >= _amount
&& _amount > 0
&& balances[_to] + _amount > balances[_to]
&& (! accountHasCurrentVote(msg.sender))) {
balances[msg.sender] -= _amount;
balances[_to] += _amount;
Transfer(msg.sender, _to, _amount);
return true;
} else {
return false;
}
}
/* Send _value amount of tokens from address _from to address _to
* The transferFrom method is used for a withdraw workflow, allowing contracts to send
* tokens on your behalf, for example to "deposit" to a contract address and/or to charge
* fees in sub-currencies; the command should fail unless the _from account has
* deliberately authorized the sender of the message via some mechanism; we propose
* these standardized APIs for approval:
*/
function transferFrom(address _from, address _to, uint256 _amount) returns (bool success) {
if (balances[_from] >= _amount
&& allowed[_from][msg.sender] >= _amount
&& _amount > 0
&& balances[_to] + _amount > balances[_to]
&& (! accountHasCurrentVote(_from))) {
balances[_from] -= _amount;
allowed[_from][msg.sender] -= _amount;
balances[_to] += _amount;
Transfer(_from, _to, _amount);
return true;
} else {
return false;
}
}
/* Pre-authorize an address to withdraw from your account, up to the _value amount.
* Doing so (using transferFrom) reduces the remaining authorized amount,
* as well as the actual account balance)
* Subsequent calls to this function overwrite any existing authorized amount.
* Therefore, to cancel an authorization, simply write a zero amount.
*/
function approve(address _spender, uint256 _amount) returns (bool success) {
allowed[msg.sender][_spender] = _amount;
Approval(msg.sender, _spender, _amount);
return true;
}
/* Get the currently authorized that can be withdrawn by account _spender from account _owner */
function allowance(address _owner, address _spender) constant returns (uint256 remaining) {
return allowed[_owner][_spender];
}
/* ******************************
* BALLOT data / functions
* ******************************/
/* Dev Note: creating a struct that contained a string, uint values and
* an array of option structs, etc, would consistently fail.
* So the ballot details are held in separate mappings with a common integer
* key for each ballot. The IDs are 1-indexed, sequential and contiguous.
*/
/* Basic ballot details: time frame and number of options */
struct BallotDetails {
uint256 start;
uint256 end;
uint32 numOptions; // 1-indexed for readability
bool sealed;
}
uint32 public numBallots = 0; // 1-indexed for readability
mapping (uint32 => string) public ballotNames;
mapping (uint32 => BallotDetails) public ballotDetails;
mapping (uint32 => mapping (uint32 => string) ) public ballotOptions;
/* Create a new ballot and set the basic details (proposal description, dates)
* The ballot still need to have options added and then to be sealed
*/
function adminAddBallot(string _proposal, uint256 _start, uint256 _end) {
/* Admin functions must be called by the contract creator. */
require(msg.sender == m_administrator);
/* Create and store the new ballot objects */
numBallots++;
uint32 ballotId = numBallots;
ballotNames[ballotId] = _proposal;
ballotDetails[ballotId] = BallotDetails(_start, _end, 0, false);
}
/* Create a new ballot and set the basic details (proposal description, dates)
* The ballot still need to have options added and then to be sealed
*/
function adminAmendBallot(uint32 _ballotId, string _proposal, uint256 _start, uint256 _end) {
/* Admin functions must be called by the contract creator. */
require(msg.sender == m_administrator);
/* verify that the ballot exists */
require(_ballotId > 0 && _ballotId <= numBallots);
/* update the ballot object */
ballotNames[_ballotId] = _proposal;
ballotDetails[_ballotId].start = _start;
ballotDetails[_ballotId].end = _end;
}
/* Add an option to an existing Ballot
*/
function adminAddBallotOption(uint32 _ballotId, string _option) {
/* Admin functions must be called by the contract creator. */
require(msg.sender == m_administrator);
/* verify that the ballot exists */
require(_ballotId > 0 && _ballotId <= numBallots);
/* cannot change a ballot once it is sealed */
if(isBallotSealed(_ballotId)) {
revert();
}
/* store the new ballot option */
ballotDetails[_ballotId].numOptions += 1;
uint32 optionId = ballotDetails[_ballotId].numOptions;
ballotOptions[_ballotId][optionId] = _option;
}
/* Amend and option in an existing Ballot
*/
function adminEditBallotOption(uint32 _ballotId, uint32 _optionId, string _option) {
/* Admin functions must be called by the contract creator. */
require(msg.sender == m_administrator);
/* verify that the ballot exists */
require(_ballotId > 0 && _ballotId <= numBallots);
/* cannot change a ballot once it is sealed */
if(isBallotSealed(_ballotId)) {
revert();
}
/* validate the ballot option */
require(_optionId > 0 && _optionId <= ballotDetails[_ballotId].numOptions);
/* update the ballot option */
ballotOptions[_ballotId][_optionId] = _option;
}
/* Seal a ballot - after this the ballot is official and no changes can be made.
*/
function adminSealBallot(uint32 _ballotId) {
/* Admin functions must be called by the contract creator. */
require(msg.sender == m_administrator);
/* verify that the ballot exists */
require(_ballotId > 0 && _ballotId <= numBallots);
/* cannot change a ballot once it is sealed */
if(isBallotSealed(_ballotId)) {
revert();
}
/* set the ballot seal flag */
ballotDetails[_ballotId].sealed = true;
}
/* Function to determine if a ballot is currently in progress, based on its
* start and end dates, and that it has been sealed.
*/
function isBallotInProgress(uint32 _ballotId) private constant returns (bool) {
return (isBallotSealed(_ballotId)
&& ballotDetails[_ballotId].start <= now
&& ballotDetails[_ballotId].end >= now);
}
/* Function to determine if a ballot has ended, based on its end date */
function hasBallotEnded(uint32 _ballotId) private constant returns (bool) {
return (ballotDetails[_ballotId].end < now);
}
/* Function to determine if a ballot has been sealed, which means it has been
* authorized by the administrator and can no longer be changed.
*/
function isBallotSealed(uint32 _ballotId) private returns (bool) {
return ballotDetails[_ballotId].sealed;
}
/* ******************************
* VOTING data / functions
* ******************************/
mapping (uint32 => mapping (address => uint256) ) public ballotVoters;
mapping (uint32 => mapping (uint32 => uint256) ) public ballotVoteCount;
/* function to allow a coin holder add to the vote count of an option in an
* active ballot. The votes added equals the balance of the account. Once this is called successfully
* the coins cannot be transferred out of the account until the end of the ballot.
*
* NB: The timing of the start and end of the voting period is determined by
* the timestamp of the block in which the transaction is included. As given by
* the current Ethereum standard this is *NOT* guaranteed to be accurate to any
* given external time source. Therefore, votes should be placed well in advance
* of the UTC end time of the Ballot.
*/
function vote(uint32 _ballotId, uint32 _selectedOptionId) {
/* verify that the ballot exists */
require(_ballotId > 0 && _ballotId <= numBallots);
/* Ballot must be in progress in order to vote */
require(isBallotInProgress(_ballotId));
/* Calculate the balance which which the coin holder has not yet voted, which is the difference between
* the current balance for the senders address and the amount they already voted in this ballot.
* If the difference is zero, this attempt to vote will fail.
*/
uint256 votableBalance = balanceOf(msg.sender) - ballotVoters[_ballotId][msg.sender];
require(votableBalance > 0);
/* validate the ballot option */
require(_selectedOptionId > 0 && _selectedOptionId <= ballotDetails[_ballotId].numOptions);
/* update the vote count and record the voter */
ballotVoteCount[_ballotId][_selectedOptionId] += votableBalance;
ballotVoters[_ballotId][msg.sender] += votableBalance;
}
/* function to determine if an address has already voted in a given ballot */
function hasAddressVotedInBallot(uint32 _ballotId, address _voter) constant returns (bool hasVoted) {
return ballotVoters[_ballotId][_voter] > 0;
}
/* function to determine if an account has voted in any current ballot */
function accountHasCurrentVote(address _voter) constant returns (bool) {
for(uint32 id = 1; id <= numBallots; id++) {
if (isBallotInProgress(id) && hasAddressVotedInBallot(id, _voter)) {
return true;
}
}
return false;
}
}