Skip to content

Commit

Permalink
feat: Add Superfluid plugin for Python SDK (#223)
Browse files Browse the repository at this point in the history
* feat: Add Superfluid plugin for Python SDK\n\n- Implement Superfluid plugin matching TypeScript functionality\n- Add flow management and pool operations\n- Fix Web3EVMWalletClient transaction hash handling\n- Include comprehensive parameter validation

Co-Authored-By: Agus Armellini Fischer <[email protected]>

* Delete python/src/plugins/superfluid/__init__.py

* Update wallet.py

* Remove additional flow validations and approvals to match TypeScript exactly

Co-Authored-By: Agus Armellini Fischer <[email protected]>

---------

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
3 people authored Jan 15, 2025
1 parent 035fe58 commit 3cbe996
Show file tree
Hide file tree
Showing 7 changed files with 827 additions and 0 deletions.
36 changes: 36 additions & 0 deletions python/src/plugins/superfluid/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Superfluid Plugin for GOAT SDK

This plugin provides integration with Superfluid Protocol for the GOAT SDK, enabling token streaming and pool management capabilities.

## Features

- Create, update, or delete token streams
- Get flow rates between addresses
- Manage pool memberships
- Query pool statistics

## Installation

```bash
poetry add goat-sdk-plugin-superfluid
```

## Usage

```python
from goat_plugins.superfluid import superfluid, SuperfluidPluginOptions

# Initialize the plugin
plugin = superfluid()
```

## Tools

The plugin provides the following tools:

- `flow`: Create, update, or delete a flow of tokens
- `get_flowrate`: Get the current flowrate between addresses
- `update_member_units`: Update units for a pool member
- `get_units`: Get units of a pool member
- `get_member_flow_rate`: Get flow rate of a pool member
- `get_total_flow_rate`: Get total flow rate of a pool
36 changes: 36 additions & 0 deletions python/src/plugins/superfluid/goat_plugins/superfluid/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from dataclasses import dataclass
from typing import Optional

from goat.classes.plugin_base import PluginBase
from goat.types.chain import Chain
from .service import SuperfluidService


@dataclass
class SuperfluidPluginOptions:
"""
Configuration options for the Superfluid plugin.
Currently no configuration is needed.
"""
pass


class SuperfluidPlugin(PluginBase):
def __init__(self, options: Optional[SuperfluidPluginOptions] = None):
super().__init__("superfluid", [SuperfluidService()])

def supports_chain(self, chain: Chain) -> bool:
return chain["type"] == "evm"


def superfluid(options: Optional[SuperfluidPluginOptions] = None) -> SuperfluidPlugin:
"""
Create a new instance of the Superfluid plugin.
Args:
options: Optional configuration options for the plugin
Returns:
A configured SuperfluidPlugin instance
"""
return SuperfluidPlugin(options)
155 changes: 155 additions & 0 deletions python/src/plugins/superfluid/goat_plugins/superfluid/abi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
CFA_FORWARDER_ABI = [
{
"inputs": [
{
"internalType": "contract ISuperfluid",
"name": "host",
"type": "address",
},
],
"stateMutability": "nonpayable",
"type": "constructor",
},
{"inputs": [], "name": "CFA_FWD_INVALID_FLOW_RATE", "type": "error"},
{
"inputs": [
{
"internalType": "contract ISuperToken",
"name": "token",
"type": "address",
},
{"internalType": "address", "name": "sender", "type": "address"},
{"internalType": "address", "name": "receiver", "type": "address"},
{"internalType": "int96", "name": "flowrate", "type": "int96"},
{"internalType": "bytes", "name": "userData", "type": "bytes"},
],
"name": "createFlow",
"outputs": [{"internalType": "bool", "name": "", "type": "bool"}],
"stateMutability": "nonpayable",
"type": "function",
},
{
"inputs": [
{
"internalType": "contract ISuperToken",
"name": "token",
"type": "address",
},
{"internalType": "address", "name": "sender", "type": "address"},
{"internalType": "address", "name": "receiver", "type": "address"},
{"internalType": "bytes", "name": "userData", "type": "bytes"},
],
"name": "deleteFlow",
"outputs": [{"internalType": "bool", "name": "", "type": "bool"}],
"stateMutability": "nonpayable",
"type": "function",
},
{
"inputs": [
{
"internalType": "contract ISuperToken",
"name": "token",
"type": "address",
},
{"internalType": "address", "name": "sender", "type": "address"},
{"internalType": "address", "name": "receiver", "type": "address"},
],
"name": "getFlowrate",
"outputs": [{"internalType": "int96", "name": "flowrate", "type": "int96"}],
"stateMutability": "view",
"type": "function",
},
{
"inputs": [
{
"internalType": "contract ISuperToken",
"name": "token",
"type": "address",
},
{"internalType": "address", "name": "receiver", "type": "address"},
{"internalType": "int96", "name": "flowrate", "type": "int96"},
],
"name": "setFlowrate",
"outputs": [{"internalType": "bool", "name": "", "type": "bool"}],
"stateMutability": "nonpayable",
"type": "function",
},
]

POOL_ABI = [
{
"inputs": [
{
"internalType": "address",
"name": "memberAddr",
"type": "address",
},
{
"internalType": "uint128",
"name": "newUnits",
"type": "uint128",
},
],
"name": "updateMemberUnits",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool",
},
],
"stateMutability": "nonpayable",
"type": "function",
},
{
"inputs": [
{
"internalType": "address",
"name": "memberAddr",
"type": "address",
},
],
"name": "getUnits",
"outputs": [
{
"internalType": "uint128",
"name": "",
"type": "uint128",
},
],
"stateMutability": "view",
"type": "function",
},
{
"inputs": [
{
"internalType": "address",
"name": "memberAddr",
"type": "address",
},
],
"name": "getMemberFlowRate",
"outputs": [
{
"internalType": "int96",
"name": "",
"type": "int96",
},
],
"stateMutability": "view",
"type": "function",
},
{
"inputs": [],
"name": "getTotalFlowRate",
"outputs": [
{
"internalType": "int96",
"name": "",
"type": "int96",
},
],
"stateMutability": "view",
"type": "function",
},
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from pydantic import BaseModel, Field


class FlowParameters(BaseModel):
token: str = Field(description="The address of the Super Token to get the flow of")
receiver: str = Field(description="The address of the receiver of the flow")
flowrate: str = Field(description="The flowrate of the flow in wei per second (must be a valid int96 value)")


class GetFlowrateParameters(BaseModel):
token: str = Field(description="The address of the Super Token to get the flow of")
sender: str = Field(description="The address of the sender of the flow")
receiver: str = Field(description="The address of the receiver of the flow")


class UpdateMemberUnitsParameters(BaseModel):
poolAddress: str = Field(description="The address of the Pool contract")
memberAddr: str = Field(description="The address of the member to update units for")
newUnits: int = Field(description="The new units amount for the member")


class GetUnitsParameters(BaseModel):
poolAddress: str = Field(description="The address of the Pool contract")
memberAddr: str = Field(description="The address of the member to get units for")


class GetMemberFlowRateParameters(BaseModel):
poolAddress: str = Field(description="The address of the Pool contract")
memberAddr: str = Field(description="The address of the member to get flow rate for")


class GetTotalFlowRateParameters(BaseModel):
poolAddress: str = Field(description="The address of the Pool contract")
129 changes: 129 additions & 0 deletions python/src/plugins/superfluid/goat_plugins/superfluid/service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
from goat.decorators.tool import Tool
from goat_wallets.evm import EVMWalletClient
from .abi import CFA_FORWARDER_ABI, POOL_ABI
from .parameters import (
FlowParameters,
GetFlowrateParameters,
UpdateMemberUnitsParameters,
GetUnitsParameters,
GetMemberFlowRateParameters,
GetTotalFlowRateParameters,
)


class SuperfluidService:
CFA_FORWARDER_ADDRESS = "0xcfA132E353cB4E398080B9700609bb008eceB125"

@Tool(
{
"name": "create_or_update_or_delete_flow",
"description": "Create, update, or delete a flow of tokens from sender to receiver",
"parameters_schema": FlowParameters,
}
)
def flow(self, wallet_client: EVMWalletClient, parameters: dict) -> str:
try:
result = wallet_client.send_transaction(
{
"to": self.CFA_FORWARDER_ADDRESS,
"abi": CFA_FORWARDER_ABI,
"functionName": "setFlowrate",
"args": [parameters["token"], parameters["receiver"], parameters["flowrate"]],
}
)
return result["hash"]
except Exception as error:
raise Exception(f"Failed to set flow: {error}")

@Tool(
{
"name": "get_flow_rate",
"description": "Get the current flowrate between a sender and receiver for a specific token",
"parameters_schema": GetFlowrateParameters,
}
)
def get_flowrate(self, wallet_client: EVMWalletClient, parameters: dict):
result = wallet_client.read(
{
"address": self.CFA_FORWARDER_ADDRESS,
"abi": CFA_FORWARDER_ABI,
"functionName": "getFlowrate",
"args": [parameters["token"], parameters["sender"], parameters["receiver"]],
}
)
return result["value"]

@Tool(
{
"name": "update_member_units",
"description": "Update the units for a member in a Superfluid Pool",
"parameters_schema": UpdateMemberUnitsParameters,
}
)
def update_member_units(self, wallet_client: EVMWalletClient, parameters: dict):
try:
hash_result = wallet_client.send_transaction(
{
"to": parameters["poolAddress"],
"abi": POOL_ABI,
"functionName": "updateMemberUnits",
"args": [parameters["memberAddr"], parameters["newUnits"]],
}
)
return hash_result["hash"]
except Exception as error:
raise Exception(f"Failed to update member units: {error}")

@Tool(
{
"name": "get_member_units",
"description": "Get the units of a member in a Superfluid Pool",
"parameters_schema": GetUnitsParameters,
}
)
def get_units(self, wallet_client: EVMWalletClient, parameters: dict):
result = wallet_client.read(
{
"address": parameters["poolAddress"],
"abi": POOL_ABI,
"functionName": "getUnits",
"args": [parameters["memberAddr"]],
}
)
return result["value"]

@Tool(
{
"name": "get_member_flow_rate",
"description": "Get the flow rate of a member in a Superfluid Pool",
"parameters_schema": GetMemberFlowRateParameters,
}
)
def get_member_flow_rate(self, wallet_client: EVMWalletClient, parameters: dict):
result = wallet_client.read(
{
"address": parameters["poolAddress"],
"abi": POOL_ABI,
"functionName": "getMemberFlowRate",
"args": [parameters["memberAddr"]],
}
)
return result["value"]

@Tool(
{
"name": "get_total_flow_rate",
"description": "Get the total flow rate of a Superfluid Pool",
"parameters_schema": GetTotalFlowRateParameters,
}
)
def get_total_flow_rate(self, wallet_client: EVMWalletClient, parameters: dict):
result = wallet_client.read(
{
"address": parameters["poolAddress"],
"abi": POOL_ABI,
"functionName": "getTotalFlowRate",
"args": [],
}
)
return result["value"]
Loading

0 comments on commit 3cbe996

Please sign in to comment.