Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- pragma solidity ^0.4.18;
- import "zeppelin-solidity/contracts/math/SafeMath.sol";
- import "zeppelin-solidity/contracts/token/ERC20/ERC20Basic.sol";
- import "../mixins/BalanceManager.sol";
- import "../mixins/Domicile.sol";
- import "./BaseRegulatorService.sol";
- import "../RegulatedToken.sol";
- /**
- * @title On-chain RegulatorService for approving US REIT trades
- * @author Elpizo Choi
- */
- contract REITRegulatorService is BaseRegulatorService, BalanceManager, Domicile {
- using SafeMath for uint256;
- // @dev REIT specific settings that affect token trading at a token level.
- struct REITSettings {
- // @dev Min number of shareholders a token can have
- uint256 shareholderMin;
- // @dev Max number of shareholders a token can have
- uint256 shareholderMax;
- /**
- * @dev Max percentage of shares a shareholder can have,
- * represented in integers. Ex: 10% will be represented as 10.
- */
- uint8 ownershipCap;
- /**
- * @dev Max percentage of shares that can be owned by foreign
- * shareholders, represented in integers.
- * Ex: 50% is represented as 50.
- */
- uint8 foreignOwnedSharesCap;
- /**
- * @dev Current count of shareholders.
- * Updated on `transfer()` functions success.
- */
- uint256 shareholderCount;
- // @dev Current count of foreign owned shares.
- // Updated after successful `transfer()` calls.
- uint256 foreignOwnedShares;
- }
- // @notice Permissions that allow/disallow token trades on a per token level
- mapping(address => REITSettings) private reitSettings;
- // @dev Event raised when a token's shareholder min setting is set
- event LogShareholderMinSet(address indexed _token, uint256 _min);
- // @dev Event raised when a token's shareholder max setting is set
- event LogShareholderMaxSet(address indexed _token, uint256 _max);
- // @dev Event raised when a token's shareholder cap setting is set
- event LogOwnershipCapSet(address indexed _token, uint8 _cap);
- // @dev Event raised when a token's foreign shareholder cap is set
- event LogForeignOwnedSharesCapSet(address indexed _token, uint8 _cap);
- // @dev Event raised when a token's shareholder max setting is set
- event LogShareholderCountSet(address indexed _token, uint256 _count);
- // @dev Event raised when a token's foreign shares count is set
- event LogForeignOwnedSharesSet(address indexed _token, uint256 _shares);
- /**
- * @notice Sets shareholder min for a token
- *
- * @dev This method can only be called by this contract's admin
- *
- * @param _token The address of the token to update
- * @param _min The number of shareholders to set minimum to
- */
- function setShareholderMin(address _token, uint256 _min) public onlyRole(ROLE_ADMIN) {
- reitSettings[_token].shareholderMin = _min;
- emit LogShareholderMinSet(_token, _min);
- }
- /**
- * @notice Sets shareholder max for a token
- *
- * @dev This method can only be called by this contract's admin
- *
- * @param _token The address of the token to update
- * @param _max The number of shareholders to set maximum to
- */
- function setShareholderMax(address _token, uint256 _max) public onlyRole(ROLE_ADMIN) {
- reitSettings[_token].shareholderMax = _max;
- emit LogShareholderMaxSet(_token, _max);
- }
- /**
- * @notice Sets ownership cap for a token
- *
- * @dev This method can only be called by this contract's admin
- * Cap must be between 1 and 100.
- *
- * @param _token The address of the token to update
- * @param _cap The max ownership % a shareholder can have for given token.
- * Given in int form (ex: 10% would be represented as 10)
- */
- function setOwnershipCap(address _token, uint8 _cap) public onlyRole(ROLE_ADMIN) {
- require(_cap >= 1 && _cap <= 100, "Cap must be between 1 and 100");
- reitSettings[_token].ownershipCap = _cap;
- emit LogOwnershipCapSet(_token, _cap);
- }
- /**
- * @notice Sets foreign owned shares cap for a token
- *
- * @dev This method can only be called by this contract's admin
- * Cap must be between 1 and 100.
- *
- * @param _token The address of the token to update
- * @param _cap The max % of foreign-owned shares a token can have.
- * Given in int form (ex: 50% is represented as 50).
- */
- function setForeignOwnedSharesCap(address _token, uint8 _cap)
- public
- onlyRole(ROLE_ADMIN)
- {
- require(_cap >= 1 && _cap <= 100, "Cap must be between 1 and 100");
- reitSettings[_token].foreignOwnedSharesCap = _cap;
- emit LogForeignOwnedSharesCapSet(_token, _cap);
- }
- /**
- * @notice Public method to set `shareholderCount`
- *
- * @dev This method can only be called by this contract's admin
- *
- * @param _token The address of the token to update
- * @param _count The number of shareholders to set current count to
- */
- function setShareholderCount(address _token, uint256 _count)
- public
- onlyRole(ROLE_ADMIN)
- {
- _setShareholderCount(_token, _count);
- }
- /**
- * @notice Public method to set `foreignOwnedShares`
- *
- * @dev This method can only be called by this contract's admin
- *
- * @param _token The address of the token to update
- * @param _count The number of foreign-owned shares for token
- */
- function setForeignOwnedShares(address _token, uint256 _count)
- public
- onlyRole(ROLE_ADMIN)
- {
- _setForeignOwnedShares(_token, _count);
- }
- /**
- * @notice Sets `isForeign` flag for a participant on a token.
- * Also updates `foreignOwnedShares` based on direction of change.
- *
- * @dev This method can only be called by this contract's controller
- *
- * @param _token The address of the token to update
- * @param _participant The address of the trade participant
- * @param _isForeign Whether the participant is foreign for this token
- */
- function setIsForeign(
- address _token,
- bytes32 _participant,
- bool _isForeign
- ) public onlyRole(ROLE_CONTROLLER) {
- bool currForeign = isForeign[_token][_participant];
- // Return if no change
- if (currForeign == _isForeign) {
- return;
- }
- // Update `foreignOwnedShares`
- uint256 currShares = reitSettings[_token].foreignOwnedShares;
- uint256 balance = balances[_participant][_token];
- // Add balance if becoming foreign
- if (_isForeign) {
- _setForeignOwnedShares(_token, currShares.add(balance));
- // Subtract balance if becoming domestic
- } else {
- _setForeignOwnedShares(_token, currShares.sub(balance));
- }
- super.setIsForeign(_token, _participant, _isForeign);
- }
- /**
- * @notice Checks for REIT rules before calling `super.check()` for
- * generic token rules.
- *
- * @param _token The address of the token to be transferred
- * @param _spender The address of the spender of the token
- * (unused in this implementation)
- * @param _from The address of the sender account
- * @param _to The address of the receiver account
- * @param _amount The quantity of the token to trade
- *
- * @return `true` if the trade should be approved and
- * `false` if the trade should not be approved
- */
- function check(
- address _token,
- address _spender,
- address _from,
- address _to,
- uint256 _amount
- )
- public
- onlyTokenOrAdmin(_token)
- returns (uint8)
- {
- if (violatesShareholderMin(_token, _from, _to, _amount)) {
- return CHECK_ESHMIN;
- }
- if (violatesShareholderMax(_token, _from, _to, _amount)) {
- return CHECK_ESHMAX;
- }
- if (violatesOwnershipCap(_token, _to, _amount)) {
- return CHECK_EOWNCAP;
- }
- if (violatesForeignOwnedSharesCap(_token, _from, _to, _amount)) {
- return CHECK_EFOWNSCAP;
- }
- return super.check(_token, _spender, _from, _to, _amount);
- }
- /**
- * @notice Updates `shareholderCount` and `foreignOwnedShares`
- * after `mint()` is successfully called by a `RegulatedToken`
- *
- * @param _to The address of the receiver account
- * @param _amount The quantity of the token to mint
- */
- function onMintSuccess(
- address _to,
- uint256 _amount
- )
- public
- onlyRole(ROLE_TOKEN)
- onlyOwnedWallet(_to)
- {
- bytes32 toUUID = walletOwners[_to];
- // Update user balance
- addBalance(toUUID, msg.sender, _amount);
- /**
- * Update shareholder count.
- *
- * Method is called after minting is successful,
- * so we subtract _amount from
- * balance to find out if `_to` address is a new shareholder.
- */
- uint256 newShareholderCount;
- if (_amount == balances[toUUID][msg.sender]) {
- newShareholderCount = reitSettings[msg.sender].shareholderCount.add(1);
- } else {
- newShareholderCount = reitSettings[msg.sender].shareholderCount;
- }
- _setShareholderCount(msg.sender, newShareholderCount);
- // Update foreign owned shares by adding if `_to` address is foreign
- uint256 currForeignOwnedShares = reitSettings[msg.sender].foreignOwnedShares;
- if (isForeign[msg.sender][toUUID]) {
- _setForeignOwnedShares(msg.sender, currForeignOwnedShares.add(_amount));
- } else {
- _setForeignOwnedShares(msg.sender, currForeignOwnedShares);
- }
- // Call super for general mint callbacks
- super.onMintSuccess(_to, _amount);
- }
- /**
- * @notice Updates `shareholderCount` and `foreignOwnedShares`
- * after `transfer()` or `transferFrom` is successfully
- * called by a `RegulatedToken`
- *
- * @param _spender The address of the spender of the token
- * @param _from The address of the sender account
- * @param _to The address of the receiver account
- * @param _amount The quantity of the token to trade
- */
- function onTransferSuccess(
- address _spender,
- address _from,
- address _to,
- uint256 _amount
- )
- public
- onlyRole(ROLE_TOKEN)
- onlyOwnedWallet(_from)
- onlyOwnedWallet(_to)
- {
- // Update user balances
- subBalance(walletOwners[_from], msg.sender, _amount);
- addBalance(walletOwners[_to], msg.sender, _amount);
- /**
- * Update shareholder count by using amount and current balance
- * to figure out who's a new shareholder and who is no longer one.
- */
- bool subShareholder = balances[walletOwners[_from]][msg.sender] == 0;
- bool addShareholder = balances[walletOwners[_to]][msg.sender] == _amount;
- uint256 currCount = reitSettings[msg.sender].shareholderCount;
- if (subShareholder && !addShareholder) {
- _setShareholderCount(msg.sender, currCount.sub(1));
- } else if (addShareholder && !subShareholder) {
- _setShareholderCount(msg.sender, currCount.add(1));
- } else {
- _setShareholderCount(msg.sender, currCount);
- }
- /**
- * Update foreign owned shares based on `isForeign` property of
- * `from` & `to`.
- *
- * Can reuse `foreignOwnedSharesFromTransfer` because it doesn't
- * depend on updated balances.
- */
- _setForeignOwnedShares(
- msg.sender,
- foreignOwnedSharesFromTransfer(msg.sender, _from, _to, _amount)
- );
- // Call super for general transfer callbacks
- super.onTransferSuccess(_spender, _from, _to, _amount);
- }
- /**
- * @notice Sets current shareholder count for a token
- *
- * @dev This method is used for internal functions
- *
- * @param _token The address of the token to update
- * @param _count The number of shareholders to set current count to
- */
- function _setShareholderCount(address _token, uint256 _count) private {
- reitSettings[_token].shareholderCount = _count;
- emit LogShareholderCountSet(_token, _count);
- }
- /**
- * @notice Sets current number of foreign-owned shares for a token
- *
- * @dev This method is used for internal functions
- *
- * @param _token The address of the token to update
- * @param _count The number of foreign-owned shares for token
- */
- function _setForeignOwnedShares(address _token, uint256 _count) private {
- ERC20Basic rToken = ERC20Basic(_token);
- require(
- _count <= rToken.totalSupply(),
- "Foreign owned shares cannot be more than total supply"
- );
- reitSettings[_token].foreignOwnedShares = _count;
- emit LogForeignOwnedSharesSet(_token, _count);
- }
- /**
- * @notice Checks to see if a trade would result in `shareholderCount`
- * dropping below `shareholderMin`
- *
- * @param _token The address of the token to be transferred
- * @param _from The address of the sender account
- * @param _to The address of the receiver account
- * @param _amount The quantity of the token to trade
- *
- * @return `true` if the trade would result in `shareholderCount`
- * going below `shareholderMin`
- */
- function violatesShareholderMin(
- address _token,
- address _from,
- address _to,
- uint256 _amount
- ) private view returns (bool) {
- uint256 tCount = shareholderCountFromTransfer(_token, _from, _to, _amount);
- return (tCount < reitSettings[_token].shareholderMin);
- }
- /**
- * @notice Checks to see if a trade would result in `shareholderCount`
- * going above `shareholderMax`
- *
- * @param _token The address of the token to be transferred
- * @param _from The address of the sender account
- * @param _to The address of the receiver account
- * @param _amount The quantity of the token to trade
- *
- * @return `true` if the trade would result in `shareholderCount`
- * going above `shareholderMax`
- */
- function violatesShareholderMax(
- address _token,
- address _from,
- address _to,
- uint256 _amount
- ) private view returns (bool) {
- uint256 tCount = shareholderCountFromTransfer(_token, _from, _to, _amount);
- return (tCount > reitSettings[_token].shareholderMax);
- }
- /**
- * @notice Checks to see if a trade would result in a shareholder
- * going over `ownershipCap`.
- *
- * @param _token The address of the token to be transferred
- * @param _to The address of the receiver account
- * @param _amount The quantity of the token to trade
- *
- * @return `true` if the trade would result in a shareholder
- * owning more than `ownershipCap`.
- */
- function violatesOwnershipCap(
- address _token,
- address _to,
- uint256 _amount
- ) private view returns (bool) {
- ERC20Basic rToken = ERC20Basic(_token);
- uint256 toBalance = balances[walletOwners[_to]][_token];
- uint256 _cap = reitSettings[_token].ownershipCap;
- // New balance should be less than capped amount
- uint256 newBalance = toBalance.add(_amount);
- uint256 maxShares = rToken.totalSupply().mul(_cap).div(100);
- return (newBalance > maxShares);
- }
- /**
- * @notice Checks to see if a trade would result in `foreignOwnedShares`
- * going above `foreignOwnedSharesCap`
- *
- * @param _token The address of the token to be transfered
- * @param _from The address of the sender account
- * @param _to The address of the receiver account
- * @param _amount The quantity of the token to trade
- *
- * @return `true` if the trade would result in `foreignOwnedShares`
- * going above `foreignOwnedSharesCap`
- */
- function violatesForeignOwnedSharesCap(
- address _token,
- address _from,
- address _to,
- uint256 _amount
- ) private view returns (bool) {
- uint256 tShares = foreignOwnedSharesFromTransfer(
- _token, _from, _to, _amount
- );
- uint256 totalSupply = ERC20Basic(_token).totalSupply();
- uint256 maxForeignShares = totalSupply.mul(
- reitSettings[_token].foreignOwnedSharesCap
- ).div(100);
- return (tShares > maxForeignShares);
- }
- /**
- * @notice Computes resulting number of foreign-owned shares
- * if a transfer is successful
- *
- * @param _token The address of the token to be transfered
- * @param _from The address of the sender account
- * @param _to The address of the receiver account
- * @param _amount The quantity of the token to trade
- *
- * @return Number of foreign-owned shares if transfer succeeds
- */
- function foreignOwnedSharesFromTransfer(
- address _token,
- address _from,
- address _to,
- uint256 _amount
- )
- private
- view
- onlyOwnedWallet(_from)
- onlyOwnedWallet(_to)
- returns (uint256)
- {
- bool fromIsForeign = isForeign[_token][walletOwners[_from]];
- bool toIsForeign = isForeign[_token][walletOwners[_to]];
- uint256 currForeignShares = reitSettings[_token].foreignOwnedShares;
- // Decrease foreign shares if `from` is foreign but `to` is not
- if (fromIsForeign && !toIsForeign) {
- return currForeignShares.sub(_amount);
- // Increase foreign shares if `from` is not foreign but `to` is
- } else if (!fromIsForeign && toIsForeign) {
- return currForeignShares.add(_amount);
- // Otherwise return same number of shares
- } else {
- return currForeignShares;
- }
- }
- /**
- * @notice Computes resulting shareholder count if a transfer is successful
- *
- * @param _token The address of the token to be transferred
- * @param _from The address of the sender account
- * @param _to The address of the receiver account
- * @param _amount The quantity of the token to trade
- *
- * @return Number of shareholders if transfer succeeds
- */
- function shareholderCountFromTransfer(
- address _token,
- address _from,
- address _to,
- uint256 _amount
- )
- private
- view
- returns (uint256)
- {
- uint256 fromBalance = balances[walletOwners[_from]][_token];
- uint256 toBalance = balances[walletOwners[_to]][_token];
- uint256 currCount = reitSettings[_token].shareholderCount;
- // Return current count if no shares are transferred
- if (_amount == 0) {
- return currCount;
- }
- // Increment if to address is new shareholder
- // and from address will remain a shareholder after transfer
- if (fromBalance > _amount && toBalance == 0) {
- return currCount.add(1);
- // Decrement if from address will go to 0 balance
- // and to address is existing shareholder
- } else if (fromBalance == _amount && toBalance > 0) {
- return currCount.sub(1);
- // Otherwise keep same number of shareholders
- } else {
- return currCount;
- }
- }
- }
Add Comment
Please, Sign In to add comment