Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
1eb8288
chore(x402): add coinbase/x402/go v2 dependency + local shims
bussyjd Apr 8, 2026
a07dd1f
chore(x402): swap all mark3labs/x402-go imports to v2 SDK + local shims
bussyjd Apr 8, 2026
deff499
chore(x402): remove mark3labs/x402-go dependency
bussyjd Apr 8, 2026
7f092a5
fix: x402 verifier TLS and facilitator compatibility
bussyjd Apr 8, 2026
db38000
feat: switch default facilitator to Obol-operated x402.gcp.obol.tech
bussyjd Apr 8, 2026
e470d33
feat: LiteLLM zero-downtime config and hot-reload
bussyjd Apr 8, 2026
bccc02d
fix: allow obolup.sh to install openclaw via Docker when Node.js is m…
bussyjd Apr 8, 2026
4f4d03c
feat: PurchaseRequest CRD and controller for buy-side x402 (#329)
bussyjd Apr 8, 2026
0ba1a3b
feat: buy.py creates PurchaseRequest CR instead of direct ConfigMap w…
bussyjd Apr 8, 2026
f7da011
fix: eRPC Host routing + private-key-file priority for sell register
bussyjd Apr 8, 2026
7d55060
fix: anchored sed patterns for Bob's port remapping
bussyjd Apr 8, 2026
fecb609
fix: add polling wait for pod readiness in flow-11
bussyjd Apr 8, 2026
9ad6480
fix: port check uses LISTEN state only (ignore FIN_WAIT)
bussyjd Apr 8, 2026
341a6f5
fix: macOS grep/kubectl compat in flow-11
bussyjd Apr 8, 2026
2c8a2b8
feat: flow-11 uses PurchaseRequest CR path for buy verification
bussyjd Apr 8, 2026
7c7f929
fix: consolidate all flow-11 fixes (polling, ports, sed, LISTEN)
bussyjd Apr 8, 2026
650764c
fix: widen agent response validation + provide model name in buy prompt
bussyjd Apr 8, 2026
86e3668
feat: auto-fund Bob's remote-signer wallet in flow-11 (shortcut for #…
bussyjd Apr 8, 2026
06b2f5c
fix: buy.py handles 409 Conflict with resourceVersion on PurchaseRequ…
bussyjd Apr 8, 2026
752dad1
fix: controller signer key format + flow-11 robustness
bussyjd Apr 8, 2026
abd4a0c
feat: embed pre-signed auths in PurchaseRequest spec (no cross-NS sec…
bussyjd Apr 8, 2026
7efd658
fix: wallet address extraction + discovery validation keywords
bussyjd Apr 8, 2026
6eae339
fix: add explicit LiteLLM model entry for paid routes with colons + s…
bussyjd Apr 8, 2026
cb520b2
fix: use kubectl replace for CA bundle to avoid annotation size limit
bussyjd Apr 8, 2026
37920e6
fix: restart x402-verifier after CA bundle population
bussyjd Apr 8, 2026
ed87f02
feat: LiteLLM API model management + buyer sidecar reload
bussyjd Apr 9, 2026
5ef467f
merge x402 v2 into buy-side integration
bussyjd Apr 9, 2026
1d8c8ed
fix: bump LiteLLM fork image to sha-778111d
bussyjd Apr 9, 2026
b15beaa
security: fix shell injection in kubectl exec + document gotcha
bussyjd Apr 9, 2026
8bf2458
Merge branch 'feat/litellm-api-management' into feat/x402-buy-side-in…
bussyjd Apr 9, 2026
d1d83f5
fix: bump LiteLLM fork image to sha-c16b156 (multiplatform)
bussyjd Apr 9, 2026
11df1cb
Merge branch 'feat/litellm-api-management' into feat/x402-buy-side-in…
bussyjd Apr 9, 2026
faba591
fix buy-side convergence and release validation
bussyjd Apr 9, 2026
7494a4e
cleanup buy-side docs and dead paths
bussyjd Apr 9, 2026
963e4f6
docs: align x402 references with validated flow
bussyjd Apr 9, 2026
e08d34b
remove in-repo release validation doc
bussyjd Apr 9, 2026
7fb8fe0
fix: default x402 template to Obol facilitator
bussyjd Apr 10, 2026
f57498e
harden buy-side controller boundaries
bussyjd Apr 10, 2026
e26d162
fix: disable legacy buy-side mutation commands
bussyjd Apr 10, 2026
16a2d83
fix(rc3): repair PurchaseRequest CRD + hot-add LiteLLM models without…
bussyjd Apr 10, 2026
0f7118b
fix(rc3): align x402 flows and purchase readiness
bussyjd Apr 10, 2026
d7d5f05
test(erc8004): cover metadata revert propagation
bussyjd Apr 10, 2026
57a047f
fix: retry litellm hot-delete for stale paid routes
bussyjd Apr 10, 2026
32f04e8
Updates from claude review
OisinKyne Apr 10, 2026
5d034ca
Add asset metadata plumbing for non-USDC x402 offers
bussyjd Apr 10, 2026
21ca390
Simplify OBOL asset selection to a single sell flag
bussyjd Apr 10, 2026
01e2d75
Add OBOL fork-token buy path for Permit2 tests
bussyjd Apr 10, 2026
478f64e
Fix local OBOL Permit2 integration path
bussyjd Apr 11, 2026
ba941d9
Polish local OBOL Permit2 test flow
bussyjd Apr 11, 2026
64efcb3
Benchmark OBOL exact and session payment paths
bussyjd Apr 11, 2026
cb8d2d7
Harden exact OBOL auth pool limits
bussyjd Apr 11, 2026
df0b7c1
Trim session prototype from exact path PR
bussyjd Apr 11, 2026
c117e34
Clarify exact-only scope in OBOL report
bussyjd Apr 11, 2026
9b9e86c
Obol token support, needs facilitator support on mainnet
OisinKyne Apr 13, 2026
0960fa0
Merge origin/main and PR340 token UX into OBOL exact path
bussyjd Apr 14, 2026
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
29 changes: 29 additions & 0 deletions .agents/skills/obol-stack-dev/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,35 @@ obol kubectl exec -i -n openclaw-<id> deploy/openclaw -c openclaw -- python3 - <
- Delegate or accept broad "review the architecture" findings without converting them into concrete file-level checks and reproducible tests.
- Push `codex/`-prefixed branches to GitHub from this repository.

## Adding a New Payment Token

The `--token` flag (`obol sell inference`, `obol sell http`) selects the payment token from a whitelist registry in `internal/x402/tokens.go`. To add support for a new ERC-20 token:

1. **Add registry entry** in `internal/x402/tokens.go` — one entry per (token, chain) pair:
```go
"WETH": {
"base": {Address: "0x4200000000000000000000000000000000000006", Symbol: "WETH", Decimals: 18, TransferMethod: "permit2", EIP712Name: "Wrapped Ether", EIP712Version: "1"},
},
```

2. **Determine the transfer method**:
- `eip3009` — token natively implements `transferWithAuthorization` (USDC, EURC)
- `permit2` — uses Uniswap Permit2 (`0x000000000022D473030F116dDEE9F6B43aC78BA3`) for authorization; works with any ERC-20

3. **Per-chain considerations**:
- Verify the token contract is deployed on each target chain (the buy-side validates this via `eth_getCode` before signing)
- Verify the EIP-712 domain name/version match the on-chain contract — wrong values produce invalid signatures
- The x402 facilitator must have the target chain configured (check `obol-infrastructure/helm-charts/x402-facilitator/templates/configmap.yaml` for `eip155:<chainId>` entries)
- The x402 ExactPermit2Proxy (`0x402085c248EeA27D92E8b30b2C58ed07f9E20001`) must be deployed on the chain for Permit2 tokens

4. **Add tests** in `internal/x402/tokens_test.go`:
- Verify `ResolveToken("WETH", "base")` returns the correct entry
- Verify `ResolveToken("WETH", "ethereum")` returns false if not registered there

5. **No CLI changes needed** — `--token WETH` resolves automatically from the registry.

6. **buy.py handles both paths** — it reads `extra.assetTransferMethod` from the 402 response and auto-selects the ERC-3009 or Permit2 signing flow. Contract existence is validated before signing.

## Sell-Side Monetize Lifecycle

### Architecture
Expand Down
6 changes: 3 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,13 @@ Components: eRPC (`erpc` ns), Frontend (`obol-frontend` ns), Cloudflared (`traef

## Monetize Subsystem

Payment-gated access to cluster services via x402 (HTTP 402 micropayments, USDC on Base/Base Sepolia, Traefik ForwardAuth).
Payment-gated access to cluster services via x402 (HTTP 402 micropayments, Traefik ForwardAuth). Supports USDC (EIP-3009) and OBOL (Permit2) via `--token [USDC|OBOL]` flag; default is USDC on Base Mainnet. Token registry in `internal/x402/tokens.go`.

**Sell-side flow**: `obol sell http` → creates ServiceOffer CR → serviceoffer-controller reconciles ModelReady → UpstreamHealthy → PaymentGateReady (x402 Middleware) → RoutePublished (HTTPRoute) → Registered (RegistrationRequest + optional ERC-8004 side effects) → Ready. Traefik routes `/services/<name>/*` through ForwardAuth to upstream.

**Buy-side flow**: `buy.py probe` sees 402 pricing → `buy.py buy` pre-signs ERC-3009 auths into ConfigMaps → LiteLLM serves static `paid/<remote-model>` aliases through the in-pod `x402-buyer` sidecar → each paid request spends one auth and forwards to the remote seller.
**Buy-side flow**: `buy.py probe` sees 402 pricing → `buy.py buy` validates token contract exists on-chain → pre-signs payment auths (ERC-3009 for USDC, Permit2 for OBOL) into ConfigMaps → LiteLLM serves static `paid/<remote-model>` aliases through the in-pod `x402-buyer` sidecar → each paid request spends one auth and forwards to the remote seller.

**CLI**: `obol sell pricing --wallet --chain`, `obol sell inference <name> --model --price|--per-mtok`, `obol sell http <name> --wallet --chain --price|--per-request|--per-mtok --upstream --port --namespace --health-path`, `obol sell list|status|stop|delete`, `obol sell register --name --private-key-file`.
**CLI**: `obol sell pricing --wallet --chain`, `obol sell inference <name> --model --price|--per-mtok [--token USDC|OBOL]`, `obol sell http <name> --wallet --chain --price|--per-request|--per-mtok --upstream --port --namespace --health-path [--token USDC|OBOL]`, `obol sell list|status|stop|delete`, `obol sell register --name --private-key-file`.

**ServiceOffer CRD** (`obol.org`): Source of truth for monetized service intent. Spec fields — `type` (inference|fine-tuning|http), `model{name,runtime}`, `upstream{service,namespace,port,healthPath}`, `payment{scheme,network,payTo,price{perRequest,perMTok,perHour}}`, `path`, `registration{enabled,name,description,image,skills,domains,supportedTrust}`.

Expand Down
158 changes: 128 additions & 30 deletions cmd/obol/sell.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ Examples:
Usage: "Payment chain (base, base-sepolia, ethereum)",
Value: "base",
},
&cli.StringFlag{
Name: "token",
Usage: "Payment token (USDC, OBOL)",
Value: "USDC",
},
&cli.StringFlag{
Name: "facilitator",
Usage: "x402 facilitator URL",
Expand Down Expand Up @@ -258,7 +263,13 @@ Examples:
}
}

chain, err := x402verifier.ResolveChainInfo(cmd.String("chain"))
chainName := cmd.String("chain")
assetTerms, err := resolveAssetTerms(cmd, &chainName)
if err != nil {
return err
}

chain, err := x402verifier.ResolveChainInfo(chainName)
if err != nil {
return err
}
Expand All @@ -281,7 +292,7 @@ Examples:
WalletAddress: wallet,
PricePerRequest: perRequest,
PricePerMTok: priceTable.PerMTok,
Chain: cmd.String("chain"),
Chain: chainName,
FacilitatorURL: cmd.String("facilitator"),
VMMode: cmd.Bool("vm"),
VMImage: cmd.String("vm-image"),
Expand Down Expand Up @@ -347,7 +358,7 @@ Examples:
d.NoPaymentGate = false
} else {
// Create a ServiceOffer CR pointing at the host service.
soSpec, err := buildInferenceServiceOfferSpec(d, priceTable, svcNs, port)
soSpec, err := buildInferenceServiceOfferSpec(d, priceTable, svcNs, port, assetTerms)
if err != nil {
return err
}
Expand Down Expand Up @@ -412,6 +423,11 @@ Example:
Usage: "Payment chain (base, base-sepolia, ethereum)",
Value: "base",
},
&cli.StringFlag{
Name: "token",
Usage: "Payment token (USDC, OBOL)",
Value: "USDC",
},
&cli.StringFlag{
Name: "price",
Usage: "Per-request price in USDC (e.g. 0.001)",
Expand Down Expand Up @@ -580,10 +596,10 @@ Example:
ns := cmd.String("namespace")

if cmd.String("upstream") == "" {
return fmt.Errorf("upstream service name required: use --upstream <service-name>\n\n Example: obol sell http %s --upstream my-svc --port 8080 --wallet 0x... --chain base --price 0.001", name)
return fmt.Errorf("upstream service name required: use --upstream <service-name>\n\n Example: obol sell http %s --upstream my-svc --port 8080 --wallet 0x... --chain base-sepolia --price 0.001", name)
}
if cmd.Int("port") == 0 {
return fmt.Errorf("upstream port required: use --port <port-number>\n\n Example: obol sell http %s --upstream my-svc --port 8080 --wallet 0x... --chain base --price 0.001", name)
return fmt.Errorf("upstream port required: use --port <port-number>\n\n Example: obol sell http %s --upstream my-svc --port 8080 --wallet 0x... --chain base-sepolia --price 0.001", name)
}

priceTable, err := resolvePriceTable(cmd, true)
Expand All @@ -602,6 +618,12 @@ Example:
price["perHour"] = priceTable.PerHour
}

chainName := cmd.String("chain")
assetTerms, err := resolveAssetTerms(cmd, &chainName)
if err != nil {
return err
}

spec := map[string]any{
"type": "http",
"upstream": map[string]any{
Expand All @@ -612,12 +634,15 @@ Example:
},
"payment": map[string]any{
"scheme": "exact",
"network": cmd.String("chain"),
"network": chainName,
"payTo": wallet,
"maxTimeoutSeconds": cmd.Int("max-timeout"),
"price": price,
},
}
if !assetTerms.IsZero() {
spec["payment"].(map[string]any)["asset"] = assetTerms
}

if path := cmd.String("path"); path != "" {
spec["path"] = path
Expand Down Expand Up @@ -701,7 +726,7 @@ Example:
}
u.Successf("ServiceOffer %s/%s %s (type: http)", ns, name, action)
if priceTable.PerMTok != "" {
u.Infof("Requests will be charged at %s", formatPriceTableSummary(priceTable))
u.Infof("Requests will be charged at %s", formatPriceTableSummary(priceTable, assetTerms.Symbol))
}
u.Infof("The agent will reconcile: health-check → payment gate → route")
u.Infof("Check status: obol sell status %s -n %s", name, ns)
Expand Down Expand Up @@ -840,7 +865,7 @@ func sellStatusCommand(cfg *config.Config) *cli.Command {
u.Printf("Local Inference Gateways:")
for _, d := range deployments {
u.Printf(" %-20s %s → %s %s chain=%s",
d.Name, d.ListenAddr, d.UpstreamURL, formatInferencePriceSummary(d), d.Chain)
d.Name, d.ListenAddr, d.UpstreamURL, formatInferencePriceSummary(d, ""), d.Chain)
}
}

Expand Down Expand Up @@ -926,7 +951,7 @@ func sellStatusGlobalJSON(cfg *config.Config, u *ui.UI) error {
Name: d.Name,
ListenAddr: d.ListenAddr,
UpstreamURL: d.UpstreamURL,
Price: formatInferencePriceSummary(d),
Price: formatInferencePriceSummary(d, ""),
Chain: d.Chain,
})
}
Expand Down Expand Up @@ -1276,7 +1301,7 @@ registration on networks that offer it (e.g. ethereum mainnet).

Examples:
obol sell register # interactive, defaults to base
obol sell register --chain base-sepolia # register on base-sepolia
obol sell register --chain base # register on base
obol sell register --chain mainnet,base # register on multiple chains
obol sell register --chain mainnet --sponsored # zero-gas on ethereum mainnet`,
Flags: []cli.Flag{
Expand Down Expand Up @@ -1760,7 +1785,7 @@ func sellInfoCommand(cfg *config.Config) *cli.Command {
u.Printf("Listen: %s", d.ListenAddr)
u.Printf("Upstream: %s", d.UpstreamURL)
u.Printf("Wallet: %s", d.WalletAddress)
u.Printf("Price: %s", formatInferencePriceSummary(d))
u.Printf("Price: %s", formatInferencePriceSummary(d, ""))
u.Printf("Chain: %s", d.Chain)
u.Printf("Facilitator: %s", d.FacilitatorURL)
u.Printf("Created: %s", d.CreatedAt)
Expand Down Expand Up @@ -1874,52 +1899,122 @@ func resolvePriceTable(cmd *cli.Command, allowPerHour bool) (schemas.PriceTable,
if allowPerHour {
return schemas.PriceTable{}, errors.New("price required: use --price, --per-request, --per-mtok, or --per-hour")
}

return schemas.PriceTable{}, errors.New("price required: use --price, --per-request, or --per-mtok")
}
}

func formatPriceTableSummary(priceTable schemas.PriceTable) string {
func resolveAssetTerms(cmd *cli.Command, chainName *string) (schemas.AssetTerms, error) {
tokenName := strings.ToUpper(strings.TrimSpace(cmd.String("token")))

// USDC = chain default — no asset override needed.
if tokenName == "USDC" {
return schemas.AssetTerms{}, nil
}

if chainName == nil {
return schemas.AssetTerms{}, fmt.Errorf("internal error: chain name pointer is nil")
}

// For non-default tokens, default to ethereum when --chain is not explicit.
if !cmd.IsSet("chain") {
if envChain := strings.TrimSpace(os.Getenv("OBOL_TOKEN_CHAIN")); envChain != "" {
*chainName = envChain
} else {
*chainName = "ethereum"
}
}

// Env var overrides bypass the registry — used for test deployments on
// chains where the token isn't officially deployed (e.g., fork-local OBOL
// on Base Sepolia via OBOL_TOKEN_ADDRESS).
if addr := strings.TrimSpace(os.Getenv("OBOL_TOKEN_ADDRESS")); addr != "" {
return schemas.AssetTerms{
Address: addr,
Symbol: envOrDefault("OBOL_TOKEN_SYMBOL", "OBOL"),
Decimals: 18,
TransferMethod: schemas.AssetTransferMethodPermit2,
EIP712Name: envOrDefault("OBOL_TOKEN_NAME", "Obol Network"),
EIP712Version: envOrDefault("OBOL_TOKEN_VERSION", "1"),
}, nil
}

// Registry lookup.
entry, ok := x402verifier.ResolveToken(tokenName, *chainName)
if !ok {
return schemas.AssetTerms{}, fmt.Errorf(
"--token %s is not available on chain %s (supported tokens: %s)",
tokenName, *chainName, strings.Join(x402verifier.SupportedTokens(), ", "),
)
}

return schemas.AssetTerms{
Address: entry.Address,
Symbol: entry.Symbol,
Decimals: entry.Decimals,
TransferMethod: entry.TransferMethod,
EIP712Name: entry.EIP712Name,
EIP712Version: entry.EIP712Version,
}, nil
}

func envOrDefault(key, fallback string) string {
if v := strings.TrimSpace(os.Getenv(key)); v != "" {
return v
}
return fallback
}

func formatPriceTableSummary(priceTable schemas.PriceTable, symbol string) string {
if symbol == "" {
symbol = "USDC"
}
switch {
case priceTable.PerRequest != "":
return priceTable.PerRequest + " USDC/request"
return fmt.Sprintf("%s %s/request", priceTable.PerRequest, symbol)
case priceTable.PerMTok != "":
return fmt.Sprintf("%s USDC/request (approx from %s USDC/MTok @ %d tok/request)",
priceTable.EffectiveRequestPrice(),
priceTable.PerMTok,
return fmt.Sprintf("%s %s/request (approx from %s %s/MTok @ %d tok/request)",
priceTable.EffectiveRequestPrice(), symbol,
priceTable.PerMTok, symbol,
schemas.ApproxTokensPerRequest,
)
case priceTable.PerHour != "":
return fmt.Sprintf("%s USDC/request (approx from %s USDC/hour @ %d min/request)",
priceTable.EffectiveRequestPrice(),
priceTable.PerHour,
return fmt.Sprintf("%s %s/request (approx from %s %s/hour @ %d min/request)",
priceTable.EffectiveRequestPrice(), symbol,
priceTable.PerHour, symbol,
schemas.ApproxMinutesPerRequest,
)
default:
return "0 USDC/request"
return fmt.Sprintf("0 %s/request", symbol)
}
}

func formatRoutePriceSummary(route x402verifier.RouteRule) string {
symbol := route.AssetSymbol
if symbol == "" {
symbol = "USDC"
}
if route.PriceModel == "perMTok" && route.PerMTok != "" && route.ApproxTokensPerRequest > 0 {
return fmt.Sprintf("%s USDC/request (approx from %s USDC/MTok @ %d tok/request)",
route.Price, route.PerMTok, route.ApproxTokensPerRequest)
return fmt.Sprintf("%s %s/request (approx from %s %s/MTok @ %d tok/request)",
route.Price, symbol, route.PerMTok, symbol, route.ApproxTokensPerRequest)
}

if route.Price != "" {
return route.Price + " USDC/request"
return fmt.Sprintf("%s %s/request", route.Price, symbol)
}

return "0 USDC/request"
return fmt.Sprintf("0 %s/request", symbol)
}

func formatInferencePriceSummary(d *inference.Deployment) string {
func formatInferencePriceSummary(d *inference.Deployment, symbol string) string {
if symbol == "" {
symbol = "USDC"
}
if d.PricePerMTok != "" && d.ApproxTokensPerRequest > 0 {
return fmt.Sprintf("%s USDC/request (approx from %s USDC/MTok @ %d tok/request)",
d.PricePerRequest, d.PricePerMTok, d.ApproxTokensPerRequest)
return fmt.Sprintf("%s %s/request (approx from %s %s/MTok @ %d tok/request)",
d.PricePerRequest, symbol, d.PricePerMTok, symbol, d.ApproxTokensPerRequest)
}

return d.PricePerRequest + " USDC/request"
return fmt.Sprintf("%s %s/request", d.PricePerRequest, symbol)
}

// loadProvenance reads a provenance JSON file and returns the parsed struct.
Expand Down Expand Up @@ -2036,7 +2131,7 @@ func resolveHostIP(cfg *config.Config) (string, error) {

// buildInferenceServiceOfferSpec builds a ServiceOffer spec for a host-side
// inference gateway routed through the cluster's x402 flow.
func buildInferenceServiceOfferSpec(d *inference.Deployment, pt schemas.PriceTable, ns, port string) (map[string]any, error) {
func buildInferenceServiceOfferSpec(d *inference.Deployment, pt schemas.PriceTable, ns, port string, asset schemas.AssetTerms) (map[string]any, error) {
portNum, err := strconv.Atoi(port)
if err != nil || portNum < 1 || portNum > 65535 {
return nil, fmt.Errorf("invalid port %q: must be a number between 1 and 65535", port)
Expand All @@ -2057,6 +2152,9 @@ func buildInferenceServiceOfferSpec(d *inference.Deployment, pt schemas.PriceTab
},
"path": "/services/" + d.Name,
}
if !asset.IsZero() {
spec["payment"].(map[string]any)["asset"] = asset
}

price := spec["payment"].(map[string]any)["price"].(map[string]any)
if pt.PerMTok != "" {
Expand Down
6 changes: 4 additions & 2 deletions cmd/obol/sell_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,14 +177,15 @@ func TestSellInference_Flags(t *testing.T) {
flags := flagMap(inf)

requireFlags(t, flags,
"model", "wallet", "price", "per-request", "per-mtok", "chain", "facilitator",
"model", "wallet", "price", "per-request", "per-mtok", "chain", "token", "facilitator",
"listen", "upstream", "enclave-tag",
"vm", "vm-image", "vm-cpus", "vm-memory", "vm-host-port",
"tee", "model-hash",
)

assertStringDefault(t, flags, "price", "0.001")
assertStringDefault(t, flags, "chain", "base")
assertStringDefault(t, flags, "token", "USDC")
assertStringDefault(t, flags, "listen", ":8402")
assertStringDefault(t, flags, "upstream", "http://localhost:11434")
assertStringDefault(t, flags, "facilitator", "https://x402.gcp.obol.tech")
Expand All @@ -201,13 +202,14 @@ func TestSellHTTP_Flags(t *testing.T) {
flags := flagMap(http)

requireFlags(t, flags,
"wallet", "chain", "price", "per-request", "per-mtok", "per-hour",
"wallet", "chain", "token", "price", "per-request", "per-mtok", "per-hour",
"namespace", "upstream", "port", "health-path", "path",
"max-timeout",
"register", "register-name", "register-description", "register-image",
)

assertStringDefault(t, flags, "chain", "base")
assertStringDefault(t, flags, "token", "USDC")
assertStringDefault(t, flags, "namespace", "default")
assertStringDefault(t, flags, "health-path", "/health")
assertIntDefault(t, flags, "port", 8080)
Expand Down
5 changes: 5 additions & 0 deletions contracts/fork-obol/foundry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[profile.default]
src = "src"
out = "out"
libs = []
solc_version = "0.8.24"
Loading
Loading