-
Notifications
You must be signed in to change notification settings - Fork 284
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
Remove price calculation #1762
Remove price calculation #1762
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
import operator | ||
from dataclasses import dataclass | ||
from datetime import datetime | ||
from enum import Enum | ||
from typing import List, Optional, Sequence | ||
|
||
from django.conf import settings | ||
|
@@ -16,13 +17,7 @@ | |
from gnosis.eth import EthereumClient, EthereumClientProvider | ||
from gnosis.eth.utils import fast_is_checksum_address | ||
|
||
from safe_transaction_service.tokens.clients import CannotGetPrice | ||
from safe_transaction_service.tokens.models import Token | ||
from safe_transaction_service.tokens.services.price_service import ( | ||
FiatCode, | ||
PriceService, | ||
PriceServiceProvider, | ||
) | ||
from safe_transaction_service.utils.redis import get_redis | ||
from safe_transaction_service.utils.utils import chunks | ||
|
||
|
@@ -72,6 +67,11 @@ def get_price_address(self) -> ChecksumAddress: | |
return self.token_address | ||
|
||
|
||
class FiatCode(Enum): | ||
USD = 1 | ||
EUR = 2 | ||
|
||
|
||
@dataclass | ||
class BalanceWithFiat(Balance): | ||
eth_value: float # Value in ether | ||
|
@@ -84,9 +84,7 @@ class BalanceWithFiat(Balance): | |
class BalanceServiceProvider: | ||
def __new__(cls): | ||
if not hasattr(cls, "instance"): | ||
cls.instance = BalanceService( | ||
EthereumClientProvider(), PriceServiceProvider(), get_redis() | ||
) | ||
cls.instance = BalanceService(EthereumClientProvider(), get_redis()) | ||
return cls.instance | ||
|
||
@classmethod | ||
|
@@ -96,12 +94,9 @@ def del_singleton(cls): | |
|
||
|
||
class BalanceService: | ||
def __init__( | ||
self, ethereum_client: EthereumClient, price_service: PriceService, redis: Redis | ||
): | ||
def __init__(self, ethereum_client: EthereumClient, redis: Redis): | ||
self.ethereum_client = ethereum_client | ||
self.ethereum_network = self.ethereum_client.get_network() | ||
self.price_service = price_service | ||
self.redis = redis | ||
self.cache_token_info = TTLCache( | ||
maxsize=4096, ttl=60 * 30 | ||
|
@@ -268,50 +263,28 @@ def get_usd_balances( | |
exclude_spam: bool = False, | ||
) -> List[BalanceWithFiat]: | ||
""" | ||
All this could be more optimal (e.g. batching requests), but as everything is cached | ||
I think we should be alright | ||
NOTE: PriceService was removed, this function return balances with price 0. | ||
:param safe_address: | ||
:param only_trusted: If True, return balance only for trusted tokens | ||
:param exclude_spam: If True, exclude spam tokens | ||
:return: List of BalanceWithFiat | ||
""" | ||
# TODO Use price service get_token_cached_usd_values | ||
balances: List[Balance] = self.get_balances( | ||
safe_address, only_trusted, exclude_spam | ||
) | ||
try: | ||
eth_price = self.price_service.get_native_coin_usd_price() | ||
except CannotGetPrice: | ||
logger.warning("Cannot get network ether price", exc_info=True) | ||
eth_price = 0 | ||
balances_with_usd = [] | ||
price_token_addresses = [balance.get_price_address() for balance in balances] | ||
token_eth_values_with_timestamp = ( | ||
self.price_service.get_token_cached_eth_values(price_token_addresses) | ||
) | ||
for balance, token_eth_value_with_timestamp in zip( | ||
balances, token_eth_values_with_timestamp | ||
): | ||
token_eth_value = token_eth_value_with_timestamp.eth_value | ||
token_address = balance.token_address | ||
if not token_address: # Ether | ||
fiat_conversion = eth_price | ||
fiat_balance = fiat_conversion * (balance.balance / 10**18) | ||
else: | ||
fiat_conversion = eth_price * token_eth_value | ||
balance_with_decimals = balance.balance / 10**balance.token.decimals | ||
fiat_balance = fiat_conversion * balance_with_decimals | ||
|
||
for balance in balances: | ||
balances_with_usd.append( | ||
BalanceWithFiat( | ||
balance.token_address, | ||
balance.token, | ||
balance.balance, | ||
token_eth_value, | ||
token_eth_value_with_timestamp.timestamp, | ||
round(fiat_balance, 4), | ||
round(fiat_conversion, 4), | ||
0.0, | ||
datetime.utcfromtimestamp(0), | ||
0.0, | ||
0.0, | ||
FiatCode.USD.name, | ||
) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @hectorgomezv just to inform you, the transaction service will return There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you @moisses89! No problem at all from the CGW perspective, the service is not using that endpoint anymore. |
||
) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,13 @@ | ||
from typing import Optional | ||
from unittest import mock | ||
from unittest.mock import MagicMock | ||
import datetime | ||
|
||
from django.test import TestCase | ||
from django.utils import timezone | ||
|
||
from eth_account import Account | ||
from eth_typing import ChecksumAddress | ||
|
||
from gnosis.eth.tests.ethereum_test_case import EthereumTestCaseMixin | ||
from gnosis.eth.tests.utils import deploy_erc20 | ||
|
||
from safe_transaction_service.tokens.models import Token | ||
from safe_transaction_service.tokens.services.price_service import PriceService | ||
from safe_transaction_service.tokens.tests.factories import TokenFactory | ||
|
||
from ..services import BalanceServiceProvider | ||
|
@@ -41,18 +36,8 @@ def test_get_token_info(self): | |
self.assertEqual(token_info.symbol, token_db.symbol) | ||
self.assertEqual(token_info.decimals, token_db.decimals) | ||
|
||
@mock.patch.object( | ||
PriceService, "get_token_eth_value", return_value=0.4, autospec=True | ||
) | ||
@mock.patch.object( | ||
PriceService, "get_native_coin_usd_price", return_value=123.4, autospec=True | ||
) | ||
@mock.patch.object(timezone, "now", return_value=timezone.now()) | ||
def test_get_usd_balances( | ||
self, | ||
timezone_now_mock: MagicMock, | ||
get_native_coin_usd_price_mock: MagicMock, | ||
get_token_eth_value_mock: MagicMock, | ||
): | ||
balance_service = self.balance_service | ||
|
||
|
@@ -85,16 +70,22 @@ def test_get_usd_balances( | |
balances, | ||
[ | ||
BalanceWithFiat( | ||
None, None, value, 1.0, timezone_now_mock.return_value, 0.0, 123.4 | ||
None, | ||
None, | ||
value, | ||
0.0, | ||
datetime.datetime.utcfromtimestamp(0), | ||
0.0, | ||
0.0, | ||
), | ||
BalanceWithFiat( | ||
erc20.address, | ||
token_info, | ||
tokens_value, | ||
0.4, | ||
timezone_now_mock.return_value, | ||
round(123.4 * 0.4 * (tokens_value / 1e18), 4), | ||
round(123.4 * 0.4, 4), | ||
0.0, | ||
datetime.datetime.utcfromtimestamp(0), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From clients atre expected a date, how we cannot say when was calculated I decided to put epoch 0. |
||
0.0, | ||
0.0, | ||
), | ||
], | ||
) | ||
|
@@ -104,7 +95,7 @@ def test_get_usd_balances( | |
balances, | ||
[ | ||
BalanceWithFiat( | ||
None, None, value, 1.0, timezone_now_mock.return_value, 0.0, 123.4 | ||
None, None, value, 0.0, datetime.datetime.utcfromtimestamp(0), 0, 0 | ||
), | ||
], | ||
) | ||
|
@@ -115,16 +106,16 @@ def test_get_usd_balances( | |
balances, | ||
[ | ||
BalanceWithFiat( | ||
None, None, value, 1.0, timezone_now_mock.return_value, 0.0, 123.4 | ||
None, None, value, 0.0, datetime.datetime.utcfromtimestamp(0), 0, 0 | ||
), | ||
BalanceWithFiat( | ||
erc20.address, | ||
token_info, | ||
tokens_value, | ||
0.4, | ||
timezone_now_mock.return_value, | ||
round(123.4 * 0.4 * (tokens_value / 1e18), 4), | ||
round(123.4 * 0.4, 4), | ||
0, | ||
datetime.datetime.utcfromtimestamp(0), | ||
0, | ||
0, | ||
), | ||
], | ||
) | ||
|
@@ -167,57 +158,46 @@ def test_get_usd_balances( | |
None, | ||
None, | ||
value, | ||
1.0, | ||
timezone_now_mock.return_value, | ||
0.0, | ||
123.4, | ||
datetime.datetime.utcfromtimestamp(0), | ||
0.0, | ||
0.0, | ||
), | ||
BalanceWithFiat( | ||
erc20_3.address, | ||
token_info_3, | ||
tokens_value, | ||
0.4, | ||
timezone_now_mock.return_value, | ||
round(123.4 * 0.4 * (tokens_value / 1e18), 4), | ||
round(123.4 * 0.4, 4), | ||
0.0, | ||
datetime.datetime.utcfromtimestamp(0), | ||
0.0, | ||
0.0, | ||
), | ||
BalanceWithFiat( | ||
erc20.address, | ||
token_info, | ||
tokens_value, | ||
0.4, | ||
timezone_now_mock.return_value, | ||
round(123.4 * 0.4 * (tokens_value / 1e18), 4), | ||
round(123.4 * 0.4, 4), | ||
0.0, | ||
datetime.datetime.utcfromtimestamp(0), | ||
0.0, | ||
0.0, | ||
), | ||
BalanceWithFiat( | ||
erc20_2.address, | ||
token_info_2, | ||
tokens_value, | ||
0.4, | ||
timezone_now_mock.return_value, | ||
round(123.4 * 0.4 * (tokens_value / 1e18), 4), | ||
round(123.4 * 0.4, 4), | ||
0.0, | ||
datetime.datetime.utcfromtimestamp(0), | ||
0.0, | ||
0.0, | ||
), | ||
], | ||
) | ||
|
||
@mock.patch.object( | ||
PriceService, "get_token_eth_value", return_value=0.4, autospec=True | ||
) | ||
@mock.patch.object( | ||
PriceService, "get_native_coin_usd_price", return_value=123.4, autospec=True | ||
) | ||
@mock.patch.object(timezone, "now", return_value=timezone.now()) | ||
def test_get_usd_balances_copy_price( | ||
self, | ||
timezone_now_mock: MagicMock, | ||
get_native_coin_usd_price_mock: MagicMock, | ||
get_token_eth_value_mock: MagicMock, | ||
): | ||
def test_get_usd_balances_copy_price(self): | ||
balance_service = self.balance_service | ||
safe_address = SafeContractFactory().address | ||
random_address = Account.create().address | ||
timestamp_str = "1970-01-01T00:00:00Z" | ||
|
||
balances = balance_service.get_usd_balances(safe_address) | ||
self.assertEqual(len(balances), 1) | ||
|
@@ -235,16 +215,7 @@ def test_get_usd_balances_copy_price( | |
) | ||
ERC20TransferFactory(address=erc20.address, to=safe_address) | ||
|
||
def get_token_eth_value( | ||
self, token_address: ChecksumAddress | ||
) -> Optional[float]: | ||
if token_address == erc20.address: | ||
return 0.4 | ||
elif token_address == random_address: | ||
return 0.1 | ||
|
||
get_token_eth_value_mock.side_effect = get_token_eth_value | ||
for expected_token_eth_value in (0.4, 0.1): | ||
for expected_token_eth_value in (0, 0): | ||
with self.subTest(expected_token_eth_value=expected_token_eth_value): | ||
balances = balance_service.get_usd_balances(safe_address) | ||
self.assertEqual(len(balances), 2) | ||
|
@@ -255,24 +226,19 @@ def get_token_eth_value( | |
None, | ||
None, | ||
0, | ||
1.0, | ||
timezone_now_mock.return_value, | ||
expected_token_eth_value, | ||
datetime.datetime.utcfromtimestamp(0), | ||
0.0, | ||
0.0, | ||
123.4, | ||
), | ||
BalanceWithFiat( | ||
erc20.address, | ||
balance_service.get_token_info(erc20.address), | ||
tokens_value, | ||
expected_token_eth_value, | ||
timezone_now_mock.return_value, | ||
round( | ||
123.4 | ||
* expected_token_eth_value | ||
* (tokens_value / 1e18), | ||
4, | ||
), | ||
round(123.4 * expected_token_eth_value, 4), | ||
datetime.datetime.utcfromtimestamp(0), | ||
0.0, | ||
0.0, | ||
), | ||
], | ||
) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added this Enum class while we keep balance with price. I think that we should do this in other issue because this needs to be coordinated with clients, for example safe-client-gateway.