Skip to content

Commit

Permalink
Merge pull request JoinColony#253 from filiplazovic/feature/token-loc…
Browse files Browse the repository at this point in the history
…king

Token Locking
  • Loading branch information
elenadimitrova authored Jul 6, 2018
2 parents 9764ca8 + fbe1732 commit 9f966ff
Show file tree
Hide file tree
Showing 16 changed files with 852 additions and 127 deletions.
43 changes: 13 additions & 30 deletions contracts/ColonyFunding.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import "../lib/dappsys/math.sol";
import "./ERC20Extended.sol";
import "./IColonyNetwork.sol";
import "./ColonyStorage.sol";
import "./ITokenLocking.sol";


contract ColonyFunding is ColonyStorage, DSMath {
Expand Down Expand Up @@ -166,44 +167,38 @@ contract ColonyFunding is ColonyStorage, DSMath {
return nonRewardPotsTotal[_token];
}

function getGlobalRewardPayoutCount() public view returns (uint256) {
return globalRewardPayoutCount;
}

function getUserRewardPayoutCount(address _user) public view returns (uint256) {
return userRewardPayoutCount[_user];
}

function startNextRewardPayout(address _token) public auth {
ITokenLocking tokenLocking = ITokenLocking(IColonyNetwork(colonyNetworkAddress).getTokenLocking());
uint256 totalLockCount = tokenLocking.lockToken(address(token));

require(!activeRewardPayouts[_token], "colony-reward-payout-token-active");

uint256 totalTokens = sub(token.totalSupply(), token.balanceOf(address(this)));
require(totalTokens > 0, "colony-reward-payout-invalid-total-tokens");

activeRewardPayouts[_token] = true;
globalRewardPayoutCount += 1;

//TODO: Lock everyones tokens

rewardPayoutCycles[globalRewardPayoutCount] = RewardPayoutCycle(
rewardPayoutCycles[totalLockCount] = RewardPayoutCycle(
IColonyNetwork(colonyNetworkAddress).getReputationRootHash(),
totalTokens,
pots[0].balance[_token],
_token,
block.timestamp
);

emit RewardPayoutCycleStarted(globalRewardPayoutCount);
emit RewardPayoutCycleStarted(totalLockCount);
}

function claimRewardPayout(uint256 _payoutId, uint256[7] _squareRoots, uint256 _userReputation, uint256 _totalReputation) public {
RewardPayoutCycle memory payout = rewardPayoutCycles[_payoutId];
// Checking if payout is active
require(block.timestamp - payout.blockTimestamp <= 60 days, "colony-reward-payout-not-active");
require(_payoutId - userRewardPayoutCount[msg.sender] == 1, "colony-reward-payout-bad-id");

//TODO: Prove that userReputation and totalReputation in reputationState are correct
//TODO: Prove that userReputation and totalReputation in reputationState (reputation root hash at the start of the payout) are correct

uint256 userTokens = token.balanceOf(msg.sender);
ITokenLocking tokenLocking = ITokenLocking(IColonyNetwork(colonyNetworkAddress).getTokenLocking());
uint256 userTokens;
(, userTokens) = tokenLocking.getUserLock(address(token), msg.sender);

require(_totalReputation > 0, "colony-reward-payout-invalid-total-reputation");
require(userTokens > 0, "colony-reward-payout-invalid-user-tokens");
Expand All @@ -230,26 +225,14 @@ contract ColonyFunding is ColonyStorage, DSMath {

uint256 reward = (mul(_squareRoots[4], _squareRoots[6]) / (_squareRoots[5] + 1)) ** 2;

pots[0].balance[payout.tokenAddress] = sub(pots[0].balance[payout.tokenAddress], reward);

userRewardPayoutCount[msg.sender] += 1;
tokenLocking.unlockTokenForUser(address(token), msg.sender, _payoutId);

// TODO: Unlock user tokens
pots[0].balance[payout.tokenAddress] = sub(pots[0].balance[payout.tokenAddress], reward);

ERC20Extended(payout.tokenAddress).transfer(msg.sender, reward);
}

function waiveRewardPayouts(uint256 _numPayouts) public {
require(add(userRewardPayoutCount[msg.sender], _numPayouts) <= globalRewardPayoutCount, "colony-reward-payout-invalid-num-payouts");

userRewardPayoutCount[msg.sender] += _numPayouts;

//TODO unlock user tokens
}

function finalizeRewardPayout(uint256 _payoutId) public {
require(_payoutId <= globalRewardPayoutCount, "colony-reward-payout-not-found");

RewardPayoutCycle memory payout = rewardPayoutCycles[_payoutId];

require(activeRewardPayouts[payout.tokenAddress], "colony-reward-payout-token-not-active");
Expand Down
16 changes: 16 additions & 0 deletions contracts/ColonyNetwork.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ contract ColonyNetwork is ColonyNetworkStorage {
_;
}

function isColony(address _colony) public view returns (bool) {
return _isColony[_colony];
}

function getCurrentColonyVersion() public view returns (uint256) {
return currentColonyVersion;
}
Expand Down Expand Up @@ -97,6 +101,18 @@ contract ColonyNetwork is ColonyNetworkStorage {
return reputationRootHashNNodes;
}

function setTokenLocking(address _tokenLocking) public
auth
{
// Token locking address can't be changed
require(tokenLocking == 0x0);
tokenLocking = _tokenLocking;
}

function getTokenLocking() public view returns (address) {
return tokenLocking;
}

function createMetaColony(address _tokenAddress) public
auth
{
Expand Down
2 changes: 2 additions & 0 deletions contracts/ColonyNetworkStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ contract ColonyNetworkStorage is DSAuth {
uint256 currentColonyVersion;
// Address of the Meta Colony
address metaColony;
// Address of token locking contract
address tokenLocking;
// Maps index to colony address
mapping (uint256 => address) colonies;
mapping (address => bool) _isColony;
Expand Down
7 changes: 0 additions & 7 deletions contracts/ColonyStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,6 @@ contract ColonyStorage is DSAuth {
mapping (uint256 => RewardPayoutCycle) rewardPayoutCycles;
// Active payouts for particular token address. Assures that one token is used for only one active payout
mapping (address => bool) activeRewardPayouts;
// Incremented every time a reward payout is created. Used for comparing with `userRewardPayoutCount`
// to ensure that rewards are claimed in the right order.
// Can be used for iterating over all reward payouts (excluding index 0)
uint256 globalRewardPayoutCount;
// Keeps track of how many payouts are claimed by the particular user.
// Can be incremented either by waiving or claiming the reward.
mapping (address => uint256) userRewardPayoutCount;

// This keeps track of how much of the colony's funds that it owns have been moved into pots other than pot 0,
// which (by definition) have also had the reward amount siphoned off and put in to pot 0.
Expand Down
13 changes: 0 additions & 13 deletions contracts/IColony.sol
Original file line number Diff line number Diff line change
Expand Up @@ -444,11 +444,6 @@ contract IColony {
/// @param _totalReputation Total reputation at the point of creation of reward payout cycle
function claimRewardPayout(uint256 _payoutId, uint256[7] _squareRoots, uint256 _userReputation, uint256 _totalReputation) public;

/// @notice Waive reward payouts. This will unlock the sender's tokens and increment users reward payout counter,
/// allowing them to claim next reward payout
/// @param _numPayouts Number of payouts you want to waive
function waiveRewardPayouts(uint256 _numPayouts) public;

/// @notice Get useful information about specific reward payout
/// @param _payoutId Id of the reward payout
/// @return Reputation root hash at the time of creation
Expand All @@ -464,14 +459,6 @@ contract IColony {
/// @param _payoutId Id of the reward payout
function finalizeRewardPayout(uint256 _payoutId) public;

/// @notice Get number of reward payout cycles
/// @return Number of reward payout cycles
function getGlobalRewardPayoutCount() public returns (uint256);

/// @notice Get number of claimed and waived reward payouts for `_user`
/// @return Number of claimed and waived reward payouts
function getUserRewardPayoutCount(address _user) public returns (uint256);

/// @notice Get the `_token` balance of pot with id `_potId`
/// @param _potId Id of the funding pot
/// @param _token Address of the token, `0x0` value indicates Ether
Expand Down
14 changes: 14 additions & 0 deletions contracts/IColonyNetwork.sol
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ contract IColonyNetwork {
/// @return The colony count
function getColonyCount() public view returns (uint256);

/// @notice Check if specific address is a colony created on colony network
/// @param _colony Address of the colony
/// @return true if specified address is a colony, otherwise false
function isColony(address _colony) public view returns (bool);

/// @notice Adds a new skill to the global or local skills tree, under skill `_parentSkillId`
/// Only the Meta Colony is allowed to add a global skill, called via `IColony.addGlobalSkill`
/// Any colony is allowed to add a local skill and which is associated with a new domain via `IColony.addDomain`
Expand Down Expand Up @@ -86,6 +91,15 @@ contract IColonyNetwork {
/// @return The root global skill id
function getRootGlobalSkillId() public view returns (uint256);

/// @notice Sets the token locking address
/// This is only set once, and can't be changed afterwards
/// @param _tokenLockingAddress Address of the locking contract
function setTokenLocking(address _tokenLockingAddress) public;

/// @notice Get token locking contract address
/// @return Token locking contract address
function getTokenLocking() public view returns (address);

/// @notice Create the Meta Colony, same as a normal colony plus the root skill
/// @param _tokenAddress Address of the CLNY token
function createMetaColony(address _tokenAddress) public;
Expand Down
52 changes: 52 additions & 0 deletions contracts/ITokenLocking.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
pragma solidity ^0.4.23;


contract ITokenLocking {

/// @notice Set the ColonyNetwork contract address
/// @dev ColonyNetwork is used for checking if sender is a colony created on colony network
/// @param _colonyNetwork Address of the ColonyNetwork
function setColonyNetwork(address _colonyNetwork) public;

/// @notice Get ColonyNetwork address
/// @return ColonyNetwork address
function getColonyNetwork() public view returns (address);

/// @notice Locks everyones' tokens on `_token` address
/// @param _token Address of the token we want to lock
/// @return Updated total token lock count
function lockToken(address _token) public returns (uint256);

/// @notice Increments the lock counter to `_lockId` for the `_user` if user's lock count is less than `_lockId` by 1.
/// Can only be called by a colony
/// @param _token Address of the token we want to unlock
/// @param _user Address of the user
/// @param _lockId Id of the lock we want to increment to
function unlockTokenForUser(address _token, address _user, uint256 _lockId) public;

/// @notice Increments sender's lock count to `_lockId`.
/// @param _token Address of the token we want to increment lock count for
/// @param _lockId Id of the lock user wants to increment to
function incrementLockCounterTo(address _token, uint256 _lockId) public;

/// @notice Deposit `_amount` of colony tokens. Can only be called if user tokens are not locked
/// Before calling this function user has to allow that their tokens can be transferred by token locking contract
/// @param _amount Amount to deposit
function deposit(address _token, uint256 _amount) public;

/// @notice Withdraw `_amount` of deposited tokens. Can only be called if user tokens are not locked
/// @param _amount Amount to withdraw
function withdraw(address _token, uint256 _amount) public;

/// @notice Get global lock count for a specific token
/// @param _token Address of the token
/// @return Global token lock count
function getTotalLockCount(address _token) public view returns (uint256);

/// @notice Get user token lock info (lock count and deposited amount)
/// @param _token Address of the token
/// @param _user Address of the user
/// @return User's token lock count
/// @return User's deposited amount
function getUserLock(address _token, address _user) public view returns (uint256, uint256);
}
74 changes: 74 additions & 0 deletions contracts/TokenLocking.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
pragma solidity ^0.4.23;

import "./ERC20Extended.sol";
import "./IColonyNetwork.sol";
import "./TokenLockingStorage.sol";
import "../lib/dappsys/math.sol";


contract TokenLocking is TokenLockingStorage, DSMath {
modifier onlyColony() {
require(IColonyNetwork(colonyNetwork).isColony(msg.sender), "token-locking-sender-not-colony");
_;
}

modifier tokenNotLocked(address _token) {
if (userLocks[_token][msg.sender].balance > 0) {
require(userLocks[_token][msg.sender].lockCount == totalLockCount[_token], "token-locking-token-locked");
}
_;
}

function setColonyNetwork(address _colonyNetwork) public auth {
colonyNetwork = _colonyNetwork;
}

function getColonyNetwork() public view returns (address) {
return colonyNetwork;
}

function lockToken(address _token) public onlyColony returns (uint256) {
totalLockCount[_token] += 1;
return totalLockCount[_token];
}

function unlockTokenForUser(address _token, address _user, uint256 _lockId) public onlyColony {
require(sub(_lockId, userLocks[_token][_user].lockCount) == 1, "token-locking-invalid-lock-id");
// If we want to unlock tokens at id greater than total lock count, we are doing something wrong
assert(_lockId <= totalLockCount[_token]);
userLocks[_token][_user].lockCount = _lockId;
}

function incrementLockCounterTo(address _token, uint256 _lockId) public {
require(_lockId <= totalLockCount[_token] && _lockId > userLocks[_token][msg.sender].lockCount, "token-locking-invalid-lock-id");
userLocks[_token][msg.sender].lockCount = _lockId;
}

function deposit(address _token, uint256 _amount) public
tokenNotLocked(_token)
{
require(_amount > 0, "token-locking-invalid-amount");

require(ERC20Extended(_token).transferFrom(msg.sender, address(this), _amount), "token-locking-transfer-failed");

userLocks[_token][msg.sender] = Lock(totalLockCount[_token], add(userLocks[_token][msg.sender].balance, _amount));
}

function withdraw(address _token, uint256 _amount) public
tokenNotLocked(_token)
{
require(_amount > 0, "token-locking-invalid-amount");

userLocks[_token][msg.sender].balance = sub(userLocks[_token][msg.sender].balance, _amount);

require(ERC20Extended(_token).transfer(msg.sender, _amount), "token-locking-transfer-failed");
}

function getTotalLockCount(address _token) public view returns (uint256) {
return totalLockCount[_token];
}

function getUserLock(address _token, address _user) public view returns (uint256, uint256) {
return (userLocks[_token][_user].lockCount, userLocks[_token][_user].balance);
}
}
26 changes: 26 additions & 0 deletions contracts/TokenLockingStorage.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
pragma solidity ^0.4.23;

import "../lib/dappsys/auth.sol";


contract TokenLockingStorage is DSAuth {
address resolver;

// Address of ColonyNetwork contract
address colonyNetwork;

struct Lock {
// Users lock count
uint256 lockCount;
// Deposited balance
uint256 balance;
}

// Maps token to user to Lock struct
mapping (address => mapping (address => Lock)) userLocks;

// Maps token to total token lock count. If user token lock count is the same as global, that means that their tokens are unlocked.
// If user token lock count is less than global, that means that their tokens are locked.
// User's lock count should never be greater than total lock count.
mapping (address => uint256) totalLockCount;
}
Loading

0 comments on commit 9f966ff

Please sign in to comment.