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 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
示例#3
0
    def claim_fees(self, original_txid: str) -> Tuple[str, Decimal]:
        """Claim fees from previous execute() call.

        When a hosted wallet calls another contract through execute() gas is spent. This gas appears as cumulative gas in the transaction receipt. This gas cost should be targeted to the hosted wallet balance, not the original caller balance (geth coinbase).

        We use this method to settle the fee transaction between the hosted wallet and coinbase. This creates another event that is easy to pick up accounting and properly credit.

        :return: The new transaction id that settles the fees.
        """

        assert original_txid.startswith("0x")

        original_txid_b = bytes(bytearray.fromhex(original_txid[2:]))

        receipt = self.web3.eth.getTransactionReceipt(original_txid)
        gas_price = self.web3.eth.gasPrice

        gas_used = receipt["cumulativeGasUsed"]
        wei_value = gas_used * gas_price

        price = wei_to_eth(wei_value)

        # TODO: Estimate the gas usage of this transaction for claiming the fees
        # and add it on the top of the original transaction gas

        # Transfer value back to owner, post a tx fee event
        txid = self.contract.transact().claimFees(original_txid_b, wei_value)
        return txid, price
示例#4
0
    def claim_fees(self, original_txid: str) -> Tuple[str, Decimal]:
        """Claim fees from previous execute() call.

        When a hosted wallet calls another contract through execute() gas is spent. This gas appears as cumulative gas in the transaction receipt. This gas cost should be targeted to the hosted wallet balance, not the original caller balance (geth coinbase).

        We use this method to settle the fee transaction between the hosted wallet and coinbase. This creates another event that is easy to pick up accounting and properly credit.

        :return: The new transaction id that settles the fees.
        """

        assert original_txid.startswith("0x")

        original_txid_b = bytes(bytearray.fromhex(original_txid[2:]))

        receipt = self.web3.eth.getTransactionReceipt(original_txid)
        gas_price = self.web3.eth.gasPrice

        gas_used = receipt["cumulativeGasUsed"]
        wei_value = gas_used * gas_price

        price = wei_to_eth(wei_value)

        # TODO: Estimate the gas usage of this transaction for claiming the fees
        # and add it on the top of the original transaction gas

        # Transfer value back to owner, post a tx fee event
        txid = self.contract.transact().claimFees(original_txid_b, wei_value)
        return txid, price
def test_cap(web3, hosted_wallet, token, coinbase):
    """Transactions that would exceed cap is rejected."""

    test_fund = wei_to_eth(CAP + 1)

    txid = send_balance_to_contract(token.contract, test_fund, gas=1000000)
    with pytest.raises(TransactionConfirmationError):
        confirm_transaction(web3, txid)
def test_withdraw_wallet(web3, topped_up_hosted_wallet, coinbase):
    """Withdraw eths from wallet contract to RPC coinbase address."""

    hosted_wallet = topped_up_hosted_wallet

    current_balance = hosted_wallet.get_balance()
    current_coinbase_balance = wei_to_eth(web3.eth.getBalance(coinbase))

    # We have enough coints to perform the test
    assert current_balance > TEST_VALUE

    # Withdraw and wait it go through
    txid = hosted_wallet.withdraw(coinbase, TEST_VALUE)
    confirm_transaction(web3, txid)

    new_balance = hosted_wallet.get_balance()
    new_coinbase_balance = wei_to_eth(web3.eth.getBalance(coinbase))

    assert new_coinbase_balance != current_coinbase_balance, "Coinbase address balance did not change: {}".format(new_coinbase_balance)

    assert new_coinbase_balance > current_coinbase_balance
    assert new_balance < current_balance
def test_fund_wallet(web3, coinbase, hosted_wallet):
    """Send some funds int the wallet and see the balance updates."""

    current_balance = wei_to_eth(web3.eth.getBalance(hosted_wallet.address))

    # value = get_wallet_balance(wallet_contract_address)
    txid = send_balance_to_contract(hosted_wallet, TEST_VALUE)

    wait_tx(web3, txid)

    new_balance = hosted_wallet.get_balance()

    assert new_balance == current_balance + TEST_VALUE
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 get_balance(self) -> Decimal:
     """Gets the balance on this contract address over RPC and converts to ETH."""
     return wei_to_eth(self.web3.eth.getBalance(self.address))
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
示例#12
0
 def get_balance(self) -> Decimal:
     """Gets the balance on this contract address over RPC and converts to ETH."""
     return wei_to_eth(self.web3.eth.getBalance(self.address))