示例#1
0
def test_balance_sheet_to_dict():
    a = BalanceSheet(
        assets={
            A_USD: Balance(amount=FVal('2'), usd_value=FVal('2')),
            A_ETH: Balance(amount=FVal('3'), usd_value=FVal('900')),
        },
        liabilities={
            A_DAI: Balance(amount=FVal('5'), usd_value=FVal('5.1')),
            A_ETH: Balance(amount=FVal('0.5'), usd_value=FVal('150')),
        },
    )
    assert a.to_dict() == {
        'assets': {
            'USD': {
                'amount': FVal('2'),
                'usd_value': FVal('2')
            },
            'ETH': {
                'amount': FVal('3'),
                'usd_value': FVal('900')
            },
        },
        'liabilities': {
            ethaddress_to_identifier('0x6B175474E89094C44Da98b954EedeAC495271d0F'):
            {
                'amount': FVal('5'),
                'usd_value': FVal('5.1')
            },  # noqa: E501
            'ETH': {
                'amount': FVal('0.5'),
                'usd_value': FVal('150')
            },
        },
    }
示例#2
0
def test_balance_sheet_addition():
    a = BalanceSheet(
        assets={
            A_USD: Balance(amount=FVal('2'), usd_value=FVal('2')),
            A_ETH: Balance(amount=FVal('1.5'), usd_value=FVal('450')),
        },
        liabilities={
            A_DAI: Balance(amount=FVal('5'), usd_value=FVal('5.1')),
            A_ETH: Balance(amount=FVal('0.5'), usd_value=FVal('150')),
        },
    )
    b = BalanceSheet(
        assets={
            A_EUR: Balance(amount=FVal('3'), usd_value=FVal('3.5')),
            A_ETH: Balance(amount=FVal('3'), usd_value=FVal('900')),
            A_BTC: Balance(amount=FVal('1'), usd_value=FVal('10000')),
        },
        liabilities={
            A_DAI: Balance(amount=FVal('10'), usd_value=FVal('10.2')),
        },
    )
    assert a != b
    c = BalanceSheet(
        assets={
            A_EUR: Balance(amount=FVal('3'), usd_value=FVal('3.5')),
            A_USD: Balance(amount=FVal('2'), usd_value=FVal('2')),
            A_ETH: Balance(amount=FVal('4.5'), usd_value=FVal('1350')),
            A_BTC: Balance(amount=FVal('1'), usd_value=FVal('10000')),
        },
        liabilities={
            A_DAI: Balance(amount=FVal('15'), usd_value=FVal('15.3')),
            A_ETH: Balance(amount=FVal('0.5'), usd_value=FVal('150')),
        },
    )
    assert a + b == c
示例#3
0
def test_balance_sheet_to_dict():
    a = BalanceSheet(
        assets={
            A_USD: Balance(amount=FVal('2'), usd_value=FVal('2')),
            A_ETH: Balance(amount=FVal('3'), usd_value=FVal('900')),
        },
        liabilities={
            A_DAI: Balance(amount=FVal('5'), usd_value=FVal('5.1')),
            A_ETH: Balance(amount=FVal('0.5'), usd_value=FVal('150')),
        },
    )
    assert a.to_dict() == {
        'assets': {
            'USD': {
                'amount': FVal('2'),
                'usd_value': FVal('2')
            },
            'ETH': {
                'amount': FVal('3'),
                'usd_value': FVal('900')
            },
        },
        'liabilities': {
            'DAI': {
                'amount': FVal('5'),
                'usd_value': FVal('5.1')
            },
            'ETH': {
                'amount': FVal('0.5'),
                'usd_value': FVal('150')
            },
        },
    }
示例#4
0
def test_balance_sheet_serialize():
    a = BalanceSheet(
        assets={
            A_USD: Balance(amount=FVal('2'), usd_value=FVal('2')),
            A_ETH: Balance(amount=FVal('3'), usd_value=FVal('900')),
        },
        liabilities={
            A_DAI: Balance(amount=FVal('5'), usd_value=FVal('5.1')),
            A_ETH: Balance(amount=FVal('0.5'), usd_value=FVal('150')),
        },
    )
    assert a.serialize() == {
        'assets': {
            'USD': {
                'amount': '2',
                'usd_value': '2'
            },
            'ETH': {
                'amount': '3',
                'usd_value': '900'
            },
        },
        'liabilities': {
            'DAI': {
                'amount': '5',
                'usd_value': '5.1'
            },
            'ETH': {
                'amount': '0.5',
                'usd_value': '150'
            },
        },
    }
示例#5
0
    def query_ethereum_balances(self, force_token_detection: bool) -> None:
        """Queries all the ethereum balances and populates the state

        May raise:
        - RemoteError if an external service such as Etherscan or cryptocompare
        is queried and there is a problem with its query.
        - EthSyncError if querying the token balances through a provided ethereum
        client and the chain is not synced
        """
        if len(self.accounts.eth) == 0:
            return

        # Query ethereum ETH balances
        eth_accounts = self.accounts.eth
        eth_usd_price = Inquirer().find_usd_price(A_ETH)
        balances = self.ethereum.get_multieth_balance(eth_accounts)
        eth_total = FVal(0)
        for account, balance in balances.items():
            eth_total += balance
            usd_value = balance * eth_usd_price
            self.balances.eth[account] = BalanceSheet(assets=defaultdict(
                Balance, {A_ETH: Balance(balance, usd_value)}), )
        self.totals.assets[A_ETH] = Balance(amount=eth_total,
                                            usd_value=eth_total *
                                            eth_usd_price)

        self.query_defi_balances()
        self.query_ethereum_tokens(force_token_detection)
        self._add_protocol_balances()
示例#6
0
文件: vaults.py 项目: rotki/rotki
 def get_balance(self) -> BalanceSheet:
     starting_assets = {self.collateral_asset: self.collateral} if self.collateral.amount != ZERO else {}  # noqa: E501
     starting_liabilities = {A_DAI: self.debt} if self.debt.amount != ZERO else {}
     return BalanceSheet(
         assets=defaultdict(Balance, starting_assets),
         liabilities=defaultdict(Balance, starting_liabilities),  # type: ignore
     )
示例#7
0
 def get_balance(self) -> BalanceSheet:
     return BalanceSheet(
         assets=defaultdict(Balance,
                            {self.collateral_asset: self.collateral}),
         liabilities=defaultdict(Balance,
                                 {EthereumToken('DAI'): self.debt}),
     )
示例#8
0
def test_get_vault_balance(
    inquirer,  # pylint: disable=unused-argument
    mocked_current_prices,
):
    debt_value = FVal('2000')
    owner = make_ethereum_address()
    vault = MakerDAOVault(
        identifier=1,
        collateral_type='ETH-A',
        collateral_asset=A_ETH,
        owner=owner,
        collateral=Balance(FVal('100'), FVal('20000')),
        debt=Balance(debt_value, debt_value * mocked_current_prices['DAI']),
        collateralization_ratio='990%',
        liquidation_ratio=FVal(1.5),
        liquidation_price=FVal('50'),  # not calculated to be correct
        urn=make_ethereum_address(),
        stability_fee=ZERO,
    )
    expected_result = BalanceSheet(
        assets=defaultdict(Balance,
                           {A_ETH: Balance(FVal('100'), FVal('20000'))}),
        liabilities=defaultdict(Balance,
                                {A_DAI: Balance(FVal('2000'), FVal('2020'))}),
    )
    assert vault.get_balance() == expected_result
示例#9
0
文件: manager.py 项目: rizoraz/rotki
    def query_kusama_balances(self, wait_available_node: bool = True) -> None:
        """Queries the KSM balances of the accounts via Kusama endpoints.

        May raise:
        - RemotError: if no nodes are available or the balances request fails.
        """
        if len(self.accounts.ksm) == 0:
            return

        ksm_usd_price = Inquirer().find_usd_price(A_KSM)
        if wait_available_node:
            wait_until_a_node_is_available(
                substrate_manager=self.kusama,
                seconds=KUSAMA_NODE_CONNECTION_TIMEOUT,
            )

        account_amount = self.kusama.get_accounts_balance(self.accounts.ksm)
        total_balance = Balance()
        for account, amount in account_amount.items():
            balance = Balance(
                amount=amount,
                usd_value=amount * ksm_usd_price,
            )
            self.balances.ksm[account] = BalanceSheet(
                assets=defaultdict(Balance, {A_KSM: balance}),
            )
            total_balance += balance
        self.totals.assets[A_KSM] = total_balance
示例#10
0
文件: manager.py 项目: rizoraz/rotki
    def modify_eth_account(
            self,
            account: ChecksumEthAddress,
            append_or_remove: str,
    ) -> None:
        """Either appends or removes an ETH acccount.

        Call with 'append' to add the account
        Call with 'remove' remove the account

        May raise:
        - Input error if the given_account is not a valid ETH address
        - BadFunctionCallOutput if a token is queried from a local chain
        and the chain is not synced
        - RemoteError if there is a problem with a query to an external
        service such as Etherscan or cryptocompare
        """
        eth_usd_price = Inquirer().find_usd_price(A_ETH)
        remove_with_populated_balance = (
            append_or_remove == 'remove' and len(self.balances.eth) != 0
        )
        # Query the balance of the account except for the case when it's removed
        # and there is no other account in the balances
        if append_or_remove == 'append' or remove_with_populated_balance:
            amount = self.ethereum.get_eth_balance(account)
            usd_value = amount * eth_usd_price

        if append_or_remove == 'append':
            self.accounts.eth.append(account)
            self.balances.eth[account] = BalanceSheet(
                assets=defaultdict(Balance, {A_ETH: Balance(amount, usd_value)}),
            )
            # Check if the new account has any staked eth2 deposits
            self.account_for_staked_eth2_balances([account], at_addition=True)
        elif append_or_remove == 'remove':
            if account not in self.accounts.eth:
                raise InputError('Tried to remove a non existing ETH account')
            self.accounts.eth.remove(account)
            balances = self.balances.eth.get(account, None)
            if balances is not None:
                for asset, balance in balances.assets.items():
                    self.totals.assets[asset] -= balance
                    if self.totals.assets[asset].amount <= ZERO:
                        self.totals.assets[asset] = Balance()
            self.balances.eth.pop(account, None)
        else:
            raise AssertionError('Programmer error: Should be append or remove')

        if len(self.balances.eth) == 0:
            # If the last account was removed balance should be 0
            self.totals.assets[A_ETH] = Balance()
        elif append_or_remove == 'append':
            self.totals.assets[A_ETH] += Balance(amount, usd_value)
            self._query_ethereum_tokens(
                action=AccountAction.APPEND,
                given_accounts=[account],
            )
示例#11
0
def test_default_balance_sheet():
    a = BalanceSheet(
        assets={
            A_USD: Balance(amount=FVal('2'), usd_value=FVal('2')),
            A_ETH: Balance(amount=FVal('1.5'), usd_value=FVal('450')),
        },
        liabilities={
            A_DAI: Balance(amount=FVal('5'), usd_value=FVal('5.1')),
            A_ETH: Balance(amount=FVal('0.5'), usd_value=FVal('150')),
        },
    )
    b = BalanceSheet()  # test no args init gives empty default dicts properly
    assert isinstance(b.assets, dict)
    assert len(b.assets) == 0
    assert isinstance(b.liabilities, dict)
    assert len(b.liabilities) == 0
    assert a != b
    b += a
    assert a == b
示例#12
0
def test_balance_sheet_raddition():
    a = BalanceSheet(
        assets={
            A_USD: Balance(amount=FVal('2'), usd_value=FVal('2')),
            A_ETH: Balance(amount=FVal('1.5'), usd_value=FVal('450')),
        },
        liabilities={
            A_DAI: Balance(amount=FVal('5'), usd_value=FVal('5.1')),
            A_ETH: Balance(amount=FVal('0.5'), usd_value=FVal('150')),
        },
    )
    b = BalanceSheet(
        assets={
            A_EUR: Balance(amount=FVal('3'), usd_value=FVal('3.5')),
            A_ETH: Balance(amount=FVal('3'), usd_value=FVal('900')),
            A_BTC: Balance(amount=FVal('1'), usd_value=FVal('10000')),
        },
        liabilities={
            A_DAI: Balance(amount=FVal('10'), usd_value=FVal('10.2')),
        },
    )
    c = BalanceSheet(
        assets={
            A_EUR: Balance(amount=FVal('3'), usd_value=FVal('3.5')),
            A_USD: Balance(amount=FVal('2'), usd_value=FVal('2')),
            A_ETH: Balance(amount=FVal('4.5'), usd_value=FVal('1350')),
            A_BTC: Balance(amount=FVal('1'), usd_value=FVal('10000')),
        },
        liabilities={
            A_DAI: Balance(amount=FVal('15'), usd_value=FVal('15.3')),
            A_ETH: Balance(amount=FVal('0.5'), usd_value=FVal('150')),
        },
    )

    result = sum([a, b])

    assert isinstance(result, BalanceSheet)
    assert result == c
示例#13
0
文件: manager.py 项目: rizoraz/rotki
    def modify_kusama_account(
            self,
            account: KusamaAddress,
            append_or_remove: Literal['append', 'remove'],
    ) -> None:
        """Either appends or removes a kusama acccount.

        Call with 'append' to add the account
        Call with 'remove' remove the account

        May raise:
        - Input error if the given_account is not a valid kusama address
        - RemoteError if there is a problem with a query to an external
        service such as Kusama nodes or cryptocompare
        """
        if append_or_remove not in ('append', 'remove'):
            raise AssertionError(f'Unexpected action: {append_or_remove}')
        if append_or_remove == 'remove' and account not in self.accounts.ksm:
            raise InputError('Tried to remove a non existing KSM account')

        ksm_usd_price = Inquirer().find_usd_price(A_KSM)
        if append_or_remove == 'append':
            # Wait until a node is connected when adding a KSM address for the
            # first time.
            if len(self.kusama.available_nodes_call_order) == 0:
                self.kusama.attempt_connections()
                wait_until_a_node_is_available(
                    substrate_manager=self.kusama,
                    seconds=KUSAMA_NODE_CONNECTION_TIMEOUT,
                )
            amount = self.kusama.get_account_balance(account)
            balance = Balance(amount=amount, usd_value=amount * ksm_usd_price)
            self.accounts.ksm.append(account)
            self.balances.ksm[account] = BalanceSheet(
                assets=defaultdict(Balance, {A_KSM: balance}),
            )
            self.totals.assets[A_KSM] += balance
        if append_or_remove == 'remove':
            if len(self.balances.ksm) > 1:
                if account in self.balances.ksm:
                    self.totals.assets[A_KSM] -= self.balances.ksm[account].assets[A_KSM]
            else:  # If the last account was removed balance should be 0
                self.totals.assets[A_KSM] = Balance()
            self.balances.ksm.pop(account, None)
            self.accounts.ksm.remove(account)
示例#14
0
    def __init__(
        self,
        blockchain_accounts: BlockchainAccounts,
        ethereum_manager: 'EthereumManager',
        msg_aggregator: MessagesAggregator,
        database: DBHandler,
        greenlet_manager: GreenletManager,
        premium: Optional[Premium],
        data_directory: Path,
        eth_modules: Optional[List[str]] = None,
    ):
        log.debug('Initializing ChainManager')
        super().__init__()
        self.ethereum = ethereum_manager
        self.database = database
        self.msg_aggregator = msg_aggregator
        self.accounts = blockchain_accounts
        self.data_directory = data_directory

        self.defi_balances_last_query_ts = Timestamp(0)
        self.defi_balances: Dict[ChecksumEthAddress,
                                 List[DefiProtocolBalances]] = {}
        self.defi_lock = Semaphore()
        self.eth2_lock = Semaphore()

        # Per account balances
        self.balances = BlockchainBalances(db=database)
        # Per asset total balances
        self.totals: BalanceSheet = BalanceSheet()
        # TODO: Perhaps turn this mapping into a typed dict?
        self.eth_modules: Dict[str, Union[EthereumModule,
                                          Literal['loading']]] = {}
        if eth_modules:
            for given_module in eth_modules:
                if given_module == 'makerdao_dsr':
                    self.eth_modules['makerdao_dsr'] = MakerDAODSR(
                        ethereum_manager=ethereum_manager,
                        database=self.database,
                        premium=premium,
                        msg_aggregator=msg_aggregator,
                    )
                elif given_module == 'makerdao_vaults':
                    self.eth_modules['makerdao_vaults'] = MakerDAOVaults(
                        ethereum_manager=ethereum_manager,
                        database=self.database,
                        premium=premium,
                        msg_aggregator=msg_aggregator,
                    )
                elif given_module == 'aave':
                    self.eth_modules['aave'] = Aave(
                        ethereum_manager=ethereum_manager,
                        database=self.database,
                        premium=premium,
                        msg_aggregator=msg_aggregator,
                    )
                elif given_module == 'compound':
                    self.eth_modules['compound'] = 'loading'
                    # Since Compound initialization needs a few network calls we do it async
                    greenlet_manager.spawn_and_track(
                        after_seconds=None,
                        task_name='Initialize Compound object',
                        method=self._initialize_compound,
                        premium=premium,
                    )
                elif given_module == 'uniswap':
                    self.eth_modules['uniswap'] = Uniswap(
                        ethereum_manager=ethereum_manager,
                        database=self.database,
                        premium=premium,
                        msg_aggregator=msg_aggregator,
                        data_directory=self.data_directory,
                    )
                elif given_module == 'yearn_vaults':
                    self.eth_modules['yearn_vaults'] = YearnVaults(
                        ethereum_manager=ethereum_manager,
                        database=self.database,
                        premium=premium,
                        msg_aggregator=msg_aggregator,
                    )
                else:
                    log.error(
                        f'Unrecognized module value {given_module} given. Skipping...'
                    )

        self.premium = premium
        self.greenlet_manager = greenlet_manager
        self.zerion = Zerion(ethereum_manager=self.ethereum,
                             msg_aggregator=self.msg_aggregator)

        for name, module in self.iterate_modules():
            self.greenlet_manager.spawn_and_track(
                after_seconds=None,
                task_name=f'startup of {name}',
                method=module.on_startup,
            )
示例#15
0
 def get_balance(self) -> BalanceSheet:
     return BalanceSheet(
         assets=defaultdict(Balance,
                            {self.collateral_asset: self.collateral}),
         liabilities=defaultdict(Balance, {A_DAI: self.debt}),
     )