Exhibit 4.3
pragma solidity ^0.4.24;
import "openzeppelin-solidity/contracts/math/SafeMath.sol";
import "./ERC20.sol";
import "./freezable.sol";
import "./Ledger.sol";
import "./ExternalStorage.sol";
import "./Registry.sol";
import "./Library.sol";
import "./displayable.sol";
import "./configurable.sol";
import "./storable.sol";
contract Token is ERC20, freezable, displayable, configurable, IStorable {
using SafeMath for uint256;
using Library for address;
struct allowedAddr {
bool status;
string details;
address vettingAgent;
}
ITokenLedger public tokenLedger;
string public storageName;
string public ledgerName;
address public externalStorage;
address public registry;
uint8 public constant decimals = 18;
bool public isTokenContract = true;
bool public haltPurchase;
// This state is specific to the first version of the Token
// token contract and the token generation event, and hence
// there is no reason to persist in external storage for
// future contracts.
bool public allowTransfers;
mapping (address => allowedAddr) public whitelistedRecipient;
address[] public whitelistedRecipientForIndex;
mapping (address => bool) private processedWhitelistedRecipient;
uint256 public contributionMinimum;
event Mint(uint256 amountMinted, uint256 totalTokens, uint256 circulationCap);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event WhiteList(address indexed buyer, uint256 holdCap);
event ConfigChanged(uint256 buyPrice, uint256 circulationCap, uint256 balanceLimit);
event CommissionCalc(uint time);
event VestedTokenGrant(address indexed beneficiary, uint256 startDate, uint256 cliffDate,
uint256 durationSec, uint256 fullyVestedAmount, bool isRevocable);
event VestedTokenRevocation(address indexed beneficiary);
event VestedTokenRelease(address indexed beneficiary, uint256 amount);
event StorageUpdated(address storageAddress, address ledgerAddress);
event PurchaseHalted();
event PurchaseResumed();
modifier onlyFoundation {
address foundation = externalStorage.getFoundation();
require(foundation != address(0));
if (msg.sender != owner && msg.sender != foundation) revert();
_;
}
modifier initStorage {
address ledgerAddress = Registry(registry).getStorage(ledgerName);
address storageAddress = Registry(registry).getStorage(storageName);
tokenLedger = ITokenLedger(ledgerAddress);
externalStorage = storageAddress;
_;
}
constructor(address _registry, string _storageName, string _ledgerName) public payable {
isTokenContract = true;
version = "2";
require(_registry != address(0));
storageName = _storageName;
ledgerName = _ledgerName;
registry = _registry;
addSuperAdmin(registry);
}
/* This unnamed function is called whenever someone tries to send ether directly to the token contract */
function () public {
revert(); // Prevents accidental sending of ether
}
function getLedgerNameHash() external view returns (bytes32) {
return keccak256(abi.encodePacked(ledgerName));
}
function getStorageNameHash() external view returns (bytes32) {
return keccak256(abi.encodePacked(storageName));
}
function configure(
bytes32 _tokenName,
bytes32 _tokenSymbol,
uint256 _buyPrice,
uint256 _circulationCap,
uint256 _balanceLimit,
address _foundation,
uint8 _commissionPercent,
uint8 _commissionPercentRel,
uint256 _commissionDate
) public onlySuperAdmins initStorage returns (bool) {
uint256 __buyPrice= externalStorage.getBuyPrice();
if (__buyPrice> 0 && __buyPrice!= _buyPrice) {
require(frozenToken);
}
commissionPercent = _commissionPercent;
commissionPercentRel = _commissionPercentRel;
commissionDate = _commissionDate;
externalStorage.setTokenName(_tokenName);
externalStorage.setTokenSymbol(_tokenSymbol);
externalStorage.setBuyPrice(_buyPrice);
externalStorage.setCirculationCap(_circulationCap);
externalStorage.setFoundation(_foundation);
externalStorage.setBalanceLimit(_balanceLimit);
emit ConfigChanged(_buyPrice, _circulationCap, _balanceLimit);
return true;
}
function configureFromStorage() public onlySuperAdmins initStorage returns (bool) {
freezeToken(true);
return true;
}
function updateStorage(string newStorageName, string newLedgerName) public onlySuperAdmins returns (bool) {
require(frozenToken);
storageName = newStorageName;
ledgerName = newLedgerName;
configureFromStorage();
address ledgerAddress = Registry(registry).getStorage(ledgerName);
address storageAddress = Registry(registry).getStorage(storageName);
emit StorageUpdated(storageAddress, ledgerAddress);
return true;
}
function name() public view returns(string) {
return bytes32ToString(externalStorage.getTokenName());
}
function symbol() public view returns(string) {
return bytes32ToString(externalStorage.getTokenSymbol());
}
function totalInCirculation() public view returns(uint256) {
return tokenLedger.totalInCirculation().add(totalUnvestedAndUnreleasedTokens());
}
function tokenBalanceLimit() public view returns(uint256) {
return externalStorage.getBalanceLimit();
}
function circulationCap() public view unlesspgraded returns(uint256) {
return externalStorage.getCirculationCap();
}
function foundation() public view returns(address) {
return externalStorage.getFoundation();
}
function totalSupply() public view returns(uint256) {
return tokenLedger.totalTokens();
}
function tokensAvailable() public view unlesspgraded returns(uint256) {
return totalSupply().sub(totalInCirculation());
}
function balanceOf(address account) public view returns (uint256) {
address thisAddress = this;
if (thisAddress == account) {
return tokensAvailable();
} else {
return tokenLedger.balanceOf(account);
}
}
function transfer(address recipient, uint256 amount) public unlessFrozen returns (bool) {
require(allowTransfers || whitelistedRecipient[recipient].status);
require(amount > 0);
require(!frozenAccount[recipient]);
uint256 commissionAmount = getCommission(amount);
uint256 _amount = amount;
if(commissionAmount > 0) {
_amount -= commissionAmount;
tokenLedger.transfer(msg.sender, owner, commissionAmount);
emit Transfer(msg.sender, owner, commissionAmount);
}
tokenLedger.transfer(msg.sender, recipient, _amount);
emit Transfer(msg.sender, recipient, _amount);
return true;
}
function mintTokens(uint256 mintedAmount) public onlySuperAdmins returns (bool) {
uint256 _circulationCap = externalStorage.getCirculationCap();
tokenLedger.mintTokens(mintedAmount);
emit Mint(mintedAmount, tokenLedger.totalTokens(), _circulationCap);
emit Transfer(address(0), this, mintedAmount);
return true;
}
function grantTokens(address recipient, uint256 amount) public onlySuperAdmins returns (bool) {
require(amount <= tokensAvailable());
require(!frozenAccount[recipient]);
tokenLedger.debitAccount(recipient, amount);
emit Transfer(this, recipient, amount);
return true;
}
function setHaltPurchase(bool _haltPurchase) public onlySuperAdmins returns (bool) {
haltPurchase = _haltPurchase;
if (_haltPurchase) {
emit PurchaseHalted();
} else {
emit PurchaseResumed();
}
return true;
}
function foundationDeposit() public payable returns (bool) {
return true;
}
function allowance(address owner, address spender) public view returns (uint256) {
return externalStorage.getAllowance(owner, spender);
}
function transferFrom(address from, address to, uint256 value) public unlessFrozen returns (bool) {
require(allowTransfers);
require(!frozenAccount[from]);
require(!frozenAccount[to]);
require(from != msg.sender);
require(value > 0);
uint256 allowanceValue = allowance(from, msg.sender);
require(allowanceValue >= value);
tokenLedger.transfer(from, to, value);
externalStorage.setAllowance(from, msg.sender, allowanceValue.sub(value));
emit Transfer(from, to, value);
return true;
}
function approve(address spender, uint256 value) public unlessFrozen returns (bool) {
require(spender != address(0));
require(!frozenAccount[spender]);
require(msg.sender != spender);
externalStorage.setAllowance(msg.sender, spender, value);
emit Approval(msg.sender, spender, value);
return true;
}
function increaseApproval(address spender, uint256 addedValue) public unlessFrozen returns (bool) {
return approve(spender, externalStorage.getAllowance(msg.sender, spender).add(addedValue));
}
function decreaseApproval(address spender, uint256 subtractedValue) public unlessFrozen returns (bool) {
uint256 oldValue = externalStorage.getAllowance(msg.sender, spender);
if (subtractedValue > oldValue) {
return approve(spender, 0);
} else {
return approve(spender, oldValue.sub(subtractedValue));
}
}
function grantVestedTokens(
address beneficiary,
uint256 fullyVestedAmount,
uint256 startDate, // 0 indicates start "now"
uint256 cliffSec,
uint256 durationSec,
bool isRevocable
) public onlySuperAdmins returns(bool) {
uint256 _circulationCap = externalStorage.getCirculationCap();
require(beneficiary != address(0));
require(!frozenAccount[beneficiary]);
require(durationSec >= cliffSec);
require(totalInCirculation().add(fullyVestedAmount) <= _circulationCap);
require(fullyVestedAmount <= tokensAvailable());
uint256 _now = now;
if (startDate == 0) {
startDate = _now;
}
uint256 cliffDate = startDate.add(cliffSec);
externalStorage.setVestingSchedule(
beneficiary,
fullyVestedAmount,
startDate,
cliffDate,
durationSec,
isRevocable
);
emit VestedTokenGrant(beneficiary, startDate, cliffDate, durationSec, fullyVestedAmount, isRevocable);
return true;
}
function revokeVesting(address beneficiary) public onlySuperAdmins returns (bool) {
require(beneficiary != address(0));
externalStorage.revokeVesting(beneficiary);
releaseVestedTokensForBeneficiary(beneficiary);
emit VestedTokenRevocation(beneficiary);
return true;
}
function releaseVestedTokens() public unlessFrozen returns (bool) {
return releaseVestedTokensForBeneficiary(msg.sender);
}
function releaseVestedTokensForBeneficiary(address beneficiary) public unlessFrozen returns (bool) {
require(beneficiary != address(0));
require(!frozenAccount[beneficiary]);
uint256 unreleased = releasableAmount(beneficiary);
if (unreleased == 0) { return true; }
externalStorage.releaseVestedTokens(beneficiary);
tokenLedger.debitAccount(beneficiary, unreleased);
emit Transfer(this, beneficiary, unreleased);
emit VestedTokenRelease(beneficiary, unreleased);
return true;
}
function releasableAmount(address beneficiary) public view returns (uint256) {
return externalStorage.releasableAmount(beneficiary);
}
function totalUnvestedAndUnreleasedTokens() public view returns (uint256) {
return externalStorage.getTotalUnvestedAndUnreleasedTokens();
}
function vestingMappingSize() public view returns (uint256) {
return externalStorage.vestingMappingSize();
}
function vestingBeneficiaryForIndex(uint256 index) public view returns (address) {
return externalStorage.vestingBeneficiaryForIndex(index);
}
function vestingSchedule(address _beneficiary) public
view returns (uint256 startDate,
uint256 cliffDate,
uint256 durationSec,
uint256 fullyVestedAmount,
uint256 vestedAmount,
uint256 vestedAvailableAmount,
uint256 releasedAmount,
uint256 revokeDate,
bool isRevocable) {
(
startDate,
cliffDate,
durationSec,
fullyVestedAmount,
releasedAmount,
revokeDate,
isRevocable
) = externalStorage.getVestingSchedule(_beneficiary);
vestedAmount = externalStorage.vestedAmount(_beneficiary);
vestedAvailableAmount = externalStorage.vestedAvailableAmount(_beneficiary);
}
function setAllowTransfers(bool _allowTransfers) public onlySuperAdmins returns (bool) {
allowTransfers = _allowTransfers;
return true;
}
function setContributionMinimum(uint256 _contributionMinimum) public onlySuperAdmins returns (bool) {
contributionMinimum = _contributionMinimum;
return true;
}
function totalTransferWhitelistMapping() public view returns (uint256) {
return whitelistedRecipientForIndex.length;
}
function setWhitelistedRecipient(
address recipient,
bool _allowTransfers,
string details,
address vettingAgent
) public onlyAdmins returns (bool) {
require(recipient != address(0));
whitelistedRecipient[recipient] = allowedAddr({status: _allowTransfers, details: details, vettingAgent: vettingAgent});
if (!processedWhitelistedRecipient[recipient]) {
whitelistedRecipientForIndex.push(recipient);
processedWhitelistedRecipient[recipient] = true;
}
return true;
}
}