mirror of https://github.com/nucypher/nucypher.git
[KMS-ETH]- Changed period in blocks to period in hours, small changes in mining reward
parent
2c8263f941
commit
7d64ec8f79
|
@ -4,15 +4,15 @@ from nkms_eth import blockchain
|
|||
from nkms_eth import token
|
||||
|
||||
ESCROW_NAME = 'Escrow'
|
||||
BLOCKS_PER_PERIOD = 50 # 6100
|
||||
HOURS_PER_PERIOD = 1 # 24
|
||||
MIN_RELEASE_PERIODS = 1 # 30
|
||||
MAX_AWARDED_PERIODS = 365
|
||||
MINING_COEFF = [
|
||||
BLOCKS_PER_PERIOD * MAX_AWARDED_PERIODS * 10 ** 9,
|
||||
BLOCKS_PER_PERIOD,
|
||||
MIN_RELEASE_PERIODS,
|
||||
BLOCKS_PER_PERIOD * MAX_AWARDED_PERIODS,
|
||||
MAX_AWARDED_PERIODS
|
||||
HOURS_PER_PERIOD,
|
||||
2 * 10 ** 7,
|
||||
MAX_AWARDED_PERIODS,
|
||||
MAX_AWARDED_PERIODS,
|
||||
MIN_RELEASE_PERIODS
|
||||
]
|
||||
NULL_ADDR = '0x' + '0' * 40
|
||||
|
||||
|
|
|
@ -43,36 +43,38 @@ contract Escrow is Miner, Ownable {
|
|||
uint256 constant MAX_PERIODS = 100;
|
||||
uint256 constant MAX_OWNERS = 50000;
|
||||
|
||||
NuCypherKMSToken token;
|
||||
mapping (address => TokenInfo) public tokenInfo;
|
||||
LinkedList.Data tokenOwners;
|
||||
|
||||
uint256 public blocksPerPeriod;
|
||||
mapping (uint256 => PeriodInfo) public lockedPerPeriod;
|
||||
uint256 public minReleasePeriods;
|
||||
|
||||
/**
|
||||
* @notice The Escrow constructor sets address of token contract and coefficients for mining
|
||||
* @param _token Token contract
|
||||
* @param _hoursPerPeriod Size of period in hours
|
||||
* @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 _lockedPeriodsCoefficient Locked blocks coefficient
|
||||
* @param _awardedPeriods Max periods that will be additionally awarded
|
||||
**/
|
||||
function Escrow(
|
||||
NuCypherKMSToken _token,
|
||||
uint256 _hoursPerPeriod,
|
||||
uint256 _miningCoefficient,
|
||||
uint256 _blocksPerPeriod,
|
||||
uint256 _minReleasePeriods,
|
||||
uint256 _lockedBlocksCoefficient,
|
||||
uint256 _awardedPeriods
|
||||
uint256 _lockedPeriodsCoefficient,
|
||||
uint256 _awardedPeriods,
|
||||
uint256 _minReleasePeriods
|
||||
)
|
||||
Miner(_token, _miningCoefficient, _lockedBlocksCoefficient, _awardedPeriods * _blocksPerPeriod)
|
||||
Miner(
|
||||
_token,
|
||||
_hoursPerPeriod,
|
||||
_miningCoefficient,
|
||||
_lockedPeriodsCoefficient,
|
||||
_awardedPeriods
|
||||
)
|
||||
{
|
||||
require(_blocksPerPeriod != 0 && _minReleasePeriods != 0);
|
||||
token = _token;
|
||||
blocksPerPeriod = _blocksPerPeriod;
|
||||
require(_minReleasePeriods != 0);
|
||||
minReleasePeriods = _minReleasePeriods;
|
||||
}
|
||||
|
||||
|
@ -83,7 +85,7 @@ contract Escrow is Miner, Ownable {
|
|||
function getLockedTokens(address _owner)
|
||||
public constant returns (uint256)
|
||||
{
|
||||
var currentPeriod = block.number.div(blocksPerPeriod);
|
||||
var currentPeriod = getCurrentPeriod();
|
||||
var info = tokenInfo[_owner];
|
||||
var numberConfirmedPeriods = info.numberConfirmedPeriods;
|
||||
|
||||
|
@ -121,8 +123,7 @@ contract Escrow is Miner, Ownable {
|
|||
function getAllLockedTokens()
|
||||
public constant returns (uint256)
|
||||
{
|
||||
var period = block.number.div(blocksPerPeriod);
|
||||
return lockedPerPeriod[period].totalLockedValue;
|
||||
return lockedPerPeriod[getCurrentPeriod()].totalLockedValue;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -160,7 +161,7 @@ contract Escrow is Miner, Ownable {
|
|||
public constant returns (uint256)
|
||||
{
|
||||
require(_periods > 0);
|
||||
var currentPeriod = block.number.div(blocksPerPeriod);
|
||||
var currentPeriod = getCurrentPeriod();
|
||||
var nextPeriod = currentPeriod.add(_periods);
|
||||
|
||||
var info = tokenInfo[_owner];
|
||||
|
@ -225,7 +226,7 @@ contract Escrow is Miner, Ownable {
|
|||
require(_value <= token.balanceOf(address(this)) &&
|
||||
_value <= info.value.sub(lockedTokens));
|
||||
|
||||
var currentPeriod = block.number.div(blocksPerPeriod);
|
||||
var currentPeriod = getCurrentPeriod();
|
||||
if (lockedTokens == 0) {
|
||||
info.lockedValue = _value;
|
||||
info.maxReleasePeriods = Math.max256(_periods, minReleasePeriods);
|
||||
|
@ -305,7 +306,7 @@ contract Escrow is Miner, Ownable {
|
|||
function confirmActivity(uint256 _lockedValue) internal {
|
||||
require(_lockedValue > 0);
|
||||
var info = tokenInfo[msg.sender];
|
||||
var nextPeriod = block.number.div(blocksPerPeriod) + 1;
|
||||
var nextPeriod = getCurrentPeriod() + 1;
|
||||
|
||||
var numberConfirmedPeriods = info.numberConfirmedPeriods;
|
||||
if (numberConfirmedPeriods > 0 &&
|
||||
|
@ -335,14 +336,14 @@ contract Escrow is Miner, Ownable {
|
|||
**/
|
||||
function confirmActivity() external {
|
||||
var info = tokenInfo[msg.sender];
|
||||
var nextPeriod = block.number.div(blocksPerPeriod) + 1;
|
||||
var currentPeriod = getCurrentPeriod();
|
||||
var nextPeriod = currentPeriod + 1;
|
||||
|
||||
if (info.numberConfirmedPeriods > 0 &&
|
||||
info.confirmedPeriods[info.numberConfirmedPeriods - 1].period >= nextPeriod) {
|
||||
return;
|
||||
}
|
||||
|
||||
var currentPeriod = nextPeriod - 1;
|
||||
var lockedTokens = calculateLockedTokens(
|
||||
msg.sender, false, getLockedTokens(msg.sender), 1);
|
||||
confirmActivity(lockedTokens);
|
||||
|
@ -352,18 +353,17 @@ contract Escrow is Miner, Ownable {
|
|||
* @notice Mint tokens for sender for previous periods if he locked his tokens and confirmed activity
|
||||
**/
|
||||
function mint() external {
|
||||
var previousPeriod = block.number.div(blocksPerPeriod).sub(1);
|
||||
var previousPeriod = getCurrentPeriod().sub(1);
|
||||
var info = tokenInfo[msg.sender];
|
||||
var numberPeriodsForMinting = info.numberConfirmedPeriods;
|
||||
require(numberPeriodsForMinting > 0 &&
|
||||
info.confirmedPeriods[0].period <= previousPeriod);
|
||||
|
||||
var currentLockedValue = getLockedTokens(msg.sender);
|
||||
var allLockedBlocks = calculateLockedPeriods(
|
||||
var allLockedPeriods = calculateLockedPeriods(
|
||||
msg.sender,
|
||||
info.confirmedPeriods[numberPeriodsForMinting - 1].lockedValue)
|
||||
.add(numberPeriodsForMinting)
|
||||
.mul(blocksPerPeriod);
|
||||
.add(numberPeriodsForMinting);
|
||||
var decimals = info.decimals;
|
||||
|
||||
if (info.confirmedPeriods[numberPeriodsForMinting - 1].period > previousPeriod) {
|
||||
|
@ -376,13 +376,13 @@ contract Escrow is Miner, Ownable {
|
|||
for(uint i = 0; i < numberPeriodsForMinting; ++i) {
|
||||
var period = info.confirmedPeriods[i].period;
|
||||
var lockedValue = info.confirmedPeriods[i].lockedValue;
|
||||
allLockedBlocks = allLockedBlocks.sub(blocksPerPeriod);
|
||||
allLockedPeriods--;
|
||||
(, decimals) = mint(
|
||||
msg.sender,
|
||||
previousPeriod,
|
||||
lockedValue,
|
||||
lockedPerPeriod[period].totalLockedValue,
|
||||
blocksPerPeriod,
|
||||
allLockedBlocks,
|
||||
allLockedPeriods,
|
||||
decimals);
|
||||
if (lockedPerPeriod[period].numberOwnersToBeRewarded > 1) {
|
||||
lockedPerPeriod[period].numberOwnersToBeRewarded--;
|
||||
|
@ -424,7 +424,7 @@ contract Escrow is Miner, Ownable {
|
|||
public constant returns (address stop, uint256 shift)
|
||||
{
|
||||
require(_periods > 0);
|
||||
var currentPeriod = block.number.div(blocksPerPeriod);
|
||||
var currentPeriod = getCurrentPeriod();
|
||||
uint256 distance = 0;
|
||||
var current = _start;
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
pragma solidity ^0.4.0;
|
||||
|
||||
|
||||
import "./MineableToken.sol";
|
||||
import "./NuCypherKMSToken.sol";
|
||||
import "./zeppelin/math/SafeMath.sol";
|
||||
|
||||
|
||||
|
@ -11,71 +11,90 @@ import "./zeppelin/math/SafeMath.sol";
|
|||
contract Miner {
|
||||
using SafeMath for uint256;
|
||||
|
||||
MineableToken token;
|
||||
NuCypherKMSToken token;
|
||||
uint256 public miningCoefficient;
|
||||
uint256 public lockedBlocksCoefficient;
|
||||
uint256 public awardedLockedBlocks;
|
||||
uint256 public secondsPerPeriod;
|
||||
uint256 public lockedPeriodsCoefficient;
|
||||
uint256 public awardedPeriods;
|
||||
|
||||
uint256 public lastMintedPeriod;
|
||||
uint256 public lastTotalSupply;
|
||||
|
||||
/**
|
||||
* @notice The Miner constructor sets address of token contract and coefficients for mining
|
||||
* @dev Formula for mining
|
||||
(futureSupply - currentSupply) * (lockedBlocks * lockedValue / totalLockedValue) * (k1 + allLockedBlocks) / k2
|
||||
if allLockedBlocks > awardedLockedBlocks then allLockedBlocks = awardedLockedBlocks
|
||||
* @dev Formula for mining in one period
|
||||
(futureSupply - currentSupply) * (lockedValue / totalLockedValue) * (k1 + allLockedPeriods) / k2
|
||||
if allLockedPeriods > awardedPeriods then allLockedPeriods = awardedPeriods
|
||||
* @param _token Token contract
|
||||
* @param _hoursPerPeriod Size of period in hours
|
||||
* @param _miningCoefficient Mining coefficient (k2)
|
||||
* @param _lockedBlocksCoefficient Locked blocks coefficient (k1)
|
||||
* @param _awardedLockedBlocks Max blocks that will be additionally awarded
|
||||
* @param _lockedPeriodsCoefficient Locked blocks coefficient (k1)
|
||||
* @param _awardedPeriods Max periods that will be additionally awarded
|
||||
**/
|
||||
function Miner(
|
||||
MineableToken _token,
|
||||
NuCypherKMSToken _token,
|
||||
uint256 _hoursPerPeriod,
|
||||
uint256 _miningCoefficient,
|
||||
uint256 _lockedBlocksCoefficient,
|
||||
uint256 _awardedLockedBlocks
|
||||
uint256 _lockedPeriodsCoefficient,
|
||||
uint256 _awardedPeriods
|
||||
) {
|
||||
require(address(_token) != 0x0 &&
|
||||
_miningCoefficient != 0 &&
|
||||
_lockedBlocksCoefficient != 0 &&
|
||||
_awardedLockedBlocks != 0);
|
||||
_hoursPerPeriod != 0 &&
|
||||
_lockedPeriodsCoefficient != 0 &&
|
||||
_awardedPeriods != 0);
|
||||
token = _token;
|
||||
miningCoefficient = _miningCoefficient;
|
||||
lockedBlocksCoefficient = _lockedBlocksCoefficient;
|
||||
awardedLockedBlocks = _awardedLockedBlocks;
|
||||
secondsPerPeriod = _hoursPerPeriod.mul(1 hours);
|
||||
lockedPeriodsCoefficient = _lockedPeriodsCoefficient;
|
||||
awardedPeriods = _awardedPeriods;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Function to mint tokens for sender
|
||||
* @return Number of current period
|
||||
**/
|
||||
function getCurrentPeriod() public constant returns (uint256) {
|
||||
return block.timestamp / secondsPerPeriod;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Function to mint tokens for sender for one period.
|
||||
* @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 _currentLockedBlocks The current amount of blocks during which tokens were locked.
|
||||
* @param _allLockedBlocks The max amount of blocks during which tokens were locked.
|
||||
* @param _period Period number.
|
||||
* @param _lockedValue The amount of tokens that were locked by user in specified period.
|
||||
* @param _totalLockedValue The amount of tokens that were locked by all users in specified period.
|
||||
* @param _allLockedPeriods The max amount of periods during which tokens will be locked after specified period.
|
||||
* @param _decimals The amount of locked tokens and blocks in decimals.
|
||||
* @return Amount of minted tokens.
|
||||
*/
|
||||
function mint(
|
||||
address _to,
|
||||
uint256 _period,
|
||||
uint256 _lockedValue,
|
||||
uint256 _totalLockedValue,
|
||||
uint256 _currentLockedBlocks,
|
||||
uint256 _allLockedBlocks,
|
||||
uint256 _allLockedPeriods,
|
||||
uint256 _decimals
|
||||
)
|
||||
internal returns (uint256 amount, uint256 decimals)
|
||||
{
|
||||
//futureSupply * currentLockedBlocks * lockedValue * (k1 + allLockedBlocks) / (totalLockedValue * k2) -
|
||||
//currentSupply * currentLockedBlocks * lockedValue * (k1 + allLockedBlocks) / (totalLockedValue * k2)
|
||||
var allLockedBlocks = (_allLockedBlocks <= awardedLockedBlocks ?
|
||||
_allLockedBlocks : awardedLockedBlocks).add(lockedBlocksCoefficient);
|
||||
if (_period > lastMintedPeriod) {
|
||||
lastTotalSupply = token.totalSupply();
|
||||
lastMintedPeriod = _period;
|
||||
}
|
||||
|
||||
//futureSupply * lockedValue * (k1 + allLockedPeriods) / (totalLockedValue * k2) -
|
||||
//currentSupply * lockedValue * (k1 + allLockedPeriods) / (totalLockedValue * k2)
|
||||
var allLockedPeriods = (_allLockedPeriods <= awardedPeriods ?
|
||||
_allLockedPeriods : awardedPeriods)
|
||||
.add(lockedPeriodsCoefficient);
|
||||
var denominator = _totalLockedValue.mul(miningCoefficient);
|
||||
var maxValue = token.futureSupply()
|
||||
.mul(_currentLockedBlocks)
|
||||
.mul(_lockedValue)
|
||||
.mul(allLockedBlocks)
|
||||
.mul(allLockedPeriods)
|
||||
.div(denominator);
|
||||
var value = token.totalSupply()
|
||||
.mul(_currentLockedBlocks)
|
||||
var value = lastTotalSupply
|
||||
.mul(_lockedValue)
|
||||
.mul(allLockedBlocks)
|
||||
.mul(allLockedPeriods)
|
||||
.div(denominator);
|
||||
amount = maxValue.sub(value);
|
||||
token.mint(_to, amount);
|
||||
|
|
|
@ -1,277 +0,0 @@
|
|||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
import "./NuCypherKMSToken.sol";
|
||||
import "./zeppelin/token/SafeERC20.sol";
|
||||
import "./zeppelin/math/SafeMath.sol";
|
||||
import "./zeppelin/math/Math.sol";
|
||||
import "./zeppelin/ownership/Ownable.sol";
|
||||
import "./lib/AdditionalMath.sol";
|
||||
|
||||
|
||||
/**
|
||||
* @notice Contract holds and locks client tokens.
|
||||
**/
|
||||
contract Wallet is Ownable {
|
||||
using SafeERC20 for NuCypherKMSToken;
|
||||
using SafeMath for uint256;
|
||||
using AdditionalMath for uint256;
|
||||
|
||||
struct PeriodInfo {
|
||||
uint256 period;
|
||||
uint256 lockedValue;
|
||||
}
|
||||
|
||||
address public manager;
|
||||
// TODO maybe get from manager
|
||||
NuCypherKMSToken public token;
|
||||
uint256 public blocksPerPeriod;
|
||||
uint256 public minReleasePeriods;
|
||||
|
||||
uint256 public lockedValue;
|
||||
uint256 public decimals;
|
||||
bool public release;
|
||||
uint256 public maxReleasePeriods;
|
||||
uint256 public releaseRate;
|
||||
PeriodInfo[] public confirmedPeriods;
|
||||
uint256 public numberConfirmedPeriods;
|
||||
|
||||
/**
|
||||
* @dev Throws if called by any account other than the manager.
|
||||
**/
|
||||
modifier onlyManager() {
|
||||
require(msg.sender == manager);
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Wallet constructor set token contract
|
||||
* @param _token Token contract
|
||||
* @param _blocksPerPeriod Size of one period in blocks
|
||||
* @param _minReleasePeriods Min amount of periods during which tokens will be released
|
||||
**/
|
||||
function Wallet(NuCypherKMSToken _token,
|
||||
uint256 _blocksPerPeriod,
|
||||
uint256 _minReleasePeriods
|
||||
) {
|
||||
token = _token;
|
||||
manager = msg.sender;
|
||||
blocksPerPeriod = _blocksPerPeriod;
|
||||
minReleasePeriods = _minReleasePeriods;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Get locked tokens value in current period
|
||||
**/
|
||||
function getLockedTokens()
|
||||
public constant returns (uint256)
|
||||
{
|
||||
var period = block.number.div(blocksPerPeriod);
|
||||
|
||||
// no confirmed periods, so current period may be release period
|
||||
if (numberConfirmedPeriods == 0) {
|
||||
var lockedValueToCheck = lockedValue;
|
||||
} else {
|
||||
var i = numberConfirmedPeriods - 1;
|
||||
var confirmedPeriod = confirmedPeriods[i].period;
|
||||
// last confirmed period is current period
|
||||
if (confirmedPeriod == period) {
|
||||
return confirmedPeriods[i].lockedValue;
|
||||
// last confirmed period is previous periods, so current period may be release period
|
||||
} else if (confirmedPeriod < period) {
|
||||
lockedValueToCheck = confirmedPeriods[i].lockedValue;
|
||||
// penultimate confirmed period is previous or current period, so get its lockedValue
|
||||
} else if (numberConfirmedPeriods > 1) {
|
||||
return confirmedPeriods[numberConfirmedPeriods - 2].lockedValue;
|
||||
// no previous periods, so return saved lockedValue
|
||||
} else {
|
||||
return lockedValue;
|
||||
}
|
||||
}
|
||||
// checks if owner can mine more tokens (before or after release period)
|
||||
if (calculateLockedTokens(false, lockedValueToCheck, 1) == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
return lockedValueToCheck;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 that need to calculate
|
||||
* @return Calculated locked tokens in next period
|
||||
**/
|
||||
function calculateLockedTokens(
|
||||
bool _forceRelease,
|
||||
uint256 _lockedTokens,
|
||||
uint256 _periods
|
||||
)
|
||||
public constant returns (uint256)
|
||||
{
|
||||
if ((_forceRelease || release) && _periods != 0) {
|
||||
var unlockedTokens = _periods.mul(releaseRate);
|
||||
return unlockedTokens <= _lockedTokens ? _lockedTokens.sub(unlockedTokens) : 0;
|
||||
} else {
|
||||
return _lockedTokens;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Calculate locked tokens value in next period
|
||||
* @param _periods Number of periods after current that need to calculate
|
||||
* @return Calculated locked tokens in next period
|
||||
**/
|
||||
function calculateLockedTokens(uint256 _periods)
|
||||
public constant returns (uint256)
|
||||
{
|
||||
require(_periods > 0);
|
||||
var currentPeriod = block.number.div(blocksPerPeriod);
|
||||
var nextPeriod = currentPeriod.add(_periods);
|
||||
|
||||
if (numberConfirmedPeriods > 0 &&
|
||||
confirmedPeriods[numberConfirmedPeriods - 1].period >= currentPeriod) {
|
||||
var lockedTokens = confirmedPeriods[numberConfirmedPeriods - 1].lockedValue;
|
||||
var period = confirmedPeriods[numberConfirmedPeriods - 1].period;
|
||||
} else {
|
||||
lockedTokens = getLockedTokens();
|
||||
period = currentPeriod;
|
||||
}
|
||||
var periods = nextPeriod.sub(period);
|
||||
|
||||
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.divCeil(releaseRate).sub(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Lock some tokens
|
||||
* @param _value Amount of tokens which should lock
|
||||
* @param _periods Amount of periods during which tokens will be locked
|
||||
**/
|
||||
function lock(uint256 _value, uint256 _periods) onlyManager public {
|
||||
require(_value != 0 || _periods != 0);
|
||||
|
||||
var lockedTokens = calculateLockedTokens(1);
|
||||
require(_value <= token.balanceOf(address(this)).sub(lockedTokens));
|
||||
|
||||
var currentPeriod = block.number.div(blocksPerPeriod);
|
||||
if (lockedTokens == 0) {
|
||||
lockedValue = _value;
|
||||
maxReleasePeriods = Math.max256(_periods, minReleasePeriods);
|
||||
releaseRate = Math.max256(_value.divCeil(maxReleasePeriods), 1);
|
||||
release = false;
|
||||
} else {
|
||||
lockedValue = lockedTokens.add(_value);
|
||||
maxReleasePeriods = maxReleasePeriods.add(_periods);
|
||||
releaseRate = Math.max256(
|
||||
lockedValue.divCeil(maxReleasePeriods), releaseRate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Sets locked tokens
|
||||
* @param _value Amount of tokens which should lock
|
||||
**/
|
||||
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 {
|
||||
// TODO optimize
|
||||
var lockedTokens = Math.max256(calculateLockedTokens(1),
|
||||
getLockedTokens());
|
||||
require(_value <= token.balanceOf(address(this)).sub(lockedTokens));
|
||||
token.safeTransfer(msg.sender, _value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Terminate contract and refund to owner
|
||||
* @dev The called token contracts could try to re-enter this contract.
|
||||
Only supply token contracts you trust.
|
||||
**/
|
||||
function destroy() onlyManager public {
|
||||
token.safeTransfer(owner, token.balanceOf(address(this)));
|
||||
selfdestruct(owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Set minted decimals
|
||||
**/
|
||||
function setDecimals(uint256 _decimals) onlyManager {
|
||||
decimals = _decimals;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Get confirmed period
|
||||
* @param _index Index of period
|
||||
**/
|
||||
function getConfirmedPeriod(uint256 _index) public constant returns (uint256) {
|
||||
return confirmedPeriods[_index].period;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Get locked value for confirmed period
|
||||
* @param _index Index of period
|
||||
**/
|
||||
function getConfirmedPeriodValue(uint256 _index) public constant returns (uint256) {
|
||||
return confirmedPeriods[_index].lockedValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Set locked value for confirmed period
|
||||
* @param _index Index of period
|
||||
* @param _value Locked tokens
|
||||
**/
|
||||
function setConfirmedPeriodValue(uint256 _index, uint256 _value) onlyManager {
|
||||
confirmedPeriods[_index].lockedValue = _value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Add period as confirmed
|
||||
* @param _period Period to add
|
||||
* @param _lockedValue Locked tokens for period
|
||||
**/
|
||||
function addConfirmedPeriod(uint256 _period, uint256 _lockedValue) onlyManager {
|
||||
if (numberConfirmedPeriods < confirmedPeriods.length) {
|
||||
confirmedPeriods[numberConfirmedPeriods].period = _period;
|
||||
confirmedPeriods[numberConfirmedPeriods].lockedValue = _lockedValue;
|
||||
} else {
|
||||
confirmedPeriods.push(PeriodInfo(_period, _lockedValue));
|
||||
}
|
||||
numberConfirmedPeriods++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Clear periods array
|
||||
* @param _number Number of periods to delete
|
||||
**/
|
||||
function deleteConfirmedPeriods(uint256 _number) onlyManager {
|
||||
var newNumberConfirmedPeriods = numberConfirmedPeriods - _number;
|
||||
for (uint256 i = 0; i < newNumberConfirmedPeriods; i++) {
|
||||
confirmedPeriods[i] = confirmedPeriods[_number + i];
|
||||
}
|
||||
numberConfirmedPeriods = newNumberConfirmedPeriods;
|
||||
}
|
||||
}
|
|
@ -1,274 +0,0 @@
|
|||
pragma solidity ^0.4.8;
|
||||
|
||||
|
||||
import "./zeppelin/token/SafeERC20.sol";
|
||||
import "./zeppelin/math/Math.sol";
|
||||
import "./zeppelin/ownership/Ownable.sol";
|
||||
import "./NuCypherKMSToken.sol";
|
||||
import "./lib/LinkedList.sol";
|
||||
import "./Miner.sol";
|
||||
import "./Wallet.sol";
|
||||
|
||||
|
||||
/**
|
||||
* @notice Contract creates wallets and manage mining for that wallets
|
||||
**/
|
||||
contract WalletManager is Miner, Ownable {
|
||||
using LinkedList for LinkedList.Data;
|
||||
using SafeERC20 for NuCypherKMSToken;
|
||||
|
||||
struct PeriodInfo {
|
||||
uint256 totalLockedValue;
|
||||
uint256 numberOwnersToBeRewarded;
|
||||
}
|
||||
|
||||
uint256 constant MAX_PERIODS = 100;
|
||||
uint256 constant MAX_OWNERS = 50000;
|
||||
|
||||
NuCypherKMSToken token;
|
||||
mapping (address => Wallet) public wallets;
|
||||
LinkedList.Data walletOwners;
|
||||
|
||||
uint256 public blocksPerPeriod;
|
||||
mapping (uint256 => PeriodInfo) public lockedPerPeriod;
|
||||
uint256 public minReleasePeriods;
|
||||
|
||||
/**
|
||||
* @notice The WalletManager constructor sets address of token contract and coefficients for mining
|
||||
* @param _token Token contract
|
||||
* @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 _lockedBlocksCoefficient,
|
||||
uint256 _awardedPeriods
|
||||
)
|
||||
Miner(_token, _miningCoefficient, _lockedBlocksCoefficient, _awardedPeriods * _blocksPerPeriod)
|
||||
{
|
||||
require(_blocksPerPeriod != 0 && _minReleasePeriods != 0);
|
||||
token = _token;
|
||||
blocksPerPeriod = _blocksPerPeriod;
|
||||
minReleasePeriods = _minReleasePeriods;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Throws if called by account that have not wallet.
|
||||
*/
|
||||
modifier walletExists() {
|
||||
require(walletOwners.valueExists(msg.sender));
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Create wallet for user
|
||||
* @return Address of created wallet
|
||||
**/
|
||||
function createWallet() public returns (address) {
|
||||
require(!walletOwners.valueExists(msg.sender) &&
|
||||
walletOwners.sizeOf() < MAX_OWNERS);
|
||||
Wallet wallet = new Wallet(token, blocksPerPeriod, minReleasePeriods);
|
||||
wallet.transferOwnership(msg.sender);
|
||||
wallets[msg.sender] = wallet;
|
||||
walletOwners.push(msg.sender, true);
|
||||
return wallet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Get locked tokens value for all owners in current period
|
||||
**/
|
||||
function getAllLockedTokens()
|
||||
public constant returns (uint256)
|
||||
{
|
||||
var period = block.number.div(blocksPerPeriod);
|
||||
return lockedPerPeriod[period].totalLockedValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Lock some tokens
|
||||
* @param _value Amount of tokens which should lock
|
||||
* @param _periods Amount of periods during which tokens will be locked
|
||||
**/
|
||||
function lock(uint256 _value, uint256 _periods) walletExists external {
|
||||
// TODO add checking min _value
|
||||
Wallet wallet = wallets[msg.sender];
|
||||
wallet.lock(_value, _periods);
|
||||
confirmActivity(wallet.lockedValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Terminate contract and refund to owners
|
||||
* @dev The called token contracts could try to re-enter this contract.
|
||||
Only supply token contracts you trust.
|
||||
**/
|
||||
function destroy() onlyOwner external {
|
||||
// Transfer tokens to owners
|
||||
var current = walletOwners.step(0x0, true);
|
||||
while (current != 0x0) {
|
||||
wallets[current].destroy();
|
||||
current = walletOwners.step(current, true);
|
||||
}
|
||||
token.safeTransfer(owner, token.balanceOf(address(this)));
|
||||
|
||||
// Transfer Eth to owner and terminate contract
|
||||
selfdestruct(owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Confirm activity for future period
|
||||
* @param _lockedValue Locked tokens in future period
|
||||
**/
|
||||
function confirmActivity(uint256 _lockedValue) internal {
|
||||
require(_lockedValue > 0);
|
||||
var wallet = wallets[msg.sender];
|
||||
var nextPeriod = block.number.div(blocksPerPeriod) + 1;
|
||||
|
||||
var numberConfirmedPeriods = wallet.numberConfirmedPeriods();
|
||||
if (numberConfirmedPeriods > 0 &&
|
||||
wallet.getConfirmedPeriod(numberConfirmedPeriods - 1) == nextPeriod) {
|
||||
var confirmedPeriodValue = wallet.getConfirmedPeriodValue(numberConfirmedPeriods - 1);
|
||||
lockedPerPeriod[nextPeriod].totalLockedValue = lockedPerPeriod[nextPeriod].totalLockedValue
|
||||
.add(_lockedValue.sub(confirmedPeriodValue));
|
||||
wallet.setConfirmedPeriodValue(numberConfirmedPeriods - 1, _lockedValue);
|
||||
return;
|
||||
}
|
||||
|
||||
require(numberConfirmedPeriods < MAX_PERIODS);
|
||||
lockedPerPeriod[nextPeriod].totalLockedValue += _lockedValue;
|
||||
lockedPerPeriod[nextPeriod].numberOwnersToBeRewarded++;
|
||||
wallet.addConfirmedPeriod(nextPeriod, _lockedValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Confirm activity for future period
|
||||
**/
|
||||
function confirmActivity() walletExists external {
|
||||
var wallet = wallets[msg.sender];
|
||||
var nextPeriod = block.number.div(blocksPerPeriod) + 1;
|
||||
|
||||
var numberConfirmedPeriods = wallet.numberConfirmedPeriods();
|
||||
if (numberConfirmedPeriods > 0 &&
|
||||
wallet.getConfirmedPeriod(numberConfirmedPeriods - 1) >= nextPeriod) {
|
||||
return;
|
||||
}
|
||||
|
||||
var currentPeriod = nextPeriod - 1;
|
||||
var lockedTokens = wallet.calculateLockedTokens(
|
||||
false, wallet.getLockedTokens(), 1);
|
||||
confirmActivity(lockedTokens);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Mint tokens for sender for previous periods if he locked his tokens and confirmed activity
|
||||
**/
|
||||
function mint() walletExists external {
|
||||
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--;
|
||||
}
|
||||
if (wallet.getConfirmedPeriod(numberPeriodsForMinting - 1) > previousPeriod) {
|
||||
numberPeriodsForMinting--;
|
||||
}
|
||||
|
||||
for(uint i = 0; i < numberPeriodsForMinting; ++i) {
|
||||
var (period, lockedValue) = wallet.confirmedPeriods(i);
|
||||
allLockedBlocks = allLockedBlocks.sub(blocksPerPeriod);
|
||||
(, decimals) = mint(
|
||||
wallet,
|
||||
lockedValue,
|
||||
lockedPerPeriod[period].totalLockedValue,
|
||||
blocksPerPeriod,
|
||||
allLockedBlocks,
|
||||
decimals);
|
||||
if (lockedPerPeriod[period].numberOwnersToBeRewarded > 1) {
|
||||
lockedPerPeriod[period].numberOwnersToBeRewarded--;
|
||||
} else {
|
||||
delete lockedPerPeriod[period];
|
||||
}
|
||||
}
|
||||
wallet.setDecimals(decimals);
|
||||
wallet.deleteConfirmedPeriods(numberPeriodsForMinting);
|
||||
|
||||
// Update lockedValue for current period
|
||||
wallet.updateLockedTokens(currentLockedValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Fixed-step in cumulative sum
|
||||
* @param _start Starting point
|
||||
* @param _delta How much to step
|
||||
* @param _periods Amount of periods to get locked tokens
|
||||
* @dev
|
||||
_start
|
||||
v
|
||||
|-------->*--------------->*---->*------------->|
|
||||
| ^
|
||||
| stop
|
||||
|
|
||||
| _delta
|
||||
|---------------------------->|
|
||||
|
|
||||
| shift
|
||||
| |----->|
|
||||
**/
|
||||
function findCumSum(address _start, uint256 _delta, uint256 _periods)
|
||||
public constant returns (address stop, uint256 shift)
|
||||
{
|
||||
require(walletOwners.valueExists(_start) && _periods > 0);
|
||||
var currentPeriod = block.number.div(blocksPerPeriod);
|
||||
uint256 distance = 0;
|
||||
var current = _start;
|
||||
|
||||
if (current == 0x0) {
|
||||
current = walletOwners.step(current, true);
|
||||
}
|
||||
|
||||
while (current != 0x0) {
|
||||
var wallet = wallets[current];
|
||||
var numberConfirmedPeriods = wallet.numberConfirmedPeriods();
|
||||
var period = currentPeriod;
|
||||
if (numberConfirmedPeriods > 0 &&
|
||||
wallet.getConfirmedPeriod(numberConfirmedPeriods - 1) == currentPeriod) {
|
||||
var lockedTokens = wallet.calculateLockedTokens(
|
||||
true,
|
||||
wallet.getConfirmedPeriodValue(numberConfirmedPeriods - 1),
|
||||
_periods);
|
||||
} else if (numberConfirmedPeriods > 1 &&
|
||||
wallet.getConfirmedPeriod(numberConfirmedPeriods - 2) == currentPeriod) {
|
||||
lockedTokens = wallet.calculateLockedTokens(
|
||||
true,
|
||||
wallet.getConfirmedPeriodValue(numberConfirmedPeriods - 1),
|
||||
_periods - 1);
|
||||
} else {
|
||||
current = walletOwners.step(current, true);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_delta < distance + lockedTokens) {
|
||||
stop = current;
|
||||
shift = _delta - distance;
|
||||
break;
|
||||
} else {
|
||||
distance += lockedTokens;
|
||||
current = walletOwners.step(current, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,159 +0,0 @@
|
|||
"""Deploy contracts in tester.
|
||||
|
||||
A simple Python script to deploy contracts and then estimate gas for different methods.
|
||||
"""
|
||||
from populus import Project
|
||||
from populus.utils.wait import wait_for_transaction_receipt
|
||||
from web3 import Web3
|
||||
|
||||
|
||||
def check_succesful_tx(web3: Web3, txid: str, timeout=180) -> dict:
|
||||
"""See if transaction went through (Solidity code did not throw).
|
||||
|
||||
:return: Transaction receipt
|
||||
"""
|
||||
|
||||
# http://ethereum.stackexchange.com/q/6007/620
|
||||
receipt = wait_for_transaction_receipt(web3, txid, timeout=timeout)
|
||||
txinfo = web3.eth.getTransaction(txid)
|
||||
|
||||
# EVM has only one error mode and it's consume all gas
|
||||
assert txinfo["gas"] != receipt["gasUsed"]
|
||||
return receipt
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
project = Project()
|
||||
|
||||
chain_name = "tester"
|
||||
print("Make sure {} chain is running, you can connect to it, or you'll get timeout".format(chain_name))
|
||||
|
||||
with project.get_chain(chain_name) as chain:
|
||||
web3 = chain.web3
|
||||
print("Web3 providers are", web3.providers)
|
||||
creator = web3.eth.accounts[0]
|
||||
ursula = web3.eth.accounts[1]
|
||||
alice = web3.eth.accounts[2]
|
||||
|
||||
# Create an ERC20 token
|
||||
token, txhash = chain.provider.get_or_deploy_contract(
|
||||
'HumanStandardToken', deploy_args=[
|
||||
10 ** 9, 10 ** 10, 'NuCypher KMS', 6, 'KMS'],
|
||||
deploy_transaction={
|
||||
'from': creator})
|
||||
check_succesful_tx(web3, txhash)
|
||||
print("Deploying HumanStandardToken, tx hash is", txhash)
|
||||
|
||||
# Creator deploys the escrow
|
||||
escrow, txhash = chain.provider.get_or_deploy_contract(
|
||||
'Escrow', deploy_args=[token.address, 10 ** 5, 10 ** 7],
|
||||
deploy_transaction={'from': creator})
|
||||
check_succesful_tx(web3, txhash)
|
||||
print("Deploying Escrow, tx hash is", txhash)
|
||||
|
||||
# Give Ursula and Alice some coins
|
||||
tx = token.transact({'from': creator}).transfer(ursula, 10000)
|
||||
chain.wait.for_receipt(tx)
|
||||
tx = token.transact({'from': creator}).transfer(alice, 10000)
|
||||
chain.wait.for_receipt(tx)
|
||||
print("Estimate gas for checking balance = " + str(token.estimateGas().balanceOf(alice)))
|
||||
|
||||
# Ursula and Alice give Escrow rights to transfer
|
||||
print("Estimate gas for approving = " +
|
||||
str(token.estimateGas({'from': ursula}).approve(escrow.address, 1000)))
|
||||
tx = token.transact({'from': ursula}).approve(escrow.address, 1000)
|
||||
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
|
||||
print("Estimate gas for deposit = " +
|
||||
str(escrow.estimateGas({'from': ursula}).deposit(1000, 100)))
|
||||
tx = escrow.transact({'from': ursula}).deposit(1000, 100)
|
||||
chain.wait.for_receipt(tx)
|
||||
tx = escrow.transact({'from': alice}).deposit(500, 200)
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Give rights for mining
|
||||
print("Estimate gas for giving rights for mining = " +
|
||||
str(token.estimateGas({'from': creator}).addMiner(escrow.address)))
|
||||
tx = token.transact({'from': creator}).addMiner(escrow.address)
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Wait 150 blocks and mint tokens
|
||||
chain.wait.for_block(web3.eth.blockNumber + 150)
|
||||
print("Estimate gas for Ursula mining = " +
|
||||
str(escrow.estimateGas({'from': ursula}).mint()))
|
||||
tx = escrow.transact({'from': ursula}).mint()
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Wait 100 blocks and mint tokens
|
||||
chain.wait.for_block(web3.eth.blockNumber + 100)
|
||||
print("Estimate gas for Alice mining = " +
|
||||
str(escrow.estimateGas({'from': alice}).mint()))
|
||||
tx = escrow.transact({'from': alice}).mint()
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Creator deploys the wallet manager
|
||||
wallet_manager, txhash = chain.provider.get_or_deploy_contract(
|
||||
'WalletManager', deploy_args=[token.address, 10 ** 5, 10 ** 7],
|
||||
deploy_transaction={'from': creator})
|
||||
check_succesful_tx(web3, txhash)
|
||||
print("Deploying WalletManager, tx hash is", txhash)
|
||||
|
||||
print("Estimate gas for creating wallet = " +
|
||||
str(wallet_manager.estimateGas({'from': ursula}).createWallet()))
|
||||
contract_factory = chain.provider.get_contract_factory("Wallet")
|
||||
tx = wallet_manager.transact({'from': ursula}).createWallet()
|
||||
chain.wait.for_receipt(tx)
|
||||
ursula_wallet = contract_factory(address=wallet_manager.call().wallets(ursula))
|
||||
tx = wallet_manager.transact({'from': alice}).createWallet()
|
||||
chain.wait.for_receipt(tx)
|
||||
alice_wallet = contract_factory(address=wallet_manager.call().wallets(alice))
|
||||
|
||||
# Give Ursula and Alice some coins
|
||||
tx = token.transact({'from': creator}).transfer(ursula, 10000)
|
||||
chain.wait.for_receipt(tx)
|
||||
tx = token.transact({'from': creator}).transfer(alice, 10000)
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Ursula and Alice transfer some money to wallets
|
||||
print("Estimate gas for deposit = " +
|
||||
str(token.estimateGas({'from': ursula}).transfer(ursula_wallet.address, 1000)))
|
||||
tx = token.transact({'from': ursula}).transfer(ursula_wallet.address, 1000)
|
||||
chain.wait.for_receipt(tx)
|
||||
tx = token.transact({'from': alice}).transfer(alice_wallet.address, 500)
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Ursula and Alice lock some tokens for 100 and 200 blocks
|
||||
print("Estimate gas for locking = " +
|
||||
str(wallet_manager.estimateGas({'from': ursula}).lock(1000, 100)))
|
||||
tx = wallet_manager.transact({'from': ursula}).lock(1000, 100)
|
||||
chain.wait.for_receipt(tx)
|
||||
tx = wallet_manager.transact({'from': alice}).lock(500, 200)
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Give rights for mining
|
||||
tx = token.transact({'from': creator}).addMiner(wallet_manager.address)
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Wait 150 blocks and mint tokens
|
||||
chain.wait.for_block(web3.eth.blockNumber + 150)
|
||||
print("Estimate gas for Ursula mining = " +
|
||||
str(wallet_manager.estimateGas({'from': ursula}).mint()))
|
||||
tx = wallet_manager.transact({'from': ursula}).mint()
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Wait 100 blocks and mint tokens
|
||||
chain.wait.for_block(web3.eth.blockNumber + 100)
|
||||
print("Estimate gas for Alice mining = " +
|
||||
str(wallet_manager.estimateGas({'from': alice}).mint()))
|
||||
tx = wallet_manager.transact({'from': alice}).mint()
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
print("All done!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -2,7 +2,7 @@ pragma solidity ^0.4.11;
|
|||
|
||||
|
||||
import "contracts/Miner.sol";
|
||||
import "contracts/MineableToken.sol";
|
||||
import "contracts/NuCypherKMSToken.sol";
|
||||
|
||||
|
||||
/**
|
||||
|
@ -11,31 +11,38 @@ import "contracts/MineableToken.sol";
|
|||
contract MinerTest is Miner {
|
||||
|
||||
function MinerTest(
|
||||
MineableToken _token,
|
||||
NuCypherKMSToken _token,
|
||||
uint256 _hoursPerPeriod,
|
||||
uint256 _miningCoefficient,
|
||||
uint256 _lockedBlocksCoefficient,
|
||||
uint256 _awardedLockedBlocks
|
||||
uint256 _lockedPeriodsCoefficient,
|
||||
uint256 _awardedPeriods
|
||||
)
|
||||
Miner(_token, _miningCoefficient, _lockedBlocksCoefficient, _awardedLockedBlocks)
|
||||
Miner(
|
||||
_token,
|
||||
_hoursPerPeriod,
|
||||
_miningCoefficient,
|
||||
_lockedPeriodsCoefficient,
|
||||
_awardedPeriods
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
function testMint(
|
||||
address _to,
|
||||
uint256 _period,
|
||||
uint256 _lockedValue,
|
||||
uint256 _totalLockedValue,
|
||||
uint256 _lockedBlocks,
|
||||
uint256 _allLockedBlocks,
|
||||
uint256 _allLockedPeriods,
|
||||
uint256 _decimals
|
||||
)
|
||||
public returns (uint256 amount, uint256 decimals)
|
||||
{
|
||||
return mint(
|
||||
_to,
|
||||
_period,
|
||||
_lockedValue,
|
||||
_totalLockedValue,
|
||||
_lockedBlocks,
|
||||
_allLockedBlocks,
|
||||
_allLockedPeriods,
|
||||
_decimals);
|
||||
}
|
||||
|
||||
|
|
|
@ -17,11 +17,19 @@ 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, 4 * 50 * 10 ** 9, 50, 2, 4 * 50, 4],
|
||||
'Escrow', deploy_args=[token.address, 1, 4 * 2 * 10 ** 7, 4, 4, 2],
|
||||
deploy_transaction={'from': creator})
|
||||
return escrow
|
||||
|
||||
|
||||
def wait_time(chain, wait_hours):
|
||||
web3 = chain.web3
|
||||
step = 50
|
||||
end_timestamp = web3.eth.getBlock(web3.eth.blockNumber).timestamp + wait_hours * 60 * 60
|
||||
while web3.eth.getBlock(web3.eth.blockNumber).timestamp < end_timestamp:
|
||||
chain.wait.for_block(web3.eth.blockNumber + step)
|
||||
|
||||
|
||||
def test_escrow(web3, chain, token, escrow):
|
||||
creator = web3.eth.accounts[0]
|
||||
ursula = web3.eth.accounts[1]
|
||||
|
@ -85,7 +93,7 @@ def test_escrow(web3, chain, token, escrow):
|
|||
assert 500 == escrow.call().calculateLockedTokens(alice, 1)
|
||||
|
||||
# Checks locked tokens in next period
|
||||
chain.wait.for_block(web3.eth.blockNumber + 50)
|
||||
wait_time(chain, 1)
|
||||
assert 1000 == escrow.call().getLockedTokens(ursula)
|
||||
assert 500 == escrow.call().getLockedTokens(alice)
|
||||
assert 1500 == escrow.call().getAllLockedTokens()
|
||||
|
@ -110,14 +118,14 @@ def test_escrow(web3, chain, token, escrow):
|
|||
chain.wait.for_receipt(tx)
|
||||
assert 750 == escrow.call().calculateLockedTokens(ursula, 2)
|
||||
|
||||
# Wait 50 blocks and checks locking
|
||||
chain.wait.for_block(web3.eth.blockNumber + 50)
|
||||
# Wait 1 period and checks locking
|
||||
wait_time(chain, 1)
|
||||
assert 1500 == escrow.call().getLockedTokens(ursula)
|
||||
|
||||
# Confirm activity and wait 50 blocks
|
||||
# Confirm activity and wait 1 period
|
||||
tx = escrow.transact({'from': ursula}).confirmActivity()
|
||||
chain.wait.for_receipt(tx)
|
||||
chain.wait.for_block(web3.eth.blockNumber + 50)
|
||||
wait_time(chain, 1)
|
||||
assert 750 == escrow.call().getLockedTokens(ursula)
|
||||
assert 0 == escrow.call().calculateLockedTokens(ursula, 1)
|
||||
|
||||
|
@ -147,7 +155,7 @@ def test_escrow(web3, chain, token, escrow):
|
|||
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)
|
||||
wait_time(chain, 1)
|
||||
assert 600 == escrow.call().getLockedTokens(ursula)
|
||||
assert 300 == escrow.call().calculateLockedTokens(ursula, 1)
|
||||
assert 0 == escrow.call().calculateLockedTokens(ursula, 2)
|
||||
|
@ -160,7 +168,7 @@ def test_escrow(web3, chain, token, escrow):
|
|||
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)
|
||||
wait_time(chain, 1)
|
||||
assert 800 == escrow.call().getLockedTokens(ursula)
|
||||
|
||||
# Alice can't deposit too low value (less then rate)
|
||||
|
@ -178,10 +186,10 @@ def test_escrow(web3, chain, token, escrow):
|
|||
assert 1000 == escrow.call().calculateLockedTokens(alice, 1)
|
||||
assert 500 == escrow.call().calculateLockedTokens(alice, 2)
|
||||
assert 0 == escrow.call().calculateLockedTokens(alice, 3)
|
||||
chain.wait.for_block(web3.eth.blockNumber + 50)
|
||||
wait_time(chain, 1)
|
||||
assert 1000 == escrow.call().getLockedTokens(alice)
|
||||
|
||||
# And increases locked blocks
|
||||
# And increases locked time
|
||||
tx = escrow.transact({'from': alice}).lock(0, 2)
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 1000 == escrow.call().getLockedTokens(alice)
|
||||
|
@ -235,7 +243,7 @@ def test_locked_distribution(web3, chain, token, escrow):
|
|||
assert 0 == shift
|
||||
|
||||
# Wait next period
|
||||
chain.wait.for_block(web3.eth.blockNumber + 50)
|
||||
wait_time(chain, 1)
|
||||
n_locked = escrow.call().getAllLockedTokens()
|
||||
assert n_locked > 0
|
||||
|
||||
|
@ -315,7 +323,7 @@ def test_mining(web3, chain, token, escrow):
|
|||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Only Ursula confirm next period
|
||||
chain.wait.for_block(web3.eth.blockNumber + 50)
|
||||
wait_time(chain, 1)
|
||||
assert 1500 == escrow.call().getAllLockedTokens()
|
||||
tx = escrow.transact({'from': ursula}).confirmActivity()
|
||||
chain.wait.for_receipt(tx)
|
||||
|
@ -325,7 +333,7 @@ def test_mining(web3, chain, token, escrow):
|
|||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Ursula and Alice mint tokens for last periods
|
||||
chain.wait.for_block(web3.eth.blockNumber + 50)
|
||||
wait_time(chain, 1)
|
||||
assert 1000 == escrow.call().getAllLockedTokens()
|
||||
tx = escrow.transact({'from': ursula}).mint()
|
||||
chain.wait.for_receipt(tx)
|
||||
|
@ -341,7 +349,7 @@ def test_mining(web3, chain, token, escrow):
|
|||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Ursula can't confirm next period because end of locking
|
||||
chain.wait.for_block(web3.eth.blockNumber + 50)
|
||||
wait_time(chain, 1)
|
||||
assert 500 == escrow.call().getAllLockedTokens()
|
||||
with pytest.raises(TransactionFailed):
|
||||
tx = escrow.transact({'from': ursula}).confirmActivity()
|
||||
|
@ -352,7 +360,7 @@ def test_mining(web3, chain, token, escrow):
|
|||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Ursula mint tokens for next period
|
||||
chain.wait.for_block(web3.eth.blockNumber + 50)
|
||||
wait_time(chain, 1)
|
||||
assert 500 == escrow.call().getAllLockedTokens()
|
||||
tx = escrow.transact({'from': ursula}).mint()
|
||||
chain.wait.for_receipt(tx)
|
||||
|
@ -368,7 +376,7 @@ def test_mining(web3, chain, token, escrow):
|
|||
chain.wait.for_receipt(tx)
|
||||
tx = escrow.transact({'from': alice}).confirmActivity()
|
||||
chain.wait.for_receipt(tx)
|
||||
chain.wait.for_block(web3.eth.blockNumber + 100)
|
||||
wait_time(chain, 2)
|
||||
assert 0 == escrow.call().getAllLockedTokens()
|
||||
tx = escrow.transact({'from': alice}).mint()
|
||||
chain.wait.for_receipt(tx)
|
||||
|
@ -419,4 +427,4 @@ def test_mining(web3, chain, token, escrow):
|
|||
chain.wait.for_receipt(tx)
|
||||
assert 10134 == token.call().balanceOf(alice)
|
||||
|
||||
# TODO test max confirmed periods
|
||||
# TODO test max confirmed periods and miners
|
||||
|
|
|
@ -1,478 +0,0 @@
|
|||
import pytest
|
||||
from ethereum.tester import TransactionFailed
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def token(web3, chain):
|
||||
creator = web3.eth.accounts[0]
|
||||
# Create an ERC20 token
|
||||
token_contract, _ = chain.provider.get_or_deploy_contract(
|
||||
'NuCypherKMSToken', deploy_args=[10 ** 9, 2 * 10 ** 9],
|
||||
deploy_transaction={'from': creator})
|
||||
return token_contract
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
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, 4 * 50 * 10 ** 9, 50, 2, 4 * 50, 4],
|
||||
deploy_transaction={'from': creator, 'gas': 4000000})
|
||||
return wallet_manager_contract
|
||||
|
||||
|
||||
def test_escrow(web3, chain, token, wallet_manager):
|
||||
creator = web3.eth.accounts[0]
|
||||
ursula = web3.eth.accounts[1]
|
||||
alice = web3.eth.accounts[2]
|
||||
|
||||
# Give Ursula and Alice some coins
|
||||
tx = token.transact({'from': creator}).transfer(ursula, 10000)
|
||||
chain.wait.for_receipt(tx)
|
||||
tx = token.transact({'from': creator}).transfer(alice, 10000)
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 10000 == token.call().balanceOf(ursula)
|
||||
assert 10000 == token.call().balanceOf(alice)
|
||||
|
||||
# Ursula and Alice create wallets
|
||||
contract_factory = chain.provider.get_contract_factory("Wallet")
|
||||
tx = wallet_manager.transact({'from': ursula}).createWallet()
|
||||
chain.wait.for_receipt(tx)
|
||||
ursula_wallet = contract_factory(address=wallet_manager.call().wallets(ursula))
|
||||
tx = wallet_manager.transact({'from': alice}).createWallet()
|
||||
chain.wait.for_receipt(tx)
|
||||
alice_wallet = contract_factory(address=wallet_manager.call().wallets(alice))
|
||||
|
||||
# Ursula's withdrawal attempt won't succeed because nothing to withdraw
|
||||
with pytest.raises(TransactionFailed):
|
||||
tx = ursula_wallet.transact({'from': ursula}).withdraw(100)
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# And can't lock because nothing to lock
|
||||
with pytest.raises(TransactionFailed):
|
||||
tx = wallet_manager.transact({'from': ursula}).lock(500, 2)
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# And can't set lock using wallet
|
||||
with pytest.raises(TransactionFailed):
|
||||
tx = ursula_wallet.transact({'from': ursula}).updateLockedTokens(1)
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Ursula and Alice transfer some money to wallets
|
||||
tx = token.transact({'from': ursula}).transfer(ursula_wallet.address, 1500)
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 1500 == token.call().balanceOf(ursula_wallet.address)
|
||||
tx = token.transact({'from': alice}).transfer(alice_wallet.address, 500)
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 500 == token.call().balanceOf(alice_wallet.address)
|
||||
|
||||
# Ursula asks for refund
|
||||
tx = ursula_wallet.transact({'from': ursula}).withdraw(500)
|
||||
chain.wait.for_receipt(tx)
|
||||
# and it works
|
||||
assert 1000 == token.call().balanceOf(ursula_wallet.address)
|
||||
assert 9000 == token.call().balanceOf(ursula)
|
||||
|
||||
# Alice can't withdraw from Ursula's wallet
|
||||
with pytest.raises(TransactionFailed):
|
||||
tx = ursula_wallet.transact({'from': alice}).withdraw(1)
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Check that nothing is locked
|
||||
assert 0 == ursula_wallet.call().getLockedTokens()
|
||||
assert 0 == alice_wallet.call().getLockedTokens()
|
||||
|
||||
# Ursula can't lock too low value (less then rate)
|
||||
# TODO uncomment
|
||||
# with pytest.raises(TransactionFailed):
|
||||
# tx = wallet_manager.transact({'from': ursula}).lock(1000, 10)
|
||||
# chain.wait.for_receipt(tx)
|
||||
|
||||
# Ursula and Alice lock some tokens for 100 and 200 blocks
|
||||
tx = wallet_manager.transact({'from': ursula}).lock(1000, 1)
|
||||
chain.wait.for_receipt(tx)
|
||||
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)
|
||||
assert 1000 == ursula_wallet.call().getLockedTokens()
|
||||
assert 500 == alice_wallet.call().getLockedTokens()
|
||||
assert 1500 == wallet_manager.call().getAllLockedTokens()
|
||||
|
||||
# Ursula's withdrawal attempt won't succeed
|
||||
with pytest.raises(TransactionFailed):
|
||||
tx = ursula_wallet.transact({'from': ursula}).withdraw(100)
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 1000 == token.call().balanceOf(ursula_wallet.address)
|
||||
assert 9000 == token.call().balanceOf(ursula)
|
||||
|
||||
# Ursula can deposit more tokens
|
||||
tx = wallet_manager.transact({'from': ursula}).confirmActivity()
|
||||
chain.wait.for_receipt(tx)
|
||||
tx = token.transact({'from': ursula}).transfer(ursula_wallet.address, 500)
|
||||
chain.wait.for_receipt(tx)
|
||||
tx = wallet_manager.transact({'from': ursula}).lock(500, 0)
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 1500 == token.call().balanceOf(ursula_wallet.address)
|
||||
assert 8500 == token.call().balanceOf(ursula)
|
||||
|
||||
# Wait 50 blocks and checks locking
|
||||
chain.wait.for_block(web3.eth.blockNumber + 50)
|
||||
assert 1500 == ursula_wallet.call().getLockedTokens()
|
||||
|
||||
# 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)
|
||||
|
||||
# Confirm activity and wait 50 blocks
|
||||
tx = wallet_manager.transact({'from': ursula}).confirmActivity()
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 1500 == ursula_wallet.call().getLockedTokens()
|
||||
assert 750 == ursula_wallet.call().calculateLockedTokens(1)
|
||||
assert 0 == ursula_wallet.call().calculateLockedTokens(2)
|
||||
chain.wait.for_block(web3.eth.blockNumber + 50)
|
||||
assert 750 == ursula_wallet.call().getLockedTokens()
|
||||
assert 0 == ursula_wallet.call().calculateLockedTokens(1)
|
||||
|
||||
# And Ursula can withdraw some tokens
|
||||
tx = ursula_wallet.transact({'from': ursula}).withdraw(100)
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 1400 == token.call().balanceOf(ursula_wallet.address)
|
||||
assert 8600 == token.call().balanceOf(ursula)
|
||||
|
||||
# But Ursula can't withdraw all without mining for locked value
|
||||
# TODO complete method
|
||||
# with pytest.raises(TransactionFailed):
|
||||
# tx = escrow.transact({'from': ursula}).withdrawAll()
|
||||
# chain.wait.for_receipt(tx)
|
||||
|
||||
# Ursula can deposit more tokens
|
||||
tx = token.transact({'from': ursula}).transfer(ursula_wallet.address, 500)
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 1900 == token.call().balanceOf(ursula_wallet.address)
|
||||
tx = wallet_manager.transact({'from': ursula}).lock(500, 0)
|
||||
chain.wait.for_receipt(tx)
|
||||
tx = wallet_manager.transact({'from': ursula}).lock(100, 0)
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# 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)
|
||||
assert 600 == ursula_wallet.call().getLockedTokens()
|
||||
assert 300 == ursula_wallet.call().calculateLockedTokens(1)
|
||||
assert 0 == ursula_wallet.call().calculateLockedTokens(2)
|
||||
|
||||
# Ursula can increase lock
|
||||
tx = wallet_manager.transact({'from': ursula}).lock(500, 2)
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 600 == ursula_wallet.call().getLockedTokens()
|
||||
assert 800 == ursula_wallet.call().calculateLockedTokens(1)
|
||||
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()
|
||||
|
||||
# Alice can't deposit too low value (less then rate)
|
||||
# TODO uncomment after completing logic
|
||||
# with pytest.raises(TransactionFailed):
|
||||
# tx = escrow.transact({'from': ursula}).deposit(100, 100)
|
||||
# chain.wait.for_receipt(tx)
|
||||
|
||||
# Alice increases lock by deposit more tokens
|
||||
tx = token.transact({'from': alice}).transfer(alice_wallet.address, 500)
|
||||
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)
|
||||
assert 0 == alice_wallet.call().calculateLockedTokens(3)
|
||||
chain.wait.for_block(web3.eth.blockNumber + 50)
|
||||
assert 1000 == alice_wallet.call().getLockedTokens()
|
||||
|
||||
# And increases locked blocks
|
||||
tx = wallet_manager.transact({'from': alice}).lock(0, 2)
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 1000 == alice_wallet.call().getLockedTokens()
|
||||
assert 500 == alice_wallet.call().calculateLockedTokens(1)
|
||||
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 100 == alice_wallet.call().calculateLockedTokens(2)
|
||||
assert 0 == alice_wallet.call().calculateLockedTokens(3)
|
||||
|
||||
# Ursula can't destroy contract
|
||||
with pytest.raises(TransactionFailed):
|
||||
tx = wallet_manager.transact({'from': ursula}).destroy()
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# # Destroy contract from creator and refund all to Ursula and Alice
|
||||
tx = wallet_manager.transact({'from': creator}).destroy()
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 0 == token.call().balanceOf(ursula_wallet.address)
|
||||
assert 0 == token.call().balanceOf(alice_wallet.address)
|
||||
assert 10000 == token.call().balanceOf(ursula)
|
||||
assert 10000 == token.call().balanceOf(alice)
|
||||
|
||||
|
||||
def test_locked_distribution(web3, chain, token, wallet_manager):
|
||||
NULL_ADDR = '0x' + '0' * 40
|
||||
creator = web3.eth.accounts[0]
|
||||
miners = web3.eth.accounts[1:]
|
||||
amount = token.call().balanceOf(creator) // 2
|
||||
largest_locked = amount
|
||||
|
||||
# Airdrop
|
||||
for miner in miners:
|
||||
tx = token.transact({'from': creator}).transfer(miner, amount)
|
||||
chain.wait.for_receipt(tx)
|
||||
amount = amount // 2
|
||||
|
||||
# Lock
|
||||
for index, miner in enumerate(miners[::-1]):
|
||||
balance = token.call().balanceOf(miner)
|
||||
tx = wallet_manager.transact({'from': miner}).createWallet()
|
||||
chain.wait.for_receipt(tx)
|
||||
wallet = wallet_manager.call().wallets(miner)
|
||||
tx = token.transact({'from': miner}).transfer(wallet, balance)
|
||||
chain.wait.for_receipt(tx)
|
||||
tx = wallet_manager.transact({'from': miner}).lock(balance, len(miners) - index + 1)
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Check current period
|
||||
address_stop, shift = wallet_manager.call().findCumSum(NULL_ADDR, 1, 1)
|
||||
assert NULL_ADDR == address_stop.lower()
|
||||
assert 0 == shift
|
||||
|
||||
# Wait next period
|
||||
chain.wait.for_block(web3.eth.blockNumber + 50)
|
||||
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
|
||||
|
||||
address_stop, shift = wallet_manager.call().findCumSum(NULL_ADDR, largest_locked, 1)
|
||||
assert miners[1].lower() == address_stop.lower()
|
||||
assert 0 == shift
|
||||
|
||||
address_stop, shift = wallet_manager.call().findCumSum(
|
||||
miners[1], largest_locked // 2 + 1, 1)
|
||||
assert miners[2].lower() == address_stop.lower()
|
||||
assert 1 == shift
|
||||
|
||||
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
|
||||
|
||||
for index, _ in enumerate(miners[:-1]):
|
||||
address_stop, shift = wallet_manager.call().findCumSum(NULL_ADDR, 1, index + 3)
|
||||
assert miners[index + 1].lower() == address_stop.lower()
|
||||
assert 1 == shift
|
||||
|
||||
|
||||
def test_mining(web3, chain, token, wallet_manager):
|
||||
creator = web3.eth.accounts[0]
|
||||
ursula = web3.eth.accounts[1]
|
||||
alice = web3.eth.accounts[2]
|
||||
|
||||
# Ursula and Alice create wallets
|
||||
contract_factory = chain.provider.get_contract_factory("Wallet")
|
||||
tx = wallet_manager.transact({'from': ursula}).createWallet()
|
||||
chain.wait.for_receipt(tx)
|
||||
ursula_wallet = contract_factory(address=wallet_manager.call().wallets(ursula))
|
||||
tx = wallet_manager.transact({'from': alice}).createWallet()
|
||||
chain.wait.for_receipt(tx)
|
||||
alice_wallet = contract_factory(address=wallet_manager.call().wallets(alice))
|
||||
|
||||
# Give Ursula and Alice some coins
|
||||
tx = token.transact({'from': creator}).transfer(ursula, 10000)
|
||||
chain.wait.for_receipt(tx)
|
||||
tx = token.transact({'from': creator}).transfer(alice, 10000)
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Ursula and Alice transfer some money to wallets
|
||||
tx = token.transact({'from': ursula}).transfer(ursula_wallet.address, 1000)
|
||||
chain.wait.for_receipt(tx)
|
||||
tx = token.transact({'from': alice}).transfer(alice_wallet.address, 500)
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Give rights for mining
|
||||
tx = token.transact({'from': creator}).addMiner(wallet_manager.address)
|
||||
chain.wait.for_receipt(tx)
|
||||
assert token.call().isMiner(wallet_manager.address)
|
||||
|
||||
# Ursula can't mint because no locked tokens
|
||||
with pytest.raises(TransactionFailed):
|
||||
tx = wallet_manager.transact({'from': ursula}).mint()
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# 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, 2)
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Using locked tokens starts from next period
|
||||
assert 0 == wallet_manager.call().getAllLockedTokens()
|
||||
|
||||
# Ursula can't use method from Miner contract
|
||||
with pytest.raises(TypeError):
|
||||
tx = wallet_manager.transact({'from': ursula}).mint(ursula, 1, 1, 1, 1, 1)
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Only Ursula confirm next period
|
||||
chain.wait.for_block(web3.eth.blockNumber + 50)
|
||||
assert 1500 == wallet_manager.call().getAllLockedTokens()
|
||||
tx = wallet_manager.transact({'from': ursula}).confirmActivity()
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Checks that no error
|
||||
tx = wallet_manager.transact({'from': ursula}).confirmActivity()
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Ursula and Alice mint tokens for last periods
|
||||
chain.wait.for_block(web3.eth.blockNumber + 50)
|
||||
assert 1000 == wallet_manager.call().getAllLockedTokens()
|
||||
tx = wallet_manager.transact({'from': ursula}).mint()
|
||||
chain.wait.for_receipt(tx)
|
||||
tx = wallet_manager.transact({'from': alice}).mint()
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 1050 == token.call().balanceOf(ursula_wallet.address)
|
||||
assert 521 == 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)
|
||||
|
||||
# Ursula can't confirm next period because end of locking
|
||||
chain.wait.for_block(web3.eth.blockNumber + 50)
|
||||
assert 500 == wallet_manager.call().getAllLockedTokens()
|
||||
with pytest.raises(TransactionFailed):
|
||||
tx = wallet_manager.transact({'from': ursula}).confirmActivity()
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# But Alice can
|
||||
tx = wallet_manager.transact({'from': alice}).confirmActivity()
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Ursula mint tokens for next period
|
||||
chain.wait.for_block(web3.eth.blockNumber + 50)
|
||||
assert 500 == wallet_manager.call().getAllLockedTokens()
|
||||
tx = wallet_manager.transact({'from': ursula}).mint()
|
||||
chain.wait.for_receipt(tx)
|
||||
# But Alice can't mining because she did not confirmed activity
|
||||
with pytest.raises(TransactionFailed):
|
||||
tx = wallet_manager.transact({'from': alice}).mint()
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 1163 == token.call().balanceOf(ursula_wallet.address)
|
||||
assert 521 == token.call().balanceOf(alice_wallet.address)
|
||||
|
||||
# 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)
|
||||
assert 0 == wallet_manager.call().getAllLockedTokens()
|
||||
tx = wallet_manager.transact({'from': alice}).mint()
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 1163 == token.call().balanceOf(ursula_wallet.address)
|
||||
assert 634 == token.call().balanceOf(alice_wallet.address)
|
||||
|
||||
# Ursula can't confirm and mint because no locked tokens
|
||||
with pytest.raises(TransactionFailed):
|
||||
tx = wallet_manager.transact({'from': ursula}).mint()
|
||||
chain.wait.for_receipt(tx)
|
||||
with pytest.raises(TransactionFailed):
|
||||
tx = wallet_manager.transact({'from': ursula}).confirmActivity()
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Ursula can't confirm and mint because no locked tokens
|
||||
with pytest.raises(TransactionFailed):
|
||||
tx = wallet_manager.transact({'from': ursula}).mint()
|
||||
chain.wait.for_receipt(tx)
|
||||
with pytest.raises(TransactionFailed):
|
||||
tx = wallet_manager.transact({'from': ursula}).confirmActivity()
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# 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(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(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(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)
|
||||
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
|
||||
# tx = wallet_manager.transact({'from': alice}).withdrawAll()
|
||||
# chain.wait.for_receipt(tx)
|
||||
tx = alice_wallet.transact({'from': alice}).withdraw(634)
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 10134 == token.call().balanceOf(alice)
|
||||
|
||||
# TODO test max confirmed periods
|
|
@ -18,45 +18,31 @@ def test_miner(web3, chain, token):
|
|||
|
||||
# Creator deploys the miner
|
||||
miner, _ = chain.provider.get_or_deploy_contract(
|
||||
'MinerTest', deploy_args=[token.address, 10 ** 48, 10 ** 7, 10 ** 7],
|
||||
'MinerTest', deploy_args=[token.address, 1, 10 ** 46, 10 ** 7, 10 ** 7],
|
||||
deploy_transaction={'from': creator})
|
||||
|
||||
# Give rights for mining
|
||||
tx = token.transact({'from': creator}).addMiner(miner.address)
|
||||
chain.wait.for_receipt(tx)
|
||||
|
||||
# Check reward
|
||||
# TODO uncomment or delete
|
||||
# assert miner.call().isEmptyReward(100, 100)
|
||||
# assert not miner.call().isEmptyReward(1000, 100)
|
||||
|
||||
# # Try to mint using low value
|
||||
# TODO uncomment or delete
|
||||
# assert miner.call().testMint(ursula, 100, 200, 100, 0) == [0, 0]
|
||||
# tx = miner.transact().testMint(ursula, 100, 200, 100, 0)
|
||||
# chain.wait.for_receipt(tx)
|
||||
# assert token.call().totalSupply() == 10 ** 9
|
||||
# assert token.call().balanceOf(ursula) == 0
|
||||
# assert miner.call().lastMintedPoint() == 0
|
||||
|
||||
# Mint some tokens
|
||||
tx = miner.transact().testMint(ursula, 1000, 2000, 100, 0, 0)
|
||||
tx = miner.transact().testMint(ursula, 0, 1000, 2000, 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, 0)
|
||||
tx = miner.transact().testMint(ursula, 0, 500, 500, 0, 0)
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 50 == token.call().balanceOf(ursula)
|
||||
assert 10 ** 30 + 50 == token.call().totalSupply()
|
||||
assert 30 == token.call().balanceOf(ursula)
|
||||
assert 10 ** 30 + 30 == token.call().totalSupply()
|
||||
|
||||
tx = miner.transact().testMint(ursula, 500, 500, 200, 10 ** 7, 0)
|
||||
tx = miner.transact().testMint(ursula, 0, 500, 500, 10 ** 7, 0)
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 130 == token.call().balanceOf(ursula)
|
||||
assert 10 ** 30 + 130 == token.call().totalSupply()
|
||||
assert 70 == token.call().balanceOf(ursula)
|
||||
assert 10 ** 30 + 70 == token.call().totalSupply()
|
||||
|
||||
tx = miner.transact().testMint(ursula, 500, 500, 200, 2 * 10 ** 7, 0)
|
||||
tx = miner.transact().testMint(ursula, 0, 500, 500, 2 * 10 ** 7, 0)
|
||||
chain.wait.for_receipt(tx)
|
||||
assert 210 == token.call().balanceOf(ursula)
|
||||
assert 10 ** 30 + 210 == token.call().totalSupply()
|
||||
assert 110 == token.call().balanceOf(ursula)
|
||||
assert 10 ** 30 + 110 == token.call().totalSupply()
|
||||
|
|
|
@ -20,6 +20,14 @@ def airdrop(chain):
|
|||
chain.wait.for_receipt(tx, timeout=10)
|
||||
|
||||
|
||||
def wait_time(chain, wait_hours):
|
||||
web3 = chain.web3
|
||||
step = 50
|
||||
end_timestamp = web3.eth.getBlock(web3.eth.blockNumber).timestamp + wait_hours * 60 * 60
|
||||
while web3.eth.getBlock(web3.eth.blockNumber).timestamp < end_timestamp:
|
||||
chain.wait.for_block(web3.eth.blockNumber + step)
|
||||
|
||||
|
||||
def test_deposit(chain):
|
||||
token.create()
|
||||
escrow.create()
|
||||
|
@ -35,7 +43,7 @@ def test_select_ursulas(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, 100, u)
|
||||
chain.wait.for_block(chain.web3.eth.blockNumber + escrow.BLOCKS_PER_PERIOD)
|
||||
wait_time(chain, escrow.HOURS_PER_PERIOD)
|
||||
|
||||
miners = escrow.sample(3)
|
||||
assert len(miners) == 3
|
||||
|
@ -57,8 +65,7 @@ def test_mine_withdraw(chain):
|
|||
for u in chain.web3.eth.accounts[1:]:
|
||||
ursula.lock((10 + random.randrange(9000)) * M, 1, u)
|
||||
|
||||
chain.wait.for_block(chain.web3.eth.blockNumber + 2 * escrow.BLOCKS_PER_PERIOD)
|
||||
|
||||
wait_time(chain, 2 * escrow.HOURS_PER_PERIOD)
|
||||
ursula.mine(addr)
|
||||
ursula.withdraw(addr)
|
||||
final_balance = token.balance(addr)
|
||||
|
|
Loading…
Reference in New Issue