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 alert for wrong size spot values #165

Merged
merged 3 commits into from
Jun 14, 2024
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions src/tellor_disputables/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@

from tellor_disputables import ALWAYS_ALERT_QUERY_TYPES
from tellor_disputables import NEW_REPORT_ABI
from tellor_disputables.discord import send_discord_msg
from tellor_disputables.utils import are_all_attributes_none
from tellor_disputables.utils import disputable_str
from tellor_disputables.utils import get_logger
Expand Down Expand Up @@ -431,6 +432,10 @@ async def parse_new_report_event(
except eth_abi.exceptions.DecodingError:
new_report.value = event_data.args._value

if new_report.query_type == "SpotPrice":
if len(event_data.args._value) != 32:
send_discord_msg("Spot price value length is not 32 bytes")

# if query of event matches a query type of the monitored feeds, fill the query parameters

monitored_feed = None
Expand All @@ -456,7 +461,7 @@ async def parse_new_report_event(
if feed_qid == new_report.query_id:
if new_report.query_type == "SpotPrice":
catalog_entry = query_catalog.find(query_id=new_report.query_id)
mf.feed = CATALOG_FEEDS.get(catalog_entry[0].tag)
mf.feed = get_feed_from_catalog(catalog_entry[0].tag)

else:

Expand Down Expand Up @@ -486,7 +491,7 @@ async def parse_new_report_event(
catalog = query_catalog.find(query_id=new_report.query_id)
if catalog:
tag = catalog[0].tag
feed = CATALOG_FEEDS.get(tag)
feed = get_feed_from_catalog(tag)
if feed is None:
logger.error(f"Unable to find feed for tag {tag}")
return None
Expand Down Expand Up @@ -571,3 +576,7 @@ def get_block_number_at_timestamp(cfg: TelliotConfig, timestamp: int) -> Any:
estimated_block_number = block_a.number + estimated_block_delta

return int(estimated_block_number)


def get_feed_from_catalog(tag: str) -> Optional[DataFeed]:
return CATALOG_FEEDS.get(tag)
4 changes: 2 additions & 2 deletions src/tellor_disputables/discord.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
"""Send text messages using Twilio."""
import os
from typing import Any

import click
from discordwebhook import Discord

from tellor_disputables import ALWAYS_ALERT_QUERY_TYPES
from tellor_disputables.data import NewReport


def generic_alert(msg: str) -> None:
Expand Down Expand Up @@ -38,7 +38,7 @@ def dispute_alert(msg: str) -> None:
return


def alert(all_values: bool, new_report: NewReport) -> None:
def alert(all_values: bool, new_report: Any) -> None:

if new_report.query_type in ALWAYS_ALERT_QUERY_TYPES:
msg = generate_alert_msg(False, new_report.link)
Expand Down
87 changes: 86 additions & 1 deletion tests/test_auto_dispute_multiple_ids.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@
from chained_accounts import ChainedAccount
from telliot_core.apps.core import TelliotConfig
from telliot_core.apps.core import TelliotCore
from telliot_feeds.datasource import DataSource
from telliot_feeds.dtypes.datapoint import datetime_now_utc
from telliot_feeds.dtypes.datapoint import OptionalDataPoint
from telliot_feeds.feeds import DATAFEED_BUILDER_MAPPING
from telliot_feeds.feeds import eth_usd_median_feed
from telliot_feeds.feeds import evm_call_feed_example
from telliot_feeds.queries.price.spot_price import SpotPrice
from web3 import Web3
Expand Down Expand Up @@ -163,7 +166,9 @@ async def setup_and_start(is_disputing, config, config_patches=None):
# using exit stack makes nested patching easier to read
with ExitStack() as stack:
stack.enter_context(patch("getpass.getpass", return_value=""))
stack.enter_context(patch("tellor_disputables.discord.send_discord_msg", side_effect=print("alert sent")))
stack.enter_context(
patch("tellor_disputables.discord.send_discord_msg", side_effect=lambda _: print("alert sent"))
)
stack.enter_context(patch("tellor_disputables.cli.TelliotConfig", new=lambda: config))
stack.enter_context(patch("telliot_feeds.feeds.evm_call_feed.source.cfg", config))
stack.enter_context(
Expand Down Expand Up @@ -384,3 +389,83 @@ async def test_evmcall_right_value_wrong_timestamp(submit_multiple_bad_values: A
result = matching_rows[0] if matching_rows else None
assert result is not None, "Expected row not found."
assert expected in ",".join(result), "Expected row not in the response."


value = 3400


class FakeDataSource(DataSource[float]):
async def fetch_new_datapoint(self) -> OptionalDataPoint[float]:
return value, datetime_now_utc()


@pytest.mark.asyncio
async def test_spot_short_value(stake_deposited: Awaitable[TelliotCore], capsys):

core = await stake_deposited
core.config.endpoints.endpoints = [core.config.endpoints.find(chain_id=1337)[0]]
contracts = core.get_tellor360_contracts()
w3 = core.endpoint._web3
oracle = contracts.oracle
submit_value = oracle.contract.get_function_by_name("submitValue")
# submit fake eth value
submit_value_txn = submit_value(
eth_query_id, int.to_bytes(int(value * 1e18), 20, "big"), 0, eth_query_data
).buildTransaction(txn_kwargs(w3))
submit_value_hash = w3.eth.send_transaction(submit_value_txn)
receipt = w3.eth.wait_for_transaction_receipt(submit_value_hash)
assert receipt["status"] == 1

eth_usd_median_feed.source = FakeDataSource()
eth_config = {
"feeds": [
{"query_id": eth_query_id, "threshold": {"type": "Percentage", "amount": 0.75}},
]
}
config_patches = [
patch("builtins.open", side_effect=custom_open_side_effect),
patch("yaml.safe_load", return_value=eth_config),
patch("tellor_disputables.data.get_feed_from_catalog", return_value=eth_usd_median_feed),
patch(
"tellor_disputables.data.send_discord_msg",
side_effect=lambda _: print("Spot price value length is not 32 bytes"),
),
]
await setup_and_start(False, core.config, config_patches)
assert "Spot price value length is not 32 bytes" in capsys.readouterr().out


@pytest.mark.asyncio
async def test_spot_long_value(stake_deposited: Awaitable[TelliotCore], capsys):

core = await stake_deposited
core.config.endpoints.endpoints = [core.config.endpoints.find(chain_id=1337)[0]]
contracts = core.get_tellor360_contracts()
w3 = core.endpoint._web3
oracle = contracts.oracle
submit_value = oracle.contract.get_function_by_name("submitValue")
# submit fake eth value
submit_value_txn = submit_value(
eth_query_id, int.to_bytes(int(value * 1e18), 33, "big"), 0, eth_query_data
).buildTransaction(txn_kwargs(w3))
submit_value_hash = w3.eth.send_transaction(submit_value_txn)
receipt = w3.eth.wait_for_transaction_receipt(submit_value_hash)
assert receipt["status"] == 1

eth_usd_median_feed.source = FakeDataSource()
eth_config = {
"feeds": [
{"query_id": eth_query_id, "threshold": {"type": "Percentage", "amount": 0.75}},
]
}
config_patches = [
patch("builtins.open", side_effect=custom_open_side_effect),
patch("yaml.safe_load", return_value=eth_config),
patch("tellor_disputables.data.get_feed_from_catalog", return_value=eth_usd_median_feed),
patch(
"tellor_disputables.data.send_discord_msg",
side_effect=lambda _: print("Spot price value length is not 32 bytes"),
),
]
await setup_and_start(False, core.config, config_patches)
assert "Spot price value length is not 32 bytes" in capsys.readouterr().out
2 changes: 1 addition & 1 deletion tests/test_disputer.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ async def test_get_dispute_fee():
chain_id=80001,
provider="polygon",
network="mumbai",
url="https://polygon-mumbai-bor-rpc.publicnode.com",
url="https://polygon-mumbai.api.onfinality.io/public",
explorer="https://mumbai.polygonscan.com/",
)
# todo: use mock instead
Expand Down
Loading