Skip to content
Merged
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
32 changes: 22 additions & 10 deletions app/components/AppShell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -505,19 +505,31 @@ function GlobalBanner({ dismissed, onDismiss, className }: GlobalBannerProps) {
className,
)}
>
<span className="flex max-w-full flex-wrap items-center justify-center gap-x-2 gap-y-1">
<Text as="span" variant="label.medium">New!</Text>
<Text as="span" variant="label.medium">EIP-8130: Native Account Abstraction</Text>
<span className="inline-block h-3.5 w-px bg-bds-gray-20"></span>
<Link href="/demos/account" className="group flex items-center gap-1 no-underline">
<Text as="span" variant="label.medium" className="text-base-blue">Test on Vibenet</Text>
<AnimatedArrowIcon size={14} strokeWidth={2} className="text-base-blue transition-transform duration-200 ease-out group-hover:translate-x-[3px]" />
</Link>
</span>
<div className="mx-auto flex w-full max-w-6xl flex-col items-center justify-center min-[860px]:flex-row min-[860px]:gap-5">
<div className="flex min-w-0 flex-wrap items-center justify-center gap-x-2 gap-y-1 pb-2 min-[860px]:flex-nowrap min-[860px]:pb-0">
<Text as="span" variant="label.medium" className="whitespace-nowrap">New!</Text>
<Text as="span" variant="label.medium" className="whitespace-nowrap">EIP-8130: Native Account Abstraction</Text>
<span className="inline-block h-3.5 w-px shrink-0 bg-bds-gray-20"></span>
<Link href="/demos/account" className="group flex shrink-0 items-center gap-1 no-underline">
<Text as="span" variant="label.medium" className="text-base-blue">Test on Vibenet</Text>
<AnimatedArrowIcon size={14} strokeWidth={2} className="text-base-blue transition-transform duration-200 ease-out group-hover:translate-x-[3px]" />
</Link>
</div>
<span className="hidden h-1 w-1 shrink-0 rounded-full bg-bds-gray-20 min-[860px]:block"></span>
<div className="flex min-w-0 flex-wrap items-center justify-center gap-x-2 gap-y-1 border-t border-bds-gray-20 pt-2 min-[860px]:flex-nowrap min-[860px]:border-t-0 min-[860px]:pt-0">
<Text as="span" variant="label.medium" className="whitespace-nowrap">New!</Text>
<Text as="span" variant="label.medium" className="whitespace-nowrap">200ms Blocks</Text>
<span className="inline-block h-3.5 w-px shrink-0 bg-bds-gray-20"></span>
<Link href="/vibenet/explorer" className="group flex shrink-0 items-center gap-1 no-underline">
<Text as="span" variant="label.medium" className="text-base-blue">Open Explorer</Text>
<AnimatedArrowIcon size={14} strokeWidth={2} className="text-base-blue transition-transform duration-200 ease-out group-hover:translate-x-[3px]" />
</Link>
</div>
</div>
<button
type="button"
onClick={onDismiss}
className="absolute right-4 top-2 flex h-5 w-5 items-center justify-center text-bds-gray-40 transition-colors hover:text-black"
className="absolute right-4 top-1/2 flex h-5 w-5 -translate-y-1/2 items-center justify-center text-bds-gray-40 transition-colors hover:text-black"
aria-label="Dismiss banner"
>
<CloseIcon size={10} />
Expand Down
6 changes: 3 additions & 3 deletions app/upgrades/UpgradesClient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,9 @@ function TimelineView({ nowMs }: { nowMs: number }) {
const entries = useMemo(() => buildTimelineEntries(nowMs), [nowMs]);
const months = useMemo(() => groupByMonth(entries), [entries]);

const planningUpgrades = upgrades.filter(
(u) => !u.lifecycle.sepolia.timestamp && !u.lifecycle.mainnet.timestamp,
);
const planningUpgrades = upgrades
.filter((u) => !u.lifecycle.sepolia.timestamp && !u.lifecycle.mainnet.timestamp)
.reverse();

let entryIndex = 0;

Expand Down
1 change: 1 addition & 0 deletions app/upgrades/components/UpgradeIllustration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const ILLO_MAP: Record<string, string> = {
azul: '/azul-illo.svg',
beryl: '/beryl-illo.svg',
cobalt: '/cobalt-illo.svg',
denim: '/denim-illo.svg',
};

type UpgradeIllustrationProps = {
Expand Down
21 changes: 21 additions & 0 deletions app/upgrades/data/changes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,27 @@ export const changes: Change[] = [
githubIssues: [],
specUrl: '',
},
{
kind: 'base',
id: 'base-0010',
slug: '200ms-blocks',
baseNumber: '0010',
owner: 'Protocol team',
title: '200ms Blocks',
category: 'execution',
upgrade: 'denim',
summary:
'<p>Base will produce native L2 blocks at a 200ms cadence. Each block has its own number, hash, state root, receipts, and unsafe-to-finalized lifecycle.</p><p class="mt-4">Unlike Flashblocks, which expose sub-second updates while building one block, this change runs the full rollup block lifecycle every 200ms.</p><p class="mt-4">Ethereum block headers and <code>block.timestamp</code> remain in whole seconds. The BaseTime predeploy records the sub-second component before user transactions, while optional <code>timestampMs</code> and <code>blockTimestampMs</code> RPC fields expose the complete millisecond timestamp to applications and infrastructure.</p>',
migrationNotes:
'<p><strong>Application developers</strong></p><p class="mt-2 pb-4">Existing contracts and applications remain compatible. Use the BaseTime <code>timestampMs()</code> getter when contracts need same-block millisecond precision; continue using <code>block.timestamp</code> when second-level precision is sufficient.</p><p><strong>RPC and indexing providers</strong></p><p class="mt-2 pb-4">Support the optional <code>timestampMs</code> block and header field and <code>blockTimestampMs</code> on mined transactions and logs. Do not infer millisecond timestamps from the whole-second header value.</p><p><strong>Node operators</strong></p><p class="mt-2">Upgrade to a Denim-compatible release before activation. Plan capacity for the higher native block rate across execution, storage, RPC events, and peer-to-peer propagation.</p>',
lastUpdated: '2026-08-05',
relatedRepos: [
'https://github.com/base/base',
'https://github.com/base/contracts/blob/4848ec70d8f1062fec59470d6e731e13ece8a728/src/L2/BaseTime.sol',
],
githubIssues: [],
specUrl: '',
},
];

export function getChangeById(id: string): Change | undefined {
Expand Down
18 changes: 18 additions & 0 deletions app/upgrades/data/upgrades.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,24 @@ export const upgrades: Upgrade[] = [
specUrl: '',
blog: '',
},
{
id: 'denim',
name: 'Denim',
summary:
'Denim introduces native blocks at a 200ms cadence, millisecond-resolution RPC timestamps, and onchain millisecond time through BaseTime.',
lifecycle: {
sepolia: { },
mainnet: { },
},
estimate: {
sepolia: 'November 2026',
mainnet: 'November 2026',
},
categories: categoryGroupsForUpgrade('denim'),
migrationGuide: [],
specUrl: '',
blog: '',
},
];

let reversedUpgrades: Upgrade[] | null = null;
Expand Down
24 changes: 24 additions & 0 deletions app/upgrades/library/upgrades.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,30 @@ describe('upgrades route helpers', () => {
expect(getUpgradeById('missing')).toBeUndefined();
});

it('includes Denim with its November 2026 estimate', () => {
const denim = getUpgradeById('denim');
expect(denim?.estimate).toEqual({
sepolia: 'November 2026',
mainnet: 'November 2026',
});
expect(denim?.lifecycle).toEqual({ sepolia: {}, mainnet: {} });
});

it('lists 200ms Blocks as a Denim execution feature', () => {
const change = getChangeBySlug('200ms-blocks');
expect(change).toMatchObject({
id: 'base-0010',
title: '200ms Blocks',
category: 'execution',
upgrade: 'denim',
});

const execution = categoryGroupsForUpgrade('denim').find(
(group) => group.category === 'execution',
);
expect(execution?.changeIds).toEqual(['base-0010']);
});

it('looks up changelog entries by slug', () => {
const change = getChangeBySlug('native-account-abstraction');
expect(change?.id).toBe('eip-8130');
Expand Down
4 changes: 3 additions & 1 deletion app/vibenet/components/FeatureCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,10 @@ export function FeatureCard({ feature }: FeatureCardProps) {
{feature.availability}
{feature.availabilityHref ? (
<Link href={feature.availabilityHref} className="text-black hover:text-base-blue dark:text-white dark:hover:text-bds-blue-20">
Base Cobalt
{feature.availabilityLabel ?? 'Base Cobalt'}
</Link>
) : feature.availabilityLabel ? (
<span className="text-black dark:text-white">{feature.availabilityLabel}</span>
) : null}
</Text>
) : null}
Expand Down
5 changes: 4 additions & 1 deletion app/vibenet/data/contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,24 @@ import type { ContractMeta, ResolvedContract } from '../library/types';
// - `hidden` keeps internal/demo deployments out of the public list;
// - `watch` adds an "add to wallet" affordance for tokens.
export const CONTRACT_REGISTRY: Record<string, ContractMeta> = {
baseTime: { label: 'BaseTime (predeploy)' },
usdv: { label: 'USDV (ERC-20)', watch: { type: 'ERC20', symbol: 'USDV', decimals: 6 } },
nfv: { label: 'NFV (ERC-721)' },
// Internal/demo deployments intentionally omitted from the public list.
faucetAddress: { label: 'Faucet', hidden: true },
vibecheck: { label: 'Vibecheck', hidden: true },
};

const BASE_TIME_ADDRESS = '0x4200000000000000000000000000000000000030';

// Turn the raw contracts map into a display list: keep only real addresses,
// drop hidden entries, and layer on registry metadata (or a humanized label).
// Returns null while the contracts payload hasn't loaded yet.
export function resolveContracts(
contracts: Record<string, unknown> | null,
): ResolvedContract[] | null {
if (!contracts) return null;
return Object.entries(contracts)
return Object.entries({ ...contracts, baseTime: BASE_TIME_ADDRESS })
.filter((entry): entry is [string, string] => isAddress(entry[1]))
.filter(([key]) => !CONTRACT_REGISTRY[key]?.hidden)
.map(([key, address]) => {
Expand Down
39 changes: 39 additions & 0 deletions app/vibenet/data/features.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,45 @@ export const FEATURES: VibenetFeature[] = [
secondaryCta: { label: 'Build Your Own', href: 'https://docs.base.org/base-chain/specs/upgrades/cobalt/eip-8130', external: true },
links: [{ label: 'EIP-8130', href: 'https://eip.tools/eip/8130', external: true }],
},
{
id: '200ms-canonical-blocks',
tag: '200ms',
title: '200ms Blocks',
summary:
'Native blocks at a 200ms cadence, each with its own hash, state, receipts, and finality lifecycle.',
status: 'preview',
availability: 'Coming soon in ',
availabilityLabel: 'Base Denim',
highlights: [
{
title: 'Native Blocks Every 200ms',
detail: 'Full blocks, not partial pending-state updates or Flashblocks.',
},
{
title: 'Millisecond RPC Timestamps',
detail: 'Read exact block time from timestampMs and blockTimestampMs fields.',
},
{
title: 'Onchain Millisecond Time',
detail: 'Contracts can read the current full timestamp from the BaseTime predeploy.',
},
{
title: 'Ethereum Compatible',
detail: 'Header timestamps and EVM block.timestamp remain in whole seconds.',
},
],
cta: {
label: 'Explore Blocks',
href: '/vibenet/explorer',
},
links: [
{
label: 'Protocol source',
href: 'https://github.com/base/contracts/blob/4848ec70d8f1062fec59470d6e731e13ece8a728/src/L2/BaseTime.sol',
external: true,
},
],
},
];

// Raw feature shape as it appears in the API `config` payload.
Expand Down
2 changes: 2 additions & 0 deletions app/vibenet/library/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ export type VibenetFeature = {
status: FeatureStatus;
/** Optional availability note, e.g. "Coming in Base Cobalt". */
availability?: string;
/** Emphasized release name appended to the availability note. */
availabilityLabel?: string;
/** Optional link for the availability note. */
availabilityHref?: string;
/** Bullet highlights; when present the card renders the richer promo layout. */
Expand Down
31 changes: 31 additions & 0 deletions public/denim-illo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading