Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
300 changes: 157 additions & 143 deletions contracts/modules/token/TokenController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ contract TokenController is ITokenController, LockHandler, MasterAwareV2 {
address public immutable claimsReward;
address public immutable stakingPoolFactory;

/* ========== CONSTRUCTOR ========== */

constructor(
address quotationDataAddress,
address claimsRewardAddress,
Expand All @@ -44,52 +46,46 @@ contract TokenController is ITokenController, LockHandler, MasterAwareV2 {
stakingPoolFactory = stakingPoolFactoryAddress;
}

// TODO: probably better to move this to master
function unlistClaimsReward() external {
token().removeFromWhiteList(claimsReward);
}
/* ========== TOKEN RELATED VIEW FUNCTIONS ========== */

/* ========== DEPENDENCIES ========== */

function token() public view returns (INXMToken) {
return INXMToken(internalContracts[uint(ID.TK)]);
function totalSupply() public override view returns (uint256) {
return token().totalSupply();
}

function pooledStaking() internal view returns (IPooledStaking) {
return IPooledStaking(internalContracts[uint(ID.PS)]);
}
/// Returns the base voting power not the balance. It is used in governance voting as well as in
/// snapshot voting.
///
/// @dev Caution, this function is improperly named because reconfiguring snapshot voting was
/// not desired. It accounts for the tokens in the user's wallet as well as tokens locked in
/// assessment and legacy staking deposits. V2 staking deposits are excluded because they are
/// delegated to the pool managers instead.
/// TODO: add stake pool balance for pool operators
///
/// @param _of The member address for which the base voting power is calculated.
function totalBalanceOf(address _of) public override view returns (uint256 amount) {

function assessment() internal view returns (IAssessment) {
return IAssessment(internalContracts[uint(ID.AS)]);
}
amount = token().balanceOf(_of);

function cover() internal view returns (ICover) {
return ICover(internalContracts[uint(ID.CO)]);
}
// This loop can be removed once all cover notes are withdrawn
for (uint256 i = 0; i < lockReason[_of].length; i++) {
amount = amount + _tokensLocked(_of, lockReason[_of][i]);
}

function governance() internal view returns (IGovernance) {
return IGovernance(internalContracts[uint(ID.GV)]);
}
// [todo] Can be removed after PooledStaking is decommissioned
uint stakerReward = pooledStaking().stakerReward(_of);
uint stakerDeposit = pooledStaking().stakerDeposit(_of);

/**
* @dev Just for interface
*/
function changeDependentContractAddress() public override {
internalContracts[uint(ID.TK)] = payable(master.tokenAddress());
internalContracts[uint(ID.PS)] = master.getLatestAddress("PS");
internalContracts[uint(ID.AS)] = master.getLatestAddress("AS");
internalContracts[uint(ID.CO)] = master.getLatestAddress("CO");
internalContracts[uint(ID.GV)] = master.getLatestAddress("GV");
}
(
uint assessmentStake,
/*uint104 rewardsWithdrawableFromIndex*/,
/*uint16 fraudCount*/
) = assessment().stakeOf(_of);

/**
* @dev to change the operator address
* @param _newOperator is the new address of operator
*/
function changeOperator(address _newOperator) public override onlyGovernance {
token().changeOperator(_newOperator);
amount += stakerDeposit + stakerReward + assessmentStake;
}

/* ========== INTERNAL OPERATOR RELATED MUTATIVE FUNCTIONS ========== */

/**
* @dev Proxies token transfer through this contract to allow staking when members are locked for voting
* @param _from Source address
Expand All @@ -108,29 +104,11 @@ contract TokenController is ITokenController, LockHandler, MasterAwareV2 {
}

/**
* @dev burns tokens of an address
* @param _of is the address to burn tokens of
* @param amount is the amount to burn
* @return the boolean status of the burning process
* @dev Lock the user's tokens
* @param _of user's address.
*/
function burnFrom(address _of, uint amount) public override onlyInternal returns (bool) {
return token().burnFrom(_of, amount);
}

/**
* @dev Adds an address to whitelist maintained in the contract
* @param _member address to add to whitelist
*/
function addToWhitelist(address _member) public virtual override onlyInternal {
token().addToWhiteList(_member);
}

/**
* @dev Removes an address from the whitelist in the token
* @param _member address to remove
*/
function removeFromWhitelist(address _member) public override onlyInternal {
token().removeFromWhiteList(_member);
function lockForMemberVote(address _of, uint _days) public override onlyInternal {
token().lockForMemberVote(_of, _days);
}

/**
Expand All @@ -143,83 +121,77 @@ contract TokenController is ITokenController, LockHandler, MasterAwareV2 {
}

/**
* @dev Lock the user's tokens
* @param _of user's address.
* @dev burns tokens of an address
* @param _of is the address to burn tokens of
* @param amount is the amount to burn
* @return the boolean status of the burning process
*/
function lockForMemberVote(address _of, uint _days) public override onlyInternal {
token().lockForMemberVote(_of, _days);
function burnFrom(address _of, uint amount) public override onlyInternal returns (bool) {
return token().burnFrom(_of, amount);
}

/**
* @dev Unlocks the withdrawable tokens against CLA of a specified addresses
* @param users Addresses of users for whom the tokens are unlocked
*/
function withdrawClaimAssessmentTokens(address[] calldata users) external {
for (uint256 i = 0; i < users.length; i++) {
if (locked[users[i]]["CLA"].claimed) {
continue;
}
uint256 amount = locked[users[i]]["CLA"].amount;
if (amount > 0) {
locked[users[i]]["CLA"].claimed = true;
emit Unlocked(users[i], "CLA", amount);
token().transfer(users[i], amount);
}
}
function _stakingPool(uint poolId) internal view returns (address) {
return StakingPoolLibrary.getAddress(stakingPoolFactory, poolId);
}

/**
* @dev Updates Uint Parameters of a code
* @param code whose details we want to update
* @param value value to set
*/
function updateUintParameters(bytes8 code, uint value) external view onlyGovernance {
// silence compiler warnings
code;
value;
revert("TokenController: invalid param code");
function mintStakingPoolNXMRewards(uint amount, uint poolId) external {
require(msg.sender == _stakingPool(poolId), "TokenController: msg.sender not staking pool");
token().mint(address(this), amount);
stakingPoolNXMBalances[poolId].rewards += amount.toUint128();
}

function getLockReasons(address _of) external override view returns (bytes32[] memory reasons) {
return lockReason[_of];
function burnStakingPoolNXMRewards(uint amount, uint poolId) external {
require(msg.sender == _stakingPool(poolId), "TokenController: msg.sender not staking pool");
stakingPoolNXMBalances[poolId].rewards -= amount.toUint128();
token().burn(amount);
}

function totalSupply() public override view returns (uint256) {
return token().totalSupply();
function depositStakedNXM(address from, uint amount, uint poolId) external {
require(msg.sender == _stakingPool(poolId), "TokenController: msg.sender not staking pool");
stakingPoolNXMBalances[poolId].deposits += amount.toUint128();
token().operatorTransfer(from, amount);
}

/// Returns the base voting power not the balance. It is used in governance voting as well as in
/// snapshot voting.
///
/// @dev Caution, this function is improperly named because reconfiguring snapshot voting was
/// not desired. It accounts for the tokens in the user's wallet as well as tokens locked in
/// assessment and legacy staking deposits. V2 staking deposits are excluded because they are
/// delegated to the pool managers instead.
/// TODO: add stake pool balance for pool operators
///
/// @param _of The member address for which the base voting power is calculated.
function totalBalanceOf(address _of) public override view returns (uint256 amount) {
function withdrawNXMStakeAndRewards(address to, uint stakeToWithdraw, uint rewardsToWithdraw, uint poolId) external {
require(msg.sender == _stakingPool(poolId), "TokenController: msg.sender not staking pool");
StakingPoolNXMBalances memory poolBalances = stakingPoolNXMBalances[poolId];
poolBalances.deposits -= stakeToWithdraw.toUint128();
poolBalances.rewards -= rewardsToWithdraw.toUint128();
stakingPoolNXMBalances[poolId] = poolBalances;
token().transfer(to, stakeToWithdraw + rewardsToWithdraw);
}

amount = token().balanceOf(_of);
function burnStakedNXM(uint amount, uint poolId) external {
require(msg.sender == _stakingPool(poolId), "TokenController: msg.sender not staking pool");
stakingPoolNXMBalances[poolId].deposits -= amount.toUint128();
token().burn(amount);
}

// This loop can be removed once all cover notes are withdrawn
for (uint256 i = 0; i < lockReason[_of].length; i++) {
amount = amount + _tokensLocked(_of, lockReason[_of][i]);
}
/* ========== WHITELIST RELATED MUTATIVE FUNCTIONS ========== */

// [todo] Can be removed after PooledStaking is decommissioned
uint stakerReward = pooledStaking().stakerReward(_of);
uint stakerDeposit = pooledStaking().stakerDeposit(_of);
/**
* @dev Adds an address to whitelist maintained in the contract
* @param _member address to add to whitelist
*/
function addToWhitelist(address _member) public virtual override onlyInternal {
token().addToWhiteList(_member);
}

(
uint assessmentStake,
/*uint104 rewardsWithdrawableFromIndex*/,
/*uint16 fraudCount*/
) = assessment().stakeOf(_of);
/**
* @dev Removes an address from the whitelist in the token
* @param _member address to remove
*/
function removeFromWhitelist(address _member) public override onlyInternal {
token().removeFromWhiteList(_member);
}

amount += stakerDeposit + stakerReward + assessmentStake;
// TODO: probably better to move this to master
function unlistClaimsReward() external {
token().removeFromWhiteList(claimsReward);
}

/* ========== USER REWARDS RELATED MUTATIVE FUNCTIONS ========== */

/// Withdraws governance rewards for the given member address
/// @dev This function requires a batchSize that fits in one block. It cannot be 0.
function withdrawGovernanceRewards(
Expand Down Expand Up @@ -289,8 +261,14 @@ contract TokenController is ITokenController, LockHandler, MasterAwareV2 {
}
}

/* ========== V1 RELATED VIEW FUNCTIONS ========== */

function getLockReasons(address _of) external override view returns (bytes32[] memory reasons) {
return lockReason[_of];
}

/**
* @dev Returns tokens locked for a specified address for a
* @dev Returns tokens locked for a specified address for a
* specified reason
*
* @param _of The address whose tokens are locked
Expand Down Expand Up @@ -341,6 +319,8 @@ contract TokenController is ITokenController, LockHandler, MasterAwareV2 {
}
}

/* ========== V1 RELATED MUTATIVE FUNCTIONS ========== */

// Can be removed once all cover notes are withdrawn
function withdrawCoverNote(
address user,
Expand Down Expand Up @@ -383,40 +363,74 @@ contract TokenController is ITokenController, LockHandler, MasterAwareV2 {
token().transfer(user, totalAmount);
}

function _stakingPool(uint poolId) internal view returns (address) {
return StakingPoolLibrary.getAddress(stakingPoolFactory, poolId);
/**
* @dev Unlocks the withdrawable tokens against CLA of a specified addresses
* @param users Addresses of users for whom the tokens are unlocked
*/
function withdrawClaimAssessmentTokens(address[] calldata users) external {
for (uint256 i = 0; i < users.length; i++) {
if (locked[users[i]]["CLA"].claimed) {
continue;
}
uint256 amount = locked[users[i]]["CLA"].amount;
if (amount > 0) {
locked[users[i]]["CLA"].claimed = true;
emit Unlocked(users[i], "CLA", amount);
token().transfer(users[i], amount);
}
}
}

function mintStakingPoolNXMRewards(uint amount, uint poolId) external {
require(msg.sender == _stakingPool(poolId), "TokenController: msg.sender not staking pool");
token().mint(address(this), amount);
stakingPoolNXMBalances[poolId].rewards += amount.toUint128();
/* ========== DEPENDENCIES ========== */

function token() public view returns (INXMToken) {
return INXMToken(internalContracts[uint(ID.TK)]);
}

function burnStakingPoolNXMRewards(uint amount, uint poolId) external {
require(msg.sender == _stakingPool(poolId), "TokenController: msg.sender not staking pool");
stakingPoolNXMBalances[poolId].rewards -= amount.toUint128();
token().burn(amount);
function pooledStaking() internal view returns (IPooledStaking) {
return IPooledStaking(internalContracts[uint(ID.PS)]);
}

function depositStakedNXM(address from, uint amount, uint poolId) external {
require(msg.sender == _stakingPool(poolId), "TokenController: msg.sender not staking pool");
stakingPoolNXMBalances[poolId].deposits += amount.toUint128();
token().operatorTransfer(from, amount);
function assessment() internal view returns (IAssessment) {
return IAssessment(internalContracts[uint(ID.AS)]);
}

function withdrawNXMStakeAndRewards(address to, uint stakeToWithdraw, uint rewardsToWithdraw, uint poolId) external {
require(msg.sender == _stakingPool(poolId), "TokenController: msg.sender not staking pool");
StakingPoolNXMBalances memory poolBalances = stakingPoolNXMBalances[poolId];
poolBalances.deposits -= stakeToWithdraw.toUint128();
poolBalances.rewards -= rewardsToWithdraw.toUint128();
stakingPoolNXMBalances[poolId] = poolBalances;
token().transfer(to, stakeToWithdraw + rewardsToWithdraw);
function cover() internal view returns (ICover) {
return ICover(internalContracts[uint(ID.CO)]);
}

function burnStakedNXM(uint amount, uint poolId) external {
require(msg.sender == _stakingPool(poolId), "TokenController: msg.sender not staking pool");
stakingPoolNXMBalances[poolId].deposits -= amount.toUint128();
token().burn(amount);
function governance() internal view returns (IGovernance) {
return IGovernance(internalContracts[uint(ID.GV)]);
}

/**
* @dev Just for interface
*/
function changeDependentContractAddress() public override {
internalContracts[uint(ID.TK)] = payable(master.tokenAddress());
internalContracts[uint(ID.PS)] = master.getLatestAddress("PS");
internalContracts[uint(ID.AS)] = master.getLatestAddress("AS");
internalContracts[uint(ID.CO)] = master.getLatestAddress("CO");
internalContracts[uint(ID.GV)] = master.getLatestAddress("GV");
}

/**
* @dev Updates Uint Parameters of a code
* @param code whose details we want to update
* @param value value to set
*/
function updateUintParameters(bytes8 code, uint value) external view onlyGovernance {
// silence compiler warnings
code;
value;
revert("TokenController: invalid param code");
}

/**
* @dev to change the operator address
* @param _newOperator is the new address of operator
*/
function changeOperator(address _newOperator) public override onlyGovernance {
token().changeOperator(_newOperator);
}
}