Пример #1
0
    def get_staking_history(
        self,
        addresses: List[ChecksumEthAddress],
        from_timestamp: Timestamp,
        to_timestamp: Timestamp,
    ) -> Dict[ChecksumEthAddress, List[LiquityEvent]]:
        try:
            staked = self._get_raw_history(addresses, 'stake')
        except RemoteError as e:
            log.error(
                f'Failed to query stake graph events for liquity. {str(e)}')
            staked = {}

        result: Dict[ChecksumEthAddress,
                     List[LiquityEvent]] = defaultdict(list)
        for stake in staked.get('lqtyStakes', []):
            owner = to_checksum_address(stake['id'])
            for change in stake['changes']:
                try:
                    timestamp = change['transaction']['timestamp']
                    if timestamp < from_timestamp:
                        continue
                    if timestamp > to_timestamp:
                        break
                    operation_stake = LiquityStakeEventType.deserialize(
                        change['stakeOperation'])
                    lqty_price = PriceHistorian().query_historical_price(
                        from_asset=A_LQTY,
                        to_asset=A_USD,
                        timestamp=timestamp,
                    )
                    lusd_price = PriceHistorian().query_historical_price(
                        from_asset=A_LUSD,
                        to_asset=A_USD,
                        timestamp=timestamp,
                    )
                    stake_after = deserialize_optional_to_fval(
                        value=change['stakedAmountAfter'],
                        name='stakedAmountAfter',
                        location='liquity',
                    )
                    stake_change = deserialize_optional_to_fval(
                        value=change['stakedAmountChange'],
                        name='stakedAmountChange',
                        location='liquity',
                    )
                    issuance_gain = deserialize_optional_to_fval(
                        value=change['issuanceGain'],
                        name='issuanceGain',
                        location='liquity',
                    )
                    redemption_gain = deserialize_optional_to_fval(
                        value=change['redemptionGain'],
                        name='redemptionGain',
                        location='liquity',
                    )
                    stake_event = LiquityStakeEvent(
                        kind='stake',
                        tx=change['transaction']['id'],
                        address=owner,
                        timestamp=timestamp,
                        stake_after=AssetBalance(
                            asset=A_LQTY,
                            balance=Balance(
                                amount=stake_after,
                                usd_value=lqty_price * stake_after,
                            ),
                        ),
                        stake_change=AssetBalance(
                            asset=A_LQTY,
                            balance=Balance(
                                amount=stake_change,
                                usd_value=lqty_price * stake_change,
                            ),
                        ),
                        issuance_gain=AssetBalance(
                            asset=A_LUSD,
                            balance=Balance(
                                amount=issuance_gain,
                                usd_value=lusd_price * issuance_gain,
                            ),
                        ),
                        redemption_gain=AssetBalance(
                            asset=A_LUSD,
                            balance=Balance(
                                amount=redemption_gain,
                                usd_value=lusd_price * redemption_gain,
                            ),
                        ),
                        stake_operation=operation_stake,
                        sequence_number=str(
                            change['transaction']['sequenceNumber']),
                    )
                    result[owner].append(stake_event)
                except (DeserializationError, KeyError) as e:
                    msg = str(e)
                    log.debug(f'Failed to deserialize Liquity entry: {change}')
                    if isinstance(e, KeyError):
                        msg = f'Missing key entry for {msg}.'
                    self.msg_aggregator.add_warning(
                        f'Ignoring Liquity Stake event in Liquity. '
                        f'Failed to decode remote information. {msg}.', )
                    continue
        return result
Пример #2
0
def test_curve_deposit_eth(database, ethereum_manager, eth_transactions):
    """Data for deposit taken from
    https://etherscan.io/tx/0x51c052c8fb60f092f98ffc3cab6340c7c5348ee3b339582feba1c17cbd97ea56
    This tests uses the steth/eth pool to verify that deposits including transfer of ETH work
    properly
    """
    msg_aggregator = MessagesAggregator()
    tx_hex = '0x51c052c8fb60f092f98ffc3cab6340c7c5348ee3b339582feba1c17cbd97ea56'
    location_label = '0x767B35b9F06F6e28e5ed05eE7C27bDf992eba5d2'
    evmhash = deserialize_evm_tx_hash(tx_hex)
    transaction = EthereumTransaction(
        tx_hash=evmhash,
        timestamp=1650276061,
        block_number=14608535,
        from_address=location_label,
        to_address='0xDC24316b9AE028F1497c275EB9192a3Ea0f67022',
        value=FVal(0.2) * EXP18,
        gas=171249,
        gas_price=22990000000,
        gas_used=171249,
        input_data=hexstring_to_bytes('0x0b4c7e4d00000000000000000000000000000000000000000000000002c68af0bb14000000000000000000000000000000000000000000000000000002c6526ca273a800000000000000000000000000000000000000000000000000054aaa619fda0c01'),  # noqa: E501
        nonce=5,
    )
    receipt = EthereumTxReceipt(
        tx_hash=evmhash,
        contract_address=None,
        status=True,
        type=0,
        logs=[
            EthereumTxReceiptLog(
                log_index=412,
                data=hexstring_to_bytes('0x00000000000000000000000000000000000000000000000002c6526ca273a800'),  # noqa: E501
                address=string_to_ethereum_address('0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84'),
                removed=False,
                topics=[
                    hexstring_to_bytes('0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'),  # noqa: E501
                    hexstring_to_bytes('0x000000000000000000000000767b35b9f06f6e28e5ed05ee7c27bdf992eba5d2'),  # noqa: E501
                    hexstring_to_bytes('0x000000000000000000000000dc24316b9ae028f1497c275eb9192a3ea0f67022'),  # noqa: E501
                ],
            ), EthereumTxReceiptLog(
                log_index=413,
                data=hexstring_to_bytes('0xfffffffffffffffffffffffffffffffffffffffffffffffffd39ad935d8c57ff'),  # noqa: E501
                address=string_to_ethereum_address('0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84'),
                removed=False,
                topics=[
                    hexstring_to_bytes('0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925'),  # noqa: E501
                    hexstring_to_bytes('0x000000000000000000000000767b35b9f06f6e28e5ed05ee7c27bdf992eba5d2'),  # noqa: E501
                    hexstring_to_bytes('0x000000000000000000000000dc24316b9ae028f1497c275eb9192a3ea0f67022'),  # noqa: E501
                ],
            ), EthereumTxReceiptLog(
                log_index=414,
                data=hexstring_to_bytes('0x00000000000000000000000000000000000000000000000005589f42020a37df'),  # noqa: E501
                address=string_to_ethereum_address('0x06325440D014e39736583c165C2963BA99fAf14E'),
                removed=False,
                topics=[
                    hexstring_to_bytes('0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'),  # noqa: E501
                    hexstring_to_bytes('0x0000000000000000000000000000000000000000000000000000000000000000'),  # noqa: E501
                    hexstring_to_bytes('0x000000000000000000000000767b35b9f06f6e28e5ed05ee7c27bdf992eba5d2'),  # noqa: E501
                ],
            ), EthereumTxReceiptLog(
                log_index=415,
                data=hexstring_to_bytes('0x00000000000000000000000000000000000000000000000002c68af0bb14000000000000000000000000000000000000000000000000000002c6526ca273a80000000000000000000000000000000000000000000000000000000016a92ed4ce00000000000000000000000000000000000000000000000000000016a9b386830000000000000000000000000000000000000000000156e4db21d9cf6a6d4f3f000000000000000000000000000000000000000000014a4959a6fb2bf53a7108'),  # noqa: E501
                address=string_to_ethereum_address('0xDC24316b9AE028F1497c275EB9192a3Ea0f67022'),
                removed=False,
                topics=[
                    hexstring_to_bytes('0x26f55a85081d24974e85c6c00045d0f0453991e95873f52bff0d21af4079a768'),  # noqa: E501
                    hexstring_to_bytes('0x000000000000000000000000767b35b9f06f6e28e5ed05ee7c27bdf992eba5d2'),  # noqa: E501
                ],
            ),
        ],
    )

    dbethtx = DBEthTx(database)
    dbethtx.add_ethereum_transactions([transaction], relevant_address=None)
    decoder = EVMTransactionDecoder(
        database=database,
        ethereum_manager=ethereum_manager,
        eth_transactions=eth_transactions,
        msg_aggregator=msg_aggregator,
    )
    events = decoder.decode_transaction(transaction=transaction, tx_receipt=receipt)
    expected_events = [
        HistoryBaseEntry(
            event_identifier=tx_hex,
            sequence_index=0,
            timestamp=1650276061000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.SPEND,
            event_subtype=HistoryEventSubType.FEE,
            asset=A_ETH,
            balance=Balance(
                amount=FVal(0.00393701451),
                usd_value=ZERO,
            ),
            location_label=location_label,
            notes='Burned 0.00393701451 ETH in gas from 0x767B35b9F06F6e28e5ed05eE7C27bDf992eba5d2',  # noqa: E501
            counterparty=CPT_GAS,
        ), HistoryBaseEntry(
            event_identifier=tx_hex,
            sequence_index=1,
            timestamp=1650276061000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.DEPOSIT,
            event_subtype=HistoryEventSubType.DEPOSIT_ASSET,
            asset=A_ETH,
            balance=Balance(amount=FVal('0.2'), usd_value=ZERO),
            location_label=location_label,
            notes='Deposit 0.2 ETH in curve pool',
            counterparty=CPT_CURVE,
        ), HistoryBaseEntry(
            event_identifier=tx_hex,
            sequence_index=414,
            timestamp=1650276061000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.DEPOSIT,
            event_subtype=HistoryEventSubType.DEPOSIT_ASSET,
            asset=EthereumToken('0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84'),
            balance=Balance(amount=FVal('0.19993786'), usd_value=ZERO),
            location_label=location_label,
            notes='Deposit 0.19993786 stETH in curve pool 0xDC24316b9AE028F1497c275EB9192a3Ea0f67022',  # noqa: E501
            counterparty=CPT_CURVE,
        ), HistoryBaseEntry(
            event_identifier=tx_hex,
            sequence_index=415,
            timestamp=1650276061000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.INFORMATIONAL,
            event_subtype=HistoryEventSubType.APPROVE,
            asset=EthereumToken('0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84'),
            balance=Balance(amount=FVal('1.157920892373161954235709850E+59'), usd_value=ZERO),
            location_label=location_label,
            notes='Approve 1.157920892373161954235709850E+59 stETH of 0x767B35b9F06F6e28e5ed05eE7C27bDf992eba5d2 for spending by 0xDC24316b9AE028F1497c275EB9192a3Ea0f67022',  # noqa: E501
            counterparty='0xDC24316b9AE028F1497c275EB9192a3Ea0f67022',
        ), HistoryBaseEntry(
            event_identifier=tx_hex,
            sequence_index=416,
            timestamp=1650276061000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.RECEIVE,
            event_subtype=HistoryEventSubType.RECEIVE_WRAPPED,
            asset=EthereumToken('0x06325440D014e39736583c165C2963BA99fAf14E'),
            balance=Balance(amount=FVal('0.385232873991059423'), usd_value=ZERO),
            location_label=location_label,
            notes='Receive 0.385232873991059423 steCRV after depositing in curve pool 0xDC24316b9AE028F1497c275EB9192a3Ea0f67022',  # noqa: E501
            counterparty=CPT_CURVE,
        )]
    assert len(events) == 5
    assert events == expected_events
Пример #3
0
def test_curve_remove_liquidity(database, ethereum_manager, eth_transactions):
    """Data for deposit taken from
    https://etherscan.io/tx/0xd63dccdbebeede3a1f50b97c0a8592255203a0559880b80377daa39f915741b0
    This tests uses the link pool to verify that withdrawals are correctly decoded
    """
    msg_aggregator = MessagesAggregator()
    tx_hex = '0xd63dccdbebeede3a1f50b97c0a8592255203a0559880b80377daa39f915741b0'
    location_label = '0xDf9f0AE722A3919fE7f9cC8805773ef142007Ca6'
    evmhash = deserialize_evm_tx_hash(tx_hex)
    transaction = EthereumTransaction(
        tx_hash=evmhash,
        timestamp=1650276061,
        block_number=14608535,
        from_address=location_label,
        to_address='0xF178C0b5Bb7e7aBF4e12A4838C7b7c5bA2C623c0',
        value=0,
        gas=171249,
        gas_price=22990000000,
        gas_used=171249,
        input_data=hexstring_to_bytes('0x1a4d01d20000000000000000000000000000000000000000000000a8815561fefbe56aa300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a8d15fc942541fea7f'),  # noqa: E501
        nonce=5,
    )
    receipt = EthereumTxReceipt(
        tx_hash=evmhash,
        contract_address=None,
        status=True,
        type=0,
        logs=[
            EthereumTxReceiptLog(
                log_index=506,
                data=hexstring_to_bytes('0x0000000000000000000000000000000000000000000000a8815561fefbe56aa3'),  # noqa: E501
                address=string_to_ethereum_address('0xcee60cFa923170e4f8204AE08B4fA6A3F5656F3a'),
                removed=False,
                topics=[
                    hexstring_to_bytes('0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'),  # noqa: E501
                    hexstring_to_bytes('0x000000000000000000000000df9f0ae722a3919fe7f9cc8805773ef142007ca6'),  # noqa: E501
                    hexstring_to_bytes('0x0000000000000000000000000000000000000000000000000000000000000000'),  # noqa: E501
                ],
            ), EthereumTxReceiptLog(
                log_index=507,
                data=hexstring_to_bytes('0000000000000000000000000000000000000000000000a93078ae269dbeca10'),  # noqa: E501
                address=string_to_ethereum_address('0x514910771AF9Ca656af840dff83E8264EcF986CA'),
                removed=False,
                topics=[
                    hexstring_to_bytes('0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'),  # noqa: E501
                    hexstring_to_bytes('0x000000000000000000000000f178c0b5bb7e7abf4e12a4838c7b7c5ba2c623c0'),  # noqa: E501
                    hexstring_to_bytes('0x000000000000000000000000df9f0ae722a3919fe7f9cc8805773ef142007ca6'),  # noqa: E501
                ],
            ), EthereumTxReceiptLog(
                log_index=508,
                data=hexstring_to_bytes('0x0000000000000000000000000000000000000000000000a8815561fefbe56aa30000000000000000000000000000000000000000000000a93078ae269dbeca100000000000000000000000000000000000000000000092c009040e68c381c519'),  # noqa: E501
                address=string_to_ethereum_address('0xF178C0b5Bb7e7aBF4e12A4838C7b7c5bA2C623c0'),
                removed=False,
                topics=[
                    hexstring_to_bytes('0x5ad056f2e28a8cec232015406b843668c1e36cda598127ec3b8c59b8c72773a0'),  # noqa: E501
                    hexstring_to_bytes('0x000000000000000000000000df9f0ae722a3919fe7f9cc8805773ef142007ca6'),  # noqa: E501
                ],
            ), EthereumTxReceiptLog(
                log_index=415,
                data=hexstring_to_bytes('0x00000000000000000000000000000000000000000000000002c68af0bb14000000000000000000000000000000000000000000000000000002c6526ca273a80000000000000000000000000000000000000000000000000000000016a92ed4ce00000000000000000000000000000000000000000000000000000016a9b386830000000000000000000000000000000000000000000156e4db21d9cf6a6d4f3f000000000000000000000000000000000000000000014a4959a6fb2bf53a7108'),  # noqa: E501
                address=string_to_ethereum_address('0xDC24316b9AE028F1497c275EB9192a3Ea0f67022'),
                removed=False,
                topics=[
                    hexstring_to_bytes('0x26f55a85081d24974e85c6c00045d0f0453991e95873f52bff0d21af4079a768'),  # noqa: E501
                    hexstring_to_bytes('0x000000000000000000000000767b35b9f06f6e28e5ed05ee7c27bdf992eba5d2'),  # noqa: E501
                ],
            ),
        ],
    )

    dbethtx = DBEthTx(database)
    dbethtx.add_ethereum_transactions([transaction], relevant_address=None)
    decoder = EVMTransactionDecoder(
        database=database,
        ethereum_manager=ethereum_manager,
        eth_transactions=eth_transactions,
        msg_aggregator=msg_aggregator,
    )
    events = decoder.decode_transaction(transaction=transaction, tx_receipt=receipt)
    expected_events = [
        HistoryBaseEntry(
            event_identifier=tx_hex,
            sequence_index=0,
            timestamp=1650276061000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.SPEND,
            event_subtype=HistoryEventSubType.FEE,
            asset=A_ETH,
            balance=Balance(
                amount=FVal(0.00393701451),
                usd_value=ZERO,
            ),
            location_label=location_label,
            notes='Burned 0.00393701451 ETH in gas from 0xDf9f0AE722A3919fE7f9cC8805773ef142007Ca6',  # noqa: E501
            counterparty=CPT_GAS,
        ), HistoryBaseEntry(
            event_identifier=tx_hex,
            sequence_index=507,
            timestamp=1650276061000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.SPEND,
            event_subtype=HistoryEventSubType.RETURN_WRAPPED,
            asset=EthereumToken('0xcee60cFa923170e4f8204AE08B4fA6A3F5656F3a'),
            balance=Balance(amount=FVal('3108.372467134893484707'), usd_value=ZERO),
            location_label=location_label,
            notes='Return 3108.372467134893484707 linkCRV',
            counterparty=CPT_CURVE,
        ), HistoryBaseEntry(
            event_identifier=tx_hex,
            sequence_index=508,
            timestamp=1650276061000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.WITHDRAWAL,
            event_subtype=HistoryEventSubType.REMOVE_ASSET,
            asset=A_LINK,
            balance=Balance(amount=FVal('3120.992481448818559504'), usd_value=ZERO),
            location_label=location_label,
            notes='Remove 3120.992481448818559504 LINK from the curve pool 0xF178C0b5Bb7e7aBF4e12A4838C7b7c5bA2C623c0',  # noqa: E501
            counterparty=CPT_CURVE,
        )]
    assert expected_events == events
Пример #4
0
    '0xfeF0E7635281eF8E3B705e9C5B86e1d3B0eAb397')
ADDR2 = string_to_ethereum_address(
    '0x00F8a0D8EE1c21151BCcB416bCa1C152f9952D19')
ADDR3 = string_to_ethereum_address(
    '0x3266F3546a1e5Dc6A15588f3324741A0E20a3B6c')

# List of ADDR1, ADDR2 and ADDR3 deposit events from 1604506685 to 1605044577
# sorted by (timestamp, log_index).
EXPECTED_DEPOSITS = [
    Eth2Deposit(
        from_address=ADDR1,
        pubkey=
        '0xb016e31f633a21fbe42a015152399361184f1e2c0803d89823c224994af74a561c4ad8cfc94b18781d589d03e952cd5b',  # noqa: E501
        withdrawal_credentials=
        '0x004c7691c2085648f394ffaef851f3b1d51b95f7263114bc923fc5338f5fc499',  # noqa: E501
        value=Balance(FVal(32), FVal(64)),
        tx_hash=deserialize_evm_tx_hash(
            '0xd9eca1c2a0c5ff2f25071713432b21cc4d0ff2e8963edc63a48478e395e08db1'
        ),  # noqa: E501
        tx_index=15,
        timestamp=Timestamp(int(1604506685)),
    ),
    Eth2Deposit(
        from_address=ADDR3,
        pubkey=
        '0x90b2f65cb43d9cdb2279af9f76010d667b9d8d72e908f2515497a7102820ce6bb15302fe2b8dc082fce9718569344ad8',  # noqa: E501
        withdrawal_credentials=
        '0x00a257d19e1650dec1ab59fc9e1cb9a9fc2fe7265b0f27e7d79ff61aeff0a1f0',  # noqa: E501
        value=Balance(FVal(32), FVal(64)),
        tx_hash=deserialize_evm_tx_hash(
            '0x3403bd94a1bf185ee18a525499e408a1b9b7d801cff6418e31efda346762e754'
Пример #5
0
def test_deserialize_accounts_balances(mock_kucoin, inquirer):  # pylint: disable=unused-argument
    accounts_data = [
        {
            'id': '601ac6f7d48f8000063ab2da',
            'currency': 'UNEXISTINGSYMBOL',
            'type': 'main',
            'balance': '999',
            'available': '999',
            'holds': '0',
        },
        {
            'id': '601ac6f7d48f8000063ab2db',
            'currency': 'BCHSV',
            'type': 'main',
            'balance': '1',
            'available': '1',
            'holds': '0',
        },
        {
            'id': '601ac6f7d48f8000063ab2de',
            'currency': 'BTC',
            'type': 'main',
            'balance': '2.52',
            'available': '2.52',
            'holds': '0',
        },
        {
            'id': '601ac6f7d48f8000063ab2e7',
            'currency': 'ETH',
            'type': 'main',
            'balance': '47.33',
            'available': '47.33',
            'holds': '0',
        },
        {
            'id': '601ac6f7d48f8000063ab2e1',
            'currency': 'USDT',
            'type': 'main',
            'balance': '34500',
            'available': '34500',
            'holds': '0',
        },
        {
            'id': '60228f81d48f8000060cec67',
            'currency': 'USDT',
            'type': 'margin',
            'balance': '10000',
            'available': '10000',
            'holds': '0',
        },
        {
            'id': '601acdb7d48f8000063c6d4a',
            'currency': 'BTC',
            'type': 'trade',
            'balance': '0.09018067',
            'available': '0.09018067',
            'holds': '0',
        },
        {
            'id': '601acdc5d48f8000063c70b3',
            'currency': 'USDT',
            'type': 'trade',
            'balance': '597.26244755',
            'available': '597.26244755',
            'holds': '0',
        },
        {
            'id': '601da9fad48f8000063960cc',
            'currency': 'KCS',
            'type': 'trade',
            'balance': '0.2',
            'available': '0.2',
            'holds': '0',
        },
        {
            'id': '601da9ddd48f80000639553f',
            'currency': 'ETH',
            'type': 'trade',
            'balance': '0.10934995',
            'available': '0.10934995',
            'holds': '0',
        },
    ]
    assets_balance = mock_kucoin._deserialize_accounts_balances(
        {'data': accounts_data})
    assert assets_balance == {
        A_BTC:
        Balance(
            amount=FVal('2.61018067'),
            usd_value=FVal('3.915271005'),
        ),
        A_ETH:
        Balance(
            amount=FVal('47.43934995'),
            usd_value=FVal('71.159024925'),
        ),
        A_KCS:
        Balance(
            amount=FVal('0.2'),
            usd_value=FVal('0.30'),
        ),
        A_USDT:
        Balance(
            amount=FVal('45097.26244755'),
            usd_value=FVal('67645.893671325'),
        ),
        A_BSV:
        Balance(
            amount=FVal('1'),
            usd_value=FVal('1.5'),
        ),
    }
Пример #6
0
    def query_balances(self) -> ExchangeQueryBalances:
        try:
            resp = self.api_query_dict('returnCompleteBalances', {"account": "all"})
        except RemoteError as e:
            msg = (
                'Poloniex API request failed. Could not reach poloniex due '
                'to {}'.format(e)
            )
            log.error(msg)
            return None, msg

        assets_balance: Dict[Asset, Balance] = {}
        for poloniex_asset, v in resp.items():
            try:
                available = deserialize_asset_amount(v['available'])
                on_orders = deserialize_asset_amount(v['onOrders'])
            except DeserializationError as e:
                self.msg_aggregator.add_error(
                    f'Could not deserialize amount from poloniex due to '
                    f'{str(e)}. Ignoring its balance query.',
                )
                continue

            if available != ZERO or on_orders != ZERO:
                try:
                    asset = asset_from_poloniex(poloniex_asset)
                except UnsupportedAsset as e:
                    self.msg_aggregator.add_warning(
                        f'Found unsupported poloniex asset {e.asset_name}. '
                        f' Ignoring its balance query.',
                    )
                    continue
                except UnknownAsset as e:
                    self.msg_aggregator.add_warning(
                        f'Found unknown poloniex asset {e.asset_name}. '
                        f' Ignoring its balance query.',
                    )
                    continue
                except DeserializationError:
                    log.error(
                        f'Unexpected poloniex asset type. Expected string '
                        f' but got {type(poloniex_asset)}',
                    )
                    self.msg_aggregator.add_error(
                        'Found poloniex asset entry with non-string type. '
                        ' Ignoring its balance query.',
                    )
                    continue

                if asset == A_LEND:  # poloniex mistakenly returns LEND balances
                    continue  # https://github.com/rotki/rotki/issues/2530

                try:
                    usd_price = Inquirer().find_usd_price(asset=asset)
                except RemoteError as e:
                    self.msg_aggregator.add_error(
                        f'Error processing poloniex balance entry due to inability to '
                        f'query USD price: {str(e)}. Skipping balance entry',
                    )
                    continue

                amount = available + on_orders
                usd_value = amount * usd_price
                assets_balance[asset] = Balance(
                    amount=amount,
                    usd_value=usd_value,
                )
                log.debug(
                    'Poloniex balance query',
                    currency=asset,
                    amount=amount,
                    usd_value=usd_value,
                )

        return assets_balance, ''
Пример #7
0
def test_pickle_withdraw(database, ethereum_manager, eth_transactions):
    """Data for withdraw taken from
    https://etherscan.io/tx/0x91bc102e1cbb0e4542a10a7a13370b5e591d8d284989bdb0ca4ece4e54e61bab
    """
    msg_aggregator = MessagesAggregator()
    tx_hex = '0x91bc102e1cbb0e4542a10a7a13370b5e591d8d284989bdb0ca4ece4e54e61bab'
    evmhash = deserialize_evm_tx_hash(tx_hex)
    transaction = EthereumTransaction(
        tx_hash=evmhash,
        timestamp=1646375440,
        block_number=14355951,
        from_address='0xC7Dc4Cd171812a441A30472219d390f4F15f6070',
        to_address='0xb4EBc2C371182DeEa04B2264B9ff5AC4F0159C69',
        value=0,
        gas=171249,
        gas_price=22990000000,
        gas_used=171249,
        input_data=hexstring_to_bytes('0x853828b6'),  # noqa: E501
        nonce=23,
    )
    receipt = EthereumTxReceipt(
        tx_hash=evmhash,
        contract_address=None,
        status=True,
        type=0,
        logs=[
            EthereumTxReceiptLog(
                log_index=105,
                data=hexstring_to_bytes('0x00000000000000000000000000000000000000000000000d4f4e1608c485628b'),  # noqa: E501
                address=string_to_ethereum_address('0xb4EBc2C371182DeEa04B2264B9ff5AC4F0159C69'),
                removed=False,
                topics=[
                    hexstring_to_bytes('0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'),  # noqa: E501
                    hexstring_to_bytes('0x000000000000000000000000c7dc4cd171812a441a30472219d390f4f15f6070'),  # noqa: E501
                    hexstring_to_bytes('0x0000000000000000000000000000000000000000000000000000000000000000'),  # noqa: E501
                ],
            ), EthereumTxReceiptLog(
                log_index=106,
                data=hexstring_to_bytes('0x000000000000000000000000000000000000000000000015da18947013228f17'),  # noqa: E501
                address=string_to_ethereum_address('0xf4d2888d29D722226FafA5d9B24F9164c092421E'),
                removed=False,
                topics=[
                    hexstring_to_bytes('0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'),  # noqa: E501
                    hexstring_to_bytes('0x000000000000000000000000b4ebc2c371182deea04b2264b9ff5ac4f0159c69'),  # noqa: E501
                    hexstring_to_bytes('0x000000000000000000000000c7dc4cd171812a441a30472219d390f4f15f6070'),  # noqa: E501
                ],
            ),
        ],
    )

    dbethtx = DBEthTx(database)
    dbethtx.add_ethereum_transactions([transaction], relevant_address=None)
    decoder = EVMTransactionDecoder(
        database=database,
        ethereum_manager=ethereum_manager,
        eth_transactions=eth_transactions,
        msg_aggregator=msg_aggregator,
    )
    events = decoder.decode_transaction(transaction=transaction, tx_receipt=receipt)
    assert len(events) == 3
    expected_events = [
        HistoryBaseEntry(
            event_identifier='0x91bc102e1cbb0e4542a10a7a13370b5e591d8d284989bdb0ca4ece4e54e61bab',
            sequence_index=0,
            timestamp=1646375440000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.SPEND,
            event_subtype=HistoryEventSubType.FEE,
            asset=A_ETH,
            balance=Balance(
                amount=FVal(0.00393701451),
                usd_value=ZERO,
            ),
            location_label='0xC7Dc4Cd171812a441A30472219d390f4F15f6070',
            notes='Burned 0.00393701451 ETH in gas from 0xC7Dc4Cd171812a441A30472219d390f4F15f6070',  # noqa: E501
            counterparty=CPT_GAS,
        ), HistoryBaseEntry(
            event_identifier='0x91bc102e1cbb0e4542a10a7a13370b5e591d8d284989bdb0ca4ece4e54e61bab',
            sequence_index=106,
            timestamp=1646375440000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.SPEND,
            event_subtype=HistoryEventSubType.RETURN_WRAPPED,
            asset=EthereumToken('0xb4EBc2C371182DeEa04B2264B9ff5AC4F0159C69'),
            balance=Balance(amount=FVal('245.522202162316534411'), usd_value=ZERO),
            location_label='0xC7Dc4Cd171812a441A30472219d390f4F15f6070',
            notes='Return 245.522202162316534411 pLOOKS to the pickle contract',
            counterparty='pickle finance',
        ), HistoryBaseEntry(
            event_identifier='0x91bc102e1cbb0e4542a10a7a13370b5e591d8d284989bdb0ca4ece4e54e61bab',
            sequence_index=107,
            timestamp=1646375440000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.WITHDRAWAL,
            event_subtype=HistoryEventSubType.REMOVE_ASSET,
            asset=EthereumToken('0xf4d2888d29D722226FafA5d9B24F9164c092421E'),
            balance=Balance(amount=FVal('403.097099656688209687'), usd_value=ZERO),
            location_label='0xC7Dc4Cd171812a441A30472219d390f4F15f6070',
            notes='Unstake 403.097099656688209687 LOOKS from the pickle contract',
            counterparty='pickle finance',
        )]
    assert events == expected_events
Пример #8
0
def test_compound_deposit_with_comp_claim(
    database,
    ethereum_manager,
    function_scope_messages_aggregator,
):
    """Data taken from:
    https://etherscan.io/tx/0xfdbfe6e9ce822bd988054945c86f2dff1fac6a12b4acb0b68c8805b5aa3b30ba
    """
    # TODO: For faster tests hard-code the transaction and the logs here so no remote query needed
    tx_hash = deserialize_evm_tx_hash(
        '0xfdbfe6e9ce822bd988054945c86f2dff1fac6a12b4acb0b68c8805b5aa3b30ba'
    )  # noqa: E501
    events = get_decoded_events_of_transaction(
        ethereum_manager=ethereum_manager,
        database=database,
        msg_aggregator=function_scope_messages_aggregator,
        tx_hash=tx_hash,
    )
    amount = FVal('14309.930911242041089052')
    wrapped_amount = FVal('687371.5068874')
    interest = FVal('0.076123031460129653')
    expected_events = [
        HistoryBaseEntry(
            event_identifier=tx_hash.hex(),  # pylint: disable=no-member
            sequence_index=0,
            timestamp=1607572696000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.SPEND,
            event_subtype=HistoryEventSubType.FEE,
            asset=A_ETH,
            balance=Balance(amount=FVal('0.00945248'), usd_value=ZERO),
            location_label=ADDY2,
            notes=f'Burned 0.00945248 ETH in gas from {ADDY2}',
            counterparty=CPT_GAS,
        ),
        HistoryBaseEntry(
            event_identifier=tx_hash.hex(),  # pylint: disable=no-member
            sequence_index=241,
            timestamp=1607572696000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.RECEIVE,
            event_subtype=HistoryEventSubType.REWARD,
            asset=A_COMP,
            balance=Balance(amount=interest),
            location_label=ADDY2,
            notes=f'Collect {interest} COMP from compound',
            counterparty=CPT_COMPOUND,
        ),
        HistoryBaseEntry(
            event_identifier=tx_hash.hex(),  # pylint: disable=no-member
            sequence_index=243,
            timestamp=1607572696000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.DEPOSIT,
            event_subtype=HistoryEventSubType.DEPOSIT_ASSET,
            asset=A_DAI,
            balance=Balance(amount=amount),
            location_label=ADDY2,
            notes=f'Deposit {amount} DAI to compound',
            counterparty=CPT_COMPOUND,
        ),
        HistoryBaseEntry(
            event_identifier=tx_hash.hex(),  # pylint: disable=no-member
            sequence_index=250,
            timestamp=1607572696000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.RECEIVE,
            event_subtype=HistoryEventSubType.RECEIVE_WRAPPED,
            asset=A_CDAI,
            balance=Balance(amount=wrapped_amount),
            location_label=ADDY2,
            notes=f'Receive {wrapped_amount} cDAI from compound',
            counterparty=CPT_COMPOUND,
        )
    ]
    assert events == expected_events
Пример #9
0
def test_1inchv2_swap_for_eth(database, ethereum_manager,
                              function_scope_messages_aggregator):
    """
    Test an 1inchv2 swap for ETH.

    Data taken from
    https://etherscan.io/tx/0x5edc23d5a05e347afc60e64a4d5831ed2551985c21dceb85d267926ca2e2c13e
    """
    # TODO: For faster tests hard-code the transaction and the logs here so no remote query needed
    tx_hash = deserialize_evm_tx_hash(
        '0x5edc23d5a05e347afc60e64a4d5831ed2551985c21dceb85d267926ca2e2c13e'
    )  # noqa: E501
    events = get_decoded_events_of_transaction(
        ethereum_manager=ethereum_manager,
        database=database,
        msg_aggregator=function_scope_messages_aggregator,
        tx_hash=tx_hash,
    )
    oneinch_v2_addy = '0x111111125434b319222CdBf8C261674aDB56F3ae'
    expected_events = [
        HistoryBaseEntry(
            event_identifier=tx_hash.hex(),  # pylint: disable=no-member
            sequence_index=0,
            timestamp=1608498702000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.SPEND,
            event_subtype=HistoryEventSubType.FEE,
            asset=A_ETH,
            balance=Balance(amount=FVal('0.002618947')),
            location_label=ADDY,
            notes=f'Burned 0.002618947 ETH in gas from {ADDY}',
            counterparty=CPT_GAS,
        ),
        HistoryBaseEntry(
            event_identifier=tx_hash.hex(),  # pylint: disable=no-member
            sequence_index=217,
            timestamp=1608498702000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.TRADE,
            event_subtype=HistoryEventSubType.SPEND,
            asset=A_PAN,
            balance=Balance(amount=FVal('2152.63')),
            location_label=ADDY,
            notes='Swap 2152.63 PAN in 1inch-v2',
            counterparty=CPT_ONEINCH_V2,
        ),
        HistoryBaseEntry(
            event_identifier=tx_hash.hex(),  # pylint: disable=no-member
            sequence_index=218,
            timestamp=1608498702000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.TRADE,
            event_subtype=HistoryEventSubType.RECEIVE,
            asset=A_ETH,
            balance=Balance(amount=FVal('0.220582251767407014')),
            location_label=ADDY,
            notes='Receive 0.220582251767407014 ETH from 1inch-v2 swap',
            counterparty=CPT_ONEINCH_V2,
        ),
        HistoryBaseEntry(
            event_identifier=tx_hash.hex(),  # pylint: disable=no-member
            sequence_index=221,
            timestamp=1608498702000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.INFORMATIONAL,
            event_subtype=HistoryEventSubType.APPROVE,
            asset=A_PAN,
            balance=Balance(amount=FVal('1.157920892373161954235709850E+59')),
            location_label=ADDY,
            notes=
            f'Approve 1.157920892373161954235709850E+59 PAN of {ADDY} for spending by {oneinch_v2_addy}',  # noqa: E501
            counterparty=oneinch_v2_addy,
        ),
    ]
    assert expected_events == events
def _add_entries(server) -> List[HistoryBaseEntry]:
    entries = [HistoryBaseEntry(
        event_identifier='0x64f1982504ab714037467fdd45d3ecf5a6356361403fc97dd325101d8c038c4e',
        sequence_index=162,
        timestamp=TimestampMS(1569924574000),
        location=Location.BLOCKCHAIN,
        event_type=HistoryEventType.INFORMATIONAL,
        asset=A_DAI,
        balance=Balance(amount=FVal('1.542'), usd_value=FVal('1.675')),
        location_label='0x2B888954421b424C5D3D9Ce9bB67c9bD47537d12',
        notes='Approve 1 DAI of 0x2B888954421b424C5D3D9Ce9bB67c9bD47537d12 for spending by 0xdf869FAD6dB91f437B59F1EdEFab319493D4C4cE',  # noqa: E501
        event_subtype=HistoryEventSubType.APPROVE,
        counterparty='0xdf869FAD6dB91f437B59F1EdEFab319493D4C4cE',
    ), HistoryBaseEntry(
        event_identifier='0x64f1982504ab714037467fdd45d3ecf5a6356361403fc97dd325101d8c038c4e',
        sequence_index=163,
        timestamp=TimestampMS(1569924574000),
        location=Location.BLOCKCHAIN,
        event_type=HistoryEventType.INFORMATIONAL,
        asset=A_USDT,
        balance=Balance(amount=FVal('1.542'), usd_value=FVal('1.675')),
        location_label='0x2B888954421b424C5D3D9Ce9bB67c9bD47537d12',
        notes='Approve 1 USDT of 0x2B888954421b424C5D3D9Ce9bB67c9bD47537d12 for spending by 0xdf869FAD6dB91f437B59F1EdEFab319493D4C4cE',  # noqa: E501
        event_subtype=HistoryEventSubType.APPROVE,
        counterparty='0xdf869FAD6dB91f437B59F1EdEFab319493D4C4cE',
    ), HistoryBaseEntry(
        event_identifier='0xf32e81dbaae8a763cad17bc96b77c7d9e8c59cc31ed4378b8109ce4b301adbbc',
        sequence_index=2,
        timestamp=TimestampMS(1619924574000),
        location=Location.BLOCKCHAIN,
        event_type=HistoryEventType.SPEND,
        asset=A_ETH,
        balance=Balance(amount=FVal('0.0001'), usd_value=FVal('5.31')),
        location_label='0x2B888954421b424C5D3D9Ce9bB67c9bD47537d12',
        notes='Burned 0.0001 ETH in gas from 0x2B888954421b424C5D3D9Ce9bB67c9bD47537d12',  # noqa: E501
        event_subtype=HistoryEventSubType.FEE,
        counterparty=CPT_GAS,
    ), HistoryBaseEntry(
        event_identifier='0xf32e81dbaae8a763cad17bc96b77c7d9e8c59cc31ed4378b8109ce4b301adbbc',
        sequence_index=3,
        timestamp=TimestampMS(1619924574000),
        location=Location.BLOCKCHAIN,
        event_type=HistoryEventType.DEPOSIT,
        asset=A_ETH,
        balance=Balance(amount=FVal('0.0001'), usd_value=FVal('5.31')),
        location_label='0x2B888954421b424C5D3D9Ce9bB67c9bD47537d12',
        notes='Deposit something somewhere',
        event_subtype=HistoryEventSubType.NONE,
        counterparty='somewhere',
    ), HistoryBaseEntry(
        event_identifier='0x4b5489ed325483db3a8c4831da1d5ac08fb9ab0fd8c570aa3657e0c267a7d023',
        sequence_index=55,
        timestamp=TimestampMS(1629924574000),
        location=Location.BLOCKCHAIN,
        event_type=HistoryEventType.RECEIVE,
        asset=A_ETH,
        balance=Balance(amount=FVal(1), usd_value=FVal('1525.11')),
        location_label='0x2B888954421b424C5D3D9Ce9bB67c9bD47537d12',
        notes='Receive 1 ETH from 0x0EbD2E2130b73107d0C45fF2E16c93E7e2e10e3a to 0x2B888954421b424C5D3D9Ce9bB67c9bD47537d12',  # noqa: E501
        event_subtype=HistoryEventSubType.NONE,
        counterparty='0x0EbD2E2130b73107d0C45fF2E16c93E7e2e10e3a',
    )]

    for entry in entries:
        json_data = entry_to_input_dict(entry, include_identifier=False)
        response = requests.put(
            api_url_for(server, 'historybaseentryresource'),
            json=json_data,
        )
        result = assert_proper_response_with_result(response)
        assert 'identifier' in result
        entry.identifier = result['identifier']

    return entries
Пример #11
0
def aave_event_from_db(event_tuple: AAVE_EVENT_DB_TUPLE) -> AaveEvent:
    """Turns a tuple read from the DB into an appropriate AaveEvent

    May raise a DeserializationError if something is wrong with the DB data
    """
    event_type = event_tuple[1]
    block_number = event_tuple[2]
    timestamp = Timestamp(event_tuple[3])
    tx_hash = event_tuple[4]
    log_index = event_tuple[5]
    asset2 = None
    if event_tuple[9] is not None:
        try:
            asset2 = Asset(event_tuple[9])
        except UnknownAsset as e:
            raise DeserializationError(
                f'Unknown asset {event_tuple[6]} encountered during deserialization '
                f'of Aave event from DB for asset2', ) from e

    try:
        asset1 = Asset(event_tuple[6])
    except UnknownAsset as e:
        raise DeserializationError(
            f'Unknown asset {event_tuple[6]} encountered during deserialization '
            f'of Aave event from DB for asset1', ) from e
    asset1_amount = FVal(event_tuple[7])
    asset1_usd_value = FVal(event_tuple[8])

    if event_type in ('deposit', 'withdrawal'):
        return AaveDepositWithdrawalEvent(
            event_type=event_type,
            block_number=block_number,
            timestamp=timestamp,
            tx_hash=tx_hash,
            log_index=log_index,
            asset=asset1,
            atoken=EthereumToken.from_asset(
                asset2),  # type: ignore # should be a token
            value=Balance(amount=asset1_amount, usd_value=asset1_usd_value),
        )
    if event_type == 'interest':
        return AaveInterestEvent(
            event_type=event_type,
            block_number=block_number,
            timestamp=timestamp,
            tx_hash=tx_hash,
            log_index=log_index,
            asset=asset1,
            value=Balance(amount=asset1_amount, usd_value=asset1_usd_value),
        )
    if event_type == 'borrow':
        if event_tuple[12] not in ('stable', 'variable'):
            raise DeserializationError(
                f'Invalid borrow rate mode encountered in the DB: {event_tuple[12]}',
            )
        borrow_rate_mode: Literal['stable',
                                  'variable'] = event_tuple[12]  # type: ignore
        borrow_rate = deserialize_optional_to_fval(
            value=event_tuple[10],
            name='borrow_rate',
            location='reading aave borrow event from DB',
        )
        accrued_borrow_interest = deserialize_optional_to_fval(
            value=event_tuple[11],
            name='accrued_borrow_interest',
            location='reading aave borrow event from DB',
        )
        return AaveBorrowEvent(
            event_type=event_type,
            block_number=block_number,
            timestamp=timestamp,
            tx_hash=tx_hash,
            log_index=log_index,
            asset=asset1,
            value=Balance(amount=asset1_amount, usd_value=asset1_usd_value),
            borrow_rate_mode=borrow_rate_mode,
            borrow_rate=borrow_rate,
            accrued_borrow_interest=accrued_borrow_interest,
        )
    if event_type == 'repay':
        fee_amount = deserialize_optional_to_fval(
            value=event_tuple[10],
            name='fee_amount',
            location='reading aave repay event from DB',
        )
        fee_usd_value = deserialize_optional_to_fval(
            value=event_tuple[11],
            name='fee_usd_value',
            location='reading aave repay event from DB',
        )
        return AaveRepayEvent(
            event_type=event_type,
            block_number=block_number,
            timestamp=timestamp,
            tx_hash=tx_hash,
            log_index=log_index,
            asset=asset1,
            value=Balance(amount=asset1_amount, usd_value=asset1_usd_value),
            fee=Balance(amount=fee_amount, usd_value=fee_usd_value),
        )
    if event_type == 'liquidation':
        if asset2 is None:
            raise DeserializationError(
                'Did not find asset2 in an aave liquidation event fom the DB.',
            )
        principal_amount = deserialize_optional_to_fval(
            value=event_tuple[10],
            name='principal_amount',
            location='reading aave liquidation event from DB',
        )
        principal_usd_value = deserialize_optional_to_fval(
            value=event_tuple[11],
            name='principal_usd_value',
            location='reading aave liquidation event from DB',
        )
        return AaveLiquidationEvent(
            event_type=event_type,
            block_number=block_number,
            timestamp=timestamp,
            tx_hash=tx_hash,
            log_index=log_index,
            collateral_asset=asset1,
            collateral_balance=Balance(amount=asset1_amount,
                                       usd_value=asset1_usd_value),
            principal_asset=asset2,
            principal_balance=Balance(
                amount=principal_amount,
                usd_value=principal_usd_value,
            ),
        )
    # else
    raise DeserializationError(
        f'Unknown event type {event_type} encountered during '
        f'deserialization of Aave event from DB', )
def test_add_edit_delete_entries(rotkehlchen_api_server):
    rotki = rotkehlchen_api_server.rest_api.rotkehlchen
    entries = _add_entries(rotkehlchen_api_server)
    db = DBHistoryEvents(rotki.data.db)
    saved_events = db.get_history_events(HistoryEventFilterQuery.make(), True)
    for idx, event in enumerate(saved_events):
        assert event == entries[idx]

    entry = entries[2]
    # test editing unknown fails
    unknown_id = 42
    json_data = entry_to_input_dict(entry, include_identifier=True)
    json_data['identifier'] = unknown_id
    response = requests.patch(
        api_url_for(rotkehlchen_api_server, 'historybaseentryresource'),
        json=json_data,
    )
    assert_error_response(
        response=response,
        contained_in_msg=f'Tried to edit event with id {unknown_id} but could not find it in the DB',  # noqa: E501
        status_code=HTTPStatus.CONFLICT,
    )
    # test editing by making sequence index same as an existing one fails
    entry.sequence_index = 3
    entry.timestamp = Timestamp(1649924575000)
    json_data = entry_to_input_dict(entry, include_identifier=True)
    response = requests.patch(
        api_url_for(rotkehlchen_api_server, 'historybaseentryresource'),
        json=json_data,
    )
    assert_error_response(
        response=response,
        contained_in_msg='Tried to edit event to have event_identifier 0xf32e81dbaae8a763cad17bc96b77c7d9e8c59cc31ed4378b8109ce4b301adbbc and sequence_index 3 but it already exists',  # noqa: E501
        status_code=HTTPStatus.CONFLICT,
    )
    # test adding event with  sequence index same as an existing one fails
    entry.sequence_index = 3
    entry.timestamp = Timestamp(1649924575000)
    json_data = entry_to_input_dict(entry, include_identifier=True)
    response = requests.put(
        api_url_for(rotkehlchen_api_server, 'historybaseentryresource'),
        json=json_data,
    )
    assert_error_response(
        response=response,
        contained_in_msg='Failed to add event to the DB due to a DB error: UNIQUE constraint failed: history_events.event_identifier, history_events.sequence_index',  # noqa: E501
        status_code=HTTPStatus.CONFLICT,
    )
    # test editing works
    entry.sequence_index = 4
    entry.timestamp = Timestamp(1639924575000)
    entry.location = Location.UNISWAP
    entry.event_type = HistoryEventType.DEPOSIT
    entry.asset = A_USDT
    entry.balance = Balance(amount=FVal('1500.1'), usd_value=FVal('1499.45'))
    entry.location_label = '0x9531C059098e3d194fF87FebB587aB07B30B1306'
    entry.notes = 'Deposit stuff for staking somewhere'
    entry.event_subtype = HistoryEventSubType.NONE
    entry.counterparty = '0xAB8d71d59827dcc90fEDc5DDb97f87eFfB1B1A5B'
    json_data = entry_to_input_dict(entry, include_identifier=True)
    response = requests.patch(
        api_url_for(rotkehlchen_api_server, 'historybaseentryresource'),
        json=json_data,
    )
    assert_simple_ok_response(response)

    entries.sort(key=lambda x: x.timestamp)  # resort by timestamp
    saved_events = db.get_history_events(HistoryEventFilterQuery.make(), True)
    assert len(saved_events) == 5
    for idx, event in enumerate(saved_events):
        assert event == entries[idx]

    # test deleting unknown fails
    response = requests.delete(
        api_url_for(rotkehlchen_api_server, 'historybaseentryresource'),
        json={'identifiers': [19, 1, 3]},
    )
    assert_error_response(
        response=response,
        contained_in_msg='Tried to remove history event with id 19 which does not exist',
        status_code=HTTPStatus.CONFLICT,
    )
    saved_events = db.get_history_events(HistoryEventFilterQuery.make(), True)
    assert len(saved_events) == 5
    for idx, event in enumerate(saved_events):
        assert event == entries[idx]

    # test deleting works
    response = requests.delete(
        api_url_for(rotkehlchen_api_server, 'historybaseentryresource'),
        json={'identifiers': [2, 4]},
    )
    result = assert_proper_response_with_result(response)
    assert result is True
    saved_events = db.get_history_events(HistoryEventFilterQuery.make(), True)
    # entry is now last since the timestamp was modified
    assert saved_events == [entries[0], entries[3], entry]

    # test that deleting last event of a transaction hash fails
    response = requests.delete(
        api_url_for(rotkehlchen_api_server, 'historybaseentryresource'),
        json={'identifiers': [1]},
    )
    assert_error_response(
        response=response,
        contained_in_msg='Tried to remove history event with id 1 which was the last event of a transaction',  # noqa: E501
        status_code=HTTPStatus.CONFLICT,
    )
    saved_events = db.get_history_events(HistoryEventFilterQuery.make(), True)
    assert saved_events == [entries[0], entries[3], entry]
Пример #13
0
    def get_history_events(
        self,
        from_timestamp: Timestamp,
        to_timestamp: Timestamp,
    ) -> List[DefiEvent]:
        """Gets the history events from maker vaults for accounting

            This is a premium only call. Check happens only in the API level.
        """
        vault_details = self.get_vault_details()
        events = []
        for detail in vault_details:
            total_vault_dai_balance = Balance()
            realized_vault_dai_loss = Balance()
            for event in detail.events:
                timestamp = event.timestamp
                if timestamp < from_timestamp:
                    continue
                if timestamp > to_timestamp:
                    break

                got_asset: Optional[Asset]
                spent_asset: Optional[Asset]
                pnl = got_asset = got_balance = spent_asset = spent_balance = None  # noqa: E501
                count_spent_got_cost_basis = False
                if event.event_type == VaultEventType.GENERATE_DEBT:
                    count_spent_got_cost_basis = True
                    got_asset = A_DAI
                    got_balance = event.value
                    total_vault_dai_balance += event.value
                elif event.event_type == VaultEventType.PAYBACK_DEBT:
                    count_spent_got_cost_basis = True
                    spent_asset = A_DAI
                    spent_balance = event.value
                    total_vault_dai_balance -= event.value
                    if total_vault_dai_balance.amount + realized_vault_dai_loss.amount < ZERO:
                        pnl_balance = total_vault_dai_balance + realized_vault_dai_loss
                        realized_vault_dai_loss += -pnl_balance
                        pnl = [AssetBalance(asset=A_DAI, balance=pnl_balance)]

                elif event.event_type == VaultEventType.DEPOSIT_COLLATERAL:
                    spent_asset = detail.collateral_asset
                    spent_balance = event.value
                elif event.event_type == VaultEventType.WITHDRAW_COLLATERAL:
                    got_asset = detail.collateral_asset
                    got_balance = event.value
                elif event.event_type == VaultEventType.LIQUIDATION:
                    count_spent_got_cost_basis = True
                    # TODO: Don't you also get the dai here -- but how to calculate it?
                    spent_asset = detail.collateral_asset
                    spent_balance = event.value
                    pnl = [
                        AssetBalance(asset=detail.collateral_asset,
                                     balance=-spent_balance)
                    ]
                else:
                    raise AssertionError(
                        f'Invalid Makerdao vault event type {event.event_type}'
                    )

                events.append(
                    DefiEvent(
                        timestamp=timestamp,
                        wrapped_event=event,
                        event_type=DefiEventType.MAKERDAO_VAULT_EVENT,
                        got_asset=got_asset,
                        got_balance=got_balance,
                        spent_asset=spent_asset,
                        spent_balance=spent_balance,
                        pnl=pnl,
                        # Depositing and withdrawing from a vault is not counted in
                        # cost basis. Assets were always yours, you did not rebuy them.
                        # Other actions are counted though to track debt and liquidations
                        count_spent_got_cost_basis=count_spent_got_cost_basis,
                        tx_hash=event.tx_hash,
                    ))

        return events
Пример #14
0
    def _query_vault_details(
        self,
        vault: MakerdaoVault,
        proxy: ChecksumEthAddress,
        urn: ChecksumEthAddress,
    ) -> Optional[MakerdaoVaultDetails]:
        # They can raise:
        # DeserializationError due to hex_or_bytes_to_address, hexstr_to_int
        # RemoteError due to external query errors
        events = self.ethereum.get_logs(
            contract_address=MAKERDAO_CDP_MANAGER.address,
            abi=MAKERDAO_CDP_MANAGER.abi,
            event_name='NewCdp',
            argument_filters={'cdp': vault.identifier},
            from_block=MAKERDAO_CDP_MANAGER.deployed_block,
        )
        if len(events) == 0:
            self.msg_aggregator.add_error(
                'No events found for a Vault creation. This should never '
                'happen. Please open a bug report: https://github.com/rotki/rotki/issues',
            )
            return None
        if len(events) != 1:
            log.error(
                f'Multiple events found for a Vault creation: {events}. Taking '
                f'only the first. This should not happen. Something is wrong',
            )
            self.msg_aggregator.add_error(
                'Multiple events found for a Vault creation. This should never '
                'happen. Please open a bug report: https://github.com/rotki/rotki/issues',
            )
        creation_ts = self.ethereum.get_event_timestamp(events[0])

        # get vat frob events for cross-checking
        argument_filters = {
            'sig': '0x76088703',  # frob
            'arg1': '0x' + vault.ilk.hex(),  # ilk
            'arg2': address_to_bytes32(urn),  # urn
            # arg3 can be urn for the 1st deposit, and proxy/owner for the next ones
            # so don't filter for it
            # 'arg3': address_to_bytes32(proxy),  # proxy - owner
        }
        frob_events = self.ethereum.get_logs(
            contract_address=MAKERDAO_VAT.address,
            abi=MAKERDAO_VAT.abi,
            event_name='LogNote',
            argument_filters=argument_filters,
            from_block=MAKERDAO_VAT.deployed_block,
        )
        frob_event_tx_hashes = [x['transactionHash'] for x in frob_events]

        gemjoin = GEMJOIN_MAPPING.get(vault.collateral_type, None)
        if gemjoin is None:
            self.msg_aggregator.add_warning(
                f'Unknown makerdao vault collateral type detected {vault.collateral_type}.'
                'Skipping ...', )
            return None

        vault_events = []
        # Get the collateral deposit events
        argument_filters = {
            'sig': '0x3b4da69f',  # join
            # In cases where a CDP has been migrated from a SAI CDP to a DAI
            # Vault the usr in the first deposit will be the old address. To
            # detect the first deposit in these cases we need to check for
            # arg1 being the urn
            # 'usr': proxy,
            'arg1': address_to_bytes32(urn),
        }
        events = self.ethereum.get_logs(
            contract_address=gemjoin.address,
            abi=gemjoin.abi,
            event_name='LogNote',
            argument_filters=argument_filters,
            from_block=gemjoin.deployed_block,
        )
        # all subsequent deposits should have the proxy as a usr
        # but for non-migrated CDPS the previous query would also work
        # so in those cases we will have the first deposit 2 times
        argument_filters = {
            'sig': '0x3b4da69f',  # join
            'usr': proxy,
        }
        events.extend(
            self.ethereum.get_logs(
                contract_address=gemjoin.address,
                abi=gemjoin.abi,
                event_name='LogNote',
                argument_filters=argument_filters,
                from_block=gemjoin.deployed_block,
            ))
        deposit_tx_hashes = set()
        for event in events:
            tx_hash = event['transactionHash']
            if tx_hash in deposit_tx_hashes:
                # Skip duplicate deposit that would be detected in non migrated CDP case
                continue

            if tx_hash not in frob_event_tx_hashes:
                # If there is no corresponding frob event then skip
                continue

            deposit_tx_hashes.add(tx_hash)
            amount = asset_normalized_value(
                amount=hexstr_to_int(event['topics'][3]),
                asset=vault.collateral_asset,
            )
            timestamp = self.ethereum.get_event_timestamp(event)
            usd_price = query_usd_price_or_use_default(
                asset=vault.collateral_asset,
                time=timestamp,
                default_value=ZERO,
                location='vault collateral deposit',
            )
            vault_events.append(
                VaultEvent(
                    event_type=VaultEventType.DEPOSIT_COLLATERAL,
                    value=Balance(amount, amount * usd_price),
                    timestamp=timestamp,
                    tx_hash=tx_hash,
                ))

        # Get the collateral withdrawal events
        argument_filters = {
            'sig': '0xef693bed',  # exit
            'usr': proxy,
        }
        events = self.ethereum.get_logs(
            contract_address=gemjoin.address,
            abi=gemjoin.abi,
            event_name='LogNote',
            argument_filters=argument_filters,
            from_block=gemjoin.deployed_block,
        )
        for event in events:
            tx_hash = event['transactionHash']
            if tx_hash not in frob_event_tx_hashes:
                # If there is no corresponding frob event then skip
                continue
            amount = asset_normalized_value(
                amount=hexstr_to_int(event['topics'][3]),
                asset=vault.collateral_asset,
            )
            timestamp = self.ethereum.get_event_timestamp(event)
            usd_price = query_usd_price_or_use_default(
                asset=vault.collateral_asset,
                time=timestamp,
                default_value=ZERO,
                location='vault collateral withdrawal',
            )
            vault_events.append(
                VaultEvent(
                    event_type=VaultEventType.WITHDRAW_COLLATERAL,
                    value=Balance(amount, amount * usd_price),
                    timestamp=timestamp,
                    tx_hash=event['transactionHash'],
                ))

        total_dai_wei = 0
        # Get the dai generation events
        argument_filters = {
            'sig': '0xbb35783b',  # move
            'arg1': address_to_bytes32(urn),
            # For CDPs that were created by migrating from SAI the first DAI generation
            # during vault creation will have the old owner as arg2. So we can't
            # filter for it here. Still seems like the urn as arg1 is sufficient
            # 'arg2': address_to_bytes32(proxy),
        }
        events = self.ethereum.get_logs(
            contract_address=MAKERDAO_VAT.address,
            abi=MAKERDAO_VAT.abi,
            event_name='LogNote',
            argument_filters=argument_filters,
            from_block=MAKERDAO_VAT.deployed_block,
        )
        for event in events:
            given_amount = shift_num_right_by(
                hexstr_to_int(event['topics'][3]), RAY_DIGITS)
            total_dai_wei += given_amount
            amount = token_normalized_value(
                token_amount=given_amount,
                token=A_DAI,
            )
            timestamp = self.ethereum.get_event_timestamp(event)
            usd_price = query_usd_price_or_use_default(
                asset=A_DAI,
                time=timestamp,
                default_value=FVal(1),
                location='vault debt generation',
            )
            vault_events.append(
                VaultEvent(
                    event_type=VaultEventType.GENERATE_DEBT,
                    value=Balance(amount, amount * usd_price),
                    timestamp=timestamp,
                    tx_hash=event['transactionHash'],
                ))

        # Get the dai payback events
        argument_filters = {
            'sig': '0x3b4da69f',  # join
            'usr': proxy,
            'arg1': address_to_bytes32(urn),
        }
        events = self.ethereum.get_logs(
            contract_address=MAKERDAO_DAI_JOIN.address,
            abi=MAKERDAO_DAI_JOIN.abi,
            event_name='LogNote',
            argument_filters=argument_filters,
            from_block=MAKERDAO_DAI_JOIN.deployed_block,
        )
        for event in events:
            given_amount = hexstr_to_int(event['topics'][3])
            total_dai_wei -= given_amount
            amount = token_normalized_value(
                token_amount=given_amount,
                token=A_DAI,
            )
            if amount == ZERO:
                # it seems there is a zero DAI value transfer from the urn when
                # withdrawing ETH. So we should ignore these as events
                continue

            timestamp = self.ethereum.get_event_timestamp(event)
            usd_price = query_usd_price_or_use_default(
                asset=A_DAI,
                time=timestamp,
                default_value=FVal(1),
                location='vault debt payback',
            )

            vault_events.append(
                VaultEvent(
                    event_type=VaultEventType.PAYBACK_DEBT,
                    value=Balance(amount, amount * usd_price),
                    timestamp=timestamp,
                    tx_hash=event['transactionHash'],
                ))

        # Get the liquidation events
        argument_filters = {'urn': urn}
        events = self.ethereum.get_logs(
            contract_address=MAKERDAO_CAT.address,
            abi=MAKERDAO_CAT.abi,
            event_name='Bite',
            argument_filters=argument_filters,
            from_block=MAKERDAO_CAT.deployed_block,
        )
        sum_liquidation_amount = ZERO
        sum_liquidation_usd = ZERO
        for event in events:
            if isinstance(event['data'], str):
                lot = event['data'][:66]
            else:  # bytes
                lot = event['data'][:32]
            amount = asset_normalized_value(
                amount=hexstr_to_int(lot),
                asset=vault.collateral_asset,
            )
            timestamp = self.ethereum.get_event_timestamp(event)
            sum_liquidation_amount += amount
            usd_price = query_usd_price_or_use_default(
                asset=vault.collateral_asset,
                time=timestamp,
                default_value=ZERO,
                location='vault collateral liquidation',
            )
            amount_usd_value = amount * usd_price
            sum_liquidation_usd += amount_usd_value
            vault_events.append(
                VaultEvent(
                    event_type=VaultEventType.LIQUIDATION,
                    value=Balance(amount, amount_usd_value),
                    timestamp=timestamp,
                    tx_hash=event['transactionHash'],
                ))

        total_interest_owed = vault.debt.amount - token_normalized_value(
            token_amount=total_dai_wei,
            token=A_DAI,
        )
        # sort vault events by timestamp
        vault_events.sort(key=lambda event: event.timestamp)

        return MakerdaoVaultDetails(
            identifier=vault.identifier,
            collateral_asset=vault.collateral_asset,
            total_interest_owed=total_interest_owed,
            creation_ts=creation_ts,
            total_liquidated=Balance(sum_liquidation_amount,
                                     sum_liquidation_usd),
            events=vault_events,
        )
Пример #15
0
def test_aave_withdraw_v1(database, ethereum_manager,
                          function_scope_messages_aggregator):
    """Data taken from
    https://etherscan.io/tx/0x4fed67963375a3f90916f0cf7cb9e4d12644629e36233025b36060494ffba486
    """
    # TODO: For faster tests hard-code the transaction and the logs here so no remote query needed
    tx_hash = deserialize_evm_tx_hash(
        '0x4fed67963375a3f90916f0cf7cb9e4d12644629e36233025b36060494ffba486'
    )  # noqa: E501
    events = get_decoded_events_of_transaction(
        ethereum_manager=ethereum_manager,
        database=database,
        msg_aggregator=function_scope_messages_aggregator,
        tx_hash=tx_hash,
    )
    amount = '7968.408929477087756071'
    interest = '88.663672238882760399'
    expected_events = [
        HistoryBaseEntry(
            event_identifier=tx_hash.hex(),  # pylint: disable=no-member
            sequence_index=0,
            timestamp=1598217272000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.SPEND,
            event_subtype=HistoryEventSubType.FEE,
            asset=A_ETH,
            balance=Balance(amount=FVal('0.028562839354')),
            location_label=ADDY,
            notes=f'Burned 0.028562839354 ETH in gas from {ADDY}',
            counterparty=CPT_GAS,
        ),
        HistoryBaseEntry(
            event_identifier=tx_hash.hex(),  # pylint: disable=no-member
            sequence_index=98,
            timestamp=1598217272000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.RECEIVE,
            event_subtype=HistoryEventSubType.REWARD,
            asset=A_ADAI_V1,
            balance=Balance(amount=FVal(interest)),
            location_label=ADDY,
            notes=f'Gain {interest} aDAI from aave-v1 as interest',
            counterparty=CPT_AAVE_V1,
        ),
        HistoryBaseEntry(
            event_identifier=tx_hash.hex(),  # pylint: disable=no-member
            sequence_index=99,
            timestamp=1598217272000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.SPEND,
            event_subtype=HistoryEventSubType.RETURN_WRAPPED,
            asset=A_ADAI_V1,
            balance=Balance(amount=FVal(amount)),
            location_label=ADDY,
            notes=f'Return {amount} aDAI to aave-v1',
            counterparty=CPT_AAVE_V1,
        ),
        HistoryBaseEntry(
            event_identifier=tx_hash.hex(),  # pylint: disable=no-member
            sequence_index=102,
            timestamp=1598217272000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.WITHDRAWAL,
            event_subtype=HistoryEventSubType.REMOVE_ASSET,
            asset=A_DAI,
            balance=Balance(amount=FVal(amount)),
            location_label=ADDY,
            notes=f'Withdraw {amount} DAI from aave-v1',
            counterparty=CPT_AAVE_V1,
        ),
    ]
    assert expected_events == events
Пример #16
0
def test_1inchv1_swap(database, ethereum_manager,
                      function_scope_messages_aggregator):
    """Data taken from
    https://etherscan.io/tx/0x8b8652c502e80ce7c5441cdedc9184ea8f07a9c13b4c3446a47ae08c6c1d6efa
    """
    # TODO: For faster tests hard-code the transaction and the logs here so no remote query needed
    tx_hash = deserialize_evm_tx_hash(
        '0x8b8652c502e80ce7c5441cdedc9184ea8f07a9c13b4c3446a47ae08c6c1d6efa'
    )  # noqa: E501
    events = get_decoded_events_of_transaction(
        ethereum_manager=ethereum_manager,
        database=database,
        msg_aggregator=function_scope_messages_aggregator,
        tx_hash=tx_hash,
    )
    chispender_addy = '0xed04A060050cc289d91779A8BB3942C3A6589254'
    expected_events = [
        HistoryBaseEntry(
            event_identifier=tx_hash.hex(),  # pylint: disable=no-member
            sequence_index=0,
            timestamp=1594500575000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.SPEND,
            event_subtype=HistoryEventSubType.FEE,
            asset=A_ETH,
            balance=Balance(amount=FVal('0.00896373909')),
            location_label=ADDY,
            notes=f'Burned 0.00896373909 ETH in gas from {ADDY}',
            counterparty=CPT_GAS,
        ),
        HistoryBaseEntry(
            event_identifier=tx_hash.hex(),  # pylint: disable=no-member
            sequence_index=90,
            timestamp=1594500575000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.TRADE,
            event_subtype=HistoryEventSubType.SPEND,
            asset=A_USDC,
            balance=Balance(amount=FVal('138.75')),
            location_label=ADDY,
            notes=f'Swap 138.75 USDC in 1inch-v1 from {ADDY}',
            counterparty=CPT_ONEINCH_V1,
        ),
        HistoryBaseEntry(
            event_identifier=tx_hash.hex(),  # pylint: disable=no-member
            sequence_index=91,
            timestamp=1594500575000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.TRADE,
            event_subtype=HistoryEventSubType.RECEIVE,
            asset=A_DAI,
            balance=Balance(amount=FVal('135.959878392183347402')),
            location_label=ADDY,
            notes=
            f'Receive 135.959878392183347402 DAI from 1inch-v1 swap in {ADDY}',
            counterparty=CPT_ONEINCH_V1,
        ),
        HistoryBaseEntry(
            event_identifier=tx_hash.hex(),  # pylint: disable=no-member
            sequence_index=102,
            timestamp=1594500575000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.SPEND,
            event_subtype=HistoryEventSubType.NONE,
            asset=A_CHI,
            balance=Balance(),
            location_label=ADDY,
            notes=f'Send 0 CHI from {ADDY} to {ZERO_ADDRESS}',
            counterparty=ZERO_ADDRESS,
        ),
        HistoryBaseEntry(
            event_identifier=tx_hash.hex(),  # pylint: disable=no-member
            sequence_index=103,
            timestamp=1594500575000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.INFORMATIONAL,
            event_subtype=HistoryEventSubType.APPROVE,
            asset=A_CHI,
            balance=Balance(),
            location_label=ADDY,
            notes=f'Approve 0 CHI of {ADDY} for spending by {chispender_addy}',
            counterparty=chispender_addy,
        ),
    ]
    assert expected_events == events
Пример #17
0
def test_get_events(rotkehlchen_api_server, ethereum_accounts):  # pylint: disable=unused-argument
    async_query = random.choice([False, True])
    rotki = rotkehlchen_api_server.rest_api.rotkehlchen

    setup = setup_balances(
        rotki,
        ethereum_accounts=ethereum_accounts,
        btc_accounts=None,
        original_queries=['zerion', 'logs', 'blocknobytime'],
    )

    with ExitStack() as stack:
        # patch ethereum/etherscan to not autodetect tokens
        setup.enter_ethereum_patches(stack)
        response = requests.get(
            api_url_for(rotkehlchen_api_server, 'adexhistoryresource'),
            json={
                'async_query': async_query,
                'to_timestamp': 1611747322
            },
        )
        if async_query:
            task_id = assert_ok_async_response(response)
            outcome = wait_for_async_task(rotkehlchen_api_server, task_id)
            assert outcome['message'] == ''
            result = outcome['result']
        else:
            result = assert_proper_response_with_result(response)

    identity_address = '0x2a6c38D16BFdc7b4a20f1F982c058F07BDCe9204'
    tom_pool_id = '0x2ce0c96383fb229d9776f33846e983a956a7d95844fac57b180ed0071d93bb28'
    bond_id = '0x540cab9883923c01e657d5da4ca5674b6e4626b4a148224635495502d674c7c5'
    channel_id = '0x30d87bab0ef1e7f8b4c3b894ca2beed41bbd54c481f31e5791c1e855c9dbf4ba'
    result = result[ADEX_TEST_ADDR]
    expected_events = [
        Bond(
            tx_hash=
            '0x9989f47c6c0a761f98f910ac24e2438d858be96c12124a13be4bb4b3150c55ea',
            address=ADEX_TEST_ADDR,
            identity_address=identity_address,
            timestamp=1604366004,
            bond_id=bond_id,
            pool_id=tom_pool_id,
            value=Balance(FVal(100000), FVal(200000)),
            nonce=0,
            slashed_at=0,
        ),
        ChannelWithdraw(
            tx_hash=
            '0xa9ee91af823c0173fc5ada908ff9fe3f4d7c84a2c9da795f0889b3f4ace75b13',
            address=ADEX_TEST_ADDR,
            identity_address=identity_address,
            timestamp=1607453764,
            channel_id=channel_id,
            pool_id=tom_pool_id,
            value=Balance(FVal('5056.894263641728544592'),
                          FVal('10113.788527283457089184')),
            token=A_ADX,
            log_index=316,
        ),
        Unbond(
            tx_hash=
            '0xa9ee91af823c0173fc5ada908ff9fe3f4d7c84a2c9da795f0889b3f4ace75b13',
            address=ADEX_TEST_ADDR,
            identity_address=identity_address,
            timestamp=1607453764,
            bond_id=bond_id,
            pool_id=tom_pool_id,
            value=Balance(FVal(100000), FVal(200000)),
        )
    ]
    assert len(result['events']) == 8
    assert result['events'][:len(expected_events)] == [
        x.serialize() for x in expected_events
    ]
    assert 'staking_details' in result
    # Make sure events end up in the DB
    assert len(rotki.data.db.get_adex_events()) != 0
    # test adex data purging from the db works
    response = requests.delete(
        api_url_for(
            rotkehlchen_api_server,
            'namedethereummoduledataresource',
            module_name='adex',
        ))
    assert_simple_ok_response(response)
    assert len(rotki.data.db.get_adex_events()) == 0
Пример #18
0
def history_event_from_kraken(
    events: List[Dict[str, Any]],
    name: str,
    msg_aggregator: MessagesAggregator,
) -> Tuple[List[HistoryBaseEntry], bool]:
    """
    This function gets raw data from kraken and creates a list of related history events
    to be used in the app. It returns a list of events and a boolean in the case that an unknown
    type is found.
    """
    group_events = []
    found_unknown_event = False
    current_fee_index = len(events)
    for idx, raw_event in enumerate(events):
        try:
            timestamp = TimestampMS((deserialize_fval(
                value=raw_event['time'],
                name='time',
                location='kraken ledger processing',
            ) * 1000).to_int(exact=False))
            identifier = raw_event['refid']
            event_type = kraken_ledger_entry_type_to_ours(raw_event['type'])
            asset = asset_from_kraken(raw_event['asset'])
            event_subtype = HistoryEventSubType.NONE
            notes = None
            raw_amount = deserialize_asset_amount(raw_event['amount'])
            # If we don't know how to handle an event atm or we find an unsupported
            # event type the logic will be to store it as unknown and if in the future
            # we need some information from it we can take actions to process them
            if event_type == HistoryEventType.TRANSFER:
                if raw_event['subtype'] == 'spottostaking':
                    event_type = HistoryEventType.STAKING
                    event_subtype = HistoryEventSubType.DEPOSIT_ASSET
                elif raw_event['subtype'] == 'stakingfromspot':
                    event_type = HistoryEventType.STAKING
                    event_subtype = HistoryEventSubType.RECEIVE_WRAPPED
                elif raw_event['subtype'] == 'stakingtospot':
                    event_type = HistoryEventType.STAKING
                    event_subtype = HistoryEventSubType.REMOVE_ASSET
                elif raw_event['subtype'] == 'spotfromstaking':
                    event_type = HistoryEventType.STAKING
                    event_subtype = HistoryEventSubType.RETURN_WRAPPED
            elif event_type == HistoryEventType.ADJUSTMENT:
                if raw_amount < ZERO:
                    event_subtype = HistoryEventSubType.SPEND
                else:
                    event_subtype = HistoryEventSubType.RECEIVE
            elif event_type == HistoryEventType.STAKING:
                event_subtype = HistoryEventSubType.REWARD
            elif event_type == HistoryEventType.INFORMATIONAL:
                found_unknown_event = True
                notes = raw_event['type']
                log.warning(
                    f'Encountered kraken historic event type we do not process. {raw_event}',
                )
            fee_amount = deserialize_asset_amount(raw_event['fee'])

            # Make sure to not generate an event for KFEES that is not of type FEE
            if asset != A_KFEE:
                group_events.append(
                    HistoryBaseEntry(
                        event_identifier=identifier,
                        sequence_index=idx,
                        timestamp=timestamp,
                        location=Location.KRAKEN,
                        location_label=name,
                        asset=asset,
                        balance=Balance(
                            amount=raw_amount,
                            usd_value=ZERO,
                        ),
                        notes=notes,
                        event_type=event_type,
                        event_subtype=event_subtype,
                    ))
            if fee_amount != ZERO:
                group_events.append(
                    HistoryBaseEntry(
                        event_identifier=identifier,
                        sequence_index=current_fee_index,
                        timestamp=timestamp,
                        location=Location.KRAKEN,
                        location_label=name,
                        asset=asset,
                        balance=Balance(
                            amount=fee_amount,
                            usd_value=ZERO,
                        ),
                        notes=notes,
                        event_type=event_type,
                        event_subtype=HistoryEventSubType.FEE,
                    ))
                # Increase the fee index to not have duplicates in the case of having a normal
                # fee and KFEE
                current_fee_index += 1
        except (DeserializationError, KeyError, UnknownAsset) as e:
            msg = str(e)
            if isinstance(e, KeyError):
                msg = f'Keyrror {msg}'
            msg_aggregator.add_error(
                f'Failed to read ledger event from kraken {raw_event} due to {msg}',
            )
            return [], False
    return group_events, found_unknown_event
Пример #19
0
            1588463542: FVal('1.009'),
            1588430911: FVal('1.009'),
            1592175763: FVal('1.013'),
            1594502373: FVal('1.019'),
        },
    },
}
aave_mocked_current_prices = {A_ADAI_V1: FVal('1.017')}

expected_aave_deposit_test_events = [
    AaveDepositWithdrawalEvent(
        event_type='deposit',
        asset=A_DAI,
        atoken=A_ADAI_V1,
        value=Balance(
            amount=FVal('102.926986169186236436'),
            usd_value=FVal('104.367963975554843746104'),
        ),
        block_number=9963767,
        timestamp=Timestamp(1588114293),
        tx_hash='0x8b72307967c4f7a486c1cb1b6ebca5e549de06e02930ece0399e2096f1a132c5',
        log_index=72,
    ), AaveDepositWithdrawalEvent(
        event_type='deposit',
        asset=A_DAI,
        atoken=A_ADAI_V1,
        value=Balance(
            amount=FVal('160'),
            usd_value=FVal('161.440'),
        ),
        block_number=9987395,
        timestamp=Timestamp(1588430911),
Пример #20
0
    def query_balances(self) -> ExchangeQueryBalances:
        try:
            kraken_balances = self.api_query('Balance', req={})
        except RemoteError as e:
            if "Missing key: 'result'" in str(e):
                # handle https://github.com/rotki/rotki/issues/946
                kraken_balances = {}
            else:
                msg = ('Kraken API request failed. Could not reach kraken due '
                       'to {}'.format(e))
                log.error(msg)
                return None, msg

        assets_balance: DefaultDict[Asset, Balance] = defaultdict(Balance)
        for kraken_name, amount_ in kraken_balances.items():
            try:
                amount = deserialize_asset_amount(amount_)
                if amount == ZERO:
                    continue

                our_asset = asset_from_kraken(kraken_name)
            except UnknownAsset as e:
                self.msg_aggregator.add_warning(
                    f'Found unsupported/unknown kraken asset {e.asset_name}. '
                    f' Ignoring its balance query.', )
                continue
            except DeserializationError as e:
                msg = str(e)
                self.msg_aggregator.add_error(
                    'Error processing kraken balance for {kraken_name}. Check logs '
                    'for details. Ignoring it.', )
                log.error(
                    'Error processing kraken balance',
                    kraken_name=kraken_name,
                    amount=amount_,
                    error=msg,
                )
                continue

            balance = Balance(amount=amount)
            if our_asset.identifier != 'KFEE':
                # There is no price value for KFEE. TODO: Shouldn't we then just skip the balance?
                try:
                    usd_price = Inquirer().find_usd_price(our_asset)
                except RemoteError as e:
                    self.msg_aggregator.add_error(
                        f'Error processing kraken balance entry due to inability to '
                        f'query USD price: {str(e)}. Skipping balance entry', )
                    continue

                balance.usd_value = balance.amount * usd_price

            assets_balance[our_asset] += balance
            log.debug(
                'kraken balance query result',
                currency=our_asset,
                amount=balance.amount,
                usd_value=balance.usd_value,
            )

        return dict(assets_balance), ''
Пример #21
0
def test_pickle_deposit(database, ethereum_manager, eth_transactions):
    """Data for deposit taken from
    https://etherscan.io/tx/0xba9a52a144d4e79580a557160e9f8269d3e5373ce44bce00ebd609754034b7bd
    """
    msg_aggregator = MessagesAggregator()
    tx_hex = '0xba9a52a144d4e79580a557160e9f8269d3e5373ce44bce00ebd609754034b7bd'
    evmhash = deserialize_evm_tx_hash(tx_hex)
    transaction = EthereumTransaction(
        tx_hash=evmhash,
        timestamp=1646375440,
        block_number=14318825,
        from_address='0x0f1a748cDF53Bbad378CE2C4429463d01CcE0C3f',
        to_address='0xb4EBc2C371182DeEa04B2264B9ff5AC4F0159C69',
        value=0,
        gas=171249,
        gas_price=22990000000,
        gas_used=171249,
        input_data=hexstring_to_bytes('0xb6b55f250000000000000000000000000000000000000000000000312ebe013bcd5d6fed'),  # noqa: E501
        nonce=507,
    )
    receipt = EthereumTxReceipt(
        tx_hash=evmhash,
        contract_address=None,
        status=True,
        type=0,
        logs=[
            EthereumTxReceiptLog(
                log_index=259,
                data=hexstring_to_bytes('0x0000000000000000000000000000000000000000000000312ebe013bcd5d6fed'),  # noqa: E501
                address=string_to_ethereum_address('0xf4d2888d29D722226FafA5d9B24F9164c092421E'),
                removed=False,
                topics=[
                    hexstring_to_bytes('0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'),  # noqa: E501
                    hexstring_to_bytes('0x0000000000000000000000000f1a748cdf53bbad378ce2c4429463d01cce0c3f'),  # noqa: E501
                    hexstring_to_bytes('0x000000000000000000000000b4ebc2c371182deea04b2264b9ff5ac4f0159c69'),  # noqa: E501
                ],
            ), EthereumTxReceiptLog(
                log_index=261,
                data=hexstring_to_bytes('0x00000000000000000000000000000000000000000000001e67da0f130b2d9371'),  # noqa: E501
                address=string_to_ethereum_address('0xb4EBc2C371182DeEa04B2264B9ff5AC4F0159C69'),
                removed=False,
                topics=[
                    hexstring_to_bytes('0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'),  # noqa: E501
                    hexstring_to_bytes('0x0000000000000000000000000000000000000000000000000000000000000000'),  # noqa: E501
                    hexstring_to_bytes('0x0000000000000000000000000f1a748cdf53bbad378ce2c4429463d01cce0c3f'),  # noqa: E501
                ],
            ),
        ],
    )

    dbethtx = DBEthTx(database)
    dbethtx.add_ethereum_transactions([transaction], relevant_address=None)
    decoder = EVMTransactionDecoder(
        database=database,
        ethereum_manager=ethereum_manager,
        eth_transactions=eth_transactions,
        msg_aggregator=msg_aggregator,
    )
    events = decoder.decode_transaction(transaction=transaction, tx_receipt=receipt)
    assert len(events) == 3
    expected_events = [
        HistoryBaseEntry(
            event_identifier='0xba9a52a144d4e79580a557160e9f8269d3e5373ce44bce00ebd609754034b7bd',
            sequence_index=0,
            timestamp=1646375440000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.SPEND,
            event_subtype=HistoryEventSubType.FEE,
            asset=A_ETH,
            balance=Balance(
                amount=FVal(0.00393701451),
                usd_value=ZERO,
            ),
            location_label='0x0f1a748cDF53Bbad378CE2C4429463d01CcE0C3f',
            notes='Burned 0.00393701451 ETH in gas from 0x0f1a748cDF53Bbad378CE2C4429463d01CcE0C3f',  # noqa: E501
            counterparty=CPT_GAS,
        ), HistoryBaseEntry(
            event_identifier='0xba9a52a144d4e79580a557160e9f8269d3e5373ce44bce00ebd609754034b7bd',
            sequence_index=260,
            timestamp=1646375440000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.DEPOSIT,
            event_subtype=HistoryEventSubType.DEPOSIT_ASSET,
            asset=EthereumToken('0xf4d2888d29D722226FafA5d9B24F9164c092421E'),
            balance=Balance(amount=FVal('907.258590539447889901'), usd_value=ZERO),
            location_label='0x0f1a748cDF53Bbad378CE2C4429463d01CcE0C3f',
            notes='Deposit 907.258590539447889901 LOOKS in pickle contract',
            counterparty='pickle finance',
        ), HistoryBaseEntry(
            event_identifier='0xba9a52a144d4e79580a557160e9f8269d3e5373ce44bce00ebd609754034b7bd',
            sequence_index=262,
            timestamp=1646375440000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.RECEIVE,
            event_subtype=HistoryEventSubType.RECEIVE_WRAPPED,
            asset=EthereumToken('0xb4EBc2C371182DeEa04B2264B9ff5AC4F0159C69'),
            balance=Balance(amount=FVal('560.885632516582380401'), usd_value=ZERO),
            location_label='0x0f1a748cDF53Bbad378CE2C4429463d01CcE0C3f',
            notes='Receive 560.885632516582380401 pLOOKS after depositing in pickle contract',
            counterparty='pickle finance',
        )]
    assert events == expected_events
Пример #22
0
    def deserialize_from_db(cls,
                            result: YEARN_EVENT_DB_TUPLE) -> 'YearnVaultEvent':
        """
        Turns a tuple read from the DB into an appropriate YearnVaultEvent
        May raise a DeserializationError if there is an issue with information in
        the database
        """
        location = 'deserialize yearn vault event from db'
        realized_pnl = None
        if result[8] is not None and result[9] is not None:
            pnl_amount = deserialize_optional_to_fval(
                value=result[8],
                name='pnl_amount',
                location=location,
            )
            pnl_usd_value = deserialize_optional_to_fval(
                value=result[9],
                name='pnl_usd_value',
                location=location,
            )
            realized_pnl = Balance(amount=pnl_amount, usd_value=pnl_usd_value)

        from_value_amount = deserialize_optional_to_fval(
            value=result[3],
            name='from_value_amount',
            location=location,
        )
        from_value_usd_value = deserialize_optional_to_fval(
            result[4],
            name='from_value_usd_value',
            location=location,
        )
        to_value_amount = deserialize_optional_to_fval(
            value=result[6],
            name='to_value_amount',
            location=location,
        )
        to_value_usd_value = deserialize_optional_to_fval(
            value=result[7],
            name='to_value_usd_value',
            location=location,
        )
        try:
            block_number = int(result[10])
        except ValueError as e:
            raise DeserializationError(
                f'Failed to deserialize block number {result[10]} in yearn vault event: {str(e)}',
            ) from e
        from_asset = Asset(result[2])
        to_asset = Asset(result[5])
        return cls(
            event_type=result[1],
            from_asset=from_asset,
            from_value=Balance(amount=from_value_amount,
                               usd_value=from_value_usd_value),
            to_asset=to_asset,
            to_value=Balance(amount=to_value_amount,
                             usd_value=to_value_usd_value),
            realized_pnl=realized_pnl,
            block_number=block_number,
            timestamp=deserialize_timestamp(result[11]),
            tx_hash=result[12],
            log_index=result[13],
            version=result[14],
        )
Пример #23
0
def test_eth2_deposits_serialization():
    addr1 = make_ethereum_address()
    addr2 = make_ethereum_address()
    deposits = [
        Eth2Deposit(
            from_address=addr1,
            pubkey=
            '0xb016e31f633a21fbe42a015152399361184f1e2c0803d89823c224994af74a561c4ad8cfc94b18781d589d03e952cd5b',  # noqa: E501
            withdrawal_credentials=
            '0x004c7691c2085648f394ffaef851f3b1d51b95f7263114bc923fc5338f5fc499',  # noqa: E501
            value=Balance(FVal(32), FVal(64)),
            tx_hash=deserialize_evm_tx_hash(
                '0xd9eca1c2a0c5ff2f25071713432b21cc4d0ff2e8963edc63a48478e395e08db1'
            ),  # noqa: E501
            tx_index=22,
            timestamp=Timestamp(int(1604506685)),
        ),
        Eth2Deposit(
            from_address=addr2,
            pubkey=
            '0xa8ff5fc88412d080a297683c25a791ef77eb52d75b265fabab1f2c2591bb927c35818ac6289bc6680ab252787d0ebab3',  # noqa: E501
            withdrawal_credentials=
            '0x00cfe1c10347d642a8b8daf86d23bcb368076972691445de2cf517ff43765817',  # noqa: E501
            value=Balance(FVal(32), FVal(64)),
            tx_hash=deserialize_evm_tx_hash(
                '0x6905f4d1843fb8c003c1fbbc2c8e6c5f9792f4f44ddb1122553412ee0b128da7'
            ),  # noqa: E501
            tx_index=221,
            timestamp=Timestamp(int(1605043544)),
        ),
    ]

    serialized = process_result_list(deposits)
    assert serialized == [
        {
            'from_address': addr1,
            'pubkey':
            '0xb016e31f633a21fbe42a015152399361184f1e2c0803d89823c224994af74a561c4ad8cfc94b18781d589d03e952cd5b',  # noqa: E501
            'withdrawal_credentials':
            '0x004c7691c2085648f394ffaef851f3b1d51b95f7263114bc923fc5338f5fc499',  # noqa: E501
            'value': {
                'amount': '32',
                'usd_value': '64'
            },
            'tx_hash':
            '0xd9eca1c2a0c5ff2f25071713432b21cc4d0ff2e8963edc63a48478e395e08db1',
            'tx_index': 22,
            'timestamp': 1604506685,
        },
        {
            'from_address': addr2,
            'pubkey':
            '0xa8ff5fc88412d080a297683c25a791ef77eb52d75b265fabab1f2c2591bb927c35818ac6289bc6680ab252787d0ebab3',  # noqa: E501
            'withdrawal_credentials':
            '0x00cfe1c10347d642a8b8daf86d23bcb368076972691445de2cf517ff43765817',  # noqa: E501
            'value': {
                'amount': '32',
                'usd_value': '64'
            },
            'tx_hash':
            '0x6905f4d1843fb8c003c1fbbc2c8e6c5f9792f4f44ddb1122553412ee0b128da7',
            'tx_index': 221,
            'timestamp': 1605043544,
        },
    ]
Пример #24
0
    def _get_balances_graph(
        self,
        addresses: List[ChecksumEthAddress],
    ) -> ProtocolBalance:
        """Get the addresses' pools data querying this AMM's subgraph

        Each liquidity position is converted into a <LiquidityPool>.
        """
        address_balances: DDAddressToLPBalances = defaultdict(list)
        known_assets: Set[EthereumToken] = set()
        unknown_assets: Set[EthereumToken] = set()

        addresses_lower = [address.lower() for address in addresses]
        querystr = format_query_indentation(LIQUIDITY_POSITIONS_QUERY.format())
        query_id = '0'
        query_offset = 0
        param_types = {
            '$limit': 'Int!',
            '$offset': 'Int!',
            '$addresses': '[String!]',
            '$balance': 'BigDecimal!',
            '$id': 'ID!',
        }
        param_values = {
            'limit': GRAPH_QUERY_LIMIT,
            'offset': 0,
            'addresses': addresses_lower,
            'balance': '0',
            'id': query_id,
        }
        while True:
            try:
                result = self.graph.query(
                    querystr=querystr,
                    param_types=param_types,
                    param_values=param_values,
                )
            except RemoteError as e:
                self.msg_aggregator.add_error(
                    SUBGRAPH_REMOTE_ERROR_MSG.format(
                        error_msg=str(e),
                        location=self.location,
                    ), )
                raise

            result_data = result['liquidityPositions']
            for lp in result_data:
                lp_pair = lp['pair']
                lp_total_supply = FVal(lp_pair['totalSupply'])
                user_lp_balance = FVal(lp['liquidityTokenBalance'])
                try:
                    user_address = deserialize_ethereum_address(
                        lp['user']['id'])
                    lp_address = deserialize_ethereum_address(lp_pair['id'])
                except DeserializationError as e:
                    msg = (
                        f'Failed to Deserialize address. Skipping {self.location} '
                        f'pool {lp_pair} with user address {lp["user"]["id"]}')
                    log.error(msg)
                    raise RemoteError(msg) from e

                # Insert LP tokens reserves within tokens dicts
                token0 = lp_pair['token0']
                token0['total_amount'] = lp_pair['reserve0']
                token1 = lp_pair['token1']
                token1['total_amount'] = lp_pair['reserve1']

                liquidity_pool_assets = []
                for token in token0, token1:
                    try:
                        deserialized_eth_address = deserialize_ethereum_address(
                            token['id'])
                    except DeserializationError as e:
                        msg = (
                            f'Failed to deserialize token address {token["id"]} '
                            f'Bad token address in {self.location} lp pair came from the graph.'
                        )
                        log.error(msg)
                        raise RemoteError(msg) from e

                    asset = get_or_create_ethereum_token(
                        userdb=self.database,
                        symbol=token['symbol'],
                        ethereum_address=deserialized_eth_address,
                        name=token['name'],
                        decimals=int(token['decimals']),
                    )
                    if asset.has_oracle():
                        known_assets.add(asset)
                    else:
                        unknown_assets.add(asset)

                    # Estimate the underlying asset total_amount
                    asset_total_amount = FVal(token['total_amount'])
                    user_asset_balance = (user_lp_balance / lp_total_supply *
                                          asset_total_amount)

                    liquidity_pool_asset = LiquidityPoolAsset(
                        asset=asset,
                        total_amount=asset_total_amount,
                        user_balance=Balance(amount=user_asset_balance),
                    )
                    liquidity_pool_assets.append(liquidity_pool_asset)

                liquidity_pool = LiquidityPool(
                    address=lp_address,
                    assets=liquidity_pool_assets,
                    total_supply=lp_total_supply,
                    user_balance=Balance(amount=user_lp_balance),
                )
                address_balances[user_address].append(liquidity_pool)
                query_id = lp['id']

            # Check whether an extra request is needed
            if len(result_data) < GRAPH_QUERY_LIMIT:
                break

            # Update pagination step
            if query_offset == GRAPH_QUERY_SKIP_LIMIT:
                query_offset = 0
                new_query_id = query_id
            else:
                query_offset += GRAPH_QUERY_LIMIT
                new_query_id = '0'
            param_values = {
                **param_values,
                'id': new_query_id,
                'offset': query_offset,
            }

        protocol_balance = ProtocolBalance(
            address_balances=dict(address_balances),
            known_assets=known_assets,
            unknown_assets=unknown_assets,
        )
        return protocol_balance
Пример #25
0
    def query_balances(self) -> ExchangeQueryBalances:
        try:
            resp = self.api_query('balances')
        except RemoteError as e:
            msg = ('Bittrex API request failed. Could not reach bittrex due '
                   'to {}'.format(e))
            log.error(msg)
            return None, msg

        returned_balances: Dict[Asset, Balance] = {}
        for entry in resp:
            try:
                asset = asset_from_bittrex(entry['currencySymbol'])
                amount = deserialize_asset_amount(entry['total'])
            except UnsupportedAsset as e:
                self.msg_aggregator.add_warning(
                    f'Found unsupported bittrex asset {e.asset_name}. '
                    f' Ignoring its balance query.', )
                continue
            except UnknownAsset as e:
                self.msg_aggregator.add_warning(
                    f'Found unknown bittrex asset {e.asset_name}. '
                    f' Ignoring its balance query.', )
                continue
            except DeserializationError as e:
                msg = (
                    f'Failed to parse bittrex balance entry due to {str(e)}.'
                    f' Ignoring its balance query. Check logs for more details'
                )
                self.msg_aggregator.add_error(msg)
                log.error(
                    'Error processing a bittrex balance entry',
                    entry=entry,
                    error=msg,
                )
                continue

            if entry['currencySymbol'] == 'BTXCRD':
                # skip BTXCRD balance, since it's bittrex internal and we can't query usd price
                continue

            try:
                usd_price = Inquirer().find_usd_price(asset=asset)
            except RemoteError as e:
                self.msg_aggregator.add_error(
                    f'Error processing bittrex balance entry due to inability to '
                    f'query USD price: {str(e)}. Skipping balance entry', )
                continue

            usd_value = amount * usd_price
            returned_balances[asset] = Balance(
                amount=amount,
                usd_value=usd_value,
            )
            log.debug(
                'bittrex balance query result',
                currency=asset,
                amount=amount,
                usd_value=usd_value,
            )

        return returned_balances, ''
Пример #26
0
def test_aave_eth_withdraw_v1(database, ethereum_manager,
                              function_scope_messages_aggregator):
    """Data taken from
    https://etherscan.io/tx/0xbd333bdd5784c10630aac5683e63f703e660a78d06f95b2ff2a8788a8dade787
    """
    # TODO: For faster tests hard-code the transaction and the logs here so no remote query needed
    tx_hash = deserialize_evm_tx_hash(
        '0xbd333bdd5784c10630aac5683e63f703e660a78d06f95b2ff2a8788a8dade787'
    )  # noqa: E501
    events = get_decoded_events_of_transaction(
        ethereum_manager=ethereum_manager,
        database=database,
        msg_aggregator=function_scope_messages_aggregator,
        tx_hash=tx_hash,
    )
    amount = '1.000240847792940067'
    interest = '0.000240847792940067'
    expected_events = [
        HistoryBaseEntry(
            event_identifier=tx_hash.hex(),  # pylint: disable=no-member
            sequence_index=0,
            timestamp=1605789951000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.SPEND,
            event_subtype=HistoryEventSubType.FEE,
            asset=A_ETH,
            balance=Balance(amount=FVal('0.021740928')),
            location_label=ADDY2,
            notes=f'Burned 0.021740928 ETH in gas from {ADDY2}',
            counterparty=CPT_GAS,
        ),
        HistoryBaseEntry(
            event_identifier=tx_hash.hex(),  # pylint: disable=no-member
            sequence_index=1,
            timestamp=1605789951000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.SPEND,
            event_subtype=HistoryEventSubType.RETURN_WRAPPED,
            asset=A_AETH_V1,
            balance=Balance(amount=FVal(amount)),
            location_label=ADDY2,
            notes=f'Return {amount} aETH to aave-v1',
            counterparty=CPT_AAVE_V1,
        ),
        HistoryBaseEntry(
            event_identifier=tx_hash.hex(),  # pylint: disable=no-member
            sequence_index=2,
            timestamp=1605789951000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.WITHDRAWAL,
            event_subtype=HistoryEventSubType.REMOVE_ASSET,
            asset=A_ETH,
            balance=Balance(amount=FVal(amount)),
            location_label=ADDY2,
            notes=f'Withdraw {amount} ETH from aave-v1',
            counterparty=CPT_AAVE_V1,
        ),
        HistoryBaseEntry(
            event_identifier=tx_hash.hex(),  # pylint: disable=no-member
            sequence_index=135,
            timestamp=1605789951000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.RECEIVE,
            event_subtype=HistoryEventSubType.REWARD,
            asset=A_AETH_V1,
            balance=Balance(amount=FVal(interest)),
            location_label=ADDY2,
            notes=f'Gain {interest} aETH from aave-v1 as interest',
            counterparty=CPT_AAVE_V1,
        ),
    ]
    assert expected_events == events
Пример #27
0
def test_curve_deposit(database, ethereum_manager, eth_transactions):
    """Data for deposit taken from
    https://etherscan.io/tx/0x523b7df8e168315e97a836a3d516d639908814785d7df1ef1745de3e55501982
    tests that a deposit for the aave pool in curve works correctly
    """
    msg_aggregator = MessagesAggregator()
    tx_hex = '0x523b7df8e168315e97a836a3d516d639908814785d7df1ef1745de3e55501982'
    location_label = '0x57bF3B0f29E37619623994071C9e12091919675c'
    evmhash = deserialize_evm_tx_hash(tx_hex)
    transaction = EthereumTransaction(
        tx_hash=evmhash,
        timestamp=1650276061,
        block_number=14608535,
        from_address='0x57bF3B0f29E37619623994071C9e12091919675c',
        to_address='0xDeBF20617708857ebe4F679508E7b7863a8A8EeE',
        value=0,
        gas=171249,
        gas_price=22990000000,
        gas_used=171249,
        input_data=hexstring_to_bytes('0x2b6e993a000000000000000000000000000000000000000000005512b9a6a672640100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060c5f3590000000000000000000000000000000000000000000005255abbd43baa53603f90000000000000000000000000000000000000000000000000000000000000001'),  # noqa: E501
        nonce=599,
    )
    receipt = EthereumTxReceipt(
        tx_hash=evmhash,
        contract_address=None,
        status=True,
        type=0,
        logs=[
            EthereumTxReceiptLog(
                log_index=370,
                data=hexstring_to_bytes('0x000000000000000000000000000000000000000000005512b9a6a67264010000'),  # noqa: E501
                address=string_to_ethereum_address('0x6B175474E89094C44Da98b954EedeAC495271d0F'),
                removed=False,
                topics=[
                    hexstring_to_bytes('0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'),  # noqa: E501
                    hexstring_to_bytes('0x00000000000000000000000057bf3b0f29e37619623994071c9e12091919675c'),  # noqa: E501
                    hexstring_to_bytes('0x000000000000000000000000debf20617708857ebe4f679508e7b7863a8a8eee'),  # noqa: E501
                ],
            ), EthereumTxReceiptLog(
                log_index=383,
                data=hexstring_to_bytes('0x000000000000000000000000000000000000000000000000000000060c5f3590'),  # noqa: E501
                address=string_to_ethereum_address('0xdAC17F958D2ee523a2206206994597C13D831ec7'),
                removed=False,
                topics=[
                    hexstring_to_bytes('0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'),  # noqa: E501
                    hexstring_to_bytes('0x00000000000000000000000057bf3b0f29e37619623994071c9e12091919675c'),  # noqa: E501
                    hexstring_to_bytes('0x000000000000000000000000debf20617708857ebe4f679508e7b7863a8a8eee'),  # noqa: E501
                ],
            ), EthereumTxReceiptLog(
                log_index=396,
                data=hexstring_to_bytes('0x000000000000000000000000000000000000000000005328394d50efea7abaf4'),  # noqa: E501
                address=string_to_ethereum_address('0xFd2a8fA60Abd58Efe3EeE34dd494cD491dC14900'),
                removed=False,
                topics=[
                    hexstring_to_bytes('0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'),  # noqa: E501
                    hexstring_to_bytes('0x0000000000000000000000000000000000000000000000000000000000000000'),  # noqa: E501
                    hexstring_to_bytes('0x00000000000000000000000057bf3b0f29e37619623994071c9e12091919675c'),  # noqa: E501
                ],
            ), EthereumTxReceiptLog(
                log_index=397,
                data=hexstring_to_bytes('0x000000000000000000000000000000000000000000005512b9a6a672640100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060c5f3590000000000000000000000000000000000000000000000002038eb27e79fe96ef0000000000000000000000000000000000000000000000000000000002e973710000000000000000000000000000000000000000000000000000000001832b050000000000000000000000000000000000000000002a38dd00eecdefe02a2fcf00000000000000000000000000000000000000000026c6b056a9a8e3b89d5717'),  # noqa: E501
                address=string_to_ethereum_address('0xDeBF20617708857ebe4F679508E7b7863a8A8EeE'),
                removed=False,
                topics=[
                    hexstring_to_bytes('0x423f6495a08fc652425cf4ed0d1f9e37e571d9b9529b1c1c23cce780b2e7df0d'),  # noqa: E501
                    hexstring_to_bytes('0x00000000000000000000000057bf3b0f29e37619623994071c9e12091919675c'),  # noqa: E501
                ],
            ),
        ],
    )

    dbethtx = DBEthTx(database)
    dbethtx.add_ethereum_transactions([transaction], relevant_address=None)
    decoder = EVMTransactionDecoder(
        database=database,
        ethereum_manager=ethereum_manager,
        eth_transactions=eth_transactions,
        msg_aggregator=msg_aggregator,
    )
    events = decoder.decode_transaction(transaction=transaction, tx_receipt=receipt)
    expected_events = [
        HistoryBaseEntry(
            event_identifier=tx_hex,
            sequence_index=0,
            timestamp=1650276061000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.SPEND,
            event_subtype=HistoryEventSubType.FEE,
            asset=A_ETH,
            balance=Balance(
                amount=FVal(0.00393701451),
                usd_value=ZERO,
            ),
            location_label=location_label,
            notes='Burned 0.00393701451 ETH in gas from 0x57bF3B0f29E37619623994071C9e12091919675c',  # noqa: E501
            counterparty=CPT_GAS,
        ), HistoryBaseEntry(
            event_identifier=tx_hex,
            sequence_index=371,
            timestamp=1650276061000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.DEPOSIT,
            event_subtype=HistoryEventSubType.DEPOSIT_ASSET,
            asset=A_DAI,
            balance=Balance(amount=FVal('401746.57'), usd_value=ZERO),
            location_label=location_label,
            notes='Deposit 401746.57 DAI in curve pool 0xDeBF20617708857ebe4F679508E7b7863a8A8EeE',
            counterparty=CPT_CURVE,
        ), HistoryBaseEntry(
            event_identifier=tx_hex,
            sequence_index=384,
            timestamp=1650276061000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.DEPOSIT,
            event_subtype=HistoryEventSubType.DEPOSIT_ASSET,
            asset=A_USDT,
            balance=Balance(amount=FVal('25977.37'), usd_value=ZERO),
            location_label=location_label,
            notes='Deposit 25977.37 USDT in curve pool 0xDeBF20617708857ebe4F679508E7b7863a8A8EeE',
            counterparty=CPT_CURVE,
        ), HistoryBaseEntry(
            event_identifier=tx_hex,
            sequence_index=397,
            timestamp=1650276061000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.RECEIVE,
            event_subtype=HistoryEventSubType.RECEIVE_WRAPPED,
            asset=EthereumToken('0xFd2a8fA60Abd58Efe3EeE34dd494cD491dC14900'),
            balance=Balance(amount=FVal('392698.416886553664731892'), usd_value=ZERO),
            location_label=location_label,
            notes='Receive 392698.416886553664731892 a3CRV after depositing in curve pool 0xDeBF20617708857ebe4F679508E7b7863a8A8EeE',  # noqa: E501
            counterparty=CPT_CURVE,
        )]
    assert len(events) == 4
    assert events == expected_events
Пример #28
0
def test_aave_deposit_v1(database, ethereum_manager,
                         function_scope_messages_aggregator):
    """Data taken from
    https://etherscan.io/tx/0x930879d66d13c37edf25cdbb2d2e85b65c3b2a026529ff4085146bb7a5398410
    """
    # TODO: For faster tests hard-code the transaction and the logs here so no remote query needed
    tx_hash = deserialize_evm_tx_hash(
        '0x930879d66d13c37edf25cdbb2d2e85b65c3b2a026529ff4085146bb7a5398410'
    )  # noqa: E501
    events = get_decoded_events_of_transaction(
        ethereum_manager=ethereum_manager,
        database=database,
        msg_aggregator=function_scope_messages_aggregator,
        tx_hash=tx_hash,
    )
    amount = '2507.675873220870275072'
    expected_events = [
        HistoryBaseEntry(
            event_identifier=tx_hash.hex(),  # pylint: disable=no-member
            sequence_index=0,
            timestamp=1595376667000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.SPEND,
            event_subtype=HistoryEventSubType.FEE,
            asset=A_ETH,
            balance=Balance(amount=FVal('0.00825148723006')),
            location_label=ADDY,
            notes=f'Burned 0.00825148723006 ETH in gas from {ADDY}',
            counterparty=CPT_GAS,
        ),
        HistoryBaseEntry(
            event_identifier=tx_hash.hex(),  # pylint: disable=no-member
            sequence_index=93,
            timestamp=1595376667000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.RECEIVE,
            event_subtype=HistoryEventSubType.REWARD,
            asset=A_ADAI_V1,
            balance=Balance(amount=FVal('17.91499070977557364')),
            location_label=ADDY,
            notes='Gain 17.91499070977557364 aDAI from aave-v1 as interest',
            counterparty=CPT_AAVE_V1,
        ),
        HistoryBaseEntry(
            event_identifier=tx_hash.hex(),  # pylint: disable=no-member
            sequence_index=94,
            timestamp=1595376667000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.DEPOSIT,
            event_subtype=HistoryEventSubType.DEPOSIT_ASSET,
            asset=A_DAI,
            balance=Balance(amount=FVal(amount)),
            location_label=ADDY,
            notes=f'Deposit {amount} DAI to aave-v1 from {ADDY}',
            counterparty=CPT_AAVE_V1,
        ),
        HistoryBaseEntry(
            event_identifier=tx_hash.hex(),  # pylint: disable=no-member
            sequence_index=96,
            timestamp=1595376667000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.RECEIVE,
            event_subtype=HistoryEventSubType.RECEIVE_WRAPPED,
            asset=A_ADAI_V1,
            balance=Balance(amount=FVal(amount)),
            location_label=ADDY,
            notes=f'Receive {amount} aDAI from aave-v1 for {ADDY}',
            counterparty=CPT_AAVE_V1,
        ),
    ]
    assert expected_events == events
Пример #29
0
def test_curve_remove_liquidity_with_internal(database, ethereum_manager, eth_transactions):
    """Data for deposit taken from
    https://etherscan.io/tx/0x30bb99f3e34fb1fbcf009320af7e290caf18b04b207319e15aa8ffbf645f4ad9
    This tests uses the steth pool to verify that withdrawals are correctly decoded when an
    internal transaction is made for eth transfers
    """
    msg_aggregator = MessagesAggregator()
    tx_hex = '0x30bb99f3e34fb1fbcf009320af7e290caf18b04b207319e15aa8ffbf645f4ad9'
    location_label = '0xa8005630caE7b7d2AFADD38FD3B3040d13cbE2BC'
    evmhash = deserialize_evm_tx_hash(tx_hex)
    transaction = EthereumTransaction(
        tx_hash=evmhash,
        timestamp=1650276061,
        block_number=14647221,
        from_address=location_label,
        to_address='0xF178C0b5Bb7e7aBF4e12A4838C7b7c5bA2C623c0',
        value=0,
        gas=171249,
        gas_price=22990000000,
        gas_used=171249,
        input_data=hexstring_to_bytes('0x1a4d01d20000000000000000000000000000000000000000000000a8815561fefbe56aa300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a8d15fc942541fea7f'),  # noqa: E501
        nonce=5,
    )
    receipt = EthereumTxReceipt(
        tx_hash=evmhash,
        contract_address=None,
        status=True,
        type=0,
        logs=[
            EthereumTxReceiptLog(
                log_index=191,
                data=hexstring_to_bytes('0x0000000000000000000000000000000000000000000000000dc335d474901e08'),  # noqa: E501
                address=string_to_ethereum_address('0x06325440D014e39736583c165C2963BA99fAf14E'),
                removed=False,
                topics=[
                    hexstring_to_bytes('0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'),  # noqa: E501
                    hexstring_to_bytes('0x000000000000000000000000a8005630cae7b7d2afadd38fd3b3040d13cbe2bc'),  # noqa: E501
                    hexstring_to_bytes('0x0000000000000000000000000000000000000000000000000000000000000000'),  # noqa: E501
                ],
            ), EthereumTxReceiptLog(
                log_index=192,
                data=hexstring_to_bytes('0x0000000000000000000000000000000000000000000000000dc335d474901e080000000000000000000000000000000000000000000000000e48d018621788fa'),  # noqa: E501
                address=string_to_ethereum_address('0xDC24316b9AE028F1497c275EB9192a3Ea0f67022'),
                removed=False,
                topics=[
                    hexstring_to_bytes('0x9e96dd3b997a2a257eec4df9bb6eaf626e206df5f543bd963682d143300be310'),  # noqa: E501
                    hexstring_to_bytes('0x000000000000000000000000a8005630cae7b7d2afadd38fd3b3040d13cbe2bc'),  # noqa: E501
                ],
            ),
        ],
    )
    internal_tx = EthereumInternalTransaction(
        parent_tx_hash=evmhash,
        trace_id=27,
        timestamp=Timestamp(1591043988),
        block_number=14647221,
        from_address='0xDC24316b9AE028F1497c275EB9192a3Ea0f67022',
        to_address='0xa8005630caE7b7d2AFADD38FD3B3040d13cbE2BC',
        value=FVal('1.02930131799766041') * EXP18,
    )
    dbethtx = DBEthTx(database)
    dbethtx.add_ethereum_transactions([transaction], relevant_address=None)
    dbethtx.add_ethereum_internal_transactions([internal_tx], relevant_address=location_label)  # noqa: E501
    decoder = EVMTransactionDecoder(
        database=database,
        ethereum_manager=ethereum_manager,
        eth_transactions=eth_transactions,
        msg_aggregator=msg_aggregator,
    )
    events = decoder.decode_transaction(transaction=transaction, tx_receipt=receipt)
    expected_events = [
        HistoryBaseEntry(
            event_identifier=tx_hex,
            sequence_index=0,
            timestamp=1650276061000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.SPEND,
            event_subtype=HistoryEventSubType.FEE,
            asset=A_ETH,
            balance=Balance(
                amount=FVal(0.00393701451),
                usd_value=ZERO,
            ),
            location_label=location_label,
            notes='Burned 0.00393701451 ETH in gas from 0xa8005630caE7b7d2AFADD38FD3B3040d13cbE2BC',  # noqa: E501
            counterparty=CPT_GAS,
        ), HistoryBaseEntry(
            event_identifier=tx_hex,
            sequence_index=1,
            timestamp=1650276061000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.WITHDRAWAL,
            event_subtype=HistoryEventSubType.REMOVE_ASSET,
            asset=A_ETH,
            balance=Balance(amount=FVal('1.02930131799766041'), usd_value=ZERO),
            location_label=location_label,
            notes='Remove 1.02930131799766041 ETH from the curve pool',
            counterparty=CPT_CURVE,
        ), HistoryBaseEntry(
            event_identifier=tx_hex,
            sequence_index=193,
            timestamp=1650276061000,
            location=Location.BLOCKCHAIN,
            event_type=HistoryEventType.SPEND,
            event_subtype=HistoryEventSubType.RETURN_WRAPPED,
            asset=EthereumToken('0x06325440D014e39736583c165C2963BA99fAf14E'),
            balance=Balance(amount=FVal('0.991695529556581896'), usd_value=ZERO),
            location_label=location_label,
            notes='Return 0.991695529556581896 steCRV',
            counterparty=CPT_CURVE,
        )]
    assert expected_events == events
Пример #30
0
    def get_trove_history(
        self,
        addresses: List[ChecksumEthAddress],
        from_timestamp: Timestamp,
        to_timestamp: Timestamp,
    ) -> Dict[ChecksumEthAddress, List[LiquityEvent]]:
        addresses_to_query = list(addresses)
        proxied_addresses = self._get_accounts_having_proxy()
        proxies_to_address = {v: k for k, v in proxied_addresses.items()}
        addresses_to_query += proxied_addresses.values()

        try:
            query = self._get_raw_history(addresses_to_query, 'trove')
        except RemoteError as e:
            log.error(
                f'Failed to query trove graph events for liquity. {str(e)}')
            query = {}

        result: Dict[ChecksumEthAddress,
                     List[LiquityEvent]] = defaultdict(list)
        for trove in query.get('troves', []):
            owner = to_checksum_address(trove['owner']['id'])
            if owner in proxies_to_address:
                owner = proxies_to_address[owner]
            for change in trove['changes']:
                try:
                    timestamp = change['transaction']['timestamp']
                    if timestamp < from_timestamp:
                        continue
                    if timestamp > to_timestamp:
                        break
                    operation = TroveOperation.deserialize(
                        change['troveOperation'])
                    collateral_change = deserialize_optional_to_fval(
                        value=change['collateralChange'],
                        name='collateralChange',
                        location='liquity',
                    )
                    debt_change = deserialize_optional_to_fval(
                        value=change['debtChange'],
                        name='debtChange',
                        location='liquity',
                    )
                    lusd_price = PriceHistorian().query_historical_price(
                        from_asset=A_LUSD,
                        to_asset=A_USD,
                        timestamp=timestamp,
                    )
                    eth_price = PriceHistorian().query_historical_price(
                        from_asset=A_ETH,
                        to_asset=A_USD,
                        timestamp=timestamp,
                    )
                    debt_after_amount = deserialize_optional_to_fval(
                        value=change['debtAfter'],
                        name='debtAfter',
                        location='liquity',
                    )
                    collateral_after_amount = deserialize_optional_to_fval(
                        value=change['collateralAfter'],
                        name='collateralAfter',
                        location='liquity',
                    )
                    event = LiquityTroveEvent(
                        kind='trove',
                        tx=change['transaction']['id'],
                        address=owner,
                        timestamp=timestamp,
                        debt_after=AssetBalance(
                            asset=A_LUSD,
                            balance=Balance(
                                amount=debt_after_amount,
                                usd_value=lusd_price * debt_after_amount,
                            ),
                        ),
                        collateral_after=AssetBalance(
                            asset=A_ETH,
                            balance=Balance(
                                amount=collateral_after_amount,
                                usd_value=eth_price * collateral_after_amount,
                            ),
                        ),
                        debt_delta=AssetBalance(
                            asset=A_LUSD,
                            balance=Balance(
                                amount=debt_change,
                                usd_value=lusd_price * debt_change,
                            ),
                        ),
                        collateral_delta=AssetBalance(
                            asset=A_ETH,
                            balance=Balance(
                                amount=collateral_change,
                                usd_value=eth_price * collateral_change,
                            ),
                        ),
                        trove_operation=operation,
                        sequence_number=str(change['sequenceNumber']),
                    )
                    result[owner].append(event)
                except (DeserializationError, KeyError) as e:
                    log.debug(
                        f'Failed to deserialize Liquity trove event: {change}')
                    msg = str(e)
                    if isinstance(e, KeyError):
                        msg = f'Missing key entry for {msg}.'
                    self.msg_aggregator.add_warning(
                        f'Ignoring Liquity Trove event in Liquity. '
                        f'Failed to decode remote information. {msg}.', )
                    continue

        return result