Example #1
0
def token_asset(dbsession, eth_network_id, deposit_address,
                eth_service: EthereumService) -> UUID:
    """Database asset referring to the token contract.

    deposit_address will hold 10000 tokens

    :return: Asset id
    """

    with transaction.manager:
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        asset = network.create_asset(name="MyToken",
                                     symbol="MY",
                                     supply=Decimal(10000),
                                     asset_class=AssetClass.token)
        address = CryptoAddress.get_network_address(
            network, eth_address_to_bin(deposit_address))
        op = address.create_token(asset)
        opid = op.id
        aid = asset.id

    # This gives op a txid
    success, fails = eth_service.run_waiting_operations()
    assert success == 1

    wait_for_op_confirmations(eth_service, opid)

    with transaction.manager:
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        asset = dbsession.query(Asset).get(aid)
        address = CryptoAddress.get_network_address(
            network, eth_address_to_bin(deposit_address))
        account = address.get_account(asset)
        assert account.account.get_balance() > 0
        return aid
Example #2
0
def token_asset(dbsession, eth_network_id, deposit_address, eth_service: EthereumService) -> UUID:
    """Database asset referring to the token contract.

    deposit_address will hold 10000 tokens

    :return: Asset id
    """

    with transaction.manager:
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        asset = network.create_asset(name="MyToken", symbol="MY", supply=Decimal(10000), asset_class=AssetClass.token)
        address = CryptoAddress.get_network_address(network, eth_address_to_bin(deposit_address))
        op = address.create_token(asset)
        opid = op.id
        aid = asset.id

    # This gives op a txid
    success, fails = eth_service.run_waiting_operations()
    assert success == 1

    wait_for_op_confirmations(eth_service, opid)

    with transaction.manager:
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        asset = dbsession.query(Asset).get(aid)
        address = CryptoAddress.get_network_address(network, eth_address_to_bin(deposit_address))
        account = address.get_account(asset)
        assert account.account.get_balance() > 0
        return aid
    def handle_event(self, event_name: str, contract_address: str,
                     log_data: dict, log_entry: dict):
        """Map incoming EVM log to database entry."""

        opid = self.get_unique_transaction_id(log_entry)

        existing_op = self.get_existing_op(opid, CryptoOperationType.deposit)
        if existing_op:
            # Already in the database, all we need to do is to call blocknumber updater now
            return False

        network = self.dbsession.query(AssetNetwork).get(self.network_id)
        asset = self.dbsession.query(Asset).filter_by(
            network=network,
            external_id=eth_address_to_bin(contract_address)).one()

        if event_name == "Transfer":

            to_address = eth_address_to_bin(log_data["to"])
            from_address = eth_address_to_bin(log_data["from"])
            value = Decimal(log_data["value"])

            self.logger.debug("Incoming transfer event %s %s %s", from_address,
                              to_address, value)

            # Get destination address entry
            address = self.dbsession.query(CryptoAddress).filter_by(
                address=to_address).one_or_none()
            if not address:
                # Address not in our system
                return False

            # Create operation
            op = CryptoAddressDeposit(network=network)
            op.opid = opid
            op.txid = txid_to_bin(log_entry["transactionHash"])
            op.external_address = from_address
            op.block = int(log_entry["blockNumber"], 16)
            op.required_confirmation_count = self.confirmation_count
            op.crypto_account = address.get_or_create_account(asset)

            # Create holding account that keeps the value until we receive N amount of confirmations
            acc = Account(asset=asset)
            self.dbsession.add(acc)
            self.dbsession.flush()

            acc.do_withdraw_or_deposit(
                value, "Token {} deposit from {} in tx {}".format(
                    asset.symbol, log_data["from"],
                    log_entry["transactionHash"]))
            op.holding_account = acc
            self.dbsession.add(op)

            self.notify_deposit(op)

            return True
        else:
            # Unmonitored event
            return False
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
def test_create_token(dbsession, eth_network_id, eth_service, coinbase, deposit_address):
    """Test user initiated token creation."""

    # Initiate token creation operation
    with transaction.manager:
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        asset = network.create_asset(name="MyToken", symbol="MY", supply=Decimal(10000), asset_class=AssetClass.token)
        address = CryptoAddress.get_network_address(network, eth_address_to_bin(deposit_address))
        op = address.create_token(asset)
        opid = op.id
        aid = asset.id
        assert op.completed_at is None

        # Check asset is intact
        assert asset.symbol == "MY"
        assert asset.supply == 10000
        assert asset.name == "MyToken"

    # This gives op a txid when smart contract creation tx is posted to geth
    success_count, failure_count = eth_service.run_waiting_operations()
    assert success_count == 1
    assert failure_count == 0

    # Check that initial asset data is in place
    with transaction.manager:
        op = dbsession.query(CryptoOperation).get(opid)
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        address = CryptoAddress.get_network_address(network, eth_address_to_bin(deposit_address))
        asset = network.get_asset(aid)

        assert op.txid
        assert not op.block
        assert op.broadcasted_at is not None
        assert op.completed_at is None

        # Initial balance doesn't hit us until tx has been confirmed
        assert address.get_account(asset).account.get_balance() == 0

        # Asset has received its smart contract address
        assert asset.external_id

    # Wait that the smart contract creation is confirmed
    wait_for_op_confirmations(eth_service, opid)

    # Initial balance doesn't hit us until op has enough confirmations
    with transaction.manager:
        op = dbsession.query(CryptoOperation).get(opid)
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        address = CryptoAddress.get_network_address(network, eth_address_to_bin(deposit_address))
        asset = network.get_asset(aid)

        assert op.broadcasted_at is not None
        assert op.completed_at is not None
        assert address.get_account(asset).account.get_balance() == 10000
def test_create_token(dbsession, eth_network_id, eth_service, coinbase, deposit_address):
    """Test user initiated token creation."""

    # Initiate token creation operation
    with transaction.manager:
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        asset = network.create_asset(name="MyToken", symbol="MY", supply=Decimal(10000), asset_class=AssetClass.token)
        address = CryptoAddress.get_network_address(network, eth_address_to_bin(deposit_address))
        op = address.create_token(asset)
        opid = op.id
        aid = asset.id
        assert op.completed_at is None

        # Check asset is intact
        assert asset.symbol == "MY"
        assert asset.supply == 10000
        assert asset.name == "MyToken"

    # This gives op a txid when smart contract creation tx is posted to geth
    success_count, failure_count = eth_service.run_waiting_operations()
    assert success_count == 1
    assert failure_count == 0

    # Check that initial asset data is in place
    with transaction.manager:
        op = dbsession.query(CryptoOperation).get(opid)
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        address = CryptoAddress.get_network_address(network, eth_address_to_bin(deposit_address))
        asset = network.get_asset(aid)

        assert op.txid
        assert not op.block
        assert op.broadcasted_at is not None
        assert op.completed_at is None

        # Initial balance doesn't hit us until tx has been confirmed
        assert address.get_account(asset).account.get_balance() == 0

        # Asset has received its smart contract address
        assert asset.external_id

    # Wait that the smart contract creation is confirmed
    wait_for_op_confirmations(eth_service, opid)

    # Initial balance doesn't hit us until op has enough confirmations
    with transaction.manager:
        op = dbsession.query(CryptoOperation).get(opid)
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        address = CryptoAddress.get_network_address(network, eth_address_to_bin(deposit_address))
        asset = network.get_asset(aid)

        assert op.broadcasted_at is not None
        assert op.completed_at is not None
        assert address.get_account(asset).account.get_balance() == 10000
def test_double_address(dbsession, eth_network_id):
    """Cannot create Address object under the same network twice."""

    with pytest.raises(sqlalchemy.exc.IntegrityError):
        with transaction.manager:
            network = dbsession.query(AssetNetwork).get(eth_network_id)
            address = CryptoAddress(network=network, address=eth_address_to_bin(TEST_ADDRESS))
            dbsession.add(address)
            dbsession.flush()
            address = CryptoAddress(network=network, address=eth_address_to_bin(TEST_ADDRESS))
            dbsession.add(address)
Example #9
0
def test_double_address(dbsession, eth_network_id):
    """Cannot create Address object under the same network twice."""

    with pytest.raises(sqlalchemy.exc.IntegrityError):
        with transaction.manager:
            network = dbsession.query(AssetNetwork).get(eth_network_id)
            address = CryptoAddress(network=network,
                                    address=eth_address_to_bin(TEST_ADDRESS))
            dbsession.add(address)
            dbsession.flush()
            address = CryptoAddress(network=network,
                                    address=eth_address_to_bin(TEST_ADDRESS))
            dbsession.add(address)
    def handle_event(self, event_name: str, contract_address: str, log_data: dict, log_entry: dict):
        """Map incoming EVM log to database entry."""

        with self._get_tm():

            opid = self.get_unique_transaction_id(log_entry)

            existing_op = self.get_existing_op(opid, CryptoOperationType.deposit)
            if existing_op:
                # Already in the database, all we need to do is to call blocknumber updater now
                return False

            network = self.dbsession.query(AssetNetwork).get(self.network_id)
            asset = self.dbsession.query(Asset).filter_by(network=network, external_id=eth_address_to_bin(contract_address)).one()

            if event_name == "Transfer":

                to_address = eth_address_to_bin(log_data["to"])
                from_address = eth_address_to_bin(log_data["from"])
                value = Decimal(log_data["value"])

                # Get destination address entry
                address = self.dbsession.query(CryptoAddress).filter_by(address=to_address).one_or_none()
                if not address:
                    # Address not in our system
                    return False

                # Create operation
                op = CryptoAddressDeposit(network=network)
                op.opid = opid
                op.txid = txid_to_bin(log_entry["transactionHash"])
                op.external_address = from_address
                op.block = int(log_entry["blockNumber"], 16)
                op.required_confirmation_count = self.confirmation_count
                op.crypto_account = address.get_or_create_account(asset)

                # Create holding account that keeps the value until we receive N amount of confirmations
                acc = Account(asset=asset)
                self.dbsession.add(acc)
                self.dbsession.flush()

                acc.do_withdraw_or_deposit(value, "Token {} deposit from {} in tx {}".format(asset.symbol, log_data["from"], log_entry["transactionHash"]))
                op.holding_account = acc
                self.dbsession.add(op)

                self.notify_deposit(op)

                return True
            else:
                # Unmonitored event
                return False
Example #11
0
    def perform_tx():
        op = dbsession.query(CryptoOperation).get(opid)
        # Check everyting looks sane
        assert op.crypto_account.id
        assert op.crypto_account.account.id

        asset = op.holding_account.asset
        assert asset.id

        # Set information on asset that we have now created and have its smart contract id
        assert not asset.external_id, "Asset has been already assigned its smart contract id. Recreate error?"

        address = bin_to_eth_address(op.crypto_account.address.address)

        # Create Tonex proxy object
        token = Token.create_token(web3, name=asset.name, symbol=asset.symbol, supply=asset.supply, owner=address)

        # Call geth RPC API over Populus contract proxy
        op.txid = txid_to_bin(token.initial_txid)
        op.block = None
        op.external_address = eth_address_to_bin(token.address)

        asset.external_id = op.external_address

        op.mark_performed()
        op.mark_broadcasted()
Example #12
0
    def perform_tx():
        op = dbsession.query(CryptoOperation).get(opid)
        # Check everyting looks sane
        assert op.crypto_account.id
        assert op.crypto_account.account.id

        asset = op.holding_account.asset
        assert asset.id

        # Set information on asset that we have now created and have its smart contract id
        assert not asset.external_id, "Asset has been already assigned its smart contract id. Recreate error?"

        address = bin_to_eth_address(op.crypto_account.address.address)

        # Create Tonex proxy object
        token = Token.create_token(web3,
                                   name=asset.name,
                                   symbol=asset.symbol,
                                   supply=asset.supply,
                                   owner=address)

        # Call geth RPC API over Populus contract proxy
        op.txid = txid_to_bin(token.initial_txid)
        op.block = None
        op.external_address = eth_address_to_bin(token.address)

        asset.external_id = op.external_address

        op.mark_performed()
        op.mark_broadcasted()
def test_create_wallet(hosted_wallet):
    """Deploy a wallet contract on a testnet chain.

    """
    # Check we get somewhat valid ids
    assert hosted_wallet.version == 2
    assert eth_address_to_bin(hosted_wallet.address)
Example #14
0
def test_create_address_accounts(dbsession, eth_network_id, eth_service,
                                 eth_faux_address, eth_asset_id):
    """Check that we can cerate different accounts under an account."""

    # Create ETH account
    with transaction.manager:
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        address = CryptoAddress(network=network,
                                address=eth_address_to_bin(TEST_ADDRESS))
        asset = dbsession.query(Asset).get(eth_asset_id)
        dbsession.flush()
        account = address.get_or_create_account(asset)
        account_id = account.id

    # We get it reflected back second time
    with transaction.manager:
        address = dbsession.query(CryptoAddress).one()
        asset = dbsession.query(Asset).get(eth_asset_id)
        account = address.get_or_create_account(asset)
        assert account.id == account_id

    # We cannot create double account for the same asset
    with pytest.raises(MultipleAssetAccountsPerAddress):
        with transaction.manager:
            address = dbsession.query(CryptoAddress).one()
            asset = dbsession.query(Asset).get(eth_asset_id)
            address.create_account(asset)
Example #15
0
 def _create_address(service, dbsession, opid):
     with transaction.manager:
         op = dbsession.query(CryptoOperation).get(opid)
         assert isinstance(op.address, CryptoAddress)
         op.address.address = eth_address_to_bin(address)
         op.mark_performed()
         op.mark_complete()
    def on_deposit(self, address: CryptoAddress, opid, log_data,
                   log_entry) -> CryptoAddressDeposit:
        """Handle Hosted Wallet Deposit event.

        Create incoming holding account holding the ETH assets until we receive enough confirmations.
        """

        op = CryptoAddressDeposit(address.network)

        # Get or create final account where we deposit the transaction
        asset = get_ether_asset(self.dbsession, network=address.network)
        crypto_account = address.get_or_create_account(asset)
        op.crypto_account = crypto_account

        op.external_address = eth_address_to_bin(log_data["from"])

        # Create holding account that keeps the value until we receive N amount of confirmations
        acc = Account(asset=asset)
        self.dbsession.add(acc)
        self.dbsession.flush()

        value = wei_to_eth(log_data["value"])
        acc.do_withdraw_or_deposit(
            value, "ETH deposit from {} in tx {}".format(
                log_data["from"], log_entry["transactionHash"]))

        op.holding_account = acc
        return op
    def handle_event(self, event_name: str, contract_address: str, log_data: dict, log_entry: dict):
        """Map incoming EVM log to database entry."""

        with self._get_tm():
            opid = self.get_unique_transaction_id(log_entry)

            existing_op = self.get_existing_op(opid, CryptoOperationType.deposit)
            if existing_op:
                # Already in the database, all we need to do is to call blocknumber updater now
                return False

            network = self.dbsession.query(AssetNetwork).get(self.network_id)
            address = self.dbsession.query(CryptoAddress).filter_by(address=eth_address_to_bin(contract_address), network=network).one()

            op = self.create_op(event_name, address, opid, log_data, log_entry)
            if not op:
                # This was an event we don't care about
                return False

            op.opid = opid
            op.txid = txid_to_bin(log_entry["transactionHash"])
            op.block = int(log_entry["blockNumber"], 16)
            op.required_confirmation_count = self.confirmation_count
            self.dbsession.add(op)

            self.notify_deposit(op)

            return True
def test_create_address_accounts(dbsession, eth_network_id, eth_service, eth_faux_address, eth_asset_id):
    """Check that we can cerate different accounts under an account."""

    # Create ETH account
    with transaction.manager:
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        address = CryptoAddress(network=network, address=eth_address_to_bin(TEST_ADDRESS))
        asset = dbsession.query(Asset).get(eth_asset_id)
        dbsession.flush()
        account = address.get_or_create_account(asset)
        account_id = account.id

    # We get it reflected back second time
    with transaction.manager:
        address = dbsession.query(CryptoAddress).one()
        asset = dbsession.query(Asset).get(eth_asset_id)
        account = address.get_or_create_account(asset)
        assert account.id == account_id

    # We cannot create double account for the same asset
    with pytest.raises(MultipleAssetAccountsPerAddress):
        with transaction.manager:
            address = dbsession.query(CryptoAddress).one()
            asset = dbsession.query(Asset).get(eth_asset_id)
            address.create_account(asset)
    def handle_event(self, event_name: str, contract_address: str,
                     log_data: dict, log_entry: dict):
        """Map incoming EVM log to database entry."""

        opid = self.get_unique_transaction_id(log_entry)

        existing_op = self.get_existing_op(opid, CryptoOperationType.deposit)
        if existing_op:
            # Already in the database, all we need to do is to call blocknumber updater now
            return False

        network = self.dbsession.query(AssetNetwork).get(self.network_id)
        address = self.dbsession.query(CryptoAddress).filter_by(
            address=eth_address_to_bin(contract_address),
            network=network).one()

        op = self.create_op(event_name, address, opid, log_data, log_entry)
        if not op:
            # This was an event we don't care about
            return False

        op.opid = opid
        op.txid = txid_to_bin(log_entry["transactionHash"])
        op.block = int(log_entry["blockNumber"], 16)
        op.required_confirmation_count = self.confirmation_count
        self.dbsession.add(op)

        self.notify_deposit(op)

        return True
Example #20
0
def test_double_deposit_same_tx(dbsession, eth_network_id, eth_service,
                                eth_asset_id):
    """Check that we have some logic to avoid depositing the same asset twice."""

    # Create ETH holding account under an address
    with transaction.manager:

        # First create the address which holds our account
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        address = CryptoAddress(network=network,
                                address=eth_address_to_bin(TEST_ADDRESS))
        dbsession.add(address)
        dbsession.flush()

    # Create deposit op
    with transaction.manager:
        address = dbsession.query(CryptoAddress).one_or_none()
        asset = dbsession.query(Asset).get(eth_asset_id)
        txid = txid_to_bin(TEST_TXID)
        op = address.deposit(Decimal(10), asset, txid, bin_to_txid(txid))
        dbsession.add(op)
        dbsession.flush()
        op_id = op.id

    with transaction.manager:
        address = dbsession.query(CryptoAddress).one_or_none()
        asset = dbsession.query(Asset).get(eth_asset_id)
        txid = txid_to_bin(TEST_TXID)
        op = address.deposit(Decimal(10), asset, txid, bin_to_txid(txid))
        assert op.id == op_id
        assert dbsession.query(CryptoOperation).count() == 1
def test_double_deposit_same_tx(dbsession, eth_network_id, eth_service, eth_asset_id):
    """Check that we have some logic to avoid depositing the same asset twice."""

    # Create ETH holding account under an address
    with transaction.manager:

        # First create the address which holds our account
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        address = CryptoAddress(network=network, address=eth_address_to_bin(TEST_ADDRESS))
        dbsession.add(address)
        dbsession.flush()

    # Create deposit op
    with transaction.manager:
        address = dbsession.query(CryptoAddress).one_or_none()
        asset = dbsession.query(Asset).get(eth_asset_id)
        txid = txid_to_bin(TEST_TXID)
        op = address.deposit(Decimal(10), asset, txid, bin_to_txid(txid))
        dbsession.add(op)
        dbsession.flush()
        op_id = op.id

    with transaction.manager:
        address = dbsession.query(CryptoAddress).one_or_none()
        asset = dbsession.query(Asset).get(eth_asset_id)
        txid = txid_to_bin(TEST_TXID)
        op = address.deposit(Decimal(10), asset, txid, bin_to_txid(txid))
        assert op.id == op_id
        assert dbsession.query(CryptoOperation).count() == 1
    def on_deposit(self, address: CryptoAddress, opid, log_data, log_entry) -> CryptoAddressDeposit:
        """Handle Hosted Wallet Deposit event.

        Create incoming holding account holding the ETH assets until we receive enough confirmations.
        """

        op = CryptoAddressDeposit(address.network)

        # Get or create final account where we deposit the transaction
        asset = get_ether_asset(self.dbsession, network=address.network)
        crypto_account = address.get_or_create_account(asset)
        op.crypto_account = crypto_account

        op.external_address = eth_address_to_bin(log_data["from"])

        # Create holding account that keeps the value until we receive N amount of confirmations
        acc = Account(asset=asset)
        self.dbsession.add(acc)
        self.dbsession.flush()

        value = wei_to_eth(log_data["value"])
        acc.do_withdraw_or_deposit(value, "ETH deposit from {} in tx {}".format(log_data["from"], log_entry["transactionHash"]))

        op.holding_account = acc
        return op
Example #23
0
 def _create_address(service, dbsession, opid):
     with transaction.manager:
         op = dbsession.query(CryptoOperation).get(opid)
         assert isinstance(op.address, CryptoAddress)
         op.address.address = eth_address_to_bin(address)
         op.mark_performed()
         op.mark_complete()
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
def test_withdraw_token(dbsession, eth_network_id, web3: Web3, eth_service: EthereumService, deposit_address: str, token_asset: str, target_account: str):
    """"See that we can withdraw token outside to an account."""

    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), eth_address_to_bin(target_account), "Sending to friend")
        opid = op.id

        assert caccount.account.get_balance() == 8000

    # Run import token operation
    success_count, failure_count = eth_service.run_waiting_operations()
    assert success_count == 1
    assert failure_count == 0

    # TX was broadcasted, marked as complete
    with transaction.manager:
        op = dbsession.query(CryptoOperation).get(opid)
        assert isinstance(op, CryptoAddressWithdraw)
        asset = dbsession.query(Asset).get(token_asset)
        assert op.broadcasted_at
        assert not op.completed_at

    wait_for_op_confirmations(eth_service, opid)

    # After tx has been mined the new balances should match
    with transaction.manager:
        op = dbsession.query(CryptoOperation).get(opid)
        assert op.broadcasted_at
        assert op.completed_at
        asset = dbsession.query(Asset).get(token_asset)

        # Tokens have been removed on from account
        token = Token.get(web3, bin_to_eth_address(asset.external_id))
        assert token.contract.call().balanceOf(deposit_address) == 8000

        # Tokens have been credited on to account
        token = Token.get(web3, bin_to_eth_address(asset.external_id))
        assert token.contract.call().balanceOf(target_account) == 2000
Example #27
0
def do_faux_withdraw(user_address: UserCryptoAddress, target_address, asset_id,
                     amount) -> UserCryptoOperation:
    """Simulate user withdrawing assets from one of his addresses."""
    dbsession = Session.object_session(user_address)
    asset = dbsession.query(Asset).get(asset_id)
    op = user_address.withdraw(asset,
                               Decimal(amount),
                               eth_address_to_bin(target_address),
                               "Simulated withraw",
                               required_confirmation_count=1)
    return op
def test_withdraw_token(dbsession, eth_network_id, web3: Web3, eth_service: EthereumService, deposit_address: str, token_asset: str, target_account: str):
    """"See that we can withdraw token outside to an account."""

    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), eth_address_to_bin(target_account), "Sending to friend")
        opid = op.id

        assert caccount.account.get_balance() == 8000

    # Run import token operation
    success_count, failure_count = eth_service.run_waiting_operations()
    assert success_count == 1
    assert failure_count == 0

    # TX was broadcasted, marked as complete
    with transaction.manager:
        op = dbsession.query(CryptoOperation).get(opid)
        assert isinstance(op, CryptoAddressWithdraw)
        asset = dbsession.query(Asset).get(token_asset)
        assert op.broadcasted_at
        assert not op.completed_at

    wait_for_op_confirmations(eth_service, opid)

    with transaction.manager:
        op = dbsession.query(CryptoOperation).get(opid)
        assert op.broadcasted_at
        assert op.completed_at
        asset = dbsession.query(Asset).get(token_asset)

        # Tokens have been removed on from account
        token = Token.get(web3, bin_to_eth_address(asset.external_id))
        assert token.contract.call().balanceOf(deposit_address) == 8000

        # Tokens have been credited on to account
        token = Token.get(web3, bin_to_eth_address(asset.external_id))
        assert token.contract.call().balanceOf(target_account) == 2000
Example #29
0
def withdraw(user_asset: UserAddressAsset, request):
    """Ask user for the withdraw details."""

    title = "Withdraw"
    wallet = user_asset.wallet
    asset_resource = user_asset
    network_resource = get_network_resource(request, asset_resource.asset.network)
    balance = format_asset_amount(user_asset.balance, user_asset.asset.asset_class)
    address_resource = asset_resource.address
    account = user_asset.account

    schema = WithdrawSchema().bind(request=request, account=account)
    b = deform.Button(name='process', title="Verify with SMS", css_class="btn-block btn-lg")
    form = deform.Form(schema, buttons=(b,))

    # User submitted this form
    if request.method == "POST":
        if 'process' in request.POST:

            try:
                appstruct = form.validate(request.POST.items())

                # Save form data from appstruct
                amount = appstruct["amount"]
                address = eth_address_to_bin(appstruct["address"])
                note = appstruct["note"]
                confirmations = get_required_confirmation_count(request.registry, user_asset.account.asset.network, CryptoOperationType.withdraw)

                user_crypto_address = asset_resource.address.address

                # Create the withdraw
                user_withdraw = user_crypto_address.withdraw(asset_resource.asset, amount, address, note, confirmations)

                # Mark it as pending for confirmation
                UserWithdrawConfirmation.require_confirmation(user_withdraw)

                # Redirect user to the confirmation page
                user_crypto_operation_resource = get_user_crypto_operation_resource(request, user_withdraw)
                return HTTPFound(request.resource_url(user_crypto_operation_resource, "confirm-withdraw"))

            except deform.ValidationFailure as e:
                # Render a form version where errors are visible next to the fields,
                # and the submitted values are posted back
                rendered_form = e.render()
        else:
            # We don't know which control caused form submission
            raise HTTPInternalServerError("Unknown form button pressed")
    else:
        # Render a form with initial values
        rendered_form = form.render()

    return locals()
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_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 #32
0
    def finish_op():
        op = dbsession.query(CryptoOperation).get(opid)
        txid = wallet.initial_txid
        receipt = web3.eth.getTransactionReceipt(txid)

        op.txid = txid_to_bin(txid)
        op.block = receipt["blockNumber"]
        op.address.address = eth_address_to_bin(wallet.address)
        op.external_address = op.address.address

        # There is no broadcast wait, so we can close this right away
        op.mark_performed()
        op.mark_broadcasted()
        op.mark_complete()
Example #33
0
    def finish_op():
        op = dbsession.query(CryptoOperation).get(opid)
        txid = wallet.initial_txid
        receipt = web3.eth.getTransactionReceipt(txid)

        op.txid = txid_to_bin(txid)
        op.block = receipt["blockNumber"]
        op.address.address = eth_address_to_bin(wallet.address)
        op.external_address = op.address.address

        # There is no broadcast wait, so we can close this right away
        op.mark_performed()
        op.mark_broadcasted()
        op.mark_complete()
Example #34
0
    def objectify(self, appstruct: dict, obj: Asset):
        """Store the dictionary data from the form submission on the object."""

        objectify(self, appstruct, obj, excludes=("long_description", "external_id"))

        if not obj.other_data:
            # When creating the object JSON value may be None
            # instead of empty dict
            obj.other_data = {}

        # Special case of field stored inside JSON bag
        obj.other_data["long_description"] = appstruct["long_description"]

        # Convert between binary storage and human readable hex presentation
        if appstruct["external_id"]:
            obj.external_id = eth_address_to_bin(appstruct["external_id"])
def test_confirm_user_withdraw_timeout(dbsession, eth_network_id, eth_asset_id, user_id, topped_up_user):
    """User did not reply to withdraw confirmation within the timeout."""
    with transaction.manager:
        uca = dbsession.query(UserCryptoAddress).first()
        asset = dbsession.query(Asset).get(eth_asset_id)
        withdraw_op = uca.withdraw(asset, Decimal(5), eth_address_to_bin(TEST_ADDRESS), "Foobar", 1)
        UserWithdrawConfirmation.require_confirmation(withdraw_op)

    with transaction.manager:
        ManualConfirmation.run_timeout_checks(dbsession, now() + timedelta(hours=12))

    with transaction.manager:
        confirmation = dbsession.query(UserWithdrawConfirmation).first()
        assert confirmation.action_taken_at
        assert confirmation.state == ManualConfirmationState.timed_out
        assert confirmation.user_crypto_operation.crypto_operation.state == CryptoOperationState.cancelled
        assert "error" in confirmation.user_crypto_operation.crypto_operation.other_data
def test_import_no_address_token(dbsession: Session, eth_network_id, web3: Web3, eth_service: EthereumService):
    """Import should fail for address that doesn't exist."""

    with transaction.manager:
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        # A random address
        op = import_token(network, eth_address_to_bin("0x2f70d3d26829e412a602e83fe8eebf80255aeea5"))
        opid = op.id

    # Success count here means the operation passed, but might be marked as failure
    success_count, failure_count = eth_service.run_waiting_operations()
    assert success_count == 1
    assert failure_count == 0

    with transaction.manager:
        op = dbsession.query(CryptoOperation).get(opid)
        assert op.failed_at
        assert op.other_data["error"].startswith("Address did not provide EIP-20 token API:")
Example #37
0
def _create_address(web3, dbsession, opid):

    with transaction.manager:
        op = dbsession.query(CryptoOperation).get(opid)
        txid = TEST_ADDRESS_INITIAL_TXID

        # Deterministically pull faux addresses from pool
        network = op.network
        address_pool = network.other_data["test_address_pool"]
        address = address_pool.pop()

        op.txid = txid_to_bin(txid)
        op.block = 666
        op.address.address = eth_address_to_bin(address)
        op.external_address = op.address.address

        op.mark_performed()
        op.mark_broadcasted()
def test_import_no_address_token(dbsession: Session, eth_network_id, web3: Web3, eth_service: EthereumService):
    """Import should fail for address that doesn't exist."""

    with transaction.manager:
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        # A random address
        op = import_token(network, eth_address_to_bin("0x2f70d3d26829e412a602e83fe8eebf80255aeea5"))
        opid = op.id

    # Success count here means the operation passed, but might be marked as failure
    success_count, failure_count = eth_service.run_waiting_operations()
    assert success_count == 1
    assert failure_count == 0

    with transaction.manager:
        op = dbsession.query(CryptoOperation).get(opid)
        assert op.failed_at
        assert op.other_data["error"].startswith("Address did not provide EIP-20 token API:")
Example #39
0
def _create_address(web3, dbsession, opid):

    with transaction.manager:
        op = dbsession.query(CryptoOperation).get(opid)
        txid = TEST_ADDRESS_INITIAL_TXID

        # Deterministically pull faux addresses from pool
        network = op.network
        address_pool = network.other_data["test_address_pool"]
        address = address_pool.pop()

        op.txid = txid_to_bin(txid)
        op.block = 666
        op.address.address = eth_address_to_bin(address)
        op.external_address = op.address.address

        op.mark_performed()
        op.mark_broadcasted()
Example #40
0
def test_deposit_eth_account(dbsession, eth_network_id, eth_service,
                             eth_asset_id):
    """Deposit Ethereums to an account."""

    # Create ETH holding account under an address
    with transaction.manager:

        # First create the address which holds our account
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        address = CryptoAddress(network=network,
                                address=eth_address_to_bin(TEST_ADDRESS))
        dbsession.add(address)
        dbsession.flush()

    # Create deposit op
    with transaction.manager:
        address = dbsession.query(CryptoAddress).one_or_none()
        asset = dbsession.query(Asset).get(eth_asset_id)
        txid = txid_to_bin(TEST_TXID)
        op = address.deposit(Decimal(10), asset, txid, bin_to_txid(txid))
        dbsession.add(op)
        opid = op.id

    # Resolve deposit op
    success_op_count, failed_op_count = eth_service.run_waiting_operations()
    assert success_op_count == 1
    assert failed_op_count == 0

    with transaction.manager:
        op = dbsession.query(CryptoOperation).get(opid)
        op.resolve()  # Force resolution regardless on confirmation count

    # Check balances are settled
    with transaction.manager:
        address = dbsession.query(CryptoAddress).one_or_none()
        asset = dbsession.query(Asset).get(eth_asset_id)
        account = address.get_account(asset)
        op = dbsession.query(CryptoOperation).one()
        assert account.account.get_balance() == Decimal(10)
        assert op.holding_account.get_balance() == 0

        # Transaction label should be the Ethereum txid
        tx = account.account.transactions.one()
        assert tx.message == TEST_TXID
Example #41
0
    def objectify(self, appstruct: dict, obj: Asset):
        """Store the dictionary data from the form submission on the object."""

        objectify(self,
                  appstruct,
                  obj,
                  excludes=("long_description", "external_id"))

        if not obj.other_data:
            # When creating the object JSON value may be None
            # instead of empty dict
            obj.other_data = {}

        # Special case of field stored inside JSON bag
        obj.other_data["long_description"] = appstruct["long_description"]

        # Convert between binary storage and human readable hex presentation
        if appstruct["external_id"]:
            obj.external_id = eth_address_to_bin(appstruct["external_id"])
Example #42
0
def toycrowd(dbsession, web3, eth_service, eth_network_id, token) -> Callable:
    """Set up a Toycrowd asset.

    :return: Crowdsale callable
    """

    data = get_crowdsale_data(token)

    with transaction.manager:
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        asset = network.create_asset(name=data["name"], symbol=data["symbol"], supply=data["supply"], asset_class=AssetClass.token)
        bin_address = eth_address_to_bin(token.address)
        asset.external_id = bin_address
        asset.other_data["contract"] = "CrowdfundToken"
        dbsession.add(asset)
        dbsession.flush()
        asset_id = asset.id

    return lambda: dbsession.query(Asset).get(asset_id)
def test_deposit_eth_account(dbsession, eth_network_id, eth_service, eth_asset_id):
    """Deposit Ethereums to an account."""

    # Create ETH holding account under an address
    with transaction.manager:

        # First create the address which holds our account
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        address = CryptoAddress(network=network, address=eth_address_to_bin(TEST_ADDRESS))
        dbsession.add(address)
        dbsession.flush()

    # Create deposit op
    with transaction.manager:
        address = dbsession.query(CryptoAddress).one_or_none()
        asset = dbsession.query(Asset).get(eth_asset_id)
        txid = txid_to_bin(TEST_TXID)
        op = address.deposit(Decimal(10), asset, txid, bin_to_txid(txid))
        dbsession.add(op)
        opid = op.id

    # Resolve deposit op
    success_op_count, failed_op_count = eth_service.run_waiting_operations()
    assert success_op_count == 1
    assert failed_op_count == 0

    with transaction.manager:
        op = dbsession.query(CryptoOperation).get(opid)
        op.resolve()  # Force resolution regardless on confirmation count

    # Check balances are settled
    with transaction.manager:
        address = dbsession.query(CryptoAddress).one_or_none()
        asset = dbsession.query(Asset).get(eth_asset_id)
        account = address.get_account(asset)
        op = dbsession.query(CryptoOperation).one()
        assert account.account.get_balance() == Decimal(10)
        assert op.holding_account.get_balance() == 0

        # Transaction label should be the Ethereum txid
        tx = account.account.transactions.one()
        assert tx.message == TEST_TXID
def test_confirm_user_withdraw_success(dbsession, eth_network_id, eth_asset_id, user_id, topped_up_user):
    """SMS confirmation success."""

    with transaction.manager:
        uca = dbsession.query(UserCryptoAddress).first()
        asset = dbsession.query(Asset).get(eth_asset_id)
        withdraw_op = uca.withdraw(asset, Decimal(5), eth_address_to_bin(TEST_ADDRESS), "Foobar", 1)
        confirmation = UserWithdrawConfirmation.require_confirmation(withdraw_op)
        assert confirmation.created_at
        assert confirmation.confirmation_type == ManualConfirmationType.sms
        assert confirmation.state == ManualConfirmationState.pending

    with transaction.manager:
        confirmation = dbsession.query(UserWithdrawConfirmation).first()
        code = confirmation.other_data["sms_code"]
        confirmation.resolve_sms(code, None)

        assert confirmation.action_taken_at
        assert confirmation.state == ManualConfirmationState.resolved
        assert confirmation.user_crypto_operation.crypto_operation.state == CryptoOperationState.waiting
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_confirm_user_withdraw_cancelled(dbsession, eth_network_id, eth_asset_id, user_id, topped_up_user):
    """User cancels the withdraw confirmation."""

    with transaction.manager:
        uca = dbsession.query(UserCryptoAddress).first()
        asset = dbsession.query(Asset).get(eth_asset_id)
        original_balance = uca.get_crypto_account(asset).account.get_balance()

        withdraw_op = uca.withdraw(asset, Decimal(7), eth_address_to_bin(TEST_ADDRESS), "Foobar", 1)
        UserWithdrawConfirmation.require_confirmation(withdraw_op)

    with transaction.manager:
        confirmation = dbsession.query(UserWithdrawConfirmation).first()
        confirmation.cancel()

        assert confirmation.action_taken_at
        assert confirmation.state == ManualConfirmationState.cancelled
        assert confirmation.user_crypto_operation.crypto_operation.state == CryptoOperationState.cancelled
        assert "error" in confirmation.user_crypto_operation.crypto_operation.other_data

        # The balance was restored
        uca = dbsession.query(UserCryptoAddress).first()
        asset = dbsession.query(Asset).get(eth_asset_id)
        assert uca.get_crypto_account(asset).account.get_balance() == original_balance
Example #47
0
def _create_token(web3, dbsession, opid):
    with transaction.manager:
        op = dbsession.query(CryptoOperation).get(opid)
        # Check everyting looks sane
        assert op.crypto_account.id
        assert op.crypto_account.account.id

        asset = op.holding_account.asset
        assert asset.id

        # Set information on asset that we have now created and have its smart contract id
        assert not asset.external_id, "Asset has been already assigned its smart contract id. Recreate error?"

        address = bin_to_eth_address(op.crypto_account.address.address)

        # Call geth RPC API over Populus contract proxy
        op.txid = txid_to_bin(TEST_TOKEN_TXID)
        op.block = None
        op.external_address = eth_address_to_bin(TEST_TOKEN_ADDRESS)

        asset.external_id = op.external_address

        op.mark_performed()
        op.mark_broadcasted()
Example #48
0
def _create_token(web3, dbsession, opid):
    with transaction.manager:
        op = dbsession.query(CryptoOperation).get(opid)
        # Check everyting looks sane
        assert op.crypto_account.id
        assert op.crypto_account.account.id

        asset = op.holding_account.asset
        assert asset.id

        # Set information on asset that we have now created and have its smart contract id
        assert not asset.external_id, "Asset has been already assigned its smart contract id. Recreate error?"

        address = bin_to_eth_address(op.crypto_account.address.address)

        # Call geth RPC API over Populus contract proxy
        op.txid = txid_to_bin(TEST_TOKEN_TXID)
        op.block = None
        op.external_address = eth_address_to_bin(TEST_TOKEN_ADDRESS)

        asset.external_id = op.external_address

        op.mark_performed()
        op.mark_broadcasted()
Example #49
0
def withdraw(user_asset: UserAddressAsset, request):
    """Ask user for the withdraw details."""

    title = "Withdraw"
    wallet = user_asset.wallet
    asset_resource = user_asset
    network_resource = get_network_resource(request, asset_resource.asset.network)
    balance = format_asset_amount(user_asset.balance, user_asset.asset.asset_class)
    address_resource = asset_resource.address
    account = user_asset.account

    schema = WithdrawSchema().bind(request=request, account=account)
    withdraw = deform.Button(name='process', title="Verify with SMS", css_class="btn-primary btn-block btn-lg")
    cancel = deform.Button(name='cancel', title="Cancel", css_class="btn-primary btn-block btn-lg")
    form = deform.Form(schema, buttons=(withdraw, cancel), resource_registry=ResourceRegistry(request))

    # User submitted this form
    if request.method == "POST":
        if 'process' in request.POST:

            try:
                appstruct = form.validate(request.POST.items())

                # Save form data from appstruct
                amount = appstruct["amount"]
                address = eth_address_to_bin(appstruct["address"])
                note = appstruct["note"]
                confirmations = get_required_confirmation_count(request.registry, user_asset.account.asset.network, CryptoOperationType.withdraw)

                user_crypto_address = asset_resource.address.address

                # Create the withdraw
                user_withdraw = user_crypto_address.withdraw(asset_resource.asset, amount, address, note, confirmations)

                # Ethereum special parameters
                user_withdraw.crypto_operation.other_data["gas"] = appstruct["advanced"]["gas"]
                data = appstruct["advanced"]["data"]
                if data:
                    user_withdraw.crypto_operation.other_data["data"] = data

                # Mark it as pending for confirmation
                UserWithdrawConfirmation.require_confirmation(user_withdraw)

                # Redirect user to the confirmation page
                user_crypto_operation_resource = get_user_crypto_operation_resource(request, user_withdraw)
                return HTTPFound(request.resource_url(user_crypto_operation_resource, "confirm-withdraw"))

            except deform.ValidationFailure as e:
                # Render a form version where errors are visible next to the fields,
                # and the submitted values are posted back
                rendered_form = e.render()
        elif "cancel" in request.POST:
            return HTTPFound(request.resource_url(wallet))
        else:
            # We don't know which control caused form submission
            raise HTTPInternalServerError("Unknown form button pressed")
    else:
        # Render a form with initial values
        rendered_form = form.render()

    # This loads widgets specific CSS/JavaScript in HTML code,
    # if form widgets specify any static assets.
    form.resource_registry.pull_in_resources(request, form)

    return locals()
Example #50
0
def test_withdraw_eth_account(dbsession, eth_service, eth_network_id,
                              eth_asset_id):
    """Withdraw ETHs to an address."""

    # Create ETH holding account under an address
    with transaction.manager:

        # First create the address which holds our account
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        address = CryptoAddress(network=network,
                                address=eth_address_to_bin(TEST_ADDRESS))
        dbsession.flush()

        assert address.id
        assert address.address
        asset = dbsession.query(Asset).get(eth_asset_id)

        # Create an account of ETH tokens on that address
        ca_account = address.create_account(asset)

    # It should have zero balance by default
    with transaction.manager:
        ca_account = dbsession.query(CryptoAddressAccount).one_or_none()
        assert ca_account.account.asset_id == eth_asset_id
        assert ca_account.account.get_balance() == Decimal(0)

    # Faux top up so we have value to withdraw
    with transaction.manager:
        ca_account = dbsession.query(CryptoAddressAccount).one_or_none()
        assert ca_account.account.do_withdraw_or_deposit(
            Decimal("+10"), "Faux top up")

    # Create withdraw operations
    withdraw_address = eth_address_to_bin(TEST_ADDRESS)
    with transaction.manager:
        ca_account = dbsession.query(CryptoAddressAccount).one_or_none()
        op = ca_account.withdraw(Decimal("10"), withdraw_address,
                                 "Bailing out")

        # We withdraw 10 ETHs
        assert op.holding_account.get_balance() == Decimal("10")
        assert op.holding_account.asset == dbsession.query(Asset).get(
            eth_asset_id)
        assert op.holding_account.transactions.count() == 1
        assert op.holding_account.transactions.first().message == "Bailing out"

        # Check all looks good on sending account
        assert ca_account.account.transactions.count() == 2
        assert ca_account.account.transactions.all(
        )[0].message == "Faux top up"
        assert ca_account.account.transactions.all(
        )[1].message == "Bailing out"
        assert ca_account.account.get_balance() == 0

    def _withdraw_eth(service, dbsession, opid):
        # Mocked withdraw op that always success
        with transaction.manager:
            op = dbsession.query(CryptoOperation).get(opid)
            op.txid = txid_to_bin(TEST_TXID)
            op.mark_complete()

    with mock.patch("websauna.wallet.ethereum.ops.withdraw_eth",
                    new=_withdraw_eth):
        success_op_count, failed_op_count = eth_service.run_waiting_operations(
        )

    # Check that operations have been marked as success
    with transaction.manager:
        ops = list(dbsession.query(CryptoOperation).all())
        assert len(ops) == 1
        assert isinstance(ops[0], CryptoAddressWithdraw)
        assert ops[0].state == CryptoOperationState.success
        assert ops[0].txid == txid_to_bin(TEST_TXID)
Example #51
0
def do_faux_withdraw(user_address: UserCryptoAddress, target_address, asset_id, amount) -> UserCryptoOperation:
    """Simulate user withdrawing assets from one of his addresses."""
    dbsession = Session.object_session(user_address)
    asset = dbsession.query(Asset).get(asset_id)
    op = user_address.withdraw(asset, Decimal(amount), eth_address_to_bin(target_address), "Simulated withraw", required_confirmation_count=1)
    return op
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
Example #54
0
 def get_title(self):
     if not self.get_object().address:
         return "-"
     return eth_address_to_bin(self.get_object().address)
Example #55
0
 def get_title(self):
     if not self.get_object().address:
         return "-"
     return eth_address_to_bin(self.get_object().address)
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_account(dbsession, eth_service, eth_network_id, eth_asset_id):
    """Withdraw ETHs to an address."""

    # Create ETH holding account under an address
    with transaction.manager:

        # First create the address which holds our account
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        address = CryptoAddress(network=network, address=eth_address_to_bin(TEST_ADDRESS))
        dbsession.flush()

        assert address.id
        assert address.address
        asset = dbsession.query(Asset).get(eth_asset_id)

        # Create an account of ETH tokens on that address
        ca_account = address.create_account(asset)

    # It should have zero balance by default
    with transaction.manager:
        ca_account = dbsession.query(CryptoAddressAccount).one_or_none()
        assert ca_account.account.asset_id == eth_asset_id
        assert ca_account.account.get_balance() == Decimal(0)

    # Faux top up so we have value to withdraw
    with transaction.manager:
        ca_account = dbsession.query(CryptoAddressAccount).one_or_none()
        assert ca_account.account.do_withdraw_or_deposit(Decimal("+10"), "Faux top up")

    # Create withdraw operations
    withdraw_address = eth_address_to_bin(TEST_ADDRESS)
    with transaction.manager:
        ca_account = dbsession.query(CryptoAddressAccount).one_or_none()
        op = ca_account.withdraw(Decimal("10"), withdraw_address, "Bailing out")

        # We withdraw 10 ETHs
        assert op.holding_account.get_balance() == Decimal("10")
        assert op.holding_account.asset == dbsession.query(Asset).get(eth_asset_id)
        assert op.holding_account.transactions.count() == 1
        assert op.holding_account.transactions.first().message == "Bailing out"

        # Check all looks good on sending account
        assert ca_account.account.transactions.count() == 2
        assert ca_account.account.transactions.all()[0].message == "Faux top up"
        assert ca_account.account.transactions.all()[1].message == "Bailing out"
        assert ca_account.account.get_balance() == 0

    def _withdraw_eth(service, dbsession, opid):
        # Mocked withdraw op that always success
        with transaction.manager:
            op = dbsession.query(CryptoOperation).get(opid)
            op.txid = txid_to_bin(TEST_TXID)
            op.mark_complete()

    with mock.patch("websauna.wallet.ethereum.ops.withdraw_eth", new=_withdraw_eth):
        success_op_count, failed_op_count = eth_service.run_waiting_operations()

    # Check that operations have been marked as success
    with transaction.manager:
        ops = list(dbsession.query(CryptoOperation).all())
        assert len(ops) == 1
        assert isinstance(ops[0], CryptoAddressWithdraw)
        assert ops[0].state == CryptoOperationState.success
        assert ops[0].txid == txid_to_bin(TEST_TXID)
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