Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions python/agents/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Check out the agent samples below, organized by category:
| [Agent Skills Tutorial](agent-skills-tutorial) | Demonstrates 4 ADK skill patterns: inline, file-based, external, and meta (skill-creator). Uses SkillToolset for progressive disclosure of skill metadata, instructions, and resources. | SkillToolset, Skills, Progressive disclosure | Conversational | Easy | Single Agent | Horizontal |
| [Academic Research](academic-research) | Assists researchers in identifying recent publications and discovering emerging research areas. | Multi-agent, Custom tool, Evaluation | Workflow | Easy | Multi Agent | Academia |
| [Brand Search Optimization](brand-search-optimization) | Enrich e-commerce product data by analyzing and comparing top search results. Useful for addressing issues like "Null & low recovery" / "Zero Results" searches and identifies gaps in product data. | Multi-agent, Custom tool, BigQuery connection, Evaluation, Computer use | Workflow | Easy | Multi Agent | Retail |
| [Crypto Payroll Agent](crypto-payroll-agent) | Batch ETH and ERC-20 payments on Base for payroll, airdrops, and revenue sharing using the Spraay protocol. | Custom tool, Tool composition | Conversational | Intermediate | Single Agent | Financial Services |
| [Cymbal Home & Garden Customer Service Agent](customer-service) | Customer service, product selection, order management for home improvement, gardening, and related supplies | Custom tool, Async tool, External system calls, Live streaming, Multimodal | Conversational | Advanced | Single Agent | Retail |
| [Currency Agent](currency-agent) | Agent for currency exchange rate lookups and conversions. | Custom tool | Conversational | Intermediate | Single Agent | Financial Services |
| [Data Engineering Agent](data-engineering) | Data Engineering Agent designed for building sophisticated BigQuery and Dataform Pipelines | BigQuery, Dataform, ELT Pipelines, Data Curation, Data Modelling, Data Preperation, Data Ingestion, Analytics Engineering, Data Engineering | Conversational | Advanced | Single Agent | Horizontal |
Expand Down
31 changes: 31 additions & 0 deletions python/agents/crypto-payroll-agent/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# --- Google Cloud / Vertex AI ---
# Required when GOOGLE_GENAI_USE_VERTEXAI=1.
GOOGLE_CLOUD_PROJECT=your-gcp-project-id
GOOGLE_CLOUD_LOCATION=us-central1
GOOGLE_GENAI_USE_VERTEXAI=1

# Alternative: use AI Studio directly (set GOOGLE_GENAI_USE_VERTEXAI=0).
# GOOGLE_API_KEY=your-ai-studio-key

# --- Model selection ---
# Gemini model used by the agent.
PAYROLL_AGENT_MODEL=gemini-2.5-flash

# --- Spraay batch contract (consumed by google-adk-community Spraay tools) ---
# Private key of the wallet that will sign batch transactions on Base.
# Use a fresh key with a small amount of ETH + the tokens you want to send.
# Treat this like cash. Do not commit. Do not reuse a personal key.
SPRAAY_PRIVATE_KEY=0x0000000000000000000000000000000000000000000000000000000000000000

# Optional: override the default Base RPC endpoint.
# Default: https://mainnet.base.org
# For testing on Base Sepolia, use: https://sepolia.base.org
# SPRAAY_RPC_URL=https://mainnet.base.org

# Optional: override the Spraay batch contract address.
# Default (Base mainnet): 0x1646452F98E36A3c9Cfc3eDD8868221E207B5eEC
# SPRAAY_CONTRACT_ADDRESS=0x1646452F98E36A3c9Cfc3eDD8868221E207B5eEC

# --- Agent safety rails ---
# Refuse batches whose USD total exceeds this ceiling.
PAYROLL_MAX_BATCH_USD=10000
194 changes: 194 additions & 0 deletions python/agents/crypto-payroll-agent/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
# Crypto Payroll Agent

A Google ADK sample agent that batch-pays multiple recipients in ETH or
ERC-20 tokens on [Base](https://base.org) in a single transaction.

Built on the [Spraay batch payment tools](https://github.com/google/adk-python-community/pull/95)
in `google-adk-community`. Demonstrates a complete payroll, airdrop, and
revenue-split workflow with built-in safety gates.

## What this agent does

A treasury operator says something like:

> *"Pay 250 USDC each to alice.eth, bob.eth, and 0x742d…f44e."*

…or:

> *"Split this 10,000 USDC airdrop across these 50 addresses by their
> contribution weights."*

The agent:

1. Identifies whether the payment is **equal** (use `spraay_batch_eth` /
`spraay_batch_token`) or **variable** (use the `_variable` siblings).
2. Looks up token metadata (decimals, canonical address) via the local
token registry so the user never has to know that USDC has 6 decimals.
3. For variable distributions, splits a pool proportionally with the
`split_pool_proportionally` helper.
4. Presents a single-screen plan: recipient count, amounts, total,
estimated gas savings vs. individual transfers.
5. Waits for explicit user confirmation, then executes the batch on Base
through the Spraay protocol.

## Why this is interesting

Most sample agents demo one or two narrow capabilities. This one shows
ADK's tool-selection in action: the model must pick the correct one of
four batch tools (eth vs. token × equal vs. variable) from free-form user
input, plus chain a helper tool when the user provides weights instead of
explicit per-recipient amounts. It's a small but realistic showcase of
multi-tool composition.

## Prerequisites

- Python 3.11+
- `uv` package manager: `pip install uv`
- A Base wallet with a small amount of ETH for gas and the token(s) you
want to send. Base testnet (Sepolia) works for development; set
`SPRAAY_RPC_URL` to a Sepolia endpoint.

## Setup

```bash
git clone https://github.com/google/adk-samples.git
cd adk-samples/python/agents/crypto-payroll-agent
uv sync
cp .env.example .env
# Edit .env: at minimum, set SPRAAY_PRIVATE_KEY and your Google Cloud project
```

> **Note:** This sample installs `google-adk-community` directly from
> the GitHub `main` branch because the Spraay batch tools merged in
> [adk-python-community#95](https://github.com/google/adk-python-community/pull/95)
> have not yet been included in a PyPI release. Once a release later
> than `0.4.1` ships, the `pyproject.toml` pin can be relaxed to a
> standard version constraint.

After `uv sync`, verify the four Spraay tools import correctly:

```bash
uv run python -c "from google.adk_community.tools.spraay import \
spraay_batch_eth, spraay_batch_token, \
spraay_batch_eth_variable, spraay_batch_token_variable; \
print('Spraay tools ready')"
```

## Run the agent

```bash
uv run adk web
```

Then open `http://localhost:4200`, choose **crypto_payroll_agent**, and
try the example prompts below.

## Example interactions

### 1. Equal-amount payroll (`spraay_batch_token`)

> *Pay 250 USDC each to 0x9f2c…81a3, 0x4ab7…d109, and 0x8eee…2017 on Base.*

The agent looks up USDC's address and decimals, summarizes the plan, and
on `confirm` calls `spraay_batch_token` with `amount_per_recipient="250"`
and `token_decimals=6`.

### 2. Equal-amount ETH bounties (`spraay_batch_eth`)

> *Send 0.01 ETH each to these 12 bounty hunters: [list of 12 addresses].*

The agent rejects amounts over the configured safety ceiling, otherwise
calls `spraay_batch_eth` with `amount_per_recipient_eth="0.01"`.

### 3. Variable token amounts (`spraay_batch_token_variable`)

> *Pay this month's contractors. Alice gets 1500 USDC, Bob 2200, Carol
> 800.*

The agent extracts the per-recipient amounts and calls
`spraay_batch_token_variable`.

### 4. Proportional split (`split_pool_proportionally` + `spraay_batch_token_variable`)

> *Distribute 10,000 USDC to these 5 contributors by their commit counts:
> 120, 80, 45, 30, 25.*

The agent calls `split_pool_proportionally` first to compute per-recipient
amounts (rounding-safe so the total is exact), then executes the variable
batch.

## Configuration

| Variable | Required | Description |
| --- | --- | --- |
| `SPRAAY_PRIVATE_KEY` | yes | Private key of the sending wallet on Base |
| `SPRAAY_RPC_URL` | no | Base RPC endpoint (default: `https://mainnet.base.org`) |
| `SPRAAY_CONTRACT_ADDRESS` | no | Override the Spraay batch contract |
| `GOOGLE_CLOUD_PROJECT` | yes¹ | GCP project for Vertex AI |
| `GOOGLE_CLOUD_LOCATION` | yes¹ | GCP region (e.g. `us-central1`) |
| `GOOGLE_GENAI_USE_VERTEXAI` | yes¹ | `1` for Vertex AI, `0` for AI Studio |
| `GOOGLE_API_KEY` | yes² | AI Studio key (alternative to Vertex AI) |
| `PAYROLL_AGENT_MODEL` | no | Gemini model (default: `gemini-2.5-flash`) |
| `PAYROLL_MAX_BATCH_USD` | no | Refuse batches above this total (default: `10000`) |

¹ if using Vertex AI · ² if using AI Studio

## Agent structure

```
crypto_payroll_agent/
├── agent.py # LlmAgent wired to 4 Spraay tools + 2 helpers
├── prompt.py # System instruction governing tool selection
├── config.py # Model + safety + token registry
└── tools/
└── helpers.py # lookup_token_info, split_pool_proportionally
```

The agent has six tools total:

| Tool | Source | Purpose |
| --- | --- | --- |
| `spraay_batch_eth` | `google.adk_community.tools.spraay` | Equal ETH to N recipients |
| `spraay_batch_token` | same | Equal ERC-20 to N recipients |
| `spraay_batch_eth_variable` | same | Variable ETH amounts |
| `spraay_batch_token_variable` | same | Variable token amounts |
| `lookup_token_info` | local | Resolve symbol → {address, decimals} |
| `split_pool_proportionally` | local | Divide a pool by weights (rounding-safe) |

## Evaluation

```bash
uv run adk eval crypto_payroll_agent eval/crypto_payroll_eval_set.evalset.json
```

The evalset covers all four batch-tool selection paths plus a safety-gate
refusal case.

## Testing

```bash
uv run pytest tests/ -v
```

All tests are offline. Spraay tools are mocked; helpers are tested
directly.

## Deployment

See `deployment/deploy.py` for a reference Vertex AI Agent Engine
deployment script.

## Protocol details

- Spraay batch contract: `0x1646452F98E36A3c9Cfc3eDD8868221E207B5eEC`
on Base mainnet
- Up to 200 recipients per transaction
- ~80% gas savings vs. individual transfers
- 0.3% protocol fee
- More info: [spraay.app](https://spraay.app)

This is a community sample. Spraay is not an official Google product.

## License

Apache 2.0 — see the repository root LICENSE.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"""Crypto Payroll Agent.

An ADK sample agent demonstrating multi-recipient stablecoin payouts via the
Spraay community tools.
"""

from .agent import root_agent

__all__ = ["root_agent"]
39 changes: 39 additions & 0 deletions python/agents/crypto-payroll-agent/crypto_payroll_agent/agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"""Root agent for the Crypto Payroll Agent sample."""

from google.adk.agents import LlmAgent

# Spraay batch tools merged in google/adk-python-community#95.
from google.adk_community.tools.spraay import ( # type: ignore[import]
spraay_batch_eth,
spraay_batch_eth_variable,
spraay_batch_token,
spraay_batch_token_variable,
)

from .config import CONFIG
from .prompt import ROOT_AGENT_INSTRUCTION
from .tools import lookup_token_info, split_pool_proportionally

root_agent = LlmAgent(
name=CONFIG.agent_name,
model=CONFIG.model,
description=(
"Batch-pays multiple recipients in ETH or ERC-20 tokens on Base "
"via the Spraay protocol. Supports equal-amount payroll, variable-"
"amount contractor pay, and proportional pool splits for airdrops "
"and revenue sharing."
),
instruction=ROOT_AGENT_INSTRUCTION.format(
max_batch_usd=str(CONFIG.max_batch_usd),
),
tools=[
# Local helpers (must be called before the batch tools when needed).
lookup_token_info,
split_pool_proportionally,
# Spraay batch tools (on-chain action).
spraay_batch_eth,
spraay_batch_token,
spraay_batch_eth_variable,
spraay_batch_token_variable,
],
)
83 changes: 83 additions & 0 deletions python/agents/crypto-payroll-agent/crypto_payroll_agent/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"""Configuration for the Crypto Payroll Agent."""

from __future__ import annotations

import os
from dataclasses import dataclass, field
from decimal import Decimal
from typing import TypedDict


class TokenInfo(TypedDict):
"""Metadata for an ERC-20 token on Base."""

symbol: str
address: str
decimals: int


# Base mainnet token registry.
# Addresses verified against https://basescan.org as of 2025.
# Verify each entry before relying on it in production.
BASE_TOKEN_REGISTRY: dict[str, TokenInfo] = {
"USDC": {
"symbol": "USDC",
"address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"decimals": 6,
},
"USDBC": {
"symbol": "USDbC",
"address": "0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA",
"decimals": 6,
},
"WETH": {
"symbol": "WETH",
"address": "0x4200000000000000000000000000000000000006",
"decimals": 18,
},
"CBETH": {
"symbol": "cbETH",
"address": "0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22",
"decimals": 18,
},
"CBBTC": {
"symbol": "cbBTC",
"address": "0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf",
"decimals": 8,
},
"DAI": {
"symbol": "DAI",
"address": "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb",
"decimals": 18,
},
"AERO": {
"symbol": "AERO",
"address": "0x940181a94A35A4569E4529A3CDfB74e38FD98631",
"decimals": 18,
},
}


@dataclass(frozen=True)
class Config:
"""Runtime config sourced from environment variables."""

# Model
model: str = os.getenv("PAYROLL_AGENT_MODEL", "gemini-2.5-flash")

# Safety ceiling for a single batch run (in USD)
max_batch_usd: Decimal = Decimal(
os.getenv("PAYROLL_MAX_BATCH_USD", "10000")
)

# Agent metadata
agent_name: str = "crypto_payroll_agent"
app_name: str = "Crypto Payroll Agent"

# Token registry (immutable copy)
token_registry: dict[str, TokenInfo] = field(
default_factory=lambda: dict(BASE_TOKEN_REGISTRY)
)


CONFIG = Config()
Loading