def test_auction_final_bid_5(web3, owner, get_bidders, contract_params, token_contract, auction_contract_fast_decline, auction_bid_tested, auction_end_tests, create_accounts): auction = auction_contract_fast_decline token = token_contract(auction.address) auction.transact({'from': owner}).setup(token.address) auction.transact({'from': owner}).startAuction() (A, late_bidder, *bidders) = get_bidders(7) missing_funds = auction.call().missingFundsToEndAuction() amount = missing_funds - 5 auction_bid_tested(auction, A, amount) pre_received_wei = auction.call().received_wei() bidded = 0 for bidder in bidders: # Some parameters decrease the price very fast missing_funds = auction.call().missingFundsToEndAuction() if missing_funds > 0: auction_bid_tested(auction, bidder, 1) bidded += 1 else: break assert auction.call().received_wei() == pre_received_wei + bidded auction.transact({'from': owner}).finalizeAuction() auction_end_tests(auction, late_bidder)
def test_distributor_init(chain, web3, wallet_address, owner, get_bidders, create_contract, contract_params): A = get_bidders(1)[0] Distributor = chain.provider.get_contract_factory('Distributor') Auction = chain.provider.get_contract_factory('DutchAuction') auction = create_contract(Auction, [wallet_address] + contract_params['args'], {'from': owner}) other_auction_params = [wallet_address] + contract_params['args'] other_owner_auction = create_contract(Auction, other_auction_params, {'from': A}) other_contract_type = create_contract(Distributor, [auction.address]) assert owner != A # Fail if no auction address is provided with pytest.raises(TypeError): create_contract(Distributor, []) # Fail if non address-type auction address is provided with pytest.raises(TypeError): create_contract(Distributor, [fake_address]) with pytest.raises(TypeError): create_contract(Distributor, [0x0]) # Fail if auction has another owner with pytest.raises(tester.TransactionFailed): create_contract(Distributor, [other_owner_auction.address]) distributor_contract = create_contract(Distributor, [auction.address])
def test_auction_setup(web3, owner, get_bidders, auction_contract, token_contract, contract_params, event_handler): auction = auction_contract A = get_bidders(2)[0] ev_handler = event_handler(auction) assert auction.call().stage() == 0 # AuctionDeployed assert auction.call().num_tokens_auctioned() == 0 # changeSettings is a private method with pytest.raises(ValueError): auction.transact({'from': owner}).changeSettings(1000, 556, 322) web3.testing.mine(5) token = token_contract(auction.address) txn_hash = auction.transact({'from': owner}).setup(token.address) ev_handler.add(txn_hash, 'Setup') assert auction.call().num_tokens_auctioned() == token.call().balanceOf( auction.address) assert auction.call().token_multiplier() == 10**token.call().decimals() assert auction.call().stage() == 1 # Token cannot be changed after setup with pytest.raises(tester.TransactionFailed): auction.call().setup(token.address) ev_handler.check()
def test_token_allowance(web3, wallet_address, get_bidders, get_token_contract, proxy_contract, decimals): (A, B) = get_bidders(2) multiplier = 10**(decimals) token = get_token_contract( [proxy_contract.address, wallet_address, 5000 * multiplier], decimals=decimals) assert token.call().decimals() == decimals token.transact({'from': wallet_address}).transfer(A, 3000) token.transact({'from': wallet_address}).transfer(B, 2000) with pytest.raises(TypeError): token.call().allowance(0, B) with pytest.raises(TypeError): token.call().allowance(fake_address, B) with pytest.raises(TypeError): token.call().allowance(A, 0) with pytest.raises(TypeError): token.call().allowance(A, fake_address) assert token.call().allowance(A, B) == 0 assert token.call().allowance(B, A) == 0 token.transact({'from': A}).approve(B, 300) assert token.call().allowance(A, B) == 300
def test_token_transfer(chain, web3, wallet_address, get_bidders, get_token_contract, token_contract, proxy_contract, proxy_erc223_contract, decimals, event_handler): (A, B, C) = get_bidders(3) multiplier = 10**(decimals) token = get_token_contract([ proxy_contract.address, wallet_address, 5000 * multiplier, ], decimals=decimals) assert token.call().decimals() == decimals token.transact({'from': wallet_address}).transfer(A, 3000) token.transact({'from': wallet_address}).transfer(B, 2000) token.transact({'from': wallet_address}).transfer(C, 1000) transfer_tests((A, B, C), [3000, 2000, 1000], multiplier, token, event_handler) token_erc223 = token_contract(proxy_erc223_contract.address) token_erc223.transact({'from': wallet_address}).transfer(A, 3000) token_erc223.transact({'from': wallet_address}).transfer(B, 2000) token_erc223.transact({'from': wallet_address}).transfer(C, 1000) transfer_erc223_tests((A, B, C), [3000, 2000, 1000], multiplier, token, proxy_contract, token_erc223, proxy_erc223_contract, event_handler)
def test_auction_final_bid_2( web3, owner, get_bidders, contract_params, token_contract, auction_contract_fast_decline, auction_bid_tested, auction_end_tests ): auction = auction_contract_fast_decline token = token_contract(auction.address) auction.transact({'from': owner}).setup(token.address) auction.transact({'from': owner}).startAuction() (A, B, late_bidder) = get_bidders(3) missing_funds = auction.call().missingFundsToEndAuction() amount = missing_funds - 2 auction_bid_tested(auction, A, amount) with pytest.raises(tester.TransactionFailed): auction_bid_tested(auction, B, 3) # Some parameters decrease the price very fast missing_funds = auction.call().missingFundsToEndAuction() if missing_funds > 0: auction_bid_tested(auction, B, missing_funds) auction.transact({'from': owner}).finalizeAuction() auction_end_tests(auction, late_bidder)
def test_auction_final_bid_1( web3, owner, get_bidders, contract_params, token_contract, auction_contract_fast_decline, auction_bid_tested, auction_end_tests ): auction = auction_contract_fast_decline token = token_contract(auction.address) auction.transact({'from': owner}).setup(token.address) auction.transact({'from': owner}).startAuction() (bidder, late_bidder) = get_bidders(2) missing_funds = auction.call().missingFundsToEndAuction() amount = missing_funds - 1 auction_bid_tested(auction, bidder, amount) # Some parameters decrease the price very fast missing_funds = auction.call().missingFundsToEndAuction() if missing_funds > 0: auction_bid_tested(auction, bidder, 1) auction.transact({'from': owner}).finalizeAuction() auction_end_tests(auction, late_bidder)
def test_auction_final_bid_more( web3, owner, get_bidders, contract_params, token_contract, auction_contract_fast_decline, auction_bid_tested, auction_end_tests ): auction = auction_contract_fast_decline token = token_contract(auction.address) auction.transact({'from': owner}).setup(token.address) auction.transact({'from': owner}).startAuction() (bidder, late_bidder) = get_bidders(2) missing_funds = auction.call().missingFundsToEndAuction() amount = missing_funds + 1 with pytest.raises(tester.TransactionFailed): web3.eth.sendTransaction({ 'from': bidder, 'to': auction.address, 'value': amount }) with pytest.raises(tester.TransactionFailed): auction_bid_tested(auction, bidder, amount)
def test_auction_start(chain, web3, owner, get_bidders, auction_contract_fast_decline, token_contract, auction_bid_tested, auction_end_tests, event_handler): auction = auction_contract_fast_decline token = token_contract(auction.address) ev_handler = event_handler(auction) (A, B) = get_bidders(2) # Should not be able to start auction before setup with pytest.raises(tester.TransactionFailed): auction.transact({'from': owner}).startAuction() txn_hash = auction.transact({'from': owner}).setup(token.address) ev_handler.add(txn_hash, 'Setup') assert auction.call().stage() == 1 token_multiplier = auction.call().token_multiplier() # Should not be able to start auction if not owner with pytest.raises(tester.TransactionFailed): auction.transact({'from': A}).startAuction() txn_hash = auction.transact({'from': owner}).startAuction() receipt = chain.wait.for_receipt(txn_hash) timestamp = web3.eth.getBlock(receipt['blockNumber'])['timestamp'] assert auction.call().stage() == 2 assert auction.call().start_time() == timestamp assert auction.call().start_block() == receipt['blockNumber'] ev_handler.add(txn_hash, 'AuctionStarted', checkAuctionStartedEvent(timestamp, receipt['blockNumber'])) # Should not be able to call start auction after it has already started with pytest.raises(tester.TransactionFailed): auction.transact({'from': owner}).startAuction() amount = web3.eth.getBalance(A) - 10000000 missing_funds = auction.call().missingFundsToEndAuction() # Fails if amount is > missing_funds if (missing_funds < amount): with pytest.raises(tester.TransactionFailed): auction_bid_tested(auction, A, amount) missing_funds = auction.call().missingFundsToEndAuction() auction_bid_tested(auction, A, missing_funds) # Finalize auction assert auction.call().missingFundsToEndAuction() == 0 txn_hash = auction.transact({'from': owner}).finalizeAuction() final_price = auction.call().final_price() ev_handler.add(txn_hash, 'AuctionEnded', checkAuctionEndedEvent(final_price)) auction_end_tests(auction, B) with pytest.raises(tester.TransactionFailed): auction.transact({'from': owner}).startAuction() ev_handler.check()
def test_burn(chain, web3, wallet_address, get_bidders, get_token_contract, proxy_contract, decimals, txnCost, event_handler): decimals = 18 eth = web3.eth (A, B) = get_bidders(2) multiplier = 10**(decimals) initial_supply = 5000 * multiplier token = get_token_contract( [proxy_contract.address, wallet_address, initial_supply], decimals=decimals) assert token.call().decimals() == decimals ev_handler = event_handler(token) token.transact({'from': wallet_address}).transfer(A, 3000) token.transact({'from': wallet_address}).transfer(B, 2000) with pytest.raises(TypeError): token.transact({'from': B}).burn(-3) with pytest.raises(TypeError): token.transact({'from': B}).burn(MAX_UINT + 1) with pytest.raises(tester.TransactionFailed): token.transact({'from': B}).burn(0) with pytest.raises(tester.TransactionFailed): token.transact({'from': B}).burn(2000 + 1) # Balance should not change besides transaction costs tokens_B = token.call().balanceOf(B) balance_B = eth.getBalance(B) burnt = 250 txn_hash = token.transact({'from': B}).burn(burnt) txn_cost = txnCost(txn_hash) ev_handler.add(txn_hash, token_events['burn']) assert token.call().totalSupply() == initial_supply - burnt assert token.call().balanceOf(B) == tokens_B - burnt assert balance_B == eth.getBalance(B) + txn_cost tokens_B = token.call().balanceOf(B) balance_B = eth.getBalance(B) total_supply = token.call().totalSupply() txn_hash = token.transact({'from': B}).burn(tokens_B) txn_cost = txnCost(txn_hash) assert token.call().totalSupply() == total_supply - tokens_B assert token.call().balanceOf(B) == 0 assert balance_B == eth.getBalance(B) + txn_cost ev_handler.check()
def test_auction_whitelist(web3, owner, wallet_address, get_bidders, auction_contract, token_contract, contract_params, event_handler): eth = web3.eth auction = auction_contract (A, B, C, D, E, F) = get_bidders(6) # Initialize token token = token_contract(auction.address) assert auction.call().whitelist(A) == False assert auction.call().whitelist(B) == False assert auction.call().whitelist(C) == False assert auction.call().whitelist(D) == False assert auction.call().whitelist(E) == False # We should be able to whitelist at this point auction.transact({'from': owner}).addToWhitelist([A, B]) assert auction.call().whitelist(A) == True assert auction.call().whitelist(B) == True auction.transact({'from': owner}).setup(token.address) auction.transact({'from': owner}).addToWhitelist([D]) assert auction.call().whitelist(D) == True auction.transact({'from': owner}).startAuction() # Bid more than bid_threshold should fail for E value = auction.call().bid_threshold() + 1 with pytest.raises(tester.TransactionFailed): eth.sendTransaction({'from': E, 'to': auction.address, 'value': value}) with pytest.raises(tester.TransactionFailed): auction.transact({'from': E, "value": value}).bid() auction.transact({'from': owner}).addToWhitelist([E]) assert auction.call().whitelist(E) == True # Bid more than bid_threshold should be ok for E eth.sendTransaction({'from': E, 'to': auction.address, 'value': value}) auction.transact({'from': A, "value": value}).bid() # Test whitelist removal auction.transact({'from': B, "value": value}).bid() auction.transact({'from': owner}).removeFromWhitelist([B]) assert auction.call().whitelist(B) == False with pytest.raises(tester.TransactionFailed): auction.transact({'from': B, "value": value}).bid()
def test_auction_final_bid_0(web3, owner, get_bidders, contract_params, token_contract, auction_contract_fast_decline, auction_bid_tested, auction_end_tests): auction = auction_contract_fast_decline token = token_contract(auction.address) auction.transact({'from': owner}).setup(token.address) auction.transact({'from': owner}).startAuction() (bidder, late_bidder) = get_bidders(2) missing_funds = auction.call().missingFundsToEndAuction() auction_bid_tested(auction, bidder, missing_funds) auction.transact({'from': owner}).finalizeAuction() auction_end_tests(auction, late_bidder)
def test_auction_bid(chain, web3, owner, wallet_address, get_bidders, auction_contract_fast_decline, token_contract, contract_params, txnCost, auction_end_tests, auction_claim_tokens_tested): eth = web3.eth auction = auction_contract_fast_decline (A, B) = get_bidders(2) # Initialize token token = token_contract(auction.address) # Try sending funds before auction starts with pytest.raises(tester.TransactionFailed): eth.sendTransaction({'from': A, 'to': auction.address, 'value': 100}) with pytest.raises(tester.TransactionFailed): auction.transact({'from': A, "value": 100}).bid() auction.transact({'from': owner}).setup(token.address) token_multiplier = auction.call().token_multiplier() auction.transact({'from': owner}).startAuction() # End auction by bidding the needed amount missing_funds = auction.call().missingFundsToEndAuction() # Test fallback function # 76116 gas cost txn_hash = eth.sendTransaction({ 'from': A, 'to': auction.address, 'value': 100 }) receipt = check_succesful_tx(web3, txn_hash) assert auction.call().received_wei() == 100 assert auction.call().bids(A) == 100 missing_funds = auction.call().missingFundsToEndAuction() # 46528 gas cost txn_hash = auction.transact({'from': A, "value": missing_funds}).bid() receipt = check_succesful_tx(web3, txn_hash) assert auction.call().received_wei() == missing_funds + 100 assert auction.call().bids(A) == missing_funds + 100 auction.transact({'from': owner}).finalizeAuction() auction_end_tests(auction, B) # Any payable transactions should fail now with pytest.raises(tester.TransactionFailed): auction.transact({'from': A, "value": 1}).bid() with pytest.raises(tester.TransactionFailed): eth.sendTransaction({'from': A, 'to': auction.address, 'value': 1}) auction_claim_tokens_tested(token, auction, A) assert auction.call().stage() == 4 # TokensDistributed # Any payable transactions should fail now with pytest.raises(tester.TransactionFailed): auction.transact({'from': A, "value": 100}).bid() with pytest.raises(tester.TransactionFailed): eth.sendTransaction({'from': A, 'to': auction.address, 'value': 100})
def test_token_transfer_from(chain, web3, wallet_address, get_bidders, get_token_contract, proxy_contract, decimals, event_handler): (A, B, C) = get_bidders(3) multiplier = 10**(decimals) token = get_token_contract( [proxy_contract.address, wallet_address, 5000 * multiplier], decimals=decimals) assert token.call().decimals() == decimals ev_handler = event_handler(token) token.transact({'from': wallet_address}).transfer(A, 3000) token.transact({'from': wallet_address}).transfer(B, 2000) token.transact({'from': wallet_address}).transfer(C, 1000) txn_hash = token.transact({'from': B}).approve(A, 300) ev_handler.add(txn_hash, token_events['approve']) assert token.call().allowance(B, A) == 300 with pytest.raises(TypeError): token.transact({'from': A}).transferFrom(0, C, 10) with pytest.raises(TypeError): token.transact({'from': A}).transferFrom(B, 0, 10) with pytest.raises(TypeError): token.transact({'from': A}).transferFrom(fake_address, C, 10) with pytest.raises(TypeError): token.transact({'from': A}).transferFrom(B, fake_address, 10) with pytest.raises(TypeError): token.transact({'from': A}).transferFrom(B, C, MAX_UINT + 1) with pytest.raises(TypeError): token.transact({'from': A}).transferFrom(B, C, -5) with pytest.raises(tester.TransactionFailed): allowance_B = token.call().allowance(B, A) token.transact({'from': A}).transferFrom(B, C, allowance_B + 1) # We can allow more than the balance, but we cannot transfer more with pytest.raises(tester.TransactionFailed): balance_B = token.call().balanceOf(B) token.transact({'from': B}).approve(A, balance_B + 10) token.transact({'from': A}).transferFrom(B, C, balance_B + 10) # Test for overflow with pytest.raises(tester.TransactionFailed): balance_B = token.call().balanceOf(B) overflow = MAX_UINT + 1 - balance_B token.transact({'from': B}).approve(A, overflow) token.transact({'from': A}).transferFrom(B, C, overflow) with pytest.raises(tester.TransactionFailed): txn_hash = token.transact({'from': B}).approve(A, 300) txn_hash = token.transact({'from': B}).approve(A, 0) txn_hash = token.transact({'from': B}).approve(A, 300) ev_handler.add(txn_hash, token_events['approve']) assert token.call().allowance(B, A) == 300 balance_A = token.call().balanceOf(A) balance_B = token.call().balanceOf(B) balance_C = token.call().balanceOf(C) txn_hash = token.transact({'from': A}).transferFrom(B, C, 0) ev_handler.add(txn_hash, token_events['transfer']) assert token.call().balanceOf(A) == balance_A assert token.call().balanceOf(B) == balance_B assert token.call().balanceOf(C) == balance_C txn_hash = token.transact({'from': A}).transferFrom(B, C, 150) ev_handler.add(txn_hash, token_events['transfer']) assert token.call().balanceOf(A) == balance_A assert token.call().balanceOf(B) == balance_B - 150 assert token.call().balanceOf(C) == balance_C + 150 ev_handler.check()
def test_distributor_distribute(chain, web3, wallet_address, owner, get_bidders, create_contract, token_contract, auction_contract_fast_decline, auction_bid_tested, auction_claim_tokens_tested, auction_post_distributed_tests, event_handler): bidders = get_bidders(10) auction = auction_contract_fast_decline token = token_contract(auction.address) ev_handler = event_handler(auction) auction.transact({'from': owner}).setup(token.address) auction.transact({'from': owner}).startAuction() Distributor = chain.provider.get_contract_factory('Distributor') distributor = create_contract(Distributor, [auction.address]) collector = ClaimsCollector(auction, token) bidders_number = 0 # Simulate some bids and collect the addresses from the events for bidder in bidders[:-1]: missing = auction.call().missingFundsToEndAuction() balance = web3.eth.getBalance(bidder) cap = (balance - 500000) // 1000000000000000000 amount = min(missing, cap) # print('-- BIDDING', bidder, amount, missing, balance, cap) if (amount > 0): tx_hash = auction_bid_tested(auction, bidder, amount) ev_handler.add(tx_hash, 'BidSubmission', collector.add) ev_handler.check() bidders_number += 1 missing = auction.call().missingFundsToEndAuction() if missing > 0: tx_hash = auction_bid_tested(auction, bidders[-1], missing) ev_handler.add(tx_hash, 'BidSubmission', collector.add) ev_handler.check() bidders_number += 1 assert auction.call().missingFundsToEndAuction() == 0 auction.transact({'from': owner}).finalizeAuction() assert len(collector.addresses) == bidders_number end_time = auction.call().end_time() elapsed = auction.call().token_claim_waiting_period() claim_ok_timestamp = end_time + elapsed + 1 if claim_ok_timestamp > web3.eth.getBlock('latest')['timestamp']: # We cannot claim tokens before waiting period has passed with pytest.raises(tester.TransactionFailed): distributor.transact({ 'from': owner }).distribute(collector.addresses[0:2]) # Simulate time travel web3.testing.timeTravel(claim_ok_timestamp) # Send 5 claiming transactions in a single batch to not run out of gas safe_distribution_no = 5 steps = math.ceil(len(collector.addresses) / safe_distribution_no) # Call the distributor contract with batches of bidder addresses for i in range(0, steps): start = i * safe_distribution_no end = (i + 1) * safe_distribution_no tx_hash = auction_claim_tokens_tested(token, auction, collector.addresses[start:end], distributor) ev_handler.add(tx_hash, 'ClaimedTokens', collector.verify) ev_handler.check() # distributor.transact({'from': owner}).distribute(collector.addresses[start:end]) auction_post_distributed_tests(auction)
def auction_ended( web3, owner, get_bidders, token_contract, auction_contract_fast_decline, auction_bid_tested, auction_end_tests): eth = web3.eth auction = auction_contract_fast_decline bidders = get_bidders(10) # Initialize token token = token_contract(auction.address, {'from': owner}) auction.transact({'from': owner}).setup(token.address) auction.transact({'from': owner}).startAuction() # Set maximum amount for a bid - we don't want 1 account draining the auction missing_funds = auction.call().missingFundsToEndAuction() maxBid = missing_funds / 4 # Bidders start ordering tokens bidders_len = len(bidders) - 1 bidded = 0 # Total bidded amount index = 0 # bidders index # Make some bids with 1 wei to be sure we test rounding errors auction_bid_tested(auction, bidders[0], 1) auction_bid_tested(auction, bidders[1], 1) index = 2 bidded = 2 approx_bid_txn_cost = 4000000 while auction.call().missingFundsToEndAuction() > 0: if bidders_len < index: print('!! Not enough accounts to simulate bidders') bidder = bidders[index] bidder_balance = eth.getBalance(bidder) assert auction.call().bids(bidder) == 0 missing_funds = auction.call().missingFundsToEndAuction() amount = int(min(bidder_balance - approx_bid_txn_cost, maxBid)) if amount <= missing_funds: auction_bid_tested(auction, bidder, amount) else: # Fail if we bid more than missing_funds with pytest.raises(tester.TransactionFailed): auction_bid_tested(auction, bidder, amount) # Bid exactly the amount needed in order to end the auction missing_funds = auction.call().missingFundsToEndAuction() amount = missing_funds auction_bid_tested(auction, bidder, amount) bidded += min(amount, missing_funds) index += 1 print('NO OF BIDDERS', index) print('received_wei / bidded', auction.call().received_wei(), bidded) assert auction.call().received_wei() == bidded auction.transact({'from': owner}).finalizeAuction() auction_end_tests(auction, bidders[index]) return (token, auction)
def test_distributor_distribute(chain, web3, wallet_address, owner, get_bidders, create_contract, token_contract, auction_contract_fast_decline, auction_bid_tested, auction_claim_tokens_tested, auction_post_distributed_tests): bidders = get_bidders(10) auction = auction_contract_fast_decline token = token_contract(auction.address) auction.transact({'from': owner}).setup(token.address) auction.transact({'from': owner}).startAuction() Distributor = chain.provider.get_contract_factory('Distributor') distributor = create_contract(Distributor, [auction.address]) # Retrieve bidder addresses from contract bid events def get_bidders_addresses(event): address = event['args']['_sender'] if address not in addresses: addresses.append(address) values.append(0) index = len(addresses) - 1 else: index = addresses.index(address) values[index] += event['args']['_amount'] def verify_claim(event): addr = event['args']['_recipient'] sent_amount = event['args']['_sent_amount'] # Check for double claiming assert addr not in verified_claim assert auction.call().bids(addr) == 0 assert sent_amount == token.call().balanceOf(addr) verified_claim.append(address) for bidder in bidders: missing = auction.call().missingFundsToEndAuction() balance = web3.eth.getBalance(bidder) amount = min(missing, balance - 500000) if (amount > 0): print('-- BIDDING', amount, missing, balance) auction_bid_tested(auction, bidder, amount) assert auction.call().missingFundsToEndAuction() == 0 auction.transact({'from': owner}).finalizeAuction() addresses = [] values = [] claimed = [] verified_claim = [] handle_logs(contract=auction, event='BidSubmission', callback=get_bidders_addresses) with pytest.raises(tester.TransactionFailed): distributor.transact({'from': owner}).distribute(addresses[0:2]) end_time = auction.call().end_time() elapsed = auction.call().token_claim_waiting_period() web3.testing.timeTravel(end_time + elapsed + 1) # Send 5 claiming transactions in a single batch to not run out of gas safe_distribution_no = 5 steps = math.ceil(len(addresses) / safe_distribution_no) # Call the distributor contract with batches of bidder addresses for i in range(0, steps): start = i * safe_distribution_no end = (i + 1) * safe_distribution_no auction_claim_tokens_tested(token, auction, addresses[start:end], distributor) # distributor.transact({'from': owner}).distribute(addresses[start:end]) auction_post_distributed_tests(auction) # Verify that a single "ClaimedTokens" event has been issued by the auction contract # for each address for j in range(0, len(addresses) - 1): address = addresses[j] assert auction.call().bids(address) == 0 # check if auction event was triggered for this user handle_logs(contract=auction, event='ClaimedTokens', params={'filter': { '_recipient': address }}, callback=verify_claim)
def test_auction_simulation(chain, web3, owner, get_bidders, auction_contract, token_contract, contract_params, auction_bid_tested, auction_end_tests, auction_post_distributed_tests, auction_claim_tokens_tested, create_accounts, txnCost, event_handler): eth = web3.eth auction = auction_contract ev_handler = event_handler(auction) bidders = get_bidders(12) # Initialize token token = token_contract(auction.address) # Initial Auction state assert auction.call().stage() == 0 # AuctionDeployed assert eth.getBalance(auction.address) == 0 assert auction.call().received_wei() == 0 # Auction setup without being the owner should fail with pytest.raises(tester.TransactionFailed): auction.transact({'from': bidders[1]}).setup(token.address) txn_hash = auction.transact({'from': owner}).setup(token.address) ev_handler.add(txn_hash, 'Setup') assert auction.call().stage() == 1 # AuctionSetUp token_multiplier = auction.call().token_multiplier() # We want to revert to these, because we set them in the fixtures initial_args = [ auction.call().price_start(), auction.call().price_constant(), auction.call().price_exponent() ] # changeSettings is a private method with pytest.raises(ValueError): auction.transact({'from': owner}).changeSettings(1000, 556, 322) # startAuction without being the owner should fail with pytest.raises(tester.TransactionFailed): auction.transact({'from': bidders[1]}).startAuction() auction.transact({'from': owner}).startAuction() assert auction.call().stage() == 2 # AuctionStarted # transferFundsToToken should fail (private) with pytest.raises(ValueError): auction.transact({'from': bidders[1]}).transferFundsToToken() # finalizeAuction should fail (missing funds not 0) with pytest.raises(tester.TransactionFailed): auction.transact({'from': bidders[1]}).finalizeAuction() with pytest.raises(tester.TransactionFailed): auction.transact({'from': owner}).finalizeAuction() # Set maximum amount for a bid - we don't want 1 account draining the auction missing_funds = auction.call().missingFundsToEndAuction() maxBid = missing_funds / 4 # TODO Test multiple orders from 1 buyer # Bidders start ordering tokens bidders_len = len(bidders) - 1 bidded = 0 # Total bidded amount index = 0 # bidders index # Make some bids with 1 wei to be sure we test rounding errors txn_hash = auction_bid_tested(auction, bidders[0], 1) ev_handler.add(txn_hash, 'BidSubmission', checkBidEvent(bidders[0], 1, missing_funds)) missing_funds = auction.call().missingFundsToEndAuction() txn_hash = auction_bid_tested(auction, bidders[1], 1) ev_handler.add(txn_hash, 'BidSubmission', checkBidEvent(bidders[1], 1, missing_funds)) index = 2 bidded = 2 approx_bid_txn_cost = 4000000 while auction.call().missingFundsToEndAuction() > 0: if bidders_len < index: new_account = create_accounts(1)[0] bidders.append(new_account) bidders_len += 1 print('Creating 1 additional bidder account', new_account) bidder = bidders[index] bidder_balance = eth.getBalance(bidder) assert auction.call().bids(bidder) == 0 missing_funds = auction.call().missingFundsToEndAuction() amount = int(min(bidder_balance - approx_bid_txn_cost, maxBid)) if amount <= missing_funds: txn_hash = auction.transact({ 'from': bidder, "value": amount }).bid() else: # Fail if we bid more than missing_funds with pytest.raises(tester.TransactionFailed): auction.transact({'from': bidder, "value": amount}).bid() # Bid exactly the amount needed in order to end the auction amount = missing_funds txn_hash = auction.transact({ 'from': bidder, "value": amount }).bid() assert auction.call().bids(bidder) == amount ev_handler.add(txn_hash, 'BidSubmission', checkBidEvent(bidder, amount, missing_funds)) txn_cost = txnCost(txn_hash) post_balance = bidder_balance - amount - txn_cost bidded += min(amount, missing_funds) assert eth.getBalance(bidder) == post_balance index += 1 print('NO OF BIDDERS', index) # Auction ended, no more orders possible if bidders_len < index: print( '!! Not enough accounts to simulate bidders. 1 additional account needed' ) # Finalize Auction txn_hash = auction.transact({'from': owner}).finalizeAuction() # Final price per TKN (Tei * token_multiplier) final_price = auction.call().final_price() # Make sure events are issued correctly ev_handler.add(txn_hash, 'AuctionEnded', checkAuctionEndedEvent(final_price)) with pytest.raises(tester.TransactionFailed): auction.transact({'from': owner}).finalizeAuction() assert auction.call().received_wei() == bidded auction_end_tests(auction, bidders[index]) # Claim all tokens funds_at_price = auction.call().num_tokens_auctioned( ) * final_price // token_multiplier received_wei = auction.call().received_wei() # FIXME sometimes: assert 5000000002 == 5000000000 assert received_wei == funds_at_price # Total Tei claimable total_tokens_claimable = auction.call().received_wei( ) * token_multiplier // final_price print('FINAL PRICE', final_price) print('TOTAL TOKENS CLAIMABLE', int(total_tokens_claimable)) # FIXME assert 5000000002000000000000000 == 5000000000000000000000000 assert int(total_tokens_claimable) == auction.call().num_tokens_auctioned() rounding_error_tokens = 0 end_time = auction.call().end_time() elapsed = auction.call().token_claim_waiting_period() claim_ok_timestamp = end_time + elapsed + 1 # We cannot claim tokens before waiting period has passed if claim_ok_timestamp > web3.eth.getBlock('latest')['timestamp']: with pytest.raises(tester.TransactionFailed): auction_claim_tokens_tested(token, auction, bidders[0]) # Simulate time travel web3.testing.timeTravel(claim_ok_timestamp) for i in range(0, index): bidder = bidders[i] tokens_expected = token_multiplier * auction.call().bids( bidder) // final_price txn_hash = auction_claim_tokens_tested(token, auction, bidder) ev_handler.add(txn_hash, 'ClaimedTokens', checkClaimedTokensEvent(bidder, tokens_expected)) # If auction funds not transferred to owner (last claimTokens) # we test for a correct claimed tokens calculation balance_auction = auction.call().received_wei() if balance_auction > 0: # Auction supply = unclaimed tokens, including rounding errors unclaimed_token_supply = token.call().balanceOf(auction.address) # Calculated unclaimed tokens unclaimed_funds = balance_auction - auction.call().funds_claimed() unclaimed_tokens = token_multiplier * unclaimed_funds // auction.call( ).final_price() # Adding previous rounding errors unclaimed_tokens += rounding_error_tokens # Token's auction balance should be the same as # the unclaimed tokens calculation based on the final_price # We assume a rounding error of 1 if unclaimed_token_supply != unclaimed_tokens: rounding_error_tokens += 1 unclaimed_tokens += 1 # FIXME assert 4999999999000000000000000 == 5000000001000000000000001 assert unclaimed_token_supply == unclaimed_tokens # Auction balance might be > 0 due to rounding errors assert token.call().balanceOf(auction.address) == rounding_error_tokens print('FINAL UNCLAIMED TOKENS', rounding_error_tokens) # Last claimTokens also triggers a TokensDistributed event ev_handler.add(txn_hash, 'TokensDistributed') auction_post_distributed_tests(auction) # Check if all registered events have been triggered ev_handler.check()
def test_auction_bid_from_gnosis_multisig( web3, owner, wallet_address, gnosis_multisig_wallet, get_bidders, auction_contract_fast_decline, token_contract, event_handler): eth = web3.eth auction = auction_contract_fast_decline (A, B, C) = get_bidders(3) # Initialize token token = token_contract(auction.address) auction.transact({'from': owner}).setup(token.address) auction.transact({'from': owner}).startAuction() gnosis_wallet1 = gnosis_multisig_wallet([A, B, C], 1) gnosis_wallet2 = gnosis_multisig_wallet([A, B, C], 2) gnosis_wallet1_balance = 100000 gnosis_wallet2_balance = 200000 web3.eth.sendTransaction({ 'from': B, 'to': gnosis_wallet1.address, 'value': gnosis_wallet1_balance }) web3.eth.sendTransaction({ 'from': B, 'to': gnosis_wallet2.address, 'value': gnosis_wallet2_balance }) # Test gnosis wallet with 2 owners and 1 confirmation # Using Auction's fallback function pre_balance_wallet = web3.eth.getBalance(wallet_address) gnosis_wallet1.transact({'from': A}).submitTransaction(auction.address, 1000, bytearray()) gnosis_wallet1_balance -= 1000 assert web3.eth.getBalance(gnosis_wallet1.address) == gnosis_wallet1_balance assert auction.call().bids(gnosis_wallet1.address) == 1000 assert web3.eth.getBalance(wallet_address) == pre_balance_wallet + 1000 # Test gnosis wallet with 2 owners and 1 confirmation # Using Auction's bid() function pre_balance_wallet = web3.eth.getBalance(wallet_address) data = function_signature_to_4byte_selector('bid()') gnosis_wallet1.transact({'from': A}).submitTransaction(auction.address, 1000, data) gnosis_wallet1_balance -= 1000 assert web3.eth.getBalance(gnosis_wallet1.address) == gnosis_wallet1_balance assert auction.call().bids(gnosis_wallet1.address) == 2000 assert web3.eth.getBalance(wallet_address) == pre_balance_wallet + 1000 transaction_id_keeper = TransactionIdKeeper() # Test gnosis wallet with 3 owners and 2 confirmations # Using Auction's fallback function pre_balance_wallet = web3.eth.getBalance(wallet_address) txhash = gnosis_wallet2.transact({'from': A}).submitTransaction(auction.address, 3000, bytearray()) assert web3.eth.getBalance(gnosis_wallet2.address) == gnosis_wallet2_balance assert auction.call().bids(gnosis_wallet2.address) == 0 assert web3.eth.getBalance(wallet_address) == pre_balance_wallet # Wait for transactionId from the Confirmation event ev_handler = event_handler(gnosis_wallet2) ev_handler.add(txhash, 'Confirmation', transaction_id_keeper.add) ev_handler.check() # Second owner confirms the transaction transaction_id = transaction_id_keeper.transaction_id gnosis_wallet2.transact({'from': B}).confirmTransaction(transaction_id) gnosis_wallet2_balance -= 3000 assert web3.eth.getBalance(gnosis_wallet2.address) == gnosis_wallet2_balance assert auction.call().bids(gnosis_wallet2.address) == 3000 assert web3.eth.getBalance(wallet_address) == pre_balance_wallet + 3000 # Test gnosis wallet with 3 owners and 2 confirmations # Using Auction's bid() function pre_balance_wallet = web3.eth.getBalance(wallet_address) data = function_signature_to_4byte_selector('bid()') txhash1 = gnosis_wallet2.transact({'from': A}).submitTransaction(auction.address, 3000, data) # Second owner confirms the transaction ev_handler.add(txhash1, 'Confirmation', transaction_id_keeper.add) ev_handler.check() transaction_id = transaction_id_keeper.transaction_id gnosis_wallet2.transact({'from': B}).confirmTransaction(transaction_id) gnosis_wallet2_balance -= 3000 assert web3.eth.getBalance(gnosis_wallet2.address) == gnosis_wallet2_balance assert auction.call().bids(gnosis_wallet2.address) == 6000 assert web3.eth.getBalance(wallet_address) == pre_balance_wallet + 3000
def test_auction_bid(chain, web3, owner, wallet_address, get_bidders, auction_contract_fast_decline, token_contract, contract_params, txnCost, auction_end_tests, auction_claim_tokens_tested, event_handler): eth = web3.eth auction = auction_contract_fast_decline ev_handler = event_handler(auction) (A, B) = get_bidders(2) # Initialize token token = token_contract(auction.address) # Try sending funds before auction starts with pytest.raises(tester.TransactionFailed): eth.sendTransaction({'from': A, 'to': auction.address, 'value': 100}) with pytest.raises(tester.TransactionFailed): auction.transact({'from': A, "value": 100}).bid() txn_hash = auction.transact({'from': owner}).setup(token.address) ev_handler.add(txn_hash, 'Setup') token_multiplier = auction.call().token_multiplier() txn_hash = auction.transact({'from': owner}).startAuction() ev_handler.add(txn_hash, 'AuctionStarted') missing_funds = auction.call().missingFundsToEndAuction() # Test fallback function # 76116 gas cost txn_hash = eth.sendTransaction({ 'from': A, 'to': auction.address, 'value': 100 }) ev_handler.add(txn_hash, 'BidSubmission', checkBidEvent(A, 100, missing_funds)) assert auction.call().received_wei() == 100 assert auction.call().bids(A) == 100 # End auction by bidding the needed amount missing_funds = auction.call().missingFundsToEndAuction() # 46528 gas cost txn_hash2 = auction.transact({'from': A, "value": missing_funds}).bid() ev_handler.add(txn_hash2, 'BidSubmission', checkBidEvent(A, missing_funds, missing_funds)) assert auction.call().received_wei() == missing_funds + 100 assert auction.call().bids(A) == missing_funds + 100 auction.transact({'from': owner}).finalizeAuction() auction_end_tests(auction, B) # Any payable transactions should fail now with pytest.raises(tester.TransactionFailed): auction.transact({'from': A, "value": 1}).bid() with pytest.raises(tester.TransactionFailed): eth.sendTransaction({'from': A, 'to': auction.address, 'value': 1}) end_time = auction.call().end_time() elapsed = auction.call().token_claim_waiting_period() claim_ok_timestamp = end_time + elapsed + 1 # We cannot claim tokens before waiting period has passed if claim_ok_timestamp > web3.eth.getBlock('latest')['timestamp']: with pytest.raises(tester.TransactionFailed): auction_claim_tokens_tested(token, auction, A) # Simulate time travel web3.testing.timeTravel(claim_ok_timestamp) auction_claim_tokens_tested(token, auction, A) assert auction.call().stage() == 4 # TokensDistributed # Any payable transactions should fail now with pytest.raises(tester.TransactionFailed): auction.transact({'from': A, "value": 100}).bid() with pytest.raises(tester.TransactionFailed): eth.sendTransaction({'from': A, 'to': auction.address, 'value': 100}) ev_handler.check()
def test_auction_bid_from_multisig(web3, owner, wallet_address, gnosis_multisig_wallet, get_bidders, auction_contract_fast_decline, token_contract, event_handler): eth = web3.eth auction = auction_contract_fast_decline (A, B, C) = get_bidders(3) # Initialize token token = token_contract(auction.address) auction.transact({'from': owner}).setup(token.address) auction.transact({'from': owner}).startAuction() gnosis_wallet1 = gnosis_multisig_wallet([A, B, C], 1) gnosis_wallet2 = gnosis_multisig_wallet([A, B, C], 2) web3.eth.sendTransaction({ 'from': B, 'to': gnosis_wallet1.address, 'value': 100000 }) web3.eth.sendTransaction({ 'from': B, 'to': gnosis_wallet2.address, 'value': 200000 }) pre_balance_wallet = web3.eth.getBalance(wallet_address) # Test gnosis wallet with 2 owners and 1 confirmation gnosis_wallet1.transact({ 'from': A }).submitTransaction(auction.address, 1000, bytearray()) assert web3.eth.getBalance(gnosis_wallet1.address) == 100000 - 1000 assert auction.call().bids(gnosis_wallet1.address) == 1000 assert web3.eth.getBalance(wallet_address) == pre_balance_wallet + 1000 transactionId = None def setId(event): nonlocal transactionId transactionId = event['args']['transactionId'] # Test gnosis wallet with 3 owners and 2 confirmations pre_balance_wallet = web3.eth.getBalance(wallet_address) txhash = gnosis_wallet2.transact({ 'from': A }).submitTransaction(auction.address, 3000, bytearray()) assert web3.eth.getBalance(gnosis_wallet2.address) == 200000 assert auction.call().bids(gnosis_wallet2.address) == 0 assert web3.eth.getBalance(wallet_address) == pre_balance_wallet # Wait for transactionId from the Confirmation event ev_handler = event_handler(gnosis_wallet2) ev_handler.add(txhash, 'Confirmation', setId) ev_handler.check() # Second owner confirms the transaction gnosis_wallet2.transact({'from': B}).confirmTransaction(transactionId) assert web3.eth.getBalance(gnosis_wallet2.address) == 200000 - 3000 assert auction.call().bids(gnosis_wallet2.address) == 3000 assert web3.eth.getBalance(wallet_address) == pre_balance_wallet + 3000
def test_auction_whitelist( chain, web3, owner, wallet_address, whitelister_address, get_bidders, create_contract, token_contract, contract_params, event_handler): eth = web3.eth (A, B, C, D, E, F) = get_bidders(6) Auction = chain.provider.get_contract_factory('DutchAuction') args = [wallet_address, whitelister_address, 2 * 10 ** 18, 1574640000, 3] auction = create_contract(Auction, args, {'from': owner}) # Initialize token token = token_contract(auction.address) bid_threshold = auction.call().bid_threshold() assert auction.call().whitelist(A) == False assert auction.call().whitelist(B) == False assert auction.call().whitelist(C) == False assert auction.call().whitelist(D) == False assert auction.call().whitelist(E) == False # Only the whitelister_address can add addresses to the whitelist with pytest.raises(tester.TransactionFailed): auction.transact({'from': owner}).addToWhitelist([A, B]) with pytest.raises(tester.TransactionFailed): auction.transact({'from': wallet_address}).addToWhitelist([A, B]) with pytest.raises(tester.TransactionFailed): auction.transact({'from': A}).addToWhitelist([A, B]) with pytest.raises(tester.TransactionFailed): auction.transact({'from': C}).addToWhitelist([A, B]) # We should be able to whitelist at this point auction.transact({'from': whitelister_address}).addToWhitelist([A, B]) assert auction.call().whitelist(A) == True assert auction.call().whitelist(B) == True auction.transact({'from': owner}).setup(token.address) auction.transact({'from': whitelister_address}).addToWhitelist([D]) assert auction.call().whitelist(D) == True auction.transact({'from': owner}).startAuction() # Bid more than bid_threshold should fail for E value = bid_threshold + 1 with pytest.raises(tester.TransactionFailed): eth.sendTransaction({ 'from': E, 'to': auction.address, 'value': value }) with pytest.raises(tester.TransactionFailed): auction.transact({'from': E, "value": value}).bid() auction.transact({'from': whitelister_address}).addToWhitelist([E]) assert auction.call().whitelist(E) == True print('--- web3.eth.getBalance(E)-- ', web3.eth.getBalance(E)) print('--- value -- ', value) print('--- bids -- ', auction.call().bids(E)) assert web3.eth.getBalance(E) > value # Bid more than bid_threshold should be ok for E eth.sendTransaction({ 'from': E, 'to': auction.address, 'value': value }) auction.transact({'from': A, "value": value}).bid() # Test whitelist removal auction.transact({'from': B, "value": value}).bid() # Only the whitelister_address can add addresses to the whitelist with pytest.raises(tester.TransactionFailed): auction.transact({'from': owner}).removeFromWhitelist([B]) with pytest.raises(tester.TransactionFailed): auction.transact({'from': wallet_address}).removeFromWhitelist([B]) with pytest.raises(tester.TransactionFailed): auction.transact({'from': A}).removeFromWhitelist([B]) with pytest.raises(tester.TransactionFailed): auction.transact({'from': C}).removeFromWhitelist([B]) auction.transact({'from': whitelister_address}).removeFromWhitelist([B]) assert auction.call().whitelist(B) == False with pytest.raises(tester.TransactionFailed): auction.transact({'from': B, "value": value}).bid()