From 8b245e77ceb03339e13e2a789ddb9379e5ad2ba1 Mon Sep 17 00:00:00 2001 From: maxnorm Date: Tue, 21 Jul 2026 15:11:54 -0400 Subject: [PATCH 1/5] docs: first draft my-first-diamond page --- .../docs/getting-started/my-first-diamond.mdx | 312 ++++++++++++++++++ 1 file changed, 312 insertions(+) create mode 100644 website/docs/getting-started/my-first-diamond.mdx diff --git a/website/docs/getting-started/my-first-diamond.mdx b/website/docs/getting-started/my-first-diamond.mdx new file mode 100644 index 00000000..45ce617b --- /dev/null +++ b/website/docs/getting-started/my-first-diamond.mdx @@ -0,0 +1,312 @@ +--- +sidebar_position: 2 +title: "Building My First Diamond" +sidebar_label: "Build Your First Diamond" +description: "Follow along to scaffold, build, test, and deploy your first diamond smart contract with Compose CLI." +--- + +import DocSubtitle from '@site/src/components/docs/DocSubtitle'; +import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; +import Callout from '@site/src/components/ui/Callout'; + +# Building My First Diamond + + +Create a working diamond project using Compose then explore every file so you know exactly what you have. + + +This tutorial uses the **Counter** example: a minimal diamond with increment, decrement, and read functions. It's the simplest way to see how diamonds works end-to-end. + +## Prerequisites + +- [Node.js](https://nodejs.org/) >= 20 +- [Foundry](https://book.getfoundry.sh/getting-started/installation) installed and on your PATH + + +Althought the CLI support both Foundry and Hardhat, this tutorial will focus on a Foundry based project. All learnings from the diamond archtiecture are the same for both framework + +If you haven't used Foundry before, follow the official installation guide first. You only need `forge` and `anvil` for this tutorial. + + +## Step 1) Install the CLI + +Install the Compose CLI so you can run all the CLI commands + +```bash +npm install -g @perfect-abstractions/compose-cli +``` + +Verify it's installed: + +```bash +compose --help +``` + +You can skip the global package installation. Just use `npx @perfect-abstractions/compose-cli` wherever you see `compose` in this tutorial. + +## Step 2) Scaffold the project + +```bash +compose init +``` + +You'll be prompt to choose from a lot of different options from framework, ERC standards templates, and others. For this tutorial, we will choose the Counter example with the following options: + +```bash + +``` + + + + + +You can skip the prompts entirely: +```bash +compose init my-first-diamond --framework foundry --base counter --yes +``` + + +When the CLI finishes you'll see: + +``` +✔ Project "my-first-diamond" scaffolded in ".../my-first-diamond" + +Next steps: +1. cd my-first-diamond +2. forge build && forge test +``` + +## Step 3) Explore the project structure + +`cd` into your new project and look around: + +```bash +cd my-first-diamond +ls -R src/ +``` + +You'll see something like this: + +``` +src/ + diamond/ + Diamond.sol ← The diamond proxy contract + facets/ + CounterDataFacet.sol ← Read the counter value + CounterIncrementFacet.sol ← Increment functions + CounterDecrementFacet.sol ← Decrement functions +``` + +There's also: + +``` +test/ + Diamond.t.sol ← Your test file +script/ + Deploy.s.sol ← Deployment script +compose.json ← Compose project config +``` + +### Diamond.sol — The proxy + +Open `src/diamond/Diamond.sol`. This is the contract users interact with. It doesn't contain any business logic itself — it **delegates** every call to the facets you registered: + +```solidity +contract Diamond { + constructor(address[] memory _facets) { + // Registers each facet's selectors + } + + fallback() external payable { + DiamondMod.diamondFallback(); + } + + receive() external payable {} +} +``` + +The `fallback` function is the magic: when someone calls a function on the diamond, `DiamondMod` figures out which facet has that function and forwards the call. + +### CounterDataFacet.sol — Shared storage + +Open `src/facets/CounterDataFacet.sol`. This defines how the counter's data is stored: + +```solidity +contract CounterDataFacet { + bytes32 constant STORAGE_POSITION = keccak256("counter"); + + struct CounterStorage { + uint256 count; + } + + function getStorage() internal pure returns (CounterStorage storage s) { + bytes32 position = STORAGE_POSITION; + assembly { + s.slot := position + } + } + + function getCount() external view returns (uint256) { + return getStorage().count; + } +} +``` + +Every facet that touches counter data uses the same `STORAGE_POSITION` and `CounterStorage` struct. This is how facets **share state** without importing each other — they all point to the same storage slot. + +### exportSelectors() + +Each facet has an `exportSelectors()` function that declares which functions the diamond should register. For example, `CounterIncrementFacet`: + +```solidity +function exportSelectors() external pure returns (bytes memory) { + return bytes.concat(this.increment.selector, this.incrementBy.selector); +} +``` + +This is how Compose knows which selectors belong to which facet during scaffolding and validation. + +## Step 3 — Build and test + +Now let's compile and run the tests: + +```bash +forge build && forge test -vv +``` + +You should see compilation succeed and a passing test: + +``` +[PASS] test_inspect_facetAddresses() (gas: ...) +``` + +The test deploys a diamond with all three counter facets, then calls `facetAddresses()` on `DiamondInspectFacet` to verify the right number of facets are registered. + + +It's a Compose library facet that comes baked into every scaffolded diamond. It gives you functions like `facetAddresses()`, `facetFunctionSelectors()`, and `facets()` — standard introspection for ERC-2535 diamonds. + + +## Step 4) Deploy locally + +Start a local Ethereum node in one terminal: + +```bash +anvil +``` + +In a second terminal, deploy your diamond: + +```bash +forge script script/Deploy.s.sol:DeployScript \ + --rpc-url http://localhost:8545 \ + --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \ + --broadcast +``` + + +The private key above is Anvil's default test key. **Never** use it on a real network. + + +You'll see the diamond address logged: + +``` +Diamond: 0x5FbDB2315678afecb367f032d93F642f64180aa3 +``` + +## Step 5) Understand the counter facets + +Your diamond now has three facets wired up. Here's what each one does: + +### CounterDataFacet + +| Function | Description | +|----------|-------------| +| `getCount()` | Returns the current counter value | + +### CounterIncrementFacet + +| Function | Description | +|----------|-------------| +| `increment()` | Adds 1 to the counter | +| `incrementBy(uint256)` | Adds a custom amount | + +### CounterDecrementFacet + +| Function | Description | +|----------|-------------| +| `decrement()` | Subtracts 1 (reverts on underflow) | +| `decrementBy(uint256)` | Subtracts a custom amount (reverts on underflow) | + +All three facets share the same `CounterStorage` struct at the same storage slot. That's how `increment()` in one facet can affect what `getCount()` returns in another. + +## Step 6) Write your own test + +Let's interact with the counter through a test. Open `test/Diamond.t.sol` and add a new test: + +```solidity +function test_increment_and_read() public { + // Deploy with counter facets + address[] memory facets = new address[](4); + facets[0] = address(new CounterDataFacet()); + facets[1] = address(new CounterIncrementFacet()); + facets[2] = address(new CounterDecrementFacet()); + facets[3] = address(new DiamondInspectFacet()); + + Diamond diamond = new Diamond(facets); + + // Interact through the diamond + CounterIncrementFacet inc = CounterIncrementFacet(address(diamond)); + CounterDataFacet data = CounterDataFacet(address(diamond)); + + assertEq(data.getCount(), 0); + + inc.increment(); + assertEq(data.getCount(), 1); + + inc.incrementBy(9); + assertEq(data.getCount(), 10); +} +``` + +Run it: + +```bash +forge test --match-test test_increment_and_read -vvv +``` + +The `-vvv` flag gives you full trace output if anything fails. + +## What's next? + +You've built a working diamond with shared storage, multiple facets, and a working test suite. From here you can: + + + + + + + + + +Run `npx @perfect-abstractions/compose-cli init` again and pick **ERC-20** or **ERC-721** to see how the same pattern scales to token standards. + From 2e222be6c2cccded644bc12d5cf7c3135b8f6d07 Mon Sep 17 00:00:00 2001 From: maxnorm Date: Thu, 23 Jul 2026 17:41:56 -0400 Subject: [PATCH 2/5] update darker bg to a darker one --- website/src/css/sidebar.css | 6 +++--- website/src/css/variables.css | 36 +++++++++++++++++------------------ 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/website/src/css/sidebar.css b/website/src/css/sidebar.css index fd674079..c7574bda 100644 --- a/website/src/css/sidebar.css +++ b/website/src/css/sidebar.css @@ -21,12 +21,12 @@ /* Dark mode sidebar */ [data-theme='dark'] .theme-doc-sidebar-container { - background-color: #0f172a; - border-right: 1px solid #1e293b; + background-color: #020613; + border-right: 1px solid #020613; } [data-theme='dark'] .menu { - background-color: #0f172a; + background-color: #020613; } [data-theme='dark'] .theme-doc-toc-desktop { diff --git a/website/src/css/variables.css b/website/src/css/variables.css index 1d64ac53..8d9343fe 100644 --- a/website/src/css/variables.css +++ b/website/src/css/variables.css @@ -110,9 +110,9 @@ --ifm-color-accent-light: #7dd3fc; /* Dark blue backgrounds */ - --ifm-background-color: #0f172a; - --ifm-background-surface-color: #1e293b; - --ifm-navbar-background-color: #0a0e1a; + --ifm-background-color: #020613; + --ifm-background-surface-color: #020613; + --ifm-navbar-background-color: #050b1d; /* Sidebar colors */ --ifm-menu-color: #a0a0a0; @@ -131,30 +131,30 @@ --ifm-color-emphasis-900: #ffffff; /* Border colors - blue tones */ - --ifm-color-emphasis-100: #1e293b; - --ifm-color-emphasis-200: #334155; + --ifm-color-emphasis-100: #020613; + --ifm-color-emphasis-200: #020613; - --ifm-code-background: #1e293b; + --ifm-code-background: #020613; --docusaurus-highlighted-code-line-bg: rgba(59, 130, 246, 0.15); - /* Brand tokens (dark) */ - --compose-bg-900: #0f172a; - --compose-bg-800: #1e293b; +/* Brand tokens (dark) */ + --compose-bg-900: #020613; + --compose-bg-800: #020613; --compose-primary-500: #60a5fa; --compose-primary-600: #3b82f6; /* Homepage CTA + stats — reversed gradients meet at same hue (mirrors light band) */ - --home-band-bg: linear-gradient(180deg, var(--compose-bg-900) 0%, #0c1323 100%); - /* CTA bottom (#0c1323) must equal stats top — same as light reverse-bg, not duplicate of --home-band-bg */ - --home-band-reverse-bg: linear-gradient(180deg, #0c1323 0%, var(--compose-bg-900) 100%); + --home-band-bg: linear-gradient(180deg, var(--compose-bg-900) 0%, #020613 100%); + /* CTA bottom (#020613) must equal stats top — same as light reverse-bg, not duplicate of --home-band-bg */ + --home-band-reverse-bg: linear-gradient(180deg, #020613 0%, var(--compose-bg-900) 100%); /* Hero gradient - dark blue */ --hero-gradient: linear-gradient(135deg, var(--compose-bg-900) 0%, var(--compose-bg-800) 100%); - --hero-gradient-alt: linear-gradient(135deg, var(--compose-bg-800) 0%, #334155 100%); + --hero-gradient-alt: linear-gradient(135deg, var(--compose-bg-800) 0%, #020613 100%); /* Homepage hero tokens (dark overrides if needed) */ - --hero-bg-start: #0f172a; - --hero-bg-end: #1e293b; + --hero-bg-start: #020613; + --hero-bg-end: #020613; --hero-text-strong: rgba(255, 255, 255, 1); --hero-text-medium: rgba(255, 255, 255, 0.9); --hero-text-weak: rgba(255, 255, 255, 0.75); @@ -182,15 +182,15 @@ html { /* Main content area dark styling */ [data-theme='dark'] .main-wrapper { - background: #0f172a; + background: #020613; } [data-theme='dark'] .docMainContainer { - background: #0f172a; + background: #020613; } [data-theme='dark'] article { - background: #0f172a; + background: #020613; } /* Documentation content wrapper */ From 229a6203968b0dd504b9ef865068f0d0141a600a Mon Sep 17 00:00:00 2001 From: maxnorm Date: Thu, 23 Jul 2026 17:42:30 -0400 Subject: [PATCH 3/5] docs: add first-diamond tutorial (counter) --- .../docs/getting-started/my-first-diamond.mdx | 345 +++++++++--------- website/src/css/code-blocks.css | 8 +- 2 files changed, 179 insertions(+), 174 deletions(-) diff --git a/website/docs/getting-started/my-first-diamond.mdx b/website/docs/getting-started/my-first-diamond.mdx index 45ce617b..9f84634f 100644 --- a/website/docs/getting-started/my-first-diamond.mdx +++ b/website/docs/getting-started/my-first-diamond.mdx @@ -8,6 +8,7 @@ description: "Follow along to scaffold, build, test, and deploy your first diamo import DocSubtitle from '@site/src/components/docs/DocSubtitle'; import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; import Callout from '@site/src/components/ui/Callout'; +import ExpandableCode from '@site/src/components/code/ExpandableCode'; # Building My First Diamond @@ -15,7 +16,7 @@ import Callout from '@site/src/components/ui/Callout'; Create a working diamond project using Compose then explore every file so you know exactly what you have. -This tutorial uses the **Counter** example: a minimal diamond with increment, decrement, and read functions. It's the simplest way to see how diamonds works end-to-end. +This tutorial uses the `Counter` example: a minimal diamond with increment and read functions. It's the simplest way to see how diamonds works end-to-end. ## Prerequisites @@ -32,89 +33,81 @@ If you haven't used Foundry before, follow the +{`npm install -g @perfect-abstractions/compose-cli`} + Verify it's installed: -```bash -compose --help -``` + +{`compose --help`} + You can skip the global package installation. Just use `npx @perfect-abstractions/compose-cli` wherever you see `compose` in this tutorial. ## Step 2) Scaffold the project -```bash -compose init -``` + +{`compose init`} + -You'll be prompt to choose from a lot of different options from framework, ERC standards templates, and others. For this tutorial, we will choose the Counter example with the following options: +You'll be prompt to choose from a lot of different options from framework, ERC standards templates, and others. -```bash +For this tutorial, we will choose the `Counter` example with the following options: -``` + +{`Enter project name: my-diamond +Select project framework: Foundry +Select base: Counter +Select extension facets: None +Select Compose library facets: None +Select ownership: None +Select access control: None +? Install project dependencies? (Y/n) Yes`} + +Take the time to review the full library available to you. We support most major token standards like `ERC20`, `ERC721` with different type of access control. +You can always skip the interactive prompts entirely: + +{`compose init my-first-diamond --framework foundry --base counter --yes`} + - -You can skip the prompts entirely: -```bash -compose init my-first-diamond --framework foundry --base counter --yes -``` - -When the CLI finishes you'll see: +When the CLI finishes, follow the next steps instruction to move into your project and test it: -``` -✔ Project "my-first-diamond" scaffolded in ".../my-first-diamond" + +{`✔ Project "my-first-diamond" scaffolded in ".../my-first-diamond" Next steps: 1. cd my-first-diamond -2. forge build && forge test -``` +2. forge build && forge test`} + ## Step 3) Explore the project structure -`cd` into your new project and look around: - -```bash -cd my-first-diamond -ls -R src/ -``` - -You'll see something like this: +The scaffold generates a diamond with two local facets and 1 from teh Compose library. -``` -src/ - diamond/ - Diamond.sol ← The diamond proxy contract + +{`src/ + Diamond.sol ← proxy: wires everything together facets/ - CounterDataFacet.sol ← Read the counter value - CounterIncrementFacet.sol ← Increment functions - CounterDecrementFacet.sol ← Decrement functions -``` + CounterDataFacet.sol ← get the current count + CounterIncrementFacet.sol ← increment the counter`} + -There's also: +Each facet owns a slice of the logic. `CounterDataFacet` exposes the count, `CounterIncrementFacet` mutates it. The `Diamond.sol` proxy routes calls to the right facet via function selectors. -``` -test/ - Diamond.t.sol ← Your test file -script/ - Deploy.s.sol ← Deployment script -compose.json ← Compose project config -``` +### Diamond.sol: The proxy -### Diamond.sol — The proxy +The diamond is the application entrypoint. Users call it, but it holds no logic itself, only the storage state. Every call is delegated to a stateless facet via the fallback function: -Open `src/diamond/Diamond.sol`. This is the contract users interact with. It doesn't contain any business logic itself — it **delegates** every call to the facets you registered: - -```solidity -contract Diamond { + +{`contract Diamond { constructor(address[] memory _facets) { - // Registers each facet's selectors + // Add all the facets address provided to constructor + DiamondMod.addFacets(_facets); } fallback() external payable { @@ -122,23 +115,28 @@ contract Diamond { } receive() external payable {} -} -``` +}`} + -The `fallback` function is the magic: when someone calls a function on the diamond, `DiamondMod` figures out which facet has that function and forwards the call. +When a function is called on the diamond, [`DiamondMod.diamondFallback()`](/docs/library/diamond/DiamondMod#diamondfallback) looks up which contract owns that selector and forwards the call. -### CounterDataFacet.sol — Shared storage +There nothing more to make a diamond proxy work. Now, let's transition where our application logic lives -Open `src/facets/CounterDataFacet.sol`. This defines how the counter's data is stored: +### CounterDataFacet.sol -```solidity -contract CounterDataFacet { +Facets need to share state. They do this by pointing to the same storage slot: + + +{`contract CounterDataFacet { + // 1. Slot address: a deterministic location shared across the diamond bytes32 constant STORAGE_POSITION = keccak256("counter"); + // 2. Data struct: defines the shape of data stored at that slot: struct CounterStorage { uint256 count; } + // 3. Assembly storage getter: \`storage\` reference pointer to that slot: function getStorage() internal pure returns (CounterStorage storage s) { bytes32 position = STORAGE_POSITION; assembly { @@ -146,163 +144,166 @@ contract CounterDataFacet { } } + // ---------------------------------- + // Facet functions + // ---------------------------------- + function getCount() external view returns (uint256) { return getStorage().count; } -} -``` - -Every facet that touches counter data uses the same `STORAGE_POSITION` and `CounterStorage` struct. This is how facets **share state** without importing each other — they all point to the same storage slot. -### exportSelectors() - -Each facet has an `exportSelectors()` function that declares which functions the diamond should register. For example, `CounterIncrementFacet`: + // Declares which selectors this facet registers on the diamond proxy + function exportSelectors() external pure returns (bytes memory) { + return bytes.concat(this.getCount.selector); + } +}`} + -```solidity -function exportSelectors() external pure returns (bytes memory) { - return bytes.concat(this.increment.selector, this.incrementBy.selector); -} -``` +Any facet that need `Counter` data uses the same `STORAGE_POSITION [ keccak256("counter") ]` and `CounterStorage` struct. They never import each other. They just agree on the same storage location. -This is how Compose knows which selectors belong to which facet during scaffolding and validation. +Facets can be scoped based on your project needs. Compose encourages small scoped facets, one facet per responsibility, to allow granular composition. In this example, `CounterDataFacet` handles reads and `CounterIncrementFacet` handles mutations. You could combine them into a single facet, but keeping them separate lets you pick exactly the functionality you need at anytime -## Step 3 — Build and test +## Step 4) Build and test Now let's compile and run the tests: -```bash -forge build && forge test -vv -``` - -You should see compilation succeed and a passing test: - -``` -[PASS] test_inspect_facetAddresses() (gas: ...) -``` - -The test deploys a diamond with all three counter facets, then calls `facetAddresses()` on `DiamondInspectFacet` to verify the right number of facets are registered. - - -It's a Compose library facet that comes baked into every scaffolded diamond. It gives you functions like `facetAddresses()`, `facetFunctionSelectors()`, and `facets()` — standard introspection for ERC-2535 diamonds. - - -## Step 4) Deploy locally + +{`forge build && forge test -vv`} + -Start a local Ethereum node in one terminal: +You should see compilation succeed and one passing test in `/test/Diamond.t.sol` -```bash -anvil -``` +The test deploys a diamond with all three facets the `Counter` needs, then calls `facetAddresses()` on `DiamondInspectFacet` to verify the right number of facets are registered. -In a second terminal, deploy your diamond: +## Step 5) Write your own test -```bash -forge script script/Deploy.s.sol:DeployScript \ - --rpc-url http://localhost:8545 \ - --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \ - --broadcast -``` +Let's interact with the `Counter` through a test. Open `test/Diamond.t.sol` and take a look at what's already there: - -The private key above is Anvil's default test key. **Never** use it on a real network. - + +{`// SPDX-License-Identifier: MIT +pragma solidity ^0.8.30; -You'll see the diamond address logged: +import {Test} from "forge-std/Test.sol"; +import {Diamond} from "../src/Diamond.sol"; +import {CounterDataFacet} from "../src/facets/CounterDataFacet.sol"; +import {CounterIncrementFacet} from "../src/facets/CounterIncrementFacet.sol"; +import {DiamondInspectFacet} from "@perfect-abstractions/compose/diamond/DiamondInspectFacet.sol"; -``` -Diamond: 0x5FbDB2315678afecb367f032d93F642f64180aa3 -``` +contract DiamondTest is Test { + Diamond diamond; -## Step 5) Understand the counter facets + // This setup function runs before each test + function setUp() public { + address[] memory facets = new address[](3); -Your diamond now has three facets wired up. Here's what each one does: + // 1. Deploying the Counter specific facets + facets[0] = address(new CounterDataFacet()); + facets[1] = address(new CounterIncrementFacet()); -### CounterDataFacet + // 2. Deploying the required Inspection Facet from @perfect-abstractions/compose + facets[2] = address(new DiamondInspectFacet()); -| Function | Description | -|----------|-------------| -| `getCount()` | Returns the current counter value | - -### CounterIncrementFacet + // 3. Deploying your proxy with all 3 addresses passed to the constructor + diamond = new Diamond(facets); + } -| Function | Description | -|----------|-------------| -| `increment()` | Adds 1 to the counter | -| `incrementBy(uint256)` | Adds a custom amount | + function test_inspect_facetAddresses() public view { + DiamondInspectFacet inspect = DiamondInspectFacet(address(diamond)); + address[] memory addresses = inspect.facetAddresses(); + assertEq(addresses.length, 3); + } +}`} + + +The test file already has a `setUp()` function that deploys a fresh `Diamond` instance before each test. There's also one test that verifies all three facets are properly registered on the proxy. + +Take a look at the order here. The facets are deployed first as separate contracts, then the Diamond proxy is created with references to those pre-deployed facet addresses. This is the core of [composition](/docs/design/design-for-composition) over inheritance. Instead of a monolithic contract that `is` everything through inheritance chains, the Diamond `has` the facets it needs. They're composed at runtime via the constructor or the upgrade functionality. + +Now let's add a test that actually uses our `Counter`. Add this function inside the `DiamondTest` contract: + + +{`function test_counter_incrementAndRead() public { + // Unique address entrypoint + address diamondAddress = address(diamond); + + // Cast diamond to both facet interfaces + CounterIncrementFacet incrementer = CounterIncrementFacet(diamondAddress); + CounterDataFacet getter = CounterDataFacet(diamondAddress); + + // Initial count should be 0 + assertEq(getter.getCount(), 0); + + // Increment through one facet + incrementer.increment(); + + // Counter should now be 1 + assertEq(getter.getCount(), 1); + + // Increment again by 2 (counter should now be 3) + incrementer.incrementBy(2); + assertEq(getter.getCount(), 3); +} +`} + -### CounterDecrementFacet +Run the test suite: -| Function | Description | -|----------|-------------| -| `decrement()` | Subtracts 1 (reverts on underflow) | -| `decrementBy(uint256)` | Subtracts a custom amount (reverts on underflow) | + +{`forge test`} + -All three facets share the same `CounterStorage` struct at the same storage slot. That's how `increment()` in one facet can affect what `getCount()` returns in another. +All tests pass, demonstrating that data written by one facet is immediately visible to another through the diamond's shared storage. -## Step 6) Write your own test +## Step 6) Deploy locally -Let's interact with the counter through a test. Open `test/Diamond.t.sol` and add a new test: +Start a local Ethereum node in one terminal: -```solidity -function test_increment_and_read() public { - // Deploy with counter facets - address[] memory facets = new address[](4); - facets[0] = address(new CounterDataFacet()); - facets[1] = address(new CounterIncrementFacet()); - facets[2] = address(new CounterDecrementFacet()); - facets[3] = address(new DiamondInspectFacet()); + +{`anvil`} + - Diamond diamond = new Diamond(facets); +In a second terminal, deploy your Counter application: - // Interact through the diamond - CounterIncrementFacet inc = CounterIncrementFacet(address(diamond)); - CounterDataFacet data = CounterDataFacet(address(diamond)); + +{`forge script script/Deploy.s.sol:DeployScript \\ + --rpc-url http://localhost:8545 \\ + --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \\ + --broadcast`} + - assertEq(data.getCount(), 0); + +The private key above is Anvil's default test key. **Never use it on a real network.** + - inc.increment(); - assertEq(data.getCount(), 1); +You'll see the deployed addresses logged: - inc.incrementBy(9); - assertEq(data.getCount(), 10); -} -``` + +{`== Logs == + CounterDataFacet: 0x5FbDB2315678afecb367f032d93F642f64180aa3 + CounterIncrementFacet: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 + DiamondInspectFacet: 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0 + Diamond: 0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9`} + -Run it: +#### Congratulations! -```bash -forge test --match-test test_increment_and_read -vvv -``` +You just deployed your first diamond proxy application. You've scaffolded a `Counter` project using the CLI, written tests that verify shared storage across facets, and deployed a composed system to a local network. -The `-vvv` flag gives you full trace output if anything fails. +We recommend you to explore all the template available to scaffold. Use the the `catalog` command to review what's available to you. ## What's next? -You've built a working diamond with shared storage, multiple facets, and a working test suite. From here you can: - - - diff --git a/website/src/css/code-blocks.css b/website/src/css/code-blocks.css index ae6b8885..c8adc663 100644 --- a/website/src/css/code-blocks.css +++ b/website/src/css/code-blocks.css @@ -144,7 +144,11 @@ code { } .token.comment { - color: #546e7a; - font-style: italic; + color: #425c68 !important; +} + +/* Dark mode - lighter comment color for readability on dark background */ +[data-theme='dark'] .token.comment { + color: #b0b9c7 !important; } From 97c914045046e697953f791b3ac0be3cce464ce7 Mon Sep 17 00:00:00 2001 From: maxnorm Date: Thu, 23 Jul 2026 18:00:29 -0400 Subject: [PATCH 4/5] docs: update /my-first-diamond page metadata --- website/docs/getting-started/my-first-diamond.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/getting-started/my-first-diamond.mdx b/website/docs/getting-started/my-first-diamond.mdx index 9f84634f..5c3753ce 100644 --- a/website/docs/getting-started/my-first-diamond.mdx +++ b/website/docs/getting-started/my-first-diamond.mdx @@ -2,7 +2,7 @@ sidebar_position: 2 title: "Building My First Diamond" sidebar_label: "Build Your First Diamond" -description: "Follow along to scaffold, build, test, and deploy your first diamond smart contract with Compose CLI." +description: "Follow along to scaffold, build, test, and deploy your first diamond smart contract application with Compose" --- import DocSubtitle from '@site/src/components/docs/DocSubtitle'; From a1d7645c96caec01dd43381f4a26f511a4bbd9cc Mon Sep 17 00:00:00 2001 From: maxnorm Date: Fri, 24 Jul 2026 16:27:38 -0400 Subject: [PATCH 5/5] fix typos --- website/docs/getting-started/my-first-diamond.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/website/docs/getting-started/my-first-diamond.mdx b/website/docs/getting-started/my-first-diamond.mdx index 5c3753ce..23d7884a 100644 --- a/website/docs/getting-started/my-first-diamond.mdx +++ b/website/docs/getting-started/my-first-diamond.mdx @@ -1,6 +1,6 @@ --- sidebar_position: 2 -title: "Building My First Diamond" +title: "Building Your First Diamond" sidebar_label: "Build Your First Diamond" description: "Follow along to scaffold, build, test, and deploy your first diamond smart contract application with Compose" --- @@ -10,7 +10,7 @@ import DocCard, { DocCardGrid } from '@site/src/components/docs/DocCard'; import Callout from '@site/src/components/ui/Callout'; import ExpandableCode from '@site/src/components/code/ExpandableCode'; -# Building My First Diamond +# Building Your First Diamond Create a working diamond project using Compose then explore every file so you know exactly what you have. @@ -87,7 +87,7 @@ Next steps: ## Step 3) Explore the project structure -The scaffold generates a diamond with two local facets and 1 from teh Compose library. +The scaffold generates a diamond with two local facets and one from the Compose library. {`src/