예제 #1
0
def test_event_withdraw_wallet(web3, topped_up_hosted_wallet, coinbase):
    """Withdraw funds from the wallet and see that we get the event of the deposit."""

    hosted_wallet = topped_up_hosted_wallet
    coinbase_address = coinbase

    listener, events = create_contract_listener(hosted_wallet.contract)

    # Do a withdraw from wallet
    txid = hosted_wallet.withdraw(coinbase_address, TEST_VALUE)
    confirm_transaction(web3, txid)

    # Wallet contract should generate events if the withdraw succeeded or not
    update_count = listener.poll()

    assert update_count == 1
    assert len(events) == 1
    event_name, input_data = events[0]
    assert event_name == "Withdraw"
    assert input_data["value"] == to_wei(TEST_VALUE)
    assert input_data["to"] == coinbase_address

    # Deposit some more, should generate one new event
    txid = hosted_wallet.withdraw(coinbase_address, TEST_VALUE)
    confirm_transaction(web3, txid)

    update_count = listener.poll()
    assert update_count == 1
    assert event_name == "Withdraw"
    assert input_data["value"] == to_wei(TEST_VALUE)
    assert input_data["to"] == coinbase_address
예제 #2
0
def test_event_fund_wallet(web3, hosted_wallet):
    """Send some funds int the wallet and see that we get the event of the deposit."""

    listener, events = create_contract_listener(hosted_wallet.contract)

    # value = get_wallet_balance(testnet_wallet_contract_address)
    txid = send_balance_to_contract(hosted_wallet, TEST_VALUE)
    confirm_transaction(web3, txid)

    update_count = listener.poll()

    assert update_count == 1
    assert len(events) == 1
    event_name, input_data = events[0]
    assert event_name == "Deposit"
    assert input_data["value"] == to_wei(TEST_VALUE)

    # Deposit some more, should generate one new event
    txid = send_balance_to_contract(hosted_wallet, TEST_VALUE)
    confirm_transaction(web3, txid)

    update_count = listener.poll()

    assert update_count == 1
    assert len(events) == 2
    event_name, input_data = events[1]
    assert event_name == "Deposit"
    assert input_data["value"] == to_wei(TEST_VALUE)
예제 #3
0
def test_event_claim_fees(web3, topped_up_hosted_wallet, coinbase):
    """We correctly can claim transaction fees from the hosted wallet contract."""

    hosted_wallet = topped_up_hosted_wallet
    coinbase_address = coinbase

    # Do a withdraw to cause some fees
    listener, events = create_contract_listener(hosted_wallet.contract)
    assert hosted_wallet.get_balance() > TEST_VALUE
    txid = hosted_wallet.withdraw(coinbase_address, TEST_VALUE)
    confirm_transaction(web3, txid)

    # Claim fees for the withdraw operation
    claim_txid, price = hosted_wallet.claim_fees(txid)
    confirm_transaction(web3, claim_txid)

    # We should have event for withdraw + claims
    update_count = listener.poll()
    assert update_count == 2
    assert len(events) == 2
    event_name, input_data = events[-1]  # Fee claim event

    assert event_name == "ClaimFee"
    assert input_data["txid"] == txid_to_bin(
        txid)  # This was correctly targeted to original withdraw
    assert input_data["value"] == to_wei(price)  # We claimed correct amount
예제 #4
0
    def withdraw(self, to_address: str, amount_in_eth: Decimal, from_account=None, max_gas=100000) -> str:
        """Withdraw funds from a wallet contract.

        :param amount_in_eth: How much as ETH
        :param to_address: Destination address we are withdrawing to
        :param from_account: Which Geth account pays the gas
        :return: Transaction hash as 0x string
        """

        assert isinstance(amount_in_eth, Decimal)  # Don't let floats slip through

        wei = to_wei(amount_in_eth)

        if not from_account:
            # Default to coinbase for transaction fees
            from_account = self.contract.web3.eth.coinbase

        tx_info = {
            # The Ethereum account that pays the gas for this operation
            "from": from_account,
            "gas": max_gas,
        }

        # Sanity check that we own this wallet
        owner = self.contract.call().owner()

        # TODO: parent ABI not stable
        owner = ensure_0x_prefixed_hex(owner)

        assert owner == from_account

        # Interact with underlying wrapped contract
        txid = self.contract.transact(tx_info).withdraw(to_address, wei)
        return txid
예제 #5
0
def xxx_test_registrar_based_wallet(web3: Web3, coinbase):
    """Create registrar contract and register a wallet against it."""

    wei_amount = to_wei(TEST_VALUE)

    # Check we get somewhat valid ids
    contract_def = get_compiled_contract_cached("OwnedRegistrar")
    registrar_contract, txid = deploy_contract(web3, contract_def)

    # Deploy wallet contract body
    wallet_contract_def = get_compiled_contract_cached("Wallet")
    wallet_contract, txid = deploy_contract(web3, wallet_contract_def)
    assert wallet_contract.call().version().decode("utf-8") == "1.0"

    # Register wallet contract body
    assert wallet_contract.address
    txid = registrar_contract.transact().setAddr(b"wallet",
                                                 wallet_contract.address)
    confirm_transaction(web3, txid)

    # Check registration succeeded
    assert decode_addr(
        registrar_contract.call().addr(b"wallet")) == wallet_contract.address

    # Deploy relay against the registered wallet
    contract_def = get_compiled_contract_cached("Relay")
    assert registrar_contract.address
    relay, txid = deploy_contract(
        web3,
        contract_def,
        constructor_arguments=[registrar_contract.address, "wallet"])

    # Test relayed wallet. We use Wallet ABI
    # against Relay contract.
    contract_def = get_compiled_contract_cached("Wallet")
    relayed_wallet = get_contract(web3, contract_def, relay.address)

    # Check relay internal data structures
    assert decode_addr(
        relay.call().registrarAddr()) == registrar_contract.address
    assert relay.call().name() == b"wallet"

    # We point to the wallet implementation
    impl_addr = decode_addr(relay.call().getImplAddr())
    assert impl_addr == wallet_contract.address

    # Read a public variable through relay contract
    assert relayed_wallet.call().version().decode("utf-8") == "1.0"

    # Deposit some ETH
    txid = send_balance_to_contract(relayed_wallet.address, wei_amount)
    confirm_transaction(web3, txid)
    assert relayed_wallet.web3.eth.getBalance(relayed_wallet.address,
                                              wei_amount)

    # Withdraw ETH back
    relayed_wallet.transact().withdraw(coinbase, wei_amount)
    confirm_transaction(web3, txid)
    assert relayed_wallet.web3.eth.getBalance(relayed_wallet.address, 0)
예제 #6
0
def send_balance_to_address(web3: Web3, address: str, value: Decimal) -> str:
    assert address.startswith("0x")
    tx = {
        "from": web3.eth.coinbase,
        "to": address,
        "value": to_wei(value)
    }
    return web3.eth.sendTransaction(tx)
예제 #7
0
def send_balance_to_address(web3: Web3, address: str, value: Decimal) -> str:
    assert address.startswith("0x")
    tx = {
        "from": web3.eth.coinbase,
        "to": address,
        "value": to_wei(value),
        "gas": 600000
    }
    return web3.eth.sendTransaction(tx)
예제 #8
0
    def execute(self,
                to_contract: Contract,
                func: str,
                args=None,
                amount_in_eth: Optional[Decimal] = None,
                max_gas=300000):
        """Calls a smart contract from the hosted wallet.

        Creates a transaction that is proxyed through hosted wallet execute method. We need to have ABI as Populus Contract instance.

        :param wallet_address: Wallet address
        :param contract: Contract to called as address bound Populus Contract class
        :param func: Method name to be called
        :param args: Arguments passed to the method
        :param value: Additional value carried in the call in ETH
        :param gas: The max amount of gas the coinbase account is allowed to pay for this transaction.
        :return: txid of the execution as hex string
        """

        assert isinstance(to_contract, Contract)

        if amount_in_eth:
            assert isinstance(amount_in_eth,
                              Decimal)  # Don't let floats slip through
            value = to_wei(amount_in_eth)
        else:
            value = 0

        # Encode function arguments
        # function_abi = to_contract._find_matching_fn_abi(func, args)
        # 4 byte function hash
        # function_selector = function_abi_to_4byte_selector(function_abi)

        # data payload passed to the function
        arg_data = to_contract.encodeABI(func, args=args)
        call_data = arg_data  # Latest web3 behavior, no manual function selector needed

        # test_event_execute() call data should look like
        # function selector + random int as 256-bit
        # 0x5093dc7d000000000000000000000000000000000000000000000000000000002a3f58fe

        # web3 takes bytes argument as actual bytes, not hex
        call_data = binascii.unhexlify(call_data[2:])

        tx_info = {
            # The Ethereum account that pays the gas for this operation
            "from": self.contract.web3.eth.coinbase,
            "gas": max_gas,
        }

        txid = self.contract.transact(tx_info).execute(to_contract.address,
                                                       value, max_gas,
                                                       call_data)
        return txid
예제 #9
0
def xxx_test_registrar_based_wallet(web3: Web3, coinbase):
    """Create registrar contract and register a wallet against it."""

    wei_amount = to_wei(TEST_VALUE)

    # Check we get somewhat valid ids
    contract_def = get_compiled_contract_cached("OwnedRegistrar")
    registrar_contract, txid = deploy_contract(web3, contract_def)

    # Deploy wallet contract body
    wallet_contract_def = get_compiled_contract_cached("Wallet")
    wallet_contract, txid = deploy_contract(web3, wallet_contract_def)
    assert wallet_contract.call().version().decode("utf-8") == "1.0"

    # Register wallet contract body
    assert wallet_contract.address
    txid = registrar_contract.transact().setAddr(b"wallet", wallet_contract.address)
    confirm_transaction(web3, txid)

    # Check registration succeeded
    assert decode_addr(registrar_contract.call().addr(b"wallet")) == wallet_contract.address

    # Deploy relay against the registered wallet
    contract_def = get_compiled_contract_cached("Relay")
    assert registrar_contract.address
    relay, txid = deploy_contract(web3, contract_def, constructor_arguments=[registrar_contract.address, "wallet"])

    # Test relayed wallet. We use Wallet ABI
    # against Relay contract.
    contract_def = get_compiled_contract_cached("Wallet")
    relayed_wallet = get_contract(web3, contract_def, relay.address)

    # Check relay internal data structures
    assert decode_addr(relay.call().registrarAddr()) == registrar_contract.address
    assert relay.call().name() == b"wallet"

    # We point to the wallet implementation
    impl_addr = decode_addr(relay.call().getImplAddr())
    assert impl_addr == wallet_contract.address

    # Read a public variable through relay contract
    assert relayed_wallet.call().version().decode("utf-8") == "1.0"

    # Deposit some ETH
    txid = send_balance_to_contract(relayed_wallet.address, wei_amount)
    confirm_transaction(web3, txid)
    assert relayed_wallet.web3.eth.getBalance(relayed_wallet.address, wei_amount)

    # Withdraw ETH back
    relayed_wallet.transact().withdraw(coinbase, wei_amount)
    confirm_transaction(web3, txid)
    assert relayed_wallet.web3.eth.getBalance(relayed_wallet.address, 0)
예제 #10
0
    def withdraw(self,
                 to_address: str,
                 amount_in_eth: Decimal,
                 from_account=None,
                 max_gas=0,
                 data=None) -> str:
        """Withdraw funds from a wallet contract.

        :param amount_in_eth: How much as ETH
        :param to_address: Destination address we are withdrawing to
        :param from_account: Which Geth account pays the gas
        :return: Transaction hash as 0x string
        """

        assert isinstance(amount_in_eth,
                          Decimal)  # Don't let floats slip through

        wei = to_wei(amount_in_eth)

        if not from_account:
            # Default to coinbase for transaction fees
            from_account = self.contract.web3.eth.coinbase

        tx_info = {
            # The Ethereum account that pays the gas for this operation
            "from": from_account,
        }

        if max_gas:
            tx_info["gas"] = max_gas
        else:
            max_gas = 0

        # Sanity check that we own this wallet
        owner = self.contract.call().owner()

        # TODO: parent ABI not stable
        owner = ensure_0x_prefixed_hex(owner)

        assert owner == from_account

        if data:
            txid = self.contract.transact(tx_info).execute(
                to_address, wei, max_gas, data)
        else:
            # Interact with underlying wrapped contract
            txid = self.contract.transact(tx_info).withdraw(
                to_address, wei, max_gas)

        return txid
예제 #11
0
    def execute(
        self, to_contract: Contract, func: str, args=None, amount_in_eth: Optional[Decimal] = None, max_gas=100000
    ):
        """Calls a smart contract from the hosted wallet.

        Creates a transaction that is proxyed through hosted wallet execute method. We need to have ABI as Populus Contract instance.

        :param wallet_address: Wallet address
        :param contract: Contract to called as address bound Populus Contract class
        :param func: Method name to be called
        :param args: Arguments passed to the method
        :param value: Additional value carried in the call in ETH
        :param gas: The max amount of gas the coinbase account is allowed to pay for this transaction.
        :return: txid of the execution as hex string
        """

        assert isinstance(to_contract, Contract)

        if amount_in_eth:
            assert isinstance(amount_in_eth, Decimal)  # Don't let floats slip through
            value = to_wei(amount_in_eth)
        else:
            value = 0

        # Encode function arguments
        function_abi = to_contract._find_matching_fn_abi(func, args)
        # 4 byte function hash
        function_selector = function_abi_to_4byte_selector(function_abi)

        # data payload passed to the function
        arg_data = to_contract.encodeABI(func, args=args)

        call_data = function_selector + arg_data[2:]

        # test_event_execute() call data should look like
        # function selector + random int as 256-bit
        # 0x5093dc7d000000000000000000000000000000000000000000000000000000002a3f58fe

        # web3 takes bytes argument as actual bytes, not hex
        call_data = binascii.unhexlify(call_data[2:])

        tx_info = {
            # The Ethereum account that pays the gas for this operation
            "from": self.contract.web3.eth.coinbase,
            "gas": max_gas,
        }

        txid = self.contract.transact(tx_info).execute(to_contract.address, value, max_gas, call_data)
        return txid
예제 #12
0
def send_balance_to_contract(contract: Contract, value: Decimal) -> str:
    """Send balance from geth coinbase to the contract.

    :param contract: Contract instance with an address

    :param value: How much to send

    :return: Transaction hash of the send operation
    """
    web3 = contract.web3
    tx = {
        "from": web3.eth.coinbase,
        "to": contract.address,
        "value": to_wei(value)
    }
    return web3.eth.sendTransaction(tx)
예제 #13
0
def send_balance_to_contract(contract: Contract,
                             value: Decimal,
                             gas=None) -> str:
    """Send balance from geth coinbase to the contract.

    :param contract: Contract instance with an address

    :param value: How much to send

    :return: Transaction hash of the send operation
    """
    web3 = contract.web3
    tx = {
        "from": web3.eth.coinbase,
        "to": contract.address,
        "value": to_wei(value)
    }

    if gas:
        tx["gas"] = gas

    return web3.eth.sendTransaction(tx)
예제 #14
0
def test_event_withdraw_wallet_too_much(web3: Web3, topped_up_hosted_wallet,
                                        coinbase):
    """Try to withdraw more than the wallet has."""

    hosted_wallet = topped_up_hosted_wallet
    coinbase_address = coinbase

    listener, events = create_contract_listener(hosted_wallet.contract)

    too_much = Decimal(99999999)

    txid = hosted_wallet.withdraw(coinbase_address, too_much)
    confirm_transaction(web3, txid)

    update_count = listener.poll()

    # XXX:
    assert update_count == 1
    assert len(events) == 1
    event_name, input_data = events[0]
    assert event_name == "ExceededWithdraw"
    assert input_data["value"] == to_wei(too_much)