Catalog PyPI
PACKAGE EXPLORERView in registry

aether-chain-client

PyPI

Python client for the Aether chain: ML-DSA-44 keys, payments, buying and selling paid APIs (x402 aether-memo / aether-prepaid) and the service directory.

aetheragent-paymentsai-agentsblockchaincosmoscrewailangchainllamaindexmcpml-dsaopenai-agentspost-quantumusdcx402
Versions collected1
Direct dependencies6From release 0.2.14
Last observed20 Oct, 20:53 UTC

README

Version 0.2.14 documentation

aether-chain-client

pip install aether-chain-client

Python client for the Aether chain: ML-DSA-44 keys, sending and receiving AETH, buying from and selling paid APIs (x402 aether-memo, aether-prepaid and aether-pull) and the on-chain service directory. It talks to a node's CometBFT RPC (port 26657) only.

import os
from aether_client import AetherClient, Key, fetch_paid, find_services, rate_service, withdraw_prepaid

client = AetherClient("http://localhost:26657", "aether-testnet-1")
key = Key.from_mnemonic(os.environ["AETHER_MNEMONIC"])  # same address as `aetherd keys add` / `agentmcp`

sent = client.send(key, "aether1...", "0.5 AETH", memo="invoice-42")
done = client.wait_for_transaction(sent.hash)            # confirmed | failed | pending (timeout)
# Retrying after an error: client.rebroadcast(sent.signed) -- never a second send().

res = fetch_paid(client, key, "https://api.example.com/forecast", max_amount="0.05 AETH", prepay="1 AETH")
# Or, where offered, pay from your own account under a capped, 7-day allowance the seller collects from later:
res2 = fetch_paid(client, key, "https://api.example.com/forecast", max_amount="0.05 AETH", pull_allowance="1 AETH")
services = find_services(client, query="weather", max_price="0.1 AETH", trusted=[owner_address])
# services[i].reputation: payments, payers, ratings (can be faked), trusted_ratings (can't)
# res.receipt: the seller's signed receipt and whether it matches exactly what was sent and received
rate_service(client, key, "https://api.example.com", 5)  # only raters who paid a service count
back = withdraw_prepaid(client, key, "https://api.example.com", withdrawal_id="w1")  # unspent prepaid balance

Standard x402

aether_client.x402.ExactAetherScheme pays the standard x402 v2 exact scheme with the x402 package's clients (x402ClientSync, x402_requests); the setup is in docs/X402.md. It signs a send of exactly what the 402 asks, and the server's facilitator broadcasts it.

AI agent frameworks

AetherToolkit gives an agent seven tools: check a balance, check a transaction, find paid APIs in the on-chain directory, quote an AETH to OSMO swap on Osmosis testnet, send a payment, call a paid API (paying it), and ask the testnet faucet for funds. Each framework gets them in its own form:

pip install "aether-chain-client[langchain]"      # or [openai-agents], [crewai], [llamaindex]
import os
from aether_client import AetherClient, Key
from aether_client.agent_tools import AetherToolkit

client = AetherClient("https://rpc.157-245-252-221.sslip.io", "aether-testnet-1")
kit = AetherToolkit(client, Key.from_mnemonic(os.environ["AETHER_MNEMONIC"]),
                    max_per_payment="0.1 AETH", daily_budget="1 AETH")

tools = kit.langchain()        # LangChain / LangGraph: create_react_agent(model, tools)
tools = kit.openai_agents()    # OpenAI Agents SDK: Agent(name=..., tools=tools)
tools = kit.crewai()           # CrewAI: Agent(..., tools=tools)
tools = kit.llamaindex()       # LlamaIndex: FunctionAgent(tools=tools, llm=...)
specs = kit.tool_specs()       # anything else: OpenAI-style function specs ("anthropic" for Messages API tools)
result = kit.call(name, arguments)   # ... and the answer to the model's tool call
  • Limits hold in code, not just the prompt. A payment over max_per_payment, or past daily_budget in a rolling 24 hours, is refused before anything is signed. kit.spending_status() shows what's left. Paid API calls count what they paid.
  • No double payments. send_payment takes an idempotency_key; the same key returns the first result instead of paying again.
  • Read-only without a key. AetherToolkit(client) offers only the balance, transaction, directory and swap-quote tools.
  • Swap quotes. aether_quote_swap reads Osmosis's own estimate for the AETH/OSMO pool (1370) and gives the OSMO out, the cost in fees and price impact, and a minOutput for a slippage. It is on for aether-testnet-1; osmosis_rest=[...] names other Osmosis REST endpoints and osmosis_rest=[] turns it off. It only quotes: the swap itself is agentmcp's swap_aeth (see docs/OSMOSIS-POOL.md).
  • Errors are results. A failed call returns {"error": {"code", "message"}}, so the model sees what went wrong instead of the run stopping.
  • Untrusted text stays data. Memos, service names and descriptions and API response bodies are set by other people; the tool descriptions tell the model so.

Tested with langchain-core 1.6, openai-agents 0.23, crewai 1.6 and llama-index-core 0.14.

Selling

Charge per request from a Python service -- all three payment schemes, the /.well-known/x402 manifest for the service directory, and withdrawals of unspent prepaid balances. Compatible with the Go paywall and every Aether buyer.

import os
from fastapi import FastAPI, Request
from aether_client import AetherClient, Key, Paywall

client = AetherClient("http://localhost:26657", "aether-testnet-1")
pw = Paywall(client, "aether1...", "0.01 AETH", name="Weather", description="Forecasts by city",
             prepaid_ledger="ledger.json",           # customers' balances: back it up
             min_deposit="0.1 AETH",
             payout_key=Key.from_mnemonic(os.environ["PAYOUT_MNEMONIC"]),  # pays withdrawals; keep a small float in it
             receipt_key=payee_key,  # sign a receipt for every paid response (or a delegated key + receipt_delegation)
             pull_collector_key=Key.from_mnemonic(os.environ["COLLECTOR_MNEMONIC"]), pull_credit="1 AETH")  # aether-pull: needs no funds
pw.start_collecting()  # collects what aether-pull buyers owe, in batches
api = FastAPI()

@api.get("/forecast")
async def forecast(request: Request):
    return {"paid_by": request.scope["aether"].payer}

app = pw.asgi(api, free=["/health"])   # Flask/Django: app.wsgi_app = pw.wsgi(app.wsgi_app); who paid: environ["aether.payment"]

The ledger is one JSON file (the Go paywall's format) for one process. Chain lookups block, so the ASGI adapter runs them in a thread.

Amounts always carry a unit ("1.5 AETH", "1500000uaeth").

USDC. AetherClient(rpc, chain_id, usdc_channel="channel-N") (Aether's end of its channel to Noble) also knows USDC: Noble's uusdc over exactly that channel, never a lookalike that arrived another way. For USDC that arrives another route, give its denom trace and its denom on the issuing chain instead: usdc_path="transfer/channel-2", usdc_base_denom="erc20:0x0C382e685bbeeFE5d3d9C29e29E341fEE8E84C5d", usdc_issuer="Injective" (usdc_base_denom defaults to uusdc). Then send(key, to, "5 USDC") sends USDC, balances(address) lists each asset, wait_for_payment matches in the asset of min_amount, and fetch_paid pays services priced in USDC with max_amount (and prepay / pull_allowance) in USDC; an amount in another asset than the service charges is refused with ASSET_MISMATCH before anything is paid. Results carry asset, amount, balance, owed and allowance in that asset's base units; the *_uaeth fields are set only for AETH. Without the channel, "5 USDC" is an unknown unit. The seller kit charges USDC too: Paywall(client, pay_to, "0.05 USDC", usdc_channel="channel-N") charges Noble USDC for everything (payments, deposits and balances, pull allowances and collections, withdrawals), min_deposit and pull_credit must then be in USDC, and 402s, the manifest and receipts carry the asset exactly as cmd/paywall's do.

Tests: python -m pytest tests (checked against ../testdata/vectors.json, generated by the Go code).

Downloads & popularity

Awaiting observations

No metric observations yet

Download counts and popularity will appear here as the collector receives them.

Versions 1

Collected releases
VersionPublishedDirect dependenciesOpen version
0.2.1418 Oct 20266

Package advisories

0
No matching advisories collected

This describes our collected data; it is not a security assessment.

Repository

PENDING
whoyoujoshin/aether

Shared repository data · GitHub

License detected in repository
Not collected

Metadata history

Meaningful changes
Current collected state Current

Observed since 20 Oct, 20:53 UTC

No superseded metadata states collected. Unchanged observations do not create new revisions.

Record identity

package:d7c06051de5a6d8d994c42dd4ff0d8fde57802700721df2b3c8cb0e51a047658