EX1A-15 ADD EXHB 31 smartcontracts.htm

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

EXHIBIT 1A-14

 

SMART CONTRACTS



 

 

Security Token

 

 

pragma solidity 0.5.0;

 

 

 

 

 

import "../utils/Pausable.sol";

 

 

 

 

contract ISecurityToken is Pausable {

 

  // ERC-20

 

 

 

 

  uint256 public totalSupply;

 

 

 

 

  function balanceOf(address tokenHolder) public view returns (uint256);

 

 

 

 

  event Transfer(address indexed from, address indexed to, uint256 amount);

 

 

 

 

  // OKTO Security Token

 

 

 

 

  string public name;

 

  string public symbol;

 

  uint8 public decimals;

 

  address public whitelistAddress;

 

  address[] public operators;

 

  bool public released;

 

 

 

 

  // Modules handling

 

  function addModule(address moduleAddress) public;



 

  function removeModule(address moduleAddress) public;

 

  function release() public;

 

 

 

 

  // Operators handling

 

  function authorizeOperator(address operator) public;

 

  function revokeOperator(address operator) public;

 

  function isOperator(address operator) public view returns (bool);

 

 

 

 

  // Tokens handling

 

  function balanceOfByTranche(bytes32 tranche, address tokenHolder) public view returns (uint256);

 

  function getDestinationTranche(bytes32 sourceTranche, address from, uint256 amount, bytes memory data) public view returns(bytes32);

 

  function canTransfer(bytes32 tranche, address operator, address from, address to, uint256 amount, bytes memory data) public view returns (byte, string memory, bytes32);

 

  function transferByTranche(bytes32 tranche, address to, uint256 amount, bytes memory data) public returns (bytes32);

 

  function operatorTransferByTranche(bytes32 tranche, address from, address to, uint256 amount, bytes memory data) public returns (bytes32);

 

  function tranchesOf(address tokenHolder) public view returns (bytes32[] memory);

 

  function issueByTranche(bytes32 tranche, address tokenHolder, uint256 amount, bytes memory data) public;

 

  function burnByTranche(bytes32 tranche, address tokenHolder, uint256 amount, bytes memory data) public;

 

 

 

 

  // Events

 

  event AddedModule(address moduleAddress, string moduleType);

 

  event RemovedModule(address moduleAddress);

 

  event Released();

 

  event AuthorizedOperator(address indexed operator);

 

  event RevokedOperator(address indexed operator);

 

  event TransferByTranche(bytes32 fromTranche, bytes32 toTranche, address indexed operator, address indexed from, address indexed to, uint256 amount, bytes data);



 

  event IssuedByTranche(bytes32 tranche, address indexed operator, address indexed to, uint256 amount, bytes data);

 

  event BurnedByTranche(bytes32 tranche, address indexed operator, address indexed from, uint256 amount, bytes data);

 

}



 

Custom Token Module

 

 

pragma solidity ^0.5.0;

 

 

 

 

 

import "../tokens/TokenModule.sol";

 

 

 

 

contract CustomTokenModule is TokenModule {

 

  string public description;

 

 

 

 

  constructor(address _tokenAddress)

 

  TokenModule(_tokenAddress, "custom")

 

  public

 

  {

 

  }

 

}



 

Forced Transfer Module

 

pragma solidity ^0.5.0;

 

 

 

 

 

import "../utils/Factory.sol";

 

import "../tokens/TokenModule.sol";

 

import "./Module.sol";

 

 

 

 

contract ForcedTransferTokenModule is TransferValidatorTokenModule,TransferListenerTokenModule,TokenModule {

 

  struct ForcedTransfer {

 

      bytes32 fromTranche;

 

      bytes32 toTranche;

 

      address operator;

 

      address from;

 

      address to;

 

      uint256 amount;

 

  }

 

 

 

 

  mapping(bytes32 => ForcedTransfer) pendingForceTransfers;

 

  uint256 numberOfPendingTransfers;

 

 

 

 

  constructor(address _tokenAddress)

 

  TokenModule(_tokenAddress, "forcedTransfer")

 

  public

 

  {

 

  }

 

 

 

 

  function getFeatures()



 

  public view returns(Module.Feature[] memory)

 

  {

 

      Module.Feature[] memory features = new Module.Feature[](2);

 

      features[0] = Module.Feature.TransferValidator;

 

      features[1] = Module.Feature.TransferListener;

 

      return features;

 

  }

 

 

 

 

  function approveForcedTransfer(bytes32 fromTranche, bytes32 toTranche, address operator, address from, address to, uint256 amount)

 

  onlyTokenOwner

 

  public

 

  {

 

      require(from != address(0), "Invalid source address");

 

      require(to != address(0), "Invalid destination address");

 

      require(operator != address(0), "Invalid operator");

 

      require(amount >= 0, "Negative amount");

 

 

 

 

      bytes memory hashBytes = abi.encodePacked(fromTranche, toTranche, operator, from, to, amount);

 

      bytes32 hash = keccak256(hashBytes);

 

      if (pendingForceTransfers[hash].from == address(0)) {

 

          pendingForceTransfers[hash].fromTranche = fromTranche;

 

          pendingForceTransfers[hash].toTranche = toTranche;

 

          pendingForceTransfers[hash].operator = operator;

 

          pendingForceTransfers[hash].from = from;

 

          pendingForceTransfers[hash].to = to;

 

          pendingForceTransfers[hash].amount = amount;

 

          numberOfPendingTransfers++;

 

          emit ApprovedForcedTransfer(fromTranche, toTranche, operator, from, to, amount, hash);

 

      }



 

  }

 

 

 

 

  function revokeForcedTransfer(bytes32 fromTranche, bytes32 toTranche, address operator, address from, address to, uint256 amount)

 

  onlyTokenOwner

 

  public

 

  {

 

      require(from != address(0), "Invalid source address");

 

      require(to != address(0), "Invalid destination address");

 

      require(operator != address(0), "Invalid operator");

 

      require(amount >= 0, "Negative amount");

 

 

 

 

      bytes memory hashBytes = abi.encodePacked(fromTranche, toTranche, operator, from, to, amount);

 

      bytes32 hash = keccak256(hashBytes);

 

      if (pendingForceTransfers[hash].from != address(0)) {

 

          delete pendingForceTransfers[hash];

 

          numberOfPendingTransfers--;

 

          emit RevokedForcedTransfer(fromTranche, toTranche, operator, from, to, amount, hash);

 

      }

 

  }

 

 

 

 

  function validateTransfer(bytes32 fromTranche, bytes32 toTranche, address operator, address from, address to, uint256 amount, bytes memory)

 

  public view returns (byte, string memory)

 

  {

 

      if (numberOfPendingTransfers > 0) {

 

          bytes memory hashBytes = abi.encodePacked(fromTranche, toTranche, operator, from, to, amount);

 

          bytes32 hash = keccak256(hashBytes);

 

          ForcedTransfer storage forcedTransfer = pendingForceTransfers[hash];

 

          // we check one attribute to see if it exists



 

          if (forcedTransfer.from == from) {

 

              return (0xAF, "Forced transfer");

 

          }

 

      }

 

      // if it is not forced, we still returned approved

 

      return (0xA1, "Approved");

 

  }

 

 

 

 

  function transferDone(bytes32 fromTranche, bytes32 toTranche, address operator, address from, address to, uint256 amount, bytes memory)

 

  onlyToken

 

  public

 

  {

 

      if (numberOfPendingTransfers == 0) {

 

          // no need to check this

 

          return;

 

      }

 

      bytes memory hashBytes = abi.encodePacked(fromTranche, toTranche, operator, from, to, amount);

 

      bytes32 hash = keccak256(hashBytes);

 

      if (pendingForceTransfers[hash].from != address(0)) {

 

          emit ExecutedForcedTransfer(fromTranche, toTranche, operator, from, to, amount, hash);

 

          delete pendingForceTransfers[hash];

 

          numberOfPendingTransfers--;

 

      }

 

  }

 

 

 

 

  event ApprovedForcedTransfer(bytes32 fromTranche, bytes32 toTranche, address indexed operator, address indexed from, address to, uint256 amount, bytes32 hash);

 

  event RevokedForcedTransfer(bytes32 fromTranche, bytes32 toTranche, address indexed operator, address indexed from, address to, uint256 amount, bytes32 hash);

 

  event ExecutedForcedTransfer(bytes32 fromTranche, bytes32 toTranche, address indexed operator, address indexed from, address to, uint256 amount, bytes32 hash);



 

}

 

 

 

 

contract ForcedTransferTokenModuleFactory is Factory {

 

  function createInstance(address tokenAddress)

 

  public returns(address)

 

  {

 

      ForcedTransferTokenModule instance = new ForcedTransferTokenModule(tokenAddress);

 

      instance.transferOwnership(msg.sender);

 

      addInstance(address(instance));

 

      return address(instance);

 

  }

 

}



 

Investor Limite Module

 

 

pragma solidity ^0.5.0;

 

 

 

 

 

import "../utils/SafeMath.sol";

 

import "../utils/Factory.sol";

 

import "../whitelists/IWhitelist.sol";

 

import "../whitelists/WhitelistModule.sol";

 

import "../tokens/ISecurityToken.sol";

 

import "../tokens/TokenModule.sol";

 

import "./Module.sol";

 

 

 

 

contract InvestorsLimitTokenModule is TransferValidatorTokenModule,TransferListenerTokenModule,TokenModule,WhitelistModule {

 

  using SafeMath for uint256;

 

 

 

 

  bytes32 constant INVESTOR_ID_PROP = bytes32("investorId");

 

 

 

 

  uint256 public limit;

 

  bool public checkInvestorId;

 

 

 

 

  uint256 public numberOfInvestors;

 

  bytes32 investorIdProperty;

 

  mapping(bytes32 => uint256) balancePerInvestor;

 

 

 

 

  constructor(address _tokenAddress, address _whitelistAddress, uint256 _limit, bool _checkInvestorId)

 

  TokenModule(_tokenAddress, "investorsLimit")



 

  WhitelistModule(_whitelistAddress, "investorsLimit")

 

  public

 

  {

 

      require(_limit > 0, "Limit must be greater than zero");

 

      ISecurityToken token = ISecurityToken(tokenAddress);

 

      require(token.whitelistAddress() == _whitelistAddress, "Whitelist must be the same as the whitelist in the token");

 

 

 

 

      limit = _limit;

 

      checkInvestorId = _checkInvestorId;

 

  }

 

 

 

 

  function getFeatures()

 

  public view returns(Module.Feature[] memory)

 

  {

 

      Module.Feature[] memory features = new Module.Feature[](3);

 

      features[0] = Module.Feature.TransferValidator;

 

      features[1] = Module.Feature.TransferListener;

 

      features[2] = Module.Feature.WhitelistListener;

 

      return features;

 

  }

 

 

 

 

  function validateTransfer(bytes32, bytes32, address, address from, address to, uint256 amount, bytes memory)

 

  public view returns (byte, string memory)

 

  {

 

      ISecurityToken token = ISecurityToken(tokenAddress);

 

      uint256 diff;

 

      if (checkInvestorId) {

 

          // validate using balance per investor

 

          IWhitelist whitelist = IWhitelist(whitelistAddress);

 

          bytes32 fromInvestorId;



 

          bytes32 toInvestorId;

 

          if (from != address(0)) {

 

              fromInvestorId = whitelist.getProperty(from, INVESTOR_ID_PROP);

 

          }

 

          if (to != address(0)) {

 

              toInvestorId = whitelist.getProperty(to, INVESTOR_ID_PROP);

 

          }

 

          if (to != address(0) && balancePerInvestor[toInvestorId] == 0) {

 

              // if the sender is transferring all its tokens, then we can assume there will be one investor less

 

              diff = (from != address(0) && balancePerInvestor[fromInvestorId] == amount) ? 1 : 0;

 

              // this is a new investor so we need to check limit

 

              if ((numberOfInvestors - diff) >= limit) {

 

                  return (0xA8, "Maximum number of investors reached");

 

              }

 

          }

 

      } else {

 

          if (to != address(0) && token.balanceOf(to) == 0) {

 

              // if the sender is transferring all its tokens, then we can assume there will be one investor less

 

              diff = (from != address(0) && token.balanceOf(from) == amount) ? 1 : 0;

 

              // this is a new investor so we need to check limit

 

              if ((numberOfInvestors - diff) >= limit) {

 

                  return (0xA8, "Maximum number of investors reached");

 

              }

 

          }

 

      }

 

      return (0xA1, "Approved");

 

  }

 

 

 

 

  function transferDone(bytes32, bytes32, address, address from, address to, uint256 amount, bytes memory)



 

  onlyToken

 

  public

 

  {

 

      ISecurityToken token = ISecurityToken(tokenAddress);

 

      if (checkInvestorId) {

 

          // if there is a whitelist we should take into account balancer per investor instead of per wallet

 

          IWhitelist whitelist = IWhitelist(whitelistAddress);

 

          bytes32 fromInvestorId;

 

          bytes32 toInvestorId;

 

          if (from != address(0)) {

 

              fromInvestorId = whitelist.getProperty(from, INVESTOR_ID_PROP);

 

              balancePerInvestor[fromInvestorId] = balancePerInvestor[fromInvestorId].sub(amount);

 

          }

 

          if (to != address(0)) {

 

              toInvestorId = whitelist.getProperty(to, INVESTOR_ID_PROP);

 

              balancePerInvestor[toInvestorId] = balancePerInvestor[toInvestorId].add(amount);

 

          }

 

          if (to != address(0) && balancePerInvestor[toInvestorId] == amount) {

 

              // it means that this is a new investor as all the tokens are the ones that were transferred in this operation

 

              numberOfInvestors++;

 

          }

 

          if (from != address(0) && balancePerInvestor[fromInvestorId] == 0) {

 

              // decrease the number of investors as the sender does not have any tokens after the transaction

 

              numberOfInvestors--;

 

          }

 

      } else {

 

          if (to != address(0) && token.balanceOf(to) == amount) {

 

              // it means that this is a new investor as all the tokens are the ones that were transferred in this operation

 

              numberOfInvestors++;



 

          }

 

          if (from != address(0) && token.balanceOf(from) == 0) {

 

              // decrease the number of investors as the sender does not have any tokens after the transaction

 

              numberOfInvestors--;

 

          }

 

      }

 

  }

 

 

 

 

  function investorUpdated(address investor, bytes32 bucket, bytes32 newValue, bytes32 oldValue)

 

  onlyWhitelist

 

  public

 

  {

 

      if (bucket == INVESTOR_ID_PROP && newValue != oldValue) {

 

          ISecurityToken token = ISecurityToken(tokenAddress);

 

          uint256 balanceOfAddress = token.balanceOf(investor);

 

          // move balance of the address to the new investor

 

          balancePerInvestor[oldValue] = balancePerInvestor[oldValue].sub(balanceOfAddress);

 

          balancePerInvestor[newValue] = balancePerInvestor[newValue].add(balanceOfAddress);

 

          if (balancePerInvestor[newValue] == balanceOfAddress) {

 

              // it means that this is a new investor as all the tokens are the ones that were moved in this operation

 

              numberOfInvestors++;

 

          }

 

          if (balancePerInvestor[oldValue] == 0) {

 

              // decrease the number of investors as the old investor does not have any tokens after the operation

 

              numberOfInvestors--;

 

          }

 

          if (numberOfInvestors > limit) {

 

              revert("Maximum number of investors reached");



 

          }

 

      }

 

  }

 

}

 

 

 

 

contract InvestorsLimitTokenModuleFactory is Factory {

 

  function createInstance(address tokenAddress, uint256 limit, bool checkInvestorId)

 

  public returns(address)

 

  {

 

      ISecurityToken token = ISecurityToken(tokenAddress);

 

      address whitelistAddress = token.whitelistAddress();

 

      InvestorsLimitTokenModule instance = new InvestorsLimitTokenModule(tokenAddress, whitelistAddress, limit, checkInvestorId);

 

      instance.transferOwnership(msg.sender);

 

      addInstance(address(instance));

 

      return address(instance);

 

  }

 

}



 

KYC Accreditation Module

 

 

pragma solidity ^0.5.0;

 

 

 

 

 

import "../whitelists/IWhitelist.sol";

 

import "../utils/Factory.sol";

 

import "../tokens/TokenModule.sol";

 

import "./Module.sol";

 

 

 

 

contract KycTokenModule is TransferValidatorTokenModule,TokenModule {

 

  address public whitelistAddress;

 

 

 

 

  bytes32 constant KYC_PROP = bytes32("kycStatus");

 

 

 

 

  constructor(address _tokenAddress)

 

  TokenModule(_tokenAddress, "kyc")

 

  public

 

  {

 

      ISecurityToken token = ISecurityToken(tokenAddress);

 

 

 

 

      whitelistAddress = token.whitelistAddress();

 

 

 

 

  }

 

 

 

 

  function getFeatures()

 

  public view returns(Module.Feature[] memory)

 

  {

 

      Module.Feature[] memory features = new Module.Feature[](1);



 

      features[0] = Module.Feature.TransferValidator;

 

      return features;

 

  }

 

 

 

 

 

 

 

  function validateTransfer(bytes32, bytes32, address, address, address to, uint256, bytes memory)

 

  public view returns (byte, string memory)

 

  {

 

      IWhitelist whitelist = IWhitelist(whitelistAddress);

 

      uint256 propValue = uint256(whitelist.getProperty(to, KYC_PROP));

 

      if (propValue == 1 || propValue == 2) {

 

          return (0xA1, "Approved");

 

      } else {

 

          return (0xA6, "Receiver not in whitelist");

 

      }

 

  }

 

}

 

 

 

 

contract KycTokenModuleFactory is Factory {

 

  function createInstance(address tokenAddress)

 

  public returns(address)

 

  {

 

      KycTokenModule instance = new KycTokenModule(tokenAddress);

 

      instance.transferOwnership(msg.sender);

 

      addInstance(address(instance));

 

      return address(instance);

 

  }

 

}

 



 

Offering Module

 

 

pragma solidity ^0.5.0;

 

 

 

 

 

import "../utils/Factory.sol";

 

import "../utils/Pausable.sol";

 

import "../tokens/TokenModule.sol";

 

import "./Module.sol";

 

 

 

 

contract OfferingTokenModule is TransferValidatorTokenModule,TokenModule,Pausable {

 

  uint256 public start;

 

  uint256 public end;

 

 

 

 

  constructor(address _tokenAddress, uint256 _start, uint256 _end)

 

  TokenModule(_tokenAddress, "offering")

 

  public

 

  {

 

      start = _start;

 

      end = _end;

 

  }

 

 

 

 

  function getFeatures()

 

  public view returns(Module.Feature[] memory)

 

  {

 

      Module.Feature[] memory features = new Module.Feature[](1);

 

      features[0] = Module.Feature.TransferValidator;

 

      return features;

 

  }



 

 

 

 

  function issueTokens(bytes32[] memory tranches, address[] memory investors, uint256[] memory amounts)

 

  onlyTokenOperator whenNotPaused

 

  public

 

  {

 

      require(investors.length == tranches.length && tranches.length == amounts.length, "Number of investors, tranches and amounts does not match");

 

      require(investors.length > 0, "Tokens for at least one investor should be issued");

 

      require(now >= start, "The offering has not started yet");

 

      require(now <= end, "The offering has finished already");

 

      byte res;

 

      string memory message;

 

      ISecurityToken token = ISecurityToken(tokenAddress);

 

      for (uint i = 0; i < investors.length; i++) {

 

          (res, message, ) = token.canTransfer(tranches[i], msg.sender, address(0), investors[i], amounts[i], abi.encodePacked("issuing"));

 

          if (res != 0xA0 && res != 0xA1 && res != 0xA2 && res != 0xAF) {

 

              emit TokenAllocationError(i, res, message);

 

          } else {

 

              token.issueByTranche(tranches[i], investors[i], amounts[i], abi.encodePacked("issuing"));

 

          }

 

      }

 

  }

 

 

 

 

  function reserveTokens(bytes32[] memory tranches, address[] memory investors, uint256[] memory amounts)

 

  onlyTokenOwner whenNotPaused

 

  public

 

  {

 

      require(investors.length == tranches.length && tranches.length == amounts.length, "Number of investors, tranches and amounts does not match");



 

      require(investors.length > 0, "Tokens for at least one investor should be issued");

 

      require(now < start, "The offering has started already");

 

      byte res;

 

      string memory message;

 

      ISecurityToken token = ISecurityToken(tokenAddress);

 

      for (uint i = 0; i < investors.length; i++) {

 

          (res, message, ) = token.canTransfer(tranches[i], msg.sender, address(0), investors[i], amounts[i], abi.encodePacked("reservation"));

 

          if (res != 0xA0 && res != 0xA1 && res != 0xA2 && res != 0xAF) {

 

              emit TokenAllocationError(i, res, message);

 

          } else {

 

              token.issueByTranche(tranches[i], investors[i], amounts[i], abi.encodePacked("reservation"));

 

          }

 

      }

 

  }

 

 

 

 

  function validateTransfer(bytes32, bytes32, address, address from, address, uint256, bytes memory data)

 

  public view returns (byte, string memory)

 

  {

 

      if (from == address(0)) {

 

          // if this is a token reservation (only done by the owner of the token) we don't perform this validation

 

          if (keccak256(data) != keccak256(abi.encodePacked("reservation"))) {

 

              // we need to only allow issuance if we are between start and end

 

              if (now < start || now > end) {

 

                  return (0xA8, "Offering not in progress");

 

              } else if (paused) {

 

                  // if offering is in progress, but it is paused we will return an error

 

                  return (0xA8, "Offering is paused");

 

              }



 

          } else {

 

              // if it is a reservation, but it is after the offering started, return an error

 

              if (now >= start) {

 

                  return (0xA8, "Offering already started");

 

              }

 

          }

 

      } else {

 

          // if this is a regular transfer and not issuance, we will reject it if the offering is not finished

 

          if (now <= end) {

 

              return (0xA8, "Transfers are not allowed until offering is finished");

 

          }

 

      }

 

      return (0xA1, "Approved");

 

  }

 

 

 

 

  event TokenAllocationError(uint256 index, byte code, string errorMessage);

 

}

 

 

 

 

contract OfferingTokenModuleFactory is Factory {

 

  function createInstance(address tokenAddress, uint256 start, uint256 end)

 

  public returns(address)

 

  {

 

      OfferingTokenModule instance = new OfferingTokenModule(tokenAddress, start, end);

 

      instance.transferOwnership(msg.sender);

 

      addInstance(address(instance));

 

      return address(instance);

 

  }

 

}



 

Restrict Sender Module

 

 

pragma solidity ^0.5.0;

 

 

 

 

 

import "../whitelists/Whitelist.sol";

 

import "../utils/Factory.sol";

 

import "../tokens/TokenModule.sol";

 

import "./Module.sol";

 

 

 

 

contract RestrictSenderTokenModule is TransferValidatorTokenModule,TokenModule {

 

  address public whitelistAddress;

 

  bool public allowOperators;

 

  bool public allowAts;

 

 

 

 

  bytes32 constant ATS_PROP = bytes32("ats");

 

 

 

 

  constructor(address _tokenAddress, bool _allowOperators, bool _allowAts)

 

  TokenModule(_tokenAddress, "restrictSender")

 

  public

 

  {

 

      require(_allowOperators || _allowAts, "You need to allow at least one type of users");

 

 

 

 

      ISecurityToken token = ISecurityToken(tokenAddress);

 

      whitelistAddress = token.whitelistAddress();

 

      allowOperators = _allowOperators;

 

      allowAts = _allowAts;

 

  }

 

 

 



 

  function getFeatures()

 

  public view returns(Module.Feature[] memory)

 

  {

 

      Module.Feature[] memory features = new Module.Feature[](1);

 

      features[0] = Module.Feature.TransferValidator;

 

      return features;

 

  }

 

 

 

 

 

 

 

  function validateTransfer(bytes32, bytes32, address operator, address from, address, uint256, bytes memory)

 

  public view returns (byte, string memory)

 

  {

 

      IWhitelist whitelist = IWhitelist(whitelistAddress);

 

      if (

 

          allowOperators && operator != address(0) ||

 

          allowAts && operator == address(0) && whitelist.getProperty(from, ATS_PROP) == bytes32(uint256(1))

 

      ) {

 

          return (0xA1, "Approved");

 

      } else {

 

          return (0xA5, "Sender is restricted");

 

      }

 

  }

 

}

 

 

 

 

contract RestrictSenderTokenModuleFactory is Factory {

 

  function createInstance(address tokenAddress, bool allowOperators, bool allowAts)

 

  public returns(address)

 

  {

 

      RestrictSenderTokenModule instance = new RestrictSenderTokenModule(tokenAddress, allowOperators, allowAts);



 

      instance.transferOwnership(msg.sender);

 

      addInstance(address(instance));

 

      return address(instance);

 

  }

 

}



 

Supply Limit Module

 

 

pragma solidity ^0.5.0;

 

 

 

 

 

import "../utils/Factory.sol";

 

import "../tokens/ISecurityToken.sol";

 

import "../tokens/TokenModule.sol";

 

import "./Module.sol";

 

 

 

 

contract SupplyLimitTokenModule is TransferValidatorTokenModule,TokenModule {

 

  uint256 public limit;

 

 

 

 

  constructor(address _tokenAddress, uint256 _limit)

 

  TokenModule(_tokenAddress, "supplyLimit")

 

  public

 

  {

 

      limit = _limit;

 

  }

 

 

 

 

  function getFeatures()

 

  public view returns(Module.Feature[] memory)

 

  {

 

      Module.Feature[] memory features = new Module.Feature[](1);

 

      features[0] = Module.Feature.TransferValidator;

 

      return features;

 

  }

 

 

 

 

 

 



 

  function validateTransfer(bytes32, bytes32, address, address from, address, uint256 amount, bytes memory)

 

  public view returns (byte, string memory)

 

  {

 

      if (from == address(0)) {

 

          // this is an issuance of tokens

 

          ISecurityToken token = ISecurityToken(tokenAddress);

 

          if ((token.totalSupply() + amount) > limit) {

 

              return (0xA8, "Supply limit reached");

 

          }

 

      }

 

      return (0xA1, "Approved");

 

  }

 

}

 

 

 

 

contract SupplyLimitTokenModuleFactory is Factory {

 

  function createInstance(address tokenAddress, uint256 limit)

 

  public returns(address)

 

  {

 

      SupplyLimitTokenModule instance = new SupplyLimitTokenModule(tokenAddress, limit);

 

      instance.transferOwnership(msg.sender);

 

      addInstance(address(instance));

 

      return address(instance);

 

  }

 

}



 

Multi-Signature Wallet

 

 

pragma solidity ^0.5.0;

 

 

 

 

 

import "../utils/Factory.sol";

 

 

 

 

 

 

 

// This multisig wallet was develop by Gnosis: https://github.com/gnosis/MultiSigWallet

 

 

 

 

/// @title Multisignature wallet - Allows multiple parties to agree on transactions before execution.

 

/// @author Stefan George - <stefan.george@consensys.net>

 

contract MultiSigWallet {

 

 

 

 

  /*

 

   *  Events

 

   */

 

  event Confirmation(address indexed sender, uint indexed transactionId);

 

  event Revocation(address indexed sender, uint indexed transactionId);

 

  event Submission(uint indexed transactionId);

 

  event Execution(uint indexed transactionId);

 

  event ExecutionFailure(uint indexed transactionId);

 

  event Deposit(address indexed sender, uint value);

 

  event OwnerAddition(address indexed owner);

 

  event OwnerRemoval(address indexed owner);

 

  event RequirementChange(uint required);

 

 

 

 

  /*



 

   *  Constants

 

   */

 

  uint constant public MAX_OWNER_COUNT = 50;

 

 

 

 

  /*

 

   *  Storage

 

   */

 

  mapping (uint => Transaction) public transactions;

 

  mapping (uint => mapping (address => bool)) public confirmations;

 

  mapping (address => bool) public isOwner;

 

  address[] public owners;

 

  uint public required;

 

  uint public transactionCount;

 

 

 

 

  struct Transaction {

 

      address destination;

 

      uint value;

 

      bytes data;

 

      bool executed;

 

  }

 

 

 

 

  /*

 

   *  Modifiers

 

   */

 

  modifier onlyWallet() {

 

      require(msg.sender == address(this));

 

      _;

 

  }

 

 

 

 

  modifier ownerDoesNotExist(address owner) {



 

      require(!isOwner[owner]);

 

      _;

 

  }

 

 

 

 

  modifier ownerExists(address owner) {

 

      require(isOwner[owner]);

 

      _;

 

  }

 

 

 

 

  modifier transactionExists(uint transactionId) {

 

      require(transactions[transactionId].destination != address(0));

 

      _;

 

  }

 

 

 

 

  modifier confirmed(uint transactionId, address owner) {

 

      require(confirmations[transactionId][owner]);

 

      _;

 

  }

 

 

 

 

  modifier notConfirmed(uint transactionId, address owner) {

 

      require(!confirmations[transactionId][owner]);

 

      _;

 

  }

 

 

 

 

  modifier notExecuted(uint transactionId) {

 

      require(!transactions[transactionId].executed);

 

      _;

 

  }

 

 

 



 

  modifier notNull(address _address) {

 

      require(_address != address(0));

 

      _;

 

  }

 

 

 

 

  modifier validRequirement(uint ownerCount, uint _required) {

 

      require(ownerCount <= MAX_OWNER_COUNT

 

      && _required <= ownerCount

 

      && _required != 0

 

      && ownerCount != 0);

 

      _;

 

  }

 

 

 

 

  /// @dev Fallback function allows to deposit ether.

 

  function()

 

  external payable

 

  {

 

      if (msg.value > 0)

 

          emit Deposit(msg.sender, msg.value);

 

  }

 

 

 

 

  /*

 

   * Public functions

 

   */

 

  /// @dev Contract constructor sets initial owners and required number of confirmations.

 

  /// @param _owners List of initial owners.

 

  /// @param _required Number of required confirmations.

 

  constructor(address[] memory _owners, uint _required)

 

  public

 

  validRequirement(_owners.length, _required)



 

  {

 

      for (uint i=0; i<_owners.length; i++) {

 

          require(!isOwner[_owners[i]] && _owners[i] != address(0));

 

          isOwner[_owners[i]] = true;

 

      }

 

      owners = _owners;

 

      required = _required;

 

  }

 

 

 

 

  /// @dev Allows to add a new owner. Transaction has to be sent by wallet.

 

  /// @param owner Address of new owner.

 

  function addOwner(address owner)

 

  public

 

  onlyWallet

 

  ownerDoesNotExist(owner)

 

  notNull(owner)

 

  validRequirement(owners.length + 1, required)

 

  {

 

      isOwner[owner] = true;

 

      owners.push(owner);

 

      emit OwnerAddition(owner);

 

  }

 

 

 

 

  /// @dev Allows to remove an owner. Transaction has to be sent by wallet.

 

  /// @param owner Address of owner.

 

  function removeOwner(address owner)

 

  public

 

  onlyWallet

 

  ownerExists(owner)

 

  {

 

      isOwner[owner] = false;



 

      for (uint i=0; i<owners.length - 1; i++)

 

          if (owners[i] == owner) {

 

              owners[i] = owners[owners.length - 1];

 

              break;

 

          }

 

      owners.length -= 1;

 

      if (required > owners.length)

 

          changeRequirement(owners.length);

 

      emit OwnerRemoval(owner);

 

  }

 

 

 

 

  /// @dev Allows to replace an owner with a new owner. Transaction has to be sent by wallet.

 

  /// @param owner Address of owner to be replaced.

 

  /// @param newOwner Address of new owner.

 

  function replaceOwner(address owner, address newOwner)

 

  public

 

  onlyWallet

 

  ownerExists(owner)

 

  ownerDoesNotExist(newOwner)

 

  {

 

      for (uint i=0; i<owners.length; i++)

 

          if (owners[i] == owner) {

 

              owners[i] = newOwner;

 

              break;

 

          }

 

      isOwner[owner] = false;

 

      isOwner[newOwner] = true;

 

      emit OwnerRemoval(owner);

 

      emit OwnerAddition(newOwner);

 

  }



 

 

 

 

  /// @dev Allows to change the number of required confirmations. Transaction has to be sent by wallet.

 

  /// @param _required Number of required confirmations.

 

  function changeRequirement(uint _required)

 

  public

 

  onlyWallet

 

  validRequirement(owners.length, _required)

 

  {

 

      required = _required;

 

      emit RequirementChange(_required);

 

  }

 

 

 

 

  /// @dev Allows an owner to submit and confirm a transaction.

 

  /// @param destination Transaction target address.

 

  /// @param value Transaction ether value.

 

  /// @param data Transaction data payload.

 

  /// @return Returns transaction ID.

 

  function submitTransaction(address destination, uint value, bytes memory data)

 

  public

 

  returns (uint transactionId)

 

  {

 

      transactionId = addTransaction(destination, value, data);

 

      confirmTransaction(transactionId);

 

  }

 

 

 

 

  /// @dev Allows an owner to confirm a transaction.

 

  /// @param transactionId Transaction ID.

 

  function confirmTransaction(uint transactionId)

 

  public

 

  ownerExists(msg.sender)



 

  transactionExists(transactionId)

 

  notConfirmed(transactionId, msg.sender)

 

  {

 

      confirmations[transactionId][msg.sender] = true;

 

      emit Confirmation(msg.sender, transactionId);

 

      executeTransaction(transactionId);

 

  }

 

 

 

 

  /// @dev Allows an owner to revoke a confirmation for a transaction.

 

  /// @param transactionId Transaction ID.

 

  function revokeConfirmation(uint transactionId)

 

  public

 

  ownerExists(msg.sender)

 

  confirmed(transactionId, msg.sender)

 

  notExecuted(transactionId)

 

  {

 

      confirmations[transactionId][msg.sender] = false;

 

      emit Revocation(msg.sender, transactionId);

 

  }

 

 

 

 

  /// @dev Allows anyone to execute a confirmed transaction.

 

  /// @param transactionId Transaction ID.

 

  function executeTransaction(uint transactionId)

 

  public

 

  ownerExists(msg.sender)

 

  confirmed(transactionId, msg.sender)

 

  notExecuted(transactionId)

 

  {

 

      if (isConfirmed(transactionId)) {

 

          Transaction storage txn = transactions[transactionId];

 

          txn.executed = true;



 

          if (external_call(txn.destination, txn.value, txn.data.length, txn.data))

 

              emit Execution(transactionId);

 

          else {

 

              emit ExecutionFailure(transactionId);

 

              txn.executed = false;

 

          }

 

      }

 

  }

 

 

 

 

  // call has been separated into its own function in order to take advantage

 

  // of the Solidity's code generator to produce a loop that copies tx.data into memory.

 

  function external_call(address destination, uint value, uint dataLength, bytes memory data) private returns (bool) {

 

      bool result;

 

      assembly {

 

          let x := mload(0x40)   // "Allocate" memory for output (0x40 is where "free memory" pointer is stored by convention)

 

          let d := add(data, 32) // First 32 bytes are the padded length of data, so exclude that

 

          result := call(

 

          sub(gas, 34710),   // 34710 is the value that solidity is currently emitting

 

          // It includes callGas (700) + callVeryLow (3, to pay for SUB) + callValueTransferGas (9000) +

 

          // callNewAccountGas (25000, in case the destination address does not exist and needs creating)

 

          destination,

 

          value,

 

          d,

 

          dataLength,        // Size of the input (in bytes) - this is what fixes the padding problem

 

          x,

 

          0                  // Output is ignored, therefore the output size is zero



 

          )

 

      }

 

      return result;

 

  }

 

 

 

 

  /// @dev Returns the confirmation status of a transaction.

 

  /// @param transactionId Transaction ID.

 

  /// @return Confirmation status.

 

  function isConfirmed(uint transactionId)

 

  public

 

  view

 

  returns (bool)

 

  {

 

      uint count = 0;

 

      for (uint i=0; i<owners.length; i++) {

 

          if (confirmations[transactionId][owners[i]])

 

              count += 1;

 

          if (count == required)

 

              return true;

 

      }

 

  }

 

 

 

 

  /*

 

   * Internal functions

 

   */

 

  /// @dev Adds a new transaction to the transaction mapping, if transaction does not exist yet.

 

  /// @param destination Transaction target address.

 

  /// @param value Transaction ether value.

 

  /// @param data Transaction data payload.

 

  /// @return Returns transaction ID.



 

  function addTransaction(address destination, uint value, bytes memory data)

 

  internal

 

  notNull(destination)

 

  returns (uint transactionId)

 

  {

 

      transactionId = transactionCount;

 

      transactions[transactionId] = Transaction({

 

          destination: destination,

 

          value: value,

 

          data: data,

 

          executed: false

 

          });

 

      transactionCount += 1;

 

      emit Submission(transactionId);

 

  }

 

 

 

 

  /*

 

   * Web3 call functions

 

   */

 

  /// @dev Returns number of confirmations of a transaction.

 

  /// @param transactionId Transaction ID.

 

  /// @return Number of confirmations.

 

  function getConfirmationCount(uint transactionId)

 

  public

 

  view

 

  returns (uint count)

 

  {

 

      for (uint i=0; i<owners.length; i++)

 

          if (confirmations[transactionId][owners[i]])

 

              count += 1;

 

  }



 

 

 

 

  /// @dev Returns total number of transactions after filers are applied.

 

  /// @param pending Include pending transactions.

 

  /// @param executed Include executed transactions.

 

  /// @return Total number of transactions after filters are applied.

 

  function getTransactionCount(bool pending, bool executed)

 

  public

 

  view

 

  returns (uint count)

 

  {

 

      for (uint i=0; i<transactionCount; i++)

 

          if (   pending && !transactions[i].executed

 

          || executed && transactions[i].executed)

 

              count += 1;

 

  }

 

 

 

 

  /// @dev Returns list of owners.

 

  /// @return List of owner addresses.

 

  function getOwners()

 

  public

 

  view

 

  returns (address[] memory)

 

  {

 

      return owners;

 

  }

 

 

 

 

  /// @dev Returns array with owner addresses, which confirmed transaction.

 

  /// @param transactionId Transaction ID.

 

  /// @return Returns array of owner addresses.

 

  function getConfirmations(uint transactionId)

 

  public



 

  view

 

  returns (address[] memory _confirmations)

 

  {

 

      address[] memory confirmationsTemp = new address[](owners.length);

 

      uint count = 0;

 

      uint i;

 

      for (i=0; i<owners.length; i++)

 

          if (confirmations[transactionId][owners[i]]) {

 

              confirmationsTemp[count] = owners[i];

 

              count += 1;

 

          }

 

      _confirmations = new address[](count);

 

      for (i=0; i<count; i++)

 

          _confirmations[i] = confirmationsTemp[i];

 

  }

 

 

 

 

  /// @dev Returns list of transaction IDs in defined range.

 

  /// @param from Index start position of transaction array.

 

  /// @param to Index end position of transaction array.

 

  /// @param pending Include pending transactions.

 

  /// @param executed Include executed transactions.

 

  /// @return Returns array of transaction IDs.

 

  function getTransactionIds(uint from, uint to, bool pending, bool executed)

 

  public

 

  view

 

  returns (uint[] memory _transactionIds)

 

  {

 

      uint[] memory transactionIdsTemp = new uint[](transactionCount);

 

      uint count = 0;

 

      uint i;

 

      for (i=0; i<transactionCount; i++)



 

          if (   pending && !transactions[i].executed

 

          || executed && transactions[i].executed)

 

          {

 

              transactionIdsTemp[count] = i;

 

              count += 1;

 

          }

 

      _transactionIds = new uint[](to - from);

 

      for (i=from; i<to; i++)

 

          _transactionIds[i - from] = transactionIdsTemp[i];

 

  }

 

}

 

 

 

 

contract MultiSigWalletFactory is Factory {

 

  function createInstance(address[] memory _owners, uint _required)

 

  public returns(address)

 

  {

 

      MultiSigWallet instance = new MultiSigWallet(_owners, _required);

 

      addInstance(address(instance));

 

      return address(instance);

 

  }

 

}



 

Whitelist

 

 

pragma solidity ^0.5.0;

 

 

 

 

 

import "../utils/Ownable.sol";

 

import "../utils/Factory.sol";

 

import "../utils/AddressArrayLib.sol";

 

import "./WhitelistModule.sol";

 

 

 

 

contract IWhitelist is Ownable {

 

  address[] public validators;

 

 

 

 

  function addValidator(address validator) public;

 

  function removeValidator(address validator) public;

 

  function isValidator(address validator) public view returns(bool);

 

  function setBucket(address investor, bytes32 bucket, bytes32 value) public;

 

  function setManyBuckets(address[] memory investors, bytes32[] memory buckets, bytes32[] memory values) public;

 

  function getBucket(address investor, bytes32 bucket) public view returns(bytes32);

 

  function getProperty(address investor, bytes32 property) public view returns(bytes32);

 

  function addProperty(bytes32 code, bytes32 bucket, uint8 from, uint16 len) public;

 

  function addModule(address moduleAddress) public;

 

  function removeModule(address moduleAddress) public;

 

  function isModule(address moduleAddress) public view returns (bool);

 

 

 

 

  event AddedValidator(address validator);

 

  event RemovedValidator(address validator);

 

  event AddedProperty(bytes32 code, bytes32 bucket, uint8 from, uint16 len);



 

  event AddedModule(address moduleAddress, string moduleType);

 

  event RemovedModule(address moduleAddress);

 

  event UpdatedInvestor(address investor, bytes32 bucket, bytes32 value);

 

}



 

Standard Whitelist

 

 

pragma solidity ^0.5.0;

 

 

 

 

 

import "../utils/Ownable.sol";

 

import "../utils/Factory.sol";

 

import "./Whitelist.sol";

 

 

 

 

///////////////////////////////////////////////////////////////////////////////////////////////////

 

// Standard properties:

 

//

 

// General Bucket ----------------------------------------------

 

// KYC status                         index   0, length   2 bits (00: pending, 01: auto-approved, 10: manually-approved, 11: disapproved)

 

// KYC status updated                 index   2, length  40 bits

 

// AML status                         index  42, length   2 bits (00: pending, 01: auto-approved, 10: manually-approved, 11: disapproved)

 

// AML status updated                 index  44, length  40 bits

 

// Accredited status                  index  84, length   3 bits (000: pending, 001: auto-approved, 010: manually-approved, 011: self-approved, 100: disapproved)

 

// Accredited status updated          index  87, length  40 bits

 

// Country code (two letters code)    index 127, length  16 bits (two letters ascii code lower case)

 

// Insider                            index 143, length   1 bits

 

// Lockup expiration                  index 144, length  40 bits

 

// ATS                                index 184, length   1 bits

 

// Blocked                            index 185, length   1 bits

 

// Investor ID Bucket ------------------------------------------

 

// Investor ID                        index   0, length 256 bits

 

// KYC Reference Bucket ----------------------------------------

 

// KYC Reference                      index   0, length 256 bits



 

// AML Reference Bucket ----------------------------------------

 

// AML Reference                      index   0, length 256 bits

 

// Accredited Reference Bucket ---------------------------------

 

// Accredited Reference               index   0, length 256 bits

 

//

 

///////////////////////////////////////////////////////////////////////////////////////////////////

 

 

 

 

contract StandardWhitelist is Whitelist {

 

  constructor(address[] memory validators, bytes32[] memory codes, bytes32[] memory buckets, uint8[] memory froms, uint16[] memory lens)

 

  Whitelist(validators, codes, buckets, froms, lens)

 

  public

 

  {

 

      // define this standard properties; if they were also passed in the constructor

 

      // they will be overridden

 

 

 

 

      propertiesDefinition[bytes32("kycStatus")].code = bytes32("kycStatus");

 

      propertiesDefinition[bytes32("kycStatus")].bucket = bytes32("general");

 

      propertiesDefinition[bytes32("kycStatus")].from = 0;

 

      propertiesDefinition[bytes32("kycStatus")].len = 2;

 

 

 

 

      propertiesDefinition[bytes32("kycStatusUpdated")].code = bytes32("kycStatusUpdated");

 

      propertiesDefinition[bytes32("kycStatusUpdated")].bucket = bytes32("general");

 

      propertiesDefinition[bytes32("kycStatusUpdated")].from = 2;

 

      propertiesDefinition[bytes32("kycStatusUpdated")].len = 40;

 

 

 

 

      propertiesDefinition[bytes32("amlStatus")].code = bytes32("amlStatus");

 

      propertiesDefinition[bytes32("amlStatus")].bucket = bytes32("general");

 

      propertiesDefinition[bytes32("amlStatus")].from = 42;



 

      propertiesDefinition[bytes32("amlStatus")].len = 2;

 

 

 

 

      propertiesDefinition[bytes32("amlStatusUpdated")].code = bytes32("amlStatusUpdated");

 

      propertiesDefinition[bytes32("amlStatusUpdated")].bucket = bytes32("general");

 

      propertiesDefinition[bytes32("amlStatusUpdated")].from = 44;

 

      propertiesDefinition[bytes32("amlStatusUpdated")].len = 40;

 

 

 

 

      propertiesDefinition[bytes32("accreditedStatus")].code = bytes32("accreditedStatus");

 

      propertiesDefinition[bytes32("accreditedStatus")].bucket = bytes32("general");

 

      propertiesDefinition[bytes32("accreditedStatus")].from = 84;

 

      propertiesDefinition[bytes32("accreditedStatus")].len = 3;

 

 

 

 

      propertiesDefinition[bytes32("accreditedStatusUpdated")].code = bytes32("accreditedStatusUpdated");

 

      propertiesDefinition[bytes32("accreditedStatusUpdated")].bucket = bytes32("general");

 

      propertiesDefinition[bytes32("accreditedStatusUpdated")].from = 87;

 

      propertiesDefinition[bytes32("accreditedStatusUpdated")].len = 40;

 

 

 

 

      propertiesDefinition[bytes32("country")].code = bytes32("country");

 

      propertiesDefinition[bytes32("country")].bucket = bytes32("general");

 

      propertiesDefinition[bytes32("country")].from = 127;

 

      propertiesDefinition[bytes32("country")].len = 16;

 

 

 

 

      propertiesDefinition[bytes32("insider")].code = bytes32("insider");

 

      propertiesDefinition[bytes32("insider")].bucket = bytes32("general");

 

      propertiesDefinition[bytes32("insider")].from = 143;

 

      propertiesDefinition[bytes32("insider")].len = 1;



 

 

 

 

      propertiesDefinition[bytes32("lockupExpiration")].code = bytes32("lockupExpiration");

 

      propertiesDefinition[bytes32("lockupExpiration")].bucket = bytes32("general");

 

      propertiesDefinition[bytes32("lockupExpiration")].from = 144;

 

      propertiesDefinition[bytes32("lockupExpiration")].len = 40;

 

 

 

 

      propertiesDefinition[bytes32("ats")].code = bytes32("ats");

 

      propertiesDefinition[bytes32("ats")].bucket = bytes32("general");

 

      propertiesDefinition[bytes32("ats")].from = 184;

 

      propertiesDefinition[bytes32("ats")].len = 1;

 

 

 

 

      propertiesDefinition[bytes32("blocked")].code = bytes32("blocked");

 

      propertiesDefinition[bytes32("blocked")].bucket = bytes32("general");

 

      propertiesDefinition[bytes32("blocked")].from = 185;

 

      propertiesDefinition[bytes32("blocked")].len = 1;

 

 

 

 

      propertiesDefinition[bytes32("investorId")].code = bytes32("investorId");

 

      propertiesDefinition[bytes32("investorId")].bucket = bytes32("investorId");

 

      propertiesDefinition[bytes32("investorId")].from = 0;

 

      propertiesDefinition[bytes32("investorId")].len = 256;

 

 

 

 

      propertiesDefinition[bytes32("kycReference")].code = bytes32("kycReference");

 

      propertiesDefinition[bytes32("kycReference")].bucket = bytes32("kycReference");

 

      propertiesDefinition[bytes32("kycReference")].from = 0;

 

      propertiesDefinition[bytes32("kycReference")].len = 256;

 

 

 

 

      propertiesDefinition[bytes32("amlReference")].code = bytes32("amlReference");



 

      propertiesDefinition[bytes32("amlReference")].bucket = bytes32("amlReference");

 

      propertiesDefinition[bytes32("amlReference")].from = 0;

 

      propertiesDefinition[bytes32("amlReference")].len = 256;

 

 

 

 

      propertiesDefinition[bytes32("accreditedReference")].code = bytes32("accreditedReference");

 

      propertiesDefinition[bytes32("accreditedReference")].bucket = bytes32("accreditedReference");

 

      propertiesDefinition[bytes32("accreditedReference")].from = 0;

 

      propertiesDefinition[bytes32("accreditedReference")].len = 256;

 

  }

 

}

 

 

 

 

contract StandardWhitelistFactory is Factory {

 

  function createInstance(address[] memory validators, bytes32[] memory codes, bytes32[] memory buckets, uint8[] memory froms, uint16[] memory lens)

 

  public returns(address)

 

  {

 

      StandardWhitelist instance = new StandardWhitelist(validators, codes, buckets, froms, lens);

 

      instance.transferOwnership(msg.sender);

 

      addInstance(address(instance));

 

      return address(instance);

 

  }

 

}