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

Add method to check if Oracle is available for network #356

Merged
merged 1 commit into from
Sep 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 0 additions & 2 deletions gnosis/eth/oracles/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
UnderlyingToken,
UniswapOracle,
UniswapV2Oracle,
UsdPricePoolOracle,
YearnOracle,
ZerionComposedOracle,
)
Expand All @@ -45,7 +44,6 @@
"UniswapOracle",
"UniswapV2Oracle",
"UniswapV3Oracle",
"UsdPricePoolOracle",
"YearnOracle",
"ZerionComposedOracle",
]
11 changes: 11 additions & 0 deletions gnosis/eth/oracles/kyber.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,17 @@ def __init__(
self.w3 = ethereum_client.w3
self._kyber_network_proxy_address = kyber_network_proxy_address

@classmethod
def is_available(
cls,
ethereum_client: EthereumClient,
) -> bool:
"""
:param ethereum_client:
:return: `True` if Oracle is available for the EthereumClient provided, `False` otherwise
"""
return ethereum_client.get_network() in cls.ADDRESSES

@cached_property
def kyber_network_proxy_address(self):
if self._kyber_network_proxy_address:
Expand Down
118 changes: 107 additions & 11 deletions gnosis/eth/oracles/oracles.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,31 +42,45 @@ class UnderlyingToken:
quantity: int


class PriceOracle(ABC):
class BaseOracle(ABC):
@classmethod
@abstractmethod
def get_price(self, *args) -> float:
pass
def is_available(
cls,
ethereum_client: EthereumClient,
) -> bool:
"""
:param ethereum_client:
:return: `True` if Oracle is available for the EthereumClient provided, `False` otherwise
"""
raise NotImplementedError


class PricePoolOracle(ABC):
class PriceOracle(BaseOracle):
@abstractmethod
def get_pool_token_price(self, pool_token_address: ChecksumAddress) -> float:
pass
def get_price(self, *args) -> float:
raise NotImplementedError


class UsdPricePoolOracle(ABC):
class PricePoolOracle(BaseOracle):
@abstractmethod
def get_pool_usd_token_price(self, pool_token_address: ChecksumAddress) -> float:
pass
def get_pool_token_price(self, pool_token_address: ChecksumAddress) -> float:
raise NotImplementedError


class ComposedPriceOracle(ABC):
class ComposedPriceOracle(BaseOracle):
@abstractmethod
def get_underlying_tokens(self, *args) -> List[Tuple[UnderlyingToken]]:
pass
raise NotImplementedError


class UniswapOracle(PriceOracle):
"""
Uniswap V1 Oracle

https://docs.uniswap.org/protocol/V1/guides/connect-to-uniswap
"""

ADDRESSES = {
EthereumNetwork.MAINNET: "0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95",
EthereumNetwork.RINKEBY: "0xf5D915570BC477f9B8D6C0E980aA81757A3AaC36",
Expand All @@ -88,6 +102,17 @@ def __init__(
self.w3 = ethereum_client.w3
self._uniswap_factory_address = uniswap_factory_address

@classmethod
def is_available(
cls,
ethereum_client: EthereumClient,
) -> bool:
"""
:param ethereum_client:
:return: `True` if Oracle is available for the EthereumClient provided, `False` otherwise
"""
return ethereum_client.get_network() in cls.ADDRESSES

@cached_property
def uniswap_factory_address(self):
if self._uniswap_factory_address:
Expand Down Expand Up @@ -231,6 +256,22 @@ def __init__(
ethereum_client.w3, self.router_address
)

@classmethod
def is_available(
cls,
ethereum_client: EthereumClient,
) -> bool:
"""
:param ethereum_client:
:return: `True` if Oracle is available for the EthereumClient provided, `False` otherwise
"""
return ethereum_client.is_contract(
cls.ROUTER_ADDRESSES.get(
ethereum_client.get_network(),
cls.ROUTER_ADDRESSES[EthereumNetwork.MAINNET],
)
Comment on lines +269 to +272
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we default to Mainnet in for UniswapV2Oracle but not for UniswapOracle?

Copy link
Member Author

Choose a reason for hiding this comment

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

Because Uniswap used deterministic deployment for V2 but not for V1

)

@cached_property
def factory(self):
return get_uniswap_v2_factory_contract(
Expand Down Expand Up @@ -436,6 +477,17 @@ def __init__(self, ethereum_client: EthereumClient, price_oracle: PriceOracle):
self.w3 = ethereum_client.w3
self.price_oracle = price_oracle

@classmethod
def is_available(
cls,
ethereum_client: EthereumClient,
) -> bool:
"""
:param ethereum_client:
:return: `True` if Oracle is available for the EthereumClient provided, `False` otherwise
"""
return ethereum_client.get_network() == EthereumNetwork.MAINNET

def get_price(self, token_address: str) -> float:
if (
token_address == "0x4da27a545c0c5B758a6BA100e3a049001de870f5"
Expand Down Expand Up @@ -467,6 +519,17 @@ def __init__(self, ethereum_client: EthereumClient, price_oracle: PriceOracle):
self.w3 = ethereum_client.w3
self.price_oracle = price_oracle

@classmethod
def is_available(
cls,
ethereum_client: EthereumClient,
) -> bool:
"""
:param ethereum_client:
:return: `True` if Oracle is available for the EthereumClient provided, `False` otherwise
"""
return ethereum_client.get_network() == EthereumNetwork.MAINNET

def get_price(self, token_address: str) -> float:
try:
underlying_token = (
Expand Down Expand Up @@ -504,6 +567,17 @@ def __init__(
self.ethereum_client = ethereum_client
self.w3 = ethereum_client.w3

@classmethod
def is_available(
cls,
ethereum_client: EthereumClient,
) -> bool:
"""
:param ethereum_client:
:return: `True` if Oracle is available for the EthereumClient provided, `False` otherwise
"""
return ethereum_client.get_network() == EthereumNetwork.MAINNET

@cached_property
def zerion_adapter_contract(self) -> Optional[Contract]:
"""
Expand Down Expand Up @@ -626,6 +700,17 @@ def __init__(
ethereum_client, iearn_token_adapter
)

@classmethod
def is_available(
cls,
ethereum_client: EthereumClient,
) -> bool:
"""
:param ethereum_client:
:return: `True` if Oracle is available for the EthereumClient provided, `False` otherwise
"""
return ethereum_client.get_network() == EthereumNetwork.MAINNET

def get_underlying_tokens(
self, token_address: ChecksumAddress
) -> List[Tuple[float, ChecksumAddress]]:
Expand Down Expand Up @@ -661,6 +746,17 @@ def __init__(self, ethereum_client: EthereumClient, price_oracle: PriceOracle):
self.w3 = ethereum_client.w3
self.price_oracle = price_oracle

@classmethod
def is_available(
cls,
ethereum_client: EthereumClient,
) -> bool:
"""
:param ethereum_client:
:return: `True` if Oracle is available for the EthereumClient provided, `False` otherwise
"""
return ethereum_client.get_network() == EthereumNetwork.MAINNET

def get_pool_token_price(self, pool_token_address: ChecksumAddress) -> float:
"""
Estimate balancer pool token price based on its components
Expand Down
2 changes: 2 additions & 0 deletions gnosis/eth/tests/oracles/test_kyber.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ class TestKyberOracle(EthereumTestCaseMixin, TestCase):
def test_kyber_oracle(self):
mainnet_node = just_test_if_mainnet_node()
ethereum_client = EthereumClient(mainnet_node)

self.assertTrue(KyberOracle.is_available(ethereum_client))
kyber_oracle = KyberOracle(ethereum_client)
price = kyber_oracle.get_price(
gno_token_mainnet_address, weth_token_mainnet_address
Expand Down
2 changes: 2 additions & 0 deletions gnosis/eth/tests/oracles/test_sushiswap.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ def test_get_price(self):
oracles_get_decimals.cache_clear()
mainnet_node = just_test_if_mainnet_node()
ethereum_client = EthereumClient(mainnet_node)

self.assertTrue(SushiswapOracle.is_available(ethereum_client))
sushiswap_oracle = SushiswapOracle(ethereum_client)

price = sushiswap_oracle.get_price(
Expand Down
20 changes: 20 additions & 0 deletions gnosis/eth/tests/test_oracles.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ class TestOracles(EthereumTestCaseMixin, TestCase):
def test_uniswap_oracle(self):
mainnet_node = just_test_if_mainnet_node()
ethereum_client = EthereumClient(mainnet_node)

self.assertTrue(UniswapOracle.is_available(ethereum_client))
uniswap_oracle = UniswapOracle(ethereum_client)
token_address = dai_token_mainnet_address
price = uniswap_oracle.get_price(token_address)
Expand Down Expand Up @@ -100,6 +102,8 @@ def test_get_price(self):
oracles_get_decimals.cache_clear()
mainnet_node = just_test_if_mainnet_node()
ethereum_client = EthereumClient(mainnet_node)

self.assertTrue(UniswapV2Oracle.is_available(ethereum_client))
uniswap_v2_oracle = UniswapV2Oracle(ethereum_client)

self.assertEqual(oracles_get_decimals.cache_info().currsize, 0)
Expand Down Expand Up @@ -195,6 +199,8 @@ class TestAaveOracle(EthereumTestCaseMixin, TestCase):
def test_get_token_price(self):
mainnet_node = just_test_if_mainnet_node()
ethereum_client = EthereumClient(mainnet_node)

self.assertTrue(AaveOracle.is_available(ethereum_client))
uniswap_oracle = UniswapV2Oracle(ethereum_client)
aave_oracle = AaveOracle(ethereum_client, uniswap_oracle)

Expand All @@ -218,6 +224,8 @@ class TestCreamOracle(EthereumTestCaseMixin, TestCase):
def test_get_price(self):
mainnet_node = just_test_if_mainnet_node()
ethereum_client = EthereumClient(mainnet_node)

self.assertTrue(CreamOracle.is_available(ethereum_client))
sushi_oracle = SushiswapOracle(ethereum_client)
cream_oracle = CreamOracle(ethereum_client, sushi_oracle)

Expand Down Expand Up @@ -249,6 +257,8 @@ def test_get_underlying_tokens(self):

mainnet_node = just_test_if_mainnet_node()
ethereum_client = EthereumClient(mainnet_node)

self.assertTrue(CurveOracle.is_available(ethereum_client))
curve_oracle = CurveOracle(ethereum_client)

# Curve.fi ETH/stETH (steCRV) is working with the updated adapter
Expand Down Expand Up @@ -321,6 +331,8 @@ def test_get_underlying_token(self):

mainnet_node = just_test_if_mainnet_node()
ethereum_client = EthereumClient(mainnet_node)

self.assertTrue(PoolTogetherOracle.is_available(ethereum_client))
pool_together_oracle = PoolTogetherOracle(ethereum_client)

underlying_tokens = pool_together_oracle.get_underlying_tokens(
Expand All @@ -345,6 +357,8 @@ class TestYearnOracle(EthereumTestCaseMixin, TestCase):
def test_get_underlying_tokens(self):
mainnet_node = just_test_if_mainnet_node()
ethereum_client = EthereumClient(mainnet_node)

self.assertTrue(YearnOracle.is_available(ethereum_client))
yearn_oracle = YearnOracle(ethereum_client)
yearn_token_address = "0x5533ed0a3b83F70c3c4a1f69Ef5546D3D4713E44" # Yearn Curve.fi DAI/USDC/USDT/sUSD
yearn_underlying_token_address = (
Expand Down Expand Up @@ -400,6 +414,8 @@ class TestBalancerOracle(EthereumTestCaseMixin, TestCase):
def test_get_pool_token_price(self):
mainnet_node = just_test_if_mainnet_node()
ethereum_client = EthereumClient(mainnet_node)

self.assertTrue(BalancerOracle.is_available(ethereum_client))
uniswap_oracle = UniswapV2Oracle(ethereum_client)
balancer_oracle = BalancerOracle(ethereum_client, uniswap_oracle)
balancer_token_address = (
Expand All @@ -424,6 +440,8 @@ class TestMooniswapOracle(EthereumTestCaseMixin, TestCase):
def test_get_pool_token_price(self):
mainnet_node = just_test_if_mainnet_node()
ethereum_client = EthereumClient(mainnet_node)

self.assertTrue(MooniswapOracle.is_available(ethereum_client))
uniswap_oracle = UniswapV2Oracle(ethereum_client)
mooniswap_oracle = MooniswapOracle(ethereum_client, uniswap_oracle)
mooniswap_pool_address = "0x6a11F3E5a01D129e566d783A7b6E8862bFD66CcA" # 1inch Liquidity Pool (ETH-WBTC)
Expand Down Expand Up @@ -455,6 +473,8 @@ class TestEnzymeOracle(EthereumTestCaseMixin, TestCase):
def test_get_underlying_tokens(self):
mainnet_node = just_test_if_mainnet_node()
ethereum_client = EthereumClient(mainnet_node)

self.assertTrue(EnzymeOracle.is_available(ethereum_client))
enzyme_oracle = EnzymeOracle(ethereum_client)
mln_vault_token_address = "0x45c45799Bcf6C7Eb2Df0DA1240BE04cE1D18CC69"
mln_vault_underlying_token = "0xec67005c4E498Ec7f55E092bd1d35cbC47C91892"
Expand Down