mirror of https://github.com/nucypher/nucypher.git
commit
bd1f2b77e1
|
@ -4,6 +4,7 @@ pragma solidity ^0.5.3;
|
|||
import "zeppelin/math/SafeMath.sol";
|
||||
import "zeppelin/token/ERC20/SafeERC20.sol";
|
||||
import "zeppelin/utils/Address.sol";
|
||||
import "zeppelin/ownership/Ownable.sol";
|
||||
import "contracts/NuCypherToken.sol";
|
||||
import "contracts/StakingEscrow.sol";
|
||||
import "contracts/lib/AdditionalMath.sol";
|
||||
|
@ -12,7 +13,7 @@ import "contracts/lib/AdditionalMath.sol";
|
|||
/**
|
||||
* @notice The WorkLock distribution contract
|
||||
*/
|
||||
contract WorkLock {
|
||||
contract WorkLock is Ownable {
|
||||
using SafeERC20 for NuCypherToken;
|
||||
using SafeMath for uint256;
|
||||
using AdditionalMath for uint256;
|
||||
|
@ -27,6 +28,7 @@ contract WorkLock {
|
|||
event BiddersChecked(address indexed sender, uint256 startIndex, uint256 endIndex);
|
||||
event ForceRefund(address indexed sender, address indexed bidder, uint256 refundETH);
|
||||
event CompensationWithdrawn(address indexed sender, uint256 value);
|
||||
event Shutdown(address indexed sender);
|
||||
|
||||
struct WorkInfo {
|
||||
uint256 depositedETH;
|
||||
|
@ -37,7 +39,6 @@ contract WorkLock {
|
|||
|
||||
NuCypherToken public token;
|
||||
StakingEscrow public escrow;
|
||||
address public creator;
|
||||
|
||||
uint256 public startBidDate;
|
||||
uint256 public endBidDate;
|
||||
|
@ -125,7 +126,6 @@ contract WorkLock {
|
|||
minAllowedBid = _minAllowedBid;
|
||||
maxAllowableLockedTokens = escrow.maxAllowableLockedTokens();
|
||||
minAllowableLockedTokens = escrow.minAllowableLockedTokens();
|
||||
creator = msg.sender;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -338,11 +338,22 @@ contract WorkLock {
|
|||
}
|
||||
|
||||
/**
|
||||
* @notice Cancels distribution, makes possible to retrieve all bids and creator gets all tokens
|
||||
* @notice Cancels distribution, makes possible to retrieve all bids and owner gets all tokens
|
||||
*/
|
||||
function shutdown() internal {
|
||||
function shutdown() external onlyOwner {
|
||||
require(!isClaimingAvailable(), "Claiming has already been enabled");
|
||||
internalShutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Cancels distribution, makes possible to retrieve all bids and owner gets all tokens
|
||||
*/
|
||||
function internalShutdown() internal {
|
||||
startBidDate = 0;
|
||||
endBidDate = 0;
|
||||
endCancellationDate = uint256(0) - 1; // "infinite" cancellation window
|
||||
token.safeTransfer(creator, tokenSupply);
|
||||
token.safeTransfer(owner(), tokenSupply);
|
||||
emit Shutdown(msg.sender);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -357,7 +368,7 @@ contract WorkLock {
|
|||
|
||||
uint256 minNumberOfBidders = tokenSupply.divCeil(maxAllowableLockedTokens);
|
||||
if (bidders.length < minNumberOfBidders) {
|
||||
shutdown();
|
||||
internalShutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ def test_worklock(testerchain, token_economics, deploy_contract, token, escrow,
|
|||
gas_to_save_state = 30000
|
||||
|
||||
# Deploy WorkLock
|
||||
now = testerchain.w3.eth.getBlock(block_identifier='latest').timestamp
|
||||
now = testerchain.get_blocktime()
|
||||
start_bid_date = now + ONE_HOUR
|
||||
end_bid_date = start_bid_date + ONE_HOUR
|
||||
end_cancellation_date = end_bid_date + ONE_HOUR
|
||||
|
@ -1135,6 +1135,7 @@ def test_force_refund(testerchain, token_economics, deploy_contract, worklock_fa
|
|||
bidding_delay=0,
|
||||
additional_time_to_cancel=0,
|
||||
boosting_refund=boosting_refund)
|
||||
shutdown_log = worklock.events.Shutdown.createFilter(fromBlock='latest')
|
||||
|
||||
bidders = bidders[0:9]
|
||||
do_bids(testerchain, worklock, bidders, 10 * MIN_ALLOWED_BID)
|
||||
|
@ -1168,3 +1169,208 @@ def test_force_refund(testerchain, token_economics, deploy_contract, worklock_fa
|
|||
tx = worklock.functions.cancelBid().transact({'from': bidder1, 'gas_price': 0})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
assert worklock.functions.workInfo(bidder1).call()[0] == 0
|
||||
|
||||
events = shutdown_log.get_all_entries()
|
||||
assert len(events) == 1
|
||||
event_args = events[-1]['args']
|
||||
assert event_args['sender'] == bidder1
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_shutdown(testerchain, token_economics, deploy_contract, worklock_factory, token):
|
||||
creator, bidder, *everyone_else = testerchain.w3.eth.accounts
|
||||
boosting_refund = 100
|
||||
gas_to_save_state = 30000
|
||||
|
||||
# Shutdown before start of bidding
|
||||
now = testerchain.get_blocktime()
|
||||
start_bid_date = now + ONE_HOUR
|
||||
end_bid_date = start_bid_date + ONE_HOUR
|
||||
end_cancellation_date = end_bid_date + ONE_HOUR
|
||||
worklock = worklock_factory(supply=0,
|
||||
bidding_delay=ONE_HOUR,
|
||||
additional_time_to_cancel=ONE_HOUR,
|
||||
boosting_refund=boosting_refund)
|
||||
shutdown_log = worklock.events.Shutdown.createFilter(fromBlock='latest')
|
||||
|
||||
# Only owner can do shutdown
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = worklock.functions.shutdown().transact({'from': bidder})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
creator_tokens = token.functions.balanceOf(creator).call()
|
||||
assert token.functions.balanceOf(worklock.address).call() == 0
|
||||
assert worklock.functions.startBidDate().call() == start_bid_date
|
||||
assert worklock.functions.endBidDate().call() == end_bid_date
|
||||
assert worklock.functions.endCancellationDate().call() == end_cancellation_date
|
||||
tx = worklock.functions.shutdown().transact({'from': creator})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
assert worklock.functions.startBidDate().call() == 0
|
||||
assert worklock.functions.endBidDate().call() == 0
|
||||
assert worklock.functions.endCancellationDate().call() > end_cancellation_date
|
||||
assert token.functions.balanceOf(worklock.address).call() == 0
|
||||
assert token.functions.balanceOf(creator).call() == creator_tokens
|
||||
|
||||
events = shutdown_log.get_all_entries()
|
||||
assert len(events) == 1
|
||||
event_args = events[-1]['args']
|
||||
assert event_args['sender'] == creator
|
||||
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = worklock.functions.bid().transact({'from': bidder, 'value': MIN_ALLOWED_BID, 'gas_price': 0})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
# Shutdown after start of bidding
|
||||
now = testerchain.get_blocktime()
|
||||
start_bid_date = now
|
||||
end_bid_date = start_bid_date + ONE_HOUR
|
||||
end_cancellation_date = end_bid_date + ONE_HOUR
|
||||
worklock_supply = token_economics.maximum_allowed_locked
|
||||
worklock = worklock_factory(supply=worklock_supply,
|
||||
bidding_delay=0,
|
||||
additional_time_to_cancel=ONE_HOUR,
|
||||
boosting_refund=boosting_refund)
|
||||
shutdown_log = worklock.events.Shutdown.createFilter(fromBlock='latest')
|
||||
do_bids(testerchain, worklock, [bidder], MIN_ALLOWED_BID)
|
||||
|
||||
# Only owner can do shutdown
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = worklock.functions.shutdown().transact({'from': bidder})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
creator_tokens = token.functions.balanceOf(creator).call()
|
||||
assert token.functions.balanceOf(worklock.address).call() == worklock_supply
|
||||
assert worklock.functions.startBidDate().call() == start_bid_date
|
||||
assert worklock.functions.endBidDate().call() == end_bid_date
|
||||
assert worklock.functions.endCancellationDate().call() == end_cancellation_date
|
||||
tx = worklock.functions.shutdown().transact({'from': creator})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
assert worklock.functions.startBidDate().call() == 0
|
||||
assert worklock.functions.endBidDate().call() == 0
|
||||
assert worklock.functions.endCancellationDate().call() > end_cancellation_date
|
||||
assert token.functions.balanceOf(worklock.address).call() == 0
|
||||
assert token.functions.balanceOf(creator).call() == creator_tokens + worklock_supply
|
||||
|
||||
events = shutdown_log.get_all_entries()
|
||||
assert len(events) == 1
|
||||
event_args = events[-1]['args']
|
||||
assert event_args['sender'] == creator
|
||||
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = worklock.functions.bid().transact({'from': bidder, 'value': MIN_ALLOWED_BID, 'gas_price': 0})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
tx = token.functions.approve(worklock.address, worklock_supply).transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = worklock.functions.tokenDeposit(worklock_supply).transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
assert worklock.functions.workInfo(bidder).call()[0] > 0
|
||||
tx = worklock.functions.cancelBid().transact({'from': bidder, 'gas_price': 0})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
assert worklock.functions.workInfo(bidder).call()[0] == 0
|
||||
|
||||
# Shutdown after end of bidding
|
||||
now = testerchain.get_blocktime()
|
||||
start_bid_date = now
|
||||
end_bid_date = start_bid_date + ONE_HOUR
|
||||
end_cancellation_date = end_bid_date + ONE_HOUR
|
||||
worklock = worklock_factory(supply=worklock_supply,
|
||||
bidding_delay=0,
|
||||
additional_time_to_cancel=ONE_HOUR,
|
||||
boosting_refund=boosting_refund)
|
||||
shutdown_log = worklock.events.Shutdown.createFilter(fromBlock='latest')
|
||||
do_bids(testerchain, worklock, [bidder], MIN_ALLOWED_BID)
|
||||
|
||||
# Wait end of bidding
|
||||
testerchain.time_travel(seconds=ONE_HOUR)
|
||||
|
||||
# Only owner can do shutdown
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = worklock.functions.shutdown().transact({'from': bidder})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
tx = worklock.functions.shutdown().transact({'from': creator})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
assert worklock.functions.startBidDate().call() == 0
|
||||
assert worklock.functions.endBidDate().call() == 0
|
||||
assert worklock.functions.endCancellationDate().call() > end_cancellation_date
|
||||
assert token.functions.balanceOf(worklock.address).call() == 0
|
||||
assert token.functions.balanceOf(creator).call() == creator_tokens + worklock_supply
|
||||
|
||||
events = shutdown_log.get_all_entries()
|
||||
assert len(events) == 1
|
||||
event_args = events[-1]['args']
|
||||
assert event_args['sender'] == creator
|
||||
|
||||
assert worklock.functions.workInfo(bidder).call()[0] > 0
|
||||
tx = worklock.functions.cancelBid().transact({'from': bidder, 'gas_price': 0})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
assert worklock.functions.workInfo(bidder).call()[0] == 0
|
||||
|
||||
tx = token.functions.approve(worklock.address, worklock_supply).transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = worklock.functions.tokenDeposit(worklock_supply).transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
# Shutdown after end of cancellation window
|
||||
now = testerchain.get_blocktime()
|
||||
start_bid_date = now
|
||||
end_bid_date = start_bid_date + ONE_HOUR
|
||||
end_cancellation_date = end_bid_date
|
||||
worklock = worklock_factory(supply=worklock_supply,
|
||||
bidding_delay=0,
|
||||
additional_time_to_cancel=0,
|
||||
boosting_refund=boosting_refund)
|
||||
shutdown_log = worklock.events.Shutdown.createFilter(fromBlock='latest')
|
||||
do_bids(testerchain, worklock, [bidder], MIN_ALLOWED_BID)
|
||||
|
||||
# Wait end of cancellation window
|
||||
testerchain.time_travel(seconds=ONE_HOUR)
|
||||
|
||||
# Only owner can do shutdown
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = worklock.functions.shutdown().transact({'from': bidder})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = worklock.functions.cancelBid().transact({'from': bidder, 'gas_price': 0})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
tx = worklock.functions.shutdown().transact({'from': creator})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
assert worklock.functions.startBidDate().call() == 0
|
||||
assert worklock.functions.endBidDate().call() == 0
|
||||
assert worklock.functions.endCancellationDate().call() > end_cancellation_date
|
||||
assert token.functions.balanceOf(worklock.address).call() == 0
|
||||
assert token.functions.balanceOf(creator).call() == creator_tokens + worklock_supply
|
||||
|
||||
events = shutdown_log.get_all_entries()
|
||||
assert len(events) == 1
|
||||
event_args = events[-1]['args']
|
||||
assert event_args['sender'] == creator
|
||||
|
||||
assert worklock.functions.workInfo(bidder).call()[0] > 0
|
||||
tx = worklock.functions.cancelBid().transact({'from': bidder, 'gas_price': 0})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
assert worklock.functions.workInfo(bidder).call()[0] == 0
|
||||
|
||||
# Shutdown after enabling of claiming
|
||||
worklock = worklock_factory(supply=worklock_supply,
|
||||
bidding_delay=0,
|
||||
additional_time_to_cancel=0,
|
||||
boosting_refund=boosting_refund)
|
||||
do_bids(testerchain, worklock, [bidder], MIN_ALLOWED_BID)
|
||||
|
||||
# Wait end of cancellation window
|
||||
testerchain.time_travel(seconds=ONE_HOUR)
|
||||
|
||||
tx = worklock.functions.verifyBiddingCorrectness(gas_to_save_state).transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
assert worklock.functions.isClaimingAvailable().call()
|
||||
|
||||
# Can't call shutdown after enabling of claiming
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = worklock.functions.shutdown().transact({'from': creator})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
|
Loading…
Reference in New Issue