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
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 #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 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()
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 #5
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()
Example #6
0
def withdraw_token(web3: Web3, dbsession: Session, opid: UUID):
    """Perform token 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
        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

    @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()

    from_address, to_address, asset_address, amount = prepare_withdraw()
    wallet = HostedWallet.get(web3, from_address)
    token = Token.get(web3, asset_address)
    amount = token.validate_transfer_amount(amount)
    # Perform actual transfer outside retryable transaction
    # boundaries to avoid double withdrwa
    txid = wallet.execute(token.contract, "transfer", [to_address, amount])
    close_withdraw()
Example #7
0
def withdraw_token(web3: Web3, dbsession: Session, opid: UUID):
    """Perform token 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
        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

    @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()

    from_address, to_address, asset_address, amount = prepare_withdraw()
    wallet = HostedWallet.get(web3, from_address)
    token = Token.get(web3, asset_address)
    amount = token.validate_transfer_amount(amount)
    # Perform actual transfer outside retryable transaction
    # boundaries to avoid double withdrwa
    txid = wallet.execute(token.contract, "transfer", [to_address, amount])
    close_withdraw()
Example #8
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