Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
142 changes: 142 additions & 0 deletions samples/python/scenarios/a2a/human-present/crypto-solana/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<!-- cspell:words Hedera Solflare keypair mainnet devnet ALGOVOI algv Helius Backpack Triton Phantom -->

# Agent Payments Protocol Sample: Human Present Purchases with On-Chain Solana USDC

This sample demonstrates the A2A `ap2-extension` for a human-present transaction
where the buyer settles with on-chain USDC on Solana (or native SOL for
micropayments). It mirrors the existing `x402` scenario but uses Solana Pay
semantics — specifically the **`reference` pubkey** primitive — to bind the
settling transaction to a specific AP2 `PaymentMandate`.

**Note:** This sample pairs with the separate `crypto-algo` human-present
scenario. Together they cover non-EVM settlement on Algorand and Solana as a
complement to the EVM-focused `x402` path.

## Scenario

Human-present flows are commerce flows where the user is present to confirm
purchase details. The user signs the `PaymentMandate` giving all parties high
confidence in the transaction.

The Solana variant adds one additional primitive on top of the standard AP2
mandate chain:

### Solana Pay `reference` binding

Solana has no native transaction memo field (unlike Algorand or Hedera), and
the SPL Memo Program is unreliable across wallets. Instead, Solana Pay defines
a `reference` pubkey mechanism:

1. The merchant (or its Merchant Agent) generates a fresh, single-use ed25519
keypair per checkout.
2. The public key is embedded in the Solana Pay URL:

```text
solana:<recipient>
?amount=<amount>
&spl-token=<USDC_MINT>
&reference=<reference_pubkey>
&label=<label>
&message=<order-id>
```

3. The buyer's wallet (Phantom, Solflare, Backpack, etc.) includes the
`reference` pubkey as a read-only, non-signer account in the transfer
instruction.
4. After the user signs and broadcasts the SPL Token transfer, the merchant
(or facilitator) queries `getSignaturesForAddress(<reference>)` to locate
the exact transaction that settled this order.
5. Final verification compares the on-chain transfer's recipient, amount, and
SPL mint against the AP2 `PaymentMandate` contents. All three must match.

This is stronger than relying on amount uniqueness (EVM-style) and more
wallet-compatible than relying on memos. It also means the merchant never has
to ask the buyer to paste a transaction signature back.

## Key Actors

This sample consists of:

- **Shopping Agent:** The main orchestrator that handles the user's shopping
request and delegates to specialist agents.
- **Merchant Agent:** Handles product queries and assembles the `CartMandate`.
- **Merchant Payment Processor Agent:** Takes payments on behalf of the
merchant and, in the Solana flow, verifies the on-chain USDC transfer
against the expected `reference`, amount, and mint.
- **Credentials Provider Agent:** Holds the user's payment credentials — in
this flow, the Solana wallet address and optional mint preferences.

## Mandate Chain

The AP2 mandate chain is unchanged by the Solana variant:

```text
IntentMandate ──▶ CartMandate ──▶ PaymentMandate
(user) (merchant) (user-signed)
Solana Pay URL (reference-bound)
SPL Token Transfer + reference pubkey
getSignaturesForAddress(reference)
PaymentReceipt
```

## Payment Method

This scenario sets `PAYMENT_METHOD=CRYPTO_SOLANA` at startup. Downstream agents
treat it as a non-card, non-x402 payment flow and route through the
Solana-aware credentials provider path.

## Assets Supported

| Asset | Mint | Decimals |
| --- | --- | --- |
| USDC (Solana mainnet) | `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v` | 6 |
| USDT (Solana mainnet) | `Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB` | 6 |
| USDC (Solana devnet) | `4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU` | 6 |
| Native SOL | — | 9 |

The Solana Pay URL format
(`solana:<recipient>?amount=...&spl-token=<mint>&reference=<pubkey>`) carries
the mint identifier, so supporting additional SPL tokens is a configuration
concern on the Merchant Agent and does not require protocol changes.

## Running the sample

```bash
# From the repository root:
export ALGOVOI_API_KEY="algv_..." # or any Solana-aware facilitator
export SOLANA_RPC_URL="https://..." # Solana mainnet or devnet RPC
export GOOGLE_API_KEY="..." # or GOOGLE_GENAI_USE_VERTEXAI=true
./samples/python/scenarios/a2a/human-present/crypto-solana/run.sh
```

The sample script starts the Merchant, Credentials Provider, and Merchant
Payment Processor agents locally, then launches the Shopping Agent via the ADK
web UI. You can then drive a purchase end-to-end with USDC on Solana.

## Why Solana Pay `reference` matters for AP2

AP2 mandates are already cryptographically signed, but they describe intent
and authorization — not the on-chain settlement event itself. The `reference`
pubkey is the missing link that ties a specific blockchain transaction to a
specific `PaymentMandate` deterministically, without trusting the buyer to
self-report their transaction signature. For agent-initiated commerce, where
the "buyer" may be an AI agent and the "merchant" may be another AI agent,
this mechanical binding removes an otherwise social trust layer.

## Reference implementations

- [AlgoVoi facilitator](https://api1.ilovechicken.co.uk) — verifies Solana
USDC transfers by (a) resolving `getSignaturesForAddress(reference)`,
(b) fetching the transaction, (c) checking mint + amount + recipient, and
(d) confirming the `reference` pubkey appears in the transaction's
account keys.
- [Solana Pay specification](https://solanapay.com/) — the canonical
reference for URL format and merchant-side flow.
122 changes: 122 additions & 0 deletions samples/python/scenarios/a2a/human-present/crypto-solana/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#!/bin/bash

# A script to automate the execution of the crypto-solana (on-chain USDC on Solana)
# AP2 example. It starts all necessary servers and agents in the background.
#
# This scenario uses Solana Pay `reference` pubkey binding to link the settling
# transaction deterministically to the signed AP2 PaymentMandate. See README.md
# for the full flow.

set -e

export PAYMENT_METHOD=CRYPTO_SOLANA

AGENTS_DIR="samples/python/src/roles"
LOG_DIR=".logs"

if [ ! -d "$AGENTS_DIR" ]; then
echo "Error: Directory '$AGENTS_DIR' not found."
echo "Please run this script from the root of the repository."
exit 1
fi

if [ -f .env ]; then
set -a
source .env
set +a
fi
Comment on lines +13 to +28
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The PAYMENT_METHOD environment variable is set at line 12, but then .env is sourced at line 25. If the user has PAYMENT_METHOD defined in their .env file, it will override the scenario-specific value (CRYPTO_SOLANA), potentially causing the script to execute with the wrong payment logic. It is safer to source the .env file first and then set scenario-specific overrides. Additionally, initializing the pids array early ensures that the cleanup trap (line 80) is always safe even if the script fails during early setup (e.g., line 20).

Suggested change
export PAYMENT_METHOD=CRYPTO_SOLANA
AGENTS_DIR="samples/python/src/roles"
LOG_DIR=".logs"
if [ ! -d "$AGENTS_DIR" ]; then
echo "Error: Directory '$AGENTS_DIR' not found."
echo "Please run this script from the root of the repository."
exit 1
fi
if [ -f .env ]; then
set -a
source .env
set +a
fi
if [ -f .env ]; then
set -a
source .env
set +a
fi
export PAYMENT_METHOD=CRYPTO_SOLANA
AGENTS_DIR="samples/python/src/roles"
LOG_DIR=".logs"
pids=()
if [ ! -d "$AGENTS_DIR" ]; then
echo "Error: Directory '$AGENTS_DIR' not found."
echo "Please run this script from the root of the repository."
exit 1
fi


USE_VERTEXAI=$(printf "%s" "${GOOGLE_GENAI_USE_VERTEXAI}" | tr '[:upper:]' '[:lower:]')
if [ -z "${GOOGLE_API_KEY}" ] && [ "${USE_VERTEXAI}" != "true" ]; then
echo "Please set your GOOGLE_API_KEY environment variable before running."
echo "Alternatively, set GOOGLE_GENAI_USE_VERTEXAI=true to use Vertex AI with ADC."
exit 1
fi

# Facilitator for on-chain verification. Defaults point at AlgoVoi Cloud but
# any Solana-aware AP2 facilitator works; set the env var to override.
if [ -z "${ALGOVOI_API_KEY}" ]; then

Check warning on line 38 in samples/python/scenarios/a2a/human-present/crypto-solana/run.sh

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (ALGOVOI)
echo "Please set your ALGOVOI_API_KEY environment variable before running."

Check warning on line 39 in samples/python/scenarios/a2a/human-present/crypto-solana/run.sh

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (ALGOVOI)
echo "Sign up at https://cloud.algovoi.co.uk (or use any Solana-aware AP2 facilitator)."
exit 1
fi

# Solana RPC. Defaults to mainnet public; override with a paid provider

Check warning on line 44 in samples/python/scenarios/a2a/human-present/crypto-solana/run.sh

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (mainnet)
# (Helius, Alchemy, Triton) for reliability.

Check warning on line 45 in samples/python/scenarios/a2a/human-present/crypto-solana/run.sh

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (Helius)
if [ -z "${SOLANA_RPC_URL}" ]; then
export SOLANA_RPC_URL="https://api.mainnet-beta.solana.com"
echo "SOLANA_RPC_URL not set; defaulting to the public Solana mainnet endpoint."

Check warning on line 48 in samples/python/scenarios/a2a/human-present/crypto-solana/run.sh

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (mainnet)
echo "For production usage, configure a paid RPC provider for rate-limit headroom."
fi

echo "Setting up the Python virtual environment..."

if [ ! -d ".venv" ]; then
uv venv
fi

case "$OSTYPE" in
msys* | cygwin*)
source .venv/Scripts/activate
;;
*)
source .venv/bin/activate
;;
esac
echo "Virtual environment activated."

mkdir -p "$LOG_DIR"

cleanup() {
echo ""
echo "Shutting down background processes..."
if [ ${#pids[@]} -ne 0 ]; then
kill "${pids[@]}" 2>/dev/null
wait "${pids[@]}" 2>/dev/null
fi
echo "Cleanup complete."
}

trap cleanup EXIT

echo "Syncing virtual environment with uv sync..."
if uv sync --package ap2-samples; then
echo "Virtual environment synced successfully."
else
echo "Error: uv sync failed. Aborting."
exit 1
fi

echo "Clearing the logs directory..."
if [ -d "$LOG_DIR" ]; then
find "$LOG_DIR" -mindepth 1 -delete
fi

pids=()

echo ""
echo "Starting remote servers and agents as background processes..."

UV_RUN_CMD="uv run --no-sync"

if [ -f ".env" ]; then
UV_RUN_CMD="$UV_RUN_CMD --env-file .env"
fi

echo "-> Starting the Merchant Agent (port:8001 log:$LOG_DIR/merchant_agent.log)..."
$UV_RUN_CMD --package ap2-samples python -m roles.merchant_agent >"$LOG_DIR/merchant_agent.log" 2>&1 &
pids+=($!)

echo "-> Starting the Credentials Provider (port:8002 log:$LOG_DIR/credentials_provider_agent.log)..."
$UV_RUN_CMD --package ap2-samples python -m roles.credentials_provider_agent >"$LOG_DIR/credentials_provider_agent.log" 2>&1 &
pids+=($!)

echo "-> Starting the Merchant Payment Processor Agent (port:8003 log:$LOG_DIR/mpp_agent.log)..."
$UV_RUN_CMD --package ap2-samples python -m roles.merchant_payment_processor_agent >"$LOG_DIR/mpp_agent.log" 2>&1 &
pids+=($!)

echo ""
echo "All remote servers are starting."

echo "Starting the Shopping Agent..."
$UV_RUN_CMD --package ap2-samples adk web --host 0.0.0.0 $AGENTS_DIR/shopping_agent
Loading