-
Notifications
You must be signed in to change notification settings - Fork 178
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support other networks than Mainnet for Sushiswap
- Organize Sushiswap and Kyber oracles files - Related to #343
- Loading branch information
Showing
7 changed files
with
234 additions
and
162 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
import logging | ||
from typing import Optional | ||
|
||
from eth_abi.exceptions import DecodingError | ||
from web3.exceptions import BadFunctionCallOutput | ||
|
||
from gnosis.util import cached_property | ||
|
||
from .. import EthereumClient, EthereumNetwork | ||
from ..contracts import get_kyber_network_proxy_contract | ||
from .oracles import CannotGetPriceFromOracle, InvalidPriceFromOracle, PriceOracle | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class KyberOracle(PriceOracle): | ||
""" | ||
KyberSwap Legacy Oracle | ||
https://docs.kyberswap.com/Legacy/addresses/addresses-mainnet | ||
""" | ||
|
||
# This is the `tokenAddress` they use for ETH ¯\_(ツ)_/¯ | ||
ETH_TOKEN_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" | ||
ADDRESSES = { | ||
EthereumNetwork.MAINNET: "0x9AAb3f75489902f3a48495025729a0AF77d4b11e", | ||
EthereumNetwork.RINKEBY: "0x0d5371e5EE23dec7DF251A8957279629aa79E9C5", | ||
EthereumNetwork.ROPSTEN: "0xd719c34261e099Fdb33030ac8909d5788D3039C4", | ||
EthereumNetwork.KOVAN: "0xc153eeAD19e0DBbDb3462Dcc2B703cC6D738A37c", | ||
} | ||
|
||
def __init__( | ||
self, | ||
ethereum_client: EthereumClient, | ||
kyber_network_proxy_address: Optional[str] = None, | ||
): | ||
""" | ||
:param ethereum_client: | ||
:param kyber_network_proxy_address: https://developer.kyber.network/docs/MainnetEnvGuide/#contract-addresses | ||
""" | ||
self.ethereum_client = ethereum_client | ||
self.w3 = ethereum_client.w3 | ||
self._kyber_network_proxy_address = kyber_network_proxy_address | ||
|
||
@cached_property | ||
def kyber_network_proxy_address(self): | ||
if self._kyber_network_proxy_address: | ||
return self._kyber_network_proxy_address | ||
return self.ADDRESSES.get( | ||
self.ethereum_client.get_network(), | ||
self.ADDRESSES.get(EthereumNetwork.MAINNET), | ||
) # By default return Mainnet address | ||
|
||
@cached_property | ||
def kyber_network_proxy_contract(self): | ||
return get_kyber_network_proxy_contract( | ||
self.w3, self.kyber_network_proxy_address | ||
) | ||
|
||
def get_price( | ||
self, token_address_1: str, token_address_2: str = ETH_TOKEN_ADDRESS | ||
) -> float: | ||
if token_address_1 == token_address_2: | ||
return 1.0 | ||
try: | ||
# Get decimals for token, estimation will be more accurate | ||
decimals = self.ethereum_client.erc20.get_decimals(token_address_1) | ||
token_unit = int(10**decimals) | ||
( | ||
expected_rate, | ||
_, | ||
) = self.kyber_network_proxy_contract.functions.getExpectedRate( | ||
token_address_1, token_address_2, int(token_unit) | ||
).call() | ||
|
||
price = expected_rate / 1e18 | ||
|
||
if price <= 0.0: | ||
# Try again the opposite | ||
( | ||
expected_rate, | ||
_, | ||
) = self.kyber_network_proxy_contract.functions.getExpectedRate( | ||
token_address_2, token_address_1, int(token_unit) | ||
).call() | ||
price = (token_unit / expected_rate) if expected_rate else 0 | ||
|
||
if price <= 0.0: | ||
error_message = ( | ||
f"price={price} <= 0 from kyber-network-proxy={self.kyber_network_proxy_address} " | ||
f"for token-1={token_address_1} to token-2={token_address_2}" | ||
) | ||
logger.warning(error_message) | ||
raise InvalidPriceFromOracle(error_message) | ||
return price | ||
except (ValueError, BadFunctionCallOutput, DecodingError) as e: | ||
error_message = ( | ||
f"Cannot get price from kyber-network-proxy={self.kyber_network_proxy_address} " | ||
f"for token-1={token_address_1} to token-2={token_address_2}" | ||
) | ||
logger.warning(error_message) | ||
raise CannotGetPriceFromOracle(error_message) from e |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import logging | ||
|
||
from hexbytes import HexBytes | ||
|
||
from .. import EthereumNetwork | ||
from .oracles import UniswapV2Oracle | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class SushiswapOracle(UniswapV2Oracle): | ||
PAIR_INIT_CODE = HexBytes( | ||
"0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303" | ||
) | ||
ROUTER_ADDRESSES = { | ||
EthereumNetwork.MAINNET: "0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F", | ||
EthereumNetwork.MATIC: "0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506", | ||
EthereumNetwork.ARBITRUM: "0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506", | ||
EthereumNetwork.AVALANCHE: "0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506", | ||
EthereumNetwork.MOON_MOONRIVER: "0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506", | ||
EthereumNetwork.FANTOM: "0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506", | ||
EthereumNetwork.BINANCE: "0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506", | ||
EthereumNetwork.XDAI: "0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506", | ||
EthereumNetwork.CELO: "0x1421bDe4B10e8dd459b3BCb598810B1337D56842", | ||
EthereumNetwork.FUSE_MAINNET: "0xF4d73326C13a4Fc5FD7A064217e12780e9Bd62c3", | ||
EthereumNetwork.OKEXCHAIN: "0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506", | ||
EthereumNetwork.PALM: "0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506", | ||
EthereumNetwork.MOON_MOONBEAM: "0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506", | ||
EthereumNetwork.HECO: "0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506", | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
from django.test import TestCase | ||
|
||
from eth_account import Account | ||
|
||
from ... import EthereumClient | ||
from ...oracles import CannotGetPriceFromOracle, KyberOracle | ||
from ..ethereum_test_case import EthereumTestCaseMixin | ||
from ..test_oracles import ( | ||
dai_token_mainnet_address, | ||
gno_token_mainnet_address, | ||
usdt_token_mainnet_address, | ||
weth_token_mainnet_address, | ||
) | ||
from ..utils import just_test_if_mainnet_node | ||
|
||
|
||
class TestKyberOracle(EthereumTestCaseMixin, TestCase): | ||
def test_kyber_oracle(self): | ||
mainnet_node = just_test_if_mainnet_node() | ||
ethereum_client = EthereumClient(mainnet_node) | ||
kyber_oracle = KyberOracle(ethereum_client) | ||
price = kyber_oracle.get_price( | ||
gno_token_mainnet_address, weth_token_mainnet_address | ||
) | ||
self.assertLess(price, 1) | ||
self.assertGreater(price, 0) | ||
|
||
# Test with 2 stablecoins | ||
price = kyber_oracle.get_price( | ||
dai_token_mainnet_address, usdt_token_mainnet_address | ||
) | ||
self.assertAlmostEqual(price, 1.0, delta=0.5) | ||
|
||
price = kyber_oracle.get_price( | ||
usdt_token_mainnet_address, dai_token_mainnet_address | ||
) | ||
self.assertAlmostEqual(price, 1.0, delta=0.5) | ||
|
||
self.assertEqual( | ||
kyber_oracle.get_price("0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"), 1.0 | ||
) | ||
|
||
def test_kyber_oracle_not_deployed(self): | ||
kyber_oracle = KyberOracle(self.ethereum_client, Account.create().address) | ||
random_token_address = Account.create().address | ||
with self.assertRaisesMessage( | ||
CannotGetPriceFromOracle, "Cannot get price from kyber-network-proxy" | ||
): | ||
kyber_oracle.get_price(random_token_address) |
Oops, something went wrong.