Esempio n. 1
0
def test_get_trade_with_1_token_pool(
        rotkehlchen_api_server,
        ethereum_accounts,  # pylint: disable=unused-argument
        rotki_premium_credentials,  # pylint: disable=unused-argument
        start_with_valid_premium,  # pylint: disable=unused-argument
):
    """
    Test the special case of a swap within an 1 token pool.
    This can probably happen if the controller has since removed tokens from the pool.
    """
    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, 'balancertradeshistoryresource'),
            json={
                'from_timestamp': 1621358338,
                'to_timestamp': 1621358340,
            },
        )
        result = assert_proper_response_with_result(response)

    db_trades = rotki.data.db.get_amm_swaps()
    assert len(db_trades) == 29

    address_trades = result[BALANCER_TEST_ADDR4]
    assert len(address_trades) == 1
    assert address_trades[0] == AMMTrade(
        trade_type=TradeType.BUY,
        base_asset=A_WETH,
        quote_asset=A_WBTC,
        amount=AssetAmount(FVal('0.205421420618533148')),
        rate=Price(FVal('0.07606382992071615428519015532')),
        trade_index=0,
        swaps=[
            AMMSwap(
                tx_hash='0x4f9e0d8aa660a5d3db276a1ade038f7027f29838dd22d5276571d2e4ea7131ae',  # noqa: E501
                log_index=84,
                address=string_to_ethereum_address(BALANCER_TEST_ADDR4),  # noqa: E501
                from_address=string_to_ethereum_address('0xFD3dFB524B2dA40c8a6D703c62BE36b5D8540626'),  # noqa: E501
                to_address=string_to_ethereum_address('0x582818356331877553F3E9Cf9557b48e5DdbD54a'),  # noqa: E501
                timestamp=Timestamp(1621358339),
                location=Location.BALANCER,
                token0=A_WBTC,
                token1=A_WETH,
                amount0_in=AssetAmount(FVal('0.01562514')),
                amount1_in=AssetAmount(ZERO),
                amount0_out=AssetAmount(ZERO),
                amount1_out=AssetAmount(FVal('0.205421420618533148')),
            ),
        ],
    ).serialize()
Esempio n. 2
0
def get_balancer_test_addr2_expected_trades():
    """In a function since the new(unknown) assets needs to have been loaded in the DB"""
    A_WCRES = EthereumToken.initialize(  # noqa: N806
        address=string_to_ethereum_address(
            '0xa0afAA285Ce85974c3C881256cB7F225e3A1178a'),
        decimals=18,
        symbol='wCRES',
    )
    return [
        AMMTrade(
            trade_type=TradeType.BUY,
            base_asset=A_WETH,
            quote_asset=A_AAVE,
            amount=AssetAmount(FVal('1.616934038985744521')),
            rate=Price(FVal('6.963972908793392530935439799')),
            trade_index=1,
            swaps=[
                AMMSwap(
                    tx_hash=
                    '0x3c457da9b541ae39a7dc781ab04a03938b98b5649512aec2a2d32635c9bbf589',  # noqa: E501
                    log_index=24,
                    address=string_to_ethereum_address(
                        '0x029f388aC4D5C8BfF490550ce0853221030E822b'
                    ),  # noqa: E501
                    from_address=string_to_ethereum_address(
                        '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56'
                    ),  # noqa: E501
                    to_address=string_to_ethereum_address(
                        '0x7c90a3cd7Ec80dd2F633ed562480AbbEEd3bE546'
                    ),  # noqa: E501
                    timestamp=Timestamp(1607008178),
                    location=Location.BALANCER,
                    token0=A_AAVE,
                    token1=A_WETH,
                    amount0_in=AssetAmount(FVal('11.260284842802604032')),
                    amount1_in=AssetAmount(ZERO),
                    amount0_out=AssetAmount(ZERO),
                    amount1_out=AssetAmount(FVal('1.616934038985744521')),
                ),
            ],
        ),
        AMMTrade(
            trade_type=TradeType.BUY,
            base_asset=A_AAVE,
            quote_asset=A_WETH,
            amount=AssetAmount(FVal('11.260286362820602094')),
            rate=Price(FVal('0.1416068599966922676173010716')),
            trade_index=0,
            swaps=[
                AMMSwap(
                    tx_hash=
                    '0x3c457da9b541ae39a7dc781ab04a03938b98b5649512aec2a2d32635c9bbf589',  # noqa: E501
                    log_index=18,
                    address=string_to_ethereum_address(
                        '0x029f388aC4D5C8BfF490550ce0853221030E822b'
                    ),  # noqa: E501
                    from_address=string_to_ethereum_address(
                        '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56'
                    ),  # noqa: E501
                    to_address=string_to_ethereum_address(
                        '0x70985E557aE0CD6dC88189a532e54FbC61927BAd'
                    ),  # noqa: E501
                    timestamp=Timestamp(1607008178),
                    location=Location.BALANCER,
                    token0=A_WETH,
                    token1=A_AAVE,
                    amount0_in=AssetAmount(FVal('1.594533794502600192')),
                    amount1_in=AssetAmount(ZERO),
                    amount0_out=AssetAmount(ZERO),
                    amount1_out=AssetAmount(FVal('11.260286362820602094')),
                ),
            ],
        ),
        AMMTrade(
            trade_type=TradeType.BUY,
            base_asset=A_WETH,
            quote_asset=A_SYN,
            amount=AssetAmount(FVal('1.352902561458047718')),
            rate=Price(FVal('724.4303350385182691258363763')),
            trade_index=0,
            swaps=[
                AMMSwap(
                    tx_hash=
                    '0x5e235216cb03e4eb234014f5ccf3efbfddd40c4576424e2a8204f1d12b96ed35',  # noqa: E501
                    log_index=143,
                    address=string_to_ethereum_address(
                        '0x029f388aC4D5C8BfF490550ce0853221030E822b'
                    ),  # noqa: E501
                    from_address=string_to_ethereum_address(
                        '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56'
                    ),  # noqa: E501
                    to_address=string_to_ethereum_address(
                        '0x8982E9bBf7AC6A49c434aD81D2fF8e16895318e5'
                    ),  # noqa: E501
                    timestamp=Timestamp(1607008218),
                    location=Location.BALANCER,
                    token0=A_SYN,
                    token1=A_WETH,
                    amount0_in=AssetAmount(FVal('980.08365587152306176')),
                    amount1_in=AssetAmount(ZERO),
                    amount0_out=AssetAmount(ZERO),
                    amount1_out=AssetAmount(FVal('1.352902561458047718')),
                ),
            ],
        ),
        AMMTrade(
            trade_type=TradeType.BUY,
            base_asset=A_WETH,
            quote_asset=A_WCRES,
            amount=AssetAmount(FVal('0.205709519074945018')),
            rate=Price(FVal('232.7409943164679514496089589')),
            trade_index=0,
            swaps=[
                AMMSwap(
                    tx_hash=
                    '0xf54be824b4619777f1db0e3da91b0cd52f6dba730c95a75644e2b085e6ab9824',  # noqa: E501
                    log_index=300,
                    address=string_to_ethereum_address(
                        '0x029f388aC4D5C8BfF490550ce0853221030E822b'
                    ),  # noqa: E501
                    from_address=string_to_ethereum_address(
                        '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56'
                    ),  # noqa: E501
                    to_address=string_to_ethereum_address(
                        '0x10996eC4f3E7A1b314EbD966Fa8b1ad0fE0f8307'
                    ),  # noqa: E501
                    timestamp=Timestamp(1607009877),
                    location=Location.BALANCER,
                    token0=A_WCRES,
                    token1=A_WETH,
                    amount0_in=AssetAmount(FVal('47.87703800986513408')),
                    amount1_in=AssetAmount(ZERO),
                    amount0_out=AssetAmount(ZERO),
                    amount1_out=AssetAmount(FVal('0.205709519074945018')),
                ),
            ],
        ),
        AMMTrade(
            trade_type=TradeType.BUY,
            base_asset=A_API3,
            quote_asset=A_WETH,
            amount=AssetAmount(FVal('295.881648100500428692')),
            rate=Price(FVal('0.003346787723157288562491614498')),
            trade_index=0,
            swaps=[
                AMMSwap(
                    tx_hash=
                    '0xfed4e15051e3ce4dc0d2816f719701e5920e40bf41614b5feaa3c5a6a0186c03',  # noqa: E501
                    log_index=22,
                    address=string_to_ethereum_address(
                        '0x029f388aC4D5C8BfF490550ce0853221030E822b'
                    ),  # noqa: E501
                    from_address=string_to_ethereum_address(
                        '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56'
                    ),  # noqa: E501
                    to_address=string_to_ethereum_address(
                        '0x997c0fc9578a8194EFDdE2E0cD7aa6A69cFCD7c1'
                    ),  # noqa: E501
                    timestamp=Timestamp(1607010888),
                    location=Location.BALANCER,
                    token0=A_WETH,
                    token1=A_API3,
                    amount0_in=AssetAmount(FVal('0.990253067370299904')),
                    amount1_in=AssetAmount(ZERO),
                    amount0_out=AssetAmount(ZERO),
                    amount1_out=AssetAmount(FVal('295.881648100500428692')),
                ),
            ],
        ),
        AMMTrade(
            trade_type=TradeType.BUY,
            base_asset=A_WETH,
            quote_asset=A_MFT,
            amount=AssetAmount(FVal('0.686544199299304057')),
            rate=Price(FVal('243775.0324093115004367119900')),
            trade_index=0,
            swaps=[
                AMMSwap(
                    tx_hash=
                    '0xf0147c4b81098676c08ae20ae5bf8f8b60d0ad79eec484f3f93ac6ab49a3c51c',  # noqa: E501
                    log_index=97,
                    address=string_to_ethereum_address(
                        '0x029f388aC4D5C8BfF490550ce0853221030E822b'
                    ),  # noqa: E501
                    from_address=string_to_ethereum_address(
                        '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56'
                    ),  # noqa: E501
                    to_address=string_to_ethereum_address(
                        '0x2Eb6CfbFFC8785Cd0D9f2d233d0a617bF4269eeF'
                    ),  # noqa: E501
                    timestamp=Timestamp(1607015059),
                    location=Location.BALANCER,
                    token0=A_MFT,
                    token1=A_WETH,
                    amount0_in=AssetAmount(FVal('167362.334434612660404224')),
                    amount1_in=AssetAmount(ZERO),
                    amount0_out=AssetAmount(ZERO),
                    amount1_out=AssetAmount(FVal('0.686544199299304057')),
                ),
            ],
        ),
        AMMTrade(
            trade_type=TradeType.BUY,
            base_asset=A_WETH,
            quote_asset=A_AAVE,
            amount=AssetAmount(FVal('3.055412574642681758')),
            rate=Price(FVal('6.916116208273240607778771150')),
            trade_index=1,
            swaps=[
                AMMSwap(
                    tx_hash=
                    '0x67c0e9a0fdd002d0b9d1cca0c8e4ca4d30435bbf57bbf0091396275efaea414b',  # noqa: E501
                    log_index=37,
                    address=string_to_ethereum_address(
                        '0x029f388aC4D5C8BfF490550ce0853221030E822b'
                    ),  # noqa: E501
                    from_address=string_to_ethereum_address(
                        '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56'
                    ),  # noqa: E501
                    to_address=string_to_ethereum_address(
                        '0x0E552307659E70bF61f918f96AA880Cdec40d7E2'
                    ),  # noqa: E501
                    timestamp=Timestamp(1607015339),
                    location=Location.BALANCER,
                    token0=A_AAVE,
                    token1=A_WETH,
                    amount0_in=AssetAmount(FVal('21.131588430448123904')),
                    amount1_in=AssetAmount(ZERO),
                    amount0_out=AssetAmount(ZERO),
                    amount1_out=AssetAmount(FVal('3.055412574642681758')),
                ),
            ],
        ),
        AMMTrade(
            trade_type=TradeType.BUY,
            base_asset=A_AAVE,
            quote_asset=A_WETH,
            amount=AssetAmount(FVal('21.131588567541018817')),
            rate=Price(FVal('0.1435213742524287826717337545')),
            trade_index=0,
            swaps=[
                AMMSwap(
                    tx_hash=
                    '0x67c0e9a0fdd002d0b9d1cca0c8e4ca4d30435bbf57bbf0091396275efaea414b',  # noqa: E501
                    log_index=31,
                    address=string_to_ethereum_address(
                        '0x029f388aC4D5C8BfF490550ce0853221030E822b'
                    ),  # noqa: E501
                    from_address=string_to_ethereum_address(
                        '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56'
                    ),  # noqa: E501
                    to_address=string_to_ethereum_address(
                        '0x7c90a3cd7Ec80dd2F633ed562480AbbEEd3bE546'
                    ),  # noqa: E501
                    timestamp=Timestamp(1607015339),
                    location=Location.BALANCER,
                    token0=A_WETH,
                    token1=A_AAVE,
                    amount0_in=AssetAmount(FVal('3.0328346313504')),
                    amount1_in=AssetAmount(ZERO),
                    amount0_out=AssetAmount(ZERO),
                    amount1_out=AssetAmount(FVal('21.131588567541018817')),
                ),
            ],
        ),
    ]
Esempio n. 3
0
from rotkehlchen.chain.ethereum.trades import AMMSwap, AMMTrade
from rotkehlchen.constants.misc import ZERO
from rotkehlchen.fval import FVal
from rotkehlchen.serialization.deserialize import deserialize_ethereum_address
from rotkehlchen.tests.api.test_balancer import BALANCER_TEST_ADDR2_EXPECTED_TRADES
from rotkehlchen.typing import AssetAmount, Location, Price, Timestamp, TradeType

TEST_SWAPS_TX_1 = [
    AMMSwap(
        tx_hash='0x1b0d3525964d8e5fbcc0dcdeebcced4bec9017f648e97c3c9761fda1ca6e7b22',
        log_index=254,
        address=deserialize_ethereum_address('0x8e670b4d6651C4051e65B21AA4a575F3f99b8B83'),
        from_address=deserialize_ethereum_address('0x65003947dC16956AfC4400008606001500940000'),
        to_address=deserialize_ethereum_address('0x7860E28ebFB8Ae052Bfe279c07aC5d94c9cD2937'),
        timestamp=Timestamp(1614094145),
        location=Location.BALANCER,
        token0=EthereumToken('USDC'),
        token1=EthereumToken('AMPL'),
        amount0_in=AssetAmount(FVal('12401.224639')),
        amount1_in=AssetAmount(ZERO),
        amount0_out=AssetAmount(ZERO),
        amount1_out=AssetAmount(FVal('14285.153512382')),
    ),
]
TEST_SWAPS_TX_2 = [
    AMMSwap(
        tx_hash='0x1b0d3525964d8e5fbcc0dcdeebcced4bec9017f648e97c3c9761fda1ca6e7b22',
        log_index=254,
        address=deserialize_ethereum_address('0x8e670b4d6651C4051e65B21AA4a575F3f99b8B83'),
        from_address=deserialize_ethereum_address('0x65003947dC16956AfC4400008606001500940000'),
        to_address=deserialize_ethereum_address('0x7860E28ebFB8Ae052Bfe279c07aC5d94c9cD2937'),
Esempio n. 4
0
    def _get_trades_graph_for_address(
            self,
            address: ChecksumEthAddress,
            start_ts: Timestamp,
            end_ts: Timestamp,
    ) -> List[AMMTrade]:
        """Get the address' trades data querying the Uniswap subgraph

        Each trade (swap) instantiates an <AMMTrade>.

        The trade pair (i.e. BASE_QUOTE) is determined by `reserve0_reserve1`.
        Translated to Uniswap lingo:

        Trade type BUY:
        - `asset1In` (QUOTE, reserve1) is gt 0.
        - `asset0Out` (BASE, reserve0) is gt 0.

        Trade type SELL:
        - `asset0In` (BASE, reserve0) is gt 0.
        - `asset1Out` (QUOTE, reserve1) is gt 0.
        """
        trades: List[AMMTrade] = []
        param_types = {
            '$limit': 'Int!',
            '$offset': 'Int!',
            '$address': 'Bytes!',
            '$start_ts': 'BigInt!',
            '$end_ts': 'BigInt!',
        }
        param_values = {
            'limit': GRAPH_QUERY_LIMIT,
            'offset': 0,
            'address': address.lower(),
            'start_ts': str(start_ts),
            'end_ts': str(end_ts),
        }
        querystr = format_query_indentation(SWAPS_QUERY.format())

        while True:
            result = self.graph.query(  # type: ignore # caller already checks
                querystr=querystr,
                param_types=param_types,
                param_values=param_values,
            )
            result_data = result['swaps']
            for entry in result_data:
                swaps = []
                for swap in entry['transaction']['swaps']:
                    timestamp = swap['timestamp']
                    swap_token0 = swap['pair']['token0']
                    swap_token1 = swap['pair']['token1']
                    token0 = get_ethereum_token(
                        symbol=swap_token0['symbol'],
                        ethereum_address=to_checksum_address(swap_token0['id']),
                        name=swap_token0['name'],
                        decimals=swap_token0['decimals'],
                    )
                    token1 = get_ethereum_token(
                        symbol=swap_token1['symbol'],
                        ethereum_address=to_checksum_address(swap_token1['id']),
                        name=swap_token1['name'],
                        decimals=int(swap_token1['decimals']),
                    )
                    amount0_in = FVal(swap['amount0In'])
                    amount1_in = FVal(swap['amount1In'])
                    amount0_out = FVal(swap['amount0Out'])
                    amount1_out = FVal(swap['amount1Out'])
                    swaps.append(AMMSwap(
                        tx_hash=swap['id'].split('-')[0],
                        log_index=int(swap['logIndex']),
                        address=address,
                        from_address=to_checksum_address(swap['sender']),
                        to_address=to_checksum_address(swap['to']),
                        timestamp=Timestamp(int(timestamp)),
                        location=Location.UNISWAP,
                        token0=token0,
                        token1=token1,
                        amount0_in=AssetAmount(amount0_in),
                        amount1_in=AssetAmount(amount1_in),
                        amount0_out=AssetAmount(amount0_out),
                        amount1_out=AssetAmount(amount1_out),
                    ))

                # Now that we got all swaps for a transaction, create the trade object
                trades.extend(self._tx_swaps_to_trades(swaps))

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

            # Update pagination step
            param_values = {
                **param_values,
                'offset': param_values['offset'] + GRAPH_QUERY_LIMIT,  # type: ignore
            }
        return trades
Esempio n. 5
0
def test_query_trades_including_ammswaps(data_dir, username):
    """Test that querying trades succesfully queries from both the trades and ammswaps"""
    msg_aggregator = MessagesAggregator()
    data = DataHandler(data_dir, msg_aggregator)
    data.unlock(username, '123', create_new=True)

    trades = [
        Trade(
            timestamp=1,
            location=Location.EXTERNAL,
            base_asset=A_ETH,
            quote_asset=A_USDC,
            trade_type=TradeType.BUY,
            amount=FVal(1),
            rate=Price(FVal(1.5)),
            fee=None,
            fee_currency=None,
            link='',
            notes=None,
        ), Trade(
            timestamp=2,
            location=Location.KRAKEN,
            base_asset=A_BTC,
            quote_asset=A_EUR,
            trade_type=TradeType.SELL,
            amount=FVal(1),
            rate=Price(FVal(1.5)),
            fee=None,
            fee_currency=None,
            link='',
            notes=None,
        ), Trade(
            timestamp=3,
            location=Location.POLONIEX,
            base_asset=A_ETH,
            quote_asset=A_EUR,
            trade_type=TradeType.BUY,
            amount=FVal(2),
            rate=Price(FVal(1.5)),
            fee=None,
            fee_currency=None,
            link='',
            notes=None,
        ),
    ]
    swaps = [
        AMMSwap(
            tx_hash='0x1',
            log_index=1,
            address='0xfoo',
            from_address='0xfrom',
            to_address='0xto',
            timestamp=2,
            location=Location.UNISWAP,
            token0=A_DAI,
            token1=A_USDC,
            amount0_in=FVal(1),
            amount1_in=ZERO,
            amount0_out=ZERO,
            amount1_out=FVal(2),
        ), AMMSwap(
            tx_hash='0x2',
            log_index=2,
            address='0xfoo',
            from_address='0xfrom',
            to_address='0xto',
            timestamp=3,
            location=Location.BALANCER,
            token0=A_UNI,
            token1=A_USDC,
            amount0_in=FVal(1),
            amount1_in=ZERO,
            amount0_out=ZERO,
            amount1_out=FVal(1.5),
        ), AMMSwap(
            tx_hash='0x2',
            log_index=3,
            address='0xfoo',
            from_address='0xfrom',
            to_address='0xto',
            timestamp=3,
            location=Location.BALANCER,
            token0=A_USDC,
            token1=A_DAI,
            amount0_in=FVal(1.5),
            amount1_in=ZERO,
            amount0_out=ZERO,
            amount1_out=FVal(3.5),
        ), AMMSwap(
            tx_hash='0x3',
            log_index=1,
            address='0xfoo',
            from_address='0xfrom',
            to_address='0xto',
            timestamp=4,
            location=Location.SUSHISWAP,
            token0=A_UNI,
            token1=A_DAI,
            amount0_in=ZERO,
            amount1_in=FVal(2),
            amount0_out=FVal(5),
            amount1_out=ZERO,
        ), AMMSwap(
            tx_hash='0x3',
            log_index=2,
            address='0xfoo',
            from_address='0xfrom',
            to_address='0xto',
            timestamp=4,
            location=Location.SUSHISWAP,
            token0=A_DAI,
            token1=A_USDC,
            amount0_in=FVal(5),
            amount1_in=ZERO,
            amount0_out=ZERO,
            amount1_out=FVal(4.95),
        ), AMMSwap(
            tx_hash='0x3',
            log_index=3,
            address='0xfoo',
            from_address='0xfrom',
            to_address='0xto',
            timestamp=4,
            location=Location.SUSHISWAP,
            token0=A_GNO,
            token1=A_USDC,
            amount0_in=ZERO,
            amount1_in=FVal(4.95),
            amount0_out=FVal(8.2),
            amount1_out=ZERO,
        ), AMMSwap(
            tx_hash='0x4',
            log_index=5,
            address='0xfoo',
            from_address='0xfrom',
            to_address='0xto',
            timestamp=11,
            location=Location.UNISWAP,
            token0=A_GNO,
            token1=A_USDC,
            amount0_in=FVal(5),
            amount1_in=FVal(6),
            amount0_out=ZERO,
            amount1_out=FVal(4.95),
        ), AMMSwap(
            tx_hash='0x4',
            log_index=10,
            address='0xfoo',
            from_address='0xfrom',
            to_address='0xto',
            timestamp=11,
            location=Location.UNISWAP,
            token0=A_USDC,
            token1=A_UNI,
            amount0_in=FVal(4.95),
            amount1_in=ZERO,
            amount0_out=ZERO,
            amount1_out=FVal(5.4),
        ), AMMSwap(
            tx_hash='0x5',
            log_index=1,
            address='0xfoo',
            from_address='0xfrom',
            to_address='0xto',
            timestamp=14,
            location=Location.SUSHISWAP,
            token0=A_UNI,
            token1=A_USDC,
            amount0_in=FVal(4.5),
            amount1_in=FVal(6),
            amount0_out=ZERO,
            amount1_out=FVal(3.2),
        ), AMMSwap(
            tx_hash='0x5',
            log_index=3,
            address='0xfoo',
            from_address='0xfrom',
            to_address='0xto',
            timestamp=14,
            location=Location.SUSHISWAP,
            token0=A_USDC,
            token1=A_GNO,
            amount0_in=FVal(5.55),
            amount1_in=ZERO,
            amount0_out=FVal(2.15),
            amount1_out=FVal(5.4),
        ),
    ]
    data.db.add_trades(trades)
    data.db.add_amm_swaps(swaps)
    swap1_trade = Trade(
        timestamp=swaps[0].timestamp,
        location=swaps[0].location,
        base_asset=swaps[0].token1,
        quote_asset=swaps[0].token0,
        trade_type=TradeType.BUY,
        amount=swaps[0].amount1_out,
        rate=swaps[0].amount0_in / swaps[0].amount1_out,
        fee=None,
        fee_currency=None,
        link=swaps[0].tx_hash,
        notes=None,
    )
    swap2_trade = Trade(
        timestamp=swaps[1].timestamp,
        location=swaps[1].location,
        base_asset=swaps[2].token1,
        quote_asset=swaps[1].token0,
        trade_type=TradeType.BUY,
        amount=swaps[2].amount1_out,
        rate=swaps[1].amount0_in / swaps[2].amount1_out,
        fee=None,
        fee_currency=None,
        link=swaps[1].tx_hash,
        notes=None,
    )
    swap3_trade = Trade(
        timestamp=swaps[3].timestamp,
        location=swaps[3].location,
        base_asset=swaps[5].token0,
        quote_asset=swaps[3].token1,
        trade_type=TradeType.BUY,
        amount=swaps[5].amount0_out,
        rate=swaps[3].amount1_in / swaps[5].amount0_out,
        fee=None,
        fee_currency=None,
        link=swaps[3].tx_hash,
        notes=None,
    )
    swap4_trade1 = Trade(
        timestamp=swaps[6].timestamp,
        location=swaps[6].location,
        base_asset=swaps[7].token1,
        quote_asset=swaps[6].token0,
        trade_type=TradeType.BUY,
        amount=swaps[7].amount1_out / 2,
        rate=swaps[6].amount0_in / (swaps[7].amount1_out / 2),
        fee=None,
        fee_currency=None,
        link=swaps[6].tx_hash,
        notes=None,
    )
    swap4_trade2 = Trade(
        timestamp=swaps[6].timestamp,
        location=swaps[6].location,
        base_asset=swaps[7].token1,
        quote_asset=swaps[6].token1,
        trade_type=TradeType.BUY,
        amount=swaps[7].amount1_out / 2,
        rate=swaps[6].amount1_in / (swaps[7].amount1_out / 2),
        fee=None,
        fee_currency=None,
        link=swaps[6].tx_hash,
        notes=None,
    )
    swap5_trade1 = Trade(
        timestamp=swaps[8].timestamp,
        location=swaps[8].location,
        base_asset=swaps[9].token1,
        quote_asset=swaps[8].token1,
        trade_type=TradeType.BUY,
        amount=swaps[9].amount1_out,
        rate=swaps[8].amount1_in / swaps[9].amount1_out,
        fee=None,
        fee_currency=None,
        link=swaps[8].tx_hash,
        notes=None,
    )
    swap5_trade2 = Trade(
        timestamp=swaps[8].timestamp,
        location=swaps[8].location,
        base_asset=swaps[9].token0,
        quote_asset=swaps[8].token0,
        trade_type=TradeType.BUY,
        amount=swaps[9].amount0_out,
        rate=swaps[8].amount0_in / swaps[9].amount0_out,
        fee=None,
        fee_currency=None,
        link=swaps[8].tx_hash,
        notes=None,
    )

    # Get all trades
    returned_trades = data.db.get_trades(filter_query=TradesFilterQuery.make(), has_premium=True)
    assert len(returned_trades) == 10
    assert returned_trades[0] == trades[0]
    assert returned_trades[2] == trades[1]
    assert returned_trades[4] == trades[2]
    assert_trades_equal(returned_trades[1], swap1_trade)
    assert_trades_equal(returned_trades[3], swap2_trade)
    assert_trades_equal(returned_trades[5], swap3_trade)
    assert_trades_equal(returned_trades[6], swap4_trade2)
    assert_trades_equal(returned_trades[7], swap4_trade1)
    assert_trades_equal(returned_trades[8], swap5_trade1)
    assert_trades_equal(returned_trades[9], swap5_trade2)

    # Get last 5 trades
    returned_trades = data.db.get_trades(
        filter_query=TradesFilterQuery.make(limit=5, offset=5),
        has_premium=True,
    )
    assert len(returned_trades) == 5
    assert_trades_equal(returned_trades[0], swap3_trade)
    assert_trades_equal(returned_trades[1], swap4_trade2)
    assert_trades_equal(returned_trades[2], swap4_trade1)
    assert_trades_equal(returned_trades[3], swap5_trade1)
    assert_trades_equal(returned_trades[4], swap5_trade2)

    # Get first 5 trades that are in uniswap and that buy USDC
    returned_trades = data.db.get_trades(
        filter_query=TradesFilterQuery.make(
            limit=5, offset=0, location=Location.UNISWAP, base_asset=A_USDC,
        ), has_premium=True,
    )
    assert len(returned_trades) == 1
    assert_trades_equal(returned_trades[0], swap1_trade)

    # Get all trades with quote asset USDC
    returned_trades = data.db.get_trades(
        filter_query=TradesFilterQuery.make(quote_asset=A_USDC),
        has_premium=True,
    )
    assert len(returned_trades) == 3
    assert_trades_equal(returned_trades[0], trades[0])
    assert_trades_equal(returned_trades[1], swap4_trade2)
    assert_trades_equal(returned_trades[2], swap5_trade1)

    # Get all trades as non premium user with 2 free trades as limit
    limit_patch = patch(
        target='rotkehlchen.db.dbhandler.FREE_TRADES_LIMIT',
        new=2,
    )
    with limit_patch:
        returned_trades, total_found = data.db.get_trades_and_limit_info(
            filter_query=TradesFilterQuery.make(),
            has_premium=False,
        )
    # trades should be the latest 2
    assert total_found == 3  # the 3 normal trades -- free users don't see swaps
    assert len(returned_trades) == 2
    assert_trades_equal(returned_trades[0], trades[1])
    assert_trades_equal(returned_trades[1], trades[2])

    # Get filtered trades as non premium user with 2 free trades as limit
    limit_patch = patch(
        target='rotkehlchen.db.dbhandler.FREE_TRADES_LIMIT',
        new=2,
    )
    with limit_patch:
        returned_trades, total_found = data.db.get_trades_and_limit_info(
            filter_query=TradesFilterQuery.make(from_ts=1, to_ts=2),
            has_premium=False,
        )
    # trades should be the second one since the free limit includes the last 2 only
    assert total_found == 2, 'total found for filter should be 2'
    assert len(returned_trades) == 1
    assert_trades_equal(returned_trades[0], trades[1])
Esempio n. 6
0
def deserialize_swap(raw_swap: Dict[str, Any]) -> AMMSwap:
    """May raise DeserializationError"""
    try:
        tx_hash, log_index = deserialize_transaction_id(raw_swap['id'])
        timestamp = deserialize_timestamp(raw_swap['timestamp'])
        raw_tokens = raw_swap['poolAddress']['tokens']
        token0_symbol = raw_swap['tokenInSym']
        token1_symbol = raw_swap['tokenOutSym']
        amount0_in = deserialize_asset_amount(raw_swap['tokenAmountIn'])
        amount1_out = deserialize_asset_amount(raw_swap['tokenAmountOut'])
        raw_user_address = raw_swap['userAddress']['id']  # address
        raw_caller_address = raw_swap['caller']  # from_address
        raw_pool_address = raw_swap['poolAddress']['id']  # to_address
        raw_token_in_address = raw_swap['tokenIn']  # token0_address
        raw_token_out_address = raw_swap['tokenOut']  # token1_address
    except KeyError as e:
        raise DeserializationError(f'Missing key: {str(e)}.') from e

    if amount0_in == ZERO:
        # Prevent a division by zero error when creating the trade
        raise DeserializationError('TokenAmountIn balance is zero.')

    # Checksum addresses
    try:
        user_address = to_checksum_address(raw_user_address)
    except ValueError as e:
        raise DeserializationError(
            f'Invalid ethereum address: {raw_user_address} in userAddress.id.',
        ) from e

    try:
        caller_address = to_checksum_address(raw_caller_address)
    except ValueError as e:
        raise DeserializationError(
            f'Invalid ethereum address: {raw_caller_address} in caller.',
        ) from e

    try:
        pool_address = to_checksum_address(raw_pool_address)
    except ValueError as e:
        raise DeserializationError(
            f'Invalid ethereum address: {raw_pool_address} in poolAddress.id.',
        ) from e

    try:
        token_in_address = to_checksum_address(raw_token_in_address)
    except ValueError as e:
        raise DeserializationError(
            f'Invalid ethereum address: {raw_token_in_address} in tokenIn.',
        ) from e

    try:
        token_out_address = to_checksum_address(raw_token_out_address)
    except ValueError as e:
        raise DeserializationError(
            f'Invalid ethereum address: {raw_token_out_address} in tokenOut.',
        ) from e

    # Get token0 and token1
    # When the controller removes all the tokens from a pool, `raw_tokens` will
    # be an empty list. Therefore it won't be possible to get their names and
    # decimals. In case of having to instantiate an UnknownEthereumToken both
    # params will be None.
    if len(raw_tokens) != 0:
        try:
            raw_address_tokens = {
                raw_token['address']: raw_token
                for raw_token in raw_tokens
            }
            raw_token0 = raw_address_tokens[raw_token_in_address]
            raw_token1 = raw_address_tokens[raw_token_out_address]
            token0_name = raw_token0['name']
            token0_decimals = raw_token0['decimals']
            token1_name = raw_token1['name']
            token1_decimals = raw_token1['decimals']
        except KeyError as e:
            raise DeserializationError(f'Missing key: {str(e)}.') from e
    else:
        token0_name = None
        token0_decimals = None
        token1_name = None
        token1_decimals = None

    token0 = get_ethereum_token(
        symbol=token0_symbol,
        ethereum_address=token_in_address,
        name=token0_name,
        decimals=token0_decimals,
    )
    token1 = get_ethereum_token(
        symbol=token1_symbol,
        ethereum_address=token_out_address,
        name=token1_name,
        decimals=token1_decimals,
    )
    amm_swap = AMMSwap(
        tx_hash=tx_hash,
        log_index=log_index,
        address=user_address,
        from_address=caller_address,
        to_address=pool_address,
        timestamp=timestamp,
        location=Location.BALANCER,
        token0=token0,
        token1=token1,
        amount0_in=amount0_in,
        amount1_in=AssetAmount(ZERO),
        amount0_out=AssetAmount(ZERO),
        amount1_out=amount1_out,
    )
    return amm_swap
Esempio n. 7
0
def test_measure_trades_api_query(rotkehlchen_api_server,
                                  start_with_valid_premium):
    """Measures the response time of the combined trades view API query.
    This is required since it's quite a complicated query and takes a lot of time to process
    so we can use this test to measure any potential optimizations.
    """
    trades = [
        Trade(
            timestamp=x,
            location=Location.EXTERNAL,
            base_asset=A_WETH,
            quote_asset=A_EUR,
            trade_type=TradeType.BUY,
            amount=AssetAmount(FVal('1')),
            rate=Price(FVal('320')),
            fee=Fee(ZERO),
            fee_currency=A_EUR,
            link='',
            notes='',
        ) for x in range(1, 10000)
    ]
    rotki = rotkehlchen_api_server.rest_api.rotkehlchen
    rotki.data.db.add_trades(trades)
    swaps = [
        AMMSwap(
            tx_hash='0x' + str(x),
            log_index=x + i,
            address='0xfoo',
            from_address='0xfrom',
            to_address='0xto',
            timestamp=11 + x,
            location=Location.UNISWAP,
            token0=A_WETH,
            token1=A_EUR,
            amount0_in=FVal(5),
            amount1_in=ZERO,
            amount0_out=ZERO,
            amount1_out=FVal(4.95),
        ) for x in range(2000) for i in range(2)
    ]
    rotki.data.db.add_amm_swaps(swaps)

    start = time.time()
    requests.get(
        api_url_for(
            rotkehlchen_api_server,
            'tradesresource',
        ),
        json={'only_cache': True},
    )
    end = time.time()
    test_warnings.warn(
        UserWarning(
            f'Premium: {start_with_valid_premium}. Full Query Time: {end - start}',
        ))
    start = time.time()
    requests.get(
        api_url_for(
            rotkehlchen_api_server,
            'tradesresource',
        ),
        json={
            'only_cache': True,
            'offset': 200,
            'limit': 10
        },
    )
    end = time.time()
    test_warnings.warn(
        UserWarning(
            f'Premium: {start_with_valid_premium}. First Page Query Time: {end - start}',
        ))
    start = time.time()
    requests.get(
        api_url_for(
            rotkehlchen_api_server,
            'tradesresource',
        ),
        json={
            'only_cache': True,
            'offset': 210,
            'limit': 10
        },
    )
    end = time.time()
    test_warnings.warn(
        UserWarning(
            f'Premium: {start_with_valid_premium}. Second Page Query Time: {end - start}',
        ))
    start = time.time()
    requests.get(
        api_url_for(
            rotkehlchen_api_server,
            'tradesresource',
        ),
        json={
            'only_cache': True,
            'offset': 220,
            'limit': 10
        },
    )
    end = time.time()
    test_warnings.warn(
        UserWarning(
            f'Premium: {start_with_valid_premium}. Third Page Query Time: {end - start}',
        ))
Esempio n. 8
0
    def _read_subgraph_trades(
        self,
        address: ChecksumEthAddress,
        start_ts: Timestamp,
        end_ts: Timestamp,
    ) -> List[AMMTrade]:
        """Get the address' trades data querying the AMM subgraph

        Each trade (swap) instantiates an <AMMTrade>.

        The trade pair (i.e. BASE_QUOTE) is determined by `reserve0_reserve1`.
        Translated to AMM lingo:

        Trade type BUY:
        - `asset1In` (QUOTE, reserve1) is gt 0.
        - `asset0Out` (BASE, reserve0) is gt 0.

        Trade type SELL:
        - `asset0In` (BASE, reserve0) is gt 0.
        - `asset1Out` (QUOTE, reserve1) is gt 0.

        May raise
        - RemoteError
        """
        trades: List[AMMTrade] = []
        query_id = '0'
        query_offset = 0
        param_types = {
            '$limit': 'Int!',
            '$offset': 'Int!',
            '$address': 'Bytes!',
            '$start_ts': 'BigInt!',
            '$end_ts': 'BigInt!',
            '$id': 'ID!',
        }
        param_values = {
            'limit': GRAPH_QUERY_LIMIT,
            'offset': 0,
            'address': address.lower(),
            'start_ts': str(start_ts),
            'end_ts': str(end_ts),
            'id': query_id,
        }
        querystr = format_query_indentation(self.swaps_query.format())

        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

            for entry in result['swaps']:
                swaps = []
                try:
                    for swap in entry['transaction']['swaps']:
                        timestamp = swap['timestamp']
                        swap_token0 = swap['pair']['token0']
                        swap_token1 = swap['pair']['token1']

                        try:
                            token0_deserialized = deserialize_ethereum_address(
                                swap_token0['id'])
                            token1_deserialized = deserialize_ethereum_address(
                                swap_token1['id'])
                            from_address_deserialized = deserialize_ethereum_address(
                                swap['sender'])  # noqa
                            to_address_deserialized = deserialize_ethereum_address(
                                swap['to'])
                        except DeserializationError:
                            msg = (
                                f'Failed to deserialize addresses in trade from {self.location} graph'  # noqa
                                f' with token 0: {swap_token0["id"]}, token 1: {swap_token1["id"]}, '  # noqa
                                f'swap sender: {swap["sender"]}, swap receiver {swap["to"]}'
                            )
                            log.error(msg)
                            continue

                        token0 = get_or_create_ethereum_token(
                            userdb=self.database,
                            symbol=swap_token0['symbol'],
                            ethereum_address=token0_deserialized,
                            name=swap_token0['name'],
                            decimals=swap_token0['decimals'],
                        )
                        token1 = get_or_create_ethereum_token(
                            userdb=self.database,
                            symbol=swap_token1['symbol'],
                            ethereum_address=token1_deserialized,
                            name=swap_token1['name'],
                            decimals=int(swap_token1['decimals']),
                        )

                        try:
                            amount0_in = FVal(swap['amount0In'])
                            amount1_in = FVal(swap['amount1In'])
                            amount0_out = FVal(swap['amount0Out'])
                            amount1_out = FVal(swap['amount1Out'])
                        except ValueError as e:
                            log.error(
                                f'Failed to read amounts in {self.location} swap {str(swap)}. '
                                f'{str(e)}.', )
                            continue

                        swaps.append(
                            AMMSwap(
                                tx_hash=swap['id'].split('-')[0],
                                log_index=int(swap['logIndex']),
                                address=address,
                                from_address=from_address_deserialized,
                                to_address=to_address_deserialized,
                                timestamp=Timestamp(int(timestamp)),
                                location=self.location,
                                token0=token0,
                                token1=token1,
                                amount0_in=AssetAmount(amount0_in),
                                amount1_in=AssetAmount(amount1_in),
                                amount0_out=AssetAmount(amount0_out),
                                amount1_out=AssetAmount(amount1_out),
                            ))
                    query_id = entry['id']
                except KeyError as e:
                    log.error(
                        f'Failed to read trade in {self.location} swap {str(entry)}. '
                        f'{str(e)}.', )
                    continue

                # with the new logic the list of swaps can be empty, in that case don't try
                # to make trades from the swaps
                if len(swaps) == 0:
                    continue

                # Now that we got all swaps for a transaction, create the trade object
                trades.extend(self._tx_swaps_to_trades(swaps))

            # Check whether an extra request is needed
            if len(result['swaps']) < 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,
            }

        return trades
Esempio n. 9
0
def get_expected_trades():
    """Function so no price (unknown) assets can be resolved only when existing in the DB"""
    address = string_to_ethereum_address('0x63BC843b9640c4D79d6aE0105bc39F773172d121')
    return [AMMTrade(
        trade_type=TradeType.BUY,
        base_asset=EthereumToken('0xdAC17F958D2ee523a2206206994597C13D831ec7'),
        quote_asset=EthereumToken('0x4b4D2e899658FB59b1D518b68fe836B100ee8958'),
        amount=AssetAmount(FVal('796.857811')),
        rate=Price(FVal('0.0008323741932057006980885326353')),
        trade_index=0,
        swaps=[AMMSwap(
            tx_hash='0x962d904d75c751fbff316f7a2ed280bd93241d5088d747a4f26fe7437813512f',
            log_index=141,
            address=address,
            from_address=string_to_ethereum_address('0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F'),
            to_address=string_to_ethereum_address('0x63BC843b9640c4D79d6aE0105bc39F773172d121'),
            timestamp=Timestamp(1609308616),
            location=Location.SUSHISWAP,
            token0=EthereumToken('0x4b4D2e899658FB59b1D518b68fe836B100ee8958'),
            token1=EthereumToken('0xdAC17F958D2ee523a2206206994597C13D831ec7'),
            amount0_in=AssetAmount(FVal('0.663283877530785731')),
            amount1_in=AssetAmount(ZERO),
            amount0_out=AssetAmount(ZERO),
            amount1_out=AssetAmount(FVal('796.857811')),
        )],
    ), AMMTrade(
        trade_type=TradeType.BUY,
        base_asset=EthereumToken('0x368B3a58B5f49392e5C9E4C998cb0bB966752E51'),
        quote_asset=EthereumToken('0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'),
        amount=AssetAmount(FVal('2.223721994593087248')),
        rate=Price(FVal('1124.241252314216598775470692')),
        trade_index=0,
        swaps=[AMMSwap(
            tx_hash='0x90f68af0ebbbb8d4938a4fbd07a70862e806124abd907d1225f25a10afda0180',
            log_index=26,
            address=address,
            from_address=string_to_ethereum_address('0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F'),
            to_address=string_to_ethereum_address('0x06da0fd433C1A5d7a4faa01111c044910A184553'),
            timestamp=Timestamp(1609303966),
            location=Location.SUSHISWAP,
            token0=EthereumToken('0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'),
            token1=EthereumToken('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'),
            amount0_in=AssetAmount(FVal('2500')),
            amount1_in=AssetAmount(ZERO),
            amount0_out=AssetAmount(ZERO),
            amount1_out=AssetAmount(FVal('3.410623314913014194')),
        ), AMMSwap(
            tx_hash='0x90f68af0ebbbb8d4938a4fbd07a70862e806124abd907d1225f25a10afda0180',
            log_index=29,
            address=address,
            from_address=string_to_ethereum_address('0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F'),
            to_address=string_to_ethereum_address('0xC9cB53B48A2f3A9e75982685644c1870F1405CCb'),
            timestamp=Timestamp(1609303966),
            location=Location.SUSHISWAP,
            token0=EthereumToken('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'),
            token1=EthereumToken('0xdAC17F958D2ee523a2206206994597C13D831ec7'),
            amount0_in=AssetAmount(FVal('3.410623314913014194')),
            amount1_in=AssetAmount(ZERO),
            amount0_out=AssetAmount(ZERO),
            amount1_out=AssetAmount(FVal('2474.601464')),
        ), AMMSwap(
            tx_hash='0x90f68af0ebbbb8d4938a4fbd07a70862e806124abd907d1225f25a10afda0180',
            log_index=32,
            address=address,
            from_address=string_to_ethereum_address('0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F'),
            to_address=string_to_ethereum_address('0x63BC843b9640c4D79d6aE0105bc39F773172d121'),
            timestamp=Timestamp(1609303966),
            location=Location.SUSHISWAP,
            token0=EthereumToken('0x368B3a58B5f49392e5C9E4C998cb0bB966752E51'),
            token1=EthereumToken('0xdAC17F958D2ee523a2206206994597C13D831ec7'),
            amount0_in=AssetAmount(ZERO),
            amount1_in=AssetAmount(FVal('2474.601464')),
            amount0_out=AssetAmount(FVal('2.223721994593087248')),
            amount1_out=AssetAmount(ZERO),
        )],
    ), AMMTrade(
        trade_type=TradeType.BUY,
        base_asset=EthereumToken('0x368B3a58B5f49392e5C9E4C998cb0bB966752E51'),
        quote_asset=EthereumToken('0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'),
        amount=AssetAmount(FVal('1.925851508322134245')),
        rate=Price(FVal('1012.539124420295894184748806')),
        trade_index=0,
        swaps=[AMMSwap(
            tx_hash='0xa54bf4c68d435e3c8f432fd7e62b7f8aca497a831a3d3fca305a954484ddd7b2',
            log_index=205,
            address=address,
            from_address=string_to_ethereum_address('0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F'),
            to_address=string_to_ethereum_address('0x06da0fd433C1A5d7a4faa01111c044910A184553'),
            timestamp=Timestamp(1609301469),
            location=Location.SUSHISWAP,
            token0=EthereumToken('0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'),
            token1=EthereumToken('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'),
            amount0_in=AssetAmount(FVal('1950')),
            amount1_in=AssetAmount(ZERO),
            amount0_out=AssetAmount(ZERO),
            amount1_out=AssetAmount(FVal('2.6455727132446468')),
        ), AMMSwap(
            tx_hash='0xa54bf4c68d435e3c8f432fd7e62b7f8aca497a831a3d3fca305a954484ddd7b2',
            log_index=208,
            address=address,
            from_address=string_to_ethereum_address('0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F'),
            to_address=string_to_ethereum_address('0xC9cB53B48A2f3A9e75982685644c1870F1405CCb'),
            timestamp=Timestamp(1609301469),
            location=Location.SUSHISWAP,
            token0=EthereumToken('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'),
            token1=EthereumToken('0xdAC17F958D2ee523a2206206994597C13D831ec7'),
            amount0_in=AssetAmount(FVal('2.6455727132446468')),
            amount1_in=AssetAmount(ZERO),
            amount0_out=AssetAmount(ZERO),
            amount1_out=AssetAmount(FVal('1936.810111')),
        ), AMMSwap(
            tx_hash='0xa54bf4c68d435e3c8f432fd7e62b7f8aca497a831a3d3fca305a954484ddd7b2',
            log_index=211,
            address=address,
            from_address=string_to_ethereum_address('0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F'),
            to_address=string_to_ethereum_address('0x63BC843b9640c4D79d6aE0105bc39F773172d121'),
            timestamp=Timestamp(1609301469),
            location=Location.SUSHISWAP,
            token0=EthereumToken('0x368B3a58B5f49392e5C9E4C998cb0bB966752E51'),
            token1=EthereumToken('0xdAC17F958D2ee523a2206206994597C13D831ec7'),
            amount0_in=AssetAmount(ZERO),
            amount1_in=AssetAmount(FVal('1936.810111')),
            amount0_out=AssetAmount(FVal('1.925851508322134245')),
            amount1_out=AssetAmount(ZERO),
        )],
    ), AMMTrade(
        trade_type=TradeType.BUY,
        base_asset=EthereumToken('0xdAC17F958D2ee523a2206206994597C13D831ec7'),
        quote_asset=EthereumToken('0x368B3a58B5f49392e5C9E4C998cb0bB966752E51'),
        amount=AssetAmount(FVal('951.611472')),
        rate=Price(FVal('0.001050849038104071952592013309')),
        trade_index=0,
        swaps=[AMMSwap(
            tx_hash='0xb3cea8179e8bc349661f265937187403ae4914c108d118889d026bac1fbec4e9',
            log_index=9,
            address=address,
            from_address=string_to_ethereum_address('0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F'),
            to_address=string_to_ethereum_address('0x63BC843b9640c4D79d6aE0105bc39F773172d121'),
            timestamp=Timestamp(1609296759),
            location=Location.SUSHISWAP,
            token0=EthereumToken('0x368B3a58B5f49392e5C9E4C998cb0bB966752E51'),
            token1=EthereumToken('0xdAC17F958D2ee523a2206206994597C13D831ec7'),
            amount0_in=AssetAmount(FVal('1')),
            amount1_in=AssetAmount(ZERO),
            amount0_out=AssetAmount(ZERO),
            amount1_out=AssetAmount(FVal('951.611472')),
        )],
    ), AMMTrade(
        trade_type=TradeType.BUY,
        base_asset=EthereumToken('0x368B3a58B5f49392e5C9E4C998cb0bB966752E51'),
        quote_asset=EthereumToken('0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'),
        amount=AssetAmount(FVal('1')),
        rate=Price(FVal('511.20342')),
        trade_index=0,
        swaps=[AMMSwap(
            tx_hash='0x0ccec8fd15c8d3ab923ee4a2406778f22769e74da20b19a35e614bfead6bab8d',
            log_index=242,
            address=address,
            from_address=string_to_ethereum_address('0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F'),
            to_address=string_to_ethereum_address('0x06da0fd433C1A5d7a4faa01111c044910A184553'),
            timestamp=Timestamp(1609294923),
            location=Location.SUSHISWAP,
            token0=EthereumToken('0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'),
            token1=EthereumToken('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'),
            amount0_in=AssetAmount(FVal('511.20342')),
            amount1_in=AssetAmount(ZERO),
            amount0_out=AssetAmount(ZERO),
            amount1_out=AssetAmount(FVal('0.690577933591789501')),
        ), AMMSwap(
            tx_hash='0x0ccec8fd15c8d3ab923ee4a2406778f22769e74da20b19a35e614bfead6bab8d',
            log_index=245,
            address=address,
            from_address=string_to_ethereum_address('0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F'),
            to_address=string_to_ethereum_address('0xC9cB53B48A2f3A9e75982685644c1870F1405CCb'),
            timestamp=Timestamp(1609294923),
            location=Location.SUSHISWAP,
            token0=EthereumToken('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'),
            token1=EthereumToken('0xdAC17F958D2ee523a2206206994597C13D831ec7'),
            amount0_in=AssetAmount(FVal('0.690577933591789501')),
            amount1_in=AssetAmount(ZERO),
            amount0_out=AssetAmount(ZERO),
            amount1_out=AssetAmount(FVal('506.399839')),
        ), AMMSwap(
            tx_hash='0x0ccec8fd15c8d3ab923ee4a2406778f22769e74da20b19a35e614bfead6bab8d',
            log_index=248,
            address=address,
            from_address=string_to_ethereum_address('0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F'),
            to_address=string_to_ethereum_address('0x63BC843b9640c4D79d6aE0105bc39F773172d121'),
            timestamp=Timestamp(1609294923),
            location=Location.SUSHISWAP,
            token0=EthereumToken('0x368B3a58B5f49392e5C9E4C998cb0bB966752E51'),
            token1=EthereumToken('0xdAC17F958D2ee523a2206206994597C13D831ec7'),
            amount0_in=AssetAmount(ZERO),
            amount1_in=AssetAmount(FVal('506.399839')),
            amount0_out=AssetAmount(FVal('1')),
            amount1_out=AssetAmount(ZERO),
        )],
    )]
Esempio n. 10
0
def test_associated_locations(database):
    """Test that locations imported in different places are correctly stored in database"""
    # Add trades from different locations
    trades = [Trade(
        timestamp=Timestamp(1595833195),
        location=Location.CRYPTOCOM,
        base_asset=A_ETH,
        quote_asset=A_EUR,
        trade_type=TradeType.BUY,
        amount=AssetAmount(FVal('1.0')),
        rate=Price(FVal('281.14')),
        fee=Fee(ZERO),
        fee_currency=A_USD,
        link='',
        notes='',
    ), Trade(
        timestamp=Timestamp(1587825824),
        location=Location.CRYPTOCOM,
        base_asset=A_ETH,
        quote_asset=A_EUR,
        trade_type=TradeType.BUY,
        amount=AssetAmount(FVal('50.0')),
        rate=Price(FVal('3.521')),
        fee=Fee(ZERO),
        fee_currency=A_USD,
        link='',
        notes='',
    ), Trade(
        timestamp=Timestamp(1596014214),
        location=Location.BLOCKFI,
        base_asset=A_ETH,
        quote_asset=A_EUR,
        trade_type=TradeType.BUY,
        amount=AssetAmount(FVal('50.0')),
        rate=Price(FVal('3.521')),
        fee=Fee(ZERO),
        fee_currency=A_USD,
        link='',
        notes='',
    ), Trade(
        timestamp=Timestamp(1565888464),
        location=Location.NEXO,
        base_asset=A_ETH,
        quote_asset=A_EUR,
        trade_type=TradeType.BUY,
        amount=AssetAmount(FVal('50.0')),
        rate=Price(FVal('3.521')),
        fee=Fee(ZERO),
        fee_currency=A_USD,
        link='',
        notes='',
    ), Trade(
        timestamp=Timestamp(1596014214),
        location=Location.NEXO,
        base_asset=A_ETH,
        quote_asset=A_EUR,
        trade_type=TradeType.BUY,
        amount=AssetAmount(FVal('50.0')),
        rate=Price(FVal('3.521')),
        fee=Fee(ZERO),
        fee_currency=A_USD,
        link='',
        notes='',
    ), Trade(
        timestamp=Timestamp(1612051199),
        location=Location.BLOCKFI,
        base_asset=symbol_to_asset_or_token('USDC'),
        quote_asset=symbol_to_asset_or_token('LTC'),
        trade_type=TradeType.BUY,
        amount=AssetAmount(FVal('6404.6')),
        rate=Price(FVal('151.6283999982779809352223797')),
        fee=None,
        fee_currency=None,
        link='',
        notes='One Time',
    ), Trade(
        timestamp=Timestamp(1595833195),
        location=Location.POLONIEX,
        base_asset=A_ETH,
        quote_asset=A_EUR,
        trade_type=TradeType.BUY,
        amount=AssetAmount(FVal('1.0')),
        rate=Price(FVal('281.14')),
        fee=Fee(ZERO),
        fee_currency=A_USD,
        link='',
        notes='',
    ), Trade(
        timestamp=Timestamp(1596429934),
        location=Location.COINBASE,
        base_asset=A_ETH,
        quote_asset=A_EUR,
        trade_type=TradeType.BUY,
        amount=AssetAmount(FVal('0.00061475')),
        rate=Price(FVal('309.0687271248474989833265555')),
        fee=Fee(ZERO),
        fee_currency=A_USD,
        link='',
        notes='',
    ), Trade(
        timestamp=Timestamp(1596429934),
        location=Location.EXTERNAL,
        base_asset=A_ETH,
        quote_asset=A_EUR,
        trade_type=TradeType.BUY,
        amount=AssetAmount(FVal('1')),
        rate=Price(FVal('320')),
        fee=Fee(ZERO),
        fee_currency=A_USD,
        link='',
        notes='',
    )]

    # Add multiple entries for same exchange + connected exchange
    database.add_trades(trades)
    kraken_api_key1 = ApiKey('kraken_api_key')
    kraken_api_secret1 = ApiSecret(b'kraken_api_secret')
    kraken_api_key2 = ApiKey('kraken_api_key2')
    kraken_api_secret2 = ApiSecret(b'kraken_api_secret2')
    binance_api_key = ApiKey('binance_api_key')
    binance_api_secret = ApiSecret(b'binance_api_secret')

    # add mock kraken and binance
    database.add_exchange('kraken1', Location.KRAKEN, kraken_api_key1, kraken_api_secret1)
    database.add_exchange('kraken2', Location.KRAKEN, kraken_api_key2, kraken_api_secret2)
    database.add_exchange('binance', Location.BINANCE, binance_api_key, binance_api_secret)

    # Add uniswap and sushiswap events
    database.add_amm_events([
        LiquidityPoolEvent(
            tx_hash='0x47ea26957ce09e84a51b51dfdab6a4ac1c3672a372eef77b15ef7677174ac847',
            log_index=23,
            address=ChecksumEthAddress('0x3163Bb273E8D9960Ce003fD542bF26b4C529f515'),
            timestamp=Timestamp(1590011534),
            event_type=EventType.MINT_SUSHISWAP,
            pool_address=ChecksumEthAddress('0xa2107FA5B38d9bbd2C461D6EDf11B11A50F6b974'),
            token0=EthereumToken('0x514910771AF9Ca656af840dff83E8264EcF986CA'),
            token1=EthereumToken('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'),
            amount0=FVal('3.313676003468974932'),
            amount1=FVal('0.064189269269768657'),
            usd_price=FVal('26.94433946158740371839009166230438'),
            lp_amount=FVal('0.460858304063739927'),
        ),
    ])
    database.add_amm_swaps([
        AMMSwap(
            tx_hash='0xa54bf4c68d435e3c8f432fd7e62b7f8aca497a831a3d3fca305a954484ddd7b2',
            log_index=208,
            address=ChecksumEthAddress('0xa2107FA5B38d9bbd2C461D6EDf11B11A50F6b974'),
            from_address=string_to_ethereum_address('0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F'),
            to_address=string_to_ethereum_address('0xC9cB53B48A2f3A9e75982685644c1870F1405CCb'),
            timestamp=Timestamp(1609301469),
            location=Location.UNISWAP,
            token0=EthereumToken('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'),
            token1=EthereumToken('0xdAC17F958D2ee523a2206206994597C13D831ec7'),
            amount0_in=AssetAmount(FVal('2.6455727132446468')),
            amount1_in=AssetAmount(ZERO),
            amount0_out=AssetAmount(ZERO),
            amount1_out=AssetAmount(FVal('1936.810111')),
        ),
    ])
    database.add_balancer_events([
        BalancerEvent(
            tx_hash='0xa54bf4c68d435e3c8f432fd7e62b7f8aca497a831a3d3fca305a954484ddd7b3',
            log_index=23,
            address=ChecksumEthAddress('0xa2107FA5B38d9bbd2C461D6EDf11B11A50F6b974'),
            timestamp=Timestamp(1609301469),
            event_type=BalancerBPTEventType.MINT,
            pool_address_token=EthereumToken('0x514910771AF9Ca656af840dff83E8264EcF986CA'),
            lp_balance=Balance(amount=FVal(2), usd_value=FVal(3)),
            amounts=[
                AssetAmount(FVal(1)),
                AssetAmount(FVal(2)),
            ],
        ),
    ])
    expected_locations = {
        Location.KRAKEN,
        Location.BINANCE,
        Location.BLOCKFI,
        Location.NEXO,
        Location.CRYPTOCOM,
        Location.POLONIEX,
        Location.COINBASE,
        Location.EXTERNAL,
        Location.SUSHISWAP,
        Location.UNISWAP,
        Location.BALANCER,
    }

    assert set(database.get_associated_locations()) == expected_locations
Esempio n. 11
0
EXPECTED_TRADES = [AMMTrade(
    trade_type=TradeType.BUY,
    base_asset=EthereumToken('USDT'),
    quote_asset=EthereumToken('WETH'),
    amount=AssetAmount(FVal('20632.012923')),
    rate=Price(FVal('0.002665760253508154513092246380')),
    trade_index=0,
    swaps=[AMMSwap(
        tx_hash='0x13723c8b286ec56e95b00e091557e6a76f723d20a52503d2e08df5867d942b51',
        log_index=319,
        address=deserialize_ethereum_address('0x21d05071cA08593e13cd3aFD0b4869537e015C92'),
        from_address=deserialize_ethereum_address('0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D'),
        to_address=deserialize_ethereum_address('0x21d05071cA08593e13cd3aFD0b4869537e015C92'),
        timestamp=Timestamp(1603180607),
        location=Location.UNISWAP,
        token0=EthereumToken('WETH'),
        token1=EthereumToken('USDT'),
        amount0_in=AssetAmount(FVal(55)),
        amount1_in=AssetAmount(ZERO),
        amount0_out=AssetAmount(ZERO),
        amount1_out=AssetAmount(FVal('20632.012923')),
    )],
), AMMTrade(
    trade_type=TradeType.BUY,
    base_asset=EthereumToken('DAI'),
    quote_asset=EthereumToken('WETH'),
    amount=AssetAmount(FVal('1411.453463704718081611')),
    rate=Price(FVal('0.002692260210992670573731568435')),
    trade_index=0,
    swaps=[AMMSwap(
Esempio n. 12
0
def deserialize_swap(userdb: 'DBHandler', raw_swap: Dict[str, Any]) -> AMMSwap:
    """May raise DeserializationError"""
    try:
        tx_hash, log_index = deserialize_transaction_id(raw_swap['id'])
        timestamp = deserialize_timestamp(raw_swap['timestamp'])
        raw_tokens = raw_swap['poolAddress']['tokens']
        token0_symbol = raw_swap['tokenInSym']
        token1_symbol = raw_swap['tokenOutSym']
        amount0_in = deserialize_asset_amount(raw_swap['tokenAmountIn'])
        amount1_out = deserialize_asset_amount(raw_swap['tokenAmountOut'])
        raw_user_address = raw_swap['userAddress']['id']  # address
        raw_caller_address = raw_swap['caller']  # from_address
        raw_pool_address = raw_swap['poolAddress']['id']  # to_address
        raw_token_in_address = raw_swap['tokenIn']  # token0_address
        raw_token_out_address = raw_swap['tokenOut']  # token1_address
    except KeyError as e:
        raise DeserializationError(f'Missing key: {str(e)}.') from e

    if amount0_in == ZERO:
        # Prevent a division by zero error when creating the trade
        raise DeserializationError('TokenAmountIn balance is zero.')

    # Checksum addresses
    user_address = deserialize_ethereum_address(raw_user_address)
    caller_address = deserialize_ethereum_address(raw_caller_address)
    pool_address = deserialize_ethereum_address(raw_pool_address)
    token_in_address = deserialize_ethereum_address(raw_token_in_address)
    token_out_address = deserialize_ethereum_address(raw_token_out_address)

    # Get token0 and token1
    # When the controller removes all the tokens from a pool, `raw_tokens` will
    # be an empty list. Therefore it won't be possible to get their names and
    # decimals.
    if len(raw_tokens) != 0:
        try:
            raw_address_tokens = {raw_token['address']: raw_token for raw_token in raw_tokens}
            raw_token0 = raw_address_tokens.get(raw_token_in_address)
            raw_token1 = raw_address_tokens.get(raw_token_out_address)
            if raw_token0 is not None:
                token0_name = raw_token0['name']
                token0_decimals = raw_token0['decimals']
            else:
                token0_name = None
                token0_decimals = None
            if raw_token1 is not None:
                token1_name = raw_token1['name']
                token1_decimals = raw_token1['decimals']
            else:
                token1_name = None
                token1_decimals = None

        except KeyError as e:
            raise DeserializationError(f'Missing key: {str(e)}.') from e
    else:
        token0_name = None
        token0_decimals = None
        token1_name = None
        token1_decimals = None

    token0 = get_or_create_ethereum_token(
        userdb=userdb,
        symbol=token0_symbol,
        ethereum_address=token_in_address,
        name=token0_name,
        decimals=token0_decimals,
    )
    token1 = get_or_create_ethereum_token(
        userdb=userdb,
        symbol=token1_symbol,
        ethereum_address=token_out_address,
        name=token1_name,
        decimals=token1_decimals,
    )
    amm_swap = AMMSwap(
        tx_hash=tx_hash,
        log_index=log_index,
        address=user_address,
        from_address=caller_address,
        to_address=pool_address,
        timestamp=timestamp,
        location=Location.BALANCER,
        token0=token0,
        token1=token1,
        amount0_in=amount0_in,
        amount1_in=AssetAmount(ZERO),
        amount0_out=AssetAmount(ZERO),
        amount1_out=amount1_out,
    )
    return amm_swap
Esempio n. 13
0
    def _get_trades_graph_v3_for_address(
            self,
            address: ChecksumEthAddress,
            start_ts: Timestamp,
            end_ts: Timestamp,
    ) -> List[AMMTrade]:
        """Get the address' trades data querying the Uniswap subgraph

        Each trade (swap) instantiates an <AMMTrade>.

        The trade pair (i.e. BASE_QUOTE) is determined by `reserve0_reserve1`.
        Translated to Uniswap lingo:

        Trade type BUY:
        - `amount1` (QUOTE, reserve1) is gt 0.
        - `amount0` (BASE, reserve0) is lt 0.

        Trade type SELL:
        - `amount0` (BASE, reserve0) is gt 0.
        - `amount1` (QUOTE, reserve1) is lt 0.

        May raise:
        - RemoteError
        """
        trades: List[AMMTrade] = []
        param_types = {
            '$limit': 'Int!',
            '$offset': 'Int!',
            '$address': 'Bytes!',
            '$start_ts': 'BigInt!',
            '$end_ts': 'BigInt!',
        }
        param_values = {
            'limit': GRAPH_QUERY_LIMIT,
            'offset': 0,
            'address': address.lower(),
            'start_ts': str(start_ts),
            'end_ts': str(end_ts),
        }
        querystr = format_query_indentation(V3_SWAPS_QUERY.format())

        while True:
            try:
                result = self.graph_v3.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)))
                raise

            result_data = result['swaps']
            for entry in result_data:
                swaps = []
                for swap in entry['transaction']['swaps']:
                    timestamp = swap['timestamp']
                    swap_token0 = swap['token0']
                    swap_token1 = swap['token1']

                    try:
                        token0_deserialized = deserialize_ethereum_address(swap_token0['id'])
                        token1_deserialized = deserialize_ethereum_address(swap_token1['id'])
                        from_address_deserialized = deserialize_ethereum_address(swap['sender'])
                        to_address_deserialized = deserialize_ethereum_address(swap['recipient'])
                    except DeserializationError:
                        msg = (
                            f'Failed to deserialize addresses in trade from uniswap graph with '
                            f'token 0: {swap_token0["id"]}, token 1: {swap_token1["id"]}, '
                            f'swap sender: {swap["sender"]}, swap receiver {swap["to"]}'
                        )
                        log.error(msg)
                        continue

                    token0 = get_or_create_ethereum_token(
                        userdb=self.database,
                        symbol=swap_token0['symbol'],
                        ethereum_address=token0_deserialized,
                        name=swap_token0['name'],
                        decimals=swap_token0['decimals'],
                    )
                    token1 = get_or_create_ethereum_token(
                        userdb=self.database,
                        symbol=swap_token1['symbol'],
                        ethereum_address=token1_deserialized,
                        name=swap_token1['name'],
                        decimals=int(swap_token1['decimals']),
                    )

                    try:
                        if swap['amount0'].startswith('-'):
                            amount0_in = AssetAmount(FVal(ZERO))
                            amount0_out = deserialize_asset_amount_force_positive(swap['amount0'])
                            amount1_in = deserialize_asset_amount_force_positive(swap['amount1'])
                            amount1_out = AssetAmount(FVal(ZERO))
                        else:
                            amount0_in = deserialize_asset_amount_force_positive(swap['amount0'])
                            amount0_out = AssetAmount(FVal(ZERO))
                            amount1_in = AssetAmount(FVal(ZERO))
                            amount1_out = deserialize_asset_amount_force_positive(swap['amount1'])
                    except ValueError as e:
                        log.error(
                            f'Failed to read amounts in Uniswap V3 swap {str(swap)}. '
                            f'{str(e)}.',
                        )
                        continue

                    swaps.append(AMMSwap(
                        tx_hash=swap['id'].split('#')[0],
                        log_index=int(swap['logIndex']),
                        address=address,
                        from_address=from_address_deserialized,
                        to_address=to_address_deserialized,
                        timestamp=Timestamp(int(timestamp)),
                        location=Location.UNISWAP,
                        token0=token0,
                        token1=token1,
                        amount0_in=amount0_in,
                        amount1_in=amount1_in,
                        amount0_out=amount0_out,
                        amount1_out=amount1_out,
                    ))

                # with the new logic the list of swaps can be empty, in that case don't try
                # to make trades from the swaps
                if len(swaps) == 0:
                    continue

                # Now that we got all swaps for a transaction, create the trade object
                trades.extend(self._tx_swaps_to_trades(swaps))
            # Check whether an extra request is needed
            if len(result_data) < GRAPH_QUERY_LIMIT:
                break

            # Update pagination step
            param_values = {
                **param_values,
                'offset': param_values['offset'] + GRAPH_QUERY_LIMIT,  # type: ignore
            }
        return trades
Esempio n. 14
0
     quote_asset=A_AAVE,
     amount=AssetAmount(FVal('1.616934038985744521')),
     rate=Price(FVal('6.963972908793392530935439799')),
     trade_index=1,
     swaps=[
         AMMSwap(
             tx_hash=
             '0x3c457da9b541ae39a7dc781ab04a03938b98b5649512aec2a2d32635c9bbf589',  # noqa: E501
             log_index=24,
             address=string_to_ethereum_address(
                 '0x029f388aC4D5C8BfF490550ce0853221030E822b'
             ),  # noqa: E501
             from_address=string_to_ethereum_address(
                 '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56'
             ),  # noqa: E501
             to_address=string_to_ethereum_address(
                 '0x7c90a3cd7Ec80dd2F633ed562480AbbEEd3bE546'
             ),  # noqa: E501
             timestamp=Timestamp(1607008178),
             location=Location.BALANCER,
             token0=A_AAVE,
             token1=A_WETH,
             amount0_in=AssetAmount(FVal('11.260284842802604032')),
             amount1_in=AssetAmount(ZERO),
             amount0_out=AssetAmount(ZERO),
             amount1_out=AssetAmount(FVal('1.616934038985744521')),
         ),
     ],
 ),
 AMMTrade(
     trade_type=TradeType.BUY,
     base_asset=A_AAVE,