EX-4.3 3 ff12019a5ex4-3_inxlimited.htm SMART CONTRACT

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