From db8354e1b0779348d99e53f944bd4e99cbf0a707 Mon Sep 17 00:00:00 2001 From: plagtech Date: Sat, 21 Mar 2026 23:42:40 -0700 Subject: [PATCH] feat: add spraay crypto payments agent example --- examples/spraay_crypto_payments/README.md | 110 +++++++++ .../spraay_crypto_payments/configs/config.yml | 63 +++++ .../spraay_crypto_payments/pyproject.toml | 18 ++ .../src/spraay_crypto_payments/__init__.py | 61 +++++ .../spraay_crypto_payments/spraay_tools.py | 233 ++++++++++++++++++ 5 files changed, 485 insertions(+) create mode 100644 examples/spraay_crypto_payments/README.md create mode 100644 examples/spraay_crypto_payments/configs/config.yml create mode 100644 examples/spraay_crypto_payments/pyproject.toml create mode 100644 examples/spraay_crypto_payments/src/spraay_crypto_payments/__init__.py create mode 100644 examples/spraay_crypto_payments/src/spraay_crypto_payments/spraay_tools.py diff --git a/examples/spraay_crypto_payments/README.md b/examples/spraay_crypto_payments/README.md new file mode 100644 index 0000000..6d8a475 --- /dev/null +++ b/examples/spraay_crypto_payments/README.md @@ -0,0 +1,110 @@ +# Spraay Crypto Payments Agent + +An AI agent that executes cryptocurrency payments across 13 blockchains using the [Spraay x402 gateway](https://gateway.spraay.app). The agent can batch-send tokens, create escrow contracts, check balances, get token prices, and hire robots via the Robot Task Protocol (RTP). + +## Overview + +This example demonstrates how to build a **crypto payment agent** using NeMo Agent Toolkit with custom tools that interact with the Spraay x402 protocol gateway. The agent uses a ReAct pattern to reason about payment tasks and execute them via HTTP API calls. + +### What is x402? + +The [x402 protocol](https://www.x402.org) enables AI agents to pay for API services using USDC micropayments over HTTP. When an agent calls a paid endpoint, the server returns HTTP 402 (Payment Required) with payment details. The agent signs a USDC transaction, resends the request with the payment proof, and the server executes the operation. + +### Supported Chains + +Base · Ethereum · Arbitrum · Polygon · BNB Chain · Avalanche · Solana · Bitcoin · Stacks · Unichain · Plasma · BOB · Bittensor + +## Prerequisites + +- Python 3.11+ +- [uv](https://docs.astral.sh/uv/) package manager +- NVIDIA API key from [build.nvidia.com](https://build.nvidia.com) + +## Setup + +1. Clone this repository and navigate to the example: + +```bash +cd examples/spraay_crypto_payments +``` + +2. Install dependencies: + +```bash +uv venv --python 3.12 --seed .venv +source .venv/bin/activate # Linux/Mac +# .venv\Scripts\activate # Windows +uv pip install nvidia-nat httpx +``` + +3. Set environment variables: + +```bash +export NVIDIA_API_KEY= +export SPRAAY_GATEWAY_URL=https://gateway.spraay.app +``` + +## Running the Example + +### Using the CLI + +```bash +nat run --config_file configs/config.yml --input "What chains does Spraay support and what is the current price of ETH on Base?" +``` + +### Example Prompts + +- `"Check the USDC balance for address 0xAd62f03C7514bb8c51f1eA70C2b75C37404695c8 on Base"` +- `"What is the current price of ETH on Base?"` +- `"List all available Spraay gateway routes and their pricing"` +- `"Discover available robots on the RTP network"` + +## Architecture + +``` +┌─────────────────────────────────────────┐ +│ NeMo Agent Toolkit │ +│ │ +│ ┌───────────────────────────────────┐ │ +│ │ ReAct Agent (Nemotron) │ │ +│ │ │ │ +│ │ Tools: │ │ +│ │ ├── spraay_health │ │ +│ │ ├── spraay_routes │ │ +│ │ ├── spraay_chains │ │ +│ │ ├── spraay_balance │ │ +│ │ ├── spraay_price │ │ +│ │ ├── spraay_batch_send │ │ +│ │ ├── spraay_escrow_create │ │ +│ │ └── spraay_rtp_discover │ │ +│ └──────────────┬────────────────────┘ │ +│ │ │ +└─────────────────┼────────────────────────┘ + │ HTTP + x402 + ▼ + ┌────────────────────────┐ + │ Spraay x402 Gateway │ + │ gateway.spraay.app │ + │ │ + │ 76+ paid endpoints │ + │ 13 blockchains │ + │ USDC micropayments │ + └────────────────────────┘ +``` + +## Files + +| File | Description | +|------|-------------| +| `configs/config.yml` | NeMo Agent Toolkit workflow configuration | +| `src/spraay_crypto_payments/spraay_tools.py` | Custom Spraay gateway tools | +| `src/spraay_crypto_payments/__init__.py` | Package init with tool registration | +| `pyproject.toml` | Project dependencies | + +## Links + +- [Spraay Gateway Docs](https://docs.spraay.app) +- [x402 Protocol](https://www.x402.org) +- [Spraay MCP Server](https://smithery.ai/server/@plagtech/spraay-x402-mcp) +- [NeMo Agent Toolkit Docs](https://docs.nvidia.com/nemo/agent-toolkit/latest/) +- [Spraay on OpenShell](https://github.com/NVIDIA/OpenShell-Community/pull/50) diff --git a/examples/spraay_crypto_payments/configs/config.yml b/examples/spraay_crypto_payments/configs/config.yml new file mode 100644 index 0000000..c16854b --- /dev/null +++ b/examples/spraay_crypto_payments/configs/config.yml @@ -0,0 +1,63 @@ +# Spraay Crypto Payments Agent +# NeMo Agent Toolkit workflow configuration +# +# This agent uses the Spraay x402 gateway to execute cryptocurrency +# payments across 13 blockchains via USDC micropayments. + +functions: + # Query tools (free endpoints — no x402 payment required) + spraay_health: + _type: spraay_health + description: "Check the health status of the Spraay x402 gateway" + + spraay_routes: + _type: spraay_routes + description: "List all available Spraay gateway routes with pricing info" + + spraay_chains: + _type: spraay_chains + description: "List all supported blockchains on the Spraay gateway" + + spraay_balance: + _type: spraay_balance + description: "Check the token balance of a wallet address on a specific chain" + + spraay_price: + _type: spraay_price + description: "Get the current price of a token on a specific chain" + + # Action tools (paid endpoints — requires x402 USDC payment) + spraay_batch_send: + _type: spraay_batch_send + description: "Send tokens to multiple recipients in a single batch transaction" + + spraay_escrow_create: + _type: spraay_escrow_create + description: "Create an escrow contract with milestone-based fund releases" + + spraay_rtp_discover: + _type: spraay_rtp_discover + description: "Discover available robots and IoT devices on the RTP network" + +llms: + nim_llm: + _type: nim + model_name: nvidia/nemotron-3-nano-30b-a3b + temperature: 0.0 + chat_template_kwargs: + enable_thinking: false + +workflow: + _type: react_agent + tool_names: + - spraay_health + - spraay_routes + - spraay_chains + - spraay_balance + - spraay_price + - spraay_batch_send + - spraay_escrow_create + - spraay_rtp_discover + llm_name: nim_llm + verbose: true + parse_agent_response_max_retries: 3 diff --git a/examples/spraay_crypto_payments/pyproject.toml b/examples/spraay_crypto_payments/pyproject.toml new file mode 100644 index 0000000..8dd616e --- /dev/null +++ b/examples/spraay_crypto_payments/pyproject.toml @@ -0,0 +1,18 @@ +[project] +name = "spraay-crypto-payments" +version = "0.1.0" +description = "Spraay x402 crypto payment tools for NVIDIA NeMo Agent Toolkit" +readme = "README.md" +license = { text = "Apache-2.0" } +requires-python = ">=3.11,<3.14" +dependencies = [ + "nvidia-nat>=1.5.0", + "httpx>=0.27.0", +] + +[build-system] +requires = ["setuptools>=75.0"] +build-backend = "setuptools.build_meta" + +[tool.setuptools.packages.find] +where = ["src"] diff --git a/examples/spraay_crypto_payments/src/spraay_crypto_payments/__init__.py b/examples/spraay_crypto_payments/src/spraay_crypto_payments/__init__.py new file mode 100644 index 0000000..6fc6319 --- /dev/null +++ b/examples/spraay_crypto_payments/src/spraay_crypto_payments/__init__.py @@ -0,0 +1,61 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. +# SPDX-License-Identifier: Apache-2.0 + +"""Spraay Crypto Payments — NeMo Agent Toolkit plugin. + +Registers Spraay x402 gateway tools for use in NeMo Agent Toolkit workflows. +""" + +from nat.components.functions.tool import tool +from nat.registry import register + +from .spraay_tools import ( + spraay_balance, + spraay_batch_send, + spraay_chains, + spraay_escrow_create, + spraay_health, + spraay_price, + spraay_routes, + spraay_rtp_discover, +) + + +@register("spraay_health") +def _register_health(**kwargs): + return tool(spraay_health, description=kwargs.get("description", "")) + + +@register("spraay_routes") +def _register_routes(**kwargs): + return tool(spraay_routes, description=kwargs.get("description", "")) + + +@register("spraay_chains") +def _register_chains(**kwargs): + return tool(spraay_chains, description=kwargs.get("description", "")) + + +@register("spraay_balance") +def _register_balance(**kwargs): + return tool(spraay_balance, description=kwargs.get("description", "")) + + +@register("spraay_price") +def _register_price(**kwargs): + return tool(spraay_price, description=kwargs.get("description", "")) + + +@register("spraay_batch_send") +def _register_batch_send(**kwargs): + return tool(spraay_batch_send, description=kwargs.get("description", "")) + + +@register("spraay_escrow_create") +def _register_escrow_create(**kwargs): + return tool(spraay_escrow_create, description=kwargs.get("description", "")) + + +@register("spraay_rtp_discover") +def _register_rtp_discover(**kwargs): + return tool(spraay_rtp_discover, description=kwargs.get("description", "")) diff --git a/examples/spraay_crypto_payments/src/spraay_crypto_payments/spraay_tools.py b/examples/spraay_crypto_payments/src/spraay_crypto_payments/spraay_tools.py new file mode 100644 index 0000000..4228afa --- /dev/null +++ b/examples/spraay_crypto_payments/src/spraay_crypto_payments/spraay_tools.py @@ -0,0 +1,233 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. +# SPDX-License-Identifier: Apache-2.0 + +"""Spraay x402 Gateway tools for NeMo Agent Toolkit. + +These tools enable AI agents to execute cryptocurrency payments across +13 blockchains using the Spraay x402 protocol gateway. +""" + +import json +import logging +import os + +import httpx + +logger = logging.getLogger(__name__) + +GATEWAY_URL = os.environ.get("SPRAAY_GATEWAY_URL", "https://gateway.spraay.app") + + +async def _gateway_get(path: str, params: dict | None = None) -> dict: + """Make a GET request to the Spraay gateway.""" + async with httpx.AsyncClient(timeout=30.0) as client: + response = await client.get( + f"{GATEWAY_URL}{path}", + params=params, + headers={"Content-Type": "application/json"}, + ) + response.raise_for_status() + return response.json() + + +async def _gateway_post(path: str, data: dict) -> dict: + """Make a POST request to the Spraay gateway.""" + async with httpx.AsyncClient(timeout=30.0) as client: + response = await client.post( + f"{GATEWAY_URL}{path}", + json=data, + headers={"Content-Type": "application/json"}, + ) + response.raise_for_status() + return response.json() + + +# ── Free query tools (no x402 payment required) ───────────────────────────── + + +async def spraay_health() -> str: + """Check the health status of the Spraay x402 gateway. + + Returns: + JSON string with gateway health status. + """ + try: + result = await _gateway_get("/health") + return json.dumps(result, indent=2) + except Exception as e: + return json.dumps({"error": str(e)}) + + +async def spraay_routes() -> str: + """List all available Spraay gateway routes with pricing information. + + Returns: + JSON string with all available routes and their x402 pricing. + """ + try: + result = await _gateway_get("/v1/routes") + return json.dumps(result, indent=2) + except Exception as e: + return json.dumps({"error": str(e)}) + + +async def spraay_chains() -> str: + """List all supported blockchains on the Spraay gateway. + + Returns: + JSON string with supported chains (Base, Ethereum, Arbitrum, + Polygon, BNB, Avalanche, Solana, Bitcoin, Stacks, Unichain, + Plasma, BOB, Bittensor). + """ + try: + result = await _gateway_get("/v1/chains") + return json.dumps(result, indent=2) + except Exception as e: + return json.dumps({"error": str(e)}) + + +async def spraay_balance(address: str, chain: str = "base", token: str = "USDC") -> str: + """Check the token balance of a wallet address on a specific chain. + + Args: + address: The wallet address to check (e.g., 0x1234...). + chain: The blockchain to query (default: base). + token: The token symbol (default: USDC). + + Returns: + JSON string with the wallet balance. + """ + try: + result = await _gateway_get( + "/v1/balance", + params={"address": address, "chain": chain, "token": token}, + ) + return json.dumps(result, indent=2) + except Exception as e: + return json.dumps({"error": str(e)}) + + +async def spraay_price(token: str, chain: str = "base") -> str: + """Get the current price of a token on a specific chain. + + Args: + token: The token symbol (e.g., ETH, USDC, MATIC). + chain: The blockchain to query (default: base). + + Returns: + JSON string with the current token price. + """ + try: + result = await _gateway_get( + "/v1/price", + params={"token": token, "chain": chain}, + ) + return json.dumps(result, indent=2) + except Exception as e: + return json.dumps({"error": str(e)}) + + +# ── Paid action tools (x402 USDC micropayment required) ───────────────────── + + +async def spraay_batch_send( + recipients: str, + token: str = "USDC", + chain: str = "base", +) -> str: + """Send tokens to multiple recipients in a single batch transaction. + + This is a PAID endpoint — requires x402 USDC micropayment. + + Args: + recipients: JSON string of recipients array, each with + 'address' and 'amount' fields. + Example: '[{"address": "0x...", "amount": "10.0"}]' + token: The token to send (default: USDC). + chain: The blockchain to use (default: base). + + Returns: + JSON string with the transaction result including tx hash. + """ + try: + recipients_list = json.loads(recipients) + result = await _gateway_post( + "/v1/batch-send", + data={ + "recipients": recipients_list, + "token": token, + "chain": chain, + }, + ) + return json.dumps(result, indent=2) + except json.JSONDecodeError: + return json.dumps({"error": "Invalid recipients JSON format"}) + except Exception as e: + return json.dumps({"error": str(e)}) + + +async def spraay_escrow_create( + depositor: str, + beneficiary: str, + total_amount: str, + milestones: str, + token: str = "USDC", + chain: str = "base", +) -> str: + """Create an escrow contract with milestone-based fund releases. + + This is a PAID endpoint — requires x402 USDC micropayment. + + Args: + depositor: Wallet address of the person depositing funds. + beneficiary: Wallet address of the person receiving funds. + total_amount: Total amount to escrow (e.g., "500.0"). + milestones: JSON string of milestones array, each with + 'description' and 'amount' fields. + token: The token to escrow (default: USDC). + chain: The blockchain to use (default: base). + + Returns: + JSON string with the escrow contract details. + """ + try: + milestones_list = json.loads(milestones) + result = await _gateway_post( + "/v1/escrow/create", + data={ + "depositor": depositor, + "beneficiary": beneficiary, + "totalAmount": total_amount, + "milestones": milestones_list, + "token": token, + "chain": chain, + }, + ) + return json.dumps(result, indent=2) + except json.JSONDecodeError: + return json.dumps({"error": "Invalid milestones JSON format"}) + except Exception as e: + return json.dumps({"error": str(e)}) + + +async def spraay_rtp_discover(category: str = "") -> str: + """Discover available robots and IoT devices on the RTP network. + + The Robot Task Protocol (RTP) enables AI agents to hire robots + and physical devices via x402 USDC micropayments. + + Args: + category: Optional category filter (e.g., 'robotics', + 'sensing', 'delivery', 'manufacturing', 'compute'). + + Returns: + JSON string with available devices and their capabilities. + """ + try: + params = {} + if category: + params["category"] = category + result = await _gateway_get("/v1/rtp/discover", params=params) + return json.dumps(result, indent=2) + except Exception as e: + return json.dumps({"error": str(e)})