-
Notifications
You must be signed in to change notification settings - Fork 163
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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]>
- Loading branch information
1 parent
7d63e9e
commit c34bf16
Showing
6 changed files
with
234 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# OpenSea Plugin for GOAT SDK | ||
|
||
This plugin provides integration with the OpenSea API for the GOAT SDK, allowing you to: | ||
- Get NFT collection statistics | ||
- Get recent NFT sales | ||
|
||
## Installation | ||
|
||
```bash | ||
pip install goat-sdk-plugin-opensea | ||
``` | ||
|
||
## Usage | ||
|
||
```python | ||
from goat_plugins.opensea import opensea, OpenSeaPluginOptions | ||
|
||
# Initialize the plugin with your API key | ||
plugin = opensea(OpenSeaPluginOptions(api_key="your-api-key")) | ||
|
||
# Get NFT collection statistics | ||
stats = await plugin.get_nft_collection_statistics({"collectionSlug": "collection-slug"}) | ||
|
||
# Get recent NFT sales | ||
sales = await plugin.get_nft_sales({"collectionSlug": "collection-slug"}) | ||
``` |
20 changes: 20 additions & 0 deletions
20
python/src/plugins/opensea/goat_plugins/opensea/__init__.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
from dataclasses import dataclass | ||
from goat.classes.plugin_base import PluginBase | ||
from .service import OpenSeaService | ||
|
||
|
||
@dataclass | ||
class OpenSeaPluginOptions: | ||
api_key: str | ||
|
||
|
||
class OpenSeaPlugin(PluginBase): | ||
def __init__(self, options: OpenSeaPluginOptions): | ||
super().__init__("opensea", [OpenSeaService(options.api_key)]) | ||
|
||
def supports_chain(self, chain) -> bool: | ||
return True | ||
|
||
|
||
def opensea(options: OpenSeaPluginOptions) -> OpenSeaPlugin: | ||
return OpenSeaPlugin(options) |
85 changes: 85 additions & 0 deletions
85
python/src/plugins/opensea/goat_plugins/opensea/parameters.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
from pydantic import BaseModel, Field | ||
from typing import List, Optional | ||
|
||
|
||
class GetNftCollectionStatisticsParameters(BaseModel): | ||
collectionSlug: str = Field( | ||
..., | ||
description="The slug of the NFT collection on OpenSea" | ||
) | ||
|
||
|
||
class GetNftSalesParameters(BaseModel): | ||
collectionSlug: str = Field( | ||
..., | ||
description="The slug of the NFT collection on OpenSea" | ||
) | ||
|
||
|
||
class NftCollectionStatisticsTotal(BaseModel): | ||
volume: float | ||
sales: int | ||
average_price: float | ||
num_owners: int | ||
market_cap: float | ||
floor_price: float | ||
floor_price_symbol: str | ||
|
||
|
||
class NftCollectionStatisticsInterval(BaseModel): | ||
interval: str | ||
volume: float | ||
volume_diff: float | ||
volume_change: float | ||
sales: int | ||
sales_diff: int | ||
average_price: float | ||
|
||
|
||
class NftCollectionStatisticsResponse(BaseModel): | ||
total: NftCollectionStatisticsTotal | ||
intervals: List[NftCollectionStatisticsInterval] | ||
|
||
|
||
class NftDetails(BaseModel): | ||
identifier: str | ||
collection: str | ||
contract: str | ||
token_standard: str | ||
name: str | ||
description: str | ||
image_url: str | ||
display_image_url: str | ||
display_animation_url: Optional[str] | ||
metadata_url: str | ||
opensea_url: str | ||
updated_at: str | ||
is_disabled: bool | ||
is_nsfw: bool | ||
|
||
|
||
class PaymentDetails(BaseModel): | ||
quantity: str | ||
token_address: str | ||
decimals: int | ||
symbol: str | ||
|
||
|
||
class NftSaleEvent(BaseModel): | ||
event_type: str | ||
order_hash: str | ||
chain: str | ||
protocol_address: str | ||
closing_date: int | ||
nft: NftDetails | ||
quantity: int | ||
seller: str | ||
buyer: str | ||
payment: PaymentDetails | ||
transaction: str | ||
event_timestamp: int | ||
|
||
|
||
class NftSalesResponse(BaseModel): | ||
asset_events: List[NftSaleEvent] | ||
next: str |
58 changes: 58 additions & 0 deletions
58
python/src/plugins/opensea/goat_plugins/opensea/service.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import aiohttp | ||
from goat.decorators.tool import Tool | ||
from .parameters import ( | ||
GetNftCollectionStatisticsParameters, | ||
GetNftSalesParameters, | ||
NftCollectionStatisticsResponse, | ||
NftSalesResponse, | ||
) | ||
|
||
|
||
class OpenSeaService: | ||
def __init__(self, api_key: str): | ||
self.api_key = api_key | ||
self.base_url = "https://api.opensea.io/api/v2" | ||
|
||
@Tool({ | ||
"description": "Get NFT collection statistics", | ||
"parameters_schema": GetNftCollectionStatisticsParameters | ||
}) | ||
async def get_nft_collection_statistics(self, parameters: dict) -> NftCollectionStatisticsResponse: | ||
"""Get statistics for an NFT collection from OpenSea""" | ||
async with aiohttp.ClientSession() as session: | ||
url = f"{self.base_url}/collections/{parameters['collectionSlug']}/stats" | ||
headers = { | ||
"accept": "application/json", | ||
"x-api-key": self.api_key | ||
} | ||
async with session.get(url, headers=headers) as response: | ||
if not response.ok: | ||
raise Exception(f"Failed to get NFT collection statistics: HTTP {response.status} - {await response.text()}") | ||
data = await response.json() | ||
return NftCollectionStatisticsResponse.model_validate(data) | ||
|
||
@Tool({ | ||
"description": "Get recent NFT sales", | ||
"parameters_schema": GetNftSalesParameters | ||
}) | ||
async def get_nft_sales(self, parameters: dict) -> list: | ||
"""Get recent NFT sales for a collection from OpenSea""" | ||
async with aiohttp.ClientSession() as session: | ||
url = f"{self.base_url}/events/collection/{parameters['collectionSlug']}?event_type=sale&limit=5" | ||
headers = { | ||
"accept": "application/json", | ||
"x-api-key": self.api_key | ||
} | ||
async with session.get(url, headers=headers) as response: | ||
if not response.ok: | ||
raise Exception(f"Failed to get NFT sales: HTTP {response.status} - {await response.text()}") | ||
data = await response.json() | ||
sales_response = NftSalesResponse.model_validate(data) | ||
|
||
# Transform the response to match TypeScript implementation | ||
return [{ | ||
"name": event.nft.name, | ||
"seller": event.seller, | ||
"buyer": event.buyer, | ||
"price": float(event.payment.quantity) / 10 ** event.payment.decimals | ||
} for event in sales_response.asset_events] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[virtualenvs] | ||
in-project = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
[tool.poetry] | ||
name = "goat-sdk-plugin-opensea" | ||
version = "0.1.0" | ||
description = "Goat plugin for OpenSea" | ||
authors = ["Devin <[email protected]>"] | ||
readme = "README.md" | ||
keywords = ["goat", "sdk", "web3", "agents", "ai", "nft", "opensea"] | ||
homepage = "https://ohmygoat.dev/" | ||
repository = "https://github.com/goat-sdk/goat" | ||
packages = [ | ||
{ include = "goat_plugins/opensea" }, | ||
] | ||
|
||
[tool.poetry.dependencies] | ||
python = "^3.10" | ||
aiohttp = "^3.8.6" | ||
goat-sdk = "^0.1.1" | ||
|
||
[tool.poetry.group.test.dependencies] | ||
pytest = "^8.3.4" | ||
pytest-asyncio = "^0.25.0" | ||
|
||
[tool.poetry.urls] | ||
"Bug Tracker" = "https://github.com/goat-sdk/goat/issues" | ||
|
||
[tool.pytest.ini_options] | ||
addopts = [ | ||
"--import-mode=importlib", | ||
] | ||
pythonpath = "src" | ||
asyncio_default_fixture_loop_scope = "function" | ||
|
||
[build-system] | ||
requires = ["poetry-core"] | ||
build-backend = "poetry.core.masonry.api" | ||
|
||
[tool.poetry.group.dev.dependencies] | ||
ruff = "^0.8.6" | ||
goat-sdk = { path = "../../goat-sdk", develop = true } | ||
|
||
[tool.ruff] | ||
line-length = 120 | ||
target-version = "py312" |