def xxx_test_upgrade_wallet(web3: Web3): # Check we get somewhat valid ids contract_def = get_compiled_contract_cached("OwnedRegistrar") registrar_contract, txid = deploy_contract(web3, contract_def) # Deploy wallet contract body contract_def = get_compiled_contract_cached("Wallet") wallet_contract, txid = deploy_contract(web3, contract_def) # Register wallet contract body txid = registrar_contract.transact().setAddr("wallet", wallet_contract.address) confirm_transaction(web3, txid) # Deploy relay against the registered wallet contract_def = get_compiled_contract_cached("Relay") relay, txid = deploy_contract( web3, contract_def, constructor_arguments=[registrar_contract.address, "wallet"]) # Test relayed wallet. We use Wallet ABI # against Relay contract. contract_def = get_compiled_contract_cached("Wallet") relayed_wallet = get_contract(web3, contract_def, relay.address) assert relayed_wallet.call().version() == "1.0" # # Upgrade wallet # # Deploy wallet contract body contract_def = get_compiled_contract_cached("Wallet") wallet_contract, txid = deploy_contract(web3, contract_def)
def test_event_fund_wallet(web3, hosted_wallet): """Send some funds int the wallet and see that we get the event of the deposit.""" listener, events = create_contract_listener(hosted_wallet.contract) # value = get_wallet_balance(testnet_wallet_contract_address) txid = send_balance_to_contract(hosted_wallet, TEST_VALUE) confirm_transaction(web3, txid) update_count = listener.poll() assert update_count == 1 assert len(events) == 1 event_name, input_data = events[0] assert event_name == "Deposit" assert input_data["value"] == to_wei(TEST_VALUE) # Deposit some more, should generate one new event txid = send_balance_to_contract(hosted_wallet, TEST_VALUE) confirm_transaction(web3, txid) update_count = listener.poll() assert update_count == 1 assert len(events) == 2 event_name, input_data = events[1] assert event_name == "Deposit" assert input_data["value"] == to_wei(TEST_VALUE)
def test_event_withdraw_wallet(web3, topped_up_hosted_wallet, coinbase): """Withdraw funds from the wallet and see that we get the event of the deposit.""" hosted_wallet = topped_up_hosted_wallet coinbase_address = coinbase listener, events = create_contract_listener(hosted_wallet.contract) # Do a withdraw from wallet txid = hosted_wallet.withdraw(coinbase_address, TEST_VALUE) confirm_transaction(web3, txid) # Wallet contract should generate events if the withdraw succeeded or not update_count = listener.poll() assert update_count == 1 assert len(events) == 1 event_name, input_data = events[0] assert event_name == "Withdraw" assert input_data["value"] == to_wei(TEST_VALUE) assert input_data["to"] == coinbase_address # Deposit some more, should generate one new event txid = hosted_wallet.withdraw(coinbase_address, TEST_VALUE) confirm_transaction(web3, txid) update_count = listener.poll() assert update_count == 1 assert event_name == "Withdraw" assert input_data["value"] == to_wei(TEST_VALUE) assert input_data["to"] == coinbase_address
def test_event_claim_fees(web3, topped_up_hosted_wallet, coinbase): """We correctly can claim transaction fees from the hosted wallet contract.""" hosted_wallet = topped_up_hosted_wallet coinbase_address = coinbase # Do a withdraw to cause some fees listener, events = create_contract_listener(hosted_wallet.contract) assert hosted_wallet.get_balance() > TEST_VALUE txid = hosted_wallet.withdraw(coinbase_address, TEST_VALUE) confirm_transaction(web3, txid) # Claim fees for the withdraw operation claim_txid, price = hosted_wallet.claim_fees(txid) confirm_transaction(web3, claim_txid) # We should have event for withdraw + claims update_count = listener.poll() assert update_count == 2 assert len(events) == 2 event_name, input_data = events[-1] # Fee claim event assert event_name == "ClaimFee" assert input_data["txid"] == txid_to_bin( txid) # This was correctly targeted to original withdraw assert input_data["value"] == to_wei(price) # We claimed correct amount
def test_event_send_tokens(web3, hosted_wallet, token, coinbase): """Hosted wallet sends tokens.""" # BBB token = token.contract # Top up hosted wallet with tokens txid = token.transact().transfer(hosted_wallet.address, 4000) confirm_transaction(web3, txid) # Prepare event listener listener, events = create_contract_listener(token) listener.poll() # Transfer tokens back # Do a withdraw from wallet txid = hosted_wallet.execute(token, "transfer", args=[coinbase, 4000]) confirm_transaction(web3, txid) update_count = listener.poll() # Check the transfer event arrives assert update_count == 1 assert len(events) == 1 event_name, input_data = events[0] assert event_name == "Transfer" assert input_data["value"] == 4000 assert input_data["to"] == coinbase assert input_data["from"] == hosted_wallet.address # Check balances token.call().balanceOf(coinbase) == 10000 token.call().balanceOf(hosted_wallet.address) == 0
def test_fund_crowdsale(web3, hosted_wallet, token): """When we fund crowdsale tokens get created.""" contract = token.contract test_fund = Decimal(1000) # ETH listener, events = create_contract_listener(contract) contract.call().investorCount() == 0 txid = send_balance_to_contract(token.contract, test_fund, gas=1000000) confirm_transaction(web3, txid) update_count = listener.poll() # receipt = web3.eth.getTransactionReceipt(txid) # print("Gas used by funding is ", receipt["cumulativeGas"]) # Check the transfer event arrives assert update_count == 2 # Buy + Transfer assert len(events) == 2 event_name, input_data = events[0] assert event_name == "Buy" assert input_data["eth"] == to_wei(test_fund, "ether") assert input_data["tokens"] == 2000 # Check balances web3.eth.getBalance(MULTISIG) == to_wei(test_fund * 2, "ether") contract.call().weiRaised() == to_wei(test_fund, "ether") contract.call().investorCount() == 1
def xxx_test_upgrade_wallet(web3: Web3): # Check we get somewhat valid ids contract_def = get_compiled_contract_cached("OwnedRegistrar") registrar_contract, txid = deploy_contract(web3, contract_def) # Deploy wallet contract body contract_def = get_compiled_contract_cached("Wallet") wallet_contract, txid = deploy_contract(web3, contract_def) # Register wallet contract body txid = registrar_contract.transact().setAddr("wallet", wallet_contract.address) confirm_transaction(web3, txid) # Deploy relay against the registered wallet contract_def = get_compiled_contract_cached("Relay") relay, txid = deploy_contract(web3, contract_def, constructor_arguments=[registrar_contract.address, "wallet"]) # Test relayed wallet. We use Wallet ABI # against Relay contract. contract_def = get_compiled_contract_cached("Wallet") relayed_wallet = get_contract(web3, contract_def, relay.address) assert relayed_wallet.call().version() == "1.0" # # Upgrade wallet # # Deploy wallet contract body contract_def = get_compiled_contract_cached("Wallet") wallet_contract, txid = deploy_contract(web3, contract_def)
def test_contract_abi(web3, simple_test_contract: Contract): """Check that we can manipulate test contract from coinbase address.""" # First check we can manipulate wallet from the coinbase address txid = simple_test_contract.transact().setValue(1) confirm_transaction(web3, txid) assert simple_test_contract.call().value() == 1
def test_call_contract(web3: Web3, hosted_wallet, simple_test_contract: Contract): """Call a test contract from the hosted wallet and see the value is correctly set.""" magic = random.randint(0, 2**30) txid = hosted_wallet.execute(simple_test_contract, "setValue", args=[magic]) confirm_transaction(web3, txid) assert simple_test_contract.call().value() == magic
def test_cap(web3, hosted_wallet, token, coinbase): """Transactions that would exceed cap is rejected.""" test_fund = wei_to_eth(CAP + 1) txid = send_balance_to_contract(token.contract, test_fund, gas=1000000) with pytest.raises(TransactionConfirmationError): confirm_transaction(web3, txid)
def test_deposit_eth(dbsession, eth_network_id, web3, eth_service, coinbase, deposit_address): """Accept incoming deposit.""" # Do a transaction over ETH network txid = send_balance_to_address(web3, deposit_address, TEST_VALUE) confirm_transaction(web3, txid) success_op_count, failed_op_count = eth_service.run_listener_operations() assert success_op_count == 1 assert failed_op_count == 0 # We get a operation, which is not resolved yet due to block confirmation numbers with transaction.manager: # Account not yet updated address = dbsession.query(CryptoAddress).filter_by(address=eth_address_to_bin(deposit_address)).one() eth_asset = get_ether_asset(dbsession) assert address.get_account(eth_asset).account.get_balance() == 0 # We have one ETH account on this address assert address.crypto_address_accounts.count() == 1 # We have one pending operation ops = list(dbsession.query(CryptoOperation).all()) assert len(ops) == 2 # Create + deposit op = ops[-1] assert isinstance(op, CryptoAddressDeposit) assert op.holding_account.get_balance() == TEST_VALUE assert op.completed_at is None opid = op.id # Wait that we reach critical confirmation count wait_for_op_confirmations(eth_service, opid) # Now account shoult have been settled with transaction.manager: # We have one complete operation ops = list(dbsession.query(CryptoOperation).all()) assert len(ops) == 2 # Create + deposit op = ops[-1] assert isinstance(op, CryptoAddressDeposit) assert op.holding_account.get_balance() == 0 assert op.completed_at is not None address = dbsession.query(CryptoAddress).filter_by(address=eth_address_to_bin(deposit_address)).one() # We have one ETH account on this address assert address.crypto_address_accounts.count() == 1 # We have one credited account eth_asset = get_ether_asset(dbsession) caccount = address.get_account(eth_asset) assert caccount.account.get_balance() == TEST_VALUE assert op.state == CryptoOperationState.success
def test_starter_eth(dbsession, registry, eth_network_id, web3: Web3, eth_service: EthereumService, house_address, starter_eth): """Test the user gets some starter ETH when signing up.""" with transaction.manager: user = create_user(dbsession, registry) setup_user_account(user, do_mainnet=True) # Let all the events completed success, fail = eth_service.run_event_cycle() assert success >= 1 assert fail == 0 # When op is confirmed, the user account is correctly credited with transaction.manager: user = dbsession.query(User).first() txid = user.user_data["starter_asset_txs"][0]["eth"] confirm_transaction(web3, txid) # Let the transfer come through eth_service.run_event_cycle() with transaction.manager: user = dbsession.query(User).first() client = get_rpc_client(web3) asset = get_ether_asset(dbsession) ua = user.owned_crypto_addresses.first() address = bin_to_eth_address(ua.address.address) # Sanity check we get events from starter deposit logs = client.get_logs(from_block=0, address=[address]) ops = list(user.owned_crypto_operations) # The event was processed on log level assert len(logs) == 1 # The last user operation is deposit depo = ops[-1] assert isinstance(depo.crypto_operation, CryptoAddressDeposit) opid = depo.crypto_operation.id # Wait deposit to confirm wait_for_op_confirmations(eth_service, opid) with transaction.manager: # User ETH balance is expected asset = get_ether_asset(dbsession) user = dbsession.query(User).first() ua = user.owned_crypto_addresses.first() caccout = ua.address.get_account(asset) assert caccout.account.get_balance() == Decimal("0.1")
def test_starter_eth(dbsession, registry, eth_network_id, web3: Web3, eth_service: EthereumService, house_address, starter_eth): """Test the user gets some starter ETH when signing up.""" with transaction.manager: user = create_user(dbsession, registry) setup_user_account(user) # Let all the events completed success, fail = eth_service.run_event_cycle() assert success >= 1 assert fail == 0 # When op is confirmed, the user account is correctly credited with transaction.manager: user = dbsession.query(User).first() txid = user.user_data["starter_asset_txs"][0]["eth"] confirm_transaction(web3, txid) # Let the transfer come through eth_service.run_event_cycle() with transaction.manager: user = dbsession.query(User).first() client = get_rpc_client(web3) asset = get_ether_asset(dbsession) ua = user.owned_crypto_addresses.first() address = bin_to_eth_address(ua.address.address) # Sanity check we get events from starter deposit logs = client.get_logs(from_block=0, address=[address]) ops = list(user.owned_crypto_operations) # The event was processed on log level assert len(logs) == 1 # The last user operation is deposit depo = ops[-1] assert isinstance(depo.crypto_operation, CryptoAddressDeposit) opid = depo.crypto_operation.id # Wait deposit to confirm wait_for_op_confirmations(eth_service, opid) with transaction.manager: # User ETH balance is expected asset = get_ether_asset(dbsession) user = dbsession.query(User).first() ua = user.owned_crypto_addresses.first() caccout = ua.address.get_account(asset) assert caccout.account.get_balance() == Decimal("0.1")
def test_event_execute(web3: Web3, topped_up_hosted_wallet: HostedWallet, simple_test_contract: Contract): """Test calling a contract froma hosted wallet.""" hosted_wallet = topped_up_hosted_wallet # Events of the hosted wallet listener, events = create_contract_listener(hosted_wallet.contract) # Events of the contract we are calling target_listener, target_events = create_contract_listener( simple_test_contract) # Make gas a huge number so we don't run out of gas. # No idea of actual gas consumption. gas_amount = 1500000 balance_before = hosted_wallet.get_balance() magic = random.randint(0, 2**30) txid = hosted_wallet.execute(simple_test_contract, "setValue", args=[magic], max_gas=gas_amount) confirm_transaction(web3, txid) balance_after = hosted_wallet.get_balance() # Check events from the wallet update_count = listener.poll() assert update_count == 1 assert len(events) == 1 event_name, input_data = events[0] assert event_name == "Execute" assert input_data["value"] == 0 # Hmm looks like this network doesn't spend any gas # assert input_data["spentGas"] == 100000000000000 # Hardcoded value for private test geth assert input_data["to"] == simple_test_contract.address # Doing call() doesn't incur any gas cost on the contract assert balance_after == balance_before # Check events from the testcontract.sol update_count = target_listener.poll() assert update_count == 1 assert len(target_events) == 1 event_name, input_data = target_events[0] assert event_name == "Received"
def test_deposit_token(dbsession, eth_network_id, web3: Web3, eth_service: EthereumService, coinbase: str, deposit_address: str, token: Token): """"See that we can deposit tokens to accounts.""" # Import a contract where coinbase has all balance with transaction.manager: network = dbsession.query(AssetNetwork).get(eth_network_id) import_token(network, eth_address_to_bin(token.address)) # Run import token operation success_count, failure_count = eth_service.run_waiting_operations() assert success_count == 1 assert failure_count == 0 # Coinbase transfers token balance to deposit address txid = token.transfer(deposit_address, Decimal(4000)) confirm_transaction(web3, txid) # We should pick up incoming deposit success_count, failure_count = eth_service.run_listener_operations() assert success_count == 1 assert failure_count == 0 # Check that data is setup correctly on incoming transaction with transaction.manager: op = dbsession.query(CryptoOperation).all()[-1] opid = op.id assert not op.completed_at address = dbsession.query(CryptoAddress).filter_by(address=eth_address_to_bin(deposit_address)).one() asset = op.holding_account.asset assert op.holding_account.get_balance() == 4000 assert op.completed_at is None assert address.get_account(asset).account.get_balance() == 0 # Not credited until confirmations reached assert address.crypto_address_accounts.count() == 1 # Wait the token transaction to get enough confirmations wait_for_op_confirmations(eth_service, opid) # Check that the transaction is not final with transaction.manager: op = dbsession.query(CryptoOperation).get(opid) assert op.completed_at assert op.completed_at address = dbsession.query(CryptoAddress).filter_by(address=eth_address_to_bin(deposit_address)).one() asset = op.holding_account.asset assert op.holding_account.get_balance() == 0 assert address.get_account(asset).account.get_balance() == 4000 assert op.state == CryptoOperationState.success
def test_event_send_tokens(web3, hosted_wallet, token, coinbase): """Hosted wallet sends tokens.""" # BBB token = token.contract listener, events = create_contract_listener(token) # Top up hosted wallet with tokens txid = token.transact().transfer(hosted_wallet.address, 4000) # Prepare event listener confirm_transaction(web3, txid) update_count = listener.poll() assert update_count == 1 assert events[0][ 0] == "Transfer" # [('Transfer', {'from': '0x41e76d4aabeb54a90ca67c59374236314ac3ecd4', 'value': 4000, 'to': '0xdde544e991dfb43808e8f1c4a750eaff3cb17ef4'})] # Prepare event listener listener, events = create_contract_listener(token) listener.poll() wallet_listener, wallet_events = create_contract_listener( hosted_wallet.contract) # Transfer tokens back # Do a withdraw from wallet txid = hosted_wallet.execute(token, "transfer", args=[coinbase, 4000]) receipt = confirm_transaction(web3, txid) update_count = listener.poll() wallet_update_count = wallet_listener.poll() # See hosted wallet processed transfer() call assert wallet_update_count == 1 assert wallet_events[0][0] != "FailedExecute" # Check the transfer event arrives assert update_count == 1 assert len(events) == 1 event_name, input_data = events[0] assert event_name == "Transfer" assert input_data["value"] == 4000 assert input_data["to"] == coinbase assert input_data["from"] == hosted_wallet.address # Check balances token.call().balanceOf(coinbase) == 10000 token.call().balanceOf(hosted_wallet.address) == 0
def withdraw_address(web3: Web3, dbsession, eth_service: EthereumService, coinbase, deposit_address) -> str: """Create a managed hosted wallet that has withdraw balance for testing.""" txid = send_balance_to_address(web3, deposit_address, TEST_VALUE) confirm_transaction(web3, txid) assert web3.eth.getBalance(deposit_address) > 0 success_op_count, failed_op_count = eth_service.run_listener_operations() assert success_op_count == 1 assert failed_op_count == 0 with transaction.manager: # We bypass normal transction confirmation count mechanism to credit the account right away to speed up the test deposit = dbsession.query(CryptoAddressDeposit).one() deposit.resolve() return deposit_address
def withdraw_address(web3: Web3, dbsession, eth_service: EthereumService, coinbase, deposit_address) -> str: """Create a managed hosted wallet that has withdraw balance for testing.""" # Do a transaction over ETH network txid = send_balance_to_address(web3, deposit_address, TEST_VALUE) confirm_transaction(web3, txid) assert web3.eth.getBalance(deposit_address) > 0 success_op_count, failed_op_count = eth_service.run_listener_operations() assert success_op_count == 1 assert failed_op_count == 0 with transaction.manager: # We bypass normal transction confirmation count mechanism to credit the account right away to speed up the test deposit = dbsession.query(CryptoAddressDeposit).one() deposit.resolve() return deposit_address
def test_double_scan_deposit(dbsession, eth_network_id, web3, eth_service, coinbase, deposit_address): """Make sure that scanning the same transaction twice doesn't get duplicated in the database.""" # Do a transaction over ETH network txid = send_balance_to_address(web3, deposit_address, TEST_VALUE) confirm_transaction(web3, txid) success_op_count, failed_op_count = eth_service.run_listener_operations() assert success_op_count == 1 # Now force run over the same blocks again success_op_count, failed_op_count = eth_service.eth_wallet_listener.force_scan(0, web3.eth.blockNumber) assert success_op_count == 0 assert failed_op_count == 0 # We get a operation, which is not resolved yet due to block confirmation numbers with transaction.manager: # We have one pending operation ops = list(dbsession.query(CryptoOperation).all()) assert len(ops) == 2 # Create + deposit
def test_import_token(dbsession, eth_network_id, web3: Web3, eth_service: EthereumService, coinbase: str, deposit_address: str, token: Token): """Import an existing smart contract token to system.""" # Make sure we have an address that holds some of the tokens so it is cleared up during import txid = token.transfer(deposit_address, Decimal(4000)) confirm_transaction(web3, txid) with transaction.manager: network = dbsession.query(AssetNetwork).get(eth_network_id) op = import_token(network, eth_address_to_bin(token.address)) opid = op.id # Let's create another address that doesn't hold tokens # and see that import doesn't fail for it addr = CryptoAddress(network=network, address=eth_address_to_bin("0x2f70d3d26829e412a602e83fe8eebf80255aeea5")) dbsession.add(addr) success_count, failure_count = eth_service.run_waiting_operations() assert success_count == 1 assert failure_count == 0 # Check that we created a new asset and its imports where fulfilled with transaction.manager: op = dbsession.query(CryptoOperation).get(opid) assert op.completed_at # We got account with tokens on it network = dbsession.query(AssetNetwork).get(eth_network_id) caddress = CryptoAddress.get_network_address(network, eth_address_to_bin(deposit_address)) asset = network.assets.filter_by(external_id=eth_address_to_bin(token.address)).one() assert asset.name == "Mootoken" assert asset.symbol == "MOO" assert asset.supply == 10000 caccount = caddress.get_account_by_address(eth_address_to_bin(token.address)) assert caccount.account.get_balance() == 4000
def test_event_withdraw_wallet_too_much(web3: Web3, topped_up_hosted_wallet, coinbase): """Try to withdraw more than the wallet has.""" hosted_wallet = topped_up_hosted_wallet coinbase_address = coinbase listener, events = create_contract_listener(hosted_wallet.contract) too_much = Decimal(99999999) txid = hosted_wallet.withdraw(coinbase_address, too_much) confirm_transaction(web3, txid) update_count = listener.poll() # XXX: assert update_count == 1 assert len(events) == 1 event_name, input_data = events[0] assert event_name == "ExceededWithdraw" assert input_data["value"] == to_wei(too_much)
def test_withdraw_wallet(web3, topped_up_hosted_wallet, coinbase): """Withdraw eths from wallet contract to RPC coinbase address.""" hosted_wallet = topped_up_hosted_wallet current_balance = hosted_wallet.get_balance() current_coinbase_balance = wei_to_eth(web3.eth.getBalance(coinbase)) # We have enough coints to perform the test assert current_balance > TEST_VALUE # Withdraw and wait it go through txid = hosted_wallet.withdraw(coinbase, TEST_VALUE) confirm_transaction(web3, txid) new_balance = hosted_wallet.get_balance() new_coinbase_balance = wei_to_eth(web3.eth.getBalance(coinbase)) assert new_coinbase_balance != current_coinbase_balance, "Coinbase address balance did not change: {}".format(new_coinbase_balance) assert new_coinbase_balance > current_coinbase_balance assert new_balance < current_balance
def test_buy_crowdfund_not_enough_gas(dbsession: Session, eth_network_id: UUID, web3: Web3, eth_service: EthereumService, token, toycrowd, withdraw_address): """Perform a crowdfundn buy operation without giving enough gas for the transaction.""" with transaction.manager: # Create withdraw operation caccount = dbsession.query(CryptoAddressAccount).one() # Use 4 as the heurestics for block account that doesn't happen right away, but still sensible to wait for it soonish asset = toycrowd() caccount.withdraw(Decimal(0.005), asset.external_id, "Buying Toycrowd", required_confirmation_count=1) print("Withdrawing") success_op_count, failed_op_count = eth_service.run_waiting_operations() assert success_op_count == 1 assert failed_op_count == 0 with transaction.manager: ops = list(dbsession.query(CryptoOperation).all()) assert len(ops) == 3 # Create + deposit + withdraw op = ops[-1] txid = bin_to_txid(op.txid) # This should make the tx to included in a block confirm_transaction(web3, txid) # This should trigger incoming notification eth_service.run_listener_operations() # Now we should get block number for the withdraw updates, failures = eth_service.run_confirmation_updates() assert failures == 1 with transaction.manager: op = dbsession.query(CryptoOperation).all()[-1] assert op.is_failed() assert "gas" in op.get_failure_reason()
def xxx_test_registrar_based_wallet(web3: Web3, coinbase): """Create registrar contract and register a wallet against it.""" wei_amount = to_wei(TEST_VALUE) # Check we get somewhat valid ids contract_def = get_compiled_contract_cached("OwnedRegistrar") registrar_contract, txid = deploy_contract(web3, contract_def) # Deploy wallet contract body wallet_contract_def = get_compiled_contract_cached("Wallet") wallet_contract, txid = deploy_contract(web3, wallet_contract_def) assert wallet_contract.call().version().decode("utf-8") == "1.0" # Register wallet contract body assert wallet_contract.address txid = registrar_contract.transact().setAddr(b"wallet", wallet_contract.address) confirm_transaction(web3, txid) # Check registration succeeded assert decode_addr( registrar_contract.call().addr(b"wallet")) == wallet_contract.address # Deploy relay against the registered wallet contract_def = get_compiled_contract_cached("Relay") assert registrar_contract.address relay, txid = deploy_contract( web3, contract_def, constructor_arguments=[registrar_contract.address, "wallet"]) # Test relayed wallet. We use Wallet ABI # against Relay contract. contract_def = get_compiled_contract_cached("Wallet") relayed_wallet = get_contract(web3, contract_def, relay.address) # Check relay internal data structures assert decode_addr( relay.call().registrarAddr()) == registrar_contract.address assert relay.call().name() == b"wallet" # We point to the wallet implementation impl_addr = decode_addr(relay.call().getImplAddr()) assert impl_addr == wallet_contract.address # Read a public variable through relay contract assert relayed_wallet.call().version().decode("utf-8") == "1.0" # Deposit some ETH txid = send_balance_to_contract(relayed_wallet.address, wei_amount) confirm_transaction(web3, txid) assert relayed_wallet.web3.eth.getBalance(relayed_wallet.address, wei_amount) # Withdraw ETH back relayed_wallet.transact().withdraw(coinbase, wei_amount) confirm_transaction(web3, txid) assert relayed_wallet.web3.eth.getBalance(relayed_wallet.address, 0)
def test_withdraw_eth_data(dbsession: Session, eth_network_id: UUID, web3: Web3, eth_service: EthereumService, withdraw_address: str, target_account: str, decode_data_contract): """Perform a withdraw operation with data and gas set. """ # First check what's our balance before sending coins back current_balance = wei_to_eth(web3.eth.getBalance(target_account)) assert current_balance == 0 with transaction.manager: # Create withdraw operation caccount = dbsession.query(CryptoAddressAccount).one() #: We are going to withdraw the full amount on the account assert caccount.account.get_balance() == TEST_VALUE # Use 4 as the heurestics for block account that doesn't happen right away, but still sensible to wait for it soonish op = caccount.withdraw(TEST_VALUE, eth_address_to_bin(decode_data_contract.address), "Getting all the moneys", required_confirmation_count=4) op.other_data["gas"] = 1000000 op.other_data["data"] = "0x001234" success_op_count, failed_op_count = eth_service.run_waiting_operations() assert success_op_count == 1 assert failed_op_count == 0 with transaction.manager: # create + deposit + withdraw op = dbsession.query(CryptoOperation).all()[2] txid = bin_to_txid(op.txid) # This should make the tx to included in a block confirm_transaction(web3, txid) value = decode_data_contract.call().value() data = decode_data_contract.call().data() assert value == 10000000000000000 assert data == '0x001234'
def test_event_receive_tokens(web3, hosted_wallet, token, coinbase): """A hosted wallet receive tokens.""" # BBB token = token.contract listener, events = create_contract_listener(token) txid = token.transact().transfer(hosted_wallet.address, 4000) confirm_transaction(web3, txid) update_count = listener.poll() # Check the transfer event arrives assert update_count == 1 assert len(events) == 1 event_name, input_data = events[0] assert event_name == "Transfer" assert input_data["value"] == 4000 assert input_data["from"] == coinbase assert input_data["to"] == hosted_wallet.address # Check balances token.call().balanceOf(coinbase) == 6000 token.call().balanceOf(hosted_wallet.address) == 400
def xxx_test_registrar_based_wallet(web3: Web3, coinbase): """Create registrar contract and register a wallet against it.""" wei_amount = to_wei(TEST_VALUE) # Check we get somewhat valid ids contract_def = get_compiled_contract_cached("OwnedRegistrar") registrar_contract, txid = deploy_contract(web3, contract_def) # Deploy wallet contract body wallet_contract_def = get_compiled_contract_cached("Wallet") wallet_contract, txid = deploy_contract(web3, wallet_contract_def) assert wallet_contract.call().version().decode("utf-8") == "1.0" # Register wallet contract body assert wallet_contract.address txid = registrar_contract.transact().setAddr(b"wallet", wallet_contract.address) confirm_transaction(web3, txid) # Check registration succeeded assert decode_addr(registrar_contract.call().addr(b"wallet")) == wallet_contract.address # Deploy relay against the registered wallet contract_def = get_compiled_contract_cached("Relay") assert registrar_contract.address relay, txid = deploy_contract(web3, contract_def, constructor_arguments=[registrar_contract.address, "wallet"]) # Test relayed wallet. We use Wallet ABI # against Relay contract. contract_def = get_compiled_contract_cached("Wallet") relayed_wallet = get_contract(web3, contract_def, relay.address) # Check relay internal data structures assert decode_addr(relay.call().registrarAddr()) == registrar_contract.address assert relay.call().name() == b"wallet" # We point to the wallet implementation impl_addr = decode_addr(relay.call().getImplAddr()) assert impl_addr == wallet_contract.address # Read a public variable through relay contract assert relayed_wallet.call().version().decode("utf-8") == "1.0" # Deposit some ETH txid = send_balance_to_contract(relayed_wallet.address, wei_amount) confirm_transaction(web3, txid) assert relayed_wallet.web3.eth.getBalance(relayed_wallet.address, wei_amount) # Withdraw ETH back relayed_wallet.transact().withdraw(coinbase, wei_amount) confirm_transaction(web3, txid) assert relayed_wallet.web3.eth.getBalance(relayed_wallet.address, 0)
def test_buy_crowdfund_with_gas(dbsession: Session, eth_network_id: UUID, web3: Web3, eth_service: EthereumService, token, toycrowd, rich_withdraw_address): """Perform a crowdfunnd buy operation without giving enough gas for the transaction.""" with transaction.manager: # Create withdraw operation caccount = dbsession.query(CryptoAddressAccount).one() # Use 4 as the heurestics for block account that doesn't happen right away, but still sensible to wait for it soonish asset = toycrowd() op = caccount.withdraw(Decimal(3), asset.external_id, "Buying Toycrowd", required_confirmation_count=1) op.other_data["gas"] = 2500333 # Limit should be ~100k success_op_count, failed_op_count = eth_service.run_waiting_operations() assert failed_op_count == 0 assert success_op_count == 1 with transaction.manager: ops = list(dbsession.query(CryptoOperation).all()) assert len(ops) == 3 # Create + deposit + withdraw op = ops[-1] txid = bin_to_txid(op.txid) # This should make the tx to included in a block confirm_transaction(web3, txid) # Set op.block eth_service.run_confirmation_updates() # Grab block number where out tx is with transaction.manager: ops = list(dbsession.query(CryptoOperation).all()) op = ops[-1] block_num = op.block wallet = get_wallet(web3, rich_withdraw_address) token_events = token.get_all_events() wallet_events = wallet.get_all_events() # Confirm we got it all right receipt = web3.eth.getTransactionReceipt(txid) logs = receipt["logs"] assert logs[0]["topics"][0] == token_events["Buy"] assert logs[1]["topics"][0] == token_events["Transfer"] assert logs[2]["topics"][0] == wallet_events["Withdraw"] data = get_crowdsale_data(token) assert data["wei_raised"] == to_wei("3", "ether") # Give tx time to confirm, so all confirmations will be there for db update run required_conf = 3 wait_for_block_number(web3, block_num + required_conf + 1, timeout=60) # This should trigger incoming notification eth_service.run_listener_operations() updates, failures = eth_service.run_confirmation_updates() assert failures == 0 assert updates == 2 # 1 eth withdraw, 1 token deposit # Check our db is updated with transaction.manager: # There is now new operation to deposit tokens ops = list(dbsession.query(CryptoOperation).all()) assert len(ops) == 4 op = ops[-1] # type: CryptoOperation assert op.operation_type == CryptoOperationType.deposit assert op.state == CryptoOperationState.success assert op.amount == 6 asset = toycrowd() crypto_address = dbsession.query(CryptoAddress).one() # type: CryptoAddress caccount = crypto_address.get_account(asset) assert caccount.account.get_balance() == 6
def test_transfer_tokens_between_accounts(dbsession, eth_network_id, web3: Web3, eth_service: EthereumService, deposit_address: str, token_asset: str, coinbase: str): """Transfer tokens between two internal accounts. Do transfers in two batches to make sure subsequent events top up correctly. """ # Initiate a target address creatin with transaction.manager: network = dbsession.query(AssetNetwork).get(eth_network_id) opid = CryptoAddress.create_address(network).id # Run address creation success_count, failure_count = eth_service.run_waiting_operations() assert success_count == 1 assert failure_count == 0 # We resolved address creation operation. # Get the fresh address for the future withdraw targets. with transaction.manager: op = dbsession.query(CryptoAddressCreation).get(opid) addr = op.address.address assert addr # Move tokens between accounts with transaction.manager: address = dbsession.query(CryptoAddress).filter_by(address=eth_address_to_bin(deposit_address)).one() asset = dbsession.query(Asset).get(token_asset) caccount = address.get_account(asset) op = caccount.withdraw(Decimal(2000), addr, "Sending to friend") opid = op.id # Resolve the 1st transaction eth_service.run_event_cycle() wait_for_op_confirmations(eth_service, opid) # See that our internal balances match with transaction.manager: # Withdraw operation is complete op = dbsession.query(CryptoOperation).get(opid) assert op.completed_at assert op.completed_at, "Op not confirmed {}".format(op) # We should have received a Transfer operation targetting target account op = dbsession.query(CryptoOperation).join(CryptoAddressAccount).join(CryptoAddress).filter_by(address=addr).one() opid = op.id # Confirm incoming Transfer wait_for_op_confirmations(eth_service, opid, timeout=180) # Check Transfer looks valid with transaction.manager: op = dbsession.query(CryptoOperation).get(opid) assert op.completed_at assert op.completed_at, "Op not confirmed {}".format(op) asset = dbsession.query(Asset).get(token_asset) source = dbsession.query(CryptoAddress).filter_by(address=eth_address_to_bin(deposit_address)).one() target = dbsession.query(CryptoAddress).filter_by(address=addr).one() assert source.get_account(asset).account.get_balance() == 8000 assert target.get_account(asset).account.get_balance() == 2000 # Add some ETH on deposit_address txid = send_balance_to_address(web3, deposit_address, TEST_VALUE) confirm_transaction(web3, txid) eth_service.run_event_cycle() # Wait for confirmations to have ETH deposit credired with transaction.manager: op = dbsession.query(CryptoOperation).filter_by(txid=txid_to_bin(txid)).one() opid = op.id confirmed = op.completed_at if not confirmed: wait_for_op_confirmations(eth_service, opid) # Send some more tokens + ether, so we see account can't get mixed up with transaction.manager: address = dbsession.query(CryptoAddress).filter_by(address=eth_address_to_bin(deposit_address)).one() asset = dbsession.query(Asset).get(token_asset) caccount = address.get_account(asset) op = caccount.withdraw(Decimal(4000), addr, "Sending tokens to friend äää") eth_asset = get_ether_asset(dbsession) caccount = address.get_account(eth_asset) op = caccount.withdraw(TEST_VALUE, addr, "Sending ETH to friend äää") opid = op.id # Resolve second transaction eth_service.run_event_cycle() wait_for_op_confirmations(eth_service, opid) # See that our internal balances match with transaction.manager: op = dbsession.query(CryptoOperation).get(opid) assert op.completed_at assert op.completed_at asset = dbsession.query(Asset).get(token_asset) source = dbsession.query(CryptoAddress).filter_by(address=eth_address_to_bin(deposit_address)).one() target = dbsession.query(CryptoAddress).filter_by(address=addr).one() eth_asset = get_ether_asset(dbsession) assert source.get_account(asset).account.get_balance() == 4000 assert target.get_account(asset).account.get_balance() == 6000 assert target.get_account(eth_asset).account.get_balance() == TEST_VALUE # Transfer 3: # Send everything back plus some ether with transaction.manager: address = dbsession.query(CryptoAddress).filter_by(address=addr).one() asset = dbsession.query(Asset).get(token_asset) caccount = address.get_account(asset) op = caccount.withdraw(Decimal(6000), eth_address_to_bin(deposit_address), "Sending everything back to friend äää") opid = op.id # Resolve third transaction wait_for_op_confirmations(eth_service, opid) eth_service.run_event_cycle() # See that our internal balances match with transaction.manager: op = dbsession.query(CryptoOperation).get(opid) assert op.completed_at assert op.completed_at asset = dbsession.query(Asset).get(token_asset) source = dbsession.query(CryptoAddress).filter_by(address=eth_address_to_bin(deposit_address)).one() target = dbsession.query(CryptoAddress).filter_by(address=addr).one() assert source.get_account(asset).account.get_balance() == 10000 assert target.get_account(asset).account.get_balance() == 0 eth_asset = get_ether_asset(dbsession) assert source.get_account(eth_asset).account.get_balance() == 0 assert target.get_account(eth_asset).account.get_balance() == TEST_VALUE
def test_withdraw_eth(dbsession: Session, eth_network_id: UUID, web3: Web3, eth_service: EthereumService, withdraw_address: str, target_account: str): """Perform a withdraw operation. Create a database address with balance. """ # First check what's our balance before sending coins back current_balance = wei_to_eth(web3.eth.getBalance(target_account)) assert current_balance == 0 with transaction.manager: # Create withdraw operation caccount = dbsession.query(CryptoAddressAccount).one() #: We are going to withdraw the full amount on the account assert caccount.account.get_balance() == TEST_VALUE # Use 4 as the heurestics for block account that doesn't happen right away, but still sensible to wait for it soonish op = caccount.withdraw(TEST_VALUE, eth_address_to_bin(target_account), "Getting all the moneys", required_confirmation_count=4) success_op_count, failed_op_count = eth_service.run_waiting_operations() assert success_op_count == 1 assert failed_op_count == 0 with transaction.manager: # We should have now three ops # One for creating the address # One for depositing value for the test # One for withdraw # We have one complete operation ops = list(dbsession.query(CryptoOperation).all()) assert len(ops) == 3 # Create + deposit + withdraw op = ops[-1] assert isinstance(op, CryptoAddressWithdraw) assert op.broadcasted_at is not None # This completes instantly, cannot be cancelled assert op.completed_at is None # We need at least one confirmation assert op.block is None assert op.txid is not None txid = bin_to_txid(op.txid) # This should make the tx to included in a block confirm_transaction(web3, txid) # Now we should get block number for the withdraw eth_service.run_confirmation_updates() # Geth reflects the deposit instantly internally, doesn't wait for blocks fee = get_withdrawal_fee(web3) new_balance = wei_to_eth(web3.eth.getBalance(target_account)) assert new_balance == current_balance + TEST_VALUE - fee current_block = web3.eth.blockNumber with transaction.manager: # Check we get block and txid # We have one complete operation ops = list(dbsession.query(CryptoOperation).all()) assert len(ops) == 3 # Create + deposit + withdraw op = ops[-1] assert op.broadcasted_at is not None # This completes instantly, cannot be cancelled assert op.completed_at is None, "Got confirmation for block {}, current {}, requires {}".format(op.block, current_block, op.required_confirmation_count) assert op.block is not None assert op.txid is not None block_num = op.block required_conf = op.required_confirmation_count # Wait block to make the confirmation happen wait_for_block_number(web3, block_num + required_conf + 1, timeout=60) # Now we should have enough blocks to mark the transaction as confirmed eth_service.run_confirmation_updates() with transaction.manager: # Check we get block and txid # We have one complete operation ops = list(dbsession.query(CryptoOperation).all()) op = ops[-1] assert op.state == CryptoOperationState.success assert op.completed_at is not None