Ejemplo n.º 1
0
def test_starter_token(dbsession, registry, eth_network_id, web3: Web3, eth_service: EthereumService, house_address, toybox):
    """See that a fresh user account is supplied with some play assets."""

    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

    # We need another event cycle to process the initial asset transfers
    with transaction.manager:
        user = dbsession.query(User).first()
        opid = user.user_data["starter_asset_txs"][0]["toybox"]
        assert opid

    wait_for_op_confirmations(eth_service, opid)

    # Let the transfer come through
    eth_service.run_event_cycle()

    # Make sure we confirm the deposit
    with transaction.manager:
        user = dbsession.query(User).first()
        user_depo = user.owned_crypto_operations.join(CryptoOperation).filter_by(operation_type=CryptoOperationType.deposit).first()
        opid = user_depo.crypto_operation.id

    wait_for_op_confirmations(eth_service, opid)

    with transaction.manager:

        # Sanity check our token contract posts us logs
        user = dbsession.query(User).first()
        client = get_rpc_client(web3)
        asset = dbsession.query(Asset).get(toybox)
        address = bin_to_eth_address(asset.external_id)
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        user_address = UserCryptoAddress.get_default(user, network).address
        house_address = dbsession.query(CryptoAddress).get(house_address)
        house = bin_to_eth_address(house_address.address)
        token = Token.get(web3, address)

        # Check we correctly resolved low level logs
        token_logs = client.get_logs(from_block=0, address=[address])
        wallet_logs = client.get_logs(from_block=0, address=[house])
        assert len(token_logs) > 0
        assert len(wallet_logs) > 0

        # Check contract state matches
        assert token.contract.call().balanceOf(house) == 9990
        assert token.contract.call().balanceOf(bin_to_eth_address(user_address.address)) == 10

        # Check our internal book keeping matches
        assert house_address.get_account(asset).account.get_balance() == 9990
        assert user_address.get_account(asset).account.get_balance() == 10
Ejemplo n.º 2
0
    def perform_tx():
        op = dbsession.query(CryptoOperation).get(opid)
        address = bin_to_eth_address(op.external_address)
        token = Token.get(web3, address)

        network = op.network

        def gen_error(e: Exception):
            # Set operation as impossible to complete
            # Set user readable and technical error explanation
            op.mark_failed()
            op.other_data[
                "error"] = "Address did not provide EIP-20 token API:" + address
            op.other_data["exception"] = str(e)
            logger.exception(e)

        try:
            name = token.contract.call().name()
            symbol = token.contract.call().symbol()
            supply = Decimal(token.contract.call().totalSupply())
        except BadFunctionCallOutput as e:
            # When we try to access a contract attrib which is not supported by underlying code
            gen_error(e)
            return

        asset = network.create_asset(name=name,
                                     symbol=symbol,
                                     supply=supply,
                                     asset_class=AssetClass.token)
        asset.external_id = op.external_address

        # Fill in balances for the addresses we host
        # TODO: Too much for one transaction
        for caddress in dbsession.query(CryptoAddress).all():

            # Returns 0 for unknown addresses
            try:
                amount = token.contract.call().balanceOf(
                    bin_to_eth_address(caddress.address))
            except BadFunctionCallOutput as e:
                # Bad contract doesn't define balanceOf()
                # This leaves badly imported asset
                gen_error(e)
                return

            if amount > 0:
                account = caddress.get_or_create_account(asset)
                account.account.do_withdraw_or_deposit(
                    Decimal(amount), "Token contract import")

        # This operation immediately closes
        op.mark_performed()
        op.mark_broadcasted()
        op.mark_complete()
Ejemplo n.º 3
0
    def perform_tx():
        op = dbsession.query(CryptoOperation).get(opid)
        address = bin_to_eth_address(op.external_address)
        token = Token.get(web3, address)

        network = op.network

        def gen_error(e: Exception):
            # Set operation as impossible to complete
            # Set user readable and technical error explanation
            op.mark_failed()
            op.other_data["error"] = "Address did not provide EIP-20 token API:" + address
            op.other_data["exception"] = str(e)
            logger.exception(e)

        try:
            name = token.contract.call().name()
            symbol = token.contract.call().symbol()
            supply = Decimal(token.contract.call().totalSupply())
        except eth_abi.exceptions.DecodingError as e:
            # When we try to access a contract attrib which is not supported by underlying code
            gen_error(e)
            return

        asset = network.create_asset(name=name, symbol=symbol, supply=supply, asset_class=AssetClass.token)
        asset.external_id = op.external_address

        # Fill in balances for the addresses we host
        # TODO: Too much for one transaction
        for caddress in dbsession.query(CryptoAddress).all():

            # Returns 0 for unknown addresses
            try:
                amount = token.contract.call().balanceOf(bin_to_eth_address(caddress.address))
            except eth_abi.exceptions.DecodingError as e:
                # Bad contract doesn't define balanceOf()
                # This leaves badly imported asset
                gen_error(e)
                return

            if amount > 0:
                account = caddress.get_or_create_account(asset)
                account.account.do_withdraw_or_deposit(Decimal(amount), "Token contract import")

        # This operation immediately closes
        op.mark_performed()
        op.mark_broadcasted()
        op.mark_complete()
Ejemplo n.º 4
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()
Ejemplo n.º 5
0
def deposit_address(eth_service, eth_network_id, dbsession, registry) -> str:
    """Creates an address that has matching account on Geth.

    Sending ETH to this address should trigger a incoming tx logic.

    :return: 0x hex presentation
    """

    with transaction.manager:
        network = dbsession.query(AssetNetwork).get(eth_network_id)

        address = CryptoAddress(network=network)

        dbsession.flush()

        # Generate address on the account
        op = CryptoAddressCreation(address)
        dbsession.add(op)
        dbsession.flush()

    # Creates a hosted wallet
    success_op_count, failed_op_count = eth_service.run_waiting_operations()
    assert success_op_count == 1

    with transaction.manager:
        return bin_to_eth_address(dbsession.query(CryptoAddress).one().address)
Ejemplo n.º 6
0
    def __str__(self):
        dbsession = Session.object_session(self)
        address = self.external_address and bin_to_eth_address(
            self.external_address) or "-"
        account = self.crypto_account and self.crypto_account.account or "-"
        failure_reason = self.other_data.get("error") or ""

        if self.has_txid():
            network_status = dbsession.query(CryptoNetworkStatus).get(
                self.network_id)
            if network_status:
                nblock = network_status.block_number
            else:
                nblock = "network-missing"

            if self.txid:
                txid = bin_to_txid(self.txid)
            else:
                txid = "-"

            txinfo = "txid:{} block:{} nblock:{}".format(
                txid, self.block, nblock)
        else:
            txinfo = ""

        return "{} {} externaladdress:{} completed:{} confirmed:{} failed:{} acc:{} holding:{} network:{} {}".format(
            self.operation_type, self.state, address, self.completed_at,
            self.confirmed_at, failure_reason, account, self.holding_account,
            self.network.name, txinfo)
Ejemplo n.º 7
0
def deposit_address(eth_service, eth_network_id, dbsession, registry) -> str:
    """Creates an address that has matching account on Geth.

    Sending ETH to this address should trigger a incoming tx logic.

    :return: 0x hex presentation
    """

    with transaction.manager:
        network = dbsession.query(AssetNetwork).get(eth_network_id)

        address = CryptoAddress(network=network)

        dbsession.flush()

        # Generate address on the account
        op = CryptoAddressCreation(address)
        dbsession.add(op)
        dbsession.flush()

    # Creates a hosted wallet
    success_op_count, failed_op_count = eth_service.run_waiting_operations()
    assert success_op_count == 1

    with transaction.manager:
        return bin_to_eth_address(dbsession.query(CryptoAddress).one().address)
Ejemplo n.º 8
0
def give_eth(event):
    """Feed user some test ETH from coinbase."""
    user = event.user

    # TODO: Rework this
    from websauna.wallet.tests.eth.utils import send_balance_to_address, do_faux_deposit

    amount = event.network.other_data["initial_assets"].get("eth_amount")
    if not amount:
        return

    # Supply eth from coinbase
    address = bin_to_eth_address(event.address.address)
    if event.web3:
        txid = send_balance_to_address(event.web3, address, Decimal(amount))
    else:
        # MockEthreumService test
        dbsession = Session.object_session(event.address)
        network = event.address.network
        asset = get_ether_asset(dbsession, network)
        op = do_faux_deposit(event.address, asset.id, Decimal(amount))
        txid = bin_to_txid(op.txid)

    # Record this operation in user data so we can verify it later
    op_txs = user.user_data.get("starter_asset_txs", [])
    op_txs.append({"eth": txid})
    user.user_data["starter_asset_txs"] = op_txs
Ejemplo n.º 9
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()
Ejemplo n.º 10
0
    def render_sms(self, template_context):

        op = self.context.op
        amount = format_asset_amount(op.amount, op.asset.asset_class)
        template_context.update({
            "amount": amount,
            "asset": op.asset.name,
            "address": bin_to_eth_address(op.external_address),
        })
        return render("wallet/sms/confirm_withdraw.txt", template_context, request=self.request)
Ejemplo n.º 11
0
    def render_sms(self, template_context):

        op = self.context.op
        amount = format_asset_amount(op.amount, op.asset.asset_class)
        template_context.update({
            "amount": amount,
            "asset": op.asset.name,
            "address": bin_to_eth_address(op.external_address),
        })
        return render("wallet/sms/confirm_withdraw.txt", template_context, request=self.request)
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
Ejemplo n.º 13
0
    def get_monitored_addresses(self) -> Iterable[str]:
        """Get list of all ETH crtypto deposit addresses."""
        addresses = []
        for addr in self.dbsession.query(
                CryptoAddress, CryptoAddress.address).filter(
                    CryptoAddress.network_id == self.network_id,
                    CryptoAddress.address != None):
            # addr.address is not set if the address is under construction
            addresses.append(bin_to_eth_address(addr.address))

        return addresses
Ejemplo n.º 14
0
    def prepare_withdraw():
        # Check everyting looks sane
        op = dbsession.query(CryptoOperation).get(opid)
        assert op.crypto_account.id
        assert op.crypto_account.account.id
        assert op.holding_account.id
        assert op.holding_account.get_balance() > 0
        assert op.external_address
        assert op.required_confirmation_count  # Should be set by the creator
        asset = op.holding_account.asset
        assert asset.asset_class == AssetClass.token

        from_address = bin_to_eth_address(op.crypto_account.address.address)
        to_address = bin_to_eth_address(op.external_address)
        asset_address = bin_to_eth_address(asset.external_id)

        # How much we are withdrawing
        amount = op.holding_account.transactions.one().amount
        op.mark_performed()  # Don't try to pick this op automatically again
        return from_address, to_address, asset_address, amount
Ejemplo n.º 15
0
    def prepare_withdraw():
        # Check everyting looks sane
        op = dbsession.query(CryptoOperation).get(opid)
        assert op.crypto_account.id
        assert op.crypto_account.account.id
        assert op.holding_account.id
        assert op.holding_account.get_balance() > 0
        assert op.external_address
        assert op.required_confirmation_count  # Should be set by the creator
        asset = op.holding_account.asset
        assert asset.asset_class == AssetClass.token

        from_address = bin_to_eth_address(op.crypto_account.address.address)
        to_address = bin_to_eth_address(op.external_address)
        asset_address = bin_to_eth_address(asset.external_id)

        # How much we are withdrawing
        amount = op.holding_account.transactions.one().amount
        op.mark_performed()  # Don't try to pick this op automatically again
        return from_address, to_address, asset_address, amount
Ejemplo n.º 16
0
    def get_monitored_addresses(self) -> Iterable[str]:
        """Get list of all known token smart contract addresses."""

        addresses = []

        for asset in self.dbsession.query(Asset, Asset.external_id).filter(
                Asset.network_id == self.network_id,
                Asset.external_id != None):
            addresses.append(bin_to_eth_address(asset.external_id))

        return addresses
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
Ejemplo n.º 18
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")
Ejemplo n.º 19
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)

    # 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")
Ejemplo n.º 20
0
def describe_operation(request, uop: UserOperation) -> dict:
    """Fetch operation details and link data for rendering."""
    assert isinstance(uop, UserOperation)
    detail = {}
    op = uop.uop.crypto_operation

    # Link to the user asset details
    detail["op"] = op
    if op.holding_account and op.holding_account.asset:
        detail["asset_resource"] = get_user_address_asset(
            request, op.address, op.holding_account.asset)

    tx = op.primary_tx

    # From: and To: swap in address rendering
    detail["deposit_like"] = op.operation_type in (
        CryptoOperationType.deposit, )

    confirmations = op.calculate_confirmations()

    if confirmations is not None:
        if confirmations > 30:
            confirmations = "30+"
        detail["confirmations"] = confirmations

    if op.external_address:
        detail["external_address"] = bin_to_eth_address(op.external_address)

    if op.txid:
        detail["txid"] = bin_to_txid(op.txid)

    amount = op.amount

    if amount:
        detail["amount"] = format_asset_amount(amount, op.asset.asset_class)

    detail["uuid"] = str(uop.uop.id)
    detail["resource"] = uop
    detail["tx_name"] = uop.get_title()
    detail["state"] = OP_STATES[op.state]
    detail["address_resource"] = get_user_address_resource(request, op.address)
    detail["network_resource"] = get_network_resource(request, op.network)

    detail[
        "manual_confirmation_needed"] = op.state == CryptoOperationState.confirmation_required

    if tx:
        detail["notes"] = tx.message

    return detail
Ejemplo n.º 21
0
    def dictify(self, obj: Asset) -> dict:
        """Serialize SQLAlchemy model instance to nested dictionary appstruct presentation."""

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

        # Convert between binary storage and human readable hex presentation
        appstruct["long_description"] = obj.other_data.pop("long_description", "")

        if obj.external_id:
            appstruct["external_id"] = bin_to_eth_address(obj.external_id)
        else:
            appstruct["external_id"] = ""

        return appstruct
Ejemplo n.º 22
0
class CryptoAddressListing(DefaultListing):
    """List all crypto addresses on the site."""

    table = listing.Table(columns=[
        listing.Column("user", "User", getter=find_user_for_address),
        listing.Column("network",
                       "Network",
                       getter=lambda v, c, address: address.network.name),
        listing.Column(
            "address",
            "Address",
            getter=lambda v, c, address: bin_to_eth_address(address.address)),
        listing.Column("eth", "ETH balance", getter=find_eth_balance),
        listing.ControlsColumn()
    ])
Ejemplo n.º 23
0
def describe_operation(request, uop: UserOperation) -> dict:
    """Fetch operation details and link data for rendering."""
    assert isinstance(uop, UserOperation)
    detail = {}
    op = uop.uop.crypto_operation

    # Link to the user asset details
    detail["op"] = op
    if op.holding_account and op.holding_account.asset:
        detail["asset_resource"] = get_user_address_asset(request, op.address, op.holding_account.asset)

    tx = op.primary_tx

    # From: and To: swap in address rendering
    detail["deposit_like"] = op.operation_type in (CryptoOperationType.deposit,)

    confirmations = op.calculate_confirmations()

    if confirmations is not None:
        if confirmations > 30:
            confirmations = "30+"
        detail["confirmations"] = confirmations

    if op.external_address:
        detail["external_address"] = bin_to_eth_address(op.external_address)

    if op.txid:
        detail["txid"] = bin_to_txid(op.txid)

    amount = op.amount

    if amount:
        detail["amount"] = format_asset_amount(amount, op.asset.asset_class)

    detail["uuid"] = str(uop.uop.id)
    detail["resource"] = uop
    detail["tx_name"] = uop.get_title()
    detail["state"] = OP_STATES[op.state]
    detail["address_resource"] = get_user_address_resource(request, op.address)
    detail["network_resource"] = get_network_resource(request, op.network)

    detail["manual_confirmation_needed"] = op.state == CryptoOperationState.confirmation_required

    if tx:
        detail["notes"] = tx.message

    return detail
Ejemplo n.º 24
0
    def dictify(self, obj: Asset) -> dict:
        """Serialize SQLAlchemy model instance to nested dictionary appstruct presentation."""

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

        # Convert between binary storage and human readable hex presentation
        appstruct["long_description"] = obj.other_data.pop(
            "long_description", "")

        if obj.external_id:
            appstruct["external_id"] = bin_to_eth_address(obj.external_id)
        else:
            appstruct["external_id"] = ""

        return appstruct
Ejemplo n.º 25
0
    def prepare_withdraw():
        # Check everyting looks sane
        op = dbsession.query(CryptoOperation).get(opid)
        assert op.crypto_account.id
        assert op.crypto_account.account.id
        assert op.holding_account.id
        assert op.holding_account.get_balance() > 0
        assert op.external_address
        assert op.required_confirmation_count  # Should be set by the creator

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

        # How much we are withdrawing
        amount = op.holding_account.transactions.one().amount
        op.mark_performed()  # Don't pick this to action list anymore

        return address, amount, op.external_address
Ejemplo n.º 26
0
def withdraw_eth(web3: Web3, dbsession: Session, opid: UUID):
    """Perform ETH withdraw operation from the wallet."""
    @retryable(tm=dbsession.transaction_manager)
    def prepare_withdraw():
        # Check everyting looks sane
        op = dbsession.query(CryptoOperation).get(opid)
        assert op.crypto_account.id
        assert op.crypto_account.account.id
        assert op.holding_account.id
        assert op.holding_account.get_balance() > 0
        assert op.external_address
        assert op.required_confirmation_count  # Should be set by the creator

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

        # How much we are withdrawing
        amount = op.holding_account.transactions.one().amount
        op.mark_performed()  # Don't pick this to action list anymore

        gas = op.other_data.get("gas")
        data = op.other_data.get("data")

        return address, amount, op.external_address, gas, data

    @retryable(tm=dbsession.transaction_manager)
    def close_withdraw():
        # Fill in details.
        # Block number will be filled in later, when confirmation updater picks a transaction receipt for this operation.
        op = dbsession.query(CryptoOperation).get(opid)
        op.txid = txid_to_bin(txid)
        op.block = None
        op.mark_broadcasted()

    address, amount, external_address, gas, data = prepare_withdraw()
    # Do actual network communications outside the transaction,
    # so we avoid double withdraws in the case transaction is retried
    wallet = HostedWallet.get(web3, address)
    txid = wallet.withdraw(bin_to_eth_address(external_address),
                           amount,
                           max_gas=gas,
                           data=data)
    close_withdraw()
Ejemplo n.º 27
0
    def prepare_withdraw():
        # Check everyting looks sane
        op = dbsession.query(CryptoOperation).get(opid)
        assert op.crypto_account.id
        assert op.crypto_account.account.id
        assert op.holding_account.id
        assert op.holding_account.get_balance() > 0
        assert op.external_address
        assert op.required_confirmation_count  # Should be set by the creator

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

        # How much we are withdrawing
        amount = op.holding_account.transactions.one().amount
        op.mark_performed()  # Don't pick this to action list anymore

        gas = op.other_data.get("gas")
        data = op.other_data.get("data")

        return address, amount, op.external_address, gas, data
Ejemplo n.º 28
0
class AssetListing(adminbaseviews.Listing):

    table = listing.Table(columns=[
        # listing.Column("id", "Id",),
        listing.Column("name", "Name", getter=lambda v, c, asset: asset.name),
        listing.Column("description",
                       "Description",
                       getter=lambda v, c, asset: asset.name),

        # Convert between binary storage and human readable hex presentation
        listing.Column("address",
                       "Address",
                       getter=lambda v, c, asset: asset.external_id and
                       bin_to_eth_address(asset.external_id or "")),
        listing.ControlsColumn()
    ])

    def order_query(self, query: Query):
        """Sort the query."""
        return query.order_by(Asset.name)
Ejemplo n.º 29
0
    def __str__(self):
        dbsession = Session.object_session(self)
        address = self.external_address and bin_to_eth_address(self.external_address) or "-"
        account = self.crypto_account and self.crypto_account.account or "-"
        failure_reason = self.other_data.get("error") or ""

        if self.has_txid():
            network_status = dbsession.query(CryptoNetworkStatus).get(self.network_id)
            if network_status:
                nblock = network_status.block_number
            else:
                nblock = "network-missing"

            if self.txid:
                txid = bin_to_txid(self.txid)
            else:
                txid = "-"

            txinfo = "txid:{} block:{} nblock:{}".format(txid, self.block, nblock)
        else:
            txinfo = ""

        return "{} {} externaladdress:{} completed:{} confirmed:{} failed:{} acc:{} holding:{} network:{} {}".format(self.operation_type, self.state, address, self.completed_at, self.confirmed_at, failure_reason, account, self.holding_account, self.network.name, txinfo)
Ejemplo n.º 30
0
    def check_bad_hosted_wallet_events(self, op: CryptoOperation, receipt: dict):

        bad_events = {

            # OutOfGasWithdraw
            "0xb41c321524c6519973481dcf28a48b2c632cc3a7a2bb4ee1b849d68392f044e0": "Out of gas when performing the transaction. Please specify higher gas limit and try again.",

            # ExceededWithdraw
            "0x77c51a30b87b909c33ea71d8a231c335e73c443c891c73b0b2f9226dc65ffe9c": "Exceeded the available balance",

            # NoMatchingFunction
            "0x1b021189060cad216b135fb489585944f25904395c4ec6fe7d76187f32e3303d": "No matching function",
        }

        # Any of HostedWallet failure events
        # {'transactionIndex': 0, 'contractAddress': None, 'from': '0xaab2a08c0e0d8fa965e0b5167bbc33c145d29416', 'to': '0x50fd35249be4ce4e48f9f97ca8e73dba93c4f45b', 'transactionHash': '0x5b36e438f548a19ed6cdb655965fc51d67a890641a7b227b9366dc0b8d3d6f89', 'logs': [{'address': '0x50fd35249be4ce4e48f9f97ca8e73dba93c4f45b', 'transactionIndex': 0, 'logIndex': 0, 'topics': ['0xb41c321524c6519973481dcf28a48b2c632cc3a7a2bb4ee1b849d68392f044e0'], 'data': '0x000000000000000000000000f77c795e79a02a39100ac0c3f027943c60a588e70000000000000000000000000000000000000000000000000011c37937e08000', 'transactionHash': '0x5b36e438f548a19ed6cdb655965fc51d67a890641a7b227b9366dc0b8d3d6f89', 'blockHash': '0xc6ae4fe252cea45493564ffc60bfc4227af3d32615da29a2df5a503cd150487b', 'blockNumber': 9}], 'blockHash': '0xc6ae4fe252cea45493564ffc60bfc4227af3d32615da29a2df5a503cd150487b', 'cumulativeGasUsed': 34136, 'gasUsed': 34136, 'root': '5edc07ef145e2eaa489bf7cfaccff1f72548fc54b44859889b96b1be2b534141', 'blockNumber': 9}
        if isinstance(op, CryptoAddressWithdraw):
            for log in receipt["logs"]:
                # Make sure event was generated by HostedWallet, not a contract it's calling
                if log["address"] == bin_to_eth_address(op.get_from_address()):
                    for t in log["topics"]:
                        if t in bad_events:
                            return bad_events[t]
Ejemplo n.º 31
0
def withdraw_eth(web3: Web3, dbsession: Session, opid: UUID):
    """Perform ETH withdraw operation from the wallet."""

    @retryable(tm=dbsession.transaction_manager)
    def prepare_withdraw():
        # Check everyting looks sane
        op = dbsession.query(CryptoOperation).get(opid)
        assert op.crypto_account.id
        assert op.crypto_account.account.id
        assert op.holding_account.id
        assert op.holding_account.get_balance() > 0
        assert op.external_address
        assert op.required_confirmation_count  # Should be set by the creator

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

        # How much we are withdrawing
        amount = op.holding_account.transactions.one().amount
        op.mark_performed()  # Don't pick this to action list anymore

        return address, amount, op.external_address

    @retryable(tm=dbsession.transaction_manager)
    def close_withdraw():
        # Fill in details.
        # Block number will be filled in later, when confirmation updater picks a transaction receipt for this operation.
        op = dbsession.query(CryptoOperation).get(opid)
        op.txid = txid_to_bin(txid)
        op.block = None
        op.mark_broadcasted()

    address, amount, external_address = prepare_withdraw()
    # Do actual network communications outside the transaction,
    # so we avoid double withdraws in the case transaction is retried
    wallet = HostedWallet.get(web3, address)
    txid = wallet.withdraw(bin_to_eth_address(external_address), amount)
    close_withdraw()
Ejemplo n.º 32
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()
Ejemplo n.º 33
0
def house_address(dbsession, eth_service, web3, eth_network_id) -> UUID:
    """Create a network specific house address.

    :return: Address UUID
    """
    with transaction.manager:
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        op = create_house_address(network)
        opid = op.id
        address_id = op.address.id
        assert address_id

    # this runs op
    eth_service.run_waiting_operations()

    with transaction.manager:
        address = dbsession.query(CryptoAddress).get(address_id)
        addr = bin_to_eth_address(address.address)

    # Send some funds to the house from coinbase
    txid = send_balance_to_address(web3, addr, Decimal("0.1"))
    wait_tx(web3, txid)

    return address_id
Ejemplo n.º 34
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()
Ejemplo n.º 35
0
def house_address(dbsession, eth_service, web3, eth_network_id) -> UUID:
    """Create a network specific house address.

    :return: Address UUID
    """
    with transaction.manager:
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        op = create_house_address(network)
        opid = op.id
        address_id = op.address.id
        assert address_id

    # this runs op
    eth_service.run_waiting_operations()

    with transaction.manager:
        address = dbsession.query(CryptoAddress).get(address_id)
        addr = bin_to_eth_address(address.address)

    # Send some funds to the house from coinbase
    txid = send_balance_to_address(web3, addr, Decimal("0.1"))
    wait_tx(web3, txid)

    return address_id
Ejemplo n.º 36
0
 def get_monitored_addresses(self) -> Iterable[str]:
     """Get list of all known token smart contract addresses."""
     with self._get_tm():
         for asset in self.dbsession.query(Asset, Asset.external_id).filter(Asset.network_id == self.network_id, Asset.external_id != None):
             yield bin_to_eth_address(asset.external_id)
Ejemplo n.º 37
0
 def get_monitored_addresses(self) -> Iterable[str]:
     """Get list of all ETH crtypto deposit addresses."""
     with self._get_tm():
         for addr in self.dbsession.query(CryptoAddress, CryptoAddress.address).filter(CryptoAddress.network_id == self.network_id, CryptoAddress.address != None):
             # addr.address is not set if the address is under construction
             yield bin_to_eth_address(addr.address)
Ejemplo n.º 38
0
def test_starter_token(dbsession, registry, eth_network_id, web3: Web3,
                       eth_service: EthereumService, house_address, toybox):
    """See that a fresh user account is supplied with some play assets."""

    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

    # We need another event cycle to process the initial asset transfers
    with transaction.manager:
        user = dbsession.query(User).first()
        opid = user.user_data["starter_asset_txs"][0]["toybox"]
        assert opid

    wait_for_op_confirmations(eth_service, opid)

    # Let the transfer come through
    eth_service.run_event_cycle()

    # Make sure we confirm the deposit
    with transaction.manager:
        user = dbsession.query(User).first()
        user_depo = user.owned_crypto_operations.join(
            CryptoOperation).filter_by(
                operation_type=CryptoOperationType.deposit).first()
        opid = user_depo.crypto_operation.id

    wait_for_op_confirmations(eth_service, opid)

    with transaction.manager:

        # Sanity check our token contract posts us logs
        user = dbsession.query(User).first()
        client = get_rpc_client(web3)
        asset = dbsession.query(Asset).get(toybox)
        address = bin_to_eth_address(asset.external_id)
        network = dbsession.query(AssetNetwork).get(eth_network_id)
        user_address = UserCryptoAddress.get_default(user, network).address
        house_address = dbsession.query(CryptoAddress).get(house_address)
        house = bin_to_eth_address(house_address.address)
        token = Token.get(web3, address)

        # Check we correctly resolved low level logs
        token_logs = client.get_logs(from_block=0, address=[address])
        wallet_logs = client.get_logs(from_block=0, address=[house])
        assert len(token_logs) > 0
        assert len(wallet_logs) > 0

        # Check contract state matches
        assert token.contract.call().balanceOf(house) == 9990
        assert token.contract.call().balanceOf(
            bin_to_eth_address(user_address.address)) == 10

        # Check our internal book keeping matches
        assert house_address.get_account(asset).account.get_balance() == 9990
        assert user_address.get_account(asset).account.get_balance() == 10
Ejemplo n.º 39
0
 def __str__(self):
     if self.address:
         return bin_to_eth_address(self.address)
     else:
         return "<no address assigned yet>"
Ejemplo n.º 40
0
 def address(self) -> str:
     if self.asset.external_id:
         return bin_to_eth_address(self.asset.external_id)
     else:
         return ""
Ejemplo n.º 41
0
 def address(self) -> str:
     if self.asset.external_id:
         return bin_to_eth_address(self.asset.external_id)
     else:
         return ""
Ejemplo n.º 42
0
 def __str__(self):
     if self.address:
         return bin_to_eth_address(self.address)
     else:
         return "<no address assigned yet>"