From 1a6b8d5f913c451c5b37a5e8259abda51c0978ba Mon Sep 17 00:00:00 2001 From: aelmanaa Date: Sun, 8 Dec 2024 14:42:16 +0100 Subject: [PATCH] update --- .../ContractAddress.tsx | 77 +++++-------- .../DeployTokenStep.module.css | 30 +++++ .../DeployTokenStep.tsx | 106 ++++++++++++++++++ .../TutorialSetup/NetworkCheck.module.css | 19 ++++ .../CCIP/TutorialSetup/NetworkCheck.tsx | 17 +++ .../CCIP/TutorialSetup/PrerequisitesCard.tsx | 100 ++++++++--------- .../TutorialSetup/SolidityParam.module.css | 92 +++++++++++++++ .../CCIP/TutorialSetup/SolidityParam.tsx | 20 ++++ .../TutorialSetup/TutorialCard.module.css | 27 +++++ .../CCIP/TutorialSetup/TutorialCard.tsx | 17 +++ .../TutorialSetup/TutorialStep.module.css | 31 +++++ .../CCIP/TutorialSetup/TutorialStep.tsx | 15 +++ .../register-from-eoa-remix.mdx | 22 +--- 13 files changed, 456 insertions(+), 117 deletions(-) create mode 100644 src/components/CCIP/TutorialBlockchainSelector/DeployTokenStep.module.css create mode 100644 src/components/CCIP/TutorialBlockchainSelector/DeployTokenStep.tsx create mode 100644 src/components/CCIP/TutorialSetup/NetworkCheck.module.css create mode 100644 src/components/CCIP/TutorialSetup/NetworkCheck.tsx create mode 100644 src/components/CCIP/TutorialSetup/SolidityParam.module.css create mode 100644 src/components/CCIP/TutorialSetup/SolidityParam.tsx create mode 100644 src/components/CCIP/TutorialSetup/TutorialCard.module.css create mode 100644 src/components/CCIP/TutorialSetup/TutorialCard.tsx create mode 100644 src/components/CCIP/TutorialSetup/TutorialStep.module.css create mode 100644 src/components/CCIP/TutorialSetup/TutorialStep.tsx diff --git a/src/components/CCIP/TutorialBlockchainSelector/ContractAddress.tsx b/src/components/CCIP/TutorialBlockchainSelector/ContractAddress.tsx index 3fff5b574b3..cee793e9013 100644 --- a/src/components/CCIP/TutorialBlockchainSelector/ContractAddress.tsx +++ b/src/components/CCIP/TutorialBlockchainSelector/ContractAddress.tsx @@ -1,75 +1,58 @@ +import { useState, useCallback } from "react" +import { utils } from "ethers" import { useStore } from "@nanostores/react" import { laneStore, setSourceContract, setDestinationContract } from "@stores/lanes" import type { DeployedContracts } from "@stores/lanes" -import { utils } from "ethers" import "./ContractAddress.css" -import { useState, useCallback, useEffect, useMemo } from "react" interface ContractAddressProps { type: keyof DeployedContracts chain: "source" | "destination" - placeholder?: string + placeholder: string } export const ContractAddress = ({ type, chain, placeholder }: ContractAddressProps) => { - const state = useStore(laneStore) - const contracts = useMemo( - () => (chain === "source" ? state.sourceContracts : state.destinationContracts), - [chain, state] - ) - const setValue = useMemo(() => (chain === "source" ? setSourceContract : setDestinationContract), [chain]) - - const [inputValue, setInputValue] = useState(contracts[type]?.toString() || "") - const [isDirty, setIsDirty] = useState(false) - const [isUpdating, setIsUpdating] = useState(false) - - useEffect(() => { - const newValue = contracts[type]?.toString() || "" - if (newValue !== inputValue) { - setInputValue(newValue) - } - }, [contracts, type, inputValue]) + const [inputValue, setInputValue] = useState("") + const [isValid, setIsValid] = useState(true) + const setValue = chain === "source" ? setSourceContract : setDestinationContract const handleChange = useCallback( - async (e: React.ChangeEvent) => { - const address = e.target.value - if (isUpdating || inputValue === address) return + (e: React.ChangeEvent) => { + const value = e.target.value.trim() + setInputValue(value) + + if (value === "") { + // Reset validation and clear store when empty + setIsValid(true) + setValue(type, "") // Clear the store value + return + } - setInputValue(address) - setIsDirty(true) + // Validate non-empty values + const valid = utils.isAddress(value) + setIsValid(valid) - if (address === "" || utils.isAddress(address)) { - setIsUpdating(true) - try { - await new Promise((resolve) => setTimeout(resolve, 0)) - setValue(type, address) - } finally { - setIsUpdating(false) - } + // Only update store if it's a valid address + if (valid) { + setValue(type, value) + } else { + // Clear store if value becomes invalid + setValue(type, "") } }, - [inputValue, setValue, type, isUpdating] + [type, setValue] ) - const isValidAddress = useCallback((address: string) => { - return address === "" || utils.isAddress(address) - }, []) - - const showError = isDirty && inputValue !== "" && !isValidAddress(inputValue) - return (
setIsDirty(true)} - placeholder={placeholder || `Enter ${type} address`} - className={`contract-address-input ${showError ? "invalid-address" : ""}`} + placeholder={placeholder} + className={`contract-address-input ${!isValid ? "invalid-address" : ""}`} /> - {showError &&
⚠️ Please enter a valid Ethereum address
} + {!isValid && Please enter a valid Ethereum address}
) } diff --git a/src/components/CCIP/TutorialBlockchainSelector/DeployTokenStep.module.css b/src/components/CCIP/TutorialBlockchainSelector/DeployTokenStep.module.css new file mode 100644 index 00000000000..a55b0866760 --- /dev/null +++ b/src/components/CCIP/TutorialBlockchainSelector/DeployTokenStep.module.css @@ -0,0 +1,30 @@ +.parameters { + background: var(--color-background-secondary); + border: 1px solid var(--color-border); + border-radius: var(--border-radius); + padding: var(--space-4x); + display: flex; + flex-direction: column; + gap: var(--space-3x); +} + +.parametersIntro { + margin-bottom: var(--space-3x); +} + +.parameterHelp { + margin-top: var(--space-4x); + padding-top: var(--space-3x); + border-top: 1px solid var(--color-border); +} + +.helpItem { + display: flex; + gap: var(--space-2x); + margin-bottom: var(--space-2x); +} + +.helpLabel { + font-weight: 600; + color: var(--color-text-primary); +} diff --git a/src/components/CCIP/TutorialBlockchainSelector/DeployTokenStep.tsx b/src/components/CCIP/TutorialBlockchainSelector/DeployTokenStep.tsx new file mode 100644 index 00000000000..49434d2d15c --- /dev/null +++ b/src/components/CCIP/TutorialBlockchainSelector/DeployTokenStep.tsx @@ -0,0 +1,106 @@ +import { useStore } from "@nanostores/react" +import { laneStore } from "@stores/lanes" +import { ContractAddress } from "./ContractAddress" +import { TutorialCard } from "../TutorialSetup/TutorialCard" +import { TutorialStep } from "../TutorialSetup/TutorialStep" +import { NetworkCheck } from "../TutorialSetup/NetworkCheck" +import { SolidityParam } from "../TutorialSetup/SolidityParam" +import styles from "./DeployTokenStep.module.css" + +interface DeployTokenStepProps { + chain: "source" | "destination" + isEnabled: boolean +} + +export const DeployTokenStep = ({ chain }: DeployTokenStepProps) => { + const state = useStore(laneStore) + const network = chain === "source" ? state.sourceNetwork : state.destinationNetwork + const networkInfo = network + ? { + name: network.name, + logo: network.logo, + } + : { name: "loading..." } + + const content = ( + <> + + +
    + +
      +
    • Open the "Deploy & Run Transactions" tab
    • +
    • Set Environment to "Injected Provider - MetaMask"
    • +
    • + Select BurnMintERC677 contract +
    • +
    +
    + + +
    +

    Configure your token by setting these required parameters in Remix:

    +
    + +
    + + + + +
    + +
    +
    + Parameter Name: + The exact name you'll type in Remix +
    +
    + Type: + The Solidity data type required +
    +
    + Example: + Click to copy a valid value +
    +
    +
    + + +
      +
    • Click "Deploy" and confirm in MetaMask
    • +
    • Copy your token address from "Deployed Contracts"
    • +
    +
    + +
    +
    +
+ + ) + + return ( + + {content} + + ) +} diff --git a/src/components/CCIP/TutorialSetup/NetworkCheck.module.css b/src/components/CCIP/TutorialSetup/NetworkCheck.module.css new file mode 100644 index 00000000000..7d3ea66cfcb --- /dev/null +++ b/src/components/CCIP/TutorialSetup/NetworkCheck.module.css @@ -0,0 +1,19 @@ +.networkCheck { + display: flex; + align-items: center; + gap: var(--space-2x); + padding: var(--space-4x); + margin-bottom: var(--space-4x); + background: var(--color-background-secondary); + border: 1px solid var(--color-border); + border-radius: var(--border-radius); +} + +.networkCheck img { + width: 24px; + height: 24px; +} + +.networkCheck strong { + color: var(--color-text-primary); +} diff --git a/src/components/CCIP/TutorialSetup/NetworkCheck.tsx b/src/components/CCIP/TutorialSetup/NetworkCheck.tsx new file mode 100644 index 00000000000..45eddcdd7da --- /dev/null +++ b/src/components/CCIP/TutorialSetup/NetworkCheck.tsx @@ -0,0 +1,17 @@ +import styles from "./NetworkCheck.module.css" + +interface NetworkCheckProps { + network: { + name: string + logo?: string + } +} + +export const NetworkCheck = ({ network }: NetworkCheckProps) => ( +
+ {network?.logo && {network.name}} + + Ensure MetaMask is connected to {network?.name || "loading..."} + +
+) diff --git a/src/components/CCIP/TutorialSetup/PrerequisitesCard.tsx b/src/components/CCIP/TutorialSetup/PrerequisitesCard.tsx index 439b1aa106f..08ece563fac 100644 --- a/src/components/CCIP/TutorialSetup/PrerequisitesCard.tsx +++ b/src/components/CCIP/TutorialSetup/PrerequisitesCard.tsx @@ -74,61 +74,59 @@ export const PrerequisitesCard = () => { return (
-
-

Prerequisites

-
- Required Setup -
-
-
- -
- {prerequisites.map((step) => ( -
-
-
- {step.title} -

{step.description}

-
-
- - -
-
+
Prerequisites
+
+
+
Wallet Setup
+
+ {prerequisites.map((step) => ( +
+
+
+ {step.title} +

{step.description}

+
+
+ + +
+
- {activeStep === step.id && step.options && ( -
- {step.options.map((option, idx) => ( -
-

{option.title}

-
    - {option.steps.map((step, stepIdx) => ( -
  • {step}
  • - ))} -
- {option.link && ( - - {option.link.text} - - - )} + {activeStep === step.id && step.options && ( +
+ {step.options.map((option, idx) => ( +
+

{option.title}

+
    + {option.steps.map((step, stepIdx) => ( +
  • {step}
  • + ))} +
+ {option.link && ( + + {option.link.text} + + + )} +
+ ))}
- ))} + )}
- )} + ))}
- ))} +
) diff --git a/src/components/CCIP/TutorialSetup/SolidityParam.module.css b/src/components/CCIP/TutorialSetup/SolidityParam.module.css new file mode 100644 index 00000000000..a1908cf9c41 --- /dev/null +++ b/src/components/CCIP/TutorialSetup/SolidityParam.module.css @@ -0,0 +1,92 @@ +.parameter { + display: grid; + gap: var(--space-3x); + align-items: start; + + /* Default mobile-first layout: stacked */ + grid-template-columns: 1fr; + + /* Tablet (768px and up) */ + @media (min-width: 768px) { + grid-template-columns: 120px 1fr; + grid-template-areas: + "name info" + "type info"; + } + + /* Desktop (1024px and up) */ + @media (min-width: 1024px) { + grid-template-columns: 120px 80px 1fr; + grid-template-areas: "name type info"; + } +} + +.name { + font-family: var(--font-mono); + background: var(--color-background); + padding: var(--space-1x) var(--space-2x); + border-radius: 4px; + grid-area: name; +} + +.type { + font-family: var(--font-mono); + color: var(--color-text-secondary); + font-size: var(--font-size-sm); + grid-area: type; + + /* On mobile/tablet, align with name */ + @media (max-width: 1023px) { + margin-top: calc(-1 * var(--space-2x)); + } +} + +.info { + grid-area: info; + display: flex; + flex-direction: column; + gap: var(--space-1x); +} + +/* Parent container adjustments */ +.parameters { + padding: var(--space-2x); + + @media (min-width: 768px) { + padding: var(--space-3x); + } + + @media (min-width: 1024px) { + padding: var(--space-4x); + } +} + +.parameterHelp { + /* Stack help items on mobile */ + @media (max-width: 767px) { + .helpItem { + flex-direction: column; + gap: var(--space-1x); + } + } +} + +/* Ensure text remains readable */ +.description { + color: var(--color-text); + line-height: var(--line-height-base); + font-size: var(--font-size-sm); + + @media (min-width: 768px) { + font-size: var(--font-size-base); + } +} + +/* Adjust spacing for different screen sizes */ +.example { + margin-top: var(--space-1x); + + @media (min-width: 768px) { + margin-top: var(--space-2x); + } +} diff --git a/src/components/CCIP/TutorialSetup/SolidityParam.tsx b/src/components/CCIP/TutorialSetup/SolidityParam.tsx new file mode 100644 index 00000000000..c6bc233c374 --- /dev/null +++ b/src/components/CCIP/TutorialSetup/SolidityParam.tsx @@ -0,0 +1,20 @@ +import styles from "./SolidityParam.module.css" +import { ReactCopyText } from "@components/ReactCopyText" + +interface SolidityParamProps { + name: string + type: string + description: string + example?: string +} + +export const SolidityParam = ({ name, type, description, example }: SolidityParamProps) => ( +
+ {name} + {type} +
+

{description}

+ {example && } +
+
+) diff --git a/src/components/CCIP/TutorialSetup/TutorialCard.module.css b/src/components/CCIP/TutorialSetup/TutorialCard.module.css new file mode 100644 index 00000000000..2734ab14550 --- /dev/null +++ b/src/components/CCIP/TutorialSetup/TutorialCard.module.css @@ -0,0 +1,27 @@ +.card { + background: var(--color-background); + border-radius: 12px; + padding: 24px; + margin: 16px 0; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.02); +} + +.title { + font-family: var(--font-display); + font-size: 16px; + font-weight: 600; + color: var(--color-text-primary); + margin-bottom: 8px; +} + +.description { + font-family: var(--font-body); + color: var(--color-text-secondary); + font-size: 14px; + line-height: 1.5; + margin-bottom: 16px; +} + +.content { + margin-top: var(--space-4x); +} diff --git a/src/components/CCIP/TutorialSetup/TutorialCard.tsx b/src/components/CCIP/TutorialSetup/TutorialCard.tsx new file mode 100644 index 00000000000..45121ab96ef --- /dev/null +++ b/src/components/CCIP/TutorialSetup/TutorialCard.tsx @@ -0,0 +1,17 @@ +import styles from "./TutorialCard.module.css" + +interface TutorialCardProps { + title: string + description?: string + children: React.ReactNode +} + +export const TutorialCard = ({ title, description, children }: TutorialCardProps) => { + return ( +
+
{title}
+ {description &&
{description}
} +
{children}
+
+ ) +} diff --git a/src/components/CCIP/TutorialSetup/TutorialStep.module.css b/src/components/CCIP/TutorialSetup/TutorialStep.module.css new file mode 100644 index 00000000000..dc61fa248a1 --- /dev/null +++ b/src/components/CCIP/TutorialSetup/TutorialStep.module.css @@ -0,0 +1,31 @@ +.step { + margin-bottom: var(--space-4x); +} + +.stepHeader { + display: flex; + align-items: center; + gap: var(--space-2x); + margin-bottom: var(--space-2x); +} + +.stepNumber { + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + border-radius: 50%; + background: var(--color-accent); + color: white; + font-weight: 500; +} + +.stepTitle { + font-weight: 600; + color: var(--color-text-primary); +} + +.stepContent { + padding-left: calc(24px + var(--space-2x)); +} diff --git a/src/components/CCIP/TutorialSetup/TutorialStep.tsx b/src/components/CCIP/TutorialSetup/TutorialStep.tsx new file mode 100644 index 00000000000..712db33f366 --- /dev/null +++ b/src/components/CCIP/TutorialSetup/TutorialStep.tsx @@ -0,0 +1,15 @@ +import styles from "./TutorialStep.module.css" + +interface TutorialStepProps { + title: string + children: React.ReactNode +} + +export const TutorialStep = ({ title, children }: TutorialStepProps) => ( +
  • +
    + {title} +
    +
    {children}
    +
  • +) diff --git a/src/content/ccip/tutorials/cross-chain-tokens/register-from-eoa-remix.mdx b/src/content/ccip/tutorials/cross-chain-tokens/register-from-eoa-remix.mdx index 43412bc6f50..d10d74eb069 100644 --- a/src/content/ccip/tutorials/cross-chain-tokens/register-from-eoa-remix.mdx +++ b/src/content/ccip/tutorials/cross-chain-tokens/register-from-eoa-remix.mdx @@ -21,6 +21,8 @@ import { ChainUpdateBuilderWrapper } from "@components/CCIP/TutorialBlockchainSe import { StepCheckbox } from "@components/CCIP/TutorialProgress/StepCheckbox" import { PrerequisitesCard } from "@components/CCIP/TutorialSetup/PrerequisitesCard" import { SetupSection } from "@components/CCIP/TutorialSetup/SetupSection" +import { DeployTokenStep } from "@components/CCIP/TutorialBlockchainSelector/DeployTokenStep" +import { TutorialCard } from "@components/CCIP/TutorialSetup/TutorialCard" This tutorial will guide you through enabling your tokens in CCIP using only your web browser and [Remix IDE](https://remix.ethereum.org). You'll learn how to deploy tokens and set up token pools without needing to install any development tools. @@ -76,25 +78,7 @@ The tutorial will use your provided token address for subsequent steps. -#### Deploy Token - -1. Ensure MetaMask is still connected to the source blockchain: - -1. In the "Deploy & Run Transactions" tab: - - - Set Environment to "Injected Provider - MetaMask" - - Select **BurnMintERC677** contract - - Configure deployment parameters: - - `name`: Your token's full name (e.g., "My Cross Chain Token") - - `symbol`: A short identifier for your token (e.g., "MCCT") - - `decimals`: Number of decimal places for token amounts (e.g., 18) - - `maxSupply`: Maximum total supply in wei (use 0 for unlimited supply) - - Click "Deploy" and confirm in MetaMask - - Your new token address will be displayed in the "Deployed Contracts" section - - Save your token address:
    -
    - -
    + #### Claim and Accept Admin Role