Conversation
There was a problem hiding this comment.
Pull request overview
This PR restructures the website’s blockchain-related content around a new /lab hub and adds a new /x402 developer documentation page (including an interactive USDC approval widget), while removing an old facilitator proposal document from x402_facilitator/.
Changes:
- Add new
/labhub page and wire it into the main navigation + homepage. - Add new
/x402documentation page with diagrams, API reference, and a wallet-based USDC approval component. - Remove
x402_facilitator/PUBLIC_FACILITATOR_PROPOSAL.md.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| x402_facilitator/PUBLIC_FACILITATOR_PROPOSAL.md | Removes a long-form public facilitator proposal doc. |
| website/pages/x402/+title.ts | Adds SEO title for /x402. |
| website/pages/x402/+description.ts | Adds SEO description for /x402. |
| website/pages/x402/+Page.tsx | Adds the full /x402 documentation page content and live status fetch. |
| website/pages/x402/+Head.tsx | Adds OG + Schema.org metadata for /x402. |
| website/pages/lab/+title.ts | Adds SEO title for /lab. |
| website/pages/lab/+description.ts | Adds SEO description for /lab. |
| website/pages/lab/+Page.tsx | Adds the /lab hub page with Cards linking to experiments/docs. |
| website/pages/lab/+Head.tsx | Adds OG + Schema.org metadata for /lab. |
| website/pages/index/+Page.tsx | Replaces the homepage “AI Image Generator” card with a “Lab” card. |
| website/layouts/LayoutDefault.tsx | Replaces “ImageGen” + “AI Assistent” nav links with a single “Lab” link. |
| website/components/FacilitatorApproval.tsx | Adds interactive ERC-20 allowance + approve UI for facilitator fee setup. |
| website/LAB_HUB_PROPOSAL.md | Adds an implementation/design proposal doc for the /lab + /x402 restructure. |
| // USDC on Optimism | ||
| const USDC_ADDRESS = "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85" as const; | ||
| const USDC_DECIMALS = 6; | ||
|
|
There was a problem hiding this comment.
The USDC address/chain are hard-coded to Optimism, but the page advertises Base support. As written, sellers on Base will end up approving Optimism USDC instead of Base USDC, and allowance reads will also be wrong. Consider making the network selectable (Optimism/Base + testnets) and derive USDC address/decimals from @fretchen/chain-utils (e.g., getUSDCConfig("eip155:10"|"eip155:8453")) or the /supported response.
| // USDC on Optimism | |
| const USDC_ADDRESS = "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85" as const; | |
| const USDC_DECIMALS = 6; | |
| type USDCConfig = { | |
| address: Address; | |
| decimals: number; | |
| }; | |
| /** | |
| * USDC configuration per chain. | |
| * | |
| * NOTE: | |
| * - Optimism mainnet (chainId 10) uses the canonical USDC at | |
| * 0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85 (6 decimals). | |
| * - Base mainnet (chainId 8453) uses the canonical USDC at | |
| * 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 (6 decimals). | |
| * | |
| * Additional chains (e.g., testnets) can be added here as needed. | |
| */ | |
| const USDC_CONFIG_BY_CHAIN_ID: Record<number, USDCConfig> = { | |
| 10: { | |
| // Optimism | |
| address: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85", | |
| decimals: 6, | |
| }, | |
| 8453: { | |
| // Base | |
| address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", | |
| decimals: 6, | |
| }, | |
| }; | |
| export function getUSDCConfig(chainId?: number): USDCConfig | undefined { | |
| if (!chainId) return undefined; | |
| return USDC_CONFIG_BY_CHAIN_ID[chainId]; | |
| } |
|
|
||
| // Write approve | ||
| const { writeContract, isPending: isApproving, data: txHash } = useWriteContract(); | ||
| const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({ hash: txHash }); |
There was a problem hiding this comment.
useWaitForTransactionReceipt() is called without a chainId. Since this component may switch chains before sending the tx, the receipt polling can end up on the wrong chain if the active chain changes afterwards. Pass chainId: optimism.id (or the selected chain) to ensure receipt tracking matches where approve() was sent.
| const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({ hash: txHash }); | |
| const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({ | |
| hash: txHash, | |
| chainId: optimism.id, | |
| }); |
| interface FacilitatorApprovalProps { | ||
| facilitatorAddress?: Address | null; | ||
| } | ||
|
|
||
| export function FacilitatorApproval({ facilitatorAddress: propAddress }: FacilitatorApprovalProps) { | ||
| const { address, isConnected, chainId } = useAccount(); | ||
| const { switchChainAsync } = useSwitchChain(); | ||
| const [facilitatorAddress, setFacilitatorAddress] = useState<Address | null>(propAddress ?? null); | ||
| const [fetchError, setFetchError] = useState<string | null>(null); | ||
|
|
||
| // Fetch facilitator address from /supported if not provided via props | ||
| useEffect(() => { | ||
| if (propAddress) { | ||
| setFacilitatorAddress(propAddress); | ||
| return; | ||
| } | ||
|
|
||
| const controller = new AbortController(); | ||
| fetch("https://facilitator.fretchen.eu/supported", { signal: controller.signal }) | ||
| .then((res) => { | ||
| if (!res.ok) throw new Error(`HTTP ${res.status}`); | ||
| return res.json(); | ||
| }) | ||
| .then((json) => { | ||
| // Extract facilitator address from fee extension | ||
| const feeExt = json.extensions?.find((ext: Record<string, unknown>) => ext.name === "facilitator_fee"); | ||
| const recipient = (feeExt as Record<string, Record<string, string>>)?.fee?.recipient; | ||
| if (recipient) { | ||
| setFacilitatorAddress(recipient as Address); | ||
| } else { | ||
| setFetchError("Facilitator address not found in /supported response"); | ||
| } | ||
| }) | ||
| .catch((err) => { | ||
| if (err.name !== "AbortError") { | ||
| setFetchError(err.message); | ||
| } | ||
| }); | ||
| return () => controller.abort(); | ||
| }, [propAddress]); |
There was a problem hiding this comment.
This new component contains non-trivial logic (fetching/parsing /supported, chain switching, contract reads/writes) but has no accompanying tests. Given the existing website test suite, adding a minimal test file covering disconnected vs connected rendering and parsing of the facilitator_fee recipient would help prevent regressions.
website/pages/x402/+Page.tsx
Outdated
| <a href="/agent-onboarding">Agent onboarding</a> — build your own x402-protected service | ||
| </li> | ||
| <li> | ||
| <a href="/blog/x402_facilitator_imagegen">Blog post</a> — detailed write-up on building the facilitator |
There was a problem hiding this comment.
The link to the facilitator blog post uses a slug path (/blog/x402_facilitator_imagegen), but blog routes in this site are numeric (/blog/:id). This will 404 unless there is an external redirect. Link to the correct numeric post URL (e.g. the x402 post appears to be /blog/22 based on publishing_date ordering) or add a slug route/redirect mechanism.
| <a href="/blog/x402_facilitator_imagegen">Blog post</a> — detailed write-up on building the facilitator | |
| <a href="/blog/22">Blog post</a> — detailed write-up on building the facilitator |
| <th>Network</th> | ||
| <th>Chain ID</th> | ||
| <th>USDC address</th> | ||
| <th>Environment</th> | ||
| </tr> | ||
| </thead> | ||
| <tbody> | ||
| <tr> | ||
| <td>Optimism</td> | ||
| <td>eip155:10</td> | ||
| <td> | ||
| <code>0x0b2C…39C5</code> | ||
| </td> | ||
| <td>Production</td> | ||
| </tr> | ||
| <tr> | ||
| <td>Base</td> | ||
| <td>eip155:8453</td> | ||
| <td> | ||
| <code>0x833…89a6</code> | ||
| </td> | ||
| <td>Production</td> | ||
| </tr> | ||
| <tr> | ||
| <td>OP Sepolia</td> | ||
| <td>eip155:11155420</td> | ||
| <td> | ||
| <code>0x5fd…cE43</code> | ||
| </td> | ||
| <td>Testnet</td> | ||
| </tr> | ||
| <tr> | ||
| <td>Base Sepolia</td> | ||
| <td>eip155:84532</td> | ||
| <td> | ||
| <code>0x03…6b31</code> | ||
| </td> | ||
| <td>Testnet</td> | ||
| </tr> |
There was a problem hiding this comment.
The abbreviated USDC addresses in this table don't match the actual addresses referenced earlier on the page (and in @fretchen/chain-utils). For example Optimism USDC ends with ...Ff85, not ...39C5, and Base USDC ends with ...2913. Please correct these abbreviations to avoid misleading users copying addresses.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
….github.io into bc_restructure
No description provided.