From 98bacec6e0ccca231f10f023eb937fa9cd23b2df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Sat, 13 Sep 2025 19:10:17 -0700 Subject: [PATCH 01/62] Navbar with dropdown menu + filtered sidebar --- docusaurus.config.js | 66 +++++++++++++++++++++-- sidebars.js | 76 +++++++++++++++++++++++---- src/css/partials/_navbar.scss | 98 +++++++++++++++++++++++++++++++++++ src/types/css-modules.d.ts | 4 ++ 4 files changed, 230 insertions(+), 14 deletions(-) create mode 100644 src/types/css-modules.d.ts diff --git a/docusaurus.config.js b/docusaurus.config.js index c13289c291..10df904542 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -42,6 +42,10 @@ const config = { integrity: 'sha384-odtC+0UGzzFL/6PNoE8rX/SPcQDXBJ+uRepguP4QkPCm2LBxH3FA3y+fKSiJ+AmM', crossorigin: 'anonymous', }, + { + href: '/css/inkeep-custom.css', + type: 'text/css', + }, ], presets: [ [ @@ -137,6 +141,14 @@ const config = { lightTheme: require('prism-react-renderer/themes/github'), darkTheme: require('prism-react-renderer/themes/palenight'), }, + components: { + SearchBarTrigger: { + // Configure responsive sizing behavior + defaultSize: 'medium', + minWidth: '180px', + maxWidth: '320px', + }, + }, }, }, modalSettings: { @@ -237,13 +249,57 @@ const config = { href: '/welcome/arbitrum-gentle-introduction', }, items: [ - // note: we can uncomment this when we want to display the locale dropdown in the top navbar - // if we enable this now, the dropdown will appear above every document; if `ja` is selected for a document that isn't yet translated, it will 404 - // there may be a way to show the dropdown only on pages that have been translated, but that's out of scope for the initial version + { + type: 'dropdown', + label: 'Build apps', + position: 'right', + items: [ + { + label: 'Quickstart', + to: '/build-decentralized-apps/quickstart-solidity-remix', + }, + { + label: 'Stylus', + to: '/stylus/gentle-introduction', + }, + ], + }, + { + type: 'docSidebar', + sidebarId: 'runArbitrumChainSidebar', + position: 'right', + label: 'Launch a chain', + }, // { - // type: 'localeDropdown', + // type: 'docSidebar', + // sidebarId: 'stylusSidebar', // position: 'right', - // } + // label: 'Use Stylus', + // }, + { + type: 'docSidebar', + sidebarId: 'runNodeSidebar', + position: 'right', + label: 'Run a node', + }, + { + type: 'docSidebar', + sidebarId: 'bridgeSidebar', + position: 'right', + label: 'Use the bridge', + }, + { + type: 'docSidebar', + sidebarId: 'howItWorksSidebar', + position: 'right', + label: 'How it works', + }, + { + type: 'docSidebar', + sidebarId: 'additionalResourcesSidebar', + position: 'right', + label: 'Resources', + }, ], }, footer: { diff --git a/sidebars.js b/sidebars.js index a01fc1828a..c38de5b746 100644 --- a/sidebars.js +++ b/sidebars.js @@ -25,6 +25,43 @@ const sdkSidebar = { /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ const sidebars = { + // Welcome sidebar - shared entry point + welcomeSidebar: [ + { + type: 'category', + label: 'Welcome', + collapsed: false, + items: [ + { + type: 'doc', + id: 'welcome/arbitrum-gentle-introduction', + label: 'A gentle introduction', + }, + { + type: 'doc', + id: 'welcome/get-started', + label: 'Get started', + }, + { + type: 'doc', + id: 'for-devs/dev-tools-and-resources/chain-info', + label: 'Chain info', + }, + ], + }, + { + type: 'doc', + id: 'learn-more/faq', + label: 'FAQ', + }, + { + type: 'doc', + id: 'intro/glossary', + label: 'Glossary', + }, + ], + + // Build dApps sidebar buildDecentralizedAppsSidebar: [ { type: 'category', @@ -255,10 +292,14 @@ const sidebars = { }, ], }, + ], + + // Run Arbitrum Chain sidebar + runArbitrumChainSidebar: [ { type: 'category', label: 'Run an Arbitrum (Orbit) chain', - collapsed: true, + collapsed: false, items: [ { type: 'doc', @@ -630,10 +671,14 @@ const sidebars = { }, ], }, + ], + + // Stylus sidebar + stylusSidebar: [ { type: 'category', label: 'Write Stylus Contracts', - collapsed: true, + collapsed: false, link: { type: 'doc', id: 'stylus/stylus-content-map', @@ -808,10 +853,14 @@ const sidebars = { }, ], }, + ], + + // Run Node sidebar + runNodeSidebar: [ { type: 'category', label: 'Run an Arbitrum node', - collapsed: true, + collapsed: false, link: { type: 'doc', id: 'node-running/node-running-content-map', @@ -977,10 +1026,14 @@ const sidebars = { }, ], }, + ], + + // Bridge sidebar + bridgeSidebar: [ { type: 'category', label: 'Arbitrum bridge', - collapsed: true, + collapsed: false, items: [ { type: 'doc', @@ -999,10 +1052,14 @@ const sidebars = { }, ], }, + ], + + // How it Works sidebar + howItWorksSidebar: [ { type: 'category', label: 'How Arbitrum works', - collapsed: true, + collapsed: false, items: [ { type: 'doc', @@ -1196,6 +1253,10 @@ const sidebars = { }, ], }, + ], + + // Additional resources sidebar + additionalResourcesSidebar: [ { type: 'doc', id: 'learn-more/faq', @@ -1209,6 +1270,7 @@ const sidebars = { { type: 'doc', id: 'for-devs/contribute', + label: 'Contribute', }, { type: 'category', @@ -1258,10 +1320,6 @@ const sidebars = { type: 'autogenerated', dirName: 'for-devs/third-party-docs', }, - { - type: 'doc', - id: 'for-devs/contribute', - }, ], }, { diff --git a/src/css/partials/_navbar.scss b/src/css/partials/_navbar.scss index ad63d96523..ebdb0444fb 100644 --- a/src/css/partials/_navbar.scss +++ b/src/css/partials/_navbar.scss @@ -12,6 +12,9 @@ > .navbar__items--right { display: flex; flex: 1; + gap: 8px; // Add spacing between navbar items + justify-content: flex-end; + align-items: center; #search_input_react { justify-content: flex-end; @@ -20,5 +23,100 @@ .DocSearch-Button-Keys { display: none; } + + // Ensure navbar items don't get squished + .navbar__item { + flex-shrink: 0; + margin-right: 8px; + } + + // Optimize dark mode toggle spacing + [class*='toggle'], + [class*='colorModeToggle'] { + margin-left: 8px; + flex-shrink: 0; + } + } +} + +// Adjust main content to account for the fixed top navigation +.main-wrapper { + margin-top: 0; +} + +// Ensure proper spacing between navbar and top navigation +.navbar { + border-bottom: none; +} + +// Inkeep search widget responsive behavior +#inkeep-widget, +[id*='inkeep'] { + // Allow natural sizing while ensuring navbar items have space + flex-shrink: 1; + min-width: 180px; + max-width: 300px; + + @media (max-width: 1200px) { + max-width: 250px; + } + + @media (max-width: 996px) { + max-width: 200px; + } + + @media (max-width: 768px) { + max-width: 180px; + min-width: 140px; + } +} + +// Fix for mobile navigation: ensure right-positioned navbar items appear in hamburger menu +@media (max-width: 996px) { + // Hide right navbar items by default on mobile + .navbar__items.navbar__items--right { + display: none; + } + + // Only show right navbar items when mobile sidebar is open + .navbar-sidebar--show .navbar__items.navbar__items--right { + display: flex !important; + flex-direction: column; + width: 100%; + margin-top: 1rem; + padding-top: 1rem; + border-top: 1px solid var(--ifm-toc-border-color); + } + + // Ensure all right navbar items are visible when mobile menu is open + .navbar-sidebar--show .navbar__items.navbar__items--right > * { + display: block !important; + position: static !important; + } + + // Style navbar links consistently in mobile menu when open + .navbar-sidebar--show .navbar__items.navbar__items--right .navbar__link { + padding: 0.75rem 1rem; + width: 100%; + text-align: left; + border-radius: 0.375rem; + margin: 0.125rem 0; + + &:hover { + background-color: var(--ifm-menu-color-background-hover); + } + } + + // Handle dropdown items in mobile menu when open + .navbar-sidebar--show .navbar__items.navbar__items--right .dropdown { + width: 100%; + + .dropdown__menu { + position: static !important; + transform: none !important; + box-shadow: none; + border: 1px solid var(--ifm-toc-border-color); + margin-top: 0.5rem; + } } } diff --git a/src/types/css-modules.d.ts b/src/types/css-modules.d.ts new file mode 100644 index 0000000000..d774364b20 --- /dev/null +++ b/src/types/css-modules.d.ts @@ -0,0 +1,4 @@ +declare module '*.module.css' { + const classes: { readonly [key: string]: string }; + export default classes; +} From 8aa34218f0fc612a7a18a1f603c221ea178dd11b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Tue, 16 Sep 2025 17:42:31 -0700 Subject: [PATCH 02/62] update pre-commit hook to origin branch --- .husky/pre-commit | 77 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 73 insertions(+), 4 deletions(-) diff --git a/.husky/pre-commit b/.husky/pre-commit index 923ed7a911..52c72905e8 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,5 +1,74 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" +#!/bin/sh -echo "Running git submodule update..." -git submodule update --init --recursive \ No newline at end of file +# Pre-commit hook for arbitrum-docs repository +# Enforces: redirect validation, submodule updates, code formatting +# Compatible with Husky v10 (no husky.sh sourcing required) + +# Color codes for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Error handling functions +log_info() { + echo "${BLUE}[INFO]${NC} $1" +} + +log_success() { + echo "${GREEN}[SUCCESS]${NC} $1" +} + +log_error() { + echo "${RED}[ERROR]${NC} $1" +} + +log_warning() { + echo "${YELLOW}[WARNING]${NC} $1" +} + +# Function to exit with error code and message +exit_with_error() { + log_error "$1" + exit 1 +} + +# Function to check command availability +check_command() { + if ! command -v "$1" >/dev/null 2>&1; then + exit_with_error "Command '$1' not found. Please ensure it's installed and in your PATH." + fi +} + +# Pre-flight checks +log_info "Starting pre-commit hook validation..." + +# Check required commands +check_command "yarn" +check_command "git" + +# 1. Redirect validation +log_info "Running redirect validation (yarn check-redirects)..." +if ! yarn check-redirects; then + exit_with_error "Redirect validation failed. Please fix redirect issues before committing." +fi +log_success "Redirect validation passed" + +# 2. Submodule updates with error checking +log_info "Running git submodule update --init --recursive..." +if ! git submodule update --init --recursive; then + exit_with_error "Git submodule update failed. Please check submodule configuration." +fi +log_success "Git submodules updated successfully" + +# 3. Code formatting with error checking +log_info "Running yarn format..." +if ! yarn format; then + exit_with_error "Code formatting failed. Please check for syntax errors." +fi +log_success "Code formatting completed" + +# Final success message +log_success "All pre-commit checks passed successfully!" +log_info "Commit can proceed..." From 75922029870693026ad75ab78aebbd3ef3b1ee80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Tue, 16 Sep 2025 18:04:06 -0700 Subject: [PATCH 03/62] add "get started" navbar item --- docusaurus.config.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docusaurus.config.js b/docusaurus.config.js index 10df904542..f564ab9cef 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -249,6 +249,12 @@ const config = { href: '/welcome/arbitrum-gentle-introduction', }, items: [ + { + type: 'docSidebar', + sidebarId: 'welcomeSidebar', + position: 'right', + label: 'Get started', + }, { type: 'dropdown', label: 'Build apps', From 60cdf957955d63ae71c404ae92f42a83dc5210b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Tue, 16 Sep 2025 18:20:23 -0700 Subject: [PATCH 04/62] rename "welcome" sidebar category to "get started" --- sidebars.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sidebars.js b/sidebars.js index c38de5b746..479d1f54ff 100644 --- a/sidebars.js +++ b/sidebars.js @@ -29,7 +29,7 @@ const sidebars = { welcomeSidebar: [ { type: 'category', - label: 'Welcome', + label: 'Get started', collapsed: false, items: [ { @@ -65,7 +65,7 @@ const sidebars = { buildDecentralizedAppsSidebar: [ { type: 'category', - label: 'Welcome', + label: 'Get started', collapsed: false, items: [ { From 06bac21567f42fb43d9f8fc9b066081081443a25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Tue, 16 Sep 2025 18:39:18 -0700 Subject: [PATCH 05/62] remove buildDecentralizedAppsSidebar sidebar content when Navbar's "Get Started" button is clicked --- sidebars.js | 40 ++++------------------------------------ 1 file changed, 4 insertions(+), 36 deletions(-) diff --git a/sidebars.js b/sidebars.js index 479d1f54ff..0b6cf3f510 100644 --- a/sidebars.js +++ b/sidebars.js @@ -27,37 +27,15 @@ const sdkSidebar = { const sidebars = { // Welcome sidebar - shared entry point welcomeSidebar: [ - { - type: 'category', - label: 'Get started', - collapsed: false, - items: [ - { - type: 'doc', - id: 'welcome/arbitrum-gentle-introduction', - label: 'A gentle introduction', - }, - { - type: 'doc', - id: 'welcome/get-started', - label: 'Get started', - }, - { - type: 'doc', - id: 'for-devs/dev-tools-and-resources/chain-info', - label: 'Chain info', - }, - ], - }, { type: 'doc', - id: 'learn-more/faq', - label: 'FAQ', + id: 'welcome/arbitrum-gentle-introduction', + label: 'A gentle introduction', }, { type: 'doc', - id: 'intro/glossary', - label: 'Glossary', + id: 'welcome/get-started', + label: 'Get started', }, ], @@ -68,16 +46,6 @@ const sidebars = { label: 'Get started', collapsed: false, items: [ - { - type: 'doc', - id: 'welcome/arbitrum-gentle-introduction', - label: 'A gentle introduction', - }, - { - type: 'doc', - id: 'welcome/get-started', - label: 'Get started', - }, { type: 'doc', id: 'for-devs/dev-tools-and-resources/chain-info', From 55506d2c8cafa151f739529b52a38505c838bd91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Tue, 16 Sep 2025 18:58:08 -0700 Subject: [PATCH 06/62] rename `docs/welcome` to `docs/get-started` --- CONTRIBUTE.md | 2 +- README.md | 2 +- .../arbitrum-gentle-introduction.mdx | 0 docs/{welcome => get-started}/get-started.mdx | 2 +- docs/partials/_contribute-docs-partial.mdx | 2 +- docusaurus.config.js | 4 ++-- sidebars.js | 8 +++---- src/pages/index.js | 2 +- vercel.json | 22 ++++++++++++++----- 9 files changed, 27 insertions(+), 17 deletions(-) rename docs/{welcome => get-started}/arbitrum-gentle-introduction.mdx (100%) rename docs/{welcome => get-started}/get-started.mdx (97%) diff --git a/CONTRIBUTE.md b/CONTRIBUTE.md index 28bcda1881..7694f0802b 100644 --- a/CONTRIBUTE.md +++ b/CONTRIBUTE.md @@ -73,7 +73,7 @@ navbar: { logo: { alt: 'My Site Logo', src: 'img/logo.svg', - href: '/welcome/arbitrum-gentle-introduction', + href: '/get-started/arbitrum-gentle-introduction', }, items: [ // note: we can uncomment this when we want to display the locale dropdown in the top navbar diff --git a/README.md b/README.md index 0ee75349e0..335d1da0dd 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ This repository is organized as follows: - `sdk/` - Auto-generated SDK documentation - Do not edit - `stylus/` - Stylus smart contract development - `stylus-by-example/` - Generated Stylus examples - Do not edit - - `welcome/` - Getting started content + - `get-started/` - Getting started content ### Application Code - **`src/`** - Docusaurus application source code diff --git a/docs/welcome/arbitrum-gentle-introduction.mdx b/docs/get-started/arbitrum-gentle-introduction.mdx similarity index 100% rename from docs/welcome/arbitrum-gentle-introduction.mdx rename to docs/get-started/arbitrum-gentle-introduction.mdx diff --git a/docs/welcome/get-started.mdx b/docs/get-started/get-started.mdx similarity index 97% rename from docs/welcome/get-started.mdx rename to docs/get-started/get-started.mdx index 7e1f7d43a8..8f0b1453a3 100644 --- a/docs/welcome/get-started.mdx +++ b/docs/get-started/get-started.mdx @@ -42,7 +42,7 @@ The Arbitrum suite includes the protocols, chains, services, and SDKs that power | Resource | Description | | ----------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | -| [A gentle introduction to Arbitrum](/welcome/arbitrum-gentle-introduction) | A technical introduction to Arbitrum's suite of scaling solutions. | +| [A gentle introduction to Arbitrum](/get-started/arbitrum-gentle-introduction) | A technical introduction to Arbitrum's suite of scaling solutions. | | [Quickstart (Solidity)](/build-decentralized-apps/01-quickstart-solidity-remix.mdx) | Targeted at Web2 developers who want to deploy their first Solidity smart contract to Arbitrum. | | [Quickstart (Rust)](/stylus/quickstart) | Targeted at Web3 developers who want to deploy their first Rust smart contract to Arbitrum using Stylus. | diff --git a/docs/partials/_contribute-docs-partial.mdx b/docs/partials/_contribute-docs-partial.mdx index a63329b2e4..051a7ca223 100644 --- a/docs/partials/_contribute-docs-partial.mdx +++ b/docs/partials/_contribute-docs-partial.mdx @@ -71,7 +71,7 @@ navbar: { logo: { alt: 'My Site Logo', src: 'img/logo.svg', - href: '/welcome/arbitrum-gentle-introduction', + href: '/get-started/arbitrum-gentle-introduction', }, items: [ // note: we can uncomment this when we want to display the locale dropdown in the top navbar diff --git a/docusaurus.config.js b/docusaurus.config.js index f564ab9cef..2b95d6d0d2 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -246,12 +246,12 @@ const config = { logo: { alt: 'Arbitrum Logo', src: 'img/logo.svg', - href: '/welcome/arbitrum-gentle-introduction', + href: '/get-started/arbitrum-gentle-introduction', }, items: [ { type: 'docSidebar', - sidebarId: 'welcomeSidebar', + sidebarId: 'getStartedSidebar', position: 'right', label: 'Get started', }, diff --git a/sidebars.js b/sidebars.js index 0b6cf3f510..1b13f35aba 100644 --- a/sidebars.js +++ b/sidebars.js @@ -25,16 +25,16 @@ const sdkSidebar = { /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ const sidebars = { - // Welcome sidebar - shared entry point - welcomeSidebar: [ + // Get started sidebar - shared entry point + getStartedSidebar: [ { type: 'doc', - id: 'welcome/arbitrum-gentle-introduction', + id: 'get-started/arbitrum-gentle-introduction', label: 'A gentle introduction', }, { type: 'doc', - id: 'welcome/get-started', + id: 'get-started/get-started', label: 'Get started', }, ], diff --git a/src/pages/index.js b/src/pages/index.js index 402b6a4dff..7091c1379c 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -15,7 +15,7 @@ function HomepageHeader(props) {

{siteConfig.title}

{/*

{siteConfig.tagline}

*/} - +
diff --git a/vercel.json b/vercel.json index d8daea8a0a..f6b354126f 100644 --- a/vercel.json +++ b/vercel.json @@ -4,7 +4,7 @@ "installCommand": "bash scripts/init-submodules.sh && yarn install", "framework": "docusaurus-2", "redirects": [ - { "source": "/", "destination": "/welcome/get-started", "permanent": false }, + { "source": "/", "destination": "/get-started/get-started", "permanent": false }, { "source": "/(/build-decentralized-apps/oracles/how-to-use-oracles/?)", "destination": "/build-decentralized-apps/oracles/overview-oracles", @@ -240,6 +240,16 @@ "destination": "/how-arbitrum-works/transaction-lifecycle", "permanent": false }, + { + "source": "/(docs/welcome/arbitrum-gentle-introduction/?)", + "destination": "/(docs/get-started/arbitrum-gentle-introduction/?)", + "permanent": false + }, + { + "source": "/(docs/welcome/get-started/?)", + "destination": "/(docs/get-started/get-started/?)", + "permanent": false + }, { "source": "/(docs/withdrawals/?)", "destination": "/how-arbitrum-works/transaction-lifecycle", @@ -352,7 +362,7 @@ }, { "source": "/(for-devs/gentle-introduction-dapps/?)", - "destination": "/welcome/arbitrum-gentle-introduction", + "destination": "/get-started/arbitrum-gentle-introduction", "permanent": false }, { @@ -547,7 +557,7 @@ }, { "source": "/(intro/?)", - "destination": "/welcome/arbitrum-gentle-introduction", + "destination": "/get-started/arbitrum-gentle-introduction", "permanent": false }, { @@ -1837,7 +1847,7 @@ }, { "source": "/(welcome/?)", - "destination": "/welcome/arbitrum-gentle-introduction", + "destination": "/get-started/arbitrum-gentle-introduction", "permanent": false }, { @@ -1897,7 +1907,7 @@ }, { "source": "/docs/developer_quickstart", - "destination": "/welcome/get-started", + "destination": "/get-started/get-started", "permanent": false }, { @@ -2013,7 +2023,7 @@ "destination": "/faqs/protocol-faqs#q-seq-vs-val", "permanent": false }, - { "source": "/faqs/the-merge", "destination": "/welcome/get-started", "permanent": false }, + { "source": "/faqs/the-merge", "destination": "/get-started/get-started", "permanent": false }, { "source": "/faqs/tooling-faqs", "destination": "/for-devs/troubleshooting-building", From aa2d37b5e2e0852460098cdac18848151bd92506 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Tue, 16 Sep 2025 19:00:17 -0700 Subject: [PATCH 07/62] rename `docs/welcome` to `docs/get-started` --- docs/get-started/get-started.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/get-started/get-started.mdx b/docs/get-started/get-started.mdx index 8f0b1453a3..4904246a08 100644 --- a/docs/get-started/get-started.mdx +++ b/docs/get-started/get-started.mdx @@ -42,7 +42,7 @@ The Arbitrum suite includes the protocols, chains, services, and SDKs that power | Resource | Description | | ----------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | -| [A gentle introduction to Arbitrum](/get-started/arbitrum-gentle-introduction) | A technical introduction to Arbitrum's suite of scaling solutions. | +| [A gentle introduction to Arbitrum](/get-started/arbitrum-gentle-introduction) | A technical introduction to Arbitrum's suite of scaling solutions. | | [Quickstart (Solidity)](/build-decentralized-apps/01-quickstart-solidity-remix.mdx) | Targeted at Web2 developers who want to deploy their first Solidity smart contract to Arbitrum. | | [Quickstart (Rust)](/stylus/quickstart) | Targeted at Web3 developers who want to deploy their first Rust smart contract to Arbitrum using Stylus. | From 5abea4a15bbaabecb3b1b922afbf0cac79dfcdce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Tue, 16 Sep 2025 19:11:16 -0700 Subject: [PATCH 08/62] rename `arbitrum-gentle-introduction.mdx` to `arbitrum-faq.mdx` and update all references --- CONTRIBUTE.md | 2 +- docs/get-started/arbitrum-faq.mdx | 11 +++++++++++ .../get-started/arbitrum-gentle-introduction.mdx | 11 ----------- docs/get-started/get-started.mdx | 2 +- docs/partials/_contribute-docs-partial.mdx | 2 +- docusaurus.config.js | 2 +- sidebars.js | 4 ++-- src/pages/index.js | 4 ++-- vercel.json | 16 ++++------------ 9 files changed, 23 insertions(+), 31 deletions(-) create mode 100644 docs/get-started/arbitrum-faq.mdx delete mode 100644 docs/get-started/arbitrum-gentle-introduction.mdx diff --git a/CONTRIBUTE.md b/CONTRIBUTE.md index 7694f0802b..4e75185f7c 100644 --- a/CONTRIBUTE.md +++ b/CONTRIBUTE.md @@ -73,7 +73,7 @@ navbar: { logo: { alt: 'My Site Logo', src: 'img/logo.svg', - href: '/get-started/arbitrum-gentle-introduction', + href: '/get-started/arbitrum-faq', }, items: [ // note: we can uncomment this when we want to display the locale dropdown in the top navbar diff --git a/docs/get-started/arbitrum-faq.mdx b/docs/get-started/arbitrum-faq.mdx new file mode 100644 index 0000000000..6a1428bffe --- /dev/null +++ b/docs/get-started/arbitrum-faq.mdx @@ -0,0 +1,11 @@ +--- +title: 'Arbitrum FAQ' +description: 'Frequently asked questions about Arbitrum, Ethereum scaling solutions including Arbitrum One, Arbitrum Nova, Stylus, Arbitrum chains, and the Arbitrum Bridge.' +author: dzgoldman +user_story: As a technical reader, I want to understand how Arbitrum scales Ethereum. +content_type: faq +--- + +import GentleIntroPartial from '../partials/_gentle-intro-partial.mdx'; + + diff --git a/docs/get-started/arbitrum-gentle-introduction.mdx b/docs/get-started/arbitrum-gentle-introduction.mdx deleted file mode 100644 index 98e8cda84d..0000000000 --- a/docs/get-started/arbitrum-gentle-introduction.mdx +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: 'A gentle introduction to Arbitrum' -description: 'Arbitrum is a suite of Ethereum scaling solutions that includes Arbitrum One, Arbitrum Nova, Stylus, Arbitrum chains, and the Arbitrum Bridge.' -author: dzgoldman -user_story: As a technical reader, I want to understand how Arbitrum scales Ethereum. -content_type: gentle-introduction ---- - -import GentleIntroPartial from '../partials/_gentle-intro-partial.mdx'; - - diff --git a/docs/get-started/get-started.mdx b/docs/get-started/get-started.mdx index 4904246a08..9e9638b6f5 100644 --- a/docs/get-started/get-started.mdx +++ b/docs/get-started/get-started.mdx @@ -42,7 +42,7 @@ The Arbitrum suite includes the protocols, chains, services, and SDKs that power | Resource | Description | | ----------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | -| [A gentle introduction to Arbitrum](/get-started/arbitrum-gentle-introduction) | A technical introduction to Arbitrum's suite of scaling solutions. | +| [Arbitrum FAQ](/get-started/arbitrum-faq) | Frequently asked questions about Arbitrum's suite of scaling solutions. | | [Quickstart (Solidity)](/build-decentralized-apps/01-quickstart-solidity-remix.mdx) | Targeted at Web2 developers who want to deploy their first Solidity smart contract to Arbitrum. | | [Quickstart (Rust)](/stylus/quickstart) | Targeted at Web3 developers who want to deploy their first Rust smart contract to Arbitrum using Stylus. | diff --git a/docs/partials/_contribute-docs-partial.mdx b/docs/partials/_contribute-docs-partial.mdx index 051a7ca223..39fe29b2fd 100644 --- a/docs/partials/_contribute-docs-partial.mdx +++ b/docs/partials/_contribute-docs-partial.mdx @@ -71,7 +71,7 @@ navbar: { logo: { alt: 'My Site Logo', src: 'img/logo.svg', - href: '/get-started/arbitrum-gentle-introduction', + href: '/get-started/arbitrum-faq', }, items: [ // note: we can uncomment this when we want to display the locale dropdown in the top navbar diff --git a/docusaurus.config.js b/docusaurus.config.js index 2b95d6d0d2..e0c95dc0b8 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -246,7 +246,7 @@ const config = { logo: { alt: 'Arbitrum Logo', src: 'img/logo.svg', - href: '/get-started/arbitrum-gentle-introduction', + href: '/get-started/arbitrum-faq', }, items: [ { diff --git a/sidebars.js b/sidebars.js index 1b13f35aba..a88c9c24cb 100644 --- a/sidebars.js +++ b/sidebars.js @@ -29,8 +29,8 @@ const sidebars = { getStartedSidebar: [ { type: 'doc', - id: 'get-started/arbitrum-gentle-introduction', - label: 'A gentle introduction', + id: 'get-started/arbitrum-faq', + label: 'FAQ', }, { type: 'doc', diff --git a/src/pages/index.js b/src/pages/index.js index 7091c1379c..f75e3e81ee 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -15,8 +15,8 @@ function HomepageHeader(props) {

{siteConfig.title}

{/*

{siteConfig.tagline}

*/} - - + +
); diff --git a/vercel.json b/vercel.json index f6b354126f..b895a97ed2 100644 --- a/vercel.json +++ b/vercel.json @@ -242,7 +242,7 @@ }, { "source": "/(docs/welcome/arbitrum-gentle-introduction/?)", - "destination": "/(docs/get-started/arbitrum-gentle-introduction/?)", + "destination": "/(docs/get-started/arbitrum-faq/?)", "permanent": false }, { @@ -362,7 +362,7 @@ }, { "source": "/(for-devs/gentle-introduction-dapps/?)", - "destination": "/get-started/arbitrum-gentle-introduction", + "destination": "/get-started/arbitrum-faq", "permanent": false }, { @@ -555,11 +555,7 @@ "destination": "/how-arbitrum-works/a-gentle-introduction", "permanent": false }, - { - "source": "/(intro/?)", - "destination": "/get-started/arbitrum-gentle-introduction", - "permanent": false - }, + { "source": "/(intro/?)", "destination": "/get-started/arbitrum-faq", "permanent": false }, { "source": "/(launch-arbitrum-chain/arbitrum-chain-quickstart/?)", "destination": "/launch-arbitrum-chain/a-gentle-introduction", @@ -1845,11 +1841,7 @@ "destination": "/how-arbitrum-works/transaction-lifecycle", "permanent": false }, - { - "source": "/(welcome/?)", - "destination": "/get-started/arbitrum-gentle-introduction", - "permanent": false - }, + { "source": "/(welcome/?)", "destination": "/get-started/arbitrum-faq", "permanent": false }, { "source": "/(why-nitro/?)", "destination": "/how-arbitrum-works/a-gentle-introduction", From 211cbd12553ff9be96e7a1eadb1641e8f1198bfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Tue, 16 Sep 2025 19:11:50 -0700 Subject: [PATCH 09/62] rename `arbitrum-gentle-introduction.mdx` to `arbitrum-faq.mdx` and update all references --- docs/get-started/get-started.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/get-started/get-started.mdx b/docs/get-started/get-started.mdx index 9e9638b6f5..61666d604b 100644 --- a/docs/get-started/get-started.mdx +++ b/docs/get-started/get-started.mdx @@ -42,7 +42,7 @@ The Arbitrum suite includes the protocols, chains, services, and SDKs that power | Resource | Description | | ----------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | -| [Arbitrum FAQ](/get-started/arbitrum-faq) | Frequently asked questions about Arbitrum's suite of scaling solutions. | +| [Arbitrum FAQ](/get-started/arbitrum-faq) | Frequently asked questions about Arbitrum's suite of scaling solutions. | | [Quickstart (Solidity)](/build-decentralized-apps/01-quickstart-solidity-remix.mdx) | Targeted at Web2 developers who want to deploy their first Solidity smart contract to Arbitrum. | | [Quickstart (Rust)](/stylus/quickstart) | Targeted at Web3 developers who want to deploy their first Rust smart contract to Arbitrum using Stylus. | From 85f4c28fc469a72d41ef7ace0296b375765037d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Tue, 16 Sep 2025 19:28:55 -0700 Subject: [PATCH 10/62] fix navbar header link --- docs/get-started/get-started.mdx | 1 + docusaurus.config.js | 2 +- sidebars.js | 8 ++++---- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/get-started/get-started.mdx b/docs/get-started/get-started.mdx index 61666d604b..a51e31f2ab 100644 --- a/docs/get-started/get-started.mdx +++ b/docs/get-started/get-started.mdx @@ -4,6 +4,7 @@ description: An abbreviated introduction to the Arbitrum suite author: symbolpunk user_story: As a curious reader, I'd like to be quickly guided towards first steps based on my particular needs. content_type: quickstart +slug: /get-started --- Arbitrum is a suite of Ethereum scaling solutions that make it diff --git a/docusaurus.config.js b/docusaurus.config.js index e0c95dc0b8..45cdbccab8 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -246,7 +246,7 @@ const config = { logo: { alt: 'Arbitrum Logo', src: 'img/logo.svg', - href: '/get-started/arbitrum-faq', + href: '/get-started', }, items: [ { diff --git a/sidebars.js b/sidebars.js index a88c9c24cb..95b4b2525f 100644 --- a/sidebars.js +++ b/sidebars.js @@ -29,13 +29,13 @@ const sidebars = { getStartedSidebar: [ { type: 'doc', - id: 'get-started/arbitrum-faq', - label: 'FAQ', + id: 'get-started/get-started', + label: 'Get started', }, { type: 'doc', - id: 'get-started/get-started', - label: 'Get started', + id: 'get-started/arbitrum-faq', + label: 'FAQ', }, ], From 03f2977e26005fe40b8483c8879d15272de1f351 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Tue, 16 Sep 2025 20:04:34 -0700 Subject: [PATCH 11/62] display chain info page everywhere --- .../01-quickstart-solidity-remix.mdx | 2 + sidebars.js | 47 ++++++++++++++----- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/docs/build-decentralized-apps/01-quickstart-solidity-remix.mdx b/docs/build-decentralized-apps/01-quickstart-solidity-remix.mdx index 9874a92831..e023d9f99a 100644 --- a/docs/build-decentralized-apps/01-quickstart-solidity-remix.mdx +++ b/docs/build-decentralized-apps/01-quickstart-solidity-remix.mdx @@ -4,6 +4,8 @@ description: This quickstart walks you through the process of converting a Javas author: symbolpunk user_story: As a web2 developer, I want to onboard into Arbitrum by building and deploying my first smart contract, and knowing how to build a web widget interacting with it. content_type: quickstart +slug: /build-decentralized-apps/quickstart-solidity-remix + --- import { VanillaAdmonition } from '@site/src/components/VanillaAdmonition/'; diff --git a/sidebars.js b/sidebars.js index 95b4b2525f..535d5d689c 100644 --- a/sidebars.js +++ b/sidebars.js @@ -37,22 +37,15 @@ const sidebars = { id: 'get-started/arbitrum-faq', label: 'FAQ', }, + { + type: 'doc', + id: 'for-devs/dev-tools-and-resources/chain-info', + label: 'Chain info', + }, ], // Build dApps sidebar buildDecentralizedAppsSidebar: [ - { - type: 'category', - label: 'Get started', - collapsed: false, - items: [ - { - type: 'doc', - id: 'for-devs/dev-tools-and-resources/chain-info', - label: 'Chain info', - }, - ], - }, { type: 'category', label: 'Build decentralized apps', @@ -260,6 +253,11 @@ const sidebars = { }, ], }, + { + type: 'doc', + id: 'for-devs/dev-tools-and-resources/chain-info', + label: 'Chain info', + }, ], // Run Arbitrum Chain sidebar @@ -637,6 +635,11 @@ const sidebars = { id: 'launch-arbitrum-chain/faq-troubleshooting/troubleshooting-building-arbitrum-chain', label: 'FAQ', }, + { + type: 'doc', + id: 'for-devs/dev-tools-and-resources/chain-info', + label: 'Chain info', + }, ], }, ], @@ -992,6 +995,11 @@ const sidebars = { label: 'FAQ', id: 'node-running/faq', }, + { + type: 'doc', + id: 'for-devs/dev-tools-and-resources/chain-info', + label: 'Chain info', + }, ], }, ], @@ -1018,6 +1026,11 @@ const sidebars = { id: 'arbitrum-bridge/troubleshooting', label: 'Troubleshooting', }, + { + type: 'doc', + id: 'for-devs/dev-tools-and-resources/chain-info', + label: 'Chain info', + }, ], }, ], @@ -1221,6 +1234,11 @@ const sidebars = { }, ], }, + { + type: 'doc', + id: 'for-devs/dev-tools-and-resources/chain-info', + label: 'Chain info', + }, ], // Additional resources sidebar @@ -1305,6 +1323,11 @@ const sidebars = { label: 'Prysm docs', href: 'https://www.offchainlabs.com/prysm/docs', }, + { + type: 'doc', + id: 'for-devs/dev-tools-and-resources/chain-info', + label: 'Chain info', + }, ], }; From 2ce81fb74ed4786e3685b055b49e4a033a7e50c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Tue, 16 Sep 2025 20:27:42 -0700 Subject: [PATCH 12/62] display stylus and build apps content in selective collapsed states in the sidebar --- .../01-quickstart-solidity-remix.mdx | 2 +- .../02-how-to-estimate-gas.mdx | 1 + .../03-public-chains.mdx | 1 + .../04-cross-chain-messaging.mdx | 1 + .../01-comparison-overview.mdx | 1 + .../02-block-numbers-and-time.mdx | 1 + .../arbitrum-vs-ethereum/03-rpc-methods.mdx | 1 + .../04-solidity-support.mdx | 1 + .../custom-gas-token-sdk.mdx | 1 + .../nodeinterface/01-overview.mdx | 1 + .../nodeinterface/02-reference.mdx | 1 + .../oracles/01-overview.mdx | 1 + .../precompiles/01-overview.mdx | 1 + .../precompiles/02-reference.mdx | 1 + .../reference/01-node-providers.mdx | 1 + .../reference/02-contract-addresses.mdx | 1 + .../reference/03-chain-params.mdx | 1 + .../reference/04-development-frameworks.mdx | 1 + .../reference/05-web3-libraries-tools.mdx | 1 + .../06-monitoring-tools-block-explorers.mdx | 1 + .../reference/07-debugging-tools.mdx | 1 + .../reference/08-mainnet-risks.mdx | 1 + .../token-bridging/01-overview.mdx | 1 + .../token-bridging/02-token-bridge-ether.mdx | 1 + .../token-bridging/03-token-bridge-erc20.mdx | 1 + .../01-get-started.mdx | 1 + .../02-how-to-bridge-tokens-standard.mdx | 1 + ...03-how-to-bridge-tokens-generic-custom.mdx | 1 + ...04-how-to-bridge-tokens-custom-gateway.mdx | 1 + docs/stylus/concepts/gas-metering.mdx | 1 + docs/stylus/gentle-introduction.mdx | 1 + .../adding-support-for-new-languages.mdx | 1 + docs/stylus/how-tos/caching-contracts.mdx | 1 + docs/stylus/how-tos/debugging-tx.mdx | 1 + docs/stylus/how-tos/optimizing-binaries.mdx | 1 + docs/stylus/how-tos/testing-contracts.mdx | 1 + docs/stylus/how-tos/using-constructors.mdx | 1 + docs/stylus/how-tos/using-inheritance.mdx | 1 + .../how-tos/verifying-contracts-arbiscan.mdx | 1 + docs/stylus/how-tos/verifying-contracts.mdx | 1 + docs/stylus/overview.mdx | 1 + docs/stylus/quickstart.mdx | 1 + docs/stylus/reference/project-structure.mdx | 1 + docs/stylus/stylus-content-map.mdx | 1 + docs/stylus/using-cli.mdx | 1 + docusaurus.config.js | 2 +- sidebars.js | 1182 +++++++++++++++++ 47 files changed, 1228 insertions(+), 2 deletions(-) diff --git a/docs/build-decentralized-apps/01-quickstart-solidity-remix.mdx b/docs/build-decentralized-apps/01-quickstart-solidity-remix.mdx index e023d9f99a..a5689be4e4 100644 --- a/docs/build-decentralized-apps/01-quickstart-solidity-remix.mdx +++ b/docs/build-decentralized-apps/01-quickstart-solidity-remix.mdx @@ -5,7 +5,7 @@ author: symbolpunk user_story: As a web2 developer, I want to onboard into Arbitrum by building and deploying my first smart contract, and knowing how to build a web widget interacting with it. content_type: quickstart slug: /build-decentralized-apps/quickstart-solidity-remix - +displayed_sidebar: buildAppsSidebarFromGetStarted --- import { VanillaAdmonition } from '@site/src/components/VanillaAdmonition/'; diff --git a/docs/build-decentralized-apps/02-how-to-estimate-gas.mdx b/docs/build-decentralized-apps/02-how-to-estimate-gas.mdx index fb8dccc832..a09d17ad2f 100644 --- a/docs/build-decentralized-apps/02-how-to-estimate-gas.mdx +++ b/docs/build-decentralized-apps/02-how-to-estimate-gas.mdx @@ -3,6 +3,7 @@ title: 'How to estimate gas in Arbitrum' description: Learn how to estimate gas before submitting transactions. author: TucksonDev content_type: how-to +displayed_sidebar: buildAppsSidebarFromGetStarted --- :::info Looking for Stylus guidance? diff --git a/docs/build-decentralized-apps/03-public-chains.mdx b/docs/build-decentralized-apps/03-public-chains.mdx index ff7dbe471c..622cd63797 100644 --- a/docs/build-decentralized-apps/03-public-chains.mdx +++ b/docs/build-decentralized-apps/03-public-chains.mdx @@ -3,6 +3,7 @@ title: 'Arbitrum chains overview' description: A high level description of the Arbitrum chains available user_story: As a developer, I want to understand the different Arbitrum chains and how they relate to each other. content_type: concept +displayed_sidebar: buildAppsSidebarFromGetStarted --- import { AddressExplorerLink as AEL } from '@site/src/components/AddressExplorerLink'; diff --git a/docs/build-decentralized-apps/04-cross-chain-messaging.mdx b/docs/build-decentralized-apps/04-cross-chain-messaging.mdx index c3edf35e81..b19d030081 100644 --- a/docs/build-decentralized-apps/04-cross-chain-messaging.mdx +++ b/docs/build-decentralized-apps/04-cross-chain-messaging.mdx @@ -3,6 +3,7 @@ title: 'Cross-chain messaging overview' description: Learn about cross-chain messaging in Arbitrum user_story: As a developer, I want to understand how cross-chain messaging works in Arbitrum. content_type: concept +displayed_sidebar: buildAppsSidebarFromGetStarted --- The Arbitrum protocol and related tooling makes it easy for developers to build cross-chain applications; i.e., applications that involve sending messages from Ethereum to an Arbitrum chain, and/or from an Arbitrum chain to Ethereum. diff --git a/docs/build-decentralized-apps/arbitrum-vs-ethereum/01-comparison-overview.mdx b/docs/build-decentralized-apps/arbitrum-vs-ethereum/01-comparison-overview.mdx index f930f09604..de5f63f00b 100644 --- a/docs/build-decentralized-apps/arbitrum-vs-ethereum/01-comparison-overview.mdx +++ b/docs/build-decentralized-apps/arbitrum-vs-ethereum/01-comparison-overview.mdx @@ -6,6 +6,7 @@ author: jose-franco sme: jose-franco target_audience: developers who want to build on Arbitrum content_type: concept +displayed_sidebar: buildAppsSidebarFromGetStarted --- Arbitrum is designed to be as compatible and consistent with Ethereum as possible, from its high-level RPCs to its low-level bytecode and everything in between. Decentralized app (dApp) developers with experience building on Ethereum will likely find that little-to-no new specific knowledge is required to build on Arbitrum. diff --git a/docs/build-decentralized-apps/arbitrum-vs-ethereum/02-block-numbers-and-time.mdx b/docs/build-decentralized-apps/arbitrum-vs-ethereum/02-block-numbers-and-time.mdx index 107cdf0469..146ec0c672 100644 --- a/docs/build-decentralized-apps/arbitrum-vs-ethereum/02-block-numbers-and-time.mdx +++ b/docs/build-decentralized-apps/arbitrum-vs-ethereum/02-block-numbers-and-time.mdx @@ -6,6 +6,7 @@ author: dzgoldman, jose-franco sme: jose-franco target_audience: developers who want to build on Arbitrum content_type: concept +displayed_sidebar: buildAppsSidebarFromGetStarted --- :::info block number vs `block.number` diff --git a/docs/build-decentralized-apps/arbitrum-vs-ethereum/03-rpc-methods.mdx b/docs/build-decentralized-apps/arbitrum-vs-ethereum/03-rpc-methods.mdx index 2c76e88038..bdf05e55cb 100644 --- a/docs/build-decentralized-apps/arbitrum-vs-ethereum/03-rpc-methods.mdx +++ b/docs/build-decentralized-apps/arbitrum-vs-ethereum/03-rpc-methods.mdx @@ -5,6 +5,7 @@ description: This concept page provides information about the differences betwee target_audience: developers who want to build on Arbitrum author: dzgoldman content_type: concept +displayed_sidebar: buildAppsSidebarFromGetStarted --- Although the majority of RPC methods follow the same behavior as in Ethereum, some methods might produce a different result, or add more information, when used on an Arbitrum chain. This page covers the differences in response body fields you'll find when calling RPC methods on an Arbitrum chain vs on Ethereum. diff --git a/docs/build-decentralized-apps/arbitrum-vs-ethereum/04-solidity-support.mdx b/docs/build-decentralized-apps/arbitrum-vs-ethereum/04-solidity-support.mdx index b7256b2295..0697cfe57c 100644 --- a/docs/build-decentralized-apps/arbitrum-vs-ethereum/04-solidity-support.mdx +++ b/docs/build-decentralized-apps/arbitrum-vs-ethereum/04-solidity-support.mdx @@ -5,6 +5,7 @@ description: This concept page provides information about the differences betwee target_audience: developers who want to build on Arbitrum author: dzgoldman content_type: concept +displayed_sidebar: buildAppsSidebarFromGetStarted --- Arbitrum chains are Ethereum compatible, and therefore allow you to trustlessly deploy Solidity smart contracts, as well as contracts written in Vyper or any other language that compiles to EVM bytecode. However, when calling certain properties and functions on a Solidity smart contract, there might be some differences between the result you'd obtain if that contract was on Ethereum, and the result on Arbitrum. diff --git a/docs/build-decentralized-apps/custom-gas-token-sdk.mdx b/docs/build-decentralized-apps/custom-gas-token-sdk.mdx index f3c788cb65..1eb728e58f 100644 --- a/docs/build-decentralized-apps/custom-gas-token-sdk.mdx +++ b/docs/build-decentralized-apps/custom-gas-token-sdk.mdx @@ -5,6 +5,7 @@ author: Mehdi Salehi sme: Mehdi Salehi target_audience: 'Developers deploying and maintaining Arbitrum chains.' sidebar_position: 2 +displayed_sidebar: buildAppsSidebarFromGetStarted --- Arbitrum SDK is a TypeScript library for client-side interactions with Arbitrum. It provides common helper functionality as well as access to the underlying smart contract interfaces. diff --git a/docs/build-decentralized-apps/nodeinterface/01-overview.mdx b/docs/build-decentralized-apps/nodeinterface/01-overview.mdx index e526c8ddff..13dc2d43ab 100644 --- a/docs/build-decentralized-apps/nodeinterface/01-overview.mdx +++ b/docs/build-decentralized-apps/nodeinterface/01-overview.mdx @@ -3,6 +3,7 @@ title: 'NodeInterface overview' description: A high level description of what the NodeInterface is and how it works user_story: As a developer, I want to understand what the NodeInterface is and how it works. content_type: concept +displayed_sidebar: buildAppsSidebarFromGetStarted --- diff --git a/docs/build-decentralized-apps/nodeinterface/02-reference.mdx b/docs/build-decentralized-apps/nodeinterface/02-reference.mdx index 5276dd4fcf..d99e8c9af8 100644 --- a/docs/build-decentralized-apps/nodeinterface/02-reference.mdx +++ b/docs/build-decentralized-apps/nodeinterface/02-reference.mdx @@ -3,6 +3,7 @@ title: 'NodeInterface reference' description: A reference page of the NodeInterface available on Arbitrum chains user_story: As a developer, I want to understand the specific methods available in the NodeInterface content_type: reference +displayed_sidebar: buildAppsSidebarFromGetStarted --- The Arbitrum Nitro software includes a special `NodeInterface` contract available at address `0xc8` that is only accessible via RPCs (it's not actually deployed onchain, and thus can't be called by smart contracts). This reference page documents the specific calls available in the `NodeInterface`. For a more conceptual description of what it is and how it works, please refer to the [`NodeInterface` conceptual page](/build-decentralized-apps/nodeinterface/01-overview.mdx). diff --git a/docs/build-decentralized-apps/oracles/01-overview.mdx b/docs/build-decentralized-apps/oracles/01-overview.mdx index 169591d654..262c403070 100644 --- a/docs/build-decentralized-apps/oracles/01-overview.mdx +++ b/docs/build-decentralized-apps/oracles/01-overview.mdx @@ -5,6 +5,7 @@ description: A high level description of what oracles are user_story: As a developer, I want to understand what oracles are and how they work. content_type: concept sidebar_label: Oracles +displayed_sidebar: buildAppsSidebarFromGetStarted --- import ImageZoom from '@site/src/components/ImageZoom'; diff --git a/docs/build-decentralized-apps/precompiles/01-overview.mdx b/docs/build-decentralized-apps/precompiles/01-overview.mdx index 2b93b1758f..97fcd2eb1f 100644 --- a/docs/build-decentralized-apps/precompiles/01-overview.mdx +++ b/docs/build-decentralized-apps/precompiles/01-overview.mdx @@ -3,6 +3,7 @@ title: 'Precompiles overview' description: A high level description of what precompiles are and how they work user_story: As a developer, I want to understand what precompiles are and how they work. content_type: concept +displayed_sidebar: buildAppsSidebarFromGetStarted --- Precompiles are predefined smart contracts that have special addresses and provide specific functionality which is executed not at the EVM bytecode level, but natively by the Arbitrum client itself. Precompiles are primarily used to introduce specific functions that would be computationally expensive if executed in EVM bytecode, and functions that facilitate the interaction between the parent chain and the child chain. By having them natively in the Arbitrum client, they can be optimized for performance. diff --git a/docs/build-decentralized-apps/precompiles/02-reference.mdx b/docs/build-decentralized-apps/precompiles/02-reference.mdx index 8c4416dbbe..9898f6a46e 100644 --- a/docs/build-decentralized-apps/precompiles/02-reference.mdx +++ b/docs/build-decentralized-apps/precompiles/02-reference.mdx @@ -3,6 +3,7 @@ title: 'Precompiles reference' description: A reference page of all precompiles available on Arbitrum chains user_story: As a developer, I want to understand the most useful precompiles available on Arbitrum chains and how to use them. content_type: reference +displayed_sidebar: buildAppsSidebarFromGetStarted --- ArbOS provides child chain-specific precompiles with methods smart contracts can call the same way they can solidity functions. This reference page exhaustively documents the specific calls ArbOS makes available through precompiles. For a more conceptual description of what precompiles are and how they work, please refer to the [precompiles conceptual page](/build-decentralized-apps/precompiles/01-overview.mdx). diff --git a/docs/build-decentralized-apps/reference/01-node-providers.mdx b/docs/build-decentralized-apps/reference/01-node-providers.mdx index e194fc9116..dc2f1eb440 100644 --- a/docs/build-decentralized-apps/reference/01-node-providers.mdx +++ b/docs/build-decentralized-apps/reference/01-node-providers.mdx @@ -3,6 +3,7 @@ title: 'RPC endpoints and providers' description: Find available RPC endpoints and providers in the ecosystem reader_audience: developers who want to build on Arbitrum content_type: overview +displayed_sidebar: buildAppsSidebarFromGetStarted --- import ArbitrumRpcEndpoints from '../../partials/_reference-arbitrum-rpc-endpoints-partial.mdx'; diff --git a/docs/build-decentralized-apps/reference/02-contract-addresses.mdx b/docs/build-decentralized-apps/reference/02-contract-addresses.mdx index 3996a0a5e1..5f8781451c 100644 --- a/docs/build-decentralized-apps/reference/02-contract-addresses.mdx +++ b/docs/build-decentralized-apps/reference/02-contract-addresses.mdx @@ -6,6 +6,7 @@ author: anegg0 sme: anegg0 user_story: As a current or prospective Arbitrum user I need to know to what addresses Arbitrum contracts have been deployed. content_type: reference +displayed_sidebar: buildAppsSidebarFromGetStarted --- import ArbitrumContractAddresses from '../../partials/_reference-arbitrum-contract-addresses-partial.mdx'; diff --git a/docs/build-decentralized-apps/reference/03-chain-params.mdx b/docs/build-decentralized-apps/reference/03-chain-params.mdx index d60fd8418d..9be8684f24 100644 --- a/docs/build-decentralized-apps/reference/03-chain-params.mdx +++ b/docs/build-decentralized-apps/reference/03-chain-params.mdx @@ -3,6 +3,7 @@ title: 'Chain parameters' description: Information about important system parameters for public Arbitrum chains user_story: As a developer, I want to understand the system parameters for the public Arbitrum chains. content_type: overview +displayed_sidebar: buildAppsSidebarFromGetStarted --- | Param | Description | Arbitrum One | Arbitrum Nova | Arb Sepolia | diff --git a/docs/build-decentralized-apps/reference/04-development-frameworks.mdx b/docs/build-decentralized-apps/reference/04-development-frameworks.mdx index a164da3752..d871310e84 100644 --- a/docs/build-decentralized-apps/reference/04-development-frameworks.mdx +++ b/docs/build-decentralized-apps/reference/04-development-frameworks.mdx @@ -3,6 +3,7 @@ title: 'Development frameworks' description: An overview of popular development frameworks that exist in the Arbitrum ecosystem user_story: As a developer, I want to understand the popular development frameworks that exist in the Arbitrum ecosystem. content_type: overview +displayed_sidebar: buildAppsSidebarFromGetStarted --- import KnowMoreToolsBox from '../../for-devs/partials/_know-more-tools-box-partial.mdx'; diff --git a/docs/build-decentralized-apps/reference/05-web3-libraries-tools.mdx b/docs/build-decentralized-apps/reference/05-web3-libraries-tools.mdx index c2073bdc64..cec005986f 100644 --- a/docs/build-decentralized-apps/reference/05-web3-libraries-tools.mdx +++ b/docs/build-decentralized-apps/reference/05-web3-libraries-tools.mdx @@ -3,6 +3,7 @@ title: 'Web3 libraries and tools' description: An overview of some popular Web3 libraries that help developers interact with the Ethereum and Arbitrum blockchains. user_story: As a developer, I want to understand what Web3 libraries and tools are available in the Ethereum and Arbitrum ecosystems. content_type: overview +displayed_sidebar: buildAppsSidebarFromGetStarted --- import KnowMoreToolsBox from '../../for-devs/partials/_know-more-tools-box-partial.mdx'; diff --git a/docs/build-decentralized-apps/reference/06-monitoring-tools-block-explorers.mdx b/docs/build-decentralized-apps/reference/06-monitoring-tools-block-explorers.mdx index ecbe319fba..96e646f404 100644 --- a/docs/build-decentralized-apps/reference/06-monitoring-tools-block-explorers.mdx +++ b/docs/build-decentralized-apps/reference/06-monitoring-tools-block-explorers.mdx @@ -3,6 +3,7 @@ title: 'Monitoring tools and block explorers' description: An overview of popular monitoring tools and block explorers that exist in the Arbitrum ecosystem user_story: As a developer, I want to understand what monitoring tools and block explorers are available in the Arbitrum ecosystem. content_type: overview +displayed_sidebar: buildAppsSidebarFromGetStarted --- import KnowMoreToolsBox from '../../for-devs/partials/_know-more-tools-box-partial.mdx'; diff --git a/docs/build-decentralized-apps/reference/07-debugging-tools.mdx b/docs/build-decentralized-apps/reference/07-debugging-tools.mdx index 9cded53979..b255a45d86 100644 --- a/docs/build-decentralized-apps/reference/07-debugging-tools.mdx +++ b/docs/build-decentralized-apps/reference/07-debugging-tools.mdx @@ -3,6 +3,7 @@ title: 'Debugging tools' description: An overview of popular debugging tools that exist in the Arbitrum ecosystem user_story: As a developer, I want to understand what debugging tools are available in the Arbitrum ecosystem. content_type: overview +displayed_sidebar: buildAppsSidebarFromGetStarted --- import KnowMoreToolsBox from '../../for-devs/partials/_know-more-tools-box-partial.mdx'; diff --git a/docs/build-decentralized-apps/reference/08-mainnet-risks.mdx b/docs/build-decentralized-apps/reference/08-mainnet-risks.mdx index 8a1ed096b6..9e7170a668 100644 --- a/docs/build-decentralized-apps/reference/08-mainnet-risks.mdx +++ b/docs/build-decentralized-apps/reference/08-mainnet-risks.mdx @@ -2,6 +2,7 @@ title: 'Arbitrum: Understanding the risks' description: 'Understand the risks associated with cutting-edge software development' author: dzgoldman +displayed_sidebar: buildAppsSidebarFromGetStarted --- # Arbitrum: Understanding the risks diff --git a/docs/build-decentralized-apps/token-bridging/01-overview.mdx b/docs/build-decentralized-apps/token-bridging/01-overview.mdx index 27f16b96ab..ee94d2de71 100644 --- a/docs/build-decentralized-apps/token-bridging/01-overview.mdx +++ b/docs/build-decentralized-apps/token-bridging/01-overview.mdx @@ -5,6 +5,7 @@ author: dzgoldman user_story: As a developer, I want to understand how the token bridge works and what options exist to bridge assets between layers. content_type: overview sidebar_position: 1 +displayed_sidebar: buildAppsSidebarFromGetStarted --- Token bridging is a fundamental aspect of any Layer 2 (child chain) protocol. Arbitrum uses its ability to pass messages between parent and child chains (see [Cross-chain messaging](/build-decentralized-apps/04-cross-chain-messaging.mdx)) to allow projects to trustlessly move assets from Ethereum to an Arbitrum chain and back. Any asset and asset type can in principle be bridged, including Ether, `ERC-20` tokens and `ERC-721` tokens among others. diff --git a/docs/build-decentralized-apps/token-bridging/02-token-bridge-ether.mdx b/docs/build-decentralized-apps/token-bridging/02-token-bridge-ether.mdx index bb98fae0ca..da4999b488 100644 --- a/docs/build-decentralized-apps/token-bridging/02-token-bridge-ether.mdx +++ b/docs/build-decentralized-apps/token-bridging/02-token-bridge-ether.mdx @@ -5,6 +5,7 @@ author: dzgoldman user_story: As a developer, I want to understand how bridging ether works on Arbitrum content_type: concept sidebar_position: 2 +displayed_sidebar: buildAppsSidebarFromGetStarted --- import ImageZoom from '@site/src/components/ImageZoom'; diff --git a/docs/build-decentralized-apps/token-bridging/03-token-bridge-erc20.mdx b/docs/build-decentralized-apps/token-bridging/03-token-bridge-erc20.mdx index 4a183c8778..3ec90b6774 100644 --- a/docs/build-decentralized-apps/token-bridging/03-token-bridge-erc20.mdx +++ b/docs/build-decentralized-apps/token-bridging/03-token-bridge-erc20.mdx @@ -5,6 +5,7 @@ author: dzgoldman user_story: As a developer, I want to understand how ERC-20 token bridging works on Arbitrum, and the architecture of the token bridge. content_type: concept sidebar_position: 3 +displayed_sidebar: buildAppsSidebarFromGetStarted --- import ImageZoom from '@site/src/components/ImageZoom'; diff --git a/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/01-get-started.mdx b/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/01-get-started.mdx index f44d070890..878544f23e 100644 --- a/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/01-get-started.mdx +++ b/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/01-get-started.mdx @@ -3,6 +3,7 @@ title: 'Get started with token bridging' description: Learn the different options available to bridge tokens programmatically user_story: As a developer, I want to understand how to bridge tokens between Ethereum and Arbitrum. content_type: overview +displayed_sidebar: buildAppsSidebarFromGetStarted --- Token bridging is a fundamental aspect of any child chain protocol. It allows projects to quickly integrate with the Arbitrum ecosystem by leveraging their existing parent chain tokens. diff --git a/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/02-how-to-bridge-tokens-standard.mdx b/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/02-how-to-bridge-tokens-standard.mdx index 6ffe855a39..c0d7da3631 100644 --- a/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/02-how-to-bridge-tokens-standard.mdx +++ b/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/02-how-to-bridge-tokens-standard.mdx @@ -3,6 +3,7 @@ title: "Bridge tokens via Arbitrum's standard `ERC-20` gateway" description: Learn how to programmatically bridge tokens between Ethereum and Arbitrum using Arbitrum’s standard ER-C20 gateway user_story: As a developer, I want to understand how to bridge tokens between Ethereum and Arbitrum using the standard ER-C20 gateway. content_type: how-to +displayed_sidebar: buildAppsSidebarFromGetStarted --- In this how-to you’ll learn how to bridge your own token between Ethereum (parent chain) and Arbitrum (child chain), using [Arbitrum’s standard `ERC20` gateway](/build-decentralized-apps/token-bridging/03-token-bridge-erc20.mdx#default-standard-bridging). For alternative ways of bridging tokens, don’t forget to check out this [overview](/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/01-get-started.mdx). diff --git a/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/03-how-to-bridge-tokens-generic-custom.mdx b/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/03-how-to-bridge-tokens-generic-custom.mdx index 387bf5a4ae..79411586ed 100644 --- a/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/03-how-to-bridge-tokens-generic-custom.mdx +++ b/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/03-how-to-bridge-tokens-generic-custom.mdx @@ -3,6 +3,7 @@ title: 'Bridge tokens via Arbitrum’s generic-custom gateway' description: Learn how to use the generic-custom gateway to bridge tokens programmatically user_story: As a developer, I want to understand how to bridge tokens between Ethereum and Arbitrum using the generic-custom gateway content_type: how-to +displayed_sidebar: buildAppsSidebarFromGetStarted --- In this how-to you’ll learn how to bridge your own token between Ethereum (parent chain) and Arbitrum (child chain), using [Arbitrum’s generic-custom gateway](/build-decentralized-apps/token-bridging/03-token-bridge-erc20.mdx#the-arbitrum-generic-custom-gateway). For alternative ways of bridging tokens, don’t forget to check out this [overview](/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/01-get-started.mdx). diff --git a/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/04-how-to-bridge-tokens-custom-gateway.mdx b/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/04-how-to-bridge-tokens-custom-gateway.mdx index 4c93202e77..b435de5645 100644 --- a/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/04-how-to-bridge-tokens-custom-gateway.mdx +++ b/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/04-how-to-bridge-tokens-custom-gateway.mdx @@ -3,6 +3,7 @@ title: 'How to bridge tokens via a custom gateway' description: Learn how to set up a custom gateway using Arbitrum's Token Bridge to bridge tokens programmatically reader_audience: developers who want to build on Ethereum/Arbitrum and bridge tokens between layers content_type: how-to +displayed_sidebar: buildAppsSidebarFromGetStarted --- :::caution Do you really need a custom gateway? diff --git a/docs/stylus/concepts/gas-metering.mdx b/docs/stylus/concepts/gas-metering.mdx index 95f6cacffa..a0164561c0 100644 --- a/docs/stylus/concepts/gas-metering.mdx +++ b/docs/stylus/concepts/gas-metering.mdx @@ -5,6 +5,7 @@ author: rachel-bousfield sme: rachel-bousfield target_audience: 'Developers deploying smart contracts using Stylus.' sidebar_position: 3 +displayed_sidebar: buildAppsSidebarFromStylus --- **Gas and ink** are the pricing primitives that are used to determine the cost of handling specific opcodes and host I/Os on Stylus. For an overview of specific opcode and host I/O costs, see [Gas and ink costs](/stylus/reference/opcode-hostio-pricing). diff --git a/docs/stylus/gentle-introduction.mdx b/docs/stylus/gentle-introduction.mdx index fa99de8296..ae6a4f8256 100644 --- a/docs/stylus/gentle-introduction.mdx +++ b/docs/stylus/gentle-introduction.mdx @@ -6,6 +6,7 @@ author: amarrazza sme: amarrazza target_audience: 'Developers who want to build on Arbitrum using popular programming languages, like Rust' sidebar_position: 1 +displayed_sidebar: buildAppsSidebarFromStylus --- import ImageZoom from '@site/src/components/ImageZoom'; diff --git a/docs/stylus/how-tos/adding-support-for-new-languages.mdx b/docs/stylus/how-tos/adding-support-for-new-languages.mdx index 5e078bb34a..f6efe6a2d5 100644 --- a/docs/stylus/how-tos/adding-support-for-new-languages.mdx +++ b/docs/stylus/how-tos/adding-support-for-new-languages.mdx @@ -6,6 +6,7 @@ sme: rauljordan target_audience: 'Developers deploying smart contracts using Stylus' content_type: how-to sidebar_position: 1 +displayed_sidebar: buildAppsSidebarFromStylus --- [Arbitrum Stylus](../gentle-introduction.mdx) is a new technology developed for Arbitrum chains which gives smart contract developers superpowers. With Stylus, developers can write EVM-compatible smart contracts in many different programming languages, and reap massive performance gains. Stylus slashes fees, with performance gains ranging from 10-70x, and memory efficiency gains as high as 100-500x. diff --git a/docs/stylus/how-tos/caching-contracts.mdx b/docs/stylus/how-tos/caching-contracts.mdx index 19cf1cd52c..b04127c701 100644 --- a/docs/stylus/how-tos/caching-contracts.mdx +++ b/docs/stylus/how-tos/caching-contracts.mdx @@ -5,6 +5,7 @@ description: 'A conceptual overview of the Stylus caching strategy and CacheMana sme: mahsa-moosavi target_audience: 'Developers deploying smart contracts using Stylus.' sidebar_position: 3 +displayed_sidebar: buildAppsSidebarFromStylus --- Stylus is designed for fast computation and efficiency. However, diff --git a/docs/stylus/how-tos/debugging-tx.mdx b/docs/stylus/how-tos/debugging-tx.mdx index df434169a7..f2b17322b4 100644 --- a/docs/stylus/how-tos/debugging-tx.mdx +++ b/docs/stylus/how-tos/debugging-tx.mdx @@ -7,6 +7,7 @@ sme: mahsamoosavi target_audience: 'Developers deploying smart contracts using Stylus' content_type: how-to sidebar_position: 2 +displayed_sidebar: buildAppsSidebarFromStylus --- Debugging smart contracts can be challenging, especially when dealing with complex transactions. The `cargo-stylus` crate simplifies the debugging process by allowing developers to replay Stylus transactions. This tool leverages GDB to provide an interactive debugging experience, enabling developers to set breakpoints, inspect state changes, and trace the execution flow step-by-step. This capability is crucial for identifying and resolving issues, ensuring that smart contracts function correctly and efficiently. diff --git a/docs/stylus/how-tos/optimizing-binaries.mdx b/docs/stylus/how-tos/optimizing-binaries.mdx index 10557c3b57..67b9cfbd88 100644 --- a/docs/stylus/how-tos/optimizing-binaries.mdx +++ b/docs/stylus/how-tos/optimizing-binaries.mdx @@ -6,6 +6,7 @@ sme: rauljordan target_audience: 'Developers deploying smart contracts using Stylus' content_type: how-to sidebar_position: 1 +displayed_sidebar: buildAppsSidebarFromStylus --- To be deployed onchain, the size of your **uncompressed WebAssembly (WASM) file** must not exceed 128Kb, while the **compressed binary** must not exceed 24KB. Stylus conforms with the same contract size limit as the EVM to remain fully interoperable with all smart contracts on Arbitrum chains. diff --git a/docs/stylus/how-tos/testing-contracts.mdx b/docs/stylus/how-tos/testing-contracts.mdx index b78656f0a1..1c3e98e405 100644 --- a/docs/stylus/how-tos/testing-contracts.mdx +++ b/docs/stylus/how-tos/testing-contracts.mdx @@ -5,6 +5,7 @@ description: 'A comprehensive guide to writing and running tests for Stylus smar sme: anegg0 target_audience: 'Developers writing smart contracts using Stylus.' sidebar_position: 3 +displayed_sidebar: buildAppsSidebarFromStylus --- import CustomDetails from '@site/src/components/CustomDetails'; diff --git a/docs/stylus/how-tos/using-constructors.mdx b/docs/stylus/how-tos/using-constructors.mdx index b06188edb2..8275417b69 100644 --- a/docs/stylus/how-tos/using-constructors.mdx +++ b/docs/stylus/how-tos/using-constructors.mdx @@ -7,6 +7,7 @@ sme: 'anegg0' user_story: 'As a Rust developer, I want to understand how to implement and use constructors in Stylus smart contracts' content_type: 'how-to' sidebar_position: 3 +displayed_sidebar: buildAppsSidebarFromStylus --- import CustomDetails from '@site/src/components/CustomDetails'; diff --git a/docs/stylus/how-tos/using-inheritance.mdx b/docs/stylus/how-tos/using-inheritance.mdx index 9735f30c4d..f3c4212f05 100644 --- a/docs/stylus/how-tos/using-inheritance.mdx +++ b/docs/stylus/how-tos/using-inheritance.mdx @@ -5,6 +5,7 @@ author: anegg0 sme: mahsamoosavi content_type: how-to sidebar_position: 1 +displayed_sidebar: buildAppsSidebarFromStylus --- import CustomDetails from '@site/src/components/CustomDetails'; diff --git a/docs/stylus/how-tos/verifying-contracts-arbiscan.mdx b/docs/stylus/how-tos/verifying-contracts-arbiscan.mdx index 6eaaa7717e..6d485e4196 100644 --- a/docs/stylus/how-tos/verifying-contracts-arbiscan.mdx +++ b/docs/stylus/how-tos/verifying-contracts-arbiscan.mdx @@ -6,6 +6,7 @@ sme: mahsamoosavi target_audience: 'Developers deploying smart contracts using Stylus' content_type: how-to sidebar_position: 1 +displayed_sidebar: buildAppsSidebarFromStylus --- import ImageZoom from '@site/src/components/ImageZoom'; diff --git a/docs/stylus/how-tos/verifying-contracts.mdx b/docs/stylus/how-tos/verifying-contracts.mdx index d17f0e5b35..acbc706610 100644 --- a/docs/stylus/how-tos/verifying-contracts.mdx +++ b/docs/stylus/how-tos/verifying-contracts.mdx @@ -8,6 +8,7 @@ target_audience: 'Developers learning how to use Stylus' sidebar_position: 2 user_story: 'As a current or prospective Stylus user, I want to learn how to make sure my Stylus contracts deployment are reproducible by anyone running the same environment.' content_type: how-to +displayed_sidebar: buildAppsSidebarFromStylus --- :::caution diff --git a/docs/stylus/overview.mdx b/docs/stylus/overview.mdx index 69af1443da..d1a519fb9b 100644 --- a/docs/stylus/overview.mdx +++ b/docs/stylus/overview.mdx @@ -2,6 +2,7 @@ id: stylus-overview title: Write Stylus Contracts sidebar_label: Write Stylus contracts +displayed_sidebar: buildAppsSidebarFromStylus --- import Card from '@site/src/components/Cards/Card'; diff --git a/docs/stylus/quickstart.mdx b/docs/stylus/quickstart.mdx index c605881e43..91c3703c74 100644 --- a/docs/stylus/quickstart.mdx +++ b/docs/stylus/quickstart.mdx @@ -6,6 +6,7 @@ author: chrisco512, anegg0 sme: chrisco512, anegg0 sidebar_position: 2 target_audience: Developers writing Stylus contracts in Rust using Stylus +displayed_sidebar: buildAppsSidebarFromStylus --- import ImageZoom from '@site/src/components/ImageZoom'; diff --git a/docs/stylus/reference/project-structure.mdx b/docs/stylus/reference/project-structure.mdx index 29542e0c4e..d4d9c28065 100644 --- a/docs/stylus/reference/project-structure.mdx +++ b/docs/stylus/reference/project-structure.mdx @@ -5,6 +5,7 @@ author: chrisco sme: chrisco sidebar_position: 1 target_audience: Developers using the Stylus Rust SDK to write and deploy smart contracts. +displayed_sidebar: buildAppsSidebarFromStylus --- Contracts in Rust are similar to contracts in Solidity. Each contract can contain declarations of State Variables, Functions, Function Modifiers, Events, Errors, Struct Types, and Enum Types. In addition, Rust contracts can import third-party packages from [crates.io](https://crates.io) as dependencies and use them for advanced functionality. diff --git a/docs/stylus/stylus-content-map.mdx b/docs/stylus/stylus-content-map.mdx index 04f9fa60f8..ff9874f23c 100644 --- a/docs/stylus/stylus-content-map.mdx +++ b/docs/stylus/stylus-content-map.mdx @@ -2,6 +2,7 @@ id: stylus-content-map title: Write Stylus Contracts sidebar_label: Write Stylus contracts +displayed_sidebar: buildAppsSidebarFromStylus --- import Card from '@site/src/components/Cards/Card'; diff --git a/docs/stylus/using-cli.mdx b/docs/stylus/using-cli.mdx index 87e3573ea7..3df13a69bc 100644 --- a/docs/stylus/using-cli.mdx +++ b/docs/stylus/using-cli.mdx @@ -6,6 +6,7 @@ author: 'anegg0' sme: 'anegg0' sidebar_position: 2 target_audience: Developers writing Stylus contracts in Rust using Stylus +displayed_sidebar: buildAppsSidebarFromStylus --- This guide will get you started using [cargo stylus](https://github.com/OffchainLabs/cargo-stylus), a CLI toolkit to help developers manage, compile, deploy, and optimize their Stylus contracts efficiently. diff --git a/docusaurus.config.js b/docusaurus.config.js index 45cdbccab8..d218c7a0f0 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -261,7 +261,7 @@ const config = { position: 'right', items: [ { - label: 'Quickstart', + label: 'Get started', to: '/build-decentralized-apps/quickstart-solidity-remix', }, { diff --git a/sidebars.js b/sidebars.js index 535d5d689c..4a6ccd5ed9 100644 --- a/sidebars.js +++ b/sidebars.js @@ -1241,6 +1241,1188 @@ const sidebars = { }, ], + // Unified build apps sidebar (combines build dApps and Stylus) + buildAppsSidebar: [ + { + type: 'category', + label: 'Build decentralized apps', + collapsed: false, + items: [ + { + type: 'doc', + id: 'build-decentralized-apps/quickstart-solidity-remix', + label: 'Quickstart (Solidity)', + }, + { + type: 'doc', + label: 'Estimate gas', + id: 'build-decentralized-apps/how-to-estimate-gas', + }, + { + type: 'doc', + label: 'Chains and testnets', + id: 'build-decentralized-apps/public-chains', + }, + { + type: 'doc', + label: 'Cross-chain messaging', + id: 'build-decentralized-apps/cross-chain-messaging', + }, + { + type: 'doc', + id: 'build-decentralized-apps/custom-gas-token-sdk', + label: 'Custom gas token SDK', + }, + { + type: 'category', + label: 'Arbitrum vs Ethereum', + items: [ + { + type: 'doc', + label: 'Comparison overview', + id: 'build-decentralized-apps/arbitrum-vs-ethereum/comparison-overview', + }, + { + type: 'doc', + label: 'Block gas limit, numbers and time', + id: 'build-decentralized-apps/arbitrum-vs-ethereum/block-numbers-and-time', + }, + { + type: 'doc', + label: 'RPC methods', + id: 'build-decentralized-apps/arbitrum-vs-ethereum/rpc-methods', + }, + { + type: 'doc', + label: 'Solidity support', + id: 'build-decentralized-apps/arbitrum-vs-ethereum/solidity-support', + }, + ], + }, + { + type: 'doc', + label: 'Oracles', + id: 'build-decentralized-apps/oracles/overview-oracles', + }, + { + type: 'category', + label: 'Precompiles', + collapsed: true, + items: [ + { + type: 'doc', + label: 'Overview', + id: 'build-decentralized-apps/precompiles/overview', + }, + { + type: 'doc', + label: 'Reference', + id: 'build-decentralized-apps/precompiles/reference', + }, + ], + }, + { + type: 'category', + label: 'NodeInterface', + collapsed: true, + items: [ + { + type: 'doc', + label: 'Overview', + id: 'build-decentralized-apps/nodeinterface/overview', + }, + { + type: 'doc', + label: 'Reference', + id: 'build-decentralized-apps/nodeinterface/reference', + }, + ], + }, + { + type: 'category', + label: 'Token bridging', + collapsed: true, + items: [ + { + type: 'doc', + label: 'Overview', + id: 'build-decentralized-apps/token-bridging/overview', + }, + { + type: 'doc', + label: 'ETH bridging', + id: 'build-decentralized-apps/token-bridging/token-bridge-ether', + }, + { + type: 'doc', + label: 'ERC-20 token bridging', + id: 'build-decentralized-apps/token-bridging/token-bridge-erc20', + }, + { + type: 'category', + label: 'Bridge tokens programmatically', + items: [ + { + type: 'doc', + label: 'Get started', + id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/get-started', + }, + { + type: 'doc', + label: 'Use the standard gateway', + id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-standard', + }, + { + type: 'doc', + label: 'Use the generic-custom gateway', + id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-generic-custom', + }, + { + type: 'doc', + label: 'Use the custom gateway', + id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-custom-gateway', + }, + ], + }, + ], + }, + { + type: 'category', + label: 'Reference', + items: [ + { + type: 'doc', + id: 'build-decentralized-apps/reference/node-providers', + label: 'RPC endpoints and providers', + }, + { + type: 'doc', + label: 'Smart contract addresses', + id: 'build-decentralized-apps/reference/contract-addresses', + }, + { + type: 'doc', + label: 'Chain parameters', + id: 'build-decentralized-apps/reference/chain-params', + }, + { + type: 'doc', + label: 'Development frameworks', + id: 'build-decentralized-apps/reference/development-frameworks', + }, + { + type: 'doc', + label: 'Web3 libraries and tools', + id: 'build-decentralized-apps/reference/web3-libraries-tools', + }, + { + type: 'doc', + label: 'Monitoring tools and block explorers', + id: 'build-decentralized-apps/reference/monitoring-tools-block-explorers', + }, + { + type: 'doc', + label: 'Debugging tools', + id: 'build-decentralized-apps/reference/debugging-tools', + }, + + { + type: 'doc', + id: 'build-decentralized-apps/reference/mainnet-risks', + label: 'Mainnet risks', + }, + ], + }, + { + type: 'doc', + label: 'Troubleshooting', + id: 'for-devs/troubleshooting-building', + }, + { + type: 'category', + label: 'Arbitrum SDK', + items: sdkSidebar.sdkSidebar, + }, + { + type: 'link', + label: 'Tutorials', + href: 'https://github.com/OffchainLabs/arbitrum-tutorials', + }, + ], + }, + { + type: 'category', + label: 'Write Stylus contracts', + collapsed: true, + link: { + type: 'doc', + id: 'stylus/stylus-content-map', + }, + items: [ + { + type: 'doc', + id: 'stylus/gentle-introduction', + label: 'A gentle introduction', + }, + { + type: 'doc', + id: 'stylus/quickstart', + label: 'Quickstart', + }, + { + type: 'category', + label: 'Rust SDK', + collapsed: true, + items: [ + { + type: 'doc', + id: 'stylus/reference/overview', + label: 'Overview', + }, + { + type: 'doc', + id: 'stylus/reference/project-structure', + label: 'Structure of a Contract', + }, + ...stylusByExampleBasicExamples, + { + type: 'doc', + id: 'stylus/how-tos/using-inheritance', + label: 'Composition and trait-based routing model', + }, + { + type: 'doc', + id: 'stylus/reference/rust-sdk-guide', + label: 'Advanced features', + }, + { + type: 'doc', + id: 'stylus/recommended-libraries', + label: 'Recommended Rust Crates', + }, + ], + }, + { + type: 'category', + label: 'Rust CLI', + collapsed: true, + items: [ + { + type: 'doc', + id: 'stylus/using-cli', + label: 'Overview', + }, + { + type: 'doc', + id: 'stylus/how-tos/debugging-tx', + label: 'Debug transactions', + }, + { + type: 'doc', + id: 'stylus/how-tos/testing-contracts', + label: 'Testing contracts', + }, + { + type: 'doc', + id: 'stylus/how-tos/verifying-contracts', + label: 'Verify contracts', + }, + { + type: 'doc', + id: 'stylus/how-tos/caching-contracts', + label: 'Cache contracts', + }, + { + type: 'doc', + id: 'stylus/how-tos/verifying-contracts-arbiscan', + label: 'Verify on Arbiscan', + }, + { + type: 'doc', + id: 'stylus/how-tos/optimizing-binaries', + label: 'Optimize WASM binaries', + }, + ], + }, + { + type: 'html', + value: + 'Run a local dev node', + }, + { + type: 'category', + label: 'Concepts', + collapsed: true, + items: [ + { + type: 'doc', + id: 'stylus/concepts/how-it-works', + label: 'Architecture overview', + }, + { + type: 'doc', + id: 'stylus/concepts/gas-metering', + label: 'Gas metering', + }, + ], + }, + { + type: 'category', + label: 'Examples', + collapsed: true, + items: [ + ...stylusByExampleApplications, + { + type: 'link', + label: 'Awesome Stylus', + href: 'https://github.com/OffchainLabs/awesome-stylus', + }, + ], + }, + { + type: 'category', + label: 'Reference', + collapsed: true, + items: [ + { + type: 'html', + value: + 'Chain Info', + }, + { + type: 'doc', + id: 'stylus/reference/opcode-hostio-pricing', + label: 'Gas & Ink Pricing', + }, + { + type: 'link', + label: 'Stylus by Example', + href: 'https://stylus-by-example.org/', + }, + { + type: 'link', + label: 'Cargo Stylus CLI GitHub', + href: 'https://github.com/OffchainLabs/cargo-stylus', + }, + { + type: 'link', + label: 'Rust SDK Crate', + href: 'https://docs.rs/stylus-sdk/latest/stylus_sdk/index.html', + }, + { + type: 'link', + label: 'Source Code Repository', + href: 'https://github.com/OffchainLabs/stylus', + }, + ], + }, + { + type: 'doc', + id: 'stylus/how-tos/adding-support-for-new-languages', + label: 'Using other languages', + }, + { + type: 'doc', + id: 'stylus/troubleshooting-building-stylus', + label: 'Troubleshooting', + }, + ], + }, + { + type: 'doc', + id: 'for-devs/dev-tools-and-resources/chain-info', + label: 'Chain info', + }, + ], + + // Build apps sidebar optimized for Get Started entry point (Build decentralized apps expanded, Stylus collapsed) + buildAppsSidebarFromGetStarted: [ + { + type: 'category', + label: 'Build decentralized apps', + collapsed: false, + items: [ + { + type: 'doc', + id: 'build-decentralized-apps/quickstart-solidity-remix', + label: 'Quickstart (Solidity)', + }, + { + type: 'doc', + label: 'Estimate gas', + id: 'build-decentralized-apps/how-to-estimate-gas', + }, + { + type: 'doc', + label: 'Chains and testnets', + id: 'build-decentralized-apps/public-chains', + }, + { + type: 'doc', + label: 'Cross-chain messaging', + id: 'build-decentralized-apps/cross-chain-messaging', + }, + { + type: 'doc', + id: 'build-decentralized-apps/custom-gas-token-sdk', + label: 'Custom gas token SDK', + }, + { + type: 'category', + label: 'Arbitrum vs Ethereum', + items: [ + { + type: 'doc', + label: 'Comparison overview', + id: 'build-decentralized-apps/arbitrum-vs-ethereum/comparison-overview', + }, + { + type: 'doc', + label: 'Block gas limit, numbers and time', + id: 'build-decentralized-apps/arbitrum-vs-ethereum/block-numbers-and-time', + }, + { + type: 'doc', + label: 'RPC methods', + id: 'build-decentralized-apps/arbitrum-vs-ethereum/rpc-methods', + }, + { + type: 'doc', + label: 'Solidity support', + id: 'build-decentralized-apps/arbitrum-vs-ethereum/solidity-support', + }, + ], + }, + { + type: 'doc', + label: 'Oracles', + id: 'build-decentralized-apps/oracles/overview-oracles', + }, + { + type: 'category', + label: 'Precompiles', + collapsed: true, + items: [ + { + type: 'doc', + label: 'Overview', + id: 'build-decentralized-apps/precompiles/overview', + }, + { + type: 'doc', + label: 'Reference', + id: 'build-decentralized-apps/precompiles/reference', + }, + ], + }, + { + type: 'category', + label: 'NodeInterface', + collapsed: true, + items: [ + { + type: 'doc', + label: 'Overview', + id: 'build-decentralized-apps/nodeinterface/overview', + }, + { + type: 'doc', + label: 'Reference', + id: 'build-decentralized-apps/nodeinterface/reference', + }, + ], + }, + { + type: 'category', + label: 'Token bridging', + collapsed: true, + items: [ + { + type: 'doc', + label: 'Overview', + id: 'build-decentralized-apps/token-bridging/overview', + }, + { + type: 'doc', + label: 'ETH bridging', + id: 'build-decentralized-apps/token-bridging/token-bridge-ether', + }, + { + type: 'doc', + label: 'ERC-20 token bridging', + id: 'build-decentralized-apps/token-bridging/token-bridge-erc20', + }, + { + type: 'category', + label: 'Bridge tokens programmatically', + items: [ + { + type: 'doc', + label: 'Get started', + id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/get-started', + }, + { + type: 'doc', + label: 'Use the standard gateway', + id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-standard', + }, + { + type: 'doc', + label: 'Use the generic-custom gateway', + id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-generic-custom', + }, + { + type: 'doc', + label: 'Use the custom gateway', + id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-custom-gateway', + }, + ], + }, + ], + }, + { + type: 'category', + label: 'Reference', + items: [ + { + type: 'doc', + id: 'build-decentralized-apps/reference/node-providers', + label: 'RPC endpoints and providers', + }, + { + type: 'doc', + label: 'Smart contract addresses', + id: 'build-decentralized-apps/reference/contract-addresses', + }, + { + type: 'doc', + label: 'Chain parameters', + id: 'build-decentralized-apps/reference/chain-params', + }, + { + type: 'doc', + label: 'Development frameworks', + id: 'build-decentralized-apps/reference/development-frameworks', + }, + { + type: 'doc', + label: 'Web3 libraries and tools', + id: 'build-decentralized-apps/reference/web3-libraries-tools', + }, + { + type: 'doc', + label: 'Monitoring tools and block explorers', + id: 'build-decentralized-apps/reference/monitoring-tools-block-explorers', + }, + { + type: 'doc', + label: 'Debugging tools', + id: 'build-decentralized-apps/reference/debugging-tools', + }, + + { + type: 'doc', + id: 'build-decentralized-apps/reference/mainnet-risks', + label: 'Mainnet risks', + }, + ], + }, + { + type: 'doc', + label: 'Troubleshooting', + id: 'for-devs/troubleshooting-building', + }, + { + type: 'category', + label: 'Arbitrum SDK', + items: sdkSidebar.sdkSidebar, + }, + { + type: 'link', + label: 'Tutorials', + href: 'https://github.com/OffchainLabs/arbitrum-tutorials', + }, + ], + }, + { + type: 'category', + label: 'Write Stylus contracts', + collapsed: true, + link: { + type: 'doc', + id: 'stylus/stylus-content-map', + }, + items: [ + { + type: 'doc', + id: 'stylus/gentle-introduction', + label: 'A gentle introduction', + }, + { + type: 'doc', + id: 'stylus/quickstart', + label: 'Quickstart', + }, + { + type: 'category', + label: 'Rust SDK', + collapsed: true, + items: [ + { + type: 'doc', + id: 'stylus/reference/overview', + label: 'Overview', + }, + { + type: 'doc', + id: 'stylus/reference/project-structure', + label: 'Structure of a Contract', + }, + ...stylusByExampleBasicExamples, + { + type: 'doc', + id: 'stylus/how-tos/using-inheritance', + label: 'Composition and trait-based routing model', + }, + { + type: 'doc', + id: 'stylus/reference/rust-sdk-guide', + label: 'Advanced features', + }, + { + type: 'doc', + id: 'stylus/recommended-libraries', + label: 'Recommended Rust Crates', + }, + ], + }, + { + type: 'category', + label: 'Rust CLI', + collapsed: true, + items: [ + { + type: 'doc', + id: 'stylus/using-cli', + label: 'Overview', + }, + { + type: 'doc', + id: 'stylus/how-tos/debugging-tx', + label: 'Debug transactions', + }, + { + type: 'doc', + id: 'stylus/how-tos/testing-contracts', + label: 'Testing contracts', + }, + { + type: 'doc', + id: 'stylus/how-tos/verifying-contracts', + label: 'Verify contracts', + }, + { + type: 'doc', + id: 'stylus/how-tos/caching-contracts', + label: 'Cache contracts', + }, + { + type: 'doc', + id: 'stylus/how-tos/verifying-contracts-arbiscan', + label: 'Verify on Arbiscan', + }, + { + type: 'doc', + id: 'stylus/how-tos/optimizing-binaries', + label: 'Optimize WASM binaries', + }, + ], + }, + { + type: 'html', + value: + 'Run a local dev node', + }, + { + type: 'category', + label: 'Concepts', + collapsed: true, + items: [ + { + type: 'doc', + id: 'stylus/concepts/how-it-works', + label: 'Architecture overview', + }, + { + type: 'doc', + id: 'stylus/concepts/gas-metering', + label: 'Gas metering', + }, + ], + }, + { + type: 'category', + label: 'Examples', + collapsed: true, + items: [ + ...stylusByExampleApplications, + { + type: 'link', + label: 'Awesome Stylus', + href: 'https://github.com/OffchainLabs/awesome-stylus', + }, + ], + }, + { + type: 'category', + label: 'Reference', + collapsed: true, + items: [ + { + type: 'html', + value: + 'Chain Info', + }, + { + type: 'doc', + id: 'stylus/reference/opcode-hostio-pricing', + label: 'Gas & Ink Pricing', + }, + { + type: 'link', + label: 'Stylus by Example', + href: 'https://stylus-by-example.org/', + }, + { + type: 'link', + label: 'Cargo Stylus CLI GitHub', + href: 'https://github.com/OffchainLabs/cargo-stylus', + }, + { + type: 'link', + label: 'Rust SDK Crate', + href: 'https://docs.rs/stylus-sdk/latest/stylus_sdk/index.html', + }, + { + type: 'link', + label: 'Source Code Repository', + href: 'https://github.com/OffchainLabs/stylus', + }, + ], + }, + { + type: 'doc', + id: 'stylus/how-tos/adding-support-for-new-languages', + label: 'Using other languages', + }, + { + type: 'doc', + id: 'stylus/troubleshooting-building-stylus', + label: 'Troubleshooting', + }, + ], + }, + { + type: 'doc', + id: 'for-devs/dev-tools-and-resources/chain-info', + label: 'Chain info', + }, + ], + + // Build apps sidebar optimized for Stylus entry point (Stylus expanded, Build decentralized apps collapsed) + buildAppsSidebarFromStylus: [ + { + type: 'category', + label: 'Build decentralized apps', + collapsed: true, + items: [ + { + type: 'doc', + id: 'build-decentralized-apps/quickstart-solidity-remix', + label: 'Quickstart (Solidity)', + }, + { + type: 'doc', + label: 'Estimate gas', + id: 'build-decentralized-apps/how-to-estimate-gas', + }, + { + type: 'doc', + label: 'Chains and testnets', + id: 'build-decentralized-apps/public-chains', + }, + { + type: 'doc', + label: 'Cross-chain messaging', + id: 'build-decentralized-apps/cross-chain-messaging', + }, + { + type: 'doc', + id: 'build-decentralized-apps/custom-gas-token-sdk', + label: 'Custom gas token SDK', + }, + { + type: 'category', + label: 'Arbitrum vs Ethereum', + items: [ + { + type: 'doc', + label: 'Comparison overview', + id: 'build-decentralized-apps/arbitrum-vs-ethereum/comparison-overview', + }, + { + type: 'doc', + label: 'Block gas limit, numbers and time', + id: 'build-decentralized-apps/arbitrum-vs-ethereum/block-numbers-and-time', + }, + { + type: 'doc', + label: 'RPC methods', + id: 'build-decentralized-apps/arbitrum-vs-ethereum/rpc-methods', + }, + { + type: 'doc', + label: 'Solidity support', + id: 'build-decentralized-apps/arbitrum-vs-ethereum/solidity-support', + }, + ], + }, + { + type: 'doc', + label: 'Oracles', + id: 'build-decentralized-apps/oracles/overview-oracles', + }, + { + type: 'category', + label: 'Precompiles', + collapsed: true, + items: [ + { + type: 'doc', + label: 'Overview', + id: 'build-decentralized-apps/precompiles/overview', + }, + { + type: 'doc', + label: 'Reference', + id: 'build-decentralized-apps/precompiles/reference', + }, + ], + }, + { + type: 'category', + label: 'NodeInterface', + collapsed: true, + items: [ + { + type: 'doc', + label: 'Overview', + id: 'build-decentralized-apps/nodeinterface/overview', + }, + { + type: 'doc', + label: 'Reference', + id: 'build-decentralized-apps/nodeinterface/reference', + }, + ], + }, + { + type: 'category', + label: 'Token bridging', + collapsed: true, + items: [ + { + type: 'doc', + label: 'Overview', + id: 'build-decentralized-apps/token-bridging/overview', + }, + { + type: 'doc', + label: 'ETH bridging', + id: 'build-decentralized-apps/token-bridging/token-bridge-ether', + }, + { + type: 'doc', + label: 'ERC-20 token bridging', + id: 'build-decentralized-apps/token-bridging/token-bridge-erc20', + }, + { + type: 'category', + label: 'Bridge tokens programmatically', + items: [ + { + type: 'doc', + label: 'Get started', + id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/get-started', + }, + { + type: 'doc', + label: 'Use the standard gateway', + id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-standard', + }, + { + type: 'doc', + label: 'Use the generic-custom gateway', + id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-generic-custom', + }, + { + type: 'doc', + label: 'Use the custom gateway', + id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-custom-gateway', + }, + ], + }, + ], + }, + { + type: 'category', + label: 'Reference', + items: [ + { + type: 'doc', + id: 'build-decentralized-apps/reference/node-providers', + label: 'RPC endpoints and providers', + }, + { + type: 'doc', + label: 'Smart contract addresses', + id: 'build-decentralized-apps/reference/contract-addresses', + }, + { + type: 'doc', + label: 'Chain parameters', + id: 'build-decentralized-apps/reference/chain-params', + }, + { + type: 'doc', + label: 'Development frameworks', + id: 'build-decentralized-apps/reference/development-frameworks', + }, + { + type: 'doc', + label: 'Web3 libraries and tools', + id: 'build-decentralized-apps/reference/web3-libraries-tools', + }, + { + type: 'doc', + label: 'Monitoring tools and block explorers', + id: 'build-decentralized-apps/reference/monitoring-tools-block-explorers', + }, + { + type: 'doc', + label: 'Debugging tools', + id: 'build-decentralized-apps/reference/debugging-tools', + }, + + { + type: 'doc', + id: 'build-decentralized-apps/reference/mainnet-risks', + label: 'Mainnet risks', + }, + ], + }, + { + type: 'doc', + label: 'Troubleshooting', + id: 'for-devs/troubleshooting-building', + }, + { + type: 'category', + label: 'Arbitrum SDK', + items: sdkSidebar.sdkSidebar, + }, + { + type: 'link', + label: 'Tutorials', + href: 'https://github.com/OffchainLabs/arbitrum-tutorials', + }, + ], + }, + { + type: 'category', + label: 'Write Stylus contracts', + collapsed: false, + link: { + type: 'doc', + id: 'stylus/stylus-content-map', + }, + items: [ + { + type: 'doc', + id: 'stylus/gentle-introduction', + label: 'A gentle introduction', + }, + { + type: 'doc', + id: 'stylus/quickstart', + label: 'Quickstart', + }, + { + type: 'category', + label: 'Rust SDK', + collapsed: true, + items: [ + { + type: 'doc', + id: 'stylus/reference/overview', + label: 'Overview', + }, + { + type: 'doc', + id: 'stylus/reference/project-structure', + label: 'Structure of a Contract', + }, + ...stylusByExampleBasicExamples, + { + type: 'doc', + id: 'stylus/how-tos/using-inheritance', + label: 'Composition and trait-based routing model', + }, + { + type: 'doc', + id: 'stylus/reference/rust-sdk-guide', + label: 'Advanced features', + }, + { + type: 'doc', + id: 'stylus/recommended-libraries', + label: 'Recommended Rust Crates', + }, + ], + }, + { + type: 'category', + label: 'Rust CLI', + collapsed: true, + items: [ + { + type: 'doc', + id: 'stylus/using-cli', + label: 'Overview', + }, + { + type: 'doc', + id: 'stylus/how-tos/debugging-tx', + label: 'Debug transactions', + }, + { + type: 'doc', + id: 'stylus/how-tos/testing-contracts', + label: 'Testing contracts', + }, + { + type: 'doc', + id: 'stylus/how-tos/verifying-contracts', + label: 'Verify contracts', + }, + { + type: 'doc', + id: 'stylus/how-tos/caching-contracts', + label: 'Cache contracts', + }, + { + type: 'doc', + id: 'stylus/how-tos/verifying-contracts-arbiscan', + label: 'Verify on Arbiscan', + }, + { + type: 'doc', + id: 'stylus/how-tos/optimizing-binaries', + label: 'Optimize WASM binaries', + }, + ], + }, + { + type: 'html', + value: + 'Run a local dev node', + }, + { + type: 'category', + label: 'Concepts', + collapsed: true, + items: [ + { + type: 'doc', + id: 'stylus/concepts/how-it-works', + label: 'Architecture overview', + }, + { + type: 'doc', + id: 'stylus/concepts/gas-metering', + label: 'Gas metering', + }, + ], + }, + { + type: 'category', + label: 'Examples', + collapsed: true, + items: [ + ...stylusByExampleApplications, + { + type: 'link', + label: 'Awesome Stylus', + href: 'https://github.com/OffchainLabs/awesome-stylus', + }, + ], + }, + { + type: 'category', + label: 'Reference', + collapsed: true, + items: [ + { + type: 'html', + value: + 'Chain Info', + }, + { + type: 'doc', + id: 'stylus/reference/opcode-hostio-pricing', + label: 'Gas & Ink Pricing', + }, + { + type: 'link', + label: 'Stylus by Example', + href: 'https://stylus-by-example.org/', + }, + { + type: 'link', + label: 'Cargo Stylus CLI GitHub', + href: 'https://github.com/OffchainLabs/cargo-stylus', + }, + { + type: 'link', + label: 'Rust SDK Crate', + href: 'https://docs.rs/stylus-sdk/latest/stylus_sdk/index.html', + }, + { + type: 'link', + label: 'Source Code Repository', + href: 'https://github.com/OffchainLabs/stylus', + }, + ], + }, + { + type: 'doc', + id: 'stylus/how-tos/adding-support-for-new-languages', + label: 'Using other languages', + }, + { + type: 'doc', + id: 'stylus/troubleshooting-building-stylus', + label: 'Troubleshooting', + }, + ], + }, + { + type: 'doc', + id: 'for-devs/dev-tools-and-resources/chain-info', + label: 'Chain info', + }, + ], + // Additional resources sidebar additionalResourcesSidebar: [ { From 231b6cce402c1ec3793c06c7b7c65b4dc7fc8d91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Wed, 17 Sep 2025 06:00:36 -0700 Subject: [PATCH 13/62] fix scripts/stylusByExampleDocsHandler.ts formatting and typos output --- scripts/stylusByExampleDocsHandler.ts | 122 +++++++++++++++++++++----- 1 file changed, 99 insertions(+), 23 deletions(-) diff --git a/scripts/stylusByExampleDocsHandler.ts b/scripts/stylusByExampleDocsHandler.ts index 0c650052ed..ecd0113c2f 100644 --- a/scripts/stylusByExampleDocsHandler.ts +++ b/scripts/stylusByExampleDocsHandler.ts @@ -2,6 +2,7 @@ const fs = require('fs'); const path = require('path'); const { RendererEvent } = require('typedoc'); const { parseMarkdownContentTitle } = require('@docusaurus/utils'); +const prettier = require('prettier'); const allowList = [ 'hello_world', @@ -49,15 +50,8 @@ function load(app) { const basicExamplesSidebarConfig = { items: basicExamplesSidebarItems }; const basicExamplesSidebarPath = path.join(basicExamplesOutputDir, 'sidebar.js'); - fs.writeFileSync( - basicExamplesSidebarPath, - `// @ts-check\n/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */\nconst sidebar = ${JSON.stringify( - basicExamplesSidebarConfig, - null, - 2, - )};\nmodule.exports = sidebar.items;`, - 'utf8', - ); + const basicSidebarContent = formatSidebarFile(basicExamplesSidebarConfig, basicExamplesSidebarPath); + fs.writeFileSync(basicExamplesSidebarPath, basicSidebarContent, 'utf8'); // Copy applications into their directory copyFiles(sourceDirApplications, applicationsOutputDir, appsAllowList); @@ -67,15 +61,8 @@ function load(app) { const applicationsSidebarConfig = { items: applicationsSidebarItems }; const applicationsSidebarPath = path.join(applicationsOutputDir, 'sidebar.js'); - fs.writeFileSync( - applicationsSidebarPath, - `// @ts-check\n/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */\nconst sidebar = ${JSON.stringify( - applicationsSidebarConfig, - null, - 2, - )};\nmodule.exports = sidebar.items;`, - 'utf8', - ); + const appsSidebarContent = formatSidebarFile(applicationsSidebarConfig, applicationsSidebarPath); + fs.writeFileSync(applicationsSidebarPath, appsSidebarContent, 'utf8'); }); } @@ -134,7 +121,8 @@ function copyFiles(source, target, allowList) { } else if (entry.name === 'page.mdx') { const content = fs.readFileSync(sourcePath, 'utf8'); const convertedContent = convertMetadataToFrontmatter(content); - fs.writeFileSync(path.join(newTargetPath, entry.name), convertedContent); + const validatedContent = validateContent(convertedContent, sourcePath); + fs.writeFileSync(path.join(newTargetPath, entry.name), validatedContent); } }); } else { @@ -149,8 +137,9 @@ function copyFiles(source, target, allowList) { const newFileName = `${parentDirName}.mdx`; const content = fs.readFileSync(sourcePath, 'utf8'); const convertedContent = convertMetadataToFrontmatter(content); + const validatedContent = validateContent(convertedContent, sourcePath); const convertedContentWithBanner = - addAdmonitionOneLineAboveFirstCodeBlock(convertedContent); + addAdmonitionOneLineAboveFirstCodeBlock(validatedContent); fs.writeFileSync(path.join(targetPath, newFileName), convertedContentWithBanner); } } @@ -171,12 +160,17 @@ function addAdmonitionOneLineAboveFirstCodeBlock(content) { const index = content.indexOf(firstCodeBlock); if (index === -1) { console.log('firstCodeBlock not found'); - return; + return content; // Return original content instead of undefined } // Find the position two lines before firstCodeBlock const lines = content.substring(0, index).split('\n'); - const insertLineIndex = lines.length - 2; + if (lines.length < 2) { + console.warn('Not enough lines before code block for banner insertion'); + return content; + } + + const insertLineIndex = Math.max(0, lines.length - 2); lines.splice(insertLineIndex, 0, admonitionNotForProduction); const newText = lines.join('\n') + content.substring(index); @@ -190,7 +184,27 @@ function convertMetadataToFrontmatter(content) { if (match) { let metadataObj; try { - metadataObj = eval(`(${match[1]})`); + // Safer parsing without eval() + // First, try to parse as a JavaScript object literal + const metadataString = match[1] + .trim() + // Handle single quotes to double quotes + .replace(/'/g, '"') + // Handle unquoted keys + .replace(/(\w+):/g, '"$1":') + // Handle template literals (basic conversion) + .replace(/`([^`]*)`/g, '"$1"') + // Clean up any double-double quotes + .replace(/""/g, '"'); + + try { + metadataObj = JSON.parse(metadataString); + } catch (jsonError) { + // If JSON parsing fails, try a more lenient approach + // Create a Function constructor (safer than eval but still evaluate the code) + const func = new Function('return ' + match[1]); + metadataObj = func(); + } } catch (error) { console.error('Error parsing metadata:', error); return content; @@ -292,4 +306,66 @@ function getTitleFromFileContent(filePath) { return contentTitle || ''; } +/** + * Validates and fixes common typos in content + */ +function validateContent(content, filePath) { + // Common typos to fix + const commonTypos = { + 'followinge': 'following', + 'recieve': 'receive', + 'occured': 'occurred', + 'seperate': 'separate', + 'definately': 'definitely', + 'occassion': 'occasion', + 'untill': 'until', + 'acheive': 'achieve', + 'arguement': 'argument', + 'existance': 'existence', + }; + + let validatedContent = content; + for (const [typo, correction] of Object.entries(commonTypos)) { + if (validatedContent.includes(typo)) { + console.warn(`Found typo "${typo}" in ${filePath}, auto-correcting to "${correction}"`); + validatedContent = validatedContent.replace(new RegExp(typo, 'g'), correction); + } + } + + return validatedContent; +} + +/** + * Formats sidebar file with Prettier + */ +function formatSidebarFile(sidebarConfig, filePath) { + // Generate properly formatted JavaScript code + const sidebarContent = `// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const sidebar = ${JSON.stringify(sidebarConfig, null, 2)}; +module.exports = sidebar.items; +`; + + try { + // Try to format with Prettier (synchronous API) + // Note: Using formatWithCursor for synchronous operation + const formattedResult = prettier.formatWithCursor(sidebarContent, { + cursorOffset: 0, + parser: 'babel', + filepath: filePath, + singleQuote: true, + trailingComma: 'all', + printWidth: 100, + tabWidth: 2, + semi: true, + }); + + return formattedResult.formatted; + } catch (error) { + console.error(`Failed to format sidebar file ${filePath}:`, error.message); + // Return the unformatted content as fallback + return sidebarContent; + } +} + exports.load = load; From ec2042fba476dfc1f10511788239d7e4516f2681 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Wed, 17 Sep 2025 06:07:26 -0700 Subject: [PATCH 14/62] make scripts/stylusByExampleDocsHandler.ts format in proper mdx --- scripts/stylusByExampleDocsHandler.ts | 34 +++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/scripts/stylusByExampleDocsHandler.ts b/scripts/stylusByExampleDocsHandler.ts index ecd0113c2f..0fb2b34ddf 100644 --- a/scripts/stylusByExampleDocsHandler.ts +++ b/scripts/stylusByExampleDocsHandler.ts @@ -122,7 +122,9 @@ function copyFiles(source, target, allowList) { const content = fs.readFileSync(sourcePath, 'utf8'); const convertedContent = convertMetadataToFrontmatter(content); const validatedContent = validateContent(convertedContent, sourcePath); - fs.writeFileSync(path.join(newTargetPath, entry.name), validatedContent); + const targetFilePath = path.join(newTargetPath, entry.name); + const formattedContent = formatMDXFile(validatedContent, targetFilePath); + fs.writeFileSync(targetFilePath, formattedContent); } }); } else { @@ -135,12 +137,14 @@ function copyFiles(source, target, allowList) { } const sourcePath = path.join(dirPath, mdxFile.name); const newFileName = `${parentDirName}.mdx`; + const targetFilePath = path.join(targetPath, newFileName); const content = fs.readFileSync(sourcePath, 'utf8'); const convertedContent = convertMetadataToFrontmatter(content); const validatedContent = validateContent(convertedContent, sourcePath); const convertedContentWithBanner = addAdmonitionOneLineAboveFirstCodeBlock(validatedContent); - fs.writeFileSync(path.join(targetPath, newFileName), convertedContentWithBanner); + const formattedContent = formatMDXFile(convertedContentWithBanner, targetFilePath); + fs.writeFileSync(targetFilePath, formattedContent); } } } @@ -335,6 +339,32 @@ function validateContent(content, filePath) { return validatedContent; } +/** + * Formats MDX file with Prettier + */ +function formatMDXFile(content, filePath) { + try { + // Try to format with Prettier + const formattedResult = prettier.formatWithCursor(content, { + cursorOffset: 0, + parser: 'mdx', + filepath: filePath, + printWidth: 100, + tabWidth: 2, + useTabs: false, + singleQuote: true, + trailingComma: 'all', + proseWrap: 'preserve', + }); + + return formattedResult.formatted; + } catch (error) { + console.error(`Failed to format MDX file ${filePath}:`, error.message); + // Return the unformatted content as fallback + return content; + } +} + /** * Formats sidebar file with Prettier */ From 3698ac95cd84225ab7357e84bc03a67992a1dfaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Wed, 17 Sep 2025 09:02:45 -0700 Subject: [PATCH 15/62] prevent docs/api docs from being generated --- .gitignore | 1 + docusaurus.config.js | 1 + 2 files changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 51b889cb91..55487e543a 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ docs/sdk/index.mdx docs/sdk/migrate.mdx docs/partials/_glossary-partial.mdx docs/stylus-by-example/ +docs/api/ # Misc .DS_Store diff --git a/docusaurus.config.js b/docusaurus.config.js index d218c7a0f0..d2c2a7e6bd 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -54,6 +54,7 @@ const config = { ({ docs: { // path: './docs', + exclude: ['**/api/**'], remarkPlugins: [remarkMath], rehypePlugins: [rehypeKatex], sidebarPath: require.resolve('./sidebars.js'), From 45b028e9a1a60e0bffa3d41bb2ea29fc1eac75d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Wed, 17 Sep 2025 09:08:46 -0700 Subject: [PATCH 16/62] fix broken link in bold public preview banner partial --- .../bold/partials/_bold-public-preview-banner-partial.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/how-arbitrum-works/bold/partials/_bold-public-preview-banner-partial.mdx b/docs/how-arbitrum-works/bold/partials/_bold-public-preview-banner-partial.mdx index fb369103b5..a4a519e9e1 100644 --- a/docs/how-arbitrum-works/bold/partials/_bold-public-preview-banner-partial.mdx +++ b/docs/how-arbitrum-works/bold/partials/_bold-public-preview-banner-partial.mdx @@ -1,6 +1,6 @@ :::caution ALPHA RELEASE, PUBLIC PREVIEW DOCS -The BoLD dispute protocol is currently deployed on a public testnet (that posts assertions to Ethereum Sepolia) and is tagged as an `alpha` release. The code has been audited by [Trail of Bits](https://github.com/trailofbits/publications/blob/master/reviews/2024-04-offchainbold-securityreview.pdf) and in a [public audit competition with Code4rena](https://code4rena.com/audits/2024-05-arbitrum-bold), but **should not be used in production scenarios**. Please note that the public testnet is intended for Arbitrum users and researchers to test and experiment with the BoLD dispute protocol for the purposes of education and hardening the protocol via the surfacing of bugs. The public testnet may be paused, and its parameters may be updated at any time in response to scenarios that impact the network and its ability to fulfill its function as a working, reliable test environment. This documentation is currently in [public preview](../public-preview-expectations.md). +The BoLD dispute protocol is currently deployed on a public testnet (that posts assertions to Ethereum Sepolia) and is tagged as an `alpha` release. The code has been audited by [Trail of Bits](https://github.com/trailofbits/publications/blob/master/reviews/2024-04-offchainbold-securityreview.pdf) and in a [public audit competition with Code4rena](https://code4rena.com/audits/2024-05-arbitrum-bold), but **should not be used in production scenarios**. Please note that the public testnet is intended for Arbitrum users and researchers to test and experiment with the BoLD dispute protocol for the purposes of education and hardening the protocol via the surfacing of bugs. The public testnet may be paused, and its parameters may be updated at any time in response to scenarios that impact the network and its ability to fulfill its function as a working, reliable test environment. This documentation is currently in [public preview](../public-preview-expectations.mdx). To provide feedback, click the _Request an update_ button at the top of this document, [join the Arbitrum Discord](https://discord.gg/arbitrum), or reach out to our team directly by completing [this form](http://bit.ly/3yy6EUK). From 0ae4cf3eb097e2acd43e77c053c7a41e60185da9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Wed, 17 Sep 2025 10:58:37 -0700 Subject: [PATCH 17/62] Add empty lines and proper stylebar format to scripts/stylusByExampleDocsHandler.ts --- scripts/stylusByExampleDocsHandler.ts | 559 ++++++++++++++++++++++++-- 1 file changed, 530 insertions(+), 29 deletions(-) diff --git a/scripts/stylusByExampleDocsHandler.ts b/scripts/stylusByExampleDocsHandler.ts index 0fb2b34ddf..415c1c5135 100644 --- a/scripts/stylusByExampleDocsHandler.ts +++ b/scripts/stylusByExampleDocsHandler.ts @@ -1,9 +1,11 @@ const fs = require('fs'); const path = require('path'); + const { RendererEvent } = require('typedoc'); const { parseMarkdownContentTitle } = require('@docusaurus/utils'); const prettier = require('prettier'); +// Configuration constants const allowList = [ 'hello_world', 'primitive_data_types', @@ -24,10 +26,14 @@ const allowList = [ const appsAllowList = ['erc20', 'erc721', 'vending_machine', 'multi_call']; +// Main plugin function function load(app) { + // Define output directories const outputDir = path.join(app.options.getValue('out'), '../../docs/stylus-by-example'); const basicExamplesOutputDir = path.join(outputDir, 'basic_examples'); const applicationsOutputDir = path.join(outputDir, 'applications'); + + // Define source directories const sourceDirBasicExamples = path.join( app.options.getValue('out'), '../../submodules/stylus-by-example/src/app/basic_examples', @@ -37,6 +43,7 @@ function load(app) { '../../submodules/stylus-by-example/src/app/applications', ); + // Register event handlers app.renderer.on(RendererEvent.START, async () => { cleanDirectory(outputDir); }); @@ -66,10 +73,12 @@ function load(app) { }); } +// Utility functions function cleanDirectory(directory) { if (fs.existsSync(directory)) { fs.readdirSync(directory).forEach((file) => { const curPath = path.join(directory, file); + if (fs.lstatSync(curPath).isDirectory()) { cleanDirectory(curPath); fs.rmdirSync(curPath); @@ -81,6 +90,7 @@ function cleanDirectory(directory) { } function copyFiles(source, target, allowList) { + // Validate source directory if (!fs.existsSync(source)) { console.error(`Source path does not exist: ${source}`); return; @@ -91,6 +101,7 @@ function copyFiles(source, target, allowList) { return; } + // Create target directory fs.mkdirSync(target, { recursive: true }); function processDirectory(dirPath, targetPath, isRoot = false) { @@ -101,6 +112,7 @@ function copyFiles(source, target, allowList) { // Special handling for root directory entries.forEach((entry) => { const sourcePath = path.join(dirPath, entry.name); + if (entry.isDirectory()) { processDirectory(sourcePath, targetPath); } @@ -115,7 +127,9 @@ function copyFiles(source, target, allowList) { if (!allowList.includes(entry.name)) { return; } + const sourcePath = path.join(dirPath, entry.name); + if (entry.isDirectory()) { processDirectory(sourcePath, newTargetPath); } else if (entry.name === 'page.mdx') { @@ -130,11 +144,14 @@ function copyFiles(source, target, allowList) { } else { // This directory only contains files, so we flatten it const mdxFile = entries.find((entry) => entry.isFile() && entry.name === 'page.mdx'); + if (mdxFile) { const parentDirName = path.basename(dirPath); + if (!allowList.includes(parentDirName)) { return; } + const sourcePath = path.join(dirPath, mdxFile.name); const newFileName = `${parentDirName}.mdx`; const targetFilePath = path.join(targetPath, newFileName); @@ -152,7 +169,7 @@ function copyFiles(source, target, allowList) { processDirectory(source, target, true); } -// Adjust the file path +// Content processing constants and functions const firstCodeBlock = `\`\`\`rust`; const admonitionNotForProduction = ` import NotForProductionBannerPartial from '../../partials/_not-for-production-banner-partial.mdx'; @@ -162,6 +179,7 @@ import NotForProductionBannerPartial from '../../partials/_not-for-production-ba function addAdmonitionOneLineAboveFirstCodeBlock(content) { const index = content.indexOf(firstCodeBlock); + if (index === -1) { console.log('firstCodeBlock not found'); return content; // Return original content instead of undefined @@ -169,6 +187,7 @@ function addAdmonitionOneLineAboveFirstCodeBlock(content) { // Find the position two lines before firstCodeBlock const lines = content.substring(0, index).split('\n'); + if (lines.length < 2) { console.warn('Not enough lines before code block for banner insertion'); return content; @@ -187,6 +206,7 @@ function convertMetadataToFrontmatter(content) { if (match) { let metadataObj; + try { // Safer parsing without eval() // First, try to parse as a JavaScript object literal @@ -230,6 +250,7 @@ function convertMetadataToFrontmatter(content) { return content; } +// Sidebar generation functions /** * Sorts entries based on the allowList order */ @@ -255,11 +276,14 @@ function generateSidebar(dir, basePath = '') { const items = entries .map((entry) => { const fullPath = path.join(dir, entry.name); + if (entry.isDirectory()) { const subItems = generateSidebar(fullPath, `${basePath}/${entry.name}`); + if (subItems.length === 0) { return null; // Filter out empty categories } + const label = capitalizeWords(entry.name.replace(/_/g, ' ')); return { type: 'category', @@ -304,18 +328,24 @@ function getTitleFromFileContent(filePath) { if (!fs.existsSync(filePath)) { return ''; } + const fileContent = fs.readFileSync(filePath, 'utf8'); const { contentTitle } = parseMarkdownContentTitle(fileContent); return contentTitle || ''; } +// Content validation and formatting functions /** - * Validates and fixes common typos in content + * Comprehensive typo and content validation */ function validateContent(content, filePath) { - // Common typos to fix - const commonTypos = { + let validatedContent = content; + let fixesApplied = []; + + // Comprehensive typo database + const typoDatabase = { + // Common English typos 'followinge': 'following', 'recieve': 'receive', 'occured': 'occurred', @@ -326,76 +356,547 @@ function validateContent(content, filePath) { 'acheive': 'achieve', 'arguement': 'argument', 'existance': 'existence', + 'begining': 'beginning', + 'calender': 'calendar', + 'cemetary': 'cemetery', + 'changable': 'changeable', + 'collegue': 'colleague', + 'concious': 'conscious', + 'definate': 'definite', + 'embarass': 'embarrass', + 'enviroment': 'environment', + 'goverment': 'government', + 'independant': 'independent', + 'neccessary': 'necessary', + 'occurance': 'occurrence', + 'perseverence': 'perseverance', + 'priviledge': 'privilege', + 'publically': 'publicly', + 'recomend': 'recommend', + 'refered': 'referred', + 'relevent': 'relevant', + 'supercede': 'supersede', + 'tendancy': 'tendency', + 'truely': 'truly', + 'wierd': 'weird', + + // Technical/Programming typos + 'lenght': 'length', + 'widht': 'width', + 'heigth': 'height', + 'indeces': 'indices', + 'accesible': 'accessible', + 'compatable': 'compatible', + 'dependancy': 'dependency', + 'efficency': 'efficiency', + 'exectuable': 'executable', + 'flexable': 'flexible', + 'initialise': 'initialize', + 'paramater': 'parameter', + 'privilage': 'privilege', + 'proccess': 'process', + 'seperation': 'separation', + 'succesful': 'successful', + 'syncronous': 'synchronous', + 'trasaction': 'transaction', + 'varient': 'variant', + 'verfication': 'verification', + + // Blockchain/Crypto specific typos + 'blokchain': 'blockchain', + 'cryptocurency': 'cryptocurrency', + 'ethereum': 'Ethereum', + 'bitcoin': 'Bitcoin', + 'smartcontract': 'smart contract', + 'decentalized': 'decentralized', + 'consesus': 'consensus', + 'validater': 'validator', + 'tranaction': 'transaction', + 'addres': 'address', + 'ballance': 'balance', + 'recipent': 'recipient', + 'recepient': 'recipient', + 'wallet': 'wallet', + 'minning': 'mining', + 'hasrate': 'hashrate', + 'merkletree': 'Merkle tree', + 'nounce': 'nonce', + 'dapp': 'dApp', + 'defi': 'DeFi', + 'nft': 'NFT', + 'dao': 'DAO', + 'api': 'API', + 'sdk': 'SDK', + 'evm': 'EVM', + 'l1': 'Layer 1', + 'l2': 'Layer 2', + 'l3': 'Layer 3', + + // Case sensitivity corrections for proper nouns + 'solidity': 'Solidity', + 'rust': 'Rust', + 'javascript': 'JavaScript', + 'typescript': 'TypeScript', + 'nodejs': 'Node.js', + 'github': 'GitHub', + 'docker': 'Docker', + 'kubernetes': 'Kubernetes', + 'aws': 'AWS', + 'gcp': 'GCP', + 'azure': 'Azure', + + // Common markdown/documentation issues + 'eg.': 'e.g.,', + 'ie.': 'i.e.,', + 'etc.': 'etc.', + 'vs.': 'vs.', }; - let validatedContent = content; - for (const [typo, correction] of Object.entries(commonTypos)) { - if (validatedContent.includes(typo)) { - console.warn(`Found typo "${typo}" in ${filePath}, auto-correcting to "${correction}"`); - validatedContent = validatedContent.replace(new RegExp(typo, 'g'), correction); + // Apply typo fixes with word boundary checks to avoid partial matches + for (const [typo, correction] of Object.entries(typoDatabase)) { + const regex = new RegExp(`\\b${typo.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`, 'gi'); + const matches = validatedContent.match(regex); + + if (matches) { + validatedContent = validatedContent.replace(regex, correction); + fixesApplied.push(`"${typo}" -> "${correction}" (${matches.length} occurrence${matches.length > 1 ? 's' : ''})`); } } + // Fix common markdown formatting issues + const markdownFixes = [ + // Fix missing spaces after list markers + { pattern: /^(\s*[-*+])([^\s])/gm, replacement: '$1 $2', description: 'Missing space after list marker' }, + // Fix multiple consecutive spaces + { pattern: / +/g, replacement: ' ', description: 'Multiple consecutive spaces' }, + // Fix trailing spaces + { pattern: / +$/gm, replacement: '', description: 'Trailing spaces' }, + // Fix inconsistent code block formatting + { pattern: /```(\w+)\s*\n/g, replacement: '```$1\n', description: 'Code block language formatting' }, + // Fix missing space after headers + { pattern: /^(#+)([^\s#])/gm, replacement: '$1 $2', description: 'Missing space after header marker' }, + // Fix double spaces in sentences + { pattern: /([.!?]) +/g, replacement: '$1 ', description: 'Double spaces after punctuation' }, + ]; + + for (const fix of markdownFixes) { + const beforeLength = validatedContent.length; + validatedContent = validatedContent.replace(fix.pattern, fix.replacement); + const afterLength = validatedContent.length; + + if (beforeLength !== afterLength) { + fixesApplied.push(fix.description); + } + } + + // Log all fixes applied + if (fixesApplied.length > 0) { + console.log(`\n📝 Content fixes applied to ${path.basename(filePath)}:`); + fixesApplied.forEach(fix => console.log(` ✓ ${fix}`)); + } + return validatedContent; } /** - * Formats MDX file with Prettier + * Formats MDX file with Prettier and additional content validation */ function formatMDXFile(content, filePath) { try { - // Try to format with Prettier - const formattedResult = prettier.formatWithCursor(content, { + // Pre-format validation and fixes + let processedContent = content; + + // Apply Prettier-compatible pre-processing + processedContent = preprocessMDXContent(processedContent); + + // Format with Prettier using enhanced configuration + const formattedResult = prettier.formatWithCursor(processedContent, { cursorOffset: 0, parser: 'mdx', filepath: filePath, + // Enhanced Prettier options for better MDX formatting printWidth: 100, tabWidth: 2, useTabs: false, singleQuote: true, trailingComma: 'all', proseWrap: 'preserve', + semi: true, + bracketSpacing: true, + arrowParens: 'avoid', + endOfLine: 'lf', + // MDX-specific options + embeddedLanguageFormatting: 'auto', }); - return formattedResult.formatted; + // Post-format validation + const finalContent = postProcessMDXContent(formattedResult.formatted, filePath); + + console.log(`✅ Successfully formatted MDX file: ${path.basename(filePath)}`); + return finalContent; } catch (error) { - console.error(`Failed to format MDX file ${filePath}:`, error.message); - // Return the unformatted content as fallback + console.error(`❌ Failed to format MDX file ${filePath}:`, error.message); + console.warn(`⚠️ Returning unformatted content as fallback`); return content; } } /** - * Formats sidebar file with Prettier + * Pre-processes MDX content for better Prettier compatibility */ -function formatSidebarFile(sidebarConfig, filePath) { - // Generate properly formatted JavaScript code - const sidebarContent = `// @ts-check -/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const sidebar = ${JSON.stringify(sidebarConfig, null, 2)}; -module.exports = sidebar.items; -`; +function preprocessMDXContent(content) { + let processed = content; + + // Fix common MDX formatting issues that Prettier might struggle with + const fixes = [ + // Ensure proper spacing around JSX components + { pattern: /(<[A-Z][^>]*>)(\S)/g, replacement: '$1\n$2', description: 'JSX component spacing' }, + { pattern: /(\S)(<\/[A-Z][^>]*>)/g, replacement: '$1\n$2', description: 'JSX closing tag spacing' }, + + // Fix frontmatter formatting + { pattern: /^---\s*\n([\s\S]*?)\n\s*---/m, replacement: (match, frontmatter) => { + return `---\n${frontmatter.trim()}\n---`; + }, description: 'Frontmatter formatting' }, + + // Ensure proper code block formatting + { pattern: /```(\w+)\s*\n\s*\n/g, replacement: '```$1\n', description: 'Code block spacing' }, + + // Fix import statements formatting + { pattern: /^import\s+(.+?)\s+from\s+['"](.+?)['"];?\s*$/gm, replacement: "import $1 from '$2';", description: 'Import statement formatting' }, + ]; + + fixes.forEach(fix => { + if (typeof fix.replacement === 'function') { + processed = processed.replace(fix.pattern, fix.replacement); + } else { + processed = processed.replace(fix.pattern, fix.replacement); + } + }); + + return processed; +} + +/** + * Post-processes formatted MDX content for final validation + */ +function postProcessMDXContent(content, filePath) { + let processed = content; + + // Final validation and cleanup + const postFixes = [ + // Ensure single newline at end of file + { pattern: /\n*$/, replacement: '\n', description: 'Single trailing newline' }, + + // Remove excessive blank lines + { pattern: /\n{3,}/g, replacement: '\n\n', description: 'Excessive blank lines' }, + + // Ensure proper spacing after headers + { pattern: /^(#{1,6}\s+.+)(?=\n[^\n#])/gm, replacement: '$1\n', description: 'Header spacing' }, + ]; + + postFixes.forEach(fix => { + const before = processed.length; + processed = processed.replace(fix.pattern, fix.replacement); + if (before !== processed.length) { + console.log(` ✓ Applied post-format fix: ${fix.description}`); + } + }); + + return processed; +} + +// Sidebar configuration types and validation +/** + * Enhanced sidebar configuration types for better type safety + */ +interface SidebarItem { + type: 'doc' | 'category' | 'link' | 'html' | 'ref'; + id?: string; + label: string; + items?: SidebarItem[]; + href?: string; + className?: string; + customProps?: Record; +} +interface SidebarConfig { + items: SidebarItem[]; +} + +/** + * Validates sidebar configuration against Docusaurus conventions + */ +function validateSidebarConfig(config: SidebarConfig, filePath: string): { isValid: boolean; errors: string[] } { + const errors: string[] = []; + const seenIds = new Set(); + const seenLabels = new Set(); + + function validateItem(item: SidebarItem, path: string = '') { + const currentPath = path ? `${path}.${item.label}` : item.label; + + // Validate required fields + if (!item.type) { + errors.push(`Missing 'type' field at ${currentPath}`); + } + + if (!item.label || typeof item.label !== 'string') { + errors.push(`Missing or invalid 'label' field at ${currentPath}`); + } + + // Validate type-specific requirements + switch (item.type) { + case 'doc': + if (!item.id) { + errors.push(`Missing 'id' field for doc item at ${currentPath}`); + } else { + // Check for duplicate IDs + if (seenIds.has(item.id)) { + errors.push(`Duplicate ID '${item.id}' found at ${currentPath}`); + } + seenIds.add(item.id); + } + break; + + case 'category': + if (!item.items || !Array.isArray(item.items)) { + errors.push(`Missing or invalid 'items' array for category at ${currentPath}`); + } else if (item.items.length === 0) { + errors.push(`Empty 'items' array for category at ${currentPath}`); + } + break; + + case 'link': + if (!item.href) { + errors.push(`Missing 'href' field for link item at ${currentPath}`); + } + break; + } + + // Check for duplicate labels at the same level + const labelKey = `${path}:${item.label}`; + + if (seenLabels.has(labelKey)) { + errors.push(`Duplicate label '${item.label}' found at ${currentPath}`); + } + seenLabels.add(labelKey); + + // Validate label conventions (sentence case) + if (item.label && typeof item.label === 'string') { + const isProperSentenceCase = /^[A-Z][a-z]/.test(item.label) || /^[A-Z]+$/.test(item.label); + + if (!isProperSentenceCase) { + console.warn(`⚠️ Label '${item.label}' at ${currentPath} should use sentence case`); + } + } + + // Recursively validate nested items + if (item.items && Array.isArray(item.items)) { + item.items.forEach(subItem => validateItem(subItem, currentPath)); + } + } + + if (!config.items || !Array.isArray(config.items)) { + errors.push('Root configuration must have an "items" array'); + } else { + config.items.forEach(item => validateItem(item)); + } + + return { isValid: errors.length === 0, errors }; +} + +// Sidebar formatting functions +/** + * Formats sidebar file with enhanced Docusaurus compliance and Prettier formatting + */ +function formatSidebarFile(sidebarConfig: SidebarConfig, filePath: string): string { try { - // Try to format with Prettier (synchronous API) - // Note: Using formatWithCursor for synchronous operation + // Validate sidebar configuration + const validation = validateSidebarConfig(sidebarConfig, filePath); + + if (!validation.isValid) { + console.error(`\n❌ Sidebar validation errors in ${path.basename(filePath)}:`); + validation.errors.forEach(error => console.error(` • ${error}`)); + throw new Error(`Invalid sidebar configuration: ${validation.errors.join(', ')}`); + } + + console.log(`✅ Sidebar validation passed for ${path.basename(filePath)}`); + + // Optimize sidebar configuration for better formatting + const optimizedConfig = optimizeSidebarConfig(sidebarConfig); + + // Generate enhanced Docusaurus-compliant sidebar content + const sidebarContent = generateSidebarContent(optimizedConfig); + + // Apply Prettier formatting with enhanced configuration const formattedResult = prettier.formatWithCursor(sidebarContent, { cursorOffset: 0, parser: 'babel', filepath: filePath, + // Enhanced Prettier options for better Docusaurus compatibility singleQuote: true, trailingComma: 'all', printWidth: 100, tabWidth: 2, semi: true, + bracketSpacing: true, + arrowParens: 'avoid', + endOfLine: 'lf', + quoteProps: 'as-needed', + // Object formatting options + bracketSameLine: false, + objectCurlySpacing: true, }); - return formattedResult.formatted; + // Post-format validation and cleanup + const finalContent = postProcessSidebarContent(formattedResult.formatted, filePath); + + console.log(`✅ Successfully formatted sidebar file: ${path.basename(filePath)}`); + return finalContent; } catch (error) { - console.error(`Failed to format sidebar file ${filePath}:`, error.message); - // Return the unformatted content as fallback - return sidebarContent; + console.error(`\n❌ Failed to format sidebar file ${filePath}:`); + console.error(` Error: ${error.message}`); + + // Create a robust fallback sidebar + const fallbackContent = generateFallbackSidebar(sidebarConfig, filePath); + console.warn(`⚠️ Using fallback sidebar configuration for ${path.basename(filePath)}`); + return fallbackContent; + } +} + +/** + * Optimizes sidebar configuration for better formatting and compliance + */ +function optimizeSidebarConfig(config: SidebarConfig): SidebarConfig { + function optimizeItem(item: SidebarItem): SidebarItem { + const optimized: SidebarItem = { + type: item.type, + label: item.label.trim(), + }; + + // Add type-specific properties in a consistent order + if (item.id) optimized.id = item.id.trim(); + if (item.href) optimized.href = item.href.trim(); + if (item.className) optimized.className = item.className.trim(); + + // Recursively optimize nested items + if (item.items && Array.isArray(item.items)) { + optimized.items = item.items.map(optimizeItem); + } + + // Add custom properties last + if (item.customProps) optimized.customProps = item.customProps; + + return optimized; + } + + return { + items: config.items.map(optimizeItem), + }; +} + +/** + * Generates properly formatted sidebar content with enhanced documentation + */ +function generateSidebarContent(config: SidebarConfig): string { + const timestamp = new Date().toISOString(); + const configJson = JSON.stringify(config, null, 2); + + return `// @ts-check +/** + * @fileoverview Autogenerated sidebar configuration for Stylus by Example + * @description This file is automatically generated by stylusByExampleDocsHandler.ts + * @generated ${timestamp} + * @see https://docusaurus.io/docs/sidebar + * @see https://docusaurus.io/docs/sidebar/items + */ + +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const sidebarConfig = ${configJson}; + +/** + * Export only the items array as required by Docusaurus + * @type {import('@docusaurus/plugin-content-docs').SidebarItem[]} + */ +module.exports = sidebarConfig.items; +`; +} + +/** + * Post-processes formatted sidebar content for final validation + */ +function postProcessSidebarContent(content: string, filePath: string): string { + let processed = content; + + // Final validation and cleanup for JavaScript/TypeScript files + const postFixes = [ + // Ensure proper module.exports formatting + { + pattern: /module\.exports\s*=\s*sidebarConfig\.items\s*;?\s*$/m, + replacement: 'module.exports = sidebarConfig.items;', + description: 'Module exports formatting' + }, + + // Ensure single newline at end of file + { pattern: /\n*$/, replacement: '\n', description: 'Single trailing newline' }, + + // Fix any double semicolons + { pattern: /;;+/g, replacement: ';', description: 'Double semicolons' }, + + // Ensure proper spacing in JSDoc comments + { pattern: /\/\*\*\s*\n\s*\*\s*/g, replacement: '/**\n * ', description: 'JSDoc formatting' }, + ]; + + postFixes.forEach(fix => { + const before = processed.length; + processed = processed.replace(fix.pattern, fix.replacement); + + if (before !== processed.length) { + console.log(` ✓ Applied sidebar post-format fix: ${fix.description}`); + } + }); + + return processed; +} + +/** + * Generates a robust fallback sidebar when formatting fails + */ +function generateFallbackSidebar(config: SidebarConfig, filePath: string): string { + const timestamp = new Date().toISOString(); + + try { + // Try to create a minimal but valid sidebar + const minimalConfig = { + items: config.items.map(item => ({ + type: item.type, + label: item.label, + ...(item.id && { id: item.id }), + ...(item.items && { items: item.items }), + })), + }; + + return `// @ts-check +// WARNING: This is a fallback sidebar generated due to formatting errors +// Generated: ${timestamp} +// File: ${path.basename(filePath)} + +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const sidebar = ${JSON.stringify(minimalConfig, null, 2)}; + +module.exports = sidebar.items; +`; + } catch (fallbackError) { + // Ultimate fallback - empty sidebar + console.error(`❌ Critical error generating fallback sidebar:`, fallbackError.message); + return `// @ts-check +// CRITICAL ERROR: Unable to generate sidebar configuration +// Generated: ${timestamp} +// File: ${path.basename(filePath)} + +module.exports = []; +`; } } +// Export the main load function exports.load = load; From 56820ea26e0f0bb6f253315fda3795a8d7654ca8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Wed, 17 Sep 2025 11:07:05 -0700 Subject: [PATCH 18/62] chore: remove generated stylus-by-example docs from git tracking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These files are auto-generated by scripts/stylusByExampleDocsHandler.ts and should not be tracked in version control. They are already listed in .gitignore but were previously committed before being ignored. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- docs/stylus-by-example/DONT-EDIT-THIS-FOLDER | 4 - docs/stylus-by-example/applications/erc20.mdx | 369 ------------- .../stylus-by-example/applications/erc721.mdx | 504 ------------------ .../applications/multi_call.mdx | 129 ----- .../stylus-by-example/applications/sidebar.js | 27 - .../applications/vending_machine.mdx | 145 ----- .../basic_examples/abi_decode.mdx | 139 ----- .../basic_examples/abi_encode.mdx | 218 -------- .../basic_examples/bytes_in_bytes_out.mdx | 54 -- .../basic_examples/constants.mdx | 102 ---- .../basic_examples/errors.mdx | 196 ------- .../basic_examples/events.mdx | 225 -------- .../basic_examples/function.mdx | 234 -------- .../basic_examples/function_selector.mdx | 40 -- .../basic_examples/hashing.mdx | 211 -------- .../basic_examples/hello_world.mdx | 95 ---- .../basic_examples/inheritance.mdx | 137 ----- .../basic_examples/primitive_data_types.mdx | 298 ----------- .../basic_examples/sending_ether.mdx | 152 ------ .../basic_examples/sidebar.js | 82 --- .../basic_examples/variables.mdx | 125 ----- .../basic_examples/vm_affordances.mdx | 161 ------ 22 files changed, 3647 deletions(-) delete mode 100644 docs/stylus-by-example/DONT-EDIT-THIS-FOLDER delete mode 100644 docs/stylus-by-example/applications/erc20.mdx delete mode 100644 docs/stylus-by-example/applications/erc721.mdx delete mode 100644 docs/stylus-by-example/applications/multi_call.mdx delete mode 100644 docs/stylus-by-example/applications/sidebar.js delete mode 100644 docs/stylus-by-example/applications/vending_machine.mdx delete mode 100644 docs/stylus-by-example/basic_examples/abi_decode.mdx delete mode 100644 docs/stylus-by-example/basic_examples/abi_encode.mdx delete mode 100644 docs/stylus-by-example/basic_examples/bytes_in_bytes_out.mdx delete mode 100644 docs/stylus-by-example/basic_examples/constants.mdx delete mode 100644 docs/stylus-by-example/basic_examples/errors.mdx delete mode 100644 docs/stylus-by-example/basic_examples/events.mdx delete mode 100644 docs/stylus-by-example/basic_examples/function.mdx delete mode 100644 docs/stylus-by-example/basic_examples/function_selector.mdx delete mode 100644 docs/stylus-by-example/basic_examples/hashing.mdx delete mode 100644 docs/stylus-by-example/basic_examples/hello_world.mdx delete mode 100644 docs/stylus-by-example/basic_examples/inheritance.mdx delete mode 100644 docs/stylus-by-example/basic_examples/primitive_data_types.mdx delete mode 100644 docs/stylus-by-example/basic_examples/sending_ether.mdx delete mode 100644 docs/stylus-by-example/basic_examples/sidebar.js delete mode 100644 docs/stylus-by-example/basic_examples/variables.mdx delete mode 100644 docs/stylus-by-example/basic_examples/vm_affordances.mdx diff --git a/docs/stylus-by-example/DONT-EDIT-THIS-FOLDER b/docs/stylus-by-example/DONT-EDIT-THIS-FOLDER deleted file mode 100644 index bc228b2f92..0000000000 --- a/docs/stylus-by-example/DONT-EDIT-THIS-FOLDER +++ /dev/null @@ -1,4 +0,0 @@ -This folder stores generated files that should not be edited. - -DO NOT hand-modify files in this folder because they will be overwritten in the -next build. diff --git a/docs/stylus-by-example/applications/erc20.mdx b/docs/stylus-by-example/applications/erc20.mdx deleted file mode 100644 index 3837718aa6..0000000000 --- a/docs/stylus-by-example/applications/erc20.mdx +++ /dev/null @@ -1,369 +0,0 @@ ---- -title: 'ERC-20 • Stylus by Example' -description: 'An example implementation of the ERC-20 token standard in Rust using Arbitrum Stylus.' ---- - -{/* Begin Content */} - -# ERC-20 - -Any contract that follows the [ERC-20 standard](https://eips.ethereum.org/EIPS/eip-20) is an ERC-20 token. - -ERC-20 tokens provide functionalities to - -- transfer tokens -- allow others to transfer tokens on behalf of the token holder - -Here is the interface for ERC-20. - -```solidity -interface IERC20 { - function totalSupply() external view returns (uint256); - function balanceOf(address account) external view returns (uint256); - function transfer(address recipient, uint256 amount) - external - returns (bool); - function allowance(address owner, address spender) - external - view - returns (uint256); - function approve(address spender, uint256 amount) external returns (bool); - function transferFrom(address sender, address recipient, uint256 amount) - external - returns (bool); -} -``` - -Example implementation of an ERC-20 token contract written in Rust. - -### src/erc20.rs - -import NotForProductionBannerPartial from '../../partials/_not-for-production-banner-partial.mdx'; - - - -```rust -//! Implementation of the ERC-20 standard -//! -//! The eponymous [`Erc20`] type provides all the standard methods, -//! and is intended to be inherited by other contract types. -//! -//! You can configure the behavior of [`Erc20`] via the [`Erc20Params`] trait, -//! which allows specifying the name, symbol, and decimals of the token. -//! -//! Note that this code is unaudited and not fit for production use. - -// Imported packages -use alloc::string::String; -use alloy_primitives::{Address, U256}; -use alloy_sol_types::sol; -use core::marker::PhantomData; -use stylus_sdk::{ - evm, - msg, - prelude::*, -}; - -pub trait Erc20Params { - /// Immutable token name - const NAME: &'static str; - - /// Immutable token symbol - const SYMBOL: &'static str; - - /// Immutable token decimals - const DECIMALS: u8; -} - -sol_storage! { - /// Erc20 implements all ERC-20 methods. - pub struct Erc20 { - /// Maps users to balances - mapping(address => uint256) balances; - /// Maps users to a mapping of each spender's allowance - mapping(address => mapping(address => uint256)) allowances; - /// The total supply of the token - uint256 total_supply; - /// Used to allow [`Erc20Params`] - PhantomData phantom; - } -} - -// Declare events and Solidity error types -sol! { - event Transfer(address indexed from, address indexed to, uint256 value); - event Approval(address indexed owner, address indexed spender, uint256 value); - - error InsufficientBalance(address from, uint256 have, uint256 want); - error InsufficientAllowance(address owner, address spender, uint256 have, uint256 want); -} - -/// Represents the ways methods may fail. -#[derive(SolidityError)] -pub enum Erc20Error { - InsufficientBalance(InsufficientBalance), - InsufficientAllowance(InsufficientAllowance), -} - -// These methods aren't exposed to other contracts -// Methods marked as "pub" here are usable outside of the erc20 module (i.e. they're callable from lib.rs) -// Note: modifying storage will become much prettier soon -impl Erc20 { - /// Movement of funds between 2 accounts - /// (invoked by the external transfer() and transfer_from() functions ) - pub fn _transfer( - &mut self, - from: Address, - to: Address, - value: U256, - ) -> Result<(), Erc20Error> { - // Decreasing sender balance - let mut sender_balance = self.balances.setter(from); - let old_sender_balance = sender_balance.get(); - if old_sender_balance < value { - return Err(Erc20Error::InsufficientBalance(InsufficientBalance { - from, - have: old_sender_balance, - want: value, - })); - } - sender_balance.set(old_sender_balance - value); - - // Increasing receiver balance - let mut to_balance = self.balances.setter(to); - let new_to_balance = to_balance.get() + value; - to_balance.set(new_to_balance); - - // Emitting the transfer event - evm::log(Transfer { from, to, value }); - Ok(()) - } - - /// Mints `value` tokens to `address` - pub fn mint(&mut self, address: Address, value: U256) -> Result<(), Erc20Error> { - // Increasing balance - let mut balance = self.balances.setter(address); - let new_balance = balance.get() + value; - balance.set(new_balance); - - // Increasing total supply - self.total_supply.set(self.total_supply.get() + value); - - // Emitting the transfer event - evm::log(Transfer { - from: Address::ZERO, - to: address, - value, - }); - - Ok(()) - } - - /// Burns `value` tokens from `address` - pub fn burn(&mut self, address: Address, value: U256) -> Result<(), Erc20Error> { - // Decreasing balance - let mut balance = self.balances.setter(address); - let old_balance = balance.get(); - if old_balance < value { - return Err(Erc20Error::InsufficientBalance(InsufficientBalance { - from: address, - have: old_balance, - want: value, - })); - } - balance.set(old_balance - value); - - // Decreasing the total supply - self.total_supply.set(self.total_supply.get() - value); - - // Emitting the transfer event - evm::log(Transfer { - from: address, - to: Address::ZERO, - value, - }); - - Ok(()) - } -} - -// These methods are external to other contracts -// Note: modifying storage will become much prettier soon -#[public] -impl Erc20 { - /// Immutable token name - pub fn name() -> String { - T::NAME.into() - } - - /// Immutable token symbol - pub fn symbol() -> String { - T::SYMBOL.into() - } - - /// Immutable token decimals - pub fn decimals() -> u8 { - T::DECIMALS - } - - /// Total supply of tokens - pub fn total_supply(&self) -> U256 { - self.total_supply.get() - } - - /// Balance of `address` - pub fn balance_of(&self, owner: Address) -> U256 { - self.balances.get(owner) - } - - /// Transfers `value` tokens from msg::sender() to `to` - pub fn transfer(&mut self, to: Address, value: U256) -> Result { - self._transfer(msg::sender(), to, value)?; - Ok(true) - } - - /// Transfers `value` tokens from `from` to `to` - /// (msg::sender() must be able to spend at least `value` tokens from `from`) - pub fn transfer_from( - &mut self, - from: Address, - to: Address, - value: U256, - ) -> Result { - // Check msg::sender() allowance - let mut sender_allowances = self.allowances.setter(from); - let mut allowance = sender_allowances.setter(msg::sender()); - let old_allowance = allowance.get(); - if old_allowance < value { - return Err(Erc20Error::InsufficientAllowance(InsufficientAllowance { - owner: from, - spender: msg::sender(), - have: old_allowance, - want: value, - })); - } - - // Decreases allowance - allowance.set(old_allowance - value); - - // Calls the internal transfer function - self._transfer(from, to, value)?; - - Ok(true) - } - - /// Approves the spenditure of `value` tokens of msg::sender() to `spender` - pub fn approve(&mut self, spender: Address, value: U256) -> bool { - self.allowances.setter(msg::sender()).insert(spender, value); - evm::log(Approval { - owner: msg::sender(), - spender, - value, - }); - true - } - - /// Returns the allowance of `spender` on `owner`'s tokens - pub fn allowance(&self, owner: Address, spender: Address) -> U256 { - self.allowances.getter(owner).get(spender) - } -} -``` - -### lib.rs - -```rust -// Only run this as a WASM if the export-abi feature is not set. -#![cfg_attr(not(any(feature = "export-abi", test)), no_main)] -extern crate alloc; - -// Modules and imports -mod erc20; - -use alloy_primitives::{Address, U256}; -use stylus_sdk::{ - msg, - prelude::* -}; -use crate::erc20::{Erc20, Erc20Params, Erc20Error}; - -/// Immutable definitions -struct StylusTokenParams; -impl Erc20Params for StylusTokenParams { - const NAME: &'static str = "StylusToken"; - const SYMBOL: &'static str = "STK"; - const DECIMALS: u8 = 18; -} - -// Define the entrypoint as a Solidity storage object. The sol_storage! macro -// will generate Rust-equivalent structs with all fields mapped to Solidity-equivalent -// storage slots and types. -sol_storage! { - #[entrypoint] - struct StylusToken { - // Allows erc20 to access StylusToken's storage and make calls - #[borrow] - Erc20 erc20; - } -} - -#[public] -#[inherit(Erc20)] -impl StylusToken { - /// Mints tokens - pub fn mint(&mut self, value: U256) -> Result<(), Erc20Error> { - self.erc20.mint(msg::sender(), value)?; - Ok(()) - } - - /// Mints tokens to another address - pub fn mint_to(&mut self, to: Address, value: U256) -> Result<(), Erc20Error> { - self.erc20.mint(to, value)?; - Ok(()) - } - - /// Burns tokens - pub fn burn(&mut self, value: U256) -> Result<(), Erc20Error> { - self.erc20.burn(msg::sender(), value)?; - Ok(()) - } -} - -``` - -### Cargo.toml - -```toml -[package] -name = "stylus_erc20_example" -version = "0.1.7" -edition = "2021" -license = "MIT OR Apache-2.0" -keywords = ["arbitrum", "ethereum", "stylus", "alloy"] - -[dependencies] -alloy-primitives = "=0.7.6" -alloy-sol-types = "=0.7.6" -mini-alloc = "0.4.2" -stylus-sdk = "0.6.0" -hex = "0.4.3" - -[dev-dependencies] -tokio = { version = "1.12.0", features = ["full"] } -ethers = "2.0" -eyre = "0.6.8" - -[features] -export-abi = ["stylus-sdk/export-abi"] - -[lib] -crate-type = ["lib", "cdylib"] - -[profile.release] -codegen-units = 1 -strip = true -lto = true -panic = "abort" -opt-level = "s" - -``` diff --git a/docs/stylus-by-example/applications/erc721.mdx b/docs/stylus-by-example/applications/erc721.mdx deleted file mode 100644 index 01efd78b4d..0000000000 --- a/docs/stylus-by-example/applications/erc721.mdx +++ /dev/null @@ -1,504 +0,0 @@ ---- -title: 'ERC-721 • Stylus by Example' -description: 'An example implementation of the ERC-721 token standard in Rust using Arbitrum Stylus.' ---- - -{/* Begin Content */} - -# ERC-721 - -Any contract that follows the [ERC-721 standard](https://eips.ethereum.org/EIPS/eip-721) is an ERC-721 token. - -Here is the interface for ERC-721. - -```solidity -interface ERC721 { - event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId); - event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId); - event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved); - - function balanceOf(address _owner) external view returns (uint256); - function ownerOf(uint256 _tokenId) external view returns (address); - function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable; - function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable; - function transferFrom(address _from, address _to, uint256 _tokenId) external payable; - function approve(address _approved, uint256 _tokenId) external payable; - function setApprovalForAll(address _operator, bool _approved) external; - function getApproved(uint256 _tokenId) external view returns (address); - function isApprovedForAll(address _owner, address _operator) external view returns (bool); -} -``` - -Example implementation of an ERC-721 token contract written in Rust. - -### src/erc721.rs - -import NotForProductionBannerPartial from '../../partials/_not-for-production-banner-partial.mdx'; - - - -```rust -//! Implementation of the ERC-721 standard -//! -//! The eponymous [`Erc721`] type provides all the standard methods, -//! and is intended to be inherited by other contract types. -//! -//! You can configure the behavior of [`Erc721`] via the [`Erc721Params`] trait, -//! which allows specifying the name, symbol, and token uri. -//! -//! Note that this code is unaudited and not fit for production use. - -use alloc::{string::String, vec, vec::Vec}; -use alloy_primitives::{Address, U256, FixedBytes}; -use alloy_sol_types::sol; -use core::{borrow::BorrowMut, marker::PhantomData}; -use stylus_sdk::{ - abi::Bytes, - evm, - msg, - prelude::* -}; - -pub trait Erc721Params { - /// Immutable NFT name. - const NAME: &'static str; - - /// Immutable NFT symbol. - const SYMBOL: &'static str; - - /// The NFT's Uniform Resource Identifier. - fn token_uri(token_id: U256) -> String; -} - -sol_storage! { - /// Erc721 implements all ERC-721 methods - pub struct Erc721 { - /// Token id to owner map - mapping(uint256 => address) owners; - /// User to balance map - mapping(address => uint256) balances; - /// Token id to approved user map - mapping(uint256 => address) token_approvals; - /// User to operator map (the operator can manage all NFTs of the owner) - mapping(address => mapping(address => bool)) operator_approvals; - /// Total supply - uint256 total_supply; - /// Used to allow [`Erc721Params`] - PhantomData phantom; - } -} - -// Declare events and Solidity error types -sol! { - event Transfer(address indexed from, address indexed to, uint256 indexed token_id); - event Approval(address indexed owner, address indexed approved, uint256 indexed token_id); - event ApprovalForAll(address indexed owner, address indexed operator, bool approved); - - // Token id has not been minted, or it has been burned - error InvalidTokenId(uint256 token_id); - // The specified address is not the owner of the specified token id - error NotOwner(address from, uint256 token_id, address real_owner); - // The specified address does not have allowance to spend the specified token id - error NotApproved(address owner, address spender, uint256 token_id); - // Attempt to transfer token id to the Zero address - error TransferToZero(uint256 token_id); - // The receiver address refused to receive the specified token id - error ReceiverRefused(address receiver, uint256 token_id, bytes4 returned); -} - -/// Represents the ways methods may fail. -#[derive(SolidityError)] -pub enum Erc721Error { - InvalidTokenId(InvalidTokenId), - NotOwner(NotOwner), - NotApproved(NotApproved), - TransferToZero(TransferToZero), - ReceiverRefused(ReceiverRefused), -} - -// External interfaces -sol_interface! { - /// Allows calls to the `onERC721Received` method of other contracts implementing `IERC721TokenReceiver`. - interface IERC721TokenReceiver { - function onERC721Received(address operator, address from, uint256 token_id, bytes data) external returns(bytes4); - } -} - -/// Selector for `onERC721Received`, which is returned by contracts implementing `IERC721TokenReceiver`. -const ERC721_TOKEN_RECEIVER_ID: u32 = 0x150b7a02; - -// These methods aren't external, but are helpers used by external methods. -// Methods marked as "pub" here are usable outside of the erc721 module (i.e. they're callable from lib.rs). -impl Erc721 { - /// Requires that msg::sender() is authorized to spend a given token - fn require_authorized_to_spend(&self, from: Address, token_id: U256) -> Result<(), Erc721Error> { - // `from` must be the owner of the token_id - let owner = self.owner_of(token_id)?; - if from != owner { - return Err(Erc721Error::NotOwner(NotOwner { - from, - token_id, - real_owner: owner, - })); - } - - // caller is the owner - if msg::sender() == owner { - return Ok(()); - } - - // caller is an operator for the owner (can manage their tokens) - if self.operator_approvals.getter(owner).get(msg::sender()) { - return Ok(()); - } - - // caller is approved to manage this token_id - if msg::sender() == self.token_approvals.get(token_id) { - return Ok(()); - } - - // otherwise, caller is not allowed to manage this token_id - Err(Erc721Error::NotApproved(NotApproved { - owner, - spender: msg::sender(), - token_id, - })) - } - - /// Transfers `token_id` from `from` to `to`. - /// This function does check that `from` is the owner of the token, but it does not check - /// that `to` is not the zero address, as this function is usable for burning. - pub fn transfer(&mut self, token_id: U256, from: Address, to: Address) -> Result<(), Erc721Error> { - let mut owner = self.owners.setter(token_id); - let previous_owner = owner.get(); - if previous_owner != from { - return Err(Erc721Error::NotOwner(NotOwner { - from, - token_id, - real_owner: previous_owner, - })); - } - owner.set(to); - - // right now working with storage can be verbose, but this will change upcoming version of the Stylus SDK - let mut from_balance = self.balances.setter(from); - let balance = from_balance.get() - U256::from(1); - from_balance.set(balance); - - let mut to_balance = self.balances.setter(to); - let balance = to_balance.get() + U256::from(1); - to_balance.set(balance); - - // cleaning app the approved mapping for this token - self.token_approvals.delete(token_id); - - evm::log(Transfer { from, to, token_id }); - Ok(()) - } - - /// Calls `onERC721Received` on the `to` address if it is a contract. - /// Otherwise it does nothing - fn call_receiver( - storage: &mut S, - token_id: U256, - from: Address, - to: Address, - data: Vec, - ) -> Result<(), Erc721Error> { - if to.has_code() { - let receiver = IERC721TokenReceiver::new(to); - let received = receiver - .on_erc_721_received(&mut *storage, msg::sender(), from, token_id, data.into()) - .map_err(|_e| Erc721Error::ReceiverRefused(ReceiverRefused { - receiver: receiver.address, - token_id, - returned: alloy_primitives::FixedBytes(0_u32.to_be_bytes()), - }))? - .0; - - if u32::from_be_bytes(received) != ERC721_TOKEN_RECEIVER_ID { - return Err(Erc721Error::ReceiverRefused(ReceiverRefused { - receiver: receiver.address, - token_id, - returned: alloy_primitives::FixedBytes(received), - })); - } - } - Ok(()) - } - - /// Transfers and calls `onERC721Received` - pub fn safe_transfer>( - storage: &mut S, - token_id: U256, - from: Address, - to: Address, - data: Vec, - ) -> Result<(), Erc721Error> { - storage.borrow_mut().transfer(token_id, from, to)?; - Self::call_receiver(storage, token_id, from, to, data) - } - - /// Mints a new token and transfers it to `to` - pub fn mint(&mut self, to: Address) -> Result<(), Erc721Error> { - let new_token_id = self.total_supply.get(); - self.total_supply.set(new_token_id + U256::from(1u8)); - self.transfer(new_token_id, Address::default(), to)?; - Ok(()) - } - - /// Burns the token `token_id` from `from` - /// Note that total_supply is not reduced since it's used to calculate the next token_id to mint - pub fn burn(&mut self, from: Address, token_id: U256) -> Result<(), Erc721Error> { - self.transfer(token_id, from, Address::default())?; - Ok(()) - } -} - -// these methods are external to other contracts -#[public] -impl Erc721 { - /// Immutable NFT name. - pub fn name() -> Result { - Ok(T::NAME.into()) - } - - /// Immutable NFT symbol. - pub fn symbol() -> Result { - Ok(T::SYMBOL.into()) - } - - /// The NFT's Uniform Resource Identifier. - #[selector(name = "tokenURI")] - pub fn token_uri(&self, token_id: U256) -> Result { - self.owner_of(token_id)?; // require NFT exist - Ok(T::token_uri(token_id)) - } - - /// Gets the number of NFTs owned by an account. - pub fn balance_of(&self, owner: Address) -> Result { - Ok(self.balances.get(owner)) - } - - /// Gets the owner of the NFT, if it exists. - pub fn owner_of(&self, token_id: U256) -> Result { - let owner = self.owners.get(token_id); - if owner.is_zero() { - return Err(Erc721Error::InvalidTokenId(InvalidTokenId { token_id })); - } - Ok(owner) - } - - /// Transfers an NFT, but only after checking the `to` address can receive the NFT. - /// It includes additional data for the receiver. - #[selector(name = "safeTransferFrom")] - pub fn safe_transfer_from_with_data>( - storage: &mut S, - from: Address, - to: Address, - token_id: U256, - data: Bytes, - ) -> Result<(), Erc721Error> { - if to.is_zero() { - return Err(Erc721Error::TransferToZero(TransferToZero { token_id })); - } - storage - .borrow_mut() - .require_authorized_to_spend(from, token_id)?; - - Self::safe_transfer(storage, token_id, from, to, data.0) - } - - /// Equivalent to [`safe_transfer_from_with_data`], but without the additional data. - /// - /// Note: because Rust doesn't allow multiple methods with the same name, - /// we use the `#[selector]` macro attribute to simulate solidity overloading. - #[selector(name = "safeTransferFrom")] - pub fn safe_transfer_from>( - storage: &mut S, - from: Address, - to: Address, - token_id: U256, - ) -> Result<(), Erc721Error> { - Self::safe_transfer_from_with_data(storage, from, to, token_id, Bytes(vec![])) - } - - /// Transfers the NFT. - pub fn transfer_from(&mut self, from: Address, to: Address, token_id: U256) -> Result<(), Erc721Error> { - if to.is_zero() { - return Err(Erc721Error::TransferToZero(TransferToZero { token_id })); - } - self.require_authorized_to_spend(from, token_id)?; - self.transfer(token_id, from, to)?; - Ok(()) - } - - /// Grants an account the ability to manage the sender's NFT. - pub fn approve(&mut self, approved: Address, token_id: U256) -> Result<(), Erc721Error> { - let owner = self.owner_of(token_id)?; - - // require authorization - if msg::sender() != owner && !self.operator_approvals.getter(owner).get(msg::sender()) { - return Err(Erc721Error::NotApproved(NotApproved { - owner, - spender: msg::sender(), - token_id, - })); - } - self.token_approvals.insert(token_id, approved); - - evm::log(Approval { - approved, - owner, - token_id, - }); - Ok(()) - } - - /// Grants an account the ability to manage all of the sender's NFTs. - pub fn set_approval_for_all(&mut self, operator: Address, approved: bool) -> Result<(), Erc721Error> { - let owner = msg::sender(); - self.operator_approvals - .setter(owner) - .insert(operator, approved); - - evm::log(ApprovalForAll { - owner, - operator, - approved, - }); - Ok(()) - } - - /// Gets the account managing an NFT, or zero if unmanaged. - pub fn get_approved(&mut self, token_id: U256) -> Result { - Ok(self.token_approvals.get(token_id)) - } - - /// Determines if an account has been authorized to managing all of a user's NFTs. - pub fn is_approved_for_all(&mut self, owner: Address, operator: Address) -> Result { - Ok(self.operator_approvals.getter(owner).get(operator)) - } - - /// Whether the NFT supports a given standard. - pub fn supports_interface(interface: FixedBytes<4>) -> Result { - let interface_slice_array: [u8; 4] = interface.as_slice().try_into().unwrap(); - - if u32::from_be_bytes(interface_slice_array) == 0xffffffff { - // special cased in the ERC165 standard - return Ok(false); - } - - const IERC165: u32 = 0x01ffc9a7; - const IERC721: u32 = 0x80ac58cd; - const IERC721_METADATA: u32 = 0x5b5e139f; - - Ok(matches!(u32::from_be_bytes(interface_slice_array), IERC165 | IERC721 | IERC721_METADATA)) - } -} -``` - -### lib.rs - -```rust -// Only run this as a WASM if the export-abi feature is not set. -#![cfg_attr(not(any(feature = "export-abi", test)), no_main)] -extern crate alloc; - -// Modules and imports -mod erc721; - -use alloy_primitives::{U256, Address}; -/// Import the Stylus SDK along with alloy primitive types for use in our program. -use stylus_sdk::{ - msg, prelude::* -}; -use crate::erc721::{Erc721, Erc721Params, Erc721Error}; - -/// Immutable definitions -struct StylusNFTParams; -impl Erc721Params for StylusNFTParams { - const NAME: &'static str = "StylusNFT"; - const SYMBOL: &'static str = "SNFT"; - - fn token_uri(token_id: U256) -> String { - format!("{}{}{}", "https://my-nft-metadata.com/", token_id, ".json") - } -} - -// Define the entrypoint as a Solidity storage object. The sol_storage! macro -// will generate Rust-equivalent structs with all fields mapped to Solidity-equivalent -// storage slots and types. -sol_storage! { - #[entrypoint] - struct StylusNFT { - #[borrow] // Allows erc721 to access StylusNFT's storage and make calls - Erc721 erc721; - } -} - -#[public] -#[inherit(Erc721)] -impl StylusNFT { - /// Mints an NFT - pub fn mint(&mut self) -> Result<(), Erc721Error> { - let minter = msg::sender(); - self.erc721.mint(minter)?; - Ok(()) - } - - /// Mints an NFT to another address - pub fn mint_to(&mut self, to: Address) -> Result<(), Erc721Error> { - self.erc721.mint(to)?; - Ok(()) - } - - /// Burns an NFT - pub fn burn(&mut self, token_id: U256) -> Result<(), Erc721Error> { - // This function checks that msg::sender() owns the specified token_id - self.erc721.burn(msg::sender(), token_id)?; - Ok(()) - } - - /// Total supply - pub fn total_supply(&mut self) -> Result { - Ok(self.erc721.total_supply.get()) - } -} -``` - -### Cargo.toml - -```toml -[package] -name = "stylus_erc721_example" -version = "0.1.7" -edition = "2021" -license = "MIT OR Apache-2.0" -keywords = ["arbitrum", "ethereum", "stylus", "alloy"] - -[dependencies] -alloy-primitives = "=0.7.6" -alloy-sol-types = "=0.7.6" -mini-alloc = "0.4.2" -stylus-sdk = "0.6.0" -hex = "0.4.3" - -[dev-dependencies] -tokio = { version = "1.12.0", features = ["full"] } -ethers = "2.0" -eyre = "0.6.8" - -[features] -export-abi = ["stylus-sdk/export-abi"] - -[lib] -crate-type = ["lib", "cdylib"] - -[profile.release] -codegen-units = 1 -strip = true -lto = true -panic = "abort" -opt-level = "s" -``` diff --git a/docs/stylus-by-example/applications/multi_call.mdx b/docs/stylus-by-example/applications/multi_call.mdx deleted file mode 100644 index c3b6f065c2..0000000000 --- a/docs/stylus-by-example/applications/multi_call.mdx +++ /dev/null @@ -1,129 +0,0 @@ ---- -title: 'Multi Call • Stylus by Example' -description: 'An example implementation of the Multi Call contract in Rust using Arbitrum Stylus.' ---- - -{/* Begin Content */} - -# Multicall - -An Arbitrum Stylus version implementation of [Solidity Multi Call contract](https://solidity-by-example.org/app/multi-call/) that aggregates multiple queries using a for loop and RawCall. - -Example implementation of a Multi Call contract written in Rust: -Here is the interface for TimeLock. - -```solidity -/** - * This file was automatically generated by Stylus and represents a Rust program. - * For more information, please see [The Stylus SDK](https://github.com/OffchainLabs/stylus-sdk-rs). - */ - -// SPDX-License-Identifier: MIT-OR-APACHE-2.0 -pragma solidity ^0.8.23; - -interface IMultiCall { - function multicall(address[] memory addresses, bytes[] memory data) external view returns (bytes[] memory); - - error ArraySizeNotMatch(); - - error CallFailed(uint256); -} -``` - -### src/lib.rs - -import NotForProductionBannerPartial from '../../partials/_not-for-production-banner-partial.mdx'; - - - -```rust -#![cfg_attr(not(feature = "export-abi"), no_main)] -extern crate alloc; - -#[global_allocator] -static ALLOC: mini_alloc::MiniAlloc = mini_alloc::MiniAlloc::INIT; - -use alloy_primitives::U256; -use alloy_sol_types::sol; -use stylus_sdk::{abi::Bytes, alloy_primitives::Address, call::RawCall, prelude::*}; - -#[solidity_storage] -#[entrypoint] -pub struct MultiCall; - -// Declare events and Solidity error types -sol! { - error ArraySizeNotMatch(); - error CallFailed(uint256 call_index); -} - -#[derive(SolidityError)] -pub enum MultiCallErrors { - ArraySizeNotMatch(ArraySizeNotMatch), - CallFailed(CallFailed), -} - -#[external] -impl MultiCall { - pub fn multicall( - &self, - addresses: Vec
, - data: Vec, - ) -> Result, MultiCallErrors> { - let addr_len = addresses.len(); - let data_len = data.len(); - let mut results: Vec = Vec::new(); - if addr_len != data_len { - return Err(MultiCallErrors::ArraySizeNotMatch(ArraySizeNotMatch {})); - } - for i in 0..addr_len { - let result = RawCall::new().call(addresses[i], data[i].to_vec().as_slice()) - .map_err(|_| MultiCallErrors::CallFailed(CallFailed { call_index: U256::from(i) }))?; - results.push(result.into()); -} - Ok(results) - } -} - -``` - -### Cargo.toml - -```toml -[package] -name = "stylus-multi-call-contract" -version = "0.1.5" -edition = "2021" -license = "MIT OR Apache-2.0" -keywords = ["arbitrum", "ethereum", "stylus", "alloy"] -description = "Stylus multi call example" - -[dependencies] -alloy-primitives = "0.3.1" -alloy-sol-types = "0.3.1" -mini-alloc = "0.4.2" -stylus-sdk = "0.5.0" -hex = "0.4.3" - -[dev-dependencies] -tokio = { version = "1.12.0", features = ["full"] } -ethers = "2.0" -eyre = "0.6.8" - -[features] -export-abi = ["stylus-sdk/export-abi"] - -[[bin]] -name = "stylus-multi-call" -path = "src/main.rs" - -[lib] -crate-type = ["lib", "cdylib"] - -[profile.release] -codegen-units = 1 -strip = true -lto = true -panic = "abort" -opt-level = "s" -``` diff --git a/docs/stylus-by-example/applications/sidebar.js b/docs/stylus-by-example/applications/sidebar.js deleted file mode 100644 index 2f47078a43..0000000000 --- a/docs/stylus-by-example/applications/sidebar.js +++ /dev/null @@ -1,27 +0,0 @@ -// @ts-check -/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const sidebar = { - "items": [ - { - "type": "doc", - "id": "stylus-by-example/applications/erc20", - "label": "Erc20" - }, - { - "type": "doc", - "id": "stylus-by-example/applications/erc721", - "label": "Erc721" - }, - { - "type": "doc", - "id": "stylus-by-example/applications/multi_call", - "label": "Multi Call" - }, - { - "type": "doc", - "id": "stylus-by-example/applications/vending_machine", - "label": "Vending Machine" - } - ] -}; -module.exports = sidebar.items; \ No newline at end of file diff --git a/docs/stylus-by-example/applications/vending_machine.mdx b/docs/stylus-by-example/applications/vending_machine.mdx deleted file mode 100644 index 7beab1a80b..0000000000 --- a/docs/stylus-by-example/applications/vending_machine.mdx +++ /dev/null @@ -1,145 +0,0 @@ ---- -title: 'Vending Machine • Stylus by Example' -description: 'An example implementation of the Vending Machine in Rust using Arbitrum Stylus.' ---- - -{/* Begin Content */} - -# Vending Machine - -An example project for writing Arbitrum Stylus programs in Rust using the [stylus-sdk](https://github.com/OffchainLabs/stylus-sdk-rs). It includes a Rust implementation of a vending machine Ethereum smart contract. - -- distribute Cupcakes to any given address -- count Cupcakes balance of any given address - -Here is the interface for Vending Machine. - -```solidity -interface IVendingMachine { - // Function to distribute a cupcake to a user - function giveCupcakeTo(address userAddress) external returns (bool); - - // Getter function for the cupcake balance of a user - function getCupcakeBalanceFor(address userAddress) external view returns (uint); -} -``` - -Example implementation of the Vending Machine contract written in Rust. - -### src/lib.rs - -import NotForProductionBannerPartial from '../../partials/_not-for-production-banner-partial.mdx'; - - - -```rust -//! -//! Stylus Cupcake Example -//! -//! The program is ABI-equivalent with Solidity, which means you can call it from both Solidity and Rust. -//! To do this, run `cargo stylus export-abi`. -//! -//! Note: this code is a template-only and has not been audited. -//! - -// Allow `cargo stylus export-abi` to generate a main function if the "export-abi" feature is enabled. -#![cfg_attr(not(feature = "export-abi"), no_main)] -extern crate alloc; - -use alloy_primitives::{Address, Uint}; -// Import items from the SDK. The prelude contains common traits and macros. -use stylus_sdk::alloy_primitives::U256; -use stylus_sdk::prelude::*; -use stylus_sdk::{block, console}; - -// Define persistent storage using the Solidity ABI. -// `VendingMachine` will be the entrypoint for the contract. -sol_storage! { - #[entrypoint] - pub struct VendingMachine { - // Mapping from user addresses to their cupcake balances. - mapping(address => uint256) cupcake_balances; - // Mapping from user addresses to the last time they received a cupcake. - mapping(address => uint256) cupcake_distribution_times; - } -} - -// Declare that `VendingMachine` is a contract with the following external methods. -#[public] -impl VendingMachine { - // Give a cupcake to the specified user if they are eligible (i.e., if at least 5 seconds have passed since their last cupcake). - pub fn give_cupcake_to(&mut self, user_address: Address) -> bool { - // Get the last distribution time for the user. - let last_distribution = self.cupcake_distribution_times.get(user_address); - // Calculate the earliest next time the user can receive a cupcake. - let five_seconds_from_last_distribution = last_distribution + U256::from(5); - - // Get the current block timestamp. - let current_time = block::timestamp(); - // Check if the user can receive a cupcake. - let user_can_receive_cupcake = - five_seconds_from_last_distribution <= Uint::<256, 4>::from(current_time); - - if user_can_receive_cupcake { - // Increment the user's cupcake balance. - let mut balance_accessor = self.cupcake_balances.setter(user_address); - let balance = balance_accessor.get() + U256::from(1); - balance_accessor.set(balance); - - // Update the distribution time to the current time. - let mut time_accessor = self.cupcake_distribution_times.setter(user_address); - let new_distribution_time = block::timestamp(); - time_accessor.set(Uint::<256, 4>::from(new_distribution_time)); - return true; - } else { - // User must wait before receiving another cupcake. - console!( - "HTTP 429: Too Many Cupcakes (you must wait at least 5 seconds between cupcakes)" - ); - return false; - } - } - - // Get the cupcake balance for the specified user. - pub fn get_cupcake_balance_for(&self, user_address: Address) -> Uint<256, 4> { - // Return the user's cupcake balance from storage. - return self.cupcake_balances.get(user_address); - } -} -``` - -### Cargo.toml - -```toml -[package] -name = "stylus_cupcake_example" -version = "0.1.7" -edition = "2021" -license = "MIT OR Apache-2.0" -keywords = ["arbitrum", "ethereum", "stylus", "alloy"] - -[dependencies] -alloy-primitives = "=0.7.6" -alloy-sol-types = "=0.7.6" -mini-alloc = "0.4.2" -stylus-sdk = "0.6.0" -hex = "0.4.3" - -[dev-dependencies] -tokio = { version = "1.12.0", features = ["full"] } -ethers = "2.0" -eyre = "0.6.8" - -[features] -export-abi = ["stylus-sdk/export-abi"] - -[lib] -crate-type = ["lib", "cdylib"] - -[profile.release] -codegen-units = 1 -strip = true -lto = true -panic = "abort" -opt-level = "s" -``` diff --git a/docs/stylus-by-example/basic_examples/abi_decode.mdx b/docs/stylus-by-example/basic_examples/abi_decode.mdx deleted file mode 100644 index c82862dd9e..0000000000 --- a/docs/stylus-by-example/basic_examples/abi_decode.mdx +++ /dev/null @@ -1,139 +0,0 @@ ---- -title: 'ABI Encode • Stylus by Example' -description: 'A simple solidity ABI encode and decode example' ---- - -{/* Begin Content */} - -# ABI Decode - -The `decode` can not be used for `encode_packed` data because it ignores padding when encode. (For more information you can refer to [ABI Encode](./abi_encode)) - -So here we show an example for using `decode` on data encoded with `abi_encode_sequence`: - -import NotForProductionBannerPartial from '../../partials/_not-for-production-banner-partial.mdx'; - - - -```rust -// This should always return true -pub fn encode_and_decode( - &self, - target: Address, - value: U256, - func: String, - data: Bytes, - timestamp: U256 -) -> Result { - // define sol types tuple - type TxIdHashType = (SOLAddress, Uint<256>, SOLString, SOLBytes, Uint<256>); - // because the abi_encode_sequence will return alloy_primitives::Bytes rather than stylus_sdk::bytes, so we need to make sure the input and return types are the same - let primative_data = alloy_primitives::Bytes::copy_from_slice(&data); - // set the tuple - let tx_hash_data = (target, value, func, primative_data, timestamp); - // encode the tuple - let tx_hash_data_encode = TxIdHashType::abi_encode_sequence(&tx_hash_data); - - let validate = true; - - // Check the result - match TxIdHashType::abi_decode_sequence(&tx_hash_data_encode, validate) { - Ok(res) => Ok(res == tx_hash_data), - Err(_) => { - return Err(HasherError::DecodedFailed(DecodedFailed{})); - }, - } -} -``` - -# Full Example code: - -### src/lib.rs - -```rust - -#![cfg_attr(not(any(feature = "export-abi", test)), no_main)] -extern crate alloc; - - -/// Import items from the SDK. The prelude contains common traits and macros. -use stylus_sdk::{alloy_primitives::{U256, Address}, prelude::*}; -// Because the naming of `alloy_primitives` and `alloy_sol_types` is the same, we need to rename the types in `alloy_sol_types`. -use alloy_sol_types::{sol_data::{Address as SOLAddress, *}, SolType, sol}; - - -// Define error -sol! { - error DecodedFailed(); -} - -// Error types for the MultiSig contract -#[derive(SolidityError)] -pub enum DecoderError{ - DecodedFailed(DecodedFailed) -} - -#[storage] -#[entrypoint] -pub struct Decoder; - - -/// Declare that `Decoder` is a contract with the following external methods. -#[public] -impl Decoder { - // This should always return true - pub fn encode_and_decode( - &self, - address: Address, - amount: U256 - ) -> Result { - // define sol types tuple - type TxIdHashType = (SOLAddress, Uint<256>); - // set the tuple - let tx_hash_data = (address, amount); - // encode the tuple - let tx_hash_data_encode = TxIdHashType::abi_encode_params(&tx_hash_data); - - let validate = true; - - // Check the result - match TxIdHashType::abi_decode_params(&tx_hash_data_encode, validate) { - Ok(res) => Ok(res == tx_hash_data), - Err(_) => { - return Err(DecoderError::DecodedFailed(DecodedFailed{})); - }, - } - } - -} -``` - -### Cargo.toml - -```rust -[package] -name = "stylus-decode-hashing" -version = "0.1.0" -edition = "2021" - -[dependencies] -alloy-primitives = "=0.7.6" -alloy-sol-types = "=0.7.6" -mini-alloc = "0.4.2" -stylus-sdk = "0.5.1" - -[features] -export-abi = ["stylus-sdk/export-abi"] -debug = ["stylus-sdk/debug"] - -[lib] -crate-type = ["lib", "cdylib"] - -[profile.release] -codegen-units = 1 -strip = true -lto = true -panic = "abort" -opt-level = "s" - -``` diff --git a/docs/stylus-by-example/basic_examples/abi_encode.mdx b/docs/stylus-by-example/basic_examples/abi_encode.mdx deleted file mode 100644 index f60d9830a6..0000000000 --- a/docs/stylus-by-example/basic_examples/abi_encode.mdx +++ /dev/null @@ -1,218 +0,0 @@ ---- -title: 'ABI Decode • Stylus by Example' -description: 'A simple solidity ABI encode and decode example' ---- - -{/* Begin Content */} - -# ABI Encode - -The `ABI Encode` has 2 types which are [`encode`](https://docs.soliditylang.org/en/latest/abi-spec.html#strict-encoding-mode) and [`encode_packed`](https://docs.soliditylang.org/en/latest/abi-spec.html#non-standard-packed-mode). - -- `encode` will concatenate all values and add padding to fit into 32 bytes for each values. -- `encode_packed` will concatenate all values in the exact byte representations without padding. (For example, `encode_packed("a", "bc") == encode_packed("ab", "c")`) - -Suppose we have a tuple of values: `(target, value, func, data, timestamp)` to encode, and their `alloy primitives type` are `(Address, U256, String, Bytes, U256)`. - -Firstly we need to import those types we need from `alloy_primitives`, `stylus_sdk::abi` and `alloc::string`: - -import NotForProductionBannerPartial from '../../partials/_not-for-production-banner-partial.mdx'; - - - -```rust -// Import items from the SDK. The prelude contains common traits and macros. -use stylus_sdk::{alloy_primitives::{U256, Address, FixedBytes}, abi::Bytes, prelude::*}; -// Import String from alloc -use alloc::string::String; -``` - -Secondly because we will use the method [`abi_encode_sequence`](https://docs.rs/alloy-sol-types/latest/alloy_sol_types/trait.SolValue.html#method.abi_encode_sequence) and [`abi_encode_packed`](https://docs.rs/alloy-sol-types/latest/alloy_sol_types/trait.SolValue.html#method.abi_encode_packed) under `alloy_sol_types` to encode data, we also need to import the types from `alloy_sol_types`: - -```rust -// Becauce the naming of alloy_primitives and alloy_sol_types is the same, so we need to re-name the types in alloy_sol_types -use alloy_sol_types::{sol_data::{Address as SOLAddress, String as SOLString, Bytes as SOLBytes, *}, SolType}; -``` - -## encode - -Then `encode` them: - -```rust -// define sol types tuple -type TxIdHashType = (SOLAddress, Uint<256>, SOLString, SOLBytes, Uint<256>); -// set the tuple -let tx_hash_data = (target, value, func, data, timestamp); -// encode the tuple -let tx_hash_bytes = TxIdHashType::abi_encode_sequence(&tx_hash_data); -``` - -## encode_packed - -There are 2 methods to `encode_packed` data: - -1. `encode_packed` them: - -```rust -// define sol types tuple -type TxIdHashType = (SOLAddress, Uint<256>, SOLString, SOLBytes, Uint<256>); -// set the tuple -let tx_hash_data = (target, value, func, data, timestamp); -// encode the tuple -let tx_hash_data_encode_packed = TxIdHashType::abi_encode_packed(&tx_hash_data); -``` - -2. We can also use the following method to `encode_packed` them: - -```rust -let tx_hash_data_encode_packed = [&target.to_vec(), &value.to_be_bytes_vec(), func.as_bytes(), &data.to_vec(), ×tamp.to_be_bytes_vec()].concat(); -``` - -# Full Example code: - -### src/main.rs - -```rust -// Allow `cargo stylus export-abi` to generate a main function. -#![cfg_attr(not(feature = "export-abi"), no_main)] -extern crate alloc; - - -/// Import items from the SDK. The prelude contains common traits and macros. -use stylus_sdk::{alloy_primitives::{U256, Address, FixedBytes}, abi::Bytes, prelude::*}; -use alloc::string::String; -// Becauce the naming of alloy_primitives and alloy_sol_types is the same, so we need to re-name the types in alloy_sol_types -use alloy_sol_types::{sol_data::{Address as SOLAddress, String as SOLString, Bytes as SOLBytes, *}, SolType}; -use sha3::{Digest, Keccak256}; - -// Define some persistent storage using the Solidity ABI. -// `Encoder` will be the entrypoint. -#[storage] -#[entrypoint] -pub struct Encoder; - -impl Encoder { - fn keccak256(&self, data: Bytes) -> FixedBytes<32> { - // prepare hasher - let mut hasher = Keccak256::new(); - // populate the data - hasher.update(data); - // hashing with keccack256 - let result = hasher.finalize(); - // convert the result hash to FixedBytes<32> - let result_vec = result.to_vec(); - FixedBytes::<32>::from_slice(&result_vec) - } -} - -/// Declare that `Encoder` is a contract with the following external methods. -#[public] -impl Encoder { - - // Encode the data and hash it - pub fn encode( - &self, - target: Address, - value: U256, - func: String, - data: Bytes, - timestamp: U256 - ) -> Vec { - // define sol types tuple - type TxIdHashType = (SOLAddress, Uint<256>, SOLString, SOLBytes, Uint<256>); - // set the tuple - let tx_hash_data = (target, value, func, data, timestamp); - // encode the tuple - let tx_hash_data_encode = TxIdHashType::abi_encode_params(&tx_hash_data); - tx_hash_data_encode - } - - // Packed encode the data and hash it, the same result with the following one - pub fn packed_encode( - &self, - target: Address, - value: U256, - func: String, - data: Bytes, - timestamp: U256 - )-> Vec { - // define sol types tuple - type TxIdHashType = (SOLAddress, Uint<256>, SOLString, SOLBytes, Uint<256>); - // set the tuple - let tx_hash_data = (target, value, func, data, timestamp); - // encode the tuple - let tx_hash_data_encode_packed = TxIdHashType::abi_encode_packed(&tx_hash_data); - tx_hash_data_encode_packed - } - - // Packed encode the data and hash it, the same result with the above one - pub fn packed_encode_2( - &self, - target: Address, - value: U256, - func: String, - data: Bytes, - timestamp: U256 - )-> Vec { - // set the data to arrary and concat it directly - let tx_hash_data_encode_packed = [&target.to_vec(), &value.to_be_bytes_vec(), func.as_bytes(), &data.to_vec(), ×tamp.to_be_bytes_vec()].concat(); - tx_hash_data_encode_packed - } - - - // The func example: "transfer(address,uint256)" - pub fn encode_with_signature( - &self, - func: String, - address: Address, - amount: U256 - ) -> Vec { - type TransferType = (SOLAddress, Uint<256>); - let tx_data = (address, amount); - let data = TransferType::abi_encode_params(&tx_data); - // Get function selector - let hashed_function_selector = self.keccak256(func.as_bytes().to_vec().into()); - // Combine function selector and input data (use abi_packed way) - let calldata = [&hashed_function_selector[..4], &data].concat(); - calldata - } - -} -``` - -### Cargo.toml - -```toml -[package] -name = "stylus-encode-hashing" -version = "0.1.7" -edition = "2021" -license = "MIT OR Apache-2.0" -keywords = ["arbitrum", "ethereum", "stylus", "alloy"] - -[dependencies] -alloy-primitives = "=0.7.6" -alloy-sol-types = "=0.7.6" -mini-alloc = "0.4.2" -stylus-sdk = "0.6.0" -hex = "0.4.3" -sha3 = "0.10" - -[dev-dependencies] -tokio = { version = "1.12.0", features = ["full"] } -ethers = "2.0" -eyre = "0.6.8" - -[features] -export-abi = ["stylus-sdk/export-abi"] - -[lib] -crate-type = ["lib", "cdylib"] - -[profile.release] -codegen-units = 1 -strip = true -lto = true -panic = "abort" -opt-level = "s" -``` diff --git a/docs/stylus-by-example/basic_examples/bytes_in_bytes_out.mdx b/docs/stylus-by-example/basic_examples/bytes_in_bytes_out.mdx deleted file mode 100644 index 370875774c..0000000000 --- a/docs/stylus-by-example/basic_examples/bytes_in_bytes_out.mdx +++ /dev/null @@ -1,54 +0,0 @@ ---- -title: 'Bytes In, Bytes Out • Stylus by Example' -description: 'A simple bytes in, bytes out Arbitrum Stylus Rust contract that shows a minimal `entrypoint` function.' ---- - -{/* Begin Content */} - -# Bytes In, Bytes Out - -This is a simple bytes in, bytes out contract that shows a minimal `entrypoint` function (denoted by the `#[entrypoint]` proc macro). If your smart contract just has one primary function, like computing a cryptographic hash, this can be a great model because it strips out the SDK and acts like a pure function or Unix-style app. - -### src/main.rs - -import NotForProductionBannerPartial from '../../partials/_not-for-production-banner-partial.mdx'; - - - -```rust -#![cfg_attr(not(feature = "export-abi"), no_main)] - -extern crate alloc; -use alloc::vec::Vec; - -use stylus_sdk::stylus_proc::entrypoint; - -#[entrypoint] -fn user_main(input: Vec) -> Result, Vec> { - Ok(input) -} -``` - -### Cargo.toml - -```toml -[package] -name = "bytes_in_bytes_out" -version = "0.1.7" -edition = "2021" - -[dependencies] -stylus-sdk = "0.6.0" - -[features] -export-abi = ["stylus-sdk/export-abi"] - -[profile.release] -codegen-units = 1 -strip = true -lto = true -panic = "abort" -opt-level = "s" - -[workspace] -``` diff --git a/docs/stylus-by-example/basic_examples/constants.mdx b/docs/stylus-by-example/basic_examples/constants.mdx deleted file mode 100644 index 5cc8cf68b5..0000000000 --- a/docs/stylus-by-example/basic_examples/constants.mdx +++ /dev/null @@ -1,102 +0,0 @@ ---- -title: 'Constants • Stylus by Example' -description: "How to declare constants in your Rust smart contracts using Arbitrum's Stylus SDK" ---- - -{/* Begin Content */} - -# Constants - -Constants are values that are bound to a name and cannot change. They are always immutable. In Rust, constants are declared with the `const` keyword. Unlike variables declared with the `let` keyword, constants _must_ be annotated with their type. - -Constants are valid for the entire length of the transaction. They are essentially _inlined_ wherever they are used, meaning that their value is copied directly into whatever context invokes them. - -Since their value is hardcoded, they can save on gas cost as their value does not need to be fetched from storage. - -## Learn More - -- [Rust docs - Constant items](https://doc.rust-lang.org/reference/items/constant-items.html) -- [Solidity docs - Constant variables](https://docs.soliditylang.org/en/v0.8.19/contracts.html#constant) - -### src/lib.rs - -import NotForProductionBannerPartial from '../../partials/_not-for-production-banner-partial.mdx'; - - - -```rust -// Only run this as a WASM if the export-abi feature is not set. -#![cfg_attr(not(any(feature = "export-abi", test)), no_main)] -extern crate alloc; - -use alloc::vec; -use alloc::vec::Vec; - -use stylus_sdk::alloy_primitives::Address; -use stylus_sdk::prelude::*; -use stylus_sdk::storage::StorageAddress; - -const OWNER: &str = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"; - -#[storage] -#[entrypoint] -pub struct Contract { - owner: StorageAddress, -} - -#[public] -impl Contract { - pub fn init(&mut self) -> Result<(), Vec> { - // Parse the const &str as a local Address variable - let owner_address = Address::parse_checksummed(OWNER, None).expect("Invalid address"); - - // Save the result as the owner - self.owner.set(owner_address); - - Ok(()) - } - pub fn owner(&self) -> Result> { - let owner_address = self.owner.get(); - - Ok(owner_address) - } -} - -``` - -### Cargo.toml - -```toml -[package] -name = "stylus_constants_example" -version = "0.1.7" -edition = "2021" -license = "MIT OR Apache-2.0" -keywords = ["arbitrum", "ethereum", "stylus", "alloy"] - -[dependencies] -alloy-primitives = "=0.7.6" -alloy-sol-types = "=0.7.6" -mini-alloc = "0.4.2" -stylus-sdk = "0.6.0" -hex = "0.4.3" - -[dev-dependencies] -tokio = { version = "1.12.0", features = ["full"] } -ethers = "2.0" -eyre = "0.6.8" - -[features] -export-abi = ["stylus-sdk/export-abi"] - -[lib] -crate-type = ["lib", "cdylib"] - -[profile.release] -codegen-units = 1 -strip = true -lto = true -panic = "abort" -opt-level = "s" - -``` diff --git a/docs/stylus-by-example/basic_examples/errors.mdx b/docs/stylus-by-example/basic_examples/errors.mdx deleted file mode 100644 index 7ecdb7d8ab..0000000000 --- a/docs/stylus-by-example/basic_examples/errors.mdx +++ /dev/null @@ -1,196 +0,0 @@ ---- -title: 'Errors • Stylus by Example' -description: 'How to define and use Errors on Stylus Rust smart contracts' ---- - -{/* Begin Content */} - -# Errors - -In Rust Stylus contracts, error handling is a crucial aspect of writing robust and reliable smart contracts. Rust differentiates between recoverable and unrecoverable errors. Recoverable errors are represented using the `Result` type, which can either be `Ok`, indicating success, or `Err`, indicating failure. This allows developers to manage errors gracefully and maintain control over the flow of execution. Unrecoverable errors are handled with the `panic!` macro, which stops execution, unwinds the stack, and returns a dataless error. - -In Stylus contracts, error types are often explicitly defined, providing clear and structured ways to handle different failure scenarios. This structured approach promotes better error management, ensuring that contracts are secure, maintainable, and behave predictably under various conditions. Similar to Solidity and EVM, errors in Stylus will undo all changes made to the state during a transaction by reverting the transaction. Thus, there are two main types of errors in Rust Stylus contracts: - -- **Recoverable Errors**: The Stylus SDK provides features that make using recoverable errors in Rust Stylus contracts convenient. This type of error handling is strongly recommended for Stylus contracts. -- **Unrecoverable Errors**: These can be defined similarly to Rust code but are not recommended for smart contracts if recoverable errors can be used instead. - -## Learn More - -- [Solidity docs: Expressions and Control Structures](https://docs.soliditylang.org/en/latest/control-structures.html) -- [`#[derive(SolidityError)]`](https://docs.rs/stylus-sdk/latest/stylus_sdk/prelude/derive.SolidityError.html) -- [`alloy_sol_types::SolError`](https://docs.rs/alloy-sol-types/latest/alloy_sol_types/trait.SolError.html) -- [`Error handling: Rust book`](https://doc.rust-lang.org/book/ch09-00-error-handling.html) - -## Recoverable Errors - -Recoverable errors are represented using the `Result` type, which can either be `Ok`, indicating success, or `Err`, indicating failure. The Stylus SDK provides tools to define custom error types and manage recoverable errors effectively. - -#### Example: Recoverable Errors - -Here's a simplified Rust Stylus contract demonstrating how to define and handle recoverable errors: - -import NotForProductionBannerPartial from '../../partials/_not-for-production-banner-partial.mdx'; - - - -```rust -#![cfg_attr(not(feature = "export-abi"), no_main)] -extern crate alloc; - - -use alloy_sol_types::sol; -use stylus_sdk::{abi::Bytes, alloy_primitives::{Address, U256}, call::RawCall, prelude::*}; - -#[storage] -#[entrypoint] -pub struct MultiCall; - -// Declare events and Solidity error types -sol! { - error ArraySizeNotMatch(); - error CallFailed(uint256 call_index); -} - -#[derive(SolidityError)] -pub enum MultiCallErrors { - ArraySizeNotMatch(ArraySizeNotMatch), - CallFailed(CallFailed), -} - -#[public] -impl MultiCall { - pub fn multicall( - &self, - addresses: Vec
, - data: Vec, - ) -> Result, MultiCallErrors> { - let addr_len = addresses.len(); - let data_len = data.len(); - let mut results: Vec = Vec::new(); - if addr_len != data_len { - return Err(MultiCallErrors::ArraySizeNotMatch(ArraySizeNotMatch {})); - } - for i in 0..addr_len { - let result: Result, Vec> = - RawCall::new().call(addresses[i], data[i].to_vec().as_slice()); - let data = match result { - Ok(data) => data, - Err(_data) => return Err(MultiCallErrors::CallFailed(CallFailed { call_index: U256::from(i) })), - }; - results.push(data.into()) - } - Ok(results) - } -} -``` - -- **Using `SolidityError` Derive Macro**: The `#[derive(SolidityError)]` attribute is used for the `MultiCallErrors` enum, automatically implementing the necessary traits for error handling. -- **Defining Errors**: Custom errors `ArraySizeNotMatch` and `CallFailed` is declared in `MultiCallErrors` enum. `CallFailed` error includes a `call_index` parameter to indicate which call failed. -- **ArraySizeNotMatch Error Handling**: The `multicall` function returns `ArraySizeNotMatch` if the size of addresses and data vectors are not equal. -- **CallFailed Error Handling**: The `multicall` function returns a `CallFailed` error with the index of the failed call if any call fails. Note that we're using match to check if the result of the call is an error or a return data. We'll describe match pattern in the further sections. - -## Unrecoverable Errors - -Here are various ways to handle such errors in the `multicall` function, which calls multiple addresses and panics in different scenarios: - -### Using `panic!` - -Directly panics if the call fails, including the index of the failed call. - -```rust - for i in 0..addr_len { - let result = RawCall::new().call(addresses[i], data[i].to_vec().as_slice()); - let data = match result { - Ok(data) => data, - Err(_data) => panic!("Call to address {:?} failed at index {}", addresses[i], i), - }; - results.push(data.into()); -} -``` - -Handling Call Failure with `panic!`: The function panics if any call fails and the transaction will be reverted without any data. - -### Using `unwrap` - -Uses `unwrap` to handle the result, panicking if the call fails. - -```rust - for i in 0..addr_len { - let result = RawCall::new().call(addresses[i], data[i].to_vec().as_slice()).unwrap(); - results.push(result.into()); -} -``` - -Handling Call Failure with `unwrap`: The function uses `unwrap` to panic if any call fails, including the index of the failed call. - -### Using `match` - -Uses a `match` statement to handle the result of `call`, panicking if the call fails. - -```rust - for i in 0..addr_len { - let result = RawCall::new().call(addresses[i], data[i].to_vec().as_slice()); - let data = match result { - Ok(data) => data, - Err(_data) => return Err(MultiCallErrors::CallFailed(CallFailed { call_index: U256::from(i) })), - }; - results.push(data.into()); -} -``` - -Handling Call Failure with `match`: The function uses a `match` statement to handle the result of `call`, returning error if any call fails. - -### Using the `?` Operator - -Uses the `?` operator to propagate the error if the call fails, including the index of the failed call. - -```rust - for i in 0..addr_len { - let result = RawCall::new().call(addresses[i], data[i].to_vec().as_slice()) - .map_err(|_| MultiCallErrors::CallFailed(CallFailed { call_index: U256::from(i) }))?; - results.push(result.into()); -} -``` - -Handling Call Failure with `?` Operator: The function uses the `?` operator to propagate the error if any call fails, including the index of the failed call. - -Each method demonstrates a different way to handle unrecoverable errors in the `multicall` function of a Rust Stylus contract, providing a comprehensive approach to error management. - -**Note** that as mentioned above, it is strongly recommended to use custom error handling instead of unrecoverable error handling. - -## Boilerplate - -### src/lib.rs - -The lib.rs code can be found at the top of the page in the recoverable error example section. - -### Cargo.toml - -```toml -[package] -name = "stylus-multicall-contract" -version = "0.1.7" -edition = "2021" - -[dependencies] -alloy-primitives = "=0.7.6" -alloy-sol-types = "=0.7.6" -stylus-sdk = "0.6.0" -hex = "0.4.3" - -[dev-dependencies] -tokio = { version = "1.12.0", features = ["full"] } -ethers = "2.0" -eyre = "0.6.8" - -[features] -export-abi = ["stylus-sdk/export-abi"] - -[[bin]] -name = "stylus-multicall-contract" -path = "src/main.rs" - -[lib] -crate-type = ["lib", "cdylib"] - -``` diff --git a/docs/stylus-by-example/basic_examples/events.mdx b/docs/stylus-by-example/basic_examples/events.mdx deleted file mode 100644 index 774f5cc6d5..0000000000 --- a/docs/stylus-by-example/basic_examples/events.mdx +++ /dev/null @@ -1,225 +0,0 @@ ---- -title: 'Events • Stylus by Example' -description: 'How to log events to the chain using the Arbitrum Stylus Rust SDK.' ---- - -{/* Begin Content */} - -# Events - -Events allow for data to be logged publicly to the blockchain. Log entries provide the contract's address, a series of up to four topics, and some arbitrary length binary data. The Stylus Rust SDK provides a few ways to publish event logs described below. - -## Learn More - -- [Solidity docs: Events](https://docs.soliditylang.org/en/v0.8.19/abi-spec.html#events) -- [`stylus_sdk::evm::log`](https://docs.rs/stylus-sdk/latest/stylus_sdk/evm/fn.log.html) -- [`alloy_sol_types::SolEvent`](https://docs.rs/alloy-sol-types/0.3.1/alloy_sol_types/trait.SolEvent.html) - -## Log - -Using the `evm::log` function in the Stylus SDK is the preferred way to log events. It ensures that an event will be logged in a Solidity ABI-compatible format. The `log` function takes any type that implements Alloy `SolEvent` trait. It's not recommended to attempt to implement this trait on your own. Instead, make use of the provided `sol!` macro to declare your Events and their schema using Solidity-style syntax to declare the parameter types. Alloy will create ABI-compatible Rust types which you can instantiate and pass to the `evm::log` function. - -### Log Usage - -import NotForProductionBannerPartial from '../../partials/_not-for-production-banner-partial.mdx'; - - - -```rust -// sol! macro event declaration -// Up to 3 parameters can be indexed. -// Indexed parameters helps you filter the logs efficiently -sol! { - event Log(address indexed sender, string message); - event AnotherLog(); -} - -#[storage] -#[entrypoint] -pub struct Events {} - -#[public] -impl Events { -fn user_main(_input: Vec) -> ArbResult { - // emits a 'Log' event, defined above in the sol! macro - evm::log(Log { - sender: Address::from([0x11; 20]), - message: "Hello world!".to_string(), - }); - - // no data, but 'AnotherLog' event will still emit to the chain - evm::log(AnotherLog {}); - - Ok(vec![]) -} -} -``` - -## Raw Log - -The `evm::raw_log` affordance offers the ability to send anonymous events that do not necessarily conform to the Solidity ABI. Instead, up to four raw 32-byte indexed topics are published along with any arbitrary bytes appended as data. - -**NOTE**: It's still possible to achieve Solidity ABI compatibility using this construct. To do so you'll have to manually compute the ABI signature for the event, [following the equation set in the Solidity docs](https://docs.soliditylang.org/en/v0.8.19/abi-spec.html#events). The result of that should be assigned to `TOPIC_0`, the first topic in the slice passed to `raw_log`. - -### Raw Log Usage - -```rust -// set up local variables -let user = Address::from([0x22; 20]); -let balance = U256::from(10_000_000); - -// declare up to 4 topics -// topics must be of type FixedBytes<32> -let topics = &[user.into_word()]; - -// store non-indexed data in a byte Vec -let mut data: Vec = vec![]; -// to_be_bytes means 'to big endian bytes' -data.extend_from_slice(balance.to_be_bytes::<32>().to_vec().as_slice()); - -// unwrap() here 'consumes' the Result -evm::raw_log(topics.as_slice(), data.as_ref()).unwrap(); -``` - -## Result - -Combining the above examples into the boiler plate provided below this section, deploying to a Stylus chain and then invoking the deployed contract will result in the following three events logged to the chain: - -### logs - -```json -[ - { - "address": "0x6cf4a18ac8efd6b0b99d3200c4fb9609dd60d4b3", - "topics": [ - "0x0738f4da267a110d810e6e89fc59e46be6de0c37b1d5cd559b267dc3688e74e0", - "0x0000000000000000000000001111111111111111111111111111111111111111" - ], - "data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c48656c6c6f20776f726c64210000000000000000000000000000000000000000", - "blockHash": "0xfef880025dc87b5ab4695a0e1a6955dd7603166ecba79ce0f503a568b2ec8940", - "blockNumber": "0x94", - "transactionHash": "0xc7318dae2164eb441fb80f5b869f844e3e97ae83c24a4639d46ec4d915a30818", - "transactionIndex": "0x1", - "logIndex": "0x0", - "removed": false - }, - { - "address": "0x6cf4a18ac8efd6b0b99d3200c4fb9609dd60d4b3", - "topics": ["0xfe1a3ad11e425db4b8e6af35d11c50118826a496df73006fc724cb27f2b99946"], - "data": "0x", - "blockHash": "0xfef880025dc87b5ab4695a0e1a6955dd7603166ecba79ce0f503a568b2ec8940", - "blockNumber": "0x94", - "transactionHash": "0xc7318dae2164eb441fb80f5b869f844e3e97ae83c24a4639d46ec4d915a30818", - "transactionIndex": "0x1", - "logIndex": "0x1", - "removed": false - }, - { - "address": "0x6cf4a18ac8efd6b0b99d3200c4fb9609dd60d4b3", - "topics": ["0x0000000000000000000000002222222222222222222222222222222222222222"], - "data": "0x0000000000000000000000000000000000000000000000000000000000989680", - "blockHash": "0xfef880025dc87b5ab4695a0e1a6955dd7603166ecba79ce0f503a568b2ec8940", - "blockNumber": "0x94", - "transactionHash": "0xc7318dae2164eb441fb80f5b869f844e3e97ae83c24a4639d46ec4d915a30818", - "transactionIndex": "0x1", - "logIndex": "0x2", - "removed": false - } -] -``` - -## Boilerplate - -### src/lib.rs - -```rust -// Only run this as a WASM if the export-abi feature is not set. -#![cfg_attr(not(any(feature = "export-abi", test)), no_main)] -extern crate alloc; - -use alloc::vec::Vec; -use alloc::{string::ToString, vec}; - -use stylus_sdk::alloy_primitives::U256; -use stylus_sdk::{alloy_primitives::Address, alloy_sol_types::sol, evm, prelude::*, ArbResult}; - -// sol! macro event declaration -// Up to 3 parameters can be indexed. -// Indexed parameters helps you filter the logs by the indexed parameter -sol! { - event Log(address indexed sender, string message); - event AnotherLog(); -} - -#[storage] -#[entrypoint] -pub struct Events {} - -#[public] -impl Events { -fn user_main(_input: Vec) -> ArbResult { - // emits a 'Log' event, defined above in the sol! macro - evm::log(Log { - sender: Address::from([0x11; 20]), - message: "Hello world!".to_string(), - }); - - // no data, but event will still log to the chain - evm::log(AnotherLog {}); - - // set up local variables - let user = Address::from([0x22; 20]); - let balance = U256::from(10_000_000); - - // declare up to 4 topics - // topics must be of type FixedBytes<32> - let topics = &[user.into_word()]; - - // store non-indexed data in a byte Vec - let mut data: Vec = vec![]; - // to_be_bytes means 'to big endian bytes' - data.extend_from_slice(balance.to_be_bytes::<32>().to_vec().as_slice()); - - // unwrap() here 'consumes' the Result - evm::raw_log(topics.as_slice(), data.as_ref()).unwrap(); - - Ok(Vec::new()) -} -} -``` - -### Cargo.toml - -```toml -[package] -name = "stylus_events_example" -version = "0.1.7" -edition = "2021" -license = "MIT OR Apache-2.0" -keywords = ["arbitrum", "ethereum", "stylus", "alloy"] - -[dependencies] -alloy-primitives = "=0.7.6" -alloy-sol-types = "=0.7.6" -mini-alloc = "0.4.2" -stylus-sdk = "0.6.0" -hex = "0.4.3" - -[dev-dependencies] -tokio = { version = "1.12.0", features = ["full"] } -ethers = "2.0" -eyre = "0.6.8" - -[features] -export-abi = ["stylus-sdk/export-abi"] - -[lib] -crate-type = ["lib", "cdylib"] - -[profile.release] -codegen-units = 1 -strip = true -lto = true -panic = "abort" -opt-level = "s" -``` diff --git a/docs/stylus-by-example/basic_examples/function.mdx b/docs/stylus-by-example/basic_examples/function.mdx deleted file mode 100644 index 49d0775c2e..0000000000 --- a/docs/stylus-by-example/basic_examples/function.mdx +++ /dev/null @@ -1,234 +0,0 @@ ---- -title: 'Function • Stylus by Example' -description: 'How to define and use internal and external functions in Stylus, and how to return multiple values from functions.' ---- - -{/* Begin Content */} - -# Functions - -Functions are a fundamental part of any programming language, including Stylus, enabling you to encapsulate logic into reusable components. - -This guide covers the syntax and usage of functions, including internal and external functions, and how to return multiple values. - -## Learn More - -- [Rust docs - Functions](https://doc.rust-lang.org/reference/items/functions.html) -- [Solidity docs - Functions](https://solidity-by-example.org/function/) - -## Overview - -A function in Stylus consists of a name, a set of parameters, an optional return type, and a body. - -Just as with storage, Stylus methods are Solidity ABI equivalent. This means that contracts written in different programming languages are fully interoperable. - -Functions are declared with the `fn` keyword. Parameters allow the function to accept inputs, and the return type specifies the output of the function. If no return type is specified, the function returns `void`. - -Following is an example of a function `add` that takes two `uint256` values and returns their sum. - -import NotForProductionBannerPartial from '../../partials/_not-for-production-banner-partial.mdx'; - - - -```rust -fn add(a: uint256, b: uint256) -> uint256 { - return a + b; -} -``` - -## Function Parameters - -Function parameters are the inputs to a function. They are specified as a list of `IDENTIFIER: Type` pairs, separated by commas. - -In this example, the function `add_numbers` takes two `u32` parameters, `a` and `b` and returns the sum of the two numbers. - -```rust -fn add_numbers(a: u32, b: u32) -> u32 { - a + b -} -``` - -## Return Types - -Return types in functions are an essential part of defining the behavior and expected outcomes of your smart contract methods. - -Here, we explain the syntax and usage of return types in Stylus with general examples. - -### Basic Syntax - -A function with a return type in Stylus follows this basic structure. The return type is specified after the `->` arrow. -Values are returned using the `return` keyword or implicitly as the last expression of the function. In Rust and Stylus, the last expression in a function is implicitly returned, so the `return` keyword is often omitted. - -```rust -pub fn function_name(&self) -> ReturnType { - // Function body -} -``` - -## Examples - -**Function returning a String:** This `get_greeting` function returns a `String`. The return type is specified as `String` after the `->` arrow. - -```rust -pub fn get_greeting() -> String { - "Hello, Stylus!".into() -} -``` - -**Function returning an Integer:** This `get_number` function returns an unsigned 32-bit integer (`u32`). - -```rust -pub fn get_number() -> u32 { - 42 -} -``` - -**Function returning a Result with `Ok` and `Err` variants:** The `perform_operation` function returns a `Result`. -The `Result` type is used for functions that can return either a success value (`Ok`) or an error (`Err`). In this case, it returns `Ok(value)` on success and an error variant of `CustomError` on failure. - -```rust -pub enum CustomError { - ErrorVariant, -} - -pub fn perform_operation(value: u32) -> Result { - if value > 0 { - Ok(value) - } else { - Err(CustomError::ErrorVariant) - } -} -``` - -## Public Functions - -Public functions are those that can be called by other contracts. - -To define a public function in a Stylus contract, you use the `#[public]` macro. This macro ensures that the function is accessible from outside the contract. - -Previously, all public methods were required to return a `Result` type with `Vec` as the error type. This is now optional. Specifically, if a method is "infallible" (i.e., it cannot produce an error), it does not need to return a Result type. Here's what this means: - -- Infallible methods: Methods that are guaranteed not to fail (no errors possible) do not need to use the `Result` type. They can return their result directly without wrapping it in `Result`. - -- Optional error handling: The `Result` type with `Vec` as the error type is now optional for methods that cannot produce an error. - -In the following example, `owner` is a public function that returns the contract owner's address. Since this function is infallible (i.e., it cannot produce an error), it does not need to return a `Result` type. - -```rust -#[external] -impl Contract { - // Define an external function to get the owner of the contract - pub fn owner(&self) -> Address { - self.owner.get() - } -} -``` - -## Internal Functions - -Internal functions are those that can only be called within the contract itself. These functions are not exposed to external calls. - -To define an internal function, you simply include it within your contract's implementation without the `#[public]` macro. - -The choice between public and internal functions depends on the desired level of accessibility and interaction within and across contracts. - -In the followinge example, `set_owner` is an internal function that sets a new owner for the contract. It is only callable within the contract itself. - -```rust -impl Contract { - // Define an internal function to set a new owner - pub fn set_owner(&mut self, new_owner: Address) { - self.owner.set(new_owner); - } -} -``` - -To mix public and internal functions within the same contract, you should use two separate `impl` blocks with the same contract name. Public functions are defined within an `impl` block annotated with the `#[public]` attribute, signifying that these functions are part of the contract's public interface and can be invoked from outside the contract. -In contrast, internal functions are placed within a separate `impl` block that does not have the `#[public]` attribute, making them internal to the contract and inaccessible to external entities. - -### src/lib.rs - -```rust -// Only run this as a WASM if the export-abi feature is not set. -#![cfg_attr(not(any(feature = "export-abi", test)), no_main)] -extern crate alloc; - -use alloc::vec; -use stylus_sdk::alloy_primitives::Address; -use stylus_sdk::prelude::*; -use stylus_sdk::storage::StorageAddress; - -use stylus_sdk::alloy_primitives::U256; -use stylus_sdk::storage::StorageU256; -use stylus_sdk::console; - - -#[storage] -#[entrypoint] -pub struct ExampleContract { - owner: StorageAddress, - data: StorageU256, -} - -#[public] -impl ExampleContract { - // External function to set the data - pub fn set_data(&mut self, value: U256) { - self.data.set(value); - } - - // External function to get the data - pub fn get_data(&self) -> U256 { - self.data.get() - } - - // External function to get the contract owner - pub fn get_owner(&self) -> Address { - self.owner.get() - } -} - -impl ExampleContract { - // Internal function to set a new owner - pub fn set_owner(&mut self, new_owner: Address) { - self.owner.set(new_owner); - } - - // Internal function to log data - pub fn log_data(&self) { - let _data = self.data.get(); - console!("Current data is: {:?}", _data); - } -} -``` - -### Cargo.toml - -```toml -[package] -name = "stylus-functions" -version = "0.1.0" -edition = "2021" - -[dependencies] -alloy-primitives = "=0.7.6" -alloy-sol-types = "=0.7.6" -mini-alloc = "0.4.2" -stylus-sdk = "0.6.0" -hex = "0.4.3" -sha3 = "0.10.8" - -[features] -export-abi = ["stylus-sdk/export-abi"] -debug = ["stylus-sdk/debug"] - -[lib] -crate-type = ["lib", "cdylib"] - -[profile.release] -codegen-units = 1 -strip = true -lto = true -panic = "abort" -opt-level = "s" -``` diff --git a/docs/stylus-by-example/basic_examples/function_selector.mdx b/docs/stylus-by-example/basic_examples/function_selector.mdx deleted file mode 100644 index 2914f7cf05..0000000000 --- a/docs/stylus-by-example/basic_examples/function_selector.mdx +++ /dev/null @@ -1,40 +0,0 @@ ---- -title: 'Function selector • Stylus by Example' -description: "How to compute the encoded function selector of a contract's function using the Arbitrum Stylus Rust SDK." ---- - -{/* Begin Content */} - -# Function selector - -When a smart contract is called, the first 4 bytes of the calldata sent as part of the request are called the "function selector", and identify which function of the smart contract to call. - -You can compute a specific function selector by using the `function_selector!` macro. - -Here's an example that computes the selector of a function named `foo`: - -import NotForProductionBannerPartial from '../../partials/_not-for-production-banner-partial.mdx'; - - - -```rust -function_selector!("foo") // returns 0xc2985578 -``` - -Functions usually take a number of arguments that you need to pass in order for the call to be successful. For example, here's the signature of a function that takes 2 arguments, an address and a uint256: - -```solidity -function transfer(address recipient, uint256 amount) external returns (bool); -``` - -To compute the selector for this function, pass the types of the arguments to the `function_selector` macro: - -```rust -function_selector!("transfer", Address, U256) // returns 0xa9059cbb -``` - -`function_selector` will return a byte array containing the encoded function selector. - -## Learn More - -- [`stylus_sdk::function_selector`](https://docs.rs/stylus-sdk/latest/stylus_sdk/macro.function_selector.html) diff --git a/docs/stylus-by-example/basic_examples/hashing.mdx b/docs/stylus-by-example/basic_examples/hashing.mdx deleted file mode 100644 index 5e6ac93eb8..0000000000 --- a/docs/stylus-by-example/basic_examples/hashing.mdx +++ /dev/null @@ -1,211 +0,0 @@ ---- -title: 'Hasing with keccak256 • Stylus by Example' -description: 'A simple solidity ABI encode and decode example' ---- - -{/* Begin Content */} - -## Hashing with keccak256 - -Keccak256 is a cryptographic hash function that takes an input of an arbitrary length and produces a fixed-length output of 256 bits. - -Keccak256 is a member of the [SHA-3](https://en.wikipedia.org/wiki/SHA-3) family of hash functions. - -keccak256 computes the Keccak-256 hash of the input. - -Some use cases are: - -- Creating a deterministic unique ID from a input -- Commit-Reveal scheme -- Compact cryptographic signature (by signing the hash instead of a larger input) - -Here we will use [`stylus-sdk::crypto::keccak`](https://docs.rs/stylus-sdk/latest/stylus_sdk/crypto/fn.keccak.html) to calculate the keccak256 hash of the input data: - -import NotForProductionBannerPartial from '../../partials/_not-for-production-banner-partial.mdx'; - - - -```rust -pub fn keccak>(bytes: T) -> B256 -``` - -# Full Example code: - -### src/main.rs - -```rust -// Only run this as a WASM if the export-abi feature is not set. -#![cfg_attr(not(any(feature = "export-abi", test)), no_main)] -extern crate alloc; - -/// Import items from the SDK. The prelude contains common traits and macros. -use stylus_sdk::{alloy_primitives::{U256, Address, FixedBytes}, abi::Bytes, prelude::*, crypto::keccak}; -use alloc::string::String; -use alloc::vec::Vec; -// Becauce the naming of alloy_primitives and alloy_sol_types is the same, so we need to re-name the types in alloy_sol_types -use alloy_sol_types::{sol_data::{Address as SOLAddress, String as SOLString, Bytes as SOLBytes, *}, SolType}; -use alloy_sol_types::sol; - -// Define error -sol! { - error DecodedFailed(); -} - -// Error types for the MultiSig contract -#[derive(SolidityError)] -pub enum HasherError{ - DecodedFailed(DecodedFailed) -} - -#[solidity_storage] -#[entrypoint] -pub struct Hasher { -} -/// Declare that `Hasher` is a contract with the following external methods. -#[public] -impl Hasher { - - // Encode the data and hash it - pub fn encode_and_hash( - &self, - target: Address, - value: U256, - func: String, - data: Bytes, - timestamp: U256 - ) -> FixedBytes<32> { - // define sol types tuple - type TxIdHashType = (SOLAddress, Uint<256>, SOLString, SOLBytes, Uint<256>); - // set the tuple - let tx_hash_data = (target, value, func, data, timestamp); - // encode the tuple - let tx_hash_data_encode = TxIdHashType::abi_encode_sequence(&tx_hash_data); - // hash the encoded data - keccak(tx_hash_data_encode).into() - } - - // This should always return true - pub fn encode_and_decode( - &self, - address: Address, - amount: U256 - ) -> Result { - // define sol types tuple - type TxIdHashType = (SOLAddress, Uint<256>); - // set the tuple - let tx_hash_data = (address, amount); - // encode the tuple - let tx_hash_data_encode = TxIdHashType::abi_encode_sequence(&tx_hash_data); - - let validate = true; - - // Check the result - match TxIdHashType::abi_decode_sequence(&tx_hash_data_encode, validate) { - Ok(res) => Ok(res == tx_hash_data), - Err(_) => { - return Err(HasherError::DecodedFailed(DecodedFailed{})); - }, - } - } - - // Packed encode the data and hash it, the same result with the following one - pub fn packed_encode_and_hash_1( - &self, - target: Address, - value: U256, - func: String, - data: Bytes, - timestamp: U256 - )-> FixedBytes<32> { - // define sol types tuple - type TxIdHashType = (SOLAddress, Uint<256>, SOLString, SOLBytes, Uint<256>); - // set the tuple - let tx_hash_data = (target, value, func, data, timestamp); - // encode the tuple - let tx_hash_data_encode_packed = TxIdHashType::abi_encode_packed(&tx_hash_data); - // hash the encoded data - keccak(tx_hash_data_encode_packed).into() - } - - // Packed encode the data and hash it, the same result with the above one - pub fn packed_encode_and_hash_2( - &self, - target: Address, - value: U256, - func: String, - data: Bytes, - timestamp: U256 - )-> FixedBytes<32> { - // set the data to arrary and concat it directly - let tx_hash_data_encode_packed = [&target.to_vec(), &value.to_be_bytes_vec(), func.as_bytes(), &data.to_vec(), ×tamp.to_be_bytes_vec()].concat(); - // hash the encoded data - keccak(tx_hash_data_encode_packed).into() - } - - - // The func example: "transfer(address,uint256)" - pub fn encode_with_signature( - &self, - func: String, - address: Address, - amount: U256 - ) -> Vec { - type TransferType = (SOLAddress, Uint<256>); - let tx_data = (address, amount); - let data = TransferType::abi_encode_sequence(&tx_data); - // Get function selector - let hashed_function_selector: FixedBytes<32> = keccak(func.as_bytes().to_vec()).into(); - // Combine function selector and input data (use abi_packed way) - let calldata = [&hashed_function_selector[..4], &data].concat(); - calldata - } - - // The func example: "transfer(address,uint256)" - pub fn encode_with_signature_and_hash( - &self, - func: String, - address: Address, - amount: U256 - ) -> FixedBytes<32> { - type TransferType = (SOLAddress, Uint<256>); - let tx_data = (address, amount); - let data = TransferType::abi_encode_sequence(&tx_data); - // Get function selector - let hashed_function_selector: FixedBytes<32> = keccak(func.as_bytes().to_vec()).into(); - // Combine function selector and input data (use abi_packed way) - let calldata = [&hashed_function_selector[..4], &data].concat(); - keccak(calldata).into() - } -} -``` - -### Cargo.toml - -```toml -[package] -name = "stylus-encode-hashing" -version = "0.1.0" -edition = "2021" - -[dependencies] -alloy-primitives = "=0.7.6" -alloy-sol-types = "=0.7.6" -mini-alloc = "0.4.2" -stylus-sdk = "0.6.0" -hex = "0.4.3" -sha3 = "0.10.8" - -[features] -export-abi = ["stylus-sdk/export-abi"] -debug = ["stylus-sdk/debug"] - -[lib] -crate-type = ["lib", "cdylib"] - -[profile.release] -codegen-units = 1 -strip = true -lto = true -panic = "abort" -opt-level = "s" -``` diff --git a/docs/stylus-by-example/basic_examples/hello_world.mdx b/docs/stylus-by-example/basic_examples/hello_world.mdx deleted file mode 100644 index 439448cb95..0000000000 --- a/docs/stylus-by-example/basic_examples/hello_world.mdx +++ /dev/null @@ -1,95 +0,0 @@ ---- -title: 'Hello World • Stylus by Example' -description: 'This example shows how to use the `console!` macro from the Arbitrum Stylus Rust SDK to print output to the terminal for debugging.' ---- - -{/* Begin Content */} - -# Hello World - -Using the `console!` macro from the `stylus_sdk` allows you to print output to the terminal for debugging purposes. To view the output, you'll need to run a local Stylus dev node as described in the [Arbitrum docs](https://docs.arbitrum.io/stylus/how-tos/local-stylus-dev-node) and **_set the debug feature flag_** as shown in line 7 of the `Cargo.toml` file below. - -The `console!` macro works similar to the built-in `println!` macro that comes with Rust. - -### Examples - -import NotForProductionBannerPartial from '../../partials/_not-for-production-banner-partial.mdx'; - - - -```rust -// Out: Stylus says: 'hello there!' -console!("hello there!"); -// Out: Stylus says: 'format some arguments' -console!("format {} arguments", "some"); - -let local_variable = "Stylus"; -// Out: Stylus says: 'Stylus is awesome!' -console!("{local_variable} is awesome!"); -// Out: Stylus says: 'When will you try out Stylus?' -console!("When will you try out {}?", local_variable); -``` - -### src/main.rs - -```rust -#![cfg_attr(not(feature = "export-abi"), no_main)] - -extern crate alloc; - - -use stylus_sdk::{console, prelude::*, stylus_proc::entrypoint, ArbResult}; - -#[storage] -#[entrypoint] -pub struct Hello; - - -#[public] -impl Hello { - fn user_main(_input: Vec) -> ArbResult { - // Will print 'Stylus says: Hello Stylus!' on your local dev node - // Be sure to add "debug" feature flag to your Cargo.toml file as - // shown below. - console!("Hello Stylus!"); - Ok(Vec::new()) - } -} -``` - -### Cargo.toml - -```toml -[package] -name = "stylus_hello_world" -version = "0.1.7" -edition = "2021" -license = "MIT OR Apache-2.0" -keywords = ["arbitrum", "ethereum", "stylus", "alloy"] - -[dependencies] -alloy-primitives = "=0.7.6" -alloy-sol-types = "=0.7.6" -mini-alloc = "0.4.2" -stylus-sdk = { version = "0.6.0", features = ["debug"] } -hex = "0.4.3" -sha3 = "0.10" - -[dev-dependencies] -tokio = { version = "1.12.0", features = ["full"] } -ethers = "2.0" -eyre = "0.6.8" - -[features] -export-abi = ["stylus-sdk/export-abi"] - -[lib] -crate-type = ["lib", "cdylib"] - -[profile.release] -codegen-units = 1 -strip = true -lto = true -panic = "abort" -opt-level = "s" -``` diff --git a/docs/stylus-by-example/basic_examples/inheritance.mdx b/docs/stylus-by-example/basic_examples/inheritance.mdx deleted file mode 100644 index adcaec49ac..0000000000 --- a/docs/stylus-by-example/basic_examples/inheritance.mdx +++ /dev/null @@ -1,137 +0,0 @@ ---- -title: 'Inheritance • Stylus by Example' -description: 'How to leverage inheritance using the Arbitrum Stylus Rust SDK.' ---- - -{/* Begin Content */} - -# Inheritance - -The Stylus Rust SDK replicates the composition pattern of Solidity. The `#[public]` macro provides the [Router](https://docs.rs/stylus-sdk/latest/stylus_sdk/abi/trait.Router.html) trait, which can be used to connect types via inheritance, via the `#[inherit]` macro. - -**Please note:** Stylus doesn't support contract multi-inheritance yet. - -Let's see an example: - -import NotForProductionBannerPartial from '../../partials/_not-for-production-banner-partial.mdx'; - - - -```rust -#[public] -#[inherit(Erc20)] -impl Token { - pub fn mint(&mut self, amount: U256) -> Result<(), Vec> { - ... - } -} - -#[public] -impl Erc20 { - pub fn balance_of() -> Result { - ... - } -} -``` - -In the above code, we can see how `Token` inherits from `Erc20`, meaning that it will inherit the public methods available in `Erc20`. If someone called the `Token` contract on the function `balanceOf`, the function `Erc20.balance_of()` would be executed. - -Additionally, the inheriting type must implement the [Borrow](https://doc.rust-lang.org/core/borrow/trait.Borrow.html) trait for borrowing data from the inherited type. In the case above, `Token` should implement `Borrow`. For simplicity, `#[storage]` and `sol_storage!` provide a `#[borrow]` annotation that can be used instead of manually implementing the trait: - -```rust -sol_storage! { - #[entrypoint] - pub struct Token { - #[borrow] - Erc20 erc20; - ... - } - - pub struct Erc20 { - ... - } -} -``` - -## Methods search order - -A type can inherit multiple other types (as long as they use the `#[public]` macro). Since execution begins in the type that uses the `#[entrypoint]` macro, that type will be first checked when searching a specific method. If the method is not found in that type, the search will continue in the inherited types, in order of inheritance. If the method is not found in any of the inherited methods, the call will revert. - -Let's see an example: - -```rust -#[public] -#[inherit(B, C)] -impl A { - pub fn foo() -> Result<(), Vec> { - ... - } -} - -#[public] -impl B { - pub fn bar() -> Result<(), Vec> { - ... - } -} - -#[public] -impl C { - pub fn bar() -> Result<(), Vec> { - ... - } - - pub fn baz() -> Result<(), Vec> { - ... - } -} -``` - -In the code above: - -- calling `foo()` will search the method in `A`, find it, and execute `A.foo()` -- calling `bar()` will search the method in `A` first, then in `B`, find it, and execute `B.bar()` -- calling `baz()` will search the method in `A`, `B` and finally `C`, so it will execute `C.baz()` - -Notice that `C.bar()` won't ever be reached, since the inheritance goes through `B` first, which has a method named `bar()` too. - -Finally, since the inherited types can also inherit other types themselves, keep in mind that method resolution finds the first matching method by [Depth First Search](https://en.wikipedia.org/wiki/Depth-first_search). - -## Overriding methods - -Because methods are checked in the inherited order, if two types implement the same method, the one in the higher level in the hierarchy will override the one in the lower levels, which won’t be callable. This allows for patterns where the developer imports a crate implementing a standard, like ERC-20, and then adds or overrides just the methods they want to without modifying the imported ERC-20 type. - -**Important warning**: The Stylus Rust SDK does not currently contain explicit `override` or `virtual` keywords for explicitly marking override functions. It is important, therefore, to carefully ensure that contracts are only overriding the functions. - -Let's see an example: - -```rust -#[public] -#[inherit(B, C)] -impl A { - pub fn foo() -> Result<(), Vec> { - ... - } -} - -#[public] -impl B { - pub fn foo() -> Result<(), Vec> { - ... - } - - pub fn bar() -> Result<(), Vec> { - ... - } -} -``` - -In the example above, even though `B` has an implementation for `foo()`, calling `foo()` will execute `A.foo()` since the method is searched first in `A`. - -## Learn more - -- [`Arbitrum documentation`](https://docs.arbitrum.io/stylus/reference/rust-sdk-guide#inheritance-inherit-and-borrow) -- [`inheritance, #[inherit] and #[borrow]`](https://docs.rs/stylus-sdk/latest/stylus_sdk/prelude/attr.public.html#inheritance-inherit-and-borrow) -- [`Router trait`](https://docs.rs/stylus-sdk/latest/stylus_sdk/abi/trait.Router.html) -- [`Borrow trait`](https://doc.rust-lang.org/core/borrow/trait.Borrow.html) -- [`BorrowMut trait`](https://doc.rust-lang.org/core/borrow/trait.BorrowMut.html) diff --git a/docs/stylus-by-example/basic_examples/primitive_data_types.mdx b/docs/stylus-by-example/basic_examples/primitive_data_types.mdx deleted file mode 100644 index 6799edce5e..0000000000 --- a/docs/stylus-by-example/basic_examples/primitive_data_types.mdx +++ /dev/null @@ -1,298 +0,0 @@ ---- -title: 'Primitive Data Types • Stylus by Example' -description: 'Defines some of the basic primitives used in Arbitrum Stylus Rust smart contracts and how they map to compatible Solidity constructs.' ---- - -{/* Begin Content */} - -# Primitive Data Types - -The **Stylus SDK** makes use of the popular **Alloy** library (from the developers of **ethers-rs** and **Foundry**) to represent various native Solidity types as Rust types and to seamlessly convert between them when needed. These are needed since there are a number of custom types (like address) and large integers that are not natively supported in Rust. - -In this section, we'll focus on the following types: - -- `U256` -- `I256` -- `Address` -- `Boolean` -- `Bytes` - -More in-depth documentation about the available methods and types in the Alloy library can be found in their docs. It also helps to cross-reference with Solidity docs if you don't already have a solid understanding of those types. - -## Learn More - -- [Alloy docs (v0.7.6)](https://docs.rs/alloy-primitives/0.7.6/alloy_primitives/index.html) - - [`Address`](https://docs.rs/alloy-primitives/0.7.6/alloy_primitives/struct.Address.html) - - [`Signed`](https://docs.rs/alloy-primitives/0.7.6/alloy_primitives/struct.Signed.html) - - [`Uint`](https://docs.rs/ruint/1.10.1/ruint/struct.Uint.html) -- [Stylus Rust SDK](https://docs.rs/stylus-sdk/latest/stylus_sdk/index.html) - - [`Bytes`](https://docs.rs/stylus-sdk/latest/stylus_sdk/abi/struct.Bytes.html) -- [Solidity docs (v0.8.19)](https://docs.soliditylang.org/en/v0.8.19/types.html) - -## Integers - -Alloy defines a set of convenient Rust types to represent the typically sized integers used in Solidity. The type `U256` represents a 256-bit _unsigned_ integer, meaning it cannot be negative. The range for a `U256` number is 0 to 2^256 - 1. - -Negative numbers are allowed for I types, such as `I256`. These represent signed integers. - -- `U256` maps to `uint256` ... `I256` maps to `int256` -- `U128` maps to `uint128` ... `I128` maps to `int128` -- ... -- `U8` maps to `uint8` ... `I8` maps to `int8` - -### Integer Usage - -import NotForProductionBannerPartial from '../../partials/_not-for-production-banner-partial.mdx'; - - - -```rust -// Unsigned -let eight_bit: U8 = U8::from(1); -let two_fifty_six_bit: U256 = U256::from(0xff_u64); - -// Out: Stylus says: '8-bit: 1 | 256-bit: 255' -console!("8-bit: {} | 256-bit: {}", eight_bit, two_fifty_six_bit); - -// Signed -let eight_bit: I8 = I8::unchecked_from(-1); -let two_fifty_six_bit: I256 = I256::unchecked_from(0xff_u64); - -// Out: Stylus says: '8-bit: -1 | 256-bit: 255' -console!("8-bit: {} | 256-bit: {}", eight_bit, two_fifty_six_bit); -``` - -### Expanded Integer Usage - -```rust -// Use `try_from` if you're not sure it'll fit -let a = I256::try_from(20003000).unwrap(); -// Or parse from a string -let b = "100".parse::().unwrap(); -// With hex characters -let c = "-0x138f".parse::().unwrap(); -// Underscores are ignored -let d = "1_000_000".parse::().unwrap(); - -// Math works great -let e = a * b + c - d; -// Out: Stylus says: '20003000 * 100 + -5007 - 1000000 = 1999294993' -console!("{} * {} + {} - {} = {}", a, b, c, d, e); - -// Useful constants -let f = I256::MAX; -let g = I256::MIN; -let h = I256::ZERO; -let i = I256::MINUS_ONE; - -// Stylus says: '5789...9967, -5789...9968, 0, -1' -console!("{f}, {g}, {h}, {i}"); -// As hex: Stylus says: '0x7fff...ffff, 0x8000...0000, 0x0, 0xffff...ffff' -console!("{:#x}, {:#x}, {:#x}, {:#x}", f, g, h, i); -``` - -## Address - -Ethereum addresses are 20 bytes in length, or 160 bits. Alloy provides a number of helper utilities for converting to addresses from strings, bytes, numbers, and addresses. - -### Address Usage - -```rust -// From a 20 byte slice, all 1s -let addr1 = Address::from([0x11; 20]); -// Out: Stylus says: '0x1111111111111111111111111111111111111111' -console!("{addr1}"); - -// Use the address! macro to parse a string as a checksummed address -let addr2 = address!("d8da6bf26964af9d7eed9e03e53415d37aa96045"); -// Out: Stylus says: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' -console!("{addr2}"); - -// Format compressed addresses for output -// Out: Stylus says: '0xd8dA…6045' -console!("{addr2:#}"); -``` - -## Boolean - -Use native Rust primitives where it makes sense and where no equivalent Alloy primitive exists. - -### Boolean Usage - -```rust -let frightened: bool = true; -// Out: Stylus says: 'Boo! Did I scare you?' -console!("Boo! Did I scare you?"); - -let response = match frightened { - true => "Yes!".to_string(), - false => "No!".to_string(), -}; - -// Out: Stylus says: 'Yes!' -console!("{response}"); -``` - -## Bytes - -The Stylus SDK provides this wrapper type around `Vec` to represent a `bytes` value in Solidity. - -```rust -let vec = vec![108, 27, 56, 87]; -let b = Bytes::from(vec); -// Out: Stylus says: '0x6c1b3857' -console!(String::from_utf8_lossy(b.as_slice())); - -let b = Bytes::from(b"Hello!".to_vec()); -// Out: Stylus says: 'Hello!' -console!(String::from_utf8_lossy(b.as_slice())); -``` - -Note: Return the `Bytes` type on your Rust function if you want to return the ABI `bytes memory` type. - -## Boilerplate - -### src/lib.rs - -```rust -#![cfg_attr(not(any(feature = "export-abi", test)), no_main)] -extern crate alloc; -use alloc::{string::ToString, vec::Vec}; - -use stylus_sdk::{ - alloy_primitives::{address, Address, I256, I8, U256, U8}, console, prelude::*, ArbResult -}; - -#[storage] -#[entrypoint] -pub struct Data { - -} - - -#[public] -impl Data { -fn user_main(_input: Vec) -> ArbResult { - // Use native Rust primitives where they make sense - // and where no equivalent Alloy primitive exists - let frightened: bool = true; - // Out: Stylus says: 'Boo! Did I scare you?' - console!("Boo! Did I scare you?"); - - let _response = match frightened { - true => "Yes!".to_string(), - false => "No!".to_string(), - }; - - // Out: Stylus says: 'Yes!' - console!("{_response}"); - - // U256 stands for a 256-bit *unsigned* integer, meaning it cannot be - // negative. The range for a U256 number is 0 to 2^256 - 1. Alloy provides - // a set of unsigned integer types to represent the various sizes available - // in the EVM. - // U256 maps to uint256 - // U128 maps to uint128 - // ... - // U8 maps to uint8 - let _eight_bit: U8 = U8::from(1); - let _two_fifty_six_bit: U256 = U256::from(0xff_u64); - - // Out: Stylus says: '8-bit: 1 | 256-bit: 255' - console!("8-bit: {} | 256-bit: {}", _eight_bit, _two_fifty_six_bit); - - // Negative numbers are allowed for I types. These represent signed integers. - // I256 maps to int256 - // I128 maps to int128 - // ... - // I8 maps to int8 - let _eight_bit: I8 = I8::unchecked_from(-1); - let _two_fifty_six_bit: I256 = I256::unchecked_from(0xff_u64); - - // Out: Stylus says: '8-bit: -1 | 256-bit: 255' - console!("8-bit: {} | 256-bit: {}", _eight_bit, _two_fifty_six_bit); - - // Additional usage of integers - - // Use `try_from` if you're not sure it'll fit - let a = I256::try_from(20003000).unwrap(); - // Or parse from a string - let b = "100".parse::().unwrap(); - // With hex characters - let c = "-0x138f".parse::().unwrap(); - // Underscores are ignored - let d = "1_000_000".parse::().unwrap(); - - // Math works great - let _e = a * b + c - d; - // Out: Stylus says: '20003000 * 100 + -5007 - 1000000 = 1999294993' - console!("{} * {} + {} - {} = {}", a, b, c, d, _e); - - // Useful constants - let _f = I256::MAX; - let _g = I256::MIN; - let _h = I256::ZERO; - let _i = I256::MINUS_ONE; - - // Stylus says: '5789...9967, -5789...9968, 0, -1' - console!("{_f}, {_g}, {_h}, {_i}"); - // As hex: Stylus says: '0x7fff...ffff, 0x8000...0000, 0x0, 0xffff...ffff' - console!("{:#x}, {:#x}, {:#x}, {:#x}", _f, _g, _h, _i); - - // Ethereum addresses are 20 bytes in length, or 160 bits. Alloy provides a number of helper utilities for converting to addresses from strings, bytes, numbers, and addresses - - // From a 20 byte slice, all 1s - let _addr1 = Address::from([0x11; 20]); - // Out: Stylus says: '0x1111111111111111111111111111111111111111' - console!("{_addr1}"); - - // Use the address! macro to parse a string as a checksummed address - let _addr2 = address!("d8da6bf26964af9d7eed9e03e53415d37aa96045"); - // Out: Stylus says: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' - console!("{_addr2}"); - - // Format compressed addresses for output - // Out: Stylus says: '0xd8dA…6045' - console!("{_addr2:#}"); - - Ok(Vec::new()) -} -} -``` - -### Cargo.toml - -```toml -[package] -name = "stylus_data_example" -version = "0.1.7" -edition = "2021" -license = "MIT OR Apache-2.0" -keywords = ["arbitrum", "ethereum", "stylus", "alloy"] - -[dependencies] -alloy-primitives = "=0.7.6" -alloy-sol-types = "=0.7.6" -mini-alloc = "0.4.2" -stylus-sdk = "0.6.0" -hex = "0.4.3" - -[dev-dependencies] -tokio = { version = "1.12.0", features = ["full"] } -ethers = "2.0" -eyre = "0.6.8" - -[features] -export-abi = ["stylus-sdk/export-abi"] - -[lib] -crate-type = ["lib", "cdylib"] - -[profile.release] -codegen-units = 1 -strip = true -lto = true -panic = "abort" -opt-level = "s" - -``` diff --git a/docs/stylus-by-example/basic_examples/sending_ether.mdx b/docs/stylus-by-example/basic_examples/sending_ether.mdx deleted file mode 100644 index 02d5ce6197..0000000000 --- a/docs/stylus-by-example/basic_examples/sending_ether.mdx +++ /dev/null @@ -1,152 +0,0 @@ ---- -title: 'Sending Ether • Stylus by Example' -description: "How to send Ether in your Rust smart contracts using Arbitrum's Stylus SDK" ---- - -{/* Begin Content */} - -# Sending Ether - -We have three main ways to send Ether in Rust Stylus: using the `transfer_eth` method, using low level `call` method, and sending value while calling an external contract. - -It's important to note that the `transfer_eth` method in Rust Stylus invokes the recipient contract, which may subsequently call other contracts. All the gas is supplied to the recipient, which it may burn. Conversely, the transfer method in Solidity is capped at 2300 gas. In Rust Stylus, you can cap the gas by using the low-level call method with a specified gas. An example of this is provided in the code on bottom of the page. - -These two methods are exactly equivalent under the hood: - -import NotForProductionBannerPartial from '../../partials/_not-for-production-banner-partial.mdx'; - - - -```rust -transfer_eth(recipient, value)?; - -call(Call::new_in(self).value(value), recipient, &[])?; -``` - -## Where to Send Ether - -1. **Externally Owned Account (EOA) Addresses**: Directly send Ether to an EOA address. - -2. **Solidity Smart Contracts with Receive Function (No Calldata)**: Send Ether to a Solidity smart contract that has a `receive` function without providing any calldata. - -3. **Solidity Smart Contracts with Fallback Function (With Calldata)**: Send Ether to a Solidity smart contract that has a `fallback` function by providing the necessary calldata. - -4. **Smart Contracts with Payable Methods (both Solidity and Stylus)**: Send Ether to smart contracts that have defined payable methods. Payable methods are identified by the `payable` modifier in Solidity, and the `#[payable]` macro in Rust. - -Below you can find examples for each of these methods and how to define them in a Rust Stylus smart contract using the Stylus SDK: - -### `src/lib.rs` - -```rust -// Only run this as a WASM if the export-abi feature is not set. -#![cfg_attr(not(any(feature = "export-abi", test)), no_main)] -extern crate alloc; - -use alloy_primitives::Address; -use stylus_sdk::{ - abi::Bytes, - call::{call, transfer_eth, Call}, - msg::{self}, - prelude::*, -}; - -sol_interface! { - interface ITarget { - function receiveEther() external payable; - } -} - -#[storage] -#[entrypoint] -pub struct SendEther {} - -#[public] -impl SendEther { - // Transfer Ether using the transfer_eth method - // This can be used to send Ether to an EOA or a Solidity smart contract that has a receive() function implemented - #[payable] - pub fn send_via_transfer(to: Address) -> Result<(), Vec> { - transfer_eth(to, msg::value())?; - Ok(()) - } - - // Transfer Ether using a low-level call - // This can be used to send Ether to an EOA or a Solidity smart contract that has a receive() function implemented - #[payable] - pub fn send_via_call(&mut self, to: Address) -> Result<(), Vec> { - call(Call::new_in(self).value(msg::value()), to, &[])?; - Ok(()) - } - - // Transfer Ether using a low-level call with a specified gas limit - // This can be used to send Ether to an EOA or a Solidity smart contract that has a receive() function implemented - #[payable] - pub fn send_via_call_gas_limit(&mut self, to: Address, gas_amount: u64) -> Result<(), Vec> { - call( - Call::new_in(self).value(msg::value()).gas(gas_amount), - to, - &[], - )?; - Ok(()) - } - - // Transfer Ether using a low-level call with calldata - // This can be used to call a Solidity smart contract's fallback function and send Ether along with calldata - #[payable] - pub fn send_via_call_with_call_data( - &mut self, - to: Address, - data: Bytes, - ) -> Result<(), Vec> { - call(Call::new_in(self).value(msg::value()), to, data.as_slice())?; - Ok(()) - } - - // Transfer Ether to another smart contract via a payable method on the target contract - // The target contract can be either a Solidity smart contract or a Stylus contract that has a receiveEther function, which is a payable function - #[payable] - pub fn send_to_stylus_contract(&mut self, to: Address) -> Result<(), Vec> { - let target = ITarget::new(to); - let config = Call::new_in(self).value(msg::value()); - target.receive_ether(config)?; - Ok(()) - } -} -``` - -### `Cargo.toml` - -```toml -[package] -name = "stylus_sending_ether_example" -version = "0.1.7" -edition = "2021" -license = "MIT OR Apache-2.0" -keywords = ["arbitrum", "ethereum", "stylus", "alloy"] - -[dependencies] -alloy-primitives = "=0.7.6" -alloy-sol-types = "=0.7.6" -mini-alloc = "0.4.2" -stylus-sdk = "0.6.0" -hex = "0.4.3" - -[dev-dependencies] -tokio = { version = "1.12.0", features = ["full"] } -ethers = "2.0" -eyre = "0.6.8" - -[features] -export-abi = ["stylus-sdk/export-abi"] - -[lib] -crate-type = ["lib", "cdylib"] - -[profile.release] -codegen-units = 1 -strip = true -lto = true -panic = "abort" -opt-level = "s" - -``` diff --git a/docs/stylus-by-example/basic_examples/sidebar.js b/docs/stylus-by-example/basic_examples/sidebar.js deleted file mode 100644 index 8a4c7a58d3..0000000000 --- a/docs/stylus-by-example/basic_examples/sidebar.js +++ /dev/null @@ -1,82 +0,0 @@ -// @ts-check -/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const sidebar = { - "items": [ - { - "type": "doc", - "id": "stylus-by-example/basic_examples/hello_world", - "label": "Hello World" - }, - { - "type": "doc", - "id": "stylus-by-example/basic_examples/primitive_data_types", - "label": "Primitive Data Types" - }, - { - "type": "doc", - "id": "stylus-by-example/basic_examples/variables", - "label": "Variables" - }, - { - "type": "doc", - "id": "stylus-by-example/basic_examples/constants", - "label": "Constants" - }, - { - "type": "doc", - "id": "stylus-by-example/basic_examples/function", - "label": "Function" - }, - { - "type": "doc", - "id": "stylus-by-example/basic_examples/errors", - "label": "Errors" - }, - { - "type": "doc", - "id": "stylus-by-example/basic_examples/events", - "label": "Events" - }, - { - "type": "doc", - "id": "stylus-by-example/basic_examples/inheritance", - "label": "Inheritance" - }, - { - "type": "doc", - "id": "stylus-by-example/basic_examples/vm_affordances", - "label": "Vm Affordances" - }, - { - "type": "doc", - "id": "stylus-by-example/basic_examples/sending_ether", - "label": "Sending Ether" - }, - { - "type": "doc", - "id": "stylus-by-example/basic_examples/function_selector", - "label": "Function Selector" - }, - { - "type": "doc", - "id": "stylus-by-example/basic_examples/abi_encode", - "label": "Abi Encode" - }, - { - "type": "doc", - "id": "stylus-by-example/basic_examples/abi_decode", - "label": "Abi Decode" - }, - { - "type": "doc", - "id": "stylus-by-example/basic_examples/hashing", - "label": "Hashing" - }, - { - "type": "doc", - "id": "stylus-by-example/basic_examples/bytes_in_bytes_out", - "label": "Bytes In Bytes Out" - } - ] -}; -module.exports = sidebar.items; \ No newline at end of file diff --git a/docs/stylus-by-example/basic_examples/variables.mdx b/docs/stylus-by-example/basic_examples/variables.mdx deleted file mode 100644 index 77d7719abc..0000000000 --- a/docs/stylus-by-example/basic_examples/variables.mdx +++ /dev/null @@ -1,125 +0,0 @@ ---- -title: 'Variables • Stylus by Example' -description: 'An explanation of the types of variables available as part of the Arbitrum Stylus Rust SDK and how it differs from Solidity.' ---- - -{/* Begin Content */} - -# Variables - -In Solidity, there are 3 types of variables: local, state, and global. Local variables are not stored on the blockchain, while state variables are (and incur a much higher cost as a result). This is true of Arbitrum Stylus Rust smart contracts as well, although how they're defined is quite different. - -In Rust, **local variables** are just ordinary variables you assign with `let` or `let mut` statements. Local variables are far cheaper than state variables, even on the EVM, however, Stylus local variables are more than 100x cheaper to allocate in memory than their Solidity equivalents. - -Unlike Solidity, Rust was not built inherently with the blockchain in mind. It is a general purpose programming language. We therefore define specific _storage_ types to explicitly denote values intended to be stored permanently as part of the contract's state. **State variables** cost the same to store as their Solidity equivalents. - -**Global variables** in Solidity, such as `msg.sender` and `block.timestamp`, are available as function calls pulled in from the `stylus_sdk` with their Rust equivalents being `msg::sender()` and `block::timestamp()`, respectively. These variables provide information about the blockchain or the active transaction. - -## Learn more - -- [Rust Docs - Variables and Mutability](https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html) -- [Stylus SDK Rust Docs - Storage](https://docs.rs/stylus-sdk/latest/stylus_sdk/storage/index.html) -- [Stylus SDK Guide - Storage](https://docs.arbitrum.io/stylus/reference/rust-sdk-guide#storage) -- [Solidity docs - state variables](https://docs.soliditylang.org/en/v0.8.19/structure-of-a-contract.html#state-variables) -- [Solidity docs - global variables](https://docs.soliditylang.org/en/v0.8.19/cheatsheet.html#global-variables) - -### src/lib.rs - -import NotForProductionBannerPartial from '../../partials/_not-for-production-banner-partial.mdx'; - - - -```rust -// Only run this as a WASM if the export-abi feature is not set. -#![cfg_attr(not(any(feature = "export-abi", test)), no_main)] -extern crate alloc; - -use stylus_sdk::alloy_primitives::{U16, U256}; -use stylus_sdk::prelude::*; -use stylus_sdk::storage::{StorageAddress, StorageBool, StorageU256}; -use stylus_sdk::{block, console, msg}; - -#[storage] -#[entrypoint] -pub struct Contract { - initialized: StorageBool, - owner: StorageAddress, - max_supply: StorageU256, -} - -#[public] -impl Contract { - // State variables are initialized in an `init` function. - pub fn init(&mut self) -> Result<(), Vec> { - // We check if contract has been initialized before. - // We return if so, we initialize if not. - let initialized = self.initialized.get(); - if initialized { - return Ok(()); - } - self.initialized.set(true); - - // We set the contract owner to the caller, - // which we get from the global msg module - self.owner.set(msg::sender()); - self.max_supply.set(U256::from(10_000)); - - Ok(()) - } - - pub fn do_something() -> Result<(), Vec> { - // Local variables are not saved to the blockchain - // 16-bit Rust integer - let _i = 456_u16; - // 16-bit int inferred from U16 Alloy primitive - let _j = U16::from(123); - - // Here are some global variables - let _timestamp = block::timestamp(); - let _amount = msg::value(); - - console!("Local variables: {_i}, {_j}"); - console!("Global variables: {_timestamp}, {_amount}"); - - Ok(()) - } -} - -``` - -### Cargo.toml - -```toml -[package] -name = "stylus_variable_example" -version = "0.1.7" -edition = "2021" -license = "MIT OR Apache-2.0" -keywords = ["arbitrum", "ethereum", "stylus", "alloy"] - -[dependencies] -alloy-primitives = "=0.7.6" -alloy-sol-types = "=0.7.6" -mini-alloc = "0.4.2" -stylus-sdk = "0.6.0" -hex = "0.4.3" - -[dev-dependencies] -tokio = { version = "1.12.0", features = ["full"] } -ethers = "2.0" -eyre = "0.6.8" - -[features] -export-abi = ["stylus-sdk/export-abi"] - -[lib] -crate-type = ["lib", "cdylib"] - -[profile.release] -codegen-units = 1 -strip = true -lto = true -panic = "abort" -opt-level = "s" - -``` diff --git a/docs/stylus-by-example/basic_examples/vm_affordances.mdx b/docs/stylus-by-example/basic_examples/vm_affordances.mdx deleted file mode 100644 index 2be96ed2b6..0000000000 --- a/docs/stylus-by-example/basic_examples/vm_affordances.mdx +++ /dev/null @@ -1,161 +0,0 @@ ---- -title: 'VM affordances • Stylus by Example' -description: 'How to access VM affordances using the Arbitrum Stylus Rust SDK.' ---- - -{/* Begin Content */} - -# VM affordances - -The Stylus Rust SDK contains several modules for interacting with the Virtual Machine (VM), which can be imported from `stylus_sdk`. - -Let's see an example: - -import NotForProductionBannerPartial from '../../partials/_not-for-production-banner-partial.mdx'; - - - -```rust -use stylus_sdk::{msg}; - -let callvalue = msg::value(); -``` - -This page lists the modules that are available, as well as the methods within those modules. - -## block - -Allows you to inspect the current block: - -- `basefee`: gets the basefee of the current block -- `chainid`: gets the unique chain identifier of the Arbitrum chain -- `coinbase`: gets the coinbase of the current block, which on Arbitrum chains is the L1 batch poster's address -- `gas_limit`: gets the gas limit of the current block -- `number`: gets a bounded estimate of the L1 block number at which the sequencer sequenced the transaction. See [Block gas limit, numbers and time](https://docs.arbitrum.io/build-decentralized-apps/arbitrum-vs-ethereum/block-numbers-and-time) for more information on how this value is determined -- `timestamp`: gets a bounded estimate of the Unix timestamp at which the sequencer sequenced the transaction. See [Block gas limit, numbers and time](https://docs.arbitrum.io/build-decentralized-apps/arbitrum-vs-ethereum/block-numbers-and-time) for more information on how this value is determined - -```rust -use stylus_sdk::{block}; - -let basefee = block::basefee(); -let chainid = block::chainid(); -let coinbase = block::coinbase(); -let gas_limit = block::gas_limit(); -let number = block::number(); -let timestamp = block::timestamp(); -``` - -## contract - -Allows you to inspect the contract itself: - -- `address`: gets the address of the current program -- `args`: reads the invocation's calldata. The entrypoint macro uses this under the hood -- `balance`: gets the balance of the current program -- `output`: writes the contract's return data. The entrypoint macro uses this under the hood -- `read_return_data`: copies the bytes of the last EVM call or deployment return result. Note: this function does not revert if out of bounds, but rather will copy the overlapping portion -- `return_data_len`: returns the length of the last EVM call or deployment return result, or 0 if neither have happened during the program's execution - -```rust -use stylus_sdk::{contract}; - -let address = contract::address(); -contract::args(); -let balance = contract::balance(); -contract::output(); -contract::read_return_data(); -contract::return_data_len(); -``` - -## crypto - -Allows you to access VM-accelerated cryptographic functions: - -- `keccak`: efficiently computes the [keccak256](https://en.wikipedia.org/wiki/SHA-3) hash of the given preimage - -```rust -use stylus_sdk::{crypto}; -use stylus_sdk::alloy_primitives::address; - -let preimage = address!("361594F5429D23ECE0A88E4fBE529E1c49D524d8"); -let hash = crypto::keccak(&preimage); -``` - -## evm - -Allows you to access affordances for the Ethereum Virtual Machine: - -- `gas_left`: gets the amount of gas remaining. See [Ink and Gas](https://docs.arbitrum.io/stylus/concepts/stylus-gas) for more information on Stylus's compute pricing -- `ink_left`: gets the amount of ink remaining. See [Ink and Gas](https://docs.arbitrum.io/stylus/concepts/stylus-gas) for more information on Stylus's compute pricing -- `log`: emits a typed alloy log -- `pay_for_memory_grow`: this function exists to force the compiler to import this symbol. Calling it will unproductively consume gas -- `raw_log`: emits an EVM log from its raw topics and data. Most users should prefer the alloy-typed [raw_log](https://docs.rs/stylus-sdk/latest/stylus_sdk/evm/fn.raw_log.html) - -```rust -use stylus_sdk::{evm}; - -let gas_left = evm::gas_left(); -let ink_left = evm::ink_left(); -evm::log(...); -evm::pay_for_memory_grow(); -evm::raw_log(...); -``` - -Here's an example of how to emit a Transfer log: - -```rust -sol! { - event Transfer(address indexed from, address indexed to, uint256 value); - event Approval(address indexed owner, address indexed spender, uint256 value); -} - -fn foo() { - ... - evm::log(Transfer { - from: Address::ZERO, - to: address, - value, - }); -} -``` - -## msg - -Allows you to inspect the current call - -- `reentrant`: whether the current call is reentrant -- `sender`: gets the address of the account that called the program. For normal L2-to-L2 transactions the semantics are equivalent to that of the EVM's [CALLER](https://www.evm.codes/#33) opcode, including in cases arising from [DELEGATE_CALL](https://www.evm.codes/#f4) -- `value`: gets the ETH value in wei sent to the program - -```rust -use stylus_sdk::{msg}; - -let reentrant = msg::reentrant(); -let sender = msg::sender(); -let value = msg::value(); -``` - -## tx - -Allows you to inspect the current transaction - -- `gas_price`: gets the gas price in wei per gas, which on Arbitrum chains equals the basefee -- `gas_to_ink`: converts evm gas to ink. See [Ink and Gas](https://docs.arbitrum.io/stylus/concepts/stylus-gas) for more information on Stylus's compute-pricing model -- `ink_price`: gets the price of ink in evm gas basis points. See [Ink and Gas](https://docs.arbitrum.io/stylus/concepts/stylus-gas) for more information on Stylus's compute-pricing model -- `ink_to_gas`: converts ink to evm gas. See [Ink and Gas](https://docs.arbitrum.io/stylus/concepts/stylus-gas) for more information on Stylus's compute-pricing model -- `origin`: gets the top-level sender of the transaction. The semantics are equivalent to that of the EVM's [ORIGIN](https://www.evm.codes/#32) opcode - -```rust -use stylus_sdk::{tx}; - -let gas_price = tx::gas_price(); -let gas_to_ink = tx::gas_to_ink(); -let ink_price = tx::ink_price(); -let ink_to_gas = tx::ink_to_gas(); -let origin = tx::origin(); -``` - -## Learn More - -- [`Arbitrum documentation`](https://docs.arbitrum.io/stylus/reference/rust-sdk-guide#evm-affordances) -- [`Stylus SDK modules`](https://docs.rs/stylus-sdk/latest/stylus_sdk/index.html#modules) From 6a9202f280acd9d0f185433a38391eeac49a8ed7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Wed, 17 Sep 2025 11:12:14 -0700 Subject: [PATCH 19/62] prevent scripts/stylusByExampleDocsHandler.ts from logging to the console --- scripts/stylusByExampleDocsHandler.ts | 36 +++++++++++---------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/scripts/stylusByExampleDocsHandler.ts b/scripts/stylusByExampleDocsHandler.ts index 415c1c5135..dc4ee465bd 100644 --- a/scripts/stylusByExampleDocsHandler.ts +++ b/scripts/stylusByExampleDocsHandler.ts @@ -92,12 +92,10 @@ function cleanDirectory(directory) { function copyFiles(source, target, allowList) { // Validate source directory if (!fs.existsSync(source)) { - console.error(`Source path does not exist: ${source}`); return; } if (!fs.lstatSync(source).isDirectory()) { - console.error(`Source path is not a directory: ${source}`); return; } @@ -181,7 +179,7 @@ function addAdmonitionOneLineAboveFirstCodeBlock(content) { const index = content.indexOf(firstCodeBlock); if (index === -1) { - console.log('firstCodeBlock not found'); + // firstCodeBlock not found return content; // Return original content instead of undefined } @@ -189,7 +187,7 @@ function addAdmonitionOneLineAboveFirstCodeBlock(content) { const lines = content.substring(0, index).split('\n'); if (lines.length < 2) { - console.warn('Not enough lines before code block for banner insertion'); + // Not enough lines before code block for banner insertion return content; } @@ -230,7 +228,7 @@ function convertMetadataToFrontmatter(content) { metadataObj = func(); } } catch (error) { - console.error('Error parsing metadata:', error); + // Error parsing metadata return content; } @@ -491,8 +489,7 @@ function validateContent(content, filePath) { // Log all fixes applied if (fixesApplied.length > 0) { - console.log(`\n📝 Content fixes applied to ${path.basename(filePath)}:`); - fixesApplied.forEach(fix => console.log(` ✓ ${fix}`)); + // Content fixes applied } return validatedContent; @@ -532,11 +529,10 @@ function formatMDXFile(content, filePath) { // Post-format validation const finalContent = postProcessMDXContent(formattedResult.formatted, filePath); - console.log(`✅ Successfully formatted MDX file: ${path.basename(filePath)}`); + // Successfully formatted MDX file return finalContent; } catch (error) { - console.error(`❌ Failed to format MDX file ${filePath}:`, error.message); - console.warn(`⚠️ Returning unformatted content as fallback`); + // Failed to format MDX file - returning unformatted content as fallback return content; } } @@ -598,7 +594,7 @@ function postProcessMDXContent(content, filePath) { const before = processed.length; processed = processed.replace(fix.pattern, fix.replacement); if (before !== processed.length) { - console.log(` ✓ Applied post-format fix: ${fix.description}`); + // Applied post-format fix } }); @@ -685,7 +681,7 @@ function validateSidebarConfig(config: SidebarConfig, filePath: string): { isVal const isProperSentenceCase = /^[A-Z][a-z]/.test(item.label) || /^[A-Z]+$/.test(item.label); if (!isProperSentenceCase) { - console.warn(`⚠️ Label '${item.label}' at ${currentPath} should use sentence case`); + // Label should use sentence case } } @@ -714,12 +710,11 @@ function formatSidebarFile(sidebarConfig: SidebarConfig, filePath: string): stri const validation = validateSidebarConfig(sidebarConfig, filePath); if (!validation.isValid) { - console.error(`\n❌ Sidebar validation errors in ${path.basename(filePath)}:`); - validation.errors.forEach(error => console.error(` • ${error}`)); + // Sidebar validation errors detected throw new Error(`Invalid sidebar configuration: ${validation.errors.join(', ')}`); } - console.log(`✅ Sidebar validation passed for ${path.basename(filePath)}`); + // Sidebar validation passed // Optimize sidebar configuration for better formatting const optimizedConfig = optimizeSidebarConfig(sidebarConfig); @@ -750,15 +745,14 @@ function formatSidebarFile(sidebarConfig: SidebarConfig, filePath: string): stri // Post-format validation and cleanup const finalContent = postProcessSidebarContent(formattedResult.formatted, filePath); - console.log(`✅ Successfully formatted sidebar file: ${path.basename(filePath)}`); + // Successfully formatted sidebar file return finalContent; } catch (error) { - console.error(`\n❌ Failed to format sidebar file ${filePath}:`); - console.error(` Error: ${error.message}`); + // Failed to format sidebar file // Create a robust fallback sidebar const fallbackContent = generateFallbackSidebar(sidebarConfig, filePath); - console.warn(`⚠️ Using fallback sidebar configuration for ${path.basename(filePath)}`); + // Using fallback sidebar configuration return fallbackContent; } } @@ -851,7 +845,7 @@ function postProcessSidebarContent(content: string, filePath: string): string { processed = processed.replace(fix.pattern, fix.replacement); if (before !== processed.length) { - console.log(` ✓ Applied sidebar post-format fix: ${fix.description}`); + // Applied sidebar post-format fix } }); @@ -887,7 +881,7 @@ module.exports = sidebar.items; `; } catch (fallbackError) { // Ultimate fallback - empty sidebar - console.error(`❌ Critical error generating fallback sidebar:`, fallbackError.message); + // Critical error generating fallback sidebar return `// @ts-check // CRITICAL ERROR: Unable to generate sidebar configuration // Generated: ${timestamp} From 73bd070e71575dfe68fb224c0a6f30b22a0965fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Thu, 18 Sep 2025 14:23:59 -0400 Subject: [PATCH 20/62] add new SBE content integration script + remove old one --- docusaurus.config.js | 1 - package.json | 5 +- scripts/stylusByExampleDocsHandler.ts | 896 -------------------------- scripts/sync-stylus-content.js | 228 +++++++ 4 files changed, 231 insertions(+), 899 deletions(-) delete mode 100644 scripts/stylusByExampleDocsHandler.ts create mode 100644 scripts/sync-stylus-content.js diff --git a/docusaurus.config.js b/docusaurus.config.js index d2c2a7e6bd..ff6b10b770 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -105,7 +105,6 @@ const config = { 'typedoc-plugin-markdown', `typedoc-plugin-frontmatter`, './scripts/sdkDocsHandler.ts', - './scripts/stylusByExampleDocsHandler.ts', ], // typedoc-plugin-markdown options diff --git a/package.json b/package.json index 9c4d66c99d..87249165ec 100644 --- a/package.json +++ b/package.json @@ -8,11 +8,12 @@ "generate-precompiles-ref-tables": "tsx scripts/precompile-reference-generator.ts", "update-variable-refs": "tsx scripts/update-variable-references.ts", "prepare": "husky || true", - "start": "yarn clear && yarn build-glossary && docusaurus start", + "start": "yarn clear && yarn sync-stylus-content && yarn build-glossary && docusaurus start", "generate-sdk-docs": "npx docusaurus generate-typedoc", + "sync-stylus-content": "node scripts/sync-stylus-content.js", "build-glossary": "tsx scripts/build-glossary.ts", "build-translation": "yarn tsx ./scripts/move-untranslated-files.ts", - "build": "yarn install-sdk-dependencies && yarn build-glossary && docusaurus build", + "build": "yarn install-sdk-dependencies && yarn sync-stylus-content && yarn build-glossary && docusaurus build", "swizzle": "docusaurus swizzle", "deploy": "docusaurus deploy", "clear": "docusaurus clear", diff --git a/scripts/stylusByExampleDocsHandler.ts b/scripts/stylusByExampleDocsHandler.ts deleted file mode 100644 index dc4ee465bd..0000000000 --- a/scripts/stylusByExampleDocsHandler.ts +++ /dev/null @@ -1,896 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -const { RendererEvent } = require('typedoc'); -const { parseMarkdownContentTitle } = require('@docusaurus/utils'); -const prettier = require('prettier'); - -// Configuration constants -const allowList = [ - 'hello_world', - 'primitive_data_types', - 'variables', - 'constants', - 'function', - 'errors', - 'events', - 'inheritance', - 'vm_affordances', - 'sending_ether', - 'function_selector', - 'abi_encode', - 'abi_decode', - 'hashing', - 'bytes_in_bytes_out', -]; - -const appsAllowList = ['erc20', 'erc721', 'vending_machine', 'multi_call']; - -// Main plugin function -function load(app) { - // Define output directories - const outputDir = path.join(app.options.getValue('out'), '../../docs/stylus-by-example'); - const basicExamplesOutputDir = path.join(outputDir, 'basic_examples'); - const applicationsOutputDir = path.join(outputDir, 'applications'); - - // Define source directories - const sourceDirBasicExamples = path.join( - app.options.getValue('out'), - '../../submodules/stylus-by-example/src/app/basic_examples', - ); - const sourceDirApplications = path.join( - app.options.getValue('out'), - '../../submodules/stylus-by-example/src/app/applications', - ); - - // Register event handlers - app.renderer.on(RendererEvent.START, async () => { - cleanDirectory(outputDir); - }); - - app.renderer.on(RendererEvent.END, async () => { - // Copy basic examples into their directory - copyFiles(sourceDirBasicExamples, basicExamplesOutputDir, allowList); - - // Generate sidebar for basic examples - const basicExamplesSidebarItems = generateSidebar(basicExamplesOutputDir, '/basic_examples'); - const basicExamplesSidebarConfig = { items: basicExamplesSidebarItems }; - const basicExamplesSidebarPath = path.join(basicExamplesOutputDir, 'sidebar.js'); - - const basicSidebarContent = formatSidebarFile(basicExamplesSidebarConfig, basicExamplesSidebarPath); - fs.writeFileSync(basicExamplesSidebarPath, basicSidebarContent, 'utf8'); - - // Copy applications into their directory - copyFiles(sourceDirApplications, applicationsOutputDir, appsAllowList); - - // Generate sidebar for applications - const applicationsSidebarItems = generateSidebar(applicationsOutputDir, '/applications'); - const applicationsSidebarConfig = { items: applicationsSidebarItems }; - const applicationsSidebarPath = path.join(applicationsOutputDir, 'sidebar.js'); - - const appsSidebarContent = formatSidebarFile(applicationsSidebarConfig, applicationsSidebarPath); - fs.writeFileSync(applicationsSidebarPath, appsSidebarContent, 'utf8'); - }); -} - -// Utility functions -function cleanDirectory(directory) { - if (fs.existsSync(directory)) { - fs.readdirSync(directory).forEach((file) => { - const curPath = path.join(directory, file); - - if (fs.lstatSync(curPath).isDirectory()) { - cleanDirectory(curPath); - fs.rmdirSync(curPath); - } else { - fs.unlinkSync(curPath); - } - }); - } -} - -function copyFiles(source, target, allowList) { - // Validate source directory - if (!fs.existsSync(source)) { - return; - } - - if (!fs.lstatSync(source).isDirectory()) { - return; - } - - // Create target directory - fs.mkdirSync(target, { recursive: true }); - - function processDirectory(dirPath, targetPath, isRoot = false) { - const entries = fs.readdirSync(dirPath, { withFileTypes: true }); - const hasChildDirectories = entries.some((entry) => entry.isDirectory()); - - if (isRoot) { - // Special handling for root directory - entries.forEach((entry) => { - const sourcePath = path.join(dirPath, entry.name); - - if (entry.isDirectory()) { - processDirectory(sourcePath, targetPath); - } - }); - } else if (hasChildDirectories) { - // This directory has subdirectories, so we preserve its structure - const currentDirName = path.basename(dirPath); - const newTargetPath = path.join(targetPath, currentDirName); - fs.mkdirSync(newTargetPath, { recursive: true }); - - entries.forEach((entry) => { - if (!allowList.includes(entry.name)) { - return; - } - - const sourcePath = path.join(dirPath, entry.name); - - if (entry.isDirectory()) { - processDirectory(sourcePath, newTargetPath); - } else if (entry.name === 'page.mdx') { - const content = fs.readFileSync(sourcePath, 'utf8'); - const convertedContent = convertMetadataToFrontmatter(content); - const validatedContent = validateContent(convertedContent, sourcePath); - const targetFilePath = path.join(newTargetPath, entry.name); - const formattedContent = formatMDXFile(validatedContent, targetFilePath); - fs.writeFileSync(targetFilePath, formattedContent); - } - }); - } else { - // This directory only contains files, so we flatten it - const mdxFile = entries.find((entry) => entry.isFile() && entry.name === 'page.mdx'); - - if (mdxFile) { - const parentDirName = path.basename(dirPath); - - if (!allowList.includes(parentDirName)) { - return; - } - - const sourcePath = path.join(dirPath, mdxFile.name); - const newFileName = `${parentDirName}.mdx`; - const targetFilePath = path.join(targetPath, newFileName); - const content = fs.readFileSync(sourcePath, 'utf8'); - const convertedContent = convertMetadataToFrontmatter(content); - const validatedContent = validateContent(convertedContent, sourcePath); - const convertedContentWithBanner = - addAdmonitionOneLineAboveFirstCodeBlock(validatedContent); - const formattedContent = formatMDXFile(convertedContentWithBanner, targetFilePath); - fs.writeFileSync(targetFilePath, formattedContent); - } - } - } - - processDirectory(source, target, true); -} - -// Content processing constants and functions -const firstCodeBlock = `\`\`\`rust`; -const admonitionNotForProduction = ` -import NotForProductionBannerPartial from '../../partials/_not-for-production-banner-partial.mdx'; - - -`; - -function addAdmonitionOneLineAboveFirstCodeBlock(content) { - const index = content.indexOf(firstCodeBlock); - - if (index === -1) { - // firstCodeBlock not found - return content; // Return original content instead of undefined - } - - // Find the position two lines before firstCodeBlock - const lines = content.substring(0, index).split('\n'); - - if (lines.length < 2) { - // Not enough lines before code block for banner insertion - return content; - } - - const insertLineIndex = Math.max(0, lines.length - 2); - lines.splice(insertLineIndex, 0, admonitionNotForProduction); - - const newText = lines.join('\n') + content.substring(index); - return newText; -} - -function convertMetadataToFrontmatter(content) { - const metadataRegex = /export const metadata = ({[\s\S]*?});/; - const match = content.match(metadataRegex); - - if (match) { - let metadataObj; - - try { - // Safer parsing without eval() - // First, try to parse as a JavaScript object literal - const metadataString = match[1] - .trim() - // Handle single quotes to double quotes - .replace(/'/g, '"') - // Handle unquoted keys - .replace(/(\w+):/g, '"$1":') - // Handle template literals (basic conversion) - .replace(/`([^`]*)`/g, '"$1"') - // Clean up any double-double quotes - .replace(/""/g, '"'); - - try { - metadataObj = JSON.parse(metadataString); - } catch (jsonError) { - // If JSON parsing fails, try a more lenient approach - // Create a Function constructor (safer than eval but still evaluate the code) - const func = new Function('return ' + match[1]); - metadataObj = func(); - } - } catch (error) { - // Error parsing metadata - return content; - } - - // Convert object to YAML-style string - let metadataContent = Object.entries(metadataObj) - .map(([key, value]) => { - // Escape double quotes and wrap value in quotes - const safeValue = String(value).replace(/"/g, '\\"'); - return `${key}: "${safeValue}"`; - }) - .join('\n'); - - const frontmatter = `---\n${metadataContent}\n---\n\n`; - return content.replace(metadataRegex, frontmatter); - } - - return content; -} - -// Sidebar generation functions -/** - * Sorts entries based on the allowList order - */ -function sortEntries(a, b) { - const allowListIndexA = allowList.indexOf(a.replace('.mdx', '')); - const allowListIndexB = allowList.indexOf(b.replace('.mdx', '')); - - if (allowListIndexA !== -1 && allowListIndexB !== -1) { - return allowListIndexA - allowListIndexB; - } else if (allowListIndexA !== -1) { - return -1; - } else if (allowListIndexB !== -1) { - return 1; - } - - return a.localeCompare(b); -} - -function generateSidebar(dir, basePath = '') { - const entries = fs.readdirSync(dir, { withFileTypes: true }); - entries.sort((a, b) => sortEntries(a.name, b.name)); - - const items = entries - .map((entry) => { - const fullPath = path.join(dir, entry.name); - - if (entry.isDirectory()) { - const subItems = generateSidebar(fullPath, `${basePath}/${entry.name}`); - - if (subItems.length === 0) { - return null; // Filter out empty categories - } - - const label = capitalizeWords(entry.name.replace(/_/g, ' ')); - return { - type: 'category', - label, - items: subItems, - }; - } else if (entry.isFile() && entry.name.endsWith('.mdx')) { - const docId = generateId(entry.name, basePath); - return { - type: 'doc', - id: docId, - label: generateLabel(entry), - }; - } - }) - .filter((item) => !!item); - - return items; -} - -function capitalizeWords(string) { - return string.replace(/\b\w/g, (l) => l.toUpperCase()); -} - -function generateId(name, basePath) { - const label = getLabelFromFilesystem(name); - return 'stylus-by-example' + (basePath + '/' + label).toLowerCase(); -} - -function generateLabel(entry) { - const filePath = `${entry.path}/${entry.name}`; - const titleFromFile = getTitleFromFileContent(filePath); - const label = titleFromFile || getLabelFromFilesystem(entry.name); - return capitalizeWords(label.replace(/_/g, ' ')); -} - -function getLabelFromFilesystem(name) { - return name.replace(/\.mdx$/, ''); -} - -function getTitleFromFileContent(filePath) { - if (!fs.existsSync(filePath)) { - return ''; - } - - const fileContent = fs.readFileSync(filePath, 'utf8'); - const { contentTitle } = parseMarkdownContentTitle(fileContent); - - return contentTitle || ''; -} - -// Content validation and formatting functions -/** - * Comprehensive typo and content validation - */ -function validateContent(content, filePath) { - let validatedContent = content; - let fixesApplied = []; - - // Comprehensive typo database - const typoDatabase = { - // Common English typos - 'followinge': 'following', - 'recieve': 'receive', - 'occured': 'occurred', - 'seperate': 'separate', - 'definately': 'definitely', - 'occassion': 'occasion', - 'untill': 'until', - 'acheive': 'achieve', - 'arguement': 'argument', - 'existance': 'existence', - 'begining': 'beginning', - 'calender': 'calendar', - 'cemetary': 'cemetery', - 'changable': 'changeable', - 'collegue': 'colleague', - 'concious': 'conscious', - 'definate': 'definite', - 'embarass': 'embarrass', - 'enviroment': 'environment', - 'goverment': 'government', - 'independant': 'independent', - 'neccessary': 'necessary', - 'occurance': 'occurrence', - 'perseverence': 'perseverance', - 'priviledge': 'privilege', - 'publically': 'publicly', - 'recomend': 'recommend', - 'refered': 'referred', - 'relevent': 'relevant', - 'supercede': 'supersede', - 'tendancy': 'tendency', - 'truely': 'truly', - 'wierd': 'weird', - - // Technical/Programming typos - 'lenght': 'length', - 'widht': 'width', - 'heigth': 'height', - 'indeces': 'indices', - 'accesible': 'accessible', - 'compatable': 'compatible', - 'dependancy': 'dependency', - 'efficency': 'efficiency', - 'exectuable': 'executable', - 'flexable': 'flexible', - 'initialise': 'initialize', - 'paramater': 'parameter', - 'privilage': 'privilege', - 'proccess': 'process', - 'seperation': 'separation', - 'succesful': 'successful', - 'syncronous': 'synchronous', - 'trasaction': 'transaction', - 'varient': 'variant', - 'verfication': 'verification', - - // Blockchain/Crypto specific typos - 'blokchain': 'blockchain', - 'cryptocurency': 'cryptocurrency', - 'ethereum': 'Ethereum', - 'bitcoin': 'Bitcoin', - 'smartcontract': 'smart contract', - 'decentalized': 'decentralized', - 'consesus': 'consensus', - 'validater': 'validator', - 'tranaction': 'transaction', - 'addres': 'address', - 'ballance': 'balance', - 'recipent': 'recipient', - 'recepient': 'recipient', - 'wallet': 'wallet', - 'minning': 'mining', - 'hasrate': 'hashrate', - 'merkletree': 'Merkle tree', - 'nounce': 'nonce', - 'dapp': 'dApp', - 'defi': 'DeFi', - 'nft': 'NFT', - 'dao': 'DAO', - 'api': 'API', - 'sdk': 'SDK', - 'evm': 'EVM', - 'l1': 'Layer 1', - 'l2': 'Layer 2', - 'l3': 'Layer 3', - - // Case sensitivity corrections for proper nouns - 'solidity': 'Solidity', - 'rust': 'Rust', - 'javascript': 'JavaScript', - 'typescript': 'TypeScript', - 'nodejs': 'Node.js', - 'github': 'GitHub', - 'docker': 'Docker', - 'kubernetes': 'Kubernetes', - 'aws': 'AWS', - 'gcp': 'GCP', - 'azure': 'Azure', - - // Common markdown/documentation issues - 'eg.': 'e.g.,', - 'ie.': 'i.e.,', - 'etc.': 'etc.', - 'vs.': 'vs.', - }; - - // Apply typo fixes with word boundary checks to avoid partial matches - for (const [typo, correction] of Object.entries(typoDatabase)) { - const regex = new RegExp(`\\b${typo.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`, 'gi'); - const matches = validatedContent.match(regex); - - if (matches) { - validatedContent = validatedContent.replace(regex, correction); - fixesApplied.push(`"${typo}" -> "${correction}" (${matches.length} occurrence${matches.length > 1 ? 's' : ''})`); - } - } - - // Fix common markdown formatting issues - const markdownFixes = [ - // Fix missing spaces after list markers - { pattern: /^(\s*[-*+])([^\s])/gm, replacement: '$1 $2', description: 'Missing space after list marker' }, - // Fix multiple consecutive spaces - { pattern: / +/g, replacement: ' ', description: 'Multiple consecutive spaces' }, - // Fix trailing spaces - { pattern: / +$/gm, replacement: '', description: 'Trailing spaces' }, - // Fix inconsistent code block formatting - { pattern: /```(\w+)\s*\n/g, replacement: '```$1\n', description: 'Code block language formatting' }, - // Fix missing space after headers - { pattern: /^(#+)([^\s#])/gm, replacement: '$1 $2', description: 'Missing space after header marker' }, - // Fix double spaces in sentences - { pattern: /([.!?]) +/g, replacement: '$1 ', description: 'Double spaces after punctuation' }, - ]; - - for (const fix of markdownFixes) { - const beforeLength = validatedContent.length; - validatedContent = validatedContent.replace(fix.pattern, fix.replacement); - const afterLength = validatedContent.length; - - if (beforeLength !== afterLength) { - fixesApplied.push(fix.description); - } - } - - // Log all fixes applied - if (fixesApplied.length > 0) { - // Content fixes applied - } - - return validatedContent; -} - -/** - * Formats MDX file with Prettier and additional content validation - */ -function formatMDXFile(content, filePath) { - try { - // Pre-format validation and fixes - let processedContent = content; - - // Apply Prettier-compatible pre-processing - processedContent = preprocessMDXContent(processedContent); - - // Format with Prettier using enhanced configuration - const formattedResult = prettier.formatWithCursor(processedContent, { - cursorOffset: 0, - parser: 'mdx', - filepath: filePath, - // Enhanced Prettier options for better MDX formatting - printWidth: 100, - tabWidth: 2, - useTabs: false, - singleQuote: true, - trailingComma: 'all', - proseWrap: 'preserve', - semi: true, - bracketSpacing: true, - arrowParens: 'avoid', - endOfLine: 'lf', - // MDX-specific options - embeddedLanguageFormatting: 'auto', - }); - - // Post-format validation - const finalContent = postProcessMDXContent(formattedResult.formatted, filePath); - - // Successfully formatted MDX file - return finalContent; - } catch (error) { - // Failed to format MDX file - returning unformatted content as fallback - return content; - } -} - -/** - * Pre-processes MDX content for better Prettier compatibility - */ -function preprocessMDXContent(content) { - let processed = content; - - // Fix common MDX formatting issues that Prettier might struggle with - const fixes = [ - // Ensure proper spacing around JSX components - { pattern: /(<[A-Z][^>]*>)(\S)/g, replacement: '$1\n$2', description: 'JSX component spacing' }, - { pattern: /(\S)(<\/[A-Z][^>]*>)/g, replacement: '$1\n$2', description: 'JSX closing tag spacing' }, - - // Fix frontmatter formatting - { pattern: /^---\s*\n([\s\S]*?)\n\s*---/m, replacement: (match, frontmatter) => { - return `---\n${frontmatter.trim()}\n---`; - }, description: 'Frontmatter formatting' }, - - // Ensure proper code block formatting - { pattern: /```(\w+)\s*\n\s*\n/g, replacement: '```$1\n', description: 'Code block spacing' }, - - // Fix import statements formatting - { pattern: /^import\s+(.+?)\s+from\s+['"](.+?)['"];?\s*$/gm, replacement: "import $1 from '$2';", description: 'Import statement formatting' }, - ]; - - fixes.forEach(fix => { - if (typeof fix.replacement === 'function') { - processed = processed.replace(fix.pattern, fix.replacement); - } else { - processed = processed.replace(fix.pattern, fix.replacement); - } - }); - - return processed; -} - -/** - * Post-processes formatted MDX content for final validation - */ -function postProcessMDXContent(content, filePath) { - let processed = content; - - // Final validation and cleanup - const postFixes = [ - // Ensure single newline at end of file - { pattern: /\n*$/, replacement: '\n', description: 'Single trailing newline' }, - - // Remove excessive blank lines - { pattern: /\n{3,}/g, replacement: '\n\n', description: 'Excessive blank lines' }, - - // Ensure proper spacing after headers - { pattern: /^(#{1,6}\s+.+)(?=\n[^\n#])/gm, replacement: '$1\n', description: 'Header spacing' }, - ]; - - postFixes.forEach(fix => { - const before = processed.length; - processed = processed.replace(fix.pattern, fix.replacement); - if (before !== processed.length) { - // Applied post-format fix - } - }); - - return processed; -} - -// Sidebar configuration types and validation -/** - * Enhanced sidebar configuration types for better type safety - */ -interface SidebarItem { - type: 'doc' | 'category' | 'link' | 'html' | 'ref'; - id?: string; - label: string; - items?: SidebarItem[]; - href?: string; - className?: string; - customProps?: Record; -} - -interface SidebarConfig { - items: SidebarItem[]; -} - -/** - * Validates sidebar configuration against Docusaurus conventions - */ -function validateSidebarConfig(config: SidebarConfig, filePath: string): { isValid: boolean; errors: string[] } { - const errors: string[] = []; - const seenIds = new Set(); - const seenLabels = new Set(); - - function validateItem(item: SidebarItem, path: string = '') { - const currentPath = path ? `${path}.${item.label}` : item.label; - - // Validate required fields - if (!item.type) { - errors.push(`Missing 'type' field at ${currentPath}`); - } - - if (!item.label || typeof item.label !== 'string') { - errors.push(`Missing or invalid 'label' field at ${currentPath}`); - } - - // Validate type-specific requirements - switch (item.type) { - case 'doc': - if (!item.id) { - errors.push(`Missing 'id' field for doc item at ${currentPath}`); - } else { - // Check for duplicate IDs - if (seenIds.has(item.id)) { - errors.push(`Duplicate ID '${item.id}' found at ${currentPath}`); - } - seenIds.add(item.id); - } - break; - - case 'category': - if (!item.items || !Array.isArray(item.items)) { - errors.push(`Missing or invalid 'items' array for category at ${currentPath}`); - } else if (item.items.length === 0) { - errors.push(`Empty 'items' array for category at ${currentPath}`); - } - break; - - case 'link': - if (!item.href) { - errors.push(`Missing 'href' field for link item at ${currentPath}`); - } - break; - } - - // Check for duplicate labels at the same level - const labelKey = `${path}:${item.label}`; - - if (seenLabels.has(labelKey)) { - errors.push(`Duplicate label '${item.label}' found at ${currentPath}`); - } - seenLabels.add(labelKey); - - // Validate label conventions (sentence case) - if (item.label && typeof item.label === 'string') { - const isProperSentenceCase = /^[A-Z][a-z]/.test(item.label) || /^[A-Z]+$/.test(item.label); - - if (!isProperSentenceCase) { - // Label should use sentence case - } - } - - // Recursively validate nested items - if (item.items && Array.isArray(item.items)) { - item.items.forEach(subItem => validateItem(subItem, currentPath)); - } - } - - if (!config.items || !Array.isArray(config.items)) { - errors.push('Root configuration must have an "items" array'); - } else { - config.items.forEach(item => validateItem(item)); - } - - return { isValid: errors.length === 0, errors }; -} - -// Sidebar formatting functions -/** - * Formats sidebar file with enhanced Docusaurus compliance and Prettier formatting - */ -function formatSidebarFile(sidebarConfig: SidebarConfig, filePath: string): string { - try { - // Validate sidebar configuration - const validation = validateSidebarConfig(sidebarConfig, filePath); - - if (!validation.isValid) { - // Sidebar validation errors detected - throw new Error(`Invalid sidebar configuration: ${validation.errors.join(', ')}`); - } - - // Sidebar validation passed - - // Optimize sidebar configuration for better formatting - const optimizedConfig = optimizeSidebarConfig(sidebarConfig); - - // Generate enhanced Docusaurus-compliant sidebar content - const sidebarContent = generateSidebarContent(optimizedConfig); - - // Apply Prettier formatting with enhanced configuration - const formattedResult = prettier.formatWithCursor(sidebarContent, { - cursorOffset: 0, - parser: 'babel', - filepath: filePath, - // Enhanced Prettier options for better Docusaurus compatibility - singleQuote: true, - trailingComma: 'all', - printWidth: 100, - tabWidth: 2, - semi: true, - bracketSpacing: true, - arrowParens: 'avoid', - endOfLine: 'lf', - quoteProps: 'as-needed', - // Object formatting options - bracketSameLine: false, - objectCurlySpacing: true, - }); - - // Post-format validation and cleanup - const finalContent = postProcessSidebarContent(formattedResult.formatted, filePath); - - // Successfully formatted sidebar file - return finalContent; - } catch (error) { - // Failed to format sidebar file - - // Create a robust fallback sidebar - const fallbackContent = generateFallbackSidebar(sidebarConfig, filePath); - // Using fallback sidebar configuration - return fallbackContent; - } -} - -/** - * Optimizes sidebar configuration for better formatting and compliance - */ -function optimizeSidebarConfig(config: SidebarConfig): SidebarConfig { - function optimizeItem(item: SidebarItem): SidebarItem { - const optimized: SidebarItem = { - type: item.type, - label: item.label.trim(), - }; - - // Add type-specific properties in a consistent order - if (item.id) optimized.id = item.id.trim(); - if (item.href) optimized.href = item.href.trim(); - if (item.className) optimized.className = item.className.trim(); - - // Recursively optimize nested items - if (item.items && Array.isArray(item.items)) { - optimized.items = item.items.map(optimizeItem); - } - - // Add custom properties last - if (item.customProps) optimized.customProps = item.customProps; - - return optimized; - } - - return { - items: config.items.map(optimizeItem), - }; -} - -/** - * Generates properly formatted sidebar content with enhanced documentation - */ -function generateSidebarContent(config: SidebarConfig): string { - const timestamp = new Date().toISOString(); - const configJson = JSON.stringify(config, null, 2); - - return `// @ts-check -/** - * @fileoverview Autogenerated sidebar configuration for Stylus by Example - * @description This file is automatically generated by stylusByExampleDocsHandler.ts - * @generated ${timestamp} - * @see https://docusaurus.io/docs/sidebar - * @see https://docusaurus.io/docs/sidebar/items - */ - -/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const sidebarConfig = ${configJson}; - -/** - * Export only the items array as required by Docusaurus - * @type {import('@docusaurus/plugin-content-docs').SidebarItem[]} - */ -module.exports = sidebarConfig.items; -`; -} - -/** - * Post-processes formatted sidebar content for final validation - */ -function postProcessSidebarContent(content: string, filePath: string): string { - let processed = content; - - // Final validation and cleanup for JavaScript/TypeScript files - const postFixes = [ - // Ensure proper module.exports formatting - { - pattern: /module\.exports\s*=\s*sidebarConfig\.items\s*;?\s*$/m, - replacement: 'module.exports = sidebarConfig.items;', - description: 'Module exports formatting' - }, - - // Ensure single newline at end of file - { pattern: /\n*$/, replacement: '\n', description: 'Single trailing newline' }, - - // Fix any double semicolons - { pattern: /;;+/g, replacement: ';', description: 'Double semicolons' }, - - // Ensure proper spacing in JSDoc comments - { pattern: /\/\*\*\s*\n\s*\*\s*/g, replacement: '/**\n * ', description: 'JSDoc formatting' }, - ]; - - postFixes.forEach(fix => { - const before = processed.length; - processed = processed.replace(fix.pattern, fix.replacement); - - if (before !== processed.length) { - // Applied sidebar post-format fix - } - }); - - return processed; -} - -/** - * Generates a robust fallback sidebar when formatting fails - */ -function generateFallbackSidebar(config: SidebarConfig, filePath: string): string { - const timestamp = new Date().toISOString(); - - try { - // Try to create a minimal but valid sidebar - const minimalConfig = { - items: config.items.map(item => ({ - type: item.type, - label: item.label, - ...(item.id && { id: item.id }), - ...(item.items && { items: item.items }), - })), - }; - - return `// @ts-check -// WARNING: This is a fallback sidebar generated due to formatting errors -// Generated: ${timestamp} -// File: ${path.basename(filePath)} - -/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const sidebar = ${JSON.stringify(minimalConfig, null, 2)}; - -module.exports = sidebar.items; -`; - } catch (fallbackError) { - // Ultimate fallback - empty sidebar - // Critical error generating fallback sidebar - return `// @ts-check -// CRITICAL ERROR: Unable to generate sidebar configuration -// Generated: ${timestamp} -// File: ${path.basename(filePath)} - -module.exports = []; -`; - } -} - -// Export the main load function -exports.load = load; diff --git a/scripts/sync-stylus-content.js b/scripts/sync-stylus-content.js new file mode 100644 index 0000000000..972a9d2331 --- /dev/null +++ b/scripts/sync-stylus-content.js @@ -0,0 +1,228 @@ +#!/usr/bin/env node + +/** + * Simple script to sync content from stylus-by-example submodule to docs directory + * Replaces the complex 897-line stylusByExampleDocsHandler.ts + */ + +const fs = require('fs-extra'); +const path = require('path'); +const glob = require('glob'); + +// Configuration +const SOURCE_DIR = path.join(__dirname, '../submodules/stylus-by-example/src/app'); +const TARGET_DIR = path.join(__dirname, '../docs/stylus-by-example'); +const DONT_EDIT_MARKER = 'DONT-EDIT-THIS-FOLDER'; + +/** + * Convert Next.js style metadata export to Docusaurus frontmatter + */ +function convertMetadataToFrontmatter(content) { + // Pattern to match: export const metadata = { ... } + const metadataPattern = /export\s+const\s+metadata\s*=\s*({[\s\S]*?});/; + const match = content.match(metadataPattern); + + if (!match) { + return content; + } + + try { + // Extract and parse the metadata object + const metadataStr = match[1]; + // Use Function constructor to safely evaluate the object literal + const metadata = new Function(`return ${metadataStr}`)(); + + // Convert to YAML frontmatter + const frontmatter = Object.entries(metadata) + .map(([key, value]) => { + // Handle different value types + if (typeof value === 'string') { + // Escape single quotes in strings + return `${key}: '${value.replace(/'/g, "''")}'`; + } + return `${key}: ${value}`; + }) + .join('\n'); + + // Replace the export with frontmatter + return content.replace(metadataPattern, `---\n${frontmatter}\n---`); + } catch (error) { + console.warn(`Warning: Could not convert metadata: ${error.message}`); + return content; + } +} + +/** + * Process and copy a single MDX file + */ +async function processFile(sourcePath, targetPath) { + try { + // Read source file + let content = await fs.readFile(sourcePath, 'utf8'); + + // Transform metadata to frontmatter + content = convertMetadataToFrontmatter(content); + + // Add content begin marker if not present + if (!content.includes('{/* Begin Content */}')) { + // Find the position after frontmatter + const frontmatterEnd = content.indexOf('---', 3); + if (frontmatterEnd !== -1) { + const insertPos = content.indexOf('\n', frontmatterEnd + 3) + 1; + content = content.slice(0, insertPos) + '\n{/* Begin Content */}\n' + content.slice(insertPos); + } + } + + // Ensure target directory exists + await fs.ensureDir(path.dirname(targetPath)); + + // Write transformed content + await fs.writeFile(targetPath, content); + + return true; + } catch (error) { + console.error(`Error processing ${sourcePath}: ${error.message}`); + return false; + } +} + +/** + * Generate sidebar configuration for a directory + */ +function generateSidebar(sectionName, files) { + const items = files + .map(file => { + // Get the relative path from the section directory + const relativePath = path.relative(path.join(TARGET_DIR, sectionName), file); + const fileName = path.basename(file, '.mdx'); + const dirName = path.dirname(relativePath); + + // Skip if filename is same as directory (already processed) + if (fileName === sectionName) { + return null; + } + + // Convert filename to label (snake_case to Title Case) + const label = fileName + .split('_') + .map(word => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); + + // Generate doc ID based on actual file structure + // For files in subdirectories: stylus-by-example/applications/erc20/erc20 + const docId = dirName === '.' + ? `stylus-by-example/${sectionName}/${fileName}` + : `stylus-by-example/${sectionName}/${dirName}/${fileName}`; + + return { + type: 'doc', + label: label, + id: docId + }; + }) + .filter(Boolean); + + return `// @ts-check +/** + * @fileoverview Autogenerated sidebar configuration for Stylus by Example + * @description This file is automatically generated by sync-stylus-content.js + * @generated ${new Date().toISOString()} + * @see https://docusaurus.io/docs/sidebar + * @see https://docusaurus.io/docs/sidebar/items + */ + +/** @type {import('@docusaurus/plugin-content-docs').SidebarItem[]} */ +module.exports = ${JSON.stringify(items, null, 2)}; +`; +} + +/** + * Main sync function + */ +async function syncContent() { + console.log('🔄 Syncing Stylus by Example content...'); + + try { + // Check if source exists + if (!await fs.pathExists(SOURCE_DIR)) { + console.error(`❌ Source directory not found: ${SOURCE_DIR}`); + console.log('Make sure the submodule is initialized:'); + console.log(' git submodule update --init --recursive'); + process.exit(1); + } + + // Clean target directory + if (await fs.pathExists(TARGET_DIR)) { + console.log('🧹 Cleaning target directory...'); + await fs.remove(TARGET_DIR); + } + + // Create target directory + await fs.ensureDir(TARGET_DIR); + + // Add marker file + await fs.writeFile( + path.join(TARGET_DIR, DONT_EDIT_MARKER), + `This folder is auto-generated from submodules/stylus-by-example +Do not edit files directly here. Edit them in the submodule instead. +Generated: ${new Date().toISOString()} +` + ); + + // Find all page.mdx files + const pattern = path.join(SOURCE_DIR, '**/page.mdx'); + const files = glob.sync(pattern); + + console.log(`📁 Found ${files.length} files to process`); + + // Process each file + let successCount = 0; + for (const sourceFile of files) { + // Calculate target path + const relativePath = path.relative(SOURCE_DIR, sourceFile); + const targetDir = path.dirname(relativePath); + + // Rename page.mdx to directory name or keep as index + let targetFileName; + if (targetDir === '.') { + targetFileName = 'index.mdx'; + } else { + // Use the parent directory name as the filename + const parentDir = path.basename(targetDir); + targetFileName = `${parentDir}.mdx`; + } + + const targetFile = path.join(TARGET_DIR, targetDir, targetFileName); + + // Process and copy file + if (await processFile(sourceFile, targetFile)) { + successCount++; + } + } + + // Generate sidebars for each directory + const directories = ['basic_examples', 'applications']; + for (const dir of directories) { + const dirPath = path.join(TARGET_DIR, dir); + if (await fs.pathExists(dirPath)) { + const dirFiles = glob.sync(path.join(dirPath, '**/*.mdx')); + const sidebarContent = generateSidebar(dir, dirFiles); + await fs.writeFile(path.join(dirPath, 'sidebar.js'), sidebarContent); + console.log(`📝 Generated sidebar for ${dir} (${dirFiles.length} items)`); + } + } + + console.log(`✅ Successfully synced ${successCount}/${files.length} files`); + + } catch (error) { + console.error(`❌ Sync failed: ${error.message}`); + process.exit(1); + } +} + +// Run if called directly +if (require.main === module) { + syncContent(); +} + +module.exports = { syncContent }; \ No newline at end of file From 7b5cd18354ef8ca83372fccca7a2d0d7a58e29a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Thu, 18 Sep 2025 14:40:24 -0400 Subject: [PATCH 21/62] file name fix for docs/partials/glossary/_fast-exit-liquidity-exit.mdx --- ...xit--liquidity-exit.mdx => _fast-exit-liquidity-exit.mdx} | 0 vercel.json | 5 +++++ 2 files changed, 5 insertions(+) rename docs/partials/glossary/{_fast-exit--liquidity-exit.mdx => _fast-exit-liquidity-exit.mdx} (100%) diff --git a/docs/partials/glossary/_fast-exit--liquidity-exit.mdx b/docs/partials/glossary/_fast-exit-liquidity-exit.mdx similarity index 100% rename from docs/partials/glossary/_fast-exit--liquidity-exit.mdx rename to docs/partials/glossary/_fast-exit-liquidity-exit.mdx diff --git a/vercel.json b/vercel.json index b895a97ed2..22e8df6f1a 100644 --- a/vercel.json +++ b/vercel.json @@ -225,6 +225,11 @@ "destination": "/build-decentralized-apps/reference/mainnet-risks", "permanent": false }, + { + "source": "/(docs/partials/glossary/_fast-exit--liquidity-exit/?)", + "destination": "/(docs/partials/glossary/_fast-exit-liquidity-exit/?)", + "permanent": false + }, { "source": "/(docs/rollup_protocol/?)", "destination": "/how-arbitrum-works/optimistic-rollup", From c76342387a7749dc9067df52351cfa847dcfa40d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Thu, 18 Sep 2025 15:04:03 -0400 Subject: [PATCH 22/62] add fix attempt for remote vercel build error --- scripts/sync-stylus-content.js | 35 +++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/scripts/sync-stylus-content.js b/scripts/sync-stylus-content.js index 972a9d2331..92e3dbd992 100644 --- a/scripts/sync-stylus-content.js +++ b/scripts/sync-stylus-content.js @@ -145,10 +145,39 @@ async function syncContent() { try { // Check if source exists if (!await fs.pathExists(SOURCE_DIR)) { - console.error(`❌ Source directory not found: ${SOURCE_DIR}`); - console.log('Make sure the submodule is initialized:'); + console.warn(`⚠️ Source directory not found: ${SOURCE_DIR}`); + console.log('Skipping Stylus by Example content sync.'); + console.log('To include this content, initialize the submodule:'); console.log(' git submodule update --init --recursive'); - process.exit(1); + + // Create empty target directory with marker + await fs.ensureDir(TARGET_DIR); + await fs.writeFile( + path.join(TARGET_DIR, DONT_EDIT_MARKER), + `This folder would contain auto-generated content from submodules/stylus-by-example +The submodule was not available during build. +Generated: ${new Date().toISOString()} +` + ); + + // Create empty sidebar files for the expected directories + const expectedDirs = ['basic_examples', 'applications']; + for (const dir of expectedDirs) { + await fs.ensureDir(path.join(TARGET_DIR, dir)); + const emptySidebar = `// @ts-check +/** + * @fileoverview Empty sidebar configuration (submodule not available) + * @generated ${new Date().toISOString()} + */ + +/** @type {import('@docusaurus/plugin-content-docs').SidebarItem[]} */ +module.exports = []; +`; + await fs.writeFile(path.join(TARGET_DIR, dir, 'sidebar.js'), emptySidebar); + } + + console.log('✅ Created empty Stylus by Example structure'); + return; } // Clean target directory From fb27d709d611fd19619a1f6afb91a95820da2b16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Sat, 20 Sep 2025 14:34:27 -0400 Subject: [PATCH 23/62] add fix SBE relative links --- scripts/sync-stylus-content.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/scripts/sync-stylus-content.js b/scripts/sync-stylus-content.js index 92e3dbd992..3075e2c84d 100644 --- a/scripts/sync-stylus-content.js +++ b/scripts/sync-stylus-content.js @@ -52,6 +52,24 @@ function convertMetadataToFrontmatter(content) { } } +/** + * Transform relative links to work with Docusaurus structure + */ +function fixRelativeLinks(content) { + // Fix relative links that point to sibling directories + // When file name matches directory name, Docusaurus simplifies the URL + // Pattern: [text](./directory_name) should become [text](/stylus-by-example/basic_examples/directory_name) + content = content.replace( + /\[([^\]]+)\]\(\.\/([\w_]+)\)/g, + (match, linkText, dirName) => { + // Convert to absolute Docusaurus path (simplified URL when filename matches directory) + return `[${linkText}](/stylus-by-example/basic_examples/${dirName})`; + } + ); + + return content; +} + /** * Process and copy a single MDX file */ @@ -63,6 +81,9 @@ async function processFile(sourcePath, targetPath) { // Transform metadata to frontmatter content = convertMetadataToFrontmatter(content); + // Fix relative links + content = fixRelativeLinks(content); + // Add content begin marker if not present if (!content.includes('{/* Begin Content */}')) { // Find the position after frontmatter From 9ad0c36437bde63349cf4c20cee8c8c3a5b893a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Mon, 22 Sep 2025 19:56:36 -0700 Subject: [PATCH 24/62] add fix to navbar and responsive design --- src/css/partials/_navbar.scss | 16 +++++++++------- src/css/partials/_responsive.scss | 2 +- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/css/partials/_navbar.scss b/src/css/partials/_navbar.scss index ebdb0444fb..0bc3f0ae42 100644 --- a/src/css/partials/_navbar.scss +++ b/src/css/partials/_navbar.scss @@ -1,17 +1,19 @@ .navbar__inner { - position: relative; + display: flex; + align-items: center; + justify-content: space-between; + min-height: 60px; + flex-wrap: nowrap; > .navbar__items:first-child { - position: absolute; - display: block; - left: 10px; - top: 6px; - flex: 0; + flex: 0 0 auto; + margin-right: auto; + padding-left: 10px; } > .navbar__items--right { display: flex; - flex: 1; + flex: 0 0 auto; gap: 8px; // Add spacing between navbar items justify-content: flex-end; align-items: center; diff --git a/src/css/partials/_responsive.scss b/src/css/partials/_responsive.scss index b77842e4df..de948f3611 100644 --- a/src/css/partials/_responsive.scss +++ b/src/css/partials/_responsive.scss @@ -11,7 +11,7 @@ } } -@media (max-width: 996px) { +@media (max-width: 1200px) { .navbar__inner a.navbar__brand { display: none; } From ca8bd220d94a5b3d5ab71c37f11b66ded4b6f966 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Mon, 22 Sep 2025 20:12:32 -0700 Subject: [PATCH 25/62] replace node-map page with overvew page in navbar --- sidebars.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sidebars.js b/sidebars.js index f2183dbe07..7e27cfca69 100644 --- a/sidebars.js +++ b/sidebars.js @@ -819,7 +819,7 @@ const sidebars = { collapsed: false, link: { type: 'doc', - id: 'node-running/node-running-content-map', + id: 'run-arbitrum-node/overview', }, items: [ { From 6fd489cd9a5019c43815dd1b5c5e4a0ade07b370 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Tue, 23 Sep 2025 07:41:13 -0700 Subject: [PATCH 26/62] Rename the "Build apps > Get started" navbar item to "Build apps > Quickstart" --- docusaurus.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docusaurus.config.js b/docusaurus.config.js index ff6b10b770..753ea5e85f 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -261,7 +261,7 @@ const config = { position: 'right', items: [ { - label: 'Get started', + label: 'Quickstart', to: '/build-decentralized-apps/quickstart-solidity-remix', }, { From 57721a4995b4bb83824f68252f7cdbb00798d26d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Tue, 23 Sep 2025 11:17:28 -0700 Subject: [PATCH 27/62] arbitrary commit to prompt vercel deployment --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 335d1da0dd..b110825163 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ git clone git@github.com:OffchainLabs/arbitrum-docs.git yarn ``` 3. Start the development server + ```shell yarn start ``` From ed4d8f177aee788f75bc7680f7636535337ab3cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Wed, 1 Oct 2025 10:57:17 -0700 Subject: [PATCH 28/62] update gitignore to latest --- .gitignore | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 55487e543a..eceaccafff 100644 --- a/.gitignore +++ b/.gitignore @@ -14,13 +14,9 @@ build .docusaurus .cache-loader sdk-sidebar.js -docs/sdk/assetBridger/ -docs/sdk/dataEntities/ -docs/sdk/inbox/ -docs/sdk/message/ -docs/sdk/utils/ -docs/sdk/index.mdx -docs/sdk/migrate.mdx +docs/sdk/ +!docs/sdk/index.mdx +!docs/sdk/migrate.mdx docs/partials/_glossary-partial.mdx docs/stylus-by-example/ docs/api/ From 3c9cd91a9161818086a8d984a3d0c347d74e4492 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Wed, 1 Oct 2025 10:58:24 -0700 Subject: [PATCH 29/62] update to latest --- .husky/pre-commit | 206 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 161 insertions(+), 45 deletions(-) diff --git a/.husky/pre-commit b/.husky/pre-commit index 52c72905e8..f42bd02050 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,74 +1,190 @@ #!/bin/sh # Pre-commit hook for arbitrum-docs repository -# Enforces: redirect validation, submodule updates, code formatting -# Compatible with Husky v10 (no husky.sh sourcing required) +# Enforces: redirect validation, submodule updates, code formatting, and type checking +# Compatible with Husky v9+ (modern Husky architecture) +# Performance optimized with selective file processing and parallel execution -# Color codes for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color +# Exit immediately on any error +set -e -# Error handling functions +# Husky environment detection and CI skip logic +if [ "$HUSKY" = "0" ] || [ "$CI" = "true" ] || [ "$GITHUB_ACTIONS" = "true" ]; then + echo "🚀 Husky pre-commit hook skipped (CI environment or HUSKY=0)" + exit 0 +fi + +# Cross-platform color support detection +if [ -t 1 ] && command -v tput >/dev/null 2>&1 && [ "$(tput colors)" -ge 8 ]; then + RED='\033[0;31m' + GREEN='\033[0;32m' + YELLOW='\033[1;33m' + BLUE='\033[0;34m' + CYAN='\033[0;36m' + BOLD='\033[1m' + NC='\033[0m' +else + RED='' + GREEN='' + YELLOW='' + BLUE='' + CYAN='' + BOLD='' + NC='' +fi + +# Enhanced logging functions with emojis for better UX log_info() { - echo "${BLUE}[INFO]${NC} $1" + printf "${BLUE}ℹ️ [INFO]${NC} %s\n" "$1" } log_success() { - echo "${GREEN}[SUCCESS]${NC} $1" + printf "${GREEN}✅ [SUCCESS]${NC} %s\n" "$1" } log_error() { - echo "${RED}[ERROR]${NC} $1" + printf "${RED}❌ [ERROR]${NC} %s\n" "$1" >&2 } log_warning() { - echo "${YELLOW}[WARNING]${NC} $1" + printf "${YELLOW}⚠️ [WARNING]${NC} %s\n" "$1" } -# Function to exit with error code and message +log_step() { + printf "${CYAN}${BOLD}🔄 %s${NC}\n" "$1" +} + +# Enhanced error handling with rollback suggestions exit_with_error() { - log_error "$1" - exit 1 + log_error "$1" + log_info "💡 Tip: Use 'git commit --no-verify' to bypass hooks (not recommended)" + log_info "💡 Or set HUSKY=0 to disable all hooks temporarily" + exit 1 } -# Function to check command availability +# Function to check command availability with installation hints check_command() { - if ! command -v "$1" >/dev/null 2>&1; then + if ! command -v "$1" >/dev/null 2>&1; then + case "$1" in + yarn) + exit_with_error "Yarn not found. Install with: npm install -g yarn" + ;; + node) + exit_with_error "Node.js not found. Install from: https://nodejs.org/" + ;; + git) + exit_with_error "Git not found. Install from: https://git-scm.com/" + ;; + *) exit_with_error "Command '$1' not found. Please ensure it's installed and in your PATH." - fi + ;; + esac + fi } -# Pre-flight checks -log_info "Starting pre-commit hook validation..." +# Performance timing function +time_command() { + local start_time=$(date +%s) + "$@" + local end_time=$(date +%s) + local duration=$((end_time - start_time)) + log_info "⏱️ Completed in ${duration}s" +} -# Check required commands -check_command "yarn" -check_command "git" +# Get list of staged files for selective processing +get_staged_files() { + git diff --cached --name-only --diff-filter=ACMR +} -# 1. Redirect validation -log_info "Running redirect validation (yarn check-redirects)..." -if ! yarn check-redirects; then - exit_with_error "Redirect validation failed. Please fix redirect issues before committing." -fi -log_success "Redirect validation passed" +# Check if specific file types are staged +has_staged_files() { + get_staged_files | grep -E "$1" >/dev/null 2>&1 +} -# 2. Submodule updates with error checking -log_info "Running git submodule update --init --recursive..." -if ! git submodule update --init --recursive; then - exit_with_error "Git submodule update failed. Please check submodule configuration." -fi -log_success "Git submodules updated successfully" +# Main pre-commit validation +main() { + log_step "Starting pre-commit hook validation..." -# 3. Code formatting with error checking -log_info "Running yarn format..." -if ! yarn format; then - exit_with_error "Code formatting failed. Please check for syntax errors." -fi -log_success "Code formatting completed" + # Environment and dependency checks + log_info "🔍 Checking environment and dependencies..." + check_command "node" + check_command "yarn" + check_command "git" + + # Verify we're in a git repository + if ! git rev-parse --git-dir >/dev/null 2>&1; then + exit_with_error "Not in a git repository" + fi + + # Get staged files for optimization + local staged_files + staged_files=$(get_staged_files) + + if [ -z "$staged_files" ]; then + log_warning "No staged files found. Commit may be empty." + exit 0 + fi + + log_info "📁 Found $(echo "$staged_files" | wc -l | tr -d ' ') staged files" + + # 1. Fast redirect validation (project-specific) + log_step "Validating redirects..." + time_command yarn check-redirects || exit_with_error "Redirect validation failed. Fix redirect issues before committing." + log_success "Redirect validation passed" + + # 2. Submodule updates (only if submodules are staged or .gitmodules changed) + if echo "$staged_files" | grep -E "(\.gitmodules|submodules/)" >/dev/null 2>&1; then + log_step "Updating git submodules..." + time_command git submodule update --init --recursive || exit_with_error "Git submodule update failed. Check submodule configuration." + log_success "Git submodules updated" + else + log_info "⏭️ Skipping submodule update (no submodule changes detected)" + fi + + # 3. Selective code formatting (only format staged files) + if has_staged_files "\.(js|jsx|ts|tsx|json|md|mdx|scss)$"; then + log_step "Formatting staged code files..." + + # Use git to format only staged files for better performance + local js_files md_files + js_files=$(echo "$staged_files" | grep -E "\.(js|jsx|ts|tsx|json|scss)$" || true) + md_files=$(echo "$staged_files" | grep -E "\.(md|mdx)$" || true) + + # Format JavaScript/TypeScript files if any + if [ -n "$js_files" ]; then + log_info "🎨 Formatting JS/TS files..." + echo "$js_files" | xargs yarn prettier --write --config "./.prettierrc.js" || exit_with_error "JavaScript/TypeScript formatting failed" + fi + + # Format Markdown files if any + if [ -n "$md_files" ]; then + log_info "📝 Formatting Markdown files..." + echo "$md_files" | xargs yarn prettier --write --config "./.prettierrc.js" || exit_with_error "Markdown formatting failed" + fi + + # Re-stage formatted files + echo "$staged_files" | grep -E "\.(js|jsx|ts|tsx|json|md|mdx|scss)$" | xargs git add || true + log_success "Code formatting completed and files re-staged" + else + log_info "⏭️ Skipping code formatting (no formattable files staged)" + fi + + # 4. TypeScript type checking (only if TS files are staged) + if has_staged_files "\.(ts|tsx)$"; then + log_step "Running TypeScript type checking..." + time_command yarn typecheck || exit_with_error "TypeScript type checking failed. Fix type errors before committing." + log_success "TypeScript type checking passed" + else + log_info "⏭️ Skipping TypeScript check (no TypeScript files staged)" + fi + + # Final success message with timing + log_success "🎉 All pre-commit checks passed successfully!" + log_info "✨ Commit is ready to proceed..." +} + +# Trap to handle interruptions gracefully +trap 'log_error "Pre-commit hook interrupted"; exit 130' INT TERM -# Final success message -log_success "All pre-commit checks passed successfully!" -log_info "Commit can proceed..." +# Execute main function +main "$@" From 6197bd3334cab4f49d65d4edce60a54bb4656479 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Wed, 1 Oct 2025 10:58:53 -0700 Subject: [PATCH 30/62] update to latest --- README.md | 46 +++++++++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index b110825163..77e8b7da79 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ Arbitrum Docs, built with docusaurus; docs are live at https://developer.arbitru This repository is organized as follows: ### Documentation Content + - **`docs/`** - Main documentation content directory - `arbitrum-bridge/` - Bridge-related documentation - `build-decentralized-apps/` - Developer guides and references @@ -22,21 +23,23 @@ This repository is organized as follows: - `sdk/` - Auto-generated SDK documentation - Do not edit - `stylus/` - Stylus smart contract development - `stylus-by-example/` - Generated Stylus examples - Do not edit - - `get-started/` - Getting started content + - `welcome/` - Getting started content ### Application Code + - **`src/`** - Docusaurus application source code - - `components/` - React components for the documentation site - - `css/` - Styling and themes - - `pages/` - Custom pages and landing pages - - `resources/` - Global variables and configuration - - `scripts/` - Build scripts - - `theme/` - Docusaurus theme customizations + - `components/` - React components for the documentation site + - `css/` - Styling and themes + - `pages/` - Custom pages and landing pages + - `resources/` - Global variables and configuration + - `scripts/` - Build scripts + - `theme/` - Docusaurus theme customizations ### Configuration & Dependencies + - **`submodules/`** - Git submodule containing SDK source code - - **`arbitrum-sdk/`** - Git submodule containing SDK source code - - **`stylus-by-example/`** - Git submodule containing Stylus examples + - **`arbitrum-sdk/`** - Git submodule containing SDK source code + - **`stylus-by-example/`** - Git submodule containing Stylus examples - **`scripts/`** - Repository maintenance, build scripts, and content generators - **`static/`** - Static assets (images, files, JSON data) @@ -46,26 +49,42 @@ For most of the docs content, you can contribute by simply reviewing our [docs c The following are the only exceptions: -- Contributing to the three troubleshooting pages — [nodes](docs/partials/_troubleshooting-nodes-partial.mdx), [builders](docs/partials/_troubleshooting-building-partial.mdx), and [users](docs/partials/_troubleshooting-users-partial.mdx) require internal Offchain Labs access. If you'd like to make a suggestion about content on any of those pages, open an [issue ticket](https://github.com/OffchainLabs/arbitrum-docs/issues). +- Contributing to the three troubleshooting pages — [nodes](docs/partials/_troubleshooting-nodes-partial.mdx), [builders](docs/partials/_troubleshooting-building-partial.mdx), and [users](docs/partials/_troubleshooting-users-partial.mdx) require internal Offchain Labs access. If you'd like to make a suggestion about content on any of those pages, open an [issue ticket](https://github.com/OffchainLabs/arbitrum-docs/issues). - To request to have your project added to the [3rd party node providers page](docs/build-decentralized-apps/reference/01-node-providers.mdx), use [this form](https://docs.google.com/forms/d/e/1FAIpQLSc_v8j7sc4ffE6U-lJJyLMdBoIubf7OIhGtCqvK3cGPGoLr7w/viewform). ### Initial set up + 1. Clone this repo + ```shell git clone git@github.com:OffchainLabs/arbitrum-docs.git ``` + 2.Install node dependencies ```shell yarn ``` -3. Start the development server + +3.Update the submodules + +```shell +git submodule update --init --recursive +``` + +4. Build + +```shell +yarn build +``` + +5. Start the development server ```shell yarn start ``` -Note: SDK docs are generated automatically on fresh clones. Use `SKIP_SDK_DOCS=true` to skip generation when docs already exist. + ### Dev Build To start a build server to serve the docs site locally, run this command from the root directory: @@ -73,6 +92,7 @@ To start a build server to serve the docs site locally, run this command from th ```shell yarn start ``` + ### Build While in the root directory, this command will build the site: @@ -86,6 +106,7 @@ To test the build locally, you can use the following command: ```shell yarn serve ``` + ### Update glossary You can add any terms to the glossary by following these steps: @@ -125,4 +146,3 @@ This part will update the glossary. ### Redirects 1. From the root directory, run `yarn check-redirects`. - From 3c62027557a08438071687a787b444612815aa0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Wed, 1 Oct 2025 11:01:00 -0700 Subject: [PATCH 31/62] update to latest --- .../_bold-public-preview-banner-partial.mdx | 7 - docusaurus.config.js | 215 ++--- scripts/build-glossary.ts | 25 +- scripts/sync-stylus-content.js | 819 +++++++++++++----- 4 files changed, 684 insertions(+), 382 deletions(-) delete mode 100644 docs/how-arbitrum-works/bold/partials/_bold-public-preview-banner-partial.mdx diff --git a/docs/how-arbitrum-works/bold/partials/_bold-public-preview-banner-partial.mdx b/docs/how-arbitrum-works/bold/partials/_bold-public-preview-banner-partial.mdx deleted file mode 100644 index a4a519e9e1..0000000000 --- a/docs/how-arbitrum-works/bold/partials/_bold-public-preview-banner-partial.mdx +++ /dev/null @@ -1,7 +0,0 @@ -:::caution ALPHA RELEASE, PUBLIC PREVIEW DOCS - -The BoLD dispute protocol is currently deployed on a public testnet (that posts assertions to Ethereum Sepolia) and is tagged as an `alpha` release. The code has been audited by [Trail of Bits](https://github.com/trailofbits/publications/blob/master/reviews/2024-04-offchainbold-securityreview.pdf) and in a [public audit competition with Code4rena](https://code4rena.com/audits/2024-05-arbitrum-bold), but **should not be used in production scenarios**. Please note that the public testnet is intended for Arbitrum users and researchers to test and experiment with the BoLD dispute protocol for the purposes of education and hardening the protocol via the surfacing of bugs. The public testnet may be paused, and its parameters may be updated at any time in response to scenarios that impact the network and its ability to fulfill its function as a working, reliable test environment. This documentation is currently in [public preview](../public-preview-expectations.mdx). - -To provide feedback, click the _Request an update_ button at the top of this document, [join the Arbitrum Discord](https://discord.gg/arbitrum), or reach out to our team directly by completing [this form](http://bit.ly/3yy6EUK). - -::: diff --git a/docusaurus.config.js b/docusaurus.config.js index 753ea5e85f..4af0829166 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -4,11 +4,40 @@ require('dotenv').config(); const markdownPreprocessor = require('./scripts/markdown-preprocessor'); -const sdkSidebarGenerator = require('./scripts/sdk-sidebar-generator'); const sdkCodebasePath = './submodules/arbitrum-sdk'; import remarkMath from 'remark-math'; import rehypeKatex from 'rehype-katex'; +// Shared Inkeep configuration +const inkeepBaseSettings = { + apiKey: process.env.INKEEP_API_KEY, + primaryBrandColor: '#213147', + organizationDisplayName: 'Arbitrum', + theme: { + syntaxHighlighter: { + lightTheme: require('prism-react-renderer/themes/github'), + darkTheme: require('prism-react-renderer/themes/palenight'), + }, + }, +}; + +const inkeepModalSettings = { + placeholder: 'Search documentation...', + defaultQuery: '', + maxResults: 40, + debounceTimeMs: 300, + shouldOpenLinksInNewTab: true, +}; + +const inkeepExampleQuestions = [ + 'How to estimate gas in Arbitrum?', + 'What is the difference between Arbitrum One and Nova?', + 'How to deploy a smart contract on Arbitrum?', + 'What are Arbitrum Orbit chains?', + 'How does Arbitrum handle L1 to L2 messaging?', + 'What is Arbitrum Stylus?', +]; + /** @type {import('@docusaurus/types').Config} */ const config = { title: 'Arbitrum Docs', @@ -21,6 +50,22 @@ const config = { markdown: { mermaid: true, preprocessor: markdownPreprocessor, + parseFrontMatter: async (params) => { + // Use the default parser + const result = await params.defaultParseFrontMatter(params); + + // Check if this is a partial file (starts with underscore) + const fileName = params.filePath.split('/').pop(); + const isPartialFile = fileName && fileName.startsWith('_'); + + // For partial files, clear frontmatter to prevent Docusaurus warnings + // The documentation-graph tool reads raw files directly, so this doesn't affect analysis + if (isPartialFile) { + result.frontMatter = {}; + } + + return result; + }, }, themes: ['@docusaurus/theme-mermaid', '@docusaurus/theme-live-codeblock'], // GitHub pages deployment config. @@ -42,10 +87,6 @@ const config = { integrity: 'sha384-odtC+0UGzzFL/6PNoE8rX/SPcQDXBJ+uRepguP4QkPCm2LBxH3FA3y+fKSiJ+AmM', crossorigin: 'anonymous', }, - { - href: '/css/inkeep-custom.css', - type: 'text/css', - }, ], presets: [ [ @@ -53,7 +94,6 @@ const config = { /** @type {import('@docusaurus/preset-classic').Options} */ ({ docs: { - // path: './docs', exclude: ['**/api/**'], remarkPlugins: [remarkMath], rehypePlugins: [rehypeKatex], @@ -132,85 +172,22 @@ const config = { '@inkeep/cxkit-docusaurus', { SearchBar: { - baseSettings: { - apiKey: process.env.INKEEP_API_KEY, - primaryBrandColor: '#213147', // Arbitrum's primary brand color - organizationDisplayName: 'Arbitrum', - theme: { - syntaxHighlighter: { - lightTheme: require('prism-react-renderer/themes/github'), - darkTheme: require('prism-react-renderer/themes/palenight'), - }, - components: { - SearchBarTrigger: { - // Configure responsive sizing behavior - defaultSize: 'medium', - minWidth: '180px', - maxWidth: '320px', - }, - }, - }, - }, - modalSettings: { - placeholder: 'Search documentation...', - defaultQuery: '', - maxResults: 40, - debounceTimeMs: 300, - shouldOpenLinksInNewTab: true, - }, - searchSettings: { - // optional settings - }, + baseSettings: inkeepBaseSettings, + modalSettings: inkeepModalSettings, aiChatSettings: { - aiAssistantAvatar: '/img/logo.svg', // Using Arbitrum logo as AI assistant avatar - exampleQuestions: [ - 'How to estimate gas in Arbitrum?', - 'What is the difference between Arbitrum One and Nova?', - 'How to deploy a smart contract on Arbitrum?', - 'What are Arbitrum Orbit chains?', - 'How does Arbitrum handle L1 to L2 messaging?', - 'What is Arbitrum Stylus?', - ], + aiAssistantAvatar: '/img/logo.svg', + exampleQuestions: inkeepExampleQuestions, botName: 'Arbitrum Assistant', getStartedMessage: "Hi! I'm here to help you navigate Arbitrum documentation. Ask me anything about building on Arbitrum, deploying contracts, or understanding our technology.", }, }, ChatButton: { - baseSettings: { - // see https://docusaurus.io/docs/deployment#using-environment-variables to use docusaurus environment variables - apiKey: process.env.INKEEP_API_KEY, - primaryBrandColor: '#213147', // Arbitrum's primary brand color - organizationDisplayName: 'Arbitrum', - // ...optional settings - theme: { - syntaxHighlighter: { - lightTheme: require('prism-react-renderer/themes/github'), - darkTheme: require('prism-react-renderer/themes/palenight'), - }, - }, - }, - modalSettings: { - placeholder: 'Search documentation...', - defaultQuery: '', - maxResults: 40, - debounceTimeMs: 300, - shouldOpenLinksInNewTab: true, - }, - searchSettings: { - // optional settings - }, + baseSettings: inkeepBaseSettings, + modalSettings: inkeepModalSettings, aiChatSettings: { - // optional settings - aiAssistantAvatar: '/img/logo.svg', // optional -- use your own AI assistant avatar - exampleQuestions: [ - 'How to estimate gas in Arbitrum?', - 'What is the difference between Arbitrum One and Nova?', - 'How to deploy a smart contract on Arbitrum?', - 'What are Arbitrum Orbit chains?', - 'How does Arbitrum handle L1 to L2 messaging?', - 'What is Arbitrum Stylus?', - ], + aiAssistantAvatar: '/img/logo.svg', + exampleQuestions: inkeepExampleQuestions, }, }, }, @@ -246,72 +223,13 @@ const config = { logo: { alt: 'Arbitrum Logo', src: 'img/logo.svg', - href: '/get-started', + href: '/welcome/arbitrum-gentle-introduction', }, - items: [ - { - type: 'docSidebar', - sidebarId: 'getStartedSidebar', - position: 'right', - label: 'Get started', - }, - { - type: 'dropdown', - label: 'Build apps', - position: 'right', - items: [ - { - label: 'Quickstart', - to: '/build-decentralized-apps/quickstart-solidity-remix', - }, - { - label: 'Stylus', - to: '/stylus/gentle-introduction', - }, - ], - }, - { - type: 'docSidebar', - sidebarId: 'runArbitrumChainSidebar', - position: 'right', - label: 'Launch a chain', - }, - // { - // type: 'docSidebar', - // sidebarId: 'stylusSidebar', - // position: 'right', - // label: 'Use Stylus', - // }, - { - type: 'docSidebar', - sidebarId: 'runNodeSidebar', - position: 'right', - label: 'Run a node', - }, - { - type: 'docSidebar', - sidebarId: 'bridgeSidebar', - position: 'right', - label: 'Use the bridge', - }, - { - type: 'docSidebar', - sidebarId: 'howItWorksSidebar', - position: 'right', - label: 'How it works', - }, - { - type: 'docSidebar', - sidebarId: 'additionalResourcesSidebar', - position: 'right', - label: 'Resources', - }, - ], + items: [], }, footer: { style: 'dark', links: [ - {}, { items: [ { @@ -413,6 +331,8 @@ const config = { }, prism: { additionalLanguages: ['solidity', 'rust', 'bash', 'toml'], + theme: require('prism-react-renderer/themes/github'), + darkTheme: require('prism-react-renderer/themes/palenight'), }, liveCodeBlock: { /** @@ -437,23 +357,4 @@ const config = { }), }; -// HACK -// this was originally included above -// it broke local builds on Windows, not sure why yet. Works fine on Mac -// `generate_sdk_docs` runs fine, no difference in outputs between environments, so it's not easy to debug - low pri -const isRunningLocally = process.env.NODE_ENV === 'development'; -const isRunningOnWindows = process.platform === 'win32'; -if (isRunningLocally && isRunningOnWindows) { - config.plugins = config.plugins.filter((plugin) => { - if (Array.isArray(plugin) && plugin[0] === '@docusaurus/plugin-content-docs') { - return false; // remove the offending plugin config - } - return true; // keep everything else - }); -} else { - // another hack for another strange windows-specific issue, reproduceable through clean clone of repo - config.themeConfig.prism.theme = require('prism-react-renderer/themes/github'); - config.themeConfig.prism.darkTheme = require('prism-react-renderer/themes/palenight'); -} - module.exports = config; diff --git a/scripts/build-glossary.ts b/scripts/build-glossary.ts index f2dbb1b759..1ad66fd987 100644 --- a/scripts/build-glossary.ts +++ b/scripts/build-glossary.ts @@ -22,7 +22,7 @@ function escapeForJSON(str: string): string { .replace(/"/g, '\\"') .replace(/\n/g, '\\n') .replace(/\r/g, '\\r') - .replace(/\t/g, '\\t') + .replace(/\t/g, '\\t'); } /** @@ -127,7 +127,9 @@ async function main(): Promise { // Generate import statements for each term (unused in current implementation) // This could be used if implementing a React component approach to term rendering let imports = terms - .map((item) => `import ${renderKey(item.data.key)} from './docs/glossary/${item.data.key}.mdx';`) + .map( + (item) => `import ${renderKey(item.data.key)} from './docs/glossary/${item.data.key}.mdx';`, + ) .join('\n'); // Generate component references for each term (unused in current implementation) @@ -135,11 +137,24 @@ async function main(): Promise { // Generate and write the consolidated glossary partial MDX file // This creates a single file with all terms formatted as Markdown headings + const currentDate = new Date().toISOString().split('T')[0]; // Format: YYYY-MM-DD + const frontmatter = `--- +partial_type: glossary +title: "Arbitrum Glossary Definitions" +description: "Comprehensive glossary of Arbitrum terminology and definitions" +author: anegg0 +last_reviewed: ${currentDate} +--- + +`; + await fs.writeFile( './docs/partials/_glossary-partial.mdx', - terms - .map((item) => `### ${item.data.title} {#${item.data.key}}\n\n${item.content.trim()}`) - .join('\n\n') + '\n', + frontmatter + + terms + .map((item) => `### ${item.data.title} {#${item.data.key}}\n\n${item.content.trim()}`) + .join('\n\n') + + '\n', ); // Generate and write the JSON glossary file for client-side usage diff --git a/scripts/sync-stylus-content.js b/scripts/sync-stylus-content.js index 3075e2c84d..504cfd29a6 100644 --- a/scripts/sync-stylus-content.js +++ b/scripts/sync-stylus-content.js @@ -1,278 +1,671 @@ #!/usr/bin/env node /** - * Simple script to sync content from stylus-by-example submodule to docs directory - * Replaces the complex 897-line stylusByExampleDocsHandler.ts + * @fileoverview Sync content from stylus-by-example submodule to docs directory + * @description Replaces the complex 897-line stylusByExampleDocsHandler.ts with a maintainable solution + * @author sync-stylus-content.js + * @version 2.0.0 (refactored for maintainability) */ const fs = require('fs-extra'); const path = require('path'); const glob = require('glob'); -// Configuration -const SOURCE_DIR = path.join(__dirname, '../submodules/stylus-by-example/src/app'); -const TARGET_DIR = path.join(__dirname, '../docs/stylus-by-example'); -const DONT_EDIT_MARKER = 'DONT-EDIT-THIS-FOLDER'; +// ============================================================================ +// CONFIGURATION - All constants and settings in one place +// ============================================================================ /** - * Convert Next.js style metadata export to Docusaurus frontmatter + * @typedef {Object} SyncConfig + * @property {Object} paths - File and directory paths + * @property {Object} content - Content processing settings + * @property {Object} allowLists - File filtering configurations + * @property {Object} output - Output generation settings */ -function convertMetadataToFrontmatter(content) { - // Pattern to match: export const metadata = { ... } - const metadataPattern = /export\s+const\s+metadata\s*=\s*({[\s\S]*?});/; - const match = content.match(metadataPattern); - - if (!match) { - return content; + +/** @type {SyncConfig} */ +const CONFIG = { + paths: { + sourceDir: path.join(__dirname, '../submodules/stylus-by-example/src/app'), + targetDir: path.join(__dirname, '../docs/stylus-by-example'), + dontEditMarker: 'DONT-EDIT-THIS-FOLDER', + sourcePattern: '**/page.mdx', + sidebarFileName: 'sidebar.js', + }, + + content: { + fileExtensions: { + source: '.mdx', + target: '.mdx', + sidebar: '.js', + }, + markers: { + contentBegin: '{/* Begin Content */}', + frontmatterDelimiter: '---', + }, + patterns: { + metadata: /export\s+const\s+metadata\s*=\s*({[\s\S]*?});/, + relativeLink: /\[([^\]]+)\]\(\.\/([\w_]+)\)/g, + }, + }, + + allowLists: { + basicExamples: [ + 'hello_world', + 'primitive_data_types', + 'variables', + 'constants', + 'function', + 'errors', + 'events', + 'inheritance', + 'vm_affordances', + 'sending_ether', + 'function_selector', + 'abi_encode', + 'abi_decode', + 'hashing', + 'bytes_in_bytes_out', + ], + applications: ['erc20', 'erc721', 'vending_machine', 'multi_call'], + }, + + output: { + sections: ['basic_examples', 'applications'], + indexFileName: 'index.mdx', + sidebarTemplate: { + fileHeader: '// @ts-check', + docString: 'Autogenerated sidebar configuration for Stylus by Example', + typeAnnotation: "/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */", + }, + }, +}; + +// ============================================================================ +// UTILITY FUNCTIONS - Reusable helper functions +// ============================================================================ + +/** + * Creates a standardized error with context information + * @param {string} message - Error message + * @param {string} context - Additional context (file path, operation, etc.) + * @param {Error} [originalError] - Original error object if available + * @returns {Error} Enhanced error object + */ +function createError(message, context, originalError = null) { + const errorMessage = `${message} (Context: ${context})`; + const error = new Error(errorMessage); + error.context = context; + error.originalError = originalError; + return error; +} + +/** + * Logs messages with consistent formatting and timestamps + * @param {string} level - Log level (info, warn, error, success) + * @param {string} message - Message to log + * @param {Object} [details] - Additional details to log + */ +function log(level, message, details = null) { + const timestamp = new Date().toISOString(); + const emoji = + { + info: '🔄', + warn: '⚠️', + error: '❌', + success: '✅', + clean: '🧹', + file: '📁', + write: '📝', + }[level] || 'ℹ️'; + + console.log(`${emoji} ${message}`); + + if (details) { + console.log(` Details: ${JSON.stringify(details, null, 2)}`); } - +} + +/** + * Converts snake_case string to Title Case + * @param {string} snakeCaseStr - String in snake_case format + * @returns {string} String in Title Case format + */ +function snakeCaseToTitleCase(snakeCaseStr) { + return snakeCaseStr + .split('_') + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); +} + +/** + * Safely evaluates a JavaScript object literal string + * @param {string} objectStr - String representation of JavaScript object + * @returns {Object} Parsed object + * @throws {Error} If evaluation fails + */ +function safeEvalObjectLiteral(objectStr) { try { - // Extract and parse the metadata object - const metadataStr = match[1]; // Use Function constructor to safely evaluate the object literal - const metadata = new Function(`return ${metadataStr}`)(); - - // Convert to YAML frontmatter - const frontmatter = Object.entries(metadata) - .map(([key, value]) => { - // Handle different value types - if (typeof value === 'string') { - // Escape single quotes in strings - return `${key}: '${value.replace(/'/g, "''")}'`; - } - return `${key}: ${value}`; - }) - .join('\n'); - - // Replace the export with frontmatter - return content.replace(metadataPattern, `---\n${frontmatter}\n---`); + return new Function(`return ${objectStr}`)(); } catch (error) { - console.warn(`Warning: Could not convert metadata: ${error.message}`); - return content; + throw createError('Failed to evaluate object literal', objectStr, error); } } /** - * Transform relative links to work with Docusaurus structure + * Escapes single quotes in strings for YAML frontmatter + * @param {string} str - String to escape + * @returns {string} Escaped string */ -function fixRelativeLinks(content) { - // Fix relative links that point to sibling directories - // When file name matches directory name, Docusaurus simplifies the URL - // Pattern: [text](./directory_name) should become [text](/stylus-by-example/basic_examples/directory_name) - content = content.replace( - /\[([^\]]+)\]\(\.\/([\w_]+)\)/g, - (match, linkText, dirName) => { - // Convert to absolute Docusaurus path (simplified URL when filename matches directory) - return `[${linkText}](/stylus-by-example/basic_examples/${dirName})`; +function escapeYamlString(str) { + return str.replace(/'/g, "''"); +} + +/** + * Generates a timestamp string for file headers + * @returns {string} ISO timestamp string + */ +function generateTimestamp() { + return new Date().toISOString(); +} + +// ============================================================================ +// PATH AND FILE UTILITIES - File system operations and path manipulation +// ============================================================================ + +/** + * Extracts section name from a file path relative to source directory + * @param {string} filePath - Full file path + * @returns {string|null} Section name ('basic_examples' or 'applications') or null + */ +function extractSectionFromPath(filePath) { + const relativePath = path.relative(CONFIG.paths.sourceDir, filePath); + const segments = relativePath.split(path.sep); + + return segments.length >= 2 ? segments[0] : null; +} + +/** + * Extracts the item name (directory name) from a file path + * @param {string} filePath - Full file path to page.mdx + * @returns {string} Directory name containing the file + */ +function extractItemNameFromPath(filePath) { + return path.basename(path.dirname(filePath)); +} + +/** + * Gets the appropriate allowlist for a section + * @param {string} sectionName - Section name ('basic_examples' or 'applications') + * @returns {string[]} Array of allowed file names + */ +function getAllowListForSection(sectionName) { + const allowListMap = { + basic_examples: CONFIG.allowLists.basicExamples, + applications: CONFIG.allowLists.applications, + }; + + return allowListMap[sectionName] || []; +} + +/** + * Determines if a file should be processed based on allowlists + * @param {string} filePath - Full path to the file + * @returns {boolean} True if file should be processed + */ +function isFileAllowed(filePath) { + const sectionName = extractSectionFromPath(filePath); + if (!sectionName) return false; + + const itemName = extractItemNameFromPath(filePath); + const allowList = getAllowListForSection(sectionName); + + return allowList.includes(itemName); +} + +/** + * Generates target file path from source file path + * @param {string} sourceFile - Source file path + * @returns {Object} Object with targetPath and metadata + */ +function generateTargetPath(sourceFile) { + const relativePath = path.relative(CONFIG.paths.sourceDir, sourceFile); + const targetDir = path.dirname(relativePath); + + let targetFileName; + let finalTargetDir; + + if (targetDir === '.') { + // Root level file + targetFileName = CONFIG.output.indexFileName; + finalTargetDir = CONFIG.paths.targetDir; + } else { + // Section subdirectory file + const pathParts = targetDir.split(path.sep); + const sectionName = pathParts[0]; + const itemName = pathParts[1]; + + targetFileName = `${itemName}${CONFIG.content.fileExtensions.target}`; + finalTargetDir = path.join(CONFIG.paths.targetDir, sectionName); + } + + return { + targetPath: path.join(finalTargetDir, targetFileName), + targetDir: finalTargetDir, + fileName: targetFileName, + }; +} + +// ============================================================================ +// CONTENT TRANSFORMATION - Text processing and content manipulation +// ============================================================================ + +/** + * Converts a metadata object to YAML frontmatter format + * @param {Object} metadata - Metadata object to convert + * @returns {string} YAML frontmatter string + */ +function convertMetadataToYaml(metadata) { + const yamlLines = Object.entries(metadata).map(([key, value]) => { + if (typeof value === 'string') { + return `${key}: '${escapeYamlString(value)}'`; } - ); - - return content; + return `${key}: ${value}`; + }); + + return yamlLines.join('\n'); } /** - * Process and copy a single MDX file + * Transforms Next.js metadata export to Docusaurus frontmatter + * @param {string} content - File content with metadata export + * @returns {string} Content with frontmatter instead of metadata export */ -async function processFile(sourcePath, targetPath) { +function convertMetadataToFrontmatter(content) { + const match = content.match(CONFIG.content.patterns.metadata); + if (!match) return content; + try { - // Read source file - let content = await fs.readFile(sourcePath, 'utf8'); - - // Transform metadata to frontmatter - content = convertMetadataToFrontmatter(content); - - // Fix relative links - content = fixRelativeLinks(content); - - // Add content begin marker if not present - if (!content.includes('{/* Begin Content */}')) { - // Find the position after frontmatter - const frontmatterEnd = content.indexOf('---', 3); - if (frontmatterEnd !== -1) { - const insertPos = content.indexOf('\n', frontmatterEnd + 3) + 1; - content = content.slice(0, insertPos) + '\n{/* Begin Content */}\n' + content.slice(insertPos); - } - } - - // Ensure target directory exists - await fs.ensureDir(path.dirname(targetPath)); - - // Write transformed content - await fs.writeFile(targetPath, content); - - return true; + const metadataStr = match[1]; + const metadata = safeEvalObjectLiteral(metadataStr); + const frontmatter = convertMetadataToYaml(metadata); + + const delimiter = CONFIG.content.markers.frontmatterDelimiter; + const replacement = `${delimiter}\n${frontmatter}\n${delimiter}`; + + return content.replace(CONFIG.content.patterns.metadata, replacement); } catch (error) { - console.error(`Error processing ${sourcePath}: ${error.message}`); - return false; + log('warn', `Could not convert metadata: ${error.message}`); + return content; } } /** - * Generate sidebar configuration for a directory + * Fixes relative links to work with Docusaurus URL structure + * @param {string} content - Content with relative links + * @returns {string} Content with fixed absolute links */ -function generateSidebar(sectionName, files) { - const items = files - .map(file => { - // Get the relative path from the section directory - const relativePath = path.relative(path.join(TARGET_DIR, sectionName), file); - const fileName = path.basename(file, '.mdx'); - const dirName = path.dirname(relativePath); - - // Skip if filename is same as directory (already processed) - if (fileName === sectionName) { - return null; - } - - // Convert filename to label (snake_case to Title Case) - const label = fileName - .split('_') - .map(word => word.charAt(0).toUpperCase() + word.slice(1)) - .join(' '); - - // Generate doc ID based on actual file structure - // For files in subdirectories: stylus-by-example/applications/erc20/erc20 - const docId = dirName === '.' - ? `stylus-by-example/${sectionName}/${fileName}` - : `stylus-by-example/${sectionName}/${dirName}/${fileName}`; - - return { - type: 'doc', - label: label, - id: docId - }; - }) - .filter(Boolean); - - return `// @ts-check +function fixRelativeLinks(content) { + return content.replace(CONFIG.content.patterns.relativeLink, (match, linkText, dirName) => { + // Convert to absolute Docusaurus path + return `[${linkText}](/stylus-by-example/basic_examples/${dirName})`; + }); +} + +/** + * Adds content begin marker if not already present + * @param {string} content - File content + * @returns {string} Content with begin marker + */ +function addContentBeginMarker(content) { + const marker = CONFIG.content.markers.contentBegin; + if (content.includes(marker)) return content; + + const delimiter = CONFIG.content.markers.frontmatterDelimiter; + const frontmatterEnd = content.indexOf(delimiter, 3); + + if (frontmatterEnd === -1) return content; + + const insertPos = content.indexOf('\n', frontmatterEnd + 3) + 1; + return content.slice(0, insertPos) + `\n${marker}\n` + content.slice(insertPos); +} + +/** + * Adds "not for production" banner import and component two lines before first rust code block + * @param {string} content - File content + * @returns {string} Content with banner added + */ +function addNotForProductionBanner(content) { + const firstCodeBlock = '```rust'; + const bannerCode = ` +import NotForProductionBannerPartial from '../../partials/_not-for-production-banner-partial.mdx'; + + +`; + + // Find the first occurrence of ```rust code block + const index = content.indexOf(firstCodeBlock); + if (index === -1) { + // No rust code block found, return content unchanged + return content; + } + + // Find the position two lines before the first code block + const lines = content.substring(0, index).split('\n'); + const insertLineIndex = lines.length - 2; + + // Insert the banner at the calculated position + lines.splice(insertLineIndex, 0, bannerCode); + + // Reconstruct the content with the banner inserted + const newContent = lines.join('\n') + content.substring(index); + return newContent; +} + /** - * @fileoverview Autogenerated sidebar configuration for Stylus by Example + * Applies all content transformations to a file + * @param {string} content - Original file content + * @returns {string} Transformed content + */ +function transformContent(content) { + let transformedContent = content; + + // Apply transformations in sequence + transformedContent = convertMetadataToFrontmatter(transformedContent); + transformedContent = fixRelativeLinks(transformedContent); + transformedContent = addContentBeginMarker(transformedContent); + transformedContent = addNotForProductionBanner(transformedContent); + + return transformedContent; +} + +// ============================================================================ +// SIDEBAR GENERATION - Sidebar configuration file generation +// ============================================================================ + +/** + * Creates a sidebar item object for a file + * @param {string} sectionName - Section name (basic_examples, applications) + * @param {string} fileName - File name without extension + * @returns {Object} Sidebar item configuration + */ +function createSidebarItem(sectionName, fileName) { + return { + type: 'doc', + id: `stylus-by-example/${sectionName}/${fileName}`, + label: snakeCaseToTitleCase(fileName), + }; +} + +/** + * Generates the complete sidebar JavaScript file content + * @param {string} sectionName - Section name for the sidebar + * @param {Object[]} sidebarItems - Array of sidebar item objects + * @returns {string} Complete sidebar file content + */ +function generateSidebarFileContent(sectionName, sidebarItems) { + const template = CONFIG.output.sidebarTemplate; + const timestamp = generateTimestamp(); + + return `${template.fileHeader} +/** + * @fileoverview ${template.docString} ${sectionName} * @description This file is automatically generated by sync-stylus-content.js - * @generated ${new Date().toISOString()} + * @generated ${timestamp} * @see https://docusaurus.io/docs/sidebar * @see https://docusaurus.io/docs/sidebar/items */ -/** @type {import('@docusaurus/plugin-content-docs').SidebarItem[]} */ -module.exports = ${JSON.stringify(items, null, 2)}; +${template.typeAnnotation} +const sidebar = ${JSON.stringify({ items: sidebarItems }, null, 2)}; + +module.exports = sidebar.items; `; } /** - * Main sync function + * Generates sidebar configuration for a section directory + * @param {string} sectionName - Section name (basic_examples, applications) + * @param {string[]} files - Array of file paths + * @returns {string} Complete sidebar JavaScript file content */ -async function syncContent() { - console.log('🔄 Syncing Stylus by Example content...'); - +function generateSidebar(sectionName, files) { + const allowList = getAllowListForSection(sectionName); + const itemsByFileName = {}; + + // Collect all items by filename, skipping index files + files.forEach((file) => { + const fileName = path.basename(file, CONFIG.content.fileExtensions.source); + + // Skip index files (filename matches section name) + if (fileName === sectionName) return; + + const item = createSidebarItem(sectionName, fileName); + itemsByFileName[fileName] = item; + }); + + // Order items according to allowlist + const orderedItems = allowList + .map((allowedFileName) => itemsByFileName[allowedFileName]) + .filter(Boolean); // Remove undefined items + + return generateSidebarFileContent(sectionName, orderedItems); +} + +// ============================================================================ +// FILE OPERATIONS - High-level file processing functions +// ============================================================================ + +/** + * Processes and copies a single MDX file with transformations + * @param {string} sourcePath - Source file path + * @param {string} targetPath - Target file path + * @returns {Promise} True if processing succeeded + */ +async function processFile(sourcePath, targetPath) { try { - // Check if source exists - if (!await fs.pathExists(SOURCE_DIR)) { - console.warn(`⚠️ Source directory not found: ${SOURCE_DIR}`); - console.log('Skipping Stylus by Example content sync.'); - console.log('To include this content, initialize the submodule:'); - console.log(' git submodule update --init --recursive'); - - // Create empty target directory with marker - await fs.ensureDir(TARGET_DIR); - await fs.writeFile( - path.join(TARGET_DIR, DONT_EDIT_MARKER), - `This folder would contain auto-generated content from submodules/stylus-by-example -The submodule was not available during build. -Generated: ${new Date().toISOString()} + // Read and transform content + const content = await fs.readFile(sourcePath, 'utf8'); + const transformedContent = transformContent(content); + + // Ensure target directory exists and write file + await fs.ensureDir(path.dirname(targetPath)); + await fs.writeFile(targetPath, transformedContent); + + return true; + } catch (error) { + log('error', `Failed to process file: ${sourcePath}`, { + error: error.message, + targetPath, + }); + return false; + } +} + +/** + * Creates the "don't edit" marker file in target directory + * @param {string} targetDir - Target directory path + * @param {boolean} isSubmoduleAvailable - Whether submodule is available + * @returns {Promise} + */ +async function createDontEditMarker(targetDir, isSubmoduleAvailable = true) { + const timestamp = generateTimestamp(); + const markerPath = path.join(targetDir, CONFIG.paths.dontEditMarker); + + const content = isSubmoduleAvailable + ? `This folder is auto-generated from submodules/stylus-by-example +Do not edit files directly here. Edit them in the submodule instead. +Generated: ${timestamp} ` - ); - - // Create empty sidebar files for the expected directories - const expectedDirs = ['basic_examples', 'applications']; - for (const dir of expectedDirs) { - await fs.ensureDir(path.join(TARGET_DIR, dir)); - const emptySidebar = `// @ts-check + : `This folder would contain auto-generated content from submodules/stylus-by-example +The submodule was not available during build. +Generated: ${timestamp} +`; + + await fs.writeFile(markerPath, content); +} + +/** + * Creates empty sidebar files for sections when submodule is unavailable + * @param {string} targetDir - Target directory path + * @returns {Promise} + */ +async function createEmptySidebars(targetDir) { + const timestamp = generateTimestamp(); + const template = CONFIG.output.sidebarTemplate; + + const emptySidebarContent = `${template.fileHeader} /** * @fileoverview Empty sidebar configuration (submodule not available) - * @generated ${new Date().toISOString()} + * @generated ${timestamp} */ -/** @type {import('@docusaurus/plugin-content-docs').SidebarItem[]} */ -module.exports = []; +${template.typeAnnotation} +const sidebar = { + items: [] +}; + +module.exports = sidebar.items; `; - await fs.writeFile(path.join(TARGET_DIR, dir, 'sidebar.js'), emptySidebar); - } - - console.log('✅ Created empty Stylus by Example structure'); - return; + + for (const sectionName of CONFIG.output.sections) { + const sectionDir = path.join(targetDir, sectionName); + await fs.ensureDir(sectionDir); + + const sidebarPath = path.join(sectionDir, CONFIG.paths.sidebarFileName); + await fs.writeFile(sidebarPath, emptySidebarContent); + } +} + +/** + * Generates sidebar files for all processed sections + * @param {string} targetDir - Target directory path + * @returns {Promise} + */ +async function generateAllSidebars(targetDir) { + for (const sectionName of CONFIG.output.sections) { + const sectionDir = path.join(targetDir, sectionName); + + if (await fs.pathExists(sectionDir)) { + const pattern = path.join(sectionDir, `**/*${CONFIG.content.fileExtensions.source}`); + const sectionFiles = glob.sync(pattern); + + const sidebarContent = generateSidebar(sectionName, sectionFiles); + const sidebarPath = path.join(sectionDir, CONFIG.paths.sidebarFileName); + + await fs.writeFile(sidebarPath, sidebarContent); + log('write', `Generated sidebar for ${sectionName}`, { + itemCount: sectionFiles.length, + }); } - - // Clean target directory - if (await fs.pathExists(TARGET_DIR)) { - console.log('🧹 Cleaning target directory...'); - await fs.remove(TARGET_DIR); + } +} + +// ============================================================================ +// MAIN SYNC LOGIC - Core synchronization workflow +// ============================================================================ + +/** + * Handles the case when the source submodule is not available + * @returns {Promise} + */ +async function handleMissingSubmodule() { + log('warn', `Source directory not found: ${CONFIG.paths.sourceDir}`); + log('info', 'Skipping Stylus by Example content sync.'); + log('info', 'To include this content, initialize the submodule:'); + log('info', ' git submodule update --init --recursive'); + + // Create empty structure + await fs.ensureDir(CONFIG.paths.targetDir); + await createDontEditMarker(CONFIG.paths.targetDir, false); + await createEmptySidebars(CONFIG.paths.targetDir); + + log('success', 'Created empty Stylus by Example structure'); +} + +/** + * Processes all allowed files from source to target directory + * @param {string[]} allowedFiles - Array of file paths to process + * @returns {Promise} Number of successfully processed files + */ +async function processAllFiles(allowedFiles) { + let successCount = 0; + + for (const sourceFile of allowedFiles) { + const { targetPath } = generateTargetPath(sourceFile); + + if (await processFile(sourceFile, targetPath)) { + successCount++; } - - // Create target directory - await fs.ensureDir(TARGET_DIR); - - // Add marker file - await fs.writeFile( - path.join(TARGET_DIR, DONT_EDIT_MARKER), - `This folder is auto-generated from submodules/stylus-by-example -Do not edit files directly here. Edit them in the submodule instead. -Generated: ${new Date().toISOString()} -` - ); - - // Find all page.mdx files - const pattern = path.join(SOURCE_DIR, '**/page.mdx'); - const files = glob.sync(pattern); - - console.log(`📁 Found ${files.length} files to process`); - - // Process each file - let successCount = 0; - for (const sourceFile of files) { - // Calculate target path - const relativePath = path.relative(SOURCE_DIR, sourceFile); - const targetDir = path.dirname(relativePath); - - // Rename page.mdx to directory name or keep as index - let targetFileName; - if (targetDir === '.') { - targetFileName = 'index.mdx'; - } else { - // Use the parent directory name as the filename - const parentDir = path.basename(targetDir); - targetFileName = `${parentDir}.mdx`; - } - - const targetFile = path.join(TARGET_DIR, targetDir, targetFileName); - - // Process and copy file - if (await processFile(sourceFile, targetFile)) { - successCount++; - } + } + + return successCount; +} + +/** + * Main synchronization function + * @returns {Promise} + */ +async function syncContent() { + log('info', 'Syncing Stylus by Example content...'); + + try { + // Check if source exists + if (!(await fs.pathExists(CONFIG.paths.sourceDir))) { + await handleMissingSubmodule(); + return; } - - // Generate sidebars for each directory - const directories = ['basic_examples', 'applications']; - for (const dir of directories) { - const dirPath = path.join(TARGET_DIR, dir); - if (await fs.pathExists(dirPath)) { - const dirFiles = glob.sync(path.join(dirPath, '**/*.mdx')); - const sidebarContent = generateSidebar(dir, dirFiles); - await fs.writeFile(path.join(dirPath, 'sidebar.js'), sidebarContent); - console.log(`📝 Generated sidebar for ${dir} (${dirFiles.length} items)`); - } + + // Clean and recreate target directory + if (await fs.pathExists(CONFIG.paths.targetDir)) { + log('clean', 'Cleaning target directory...'); + await fs.remove(CONFIG.paths.targetDir); } - - console.log(`✅ Successfully synced ${successCount}/${files.length} files`); - + + await fs.ensureDir(CONFIG.paths.targetDir); + await createDontEditMarker(CONFIG.paths.targetDir, true); + + // Find and filter files + const sourcePattern = path.join(CONFIG.paths.sourceDir, CONFIG.paths.sourcePattern); + const allFiles = glob.sync(sourcePattern); + const allowedFiles = allFiles.filter(isFileAllowed); + + log( + 'file', + `Found ${allFiles.length} total files, ${allowedFiles.length} allowed files to process`, + ); + + // Process all files + const successCount = await processAllFiles(allowedFiles); + + // Generate sidebars + await generateAllSidebars(CONFIG.paths.targetDir); + + log('success', `Successfully synced ${successCount}/${allowedFiles.length} files`); } catch (error) { - console.error(`❌ Sync failed: ${error.message}`); + log('error', `Sync failed: ${error.message}`, { + stack: error.stack, + context: error.context || 'Unknown', + }); process.exit(1); } } +// ============================================================================ +// MODULE EXPORTS AND EXECUTION +// ============================================================================ + // Run if called directly if (require.main === module) { syncContent(); } -module.exports = { syncContent }; \ No newline at end of file +module.exports = { + syncContent, + // Export utilities for testing + CONFIG, + isFileAllowed, + transformContent, + generateSidebar, +}; From a0eb585eb0af233a57d38378e5f5815ca519a5c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Wed, 1 Oct 2025 11:04:02 -0700 Subject: [PATCH 32/62] Revert "update to latest" This reverts commit 3c62027557a08438071687a787b444612815aa0a. --- .../_bold-public-preview-banner-partial.mdx | 7 + docusaurus.config.js | 215 +++-- scripts/build-glossary.ts | 25 +- scripts/sync-stylus-content.js | 819 +++++------------- 4 files changed, 382 insertions(+), 684 deletions(-) create mode 100644 docs/how-arbitrum-works/bold/partials/_bold-public-preview-banner-partial.mdx diff --git a/docs/how-arbitrum-works/bold/partials/_bold-public-preview-banner-partial.mdx b/docs/how-arbitrum-works/bold/partials/_bold-public-preview-banner-partial.mdx new file mode 100644 index 0000000000..a4a519e9e1 --- /dev/null +++ b/docs/how-arbitrum-works/bold/partials/_bold-public-preview-banner-partial.mdx @@ -0,0 +1,7 @@ +:::caution ALPHA RELEASE, PUBLIC PREVIEW DOCS + +The BoLD dispute protocol is currently deployed on a public testnet (that posts assertions to Ethereum Sepolia) and is tagged as an `alpha` release. The code has been audited by [Trail of Bits](https://github.com/trailofbits/publications/blob/master/reviews/2024-04-offchainbold-securityreview.pdf) and in a [public audit competition with Code4rena](https://code4rena.com/audits/2024-05-arbitrum-bold), but **should not be used in production scenarios**. Please note that the public testnet is intended for Arbitrum users and researchers to test and experiment with the BoLD dispute protocol for the purposes of education and hardening the protocol via the surfacing of bugs. The public testnet may be paused, and its parameters may be updated at any time in response to scenarios that impact the network and its ability to fulfill its function as a working, reliable test environment. This documentation is currently in [public preview](../public-preview-expectations.mdx). + +To provide feedback, click the _Request an update_ button at the top of this document, [join the Arbitrum Discord](https://discord.gg/arbitrum), or reach out to our team directly by completing [this form](http://bit.ly/3yy6EUK). + +::: diff --git a/docusaurus.config.js b/docusaurus.config.js index 4af0829166..753ea5e85f 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -4,40 +4,11 @@ require('dotenv').config(); const markdownPreprocessor = require('./scripts/markdown-preprocessor'); +const sdkSidebarGenerator = require('./scripts/sdk-sidebar-generator'); const sdkCodebasePath = './submodules/arbitrum-sdk'; import remarkMath from 'remark-math'; import rehypeKatex from 'rehype-katex'; -// Shared Inkeep configuration -const inkeepBaseSettings = { - apiKey: process.env.INKEEP_API_KEY, - primaryBrandColor: '#213147', - organizationDisplayName: 'Arbitrum', - theme: { - syntaxHighlighter: { - lightTheme: require('prism-react-renderer/themes/github'), - darkTheme: require('prism-react-renderer/themes/palenight'), - }, - }, -}; - -const inkeepModalSettings = { - placeholder: 'Search documentation...', - defaultQuery: '', - maxResults: 40, - debounceTimeMs: 300, - shouldOpenLinksInNewTab: true, -}; - -const inkeepExampleQuestions = [ - 'How to estimate gas in Arbitrum?', - 'What is the difference between Arbitrum One and Nova?', - 'How to deploy a smart contract on Arbitrum?', - 'What are Arbitrum Orbit chains?', - 'How does Arbitrum handle L1 to L2 messaging?', - 'What is Arbitrum Stylus?', -]; - /** @type {import('@docusaurus/types').Config} */ const config = { title: 'Arbitrum Docs', @@ -50,22 +21,6 @@ const config = { markdown: { mermaid: true, preprocessor: markdownPreprocessor, - parseFrontMatter: async (params) => { - // Use the default parser - const result = await params.defaultParseFrontMatter(params); - - // Check if this is a partial file (starts with underscore) - const fileName = params.filePath.split('/').pop(); - const isPartialFile = fileName && fileName.startsWith('_'); - - // For partial files, clear frontmatter to prevent Docusaurus warnings - // The documentation-graph tool reads raw files directly, so this doesn't affect analysis - if (isPartialFile) { - result.frontMatter = {}; - } - - return result; - }, }, themes: ['@docusaurus/theme-mermaid', '@docusaurus/theme-live-codeblock'], // GitHub pages deployment config. @@ -87,6 +42,10 @@ const config = { integrity: 'sha384-odtC+0UGzzFL/6PNoE8rX/SPcQDXBJ+uRepguP4QkPCm2LBxH3FA3y+fKSiJ+AmM', crossorigin: 'anonymous', }, + { + href: '/css/inkeep-custom.css', + type: 'text/css', + }, ], presets: [ [ @@ -94,6 +53,7 @@ const config = { /** @type {import('@docusaurus/preset-classic').Options} */ ({ docs: { + // path: './docs', exclude: ['**/api/**'], remarkPlugins: [remarkMath], rehypePlugins: [rehypeKatex], @@ -172,22 +132,85 @@ const config = { '@inkeep/cxkit-docusaurus', { SearchBar: { - baseSettings: inkeepBaseSettings, - modalSettings: inkeepModalSettings, + baseSettings: { + apiKey: process.env.INKEEP_API_KEY, + primaryBrandColor: '#213147', // Arbitrum's primary brand color + organizationDisplayName: 'Arbitrum', + theme: { + syntaxHighlighter: { + lightTheme: require('prism-react-renderer/themes/github'), + darkTheme: require('prism-react-renderer/themes/palenight'), + }, + components: { + SearchBarTrigger: { + // Configure responsive sizing behavior + defaultSize: 'medium', + minWidth: '180px', + maxWidth: '320px', + }, + }, + }, + }, + modalSettings: { + placeholder: 'Search documentation...', + defaultQuery: '', + maxResults: 40, + debounceTimeMs: 300, + shouldOpenLinksInNewTab: true, + }, + searchSettings: { + // optional settings + }, aiChatSettings: { - aiAssistantAvatar: '/img/logo.svg', - exampleQuestions: inkeepExampleQuestions, + aiAssistantAvatar: '/img/logo.svg', // Using Arbitrum logo as AI assistant avatar + exampleQuestions: [ + 'How to estimate gas in Arbitrum?', + 'What is the difference between Arbitrum One and Nova?', + 'How to deploy a smart contract on Arbitrum?', + 'What are Arbitrum Orbit chains?', + 'How does Arbitrum handle L1 to L2 messaging?', + 'What is Arbitrum Stylus?', + ], botName: 'Arbitrum Assistant', getStartedMessage: "Hi! I'm here to help you navigate Arbitrum documentation. Ask me anything about building on Arbitrum, deploying contracts, or understanding our technology.", }, }, ChatButton: { - baseSettings: inkeepBaseSettings, - modalSettings: inkeepModalSettings, + baseSettings: { + // see https://docusaurus.io/docs/deployment#using-environment-variables to use docusaurus environment variables + apiKey: process.env.INKEEP_API_KEY, + primaryBrandColor: '#213147', // Arbitrum's primary brand color + organizationDisplayName: 'Arbitrum', + // ...optional settings + theme: { + syntaxHighlighter: { + lightTheme: require('prism-react-renderer/themes/github'), + darkTheme: require('prism-react-renderer/themes/palenight'), + }, + }, + }, + modalSettings: { + placeholder: 'Search documentation...', + defaultQuery: '', + maxResults: 40, + debounceTimeMs: 300, + shouldOpenLinksInNewTab: true, + }, + searchSettings: { + // optional settings + }, aiChatSettings: { - aiAssistantAvatar: '/img/logo.svg', - exampleQuestions: inkeepExampleQuestions, + // optional settings + aiAssistantAvatar: '/img/logo.svg', // optional -- use your own AI assistant avatar + exampleQuestions: [ + 'How to estimate gas in Arbitrum?', + 'What is the difference between Arbitrum One and Nova?', + 'How to deploy a smart contract on Arbitrum?', + 'What are Arbitrum Orbit chains?', + 'How does Arbitrum handle L1 to L2 messaging?', + 'What is Arbitrum Stylus?', + ], }, }, }, @@ -223,13 +246,72 @@ const config = { logo: { alt: 'Arbitrum Logo', src: 'img/logo.svg', - href: '/welcome/arbitrum-gentle-introduction', + href: '/get-started', }, - items: [], + items: [ + { + type: 'docSidebar', + sidebarId: 'getStartedSidebar', + position: 'right', + label: 'Get started', + }, + { + type: 'dropdown', + label: 'Build apps', + position: 'right', + items: [ + { + label: 'Quickstart', + to: '/build-decentralized-apps/quickstart-solidity-remix', + }, + { + label: 'Stylus', + to: '/stylus/gentle-introduction', + }, + ], + }, + { + type: 'docSidebar', + sidebarId: 'runArbitrumChainSidebar', + position: 'right', + label: 'Launch a chain', + }, + // { + // type: 'docSidebar', + // sidebarId: 'stylusSidebar', + // position: 'right', + // label: 'Use Stylus', + // }, + { + type: 'docSidebar', + sidebarId: 'runNodeSidebar', + position: 'right', + label: 'Run a node', + }, + { + type: 'docSidebar', + sidebarId: 'bridgeSidebar', + position: 'right', + label: 'Use the bridge', + }, + { + type: 'docSidebar', + sidebarId: 'howItWorksSidebar', + position: 'right', + label: 'How it works', + }, + { + type: 'docSidebar', + sidebarId: 'additionalResourcesSidebar', + position: 'right', + label: 'Resources', + }, + ], }, footer: { style: 'dark', links: [ + {}, { items: [ { @@ -331,8 +413,6 @@ const config = { }, prism: { additionalLanguages: ['solidity', 'rust', 'bash', 'toml'], - theme: require('prism-react-renderer/themes/github'), - darkTheme: require('prism-react-renderer/themes/palenight'), }, liveCodeBlock: { /** @@ -357,4 +437,23 @@ const config = { }), }; +// HACK +// this was originally included above +// it broke local builds on Windows, not sure why yet. Works fine on Mac +// `generate_sdk_docs` runs fine, no difference in outputs between environments, so it's not easy to debug - low pri +const isRunningLocally = process.env.NODE_ENV === 'development'; +const isRunningOnWindows = process.platform === 'win32'; +if (isRunningLocally && isRunningOnWindows) { + config.plugins = config.plugins.filter((plugin) => { + if (Array.isArray(plugin) && plugin[0] === '@docusaurus/plugin-content-docs') { + return false; // remove the offending plugin config + } + return true; // keep everything else + }); +} else { + // another hack for another strange windows-specific issue, reproduceable through clean clone of repo + config.themeConfig.prism.theme = require('prism-react-renderer/themes/github'); + config.themeConfig.prism.darkTheme = require('prism-react-renderer/themes/palenight'); +} + module.exports = config; diff --git a/scripts/build-glossary.ts b/scripts/build-glossary.ts index 1ad66fd987..f2dbb1b759 100644 --- a/scripts/build-glossary.ts +++ b/scripts/build-glossary.ts @@ -22,7 +22,7 @@ function escapeForJSON(str: string): string { .replace(/"/g, '\\"') .replace(/\n/g, '\\n') .replace(/\r/g, '\\r') - .replace(/\t/g, '\\t'); + .replace(/\t/g, '\\t') } /** @@ -127,9 +127,7 @@ async function main(): Promise { // Generate import statements for each term (unused in current implementation) // This could be used if implementing a React component approach to term rendering let imports = terms - .map( - (item) => `import ${renderKey(item.data.key)} from './docs/glossary/${item.data.key}.mdx';`, - ) + .map((item) => `import ${renderKey(item.data.key)} from './docs/glossary/${item.data.key}.mdx';`) .join('\n'); // Generate component references for each term (unused in current implementation) @@ -137,24 +135,11 @@ async function main(): Promise { // Generate and write the consolidated glossary partial MDX file // This creates a single file with all terms formatted as Markdown headings - const currentDate = new Date().toISOString().split('T')[0]; // Format: YYYY-MM-DD - const frontmatter = `--- -partial_type: glossary -title: "Arbitrum Glossary Definitions" -description: "Comprehensive glossary of Arbitrum terminology and definitions" -author: anegg0 -last_reviewed: ${currentDate} ---- - -`; - await fs.writeFile( './docs/partials/_glossary-partial.mdx', - frontmatter + - terms - .map((item) => `### ${item.data.title} {#${item.data.key}}\n\n${item.content.trim()}`) - .join('\n\n') + - '\n', + terms + .map((item) => `### ${item.data.title} {#${item.data.key}}\n\n${item.content.trim()}`) + .join('\n\n') + '\n', ); // Generate and write the JSON glossary file for client-side usage diff --git a/scripts/sync-stylus-content.js b/scripts/sync-stylus-content.js index 504cfd29a6..3075e2c84d 100644 --- a/scripts/sync-stylus-content.js +++ b/scripts/sync-stylus-content.js @@ -1,671 +1,278 @@ #!/usr/bin/env node /** - * @fileoverview Sync content from stylus-by-example submodule to docs directory - * @description Replaces the complex 897-line stylusByExampleDocsHandler.ts with a maintainable solution - * @author sync-stylus-content.js - * @version 2.0.0 (refactored for maintainability) + * Simple script to sync content from stylus-by-example submodule to docs directory + * Replaces the complex 897-line stylusByExampleDocsHandler.ts */ const fs = require('fs-extra'); const path = require('path'); const glob = require('glob'); -// ============================================================================ -// CONFIGURATION - All constants and settings in one place -// ============================================================================ +// Configuration +const SOURCE_DIR = path.join(__dirname, '../submodules/stylus-by-example/src/app'); +const TARGET_DIR = path.join(__dirname, '../docs/stylus-by-example'); +const DONT_EDIT_MARKER = 'DONT-EDIT-THIS-FOLDER'; /** - * @typedef {Object} SyncConfig - * @property {Object} paths - File and directory paths - * @property {Object} content - Content processing settings - * @property {Object} allowLists - File filtering configurations - * @property {Object} output - Output generation settings + * Convert Next.js style metadata export to Docusaurus frontmatter */ - -/** @type {SyncConfig} */ -const CONFIG = { - paths: { - sourceDir: path.join(__dirname, '../submodules/stylus-by-example/src/app'), - targetDir: path.join(__dirname, '../docs/stylus-by-example'), - dontEditMarker: 'DONT-EDIT-THIS-FOLDER', - sourcePattern: '**/page.mdx', - sidebarFileName: 'sidebar.js', - }, - - content: { - fileExtensions: { - source: '.mdx', - target: '.mdx', - sidebar: '.js', - }, - markers: { - contentBegin: '{/* Begin Content */}', - frontmatterDelimiter: '---', - }, - patterns: { - metadata: /export\s+const\s+metadata\s*=\s*({[\s\S]*?});/, - relativeLink: /\[([^\]]+)\]\(\.\/([\w_]+)\)/g, - }, - }, - - allowLists: { - basicExamples: [ - 'hello_world', - 'primitive_data_types', - 'variables', - 'constants', - 'function', - 'errors', - 'events', - 'inheritance', - 'vm_affordances', - 'sending_ether', - 'function_selector', - 'abi_encode', - 'abi_decode', - 'hashing', - 'bytes_in_bytes_out', - ], - applications: ['erc20', 'erc721', 'vending_machine', 'multi_call'], - }, - - output: { - sections: ['basic_examples', 'applications'], - indexFileName: 'index.mdx', - sidebarTemplate: { - fileHeader: '// @ts-check', - docString: 'Autogenerated sidebar configuration for Stylus by Example', - typeAnnotation: "/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */", - }, - }, -}; - -// ============================================================================ -// UTILITY FUNCTIONS - Reusable helper functions -// ============================================================================ - -/** - * Creates a standardized error with context information - * @param {string} message - Error message - * @param {string} context - Additional context (file path, operation, etc.) - * @param {Error} [originalError] - Original error object if available - * @returns {Error} Enhanced error object - */ -function createError(message, context, originalError = null) { - const errorMessage = `${message} (Context: ${context})`; - const error = new Error(errorMessage); - error.context = context; - error.originalError = originalError; - return error; -} - -/** - * Logs messages with consistent formatting and timestamps - * @param {string} level - Log level (info, warn, error, success) - * @param {string} message - Message to log - * @param {Object} [details] - Additional details to log - */ -function log(level, message, details = null) { - const timestamp = new Date().toISOString(); - const emoji = - { - info: '🔄', - warn: '⚠️', - error: '❌', - success: '✅', - clean: '🧹', - file: '📁', - write: '📝', - }[level] || 'ℹ️'; - - console.log(`${emoji} ${message}`); - - if (details) { - console.log(` Details: ${JSON.stringify(details, null, 2)}`); +function convertMetadataToFrontmatter(content) { + // Pattern to match: export const metadata = { ... } + const metadataPattern = /export\s+const\s+metadata\s*=\s*({[\s\S]*?});/; + const match = content.match(metadataPattern); + + if (!match) { + return content; } -} - -/** - * Converts snake_case string to Title Case - * @param {string} snakeCaseStr - String in snake_case format - * @returns {string} String in Title Case format - */ -function snakeCaseToTitleCase(snakeCaseStr) { - return snakeCaseStr - .split('_') - .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) - .join(' '); -} - -/** - * Safely evaluates a JavaScript object literal string - * @param {string} objectStr - String representation of JavaScript object - * @returns {Object} Parsed object - * @throws {Error} If evaluation fails - */ -function safeEvalObjectLiteral(objectStr) { + try { + // Extract and parse the metadata object + const metadataStr = match[1]; // Use Function constructor to safely evaluate the object literal - return new Function(`return ${objectStr}`)(); + const metadata = new Function(`return ${metadataStr}`)(); + + // Convert to YAML frontmatter + const frontmatter = Object.entries(metadata) + .map(([key, value]) => { + // Handle different value types + if (typeof value === 'string') { + // Escape single quotes in strings + return `${key}: '${value.replace(/'/g, "''")}'`; + } + return `${key}: ${value}`; + }) + .join('\n'); + + // Replace the export with frontmatter + return content.replace(metadataPattern, `---\n${frontmatter}\n---`); } catch (error) { - throw createError('Failed to evaluate object literal', objectStr, error); - } -} - -/** - * Escapes single quotes in strings for YAML frontmatter - * @param {string} str - String to escape - * @returns {string} Escaped string - */ -function escapeYamlString(str) { - return str.replace(/'/g, "''"); -} - -/** - * Generates a timestamp string for file headers - * @returns {string} ISO timestamp string - */ -function generateTimestamp() { - return new Date().toISOString(); -} - -// ============================================================================ -// PATH AND FILE UTILITIES - File system operations and path manipulation -// ============================================================================ - -/** - * Extracts section name from a file path relative to source directory - * @param {string} filePath - Full file path - * @returns {string|null} Section name ('basic_examples' or 'applications') or null - */ -function extractSectionFromPath(filePath) { - const relativePath = path.relative(CONFIG.paths.sourceDir, filePath); - const segments = relativePath.split(path.sep); - - return segments.length >= 2 ? segments[0] : null; -} - -/** - * Extracts the item name (directory name) from a file path - * @param {string} filePath - Full file path to page.mdx - * @returns {string} Directory name containing the file - */ -function extractItemNameFromPath(filePath) { - return path.basename(path.dirname(filePath)); -} - -/** - * Gets the appropriate allowlist for a section - * @param {string} sectionName - Section name ('basic_examples' or 'applications') - * @returns {string[]} Array of allowed file names - */ -function getAllowListForSection(sectionName) { - const allowListMap = { - basic_examples: CONFIG.allowLists.basicExamples, - applications: CONFIG.allowLists.applications, - }; - - return allowListMap[sectionName] || []; -} - -/** - * Determines if a file should be processed based on allowlists - * @param {string} filePath - Full path to the file - * @returns {boolean} True if file should be processed - */ -function isFileAllowed(filePath) { - const sectionName = extractSectionFromPath(filePath); - if (!sectionName) return false; - - const itemName = extractItemNameFromPath(filePath); - const allowList = getAllowListForSection(sectionName); - - return allowList.includes(itemName); -} - -/** - * Generates target file path from source file path - * @param {string} sourceFile - Source file path - * @returns {Object} Object with targetPath and metadata - */ -function generateTargetPath(sourceFile) { - const relativePath = path.relative(CONFIG.paths.sourceDir, sourceFile); - const targetDir = path.dirname(relativePath); - - let targetFileName; - let finalTargetDir; - - if (targetDir === '.') { - // Root level file - targetFileName = CONFIG.output.indexFileName; - finalTargetDir = CONFIG.paths.targetDir; - } else { - // Section subdirectory file - const pathParts = targetDir.split(path.sep); - const sectionName = pathParts[0]; - const itemName = pathParts[1]; - - targetFileName = `${itemName}${CONFIG.content.fileExtensions.target}`; - finalTargetDir = path.join(CONFIG.paths.targetDir, sectionName); + console.warn(`Warning: Could not convert metadata: ${error.message}`); + return content; } - - return { - targetPath: path.join(finalTargetDir, targetFileName), - targetDir: finalTargetDir, - fileName: targetFileName, - }; } -// ============================================================================ -// CONTENT TRANSFORMATION - Text processing and content manipulation -// ============================================================================ - /** - * Converts a metadata object to YAML frontmatter format - * @param {Object} metadata - Metadata object to convert - * @returns {string} YAML frontmatter string + * Transform relative links to work with Docusaurus structure */ -function convertMetadataToYaml(metadata) { - const yamlLines = Object.entries(metadata).map(([key, value]) => { - if (typeof value === 'string') { - return `${key}: '${escapeYamlString(value)}'`; +function fixRelativeLinks(content) { + // Fix relative links that point to sibling directories + // When file name matches directory name, Docusaurus simplifies the URL + // Pattern: [text](./directory_name) should become [text](/stylus-by-example/basic_examples/directory_name) + content = content.replace( + /\[([^\]]+)\]\(\.\/([\w_]+)\)/g, + (match, linkText, dirName) => { + // Convert to absolute Docusaurus path (simplified URL when filename matches directory) + return `[${linkText}](/stylus-by-example/basic_examples/${dirName})`; } - return `${key}: ${value}`; - }); - - return yamlLines.join('\n'); + ); + + return content; } /** - * Transforms Next.js metadata export to Docusaurus frontmatter - * @param {string} content - File content with metadata export - * @returns {string} Content with frontmatter instead of metadata export + * Process and copy a single MDX file */ -function convertMetadataToFrontmatter(content) { - const match = content.match(CONFIG.content.patterns.metadata); - if (!match) return content; - +async function processFile(sourcePath, targetPath) { try { - const metadataStr = match[1]; - const metadata = safeEvalObjectLiteral(metadataStr); - const frontmatter = convertMetadataToYaml(metadata); - - const delimiter = CONFIG.content.markers.frontmatterDelimiter; - const replacement = `${delimiter}\n${frontmatter}\n${delimiter}`; - - return content.replace(CONFIG.content.patterns.metadata, replacement); + // Read source file + let content = await fs.readFile(sourcePath, 'utf8'); + + // Transform metadata to frontmatter + content = convertMetadataToFrontmatter(content); + + // Fix relative links + content = fixRelativeLinks(content); + + // Add content begin marker if not present + if (!content.includes('{/* Begin Content */}')) { + // Find the position after frontmatter + const frontmatterEnd = content.indexOf('---', 3); + if (frontmatterEnd !== -1) { + const insertPos = content.indexOf('\n', frontmatterEnd + 3) + 1; + content = content.slice(0, insertPos) + '\n{/* Begin Content */}\n' + content.slice(insertPos); + } + } + + // Ensure target directory exists + await fs.ensureDir(path.dirname(targetPath)); + + // Write transformed content + await fs.writeFile(targetPath, content); + + return true; } catch (error) { - log('warn', `Could not convert metadata: ${error.message}`); - return content; + console.error(`Error processing ${sourcePath}: ${error.message}`); + return false; } } /** - * Fixes relative links to work with Docusaurus URL structure - * @param {string} content - Content with relative links - * @returns {string} Content with fixed absolute links - */ -function fixRelativeLinks(content) { - return content.replace(CONFIG.content.patterns.relativeLink, (match, linkText, dirName) => { - // Convert to absolute Docusaurus path - return `[${linkText}](/stylus-by-example/basic_examples/${dirName})`; - }); -} - -/** - * Adds content begin marker if not already present - * @param {string} content - File content - * @returns {string} Content with begin marker + * Generate sidebar configuration for a directory */ -function addContentBeginMarker(content) { - const marker = CONFIG.content.markers.contentBegin; - if (content.includes(marker)) return content; - - const delimiter = CONFIG.content.markers.frontmatterDelimiter; - const frontmatterEnd = content.indexOf(delimiter, 3); - - if (frontmatterEnd === -1) return content; - - const insertPos = content.indexOf('\n', frontmatterEnd + 3) + 1; - return content.slice(0, insertPos) + `\n${marker}\n` + content.slice(insertPos); -} - -/** - * Adds "not for production" banner import and component two lines before first rust code block - * @param {string} content - File content - * @returns {string} Content with banner added - */ -function addNotForProductionBanner(content) { - const firstCodeBlock = '```rust'; - const bannerCode = ` -import NotForProductionBannerPartial from '../../partials/_not-for-production-banner-partial.mdx'; - - -`; - - // Find the first occurrence of ```rust code block - const index = content.indexOf(firstCodeBlock); - if (index === -1) { - // No rust code block found, return content unchanged - return content; - } - - // Find the position two lines before the first code block - const lines = content.substring(0, index).split('\n'); - const insertLineIndex = lines.length - 2; - - // Insert the banner at the calculated position - lines.splice(insertLineIndex, 0, bannerCode); - - // Reconstruct the content with the banner inserted - const newContent = lines.join('\n') + content.substring(index); - return newContent; -} - -/** - * Applies all content transformations to a file - * @param {string} content - Original file content - * @returns {string} Transformed content - */ -function transformContent(content) { - let transformedContent = content; - - // Apply transformations in sequence - transformedContent = convertMetadataToFrontmatter(transformedContent); - transformedContent = fixRelativeLinks(transformedContent); - transformedContent = addContentBeginMarker(transformedContent); - transformedContent = addNotForProductionBanner(transformedContent); - - return transformedContent; -} - -// ============================================================================ -// SIDEBAR GENERATION - Sidebar configuration file generation -// ============================================================================ - -/** - * Creates a sidebar item object for a file - * @param {string} sectionName - Section name (basic_examples, applications) - * @param {string} fileName - File name without extension - * @returns {Object} Sidebar item configuration - */ -function createSidebarItem(sectionName, fileName) { - return { - type: 'doc', - id: `stylus-by-example/${sectionName}/${fileName}`, - label: snakeCaseToTitleCase(fileName), - }; -} - -/** - * Generates the complete sidebar JavaScript file content - * @param {string} sectionName - Section name for the sidebar - * @param {Object[]} sidebarItems - Array of sidebar item objects - * @returns {string} Complete sidebar file content - */ -function generateSidebarFileContent(sectionName, sidebarItems) { - const template = CONFIG.output.sidebarTemplate; - const timestamp = generateTimestamp(); - - return `${template.fileHeader} +function generateSidebar(sectionName, files) { + const items = files + .map(file => { + // Get the relative path from the section directory + const relativePath = path.relative(path.join(TARGET_DIR, sectionName), file); + const fileName = path.basename(file, '.mdx'); + const dirName = path.dirname(relativePath); + + // Skip if filename is same as directory (already processed) + if (fileName === sectionName) { + return null; + } + + // Convert filename to label (snake_case to Title Case) + const label = fileName + .split('_') + .map(word => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); + + // Generate doc ID based on actual file structure + // For files in subdirectories: stylus-by-example/applications/erc20/erc20 + const docId = dirName === '.' + ? `stylus-by-example/${sectionName}/${fileName}` + : `stylus-by-example/${sectionName}/${dirName}/${fileName}`; + + return { + type: 'doc', + label: label, + id: docId + }; + }) + .filter(Boolean); + + return `// @ts-check /** - * @fileoverview ${template.docString} ${sectionName} + * @fileoverview Autogenerated sidebar configuration for Stylus by Example * @description This file is automatically generated by sync-stylus-content.js - * @generated ${timestamp} + * @generated ${new Date().toISOString()} * @see https://docusaurus.io/docs/sidebar * @see https://docusaurus.io/docs/sidebar/items */ -${template.typeAnnotation} -const sidebar = ${JSON.stringify({ items: sidebarItems }, null, 2)}; - -module.exports = sidebar.items; +/** @type {import('@docusaurus/plugin-content-docs').SidebarItem[]} */ +module.exports = ${JSON.stringify(items, null, 2)}; `; } /** - * Generates sidebar configuration for a section directory - * @param {string} sectionName - Section name (basic_examples, applications) - * @param {string[]} files - Array of file paths - * @returns {string} Complete sidebar JavaScript file content - */ -function generateSidebar(sectionName, files) { - const allowList = getAllowListForSection(sectionName); - const itemsByFileName = {}; - - // Collect all items by filename, skipping index files - files.forEach((file) => { - const fileName = path.basename(file, CONFIG.content.fileExtensions.source); - - // Skip index files (filename matches section name) - if (fileName === sectionName) return; - - const item = createSidebarItem(sectionName, fileName); - itemsByFileName[fileName] = item; - }); - - // Order items according to allowlist - const orderedItems = allowList - .map((allowedFileName) => itemsByFileName[allowedFileName]) - .filter(Boolean); // Remove undefined items - - return generateSidebarFileContent(sectionName, orderedItems); -} - -// ============================================================================ -// FILE OPERATIONS - High-level file processing functions -// ============================================================================ - -/** - * Processes and copies a single MDX file with transformations - * @param {string} sourcePath - Source file path - * @param {string} targetPath - Target file path - * @returns {Promise} True if processing succeeded + * Main sync function */ -async function processFile(sourcePath, targetPath) { +async function syncContent() { + console.log('🔄 Syncing Stylus by Example content...'); + try { - // Read and transform content - const content = await fs.readFile(sourcePath, 'utf8'); - const transformedContent = transformContent(content); - - // Ensure target directory exists and write file - await fs.ensureDir(path.dirname(targetPath)); - await fs.writeFile(targetPath, transformedContent); - - return true; - } catch (error) { - log('error', `Failed to process file: ${sourcePath}`, { - error: error.message, - targetPath, - }); - return false; - } -} - -/** - * Creates the "don't edit" marker file in target directory - * @param {string} targetDir - Target directory path - * @param {boolean} isSubmoduleAvailable - Whether submodule is available - * @returns {Promise} - */ -async function createDontEditMarker(targetDir, isSubmoduleAvailable = true) { - const timestamp = generateTimestamp(); - const markerPath = path.join(targetDir, CONFIG.paths.dontEditMarker); - - const content = isSubmoduleAvailable - ? `This folder is auto-generated from submodules/stylus-by-example -Do not edit files directly here. Edit them in the submodule instead. -Generated: ${timestamp} -` - : `This folder would contain auto-generated content from submodules/stylus-by-example + // Check if source exists + if (!await fs.pathExists(SOURCE_DIR)) { + console.warn(`⚠️ Source directory not found: ${SOURCE_DIR}`); + console.log('Skipping Stylus by Example content sync.'); + console.log('To include this content, initialize the submodule:'); + console.log(' git submodule update --init --recursive'); + + // Create empty target directory with marker + await fs.ensureDir(TARGET_DIR); + await fs.writeFile( + path.join(TARGET_DIR, DONT_EDIT_MARKER), + `This folder would contain auto-generated content from submodules/stylus-by-example The submodule was not available during build. -Generated: ${timestamp} -`; - - await fs.writeFile(markerPath, content); -} - -/** - * Creates empty sidebar files for sections when submodule is unavailable - * @param {string} targetDir - Target directory path - * @returns {Promise} - */ -async function createEmptySidebars(targetDir) { - const timestamp = generateTimestamp(); - const template = CONFIG.output.sidebarTemplate; - - const emptySidebarContent = `${template.fileHeader} +Generated: ${new Date().toISOString()} +` + ); + + // Create empty sidebar files for the expected directories + const expectedDirs = ['basic_examples', 'applications']; + for (const dir of expectedDirs) { + await fs.ensureDir(path.join(TARGET_DIR, dir)); + const emptySidebar = `// @ts-check /** * @fileoverview Empty sidebar configuration (submodule not available) - * @generated ${timestamp} + * @generated ${new Date().toISOString()} */ -${template.typeAnnotation} -const sidebar = { - items: [] -}; - -module.exports = sidebar.items; +/** @type {import('@docusaurus/plugin-content-docs').SidebarItem[]} */ +module.exports = []; `; - - for (const sectionName of CONFIG.output.sections) { - const sectionDir = path.join(targetDir, sectionName); - await fs.ensureDir(sectionDir); - - const sidebarPath = path.join(sectionDir, CONFIG.paths.sidebarFileName); - await fs.writeFile(sidebarPath, emptySidebarContent); - } -} - -/** - * Generates sidebar files for all processed sections - * @param {string} targetDir - Target directory path - * @returns {Promise} - */ -async function generateAllSidebars(targetDir) { - for (const sectionName of CONFIG.output.sections) { - const sectionDir = path.join(targetDir, sectionName); - - if (await fs.pathExists(sectionDir)) { - const pattern = path.join(sectionDir, `**/*${CONFIG.content.fileExtensions.source}`); - const sectionFiles = glob.sync(pattern); - - const sidebarContent = generateSidebar(sectionName, sectionFiles); - const sidebarPath = path.join(sectionDir, CONFIG.paths.sidebarFileName); - - await fs.writeFile(sidebarPath, sidebarContent); - log('write', `Generated sidebar for ${sectionName}`, { - itemCount: sectionFiles.length, - }); - } - } -} - -// ============================================================================ -// MAIN SYNC LOGIC - Core synchronization workflow -// ============================================================================ - -/** - * Handles the case when the source submodule is not available - * @returns {Promise} - */ -async function handleMissingSubmodule() { - log('warn', `Source directory not found: ${CONFIG.paths.sourceDir}`); - log('info', 'Skipping Stylus by Example content sync.'); - log('info', 'To include this content, initialize the submodule:'); - log('info', ' git submodule update --init --recursive'); - - // Create empty structure - await fs.ensureDir(CONFIG.paths.targetDir); - await createDontEditMarker(CONFIG.paths.targetDir, false); - await createEmptySidebars(CONFIG.paths.targetDir); - - log('success', 'Created empty Stylus by Example structure'); -} - -/** - * Processes all allowed files from source to target directory - * @param {string[]} allowedFiles - Array of file paths to process - * @returns {Promise} Number of successfully processed files - */ -async function processAllFiles(allowedFiles) { - let successCount = 0; - - for (const sourceFile of allowedFiles) { - const { targetPath } = generateTargetPath(sourceFile); - - if (await processFile(sourceFile, targetPath)) { - successCount++; - } - } - - return successCount; -} - -/** - * Main synchronization function - * @returns {Promise} - */ -async function syncContent() { - log('info', 'Syncing Stylus by Example content...'); - - try { - // Check if source exists - if (!(await fs.pathExists(CONFIG.paths.sourceDir))) { - await handleMissingSubmodule(); + await fs.writeFile(path.join(TARGET_DIR, dir, 'sidebar.js'), emptySidebar); + } + + console.log('✅ Created empty Stylus by Example structure'); return; } - - // Clean and recreate target directory - if (await fs.pathExists(CONFIG.paths.targetDir)) { - log('clean', 'Cleaning target directory...'); - await fs.remove(CONFIG.paths.targetDir); + + // Clean target directory + if (await fs.pathExists(TARGET_DIR)) { + console.log('🧹 Cleaning target directory...'); + await fs.remove(TARGET_DIR); } - - await fs.ensureDir(CONFIG.paths.targetDir); - await createDontEditMarker(CONFIG.paths.targetDir, true); - - // Find and filter files - const sourcePattern = path.join(CONFIG.paths.sourceDir, CONFIG.paths.sourcePattern); - const allFiles = glob.sync(sourcePattern); - const allowedFiles = allFiles.filter(isFileAllowed); - - log( - 'file', - `Found ${allFiles.length} total files, ${allowedFiles.length} allowed files to process`, + + // Create target directory + await fs.ensureDir(TARGET_DIR); + + // Add marker file + await fs.writeFile( + path.join(TARGET_DIR, DONT_EDIT_MARKER), + `This folder is auto-generated from submodules/stylus-by-example +Do not edit files directly here. Edit them in the submodule instead. +Generated: ${new Date().toISOString()} +` ); - - // Process all files - const successCount = await processAllFiles(allowedFiles); - - // Generate sidebars - await generateAllSidebars(CONFIG.paths.targetDir); - - log('success', `Successfully synced ${successCount}/${allowedFiles.length} files`); + + // Find all page.mdx files + const pattern = path.join(SOURCE_DIR, '**/page.mdx'); + const files = glob.sync(pattern); + + console.log(`📁 Found ${files.length} files to process`); + + // Process each file + let successCount = 0; + for (const sourceFile of files) { + // Calculate target path + const relativePath = path.relative(SOURCE_DIR, sourceFile); + const targetDir = path.dirname(relativePath); + + // Rename page.mdx to directory name or keep as index + let targetFileName; + if (targetDir === '.') { + targetFileName = 'index.mdx'; + } else { + // Use the parent directory name as the filename + const parentDir = path.basename(targetDir); + targetFileName = `${parentDir}.mdx`; + } + + const targetFile = path.join(TARGET_DIR, targetDir, targetFileName); + + // Process and copy file + if (await processFile(sourceFile, targetFile)) { + successCount++; + } + } + + // Generate sidebars for each directory + const directories = ['basic_examples', 'applications']; + for (const dir of directories) { + const dirPath = path.join(TARGET_DIR, dir); + if (await fs.pathExists(dirPath)) { + const dirFiles = glob.sync(path.join(dirPath, '**/*.mdx')); + const sidebarContent = generateSidebar(dir, dirFiles); + await fs.writeFile(path.join(dirPath, 'sidebar.js'), sidebarContent); + console.log(`📝 Generated sidebar for ${dir} (${dirFiles.length} items)`); + } + } + + console.log(`✅ Successfully synced ${successCount}/${files.length} files`); + } catch (error) { - log('error', `Sync failed: ${error.message}`, { - stack: error.stack, - context: error.context || 'Unknown', - }); + console.error(`❌ Sync failed: ${error.message}`); process.exit(1); } } -// ============================================================================ -// MODULE EXPORTS AND EXECUTION -// ============================================================================ - // Run if called directly if (require.main === module) { syncContent(); } -module.exports = { - syncContent, - // Export utilities for testing - CONFIG, - isFileAllowed, - transformContent, - generateSidebar, -}; +module.exports = { syncContent }; \ No newline at end of file From 9ba0dd0bea3a7e35c393a8d580fa51fad0ffdcda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Wed, 1 Oct 2025 11:11:39 -0700 Subject: [PATCH 33/62] Reapply "update to latest" This reverts commit a0eb585eb0af233a57d38378e5f5815ca519a5c7. --- .../_bold-public-preview-banner-partial.mdx | 7 - docusaurus.config.js | 215 ++--- scripts/build-glossary.ts | 25 +- scripts/sync-stylus-content.js | 819 +++++++++++++----- 4 files changed, 684 insertions(+), 382 deletions(-) delete mode 100644 docs/how-arbitrum-works/bold/partials/_bold-public-preview-banner-partial.mdx diff --git a/docs/how-arbitrum-works/bold/partials/_bold-public-preview-banner-partial.mdx b/docs/how-arbitrum-works/bold/partials/_bold-public-preview-banner-partial.mdx deleted file mode 100644 index a4a519e9e1..0000000000 --- a/docs/how-arbitrum-works/bold/partials/_bold-public-preview-banner-partial.mdx +++ /dev/null @@ -1,7 +0,0 @@ -:::caution ALPHA RELEASE, PUBLIC PREVIEW DOCS - -The BoLD dispute protocol is currently deployed on a public testnet (that posts assertions to Ethereum Sepolia) and is tagged as an `alpha` release. The code has been audited by [Trail of Bits](https://github.com/trailofbits/publications/blob/master/reviews/2024-04-offchainbold-securityreview.pdf) and in a [public audit competition with Code4rena](https://code4rena.com/audits/2024-05-arbitrum-bold), but **should not be used in production scenarios**. Please note that the public testnet is intended for Arbitrum users and researchers to test and experiment with the BoLD dispute protocol for the purposes of education and hardening the protocol via the surfacing of bugs. The public testnet may be paused, and its parameters may be updated at any time in response to scenarios that impact the network and its ability to fulfill its function as a working, reliable test environment. This documentation is currently in [public preview](../public-preview-expectations.mdx). - -To provide feedback, click the _Request an update_ button at the top of this document, [join the Arbitrum Discord](https://discord.gg/arbitrum), or reach out to our team directly by completing [this form](http://bit.ly/3yy6EUK). - -::: diff --git a/docusaurus.config.js b/docusaurus.config.js index 753ea5e85f..4af0829166 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -4,11 +4,40 @@ require('dotenv').config(); const markdownPreprocessor = require('./scripts/markdown-preprocessor'); -const sdkSidebarGenerator = require('./scripts/sdk-sidebar-generator'); const sdkCodebasePath = './submodules/arbitrum-sdk'; import remarkMath from 'remark-math'; import rehypeKatex from 'rehype-katex'; +// Shared Inkeep configuration +const inkeepBaseSettings = { + apiKey: process.env.INKEEP_API_KEY, + primaryBrandColor: '#213147', + organizationDisplayName: 'Arbitrum', + theme: { + syntaxHighlighter: { + lightTheme: require('prism-react-renderer/themes/github'), + darkTheme: require('prism-react-renderer/themes/palenight'), + }, + }, +}; + +const inkeepModalSettings = { + placeholder: 'Search documentation...', + defaultQuery: '', + maxResults: 40, + debounceTimeMs: 300, + shouldOpenLinksInNewTab: true, +}; + +const inkeepExampleQuestions = [ + 'How to estimate gas in Arbitrum?', + 'What is the difference between Arbitrum One and Nova?', + 'How to deploy a smart contract on Arbitrum?', + 'What are Arbitrum Orbit chains?', + 'How does Arbitrum handle L1 to L2 messaging?', + 'What is Arbitrum Stylus?', +]; + /** @type {import('@docusaurus/types').Config} */ const config = { title: 'Arbitrum Docs', @@ -21,6 +50,22 @@ const config = { markdown: { mermaid: true, preprocessor: markdownPreprocessor, + parseFrontMatter: async (params) => { + // Use the default parser + const result = await params.defaultParseFrontMatter(params); + + // Check if this is a partial file (starts with underscore) + const fileName = params.filePath.split('/').pop(); + const isPartialFile = fileName && fileName.startsWith('_'); + + // For partial files, clear frontmatter to prevent Docusaurus warnings + // The documentation-graph tool reads raw files directly, so this doesn't affect analysis + if (isPartialFile) { + result.frontMatter = {}; + } + + return result; + }, }, themes: ['@docusaurus/theme-mermaid', '@docusaurus/theme-live-codeblock'], // GitHub pages deployment config. @@ -42,10 +87,6 @@ const config = { integrity: 'sha384-odtC+0UGzzFL/6PNoE8rX/SPcQDXBJ+uRepguP4QkPCm2LBxH3FA3y+fKSiJ+AmM', crossorigin: 'anonymous', }, - { - href: '/css/inkeep-custom.css', - type: 'text/css', - }, ], presets: [ [ @@ -53,7 +94,6 @@ const config = { /** @type {import('@docusaurus/preset-classic').Options} */ ({ docs: { - // path: './docs', exclude: ['**/api/**'], remarkPlugins: [remarkMath], rehypePlugins: [rehypeKatex], @@ -132,85 +172,22 @@ const config = { '@inkeep/cxkit-docusaurus', { SearchBar: { - baseSettings: { - apiKey: process.env.INKEEP_API_KEY, - primaryBrandColor: '#213147', // Arbitrum's primary brand color - organizationDisplayName: 'Arbitrum', - theme: { - syntaxHighlighter: { - lightTheme: require('prism-react-renderer/themes/github'), - darkTheme: require('prism-react-renderer/themes/palenight'), - }, - components: { - SearchBarTrigger: { - // Configure responsive sizing behavior - defaultSize: 'medium', - minWidth: '180px', - maxWidth: '320px', - }, - }, - }, - }, - modalSettings: { - placeholder: 'Search documentation...', - defaultQuery: '', - maxResults: 40, - debounceTimeMs: 300, - shouldOpenLinksInNewTab: true, - }, - searchSettings: { - // optional settings - }, + baseSettings: inkeepBaseSettings, + modalSettings: inkeepModalSettings, aiChatSettings: { - aiAssistantAvatar: '/img/logo.svg', // Using Arbitrum logo as AI assistant avatar - exampleQuestions: [ - 'How to estimate gas in Arbitrum?', - 'What is the difference between Arbitrum One and Nova?', - 'How to deploy a smart contract on Arbitrum?', - 'What are Arbitrum Orbit chains?', - 'How does Arbitrum handle L1 to L2 messaging?', - 'What is Arbitrum Stylus?', - ], + aiAssistantAvatar: '/img/logo.svg', + exampleQuestions: inkeepExampleQuestions, botName: 'Arbitrum Assistant', getStartedMessage: "Hi! I'm here to help you navigate Arbitrum documentation. Ask me anything about building on Arbitrum, deploying contracts, or understanding our technology.", }, }, ChatButton: { - baseSettings: { - // see https://docusaurus.io/docs/deployment#using-environment-variables to use docusaurus environment variables - apiKey: process.env.INKEEP_API_KEY, - primaryBrandColor: '#213147', // Arbitrum's primary brand color - organizationDisplayName: 'Arbitrum', - // ...optional settings - theme: { - syntaxHighlighter: { - lightTheme: require('prism-react-renderer/themes/github'), - darkTheme: require('prism-react-renderer/themes/palenight'), - }, - }, - }, - modalSettings: { - placeholder: 'Search documentation...', - defaultQuery: '', - maxResults: 40, - debounceTimeMs: 300, - shouldOpenLinksInNewTab: true, - }, - searchSettings: { - // optional settings - }, + baseSettings: inkeepBaseSettings, + modalSettings: inkeepModalSettings, aiChatSettings: { - // optional settings - aiAssistantAvatar: '/img/logo.svg', // optional -- use your own AI assistant avatar - exampleQuestions: [ - 'How to estimate gas in Arbitrum?', - 'What is the difference between Arbitrum One and Nova?', - 'How to deploy a smart contract on Arbitrum?', - 'What are Arbitrum Orbit chains?', - 'How does Arbitrum handle L1 to L2 messaging?', - 'What is Arbitrum Stylus?', - ], + aiAssistantAvatar: '/img/logo.svg', + exampleQuestions: inkeepExampleQuestions, }, }, }, @@ -246,72 +223,13 @@ const config = { logo: { alt: 'Arbitrum Logo', src: 'img/logo.svg', - href: '/get-started', + href: '/welcome/arbitrum-gentle-introduction', }, - items: [ - { - type: 'docSidebar', - sidebarId: 'getStartedSidebar', - position: 'right', - label: 'Get started', - }, - { - type: 'dropdown', - label: 'Build apps', - position: 'right', - items: [ - { - label: 'Quickstart', - to: '/build-decentralized-apps/quickstart-solidity-remix', - }, - { - label: 'Stylus', - to: '/stylus/gentle-introduction', - }, - ], - }, - { - type: 'docSidebar', - sidebarId: 'runArbitrumChainSidebar', - position: 'right', - label: 'Launch a chain', - }, - // { - // type: 'docSidebar', - // sidebarId: 'stylusSidebar', - // position: 'right', - // label: 'Use Stylus', - // }, - { - type: 'docSidebar', - sidebarId: 'runNodeSidebar', - position: 'right', - label: 'Run a node', - }, - { - type: 'docSidebar', - sidebarId: 'bridgeSidebar', - position: 'right', - label: 'Use the bridge', - }, - { - type: 'docSidebar', - sidebarId: 'howItWorksSidebar', - position: 'right', - label: 'How it works', - }, - { - type: 'docSidebar', - sidebarId: 'additionalResourcesSidebar', - position: 'right', - label: 'Resources', - }, - ], + items: [], }, footer: { style: 'dark', links: [ - {}, { items: [ { @@ -413,6 +331,8 @@ const config = { }, prism: { additionalLanguages: ['solidity', 'rust', 'bash', 'toml'], + theme: require('prism-react-renderer/themes/github'), + darkTheme: require('prism-react-renderer/themes/palenight'), }, liveCodeBlock: { /** @@ -437,23 +357,4 @@ const config = { }), }; -// HACK -// this was originally included above -// it broke local builds on Windows, not sure why yet. Works fine on Mac -// `generate_sdk_docs` runs fine, no difference in outputs between environments, so it's not easy to debug - low pri -const isRunningLocally = process.env.NODE_ENV === 'development'; -const isRunningOnWindows = process.platform === 'win32'; -if (isRunningLocally && isRunningOnWindows) { - config.plugins = config.plugins.filter((plugin) => { - if (Array.isArray(plugin) && plugin[0] === '@docusaurus/plugin-content-docs') { - return false; // remove the offending plugin config - } - return true; // keep everything else - }); -} else { - // another hack for another strange windows-specific issue, reproduceable through clean clone of repo - config.themeConfig.prism.theme = require('prism-react-renderer/themes/github'); - config.themeConfig.prism.darkTheme = require('prism-react-renderer/themes/palenight'); -} - module.exports = config; diff --git a/scripts/build-glossary.ts b/scripts/build-glossary.ts index f2dbb1b759..1ad66fd987 100644 --- a/scripts/build-glossary.ts +++ b/scripts/build-glossary.ts @@ -22,7 +22,7 @@ function escapeForJSON(str: string): string { .replace(/"/g, '\\"') .replace(/\n/g, '\\n') .replace(/\r/g, '\\r') - .replace(/\t/g, '\\t') + .replace(/\t/g, '\\t'); } /** @@ -127,7 +127,9 @@ async function main(): Promise { // Generate import statements for each term (unused in current implementation) // This could be used if implementing a React component approach to term rendering let imports = terms - .map((item) => `import ${renderKey(item.data.key)} from './docs/glossary/${item.data.key}.mdx';`) + .map( + (item) => `import ${renderKey(item.data.key)} from './docs/glossary/${item.data.key}.mdx';`, + ) .join('\n'); // Generate component references for each term (unused in current implementation) @@ -135,11 +137,24 @@ async function main(): Promise { // Generate and write the consolidated glossary partial MDX file // This creates a single file with all terms formatted as Markdown headings + const currentDate = new Date().toISOString().split('T')[0]; // Format: YYYY-MM-DD + const frontmatter = `--- +partial_type: glossary +title: "Arbitrum Glossary Definitions" +description: "Comprehensive glossary of Arbitrum terminology and definitions" +author: anegg0 +last_reviewed: ${currentDate} +--- + +`; + await fs.writeFile( './docs/partials/_glossary-partial.mdx', - terms - .map((item) => `### ${item.data.title} {#${item.data.key}}\n\n${item.content.trim()}`) - .join('\n\n') + '\n', + frontmatter + + terms + .map((item) => `### ${item.data.title} {#${item.data.key}}\n\n${item.content.trim()}`) + .join('\n\n') + + '\n', ); // Generate and write the JSON glossary file for client-side usage diff --git a/scripts/sync-stylus-content.js b/scripts/sync-stylus-content.js index 3075e2c84d..504cfd29a6 100644 --- a/scripts/sync-stylus-content.js +++ b/scripts/sync-stylus-content.js @@ -1,278 +1,671 @@ #!/usr/bin/env node /** - * Simple script to sync content from stylus-by-example submodule to docs directory - * Replaces the complex 897-line stylusByExampleDocsHandler.ts + * @fileoverview Sync content from stylus-by-example submodule to docs directory + * @description Replaces the complex 897-line stylusByExampleDocsHandler.ts with a maintainable solution + * @author sync-stylus-content.js + * @version 2.0.0 (refactored for maintainability) */ const fs = require('fs-extra'); const path = require('path'); const glob = require('glob'); -// Configuration -const SOURCE_DIR = path.join(__dirname, '../submodules/stylus-by-example/src/app'); -const TARGET_DIR = path.join(__dirname, '../docs/stylus-by-example'); -const DONT_EDIT_MARKER = 'DONT-EDIT-THIS-FOLDER'; +// ============================================================================ +// CONFIGURATION - All constants and settings in one place +// ============================================================================ /** - * Convert Next.js style metadata export to Docusaurus frontmatter + * @typedef {Object} SyncConfig + * @property {Object} paths - File and directory paths + * @property {Object} content - Content processing settings + * @property {Object} allowLists - File filtering configurations + * @property {Object} output - Output generation settings */ -function convertMetadataToFrontmatter(content) { - // Pattern to match: export const metadata = { ... } - const metadataPattern = /export\s+const\s+metadata\s*=\s*({[\s\S]*?});/; - const match = content.match(metadataPattern); - - if (!match) { - return content; + +/** @type {SyncConfig} */ +const CONFIG = { + paths: { + sourceDir: path.join(__dirname, '../submodules/stylus-by-example/src/app'), + targetDir: path.join(__dirname, '../docs/stylus-by-example'), + dontEditMarker: 'DONT-EDIT-THIS-FOLDER', + sourcePattern: '**/page.mdx', + sidebarFileName: 'sidebar.js', + }, + + content: { + fileExtensions: { + source: '.mdx', + target: '.mdx', + sidebar: '.js', + }, + markers: { + contentBegin: '{/* Begin Content */}', + frontmatterDelimiter: '---', + }, + patterns: { + metadata: /export\s+const\s+metadata\s*=\s*({[\s\S]*?});/, + relativeLink: /\[([^\]]+)\]\(\.\/([\w_]+)\)/g, + }, + }, + + allowLists: { + basicExamples: [ + 'hello_world', + 'primitive_data_types', + 'variables', + 'constants', + 'function', + 'errors', + 'events', + 'inheritance', + 'vm_affordances', + 'sending_ether', + 'function_selector', + 'abi_encode', + 'abi_decode', + 'hashing', + 'bytes_in_bytes_out', + ], + applications: ['erc20', 'erc721', 'vending_machine', 'multi_call'], + }, + + output: { + sections: ['basic_examples', 'applications'], + indexFileName: 'index.mdx', + sidebarTemplate: { + fileHeader: '// @ts-check', + docString: 'Autogenerated sidebar configuration for Stylus by Example', + typeAnnotation: "/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */", + }, + }, +}; + +// ============================================================================ +// UTILITY FUNCTIONS - Reusable helper functions +// ============================================================================ + +/** + * Creates a standardized error with context information + * @param {string} message - Error message + * @param {string} context - Additional context (file path, operation, etc.) + * @param {Error} [originalError] - Original error object if available + * @returns {Error} Enhanced error object + */ +function createError(message, context, originalError = null) { + const errorMessage = `${message} (Context: ${context})`; + const error = new Error(errorMessage); + error.context = context; + error.originalError = originalError; + return error; +} + +/** + * Logs messages with consistent formatting and timestamps + * @param {string} level - Log level (info, warn, error, success) + * @param {string} message - Message to log + * @param {Object} [details] - Additional details to log + */ +function log(level, message, details = null) { + const timestamp = new Date().toISOString(); + const emoji = + { + info: '🔄', + warn: '⚠️', + error: '❌', + success: '✅', + clean: '🧹', + file: '📁', + write: '📝', + }[level] || 'ℹ️'; + + console.log(`${emoji} ${message}`); + + if (details) { + console.log(` Details: ${JSON.stringify(details, null, 2)}`); } - +} + +/** + * Converts snake_case string to Title Case + * @param {string} snakeCaseStr - String in snake_case format + * @returns {string} String in Title Case format + */ +function snakeCaseToTitleCase(snakeCaseStr) { + return snakeCaseStr + .split('_') + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); +} + +/** + * Safely evaluates a JavaScript object literal string + * @param {string} objectStr - String representation of JavaScript object + * @returns {Object} Parsed object + * @throws {Error} If evaluation fails + */ +function safeEvalObjectLiteral(objectStr) { try { - // Extract and parse the metadata object - const metadataStr = match[1]; // Use Function constructor to safely evaluate the object literal - const metadata = new Function(`return ${metadataStr}`)(); - - // Convert to YAML frontmatter - const frontmatter = Object.entries(metadata) - .map(([key, value]) => { - // Handle different value types - if (typeof value === 'string') { - // Escape single quotes in strings - return `${key}: '${value.replace(/'/g, "''")}'`; - } - return `${key}: ${value}`; - }) - .join('\n'); - - // Replace the export with frontmatter - return content.replace(metadataPattern, `---\n${frontmatter}\n---`); + return new Function(`return ${objectStr}`)(); } catch (error) { - console.warn(`Warning: Could not convert metadata: ${error.message}`); - return content; + throw createError('Failed to evaluate object literal', objectStr, error); } } /** - * Transform relative links to work with Docusaurus structure + * Escapes single quotes in strings for YAML frontmatter + * @param {string} str - String to escape + * @returns {string} Escaped string */ -function fixRelativeLinks(content) { - // Fix relative links that point to sibling directories - // When file name matches directory name, Docusaurus simplifies the URL - // Pattern: [text](./directory_name) should become [text](/stylus-by-example/basic_examples/directory_name) - content = content.replace( - /\[([^\]]+)\]\(\.\/([\w_]+)\)/g, - (match, linkText, dirName) => { - // Convert to absolute Docusaurus path (simplified URL when filename matches directory) - return `[${linkText}](/stylus-by-example/basic_examples/${dirName})`; +function escapeYamlString(str) { + return str.replace(/'/g, "''"); +} + +/** + * Generates a timestamp string for file headers + * @returns {string} ISO timestamp string + */ +function generateTimestamp() { + return new Date().toISOString(); +} + +// ============================================================================ +// PATH AND FILE UTILITIES - File system operations and path manipulation +// ============================================================================ + +/** + * Extracts section name from a file path relative to source directory + * @param {string} filePath - Full file path + * @returns {string|null} Section name ('basic_examples' or 'applications') or null + */ +function extractSectionFromPath(filePath) { + const relativePath = path.relative(CONFIG.paths.sourceDir, filePath); + const segments = relativePath.split(path.sep); + + return segments.length >= 2 ? segments[0] : null; +} + +/** + * Extracts the item name (directory name) from a file path + * @param {string} filePath - Full file path to page.mdx + * @returns {string} Directory name containing the file + */ +function extractItemNameFromPath(filePath) { + return path.basename(path.dirname(filePath)); +} + +/** + * Gets the appropriate allowlist for a section + * @param {string} sectionName - Section name ('basic_examples' or 'applications') + * @returns {string[]} Array of allowed file names + */ +function getAllowListForSection(sectionName) { + const allowListMap = { + basic_examples: CONFIG.allowLists.basicExamples, + applications: CONFIG.allowLists.applications, + }; + + return allowListMap[sectionName] || []; +} + +/** + * Determines if a file should be processed based on allowlists + * @param {string} filePath - Full path to the file + * @returns {boolean} True if file should be processed + */ +function isFileAllowed(filePath) { + const sectionName = extractSectionFromPath(filePath); + if (!sectionName) return false; + + const itemName = extractItemNameFromPath(filePath); + const allowList = getAllowListForSection(sectionName); + + return allowList.includes(itemName); +} + +/** + * Generates target file path from source file path + * @param {string} sourceFile - Source file path + * @returns {Object} Object with targetPath and metadata + */ +function generateTargetPath(sourceFile) { + const relativePath = path.relative(CONFIG.paths.sourceDir, sourceFile); + const targetDir = path.dirname(relativePath); + + let targetFileName; + let finalTargetDir; + + if (targetDir === '.') { + // Root level file + targetFileName = CONFIG.output.indexFileName; + finalTargetDir = CONFIG.paths.targetDir; + } else { + // Section subdirectory file + const pathParts = targetDir.split(path.sep); + const sectionName = pathParts[0]; + const itemName = pathParts[1]; + + targetFileName = `${itemName}${CONFIG.content.fileExtensions.target}`; + finalTargetDir = path.join(CONFIG.paths.targetDir, sectionName); + } + + return { + targetPath: path.join(finalTargetDir, targetFileName), + targetDir: finalTargetDir, + fileName: targetFileName, + }; +} + +// ============================================================================ +// CONTENT TRANSFORMATION - Text processing and content manipulation +// ============================================================================ + +/** + * Converts a metadata object to YAML frontmatter format + * @param {Object} metadata - Metadata object to convert + * @returns {string} YAML frontmatter string + */ +function convertMetadataToYaml(metadata) { + const yamlLines = Object.entries(metadata).map(([key, value]) => { + if (typeof value === 'string') { + return `${key}: '${escapeYamlString(value)}'`; } - ); - - return content; + return `${key}: ${value}`; + }); + + return yamlLines.join('\n'); } /** - * Process and copy a single MDX file + * Transforms Next.js metadata export to Docusaurus frontmatter + * @param {string} content - File content with metadata export + * @returns {string} Content with frontmatter instead of metadata export */ -async function processFile(sourcePath, targetPath) { +function convertMetadataToFrontmatter(content) { + const match = content.match(CONFIG.content.patterns.metadata); + if (!match) return content; + try { - // Read source file - let content = await fs.readFile(sourcePath, 'utf8'); - - // Transform metadata to frontmatter - content = convertMetadataToFrontmatter(content); - - // Fix relative links - content = fixRelativeLinks(content); - - // Add content begin marker if not present - if (!content.includes('{/* Begin Content */}')) { - // Find the position after frontmatter - const frontmatterEnd = content.indexOf('---', 3); - if (frontmatterEnd !== -1) { - const insertPos = content.indexOf('\n', frontmatterEnd + 3) + 1; - content = content.slice(0, insertPos) + '\n{/* Begin Content */}\n' + content.slice(insertPos); - } - } - - // Ensure target directory exists - await fs.ensureDir(path.dirname(targetPath)); - - // Write transformed content - await fs.writeFile(targetPath, content); - - return true; + const metadataStr = match[1]; + const metadata = safeEvalObjectLiteral(metadataStr); + const frontmatter = convertMetadataToYaml(metadata); + + const delimiter = CONFIG.content.markers.frontmatterDelimiter; + const replacement = `${delimiter}\n${frontmatter}\n${delimiter}`; + + return content.replace(CONFIG.content.patterns.metadata, replacement); } catch (error) { - console.error(`Error processing ${sourcePath}: ${error.message}`); - return false; + log('warn', `Could not convert metadata: ${error.message}`); + return content; } } /** - * Generate sidebar configuration for a directory + * Fixes relative links to work with Docusaurus URL structure + * @param {string} content - Content with relative links + * @returns {string} Content with fixed absolute links */ -function generateSidebar(sectionName, files) { - const items = files - .map(file => { - // Get the relative path from the section directory - const relativePath = path.relative(path.join(TARGET_DIR, sectionName), file); - const fileName = path.basename(file, '.mdx'); - const dirName = path.dirname(relativePath); - - // Skip if filename is same as directory (already processed) - if (fileName === sectionName) { - return null; - } - - // Convert filename to label (snake_case to Title Case) - const label = fileName - .split('_') - .map(word => word.charAt(0).toUpperCase() + word.slice(1)) - .join(' '); - - // Generate doc ID based on actual file structure - // For files in subdirectories: stylus-by-example/applications/erc20/erc20 - const docId = dirName === '.' - ? `stylus-by-example/${sectionName}/${fileName}` - : `stylus-by-example/${sectionName}/${dirName}/${fileName}`; - - return { - type: 'doc', - label: label, - id: docId - }; - }) - .filter(Boolean); - - return `// @ts-check +function fixRelativeLinks(content) { + return content.replace(CONFIG.content.patterns.relativeLink, (match, linkText, dirName) => { + // Convert to absolute Docusaurus path + return `[${linkText}](/stylus-by-example/basic_examples/${dirName})`; + }); +} + +/** + * Adds content begin marker if not already present + * @param {string} content - File content + * @returns {string} Content with begin marker + */ +function addContentBeginMarker(content) { + const marker = CONFIG.content.markers.contentBegin; + if (content.includes(marker)) return content; + + const delimiter = CONFIG.content.markers.frontmatterDelimiter; + const frontmatterEnd = content.indexOf(delimiter, 3); + + if (frontmatterEnd === -1) return content; + + const insertPos = content.indexOf('\n', frontmatterEnd + 3) + 1; + return content.slice(0, insertPos) + `\n${marker}\n` + content.slice(insertPos); +} + +/** + * Adds "not for production" banner import and component two lines before first rust code block + * @param {string} content - File content + * @returns {string} Content with banner added + */ +function addNotForProductionBanner(content) { + const firstCodeBlock = '```rust'; + const bannerCode = ` +import NotForProductionBannerPartial from '../../partials/_not-for-production-banner-partial.mdx'; + + +`; + + // Find the first occurrence of ```rust code block + const index = content.indexOf(firstCodeBlock); + if (index === -1) { + // No rust code block found, return content unchanged + return content; + } + + // Find the position two lines before the first code block + const lines = content.substring(0, index).split('\n'); + const insertLineIndex = lines.length - 2; + + // Insert the banner at the calculated position + lines.splice(insertLineIndex, 0, bannerCode); + + // Reconstruct the content with the banner inserted + const newContent = lines.join('\n') + content.substring(index); + return newContent; +} + /** - * @fileoverview Autogenerated sidebar configuration for Stylus by Example + * Applies all content transformations to a file + * @param {string} content - Original file content + * @returns {string} Transformed content + */ +function transformContent(content) { + let transformedContent = content; + + // Apply transformations in sequence + transformedContent = convertMetadataToFrontmatter(transformedContent); + transformedContent = fixRelativeLinks(transformedContent); + transformedContent = addContentBeginMarker(transformedContent); + transformedContent = addNotForProductionBanner(transformedContent); + + return transformedContent; +} + +// ============================================================================ +// SIDEBAR GENERATION - Sidebar configuration file generation +// ============================================================================ + +/** + * Creates a sidebar item object for a file + * @param {string} sectionName - Section name (basic_examples, applications) + * @param {string} fileName - File name without extension + * @returns {Object} Sidebar item configuration + */ +function createSidebarItem(sectionName, fileName) { + return { + type: 'doc', + id: `stylus-by-example/${sectionName}/${fileName}`, + label: snakeCaseToTitleCase(fileName), + }; +} + +/** + * Generates the complete sidebar JavaScript file content + * @param {string} sectionName - Section name for the sidebar + * @param {Object[]} sidebarItems - Array of sidebar item objects + * @returns {string} Complete sidebar file content + */ +function generateSidebarFileContent(sectionName, sidebarItems) { + const template = CONFIG.output.sidebarTemplate; + const timestamp = generateTimestamp(); + + return `${template.fileHeader} +/** + * @fileoverview ${template.docString} ${sectionName} * @description This file is automatically generated by sync-stylus-content.js - * @generated ${new Date().toISOString()} + * @generated ${timestamp} * @see https://docusaurus.io/docs/sidebar * @see https://docusaurus.io/docs/sidebar/items */ -/** @type {import('@docusaurus/plugin-content-docs').SidebarItem[]} */ -module.exports = ${JSON.stringify(items, null, 2)}; +${template.typeAnnotation} +const sidebar = ${JSON.stringify({ items: sidebarItems }, null, 2)}; + +module.exports = sidebar.items; `; } /** - * Main sync function + * Generates sidebar configuration for a section directory + * @param {string} sectionName - Section name (basic_examples, applications) + * @param {string[]} files - Array of file paths + * @returns {string} Complete sidebar JavaScript file content */ -async function syncContent() { - console.log('🔄 Syncing Stylus by Example content...'); - +function generateSidebar(sectionName, files) { + const allowList = getAllowListForSection(sectionName); + const itemsByFileName = {}; + + // Collect all items by filename, skipping index files + files.forEach((file) => { + const fileName = path.basename(file, CONFIG.content.fileExtensions.source); + + // Skip index files (filename matches section name) + if (fileName === sectionName) return; + + const item = createSidebarItem(sectionName, fileName); + itemsByFileName[fileName] = item; + }); + + // Order items according to allowlist + const orderedItems = allowList + .map((allowedFileName) => itemsByFileName[allowedFileName]) + .filter(Boolean); // Remove undefined items + + return generateSidebarFileContent(sectionName, orderedItems); +} + +// ============================================================================ +// FILE OPERATIONS - High-level file processing functions +// ============================================================================ + +/** + * Processes and copies a single MDX file with transformations + * @param {string} sourcePath - Source file path + * @param {string} targetPath - Target file path + * @returns {Promise} True if processing succeeded + */ +async function processFile(sourcePath, targetPath) { try { - // Check if source exists - if (!await fs.pathExists(SOURCE_DIR)) { - console.warn(`⚠️ Source directory not found: ${SOURCE_DIR}`); - console.log('Skipping Stylus by Example content sync.'); - console.log('To include this content, initialize the submodule:'); - console.log(' git submodule update --init --recursive'); - - // Create empty target directory with marker - await fs.ensureDir(TARGET_DIR); - await fs.writeFile( - path.join(TARGET_DIR, DONT_EDIT_MARKER), - `This folder would contain auto-generated content from submodules/stylus-by-example -The submodule was not available during build. -Generated: ${new Date().toISOString()} + // Read and transform content + const content = await fs.readFile(sourcePath, 'utf8'); + const transformedContent = transformContent(content); + + // Ensure target directory exists and write file + await fs.ensureDir(path.dirname(targetPath)); + await fs.writeFile(targetPath, transformedContent); + + return true; + } catch (error) { + log('error', `Failed to process file: ${sourcePath}`, { + error: error.message, + targetPath, + }); + return false; + } +} + +/** + * Creates the "don't edit" marker file in target directory + * @param {string} targetDir - Target directory path + * @param {boolean} isSubmoduleAvailable - Whether submodule is available + * @returns {Promise} + */ +async function createDontEditMarker(targetDir, isSubmoduleAvailable = true) { + const timestamp = generateTimestamp(); + const markerPath = path.join(targetDir, CONFIG.paths.dontEditMarker); + + const content = isSubmoduleAvailable + ? `This folder is auto-generated from submodules/stylus-by-example +Do not edit files directly here. Edit them in the submodule instead. +Generated: ${timestamp} ` - ); - - // Create empty sidebar files for the expected directories - const expectedDirs = ['basic_examples', 'applications']; - for (const dir of expectedDirs) { - await fs.ensureDir(path.join(TARGET_DIR, dir)); - const emptySidebar = `// @ts-check + : `This folder would contain auto-generated content from submodules/stylus-by-example +The submodule was not available during build. +Generated: ${timestamp} +`; + + await fs.writeFile(markerPath, content); +} + +/** + * Creates empty sidebar files for sections when submodule is unavailable + * @param {string} targetDir - Target directory path + * @returns {Promise} + */ +async function createEmptySidebars(targetDir) { + const timestamp = generateTimestamp(); + const template = CONFIG.output.sidebarTemplate; + + const emptySidebarContent = `${template.fileHeader} /** * @fileoverview Empty sidebar configuration (submodule not available) - * @generated ${new Date().toISOString()} + * @generated ${timestamp} */ -/** @type {import('@docusaurus/plugin-content-docs').SidebarItem[]} */ -module.exports = []; +${template.typeAnnotation} +const sidebar = { + items: [] +}; + +module.exports = sidebar.items; `; - await fs.writeFile(path.join(TARGET_DIR, dir, 'sidebar.js'), emptySidebar); - } - - console.log('✅ Created empty Stylus by Example structure'); - return; + + for (const sectionName of CONFIG.output.sections) { + const sectionDir = path.join(targetDir, sectionName); + await fs.ensureDir(sectionDir); + + const sidebarPath = path.join(sectionDir, CONFIG.paths.sidebarFileName); + await fs.writeFile(sidebarPath, emptySidebarContent); + } +} + +/** + * Generates sidebar files for all processed sections + * @param {string} targetDir - Target directory path + * @returns {Promise} + */ +async function generateAllSidebars(targetDir) { + for (const sectionName of CONFIG.output.sections) { + const sectionDir = path.join(targetDir, sectionName); + + if (await fs.pathExists(sectionDir)) { + const pattern = path.join(sectionDir, `**/*${CONFIG.content.fileExtensions.source}`); + const sectionFiles = glob.sync(pattern); + + const sidebarContent = generateSidebar(sectionName, sectionFiles); + const sidebarPath = path.join(sectionDir, CONFIG.paths.sidebarFileName); + + await fs.writeFile(sidebarPath, sidebarContent); + log('write', `Generated sidebar for ${sectionName}`, { + itemCount: sectionFiles.length, + }); } - - // Clean target directory - if (await fs.pathExists(TARGET_DIR)) { - console.log('🧹 Cleaning target directory...'); - await fs.remove(TARGET_DIR); + } +} + +// ============================================================================ +// MAIN SYNC LOGIC - Core synchronization workflow +// ============================================================================ + +/** + * Handles the case when the source submodule is not available + * @returns {Promise} + */ +async function handleMissingSubmodule() { + log('warn', `Source directory not found: ${CONFIG.paths.sourceDir}`); + log('info', 'Skipping Stylus by Example content sync.'); + log('info', 'To include this content, initialize the submodule:'); + log('info', ' git submodule update --init --recursive'); + + // Create empty structure + await fs.ensureDir(CONFIG.paths.targetDir); + await createDontEditMarker(CONFIG.paths.targetDir, false); + await createEmptySidebars(CONFIG.paths.targetDir); + + log('success', 'Created empty Stylus by Example structure'); +} + +/** + * Processes all allowed files from source to target directory + * @param {string[]} allowedFiles - Array of file paths to process + * @returns {Promise} Number of successfully processed files + */ +async function processAllFiles(allowedFiles) { + let successCount = 0; + + for (const sourceFile of allowedFiles) { + const { targetPath } = generateTargetPath(sourceFile); + + if (await processFile(sourceFile, targetPath)) { + successCount++; } - - // Create target directory - await fs.ensureDir(TARGET_DIR); - - // Add marker file - await fs.writeFile( - path.join(TARGET_DIR, DONT_EDIT_MARKER), - `This folder is auto-generated from submodules/stylus-by-example -Do not edit files directly here. Edit them in the submodule instead. -Generated: ${new Date().toISOString()} -` - ); - - // Find all page.mdx files - const pattern = path.join(SOURCE_DIR, '**/page.mdx'); - const files = glob.sync(pattern); - - console.log(`📁 Found ${files.length} files to process`); - - // Process each file - let successCount = 0; - for (const sourceFile of files) { - // Calculate target path - const relativePath = path.relative(SOURCE_DIR, sourceFile); - const targetDir = path.dirname(relativePath); - - // Rename page.mdx to directory name or keep as index - let targetFileName; - if (targetDir === '.') { - targetFileName = 'index.mdx'; - } else { - // Use the parent directory name as the filename - const parentDir = path.basename(targetDir); - targetFileName = `${parentDir}.mdx`; - } - - const targetFile = path.join(TARGET_DIR, targetDir, targetFileName); - - // Process and copy file - if (await processFile(sourceFile, targetFile)) { - successCount++; - } + } + + return successCount; +} + +/** + * Main synchronization function + * @returns {Promise} + */ +async function syncContent() { + log('info', 'Syncing Stylus by Example content...'); + + try { + // Check if source exists + if (!(await fs.pathExists(CONFIG.paths.sourceDir))) { + await handleMissingSubmodule(); + return; } - - // Generate sidebars for each directory - const directories = ['basic_examples', 'applications']; - for (const dir of directories) { - const dirPath = path.join(TARGET_DIR, dir); - if (await fs.pathExists(dirPath)) { - const dirFiles = glob.sync(path.join(dirPath, '**/*.mdx')); - const sidebarContent = generateSidebar(dir, dirFiles); - await fs.writeFile(path.join(dirPath, 'sidebar.js'), sidebarContent); - console.log(`📝 Generated sidebar for ${dir} (${dirFiles.length} items)`); - } + + // Clean and recreate target directory + if (await fs.pathExists(CONFIG.paths.targetDir)) { + log('clean', 'Cleaning target directory...'); + await fs.remove(CONFIG.paths.targetDir); } - - console.log(`✅ Successfully synced ${successCount}/${files.length} files`); - + + await fs.ensureDir(CONFIG.paths.targetDir); + await createDontEditMarker(CONFIG.paths.targetDir, true); + + // Find and filter files + const sourcePattern = path.join(CONFIG.paths.sourceDir, CONFIG.paths.sourcePattern); + const allFiles = glob.sync(sourcePattern); + const allowedFiles = allFiles.filter(isFileAllowed); + + log( + 'file', + `Found ${allFiles.length} total files, ${allowedFiles.length} allowed files to process`, + ); + + // Process all files + const successCount = await processAllFiles(allowedFiles); + + // Generate sidebars + await generateAllSidebars(CONFIG.paths.targetDir); + + log('success', `Successfully synced ${successCount}/${allowedFiles.length} files`); } catch (error) { - console.error(`❌ Sync failed: ${error.message}`); + log('error', `Sync failed: ${error.message}`, { + stack: error.stack, + context: error.context || 'Unknown', + }); process.exit(1); } } +// ============================================================================ +// MODULE EXPORTS AND EXECUTION +// ============================================================================ + // Run if called directly if (require.main === module) { syncContent(); } -module.exports = { syncContent }; \ No newline at end of file +module.exports = { + syncContent, + // Export utilities for testing + CONFIG, + isFileAllowed, + transformContent, + generateSidebar, +}; From 6fccb7bcae8c6fe8d341bafdf63f91e9780ff305 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Wed, 1 Oct 2025 11:16:37 -0700 Subject: [PATCH 34/62] Revert "Reapply "update to latest"" This reverts commit 9ba0dd0bea3a7e35c393a8d580fa51fad0ffdcda. --- .../_bold-public-preview-banner-partial.mdx | 7 + docusaurus.config.js | 215 +++-- scripts/build-glossary.ts | 25 +- scripts/sync-stylus-content.js | 819 +++++------------- 4 files changed, 382 insertions(+), 684 deletions(-) create mode 100644 docs/how-arbitrum-works/bold/partials/_bold-public-preview-banner-partial.mdx diff --git a/docs/how-arbitrum-works/bold/partials/_bold-public-preview-banner-partial.mdx b/docs/how-arbitrum-works/bold/partials/_bold-public-preview-banner-partial.mdx new file mode 100644 index 0000000000..a4a519e9e1 --- /dev/null +++ b/docs/how-arbitrum-works/bold/partials/_bold-public-preview-banner-partial.mdx @@ -0,0 +1,7 @@ +:::caution ALPHA RELEASE, PUBLIC PREVIEW DOCS + +The BoLD dispute protocol is currently deployed on a public testnet (that posts assertions to Ethereum Sepolia) and is tagged as an `alpha` release. The code has been audited by [Trail of Bits](https://github.com/trailofbits/publications/blob/master/reviews/2024-04-offchainbold-securityreview.pdf) and in a [public audit competition with Code4rena](https://code4rena.com/audits/2024-05-arbitrum-bold), but **should not be used in production scenarios**. Please note that the public testnet is intended for Arbitrum users and researchers to test and experiment with the BoLD dispute protocol for the purposes of education and hardening the protocol via the surfacing of bugs. The public testnet may be paused, and its parameters may be updated at any time in response to scenarios that impact the network and its ability to fulfill its function as a working, reliable test environment. This documentation is currently in [public preview](../public-preview-expectations.mdx). + +To provide feedback, click the _Request an update_ button at the top of this document, [join the Arbitrum Discord](https://discord.gg/arbitrum), or reach out to our team directly by completing [this form](http://bit.ly/3yy6EUK). + +::: diff --git a/docusaurus.config.js b/docusaurus.config.js index 4af0829166..753ea5e85f 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -4,40 +4,11 @@ require('dotenv').config(); const markdownPreprocessor = require('./scripts/markdown-preprocessor'); +const sdkSidebarGenerator = require('./scripts/sdk-sidebar-generator'); const sdkCodebasePath = './submodules/arbitrum-sdk'; import remarkMath from 'remark-math'; import rehypeKatex from 'rehype-katex'; -// Shared Inkeep configuration -const inkeepBaseSettings = { - apiKey: process.env.INKEEP_API_KEY, - primaryBrandColor: '#213147', - organizationDisplayName: 'Arbitrum', - theme: { - syntaxHighlighter: { - lightTheme: require('prism-react-renderer/themes/github'), - darkTheme: require('prism-react-renderer/themes/palenight'), - }, - }, -}; - -const inkeepModalSettings = { - placeholder: 'Search documentation...', - defaultQuery: '', - maxResults: 40, - debounceTimeMs: 300, - shouldOpenLinksInNewTab: true, -}; - -const inkeepExampleQuestions = [ - 'How to estimate gas in Arbitrum?', - 'What is the difference between Arbitrum One and Nova?', - 'How to deploy a smart contract on Arbitrum?', - 'What are Arbitrum Orbit chains?', - 'How does Arbitrum handle L1 to L2 messaging?', - 'What is Arbitrum Stylus?', -]; - /** @type {import('@docusaurus/types').Config} */ const config = { title: 'Arbitrum Docs', @@ -50,22 +21,6 @@ const config = { markdown: { mermaid: true, preprocessor: markdownPreprocessor, - parseFrontMatter: async (params) => { - // Use the default parser - const result = await params.defaultParseFrontMatter(params); - - // Check if this is a partial file (starts with underscore) - const fileName = params.filePath.split('/').pop(); - const isPartialFile = fileName && fileName.startsWith('_'); - - // For partial files, clear frontmatter to prevent Docusaurus warnings - // The documentation-graph tool reads raw files directly, so this doesn't affect analysis - if (isPartialFile) { - result.frontMatter = {}; - } - - return result; - }, }, themes: ['@docusaurus/theme-mermaid', '@docusaurus/theme-live-codeblock'], // GitHub pages deployment config. @@ -87,6 +42,10 @@ const config = { integrity: 'sha384-odtC+0UGzzFL/6PNoE8rX/SPcQDXBJ+uRepguP4QkPCm2LBxH3FA3y+fKSiJ+AmM', crossorigin: 'anonymous', }, + { + href: '/css/inkeep-custom.css', + type: 'text/css', + }, ], presets: [ [ @@ -94,6 +53,7 @@ const config = { /** @type {import('@docusaurus/preset-classic').Options} */ ({ docs: { + // path: './docs', exclude: ['**/api/**'], remarkPlugins: [remarkMath], rehypePlugins: [rehypeKatex], @@ -172,22 +132,85 @@ const config = { '@inkeep/cxkit-docusaurus', { SearchBar: { - baseSettings: inkeepBaseSettings, - modalSettings: inkeepModalSettings, + baseSettings: { + apiKey: process.env.INKEEP_API_KEY, + primaryBrandColor: '#213147', // Arbitrum's primary brand color + organizationDisplayName: 'Arbitrum', + theme: { + syntaxHighlighter: { + lightTheme: require('prism-react-renderer/themes/github'), + darkTheme: require('prism-react-renderer/themes/palenight'), + }, + components: { + SearchBarTrigger: { + // Configure responsive sizing behavior + defaultSize: 'medium', + minWidth: '180px', + maxWidth: '320px', + }, + }, + }, + }, + modalSettings: { + placeholder: 'Search documentation...', + defaultQuery: '', + maxResults: 40, + debounceTimeMs: 300, + shouldOpenLinksInNewTab: true, + }, + searchSettings: { + // optional settings + }, aiChatSettings: { - aiAssistantAvatar: '/img/logo.svg', - exampleQuestions: inkeepExampleQuestions, + aiAssistantAvatar: '/img/logo.svg', // Using Arbitrum logo as AI assistant avatar + exampleQuestions: [ + 'How to estimate gas in Arbitrum?', + 'What is the difference between Arbitrum One and Nova?', + 'How to deploy a smart contract on Arbitrum?', + 'What are Arbitrum Orbit chains?', + 'How does Arbitrum handle L1 to L2 messaging?', + 'What is Arbitrum Stylus?', + ], botName: 'Arbitrum Assistant', getStartedMessage: "Hi! I'm here to help you navigate Arbitrum documentation. Ask me anything about building on Arbitrum, deploying contracts, or understanding our technology.", }, }, ChatButton: { - baseSettings: inkeepBaseSettings, - modalSettings: inkeepModalSettings, + baseSettings: { + // see https://docusaurus.io/docs/deployment#using-environment-variables to use docusaurus environment variables + apiKey: process.env.INKEEP_API_KEY, + primaryBrandColor: '#213147', // Arbitrum's primary brand color + organizationDisplayName: 'Arbitrum', + // ...optional settings + theme: { + syntaxHighlighter: { + lightTheme: require('prism-react-renderer/themes/github'), + darkTheme: require('prism-react-renderer/themes/palenight'), + }, + }, + }, + modalSettings: { + placeholder: 'Search documentation...', + defaultQuery: '', + maxResults: 40, + debounceTimeMs: 300, + shouldOpenLinksInNewTab: true, + }, + searchSettings: { + // optional settings + }, aiChatSettings: { - aiAssistantAvatar: '/img/logo.svg', - exampleQuestions: inkeepExampleQuestions, + // optional settings + aiAssistantAvatar: '/img/logo.svg', // optional -- use your own AI assistant avatar + exampleQuestions: [ + 'How to estimate gas in Arbitrum?', + 'What is the difference between Arbitrum One and Nova?', + 'How to deploy a smart contract on Arbitrum?', + 'What are Arbitrum Orbit chains?', + 'How does Arbitrum handle L1 to L2 messaging?', + 'What is Arbitrum Stylus?', + ], }, }, }, @@ -223,13 +246,72 @@ const config = { logo: { alt: 'Arbitrum Logo', src: 'img/logo.svg', - href: '/welcome/arbitrum-gentle-introduction', + href: '/get-started', }, - items: [], + items: [ + { + type: 'docSidebar', + sidebarId: 'getStartedSidebar', + position: 'right', + label: 'Get started', + }, + { + type: 'dropdown', + label: 'Build apps', + position: 'right', + items: [ + { + label: 'Quickstart', + to: '/build-decentralized-apps/quickstart-solidity-remix', + }, + { + label: 'Stylus', + to: '/stylus/gentle-introduction', + }, + ], + }, + { + type: 'docSidebar', + sidebarId: 'runArbitrumChainSidebar', + position: 'right', + label: 'Launch a chain', + }, + // { + // type: 'docSidebar', + // sidebarId: 'stylusSidebar', + // position: 'right', + // label: 'Use Stylus', + // }, + { + type: 'docSidebar', + sidebarId: 'runNodeSidebar', + position: 'right', + label: 'Run a node', + }, + { + type: 'docSidebar', + sidebarId: 'bridgeSidebar', + position: 'right', + label: 'Use the bridge', + }, + { + type: 'docSidebar', + sidebarId: 'howItWorksSidebar', + position: 'right', + label: 'How it works', + }, + { + type: 'docSidebar', + sidebarId: 'additionalResourcesSidebar', + position: 'right', + label: 'Resources', + }, + ], }, footer: { style: 'dark', links: [ + {}, { items: [ { @@ -331,8 +413,6 @@ const config = { }, prism: { additionalLanguages: ['solidity', 'rust', 'bash', 'toml'], - theme: require('prism-react-renderer/themes/github'), - darkTheme: require('prism-react-renderer/themes/palenight'), }, liveCodeBlock: { /** @@ -357,4 +437,23 @@ const config = { }), }; +// HACK +// this was originally included above +// it broke local builds on Windows, not sure why yet. Works fine on Mac +// `generate_sdk_docs` runs fine, no difference in outputs between environments, so it's not easy to debug - low pri +const isRunningLocally = process.env.NODE_ENV === 'development'; +const isRunningOnWindows = process.platform === 'win32'; +if (isRunningLocally && isRunningOnWindows) { + config.plugins = config.plugins.filter((plugin) => { + if (Array.isArray(plugin) && plugin[0] === '@docusaurus/plugin-content-docs') { + return false; // remove the offending plugin config + } + return true; // keep everything else + }); +} else { + // another hack for another strange windows-specific issue, reproduceable through clean clone of repo + config.themeConfig.prism.theme = require('prism-react-renderer/themes/github'); + config.themeConfig.prism.darkTheme = require('prism-react-renderer/themes/palenight'); +} + module.exports = config; diff --git a/scripts/build-glossary.ts b/scripts/build-glossary.ts index 1ad66fd987..f2dbb1b759 100644 --- a/scripts/build-glossary.ts +++ b/scripts/build-glossary.ts @@ -22,7 +22,7 @@ function escapeForJSON(str: string): string { .replace(/"/g, '\\"') .replace(/\n/g, '\\n') .replace(/\r/g, '\\r') - .replace(/\t/g, '\\t'); + .replace(/\t/g, '\\t') } /** @@ -127,9 +127,7 @@ async function main(): Promise { // Generate import statements for each term (unused in current implementation) // This could be used if implementing a React component approach to term rendering let imports = terms - .map( - (item) => `import ${renderKey(item.data.key)} from './docs/glossary/${item.data.key}.mdx';`, - ) + .map((item) => `import ${renderKey(item.data.key)} from './docs/glossary/${item.data.key}.mdx';`) .join('\n'); // Generate component references for each term (unused in current implementation) @@ -137,24 +135,11 @@ async function main(): Promise { // Generate and write the consolidated glossary partial MDX file // This creates a single file with all terms formatted as Markdown headings - const currentDate = new Date().toISOString().split('T')[0]; // Format: YYYY-MM-DD - const frontmatter = `--- -partial_type: glossary -title: "Arbitrum Glossary Definitions" -description: "Comprehensive glossary of Arbitrum terminology and definitions" -author: anegg0 -last_reviewed: ${currentDate} ---- - -`; - await fs.writeFile( './docs/partials/_glossary-partial.mdx', - frontmatter + - terms - .map((item) => `### ${item.data.title} {#${item.data.key}}\n\n${item.content.trim()}`) - .join('\n\n') + - '\n', + terms + .map((item) => `### ${item.data.title} {#${item.data.key}}\n\n${item.content.trim()}`) + .join('\n\n') + '\n', ); // Generate and write the JSON glossary file for client-side usage diff --git a/scripts/sync-stylus-content.js b/scripts/sync-stylus-content.js index 504cfd29a6..3075e2c84d 100644 --- a/scripts/sync-stylus-content.js +++ b/scripts/sync-stylus-content.js @@ -1,671 +1,278 @@ #!/usr/bin/env node /** - * @fileoverview Sync content from stylus-by-example submodule to docs directory - * @description Replaces the complex 897-line stylusByExampleDocsHandler.ts with a maintainable solution - * @author sync-stylus-content.js - * @version 2.0.0 (refactored for maintainability) + * Simple script to sync content from stylus-by-example submodule to docs directory + * Replaces the complex 897-line stylusByExampleDocsHandler.ts */ const fs = require('fs-extra'); const path = require('path'); const glob = require('glob'); -// ============================================================================ -// CONFIGURATION - All constants and settings in one place -// ============================================================================ +// Configuration +const SOURCE_DIR = path.join(__dirname, '../submodules/stylus-by-example/src/app'); +const TARGET_DIR = path.join(__dirname, '../docs/stylus-by-example'); +const DONT_EDIT_MARKER = 'DONT-EDIT-THIS-FOLDER'; /** - * @typedef {Object} SyncConfig - * @property {Object} paths - File and directory paths - * @property {Object} content - Content processing settings - * @property {Object} allowLists - File filtering configurations - * @property {Object} output - Output generation settings + * Convert Next.js style metadata export to Docusaurus frontmatter */ - -/** @type {SyncConfig} */ -const CONFIG = { - paths: { - sourceDir: path.join(__dirname, '../submodules/stylus-by-example/src/app'), - targetDir: path.join(__dirname, '../docs/stylus-by-example'), - dontEditMarker: 'DONT-EDIT-THIS-FOLDER', - sourcePattern: '**/page.mdx', - sidebarFileName: 'sidebar.js', - }, - - content: { - fileExtensions: { - source: '.mdx', - target: '.mdx', - sidebar: '.js', - }, - markers: { - contentBegin: '{/* Begin Content */}', - frontmatterDelimiter: '---', - }, - patterns: { - metadata: /export\s+const\s+metadata\s*=\s*({[\s\S]*?});/, - relativeLink: /\[([^\]]+)\]\(\.\/([\w_]+)\)/g, - }, - }, - - allowLists: { - basicExamples: [ - 'hello_world', - 'primitive_data_types', - 'variables', - 'constants', - 'function', - 'errors', - 'events', - 'inheritance', - 'vm_affordances', - 'sending_ether', - 'function_selector', - 'abi_encode', - 'abi_decode', - 'hashing', - 'bytes_in_bytes_out', - ], - applications: ['erc20', 'erc721', 'vending_machine', 'multi_call'], - }, - - output: { - sections: ['basic_examples', 'applications'], - indexFileName: 'index.mdx', - sidebarTemplate: { - fileHeader: '// @ts-check', - docString: 'Autogenerated sidebar configuration for Stylus by Example', - typeAnnotation: "/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */", - }, - }, -}; - -// ============================================================================ -// UTILITY FUNCTIONS - Reusable helper functions -// ============================================================================ - -/** - * Creates a standardized error with context information - * @param {string} message - Error message - * @param {string} context - Additional context (file path, operation, etc.) - * @param {Error} [originalError] - Original error object if available - * @returns {Error} Enhanced error object - */ -function createError(message, context, originalError = null) { - const errorMessage = `${message} (Context: ${context})`; - const error = new Error(errorMessage); - error.context = context; - error.originalError = originalError; - return error; -} - -/** - * Logs messages with consistent formatting and timestamps - * @param {string} level - Log level (info, warn, error, success) - * @param {string} message - Message to log - * @param {Object} [details] - Additional details to log - */ -function log(level, message, details = null) { - const timestamp = new Date().toISOString(); - const emoji = - { - info: '🔄', - warn: '⚠️', - error: '❌', - success: '✅', - clean: '🧹', - file: '📁', - write: '📝', - }[level] || 'ℹ️'; - - console.log(`${emoji} ${message}`); - - if (details) { - console.log(` Details: ${JSON.stringify(details, null, 2)}`); +function convertMetadataToFrontmatter(content) { + // Pattern to match: export const metadata = { ... } + const metadataPattern = /export\s+const\s+metadata\s*=\s*({[\s\S]*?});/; + const match = content.match(metadataPattern); + + if (!match) { + return content; } -} - -/** - * Converts snake_case string to Title Case - * @param {string} snakeCaseStr - String in snake_case format - * @returns {string} String in Title Case format - */ -function snakeCaseToTitleCase(snakeCaseStr) { - return snakeCaseStr - .split('_') - .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) - .join(' '); -} - -/** - * Safely evaluates a JavaScript object literal string - * @param {string} objectStr - String representation of JavaScript object - * @returns {Object} Parsed object - * @throws {Error} If evaluation fails - */ -function safeEvalObjectLiteral(objectStr) { + try { + // Extract and parse the metadata object + const metadataStr = match[1]; // Use Function constructor to safely evaluate the object literal - return new Function(`return ${objectStr}`)(); + const metadata = new Function(`return ${metadataStr}`)(); + + // Convert to YAML frontmatter + const frontmatter = Object.entries(metadata) + .map(([key, value]) => { + // Handle different value types + if (typeof value === 'string') { + // Escape single quotes in strings + return `${key}: '${value.replace(/'/g, "''")}'`; + } + return `${key}: ${value}`; + }) + .join('\n'); + + // Replace the export with frontmatter + return content.replace(metadataPattern, `---\n${frontmatter}\n---`); } catch (error) { - throw createError('Failed to evaluate object literal', objectStr, error); - } -} - -/** - * Escapes single quotes in strings for YAML frontmatter - * @param {string} str - String to escape - * @returns {string} Escaped string - */ -function escapeYamlString(str) { - return str.replace(/'/g, "''"); -} - -/** - * Generates a timestamp string for file headers - * @returns {string} ISO timestamp string - */ -function generateTimestamp() { - return new Date().toISOString(); -} - -// ============================================================================ -// PATH AND FILE UTILITIES - File system operations and path manipulation -// ============================================================================ - -/** - * Extracts section name from a file path relative to source directory - * @param {string} filePath - Full file path - * @returns {string|null} Section name ('basic_examples' or 'applications') or null - */ -function extractSectionFromPath(filePath) { - const relativePath = path.relative(CONFIG.paths.sourceDir, filePath); - const segments = relativePath.split(path.sep); - - return segments.length >= 2 ? segments[0] : null; -} - -/** - * Extracts the item name (directory name) from a file path - * @param {string} filePath - Full file path to page.mdx - * @returns {string} Directory name containing the file - */ -function extractItemNameFromPath(filePath) { - return path.basename(path.dirname(filePath)); -} - -/** - * Gets the appropriate allowlist for a section - * @param {string} sectionName - Section name ('basic_examples' or 'applications') - * @returns {string[]} Array of allowed file names - */ -function getAllowListForSection(sectionName) { - const allowListMap = { - basic_examples: CONFIG.allowLists.basicExamples, - applications: CONFIG.allowLists.applications, - }; - - return allowListMap[sectionName] || []; -} - -/** - * Determines if a file should be processed based on allowlists - * @param {string} filePath - Full path to the file - * @returns {boolean} True if file should be processed - */ -function isFileAllowed(filePath) { - const sectionName = extractSectionFromPath(filePath); - if (!sectionName) return false; - - const itemName = extractItemNameFromPath(filePath); - const allowList = getAllowListForSection(sectionName); - - return allowList.includes(itemName); -} - -/** - * Generates target file path from source file path - * @param {string} sourceFile - Source file path - * @returns {Object} Object with targetPath and metadata - */ -function generateTargetPath(sourceFile) { - const relativePath = path.relative(CONFIG.paths.sourceDir, sourceFile); - const targetDir = path.dirname(relativePath); - - let targetFileName; - let finalTargetDir; - - if (targetDir === '.') { - // Root level file - targetFileName = CONFIG.output.indexFileName; - finalTargetDir = CONFIG.paths.targetDir; - } else { - // Section subdirectory file - const pathParts = targetDir.split(path.sep); - const sectionName = pathParts[0]; - const itemName = pathParts[1]; - - targetFileName = `${itemName}${CONFIG.content.fileExtensions.target}`; - finalTargetDir = path.join(CONFIG.paths.targetDir, sectionName); + console.warn(`Warning: Could not convert metadata: ${error.message}`); + return content; } - - return { - targetPath: path.join(finalTargetDir, targetFileName), - targetDir: finalTargetDir, - fileName: targetFileName, - }; } -// ============================================================================ -// CONTENT TRANSFORMATION - Text processing and content manipulation -// ============================================================================ - /** - * Converts a metadata object to YAML frontmatter format - * @param {Object} metadata - Metadata object to convert - * @returns {string} YAML frontmatter string + * Transform relative links to work with Docusaurus structure */ -function convertMetadataToYaml(metadata) { - const yamlLines = Object.entries(metadata).map(([key, value]) => { - if (typeof value === 'string') { - return `${key}: '${escapeYamlString(value)}'`; +function fixRelativeLinks(content) { + // Fix relative links that point to sibling directories + // When file name matches directory name, Docusaurus simplifies the URL + // Pattern: [text](./directory_name) should become [text](/stylus-by-example/basic_examples/directory_name) + content = content.replace( + /\[([^\]]+)\]\(\.\/([\w_]+)\)/g, + (match, linkText, dirName) => { + // Convert to absolute Docusaurus path (simplified URL when filename matches directory) + return `[${linkText}](/stylus-by-example/basic_examples/${dirName})`; } - return `${key}: ${value}`; - }); - - return yamlLines.join('\n'); + ); + + return content; } /** - * Transforms Next.js metadata export to Docusaurus frontmatter - * @param {string} content - File content with metadata export - * @returns {string} Content with frontmatter instead of metadata export + * Process and copy a single MDX file */ -function convertMetadataToFrontmatter(content) { - const match = content.match(CONFIG.content.patterns.metadata); - if (!match) return content; - +async function processFile(sourcePath, targetPath) { try { - const metadataStr = match[1]; - const metadata = safeEvalObjectLiteral(metadataStr); - const frontmatter = convertMetadataToYaml(metadata); - - const delimiter = CONFIG.content.markers.frontmatterDelimiter; - const replacement = `${delimiter}\n${frontmatter}\n${delimiter}`; - - return content.replace(CONFIG.content.patterns.metadata, replacement); + // Read source file + let content = await fs.readFile(sourcePath, 'utf8'); + + // Transform metadata to frontmatter + content = convertMetadataToFrontmatter(content); + + // Fix relative links + content = fixRelativeLinks(content); + + // Add content begin marker if not present + if (!content.includes('{/* Begin Content */}')) { + // Find the position after frontmatter + const frontmatterEnd = content.indexOf('---', 3); + if (frontmatterEnd !== -1) { + const insertPos = content.indexOf('\n', frontmatterEnd + 3) + 1; + content = content.slice(0, insertPos) + '\n{/* Begin Content */}\n' + content.slice(insertPos); + } + } + + // Ensure target directory exists + await fs.ensureDir(path.dirname(targetPath)); + + // Write transformed content + await fs.writeFile(targetPath, content); + + return true; } catch (error) { - log('warn', `Could not convert metadata: ${error.message}`); - return content; + console.error(`Error processing ${sourcePath}: ${error.message}`); + return false; } } /** - * Fixes relative links to work with Docusaurus URL structure - * @param {string} content - Content with relative links - * @returns {string} Content with fixed absolute links - */ -function fixRelativeLinks(content) { - return content.replace(CONFIG.content.patterns.relativeLink, (match, linkText, dirName) => { - // Convert to absolute Docusaurus path - return `[${linkText}](/stylus-by-example/basic_examples/${dirName})`; - }); -} - -/** - * Adds content begin marker if not already present - * @param {string} content - File content - * @returns {string} Content with begin marker + * Generate sidebar configuration for a directory */ -function addContentBeginMarker(content) { - const marker = CONFIG.content.markers.contentBegin; - if (content.includes(marker)) return content; - - const delimiter = CONFIG.content.markers.frontmatterDelimiter; - const frontmatterEnd = content.indexOf(delimiter, 3); - - if (frontmatterEnd === -1) return content; - - const insertPos = content.indexOf('\n', frontmatterEnd + 3) + 1; - return content.slice(0, insertPos) + `\n${marker}\n` + content.slice(insertPos); -} - -/** - * Adds "not for production" banner import and component two lines before first rust code block - * @param {string} content - File content - * @returns {string} Content with banner added - */ -function addNotForProductionBanner(content) { - const firstCodeBlock = '```rust'; - const bannerCode = ` -import NotForProductionBannerPartial from '../../partials/_not-for-production-banner-partial.mdx'; - - -`; - - // Find the first occurrence of ```rust code block - const index = content.indexOf(firstCodeBlock); - if (index === -1) { - // No rust code block found, return content unchanged - return content; - } - - // Find the position two lines before the first code block - const lines = content.substring(0, index).split('\n'); - const insertLineIndex = lines.length - 2; - - // Insert the banner at the calculated position - lines.splice(insertLineIndex, 0, bannerCode); - - // Reconstruct the content with the banner inserted - const newContent = lines.join('\n') + content.substring(index); - return newContent; -} - -/** - * Applies all content transformations to a file - * @param {string} content - Original file content - * @returns {string} Transformed content - */ -function transformContent(content) { - let transformedContent = content; - - // Apply transformations in sequence - transformedContent = convertMetadataToFrontmatter(transformedContent); - transformedContent = fixRelativeLinks(transformedContent); - transformedContent = addContentBeginMarker(transformedContent); - transformedContent = addNotForProductionBanner(transformedContent); - - return transformedContent; -} - -// ============================================================================ -// SIDEBAR GENERATION - Sidebar configuration file generation -// ============================================================================ - -/** - * Creates a sidebar item object for a file - * @param {string} sectionName - Section name (basic_examples, applications) - * @param {string} fileName - File name without extension - * @returns {Object} Sidebar item configuration - */ -function createSidebarItem(sectionName, fileName) { - return { - type: 'doc', - id: `stylus-by-example/${sectionName}/${fileName}`, - label: snakeCaseToTitleCase(fileName), - }; -} - -/** - * Generates the complete sidebar JavaScript file content - * @param {string} sectionName - Section name for the sidebar - * @param {Object[]} sidebarItems - Array of sidebar item objects - * @returns {string} Complete sidebar file content - */ -function generateSidebarFileContent(sectionName, sidebarItems) { - const template = CONFIG.output.sidebarTemplate; - const timestamp = generateTimestamp(); - - return `${template.fileHeader} +function generateSidebar(sectionName, files) { + const items = files + .map(file => { + // Get the relative path from the section directory + const relativePath = path.relative(path.join(TARGET_DIR, sectionName), file); + const fileName = path.basename(file, '.mdx'); + const dirName = path.dirname(relativePath); + + // Skip if filename is same as directory (already processed) + if (fileName === sectionName) { + return null; + } + + // Convert filename to label (snake_case to Title Case) + const label = fileName + .split('_') + .map(word => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); + + // Generate doc ID based on actual file structure + // For files in subdirectories: stylus-by-example/applications/erc20/erc20 + const docId = dirName === '.' + ? `stylus-by-example/${sectionName}/${fileName}` + : `stylus-by-example/${sectionName}/${dirName}/${fileName}`; + + return { + type: 'doc', + label: label, + id: docId + }; + }) + .filter(Boolean); + + return `// @ts-check /** - * @fileoverview ${template.docString} ${sectionName} + * @fileoverview Autogenerated sidebar configuration for Stylus by Example * @description This file is automatically generated by sync-stylus-content.js - * @generated ${timestamp} + * @generated ${new Date().toISOString()} * @see https://docusaurus.io/docs/sidebar * @see https://docusaurus.io/docs/sidebar/items */ -${template.typeAnnotation} -const sidebar = ${JSON.stringify({ items: sidebarItems }, null, 2)}; - -module.exports = sidebar.items; +/** @type {import('@docusaurus/plugin-content-docs').SidebarItem[]} */ +module.exports = ${JSON.stringify(items, null, 2)}; `; } /** - * Generates sidebar configuration for a section directory - * @param {string} sectionName - Section name (basic_examples, applications) - * @param {string[]} files - Array of file paths - * @returns {string} Complete sidebar JavaScript file content - */ -function generateSidebar(sectionName, files) { - const allowList = getAllowListForSection(sectionName); - const itemsByFileName = {}; - - // Collect all items by filename, skipping index files - files.forEach((file) => { - const fileName = path.basename(file, CONFIG.content.fileExtensions.source); - - // Skip index files (filename matches section name) - if (fileName === sectionName) return; - - const item = createSidebarItem(sectionName, fileName); - itemsByFileName[fileName] = item; - }); - - // Order items according to allowlist - const orderedItems = allowList - .map((allowedFileName) => itemsByFileName[allowedFileName]) - .filter(Boolean); // Remove undefined items - - return generateSidebarFileContent(sectionName, orderedItems); -} - -// ============================================================================ -// FILE OPERATIONS - High-level file processing functions -// ============================================================================ - -/** - * Processes and copies a single MDX file with transformations - * @param {string} sourcePath - Source file path - * @param {string} targetPath - Target file path - * @returns {Promise} True if processing succeeded + * Main sync function */ -async function processFile(sourcePath, targetPath) { +async function syncContent() { + console.log('🔄 Syncing Stylus by Example content...'); + try { - // Read and transform content - const content = await fs.readFile(sourcePath, 'utf8'); - const transformedContent = transformContent(content); - - // Ensure target directory exists and write file - await fs.ensureDir(path.dirname(targetPath)); - await fs.writeFile(targetPath, transformedContent); - - return true; - } catch (error) { - log('error', `Failed to process file: ${sourcePath}`, { - error: error.message, - targetPath, - }); - return false; - } -} - -/** - * Creates the "don't edit" marker file in target directory - * @param {string} targetDir - Target directory path - * @param {boolean} isSubmoduleAvailable - Whether submodule is available - * @returns {Promise} - */ -async function createDontEditMarker(targetDir, isSubmoduleAvailable = true) { - const timestamp = generateTimestamp(); - const markerPath = path.join(targetDir, CONFIG.paths.dontEditMarker); - - const content = isSubmoduleAvailable - ? `This folder is auto-generated from submodules/stylus-by-example -Do not edit files directly here. Edit them in the submodule instead. -Generated: ${timestamp} -` - : `This folder would contain auto-generated content from submodules/stylus-by-example + // Check if source exists + if (!await fs.pathExists(SOURCE_DIR)) { + console.warn(`⚠️ Source directory not found: ${SOURCE_DIR}`); + console.log('Skipping Stylus by Example content sync.'); + console.log('To include this content, initialize the submodule:'); + console.log(' git submodule update --init --recursive'); + + // Create empty target directory with marker + await fs.ensureDir(TARGET_DIR); + await fs.writeFile( + path.join(TARGET_DIR, DONT_EDIT_MARKER), + `This folder would contain auto-generated content from submodules/stylus-by-example The submodule was not available during build. -Generated: ${timestamp} -`; - - await fs.writeFile(markerPath, content); -} - -/** - * Creates empty sidebar files for sections when submodule is unavailable - * @param {string} targetDir - Target directory path - * @returns {Promise} - */ -async function createEmptySidebars(targetDir) { - const timestamp = generateTimestamp(); - const template = CONFIG.output.sidebarTemplate; - - const emptySidebarContent = `${template.fileHeader} +Generated: ${new Date().toISOString()} +` + ); + + // Create empty sidebar files for the expected directories + const expectedDirs = ['basic_examples', 'applications']; + for (const dir of expectedDirs) { + await fs.ensureDir(path.join(TARGET_DIR, dir)); + const emptySidebar = `// @ts-check /** * @fileoverview Empty sidebar configuration (submodule not available) - * @generated ${timestamp} + * @generated ${new Date().toISOString()} */ -${template.typeAnnotation} -const sidebar = { - items: [] -}; - -module.exports = sidebar.items; +/** @type {import('@docusaurus/plugin-content-docs').SidebarItem[]} */ +module.exports = []; `; - - for (const sectionName of CONFIG.output.sections) { - const sectionDir = path.join(targetDir, sectionName); - await fs.ensureDir(sectionDir); - - const sidebarPath = path.join(sectionDir, CONFIG.paths.sidebarFileName); - await fs.writeFile(sidebarPath, emptySidebarContent); - } -} - -/** - * Generates sidebar files for all processed sections - * @param {string} targetDir - Target directory path - * @returns {Promise} - */ -async function generateAllSidebars(targetDir) { - for (const sectionName of CONFIG.output.sections) { - const sectionDir = path.join(targetDir, sectionName); - - if (await fs.pathExists(sectionDir)) { - const pattern = path.join(sectionDir, `**/*${CONFIG.content.fileExtensions.source}`); - const sectionFiles = glob.sync(pattern); - - const sidebarContent = generateSidebar(sectionName, sectionFiles); - const sidebarPath = path.join(sectionDir, CONFIG.paths.sidebarFileName); - - await fs.writeFile(sidebarPath, sidebarContent); - log('write', `Generated sidebar for ${sectionName}`, { - itemCount: sectionFiles.length, - }); - } - } -} - -// ============================================================================ -// MAIN SYNC LOGIC - Core synchronization workflow -// ============================================================================ - -/** - * Handles the case when the source submodule is not available - * @returns {Promise} - */ -async function handleMissingSubmodule() { - log('warn', `Source directory not found: ${CONFIG.paths.sourceDir}`); - log('info', 'Skipping Stylus by Example content sync.'); - log('info', 'To include this content, initialize the submodule:'); - log('info', ' git submodule update --init --recursive'); - - // Create empty structure - await fs.ensureDir(CONFIG.paths.targetDir); - await createDontEditMarker(CONFIG.paths.targetDir, false); - await createEmptySidebars(CONFIG.paths.targetDir); - - log('success', 'Created empty Stylus by Example structure'); -} - -/** - * Processes all allowed files from source to target directory - * @param {string[]} allowedFiles - Array of file paths to process - * @returns {Promise} Number of successfully processed files - */ -async function processAllFiles(allowedFiles) { - let successCount = 0; - - for (const sourceFile of allowedFiles) { - const { targetPath } = generateTargetPath(sourceFile); - - if (await processFile(sourceFile, targetPath)) { - successCount++; - } - } - - return successCount; -} - -/** - * Main synchronization function - * @returns {Promise} - */ -async function syncContent() { - log('info', 'Syncing Stylus by Example content...'); - - try { - // Check if source exists - if (!(await fs.pathExists(CONFIG.paths.sourceDir))) { - await handleMissingSubmodule(); + await fs.writeFile(path.join(TARGET_DIR, dir, 'sidebar.js'), emptySidebar); + } + + console.log('✅ Created empty Stylus by Example structure'); return; } - - // Clean and recreate target directory - if (await fs.pathExists(CONFIG.paths.targetDir)) { - log('clean', 'Cleaning target directory...'); - await fs.remove(CONFIG.paths.targetDir); + + // Clean target directory + if (await fs.pathExists(TARGET_DIR)) { + console.log('🧹 Cleaning target directory...'); + await fs.remove(TARGET_DIR); } - - await fs.ensureDir(CONFIG.paths.targetDir); - await createDontEditMarker(CONFIG.paths.targetDir, true); - - // Find and filter files - const sourcePattern = path.join(CONFIG.paths.sourceDir, CONFIG.paths.sourcePattern); - const allFiles = glob.sync(sourcePattern); - const allowedFiles = allFiles.filter(isFileAllowed); - - log( - 'file', - `Found ${allFiles.length} total files, ${allowedFiles.length} allowed files to process`, + + // Create target directory + await fs.ensureDir(TARGET_DIR); + + // Add marker file + await fs.writeFile( + path.join(TARGET_DIR, DONT_EDIT_MARKER), + `This folder is auto-generated from submodules/stylus-by-example +Do not edit files directly here. Edit them in the submodule instead. +Generated: ${new Date().toISOString()} +` ); - - // Process all files - const successCount = await processAllFiles(allowedFiles); - - // Generate sidebars - await generateAllSidebars(CONFIG.paths.targetDir); - - log('success', `Successfully synced ${successCount}/${allowedFiles.length} files`); + + // Find all page.mdx files + const pattern = path.join(SOURCE_DIR, '**/page.mdx'); + const files = glob.sync(pattern); + + console.log(`📁 Found ${files.length} files to process`); + + // Process each file + let successCount = 0; + for (const sourceFile of files) { + // Calculate target path + const relativePath = path.relative(SOURCE_DIR, sourceFile); + const targetDir = path.dirname(relativePath); + + // Rename page.mdx to directory name or keep as index + let targetFileName; + if (targetDir === '.') { + targetFileName = 'index.mdx'; + } else { + // Use the parent directory name as the filename + const parentDir = path.basename(targetDir); + targetFileName = `${parentDir}.mdx`; + } + + const targetFile = path.join(TARGET_DIR, targetDir, targetFileName); + + // Process and copy file + if (await processFile(sourceFile, targetFile)) { + successCount++; + } + } + + // Generate sidebars for each directory + const directories = ['basic_examples', 'applications']; + for (const dir of directories) { + const dirPath = path.join(TARGET_DIR, dir); + if (await fs.pathExists(dirPath)) { + const dirFiles = glob.sync(path.join(dirPath, '**/*.mdx')); + const sidebarContent = generateSidebar(dir, dirFiles); + await fs.writeFile(path.join(dirPath, 'sidebar.js'), sidebarContent); + console.log(`📝 Generated sidebar for ${dir} (${dirFiles.length} items)`); + } + } + + console.log(`✅ Successfully synced ${successCount}/${files.length} files`); + } catch (error) { - log('error', `Sync failed: ${error.message}`, { - stack: error.stack, - context: error.context || 'Unknown', - }); + console.error(`❌ Sync failed: ${error.message}`); process.exit(1); } } -// ============================================================================ -// MODULE EXPORTS AND EXECUTION -// ============================================================================ - // Run if called directly if (require.main === module) { syncContent(); } -module.exports = { - syncContent, - // Export utilities for testing - CONFIG, - isFileAllowed, - transformContent, - generateSidebar, -}; +module.exports = { syncContent }; \ No newline at end of file From 73ecfb4760da825da826687f9f28db7367277672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Wed, 1 Oct 2025 11:22:58 -0700 Subject: [PATCH 35/62] reinclude navbar in docusaurus.config.js --- docusaurus.config.js | 153 ++++++++++++++++--------------------------- 1 file changed, 56 insertions(+), 97 deletions(-) diff --git a/docusaurus.config.js b/docusaurus.config.js index 753ea5e85f..bdedaae771 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -4,11 +4,40 @@ require('dotenv').config(); const markdownPreprocessor = require('./scripts/markdown-preprocessor'); -const sdkSidebarGenerator = require('./scripts/sdk-sidebar-generator'); const sdkCodebasePath = './submodules/arbitrum-sdk'; import remarkMath from 'remark-math'; import rehypeKatex from 'rehype-katex'; +// Shared Inkeep configuration +const inkeepBaseSettings = { + apiKey: process.env.INKEEP_API_KEY, + primaryBrandColor: '#213147', + organizationDisplayName: 'Arbitrum', + theme: { + syntaxHighlighter: { + lightTheme: require('prism-react-renderer/themes/github'), + darkTheme: require('prism-react-renderer/themes/palenight'), + }, + }, +}; + +const inkeepModalSettings = { + placeholder: 'Search documentation...', + defaultQuery: '', + maxResults: 40, + debounceTimeMs: 300, + shouldOpenLinksInNewTab: true, +}; + +const inkeepExampleQuestions = [ + 'How to estimate gas in Arbitrum?', + 'What is the difference between Arbitrum One and Nova?', + 'How to deploy a smart contract on Arbitrum?', + 'What are Arbitrum Orbit chains?', + 'How does Arbitrum handle L1 to L2 messaging?', + 'What is Arbitrum Stylus?', +]; + /** @type {import('@docusaurus/types').Config} */ const config = { title: 'Arbitrum Docs', @@ -21,6 +50,22 @@ const config = { markdown: { mermaid: true, preprocessor: markdownPreprocessor, + parseFrontMatter: async (params) => { + // Use the default parser + const result = await params.defaultParseFrontMatter(params); + + // Check if this is a partial file (starts with underscore) + const fileName = params.filePath.split('/').pop(); + const isPartialFile = fileName && fileName.startsWith('_'); + + // For partial files, clear frontmatter to prevent Docusaurus warnings + // The documentation-graph tool reads raw files directly, so this doesn't affect analysis + if (isPartialFile) { + result.frontMatter = {}; + } + + return result; + }, }, themes: ['@docusaurus/theme-mermaid', '@docusaurus/theme-live-codeblock'], // GitHub pages deployment config. @@ -42,10 +87,6 @@ const config = { integrity: 'sha384-odtC+0UGzzFL/6PNoE8rX/SPcQDXBJ+uRepguP4QkPCm2LBxH3FA3y+fKSiJ+AmM', crossorigin: 'anonymous', }, - { - href: '/css/inkeep-custom.css', - type: 'text/css', - }, ], presets: [ [ @@ -53,7 +94,6 @@ const config = { /** @type {import('@docusaurus/preset-classic').Options} */ ({ docs: { - // path: './docs', exclude: ['**/api/**'], remarkPlugins: [remarkMath], rehypePlugins: [rehypeKatex], @@ -132,85 +172,22 @@ const config = { '@inkeep/cxkit-docusaurus', { SearchBar: { - baseSettings: { - apiKey: process.env.INKEEP_API_KEY, - primaryBrandColor: '#213147', // Arbitrum's primary brand color - organizationDisplayName: 'Arbitrum', - theme: { - syntaxHighlighter: { - lightTheme: require('prism-react-renderer/themes/github'), - darkTheme: require('prism-react-renderer/themes/palenight'), - }, - components: { - SearchBarTrigger: { - // Configure responsive sizing behavior - defaultSize: 'medium', - minWidth: '180px', - maxWidth: '320px', - }, - }, - }, - }, - modalSettings: { - placeholder: 'Search documentation...', - defaultQuery: '', - maxResults: 40, - debounceTimeMs: 300, - shouldOpenLinksInNewTab: true, - }, - searchSettings: { - // optional settings - }, + baseSettings: inkeepBaseSettings, + modalSettings: inkeepModalSettings, aiChatSettings: { - aiAssistantAvatar: '/img/logo.svg', // Using Arbitrum logo as AI assistant avatar - exampleQuestions: [ - 'How to estimate gas in Arbitrum?', - 'What is the difference between Arbitrum One and Nova?', - 'How to deploy a smart contract on Arbitrum?', - 'What are Arbitrum Orbit chains?', - 'How does Arbitrum handle L1 to L2 messaging?', - 'What is Arbitrum Stylus?', - ], + aiAssistantAvatar: '/img/logo.svg', + exampleQuestions: inkeepExampleQuestions, botName: 'Arbitrum Assistant', getStartedMessage: "Hi! I'm here to help you navigate Arbitrum documentation. Ask me anything about building on Arbitrum, deploying contracts, or understanding our technology.", }, }, ChatButton: { - baseSettings: { - // see https://docusaurus.io/docs/deployment#using-environment-variables to use docusaurus environment variables - apiKey: process.env.INKEEP_API_KEY, - primaryBrandColor: '#213147', // Arbitrum's primary brand color - organizationDisplayName: 'Arbitrum', - // ...optional settings - theme: { - syntaxHighlighter: { - lightTheme: require('prism-react-renderer/themes/github'), - darkTheme: require('prism-react-renderer/themes/palenight'), - }, - }, - }, - modalSettings: { - placeholder: 'Search documentation...', - defaultQuery: '', - maxResults: 40, - debounceTimeMs: 300, - shouldOpenLinksInNewTab: true, - }, - searchSettings: { - // optional settings - }, + baseSettings: inkeepBaseSettings, + modalSettings: inkeepModalSettings, aiChatSettings: { - // optional settings - aiAssistantAvatar: '/img/logo.svg', // optional -- use your own AI assistant avatar - exampleQuestions: [ - 'How to estimate gas in Arbitrum?', - 'What is the difference between Arbitrum One and Nova?', - 'How to deploy a smart contract on Arbitrum?', - 'What are Arbitrum Orbit chains?', - 'How does Arbitrum handle L1 to L2 messaging?', - 'What is Arbitrum Stylus?', - ], + aiAssistantAvatar: '/img/logo.svg', + exampleQuestions: inkeepExampleQuestions, }, }, }, @@ -311,7 +288,6 @@ const config = { footer: { style: 'dark', links: [ - {}, { items: [ { @@ -413,6 +389,8 @@ const config = { }, prism: { additionalLanguages: ['solidity', 'rust', 'bash', 'toml'], + theme: require('prism-react-renderer/themes/github'), + darkTheme: require('prism-react-renderer/themes/palenight'), }, liveCodeBlock: { /** @@ -437,23 +415,4 @@ const config = { }), }; -// HACK -// this was originally included above -// it broke local builds on Windows, not sure why yet. Works fine on Mac -// `generate_sdk_docs` runs fine, no difference in outputs between environments, so it's not easy to debug - low pri -const isRunningLocally = process.env.NODE_ENV === 'development'; -const isRunningOnWindows = process.platform === 'win32'; -if (isRunningLocally && isRunningOnWindows) { - config.plugins = config.plugins.filter((plugin) => { - if (Array.isArray(plugin) && plugin[0] === '@docusaurus/plugin-content-docs') { - return false; // remove the offending plugin config - } - return true; // keep everything else - }); -} else { - // another hack for another strange windows-specific issue, reproduceable through clean clone of repo - config.themeConfig.prism.theme = require('prism-react-renderer/themes/github'); - config.themeConfig.prism.darkTheme = require('prism-react-renderer/themes/palenight'); -} - module.exports = config; From 56d1eab02c5dbe1edef8eb320ef5e7dd1ba34759 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Wed, 1 Oct 2025 11:26:39 -0700 Subject: [PATCH 36/62] restore file to master state --- scripts/sync-stylus-content.js | 819 ++++++++++++++++++++++++--------- 1 file changed, 606 insertions(+), 213 deletions(-) diff --git a/scripts/sync-stylus-content.js b/scripts/sync-stylus-content.js index 3075e2c84d..504cfd29a6 100644 --- a/scripts/sync-stylus-content.js +++ b/scripts/sync-stylus-content.js @@ -1,278 +1,671 @@ #!/usr/bin/env node /** - * Simple script to sync content from stylus-by-example submodule to docs directory - * Replaces the complex 897-line stylusByExampleDocsHandler.ts + * @fileoverview Sync content from stylus-by-example submodule to docs directory + * @description Replaces the complex 897-line stylusByExampleDocsHandler.ts with a maintainable solution + * @author sync-stylus-content.js + * @version 2.0.0 (refactored for maintainability) */ const fs = require('fs-extra'); const path = require('path'); const glob = require('glob'); -// Configuration -const SOURCE_DIR = path.join(__dirname, '../submodules/stylus-by-example/src/app'); -const TARGET_DIR = path.join(__dirname, '../docs/stylus-by-example'); -const DONT_EDIT_MARKER = 'DONT-EDIT-THIS-FOLDER'; +// ============================================================================ +// CONFIGURATION - All constants and settings in one place +// ============================================================================ /** - * Convert Next.js style metadata export to Docusaurus frontmatter + * @typedef {Object} SyncConfig + * @property {Object} paths - File and directory paths + * @property {Object} content - Content processing settings + * @property {Object} allowLists - File filtering configurations + * @property {Object} output - Output generation settings */ -function convertMetadataToFrontmatter(content) { - // Pattern to match: export const metadata = { ... } - const metadataPattern = /export\s+const\s+metadata\s*=\s*({[\s\S]*?});/; - const match = content.match(metadataPattern); - - if (!match) { - return content; + +/** @type {SyncConfig} */ +const CONFIG = { + paths: { + sourceDir: path.join(__dirname, '../submodules/stylus-by-example/src/app'), + targetDir: path.join(__dirname, '../docs/stylus-by-example'), + dontEditMarker: 'DONT-EDIT-THIS-FOLDER', + sourcePattern: '**/page.mdx', + sidebarFileName: 'sidebar.js', + }, + + content: { + fileExtensions: { + source: '.mdx', + target: '.mdx', + sidebar: '.js', + }, + markers: { + contentBegin: '{/* Begin Content */}', + frontmatterDelimiter: '---', + }, + patterns: { + metadata: /export\s+const\s+metadata\s*=\s*({[\s\S]*?});/, + relativeLink: /\[([^\]]+)\]\(\.\/([\w_]+)\)/g, + }, + }, + + allowLists: { + basicExamples: [ + 'hello_world', + 'primitive_data_types', + 'variables', + 'constants', + 'function', + 'errors', + 'events', + 'inheritance', + 'vm_affordances', + 'sending_ether', + 'function_selector', + 'abi_encode', + 'abi_decode', + 'hashing', + 'bytes_in_bytes_out', + ], + applications: ['erc20', 'erc721', 'vending_machine', 'multi_call'], + }, + + output: { + sections: ['basic_examples', 'applications'], + indexFileName: 'index.mdx', + sidebarTemplate: { + fileHeader: '// @ts-check', + docString: 'Autogenerated sidebar configuration for Stylus by Example', + typeAnnotation: "/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */", + }, + }, +}; + +// ============================================================================ +// UTILITY FUNCTIONS - Reusable helper functions +// ============================================================================ + +/** + * Creates a standardized error with context information + * @param {string} message - Error message + * @param {string} context - Additional context (file path, operation, etc.) + * @param {Error} [originalError] - Original error object if available + * @returns {Error} Enhanced error object + */ +function createError(message, context, originalError = null) { + const errorMessage = `${message} (Context: ${context})`; + const error = new Error(errorMessage); + error.context = context; + error.originalError = originalError; + return error; +} + +/** + * Logs messages with consistent formatting and timestamps + * @param {string} level - Log level (info, warn, error, success) + * @param {string} message - Message to log + * @param {Object} [details] - Additional details to log + */ +function log(level, message, details = null) { + const timestamp = new Date().toISOString(); + const emoji = + { + info: '🔄', + warn: '⚠️', + error: '❌', + success: '✅', + clean: '🧹', + file: '📁', + write: '📝', + }[level] || 'ℹ️'; + + console.log(`${emoji} ${message}`); + + if (details) { + console.log(` Details: ${JSON.stringify(details, null, 2)}`); } - +} + +/** + * Converts snake_case string to Title Case + * @param {string} snakeCaseStr - String in snake_case format + * @returns {string} String in Title Case format + */ +function snakeCaseToTitleCase(snakeCaseStr) { + return snakeCaseStr + .split('_') + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); +} + +/** + * Safely evaluates a JavaScript object literal string + * @param {string} objectStr - String representation of JavaScript object + * @returns {Object} Parsed object + * @throws {Error} If evaluation fails + */ +function safeEvalObjectLiteral(objectStr) { try { - // Extract and parse the metadata object - const metadataStr = match[1]; // Use Function constructor to safely evaluate the object literal - const metadata = new Function(`return ${metadataStr}`)(); - - // Convert to YAML frontmatter - const frontmatter = Object.entries(metadata) - .map(([key, value]) => { - // Handle different value types - if (typeof value === 'string') { - // Escape single quotes in strings - return `${key}: '${value.replace(/'/g, "''")}'`; - } - return `${key}: ${value}`; - }) - .join('\n'); - - // Replace the export with frontmatter - return content.replace(metadataPattern, `---\n${frontmatter}\n---`); + return new Function(`return ${objectStr}`)(); } catch (error) { - console.warn(`Warning: Could not convert metadata: ${error.message}`); - return content; + throw createError('Failed to evaluate object literal', objectStr, error); } } /** - * Transform relative links to work with Docusaurus structure + * Escapes single quotes in strings for YAML frontmatter + * @param {string} str - String to escape + * @returns {string} Escaped string */ -function fixRelativeLinks(content) { - // Fix relative links that point to sibling directories - // When file name matches directory name, Docusaurus simplifies the URL - // Pattern: [text](./directory_name) should become [text](/stylus-by-example/basic_examples/directory_name) - content = content.replace( - /\[([^\]]+)\]\(\.\/([\w_]+)\)/g, - (match, linkText, dirName) => { - // Convert to absolute Docusaurus path (simplified URL when filename matches directory) - return `[${linkText}](/stylus-by-example/basic_examples/${dirName})`; +function escapeYamlString(str) { + return str.replace(/'/g, "''"); +} + +/** + * Generates a timestamp string for file headers + * @returns {string} ISO timestamp string + */ +function generateTimestamp() { + return new Date().toISOString(); +} + +// ============================================================================ +// PATH AND FILE UTILITIES - File system operations and path manipulation +// ============================================================================ + +/** + * Extracts section name from a file path relative to source directory + * @param {string} filePath - Full file path + * @returns {string|null} Section name ('basic_examples' or 'applications') or null + */ +function extractSectionFromPath(filePath) { + const relativePath = path.relative(CONFIG.paths.sourceDir, filePath); + const segments = relativePath.split(path.sep); + + return segments.length >= 2 ? segments[0] : null; +} + +/** + * Extracts the item name (directory name) from a file path + * @param {string} filePath - Full file path to page.mdx + * @returns {string} Directory name containing the file + */ +function extractItemNameFromPath(filePath) { + return path.basename(path.dirname(filePath)); +} + +/** + * Gets the appropriate allowlist for a section + * @param {string} sectionName - Section name ('basic_examples' or 'applications') + * @returns {string[]} Array of allowed file names + */ +function getAllowListForSection(sectionName) { + const allowListMap = { + basic_examples: CONFIG.allowLists.basicExamples, + applications: CONFIG.allowLists.applications, + }; + + return allowListMap[sectionName] || []; +} + +/** + * Determines if a file should be processed based on allowlists + * @param {string} filePath - Full path to the file + * @returns {boolean} True if file should be processed + */ +function isFileAllowed(filePath) { + const sectionName = extractSectionFromPath(filePath); + if (!sectionName) return false; + + const itemName = extractItemNameFromPath(filePath); + const allowList = getAllowListForSection(sectionName); + + return allowList.includes(itemName); +} + +/** + * Generates target file path from source file path + * @param {string} sourceFile - Source file path + * @returns {Object} Object with targetPath and metadata + */ +function generateTargetPath(sourceFile) { + const relativePath = path.relative(CONFIG.paths.sourceDir, sourceFile); + const targetDir = path.dirname(relativePath); + + let targetFileName; + let finalTargetDir; + + if (targetDir === '.') { + // Root level file + targetFileName = CONFIG.output.indexFileName; + finalTargetDir = CONFIG.paths.targetDir; + } else { + // Section subdirectory file + const pathParts = targetDir.split(path.sep); + const sectionName = pathParts[0]; + const itemName = pathParts[1]; + + targetFileName = `${itemName}${CONFIG.content.fileExtensions.target}`; + finalTargetDir = path.join(CONFIG.paths.targetDir, sectionName); + } + + return { + targetPath: path.join(finalTargetDir, targetFileName), + targetDir: finalTargetDir, + fileName: targetFileName, + }; +} + +// ============================================================================ +// CONTENT TRANSFORMATION - Text processing and content manipulation +// ============================================================================ + +/** + * Converts a metadata object to YAML frontmatter format + * @param {Object} metadata - Metadata object to convert + * @returns {string} YAML frontmatter string + */ +function convertMetadataToYaml(metadata) { + const yamlLines = Object.entries(metadata).map(([key, value]) => { + if (typeof value === 'string') { + return `${key}: '${escapeYamlString(value)}'`; } - ); - - return content; + return `${key}: ${value}`; + }); + + return yamlLines.join('\n'); } /** - * Process and copy a single MDX file + * Transforms Next.js metadata export to Docusaurus frontmatter + * @param {string} content - File content with metadata export + * @returns {string} Content with frontmatter instead of metadata export */ -async function processFile(sourcePath, targetPath) { +function convertMetadataToFrontmatter(content) { + const match = content.match(CONFIG.content.patterns.metadata); + if (!match) return content; + try { - // Read source file - let content = await fs.readFile(sourcePath, 'utf8'); - - // Transform metadata to frontmatter - content = convertMetadataToFrontmatter(content); - - // Fix relative links - content = fixRelativeLinks(content); - - // Add content begin marker if not present - if (!content.includes('{/* Begin Content */}')) { - // Find the position after frontmatter - const frontmatterEnd = content.indexOf('---', 3); - if (frontmatterEnd !== -1) { - const insertPos = content.indexOf('\n', frontmatterEnd + 3) + 1; - content = content.slice(0, insertPos) + '\n{/* Begin Content */}\n' + content.slice(insertPos); - } - } - - // Ensure target directory exists - await fs.ensureDir(path.dirname(targetPath)); - - // Write transformed content - await fs.writeFile(targetPath, content); - - return true; + const metadataStr = match[1]; + const metadata = safeEvalObjectLiteral(metadataStr); + const frontmatter = convertMetadataToYaml(metadata); + + const delimiter = CONFIG.content.markers.frontmatterDelimiter; + const replacement = `${delimiter}\n${frontmatter}\n${delimiter}`; + + return content.replace(CONFIG.content.patterns.metadata, replacement); } catch (error) { - console.error(`Error processing ${sourcePath}: ${error.message}`); - return false; + log('warn', `Could not convert metadata: ${error.message}`); + return content; } } /** - * Generate sidebar configuration for a directory + * Fixes relative links to work with Docusaurus URL structure + * @param {string} content - Content with relative links + * @returns {string} Content with fixed absolute links */ -function generateSidebar(sectionName, files) { - const items = files - .map(file => { - // Get the relative path from the section directory - const relativePath = path.relative(path.join(TARGET_DIR, sectionName), file); - const fileName = path.basename(file, '.mdx'); - const dirName = path.dirname(relativePath); - - // Skip if filename is same as directory (already processed) - if (fileName === sectionName) { - return null; - } - - // Convert filename to label (snake_case to Title Case) - const label = fileName - .split('_') - .map(word => word.charAt(0).toUpperCase() + word.slice(1)) - .join(' '); - - // Generate doc ID based on actual file structure - // For files in subdirectories: stylus-by-example/applications/erc20/erc20 - const docId = dirName === '.' - ? `stylus-by-example/${sectionName}/${fileName}` - : `stylus-by-example/${sectionName}/${dirName}/${fileName}`; - - return { - type: 'doc', - label: label, - id: docId - }; - }) - .filter(Boolean); - - return `// @ts-check +function fixRelativeLinks(content) { + return content.replace(CONFIG.content.patterns.relativeLink, (match, linkText, dirName) => { + // Convert to absolute Docusaurus path + return `[${linkText}](/stylus-by-example/basic_examples/${dirName})`; + }); +} + +/** + * Adds content begin marker if not already present + * @param {string} content - File content + * @returns {string} Content with begin marker + */ +function addContentBeginMarker(content) { + const marker = CONFIG.content.markers.contentBegin; + if (content.includes(marker)) return content; + + const delimiter = CONFIG.content.markers.frontmatterDelimiter; + const frontmatterEnd = content.indexOf(delimiter, 3); + + if (frontmatterEnd === -1) return content; + + const insertPos = content.indexOf('\n', frontmatterEnd + 3) + 1; + return content.slice(0, insertPos) + `\n${marker}\n` + content.slice(insertPos); +} + +/** + * Adds "not for production" banner import and component two lines before first rust code block + * @param {string} content - File content + * @returns {string} Content with banner added + */ +function addNotForProductionBanner(content) { + const firstCodeBlock = '```rust'; + const bannerCode = ` +import NotForProductionBannerPartial from '../../partials/_not-for-production-banner-partial.mdx'; + + +`; + + // Find the first occurrence of ```rust code block + const index = content.indexOf(firstCodeBlock); + if (index === -1) { + // No rust code block found, return content unchanged + return content; + } + + // Find the position two lines before the first code block + const lines = content.substring(0, index).split('\n'); + const insertLineIndex = lines.length - 2; + + // Insert the banner at the calculated position + lines.splice(insertLineIndex, 0, bannerCode); + + // Reconstruct the content with the banner inserted + const newContent = lines.join('\n') + content.substring(index); + return newContent; +} + /** - * @fileoverview Autogenerated sidebar configuration for Stylus by Example + * Applies all content transformations to a file + * @param {string} content - Original file content + * @returns {string} Transformed content + */ +function transformContent(content) { + let transformedContent = content; + + // Apply transformations in sequence + transformedContent = convertMetadataToFrontmatter(transformedContent); + transformedContent = fixRelativeLinks(transformedContent); + transformedContent = addContentBeginMarker(transformedContent); + transformedContent = addNotForProductionBanner(transformedContent); + + return transformedContent; +} + +// ============================================================================ +// SIDEBAR GENERATION - Sidebar configuration file generation +// ============================================================================ + +/** + * Creates a sidebar item object for a file + * @param {string} sectionName - Section name (basic_examples, applications) + * @param {string} fileName - File name without extension + * @returns {Object} Sidebar item configuration + */ +function createSidebarItem(sectionName, fileName) { + return { + type: 'doc', + id: `stylus-by-example/${sectionName}/${fileName}`, + label: snakeCaseToTitleCase(fileName), + }; +} + +/** + * Generates the complete sidebar JavaScript file content + * @param {string} sectionName - Section name for the sidebar + * @param {Object[]} sidebarItems - Array of sidebar item objects + * @returns {string} Complete sidebar file content + */ +function generateSidebarFileContent(sectionName, sidebarItems) { + const template = CONFIG.output.sidebarTemplate; + const timestamp = generateTimestamp(); + + return `${template.fileHeader} +/** + * @fileoverview ${template.docString} ${sectionName} * @description This file is automatically generated by sync-stylus-content.js - * @generated ${new Date().toISOString()} + * @generated ${timestamp} * @see https://docusaurus.io/docs/sidebar * @see https://docusaurus.io/docs/sidebar/items */ -/** @type {import('@docusaurus/plugin-content-docs').SidebarItem[]} */ -module.exports = ${JSON.stringify(items, null, 2)}; +${template.typeAnnotation} +const sidebar = ${JSON.stringify({ items: sidebarItems }, null, 2)}; + +module.exports = sidebar.items; `; } /** - * Main sync function + * Generates sidebar configuration for a section directory + * @param {string} sectionName - Section name (basic_examples, applications) + * @param {string[]} files - Array of file paths + * @returns {string} Complete sidebar JavaScript file content */ -async function syncContent() { - console.log('🔄 Syncing Stylus by Example content...'); - +function generateSidebar(sectionName, files) { + const allowList = getAllowListForSection(sectionName); + const itemsByFileName = {}; + + // Collect all items by filename, skipping index files + files.forEach((file) => { + const fileName = path.basename(file, CONFIG.content.fileExtensions.source); + + // Skip index files (filename matches section name) + if (fileName === sectionName) return; + + const item = createSidebarItem(sectionName, fileName); + itemsByFileName[fileName] = item; + }); + + // Order items according to allowlist + const orderedItems = allowList + .map((allowedFileName) => itemsByFileName[allowedFileName]) + .filter(Boolean); // Remove undefined items + + return generateSidebarFileContent(sectionName, orderedItems); +} + +// ============================================================================ +// FILE OPERATIONS - High-level file processing functions +// ============================================================================ + +/** + * Processes and copies a single MDX file with transformations + * @param {string} sourcePath - Source file path + * @param {string} targetPath - Target file path + * @returns {Promise} True if processing succeeded + */ +async function processFile(sourcePath, targetPath) { try { - // Check if source exists - if (!await fs.pathExists(SOURCE_DIR)) { - console.warn(`⚠️ Source directory not found: ${SOURCE_DIR}`); - console.log('Skipping Stylus by Example content sync.'); - console.log('To include this content, initialize the submodule:'); - console.log(' git submodule update --init --recursive'); - - // Create empty target directory with marker - await fs.ensureDir(TARGET_DIR); - await fs.writeFile( - path.join(TARGET_DIR, DONT_EDIT_MARKER), - `This folder would contain auto-generated content from submodules/stylus-by-example -The submodule was not available during build. -Generated: ${new Date().toISOString()} + // Read and transform content + const content = await fs.readFile(sourcePath, 'utf8'); + const transformedContent = transformContent(content); + + // Ensure target directory exists and write file + await fs.ensureDir(path.dirname(targetPath)); + await fs.writeFile(targetPath, transformedContent); + + return true; + } catch (error) { + log('error', `Failed to process file: ${sourcePath}`, { + error: error.message, + targetPath, + }); + return false; + } +} + +/** + * Creates the "don't edit" marker file in target directory + * @param {string} targetDir - Target directory path + * @param {boolean} isSubmoduleAvailable - Whether submodule is available + * @returns {Promise} + */ +async function createDontEditMarker(targetDir, isSubmoduleAvailable = true) { + const timestamp = generateTimestamp(); + const markerPath = path.join(targetDir, CONFIG.paths.dontEditMarker); + + const content = isSubmoduleAvailable + ? `This folder is auto-generated from submodules/stylus-by-example +Do not edit files directly here. Edit them in the submodule instead. +Generated: ${timestamp} ` - ); - - // Create empty sidebar files for the expected directories - const expectedDirs = ['basic_examples', 'applications']; - for (const dir of expectedDirs) { - await fs.ensureDir(path.join(TARGET_DIR, dir)); - const emptySidebar = `// @ts-check + : `This folder would contain auto-generated content from submodules/stylus-by-example +The submodule was not available during build. +Generated: ${timestamp} +`; + + await fs.writeFile(markerPath, content); +} + +/** + * Creates empty sidebar files for sections when submodule is unavailable + * @param {string} targetDir - Target directory path + * @returns {Promise} + */ +async function createEmptySidebars(targetDir) { + const timestamp = generateTimestamp(); + const template = CONFIG.output.sidebarTemplate; + + const emptySidebarContent = `${template.fileHeader} /** * @fileoverview Empty sidebar configuration (submodule not available) - * @generated ${new Date().toISOString()} + * @generated ${timestamp} */ -/** @type {import('@docusaurus/plugin-content-docs').SidebarItem[]} */ -module.exports = []; +${template.typeAnnotation} +const sidebar = { + items: [] +}; + +module.exports = sidebar.items; `; - await fs.writeFile(path.join(TARGET_DIR, dir, 'sidebar.js'), emptySidebar); - } - - console.log('✅ Created empty Stylus by Example structure'); - return; + + for (const sectionName of CONFIG.output.sections) { + const sectionDir = path.join(targetDir, sectionName); + await fs.ensureDir(sectionDir); + + const sidebarPath = path.join(sectionDir, CONFIG.paths.sidebarFileName); + await fs.writeFile(sidebarPath, emptySidebarContent); + } +} + +/** + * Generates sidebar files for all processed sections + * @param {string} targetDir - Target directory path + * @returns {Promise} + */ +async function generateAllSidebars(targetDir) { + for (const sectionName of CONFIG.output.sections) { + const sectionDir = path.join(targetDir, sectionName); + + if (await fs.pathExists(sectionDir)) { + const pattern = path.join(sectionDir, `**/*${CONFIG.content.fileExtensions.source}`); + const sectionFiles = glob.sync(pattern); + + const sidebarContent = generateSidebar(sectionName, sectionFiles); + const sidebarPath = path.join(sectionDir, CONFIG.paths.sidebarFileName); + + await fs.writeFile(sidebarPath, sidebarContent); + log('write', `Generated sidebar for ${sectionName}`, { + itemCount: sectionFiles.length, + }); } - - // Clean target directory - if (await fs.pathExists(TARGET_DIR)) { - console.log('🧹 Cleaning target directory...'); - await fs.remove(TARGET_DIR); + } +} + +// ============================================================================ +// MAIN SYNC LOGIC - Core synchronization workflow +// ============================================================================ + +/** + * Handles the case when the source submodule is not available + * @returns {Promise} + */ +async function handleMissingSubmodule() { + log('warn', `Source directory not found: ${CONFIG.paths.sourceDir}`); + log('info', 'Skipping Stylus by Example content sync.'); + log('info', 'To include this content, initialize the submodule:'); + log('info', ' git submodule update --init --recursive'); + + // Create empty structure + await fs.ensureDir(CONFIG.paths.targetDir); + await createDontEditMarker(CONFIG.paths.targetDir, false); + await createEmptySidebars(CONFIG.paths.targetDir); + + log('success', 'Created empty Stylus by Example structure'); +} + +/** + * Processes all allowed files from source to target directory + * @param {string[]} allowedFiles - Array of file paths to process + * @returns {Promise} Number of successfully processed files + */ +async function processAllFiles(allowedFiles) { + let successCount = 0; + + for (const sourceFile of allowedFiles) { + const { targetPath } = generateTargetPath(sourceFile); + + if (await processFile(sourceFile, targetPath)) { + successCount++; } - - // Create target directory - await fs.ensureDir(TARGET_DIR); - - // Add marker file - await fs.writeFile( - path.join(TARGET_DIR, DONT_EDIT_MARKER), - `This folder is auto-generated from submodules/stylus-by-example -Do not edit files directly here. Edit them in the submodule instead. -Generated: ${new Date().toISOString()} -` - ); - - // Find all page.mdx files - const pattern = path.join(SOURCE_DIR, '**/page.mdx'); - const files = glob.sync(pattern); - - console.log(`📁 Found ${files.length} files to process`); - - // Process each file - let successCount = 0; - for (const sourceFile of files) { - // Calculate target path - const relativePath = path.relative(SOURCE_DIR, sourceFile); - const targetDir = path.dirname(relativePath); - - // Rename page.mdx to directory name or keep as index - let targetFileName; - if (targetDir === '.') { - targetFileName = 'index.mdx'; - } else { - // Use the parent directory name as the filename - const parentDir = path.basename(targetDir); - targetFileName = `${parentDir}.mdx`; - } - - const targetFile = path.join(TARGET_DIR, targetDir, targetFileName); - - // Process and copy file - if (await processFile(sourceFile, targetFile)) { - successCount++; - } + } + + return successCount; +} + +/** + * Main synchronization function + * @returns {Promise} + */ +async function syncContent() { + log('info', 'Syncing Stylus by Example content...'); + + try { + // Check if source exists + if (!(await fs.pathExists(CONFIG.paths.sourceDir))) { + await handleMissingSubmodule(); + return; } - - // Generate sidebars for each directory - const directories = ['basic_examples', 'applications']; - for (const dir of directories) { - const dirPath = path.join(TARGET_DIR, dir); - if (await fs.pathExists(dirPath)) { - const dirFiles = glob.sync(path.join(dirPath, '**/*.mdx')); - const sidebarContent = generateSidebar(dir, dirFiles); - await fs.writeFile(path.join(dirPath, 'sidebar.js'), sidebarContent); - console.log(`📝 Generated sidebar for ${dir} (${dirFiles.length} items)`); - } + + // Clean and recreate target directory + if (await fs.pathExists(CONFIG.paths.targetDir)) { + log('clean', 'Cleaning target directory...'); + await fs.remove(CONFIG.paths.targetDir); } - - console.log(`✅ Successfully synced ${successCount}/${files.length} files`); - + + await fs.ensureDir(CONFIG.paths.targetDir); + await createDontEditMarker(CONFIG.paths.targetDir, true); + + // Find and filter files + const sourcePattern = path.join(CONFIG.paths.sourceDir, CONFIG.paths.sourcePattern); + const allFiles = glob.sync(sourcePattern); + const allowedFiles = allFiles.filter(isFileAllowed); + + log( + 'file', + `Found ${allFiles.length} total files, ${allowedFiles.length} allowed files to process`, + ); + + // Process all files + const successCount = await processAllFiles(allowedFiles); + + // Generate sidebars + await generateAllSidebars(CONFIG.paths.targetDir); + + log('success', `Successfully synced ${successCount}/${allowedFiles.length} files`); } catch (error) { - console.error(`❌ Sync failed: ${error.message}`); + log('error', `Sync failed: ${error.message}`, { + stack: error.stack, + context: error.context || 'Unknown', + }); process.exit(1); } } +// ============================================================================ +// MODULE EXPORTS AND EXECUTION +// ============================================================================ + // Run if called directly if (require.main === module) { syncContent(); } -module.exports = { syncContent }; \ No newline at end of file +module.exports = { + syncContent, + // Export utilities for testing + CONFIG, + isFileAllowed, + transformContent, + generateSidebar, +}; From 82ce7978782b73d21cf903d7c03c734573b4aee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Wed, 1 Oct 2025 11:27:32 -0700 Subject: [PATCH 37/62] restore file to master state --- scripts/build-glossary.ts | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/scripts/build-glossary.ts b/scripts/build-glossary.ts index f2dbb1b759..1ad66fd987 100644 --- a/scripts/build-glossary.ts +++ b/scripts/build-glossary.ts @@ -22,7 +22,7 @@ function escapeForJSON(str: string): string { .replace(/"/g, '\\"') .replace(/\n/g, '\\n') .replace(/\r/g, '\\r') - .replace(/\t/g, '\\t') + .replace(/\t/g, '\\t'); } /** @@ -127,7 +127,9 @@ async function main(): Promise { // Generate import statements for each term (unused in current implementation) // This could be used if implementing a React component approach to term rendering let imports = terms - .map((item) => `import ${renderKey(item.data.key)} from './docs/glossary/${item.data.key}.mdx';`) + .map( + (item) => `import ${renderKey(item.data.key)} from './docs/glossary/${item.data.key}.mdx';`, + ) .join('\n'); // Generate component references for each term (unused in current implementation) @@ -135,11 +137,24 @@ async function main(): Promise { // Generate and write the consolidated glossary partial MDX file // This creates a single file with all terms formatted as Markdown headings + const currentDate = new Date().toISOString().split('T')[0]; // Format: YYYY-MM-DD + const frontmatter = `--- +partial_type: glossary +title: "Arbitrum Glossary Definitions" +description: "Comprehensive glossary of Arbitrum terminology and definitions" +author: anegg0 +last_reviewed: ${currentDate} +--- + +`; + await fs.writeFile( './docs/partials/_glossary-partial.mdx', - terms - .map((item) => `### ${item.data.title} {#${item.data.key}}\n\n${item.content.trim()}`) - .join('\n\n') + '\n', + frontmatter + + terms + .map((item) => `### ${item.data.title} {#${item.data.key}}\n\n${item.content.trim()}`) + .join('\n\n') + + '\n', ); // Generate and write the JSON glossary file for client-side usage From 6a164486a621f15440a67b39b7e1228354b1c166 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Wed, 1 Oct 2025 11:28:09 -0700 Subject: [PATCH 38/62] restore file to master state --- .../bold/partials/_bold-public-preview-banner-partial.mdx | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 docs/how-arbitrum-works/bold/partials/_bold-public-preview-banner-partial.mdx diff --git a/docs/how-arbitrum-works/bold/partials/_bold-public-preview-banner-partial.mdx b/docs/how-arbitrum-works/bold/partials/_bold-public-preview-banner-partial.mdx deleted file mode 100644 index a4a519e9e1..0000000000 --- a/docs/how-arbitrum-works/bold/partials/_bold-public-preview-banner-partial.mdx +++ /dev/null @@ -1,7 +0,0 @@ -:::caution ALPHA RELEASE, PUBLIC PREVIEW DOCS - -The BoLD dispute protocol is currently deployed on a public testnet (that posts assertions to Ethereum Sepolia) and is tagged as an `alpha` release. The code has been audited by [Trail of Bits](https://github.com/trailofbits/publications/blob/master/reviews/2024-04-offchainbold-securityreview.pdf) and in a [public audit competition with Code4rena](https://code4rena.com/audits/2024-05-arbitrum-bold), but **should not be used in production scenarios**. Please note that the public testnet is intended for Arbitrum users and researchers to test and experiment with the BoLD dispute protocol for the purposes of education and hardening the protocol via the surfacing of bugs. The public testnet may be paused, and its parameters may be updated at any time in response to scenarios that impact the network and its ability to fulfill its function as a working, reliable test environment. This documentation is currently in [public preview](../public-preview-expectations.mdx). - -To provide feedback, click the _Request an update_ button at the top of this document, [join the Arbitrum Discord](https://discord.gg/arbitrum), or reach out to our team directly by completing [this form](http://bit.ly/3yy6EUK). - -::: From 7618d5b90d3ef149dc828aa02e6e34523407b975 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Wed, 1 Oct 2025 12:20:02 -0700 Subject: [PATCH 39/62] rename navbar's "build dApps" submenus --- docusaurus.config.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docusaurus.config.js b/docusaurus.config.js index bdedaae771..e7e47fe5aa 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -234,15 +234,15 @@ const config = { }, { type: 'dropdown', - label: 'Build apps', + label: 'Build dApps', position: 'right', items: [ { - label: 'Quickstart', + label: 'Build with Solidity', to: '/build-decentralized-apps/quickstart-solidity-remix', }, { - label: 'Stylus', + label: 'Build with Stylus', to: '/stylus/gentle-introduction', }, ], From 48b52db13325cb513ca24d2f121754953d3dc470 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Wed, 1 Oct 2025 12:23:32 -0700 Subject: [PATCH 40/62] redirect land page to welcom/arbitrum-gentle-introduction --- sidebars.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sidebars.js b/sidebars.js index 7e27cfca69..d27548d77c 100644 --- a/sidebars.js +++ b/sidebars.js @@ -29,7 +29,7 @@ const sidebars = { getStartedSidebar: [ { type: 'doc', - id: 'get-started/get-started', + id: 'welcome/arbitrum-gentle-introduction', label: 'Get started', }, { From 38644e9c2cfad6be864ea3f252d27ef7ec76450c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Wed, 1 Oct 2025 12:56:41 -0700 Subject: [PATCH 41/62] fix landing page article link --- sidebars.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sidebars.js b/sidebars.js index d27548d77c..f14f2b5414 100644 --- a/sidebars.js +++ b/sidebars.js @@ -29,7 +29,7 @@ const sidebars = { getStartedSidebar: [ { type: 'doc', - id: 'welcome/arbitrum-gentle-introduction', + id: 'intro/intro', label: 'Get started', }, { From 8b25d684b3ed008f2c693e82b57e6d97feb58e5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Wed, 1 Oct 2025 18:21:45 -0700 Subject: [PATCH 42/62] redistribute content across sidebar/navbar --- .../01-quickstart-solidity-remix.mdx | 2 +- docusaurus.config.js | 2 +- sidebars.js | 1184 ++++++++++++++--- 3 files changed, 1038 insertions(+), 150 deletions(-) diff --git a/docs/build-decentralized-apps/01-quickstart-solidity-remix.mdx b/docs/build-decentralized-apps/01-quickstart-solidity-remix.mdx index a5689be4e4..bc1a09d583 100644 --- a/docs/build-decentralized-apps/01-quickstart-solidity-remix.mdx +++ b/docs/build-decentralized-apps/01-quickstart-solidity-remix.mdx @@ -1,5 +1,5 @@ --- -title: 'Quickstart: Build a decentralized app (Solidity)' +title: 'Build a decentralized app with Solidity (Quickstart)' description: This quickstart walks you through the process of converting a Javascript vending machine's business logic into a Solidity smart contract. We'll then deploy the smart contract to a local Anvil/Foundry network, then Arbitrum Sepolia testnet, and finally Arbitrum One mainnet. author: symbolpunk user_story: As a web2 developer, I want to onboard into Arbitrum by building and deploying my first smart contract, and knowing how to build a web widget interacting with it. diff --git a/docusaurus.config.js b/docusaurus.config.js index e7e47fe5aa..92aa1d1b4e 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -243,7 +243,7 @@ const config = { }, { label: 'Build with Stylus', - to: '/stylus/gentle-introduction', + to: '/stylus/quickstart', }, ], }, diff --git a/sidebars.js b/sidebars.js index b6069cd9f6..41cb6c09f8 100644 --- a/sidebars.js +++ b/sidebars.js @@ -29,7 +29,7 @@ const sidebars = { getStartedSidebar: [ { type: 'doc', - id: 'intro/intro', + id: 'get-started/get-started', label: 'Get started', }, { @@ -38,219 +38,1124 @@ const sidebars = { label: 'FAQ', }, { - type: 'doc', - id: 'for-devs/dev-tools-and-resources/chain-info', - label: 'Chain info', + type: 'category', + label: 'Build dApps with Solidity', + collapsed: true, + items: [ + { + type: 'doc', + id: 'build-decentralized-apps/quickstart-solidity-remix', + label: 'Quickstart', + }, + { + type: 'doc', + label: 'Estimate gas', + id: 'build-decentralized-apps/how-to-estimate-gas', + }, + { + type: 'doc', + label: 'Chains and testnets', + id: 'build-decentralized-apps/public-chains', + }, + { + type: 'doc', + label: 'Cross-chain messaging', + id: 'build-decentralized-apps/cross-chain-messaging', + }, + { + type: 'doc', + id: 'build-decentralized-apps/custom-gas-token-sdk', + label: 'Custom gas token SDK', + }, + { + type: 'category', + label: 'Arbitrum vs Ethereum', + items: [ + { + type: 'doc', + label: 'Comparison overview', + id: 'build-decentralized-apps/arbitrum-vs-ethereum/comparison-overview', + }, + { + type: 'doc', + label: 'Block gas limit, numbers and time', + id: 'build-decentralized-apps/arbitrum-vs-ethereum/block-numbers-and-time', + }, + { + type: 'doc', + label: 'RPC methods', + id: 'build-decentralized-apps/arbitrum-vs-ethereum/rpc-methods', + }, + { + type: 'doc', + label: 'Solidity support', + id: 'build-decentralized-apps/arbitrum-vs-ethereum/solidity-support', + }, + ], + }, + { + type: 'doc', + label: 'Oracles', + id: 'build-decentralized-apps/oracles/overview-oracles', + }, + { + type: 'category', + label: 'Precompiles', + collapsed: true, + items: [ + { + type: 'doc', + label: 'Overview', + id: 'build-decentralized-apps/precompiles/overview', + }, + { + type: 'doc', + label: 'Reference', + id: 'build-decentralized-apps/precompiles/reference', + }, + ], + }, + { + type: 'category', + label: 'NodeInterface', + collapsed: true, + items: [ + { + type: 'doc', + label: 'Overview', + id: 'build-decentralized-apps/nodeinterface/overview', + }, + { + type: 'doc', + label: 'Reference', + id: 'build-decentralized-apps/nodeinterface/reference', + }, + ], + }, + { + type: 'category', + label: 'Token bridging', + collapsed: true, + items: [ + { + type: 'doc', + label: 'Overview', + id: 'build-decentralized-apps/token-bridging/overview', + }, + { + type: 'doc', + label: 'ETH bridging', + id: 'build-decentralized-apps/token-bridging/token-bridge-ether', + }, + { + type: 'doc', + label: 'ERC-20 token bridging', + id: 'build-decentralized-apps/token-bridging/token-bridge-erc20', + }, + { + type: 'category', + label: 'Bridge tokens programmatically', + items: [ + { + type: 'doc', + label: 'Get started', + id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/get-started', + }, + { + type: 'doc', + label: 'Use the standard gateway', + id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-standard', + }, + { + type: 'doc', + label: 'Use the generic-custom gateway', + id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-generic-custom', + }, + { + type: 'doc', + label: 'Use the custom gateway', + id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-custom-gateway', + }, + ], + }, + ], + }, + { + type: 'category', + label: 'Reference', + items: [ + { + type: 'doc', + id: 'build-decentralized-apps/reference/node-providers', + label: 'RPC endpoints and providers', + }, + { + type: 'doc', + label: 'Smart contract addresses', + id: 'build-decentralized-apps/reference/contract-addresses', + }, + { + type: 'doc', + label: 'Chain parameters', + id: 'build-decentralized-apps/reference/chain-params', + }, + { + type: 'doc', + label: 'Development frameworks', + id: 'build-decentralized-apps/reference/development-frameworks', + }, + { + type: 'doc', + label: 'Web3 libraries and tools', + id: 'build-decentralized-apps/reference/web3-libraries-tools', + }, + { + type: 'doc', + label: 'Monitoring tools and block explorers', + id: 'build-decentralized-apps/reference/monitoring-tools-block-explorers', + }, + { + type: 'doc', + label: 'Debugging tools', + id: 'build-decentralized-apps/reference/debugging-tools', + }, + + { + type: 'doc', + id: 'build-decentralized-apps/reference/mainnet-risks', + label: 'Mainnet risks', + }, + ], + }, + { + type: 'doc', + label: 'Troubleshooting', + id: 'for-devs/troubleshooting-building', + }, + { + type: 'category', + label: 'Arbitrum SDK', + items: sdkSidebar.sdkSidebar, + }, + { + type: 'link', + label: 'Tutorials', + href: 'https://github.com/OffchainLabs/arbitrum-tutorials', + }, + ], + }, + { + type: 'category', + label: 'Build dApps with Stylus', + collapsed: true, + items: [ + { + type: 'doc', + id: 'stylus/gentle-introduction', + label: 'A gentle introduction', + }, + { + type: 'doc', + id: 'stylus/quickstart', + label: 'Quickstart', + }, + { + type: 'category', + label: 'Rust SDK', + collapsed: true, + items: [ + { + type: 'doc', + id: 'stylus/reference/overview', + label: 'Overview', + }, + { + type: 'doc', + id: 'stylus/reference/project-structure', + label: 'Structure of a Contract', + }, + ...stylusByExampleBasicExamples, + { + type: 'doc', + id: 'stylus/how-tos/using-inheritance', + label: 'Composition and trait-based routing model', + }, + { + type: 'doc', + id: 'stylus/reference/rust-sdk-guide', + label: 'Advanced features', + }, + { + type: 'doc', + id: 'stylus/recommended-libraries', + label: 'Recommended Rust Crates', + }, + ], + }, + { + type: 'category', + label: 'Rust CLI', + collapsed: true, + items: [ + { + type: 'doc', + id: 'stylus/using-cli', + label: 'Overview', + }, + { + type: 'doc', + id: 'stylus/how-tos/debugging-tx', + label: 'Debug transactions', + }, + { + type: 'doc', + id: 'stylus/how-tos/testing-contracts', + label: 'Testing contracts', + }, + { + type: 'doc', + id: 'stylus/how-tos/verifying-contracts', + label: 'Verify contracts', + }, + { + type: 'doc', + id: 'stylus/how-tos/caching-contracts', + label: 'Cache contracts', + }, + { + type: 'doc', + id: 'stylus/how-tos/verifying-contracts-arbiscan', + label: 'Verify on Arbiscan', + }, + { + type: 'doc', + id: 'stylus/how-tos/optimizing-binaries', + label: 'Optimize WASM binaries', + }, + ], + }, + { + type: 'html', + value: + 'Run a local dev node', + }, + { + type: 'category', + label: 'Concepts', + collapsed: true, + items: [ + { + type: 'doc', + id: 'stylus/concepts/how-it-works', + label: 'Architecture overview', + }, + { + type: 'doc', + id: 'stylus/concepts/gas-metering', + label: 'Gas metering', + }, + ], + }, + { + type: 'category', + label: 'Examples', + collapsed: true, + items: [ + ...stylusByExampleApplications, + { + type: 'link', + label: 'Awesome Stylus', + href: 'https://github.com/OffchainLabs/awesome-stylus', + }, + ], + }, + { + type: 'category', + label: 'Reference', + collapsed: true, + items: [ + { + type: 'html', + value: + 'Chain Info', + }, + { + type: 'doc', + id: 'stylus/reference/opcode-hostio-pricing', + label: 'Gas & Ink Pricing', + }, + { + type: 'link', + label: 'Stylus by Example', + href: 'https://stylus-by-example.org/', + }, + { + type: 'link', + label: 'Cargo Stylus CLI GitHub', + href: 'https://github.com/OffchainLabs/cargo-stylus', + }, + { + type: 'link', + label: 'Rust SDK Crate', + href: 'https://docs.rs/stylus-sdk/latest/stylus_sdk/index.html', + }, + { + type: 'link', + label: 'Source Code Repository', + href: 'https://github.com/OffchainLabs/stylus', + }, + ], + }, + { + type: 'doc', + id: 'stylus/how-tos/adding-support-for-new-languages', + label: 'Using other languages', + }, + { + type: 'doc', + id: 'stylus/troubleshooting-building-stylus', + label: 'Troubleshooting', + }, + ], + }, + { + type: 'category', + label: 'Run an Arbitrum (Orbit) chain', + collapsed: true, + items: [ + { + type: 'doc', + id: 'launch-arbitrum-chain/a-gentle-introduction', + label: 'A gentle introduction', + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/aep-license', + label: 'Arbitrum chain licensing', + }, + { + type: 'category', + label: 'Configure your chain', + collapsed: true, + items: [ + { + type: 'category', + label: 'Common features', + collapsed: true, + items: [ + { + type: 'doc', + id: 'launch-arbitrum-chain/configure-your-chain/common-configurations/use-a-custom-gas-token-anytrust', + label: `Configure a custom AnyTrust gas token`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/configure-your-chain/common-configurations/use-a-custom-gas-token-rollup', + label: `Configure a custom Rollup gas token`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/configure-your-chain/common-configurations/customizable-challenge-period', + label: 'Customize the challenge period', + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/configure-your-chain/common-configurations/fee-management', + label: `Manage the fee parameters`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/configure-your-chain/common-configurations/enable-post-4844-blobs', + label: `Enable blob transactions`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/configure-your-chain/common-configurations/arbitrum-chain-finality', + label: `Configure delayed inbox finality`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/configure-your-chain/common-configurations/gas-optimization-tools', + label: `Gas optimization tools`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/configure-your-chain/common-configurations/bold-adoption-for-arbitrum-chains', + label: 'BoLD for Arbitrum chains', + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/configure-your-chain/common-configurations/timeboost-for-arbitrum-chains', + label: 'Timeboost for Arbitrum chains', + }, + { + type: 'category', + label: 'Data Availability Committees', + collapsed: true, + items: [ + { + type: 'doc', + id: 'run-arbitrum-node/data-availability-committees/get-started', + label: 'Get started', + }, + { + type: 'doc', + id: 'run-arbitrum-node/data-availability-committees/deploy-das', + label: 'Deploy a Data Availability Server (DAS)', + }, + { + type: 'doc', + id: 'run-arbitrum-node/data-availability-committees/deploy-mirror-das', + label: 'Deploy a mirror Data Availability Server', + }, + { + type: 'doc', + id: 'run-arbitrum-node/data-availability-committees/configure-dac', + label: 'Configure a Data Availability Committee (DAC)', + }, + ], + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/how-tos/customize-deployment-configuration', + label: `Customize your chain's deployment`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/reference/additional-configuration-parameters', + label: `Additional configuration parameters`, + }, + ], + }, + { + type: 'category', + label: 'Advanced features', + collapsed: true, + items: [ + { + type: 'doc', + id: 'launch-arbitrum-chain/configure-your-chain/advanced-configurations/fast-withdrawals', + label: `Enable fast withdrawals`, + }, + { + type: 'doc', + label: 'Use Layer Leap', + id: 'launch-arbitrum-chain/configure-your-chain/advanced-configurations/layer-leap', + }, + { + type: 'category', + label: 'Configure AEP fee routing', + collapsed: true, + items: [ + { + type: 'doc', + id: 'launch-arbitrum-chain/configure-your-chain/advanced-configurations/aep-fee-router/aep-fee-router-introduction', + label: `AEP fee router overview`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/configure-your-chain/advanced-configurations/aep-fee-router/set-up-aep-fee-router', + label: `Set up AEP fee router`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/configure-your-chain/advanced-configurations/aep-fee-router/calculate-aep-fees', + label: `Calculate AEP license fees`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/configure-your-chain/advanced-configurations/aep-fee-router/reporting-on-fees', + label: `Report your fees`, + }, + ], + }, + ], + }, + ], + }, + { + type: 'category', + label: 'Deploy an Arbitrum chain', + collapsed: true, + items: [ + { + type: 'category', + label: 'Deploy a production chain', + collapsed: true, + items: [ + { + type: 'doc', + id: 'launch-arbitrum-chain/arbitrum-chain-sdk-introduction', + label: `Overview`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/deploy-an-arbitrum-chain/deploying-an-arbitrum-chain', + label: `Deploy an Arbitrum chain`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/how-tos/arbitrum-chain-sdk-preparing-node-config', + label: `Generate the node config file`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/deploy-an-arbitrum-chain/deploying-token-bridge', + label: `Deploy a token bridge`, + }, + ], + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/deploy-an-arbitrum-chain/canonical-factory-contracts', + label: 'Canonical factory contracts', + }, + ], + }, + { + type: 'category', + label: 'Maintain your chain', + collapsed: true, + items: [ + { + type: 'doc', + id: 'launch-arbitrum-chain/maintain-your-chain/ownership-structure-access-control', + label: 'Ownership structure and access control', + }, + { + type: 'category', + label: 'ArbOS', + collapsed: true, + items: [ + { + type: 'html', + value: + 'ArbOS software releases ', + // q: why use an anchor html tag here? + // a: see note at end of file + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/configure-your-chain/common-configurations/arbos-upgrade', + label: `Upgrade ArbOS`, + }, + ], + }, + { + type: 'category', + label: 'Guidance', + collapsed: true, + items: [ + { + type: 'doc', + id: 'launch-arbitrum-chain/maintain-your-chain/guidance/state-growth', + label: `Manage gas state growth`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/maintain-your-chain/guidance/state-size-limit', + label: `Manage gas speed limit`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/maintain-your-chain/guidance/post-launch-contract-deployments', + label: `Post-launch deployments`, + }, + ], + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/maintain-your-chain/monitoring-tools-and-considerations', + label: 'Monitoring tools and considerations', + }, + ], + }, + { + type: 'category', + label: 'Customize your chain', + collapsed: true, + items: [ + { + type: 'doc', + id: 'launch-arbitrum-chain/customize-your-chain/customize-precompile', + label: `Customize your chain's precompiles`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/customize-your-chain/customize-stf', + label: `Customize your chain's behavior`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/customize-your-chain/customize-arbos', + label: `Customize ArbOS version`, + }, + ], + }, + { + type: 'category', + label: 'Migrate your chain', + collapsed: true, + items: [ + { + type: 'doc', + id: 'launch-arbitrum-chain/migrate-from-another-stack', + label: 'Migrate from another stack', + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/migrate-between-raases', + label: 'Migrate between RaaSes', + }, + ], + }, + { + type: 'category', + label: 'Third-party integrations and features', + collapsed: true, + items: [ + { + type: 'doc', + id: 'launch-arbitrum-chain/third-party-integrations/bridged-usdc-standard', + label: `Implement Circle bridged USDC`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/third-party-integrations/third-party-providers', + label: 'Third-party infrastructure providers', + }, + ], + }, + { + type: 'category', + label: 'Run a node for an Arbitrum chain', + collapsed: true, + items: [ + { + type: 'html', + value: + 'Run a full node ', + // q: why use an anchor html tag here? + // a: see note at end of file + }, + { + type: 'html', + value: + 'Run a validator ', + // q: why use an anchor html tag here? + // a: see note at end of file + }, + { + type: 'html', + value: + 'Run a sequencer node ', + // q: why use an anchor html tag here? + // a: see note at end of file + }, + { + type: 'html', + value: + 'Run high-availability sequencer nodes ', + // q: why use an anchor html tag here? + // a: see note at end of file + }, + ], + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/ecosystem-support/add-arbitrum-chain-to-bridge-ui', + label: `Add your chain to the bridge`, + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/concepts/public-preview-expectations', + label: 'Public preview', + }, + { + type: 'doc', + id: 'launch-arbitrum-chain/faq-troubleshooting/troubleshooting-building-arbitrum-chain', + label: 'FAQ', + }, + ], }, - ], - - // Build dApps sidebar - buildDecentralizedAppsSidebar: [ { type: 'category', - label: 'Build decentralized apps', + label: 'Run an Arbitrum node', + collapsed: true, + items: [ + { + type: 'doc', + id: 'run-arbitrum-node/overview', + label: 'Overview', + }, + { + type: 'doc', + id: 'run-arbitrum-node/run-full-node', + label: 'Run a full node', + }, + { + type: 'doc', + id: 'run-arbitrum-node/run-local-full-chain-simulation', + label: 'Run a local full chain simulation', + }, + { + type: 'doc', + id: 'run-arbitrum-node/run-nitro-dev-node', + label: 'Run a local dev node', + }, + { + type: 'doc', + id: 'run-arbitrum-node/l1-ethereum-beacon-chain-rpc-providers', + label: 'L1 Ethereum RPC providers', + }, + { + type: 'doc', + id: 'run-arbitrum-node/run-feed-relay', + label: 'Run a feed relay', + }, + { + type: 'html', + value: + 'Data Availability Committees ', + // q: why use an anchor html tag here? + // a: see note at end of file + }, + { + type: 'category', + label: 'ArbOS software releases', + collapsed: true, + items: [ + { + type: 'doc', + id: 'run-arbitrum-node/arbos-releases/overview', + label: 'Overview', + }, + { + type: 'doc', + id: 'run-arbitrum-node/arbos-releases/arbos40', + label: 'Callisto (ArbOS 40)', + }, + { + type: 'doc', + id: 'run-arbitrum-node/arbos-releases/arbos32', + label: 'Bianca (ArbOS 32)', + }, + { + type: 'doc', + id: 'run-arbitrum-node/arbos-releases/arbos20', + label: 'Atlas (ArbOS 20)', + }, + { + type: 'doc', + id: 'run-arbitrum-node/arbos-releases/arbos11', + label: 'ArbOS 11', + }, + ], + }, + { + type: 'category', + label: 'More node types', + collapsed: true, + items: [ + { + type: 'doc', + id: 'run-arbitrum-node/more-types/run-archive-node', + label: 'Run an archive node', + }, + { + type: 'doc', + id: 'run-arbitrum-node/more-types/run-validator-node', + label: 'Run a validator', + }, + { + type: 'doc', + id: 'run-arbitrum-node/more-types/run-split-validator-node', + label: 'Run a split validator', + }, + { + type: 'doc', + id: 'run-arbitrum-node/more-types/run-classic-node', + label: 'Run a Classic node', + }, + ], + }, + { + type: 'category', + label: 'Sequencer', + collapsed: true, + items: [ + { + type: 'doc', + id: 'run-arbitrum-node/sequencer/run-sequencer-node', + label: 'Run a sequencer node', + }, + { + type: 'doc', + id: 'run-arbitrum-node/sequencer/read-sequencer-feed', + label: 'Read the sequencer feed', + }, + { + type: 'doc', + id: 'run-arbitrum-node/sequencer/run-sequencer-coordination-manager', + label: 'Run a Sequencer Coordination Manager (SQM)', + }, + { + type: 'doc', + id: 'run-arbitrum-node/sequencer/high-availability-sequencer-docs', + label: 'Run high availability sequencer nodes', + }, + ], + }, + { + type: 'doc', + id: 'run-arbitrum-node/nitro/build-nitro-locally', + label: 'Build Nitro locally', + }, + { + type: 'doc', + id: 'run-arbitrum-node/nitro/migrate-state-and-history-from-classic', + label: 'Migrate to Nitro from Classic', + }, + { + type: 'doc', + id: 'run-arbitrum-node/nitro/nitro-database-snapshots', + label: 'Database snapshots', + }, + { + type: 'doc', + id: 'run-arbitrum-node/nitro/how-to-convert-databases-from-leveldb-to-pebble', + label: 'Convert databases from LevelDB to Pebble', + }, + , + { + type: 'doc', + id: 'run-arbitrum-node/troubleshooting', + label: 'Troubleshooting', + }, + { + type: 'doc', + label: 'FAQ', + id: 'node-running/faq', + }, + ], + }, + { + type: 'category', + label: 'Arbitrum bridge', collapsed: true, items: [ { type: 'doc', - id: 'build-decentralized-apps/quickstart-solidity-remix', - label: 'Quickstart (Solidity)', + id: 'arbitrum-bridge/quickstart', + label: 'Quickstart', }, { type: 'doc', - label: 'Estimate gas', - id: 'build-decentralized-apps/how-to-estimate-gas', + id: 'arbitrum-bridge/usdc-arbitrum-one', + label: 'USDC on Arbitrum One', }, { type: 'doc', - label: 'Chains and testnets', - id: 'build-decentralized-apps/public-chains', + id: 'arbitrum-bridge/troubleshooting', + label: 'Troubleshooting', + }, + ], + }, + { + type: 'category', + label: 'How Arbitrum works', + collapsed: true, + items: [ + { + type: 'doc', + id: 'how-arbitrum-works/a-gentle-introduction', + label: 'A gentle introduction', }, { type: 'doc', - label: 'Cross-chain messaging', - id: 'build-decentralized-apps/cross-chain-messaging', + id: 'how-arbitrum-works/transaction-lifecycle', + label: 'Sequencing, Followed by Deterministic Execution', }, { type: 'doc', - id: 'build-decentralized-apps/custom-gas-token-sdk', - label: 'Custom gas token SDK', + id: 'how-arbitrum-works/sequencer', + label: 'The Sequencer and Censorship Resistance', + }, + { + type: 'doc', + id: 'how-arbitrum-works/l1-to-l2-messaging', + label: 'Parent to Child chain messaging', }, { type: 'category', - label: 'Arbitrum vs Ethereum', + label: 'State Transition Function', items: [ { type: 'doc', - label: 'Comparison overview', - id: 'build-decentralized-apps/arbitrum-vs-ethereum/comparison-overview', + label: 'STF Overview', + id: 'how-arbitrum-works/state-transition-function/stf-gentle-intro', }, { type: 'doc', - label: 'Block gas limit, numbers and time', - id: 'build-decentralized-apps/arbitrum-vs-ethereum/block-numbers-and-time', + label: 'STF inputs', + id: 'how-arbitrum-works/state-transition-function/stf-inputs', }, { type: 'doc', - label: 'RPC methods', - id: 'build-decentralized-apps/arbitrum-vs-ethereum/rpc-methods', + label: 'Ethereum vs Arbitrum', + id: 'how-arbitrum-works/state-transition-function/ethereum-vs-arbitrum', }, { type: 'doc', - label: 'Solidity support', - id: 'build-decentralized-apps/arbitrum-vs-ethereum/solidity-support', + label: 'Modified Geth on Arbitrum', + id: 'how-arbitrum-works/state-transition-function/modified-geth-on-arbitrum', }, - ], - }, - { - type: 'doc', - label: 'Oracles', - id: 'build-decentralized-apps/oracles/overview-oracles', - }, - { - type: 'category', - label: 'Precompiles', - collapsed: true, - items: [ { type: 'doc', - label: 'Overview', - id: 'build-decentralized-apps/precompiles/overview', + label: 'ArbOS', + id: 'how-arbitrum-works/state-transition-function/arbos', }, { type: 'doc', - label: 'Reference', - id: 'build-decentralized-apps/precompiles/reference', + label: 'Stylus execution path', + id: 'how-arbitrum-works/state-transition-function/stylus-execution-path', }, ], }, { type: 'category', - label: 'NodeInterface', - collapsed: true, + label: 'Validation and Proving', items: [ { type: 'doc', - label: 'Overview', - id: 'build-decentralized-apps/nodeinterface/overview', + label: 'Validation and proving overview', + id: 'how-arbitrum-works/validation-and-proving/validation-and-proving', }, { type: 'doc', - label: 'Reference', - id: 'build-decentralized-apps/nodeinterface/reference', + label: 'Rollup protocol', + id: 'how-arbitrum-works/validation-and-proving/rollup-protocol', + }, + { + type: 'doc', + label: 'Proving and challenges', + id: 'how-arbitrum-works/validation-and-proving/proving-and-challenges', }, ], }, + { + type: 'doc', + id: 'how-arbitrum-works/anytrust-protocol', + label: 'AnyTrust protocol', + }, + { + type: 'doc', + id: 'how-arbitrum-works/l2-to-l1-messaging', + label: 'Child to parent chain messaging', + }, + { + type: 'doc', + id: 'how-arbitrum-works/gas-fees', + label: 'Gas and fees', + }, + { + type: 'doc', + id: 'how-arbitrum-works/data-availability', + label: 'Data Availability', + }, + { + type: 'link', + href: 'https://github.com/OffchainLabs/nitro/blob/master/docs/Nitro-whitepaper.pdf', + label: 'Nitro whitepaper', + }, + { type: 'category', - label: 'Token bridging', - collapsed: true, + label: 'The BoLD dispute protocol', items: [ { type: 'doc', - label: 'Overview', - id: 'build-decentralized-apps/token-bridging/overview', + id: 'how-arbitrum-works/bold/gentle-introduction', + label: 'A gentle introduction', + }, + { + type: 'link', + href: 'https://github.com/offchainlabs/bold-validator-starter-kit', + label: 'Deploy a validator on testnet', + }, + { + type: 'link', + href: 'https://arxiv.org/abs/2404.10491', + label: 'BoLD Whitepaper', }, { type: 'doc', - label: 'ETH bridging', - id: 'build-decentralized-apps/token-bridging/token-bridge-ether', + id: 'how-arbitrum-works/bold/bold-technical-deep-dive', + label: 'Technical deep dive', }, { type: 'doc', - label: 'ERC-20 token bridging', - id: 'build-decentralized-apps/token-bridging/token-bridge-erc20', + id: 'how-arbitrum-works/bold/bold-economics-of-disputes', + label: 'Economics of disputes', }, { - type: 'category', - label: 'Bridge tokens programmatically', - items: [ - { - type: 'doc', - label: 'Get started', - id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/get-started', - }, - { - type: 'doc', - label: 'Use the standard gateway', - id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-standard', - }, - { - type: 'doc', - label: 'Use the generic-custom gateway', - id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-generic-custom', - }, - { - type: 'doc', - label: 'Use the custom gateway', - id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-custom-gateway', - }, - ], + type: 'link', + href: 'https://github.com/OffchainLabs/bold', + label: 'Specification on Github', + }, + { + type: 'link', + href: 'https://github.com/trailofbits/publications/blob/master/reviews/2024-04-offchainbold-securityreview.pdf', + label: 'Audit Report by Trail of Bits', + }, + { + type: 'link', + href: 'https://code4rena.com/reports/2024-05-arbitrum-foundation', + label: 'Audit Report by Code4rena', }, ], }, { type: 'category', - label: 'Reference', + label: 'Timeboost', items: [ { type: 'doc', - id: 'build-decentralized-apps/reference/node-providers', - label: 'RPC endpoints and providers', - }, - { - type: 'doc', - label: 'Smart contract addresses', - id: 'build-decentralized-apps/reference/contract-addresses', + id: 'how-arbitrum-works/timeboost/gentle-introduction', + label: 'Public preview', }, { type: 'doc', - label: 'Chain parameters', - id: 'build-decentralized-apps/reference/chain-params', + label: 'Use Timeboost', + id: 'how-arbitrum-works/timeboost/how-to-use-timeboost', }, { type: 'doc', - label: 'Development frameworks', - id: 'build-decentralized-apps/reference/development-frameworks', + label: 'Troubleshoot Timeboost', + id: 'how-arbitrum-works/timeboost/troubleshoot-timeboost', }, { type: 'doc', - label: 'Web3 libraries and tools', - id: 'build-decentralized-apps/reference/web3-libraries-tools', + label: 'Timeboost FAQ', + id: 'how-arbitrum-works/timeboost/timeboost-faq', }, { - type: 'doc', - label: 'Monitoring tools and block explorers', - id: 'build-decentralized-apps/reference/monitoring-tools-block-explorers', + type: 'link', + href: 'https://github.com/OffchainLabs/timeboost-design/blob/main/research_spec.md', + label: 'Specification: Timeboost', }, { - type: 'doc', - label: 'Debugging tools', - id: 'build-decentralized-apps/reference/debugging-tools', + type: 'link', + href: 'https://github.com/OffchainLabs/decentralized-timeboost-spec', + label: 'Specification: Timeboost with decentralized sequencing', }, - { - type: 'doc', - id: 'build-decentralized-apps/reference/mainnet-risks', - label: 'Mainnet risks', + type: 'link', + href: 'https://arxiv.org/abs/2306.02179', + label: 'White paper: Timeboost', }, ], }, - { - type: 'doc', - label: 'Troubleshooting', - id: 'for-devs/troubleshooting-building', - }, - { - type: 'category', - label: 'Arbitrum SDK', - items: sdkSidebar.sdkSidebar, - }, - { - type: 'link', - label: 'Tutorials', - href: 'https://github.com/OffchainLabs/arbitrum-tutorials', - }, ], }, { @@ -259,7 +1164,6 @@ const sidebars = { label: 'Chain info', }, ], - // Run Arbitrum Chain sidebar runArbitrumChainSidebar: [ { @@ -638,12 +1542,8 @@ const sidebars = { stylusSidebar: [ { type: 'category', - label: 'Write Stylus Contracts', + label: 'Build dApps with Stylus', collapsed: false, - link: { - type: 'doc', - id: 'stylus/stylus-content-map', - }, items: [ { type: 'doc', @@ -1240,13 +2140,13 @@ const sidebars = { buildAppsSidebar: [ { type: 'category', - label: 'Build decentralized apps', + label: 'Build dApps with Solidity', collapsed: false, items: [ { type: 'doc', id: 'build-decentralized-apps/quickstart-solidity-remix', - label: 'Quickstart (Solidity)', + label: 'Quickstart', }, { type: 'doc', @@ -1447,12 +2347,8 @@ const sidebars = { }, { type: 'category', - label: 'Write Stylus contracts', + label: 'Build dApps with Stylus', collapsed: true, - link: { - type: 'doc', - id: 'stylus/stylus-content-map', - }, items: [ { type: 'doc', @@ -1634,13 +2530,13 @@ const sidebars = { buildAppsSidebarFromGetStarted: [ { type: 'category', - label: 'Build decentralized apps', - collapsed: false, + label: 'Build dApps with Solidity', + collapsed: true, items: [ { type: 'doc', id: 'build-decentralized-apps/quickstart-solidity-remix', - label: 'Quickstart (Solidity)', + label: 'Quickstart', }, { type: 'doc', @@ -1841,12 +2737,8 @@ const sidebars = { }, { type: 'category', - label: 'Write Stylus contracts', + label: 'Build dApps with Stylus', collapsed: true, - link: { - type: 'doc', - id: 'stylus/stylus-content-map', - }, items: [ { type: 'doc', @@ -2034,7 +2926,7 @@ const sidebars = { { type: 'doc', id: 'build-decentralized-apps/quickstart-solidity-remix', - label: 'Quickstart (Solidity)', + label: 'Quickstart', }, { type: 'doc', @@ -2235,12 +3127,8 @@ const sidebars = { }, { type: 'category', - label: 'Write Stylus contracts', + label: 'Build dApps with Stylus', collapsed: false, - link: { - type: 'doc', - id: 'stylus/stylus-content-map', - }, items: [ { type: 'doc', From 8be610fb651d0c3737f947091f203f29aad0f91e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Mon, 6 Oct 2025 15:40:57 -0700 Subject: [PATCH 43/62] rename "FAQ" to "Introduction" --- ...trum-faq.mdx => arbitrum-introduction.mdx} | 2 +- docs/get-started/get-started.mdx | 2 +- sidebars.js | 4 ++-- vercel.json | 21 +++++++++++++++---- 4 files changed, 21 insertions(+), 8 deletions(-) rename docs/get-started/{arbitrum-faq.mdx => arbitrum-introduction.mdx} (92%) diff --git a/docs/get-started/arbitrum-faq.mdx b/docs/get-started/arbitrum-introduction.mdx similarity index 92% rename from docs/get-started/arbitrum-faq.mdx rename to docs/get-started/arbitrum-introduction.mdx index 6a1428bffe..72e8280d60 100644 --- a/docs/get-started/arbitrum-faq.mdx +++ b/docs/get-started/arbitrum-introduction.mdx @@ -1,5 +1,5 @@ --- -title: 'Arbitrum FAQ' +title: 'Arbitrum introduction' description: 'Frequently asked questions about Arbitrum, Ethereum scaling solutions including Arbitrum One, Arbitrum Nova, Stylus, Arbitrum chains, and the Arbitrum Bridge.' author: dzgoldman user_story: As a technical reader, I want to understand how Arbitrum scales Ethereum. diff --git a/docs/get-started/get-started.mdx b/docs/get-started/get-started.mdx index a51e31f2ab..b9e1f9fa07 100644 --- a/docs/get-started/get-started.mdx +++ b/docs/get-started/get-started.mdx @@ -43,7 +43,7 @@ The Arbitrum suite includes the protocols, chains, services, and SDKs that power | Resource | Description | | ----------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | -| [Arbitrum FAQ](/get-started/arbitrum-faq) | Frequently asked questions about Arbitrum's suite of scaling solutions. | +| [Arbitrum Introduction](/get-started/arbitrum-introduction) | Frequently asked questions about Arbitrum's suite of scaling solutions. | | [Quickstart (Solidity)](/build-decentralized-apps/01-quickstart-solidity-remix.mdx) | Targeted at Web2 developers who want to deploy their first Solidity smart contract to Arbitrum. | | [Quickstart (Rust)](/stylus/quickstart) | Targeted at Web3 developers who want to deploy their first Rust smart contract to Arbitrum using Stylus. | diff --git a/sidebars.js b/sidebars.js index 41cb6c09f8..f6c2ba0b3d 100644 --- a/sidebars.js +++ b/sidebars.js @@ -34,8 +34,8 @@ const sidebars = { }, { type: 'doc', - id: 'get-started/arbitrum-faq', - label: 'FAQ', + id: 'get-started/arbitrum-introduction', + label: 'Arbitrum: introduction', }, { type: 'category', diff --git a/vercel.json b/vercel.json index eaf23cdc00..b4ccb10b84 100644 --- a/vercel.json +++ b/vercel.json @@ -190,6 +190,11 @@ "destination": "/how-arbitrum-works/transaction-lifecycle", "permanent": false }, + { + "source": "/(docs/get-started/arbitrum-faq/?)", + "destination": "/(docs/get-started/arbitrum-introduction/?)", + "permanent": false + }, { "source": "/(docs/inside_arbitrum/?)", "destination": "/how-arbitrum-works/a-gentle-introduction", @@ -247,7 +252,7 @@ }, { "source": "/(docs/welcome/arbitrum-gentle-introduction/?)", - "destination": "/(docs/get-started/arbitrum-faq/?)", + "destination": "/(docs/get-started/arbitrum-introduction/?)", "permanent": false }, { @@ -367,7 +372,7 @@ }, { "source": "/(for-devs/gentle-introduction-dapps/?)", - "destination": "/get-started/arbitrum-faq", + "destination": "/get-started/arbitrum-introduction", "permanent": false }, { @@ -565,7 +570,11 @@ "destination": "/how-arbitrum-works/a-gentle-introduction", "permanent": false }, - { "source": "/(intro/?)", "destination": "/get-started/arbitrum-faq", "permanent": false }, + { + "source": "/(intro/?)", + "destination": "/get-started/arbitrum-introduction", + "permanent": false + }, { "source": "/(launch-arbitrum-chain/arbitrum-chain-quickstart/?)", "destination": "/launch-arbitrum-chain/a-gentle-introduction", @@ -1851,7 +1860,11 @@ "destination": "/how-arbitrum-works/transaction-lifecycle", "permanent": false }, - { "source": "/(welcome/?)", "destination": "/get-started/arbitrum-faq", "permanent": false }, + { + "source": "/(welcome/?)", + "destination": "/get-started/arbitrum-introduction", + "permanent": false + }, { "source": "/(why-nitro/?)", "destination": "/how-arbitrum-works/a-gentle-introduction", From 4f4ac30af828f591ca7fec7ffe79517fcdaf5e40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Mon, 6 Oct 2025 18:59:30 -0700 Subject: [PATCH 44/62] remove commented navbar lines --- docusaurus.config.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docusaurus.config.js b/docusaurus.config.js index 92aa1d1b4e..dafa157565 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -253,12 +253,6 @@ const config = { position: 'right', label: 'Launch a chain', }, - // { - // type: 'docSidebar', - // sidebarId: 'stylusSidebar', - // position: 'right', - // label: 'Use Stylus', - // }, { type: 'docSidebar', sidebarId: 'runNodeSidebar', From 1e4f041c8381bf29f321d73c035f4b28a7bad2db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Tue, 7 Oct 2025 10:27:48 -0700 Subject: [PATCH 45/62] tmp navbar --- sidebars.js | 1132 ++------------------------------------------------- 1 file changed, 24 insertions(+), 1108 deletions(-) diff --git a/sidebars.js b/sidebars.js index f6c2ba0b3d..421cef1da0 100644 --- a/sidebars.js +++ b/sidebars.js @@ -39,1124 +39,40 @@ const sidebars = { }, { type: 'category', - label: 'Build dApps with Solidity', - collapsed: true, - items: [ - { - type: 'doc', - id: 'build-decentralized-apps/quickstart-solidity-remix', - label: 'Quickstart', - }, - { - type: 'doc', - label: 'Estimate gas', - id: 'build-decentralized-apps/how-to-estimate-gas', - }, - { - type: 'doc', - label: 'Chains and testnets', - id: 'build-decentralized-apps/public-chains', - }, - { - type: 'doc', - label: 'Cross-chain messaging', - id: 'build-decentralized-apps/cross-chain-messaging', - }, - { - type: 'doc', - id: 'build-decentralized-apps/custom-gas-token-sdk', - label: 'Custom gas token SDK', - }, - { - type: 'category', - label: 'Arbitrum vs Ethereum', - items: [ - { - type: 'doc', - label: 'Comparison overview', - id: 'build-decentralized-apps/arbitrum-vs-ethereum/comparison-overview', - }, - { - type: 'doc', - label: 'Block gas limit, numbers and time', - id: 'build-decentralized-apps/arbitrum-vs-ethereum/block-numbers-and-time', - }, - { - type: 'doc', - label: 'RPC methods', - id: 'build-decentralized-apps/arbitrum-vs-ethereum/rpc-methods', - }, - { - type: 'doc', - label: 'Solidity support', - id: 'build-decentralized-apps/arbitrum-vs-ethereum/solidity-support', - }, - ], - }, - { - type: 'doc', - label: 'Oracles', - id: 'build-decentralized-apps/oracles/overview-oracles', - }, - { - type: 'category', - label: 'Precompiles', - collapsed: true, - items: [ - { - type: 'doc', - label: 'Overview', - id: 'build-decentralized-apps/precompiles/overview', - }, - { - type: 'doc', - label: 'Reference', - id: 'build-decentralized-apps/precompiles/reference', - }, - ], - }, - { - type: 'category', - label: 'NodeInterface', - collapsed: true, - items: [ - { - type: 'doc', - label: 'Overview', - id: 'build-decentralized-apps/nodeinterface/overview', - }, - { - type: 'doc', - label: 'Reference', - id: 'build-decentralized-apps/nodeinterface/reference', - }, - ], - }, - { - type: 'category', - label: 'Token bridging', - collapsed: true, - items: [ - { - type: 'doc', - label: 'Overview', - id: 'build-decentralized-apps/token-bridging/overview', - }, - { - type: 'doc', - label: 'ETH bridging', - id: 'build-decentralized-apps/token-bridging/token-bridge-ether', - }, - { - type: 'doc', - label: 'ERC-20 token bridging', - id: 'build-decentralized-apps/token-bridging/token-bridge-erc20', - }, - { - type: 'category', - label: 'Bridge tokens programmatically', - items: [ - { - type: 'doc', - label: 'Get started', - id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/get-started', - }, - { - type: 'doc', - label: 'Use the standard gateway', - id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-standard', - }, - { - type: 'doc', - label: 'Use the generic-custom gateway', - id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-generic-custom', - }, - { - type: 'doc', - label: 'Use the custom gateway', - id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-custom-gateway', - }, - ], - }, - ], - }, - { - type: 'category', - label: 'Reference', - items: [ - { - type: 'doc', - id: 'build-decentralized-apps/reference/node-providers', - label: 'RPC endpoints and providers', - }, - { - type: 'doc', - label: 'Smart contract addresses', - id: 'build-decentralized-apps/reference/contract-addresses', - }, - { - type: 'doc', - label: 'Chain parameters', - id: 'build-decentralized-apps/reference/chain-params', - }, - { - type: 'doc', - label: 'Development frameworks', - id: 'build-decentralized-apps/reference/development-frameworks', - }, - { - type: 'doc', - label: 'Web3 libraries and tools', - id: 'build-decentralized-apps/reference/web3-libraries-tools', - }, - { - type: 'doc', - label: 'Monitoring tools and block explorers', - id: 'build-decentralized-apps/reference/monitoring-tools-block-explorers', - }, - { - type: 'doc', - label: 'Debugging tools', - id: 'build-decentralized-apps/reference/debugging-tools', - }, - - { - type: 'doc', - id: 'build-decentralized-apps/reference/mainnet-risks', - label: 'Mainnet risks', - }, - ], - }, - { - type: 'doc', - label: 'Troubleshooting', - id: 'for-devs/troubleshooting-building', - }, - { - type: 'category', - label: 'Arbitrum SDK', - items: sdkSidebar.sdkSidebar, - }, - { - type: 'link', - label: 'Tutorials', - href: 'https://github.com/OffchainLabs/arbitrum-tutorials', - }, - ], - }, - { - type: 'category', - label: 'Build dApps with Stylus', - collapsed: true, - items: [ - { - type: 'doc', - id: 'stylus/gentle-introduction', - label: 'A gentle introduction', - }, - { - type: 'doc', - id: 'stylus/quickstart', - label: 'Quickstart', - }, - { - type: 'category', - label: 'Rust SDK', - collapsed: true, - items: [ - { - type: 'doc', - id: 'stylus/reference/overview', - label: 'Overview', - }, - { - type: 'doc', - id: 'stylus/reference/project-structure', - label: 'Structure of a Contract', - }, - ...stylusByExampleBasicExamples, - { - type: 'doc', - id: 'stylus/how-tos/using-inheritance', - label: 'Composition and trait-based routing model', - }, - { - type: 'doc', - id: 'stylus/reference/rust-sdk-guide', - label: 'Advanced features', - }, - { - type: 'doc', - id: 'stylus/recommended-libraries', - label: 'Recommended Rust Crates', - }, - ], - }, - { - type: 'category', - label: 'Rust CLI', - collapsed: true, - items: [ - { - type: 'doc', - id: 'stylus/using-cli', - label: 'Overview', - }, - { - type: 'doc', - id: 'stylus/how-tos/debugging-tx', - label: 'Debug transactions', - }, - { - type: 'doc', - id: 'stylus/how-tos/testing-contracts', - label: 'Testing contracts', - }, - { - type: 'doc', - id: 'stylus/how-tos/verifying-contracts', - label: 'Verify contracts', - }, - { - type: 'doc', - id: 'stylus/how-tos/caching-contracts', - label: 'Cache contracts', - }, - { - type: 'doc', - id: 'stylus/how-tos/verifying-contracts-arbiscan', - label: 'Verify on Arbiscan', - }, - { - type: 'doc', - id: 'stylus/how-tos/optimizing-binaries', - label: 'Optimize WASM binaries', - }, - ], - }, - { - type: 'html', - value: - 'Run a local dev node', - }, - { - type: 'category', - label: 'Concepts', - collapsed: true, - items: [ - { - type: 'doc', - id: 'stylus/concepts/how-it-works', - label: 'Architecture overview', - }, - { - type: 'doc', - id: 'stylus/concepts/gas-metering', - label: 'Gas metering', - }, - ], - }, - { - type: 'category', - label: 'Examples', - collapsed: true, - items: [ - ...stylusByExampleApplications, - { - type: 'link', - label: 'Awesome Stylus', - href: 'https://github.com/OffchainLabs/awesome-stylus', - }, - ], - }, - { - type: 'category', - label: 'Reference', - collapsed: true, - items: [ - { - type: 'html', - value: - 'Chain Info', - }, - { - type: 'doc', - id: 'stylus/reference/opcode-hostio-pricing', - label: 'Gas & Ink Pricing', - }, - { - type: 'link', - label: 'Stylus by Example', - href: 'https://stylus-by-example.org/', - }, - { - type: 'link', - label: 'Cargo Stylus CLI GitHub', - href: 'https://github.com/OffchainLabs/cargo-stylus', - }, - { - type: 'link', - label: 'Rust SDK Crate', - href: 'https://docs.rs/stylus-sdk/latest/stylus_sdk/index.html', - }, - { - type: 'link', - label: 'Source Code Repository', - href: 'https://github.com/OffchainLabs/stylus', - }, - ], - }, - { - type: 'doc', - id: 'stylus/how-tos/adding-support-for-new-languages', - label: 'Using other languages', - }, - { - type: 'doc', - id: 'stylus/troubleshooting-building-stylus', - label: 'Troubleshooting', - }, - ], - }, - { - type: 'category', - label: 'Run an Arbitrum (Orbit) chain', - collapsed: true, - items: [ - { - type: 'doc', - id: 'launch-arbitrum-chain/a-gentle-introduction', - label: 'A gentle introduction', - }, - { - type: 'doc', - id: 'launch-arbitrum-chain/aep-license', - label: 'Arbitrum chain licensing', - }, - { - type: 'category', - label: 'Configure your chain', - collapsed: true, - items: [ - { - type: 'category', - label: 'Common features', - collapsed: true, - items: [ - { - type: 'doc', - id: 'launch-arbitrum-chain/configure-your-chain/common-configurations/use-a-custom-gas-token-anytrust', - label: `Configure a custom AnyTrust gas token`, - }, - { - type: 'doc', - id: 'launch-arbitrum-chain/configure-your-chain/common-configurations/use-a-custom-gas-token-rollup', - label: `Configure a custom Rollup gas token`, - }, - { - type: 'doc', - id: 'launch-arbitrum-chain/configure-your-chain/common-configurations/customizable-challenge-period', - label: 'Customize the challenge period', - }, - { - type: 'doc', - id: 'launch-arbitrum-chain/configure-your-chain/common-configurations/fee-management', - label: `Manage the fee parameters`, - }, - { - type: 'doc', - id: 'launch-arbitrum-chain/configure-your-chain/common-configurations/enable-post-4844-blobs', - label: `Enable blob transactions`, - }, - { - type: 'doc', - id: 'launch-arbitrum-chain/configure-your-chain/common-configurations/arbitrum-chain-finality', - label: `Configure delayed inbox finality`, - }, - { - type: 'doc', - id: 'launch-arbitrum-chain/configure-your-chain/common-configurations/gas-optimization-tools', - label: `Gas optimization tools`, - }, - { - type: 'doc', - id: 'launch-arbitrum-chain/configure-your-chain/common-configurations/bold-adoption-for-arbitrum-chains', - label: 'BoLD for Arbitrum chains', - }, - { - type: 'doc', - id: 'launch-arbitrum-chain/configure-your-chain/common-configurations/timeboost-for-arbitrum-chains', - label: 'Timeboost for Arbitrum chains', - }, - { - type: 'category', - label: 'Data Availability Committees', - collapsed: true, - items: [ - { - type: 'doc', - id: 'run-arbitrum-node/data-availability-committees/get-started', - label: 'Get started', - }, - { - type: 'doc', - id: 'run-arbitrum-node/data-availability-committees/deploy-das', - label: 'Deploy a Data Availability Server (DAS)', - }, - { - type: 'doc', - id: 'run-arbitrum-node/data-availability-committees/deploy-mirror-das', - label: 'Deploy a mirror Data Availability Server', - }, - { - type: 'doc', - id: 'run-arbitrum-node/data-availability-committees/configure-dac', - label: 'Configure a Data Availability Committee (DAC)', - }, - ], - }, - { - type: 'doc', - id: 'launch-arbitrum-chain/how-tos/customize-deployment-configuration', - label: `Customize your chain's deployment`, - }, - { - type: 'doc', - id: 'launch-arbitrum-chain/reference/additional-configuration-parameters', - label: `Additional configuration parameters`, - }, - ], - }, - { - type: 'category', - label: 'Advanced features', - collapsed: true, - items: [ - { - type: 'doc', - id: 'launch-arbitrum-chain/configure-your-chain/advanced-configurations/fast-withdrawals', - label: `Enable fast withdrawals`, - }, - { - type: 'doc', - label: 'Use Layer Leap', - id: 'launch-arbitrum-chain/configure-your-chain/advanced-configurations/layer-leap', - }, - { - type: 'category', - label: 'Configure AEP fee routing', - collapsed: true, - items: [ - { - type: 'doc', - id: 'launch-arbitrum-chain/configure-your-chain/advanced-configurations/aep-fee-router/aep-fee-router-introduction', - label: `AEP fee router overview`, - }, - { - type: 'doc', - id: 'launch-arbitrum-chain/configure-your-chain/advanced-configurations/aep-fee-router/set-up-aep-fee-router', - label: `Set up AEP fee router`, - }, - { - type: 'doc', - id: 'launch-arbitrum-chain/configure-your-chain/advanced-configurations/aep-fee-router/calculate-aep-fees', - label: `Calculate AEP license fees`, - }, - { - type: 'doc', - id: 'launch-arbitrum-chain/configure-your-chain/advanced-configurations/aep-fee-router/reporting-on-fees', - label: `Report your fees`, - }, - ], - }, - ], - }, - ], - }, - { - type: 'category', - label: 'Deploy an Arbitrum chain', - collapsed: true, - items: [ - { - type: 'category', - label: 'Deploy a production chain', - collapsed: true, - items: [ - { - type: 'doc', - id: 'launch-arbitrum-chain/arbitrum-chain-sdk-introduction', - label: `Overview`, - }, - { - type: 'doc', - id: 'launch-arbitrum-chain/deploy-an-arbitrum-chain/deploying-an-arbitrum-chain', - label: `Deploy an Arbitrum chain`, - }, - { - type: 'doc', - id: 'launch-arbitrum-chain/how-tos/arbitrum-chain-sdk-preparing-node-config', - label: `Generate the node config file`, - }, - { - type: 'doc', - id: 'launch-arbitrum-chain/deploy-an-arbitrum-chain/deploying-token-bridge', - label: `Deploy a token bridge`, - }, - ], - }, - { - type: 'doc', - id: 'launch-arbitrum-chain/deploy-an-arbitrum-chain/canonical-factory-contracts', - label: 'Canonical factory contracts', - }, - ], - }, - { - type: 'category', - label: 'Maintain your chain', - collapsed: true, - items: [ - { - type: 'doc', - id: 'launch-arbitrum-chain/maintain-your-chain/ownership-structure-access-control', - label: 'Ownership structure and access control', - }, - { - type: 'category', - label: 'ArbOS', - collapsed: true, - items: [ - { - type: 'html', - value: - 'ArbOS software releases ', - // q: why use an anchor html tag here? - // a: see note at end of file - }, - { - type: 'doc', - id: 'launch-arbitrum-chain/configure-your-chain/common-configurations/arbos-upgrade', - label: `Upgrade ArbOS`, - }, - ], - }, - { - type: 'category', - label: 'Guidance', - collapsed: true, - items: [ - { - type: 'doc', - id: 'launch-arbitrum-chain/maintain-your-chain/guidance/state-growth', - label: `Manage gas state growth`, - }, - { - type: 'doc', - id: 'launch-arbitrum-chain/maintain-your-chain/guidance/state-size-limit', - label: `Manage gas speed limit`, - }, - { - type: 'doc', - id: 'launch-arbitrum-chain/maintain-your-chain/guidance/post-launch-contract-deployments', - label: `Post-launch deployments`, - }, - ], - }, - { - type: 'doc', - id: 'launch-arbitrum-chain/maintain-your-chain/monitoring-tools-and-considerations', - label: 'Monitoring tools and considerations', - }, - ], - }, - { - type: 'category', - label: 'Customize your chain', - collapsed: true, - items: [ - { - type: 'doc', - id: 'launch-arbitrum-chain/customize-your-chain/customize-precompile', - label: `Customize your chain's precompiles`, - }, - { - type: 'doc', - id: 'launch-arbitrum-chain/customize-your-chain/customize-stf', - label: `Customize your chain's behavior`, - }, - { - type: 'doc', - id: 'launch-arbitrum-chain/customize-your-chain/customize-arbos', - label: `Customize ArbOS version`, - }, - ], - }, - { - type: 'category', - label: 'Migrate your chain', - collapsed: true, - items: [ - { - type: 'doc', - id: 'launch-arbitrum-chain/migrate-from-another-stack', - label: 'Migrate from another stack', - }, - { - type: 'doc', - id: 'launch-arbitrum-chain/migrate-between-raases', - label: 'Migrate between RaaSes', - }, - ], - }, - { - type: 'category', - label: 'Third-party integrations and features', - collapsed: true, - items: [ - { - type: 'doc', - id: 'launch-arbitrum-chain/third-party-integrations/bridged-usdc-standard', - label: `Implement Circle bridged USDC`, - }, - { - type: 'doc', - id: 'launch-arbitrum-chain/third-party-integrations/third-party-providers', - label: 'Third-party infrastructure providers', - }, - ], - }, - { - type: 'category', - label: 'Run a node for an Arbitrum chain', - collapsed: true, - items: [ - { - type: 'html', - value: - 'Run a full node ', - // q: why use an anchor html tag here? - // a: see note at end of file - }, - { - type: 'html', - value: - 'Run a validator ', - // q: why use an anchor html tag here? - // a: see note at end of file - }, - { - type: 'html', - value: - 'Run a sequencer node ', - // q: why use an anchor html tag here? - // a: see note at end of file - }, - { - type: 'html', - value: - 'Run high-availability sequencer nodes ', - // q: why use an anchor html tag here? - // a: see note at end of file - }, - ], - }, - { - type: 'doc', - id: 'launch-arbitrum-chain/ecosystem-support/add-arbitrum-chain-to-bridge-ui', - label: `Add your chain to the bridge`, - }, - { - type: 'doc', - id: 'launch-arbitrum-chain/concepts/public-preview-expectations', - label: 'Public preview', - }, - { - type: 'doc', - id: 'launch-arbitrum-chain/faq-troubleshooting/troubleshooting-building-arbitrum-chain', - label: 'FAQ', - }, - ], - }, - { - type: 'category', - label: 'Run an Arbitrum node', - collapsed: true, - items: [ - { - type: 'doc', - id: 'run-arbitrum-node/overview', - label: 'Overview', - }, - { - type: 'doc', - id: 'run-arbitrum-node/run-full-node', - label: 'Run a full node', - }, - { - type: 'doc', - id: 'run-arbitrum-node/run-local-full-chain-simulation', - label: 'Run a local full chain simulation', - }, - { - type: 'doc', - id: 'run-arbitrum-node/run-nitro-dev-node', - label: 'Run a local dev node', - }, - { - type: 'doc', - id: 'run-arbitrum-node/l1-ethereum-beacon-chain-rpc-providers', - label: 'L1 Ethereum RPC providers', - }, - { - type: 'doc', - id: 'run-arbitrum-node/run-feed-relay', - label: 'Run a feed relay', - }, - { - type: 'html', - value: - 'Data Availability Committees ', - // q: why use an anchor html tag here? - // a: see note at end of file - }, - { - type: 'category', - label: 'ArbOS software releases', - collapsed: true, - items: [ - { - type: 'doc', - id: 'run-arbitrum-node/arbos-releases/overview', - label: 'Overview', - }, - { - type: 'doc', - id: 'run-arbitrum-node/arbos-releases/arbos40', - label: 'Callisto (ArbOS 40)', - }, - { - type: 'doc', - id: 'run-arbitrum-node/arbos-releases/arbos32', - label: 'Bianca (ArbOS 32)', - }, - { - type: 'doc', - id: 'run-arbitrum-node/arbos-releases/arbos20', - label: 'Atlas (ArbOS 20)', - }, - { - type: 'doc', - id: 'run-arbitrum-node/arbos-releases/arbos11', - label: 'ArbOS 11', - }, - ], - }, - { - type: 'category', - label: 'More node types', - collapsed: true, - items: [ - { - type: 'doc', - id: 'run-arbitrum-node/more-types/run-archive-node', - label: 'Run an archive node', - }, - { - type: 'doc', - id: 'run-arbitrum-node/more-types/run-validator-node', - label: 'Run a validator', - }, - { - type: 'doc', - id: 'run-arbitrum-node/more-types/run-split-validator-node', - label: 'Run a split validator', - }, - { - type: 'doc', - id: 'run-arbitrum-node/more-types/run-classic-node', - label: 'Run a Classic node', - }, - ], - }, - { - type: 'category', - label: 'Sequencer', - collapsed: true, - items: [ - { - type: 'doc', - id: 'run-arbitrum-node/sequencer/run-sequencer-node', - label: 'Run a sequencer node', - }, - { - type: 'doc', - id: 'run-arbitrum-node/sequencer/read-sequencer-feed', - label: 'Read the sequencer feed', - }, - { - type: 'doc', - id: 'run-arbitrum-node/sequencer/run-sequencer-coordination-manager', - label: 'Run a Sequencer Coordination Manager (SQM)', - }, - { - type: 'doc', - id: 'run-arbitrum-node/sequencer/high-availability-sequencer-docs', - label: 'Run high availability sequencer nodes', - }, - ], - }, - { - type: 'doc', - id: 'run-arbitrum-node/nitro/build-nitro-locally', - label: 'Build Nitro locally', - }, - { - type: 'doc', - id: 'run-arbitrum-node/nitro/migrate-state-and-history-from-classic', - label: 'Migrate to Nitro from Classic', - }, - { - type: 'doc', - id: 'run-arbitrum-node/nitro/nitro-database-snapshots', - label: 'Database snapshots', - }, - { - type: 'doc', - id: 'run-arbitrum-node/nitro/how-to-convert-databases-from-leveldb-to-pebble', - label: 'Convert databases from LevelDB to Pebble', - }, - , + label: 'Build dApps', + collapsed: true, + items: [ { type: 'doc', - id: 'run-arbitrum-node/troubleshooting', - label: 'Troubleshooting', + id: 'build-decentralized-apps/quickstart-solidity-remix', + label: 'Build dApps with Solidity', }, { type: 'doc', - label: 'FAQ', - id: 'node-running/faq', + id: 'stylus/quickstart', + label: 'Build dApps with Stylus', }, ], }, { - type: 'category', + type: 'link', + label: 'Run an Arbitrum (Orbit) chain', + href: 'launch-arbitrum-chain/a-gentle-introduction', + }, + { + type: 'link', + label: 'Run an Arbitrum node', + href: 'run-arbitrum-node/overview', + }, + { + type: 'link', label: 'Arbitrum bridge', - collapsed: true, - items: [ - { - type: 'doc', - id: 'arbitrum-bridge/quickstart', - label: 'Quickstart', - }, - { - type: 'doc', - id: 'arbitrum-bridge/usdc-arbitrum-one', - label: 'USDC on Arbitrum One', - }, - { - type: 'doc', - id: 'arbitrum-bridge/troubleshooting', - label: 'Troubleshooting', - }, - ], + href: 'run-arbitrum-node/overview', }, { - type: 'category', + type: 'link', label: 'How Arbitrum works', - collapsed: true, - items: [ - { - type: 'doc', - id: 'how-arbitrum-works/a-gentle-introduction', - label: 'A gentle introduction', - }, - { - type: 'doc', - id: 'how-arbitrum-works/transaction-lifecycle', - label: 'Sequencing, Followed by Deterministic Execution', - }, - { - type: 'doc', - id: 'how-arbitrum-works/sequencer', - label: 'The Sequencer and Censorship Resistance', - }, - { - type: 'doc', - id: 'how-arbitrum-works/l1-to-l2-messaging', - label: 'Parent to Child chain messaging', - }, - { - type: 'category', - label: 'State Transition Function', - items: [ - { - type: 'doc', - label: 'STF Overview', - id: 'how-arbitrum-works/state-transition-function/stf-gentle-intro', - }, - { - type: 'doc', - label: 'STF inputs', - id: 'how-arbitrum-works/state-transition-function/stf-inputs', - }, - { - type: 'doc', - label: 'Ethereum vs Arbitrum', - id: 'how-arbitrum-works/state-transition-function/ethereum-vs-arbitrum', - }, - { - type: 'doc', - label: 'Modified Geth on Arbitrum', - id: 'how-arbitrum-works/state-transition-function/modified-geth-on-arbitrum', - }, - { - type: 'doc', - label: 'ArbOS', - id: 'how-arbitrum-works/state-transition-function/arbos', - }, - { - type: 'doc', - label: 'Stylus execution path', - id: 'how-arbitrum-works/state-transition-function/stylus-execution-path', - }, - ], - }, - { - type: 'category', - label: 'Validation and Proving', - items: [ - { - type: 'doc', - label: 'Validation and proving overview', - id: 'how-arbitrum-works/validation-and-proving/validation-and-proving', - }, - { - type: 'doc', - label: 'Rollup protocol', - id: 'how-arbitrum-works/validation-and-proving/rollup-protocol', - }, - { - type: 'doc', - label: 'Proving and challenges', - id: 'how-arbitrum-works/validation-and-proving/proving-and-challenges', - }, - ], - }, - { - type: 'doc', - id: 'how-arbitrum-works/anytrust-protocol', - label: 'AnyTrust protocol', - }, - { - type: 'doc', - id: 'how-arbitrum-works/l2-to-l1-messaging', - label: 'Child to parent chain messaging', - }, - { - type: 'doc', - id: 'how-arbitrum-works/gas-fees', - label: 'Gas and fees', - }, - { - type: 'doc', - id: 'how-arbitrum-works/data-availability', - label: 'Data Availability', - }, - { - type: 'link', - href: 'https://github.com/OffchainLabs/nitro/blob/master/docs/Nitro-whitepaper.pdf', - label: 'Nitro whitepaper', - }, - - { - type: 'category', - label: 'The BoLD dispute protocol', - items: [ - { - type: 'doc', - id: 'how-arbitrum-works/bold/gentle-introduction', - label: 'A gentle introduction', - }, - { - type: 'link', - href: 'https://github.com/offchainlabs/bold-validator-starter-kit', - label: 'Deploy a validator on testnet', - }, - { - type: 'link', - href: 'https://arxiv.org/abs/2404.10491', - label: 'BoLD Whitepaper', - }, - { - type: 'doc', - id: 'how-arbitrum-works/bold/bold-technical-deep-dive', - label: 'Technical deep dive', - }, - { - type: 'doc', - id: 'how-arbitrum-works/bold/bold-economics-of-disputes', - label: 'Economics of disputes', - }, - { - type: 'link', - href: 'https://github.com/OffchainLabs/bold', - label: 'Specification on Github', - }, - { - type: 'link', - href: 'https://github.com/trailofbits/publications/blob/master/reviews/2024-04-offchainbold-securityreview.pdf', - label: 'Audit Report by Trail of Bits', - }, - { - type: 'link', - href: 'https://code4rena.com/reports/2024-05-arbitrum-foundation', - label: 'Audit Report by Code4rena', - }, - ], - }, - { - type: 'category', - label: 'Timeboost', - items: [ - { - type: 'doc', - id: 'how-arbitrum-works/timeboost/gentle-introduction', - label: 'Public preview', - }, - { - type: 'doc', - label: 'Use Timeboost', - id: 'how-arbitrum-works/timeboost/how-to-use-timeboost', - }, - { - type: 'doc', - label: 'Troubleshoot Timeboost', - id: 'how-arbitrum-works/timeboost/troubleshoot-timeboost', - }, - { - type: 'doc', - label: 'Timeboost FAQ', - id: 'how-arbitrum-works/timeboost/timeboost-faq', - }, - { - type: 'link', - href: 'https://github.com/OffchainLabs/timeboost-design/blob/main/research_spec.md', - label: 'Specification: Timeboost', - }, - { - type: 'link', - href: 'https://github.com/OffchainLabs/decentralized-timeboost-spec', - label: 'Specification: Timeboost with decentralized sequencing', - }, - { - type: 'link', - href: 'https://arxiv.org/abs/2306.02179', - label: 'White paper: Timeboost', - }, - ], - }, - ], + href: 'how-arbitrum-works/a-gentle-introduction', }, { type: 'doc', @@ -3256,9 +2172,9 @@ const sidebars = { collapsed: true, items: [ { - type: 'html', - value: - 'Chain Info', + type: 'doc', + id: 'for-devs/dev-tools-and-resources/chain-info', + label: 'Chain info', }, { type: 'doc', From 5aaf0160d87dc21d2e7284fccd05e4e2dbb84bef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Tue, 7 Oct 2025 10:54:58 -0700 Subject: [PATCH 46/62] update FAQ1 for on-ramp support request & remove playground request --- docs/arbitrum-bridge/04-embedded-bridge-widget.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/arbitrum-bridge/04-embedded-bridge-widget.mdx b/docs/arbitrum-bridge/04-embedded-bridge-widget.mdx index eab159d797..e50b36382a 100644 --- a/docs/arbitrum-bridge/04-embedded-bridge-widget.mdx +++ b/docs/arbitrum-bridge/04-embedded-bridge-widget.mdx @@ -65,7 +65,7 @@ The embedded bridge currently supports Arbitrum One, Base, Ethereum Mainnet, Ape You can adopt the bridge functionality permissionlessly if your chain is already supported. If your chain is already supported, visit the playground and accept the developer terms of service to generate code for the embedded bridge. -To receive a link to the playground, contact [partnerships@offchainlabs.com](mailto:partnerships@offchainlabs.com). If you are interested in on-ramp support, please mention this in your email. +If you are interested in on-ramp support, please contact [partnerships@offchainlabs.com](mailto:partnerships@offchainlabs.com). #### 2. If I'm an Orbit chain and I want support, how do I adopt? From 0a90eaef37c5607a4fe0673113bb95185bfe1f0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Tue, 7 Oct 2025 10:57:23 -0700 Subject: [PATCH 47/62] Revert "update FAQ1 for on-ramp support request & remove playground request" This reverts commit 5aaf0160d87dc21d2e7284fccd05e4e2dbb84bef. --- docs/arbitrum-bridge/04-embedded-bridge-widget.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/arbitrum-bridge/04-embedded-bridge-widget.mdx b/docs/arbitrum-bridge/04-embedded-bridge-widget.mdx index e50b36382a..eab159d797 100644 --- a/docs/arbitrum-bridge/04-embedded-bridge-widget.mdx +++ b/docs/arbitrum-bridge/04-embedded-bridge-widget.mdx @@ -65,7 +65,7 @@ The embedded bridge currently supports Arbitrum One, Base, Ethereum Mainnet, Ape You can adopt the bridge functionality permissionlessly if your chain is already supported. If your chain is already supported, visit the playground and accept the developer terms of service to generate code for the embedded bridge. -If you are interested in on-ramp support, please contact [partnerships@offchainlabs.com](mailto:partnerships@offchainlabs.com). +To receive a link to the playground, contact [partnerships@offchainlabs.com](mailto:partnerships@offchainlabs.com). If you are interested in on-ramp support, please mention this in your email. #### 2. If I'm an Orbit chain and I want support, how do I adopt? From 582e9c9181e28f5bc5bd322fc8495dcc2aa98e91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Tue, 7 Oct 2025 17:12:21 -0700 Subject: [PATCH 48/62] change frontmatter displayed_sidebar: attribution --- docs/build-decentralized-apps/01-quickstart-solidity-remix.mdx | 2 +- docs/build-decentralized-apps/02-how-to-estimate-gas.mdx | 2 +- docs/build-decentralized-apps/03-public-chains.mdx | 2 +- docs/build-decentralized-apps/04-cross-chain-messaging.mdx | 2 +- .../arbitrum-vs-ethereum/01-comparison-overview.mdx | 2 +- .../arbitrum-vs-ethereum/02-block-numbers-and-time.mdx | 2 +- .../arbitrum-vs-ethereum/03-rpc-methods.mdx | 2 +- .../arbitrum-vs-ethereum/04-solidity-support.mdx | 2 +- docs/build-decentralized-apps/custom-gas-token-sdk.mdx | 2 +- docs/build-decentralized-apps/nodeinterface/01-overview.mdx | 2 +- docs/build-decentralized-apps/nodeinterface/02-reference.mdx | 2 +- docs/build-decentralized-apps/oracles/01-overview.mdx | 2 +- docs/build-decentralized-apps/precompiles/01-overview.mdx | 2 +- docs/build-decentralized-apps/precompiles/02-reference.mdx | 2 +- docs/build-decentralized-apps/reference/01-node-providers.mdx | 2 +- .../reference/02-contract-addresses.mdx | 2 +- docs/build-decentralized-apps/reference/03-chain-params.mdx | 2 +- .../reference/04-development-frameworks.mdx | 2 +- .../reference/05-web3-libraries-tools.mdx | 2 +- .../reference/06-monitoring-tools-block-explorers.mdx | 2 +- docs/build-decentralized-apps/reference/07-debugging-tools.mdx | 2 +- docs/build-decentralized-apps/reference/08-mainnet-risks.mdx | 2 +- docs/build-decentralized-apps/token-bridging/01-overview.mdx | 2 +- .../token-bridging/02-token-bridge-ether.mdx | 2 +- .../token-bridging/03-token-bridge-erc20.mdx | 2 +- .../bridge-tokens-programmatically/01-get-started.mdx | 2 +- .../02-how-to-bridge-tokens-standard.mdx | 2 +- .../03-how-to-bridge-tokens-generic-custom.mdx | 2 +- .../04-how-to-bridge-tokens-custom-gateway.mdx | 2 +- docs/stylus/concepts/gas-metering.mdx | 2 +- docs/stylus/gentle-introduction.mdx | 2 +- docs/stylus/how-tos/adding-support-for-new-languages.mdx | 2 +- docs/stylus/how-tos/caching-contracts.mdx | 2 +- docs/stylus/how-tos/debugging-tx.mdx | 2 +- docs/stylus/how-tos/optimizing-binaries.mdx | 2 +- docs/stylus/how-tos/testing-contracts.mdx | 2 +- docs/stylus/how-tos/using-constructors.mdx | 2 +- docs/stylus/how-tos/using-inheritance.mdx | 2 +- docs/stylus/how-tos/verifying-contracts-arbiscan.mdx | 2 +- docs/stylus/how-tos/verifying-contracts.mdx | 2 +- docs/stylus/overview.mdx | 2 +- docs/stylus/quickstart.mdx | 2 +- docs/stylus/reference/project-structure.mdx | 2 +- docs/stylus/stylus-content-map.mdx | 2 +- docs/stylus/using-cli.mdx | 2 +- 45 files changed, 45 insertions(+), 45 deletions(-) diff --git a/docs/build-decentralized-apps/01-quickstart-solidity-remix.mdx b/docs/build-decentralized-apps/01-quickstart-solidity-remix.mdx index bc1a09d583..29adb8c9e3 100644 --- a/docs/build-decentralized-apps/01-quickstart-solidity-remix.mdx +++ b/docs/build-decentralized-apps/01-quickstart-solidity-remix.mdx @@ -5,7 +5,7 @@ author: symbolpunk user_story: As a web2 developer, I want to onboard into Arbitrum by building and deploying my first smart contract, and knowing how to build a web widget interacting with it. content_type: quickstart slug: /build-decentralized-apps/quickstart-solidity-remix -displayed_sidebar: buildAppsSidebarFromGetStarted +displayed_sidebar: buildAppsSidebar --- import { VanillaAdmonition } from '@site/src/components/VanillaAdmonition/'; diff --git a/docs/build-decentralized-apps/02-how-to-estimate-gas.mdx b/docs/build-decentralized-apps/02-how-to-estimate-gas.mdx index 6bdd4dfe1d..0f7f93b980 100644 --- a/docs/build-decentralized-apps/02-how-to-estimate-gas.mdx +++ b/docs/build-decentralized-apps/02-how-to-estimate-gas.mdx @@ -3,7 +3,7 @@ title: 'How to estimate gas in Arbitrum' description: Learn how to estimate gas before submitting transactions. author: TucksonDev content_type: how-to -displayed_sidebar: buildAppsSidebarFromGetStarted +displayed_sidebar: buildAppsSidebar --- :::info Looking for Stylus guidance? diff --git a/docs/build-decentralized-apps/03-public-chains.mdx b/docs/build-decentralized-apps/03-public-chains.mdx index 622cd63797..46912181bc 100644 --- a/docs/build-decentralized-apps/03-public-chains.mdx +++ b/docs/build-decentralized-apps/03-public-chains.mdx @@ -3,7 +3,7 @@ title: 'Arbitrum chains overview' description: A high level description of the Arbitrum chains available user_story: As a developer, I want to understand the different Arbitrum chains and how they relate to each other. content_type: concept -displayed_sidebar: buildAppsSidebarFromGetStarted +displayed_sidebar: buildAppsSidebar --- import { AddressExplorerLink as AEL } from '@site/src/components/AddressExplorerLink'; diff --git a/docs/build-decentralized-apps/04-cross-chain-messaging.mdx b/docs/build-decentralized-apps/04-cross-chain-messaging.mdx index b19d030081..a9e53218d8 100644 --- a/docs/build-decentralized-apps/04-cross-chain-messaging.mdx +++ b/docs/build-decentralized-apps/04-cross-chain-messaging.mdx @@ -3,7 +3,7 @@ title: 'Cross-chain messaging overview' description: Learn about cross-chain messaging in Arbitrum user_story: As a developer, I want to understand how cross-chain messaging works in Arbitrum. content_type: concept -displayed_sidebar: buildAppsSidebarFromGetStarted +displayed_sidebar: buildAppsSidebar --- The Arbitrum protocol and related tooling makes it easy for developers to build cross-chain applications; i.e., applications that involve sending messages from Ethereum to an Arbitrum chain, and/or from an Arbitrum chain to Ethereum. diff --git a/docs/build-decentralized-apps/arbitrum-vs-ethereum/01-comparison-overview.mdx b/docs/build-decentralized-apps/arbitrum-vs-ethereum/01-comparison-overview.mdx index 4e64cb7ff8..bdc949749a 100644 --- a/docs/build-decentralized-apps/arbitrum-vs-ethereum/01-comparison-overview.mdx +++ b/docs/build-decentralized-apps/arbitrum-vs-ethereum/01-comparison-overview.mdx @@ -6,7 +6,7 @@ author: jose-franco sme: jose-franco target_audience: developers who want to build on Arbitrum content_type: concept -displayed_sidebar: buildAppsSidebarFromGetStarted +displayed_sidebar: buildAppsSidebar --- Arbitrum's design is to be as compatible and consistent with Ethereum as possible, from its high-level RPCs to its low-level bytecode and everything in between. Decentralized app (dApp) developers with experience building on Ethereum will likely find that little to no new specific knowledge is required to build on Arbitrum. diff --git a/docs/build-decentralized-apps/arbitrum-vs-ethereum/02-block-numbers-and-time.mdx b/docs/build-decentralized-apps/arbitrum-vs-ethereum/02-block-numbers-and-time.mdx index 34786867d0..c2f81883bf 100644 --- a/docs/build-decentralized-apps/arbitrum-vs-ethereum/02-block-numbers-and-time.mdx +++ b/docs/build-decentralized-apps/arbitrum-vs-ethereum/02-block-numbers-and-time.mdx @@ -6,7 +6,7 @@ author: dzgoldman, jose-franco sme: jose-franco target_audience: developers who want to build on Arbitrum content_type: concept -displayed_sidebar: buildAppsSidebarFromGetStarted +displayed_sidebar: buildAppsSidebar --- :::info block number vs `block.number` diff --git a/docs/build-decentralized-apps/arbitrum-vs-ethereum/03-rpc-methods.mdx b/docs/build-decentralized-apps/arbitrum-vs-ethereum/03-rpc-methods.mdx index 2005a5120b..606a93e2ba 100644 --- a/docs/build-decentralized-apps/arbitrum-vs-ethereum/03-rpc-methods.mdx +++ b/docs/build-decentralized-apps/arbitrum-vs-ethereum/03-rpc-methods.mdx @@ -5,7 +5,7 @@ description: This concept page provides information about the differences betwee target_audience: developers who want to build on Arbitrum author: dzgoldman content_type: concept -displayed_sidebar: buildAppsSidebarFromGetStarted +displayed_sidebar: buildAppsSidebar --- Although the majority of RPC methods follow the same behavior as in Ethereum, some methods may produce a different result or add more information when used on an Arbitrum chain. This page covers the differences in response body fields you'll find when calling RPC methods on an Arbitrum chain vs on Ethereum. diff --git a/docs/build-decentralized-apps/arbitrum-vs-ethereum/04-solidity-support.mdx b/docs/build-decentralized-apps/arbitrum-vs-ethereum/04-solidity-support.mdx index 9c08b027e8..e74671e3a9 100644 --- a/docs/build-decentralized-apps/arbitrum-vs-ethereum/04-solidity-support.mdx +++ b/docs/build-decentralized-apps/arbitrum-vs-ethereum/04-solidity-support.mdx @@ -5,7 +5,7 @@ description: This concept page provides information about the differences betwee target_audience: developers who want to build on Arbitrum author: dzgoldman content_type: concept -displayed_sidebar: buildAppsSidebarFromGetStarted +displayed_sidebar: buildAppsSidebar --- Arbitrum chains are Ethereum-compatible and, therefore, allow you to trustlessly deploy Solidity smart contracts, as well as contracts written in Vyper or any other language that compiles to EVM bytecode. However, when calling certain properties and functions on a Solidity smart contract, there are some differences between the result you'd obtain if that contract were on Ethereum and the result on Arbitrum. diff --git a/docs/build-decentralized-apps/custom-gas-token-sdk.mdx b/docs/build-decentralized-apps/custom-gas-token-sdk.mdx index 1eb728e58f..069bae5e05 100644 --- a/docs/build-decentralized-apps/custom-gas-token-sdk.mdx +++ b/docs/build-decentralized-apps/custom-gas-token-sdk.mdx @@ -5,7 +5,7 @@ author: Mehdi Salehi sme: Mehdi Salehi target_audience: 'Developers deploying and maintaining Arbitrum chains.' sidebar_position: 2 -displayed_sidebar: buildAppsSidebarFromGetStarted +displayed_sidebar: buildAppsSidebar --- Arbitrum SDK is a TypeScript library for client-side interactions with Arbitrum. It provides common helper functionality as well as access to the underlying smart contract interfaces. diff --git a/docs/build-decentralized-apps/nodeinterface/01-overview.mdx b/docs/build-decentralized-apps/nodeinterface/01-overview.mdx index e1b5b95e54..b976c31a1e 100644 --- a/docs/build-decentralized-apps/nodeinterface/01-overview.mdx +++ b/docs/build-decentralized-apps/nodeinterface/01-overview.mdx @@ -3,7 +3,7 @@ title: 'NodeInterface overview' description: A high level description of what the NodeInterface is and how it works user_story: As a developer, I want to understand what the NodeInterface is and how it works. content_type: concept -displayed_sidebar: buildAppsSidebarFromGetStarted +displayed_sidebar: buildAppsSidebar --- diff --git a/docs/build-decentralized-apps/nodeinterface/02-reference.mdx b/docs/build-decentralized-apps/nodeinterface/02-reference.mdx index d99e8c9af8..023bcc2db5 100644 --- a/docs/build-decentralized-apps/nodeinterface/02-reference.mdx +++ b/docs/build-decentralized-apps/nodeinterface/02-reference.mdx @@ -3,7 +3,7 @@ title: 'NodeInterface reference' description: A reference page of the NodeInterface available on Arbitrum chains user_story: As a developer, I want to understand the specific methods available in the NodeInterface content_type: reference -displayed_sidebar: buildAppsSidebarFromGetStarted +displayed_sidebar: buildAppsSidebar --- The Arbitrum Nitro software includes a special `NodeInterface` contract available at address `0xc8` that is only accessible via RPCs (it's not actually deployed onchain, and thus can't be called by smart contracts). This reference page documents the specific calls available in the `NodeInterface`. For a more conceptual description of what it is and how it works, please refer to the [`NodeInterface` conceptual page](/build-decentralized-apps/nodeinterface/01-overview.mdx). diff --git a/docs/build-decentralized-apps/oracles/01-overview.mdx b/docs/build-decentralized-apps/oracles/01-overview.mdx index 262c403070..39920000f8 100644 --- a/docs/build-decentralized-apps/oracles/01-overview.mdx +++ b/docs/build-decentralized-apps/oracles/01-overview.mdx @@ -5,7 +5,7 @@ description: A high level description of what oracles are user_story: As a developer, I want to understand what oracles are and how they work. content_type: concept sidebar_label: Oracles -displayed_sidebar: buildAppsSidebarFromGetStarted +displayed_sidebar: buildAppsSidebar --- import ImageZoom from '@site/src/components/ImageZoom'; diff --git a/docs/build-decentralized-apps/precompiles/01-overview.mdx b/docs/build-decentralized-apps/precompiles/01-overview.mdx index 97fcd2eb1f..6b87a8c694 100644 --- a/docs/build-decentralized-apps/precompiles/01-overview.mdx +++ b/docs/build-decentralized-apps/precompiles/01-overview.mdx @@ -3,7 +3,7 @@ title: 'Precompiles overview' description: A high level description of what precompiles are and how they work user_story: As a developer, I want to understand what precompiles are and how they work. content_type: concept -displayed_sidebar: buildAppsSidebarFromGetStarted +displayed_sidebar: buildAppsSidebar --- Precompiles are predefined smart contracts that have special addresses and provide specific functionality which is executed not at the EVM bytecode level, but natively by the Arbitrum client itself. Precompiles are primarily used to introduce specific functions that would be computationally expensive if executed in EVM bytecode, and functions that facilitate the interaction between the parent chain and the child chain. By having them natively in the Arbitrum client, they can be optimized for performance. diff --git a/docs/build-decentralized-apps/precompiles/02-reference.mdx b/docs/build-decentralized-apps/precompiles/02-reference.mdx index b8452bf456..b982d0eb3c 100644 --- a/docs/build-decentralized-apps/precompiles/02-reference.mdx +++ b/docs/build-decentralized-apps/precompiles/02-reference.mdx @@ -3,7 +3,7 @@ title: 'Precompiles reference' description: A reference page of all precompiles available on Arbitrum chains user_story: As a developer, I want to understand the most useful precompiles available on Arbitrum chains and how to use them. content_type: reference -displayed_sidebar: buildAppsSidebarFromGetStarted +displayed_sidebar: buildAppsSidebar --- ArbOS provides child chain-specific precompiles with methods smart contracts can call the same way they can solidity functions. This reference page exhaustively documents the specific calls ArbOS makes available through precompiles. For a more conceptual description of what precompiles are and how they work, please refer to the [precompiles conceptual page](/build-decentralized-apps/precompiles/01-overview.mdx). diff --git a/docs/build-decentralized-apps/reference/01-node-providers.mdx b/docs/build-decentralized-apps/reference/01-node-providers.mdx index dc2f1eb440..1a6016b0cd 100644 --- a/docs/build-decentralized-apps/reference/01-node-providers.mdx +++ b/docs/build-decentralized-apps/reference/01-node-providers.mdx @@ -3,7 +3,7 @@ title: 'RPC endpoints and providers' description: Find available RPC endpoints and providers in the ecosystem reader_audience: developers who want to build on Arbitrum content_type: overview -displayed_sidebar: buildAppsSidebarFromGetStarted +displayed_sidebar: buildAppsSidebar --- import ArbitrumRpcEndpoints from '../../partials/_reference-arbitrum-rpc-endpoints-partial.mdx'; diff --git a/docs/build-decentralized-apps/reference/02-contract-addresses.mdx b/docs/build-decentralized-apps/reference/02-contract-addresses.mdx index 5f8781451c..01c4f451dd 100644 --- a/docs/build-decentralized-apps/reference/02-contract-addresses.mdx +++ b/docs/build-decentralized-apps/reference/02-contract-addresses.mdx @@ -6,7 +6,7 @@ author: anegg0 sme: anegg0 user_story: As a current or prospective Arbitrum user I need to know to what addresses Arbitrum contracts have been deployed. content_type: reference -displayed_sidebar: buildAppsSidebarFromGetStarted +displayed_sidebar: buildAppsSidebar --- import ArbitrumContractAddresses from '../../partials/_reference-arbitrum-contract-addresses-partial.mdx'; diff --git a/docs/build-decentralized-apps/reference/03-chain-params.mdx b/docs/build-decentralized-apps/reference/03-chain-params.mdx index 9be8684f24..221f35769a 100644 --- a/docs/build-decentralized-apps/reference/03-chain-params.mdx +++ b/docs/build-decentralized-apps/reference/03-chain-params.mdx @@ -3,7 +3,7 @@ title: 'Chain parameters' description: Information about important system parameters for public Arbitrum chains user_story: As a developer, I want to understand the system parameters for the public Arbitrum chains. content_type: overview -displayed_sidebar: buildAppsSidebarFromGetStarted +displayed_sidebar: buildAppsSidebar --- | Param | Description | Arbitrum One | Arbitrum Nova | Arb Sepolia | diff --git a/docs/build-decentralized-apps/reference/04-development-frameworks.mdx b/docs/build-decentralized-apps/reference/04-development-frameworks.mdx index d871310e84..8b7ac1eccb 100644 --- a/docs/build-decentralized-apps/reference/04-development-frameworks.mdx +++ b/docs/build-decentralized-apps/reference/04-development-frameworks.mdx @@ -3,7 +3,7 @@ title: 'Development frameworks' description: An overview of popular development frameworks that exist in the Arbitrum ecosystem user_story: As a developer, I want to understand the popular development frameworks that exist in the Arbitrum ecosystem. content_type: overview -displayed_sidebar: buildAppsSidebarFromGetStarted +displayed_sidebar: buildAppsSidebar --- import KnowMoreToolsBox from '../../for-devs/partials/_know-more-tools-box-partial.mdx'; diff --git a/docs/build-decentralized-apps/reference/05-web3-libraries-tools.mdx b/docs/build-decentralized-apps/reference/05-web3-libraries-tools.mdx index cec005986f..103f5582f1 100644 --- a/docs/build-decentralized-apps/reference/05-web3-libraries-tools.mdx +++ b/docs/build-decentralized-apps/reference/05-web3-libraries-tools.mdx @@ -3,7 +3,7 @@ title: 'Web3 libraries and tools' description: An overview of some popular Web3 libraries that help developers interact with the Ethereum and Arbitrum blockchains. user_story: As a developer, I want to understand what Web3 libraries and tools are available in the Ethereum and Arbitrum ecosystems. content_type: overview -displayed_sidebar: buildAppsSidebarFromGetStarted +displayed_sidebar: buildAppsSidebar --- import KnowMoreToolsBox from '../../for-devs/partials/_know-more-tools-box-partial.mdx'; diff --git a/docs/build-decentralized-apps/reference/06-monitoring-tools-block-explorers.mdx b/docs/build-decentralized-apps/reference/06-monitoring-tools-block-explorers.mdx index 96e646f404..2b59378770 100644 --- a/docs/build-decentralized-apps/reference/06-monitoring-tools-block-explorers.mdx +++ b/docs/build-decentralized-apps/reference/06-monitoring-tools-block-explorers.mdx @@ -3,7 +3,7 @@ title: 'Monitoring tools and block explorers' description: An overview of popular monitoring tools and block explorers that exist in the Arbitrum ecosystem user_story: As a developer, I want to understand what monitoring tools and block explorers are available in the Arbitrum ecosystem. content_type: overview -displayed_sidebar: buildAppsSidebarFromGetStarted +displayed_sidebar: buildAppsSidebar --- import KnowMoreToolsBox from '../../for-devs/partials/_know-more-tools-box-partial.mdx'; diff --git a/docs/build-decentralized-apps/reference/07-debugging-tools.mdx b/docs/build-decentralized-apps/reference/07-debugging-tools.mdx index 844569b670..e6c6ce46b3 100644 --- a/docs/build-decentralized-apps/reference/07-debugging-tools.mdx +++ b/docs/build-decentralized-apps/reference/07-debugging-tools.mdx @@ -3,7 +3,7 @@ title: 'Debugging tools' description: An overview of popular debugging tools that exist in the Arbitrum ecosystem user_story: As a developer, I want to understand what debugging tools are available in the Arbitrum ecosystem. content_type: overview -displayed_sidebar: buildAppsSidebarFromGetStarted +displayed_sidebar: buildAppsSidebar --- import KnowMoreToolsBox from '../../for-devs/partials/_know-more-tools-box-partial.mdx'; diff --git a/docs/build-decentralized-apps/reference/08-mainnet-risks.mdx b/docs/build-decentralized-apps/reference/08-mainnet-risks.mdx index 9e7170a668..6c0005677a 100644 --- a/docs/build-decentralized-apps/reference/08-mainnet-risks.mdx +++ b/docs/build-decentralized-apps/reference/08-mainnet-risks.mdx @@ -2,7 +2,7 @@ title: 'Arbitrum: Understanding the risks' description: 'Understand the risks associated with cutting-edge software development' author: dzgoldman -displayed_sidebar: buildAppsSidebarFromGetStarted +displayed_sidebar: buildAppsSidebar --- # Arbitrum: Understanding the risks diff --git a/docs/build-decentralized-apps/token-bridging/01-overview.mdx b/docs/build-decentralized-apps/token-bridging/01-overview.mdx index fd003e61c3..139e5b1ccd 100644 --- a/docs/build-decentralized-apps/token-bridging/01-overview.mdx +++ b/docs/build-decentralized-apps/token-bridging/01-overview.mdx @@ -5,7 +5,7 @@ author: dzgoldman user_story: As a developer, I want to understand how the token bridge works and what options exist to bridge assets between layers. content_type: overview sidebar_position: 1 -displayed_sidebar: buildAppsSidebarFromGetStarted +displayed_sidebar: buildAppsSidebar --- Token bridging is a fundamental aspect of any Layer 2 (child chain) protocol. Arbitrum uses its ability to pass messages between parent and child chains (see [Cross-chain messaging](/build-decentralized-apps/04-cross-chain-messaging.mdx)) to enable projects to move assets between Ethereum and an Arbitrum chain trustlessly, and vice versa. Any asset and asset type in principle can be bridged, including `ETH`, `ERC-20` tokens, and `ERC-721` tokens, among others. diff --git a/docs/build-decentralized-apps/token-bridging/02-token-bridge-ether.mdx b/docs/build-decentralized-apps/token-bridging/02-token-bridge-ether.mdx index 07f9f1e24c..d51c2789aa 100644 --- a/docs/build-decentralized-apps/token-bridging/02-token-bridge-ether.mdx +++ b/docs/build-decentralized-apps/token-bridging/02-token-bridge-ether.mdx @@ -5,7 +5,7 @@ author: dzgoldman user_story: As a developer, I want to understand how bridging ether works on Arbitrum content_type: concept sidebar_position: 2 -displayed_sidebar: buildAppsSidebarFromGetStarted +displayed_sidebar: buildAppsSidebar --- import ImageZoom from '@site/src/components/ImageZoom'; diff --git a/docs/build-decentralized-apps/token-bridging/03-token-bridge-erc20.mdx b/docs/build-decentralized-apps/token-bridging/03-token-bridge-erc20.mdx index 6d18ff2712..5d9e8e6f43 100644 --- a/docs/build-decentralized-apps/token-bridging/03-token-bridge-erc20.mdx +++ b/docs/build-decentralized-apps/token-bridging/03-token-bridge-erc20.mdx @@ -5,7 +5,7 @@ author: dzgoldman user_story: As a developer, I want to understand how ERC-20 token bridging works on Arbitrum, and the architecture of the token bridge. content_type: concept sidebar_position: 3 -displayed_sidebar: buildAppsSidebarFromGetStarted +displayed_sidebar: buildAppsSidebar --- import ImageZoom from '@site/src/components/ImageZoom'; diff --git a/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/01-get-started.mdx b/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/01-get-started.mdx index 7b86d0c5a3..93ed153406 100644 --- a/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/01-get-started.mdx +++ b/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/01-get-started.mdx @@ -3,7 +3,7 @@ title: 'Get started with token bridging' description: Learn the different options available to bridge tokens programmatically user_story: As a developer, I want to understand how to bridge tokens between Ethereum and Arbitrum. content_type: overview -displayed_sidebar: buildAppsSidebarFromGetStarted +displayed_sidebar: buildAppsSidebar --- Token bridging is a fundamental aspect of any child chain protocol. It allows projects to quickly integrate with the Arbitrum ecosystem by leveraging their existing parent chain tokens. diff --git a/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/02-how-to-bridge-tokens-standard.mdx b/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/02-how-to-bridge-tokens-standard.mdx index ca939f42b7..1059d2b537 100644 --- a/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/02-how-to-bridge-tokens-standard.mdx +++ b/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/02-how-to-bridge-tokens-standard.mdx @@ -3,7 +3,7 @@ title: "Bridge tokens via Arbitrum's standard `ERC-20` gateway" description: Learn how to programmatically bridge tokens between Ethereum and Arbitrum using Arbitrum’s standard ER-C20 gateway user_story: As a developer, I want to understand how to bridge tokens between Ethereum and Arbitrum using the standard ER-C20 gateway. content_type: how-to -displayed_sidebar: buildAppsSidebarFromGetStarted +displayed_sidebar: buildAppsSidebar --- In this how-to, you’ll learn how to bridge your own token between Ethereum (parent chain) and Arbitrum (child chain), using [Arbitrum’s standard `ERC20` gateway](/build-decentralized-apps/token-bridging/03-token-bridge-erc20.mdx#default-standard-bridging). For alternative ways of bridging tokens, don’t forget to check out this [overview](/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/01-get-started.mdx). diff --git a/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/03-how-to-bridge-tokens-generic-custom.mdx b/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/03-how-to-bridge-tokens-generic-custom.mdx index ccbfb0bad7..81c8acd8f9 100644 --- a/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/03-how-to-bridge-tokens-generic-custom.mdx +++ b/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/03-how-to-bridge-tokens-generic-custom.mdx @@ -3,7 +3,7 @@ title: 'Bridge tokens via Arbitrum’s generic-custom gateway' description: Learn how to use the generic-custom gateway to bridge tokens programmatically user_story: As a developer, I want to understand how to bridge tokens between Ethereum and Arbitrum using the generic-custom gateway content_type: how-to -displayed_sidebar: buildAppsSidebarFromGetStarted +displayed_sidebar: buildAppsSidebar --- In this how-to, you’ll learn how to bridge your own token between Ethereum (parent chain) and Arbitrum (child chain), using [Arbitrum’s generic-custom gateway](/build-decentralized-apps/token-bridging/03-token-bridge-erc20.mdx#the-arbitrum-generic-custom-gateway). For alternative ways of bridging tokens, don’t forget to check out this [overview](/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/01-get-started.mdx). diff --git a/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/04-how-to-bridge-tokens-custom-gateway.mdx b/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/04-how-to-bridge-tokens-custom-gateway.mdx index 20cf36879f..29b7fbb608 100644 --- a/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/04-how-to-bridge-tokens-custom-gateway.mdx +++ b/docs/build-decentralized-apps/token-bridging/bridge-tokens-programmatically/04-how-to-bridge-tokens-custom-gateway.mdx @@ -3,7 +3,7 @@ title: 'How to bridge tokens via a custom gateway' description: Learn how to set up a custom gateway using Arbitrum's Token Bridge to bridge tokens programmatically reader_audience: developers who want to build on Ethereum/Arbitrum and bridge tokens between layers content_type: how-to -displayed_sidebar: buildAppsSidebarFromGetStarted +displayed_sidebar: buildAppsSidebar --- :::caution Do you really need a custom gateway? diff --git a/docs/stylus/concepts/gas-metering.mdx b/docs/stylus/concepts/gas-metering.mdx index a0164561c0..9354556719 100644 --- a/docs/stylus/concepts/gas-metering.mdx +++ b/docs/stylus/concepts/gas-metering.mdx @@ -5,7 +5,7 @@ author: rachel-bousfield sme: rachel-bousfield target_audience: 'Developers deploying smart contracts using Stylus.' sidebar_position: 3 -displayed_sidebar: buildAppsSidebarFromStylus +displayed_sidebar: buildAppsSidebar --- **Gas and ink** are the pricing primitives that are used to determine the cost of handling specific opcodes and host I/Os on Stylus. For an overview of specific opcode and host I/O costs, see [Gas and ink costs](/stylus/reference/opcode-hostio-pricing). diff --git a/docs/stylus/gentle-introduction.mdx b/docs/stylus/gentle-introduction.mdx index ae6a4f8256..6662a24ab3 100644 --- a/docs/stylus/gentle-introduction.mdx +++ b/docs/stylus/gentle-introduction.mdx @@ -6,7 +6,7 @@ author: amarrazza sme: amarrazza target_audience: 'Developers who want to build on Arbitrum using popular programming languages, like Rust' sidebar_position: 1 -displayed_sidebar: buildAppsSidebarFromStylus +displayed_sidebar: buildAppsSidebar --- import ImageZoom from '@site/src/components/ImageZoom'; diff --git a/docs/stylus/how-tos/adding-support-for-new-languages.mdx b/docs/stylus/how-tos/adding-support-for-new-languages.mdx index f6efe6a2d5..03f167dc7b 100644 --- a/docs/stylus/how-tos/adding-support-for-new-languages.mdx +++ b/docs/stylus/how-tos/adding-support-for-new-languages.mdx @@ -6,7 +6,7 @@ sme: rauljordan target_audience: 'Developers deploying smart contracts using Stylus' content_type: how-to sidebar_position: 1 -displayed_sidebar: buildAppsSidebarFromStylus +displayed_sidebar: buildAppsSidebar --- [Arbitrum Stylus](../gentle-introduction.mdx) is a new technology developed for Arbitrum chains which gives smart contract developers superpowers. With Stylus, developers can write EVM-compatible smart contracts in many different programming languages, and reap massive performance gains. Stylus slashes fees, with performance gains ranging from 10-70x, and memory efficiency gains as high as 100-500x. diff --git a/docs/stylus/how-tos/caching-contracts.mdx b/docs/stylus/how-tos/caching-contracts.mdx index b04127c701..92c3a75094 100644 --- a/docs/stylus/how-tos/caching-contracts.mdx +++ b/docs/stylus/how-tos/caching-contracts.mdx @@ -5,7 +5,7 @@ description: 'A conceptual overview of the Stylus caching strategy and CacheMana sme: mahsa-moosavi target_audience: 'Developers deploying smart contracts using Stylus.' sidebar_position: 3 -displayed_sidebar: buildAppsSidebarFromStylus +displayed_sidebar: buildAppsSidebar --- Stylus is designed for fast computation and efficiency. However, diff --git a/docs/stylus/how-tos/debugging-tx.mdx b/docs/stylus/how-tos/debugging-tx.mdx index f2b17322b4..c47cb69116 100644 --- a/docs/stylus/how-tos/debugging-tx.mdx +++ b/docs/stylus/how-tos/debugging-tx.mdx @@ -7,7 +7,7 @@ sme: mahsamoosavi target_audience: 'Developers deploying smart contracts using Stylus' content_type: how-to sidebar_position: 2 -displayed_sidebar: buildAppsSidebarFromStylus +displayed_sidebar: buildAppsSidebar --- Debugging smart contracts can be challenging, especially when dealing with complex transactions. The `cargo-stylus` crate simplifies the debugging process by allowing developers to replay Stylus transactions. This tool leverages GDB to provide an interactive debugging experience, enabling developers to set breakpoints, inspect state changes, and trace the execution flow step-by-step. This capability is crucial for identifying and resolving issues, ensuring that smart contracts function correctly and efficiently. diff --git a/docs/stylus/how-tos/optimizing-binaries.mdx b/docs/stylus/how-tos/optimizing-binaries.mdx index 67b9cfbd88..d88274f8ad 100644 --- a/docs/stylus/how-tos/optimizing-binaries.mdx +++ b/docs/stylus/how-tos/optimizing-binaries.mdx @@ -6,7 +6,7 @@ sme: rauljordan target_audience: 'Developers deploying smart contracts using Stylus' content_type: how-to sidebar_position: 1 -displayed_sidebar: buildAppsSidebarFromStylus +displayed_sidebar: buildAppsSidebar --- To be deployed onchain, the size of your **uncompressed WebAssembly (WASM) file** must not exceed 128Kb, while the **compressed binary** must not exceed 24KB. Stylus conforms with the same contract size limit as the EVM to remain fully interoperable with all smart contracts on Arbitrum chains. diff --git a/docs/stylus/how-tos/testing-contracts.mdx b/docs/stylus/how-tos/testing-contracts.mdx index 1c3e98e405..dd61568bb5 100644 --- a/docs/stylus/how-tos/testing-contracts.mdx +++ b/docs/stylus/how-tos/testing-contracts.mdx @@ -5,7 +5,7 @@ description: 'A comprehensive guide to writing and running tests for Stylus smar sme: anegg0 target_audience: 'Developers writing smart contracts using Stylus.' sidebar_position: 3 -displayed_sidebar: buildAppsSidebarFromStylus +displayed_sidebar: buildAppsSidebar --- import CustomDetails from '@site/src/components/CustomDetails'; diff --git a/docs/stylus/how-tos/using-constructors.mdx b/docs/stylus/how-tos/using-constructors.mdx index 8275417b69..c50a713bd4 100644 --- a/docs/stylus/how-tos/using-constructors.mdx +++ b/docs/stylus/how-tos/using-constructors.mdx @@ -7,7 +7,7 @@ sme: 'anegg0' user_story: 'As a Rust developer, I want to understand how to implement and use constructors in Stylus smart contracts' content_type: 'how-to' sidebar_position: 3 -displayed_sidebar: buildAppsSidebarFromStylus +displayed_sidebar: buildAppsSidebar --- import CustomDetails from '@site/src/components/CustomDetails'; diff --git a/docs/stylus/how-tos/using-inheritance.mdx b/docs/stylus/how-tos/using-inheritance.mdx index f3c4212f05..155ff8813f 100644 --- a/docs/stylus/how-tos/using-inheritance.mdx +++ b/docs/stylus/how-tos/using-inheritance.mdx @@ -5,7 +5,7 @@ author: anegg0 sme: mahsamoosavi content_type: how-to sidebar_position: 1 -displayed_sidebar: buildAppsSidebarFromStylus +displayed_sidebar: buildAppsSidebar --- import CustomDetails from '@site/src/components/CustomDetails'; diff --git a/docs/stylus/how-tos/verifying-contracts-arbiscan.mdx b/docs/stylus/how-tos/verifying-contracts-arbiscan.mdx index 6d485e4196..358f20ba2c 100644 --- a/docs/stylus/how-tos/verifying-contracts-arbiscan.mdx +++ b/docs/stylus/how-tos/verifying-contracts-arbiscan.mdx @@ -6,7 +6,7 @@ sme: mahsamoosavi target_audience: 'Developers deploying smart contracts using Stylus' content_type: how-to sidebar_position: 1 -displayed_sidebar: buildAppsSidebarFromStylus +displayed_sidebar: buildAppsSidebar --- import ImageZoom from '@site/src/components/ImageZoom'; diff --git a/docs/stylus/how-tos/verifying-contracts.mdx b/docs/stylus/how-tos/verifying-contracts.mdx index acbc706610..819ec19f4b 100644 --- a/docs/stylus/how-tos/verifying-contracts.mdx +++ b/docs/stylus/how-tos/verifying-contracts.mdx @@ -8,7 +8,7 @@ target_audience: 'Developers learning how to use Stylus' sidebar_position: 2 user_story: 'As a current or prospective Stylus user, I want to learn how to make sure my Stylus contracts deployment are reproducible by anyone running the same environment.' content_type: how-to -displayed_sidebar: buildAppsSidebarFromStylus +displayed_sidebar: buildAppsSidebar --- :::caution diff --git a/docs/stylus/overview.mdx b/docs/stylus/overview.mdx index d1a519fb9b..10b1700705 100644 --- a/docs/stylus/overview.mdx +++ b/docs/stylus/overview.mdx @@ -2,7 +2,7 @@ id: stylus-overview title: Write Stylus Contracts sidebar_label: Write Stylus contracts -displayed_sidebar: buildAppsSidebarFromStylus +displayed_sidebar: buildAppsSidebar --- import Card from '@site/src/components/Cards/Card'; diff --git a/docs/stylus/quickstart.mdx b/docs/stylus/quickstart.mdx index 91c3703c74..588f015ca1 100644 --- a/docs/stylus/quickstart.mdx +++ b/docs/stylus/quickstart.mdx @@ -6,7 +6,7 @@ author: chrisco512, anegg0 sme: chrisco512, anegg0 sidebar_position: 2 target_audience: Developers writing Stylus contracts in Rust using Stylus -displayed_sidebar: buildAppsSidebarFromStylus +displayed_sidebar: buildAppsSidebar --- import ImageZoom from '@site/src/components/ImageZoom'; diff --git a/docs/stylus/reference/project-structure.mdx b/docs/stylus/reference/project-structure.mdx index d4d9c28065..c37969b3d7 100644 --- a/docs/stylus/reference/project-structure.mdx +++ b/docs/stylus/reference/project-structure.mdx @@ -5,7 +5,7 @@ author: chrisco sme: chrisco sidebar_position: 1 target_audience: Developers using the Stylus Rust SDK to write and deploy smart contracts. -displayed_sidebar: buildAppsSidebarFromStylus +displayed_sidebar: buildAppsSidebar --- Contracts in Rust are similar to contracts in Solidity. Each contract can contain declarations of State Variables, Functions, Function Modifiers, Events, Errors, Struct Types, and Enum Types. In addition, Rust contracts can import third-party packages from [crates.io](https://crates.io) as dependencies and use them for advanced functionality. diff --git a/docs/stylus/stylus-content-map.mdx b/docs/stylus/stylus-content-map.mdx index ff9874f23c..3aa5044cc7 100644 --- a/docs/stylus/stylus-content-map.mdx +++ b/docs/stylus/stylus-content-map.mdx @@ -2,7 +2,7 @@ id: stylus-content-map title: Write Stylus Contracts sidebar_label: Write Stylus contracts -displayed_sidebar: buildAppsSidebarFromStylus +displayed_sidebar: buildAppsSidebar --- import Card from '@site/src/components/Cards/Card'; diff --git a/docs/stylus/using-cli.mdx b/docs/stylus/using-cli.mdx index 3df13a69bc..9fa36931e9 100644 --- a/docs/stylus/using-cli.mdx +++ b/docs/stylus/using-cli.mdx @@ -6,7 +6,7 @@ author: 'anegg0' sme: 'anegg0' sidebar_position: 2 target_audience: Developers writing Stylus contracts in Rust using Stylus -displayed_sidebar: buildAppsSidebarFromStylus +displayed_sidebar: buildAppsSidebar --- This guide will get you started using [cargo stylus](https://github.com/OffchainLabs/cargo-stylus), a CLI toolkit to help developers manage, compile, deploy, and optimize their Stylus contracts efficiently. From 989e825022e8b79cded0774c2f082f86bae5751f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Tue, 7 Oct 2025 17:15:23 -0700 Subject: [PATCH 49/62] cleanup sidebars.js from redundant entries --- sidebars.js | 1016 +-------------------------------------------------- 1 file changed, 16 insertions(+), 1000 deletions(-) diff --git a/sidebars.js b/sidebars.js index 421cef1da0..551fcb99fe 100644 --- a/sidebars.js +++ b/sidebars.js @@ -454,184 +454,6 @@ const sidebars = { }, ], - // Stylus sidebar - stylusSidebar: [ - { - type: 'category', - label: 'Build dApps with Stylus', - collapsed: false, - items: [ - { - type: 'doc', - id: 'stylus/gentle-introduction', - label: 'A gentle introduction', - }, - { - type: 'doc', - id: 'stylus/quickstart', - label: 'Quickstart', - }, - { - type: 'category', - label: 'Rust SDK', - collapsed: true, - items: [ - { - type: 'doc', - id: 'stylus/reference/overview', - label: 'Overview', - }, - { - type: 'doc', - id: 'stylus/reference/project-structure', - label: 'Structure of a Contract', - }, - ...stylusByExampleBasicExamples, - { - type: 'doc', - id: 'stylus/how-tos/using-inheritance', - label: 'Composition and trait-based routing model', - }, - { - type: 'doc', - id: 'stylus/reference/rust-sdk-guide', - label: 'Advanced features', - }, - { - type: 'doc', - id: 'stylus/recommended-libraries', - label: 'Recommended Rust Crates', - }, - ], - }, - { - type: 'category', - label: 'Rust CLI', - collapsed: true, - items: [ - { - type: 'doc', - id: 'stylus/using-cli', - label: 'Overview', - }, - { - type: 'doc', - id: 'stylus/how-tos/debugging-tx', - label: 'Debug transactions', - }, - { - type: 'doc', - id: 'stylus/how-tos/testing-contracts', - label: 'Testing contracts', - }, - { - type: 'doc', - id: 'stylus/how-tos/verifying-contracts', - label: 'Verify contracts', - }, - { - type: 'doc', - id: 'stylus/how-tos/caching-contracts', - label: 'Cache contracts', - }, - { - type: 'doc', - id: 'stylus/how-tos/verifying-contracts-arbiscan', - label: 'Verify on Arbiscan', - }, - { - type: 'doc', - id: 'stylus/how-tos/optimizing-binaries', - label: 'Optimize WASM binaries', - }, - ], - }, - { - type: 'html', - value: - 'Run a local dev node', - }, - { - type: 'category', - label: 'Concepts', - collapsed: true, - items: [ - { - type: 'doc', - id: 'stylus/concepts/how-it-works', - label: 'Architecture overview', - }, - { - type: 'doc', - id: 'stylus/concepts/gas-metering', - label: 'Gas metering', - }, - ], - }, - { - type: 'category', - label: 'Examples', - collapsed: true, - items: [ - ...stylusByExampleApplications, - { - type: 'link', - label: 'Awesome Stylus', - href: 'https://github.com/OffchainLabs/awesome-stylus', - }, - ], - }, - { - type: 'category', - label: 'Reference', - collapsed: true, - items: [ - { - type: 'html', - value: - 'Chain Info', - }, - { - type: 'doc', - id: 'stylus/reference/opcode-hostio-pricing', - label: 'Gas & Ink Pricing', - }, - { - type: 'link', - label: 'Stylus by Example', - href: 'https://stylus-by-example.org/', - }, - { - type: 'link', - label: 'Cargo Stylus CLI GitHub', - href: 'https://github.com/OffchainLabs/cargo-stylus', - }, - { - type: 'link', - label: 'Rust SDK Crate', - href: 'https://docs.rs/stylus-sdk/latest/stylus_sdk/index.html', - }, - { - type: 'link', - label: 'Source Code Repository', - href: 'https://github.com/OffchainLabs/stylus', - }, - ], - }, - { - type: 'doc', - id: 'stylus/how-tos/adding-support-for-new-languages', - label: 'Using other languages', - }, - { - type: 'doc', - id: 'stylus/troubleshooting-building-stylus', - label: 'Troubleshooting', - }, - ], - }, - ], - // Run Node sidebar runNodeSidebar: [ { @@ -1391,11 +1213,6 @@ const sidebars = { label: 'Reference', collapsed: true, items: [ - { - type: 'html', - value: - 'Chain Info', - }, { type: 'doc', id: 'stylus/reference/opcode-hostio-pricing', @@ -1441,845 +1258,49 @@ const sidebars = { label: 'Chain info', }, ], - - // Build apps sidebar optimized for Get Started entry point (Build decentralized apps expanded, Stylus collapsed) - buildAppsSidebarFromGetStarted: [ + // Additional resources sidebar + additionalResourcesSidebar: [ { type: 'category', - label: 'Build dApps with Solidity', + label: 'Third-party docs', collapsed: true, items: [ - { - type: 'doc', - id: 'build-decentralized-apps/quickstart-solidity-remix', - label: 'Quickstart', - }, - { - type: 'doc', - label: 'Estimate gas', - id: 'build-decentralized-apps/how-to-estimate-gas', - }, - { - type: 'doc', - label: 'Chains and testnets', - id: 'build-decentralized-apps/public-chains', - }, - { - type: 'doc', - label: 'Cross-chain messaging', - id: 'build-decentralized-apps/cross-chain-messaging', - }, - { - type: 'doc', - id: 'build-decentralized-apps/custom-gas-token-sdk', - label: 'Custom gas token SDK', - }, { type: 'category', - label: 'Arbitrum vs Ethereum', + label: 'Oracles', + collapsed: true, + link: { + type: 'doc', + id: 'for-devs/oracles/oracles-content-map', + }, items: [ { type: 'doc', - label: 'Comparison overview', - id: 'build-decentralized-apps/arbitrum-vs-ethereum/comparison-overview', + id: 'for-devs/oracles/api3/api3', }, { type: 'doc', - label: 'Block gas limit, numbers and time', - id: 'build-decentralized-apps/arbitrum-vs-ethereum/block-numbers-and-time', + id: 'for-devs/oracles/chainlink/chainlink', }, { type: 'doc', - label: 'RPC methods', - id: 'build-decentralized-apps/arbitrum-vs-ethereum/rpc-methods', + id: 'for-devs/oracles/chronicle/chronicle', }, { type: 'doc', - label: 'Solidity support', - id: 'build-decentralized-apps/arbitrum-vs-ethereum/solidity-support', + id: 'for-devs/oracles/ora/ora', }, - ], - }, - { - type: 'doc', - label: 'Oracles', - id: 'build-decentralized-apps/oracles/overview-oracles', - }, - { - type: 'category', - label: 'Precompiles', - collapsed: true, - items: [ { type: 'doc', - label: 'Overview', - id: 'build-decentralized-apps/precompiles/overview', + id: 'for-devs/oracles/supra/supras-price-feed', }, { type: 'doc', - label: 'Reference', - id: 'build-decentralized-apps/precompiles/reference', + id: 'for-devs/oracles/supra/supras-vrf', }, - ], - }, - { - type: 'category', - label: 'NodeInterface', - collapsed: true, - items: [ { type: 'doc', - label: 'Overview', - id: 'build-decentralized-apps/nodeinterface/overview', - }, - { - type: 'doc', - label: 'Reference', - id: 'build-decentralized-apps/nodeinterface/reference', - }, - ], - }, - { - type: 'category', - label: 'Token bridging', - collapsed: true, - items: [ - { - type: 'doc', - label: 'Overview', - id: 'build-decentralized-apps/token-bridging/overview', - }, - { - type: 'doc', - label: 'ETH bridging', - id: 'build-decentralized-apps/token-bridging/token-bridge-ether', - }, - { - type: 'doc', - label: 'ERC-20 token bridging', - id: 'build-decentralized-apps/token-bridging/token-bridge-erc20', - }, - { - type: 'category', - label: 'Bridge tokens programmatically', - items: [ - { - type: 'doc', - label: 'Get started', - id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/get-started', - }, - { - type: 'doc', - label: 'Use the standard gateway', - id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-standard', - }, - { - type: 'doc', - label: 'Use the generic-custom gateway', - id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-generic-custom', - }, - { - type: 'doc', - label: 'Use the custom gateway', - id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-custom-gateway', - }, - ], - }, - ], - }, - { - type: 'category', - label: 'Reference', - items: [ - { - type: 'doc', - id: 'build-decentralized-apps/reference/node-providers', - label: 'RPC endpoints and providers', - }, - { - type: 'doc', - label: 'Smart contract addresses', - id: 'build-decentralized-apps/reference/contract-addresses', - }, - { - type: 'doc', - label: 'Chain parameters', - id: 'build-decentralized-apps/reference/chain-params', - }, - { - type: 'doc', - label: 'Development frameworks', - id: 'build-decentralized-apps/reference/development-frameworks', - }, - { - type: 'doc', - label: 'Web3 libraries and tools', - id: 'build-decentralized-apps/reference/web3-libraries-tools', - }, - { - type: 'doc', - label: 'Monitoring tools and block explorers', - id: 'build-decentralized-apps/reference/monitoring-tools-block-explorers', - }, - { - type: 'doc', - label: 'Debugging tools', - id: 'build-decentralized-apps/reference/debugging-tools', - }, - - { - type: 'doc', - id: 'build-decentralized-apps/reference/mainnet-risks', - label: 'Mainnet risks', - }, - ], - }, - { - type: 'doc', - label: 'Troubleshooting', - id: 'for-devs/troubleshooting-building', - }, - { - type: 'category', - label: 'Arbitrum SDK', - items: sdkSidebar.sdkSidebar, - }, - { - type: 'link', - label: 'Tutorials', - href: 'https://github.com/OffchainLabs/arbitrum-tutorials', - }, - ], - }, - { - type: 'category', - label: 'Build dApps with Stylus', - collapsed: true, - items: [ - { - type: 'doc', - id: 'stylus/gentle-introduction', - label: 'A gentle introduction', - }, - { - type: 'doc', - id: 'stylus/quickstart', - label: 'Quickstart', - }, - { - type: 'category', - label: 'Rust SDK', - collapsed: true, - items: [ - { - type: 'doc', - id: 'stylus/reference/overview', - label: 'Overview', - }, - { - type: 'doc', - id: 'stylus/reference/project-structure', - label: 'Structure of a Contract', - }, - ...stylusByExampleBasicExamples, - { - type: 'doc', - id: 'stylus/how-tos/using-inheritance', - label: 'Composition and trait-based routing model', - }, - { - type: 'doc', - id: 'stylus/reference/rust-sdk-guide', - label: 'Advanced features', - }, - { - type: 'doc', - id: 'stylus/recommended-libraries', - label: 'Recommended Rust Crates', - }, - ], - }, - { - type: 'category', - label: 'Rust CLI', - collapsed: true, - items: [ - { - type: 'doc', - id: 'stylus/using-cli', - label: 'Overview', - }, - { - type: 'doc', - id: 'stylus/how-tos/debugging-tx', - label: 'Debug transactions', - }, - { - type: 'doc', - id: 'stylus/how-tos/testing-contracts', - label: 'Testing contracts', - }, - { - type: 'doc', - id: 'stylus/how-tos/verifying-contracts', - label: 'Verify contracts', - }, - { - type: 'doc', - id: 'stylus/how-tos/caching-contracts', - label: 'Cache contracts', - }, - { - type: 'doc', - id: 'stylus/how-tos/verifying-contracts-arbiscan', - label: 'Verify on Arbiscan', - }, - { - type: 'doc', - id: 'stylus/how-tos/optimizing-binaries', - label: 'Optimize WASM binaries', - }, - ], - }, - { - type: 'html', - value: - 'Run a local dev node', - }, - { - type: 'category', - label: 'Concepts', - collapsed: true, - items: [ - { - type: 'doc', - id: 'stylus/concepts/how-it-works', - label: 'Architecture overview', - }, - { - type: 'doc', - id: 'stylus/concepts/gas-metering', - label: 'Gas metering', - }, - ], - }, - { - type: 'category', - label: 'Examples', - collapsed: true, - items: [ - ...stylusByExampleApplications, - { - type: 'link', - label: 'Awesome Stylus', - href: 'https://github.com/OffchainLabs/awesome-stylus', - }, - ], - }, - { - type: 'category', - label: 'Reference', - collapsed: true, - items: [ - { - type: 'html', - value: - 'Chain Info', - }, - { - type: 'doc', - id: 'stylus/reference/opcode-hostio-pricing', - label: 'Gas & Ink Pricing', - }, - { - type: 'link', - label: 'Stylus by Example', - href: 'https://stylus-by-example.org/', - }, - { - type: 'link', - label: 'Cargo Stylus CLI GitHub', - href: 'https://github.com/OffchainLabs/cargo-stylus', - }, - { - type: 'link', - label: 'Rust SDK Crate', - href: 'https://docs.rs/stylus-sdk/latest/stylus_sdk/index.html', - }, - { - type: 'link', - label: 'Source Code Repository', - href: 'https://github.com/OffchainLabs/stylus', - }, - ], - }, - { - type: 'doc', - id: 'stylus/how-tos/adding-support-for-new-languages', - label: 'Using other languages', - }, - { - type: 'doc', - id: 'stylus/troubleshooting-building-stylus', - label: 'Troubleshooting', - }, - ], - }, - { - type: 'doc', - id: 'for-devs/dev-tools-and-resources/chain-info', - label: 'Chain info', - }, - ], - - // Build apps sidebar optimized for Stylus entry point (Stylus expanded, Build decentralized apps collapsed) - buildAppsSidebarFromStylus: [ - { - type: 'category', - label: 'Build decentralized apps', - collapsed: true, - items: [ - { - type: 'doc', - id: 'build-decentralized-apps/quickstart-solidity-remix', - label: 'Quickstart', - }, - { - type: 'doc', - label: 'Estimate gas', - id: 'build-decentralized-apps/how-to-estimate-gas', - }, - { - type: 'doc', - label: 'Chains and testnets', - id: 'build-decentralized-apps/public-chains', - }, - { - type: 'doc', - label: 'Cross-chain messaging', - id: 'build-decentralized-apps/cross-chain-messaging', - }, - { - type: 'doc', - id: 'build-decentralized-apps/custom-gas-token-sdk', - label: 'Custom gas token SDK', - }, - { - type: 'category', - label: 'Arbitrum vs Ethereum', - items: [ - { - type: 'doc', - label: 'Comparison overview', - id: 'build-decentralized-apps/arbitrum-vs-ethereum/comparison-overview', - }, - { - type: 'doc', - label: 'Block gas limit, numbers and time', - id: 'build-decentralized-apps/arbitrum-vs-ethereum/block-numbers-and-time', - }, - { - type: 'doc', - label: 'RPC methods', - id: 'build-decentralized-apps/arbitrum-vs-ethereum/rpc-methods', - }, - { - type: 'doc', - label: 'Solidity support', - id: 'build-decentralized-apps/arbitrum-vs-ethereum/solidity-support', - }, - ], - }, - { - type: 'doc', - label: 'Oracles', - id: 'build-decentralized-apps/oracles/overview-oracles', - }, - { - type: 'category', - label: 'Precompiles', - collapsed: true, - items: [ - { - type: 'doc', - label: 'Overview', - id: 'build-decentralized-apps/precompiles/overview', - }, - { - type: 'doc', - label: 'Reference', - id: 'build-decentralized-apps/precompiles/reference', - }, - ], - }, - { - type: 'category', - label: 'NodeInterface', - collapsed: true, - items: [ - { - type: 'doc', - label: 'Overview', - id: 'build-decentralized-apps/nodeinterface/overview', - }, - { - type: 'doc', - label: 'Reference', - id: 'build-decentralized-apps/nodeinterface/reference', - }, - ], - }, - { - type: 'category', - label: 'Token bridging', - collapsed: true, - items: [ - { - type: 'doc', - label: 'Overview', - id: 'build-decentralized-apps/token-bridging/overview', - }, - { - type: 'doc', - label: 'ETH bridging', - id: 'build-decentralized-apps/token-bridging/token-bridge-ether', - }, - { - type: 'doc', - label: 'ERC-20 token bridging', - id: 'build-decentralized-apps/token-bridging/token-bridge-erc20', - }, - { - type: 'category', - label: 'Bridge tokens programmatically', - items: [ - { - type: 'doc', - label: 'Get started', - id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/get-started', - }, - { - type: 'doc', - label: 'Use the standard gateway', - id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-standard', - }, - { - type: 'doc', - label: 'Use the generic-custom gateway', - id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-generic-custom', - }, - { - type: 'doc', - label: 'Use the custom gateway', - id: 'build-decentralized-apps/token-bridging/bridge-tokens-programmatically/how-to-bridge-tokens-custom-gateway', - }, - ], - }, - ], - }, - { - type: 'category', - label: 'Reference', - items: [ - { - type: 'doc', - id: 'build-decentralized-apps/reference/node-providers', - label: 'RPC endpoints and providers', - }, - { - type: 'doc', - label: 'Smart contract addresses', - id: 'build-decentralized-apps/reference/contract-addresses', - }, - { - type: 'doc', - label: 'Chain parameters', - id: 'build-decentralized-apps/reference/chain-params', - }, - { - type: 'doc', - label: 'Development frameworks', - id: 'build-decentralized-apps/reference/development-frameworks', - }, - { - type: 'doc', - label: 'Web3 libraries and tools', - id: 'build-decentralized-apps/reference/web3-libraries-tools', - }, - { - type: 'doc', - label: 'Monitoring tools and block explorers', - id: 'build-decentralized-apps/reference/monitoring-tools-block-explorers', - }, - { - type: 'doc', - label: 'Debugging tools', - id: 'build-decentralized-apps/reference/debugging-tools', - }, - - { - type: 'doc', - id: 'build-decentralized-apps/reference/mainnet-risks', - label: 'Mainnet risks', - }, - ], - }, - { - type: 'doc', - label: 'Troubleshooting', - id: 'for-devs/troubleshooting-building', - }, - { - type: 'category', - label: 'Arbitrum SDK', - items: sdkSidebar.sdkSidebar, - }, - { - type: 'link', - label: 'Tutorials', - href: 'https://github.com/OffchainLabs/arbitrum-tutorials', - }, - ], - }, - { - type: 'category', - label: 'Build dApps with Stylus', - collapsed: false, - items: [ - { - type: 'doc', - id: 'stylus/gentle-introduction', - label: 'A gentle introduction', - }, - { - type: 'doc', - id: 'stylus/quickstart', - label: 'Quickstart', - }, - { - type: 'category', - label: 'Rust SDK', - collapsed: true, - items: [ - { - type: 'doc', - id: 'stylus/reference/overview', - label: 'Overview', - }, - { - type: 'doc', - id: 'stylus/reference/project-structure', - label: 'Structure of a Contract', - }, - ...stylusByExampleBasicExamples, - { - type: 'doc', - id: 'stylus/how-tos/using-inheritance', - label: 'Composition and trait-based routing model', - }, - { - type: 'doc', - id: 'stylus/reference/rust-sdk-guide', - label: 'Advanced features', - }, - { - type: 'doc', - id: 'stylus/recommended-libraries', - label: 'Recommended Rust Crates', - }, - ], - }, - { - type: 'category', - label: 'Rust CLI', - collapsed: true, - items: [ - { - type: 'doc', - id: 'stylus/using-cli', - label: 'Overview', - }, - { - type: 'doc', - id: 'stylus/how-tos/debugging-tx', - label: 'Debug transactions', - }, - { - type: 'doc', - id: 'stylus/how-tos/testing-contracts', - label: 'Testing contracts', - }, - { - type: 'doc', - id: 'stylus/how-tos/verifying-contracts', - label: 'Verify contracts', - }, - { - type: 'doc', - id: 'stylus/how-tos/caching-contracts', - label: 'Cache contracts', - }, - { - type: 'doc', - id: 'stylus/how-tos/verifying-contracts-arbiscan', - label: 'Verify on Arbiscan', - }, - { - type: 'doc', - id: 'stylus/how-tos/optimizing-binaries', - label: 'Optimize WASM binaries', - }, - ], - }, - { - type: 'html', - value: - 'Run a local dev node', - }, - { - type: 'category', - label: 'Concepts', - collapsed: true, - items: [ - { - type: 'doc', - id: 'stylus/concepts/how-it-works', - label: 'Architecture overview', - }, - { - type: 'doc', - id: 'stylus/concepts/gas-metering', - label: 'Gas metering', - }, - ], - }, - { - type: 'category', - label: 'Examples', - collapsed: true, - items: [ - ...stylusByExampleApplications, - { - type: 'link', - label: 'Awesome Stylus', - href: 'https://github.com/OffchainLabs/awesome-stylus', - }, - ], - }, - { - type: 'category', - label: 'Reference', - collapsed: true, - items: [ - { - type: 'doc', - id: 'for-devs/dev-tools-and-resources/chain-info', - label: 'Chain info', - }, - { - type: 'doc', - id: 'stylus/reference/opcode-hostio-pricing', - label: 'Gas & Ink Pricing', - }, - { - type: 'link', - label: 'Stylus by Example', - href: 'https://stylus-by-example.org/', - }, - { - type: 'link', - label: 'Cargo Stylus CLI GitHub', - href: 'https://github.com/OffchainLabs/cargo-stylus', - }, - { - type: 'link', - label: 'Rust SDK Crate', - href: 'https://docs.rs/stylus-sdk/latest/stylus_sdk/index.html', - }, - { - type: 'link', - label: 'Source Code Repository', - href: 'https://github.com/OffchainLabs/stylus', - }, - ], - }, - { - type: 'doc', - id: 'stylus/how-tos/adding-support-for-new-languages', - label: 'Using other languages', - }, - { - type: 'doc', - id: 'stylus/troubleshooting-building-stylus', - label: 'Troubleshooting', - }, - ], - }, - { - type: 'doc', - id: 'for-devs/dev-tools-and-resources/chain-info', - label: 'Chain info', - }, - ], - - // Additional resources sidebar - additionalResourcesSidebar: [ - { - type: 'doc', - id: 'learn-more/faq', - label: 'FAQ', - }, - { - type: 'doc', - id: 'intro/glossary', - label: 'Glossary', - }, - { - type: 'doc', - id: 'for-devs/contribute', - label: 'Contribute', - }, - { - type: 'category', - label: 'Third-party docs', - collapsed: true, - items: [ - { - type: 'category', - label: 'Oracles', - collapsed: true, - link: { - type: 'doc', - id: 'for-devs/oracles/oracles-content-map', - }, - items: [ - { - type: 'doc', - id: 'for-devs/oracles/api3/api3', - }, - { - type: 'doc', - id: 'for-devs/oracles/chainlink/chainlink', - }, - { - type: 'doc', - id: 'for-devs/oracles/chronicle/chronicle', - }, - { - type: 'doc', - id: 'for-devs/oracles/ora/ora', - }, - { - type: 'doc', - id: 'for-devs/oracles/supra/supras-price-feed', - }, - { - type: 'doc', - id: 'for-devs/oracles/supra/supras-vrf', - }, - { - type: 'doc', - id: 'for-devs/oracles/trellor/trellor', + id: 'for-devs/oracles/trellor/trellor', }, ], }, @@ -2304,11 +1325,6 @@ const sidebars = { label: 'Prysm docs', href: 'https://www.offchainlabs.com/prysm/docs', }, - { - type: 'doc', - id: 'for-devs/dev-tools-and-resources/chain-info', - label: 'Chain info', - }, ], }; From 6e9d8f7f62a38d9cfbe5aa306938030a1f36f89a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Tue, 7 Oct 2025 17:35:08 -0700 Subject: [PATCH 50/62] re-add glossary, chain info, and contribute to externalResourcesSidebar + convert chain info links to html --- sidebars.js | 52 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/sidebars.js b/sidebars.js index 551fcb99fe..9f25b2cef1 100644 --- a/sidebars.js +++ b/sidebars.js @@ -75,9 +75,9 @@ const sidebars = { href: 'how-arbitrum-works/a-gentle-introduction', }, { - type: 'doc', - id: 'for-devs/dev-tools-and-resources/chain-info', - label: 'Chain info', + type: 'html', + value: + 'Chain Info', }, ], // Run Arbitrum Chain sidebar @@ -446,9 +446,9 @@ const sidebars = { label: 'FAQ', }, { - type: 'doc', - id: 'for-devs/dev-tools-and-resources/chain-info', - label: 'Chain info', + type: 'html', + value: + 'Chain Info', }, ], }, @@ -624,9 +624,9 @@ const sidebars = { id: 'node-running/faq', }, { - type: 'doc', - id: 'for-devs/dev-tools-and-resources/chain-info', - label: 'Chain info', + type: 'html', + value: + 'Chain Info', }, ], }, @@ -660,9 +660,9 @@ const sidebars = { label: 'Troubleshooting', }, { - type: 'doc', - id: 'for-devs/dev-tools-and-resources/chain-info', - label: 'Chain info', + type: 'html', + value: + 'Chain Info', }, ], }, @@ -868,9 +868,9 @@ const sidebars = { ], }, { - type: 'doc', - id: 'for-devs/dev-tools-and-resources/chain-info', - label: 'Chain info', + type: 'html', + value: + 'Chain Info', }, ], @@ -1253,11 +1253,12 @@ const sidebars = { ], }, { - type: 'doc', - id: 'for-devs/dev-tools-and-resources/chain-info', - label: 'Chain info', + type: 'html', + value: + 'Chain Info', }, ], + // Additional resources sidebar additionalResourcesSidebar: [ { @@ -1310,6 +1311,21 @@ const sidebars = { }, ], }, + { + type: 'doc', + id: 'intro/glossary', + label: 'Glossary', + }, + { + type: 'doc', + id: 'for-devs/contribute', + label: 'Contribute', + }, + { + type: 'doc', + id: 'for-devs/dev-tools-and-resources/chain-info', + label: 'Chain info', + }, { type: 'doc', label: 'Audit reports', From ec108998e18ba88b66eed5febce57ab493a9db22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Tue, 7 Oct 2025 17:55:04 -0700 Subject: [PATCH 51/62] add glossary + contribute to getting started sidebar --- sidebars.js | 60 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/sidebars.js b/sidebars.js index 9f25b2cef1..3c18a9db78 100644 --- a/sidebars.js +++ b/sidebars.js @@ -79,6 +79,16 @@ const sidebars = { value: 'Chain Info', }, + { + type: 'html', + value: + 'Glossary', + }, + { + type: 'html', + value: + 'Contribute', + }, ], // Run Arbitrum Chain sidebar runArbitrumChainSidebar: [ @@ -1252,6 +1262,16 @@ const sidebars = { }, ], }, + { + type: 'html', + value: + 'Glossary', + }, + { + type: 'html', + value: + 'Contribute', + }, { type: 'html', value: @@ -1261,6 +1281,26 @@ const sidebars = { // Additional resources sidebar additionalResourcesSidebar: [ + { + type: 'doc', + id: 'intro/glossary', + label: 'Glossary', + }, + { + type: 'doc', + id: 'for-devs/contribute', + label: 'Contribute', + }, + { + type: 'doc', + id: 'for-devs/dev-tools-and-resources/chain-info', + label: 'Chain info', + }, + { + type: 'doc', + label: 'Audit reports', + id: 'audit-reports', + }, { type: 'category', label: 'Third-party docs', @@ -1311,26 +1351,6 @@ const sidebars = { }, ], }, - { - type: 'doc', - id: 'intro/glossary', - label: 'Glossary', - }, - { - type: 'doc', - id: 'for-devs/contribute', - label: 'Contribute', - }, - { - type: 'doc', - id: 'for-devs/dev-tools-and-resources/chain-info', - label: 'Chain info', - }, - { - type: 'doc', - label: 'Audit reports', - id: 'audit-reports', - }, { type: 'link', label: 'DAO docs', From df3bd23e0f7262c2ba69c6f7b1edb88c300d6881 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Tue, 7 Oct 2025 18:03:53 -0700 Subject: [PATCH 52/62] add everywhere content to more navbar items --- sidebars.js | 40 +++++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/sidebars.js b/sidebars.js index 3c18a9db78..319792a9c9 100644 --- a/sidebars.js +++ b/sidebars.js @@ -460,6 +460,16 @@ const sidebars = { value: 'Chain Info', }, + { + type: 'html', + value: + 'Glossary', + }, + { + type: 'html', + value: + 'Contribute', + }, ], }, ], @@ -633,13 +643,23 @@ const sidebars = { label: 'FAQ', id: 'node-running/faq', }, - { - type: 'html', - value: - 'Chain Info', - }, ], }, + { + type: 'html', + value: + 'Chain Info', + }, + { + type: 'html', + value: + 'Glossary', + }, + { + type: 'html', + value: + 'Contribute', + }, ], // Bridge sidebar @@ -882,6 +902,16 @@ const sidebars = { value: 'Chain Info', }, + { + type: 'html', + value: + 'Glossary', + }, + { + type: 'html', + value: + 'Contribute', + }, ], // Unified build apps sidebar (combines build dApps and Stylus) From d6f99dc28c566ae5fec2aaa8c553c4304938a457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Tue, 7 Oct 2025 18:11:46 -0700 Subject: [PATCH 53/62] add and reorder permanent sidebar items --- sidebars.js | 52 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/sidebars.js b/sidebars.js index 319792a9c9..b777b500b9 100644 --- a/sidebars.js +++ b/sidebars.js @@ -472,6 +472,22 @@ const sidebars = { }, ], }, + + { + type: 'html', + value: + 'Chain Info', + }, + { + type: 'html', + value: + 'Glossary', + }, + { + type: 'html', + value: + 'Contribute', + }, ], // Run Node sidebar @@ -689,13 +705,23 @@ const sidebars = { id: 'arbitrum-bridge/troubleshooting', label: 'Troubleshooting', }, - { - type: 'html', - value: - 'Chain Info', - }, ], }, + { + type: 'html', + value: + 'Chain Info', + }, + { + type: 'html', + value: + 'Glossary', + }, + { + type: 'html', + value: + 'Contribute', + }, ], // How it Works sidebar @@ -1295,22 +1321,27 @@ const sidebars = { { type: 'html', value: - 'Glossary', + 'Chain Info', }, { type: 'html', value: - 'Contribute', + 'Glossary', }, { type: 'html', value: - 'Chain Info', + 'Contribute', }, ], // Additional resources sidebar additionalResourcesSidebar: [ + { + type: 'doc', + id: 'for-devs/dev-tools-and-resources/chain-info', + label: 'Chain info', + }, { type: 'doc', id: 'intro/glossary', @@ -1321,11 +1352,6 @@ const sidebars = { id: 'for-devs/contribute', label: 'Contribute', }, - { - type: 'doc', - id: 'for-devs/dev-tools-and-resources/chain-info', - label: 'Chain info', - }, { type: 'doc', label: 'Audit reports', From 53e88e14d375c895e1ffb5632ade4705bbed6009 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Tue, 7 Oct 2025 18:19:59 -0700 Subject: [PATCH 54/62] ensure navbar remains highlighted on dropdown selection --- src/css/partials/_navbar.scss | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/css/partials/_navbar.scss b/src/css/partials/_navbar.scss index 0bc3f0ae42..750477535c 100644 --- a/src/css/partials/_navbar.scss +++ b/src/css/partials/_navbar.scss @@ -122,3 +122,34 @@ } } } + +// Keep "Build dApps" dropdown highlighted when on child pages +// Target the dropdown link when any of its menu items are active +.navbar__item.dropdown:has(.dropdown__menu .dropdown__link--active) > .navbar__link { + color: var(--ifm-navbar-link-active-color); + + &::after { + border-bottom-color: var(--ifm-navbar-link-active-color); + border-right-color: var(--ifm-navbar-link-active-color); + } +} + +// Alternative approach for browsers that don't support :has() (fallback) +// Specifically target Build dApps dropdown based on URL patterns +[data-theme='light'] body[class*='build-decentralized-apps'], +[data-theme='light'] body[class*='stylus'] { + .navbar__item.dropdown .navbar__link[aria-label*='Build dApps'], + .navbar__item.dropdown .navbar__link:has(+ .dropdown__menu a[href*='/build-decentralized-apps/']), + .navbar__item.dropdown .navbar__link:has(+ .dropdown__menu a[href*='/stylus/']) { + color: var(--ifm-navbar-link-active-color); + } +} + +[data-theme='dark'] body[class*='build-decentralized-apps'], +[data-theme='dark'] body[class*='stylus'] { + .navbar__item.dropdown .navbar__link[aria-label*='Build dApps'], + .navbar__item.dropdown .navbar__link:has(+ .dropdown__menu a[href*='/build-decentralized-apps/']), + .navbar__item.dropdown .navbar__link:has(+ .dropdown__menu a[href*='/stylus/']) { + color: var(--ifm-navbar-link-active-color); + } +} From caa7ba7b42746ef975d994a3f70d6917af99895a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Tue, 7 Oct 2025 18:48:41 -0700 Subject: [PATCH 55/62] fix broken links --- CONTRIBUTE.md | 5 ++--- docs/partials/_contribute-docs-partial.mdx | 2 +- docs/partials/_gentle-intro-partial.mdx | 2 +- sidebars.js | 8 ++++---- src/pages/index.js | 2 +- vercel.json | 2 +- 6 files changed, 10 insertions(+), 11 deletions(-) diff --git a/CONTRIBUTE.md b/CONTRIBUTE.md index 4e75185f7c..6d8363aa98 100644 --- a/CONTRIBUTE.md +++ b/CONTRIBUTE.md @@ -73,7 +73,7 @@ navbar: { logo: { alt: 'My Site Logo', src: 'img/logo.svg', - href: '/get-started/arbitrum-faq', + href: '/get-started/arbitrum-introduction', }, items: [ // note: we can uncomment this when we want to display the locale dropdown in the top navbar @@ -119,10 +119,9 @@ Every document should be a specific _type_ of document. Each type of document ha This isn't an exhaustive list, but it includes most of the document types that we use. - :::info About Promotional Content -While it is acceptable to include conceptual and how-to content that links to products, services, and protocols in the third party section, we do not accept promotional content in our core docs. +While it is acceptable to include conceptual and how-to content that links to products, services, and protocols in the third party section, we do not accept promotional content in our core docs. Feature pieces that are primarily promotional and do not provide actionable guidance to readers are not accepted as third-party docs, either. diff --git a/docs/partials/_contribute-docs-partial.mdx b/docs/partials/_contribute-docs-partial.mdx index a98691f157..3f1419024a 100644 --- a/docs/partials/_contribute-docs-partial.mdx +++ b/docs/partials/_contribute-docs-partial.mdx @@ -79,7 +79,7 @@ navbar: { logo: { alt: 'My Site Logo', src: 'img/logo.svg', - href: '/get-started/arbitrum-faq', + href: '/get-started/arbitrum-introduction', }, items: [ // note: we can uncomment this when we want to display the locale dropdown in the top navbar diff --git a/docs/partials/_gentle-intro-partial.mdx b/docs/partials/_gentle-intro-partial.mdx index 9e890937a0..d73baf0ff9 100644 --- a/docs/partials/_gentle-intro-partial.mdx +++ b/docs/partials/_gentle-intro-partial.mdx @@ -1,6 +1,6 @@ --- partial_type: content -title: 'Arbitrum Gentle Introduction' +title: 'Arbitrum: Introduction' description: 'FAQ-style introduction to Arbitrum technology and ecosystem' author: anegg0 last_reviewed: 2025-01-15 diff --git a/sidebars.js b/sidebars.js index b777b500b9..8b830641ec 100644 --- a/sidebars.js +++ b/sidebars.js @@ -57,22 +57,22 @@ const sidebars = { { type: 'link', label: 'Run an Arbitrum (Orbit) chain', - href: 'launch-arbitrum-chain/a-gentle-introduction', + href: '/launch-arbitrum-chain/a-gentle-introduction', }, { type: 'link', label: 'Run an Arbitrum node', - href: 'run-arbitrum-node/overview', + href: '/run-arbitrum-node/overview', }, { type: 'link', label: 'Arbitrum bridge', - href: 'run-arbitrum-node/overview', + href: '/arbitrum-bridge/quickstart', }, { type: 'link', label: 'How Arbitrum works', - href: 'how-arbitrum-works/a-gentle-introduction', + href: '/how-arbitrum-works/a-gentle-introduction', }, { type: 'html', diff --git a/src/pages/index.js b/src/pages/index.js index f75e3e81ee..2a7112a7e4 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -15,7 +15,7 @@ function HomepageHeader(props) {

{siteConfig.title}

{/*

{siteConfig.tagline}

*/} - +
diff --git a/vercel.json b/vercel.json index b4ccb10b84..5bcb8d76e0 100644 --- a/vercel.json +++ b/vercel.json @@ -191,7 +191,7 @@ "permanent": false }, { - "source": "/(docs/get-started/arbitrum-faq/?)", + "source": "/(docs/get-started/arbitrum-introduction/?)", "destination": "/(docs/get-started/arbitrum-introduction/?)", "permanent": false }, From bc51c46d6316ff658203629085bc5c920c1b9036 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Wed, 8 Oct 2025 10:00:24 -0700 Subject: [PATCH 56/62] remove duplicate resources entries in "launch a chain" sidebar + add FAQ entry in "Resources" sidebar --- sidebars.js | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/sidebars.js b/sidebars.js index 8b830641ec..0fcf53dac2 100644 --- a/sidebars.js +++ b/sidebars.js @@ -455,21 +455,6 @@ const sidebars = { id: 'launch-arbitrum-chain/faq-troubleshooting/troubleshooting-building-arbitrum-chain', label: 'FAQ', }, - { - type: 'html', - value: - 'Chain Info', - }, - { - type: 'html', - value: - 'Glossary', - }, - { - type: 'html', - value: - 'Contribute', - }, ], }, @@ -1352,6 +1337,11 @@ const sidebars = { id: 'for-devs/contribute', label: 'Contribute', }, + { + type: 'doc', + id: 'learn-more/faq', + label: 'FAQ', + }, { type: 'doc', label: 'Audit reports', From 2048d016d14285ecada7367ac1efcd732d6fda8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Wed, 8 Oct 2025 11:02:34 -0700 Subject: [PATCH 57/62] place FAQ in getting-started sidebar --- sidebars.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sidebars.js b/sidebars.js index 0fcf53dac2..e2b5920b77 100644 --- a/sidebars.js +++ b/sidebars.js @@ -89,6 +89,11 @@ const sidebars = { value: 'Contribute', }, + { + type: 'html', + value: + 'FAQ', + }, ], // Run Arbitrum Chain sidebar runArbitrumChainSidebar: [ From bcc938b17489d84b6c515ecd3529851836f29f74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Fri, 10 Oct 2025 11:31:24 -0700 Subject: [PATCH 58/62] rename "dApps" to "apps" NavBar and SideBar --- docusaurus.config.js | 2 +- sidebars.js | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docusaurus.config.js b/docusaurus.config.js index dafa157565..9726b6a7b4 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -234,7 +234,7 @@ const config = { }, { type: 'dropdown', - label: 'Build dApps', + label: 'Build apps', position: 'right', items: [ { diff --git a/sidebars.js b/sidebars.js index e2b5920b77..d22f4442db 100644 --- a/sidebars.js +++ b/sidebars.js @@ -39,18 +39,18 @@ const sidebars = { }, { type: 'category', - label: 'Build dApps', + label: 'Build apps', collapsed: true, items: [ { type: 'doc', id: 'build-decentralized-apps/quickstart-solidity-remix', - label: 'Build dApps with Solidity', + label: 'Build apps with Solidity', }, { type: 'doc', id: 'stylus/quickstart', - label: 'Build dApps with Stylus', + label: 'Build apps with Stylus', }, ], }, @@ -930,11 +930,11 @@ const sidebars = { }, ], - // Unified build apps sidebar (combines build dApps and Stylus) + // Unified build apps sidebar (combines build apps and Stylus) buildAppsSidebar: [ { type: 'category', - label: 'Build dApps with Solidity', + label: 'Build apps with Solidity', collapsed: false, items: [ { @@ -1141,7 +1141,7 @@ const sidebars = { }, { type: 'category', - label: 'Build dApps with Stylus', + label: 'Build apps with Stylus', collapsed: true, items: [ { From ea05cb79d7b3cd4502e60992bc7df015961d51f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Fri, 10 Oct 2025 12:03:44 -0700 Subject: [PATCH 59/62] fix landing page 404 --- docs/get-started/get-started.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/get-started/get-started.mdx b/docs/get-started/get-started.mdx index b9e1f9fa07..1d94afac3a 100644 --- a/docs/get-started/get-started.mdx +++ b/docs/get-started/get-started.mdx @@ -4,7 +4,7 @@ description: An abbreviated introduction to the Arbitrum suite author: symbolpunk user_story: As a curious reader, I'd like to be quickly guided towards first steps based on my particular needs. content_type: quickstart -slug: /get-started +slug: /get-started/get-started --- Arbitrum is a suite of Ethereum scaling solutions that make it From 1974445081b78717a06e29818bbaf781c7b42498 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Fri, 10 Oct 2025 12:12:20 -0700 Subject: [PATCH 60/62] Revert "fix landing page 404" This reverts commit ea05cb79d7b3cd4502e60992bc7df015961d51f6. --- docs/get-started/get-started.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/get-started/get-started.mdx b/docs/get-started/get-started.mdx index 1d94afac3a..b9e1f9fa07 100644 --- a/docs/get-started/get-started.mdx +++ b/docs/get-started/get-started.mdx @@ -4,7 +4,7 @@ description: An abbreviated introduction to the Arbitrum suite author: symbolpunk user_story: As a curious reader, I'd like to be quickly guided towards first steps based on my particular needs. content_type: quickstart -slug: /get-started/get-started +slug: /get-started --- Arbitrum is a suite of Ethereum scaling solutions that make it From 23470169d0b86a866f1298244666ae700aa01a52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Fri, 10 Oct 2025 12:19:53 -0700 Subject: [PATCH 61/62] Revert "fix landing page 404" --- docs/get-started/{get-started.mdx => overview.mdx} | 1 - sidebars.js | 2 +- vercel.json | 13 +++++++++---- 3 files changed, 10 insertions(+), 6 deletions(-) rename docs/get-started/{get-started.mdx => overview.mdx} (99%) diff --git a/docs/get-started/get-started.mdx b/docs/get-started/overview.mdx similarity index 99% rename from docs/get-started/get-started.mdx rename to docs/get-started/overview.mdx index b9e1f9fa07..2082ae1027 100644 --- a/docs/get-started/get-started.mdx +++ b/docs/get-started/overview.mdx @@ -4,7 +4,6 @@ description: An abbreviated introduction to the Arbitrum suite author: symbolpunk user_story: As a curious reader, I'd like to be quickly guided towards first steps based on my particular needs. content_type: quickstart -slug: /get-started --- Arbitrum is a suite of Ethereum scaling solutions that make it diff --git a/sidebars.js b/sidebars.js index d22f4442db..bfe9aa0da7 100644 --- a/sidebars.js +++ b/sidebars.js @@ -29,7 +29,7 @@ const sidebars = { getStartedSidebar: [ { type: 'doc', - id: 'get-started/get-started', + id: 'get-started/overview', label: 'Get started', }, { diff --git a/vercel.json b/vercel.json index 5bcb8d76e0..a22c01a13f 100644 --- a/vercel.json +++ b/vercel.json @@ -4,7 +4,7 @@ "installCommand": "bash scripts/init-submodules.sh && yarn install", "framework": "docusaurus-2", "redirects": [ - { "source": "/", "destination": "/get-started/get-started", "permanent": false }, + { "source": "/", "destination": "/get-started/overview", "permanent": false }, { "source": "/(/build-decentralized-apps/oracles/how-to-use-oracles/?)", "destination": "/build-decentralized-apps/oracles/overview-oracles", @@ -195,6 +195,11 @@ "destination": "/(docs/get-started/arbitrum-introduction/?)", "permanent": false }, + { + "source": "/(docs/get-started/get-started/?)", + "destination": "/(docs/get-started/overview/?)", + "permanent": false + }, { "source": "/(docs/inside_arbitrum/?)", "destination": "/how-arbitrum-works/a-gentle-introduction", @@ -257,7 +262,7 @@ }, { "source": "/(docs/welcome/get-started/?)", - "destination": "/(docs/get-started/get-started/?)", + "destination": "/(docs/get-started/overview/?)", "permanent": false }, { @@ -1922,7 +1927,7 @@ }, { "source": "/docs/developer_quickstart", - "destination": "/get-started/get-started", + "destination": "/get-started/overview", "permanent": false }, { @@ -2038,7 +2043,7 @@ "destination": "/faqs/protocol-faqs#q-seq-vs-val", "permanent": false }, - { "source": "/faqs/the-merge", "destination": "/get-started/get-started", "permanent": false }, + { "source": "/faqs/the-merge", "destination": "/get-started/overview", "permanent": false }, { "source": "/faqs/tooling-faqs", "destination": "/for-devs/troubleshooting-building", From 33aad27864df415939ee26950b47e553993b383e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Blanchemain?= Date: Fri, 10 Oct 2025 12:35:00 -0700 Subject: [PATCH 62/62] fix: update logo href from /get-started to /get-started/overview --- docusaurus.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docusaurus.config.js b/docusaurus.config.js index 9726b6a7b4..eacc8adac4 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -223,7 +223,7 @@ const config = { logo: { alt: 'Arbitrum Logo', src: 'img/logo.svg', - href: '/get-started', + href: '/get-started/overview', }, items: [ {