Skip to content

Commit 351f5a2

Browse files
devin-ai-integration[bot]aigustin0xaguspunk
authored
feat: python Nansen plugin (#220)
* feat: add Python implementation of Nansen plugin Co-Authored-By: Agus Armellini Fischer <[email protected]> * chore: add missing files and reduce code duplication Co-Authored-By: Agus Armellini Fischer <[email protected]> * chore: update files to match Coingecko structure Co-Authored-By: Agus Armellini Fischer <[email protected]> * Update README.md --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Agus Armellini Fischer <[email protected]> Co-authored-by: Agus <[email protected]>
1 parent c34bf16 commit 351f5a2

File tree

5 files changed

+276
-0
lines changed

5 files changed

+276
-0
lines changed

python/src/plugins/nansen/README.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# GOAT SDK Nansen Plugin
2+
3+
This plugin provides tools for interacting with the Nansen API, allowing you to:
4+
- Get token details and trades
5+
- Retrieve NFT collection details and trades
6+
- Access smart money wallet analysis
7+
- Get trading signals based on on-chain data
8+
9+
## Installation
10+
11+
```bash
12+
pip install goat-sdk-plugin-nansen
13+
```
14+
15+
## Usage
16+
17+
```python
18+
from goat_plugins.nansen import nansen, NansenPluginOptions
19+
20+
# Initialize the plugin with your API key
21+
plugin = nansen(NansenPluginOptions(
22+
api_key="your-nansen-api-key"
23+
))
24+
25+
## Features
26+
27+
- Token Analysis:
28+
- Get detailed token information
29+
- Track token trading activity
30+
31+
- NFT Analytics:
32+
- Fetch NFT collection details
33+
- Monitor NFT trading activity
34+
35+
- Smart Money Tracking:
36+
- Analyze smart money wallet behavior
37+
- Track token flows
38+
39+
- Trading Signals:
40+
- Get trading signals based on on-chain data
41+
- Filter by date ranges and specific tokens
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from dataclasses import dataclass
2+
from goat.classes.plugin_base import PluginBase
3+
from .service import NansenService
4+
5+
6+
@dataclass
7+
class NansenPluginOptions:
8+
api_key: str
9+
10+
11+
class NansenPlugin(PluginBase):
12+
def __init__(self, options: NansenPluginOptions):
13+
super().__init__("nansen", [NansenService(options.api_key)])
14+
15+
def supports_chain(self, chain) -> bool:
16+
return True
17+
18+
19+
def nansen(options: NansenPluginOptions) -> NansenPlugin:
20+
return NansenPlugin(options)
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
from pydantic import BaseModel, Field
2+
from typing import Optional
3+
4+
5+
class GetTokenDetailsParameters(BaseModel):
6+
address: str = Field(
7+
description="Token contract address"
8+
)
9+
10+
11+
class GetTokenTradesParameters(BaseModel):
12+
address: str = Field(
13+
description="Token contract address"
14+
)
15+
start_date: str = Field(
16+
description="Start date to filter for (format: YYYY-MM-DD)"
17+
)
18+
end_date: str = Field(
19+
description="End date to filter for (format: YYYY-MM-DD)"
20+
)
21+
22+
23+
class GetNFTDetailsParameters(BaseModel):
24+
token_address: str = Field(
25+
description="NFT contract address"
26+
)
27+
nft_id: str = Field(
28+
description="Specific NFT token ID"
29+
)
30+
31+
32+
class GetNFTTradesParameters(BaseModel):
33+
token_address: str = Field(
34+
description="NFT contract address"
35+
)
36+
nft_id: str = Field(
37+
description="Specific NFT token ID"
38+
)
39+
start_date: str = Field(
40+
description="Start date to filter for (format: YYYY-MM-DD)"
41+
)
42+
end_date: str = Field(
43+
description="End date to filter for (format: YYYY-MM-DD)"
44+
)
45+
46+
47+
class GetSmartMoneyParameters(BaseModel):
48+
start_date: str = Field(
49+
description="Start date to filter for (format: YYYY-MM-DD)"
50+
)
51+
end_date: str = Field(
52+
description="End date to filter for (format: YYYY-MM-DD)"
53+
)
54+
token_address: Optional[str] = Field(
55+
None,
56+
description="Token address to filter by"
57+
)
58+
59+
60+
class GetTradingSignalParameters(BaseModel):
61+
start_date: str = Field(
62+
description="Start date to filter for (format: YYYY-MM-DD)"
63+
)
64+
end_date: str = Field(
65+
description="End date to filter for (format: YYYY-MM-DD)"
66+
)
67+
token_address: Optional[str] = Field(
68+
None,
69+
description="Token address to filter by"
70+
)
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import aiohttp
2+
from goat.decorators.tool import Tool
3+
from .parameters import (
4+
GetTokenDetailsParameters,
5+
GetTokenTradesParameters,
6+
GetNFTDetailsParameters,
7+
GetNFTTradesParameters,
8+
GetSmartMoneyParameters,
9+
GetTradingSignalParameters,
10+
)
11+
12+
13+
class NansenService:
14+
def __init__(self, api_key: str):
15+
self.api_key = api_key
16+
self.base_url = "https://api.nansen.ai/v1"
17+
18+
async def _fetch(self, endpoint: str, action: str, params: dict[str, str] = {}):
19+
"""Helper method to handle HTTP requests with error handling"""
20+
try:
21+
url = f"{self.base_url}{endpoint}"
22+
headers = {"api-key": self.api_key}
23+
async with aiohttp.ClientSession() as session:
24+
async with session.get(url, params=params, headers=headers) as response:
25+
if not response.ok:
26+
raise Exception(f"HTTP error! status: {response.status} {await response.text()}")
27+
return await response.json()
28+
except Exception as e:
29+
raise Exception(f"Failed to {action}: {e}")
30+
31+
@Tool({
32+
"description": "Get details for a specific token from Nansen",
33+
"parameters_schema": GetTokenDetailsParameters
34+
})
35+
async def get_token_details(self, parameters: dict):
36+
"""Get details for a specific token from Nansen"""
37+
params = {"address": parameters["address"]}
38+
return await self._fetch("/token", "get token details", params)
39+
40+
@Tool({
41+
"description": "Get trades for a specific token from Nansen",
42+
"parameters_schema": GetTokenTradesParameters
43+
})
44+
async def get_token_trades(self, parameters: dict):
45+
"""Get trades for a specific token from Nansen"""
46+
params = {
47+
"address": parameters["address"],
48+
"start_date": parameters["start_date"],
49+
"end_date": parameters["end_date"]
50+
}
51+
return await self._fetch("/token/dex_trades", "get token trades", params)
52+
53+
@Tool({
54+
"description": "Get details for a specific NFT collection or token from Nansen",
55+
"parameters_schema": GetNFTDetailsParameters
56+
})
57+
async def get_nft_details(self, parameters: dict):
58+
"""Get details for a specific NFT collection or token from Nansen"""
59+
params = {
60+
"token_address": parameters["token_address"],
61+
"nft_id": parameters["nft_id"]
62+
}
63+
return await self._fetch("/nft", "get NFT details", params)
64+
65+
@Tool({
66+
"description": "Get trades for a specific NFT collection or token from Nansen",
67+
"parameters_schema": GetNFTTradesParameters
68+
})
69+
async def get_nft_trades(self, parameters: dict):
70+
"""Get trades for a specific NFT collection or token from Nansen"""
71+
params = {
72+
"token_address": parameters["token_address"],
73+
"nft_id": parameters["nft_id"],
74+
"start_date": parameters["start_date"],
75+
"end_date": parameters["end_date"]
76+
}
77+
return await self._fetch("/nft/trades", "get NFT trades", params)
78+
79+
@Tool({
80+
"description": "Get the flows of tokens associated with smart money addresses",
81+
"parameters_schema": GetSmartMoneyParameters
82+
})
83+
async def get_smart_money_status(self, parameters: dict):
84+
"""Get the flows of tokens associated with smart money addresses"""
85+
params = {
86+
"start_date": parameters["start_date"],
87+
"end_date": parameters["end_date"]
88+
}
89+
if parameters.get("token_address"):
90+
params["token_address"] = parameters["token_address"]
91+
return await self._fetch("/token_flows", "get smart money status", params)
92+
93+
@Tool({
94+
"description": "Get trading signals and alerts based on onchain data and patterns",
95+
"parameters_schema": GetTradingSignalParameters
96+
})
97+
async def get_trading_signal(self, parameters: dict):
98+
"""Get trading signals and alerts based on onchain data and patterns"""
99+
params = {
100+
"start_date": parameters["start_date"],
101+
"end_date": parameters["end_date"]
102+
}
103+
if parameters.get("token_address"):
104+
params["token_address"] = parameters["token_address"]
105+
return await self._fetch("/signals", "get trading signals", params)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
[tool.poetry]
2+
name = "goat-plugin-nansen"
3+
version = "0.1.0"
4+
description = "Nansen plugin for GOAT SDK"
5+
authors = ["GOAT SDK Team"]
6+
readme = "README.md"
7+
keywords = ["goat", "sdk", "web3", "agents", "ai", "nansen"]
8+
homepage = "https://ohmygoat.dev/"
9+
repository = "https://github.com/goat-sdk/goat"
10+
packages = [
11+
{ include = "goat_plugins/nansen" },
12+
]
13+
14+
[tool.poetry.urls]
15+
"Bug Tracker" = "https://github.com/goat-sdk/goat/issues"
16+
17+
[tool.poetry.dependencies]
18+
python = "^3.10"
19+
aiohttp = "^3.8.6"
20+
goat-sdk = "^0.1.1"
21+
pydantic = "^2.0.0"
22+
23+
[tool.poetry.group.test.dependencies]
24+
pytest = "^8.3.4"
25+
pytest-asyncio = "^0.25.0"
26+
27+
[tool.poetry.group.dev.dependencies]
28+
ruff = "^0.8.6"
29+
goat-sdk = { path = "../../goat-sdk", develop = true }
30+
31+
[build-system]
32+
requires = ["poetry-core"]
33+
build-backend = "poetry.core.masonry.api"
34+
35+
[tool.pytest.ini_options]
36+
asyncio_mode = "auto"
37+
38+
[tool.ruff]
39+
line-length = 120
40+
target-version = "py312"

0 commit comments

Comments
 (0)