Skip to content

Commit

Permalink
add latest version of token distro
Browse files Browse the repository at this point in the history
  • Loading branch information
divine-comedian committed Feb 20, 2024
1 parent 5e9c632 commit 9b41730
Showing 1 changed file with 165 additions and 50 deletions.
215 changes: 165 additions & 50 deletions contracts/TokenDistro.sol
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.6;
pragma solidity =0.8.6;

import '@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol';
import '@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol';
import '@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol';
import '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
import './interfaces/IDistro.sol';
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "./interfaces/IDistro.sol";

/**
* Contract responsible for managing the release of tokens over time.
* The distributor is in charge of releasing the corresponding amounts to its recipients.
* This distributor is expected to be another smart contract, such as a merkledrop or the liquidity mining smart contract
*/
contract TokenDistro is Initializable, IDistro, AccessControlEnumerableUpgradeable {
contract TokenDistro is
Initializable,
IDistro,
AccessControlEnumerableUpgradeable
{
using SafeERC20Upgradeable for IERC20Upgradeable;

// bytes32 public constant DISTRIBUTOR_ROLE = keccak256("DISTRIBUTOR_ROLE");
bytes32 public constant DISTRIBUTOR_ROLE = 0xfbd454f36a7e1a388bd6fc3ab10d434aa4578f811acbbcf33afb1c697486313c;
bytes32 public constant DISTRIBUTOR_ROLE =
0xfbd454f36a7e1a388bd6fc3ab10d434aa4578f811acbbcf33afb1c697486313c;

// Structure to of the accounting for each account
struct accountStatus {
Expand All @@ -41,15 +46,26 @@ contract TokenDistro is Initializable, IDistro, AccessControlEnumerableUpgradeab
*/
event GivBackPaid(address distributor);

/**
* @dev Emitted when the DISTRIBUTOR allocate an amount of praise rewards to a recipient
*/
event PraiseRewardPaid(address distributor);

/**
* @dev Emitted when the duration is changed
*/
event DurationChanged(uint256 newDuration);

modifier onlyDistributor() {
require(hasRole(DISTRIBUTOR_ROLE, msg.sender), 'TokenDistro::onlyDistributor: ONLY_DISTRIBUTOR_ROLE');
require(
hasRole(DISTRIBUTOR_ROLE, msg.sender),
"TokenDistro::onlyDistributor: ONLY_DISTRIBUTOR_ROLE"
);

require(balances[msg.sender].claimed == 0, 'TokenDistro::onlyDistributor: DISTRIBUTOR_CANNOT_CLAIM');
require(
balances[msg.sender].claimed == 0,
"TokenDistro::onlyDistributor: DISTRIBUTOR_CANNOT_CLAIM"
);
_;
}

Expand All @@ -74,8 +90,14 @@ contract TokenDistro is Initializable, IDistro, AccessControlEnumerableUpgradeab
IERC20Upgradeable _token,
bool _cancelable
) public initializer {
require(_duration >= _cliffPeriod, 'TokenDistro::constructor: DURATION_LESS_THAN_CLIFF');
require(_initialPercentage <= 10000, 'TokenDistro::constructor: INITIALPERCENTAGE_GREATER_THAN_100');
require(
_duration >= _cliffPeriod,
"TokenDistro::constructor: DURATION_LESS_THAN_CLIFF"
);
require(
_initialPercentage <= 10000,
"TokenDistro::constructor: INITIALPERCENTAGE_GREATER_THAN_100"
);
__AccessControlEnumerable_init();
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);

Expand All @@ -100,10 +122,13 @@ contract TokenDistro is Initializable, IDistro, AccessControlEnumerableUpgradeab
*
*/
function setStartTime(uint256 newStartTime) external override {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), 'TokenDistro::setStartTime: ONLY_ADMIN_ROLE');
require(
hasRole(DEFAULT_ADMIN_ROLE, msg.sender),
"TokenDistro::setStartTime: ONLY_ADMIN_ROLE"
);
require(
startTime > getTimestamp() && newStartTime > getTimestamp(),
'TokenDistro::setStartTime: IF_HAS_NOT_STARTED_YET'
"TokenDistro::setStartTime: IF_HAS_NOT_STARTED_YET"
);

uint256 _cliffPeriod = cliffTime - startTime;
Expand All @@ -123,11 +148,21 @@ contract TokenDistro is Initializable, IDistro, AccessControlEnumerableUpgradeab
*
*/
function assign(address distributor, uint256 amount) external override {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), 'TokenDistro::assign: ONLY_ADMIN_ROLE');
require(hasRole(DISTRIBUTOR_ROLE, distributor), 'TokenDistro::assign: ONLY_TO_DISTRIBUTOR_ROLE');
require(
hasRole(DEFAULT_ADMIN_ROLE, msg.sender),
"TokenDistro::assign: ONLY_ADMIN_ROLE"
);
require(
hasRole(DISTRIBUTOR_ROLE, distributor),
"TokenDistro::assign: ONLY_TO_DISTRIBUTOR_ROLE"
);

balances[address(this)].allocatedTokens = balances[address(this)].allocatedTokens - amount;
balances[distributor].allocatedTokens = balances[distributor].allocatedTokens + amount;
balances[address(this)].allocatedTokens =
balances[address(this)].allocatedTokens -
amount;
balances[distributor].allocatedTokens =
balances[distributor].allocatedTokens +
amount;

emit Assign(msg.sender, distributor, amount);
}
Expand Down Expand Up @@ -164,12 +199,23 @@ contract TokenDistro is Initializable, IDistro, AccessControlEnumerableUpgradeab
* Emits a {Allocate} event.
*
*/
function _allocate(address recipient, uint256 amount, bool claim) internal {
require(!hasRole(DISTRIBUTOR_ROLE, recipient), 'TokenDistro::allocate: DISTRIBUTOR_NOT_VALID_RECIPIENT');
function _allocate(
address recipient,
uint256 amount,
bool claim
) internal {
require(
!hasRole(DISTRIBUTOR_ROLE, recipient),
"TokenDistro::allocate: DISTRIBUTOR_NOT_VALID_RECIPIENT"
);

balances[msg.sender].allocatedTokens = balances[msg.sender].allocatedTokens - amount;
balances[msg.sender].allocatedTokens =
balances[msg.sender].allocatedTokens -
amount;

balances[recipient].allocatedTokens = balances[recipient].allocatedTokens + amount;
balances[recipient].allocatedTokens =
balances[recipient].allocatedTokens +
amount;

if (claim && claimableNow(recipient) > 0) {
_claim(recipient);
Expand All @@ -178,7 +224,11 @@ contract TokenDistro is Initializable, IDistro, AccessControlEnumerableUpgradeab
emit Allocate(msg.sender, recipient, amount);
}

function allocate(address recipient, uint256 amount, bool claim) external override onlyDistributor {
function allocate(
address recipient,
uint256 amount,
bool claim
) external override onlyDistributor {
_allocate(recipient, amount, claim);
}

Expand All @@ -190,23 +240,43 @@ contract TokenDistro is Initializable, IDistro, AccessControlEnumerableUpgradeab
*
* Unlike allocate method it doesn't claim recipients available balance
*/
function _allocateMany(address[] memory recipients, uint256[] memory amounts) internal onlyDistributor {
require(recipients.length == amounts.length, 'TokenDistro::allocateMany: INPUT_LENGTH_NOT_MATCH');
function _allocateMany(
address[] memory recipients,
uint256[] memory amounts
) internal onlyDistributor {
require(
recipients.length == amounts.length,
"TokenDistro::allocateMany: INPUT_LENGTH_NOT_MATCH"
);

for (uint256 i = 0; i < recipients.length; i++) {
_allocate(recipients[i], amounts[i], false);
}
}

function allocateMany(address[] memory recipients, uint256[] memory amounts) external override {
function allocateMany(address[] memory recipients, uint256[] memory amounts)
external
override
{
_allocateMany(recipients, amounts);
}

function sendGIVbacks(address[] memory recipients, uint256[] memory amounts) external override {
function sendGIVbacks(address[] memory recipients, uint256[] memory amounts)
external
override
{
_allocateMany(recipients, amounts);
emit GivBackPaid(msg.sender);
}

function sendPraiseRewards(
address[] memory recipients,
uint256[] memory amounts
) external override {
_allocateMany(recipients, amounts);
emit PraiseRewardPaid(msg.sender);
}

/**
* Function that allows a recipient to change its address
* @dev The change can only be made to an address that has not previously received an allocation &
Expand All @@ -217,16 +287,19 @@ contract TokenDistro is Initializable, IDistro, AccessControlEnumerableUpgradeab
*/
function changeAddress(address newAddress) external override {
require(
balances[newAddress].allocatedTokens == 0 && balances[newAddress].claimed == 0,
'TokenDistro::changeAddress: ADDRESS_ALREADY_IN_USE'
balances[newAddress].allocatedTokens == 0 &&
balances[newAddress].claimed == 0,
"TokenDistro::changeAddress: ADDRESS_ALREADY_IN_USE"
);

require(
!hasRole(DISTRIBUTOR_ROLE, msg.sender) && !hasRole(DISTRIBUTOR_ROLE, newAddress),
'TokenDistro::changeAddress: DISTRIBUTOR_ROLE_NOT_A_VALID_ADDRESS'
!hasRole(DISTRIBUTOR_ROLE, msg.sender) &&
!hasRole(DISTRIBUTOR_ROLE, newAddress),
"TokenDistro::changeAddress: DISTRIBUTOR_ROLE_NOT_A_VALID_ADDRESS"
);

balances[newAddress].allocatedTokens = balances[msg.sender].allocatedTokens;
balances[newAddress].allocatedTokens = balances[msg.sender]
.allocatedTokens;
balances[msg.sender].allocatedTokens = 0;

balances[newAddress].claimed = balances[msg.sender].claimed;
Expand All @@ -247,7 +320,12 @@ contract TokenDistro is Initializable, IDistro, AccessControlEnumerableUpgradeab
* @param timestamp Unix time to check the number of tokens claimable
* @return Number of tokens claimable at that timestamp
*/
function globallyClaimableAt(uint256 timestamp) public view override returns (uint256) {
function globallyClaimableAt(uint256 timestamp)
public
view
override
returns (uint256)
{
if (timestamp < startTime) return 0;
if (timestamp < cliffTime) return initialAmount;
if (timestamp > startTime + duration) return totalTokens;
Expand All @@ -261,10 +339,22 @@ contract TokenDistro is Initializable, IDistro, AccessControlEnumerableUpgradeab
* @param recipient account to query
* @param timestamp Instant of time in which the calculation is made
*/
function claimableAt(address recipient, uint256 timestamp) public view override returns (uint256) {
require(!hasRole(DISTRIBUTOR_ROLE, recipient), 'TokenDistro::claimableAt: DISTRIBUTOR_ROLE_CANNOT_CLAIM');
require(timestamp >= getTimestamp(), 'TokenDistro::claimableAt: NOT_VALID_PAST_TIMESTAMP');
uint256 unlockedAmount = (globallyClaimableAt(timestamp) * balances[recipient].allocatedTokens) / totalTokens;
function claimableAt(address recipient, uint256 timestamp)
public
view
override
returns (uint256)
{
require(
!hasRole(DISTRIBUTOR_ROLE, recipient),
"TokenDistro::claimableAt: DISTRIBUTOR_ROLE_CANNOT_CLAIM"
);
require(
timestamp >= getTimestamp(),
"TokenDistro::claimableAt: NOT_VALID_PAST_TIMESTAMP"
);
uint256 unlockedAmount = (globallyClaimableAt(timestamp) *
balances[recipient].allocatedTokens) / totalTokens;

return unlockedAmount - balances[recipient].claimed;
}
Expand All @@ -273,7 +363,12 @@ contract TokenDistro is Initializable, IDistro, AccessControlEnumerableUpgradeab
* Function to get the unlocked tokens for a specific address. It uses the current timestamp
* @param recipient account to query
*/
function claimableNow(address recipient) public view override returns (uint256) {
function claimableNow(address recipient)
public
view
override
returns (uint256)
{
return claimableAt(recipient, getTimestamp());
}

Expand All @@ -285,22 +380,31 @@ contract TokenDistro is Initializable, IDistro, AccessControlEnumerableUpgradeab
* Emits a {ChangeAddress} event.
*
*/
function cancelAllocation(address prevRecipient, address newRecipient) external override {
require(cancelable, 'TokenDistro::cancelAllocation: NOT_CANCELABLE');
function cancelAllocation(address prevRecipient, address newRecipient)
external
override
{
require(cancelable, "TokenDistro::cancelAllocation: NOT_CANCELABLE");

require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), 'TokenDistro::cancelAllocation: ONLY_ADMIN_ROLE');
require(
hasRole(DEFAULT_ADMIN_ROLE, msg.sender),
"TokenDistro::cancelAllocation: ONLY_ADMIN_ROLE"
);

require(
balances[newRecipient].allocatedTokens == 0 && balances[newRecipient].claimed == 0,
'TokenDistro::cancelAllocation: ADDRESS_ALREADY_IN_USE'
balances[newRecipient].allocatedTokens == 0 &&
balances[newRecipient].claimed == 0,
"TokenDistro::cancelAllocation: ADDRESS_ALREADY_IN_USE"
);

require(
!hasRole(DISTRIBUTOR_ROLE, prevRecipient) && !hasRole(DISTRIBUTOR_ROLE, newRecipient),
'TokenDistro::cancelAllocation: DISTRIBUTOR_ROLE_NOT_A_VALID_ADDRESS'
!hasRole(DISTRIBUTOR_ROLE, prevRecipient) &&
!hasRole(DISTRIBUTOR_ROLE, newRecipient),
"TokenDistro::cancelAllocation: DISTRIBUTOR_ROLE_NOT_A_VALID_ADDRESS"
);

balances[newRecipient].allocatedTokens = balances[prevRecipient].allocatedTokens;
balances[newRecipient].allocatedTokens = balances[prevRecipient]
.allocatedTokens;
balances[prevRecipient].allocatedTokens = 0;

balances[newRecipient].claimed = balances[prevRecipient].claimed;
Expand All @@ -318,9 +422,14 @@ contract TokenDistro is Initializable, IDistro, AccessControlEnumerableUpgradeab
function _claim(address recipient) private {
uint256 remainingToClaim = claimableNow(recipient);

require(remainingToClaim > 0, 'TokenDistro::claim: NOT_ENOUGH_TOKENS_TO_CLAIM');
require(
remainingToClaim > 0,
"TokenDistro::claim: NOT_ENOUGH_TOKENS_TO_CLAIM"
);

balances[recipient].claimed = balances[recipient].claimed + remainingToClaim;
balances[recipient].claimed =
balances[recipient].claimed +
remainingToClaim;

token.safeTransfer(recipient, remainingToClaim);

Expand All @@ -331,9 +440,15 @@ contract TokenDistro is Initializable, IDistro, AccessControlEnumerableUpgradeab
* Function to change the duration
*/
function setDuration(uint256 newDuration) public {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), 'TokenDistro::setDuration: ONLY_ADMIN_ROLE');
require(
hasRole(DEFAULT_ADMIN_ROLE, msg.sender),
"TokenDistro::setDuration: ONLY_ADMIN_ROLE"
);

require(startTime > getTimestamp(), 'TokenDistro::setDuration: IF_HAS_NOT_STARTED_YET');
require(
startTime > getTimestamp(),
"TokenDistro::setDuration: IF_HAS_NOT_STARTED_YET"
);

duration = newDuration;

Expand Down

0 comments on commit 9b41730

Please sign in to comment.