Documentation
¶
Overview ¶
Package buy contains host-side helpers for `obol buy` commands.
Today this is primarily pre-flight logic for `obol buy inference`:
- probe the seller's 402 pricing response
- verify the seller's ERC-8004 registration document against the priced chain
- reject budgets that would exceed the advertised one-request price floor
Signing, PurchaseRequest creation, facilitator interaction, and refill bookkeeping all live inside the agent pod's buy.py — see internal/embed/skills/buy-x402/scripts/buy.py.
Index ¶
- Variables
- func BuyPyCommand(runtime agentruntime.Runtime, args ...string) []string
- func FetchSellerRegistration(ctx context.Context, sellerURL string) (*erc8004.AgentRegistration, error)
- func ValidateBudgetAgainstPricing(budgetBase string, pricing *PricingResponse) error
- func ValidateTokenAgainstPricing(token string, pricing *PricingResponse) error
- func VerifyAgentID(reg *erc8004.AgentRegistration, expected int64) error
- func VerifyAgentIDForPricing(reg *erc8004.AgentRegistration, expected int64, pricing *PricingResponse) error
- func VerifyAgentIDOnRegistry(reg *erc8004.AgentRegistration, expected int64, expectedRegistry string) error
- func VerifySellerEndpoint(reg *erc8004.AgentRegistration, sellerURL string) error
- type CatalogEntry
- type PaymentOption
- type PricingResponse
- type PurchaseSummary
- type WalletInfo
Constants ¶
This section is empty.
Variables ¶
var ErrNoAgent = errors.New("no agent instance available to query purchases")
ErrNoAgent is returned when no agent target is available to query the purchase list (e.g. `obol stack up` not yet run, or no agents created).
Functions ¶
func BuyPyCommand ¶
func BuyPyCommand(runtime agentruntime.Runtime, args ...string) []string
BuyPyCommand returns the in-pod argv prefix for the buy-x402 helper in the selected runtime. Hermes carries its own venv; OpenClaw exposes python3 on PATH and mounts skills under /data/.openclaw/skills.
func FetchSellerRegistration ¶
func FetchSellerRegistration(ctx context.Context, sellerURL string) (*erc8004.AgentRegistration, error)
FetchSellerRegistration fetches and parses the seller's ERC-8004 registration document. sellerURL may be either the seller's base URL (e.g. "https://demo-seller.obol.tech") or a service URL (e.g. ".../services/foo"); the path component is replaced with the well-known path so callers can pass either shape.
func ValidateBudgetAgainstPricing ¶
func ValidateBudgetAgainstPricing(budgetBase string, pricing *PricingResponse) error
ValidateBudgetAgainstPricing rejects budgets smaller than one request price. buy.py currently rounds `budget // price` up to at least one auth; the host CLI advertises --budget as a spending cap, so we fail early instead of silently overspending the requested cap.
Call ValidateTokenAgainstPricing first; this function assumes the token has already been validated against the seller's priced asset.
func ValidateTokenAgainstPricing ¶
func ValidateTokenAgainstPricing(token string, pricing *PricingResponse) error
ValidateTokenAgainstPricing checks that the requested --token matches the seller's priced asset. When there is a mismatch it returns a structured error that names both the requested token and the seller's required asset, and suggests the correct --token value when the asset address is known.
Call this before ValidateBudgetAgainstPricing so the caller sees a clear token-mismatch error instead of a confusing budget-validation failure.
func VerifyAgentID ¶
func VerifyAgentID(reg *erc8004.AgentRegistration, expected int64) error
VerifyAgentID returns nil iff at least one of reg.Registrations matches the expected ERC-8004 tokenId. The seller may publish multiple registrations (one per chain); a match on any of them is sufficient.
func VerifyAgentIDForPricing ¶
func VerifyAgentIDForPricing(reg *erc8004.AgentRegistration, expected int64, pricing *PricingResponse) error
VerifyAgentIDForPricing pins the ERC-8004 identity check to the seller's priced network so a matching tokenId on an unrelated registry does not pass.
func VerifyAgentIDOnRegistry ¶
func VerifyAgentIDOnRegistry(reg *erc8004.AgentRegistration, expected int64, expectedRegistry string) error
VerifyAgentIDOnRegistry requires the expected agentId to appear on the specific CAIP-10 registry identifier resolved from the seller's priced payment network.
func VerifySellerEndpoint ¶
func VerifySellerEndpoint(reg *erc8004.AgentRegistration, sellerURL string) error
VerifySellerEndpoint ensures the requested seller URL is one of the service endpoints advertised in the seller's ERC-8004 registration document.
Types ¶
type CatalogEntry ¶
type CatalogEntry = schemas.ServiceCatalogEntry
CatalogEntry re-exports the wire schema so callers don't have to import internal/schemas just to read a /api/services.json response.
func FetchServiceCatalog ¶
func FetchServiceCatalog(ctx context.Context, sellerURL string) ([]CatalogEntry, error)
FetchServiceCatalog fetches the seller's /api/services.json feed and returns the parsed catalog. sellerURL may be either the seller's base URL (storefront hostname) or any /services/<name>/... URL — the path component is replaced with /api/services.json.
func PickCatalogEntry ¶
func PickCatalogEntry(entries []CatalogEntry, sellerURL string) (*CatalogEntry, error)
PickCatalogEntry picks the entry whose endpoint matches sellerURL. When sellerURL is a bare storefront base (no /services/<name>) and exactly one inference entry is advertised, that entry is returned. Returns a human-readable error listing the inference offers otherwise.
type PaymentOption ¶
type PaymentOption struct {
PayTo string `json:"payTo"`
Network string `json:"network"`
Asset string `json:"asset"`
Amount string `json:"amount"`
MaxAmountRequired string `json:"maxAmountRequired"`
}
PaymentOption is one entry from the seller's x402 accepts array.
type PricingResponse ¶
type PricingResponse struct {
X402Version int `json:"x402Version"`
Accepts []PaymentOption `json:"accepts"`
}
PricingResponse is the subset of the seller's x402 402 body that the host pre-flight needs before dispatching into buy.py.
func FetchSellerPricing ¶
func FetchSellerPricing(ctx context.Context, sellerURL, model string) (*PricingResponse, error)
FetchSellerPricing probes the seller's chat-completions endpoint and returns the parsed x402 402 body. sellerURL may be either the service root (`.../services/foo`) or the final chat-completions URL.
type PurchaseSummary ¶
type PurchaseSummary struct {
Name string `json:"name"`
Alias string `json:"alias"`
Model string `json:"model"`
Remaining int `json:"remaining"`
Spent int `json:"spent"`
TotalSigned int `json:"totalSigned"`
Expired int `json:"expired"`
Price string `json:"price"` // per-request atomic amount
Chain string `json:"chain"`
Endpoint string `json:"endpoint"`
AutoRefill bool `json:"autoRefill"`
AssetSymbol string `json:"assetSymbol"`
AssetDecimals int `json:"assetDecimals"`
}
PurchaseSummary is the host-side view of one paid-inference subscription the agent is funding. The fields mirror buy.py's `list --json` payload — rename in lockstep with internal/embed/skills/buy-x402/scripts/buy.py (cmd_list `as_json=True`) when changing either side.
func ListPurchases ¶
func ListPurchases(cfg *config.Config, runtime agentruntime.Runtime, id string) ([]PurchaseSummary, error)
ListPurchases shells `buy.py list --json` inside the named agent pod and returns the parsed payload. Used by `obol model status` to surface pre-authorized budgets alongside the provider table.
Returns an empty slice + nil error when the agent has no purchases, so callers can render a "no paid models" message without distinguishing from the "buy.py failed" case.
type WalletInfo ¶
type WalletInfo struct {
Address string // 0x-prefixed signer address from buy.py.
Token string // Upper-case symbol the caller asked for (USDC, OBOL).
Chain string // Canonical chain name the balance was read on.
AtomicUnits *big.Int // Raw on-chain balance, scaled by the token's decimals.
Decimals int // Decimals for the token, for display formatting.
}
WalletInfo is the host-side view of an agent's remote-signer wallet on a given chain, including the atomic balance for one token.
func FetchWalletInfo ¶
func FetchWalletInfo(cfg *config.Config, runtime agentruntime.Runtime, id, token, chain string) (*WalletInfo, error)
FetchWalletInfo execs `buy.py balance --chain <chain>` inside the named agent pod and parses the output to recover the signer address and the balance for `token` (e.g. "USDC" or "OBOL").
We shell out instead of re-implementing the signer + eRPC RPC walk in Go because the agent pod already has SA credentials, the signer URL, and the eRPC alias resolution wired up via the buy-x402 skill. Re-deriving all three from the host would be a meaningful surface area expansion for what is a single confirmation-line preflight.
func (WalletInfo) HumanBalance ¶
func (w WalletInfo) HumanBalance() string
HumanBalance returns the balance with the minimum fractional digits needed (whole numbers render as "12", dust as "0.001"). Matches the host-side summary formatter (cmd/obol/buy.go::formatTokenAmount) so balance lines and budget lines look consistent.