Created
March 30, 2018 13:19
-
-
Save michielmulders/f1d084b641087578118400c60295dda2 to your computer and use it in GitHub Desktop.
FaceToken ERC20 Full Contract Solidity Code
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
library SafeMath { | |
function mul(uint256 a, uint256 b) internal pure returns (uint256) { | |
uint256 c = a * b; | |
assert(a == 0 || c / a == b); | |
return c; | |
} | |
function div(uint256 a, uint256 b) internal pure returns (uint256) { | |
// assert(b > 0); // Solidity automatically throws when dividing by 0 | |
uint256 c = a / b; | |
// assert(a == b * c + a % b); // There is no case in which this doesn't hold | |
return c; | |
} | |
function sub(uint256 a, uint256 b) internal pure returns (uint256) { | |
assert(b <= a); | |
return a - b; | |
} | |
function add(uint256 a, uint256 b) internal pure returns (uint256) { | |
uint256 c = a + b; | |
assert(c >= a); | |
return c; | |
} | |
} | |
contract ERC20Basic { | |
uint256 public totalSupply; | |
function balanceOf(address who) public constant returns (uint256); | |
function transfer(address to, uint256 value) public returns (bool); | |
event Transfer(address indexed from, address indexed to, uint value); | |
} | |
contract ERC20 is ERC20Basic { | |
function allowance(address owner, address spender) public constant returns (uint256); | |
function transferFrom(address from, address to, uint256 value) public returns (bool); | |
function approve(address spender, uint256 value) public returns (bool); | |
event Approval(address indexed owner, address indexed spender, uint256 value); | |
} | |
contract Ownable { | |
address public owner; | |
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); | |
/** | |
* @dev The Ownable constructor sets the original `owner` of the contract to the sender | |
* account. | |
*/ | |
function Ownable() 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) onlyOwner public { | |
require(newOwner != address(0)); | |
OwnershipTransferred(owner, newOwner); | |
owner = newOwner; | |
} | |
} | |
contract BasicToken is ERC20 { | |
using SafeMath for uint256; | |
mapping(address => uint256) balances; | |
/** | |
* @dev transfer token for a specified address | |
* @param _to The address to transfer to. | |
* @param _value The amount to be transferred. | |
*/ | |
function transfer(address _to, uint256 _value) public returns (bool success) { | |
if (balances[msg.sender] >= _value) { | |
balances[msg.sender] = balances[msg.sender].sub(_value); | |
balances[_to] = balances[_to].add(_value); | |
Transfer(msg.sender, _to, _value); | |
return true; | |
} else { | |
return false; | |
} | |
} | |
/** | |
* @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 constant returns (uint256 balance) { | |
return balances[_owner]; | |
} | |
} | |
contract StandardToken is BasicToken { | |
mapping (address => mapping (address => uint256)) allowed; | |
/** | |
* @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 | |
*/ | |
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) { | |
if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value) { | |
balances[_to] = balances[_to].add(_value); | |
balances[_from] = balances[_from].sub(_value); | |
allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value); | |
Transfer(_from, _to, _value); | |
return true; | |
} else { | |
return false; | |
} | |
} | |
/** | |
* @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. | |
*/ | |
function approve(address _spender, uint256 _value) public returns (bool) { | |
allowed[msg.sender][_spender] = _value; | |
Approval(msg.sender, _spender, _value); | |
return true; | |
} | |
/** | |
* @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 constant returns (uint256 remaining) { | |
return allowed[_owner][_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 | |
*/ | |
function increaseApproval (address _spender, uint _addedValue) | |
public | |
returns (bool success) { | |
allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue); | |
Approval(msg.sender, _spender, allowed[msg.sender][_spender]); | |
return true; | |
} | |
function decreaseApproval (address _spender, uint _subtractedValue) | |
public | |
returns (bool success) { | |
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; | |
} | |
} | |
contract MintableToken is StandardToken, Ownable { | |
event Mint(address indexed to, uint256 amount); | |
event MintFinished(); | |
bool public mintingFinished = false; | |
modifier canMint() { | |
require(!mintingFinished); | |
_; | |
} | |
/** | |
* @dev Function to mint tokens | |
* @param _to The address that will receive the minted tokens. | |
* @param _amount The amount of tokens to mint. | |
* @return A boolean that indicates if the operation was successful. | |
*/ | |
function mint(address _to, uint256 _amount) onlyOwner canMint public returns (bool) { | |
totalSupply = totalSupply.add(_amount); | |
balances[_to] = balances[_to].add(_amount); | |
Mint(_to, _amount); | |
Transfer(0x0, _to, _amount); | |
return true; | |
} | |
/** | |
* @dev Function to stop minting new tokens. | |
* @return True if the operation was successful. | |
*/ | |
function finishMinting() onlyOwner public returns (bool) { | |
mintingFinished = true; | |
MintFinished(); | |
return true; | |
} | |
} | |
contract FaceToken is MintableToken { | |
using SafeMath for uint; | |
string public constant name = "Face Token"; | |
string public constant symbol = "FACE"; | |
uint8 public constant decimals = 18; | |
uint256 public constant decimalFactor = 10 ** uint256(decimals); | |
uint256 private initialSupply = 300000000 * decimalFactor; | |
address public identityProvider; //Public address of the Face Token identity provider (Kairos) | |
uint256 public confidenceThreshold; //Updateable confidence threshold to compare against confidence results sent from identity provider | |
function FaceToken (address _identityProvider, uint _confidenceThreshold) public { | |
identityProvider = _identityProvider; | |
confidenceThreshold = _confidenceThreshold; | |
owner = msg.sender; | |
mint(msg.sender, initialSupply); | |
finishMinting(); | |
} | |
event RegisterUser(address sender, string faceId, bool success); | |
event VerifyUser(address sender, string faceId, uint confidenceResult, bool success); | |
event IdentityProviderUpdated(address oldProvider, address newProvider); | |
event ConfidenceThresholdUpdated(uint oldThreshold, uint newThreshold); | |
struct Registration { | |
uint timestamp; // Seconds since 1/1/1970 | |
address identityProvider; // Public address of identityProvider | |
string faceId; // Unique event identifier returned by identityProvider | |
bool registered; // Bool indicating registration status (for code optimizations) | |
} | |
struct Verification { | |
string faceId; // Unique event identifier returned by identityProvider | |
uint confidenceResult; // Confidence value returned by identityProvider | |
address user; // Address of user attempting verification (used as hash input) | |
uint threshold; // Confidence threshold required for verification (set on Face deployment) | |
uint timestamp; // Seconds since 1/1/1970 | |
address identityProvider; // Public address of identityProvider | |
} | |
mapping(address => Registration) public registrations; //User address -> Registration object | |
mapping(bytes32 => Verification) public verifications; //Hash of user address + verification number -> Verification object | |
mapping(address => uint) public verificationCounts; //User address -> number of verification attempts | |
mapping(bytes32 => bool) public verificationAttempts; //Hash of signed data -> bool of whether it's been seen before | |
modifier signerIsIdentityProvider( | |
string faceId, | |
uint confidenceResult, | |
address user, | |
bytes32 r, | |
bytes32 s, | |
uint8 v | |
) { | |
require(address(identityProvider) == ecrecover(keccak256(faceId, confidenceResult, user), v, r, s)); | |
_; | |
} | |
function verifyAndTransfer( | |
string faceId, | |
uint confidenceResult, | |
address user, | |
bytes32 r, | |
bytes32 s, | |
uint8 v, | |
address to, | |
uint amount | |
) | |
public | |
returns (bool success) | |
{ | |
success = verify(faceId, confidenceResult, user, r, s, v); | |
if (success) { | |
success = transferFrom(msg.sender, to, amount); | |
} | |
} | |
function register( | |
string faceId, | |
uint confidenceResult, | |
address user, | |
bytes32 r, | |
bytes32 s, | |
uint8 v | |
) | |
public | |
signerIsIdentityProvider(faceId, confidenceResult, user, r, s, v) | |
returns (bool success) | |
{ | |
Registration memory registration; | |
registration.timestamp = block.timestamp; | |
registration.identityProvider = address(identityProvider); | |
registration.faceId = faceId; | |
registration.registered = true; | |
registrations[msg.sender] = registration; | |
success = registrations[msg.sender].timestamp != 0; | |
RegisterUser(msg.sender, faceId, success); | |
} | |
function verify( | |
string faceId, | |
uint confidenceResult, | |
address user, | |
bytes32 r, | |
bytes32 s, | |
uint8 v | |
) | |
public | |
returns (bool success) | |
{ | |
bytes32 dataHash = keccak256(faceId, confidenceResult, user); | |
require(address(identityProvider) == ecrecover(dataHash, v, r, s)); | |
require(registrations[msg.sender].registered == true); | |
require(verificationAttempts[dataHash] == false); | |
Verification memory verification = generateVerification(faceId, confidenceResult, user); | |
verifications[keccak256(msg.sender, verificationCounts[msg.sender]++)] = verification; | |
verificationCounts[msg.sender] = verificationCounts[msg.sender]++; | |
verificationAttempts[dataHash] = true; | |
success = confidenceResult >= confidenceThreshold; | |
VerifyUser(msg.sender, faceId, confidenceResult, success); | |
} | |
function updateIdentityProvider | |
( | |
address newIdentityProvider | |
) | |
public | |
onlyOwner | |
{ | |
require(newIdentityProvider != address(0)); | |
identityProvider = newIdentityProvider; | |
IdentityProviderUpdated(identityProvider, newIdentityProvider); | |
} | |
function updateConfidenceThreshold | |
( | |
uint newThreshold | |
) | |
public | |
onlyOwner | |
{ | |
require(newThreshold != 0); | |
confidenceThreshold = newThreshold; | |
ConfidenceThresholdUpdated(confidenceThreshold, newThreshold); | |
} | |
function generateVerification | |
( | |
string faceId, | |
uint confidenceResult, | |
address user | |
) | |
internal | |
view | |
returns (Verification verification) | |
{ | |
verification.user = user; | |
verification.threshold = confidenceThreshold; | |
verification.timestamp = block.timestamp; | |
verification.identityProvider = address(identityProvider); | |
verification.faceId = faceId; | |
verification.confidenceResult = confidenceResult; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment