Skip to content
Open
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
6 changes: 6 additions & 0 deletions .changeset/nine-seas-dream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@human-protocol/sdk": major
"@human-protocol/python-sdk": major
---

Updated SDK to use a dedicated HMT stats subgraph endpoint for HMT statistics methods and removed `totalAmountPaid` and `averageAmountPerWorker` from `IDailyPayment`
4 changes: 2 additions & 2 deletions .github/workflows/cd-subgraph.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ jobs:
- name: Generate and build Subgraph
if: steps.filter_networks.outputs.continue == 'true'
run: yarn generate && yarn build
working-directory: ./packages/sdk/typescript/subgraph
working-directory: ./packages/sdk/typescript/subgraph/human-protocol
env:
NETWORK: ${{ matrix.network.name }}
- name: Authenticate & Deploy
Expand All @@ -64,7 +64,7 @@ jobs:
API_KEY: ${{ secrets.HP_GRAPH_API_KEY }}
NETWORK: ${{ matrix.network.name }}
LABEL: ${{ github.event.inputs.label }}
working-directory: ./packages/sdk/typescript/subgraph
working-directory: ./packages/sdk/typescript/subgraph/human-protocol
run: |
yarn dlx @graphprotocol/graph-cli@0.71.2 \
auth --studio "$API_KEY"
Expand Down
17 changes: 13 additions & 4 deletions .github/workflows/ci-test-subgraph.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,31 @@ on:
- "packages/core/**"
- "packages/sdk/typescript/subgraph/**"

permissions:
contents: read

jobs:
subgraph-test:
name: Subgraph Test
name: Subgraph Test (${{ matrix.workspace }})
# TODO: Use ubuntu-latest when graph binary is not failing on ubuntu 24.04
runs-on: ubuntu-22.04
strategy:
matrix:
workspace:
- "@tools/subgraph"
- "@tools/subgraph-hmt-stats"
fail-fast: true
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version-file: .nvmrc
cache: yarn
- name: Install dependencies
run: yarn workspaces focus @tools/subgraph
run: yarn workspaces focus @tools/subgraph @tools/subgraph-hmt-stats
- name: Build core package
run: yarn workspace @human-protocol/core build
- name: Generate manifest for Polygon for tests
run: NETWORK=polygon yarn workspace @tools/subgraph generate
run: NETWORK=polygon yarn workspace ${{ matrix.workspace }} generate
- name: Run subgraph test
run: yarn workspace @tools/subgraph test
run: yarn workspace ${{ matrix.workspace }} test
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,12 @@ class OperatorCategory(Enum):
"subgraph_url_api_key": (
"https://gateway.thegraph.com/api/deployments/id/QmcLwLMw3UzCSbNbjegrpNu6PB3kAd67xquuyaVWvc5Q7Q"
),
"hmt_subgraph_url": (
"https://api.studio.thegraph.com/query/74256/hmt-stats-amoy/version/latest"
),
"hmt_subgraph_url_api_key": (
"https://gateway.thegraph.com/api/deployments/id/QmZJYBQKyrsEJHQwNQ8JhkKcB5G8zQdrcwnr7ecGRNTBit"
),
"hmt_address": "0x792abbcC99c01dbDec49c9fa9A828a186Da45C33",
"factory_address": "0xAFf5a986A530ff839d49325A5dF69F96627E8D29",
"staking_address": "0xffE496683F842a923110415b7278ded3F265f2C5",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@
dailyEscrowCount
dailyWorkerCount
dailyPayoutCount
dailyHMTPayoutAmount
}
"""

hmt_event_day_data_fragment = """
fragment HMTEventDayDataFields on EventDayData {
timestamp
dailyHMTTransferCount
dailyHMTTransferAmount
dailyUniqueSenders
Expand Down Expand Up @@ -100,3 +105,33 @@ def get_event_day_data_query(filter: StatisticsFilter) -> str:
from_clause="timestamp_gte: $from" if filter.date_from else "",
to_clause="timestamp_lte: $to" if filter.date_to else "",
)


def get_hmt_event_day_data_query(filter: StatisticsFilter) -> str:
return """
query GetHMTDayData(
$from: Int
$to: Int
$orderDirection: String
$first: Int
$skip: Int
) {{
eventDayDatas(
where: {{
{from_clause}
{to_clause}
}},
orderBy: timestamp,
orderDirection: $orderDirection
first: $first
skip: $skip
) {{
...HMTEventDayDataFields
}}
}}
{hmt_event_day_data_fragment}
""".format(
hmt_event_day_data_fragment=hmt_event_day_data_fragment,
from_clause="timestamp_gte: $from" if filter.date_from else "",
to_clause="timestamp_lte: $to" if filter.date_to else "",
)
Original file line number Diff line number Diff line change
Expand Up @@ -127,22 +127,16 @@ class DailyPaymentData:

Attributes:
timestamp (datetime): Day boundary timestamp.
total_amount_paid (int): Total amount paid out on this day.
total_count (int): Number of payment transactions.
average_amount_per_worker (int): Average payout amount per worker.
"""

def __init__(
self,
timestamp: datetime,
total_amount_paid: int,
total_count: int,
average_amount_per_worker: int,
):
self.timestamp = timestamp
self.total_amount_paid = total_amount_paid
self.total_count = total_count
self.average_amount_per_worker = average_amount_per_worker


class PaymentStatistics:
Expand Down Expand Up @@ -446,8 +440,7 @@ def get_payment_statistics(
)
)
for day_data in stats.daily_payments_data:
print(f"{day_data.timestamp}: {day_data.total_amount_paid} paid")
print(f" Average per worker: {day_data.average_amount_per_worker}")
print(f"{day_data.total_count}: {day_data.total_count} payments")
```
"""
if chain_id.value not in [cid.value for cid in ChainId]:
Expand Down Expand Up @@ -481,16 +474,7 @@ def get_payment_statistics(
timestamp=datetime.fromtimestamp(
int(event_day_data.get("timestamp", 0))
),
total_amount_paid=int(
event_day_data.get("dailyHMTPayoutAmount", 0)
),
total_count=int(event_day_data.get("dailyPayoutCount", 0)),
average_amount_per_worker=(
int(event_day_data.get("dailyHMTPayoutAmount", 0))
/ int(event_day_data.get("dailyWorkerCount"))
if event_day_data.get("dailyWorkerCount", "0") != "0"
else 0
),
)
for event_day_data in event_day_datas
],
Expand Down Expand Up @@ -540,6 +524,7 @@ def get_hmt_statistics(
network,
query=get_hmtoken_statistics_query,
options=options,
use_hmt_subgraph=True,
)
hmtoken_statistics = hmtoken_statistics_data["data"]["hmtokenStatistics"]

Expand Down Expand Up @@ -612,6 +597,7 @@ def get_hmt_holders(
"orderDirection": param.order_direction,
},
options=options,
use_hmt_subgraph=True,
)

holders = holders_data["data"]["holders"]
Expand Down Expand Up @@ -675,12 +661,12 @@ def get_hmt_daily_data(
raise StatisticsUtilsError("Empty network configuration")

from human_protocol_sdk.gql.statistics import (
get_event_day_data_query,
get_hmt_event_day_data_query,
)

event_day_datas_data = custom_gql_fetch(
network,
query=get_event_day_data_query(filter),
query=get_hmt_event_day_data_query(filter),
params={
"from": int(filter.date_from.timestamp()) if filter.date_from else None,
"to": int(filter.date_to.timestamp()) if filter.date_to else None,
Expand All @@ -689,6 +675,7 @@ def get_hmt_daily_data(
"orderDirection": filter.order_direction.value,
},
options=options,
use_hmt_subgraph=True,
)
event_day_datas = event_day_datas_data["data"]["eventDayDatas"]

Expand Down
44 changes: 40 additions & 4 deletions packages/sdk/python/human-protocol-sdk/human_protocol_sdk/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,11 +207,30 @@ def is_indexer_error(error: Exception) -> bool:
return "bad indexers" in message.lower()


def _resolve_subgraph_urls(
network: Dict[str, Any], use_hmt_subgraph: bool = False
) -> Tuple[str, str]:
"""Resolve public/API-key URLs for either default or HMT subgraph."""
if not use_hmt_subgraph:
return network["subgraph_url"], network["subgraph_url_api_key"]

hmt_subgraph_url = network.get("hmt_subgraph_url")
hmt_subgraph_url_api_key = network.get("hmt_subgraph_url_api_key")

public_url = hmt_subgraph_url or network["subgraph_url"]
api_key_url = (
hmt_subgraph_url_api_key or hmt_subgraph_url or network["subgraph_url_api_key"]
)

return public_url, api_key_url


def custom_gql_fetch(
network: Dict[str, Any],
query: str,
params: Optional[Dict[str, Any]] = None,
options: Optional[SubgraphOptions] = None,
use_hmt_subgraph: bool = False,
) -> Dict[str, Any]:
"""Fetch data from the subgraph with optional retry logic and indexer routing.

Expand All @@ -220,6 +239,7 @@ def custom_gql_fetch(
query (str): GraphQL query string to execute.
params (Optional[Dict[str, Any]]): Optional query parameters/variables dictionary.
options (Optional[SubgraphOptions]): Optional subgraph configuration for retries and indexer selection.
use_hmt_subgraph (bool): If true, resolves URL using HMT subgraph keys with fallback.

Returns:
JSON response from the subgraph containing the query results.
Expand Down Expand Up @@ -250,7 +270,12 @@ def custom_gql_fetch(
subgraph_api_key = os.getenv("SUBGRAPH_API_KEY", "")

if not options:
return _fetch_subgraph_data(network, query, params)
return _fetch_subgraph_data(
network,
query,
params,
use_hmt_subgraph=use_hmt_subgraph,
)

has_max_retries = options.max_retries is not None
has_base_delay = options.base_delay is not None
Expand All @@ -272,7 +297,13 @@ def custom_gql_fetch(

for attempt in range(max_retries + 1):
try:
return _fetch_subgraph_data(network, query, params, options.indexer_id)
return _fetch_subgraph_data(
network,
query,
params,
options.indexer_id,
use_hmt_subgraph,
)
except Exception as error:
last_error = error

Expand All @@ -290,6 +321,7 @@ def _fetch_subgraph_data(
query: str,
params: Optional[Dict[str, Any]] = None,
indexer_id: Optional[str] = None,
use_hmt_subgraph: bool = False,
) -> Dict[str, Any]:
"""Internal function to fetch data from the subgraph API.

Expand All @@ -298,21 +330,25 @@ def _fetch_subgraph_data(
query (str): GraphQL query string to execute.
params (Optional[Dict[str, Any]]): Optional query parameters/variables dictionary.
indexer_id (Optional[str]): Optional indexer ID to route the request to.
use_hmt_subgraph (bool): If true, resolves URL using HMT subgraph keys with fallback.

Returns:
JSON response from the subgraph.

Raises:
Exception: If the HTTP request fails or returns a non-200 status code.
"""
default_subgraph_url, default_subgraph_url_api_key = _resolve_subgraph_urls(
network, use_hmt_subgraph
)
subgraph_api_key = os.getenv("SUBGRAPH_API_KEY", "")
if subgraph_api_key:
subgraph_url = network["subgraph_url_api_key"]
subgraph_url = default_subgraph_url_api_key
else:
logger.warning(
"Warning: SUBGRAPH_API_KEY is not provided. It might cause issues with the subgraph."
)
subgraph_url = network["subgraph_url"]
subgraph_url = default_subgraph_url

subgraph_url = _attach_indexer_id(subgraph_url, indexer_id)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import unittest
from datetime import datetime
from unittest.mock import MagicMock, patch
from unittest.mock import patch

from human_protocol_sdk.constants import NETWORKS, ChainId
from human_protocol_sdk.gql.hmtoken import get_holders_query
from human_protocol_sdk.gql.statistics import (
get_event_day_data_query,
get_escrow_statistics_query,
get_hmt_event_day_data_query,
get_hmtoken_statistics_query,
)
from human_protocol_sdk.statistics import (
Expand Down Expand Up @@ -147,7 +148,6 @@ def test_get_payment_statistics(self):
{
"timestamp": 1,
"dailyPayoutCount": "4",
"dailyHMTPayoutAmount": "100",
"dailyWorkerCount": "4",
},
],
Expand Down Expand Up @@ -177,13 +177,7 @@ def test_get_payment_statistics(self):
payment_statistics.daily_payments_data[0].timestamp,
datetime.fromtimestamp(1),
)
self.assertEqual(
payment_statistics.daily_payments_data[0].total_amount_paid, 100
)
self.assertEqual(payment_statistics.daily_payments_data[0].total_count, 4)
self.assertEqual(
payment_statistics.daily_payments_data[0].average_amount_per_worker, 25
)

def test_get_hmt_statistics(self):
with patch(
Expand All @@ -207,6 +201,7 @@ def test_get_hmt_statistics(self):
NETWORKS[ChainId.LOCALHOST],
query=get_hmtoken_statistics_query,
options=None,
use_hmt_subgraph=True,
)

self.assertEqual(hmt_statistics.total_transfer_amount, 100)
Expand Down Expand Up @@ -245,6 +240,7 @@ def test_get_hmt_holders(self):
"orderDirection": param.order_direction,
},
options=None,
use_hmt_subgraph=True,
)

self.assertEqual(len(holders), 2)
Expand Down Expand Up @@ -283,7 +279,7 @@ def test_get_hmt_daily_data(self):

mock_function.assert_any_call(
NETWORKS[ChainId.LOCALHOST],
query=get_event_day_data_query(param),
query=get_hmt_event_day_data_query(param),
params={
"from": 1683811973,
"to": 1683812007,
Expand All @@ -292,6 +288,7 @@ def test_get_hmt_daily_data(self):
"orderDirection": "asc",
},
options=None,
use_hmt_subgraph=True,
)

self.assertEqual(len(hmt_statistics), 1)
Expand Down
Loading
Loading