From ca566d005211f26730e877a5cedf13cbcffb7830 Mon Sep 17 00:00:00 2001 From: 0xstt Date: Fri, 31 Oct 2025 14:19:18 +0300 Subject: [PATCH] add flow introduction page --- app/console/icm/setup/page.tsx | 41 ++- app/console/icm/setup/steps.ts | 31 ++- app/console/ictt/setup/page.tsx | 41 ++- app/console/ictt/setup/steps.ts | 57 +++- components/console/flow-introduction.tsx | 335 +++++++++++++++++++++++ components/console/step-flow.tsx | 23 ++ components/toolbox/utils/github-url.ts | 49 +++- 7 files changed, 563 insertions(+), 14 deletions(-) create mode 100644 components/console/flow-introduction.tsx diff --git a/app/console/icm/setup/page.tsx b/app/console/icm/setup/page.tsx index e7b15eacf8a..5af8014b66a 100644 --- a/app/console/icm/setup/page.tsx +++ b/app/console/icm/setup/page.tsx @@ -1,7 +1,42 @@ -import { redirect } from "next/navigation"; +import FlowIntroduction from "@/components/console/flow-introduction"; +import { type FlowConfig } from "@/components/console/step-flow"; +import { steps } from "./steps"; export default function Page() { - redirect("/console/icm/setup/icm-messenger"); -} + const config: FlowConfig = { + title: "ICM Setup", + description: "Learn how to set up Interchain Messaging (ICM) on your L1, enabling cross-chain communication and applications like Interchain Token Transfers.", + metadata: { + prerequisites: [ + { requirement: "Access to your L1 RPC endpoint", type: "infrastructure" }, + { requirement: "Deployed L1 blockchain network", type: "infrastructure" }, + { requirement: "Wallet connected to your L1 network", type: "wallet" }, + { requirement: "Sufficient balance for contract deployments", type: "wallet" }, + { requirement: "Basic understanding of smart contracts", type: "knowledge" }, + { requirement: "Familiarity with cross-chain messaging concepts", type: "knowledge" }, + ], + estimatedTime: "15-30 min", + useCases: [ + "Enable cross-chain token transfers between your L1 and other chains", + "Build multi-chain dApps that communicate across networks", + "Create cross-chain NFT marketplaces or gaming ecosystems", + "Implement cross-chain governance or DAO voting", + "Bridge assets between your L1 and C-Chain or other L1s", + ], + }, + resources: [ + { label: "Interchain Messaging", href: "/academy/interchain-messaging" }, + { label: "ICM & Chainlink", href: "/academy/icm-chainlink" }, + { label: "Token Transfer", href: "/academy/interchain-token-transfer" }, + ], + }; + return ( + + ); +} diff --git a/app/console/icm/setup/steps.ts b/app/console/icm/setup/steps.ts index da7b8a2b8ff..2bad1e21617 100644 --- a/app/console/icm/setup/steps.ts +++ b/app/console/icm/setup/steps.ts @@ -10,20 +10,47 @@ export const steps: StepDefinition[] = [ key: "icm-messenger", title: "Deploy Teleporter Messenger", component: TeleporterMessenger, + description: "Deploy the core Teleporter Messenger contract that handles cross-chain message passing with built-in security guarantees. This contract manages message verification, execution, and receipt handling between chains.", + outcomes: [ + "Teleporter Messenger contract deployed on your L1", + "Foundation for cross-chain communication established", + ], }, { type: "single", key: "icm-registry", title: "Deploy Teleporter Registry", component: TeleporterRegistry, + description: "Deploy the Teleporter Registry contract that tracks different versions of the Teleporter Messenger and enables seamless upgrades. This ensures your cross-chain communication can evolve without disrupting existing integrations.", + outcomes: [ + "Registry contract deployed with version tracking capabilities", + "Upgrade path configured for future Teleporter versions", + ], }, { type: "branch", key: "icm-relayer-type", title: "Setup ICM Relayer", + description: "Configure a relayer to automatically deliver cross-chain messages. Choose between running your own infrastructure for full control, or using managed services for simplified deployment and maintenance.", options: [ - { key: "self-hosted-relayer", label: "Setup Self Hosted ICM Relayer", component: ICMRelayer }, - { key: "managed-testnet-relayer", label: "Managed Testnet Relayer", component: CreateManagedTestnetRelayer }, + { + key: "self-hosted-relayer", + label: "Setup Self Hosted ICM Relayer", + component: ICMRelayer, + outcomes: [ + "Full control over relayer infrastructure and configuration", + "Ability to customize message delivery and monitoring", + ], + }, + { + key: "managed-testnet-relayer", + label: "Managed Testnet Relayer", + component: CreateManagedTestnetRelayer, + outcomes: [ + "Quick setup with zero infrastructure management", + "Automated message delivery for testnet development", + ], + }, ], }, ]; diff --git a/app/console/ictt/setup/page.tsx b/app/console/ictt/setup/page.tsx index a6bfb9fe9a7..fac414e5d2f 100644 --- a/app/console/ictt/setup/page.tsx +++ b/app/console/ictt/setup/page.tsx @@ -1,7 +1,40 @@ -import { redirect } from "next/navigation"; +import FlowIntroduction from "@/components/console/flow-introduction"; +import { type FlowConfig } from "@/components/console/step-flow"; +import { steps } from "./steps"; export default function Page() { - redirect("/console/ictt/setup/deploy-test-erc20"); -} - + const config: FlowConfig = { + title: "ICTT Setup", + description: "Set up Interchain Token Transfer (ICTT) to enable seamless token bridging between your L1 and other chains in the Avalanche ecosystem.", + metadata: { + prerequisites: [ + { requirement: "ICM (Interchain Messaging) already set up on your L1", type: "infrastructure" }, + { requirement: "RPC endpoints for home and remote chains", type: "infrastructure" }, + { requirement: "Wallet with sufficient balance on both chains", type: "wallet" }, + { requirement: "Source token contract address (or deploy new one)", type: "wallet" }, + { requirement: "Understanding of token standards (ERC20)", type: "knowledge" }, + { requirement: "Familiarity with cross-chain token bridging concepts", type: "knowledge" }, + ], + estimatedTime: "20-40 min", + useCases: [ + "Bridge existing ERC20 tokens between your L1 and other chains", + "Create wrapped versions of your native token on other networks", + "Enable liquidity sharing across multiple chains", + "Build cross-chain DeFi protocols with unified token access", + "Support multi-chain gaming with in-game currency transfers", + ], + }, + resources: [ + { label: "Interchain Token Transfer", href: "/academy/interchain-token-transfer" }, + { label: "Interchain Messaging", href: "/academy/interchain-messaging" }, + ], + }; + return ( + + ); +} diff --git a/app/console/ictt/setup/steps.ts b/app/console/ictt/setup/steps.ts index d4c1e73b363..c5f5ce0fd3c 100644 --- a/app/console/ictt/setup/steps.ts +++ b/app/console/ictt/setup/steps.ts @@ -12,10 +12,27 @@ export const steps: StepDefinition[] = [ type: "branch", key: "deploy-source-token", title: "Deploy Source Token", + description: "Deploy or select the token that will be bridged across chains. You can deploy a test ERC20 token for development purposes, or wrap your L1's native token to enable native asset transfers. If you already have a token deployed, you can skip this step.", optional: true, options: [ - { key: "deploy-test-erc20", label: "Deploy Test ERC20", component: DeployExampleERC20 }, - { key: "deploy-wrapped-native", label: "Deploy Wrapped Native Token", component: DeployWrappedNative }, + { + key: "deploy-test-erc20", + label: "Deploy Test ERC20", + component: DeployExampleERC20, + outcomes: [ + "Standard ERC20 token deployed for testing purposes", + "Full control over token supply, name, and symbol", + ], + }, + { + key: "deploy-wrapped-native", + label: "Deploy Wrapped Native Token", + component: DeployWrappedNative, + outcomes: [ + "Wrapped version of your L1's native token created", + "Support for bridging native assets across chains", + ], + }, ], }, { @@ -23,14 +40,36 @@ export const steps: StepDefinition[] = [ key: "deploy-token-home", title: "Deploy Token Home", component: DeployTokenHome, + description: "Deploy the TokenHome contract on your source chain. This contract acts as the central hub for your token bridge, managing token locking (for native transfers), burning (for multi-mint), and minting operations. It coordinates with remote contracts to ensure secure cross-chain transfers.", + outcomes: [ + "TokenHome contract deployed and linked to your token", + "Token bridge infrastructure configured on home chain", + ], }, { type: "branch", key: "deploy-remote", title: "Deploy Remote", + description: "Deploy the token representation on the destination chain. Choose ERC20 Remote for standard token transfers with custom metadata support, or Native Remote if you want the token to be the native gas token on the destination chain with lower transaction costs.", options: [ - { key: "erc20-remote", label: "Deploy ERC20 Token Remote", component: DeployERC20TokenRemote }, - { key: "native-remote", label: "Deploy Native Token Remote", component: DeployNativeTokenRemote }, + { + key: "erc20-remote", + label: "Deploy ERC20 Token Remote", + component: DeployERC20TokenRemote, + outcomes: [ + "ERC20 token representation deployed on remote chain", + "Supports custom token decimals, names, and metadata", + ], + }, + { + key: "native-remote", + label: "Deploy Native Token Remote", + component: DeployNativeTokenRemote, + outcomes: [ + "Native token representation on remote chain", + "Lower gas costs and native-like user experience", + ], + }, ], }, { @@ -38,11 +77,21 @@ export const steps: StepDefinition[] = [ key: "register-with-home", title: "Register With Home", component: RegisterWithHome, + description: "Register the remote token contract with the TokenHome contract to establish the bidirectional connection. This step configures the routing information so that cross-chain messages can flow between your home and remote contracts securely.", + outcomes: [ + "Remote token registered and verified with TokenHome", + "Cross-chain message routing and validation configured", + ], }, { type: "single", key: "add-collateral", title: "Add Collateral", component: AddCollateral, + description: "Deposit initial collateral on the remote chain to enable bidirectional token transfers. This collateral allows users to transfer tokens back from the remote chain to the home chain. The amount of collateral determines how much value can flow in the reverse direction.", + outcomes: [ + "Initial collateral deposited and locked on remote chain", + "Bidirectional token transfers fully enabled and operational", + ], }, ]; diff --git a/components/console/flow-introduction.tsx b/components/console/flow-introduction.tsx new file mode 100644 index 00000000000..3c5448822fc --- /dev/null +++ b/components/console/flow-introduction.tsx @@ -0,0 +1,335 @@ +"use client"; + +import React from "react"; +import Link from "next/link"; +import { ArrowRight, BookOpen, CheckCircle2, GitBranch, Clock, Lightbulb } from "lucide-react"; +import type { StepDefinition, FlowConfig } from "./step-flow"; +import { EditOnGitHubButton } from "@/components/console/edit-on-github-button"; +import { ReportIssueButton } from "@/components/console/report-issue-button"; +import { generateFlowSetupGitHubUrl, generateFlowBasePath } from "@/components/toolbox/utils/github-url"; + +type FlowIntroductionProps = { + config: FlowConfig; + steps: StepDefinition[]; + url: string; +}; + +export default function FlowIntroduction({ + config, + steps, + url, +}: FlowIntroductionProps) { + // Auto-generate basePath and githubUrl from import.meta.url + const basePath = generateFlowBasePath(url); + const githubUrl = generateFlowSetupGitHubUrl(url); + + // Calculate first step key from steps + const firstStepKey = steps[0].type === "single" + ? steps[0].key + : steps[0].options[0].key; + return ( +
+ {/* Content with Right Pane */} +
+ {/* SVG Pattern Background - covers header and right pane */} +
+
+ +
+
+ +
+ {/* Main Content */} +
+ {/* Header */} +
+

{config.title}

+ {config.description && ( +

{config.description}

+ )} + + {/* Minimal Learning Resources */} + {config.resources && config.resources.length > 0 && ( +
+ Learning + {config.resources.map((resource, idx) => ( + + + {resource.label} + + ))} +
+ )} +
+ + {/* Timeline View */} + +
+ + {/* Right Pane - Prerequisites Sidebar */} + {config.metadata && (config.metadata.prerequisites || config.metadata.estimatedTime) && ( +
+
+ {config.metadata.prerequisites && config.metadata.prerequisites.length > 0 && ( +
+
+

Prerequisites

+ +
+ {(() => { + // Group prerequisites by type + const grouped = config.metadata.prerequisites.reduce((acc, prereq) => { + const type = prereq.type || "other"; + if (!acc[type]) acc[type] = []; + acc[type].push(prereq); + return acc; + }, {} as Record); + + // Define type labels + const typeLabels: Record = { + infrastructure: "Infrastructure", + wallet: "Wallet", + knowledge: "Knowledge", + }; + + return Object.entries(grouped).map(([type, items], groupIdx) => ( +
+ {groupIdx > 0 && ( +
+
+
+ )} +
+

+ {typeLabels[type] || type.charAt(0).toUpperCase() + type.slice(1)} +

+
    + {items.map((prereq, idx) => ( +
  • + + {prereq.requirement} +
  • + ))} +
+
+
+ )); + })()} +
+ + {config.metadata.estimatedTime && ( + <> +
+
+
+
+ +

+ {config.metadata.estimatedTime} + to complete +

+
+ + )} +
+
+ )} + + {/* Common Use Cases */} + {config.metadata.useCases && config.metadata.useCases.length > 0 && ( +
+
+

Common Use Cases

+
    + {config.metadata.useCases.map((useCase, idx) => ( +
  • + + {useCase} +
  • + ))} +
+
+
+ )} + + {/* Action Buttons */} + + +
+
+ )} +
+ + {/* Floating CTA Button - Centered in Component */} + + Get Started + + + +
+
+ ); +} + +// Timeline View Component +function TimelineVariant({ steps }: { steps: StepDefinition[] }) { + return ( +
+

Understanding the Flow

+ +
+ {/* Enhanced Timeline line */} +
+ +
+ {steps.map((step, stepIdx) => { + if (step.type === "single") { + // Calculate the step number (counting both single and branch steps) + const stepNumber = stepIdx + 1; + + return ( +
+ {/* Enhanced Timeline dot with improved step number */} +
+
+ + {String(stepNumber).padStart(2, "0")} + +
+ {/* Connecting line to content */} +
+
+ + {/* Enhanced Content */} +
+
+

{step.title}

+ {step.description && ( +

{step.description}

+ )} + {step.outcomes && step.outcomes.length > 0 && ( +
+

What You'll Achieve

+
+ {step.outcomes.slice(0, 2).map((outcome, idx) => ( +
+ + {outcome} +
+ ))} +
+
+ )} +
+
+
+ ); + } else { + // Branch step + const stepNumber = stepIdx + 1; + + return ( +
+
+ {/* Branch Timeline dot with improved styling */} +
+
+ +
+ {/* Connecting line to content */} +
+
+ +
+
+

{step.title}

+ {step.description && ( +

{step.description}

+ )} +
+ + {/* Branch Options */} +
+ {step.options.map((option, optionIdx) => { + const subLetter = String.fromCharCode(97 + optionIdx); + const numberLabel = `${String(stepNumber).padStart(2, "0")}.${subLetter}`; + + return ( +
+
+
+ + {numberLabel} + +
+
+

{option.label}

+ {option.outcomes && option.outcomes.length > 0 && ( +
+ {option.outcomes.slice(0, 2).map((outcome, idx) => ( +
+ + {outcome} +
+ ))} +
+ )} +
+ ); + })} +
+
+
+
+ ); + } + })} +
+
+
+ ); +} diff --git a/components/console/step-flow.tsx b/components/console/step-flow.tsx index e5549ddfdf9..b6f5158534c 100644 --- a/components/console/step-flow.tsx +++ b/components/console/step-flow.tsx @@ -9,12 +9,15 @@ type SingleStep = { title: string; optional?: boolean; component: React.ComponentType; + description?: string; + outcomes?: string[]; }; type BranchOption = { key: string; label: string; component: React.ComponentType; + outcomes?: string[]; }; type BranchStep = { @@ -23,10 +26,30 @@ type BranchStep = { title: string; optional?: boolean; options: BranchOption[]; + description?: string; }; export type StepDefinition = SingleStep | BranchStep; +export type PrerequisiteItem = { + requirement: string; + type?: string; +}; + +export type FlowMetadata = { + prerequisites?: PrerequisiteItem[]; + estimatedTime?: string; + useCases?: string[]; +}; + +export type FlowConfig = { + title: string; + description?: string; + metadata?: FlowMetadata; + resources?: Array<{ label: string; href: string }>; + githubUrl?: string; +}; + type StepFlowProps = { steps: StepDefinition[]; className?: string; diff --git a/components/toolbox/utils/github-url.ts b/components/toolbox/utils/github-url.ts index a5eac93c10d..ace7e9ec5ed 100644 --- a/components/toolbox/utils/github-url.ts +++ b/components/toolbox/utils/github-url.ts @@ -2,7 +2,7 @@ * Generates a GitHub edit URL for console tools using import.meta.url * Automatically extracts the file path from import.meta.url and converts it to a GitHub edit URL * @example - * // In any console tool file: + * In any console tool file: * const metadata: ConsoleToolMetadata = { * ..., // other metadata * githubUrl: generateConsoleToolGitHubUrl(import.meta.url) @@ -19,3 +19,50 @@ export function generateConsoleToolGitHubUrl(importMetaUrl: string): string { return ''; } } + +/** + * Generates a GitHub URL for flow setup pages using import.meta.url + * Automatically extracts the file path from import.meta.url and converts it to a GitHub URL + * @example + * In any flow setup page.tsx file: + * const config: FlowConfig = { + * ..., // other config + * githubUrl: generateFlowSetupGitHubUrl(import.meta.url) + * }; + */ +export function generateFlowSetupGitHubUrl(importMetaUrl: string): string { + try { + const url = new URL(importMetaUrl); + const parts = url.pathname.split('app/console/'); + if (parts.length !== 2) { return ''; } + + return `https://github.com/ava-labs/builders-hub/blob/main/app/console/${parts[1]}`; + } catch { + return ''; + } +} + +/** + * Extracts the console base path from import.meta.url + * Automatically generates the basePath for flow setup pages + * @example + * In any flow setup page.tsx file: + * const basePath = generateFlowBasePath(import.meta.url); + * // Returns: "/console/icm/setup" or "/console/ictt/setup" + */ +export function generateFlowBasePath(importMetaUrl: string): string { + try { + const url = new URL(importMetaUrl); + const parts = url.pathname.split('app/'); + if (parts.length !== 2) { return ''; } + + // Remove the filename (page.tsx) and get just the directory path + const pathWithFile = parts[1]; + const pathParts = pathWithFile.split('/'); + pathParts.pop(); // Remove page.tsx + + return '/' + pathParts.join('/'); + } catch { + return ''; + } +}