mirror of https://github.com/nucypher/nucypher.git
[KMS-ETH]- Changed locking logic, Miner contract prepared for new parameter
parent
14c0cb99a0
commit
1e8ee747bf
|
@ -4,7 +4,7 @@ from nkms_eth import blockchain
|
|||
from nkms_eth import token
|
||||
|
||||
ESCROW_NAME = 'Escrow'
|
||||
MINING_COEFF = [10 ** 9, 50, 1]
|
||||
MINING_COEFF = [10 ** 9, 50, 1, 1, 1]
|
||||
NULL_ADDR = '0x' + '0' * 40
|
||||
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ pragma solidity ^0.4.8;
|
|||
import "./zeppelin/token/SafeERC20.sol";
|
||||
import "./zeppelin/ownership/Ownable.sol";
|
||||
import "./zeppelin/math/Math.sol";
|
||||
import "./lib/AdditionalMath.sol";
|
||||
import "./lib/LinkedList.sol";
|
||||
import "./Miner.sol";
|
||||
import "./NuCypherKMSToken.sol";
|
||||
|
@ -16,6 +17,7 @@ Each client that lock his tokens will receive some compensation
|
|||
contract Escrow is Miner, Ownable {
|
||||
using LinkedList for LinkedList.Data;
|
||||
using SafeERC20 for NuCypherKMSToken;
|
||||
using AdditionalMath for uint256;
|
||||
|
||||
struct ConfirmedPeriodInfo {
|
||||
uint256 period;
|
||||
|
@ -26,8 +28,8 @@ contract Escrow is Miner, Ownable {
|
|||
uint256 value;
|
||||
uint256 decimals;
|
||||
uint256 lockedValue;
|
||||
// last period before the tokens begin to unlock
|
||||
uint256 releasePeriod;
|
||||
bool release;
|
||||
uint256 maxReleasePeriods;
|
||||
uint256 releaseRate;
|
||||
ConfirmedPeriodInfo[] confirmedPeriods;
|
||||
uint256 numberConfirmedPeriods;
|
||||
|
@ -55,14 +57,18 @@ contract Escrow is Miner, Ownable {
|
|||
* @param _miningCoefficient Mining coefficient
|
||||
* @param _blocksPerPeriod Size of one period in blocks
|
||||
* @param _minReleasePeriods Min amount of periods during which tokens will be released
|
||||
* @param _lockedBlocksCoefficient Locked blocks coefficient
|
||||
* @param _awardedPeriods Max periods that will be additionally awarded
|
||||
**/
|
||||
function Escrow(
|
||||
NuCypherKMSToken _token,
|
||||
uint256 _miningCoefficient,
|
||||
uint256 _blocksPerPeriod,
|
||||
uint256 _minReleasePeriods
|
||||
uint256 _minReleasePeriods,
|
||||
uint256 _lockedBlocksCoefficient,
|
||||
uint256 _awardedPeriods
|
||||
)
|
||||
Miner(_token, _miningCoefficient)
|
||||
Miner(_token, _miningCoefficient, _lockedBlocksCoefficient, _awardedPeriods * _blocksPerPeriod)
|
||||
{
|
||||
require(_blocksPerPeriod != 0 && _minReleasePeriods != 0);
|
||||
token = _token;
|
||||
|
@ -102,7 +108,7 @@ contract Escrow is Miner, Ownable {
|
|||
}
|
||||
}
|
||||
// checks if owner can mine more tokens (before or after release period)
|
||||
if (calculateLockedTokens(_owner, currentPeriod, lockedValue, 1) == 0) {
|
||||
if (calculateLockedTokens(_owner, false, lockedValue, 1) == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
return lockedValue;
|
||||
|
@ -122,25 +128,22 @@ contract Escrow is Miner, Ownable {
|
|||
/**
|
||||
* @notice Calculate locked tokens value for owner in next period
|
||||
* @param _owner Tokens owner
|
||||
* @param _period Current or future period number
|
||||
* @param _forceRelease Force unlocking period calculation
|
||||
* @param _lockedTokens Locked tokens in specified period
|
||||
* @param _periods Number of periods after _period that need to calculate
|
||||
* @param _periods Number of periods that need to calculate
|
||||
* @return Calculated locked tokens in next period
|
||||
**/
|
||||
function calculateLockedTokens(
|
||||
address _owner,
|
||||
uint256 _period,
|
||||
bool _forceRelease,
|
||||
uint256 _lockedTokens,
|
||||
uint256 _periods
|
||||
)
|
||||
internal constant returns (uint256)
|
||||
{
|
||||
var nextPeriod = _period.add(_periods);
|
||||
var info = tokenInfo[_owner];
|
||||
var releasePeriod = info.releasePeriod;
|
||||
if (releasePeriod != 0 && releasePeriod < nextPeriod) {
|
||||
var period = Math.max256(_period, releasePeriod);
|
||||
var unlockedTokens = nextPeriod.sub(period).mul(info.releaseRate);
|
||||
if ((_forceRelease || info.release) && _periods != 0) {
|
||||
var unlockedTokens = _periods.mul(info.releaseRate);
|
||||
return unlockedTokens <= _lockedTokens ? _lockedTokens.sub(unlockedTokens) : 0;
|
||||
} else {
|
||||
return _lockedTokens;
|
||||
|
@ -172,15 +175,31 @@ contract Escrow is Miner, Ownable {
|
|||
}
|
||||
var periods = nextPeriod.sub(period);
|
||||
|
||||
return calculateLockedTokens(_owner, period, lockedTokens, periods);
|
||||
return calculateLockedTokens(_owner, false, lockedTokens, periods);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Calculate locked periods for owner from start period
|
||||
* @param _owner Tokens owner
|
||||
* @param _lockedTokens Locked tokens in start period
|
||||
* @return Calculated locked periods
|
||||
**/
|
||||
function calculateLockedPeriods(
|
||||
address _owner,
|
||||
uint256 _lockedTokens
|
||||
)
|
||||
internal constant returns (uint256)
|
||||
{
|
||||
var info = tokenInfo[_owner];
|
||||
return _lockedTokens.div(info.releaseRate);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Deposit tokens
|
||||
* @param _value Amount of token to deposit
|
||||
* @param _periods Amount of periods during which tokens will be locked
|
||||
* @param _periods Amount of periods during which tokens will be unlocked
|
||||
**/
|
||||
function deposit(uint256 _value, uint256 _periods) {
|
||||
function deposit(uint256 _value, uint256 _periods) public {
|
||||
require(_value != 0);
|
||||
if (!tokenOwners.valueExists(msg.sender)) {
|
||||
require(tokenOwners.sizeOf() < MAX_OWNERS);
|
||||
|
@ -195,9 +214,9 @@ contract Escrow is Miner, Ownable {
|
|||
/**
|
||||
* @notice Lock some tokens or increase lock
|
||||
* @param _value Amount of tokens which should lock
|
||||
* @param _periods Amount of periods during which tokens will be locked
|
||||
* @param _periods Amount of periods during which tokens will be unlocked
|
||||
**/
|
||||
function lock(uint256 _value, uint256 _periods) {
|
||||
function lock(uint256 _value, uint256 _periods) public {
|
||||
// TODO add checking min _value
|
||||
require(_value != 0 || _periods != 0);
|
||||
|
||||
|
@ -209,27 +228,38 @@ contract Escrow is Miner, Ownable {
|
|||
var currentPeriod = block.number.div(blocksPerPeriod);
|
||||
if (lockedTokens == 0) {
|
||||
info.lockedValue = _value;
|
||||
info.releasePeriod = currentPeriod.add(_periods);
|
||||
info.releaseRate = _value.div(minReleasePeriods);
|
||||
info.maxReleasePeriods = Math.max256(_periods, minReleasePeriods);
|
||||
info.releaseRate = Math.max256(_value.divCeil(info.maxReleasePeriods), 1);
|
||||
info.release = false;
|
||||
} else {
|
||||
info.lockedValue = lockedTokens.add(_value);
|
||||
var period = Math.max256(info.releasePeriod, currentPeriod);
|
||||
info.releasePeriod = period.add(_periods);
|
||||
info.maxReleasePeriods = info.maxReleasePeriods.add(_periods);
|
||||
info.releaseRate = Math.max256(
|
||||
info.releaseRate, info.lockedValue.div(minReleasePeriods));
|
||||
info.lockedValue.divCeil(info.maxReleasePeriods), info.releaseRate);
|
||||
}
|
||||
|
||||
confirmActivity(info.lockedValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Switch lock
|
||||
**/
|
||||
function switchLock() public {
|
||||
var info = tokenInfo[msg.sender];
|
||||
info.release = !info.release;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Withdraw available amount of tokens back to owner
|
||||
* @param _value Amount of token to withdraw
|
||||
**/
|
||||
function withdraw(uint256 _value) {
|
||||
function withdraw(uint256 _value) public {
|
||||
var info = tokenInfo[msg.sender];
|
||||
// TODO optimize
|
||||
var lockedTokens = Math.max256(calculateLockedTokens(msg.sender, 1),
|
||||
getLockedTokens(msg.sender));
|
||||
require(_value <= token.balanceOf(address(this)) &&
|
||||
_value <= info.value.sub(getLockedTokens(msg.sender)));
|
||||
_value <= info.value.sub(lockedTokens));
|
||||
info.value -= _value;
|
||||
token.safeTransfer(msg.sender, _value);
|
||||
}
|
||||
|
@ -237,7 +267,7 @@ contract Escrow is Miner, Ownable {
|
|||
/**
|
||||
* @notice Withdraw all amount of tokens back to owner (only if no locked)
|
||||
**/
|
||||
function withdrawAll() {
|
||||
function withdrawAll() public {
|
||||
// TODO extract modifier
|
||||
require(tokenOwners.valueExists(msg.sender));
|
||||
var info = tokenInfo[msg.sender];
|
||||
|
@ -314,7 +344,7 @@ contract Escrow is Miner, Ownable {
|
|||
|
||||
var currentPeriod = nextPeriod - 1;
|
||||
var lockedTokens = calculateLockedTokens(
|
||||
msg.sender, currentPeriod, getLockedTokens(msg.sender), 1);
|
||||
msg.sender, false, getLockedTokens(msg.sender), 1);
|
||||
confirmActivity(lockedTokens);
|
||||
}
|
||||
|
||||
|
@ -327,9 +357,15 @@ contract Escrow is Miner, Ownable {
|
|||
var numberPeriodsForMinting = info.numberConfirmedPeriods;
|
||||
require(numberPeriodsForMinting > 0 &&
|
||||
info.confirmedPeriods[0].period <= previousPeriod);
|
||||
var currentLockedValue = getLockedTokens(msg.sender);
|
||||
|
||||
var currentLockedValue = getLockedTokens(msg.sender);
|
||||
var allLockedBlocks = calculateLockedPeriods(
|
||||
msg.sender,
|
||||
info.confirmedPeriods[numberPeriodsForMinting - 1].lockedValue)
|
||||
.add(numberPeriodsForMinting)
|
||||
.mul(blocksPerPeriod);
|
||||
var decimals = info.decimals;
|
||||
|
||||
if (info.confirmedPeriods[numberPeriodsForMinting - 1].period > previousPeriod) {
|
||||
numberPeriodsForMinting--;
|
||||
}
|
||||
|
@ -345,7 +381,9 @@ contract Escrow is Miner, Ownable {
|
|||
lockedValue,
|
||||
lockedPerPeriod[period].totalLockedValue,
|
||||
blocksPerPeriod,
|
||||
allLockedBlocks,
|
||||
decimals);
|
||||
allLockedBlocks = allLockedBlocks.sub(blocksPerPeriod);
|
||||
if (lockedPerPeriod[period].numberOwnersToBeRewarded > 1) {
|
||||
lockedPerPeriod[period].numberOwnersToBeRewarded--;
|
||||
} else {
|
||||
|
@ -402,14 +440,14 @@ contract Escrow is Miner, Ownable {
|
|||
info.confirmedPeriods[numberConfirmedPeriods - 1].period == currentPeriod) {
|
||||
var lockedTokens = calculateLockedTokens(
|
||||
current,
|
||||
currentPeriod,
|
||||
true,
|
||||
info.confirmedPeriods[numberConfirmedPeriods - 1].lockedValue,
|
||||
_periods);
|
||||
} else if (numberConfirmedPeriods > 1 &&
|
||||
info.confirmedPeriods[numberConfirmedPeriods - 2].period == currentPeriod) {
|
||||
lockedTokens = calculateLockedTokens(
|
||||
current,
|
||||
currentPeriod + 1,
|
||||
true,
|
||||
info.confirmedPeriods[numberConfirmedPeriods - 1].lockedValue,
|
||||
_periods - 1);
|
||||
} else {
|
||||
|
|
|
@ -2,7 +2,6 @@ pragma solidity ^0.4.0;
|
|||
|
||||
|
||||
import "./MineableToken.sol";
|
||||
import "./lib/ExponentMath.sol";
|
||||
import "./zeppelin/math/SafeMath.sol";
|
||||
|
||||
|
||||
|
@ -14,18 +13,33 @@ contract Miner {
|
|||
|
||||
MineableToken token;
|
||||
uint256 public miningCoefficient;
|
||||
uint256 public lockedBlocksCoefficient;
|
||||
uint256 public awardedLockedBlocks;
|
||||
|
||||
/**
|
||||
* @notice The Miner constructor sets address of token contract and coefficients for mining
|
||||
* @dev Formula for mining
|
||||
(futureSupply - currentSupply) * (lockedBlocks * lockedValue / totalLockedValue / k)
|
||||
(futureSupply - currentSupply) * (lockedBlocks * lockedValue / totalLockedValue) * (k1 + allLockedBlocks) / k2
|
||||
if allLockedBlocks > awardedLockedBlocks then allLockedBlocks = awardedLockedBlocks
|
||||
* @param _token Token contract
|
||||
* @param _miningCoefficient Mining coefficient (k)
|
||||
* @param _miningCoefficient Mining coefficient (k2)
|
||||
* @param _lockedBlocksCoefficient Locked blocks coefficient (k1)
|
||||
* @param _awardedLockedBlocks Max blocks that will be additionally awarded
|
||||
**/
|
||||
function Miner(MineableToken _token, uint256 _miningCoefficient) {
|
||||
require(address(_token) != 0x0 && _miningCoefficient != 0);
|
||||
function Miner(
|
||||
MineableToken _token,
|
||||
uint256 _miningCoefficient,
|
||||
uint256 _lockedBlocksCoefficient,
|
||||
uint256 _awardedLockedBlocks
|
||||
) {
|
||||
require(address(_token) != 0x0 &&
|
||||
_miningCoefficient != 0 &&
|
||||
_lockedBlocksCoefficient != 0 &&
|
||||
_awardedLockedBlocks != 0);
|
||||
token = _token;
|
||||
miningCoefficient = _miningCoefficient;
|
||||
lockedBlocksCoefficient = _lockedBlocksCoefficient;
|
||||
awardedLockedBlocks = _awardedLockedBlocks;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -33,7 +47,8 @@ contract Miner {
|
|||
* @param _to The address that will receive the minted tokens.
|
||||
* @param _lockedValue The amount of tokens that were locked by user.
|
||||
* @param _totalLockedValue The amount of tokens that were locked by all users.
|
||||
* @param _lockedBlocks The amount of blocks during which tokens were locked.
|
||||
* @param _currentLockedBlocks The current amount of blocks during which tokens were locked.
|
||||
* @param _allLockedBlocks The max amount of blocks during which tokens were locked.
|
||||
* @param _decimals The amount of locked tokens and blocks in decimals.
|
||||
* @return Amount of minted tokens.
|
||||
*/
|
||||
|
@ -41,16 +56,27 @@ contract Miner {
|
|||
address _to,
|
||||
uint256 _lockedValue,
|
||||
uint256 _totalLockedValue,
|
||||
uint256 _lockedBlocks,
|
||||
uint256 _currentLockedBlocks,
|
||||
uint256 _allLockedBlocks,
|
||||
uint256 _decimals
|
||||
)
|
||||
internal returns (uint256 amount, uint256 decimals)
|
||||
{
|
||||
//futureSupply * lockedBlocks * lockedValue / (totalLockedValue * k) -
|
||||
//currentSupply * lockedBlocks * lockedValue / (totalLockedValue * k)
|
||||
uint256 denominator = _totalLockedValue.mul(miningCoefficient);
|
||||
uint256 maxValue = token.futureSupply().mul(_lockedBlocks).mul(_lockedValue).div(denominator);
|
||||
uint256 value = token.totalSupply().mul(_lockedBlocks).mul(_lockedValue).div(denominator);
|
||||
//futureSupply * currentLockedBlocks * lockedValue * (k1 + allLockedBlocks) / (totalLockedValue * k2) -
|
||||
//currentSupply * currentLockedBlocks * lockedValue * (k1 + allLockedBlocks) / (totalLockedValue * k2)
|
||||
var allLockedBlocks = (_allLockedBlocks <= awardedLockedBlocks ?
|
||||
_allLockedBlocks : awardedLockedBlocks).add(lockedBlocksCoefficient);
|
||||
var denominator = _totalLockedValue.mul(miningCoefficient);
|
||||
var maxValue = token.futureSupply()
|
||||
.mul(_currentLockedBlocks)
|
||||
.mul(_lockedValue)
|
||||
// .mul(allLockedBlocks)
|
||||
.div(denominator);
|
||||
var value = token.totalSupply()
|
||||
.mul(_currentLockedBlocks)
|
||||
.mul(_lockedValue)
|
||||
// .mul(allLockedBlocks)
|
||||
.div(denominator);
|
||||
amount = maxValue.sub(value);
|
||||
token.mint(_to, amount);
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import "./zeppelin/token/SafeERC20.sol";
|
|||
import "./zeppelin/math/SafeMath.sol";
|
||||
import "./zeppelin/math/Math.sol";
|
||||
import "./zeppelin/ownership/Ownable.sol";
|
||||
import "./lib/AdditionalMath.sol";
|
||||
|
||||
|
||||
/**
|
||||
|
@ -14,6 +15,7 @@ import "./zeppelin/ownership/Ownable.sol";
|
|||
contract Wallet is Ownable {
|
||||
using SafeERC20 for NuCypherKMSToken;
|
||||
using SafeMath for uint256;
|
||||
using AdditionalMath for uint256;
|
||||
|
||||
struct PeriodInfo {
|
||||
uint256 period;
|
||||
|
@ -21,15 +23,16 @@ contract Wallet is Ownable {
|
|||
}
|
||||
|
||||
address public manager;
|
||||
// TODO maybe get from manager
|
||||
NuCypherKMSToken public token;
|
||||
uint256 public blocksPerPeriod;
|
||||
uint256 public minReleasePeriods;
|
||||
|
||||
uint256 public lockedValue;
|
||||
uint256 public decimals;
|
||||
// last period before the tokens begin to unlock
|
||||
uint256 releasePeriod;
|
||||
uint256 releaseRate;
|
||||
bool public release;
|
||||
uint256 public maxReleasePeriods;
|
||||
uint256 public releaseRate;
|
||||
PeriodInfo[] public confirmedPeriods;
|
||||
uint256 public numberConfirmedPeriods;
|
||||
|
||||
|
@ -86,7 +89,7 @@ contract Wallet is Ownable {
|
|||
}
|
||||
}
|
||||
// checks if owner can mine more tokens (before or after release period)
|
||||
if (calculateLockedTokens(period, lockedValueToCheck, 1) == 0) {
|
||||
if (calculateLockedTokens(false, lockedValueToCheck, 1) == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
return lockedValueToCheck;
|
||||
|
@ -94,23 +97,21 @@ contract Wallet is Ownable {
|
|||
}
|
||||
|
||||
/**
|
||||
* @notice Calculate locked tokens value in next period
|
||||
* @param _period Current or future period number
|
||||
* @notice Calculate locked tokens value for owner in next period
|
||||
* @param _forceRelease Force unlocking period calculation
|
||||
* @param _lockedTokens Locked tokens in specified period
|
||||
* @param _periods Number of periods after _period that need to calculate
|
||||
* @param _periods Number of periods that need to calculate
|
||||
* @return Calculated locked tokens in next period
|
||||
**/
|
||||
function calculateLockedTokens(
|
||||
uint256 _period,
|
||||
bool _forceRelease,
|
||||
uint256 _lockedTokens,
|
||||
uint256 _periods
|
||||
)
|
||||
public constant returns (uint256)
|
||||
{
|
||||
var nextPeriod = _period.add(_periods);
|
||||
if (releasePeriod != 0 && releasePeriod < nextPeriod) {
|
||||
var period = Math.max256(_period, releasePeriod);
|
||||
var unlockedTokens = nextPeriod.sub(period).mul(releaseRate);
|
||||
if ((_forceRelease || release) && _periods != 0) {
|
||||
var unlockedTokens = _periods.mul(releaseRate);
|
||||
return unlockedTokens <= _lockedTokens ? _lockedTokens.sub(unlockedTokens) : 0;
|
||||
} else {
|
||||
return _lockedTokens;
|
||||
|
@ -139,7 +140,18 @@ contract Wallet is Ownable {
|
|||
}
|
||||
var periods = nextPeriod.sub(period);
|
||||
|
||||
return calculateLockedTokens(period, lockedTokens, periods);
|
||||
return calculateLockedTokens(false, lockedTokens, periods);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Calculate locked periods from start period
|
||||
* @param _lockedTokens Locked tokens in start period
|
||||
* @return Calculated locked periods
|
||||
**/
|
||||
function calculateLockedPeriods(uint256 _lockedTokens)
|
||||
public constant returns (uint256)
|
||||
{
|
||||
return _lockedTokens.div(releaseRate);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -156,14 +168,14 @@ contract Wallet is Ownable {
|
|||
var currentPeriod = block.number.div(blocksPerPeriod);
|
||||
if (lockedTokens == 0) {
|
||||
lockedValue = _value;
|
||||
releasePeriod = currentPeriod.add(_periods);
|
||||
releaseRate = _value.div(minReleasePeriods);
|
||||
maxReleasePeriods = Math.max256(_periods, minReleasePeriods);
|
||||
releaseRate = Math.max256(_value.divCeil(maxReleasePeriods), 1);
|
||||
release = false;
|
||||
} else {
|
||||
lockedValue = lockedTokens.add(_value);
|
||||
var period = Math.max256(releasePeriod, currentPeriod);
|
||||
releasePeriod = period.add(_periods);
|
||||
maxReleasePeriods = maxReleasePeriods.add(_periods);
|
||||
releaseRate = Math.max256(
|
||||
releaseRate, lockedValue.div(minReleasePeriods));
|
||||
lockedValue.divCeil(maxReleasePeriods), releaseRate);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,16 +183,26 @@ contract Wallet is Ownable {
|
|||
* @notice Sets locked tokens
|
||||
* @param _value Amount of tokens which should lock
|
||||
**/
|
||||
function updateLock(uint256 _value) onlyManager public {
|
||||
function updateLockedTokens(uint256 _value) onlyManager public {
|
||||
lockedValue = _value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Switch lock
|
||||
**/
|
||||
function switchLock() onlyOwner public {
|
||||
release = !release;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Withdraw available amount of tokens back to owner
|
||||
* @param _value Amount of token to withdraw
|
||||
**/
|
||||
function withdraw(uint256 _value) onlyOwner public {
|
||||
require(_value <= token.balanceOf(address(this)).sub(getLockedTokens()));
|
||||
// TODO optimize
|
||||
var lockedTokens = Math.max256(calculateLockedTokens(1),
|
||||
getLockedTokens());
|
||||
require(_value <= token.balanceOf(address(this)).sub(lockedTokens));
|
||||
token.safeTransfer(msg.sender, _value);
|
||||
}
|
||||
|
||||
|
|
|
@ -39,14 +39,18 @@ contract WalletManager is Miner, Ownable {
|
|||
* @param _miningCoefficient Mining coefficient
|
||||
* @param _blocksPerPeriod Size of one period in blocks
|
||||
* @param _minReleasePeriods Min amount of periods during which tokens will be released
|
||||
* @param _lockedBlocksCoefficient Locked blocks coefficient
|
||||
* @param _awardedPeriods Max periods that will be additionally awarded
|
||||
**/
|
||||
function WalletManager(
|
||||
NuCypherKMSToken _token,
|
||||
uint256 _miningCoefficient,
|
||||
uint256 _blocksPerPeriod,
|
||||
uint256 _minReleasePeriods
|
||||
uint256 _minReleasePeriods,
|
||||
uint256 _lockedBlocksCoefficient,
|
||||
uint256 _awardedPeriods
|
||||
)
|
||||
Miner(_token, _miningCoefficient)
|
||||
Miner(_token, _miningCoefficient, _lockedBlocksCoefficient, _awardedPeriods * _blocksPerPeriod)
|
||||
{
|
||||
require(_blocksPerPeriod != 0 && _minReleasePeriods != 0);
|
||||
token = _token;
|
||||
|
@ -66,7 +70,7 @@ contract WalletManager is Miner, Ownable {
|
|||
* @notice Create wallet for user
|
||||
* @return Address of created wallet
|
||||
**/
|
||||
function createWallet() returns (address) {
|
||||
function createWallet() public returns (address) {
|
||||
require(!walletOwners.valueExists(msg.sender) &&
|
||||
walletOwners.sizeOf() < MAX_OWNERS);
|
||||
Wallet wallet = new Wallet(token, blocksPerPeriod, minReleasePeriods);
|
||||
|
@ -156,7 +160,7 @@ contract WalletManager is Miner, Ownable {
|
|||
|
||||
var currentPeriod = nextPeriod - 1;
|
||||
var lockedTokens = wallet.calculateLockedTokens(
|
||||
currentPeriod, wallet.getLockedTokens(), 1);
|
||||
false, wallet.getLockedTokens(), 1);
|
||||
confirmActivity(lockedTokens);
|
||||
}
|
||||
|
||||
|
@ -164,14 +168,18 @@ contract WalletManager is Miner, Ownable {
|
|||
* @notice Mint tokens for sender for previous periods if he locked his tokens and confirmed activity
|
||||
**/
|
||||
function mint() walletExists external {
|
||||
var previousPeriod = block.number / blocksPerPeriod - 1;
|
||||
var previousPeriod = block.number.div(blocksPerPeriod).sub(1);
|
||||
var wallet = wallets[msg.sender];
|
||||
var numberPeriodsForMinting = wallet.numberConfirmedPeriods();
|
||||
require(numberPeriodsForMinting > 0 &&
|
||||
wallet.getConfirmedPeriod(0) <= previousPeriod);
|
||||
var currentLockedValue = wallet.getLockedTokens();
|
||||
|
||||
var allLockedBlocks = wallet.calculateLockedPeriods(
|
||||
wallet.getConfirmedPeriodValue(numberPeriodsForMinting - 1))
|
||||
.add(numberPeriodsForMinting)
|
||||
.mul(blocksPerPeriod);
|
||||
var decimals = wallet.decimals();
|
||||
|
||||
if (wallet.getConfirmedPeriod(numberPeriodsForMinting - 1) > previousPeriod) {
|
||||
numberPeriodsForMinting--;
|
||||
}
|
||||
|
@ -186,7 +194,9 @@ contract WalletManager is Miner, Ownable {
|
|||
lockedValue,
|
||||
lockedPerPeriod[period].totalLockedValue,
|
||||
blocksPerPeriod,
|
||||
allLockedBlocks,
|
||||
decimals);
|
||||
allLockedBlocks = allLockedBlocks.sub(blocksPerPeriod);
|
||||
if (lockedPerPeriod[period].numberOwnersToBeRewarded > 1) {
|
||||
lockedPerPeriod[period].numberOwnersToBeRewarded--;
|
||||
} else {
|
||||
|
@ -197,7 +207,7 @@ contract WalletManager is Miner, Ownable {
|
|||
wallet.deleteConfirmedPeriods(numberPeriodsForMinting);
|
||||
|
||||
// Update lockedValue for current period
|
||||
wallet.updateLock(currentLockedValue);
|
||||
wallet.updateLockedTokens(currentLockedValue);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -237,13 +247,13 @@ contract WalletManager is Miner, Ownable {
|
|||
if (numberConfirmedPeriods > 0 &&
|
||||
wallet.getConfirmedPeriod(numberConfirmedPeriods - 1) == currentPeriod) {
|
||||
var lockedTokens = wallet.calculateLockedTokens(
|
||||
currentPeriod,
|
||||
true,
|
||||
wallet.getConfirmedPeriodValue(numberConfirmedPeriods - 1),
|
||||
_periods);
|
||||
} else if (numberConfirmedPeriods > 1 &&
|
||||
wallet.getConfirmedPeriod(numberConfirmedPeriods - 2) == currentPeriod) {
|
||||
lockedTokens = wallet.calculateLockedTokens(
|
||||
currentPeriod + 1,
|
||||
true,
|
||||
wallet.getConfirmedPeriodValue(numberConfirmedPeriods - 1),
|
||||
_periods - 1);
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
pragma solidity ^0.4.11;
|
||||
|
||||
|
||||
import "../zeppelin/math/SafeMath.sol";
|
||||
|
||||
|
||||
/**
|
||||
* @notice Additional math operations
|
||||
**/
|
||||
library AdditionalMath {
|
||||
using SafeMath for uint256;
|
||||
|
||||
/**
|
||||
* @notice Division and ceil
|
||||
**/
|
||||
function divCeil(uint256 a, uint256 b)
|
||||
public constant returns (uint256) {
|
||||
return (a.add(b) - 1) / b;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
pragma solidity ^0.4.11;
|
||||
|
||||
|
||||
/**
|
||||
* @notice Math operations for calculating exponent
|
||||
**/
|
||||
library ExponentMath {
|
||||
|
||||
/**
|
||||
* @notice Calculate k*e^(x/rate)
|
||||
* @param x Point on the curve
|
||||
* @param k Coefficient used for increase precision
|
||||
* @param rate Curve growing rate
|
||||
* @param iterations Calculate iterations. The higher the value,
|
||||
the greater the accuracy and the higher the cost
|
||||
* @return k*e^(x/rate)
|
||||
**/
|
||||
function exp(
|
||||
uint256 x,
|
||||
uint256 k,
|
||||
uint256 rate,
|
||||
uint64 iterations
|
||||
)
|
||||
internal constant returns (uint256)
|
||||
{
|
||||
require(iterations != 0);
|
||||
uint256 result = k + k * x / rate;
|
||||
uint256 factorial = 1;
|
||||
for (uint i = 2; i <= iterations; i++) {
|
||||
factorial *= i;
|
||||
uint256 value = k * (x ** i) / (factorial * (rate ** i));
|
||||
if (value == 0) {
|
||||
break;
|
||||
}
|
||||
result += value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Calculate maxValue*(1-1/e^(x/rate))
|
||||
* @param x Point on the curve
|
||||
* @param maxValue Max value
|
||||
* @param rate Curve growing rate
|
||||
* @param multiplicator Coefficient used for increase precision.
|
||||
Low values lead to low accuracy, but high value can cause overflow
|
||||
* @param iterations Calculate iterations. The higher the value,
|
||||
the greater the accuracy and the higher the cost
|
||||
* @return maxValue*(1-1/e^(x/rate))
|
||||
**/
|
||||
function exponentialFunction(
|
||||
uint256 x,
|
||||
uint256 maxValue,
|
||||
uint256 rate,
|
||||
uint256 multiplicator,
|
||||
uint64 iterations
|
||||
)
|
||||
internal constant returns (uint256)
|
||||
{
|
||||
return (multiplicator * maxValue -
|
||||
maxValue * multiplicator ** 2 / exp(x, multiplicator, rate, iterations)) /
|
||||
multiplicator;
|
||||
}
|
||||
|
||||
}
|
|
@ -21,6 +21,8 @@ def lock(amount: int, locktime: int, address: str = None):
|
|||
chain.wait.for_receipt(tx, timeout=blockchain.TIMEOUT)
|
||||
tx = escrow.transact({'from': address}).deposit(amount, locktime)
|
||||
chain.wait.for_receipt(tx, timeout=blockchain.TIMEOUT)
|
||||
tx = escrow.transact({'from': address}).switchLock()
|
||||
chain.wait.for_receipt(tx, timeout=blockchain.TIMEOUT)
|
||||
|
||||
|
||||
def mine(address: str = None):
|
||||
|
|
|
@ -12,9 +12,11 @@ contract MinerTest is Miner {
|
|||
|
||||
function MinerTest(
|
||||
MineableToken _token,
|
||||
uint256 _miningCoefficient
|
||||
uint256 _miningCoefficient,
|
||||
uint256 _lockedBlocksCoefficient,
|
||||
uint256 _awardedLockedBlocks
|
||||
)
|
||||
Miner(_token, _miningCoefficient)
|
||||
Miner(_token, _miningCoefficient, _lockedBlocksCoefficient, _awardedLockedBlocks)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -23,11 +25,18 @@ contract MinerTest is Miner {
|
|||
uint256 _lockedValue,
|
||||
uint256 _totalLockedValue,
|
||||
uint256 _lockedBlocks,
|
||||
uint256 _allLockedBlocks,
|
||||
uint256 _decimals
|
||||
)
|
||||
public returns (uint256 amount, uint256 decimals)
|
||||
{
|
||||
return mint(_to, _lockedValue, _totalLockedValue, _lockedBlocks, _decimals);
|
||||
return mint(
|
||||
_to,
|
||||
_lockedValue,
|
||||
_totalLockedValue,
|
||||
_lockedBlocks,
|
||||
_allLockedBlocks,
|
||||
_decimals);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
pragma solidity ^0.4.11;
|
||||
|
||||
|
||||
import "contracts/lib/ExponentMath.sol";
|
||||
|
||||
|
||||
/**
|
||||
* @dev Contract for testing ExponentMath library
|
||||
**/
|
||||
contract ExponentMathTest {
|
||||
|
||||
/**
|
||||
* @dev maxValue*(1-1/e^(x/rate))
|
||||
**/
|
||||
function exponentialFunction(
|
||||
uint256 x,
|
||||
uint256 maxValue,
|
||||
uint256 rate,
|
||||
uint256 multiplicator,
|
||||
uint64 iterations
|
||||
)
|
||||
constant returns (uint256)
|
||||
{
|
||||
return ExponentMath.exponentialFunction(x, maxValue, rate, multiplicator, iterations);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev k*e^(x/rate)
|
||||
**/
|
||||
function exp(
|
||||
uint256 x,
|
||||
uint256 k,
|
||||
uint256 rate,
|
||||
uint64 iterations
|
||||
)
|
||||
constant returns (uint256)
|
||||
{
|
||||
return ExponentMath.exp(x, k, rate, iterations);
|
||||
}
|
||||
|
||||
}
|
|
@ -17,7 +17,7 @@ def escrow(web3, chain, token):
|
|||
creator = web3.eth.accounts[0]
|
||||
# Creator deploys the escrow
|
||||
escrow, _ = chain.provider.get_or_deploy_contract(
|
||||
'Escrow', deploy_args=[token.address, 10 ** 9, 50, 2],
|
||||
'Escrow', deploy_args=[token.address, 10 ** 9, 50, 2, 1, 1],
|
||||
deploy_transaction={'from': creator})
|
||||
return escrow
|
||||
|
||||
|
@ -50,7 +50,7 @@ def test_escrow(web3, chain, token, escrow):
|
|||
|
||||
# And can't lock because nothing to lock
|
||||
with pytest.raises(TransactionFailed):
|
||||
tx = escrow.transact({'from': ursula}).lock(500, 100)
|
||||
tx = escrow.transact({'from': ursula}).lock(500, 2)
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Check that nothing is locked
|
||||
|
@ -64,21 +64,25 @@ def test_escrow(web3, chain, token, escrow):
|
|||
# chain.wait.for_receipt(tx)
|
||||
|
||||
# Ursula and Alice transfer some tokens to the escrow and lock them
|
||||
tx = escrow.transact({'from': ursula}).deposit(1000, 2)
|
||||
tx = escrow.transact({'from': ursula}).deposit(1000, 1)
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 1000 == token.call().balanceOf(escrow.address)
|
||||
assert 9000 == token.call().balanceOf(ursula)
|
||||
assert 1000 == escrow.call().getLockedTokens(ursula)
|
||||
assert 500 == escrow.call().calculateLockedTokens(ursula, 3)
|
||||
assert 0 == escrow.call().calculateLockedTokens(ursula, 4)
|
||||
tx = escrow.transact({'from': alice}).deposit(500, 6)
|
||||
assert 1000 == escrow.call().calculateLockedTokens(ursula, 1)
|
||||
assert 1000 == escrow.call().calculateLockedTokens(ursula, 2)
|
||||
tx = escrow.transact({'from': ursula}).switchLock()
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 500 == escrow.call().calculateLockedTokens(ursula, 2)
|
||||
tx = escrow.transact({'from': ursula}).switchLock()
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 1000 == escrow.call().calculateLockedTokens(ursula, 2)
|
||||
tx = escrow.transact({'from': alice}).deposit(500, 2)
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 1500 == token.call().balanceOf(escrow.address)
|
||||
assert 9500 == token.call().balanceOf(alice)
|
||||
assert 500 == escrow.call().getLockedTokens(alice)
|
||||
assert 500 == escrow.call().calculateLockedTokens(alice, 6)
|
||||
assert 250 == escrow.call().calculateLockedTokens(alice, 7)
|
||||
assert 0 == escrow.call().calculateLockedTokens(alice, 8)
|
||||
assert 500 == escrow.call().calculateLockedTokens(alice, 1)
|
||||
|
||||
# Checks locked tokens in next period
|
||||
chain.wait.for_block(web3.eth.blockNumber + 50)
|
||||
|
@ -100,6 +104,10 @@ def test_escrow(web3, chain, token, escrow):
|
|||
chain.wait.for_receipt(tx)
|
||||
assert 2000 == token.call().balanceOf(escrow.address)
|
||||
assert 8500 == token.call().balanceOf(ursula)
|
||||
|
||||
# Ursula starts unlocking
|
||||
tx = escrow.transact({'from': ursula}).switchLock()
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 750 == escrow.call().calculateLockedTokens(ursula, 2)
|
||||
|
||||
# Wait 50 blocks and checks locking
|
||||
|
@ -134,6 +142,9 @@ def test_escrow(web3, chain, token, escrow):
|
|||
# Release rate will be updated too because of end of previous locking
|
||||
assert 750 == escrow.call().getLockedTokens(ursula)
|
||||
assert 600 == escrow.call().calculateLockedTokens(ursula, 1)
|
||||
assert 600 == escrow.call().calculateLockedTokens(ursula, 2)
|
||||
tx = escrow.transact({'from': ursula}).switchLock()
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 300 == escrow.call().calculateLockedTokens(ursula, 2)
|
||||
assert 0 == escrow.call().calculateLockedTokens(ursula, 3)
|
||||
chain.wait.for_block(web3.eth.blockNumber + 50)
|
||||
|
@ -146,8 +157,8 @@ def test_escrow(web3, chain, token, escrow):
|
|||
chain.wait.for_receipt(tx)
|
||||
assert 600 == escrow.call().getLockedTokens(ursula)
|
||||
assert 800 == escrow.call().calculateLockedTokens(ursula, 1)
|
||||
assert 800 == escrow.call().calculateLockedTokens(ursula, 2)
|
||||
assert 400 == escrow.call().calculateLockedTokens(ursula, 3)
|
||||
assert 500 == escrow.call().calculateLockedTokens(ursula, 2)
|
||||
assert 200 == escrow.call().calculateLockedTokens(ursula, 3)
|
||||
assert 0 == escrow.call().calculateLockedTokens(ursula, 4)
|
||||
chain.wait.for_block(web3.eth.blockNumber + 50)
|
||||
assert 800 == escrow.call().getLockedTokens(ursula)
|
||||
|
@ -158,9 +169,11 @@ def test_escrow(web3, chain, token, escrow):
|
|||
# tx = escrow.transact({'from': ursula}).deposit(100, 100)
|
||||
# chain.wait.for_receipt(tx)
|
||||
|
||||
# Alice increases lock by deposit more tokens
|
||||
# Alice starts unlocking and increases lock by deposit more tokens
|
||||
tx = escrow.transact({'from': alice}).deposit(500, 0)
|
||||
chain.wait.for_receipt(tx)
|
||||
tx = escrow.transact({'from': alice}).switchLock()
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 500 == escrow.call().getLockedTokens(alice)
|
||||
assert 1000 == escrow.call().calculateLockedTokens(alice, 1)
|
||||
assert 500 == escrow.call().calculateLockedTokens(alice, 2)
|
||||
|
@ -173,16 +186,14 @@ def test_escrow(web3, chain, token, escrow):
|
|||
chain.wait.for_receipt(tx)
|
||||
assert 1000 == escrow.call().getLockedTokens(alice)
|
||||
assert 500 == escrow.call().calculateLockedTokens(alice, 1)
|
||||
assert 500 == escrow.call().calculateLockedTokens(alice, 2)
|
||||
assert 0 == escrow.call().calculateLockedTokens(alice, 3)
|
||||
assert 0 == escrow.call().calculateLockedTokens(alice, 2)
|
||||
|
||||
# Alice increases lock by small amount of tokens
|
||||
tx = escrow.transact({'from': alice}).deposit(100, 0)
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 600 == escrow.call().calculateLockedTokens(alice, 1)
|
||||
assert 600 == escrow.call().calculateLockedTokens(alice, 2)
|
||||
assert 100 == escrow.call().calculateLockedTokens(alice, 3)
|
||||
assert 0 == escrow.call().calculateLockedTokens(alice, 4)
|
||||
assert 100 == escrow.call().calculateLockedTokens(alice, 2)
|
||||
assert 0 == escrow.call().calculateLockedTokens(alice, 3)
|
||||
|
||||
# Ursula can't destroy contract
|
||||
with pytest.raises(TransactionFailed):
|
||||
|
@ -228,6 +239,11 @@ def test_locked_distribution(web3, chain, token, escrow):
|
|||
n_locked = escrow.call().getAllLockedTokens()
|
||||
assert n_locked > 0
|
||||
|
||||
# And confirm activity
|
||||
for miner in miners:
|
||||
tx = escrow.transact({'from': miner}).confirmActivity()
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
address_stop, shift = escrow.call().findCumSum(NULL_ADDR, n_locked // 3, 1)
|
||||
assert miners[0].lower() == address_stop.lower()
|
||||
assert n_locked // 3 == shift
|
||||
|
@ -241,7 +257,10 @@ def test_locked_distribution(web3, chain, token, escrow):
|
|||
assert miners[2].lower() == address_stop.lower()
|
||||
assert 1 == shift
|
||||
|
||||
address_stop, shift = escrow.call().findCumSum(NULL_ADDR, 1, 12)
|
||||
address_stop, shift = escrow.call().findCumSum(NULL_ADDR, 1, 10)
|
||||
assert NULL_ADDR != address_stop.lower()
|
||||
assert 0 != shift
|
||||
address_stop, shift = escrow.call().findCumSum(NULL_ADDR, 1, 11)
|
||||
assert NULL_ADDR == address_stop.lower()
|
||||
assert 0 == shift
|
||||
|
||||
|
@ -271,15 +290,15 @@ def test_mining(web3, chain, token, escrow):
|
|||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Ursula and Alice give Escrow rights to transfer
|
||||
tx = token.transact({'from': ursula}).approve(escrow.address, 1000)
|
||||
tx = token.transact({'from': ursula}).approve(escrow.address, 2000)
|
||||
chain.wait.for_receipt(tx)
|
||||
tx = token.transact({'from': alice}).approve(escrow.address, 500)
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Ursula and Alice transfer some tokens to the escrow and lock them
|
||||
tx = escrow.transact({'from': ursula}).deposit(1000, 2)
|
||||
tx = escrow.transact({'from': ursula}).deposit(1000, 1)
|
||||
chain.wait.for_receipt(tx)
|
||||
tx = escrow.transact({'from': alice}).deposit(500, 4)
|
||||
tx = escrow.transact({'from': alice}).deposit(500, 2)
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Using locked tokens starts from next period
|
||||
|
@ -292,7 +311,7 @@ def test_mining(web3, chain, token, escrow):
|
|||
|
||||
# Ursula can't use method from Miner contract
|
||||
with pytest.raises(TypeError):
|
||||
tx = escrow.transact({'from': ursula}).mint(ursula, 1000, 1000, 1000, 1000)
|
||||
tx = escrow.transact({'from': ursula}).mint(ursula, 1, 1, 1, 1, 1)
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Only Ursula confirm next period
|
||||
|
@ -316,6 +335,8 @@ def test_mining(web3, chain, token, escrow):
|
|||
assert 9517 == token.call().balanceOf(alice)
|
||||
|
||||
# Only Ursula confirm activity for next period
|
||||
tx = escrow.transact({'from': ursula}).switchLock()
|
||||
chain.wait.for_receipt(tx)
|
||||
tx = escrow.transact({'from': ursula}).confirmActivity()
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
|
@ -342,7 +363,9 @@ def test_mining(web3, chain, token, escrow):
|
|||
assert 9133 == token.call().balanceOf(ursula)
|
||||
assert 9517 == token.call().balanceOf(alice)
|
||||
|
||||
# Alice confirm 2 periods and mint tokens
|
||||
# Alice confirm next period and mint tokens
|
||||
tx = escrow.transact({'from': alice}).switchLock()
|
||||
chain.wait.for_receipt(tx)
|
||||
tx = escrow.transact({'from': alice}).confirmActivity()
|
||||
chain.wait.for_receipt(tx)
|
||||
chain.wait.for_block(web3.eth.blockNumber + 100)
|
||||
|
@ -363,29 +386,33 @@ def test_mining(web3, chain, token, escrow):
|
|||
# Ursula can lock some tokens again
|
||||
tx = escrow.transact({'from': ursula}).lock(500, 4)
|
||||
chain.wait.for_receipt(tx)
|
||||
tx = escrow.transact({'from': ursula}).switchLock()
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 500 == escrow.call().getLockedTokens(ursula)
|
||||
assert 500 == escrow.call().calculateLockedTokens(ursula, 2)
|
||||
assert 250 == escrow.call().calculateLockedTokens(ursula, 5)
|
||||
assert 0 == escrow.call().calculateLockedTokens(ursula, 6)
|
||||
assert 500 == escrow.call().calculateLockedTokens(ursula, 1)
|
||||
assert 375 == escrow.call().calculateLockedTokens(ursula, 2)
|
||||
assert 250 == escrow.call().calculateLockedTokens(ursula, 3)
|
||||
assert 0 == escrow.call().calculateLockedTokens(ursula, 5)
|
||||
# And can increase lock
|
||||
tx = escrow.transact({'from': ursula}).lock(100, 0)
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 600 == escrow.call().getLockedTokens(ursula)
|
||||
assert 600 == escrow.call().calculateLockedTokens(ursula, 2)
|
||||
assert 300 == escrow.call().calculateLockedTokens(ursula, 5)
|
||||
assert 0 == escrow.call().calculateLockedTokens(ursula, 6)
|
||||
assert 600 == escrow.call().calculateLockedTokens(ursula, 1)
|
||||
assert 450 == escrow.call().calculateLockedTokens(ursula, 2)
|
||||
assert 0 == escrow.call().calculateLockedTokens(ursula, 5)
|
||||
tx = escrow.transact({'from': ursula}).lock(0, 2)
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 600 == escrow.call().getLockedTokens(ursula)
|
||||
assert 600 == escrow.call().calculateLockedTokens(ursula, 5)
|
||||
assert 300 == escrow.call().calculateLockedTokens(ursula, 7)
|
||||
assert 0 == escrow.call().calculateLockedTokens(ursula, 9)
|
||||
tx = escrow.transact({'from': ursula}).lock(100, 1)
|
||||
assert 600 == escrow.call().calculateLockedTokens(ursula, 1)
|
||||
assert 450 == escrow.call().calculateLockedTokens(ursula, 2)
|
||||
assert 0 == escrow.call().calculateLockedTokens(ursula, 5)
|
||||
tx = escrow.transact({'from': ursula}).deposit(800, 1)
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 700 == escrow.call().getLockedTokens(ursula)
|
||||
assert 700 == escrow.call().calculateLockedTokens(ursula, 6)
|
||||
assert 350 == escrow.call().calculateLockedTokens(ursula, 8)
|
||||
assert 0 == escrow.call().calculateLockedTokens(ursula, 9)
|
||||
assert 1400 == escrow.call().getLockedTokens(ursula)
|
||||
assert 1400 == escrow.call().calculateLockedTokens(ursula, 1)
|
||||
assert 1000 == escrow.call().calculateLockedTokens(ursula, 3)
|
||||
assert 400 == escrow.call().calculateLockedTokens(ursula, 6)
|
||||
assert 0 == escrow.call().calculateLockedTokens(ursula, 8)
|
||||
|
||||
# Alice can withdraw all
|
||||
tx = escrow.transact({'from': alice}).withdrawAll()
|
||||
|
|
|
@ -17,7 +17,7 @@ def wallet_manager(web3, chain, token):
|
|||
creator = web3.eth.accounts[0]
|
||||
# Creator deploys the wallet manager
|
||||
wallet_manager_contract, _ = chain.provider.get_or_deploy_contract(
|
||||
'WalletManager', deploy_args=[token.address, 10 ** 9, 50, 2],
|
||||
'WalletManager', deploy_args=[token.address, 10 ** 9, 50, 2, 1, 1],
|
||||
deploy_transaction={'from': creator, 'gas': 4000000})
|
||||
return wallet_manager_contract
|
||||
|
||||
|
@ -56,7 +56,7 @@ def test_escrow(web3, chain, token, wallet_manager):
|
|||
|
||||
# And can't set lock using wallet
|
||||
with pytest.raises(TransactionFailed):
|
||||
tx = ursula_wallet.transact({'from': ursula}).updateLock(1)
|
||||
tx = ursula_wallet.transact({'from': ursula}).updateLockedTokens(1)
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Ursula and Alice transfer some money to wallets
|
||||
|
@ -90,10 +90,21 @@ def test_escrow(web3, chain, token, wallet_manager):
|
|||
# chain.wait.for_receipt(tx)
|
||||
|
||||
# Ursula and Alice lock some tokens for 100 and 200 blocks
|
||||
tx = wallet_manager.transact({'from': ursula}).lock(1000, 2)
|
||||
tx = wallet_manager.transact({'from': ursula}).lock(1000, 1)
|
||||
chain.wait.for_receipt(tx)
|
||||
tx = wallet_manager.transact({'from': alice}).lock(500, 6)
|
||||
tx = wallet_manager.transact({'from': alice}).lock(500, 2)
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 1000 == ursula_wallet.call().getLockedTokens()
|
||||
assert 1000 == ursula_wallet.call().calculateLockedTokens(1)
|
||||
assert 1000 == ursula_wallet.call().calculateLockedTokens(2)
|
||||
tx = ursula_wallet.transact({'from': ursula}).switchLock()
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 500 == ursula_wallet.call().calculateLockedTokens(2)
|
||||
tx = ursula_wallet.transact({'from': ursula}).switchLock()
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 1000 == ursula_wallet.call().calculateLockedTokens(2)
|
||||
assert 500 == alice_wallet.call().getLockedTokens()
|
||||
assert 500 == alice_wallet.call().calculateLockedTokens(1)
|
||||
|
||||
# Checks locked tokens in next period
|
||||
chain.wait.for_block(web3.eth.blockNumber + 50)
|
||||
|
@ -121,7 +132,10 @@ def test_escrow(web3, chain, token, wallet_manager):
|
|||
# Wait 50 blocks and checks locking
|
||||
chain.wait.for_block(web3.eth.blockNumber + 50)
|
||||
assert 1500 == ursula_wallet.call().getLockedTokens()
|
||||
assert 750 == ursula_wallet.call().calculateLockedTokens(web3.eth.blockNumber // 50, 1500, 1)
|
||||
|
||||
# Ursula starts unlocking
|
||||
tx = ursula_wallet.transact({'from': ursula}).switchLock()
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 750 == ursula_wallet.call().calculateLockedTokens(1)
|
||||
assert 0 == ursula_wallet.call().calculateLockedTokens(2)
|
||||
|
||||
|
@ -159,6 +173,9 @@ def test_escrow(web3, chain, token, wallet_manager):
|
|||
# Locked tokens will be updated in next period
|
||||
assert 750 == ursula_wallet.call().getLockedTokens()
|
||||
assert 600 == ursula_wallet.call().calculateLockedTokens(1)
|
||||
assert 600 == ursula_wallet.call().calculateLockedTokens(2)
|
||||
tx = ursula_wallet.transact({'from': ursula}).switchLock()
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 300 == ursula_wallet.call().calculateLockedTokens(2)
|
||||
assert 0 == ursula_wallet.call().calculateLockedTokens(3)
|
||||
chain.wait.for_block(web3.eth.blockNumber + 50)
|
||||
|
@ -171,8 +188,8 @@ def test_escrow(web3, chain, token, wallet_manager):
|
|||
chain.wait.for_receipt(tx)
|
||||
assert 600 == ursula_wallet.call().getLockedTokens()
|
||||
assert 800 == ursula_wallet.call().calculateLockedTokens(1)
|
||||
assert 800 == ursula_wallet.call().calculateLockedTokens(2)
|
||||
assert 400 == ursula_wallet.call().calculateLockedTokens(3)
|
||||
assert 500 == ursula_wallet.call().calculateLockedTokens(2)
|
||||
assert 200 == ursula_wallet.call().calculateLockedTokens(3)
|
||||
assert 0 == ursula_wallet.call().calculateLockedTokens(4)
|
||||
chain.wait.for_block(web3.eth.blockNumber + 50)
|
||||
assert 800 == ursula_wallet.call().getLockedTokens()
|
||||
|
@ -188,6 +205,8 @@ def test_escrow(web3, chain, token, wallet_manager):
|
|||
chain.wait.for_receipt(tx)
|
||||
tx = wallet_manager.transact({'from': alice}).lock(500, 0)
|
||||
chain.wait.for_receipt(tx)
|
||||
tx = alice_wallet.transact({'from': alice}).switchLock()
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 500 == alice_wallet.call().getLockedTokens()
|
||||
assert 1000 == alice_wallet.call().calculateLockedTokens(1)
|
||||
assert 500 == alice_wallet.call().calculateLockedTokens(2)
|
||||
|
@ -200,16 +219,14 @@ def test_escrow(web3, chain, token, wallet_manager):
|
|||
chain.wait.for_receipt(tx)
|
||||
assert 1000 == alice_wallet.call().getLockedTokens()
|
||||
assert 500 == alice_wallet.call().calculateLockedTokens(1)
|
||||
assert 500 == alice_wallet.call().calculateLockedTokens(2)
|
||||
assert 0 == alice_wallet.call().calculateLockedTokens(3)
|
||||
assert 0 == alice_wallet.call().calculateLockedTokens(2)
|
||||
|
||||
# Alice increases lock by small amount of tokens
|
||||
tx = wallet_manager.transact({'from': alice}).lock(100, 0)
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 600 == alice_wallet.call().calculateLockedTokens(1)
|
||||
assert 600 == alice_wallet.call().calculateLockedTokens(2)
|
||||
assert 100 == alice_wallet.call().calculateLockedTokens(3)
|
||||
assert 0 == alice_wallet.call().calculateLockedTokens(4)
|
||||
assert 100 == alice_wallet.call().calculateLockedTokens(2)
|
||||
assert 0 == alice_wallet.call().calculateLockedTokens(3)
|
||||
|
||||
# Ursula can't destroy contract
|
||||
with pytest.raises(TransactionFailed):
|
||||
|
@ -259,6 +276,11 @@ def test_locked_distribution(web3, chain, token, wallet_manager):
|
|||
n_locked = wallet_manager.call().getAllLockedTokens()
|
||||
assert n_locked > 0
|
||||
|
||||
# And confirm activity
|
||||
for miner in miners:
|
||||
tx = wallet_manager.transact({'from': miner}).confirmActivity()
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
address_stop, shift = wallet_manager.call().findCumSum(NULL_ADDR, n_locked // 3, 1)
|
||||
assert miners[0].lower() == address_stop.lower()
|
||||
assert n_locked // 3 == shift
|
||||
|
@ -272,7 +294,10 @@ def test_locked_distribution(web3, chain, token, wallet_manager):
|
|||
assert miners[2].lower() == address_stop.lower()
|
||||
assert 1 == shift
|
||||
|
||||
address_stop, shift = wallet_manager.call().findCumSum(NULL_ADDR, 1, 12)
|
||||
address_stop, shift = wallet_manager.call().findCumSum(NULL_ADDR, 1, 10)
|
||||
assert NULL_ADDR != address_stop.lower()
|
||||
assert 0 != shift
|
||||
address_stop, shift = wallet_manager.call().findCumSum(NULL_ADDR, 1, 11)
|
||||
assert NULL_ADDR == address_stop.lower()
|
||||
assert 0 == shift
|
||||
|
||||
|
@ -318,10 +343,10 @@ def test_mining(web3, chain, token, wallet_manager):
|
|||
tx = wallet_manager.transact({'from': ursula}).mint()
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Ursula and Alice lock some tokens for 100 and 200 blocks
|
||||
tx = wallet_manager.transact({'from': ursula}).lock(1000, 2)
|
||||
# Ursula and Alice lock some tokens
|
||||
tx = wallet_manager.transact({'from': ursula}).lock(1000, 1)
|
||||
chain.wait.for_receipt(tx)
|
||||
tx = wallet_manager.transact({'from': alice}).lock(500, 4)
|
||||
tx = wallet_manager.transact({'from': alice}).lock(500, 2)
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Using locked tokens starts from next period
|
||||
|
@ -329,7 +354,7 @@ def test_mining(web3, chain, token, wallet_manager):
|
|||
|
||||
# Ursula can't use method from Miner contract
|
||||
with pytest.raises(TypeError):
|
||||
tx = wallet_manager.transact({'from': ursula}).mint(ursula, 1000, 1000, 1000, 1000)
|
||||
tx = wallet_manager.transact({'from': ursula}).mint(ursula, 1, 1, 1, 1, 1)
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Only Ursula confirm next period
|
||||
|
@ -353,6 +378,8 @@ def test_mining(web3, chain, token, wallet_manager):
|
|||
assert 517 == token.call().balanceOf(alice_wallet.address)
|
||||
|
||||
# Only Ursula confirm activity for next period
|
||||
tx = ursula_wallet.transact({'from': ursula}).switchLock()
|
||||
chain.wait.for_receipt(tx)
|
||||
tx = wallet_manager.transact({'from': ursula}).confirmActivity()
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
|
@ -379,7 +406,9 @@ def test_mining(web3, chain, token, wallet_manager):
|
|||
assert 1133 == token.call().balanceOf(ursula_wallet.address)
|
||||
assert 517 == token.call().balanceOf(alice_wallet.address)
|
||||
|
||||
# Alice confirm 2 periods and mint tokens
|
||||
# Alice confirm next period and mint tokens
|
||||
tx = alice_wallet.transact({'from': alice}).switchLock()
|
||||
chain.wait.for_receipt(tx)
|
||||
tx = wallet_manager.transact({'from': alice}).confirmActivity()
|
||||
chain.wait.for_receipt(tx)
|
||||
chain.wait.for_block(web3.eth.blockNumber + 100)
|
||||
|
@ -408,29 +437,35 @@ def test_mining(web3, chain, token, wallet_manager):
|
|||
# Ursula can lock some tokens again
|
||||
tx = wallet_manager.transact({'from': ursula}).lock(500, 4)
|
||||
chain.wait.for_receipt(tx)
|
||||
tx = ursula_wallet.transact({'from': ursula}).switchLock()
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 500 == ursula_wallet.call().getLockedTokens()
|
||||
assert 500 == ursula_wallet.call().calculateLockedTokens(2)
|
||||
assert 250 == ursula_wallet.call().calculateLockedTokens(5)
|
||||
assert 0 == ursula_wallet.call().calculateLockedTokens(6)
|
||||
assert 500 == ursula_wallet.call().calculateLockedTokens(1)
|
||||
assert 375 == ursula_wallet.call().calculateLockedTokens(2)
|
||||
assert 250 == ursula_wallet.call().calculateLockedTokens(3)
|
||||
assert 0 == ursula_wallet.call().calculateLockedTokens(5)
|
||||
# And can increase lock
|
||||
tx = wallet_manager.transact({'from': ursula}).lock(100, 0)
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 600 == ursula_wallet.call().getLockedTokens()
|
||||
assert 600 == ursula_wallet.call().calculateLockedTokens(2)
|
||||
assert 300 == ursula_wallet.call().calculateLockedTokens(5)
|
||||
assert 0 == ursula_wallet.call().calculateLockedTokens(6)
|
||||
assert 600 == ursula_wallet.call().calculateLockedTokens(1)
|
||||
assert 450 == ursula_wallet.call().calculateLockedTokens(2)
|
||||
assert 0 == ursula_wallet.call().calculateLockedTokens(5)
|
||||
tx = wallet_manager.transact({'from': ursula}).lock(0, 2)
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 600 == ursula_wallet.call().getLockedTokens()
|
||||
assert 600 == ursula_wallet.call().calculateLockedTokens(5)
|
||||
assert 300 == ursula_wallet.call().calculateLockedTokens(7)
|
||||
assert 0 == ursula_wallet.call().calculateLockedTokens(8)
|
||||
tx = wallet_manager.transact({'from': ursula}).lock(100, 1)
|
||||
assert 600 == ursula_wallet.call().calculateLockedTokens(1)
|
||||
assert 450 == ursula_wallet.call().calculateLockedTokens(2)
|
||||
assert 0 == ursula_wallet.call().calculateLockedTokens(5)
|
||||
tx = token.transact({'from': ursula}).transfer(ursula_wallet.address, 800)
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 700 == ursula_wallet.call().getLockedTokens()
|
||||
assert 700 == ursula_wallet.call().calculateLockedTokens(6)
|
||||
assert 350 == ursula_wallet.call().calculateLockedTokens(8)
|
||||
assert 0 == ursula_wallet.call().calculateLockedTokens(9)
|
||||
tx = wallet_manager.transact({'from': ursula}).lock(800, 1)
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 1400 == ursula_wallet.call().getLockedTokens()
|
||||
assert 1400 == ursula_wallet.call().calculateLockedTokens(1)
|
||||
assert 1000 == ursula_wallet.call().calculateLockedTokens(3)
|
||||
assert 400 == ursula_wallet.call().calculateLockedTokens(6)
|
||||
assert 0 == ursula_wallet.call().calculateLockedTokens(8)
|
||||
|
||||
# Alice can withdraw all
|
||||
# TODO complete method
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
import math
|
||||
|
||||
|
||||
def test_math(chain):
|
||||
"""
|
||||
These are tests for math library
|
||||
"""
|
||||
|
||||
math_contract, _ = chain.provider.get_or_deploy_contract('ExponentMathTest')
|
||||
|
||||
assert int(math.exp(2)) == int(math_contract.call().exp(2, 100, 1, 30) / 100)
|
||||
assert int(math.exp(2.5)) == int(math_contract.call().exp(25, 100, 10, 30) / 100)
|
||||
assert int(math.exp(10)) == int(math_contract.call().exp(10, 100, 1, 30) / 100)
|
||||
|
||||
assert int(1000 * (1 - math.exp(-2))) == \
|
||||
math_contract.call().exponentialFunction(2, 1000, 1, 200, 30)
|
||||
assert int(1000 * (1 - math.exp(-2.5))) == \
|
||||
math_contract.call().exponentialFunction(25, 1000, 10, 200, 30)
|
||||
assert int(10 ** 9 * (1 - math.exp(-0.0000001))) == \
|
||||
math_contract.call().exponentialFunction(10, 10 ** 9, 10 ** 8, 10 ** 8, 30)
|
||||
# assert int(10 ** 9 * (1 - math.exp(-0.00000001))) == \
|
||||
# math_contract.call().exponentialFunction(1, 10 ** 9, 10 ** 8, 10 ** 8, 30)
|
||||
# assert int(10 ** 9 * (1 - math.exp(-10))) == \
|
||||
# math_contract.call().exponentialFunction(10 ** 9, 10 ** 9, 10 ** 8, 10 ** 8, 30)
|
|
@ -18,7 +18,7 @@ def test_miner(web3, chain, token):
|
|||
|
||||
# Creator deploys the miner
|
||||
miner, _ = chain.provider.get_or_deploy_contract(
|
||||
'MinerTest', deploy_args=[token.address, 10 ** 41],
|
||||
'MinerTest', deploy_args=[token.address, 10 ** 41, 1, 1],
|
||||
deploy_transaction={'from': creator})
|
||||
|
||||
# Give rights for mining
|
||||
|
@ -40,13 +40,13 @@ def test_miner(web3, chain, token):
|
|||
# assert miner.call().lastMintedPoint() == 0
|
||||
|
||||
# Mint some tokens
|
||||
tx = miner.transact().testMint(ursula, 1000, 2000, 100, 0)
|
||||
tx = miner.transact().testMint(ursula, 1000, 2000, 100, 0, 0)
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 10 == token.call().balanceOf(ursula)
|
||||
assert 10 ** 30 + 10 == token.call().totalSupply()
|
||||
|
||||
# Mint more tokens
|
||||
tx = miner.transact().testMint(ursula, 500, 500, 200, 0)
|
||||
tx = miner.transact().testMint(ursula, 500, 500, 200, 0, 0)
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 50 == token.call().balanceOf(ursula)
|
||||
assert 10 ** 30 + 50 == token.call().totalSupply()
|
||||
|
|
|
@ -55,7 +55,7 @@ def test_mine_withdraw(chain):
|
|||
|
||||
# Create a random set of miners (we have 9 in total)
|
||||
for u in chain.web3.eth.accounts[1:]:
|
||||
ursula.lock((10 + random.randrange(9000)) * M, 2, u)
|
||||
ursula.lock((10 + random.randrange(9000)) * M, 1, u)
|
||||
|
||||
chain.wait.for_block(chain.web3.eth.blockNumber + 100)
|
||||
|
||||
|
|
Loading…
Reference in New Issue