VolumeRestrictionTransferManager

Introduced in

3.0.0

Contract name

VolumeRestrictionTM.sol

Compatible Protocol Version

^3.0.0

Type

Transfer Manager Module

How it works

It is used to restrict the maximum volume of tokens being traded by any individual investor in a given period of time and it also restricts maximum transfers that should be across all the token holders associated. An issuer can also exempt any token holders by adding then into the exemption list to make them unaffected by this module.

Key functionalities (as defined in the Smart Contract)

Initialization

This module is initialized with no parameters. That means during the creation of this contract there’s no need to call any type of configure() function.

Using the module

Layout for the Individual restriction

Note - Below use-case only works when the Alice has only Individual restriction not Individual daily restriction (24 hrs rolling period).

UseCase: Issuer wants to restrict Alice’s transfers as per the volume of tokens. An issuer will do so with the following transaction.

Call the addIndividualRestriction() function with following data.

Now Alice starts trading with the following assumptions.

Day1: Alice tries to sell 1000 tokens

Day 2: Alice tries to sell 5000 tokens

Day 3: No trading

Day 4: No trading

Day 5: Alice tries to sell 6000 tokens

Day 6: Alice tries to sell 3000 tokens

Day 7: No trading

Day 8: Alice tries to sell 4000 tokens

Transfer Verification

  • If _from address is 0x0 or present in the exemption list or paused variable is true then transfers will be unaffected by this module.

  • If _from address has any individual restriction, whether Individual or Individual daily or both. isDefault will be false in _restrictionCheck() function params. Otherwise, it will be true.

  • 4 cases related to _from address

    • _from has Individual Restriction

    • _from has 24 hrs Individual daily restriction

    • _from has both individual restriction and individual daily restriction

    • _from doesn’t have any Individual restriction. It falls into the default restriction. Whatever the default restriction will active that time will apply, whether default or default daily or both.

First Case :-

  • If _from has Individual restriction only & transaction time is between startTime and endTime of the restriction, then transaction will go through only when:

    • _amount is less than or equal to the _allowedAmount - sumOfLastPeriod, where _allowedAmount is the fixed number of tokens (allowedTokens) allowed to transact in a given rolling period when restriction type is Fixed . If not, then allowedAmount will be calculated at the tx processing time according to the current totalSupply of the ST. i.e _allowedAmount = (_restriction.allowedTokens.mul(ISecurityToken(securityToken).totalSupply())) / uint256(10) ** 18.

      sumOfLastPeriod will be the sum of the volume traded by the _from in last n days, where n is always less than or equal to the rollingPeriod. (given n will always be calculated after the startTime of the individual restriction ).

Second Case :-

  • If _from address has individual daily restriction & transaction time is between startTime and endTime of the restriction, then the transaction will go through only when:

    • _amount is less than or equal to the _allowedAmount - txSumOfDay, where _allowedAmount will be calculated the same as above but according to the individual daily volume restriction. While txSumOfDay is the amount traded till now within the 24 hrs span* . This amount is stored according to at the start time of 24 hrs i.e dailyLastTradedDayTime.

Third Case :-

  • If _from address has (individual daily restriction + individual restriction) & transaction time is between startTime and endTime of the restriction, then transaction will go through only when:

    • _amount is less than or equal to the _allowedAmount - sumOfLastPeriod (First case)

    • _amount is less than or equal to the _allowedAmount - txSumOfDay (Second Case)

      Both above condition should be true otherwise tx gets failed. #807.

Fourth Case :-

  • It works similar to any one of the first/second/third case. It depends upon which default restriction is active. The only difference between the above three and this is to use different storage variables to store the data.

Add Volume restriction

For Individual restriction for a given holder address.

Require checks

  • individualRestriction[_holder].endTime < now it means if holder has no restriction or holder already has restriction but it is expired then it will allow the add new restriction otherwise tx get reverted.

  • _holder should not be a present in the exemption list.

  • _restrictionType could be 0 or 1. No other value allowed.

  • _rollingPeriodInDays always between [1, 365].

  • _restrictionType == 0 then _allowedTokens always greater then zero otherwise _allowedTokens should be non zero and value lies between (0, 100 _10*_16 ].

  • Difference of days between _startTime and _endTime should be >= _rollingPeriodInDays

  • If _startTime is 0 then it will takes current block timestamp + 1 as the startTime.

For multiple _holders (Similar require checks are applied to it):

For Individual daily restriction:

Note: In Individual daily restriction _rollingPeriodInDays always hardcoded to 1.

Individual daily restriction for multiple holders:

For Global restriction(same require check applies):

For default daily restriction:

Note: Same require checks only one change that _rollingPeriodInDays values are by default 1.

Modify Volume Restrictions

For modifying the individual restriction **

Note: It follows the same require checks as addxxx.. functions have.

For modifying multiple individual restrictions:

For modifying the individual daily restriction

Modifying the Multiple daily restrictions

Modifying default restriction:

Modifying daily default restriction

Remove Volume Restriction

Similar to addition or modification contract has the removeXX() or removeXXMulti() functions that will use to remove the individual, individual daily default, default daily restriction.

Getters

Provide the Individual bucket details for a given address.

Provide the Default bucket details for a given address.

Use to get the volume of token that is traded at a particular day for a given address

Use to get the balance of the token holder for the given partition

  • getExemptAddress() use to return the list of exempted addresses.

  • getIndividualRestriction(address _investor) use to return the individual restriction details for a given user.

  • getIndividualDailyRestriction(address _investor) use to return the individual daily restriction details for a given user.

  • getDefaultRestriction() use to return the default restriction details.

  • getDefaultDailyRestriction() use to return the default daily restriction details.

  • getRestrictionData() Provide the restriction details of all the restricted addresses

Other functions

Below function will be used to add/remove the wallet address from the exemption list.

Special considerations / notes

** modification only allowed when the former restriction startTime should be greater than the current timestamp.

  • 24hrs span will not be always morning to midnight of a day. It can be the afternoon of the day to the afternoon of the other day. It always depends on the start time of the Daily restriction.

Below internal function is used to mitigate the subtle edge case where user restriction type changes from default to an individual or vice versa.

Know Issues/bugs

All batch functions will hit the block gas limits if the array length is greater than 80(approx.).

Last updated

Was this helpful?