diff --git a/provider/provider/src/lib.rs b/provider/provider/src/lib.rs index 1d86216..90d3034 100644 --- a/provider/provider/src/lib.rs +++ b/provider/provider/src/lib.rs @@ -190,7 +190,7 @@ pub struct ParameterDefinition { } // --- New Provider Struct --- -#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] +#[derive(PartialEq, Clone, Debug, Serialize)] pub struct RegisteredProvider { pub provider_name: String, // Provide Node Identity (HNS entry (Node Identity) of the the process serving as the provider) @@ -203,6 +203,82 @@ pub struct RegisteredProvider { // Price per call in USDC, should be clear in HNS entry pub price: f64, pub endpoint: EndpointDefinition, + // Whether the provider is currently live/active (None = legacy, Some(false) = offline, Some(true) = online) + pub is_live: Option, +} + +// Old version of RegisteredProvider for migration purposes +#[derive(Deserialize)] +struct OldRegisteredProvider { + pub provider_name: String, + pub provider_id: String, + pub description: String, + pub instructions: String, + pub registered_provider_wallet: String, + pub price: f64, + pub endpoint: EndpointDefinition, +} + +// New version with is_live field for deserialization (avoids recursion) +#[derive(Deserialize)] +struct NewRegisteredProvider { + pub provider_name: String, + pub provider_id: String, + pub description: String, + pub instructions: String, + pub registered_provider_wallet: String, + pub price: f64, + pub endpoint: EndpointDefinition, + pub is_live: Option, +} + +// Custom Deserialize implementation for RegisteredProvider to handle migration +impl<'de> Deserialize<'de> for RegisteredProvider { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + // Use an untagged enum to try different deserialization strategies + #[derive(Deserialize)] + #[serde(untagged)] + enum RegisteredProviderVariant { + New(NewRegisteredProvider), + Old(OldRegisteredProvider), + } + + match RegisteredProviderVariant::deserialize(deserializer) { + Ok(RegisteredProviderVariant::New(new_provider)) => { + Ok(RegisteredProvider { + provider_name: new_provider.provider_name, + provider_id: new_provider.provider_id, + description: new_provider.description, + instructions: new_provider.instructions, + registered_provider_wallet: new_provider.registered_provider_wallet, + price: new_provider.price, + endpoint: new_provider.endpoint, + is_live: new_provider.is_live, + }) + }, + Ok(RegisteredProviderVariant::Old(old_provider)) => { + info!("Migrating old RegisteredProvider to new structure - setting is_live to None (legacy)"); + // Migrate old provider to new structure with is_live set to None (legacy) + Ok(RegisteredProvider { + provider_name: old_provider.provider_name, + provider_id: old_provider.provider_id, + description: old_provider.description, + instructions: old_provider.instructions, + registered_provider_wallet: old_provider.registered_provider_wallet, + price: old_provider.price, + endpoint: old_provider.endpoint, + is_live: None, // None = legacy provider, no explicit state set + }) + }, + Err(_) => { + // If both fail, return the error + Err(serde::de::Error::custom("Failed to deserialize RegisteredProvider")) + } + } + } } #[derive(Clone, Debug, Serialize, Deserialize)] diff --git a/provider/ui/src/App.tsx b/provider/ui/src/App.tsx index b42b318..c1c6de3 100644 --- a/provider/ui/src/App.tsx +++ b/provider/ui/src/App.tsx @@ -222,6 +222,7 @@ function AppContent() { setProvidersError(null); try { const providers = await fetchRegisteredProvidersApi(); + setRegisteredProviders(providers); console.log("Fetched registered providers:", providers); } catch (error) { @@ -288,6 +289,52 @@ function AppContent() { setShowForm(true); }, []); + const handleToggleProviderLive = useCallback(async (provider: RegisteredProvider) => { + if (!isWalletConnected) { + alert('Please connect your wallet to update provider status on the hypergrid.'); + return; + } + + // Determine the new live status + const newLiveStatus = provider.is_live === false ? true : false; + + try { + // Look up the actual TBA address for this provider from backend + const tbaAddress = await lookupProviderTbaAddressFromBackend(provider.provider_name, publicClient); + + if (!tbaAddress) { + alert(`No blockchain entry found for provider "${provider.provider_name}". Please register on the hypergrid first.`); + return; + } + + console.log(`Toggling provider ${provider.provider_name} to ${newLiveStatus ? 'live' : 'offline'}`); + + // Create a simple update plan for just the is_live toggle + const toggleUpdatePlan = { + needsOnChainUpdate: true, + needsOffChainUpdate: true, + onChainNotes: [{ key: '~is-live', value: newLiveStatus.toString() }], + updatedProvider: { + ...provider, + is_live: newLiveStatus + } + }; + + // Store the update plan for the callback + (window as any).pendingUpdatePlan = toggleUpdatePlan; + + // Update just the ~is-live note using the existing update flow + await providerUpdate.updateProviderNotes(tbaAddress, [ + { key: '~is-live', value: newLiveStatus.toString() } + ]); + + // The success will be handled by the providerUpdate.onUpdateComplete callback + } catch (error) { + console.error('Error toggling provider live status:', error); + alert(`Failed to update provider status: ${(error as Error).message}`); + } + }, [isWalletConnected, publicClient, providerUpdate]); + const handleProviderRegistration = useCallback(async (provider: RegisteredProvider) => { console.log("Starting registration for provider:", provider); @@ -462,9 +509,10 @@ function AppContent() {
{registeredProviders.map((provider) => ( ))}
diff --git a/provider/ui/src/components/CurlJsonViewer.tsx b/provider/ui/src/components/CurlJsonViewer.tsx index b07576b..8c0877f 100644 --- a/provider/ui/src/components/CurlJsonViewer.tsx +++ b/provider/ui/src/components/CurlJsonViewer.tsx @@ -17,10 +17,6 @@ const CurlJsonViewer: React.FC = ({ }) => { - const isFieldModifiable = useCallback((jsonPointer: string) => { - return modifiableFields.some(f => f.jsonPointer === jsonPointer); - }, [modifiableFields]); - const getFieldByPointer = useCallback((pointer: string) => { return potentialFields.find(f => f.jsonPointer === pointer); }, [potentialFields]); diff --git a/provider/ui/src/components/ModifiableFieldsList.tsx b/provider/ui/src/components/ModifiableFieldsList.tsx index 5a32ea6..9a60075 100644 --- a/provider/ui/src/components/ModifiableFieldsList.tsx +++ b/provider/ui/src/components/ModifiableFieldsList.tsx @@ -8,23 +8,6 @@ interface ModifiableFieldsListProps { onFieldNameChange: (field: ModifiableField, newName: string) => void; } -// Helper to group fields by their parent path -function groupFieldsByParent(fields: ModifiableField[]) { - const groups: Record = {}; - - fields.forEach(field => { - // Find the parent path (everything before the last segment) - const parts = field.jsonPointer.split('/'); - const parentPath = parts.slice(0, -1).join('/'); - - if (!groups[parentPath]) { - groups[parentPath] = []; - } - groups[parentPath].push(field); - }); - - return groups; -} const ModifiableFieldsList: React.FC = ({ modifiableFields, diff --git a/provider/ui/src/components/ProviderConfigModal.tsx b/provider/ui/src/components/ProviderConfigModal.tsx index 3305d6c..8a8a1c5 100644 --- a/provider/ui/src/components/ProviderConfigModal.tsx +++ b/provider/ui/src/components/ProviderConfigModal.tsx @@ -52,11 +52,6 @@ const ProviderConfigModal: React.FC = ({ onProviderUpdate, providerRegistration, providerUpdate, - publicClient, - handleProviderUpdated, - processUpdateResponse, - resetEditState, - handleCloseAddNewModal }) => { const { address: connectedWalletAddress } = useAccount(); const [showValidation, setShowValidation] = useState(false); @@ -422,7 +417,8 @@ const ProviderConfigModal: React.FC = ({ providerDescription, instructions, registeredProviderWallet, - price: parseFloat(price) || 0 + price: parseFloat(price) || 0, + isLive: isEditMode && editingProvider ? editingProvider.is_live : true }} onValidationSuccess={handleValidationSuccess} onValidationError={handleValidationError} diff --git a/provider/ui/src/components/RegisteredProviderView.tsx b/provider/ui/src/components/RegisteredProviderView.tsx index a8959c2..23c7e97 100644 --- a/provider/ui/src/components/RegisteredProviderView.tsx +++ b/provider/ui/src/components/RegisteredProviderView.tsx @@ -4,9 +4,10 @@ import { RegisteredProvider } from '../types/hypergrid_provider'; export interface RegisteredProviderViewProps { provider: RegisteredProvider; onEdit?: (provider: RegisteredProvider) => void; + onToggleLive?: (provider: RegisteredProvider) => void; } -const RegisteredProviderView: React.FC = ({ provider, onEdit }) => { +const RegisteredProviderView: React.FC = ({ provider, onEdit, onToggleLive }) => { const formatPrice = (price: number) => { if (typeof price !== 'number' || isNaN(price)) return 'N/A'; @@ -37,19 +38,25 @@ const RegisteredProviderView: React.FC = ({ provide }; const handleClick = (e: React.MouseEvent) => { - // Only trigger edit if not clicking the button itself - if ((e.target as HTMLElement).tagName !== 'BUTTON') { + // Only trigger edit if not clicking a button + const target = e.target as HTMLElement; + if (target.tagName !== 'BUTTON' && !target.closest('button')) { onEdit?.(provider); } }; + const handleToggleLive = (e: React.MouseEvent) => { + e.stopPropagation(); + onToggleLive?.(provider); + }; + return (
- {/* Top row with icon, name, and edit button */} + {/* Top row with icon, name, status, and edit button */}
🔌 @@ -75,10 +82,32 @@ const RegisteredProviderView: React.FC = ({ provide
- {/* Bottom row with price */} -
- 💰 Price: - {formatPrice(provider.price)} + {/* Bottom row with price and toggle */} +
+
+ 💰 Price: + {formatPrice(provider.price)} +
+ + {/* Toggle switch - legacy providers default to "on" */} +
+ + {provider.is_live === false ? 'Off' : 'On'} + + +
diff --git a/provider/ui/src/components/ValidationPanel.tsx b/provider/ui/src/components/ValidationPanel.tsx index 2bb34fa..415ceb8 100644 --- a/provider/ui/src/components/ValidationPanel.tsx +++ b/provider/ui/src/components/ValidationPanel.tsx @@ -9,6 +9,7 @@ interface ValidationPanelProps { instructions: string; registeredProviderWallet: string; price: number; + isLive?: boolean; // For preserving is_live state in edit mode }; onValidationSuccess: (validatedProvider: any) => void; onValidationError: (error: string) => void; @@ -108,7 +109,9 @@ const ValidationPanel: React.FC = ({ instructions: providerMetadata.instructions, registered_provider_wallet: providerMetadata.registeredProviderWallet, price: providerMetadata.price, - endpoint: curlTemplate // The curlTemplate IS the new EndpointDefinition + endpoint: curlTemplate, // The curlTemplate IS the new EndpointDefinition + // For new registrations, set is_live to true. For edits, preserve existing value + is_live: isEditMode ? providerMetadata.isLive : true }; // Send provider object and arguments for validation diff --git a/provider/ui/src/registration/hypermap.ts b/provider/ui/src/registration/hypermap.ts index 01f7152..08ad36e 100644 --- a/provider/ui/src/registration/hypermap.ts +++ b/provider/ui/src/registration/hypermap.ts @@ -12,6 +12,7 @@ export const PROVIDER_NOTE_KEYS = { DESCRIPTION: '~description', INSTRUCTIONS: '~instructions', PRICE: '~price', + IS_LIVE: '~is-live', } as const; // Default parent namehash - can be overridden when calling functions @@ -48,22 +49,6 @@ export const tbaExecuteAbi = parseAbi([ 'function execute(address to, uint256 value, bytes calldata data, uint8 operation) returns (bytes memory returnData)', ]); -/** - * Calculates the namehash for a provider name under the grid.hypr namespace - */ -export function calculateProviderNamehash(providerName: string, parentNamehash: `0x${string}` = DEFAULT_PARENT_NAMEHASH): `0x${string}` { - // Convert provider name to bytes - const labelBytes = stringToHex(providerName); - - // Calculate labelhash = keccak256(label) - const labelHash = keccak256(labelBytes); - - // Calculate namehash = keccak256(parenthash + labelhash) - const namehash = keccak256(encodePacked(['bytes32', 'bytes32'], [parentNamehash, labelHash])); - - return namehash; -} - /** * Simplified TBA lookup that gets namehash from backend: * This ensures consistency and enforces that provider exists in both systems @@ -104,39 +89,6 @@ export async function lookupProviderTbaAddressFromBackend( } } -/** - * Verifies if a provider has been registered on-chain by checking if their TBA exists - */ -export async function verifyProviderOnChain( - providerName: string, - publicClient: any -): Promise { - const tbaAddress = await lookupProviderTbaAddressFromBackend(providerName, publicClient); - return tbaAddress !== null; -} - -// Helper functions - -/** - * Generates the calldata for minting a new provider entry - */ -export function generateProviderMintCall({ - owner, - providerName, -}: { - owner: Address; - providerName: string; -}): Hex { - return encodeFunctionData({ - abi: hyperGridNamespaceMinterAbi, - functionName: 'mint', - args: [ - owner, - encodePacked(["bytes"], [stringToHex(providerName)]), - ] - }); -} - /** * Generates the calldata for setting a note on a provider entry */ @@ -157,90 +109,6 @@ export function generateNoteCall({ }); } -/** - * Prepares all note calls for provider metadata - */ -export function prepareProviderNoteCalls({ - providerId, - wallet, - description, - instructions, - price, -}: { - providerId: string; - wallet: string; - description: string; - instructions: string; - price: string; -}): Array<{ key: string; value: string; calldata: Hex }> { - return [ - { - key: PROVIDER_NOTE_KEYS.PROVIDER_ID, - value: providerId, - calldata: generateNoteCall({ noteKey: PROVIDER_NOTE_KEYS.PROVIDER_ID, noteValue: providerId }), - }, - { - key: PROVIDER_NOTE_KEYS.WALLET, - value: wallet, - calldata: generateNoteCall({ noteKey: PROVIDER_NOTE_KEYS.WALLET, noteValue: wallet }), - }, - { - key: PROVIDER_NOTE_KEYS.DESCRIPTION, - value: description, - calldata: generateNoteCall({ noteKey: PROVIDER_NOTE_KEYS.DESCRIPTION, noteValue: description }), - }, - { - key: PROVIDER_NOTE_KEYS.INSTRUCTIONS, - value: instructions, - calldata: generateNoteCall({ noteKey: PROVIDER_NOTE_KEYS.INSTRUCTIONS, noteValue: instructions }), - }, - { - key: PROVIDER_NOTE_KEYS.PRICE, - value: price, - calldata: generateNoteCall({ noteKey: PROVIDER_NOTE_KEYS.PRICE, noteValue: price }), - }, - ]; -} - -/** - * Generates a multicall for setting all provider notes in a single transaction - */ -export function generateProviderNotesMulticall({ - tbaAddress, - providerId, - wallet, - description, - instructions, - price, -}: { - tbaAddress: Address; - providerId: string; - wallet: string; - description: string; - instructions: string; - price: string; -}): Hex { - const noteCalls = [ - generateNoteCall({ noteKey: PROVIDER_NOTE_KEYS.PROVIDER_ID, noteValue: providerId }), - generateNoteCall({ noteKey: PROVIDER_NOTE_KEYS.WALLET, noteValue: wallet }), - generateNoteCall({ noteKey: PROVIDER_NOTE_KEYS.DESCRIPTION, noteValue: description }), - generateNoteCall({ noteKey: PROVIDER_NOTE_KEYS.INSTRUCTIONS, noteValue: instructions }), - generateNoteCall({ noteKey: PROVIDER_NOTE_KEYS.PRICE, noteValue: price }), - ]; - - // Each call targets HYPERMAP_ADDRESS directly (not through TBA.execute) - const calls = noteCalls.map(calldata => ({ - target: HYPERMAP_ADDRESS, - callData: calldata, - })); - - return encodeFunctionData({ - abi: multicallAbi, - functionName: 'aggregate', - args: [calls] - }); -} - /** * Generates TBA execute arguments for setting provider notes via multicall * Uses DELEGATECALL pattern from the example code @@ -252,6 +120,7 @@ export function generateProviderNotesCallsArray({ description, instructions, price, + isLive, }: { tbaAddress: Address; providerId: string; @@ -259,6 +128,7 @@ export function generateProviderNotesCallsArray({ description: string; instructions: string; price: string; + isLive?: string; // Optional - only include if explicitly set }) { // 1. Generate individual note calls const noteCalls = [ @@ -269,6 +139,11 @@ export function generateProviderNotesCallsArray({ generateNoteCall({ noteKey: PROVIDER_NOTE_KEYS.PRICE, noteValue: price }), ]; + // Only add is_live note if it's explicitly provided + if (isLive !== undefined) { + noteCalls.push(generateNoteCall({ noteKey: PROVIDER_NOTE_KEYS.IS_LIVE, noteValue: isLive })); + } + // 2. Create multicall data const calls = noteCalls.map(calldata => ({ target: HYPERMAP_ADDRESS, @@ -315,4 +190,4 @@ export function validateProviderName(name: string): { valid: boolean; error?: st } return { valid: true }; -} \ No newline at end of file +} diff --git a/provider/ui/src/registration/hypermapUtils.ts b/provider/ui/src/registration/hypermapUtils.ts index 6d50263..f873568 100644 --- a/provider/ui/src/registration/hypermapUtils.ts +++ b/provider/ui/src/registration/hypermapUtils.ts @@ -12,7 +12,6 @@ import { multicallAbi } from './hypermap'; import { RegisteredProvider } from '../types/hypergrid_provider'; -import React from 'react'; export type ProviderRegistrationStep = 'idle' | 'minting' | 'notes' | 'complete'; @@ -217,6 +216,8 @@ export function useProviderRegistration(callbacks: ProviderRegistrationCallbacks description: providerData.description, instructions: providerData.instructions, price: providerData.price.toString(), + // For new registrations through this flow, always include is_live: true + isLive: 'true', }); console.log('TBA execute args for multicall:', { @@ -676,43 +677,3 @@ export function useProviderUpdate(callbacks: ProviderUpdateCallbacks) { updateProviderNotes, }; } - -/** - * Custom hook for handling animation triggers based on registration state - */ -export function useRegistrationAnimations( - step: ProviderRegistrationStep, - mintedProviderAddress: Address | null, - onAnimationComplete?: () => void -) { - const [animationState, setAnimationState] = React.useState<{ - showSuccessAnimation: boolean; - showConfetti: boolean; - }>({ - showSuccessAnimation: false, - showConfetti: false, - }); - - React.useEffect(() => { - if (step === 'complete' && mintedProviderAddress) { - // Trigger success animation - setAnimationState({ - showSuccessAnimation: true, - showConfetti: true, - }); - - // Optional callback after animation - if (onAnimationComplete) { - const timer = setTimeout(onAnimationComplete, 3000); - return () => clearTimeout(timer); - } - } else { - setAnimationState({ - showSuccessAnimation: false, - showConfetti: false, - }); - } - }, [step, mintedProviderAddress, onAnimationComplete]); - - return animationState; -} \ No newline at end of file diff --git a/provider/ui/src/types/hypergrid_provider.ts b/provider/ui/src/types/hypergrid_provider.ts index b644c54..1a0e84b 100644 --- a/provider/ui/src/types/hypergrid_provider.ts +++ b/provider/ui/src/types/hypergrid_provider.ts @@ -66,6 +66,7 @@ export interface RegisteredProvider { registered_provider_wallet: string; // Eth address as string price: number; // Price per call endpoint: EndpointDefinition; + is_live?: boolean; // Optional: undefined = legacy, false = explicitly offline, true = explicitly online } // Request body for the register_provider endpoint @@ -78,19 +79,10 @@ export interface RegisterProviderRequest { // Response type for the register_provider endpoint export type RegisterProviderResponse = RustResponse; -// Request body for the get_registered_providers endpoint -export interface GetRegisteredProvidersRequest { - GetRegisteredProviders: null; // Key matches the Rust function name, value is null since no parameters needed -} // Response type for the get_registered_providers endpoint export type GetRegisteredProvidersResponse = RustResponse; -// Request body for the update_provider endpoint -export interface UpdateProvider { - provider_name: string; - updated_provider: RegisteredProvider; -} // Response type for the update_provider endpoint export type UpdateProviderResponse = RustResponse; @@ -100,15 +92,6 @@ export interface HypergridProviderState { registeredProviders: RegisteredProvider[]; } -// New API type for namehash lookup -export interface GetProviderNamehashResponse { - Ok?: string; - Err?: string; -} - -// Define top-level request types for forms -export type TopLevelRequestType = "getWithPath" | "getWithQuery" | "postWithJson"; -export type AuthChoice = "none" | "query" | "header"; // --- Indexed Provider Types --- // Interface for indexed providers from the operator's database diff --git a/provider/ui/src/utils/api.ts b/provider/ui/src/utils/api.ts index 2fcbfaf..f7d1675 100644 --- a/provider/ui/src/utils/api.ts +++ b/provider/ui/src/utils/api.ts @@ -5,11 +5,10 @@ import { RegisterProviderResponse, UpdateProviderResponse, IndexedProvider, - GetIndexedProvidersResponse, SearchIndexedProvidersResponse, - GetIndexedProviderDetailsResponse, ProviderSyncStatus, GetProviderSyncStatusResponse, + } from '../types/hypergrid_provider'; const BASE_URL = import.meta.env.BASE_URL; @@ -285,33 +284,7 @@ export const updateProviderApi = async (providerName: string, updatedProvider: R } }; -// --- Indexed Provider API Functions --- -export const fetchAllIndexedProvidersApi = async (): Promise => { - const requestData = { GetIndexedProviders: null }; - try { - const result = await fetch(`${BASE_URL}/api`, { - method: "POST", - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(requestData), - }); - if (!result.ok) { - const errorText = await result.text(); - console.error(`HTTP request failed: ${result.status} ${result.statusText}. Response:`, errorText); - throw new Error(`Failed to fetch indexed providers: ${result.statusText} - ${errorText}`); - } - const responseData = await result.json() as GetIndexedProvidersResponse; - if (responseData.Ok) { - // Parse the JSON string response - return JSON.parse(responseData.Ok) as IndexedProvider[]; - } else { - throw new Error(responseData.Err || "Unknown error fetching indexed providers"); - } - } catch (error) { - console.error("Failed to fetch indexed providers:", error); - throw error; - } -}; export const searchIndexedProvidersApi = async (query: string): Promise => { const requestData = { SearchIndexedProviders: query }; @@ -339,32 +312,6 @@ export const searchIndexedProvidersApi = async (query: string): Promise => { - const requestData = { GetIndexedProviderDetails: name }; - try { - const result = await fetch(`${BASE_URL}/api`, { - method: "POST", - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(requestData), - }); - if (!result.ok) { - const errorText = await result.text(); - console.error(`HTTP request failed: ${result.status} ${result.statusText}. Response:`, errorText); - throw new Error(`Failed to get indexed provider details: ${result.statusText} - ${errorText}`); - } - const responseData = await result.json() as GetIndexedProviderDetailsResponse; - if (responseData.Ok) { - // Parse the JSON string response (could be null if provider not found) - return JSON.parse(responseData.Ok) as IndexedProvider | null; - } else { - throw new Error(responseData.Err || "Unknown error getting indexed provider details"); - } - } catch (error) { - console.error("Failed to get indexed provider details:", error); - throw error; - } -}; - export const getProviderSyncStatusApi = async (): Promise => { const requestData = { GetProviderSyncStatus: null }; try { diff --git a/provider/ui/src/utils/enhancedCurlParser.ts b/provider/ui/src/utils/enhancedCurlParser.ts index ded5894..e63170b 100644 --- a/provider/ui/src/utils/enhancedCurlParser.ts +++ b/provider/ui/src/utils/enhancedCurlParser.ts @@ -37,32 +37,6 @@ export function redactApiKey(value: string): string { return `${value.slice(0, 4)}...`; } -/** - * Helper function to describe the structure of a value for documentation - */ -function describeStructure(value: any): any { - if (value === null || value === undefined) { - return 'null'; - } - - if (Array.isArray(value)) { - if (value.length === 0) { - return '[]'; - } - return [`array of ${describeStructure(value[0])}`]; - } - - if (typeof value === 'object') { - const structure: Record = {}; - for (const [key, val] of Object.entries(value)) { - structure[key] = describeStructure(val); - } - return structure; - } - - return typeof value; -} - export interface ParsedCurlRequest { @@ -371,52 +345,6 @@ export function createCurlTemplate( }; } -/** - * Applies values to a curl template to create an executable request - */ -export function applyCurlTemplateValues( - template: CurlTemplate, - values: Record -): ParsedCurlRequest { - // Deep clone the parsed request - const request = JSON.parse(JSON.stringify(template.parsedRequest)); - - // Apply each modifiable field value - template.modifiableFields.forEach(field => { - if (values.hasOwnProperty(field.name)) { - try { - // For body fields, we need to ensure the body object exists - if (field.fieldType === 'body' && field.jsonPointer.startsWith('/body')) { - if (!request.body) { - request.body = {}; - } - } - - jsonpointer.set(request, field.jsonPointer, values[field.name]); - } catch (error) { - console.error(`Failed to set value for ${field.jsonPointer}:`, error); - } - } - }); - - // Rebuild the URL if path or query parameters were modified - const urlObj = new URL(request.baseUrl); - - // Update path if segments were modified - if (request.pathSegments.length > 0) { - urlObj.pathname = '/' + request.pathSegments.join('/'); - } - - // Update query parameters - urlObj.search = ''; - Object.entries(request.queryParams).forEach(([key, value]) => { - urlObj.searchParams.set(key, String(value)); - }); - - request.url = urlObj.toString(); - - return request; -} /** * Converts a curl template to the format expected by the backend diff --git a/provider/ui/src/utils/providerFormUtils.ts b/provider/ui/src/utils/providerFormUtils.ts index 4c085d4..5a0fff1 100644 --- a/provider/ui/src/utils/providerFormUtils.ts +++ b/provider/ui/src/utils/providerFormUtils.ts @@ -15,6 +15,7 @@ export const ON_CHAIN_NOTE_KEYS = { DESCRIPTION: '~description', INSTRUCTIONS: '~instructions', PRICE: '~price', + IS_LIVE: '~is-live', } as const; /** @@ -52,6 +53,11 @@ export function createSmartUpdatePlan( originalValue: originalProvider.price?.toString(), updatedValue: updatedProvider.price?.toString(), }, + { + key: ON_CHAIN_NOTE_KEYS.IS_LIVE, + originalValue: originalProvider.is_live?.toString(), + updatedValue: updatedProvider.is_live?.toString(), + }, ]; // Check each onchain field for changes