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)
Example #2
0
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)
Example #3
0
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
Example #4
0
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
Example #5
0
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)
Example #8
0
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_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
Example #13
0
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")
Example #15
0
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_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
Example #18
0
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
Example #19
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
Example #20
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."""

    # 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_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
Example #24
0
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
Example #26
0
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_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_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'
Example #30
0
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
Example #31
0
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)
Example #33
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
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
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