From 53bfe2f116f81aef2f7320a49a2e15e2a1ad5daa Mon Sep 17 00:00:00 2001 From: Sasha Aldrick Date: Tue, 29 Jul 2025 15:02:12 +0100 Subject: [PATCH 1/6] finish this --- .../site/components/pricing-calculator.tsx | 303 +++++++++--------- documentation/site/layout.tsx | 13 +- .../pages/developers/tutorials/pricing.mdx | 2 +- .../pages/developers/tutorials/request.mdx | 2 +- 4 files changed, 155 insertions(+), 165 deletions(-) diff --git a/documentation/site/components/pricing-calculator.tsx b/documentation/site/components/pricing-calculator.tsx index b008b2ff6..ac3823473 100644 --- a/documentation/site/components/pricing-calculator.tsx +++ b/documentation/site/components/pricing-calculator.tsx @@ -1,37 +1,61 @@ import NumberFlow from "@number-flow/react"; -import { useEffect, useState } from "react"; -import { useBlockNumber, usePublicClient } from "wagmi"; +import { useState } from "react"; -const SEPOLIA_CHAIN_ID = 11_155_111; +type Network = "base-mainnet" | "base-sepolia"; type PricingInputs = { programCycles: number; desiredTimeMinutes: number; }; -function calculateSuggestion(cycles: number, minutes: number, blocksPerMinute: number) { - const basePrice = (cycles / 1_000_000) * 0.0001; +type NetworkConfig = { + name: string; + blocksPerMinute: number; + currencySymbol: string; + basePriceMultiplier: number; +}; + +const NETWORK_CONFIGS: Record = { + "base-mainnet": { + name: "Base Mainnet", + blocksPerMinute: 30, // ~2 second block time + currencySymbol: "ETH", + basePriceMultiplier: 1, + }, + "base-sepolia": { + name: "Base Sepolia Testnet", + blocksPerMinute: 30, + currencySymbol: "Base SepETH", + basePriceMultiplier: 0.001, // Much cheaper for testnet + }, +}; + +function calculateSuggestion(cycles: number, minutes: number, networkConfig: NetworkConfig) { + const basePrice = (cycles / 1_000_000) * 0.0001 * networkConfig.basePriceMultiplier; const biddingStartDelay = Math.ceil(cycles / (30 * 1_000_000)); // cycles / 30MHz in blocks + const lockInStakeUSDC = basePrice * 4 * 3000; + return { minPrice: basePrice, maxPrice: basePrice * 2, biddingStartDelay, - rampUpBlocks: Math.min(100, Math.ceil(minutes * 0.5 * blocksPerMinute)), // Cap at 100 blocks - timeoutBlocks: Math.ceil(minutes * blocksPerMinute), - lockInStake: basePrice * 4, + rampUpBlocks: Math.min(100, Math.ceil(minutes * 0.5 * networkConfig.blocksPerMinute)), + timeoutBlocks: Math.ceil(minutes * networkConfig.blocksPerMinute), + lockInStake: lockInStakeUSDC, }; } export default function PricingCalculator() { - const { data: blockNumber } = useBlockNumber({ chainId: SEPOLIA_CHAIN_ID }); - const publicClient = usePublicClient({ chainId: SEPOLIA_CHAIN_ID }); - const [blocksPerMinute, setBlocksPerMinute] = useState(undefined); + const [network, setNetwork] = useState("base-sepolia"); const [inputs, setInputs] = useState({ programCycles: 1_000_000, desiredTimeMinutes: 10, }); + const networkConfig = NETWORK_CONFIGS[network]; + const suggestion = calculateSuggestion(inputs.programCycles, inputs.desiredTimeMinutes, networkConfig); +cd const handleNumericInput = (e, field: keyof PricingInputs) => { const value = e.target.value.replace(/[^0-9]/g, ""); setInputs((prev) => ({ @@ -40,150 +64,127 @@ export default function PricingCalculator() { })); }; - useEffect(() => { - async function calculateBlocksPerMinute() { - if (!blockNumber || !publicClient) { - return; - } - - try { - // Get current block timestamp - const currentBlock = await publicClient.getBlock(); - - // Get block from ~5 minutes ago - const pastBlock = await publicClient.getBlock({ - blockNumber: blockNumber - 25n, // ~5 minutes worth of blocks - }); - - if (currentBlock.timestamp && pastBlock.timestamp) { - const timeDiffMinutes = (Number(currentBlock.timestamp) - Number(pastBlock.timestamp)) / 60; - const blockDiff = Number(currentBlock.number - pastBlock.number); - const actualBlocksPerMinute = blockDiff / timeDiffMinutes; - - setBlocksPerMinute(Math.round(actualBlocksPerMinute)); - } - } catch (error) { - console.error("Failed to calculate blocks per minute:", error); - } - } - - calculateBlocksPerMinute(); - }, [blockNumber, publicClient]); - - const suggestion = blocksPerMinute - ? calculateSuggestion(inputs.programCycles, inputs.desiredTimeMinutes, blocksPerMinute) - : null; - return (
- {suggestion ? ( - <> -

Request Parameters Calculator

- -
-
- - handleNumericInput(e, "programCycles")} - className="w-full rounded border border-[var(--vocs-color_border);] px-3 py-2" - /> -
- -
- - handleNumericInput(e, "desiredTimeMinutes")} - className="w-full rounded border border-[var(--vocs-color_border);] px-3 py-2" - /> -
- -
-

Suggested Parameters

-
-
-
-
Minimum Price:
-
- -
-
-
-
Maximum Price:
-
- -
-
-
-
Bidding Start Delay:
-
- -
-
-
-
Ramp-up Period:
-
- -
-
-
-
Timeout:
-
- -
-
-
-
Lock-in Stake:
-
- -
-
-
+
+ {/* Network Dropdown */} +
+ + +
+ +
+ + handleNumericInput(e, "programCycles")} + className="w-full rounded border border-[var(--vocs-color_border);] px-3 py-2" + /> +
+ +
+ + handleNumericInput(e, "desiredTimeMinutes")} + className="w-full rounded border border-[var(--vocs-color_border);] px-3 py-2" + /> +
+ +
+

Suggested Parameters

+
+
+
+
Minimum Price:
+
+ +
+
+
+
Maximum Price:
+
+ +
+
+
+
Bidding Start Delay:
+
+ +
+
+
+
Ramp-up Period:
+
+ +
+
+
+
Timeout:
+
+ +
+
+
+
Lock-in Stake:
+
+ +
-
+
- - ) : null} +
+
); } diff --git a/documentation/site/layout.tsx b/documentation/site/layout.tsx index e6af2fa11..ad6cecbdf 100644 --- a/documentation/site/layout.tsx +++ b/documentation/site/layout.tsx @@ -1,13 +1,4 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import { http, WagmiProvider, createConfig } from "wagmi"; -import { sepolia } from "wagmi/chains"; - -const config = createConfig({ - chains: [sepolia], - transports: { - [sepolia.id]: http(), - }, -}); const queryClient = new QueryClient(); @@ -21,9 +12,7 @@ export default function RootLayout({ children }) { {/* Custom JS scripts */}