Skip to content

Add CL4RRedeemer #791

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Sep 30, 2020
Merged
Show file tree
Hide file tree
Changes from 2 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
156 changes: 156 additions & 0 deletions contracts/schemes/CL4RRedeemer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
pragma solidity ^0.6.12;
// SPDX-License-Identifier: GPL-3.0

import "./ContinuousLocking4Reputation.sol";

/**
* @title A scheme for continuous locking ERC20 Token for reputation
*/
contract CL4RRedeemer is ArcScheme {
using SafeMath for uint256;
using SafeERC20 for IERC20;
using RealMath for uint216;
using RealMath for uint256;
using Math for uint256;

event Redeem(uint256 indexed _lockingId, address indexed _beneficiary, uint256 _amount, uint256 _batchIndex);

struct Batch {
uint256 totalScore;
// A mapping from lockingId to its score
mapping(uint256=>uint) scores;
mapping(uint256=>uint) redeemedScores;
}

struct Lock {
uint256 amount;
uint256 lockingTime;
uint256 period;
}

// A mapping from lockers addresses to their locks
mapping(address => mapping(uint256=>Lock)) public lockers;
// A mapping from batch index to batch
mapping(uint256 => Batch) public batches;

uint256 public reputationRewardLeft; // the amount of reputation that is still left to distribute
uint256 public startTime; // the time (in secs since epoch) that locking can start (is enable)
uint256 public redeemEnableTime;
uint256 public maxLockingBatches;
uint256 public batchTime; // the length of a batch, in seconds
IERC20 public token; // the token to be locked
uint256 public lockCounter; // Total number of locks
uint256 public totalLockedLeft; // the amount of reputation that is still left to distribute
uint256 public repRewardConstA;
uint256 public repRewardConstB;
uint256 public batchesIndexCap;
ContinuousLocking4Reputation public cl4r;

uint256 constant private REAL_FBITS = 40;
// What's the first non-fractional bit
uint256 constant private REAL_ONE = uint256(1) << REAL_FBITS;
uint256 constant private BATCHES_INDEX_HARDCAP = 100;

/**
* @dev initialize
* @param _avatar the avatar to mint reputation from
* @param _cl4r the ContinuousLocking4Reputation address
*/
function initialize(Avatar _avatar, ContinuousLocking4Reputation _cl4r) external {
super._initialize(_avatar);
require(address(_cl4r) != address(0), "ContinuousLocking4Reputation reference contract must be specified");
cl4r = _cl4r;
token = _cl4r.token();
startTime = _cl4r.startTime();
reputationRewardLeft = _cl4r.reputationRewardLeft();
redeemEnableTime = _cl4r.redeemEnableTime();
maxLockingBatches = _cl4r.maxLockingBatches();
batchTime = _cl4r.batchTime();
repRewardConstA = _cl4r.repRewardConstA();
repRewardConstB = _cl4r.repRewardConstB();
batchesIndexCap = _cl4r.batchesIndexCap();
}

/**
* @dev redeem reputation function
* @param _beneficiary the beneficiary to redeem.
* @param _lockingId the lockingId to redeem from.
* @return reputation reputation rewarded
*/
function redeem(address _beneficiary, uint256 _lockingId) public returns(uint256 reputation) {

// solhint-disable-next-line not-rely-on-time
require(now > redeemEnableTime, "now > redeemEnableTime");
Lock storage locker = lockers[_beneficiary][_lockingId];
(locker.amount, locker.lockingTime, locker.period) = cl4r.lockers(_beneficiary, _lockingId);

require(locker.lockingTime != 0, "_lockingId does not exist");
uint256 batchIndexToRedeemFrom = (locker.lockingTime - startTime) / batchTime;
// solhint-disable-next-line not-rely-on-time
uint256 currentBatch = (now - startTime) / batchTime;
uint256 lastBatchIndexToRedeem = currentBatch.min(batchIndexToRedeemFrom.add(locker.period));
for (batchIndexToRedeemFrom; batchIndexToRedeemFrom < lastBatchIndexToRedeem; batchIndexToRedeemFrom++) {
if (batches[batchIndexToRedeemFrom].scores[_lockingId] == 0) {
batches[batchIndexToRedeemFrom].totalScore = cl4r.batches(batchIndexToRedeemFrom);
batches[batchIndexToRedeemFrom].scores[_lockingId] = cl4r.getLockingIdScore(
batchIndexToRedeemFrom, _lockingId
) - batches[batchIndexToRedeemFrom].redeemedScores[_lockingId];
batches[
batchIndexToRedeemFrom
].redeemedScores[_lockingId] += batches[batchIndexToRedeemFrom].scores[_lockingId];
}
Batch storage locking = batches[batchIndexToRedeemFrom];
uint256 score = locking.scores[_lockingId];
if (score > 0) {
locking.scores[_lockingId] = 0;
uint256 batchReputationReward = cl4r.getRepRewardPerBatch(batchIndexToRedeemFrom);
uint256 repRelation = mul(toReal(uint216(score)), batchReputationReward);
uint256 redeemForBatch = div(repRelation, toReal(uint216(locking.totalScore)));
reputation = reputation.add(redeemForBatch);
emit Redeem(_lockingId, _beneficiary, uint256(fromReal(redeemForBatch)), batchIndexToRedeemFrom);
}
}
reputation = uint256(fromReal(reputation));
require(reputation > 0, "reputation to redeem is 0");
// check that the reputation is sum zero
reputationRewardLeft = reputationRewardLeft.sub(reputation);
require(
Controller(avatar.owner())
.mintReputation(reputation, _beneficiary), "mint reputation should succeed");
}

/**
* Multiply one real by another. Truncates overflows.
*/
function mul(uint256 realA, uint256 realB) private pure returns (uint256) {
// When multiplying fixed point in x.y and z.w formats we get (x+z).(y+w) format.
// So we just have to clip off the extra REAL_FBITS fractional bits.
uint256 res = realA * realB;
require(res/realA == realB, "RealMath mul overflow");
return (res >> REAL_FBITS);
}

/**
* Convert an integer to a real. Preserves sign.
*/
function toReal(uint216 ipart) private pure returns (uint256) {
return uint256(ipart) * REAL_ONE;
}

/**
* Convert a real to an integer. Preserves sign.
*/
function fromReal(uint256 _realValue) private pure returns (uint216) {
return uint216(_realValue / REAL_ONE);
}

/**
* Divide one real by another real. Truncates overflows.
*/
function div(uint256 realNumerator, uint256 realDenominator) private pure returns (uint256) {
// We use the reverse of the multiplication trick: convert numerator from
// x.y to (x+z).(y+w) fixed point, then divide by denom in z.w fixed point.
return uint256((uint256(realNumerator) * REAL_ONE) / uint256(realDenominator));
}

}
Loading