Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions docs/how-arbitrum-works/01-inside-arbitrum-nitro.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,15 @@ content_type: get-started
---

import ImageWithCaption from '@site/src/components/ImageCaptions/';
import InteractiveDiagrams from '@site/src/components/InteractiveDiagrams';

### Transaction processing journey on Arbitrum

As a developer initiating a transaction on Arbitrum, gaining a clear understanding of the end-to-end flow—from initial submission through to finality—is helpful, but it is not required.

This overview methodically traces the transaction lifecycle, emphasizing how Arbitrum's architecture ensures precise, efficient, and secure handling at every stage. This article encompasses the complete Arbitrum Nitro stack, beginning with the Sequencer responsible for transaction ordering, advancing to the State Transition Function for execution, and culminating in validation mechanisms that uphold integrity.

<ImageZoom
src="/img/haw-transaction-lifecycle.png"
className="img-600px"
alt="Transaction lifecycle"
>
Transaction lifecycle
</ImageZoom>
<InteractiveDiagrams type="transaction-lifecycle" />

Along the way, we will highlight Arbitrum's ability to deliver security on par with Ethereum, while achieving fee reductions by a factor of ten and transaction speeds accelerated by a factor of 100 through optimized scaling techniques.

Expand Down
165 changes: 165 additions & 0 deletions src/components/InteractiveDiagrams/TransactionLifecycle/FlowChart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import React from 'react';
import { Modal } from './Modal';
import { UserInbox } from './components/UserInbox';
import { SequencedTxs } from './components/SequencedTxs';
import { L2Blocks } from './components/L2Blocks';
import { DiagramNode } from './components/DiagramNode';
import { VIEWBOX, COLORS, NODE_POSITIONS, FLOW_PATHS } from './constants';
import type { NodeId } from './types';

interface FlowChartProps {
activeNodes: NodeId[];
activeFlowPaths: string[];
}

export function FlowChart({ activeNodes, activeFlowPaths }: FlowChartProps) {
const isActive = (nodeId: NodeId) => activeNodes.includes(nodeId);

return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox={`0 0 ${VIEWBOX.width} ${VIEWBOX.height}`}
className="transaction-lifecycle__svg"
style={{ pointerEvents: 'none' }}
>
<defs>
{/* Background gradient */}
<linearGradient id="tl-bg-gradient" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stopColor={COLORS.gradientTop} />
<stop offset="50%" stopColor={COLORS.gradientMid} />
<stop offset="100%" stopColor={COLORS.gradientBottom} />
</linearGradient>

{/* Arrow marker */}
<marker
id="tl-arrow"
markerWidth="10"
markerHeight="10"
refX="9"
refY="3"
orient="auto"
markerUnits="strokeWidth"
>
<path d="M0,0 L0,6 L9,3 z" fill={COLORS.white} />
</marker>

{/* Glow filter for active nodes */}
<filter id="tl-glow" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="4" result="coloredBlur" />
<feMerge>
<feMergeNode in="coloredBlur" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
</defs>

{/* Background */}
<rect x="0" y="0" width={VIEWBOX.width} height={VIEWBOX.height} fill="url(#tl-bg-gradient)" />

{/* Decorative circuit lines (like CentralizedAuction) */}
<g className="transaction-lifecycle__decorative" opacity="0.1">
<line x1="0" y1="100" x2="150" y2="100" stroke="white" strokeWidth="1" />
<line x1="850" y1="50" x2="1000" y2="50" stroke="white" strokeWidth="1" />
<line x1="900" y1="500" x2="1000" y2="500" stroke="white" strokeWidth="1" />
<circle cx="150" cy="100" r="3" fill="white" />
<circle cx="850" cy="50" r="3" fill="white" />
</g>

{/* Flow paths (connections between nodes) */}
<g className="transaction-lifecycle__paths">
{FLOW_PATHS.map((path) => {
const isPathActive = activeFlowPaths.includes(path.id);
const pathClass = isPathActive
? 'transaction-lifecycle__path transaction-lifecycle__path--active'
: 'transaction-lifecycle__path';

// Build the path string
const pathD = path.points.map((p, i) => `${i === 0 ? 'M' : 'L'} ${p.x} ${p.y}`).join(' ');

return (
<path
key={path.id}
d={pathD}
fill="none"
stroke={COLORS.white}
strokeWidth="2"
strokeDasharray={path.dashed ? '5,5' : undefined}
markerEnd="url(#tl-arrow)"
className={pathClass}
opacity={0.6}
/>
);
})}
</g>

{/* Nodes */}
<g className="transaction-lifecycle__nodes">
{/* User Inbox - special component with stacked envelopes */}
<Modal nodeId="userInbox">
<UserInbox isActive={isActive('userInbox')} />
</Modal>

{/* Delayed Inbox */}
<Modal nodeId="delayedInbox">
<DiagramNode nodeId="delayedInbox" isActive={isActive('delayedInbox')} />
</Modal>

{/* Sequencer */}
<Modal nodeId="sequencer">
<DiagramNode nodeId="sequencer" isActive={isActive('sequencer')} />
</Modal>

{/* Sequencer Feed */}
<Modal nodeId="sequencerFeed">
<DiagramNode nodeId="sequencerFeed" isActive={isActive('sequencerFeed')} />
</Modal>

{/* Batch and Compress */}
<Modal nodeId="batchCompress">
<DiagramNode nodeId="batchCompress" isActive={isActive('batchCompress')} />
</Modal>

{/* Sequenced Txs - special component with numbered envelopes */}
<Modal nodeId="sequencedTxs">
<SequencedTxs isActive={isActive('sequencedTxs')} />
</Modal>

{/* STF */}
<Modal nodeId="stf">
<DiagramNode nodeId="stf" isActive={isActive('stf')} />
</Modal>

{/* State */}
<Modal nodeId="state">
<DiagramNode nodeId="state" isActive={isActive('state')} />
</Modal>

{/* L2 Blocks - special component with stacked blocks */}
<Modal nodeId="l2Blocks">
<L2Blocks isActive={isActive('l2Blocks')} />
</Modal>

{/* L1 Chain */}
<Modal nodeId="l1Chain">
<DiagramNode nodeId="l1Chain" isActive={isActive('l1Chain')} />
</Modal>
</g>

{/* Arbitrum Logo */}
<g transform="translate(20, 520)">
<ArbitrumLogo />
</g>
</svg>
);
}

function ArbitrumLogo() {
return (
<g fill="white" opacity="0.8">
<path d="M10.5 0L0 18h4.2l6.3-10.9L16.8 18H21L10.5 0z" />
<text x="25" y="14" fontSize="14" fontWeight="600" fill="white">
ARBITRUM
</text>
</g>
);
}
162 changes: 162 additions & 0 deletions src/components/InteractiveDiagrams/TransactionLifecycle/Modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import React, { useState } from 'react';
import { useTransition, animated } from '@react-spring/web';
import * as Dialog from '@radix-ui/react-dialog';
import { createPortal } from 'react-dom';
import { MDXProvider } from '@mdx-js/react';
import { useColorMode } from '@docusaurus/theme-common';
import type { NodeId } from './types';
import { NODE_LABELS, DEEP_DIVE_LINKS } from './constants';

import userInboxContent from './modals/modal-user-inbox.mdx';
import delayedInboxContent from './modals/modal-delayed-inbox.mdx';
import sequencerContent from './modals/modal-sequencer.mdx';
import sequencerFeedContent from './modals/modal-sequencer-feed.mdx';
import batchCompressContent from './modals/modal-batch-compress.mdx';
import sequencedTxsContent from './modals/modal-sequenced-txs.mdx';
import stfContent from './modals/modal-stf.mdx';
import stateContent from './modals/modal-state.mdx';
import l2BlocksContent from './modals/modal-l2-blocks.mdx';
import l1ChainContent from './modals/modal-l1-chain.mdx';

const components = {
h1: ({ children }: { children: React.ReactNode }) => <h1 className="modal__title">{children}</h1>,
p: ({ children }: { children: React.ReactNode }) => <p>{children}</p>,
ul: ({ children }: { children: React.ReactNode }) => <ul>{children}</ul>,
ol: ({ children }: { children: React.ReactNode }) => <ol>{children}</ol>,
li: ({ children }: { children: React.ReactNode }) => <li>{children}</li>,
a: ({ href, children }: { href: string; children: React.ReactNode }) => (
<a href={href} target="_blank" rel="noopener noreferrer">
{children}
</a>
),
};

const contentMap: Record<NodeId, React.ComponentType> = {
userInbox: userInboxContent,
delayedInbox: delayedInboxContent,
sequencer: sequencerContent,
sequencerFeed: sequencerFeedContent,
batchCompress: batchCompressContent,
sequencedTxs: sequencedTxsContent,
stf: stfContent,
state: stateContent,
l2Blocks: l2BlocksContent,
l1Chain: l1ChainContent,
};

interface ModalProps {
nodeId: NodeId;
children: React.ReactNode;
}

export function Modal({ nodeId, children }: ModalProps) {
const [isOpen, setIsOpen] = useState(false);

let isDarkTheme = false;
try {
const colorMode = useColorMode();
isDarkTheme = colorMode.isDarkTheme;
} catch {
// During SSG, useColorMode throws an error - use default
}

const ContentComponent = contentMap[nodeId];
const deepDiveLink = DEEP_DIVE_LINKS[nodeId];

const transitions = useTransition(isOpen, {
from: { opacity: 0, transform: 'scale(0.95)' },
enter: { opacity: 1, transform: 'scale(1)' },
leave: { opacity: 0, transform: 'scale(0.95)' },
config: { tension: 300, friction: 20 },
});

const overlayTransitions = useTransition(isOpen, {
from: { opacity: 0 },
enter: { opacity: 1 },
leave: { opacity: 0 },
config: { duration: 200 },
});

return (
<>
<g
onClick={() => setIsOpen(true)}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
setIsOpen(true);
}
}}
role="button"
tabIndex={0}
aria-label={`Learn more about ${NODE_LABELS[nodeId]}`}
style={{
cursor: 'pointer',
pointerEvents: 'all',
}}
>
{children}
</g>
{typeof document !== 'undefined' &&
createPortal(
<Dialog.Root open={isOpen} onOpenChange={setIsOpen}>
<Dialog.Portal>
{overlayTransitions(
(styles, item) =>
item && (
<animated.div>
<Dialog.Overlay
className="modal__overlay"
style={styles as React.CSSProperties}
/>
</animated.div>
),
)}
{transitions(
(styles, item) =>
item && (
<div className="modal__container">
<Dialog.Content
className="modal__content"
forceMount
style={{
...(styles as React.CSSProperties),
pointerEvents: 'auto',
}}
>
<header className="modal__header">
<Dialog.Close
className="modal__close-button"
onClick={() => setIsOpen(false)}
>
<CloseIcon />
</Dialog.Close>
</header>
<div className="modal__body">
<MDXProvider components={components}>
<ContentComponent />
</MDXProvider>
<div className="transaction-lifecycle-modal__deep-dive">
<a href={deepDiveLink}>Read the deep dive documentation &rarr;</a>
</div>
</div>
</Dialog.Content>
</div>
),
)}
</Dialog.Portal>
</Dialog.Root>,
document.body,
)}
</>
);
}

const CloseIcon = () => (
<svg width="22" height="22" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M15.9574 14.1689L8.59651 6.75098L6.73232 8.59598L14.1313 16.071L6.71338 23.4129L8.5964 25.2769L15.9574 17.8779L23.3943 25.2769L25.2392 23.4129L17.8213 16.071L25.2202 8.59598L23.3752 6.75098L15.9574 14.1689Z"
fill="currentColor"
/>
</svg>
);
Loading
Loading