diff --git a/content/canton/get-started.mdx b/content/canton/get-started.mdx new file mode 100644 index 00000000..036b58fc --- /dev/null +++ b/content/canton/get-started.mdx @@ -0,0 +1,38 @@ +--- +title: Get Started +--- + +This guide covers the prerequisites and the basic project wiring for building on Canton with OpenZeppelin's Daml packages. + + + These packages target the Canton 3.4.x baseline and are under active + development. Pin exact versions from the package manifests in + [`OpenZeppelin/canton-contracts`](https://github.com/OpenZeppelin/canton-contracts) + rather than copying version numbers from this page. + + +## Prerequisites + +* **JDK 21**: the Daml toolchain runs on Java. Install a recent OpenJDK 21 build and make sure `JAVA_HOME` points at it. +* **DPM (Daml Package Manager)**: Digital Asset's package manager and SDK installer. Install it from Digital Asset's public installer, then use it to provision the Daml SDK / Canton baseline. +* **A Daml project**: a directory with a `daml.yaml` (single package) or a `multi-package.yaml` (workspace of packages). + +## Add OpenZeppelin packages + +OpenZeppelin's Canton library is distributed as Daml packages. Add the packages you need as dependencies of your Daml project, then build: + +```bash +# from your project root, with DPM on PATH +dpm build --all +``` + +Refer to each component's page for the module path to import and the templates and interfaces it exposes: + +* [Access Control](/canton/library/access-control) +* [Ownable](/canton/library/ownable) +* [Pausable](/canton/library/pausable) + +## Explore further + +* The **[Settlement (CIP-112)](/canton/settlement)** primitive shows how to compose the library into an atomic, value-moving settlement flow. +* The **[Reference Implementations](/canton/reference-implementations)** walk through complete application blueprints built on these pieces. diff --git a/content/canton/index.mdx b/content/canton/index.mdx new file mode 100644 index 00000000..85e0da24 --- /dev/null +++ b/content/canton/index.mdx @@ -0,0 +1,38 @@ +--- +title: OpenZeppelin for Canton +--- + +OpenZeppelin is building a suite of secure, reusable building blocks for the [Canton Network](https://www.canton.network/), the privacy-enabled network of applications built on [Daml](https://www.digitalasset.com/developers). This section is the home for OpenZeppelin's Canton documentation: the general-purpose library, the settlement primitive, and the Reference Implementations that show how they fit together. + + + The Canton ecosystem stack is under active development. Components are labelled + by maturity throughout these docs. Treat everything marked *experimental* as + a preview surface: interfaces may change, and it is not yet audited or intended + for production use. + + +## Library + +Foundational Daml modules that other packages and applications compose on top of. These live in [`OpenZeppelin/canton-contracts`](https://github.com/OpenZeppelin/canton-contracts). + +* **[Access Control](/canton/library/access-control)**: Role-based authorization for Daml workflows, with an admin role that grants and revokes other roles. +* **[Ownable](/canton/library/ownable)**: A single-owner authorization primitive for privileged actions, with ownership transfer. +* **[Pausable](/canton/library/pausable)**: An emergency stop mechanism that lets an authorized party halt and resume sensitive choices. + +## Settlement + +* **[Settlement (CIP-112)](/canton/settlement)**: An experimental, interface-shaped settlement engine for atomic multi-leg, value-moving delivery-versus-payment, aligned with the Canton Token Standard. + +## Reference Implementations + +* **[Reference Implementations](/canton/reference-implementations)**: End-to-end application blueprints (DEX, Lending, Cross-Chain Stablecoin, Confidential Auction) that demonstrate how the library and settlement primitives compose into real applications. + +## Where to start + +New to the Canton stack? Head to **[Get Started](/canton/get-started)** for prerequisites, toolchain setup, and how to add OpenZeppelin packages to a Daml project. + +--- + + +Canton is a registered trademark of Digital Asset (Switzerland) GmbH. Digital Asset is not affiliated with, and has not sponsored or endorsed, this documentation. + diff --git a/content/canton/library/access-control.mdx b/content/canton/library/access-control.mdx new file mode 100644 index 00000000..e79a1a04 --- /dev/null +++ b/content/canton/library/access-control.mdx @@ -0,0 +1,102 @@ +--- +title: Access Control +--- + +Access Control is a standalone, token-agnostic role-based access control (RBAC) substrate for Daml. It is the Daml analogue of OpenZeppelin's `AccessControl.sol`: an authority (the `admin`, which plays the role of Solidity's `DEFAULT_ADMIN_ROLE` holder) grants named roles to accounts, and gated operations require that the caller holds the right role. + +The package is independent: it has no dependency on [Ownable](/canton/library/ownable) or [Pausable](/canton/library/pausable), so a project that wants only RBAC imports only this one package. + + + Version 0.x, unstable, not yet public API. Interfaces may change before a 1.0 + release. Source: [`OpenZeppelin/canton-contracts`](https://github.com/OpenZeppelin/canton-contracts). + + +```daml +import OpenZeppelin.AccessControl +``` + +## The Daml model + +Two design choices differ from Solidity, and both are deliberate. + +**Roles are `Text` identifiers.** Solidity identifies roles with `bytes32` constants (for example `keccak256("MINTER_ROLE")`). Daml templates are monomorphic (a template field cannot be a type variable), so a reusable role primitive stores `role : Text`. This keeps the library generic: any contract can reuse it. A consumer that wants compile-time exhaustiveness layers its own closed role type on top with a thin `roleId : MyRole -> Text` wrapper. + +**A grant is a bearer credential, not a global map.** Daml-LF 2.1 has no contract keys, so there is no global role table to look up. Instead a `RoleGrant` is a contract signed by `admin` that names one `account`. Possession of a valid grant is the authorization: a gated choice fetches the grant the caller presents and checks that it is admin-signed, names the caller, and carries the required role. Because the grant is admin-signed it cannot be forged, and because it names a specific account the worst an adversary can do is present a grant they do not hold and fail their own authorization. + +## Templates + +### `RoleGrant` + +A role granted by `admin` to `account`. Possession is authorization. + +| Field | Type | Description | +| --- | --- | --- | +| `admin` | `Party` | The role authority (the `DEFAULT_ADMIN_ROLE` analogue) that issued the grant. | +| `account` | `Party` | The party granted the role. | +| `role` | `Text` | The role identifier, for example `"MINTER_ROLE"`. | + +Signatory `admin`, observer `account`. + +* **`RoleGrant_Renounce`**: the grantee gives up its own role, archiving the grant (the `renounceRole` analogue). Self-only by construction, since `account` is the controller. + +### `RoleAdmin` + +The role-administration authority that mints and revokes grants. Two paths coexist: + +* The root path, controlled by `admin`, where the `DEFAULT_ADMIN_ROLE` holder manages any role directly. +* The role-admin path, controlled by an arbitrary `caller` (the `getRoleAdmin` analogue), where a delegate presents a grant for a caller-supplied `adminRole` and may then grant or revoke the target role. The new grant is still `admin`-signed because the choice runs with the contract's authority, so a delegate administers roles without holding the admin key. + +| Choice | Controller | Result | Description | +| --- | --- | --- | --- | +| `RoleAdmin_GrantRole` | `admin` | `ContractId RoleGrant` | Grant `role` to `account`. | +| `RoleAdmin_RevokeRole` | `admin` | `()` | Revoke a grant this admin issued. | +| `RoleAdmin_GrantRoleAs` | `caller` | `ContractId RoleGrant` | Delegate grant: `caller` grants `role` by presenting its own grant for `adminRole`. | +| `RoleAdmin_RevokeRoleAs` | `caller` | `()` | Delegate revoke, gated the same way. | +| `RoleAdmin_BeginDefaultAdminTransfer` | `admin` | `ContractId DefaultAdminTransferOffer` | Begin a two-step, timelocked handoff of a role. | + +The library hard-codes no role-to-admin graph. `adminRole` is a caller-supplied `Text`: a consumer that wants a fixed hierarchy computes which admin role gates which target role in its own code and passes the result in. + +### `DefaultAdminTransferOffer` + +A pending, timelocked transfer of a role to `newAdmin`, the `AccessControlDefaultAdminRules` analogue. It is an offer / accept handshake plus a ledger-time gate: `newAdmin` cannot accept before `effectiveTime`, and `admin` can cancel within the window. Pass the `DEFAULT_ADMIN_ROLE` id as `role` for a default-admin handoff. + +* **`DefaultAdminTransferOffer_Accept`** (controller `newAdmin`): accept once the timelock has elapsed; grants `role` by creating a `RoleGrant` for `newAdmin`. +* **`DefaultAdminTransferOffer_Cancel`** (controller `admin`): cancel the pending handoff (the `cancelDefaultAdminTransfer` analogue). + +## Helper functions + +* **`requireRole : Party -> Text -> Party -> RoleGrant -> Update ()`**: the `requireRole` modifier analogue. Call it at the top of a gated choice. It asserts the grant is issued by the expected `admin`, names the `caller` (anti-impersonation), and carries the required `role`. +* **`hasRole : Party -> Text -> Party -> RoleGrant -> Bool`**: the pure predicate form, for callers that already hold the fetched grant. +* **`requireTimelockElapsed : Time -> Update ()`**: asserts the current ledger time has reached a given effective time. Shared by `DefaultAdminTransferOffer` and reusable by consumers running their own timelocked handoff. + +## Gating an operation + +A consumer's privileged choice fetches the grant the caller presents and validates it before doing work: + +```daml +nonconsuming choice Mint : ContractId Token + with + caller : Party + grantCid : ContractId RoleGrant + amount : Int + controller caller + do + grant <- fetch grantCid + requireRole caller "MINTER_ROLE" admin grant + create Token with owner = caller; amount +``` + +## Errors + +| Message | When | +| --- | --- | +| `AccessControl: grant admin is not the expected authority` | The presented grant was issued by a different admin. | +| `AccessControl: grant does not name the caller (impersonation)` | The grant does not name the calling party. | +| `AccessControl: grant does not carry the required role` | The grant is for a different role. | +| `AccessControl: default-admin handoff nominee is the current admin` | A begin-transfer named the current admin. | +| `AccessControl: default-admin transfer timelock has not elapsed` | Acceptance was attempted before `effectiveTime`. | + +## Related + +* [Ownable](/canton/library/ownable), for the single-owner case. +* [Pausable](/canton/library/pausable), for an emergency stop. diff --git a/content/canton/library/ownable.mdx b/content/canton/library/ownable.mdx new file mode 100644 index 00000000..ed0456ca --- /dev/null +++ b/content/canton/library/ownable.mdx @@ -0,0 +1,77 @@ +--- +title: Ownable +--- + +Ownable is a standalone, token-agnostic single-owner primitive for Daml. It is the analogue of OpenZeppelin's `Ownable.sol` and `Ownable2Step.sol`: exactly one `owner` party at a time, with an explicit transfer handshake and a renounce path. + +The package is independent: it carries no role type and has no dependency on [Access Control](/canton/library/access-control), so a project that wants only "an owner" imports only this one package. + + + Version 0.x, unstable, not yet public API. Interfaces may change before a 1.0 + release. Source: [`OpenZeppelin/canton-contracts`](https://github.com/OpenZeppelin/canton-contracts). + + +```daml +import OpenZeppelin.Ownable +``` + +## Why transfer is always two-step in Daml + +In Solidity `transferOwnership(newOwner)` is a single call: the old owner writes the new owner into storage. That is not possible in Daml. The owner is a signatory of the `Ownership` contract, and a contract cannot be created without the authorization of every signatory, so the current owner cannot unilaterally bind a new owner. + +Transfer is therefore a handshake: the owner makes an `OwnershipOffer`, and the prospective owner accepts it. This is exactly why OpenZeppelin recommends `Ownable2Step` on other ecosystems, and here it is the only correct shape, not merely the safer one. It also guarantees ownership never lands on a party that has not actively agreed to hold it. + +During a pending offer, ownership is suspended: the `Ownership` contract is archived and re-created on accept (to the new owner) or on withdraw / decline (back to the current owner). Consumers that gate on "owner exists" must treat the offer window accordingly. + +## Templates + +### `Ownership` + +Sole ownership of a resource by `owner`. + +| Field | Type | Description | +| --- | --- | --- | +| `owner` | `Party` | The current sole owner. | + +Signatory `owner`. Every choice is owner-controlled. + +* **`Ownership_OfferOwnership`** (returns `ContractId OwnershipOffer`): begin a two-step transfer to `newOwner`. Consuming: ownership is suspended into the returned offer until accepted, withdrawn, or declined. Offering to the current owner is rejected. +* **`Ownership_RenounceOwnership`** (returns `()`): renounce ownership, leaving the resource permanently ownerless (the `renounceOwnership` analogue). Irreversible. + +### `OwnershipOffer` + +A pending ownership transfer awaiting `newOwner`'s decision. + +| Field | Type | Description | +| --- | --- | --- | +| `owner` | `Party` | The current owner who made the offer. | +| `newOwner` | `Party` | The prospective owner who must accept. | + +Signatory `owner`, observer `newOwner`. + +| Choice | Controller | Result | Description | +| --- | --- | --- | --- | +| `OwnershipOffer_Accept` | `newOwner` | `ContractId Ownership` | Accept the transfer. Creates `Ownership` signed by `newOwner`, whose authorization is what makes the transfer sound. | +| `OwnershipOffer_Decline` | `newOwner` | `ContractId Ownership` | Decline; ownership returns to the offerer. | +| `OwnershipOffer_Withdraw` | `owner` | `ContractId Ownership` | Withdraw the offer; ownership returns to the offerer. | + +## Transferring ownership + +```daml +-- current owner offers +offerCid <- exercise ownershipCid Ownership_OfferOwnership with newOwner = bob + +-- prospective owner accepts, becoming the new owner +newOwnershipCid <- exercise offerCid OwnershipOffer_Accept +``` + +## Errors + +| Message | When | +| --- | --- | +| `Ownable: new owner is the current owner` | An offer named the current owner. | + +## Related + +* [Access Control](/canton/library/access-control), when more than one party or action needs independent authorization. +* [Pausable](/canton/library/pausable), for an emergency stop. diff --git a/content/canton/library/pausable.mdx b/content/canton/library/pausable.mdx new file mode 100644 index 00000000..dac20334 --- /dev/null +++ b/content/canton/library/pausable.mdx @@ -0,0 +1,69 @@ +--- +title: Pausable +--- + +Pausable is a standalone, token-agnostic emergency-stop switch for Daml. It is the analogue of OpenZeppelin's `Pausable.sol`: a single boolean the authority can flip, plus a guard (`whenNotPaused`) that gated operations call to refuse work while paused. + +The package is independent: it carries no role type and depends on no other OpenZeppelin package, so a project that wants only a pause switch imports only this one package. + + + Version 0.x, unstable, not yet public API. Interfaces may change before a 1.0 + release. Source: [`OpenZeppelin/canton-contracts`](https://github.com/OpenZeppelin/canton-contracts). + + +```daml +import OpenZeppelin.Pausable +``` + +## What "paused" means on a UTXO ledger + +Solidity stores `_paused` in contract storage and reads it through a modifier. Daml-LF 2.1 has no contract keys, so there is no global "is the resource paused?" lookup. Instead the live `PauseState` is a contract that gated operations read by `ContractId` (disclosed to them by the pauser) and check via `whenNotPaused`. + +Pause is therefore origination control: new operations that consult the flag refuse to start while paused, while already-committed work is unaffected. That is the only pause semantic that is robustly enforceable on a UTXO ledger. Flipping the flag archives the old `PauseState` and creates the next one, which is how state transitions work throughout Daml. + +## Templates + +### `PauseState` + +The current pause state of a resource, administered by `pauser`. + +| Field | Type | Description | +| --- | --- | --- | +| `pauser` | `Party` | The authority that may pause and unpause. | +| `paused` | `Bool` | Whether the resource is currently paused. | + +Signatory `pauser` (the contract is pauser-signed and so unforgeable). Observers are granted read access by explicit disclosure, keeping the privacy surface minimal. + +* **`PauseState_Set`** (returns `ContractId PauseState`): flip the flag, archiving the current state and creating its successor. Rejects a redundant change (setting the flag to its current value) so on-ledger history records only real transitions. +* **`PauseState_Get`** (nonconsuming, returns `Bool`): read the current flag without archiving the contract. + +## Helper functions + +* **`whenNotPaused : PauseState -> Update ()`**: the `whenNotPaused` modifier analogue. A gated operation fetches the disclosed `PauseState` and calls this before doing origination work; it fails the transaction when paused. +* **`isPaused : PauseState -> Bool`**: the pure predicate form, for callers that already hold the flag. + +## Guarding an operation + +```daml +nonconsuming choice Transfer : ContractId Token + with + pauseStateCid : ContractId PauseState + to : Party + amount : Int + controller owner + do + ps <- fetch pauseStateCid + whenNotPaused ps + create Token with owner = to; amount +``` + +## Errors + +| Message | When | +| --- | --- | +| `Pausable: paused` | A guarded operation ran while the resource was paused. | +| `Pausable: redundant pause change` | `PauseState_Set` was called with the flag's current value. | + +## Related + +* [Access Control](/canton/library/access-control) or [Ownable](/canton/library/ownable), to decide who is allowed to pause and resume. diff --git a/content/canton/reference-implementations.mdx b/content/canton/reference-implementations.mdx new file mode 100644 index 00000000..71173f60 --- /dev/null +++ b/content/canton/reference-implementations.mdx @@ -0,0 +1,29 @@ +--- +title: Reference Implementations +--- + +Reference Implementations (RIs) are complete application blueprints that show how OpenZeppelin's Canton library and settlement primitives compose into real applications. Each RI pairs an architecture write-up with working Daml so teams can see an end-to-end pattern, not just isolated modules. + + + The Year-1 Reference Implementations are in the research and design phase. + Architecture reports exist for all four; their application logic is being + built out. See + [`OpenZeppelin/canton-specs`](https://github.com/OpenZeppelin/canton-specs) + for the current reports. + + +## The Year-1 set + +* **DEX**: a privacy-preserving decentralized exchange, settling trades atomically through the settlement primitive. +* **Lending**: a vault-based lending protocol, with collateral, borrowing, and liquidation flows. +* **Cross-Chain Stablecoin**: payment orchestration for a stablecoin across synchronizers. +* **Confidential Auction**: a sealed-bid auction launchpad that keeps bids confidential until resolution. + +## How they relate to the library + +Each RI is intended to reuse the same foundation: [Access Control](/canton/library/access-control), [Ownable](/canton/library/ownable), and [Pausable](/canton/library/pausable) for authorization and safety, and [Settlement](/canton/settlement) for atomic value movement. As the RIs mature, common patterns they surface are candidates for promotion into the general library. + +## Related + +* [Settlement (CIP-112)](/canton/settlement) +* [Get Started](/canton/get-started) diff --git a/content/canton/settlement.mdx b/content/canton/settlement.mdx new file mode 100644 index 00000000..966146c1 --- /dev/null +++ b/content/canton/settlement.mdx @@ -0,0 +1,32 @@ +--- +title: Settlement (CIP-112) +--- + +The settlement primitive is an interface-shaped engine for atomic, value-moving delivery-versus-payment (DvP) on Canton, aligned with the Canton Token Standard and the CIP-112 settlement design. + + + Experimental. The settlement engine is a preview surface built against + stand-in token interfaces while the upstream Canton Token Standard packages + stabilize. It is not audited and not intended for production use. Interfaces + and behavior may change. + + +## What it does + +Settlement coordinates the atomic exchange of assets across multiple parties. A settlement batch groups several legs (each leg moves value from one party to another) and either commits all of them together or none of them, so no party is left partially settled. + +At a high level the primitive covers: + +* **Atomic multi-leg settlement**: settle a batch of legs as a single all-or-nothing operation. +* **Value-moving settlement**: on settlement, receiver legs are credited, rather than only recording a receipt. +* **Allocation lifecycle**: a request, instruction, and allocation flow, with cancel and withdraw paths. +* **Compliance hooks**: fail-closed reference hooks for node-level attestation, and a controlled seizure path for lawful process, gated behind explicit authority. + +## Status + +This primitive currently builds and is exercised by an in-memory test suite against mock token interfaces. Importing the real Canton Token Standard packages, promoting the primitive into the general library, and running it against a live network are tracked as follow-on work. See [`OpenZeppelin/canton-specs`](https://github.com/OpenZeppelin/canton-specs) for the current engine, its threat model, and its audit-readiness notes. + +## Related + +* [Reference Implementations](/canton/reference-implementations), which compose settlement into complete applications. +* [Library](/canton), the access-control, ownable, and pausable primitives settlement builds on. diff --git a/src/app/page.tsx b/src/app/page.tsx index e1db814e..01492560 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -11,6 +11,7 @@ import { import { AnnotationDotsIcon, ArbitrumIcon, + CantonHomeIcon, ContractsLibraryIcon, ContractsMcpIcon, ContractsUpgradesIcon, @@ -269,6 +270,14 @@ export default function HomePage() { description="Implement fully homomorphic encryption for confidential smart contracts in Solidity" glowColor="zama" /> + + } + title="Canton" + description="Build privacy-enabled Daml applications on the Canton Network with secure, reusable primitives" + glowColor="canton" + /> diff --git a/src/components/home-cards.tsx b/src/components/home-cards.tsx index 1b6be5fa..fc56a6f1 100644 --- a/src/components/home-cards.tsx +++ b/src/components/home-cards.tsx @@ -163,6 +163,7 @@ function EcosystemCard({ uniswap: "before:bg-gradient-to-r before:from-[#e6007a] before:to-[#FC75FE]", zama: "before:bg-gradient-to-r before:from-[#FFD205] before:to-[#FFD205]", + canton: "before:bg-gradient-to-r before:from-[#2f6df6] before:to-[#12c2c2]", }; return ( diff --git a/src/components/icons/canton-icon.tsx b/src/components/icons/canton-icon.tsx new file mode 100644 index 00000000..3067d8c2 --- /dev/null +++ b/src/components/icons/canton-icon.tsx @@ -0,0 +1,860 @@ +import { cn } from "@/lib/utils"; + +// Canton Network symbol, official brand asset (unmodified geometry) from +// https://www.canton.network/brand-kit-trademark-use, with per-surface color +// variants prepared by design. Canton is a registered trademark of +// Digital Asset (Switzerland) GmbH. +function CantonGlyph({ className }: { className: string }) { + return ( + + Canton + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {" "} + + ); +} + +// Sidebar/dropdown variant: theme-aware colors. +export function CantonIcon({ className }: { className: string }) { + return ( + + ); +} + +// Home page card variant. +export function CantonHomeIcon({ className }: { className: string }) { + return ( + + ); +} diff --git a/src/components/icons/index.ts b/src/components/icons/index.ts index 6581ba71..c84855c7 100644 --- a/src/components/icons/index.ts +++ b/src/components/icons/index.ts @@ -1,5 +1,6 @@ export { AnnotationDotsIcon } from "./annotation-dots-icon"; export { ArbitrumIcon } from "./arbitrum-icon"; +export { CantonHomeIcon, CantonIcon } from "./canton-icon"; export { ContractsLibraryIcon } from "./contracts-library-icon"; export { ContractsMcpIcon } from "./contracts-mcp-icon"; export { ContractsUpgradesIcon } from "./contracts-upgrades-icon"; diff --git a/src/components/layout/docs-layout-client.tsx b/src/components/layout/docs-layout-client.tsx index 238746e7..dbc855c0 100644 --- a/src/components/layout/docs-layout-client.tsx +++ b/src/components/layout/docs-layout-client.tsx @@ -6,6 +6,7 @@ import { useEffect, useMemo, useState } from "react"; import { baseOptions } from "@/app/layout.config"; import { ArbitrumIcon, + CantonIcon, EthereumIcon, MidnightIcon, PolkadotIcon, @@ -182,6 +183,11 @@ export function DocsLayoutClient({ children }: DocsLayoutClientProps) { icon: , urls: zamaUrls, }, + { + title: "Canton", + url: "/canton", + icon: , + }, ]; }, [pathname, lastEcosystem]); diff --git a/src/hooks/use-navigation-tree.ts b/src/hooks/use-navigation-tree.ts index 50b8c9c3..bf224a27 100644 --- a/src/hooks/use-navigation-tree.ts +++ b/src/hooks/use-navigation-tree.ts @@ -6,6 +6,7 @@ import { getEcosystemFromPath } from "@/lib/ecosystem-detection"; import { withVersionedRustBook } from "@/lib/relayer-rust-book"; import { arbitrumStylusTree, + cantonTree, ethereumEvmTree, impactTree, midnightTree, @@ -32,6 +33,8 @@ export function useNavigationTree() { if (pathname.startsWith("/stellar-contracts")) { sessionStorage.setItem("lastEcosystem", "stellar"); + } else if (pathname.startsWith("/canton")) { + sessionStorage.setItem("lastEcosystem", "canton"); } else if (pathname.startsWith("/substrate-runtimes")) { sessionStorage.setItem("lastEcosystem", "polkadot"); } else if (pathname.startsWith("/contracts-sui")) { @@ -78,6 +81,8 @@ export function useNavigationTree() { return suiTree; } else if (pathname.startsWith("/stellar-contracts")) { return stellarTree; + } else if (pathname.startsWith("/canton")) { + return cantonTree; } else if (pathname.startsWith("/contracts-compact")) { return midnightTree; } else if (pathname.startsWith("/confidential-contracts")) { diff --git a/src/lib/ecosystem-detection.ts b/src/lib/ecosystem-detection.ts index 6a8667c6..51cd8f5d 100644 --- a/src/lib/ecosystem-detection.ts +++ b/src/lib/ecosystem-detection.ts @@ -1,5 +1,6 @@ import { arbitrumStylusTree, + cantonTree, ethereumEvmTree, midnightTree, type NavigationNode, @@ -27,7 +28,8 @@ export type EcosystemKey = | "midnight" | "sui" | "starknet" - | "uniswap"; + | "uniswap" + | "canton"; const TREES: Array<{ key: EcosystemKey; tree: NavigationTree }> = [ { key: "ethereum", tree: ethereumEvmTree }, @@ -39,6 +41,7 @@ const TREES: Array<{ key: EcosystemKey; tree: NavigationTree }> = [ { key: "sui", tree: suiTree }, { key: "starknet", tree: starknetTree }, { key: "uniswap", tree: uniswapTree }, + { key: "canton", tree: cantonTree }, ]; /** diff --git a/src/navigation/canton.json b/src/navigation/canton.json new file mode 100644 index 00000000..5e847f70 --- /dev/null +++ b/src/navigation/canton.json @@ -0,0 +1,43 @@ +[ + { + "type": "page", + "name": "Overview", + "url": "/canton" + }, + { + "type": "page", + "name": "Get Started", + "url": "/canton/get-started" + }, + { + "type": "folder", + "name": "Library", + "index": { + "type": "page", + "name": "Access Control", + "url": "/canton/library/access-control" + }, + "children": [ + { + "type": "page", + "name": "Ownable", + "url": "/canton/library/ownable" + }, + { + "type": "page", + "name": "Pausable", + "url": "/canton/library/pausable" + } + ] + }, + { + "type": "page", + "name": "Settlement (CIP-112)", + "url": "/canton/settlement" + }, + { + "type": "page", + "name": "Reference Implementations", + "url": "/canton/reference-implementations" + } +] diff --git a/src/navigation/index.ts b/src/navigation/index.ts index 541cf948..485e9068 100644 --- a/src/navigation/index.ts +++ b/src/navigation/index.ts @@ -1,4 +1,5 @@ import arbitrumStylusData from "./arbitrum-stylus.json"; +import cantonData from "./canton.json"; import ethereumEvmData from "./ethereum-evm.json"; import impactData from "./impact.json"; import { mergeDeveloperLibraries } from "./merge-developer-libraries"; @@ -26,6 +27,7 @@ const sui = suiData as NavigationNode[]; const polkadot = mergeDeveloperLibraries(polkadotData as NavigationNode[]); const zama = mergeDeveloperLibraries(zamaData as NavigationNode[]); const uniswap = uniswapData as NavigationNode[]; +const canton = cantonData as NavigationNode[]; const impact = impactData as NavigationNode[]; // Create separate navigation trees for each blockchain @@ -74,6 +76,11 @@ export const polkadotTree: NavigationTree = { children: polkadot, }; +export const cantonTree: NavigationTree = { + name: "Canton", + children: canton, +}; + export const impactTree: NavigationTree = { name: "OpenZeppelin Impact", children: impact,