-
Notifications
You must be signed in to change notification settings - Fork 8.4k
Add AgentTollSafetyTool: honeypot/rug checks for a Base token via x402 (#7227) #7228
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| # AgentTollSafetyTool Documentation | ||
|
|
||
| ## Description | ||
|
|
||
| This tool checks whether a Base (Coinbase's L2) token contract is a honeypot or a rug | ||
| before you trade it or recommend it. It calls | ||
| [AgentToll](https://agenttoll.app)'s `/api/base/safety` endpoint, which runs a | ||
| simulated buy **and** sell, checks buy/sell tax, owner privileges, holder | ||
| concentration, liquidity risk, and the deployer's own history — a token shipped from a | ||
| wallet with a handful of transactions and dust is the shape most rugs share. | ||
|
|
||
| There is no API key and no subscription. Every call is paid for inline, in USDC on | ||
| Base, via the [x402 protocol](https://x402.org) (HTTP 402) — the agent's wallet pays | ||
| $0.003 per call, and only once the response comes back successfully. | ||
|
|
||
| ## Installation | ||
|
|
||
| ```shell | ||
| uv add crewai-tools --extra x402 | ||
| # or | ||
| pip install 'crewai[tools]' 'x402[requests,evm]' | ||
| ``` | ||
|
|
||
| ## Example | ||
|
|
||
| ```python | ||
| from crewai_tools import AgentTollSafetyTool | ||
|
|
||
| # EVM_PRIVATE_KEY must be set to a Base wallet holding a little USDC | ||
| tool = AgentTollSafetyTool() | ||
|
|
||
| result = tool.run(address="0x940181a94a35a4569e4529a3cdfb74e38fd98631") | ||
| ``` | ||
|
|
||
| ## Steps to Get Started | ||
|
|
||
| 1. **Package installation**: install the `x402` extra as shown above. | ||
| 2. **Wallet funding**: the wallet behind `EVM_PRIVATE_KEY` needs a small amount of USDC | ||
| on Base mainnet (each call costs $0.003). To test without real funds, pass | ||
| `base_url="http://localhost:4021"` when constructing the tool to point at a | ||
| self-hosted AgentToll instance running on Base Sepolia, and fund the wallet with | ||
| free testnet USDC from [faucet.circle.com](https://faucet.circle.com). | ||
| 3. **Environment configuration**: `EVM_PRIVATE_KEY=0x...` | ||
|
|
||
| ## Arguments | ||
|
|
||
| | Argument | Type | Description | | ||
| |---|---|---| | ||
| | `address` | `str` | Base token contract address to check, e.g. `0x1234...` | | ||
|
|
||
| ## Conclusion | ||
|
|
||
| `AgentTollSafetyTool` gives an agent a real safety verdict on a Base token — clear, | ||
| caution, high-risk, or insufficient-data — without an account, an API key, or a | ||
| subscription: the agent pays for exactly the checks it runs, and nothing when a call | ||
| fails. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| import os | ||
| from typing import Any | ||
|
|
||
| from crewai.tools import BaseTool, EnvVar | ||
| from pydantic import BaseModel, Field | ||
|
|
||
|
|
||
| class AgentTollSafetyToolInput(BaseModel): | ||
| """Input for AgentTollSafetyTool.""" | ||
|
|
||
| address: str = Field( | ||
| ..., | ||
| description="Base (chain id 8453) token contract address to check, e.g. '0x1234...'", | ||
| ) | ||
|
|
||
|
|
||
| class AgentTollSafetyTool(BaseTool): | ||
| """ | ||
| AgentTollSafetyTool - checks whether a Base token is a honeypot or rug before you trade it. | ||
|
|
||
| Calls AgentToll's /api/base/safety endpoint (https://agenttoll.app): a simulated buy | ||
| AND sell, taxes, owner privileges, holder concentration, liquidity risk, and the | ||
| deployer's own history. Paid per call in USDC on Base via the x402 protocol (HTTP 402) | ||
| -- no API key, no subscription, no signup. Costs $0.003, charged only once the | ||
| response comes back successfully. | ||
|
|
||
| Dependencies: | ||
| - x402[requests,evm] | ||
| """ | ||
|
|
||
| name: str = "Check Base token safety" | ||
| description: str = ( | ||
| "Check whether a Base token contract is a honeypot or rug: simulated buy and " | ||
| "sell, taxes, owner privileges, holder concentration, liquidity risk, and the " | ||
| "deployer's history. Use before trading or recommending an unfamiliar Base token." | ||
| ) | ||
| args_schema: type[BaseModel] = AgentTollSafetyToolInput | ||
| base_url: str = "https://agenttoll.app" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- scoped conventions ---'
find /tmp/coderabbit-repo-knowledge/crewaiinc-crewai-a921f2d1 -type f -name '*.md' -print
printf '%s\n' '--- tool source ---'
sed -n '1,115p' lib/crewai-tools/src/crewai_tools/tools/agenttoll_safety_tool/agenttoll_safety_tool.py
printf '%s\n' '--- README excerpt ---'
sed -n '1,70p' lib/crewai-tools/src/crewai_tools/tools/agenttoll_safety_tool/README.mdRepository: crewAIInc/crewAI Length of output: 7490 🌐 Web query:
💡 Result: The x402 protocol is an open standard that utilizes the HTTP 402 Payment Required status code to enable programmatic, account-less payments for API access and web resources [1][2]. The official Python SDK for x402 (package name Citations:
Sensitive Data Exposure (CWE-319): Cleartext Transmission of Sensitive Information Exploitability: Moderate Require HTTPS for automatic x402 payments. Reject non-HTTPS 📍 Affects 2 files
🤖 Prompt for AI Agents |
||
|
|
||
| env_vars: list[EnvVar] = Field( | ||
| default_factory=lambda: [ | ||
| EnvVar( | ||
| name="EVM_PRIVATE_KEY", | ||
| description=( | ||
| "Private key of a Base wallet holding a little USDC, used to pay " | ||
| "$0.003 per call via x402" | ||
| ), | ||
| required=True, | ||
| ), | ||
| ] | ||
| ) | ||
| package_dependencies: list[str] = Field(default_factory=lambda: ["x402"]) | ||
|
|
||
| def __init__(self, *args: Any, **kwargs: Any) -> None: | ||
| super().__init__(*args, **kwargs) | ||
| try: | ||
| import x402 # noqa: F401 | ||
| except ImportError as exc: | ||
| raise ImportError( | ||
| "Missing optional dependency 'x402'. Install with: \n" | ||
| " uv add crewai-tools --extra x402\n" | ||
| "or\n" | ||
| " pip install 'x402[requests,evm]'\n" | ||
| ) from exc | ||
|
|
||
| if "EVM_PRIVATE_KEY" not in os.environ: | ||
| raise ValueError( | ||
| "Environment variable EVM_PRIVATE_KEY is required for AgentTollSafetyTool" | ||
| ) | ||
|
|
||
| def _paid_session(self) -> Any: | ||
| """A requests session that pays x402 quotes automatically, capped per call.""" | ||
| from eth_account import Account | ||
| from x402 import x402ClientSync | ||
| from x402.http.clients import x402_requests | ||
| from x402.mechanisms.evm import EthAccountSigner | ||
| from x402.mechanisms.evm.exact.register import register_exact_evm_client | ||
|
|
||
| client = x402ClientSync().set_spend_controls( | ||
| {"max_amount_per_payment": "$0.05"} | ||
| ) | ||
| account = Account.from_key(os.environ["EVM_PRIVATE_KEY"]) | ||
| register_exact_evm_client(client, EthAccountSigner(account)) | ||
| return x402_requests(client) | ||
|
|
||
| def _run(self, address: str) -> str: | ||
| try: | ||
| with self._paid_session() as session: | ||
| response = session.get(f"{self.base_url}/api/base/safety/{address}") | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
set -euo pipefail
# Resolve the installed x402 version from the repository lockfile.
fd -HI '^uv\.lock$' . -x rg -n -C3 '^name = "x402"$' {}
# Confirm that the resolved x402 Requests wrapper forwards `timeout` to both
# the initial request and the x402 payment retry before applying the change.Repository: crewAIInc/crewAI Length of output: 154 🏁 Script executed: set -euo pipefail
printf '%s\n' '--- candidate files ---'
fd -HI '(^uv\.lock$|agenttoll_safety_tool\.py$|.*crewai-tools.*)' . | head -80
printf '%s\n' '--- review conventions ---'
find /tmp/coderabbit-repo-knowledge/crewaiinc-crewai-a921f2d1 -maxdepth 2 -type f -name '*.md' -print
printf '%s\n' '--- tool source ---'
cat -n lib/crewai-tools/src/crewai_tools/tools/agenttoll_safety_tool/agenttoll_safety_tool.py
printf '%s\n' '--- dependency declarations ---'
rg -n -C3 'x402|requests' pyproject.toml uv.lock lib/crewai-tools 2>/dev/null | head -160Repository: crewAIInc/crewAI Length of output: 14270 🏁 Script executed: set -euo pipefail
printf '%s\n' '--- applicable repository guidance ---'
cat /tmp/coderabbit-repo-knowledge/crewaiinc-crewai-a921f2d1/conventions/lib-crewai-tools-src.md 2>/dev/null || true
cat /tmp/coderabbit-repo-knowledge/crewaiinc-crewai-a921f2d1/learnings/lib-crewai-tools-src.md 2>/dev/null || true
printf '%s\n' '--- x402 references and package metadata ---'
rg -n -C4 'x402|AgentTollSafetyTool|agenttoll' lib/crewai-tools pyproject.toml uv.lock README.md 2>/dev/null | head -240
printf '%s\n' '--- lockfile package headers near any x402-like entries ---'
rg -n -i -C3 'name = ".*402.*"|name = "requests"' uv.lock | head -180Repository: crewAIInc/crewAI Length of output: 33232 🌐 Web query:
💡 Result: The x402 Python SDK provides support for the Requests library through the Citations:
🌐 Web query:
💡 Result: In the x402 Python SDK, x402_requests provides a synchronized HTTP client wrapper designed to handle X402 payment flows [1][2]. While x402_requests does not have a single dedicated "timeout" parameter on the helper function itself, you can manage timeouts by utilizing the underlying requests.Session object [3] or by configuring the hooks that perform the payment operations [2]. In the v2 Python SDK, the library was refactored to simplify API usage, and specifically, hook_timeout_seconds configuration was introduced [2]. By default, this value is None (opt-in), which allows you to define per-route timeouts for payment hooks [2]. For more complex requirements, especially when dealing with flaky networks or specific latency needs, developers have identified that the underlying FacilitatorClient (often used in conjunction with these clients) may require custom dependency injection [4]. You can instantiate a custom HTTP client (such as httpx.AsyncClient or a configured requests.Session) with your specific timeout settings and pass it to the relevant components to ensure connection reuse and controlled timeout behavior [4]. For implementation details, refer to the x402 Python SDK examples in the official repository, which demonstrate how to initialize sessions and customize payment selectors [1][3]. Citations:
Set a finite timeout for the paid request.
🤖 Prompt for AI Agents |
||
| response.raise_for_status() | ||
| return str(response.text) | ||
| except Exception as e: | ||
| return f"Error checking token safety: {e}" | ||
|
|
||
| async def _arun(self, *args: Any, **kwargs: Any) -> str: | ||
| return self._run(*args, **kwargs) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win Do not run synchronous payment I/O on the event loop.
🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| from unittest.mock import MagicMock, patch | ||
|
|
||
| from crewai_tools.tools.agenttoll_safety_tool.agenttoll_safety_tool import ( | ||
| AgentTollSafetyTool, | ||
| ) | ||
| import pytest | ||
|
|
||
|
|
||
| DUMMY_KEY = "0x" + "11" * 32 # syntactically valid, never funded | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def agenttoll_tool(monkeypatch): | ||
| monkeypatch.setenv("EVM_PRIVATE_KEY", DUMMY_KEY) | ||
| return AgentTollSafetyTool() | ||
|
|
||
|
|
||
| def test_requires_env_var(monkeypatch): | ||
| monkeypatch.delenv("EVM_PRIVATE_KEY", raising=False) | ||
| with pytest.raises(ValueError): | ||
| AgentTollSafetyTool() | ||
|
|
||
|
|
||
| def test_happy_path(agenttoll_tool): | ||
| mock_response = MagicMock() | ||
| mock_response.text = '{"verdict": "clear"}' | ||
| mock_response.raise_for_status = MagicMock() | ||
|
|
||
| mock_session = MagicMock() | ||
| mock_session.__enter__.return_value = mock_session | ||
| mock_session.__exit__.return_value = False | ||
| mock_session.get.return_value = mock_response | ||
|
|
||
| with patch.object(AgentTollSafetyTool, "_paid_session", return_value=mock_session): | ||
| result = agenttoll_tool.run(address="0x940181a94a35a4569e4529a3cdfb74e38fd98631") | ||
|
|
||
| assert "clear" in result | ||
| mock_session.get.assert_called_once_with( | ||
| "https://agenttoll.app/api/base/safety/0x940181a94a35a4569e4529a3cdfb74e38fd98631" | ||
| ) | ||
|
|
||
|
|
||
| def test_error_is_returned_not_raised(agenttoll_tool): | ||
| with patch.object(AgentTollSafetyTool, "_paid_session", side_effect=RuntimeError("boom")): | ||
| result = agenttoll_tool.run(address="0x940181a94a35a4569e4529a3cdfb74e38fd98631") | ||
|
|
||
| assert "Error checking token safety" in result | ||
| assert "boom" in result |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Do not promise that a failed call cannot incur a payment.
x402 settlement occurs before the resource server returns its final success response. A failure after settlement can make
_runreturn an error even though payment occurred. State that callers may need to inspect payment or chain status after failures. (github.com)lib/crewai-tools/src/crewai_tools/tools/agenttoll_safety_tool/README.md#L12-L14: remove the unconditional successful-response charging guarantee.lib/crewai-tools/src/crewai_tools/tools/agenttoll_safety_tool/README.md#L53-L56: remove the claim that failed calls cost nothing.📍 Affects 1 file
lib/crewai-tools/src/crewai_tools/tools/agenttoll_safety_tool/README.md#L12-L14(this comment)lib/crewai-tools/src/crewai_tools/tools/agenttoll_safety_tool/README.md#L53-L56🤖 Prompt for AI Agents