WorkLock: method to cancel distribution when not enough bidders

pull/1773/head
vzotova 2020-03-02 21:20:50 +03:00
parent 38ddc5e591
commit 18bffc15c1
2 changed files with 66 additions and 11 deletions

View File

@ -37,6 +37,7 @@ contract WorkLock {
NuCypherToken public token;
StakingEscrow public escrow;
address public creator;
uint256 public startBidDate;
uint256 public endBidDate;
@ -119,6 +120,7 @@ contract WorkLock {
stakingPeriods = _stakingPeriods;
minAllowedBid = _minAllowedBid;
maxAllowableLockedTokens = escrow.maxAllowableLockedTokens();
creator = msg.sender;
}
/**
@ -221,6 +223,14 @@ contract WorkLock {
emit Canceled(msg.sender, refundETH);
}
/**
* @notice Cancels distribution, makes possible to retrieve all bids and creator gets all tokens
*/
function shutdown() internal {
endCancellationDate = uint256(0) - 1; // "infinite" cancellation window
token.safeTransfer(creator, tokenSupply);
}
/**
* @notice Make force refund to bidders who can get tokens more than maximum allowed
* @param _biddersForRefund Sorted list of unique bidders. Only bidders who must receive a refund
@ -231,7 +241,11 @@ contract WorkLock {
uint256 length = _biddersForRefund.length;
require(length > 0, "Must be at least one bidder for a refund");
// TODO quit if can't be adjusted
uint256 minNumberOfBidders = tokenSupply.divCeil(defaultMaxAllowableLockedTokens);
if (bidders.length < minNumberOfBidders) {
shutdown();
return;
}
address previousBidder = _biddersForRefund[0];
uint256 minBid = workInfo[previousBidder].depositedETH;

View File

@ -826,7 +826,7 @@ def test_verifying_correctness(testerchain, token_economics, escrow, deploy_cont
@pytest.mark.slow
def test_force_refund(testerchain, token_economics, deploy_contract, worklock_factory):
def test_force_refund(testerchain, token_economics, deploy_contract, worklock_factory, token):
creator, *bidders = testerchain.w3.eth.accounts
boosting_refund = 100
gas_to_save_state = 30000
@ -842,7 +842,7 @@ def test_force_refund(testerchain, token_economics, deploy_contract, worklock_fa
# Wait end of bidding
testerchain.time_travel(seconds=ONE_HOUR)
bidders = list(sorted(bidders))
bidders = sorted(bidders, key=str.casefold)
# There is no bidders with unacceptable bid
with pytest.raises((TransactionFailed, ValueError)):
tx = worklock.functions.forceRefund([]).transact()
@ -930,24 +930,24 @@ def test_force_refund(testerchain, token_economics, deploy_contract, worklock_fa
testerchain.wait_for_receipt(tx)
# Group of addresses to refund can't include normal bidders
group = list(sorted([normal_bidders[0], whales[1]]))
group = sorted([normal_bidders[0], whales[1]], key=str.casefold)
with pytest.raises((TransactionFailed, ValueError)):
tx = worklock.functions.forceRefund(group).transact()
testerchain.wait_for_receipt(tx)
# Bad input: not unique addresses, nonexistent address, not sorted list
group = list(sorted([whales[1], *whales]))
group = sorted([whales[1], *whales], key=str.casefold)
with pytest.raises((TransactionFailed, ValueError)):
tx = worklock.functions.forceRefund(group).transact()
testerchain.wait_for_receipt(tx)
group = list(sorted([BlockchainInterface.NULL_ADDRESS, *whales]))
group = sorted([BlockchainInterface.NULL_ADDRESS, *whales], key=str.casefold)
with pytest.raises((TransactionFailed, ValueError)):
tx = worklock.functions.forceRefund(group).transact()
testerchain.wait_for_receipt(tx)
group = list(sorted([creator, *whales]))
group = sorted([creator, *whales], key=str.casefold)
with pytest.raises((TransactionFailed, ValueError)):
tx = worklock.functions.forceRefund(group).transact()
testerchain.wait_for_receipt(tx)
group = list(sorted(whales))[::-1]
group = sorted(whales, key=str.casefold)[::-1]
with pytest.raises((TransactionFailed, ValueError)):
tx = worklock.functions.forceRefund(group).transact()
testerchain.wait_for_receipt(tx)
@ -957,7 +957,7 @@ def test_force_refund(testerchain, token_economics, deploy_contract, worklock_fa
whale_3 = whales[2]
whale_2_balance = testerchain.w3.eth.getBalance(whale_2)
whale_3_balance = testerchain.w3.eth.getBalance(whale_3)
group = list(sorted([whale_2, whale_3]))
group = sorted([whale_2, whale_3], key=str.casefold)
tx = worklock.functions.forceRefund(group).transact()
testerchain.wait_for_receipt(tx)
bid = worklock.functions.workInfo(whale_2).call()[0]
@ -996,7 +996,7 @@ def test_force_refund(testerchain, token_economics, deploy_contract, worklock_fa
assert worklock.functions.nextBidderToCheck().call() == 1
# Full force refund
group = list(sorted(whales + hidden_whales))
group = sorted(whales + hidden_whales, key=str.casefold)
balances = [testerchain.w3.eth.getBalance(bidder) for bidder in group]
bids = [worklock.functions.workInfo(bidder).call()[0] for bidder in group]
@ -1053,7 +1053,7 @@ def test_force_refund(testerchain, token_economics, deploy_contract, worklock_fa
# Force refund
whales = bidders[len(small_bids):len(initial_bids)]
whales = list(sorted(whales))
whales = sorted(whales, key=str.casefold)
tx = worklock.functions.forceRefund(whales).transact()
testerchain.wait_for_receipt(tx)
@ -1063,3 +1063,44 @@ def test_force_refund(testerchain, token_economics, deploy_contract, worklock_fa
assert worklock.functions.ethToTokens(bid).call() <= token_economics.maximum_allowed_locked
tx = worklock.functions.verifyBiddingCorrectness(gas_to_save_state).transact()
testerchain.wait_for_receipt(tx)
# Special case: there are less bidders than n, where n is `worklock_supply // maximum_allowed_locked`
worklock_supply = 10 * token_economics.maximum_allowed_locked
worklock = worklock_factory(supply=worklock_supply,
bidding_delay=0,
cancellation_duration=0,
boosting_refund=boosting_refund,
max_bid=max_allowed_bid)
bidders = bidders[0:9]
do_bids(testerchain, worklock, bidders, max_allowed_bid)
# Wait end of bidding
testerchain.time_travel(seconds=ONE_HOUR)
bidder1 = bidders[0]
worklock_tokens = token.functions.balanceOf(worklock.address).call()
creator_tokens = token.functions.balanceOf(creator).call()
bidder1_tokens = token.functions.balanceOf(bidder1).call()
bidders = sorted(bidders, key=str.casefold)
tx = worklock.functions.forceRefund(bidders).transact({'from': bidder1})
testerchain.wait_for_receipt(tx)
end_cancellation_date = worklock.functions.endCancellationDate().call()
now = testerchain.w3.eth.getBlock(block_identifier='latest').timestamp
assert end_cancellation_date > now
assert token.functions.balanceOf(worklock.address).call() == 0
assert token.functions.balanceOf(creator).call() == creator_tokens + worklock_tokens
assert token.functions.balanceOf(bidder1).call() == bidder1_tokens
# Distribution is canceled
with pytest.raises((TransactionFailed, ValueError)):
do_bids(testerchain, worklock, [bidder1], MIN_ALLOWED_BID)
with pytest.raises((TransactionFailed, ValueError)):
tx = worklock.functions.claim().transact({'from': bidder1, 'gas_price': 0})
testerchain.wait_for_receipt(tx)
assert worklock.functions.workInfo(bidder1).call()[0] > 0
tx = worklock.functions.cancelBid().transact({'from': bidder1, 'gas_price': 0})
testerchain.wait_for_receipt(tx)
assert worklock.functions.workInfo(bidder1).call()[0] == 0