Skip to content

Commit c34bf16

Browse files
devin-ai-integration[bot]aigustin0xaguspunk
authored
feat: Add Python implementation of OpenSea plugin (#216)
* feat: Add Python implementation of OpenSea plugin - Created Python version of OpenSea plugin following coingecko structure - Implemented getNftCollectionStatistics and getNftSales endpoints - Added parameter schemas using Pydantic - Added async service implementation using aiohttp - Added plugin initialization with API key configuration - Includes proper error handling and response validation - Matches TypeScript functionality exactly Co-Authored-By: Agus Armellini Fischer <[email protected]> * Delete python/src/plugins/opensea/poetry.lock * 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 7d63e9e commit c34bf16

File tree

6 files changed

+234
-0
lines changed

6 files changed

+234
-0
lines changed

python/src/plugins/opensea/README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# OpenSea Plugin for GOAT SDK
2+
3+
This plugin provides integration with the OpenSea API for the GOAT SDK, allowing you to:
4+
- Get NFT collection statistics
5+
- Get recent NFT sales
6+
7+
## Installation
8+
9+
```bash
10+
pip install goat-sdk-plugin-opensea
11+
```
12+
13+
## Usage
14+
15+
```python
16+
from goat_plugins.opensea import opensea, OpenSeaPluginOptions
17+
18+
# Initialize the plugin with your API key
19+
plugin = opensea(OpenSeaPluginOptions(api_key="your-api-key"))
20+
21+
# Get NFT collection statistics
22+
stats = await plugin.get_nft_collection_statistics({"collectionSlug": "collection-slug"})
23+
24+
# Get recent NFT sales
25+
sales = await plugin.get_nft_sales({"collectionSlug": "collection-slug"})
26+
```
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 OpenSeaService
4+
5+
6+
@dataclass
7+
class OpenSeaPluginOptions:
8+
api_key: str
9+
10+
11+
class OpenSeaPlugin(PluginBase):
12+
def __init__(self, options: OpenSeaPluginOptions):
13+
super().__init__("opensea", [OpenSeaService(options.api_key)])
14+
15+
def supports_chain(self, chain) -> bool:
16+
return True
17+
18+
19+
def opensea(options: OpenSeaPluginOptions) -> OpenSeaPlugin:
20+
return OpenSeaPlugin(options)
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
from pydantic import BaseModel, Field
2+
from typing import List, Optional
3+
4+
5+
class GetNftCollectionStatisticsParameters(BaseModel):
6+
collectionSlug: str = Field(
7+
...,
8+
description="The slug of the NFT collection on OpenSea"
9+
)
10+
11+
12+
class GetNftSalesParameters(BaseModel):
13+
collectionSlug: str = Field(
14+
...,
15+
description="The slug of the NFT collection on OpenSea"
16+
)
17+
18+
19+
class NftCollectionStatisticsTotal(BaseModel):
20+
volume: float
21+
sales: int
22+
average_price: float
23+
num_owners: int
24+
market_cap: float
25+
floor_price: float
26+
floor_price_symbol: str
27+
28+
29+
class NftCollectionStatisticsInterval(BaseModel):
30+
interval: str
31+
volume: float
32+
volume_diff: float
33+
volume_change: float
34+
sales: int
35+
sales_diff: int
36+
average_price: float
37+
38+
39+
class NftCollectionStatisticsResponse(BaseModel):
40+
total: NftCollectionStatisticsTotal
41+
intervals: List[NftCollectionStatisticsInterval]
42+
43+
44+
class NftDetails(BaseModel):
45+
identifier: str
46+
collection: str
47+
contract: str
48+
token_standard: str
49+
name: str
50+
description: str
51+
image_url: str
52+
display_image_url: str
53+
display_animation_url: Optional[str]
54+
metadata_url: str
55+
opensea_url: str
56+
updated_at: str
57+
is_disabled: bool
58+
is_nsfw: bool
59+
60+
61+
class PaymentDetails(BaseModel):
62+
quantity: str
63+
token_address: str
64+
decimals: int
65+
symbol: str
66+
67+
68+
class NftSaleEvent(BaseModel):
69+
event_type: str
70+
order_hash: str
71+
chain: str
72+
protocol_address: str
73+
closing_date: int
74+
nft: NftDetails
75+
quantity: int
76+
seller: str
77+
buyer: str
78+
payment: PaymentDetails
79+
transaction: str
80+
event_timestamp: int
81+
82+
83+
class NftSalesResponse(BaseModel):
84+
asset_events: List[NftSaleEvent]
85+
next: str
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import aiohttp
2+
from goat.decorators.tool import Tool
3+
from .parameters import (
4+
GetNftCollectionStatisticsParameters,
5+
GetNftSalesParameters,
6+
NftCollectionStatisticsResponse,
7+
NftSalesResponse,
8+
)
9+
10+
11+
class OpenSeaService:
12+
def __init__(self, api_key: str):
13+
self.api_key = api_key
14+
self.base_url = "https://api.opensea.io/api/v2"
15+
16+
@Tool({
17+
"description": "Get NFT collection statistics",
18+
"parameters_schema": GetNftCollectionStatisticsParameters
19+
})
20+
async def get_nft_collection_statistics(self, parameters: dict) -> NftCollectionStatisticsResponse:
21+
"""Get statistics for an NFT collection from OpenSea"""
22+
async with aiohttp.ClientSession() as session:
23+
url = f"{self.base_url}/collections/{parameters['collectionSlug']}/stats"
24+
headers = {
25+
"accept": "application/json",
26+
"x-api-key": self.api_key
27+
}
28+
async with session.get(url, headers=headers) as response:
29+
if not response.ok:
30+
raise Exception(f"Failed to get NFT collection statistics: HTTP {response.status} - {await response.text()}")
31+
data = await response.json()
32+
return NftCollectionStatisticsResponse.model_validate(data)
33+
34+
@Tool({
35+
"description": "Get recent NFT sales",
36+
"parameters_schema": GetNftSalesParameters
37+
})
38+
async def get_nft_sales(self, parameters: dict) -> list:
39+
"""Get recent NFT sales for a collection from OpenSea"""
40+
async with aiohttp.ClientSession() as session:
41+
url = f"{self.base_url}/events/collection/{parameters['collectionSlug']}?event_type=sale&limit=5"
42+
headers = {
43+
"accept": "application/json",
44+
"x-api-key": self.api_key
45+
}
46+
async with session.get(url, headers=headers) as response:
47+
if not response.ok:
48+
raise Exception(f"Failed to get NFT sales: HTTP {response.status} - {await response.text()}")
49+
data = await response.json()
50+
sales_response = NftSalesResponse.model_validate(data)
51+
52+
# Transform the response to match TypeScript implementation
53+
return [{
54+
"name": event.nft.name,
55+
"seller": event.seller,
56+
"buyer": event.buyer,
57+
"price": float(event.payment.quantity) / 10 ** event.payment.decimals
58+
} for event in sales_response.asset_events]
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[virtualenvs]
2+
in-project = true
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
[tool.poetry]
2+
name = "goat-sdk-plugin-opensea"
3+
version = "0.1.0"
4+
description = "Goat plugin for OpenSea"
5+
authors = ["Devin <[email protected]>"]
6+
readme = "README.md"
7+
keywords = ["goat", "sdk", "web3", "agents", "ai", "nft", "opensea"]
8+
homepage = "https://ohmygoat.dev/"
9+
repository = "https://github.com/goat-sdk/goat"
10+
packages = [
11+
{ include = "goat_plugins/opensea" },
12+
]
13+
14+
[tool.poetry.dependencies]
15+
python = "^3.10"
16+
aiohttp = "^3.8.6"
17+
goat-sdk = "^0.1.1"
18+
19+
[tool.poetry.group.test.dependencies]
20+
pytest = "^8.3.4"
21+
pytest-asyncio = "^0.25.0"
22+
23+
[tool.poetry.urls]
24+
"Bug Tracker" = "https://github.com/goat-sdk/goat/issues"
25+
26+
[tool.pytest.ini_options]
27+
addopts = [
28+
"--import-mode=importlib",
29+
]
30+
pythonpath = "src"
31+
asyncio_default_fixture_loop_scope = "function"
32+
33+
[build-system]
34+
requires = ["poetry-core"]
35+
build-backend = "poetry.core.masonry.api"
36+
37+
[tool.poetry.group.dev.dependencies]
38+
ruff = "^0.8.6"
39+
goat-sdk = { path = "../../goat-sdk", develop = true }
40+
41+
[tool.ruff]
42+
line-length = 120
43+
target-version = "py312"

0 commit comments

Comments
 (0)