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));


}


 






    2


     


    

 


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());


}


 






    3


     


    

 


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;


}


 






    4


     


    

 


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);


 






    5


     


    

 


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) {


 






    6


     


    

 


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]);


 






    7


     


    

 


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);


 






    8


     


    

 


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;


}


}




 


 


9