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

adding adapter for spectral #79

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ dmypy.json
# Pyre type checker
.pyre/

# env files
# env files
*.env
*.envrc
.idea/
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ repos:
args: ['--target-version', 'py310']
types: [python]
- repo: https://github.com/timothycrosley/isort
rev: 5.5.4
rev: 5.12.0
hooks:
- id: isort
name: 'isort'
Expand Down
1 change: 1 addition & 0 deletions docker-compose-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@ services:
- POLYGONSCAN_BASE_URL=https://api.polygonscan.com
- REQUEST_NETWORK_SUBGRAPH_ENDPOINT_URL=https://api.thegraph.com/subgraphs/name/requestnetwork/request-payments-goerli
- REQUEST_NETWORK_INVOICE_API_URL=https://goerli.api.huma.finance/invoice
- SPECTRAL_API_KEY
2 changes: 2 additions & 0 deletions huma_signals/adapters/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@
from huma_signals.adapters.lending_pools import adapter as lending_pools_adapter
from huma_signals.adapters.polygon_wallet import adapter as polygon_wallet_adapter
from huma_signals.adapters.request_network import adapter as request_network_adapter
from huma_signals.adapters.spectral import adapter as spectral_adapter

ADAPTER_REGISTRY: Dict[str, Type[models.SignalAdapterBase]] = {
lending_pools_adapter.LendingPoolAdapter.name: lending_pools_adapter.LendingPoolAdapter,
request_network_adapter.RequestNetworkInvoiceAdapter.name: request_network_adapter.RequestNetworkInvoiceAdapter,
allowlist_adapter.AllowListAdapter.name: allowlist_adapter.AllowListAdapter,
ethereum_wallet_adapter.EthereumWalletAdapter.name: ethereum_wallet_adapter.EthereumWalletAdapter,
polygon_wallet_adapter.PolygonWalletAdapter.name: polygon_wallet_adapter.PolygonWalletAdapter,
spectral_adapter.SpectralWalletAdapter.name: spectral_adapter.SpectralWalletAdapter,
}


Expand Down
30 changes: 30 additions & 0 deletions huma_signals/adapters/spectral/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Polygon Wallet Signal Adapter

This is the repository for the Signal Adapter that fetch signal about Polygon addresses.

## Type of signals

- Address' tenure
- Address' number of transactions
- Address' current and historical balance
- ...

## Local Development

See [here](../../../docs/getting_started.md) for the development guide.

## Required environment variable

The following environment variable is required to run the adapter.
(Sign up at https://polygonscan.com/myapikey to get an API key)

```bash
POLYGONSCAN_BASE_URL
POLYGONSCAN_API_KEY
```

## Tests

```bash
make test
```
Empty file.
25 changes: 25 additions & 0 deletions huma_signals/adapters/spectral/adapter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from typing import Any, ClassVar, List

import structlog

from huma_signals.adapters import models
from huma_signals.adapters.spectral import spectral_client

logger = structlog.get_logger()


class SpectralWalletAdapter(models.SignalAdapterBase):
name: ClassVar[str] = "spectral_wallet"
required_inputs: ClassVar[List[str]] = ["borrower_wallet_address"]
signals: ClassVar[List[str]] = list(
spectral_client.SpectralWalletSignals.__fields__.keys()
)

spectral_api_client: spectral_client.SpectralClient = (
spectral_client.SpectralClient()
)

async def fetch( # pylint: disable=arguments-differ
self, borrower_wallet_address: str, *args: Any, **kwargs: Any
) -> spectral_client.SpectralWalletSignals:
return await self.spectral_api_client.get_scores(borrower_wallet_address)
74 changes: 74 additions & 0 deletions huma_signals/adapters/spectral/spectral_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import datetime

import httpx
import pydantic
import structlog

from huma_signals import models
from huma_signals.settings import settings

logger = structlog.get_logger()


class SpectralScoreIngredients(models.HumaBaseModel):
credit_mix: int
defi_actions: int
health_and_risk: int
liquidation: int
market: float
time: int
wallet: int


class SpectralWalletSignals(models.HumaBaseModel):
score: float
score_ingredients: SpectralScoreIngredients
score_timestamp: datetime.datetime
probability_of_liquidation: float
risk_level: str
wallet_address: str


class SpectralClient(models.HumaBaseModel):
"""Spectral Client"""

base_url: str = pydantic.Field(default="https://api.spectral.finance")
api_key: str = pydantic.Field(
default=settings.spectral_api_key,
description="Ethereum private key of the Spectral client",
)

@pydantic.validator("base_url")
def validate_pbase_url(cls, value: str) -> str:
if not value:
raise ValueError("spectral base_url is required")
return value

@pydantic.validator("api_key")
def validate_api_key(cls, value: str) -> str:
if not value:
raise ValueError("spectral api_key is required")
return value

async def _create_score(self, wallet_address: str) -> None:
try:
async with httpx.AsyncClient(base_url=self.base_url) as client:
request = f"/api/v1/addresses/{wallet_address}" f"/calculate_score"
headers = {"Authorization": f"Bearer {self.api_key}"}
resp = await client.post(request, headers=headers)
resp.raise_for_status()
except httpx.HTTPStatusError:
logger.error("Error fetching transactions", exc_info=True, request=request)

async def get_scores(self, wallet_address: str) -> SpectralWalletSignals:
await self._create_score(wallet_address)
try:
async with httpx.AsyncClient(base_url=self.base_url) as client:
request = f"/api/v1/addresses/{wallet_address}"
headers = {"Authorization": f"Bearer {self.api_key}"}
resp = await client.get(request, headers=headers)
resp.raise_for_status()
return SpectralWalletSignals(**resp.json())
except httpx.HTTPStatusError as e:
logger.error("Error fetching transactions", exc_info=True, request=request)
raise e
3 changes: 3 additions & 0 deletions huma_signals/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ class Config:
polygonscan_base_url: str = "https://api.polygonscan.com"
polygonscan_api_key: str

# adapter: spectral
spectral_api_key: str

# adapter: request_network
request_network_subgraph_endpoint_url: str
request_network_invoice_api_url: str
Expand Down