-
Notifications
You must be signed in to change notification settings - Fork 559
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #145 from TokenMarketNet/security-token
Security token
- Loading branch information
Showing
16 changed files
with
1,025 additions
and
1 deletion.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,297 @@ | ||
/** | ||
* This smart contract code is Copyright 2018 TokenMarket Ltd. For more information see https://tokenmarket.net | ||
* Licensed under the Apache License, version 2.0: https://github.com/TokenMarketNet/ico/blob/master/LICENSE.txt | ||
* NatSpec is used intentionally to cover also other than public functions | ||
* Solidity 0.4.18 is intentionally used: it's stable, and our framework is | ||
* based on that. | ||
*/ | ||
|
||
pragma solidity ^0.4.18; | ||
|
||
import "../CrowdsaleToken.sol"; | ||
import "./SecurityTransferAgent.sol"; | ||
import "zeppelin/contracts/math/SafeMath.sol"; | ||
import "zeppelin/contracts/ownership/Whitelist.sol"; | ||
import "zeppelin/contracts/token/ERC20/ERC20.sol"; | ||
import "./ERC677Token.sol"; | ||
|
||
/** | ||
* @author TokenMarket / Ville Sundell <ville at tokenmarket.net> | ||
*/ | ||
contract CheckpointToken is ERC677Token { | ||
using SafeMath for uint256; // We use only uint256 for safety reasons (no boxing) | ||
|
||
string public name; | ||
string public symbol; | ||
uint256 public decimals; | ||
SecurityTransferAgent public transferVerifier; | ||
|
||
struct Checkpoint { | ||
uint256 blockNumber; | ||
uint256 value; | ||
} | ||
|
||
mapping (address => Checkpoint[]) public tokenBalances; | ||
Checkpoint[] public tokensTotal; | ||
|
||
mapping (address => mapping (address => uint256)) public allowed; | ||
|
||
/** | ||
* @dev Constructor for CheckpointToken, initializing the token | ||
* | ||
* Here we define initial values for name, symbol and decimals. | ||
* | ||
* @param _name Initial name of the token | ||
* @param _symbol Initial symbol of the token | ||
* @param _decimals Number of decimals for the token, industry standard is 18 | ||
*/ | ||
function CheckpointToken(string _name, string _symbol, uint256 _decimals) public { | ||
name = _name; | ||
symbol = _symbol; | ||
decimals = _decimals; | ||
} | ||
|
||
/** PUBLIC FUNCTIONS | ||
****************************************/ | ||
|
||
/** | ||
* @dev Function to check the amount of tokens that an owner allowed to a spender. | ||
* @param owner address The address which owns the funds. | ||
* @param spender address The address which will spend the funds. | ||
* @return A uint256 specifying the amount of tokens still available for the spender. | ||
*/ | ||
function allowance(address owner, address spender) public view returns (uint256) { | ||
return allowed[owner][spender]; | ||
} | ||
|
||
/** | ||
* @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. | ||
* | ||
* Beware that changing an allowance with this method brings the risk that someone may use both the old | ||
* and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this | ||
* race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: | ||
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 | ||
* @param spender The address which will spend the funds. | ||
* @param value The amount of tokens to be spent. | ||
* @return true if the call function was executed successfully | ||
*/ | ||
function approve(address spender, uint256 value) public returns (bool) { | ||
allowed[msg.sender][spender] = value; | ||
Approval(msg.sender, spender, value); | ||
return true; | ||
} | ||
|
||
/** | ||
* @dev Transfer tokens from one address to another | ||
* @param from address The address which you want to send tokens from | ||
* @param to address The address which you want to transfer to | ||
* @param value uint256 the amount of tokens to be transferred | ||
* @return true if the call function was executed successfully | ||
*/ | ||
function transferFrom(address from, address to, uint256 value) public returns (bool) { | ||
require(value <= allowed[from][msg.sender]); | ||
|
||
transferInternal(from, to, value); | ||
Transfer(from, to, value); | ||
return true; | ||
} | ||
|
||
/** | ||
* @dev transfer token for a specified address | ||
* @param to The address to transfer to. | ||
* @param value The amount to be transferred. | ||
* @return true if the call function was executed successfully | ||
*/ | ||
function transfer(address to, uint256 value) public returns (bool) { | ||
transferInternal(msg.sender, to, value); | ||
Transfer(msg.sender, to, value); | ||
return true; | ||
} | ||
|
||
/** | ||
* @dev total number of tokens in existence | ||
* @return A uint256 specifying the total number of tokens in existence | ||
*/ | ||
function totalSupply() public view returns (uint256 tokenCount) { | ||
tokenCount = balanceAtBlock(tokensTotal, block.number); | ||
} | ||
|
||
/** | ||
* @dev total number of tokens in existence at the given block | ||
* @param blockNumber The block number we want to query for the total supply | ||
* @return A uint256 specifying the total number of tokens at a given block | ||
*/ | ||
function totalSupplyAt(uint256 blockNumber) public view returns (uint256 tokenCount) { | ||
tokenCount = balanceAtBlock(tokensTotal, blockNumber); | ||
} | ||
|
||
/** | ||
* @dev Gets the balance of the specified address. | ||
* @param owner The address to query the the balance of. | ||
* @return An uint256 representing the amount owned by the passed address. | ||
*/ | ||
function balanceOf(address owner) public view returns (uint256 balance) { | ||
balance = balanceAtBlock(tokenBalances[owner], block.number); | ||
} | ||
|
||
/** | ||
* @dev Gets the balance of the specified address. | ||
* @param owner The address to query the the balance of. | ||
* @param blockNumber The block number we want to query for the balance. | ||
* @return An uint256 representing the amount owned by the passed address. | ||
*/ | ||
function balanceAt(address owner, uint256 blockNumber) public view returns (uint256 balance) { | ||
balance = balanceAtBlock(tokenBalances[owner], blockNumber); | ||
} | ||
|
||
/** | ||
* @dev Increase the amount of tokens that an owner allowed to a spender. | ||
* | ||
* approve should be called when allowed[spender] == 0. To increment | ||
* allowed value is better to use this function to avoid 2 calls (and wait until | ||
* the first transaction is mined) | ||
* From MonolithDAO Token.sol | ||
* @param spender The address which will spend the funds. | ||
* @param addedValue The amount of tokens to increase the allowance by. | ||
*/ | ||
function increaseApproval(address spender, uint addedValue) public returns (bool) { | ||
allowed[msg.sender][spender] = allowed[msg.sender][spender].add(addedValue); | ||
Approval(msg.sender, spender, allowed[msg.sender][spender]); | ||
return true; | ||
} | ||
|
||
/** | ||
* @dev Decrease the amount of tokens that an owner allowed to a spender. | ||
* | ||
* approve should be called when allowed[spender] == 0. To decrement | ||
* allowed value is better to use this function to avoid 2 calls (and wait until | ||
* the first transaction is mined) | ||
* From MonolithDAO Token.sol | ||
* @param spender The address which will spend the funds. | ||
* @param subtractedValue The amount of tokens to decrease the allowance by. | ||
*/ | ||
function decreaseApproval(address spender, uint subtractedValue) public returns (bool) { | ||
uint oldValue = allowed[msg.sender][spender]; | ||
if (subtractedValue > oldValue) { | ||
allowed[msg.sender][spender] = 0; | ||
} else { | ||
allowed[msg.sender][spender] = oldValue.sub(subtractedValue); | ||
} | ||
Approval(msg.sender, spender, allowed[msg.sender][spender]); | ||
return true; | ||
} | ||
|
||
/** | ||
* @dev Addition to StandardToken methods. Increase the amount of tokens that | ||
* an owner allowed to a spender and execute a call with the sent data. | ||
* | ||
* This is originally from OpenZeppelin. | ||
* | ||
* approve should be called when allowed[spender] == 0. To increment | ||
* allowed value is better to use this function to avoid 2 calls (and wait until | ||
* the first transaction is mined) | ||
* From MonolithDAO Token.sol | ||
* @param spender The address which will spend the funds. | ||
* @param addedValue The amount of tokens to increase the allowance by. | ||
* @param data ABI-encoded contract call to call `spender` address. | ||
*/ | ||
function increaseApproval(address spender, uint addedValue, bytes data) public returns (bool) { | ||
require(spender != address(this)); | ||
|
||
increaseApproval(spender, addedValue); | ||
|
||
require(spender.call(data)); | ||
|
||
return true; | ||
} | ||
|
||
/** | ||
* @dev Addition to StandardToken methods. Decrease the amount of tokens that | ||
* an owner allowed to a spender and execute a call with the sent data. | ||
* | ||
* This is originally from OpenZeppelin. | ||
* | ||
* approve should be called when allowed[spender] == 0. To decrement | ||
* allowed value is better to use this function to avoid 2 calls (and wait until | ||
* the first transaction is mined) | ||
* From MonolithDAO Token.sol | ||
* @param spender The address which will spend the funds. | ||
* @param subtractedValue The amount of tokens to decrease the allowance by. | ||
* @param data ABI-encoded contract call to call `spender` address. | ||
*/ | ||
function decreaseApproval(address spender, uint subtractedValue, bytes data) public returns (bool) { | ||
require(spender != address(this)); | ||
|
||
decreaseApproval(spender, subtractedValue); | ||
|
||
require(spender.call(data)); | ||
|
||
return true; | ||
} | ||
|
||
/** INTERNALS | ||
****************************************/ | ||
|
||
function balanceAtBlock(Checkpoint[] storage checkpoints, uint256 blockNumber) internal returns (uint256 balance) { | ||
uint256 currentBlockNumber; | ||
(currentBlockNumber, balance) = getCheckpoint(checkpoints, blockNumber); | ||
} | ||
|
||
function transferInternal(address from, address to, uint256 value) internal { | ||
if (address(transferVerifier) != address(0)) { | ||
value = transferVerifier.verify(from, to, value); | ||
require(value > 0); | ||
} | ||
|
||
uint256 fromBalance; | ||
uint256 toBalance; | ||
|
||
fromBalance = balanceOf(from); | ||
toBalance = balanceOf(to); | ||
|
||
setCheckpoint(tokenBalances[from], fromBalance.sub(value)); | ||
setCheckpoint(tokenBalances[to], toBalance.add(value)); | ||
} | ||
|
||
|
||
/** CORE | ||
** The Magic happens below: | ||
***************************************/ | ||
|
||
function setCheckpoint(Checkpoint[] storage checkpoints, uint256 newValue) internal { | ||
if ((checkpoints.length == 0) || (checkpoints[checkpoints.length.sub(1)].blockNumber < block.number)) { | ||
checkpoints.push(Checkpoint(block.number, newValue)); | ||
} else { | ||
checkpoints[checkpoints.length.sub(1)] = Checkpoint(block.number, newValue); | ||
} | ||
} | ||
|
||
function getCheckpoint(Checkpoint[] storage checkpoints, uint256 blockNumber) internal returns (uint256 blockNumber_, uint256 value) { | ||
if (checkpoints.length == 0) { | ||
return (0, 0); | ||
} | ||
|
||
// Shortcut for the actual value | ||
if (blockNumber >= checkpoints[checkpoints.length.sub(1)].blockNumber) { | ||
return (checkpoints[checkpoints.length.sub(1)].blockNumber, checkpoints[checkpoints.length.sub(1)].value); | ||
} | ||
|
||
if (blockNumber < checkpoints[0].blockNumber) { | ||
return (0, 0); | ||
} | ||
|
||
// Binary search of the value in the array | ||
uint256 min = 0; | ||
uint256 max = checkpoints.length.sub(1); | ||
while (max > min) { | ||
uint256 mid = (max.add(min.add(1))).div(2); | ||
if (checkpoints[mid].blockNumber <= blockNumber) { | ||
min = mid; | ||
} else { | ||
max = mid.sub(1); | ||
} | ||
} | ||
|
||
return (checkpoints[min].blockNumber, checkpoints[min].value); | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
pragma solidity ^0.4.18; | ||
|
||
interface ERC677Receiver { | ||
function tokenFallback(address from, uint256 amount, bytes data) returns (bool success); | ||
} | ||
|
||
interface ERC677 { | ||
event Transfer(address from, address receiver, uint256 amount, bytes data); | ||
function transferAndCall(ERC677Receiver receiver, uint amount, bytes data) returns (bool success); | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
pragma solidity ^0.4.18; | ||
|
||
import "zeppelin/contracts/token/ERC20/ERC20.sol"; | ||
import "./ERC677.sol"; | ||
|
||
|
||
contract ERC677Token is ERC20, ERC677 { | ||
function transferAndCall(ERC677Receiver receiver, uint amount, bytes data) returns (bool success) { | ||
require(transfer(address(receiver), amount)); | ||
|
||
Transfer(msg.sender, address(receiver), amount, data); | ||
|
||
require(receiver.tokenFallback(msg.sender, amount, data)); | ||
} | ||
} |
Oops, something went wrong.