You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
asset: obol should resolve to the configured OBOL token for the selected network, including address, decimals, symbol, and EIP-3009 domain metadata required to sign/verify TransferWithAuthorization.
Current surface validation
A quick code audit shows OBOL-token settlement is not release-ready yet. The current sell path is still USDC-shaped in several places.
x402 integration tests and mock facilitator fixtures assume USDC/base-sepolia.
Need a dedicated OBOL-token fixture path, ideally using an ERC-3009-compatible test OBOL token on Anvil and/or official base-sepolia OBOL from Make offical base-sepolia OBOL + faucet #406.
Implementation checklist
1. Token/asset model
Add spec.payment.asset to the ServiceOffer CRD.
Add PaymentTerms.Asset string to Go schema.
Define asset aliases:
usdc
obol
0x... explicit token address, optional later
Add a token resolver, e.g. ResolvePaymentAsset(network, asset) returning:
network ID
token address
symbol
decimals
EIP-3009 domain name
EIP-3009 domain version
Default payment.asset to usdc for backwards compatibility.
2. Seller/verifier path
Add asset/token metadata to PricingConfig and RouteRule.
Have monetize.py preserve payment.asset into x402-pricing routes.
Replace verifier's unconditional NewUSDCPaymentRequirement() with generalized payment requirement construction.
Ensure 402 response for asset: obol includes:
asset: <OBOL token address>
maxAmountRequired converted using OBOL decimals
extra.name and extra.version for the OBOL EIP-3009 domain
Update metrics/status labels or output to include asset symbol/address where useful.
3. CLI/status/docs
Add --payment-asset usdc|obol|0x... to obol sell inference and obol sell http.
Replace “USDC recipient wallet” language with “payment recipient wallet”.
obol sell status should show route asset, not only price/chain/payTo.
Document Spark2 OBOL example as the release-path smoke.
4. Buyer path
buy.py probe prints asset symbol/address and atomic amount without saying USDC unless asset is USDC.
buy.py buy signs typed data using the payment requirement's asset + extra.name/version, not hardcoded USDC constants.
Balance checks use the requirement asset address and decimals.
Buyer ConfigMap preserves asset symbol/decimals/domain metadata, not just asset address.
PreSignedSigner.GetTokens() returns the configured token symbol/decimals instead of hardcoded USDC/6.
5. Validation tests
Unit test: payment.asset survives CRD/schema/JSON/YAML roundtrip.
Unit test: asset=obol resolves to the expected network token address/domain/decimals.
Unit test: verifier emits an OBOL PaymentRequirement with the OBOL asset, amount, and domain metadata.
Unit test: buyer pre-signs authorization against the OBOL verifying contract/domain.
E2E release smoke: Spark2 endpoint exposed through ServiceOffer with payment.asset=obol; unpaid request gets OBOL 402; paid request reaches Spark2.
External dependency / open question
If we use base-sepolia for the release smoke, #406 is a blocker/adjacent dependency: we need an official test OBOL token + faucet or a documented temporary OBOL-compatible ERC-3009 token for e2e testing.
Also confirm the production OBOL token/payment token supports the exact EIP-3009 transferWithAuthorization flow required by x402. If OBOL does not support ERC-3009, we need a facilitator/scheme/token wrapper decision before this can ship.
Release target
Next release horizon: sell the Spark2 external vLLM/OpenAI-compatible inference endpoint with OBOL token as the x402 settlement asset.
This is the payment-surface blocker for #439. Keep scope minimal:
ServiceOffer/route for the Spark2 endpointaccepts[]negotiation yetDesired seller UX
Example shape:
CRD shape:
asset: obolshould resolve to the configured OBOL token for the selected network, including address, decimals, symbol, and EIP-3009 domain metadata required to sign/verifyTransferWithAuthorization.Current surface validation
A quick code audit shows OBOL-token settlement is not release-ready yet. The current sell path is still USDC-shaped in several places.
Sell-side gaps
internal/embed/infrastructure/base/templates/serviceoffer-crd.yamlspec.paymenthasnetwork,payTo,price, but noasset.internal/schemas/payment.goPaymentTermshas no asset field.PriceTablecomments and conversion logic assume USDC-style human decimals.cmd/obol/sell.go--payment-assetflag.buildInferenceServiceOfferSpec()does not emitpayment.asset.internal/embed/skills/sell/scripts/monetize.pyget_asset()accessor._add_pricing_route()writesprice,payTo,network, but notasset/token metadata.internal/x402/config.goPricingConfig/RouteRulehave no asset/token fields.ResolveChain()returns x402-goChainConfigvalues with USDC addresses.internal/x402/verifier.gox402lib.NewUSDCPaymentRequirement(...).assetis the chain's USDC address.PaymentRequirementconstruction or a generalized token resolver.Buy-side gaps
The buy side is closer, but still has USDC assumptions:
internal/x402/buyer/config.goassetaddress inUpstreamConfig, good.internal/x402/buyer/signer.goCanSign()comparesreq.Asset, good.GetTokens()hardcodes symbolUSDCand decimals6.internal/embed/skills/buy-inference/scripts/buy.pyUSDC_CONTRACTS,USDC_DOMAIN_NAME,USDC_DOMAIN_VERSION._presign_auths()signs typed data using USDC domain metadata even if the 402 requirement asset is OBOL.balance,probe,status, and budget output are USDC-worded/micro-unit-worded.paymentRequirement.extra.name/version, which is where the verifier should publish token EIP-3009 domain metadata.Test gaps
internal/testutil/eip712_signer.gohardcodes USDC contract/domain.Implementation checklist
1. Token/asset model
spec.payment.assetto the ServiceOffer CRD.PaymentTerms.Asset stringto Go schema.usdcobol0x...explicit token address, optional laterResolvePaymentAsset(network, asset)returning:payment.assettousdcfor backwards compatibility.2. Seller/verifier path
asset/token metadata toPricingConfigandRouteRule.monetize.pypreservepayment.assetintox402-pricingroutes.NewUSDCPaymentRequirement()with generalized payment requirement construction.asset: obolincludes:asset: <OBOL token address>maxAmountRequiredconverted using OBOL decimalsextra.nameandextra.versionfor the OBOL EIP-3009 domain3. CLI/status/docs
--payment-asset usdc|obol|0x...toobol sell inferenceandobol sell http.obol sell statusshould show route asset, not only price/chain/payTo.4. Buyer path
buy.py probeprints asset symbol/address and atomic amount without saying USDC unless asset is USDC.buy.py buysigns typed data using the payment requirement's asset +extra.name/version, not hardcoded USDC constants.PreSignedSigner.GetTokens()returns the configured token symbol/decimals instead of hardcoded USDC/6.5. Validation tests
payment.assetsurvives CRD/schema/JSON/YAML roundtrip.asset=obolresolves to the expected network token address/domain/decimals.PaymentRequirementwith the OBOL asset, amount, and domain metadata.ServiceOfferwithpayment.asset=obol; unpaid request gets OBOL 402; paid request reaches Spark2.External dependency / open question
If we use
base-sepoliafor the release smoke, #406 is a blocker/adjacent dependency: we need an official test OBOL token + faucet or a documented temporary OBOL-compatible ERC-3009 token for e2e testing.Also confirm the production OBOL token/payment token supports the exact EIP-3009
transferWithAuthorizationflow required by x402. If OBOL does not support ERC-3009, we need a facilitator/scheme/token wrapper decision before this can ship.Links