Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support balance pagination #2118

Merged
merged 7 commits into from
Jul 4, 2024
Merged

Support balance pagination #2118

merged 7 commits into from
Jul 4, 2024

Conversation

moisses89
Copy link
Member

@moisses89 moisses89 commented Jul 2, 2024

Description

Enable pagination to support balances with max_limit = 200 and default pagination 100.
This PR is not affecting the database query, because we are getting the token balance from the ERC20 transfers, it is just reducing the number of calls to the RPC node.

Close #1432

@moisses89 moisses89 marked this pull request as ready for review July 2, 2024 15:55
@moisses89 moisses89 requested a review from a team as a code owner July 2, 2024 15:55
@moisses89 moisses89 marked this pull request as draft July 2, 2024 15:55
@moisses89 moisses89 force-pushed the add_balances_pagination branch 3 times, most recently from 5cd8bee to e1c3d01 Compare July 3, 2024 11:07
@moisses89 moisses89 marked this pull request as ready for review July 3, 2024 11:07
django_cache.set(cache_key_count, count, 60 * 10) # 10 minutes cache
return balances, count

def _get_erc20_list_limits(self, limit: int, offset: int) -> Tuple[int, int]:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this functions is a little bit confusing, what do you think?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe better return just the list and pass the list as parameter?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a strong opinion here, but indeed sounds confusing

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion I would do a method that directly gets the erc20_addresses_paginated. That is to say something like "get_erc20_addresses_paginated_list" and inside add all the logic to retrieve the list and paginate it. Additionally, it would return the total count of the addresses list.

Something like this:

    def _get_erc20_addresses_paginated_list(
        self,
        safe_address: ChecksumAddress,
        limit: int,
        offset: int,
        only_trusted: bool = False,
        exclude_spam: bool = False,
    ) -> Tuple[List[ChecksumAddress], int]:

        all_erc20_addresses = ERC20Transfer.objects.tokens_used_by_address(safe_address)
        erc20_addresses = self._filter_addresses(
            all_erc20_addresses, only_trusted, exclude_spam
        )

        count = len(erc20_addresses)
        if not limit:
            # No pagination no limits
            return erc20_addresses[0:None], count

        if offset == 0:
            # First page will include also native token balance
            return erc20_addresses[offset : (limit - 1)], count
        else:
            # Include previous ERC20 after first page
            previous_offset = offset - 1
            return erc20_addresses[previous_offset : (previous_offset + limit)], count

This way, at least for me, it is easier to understand what it does inside, and the logic of the main method remains clearer.

WDYT?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done 0d1a9ab

@moisses89 moisses89 marked this pull request as draft July 3, 2024 11:12
@moisses89 moisses89 marked this pull request as ready for review July 3, 2024 11:28
) -> List[Balance]:
limit: Optional[int] = None,
offset: int = 0,
) -> Tuple[List[Balance], int]:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we should update the :return: in method docstring

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done 0d1a9ab

django_cache.set(cache_key_count, count, 60 * 10) # 10 minutes cache
return balances, count

def _get_erc20_list_limits(self, limit: int, offset: int) -> Tuple[int, int]:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion I would do a method that directly gets the erc20_addresses_paginated. That is to say something like "get_erc20_addresses_paginated_list" and inside add all the logic to retrieve the list and paginate it. Additionally, it would return the total count of the addresses list.

Something like this:

    def _get_erc20_addresses_paginated_list(
        self,
        safe_address: ChecksumAddress,
        limit: int,
        offset: int,
        only_trusted: bool = False,
        exclude_spam: bool = False,
    ) -> Tuple[List[ChecksumAddress], int]:

        all_erc20_addresses = ERC20Transfer.objects.tokens_used_by_address(safe_address)
        erc20_addresses = self._filter_addresses(
            all_erc20_addresses, only_trusted, exclude_spam
        )

        count = len(erc20_addresses)
        if not limit:
            # No pagination no limits
            return erc20_addresses[0:None], count

        if offset == 0:
            # First page will include also native token balance
            return erc20_addresses[offset : (limit - 1)], count
        else:
            # Include previous ERC20 after first page
            previous_offset = offset - 1
            return erc20_addresses[previous_offset : (previous_offset + limit)], count

This way, at least for me, it is easier to understand what it does inside, and the logic of the main method remains clearer.

WDYT?

@@ -50,7 +53,7 @@ def get(self, request, address):
self.request.query_params.get("exclude_spam", False)
)

paginator = pagination.ListPagination(self.request)
paginator = pagination.ListPagination(self.request, max_limit=10)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we keep the same criteria for max_limit in all endpoints? In any case, we should document it in the swagger. WDYT?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don´t think that we should have max_limit 10 for all the endpoints, we are setting just 10 for collectibles endpoint because have several outgoing requests to third parties endpoints to fill the metadata information of each collectible.

@falvaradorodriguez
Copy link
Contributor

I didn't require changes, because all the comments are free of interpretation. We can discuss it :)

Fix wrong _get_page_erc20_balances function doc
Copy link
Contributor

@falvaradorodriguez falvaradorodriguez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@moisses89 moisses89 merged commit 49f0860 into main Jul 4, 2024
6 checks passed
@moisses89 moisses89 deleted the add_balances_pagination branch July 4, 2024 14:37
@github-actions github-actions bot locked and limited conversation to collaborators Jul 4, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Paginate balances endpoint
3 participants