sp3nd
Buy Amazon and eBay products with USDC on Solana via SP3ND x402 payments. Use when paying for real-world goods with crypto (e.g. order this Amazon listing, buy eBay item, ship to Tokyo with USDC).
What this skill does
# SP3ND — Autonomous Amazon/eBay Shopping with USDC on Solana
SP3ND lets an AI agent buy real products from Amazon and eBay using USDC on Solana. No KYC, no accounts, 0% platform fee, free Prime shipping on eligible Amazon items.
**Full API docs:** https://sp3nd.shop/partner-api/docs
**SKILL.md:** https://sp3nd.shop/skill.md
**Base URL:** `https://us-central1-sp3nddotshop-prod.cloudfunctions.net`
---
## Prerequisites
- Solana wallet with USDC balance (mint: `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v`)
- `solders` and `solana` Python packages (`pip install solders solana`)
- SP3ND API credentials in `.env` (obtained via Step 1 below)
---
## Step 1: Register Agent (one-time)
Self-registration — no approval queue. Credentials are shown once; save immediately.
```python
import urllib.request, json
BASE_URL = "https://us-central1-sp3nddotshop-prod.cloudfunctions.net"
wallet_pubkey = "<your Solana wallet pubkey>"
payload = json.dumps({
"agent_name": "My Agent",
"solana_public_key": wallet_pubkey,
"contact_email": "[email protected]",
"description": "Autonomous shopping agent"
}).encode()
req = urllib.request.Request(
f"{BASE_URL}/registerAgent",
data=payload,
headers={"Content-Type": "application/json"},
method="POST"
)
with urllib.request.urlopen(req, timeout=15) as r:
d = json.loads(r.read())
print(d["api_key"], d["api_secret"]) # save both to .env
```
Save to `.env`:
```
SP3ND_API_KEY=sp3nd_...
SP3ND_API_SECRET=sp3nd_sec_...
```
---
## Step 2: Pick the Correct Amazon/eBay TLD
**Critical:** Use the marketplace TLD that matches the shipping country. Wrong TLD = failed order or wrong pricing.
### Amazon TLD by Region
| TLD | Countries covered |
|-----|------------------|
| `amazon.com` | US, and default fallback |
| `amazon.co.uk` | GB, IE |
| `amazon.de` | DE, AT, CH, and much of Europe |
| `amazon.fr` | FR |
| `amazon.it` | IT |
| `amazon.es` | ES |
| `amazon.nl` | NL, BE |
| `amazon.pl` | PL |
| `amazon.se` | SE |
| `amazon.com.tr` | TR |
| `amazon.co.jp` | JP |
| `amazon.com.au` | AU, NZ |
| `amazon.in` | IN |
| `amazon.sg` | SG, MY, TH, VN, ID, PH, MM, BN, KH, LA |
| `amazon.ae` | AE, OM, BH, KW, QA, JO, IQ, LB |
| `amazon.sa` | SA, YE |
| `amazon.eg` | EG |
| `amazon.com.br` | BR |
| `amazon.com.mx` | MX |
| `amazon.ca` | CA |
### eBay TLD by Region
| TLD | Countries |
|-----|-----------|
| `ebay.com` | US (default) |
| `ebay.co.uk` | GB |
| `ebay.de` | DE, AT, CH |
| `ebay.fr` | FR |
| `ebay.it` | IT |
| `ebay.es` | ES |
| `ebay.com.au` | AU |
| `ebay.ca` | CA |
---
## Step 3: Create Cart
```python
API_KEY = os.environ["SP3ND_API_KEY"]
API_SECRET = os.environ["SP3ND_API_SECRET"]
HEADERS = {"Content-Type": "application/json", "X-API-Key": API_KEY, "X-API-Secret": API_SECRET}
payload = json.dumps({
"items": [{"product_url": "https://www.amazon.sg/dp/ASIN", "quantity": 1}]
}).encode()
req = urllib.request.Request(f"{BASE_URL}/createPartnerCart", data=payload, headers=HEADERS, method="POST")
with urllib.request.urlopen(req, timeout=20) as r:
cart = json.loads(r.read())
cart_id = cart["cart"]["cart_id"]
total = cart["cart"]["total_amount"] # in USD = USDC amount needed
```
---
## Step 4: Create Order
```python
payload = json.dumps({
"cart_id": cart_id,
"customer_email": "[email protected]", # required
"shipping_address": {
"name": "Full Name",
"address_line1": "Street address",
"address_line2": "Unit/suburb", # optional
"city": "City",
"state": "State/province",
"zip": "Postal code",
"country": "ISO 2-letter code (e.g. MY, US, GB)",
"phone": "+1234567890"
}
}).encode()
req = urllib.request.Request(f"{BASE_URL}/createPartnerOrder", data=payload, headers=HEADERS, method="POST")
with urllib.request.urlopen(req, timeout=20) as r:
order = json.loads(r.read())
order_id = order["order"]["order_id"]
order_number = order["order"]["order_number"]
total_amount = order["order"]["total_amount"]
```
---
## Step 5: Build & Sign the Payment Transaction
**This is the tricky part.** SP3ND uses x402 v2 protocol. When you call `payAgentOrder` it returns HTTP 402 with payment requirements. You must:
1. Find the treasury's **actual USDC token account** (ATA) on-chain — do NOT derive it, fetch it
2. Build a transaction with **two instructions**: USDC `transferChecked` + a **Memo** with `SP3ND Order: <order_number>`
3. The Memo is mandatory — without it SP3ND's webhook cannot match the payment to your order
4. Sign and broadcast directly via Solana RPC
```python
import base64, struct, urllib.request, json
from solders.pubkey import Pubkey
from solders.hash import Hash
from solders.message import Message
from solders.transaction import Transaction
from solders.instruction import Instruction, AccountMeta
from solana.rpc.api import Client
USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
TOKEN_PROG = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
MEMO_PROG = "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"
TREASURY = "2nkTRv3qxk7n2eYYjFAndReVXaV7sTF3Z9pNimvp5jcp"
RPC_URL = "https://api.mainnet-beta.solana.com"
def get_treasury_ata():
"""Fetch SP3ND treasury's actual USDC ATA on-chain. Do NOT derive — fetch."""
payload = json.dumps({
"jsonrpc": "2.0", "id": 1,
"method": "getTokenAccountsByOwner",
"params": [TREASURY, {"mint": USDC_MINT}, {"encoding": "jsonParsed"}]
}).encode()
req = urllib.request.Request(RPC_URL, data=payload, headers={"Content-Type": "application/json"})
with urllib.request.urlopen(req, timeout=10) as r:
d = json.loads(r.read())
return d["result"]["value"][0]["pubkey"]
def build_unsigned_tx(wallet_pubkey, our_ata, treasury_ata, amount_usdc_units, order_number):
"""Build unsigned tx: transferChecked + memo. amount_usdc_units = USDC * 1_000_000."""
client = Client(RPC_URL)
bh = client.get_latest_blockhash().value.blockhash
wallet_pk = Pubkey.from_string(wallet_pubkey)
src = Pubkey.from_string(our_ata)
dst = Pubkey.from_string(treasury_ata)
mint = Pubkey.from_string(USDC_MINT)
token_prog = Pubkey.from_string(TOKEN_PROG)
memo_prog = Pubkey.from_string(MEMO_PROG)
# Instruction 1: transferChecked (discriminator=12, u64 amount, u8 decimals=6)
ix_transfer = Instruction(
program_id=token_prog,
accounts=[
AccountMeta(pubkey=src, is_signer=False, is_writable=True),
AccountMeta(pubkey=mint, is_signer=False, is_writable=False),
AccountMeta(pubkey=dst, is_signer=False, is_writable=True),
AccountMeta(pubkey=wallet_pk, is_signer=True, is_writable=False),
],
data=bytes([12]) + struct.pack("<Q", amount_usdc_units) + bytes([6])
)
# Instruction 2: Memo — required for SP3ND webhook to match payment to order
ix_memo = Instruction(
program_id=memo_prog,
accounts=[AccountMeta(pubkey=wallet_pk, is_signer=True, is_writable=False)],
data=f"SP3ND Order: {order_number}".encode("utf-8")
)
msg = Message.new_with_blockhash([ix_transfer, ix_memo], wallet_pk, Hash.from_string(str(bh)))
tx = Transaction.new_unsigned(msg)
return base64.b64encode(bytes(tx)).decode()
```
### Sign and broadcast (Starchild Privy wallet)
Starchild agents have a built-in Privy-managed Solana wallet. Use these two tools in sequence:
**Step 1 — Get the wallet address:**
```
wallet_info() → returns solana address (e.g. C4ffoATXA5j3UdyvP4UQ3vMuQ547G18bG2QqhSs5bDvb)
```
**Step 2 — Check USDC balance before building:**
```
wallet_sol_balance() → lists all SPL tokens including USDC
```
**Step 3 — Sign the unsigned tx:**
```
wallet_sol_sign_transaction(transaction=unsigned_b64)
→ returns { signed_transaction: "<base64>" }
```
**Step 4 — Broadcast via Solana RPC directly:*Related in Web3
xaut-trade
IncludedBuy or sell XAUT (Tether Gold) on Ethereum. Supports market orders (Uniswap V3) and limit orders (UniswapX). Wallet modes: Foundry keystore or WDK. Delegates non-XAUT intents to registered skills (e.g. Polymarket prediction markets, Hyperliquid trading). Triggers: buy XAUT, XAUT trade, swap USDT for XAUT, sell XAUT, swap XAUT for USDT, limit order, limit buy XAUT, limit sell XAUT, check limit order, cancel limit order, XAUT when, create wallet, setup wallet, polymarket, prediction market, bet on, odds on, hyperliquid, perp, perpetual, long, short, open long, open short, close position, leverage.
qfc-openclaw-skill
IncludedQFC blockchain interaction — wallet, faucet, chain queries, staking, epoch & finality, AI inference
gate-dex-trade
IncludedExecutes on-chain token swaps via Gate DEX. Use when user wants to swap, buy, sell, exchange, or convert tokens, or bridge cross-chain. Covers full swap flow: price quotes, transaction build, signing, and submission. Do NOT use for read-only data lookups or wallet account management.
hunch
IncludedDiscover, bet on, track, and settle Hunch prediction markets in natural language. Trigger when a user wants to bet, take a position, or get odds on a crypto outcome — token market-cap milestones and flips, launchpad races (Bankr vs pump.fun volume / #1-days / launches over a cap), token head-to-head outperformance, mcap strike-ladders, and up/down price rounds. Also trigger on "what can I bet on about $TOKEN", "odds on …", "take YES/NO on …", "show my Hunch bets", "did my market resolve". Settles in USDC on Base via x402 (≤ $10 / bet); every bet returns an on-chain proof.
opensea
IncludedQuery NFT data, trade on the Seaport marketplace, and swap ERC20 tokens across Ethereum, Base, Arbitrum, Optimism, Polygon, and more.
polymarket
IncludedTrade on Polymarket prediction markets (CLOB V2) from a Privy EOA wallet. Search markets, place/cancel orders, manage positions. No private key handling. Use when the user wants to bet on event outcomes (e.g. "buy YES at 0.65 on the ceasefire market", "what are my open positions", "close my Trump bet").